Ticket #13309: patch-preferences-listenable-2.patch
File patch-preferences-listenable-2.patch, 86.8 KB (added by , 8 years ago) |
---|
-
src/org/openstreetmap/josm/data/Preferences.java
diff --git a/src/org/openstreetmap/josm/data/Preferences.java b/src/org/openstreetmap/josm/data/Preferences.java index f79ba40..68f4cf3 100644
a b import java.util.ResourceBundle; 36 36 import java.util.Set; 37 37 import java.util.SortedMap; 38 38 import java.util.TreeMap; 39 import java.util.concurrent.CopyOnWriteArrayList;40 39 import java.util.function.Predicate; 41 40 import java.util.regex.Matcher; 42 41 import java.util.regex.Pattern; 42 import java.util.stream.Stream; 43 43 44 44 import javax.json.Json; 45 45 import javax.json.JsonArray; … … import javax.xml.stream.XMLStreamException; 55 55 56 56 import org.openstreetmap.josm.Main; 57 57 import org.openstreetmap.josm.data.preferences.ColorProperty; 58 import org.openstreetmap.josm.data.preferences.DoubleProperty; 59 import org.openstreetmap.josm.data.preferences.IntegerProperty; 58 60 import org.openstreetmap.josm.data.preferences.ListListSetting; 59 61 import org.openstreetmap.josm.data.preferences.ListSetting; 62 import org.openstreetmap.josm.data.preferences.LongProperty; 60 63 import org.openstreetmap.josm.data.preferences.MapListSetting; 61 64 import org.openstreetmap.josm.data.preferences.PreferencesReader; 62 65 import org.openstreetmap.josm.data.preferences.PreferencesWriter; … … import org.openstreetmap.josm.io.OfflineAccessException; 66 69 import org.openstreetmap.josm.io.OnlineResource; 67 70 import org.openstreetmap.josm.tools.CheckParameterUtil; 68 71 import org.openstreetmap.josm.tools.ColorHelper; 69 import org.openstreetmap.josm.tools.FilteredCollection;70 72 import org.openstreetmap.josm.tools.I18n; 73 import org.openstreetmap.josm.tools.ListenerList; 71 74 import org.openstreetmap.josm.tools.MultiMap; 72 75 import org.openstreetmap.josm.tools.Utils; 73 76 import org.xml.sax.SAXException; … … public class Preferences { 214 217 } 215 218 } 216 219 220 /** 221 * Old color interface 222 * <p> 223 * To be removed: end of 2016 224 * @deprecated Use a {@link ColorProperty} instead. 225 */ 226 @Deprecated 217 227 public interface ColorKey { 218 228 String getColorName(); 219 229 … … public class Preferences { 222 232 Color getDefaultValue(); 223 233 } 224 234 225 private final CopyOnWriteArrayList<PreferenceChangedListener> listeners = new CopyOnWriteArrayList<>(); 235 private final ListenerList<PreferenceChangedListener> listeners = ListenerList.create(); 236 237 private final HashMap<String, ListenerList<PreferenceChangedListener>> keyListeners = new HashMap<>(); 226 238 227 239 /** 228 240 * Adds a new preferences listener. … … public class Preferences { 230 242 */ 231 243 public void addPreferenceChangeListener(PreferenceChangedListener listener) { 232 244 if (listener != null) { 233 listeners.add IfAbsent(listener);245 listeners.addListener(listener); 234 246 } 235 247 } 236 248 … … public class Preferences { 239 251 * @param listener The listener to remove 240 252 */ 241 253 public void removePreferenceChangeListener(PreferenceChangedListener listener) { 242 listeners.remove(listener); 254 listeners.removeListener(listener); 255 } 256 257 /** 258 * Adds a listener that only listens to changes in one preference 259 * @param key The preference key to listen to 260 * @param listener The listener to add. 261 */ 262 public void addKeyPreferenceChangeListener(String key, PreferenceChangedListener listener) { 263 listenersForKey(key).addListener(listener); 264 } 265 266 /** 267 * Adds a weak listener that only listens to changes in one preference 268 * @param key The preference key to listen to 269 * @param listener The listener to add. 270 */ 271 public void addWeakKeyPreferenceChangeListener(String key, PreferenceChangedListener listener) { 272 listenersForKey(key).addWeakListener(listener); 273 } 274 275 private ListenerList<PreferenceChangedListener> listenersForKey(String key) { 276 ListenerList<PreferenceChangedListener> keyListener = keyListeners.get(key); 277 if (keyListener == null) { 278 keyListener = ListenerList.create(); 279 keyListeners.put(key, keyListener); 280 } 281 return keyListener; 282 } 283 284 /** 285 * Removes a listener that only listens to changes in one preference 286 * @param key The preference key to listen to 287 * @param listener The listener to add. 288 */ 289 public void removeKeyPreferenceChangeListener(String key, PreferenceChangedListener listener) { 290 ListenerList<PreferenceChangedListener> keyListener = keyListeners.get(key); 291 if (keyListener == null) { 292 throw new IllegalArgumentException("There are no listeners registered for " + key); 293 } 294 keyListener.removeListener(listener); 243 295 } 244 296 245 297 protected void firePreferenceChanged(String key, Setting<?> oldValue, Setting<?> newValue) { 246 PreferenceChangeEvent evt = new DefaultPreferenceChangeEvent(key, oldValue, newValue); 247 for (PreferenceChangedListener l : listeners) { 248 l.preferenceChanged(evt); 298 final PreferenceChangeEvent evt = new DefaultPreferenceChangeEvent(key, oldValue, newValue); 299 listeners.fireEvent(listener -> listener.preferenceChanged(evt)); 300 301 ListenerList<PreferenceChangedListener> forKey = keyListeners.get(key); 302 if (forKey != null) { 303 forKey.fireEvent(listener -> listener.preferenceChanged(evt)); 249 304 } 250 305 } 251 306 … … public class Preferences { 478 533 * @return {@code true}, if something has changed (i.e. value is different than before) 479 534 */ 480 535 public boolean put(final String key, String value) { 481 if (value != null && value.isEmpty()) { 482 value = null; 483 } 484 return putSetting(key, value == null ? null : new StringSetting(value)); 536 return putSetting(key, value == null || value.isEmpty() ? null : new StringSetting(value)); 485 537 } 486 538 539 /** 540 * Set a boolean value for a certain setting. 541 * @param key the unique identifier for the setting 542 * @param value The new value 543 * @return {@code true}, if something has changed (i.e. value is different than before) 544 * @see BooleanProperty 545 */ 487 546 public boolean put(final String key, final boolean value) { 488 547 return put(key, Boolean.toString(value)); 489 548 } 490 549 550 /** 551 * Set a boolean value for a certain setting. 552 * @param key the unique identifier for the setting 553 * @param value The new value 554 * @return {@code true}, if something has changed (i.e. value is different than before) 555 * @see IntegerProperty 556 */ 491 557 public boolean putInteger(final String key, final Integer value) { 492 558 return put(key, Integer.toString(value)); 493 559 } 494 560 561 /** 562 * Set a boolean value for a certain setting. 563 * @param key the unique identifier for the setting 564 * @param value The new value 565 * @return {@code true}, if something has changed (i.e. value is different than before) 566 * @see DoubleProperty 567 */ 495 568 public boolean putDouble(final String key, final Double value) { 496 569 return put(key, Double.toString(value)); 497 570 } 498 571 572 /** 573 * Set a boolean value for a certain setting. 574 * @param key the unique identifier for the setting 575 * @param value The new value 576 * @return {@code true}, if something has changed (i.e. value is different than before) 577 * @see LongProperty 578 */ 499 579 public boolean putLong(final String key, final Long value) { 500 580 return put(key, Long.toString(value)); 501 581 } … … public class Preferences { 505 585 * @throws IOException if any I/O error occurs 506 586 */ 507 587 public synchronized void save() throws IOException { 508 save(getPreferenceFile(), 509 new FilteredCollection<>(settingsMap.entrySet(), NO_DEFAULT_SETTINGS_ENTRY), false); 588 save(getPreferenceFile(), settingsMap.entrySet().stream().filter(NO_DEFAULT_SETTINGS_ENTRY), false); 510 589 } 511 590 512 591 public synchronized void saveDefaults() throws IOException { 513 save(getDefaultsCacheFile(), defaultsMap.entrySet() , true);592 save(getDefaultsCacheFile(), defaultsMap.entrySet().stream(), true); 514 593 } 515 594 516 protected void save(File prefFile, Collection<Entry<String, Setting<?>>> settings, boolean defaults) throws IOException { 517 595 protected void save(File prefFile, Stream<Entry<String, Setting<?>>> settings, boolean defaults) throws IOException { 518 596 if (!defaults) { 519 597 /* currently unused, but may help to fix configuration issues in future */ 520 598 putInteger("josm.version", Version.getInstance().getVersion()); … … public class Preferences { 718 796 719 797 /** 720 798 * Convenience method for accessing colour preferences. 799 * <p> 800 * To be removed: end of 2016 721 801 * 722 802 * @param colName name of the colour 723 803 * @param def default value 724 804 * @return a Color object for the configured colour, or the default value if none configured. 805 * @deprecated Use a {@link ColorProperty} instead. 725 806 */ 807 @Deprecated 726 808 public synchronized Color getColor(String colName, Color def) { 727 809 return getColor(colName, null, def); 728 810 } … … public class Preferences { 742 824 743 825 /** 744 826 * Returns the color for the given key. 827 * <p> 828 * To be removed: end of 2016 745 829 * @param key The color key 746 830 * @return the color 831 * @deprecated Use a {@link ColorProperty} instead. 747 832 */ 833 @Deprecated 748 834 public Color getColor(ColorKey key) { 749 835 return getColor(key.getColorName(), key.getSpecialName(), key.getDefaultValue()); 750 836 } 751 837 752 838 /** 753 839 * Convenience method for accessing colour preferences. 754 * 840 * <p> 841 * To be removed: end of 2016 755 842 * @param colName name of the colour 756 843 * @param specName name of the special colour settings 757 844 * @param def default value 758 845 * @return a Color object for the configured colour, or the default value if none configured. 846 * @deprecated Use a {@link ColorProperty} instead. You can replace this by: <code>new ColorProperty(colName, def).getChildColor(specName)</code> 759 847 */ 848 @Deprecated 760 849 public synchronized Color getColor(String colName, String specName, Color def) { 761 850 String colKey = ColorProperty.getColorKey(colName); 762 if (!colKey.equals(colName)) { 763 colornames.put(colKey, colName); 764 } 851 registerColor(colKey, colName); 765 852 String colStr = specName != null ? get("color."+specName) : ""; 766 853 if (colStr.isEmpty()) { 767 854 colStr = get("color." + colKey, ColorHelper.color2html(def, true)); … … public class Preferences { 773 860 } 774 861 } 775 862 863 /** 864 * Registers a color name conversion for the global color registry. 865 * @param colKey The key 866 * @param colName The name of the color. 867 */ 868 public void registerColor(String colKey, String colName) { 869 if (!colKey.equals(colName)) { 870 colornames.put(colKey, colName); 871 } 872 } 873 776 874 public synchronized Color getDefaultColor(String colKey) { 777 875 StringSetting col = Utils.cast(defaultsMap.get("color."+colKey), StringSetting.class); 778 876 String colStr = col == null ? null : col.getValue(); -
src/org/openstreetmap/josm/data/osm/visitor/paint/PaintColors.java
diff --git a/src/org/openstreetmap/josm/data/osm/visitor/paint/PaintColors.java b/src/org/openstreetmap/josm/data/osm/visitor/paint/PaintColors.java index fb9f625..7406ff5 100644
a b import static org.openstreetmap.josm.tools.I18n.marktr; 6 6 import java.awt.Color; 7 7 import java.util.List; 8 8 9 import org.openstreetmap.josm. Main;10 import org.openstreetmap.josm.data. Preferences.ColorKey;9 import org.openstreetmap.josm.data.preferences.CachingProperty; 10 import org.openstreetmap.josm.data.preferences.ColorProperty; 11 11 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles; 12 12 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles.MapPaintSylesUpdateListener; 13 13 import org.openstreetmap.josm.gui.mappaint.StyleSource; 14 14 15 public enum PaintColors implements ColorKey{15 public enum PaintColors { 16 16 17 17 INACTIVE(marktr("inactive"), Color.darkGray), 18 18 SELECTED(marktr("selected"), Color.red), … … public enum PaintColors implements ColorKey { 33 33 34 34 private final String name; 35 35 private final Color defaultColor; 36 private final CachingProperty<Color> property; 36 37 37 38 private static volatile Color backgroundColorCache; 38 39 39 40 private static final MapPaintSylesUpdateListener styleOverrideListener = new MapPaintSylesUpdateListener() { 40 41 //TODO: Listen to wireframe map mode changes. 41 42 @Override 42 43 public void mapPaintStylesUpdated() { 43 44 backgroundColorCache = null; … … public enum PaintColors implements ColorKey { 54 55 } 55 56 56 57 PaintColors(String name, Color defaultColor) { 58 property = new ColorProperty(name, defaultColor).cached(); 57 59 this.name = name; 58 60 this.defaultColor = defaultColor; 59 61 } 60 62 61 @Override62 public String getColorName() {63 return name;64 }65 66 @Override67 63 public Color getDefaultValue() { 68 return defaultColor; 69 } 70 71 @Override 72 public String getSpecialName() { 73 return null; 64 return property.getDefaultValue(); 74 65 } 75 66 76 67 public Color get() { 77 return Main.pref.getColor(this); 78 } 79 80 public static void getColors() { 81 for (PaintColors c:values()) { 82 c.get(); 83 } 68 return property.get(); 84 69 } 85 70 86 71 public static Color getBackgroundColor() { … … public enum PaintColors implements ColorKey { 97 82 } 98 83 } 99 84 if (backgroundColorCache == null) { 100 backgroundColorCache = BACKGROUND.get(); 85 return BACKGROUND.get(); 86 } else { 87 return backgroundColorCache; 101 88 } 102 return backgroundColorCache;103 89 } 104 90 } -
src/org/openstreetmap/josm/data/preferences/AbstractProperty.java
diff --git a/src/org/openstreetmap/josm/data/preferences/AbstractProperty.java b/src/org/openstreetmap/josm/data/preferences/AbstractProperty.java index 1516d42..2a5bff8 100644
a b 2 2 package org.openstreetmap.josm.data.preferences; 3 3 4 4 import org.openstreetmap.josm.Main; 5 import org.openstreetmap.josm.data.Preferences; 6 import org.openstreetmap.josm.data.Preferences.PreferenceChangeEvent; 7 import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener; 5 8 6 9 /** 7 10 * Captures the common functionality of preference properties 8 11 * @param <T> The type of object accessed by this property 9 12 */ 10 13 public abstract class AbstractProperty<T> { 14 15 private final class PreferenceChangedListenerAdapter implements PreferenceChangedListener { 16 private ValueChangeListener<? super T> listener; 17 18 public PreferenceChangedListenerAdapter(ValueChangeListener<? super T> listener) { 19 this.listener = listener; 20 } 21 22 @Override 23 public void preferenceChanged(PreferenceChangeEvent e) { 24 listener.valueChanged(new ValueChangeEvent<>(e, AbstractProperty.this)); 25 } 26 27 /* (non-Javadoc) 28 * @see java.lang.Object#hashCode() 29 */ 30 @Override 31 public int hashCode() { 32 final int prime = 31; 33 int result = 1; 34 result = prime * result + getOuterType().hashCode(); 35 result = prime * result + ((listener == null) ? 0 : listener.hashCode()); 36 return result; 37 } 38 39 /* (non-Javadoc) 40 * @see java.lang.Object#equals(java.lang.Object) 41 */ 42 @Override 43 public boolean equals(Object obj) { 44 if (this == obj) 45 return true; 46 if (obj == null) 47 return false; 48 if (getClass() != obj.getClass()) 49 return false; 50 PreferenceChangedListenerAdapter other = (PreferenceChangedListenerAdapter) obj; 51 if (!getOuterType().equals(other.getOuterType())) 52 return false; 53 if (listener == null) { 54 if (other.listener != null) 55 return false; 56 } else if (!listener.equals(other.listener)) 57 return false; 58 return true; 59 } 60 61 private AbstractProperty<T> getOuterType() { 62 return AbstractProperty.this; 63 } 64 65 @Override 66 public String toString() { 67 return "PreferenceChangedListenerAdapter [listener=" + listener + "]"; 68 } 69 } 70 71 /** 72 * A listener that listens to changes in the properties value. 73 * @author michael 74 * 75 */ 76 public interface ValueChangeListener<T> { 77 public void valueChanged(ValueChangeEvent<? extends T> e); 78 } 79 80 /** 81 * An event that is triggered if the value of a property changes. 82 * @author Michael Zangl 83 * @param <T> 84 * @since xxx 85 */ 86 public static class ValueChangeEvent<T> { 87 private final PreferenceChangeEvent base; 88 89 private final AbstractProperty<T> source; 90 91 ValueChangeEvent(PreferenceChangeEvent base, AbstractProperty<T> source) { 92 this.base = base; 93 this.source = source; 94 } 95 96 /** 97 * Get the property that was changed 98 * @return The property. 99 */ 100 public AbstractProperty<T> getProperty() { 101 return source; 102 } 103 } 104 105 /** 106 * An exception that is thrown if a preference value is invalid. 107 * @author Michael Zangl 108 */ 109 public static class InvalidPreferenceValueException extends RuntimeException { 110 111 public InvalidPreferenceValueException() { 112 super(); 113 } 114 115 public InvalidPreferenceValueException(String message, Throwable cause) { 116 super(message, cause); 117 } 118 119 public InvalidPreferenceValueException(String message) { 120 super(message); 121 } 122 123 public InvalidPreferenceValueException(Throwable cause) { 124 super(cause); 125 } 126 } 127 128 /** 129 * The preferences object this property is for. 130 */ 131 protected final Preferences preferences; 11 132 protected final String key; 12 133 protected final T defaultValue; 13 134 … … public abstract class AbstractProperty<T> { 18 139 * @since 5464 19 140 */ 20 141 public AbstractProperty(String key, T defaultValue) { 142 // Main.pref should not change in production but may change during tests. 143 preferences = Main.pref; 21 144 this.key = key; 22 145 this.defaultValue = defaultValue; 23 146 } 24 147 25 148 /** 149 * Store the default value to {@link Preferences}. 150 */ 151 protected void storeDefaultValue() { 152 if (getPreferences() != null) { 153 get(); 154 } 155 } 156 157 /** 26 158 * Replies the property key. 27 159 * @return The property key 28 160 */ … … public abstract class AbstractProperty<T> { 35 167 * @return true if {@code Main.pref} contains this property. 36 168 */ 37 169 public boolean isSet() { 38 return ! Main.pref.get(key).isEmpty();170 return !getPreferences().get(key).isEmpty(); 39 171 } 40 172 41 173 /** … … public abstract class AbstractProperty<T> { 50 182 * Removes this property from JOSM preferences (i.e replace it by its default value). 51 183 */ 52 184 public void remove() { 53 Main.pref.put(getKey(), String.valueOf(getDefaultValue()));185 put(getDefaultValue()); 54 186 } 55 187 56 188 /** … … public abstract class AbstractProperty<T> { 67 199 * @since 5464 68 200 */ 69 201 public abstract boolean put(T value); 202 203 /** 204 * Gets the preferences used for this property. 205 * @return The preferences for this property. 206 * @since xxx 207 */ 208 protected Preferences getPreferences() { 209 return preferences; 210 } 211 212 /** 213 * Adds a listener that listens only for changes to this preference key. 214 * @param listener The listener to add. 215 */ 216 public void addListener(ValueChangeListener<? super T> listener) { 217 addListenerImpl(new PreferenceChangedListenerAdapter(listener)); 218 } 219 220 protected void addListenerImpl(PreferenceChangedListener adapter) { 221 getPreferences().addKeyPreferenceChangeListener(getKey(), adapter); 222 } 223 224 /** 225 * Adds a weak listener that listens only for changes to this preference key. 226 * @param listener The listener to add. 227 */ 228 public void addWeakListener(ValueChangeListener<? super T> listener) { 229 addWeakListenerImpl(new PreferenceChangedListenerAdapter(listener)); 230 } 231 232 protected void addWeakListenerImpl(PreferenceChangedListener adapter) { 233 getPreferences().addWeakKeyPreferenceChangeListener(getKey(), adapter); 234 } 235 236 /** 237 * Removes a listener that listens only for changes to this preference key. 238 * @param listener The listener to add. 239 */ 240 public void removeListener(ValueChangeListener<? super T> listener) { 241 removeListenerImpl(new PreferenceChangedListenerAdapter(listener)); 242 } 243 244 protected void removeListenerImpl(PreferenceChangedListener adapter) { 245 getPreferences().removeKeyPreferenceChangeListener(getKey(), adapter); 246 } 247 248 @Override 249 public int hashCode() { 250 final int prime = 31; 251 int result = 1; 252 result = prime * result + ((key == null) ? 0 : key.hashCode()); 253 result = prime * result + ((preferences == null) ? 0 : preferences.hashCode()); 254 return result; 255 } 256 257 @Override 258 public boolean equals(Object obj) { 259 if (this == obj) 260 return true; 261 if (obj == null) 262 return false; 263 if (getClass() != obj.getClass()) 264 return false; 265 AbstractProperty other = (AbstractProperty) obj; 266 if (key == null) { 267 if (other.key != null) 268 return false; 269 } else if (!key.equals(other.key)) 270 return false; 271 if (preferences == null) { 272 if (other.preferences != null) 273 return false; 274 } else if (!preferences.equals(other.preferences)) 275 return false; 276 return true; 277 } 70 278 } -
new file src/org/openstreetmap/josm/data/preferences/AbstractToStringProperty.java
diff --git a/src/org/openstreetmap/josm/data/preferences/AbstractToStringProperty.java b/src/org/openstreetmap/josm/data/preferences/AbstractToStringProperty.java new file mode 100644 index 0000000..6a3a0b9
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.preferences; 3 4 import org.openstreetmap.josm.Main; 5 import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener; 6 import org.openstreetmap.josm.tools.CheckParameterUtil; 7 import org.openstreetmap.josm.tools.bugreport.BugReport; 8 9 /** 10 * This class represents a property that can be represented as String. 11 * 12 * @author Michael Zangl 13 * 14 * @param <T> The property content type. 15 * @since xxx 16 */ 17 public abstract class AbstractToStringProperty<T> extends AbstractProperty<T> { 18 19 /** 20 * This is a version of this property that attempts to get the property with a more specialized key and - if that fails - uses the property 21 * value as default. 22 * 23 * @author Michael Zangl 24 * @param <T> The content type 25 * @since xxx 26 */ 27 public static class ChildProperty<T> extends AbstractToStringProperty<T> { 28 private AbstractToStringProperty<T> parent; 29 30 ChildProperty(AbstractToStringProperty<T> parent, String key) { 31 super(key, null); 32 CheckParameterUtil.ensureParameterNotNull(parent, "parent"); 33 this.parent = parent; 34 } 35 36 @Override 37 protected void storeDefaultValue() { 38 // Default value hidden in preferences. 39 } 40 41 @Override 42 public T getDefaultValue() { 43 return parent.get(); 44 } 45 46 @Override 47 protected T fromString(String string) { 48 return parent.fromString(string); 49 } 50 51 @Override 52 protected String toString(T t) { 53 return parent.toString(t); 54 } 55 56 @Override 57 protected void addListenerImpl(PreferenceChangedListener adapter) { 58 super.addListenerImpl(adapter); 59 parent.addListenerImpl(adapter); 60 } 61 62 @Override 63 protected void addWeakListenerImpl(PreferenceChangedListener adapter) { 64 super.addWeakListenerImpl(adapter); 65 parent.addWeakListenerImpl(adapter); 66 } 67 68 @Override 69 protected void removeListenerImpl(PreferenceChangedListener adapter) { 70 super.removeListenerImpl(adapter); 71 parent.removeListenerImpl(adapter); 72 } 73 74 @Override 75 public CachingProperty<T> cached() { 76 throw new UnsupportedOperationException("Not implemented yet."); 77 } 78 79 } 80 81 /** 82 * Create a new property and store the default value. 83 * @param key The key 84 * @param defaultValue The default value. 85 * @see AbstractProperty#AbstractProperty(String, Object) 86 */ 87 public AbstractToStringProperty(String key, T defaultValue) { 88 super(key, defaultValue); 89 storeDefaultValue(); 90 } 91 92 @Override 93 public T get() { 94 String string = getAsString(); 95 if (!string.isEmpty()) { 96 try { 97 return fromString(string); 98 } catch (InvalidPreferenceValueException e) { 99 Main.warn(BugReport.intercept(e).put("key", key).put("value", string)); 100 } 101 } 102 return getDefaultValue(); 103 } 104 105 /** 106 * Converts the string to an object of the given type. 107 * @param string The string 108 * @return The object. 109 * @throws InvalidPreferenceValueException If the value could not be converted. 110 * @since xxx 111 */ 112 protected abstract T fromString(String string); 113 114 @Override 115 public boolean put(T value) { 116 String string = value == null ? null : toString(value); 117 return getPreferences().put(getKey(), string); 118 } 119 120 /** 121 * Converts the string to an object of the given type. 122 * @param t The object. 123 * @return The string representing the object 124 * @throws InvalidPreferenceValueException If the value could not be converted. 125 * @since xxx 126 */ 127 protected abstract String toString(T t); 128 129 /** 130 * Gets the preference value as String. 131 * @return The string preference value. 132 */ 133 protected String getAsString() { 134 T def = getDefaultValue(); 135 return getPreferences().get(key, def == null ? "" : toString(def)); 136 } 137 138 /** 139 * Gets a specialized setting value that has the current value as default 140 * <p> 141 * The key will be getKey().spec 142 * @param spec The key specialization 143 * @return The property 144 */ 145 public AbstractToStringProperty<T> getSpecialized(String spec) { 146 return getChildProperty(getKey() + "." + spec); 147 } 148 149 /** 150 * Gets a setting that defaults to this setting if the key is not set. 151 * @param key The more specialized key. 152 * @return The new setting. 153 */ 154 protected AbstractToStringProperty<T> getChildProperty(String key) { 155 return new ChildProperty<>(this, key); 156 } 157 158 /** 159 * Creates a new {@link CachingProperty} instance for this property. 160 * @return The new caching property instance. 161 */ 162 public CachingProperty<T> cached() { 163 return new CachingProperty<>(this); 164 } 165 } -
src/org/openstreetmap/josm/data/preferences/BooleanProperty.java
diff --git a/src/org/openstreetmap/josm/data/preferences/BooleanProperty.java b/src/org/openstreetmap/josm/data/preferences/BooleanProperty.java index 578edca..93ab3b1 100644
a b 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.preferences; 3 3 4 import org.openstreetmap.josm.Main;5 6 4 /** 7 5 * A property containing a {@code Boolean} value. 8 6 */ 9 public class BooleanProperty extends Abstract Property<Boolean> {7 public class BooleanProperty extends AbstractToStringProperty<Boolean> { 10 8 11 9 /** 12 10 * Constructs a new {@code BooleanProperty}. … … public class BooleanProperty extends AbstractProperty<Boolean> { 15 13 */ 16 14 public BooleanProperty(String key, boolean defaultValue) { 17 15 super(key, defaultValue); 18 if (Main.pref != null) {19 get();20 }21 16 } 22 17 23 18 @Override 24 19 public Boolean get() { 25 return Main.pref.getBoolean(getKey(), defaultValue); 20 // Removing this implementation breaks binary compatibility 21 return super.get(); 26 22 } 27 23 28 24 @Override 29 25 public boolean put(Boolean value) { 30 return Main.pref.put(getKey(), value); 26 // Removing this implementation breaks binary compatibility 27 return super.put(value); 28 } 29 30 @Override 31 protected Boolean fromString(String string) { 32 return Boolean.valueOf(string); 33 } 34 35 @Override 36 protected String toString(Boolean t) { 37 return t.toString(); 31 38 } 32 39 } -
src/org/openstreetmap/josm/data/preferences/CachedProperty.java
diff --git a/src/org/openstreetmap/josm/data/preferences/CachedProperty.java b/src/org/openstreetmap/josm/data/preferences/CachedProperty.java index d8e36a4..c3459e7 100644
a b public abstract class CachedProperty<T> extends AbstractProperty<T> implements P 13 13 14 14 protected CachedProperty(String key, String defaultValueAsString) { 15 15 super(key, null); 16 Main.pref.add PreferenceChangeListener(this);16 Main.pref.addKeyPreferenceChangeListener(key, this); 17 17 this.defaultValueAsString = defaultValueAsString; 18 18 updateValue(); 19 19 } … … public abstract class CachedProperty<T> extends AbstractProperty<T> implements P 60 60 } 61 61 62 62 public String getAsString() { 63 return Main.pref.get(getKey(), getDefaultValueAsString());63 return getPreferences().get(getKey(), getDefaultValueAsString()); 64 64 } 65 65 66 66 @Override -
new file src/org/openstreetmap/josm/data/preferences/CachingProperty.java
diff --git a/src/org/openstreetmap/josm/data/preferences/CachingProperty.java b/src/org/openstreetmap/josm/data/preferences/CachingProperty.java new file mode 100644 index 0000000..d8644ee
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.preferences; 3 4 import org.openstreetmap.josm.data.preferences.AbstractProperty.ValueChangeListener; 5 6 /** 7 * This is a special wrapper of {@link AbstractProperty}. The current preference value is cached. The value is invalidated if the preference was 8 * changed. 9 * @author Michael Zangl 10 * 11 * @param <T> 12 * @since xxx 13 */ 14 public class CachingProperty<T> extends AbstractProperty<T> implements ValueChangeListener<T> { 15 16 private T cache; 17 private boolean cacheActive; 18 private final AbstractProperty<T> toCache; 19 20 /** 21 * Create a new caching property. 22 * @param toCache The property to cache. 23 */ 24 CachingProperty(AbstractProperty<T> toCache) { 25 super(toCache.getKey(), toCache.getDefaultValue()); 26 this.toCache = toCache; 27 addWeakListener(this); 28 } 29 30 @Override 31 public synchronized T get() { 32 if (!cacheActive) { 33 cache = toCache.get(); 34 cacheActive = true; 35 } 36 return cache; 37 } 38 39 @Override 40 public boolean put(T value) { 41 return toCache.put(cache); 42 } 43 44 @Override 45 public synchronized void valueChanged(org.openstreetmap.josm.data.preferences.AbstractProperty.ValueChangeEvent<? extends T> e) { 46 cacheActive = false; 47 } 48 } -
src/org/openstreetmap/josm/data/preferences/CollectionProperty.java
diff --git a/src/org/openstreetmap/josm/data/preferences/CollectionProperty.java b/src/org/openstreetmap/josm/data/preferences/CollectionProperty.java index 0b5c854..44f6864 100644
a b public class CollectionProperty extends AbstractProperty<Collection<String>> { 24 24 25 25 @Override 26 26 public Collection<String> get() { 27 return Main.pref.getCollection(getKey(), getDefaultValue());27 return getPreferences().getCollection(getKey(), getDefaultValue()); 28 28 } 29 29 30 30 @Override 31 31 public boolean put(Collection<String> value) { 32 return Main.pref.putCollection(getKey(), value);32 return getPreferences().putCollection(getKey(), value); 33 33 } 34 34 } -
src/org/openstreetmap/josm/data/preferences/ColorProperty.java
diff --git a/src/org/openstreetmap/josm/data/preferences/ColorProperty.java b/src/org/openstreetmap/josm/data/preferences/ColorProperty.java index b597bb2..873dba8 100644
a b package org.openstreetmap.josm.data.preferences; 4 4 import java.awt.Color; 5 5 import java.util.Locale; 6 6 7 import org.openstreetmap.josm.Main; 8 import org.openstreetmap.josm.data.Preferences.ColorKey; 7 import org.openstreetmap.josm.tools.ColorHelper; 9 8 10 9 /** 11 10 * A property containing a {@link Color} value. 12 11 * @since 5464 13 12 */ 14 public class ColorProperty extends Abstract Property<Color> implements ColorKey{13 public class ColorProperty extends AbstractToStringProperty<Color> { 15 14 16 15 private final String name; 17 16 18 17 /** 19 18 * Constructs a new {@code ColorProperty}. 20 19 * @param colName The color name 20 * @param defaultValue The default value as HTML string 21 */ 22 public ColorProperty(String colName, String defaultValue) { 23 this(colName, ColorHelper.html2color(defaultValue)); 24 } 25 26 /** 27 * Constructs a new {@code ColorProperty}. 28 * @param colName The color name 21 29 * @param defaultValue The default value 22 30 */ 23 31 public ColorProperty(String colName, Color defaultValue) { 24 32 super(getColorKey(colName), defaultValue); 25 33 this.name = colName; 26 if (Main.pref != null) { 27 get(); 28 } 34 getPreferences().registerColor(getColorKey(colName), colName); 29 35 } 30 36 31 37 @Override 32 38 public Color get() { 33 return Main.pref.getColor(this); 39 // Removing this implementation breaks binary compatibility due to the way generics work 40 return super.get(); 34 41 } 35 42 36 43 @Override 37 44 public boolean put(Color value) { 38 return Main.pref.putColor(getColorKey(name), value); 45 // Removing this implementation breaks binary compatibility due to the way generics work 46 return super.put(value); 47 } 48 49 @Override 50 protected Color fromString(String string) { 51 return ColorHelper.html2color(string); 52 } 53 54 @Override 55 protected String toString(Color t) { 56 return ColorHelper.color2html(t, true); 57 } 58 59 /** 60 * Gets a color of which the value can be set. 61 * @param colorName the name of the color. 62 * @return The child property that inherits this value if it is not set. 63 */ 64 public AbstractToStringProperty<Color> getChildColor(String colorName) { 65 return getChildProperty(getColorKey(colorName)); 66 } 67 68 /** 69 * Gets the name this color was registered with. 70 * @return The name. 71 */ 72 public String getName() { 73 return name; 39 74 } 40 75 41 76 /** … … public class ColorProperty extends AbstractProperty<Color> implements ColorKey { 44 79 * @return The color key for this property 45 80 */ 46 81 public static String getColorKey(String colName) { 47 return colName == null ? null : colName.toLowerCase(Locale.ENGLISH).replaceAll("[^a-z0-9]+", "."); 48 } 49 50 @Override 51 public String getColorName() { 52 return name; 82 return colName == null ? null : "color." + colName.toLowerCase(Locale.ENGLISH).replaceAll("[^a-z0-9]+", "."); 53 83 } 54 84 55 85 @Override 56 public String getSpecialName() {57 return null;86 public String toString() { 87 return "ColorProperty [name=" + name + ", defaultValue=" + getDefaultValue() + "]"; 58 88 } 59 89 } -
src/org/openstreetmap/josm/data/preferences/DoubleProperty.java
diff --git a/src/org/openstreetmap/josm/data/preferences/DoubleProperty.java b/src/org/openstreetmap/josm/data/preferences/DoubleProperty.java index 44ca4c2..a44066d 100644
a b 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.preferences; 3 3 4 import org.openstreetmap.josm.Main;5 6 4 /** 7 5 * A property containing an {@code Double} value. 8 6 * @since 3246 9 7 */ 10 public class DoubleProperty extends Abstract Property<Double> {8 public class DoubleProperty extends AbstractToStringProperty<Double> { 11 9 12 10 /** 13 11 * Constructs a new {@code DoubleProperty}. … … public class DoubleProperty extends AbstractProperty<Double> { 20 18 21 19 @Override 22 20 public Double get() { 23 return Main.pref.getDouble(getKey(), getDefaultValue()); 21 // Removing this implementation breaks binary compatibility 22 return super.get(); 24 23 } 25 24 26 25 @Override 27 26 public boolean put(Double value) { 28 return Main.pref.putDouble(getKey(), value); 27 // Removing this implementation breaks binary compatibility 28 return super.put(value); 29 } 30 31 @Override 32 protected Double fromString(String string) { 33 try { 34 return Double.valueOf(string); 35 } catch (NumberFormatException e) { 36 throw new InvalidPreferenceValueException(e); 37 } 38 } 39 40 @Override 41 protected String toString(Double t) { 42 return t.toString(); 29 43 } 30 44 31 45 /** -
src/org/openstreetmap/josm/data/preferences/IntegerProperty.java
diff --git a/src/org/openstreetmap/josm/data/preferences/IntegerProperty.java b/src/org/openstreetmap/josm/data/preferences/IntegerProperty.java index f15d082..f0cb061 100644
a b import org.openstreetmap.josm.Main; 7 7 * A property containing an {@code Integer} value. 8 8 * @since 3246 9 9 */ 10 public class IntegerProperty extends Abstract Property<Integer> {10 public class IntegerProperty extends AbstractToStringProperty<Integer> { 11 11 12 12 /** 13 13 * Constructs a new {@code IntegerProperty}. … … public class IntegerProperty extends AbstractProperty<Integer> { 23 23 24 24 @Override 25 25 public Integer get() { 26 return Main.pref.getInteger(getKey(), getDefaultValue()); 26 // Removing this implementation breaks binary compatibility 27 return super.get(); 27 28 } 28 29 29 30 @Override 30 31 public boolean put(Integer value) { 31 return Main.pref.putInteger(getKey(), value); 32 // Removing this implementation breaks binary compatibility 33 return super.put(value); 32 34 } 33 35 36 @Override 37 protected Integer fromString(String string) { 38 try { 39 return Integer.valueOf(string); 40 } catch (NumberFormatException e) { 41 throw new InvalidPreferenceValueException(e); 42 } 43 } 44 45 @Override 46 protected String toString(Integer t) { 47 return t.toString(); 48 } 49 50 34 51 /** 35 52 * parses and saves an integer value 36 53 * @param value the value to be parsed -
src/org/openstreetmap/josm/data/preferences/LongProperty.java
diff --git a/src/org/openstreetmap/josm/data/preferences/LongProperty.java b/src/org/openstreetmap/josm/data/preferences/LongProperty.java index d480e9e..689f354 100644
a b 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.preferences; 3 3 4 import org.openstreetmap.josm.Main;5 6 4 /** 7 5 * A property containing an {@code Long} value. 8 6 * @since 10087 9 7 * 10 8 */ 11 public class LongProperty extends Abstract Property<Long> {9 public class LongProperty extends AbstractToStringProperty<Long> { 12 10 13 11 /** 14 12 * Constructs a new {@code LongProperty} … … public class LongProperty extends AbstractProperty<Long> { 17 15 */ 18 16 public LongProperty(String key, long defaultValue) { 19 17 super(key, defaultValue); 20 if (Main.pref != null) {21 get();22 }23 18 } 24 19 25 20 @Override 26 21 public Long get() { 27 return Main.pref.getLong(getKey(), getDefaultValue()); 22 // Removing this implementation breaks binary compatibility 23 return super.get(); 28 24 } 29 25 30 26 @Override 31 27 public boolean put(Long value) { 32 return Main.pref.putLong(getKey(), value); 28 // Removing this implementation breaks binary compatibility 29 return super.put(value); 30 } 31 32 @Override 33 protected Long fromString(String string) { 34 try { 35 return Long.valueOf(string); 36 } catch (NumberFormatException e) { 37 throw new InvalidPreferenceValueException(e); 38 } 33 39 } 34 40 41 @Override 42 protected String toString(Long t) { 43 return t.toString(); 44 } 35 45 } -
src/org/openstreetmap/josm/data/preferences/PreferencesWriter.java
diff --git a/src/org/openstreetmap/josm/data/preferences/PreferencesWriter.java b/src/org/openstreetmap/josm/data/preferences/PreferencesWriter.java index f247cde..80fd8d9 100644
a b import java.io.PrintWriter; 5 5 import java.util.Collection; 6 6 import java.util.List; 7 7 import java.util.Map; 8 import java.util.stream.Stream; 8 9 9 10 import org.openstreetmap.josm.Main; 10 11 import org.openstreetmap.josm.data.Version; … … public class PreferencesWriter extends XmlWriter implements SettingVisitor { 37 38 * @param settings preferences settings to write 38 39 */ 39 40 public void write(Collection<Map.Entry<String, Setting<?>>> settings) { 41 write(settings.stream()); 42 } 43 44 /** 45 * Write preferences. 46 * 47 * @param settings preferences settings to write as stream. 48 */ 49 public void write(Stream<Map.Entry<String, Setting<?>>> settings) { 40 50 out.write(String.format("<?xml version=\"1.0\" encoding=\"UTF-8\"?>%n")); 41 51 String rootElement = defaults ? "preferences-defaults" : "preferences"; 42 52 out.write(String.format("<%s xmlns='%s/preferences-1.0'", rootElement, Main.getXMLBase())); … … public class PreferencesWriter extends XmlWriter implements SettingVisitor { 44 54 out.write(" xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'"); 45 55 } 46 56 out.write(String.format(" version='%d'>%n", Version.getInstance().getVersion())); 47 for (Map.Entry<String, Setting<?>> e : settings){57 settings.forEachOrdered(e -> { 48 58 setKey(e.getKey()); 49 59 e.getValue().visit(this); 50 } 60 }); 51 61 out.write(String.format("</%s>%n", rootElement)); 52 62 } 53 63 -
src/org/openstreetmap/josm/data/preferences/StringProperty.java
diff --git a/src/org/openstreetmap/josm/data/preferences/StringProperty.java b/src/org/openstreetmap/josm/data/preferences/StringProperty.java index 25e42a1..cab4539 100644
a b 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.preferences; 3 3 4 import org.openstreetmap.josm.Main;5 6 4 /** 7 5 * A property containing an {@code String} value. 8 6 */ 9 public class StringProperty extends Abstract Property<String> {7 public class StringProperty extends AbstractToStringProperty<String> { 10 8 11 9 /** 12 10 * Constructs a new {@code StringProperty}. … … public class StringProperty extends AbstractProperty<String> { 15 13 */ 16 14 public StringProperty(String key, String defaultValue) { 17 15 super(key, defaultValue); 18 if (Main.pref != null) {19 get();20 }21 16 } 22 17 23 18 @Override 24 19 public String get() { 25 return Main.pref.get(getKey(), getDefaultValue()); 20 // Removing this implementation breaks binary compatibility 21 return super.get(); 26 22 } 27 23 28 24 @Override 29 25 public boolean put(String value) { 30 return Main.pref.put(getKey(), value); 26 // Removing this implementation breaks binary compatibility 27 return super.put(value); 28 } 29 30 @Override 31 protected String fromString(String string) { 32 return string; 33 } 34 35 @Override 36 protected String toString(String string) { 37 return string; 31 38 } 32 39 } -
src/org/openstreetmap/josm/gui/MainApplication.java
diff --git a/src/org/openstreetmap/josm/gui/MainApplication.java b/src/org/openstreetmap/josm/gui/MainApplication.java index f58a661..520bfa4 100644
a b public class MainApplication extends Main { 314 314 I18n.set(args.get(Option.LANGUAGE).iterator().next()); 315 315 } 316 316 317 if (args.containsKey(Option.TRACE)) { 318 // Enable JOSM debug level 319 logLevel = 5; 320 // Enable debug in OAuth signpost via system preference, but only at trace level 321 Utils.updateSystemProperty("debug", "true"); 322 Main.info(tr("Enabled detailed debug level (trace)")); 323 } else if (args.containsKey(Option.DEBUG)) { 324 // Enable JOSM debug level 325 logLevel = 4; 326 Main.info(tr("Printing debugging messages to console")); 327 } 328 317 329 initApplicationPreferences(); 318 330 319 331 Policy.setPolicy(new Policy() { … … public class MainApplication extends Main { 345 357 System.exit(0); 346 358 } 347 359 348 if (args.containsKey(Option.DEBUG) || args.containsKey(Option.TRACE)) {349 // Enable JOSM debug level350 logLevel = 4;351 Main.info(tr("Printing debugging messages to console"));352 }353 354 360 boolean skipLoadingPlugins = false; 355 361 if (args.containsKey(Option.SKIP_PLUGINS)) { 356 362 skipLoadingPlugins = true; 357 363 Main.info(tr("Plugin loading skipped")); 358 364 } 359 365 360 if (args.containsKey(Option.TRACE)) {361 // Enable JOSM debug level362 logLevel = 5;363 // Enable debug in OAuth signpost via system preference, but only at trace level364 Utils.updateSystemProperty("debug", "true");365 Main.info(tr("Enabled detailed debug level (trace)"));366 }367 368 366 Main.pref.init(args.containsKey(Option.RESET_PREFERENCES)); 369 367 370 368 if (args.containsKey(Option.SET)) { -
src/org/openstreetmap/josm/gui/MapFrame.java
diff --git a/src/org/openstreetmap/josm/gui/MapFrame.java b/src/org/openstreetmap/josm/gui/MapFrame.java index 54d8c2d..7613e0d 100644
a b public class MapFrame extends JPanel implements Destroyable, ActiveLayerChangeLi 759 759 selectMapMode(newMapMode, newLayer); 760 760 } else if (mapMode != null) { 761 761 mapMode.exitMode(); // if new mode is null - simply exit from previous mode 762 mapMode = null; 762 763 } 763 764 } 764 765 // if this is really a change (and not the first active layer) -
src/org/openstreetmap/josm/gui/MapStatus.java
diff --git a/src/org/openstreetmap/josm/gui/MapStatus.java b/src/org/openstreetmap/josm/gui/MapStatus.java index 50ef27c..d59d8fe 100644
a b import org.openstreetmap.josm.data.coor.LatLon; 64 64 import org.openstreetmap.josm.data.osm.DataSet; 65 65 import org.openstreetmap.josm.data.osm.OsmPrimitive; 66 66 import org.openstreetmap.josm.data.osm.Way; 67 import org.openstreetmap.josm.data.preferences.AbstractProperty; 68 import org.openstreetmap.josm.data.preferences.BooleanProperty; 67 69 import org.openstreetmap.josm.data.preferences.ColorProperty; 70 import org.openstreetmap.josm.data.preferences.DoubleProperty; 68 71 import org.openstreetmap.josm.gui.help.Helpful; 69 72 import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference; 70 73 import org.openstreetmap.josm.gui.progress.PleaseWaitProgressMonitor; … … import org.openstreetmap.josm.tools.ImageProvider; 92 95 public final class MapStatus extends JPanel implements Helpful, Destroyable, PreferenceChangedListener, SoMChangeListener { 93 96 94 97 private final DecimalFormat DECIMAL_FORMAT = new DecimalFormat(Main.pref.get("statusbar.decimal-format", "0.0")); 95 private final double DISTANCE_THRESHOLD = Main.pref.getDouble("statusbar.distance-threshold", 0.01); 98 private static final AbstractProperty<Double> DISTANCE_THRESHOLD = new DoubleProperty("statusbar.distance-threshold", 0.01).cached(); 99 100 private static final AbstractProperty<Boolean> SHOW_ID = new BooleanProperty("osm-primitives.showid", false); 96 101 97 102 /** 98 103 * Property for map status background color. 99 104 * @since 6789 100 105 */ 101 106 public static final ColorProperty PROP_BACKGROUND_COLOR = new ColorProperty( 102 marktr("Status bar background"), Color.decode("#b8cfe5"));107 marktr("Status bar background"), "#b8cfe5"); 103 108 104 109 /** 105 110 * Property for map status background color (active state). 106 111 * @since 6789 107 112 */ 108 113 public static final ColorProperty PROP_ACTIVE_BACKGROUND_COLOR = new ColorProperty( 109 marktr("Status bar background: active"), Color.decode("#aaff5e"));114 marktr("Status bar background: active"), "#aaff5e"); 110 115 111 116 /** 112 117 * Property for map status foreground color. … … public final class MapStatus extends JPanel implements Helpful, Destroyable, Pre 567 572 } 568 573 text.append(name); 569 574 570 boolean idShown = Main.pref.getBoolean("osm-primitives.showid");575 boolean idShown = SHOW_ID.get(); 571 576 // fix #7557 - do not show ID twice 572 577 573 578 if (!osm.isNew() && !idShown) { … … public final class MapStatus extends JPanel implements Helpful, Destroyable, Pre 1020 1025 */ 1021 1026 public void setDist(double dist) { 1022 1027 distValue = dist; 1023 distText.setText(dist < 0 ? "--" : NavigatableComponent.getDistText(dist, DECIMAL_FORMAT, DISTANCE_THRESHOLD ));1028 distText.setText(dist < 0 ? "--" : NavigatableComponent.getDistText(dist, DECIMAL_FORMAT, DISTANCE_THRESHOLD.get())); 1024 1029 } 1025 1030 1026 1031 /** -
src/org/openstreetmap/josm/gui/conflict/ConflictColors.java
diff --git a/src/org/openstreetmap/josm/gui/conflict/ConflictColors.java b/src/org/openstreetmap/josm/gui/conflict/ConflictColors.java index 2d5e7e3..96e5a29 100644
a b import static org.openstreetmap.josm.tools.I18n.marktr; 5 5 6 6 import java.awt.Color; 7 7 8 import org.openstreetmap.josm.Main; 9 import org.openstreetmap.josm.data.Preferences.ColorKey; 8 import org.openstreetmap.josm.data.preferences.ColorProperty; 10 9 11 10 /** 12 11 * Conflict color constants. 13 12 * @since 4162 14 13 */ 15 public enum ConflictColors implements ColorKey{14 public enum ConflictColors { 16 15 17 16 /** Conflict background: no conflict */ 18 17 BGCOLOR_NO_CONFLICT(marktr("Conflict background: no conflict"), new Color(234, 234, 234)), … … public enum ConflictColors implements ColorKey { 82 81 /** Conflict foreground: remove member */ 83 82 FGCOLOR_MEMBER_REMOVE(marktr("Conflict foreground: remove member"), Color.black); 84 83 85 private final String name; 86 private final Color defaultColor; 84 private final ColorProperty property; 87 85 88 86 ConflictColors(String name, Color defaultColor) { 89 this.name = name; 90 this.defaultColor = defaultColor; 91 } 92 93 @Override 94 public String getColorName() { 95 return name; 96 } 97 98 @Override 99 public Color getDefaultValue() { 100 return defaultColor; 101 } 102 103 @Override 104 public String getSpecialName() { 105 return null; 87 property = new ColorProperty(name, defaultColor); 106 88 } 107 89 108 90 /** … … public enum ConflictColors implements ColorKey { 110 92 * @return the color 111 93 */ 112 94 public Color get() { 113 return Main.pref.getColor(this);95 return property.get(); 114 96 } 115 97 116 98 /** -
src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
diff --git a/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java b/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java index 2b7798b..9dbc439 100644
a b import java.util.ArrayList; 20 20 import java.util.Arrays; 21 21 import java.util.Collections; 22 22 import java.util.List; 23 import java.util.Objects; 23 24 import java.util.concurrent.CopyOnWriteArrayList; 24 25 25 26 import javax.swing.AbstractAction; … … import javax.swing.table.TableModel; 44 45 45 46 import org.openstreetmap.josm.Main; 46 47 import org.openstreetmap.josm.actions.MergeLayerAction; 48 import org.openstreetmap.josm.data.preferences.AbstractProperty; 47 49 import org.openstreetmap.josm.gui.MapFrame; 48 50 import org.openstreetmap.josm.gui.MapView; 49 51 import org.openstreetmap.josm.gui.SideButton; … … public class LayerListDialog extends ToggleDialog { 545 547 label.setFont(label.getFont().deriveFont(Font.BOLD)); 546 548 } 547 549 if (Main.pref.getBoolean("dialog.layer.colorname", true)) { 548 Color c = layer.getColor(false); 549 if (c != null) { 550 Color oc = null; 551 for (Layer l : model.getLayers()) { 552 oc = l.getColor(false); 553 if (oc != null) { 554 if (oc.equals(c)) { 555 oc = null; 556 } else { 557 break; 558 } 559 } 560 } 550 AbstractProperty<Color> prop = layer.getColorProperty(); 551 Color c = prop == null ? null : prop.get(); 552 if (c == null || !model.getLayers().stream() 553 .map(Layer::getColorProperty) 554 .filter(Objects::nonNull) 555 .map(AbstractProperty::get) 556 .anyMatch(oc -> oc != null && !oc.equals(c))) { 561 557 /* not more than one color, don't use coloring */ 562 if (oc == null) { 563 c = null; 564 } 565 } 566 if (c == null) { 567 c = UIManager.getColor(isSelected ? "Table.selectionForeground" : "Table.foreground"); 558 label.setForeground(UIManager.getColor(isSelected ? "Table.selectionForeground" : "Table.foreground")); 559 } else { 560 label.setForeground(c); 568 561 } 569 label.setForeground(c);570 562 } 571 563 label.setIcon(layer.getIcon()); 572 564 label.setToolTipText(layer.getToolTipText()); -
src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java
diff --git a/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java b/src/org/openstreetmap/josm/gui/dialogs/properties/PropertiesDialog.java index 70c3a4f..f7fa2af 100644
a b import org.openstreetmap.josm.actions.search.SearchCompiler; 63 63 import org.openstreetmap.josm.command.ChangeCommand; 64 64 import org.openstreetmap.josm.command.ChangePropertyCommand; 65 65 import org.openstreetmap.josm.command.Command; 66 import org.openstreetmap.josm.data.Preferences.PreferenceChange Event;66 import org.openstreetmap.josm.data.Preferences.PreferenceChangedListener; 67 67 import org.openstreetmap.josm.data.SelectionChangedListener; 68 68 import org.openstreetmap.josm.data.osm.IRelation; 69 69 import org.openstreetmap.josm.data.osm.Node; … … implements SelectionChangedListener, ActiveLayerChangeListener, DataSetListenerA 225 225 private final JLabel selectSth = new JLabel("<html><p>" 226 226 + tr("Select objects for which to change tags.") + "</p></html>"); 227 227 228 private final PreferenceChangedListener preferenceListener = e -> { 229 if (Main.getLayerManager().getEditDataSet() != null) { 230 // Re-load data when display preference change 231 updateSelection(); 232 }}; 233 228 234 private final transient TaggingPresetHandler presetHandler = new TaggingPresetHandler() { 229 235 @Override 230 236 public void updateTags(List<Tag> tags) { … … implements SelectionChangedListener, ActiveLayerChangeListener, DataSetListenerA 297 303 298 304 editHelper.loadTagsIfNeeded(); 299 305 300 Main.pref.add PreferenceChangeListener(this);306 Main.pref.addKeyPreferenceChangeListener("display.discardable-keys", preferenceListener); 301 307 } 302 308 303 309 private void buildTagsTable() { … … implements SelectionChangedListener, ActiveLayerChangeListener, DataSetListenerA 605 611 @Override 606 612 public void destroy() { 607 613 super.destroy(); 608 Main.pref.remove PreferenceChangeListener(this);614 Main.pref.removeKeyPreferenceChangeListener("display.discardable-keys", preferenceListener); 609 615 Container parent = pluginHook.getParent(); 610 616 if (parent != null) { 611 617 parent.remove(pluginHook); … … implements SelectionChangedListener, ActiveLayerChangeListener, DataSetListenerA 1393 1399 return ss; 1394 1400 } 1395 1401 1396 @Override1397 public void preferenceChanged(PreferenceChangeEvent e) {1398 super.preferenceChanged(e);1399 if ("display.discardable-keys".equals(e.getKey()) && Main.getLayerManager().getEditDataSet() != null) {1400 // Re-load data when display preference change1401 updateSelection();1402 }1403 }1404 1405 1402 /** 1406 1403 * Clears the row selection when it is filtered away by the row sorter. 1407 1404 */ -
src/org/openstreetmap/josm/gui/layer/CustomizeColor.java
diff --git a/src/org/openstreetmap/josm/gui/layer/CustomizeColor.java b/src/org/openstreetmap/josm/gui/layer/CustomizeColor.java index e39f575..1d53e28 100644
a b import static org.openstreetmap.josm.tools.I18n.tr; 7 7 import java.awt.Color; 8 8 import java.awt.Component; 9 9 import java.awt.event.ActionEvent; 10 import java.util. LinkedList;10 import java.util.Collections; 11 11 import java.util.List; 12 import java.util.Objects; 13 import java.util.stream.Collectors; 12 14 13 15 import javax.swing.AbstractAction; 14 16 import javax.swing.Action; … … import javax.swing.JMenuItem; 17 19 import javax.swing.JOptionPane; 18 20 19 21 import org.openstreetmap.josm.Main; 22 import org.openstreetmap.josm.data.preferences.AbstractProperty; 23 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 20 24 import org.openstreetmap.josm.gui.layer.Layer.LayerAction; 21 25 import org.openstreetmap.josm.gui.layer.Layer.MultiLayerAction; 26 import org.openstreetmap.josm.tools.CheckParameterUtil; 22 27 import org.openstreetmap.josm.tools.ImageProvider; 23 28 24 29 public class CustomizeColor extends AbstractAction implements LayerAction, MultiLayerAction { 25 private final transient List< Layer> layers;30 private final transient List<AbstractProperty<Color>> colors; 26 31 27 32 /** 28 33 * Constructs a new {@code CustomizeColor} for a given list of layers. … … public class CustomizeColor extends AbstractAction implements LayerAction, Multi 30 35 */ 31 36 public CustomizeColor(List<Layer> l) { 32 37 super(tr("Customize Color"), ImageProvider.get("colorchooser")); 38 colors = l.stream().map(Layer::getColorProperty).collect(Collectors.toList()); 39 CheckParameterUtil.ensureThat(colors.stream().allMatch(Objects::nonNull), "All layers must have colors."); 33 40 putValue("help", ht("/Action/LayerCustomizeColor")); 34 layers = l;35 41 } 36 42 37 43 /** … … public class CustomizeColor extends AbstractAction implements LayerAction, Multi 39 45 * @param l layer 40 46 */ 41 47 public CustomizeColor(Layer l) { 42 this(new LinkedList<Layer>()); 43 layers.add(l); 48 this(Collections.singletonList(l)); 44 49 } 45 50 46 51 @Override 47 52 public boolean supportLayers(List<Layer> layers) { 48 for (Layer layer: layers) { 49 if (layer.getColor(false) == null) 50 return false; 51 } 52 return true; 53 return layers.stream().allMatch(l -> l.getColorProperty() != null); 53 54 } 54 55 55 56 @Override … … public class CustomizeColor extends AbstractAction implements LayerAction, Multi 64 65 65 66 @Override 66 67 public void actionPerformed(ActionEvent e) { 67 Color cl = layers.get(0).getColor(false); 68 if (cl == null) 69 cl = Color.gray; 68 Color cl = colors.stream().map(c -> c.get()).filter(Objects::nonNull).findAny().orElse(Color.GRAY); 70 69 JColorChooser c = new JColorChooser(cl); 71 70 Object[] options = new Object[]{tr("OK"), tr("Cancel"), tr("Default")}; 72 71 int answer = JOptionPane.showOptionDialog( … … public class CustomizeColor extends AbstractAction implements LayerAction, Multi 81 80 ); 82 81 switch (answer) { 83 82 case 0: 84 for (Layer layer : layers) { 85 Main.pref.putColor("layer "+layer.getName(), c.getColor()); 86 } 83 colors.stream().forEach(prop -> prop.put(c.getColor())); 87 84 break; 88 85 case 1: 89 86 return; 90 87 case 2: 91 for (Layer layer : layers) { 92 Main.pref.putColor("layer "+layer.getName(), null); 93 } 88 colors.stream().forEach(prop -> prop.put(null)); 94 89 break; 95 90 } 96 Main.map.repaint(); 91 // TODO: Make the layer dialog listen to property change events so that this is not needed any more. 92 LayerListDialog.getInstance().repaint(); 97 93 } 98 94 } -
src/org/openstreetmap/josm/gui/layer/GpxLayer.java
diff --git a/src/org/openstreetmap/josm/gui/layer/GpxLayer.java b/src/org/openstreetmap/josm/gui/layer/GpxLayer.java index 482d7f7..a10d1b8 100644
a b package org.openstreetmap.josm.gui.layer; 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 import static org.openstreetmap.josm.tools.I18n.trn; 6 6 7 import java.awt.Color;8 7 import java.awt.Dimension; 9 8 import java.awt.Graphics2D; 10 9 import java.io.File; … … import org.openstreetmap.josm.data.gpx.GpxData; 31 30 import org.openstreetmap.josm.data.gpx.GpxTrack; 32 31 import org.openstreetmap.josm.data.gpx.WayPoint; 33 32 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 33 import org.openstreetmap.josm.data.preferences.ColorProperty; 34 34 import org.openstreetmap.josm.data.projection.Projection; 35 35 import org.openstreetmap.josm.gui.MapView; 36 36 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; … … public class GpxLayer extends Layer { 88 88 public GpxLayer(GpxData d, String name, boolean isLocal) { 89 89 super(d.getString(GpxConstants.META_NAME)); 90 90 data = d; 91 drawHelper = new GpxDrawHelper(data );91 drawHelper = new GpxDrawHelper(data, getColorProperty()); 92 92 SystemOfMeasurement.addSoMChangeListener(drawHelper); 93 93 ensureTrackVisibilityLength(); 94 94 setName(name); … … public class GpxLayer extends Layer { 96 96 } 97 97 98 98 @Override 99 p ublic Color getColor(boolean ignoreCustom) {100 return drawHelper.getColor(getName(), ignoreCustom);99 protected ColorProperty getBaseColorProperty() { 100 return GpxDrawHelper.DEFAULT_COLOR; 101 101 } 102 102 103 103 /** -
src/org/openstreetmap/josm/gui/layer/Layer.java
diff --git a/src/org/openstreetmap/josm/gui/layer/Layer.java b/src/org/openstreetmap/josm/gui/layer/Layer.java index 7347110..7c85a6c 100644
a b import org.openstreetmap.josm.actions.SaveActionBase; 25 25 import org.openstreetmap.josm.actions.SaveAsAction; 26 26 import org.openstreetmap.josm.data.ProjectionBounds; 27 27 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 28 import org.openstreetmap.josm.data.preferences.AbstractProperty; 29 import org.openstreetmap.josm.data.preferences.AbstractProperty.ValueChangeListener; 30 import org.openstreetmap.josm.data.preferences.ColorProperty; 28 31 import org.openstreetmap.josm.data.projection.Projection; 29 32 import org.openstreetmap.josm.data.projection.ProjectionChangeListener; 30 33 import org.openstreetmap.josm.tools.Destroyable; … … public abstract class Layer extends AbstractMapViewPaintable implements Destroya 145 148 */ 146 149 private File associatedFile; 147 150 151 private final ValueChangeListener<Object> invalidateListener = change -> invalidate(); 152 148 153 /** 149 154 * Create the layer and fill in the necessary components. 150 155 * @param name Layer name … … public abstract class Layer extends AbstractMapViewPaintable implements Destroya 181 186 * is used. When this is true, then even for custom coloring the base 182 187 * color is returned - mainly for layer internal use. 183 188 * @return layer color 189 * @deprecated Use the new {@link #getColorProperty()}. To be removed end of 2016. 184 190 */ 191 @Deprecated 185 192 public Color getColor(boolean ignoreCustom) { 186 193 return null; 187 194 } 188 195 189 196 /** 197 * Gets the color property to use for this layer. 198 * @return The color property. 199 */ 200 public AbstractProperty<Color> getColorProperty() { 201 ColorProperty base = getBaseColorProperty(); 202 if (base != null) { 203 // cannot cache this - name may change. 204 return base.getChildColor("layer " + getName()); 205 } else { 206 return null; 207 } 208 } 209 210 /** 211 * Gets the color property that stores the default color for this layer. 212 * @return The property or <code>null</code> if this layer is not colored. 213 */ 214 protected ColorProperty getBaseColorProperty() { 215 return null; 216 } 217 218 private void addColorPropertyListener() { 219 AbstractProperty<Color> colorProperty = getColorProperty(); 220 if (colorProperty != null) { 221 colorProperty.addWeakListener(invalidateListener); 222 } 223 } 224 225 private void removeColorPropertyListener() { 226 AbstractProperty<Color> colorProperty = getColorProperty(); 227 if (colorProperty != null) { 228 colorProperty.removeListener(invalidateListener); 229 } 230 } 231 232 /** 190 233 * @return A small tooltip hint about some statistics for this layer. 191 234 */ 192 235 public abstract String getToolTipText(); … … public abstract class Layer extends AbstractMapViewPaintable implements Destroya 264 307 * @param name the name. If null, the name is set to the empty string. 265 308 */ 266 309 public final void setName(String name) { 310 if (this.name != null) { 311 removeColorPropertyListener(); 312 } 267 313 if (name == null) { 268 314 name = ""; 269 315 } 316 270 317 String oldValue = this.name; 271 318 this.name = name; 272 319 if (!this.name.equals(oldValue)) { 273 320 propertyChangeSupport.firePropertyChange(NAME_PROP, oldValue, this.name); 274 321 } 322 323 // re-add listener 324 addColorPropertyListener(); 325 invalidate(); 275 326 } 276 327 277 328 /** -
src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java
diff --git a/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java b/src/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelper.java index 8e138db..03b537a 100644
a b import org.openstreetmap.josm.data.coor.LatLon; 24 24 import org.openstreetmap.josm.data.gpx.GpxConstants; 25 25 import org.openstreetmap.josm.data.gpx.GpxData; 26 26 import org.openstreetmap.josm.data.gpx.WayPoint; 27 import org.openstreetmap.josm.data.preferences.AbstractProperty; 28 import org.openstreetmap.josm.data.preferences.ColorProperty; 27 29 import org.openstreetmap.josm.gui.MapView; 28 30 import org.openstreetmap.josm.tools.ColorScale; 29 31 … … import org.openstreetmap.josm.tools.ColorScale; 32 34 * @since 7319 33 35 */ 34 36 public class GpxDrawHelper implements SoMChangeListener { 37 38 /** 39 * The color that is used for drawing GPX points. 40 */ 41 public static final ColorProperty DEFAULT_COLOR = new ColorProperty(marktr("gps point"), Color.magenta); 42 35 43 private final GpxData data; 36 44 37 45 // draw lines between points belonging to different segments … … public class GpxDrawHelper implements SoMChangeListener { 83 91 /** Opacity for hdop points **/ 84 92 private int hdopAlpha; 85 93 86 private static final Color DEFAULT_COLOR = Color.magenta;87 88 94 // lookup array to draw arrows without doing any math 89 95 private static final int ll0 = 9; 90 96 private static final int sl4 = 5; … … public class GpxDrawHelper implements SoMChangeListener { 133 139 /** 134 140 * Constructs a new {@code GpxDrawHelper}. 135 141 * @param gpxData GPX data 142 * @param abstractProperty The color to draw with 136 143 */ 137 public GpxDrawHelper(GpxData gpxData ) {144 public GpxDrawHelper(GpxData gpxData, AbstractProperty<Color> abstractProperty) { 138 145 data = gpxData; 139 146 setupColors(); 140 147 } … … public class GpxDrawHelper implements SoMChangeListener { 150 157 * @return the color or null if the color is not constant 151 158 */ 152 159 public Color getColor(String layerName, boolean ignoreCustom) { 153 Color c = Main.pref.getColor(marktr("gps point"), specName(layerName), DEFAULT_COLOR); 154 return ignoreCustom || getColorMode(layerName) == ColorMode.NONE ? c : null; 160 if (ignoreCustom || getColorMode(layerName) == ColorMode.NONE) { 161 return DEFAULT_COLOR.getChildColor(specName(layerName)).get(); 162 } else { 163 return null; 164 } 155 165 } 156 166 157 167 /** … … public class GpxDrawHelper implements SoMChangeListener { 173 183 * @return the color 174 184 **/ 175 185 public static Color getGenericColor() { 176 return Main.pref.getColor(marktr("gps point"), DEFAULT_COLOR);186 return DEFAULT_COLOR.get(); 177 187 } 178 188 179 189 /** -
src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java
diff --git a/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java b/src/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayer.java index 15abf70..43c265b 100644
a b import org.openstreetmap.josm.data.gpx.GpxData; 37 37 import org.openstreetmap.josm.data.gpx.GpxLink; 38 38 import org.openstreetmap.josm.data.gpx.WayPoint; 39 39 import org.openstreetmap.josm.data.osm.visitor.BoundingXYVisitor; 40 import org.openstreetmap.josm.data.preferences.ColorProperty; 40 41 import org.openstreetmap.josm.gui.MapView; 41 42 import org.openstreetmap.josm.gui.dialogs.LayerListDialog; 42 43 import org.openstreetmap.josm.gui.dialogs.LayerListPopup; … … public class MarkerLayer extends Layer implements JumpToMarkerLayer { 73 74 public AudioMarker syncAudioMarker; 74 75 75 76 private static final Color DEFAULT_COLOR = Color.magenta; 77 private static final ColorProperty COLOR_PROPERTY = new ColorProperty(marktr("gps marker"), DEFAULT_COLOR); 76 78 77 79 /** 78 80 * Constructs a new {@code MarkerLayer}. … … public class MarkerLayer extends Layer implements JumpToMarkerLayer { 194 196 } 195 197 196 198 @Override 197 p ublic Color getColor(boolean ignoreCustom) {198 return Main.pref.getColor(marktr("gps marker"), "layer "+getName(), DEFAULT_COLOR);199 protected ColorProperty getBaseColorProperty() { 200 return COLOR_PROPERTY; 199 201 } 200 202 201 203 /* for preferences */ 202 204 public static Color getGenericColor() { 203 return Main.pref.getColor(marktr("gps marker"), DEFAULT_COLOR);205 return COLOR_PROPERTY.get(); 204 206 } 205 207 206 208 @Override 207 209 public void paint(Graphics2D g, MapView mv, Bounds box) { 208 210 boolean showTextOrIcon = isTextOrIconShown(); 209 g.setColor(getColor (true));211 g.setColor(getColorProperty().get()); 210 212 211 213 if (mousePressed) { 212 214 boolean mousePressedTmp = mousePressed; -
src/org/openstreetmap/josm/gui/preferences/display/ColorPreference.java
diff --git a/src/org/openstreetmap/josm/gui/preferences/display/ColorPreference.java b/src/org/openstreetmap/josm/gui/preferences/display/ColorPreference.java index ca7c4aa..f7ca354 100644
a b public class ColorPreference implements SubPreferenceSetting { 256 256 * Add all missing color entries. 257 257 */ 258 258 private static void fixColorPrefixes() { 259 PaintColors. getColors();259 PaintColors.values(); 260 260 ConflictColors.getColors(); 261 261 Severity.getColors(); 262 262 MarkerLayer.getGenericColor(); -
new file src/org/openstreetmap/josm/tools/ListenerList.java
diff --git a/src/org/openstreetmap/josm/tools/ListenerList.java b/src/org/openstreetmap/josm/tools/ListenerList.java new file mode 100644 index 0000000..761e675
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.tools; 3 4 import java.lang.ref.WeakReference; 5 import java.text.MessageFormat; 6 import java.util.HashMap; 7 import java.util.Iterator; 8 import java.util.Objects; 9 import java.util.concurrent.CopyOnWriteArrayList; 10 import java.util.stream.Stream; 11 12 import org.openstreetmap.josm.Main; 13 14 /** 15 * This is a list of listeners. It does error checking and allows you to fire all listeners. 16 * 17 * @author Michael Zangl 18 * @param <T> The type of listener contained in this list. 19 */ 20 public class ListenerList<T> { 21 /** 22 * This is a function that can be invoked for every listener. 23 * @param <T> the listener type. 24 */ 25 @FunctionalInterface 26 public interface EventFirerer<T> { 27 /** 28 * Should fire the event for the given listener. 29 * @param listener The listener to fire the event for. 30 */ 31 void fire(T listener); 32 } 33 34 private static final class WeakListener<T> { 35 36 private WeakReference<T> listener; 37 38 WeakListener(T listener) { 39 this.listener = new WeakReference<>(listener); 40 } 41 42 @Override 43 public boolean equals(Object obj) { 44 if (obj != null && obj.getClass() == WeakListener.class) { 45 return Objects.equals(listener.get(), ((WeakListener<?>) obj).listener.get()); 46 } else { 47 return false; 48 } 49 } 50 51 @Override 52 public int hashCode() { 53 T l = listener.get(); 54 if (l == null) { 55 return 0; 56 } else { 57 return l.hashCode(); 58 } 59 } 60 61 @Override 62 public String toString() { 63 return "WeakListener [listener=" + listener + "]"; 64 } 65 } 66 67 private final CopyOnWriteArrayList<T> listeners = new CopyOnWriteArrayList<>(); 68 private final CopyOnWriteArrayList<WeakListener<T>> weakListeners = new CopyOnWriteArrayList<>(); 69 70 protected ListenerList() { 71 // hide 72 } 73 74 /** 75 * Adds a listener. The listener will not prevent the object from being garbage collected. 76 * 77 * This should be used with care. It is better to add good cleanup code. 78 * @param listener The listener. 79 */ 80 public synchronized void addWeakListener(T listener) { 81 ensureNotInList(listener); 82 // clean the weak listeners, just to be sure... 83 while (weakListeners.remove(new WeakListener<T>(null))) { 84 // continue 85 } 86 weakListeners.add(new WeakListener<>(listener)); 87 } 88 89 /** 90 * Adds a listener. 91 * @param listener The listener to add. 92 */ 93 public synchronized void addListener(T listener) { 94 ensureNotInList(listener); 95 listeners.add(listener); 96 } 97 98 private void ensureNotInList(T listener) { 99 CheckParameterUtil.ensureParameterNotNull(listener, "listener"); 100 if (containsListener(listener)) { 101 failAdd(listener); 102 } 103 } 104 105 protected void failAdd(T listener) { 106 throw new IllegalArgumentException( 107 MessageFormat.format("Listener {0} (instance of {1}) was already registered.", listener.toString(), 108 listener.getClass().getName())); 109 } 110 111 private boolean containsListener(T listener) { 112 return listeners.contains(listener) || weakListeners.contains(new WeakListener<>(listener)); 113 } 114 115 /** 116 * Removes a listener. 117 * @param listener The listener to remove. 118 * @throws IllegalArgumentException if the listener was not registered before 119 */ 120 public synchronized void removeListener(T listener) { 121 if (!listeners.remove(listener) && !weakListeners.remove(new WeakListener<>(listener))) { 122 failRemove(listener); 123 } 124 } 125 126 protected void failRemove(T listener) { 127 throw new IllegalArgumentException( 128 MessageFormat.format("Listener {0} (instance of {1}) was not registered before or already removed.", 129 listener.toString(), listener.getClass().getName())); 130 } 131 132 /** 133 * Check if any listeners are registered. 134 * @return <code>true</code> if any are registered. 135 */ 136 public boolean hasListeners() { 137 return !listeners.isEmpty(); 138 } 139 140 /** 141 * Fires an event to every listener. 142 * @param eventFirerer The firerer to invoke the event method of the listener. 143 */ 144 public void fireEvent(EventFirerer<T> eventFirerer) { 145 for (T l : listeners) { 146 eventFirerer.fire(l); 147 } 148 for (Iterator<WeakListener<T>> iterator = weakListeners.iterator(); iterator.hasNext();) { 149 WeakListener<T> weakLink = iterator.next(); 150 T l = weakLink.listener.get(); 151 if (l == null) { 152 iterator.remove(); 153 } else { 154 eventFirerer.fire(l); 155 } 156 } 157 } 158 159 /** 160 * This is a special {@link ListenerList} that traces calls to the add/remove methods. This may cause memory leaks. 161 * @author Michael Zangl 162 * 163 * @param <T> 164 */ 165 public static class TracingListenerList<T> extends ListenerList<T> { 166 private HashMap<T, StackTraceElement[]> listenersAdded = new HashMap<>(); 167 private HashMap<T, StackTraceElement[]> listenersRemoved = new HashMap<>(); 168 169 protected TracingListenerList() { 170 // hidden 171 } 172 173 @Override 174 public synchronized void addListener(T listener) { 175 super.addListener(listener); 176 listenersRemoved.remove(listener); 177 listenersAdded.put(listener, Thread.currentThread().getStackTrace()); 178 } 179 180 @Override 181 public synchronized void addWeakListener(T listener) { 182 super.addWeakListener(listener); 183 listenersRemoved.remove(listener); 184 listenersAdded.put(listener, Thread.currentThread().getStackTrace()); 185 } 186 187 @Override 188 public synchronized void removeListener(T listener) { 189 super.removeListener(listener); 190 listenersAdded.remove(listener); 191 listenersRemoved.put(listener, Thread.currentThread().getStackTrace()); 192 } 193 194 @Override 195 protected void failAdd(T listener) { 196 Main.trace("Previous addition of the listener"); 197 dumpStack(listenersAdded.get(listener)); 198 super.failAdd(listener); 199 } 200 201 @Override 202 protected void failRemove(T listener) { 203 Main.trace("Previous removal of the listener"); 204 dumpStack(listenersRemoved.get(listener)); 205 super.failRemove(listener); 206 } 207 208 private static void dumpStack(StackTraceElement[] stackTraceElements) { 209 if (stackTraceElements == null) { 210 Main.trace(" - (no trace recorded)"); 211 } else { 212 Stream.of(stackTraceElements).limit(20).forEach( 213 e -> Main.trace(e.getClassName() + "." + e.getMethodName() + " line " + e.getLineNumber())); 214 } 215 } 216 } 217 218 /** 219 * Create a new listener list 220 * @param <T> The listener type the list should hold. 221 * @return A new list. A tracing list is created if trace is enabled. 222 */ 223 public static <T> ListenerList<T> create() { 224 if (Main.isTraceEnabled()) { 225 return new TracingListenerList<>(); 226 } else { 227 return new ListenerList<>(); 228 } 229 } 230 } -
new file test/unit/org/openstreetmap/josm/data/preferences/ColorPropertyTest.java
diff --git a/test/unit/org/openstreetmap/josm/data/preferences/ColorPropertyTest.java b/test/unit/org/openstreetmap/josm/data/preferences/ColorPropertyTest.java new file mode 100644 index 0000000..d76ad8f
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.data.preferences; 3 4 import static org.junit.Assert.assertEquals; 5 6 import java.awt.Color; 7 8 import org.junit.Before; 9 import org.junit.Rule; 10 import org.junit.Test; 11 import org.openstreetmap.josm.Main; 12 import org.openstreetmap.josm.testutils.JOSMTestRules; 13 14 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 15 16 /** 17 * Test {@link ColorProperty} 18 * @author Michael Zangl 19 * @since xxx 20 */ 21 public class ColorPropertyTest { 22 /** 23 * This is a preference test. 24 */ 25 @Rule 26 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") 27 public JOSMTestRules test = new JOSMTestRules().preferences(); 28 private ColorProperty base; 29 30 /** 31 * Set up test case 32 */ 33 @Before 34 public void createTestProperty() { 35 base = new ColorProperty("test", Color.RED); 36 } 37 38 /** 39 * Test {@link ColorProperty#get()} 40 */ 41 @Test 42 public void testGet() { 43 assertEquals(Color.RED, base.get()); 44 45 Main.pref.put("color.test", "#00ff00"); 46 assertEquals(new Color(0xff00ff00), base.get()); 47 } 48 49 /** 50 * Test {@link ColorProperty#put()} 51 */ 52 @Test 53 public void testPut() { 54 assertEquals(Color.RED, base.get()); 55 56 base.put(new Color(0xff00ff00)); 57 assertEquals(new Color(0xff00ff00), base.get()); 58 assertEquals("#00ff00", Main.pref.get("color.test").toLowerCase()); 59 60 base.put(null); 61 assertEquals(Color.RED, base.get()); 62 } 63 64 /** 65 * Test {@link ColorProperty#getChildColor(String)} 66 */ 67 @Test 68 public void testGetChildColor() { 69 AbstractToStringProperty<Color> child = base.getChildColor("test2"); 70 71 assertEquals(Color.RED, child.get()); 72 73 base.put(Color.GREEN); 74 assertEquals(Color.GREEN, child.get()); 75 76 child.put(Color.YELLOW); 77 assertEquals(Color.YELLOW, child.get()); 78 assertEquals(Color.GREEN, base.get()); 79 80 child.put(null); 81 assertEquals(Color.GREEN, child.get()); 82 } 83 } -
test/unit/org/openstreetmap/josm/gui/layer/GpxLayerTest.java
diff --git a/test/unit/org/openstreetmap/josm/gui/layer/GpxLayerTest.java b/test/unit/org/openstreetmap/josm/gui/layer/GpxLayerTest.java index 1d8acb0..b151773 100644
a b public class GpxLayerTest { 73 73 GpxLayer layer = new GpxLayer(new GpxData(), "foo", false); 74 74 assertEquals("foo", layer.getName()); 75 75 assertFalse(layer.isLocalFile()); 76 assertEquals(Color.MAGENTA, layer.getColor (false));76 assertEquals(Color.MAGENTA, layer.getColorProperty().get()); 77 77 assertEquals("<html>0 tracks, 0 routes, 0 waypoints<br>Length: < 0.01 m<br></html>", layer.getToolTipText()); 78 78 79 79 GpxLayer layer2 = new GpxLayer(new GpxData(), "bar", true); 80 80 assertEquals("bar", layer2.getName()); 81 81 assertTrue(layer2.isLocalFile()); 82 assertEquals(Color.MAGENTA, layer2.getColor (true));82 assertEquals(Color.MAGENTA, layer2.getColorProperty().get()); 83 83 assertEquals("<html>0 tracks, 0 routes, 0 waypoints<br>Length: < 0.01 m<br></html>", layer2.getToolTipText()); 84 84 85 85 assertFalse(layer.isChanged()); -
test/unit/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelperTest.java
diff --git a/test/unit/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelperTest.java b/test/unit/org/openstreetmap/josm/gui/layer/gpx/GpxDrawHelperTest.java index ed17db6..9e83236 100644
a b package org.openstreetmap.josm.gui.layer.gpx; 3 3 4 4 import static org.junit.Assert.assertEquals; 5 5 6 import java.awt.Color; 6 7 import java.io.FileNotFoundException; 7 8 import java.io.IOException; 8 9 import java.util.ArrayList; … … import org.openstreetmap.josm.Main; 16 17 import org.openstreetmap.josm.TestUtils; 17 18 import org.openstreetmap.josm.data.gpx.GpxData; 18 19 import org.openstreetmap.josm.data.gpx.WayPoint; 20 import org.openstreetmap.josm.data.preferences.ColorProperty; 19 21 import org.openstreetmap.josm.io.GpxReaderTest; 20 22 import org.openstreetmap.josm.tools.ColorHelper; 21 23 import org.xml.sax.SAXException; … … public class GpxDrawHelperTest { 124 126 */ 125 127 static List<String> calculateColors(String fileName, String layerName, int n) throws IOException, SAXException { 126 128 final GpxData data = GpxReaderTest.parseGpxData(fileName); 127 final GpxDrawHelper gdh = new GpxDrawHelper(data );129 final GpxDrawHelper gdh = new GpxDrawHelper(data, new ColorProperty("x", Color.MAGENTA)); 128 130 gdh.readPreferences(layerName); 129 131 gdh.calculateColors(); 130 132 final Iterator<WayPoint> wayPointIterator = data.tracks.iterator().next().getSegments().iterator().next().getWayPoints().iterator(); -
test/unit/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayerTest.java
diff --git a/test/unit/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayerTest.java b/test/unit/org/openstreetmap/josm/gui/layer/markerlayer/MarkerLayerTest.java index 19dfbb0..a5596eb 100644
a b public class MarkerLayerTest { 41 41 MarkerLayer layer = new MarkerLayer(new GpxData(), "foo", null, null); 42 42 43 43 assertEquals("foo", layer.getName()); 44 assertEquals(Color.magenta, layer.getColor (false));44 assertEquals(Color.magenta, layer.getColorProperty().get()); 45 45 assertNotNull(layer.getIcon()); 46 46 assertEquals("0 markers", layer.getToolTipText()); 47 47 assertEquals("<html>foo consists of 0 markers</html>", layer.getInfoComponent()); … … public class MarkerLayerTest { 58 58 layer = new MarkerLayer(gpx, "bar", null, null); 59 59 60 60 assertEquals("bar", layer.getName()); 61 assertEquals(Color.magenta, layer.getColor (false));61 assertEquals(Color.magenta, layer.getColorProperty().get()); 62 62 assertNotNull(layer.getIcon()); 63 63 assertEquals("3 markers", layer.getToolTipText()); 64 64 assertEquals("<html>bar consists of 3 markers</html>", layer.getInfoComponent()); -
test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java
diff --git a/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java b/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java index 634b625..246bbff 100644
a b public class JOSMTestRules implements TestRule { 159 159 Statement statement = base; 160 160 if (timeout > 0) { 161 161 // TODO: new DisableOnDebug(timeout) 162 statement = new FailOnTimeoutStatement(statement, timeout);162 // statement = new FailOnTimeoutStatement(statement, timeout); 163 163 } 164 164 statement = new CreateJosmEnvironment(statement); 165 165 if (josmHome != null) {