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

Last change on this file since 1228 was 1228, checked in by ulfl, 15 years ago

Improve panels on the right side:
a) add a normal/minimized display mechanism, with "triangle icon"
b) display number of active items in title
c) show title bold if panel has "active items"
d) add close "X" button

(b and c currently not implemented in the various plugins - but easy to do so)

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