Contents of /trunk/src/info.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (hide annotations)
Wed Jan 21 20:01:18 2009 UTC (15 years, 3 months ago) by harbaum
File MIME type: text/plain
File size: 18736 byte(s)
ID hashing for fast ID resolving
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     enum {
23     TAG_COL_KEY = 0,
24     TAG_COL_VALUE,
25     TAG_COL_COLLISION,
26     TAG_COL_DATA,
27     TAG_NUM_COLS
28     };
29    
30     gboolean info_tag_key_collision(tag_t *tags, tag_t *tag) {
31     while(tags) {
32     if((tags != tag) && (strcasecmp(tags->key, tag->key) == 0))
33     return TRUE;
34    
35     tags = tags->next;
36     }
37     return FALSE;
38     }
39    
40     static gboolean
41     view_selection_func(GtkTreeSelection *selection, GtkTreeModel *model,
42     GtkTreePath *path, gboolean path_currently_selected,
43     gpointer userdata) {
44     tag_context_t *context = (tag_context_t*)userdata;
45     GtkTreeIter iter;
46    
47     if(gtk_tree_model_get_iter(model, &iter, path)) {
48     g_assert(gtk_tree_path_get_depth(path) == 1);
49    
50     tag_t *tag;
51     gtk_tree_model_get(model, &iter, TAG_COL_DATA, &tag, -1);
52    
53     if(context->but_remove && context->but_edit) {
54    
55     /* you just cannot delete or edit the "created_by" tag */
56     if(context->but_remove && context->but_edit) {
57     if(strcasecmp(tag->key, "created_by") == 0) {
58     gtk_widget_set_sensitive(context->but_remove, FALSE);
59     gtk_widget_set_sensitive(context->but_edit, FALSE);
60     } else {
61     gtk_widget_set_sensitive(context->but_remove, TRUE);
62     gtk_widget_set_sensitive(context->but_edit, TRUE);
63     }
64     }
65     }
66     }
67    
68     return TRUE; /* allow selection state to change */
69     }
70    
71     static void update_collisions(GtkListStore *store, tag_t *tags) {
72     GtkTreeIter iter;
73     tag_t *tag = NULL;
74    
75     /* walk the entire store to get all values */
76     if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
77     gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, TAG_COL_DATA, &tag, -1);
78     g_assert(tag);
79     gtk_list_store_set(store, &iter,
80     TAG_COL_COLLISION, info_tag_key_collision(tags, tag), -1);
81    
82     while(gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)) {
83     gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
84     TAG_COL_DATA, &tag, -1);
85     g_assert(tag);
86     gtk_list_store_set(store, &iter,
87     TAG_COL_COLLISION, info_tag_key_collision(tags, tag), -1);
88     }
89     }
90     }
91    
92     static void on_tag_remove(GtkWidget *but, tag_context_t *context) {
93     GtkTreeSelection *selection;
94     GtkTreeModel *model;
95     GtkTreeIter iter;
96    
97     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view));
98     if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
99     tag_t *tag;
100     gtk_tree_model_get(model, &iter, TAG_COL_DATA, &tag, -1);
101    
102     g_assert(tag);
103    
104     /* de-chain */
105     printf("de-chaining tag %s/%s\n", tag->key, tag->value);
106     tag_t **prev = context->tag;
107     while(*prev != tag) prev = &((*prev)->next);
108     *prev = tag->next;
109    
110     /* free tag itself */
111     osm_tag_free(tag);
112    
113     /* and remove from store */
114     gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
115    
116     update_collisions(context->store, *context->tag);
117     }
118    
119     /* disable remove and edit buttons */
120     gtk_widget_set_sensitive(context->but_remove, FALSE);
121     gtk_widget_set_sensitive(context->but_edit, FALSE);
122     }
123    
124     static gboolean tag_edit(tag_context_t *context) {
125    
126     GtkTreeModel *model;
127     GtkTreeIter iter;
128     tag_t *tag;
129    
130     GtkTreeSelection *sel = gtk_tree_view_get_selection(
131     GTK_TREE_VIEW(context->view));
132     if(!sel) {
133     printf("got no selection object\n");
134     return FALSE;
135     }
136    
137     if(!gtk_tree_selection_get_selected(sel, &model, &iter)) {
138     printf("nothing selected\n");
139     return FALSE;
140     }
141    
142     gtk_tree_model_get(model, &iter, TAG_COL_DATA, &tag, -1);
143     printf("got %s/%s\n", tag->key, tag->value);
144    
145     GtkWidget *dialog = gtk_dialog_new_with_buttons(_("Edit Tag"),
146     GTK_WINDOW(context->dialog), GTK_DIALOG_MODAL,
147     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
148     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
149     NULL);
150    
151 harbaum 42 #ifdef USE_HILDON
152     gtk_window_set_default_size(GTK_WINDOW(dialog), 500, 100);
153     #else
154     gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 100);
155     #endif
156    
157 harbaum 1 gtk_dialog_set_default_response(GTK_DIALOG(dialog),
158     GTK_RESPONSE_ACCEPT);
159    
160     GtkWidget *label, *key, *value;
161     GtkWidget *table = gtk_table_new(2, 2, FALSE);
162    
163 harbaum 42 gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Key:")),
164     0, 1, 0, 1, 0, 0, 0, 0);
165 harbaum 1 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
166     gtk_table_attach_defaults(GTK_TABLE(table),
167 harbaum 42 key = gtk_entry_new(), 1, 2, 0, 1);
168 harbaum 1 gtk_entry_set_activates_default(GTK_ENTRY(key), TRUE);
169     HILDON_ENTRY_NO_AUTOCAP(key);
170    
171 harbaum 42 gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Value:")),
172     0, 1, 1, 2, 0, 0, 0, 0);
173 harbaum 1 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
174     gtk_table_attach_defaults(GTK_TABLE(table),
175     value = gtk_entry_new(), 1, 2, 1, 2);
176     gtk_entry_set_activates_default(GTK_ENTRY(value), TRUE);
177     HILDON_ENTRY_NO_AUTOCAP(value);
178    
179     gtk_entry_set_text(GTK_ENTRY(key), tag->key);
180     gtk_entry_set_text(GTK_ENTRY(value), tag->value);
181    
182     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table);
183    
184     gtk_widget_show_all(dialog);
185    
186     if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) {
187     free(tag->key); free(tag->value);
188     tag->key = strdup((char*)gtk_entry_get_text(GTK_ENTRY(key)));
189     tag->value = strdup((char*)gtk_entry_get_text(GTK_ENTRY(value)));
190     printf("setting %s/%s\n", tag->key, tag->value);
191    
192     gtk_list_store_set(context->store, &iter,
193     TAG_COL_KEY, tag->key,
194     TAG_COL_VALUE, tag->value,
195     -1);
196    
197     gtk_widget_destroy(dialog);
198    
199     /* update collisions for all entries */
200     update_collisions(context->store, *context->tag);
201     return TRUE;
202     }
203    
204     gtk_widget_destroy(dialog);
205     return FALSE;
206     }
207    
208     static void on_tag_edit(GtkWidget *button, tag_context_t *context) {
209     tag_edit(context);
210     }
211    
212     static void on_tag_last(GtkWidget *button, tag_context_t *context) {
213     static const char *type_name[] = { "illegal", "node", "way", "relation" };
214    
215     if(yes_no_f(context->dialog,
216     context->appdata, MISC_AGAIN_ID_OVERWRITE_TAGS, 0,
217     _("Overwrite tags?"),
218     _("This will overwrite all tags of this %s with the "
219     "ones from the %s selected last.\n\n"
220     "Do you really want this?"),
221     type_name[context->type], type_name[context->type])) {
222    
223     osm_tags_free(*context->tag);
224    
225     if(context->type == NODE)
226     *context->tag = osm_tags_copy(context->appdata->map->last_node_tags, TRUE);
227     else
228     *context->tag = osm_tags_copy(context->appdata->map->last_way_tags, TRUE);
229    
230     info_tags_replace(context);
231     }
232     }
233    
234     static void on_tag_add(GtkWidget *button, tag_context_t *context) {
235     /* search end of tag chain */
236     tag_t **tag = context->tag;
237     while(*tag)
238     tag = &(*tag)->next;
239    
240     /* create and append a new tag */
241     *tag = g_new0(tag_t, 1);
242     if(!*tag) {
243     errorf(GTK_WIDGET(context->appdata->window), _("Out of memory"));
244     return;
245     }
246    
247     /* fill with some empty strings */
248     (*tag)->key = strdup("");
249     (*tag)->value = strdup("");
250    
251     /* append a row for the new data */
252     GtkTreeIter iter;
253     gtk_list_store_append(context->store, &iter);
254     gtk_list_store_set(context->store, &iter,
255     TAG_COL_KEY, (*tag)->key,
256     TAG_COL_VALUE, (*tag)->value,
257     TAG_COL_COLLISION, FALSE,
258     TAG_COL_DATA, *tag,
259     -1);
260    
261     gtk_tree_selection_select_iter(gtk_tree_view_get_selection(
262     GTK_TREE_VIEW(context->view)), &iter);
263    
264     if(!tag_edit(context)) {
265     printf("cancelled\n");
266     on_tag_remove(NULL, context);
267     }
268     }
269    
270     void info_tags_replace(tag_context_t *context) {
271     gtk_list_store_clear(context->store);
272    
273     GtkTreeIter iter;
274     tag_t *tag = *context->tag;
275     while(tag) {
276     gtk_list_store_append(context->store, &iter);
277     gtk_list_store_set(context->store, &iter,
278     TAG_COL_KEY, tag->key,
279     TAG_COL_VALUE, tag->value,
280     TAG_COL_COLLISION, info_tag_key_collision(*context->tag, tag),
281     TAG_COL_DATA, tag,
282     -1);
283     tag = tag->next;
284     }
285     }
286    
287     static GtkWidget *tag_widget(tag_context_t *context) {
288     GtkWidget *vbox = gtk_vbox_new(FALSE,3);
289     context->view = gtk_tree_view_new();
290    
291     gtk_tree_selection_set_select_function(
292     gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view)),
293     view_selection_func,
294     context, NULL);
295    
296     /* --- "Key" column --- */
297     GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
298     g_object_set(renderer,
299     "ellipsize", PANGO_ELLIPSIZE_END,
300     "background", "red",
301     NULL );
302     GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
303     _("Key"), renderer,
304     "text", TAG_COL_KEY,
305     "background-set", TAG_COL_COLLISION,
306     NULL);
307     gtk_tree_view_column_set_expand(column, TRUE);
308     gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
309    
310     /* --- "Value" column --- */
311     renderer = gtk_cell_renderer_text_new();
312     g_object_set(renderer,
313     "ellipsize", PANGO_ELLIPSIZE_END,
314     NULL );
315     column = gtk_tree_view_column_new_with_attributes(
316     _("Value"), renderer,
317     "text", TAG_COL_VALUE,
318     NULL);
319     gtk_tree_view_column_set_expand(column, TRUE);
320     gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
321    
322     /* build and fill the store */
323     context->store = gtk_list_store_new(TAG_NUM_COLS,
324     G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER);
325    
326     gtk_tree_view_set_model(GTK_TREE_VIEW(context->view),
327     GTK_TREE_MODEL(context->store));
328    
329     GtkTreeIter iter;
330     tag_t *tag = *context->tag;
331     while(tag) {
332     /* Append a row and fill in some data */
333     gtk_list_store_append(context->store, &iter);
334     gtk_list_store_set(context->store, &iter,
335     TAG_COL_KEY, tag->key,
336     TAG_COL_VALUE, tag->value,
337     TAG_COL_COLLISION, info_tag_key_collision(*context->tag, tag),
338     TAG_COL_DATA, tag,
339     -1);
340     tag = tag->next;
341     }
342    
343     g_object_unref(context->store);
344    
345     /* put it into a scrolled window */
346     GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
347     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
348     GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
349     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
350     GTK_SHADOW_ETCHED_IN);
351     // gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 3);
352     gtk_container_add(GTK_CONTAINER(scrolled_window), context->view);
353     gtk_box_pack_start_defaults(GTK_BOX(vbox), scrolled_window);
354    
355     /* ------- button box ------------ */
356    
357     GtkWidget *hbox = gtk_hbox_new(TRUE,3);
358    
359     GtkWidget *but_last = gtk_button_new_with_label(_("Last..."));
360     gtk_box_pack_start_defaults(GTK_BOX(hbox), but_last);
361    
362     /* disable if no appropriate "last" tags have been stored or if the */
363     /* selected item isn't a node or way */
364     if(((context->type == NODE) &&
365     (!context->appdata->map->last_node_tags)) ||
366     ((context->type == WAY) &&
367     (!context->appdata->map->last_way_tags)) ||
368     ((context->type != NODE) && (context->type != WAY)))
369     gtk_widget_set_sensitive(but_last, FALSE);
370    
371     gtk_signal_connect(GTK_OBJECT(but_last), "clicked",
372     GTK_SIGNAL_FUNC(on_tag_last), context);
373    
374     GtkWidget *presets = josm_presets_select(context->appdata, context);
375     if(presets) gtk_box_pack_start_defaults(GTK_BOX(hbox), presets);
376    
377     context->but_add = gtk_button_new_with_label(_("Add..."));
378     gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_add);
379     gtk_signal_connect(GTK_OBJECT(context->but_add), "clicked",
380     GTK_SIGNAL_FUNC(on_tag_add), context);
381    
382     context->but_edit = gtk_button_new_with_label(_("Edit..."));
383     gtk_widget_set_sensitive(context->but_edit, FALSE);
384     gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_edit);
385     gtk_signal_connect(GTK_OBJECT(context->but_edit), "clicked",
386     GTK_SIGNAL_FUNC(on_tag_edit), context);
387    
388     context->but_remove = gtk_button_new_with_label(_("Remove"));
389     gtk_widget_set_sensitive(context->but_remove, FALSE);
390     gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_remove);
391     gtk_signal_connect(GTK_OBJECT(context->but_remove), "clicked",
392     GTK_SIGNAL_FUNC(on_tag_remove), context);
393    
394    
395     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
396     return vbox;
397     }
398    
399     /* edit tags of currently selected node or way or of the relation */
400     /* given */
401     void info_dialog(GtkWidget *parent, appdata_t *appdata, relation_t *relation) {
402     if(!relation)
403     g_assert(appdata->map->selected.type != MAP_TYPE_ILLEGAL);
404    
405     tag_context_t *context = g_new0(tag_context_t, 1);
406     user_t *user = NULL;
407     char *str = NULL;
408     time_t stime = 0;
409     tag_t *work_copy = NULL;
410    
411     context->appdata = appdata;
412     context->tag = &work_copy;
413    
414     if(!relation) {
415     switch(appdata->map->selected.type) {
416     case MAP_TYPE_NODE:
417     str = g_strdup_printf(_("Node #%ld"), appdata->map->selected.node->id);
418     user = appdata->map->selected.node->user;
419     work_copy = osm_tags_copy(appdata->map->selected.node->tag, FALSE);
420     stime = appdata->map->selected.node->time;
421     context->type = NODE;
422     break;
423     case MAP_TYPE_WAY:
424     str = g_strdup_printf(_("Way #%ld"), appdata->map->selected.way->id);
425     user = appdata->map->selected.way->user;
426     work_copy = osm_tags_copy(appdata->map->selected.way->tag, FALSE);
427     stime = appdata->map->selected.way->time;
428     context->type = WAY;
429     break;
430     default:
431     g_assert((appdata->map->selected.type == MAP_TYPE_NODE) ||
432     (appdata->map->selected.type == MAP_TYPE_WAY));
433     break;
434     }
435     } else {
436     str = g_strdup_printf(_("Relation #%ld"), relation->id);
437     user = relation->user;
438     work_copy = osm_tags_copy(relation->tag, FALSE);
439     stime = relation->time;
440     context->type = RELATION;
441     }
442    
443     context->dialog = gtk_dialog_new_with_buttons(str,
444     GTK_WINDOW(parent), GTK_DIALOG_MODAL,
445     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
446     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
447     NULL);
448     g_free(str);
449    
450     gtk_dialog_set_default_response(GTK_DIALOG(context->dialog),
451     GTK_RESPONSE_ACCEPT);
452    
453     /* making the dialog a little wider makes it less "crowded" */
454     #ifdef USE_HILDON
455     gtk_window_set_default_size(GTK_WINDOW(context->dialog), 500, 300);
456     #else
457     gtk_window_set_default_size(GTK_WINDOW(context->dialog), 400, 200);
458     #endif
459    
460     GtkWidget *label;
461     GtkWidget *table = gtk_table_new(2, 2, FALSE); // x, y
462    
463     /* ------------ user ----------------- */
464     char *u_str = NULL;
465     if(user) u_str = g_strdup_printf(_("User: %s"), user->name);
466     else u_str = g_strdup_printf(_("User: ---"));
467     label = gtk_label_new(u_str);
468     gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 0, 1);
469     g_free(u_str);
470    
471     /* ------------ time ----------------- */
472    
473     struct tm *loctime = localtime(&stime);
474     char time_str[32];
475     strftime(time_str, sizeof(time_str), "%x %X", loctime);
476     char *t_str = g_strdup_printf(_("Time: %s"), time_str);
477     label = gtk_label_new(t_str);
478     g_free(t_str);
479     gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 0, 1);
480    
481     /* ------------ coordinate (only for nodes) ----------------- */
482     if(!relation) {
483     if(appdata->map->selected.type == MAP_TYPE_NODE) {
484     char pos_str[32];
485     pos_lat_str(pos_str, sizeof(pos_str),appdata->map->selected.node->pos.lat);
486     label = gtk_label_new(pos_str);
487     gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
488     pos_lat_str(pos_str, sizeof(pos_str),appdata->map->selected.node->pos.lon);
489     label = gtk_label_new(pos_str);
490     gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 1, 2);
491     } else {
492     char *nodes_str = g_strdup_printf(_("Length: %u nodes"),
493     osm_way_number_of_nodes(appdata->map->selected.way));
494     label = gtk_label_new(nodes_str);
495     gtk_table_attach_defaults(GTK_TABLE(table), label, 0, 1, 1, 2);
496     g_free(nodes_str);
497    
498     char *type_str = g_strdup_printf("%s (%s)",
499     (osm_way_get_last_node(appdata->map->selected.way) ==
500     osm_way_get_first_node(appdata->map->selected.way))?
501     "closed way":"open way",
502     (appdata->map->selected.way->draw.flags & OSM_DRAW_FLAG_AREA)?
503     "area":"line");
504    
505     label = gtk_label_new(type_str);
506     gtk_table_attach_defaults(GTK_TABLE(table), label, 1, 2, 1, 2);
507     g_free(type_str);
508     }
509     } else {
510     /* relations tell something about their members */
511     gint nodes = 0, ways = 0, relations = 0;
512     member_t *member = relation->member;
513     while(member) {
514     switch(member->type) {
515     case NODE:
516     case NODE_ID:
517     nodes++;
518     break;
519     case WAY:
520     case WAY_ID:
521     ways++;
522     break;
523     case RELATION:
524     case RELATION_ID:
525     relations++;
526     break;
527    
528     default:
529     break;
530     }
531    
532     member = member->next;
533     }
534    
535     char *str = g_strdup_printf(_("Members: %d nodes, %d ways, %d relations"),
536     nodes, ways, relations);
537    
538     gtk_table_attach_defaults(GTK_TABLE(table), gtk_label_new(str), 0, 2, 1, 2);
539     g_free(str);
540     }
541    
542     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox), table,
543     FALSE, FALSE, 0);
544    
545    
546     /* ------------ tags ----------------- */
547    
548     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
549     tag_widget(context), TRUE, TRUE, 0);
550    
551     /* ----------------------------------- */
552    
553     gtk_widget_show_all(context->dialog);
554     if(gtk_dialog_run(GTK_DIALOG(context->dialog)) == GTK_RESPONSE_ACCEPT) {
555     gtk_widget_destroy(context->dialog);
556    
557     if(!relation) {
558     /* replace original tags with work copy */
559     switch(appdata->map->selected.type) {
560    
561     case MAP_TYPE_NODE:
562     osm_tags_free(appdata->map->selected.node->tag);
563     appdata->map->selected.node->tag = osm_tags_copy(work_copy, TRUE);
564     break;
565    
566     case MAP_TYPE_WAY:
567     osm_tags_free(appdata->map->selected.way->tag);
568     appdata->map->selected.way->tag = osm_tags_copy(work_copy, TRUE);
569     break;
570    
571     default:
572     break;
573     }
574    
575     /* since nodes being parts of ways but with no tags are invisible, */
576     /* the result of editing them may have changed their visibility */
577     map_item_redraw(appdata, &appdata->map->selected);
578     map_item_set_flags(&context->appdata->map->selected, OSM_FLAG_DIRTY, 0);
579     } else {
580     osm_tags_free(relation->tag);
581     relation->tag = osm_tags_copy(work_copy, TRUE);
582     relation->flags |= OSM_FLAG_DIRTY;
583     }
584     } else {
585     gtk_widget_destroy(context->dialog);
586     osm_tags_free(work_copy);
587     }
588    
589     g_free(context);
590     }