source: josm/trunk/src/org/openstreetmap/josm/io/remotecontrol/AddTagsDialog.java@ 7956

Last change on this file since 7956 was 7679, checked in by Don-vip, 10 years ago

fix #10498 - Remote Control applies tags incorrectly

  • Property svn:eol-style set to native
File size: 12.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.io.remotecontrol;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Component;
8import java.awt.Font;
9import java.awt.GridBagLayout;
10import java.awt.event.ActionEvent;
11import java.awt.event.KeyEvent;
12import java.awt.event.MouseEvent;
13import java.io.UnsupportedEncodingException;
14import java.net.URLDecoder;
15import java.util.Collection;
16import java.util.HashMap;
17import java.util.HashSet;
18import java.util.Map;
19import java.util.Set;
20
21import javax.swing.AbstractAction;
22import javax.swing.JCheckBox;
23import javax.swing.JPanel;
24import javax.swing.JTable;
25import javax.swing.KeyStroke;
26import javax.swing.table.DefaultTableModel;
27import javax.swing.table.TableCellEditor;
28import javax.swing.table.TableCellRenderer;
29import javax.swing.table.TableModel;
30
31import org.openstreetmap.josm.Main;
32import org.openstreetmap.josm.command.ChangePropertyCommand;
33import org.openstreetmap.josm.data.osm.OsmPrimitive;
34import org.openstreetmap.josm.gui.ExtendedDialog;
35import org.openstreetmap.josm.gui.util.GuiHelper;
36import org.openstreetmap.josm.gui.util.TableHelper;
37import org.openstreetmap.josm.tools.GBC;
38
39/**
40 * Dialog to add tags as part of the remotecontrol.
41 * Existing Keys get grey color and unchecked selectboxes so they will not overwrite the old Key-Value-Pairs by default.
42 * You can choose the tags you want to add by selectboxes. You can edit the tags before you apply them.
43 * @author master
44 * @since 3850
45 */
46public class AddTagsDialog extends ExtendedDialog {
47
48 private final JTable propertyTable;
49 private final Collection<? extends OsmPrimitive> sel;
50 private final int[] count;
51
52 private final String sender;
53 private static final Set<String> trustedSenders = new HashSet<>();
54
55 /**
56 * Class for displaying "delete from ... objects" in the table
57 */
58 static class DeleteTagMarker {
59 int num;
60 public DeleteTagMarker(int num) {
61 this.num = num;
62 }
63 @Override
64 public String toString() {
65 return tr("<delete from {0} objects>", num);
66 }
67 }
68
69 /**
70 * Class for displaying list of existing tag values in the table
71 */
72 static class ExistingValues {
73 String tag;
74 Map<String, Integer> valueCount;
75 public ExistingValues(String tag) {
76 this.tag=tag; valueCount=new HashMap<>();
77 }
78
79 int addValue(String val) {
80 Integer c = valueCount.get(val);
81 int r = c==null? 1 : (c.intValue()+1);
82 valueCount.put(val, r);
83 return r;
84 }
85
86 @Override
87 public String toString() {
88 StringBuilder sb=new StringBuilder();
89 for (String k: valueCount.keySet()) {
90 if (sb.length()>0) sb.append(", ");
91 sb.append(k);
92 }
93 return sb.toString();
94 }
95
96 private String getToolTip() {
97 StringBuilder sb=new StringBuilder();
98 sb.append("<html>");
99 sb.append(tr("Old values of"));
100 sb.append(" <b>");
101 sb.append(tag);
102 sb.append("</b><br/>");
103 for (String k: valueCount.keySet()) {
104 sb.append("<b>");
105 sb.append(valueCount.get(k));
106 sb.append(" x </b>");
107 sb.append(k);
108 sb.append("<br/>");
109 }
110 sb.append("</html>");
111 return sb.toString();
112
113 }
114 }
115
116 /**
117 * Constructs a new {@code AddTagsDialog}.
118 */
119 public AddTagsDialog(String[][] tags, String senderName, Collection<? extends OsmPrimitive> primitives) {
120 super(Main.parent, tr("Add tags to selected objects"), new String[] { tr("Add selected tags"), tr("Add all tags"), tr("Cancel")},
121 false,
122 true);
123 setToolTipTexts(new String[]{tr("Add checked tags to selected objects"), tr("Shift+Enter: Add all tags to selected objects"), ""});
124
125 this.sender = senderName;
126
127 final DefaultTableModel tm = new DefaultTableModel(new String[] {tr("Assume"), tr("Key"), tr("Value"), tr("Existing values")}, tags.length) {
128 final Class<?>[] types = {Boolean.class, String.class, Object.class, ExistingValues.class};
129 @Override
130 public Class<?> getColumnClass(int c) {
131 return types[c];
132 }
133 };
134
135 sel = primitives;
136 count = new int[tags.length];
137
138 for (int i = 0; i<tags.length; i++) {
139 count[i] = 0;
140 String key = tags[i][0];
141 String value = tags[i][1], oldValue;
142 Boolean b = Boolean.TRUE;
143 ExistingValues old = new ExistingValues(key);
144 for (OsmPrimitive osm : sel) {
145 oldValue = osm.get(key);
146 if (oldValue!=null) {
147 old.addValue(oldValue);
148 if (!oldValue.equals(value)) {
149 b = Boolean.FALSE;
150 count[i]++;
151 }
152 }
153 }
154 tm.setValueAt(b, i, 0);
155 tm.setValueAt(tags[i][0], i, 1);
156 tm.setValueAt(tags[i][1].isEmpty() ? new DeleteTagMarker(count[i]) : tags[i][1], i, 2);
157 tm.setValueAt(old , i, 3);
158 }
159
160 propertyTable = new JTable(tm) {
161
162 @Override
163 public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
164 Component c = super.prepareRenderer(renderer, row, column);
165 if (count[row]>0) {
166 c.setFont(c.getFont().deriveFont(Font.ITALIC));
167 c.setForeground(new Color(100, 100, 100));
168 } else {
169 c.setFont(c.getFont().deriveFont(Font.PLAIN));
170 c.setForeground(new Color(0, 0, 0));
171 }
172 return c;
173 }
174
175 @Override
176 public TableCellEditor getCellEditor(int row, int column) {
177 Object value = getValueAt(row,column);
178 if (value instanceof DeleteTagMarker) return null;
179 if (value instanceof ExistingValues) return null;
180 return getDefaultEditor(value.getClass());
181 }
182
183 @Override
184 public String getToolTipText(MouseEvent event) {
185 int r = rowAtPoint(event.getPoint());
186 int c = columnAtPoint(event.getPoint());
187 Object o = getValueAt(r, c);
188 if (c==1 || c==2) return o.toString();
189 if (c==3) return ((ExistingValues)o).getToolTip();
190 return tr("Enable the checkbox to accept the value");
191 }
192 };
193
194 propertyTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
195 // a checkbox has a size of 15 px
196 propertyTable.getColumnModel().getColumn(0).setMaxWidth(15);
197 TableHelper.adjustColumnWidth(propertyTable, 1, 150);
198 TableHelper.adjustColumnWidth(propertyTable, 2, 400);
199 TableHelper.adjustColumnWidth(propertyTable, 3, 300);
200 // get edit results if the table looses the focus, for example if a user clicks "add tags"
201 propertyTable.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
202 propertyTable.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, KeyEvent.SHIFT_MASK), "shiftenter");
203 propertyTable.getActionMap().put("shiftenter", new AbstractAction() {
204 @Override public void actionPerformed(ActionEvent e) {
205 buttonAction(1, e); // add all tags on Shift-Enter
206 }
207 });
208
209 // set the content of this AddTagsDialog consisting of the tableHeader and the table itself.
210 JPanel tablePanel = new JPanel();
211 tablePanel.setLayout(new GridBagLayout());
212 tablePanel.add(propertyTable.getTableHeader(), GBC.eol().fill(GBC.HORIZONTAL));
213 tablePanel.add(propertyTable, GBC.eol().fill(GBC.BOTH));
214 if (!sender.isEmpty() && !trustedSenders.contains(sender)) {
215 final JCheckBox c = new JCheckBox();
216 c.setAction(new AbstractAction(tr("Accept all tags from {0} for this session", sender) ) {
217 @Override public void actionPerformed(ActionEvent e) {
218 if (c.isSelected())
219 trustedSenders.add(sender);
220 else
221 trustedSenders.remove(sender);
222 }
223 } );
224 tablePanel.add(c , GBC.eol().insets(20,10,0,0));
225 }
226 setContent(tablePanel);
227 setDefaultButton(2);
228 }
229
230 /**
231 * If you click the "Add tags" button build a ChangePropertyCommand for every key that has a checked checkbox to apply the key value pair to all selected osm objects.
232 * You get a entry for every key in the command queue.
233 */
234 @Override
235 protected void buttonAction(int buttonIndex, ActionEvent evt) {
236 // if layer all layers were closed, ignore all actions
237 if (Main.main.getCurrentDataSet() != null && buttonIndex != 2) {
238 TableModel tm = propertyTable.getModel();
239 for (int i=0; i<tm.getRowCount(); i++) {
240 if (buttonIndex==1 || (Boolean)tm.getValueAt(i, 0)) {
241 String key =(String)tm.getValueAt(i, 1);
242 Object value = tm.getValueAt(i, 2);
243 Main.main.undoRedo.add(new ChangePropertyCommand(sel,
244 key, value instanceof String ? (String) value : ""));
245 }
246 }
247 }
248 if (buttonIndex == 2) {
249 trustedSenders.remove(sender);
250 }
251 setVisible(false);
252 }
253
254 /**
255 * parse addtags parameters Example URL (part):
256 * addtags=wikipedia:de%3DResidenzschloss Dresden|name:en%3DDresden Castle
257 */
258 public static void addTags(final Map<String, String> args, final String sender, final Collection<? extends OsmPrimitive> primitives) {
259 if (args.containsKey("addtags")) {
260 GuiHelper.executeByMainWorkerInEDT(new Runnable() {
261
262 @Override
263 public void run() {
264 String[] tags = null;
265 try {
266 tags = URLDecoder.decode(args.get("addtags"), "UTF-8").split("\\|");
267 } catch (UnsupportedEncodingException e) {
268 throw new RuntimeException(e);
269 }
270 Set<String> tagSet = new HashSet<>();
271 for (String tag : tags) {
272 if (!tag.trim().isEmpty() && tag.contains("=")) {
273 tagSet.add(tag.trim());
274 }
275 }
276 if (!tagSet.isEmpty()) {
277 String[][] keyValue = new String[tagSet.size()][2];
278 int i = 0;
279 for (String tag : tagSet) {
280 // support a = b===c as "a"="b===c"
281 String [] pair = tag.split("\\s*=\\s*",2);
282 keyValue[i][0] = pair[0];
283 keyValue[i][1] = pair.length<2 ? "": pair[1];
284 i++;
285 }
286 addTags(keyValue, sender, primitives);
287 }
288 }
289 });
290 }
291 }
292
293 /**
294 * Ask user and add the tags he confirm.
295 * @param keyValue is a table or {{tag1,val1},{tag2,val2},...}
296 * @param sender is a string for skipping confirmations. Use empty string for always confirmed adding.
297 * @param primitives OSM objects that will be modified
298 * @since 7521
299 */
300 public static void addTags(String[][] keyValue, String sender, Collection<? extends OsmPrimitive> primitives) {
301 if (trustedSenders.contains(sender)) {
302 if (Main.main.getCurrentDataSet() != null) {
303 for (String[] row : keyValue) {
304 Main.main.undoRedo.add(new ChangePropertyCommand(primitives, row[0], row[1]));
305 }
306 }
307 } else {
308 new AddTagsDialog(keyValue, sender, primitives).showDialog();
309 }
310 }
311}
Note: See TracBrowser for help on using the repository browser.