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

Last change on this file since 12279 was 12279, checked in by Don-vip, 7 years ago

sonar - squid:S3878 - Arrays should not be created for varargs parameters

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