Contents of /trunk/src/info.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 315 - (show annotations)
Wed Dec 16 20:07:58 2009 UTC (14 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 18025 byte(s)
Various fremantleization
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 /* you just cannot delete or edit the "created_by" tag */
54 if(!tag || 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 }
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 GtkTreeSelection *selection = list_get_selection(context->list);
92 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 list_button_enable(context->list, LIST_BUTTON_REMOVE, FALSE);
115 list_button_enable(context->list, LIST_BUTTON_EDIT, FALSE);
116 }
117
118 static gboolean tag_edit(tag_context_t *context) {
119
120 GtkTreeModel *model;
121 GtkTreeIter iter;
122 tag_t *tag;
123
124 GtkTreeSelection *sel = list_get_selection(context->list);
125 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 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
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 gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Key:")),
151 0, 1, 0, 1, 0, 0, 0, 0);
152 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
153 gtk_table_attach_defaults(GTK_TABLE(table),
154 key = entry_new(), 1, 2, 0, 1);
155 gtk_entry_set_activates_default(GTK_ENTRY(key), TRUE);
156 HILDON_ENTRY_NO_AUTOCAP(key);
157
158 gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Value:")),
159 0, 1, 1, 2, 0, 0, 0, 0);
160 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
161 gtk_table_attach_defaults(GTK_TABLE(table),
162 value = 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 type_name[context->object.type], type_name[context->object.type])) {
209
210 osm_tags_free(*context->tag);
211
212 if(context->object.type == NODE)
213 *context->tag = osm_tags_copy(context->appdata->map->last_node_tags);
214 else
215 *context->tag = osm_tags_copy(context->appdata->map->last_way_tags);
216
217 info_tags_replace(context);
218 }
219 }
220
221 static void on_tag_add(GtkWidget *button, tag_context_t *context) {
222
223 /* search end of tag chain */
224 tag_t **tag = context->tag;
225 while(*tag)
226 tag = &(*tag)->next;
227
228 /* create and append a new tag */
229 *tag = g_new0(tag_t, 1);
230 if(!*tag) {
231 errorf(GTK_WIDGET(context->appdata->window), _("Out of memory"));
232 return;
233 }
234
235 /* fill with some empty strings */
236 (*tag)->key = strdup("");
237 (*tag)->value = strdup("");
238
239 /* append a row for the new data */
240 GtkTreeIter iter;
241 gtk_list_store_append(context->store, &iter);
242 gtk_list_store_set(context->store, &iter,
243 TAG_COL_KEY, (*tag)->key,
244 TAG_COL_VALUE, (*tag)->value,
245 TAG_COL_COLLISION, FALSE,
246 TAG_COL_DATA, *tag,
247 -1);
248
249 gtk_tree_selection_select_iter(
250 list_get_selection(context->list), &iter);
251
252 if(!tag_edit(context)) {
253 printf("cancelled\n");
254 on_tag_remove(NULL, context);
255 }
256 }
257
258 void info_tags_replace(tag_context_t *context) {
259 gtk_list_store_clear(context->store);
260
261 GtkTreeIter iter;
262 tag_t *tag = *context->tag;
263 while(tag) {
264 gtk_list_store_append(context->store, &iter);
265 gtk_list_store_set(context->store, &iter,
266 TAG_COL_KEY, tag->key,
267 TAG_COL_VALUE, tag->value,
268 TAG_COL_COLLISION, info_tag_key_collision(*context->tag, tag),
269 TAG_COL_DATA, tag,
270 -1);
271 tag = tag->next;
272 }
273 }
274
275 static void on_relations(GtkWidget *button, tag_context_t *context) {
276 relation_add_dialog(context->dialog, context->appdata, &context->object);
277 }
278
279 static GtkWidget *tag_widget(tag_context_t *context) {
280 context->list = list_new(LIST_HILDON_WITHOUT_HEADERS);
281
282 list_set_static_buttons(context->list, 0, G_CALLBACK(on_tag_add),
283 G_CALLBACK(on_tag_edit), G_CALLBACK(on_tag_remove), context);
284
285 list_set_selection_function(context->list, view_selection_func, context);
286
287 list_set_user_buttons(context->list,
288 LIST_BUTTON_USER0, _("Last"), on_tag_last,
289 LIST_BUTTON_USER2, _("Relations"), on_relations,
290 0);
291
292 /* setup both columns */
293 list_set_columns(context->list,
294 _("Key"), TAG_COL_KEY,
295 LIST_FLAG_ELLIPSIZE|LIST_FLAG_CAN_HIGHLIGHT, TAG_COL_COLLISION,
296 _("Value"), TAG_COL_VALUE,
297 LIST_FLAG_ELLIPSIZE,
298 NULL);
299
300 GtkWidget *presets = josm_build_presets_button(context->appdata, context);
301 if(presets)
302 list_set_custom_user_button(context->list, LIST_BUTTON_USER1, presets);
303
304 /* disable if no appropriate "last" tags have been stored or if the */
305 /* selected item isn't a node or way */
306 if(((context->object.type == NODE) &&
307 (!context->appdata->map->last_node_tags)) ||
308 ((context->object.type == WAY) &&
309 (!context->appdata->map->last_way_tags)) ||
310 ((context->object.type != NODE) && (context->object.type != WAY)))
311 list_button_enable(context->list, LIST_BUTTON_USER0, FALSE);
312
313 /* --------- build and fill the store ------------ */
314 context->store = gtk_list_store_new(TAG_NUM_COLS,
315 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_POINTER);
316
317 list_set_store(context->list, context->store);
318
319 GtkTreeIter iter;
320
321 tag_t *tag = *context->tag;
322 while(tag) {
323 /* Append a row and fill in some data */
324 gtk_list_store_append(context->store, &iter);
325 gtk_list_store_set(context->store, &iter,
326 TAG_COL_KEY, tag->key,
327 TAG_COL_VALUE, tag->value,
328 TAG_COL_COLLISION, info_tag_key_collision(*context->tag, tag),
329 TAG_COL_DATA, tag,
330 -1);
331 tag = tag->next;
332 }
333
334 g_object_unref(context->store);
335
336 return context->list;
337 }
338
339 static void on_relation_members(GtkWidget *but, tag_context_t *context) {
340 g_assert(context->object.type == RELATION);
341 relation_show_members(context->dialog, context->object.relation);
342 }
343
344 static void table_attach(GtkWidget *table, GtkWidget *child, int x, int y) {
345 gtk_table_attach_defaults(GTK_TABLE(table), child, x, x+1, y, y+1);
346 }
347
348 static GtkWidget *details_widget(tag_context_t *context, gboolean big) {
349 GtkWidget *table = gtk_table_new(big?4:2, 2, FALSE); // y, x
350
351 user_t *user = OBJECT_USER(context->object);
352 time_t stime = OBJECT_TIME(context->object);
353 GtkWidget *label;
354
355 /* ------------ user ----------------- */
356 if(user) {
357 if(big) table_attach(table, gtk_label_new(_("User:")), 0, 0);
358
359 label = gtk_label_new(user->name);
360 gtk_label_set_ellipsize(GTK_LABEL(label), PANGO_ELLIPSIZE_END);
361 table_attach(table, label, big?1:0, 0);
362 }
363
364 /* ------------ time ----------------- */
365
366 if(big) table_attach(table, gtk_label_new(_("Date/Time:")), 0, 1);
367 struct tm *loctime = localtime(&stime);
368 char time_str[32];
369 strftime(time_str, sizeof(time_str), "%x %X", loctime);
370 label = gtk_label_new(time_str);
371 table_attach(table, label, 1, big?1:0);
372
373 /* ------------ coordinate (only for nodes) ----------------- */
374 switch(context->object.type) {
375 case NODE: {
376 char pos_str[32];
377 pos_lat_str(pos_str, sizeof(pos_str), context->object.node->pos.lat);
378 label = gtk_label_new(pos_str);
379 if(big) table_attach(table, gtk_label_new(_("Latitude:")), 0, 2);
380 table_attach(table, label, big?1:0, big?2:1);
381 pos_lat_str(pos_str, sizeof(pos_str), context->object.node->pos.lon);
382 label = gtk_label_new(pos_str);
383 if(big) table_attach(table, gtk_label_new(_("Longitude:")), 0, 3);
384 table_attach(table, label, 1, big?3:1);
385 } break;
386
387 case WAY: {
388 char *nodes_str = g_strdup_printf(_("%s%u nodes"),
389 big?"":_("Length: "),
390 osm_way_number_of_nodes(context->object.way));
391 label = gtk_label_new(nodes_str);
392 if(big) table_attach(table, gtk_label_new(_("Length:")), 0, 2);
393 table_attach(table, label, big?1:0, big?2:1);
394 g_free(nodes_str);
395
396 char *type_str = g_strdup_printf("%s (%s)",
397 (osm_way_get_last_node(context->object.way) ==
398 osm_way_get_first_node(context->object.way))?
399 "closed way":"open way",
400 (context->object.way->draw.flags & OSM_DRAW_FLAG_AREA)?
401 "area":"line");
402
403 label = gtk_label_new(type_str);
404 if(big) table_attach(table, gtk_label_new(_("Type:")), 0, 3);
405 table_attach(table, label, 1, big?3:1);
406 g_free(type_str);
407 } break;
408
409 case RELATION: {
410 /* relations tell something about their members */
411 gint nodes = 0, ways = 0, relations = 0;
412 member_t *member = context->object.relation->member;
413 while(member) {
414 switch(member->object.type) {
415 case NODE:
416 case NODE_ID:
417 nodes++;
418 break;
419 case WAY:
420 case WAY_ID:
421 ways++;
422 break;
423 case RELATION:
424 case RELATION_ID:
425 relations++;
426 break;
427
428 default:
429 break;
430 }
431
432 member = member->next;
433 }
434
435 char *str =
436 g_strdup_printf(_("Members: %d nodes, %d ways, %d relations"),
437 nodes, ways, relations);
438
439 GtkWidget *member_btn = button_new_with_label(str);
440 gtk_signal_connect(GTK_OBJECT(member_btn), "clicked",
441 GTK_SIGNAL_FUNC(on_relation_members), context);
442
443 gtk_table_attach_defaults(GTK_TABLE(table), member_btn, 0, 2,
444 big?2:1, big?4:2);
445
446 g_free(str);
447 break;
448
449 default:
450 printf("ERROR: No node, way or relation\n");
451 g_assert(0);
452 break;
453 } }
454
455 return table;
456 }
457
458 #ifdef FREMANTLE
459 /* put additional infos into a seperate dialog for fremantle as */
460 /* screen space is sparse there */
461 static void info_more(tag_context_t *context) {
462 GtkWidget *dialog =
463 misc_dialog_new(MISC_DIALOG_SMALL, _("Object details"),
464 GTK_WINDOW(context->dialog),
465 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
466 NULL);
467
468 gtk_dialog_set_default_response(GTK_DIALOG(dialog),
469 GTK_RESPONSE_CANCEL);
470
471 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
472 details_widget(context, TRUE),
473 FALSE, FALSE, 0);
474 gtk_widget_show_all(dialog);
475 gtk_dialog_run(GTK_DIALOG(dialog));
476 gtk_widget_destroy(dialog);
477 }
478 #endif
479
480 /* edit tags of currently selected node or way or of the relation */
481 /* given */
482 gboolean info_dialog(GtkWidget *parent, appdata_t *appdata, object_t *object) {
483
484 tag_context_t *context = g_new0(tag_context_t, 1);
485 char *str = NULL;
486 tag_t *work_copy = NULL;
487
488 context->appdata = appdata;
489 context->tag = &work_copy;
490
491 /* use implicit selection if not explicitely given */
492 if(!object) {
493 g_assert((appdata->map->selected.object.type == NODE) ||
494 (appdata->map->selected.object.type == WAY) ||
495 (appdata->map->selected.object.type == RELATION));
496
497 context->object = appdata->map->selected.object;
498 } else
499 context->object = *object;
500
501 // str = osm_object_string(&context->object);
502 // str[0] = g_ascii_toupper (str[0]);
503
504 g_assert(osm_object_is_real(&context->object));
505
506 work_copy = osm_tags_copy(OBJECT_TAG(context->object));
507
508 switch(context->object.type) {
509 case NODE:
510 str = g_strdup_printf(_("Node #" ITEM_ID_FORMAT),
511 OBJECT_ID(context->object));
512 context->presets_type = PRESETS_TYPE_NODE;
513 break;
514
515 case WAY:
516 str = g_strdup_printf(_("Way #" ITEM_ID_FORMAT),
517 OBJECT_ID(context->object));
518 context->presets_type = PRESETS_TYPE_WAY;
519
520 if(osm_way_get_last_node(context->object.way) ==
521 osm_way_get_first_node(context->object.way))
522 context->presets_type |= PRESETS_TYPE_CLOSEDWAY;
523
524 break;
525
526 case RELATION:
527 str = g_strdup_printf(_("Relation #" ITEM_ID_FORMAT),
528 OBJECT_ID(context->object));
529 context->presets_type = PRESETS_TYPE_RELATION;
530 break;
531
532 default:
533 g_assert((context->object.type == NODE) ||
534 (context->object.type == WAY) ||
535 (context->object.type == RELATION));
536 break;
537 }
538
539 context->dialog = misc_dialog_new(MISC_DIALOG_LARGE, str,
540 GTK_WINDOW(parent),
541 #ifdef FREMANTLE
542 _("More"), GTK_RESPONSE_HELP,
543 #endif
544 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
545 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
546 NULL);
547 g_free(str);
548
549 gtk_dialog_set_default_response(GTK_DIALOG(context->dialog),
550 GTK_RESPONSE_ACCEPT);
551
552 #ifndef FREMANTLE
553 /* -------- details box --------- */
554 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
555 details_widget(context, FALSE),
556 FALSE, FALSE, 0);
557 #endif
558
559 /* ------------ tags ----------------- */
560
561 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
562 tag_widget(context), TRUE, TRUE, 0);
563
564 /* ----------------------------------- */
565
566 gtk_widget_show_all(context->dialog);
567 gboolean ok = FALSE, quit = FALSE;
568
569 do {
570 switch(gtk_dialog_run(GTK_DIALOG(context->dialog))) {
571 case GTK_RESPONSE_ACCEPT:
572 quit = TRUE;
573 ok = TRUE;
574 break;
575 #ifdef FREMANTLE
576 case GTK_RESPONSE_HELP:
577 info_more(context);
578 break;
579 #endif
580
581 default:
582 quit = TRUE;
583 break;
584 }
585
586 } while(!quit);
587
588 gtk_widget_destroy(context->dialog);
589
590 if(ok) {
591 if(osm_object_is_real(&context->object)) {
592 osm_tags_free(OBJECT_TAG(context->object));
593 OBJECT_TAG(context->object) = osm_tags_copy(work_copy);
594 }
595
596 /* since nodes being parts of ways but with no tags are invisible, */
597 /* the result of editing them may have changed their visibility */
598 if(!object && context->object.type != RELATION)
599 map_item_redraw(appdata, &appdata->map->selected);
600
601 osm_object_set_flags(&context->object, OSM_FLAG_DIRTY, 0);
602 } else
603 osm_tags_free(work_copy);
604
605 g_free(context);
606 return ok;
607 }