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

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