source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/MapPaintStyles.java@ 6830

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

javadoc fixes for jdk8 compatibility

  • Property svn:eol-style set to native
File size: 14.6 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.io.File;
7import java.io.IOException;
8import java.io.InputStreamReader;
9import java.util.ArrayList;
10import java.util.Arrays;
11import java.util.Collection;
12import java.util.LinkedList;
13import java.util.List;
14import java.util.concurrent.CopyOnWriteArrayList;
15
16import javax.swing.ImageIcon;
17import javax.swing.SwingUtilities;
18
19import org.openstreetmap.josm.Main;
20import org.openstreetmap.josm.data.osm.Node;
21import org.openstreetmap.josm.data.osm.Tag;
22import org.openstreetmap.josm.gui.PleaseWaitRunnable;
23import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
24import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
25import org.openstreetmap.josm.gui.mappaint.xml.XmlStyleSource;
26import org.openstreetmap.josm.gui.preferences.SourceEntry;
27import org.openstreetmap.josm.gui.preferences.map.MapPaintPreference.MapPaintPrefHelper;
28import org.openstreetmap.josm.gui.progress.ProgressMonitor;
29import org.openstreetmap.josm.io.MirroredInputStream;
30import org.openstreetmap.josm.tools.ImageProvider;
31import org.openstreetmap.josm.tools.Utils;
32
33/**
34 * This class manages the ElemStyles instance. The object you get with
35 * getStyles() is read only, any manipulation happens via one of
36 * the wrapper methods here. (readFromPreferences, moveStyles, ...)
37 *
38 * On change, mapPaintSylesUpdated() is fired for all listeners.
39 */
40public final class MapPaintStyles {
41
42 private static ElemStyles styles = new ElemStyles();
43
44 /**
45 * Returns the {@link ElemStyles} instance.
46 * @return the {@code ElemStyles} instance
47 */
48 public static ElemStyles getStyles() {
49 return styles;
50 }
51
52 private MapPaintStyles() {
53 // Hide default constructor for utils classes
54 }
55
56 /**
57 * Value holder for a reference to a tag name. A style instruction
58 * <pre>
59 * text: a_tag_name;
60 * </pre>
61 * results in a tag reference for the tag <tt>a_tag_name</tt> in the
62 * style cascade.
63 */
64 public static class TagKeyReference {
65 public final String key;
66 public TagKeyReference(String key) {
67 this.key = key;
68 }
69
70 @Override
71 public String toString() {
72 return "TagKeyReference{" + "key='" + key + "'}";
73 }
74 }
75
76 /**
77 * IconReference is used to remember the associated style source for
78 * each icon URL.
79 * This is necessary because image URLs can be paths relative
80 * to the source file and we have cascading of properties from different
81 * source files.
82 */
83 public static class IconReference {
84
85 public final String iconName;
86 public final StyleSource source;
87
88 public IconReference(String iconName, StyleSource source) {
89 this.iconName = iconName;
90 this.source = source;
91 }
92
93 @Override
94 public String toString() {
95 return "IconReference{" + "iconName='" + iconName + "' source='" + source.getDisplayString() + "'}";
96 }
97 }
98
99 public static ImageIcon getIcon(IconReference ref, int width, int height) {
100 final String namespace = ref.source.getPrefName();
101 ImageIcon i = new ImageProvider(ref.iconName)
102 .setDirs(getIconSourceDirs(ref.source))
103 .setId("mappaint."+namespace)
104 .setArchive(ref.source.zipIcons)
105 .setInArchiveDir(ref.source.getZipEntryDirName())
106 .setWidth(width)
107 .setHeight(height)
108 .setOptional(true).get();
109 if (i == null) {
110 Main.warn("Mappaint style \""+namespace+"\" ("+ref.source.getDisplayString()+") icon \"" + ref.iconName + "\" not found.");
111 return null;
112 }
113 return i;
114 }
115
116 /**
117 * No icon with the given name was found, show a dummy icon instead
118 * @return the icon misc/no_icon.png, in descending priority:
119 * - relative to source file
120 * - from user icon paths
121 * - josm's default icon
122 * can be null if the defaults are turned off by user
123 */
124 public static ImageIcon getNoIcon_Icon(StyleSource source) {
125 return new ImageProvider("misc/no_icon.png")
126 .setDirs(getIconSourceDirs(source))
127 .setId("mappaint."+source.getPrefName())
128 .setArchive(source.zipIcons)
129 .setInArchiveDir(source.getZipEntryDirName())
130 .setOptional(true).get();
131 }
132
133 public static ImageIcon getNodeIcon(Tag tag) {
134 return getNodeIcon(tag, true);
135 }
136
137 public static ImageIcon getNodeIcon(Tag tag, boolean includeDeprecatedIcon) {
138 if (tag != null) {
139 Node virtualNode = new Node();
140 virtualNode.put(tag.getKey(), tag.getValue());
141 StyleList styleList = getStyles().generateStyles(virtualNode, 0.5, null, false).a;
142 if (styleList != null) {
143 for (ElemStyle style : styleList) {
144 if (style instanceof NodeElemStyle) {
145 MapImage mapImage = ((NodeElemStyle) style).mapImage;
146 if (mapImage != null) {
147 if (includeDeprecatedIcon || mapImage.name == null || !mapImage.name.equals("misc/deprecated.png")) {
148 return new ImageIcon(mapImage.getDisplayedNodeIcon(false));
149 } else {
150 return null; // Deprecated icon found but not wanted
151 }
152 }
153 }
154 }
155 }
156 }
157 return null;
158 }
159
160 public static List<String> getIconSourceDirs(StyleSource source) {
161 List<String> dirs = new LinkedList<String>();
162
163 String sourceDir = source.getLocalSourceDir();
164 if (sourceDir != null) {
165 dirs.add(sourceDir);
166 }
167
168 Collection<String> prefIconDirs = Main.pref.getCollection("mappaint.icon.sources");
169 for(String fileset : prefIconDirs)
170 {
171 String[] a;
172 if(fileset.indexOf('=') >= 0) {
173 a = fileset.split("=", 2);
174 } else {
175 a = new String[] {"", fileset};
176 }
177
178 /* non-prefixed path is generic path, always take it */
179 if(a[0].length() == 0 || source.getPrefName().equals(a[0])) {
180 dirs.add(a[1]);
181 }
182 }
183
184 if (Main.pref.getBoolean("mappaint.icon.enable-defaults", true)) {
185 /* don't prefix icon path, as it should be generic */
186 dirs.add("resource://images/styles/standard/");
187 dirs.add("resource://images/styles/");
188 }
189
190 return dirs;
191 }
192
193 public static void readFromPreferences() {
194 styles.clear();
195
196 Collection<? extends SourceEntry> sourceEntries = MapPaintPrefHelper.INSTANCE.get();
197
198 for (SourceEntry entry : sourceEntries) {
199 StyleSource source = fromSourceEntry(entry);
200 if (source != null) {
201 styles.add(source);
202 }
203 }
204 for (StyleSource source : styles.getStyleSources()) {
205 source.loadStyleSource();
206 if (Main.pref.getBoolean("mappaint.auto_reload_local_styles", true)) {
207 if (source.isLocal()) {
208 File f = new File(source.url);
209 source.setLastMTime(f.lastModified());
210 }
211 }
212 }
213 fireMapPaintSylesUpdated();
214 }
215
216 private static StyleSource fromSourceEntry(SourceEntry entry) {
217 MirroredInputStream in = null;
218 try {
219 in = new MirroredInputStream(entry.url);
220 String zipEntryPath = in.findZipEntryPath("mapcss", "style");
221 if (zipEntryPath != null) {
222 entry.isZip = true;
223 entry.zipEntryPath = zipEntryPath;
224 return new MapCSSStyleSource(entry);
225 }
226 zipEntryPath = in.findZipEntryPath("xml", "style");
227 if (zipEntryPath != null)
228 return new XmlStyleSource(entry);
229 if (entry.url.toLowerCase().endsWith(".mapcss"))
230 return new MapCSSStyleSource(entry);
231 if (entry.url.toLowerCase().endsWith(".xml"))
232 return new XmlStyleSource(entry);
233 else {
234 InputStreamReader reader = new InputStreamReader(in);
235 try {
236 WHILE: while (true) {
237 int c = reader.read();
238 switch (c) {
239 case -1:
240 break WHILE;
241 case ' ':
242 case '\t':
243 case '\n':
244 case '\r':
245 continue;
246 case '<':
247 return new XmlStyleSource(entry);
248 default:
249 return new MapCSSStyleSource(entry);
250 }
251 }
252 } finally {
253 reader.close();
254 }
255 Main.warn("Could not detect style type. Using default (xml).");
256 return new XmlStyleSource(entry);
257 }
258 } catch (IOException e) {
259 Main.warn(tr("Failed to load Mappaint styles from ''{0}''. Exception was: {1}", entry.url, e.toString()));
260 Main.error(e);
261 } finally {
262 Utils.close(in);
263 }
264 return null;
265 }
266
267 /**
268 * reload styles
269 * preferences are the same, but the file source may have changed
270 * @param sel the indices of styles to reload
271 */
272 public static void reloadStyles(final int... sel) {
273 List<StyleSource> toReload = new ArrayList<StyleSource>();
274 List<StyleSource> data = styles.getStyleSources();
275 for (int i : sel) {
276 toReload.add(data.get(i));
277 }
278 Main.worker.submit(new MapPaintStyleLoader(toReload));
279 }
280
281 public static class MapPaintStyleLoader extends PleaseWaitRunnable {
282 private boolean canceled;
283 private List<StyleSource> sources;
284
285 public MapPaintStyleLoader(List<StyleSource> sources) {
286 super(tr("Reloading style sources"));
287 this.sources = sources;
288 }
289
290 @Override
291 protected void cancel() {
292 canceled = true;
293 }
294
295 @Override
296 protected void finish() {
297 SwingUtilities.invokeLater(new Runnable() {
298 @Override
299 public void run() {
300 fireMapPaintSylesUpdated();
301 styles.clearCached();
302 Main.map.mapView.preferenceChanged(null);
303 Main.map.mapView.repaint();
304 }
305 });
306 }
307
308 @Override
309 protected void realRun() {
310 ProgressMonitor monitor = getProgressMonitor();
311 monitor.setTicksCount(sources.size());
312 for (StyleSource s : sources) {
313 if (canceled)
314 return;
315 monitor.subTask(tr("loading style ''{0}''...", s.getDisplayString()));
316 s.loadStyleSource();
317 monitor.worked(1);
318 }
319 }
320 }
321
322 /**
323 * Move position of entries in the current list of StyleSources
324 * @param sel The indices of styles to be moved.
325 * @param delta The number of lines it should move. positive int moves
326 * down and negative moves up.
327 */
328 public static void moveStyles(int[] sel, int delta) {
329 if (!canMoveStyles(sel, delta))
330 return;
331 int[] selSorted = Arrays.copyOf(sel, sel.length);
332 Arrays.sort(selSorted);
333 List<StyleSource> data = new ArrayList<StyleSource>(styles.getStyleSources());
334 for (int row: selSorted) {
335 StyleSource t1 = data.get(row);
336 StyleSource t2 = data.get(row + delta);
337 data.set(row, t2);
338 data.set(row + delta, t1);
339 }
340 styles.setStyleSources(data);
341 MapPaintPrefHelper.INSTANCE.put(data);
342 fireMapPaintSylesUpdated();
343 styles.clearCached();
344 Main.map.mapView.repaint();
345 }
346
347 public static boolean canMoveStyles(int[] sel, int i) {
348 if (sel.length == 0)
349 return false;
350 int[] selSorted = Arrays.copyOf(sel, sel.length);
351 Arrays.sort(selSorted);
352
353 if (i < 0) // Up
354 return selSorted[0] >= -i;
355 else if (i > 0) // Down
356 return selSorted[selSorted.length-1] <= styles.getStyleSources().size() - 1 - i;
357 else
358 return true;
359 }
360
361 public static void toggleStyleActive(int... sel) {
362 List<StyleSource> data = styles.getStyleSources();
363 for (int p : sel) {
364 StyleSource s = data.get(p);
365 s.active = !s.active;
366 }
367 MapPaintPrefHelper.INSTANCE.put(data);
368 if (sel.length == 1) {
369 fireMapPaintStyleEntryUpdated(sel[0]);
370 } else {
371 fireMapPaintSylesUpdated();
372 }
373 styles.clearCached();
374 Main.map.mapView.repaint();
375 }
376
377 public static void addStyle(SourceEntry entry) {
378 StyleSource source = fromSourceEntry(entry);
379 if (source != null) {
380 styles.add(source);
381 source.loadStyleSource();
382 MapPaintPrefHelper.INSTANCE.put(styles.getStyleSources());
383 fireMapPaintSylesUpdated();
384 styles.clearCached();
385 Main.map.mapView.repaint();
386 }
387 }
388
389 /***********************************
390 * MapPaintSylesUpdateListener &amp; related code
391 * (get informed when the list of MapPaint StyleSources changes)
392 */
393
394 public interface MapPaintSylesUpdateListener {
395 public void mapPaintStylesUpdated();
396 public void mapPaintStyleEntryUpdated(int idx);
397 }
398
399 protected static final CopyOnWriteArrayList<MapPaintSylesUpdateListener> listeners
400 = new CopyOnWriteArrayList<MapPaintSylesUpdateListener>();
401
402 public static void addMapPaintSylesUpdateListener(MapPaintSylesUpdateListener listener) {
403 if (listener != null) {
404 listeners.addIfAbsent(listener);
405 }
406 }
407
408 public static void removeMapPaintSylesUpdateListener(MapPaintSylesUpdateListener listener) {
409 listeners.remove(listener);
410 }
411
412 public static void fireMapPaintSylesUpdated() {
413 for (MapPaintSylesUpdateListener l : listeners) {
414 l.mapPaintStylesUpdated();
415 }
416 }
417
418 public static void fireMapPaintStyleEntryUpdated(int idx) {
419 for (MapPaintSylesUpdateListener l : listeners) {
420 l.mapPaintStyleEntryUpdated(idx);
421 }
422 }
423}
Note: See TracBrowser for help on using the repository browser.