Contents of /trunk/src/relation_edit.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: 30067 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 /* --------------- relation dialog for an item (node or way) ----------- */
23
24 typedef struct {
25 object_t *item;
26 appdata_t *appdata;
27 GtkWidget *dialog, *list;
28 GtkListStore *store;
29 } relitem_context_t;
30
31 enum {
32 RELITEM_COL_SELECTED = 0,
33 RELITEM_COL_TYPE,
34 RELITEM_COL_ROLE,
35 RELITEM_COL_NAME,
36 RELITEM_COL_DATA,
37 RELITEM_NUM_COLS
38 };
39
40 typedef struct role_chain_s {
41 char *role;
42 struct role_chain_s *next;
43 } role_chain_t;
44
45 static gboolean relation_add_item(GtkWidget *parent,
46 relation_t *relation, object_t *object) {
47 role_chain_t *chain = NULL, **chainP = &chain;
48
49 printf("add object of type %d to relation #" ITEM_ID_FORMAT "\n",
50 object->type, OSM_ID(relation));
51
52 /* ask the user for the role of the new object in this relation */
53
54 /* collect roles first */
55 member_t *member = relation->member;
56 while(member) {
57 if(member->role) {
58 /* check if this role has already been saved */
59 gboolean already_stored = FALSE;
60 role_chain_t *crole = chain;
61 while(crole) {
62 if(strcasecmp(crole->role, member->role) == 0) already_stored = TRUE;
63 crole = crole->next;
64 }
65
66 /* not stored yet: attach it */
67 if(!already_stored) {
68 *chainP = g_new0(role_chain_t, 1);
69 (*chainP)->role = g_strdup(member->role);
70 chainP = &(*chainP)->next;
71 }
72 }
73 member = member->next;
74 }
75
76 /* ------------------ role dialog ---------------- */
77 GtkWidget *dialog =
78 misc_dialog_new(MISC_DIALOG_NOSIZE,_("Select role"),
79 GTK_WINDOW(parent),
80 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
81 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
82 NULL);
83
84 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
85
86 char *type = osm_tag_get_by_key(OSM_TAG(relation), "type");
87
88 char *info_str = NULL;
89 if(type) info_str = g_strdup_printf(_("In relation of type: %s"), type);
90 else info_str = g_strdup_printf(_("In relation #" ITEM_ID_FORMAT),
91 OSM_ID(relation));
92 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
93 gtk_label_new(info_str));
94 g_free(info_str);
95
96 char *name = osm_tag_get_by_key(OSM_TAG(relation), "name");
97 if(name)
98 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
99 gtk_label_new(name));
100
101 GtkWidget *hbox = gtk_hbox_new(FALSE, 8);
102 gtk_box_pack_start_defaults(GTK_BOX(hbox), gtk_label_new(_("Role:")));
103
104 GtkWidget *entry = NULL;
105 if(chain) {
106 entry = gtk_combo_box_entry_new_text();
107
108 /* fill combo box with presets */
109 while(chain) {
110 role_chain_t *next = chain->next;
111 gtk_combo_box_append_text(GTK_COMBO_BOX(entry), chain->role);
112 g_free(chain);
113 chain = next;
114 }
115 } else
116 entry = entry_new();
117
118 gtk_box_pack_start_defaults(GTK_BOX(hbox), entry);
119 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
120
121 gtk_widget_show_all(dialog);
122 if(GTK_RESPONSE_ACCEPT != gtk_dialog_run(GTK_DIALOG(dialog))) {
123 printf("user clicked cancel\n");
124 gtk_widget_destroy(dialog);
125 return FALSE;
126 }
127
128 printf("user clicked ok\n");
129
130 /* get role from dialog */
131 char *ptr = NULL;
132
133 if(GTK_IS_COMBO_BOX(entry))
134 ptr = gtk_combo_box_get_active_text(GTK_COMBO_BOX(entry));
135 else
136 ptr = (char*)gtk_entry_get_text(GTK_ENTRY(entry));
137
138 char *role = NULL;
139 if(ptr && strlen(ptr)) role = g_strdup(ptr);
140
141 gtk_widget_destroy(dialog);
142
143 /* search end of member chain */
144 member_t **memberP = &relation->member;
145 while(*memberP) memberP = &(*memberP)->next;
146
147 g_assert((object->type == NODE)||(object->type == WAY)||
148 (object->type == RELATION));
149
150 /* create new member */
151 *memberP = g_new0(member_t, 1);
152 (*memberP)->object = *object;
153 (*memberP)->role = role;
154
155 OSM_FLAGS(relation) |= OSM_FLAG_DIRTY;
156 return TRUE;
157 }
158
159 static void relation_remove_item(relation_t *relation, object_t *object) {
160
161 printf("remove object of type %d from relation #" ITEM_ID_FORMAT "\n",
162 object->type, OSM_ID(relation));
163
164 member_t **member = &relation->member;
165 while(*member) {
166 if(((*member)->object.type == object->type) &&
167 (((object->type == NODE) &&
168 (object->node == (*member)->object.node)) ||
169 ((object->type == WAY) &&
170 (object->way == (*member)->object.way)) ||
171 ((object->type == RELATION) &&
172 (object->relation == (*member)->object.relation)))) {
173
174 member_t *next = (*member)->next;
175 osm_member_free(*member);
176 *member = next;
177
178 OSM_FLAGS(relation) |= OSM_FLAG_DIRTY;
179
180 return;
181 } else
182 member = &(*member)->next;
183 }
184 g_assert(0);
185 }
186
187 static void relation_item_list_selected(relitem_context_t *context,
188 gboolean selected) {
189
190 list_button_enable(context->list, LIST_BUTTON_REMOVE, selected);
191 list_button_enable(context->list, LIST_BUTTON_EDIT, selected);
192 }
193
194 /* try to find something descriptive */
195 static char *relation_get_descriptive_name(relation_t *relation) {
196 char *name = osm_tag_get_by_key(OSM_TAG(relation), "ref");
197 if (!name)
198 name = osm_tag_get_by_key(OSM_TAG(relation), "name");
199 if (!name)
200 name = osm_tag_get_by_key(OSM_TAG(relation), "note");
201 if (!name)
202 name = osm_tag_get_by_key(OSM_TAG(relation), "fix" "me");
203 return name;
204 }
205
206 static gboolean relation_info_dialog(GtkWidget *parent, appdata_t *appdata,
207 relation_t *relation) {
208
209 object_t object = { .type = RELATION };
210 object.relation = relation;
211 return info_dialog(parent, appdata, &object);
212 }
213
214 static void on_relation_item_add(GtkWidget *but, relitem_context_t *context) {
215 /* create a new relation */
216
217 relation_t *relation = osm_relation_new();
218 if(!relation_info_dialog(context->dialog, context->appdata, relation)) {
219 printf("tag edit cancelled\n");
220 osm_relation_free(relation);
221 } else {
222 osm_relation_attach(context->appdata->osm, relation);
223
224 /* add to list */
225
226 /* append a row for the new data */
227 char *name = relation_get_descriptive_name(relation);
228
229 GtkTreeIter iter;
230 gtk_list_store_append(context->store, &iter);
231 gtk_list_store_set(context->store, &iter,
232 RELITEM_COL_SELECTED, FALSE,
233 RELITEM_COL_TYPE,
234 osm_tag_get_by_key(OSM_TAG(relation), "type"),
235 RELITEM_COL_NAME, name,
236 RELITEM_COL_DATA, relation,
237 -1);
238
239 gtk_tree_selection_select_iter(list_get_selection(context->list), &iter);
240 }
241 }
242
243 static relation_t *get_selection(relitem_context_t *context) {
244 GtkTreeSelection *selection;
245 GtkTreeModel *model;
246 GtkTreeIter iter;
247
248 selection = list_get_selection(context->list);
249 if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
250 relation_t *relation;
251 gtk_tree_model_get(model, &iter, RELITEM_COL_DATA, &relation, -1);
252 return(relation);
253 }
254 return NULL;
255 }
256
257 static void on_relation_item_edit(GtkWidget *but, relitem_context_t *context) {
258 relation_t *sel = get_selection(context);
259 if(!sel) return;
260
261 printf("edit relation item #" ITEM_ID_FORMAT "\n", OSM_ID(sel));
262
263 if (!relation_info_dialog(context->dialog, context->appdata, sel))
264 return;
265
266 // Locate the changed item
267 GtkTreeIter iter;
268 gboolean valid = gtk_tree_model_get_iter_first(
269 GTK_TREE_MODEL(context->store), &iter);
270 while (valid) {
271 relation_t *row_rel;
272 gtk_tree_model_get(GTK_TREE_MODEL(context->store), &iter,
273 RELITEM_COL_DATA, &row_rel,
274 -1);
275 if (row_rel == sel)
276 break;
277 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(context->store), &iter);
278 }
279 if (!valid)
280 return;
281
282 // Found it. Update all visible fields that belong to the relation iself.
283 gtk_list_store_set(context->store, &iter,
284 RELITEM_COL_TYPE, osm_tag_get_by_key(OSM_TAG(sel), "type"),
285 RELITEM_COL_NAME, relation_get_descriptive_name(sel),
286 -1);
287
288 // Order will probably have changed, so refocus
289 list_focus_on(context->list, &iter, TRUE);
290 }
291
292 /* remove the selected relation */
293 static void on_relation_item_remove(GtkWidget *but, relitem_context_t *context) {
294 relation_t *sel = get_selection(context);
295 if(!sel) return;
296
297 printf("remove relation #" ITEM_ID_FORMAT "\n", OSM_ID(sel));
298
299 gint members = osm_relation_members_num(sel);
300
301 if(members)
302 if(!yes_no_f(context->dialog, NULL, 0, 0,
303 _("Delete non-empty relation?"),
304 _("This relation still has %d members. "
305 "Delete it anyway?"), members))
306 return;
307
308 /* first remove selected row from list */
309 GtkTreeIter iter;
310 GtkTreeSelection *selection = list_get_selection(context->list);
311 if(gtk_tree_selection_get_selected(selection, NULL, &iter))
312 gtk_list_store_remove(context->store, &iter);
313
314 /* then really delete it */
315 osm_relation_delete(context->appdata->osm, sel, FALSE);
316
317 relation_item_list_selected(context, FALSE);
318 }
319
320 static char *relitem_get_role_in_relation(object_t *item, relation_t *relation) {
321 member_t *member = relation->member;
322 while(member) {
323 switch(member->object.type) {
324
325 case NODE:
326 if((item->type == NODE) && (item->node == member->object.node))
327 return member->role;
328 break;
329
330 case WAY:
331 if((item->type == WAY) && (item->way == member->object.way))
332 return member->role;
333 break;
334
335 default:
336 break;
337 }
338 member = member->next;
339 }
340 return NULL;
341 }
342
343 static void
344 relitem_toggled(GtkCellRendererToggle *cell, const gchar *path_str,
345 relitem_context_t *context) {
346 GtkTreePath *path;
347 GtkTreeIter iter;
348
349 path = gtk_tree_path_new_from_string(path_str);
350 gtk_tree_model_get_iter(GTK_TREE_MODEL(context->store), &iter, path);
351 gtk_tree_path_free(path);
352
353 /* get current enabled flag */
354 gboolean enabled;
355 relation_t *relation = NULL;
356 gtk_tree_model_get(GTK_TREE_MODEL(context->store), &iter,
357 RELITEM_COL_SELECTED, &enabled,
358 RELITEM_COL_DATA, &relation,
359 -1);
360
361 list_pre_inplace_edit_tweak(GTK_TREE_MODEL(context->store));
362
363 if(!enabled) {
364 printf("will now become be part of this relation\n");
365 if(relation_add_item(context->dialog, relation, context->item))
366 gtk_list_store_set(context->store, &iter,
367 RELITEM_COL_SELECTED, TRUE,
368 RELITEM_COL_ROLE,
369 relitem_get_role_in_relation(context->item, relation),
370 -1);
371 } else {
372 printf("item will not be part of this relation anymore\n");
373 relation_remove_item(relation, context->item);
374 gtk_list_store_set(context->store, &iter,
375 RELITEM_COL_SELECTED, FALSE,
376 RELITEM_COL_ROLE, NULL,
377 -1);
378 }
379
380 }
381
382 static gboolean relitem_is_in_relation(object_t *item, relation_t *relation) {
383 member_t *member = relation->member;
384 while(member) {
385 switch(member->object.type) {
386
387 case NODE:
388 if((item->type == NODE) && (item->node == member->object.node))
389 return TRUE;
390 break;
391
392 case WAY:
393 if((item->type == WAY) && (item->way == member->object.way))
394 return TRUE;
395 break;
396
397 default:
398 break;
399 }
400 member = member->next;
401 }
402 return FALSE;
403 }
404
405 static GtkWidget *relation_item_list_widget(relitem_context_t *context) {
406 context->list = list_new(LIST_HILDON_WITH_HEADERS);
407
408 list_set_columns(context->list,
409 _(""), RELITEM_COL_SELECTED, LIST_FLAG_TOGGLE,
410 G_CALLBACK(relitem_toggled), context,
411 _("Type"), RELITEM_COL_TYPE, 0,
412 _("Role"), RELITEM_COL_ROLE, 0,
413 _("Name"), RELITEM_COL_NAME, LIST_FLAG_ELLIPSIZE,
414 NULL);
415
416 /* build and fill the store */
417 context->store = gtk_list_store_new(RELITEM_NUM_COLS,
418 G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING,
419 G_TYPE_STRING, G_TYPE_POINTER);
420
421 list_set_store(context->list, context->store);
422
423 // Debatable whether to sort by the "selected" or the "Name" column by
424 // default. Both are be useful, in different ways.
425 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(context->store),
426 RELITEM_COL_NAME, GTK_SORT_ASCENDING);
427
428 GtkTreeIter iter;
429 relation_t *relation = context->appdata->osm->relation;
430 while(relation) {
431 /* try to find something descriptive */
432 char *name = relation_get_descriptive_name(relation);
433
434 /* Append a row and fill in some data */
435 gtk_list_store_append(context->store, &iter);
436 gtk_list_store_set(context->store, &iter,
437 RELITEM_COL_SELECTED, relitem_is_in_relation(context->item, relation),
438 RELITEM_COL_TYPE, osm_tag_get_by_key(OSM_TAG(relation), "type"),
439 RELITEM_COL_ROLE, relitem_get_role_in_relation(context->item, relation),
440 RELITEM_COL_NAME, name,
441 RELITEM_COL_DATA, relation,
442 -1);
443
444 relation = relation->next;
445 }
446
447 g_object_unref(context->store);
448
449 list_set_static_buttons(context->list, 0,
450 G_CALLBACK(on_relation_item_add), G_CALLBACK(on_relation_item_edit),
451 G_CALLBACK(on_relation_item_remove), context);
452
453 relation_item_list_selected(context, FALSE);
454
455 return context->list;
456 }
457
458 void relation_add_dialog(GtkWidget *parent,
459 appdata_t *appdata, object_t *object) {
460 relitem_context_t *context = g_new0(relitem_context_t, 1);
461 map_t *map = appdata->map;
462 g_assert(map);
463
464 context->appdata = appdata;
465 context->item = object;
466
467 char *str = NULL;
468 switch(object->type) {
469 case NODE:
470 str = g_strdup_printf(_("Relations for node #" ITEM_ID_FORMAT),
471 OBJECT_ID(*object));
472 break;
473 case WAY:
474 str = g_strdup_printf(_("Relations for way #" ITEM_ID_FORMAT),
475 OBJECT_ID(*object));
476 break;
477 default:
478 g_assert((object->type == NODE) || (object->type == WAY));
479 break;
480 }
481
482 context->dialog =
483 misc_dialog_new(MISC_DIALOG_LARGE, str,
484 GTK_WINDOW(parent),
485 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
486 NULL);
487 g_free(str);
488
489 gtk_dialog_set_default_response(GTK_DIALOG(context->dialog),
490 GTK_RESPONSE_CLOSE);
491
492 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
493 relation_item_list_widget(context), TRUE, TRUE, 0);
494
495 /* ----------------------------------- */
496
497 gtk_widget_show_all(context->dialog);
498 gtk_dialog_run(GTK_DIALOG(context->dialog));
499 gtk_widget_destroy(context->dialog);
500
501 g_free(context);
502 }
503
504 /* -------------------- global relation list ----------------- */
505
506 typedef struct {
507 appdata_t *appdata;
508 GtkWidget *dialog, *list, *show_btn;
509 GtkListStore *store;
510 object_t *object; /* object this list relates to, NULL if global */
511 } relation_context_t;
512
513 enum {
514 RELATION_COL_ID = 0,
515 RELATION_COL_TYPE,
516 RELATION_COL_NAME,
517 RELATION_COL_MEMBERS,
518 RELATION_COL_DATA,
519 RELATION_NUM_COLS
520 };
521
522 static relation_t *get_selected_relation(relation_context_t *context) {
523 GtkTreeSelection *selection;
524 GtkTreeModel *model;
525 GtkTreeIter iter;
526
527 selection = list_get_selection(context->list);
528 if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
529 relation_t *relation;
530 gtk_tree_model_get(model, &iter, RELATION_COL_DATA, &relation, -1);
531 return(relation);
532 }
533 return NULL;
534 }
535
536 static void relation_list_selected(relation_context_t *context,
537 relation_t *selected) {
538
539 list_button_enable(context->list, LIST_BUTTON_USER0,
540 (selected != NULL) && (selected->member != NULL));
541 list_button_enable(context->list, LIST_BUTTON_USER1,
542 (selected != NULL) && (selected->member != NULL));
543
544 list_button_enable(context->list, LIST_BUTTON_REMOVE, selected != NULL);
545 list_button_enable(context->list, LIST_BUTTON_EDIT, selected != NULL);
546 }
547
548 static gboolean
549 relation_list_selection_func(GtkTreeSelection *selection, GtkTreeModel *model,
550 GtkTreePath *path, gboolean path_currently_selected,
551 gpointer userdata) {
552 relation_context_t *context = (relation_context_t*)userdata;
553 GtkTreeIter iter;
554
555 if(gtk_tree_model_get_iter(model, &iter, path)) {
556 g_assert(gtk_tree_path_get_depth(path) == 1);
557
558 relation_t *relation = NULL;
559 gtk_tree_model_get(model, &iter, RELATION_COL_DATA, &relation, -1);
560 relation_list_selected(context, relation);
561 }
562
563 return TRUE; /* allow selection state to change */
564 }
565
566 typedef struct {
567 relation_t *relation;
568 GtkWidget *dialog, *view;
569 GtkListStore *store;
570 } member_context_t;
571
572 enum {
573 MEMBER_COL_TYPE = 0,
574 MEMBER_COL_ID,
575 MEMBER_COL_NAME,
576 MEMBER_COL_ROLE,
577 MEMBER_COL_REF_ONLY,
578 MEMBER_COL_DATA,
579 MEMBER_NUM_COLS
580 };
581
582 static gboolean
583 member_list_selection_func(GtkTreeSelection *selection, GtkTreeModel *model,
584 GtkTreePath *path, gboolean path_currently_selected,
585 gpointer userdata) {
586 GtkTreeIter iter;
587
588 if(gtk_tree_model_get_iter(model, &iter, path)) {
589 g_assert(gtk_tree_path_get_depth(path) == 1);
590
591 member_t *member = NULL;
592 gtk_tree_model_get(model, &iter, MEMBER_COL_DATA, &member, -1);
593 if(member && member->object.type < NODE_ID)
594 return TRUE;
595 }
596
597 return FALSE;
598 }
599
600
601 static GtkWidget *member_list_widget(member_context_t *context) {
602 GtkWidget *vbox = gtk_vbox_new(FALSE,3);
603
604 #ifndef FREMANTLE_PANNABLE_AREA
605 context->view = gtk_tree_view_new();
606 #else
607 context->view = hildon_gtk_tree_view_new(HILDON_UI_MODE_EDIT);
608 #endif
609
610 gtk_tree_selection_set_select_function(
611 gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view)),
612 member_list_selection_func,
613 context, NULL);
614
615 /* --- "type" column --- */
616 GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
617 g_object_set(renderer, "foreground", "grey", NULL);
618 GtkTreeViewColumn *column =
619 gtk_tree_view_column_new_with_attributes(_("Type"), renderer,
620 "text", MEMBER_COL_TYPE,
621 "foreground-set", MEMBER_COL_REF_ONLY, NULL);
622 gtk_tree_view_column_set_sort_column_id(column, MEMBER_COL_TYPE);
623 gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
624
625 /* --- "id" column --- */
626 renderer = gtk_cell_renderer_text_new();
627 g_object_set(renderer, "foreground", "grey", NULL);
628 column = gtk_tree_view_column_new_with_attributes(_("Id"), renderer,
629 "text", MEMBER_COL_ID,
630 "foreground-set", MEMBER_COL_REF_ONLY, NULL);
631 gtk_tree_view_column_set_sort_column_id(column, MEMBER_COL_ID);
632 gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
633
634
635 /* --- "Name" column --- */
636 renderer = gtk_cell_renderer_text_new();
637 g_object_set(renderer, "foreground", "grey", NULL);
638 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
639 column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer,
640 "text", MEMBER_COL_NAME,
641 "foreground-set", MEMBER_COL_REF_ONLY, NULL);
642 gtk_tree_view_column_set_expand(column, TRUE);
643 gtk_tree_view_column_set_sort_column_id(column, MEMBER_COL_NAME);
644 gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
645
646 /* --- "role" column --- */
647 renderer = gtk_cell_renderer_text_new();
648 g_object_set(renderer, "foreground", "grey", NULL);
649 column = gtk_tree_view_column_new_with_attributes(_("Role"), renderer,
650 "text", MEMBER_COL_ROLE,
651 "foreground-set", MEMBER_COL_REF_ONLY, NULL);
652 gtk_tree_view_column_set_sort_column_id(column, MEMBER_COL_ROLE);
653 gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
654
655
656 /* build and fill the store */
657 context->store = gtk_list_store_new(MEMBER_NUM_COLS,
658 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
659 G_TYPE_BOOLEAN, G_TYPE_POINTER);
660
661 gtk_tree_view_set_model(GTK_TREE_VIEW(context->view),
662 GTK_TREE_MODEL(context->store));
663
664 GtkTreeIter iter;
665 member_t *member = context->relation->member;
666 while(member) {
667 tag_t *tags = osm_object_get_tags(&member->object);
668 char *id = osm_object_id_string(&member->object);
669
670 /* try to find something descriptive */
671 char *name = NULL;
672 if(tags)
673 name = osm_tag_get_by_key(tags, "name");
674
675 /* Append a row and fill in some data */
676 gtk_list_store_append(context->store, &iter);
677 gtk_list_store_set(context->store, &iter,
678 MEMBER_COL_TYPE, osm_object_type_string(&member->object),
679 MEMBER_COL_ID, id,
680 MEMBER_COL_NAME, name,
681 MEMBER_COL_ROLE, member->role,
682 MEMBER_COL_REF_ONLY, member->object.type >= NODE_ID,
683 MEMBER_COL_DATA, member,
684 -1);
685
686 g_free(id);
687 member = member->next;
688 }
689
690 g_object_unref(context->store);
691
692 #ifndef FREMANTLE_PANNABLE_AREA
693 /* put it into a scrolled window */
694 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
695 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
696 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
697 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
698 GTK_SHADOW_ETCHED_IN);
699 gtk_container_add(GTK_CONTAINER(scrolled_window), context->view);
700
701 gtk_box_pack_start_defaults(GTK_BOX(vbox), scrolled_window);
702 #else
703 /* put view into a pannable area */
704 GtkWidget *pannable_area = hildon_pannable_area_new();
705 gtk_container_add(GTK_CONTAINER(pannable_area), context->view);
706 gtk_box_pack_start_defaults(GTK_BOX(vbox), pannable_area);
707 #endif
708
709 return vbox;
710 }
711
712 void relation_show_members(GtkWidget *parent, relation_t *relation) {
713 member_context_t *mcontext = g_new0(member_context_t, 1);
714 mcontext->relation = relation;
715
716 char *str = osm_tag_get_by_key(OSM_TAG(mcontext->relation), "name");
717 if(!str) str = osm_tag_get_by_key(OSM_TAG(mcontext->relation), "ref");
718 if(!str)
719 str = g_strdup_printf(_("Members of relation #" ITEM_ID_FORMAT),
720 OSM_ID(mcontext->relation));
721 else
722 str = g_strdup_printf(_("Members of relation \"%s\""), str);
723
724 mcontext->dialog =
725 misc_dialog_new(MISC_DIALOG_MEDIUM, str,
726 GTK_WINDOW(parent),
727 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
728 NULL);
729 g_free(str);
730
731 gtk_dialog_set_default_response(GTK_DIALOG(mcontext->dialog),
732 GTK_RESPONSE_CLOSE);
733
734 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(mcontext->dialog)->vbox),
735 member_list_widget(mcontext), TRUE, TRUE, 0);
736
737 /* ----------------------------------- */
738
739 gtk_widget_show_all(mcontext->dialog);
740 gtk_dialog_run(GTK_DIALOG(mcontext->dialog));
741 gtk_widget_destroy(mcontext->dialog);
742
743 g_free(mcontext);
744 }
745
746 /* user clicked "members" button in relation list */
747 static void on_relation_members(GtkWidget *but, relation_context_t *context) {
748 relation_t *sel = get_selected_relation(context);
749
750 if(sel) relation_show_members(context->dialog, sel);
751 }
752
753 /* user clicked "select" button in relation list */
754 static void on_relation_select(GtkWidget *but, relation_context_t *context) {
755 relation_t *sel = get_selected_relation(context);
756 map_item_deselect(context->appdata);
757
758 if(sel) {
759 map_relation_select(context->appdata, sel);
760
761 /* tell dialog to close as we want to see the selected relation */
762
763 GtkWidget *toplevel = gtk_widget_get_toplevel(GTK_WIDGET(but));
764 g_assert(GTK_IS_DIALOG(toplevel));
765
766 /* emit a "response" signal so we might close the dialog */
767 gtk_dialog_response(GTK_DIALOG(toplevel), GTK_RESPONSE_CLOSE);
768 }
769 }
770
771
772 static void on_relation_add(GtkWidget *but, relation_context_t *context) {
773 /* create a new relation */
774
775 relation_t *relation = osm_relation_new();
776 if(!relation_info_dialog(context->dialog, context->appdata, relation)) {
777 printf("tag edit cancelled\n");
778 osm_relation_free(relation);
779 } else {
780 osm_relation_attach(context->appdata->osm, relation);
781
782 /* append a row for the new data */
783
784 char *name = relation_get_descriptive_name(relation);
785
786 guint num = osm_relation_members_num(relation);
787
788 /* Append a row and fill in some data */
789 GtkTreeIter iter;
790 gtk_list_store_append(context->store, &iter);
791 gtk_list_store_set(context->store, &iter,
792 RELATION_COL_ID, OSM_ID(relation),
793 RELATION_COL_TYPE,
794 osm_tag_get_by_key(OSM_TAG(relation), "type"),
795 RELATION_COL_NAME, name,
796 RELATION_COL_MEMBERS, num,
797 RELATION_COL_DATA, relation,
798 -1);
799
800 gtk_tree_selection_select_iter(list_get_selection(context->list), &iter);
801
802 /* scroll to end */
803 // GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment();
804 /* xyz */
805 }
806 }
807
808 /* user clicked "edit..." button in relation list */
809 static void on_relation_edit(GtkWidget *but, relation_context_t *context) {
810 relation_t *sel = get_selected_relation(context);
811 if(!sel) return;
812
813 printf("edit relation #" ITEM_ID_FORMAT "\n", OSM_ID(sel));
814
815 if (!relation_info_dialog(context->dialog, context->appdata, sel))
816 return;
817
818 // Locate the changed item
819 GtkTreeIter iter;
820 gboolean valid = gtk_tree_model_get_iter_first(
821 GTK_TREE_MODEL(context->store), &iter);
822 while (valid) {
823 relation_t *row_rel;
824 gtk_tree_model_get(GTK_TREE_MODEL(context->store), &iter,
825 RELATION_COL_DATA, &row_rel,
826 -1);
827 if (row_rel == sel)
828 break;
829 valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(context->store), &iter);
830 }
831 if (!valid)
832 return;
833
834 // Found it. Update all visible fields.
835 gtk_list_store_set(context->store, &iter,
836 RELATION_COL_ID, OSM_ID(sel),
837 RELATION_COL_TYPE, osm_tag_get_by_key(OSM_TAG(sel), "type"),
838 RELATION_COL_NAME, relation_get_descriptive_name(sel),
839 RELATION_COL_MEMBERS, osm_relation_members_num(sel),
840 -1);
841
842 // Order will probably have changed, so refocus
843 list_focus_on(context->list, &iter, TRUE);
844 }
845
846
847 /* remove the selected relation */
848 static void on_relation_remove(GtkWidget *but, relation_context_t *context) {
849 relation_t *sel = get_selected_relation(context);
850 if(!sel) return;
851
852 printf("remove relation #" ITEM_ID_FORMAT "\n", OSM_ID(sel));
853
854 gint members = osm_relation_members_num(sel);
855
856 if(members)
857 if(!yes_no_f(context->dialog, NULL, 0, 0,
858 _("Delete non-empty relation?"),
859 _("This relation still has %d members. "
860 "Delete it anyway?"), members))
861 return;
862
863 /* first remove selected row from list */
864 GtkTreeIter iter;
865 GtkTreeSelection *selection = list_get_selection(context->list);
866 if(gtk_tree_selection_get_selected(selection, NULL, &iter))
867 gtk_list_store_remove(context->store, &iter);
868
869 /* then really delete it */
870 osm_relation_delete(context->appdata->osm, sel, FALSE);
871
872 relation_list_selected(context, NULL);
873 }
874
875 static GtkWidget *relation_list_widget(relation_context_t *context) {
876 context->list = list_new(LIST_HILDON_WITH_HEADERS);
877
878 list_set_selection_function(context->list, relation_list_selection_func,
879 context);
880
881 list_set_columns(context->list,
882 _("Id"), RELATION_COL_ID, 0,
883 _("Type"), RELATION_COL_TYPE, 0,
884 _("Name"), RELATION_COL_NAME, LIST_FLAG_ELLIPSIZE,
885 _("Members"), RELATION_COL_MEMBERS, 0,
886 NULL);
887
888 /* build and fill the store */
889 context->store = gtk_list_store_new(RELATION_NUM_COLS,
890 G_TYPE_ITEM_ID_T, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT,
891 G_TYPE_POINTER);
892
893 list_set_store(context->list, context->store);
894
895 // Sorting by ref/name by default is useful for places with lots of numbered
896 // bus routes. Especially for small screens.
897 gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(context->store),
898 RELATION_COL_NAME, GTK_SORT_ASCENDING);
899
900 GtkTreeIter iter;
901 relation_t *relation = NULL;
902 relation_chain_t *rchain = NULL;
903
904 if(context->object)
905 rchain = osm_object_to_relation(context->appdata->osm, context->object);
906 else
907 relation = context->appdata->osm->relation;
908
909 while(relation || rchain) {
910 relation_t *rel = relation?relation:rchain->relation;
911
912 char *name = relation_get_descriptive_name(rel);
913 guint num = osm_relation_members_num(rel);
914
915 /* Append a row and fill in some data */
916 gtk_list_store_append(context->store, &iter);
917 gtk_list_store_set(context->store, &iter,
918 RELATION_COL_ID, OSM_ID(rel),
919 RELATION_COL_TYPE,
920 osm_tag_get_by_key(OSM_TAG(rel), "type"),
921 RELATION_COL_NAME, name,
922 RELATION_COL_MEMBERS, num,
923 RELATION_COL_DATA, rel,
924 -1);
925
926 if(relation) relation = relation->next;
927 if(rchain) rchain = rchain->next;
928 }
929
930 if(rchain)
931 osm_relation_chain_free(rchain);
932
933 g_object_unref(context->store);
934
935 list_set_static_buttons(context->list, LIST_BTN_NEW | LIST_BTN_WIDE,
936 G_CALLBACK(on_relation_add), G_CALLBACK(on_relation_edit),
937 G_CALLBACK(on_relation_remove), context);
938
939 list_set_user_buttons(context->list,
940 LIST_BUTTON_USER0, _("Members"), G_CALLBACK(on_relation_members),
941 LIST_BUTTON_USER1, _("Select"), G_CALLBACK(on_relation_select),
942 0);
943
944 relation_list_selected(context, NULL);
945
946 return context->list;
947 }
948
949 /* a global view on all relations */
950 void relation_list(GtkWidget *parent, appdata_t *appdata, object_t *object) {
951 relation_context_t *context = g_new0(relation_context_t, 1);
952 context->appdata = appdata;
953
954 char *str = NULL;
955 if(!object)
956 str = g_strdup(_("All relations"));
957 else {
958 str = g_strdup_printf(_("Relations of %s"), osm_object_string(object));
959 context->object = object;
960 }
961
962 context->dialog =
963 misc_dialog_new(MISC_DIALOG_LARGE, str,
964 GTK_WINDOW(parent),
965 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
966 NULL);
967
968 g_free(str);
969
970 gtk_dialog_set_default_response(GTK_DIALOG(context->dialog),
971 GTK_RESPONSE_CLOSE);
972
973 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
974 relation_list_widget(context), TRUE, TRUE, 0);
975
976 /* ----------------------------------- */
977
978
979 gtk_widget_show_all(context->dialog);
980 gtk_dialog_run(GTK_DIALOG(context->dialog));
981
982 gtk_widget_destroy(context->dialog);
983 g_free(context);
984 }
985
986 // vim:et:ts=8:sw=2:sts=2:ai