Contents of /trunk/src/style.c

Parent Directory Parent Directory | Revision Log Revision Log


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