Changeset 6068 in josm for trunk/src/org/openstreetmap/josm
- Timestamp:
- 2013-07-16T21:38:36+02:00 (11 years ago)
- Location:
- trunk/src/org/openstreetmap/josm
- Files:
-
- 5 added
- 14 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/validation/tests/RelationChecker.java
r6062 r6068 25 25 import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference; 26 26 import org.openstreetmap.josm.gui.tagging.TaggingPreset; 27 import org.openstreetmap.josm.gui.tagging.TaggingPreset.PresetType; 27 import org.openstreetmap.josm.gui.tagging.TaggingPresetItem; 28 import org.openstreetmap.josm.gui.tagging.TaggingPresetItems.Role; 29 import org.openstreetmap.josm.gui.tagging.TaggingPresetItems.Key; 30 import org.openstreetmap.josm.gui.tagging.TaggingPresetItems.Roles; 31 import org.openstreetmap.josm.gui.tagging.TaggingPresetType; 28 32 29 33 /** … … 65 69 if (presets != null) { 66 70 for (TaggingPreset p : presets) { 67 for (TaggingPreset .Item i : p.data) {68 if (i instanceof TaggingPreset.Roles) {71 for (TaggingPresetItem i : p.data) { 72 if (i instanceof Roles) { 69 73 relationpresets.add(p); 70 74 break; … … 86 90 @Override 87 91 public void visit(Relation n) { 88 LinkedList< TaggingPreset.Role> allroles = new LinkedList<TaggingPreset.Role>();92 LinkedList<Role> allroles = new LinkedList<Role>(); 89 93 for (TaggingPreset p : relationpresets) { 90 94 boolean matches = true; 91 TaggingPreset.Roles r = null;92 for (TaggingPreset .Item i : p.data) {93 if (i instanceof TaggingPreset.Key) {94 TaggingPreset.Key k = (TaggingPreset.Key) i;95 Roles r = null; 96 for (TaggingPresetItem i : p.data) { 97 if (i instanceof Key) { 98 Key k = (Key) i; 95 99 if (!k.value.equals(n.get(k.key))) { 96 100 matches = false; 97 101 break; 98 102 } 99 } else if (i instanceof TaggingPreset.Roles) {100 r = ( TaggingPreset.Roles) i;103 } else if (i instanceof Roles) { 104 r = (Roles) i; 101 105 } 102 106 } … … 140 144 } else { 141 145 LinkedList<String> done = new LinkedList<String>(); 142 for ( TaggingPreset.Role r : allroles) {146 for (Role r : allroles) { 143 147 done.add(r.key); 144 148 String keyname = r.key; … … 168 172 Set<OsmPrimitive> wrongTypes = new HashSet<OsmPrimitive>(); 169 173 if (r.types != null) { 170 if (!r.types.contains( PresetType.WAY)) {171 wrongTypes.addAll(r.types.contains( PresetType.CLOSEDWAY) ? ri.openways : ri.ways);172 } 173 if (!r.types.contains( PresetType.NODE)) {174 if (!r.types.contains(TaggingPresetType.WAY)) { 175 wrongTypes.addAll(r.types.contains(TaggingPresetType.CLOSEDWAY) ? ri.openways : ri.ways); 176 } 177 if (!r.types.contains(TaggingPresetType.NODE)) { 174 178 wrongTypes.addAll(ri.nodes); 175 179 } 176 if (!r.types.contains( PresetType.RELATION)) {180 if (!r.types.contains(TaggingPresetType.RELATION)) { 177 181 wrongTypes.addAll(ri.relations); 178 182 } -
trunk/src/org/openstreetmap/josm/data/validation/tests/TagChecker.java
r5791 r6068 57 57 import org.openstreetmap.josm.gui.progress.ProgressMonitor; 58 58 import org.openstreetmap.josm.gui.tagging.TaggingPreset; 59 import org.openstreetmap.josm.gui.tagging.TaggingPresetItem; 60 import org.openstreetmap.josm.gui.tagging.TaggingPresetItems.KeyedItem; 59 61 import org.openstreetmap.josm.io.MirroredInputStream; 60 62 import org.openstreetmap.josm.tools.GBC; … … 301 303 } 302 304 for (TaggingPreset p : presets) { 303 for (TaggingPreset .Item i : p.data) {304 if (i instanceof TaggingPreset.KeyedItem) {305 TaggingPreset.KeyedItem ky = (TaggingPreset.KeyedItem) i;305 for (TaggingPresetItem i : p.data) { 306 if (i instanceof KeyedItem) { 307 KeyedItem ky = (KeyedItem) i; 306 308 if (ky.key != null && ky.getValues() != null) { 307 309 try { -
trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PresetListPanel.java
r5614 r6068 22 22 import org.openstreetmap.josm.data.osm.Tag; 23 23 import org.openstreetmap.josm.gui.tagging.TaggingPreset; 24 import org.openstreetmap.josm.gui.tagging.TaggingPreset .PresetType;24 import org.openstreetmap.josm.gui.tagging.TaggingPresetType; 25 25 import org.openstreetmap.josm.tools.GBC; 26 26 … … 77 77 } 78 78 79 public void updatePresets(final Collection< PresetType> types, final Map<String, String> tags, PresetHandler presetHandler) {79 public void updatePresets(final Collection<TaggingPresetType> types, final Map<String, String> tags, PresetHandler presetHandler) { 80 80 81 81 removeAll(); -
trunk/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
r6038 r6068 84 84 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 85 85 import org.openstreetmap.josm.gui.tagging.TaggingPreset; 86 import org.openstreetmap.josm.gui.tagging.TaggingPreset .PresetType;86 import org.openstreetmap.josm.gui.tagging.TaggingPresetType; 87 87 import org.openstreetmap.josm.gui.util.GuiHelper; 88 88 import org.openstreetmap.josm.gui.util.HighlightHelper; … … 597 597 final Map<String, String> tags = new HashMap<String, String>(); 598 598 valueCount.clear(); 599 EnumSet< PresetType> types = EnumSet.noneOf(TaggingPreset.PresetType.class);599 EnumSet<TaggingPresetType> types = EnumSet.noneOf(TaggingPresetType.class); 600 600 for (OsmPrimitive osm : newSelection) { 601 types.add( PresetType.forPrimitive(osm));601 types.add(TaggingPresetType.forPrimitive(osm)); 602 602 for (String key : osm.keySet()) { 603 603 String value = osm.get(key); -
trunk/src/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditor.java
r6064 r6068 85 85 import org.openstreetmap.josm.gui.tagging.TagModel; 86 86 import org.openstreetmap.josm.gui.tagging.TaggingPreset; 87 import org.openstreetmap.josm.gui.tagging.TaggingPresetType; 87 88 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField; 88 89 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList; … … 769 770 public static Command addPrimitivesToRelation(final Relation orig, Collection<? extends OsmPrimitive> primitivesToAdd) { 770 771 try { 771 final Collection<TaggingPreset> presets = TaggingPreset.getMatchingPresets(EnumSet.of(TaggingPreset .PresetType.RELATION), orig.getKeys(), false);772 final Collection<TaggingPreset> presets = TaggingPreset.getMatchingPresets(EnumSet.of(TaggingPresetType.RELATION), orig.getKeys(), false); 772 773 Relation relation = new Relation(orig); 773 774 boolean modified = false; -
trunk/src/org/openstreetmap/josm/gui/dialogs/relation/MemberTableModel.java
r5630 r6068 40 40 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 41 41 import org.openstreetmap.josm.gui.tagging.TaggingPreset; 42 import org.openstreetmap.josm.gui.tagging.TaggingPresetType; 42 43 import org.openstreetmap.josm.gui.widgets.OsmPrimitivesTableModel; 43 44 … … 384 385 385 386 private void addMembersAtIndex(List<? extends OsmPrimitive> primitives, int index) { 386 final Collection<TaggingPreset> presets = TaggingPreset.getMatchingPresets(EnumSet.of(TaggingPreset .PresetType.RELATION), presetHandler.getSelection().iterator().next().getKeys(), false);387 final Collection<TaggingPreset> presets = TaggingPreset.getMatchingPresets(EnumSet.of(TaggingPresetType.RELATION), presetHandler.getSelection().iterator().next().getKeys(), false); 387 388 if (primitives == null) 388 389 return; -
trunk/src/org/openstreetmap/josm/gui/preferences/map/TaggingPresetPreference.java
r5763 r6068 39 39 import org.openstreetmap.josm.gui.tagging.TaggingPreset; 40 40 import org.openstreetmap.josm.gui.tagging.TaggingPresetMenu; 41 import org.openstreetmap.josm.gui.tagging.TaggingPresetReader; 41 42 import org.openstreetmap.josm.gui.tagging.TaggingPresetSeparator; 42 43 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionManager; … … 48 49 49 50 public static class Factory implements PreferenceSettingFactory { 51 @Override 50 52 public PreferenceSetting createPreferenceSetting() { 51 53 return new TaggingPresetPreference(); … … 69 71 70 72 private ValidationListener validationListener = new ValidationListener() { 73 @Override 71 74 public boolean validatePreferences() { 72 75 if (sources.hasActiveSourcesChanged()) { … … 78 81 boolean canLoad = false; 79 82 try { 80 TaggingPreset .readAll(source.url, false);83 TaggingPresetReader.readAll(source.url, false); 81 84 canLoad = true; 82 85 } catch (IOException e) { … … 101 104 102 105 try { 103 TaggingPreset .readAll(source.url, true);106 TaggingPresetReader.readAll(source.url, true); 104 107 } catch (IOException e) { 105 108 // Should not happen, but at least show message … … 154 157 }; 155 158 159 @Override 156 160 public void addGui(final PreferenceTabbedPane gui) { 157 161 sortMenu = new JCheckBox(tr("Sort presets menu"), … … 258 262 } 259 263 264 @Override 260 265 public boolean ok() { 261 266 boolean restart = Main.pref.put("taggingpreset.sortmenu", sortMenu.getSelectedObjects() != null); … … 269 274 */ 270 275 public static void initialize() { 271 taggingPresets = TaggingPreset .readFromPreferences(false);276 taggingPresets = TaggingPresetReader.readFromPreferences(false); 272 277 for (TaggingPreset tp: taggingPresets) { 273 278 if (!(tp instanceof TaggingPresetSeparator)) { -
trunk/src/org/openstreetmap/josm/gui/tagging/TagEditorPanel.java
r6064 r6068 193 193 private void updatePresets() { 194 194 presetListPanel.updatePresets( 195 EnumSet.of(TaggingPreset .PresetType.RELATION),195 EnumSet.of(TaggingPresetType.RELATION), 196 196 model.getTags(), presetHandler); 197 197 validate(); -
trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPreset.java
r5997 r6068 8 8 import java.awt.Component; 9 9 import java.awt.Dimension; 10 import java.awt.Font;11 10 import java.awt.GridBagLayout; 12 11 import java.awt.Insets; 13 12 import java.awt.event.ActionEvent; 14 import java.awt.event.ActionListener;15 import java.io.BufferedReader;16 import java.io.File;17 import java.io.IOException;18 import java.io.InputStream;19 import java.io.InputStreamReader;20 import java.io.Reader;21 import java.io.UnsupportedEncodingException;22 import java.lang.reflect.Method;23 import java.lang.reflect.Modifier;24 import java.text.NumberFormat;25 import java.text.ParseException;26 13 import java.util.ArrayList; 27 import java.util.Arrays;28 14 import java.util.Collection; 29 import java.util.Collections;30 15 import java.util.EnumSet; 31 import java.util.HashMap;32 16 import java.util.HashSet; 33 import java.util.LinkedHashMap;34 17 import java.util.LinkedList; 35 18 import java.util.List; 36 19 import java.util.Map; 37 import java.util.TreeSet;38 20 39 21 import javax.swing.AbstractAction; 40 22 import javax.swing.Action; 41 import javax.swing.ButtonGroup;42 23 import javax.swing.ImageIcon; 43 import javax.swing.JButton;44 import javax.swing.JComponent;45 24 import javax.swing.JLabel; 46 import javax.swing.JList;47 import javax.swing.JOptionPane;48 25 import javax.swing.JPanel; 49 import javax.swing.JScrollPane;50 import javax.swing.JToggleButton;51 import javax.swing.ListCellRenderer;52 import javax.swing.ListModel;53 26 import javax.swing.SwingUtilities; 54 27 … … 61 34 import org.openstreetmap.josm.data.osm.Node; 62 35 import org.openstreetmap.josm.data.osm.OsmPrimitive; 63 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;64 import org.openstreetmap.josm.data.osm.OsmUtils;65 36 import org.openstreetmap.josm.data.osm.Relation; 66 37 import org.openstreetmap.josm.data.osm.RelationMember; 67 38 import org.openstreetmap.josm.data.osm.Tag; 68 39 import org.openstreetmap.josm.data.osm.Way; 69 import org.openstreetmap.josm.data.preferences.BooleanProperty;70 40 import org.openstreetmap.josm.gui.ExtendedDialog; 71 41 import org.openstreetmap.josm.gui.MapView; 72 import org.openstreetmap.josm.gui.QuadStateCheckBox;73 42 import org.openstreetmap.josm.gui.dialogs.relation.RelationEditor; 74 43 import org.openstreetmap.josm.gui.layer.Layer; 75 import org.openstreetmap.josm.gui.layer.OsmDataLayer;76 import org.openstreetmap.josm.gui.preferences.SourceEntry;77 44 import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference; 78 import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference.PresetPrefHelper; 79 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField; 80 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionItemPritority; 81 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletionList; 45 import org.openstreetmap.josm.gui.tagging.TaggingPresetItems.Link; 46 import org.openstreetmap.josm.gui.tagging.TaggingPresetItems.Role; 47 import org.openstreetmap.josm.gui.tagging.TaggingPresetItems.Roles; 82 48 import org.openstreetmap.josm.gui.util.GuiHelper; 83 import org.openstreetmap.josm.gui.widgets.JosmComboBox;84 import org.openstreetmap.josm.gui.widgets.JosmTextField;85 import org.openstreetmap.josm.io.MirroredInputStream;86 49 import org.openstreetmap.josm.tools.GBC; 87 50 import org.openstreetmap.josm.tools.ImageProvider; 88 51 import org.openstreetmap.josm.tools.Predicate; 89 import org.openstreetmap.josm.tools.UrlLabel;90 52 import org.openstreetmap.josm.tools.Utils; 91 import org.openstreetmap.josm.tools.XmlObjectParser;92 53 import org.openstreetmap.josm.tools.template_engine.ParseError; 93 54 import org.openstreetmap.josm.tools.template_engine.TemplateEntry; … … 105 66 public class TaggingPreset extends AbstractAction implements MapView.LayerChangeListener { 106 67 107 public enum PresetType {108 NODE(/* ICON */"Mf_node", "node"),109 WAY(/* ICON */"Mf_way", "way"),110 RELATION(/* ICON */"Mf_relation", "relation"),111 CLOSEDWAY(/* ICON */"Mf_closedway", "closedway");112 113 private final String iconName;114 private final String name;115 116 PresetType(String iconName, String name) {117 this.iconName = iconName;118 this.name = name;119 }120 121 public String getIconName() {122 return iconName;123 }124 125 public String getName() {126 return name;127 }128 129 public static PresetType forPrimitive(OsmPrimitive p) {130 return forPrimitiveType(p.getDisplayType());131 }132 133 public static PresetType forPrimitiveType(OsmPrimitiveType type) {134 switch (type) {135 case NODE:136 return NODE;137 case WAY:138 return WAY;139 case CLOSEDWAY:140 return CLOSEDWAY;141 case RELATION:142 case MULTIPOLYGON:143 return RELATION;144 default:145 throw new IllegalArgumentException("Unexpected primitive type: " + type);146 }147 }148 149 public static PresetType fromString(String type) {150 for (PresetType t : PresetType.values()) {151 if (t.getName().equals(type))152 return t;153 }154 return null;155 }156 }157 158 /**159 * Enum denoting how a match (see {@link Item#matches}) is performed.160 */161 private enum MatchType {162 163 /**164 * Neutral, i.e., do not consider this item for matching.165 */166 NONE("none"),167 /**168 * Positive if key matches, neutral otherwise.169 */170 KEY("key"),171 /**172 * Positive if key matches, negative otherwise.173 */174 KEY_REQUIRED("key!"),175 /**176 * Positive if key and value matches, negative otherwise.177 */178 KEY_VALUE("keyvalue");179 180 private final String value;181 182 private MatchType(String value) {183 this.value = value;184 }185 186 public String getValue() {187 return value;188 }189 190 public static MatchType ofString(String type) {191 for (MatchType i : EnumSet.allOf(MatchType.class)) {192 if (i.getValue().equals(type))193 return i;194 }195 throw new IllegalArgumentException(type + " is not allowed");196 }197 }198 199 68 public static final int DIALOG_ANSWER_APPLY = 1; 200 69 public static final int DIALOG_ANSWER_NEW_RELATION = 2; … … 205 74 public String name_context; 206 75 public String locale_name; 207 public final static String OPTIONAL_TOOLTIP_TEXT = "Optional tooltip text"; 208 private static File zipIcons = null; 209 private static final BooleanProperty PROP_FILL_DEFAULT = new BooleanProperty("taggingpreset.fill-default-for-tagged-primitives", false); 210 211 public static abstract class Item { 212 213 protected void initAutoCompletionField(AutoCompletingTextField field, String key) { 214 OsmDataLayer layer = Main.main.getEditLayer(); 215 if (layer == null) 216 return; 217 AutoCompletionList list = new AutoCompletionList(); 218 Main.main.getEditLayer().data.getAutoCompletionManager().populateWithTagValues(list, key); 219 field.setAutoCompletionList(list); 220 } 221 222 abstract boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel); 223 224 abstract void addCommands(List<Tag> changedTags); 225 226 boolean requestFocusInWindow() { 227 return false; 228 } 229 230 /** 231 * Tests whether the tags match this item. 232 * Note that for a match, at least one positive and no negative is required. 233 * @param tags the tags of an {@link OsmPrimitive} 234 * @return {@code true} if matches (positive), {@code null} if neutral, {@code false} if mismatches (negative). 235 */ 236 Boolean matches(Map<String, String> tags) { 237 return null; 238 } 239 } 240 241 public static abstract class KeyedItem extends Item { 242 243 public String key; 244 public String text; 245 public String text_context; 246 public String match = getDefaultMatch().getValue(); 247 248 public abstract MatchType getDefaultMatch(); 249 public abstract Collection<String> getValues(); 250 251 @Override 252 Boolean matches(Map<String, String> tags) { 253 switch (MatchType.ofString(match)) { 254 case NONE: 255 return null; 256 case KEY: 257 return tags.containsKey(key) ? true : null; 258 case KEY_REQUIRED: 259 return tags.containsKey(key); 260 case KEY_VALUE: 261 return tags.containsKey(key) && (getValues().contains(tags.get(key))); 262 default: 263 throw new IllegalStateException(); 264 } 265 } 266 267 @Override 268 public String toString() { 269 return "KeyedItem [key=" + key + ", text=" + text 270 + ", text_context=" + text_context + ", match=" + match 271 + "]"; 272 } 273 } 274 275 public static class Usage { 276 TreeSet<String> values; 277 boolean hadKeys = false; 278 boolean hadEmpty = false; 279 public boolean hasUniqueValue() { 280 return values.size() == 1 && !hadEmpty; 281 } 282 283 public boolean unused() { 284 return values.isEmpty(); 285 } 286 public String getFirst() { 287 return values.first(); 288 } 289 290 public boolean hadKeys() { 291 return hadKeys; 292 } 293 } 294 295 public static final String DIFFERENT = tr("<different>"); 296 297 static Usage determineTextUsage(Collection<OsmPrimitive> sel, String key) { 298 Usage returnValue = new Usage(); 299 returnValue.values = new TreeSet<String>(); 300 for (OsmPrimitive s : sel) { 301 String v = s.get(key); 302 if (v != null) { 303 returnValue.values.add(v); 304 } else { 305 returnValue.hadEmpty = true; 306 } 307 if(s.hasKeys()) { 308 returnValue.hadKeys = true; 309 } 310 } 311 return returnValue; 312 } 313 314 static Usage determineBooleanUsage(Collection<OsmPrimitive> sel, String key) { 315 316 Usage returnValue = new Usage(); 317 returnValue.values = new TreeSet<String>(); 318 for (OsmPrimitive s : sel) { 319 String booleanValue = OsmUtils.getNamedOsmBoolean(s.get(key)); 320 if (booleanValue != null) { 321 returnValue.values.add(booleanValue); 322 } 323 } 324 return returnValue; 325 } 326 327 public static class PresetListEntry { 328 public String value; 329 public String value_context; 330 public String display_value; 331 public String short_description; 332 public String icon; 333 public String icon_size; 334 public String locale_display_value; 335 public String locale_short_description; 336 private final File zipIcons = TaggingPreset.zipIcons; 337 338 // Cached size (currently only for Combo) to speed up preset dialog initialization 339 private int prefferedWidth = -1; 340 private int prefferedHeight = -1; 341 342 public String getListDisplay() { 343 if (value.equals(DIFFERENT)) 344 return "<b>"+DIFFERENT.replaceAll("<", "<").replaceAll(">", ">")+"</b>"; 345 346 if (value.equals("")) 347 return " "; 348 349 final StringBuilder res = new StringBuilder("<b>"); 350 res.append(getDisplayValue(true)); 351 res.append("</b>"); 352 if (getShortDescription(true) != null) { 353 // wrap in table to restrict the text width 354 res.append("<div style=\"width:300px; padding:0 0 5px 5px\">"); 355 res.append(getShortDescription(true)); 356 res.append("</div>"); 357 } 358 return res.toString(); 359 } 360 361 public ImageIcon getIcon() { 362 return icon == null ? null : loadImageIcon(icon, zipIcons, parseInteger(icon_size)); 363 } 364 365 private Integer parseInteger(String str) { 366 if (str == null || "".equals(str)) 367 return null; 368 try { 369 return Integer.parseInt(str); 370 } catch (Exception e) { 371 // 372 } 373 return null; 374 } 375 376 public PresetListEntry() { 377 } 378 379 public PresetListEntry(String value) { 380 this.value = value; 381 } 382 383 public String getDisplayValue(boolean translated) { 384 return translated 385 ? Utils.firstNonNull(locale_display_value, tr(display_value), trc(value_context, value)) 386 : Utils.firstNonNull(display_value, value); 387 } 388 389 public String getShortDescription(boolean translated) { 390 return translated 391 ? Utils.firstNonNull(locale_short_description, tr(short_description)) 392 : short_description; 393 } 394 395 // toString is mainly used to initialize the Editor 396 @Override 397 public String toString() { 398 if (value.equals(DIFFERENT)) 399 return DIFFERENT; 400 return getDisplayValue(true).replaceAll("<.*>", ""); // remove additional markup, e.g. <br> 401 } 402 } 403 404 public static class Text extends KeyedItem { 405 406 public String locale_text; 407 public String default_; 408 public String originalValue; 409 public String use_last_as_default = "false"; 410 public String auto_increment; 411 public String length; 412 413 private JComponent value; 414 415 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) { 416 417 // find out if our key is already used in the selection. 418 Usage usage = determineTextUsage(sel, key); 419 AutoCompletingTextField textField = new AutoCompletingTextField(); 420 initAutoCompletionField(textField, key); 421 if (length != null && !length.isEmpty()) { 422 textField.setMaxChars(new Integer(length)); 423 } 424 if (usage.unused()){ 425 if (auto_increment_selected != 0 && auto_increment != null) { 426 try { 427 textField.setText(Integer.toString(Integer.parseInt(lastValue.get(key)) + auto_increment_selected)); 428 } catch (NumberFormatException ex) { 429 // Ignore - cannot auto-increment if last was non-numeric 430 } 431 } 432 else if (!usage.hadKeys() || PROP_FILL_DEFAULT.get() || "force".equals(use_last_as_default)) { 433 // selected osm primitives are untagged or filling default values feature is enabled 434 if (!"false".equals(use_last_as_default) && lastValue.containsKey(key)) { 435 textField.setText(lastValue.get(key)); 436 } else { 437 textField.setText(default_); 438 } 439 } else { 440 // selected osm primitives are tagged and filling default values feature is disabled 441 textField.setText(""); 442 } 443 value = textField; 444 originalValue = null; 445 } else if (usage.hasUniqueValue()) { 446 // all objects use the same value 447 textField.setText(usage.getFirst()); 448 value = textField; 449 originalValue = usage.getFirst(); 450 } else { 451 // the objects have different values 452 JosmComboBox comboBox = new JosmComboBox(usage.values.toArray()); 453 comboBox.setEditable(true); 454 comboBox.setEditor(textField); 455 comboBox.getEditor().setItem(DIFFERENT); 456 value=comboBox; 457 originalValue = DIFFERENT; 458 } 459 if (locale_text == null) { 460 if (text != null) { 461 if (text_context != null) { 462 locale_text = trc(text_context, fixPresetString(text)); 463 } else { 464 locale_text = tr(fixPresetString(text)); 465 } 466 } 467 } 468 469 // if there's an auto_increment setting, then wrap the text field 470 // into a panel, appending a number of buttons. 471 // auto_increment has a format like -2,-1,1,2 472 // the text box being the first component in the panel is relied 473 // on in a rather ugly fashion further down. 474 if (auto_increment != null) { 475 ButtonGroup bg = new ButtonGroup(); 476 JPanel pnl = new JPanel(new GridBagLayout()); 477 pnl.add(value, GBC.std().fill(GBC.HORIZONTAL)); 478 479 // first, one button for each auto_increment value 480 for (final String ai : auto_increment.split(",")) { 481 JToggleButton aibutton = new JToggleButton(ai); 482 aibutton.setToolTipText(tr("Select auto-increment of {0} for this field", ai)); 483 aibutton.setMargin(new java.awt.Insets(0,0,0,0)); 484 bg.add(aibutton); 485 try { 486 // TODO there must be a better way to parse a number like "+3" than this. 487 final int buttonvalue = ((Number)NumberFormat.getIntegerInstance().parse(ai.replace("+", ""))).intValue(); 488 if (auto_increment_selected == buttonvalue) aibutton.setSelected(true); 489 aibutton.addActionListener(new ActionListener() { 490 public void actionPerformed(ActionEvent e) { 491 auto_increment_selected = buttonvalue; 492 } 493 }); 494 pnl.add(aibutton, GBC.std()); 495 } catch (ParseException x) { 496 System.err.println("Cannot parse auto-increment value of '" + ai + "' into an integer"); 497 } 498 } 499 500 // an invisible toggle button for "release" of the button group 501 final JToggleButton clearbutton = new JToggleButton("X"); 502 clearbutton.setVisible(false); 503 bg.add(clearbutton); 504 // and its visible counterpart. - this mechanism allows us to 505 // have *no* button selected after the X is clicked, instead 506 // of the X remaining selected 507 JButton releasebutton = new JButton("X"); 508 releasebutton.setToolTipText(tr("Cancel auto-increment for this field")); 509 releasebutton.setMargin(new java.awt.Insets(0,0,0,0)); 510 releasebutton.addActionListener(new ActionListener() { 511 public void actionPerformed(ActionEvent e) { 512 auto_increment_selected = 0; 513 clearbutton.setSelected(true); 514 } 515 }); 516 pnl.add(releasebutton, GBC.eol()); 517 value = pnl; 518 } 519 p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0)); 520 p.add(value, GBC.eol().fill(GBC.HORIZONTAL)); 521 return true; 522 } 523 524 private static String getValue(Component comp) { 525 if (comp instanceof JosmComboBox) { 526 return ((JosmComboBox) comp).getEditor().getItem().toString(); 527 } else if (comp instanceof JosmTextField) { 528 return ((JosmTextField) comp).getText(); 529 } else if (comp instanceof JPanel) { 530 return getValue(((JPanel)comp).getComponent(0)); 531 } else { 532 return null; 533 } 534 } 535 536 @Override 537 public void addCommands(List<Tag> changedTags) { 538 539 // return if unchanged 540 String v = getValue(value); 541 if (v == null) { 542 System.err.println("No 'last value' support for component " + value); 543 return; 544 } 545 546 v = v.trim(); 547 548 if (!"false".equals(use_last_as_default) || auto_increment != null) { 549 lastValue.put(key, v); 550 } 551 if (v.equals(originalValue) || (originalValue == null && v.length() == 0)) 552 return; 553 554 changedTags.add(new Tag(key, v)); 555 } 556 557 @Override 558 boolean requestFocusInWindow() { 559 return value.requestFocusInWindow(); 560 } 561 562 @Override 563 public MatchType getDefaultMatch() { 564 return MatchType.NONE; 565 } 566 567 @Override 568 public Collection<String> getValues() { 569 if (default_ == null || default_.isEmpty()) 570 return Collections.emptyList(); 571 return Collections.singleton(default_); 572 } 573 } 574 575 public static class Check extends KeyedItem { 576 577 public String locale_text; 578 public String value_on = OsmUtils.trueval; 579 public String value_off = OsmUtils.falseval; 580 public boolean default_ = false; // only used for tagless objects 581 582 private QuadStateCheckBox check; 583 private QuadStateCheckBox.State initialState; 584 private boolean def; 585 586 @Override public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) { 587 588 // find out if our key is already used in the selection. 589 Usage usage = determineBooleanUsage(sel, key); 590 def = default_; 591 592 if(locale_text == null) { 593 if(text_context != null) { 594 locale_text = trc(text_context, fixPresetString(text)); 595 } else { 596 locale_text = tr(fixPresetString(text)); 597 } 598 } 599 600 String oneValue = null; 601 for (String s : usage.values) { 602 oneValue = s; 603 } 604 if (usage.values.size() < 2 && (oneValue == null || value_on.equals(oneValue) || value_off.equals(oneValue))) { 605 if (def && !PROP_FILL_DEFAULT.get()) { 606 // default is set and filling default values feature is disabled - check if all primitives are untagged 607 for (OsmPrimitive s : sel) 608 if(s.hasKeys()) { 609 def = false; 610 } 611 } 612 613 // all selected objects share the same value which is either true or false or unset, 614 // we can display a standard check box. 615 initialState = value_on.equals(oneValue) ? 616 QuadStateCheckBox.State.SELECTED : 617 value_off.equals(oneValue) ? 618 QuadStateCheckBox.State.NOT_SELECTED : 619 def ? QuadStateCheckBox.State.SELECTED 620 : QuadStateCheckBox.State.UNSET; 621 check = new QuadStateCheckBox(locale_text, initialState, 622 new QuadStateCheckBox.State[] { 623 QuadStateCheckBox.State.SELECTED, 624 QuadStateCheckBox.State.NOT_SELECTED, 625 QuadStateCheckBox.State.UNSET }); 626 } else { 627 def = false; 628 // the objects have different values, or one or more objects have something 629 // else than true/false. we display a quad-state check box 630 // in "partial" state. 631 initialState = QuadStateCheckBox.State.PARTIAL; 632 check = new QuadStateCheckBox(locale_text, QuadStateCheckBox.State.PARTIAL, 633 new QuadStateCheckBox.State[] { 634 QuadStateCheckBox.State.PARTIAL, 635 QuadStateCheckBox.State.SELECTED, 636 QuadStateCheckBox.State.NOT_SELECTED, 637 QuadStateCheckBox.State.UNSET }); 638 } 639 p.add(check, GBC.eol().fill(GBC.HORIZONTAL)); 640 return true; 641 } 642 643 @Override public void addCommands(List<Tag> changedTags) { 644 // if the user hasn't changed anything, don't create a command. 645 if (check.getState() == initialState && !def) return; 646 647 // otherwise change things according to the selected value. 648 changedTags.add(new Tag(key, 649 check.getState() == QuadStateCheckBox.State.SELECTED ? value_on : 650 check.getState() == QuadStateCheckBox.State.NOT_SELECTED ? value_off : 651 null)); 652 } 653 @Override boolean requestFocusInWindow() {return check.requestFocusInWindow();} 654 655 @Override 656 public MatchType getDefaultMatch() { 657 return MatchType.NONE; 658 } 659 660 @Override 661 public Collection<String> getValues() { 662 return Arrays.asList(value_on, value_off); 663 } 664 } 665 666 public static abstract class ComboMultiSelect extends KeyedItem { 667 668 public String locale_text; 669 public String values; 670 public String values_from; 671 public String values_context; 672 public String display_values; 673 public String locale_display_values; 674 public String short_descriptions; 675 public String locale_short_descriptions; 676 public String default_; 677 public String delimiter = ";"; 678 public String use_last_as_default = "false"; 679 680 protected JComponent component; 681 protected final Map<String, PresetListEntry> lhm = new LinkedHashMap<String, PresetListEntry>(); 682 private boolean initialized = false; 683 protected Usage usage; 684 protected Object originalValue; 685 686 protected abstract Object getSelectedItem(); 687 protected abstract void addToPanelAnchor(JPanel p, String def); 688 689 protected char getDelChar() { 690 return delimiter.isEmpty() ? ';' : delimiter.charAt(0); 691 } 692 693 @Override 694 public Collection<String> getValues() { 695 initListEntries(); 696 return lhm.keySet(); 697 } 698 699 public Collection<String> getDisplayValues() { 700 initListEntries(); 701 return Utils.transform(lhm.values(), new Utils.Function<PresetListEntry, String>() { 702 703 @Override 704 public String apply(PresetListEntry x) { 705 return x.getDisplayValue(true); 706 } 707 }); 708 } 709 710 @Override 711 public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) { 712 713 initListEntries(); 714 715 // find out if our key is already used in the selection. 716 usage = determineTextUsage(sel, key); 717 if (!usage.hasUniqueValue() && !usage.unused()) { 718 lhm.put(DIFFERENT, new PresetListEntry(DIFFERENT)); 719 } 720 721 p.add(new JLabel(tr("{0}:", locale_text)), GBC.std().insets(0, 0, 10, 0)); 722 addToPanelAnchor(p, default_); 723 724 return true; 725 726 } 727 728 private void initListEntries() { 729 if (initialized) { 730 lhm.remove(DIFFERENT); // possibly added in #addToPanel 731 return; 732 } else if (lhm.isEmpty()) { 733 initListEntriesFromAttributes(); 734 } else { 735 if (values != null) { 736 System.err.println(tr("Warning in tagging preset \"{0}-{1}\": " 737 + "Ignoring ''{2}'' attribute as ''{3}'' elements are given.", 738 key, text, "values", "list_entry")); 739 } 740 if (display_values != null || locale_display_values != null) { 741 System.err.println(tr("Warning in tagging preset \"{0}-{1}\": " 742 + "Ignoring ''{2}'' attribute as ''{3}'' elements are given.", 743 key, text, "display_values", "list_entry")); 744 } 745 if (short_descriptions != null || locale_short_descriptions != null) { 746 System.err.println(tr("Warning in tagging preset \"{0}-{1}\": " 747 + "Ignoring ''{2}'' attribute as ''{3}'' elements are given.", 748 key, text, "short_descriptions", "list_entry")); 749 } 750 for (PresetListEntry e : lhm.values()) { 751 if (e.value_context == null) { 752 e.value_context = values_context; 753 } 754 } 755 } 756 if (locale_text == null) { 757 locale_text = trc(text_context, fixPresetString(text)); 758 } 759 initialized = true; 760 } 761 762 private String[] initListEntriesFromAttributes() { 763 char delChar = getDelChar(); 764 765 String[] value_array = null; 766 767 if (values_from != null) { 768 String[] class_method = values_from.split("#"); 769 if (class_method != null && class_method.length == 2) { 770 try { 771 Method method = Class.forName(class_method[0]).getMethod(class_method[1]); 772 // Check method is public static String[] methodName(); 773 int mod = method.getModifiers(); 774 if (Modifier.isPublic(mod) && Modifier.isStatic(mod) 775 && method.getReturnType().equals(String[].class) && method.getParameterTypes().length == 0) { 776 value_array = (String[]) method.invoke(null); 777 } else { 778 System.err.println(tr("Broken tagging preset \"{0}-{1}\" - Java method given in ''values_from'' is not \"{2}\"", key, text, 779 "public static String[] methodName()")); 780 } 781 } catch (Exception e) { 782 System.err.println(tr("Broken tagging preset \"{0}-{1}\" - Java method given in ''values_from'' threw {2} ({3})", key, text, 783 e.getClass().getName(), e.getMessage())); 784 } 785 } 786 } 787 788 if (value_array == null) { 789 value_array = splitEscaped(delChar, values); 790 } 791 792 final String displ = Utils.firstNonNull(locale_display_values, display_values); 793 String[] display_array = displ == null ? value_array : splitEscaped(delChar, displ); 794 795 final String descr = Utils.firstNonNull(locale_short_descriptions, short_descriptions); 796 String[] short_descriptions_array = descr == null ? null : splitEscaped(delChar, descr); 797 798 if (display_array.length != value_array.length) { 799 System.err.println(tr("Broken tagging preset \"{0}-{1}\" - number of items in ''display_values'' must be the same as in ''values''", key, text)); 800 display_array = value_array; 801 } 802 803 if (short_descriptions_array != null && short_descriptions_array.length != value_array.length) { 804 System.err.println(tr("Broken tagging preset \"{0}-{1}\" - number of items in ''short_descriptions'' must be the same as in ''values''", key, text)); 805 short_descriptions_array = null; 806 } 807 808 for (int i = 0; i < value_array.length; i++) { 809 final PresetListEntry e = new PresetListEntry(value_array[i]); 810 e.locale_display_value = locale_display_values != null 811 ? display_array[i] 812 : trc(values_context, fixPresetString(display_array[i])); 813 if (short_descriptions_array != null) { 814 e.locale_short_description = locale_short_descriptions != null 815 ? short_descriptions_array[i] 816 : tr(fixPresetString(short_descriptions_array[i])); 817 } 818 lhm.put(value_array[i], e); 819 display_array[i] = e.getDisplayValue(true); 820 } 821 822 return display_array; 823 } 824 825 protected String getDisplayIfNull(String display) { 826 return display; 827 } 828 829 @Override 830 public void addCommands(List<Tag> changedTags) { 831 Object obj = getSelectedItem(); 832 String display = (obj == null) ? null : obj.toString(); 833 String value = null; 834 if (display == null) { 835 display = getDisplayIfNull(display); 836 } 837 838 if (display != null) { 839 for (String key : lhm.keySet()) { 840 String k = lhm.get(key).toString(); 841 if (k != null && k.equals(display)) { 842 value = key; 843 break; 844 } 845 } 846 if (value == null) { 847 value = display; 848 } 849 } else { 850 value = ""; 851 } 852 value = value.trim(); 853 854 // no change if same as before 855 if (originalValue == null) { 856 if (value.length() == 0) 857 return; 858 } else if (value.equals(originalValue.toString())) 859 return; 860 861 if (!"false".equals(use_last_as_default)) { 862 lastValue.put(key, value); 863 } 864 changedTags.add(new Tag(key, value)); 865 } 866 867 public void addListEntry(PresetListEntry e) { 868 lhm.put(e.value, e); 869 } 870 871 public void addListEntries(Collection<PresetListEntry> e) { 872 for (PresetListEntry i : e) { 873 addListEntry(i); 874 } 875 } 876 877 @Override 878 boolean requestFocusInWindow() { 879 return component.requestFocusInWindow(); 880 } 881 882 private static ListCellRenderer RENDERER = new ListCellRenderer() { 883 884 JLabel lbl = new JLabel(); 885 886 public Component getListCellRendererComponent( 887 JList list, 888 Object value, 889 int index, 890 boolean isSelected, 891 boolean cellHasFocus) { 892 PresetListEntry item = (PresetListEntry) value; 893 894 // Only return cached size, item is not shown 895 if (!list.isShowing() && item.prefferedWidth != -1 && item.prefferedHeight != -1) { 896 if (index == -1) { 897 lbl.setPreferredSize(new Dimension(item.prefferedWidth, 10)); 898 } else { 899 lbl.setPreferredSize(new Dimension(item.prefferedWidth, item.prefferedHeight)); 900 } 901 return lbl; 902 } 903 904 lbl.setPreferredSize(null); 905 906 907 if (isSelected) { 908 lbl.setBackground(list.getSelectionBackground()); 909 lbl.setForeground(list.getSelectionForeground()); 910 } else { 911 lbl.setBackground(list.getBackground()); 912 lbl.setForeground(list.getForeground()); 913 } 914 915 lbl.setOpaque(true); 916 lbl.setFont(lbl.getFont().deriveFont(Font.PLAIN)); 917 lbl.setText("<html>" + item.getListDisplay() + "</html>"); 918 lbl.setIcon(item.getIcon()); 919 lbl.setEnabled(list.isEnabled()); 920 921 // Cache size 922 item.prefferedWidth = lbl.getPreferredSize().width; 923 item.prefferedHeight = lbl.getPreferredSize().height; 924 925 // We do not want the editor to have the maximum height of all 926 // entries. Return a dummy with bogus height. 927 if (index == -1) { 928 lbl.setPreferredSize(new Dimension(lbl.getPreferredSize().width, 10)); 929 } 930 return lbl; 931 } 932 }; 933 934 935 protected ListCellRenderer getListCellRenderer() { 936 return RENDERER; 937 } 938 939 @Override 940 public MatchType getDefaultMatch() { 941 return MatchType.NONE; 942 } 943 } 944 945 public static class Combo extends ComboMultiSelect { 946 947 public boolean editable = true; 948 protected JosmComboBox combo; 949 public String length; 950 951 public Combo() { 952 delimiter = ","; 953 } 954 955 @Override 956 protected void addToPanelAnchor(JPanel p, String def) { 957 if (!usage.unused()) { 958 for (String s : usage.values) { 959 if (!lhm.containsKey(s)) { 960 lhm.put(s, new PresetListEntry(s)); 961 } 962 } 963 } 964 if (def != null && !lhm.containsKey(def)) { 965 lhm.put(def, new PresetListEntry(def)); 966 } 967 lhm.put("", new PresetListEntry("")); 968 969 combo = new JosmComboBox(lhm.values().toArray()); 970 component = combo; 971 combo.setRenderer(getListCellRenderer()); 972 combo.setEditable(editable); 973 combo.reinitialize(lhm.values()); 974 AutoCompletingTextField tf = new AutoCompletingTextField(); 975 initAutoCompletionField(tf, key); 976 if (length != null && !length.isEmpty()) { 977 tf.setMaxChars(new Integer(length)); 978 } 979 AutoCompletionList acList = tf.getAutoCompletionList(); 980 if (acList != null) { 981 acList.add(getDisplayValues(), AutoCompletionItemPritority.IS_IN_STANDARD); 982 } 983 combo.setEditor(tf); 984 985 if (usage.hasUniqueValue()) { 986 // all items have the same value (and there were no unset items) 987 originalValue = lhm.get(usage.getFirst()); 988 combo.setSelectedItem(originalValue); 989 } else if (def != null && usage.unused()) { 990 // default is set and all items were unset 991 if (!usage.hadKeys() || PROP_FILL_DEFAULT.get() || "force".equals(use_last_as_default)) { 992 // selected osm primitives are untagged or filling default feature is enabled 993 combo.setSelectedItem(lhm.get(def).getDisplayValue(true)); 994 } else { 995 // selected osm primitives are tagged and filling default feature is disabled 996 combo.setSelectedItem(""); 997 } 998 originalValue = lhm.get(DIFFERENT); 999 } else if (usage.unused()) { 1000 // all items were unset (and so is default) 1001 originalValue = lhm.get(""); 1002 if ("force".equals(use_last_as_default) && lastValue.containsKey(key)) { 1003 combo.setSelectedItem(lhm.get(lastValue.get(key))); 1004 } else { 1005 combo.setSelectedItem(originalValue); 1006 } 1007 } else { 1008 originalValue = lhm.get(DIFFERENT); 1009 combo.setSelectedItem(originalValue); 1010 } 1011 p.add(combo, GBC.eol().fill(GBC.HORIZONTAL)); 1012 1013 } 1014 1015 @Override 1016 protected Object getSelectedItem() { 1017 return combo.getSelectedItem(); 1018 1019 } 1020 1021 @Override 1022 protected String getDisplayIfNull(String display) { 1023 if (combo.isEditable()) 1024 return combo.getEditor().getItem().toString(); 1025 else 1026 return display; 1027 1028 } 1029 } 1030 1031 /** 1032 * Class that allows list values to be assigned and retrieved as a comma-delimited 1033 * string. 1034 */ 1035 public static class ConcatenatingJList extends JList { 1036 private String delimiter; 1037 public ConcatenatingJList(String del, Object[] o) { 1038 super(o); 1039 delimiter = del; 1040 } 1041 public void setSelectedItem(Object o) { 1042 if (o == null) { 1043 clearSelection(); 1044 } else { 1045 String s = o.toString(); 1046 TreeSet<String> parts = new TreeSet<String>(Arrays.asList(s.split(delimiter))); 1047 ListModel lm = getModel(); 1048 int[] intParts = new int[lm.getSize()]; 1049 int j = 0; 1050 for (int i = 0; i < lm.getSize(); i++) { 1051 if (parts.contains((((PresetListEntry)lm.getElementAt(i)).value))) { 1052 intParts[j++]=i; 1053 } 1054 } 1055 setSelectedIndices(Arrays.copyOf(intParts, j)); 1056 // check if we have actually managed to represent the full 1057 // value with our presets. if not, cop out; we will not offer 1058 // a selection list that threatens to ruin the value. 1059 setEnabled(Utils.join(delimiter, parts).equals(getSelectedItem())); 1060 } 1061 } 1062 public String getSelectedItem() { 1063 ListModel lm = getModel(); 1064 int[] si = getSelectedIndices(); 1065 StringBuilder builder = new StringBuilder(); 1066 for (int i=0; i<si.length; i++) { 1067 if (i>0) { 1068 builder.append(delimiter); 1069 } 1070 builder.append(((PresetListEntry)lm.getElementAt(si[i])).value); 1071 } 1072 return builder.toString(); 1073 } 1074 } 1075 1076 public static class MultiSelect extends ComboMultiSelect { 1077 1078 public long rows = -1; 1079 protected ConcatenatingJList list; 1080 1081 @Override 1082 protected void addToPanelAnchor(JPanel p, String def) { 1083 list = new ConcatenatingJList(delimiter, lhm.values().toArray()); 1084 component = list; 1085 ListCellRenderer renderer = getListCellRenderer(); 1086 list.setCellRenderer(renderer); 1087 1088 if (usage.hasUniqueValue() && !usage.unused()) { 1089 originalValue = usage.getFirst(); 1090 list.setSelectedItem(originalValue); 1091 } else if (def != null && !usage.hadKeys() || PROP_FILL_DEFAULT.get() || "force".equals(use_last_as_default)) { 1092 originalValue = DIFFERENT; 1093 list.setSelectedItem(def); 1094 } else if (usage.unused()) { 1095 originalValue = null; 1096 list.setSelectedItem(originalValue); 1097 } else { 1098 originalValue = DIFFERENT; 1099 list.setSelectedItem(originalValue); 1100 } 1101 1102 JScrollPane sp = new JScrollPane(list); 1103 // if a number of rows has been specified in the preset, 1104 // modify preferred height of scroll pane to match that row count. 1105 if (rows != -1) { 1106 double height = renderer.getListCellRendererComponent(list, 1107 new PresetListEntry("x"), 0, false, false).getPreferredSize().getHeight() * rows; 1108 sp.setPreferredSize(new Dimension((int) sp.getPreferredSize().getWidth(), (int) height)); 1109 } 1110 p.add(sp, GBC.eol().fill(GBC.HORIZONTAL)); 1111 1112 1113 } 1114 1115 @Override 1116 protected Object getSelectedItem() { 1117 return list.getSelectedItem(); 1118 } 1119 1120 @Override 1121 public void addCommands(List<Tag> changedTags) { 1122 // Do not create any commands if list has been disabled because of an unknown value (fix #8605) 1123 if (list.isEnabled()) { 1124 super.addCommands(changedTags); 1125 } 1126 } 1127 } 1128 1129 /** 1130 * allow escaped comma in comma separated list: 1131 * "A\, B\, C,one\, two" --> ["A, B, C", "one, two"] 1132 * @param delimiter the delimiter, e.g. a comma. separates the entries and 1133 * must be escaped within one entry 1134 * @param s the string 1135 */ 1136 private static String[] splitEscaped(char delimiter, String s) { 1137 if (s == null) 1138 return new String[0]; 1139 List<String> result = new ArrayList<String>(); 1140 boolean backslash = false; 1141 StringBuilder item = new StringBuilder(); 1142 for (int i=0; i<s.length(); i++) { 1143 char ch = s.charAt(i); 1144 if (backslash) { 1145 item.append(ch); 1146 backslash = false; 1147 } else if (ch == '\\') { 1148 backslash = true; 1149 } else if (ch == delimiter) { 1150 result.add(item.toString()); 1151 item.setLength(0); 1152 } else { 1153 item.append(ch); 1154 } 1155 } 1156 if (item.length() > 0) { 1157 result.add(item.toString()); 1158 } 1159 return result.toArray(new String[result.size()]); 1160 } 1161 1162 public static class Label extends Item { 1163 1164 public String text; 1165 public String text_context; 1166 public String locale_text; 1167 1168 @Override 1169 public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) { 1170 if (locale_text == null) { 1171 if (text_context != null) { 1172 locale_text = trc(text_context, fixPresetString(text)); 1173 } else { 1174 locale_text = tr(fixPresetString(text)); 1175 } 1176 } 1177 p.add(new JLabel(locale_text), GBC.eol()); 1178 return false; 1179 } 1180 1181 @Override 1182 public void addCommands(List<Tag> changedTags) { 1183 } 1184 } 1185 1186 public static class Link extends Item { 1187 1188 public String href; 1189 public String text; 1190 public String text_context; 1191 public String locale_text; 1192 public String locale_href; 1193 1194 @Override 1195 public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) { 1196 if (locale_text == null) { 1197 if (text == null) { 1198 locale_text = tr("More information about this feature"); 1199 } else if (text_context != null) { 1200 locale_text = trc(text_context, fixPresetString(text)); 1201 } else { 1202 locale_text = tr(fixPresetString(text)); 1203 } 1204 } 1205 String url = locale_href; 1206 if (url == null) { 1207 url = href; 1208 } 1209 if (url != null) { 1210 p.add(new UrlLabel(url, locale_text, 2), GBC.eol().anchor(GBC.WEST)); 1211 } 1212 return false; 1213 } 1214 1215 @Override 1216 public void addCommands(List<Tag> changedTags) { 1217 } 1218 } 1219 1220 public static class Role { 1221 public EnumSet<PresetType> types; 1222 public String key; 1223 public String text; 1224 public String text_context; 1225 public String locale_text; 1226 public Match memberExpression; 1227 1228 public boolean required = false; 1229 public long count = 0; 1230 1231 public void setType(String types) throws SAXException { 1232 this.types = TaggingPreset.getType(types); 1233 } 1234 1235 public void setRequisite(String str) throws SAXException { 1236 if("required".equals(str)) { 1237 required = true; 1238 } else if(!"optional".equals(str)) 1239 throw new SAXException(tr("Unknown requisite: {0}", str)); 1240 } 1241 1242 public void setMember_expression(String member_expression) throws SAXException { 1243 try { 1244 this.memberExpression = SearchCompiler.compile(member_expression, true, true); 1245 } catch (SearchCompiler.ParseError ex) { 1246 throw new SAXException(tr("Illegal member expression: {0}", ex.getMessage()), ex); 1247 } 1248 } 1249 1250 /* return either argument, the highest possible value or the lowest 1251 allowed value */ 1252 public long getValidCount(long c) 1253 { 1254 if(count > 0 && !required) 1255 return c != 0 ? count : 0; 1256 else if(count > 0) 1257 return count; 1258 else if(!required) 1259 return c != 0 ? c : 0; 1260 else 1261 return c != 0 ? c : 1; 1262 } 1263 public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) { 1264 String cstring; 1265 if(count > 0 && !required) { 1266 cstring = "0,"+String.valueOf(count); 1267 } else if(count > 0) { 1268 cstring = String.valueOf(count); 1269 } else if(!required) { 1270 cstring = "0-..."; 1271 } else { 1272 cstring = "1-..."; 1273 } 1274 if(locale_text == null) { 1275 if (text != null) { 1276 if(text_context != null) { 1277 locale_text = trc(text_context, fixPresetString(text)); 1278 } else { 1279 locale_text = tr(fixPresetString(text)); 1280 } 1281 } 1282 } 1283 p.add(new JLabel(locale_text+":"), GBC.std().insets(0,0,10,0)); 1284 p.add(new JLabel(key), GBC.std().insets(0,0,10,0)); 1285 p.add(new JLabel(cstring), types == null ? GBC.eol() : GBC.std().insets(0,0,10,0)); 1286 if(types != null){ 1287 JPanel pp = new JPanel(); 1288 for(PresetType t : types) { 1289 pp.add(new JLabel(ImageProvider.get(t.getIconName()))); 1290 } 1291 p.add(pp, GBC.eol()); 1292 } 1293 return true; 1294 } 1295 } 1296 1297 public static class Roles extends Item { 1298 1299 public final List<Role> roles = new LinkedList<Role>(); 1300 1301 @Override 1302 public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) { 1303 p.add(new JLabel(" "), GBC.eol()); // space 1304 if (roles.size() > 0) { 1305 JPanel proles = new JPanel(new GridBagLayout()); 1306 proles.add(new JLabel(tr("Available roles")), GBC.std().insets(0, 0, 10, 0)); 1307 proles.add(new JLabel(tr("role")), GBC.std().insets(0, 0, 10, 0)); 1308 proles.add(new JLabel(tr("count")), GBC.std().insets(0, 0, 10, 0)); 1309 proles.add(new JLabel(tr("elements")), GBC.eol()); 1310 for (Role i : roles) { 1311 i.addToPanel(proles, sel); 1312 } 1313 p.add(proles, GBC.eol()); 1314 } 1315 return false; 1316 } 1317 1318 @Override 1319 public void addCommands(List<Tag> changedTags) { 1320 } 1321 } 1322 1323 public static class Optional extends Item { 1324 1325 // TODO: Draw a box around optional stuff 1326 @Override 1327 public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) { 1328 p.add(new JLabel(" "), GBC.eol()); // space 1329 p.add(new JLabel(tr("Optional Attributes:")), GBC.eol()); 1330 p.add(new JLabel(" "), GBC.eol()); // space 1331 return false; 1332 } 1333 1334 @Override 1335 public void addCommands(List<Tag> changedTags) { 1336 } 1337 } 1338 1339 public static class Space extends Item { 1340 1341 @Override 1342 public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) { 1343 p.add(new JLabel(" "), GBC.eol()); // space 1344 return false; 1345 } 1346 1347 @Override 1348 public void addCommands(List<Tag> changedTags) { 1349 } 1350 } 1351 1352 public static class Key extends KeyedItem { 1353 1354 public String value; 1355 1356 @Override 1357 public boolean addToPanel(JPanel p, Collection<OsmPrimitive> sel) { 1358 return false; 1359 } 1360 1361 @Override 1362 public void addCommands(List<Tag> changedTags) { 1363 changedTags.add(new Tag(key, value)); 1364 } 1365 1366 @Override 1367 public MatchType getDefaultMatch() { 1368 return MatchType.KEY_VALUE; 1369 } 1370 1371 @Override 1372 public Collection<String> getValues() { 1373 return Collections.singleton(value); 1374 } 1375 1376 @Override 1377 public String toString() { 1378 return "Key [key=" + key + ", value=" + value + ", text=" + text 1379 + ", text_context=" + text_context + ", match=" + match 1380 + "]"; 1381 } 1382 } 1383 76 public final static String OPTIONAL_TOOLTIP_TEXT = "Optional tooltip text"; 77 1384 78 /** 1385 79 * The types as preparsed collection. 1386 80 */ 1387 public EnumSet< PresetType> types;1388 public List< Item> data = new LinkedList<Item>();81 public EnumSet<TaggingPresetType> types; 82 public List<TaggingPresetItem> data = new LinkedList<TaggingPresetItem>(); 1389 83 public Roles roles; 1390 84 public TemplateEntry nameTemplate; 1391 85 public Match nameTemplateFilter; 1392 private static final HashMap<String,String> lastValue = new HashMap<String,String>(); 1393 private static int auto_increment_selected = 0; 1394 86 1395 87 /** 1396 88 * Create an empty tagging preset. This will not have any items and … … 1417 109 if(locale_name == null) { 1418 110 if(name_context != null) { 1419 locale_name = trc(name_context, fixPresetString(name));111 locale_name = trc(name_context, TaggingPresetItems.fixPresetString(name)); 1420 112 } else { 1421 locale_name = tr( fixPresetString(name));113 locale_name = tr(TaggingPresetItems.fixPresetString(name)); 1422 114 } 1423 115 } … … 1430 122 public String getRawName() { 1431 123 return group != null ? group.getRawName() + "/" + name : name; 1432 }1433 1434 protected static ImageIcon loadImageIcon(String iconName, File zipIcons, Integer maxSize) {1435 final Collection<String> s = Main.pref.getCollection("taggingpreset.icon.sources", null);1436 ImageProvider imgProv = new ImageProvider(iconName).setDirs(s).setId("presets").setArchive(zipIcons).setOptional(true);1437 if (maxSize != null) {1438 imgProv.setMaxSize(maxSize);1439 }1440 return imgProv.get();1441 124 } 1442 125 … … 1453 136 imgProv.setDirs(s); 1454 137 imgProv.setId("presets"); 1455 imgProv.setArchive(TaggingPreset .zipIcons);138 imgProv.setArchive(TaggingPresetReader.getZipIcons()); 1456 139 imgProv.setOptional(true); 1457 140 imgProv.setMaxWidth(16).setMaxHeight(16); … … 1473 156 } 1474 157 1475 // cache the parsing of types using a LRU cache (http://java-planet.blogspot.com/2005/08/how-to-set-up-simple-lru-cache-using.html)1476 private static final Map<String,EnumSet<PresetType>> typeCache =1477 new LinkedHashMap<String, EnumSet<PresetType>>(16, 1.1f, true);1478 1479 static public EnumSet<PresetType> getType(String types) throws SAXException {1480 if (typeCache.containsKey(types))1481 return typeCache.get(types);1482 EnumSet<PresetType> result = EnumSet.noneOf(PresetType.class);1483 for (String type : Arrays.asList(types.split(","))) {1484 try {1485 PresetType presetType = PresetType.fromString(type);1486 result.add(presetType);1487 } catch (IllegalArgumentException e) {1488 throw new SAXException(tr("Unknown type: {0}", type));1489 }1490 }1491 typeCache.put(types, result);1492 return result;1493 }1494 1495 158 /* 1496 159 * Called from the XML parser to set the types this preset affects. 1497 160 */ 1498 161 public void setType(String types) throws SAXException { 1499 this.types = getType(types);162 this.types = TaggingPresetItems.getType(types); 1500 163 } 1501 164 … … 1518 181 } 1519 182 1520 1521 public static List<TaggingPreset> readAll(Reader in, boolean validate) throws SAXException {1522 XmlObjectParser parser = new XmlObjectParser();1523 parser.mapOnStart("item", TaggingPreset.class);1524 parser.mapOnStart("separator", TaggingPresetSeparator.class);1525 parser.mapBoth("group", TaggingPresetMenu.class);1526 parser.map("text", Text.class);1527 parser.map("link", Link.class);1528 parser.mapOnStart("optional", Optional.class);1529 parser.mapOnStart("roles", Roles.class);1530 parser.map("role", Role.class);1531 parser.map("check", Check.class);1532 parser.map("combo", Combo.class);1533 parser.map("multiselect", MultiSelect.class);1534 parser.map("label", Label.class);1535 parser.map("space", Space.class);1536 parser.map("key", Key.class);1537 parser.map("list_entry", PresetListEntry.class);1538 LinkedList<TaggingPreset> all = new LinkedList<TaggingPreset>();1539 TaggingPresetMenu lastmenu = null;1540 Roles lastrole = null;1541 List<PresetListEntry> listEntries = new LinkedList<PresetListEntry>();1542 1543 if (validate) {1544 parser.startWithValidation(in, "http://josm.openstreetmap.de/tagging-preset-1.0", "resource://data/tagging-preset.xsd");1545 } else {1546 parser.start(in);1547 }1548 while(parser.hasNext()) {1549 Object o = parser.next();1550 if (o instanceof TaggingPresetMenu) {1551 TaggingPresetMenu tp = (TaggingPresetMenu) o;1552 if(tp == lastmenu) {1553 lastmenu = tp.group;1554 } else1555 {1556 tp.group = lastmenu;1557 tp.setDisplayName();1558 lastmenu = tp;1559 all.add(tp);1560 1561 }1562 lastrole = null;1563 } else if (o instanceof TaggingPresetSeparator) {1564 TaggingPresetSeparator tp = (TaggingPresetSeparator) o;1565 tp.group = lastmenu;1566 all.add(tp);1567 lastrole = null;1568 } else if (o instanceof TaggingPreset) {1569 TaggingPreset tp = (TaggingPreset) o;1570 tp.group = lastmenu;1571 tp.setDisplayName();1572 all.add(tp);1573 lastrole = null;1574 } else {1575 if (all.size() != 0) {1576 if (o instanceof Roles) {1577 all.getLast().data.add((Item) o);1578 if (all.getLast().roles != null) {1579 throw new SAXException(tr("Roles cannot appear more than once"));1580 }1581 all.getLast().roles = (Roles) o;1582 lastrole = (Roles) o;1583 } else if (o instanceof Role) {1584 if (lastrole == null)1585 throw new SAXException(tr("Preset role element without parent"));1586 lastrole.roles.add((Role) o);1587 } else if (o instanceof PresetListEntry) {1588 listEntries.add((PresetListEntry) o);1589 } else {1590 all.getLast().data.add((Item) o);1591 if (o instanceof ComboMultiSelect) {1592 ((ComboMultiSelect) o).addListEntries(listEntries);1593 } else if (o instanceof Key) {1594 if (((Key) o).value == null) {1595 ((Key) o).value = ""; // Fix #85301596 }1597 }1598 listEntries = new LinkedList<PresetListEntry>();1599 lastrole = null;1600 }1601 } else1602 throw new SAXException(tr("Preset sub element without parent"));1603 }1604 }1605 return all;1606 }1607 1608 public static Collection<TaggingPreset> readAll(String source, boolean validate) throws SAXException, IOException {1609 Collection<TaggingPreset> tp;1610 MirroredInputStream s = new MirroredInputStream(source);1611 try {1612 InputStream zip = s.getZipEntry("xml","preset");1613 if(zip != null) {1614 zipIcons = s.getFile();1615 }1616 InputStreamReader r;1617 try {1618 r = new InputStreamReader(zip == null ? s : zip, "UTF-8");1619 } catch (UnsupportedEncodingException e) {1620 r = new InputStreamReader(zip == null ? s: zip);1621 }1622 try {1623 tp = TaggingPreset.readAll(new BufferedReader(r), validate);1624 } finally {1625 Utils.close(r);1626 }1627 } finally {1628 Utils.close(s);1629 }1630 return tp;1631 }1632 1633 public static Collection<TaggingPreset> readAll(Collection<String> sources, boolean validate) {1634 LinkedList<TaggingPreset> allPresets = new LinkedList<TaggingPreset>();1635 for(String source : sources) {1636 try {1637 allPresets.addAll(TaggingPreset.readAll(source, validate));1638 } catch (IOException e) {1639 System.err.println(e.getClass().getName()+": "+e.getMessage());1640 System.err.println(source);1641 JOptionPane.showMessageDialog(1642 Main.parent,1643 tr("Could not read tagging preset source: {0}",source),1644 tr("Error"),1645 JOptionPane.ERROR_MESSAGE1646 );1647 } catch (SAXException e) {1648 System.err.println(e.getClass().getName()+": "+e.getMessage());1649 System.err.println(source);1650 JOptionPane.showMessageDialog(1651 Main.parent,1652 tr("Error parsing {0}: ", source)+e.getMessage(),1653 tr("Error"),1654 JOptionPane.ERROR_MESSAGE1655 );1656 }1657 }1658 return allPresets;1659 }1660 1661 public static LinkedList<String> getPresetSources() {1662 LinkedList<String> sources = new LinkedList<String>();1663 1664 for (SourceEntry e : (new PresetPrefHelper()).get()) {1665 sources.add(e.url);1666 }1667 1668 return sources;1669 }1670 1671 public static Collection<TaggingPreset> readFromPreferences(boolean validate) {1672 return readAll(getPresetSources(), validate);1673 }1674 1675 183 private static class PresetPanel extends JPanel { 1676 184 boolean hasElements = false; … … 1685 193 return null; 1686 194 PresetPanel p = new PresetPanel(); 1687 LinkedList< Item> l = new LinkedList<Item>();195 LinkedList<TaggingPresetItem> l = new LinkedList<TaggingPresetItem>(); 1688 196 if(types != null){ 1689 197 JPanel pp = new JPanel(); 1690 for( PresetType t : types){198 for(TaggingPresetType t : types){ 1691 199 JLabel la = new JLabel(ImageProvider.get(t.getIconName())); 1692 200 la.setToolTipText(tr("Elements of type {0} are supported.", tr(t.getName()))); … … 1697 205 1698 206 JPanel items = new JPanel(new GridBagLayout()); 1699 for ( Item i : data){207 for (TaggingPresetItem i : data){ 1700 208 if(i instanceof Link) { 1701 209 l.add(i); … … 1711 219 } 1712 220 1713 for( Item link : l) {221 for(TaggingPresetItem link : l) { 1714 222 link.addToPanel(p, selected); 1715 223 } … … 1720 228 public boolean isShowable() 1721 229 { 1722 for( Item i : data)230 for(TaggingPresetItem i : data) 1723 231 { 1724 if(!(i instanceof Optional || i instanceof Space || i instanceofKey))232 if(!(i instanceof TaggingPresetItems.Optional || i instanceof TaggingPresetItems.Space || i instanceof TaggingPresetItems.Key)) 1725 233 return true; 1726 234 } … … 1732 240 for (Role i : roles.roles) { 1733 241 if (i.memberExpression != null && i.memberExpression.match(osm) 1734 && (i.types == null || i.types.isEmpty() || i.types.contains( PresetType.forPrimitive(osm)) )) {242 && (i.types == null || i.types.isEmpty() || i.types.contains(TaggingPresetType.forPrimitive(osm)) )) { 1735 243 return i.key; 1736 244 } … … 1740 248 } 1741 249 250 @Override 1742 251 public void actionPerformed(ActionEvent e) { 1743 252 if (Main.main == null) return; … … 1747 256 int answer = showDialog(sel, supportsRelation()); 1748 257 1749 if ( sel.size() != 0&& answer == DIALOG_ANSWER_APPLY) {258 if (!sel.isEmpty() && answer == DIALOG_ANSWER_APPLY) { 1750 259 Command cmd = createCommand(sel, getChangedTags()); 1751 260 if (cmd != null) { … … 1781 290 1782 291 int answer = 1; 1783 if (p.getComponentCount() != 0 && (sel. size() == 0|| p.hasElements)) {292 if (p.getComponentCount() != 0 && (sel.isEmpty() || p.hasElements)) { 1784 293 String title = trn("Change {0} object", "Change {0} objects", sel.size(), sel.size()); 1785 if(sel. size() == 0) {294 if(sel.isEmpty()) { 1786 295 if(originalSelectionEmpty) { 1787 296 title = tr("Nothing selected!"); … … 1849 358 if(osm instanceof Relation) 1850 359 { 1851 if(!types.contains( PresetType.RELATION) &&1852 !(types.contains( PresetType.CLOSEDWAY) && ((Relation)osm).isMultipolygon())) {360 if(!types.contains(TaggingPresetType.RELATION) && 361 !(types.contains(TaggingPresetType.CLOSEDWAY) && ((Relation)osm).isMultipolygon())) { 1853 362 continue; 1854 363 } … … 1856 365 else if(osm instanceof Node) 1857 366 { 1858 if(!types.contains( PresetType.NODE)) {367 if(!types.contains(TaggingPresetType.NODE)) { 1859 368 continue; 1860 369 } … … 1862 371 else if(osm instanceof Way) 1863 372 { 1864 if(!types.contains( PresetType.WAY) &&1865 !(types.contains( PresetType.CLOSEDWAY) && ((Way)osm).isClosed())) {373 if(!types.contains(TaggingPresetType.WAY) && 374 !(types.contains(TaggingPresetType.CLOSEDWAY) && ((Way)osm).isClosed())) { 1866 375 continue; 1867 376 } … … 1875 384 public List<Tag> getChangedTags() { 1876 385 List<Tag> result = new ArrayList<Tag>(); 1877 for ( Item i: data) {386 for (TaggingPresetItem i: data) { 1878 387 i.addCommands(result); 1879 388 } 1880 389 return result; 1881 }1882 1883 private static String fixPresetString(String s) {1884 return s == null ? s : s.replaceAll("'","''");1885 390 } 1886 391 … … 1900 405 1901 406 private boolean supportsRelation() { 1902 return types == null || types.contains( PresetType.RELATION);407 return types == null || types.contains(TaggingPresetType.RELATION); 1903 408 } 1904 409 … … 1907 412 } 1908 413 414 @Override 1909 415 public void activeLayerChange(Layer oldLayer, Layer newLayer) { 1910 416 updateEnabledState(); 1911 417 } 1912 418 419 @Override 1913 420 public void layerAdded(Layer newLayer) { 1914 421 updateEnabledState(); 1915 422 } 1916 423 424 @Override 1917 425 public void layerRemoved(Layer oldLayer) { 1918 426 updateEnabledState(); … … 1924 432 } 1925 433 1926 public boolean typeMatches(Collection< PresetType> t) {434 public boolean typeMatches(Collection<TaggingPresetType> t) { 1927 435 return t == null || types == null || types.containsAll(t); 1928 436 } 1929 437 1930 public boolean matches(Collection< PresetType> t, Map<String, String> tags, boolean onlyShowable) {438 public boolean matches(Collection<TaggingPresetType> t, Map<String, String> tags, boolean onlyShowable) { 1931 439 if (onlyShowable && !isShowable()) 1932 440 return false; … … 1934 442 return false; 1935 443 boolean atLeastOnePositiveMatch = false; 1936 for ( Item item : data) {444 for (TaggingPresetItem item : data) { 1937 445 Boolean m = item.matches(tags); 1938 446 if (m != null && !m) … … 1945 453 } 1946 454 1947 public static Collection<TaggingPreset> getMatchingPresets(final Collection< PresetType> t, final Map<String, String> tags, final boolean onlyShowable) {455 public static Collection<TaggingPreset> getMatchingPresets(final Collection<TaggingPresetType> t, final Map<String, String> tags, final boolean onlyShowable) { 1948 456 return Utils.filter(TaggingPresetPreference.taggingPresets, new Predicate<TaggingPreset>() { 1949 457 @Override -
trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPresetMenu.java
r5954 r6068 22 22 public class TaggingPresetMenu extends TaggingPreset { 23 23 public JMenu menu = null; // set by TaggingPresetPreferences 24 @Override 24 25 public void setDisplayName() { 25 26 putValue(Action.NAME, getName()); … … 30 31 putValue("toolbar", "tagginggroup_" + getRawName()); 31 32 } 33 @Override 32 34 public void setIcon(String iconName) { 33 35 super.setIcon(iconName); … … 55 57 } 56 58 59 @Override 57 60 public void actionPerformed(ActionEvent e) { 58 61 Object s = e.getSource(); -
trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPresetSearchDialog.java
r5899 r6068 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import java.awt.BorderLayout;7 import java.awt.Component;8 import java.awt.Dimension;9 6 import java.awt.event.ActionEvent; 10 import java.awt.event.ItemEvent; 11 import java.awt.event.ItemListener; 12 import java.awt.event.KeyAdapter; 13 import java.awt.event.KeyEvent; 14 import java.awt.event.MouseAdapter; 15 import java.awt.event.MouseEvent; 16 import java.util.ArrayList; 17 import java.util.Collection; 18 import java.util.Collections; 19 import java.util.EnumSet; 20 import java.util.HashSet; 21 import java.util.List; 22 23 import javax.swing.AbstractListModel; 24 import javax.swing.Action; 25 import javax.swing.BoxLayout; 26 import javax.swing.DefaultListCellRenderer; 27 import javax.swing.Icon; 28 import javax.swing.JCheckBox; 29 import javax.swing.JLabel; 30 import javax.swing.JList; 31 import javax.swing.JPanel; 32 import javax.swing.JScrollPane; 33 import javax.swing.event.DocumentEvent; 34 import javax.swing.event.DocumentListener; 7 import java.awt.event.ActionListener; 35 8 36 9 import org.openstreetmap.josm.Main; 37 import org.openstreetmap.josm.data.SelectionChangedListener;38 10 import org.openstreetmap.josm.data.osm.DataSet; 39 import org.openstreetmap.josm.data.osm.Node;40 import org.openstreetmap.josm.data.osm.OsmPrimitive;41 import org.openstreetmap.josm.data.osm.Relation;42 import org.openstreetmap.josm.data.osm.Way;43 import org.openstreetmap.josm.data.preferences.BooleanProperty;44 11 import org.openstreetmap.josm.gui.ExtendedDialog; 45 import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference;46 import org.openstreetmap.josm.gui.tagging.TaggingPreset.Item;47 import org.openstreetmap.josm.gui.tagging.TaggingPreset.Key;48 import org.openstreetmap.josm.gui.tagging.TaggingPreset.PresetType;49 import org.openstreetmap.josm.gui.tagging.TaggingPreset.Role;50 import org.openstreetmap.josm.gui.tagging.TaggingPreset.Roles;51 import org.openstreetmap.josm.gui.widgets.JosmTextField;52 12 53 13 54 public class TaggingPresetSearchDialog extends ExtendedDialog implements SelectionChangedListener{14 public class TaggingPresetSearchDialog extends ExtendedDialog { 55 15 56 private static final int CLASSIFICATION_IN_FAVORITES = 300; 57 private static final int CLASSIFICATION_NAME_MATCH = 300; 58 private static final int CLASSIFICATION_GROUP_MATCH = 200; 59 private static final int CLASSIFICATION_TAGS_MATCH = 100; 60 61 private static final BooleanProperty SEARCH_IN_TAGS = new BooleanProperty("taggingpreset.dialog.search-in-tags", true); 62 private static final BooleanProperty ONLY_APPLICABLE = new BooleanProperty("taggingpreset.dialog.only-applicable-to-selection", true); 63 64 private static class ResultListCellRenderer extends DefaultListCellRenderer { 65 @Override 66 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, 67 boolean cellHasFocus) { 68 JLabel result = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); 69 TaggingPreset tp = (TaggingPreset)value; 70 result.setText(tp.getName()); 71 result.setIcon((Icon) tp.getValue(Action.SMALL_ICON)); 72 return result; 73 } 74 } 75 76 private static class ResultListModel extends AbstractListModel { 77 78 private List<PresetClasification> presets = new ArrayList<PresetClasification>(); 79 80 public void setPresets(List<PresetClasification> presets) { 81 this.presets = presets; 82 fireContentsChanged(this, 0, Integer.MAX_VALUE); 83 } 84 85 public List<PresetClasification> getPresets() { 86 return presets; 87 } 88 89 @Override 90 public Object getElementAt(int index) { 91 return presets.get(index).preset; 92 } 93 94 @Override 95 public int getSize() { 96 return presets.size(); 97 } 98 99 } 100 101 private static class PresetClasification implements Comparable<PresetClasification> { 102 public final TaggingPreset preset; 103 public int classification; 104 public int favoriteIndex; 105 private final Collection<String> groups = new HashSet<String>(); 106 private final Collection<String> names = new HashSet<String>(); 107 private final Collection<String> tags = new HashSet<String>(); 108 109 PresetClasification(TaggingPreset preset) { 110 this.preset = preset; 111 TaggingPreset group = preset.group; 112 while (group != null) { 113 for (String word: group.getLocaleName().toLowerCase().split("\\s")) { 114 groups.add(word); 115 } 116 group = group.group; 117 } 118 for (String word: preset.getLocaleName().toLowerCase().split("\\s")) { 119 names.add(word); 120 } 121 for (Item item: preset.data) { 122 if (item instanceof TaggingPreset.KeyedItem) { 123 tags.add(((TaggingPreset.KeyedItem) item).key); 124 // Should combo values also be added? 125 if (item instanceof Key && ((Key) item).value != null) { 126 tags.add(((Key) item).value); 127 } 128 } else if (item instanceof Roles) { 129 for (Role role : ((Roles) item).roles) { 130 tags.add(role.key); 131 } 132 } 133 } 134 } 135 136 private int isMatching(Collection<String> values, String[] searchString) { 137 int sum = 0; 138 for (String word: searchString) { 139 boolean found = false; 140 boolean foundFirst = false; 141 for (String value: values) { 142 int index = value.indexOf(word); 143 if (index == 0) { 144 foundFirst = true; 145 break; 146 } else if (index > 0) { 147 found = true; 148 } 149 } 150 if (foundFirst) { 151 sum += 2; 152 } else if (found) { 153 sum += 1; 154 } else 155 return 0; 156 } 157 return sum; 158 } 159 160 int isMatchingGroup(String[] words) { 161 return isMatching(groups, words); 162 } 163 164 int isMatchingName(String[] words) { 165 return isMatching(names, words); 166 } 167 168 int isMatchingTags(String[] words) { 169 return isMatching(tags, words); 170 } 171 172 @Override 173 public int compareTo(PresetClasification o) { 174 int result = o.classification - classification; 175 if (result == 0) 176 return preset.getName().compareTo(o.preset.getName()); 177 else 178 return result; 179 } 180 181 @Override 182 public String toString() { 183 return classification + " " + preset.toString(); 184 } 185 } 16 private TaggingPresetSelector selector; 186 17 187 18 private static TaggingPresetSearchDialog instance; … … 192 23 return instance; 193 24 } 194 195 private JosmTextField edSearchText;196 private JList lsResult;197 private JCheckBox ckOnlyApplicable;198 private JCheckBox ckSearchInTags;199 private final EnumSet<PresetType> typesInSelection = EnumSet.noneOf(PresetType.class);200 private boolean typesInSelectionDirty = true;201 private final List<PresetClasification> classifications = new ArrayList<PresetClasification>();202 private ResultListModel lsResultModel = new ResultListModel();203 204 25 private TaggingPresetSearchDialog() { 205 26 super(Main.parent, tr("Presets"), new String[] {tr("Select"), tr("Cancel")}); 206 DataSet.addSelectionListener(this); 207 208 for (TaggingPreset preset: TaggingPresetPreference.taggingPresets) { 209 if (preset instanceof TaggingPresetSeparator || preset instanceof TaggingPresetMenu) { 210 continue; 27 selector = new TaggingPresetSelector(); 28 setContent(selector); 29 DataSet.addSelectionListener(selector); 30 selector.setDblClickListener(new ActionListener() { 31 @Override 32 public void actionPerformed(ActionEvent e) { 33 buttonAction(0, null); 211 34 } 212 213 classifications.add(new PresetClasification(preset)); 214 } 215 216 build(); 217 filterPresets(); 218 } 219 220 @Override 221 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) { 222 typesInSelectionDirty = true; 35 }); 223 36 } 224 37 225 38 @Override 226 39 public ExtendedDialog showDialog() { 227 228 ckOnlyApplicable.setEnabled(!getTypesInSelection().isEmpty()); 229 ckOnlyApplicable.setSelected(!getTypesInSelection().isEmpty() && ONLY_APPLICABLE.get()); 230 edSearchText.setText(""); 231 filterPresets(); 232 40 selector.init(); 233 41 super.showDialog(); 234 lsResult.getSelectionModel().clearSelection();42 selector.clearSelection(); 235 43 return this; 236 }237 238 private void build() {239 JPanel content = new JPanel();240 content.setLayout(new BorderLayout());241 242 edSearchText = new JosmTextField();243 edSearchText.getDocument().addDocumentListener(new DocumentListener() {244 245 @Override246 public void removeUpdate(DocumentEvent e) {247 filterPresets();248 }249 250 @Override251 public void insertUpdate(DocumentEvent e) {252 filterPresets();253 254 }255 256 @Override257 public void changedUpdate(DocumentEvent e) {258 filterPresets();259 260 }261 });262 edSearchText.addKeyListener(new KeyAdapter() {263 @Override264 public void keyPressed(KeyEvent e) {265 switch (e.getKeyCode()) {266 case KeyEvent.VK_DOWN:267 selectPreset(lsResult.getSelectedIndex() + 1);268 break;269 case KeyEvent.VK_UP:270 selectPreset(lsResult.getSelectedIndex() - 1);271 break;272 case KeyEvent.VK_PAGE_DOWN:273 selectPreset(lsResult.getSelectedIndex() + 10);274 break;275 case KeyEvent.VK_PAGE_UP:276 selectPreset(lsResult.getSelectedIndex() - 10);277 break;278 case KeyEvent.VK_HOME:279 selectPreset(0);280 break;281 case KeyEvent.VK_END:282 selectPreset(lsResultModel.getSize());283 break;284 }285 }286 });287 content.add(edSearchText, BorderLayout.NORTH);288 289 lsResult = new JList();290 lsResult.setModel(lsResultModel);291 lsResult.setCellRenderer(new ResultListCellRenderer());292 lsResult.addMouseListener(new MouseAdapter() {293 @Override294 public void mouseClicked(MouseEvent e) {295 if (e.getClickCount()>1) {296 buttonAction(0, null);297 }298 }299 });300 content.add(new JScrollPane(lsResult), BorderLayout.CENTER);301 302 JPanel pnChecks = new JPanel();303 pnChecks.setLayout(new BoxLayout(pnChecks, BoxLayout.Y_AXIS));304 305 ckOnlyApplicable = new JCheckBox();306 ckOnlyApplicable.setText(tr("Show only applicable to selection"));307 pnChecks.add(ckOnlyApplicable);308 ckOnlyApplicable.addItemListener(new ItemListener() {309 @Override310 public void itemStateChanged(ItemEvent e) {311 filterPresets();312 }313 });314 315 ckSearchInTags = new JCheckBox();316 ckSearchInTags.setText(tr("Search in tags"));317 ckSearchInTags.setSelected(SEARCH_IN_TAGS.get());318 ckSearchInTags.addItemListener(new ItemListener() {319 @Override320 public void itemStateChanged(ItemEvent e) {321 filterPresets();322 }323 });324 pnChecks.add(ckSearchInTags);325 326 content.add(pnChecks, BorderLayout.SOUTH);327 328 content.setPreferredSize(new Dimension(400, 300));329 setContent(content);330 }331 332 private void selectPreset(int newIndex) {333 if (newIndex < 0) {334 newIndex = 0;335 }336 if (newIndex > lsResultModel.getSize() - 1) {337 newIndex = lsResultModel.getSize() - 1;338 }339 lsResult.setSelectedIndex(newIndex);340 lsResult.ensureIndexIsVisible(newIndex);341 }342 343 /**344 * Search expression can be in form: "group1/group2/name" where names can contain multiple words345 */346 private void filterPresets() {347 //TODO Save favorites to file348 String text = edSearchText.getText().toLowerCase();349 350 String[] groupWords;351 String[] nameWords;352 353 if (text.contains("/")) {354 groupWords = text.substring(0, text.lastIndexOf('/')).split("[\\s/]");355 nameWords = text.substring(text.indexOf('/') + 1).split("\\s");356 } else {357 groupWords = null;358 nameWords = text.split("\\s");359 }360 361 boolean onlyApplicable = ckOnlyApplicable.isSelected();362 boolean inTags = ckSearchInTags.isSelected();363 364 List<PresetClasification> result = new ArrayList<PresetClasification>();365 PRESET_LOOP:366 for (PresetClasification presetClasification: classifications) {367 TaggingPreset preset = presetClasification.preset;368 presetClasification.classification = 0;369 370 if (onlyApplicable && preset.types != null) {371 boolean found = false;372 for (PresetType type: preset.types) {373 if (getTypesInSelection().contains(type)) {374 found = true;375 break;376 }377 }378 if (!found) {379 continue;380 }381 }382 383 384 385 if (groupWords != null && presetClasification.isMatchingGroup(groupWords) == 0) {386 continue PRESET_LOOP;387 }388 389 int matchName = presetClasification.isMatchingName(nameWords);390 391 if (matchName == 0) {392 if (groupWords == null) {393 int groupMatch = presetClasification.isMatchingGroup(nameWords);394 if (groupMatch > 0) {395 presetClasification.classification = CLASSIFICATION_GROUP_MATCH + groupMatch;396 }397 }398 if (presetClasification.classification == 0 && inTags) {399 int tagsMatch = presetClasification.isMatchingTags(nameWords);400 if (tagsMatch > 0) {401 presetClasification.classification = CLASSIFICATION_TAGS_MATCH + tagsMatch;402 }403 }404 } else {405 presetClasification.classification = CLASSIFICATION_NAME_MATCH + matchName;406 }407 408 if (presetClasification.classification > 0) {409 presetClasification.classification += presetClasification.favoriteIndex;410 result.add(presetClasification);411 }412 }413 414 Collections.sort(result);415 lsResultModel.setPresets(result);416 if (!buttons.isEmpty()) {417 buttons.get(0).setEnabled(!result.isEmpty());418 }419 }420 421 private EnumSet<PresetType> getTypesInSelection() {422 if (typesInSelectionDirty) {423 synchronized (typesInSelection) {424 typesInSelectionDirty = false;425 typesInSelection.clear();426 for (OsmPrimitive primitive : Main.main.getCurrentDataSet().getSelected()) {427 if (primitive instanceof Node) {428 typesInSelection.add(PresetType.NODE);429 } else if (primitive instanceof Way) {430 typesInSelection.add(PresetType.WAY);431 if (((Way) primitive).isClosed()) {432 typesInSelection.add(PresetType.CLOSEDWAY);433 }434 } else if (primitive instanceof Relation) {435 typesInSelection.add(PresetType.RELATION);436 }437 }438 }439 }440 return typesInSelection;441 44 } 442 45 … … 445 48 super.buttonAction(buttonIndex, evt); 446 49 if (buttonIndex == 0) { 447 int selectPreset = lsResult.getSelectedIndex(); 448 if (selectPreset == -1) { 449 selectPreset = 0; 450 } 451 TaggingPreset preset = lsResultModel.getPresets().get(selectPreset).preset; 452 for (PresetClasification pc: classifications) { 453 if (pc.preset == preset) { 454 pc.favoriteIndex = CLASSIFICATION_IN_FAVORITES; 455 } else if (pc.favoriteIndex > 0) { 456 pc.favoriteIndex--; 457 } 458 } 50 TaggingPreset preset = selector.getSelectedPreset(); 459 51 preset.actionPerformed(null); 460 52 } 461 462 SEARCH_IN_TAGS.put(ckSearchInTags.isSelected()); 463 if (ckOnlyApplicable.isEnabled()) { 464 ONLY_APPLICABLE.put(ckOnlyApplicable.isSelected()); 465 } 53 selector.savePreferences(); 466 54 } 467 468 55 } -
trunk/src/org/openstreetmap/josm/gui/tagging/TaggingPresetSeparator.java
r3083 r6068 3 3 4 4 public class TaggingPresetSeparator extends TaggingPreset { 5 @Override 5 6 public void setDisplayName() {} 6 7 } -
trunk/src/org/openstreetmap/josm/gui/tagging/ac/AutoCompletionManager.java
r5926 r6068 26 26 import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent; 27 27 import org.openstreetmap.josm.gui.tagging.TaggingPreset; 28 import org.openstreetmap.josm.gui.tagging.TaggingPresetItem; 29 import org.openstreetmap.josm.gui.tagging.TaggingPresetItems; 28 30 import org.openstreetmap.josm.tools.MultiMap; 29 31 … … 147 149 public static void cachePresets(Collection<TaggingPreset> presets) { 148 150 for (final TaggingPreset p : presets) { 149 for (TaggingPreset .Item item : p.data) {150 if (item instanceof TaggingPreset .KeyedItem) {151 TaggingPreset .KeyedItem ki = (TaggingPreset.KeyedItem) item;151 for (TaggingPresetItem item : p.data) { 152 if (item instanceof TaggingPresetItems.KeyedItem) { 153 TaggingPresetItems.KeyedItem ki = (TaggingPresetItems.KeyedItem) item; 152 154 if (ki.key != null && ki.getValues() != null) { 153 155 try { … … 157 159 } 158 160 } 159 } else if (item instanceof TaggingPreset .Roles) {160 TaggingPreset .Roles r = (TaggingPreset.Roles) item;161 for (TaggingPreset .Role i : r.roles) {161 } else if (item instanceof TaggingPresetItems.Roles) { 162 TaggingPresetItems.Roles r = (TaggingPresetItems.Roles) item; 163 for (TaggingPresetItems.Role i : r.roles) { 162 164 if (i.key != null) { 163 165 presetRoleCache.add(i.key); … … 288 290 **/ 289 291 292 @Override 290 293 public void primitivesAdded(PrimitivesAddedEvent event) { 291 294 if (dirty) … … 294 297 } 295 298 299 @Override 296 300 public void primitivesRemoved(PrimitivesRemovedEvent event) { 297 301 dirty = true; 298 302 } 299 303 304 @Override 300 305 public void tagsChanged(TagsChangedEvent event) { 301 306 if (dirty) … … 319 324 } 320 325 326 @Override 321 327 public void nodeMoved(NodeMovedEvent event) {/* ignored */} 322 328 329 @Override 323 330 public void wayNodesChanged(WayNodesChangedEvent event) {/* ignored */} 324 331 332 @Override 325 333 public void relationMembersChanged(RelationMembersChangedEvent event) { 326 334 dirty = true; // TODO: not necessary to rebuid if a member is added 327 335 } 328 336 337 @Override 329 338 public void otherDatasetChange(AbstractDatasetChangedEvent event) {/* ignored */} 330 339 340 @Override 331 341 public void dataChanged(DataChangedEvent event) { 332 342 dirty = true; -
trunk/src/org/openstreetmap/josm/tools/TaggingPresetNameTemplateList.java
r5169 r6068 10 10 import org.openstreetmap.josm.gui.preferences.map.TaggingPresetPreference; 11 11 import org.openstreetmap.josm.gui.tagging.TaggingPreset; 12 import org.openstreetmap.josm.gui.tagging.TaggingPreset .PresetType;12 import org.openstreetmap.josm.gui.tagging.TaggingPresetType; 13 13 14 14 /** … … 40 40 41 41 for (TaggingPreset t : presetsWithPattern) { 42 Collection< PresetType> type = Collections.singleton(PresetType.forPrimitive(primitive));42 Collection<TaggingPresetType> type = Collections.singleton(TaggingPresetType.forPrimitive(primitive)); 43 43 if (t.typeMatches(type)) { 44 44 if (t.nameTemplateFilter != null) {
Note:
See TracChangeset
for help on using the changeset viewer.