001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.layer.imagery; 003 004import java.util.Map; 005import java.util.concurrent.CopyOnWriteArrayList; 006 007import org.openstreetmap.gui.jmapviewer.interfaces.TileSource; 008import org.openstreetmap.josm.Main; 009import org.openstreetmap.josm.data.coor.EastNorth; 010import org.openstreetmap.josm.data.preferences.BooleanProperty; 011import org.openstreetmap.josm.gui.layer.AbstractTileSourceLayer; 012import org.openstreetmap.josm.tools.CheckParameterUtil; 013import org.openstreetmap.josm.tools.bugreport.BugReport; 014 015/** 016 * This are the preferences of how to display a {@link TileSource}. 017 * <p> 018 * They have been extracted from the {@link AbstractTileSourceLayer}. Each layer has one set of such settings. 019 * @author michael 020 * @since 10568 021 */ 022public class TileSourceDisplaySettings { 023 /** 024 * A string returned by {@link DisplaySettingsChangeEvent#getChangedSetting()} if auto load was changed. 025 * @see TileSourceDisplaySettings#isAutoLoad() 026 */ 027 public static final String AUTO_LOAD = "automatic-downloading"; 028 029 /** 030 * A string returned by {@link DisplaySettingsChangeEvent#getChangedSetting()} if auto zoom was changed. 031 * @see TileSourceDisplaySettings#isAutoZoom() 032 */ 033 public static final String AUTO_ZOOM = "automatically-change-resolution"; 034 035 /** 036 * A string returned by {@link DisplaySettingsChangeEvent#getChangedSetting()} if the sow errors property was changed. 037 * @see TileSourceDisplaySettings#isShowErrors() 038 */ 039 private static final String SHOW_ERRORS = "show-errors"; 040 041 private static final String DISPLACEMENT = "displacement"; 042 043 private static final String PREFERENCE_PREFIX = "imagery.generic"; 044 045 /** 046 * The default auto load property 047 */ 048 public static final BooleanProperty PROP_AUTO_LOAD = new BooleanProperty(PREFERENCE_PREFIX + ".default_autoload", true); 049 050 /** 051 * The default auto zoom property 052 */ 053 public static final BooleanProperty PROP_AUTO_ZOOM = new BooleanProperty(PREFERENCE_PREFIX + ".default_autozoom", true); 054 055 056 /** if layers changes automatically, when user zooms in */ 057 private boolean autoZoom; 058 /** if layer automatically loads new tiles */ 059 private boolean autoLoad; 060 /** if layer should show errors on tiles */ 061 private boolean showErrors; 062 063 /** 064 * The displacement 065 */ 066 private EastNorth displacement = new EastNorth(0, 0); 067 068 private final CopyOnWriteArrayList<DisplaySettingsChangeListener> listeners = new CopyOnWriteArrayList<>(); 069 070 /** 071 * Create a new {@link TileSourceDisplaySettings} 072 */ 073 public TileSourceDisplaySettings() { 074 this(new String[] {PREFERENCE_PREFIX}); 075 } 076 077 /** 078 * Create a new {@link TileSourceDisplaySettings} 079 * @param preferencePrefix The additional prefix to scan for preferences. 080 */ 081 public TileSourceDisplaySettings(String preferencePrefix) { 082 this(PREFERENCE_PREFIX, preferencePrefix); 083 } 084 085 private TileSourceDisplaySettings(String... prefixes) { 086 autoZoom = getProperty(prefixes, "default_autozoom"); 087 autoLoad = getProperty(prefixes, "default_autoload"); 088 showErrors = getProperty(prefixes, "default_showerrors"); 089 } 090 091 private static boolean getProperty(String[] prefixes, String name) { 092 // iterate through all values to force the preferences to receive the default value. 093 // we only support a default value of true. 094 boolean value = true; 095 for (String p : prefixes) { 096 String key = p + "." + name; 097 boolean currentValue = Main.pref.getBoolean(key, true); 098 if (!Main.pref.get(key).isEmpty()) { 099 value = currentValue; 100 } 101 } 102 return value; 103 } 104 105 /** 106 * Let the layer zoom automatically if the user zooms in 107 * @return auto zoom 108 */ 109 public boolean isAutoZoom() { 110 return autoZoom; 111 } 112 113 /** 114 * Sets the auto zoom property 115 * @param autoZoom {@code true} to let the layer zoom automatically if the user zooms in 116 * @see #isAutoZoom() 117 * @see #AUTO_ZOOM 118 */ 119 public void setAutoZoom(boolean autoZoom) { 120 this.autoZoom = autoZoom; 121 fireSettingsChange(AUTO_ZOOM); 122 } 123 124 /** 125 * Gets if the layer should automatically load new tiles. 126 * @return <code>true</code> if it should 127 */ 128 public boolean isAutoLoad() { 129 return autoLoad; 130 } 131 132 /** 133 * Sets the auto load property 134 * @param autoLoad {@code true} if the layer should automatically load new tiles 135 * @see #isAutoLoad() 136 * @see #AUTO_LOAD 137 */ 138 public void setAutoLoad(boolean autoLoad) { 139 this.autoLoad = autoLoad; 140 fireSettingsChange(AUTO_LOAD); 141 } 142 143 /** 144 * If the layer should display the errors it encountered while loading the tiles. 145 * @return <code>true</code> to show errors. 146 */ 147 public boolean isShowErrors() { 148 return showErrors; 149 } 150 151 /** 152 * Sets the show errors property. Fires a change event. 153 * @param showErrors {@code true} if the layer should display the errors it encountered while loading the tiles 154 * @see #isShowErrors() 155 * @see #SHOW_ERRORS 156 */ 157 public void setShowErrors(boolean showErrors) { 158 this.showErrors = showErrors; 159 fireSettingsChange(SHOW_ERRORS); 160 } 161 162 /** 163 * Gets the displacement in x (east) direction 164 * @return The displacement. 165 * @since 10571 166 */ 167 public double getDx() { 168 return displacement.east(); 169 } 170 171 /** 172 * Gets the displacement in y (north) direction 173 * @return The displacement. 174 * @since 10571 175 */ 176 public double getDy() { 177 return displacement.north(); 178 } 179 180 /** 181 * Gets the displacement of the image 182 * @return The displacement. 183 * @since 10571 184 */ 185 public EastNorth getDisplacement() { 186 return displacement; 187 } 188 189 /** 190 * Set the displacement 191 * @param displacement The new displacement 192 * @since 10571 193 */ 194 public void setDisplacement(EastNorth displacement) { 195 CheckParameterUtil.ensureValidCoordinates(displacement, "displacement"); 196 this.displacement = displacement; 197 fireSettingsChange(DISPLACEMENT); 198 } 199 200 /** 201 * Adds the given value to the displacement. 202 * @param displacement The value to add. 203 * @since 10571 204 */ 205 public void addDisplacement(EastNorth displacement) { 206 CheckParameterUtil.ensureValidCoordinates(displacement, "displacement"); 207 setDisplacement(this.displacement.add(displacement)); 208 } 209 210 /** 211 * Notifies all listeners that the paint settings have changed 212 * @param changedSetting The setting name 213 */ 214 private void fireSettingsChange(String changedSetting) { 215 DisplaySettingsChangeEvent e = new DisplaySettingsChangeEvent(changedSetting); 216 for (DisplaySettingsChangeListener l : listeners) { 217 l.displaySettingsChanged(e); 218 } 219 } 220 221 /** 222 * Add a listener that listens to display settings changes. 223 * @param l The listener 224 */ 225 public void addSettingsChangeListener(DisplaySettingsChangeListener l) { 226 listeners.add(l); 227 } 228 229 /** 230 * Remove a listener that listens to display settings changes. 231 * @param l The listener 232 */ 233 public void removeSettingsChangeListener(DisplaySettingsChangeListener l) { 234 listeners.remove(l); 235 } 236 237 /** 238 * Stores the current settings object to the given hashmap. 239 * @param data The map to store the settings to. 240 * @see #loadFrom(Map) 241 */ 242 public void storeTo(Map<String, String> data) { 243 data.put(AUTO_LOAD, Boolean.toString(autoLoad)); 244 data.put(AUTO_ZOOM, Boolean.toString(autoZoom)); 245 data.put(SHOW_ERRORS, Boolean.toString(showErrors)); 246 data.put("dx", String.valueOf(getDx())); 247 data.put("dy", String.valueOf(getDy())); 248 } 249 250 /** 251 * Load the settings from the given data instance. 252 * @param data The data 253 * @see #storeTo(Map) 254 */ 255 public void loadFrom(Map<String, String> data) { 256 try { 257 String doAutoLoad = data.get(AUTO_LOAD); 258 if (doAutoLoad != null) { 259 setAutoLoad(Boolean.parseBoolean(doAutoLoad)); 260 } 261 262 String doAutoZoom = data.get(AUTO_ZOOM); 263 if (doAutoZoom != null) { 264 setAutoZoom(Boolean.parseBoolean(doAutoZoom)); 265 } 266 267 String doShowErrors = data.get(SHOW_ERRORS); 268 if (doShowErrors != null) { 269 setShowErrors(Boolean.parseBoolean(doShowErrors)); 270 } 271 272 String dx = data.get("dx"); 273 String dy = data.get("dy"); 274 if (dx != null && dy != null) { 275 setDisplacement(new EastNorth(Double.parseDouble(dx), Double.parseDouble(dy))); 276 } 277 } catch (RuntimeException e) { 278 throw BugReport.intercept(e).put("data", data); 279 } 280 } 281 282 @Override 283 public int hashCode() { 284 final int prime = 31; 285 int result = 1; 286 result = prime * result + (autoLoad ? 1231 : 1237); 287 result = prime * result + (autoZoom ? 1231 : 1237); 288 result = prime * result + (showErrors ? 1231 : 1237); 289 return result; 290 } 291 292 @Override 293 public boolean equals(Object obj) { 294 if (this == obj) 295 return true; 296 if (obj == null) 297 return false; 298 if (getClass() != obj.getClass()) 299 return false; 300 TileSourceDisplaySettings other = (TileSourceDisplaySettings) obj; 301 if (autoLoad != other.autoLoad) 302 return false; 303 if (autoZoom != other.autoZoom) 304 return false; 305 if (showErrors != other.showErrors) 306 return false; 307 return true; 308 } 309 310 @Override 311 public String toString() { 312 return "TileSourceDisplaySettings [autoZoom=" + autoZoom + ", autoLoad=" + autoLoad + ", showErrors=" 313 + showErrors + ']'; 314 } 315 316 /** 317 * A listener that listens to changes to the {@link TileSourceDisplaySettings} object. 318 * @author Michael Zangl 319 * @since 10600 (functional interface) 320 */ 321 @FunctionalInterface 322 public interface DisplaySettingsChangeListener { 323 /** 324 * Called whenever the display settings have changed. 325 * @param e The change event. 326 */ 327 void displaySettingsChanged(DisplaySettingsChangeEvent e); 328 } 329 330 /** 331 * An event that is created whenever the display settings change. 332 * @author Michael Zangl 333 */ 334 public static final class DisplaySettingsChangeEvent { 335 private final String changedSetting; 336 337 DisplaySettingsChangeEvent(String changedSetting) { 338 this.changedSetting = changedSetting; 339 } 340 341 /** 342 * Gets the setting that was changed 343 * @return The name of the changed setting. 344 */ 345 public String getChangedSetting() { 346 return changedSetting; 347 } 348 349 @Override 350 public String toString() { 351 return "DisplaySettingsChangeEvent [changedSetting=" + changedSetting + ']'; 352 } 353 } 354}