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

Last change on this file since 16006 was 15319, checked in by Don-vip, 5 years ago

see #18038 - checkstyle

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