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

Last change on this file since 1407 was 1407, checked in by stoecker, 15 years ago

close #2149

  • Property svn:eol-style set to native
File size: 31.8 KB
Line 
1// License: GPL. See LICENSE file for details.
2
3package org.openstreetmap.josm.gui.dialogs;
4
5import static org.openstreetmap.josm.tools.I18n.marktr;
6import static org.openstreetmap.josm.tools.I18n.tr;
7import static org.openstreetmap.josm.tools.I18n.trn;
8
9import java.awt.BorderLayout;
10import java.awt.Component;
11import java.awt.Font;
12import java.awt.GridBagLayout;
13import java.awt.GridLayout;
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.lang.String;
22import java.util.Collection;
23import java.util.Collections;
24import java.util.HashMap;
25import java.util.HashSet;
26import java.util.LinkedList;
27import java.util.Map;
28import java.util.TreeMap;
29import java.util.TreeSet;
30import java.util.Vector;
31import java.util.Map.Entry;
32
33import javax.swing.Box;
34import javax.swing.DefaultComboBoxModel;
35import javax.swing.DefaultListCellRenderer;
36import javax.swing.JComboBox;
37import javax.swing.JDialog;
38import javax.swing.JLabel;
39import javax.swing.JList;
40import javax.swing.JOptionPane;
41import javax.swing.JPanel;
42import javax.swing.JScrollPane;
43import javax.swing.JTable;
44import javax.swing.ListSelectionModel;
45import javax.swing.table.DefaultTableCellRenderer;
46import javax.swing.table.DefaultTableModel;
47import javax.swing.text.JTextComponent;
48
49import org.openstreetmap.josm.Main;
50import org.openstreetmap.josm.command.ChangeCommand;
51import org.openstreetmap.josm.command.ChangePropertyCommand;
52import org.openstreetmap.josm.command.Command;
53import org.openstreetmap.josm.command.SequenceCommand;
54import org.openstreetmap.josm.data.SelectionChangedListener;
55import org.openstreetmap.josm.data.osm.DataSet;
56import org.openstreetmap.josm.data.osm.Node;
57import org.openstreetmap.josm.data.osm.OsmPrimitive;
58import org.openstreetmap.josm.data.osm.Relation;
59import org.openstreetmap.josm.data.osm.RelationMember;
60import org.openstreetmap.josm.data.osm.Way;
61import org.openstreetmap.josm.data.osm.visitor.NameVisitor;
62import org.openstreetmap.josm.gui.ExtendedDialog;
63import org.openstreetmap.josm.gui.JMultilineLabel;
64import org.openstreetmap.josm.gui.MapFrame;
65import org.openstreetmap.josm.gui.SideButton;
66import org.openstreetmap.josm.gui.preferences.TaggingPresetPreference;
67import org.openstreetmap.josm.gui.tagging.TaggingPreset;
68import org.openstreetmap.josm.tools.AutoCompleteComboBox;
69import org.openstreetmap.josm.tools.GBC;
70import org.openstreetmap.josm.tools.Shortcut;
71
72/**
73 * This dialog displays the properties of the current selected primitives.
74 *
75 * If no object is selected, the dialog list is empty.
76 * If only one is selected, all properties of this object are selected.
77 * If more than one object are selected, the sum of all properties are displayed. If the
78 * different objects share the same property, the shared value is displayed. If they have
79 * different values, all of them are put in a combo box and the string "<different>"
80 * is displayed in italic.
81 *
82 * Below the list, the user can click on an add, modify and delete property button to
83 * edit the table selection value.
84 *
85 * The command is applied to all selected entries.
86 *
87 * @author imi
88 */
89public class PropertiesDialog extends ToggleDialog implements SelectionChangedListener {
90
91 /**
92 * Used to display relation names in the membership table
93 */
94 private NameVisitor nameVisitor = new NameVisitor();
95
96 /**
97 * Watches for double clicks and from editing or new property, depending on the
98 * location, the click was.
99 * @author imi
100 */
101 public class DblClickWatch extends MouseAdapter {
102 @Override public void mouseClicked(MouseEvent e) {
103 if (e.getClickCount() < 2)
104 {
105 if (e.getSource() == propertyTable)
106 membershipTable.clearSelection();
107 else if (e.getSource() == membershipTable)
108 propertyTable.clearSelection();
109 }
110 else if (e.getSource() == propertyTable)
111 {
112 int row = propertyTable.rowAtPoint(e.getPoint());
113 if (row > -1)
114 propertyEdit(row);
115 } else if (e.getSource() == membershipTable) {
116 int row = membershipTable.rowAtPoint(e.getPoint());
117 if (row > -1)
118 membershipEdit(row);
119 }
120 else
121 {
122 add();
123 }
124 }
125 }
126
127 private final Map<String, Map<String, Integer>> valueCount = new TreeMap<String, Map<String, Integer>>();
128 /**
129 * Edit the value in the properties table row
130 * @param row The row of the table from which the value is edited.
131 */
132 void propertyEdit(int row) {
133 Collection<OsmPrimitive> sel = Main.ds.getSelected();
134 if (sel.isEmpty()) return;
135
136 String key = propertyData.getValueAt(row, 0).toString();
137 objKey=key;
138
139 String msg = "<html>"+trn("This will change up to {0} object.", "This will change up to {0} objects.", sel.size(), sel.size())+"<br><br>("+tr("An empty value deletes the key.", key)+")</html>";
140
141 JPanel panel = new JPanel(new BorderLayout());
142 panel.add(new JLabel(msg), BorderLayout.NORTH);
143
144 final TreeMap<String, TreeSet<String>> allData = createAutoCompletionInfo(true);
145
146 JPanel p = new JPanel(new GridBagLayout());
147 panel.add(p, BorderLayout.CENTER);
148
149 final AutoCompleteComboBox keys = new AutoCompleteComboBox();
150 keys.setPossibleItems(allData.keySet());
151 keys.setEditable(true);
152 keys.setSelectedItem(key);
153
154 p.add(new JLabel(tr("Key")), GBC.std());
155 p.add(Box.createHorizontalStrut(10), GBC.std());
156 p.add(keys, GBC.eol().fill(GBC.HORIZONTAL));
157
158 final AutoCompleteComboBox values = new AutoCompleteComboBox();
159 values.setRenderer(new DefaultListCellRenderer() {
160 @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
161 Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
162 if (c instanceof JLabel) {
163 String str = null;
164 str=(String) value;
165 if (valueCount.containsKey(objKey)){
166 Map<String, Integer> m=valueCount.get(objKey);
167 if (m.containsKey(str)) {
168 str+="("+m.get(str)+")";
169 c.setFont(c.getFont().deriveFont(Font.ITALIC+Font.BOLD));
170 }
171 }
172 ((JLabel)c).setText(str);
173 }
174 return c;
175 }
176 });
177 values.setEditable(true);
178 updateListData(key, allData, values);
179 Map<String, Integer> m=(Map<String, Integer>)propertyData.getValueAt(row, 1);
180 final String selection= m.size()!=1?tr("<different>"):m.entrySet().iterator().next().getKey();
181 values.setSelectedItem(selection);
182 values.getEditor().setItem(selection);
183 p.add(new JLabel(tr("Value")), GBC.std());
184 p.add(Box.createHorizontalStrut(10), GBC.std());
185 p.add(values, GBC.eol().fill(GBC.HORIZONTAL));
186 addFocusAdapter(row, allData, keys, values);
187
188 final JOptionPane optionPane = new JOptionPane(panel, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION) {
189 @Override public void selectInitialValue() {
190 values.requestFocusInWindow();
191 values.getEditor().selectAll();
192 }
193 };
194 final JDialog dlg = optionPane.createDialog(Main.parent, tr("Change values?"));
195
196 values.getEditor().addActionListener(new ActionListener() {
197 public void actionPerformed(ActionEvent e) {
198 dlg.setVisible(false);
199 optionPane.setValue(JOptionPane.OK_OPTION);
200 }
201 });
202
203 String oldValue = values.getEditor().getItem().toString();
204 dlg.setVisible(true);
205
206 Object answer = optionPane.getValue();
207 if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE ||
208 (answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION)) {
209 values.getEditor().setItem(oldValue);
210 return;
211 }
212
213 String value = values.getEditor().getItem().toString().trim();
214 // is not Java 1.5
215 //value = java.text.Normalizer.normalize(value, java.text.Normalizer.Form.NFC);
216 if (value.equals(""))
217 value = null; // delete the key
218 String newkey = keys.getEditor().getItem().toString().trim();
219 //newkey = java.text.Normalizer.normalize(newkey, java.text.Normalizer.Form.NFC);
220 if (newkey.equals("")) {
221 newkey = key;
222 value = null; // delete the key instead
223 }
224 if (newkey.equals("created_by"))
225 {
226 // we don't allow created_by to be changed.
227 return;
228 }
229 if (key.equals(newkey) || value == null)
230 Main.main.undoRedo.add(new ChangePropertyCommand(sel, newkey, value));
231 else {
232 Collection<Command> commands=new Vector<Command>();
233 commands.add(new ChangePropertyCommand(sel, key, null));
234 if (value.equals(tr("<different>"))) {
235 HashMap<String, Vector<OsmPrimitive>> map=new HashMap<String, Vector<OsmPrimitive>>();
236 for (OsmPrimitive osm: sel) {
237 if(osm.keys != null)
238 {
239 String val=osm.keys.get(key);
240 if(val != null)
241 {
242 if (map.containsKey(val)) {
243 map.get(val).add(osm);
244 } else {
245 Vector<OsmPrimitive> v = new Vector<OsmPrimitive>();
246 v.add(osm);
247 map.put(val, v);
248 }
249 }
250 }
251 }
252 for (Entry<String, Vector<OsmPrimitive>> e: map.entrySet()) {
253 commands.add(new ChangePropertyCommand(e.getValue(), newkey, e.getKey()));
254 }
255 } else {
256 commands.add(new ChangePropertyCommand(sel, newkey, value));
257 }
258 Main.main.undoRedo.add(new SequenceCommand(trn("Change properties of up to {0} object", "Change properties of up to {0} objects", sel.size(), sel.size()), commands));
259 }
260
261 Main.ds.fireSelectionChanged(sel);
262 selectionChanged(sel); // update whole table
263 Main.parent.repaint(); // repaint all - drawing could have been changed
264
265 if(!key.equals(newkey)) {
266 for(int i=0; i < propertyTable.getRowCount(); i++)
267 if(propertyData.getValueAt(i, 0).toString() == newkey) {
268 row=i;
269 break;
270 }
271 }
272 propertyTable.changeSelection(row, 0, false, false);
273 }
274
275 /**
276 * @param key
277 * @param allData
278 * @param values
279 */
280 private void updateListData(String key, final TreeMap<String, TreeSet<String>> allData, final AutoCompleteComboBox values) {
281 Collection<String> newItems;
282 if (allData.containsKey(key)) {
283 newItems = allData.get(key);
284 } else {
285 newItems = Collections.emptyList();
286 }
287 values.setPossibleItems(newItems);
288 }
289
290 /**
291 * This simply fires up an relation editor for the relation shown; everything else
292 * is the editor's business.
293 *
294 * @param row
295 */
296 void membershipEdit(int row) {
297 final RelationEditor editor = new RelationEditor((Relation)membershipData.getValueAt(row, 0),
298 (Collection<RelationMember>) membershipData.getValueAt(row, 1) );
299 editor.setVisible(true);
300 }
301
302 /**
303 * Open the add selection dialog and add a new key/value to the table (and
304 * to the dataset, of course).
305 */
306 void add() {
307 Collection<OsmPrimitive> sel = Main.ds.getSelected();
308 if (sel.isEmpty()) return;
309
310 JPanel p = new JPanel(new BorderLayout());
311 p.add(new JLabel("<html>"+trn("This will change up to {0} object.","This will change up to {0} objects.", sel.size(),sel.size())+"<br><br>"+tr("Please select a key")),
312 BorderLayout.NORTH);
313 final TreeMap<String, TreeSet<String>> allData = createAutoCompletionInfo(false);
314 final AutoCompleteComboBox keys = new AutoCompleteComboBox();
315 keys.setPossibleItems(allData.keySet());
316 keys.setEditable(true);
317
318 p.add(keys, BorderLayout.CENTER);
319
320 JPanel p2 = new JPanel(new BorderLayout());
321 p.add(p2, BorderLayout.SOUTH);
322 p2.add(new JLabel(tr("Please select a value")), BorderLayout.NORTH);
323 final AutoCompleteComboBox values = new AutoCompleteComboBox();
324 values.setEditable(true);
325 p2.add(values, BorderLayout.CENTER);
326
327 addFocusAdapter(-1, allData, keys, values);
328 JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
329 @Override public void selectInitialValue() {
330 keys.requestFocusInWindow();
331 keys.getEditor().selectAll();
332 }
333 };
334 pane.createDialog(Main.parent, tr("Change values?")).setVisible(true);
335 if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue()))
336 return;
337 String key = keys.getEditor().getItem().toString().trim();
338 String value = values.getEditor().getItem().toString().trim();
339 if (value.equals(""))
340 return;
341 if (key.equals("created_by"))
342 return;
343 Main.main.undoRedo.add(new ChangePropertyCommand(sel, key, value));
344 Main.ds.fireSelectionChanged(sel);
345 selectionChanged(sel); // update table
346 Main.parent.repaint(); // repaint all - drawing could have been changed
347 }
348
349 /**
350 * @param allData
351 * @param keys
352 * @param values
353 */
354 private void addFocusAdapter(final int row, final TreeMap<String, TreeSet<String>> allData,final AutoCompleteComboBox keys, final AutoCompleteComboBox values) {
355 // get the combo box' editor component
356 JTextComponent editor = (JTextComponent)values.getEditor()
357 .getEditorComponent();
358 // Refresh the values model when focus is gained
359 editor.addFocusListener(new FocusAdapter() {
360 @Override public void focusGained(FocusEvent e) {
361 String key = keys.getEditor().getItem().toString();
362 updateListData(key, allData, values);
363 objKey=key;
364 }
365 });
366 }
367 private String objKey;
368 /**
369 * @return
370 */
371 private TreeMap<String, TreeSet<String>> createAutoCompletionInfo(
372 boolean edit) {
373 final TreeMap<String, TreeSet<String>> allData = new TreeMap<String, TreeSet<String>>();
374 for (OsmPrimitive osm : Main.ds.allNonDeletedPrimitives()) {
375 for (String key : osm.keySet()) {
376 TreeSet<String> values = null;
377 if (allData.containsKey(key))
378 values = allData.get(key);
379 else {
380 values = new TreeSet<String>();
381 allData.put(key, values);
382 }
383 values.add(osm.get(key));
384 }
385 }
386 if (!edit) {
387 for (int i = 0; i < propertyData.getRowCount(); ++i)
388 allData.remove(propertyData.getValueAt(i, 0));
389 }
390 return allData;
391 }
392
393 /**
394 * Delete the keys from the given row.
395 * @param row The row, which key gets deleted from the dataset.
396 */
397 private void delete(int row) {
398 String key = propertyData.getValueAt(row, 0).toString();
399 Collection<OsmPrimitive> sel = Main.ds.getSelected();
400 Main.main.undoRedo.add(new ChangePropertyCommand(sel, key, null));
401 Main.ds.fireSelectionChanged(sel);
402 selectionChanged(sel); // update table
403
404 int rowCount = propertyTable.getRowCount();
405 propertyTable.changeSelection((row < rowCount ? row : (rowCount-1)), 0, false, false);
406 }
407
408 /**
409 * The property data.
410 */
411 private final DefaultTableModel propertyData = new DefaultTableModel() {
412 @Override public boolean isCellEditable(int row, int column) {
413 return false;
414 }
415 @Override public Class<?> getColumnClass(int columnIndex) {
416 return String.class;
417 }
418 };
419
420 /**
421 * The membership data.
422 */
423 private final DefaultTableModel membershipData = new DefaultTableModel() {
424 @Override public boolean isCellEditable(int row, int column) {
425 return false;
426 }
427 @Override public Class<?> getColumnClass(int columnIndex) {
428 return String.class;
429 }
430 };
431
432 /**
433 * The properties list.
434 */
435 private final JTable propertyTable = new JTable(propertyData);
436 private final JTable membershipTable = new JTable(membershipData);
437
438 public JComboBox taggingPresets = new JComboBox();
439
440 /**
441 * The Add/Edit/Delete buttons (needed to be able to disable them)
442 */
443 private final SideButton btnAdd;
444 private final SideButton btnEdit;
445 private final SideButton btnDel;
446 private final JMultilineLabel presets = new JMultilineLabel("");
447
448 private final JLabel selectSth = new JLabel("<html><p>" + tr("Please select the objects you want to change properties for.") + "</p></html>");
449
450 /**
451 * Create a new PropertiesDialog
452 */
453 public PropertiesDialog(MapFrame mapFrame) {
454 super(tr("Properties/Memberships"), "propertiesdialog", tr("Properties for selected objects."),
455 Shortcut.registerShortcut("subwindow:properties", tr("Toggle: {0}", tr("Properties/Memberships")), KeyEvent.VK_P,
456 Shortcut.GROUP_LAYER, Shortcut.SHIFT_DEFAULT), 150);
457
458 // setting up the properties table
459 propertyData.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")});
460 propertyTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
461
462 propertyTable.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer(){
463 @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
464 Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
465 if (c instanceof JLabel) {
466 String str = null;
467 switch (column) {
468 case 0:
469 str = (String) value;
470 break;
471 case 1:
472 Map<String, Integer> v = (Map<String,Integer>) value;
473 if (v.size()!=1) {
474 str=tr("<different>");
475 c.setFont(c.getFont().deriveFont(Font.ITALIC));
476 } else {
477 str=v.entrySet().iterator().next().getKey();
478 }
479 break;
480 }
481 ((JLabel)c).setText(str);
482 }
483 return c;
484 }
485 });
486
487 // setting up the membership table
488
489 membershipData.setColumnIdentifiers(new String[]{tr("Member Of"),tr("Role")});
490 membershipTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
491
492 membershipTable.getColumnModel().getColumn(0).setCellRenderer(new DefaultTableCellRenderer() {
493 @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
494 Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
495 if (c instanceof JLabel) {
496 nameVisitor.visit((Relation)value);
497 ((JLabel)c).setText(nameVisitor.name);
498 }
499 return c;
500 }
501 });
502
503 membershipTable.getColumnModel().getColumn(1).setCellRenderer(new DefaultTableCellRenderer() {
504 @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
505 Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
506 if (c instanceof JLabel) {
507 Collection<RelationMember> col = (Collection<RelationMember>) value;
508
509 String text = null;
510 for (RelationMember r : col) {
511 if (text == null) {
512 text = r.role;
513 }
514 else if (!text.equals(r.role)) {
515 text = tr("<different>");
516 break;
517 }
518 }
519
520 ((JLabel)c).setText(text);
521 }
522 return c;
523 }
524 });
525
526 // combine both tables and wrap them in a scrollPane
527 JPanel bothTables = new JPanel();
528 bothTables.setLayout(new GridBagLayout());
529 bothTables.add(selectSth, GBC.eol().fill().insets(10, 10, 10, 10));
530 bothTables.add(propertyTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
531 bothTables.add(propertyTable, GBC.eol().fill(GBC.BOTH));
532 bothTables.add(membershipTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
533 bothTables.add(membershipTable, GBC.eol().fill(GBC.BOTH));
534 bothTables.add(presets, GBC.eol().fill().insets(10, 10, 10, 10));
535
536 DblClickWatch dblClickWatch = new DblClickWatch();
537 propertyTable.addMouseListener(dblClickWatch);
538 membershipTable.addMouseListener(dblClickWatch);
539 JScrollPane scrollPane = new JScrollPane(bothTables);
540 scrollPane.addMouseListener(dblClickWatch);
541 add(scrollPane, BorderLayout.CENTER);
542
543 selectSth.setPreferredSize(scrollPane.getSize());
544 presets.setPreferredSize(scrollPane.getSize());
545
546 JPanel buttonPanel = new JPanel(new GridLayout(1,3));
547 ActionListener buttonAction = new ActionListener(){
548 public void actionPerformed(ActionEvent e) {
549 int row = membershipTable.getSelectedRow();
550 if (e.getActionCommand().equals("Add"))
551 add();
552 else if(row >= 0)
553 {
554 if (e.getActionCommand().equals("Edit"))
555 membershipEdit(row);
556 else if (e.getActionCommand().equals("Delete")) {
557 Relation cur = (Relation)membershipData.getValueAt(row, 0);
558 NameVisitor n = new NameVisitor();
559 cur.visit(n);
560
561 int result = new ExtendedDialog(Main.parent,
562 tr("Change relation"),
563 tr("Really delete selection from relation {0}?", n.name),
564 new String[] {tr("Delete from relation"), tr("Cancel")},
565 new String[] {"dialogs/delete.png", "cancel.png"}).getValue();
566
567 if(result == 1)
568 {
569 Relation rel = new Relation(cur);
570 Collection<OsmPrimitive> sel = Main.ds.getSelected();
571 for (RelationMember rm : cur.members) {
572 for (OsmPrimitive osm : sel) {
573 if (rm.member == osm)
574 {
575 RelationMember mem = new RelationMember();
576 mem.role = rm.role;
577 mem.member = rm.member;
578 rel.members.remove(mem);
579 break;
580 }
581 }
582 }
583 Main.main.undoRedo.add(new ChangeCommand(cur, rel));
584 Main.ds.fireSelectionChanged(sel);
585 selectionChanged(sel); // update whole table
586 }
587
588 }
589 }
590 else
591 {
592 int sel = propertyTable.getSelectedRow();
593 // Although we might edit/delete the wrong tag here, chances are still better
594 // than just displaying an error message (which always "fails").
595 if (e.getActionCommand().equals("Edit"))
596 propertyEdit(sel >= 0 ? sel : 0);
597 else if (e.getActionCommand().equals("Delete"))
598 delete(sel >= 0 ? sel : 0);
599 }
600 }
601 };
602
603 Shortcut s = Shortcut.registerShortcut("properties:add", tr("Add Properties"), KeyEvent.VK_B,
604 Shortcut.GROUP_MNEMONIC);
605 this.btnAdd = new SideButton(marktr("Add"),"add","Properties",
606 tr("Add a new key/value pair to all objects"), s, buttonAction);
607 buttonPanel.add(this.btnAdd);
608
609 s = Shortcut.registerShortcut("properties:edit", tr("Edit Properties"), KeyEvent.VK_I,
610 Shortcut.GROUP_MNEMONIC);
611 this.btnEdit = new SideButton(marktr("Edit"),"edit","Properties",
612 tr("Edit the value of the selected key for all objects"), s, buttonAction);
613 buttonPanel.add(this.btnEdit);
614
615 s = Shortcut.registerShortcut("properties:delete", tr("Delete Properties"), KeyEvent.VK_Q,
616 Shortcut.GROUP_MNEMONIC);
617 this.btnDel = new SideButton(marktr("Delete"),"delete","Properties",
618 tr("Delete the selected key in all objects"), s, buttonAction);
619 buttonPanel.add(this.btnDel);
620 add(buttonPanel, BorderLayout.SOUTH);
621
622 DataSet.selListeners.add(this);
623 }
624
625 @Override public void setVisible(boolean b) {
626 super.setVisible(b);
627 if (b)
628 selectionChanged(Main.ds.getSelected());
629 }
630
631 private void checkPresets(int nodes, int ways, int relations, int closedways)
632 {
633 LinkedList<TaggingPreset> p = new LinkedList<TaggingPreset>();
634 int total = nodes+ways+relations+closedways;
635 if(total != 0)
636 {
637 for(TaggingPreset t : TaggingPresetPreference.taggingPresets)
638 {
639 if(!( (relations > 0 && !t.types.contains("relation")) &&
640 (nodes > 0 && !t.types.contains("node")) &&
641 (ways+closedways > 0 && !t.types.contains("way")) &&
642 (closedways > 0 && !t.types.contains("closedway"))))
643 {
644 int found = 0;
645 for(TaggingPreset.Item i : t.data)
646 {
647 if(i instanceof TaggingPreset.Key)
648 {
649 String val = ((TaggingPreset.Key)i).value;
650 String key = ((TaggingPreset.Key)i).key;
651 // we subtract 100 if not found and add 1 if found
652 found -= 100;
653 if(valueCount.containsKey(key))
654 {
655 Map<String, Integer> v = valueCount.get(key);
656 if(v.size() == 1 && v.containsKey(val) && v.get(val) == total)
657 {
658 found += 101;
659 }
660 }
661 }
662 }
663 if(found > 0)
664 p.add(t);
665 }
666 }
667 }
668 String t = "";
669 for(TaggingPreset tp : p)
670 {
671 if(t.length() > 0)
672 t += "\n";
673 t += tp.getName();
674 }
675 presets.setText(t);
676 presets.setVisible(t.length() > 0);
677 }
678
679 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
680 if (!isVisible())
681 return;
682 if (propertyTable == null)
683 return; // selection changed may be received in base class constructor before init
684 if (propertyTable.getCellEditor() != null)
685 propertyTable.getCellEditor().cancelCellEditing();
686
687 // re-load property data
688 propertyData.setRowCount(0);
689 int nodes = 0;
690 int ways = 0;
691 int relations = 0;
692 int closedways = 0;
693
694 Map<String, Integer> keyCount = new HashMap<String, Integer>();
695 valueCount.clear();
696 for (OsmPrimitive osm : newSelection) {
697 if(osm instanceof Node) ++nodes;
698 else if(osm instanceof Relation) ++relations;
699 else if(((Way)osm).isClosed()) ++closedways;
700 else ++ways;
701 for (Entry<String, String> e : osm.entrySet()) {
702 keyCount.put(e.getKey(), keyCount.containsKey(e.getKey()) ? keyCount.get(e.getKey())+1 : 1);
703 if (valueCount.containsKey(e.getKey())) {
704 Map<String, Integer> v = valueCount.get(e.getKey());
705 v.put(e.getValue(), v.containsKey(e.getValue())? v.get(e.getValue())+1 : 1 );
706 } else {
707 TreeMap<String,Integer> v = new TreeMap<String, Integer>();
708 v.put(e.getValue(), 1);
709 valueCount.put(e.getKey(), v);
710 }
711 }
712 }
713 for (Entry<String, Map<String, Integer>> e : valueCount.entrySet()) {
714 int count=0;
715 for (Entry<String, Integer> e1: e.getValue().entrySet()) {
716 count+=e1.getValue();
717 }
718 if (count < newSelection.size()) {
719 e.getValue().put("", newSelection.size()-count);
720 }
721 propertyData.addRow(new Object[]{e.getKey(), e.getValue()});
722 }
723
724 boolean hasTags = !newSelection.isEmpty() && propertyData.getRowCount() > 0;
725 boolean hasSelection = !newSelection.isEmpty();
726 btnAdd.setEnabled(hasSelection);
727 btnEdit.setEnabled(hasTags);
728 btnDel.setEnabled(hasTags);
729 propertyTable.setVisible(hasSelection);
730 propertyTable.getTableHeader().setVisible(hasSelection);
731 selectSth.setVisible(!hasSelection);
732 if(hasTags) propertyTable.changeSelection(0, 0, false, false);
733
734 checkPresets(nodes, ways, relations, closedways);
735
736 // re-load membership data
737 // this is rather expensive since we have to walk through all members of all existing relationships.
738 // could use back references here for speed if necessary.
739
740 membershipData.setRowCount(0);
741
742 Map<Relation, Collection<RelationMember>> roles = new HashMap<Relation, Collection<RelationMember>>();
743 for (Relation r : Main.ds.relations) {
744 if (!r.deleted && !r.incomplete) {
745 for (RelationMember m : r.members) {
746 if (newSelection.contains(m.member)) {
747 Collection<RelationMember> value = roles.get(r);
748 if (value == null) {
749 value = new HashSet<RelationMember>();
750 roles.put(r, value);
751 }
752 value.add(m);
753 }
754 }
755 }
756 }
757
758 for (Entry<Relation, Collection<RelationMember>> e : roles.entrySet()) {
759 membershipData.addRow(new Object[]{e.getKey(), e.getValue()});
760 }
761
762 membershipTable.getTableHeader().setVisible(membershipData.getRowCount() > 0);
763 membershipTable.setVisible(membershipData.getRowCount() > 0);
764
765 if(propertyData.getRowCount() != 0 || membershipData.getRowCount() != 0) {
766 setTitle(tr("Properties: {0} / Memberships: {1}",
767 propertyData.getRowCount(), membershipData.getRowCount()), true);
768 } else {
769 setTitle(tr("Properties / Memberships"), false);
770 }
771 }
772}
Note: See TracBrowser for help on using the repository browser.