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

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

see #3475 - patch by Petr Dlouhý - cleanups

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