Contents of /trunk/src/relation_edit.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 201 - (hide annotations)
Thu Jul 9 13:04:52 2009 UTC (14 years, 11 months ago) by harbaum
File MIME type: text/plain
File size: 29153 byte(s)
Moved relation handling into info dialog
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     /* --------------- relation dialog for an item (node or way) ----------- */
23    
24     typedef struct {
25 harbaum 154 object_t *item;
26 harbaum 1 appdata_t *appdata;
27 harbaum 148 GtkWidget *dialog, *list;
28 harbaum 1 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 harbaum 73 static gboolean relation_add_item(GtkWidget *parent,
46 harbaum 155 relation_t *relation, object_t *object) {
47 harbaum 1 role_chain_t *chain = NULL, **chainP = &chain;
48    
49 harbaum 161 printf("add object of type %d to relation #" ITEM_ID_FORMAT "\n",
50 harbaum 155 object->type, relation->id);
51 harbaum 1
52 harbaum 155 /* ask the user for the role of the new object in this relation */
53 harbaum 1
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 harbaum 167 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 harbaum 1
84     gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
85    
86     char *type = osm_tag_get_by_key(relation->tag, "type");
87    
88     char *info_str = NULL;
89     if(type) info_str = g_strdup_printf(_("In relation of type: %s"), type);
90 harbaum 161 else info_str = g_strdup_printf(_("In relation #" ITEM_ID_FORMAT),
91     relation->id);
92 harbaum 1 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(relation->tag, "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 = gtk_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 harbaum 73 return FALSE;
126 harbaum 1 }
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 harbaum 155 g_assert((object->type == NODE)||(object->type == WAY)||
148     (object->type == RELATION));
149    
150 harbaum 1 /* create new member */
151     *memberP = g_new0(member_t, 1);
152 harbaum 155 (*memberP)->object = *object;
153 harbaum 1 (*memberP)->role = role;
154    
155     relation->flags |= OSM_FLAG_DIRTY;
156 harbaum 73 return TRUE;
157 harbaum 1 }
158    
159 harbaum 155 static void relation_remove_item(relation_t *relation, object_t *object) {
160 harbaum 73
161 harbaum 161 printf("remove object of type %d from relation #" ITEM_ID_FORMAT "\n",
162 harbaum 155 object->type, relation->id);
163 harbaum 73
164     member_t **member = &relation->member;
165     while(*member) {
166 harbaum 155 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 harbaum 73
174     member_t *next = (*member)->next;
175     osm_member_free(*member);
176     *member = next;
177    
178     relation->flags |= OSM_FLAG_DIRTY;
179    
180     return;
181     } else
182     member = &(*member)->next;
183     }
184     g_assert(0);
185     }
186    
187 harbaum 76 static void relation_item_list_selected(relitem_context_t *context,
188 harbaum 73 gboolean selected) {
189    
190 harbaum 148 list_button_enable(context->list, LIST_BUTTON_REMOVE, selected);
191     list_button_enable(context->list, LIST_BUTTON_EDIT, selected);
192 harbaum 73 }
193    
194 achadwick 99 /* try to find something descriptive */
195     static char *relation_get_descriptive_name(relation_t *relation) {
196     char *name = osm_tag_get_by_key(relation->tag, "ref");
197     if (!name)
198     name = osm_tag_get_by_key(relation->tag, "name");
199     if (!name)
200     name = osm_tag_get_by_key(relation->tag, "note");
201     if (!name)
202     name = osm_tag_get_by_key(relation->tag, "fix" "me");
203     return name;
204     }
205    
206 harbaum 153 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 harbaum 76 static void on_relation_item_add(GtkWidget *but, relitem_context_t *context) {
215 harbaum 73 /* create a new relation */
216    
217     relation_t *relation = osm_relation_new();
218 harbaum 153 if(!relation_info_dialog(context->dialog, context->appdata, relation)) {
219 harbaum 73 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 achadwick 99 char *name = relation_get_descriptive_name(relation);
228 harbaum 73
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(relation->tag, "type"),
235     RELITEM_COL_NAME, name,
236     RELITEM_COL_DATA, relation,
237     -1);
238    
239 harbaum 148 gtk_tree_selection_select_iter(list_get_selection(context->list), &iter);
240 harbaum 73 }
241 harbaum 1 }
242    
243     static relation_t *get_selection(relitem_context_t *context) {
244     GtkTreeSelection *selection;
245     GtkTreeModel *model;
246     GtkTreeIter iter;
247    
248 harbaum 148 selection = list_get_selection(context->list);
249 harbaum 1 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 harbaum 76 static void on_relation_item_edit(GtkWidget *but, relitem_context_t *context) {
258 harbaum 1 relation_t *sel = get_selection(context);
259     if(!sel) return;
260    
261 harbaum 161 printf("edit relation item #" ITEM_ID_FORMAT "\n", sel->id);
262 harbaum 1
263 harbaum 153 if (!relation_info_dialog(context->dialog, context->appdata, sel))
264 achadwick 99 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(sel->tag, "type"),
285     RELITEM_COL_NAME, relation_get_descriptive_name(sel),
286     -1);
287    
288     // Order will probably have changed, so refocus
289 harbaum 148 list_focus_on(context->list, &iter, TRUE);
290 harbaum 1 }
291    
292 harbaum 75 /* remove the selected relation */
293 harbaum 76 static void on_relation_item_remove(GtkWidget *but, relitem_context_t *context) {
294 harbaum 1 relation_t *sel = get_selection(context);
295     if(!sel) return;
296    
297 harbaum 161 printf("remove relation #" ITEM_ID_FORMAT "\n", sel->id);
298 harbaum 75
299 harbaum 77 gint members = osm_relation_members_num(sel);
300 harbaum 75
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 harbaum 148 GtkTreeSelection *selection = list_get_selection(context->list);
311 harbaum 75 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 harbaum 76 relation_item_list_selected(context, FALSE);
318 harbaum 1 }
319    
320 harbaum 154 static char *relitem_get_role_in_relation(object_t *item, relation_t *relation) {
321 harbaum 73 member_t *member = relation->member;
322     while(member) {
323 harbaum 155 switch(member->object.type) {
324 harbaum 73
325     case NODE:
326 harbaum 155 if((item->type == NODE) && (item->node == member->object.node))
327 harbaum 73 return member->role;
328     break;
329    
330     case WAY:
331 harbaum 155 if((item->type == WAY) && (item->way == member->object.way))
332 harbaum 73 return member->role;
333     break;
334    
335     default:
336     break;
337     }
338     member = member->next;
339     }
340     return NULL;
341     }
342    
343 harbaum 1 static void
344     relitem_toggled(GtkCellRendererToggle *cell, const gchar *path_str,
345 harbaum 75 relitem_context_t *context) {
346 harbaum 1 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 harbaum 73 gtk_tree_path_free(path);
352 harbaum 1
353     /* get current enabled flag */
354     gboolean enabled;
355 harbaum 73 relation_t *relation = NULL;
356 harbaum 1 gtk_tree_model_get(GTK_TREE_MODEL(context->store), &iter,
357 harbaum 73 RELITEM_COL_SELECTED, &enabled,
358     RELITEM_COL_DATA, &relation,
359     -1);
360 harbaum 1
361 achadwick 99 list_pre_inplace_edit_tweak(GTK_TREE_MODEL(context->store));
362    
363 harbaum 73 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 achadwick 99
380 harbaum 1 }
381    
382 harbaum 154 static gboolean relitem_is_in_relation(object_t *item, relation_t *relation) {
383 harbaum 1 member_t *member = relation->member;
384     while(member) {
385 harbaum 155 switch(member->object.type) {
386 harbaum 1
387     case NODE:
388 harbaum 155 if((item->type == NODE) && (item->node == member->object.node))
389 harbaum 1 return TRUE;
390     break;
391    
392     case WAY:
393 harbaum 155 if((item->type == WAY) && (item->way == member->object.way))
394 harbaum 1 return TRUE;
395     break;
396    
397     default:
398     break;
399     }
400     member = member->next;
401     }
402     return FALSE;
403     }
404    
405 harbaum 76 static GtkWidget *relation_item_list_widget(relitem_context_t *context) {
406 harbaum 148 context->list = list_new(LIST_HILDON_WITH_HEADERS);
407 harbaum 1
408 harbaum 148 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 harbaum 1
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 harbaum 148 list_set_store(context->list, context->store);
422 harbaum 1
423 achadwick 99 // 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 harbaum 1 GtkTreeIter iter;
429     relation_t *relation = context->appdata->osm->relation;
430     while(relation) {
431     /* try to find something descriptive */
432 achadwick 99 char *name = relation_get_descriptive_name(relation);
433 harbaum 1
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(relation->tag, "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 harbaum 148 list_set_static_buttons(context->list, G_CALLBACK(on_relation_item_add),
450 harbaum 153 G_CALLBACK(on_relation_item_edit),G_CALLBACK(on_relation_item_remove),
451     context);
452 harbaum 1
453 harbaum 76 relation_item_list_selected(context, FALSE);
454 harbaum 1
455 harbaum 148 return context->list;
456 harbaum 1 }
457    
458 harbaum 201 void relation_add_dialog(GtkWidget *parent,
459     appdata_t *appdata, object_t *object) {
460 harbaum 1 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 harbaum 154 context->item = object;
466 harbaum 1
467     char *str = NULL;
468 harbaum 154 switch(object->type) {
469 harbaum 1 case NODE:
470 harbaum 161 str = g_strdup_printf(_("Relations for node #" ITEM_ID_FORMAT),
471     object->node->id);
472 harbaum 1 break;
473     case WAY:
474 harbaum 161 str = g_strdup_printf(_("Relations for way #" ITEM_ID_FORMAT),
475     object->way->id);
476 harbaum 1 break;
477     default:
478 harbaum 154 g_assert((object->type == NODE) || (object->type == WAY));
479 harbaum 1 break;
480     }
481    
482 harbaum 167 context->dialog =
483     misc_dialog_new(MISC_DIALOG_LARGE, str,
484 harbaum 201 GTK_WINDOW(parent),
485 harbaum 167 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
486     NULL);
487 harbaum 1 g_free(str);
488    
489     gtk_dialog_set_default_response(GTK_DIALOG(context->dialog),
490 harbaum 76 GTK_RESPONSE_CLOSE);
491 harbaum 1
492     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
493 harbaum 76 relation_item_list_widget(context), TRUE, TRUE, 0);
494 harbaum 1
495     /* ----------------------------------- */
496    
497     gtk_widget_show_all(context->dialog);
498 harbaum 73 gtk_dialog_run(GTK_DIALOG(context->dialog));
499 harbaum 1 gtk_widget_destroy(context->dialog);
500    
501     g_free(context);
502     }
503 harbaum 76
504     /* -------------------- global relation list ----------------- */
505    
506     typedef struct {
507     appdata_t *appdata;
508 harbaum 153 GtkWidget *dialog, *list, *show_btn;
509 harbaum 76 GtkListStore *store;
510 harbaum 191 object_t *object; /* object this list relates to, NULL if global */
511 harbaum 76 } 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 harbaum 77 static relation_t *get_selected_relation(relation_context_t *context) {
523     GtkTreeSelection *selection;
524     GtkTreeModel *model;
525     GtkTreeIter iter;
526    
527 harbaum 148 selection = list_get_selection(context->list);
528 harbaum 77 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 harbaum 76 static void relation_list_selected(relation_context_t *context,
537 harbaum 77 relation_t *selected) {
538 harbaum 76
539 harbaum 148 list_button_enable(context->list, LIST_BUTTON_USER0,
540     (selected != NULL) && (selected->member != NULL));
541 harbaum 153 gtk_widget_set_sensitive(context->show_btn,
542     (selected != NULL) && (selected->member != NULL));
543 harbaum 77
544 harbaum 148 list_button_enable(context->list, LIST_BUTTON_REMOVE, selected != NULL);
545     list_button_enable(context->list, LIST_BUTTON_EDIT, selected != NULL);
546 harbaum 76 }
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 harbaum 77
558     relation_t *relation = NULL;
559     gtk_tree_model_get(model, &iter, RELATION_COL_DATA, &relation, -1);
560     relation_list_selected(context, relation);
561 harbaum 76 }
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 harbaum 155 if(member && member->object.type < NODE_ID)
594 harbaum 76 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     context->view = gtk_tree_view_new();
604    
605     gtk_tree_selection_set_select_function(
606     gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view)),
607     member_list_selection_func,
608     context, NULL);
609    
610     /* --- "type" column --- */
611     GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
612     g_object_set(renderer, "foreground", "grey", NULL);
613 harbaum 155 GtkTreeViewColumn *column =
614     gtk_tree_view_column_new_with_attributes(_("Type"), renderer,
615     "text", MEMBER_COL_TYPE,
616     "foreground-set", MEMBER_COL_REF_ONLY, NULL);
617     gtk_tree_view_column_set_sort_column_id(column, MEMBER_COL_TYPE);
618     gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
619 harbaum 76
620     /* --- "id" column --- */
621     renderer = gtk_cell_renderer_text_new();
622     g_object_set(renderer, "foreground", "grey", NULL);
623 harbaum 155 column = gtk_tree_view_column_new_with_attributes(_("Id"), renderer,
624     "text", MEMBER_COL_ID,
625     "foreground-set", MEMBER_COL_REF_ONLY, NULL);
626     gtk_tree_view_column_set_sort_column_id(column, MEMBER_COL_ID);
627     gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
628 harbaum 76
629 harbaum 155
630 harbaum 76 /* --- "Name" column --- */
631     renderer = gtk_cell_renderer_text_new();
632     g_object_set(renderer, "foreground", "grey", NULL);
633     g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
634 harbaum 155 column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer,
635 harbaum 76 "text", MEMBER_COL_NAME,
636     "foreground-set", MEMBER_COL_REF_ONLY, NULL);
637     gtk_tree_view_column_set_expand(column, TRUE);
638 harbaum 155 gtk_tree_view_column_set_sort_column_id(column, MEMBER_COL_NAME);
639 harbaum 76 gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
640    
641     /* --- "role" column --- */
642     renderer = gtk_cell_renderer_text_new();
643     g_object_set(renderer, "foreground", "grey", NULL);
644 harbaum 155 column = gtk_tree_view_column_new_with_attributes(_("Role"), renderer,
645     "text", MEMBER_COL_ROLE,
646     "foreground-set", MEMBER_COL_REF_ONLY, NULL);
647     gtk_tree_view_column_set_sort_column_id(column, MEMBER_COL_ROLE);
648     gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
649 harbaum 76
650 harbaum 155
651 harbaum 76 /* build and fill the store */
652     context->store = gtk_list_store_new(MEMBER_NUM_COLS,
653     G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
654     G_TYPE_BOOLEAN, G_TYPE_POINTER);
655    
656     gtk_tree_view_set_model(GTK_TREE_VIEW(context->view),
657     GTK_TREE_MODEL(context->store));
658    
659     GtkTreeIter iter;
660     member_t *member = context->relation->member;
661     while(member) {
662 harbaum 155 tag_t *tags = osm_object_get_tags(&member->object);
663     char *id = osm_object_id_string(&member->object);
664 harbaum 76
665     /* try to find something descriptive */
666     char *name = NULL;
667     if(tags)
668     name = osm_tag_get_by_key(tags, "name");
669    
670     /* Append a row and fill in some data */
671     gtk_list_store_append(context->store, &iter);
672     gtk_list_store_set(context->store, &iter,
673 harbaum 155 MEMBER_COL_TYPE, osm_object_type_string(&member->object),
674 harbaum 76 MEMBER_COL_ID, id,
675     MEMBER_COL_NAME, name,
676     MEMBER_COL_ROLE, member->role,
677 harbaum 155 MEMBER_COL_REF_ONLY, member->object.type >= NODE_ID,
678 harbaum 76 MEMBER_COL_DATA, member,
679     -1);
680    
681     g_free(id);
682     member = member->next;
683     }
684    
685     g_object_unref(context->store);
686    
687     /* put it into a scrolled window */
688     GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
689     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
690     GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
691     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
692     GTK_SHADOW_ETCHED_IN);
693     gtk_container_add(GTK_CONTAINER(scrolled_window), context->view);
694    
695     gtk_box_pack_start_defaults(GTK_BOX(vbox), scrolled_window);
696    
697     return vbox;
698     }
699    
700 harbaum 155 void relation_show_members(GtkWidget *parent, relation_t *relation) {
701 harbaum 76 member_context_t *mcontext = g_new0(member_context_t, 1);
702 harbaum 155 mcontext->relation = relation;
703 harbaum 76
704     char *str = osm_tag_get_by_key(mcontext->relation->tag, "name");
705     if(!str) str = osm_tag_get_by_key(mcontext->relation->tag, "ref");
706     if(!str)
707 harbaum 161 str = g_strdup_printf(_("Members of relation #" ITEM_ID_FORMAT),
708 harbaum 76 mcontext->relation->id);
709     else
710     str = g_strdup_printf(_("Members of relation \"%s\""), str);
711    
712     mcontext->dialog =
713 harbaum 167 misc_dialog_new(MISC_DIALOG_MEDIUM, str,
714     GTK_WINDOW(parent),
715     GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
716     NULL);
717 harbaum 76 g_free(str);
718    
719     gtk_dialog_set_default_response(GTK_DIALOG(mcontext->dialog),
720     GTK_RESPONSE_CLOSE);
721    
722     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(mcontext->dialog)->vbox),
723     member_list_widget(mcontext), TRUE, TRUE, 0);
724    
725     /* ----------------------------------- */
726    
727     gtk_widget_show_all(mcontext->dialog);
728     gtk_dialog_run(GTK_DIALOG(mcontext->dialog));
729     gtk_widget_destroy(mcontext->dialog);
730    
731     g_free(mcontext);
732     }
733    
734 harbaum 155 /* user clicked "members..." button in relation list */
735     static void on_relation_members(GtkWidget *but, relation_context_t *context) {
736     relation_t *sel = get_selected_relation(context);
737    
738     if(sel)
739     relation_show_members(context->dialog, sel);
740     }
741    
742    
743 harbaum 77 static void on_relation_add(GtkWidget *but, relation_context_t *context) {
744     /* create a new relation */
745    
746     relation_t *relation = osm_relation_new();
747 harbaum 153 if(!relation_info_dialog(context->dialog, context->appdata, relation)) {
748 harbaum 77 printf("tag edit cancelled\n");
749     osm_relation_free(relation);
750     } else {
751     osm_relation_attach(context->appdata->osm, relation);
752    
753     /* append a row for the new data */
754    
755 achadwick 99 char *name = relation_get_descriptive_name(relation);
756 harbaum 77
757 achadwick 99 guint num = osm_relation_members_num(relation);
758 harbaum 77
759     /* Append a row and fill in some data */
760     GtkTreeIter iter;
761     gtk_list_store_append(context->store, &iter);
762     gtk_list_store_set(context->store, &iter,
763 achadwick 99 RELATION_COL_ID, relation->id,
764 harbaum 77 RELATION_COL_TYPE,
765     osm_tag_get_by_key(relation->tag, "type"),
766     RELATION_COL_NAME, name,
767     RELATION_COL_MEMBERS, num,
768     RELATION_COL_DATA, relation,
769     -1);
770    
771 harbaum 148 gtk_tree_selection_select_iter(list_get_selection(context->list), &iter);
772 harbaum 77
773     /* scroll to end */
774     // GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment();
775     /* xyz */
776     }
777     }
778    
779     /* user clicked "edit..." button in relation list */
780     static void on_relation_edit(GtkWidget *but, relation_context_t *context) {
781     relation_t *sel = get_selected_relation(context);
782     if(!sel) return;
783    
784 harbaum 161 printf("edit relation #" ITEM_ID_FORMAT "\n", sel->id);
785 harbaum 77
786 harbaum 153 if (!relation_info_dialog(context->dialog, context->appdata, sel))
787 achadwick 99 return;
788    
789     // Locate the changed item
790     GtkTreeIter iter;
791     gboolean valid = gtk_tree_model_get_iter_first(
792     GTK_TREE_MODEL(context->store), &iter);
793     while (valid) {
794     relation_t *row_rel;
795     gtk_tree_model_get(GTK_TREE_MODEL(context->store), &iter,
796     RELATION_COL_DATA, &row_rel,
797     -1);
798     if (row_rel == sel)
799     break;
800     valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(context->store), &iter);
801     }
802     if (!valid)
803     return;
804    
805     // Found it. Update all visible fields.
806     gtk_list_store_set(context->store, &iter,
807     RELATION_COL_ID, sel->id,
808     RELATION_COL_TYPE, osm_tag_get_by_key(sel->tag, "type"),
809     RELATION_COL_NAME, relation_get_descriptive_name(sel),
810     RELATION_COL_MEMBERS, osm_relation_members_num(sel),
811     -1);
812    
813     // Order will probably have changed, so refocus
814 harbaum 148 list_focus_on(context->list, &iter, TRUE);
815 harbaum 77 }
816    
817 achadwick 99
818 harbaum 77 /* remove the selected relation */
819     static void on_relation_remove(GtkWidget *but, relation_context_t *context) {
820     relation_t *sel = get_selected_relation(context);
821     if(!sel) return;
822    
823 harbaum 161 printf("remove relation #" ITEM_ID_FORMAT "\n", sel->id);
824 harbaum 77
825     gint members = osm_relation_members_num(sel);
826    
827     if(members)
828     if(!yes_no_f(context->dialog, NULL, 0, 0,
829     _("Delete non-empty relation?"),
830     _("This relation still has %d members. "
831     "Delete it anyway?"), members))
832     return;
833    
834     /* first remove selected row from list */
835     GtkTreeIter iter;
836 harbaum 148 GtkTreeSelection *selection = list_get_selection(context->list);
837 harbaum 77 if(gtk_tree_selection_get_selected(selection, NULL, &iter))
838     gtk_list_store_remove(context->store, &iter);
839    
840     /* then really delete it */
841     osm_relation_delete(context->appdata->osm, sel, FALSE);
842    
843     relation_list_selected(context, NULL);
844     }
845    
846 harbaum 76 static GtkWidget *relation_list_widget(relation_context_t *context) {
847 harbaum 148 context->list = list_new(LIST_HILDON_WITH_HEADERS);
848 harbaum 76
849 harbaum 153 list_set_selection_function(context->list, relation_list_selection_func,
850     context);
851 harbaum 76
852 harbaum 148 list_set_columns(context->list,
853     _("Id"), RELATION_COL_ID, 0,
854     _("Type"), RELATION_COL_TYPE, 0,
855     _("Name"), RELATION_COL_NAME, LIST_FLAG_ELLIPSIZE,
856     _("Members"), RELATION_COL_MEMBERS, 0,
857     NULL);
858 harbaum 76
859     /* build and fill the store */
860     context->store = gtk_list_store_new(RELATION_NUM_COLS,
861 achadwick 99 G_TYPE_ITEM_ID_T, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT,
862 harbaum 76 G_TYPE_POINTER);
863    
864 harbaum 148 list_set_store(context->list, context->store);
865 harbaum 76
866 achadwick 99 // Sorting by ref/name by default is useful for places with lots of numbered
867     // bus routes. Especially for small screens.
868     gtk_tree_sortable_set_sort_column_id(GTK_TREE_SORTABLE(context->store),
869     RELATION_COL_NAME, GTK_SORT_ASCENDING);
870    
871 harbaum 76 GtkTreeIter iter;
872 harbaum 191 relation_t *relation = NULL;
873     relation_chain_t *rchain = NULL;
874 harbaum 76
875 harbaum 191 if(context->object)
876     rchain = osm_object_to_relation(context->appdata->osm, context->object);
877     else
878     relation = context->appdata->osm->relation;
879 harbaum 76
880 harbaum 191 while(relation || rchain) {
881     relation_t *rel = relation?relation:rchain->relation;
882    
883     char *name = relation_get_descriptive_name(rel);
884     guint num = osm_relation_members_num(rel);
885    
886 harbaum 76 /* Append a row and fill in some data */
887     gtk_list_store_append(context->store, &iter);
888     gtk_list_store_set(context->store, &iter,
889 harbaum 191 RELATION_COL_ID, rel->id,
890 harbaum 76 RELATION_COL_TYPE,
891 harbaum 191 osm_tag_get_by_key(rel->tag, "type"),
892 harbaum 76 RELATION_COL_NAME, name,
893     RELATION_COL_MEMBERS, num,
894 harbaum 191 RELATION_COL_DATA, rel,
895 harbaum 76 -1);
896 harbaum 191
897     if(relation) relation = relation->next;
898     if(rchain) rchain = rchain->next;
899 harbaum 76 }
900 harbaum 191
901     if(rchain)
902     osm_relation_chain_free(rchain);
903 harbaum 76
904     g_object_unref(context->store);
905    
906 harbaum 148 list_set_static_buttons(context->list, G_CALLBACK(on_relation_add),
907     G_CALLBACK(on_relation_edit), G_CALLBACK(on_relation_remove), context);
908 harbaum 76
909 harbaum 148 list_set_user_buttons(context->list,
910 harbaum 177 LIST_BUTTON_USER0, _("Members"), G_CALLBACK(on_relation_members),
911 harbaum 148 0);
912 harbaum 76
913 harbaum 77 relation_list_selected(context, NULL);
914 harbaum 76
915 harbaum 148 return context->list;
916 harbaum 76 }
917    
918     /* a global view on all relations */
919 harbaum 191 void relation_list(GtkWidget *parent, appdata_t *appdata, object_t *object) {
920 harbaum 76 relation_context_t *context = g_new0(relation_context_t, 1);
921     context->appdata = appdata;
922    
923 harbaum 191 char *str = NULL;
924     if(!object)
925     str = g_strdup(_("All relations"));
926     else {
927     str = g_strdup_printf(_("Relations of %s"), osm_object_string(object));
928     context->object = object;
929     }
930    
931 harbaum 76 context->dialog =
932 harbaum 191 misc_dialog_new(MISC_DIALOG_LARGE, str,
933     GTK_WINDOW(parent),
934 harbaum 167 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
935     NULL);
936 harbaum 191
937     g_free(str);
938 harbaum 76
939     gtk_dialog_set_default_response(GTK_DIALOG(context->dialog),
940     GTK_RESPONSE_CLOSE);
941    
942 harbaum 153 context->show_btn = gtk_dialog_add_button(GTK_DIALOG(context->dialog),
943     _("Select"), GTK_RESPONSE_HELP);
944    
945 harbaum 76 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
946     relation_list_widget(context), TRUE, TRUE, 0);
947    
948     /* ----------------------------------- */
949    
950 harbaum 153
951 harbaum 76 gtk_widget_show_all(context->dialog);
952 harbaum 153 if(gtk_dialog_run(GTK_DIALOG(context->dialog)) == GTK_RESPONSE_HELP) {
953     map_item_deselect(appdata);
954    
955     relation_t *sel = get_selected_relation(context);
956     if(sel) map_relation_select(appdata, sel);
957     }
958    
959 harbaum 76 gtk_widget_destroy(context->dialog);
960     g_free(context);
961     }
962 achadwick 99
963     // vim:et:ts=8:sw=2:sts=2:ai