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

Last change on this file since 5891 was 5891, checked in by stoecker, 11 years ago

fix javadoc

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