Contents of /trunk/src/josm_presets.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 50 - (show annotations)
Thu Feb 5 10:34:53 2009 UTC (15 years, 3 months ago) by achadwick
File MIME type: text/plain
File size: 22506 byte(s)
Quick fix: support the presets language's new <optional/>.
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 /* parse children of a given node for into *widget */
85 static presets_widget_t **parse_widgets(xmlNode *a_node, presets_widget_t **widget) {
86 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 /* special handling of pre-<space/> separators */
98 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 }
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
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 }
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 printf("found unhandled annotations/item/%s\n", cur_node->name);
172 }
173 }
174 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 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
768 // vim:et:ts=8:sw=2:sts=2:ai