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

Last change on this file since 5800 was 5800, checked in by akks, 11 years ago

Properties toggle dialog: allow to select and copy/delete multiple keys/values, see #7846
fix #6917, fix #7895 - select multiple "member of" (fixed one non-working action in previous revision)

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