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

Last change on this file since 14628 was 14153, checked in by Don-vip, 6 years ago

see #15229 - deprecate Main.parent and Main itself

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