Contents of /src/style.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (hide annotations)
Tue Dec 9 20:06:06 2008 UTC (15 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 13921 byte(s)
Initial import
1 harbaum 1 /*
2     * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3     *
4     * This file is part of OSM2Go.
5     *
6     * OSM2Go is free software: you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation, either version 3 of the License, or
9     * (at your option) any later version.
10     *
11     * OSM2Go is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18     */
19    
20     #include "appdata.h"
21    
22     #include <libxml/parser.h>
23     #include <libxml/tree.h>
24    
25     #if !defined(LIBXML_TREE_ENABLED) || !defined(LIBXML_OUTPUT_ENABLED)
26     #error "libxml doesn't support required tree or output"
27     #endif
28    
29     static void xml_get_prop_float(xmlNode *node, char *prop, float *value) {
30     char *str = (char*)xmlGetProp(node, BAD_CAST prop);
31     if(str) {
32     *value = strtod(str, NULL);
33     xmlFree(str);
34     }
35     }
36    
37     static int xml_get_prop_opaque(xmlNode *node, char *prop) {
38     float opaque = 100;
39     xml_get_prop_float(node, prop, &opaque);
40     if(opaque > 255) opaque = 255;
41     return(0xff & (int)(0.5 + opaque * 2.55));
42     }
43    
44     static gboolean xml_prop_is(xmlNode *node, char *prop, char *str) {
45     char *prop_str = (char*)xmlGetProp(node, BAD_CAST prop);
46     if(!prop_str) return FALSE;
47    
48     gboolean match = (strcasecmp(prop_str, str) == 0);
49     xmlFree(prop_str);
50     return match;
51     }
52    
53     static style_t *parse_style(xmlDocPtr doc, xmlNode *a_node) {
54     xmlNode *cur_node = NULL, *sub_node = NULL;
55     style_t *style = g_new0(style_t, 1);
56    
57     style->name = (char*)xmlGetProp(a_node, BAD_CAST "name");
58    
59     /* -------------- setup defaults -------------------- */
60     /* (the defaults are pretty much the potlatch style) */
61     style->background.color = 0xffffff; // white
62    
63     style->area.border_width = 2.0;
64     style->area.opaque = 0x60; // 37.5%
65     style->area.zoom_max = 0.1111; // zoom factor above which an area is visible & selectable
66    
67     style->node.radius = 4.0;
68     style->node.border_radius = 2.0;
69     style->node.color = 0x000000; // black
70     style->node.has_fill_color = TRUE; // is filled ...
71     style->node.fill_color = 0x008800; // ... in dark green
72     style->node.show_untagged = FALSE;
73     style->node.zoom_max = 0.4444; // zoom factor above which a node is visible & selectable
74    
75     style->track.width = 6.0;
76     style->track.color = 0x0000ff40; // blue
77     style->track.gps_color = 0x000080;
78    
79     style->way.width = 3.0;
80     style->way.color = 0x606060; // grey
81     style->way.zoom_max = 0.2222; // zoom above which it's visible & selectable
82    
83     style->highlight.width = 3.0;
84     style->highlight.color = 0xffff0080;
85     style->highlight.node_color = 0xff000080;
86     style->highlight.touch_color = 0x0000ff80;
87     style->highlight.arrow_color = 0x0000ff80;
88     style->highlight.arrow_limit = 4.0;
89    
90     style->frisket.mult = 3.0;
91     style->frisket.opaque = 0xff;
92     style->frisket.border.present = TRUE;
93     style->frisket.border.width = 6.0;
94     style->frisket.border.color = 0x00000099;
95    
96     style->icon.enable = FALSE;
97     style->icon.scale = 1.0; // icon size (multiplier)
98     style->icon.path_prefix = g_strdup("standard");
99    
100     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
101     if (cur_node->type == XML_ELEMENT_NODE) {
102     if(strcasecmp((char*)cur_node->name, "elemstyles") == 0) {
103     style->elemstyles_filename =
104     (char*)xmlGetProp(cur_node, BAD_CAST "filename");
105    
106     /* ---------- node ------------------------------------- */
107     } else if(strcasecmp((char*)cur_node->name, "node") == 0) {
108     parse_color(cur_node, "color", &style->node.color);
109     style->node.has_fill_color =
110     parse_color(cur_node, "fill-color", &style->node.fill_color);
111     xml_get_prop_float(cur_node, "radius", &style->node.radius);
112     xml_get_prop_float(cur_node, "border-radius",
113     &style->node.border_radius);
114     float scale_max = 0;
115     xml_get_prop_float(cur_node, "scale-max", &scale_max);
116     if (scale_max > 0)
117     style->node.zoom_max = scaledn_to_zoom(scale_max);
118     else
119     style->node.zoom_max = 0;
120    
121     style->node.show_untagged =
122     xml_prop_is(cur_node, "show-untagged", "true");
123    
124     /* ---------- icon ------------------------------------- */
125     } else if(strcasecmp((char*)cur_node->name, "icon") == 0) {
126     xml_get_prop_float(cur_node, "scale", &style->icon.scale);
127     style->icon.path_prefix = (char*)xmlGetProp(cur_node, BAD_CAST "path-prefix");
128     style->icon.enable = xml_prop_is(cur_node, "enable", "true");
129    
130     /* ---------- way ------------------------------------- */
131     } else if(strcasecmp((char*)cur_node->name, "way") == 0) {
132     parse_color(cur_node, "color", &style->way.color);
133     xml_get_prop_float(cur_node, "width", &style->way.width);
134     float scale_max = 0;
135     xml_get_prop_float(cur_node, "scale-max", &scale_max);
136     if (scale_max > 0)
137     style->way.zoom_max = scaledn_to_zoom(scale_max);
138     else
139     style->way.zoom_max = 0;
140    
141     /* ---------- frisket --------------------------------- */
142     } else if(strcasecmp((char*)cur_node->name, "frisket") == 0) {
143     xml_get_prop_float(cur_node, "mult", &style->frisket.mult);
144     style->frisket.opaque = xml_get_prop_opaque(cur_node, "opaque");
145     style->frisket.border.present = FALSE;
146    
147     for(sub_node = cur_node->children; sub_node; sub_node=sub_node->next) {
148     if(sub_node->type == XML_ELEMENT_NODE) {
149     if(strcasecmp((char*)sub_node->name, "border") == 0) {
150     style->frisket.border.present = TRUE;
151     xml_get_prop_float(sub_node, "width",
152     &style->frisket.border.width);
153    
154     gboolean color_set =
155     parse_color(sub_node, "color", &style->frisket.border.color);
156     int opaque = xml_get_prop_opaque(sub_node, "opaque");
157     if(color_set)
158     style->frisket.border.color =
159     (style->frisket.border.color<<8) | opaque;
160     }
161     }
162     }
163    
164     /* ---------- highlight ------------------------------- */
165     } else if(strcasecmp((char*)cur_node->name, "highlight") == 0) {
166     gboolean color_set =
167     parse_color(cur_node, "color", &style->highlight.color);
168     gboolean node_color_set =
169     parse_color(cur_node, "node-color", &style->highlight.node_color);
170     gboolean touch_color_set =
171     parse_color(cur_node, "touch-color", &style->highlight.touch_color);
172     gboolean arrow_color_set =
173     parse_color(cur_node, "arrow-color", &style->highlight.arrow_color);
174     xml_get_prop_float(cur_node, "width", &style->highlight.width);
175     xml_get_prop_float(cur_node, "arrow-limit",
176     &style->highlight.arrow_limit);
177    
178     int op = xml_get_prop_opaque(cur_node, "opaque");
179     if(color_set)
180     style->highlight.color = (style->highlight.color << 8) | op;
181     if(node_color_set)
182     style->highlight.node_color =(style->highlight.node_color << 8)|op;
183     if(touch_color_set)
184     style->highlight.touch_color =(style->highlight.touch_color << 8)|op;
185     if(arrow_color_set)
186     style->highlight.arrow_color =(style->highlight.arrow_color << 8)|op;
187    
188     /* ---------- track ------------------------------------ */
189     } else if(strcasecmp((char*)cur_node->name, "track") == 0) {
190     gboolean color_set =
191     parse_color(cur_node, "color", &style->track.color);
192     parse_color(cur_node, "gps-color", &style->track.gps_color);
193     xml_get_prop_float(cur_node, "width", &style->track.width);
194    
195     int opaque = xml_get_prop_opaque(cur_node, "opaque");
196     if(color_set) style->track.color = (style->track.color<<8) | opaque;
197    
198     /* ---------- area ------------------------------------- */
199     } else if(strcasecmp((char*)cur_node->name, "area") == 0) {
200     style->area.has_border_color =
201     parse_color(cur_node, "border-color", &style->area.border_color);
202     xml_get_prop_float(cur_node,"border-width", &style->area.border_width);
203     float scale_max = 0;
204     xml_get_prop_float(cur_node, "scale-max", &scale_max);
205     if (scale_max > 0)
206     style->area.zoom_max = scaledn_to_zoom(scale_max);
207     else
208     style->area.zoom_max = 0;
209    
210     style->area.opaque = xml_get_prop_opaque(cur_node, "opaque");
211    
212     /* ---------- background ------------------------------- */
213     } else if(strcasecmp((char*)cur_node->name, "background") == 0) {
214     parse_color(cur_node, "color", &style->background.color);
215    
216     } else
217     printf(" found unhandled style/%s\n", cur_node->name);
218     }
219     }
220     return style;
221     }
222    
223     static style_t *parse_doc(xmlDocPtr doc) {
224     /* Get the root element node */
225     xmlNode *cur_node = NULL;
226     style_t *style = NULL;
227    
228     for(cur_node = xmlDocGetRootElement(doc);
229     cur_node; cur_node = cur_node->next) {
230     if (cur_node->type == XML_ELEMENT_NODE) {
231     if(strcasecmp((char*)cur_node->name, "style") == 0) {
232     if(!style)
233     style = parse_style(doc, cur_node);
234     } else
235     printf(" found unhandled %s\n", cur_node->name);
236     }
237     }
238    
239     xmlFreeDoc(doc);
240     xmlCleanupParser();
241     return style;
242     }
243    
244     static style_t *style_parse(appdata_t *appdata, char *fullname) {
245     style_t *style = NULL;
246    
247     xmlDoc *doc = NULL;
248    
249     LIBXML_TEST_VERSION;
250    
251     /* parse the file and get the DOM */
252     if((doc = xmlReadFile(fullname, NULL, 0)) == NULL) {
253     xmlErrorPtr errP = xmlGetLastError();
254     errorf(GTK_WIDGET(appdata->window),
255     _("Style parsing failed:\n\n"
256     "XML error while parsing style file\n"
257     "%s"), errP->message);
258    
259     return NULL;
260     } else {
261     style = parse_doc(doc);
262     style->iconP = &appdata->icon;
263     }
264    
265     return style;
266     }
267    
268     style_t *style_load(appdata_t *appdata, char *name) {
269     printf("Trying to load style %s\n", name);
270    
271     char *filename = g_strdup_printf("%s.style", name);
272     char *fullname = find_file(filename);
273     g_free(filename);
274    
275     if (!fullname) {
276     printf("style %s not found, trying %s instead\n", name, DEFAULT_STYLE);
277     filename = g_strdup_printf("%s.style", DEFAULT_STYLE);
278     fullname = find_file(filename);
279     g_free(filename);
280     if (!fullname) {
281     printf(" style not found, failed to find fallback style too\n");
282     return NULL;
283     }
284     }
285    
286     printf(" style filename: %s\n", fullname);
287    
288     style_t *style = style_parse(appdata, fullname);
289     g_free(fullname);
290    
291     printf(" elemstyle filename: %s\n", style->elemstyles_filename);
292     elemstyle_t *elemstyles =
293     josm_elemstyles_load(style->elemstyles_filename);
294     xmlFree(style->elemstyles_filename);
295     style->elemstyles = elemstyles;
296    
297     return style;
298     }
299    
300     void style_free(style_t *style) {
301     if(!style) return;
302    
303     printf("freeing style\n");
304    
305     if(style->elemstyles)
306     josm_elemstyles_free(style->elemstyles);
307    
308     if(style->name) g_free(style->name);
309    
310     if (style->icon.path_prefix) g_free(style->icon.path_prefix);
311    
312     g_free(style);
313     }
314    
315     static char *style_basename(char *name) {
316     char *retval = name;
317    
318     if(strrchr(name, '/'))
319     retval = strrchr(name, '/') + 1;
320    
321     /* create a local copy */
322     retval = g_strdup(retval);
323    
324     /* and cut off extension */
325     if(strrchr(retval, '.'))
326     *strrchr(retval, '.') = 0;
327    
328     return retval;
329     }
330    
331     void style_select(GtkWidget *parent, appdata_t *appdata) {
332     file_chain_t *chain = file_scan("*.style");
333    
334     printf("select style\n");
335    
336     /* there must be at least one style, otherwise */
337     /* the program wouldn't be running */
338     g_assert(chain);
339    
340     /* ------------------ style dialog ---------------- */
341     GtkWidget *dialog = gtk_dialog_new_with_buttons(_("Select style"),
342     GTK_WINDOW(parent), GTK_DIALOG_MODAL,
343     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
344     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
345     NULL);
346    
347     gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
348    
349     GtkWidget *hbox = gtk_hbox_new(FALSE, 8);
350     gtk_box_pack_start_defaults(GTK_BOX(hbox), gtk_label_new(_("Style:")));
351    
352     GtkWidget *cbox = NULL;
353     cbox = gtk_combo_box_new_text();
354    
355     /* fill combo box with presets */
356     int cnt = 0, match = -1;
357     file_chain_t *lchain = chain;
358     while(lchain) {
359     printf(" file: %s\n", lchain->name);
360    
361     style_t *style = style_parse(appdata, lchain->name);
362     printf(" name: %s\n", style->name);
363     gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), style->name);
364    
365     char *basename = style_basename(lchain->name);
366     if(strcmp(basename, appdata->settings->style) == 0) match = cnt;
367     g_free(basename);
368    
369     xmlFree(style->elemstyles_filename);
370     style->elemstyles_filename = NULL;
371     style_free(style);
372    
373     lchain = lchain->next;
374     cnt++;
375     }
376    
377     if(match >= 0)
378     gtk_combo_box_set_active(GTK_COMBO_BOX(cbox), match);
379    
380     gtk_box_pack_start_defaults(GTK_BOX(hbox), cbox);
381     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
382    
383     gtk_widget_show_all(dialog);
384    
385     if(GTK_RESPONSE_ACCEPT != gtk_dialog_run(GTK_DIALOG(dialog))) {
386     printf("user clicked cancel\n");
387     gtk_widget_destroy(dialog);
388     return;
389     }
390    
391     char *ptr = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cbox));
392     printf("user clicked ok on %s\n", ptr);
393    
394     while(chain) {
395     file_chain_t *next = chain->next;
396     style_t *style = style_parse(appdata, chain->name);
397    
398     if(strcmp(style->name, ptr) == 0) {
399     if(appdata->settings->style)
400     g_free(appdata->settings->style);
401    
402     appdata->settings->style = style_basename(chain->name);
403     }
404    
405     xmlFree(style->elemstyles_filename);
406     style->elemstyles_filename = NULL;
407     style_free(style);
408    
409     g_free(chain);
410     chain = next;
411     }
412    
413     gtk_widget_destroy(dialog);
414    
415     map_clear(appdata, MAP_LAYER_OBJECTS_ONLY);
416     /* let gtk clean up first */
417     while(gtk_events_pending()) {
418     putchar('.');
419     gtk_main_iteration();
420     }
421    
422     style_free(appdata->map->style);
423     appdata->map->style = style_load(appdata, appdata->settings->style);
424    
425     /* canvas background may have changed */
426     g_object_set(G_OBJECT(appdata->map->canvas), "background-color-rgb",
427     appdata->map->style->background.color, NULL);
428    
429     map_paint(appdata);
430     }