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

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

global replacement of e.printStackTrace() by Main.error(e)

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