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

Last change on this file since 3930 was 3930, checked in by bastiK, 13 years ago

detect style type (xml or mapcss) by looking at the first non-whitespace byte in the inputstream

  • Property svn:eol-style set to native
File size: 11.6 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.IOException;
7import java.io.InputStream;
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.gui.PleaseWaitRunnable;
21import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
22import org.openstreetmap.josm.gui.mappaint.xml.XmlStyleSource;
23import org.openstreetmap.josm.gui.preferences.SourceEntry;
24import org.openstreetmap.josm.gui.preferences.MapPaintPreference.MapPaintPrefMigration;
25import org.openstreetmap.josm.gui.progress.ProgressMonitor;
26import org.openstreetmap.josm.io.MirroredInputStream;
27import org.openstreetmap.josm.tools.ImageProvider;
28
29/**
30 * This class manages the ElemStyles instance. The object you get with
31 * getStyles() is read only, any manipulation happens via one of
32 * the wrapper methods here. (readFromPreferences, moveStyles, ...)
33 *
34 * On change, mapPaintSylesUpdated() is fired for all listeners.
35 */
36public class MapPaintStyles {
37
38 private static ElemStyles styles = new ElemStyles();
39
40 public static ElemStyles getStyles()
41 {
42 return styles;
43 }
44
45 public static class IconReference {
46
47 public String iconName;
48 public StyleSource source;
49
50 public IconReference(String iconName, StyleSource source) {
51 this.iconName = iconName;
52 this.source = source;
53 }
54
55 @Override
56 public String toString() {
57 return "IconReference{" + "iconName=" + iconName + " source=" + source.getDisplayString() + '}';
58 }
59 }
60
61 public static ImageIcon getIcon(IconReference ref, boolean sanitize)
62 {
63 String namespace = ref.source.getPrefName();
64 ImageIcon i = ImageProvider.getIfAvailable(getIconSourceDirs(ref.source), "mappaint."+namespace, null, ref.iconName, ref.source.zipIcons, sanitize);
65 if(i == null)
66 {
67 System.out.println("Mappaint style \""+namespace+"\" icon \"" + ref.iconName + "\" not found.");
68 return null;
69 }
70 return i;
71 }
72
73 /**
74 * No icon with the given name was found, show a dummy icon instead
75 * @return the icon misc/no_icon.png, in descending priority:
76 * - relative to source file
77 * - from user icon paths
78 * - josm's default icon
79 * can be null if the defaults are turned off by user
80 */
81 public static ImageIcon getNoIcon_Icon(StyleSource source, boolean sanitize) {
82 return ImageProvider.getIfAvailable(getIconSourceDirs(source), "mappaint."+source.getPrefName(), null, "misc/no_icon.png", source.zipIcons, sanitize);
83 }
84
85 private static List<String> getIconSourceDirs(StyleSource source) {
86 List<String> dirs = new LinkedList<String>();
87
88 String sourceDir = source.getLocalSourceDir();
89 if (sourceDir != null) {
90 dirs.add(sourceDir);
91 }
92
93 Collection<String> prefIconDirs = Main.pref.getCollection("mappaint.icon.sources");
94 for(String fileset : prefIconDirs)
95 {
96 String[] a;
97 if(fileset.indexOf("=") >= 0) {
98 a = fileset.split("=", 2);
99 } else {
100 a = new String[] {"", fileset};
101 }
102
103 /* non-prefixed path is generic path, always take it */
104 if(a[0].length() == 0 || source.getPrefName().equals(a[0])) {
105 dirs.add(a[1]);
106 }
107 }
108
109 if (Main.pref.getBoolean("mappaint.icon.enable-defaults", true)) {
110 /* don't prefix icon path, as it should be generic */
111 dirs.add("resource://images/styles/standard/");
112 dirs.add("resource://images/styles/");
113 }
114
115 return dirs;
116 }
117
118 public static void readFromPreferences() {
119 styles.clear();
120
121 Collection<? extends SourceEntry> sourceEntries = MapPaintPrefMigration.INSTANCE.get();
122
123 for (SourceEntry entry : sourceEntries) {
124 StyleSource source = fromSourceEntry(entry);
125 if (source != null) {
126 styles.add(source);
127 }
128 }
129 for (StyleSource source : styles.getStyleSources()) {
130 source.loadStyleSource();
131 }
132
133 fireMapPaintSylesUpdated();
134 }
135
136 private static StyleSource fromSourceEntry(SourceEntry entry) {
137 MirroredInputStream in = null;
138 try {
139 in = new MirroredInputStream(entry.url);
140 InputStream zip = in.getZipEntry("xml", "style");
141 if (zip != null) {
142 return new XmlStyleSource(entry);
143 }
144 zip = in.getZipEntry("mapcss", "style");
145 if (zip != null) {
146 return new MapCSSStyleSource(entry);
147 }
148 if (entry.url.toLowerCase().endsWith(".mapcss")) {
149 return new MapCSSStyleSource(entry);
150 }
151 if (entry.url.toLowerCase().endsWith(".xml")) {
152 return new XmlStyleSource(entry);
153 } else {
154 InputStreamReader reader = new InputStreamReader(in);
155 WHILE: while (true) {
156 int c = reader.read();
157 switch (c) {
158 case -1:
159 break WHILE;
160 case ' ':
161 case '\t':
162 case '\n':
163 case '\r':
164 continue;
165 case '<':
166 return new XmlStyleSource(entry);
167 default:
168 return new MapCSSStyleSource(entry);
169 }
170 }
171 System.err.println("Warning: Could not detect style type. Using default (xml).");
172 return new XmlStyleSource(entry);
173 }
174 } catch (IOException e) {
175 System.err.println(tr("Warning: failed to load Mappaint styles from ''{0}''. Exception was: {1}", entry.url, e.toString()));
176 e.printStackTrace();
177 } finally {
178 try {
179 if (in != null) {
180 in.close();
181 }
182 } catch (IOException ex) {
183 }
184 }
185 return null;
186 }
187
188 /**
189 * reload styles
190 * preferences are the same, but the file source may have changed
191 * @param sel the indices of styles to reload
192 */
193 public static void reloadStyles(final int... sel) {
194 List<StyleSource> toReload = new ArrayList<StyleSource>();
195 List<StyleSource> data = styles.getStyleSources();
196 for (int i : sel) {
197 toReload.add(data.get(i));
198 }
199 Main.worker.submit(new MapPaintStyleLoader(toReload));
200 }
201
202 public static class MapPaintStyleLoader extends PleaseWaitRunnable {
203 private boolean canceled;
204 private List<StyleSource> sources;
205
206 public MapPaintStyleLoader(List<StyleSource> sources) {
207 super(tr("Reloading style sources"));
208 this.sources = sources;
209 }
210
211 @Override
212 protected void cancel() {
213 canceled = true;
214 }
215
216 @Override
217 protected void finish() {
218 SwingUtilities.invokeLater(new Runnable() {
219 @Override
220 public void run() {
221 fireMapPaintSylesUpdated();
222 styles.clearCached();
223 Main.map.mapView.preferenceChanged(null);
224 Main.map.mapView.repaint();
225 }
226 });
227 }
228
229 @Override
230 protected void realRun() {
231 ProgressMonitor monitor = getProgressMonitor();
232 monitor.setTicksCount(sources.size());
233 for (StyleSource s : sources) {
234 if (canceled)
235 return;
236 monitor.subTask(tr("loading style ''{0}''...", s.getDisplayString()));
237 s.loadStyleSource();
238 monitor.worked(1);
239 }
240 }
241 }
242
243 /**
244 * Move position of entries in the current list of StyleSources
245 * @param sele The indices of styles to be moved.
246 * @param delta The number of lines it should move. positive int moves
247 * down and negative moves up.
248 */
249 public static void moveStyles(int[] sel, int delta) {
250 if (!canMoveStyles(sel, delta))
251 return;
252 int[] selSorted = Arrays.copyOf(sel, sel.length);
253 Arrays.sort(selSorted);
254 List<StyleSource> data = new ArrayList<StyleSource>(styles.getStyleSources());
255 for (int row: selSorted) {
256 StyleSource t1 = data.get(row);
257 StyleSource t2 = data.get(row + delta);
258 data.set(row, t2);
259 data.set(row + delta, t1);
260 }
261 styles.setStyleSources(data);
262 MapPaintPrefMigration.INSTANCE.put(data);
263 fireMapPaintSylesUpdated();
264 styles.clearCached();
265 Main.map.mapView.repaint();
266 }
267
268 public static boolean canMoveStyles(int[] sel, int i) {
269 if (sel.length == 0)
270 return false;
271 int[] selSorted = Arrays.copyOf(sel, sel.length);
272 Arrays.sort(selSorted);
273
274 if (i < 0) { // Up
275 return selSorted[0] >= -i;
276 } else
277 if (i > 0) { // Down
278 return selSorted[selSorted.length-1] <= styles.getStyleSources().size() - 1 - i;
279 } else
280 return true;
281 }
282
283 public static void toggleStyleActive(int... sel) {
284 List<StyleSource> data = styles.getStyleSources();
285 for (int p : sel) {
286 StyleSource s = data.get(p);
287 s.active = !s.active;
288 }
289 MapPaintPrefMigration.INSTANCE.put(data);
290 if (sel.length == 1) {
291 fireMapPaintStyleEntryUpdated(sel[0]);
292 } else {
293 fireMapPaintSylesUpdated();
294 }
295 styles.clearCached();
296 Main.map.mapView.repaint();
297 }
298
299 public static void addStyle(SourceEntry entry) {
300 StyleSource source = fromSourceEntry(entry);
301 if (source != null) {
302 styles.add(source);
303 source.loadStyleSource();
304 MapPaintPrefMigration.INSTANCE.put(styles.getStyleSources());
305 fireMapPaintSylesUpdated();
306 styles.clearCached();
307 Main.map.mapView.repaint();
308 }
309 }
310
311 /***********************************
312 * MapPaintSylesUpdateListener & related code
313 * (get informed when the list of MapPaint StyleSources changes)
314 */
315
316 public interface MapPaintSylesUpdateListener {
317 public void mapPaintStylesUpdated();
318 public void mapPaintStyleEntryUpdated(int idx);
319 }
320
321 protected static final CopyOnWriteArrayList<MapPaintSylesUpdateListener> listeners
322 = new CopyOnWriteArrayList<MapPaintSylesUpdateListener>();
323
324 public static void addMapPaintSylesUpdateListener(MapPaintSylesUpdateListener listener) {
325 if (listener != null) {
326 listeners.addIfAbsent(listener);
327 }
328 }
329
330 public static void removeMapPaintSylesUpdateListener(MapPaintSylesUpdateListener listener) {
331 listeners.remove(listener);
332 }
333
334 public static void fireMapPaintSylesUpdated() {
335 for (MapPaintSylesUpdateListener l : listeners) {
336 l.mapPaintStylesUpdated();
337 }
338 }
339
340 public static void fireMapPaintStyleEntryUpdated(int idx) {
341 for (MapPaintSylesUpdateListener l : listeners) {
342 l.mapPaintStyleEntryUpdated(idx);
343 }
344 }
345}
Note: See TracBrowser for help on using the repository browser.