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

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