001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.mappaint;
003
004import java.awt.Color;
005import java.util.Objects;
006import java.util.Optional;
007
008import javax.swing.Icon;
009
010import org.openstreetmap.josm.data.preferences.AbstractToStringProperty;
011import org.openstreetmap.josm.tools.ImageProvider;
012import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
013import org.openstreetmap.josm.tools.Logging;
014
015/**
016 * Setting to customize a MapPaint style.
017 *
018 * Can be changed by the user in the right click menu of the mappaint style
019 * dialog.
020 *
021 * Defined in the MapCSS style, e.g.
022 * <pre>
023 * setting::highway_casing {
024 *   type: boolean;
025 *   label: tr("Draw highway casing");
026 *   default: true;
027 * }
028 *
029 * way[highway][setting("highway_casing")] {
030 *   casing-width: 2;
031 *   casing-color: white;
032 * }
033 * </pre>
034 */
035public interface StyleSetting {
036
037    /**
038     * gets the value for this setting
039     * @return The value the user selected
040     */
041    Object getValue();
042
043    /**
044     * Create a matching {@link StyleSettingGui} instances for a given {@link StyleSetting} object.
045     * @return matching {@code StyleSettingGui}
046     * @throws UnsupportedOperationException when class of {@link StyleSetting} is not supported
047     */
048    default StyleSettingGui getStyleSettingGui() {
049        throw new UnsupportedOperationException(getClass() + " not supported");
050    }
051
052    /**
053     * Superclass of style settings and groups.
054     * @since 15289
055     */
056    abstract class LabeledStyleSetting implements Comparable<LabeledStyleSetting> {
057        public final StyleSource parentStyle;
058        public final String label;
059
060        LabeledStyleSetting(StyleSource parentStyle, String label) {
061            this.parentStyle = Objects.requireNonNull(parentStyle);
062            this.label = Objects.requireNonNull(label);
063        }
064
065        @Override
066        public int hashCode() {
067            return Objects.hash(label, parentStyle);
068        }
069
070        @Override
071        public boolean equals(Object obj) {
072            if (this == obj)
073                return true;
074            if (obj == null || getClass() != obj.getClass())
075                return false;
076            LabeledStyleSetting other = (LabeledStyleSetting) obj;
077            return Objects.equals(label, other.label) && Objects.equals(parentStyle, other.parentStyle);
078        }
079
080        @Override
081        public int compareTo(LabeledStyleSetting o) {
082            return label.compareTo(o.label);
083        }
084    }
085
086    /**
087     * A style setting group.
088     * @since 15289
089     */
090    class StyleSettingGroup extends LabeledStyleSetting {
091        /** group identifier */
092        public final String key;
093        /** group icon (optional) */
094        public final Icon icon;
095
096        StyleSettingGroup(StyleSource parentStyle, String label, String key, Icon icon) {
097            super(parentStyle, label);
098            this.key = Objects.requireNonNull(key);
099            this.icon = icon;
100        }
101
102        /**
103         * Creates a new {@code StyleSettingGroup}.
104         * @param c cascade
105         * @param parentStyle parent style source
106         * @param key group identifier
107         * @return newly created {@code StyleSettingGroup}
108         */
109        public static StyleSettingGroup create(Cascade c, StyleSource parentStyle, String key) {
110            String label = c.get("label", null, String.class);
111            if (label == null) {
112                Logging.warn("property 'label' required for StyleSettingGroup");
113                return null;
114            }
115            Icon icon = Optional.ofNullable(c.get("icon", null, String.class))
116                    .map(s -> ImageProvider.get(s, ImageSizes.MENU)).orElse(null);
117            return new StyleSettingGroup(parentStyle, label, key, icon);
118        }
119    }
120
121    class PropertyStyleSetting<T> extends LabeledStyleSetting implements StyleSetting {
122        private final Class<T> type;
123        private final AbstractToStringProperty<T> property;
124
125        PropertyStyleSetting(StyleSource parentStyle, String label, Class<T> type, AbstractToStringProperty<T> property) {
126            super(parentStyle, label);
127            this.type = type;
128            this.property = property;
129        }
130
131        /**
132         * Replies the property key.
133         * @return The property key
134         */
135        public String getKey() {
136            return property.getKey();
137        }
138
139        @Override
140        public T getValue() {
141            return property.get();
142        }
143
144        /**
145         * Sets this property to the specified value.
146         * @param value The new value of this property
147         */
148        public void setValue(T value) {
149            property.put(value);
150        }
151
152        /**
153         * Sets this property to the specified string value.
154         * @param value The new string value of this property
155         */
156        public void setStringValue(String value) {
157            setValue(Cascade.convertTo(value, type));
158        }
159
160        @Override
161        public StyleSettingGui getStyleSettingGui() {
162            return new PropertyStyleSettingGui<>(this);
163        }
164    }
165
166    /**
167     * A style setting for boolean value (yes / no).
168     */
169    class BooleanStyleSetting extends PropertyStyleSetting<Boolean> {
170        BooleanStyleSetting(StyleSource parentStyle, String label, AbstractToStringProperty<Boolean> property) {
171            super(parentStyle, label, Boolean.class, property);
172        }
173
174        @Override
175        public StyleSettingGui getStyleSettingGui() {
176            return new BooleanStyleSettingGui(this);
177        }
178    }
179
180    /**
181     * A style setting for color values.
182     * @since 16842
183     */
184    class ColorStyleSetting extends PropertyStyleSetting<Color> {
185        ColorStyleSetting(StyleSource parentStyle, String label, AbstractToStringProperty<Color> property) {
186            super(parentStyle, label, Color.class, property);
187        }
188
189        @Override
190        public StyleSettingGui getStyleSettingGui() {
191            return new ColorStyleSettingGui(this);
192        }
193    }
194}