Contents of /trunk/src/info.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (show 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 /*
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 #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 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 gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Key:")),
164 0, 1, 0, 1, 0, 0, 0, 0);
165 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
166 gtk_table_attach_defaults(GTK_TABLE(table),
167 key = gtk_entry_new(), 1, 2, 0, 1);
168 gtk_entry_set_activates_default(GTK_ENTRY(key), TRUE);
169 HILDON_ENTRY_NO_AUTOCAP(key);
170
171 gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Value:")),
172 0, 1, 1, 2, 0, 0, 0, 0);
173 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 }