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

Last change on this file since 180 was 180, checked in by imi, 17 years ago
  • added a warning for first-users when converting gps to osm data
  • fixed missing property dialog caption when annotations were loaded
File size: 13.2 KB
Line 
1package org.openstreetmap.josm.gui.dialogs;
2
3import static org.openstreetmap.josm.tools.I18n.tr;
4import static org.openstreetmap.josm.tools.I18n.trn;
5import static org.xnap.commons.i18n.I18n.marktr;
6
7import java.awt.BorderLayout;
8import java.awt.Component;
9import java.awt.Font;
10import java.awt.GridBagLayout;
11import java.awt.GridLayout;
12import java.awt.event.ActionEvent;
13import java.awt.event.ActionListener;
14import java.awt.event.KeyEvent;
15import java.awt.event.MouseAdapter;
16import java.awt.event.MouseEvent;
17import java.util.Collection;
18import java.util.HashMap;
19import java.util.Map;
20import java.util.TreeMap;
21import java.util.TreeSet;
22import java.util.Vector;
23import java.util.Map.Entry;
24
25import javax.swing.DefaultComboBoxModel;
26import javax.swing.JButton;
27import javax.swing.JComboBox;
28import javax.swing.JDialog;
29import javax.swing.JLabel;
30import javax.swing.JOptionPane;
31import javax.swing.JPanel;
32import javax.swing.JScrollPane;
33import javax.swing.JTable;
34import javax.swing.JTextField;
35import javax.swing.ListSelectionModel;
36import javax.swing.table.DefaultTableCellRenderer;
37import javax.swing.table.DefaultTableModel;
38
39import org.openstreetmap.josm.Main;
40import org.openstreetmap.josm.command.ChangePropertyCommand;
41import org.openstreetmap.josm.data.SelectionChangedListener;
42import org.openstreetmap.josm.data.osm.OsmPrimitive;
43import org.openstreetmap.josm.gui.MapFrame;
44import org.openstreetmap.josm.gui.annotation.AnnotationCellRenderer;
45import org.openstreetmap.josm.gui.annotation.AnnotationPreset;
46import org.openstreetmap.josm.gui.annotation.ForwardActionListener;
47import org.openstreetmap.josm.gui.preferences.AnnotationPresetPreference;
48import org.openstreetmap.josm.tools.GBC;
49import org.openstreetmap.josm.tools.ImageProvider;
50
51/**
52 * This dialog displays the properties of the current selected primitives.
53 *
54 * If no object is selected, the dialog list is empty.
55 * If only one is selected, all properties of this object are selected.
56 * If more than one object are selected, the sum of all properties are displayed. If the
57 * different objects share the same property, the shared value is displayed. If they have
58 * different values, all of them are put in a combo box and the string "<different>"
59 * is displayed in italic.
60 *
61 * Below the list, the user can click on an add, modify and delete property button to
62 * edit the table selection value.
63 *
64 * The command is applied to all selected entries.
65 *
66 * @author imi
67 */
68public class PropertiesDialog extends ToggleDialog implements SelectionChangedListener {
69
70 /**
71 * Watches for double clicks and from editing or new property, depending on the
72 * location, the click was.
73 * @author imi
74 */
75 public class DblClickWatch extends MouseAdapter {
76 @Override public void mouseClicked(MouseEvent e) {
77 if (e.getClickCount() < 2)
78 return;
79 if (e.getSource() instanceof JScrollPane)
80 add();
81 else {
82 int row = propertyTable.rowAtPoint(e.getPoint());
83 edit(row);
84 }
85 }
86 }
87
88 /**
89 * Edit the value in the table row
90 * @param row The row of the table, from which the value is edited.
91 */
92 void edit(int row) {
93 String key = data.getValueAt(row, 0).toString();
94 Collection<OsmPrimitive> sel = Main.ds.getSelected();
95 if (sel.isEmpty()) {
96 JOptionPane.showMessageDialog(Main.parent, tr("Please select the objects you want to change properties for."));
97 return;
98 }
99 String msg = "<html>"+trn("This will change {0} object.", "This will change {0} objects.", sel.size(), sel.size())+"<br><br> "+tr("Please select a new value for \"{0}\".<br>(Empty string deletes the key.)", key)+"</html>";
100 final JComboBox combo = (JComboBox)data.getValueAt(row, 1);
101 JPanel p = new JPanel(new BorderLayout());
102 p.add(new JLabel(msg), BorderLayout.NORTH);
103 p.add(combo, BorderLayout.CENTER);
104
105 final JOptionPane optionPane = new JOptionPane(p, JOptionPane.QUESTION_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
106 @Override public void selectInitialValue() {
107 combo.requestFocusInWindow();
108 combo.getEditor().selectAll();
109 }
110 };
111 final JDialog dlg = optionPane.createDialog(Main.parent, tr("Change values?"));
112 combo.getEditor().addActionListener(new ActionListener(){
113 public void actionPerformed(ActionEvent e) {
114 optionPane.setValue(JOptionPane.OK_OPTION);
115 dlg.setVisible(false);
116 }
117 });
118 String oldComboEntry = combo.getEditor().getItem().toString();
119 dlg.setVisible(true);
120
121 Object answer = optionPane.getValue();
122 if (answer == null || answer == JOptionPane.UNINITIALIZED_VALUE ||
123 (answer instanceof Integer && (Integer)answer != JOptionPane.OK_OPTION)) {
124 combo.getEditor().setItem(oldComboEntry);
125 return;
126 }
127
128 String value = combo.getEditor().getItem().toString();
129 if (value.equals(tr("<different>")))
130 return;
131 if (value.equals(""))
132 value = null; // delete the key
133 Main.main.editLayer().add(new ChangePropertyCommand(sel, key, value));
134
135 if (value == null)
136 selectionChanged(sel); // update whole table
137
138 Main.parent.repaint(); // repaint all - drawing could have been changed
139 }
140
141 /**
142 * Open the add selection dialog and add a new key/value to the table (and to the
143 * dataset, of course).
144 */
145 void add() {
146 Collection<OsmPrimitive> sel = Main.ds.getSelected();
147 if (sel.isEmpty()) {
148 JOptionPane.showMessageDialog(Main.parent, tr("Please select objects for which you want to change properties."));
149 return;
150 }
151
152 JPanel p = new JPanel(new BorderLayout());
153 p.add(new JLabel("<html>"+trn("This will change {0} object.","This will change {0} objects.", sel.size(),sel.size())+"<br><br>"+tr("Please select a key")),
154 BorderLayout.NORTH);
155 TreeSet<String> allKeys = new TreeSet<String>();
156 for (OsmPrimitive osm : Main.ds.allNonDeletedPrimitives())
157 allKeys.addAll(osm.keySet());
158 for (int i = 0; i < data.getRowCount(); ++i)
159 allKeys.remove(data.getValueAt(i, 0));
160 final JComboBox keys = new JComboBox(new Vector<String>(allKeys));
161 keys.setEditable(true);
162 p.add(keys, BorderLayout.CENTER);
163
164 JPanel p2 = new JPanel(new BorderLayout());
165 p.add(p2, BorderLayout.SOUTH);
166 p2.add(new JLabel(tr("Please select a value")), BorderLayout.NORTH);
167 final JTextField values = new JTextField();
168 p2.add(values, BorderLayout.CENTER);
169 JOptionPane pane = new JOptionPane(p, JOptionPane.PLAIN_MESSAGE, JOptionPane.OK_CANCEL_OPTION){
170 @Override public void selectInitialValue() {
171 keys.requestFocusInWindow();
172 keys.getEditor().selectAll();
173 }
174 };
175 pane.createDialog(Main.parent, tr("Change values?")).setVisible(true);
176 if (!Integer.valueOf(JOptionPane.OK_OPTION).equals(pane.getValue()))
177 return;
178 String key = keys.getEditor().getItem().toString();
179 String value = values.getText();
180 if (value.equals(""))
181 return;
182 Main.main.editLayer().add(new ChangePropertyCommand(sel, key, value));
183 selectionChanged(sel); // update table
184 Main.parent.repaint(); // repaint all - drawing could have been changed
185 }
186
187 /**
188 * Delete the keys from the given row.
189 * @param row The row, which key gets deleted from the dataset.
190 */
191 private void delete(int row) {
192 String key = data.getValueAt(row, 0).toString();
193 Collection<OsmPrimitive> sel = Main.ds.getSelected();
194 Main.main.editLayer().add(new ChangePropertyCommand(sel, key, null));
195 selectionChanged(sel); // update table
196 }
197
198 /**
199 * The property data.
200 */
201 private final DefaultTableModel data = new DefaultTableModel(){
202 @Override public boolean isCellEditable(int row, int column) {
203 return false;
204 }
205 @Override public Class<?> getColumnClass(int columnIndex) {
206 return columnIndex == 1 ? JComboBox.class : String.class;
207 }
208 };
209 /**
210 * The properties list.
211 */
212 private final JTable propertyTable = new JTable(data);
213 public JComboBox annotationPresets = new JComboBox();
214
215
216 /**
217 * Create a new PropertiesDialog
218 */
219 public PropertiesDialog(MapFrame mapFrame) {
220 super(tr("Properties"), "propertiesdialog", tr("Property for selected objects."), KeyEvent.VK_P, 150);
221
222 if (AnnotationPresetPreference.annotationPresets.size() > 0) {
223 Vector<ActionListener> allPresets = new Vector<ActionListener>();
224 for (final AnnotationPreset p : AnnotationPresetPreference.annotationPresets)
225 allPresets.add(new ForwardActionListener(this, p));
226
227 allPresets.add(0, new ForwardActionListener(this, new AnnotationPreset()));
228 annotationPresets.setModel(new DefaultComboBoxModel(allPresets));
229 JPanel north = new JPanel(new GridBagLayout());
230 north.add(getComponent(0),GBC.eol().fill(GBC.HORIZONTAL));
231 north.add(annotationPresets,GBC.eol().fill(GBC.HORIZONTAL));
232 add(north, BorderLayout.NORTH);
233 }
234 annotationPresets.addActionListener(new ActionListener(){
235 public void actionPerformed(ActionEvent e) {
236 AnnotationPreset preset = ((ForwardActionListener)annotationPresets.getSelectedItem()).preset;
237 preset.actionPerformed(e);
238 annotationPresets.setSelectedItem(null);
239 }
240 });
241 annotationPresets.setRenderer(new AnnotationCellRenderer());
242
243 data.setColumnIdentifiers(new String[]{tr("Key"),tr("Value")});
244 propertyTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
245 propertyTable.setDefaultRenderer(JComboBox.class, new DefaultTableCellRenderer(){
246 @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
247 Component c = super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
248 if (c instanceof JLabel) {
249 String str = ((JComboBox)value).getEditor().getItem().toString();
250 ((JLabel)c).setText(str);
251 if (str.equals(tr("<different>")))
252 c.setFont(c.getFont().deriveFont(Font.ITALIC));
253 }
254 return c;
255 }
256 });
257 propertyTable.setDefaultRenderer(String.class, new DefaultTableCellRenderer(){
258 @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
259 return super.getTableCellRendererComponent(table, value, isSelected, false, row, column);
260 }
261 });
262 DblClickWatch dblClickWatch = new DblClickWatch();
263 propertyTable.addMouseListener(dblClickWatch);
264 JScrollPane scrollPane = new JScrollPane(propertyTable);
265 scrollPane.addMouseListener(dblClickWatch);
266 add(scrollPane, BorderLayout.CENTER);
267
268 JPanel buttonPanel = new JPanel(new GridLayout(1,3));
269 ActionListener buttonAction = new ActionListener(){
270 public void actionPerformed(ActionEvent e) {
271 int sel = propertyTable.getSelectedRow();
272 if (e.getActionCommand().equals("Add"))
273 add();
274 else if (e.getActionCommand().equals("Edit")) {
275 if (sel == -1)
276 JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to edit."));
277 else
278 edit(sel);
279 } else if (e.getActionCommand().equals("Delete")) {
280 if (sel == -1)
281 JOptionPane.showMessageDialog(Main.parent, tr("Please select the row to delete."));
282 else
283 delete(sel);
284 }
285 }
286 };
287 buttonPanel.add(createButton(marktr("Add"),tr("Add a new key/value pair to all objects"), KeyEvent.VK_A, buttonAction));
288 buttonPanel.add(createButton(marktr("Edit"),tr( "Edit the value of the selected key for all objects"), KeyEvent.VK_E, buttonAction));
289 buttonPanel.add(createButton(marktr("Delete"),tr("Delete the selected key in all objects"), KeyEvent.VK_D, buttonAction));
290 add(buttonPanel, BorderLayout.SOUTH);
291 }
292
293 private JButton createButton(String name, String tooltip, int mnemonic, ActionListener actionListener) {
294 JButton b = new JButton(tr(name), ImageProvider.get("dialogs", name.toLowerCase()));
295 b.setActionCommand(name);
296 b.addActionListener(actionListener);
297 b.setToolTipText(tooltip);
298 b.setMnemonic(mnemonic);
299 b.putClientProperty("help", "Dialog/Properties/"+name);
300 return b;
301 }
302
303 @Override public void setVisible(boolean b) {
304 if (b) {
305 Main.ds.addSelectionChangedListener(this);
306 selectionChanged(Main.ds.getSelected());
307 } else {
308 Main.ds.removeSelectionChangedListener(this);
309 }
310 super.setVisible(b);
311 }
312
313 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
314 if (propertyTable == null)
315 return; // selection changed may be received in base class constructor before init
316 if (propertyTable.getCellEditor() != null)
317 propertyTable.getCellEditor().cancelCellEditing();
318 data.setRowCount(0);
319
320 Map<String, Integer> valueCount = new HashMap<String, Integer>();
321 TreeMap<String, Collection<String>> props = new TreeMap<String, Collection<String>>();
322 for (OsmPrimitive osm : newSelection) {
323 for (Entry<String, String> e : osm.entrySet()) {
324 Collection<String> value = props.get(e.getKey());
325 if (value == null) {
326 value = new TreeSet<String>();
327 props.put(e.getKey(), value);
328 }
329 value.add(e.getValue());
330 valueCount.put(e.getKey(), valueCount.containsKey(e.getKey()) ? valueCount.get(e.getKey())+1 : 1);
331 }
332 }
333 for (Entry<String, Collection<String>> e : props.entrySet()) {
334 JComboBox value = new JComboBox(e.getValue().toArray());
335 value.setEditable(true);
336 value.getEditor().setItem(valueCount.get(e.getKey()) != newSelection.size() ? tr("<different>") : e.getValue().iterator().next());
337 data.addRow(new Object[]{e.getKey(), value});
338 }
339 }
340}
Note: See TracBrowser for help on using the repository browser.