source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/styleelement/BoxTextElement.java@ 12378

Last change on this file since 12378 was 12332, checked in by Don-vip, 7 years ago

sonar - squid:RightCurlyBraceStartLineCheck - A close curly brace should be located at the beginning of a line

  • Property svn:eol-style set to native
File size: 9.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint.styleelement;
3
4import java.awt.Color;
5import java.awt.Rectangle;
6import java.util.Objects;
7
8import org.openstreetmap.josm.data.osm.Node;
9import org.openstreetmap.josm.data.osm.OsmPrimitive;
10import org.openstreetmap.josm.data.osm.visitor.paint.MapPaintSettings;
11import org.openstreetmap.josm.data.osm.visitor.paint.PaintColors;
12import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
13import org.openstreetmap.josm.gui.mappaint.Cascade;
14import org.openstreetmap.josm.gui.mappaint.Environment;
15import org.openstreetmap.josm.gui.mappaint.Keyword;
16import org.openstreetmap.josm.gui.mappaint.MultiCascade;
17import org.openstreetmap.josm.tools.CheckParameterUtil;
18
19/**
20 * Text style attached to a style with a bounding box, like an icon or a symbol.
21 */
22public class BoxTextElement extends StyleElement {
23
24 /**
25 * MapCSS text-anchor-horizontal
26 */
27 public enum HorizontalTextAlignment {
28 /**
29 * Align to the left
30 */
31 LEFT,
32 /**
33 * Align in the center
34 */
35 CENTER,
36 /**
37 * Align to the right
38 */
39 RIGHT
40 }
41
42 /**
43 * MapCSS text-anchor-vertical
44 */
45 public enum VerticalTextAlignment {
46 /**
47 * Render above the box
48 */
49 ABOVE,
50 /**
51 * Align to the top of the box
52 */
53 TOP,
54 /**
55 * Render at the center of the box
56 */
57 CENTER,
58 /**
59 * Align to the bottom of the box
60 */
61 BOTTOM,
62 /**
63 * Render below the box
64 */
65 BELOW
66 }
67
68 /**
69 * Something that provides us with a {@link BoxProviderResult}
70 * @since 10600 (functional interface)
71 */
72 @FunctionalInterface
73 public interface BoxProvider {
74 /**
75 * Compute and get the {@link BoxProviderResult}. The temporary flag is set if the result of the computation may change in the future.
76 * @return The result of the computation.
77 */
78 BoxProviderResult get();
79 }
80
81 /**
82 * A box rectangle with a flag if it is temporary.
83 */
84 public static class BoxProviderResult {
85 private final Rectangle box;
86 private final boolean temporary;
87
88 /**
89 * Create a new box provider result
90 * @param box The box
91 * @param temporary The temporary flag, will be returned by {@link #isTemporary()}
92 */
93 public BoxProviderResult(Rectangle box, boolean temporary) {
94 this.box = box;
95 this.temporary = temporary;
96 }
97
98 /**
99 * Returns the box.
100 * @return the box
101 */
102 public Rectangle getBox() {
103 return box;
104 }
105
106 /**
107 * Determines if the box can change in future calls of the {@link BoxProvider#get()} method
108 * @return {@code true} if the box can change in future calls of the {@code BoxProvider#get()} method
109 */
110 public boolean isTemporary() {
111 return temporary;
112 }
113 }
114
115 /**
116 * A {@link BoxProvider} that always returns the same non-temporary rectangle
117 */
118 public static class SimpleBoxProvider implements BoxProvider {
119 private final Rectangle box;
120
121 /**
122 * Constructs a new {@code SimpleBoxProvider}.
123 * @param box the box
124 */
125 public SimpleBoxProvider(Rectangle box) {
126 this.box = box;
127 }
128
129 @Override
130 public BoxProviderResult get() {
131 return new BoxProviderResult(box, false);
132 }
133
134 @Override
135 public int hashCode() {
136 return Objects.hash(box);
137 }
138
139 @Override
140 public boolean equals(Object obj) {
141 if (this == obj) return true;
142 if (obj == null || getClass() != obj.getClass()) return false;
143 SimpleBoxProvider that = (SimpleBoxProvider) obj;
144 return Objects.equals(box, that.box);
145 }
146 }
147
148 /**
149 * The default style a simple node should use for it's text
150 */
151 public static final BoxTextElement SIMPLE_NODE_TEXT_ELEMSTYLE;
152 static {
153 MultiCascade mc = new MultiCascade();
154 Cascade c = mc.getOrCreateCascade("default");
155 c.put(TEXT, Keyword.AUTO);
156 Node n = new Node();
157 n.put("name", "dummy");
158 SIMPLE_NODE_TEXT_ELEMSTYLE = create(new Environment(n, mc, "default", null), NodeElement.SIMPLE_NODE_ELEMSTYLE.getBoxProvider());
159 if (SIMPLE_NODE_TEXT_ELEMSTYLE == null) throw new AssertionError();
160 }
161
162 /**
163 * Caches the default text color from the preferences.
164 *
165 * FIXME: the cache isn't updated if the user changes the preference during a JOSM
166 * session. There should be preference listener updating this cache.
167 */
168 private static volatile Color defaultTextColorCache;
169
170 /**
171 * The text this element should display.
172 */
173 public TextLabel text;
174 /**
175 * The {@link HorizontalTextAlignment} for this text.
176 */
177 public HorizontalTextAlignment hAlign;
178 /**
179 * The {@link VerticalTextAlignment} for this text.
180 */
181 public VerticalTextAlignment vAlign;
182 protected BoxProvider boxProvider;
183
184 /**
185 * Create a new {@link BoxTextElement}
186 * @param c The current cascade
187 * @param text The text to display
188 * @param boxProvider The box provider to use
189 * @param hAlign The {@link HorizontalTextAlignment}
190 * @param vAlign The {@link VerticalTextAlignment}
191 */
192 public BoxTextElement(Cascade c, TextLabel text, BoxProvider boxProvider,
193 HorizontalTextAlignment hAlign, VerticalTextAlignment vAlign) {
194 super(c, 5f);
195 CheckParameterUtil.ensureParameterNotNull(text);
196 CheckParameterUtil.ensureParameterNotNull(hAlign);
197 CheckParameterUtil.ensureParameterNotNull(vAlign);
198 this.text = text;
199 this.boxProvider = boxProvider;
200 this.hAlign = hAlign;
201 this.vAlign = vAlign;
202 }
203
204 /**
205 * Create a new {@link BoxTextElement} with a boxprovider and a box.
206 * @param env The MapCSS environment
207 * @param boxProvider The box provider.
208 * @return A new {@link BoxTextElement} or <code>null</code> if the creation failed.
209 */
210 public static BoxTextElement create(Environment env, BoxProvider boxProvider) {
211 initDefaultParameters();
212
213 TextLabel text = TextLabel.create(env, defaultTextColorCache, false);
214 if (text == null) return null;
215 // Skip any primitives that don't have text to draw. (Styles are recreated for any tag change.)
216 // The concrete text to render is not cached in this object, but computed for each
217 // repaint. This way, one BoxTextElement object can be used by multiple primitives (to save memory).
218 if (text.labelCompositionStrategy.compose(env.osm) == null) return null;
219
220 Cascade c = env.mc.getCascade(env.layer);
221
222 HorizontalTextAlignment hAlign;
223 switch (c.get(TEXT_ANCHOR_HORIZONTAL, Keyword.RIGHT, Keyword.class).val) {
224 case "left":
225 hAlign = HorizontalTextAlignment.LEFT;
226 break;
227 case "center":
228 hAlign = HorizontalTextAlignment.CENTER;
229 break;
230 case "right":
231 default:
232 hAlign = HorizontalTextAlignment.RIGHT;
233 }
234 VerticalTextAlignment vAlign;
235 switch (c.get(TEXT_ANCHOR_VERTICAL, Keyword.BOTTOM, Keyword.class).val) {
236 case "above":
237 vAlign = VerticalTextAlignment.ABOVE;
238 break;
239 case "top":
240 vAlign = VerticalTextAlignment.TOP;
241 break;
242 case "center":
243 vAlign = VerticalTextAlignment.CENTER;
244 break;
245 case "below":
246 vAlign = VerticalTextAlignment.BELOW;
247 break;
248 case "bottom":
249 default:
250 vAlign = VerticalTextAlignment.BOTTOM;
251 }
252
253 return new BoxTextElement(c, text, boxProvider, hAlign, vAlign);
254 }
255
256 /**
257 * Get the box in which the content should be drawn.
258 * @return The box.
259 */
260 public Rectangle getBox() {
261 return boxProvider.get().getBox();
262 }
263
264 private static void initDefaultParameters() {
265 if (defaultTextColorCache != null) return;
266 defaultTextColorCache = PaintColors.TEXT.get();
267 }
268
269 @Override
270 public void paintPrimitive(OsmPrimitive osm, MapPaintSettings settings, StyledMapRenderer painter,
271 boolean selected, boolean outermember, boolean member) {
272 if (osm instanceof Node) {
273 painter.drawBoxText((Node) osm, this);
274 }
275 }
276
277 @Override
278 public boolean equals(Object obj) {
279 if (this == obj) return true;
280 if (obj == null || getClass() != obj.getClass()) return false;
281 if (!super.equals(obj)) return false;
282 BoxTextElement that = (BoxTextElement) obj;
283 return hAlign == that.hAlign &&
284 vAlign == that.vAlign &&
285 Objects.equals(text, that.text) &&
286 Objects.equals(boxProvider, that.boxProvider);
287 }
288
289 @Override
290 public int hashCode() {
291 return Objects.hash(super.hashCode(), text, boxProvider, hAlign, vAlign);
292 }
293
294 @Override
295 public String toString() {
296 return "BoxTextElement{" + super.toString() + ' ' + text.toStringImpl()
297 + " box=" + getBox() + " hAlign=" + hAlign + " vAlign=" + vAlign + '}';
298 }
299}
Note: See TracBrowser for help on using the repository browser.