001 // License: GPL. For details, see LICENSE file. 002 package org.openstreetmap.josm.gui.mappaint; 003 004 import static org.openstreetmap.josm.tools.Utils.equal; 005 006 import java.awt.Font; 007 import java.util.HashMap; 008 import java.util.Map; 009 010 import org.openstreetmap.josm.Main; 011 import org.openstreetmap.josm.data.osm.OsmPrimitive; 012 import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings; 013 import org.openstreetmap.josm.data.osm.visitor.paint.MapPainter; 014 import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction.RelativeFloat; 015 016 abstract public class ElemStyle implements StyleKeys { 017 018 public float major_z_index; 019 public float z_index; 020 public float object_z_index; 021 public boolean isModifier; // false, if style can serve as main style for the 022 // primitive; true, if it is a highlight or modifier 023 024 public ElemStyle(float major_z_index, float z_index, float object_z_index, boolean isModifier) { 025 this.major_z_index = major_z_index; 026 this.z_index = z_index; 027 this.object_z_index = object_z_index; 028 this.isModifier = isModifier; 029 } 030 031 protected ElemStyle(Cascade c, float default_major_z_index) { 032 major_z_index = c.get("major-z-index", default_major_z_index, Float.class); 033 z_index = c.get(Z_INDEX, 0f, Float.class); 034 object_z_index = c.get(OBJECT_Z_INDEX, 0f, Float.class); 035 isModifier = c.get(MODIFIER, false, Boolean.class); 036 } 037 038 /** 039 * draws a primitive 040 * @param primitive 041 * @param paintSettings 042 * @param painter 043 * @param selected true, if primitive is selected 044 * @param member true, if primitive is not selected and member of a selected relation 045 */ 046 public abstract void paintPrimitive(OsmPrimitive primitive, MapPaintSettings paintSettings, MapPainter painter, boolean selected, boolean member); 047 048 public boolean isProperLineStyle() { 049 return false; 050 } 051 052 /** 053 * Get a property value of type Width 054 * @param c the cascade 055 * @param key property key for the width value 056 * @param relativeTo reference width. Only needed, when relative width syntax 057 * is used, e.g. "+4". 058 */ 059 protected static Float getWidth(Cascade c, String key, Float relativeTo) { 060 Float width = c.get(key, null, Float.class, true); 061 if (width != null) { 062 if (width > 0) 063 return width; 064 } else { 065 Keyword widthKW = c.get(key, null, Keyword.class, true); 066 if (equal(widthKW, Keyword.THINNEST)) 067 return 0f; 068 if (equal(widthKW, Keyword.DEFAULT)) 069 return (float) MapPaintSettings.INSTANCE.getDefaultSegmentWidth(); 070 if (relativeTo != null) { 071 RelativeFloat width_rel = c.get(key, null, RelativeFloat.class, true); 072 if (width_rel != null) 073 return relativeTo + width_rel.val; 074 } 075 } 076 return null; 077 } 078 079 /* ------------------------------------------------------------------------------- */ 080 /* cached values */ 081 /* ------------------------------------------------------------------------------- */ 082 /* 083 * Two preference values and the set of created fonts are cached in order to avoid 084 * expensive lookups and to avoid too many font objects 085 * (in analogy to flyweight pattern). 086 * 087 * FIXME: cached preference values are not updated if the user changes them during 088 * a JOSM session. Should have a listener listening to preference changes. 089 */ 090 static private String DEFAULT_FONT_NAME = null; 091 static private Float DEFAULT_FONT_SIZE = null; 092 static private void initDefaultFontParameters() { 093 if (DEFAULT_FONT_NAME != null) return; // already initialized - skip initialization 094 DEFAULT_FONT_NAME = Main.pref.get("mappaint.font", "Helvetica"); 095 DEFAULT_FONT_SIZE = (float) Main.pref.getInteger("mappaint.fontsize", 8); 096 } 097 098 static private class FontDescriptor { 099 public String name; 100 public int style; 101 public int size; 102 103 public FontDescriptor(String name, int style, int size){ 104 this.name = name; 105 this.style = style; 106 this.size = size; 107 } 108 109 @Override 110 public int hashCode() { 111 final int prime = 31; 112 int result = 1; 113 result = prime * result + ((name == null) ? 0 : name.hashCode()); 114 result = prime * result + size; 115 result = prime * result + style; 116 return result; 117 } 118 @Override 119 public boolean equals(Object obj) { 120 if (this == obj) 121 return true; 122 if (obj == null) 123 return false; 124 if (getClass() != obj.getClass()) 125 return false; 126 FontDescriptor other = (FontDescriptor) obj; 127 if (name == null) { 128 if (other.name != null) 129 return false; 130 } else if (!name.equals(other.name)) 131 return false; 132 if (size != other.size) 133 return false; 134 if (style != other.style) 135 return false; 136 return true; 137 } 138 } 139 140 static private final Map<FontDescriptor, Font> FONT_MAP = new HashMap<FontDescriptor, Font>(); 141 static private Font getCachedFont(FontDescriptor fd) { 142 Font f = FONT_MAP.get(fd); 143 if (f != null) return f; 144 f = new Font(fd.name, fd.style, fd.size); 145 FONT_MAP.put(fd, f); 146 return f; 147 } 148 149 static private Font getCachedFont(String name, int style, int size){ 150 return getCachedFont(new FontDescriptor(name, style, size)); 151 } 152 153 protected static Font getFont(Cascade c) { 154 initDefaultFontParameters(); // populated cached preferences, if necesary 155 String name = c.get("font-family", DEFAULT_FONT_NAME, String.class); 156 float size = c.get("font-size", DEFAULT_FONT_SIZE, Float.class); 157 int weight = Font.PLAIN; 158 Keyword weightKW = c.get("font-weight", null, Keyword.class); 159 if (weightKW != null && equal(weightKW, "bold")) { 160 weight = Font.BOLD; 161 } 162 int style = Font.PLAIN; 163 Keyword styleKW = c.get("font-style", null, Keyword.class); 164 if (styleKW != null && equal(styleKW.val, "italic")) { 165 style = Font.ITALIC; 166 } 167 return getCachedFont(name, style | weight, Math.round(size)); 168 } 169 170 @Override 171 public boolean equals(Object o) { 172 if (!(o instanceof ElemStyle)) 173 return false; 174 ElemStyle s = (ElemStyle) o; 175 return major_z_index == s.major_z_index && 176 z_index == s.z_index && 177 object_z_index == s.object_z_index && 178 isModifier == s.isModifier; 179 } 180 181 @Override 182 public int hashCode() { 183 int hash = 5; 184 hash = 41 * hash + Float.floatToIntBits(this.major_z_index); 185 hash = 41 * hash + Float.floatToIntBits(this.z_index); 186 hash = 41 * hash + Float.floatToIntBits(this.object_z_index); 187 hash = 41 * hash + (isModifier ? 1 : 0); 188 return hash; 189 } 190 191 @Override 192 public String toString() { 193 return String.format("z_idx=[%s/%s/%s] ", major_z_index, z_index, object_z_index) + (isModifier ? "modifier " : ""); 194 } 195 }