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

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