source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java@ 6643

Last change on this file since 6643 was 6643, checked in by Don-vip, 10 years ago

global replacement of e.printStackTrace() by Main.error(e)

  • Property svn:eol-style set to native
File size: 51.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.properties;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Component;
7import java.awt.Container;
8import java.awt.Font;
9import java.awt.GridBagLayout;
10import java.awt.Point;
11import java.awt.event.ActionEvent;
12import java.awt.event.InputEvent;
13import java.awt.event.KeyEvent;
14import java.awt.event.MouseAdapter;
15import java.awt.event.MouseEvent;
16import java.net.HttpURLConnection;
17import java.net.URI;
18import java.net.URLEncoder;
19import java.util.ArrayList;
20import java.util.Arrays;
21import java.util.Collection;
22import java.util.Collections;
23import java.util.Comparator;
24import java.util.EnumSet;
25import java.util.HashMap;
26import java.util.LinkedList;
27import java.util.List;
28import java.util.Map;
29import java.util.Map.Entry;
30import java.util.Set;
31import java.util.TreeMap;
32import java.util.TreeSet;
33
34import javax.swing.AbstractAction;
35import javax.swing.JComponent;
36import javax.swing.JLabel;
37import javax.swing.JPanel;
38import javax.swing.JPopupMenu;
39import javax.swing.JScrollPane;
40import javax.swing.JTable;
41import javax.swing.KeyStroke;
42import javax.swing.ListSelectionModel;
43import javax.swing.event.ListSelectionEvent;
44import javax.swing.event.ListSelectionListener;
45import javax.swing.table.DefaultTableCellRenderer;
46import javax.swing.table.DefaultTableModel;
47import javax.swing.table.TableColumnModel;
48import javax.swing.table.TableModel;
49
50import org.openstreetmap.josm.Main;
51import org.openstreetmap.josm.actions.JosmAction;
52import org.openstreetmap.josm.actions.relation.DownloadMembersAction;
53import org.openstreetmap.josm.actions.relation.DownloadSelectedIncompleteMembersAction;
54import org.openstreetmap.josm.actions.relation.SelectInRelationListAction;
55import org.openstreetmap.josm.actions.relation.SelectMembersAction;
56import org.openstreetmap.josm.actions.relation.SelectRelationAction;
57import org.openstreetmap.josm.actions.search.SearchAction.SearchMode;
58import org.openstreetmap.josm.actions.search.SearchAction.SearchSetting;
59import org.openstreetmap.josm.command.ChangeCommand;
60import org.openstreetmap.josm.command.ChangePropertyCommand;
61import org.openstreetmap.josm.command.Command;
62import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent;
63import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener;
64import org.openstreetmap.josm.data.SelectionChangedListener;
65import org.openstreetmap.josm.data.osm.IRelation;
66import org.openstreetmap.josm.data.osm.Node;
67import org.openstreetmap.josm.data.osm.OsmPrimitive;
68import org.openstreetmap.josm.data.osm.Relation;
69import org.openstreetmap.josm.data.osm.RelationMember;
70import org.openstreetmap.josm.data.osm.Tag;
71import org.openstreetmap.josm.data.osm.Way;
72import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
73import org.openstreetmap.josm.data.osm.event.DataSetListenerAdapter;
74import org.openstreetmap.josm.data.osm.event.DatasetEventManager;
75import org.openstreetmap.josm.data.osm.event.DatasetEventManager.FireMode;
76import org.openstreetmap.josm.data.osm.event.SelectionEventManager;
77import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
78import org.openstreetmap.josm.gui.DefaultNameFormatter;
79import org.openstreetmap.josm.gui.ExtendedDialog;
80import org.openstreetmap.josm.gui.MapView;
81import org.openstreetmap.josm.gui.PopupMenuHandler;
82import org.openstreetmap.josm.gui.SideButton;
83import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
84import org.openstreetmap.josm.gui.dialogs.properties.PresetListPanel.PresetHandler;
85import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor;
86import org.openstreetmap.josm.gui.layer.OsmDataLayer;
87import org.openstreetmap.josm.gui.tagging.TaggingPreset;
88import org.openstreetmap.josm.gui.tagging.TaggingPresetType;
89import org.openstreetmap.josm.gui.util.GuiHelper;
90import org.openstreetmap.josm.gui.util.HighlightHelper;
91import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
92import org.openstreetmap.josm.tools.GBC;
93import org.openstreetmap.josm.tools.ImageProvider;
94import org.openstreetmap.josm.tools.InputMapUtils;
95import org.openstreetmap.josm.tools.LanguageInfo;
96import org.openstreetmap.josm.tools.OpenBrowser;
97import org.openstreetmap.josm.tools.Shortcut;
98import org.openstreetmap.josm.tools.Utils;
99
100/**
101 * This dialog displays the tags of the current selected primitives.
102 *
103 * If no object is selected, the dialog list is empty.
104 * If only one is selected, all tags of this object are selected.
105 * If more than one object are selected, the sum of all tags are displayed. If the
106 * different objects share the same tag, the shared value is displayed. If they have
107 * different values, all of them are put in a combo box and the string "<different>"
108 * is displayed in italic.
109 *
110 * Below the list, the user can click on an add, modify and delete tag button to
111 * edit the table selection value.
112 *
113 * The command is applied to all selected entries.
114 *
115 * @author imi
116 */
117public class PropertiesDialog extends ToggleDialog implements SelectionChangedListener, MapView.EditLayerChangeListener, DataSetListenerAdapter.Listener, PreferenceChangedListener {
118
119 /**
120 * hook for roadsigns plugin to display a small button in the upper right corner of this dialog
121 */
122 public static final JPanel pluginHook = new JPanel();
123
124 /**
125 * The tag data of selected objects.
126 */
127 private final DefaultTableModel tagData = new ReadOnlyTableModel();
128
129 /**
130 * The membership data of selected objects.
131 */
132 private final DefaultTableModel membershipData = new ReadOnlyTableModel();
133
134 /**
135 * The tags table.
136 */
137 private final JTable tagTable = new JTable(tagData);
138
139 /**
140 * The membership table.
141 */
142 private final JTable membershipTable = new JTable(membershipData);
143
144 // Popup menus
145 private final JPopupMenu tagMenu = new JPopupMenu();
146 private final JPopupMenu membershipMenu = new JPopupMenu();
147
148 // Popup menu handlers
149 private final PopupMenuHandler tagMenuHandler = new PopupMenuHandler(tagMenu);
150 private final PopupMenuHandler membershipMenuHandler = new PopupMenuHandler(membershipMenu);
151
152 private final Map<String, Map<String, Integer>> valueCount = new TreeMap<String, Map<String, Integer>>();
153 /**
154 * This sub-object is responsible for all adding and editing of tags
155 */
156 private final TagEditHelper editHelper = new TagEditHelper(tagData, valueCount);
157
158 private final DataSetListenerAdapter dataChangedAdapter = new DataSetListenerAdapter(this);
159 private final HelpAction helpAction = new HelpAction();
160 private final PasteValueAction pasteValueAction = new PasteValueAction();
161 private final CopyValueAction copyValueAction = new CopyValueAction();
162 private final CopyKeyValueAction copyKeyValueAction = new CopyKeyValueAction();
163 private final CopyAllKeyValueAction copyAllKeyValueAction = new CopyAllKeyValueAction();
164 private final SearchAction searchActionSame = new SearchAction(true);
165 private final SearchAction searchActionAny = new SearchAction(false);
166 private final AddAction addAction = new AddAction();
167 private final EditAction editAction = new EditAction();
168 private final DeleteAction deleteAction = new DeleteAction();
169 private final JosmAction[] josmActions = new JosmAction[]{addAction, editAction, deleteAction};
170
171 // relation actions
172 private final SelectInRelationListAction setRelationSelectionAction = new SelectInRelationListAction();
173 private final SelectRelationAction selectRelationAction = new SelectRelationAction(false);
174 private final SelectRelationAction addRelationToSelectionAction = new SelectRelationAction(true);
175
176 private final DownloadMembersAction downloadMembersAction = new DownloadMembersAction();
177 private final DownloadSelectedIncompleteMembersAction downloadSelectedIncompleteMembersAction = new DownloadSelectedIncompleteMembersAction();
178
179 private final SelectMembersAction selectMembersAction = new SelectMembersAction(false);
180 private final SelectMembersAction addMembersToSelectionAction = new SelectMembersAction(true);
181
182 private final HighlightHelper highlightHelper= new HighlightHelper();
183
184 /**
185 * The Add button (needed to be able to disable it)
186 */
187 private final SideButton btnAdd = new SideButton(addAction);
188 /**
189 * The Edit button (needed to be able to disable it)
190 */
191 private final SideButton btnEdit = new SideButton(editAction);
192 /**
193 * The Delete button (needed to be able to disable it)
194 */
195 private final SideButton btnDel = new SideButton(deleteAction);
196 /**
197 * Matching preset display class
198 */
199 private final PresetListPanel presets = new PresetListPanel();
200
201 /**
202 * Text to display when nothing selected.
203 */
204 private final JLabel selectSth = new JLabel("<html><p>"
205 + tr("Select objects for which to change tags.") + "</p></html>");
206
207 private PresetHandler presetHandler = new PresetHandler() {
208 @Override public void updateTags(List<Tag> tags) {
209 Command command = TaggingPreset.createCommand(getSelection(), tags);
210 if (command != null) Main.main.undoRedo.add(command);
211 }
212
213 @Override public Collection<OsmPrimitive> getSelection() {
214 if (Main.main == null) return null;
215 return Main.main.getInProgressSelection();
216 }
217 };
218
219 // <editor-fold defaultstate="collapsed" desc="Dialog construction and helper methods">
220
221 /**
222 * Create a new PropertiesDialog
223 */
224 public PropertiesDialog() {
225 super(tr("Tags/Memberships"), "propertiesdialog", tr("Tags for selected objects."),
226 Shortcut.registerShortcut("subwindow:properties", tr("Toggle: {0}", tr("Tags/Memberships")), KeyEvent.VK_P,
227 Shortcut.ALT_SHIFT), 150, true);
228
229 setupTagsMenu();
230 buildTagsTable();
231
232 setupMembershipMenu();
233 buildMembershipTable();
234
235 // combine both tables and wrap them in a scrollPane
236 JPanel bothTables = new JPanel();
237 boolean top = Main.pref.getBoolean("properties.presets.top", true);
238 bothTables.setLayout(new GridBagLayout());
239 if(top) {
240 bothTables.add(presets, GBC.std().fill(GBC.HORIZONTAL).insets(5, 2, 5, 2).anchor(GBC.NORTHWEST));
241 double epsilon = Double.MIN_VALUE; // need to set a weight or else anchor value is ignored
242 bothTables.add(pluginHook, GBC.eol().insets(0,1,1,1).anchor(GBC.NORTHEAST).weight(epsilon, epsilon));
243 }
244 bothTables.add(selectSth, GBC.eol().fill().insets(10, 10, 10, 10));
245 bothTables.add(tagTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
246 bothTables.add(tagTable, GBC.eol().fill(GBC.BOTH));
247 bothTables.add(membershipTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
248 bothTables.add(membershipTable, GBC.eol().fill(GBC.BOTH));
249 if(!top) {
250 bothTables.add(presets, GBC.eol().fill(GBC.HORIZONTAL).insets(5, 2, 5, 2));
251 }
252
253 setupKeyboardShortcuts();
254
255 // Let the action know when selection in the tables change
256 tagTable.getSelectionModel().addListSelectionListener(editAction);
257 membershipTable.getSelectionModel().addListSelectionListener(editAction);
258 tagTable.getSelectionModel().addListSelectionListener(deleteAction);
259 membershipTable.getSelectionModel().addListSelectionListener(deleteAction);
260
261
262 JScrollPane scrollPane = (JScrollPane) createLayout(bothTables, true, Arrays.asList(new SideButton[] {
263 this.btnAdd, this.btnEdit, this.btnDel
264 }));
265
266 MouseClickWatch mouseClickWatch = new MouseClickWatch();
267 tagTable.addMouseListener(mouseClickWatch);
268 membershipTable.addMouseListener(mouseClickWatch);
269 scrollPane.addMouseListener(mouseClickWatch);
270
271 selectSth.setPreferredSize(scrollPane.getSize());
272 presets.setSize(scrollPane.getSize());
273
274 editHelper.loadTagsIfNeeded();
275
276 Main.pref.addPreferenceChangeListener(this);
277 }
278
279 private void buildTagsTable() {
280 // setting up the tags table
281
282 tagData.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")});
283 tagTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
284 tagTable.getTableHeader().setReorderingAllowed(false);
285
286 PropertiesCellRenderer cellRenderer = new PropertiesCellRenderer();
287 tagTable.getColumnModel().getColumn(0).setCellRenderer(cellRenderer);
288 tagTable.getColumnModel().getColumn(1).setCellRenderer(cellRenderer);
289 }
290
291 private void buildMembershipTable() {
292 membershipData.setColumnIdentifiers(new String[]{tr("Member Of"),tr("Role"),tr("Position")});
293 membershipTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
294
295 TableColumnModel mod = membershipTable.getColumnModel();
296 membershipTable.getTableHeader().setReorderingAllowed(false);
297 mod.getColumn(0).setCellRenderer(new DefaultTableCellRenderer() {
298 @Override public Component getTableCellRendererComponent(JTable table, Object value,
299 boolean isSelected, boolean hasFocus, int row, int column) {
300 Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
301 if (value == null)
302 return this;
303 if (c instanceof JLabel) {
304 JLabel label = (JLabel)c;
305 Relation r = (Relation)value;
306 label.setText(r.getDisplayName(DefaultNameFormatter.getInstance()));
307 if (r.isDisabledAndHidden()) {
308 label.setFont(label.getFont().deriveFont(Font.ITALIC));
309 }
310 }
311 return c;
312 }
313 });
314
315 mod.getColumn(1).setCellRenderer(new DefaultTableCellRenderer() {
316 @Override public Component getTableCellRendererComponent(JTable table, Object value,
317 boolean isSelected, boolean hasFocus, int row, int column) {
318 if (value == null)
319 return this;
320 Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
321 boolean isDisabledAndHidden = (((Relation)table.getValueAt(row, 0))).isDisabledAndHidden();
322 if (c instanceof JLabel) {
323 JLabel label = (JLabel)c;
324 MemberInfo col = (MemberInfo) value;
325
326 String text = null;
327 for (RelationMember r : col.role) {
328 if (text == null) {
329 text = r.getRole();
330 }
331 else if (!text.equals(r.getRole())) {
332 text = tr("<different>");
333 break;
334 }
335 }
336
337 label.setText(text);
338 if (isDisabledAndHidden) {
339 label.setFont(label.getFont().deriveFont(Font.ITALIC));
340 }
341 }
342 return c;
343 }
344 });
345
346 mod.getColumn(2).setCellRenderer(new DefaultTableCellRenderer() {
347 @Override public Component getTableCellRendererComponent(JTable table, Object value,
348 boolean isSelected, boolean hasFocus, int row, int column) {
349 Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
350 boolean isDisabledAndHidden = (((Relation)table.getValueAt(row, 0))).isDisabledAndHidden();
351 if (c instanceof JLabel) {
352 JLabel label = (JLabel)c;
353 label.setText(((MemberInfo) table.getValueAt(row, 1)).getPositionString());
354 if (isDisabledAndHidden) {
355 label.setFont(label.getFont().deriveFont(Font.ITALIC));
356 }
357 }
358 return c;
359 }
360 });
361 mod.getColumn(2).setPreferredWidth(20);
362 mod.getColumn(1).setPreferredWidth(40);
363 mod.getColumn(0).setPreferredWidth(200);
364 }
365
366 /**
367 * creates the popup menu @field membershipMenu and its launcher on membership table
368 */
369 private void setupMembershipMenu() {
370 // setting up the membership table
371 membershipMenuHandler.addAction(setRelationSelectionAction);
372 membershipMenuHandler.addAction(selectRelationAction);
373 membershipMenuHandler.addAction(addRelationToSelectionAction);
374 membershipMenuHandler.addAction(selectMembersAction);
375 membershipMenuHandler.addAction(addMembersToSelectionAction);
376 membershipMenu.addSeparator();
377 membershipMenuHandler.addAction(downloadMembersAction);
378 membershipMenuHandler.addAction(downloadSelectedIncompleteMembersAction);
379 membershipMenu.addSeparator();
380 membershipMenu.add(helpAction);
381
382 membershipTable.addMouseListener(new PopupMenuLauncher(membershipMenu) {
383 @Override
384 protected int checkTableSelection(JTable table, Point p) {
385 int row = super.checkTableSelection(table, p);
386 List<Relation> rels = new ArrayList<Relation>();
387 for (int i: table.getSelectedRows()) {
388 rels.add((Relation) table.getValueAt(i, 0));
389 }
390 membershipMenuHandler.setPrimitives(rels);
391 return row;
392 }
393
394 @Override
395 public void mouseClicked(MouseEvent e) {
396 //update highlights
397 if (Main.isDisplayingMapView()) {
398 int row = membershipTable.rowAtPoint(e.getPoint());
399 if (row>=0) {
400 if (highlightHelper.highlightOnly((Relation) membershipTable.getValueAt(row, 0))) {
401 Main.map.mapView.repaint();
402 }
403 }
404 }
405 super.mouseClicked(e);
406 }
407
408 @Override
409 public void mouseExited(MouseEvent me) {
410 highlightHelper.clear();
411 }
412 });
413 }
414
415 /**
416 * creates the popup menu @field tagMenu and its launcher on tag table
417 */
418 private void setupTagsMenu() {
419 tagMenu.add(pasteValueAction);
420 tagMenu.add(copyValueAction);
421 tagMenu.add(copyKeyValueAction);
422 tagMenu.add(copyAllKeyValueAction);
423 tagMenu.addSeparator();
424 tagMenu.add(searchActionAny);
425 tagMenu.add(searchActionSame);
426 tagMenu.addSeparator();
427 tagMenu.add(helpAction);
428 tagTable.addMouseListener(new PopupMenuLauncher(tagMenu));
429 }
430
431 /**
432 * Assignas all needed keys like Enter and Spacebar to most important actions
433 */
434 private void setupKeyboardShortcuts() {
435
436 // ENTER = editAction, open "edit" dialog
437 tagTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
438 .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),"onTableEnter");
439 tagTable.getActionMap().put("onTableEnter",editAction);
440 membershipTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
441 .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0),"onTableEnter");
442 membershipTable.getActionMap().put("onTableEnter",editAction);
443
444 // INSERT button = addAction, open "add tag" dialog
445 tagTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
446 .put(KeyStroke.getKeyStroke(KeyEvent.VK_INSERT, 0),"onTableInsert");
447 tagTable.getActionMap().put("onTableInsert",addAction);
448
449 // unassign some standard shortcuts for JTable to allow upload / download / image browsing
450 InputMapUtils.unassignCtrlShiftUpDown(tagTable, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
451 InputMapUtils.unassignPageUpDown(tagTable, JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
452
453 // unassign some standard shortcuts for correct copy-pasting, fix #8508
454 tagTable.setTransferHandler(null);
455
456 tagTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
457 .put(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK),"onCopy");
458 tagTable.getActionMap().put("onCopy",copyKeyValueAction);
459
460 // allow using enter to add tags for all look&feel configurations
461 InputMapUtils.enableEnter(this.btnAdd);
462
463 // DEL button = deleteAction
464 getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
465 KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),"delete"
466 );
467 getActionMap().put("delete", deleteAction);
468
469 // F1 button = custom help action
470 getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
471 KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0), "onHelp");
472 getActionMap().put("onHelp", helpAction);
473 }
474
475 /**
476 * This simply fires up an {@link RelationEditor} for the relation shown; everything else
477 * is the editor's business.
478 *
479 * @param row
480 */
481 private void editMembership(int row) {
482 Relation relation = (Relation)membershipData.getValueAt(row, 0);
483 Main.map.relationListDialog.selectRelation(relation);
484 RelationEditor.getEditor(
485 Main.main.getEditLayer(),
486 relation,
487 ((MemberInfo) membershipData.getValueAt(row, 1)).role).setVisible(true);
488 }
489
490 private int findRow(TableModel model, Object value) {
491 for (int i=0; i<model.getRowCount(); i++) {
492 if (model.getValueAt(i, 0).equals(value))
493 return i;
494 }
495 return -1;
496 }
497
498 /**
499 * Update selection status, call @{link #selectionChanged} function.
500 */
501 private void updateSelection() {
502 // Parameter is ignored in this class
503 selectionChanged(null);
504 }
505
506 // </editor-fold>
507
508 // <editor-fold defaultstate="collapsed" desc="Event listeners methods">
509
510 @Override
511 public void showNotify() {
512 DatasetEventManager.getInstance().addDatasetListener(dataChangedAdapter, FireMode.IN_EDT_CONSOLIDATED);
513 SelectionEventManager.getInstance().addSelectionListener(this, FireMode.IN_EDT_CONSOLIDATED);
514 MapView.addEditLayerChangeListener(this);
515 for (JosmAction action : josmActions) {
516 Main.registerActionShortcut(action);
517 }
518 updateSelection();
519 }
520
521 @Override
522 public void hideNotify() {
523 DatasetEventManager.getInstance().removeDatasetListener(dataChangedAdapter);
524 SelectionEventManager.getInstance().removeSelectionListener(this);
525 MapView.removeEditLayerChangeListener(this);
526 for (JosmAction action : josmActions) {
527 Main.unregisterActionShortcut(action);
528 }
529 }
530
531 @Override
532 public void setVisible(boolean b) {
533 super.setVisible(b);
534 if (b && Main.main.getCurrentDataSet() != null) {
535 updateSelection();
536 }
537 }
538
539 @Override
540 public void destroy() {
541 super.destroy();
542 Main.pref.removePreferenceChangeListener(this);
543 for (JosmAction action : josmActions) {
544 action.destroy();
545 }
546 Container parent = pluginHook.getParent();
547 if (parent != null) {
548 parent.remove(pluginHook);
549 }
550 }
551
552 @Override
553 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
554 if (!isVisible())
555 return;
556 if (tagTable == null)
557 return; // selection changed may be received in base class constructor before init
558 if (tagTable.getCellEditor() != null) {
559 tagTable.getCellEditor().cancelCellEditing();
560 }
561
562 // Ignore parameter as we do not want to operate always on real selection here, especially in draw mode
563 Collection<OsmPrimitive> newSel = Main.main.getInProgressSelection();
564 if (newSel == null) {
565 newSel = Collections.<OsmPrimitive>emptyList();
566 }
567
568 String selectedTag;
569 Relation selectedRelation = null;
570 selectedTag = editHelper.getChangedKey(); // select last added or last edited key by default
571 if (selectedTag == null && tagTable.getSelectedRowCount() == 1) {
572 selectedTag = (String)tagData.getValueAt(tagTable.getSelectedRow(), 0);
573 }
574 if (membershipTable.getSelectedRowCount() == 1) {
575 selectedRelation = (Relation)membershipData.getValueAt(membershipTable.getSelectedRow(), 0);
576 }
577
578 // re-load tag data
579 tagData.setRowCount(0);
580
581 final boolean displayDiscardableKeys = Main.pref.getBoolean("display.discardable-keys", false);
582 final Map<String, Integer> keyCount = new HashMap<String, Integer>();
583 final Map<String, String> tags = new HashMap<String, String>();
584 valueCount.clear();
585 EnumSet<TaggingPresetType> types = EnumSet.noneOf(TaggingPresetType.class);
586 for (OsmPrimitive osm : newSel) {
587 types.add(TaggingPresetType.forPrimitive(osm));
588 for (String key : osm.keySet()) {
589 if (displayDiscardableKeys || !OsmPrimitive.getDiscardableKeys().contains(key)) {
590 String value = osm.get(key);
591 keyCount.put(key, keyCount.containsKey(key) ? keyCount.get(key) + 1 : 1);
592 if (valueCount.containsKey(key)) {
593 Map<String, Integer> v = valueCount.get(key);
594 v.put(value, v.containsKey(value) ? v.get(value) + 1 : 1);
595 } else {
596 TreeMap<String, Integer> v = new TreeMap<String, Integer>();
597 v.put(value, 1);
598 valueCount.put(key, v);
599 }
600 }
601 }
602 }
603 for (Entry<String, Map<String, Integer>> e : valueCount.entrySet()) {
604 int count = 0;
605 for (Entry<String, Integer> e1 : e.getValue().entrySet()) {
606 count += e1.getValue();
607 }
608 if (count < newSel.size()) {
609 e.getValue().put("", newSel.size() - count);
610 }
611 tagData.addRow(new Object[]{e.getKey(), e.getValue()});
612 tags.put(e.getKey(), e.getValue().size() == 1
613 ? e.getValue().keySet().iterator().next() : tr("<different>"));
614 }
615
616 membershipData.setRowCount(0);
617
618 Map<Relation, MemberInfo> roles = new HashMap<Relation, MemberInfo>();
619 for (OsmPrimitive primitive: newSel) {
620 for (OsmPrimitive ref: primitive.getReferrers()) {
621 if (ref instanceof Relation && !ref.isIncomplete() && !ref.isDeleted()) {
622 Relation r = (Relation) ref;
623 MemberInfo mi = roles.get(r);
624 if(mi == null) {
625 mi = new MemberInfo();
626 }
627 roles.put(r, mi);
628 int i = 1;
629 for (RelationMember m : r.getMembers()) {
630 if (m.getMember() == primitive) {
631 mi.add(m, i);
632 }
633 ++i;
634 }
635 }
636 }
637 }
638
639 List<Relation> sortedRelations = new ArrayList<Relation>(roles.keySet());
640 Collections.sort(sortedRelations, new Comparator<Relation>() {
641 @Override public int compare(Relation o1, Relation o2) {
642 int comp = Boolean.valueOf(o1.isDisabledAndHidden()).compareTo(o2.isDisabledAndHidden());
643 if (comp == 0) {
644 comp = o1.getDisplayName(DefaultNameFormatter.getInstance()).compareTo(o2.getDisplayName(DefaultNameFormatter.getInstance()));
645 }
646 return comp;
647 }}
648 );
649
650 for (Relation r: sortedRelations) {
651 membershipData.addRow(new Object[]{r, roles.get(r)});
652 }
653
654 presets.updatePresets(types, tags, presetHandler);
655
656 membershipTable.getTableHeader().setVisible(membershipData.getRowCount() > 0);
657 membershipTable.setVisible(membershipData.getRowCount() > 0);
658
659 boolean hasSelection = !newSel.isEmpty();
660 boolean hasTags = hasSelection && tagData.getRowCount() > 0;
661 boolean hasMemberships = hasSelection && membershipData.getRowCount() > 0;
662 btnAdd.setEnabled(hasSelection);
663 btnEdit.setEnabled(hasTags || hasMemberships);
664 btnDel.setEnabled(hasTags || hasMemberships);
665 tagTable.setVisible(hasTags);
666 tagTable.getTableHeader().setVisible(hasTags);
667 selectSth.setVisible(!hasSelection);
668 pluginHook.setVisible(hasSelection);
669
670 int selectedIndex;
671 if (selectedTag != null && (selectedIndex = findRow(tagData, selectedTag)) != -1) {
672 tagTable.changeSelection(selectedIndex, 0, false, false);
673 } else if (selectedRelation != null && (selectedIndex = findRow(membershipData, selectedRelation)) != -1) {
674 membershipTable.changeSelection(selectedIndex, 0, false, false);
675 } else if(hasTags) {
676 tagTable.changeSelection(0, 0, false, false);
677 } else if(hasMemberships) {
678 membershipTable.changeSelection(0, 0, false, false);
679 }
680
681 if(tagData.getRowCount() != 0 || membershipData.getRowCount() != 0) {
682 setTitle(tr("Tags: {0} / Memberships: {1}",
683 tagData.getRowCount(), membershipData.getRowCount()));
684 } else {
685 setTitle(tr("Tags / Memberships"));
686 }
687 }
688
689 /* ---------------------------------------------------------------------------------- */
690 /* EditLayerChangeListener */
691 /* ---------------------------------------------------------------------------------- */
692 @Override
693 public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
694 if (newLayer == null) editHelper.saveTagsIfNeeded();
695 // it is time to save history of tags
696 GuiHelper.runInEDT(new Runnable() {
697 @Override public void run() {
698 updateSelection();
699 }
700 });
701 }
702
703 @Override
704 public void processDatasetEvent(AbstractDatasetChangedEvent event) {
705 updateSelection();
706 }
707
708 // </editor-fold>
709
710 // <editor-fold defaultstate="collapsed" desc="Methods that are called by plugins to extend fuctionality ">
711
712 /**
713 * Replies the tag popup menu handler.
714 * @return The tag popup menu handler
715 */
716 public PopupMenuHandler getPropertyPopupMenuHandler() {
717 return tagMenuHandler;
718 }
719
720 @SuppressWarnings("unchecked")
721 public Tag getSelectedProperty() {
722 int row = tagTable.getSelectedRow();
723 if (row == -1) return null;
724 TreeMap<String, Integer> map = (TreeMap<String, Integer>) tagData.getValueAt(row, 1);
725 return new Tag(
726 tagData.getValueAt(row, 0).toString(),
727 map.size() > 1 ? "" : map.keySet().iterator().next());
728 }
729
730 /**
731 * Replies the membership popup menu handler.
732 * @return The membership popup menu handler
733 */
734 public PopupMenuHandler getMembershipPopupMenuHandler() {
735 return membershipMenuHandler;
736 }
737
738 public IRelation getSelectedMembershipRelation() {
739 int row = membershipTable.getSelectedRow();
740 return row > -1 ? (IRelation) membershipData.getValueAt(row, 0) : null;
741 }
742
743 // </editor-fold>
744
745 /**
746 * Class that watches for mouse clicks
747 * @author imi
748 */
749 public class MouseClickWatch extends MouseAdapter {
750 @Override public void mouseClicked(MouseEvent e) {
751 if (e.getClickCount() < 2)
752 {
753 // single click, clear selection in other table not clicked in
754 if (e.getSource() == tagTable) {
755 membershipTable.clearSelection();
756 } else if (e.getSource() == membershipTable) {
757 tagTable.clearSelection();
758 }
759 }
760 // double click, edit or add tag
761 else if (e.getSource() == tagTable)
762 {
763 int row = tagTable.rowAtPoint(e.getPoint());
764 if (row > -1) {
765 boolean focusOnKey = (tagTable.columnAtPoint(e.getPoint()) == 0);
766 editHelper.editTag(row, focusOnKey);
767 } else {
768 editHelper.addTag();
769 btnAdd.requestFocusInWindow();
770 }
771 } else if (e.getSource() == membershipTable) {
772 int row = membershipTable.rowAtPoint(e.getPoint());
773 if (row > -1) {
774 editMembership(row);
775 }
776 }
777 else
778 {
779 editHelper.addTag();
780 btnAdd.requestFocusInWindow();
781 }
782 }
783 @Override public void mousePressed(MouseEvent e) {
784 if (e.getSource() == tagTable) {
785 membershipTable.clearSelection();
786 } else if (e.getSource() == membershipTable) {
787 tagTable.clearSelection();
788 }
789 }
790
791 }
792
793 static class MemberInfo {
794 List<RelationMember> role = new ArrayList<RelationMember>();
795 List<Integer> position = new ArrayList<Integer>();
796 private String positionString = null;
797 void add(RelationMember r, Integer p) {
798 role.add(r);
799 position.add(p);
800 }
801 String getPositionString() {
802 if (positionString == null) {
803 Collections.sort(position);
804 positionString = String.valueOf(position.get(0));
805 int cnt = 0;
806 int last = position.get(0);
807 for (int i = 1; i < position.size(); ++i) {
808 int cur = position.get(i);
809 if (cur == last + 1) {
810 ++cnt;
811 } else if (cnt == 0) {
812 positionString += "," + cur;
813 } else {
814 positionString += "-" + last;
815 positionString += "," + cur;
816 cnt = 0;
817 }
818 last = cur;
819 }
820 if (cnt >= 1) {
821 positionString += "-" + last;
822 }
823 }
824 if (positionString.length() > 20) {
825 positionString = positionString.substring(0, 17) + "...";
826 }
827 return positionString;
828 }
829 }
830
831 /**
832 * Class that allows fast creation of read-only table model with String columns
833 */
834 public static class ReadOnlyTableModel extends DefaultTableModel {
835 @Override public boolean isCellEditable(int row, int column) {
836 return false;
837 }
838 @Override public Class<?> getColumnClass(int columnIndex) {
839 return String.class;
840 }
841 }
842
843 /**
844 * Action handling delete button press in properties dialog.
845 */
846 class DeleteAction extends JosmAction implements ListSelectionListener {
847
848 static final String DELETE_FROM_RELATION_PREF = "delete_from_relation";
849
850 public DeleteAction() {
851 super(tr("Delete"), "dialogs/delete", tr("Delete the selected key in all objects"),
852 Shortcut.registerShortcut("properties:delete", tr("Delete Tags"), KeyEvent.VK_D,
853 Shortcut.ALT_CTRL_SHIFT), false);
854 updateEnabledState();
855 }
856
857 protected void deleteTags(int[] rows){
858 // convert list of rows to HashMap (and find gap for nextKey)
859 HashMap<String, String> tags = new HashMap<String, String>(rows.length);
860 int nextKeyIndex = rows[0];
861 for (int row : rows) {
862 String key = tagData.getValueAt(row, 0).toString();
863 if (row == nextKeyIndex + 1) {
864 nextKeyIndex = row; // no gap yet
865 }
866 tags.put(key, null);
867 }
868
869 // find key to select after deleting other tags
870 String nextKey = null;
871 int rowCount = tagData.getRowCount();
872 if (rowCount > rows.length) {
873 if (nextKeyIndex == rows[rows.length-1]) {
874 // no gap found, pick next or previous key in list
875 nextKeyIndex = (nextKeyIndex + 1 < rowCount ? nextKeyIndex + 1 : rows[0] - 1);
876 } else {
877 // gap found
878 nextKeyIndex++;
879 }
880 nextKey = (String)tagData.getValueAt(nextKeyIndex, 0);
881 }
882
883 Collection<OsmPrimitive> sel = Main.main.getInProgressSelection();
884 Main.main.undoRedo.add(new ChangePropertyCommand(sel, tags));
885
886 membershipTable.clearSelection();
887 if (nextKey != null) {
888 tagTable.changeSelection(findRow(tagData, nextKey), 0, false, false);
889 }
890 }
891
892 protected void deleteFromRelation(int row) {
893 Relation cur = (Relation)membershipData.getValueAt(row, 0);
894
895 Relation nextRelation = null;
896 int rowCount = membershipTable.getRowCount();
897 if (rowCount > 1) {
898 nextRelation = (Relation)membershipData.getValueAt((row + 1 < rowCount ? row + 1 : row - 1), 0);
899 }
900
901 ExtendedDialog ed = new ExtendedDialog(Main.parent,
902 tr("Change relation"),
903 new String[] {tr("Delete from relation"), tr("Cancel")});
904 ed.setButtonIcons(new String[] {"dialogs/delete.png", "cancel.png"});
905 ed.setContent(tr("Really delete selection from relation {0}?", cur.getDisplayName(DefaultNameFormatter.getInstance())));
906 ed.toggleEnable(DELETE_FROM_RELATION_PREF);
907 ed.showDialog();
908
909 if(ed.getValue() != 1)
910 return;
911
912 Relation rel = new Relation(cur);
913 for (OsmPrimitive primitive: Main.main.getInProgressSelection()) {
914 rel.removeMembersFor(primitive);
915 }
916 Main.main.undoRedo.add(new ChangeCommand(cur, rel));
917
918 tagTable.clearSelection();
919 if (nextRelation != null) {
920 membershipTable.changeSelection(findRow(membershipData, nextRelation), 0, false, false);
921 }
922 }
923
924 @Override
925 public void actionPerformed(ActionEvent e) {
926 if (tagTable.getSelectedRowCount() > 0) {
927 int[] rows = tagTable.getSelectedRows();
928 deleteTags(rows);
929 } else if (membershipTable.getSelectedRowCount() > 0) {
930 ConditionalOptionPaneUtil.startBulkOperation(DELETE_FROM_RELATION_PREF);
931 int[] rows = membershipTable.getSelectedRows();
932 // delete from last relation to conserve row numbers in the table
933 for (int i=rows.length-1; i>=0; i--) {
934 deleteFromRelation(rows[i]);
935 }
936 ConditionalOptionPaneUtil.endBulkOperation(DELETE_FROM_RELATION_PREF);
937 }
938 }
939
940 @Override
941 protected void updateEnabledState() {
942 setEnabled(
943 (tagTable != null && tagTable.getSelectedRowCount() >= 1)
944 || (membershipTable != null && membershipTable.getSelectedRowCount() > 0)
945 );
946 }
947
948 @Override
949 public void valueChanged(ListSelectionEvent e) {
950 updateEnabledState();
951 }
952 }
953
954 /**
955 * Action handling add button press in properties dialog.
956 */
957 class AddAction extends JosmAction {
958 public AddAction() {
959 super(tr("Add"), "dialogs/add", tr("Add a new key/value pair to all objects"),
960 Shortcut.registerShortcut("properties:add", tr("Add Tag"), KeyEvent.VK_A,
961 Shortcut.ALT), false);
962 }
963
964 @Override
965 public void actionPerformed(ActionEvent e) {
966 editHelper.addTag();
967 btnAdd.requestFocusInWindow();
968 }
969 }
970
971 /**
972 * Action handling edit button press in properties dialog.
973 */
974 class EditAction extends JosmAction implements ListSelectionListener {
975 public EditAction() {
976 super(tr("Edit"), "dialogs/edit", tr("Edit the value of the selected key for all objects"),
977 Shortcut.registerShortcut("properties:edit", tr("Edit Tags"), KeyEvent.VK_S,
978 Shortcut.ALT), false);
979 updateEnabledState();
980 }
981
982 @Override
983 public void actionPerformed(ActionEvent e) {
984 if (!isEnabled())
985 return;
986 if (tagTable.getSelectedRowCount() == 1) {
987 int row = tagTable.getSelectedRow();
988 editHelper.editTag(row, false);
989 } else if (membershipTable.getSelectedRowCount() == 1) {
990 int row = membershipTable.getSelectedRow();
991 editMembership(row);
992 }
993 }
994
995 @Override
996 protected void updateEnabledState() {
997 setEnabled(
998 (tagTable != null && tagTable.getSelectedRowCount() == 1)
999 ^ (membershipTable != null && membershipTable.getSelectedRowCount() == 1)
1000 );
1001 }
1002
1003 @Override
1004 public void valueChanged(ListSelectionEvent e) {
1005 updateEnabledState();
1006 }
1007 }
1008
1009 class HelpAction extends AbstractAction {
1010 public HelpAction() {
1011 putValue(NAME, tr("Go to OSM wiki for tag help (F1)"));
1012 putValue(SHORT_DESCRIPTION, tr("Launch browser with wiki help for selected object"));
1013 putValue(SMALL_ICON, ImageProvider.get("dialogs", "search"));
1014 }
1015
1016 @Override
1017 public void actionPerformed(ActionEvent e) {
1018 try {
1019 String base = Main.pref.get("url.openstreetmap-wiki", "http://wiki.openstreetmap.org/wiki/");
1020 String lang = LanguageInfo.getWikiLanguagePrefix();
1021 final List<URI> uris = new ArrayList<URI>();
1022 int row;
1023 if (tagTable.getSelectedRowCount() == 1) {
1024 row = tagTable.getSelectedRow();
1025 String key = URLEncoder.encode(tagData.getValueAt(row, 0).toString(), "UTF-8");
1026 @SuppressWarnings("unchecked")
1027 Map<String, Integer> m = (Map<String, Integer>) tagData.getValueAt(row, 1);
1028 String val = URLEncoder.encode(m.entrySet().iterator().next().getKey(), "UTF-8");
1029
1030 uris.add(new URI(String.format("%s%sTag:%s=%s", base, lang, key, val)));
1031 uris.add(new URI(String.format("%sTag:%s=%s", base, key, val)));
1032 uris.add(new URI(String.format("%s%sKey:%s", base, lang, key)));
1033 uris.add(new URI(String.format("%sKey:%s", base, key)));
1034 uris.add(new URI(String.format("%s%sMap_Features", base, lang)));
1035 uris.add(new URI(String.format("%sMap_Features", base)));
1036 } else if (membershipTable.getSelectedRowCount() == 1) {
1037 row = membershipTable.getSelectedRow();
1038 String type = URLEncoder.encode(
1039 ((Relation)membershipData.getValueAt(row, 0)).get("type"), "UTF-8"
1040 );
1041
1042 if (type != null && !type.isEmpty()) {
1043 uris.add(new URI(String.format("%s%sRelation:%s", base, lang, type)));
1044 uris.add(new URI(String.format("%sRelation:%s", base, type)));
1045 }
1046
1047 uris.add(new URI(String.format("%s%sRelations", base, lang)));
1048 uris.add(new URI(String.format("%sRelations", base)));
1049 } else {
1050 // give the generic help page, if more than one element is selected
1051 uris.add(new URI(String.format("%s%sMap_Features", base, lang)));
1052 uris.add(new URI(String.format("%sMap_Features", base)));
1053 }
1054
1055 Main.worker.execute(new Runnable(){
1056 @Override public void run() {
1057 try {
1058 // find a page that actually exists in the wiki
1059 HttpURLConnection conn;
1060 for (URI u : uris) {
1061 conn = Utils.openHttpConnection(u.toURL());
1062 conn.setConnectTimeout(Main.pref.getInteger("socket.timeout.connect",15)*1000);
1063
1064 if (conn.getResponseCode() != 200) {
1065 Main.info("{0} does not exist", u);
1066 conn.disconnect();
1067 } else {
1068 int osize = conn.getContentLength();
1069 if (osize > -1) {
1070 conn.disconnect();
1071
1072 conn = Utils.openHttpConnection(new URI(u.toString()
1073 .replace("=", "%3D") /* do not URLencode whole string! */
1074 .replaceFirst("/wiki/", "/w/index.php?redirect=no&title=")
1075 ).toURL());
1076 conn.setConnectTimeout(Main.pref.getInteger("socket.timeout.connect",15)*1000);
1077 }
1078
1079 /* redirect pages have different content length, but retrieving a "nonredirect"
1080 * page using index.php and the direct-link method gives slightly different
1081 * content lengths, so we have to be fuzzy.. (this is UGLY, recode if u know better)
1082 */
1083 if (conn.getContentLength() != -1 && osize > -1 && Math.abs(conn.getContentLength() - osize) > 200) {
1084 Main.info("{0} is a mediawiki redirect", u);
1085 conn.disconnect();
1086 } else {
1087 Main.info("browsing to {0}", u);
1088 conn.disconnect();
1089
1090 OpenBrowser.displayUrl(u.toString());
1091 break;
1092 }
1093 }
1094 }
1095 } catch (Exception e) {
1096 Main.error(e);
1097 }
1098 }
1099 });
1100 } catch (Exception e1) {
1101 Main.error(e1);
1102 }
1103 }
1104 }
1105
1106 class PasteValueAction extends AbstractAction {
1107 public PasteValueAction() {
1108 putValue(NAME, tr("Paste Value"));
1109 putValue(SHORT_DESCRIPTION, tr("Paste the value of the selected tag from clipboard"));
1110 }
1111
1112 @Override
1113 public void actionPerformed(ActionEvent ae) {
1114 if (tagTable.getSelectedRowCount() != 1)
1115 return;
1116 String key = tagData.getValueAt(tagTable.getSelectedRow(), 0).toString();
1117 Collection<OsmPrimitive> sel = Main.main.getInProgressSelection();
1118 String clipboard = Utils.getClipboardContent();
1119 if (sel.isEmpty() || clipboard == null)
1120 return;
1121 Main.main.undoRedo.add(new ChangePropertyCommand(sel, key, Utils.strip(clipboard)));
1122 }
1123 }
1124
1125 abstract class AbstractCopyAction extends AbstractAction {
1126
1127 protected abstract Collection<String> getString(OsmPrimitive p, String key);
1128
1129 @Override
1130 public void actionPerformed(ActionEvent ae) {
1131 int[] rows = tagTable.getSelectedRows();
1132 Set<String> values = new TreeSet<String>();
1133 Collection<OsmPrimitive> sel = Main.main.getInProgressSelection();
1134 if (rows.length == 0 || sel.isEmpty()) return;
1135
1136 for (int row: rows) {
1137 String key = tagData.getValueAt(row, 0).toString();
1138 if (sel.isEmpty())
1139 return;
1140 for (OsmPrimitive p : sel) {
1141 Collection<String> s = getString(p,key);
1142 if (s != null) {
1143 values.addAll(s);
1144 }
1145 }
1146 }
1147 if (!values.isEmpty()) {
1148 Utils.copyToClipboard(Utils.join("\n", values));
1149 }
1150 }
1151 }
1152
1153 class CopyValueAction extends AbstractCopyAction {
1154
1155 public CopyValueAction() {
1156 putValue(NAME, tr("Copy Value"));
1157 putValue(SHORT_DESCRIPTION, tr("Copy the value of the selected tag to clipboard"));
1158 }
1159
1160 @Override
1161 protected Collection<String> getString(OsmPrimitive p, String key) {
1162 String v = p.get(key);
1163 return v == null ? null : Collections.singleton(v);
1164 }
1165 }
1166
1167 class CopyKeyValueAction extends AbstractCopyAction {
1168
1169 public CopyKeyValueAction() {
1170 putValue(NAME, tr("Copy Key/Value"));
1171 putValue(SHORT_DESCRIPTION, tr("Copy the key and value of the selected tag to clipboard"));
1172 }
1173
1174 @Override
1175 protected Collection<String> getString(OsmPrimitive p, String key) {
1176 String v = p.get(key);
1177 return v == null ? null : Collections.singleton(new Tag(key, v).toString());
1178 }
1179 }
1180
1181 class CopyAllKeyValueAction extends AbstractCopyAction {
1182
1183 public CopyAllKeyValueAction() {
1184 putValue(NAME, tr("Copy all Keys/Values"));
1185 putValue(SHORT_DESCRIPTION, tr("Copy the key and value of the all tags to clipboard"));
1186 }
1187
1188 @Override
1189 protected Collection<String> getString(OsmPrimitive p, String key) {
1190 List<String> r = new LinkedList<String>();
1191 for (Entry<String, String> kv : p.getKeys().entrySet()) {
1192 r.add(new Tag(kv.getKey(), kv.getValue()).toString());
1193 }
1194 return r;
1195 }
1196 }
1197
1198 class SearchAction extends AbstractAction {
1199 final boolean sameType;
1200
1201 public SearchAction(boolean sameType) {
1202 this.sameType = sameType;
1203 if (sameType) {
1204 putValue(NAME, tr("Search Key/Value/Type"));
1205 putValue(SHORT_DESCRIPTION, tr("Search with the key and value of the selected tag, restrict to type (i.e., node/way/relation)"));
1206 } else {
1207 putValue(NAME, tr("Search Key/Value"));
1208 putValue(SHORT_DESCRIPTION, tr("Search with the key and value of the selected tag"));
1209 }
1210 }
1211
1212 @Override
1213 public void actionPerformed(ActionEvent e) {
1214 if (tagTable.getSelectedRowCount() != 1)
1215 return;
1216 String key = tagData.getValueAt(tagTable.getSelectedRow(), 0).toString();
1217 Collection<OsmPrimitive> sel = Main.main.getInProgressSelection();
1218 if (sel.isEmpty())
1219 return;
1220 String sep = "";
1221 StringBuilder s = new StringBuilder();
1222 for (OsmPrimitive p : sel) {
1223 String val = p.get(key);
1224 if (val == null) {
1225 continue;
1226 }
1227 String t = "";
1228 if (!sameType) {
1229 t = "";
1230 } else if (p instanceof Node) {
1231 t = "type:node ";
1232 } else if (p instanceof Way) {
1233 t = "type:way ";
1234 } else if (p instanceof Relation) {
1235 t = "type:relation ";
1236 }
1237 s.append(sep).append("(").append(t).append("\"").append(
1238 org.openstreetmap.josm.actions.search.SearchAction.escapeStringForSearch(key)).append("\"=\"").append(
1239 org.openstreetmap.josm.actions.search.SearchAction.escapeStringForSearch(val)).append("\")");
1240 sep = " OR ";
1241 }
1242
1243 SearchSetting ss = new SearchSetting(s.toString(), SearchMode.replace, true, false, false);
1244 org.openstreetmap.josm.actions.search.SearchAction.searchWithoutHistory(ss);
1245 }
1246 }
1247
1248 @Override
1249 public void preferenceChanged(PreferenceChangeEvent e) {
1250 if ("display.discardable-keys".equals(e.getKey()) && Main.main.getCurrentDataSet() != null) {
1251 // Re-load data when display preference change
1252 updateSelection();
1253 }
1254 }
1255}
Note: See TracBrowser for help on using the repository browser.