Contents of /trunk/src/style.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 22 - (hide annotations)
Wed Dec 17 16:43:46 2008 UTC (15 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 12756 byte(s)
Color system cleaned up, desktop fullscreen
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 gboolean xml_prop_is(xmlNode *node, char *prop, char *str) {
38     char *prop_str = (char*)xmlGetProp(node, BAD_CAST prop);
39     if(!prop_str) return FALSE;
40    
41     gboolean match = (strcasecmp(prop_str, str) == 0);
42     xmlFree(prop_str);
43     return match;
44     }
45    
46     static style_t *parse_style(xmlDocPtr doc, xmlNode *a_node) {
47     xmlNode *cur_node = NULL, *sub_node = NULL;
48     style_t *style = g_new0(style_t, 1);
49    
50     style->name = (char*)xmlGetProp(a_node, BAD_CAST "name");
51    
52     /* -------------- setup defaults -------------------- */
53     /* (the defaults are pretty much the potlatch style) */
54 harbaum 22 style->background.color = 0xffffffff; // white
55 harbaum 1
56     style->area.border_width = 2.0;
57 harbaum 22 style->area.color = 0x00000060; // 37.5%
58 harbaum 1 style->area.zoom_max = 0.1111; // zoom factor above which an area is visible & selectable
59    
60     style->node.radius = 4.0;
61     style->node.border_radius = 2.0;
62 harbaum 22 style->node.color = 0x000000ff; // black with filling ...
63     style->node.fill_color = 0x008800ff; // ... in dark green
64 harbaum 1 style->node.show_untagged = FALSE;
65     style->node.zoom_max = 0.4444; // zoom factor above which a node is visible & selectable
66    
67     style->track.width = 6.0;
68     style->track.color = 0x0000ff40; // blue
69 harbaum 22 style->track.gps_color = 0x000080ff;
70 harbaum 1
71     style->way.width = 3.0;
72 harbaum 22 style->way.color = 0x606060ff; // grey
73     style->way.zoom_max = 0.2222; // zoom above which it's visible & selectable
74 harbaum 1
75     style->highlight.width = 3.0;
76     style->highlight.color = 0xffff0080;
77     style->highlight.node_color = 0xff000080;
78     style->highlight.touch_color = 0x0000ff80;
79     style->highlight.arrow_color = 0x0000ff80;
80     style->highlight.arrow_limit = 4.0;
81    
82     style->frisket.mult = 3.0;
83 harbaum 22 style->frisket.color = 0xffffffff;
84 harbaum 1 style->frisket.border.present = TRUE;
85     style->frisket.border.width = 6.0;
86     style->frisket.border.color = 0x00000099;
87    
88     style->icon.enable = FALSE;
89     style->icon.scale = 1.0; // icon size (multiplier)
90     style->icon.path_prefix = g_strdup("standard");
91    
92     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
93     if (cur_node->type == XML_ELEMENT_NODE) {
94     if(strcasecmp((char*)cur_node->name, "elemstyles") == 0) {
95     style->elemstyles_filename =
96     (char*)xmlGetProp(cur_node, BAD_CAST "filename");
97    
98     /* ---------- node ------------------------------------- */
99     } else if(strcasecmp((char*)cur_node->name, "node") == 0) {
100     parse_color(cur_node, "color", &style->node.color);
101 harbaum 22 parse_color(cur_node, "fill-color", &style->node.fill_color);
102 harbaum 1 xml_get_prop_float(cur_node, "radius", &style->node.radius);
103     xml_get_prop_float(cur_node, "border-radius",
104     &style->node.border_radius);
105     float scale_max = 0;
106     xml_get_prop_float(cur_node, "scale-max", &scale_max);
107     if (scale_max > 0)
108     style->node.zoom_max = scaledn_to_zoom(scale_max);
109     else
110     style->node.zoom_max = 0;
111    
112     style->node.show_untagged =
113     xml_prop_is(cur_node, "show-untagged", "true");
114    
115     /* ---------- icon ------------------------------------- */
116     } else if(strcasecmp((char*)cur_node->name, "icon") == 0) {
117     xml_get_prop_float(cur_node, "scale", &style->icon.scale);
118 harbaum 22 style->icon.path_prefix =
119     (char*)xmlGetProp(cur_node, BAD_CAST "path-prefix");
120 harbaum 1 style->icon.enable = xml_prop_is(cur_node, "enable", "true");
121    
122     /* ---------- way ------------------------------------- */
123     } else if(strcasecmp((char*)cur_node->name, "way") == 0) {
124     parse_color(cur_node, "color", &style->way.color);
125     xml_get_prop_float(cur_node, "width", &style->way.width);
126     float scale_max = 0;
127     xml_get_prop_float(cur_node, "scale-max", &scale_max);
128     if (scale_max > 0)
129     style->way.zoom_max = scaledn_to_zoom(scale_max);
130     else
131     style->way.zoom_max = 0;
132    
133     /* ---------- frisket --------------------------------- */
134     } else if(strcasecmp((char*)cur_node->name, "frisket") == 0) {
135     xml_get_prop_float(cur_node, "mult", &style->frisket.mult);
136 harbaum 22 parse_color(cur_node, "arrow-color", &style->frisket.color);
137 harbaum 1 style->frisket.border.present = FALSE;
138    
139     for(sub_node = cur_node->children; sub_node; sub_node=sub_node->next) {
140     if(sub_node->type == XML_ELEMENT_NODE) {
141     if(strcasecmp((char*)sub_node->name, "border") == 0) {
142     style->frisket.border.present = TRUE;
143     xml_get_prop_float(sub_node, "width",
144     &style->frisket.border.width);
145    
146 harbaum 22 parse_color(sub_node, "color", &style->frisket.border.color);
147 harbaum 1 }
148     }
149     }
150    
151     /* ---------- highlight ------------------------------- */
152     } else if(strcasecmp((char*)cur_node->name, "highlight") == 0) {
153 harbaum 22 parse_color(cur_node, "color", &style->highlight.color);
154     parse_color(cur_node, "node-color", &style->highlight.node_color);
155     parse_color(cur_node, "touch-color", &style->highlight.touch_color);
156     parse_color(cur_node, "arrow-color", &style->highlight.arrow_color);
157 harbaum 1 xml_get_prop_float(cur_node, "width", &style->highlight.width);
158     xml_get_prop_float(cur_node, "arrow-limit",
159     &style->highlight.arrow_limit);
160    
161     /* ---------- track ------------------------------------ */
162     } else if(strcasecmp((char*)cur_node->name, "track") == 0) {
163 harbaum 22 parse_color(cur_node, "color", &style->track.color);
164 harbaum 1 parse_color(cur_node, "gps-color", &style->track.gps_color);
165     xml_get_prop_float(cur_node, "width", &style->track.width);
166    
167     /* ---------- area ------------------------------------- */
168     } else if(strcasecmp((char*)cur_node->name, "area") == 0) {
169     style->area.has_border_color =
170     parse_color(cur_node, "border-color", &style->area.border_color);
171     xml_get_prop_float(cur_node,"border-width", &style->area.border_width);
172 harbaum 22 float scale_max = 0;
173 harbaum 1 xml_get_prop_float(cur_node, "scale-max", &scale_max);
174 harbaum 22 if (scale_max > 0)
175 harbaum 1 style->area.zoom_max = scaledn_to_zoom(scale_max);
176     else
177 harbaum 22 style->area.zoom_max = 0;
178 harbaum 1
179 harbaum 22 parse_color(cur_node, "color", &style->area.color);
180 harbaum 1
181     /* ---------- background ------------------------------- */
182     } else if(strcasecmp((char*)cur_node->name, "background") == 0) {
183     parse_color(cur_node, "color", &style->background.color);
184    
185     } else
186     printf(" found unhandled style/%s\n", cur_node->name);
187     }
188     }
189     return style;
190     }
191    
192     static style_t *parse_doc(xmlDocPtr doc) {
193     /* Get the root element node */
194     xmlNode *cur_node = NULL;
195     style_t *style = NULL;
196    
197     for(cur_node = xmlDocGetRootElement(doc);
198     cur_node; cur_node = cur_node->next) {
199     if (cur_node->type == XML_ELEMENT_NODE) {
200     if(strcasecmp((char*)cur_node->name, "style") == 0) {
201     if(!style)
202     style = parse_style(doc, cur_node);
203     } else
204     printf(" found unhandled %s\n", cur_node->name);
205     }
206     }
207    
208     xmlFreeDoc(doc);
209     xmlCleanupParser();
210     return style;
211     }
212    
213     static style_t *style_parse(appdata_t *appdata, char *fullname) {
214     style_t *style = NULL;
215    
216     xmlDoc *doc = NULL;
217    
218     LIBXML_TEST_VERSION;
219    
220     /* parse the file and get the DOM */
221     if((doc = xmlReadFile(fullname, NULL, 0)) == NULL) {
222     xmlErrorPtr errP = xmlGetLastError();
223     errorf(GTK_WIDGET(appdata->window),
224     _("Style parsing failed:\n\n"
225     "XML error while parsing style file\n"
226     "%s"), errP->message);
227    
228     return NULL;
229     } else {
230     style = parse_doc(doc);
231     style->iconP = &appdata->icon;
232     }
233    
234     return style;
235     }
236    
237     style_t *style_load(appdata_t *appdata, char *name) {
238     printf("Trying to load style %s\n", name);
239    
240     char *filename = g_strdup_printf("%s.style", name);
241     char *fullname = find_file(filename);
242     g_free(filename);
243    
244     if (!fullname) {
245     printf("style %s not found, trying %s instead\n", name, DEFAULT_STYLE);
246     filename = g_strdup_printf("%s.style", DEFAULT_STYLE);
247     fullname = find_file(filename);
248     g_free(filename);
249     if (!fullname) {
250     printf(" style not found, failed to find fallback style too\n");
251     return NULL;
252     }
253     }
254    
255     printf(" style filename: %s\n", fullname);
256    
257     style_t *style = style_parse(appdata, fullname);
258     g_free(fullname);
259    
260     printf(" elemstyle filename: %s\n", style->elemstyles_filename);
261     elemstyle_t *elemstyles =
262     josm_elemstyles_load(style->elemstyles_filename);
263     xmlFree(style->elemstyles_filename);
264     style->elemstyles = elemstyles;
265    
266     return style;
267     }
268    
269     void style_free(style_t *style) {
270     if(!style) return;
271    
272     printf("freeing style\n");
273    
274     if(style->elemstyles)
275     josm_elemstyles_free(style->elemstyles);
276    
277     if(style->name) g_free(style->name);
278    
279     if (style->icon.path_prefix) g_free(style->icon.path_prefix);
280    
281     g_free(style);
282     }
283    
284     static char *style_basename(char *name) {
285     char *retval = name;
286    
287     if(strrchr(name, '/'))
288     retval = strrchr(name, '/') + 1;
289    
290     /* create a local copy */
291     retval = g_strdup(retval);
292    
293     /* and cut off extension */
294     if(strrchr(retval, '.'))
295     *strrchr(retval, '.') = 0;
296    
297     return retval;
298     }
299    
300     void style_select(GtkWidget *parent, appdata_t *appdata) {
301     file_chain_t *chain = file_scan("*.style");
302    
303     printf("select style\n");
304    
305     /* there must be at least one style, otherwise */
306     /* the program wouldn't be running */
307     g_assert(chain);
308    
309     /* ------------------ style dialog ---------------- */
310     GtkWidget *dialog = gtk_dialog_new_with_buttons(_("Select style"),
311     GTK_WINDOW(parent), GTK_DIALOG_MODAL,
312     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
313     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
314     NULL);
315    
316     gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
317    
318     GtkWidget *hbox = gtk_hbox_new(FALSE, 8);
319     gtk_box_pack_start_defaults(GTK_BOX(hbox), gtk_label_new(_("Style:")));
320    
321     GtkWidget *cbox = NULL;
322     cbox = gtk_combo_box_new_text();
323    
324     /* fill combo box with presets */
325     int cnt = 0, match = -1;
326     file_chain_t *lchain = chain;
327     while(lchain) {
328     printf(" file: %s\n", lchain->name);
329    
330     style_t *style = style_parse(appdata, lchain->name);
331     printf(" name: %s\n", style->name);
332     gtk_combo_box_append_text(GTK_COMBO_BOX(cbox), style->name);
333    
334     char *basename = style_basename(lchain->name);
335     if(strcmp(basename, appdata->settings->style) == 0) match = cnt;
336     g_free(basename);
337    
338     xmlFree(style->elemstyles_filename);
339     style->elemstyles_filename = NULL;
340     style_free(style);
341    
342     lchain = lchain->next;
343     cnt++;
344     }
345    
346     if(match >= 0)
347     gtk_combo_box_set_active(GTK_COMBO_BOX(cbox), match);
348    
349     gtk_box_pack_start_defaults(GTK_BOX(hbox), cbox);
350     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
351    
352     gtk_widget_show_all(dialog);
353    
354     if(GTK_RESPONSE_ACCEPT != gtk_dialog_run(GTK_DIALOG(dialog))) {
355     printf("user clicked cancel\n");
356     gtk_widget_destroy(dialog);
357     return;
358     }
359    
360     char *ptr = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cbox));
361     printf("user clicked ok on %s\n", ptr);
362    
363     while(chain) {
364     file_chain_t *next = chain->next;
365     style_t *style = style_parse(appdata, chain->name);
366    
367     if(strcmp(style->name, ptr) == 0) {
368     if(appdata->settings->style)
369     g_free(appdata->settings->style);
370    
371     appdata->settings->style = style_basename(chain->name);
372     }
373    
374     xmlFree(style->elemstyles_filename);
375     style->elemstyles_filename = NULL;
376     style_free(style);
377    
378     g_free(chain);
379     chain = next;
380     }
381    
382     gtk_widget_destroy(dialog);
383    
384     map_clear(appdata, MAP_LAYER_OBJECTS_ONLY);
385     /* let gtk clean up first */
386     while(gtk_events_pending()) {
387     putchar('.');
388     gtk_main_iteration();
389     }
390    
391     style_free(appdata->map->style);
392     appdata->map->style = style_load(appdata, appdata->settings->style);
393    
394     /* canvas background may have changed */
395     g_object_set(G_OBJECT(appdata->map->canvas), "background-color-rgb",
396     appdata->map->style->background.color, NULL);
397    
398     map_paint(appdata);
399     }