Contents of /src/style.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show 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 /*
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 }