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

Last change on this file since 4107 was 4107, checked in by stoecker, 13 years ago

fix #6409 - patch by bilbo - Add 'do not ask again' to delete member from relation confirmation

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