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

Last change on this file since 10375 was 10375, checked in by Don-vip, 8 years ago

fix #12959 - Move state changes to MapViewState class (patch by michael2402) - gscore-core + checkstyle fixes

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