Contents of /trunk/src/josm_presets.c

Parent Directory Parent Directory | Revision Log Revision Log


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