Contents of /trunk/src/info.c

Parent Directory Parent Directory | Revision Log Revision Log


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