Contents of /trunk/src/josm_presets.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 51 - (show annotations)
Thu Feb 5 20:08:46 2009 UTC (15 years, 3 months ago) by harbaum
File MIME type: text/plain
File size: 24765 byte(s)
Updated presets parser
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 #ifndef LIBXML_TREE_ENABLED
26 #error "Tree not enabled in libxml"
27 #endif
28
29 #ifndef USE_HILDON
30 #include <libgnome/gnome-url.h>
31 #else
32 #include <tablet-browser-interface.h>
33 #endif
34
35 typedef struct {
36 appdata_t *appdata;
37 char *link;
38 } www_context_t;
39
40 /* ---------- simple interface to the systems web browser ---------- */
41 static void on_info(GtkWidget *widget, www_context_t *context) {
42 #ifndef USE_HILDON
43 /* taken from gnome-open, part of libgnome */
44 GError *err = NULL;
45 gnome_url_show(context->link, &err);
46 #else
47 osso_rpc_run_with_defaults(context->appdata->osso_context, "osso_browser",
48 OSSO_BROWSER_OPEN_NEW_WINDOW_REQ, NULL,
49 DBUS_TYPE_STRING, context->link,
50 DBUS_TYPE_BOOLEAN, FALSE, DBUS_TYPE_INVALID);
51 #endif
52 }
53
54 /* --------------------- presets.xml parsing ----------------------- */
55
56 static gboolean xmlGetPropIs(xmlNode *node, char *prop, char *is) {
57 char *prop_str = (char*)xmlGetProp(node, BAD_CAST prop);
58 if(!prop_str) return FALSE;
59
60 gboolean retval = FALSE;
61 retval = (strcasecmp(prop_str, is) == 0);
62 xmlFree(prop_str);
63 return retval;
64 }
65
66 static presets_value_t *xmlGetPropValues(xmlNode *node, char *prop) {
67 char *prop_str = (char*)xmlGetProp(node, BAD_CAST prop);
68 if(!prop_str) return NULL;
69 presets_value_t *value = NULL, **cur = &value;
70
71 /* cut values strings */
72 char *c, *p = prop_str;
73 while((c = strchr(p, ','))) {
74
75 *cur = g_new0(presets_value_t, 1);
76 (*cur)->text = g_strndup(p, c-p);
77 cur = &((*cur)->next);
78
79 p = c+1;
80 }
81
82 /* attach remaining string as last value */
83 *cur = g_new0(presets_value_t, 1);
84 (*cur)->text = g_strdup(p);
85
86 xmlFree(prop_str);
87 return value;
88 }
89
90 char *josm_icon_name_adjust(char *name) {
91 /* the icon loader uses names without extension */
92 if(!strcasecmp(name+strlen(name)-4, ".png"))
93 name[strlen(name)-4] = 0;
94
95 #ifdef JOSM_PATH_ADJUST
96 if(strncmp(name, "presets/", strlen("presets/")) != 0) {
97 if(strrchr(name, '/')) {
98 char *new = g_strdup_printf("presets%s", strrchr(name, '/'));
99 xmlFree(name);
100 name = new;
101
102 printf("icon path adjusted to %s\n", name);
103 }
104 }
105 #endif
106 return name;
107 }
108
109 /* parse children of a given node for into *widget */
110 static presets_widget_t **parse_widgets(xmlNode *a_node,
111 presets_item_t *item,
112 presets_widget_t **widget) {
113 xmlNode *cur_node = NULL;
114
115 for(cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
116 if(cur_node->type == XML_ELEMENT_NODE) {
117
118 if(strcasecmp((char*)cur_node->name, "space") == 0) {
119
120 /* --------- space widget --------- */
121 /* we are low on screen space on the handhelds, */
122 /* so we just ignore extra spaces */
123 #ifndef USE_HILDON
124 *widget = g_new0(presets_widget_t, 1);
125 (*widget)->type = WIDGET_TYPE_SPACE;
126 #endif
127
128 } else if(strcasecmp((char*)cur_node->name, "label") == 0) {
129
130 /* --------- label widget --------- */
131 *widget = g_new0(presets_widget_t, 1);
132 (*widget)->type = WIDGET_TYPE_LABEL;
133 (*widget)->text = (char*)xmlGetProp(cur_node, BAD_CAST "text");
134
135 /* special handling of pre-<space/> separators */
136 if(!(*widget)->text || (strcmp((*widget)->text, " ") == 0)) {
137 (*widget)->type = WIDGET_TYPE_SEPARATOR;
138 if((*widget)->text) xmlFree((*widget)->text);
139 (*widget)->text = NULL;
140 }
141
142 widget = &((*widget)->next);
143
144 }
145 else if(strcasecmp((char*)cur_node->name, "space") == 0) {
146 // new-style separators
147 *widget = g_new0(presets_widget_t, 1);
148 (*widget)->type = WIDGET_TYPE_SEPARATOR;
149 (*widget)->text = NULL;
150 widget = &((*widget)->next);
151 }
152 else if(strcasecmp((char*)cur_node->name, "text") == 0) {
153
154 /* --------- text widget --------- */
155 *widget = g_new0(presets_widget_t, 1);
156 (*widget)->type = WIDGET_TYPE_TEXT;
157 (*widget)->text = (char*)xmlGetProp(cur_node, BAD_CAST "text");
158 (*widget)->key = (char*)xmlGetProp(cur_node, BAD_CAST "key");
159 (*widget)->del_if_empty = xmlGetPropIs(cur_node, "delete_if_empty", "true");
160 (*widget)->text_w.def = (char*)xmlGetProp(cur_node, BAD_CAST "default");
161 widget = &((*widget)->next);
162
163 } else if(strcasecmp((char*)cur_node->name, "combo") == 0) {
164
165 /* --------- combo widget --------- */
166 *widget = g_new0(presets_widget_t, 1);
167 (*widget)->type = WIDGET_TYPE_COMBO;
168 (*widget)->text = (char*)xmlGetProp(cur_node, BAD_CAST "text");
169 (*widget)->key = (char*)xmlGetProp(cur_node, BAD_CAST "key");
170 (*widget)->del_if_empty = xmlGetPropIs(cur_node,
171 "delete_if_empty", "true");
172 (*widget)->combo_w.def = (char*)xmlGetProp(cur_node,
173 BAD_CAST "default");
174 (*widget)->combo_w.values = xmlGetPropValues(cur_node, "values");
175 widget = &((*widget)->next);
176
177 } else if(strcasecmp((char*)cur_node->name, "key") == 0) {
178
179 /* --------- invisible key widget --------- */
180 *widget = g_new0(presets_widget_t, 1);
181 (*widget)->type = WIDGET_TYPE_KEY;
182 (*widget)->key = (char*)xmlGetProp(cur_node, BAD_CAST "key");
183 (*widget)->key_w.value = (char*)xmlGetProp(cur_node, BAD_CAST "value");
184 widget = &((*widget)->next);
185
186 } else if(strcasecmp((char*)cur_node->name, "check") == 0) {
187
188 /* --------- check widget --------- */
189 *widget = g_new0(presets_widget_t, 1);
190 (*widget)->type = WIDGET_TYPE_CHECK;
191 (*widget)->text = (char*)xmlGetProp(cur_node, BAD_CAST "text");
192 (*widget)->key = (char*)xmlGetProp(cur_node, BAD_CAST "key");
193 (*widget)->del_if_empty = xmlGetPropIs(cur_node,
194 "delete_if_empty", "true");
195 (*widget)->check_w.def = xmlGetPropIs(cur_node, "default", "on");
196 widget = &((*widget)->next);
197
198 }
199 else if (strcasecmp((char*)cur_node->name, "optional") == 0) {
200 // Could be done as a fold-out box width twisties.
201 // Or maybe as a separate dialog for small screens.
202 // For now, just recurse and build up our current list.
203 widget = parse_widgets(cur_node, item, widget);
204 }
205
206 else if (strcasecmp((char*)cur_node->name, "link") == 0) {
207
208 /* --------- link is not a widget, but a property of item --------- */
209 if(!item->link) {
210 item->link = (char*)xmlGetProp(cur_node, BAD_CAST "href");
211 } else
212 printf("ignoring surplus link\n");
213
214 } else
215 printf("found unhandled annotations/item/%s\n", cur_node->name);
216 }
217 }
218 return widget;
219 }
220
221 static presets_item_t *parse_item(xmlDocPtr doc, xmlNode *a_node) {
222 presets_item_t *item = g_new0(presets_item_t, 1);
223 item->is_group = FALSE;
224
225 /* ------ parse items own properties ------ */
226 item->name = (char*)xmlGetProp(a_node, BAD_CAST "name");
227
228 if((item->icon = (char*)xmlGetProp(a_node, BAD_CAST "icon")))
229 item->icon = josm_icon_name_adjust(item->icon);
230
231 presets_widget_t **widget = &item->widget;
232 parse_widgets(a_node, item, widget);
233 return item;
234 }
235
236 static presets_item_t *parse_group(xmlDocPtr doc, xmlNode *a_node) {
237 xmlNode *cur_node = NULL;
238
239 presets_item_t *group = g_new0(presets_item_t, 1);
240 group->is_group = TRUE;
241
242 /* ------ parse groups own properties ------ */
243 group->name = (char*)xmlGetProp(a_node, BAD_CAST "name");
244
245 if((group->icon = (char*)xmlGetProp(a_node, BAD_CAST "icon")))
246 group->icon = josm_icon_name_adjust(group->icon);
247
248 presets_item_t **preset = &group->group;
249
250 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
251 if (cur_node->type == XML_ELEMENT_NODE) {
252 if(strcasecmp((char*)cur_node->name, "item") == 0) {
253 *preset = parse_item(doc, cur_node);
254 if(*preset) preset = &((*preset)->next);
255 } else if(strcasecmp((char*)cur_node->name, "group") == 0) {
256 *preset = parse_group(doc, cur_node);
257 if(*preset) preset = &((*preset)->next);
258 } else if(strcasecmp((char*)cur_node->name, "separator") == 0) {
259 *preset = g_new0(presets_item_t, 1);
260 preset = &((*preset)->next);
261 } else
262 printf("found unhandled annotations/group/%s\n", cur_node->name);
263 }
264 }
265 return group;
266 }
267
268 static presets_item_t *parse_annotations(xmlDocPtr doc, xmlNode *a_node) {
269 xmlNode *cur_node = NULL;
270 presets_item_t *presets = NULL, **preset = &presets;
271
272 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
273 if (cur_node->type == XML_ELEMENT_NODE) {
274 if(strcasecmp((char*)cur_node->name, "item") == 0) {
275 *preset = parse_item(doc, cur_node);
276 if(*preset) preset = &((*preset)->next);
277 } else if(strcasecmp((char*)cur_node->name, "group") == 0) {
278 *preset = parse_group(doc, cur_node);
279 if(*preset) preset = &((*preset)->next);
280 } else if(strcasecmp((char*)cur_node->name, "separator") == 0) {
281 *preset = g_new0(presets_item_t, 1);
282 preset = &((*preset)->next);
283 } else
284 printf("found unhandled annotations/%s\n", cur_node->name);
285 }
286 }
287 return presets;
288 }
289
290 static presets_item_t *parse_doc(xmlDocPtr doc) {
291 /* Get the root element node */
292 xmlNode *cur_node = NULL;
293 presets_item_t *presets = NULL;
294
295 for(cur_node = xmlDocGetRootElement(doc);
296 cur_node; cur_node = cur_node->next) {
297 if (cur_node->type == XML_ELEMENT_NODE) {
298 if(strcasecmp((char*)cur_node->name, "annotations") == 0) {
299 presets = parse_annotations(doc, cur_node);
300 } else
301 printf("found unhandled %s\n", cur_node->name);
302 }
303 }
304
305 xmlFreeDoc(doc);
306 xmlCleanupParser();
307 return presets;
308 }
309
310 presets_item_t *josm_presets_load(void) {
311 presets_item_t *presets = NULL;
312
313 printf("Loading JOSM presets ...\n");
314
315 LIBXML_TEST_VERSION;
316
317 char *filename = find_file("presets.xml");
318 if(!filename) return NULL;
319
320 /* parse the file and get the DOM */
321 xmlDoc *doc = NULL;
322 if((doc = xmlReadFile(filename, NULL, 0)) == NULL) {
323 xmlErrorPtr errP = xmlGetLastError();
324 printf("presets download failed: "
325 "XML error while parsing:\n"
326 "%s\n", errP->message);
327 } else {
328 printf("ok, parse doc tree\n");
329 presets = parse_doc(doc);
330 }
331
332 g_free(filename);
333 return presets;
334 }
335
336 /* --------------------- the items dialog -------------------- */
337
338 static void attach_both(GtkWidget *table, GtkWidget *widget, gint y) {
339 gtk_table_attach(GTK_TABLE(table), widget, 0,2,y,y+1,
340 GTK_EXPAND | GTK_FILL, 0,0,0);
341 }
342
343 static void attach_text(GtkWidget *table, char *text, gint y) {
344 gtk_table_attach(GTK_TABLE(table), gtk_label_new(text), 0,1,y,y+1,
345 GTK_EXPAND | GTK_FILL, 0,0,0);
346 }
347
348 static void attach_right(GtkWidget *table, GtkWidget *widget, gint y) {
349 gtk_table_attach(GTK_TABLE(table), widget, 1,2,y,y+1,
350 GTK_EXPAND | GTK_FILL, 0,0,0);
351 }
352
353 static tag_t **store_value(presets_widget_t *widget, tag_t **ctag,
354 char *value) {
355 if((value && strlen(value)) || !widget->del_if_empty) {
356 *ctag = g_new0(tag_t, 1);
357 (*ctag)->key = g_strdup(widget->key);
358 (*ctag)->value = g_strdup(value?value:"");
359
360 printf("key = %s, value = %s\n",
361 widget->key, (*ctag)->value);
362
363 ctag = &((*ctag)->next);
364 } else
365 printf("ignore empty key = %s\n", widget->key);
366
367 return ctag;
368 }
369
370 #ifdef USE_HILDON
371 static gint table_expose_event(GtkWidget *widget, GdkEventExpose *event,
372 gboolean *first) {
373
374 if(*first) {
375 guint border_width =
376 gtk_container_get_border_width(GTK_CONTAINER(widget->parent));
377 gtk_viewport_set_shadow_type(GTK_VIEWPORT(widget->parent), GTK_SHADOW_NONE);
378
379 gtk_widget_set_size_request(GTK_WIDGET(widget->parent), -1,
380 widget->allocation.height + 2*border_width);
381 *first = FALSE;
382 }
383 return FALSE;
384 }
385 #endif
386
387 static tag_t *presets_item_dialog(appdata_t *appdata, GtkWindow *parent,
388 presets_item_t *item, tag_t *orig_tag) {
389 GtkWidget *dialog = NULL;
390 gboolean ok = FALSE;
391 tag_t *tag = NULL, **ctag = &tag;
392 www_context_t *www_context = NULL;
393
394 printf("dialog for item %s\n", item->name);
395
396 /* build dialog from items widget list */
397 presets_widget_t *widget = item->widget;
398
399 /* count total number of widgets and number of widgets that */
400 /* have an interactive gui element. We won't show a dialog */
401 /* at all if there's no interactive gui element at all */
402 gint widget_cnt = 0, interactive_widget_cnt = 0;
403 while(widget) {
404 if((widget->type != WIDGET_TYPE_LABEL) &&
405 (widget->type != WIDGET_TYPE_SEPARATOR) &&
406 (widget->type != WIDGET_TYPE_KEY))
407 interactive_widget_cnt++;
408
409 widget_cnt++;
410 widget = widget->next;
411 }
412
413 /* allocate space for required number of gtk widgets */
414 GtkWidget **gtk_widgets = (GtkWidget**)g_new0(GtkWidget, widget_cnt);
415
416 if(interactive_widget_cnt) {
417 dialog = gtk_dialog_new_with_buttons(
418 item->name, parent, GTK_DIALOG_MODAL,
419 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
420 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
421 NULL);
422
423 /* if a web link has been provided for this item install */
424 /* a button for this */
425 if(item->link) {
426 www_context = g_new0(www_context_t, 1);
427 www_context->link = item->link;
428 www_context->appdata = appdata;
429
430 GtkWidget *button = gtk_dialog_add_button(GTK_DIALOG(dialog), _
431 ("Info..."), GTK_RESPONSE_HELP);
432 gtk_signal_connect(GTK_OBJECT(button), "clicked",
433 GTK_SIGNAL_FUNC(on_info), (gpointer)www_context);
434 }
435
436 /* special handling for the first label/separators */
437 guint widget_skip = 0; // number of initial widgets to skip
438 widget = item->widget;
439 if(widget && (widget->type == WIDGET_TYPE_LABEL)) {
440 gtk_window_set_title(GTK_WINDOW(dialog), widget->text);
441
442 widget_skip++; // this widget isn't part of the contents anymore
443 widget = widget->next;
444
445 /* skip all following separators (and keys) */
446 while(widget &&
447 ((widget->type == WIDGET_TYPE_SEPARATOR) ||
448 (widget->type == WIDGET_TYPE_SPACE) ||
449 (widget->type == WIDGET_TYPE_KEY))) {
450 widget_skip++; // this widget isn't part of the contents anymore
451 widget = widget->next;
452 }
453 }
454
455 /* create table of required size */
456 GtkWidget *table = gtk_table_new(widget_cnt-widget_skip, 2, FALSE);
457
458 widget_cnt = widget_skip;
459 while(widget) {
460 /* check if there's a value with this key already */
461 char *preset = osm_tag_get_by_key(orig_tag, widget->key);
462
463 switch(widget->type) {
464 case WIDGET_TYPE_SEPARATOR:
465 attach_both(table, gtk_hseparator_new(), widget_cnt-widget_skip);
466 break;
467
468 case WIDGET_TYPE_SPACE:
469 /* space is just an empty label until we find something better */
470 attach_both(table, gtk_label_new(" "), widget_cnt-widget_skip);
471 break;
472
473 case WIDGET_TYPE_LABEL:
474 attach_both(table, gtk_label_new(widget->text), widget_cnt-widget_skip);
475 break;
476
477 case WIDGET_TYPE_COMBO:
478 attach_text(table, widget->text, widget_cnt-widget_skip);
479
480 if(!preset && widget->combo_w.def) preset = widget->combo_w.def;
481 gtk_widgets[widget_cnt] = gtk_combo_box_new_text();
482 presets_value_t *value = widget->combo_w.values;
483 int count = 0, active = -1;
484 while(value) {
485 if(active < 0 && preset && strcmp(preset, value->text)==0)
486 active = count;
487
488 gtk_combo_box_append_text(GTK_COMBO_BOX(gtk_widgets[widget_cnt]),
489 value->text);
490 value = value->next;
491 count++;
492 }
493
494 gtk_combo_box_set_active(GTK_COMBO_BOX(gtk_widgets[widget_cnt]),
495 active);
496 attach_right(table, gtk_widgets[widget_cnt], widget_cnt-widget_skip);
497 break;
498
499 case WIDGET_TYPE_CHECK:
500 { gboolean def = FALSE;
501 if(preset) def = ((strcasecmp(preset, "true") == 0) ||
502 (strcasecmp(preset, "yes") == 0));
503 else def = widget->check_w.def;
504
505 gtk_widgets[widget_cnt] =
506 gtk_check_button_new_with_label(widget->text);
507 gtk_toggle_button_set_active(
508 GTK_TOGGLE_BUTTON(gtk_widgets[widget_cnt]), def);
509 attach_right(table, gtk_widgets[widget_cnt], widget_cnt-widget_skip);
510 } break;
511
512 case WIDGET_TYPE_TEXT:
513 attach_text(table, widget->text, widget_cnt-widget_skip);
514
515 if(!preset && widget->text_w.def) preset = widget->text_w.def;
516 gtk_widgets[widget_cnt] = gtk_entry_new();
517 if(preset)
518 gtk_entry_set_text(GTK_ENTRY(gtk_widgets[widget_cnt]), preset);
519
520 attach_right(table, gtk_widgets[widget_cnt], widget_cnt-widget_skip);
521 break;
522
523 default:
524 break;
525 }
526
527 widget_cnt++;
528 widget = widget->next;
529 }
530
531 #ifndef USE_HILDON
532 /* add widget to dialog */
533 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table);
534 gtk_window_set_default_size(GTK_WINDOW(dialog), 300, 50);
535 #else
536 /* put it into a scrolled window */
537 GtkWidget *scroll_win = gtk_scrolled_window_new(NULL, NULL);
538 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll_win),
539 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
540 gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scroll_win),
541 table);
542
543 gboolean first = TRUE;
544 gtk_signal_connect(GTK_OBJECT(table), "expose_event",
545 G_CALLBACK(table_expose_event), &first);
546
547 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), scroll_win);
548 // gtk_window_set_default_size(GTK_WINDOW(dialog), 50, 400);
549 #endif
550
551 gtk_widget_show_all(dialog);
552
553 /* run gtk_dialog_run, but continue if e.g. the help button was pressed */
554 int result = -1;
555 do
556 result = gtk_dialog_run(GTK_DIALOG(dialog));
557 while((result != GTK_RESPONSE_DELETE_EVENT) &&
558 (result != GTK_RESPONSE_ACCEPT) &&
559 (result != GTK_RESPONSE_REJECT));
560
561 if(result == GTK_RESPONSE_ACCEPT)
562 ok = TRUE;
563
564 } else
565 ok = TRUE;
566
567 if(ok) {
568 /* handle all children of the table */
569 widget = item->widget;
570 widget_cnt = 0;
571 while(widget) {
572 switch(widget->type) {
573 case WIDGET_TYPE_COMBO:
574 g_assert(GTK_WIDGET_TYPE(gtk_widgets[widget_cnt]) ==
575 GTK_TYPE_COMBO_BOX);
576
577 ctag = store_value(widget, ctag, gtk_combo_box_get_active_text(
578 GTK_COMBO_BOX(gtk_widgets[widget_cnt])));
579 break;
580
581 case WIDGET_TYPE_TEXT:
582 g_assert(GTK_WIDGET_TYPE(gtk_widgets[widget_cnt]) ==
583 GTK_TYPE_ENTRY);
584
585 ctag = store_value(widget, ctag, (char*)gtk_entry_get_text(
586 GTK_ENTRY(gtk_widgets[widget_cnt])));
587 break;
588
589 case WIDGET_TYPE_CHECK:
590 g_assert(GTK_WIDGET_TYPE(gtk_widgets[widget_cnt]) ==
591 GTK_TYPE_CHECK_BUTTON);
592
593 ctag = store_value(widget, ctag,
594 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
595 gtk_widgets[widget_cnt]))?"yes":
596 (widget->del_if_empty?NULL:"no"));
597 break;
598
599 case WIDGET_TYPE_KEY:
600 g_assert(!gtk_widgets[widget_cnt]);
601
602 ctag = store_value(widget, ctag, widget->key_w.value);
603 break;
604
605 default:
606 break;
607 }
608
609 widget_cnt++;
610 widget = widget->next;
611 }
612
613 *ctag = g_new0(tag_t, 1);
614 (*ctag)->key = g_strdup("created_by");
615 (*ctag)->value = g_strdup(PACKAGE " v" VERSION);
616 }
617
618 g_free(gtk_widgets);
619
620 if(interactive_widget_cnt)
621 gtk_widget_destroy(dialog);
622
623 if(www_context)
624 g_free(www_context);
625
626 return tag;
627 }
628
629 /* ------------------- the item list (popup menu) -------------- */
630
631 typedef struct {
632 appdata_t *appdata;
633 GtkWidget *menu;
634 tag_context_t *tag_context;
635 } presets_context_t;
636
637 static void
638 cb_menu_item(GtkMenuItem *menu_item, gpointer data) {
639 presets_context_t *context = (presets_context_t*)data;
640
641 presets_item_t *item = g_object_get_data(G_OBJECT(menu_item), "item");
642 g_assert(item);
643
644 tag_t *tag =
645 presets_item_dialog(context->appdata,
646 GTK_WINDOW(context->tag_context->dialog), item,
647 *context->tag_context->tag);
648
649 if(tag) {
650 tag_context_t *tag_context = context->tag_context;
651
652 /* add new tags to the old list and replace entries with the same key */
653
654 while(tag) {
655 #if 0
656 printf("current:\n");
657 tag_t *mdst = &tag_context->tag;
658 while(mdst) {
659 printf("%s: %s\n", msdt
660 #endif
661
662 tag_t *next = tag->next;
663 tag->next = NULL;
664
665 tag_t **dst = tag_context->tag;
666 gboolean replaced = FALSE;
667 while(*dst && !replaced) {
668 if(strcasecmp((*dst)->key, tag->key) == 0) {
669 g_free((*dst)->value);
670 (*dst)->value = g_strdup(tag->value);
671 replaced = TRUE;
672 }
673 dst = &(*dst)->next;
674 }
675
676 /* if nothing was replaced, then just append new tag */
677 if(!replaced)
678 *dst = tag;
679 else
680 osm_tag_free(tag);
681
682 tag = next;
683 }
684
685 #if 0
686 /* free existing tags */
687 osm_tags_free(*tag_context->tag);
688
689 /* attach new tags */
690 *tag_context->tag = tag;
691 #endif
692
693 info_tags_replace(tag_context);
694 }
695 }
696
697 static GtkWidget *build_menu(presets_context_t *context,
698 presets_item_t *item) {
699 GtkWidget *menu = gtk_menu_new();
700
701 while(item) {
702 GtkWidget *menu_item;
703
704 if(item->name) {
705 if(!item->icon)
706 menu_item = gtk_menu_item_new_with_label(item->name);
707 else {
708 menu_item = gtk_image_menu_item_new_with_label(item->name);
709 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),
710 icon_widget_load(&context->appdata->icon, item->icon));
711 }
712
713 if(item->is_group)
714 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item),
715 build_menu(context, item->group));
716 else {
717 g_object_set_data(G_OBJECT(menu_item), "item", item);
718 g_signal_connect(menu_item, "activate",
719 GTK_SIGNAL_FUNC(cb_menu_item), context);
720 }
721 } else
722 menu_item = gtk_separator_menu_item_new();
723
724 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
725 item = item->next;
726 }
727
728 return menu;
729 }
730
731 static gint button_press(GtkWidget *widget, GdkEventButton *event,
732 gpointer data) {
733 presets_context_t *context = (presets_context_t*)data;
734
735 if(event->type == GDK_BUTTON_PRESS) {
736 printf("button press %d %d\n", event->button, event->time);
737
738 gtk_menu_popup(GTK_MENU(context->menu), NULL, NULL, NULL, NULL,
739 event->button, event->time);
740
741 /* Tell calling code that we have handled this event; the buck
742 * stops here. */
743 return TRUE;
744 }
745 return FALSE;
746 }
747
748 static gint on_button_destroy(GtkWidget *widget, gpointer data) {
749 presets_context_t *context = (presets_context_t*)data;
750
751 printf("freeing preset button context\n");
752 gtk_widget_destroy(context->menu);
753 g_free(context);
754
755 return FALSE;
756 }
757
758 GtkWidget *josm_presets_select(appdata_t *appdata, tag_context_t *tag_context) {
759 presets_context_t *context = g_new0(presets_context_t, 1);
760 context->appdata = appdata;
761 context->tag_context = tag_context;
762
763 context->menu = build_menu(context, appdata->presets);
764 gtk_widget_show_all( GTK_WIDGET(context->menu) );
765
766 GtkWidget *but = gtk_button_new_with_label(_("Presets..."));
767 gtk_widget_set_events(but, GDK_EXPOSURE_MASK);
768 gtk_widget_add_events(but, GDK_BUTTON_PRESS_MASK);
769 gtk_signal_connect(GTK_OBJECT(but), "button-press-event",
770 (GtkSignalFunc)button_press, context);
771
772 gtk_signal_connect(GTK_OBJECT(but), "destroy",
773 (GtkSignalFunc)on_button_destroy, context);
774
775 return but;
776 }
777
778 /* ----------------------- cleaning up --------------------- */
779
780 static void free_values(presets_value_t *value) {
781 while(value) {
782 presets_value_t *next = value->next;
783 if(value->text) g_free(value->text);
784 g_free(value);
785 value = next;
786 }
787
788 }
789
790 static void free_widget(presets_widget_t *widget) {
791 if(widget->key) xmlFree(widget->key);
792 if(widget->text) xmlFree(widget->text);
793
794 switch(widget->type) {
795 case WIDGET_TYPE_TEXT:
796 if(widget->text_w.def) xmlFree(widget->text_w.def);
797 break;
798
799 case WIDGET_TYPE_COMBO:
800 if(widget->combo_w.def) xmlFree(widget->combo_w.def);
801 if(widget->combo_w.values) free_values(widget->combo_w.values);
802 break;
803
804 case WIDGET_TYPE_KEY:
805 if(widget->key_w.value) xmlFree(widget->key_w.value);
806 break;
807
808 default:
809 break;
810 }
811
812 g_free(widget);
813 }
814
815 static void free_widgets(presets_widget_t *widget) {
816 while(widget) {
817 presets_widget_t *next = widget->next;
818 free_widget(widget);
819 widget = next;
820 }
821 }
822
823 static void free_items(presets_item_t *item);
824 static void free_item(presets_item_t *item) {
825 if(item->name) xmlFree(item->name);
826 if(item->icon) xmlFree(item->icon);
827
828 if(item->is_group)
829 free_items(item->group);
830 else
831 free_widgets(item->widget);
832
833 g_free(item);
834 }
835
836 static void free_items(presets_item_t *item) {
837 while(item) {
838 presets_item_t *next = item->next;
839 free_item(item);
840 item = next;
841 }
842 }
843
844 void josm_presets_free(presets_item_t *presets) {
845 free_items(presets);
846 }
847
848 // vim:et:ts=8:sw=2:sts=2:ai