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}