// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.gui.layer.imagery;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import org.openstreetmap.gui.jmapviewer.interfaces.TileSource;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.coor.EastNorth;
import org.openstreetmap.josm.data.preferences.BooleanProperty;
import org.openstreetmap.josm.gui.layer.AbstractTileSourceLayer;
import org.openstreetmap.josm.tools.CheckParameterUtil;
import org.openstreetmap.josm.tools.JosmRuntimeException;
import org.openstreetmap.josm.tools.bugreport.BugReport;
/**
* This are the preferences of how to display a {@link TileSource}.
* <p>
* They have been extracted from the {@link AbstractTileSourceLayer}. Each layer has one set of such settings.
* @author michael
* @since 10568
*/
public class TileSourceDisplaySettings {
/**
* A string returned by {@link DisplaySettingsChangeEvent#getChangedSetting()} if auto load was changed.
* @see TileSourceDisplaySettings#isAutoLoad()
*/
public static final String AUTO_LOAD = "automatic-downloading";
/**
* A string returned by {@link DisplaySettingsChangeEvent#getChangedSetting()} if auto zoom was changed.
* @see TileSourceDisplaySettings#isAutoZoom()
*/
public static final String AUTO_ZOOM = "automatically-change-resolution";
/**
* A string returned by {@link DisplaySettingsChangeEvent#getChangedSetting()} if the sow errors property was changed.
* @see TileSourceDisplaySettings#isShowErrors()
*/
private static final String SHOW_ERRORS = "show-errors";
private static final String DISPLACEMENT = "displacement";
private static final String PREFERENCE_PREFIX = "imagery.generic";
/**
* The default auto load property
*/
public static final BooleanProperty PROP_AUTO_LOAD = new BooleanProperty(PREFERENCE_PREFIX + ".default_autoload", true);
/**
* The default auto zoom property
*/
public static final BooleanProperty PROP_AUTO_ZOOM = new BooleanProperty(PREFERENCE_PREFIX + ".default_autozoom", true);
/** if layers changes automatically, when user zooms in */
private boolean autoZoom;
/** if layer automatically loads new tiles */
private boolean autoLoad;
/** if layer should show errors on tiles */
private boolean showErrors;
/**
* The displacement
*/
private EastNorth displacement = new EastNorth(0, 0);
private final CopyOnWriteArrayList<DisplaySettingsChangeListener> listeners = new CopyOnWriteArrayList<>();
/**
* Create a new {@link TileSourceDisplaySettings}
*/
public TileSourceDisplaySettings() {
this(new String[] {PREFERENCE_PREFIX});
}
/**
* Create a new {@link TileSourceDisplaySettings}
* @param preferencePrefix The additional prefix to scan for preferences.
*/
public TileSourceDisplaySettings(String preferencePrefix) {
this(PREFERENCE_PREFIX, preferencePrefix);
}
private TileSourceDisplaySettings(String... prefixes) {
autoZoom = getProperty(prefixes, "default_autozoom");
autoLoad = getProperty(prefixes, "default_autoload");
showErrors = getProperty(prefixes, "default_showerrors");
}
private static boolean getProperty(String[] prefixes, String name) {
// iterate through all values to force the preferences to receive the default value.
// we only support a default value of true.
boolean value = true;
for (String p : prefixes) {
String key = p + "." + name;
boolean currentValue = Main.pref.getBoolean(key, true);
if (!Main.pref.get(key).isEmpty()) {
value = currentValue;
}
}
return value;
}
/**
* Let the layer zoom automatically if the user zooms in
* @return auto zoom
*/
public boolean isAutoZoom() {
return autoZoom;
}
/**
* Sets the auto zoom property
* @param autoZoom {@code true} to let the layer zoom automatically if the user zooms in
* @see #isAutoZoom()
* @see #AUTO_ZOOM
*/
public void setAutoZoom(boolean autoZoom) {
this.autoZoom = autoZoom;
fireSettingsChange(AUTO_ZOOM);
}
/**
* Gets if the layer should automatically load new tiles.
* @return <code>true</code> if it should
*/
public boolean isAutoLoad() {
return autoLoad;
}
/**
* Sets the auto load property
* @param autoLoad {@code true} if the layer should automatically load new tiles
* @see #isAutoLoad()
* @see #AUTO_LOAD
*/
public void setAutoLoad(boolean autoLoad) {
this.autoLoad = autoLoad;
fireSettingsChange(AUTO_LOAD);
}
/**
* If the layer should display the errors it encountered while loading the tiles.
* @return <code>true</code> to show errors.
*/
public boolean isShowErrors() {
return showErrors;
}
/**
* Sets the show errors property. Fires a change event.
* @param showErrors {@code true} if the layer should display the errors it encountered while loading the tiles
* @see #isShowErrors()
* @see #SHOW_ERRORS
*/
public void setShowErrors(boolean showErrors) {
this.showErrors = showErrors;
fireSettingsChange(SHOW_ERRORS);
}
/**
* Gets the displacement in x (east) direction
* @return The displacement.
* @since 10571
*/
public double getDx() {
return displacement.east();
}
/**
* Gets the displacement in y (north) direction
* @return The displacement.
* @since 10571
*/
public double getDy() {
return displacement.north();
}
/**
* Gets the displacement of the image
* @return The displacement.
* @since 10571
*/
public EastNorth getDisplacement() {
return displacement;
}
/**
* Set the displacement
* @param displacement The new displacement
* @since 10571
*/
public void setDisplacement(EastNorth displacement) {
CheckParameterUtil.ensureValidCoordinates(displacement, "displacement");
this.displacement = displacement;
fireSettingsChange(DISPLACEMENT);
}
/**
* Adds the given value to the displacement.
* @param displacement The value to add.
* @since 10571
*/
public void addDisplacement(EastNorth displacement) {
CheckParameterUtil.ensureValidCoordinates(displacement, "displacement");
setDisplacement(this.displacement.add(displacement));
}
/**
* Notifies all listeners that the paint settings have changed
* @param changedSetting The setting name
*/
private void fireSettingsChange(String changedSetting) {
DisplaySettingsChangeEvent e = new DisplaySettingsChangeEvent(changedSetting);
for (DisplaySettingsChangeListener l : listeners) {
l.displaySettingsChanged(e);
}
}
/**
* Add a listener that listens to display settings changes.
* @param l The listener
*/
public void addSettingsChangeListener(DisplaySettingsChangeListener l) {
listeners.add(l);
}
/**
* Remove a listener that listens to display settings changes.
* @param l The listener
*/
public void removeSettingsChangeListener(DisplaySettingsChangeListener l) {
listeners.remove(l);
}
/**
* Stores the current settings object to the given hashmap.
* @param data The map to store the settings to.
* @see #loadFrom(Map)
*/
public void storeTo(Map<String, String> data) {
data.put(AUTO_LOAD, Boolean.toString(autoLoad));
data.put(AUTO_ZOOM, Boolean.toString(autoZoom));
data.put(SHOW_ERRORS, Boolean.toString(showErrors));
data.put("dx", String.valueOf(getDx()));
data.put("dy", String.valueOf(getDy()));
}
/**
* Load the settings from the given data instance.
* @param data The data
* @see #storeTo(Map)
*/
public void loadFrom(Map<String, String> data) {
try {
String doAutoLoad = data.get(AUTO_LOAD);
if (doAutoLoad != null) {
setAutoLoad(Boolean.parseBoolean(doAutoLoad));
}
String doAutoZoom = data.get(AUTO_ZOOM);
if (doAutoZoom != null) {
setAutoZoom(Boolean.parseBoolean(doAutoZoom));
}
String doShowErrors = data.get(SHOW_ERRORS);
if (doShowErrors != null) {
setShowErrors(Boolean.parseBoolean(doShowErrors));
}
String dx = data.get("dx");
String dy = data.get("dy");
if (dx != null && dy != null) {
setDisplacement(new EastNorth(Double.parseDouble(dx), Double.parseDouble(dy)));
}
} catch (JosmRuntimeException | IllegalArgumentException | IllegalStateException e) {
throw BugReport.intercept(e).put("data", data);
}
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (autoLoad ? 1231 : 1237);
result = prime * result + (autoZoom ? 1231 : 1237);
result = prime * result + (showErrors ? 1231 : 1237);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
TileSourceDisplaySettings other = (TileSourceDisplaySettings) obj;
if (autoLoad != other.autoLoad)
return false;
if (autoZoom != other.autoZoom)
return false;
if (showErrors != other.showErrors)
return false;
return true;
}
@Override
public String toString() {
return "TileSourceDisplaySettings [autoZoom=" + autoZoom + ", autoLoad=" + autoLoad + ", showErrors="
+ showErrors + ']';
}
/**
* A listener that listens to changes to the {@link TileSourceDisplaySettings} object.
* @author Michael Zangl
* @since 10600 (functional interface)
*/
@FunctionalInterface
public interface DisplaySettingsChangeListener {
/**
* Called whenever the display settings have changed.
* @param e The change event.
*/
void displaySettingsChanged(DisplaySettingsChangeEvent e);
}
/**
* An event that is created whenever the display settings change.
* @author Michael Zangl
*/
public static final class DisplaySettingsChangeEvent {
private final String changedSetting;
DisplaySettingsChangeEvent(String changedSetting) {
this.changedSetting = changedSetting;
}
/**
* Gets the setting that was changed
* @return The name of the changed setting.
*/
public String getChangedSetting() {
return changedSetting;
}
@Override
public String toString() {
return "DisplaySettingsChangeEvent [changedSetting=" + changedSetting + ']';
}
}
}