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

Last change on this file since 6340 was 6289, checked in by Don-vip, 11 years ago

Sonar/Findbugs - fix various problems

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