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

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

Rework console output:

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