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

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

see #8849 - use scaled down version of images in recently added tags panel

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