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

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

fix #7386 - Major rework of preferences GUI settings in order to speed up preferences dialog startup time. The building of each preferences tab is delayed until this tab is selected. Plugins that use preferences will need to make some (minor) changes.

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