Contents of /trunk/src/style.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 22 - (show annotations)
Wed Dec 17 16:43:46 2008 UTC (15 years, 4 months ago) by harbaum
File MIME type: text/plain
File size: 12756 byte(s)
Color system cleaned up, desktop fullscreen
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;
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 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 style->icon.path_prefix =
119 (char*)xmlGetProp(cur_node, BAD_CAST "path-prefix");
120 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 parse_color(cur_node, "arrow-color", &style->frisket.color);
137 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 parse_color(sub_node, "color", &style->frisket.border.color);
147 }
148 }
149 }
150
151 /* ---------- highlight ------------------------------- */
152 } else if(strcasecmp((char*)cur_node->name, "highlight") == 0) {
153 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 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 parse_color(cur_node, "color", &style->track.color);
164 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 float scale_max = 0;
173 xml_get_prop_float(cur_node, "scale-max", &scale_max);
174 if (scale_max > 0)
175 style->area.zoom_max = scaledn_to_zoom(scale_max);
176 else
177 style->area.zoom_max = 0;
178
179 parse_color(cur_node, "color", &style->area.color);
180
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 }