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

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

fix #8574: relation list dialog side buttons din not work
Сontext menu unification (see #7846): relation list and membership table of properties dialog

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