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

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