Contents of /trunk/src/relation_edit.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 315 - (hide 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 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 239 object->type, OSM_ID(relation));
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 harbaum 240 char *type = osm_tag_get_by_key(OSM_TAG(relation), "type");
87 harbaum 1
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 harbaum 239 OSM_ID(relation));
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 harbaum 240 char *name = osm_tag_get_by_key(OSM_TAG(relation), "name");
97 harbaum 1 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 harbaum 315 entry = entry_new();
117 harbaum 1
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 harbaum 240 OSM_FLAGS(relation) |= 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 239 object->type, OSM_ID(relation));
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 harbaum 240 OSM_FLAGS(relation) |= OSM_FLAG_DIRTY;
179 harbaum 73
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 harbaum 240 char *name = osm_tag_get_by_key(OSM_TAG(relation), "ref");
197 achadwick 99 if (!name)
198 harbaum 240 name = osm_tag_get_by_key(OSM_TAG(relation), "name");
199 achadwick 99 if (!name)
200 harbaum 240 name = osm_tag_get_by_key(OSM_TAG(relation), "note");
201 achadwick 99 if (!name)
202 harbaum 240 name = osm_tag_get_by_key(OSM_TAG(relation), "fix" "me");
203 achadwick 99 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 harbaum 240 osm_tag_get_by_key(OSM_TAG(relation), "type"),
235 harbaum 73 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 239 printf("edit relation item #" ITEM_ID_FORMAT "\n", OSM_ID(sel));
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 harbaum 240 RELITEM_COL_TYPE, osm_tag_get_by_key(OSM_TAG(sel), "type"),
285     RELITEM_COL_NAME, relation_get_descriptive_name(sel),
286 achadwick 99 -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 239 printf("remove relation #" ITEM_ID_FORMAT "\n", OSM_ID(sel));
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 harbaum 240 RELITEM_COL_TYPE, osm_tag_get_by_key(OSM_TAG(relation), "type"),
439 harbaum 1 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 315 list_set_static_buttons(context->list, 0,
450 harbaum 266 G_CALLBACK(on_relation_item_add), G_CALLBACK(on_relation_item_edit),
451     G_CALLBACK(on_relation_item_remove), 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 harbaum 239 OBJECT_ID(*object));
472 harbaum 1 break;
473     case WAY:
474 harbaum 161 str = g_strdup_printf(_("Relations for way #" ITEM_ID_FORMAT),
475 harbaum 239 OBJECT_ID(*object));
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 264 list_button_enable(context->list, LIST_BUTTON_USER1,
542 harbaum 153 (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 harbaum 314
604     #ifndef FREMANTLE_PANNABLE_AREA
605 harbaum 76 context->view = gtk_tree_view_new();
606 harbaum 314 #else
607     context->view = hildon_gtk_tree_view_new(HILDON_UI_MODE_EDIT);
608     #endif
609 harbaum 76
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 harbaum 155 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 harbaum 76
625     /* --- "id" column --- */
626     renderer = gtk_cell_renderer_text_new();
627     g_object_set(renderer, "foreground", "grey", NULL);
628 harbaum 155 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 harbaum 76
634 harbaum 155
635 harbaum 76 /* --- "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 harbaum 155 column = gtk_tree_view_column_new_with_attributes(_("Name"), renderer,
640 harbaum 76 "text", MEMBER_COL_NAME,
641     "foreground-set", MEMBER_COL_REF_ONLY, NULL);
642     gtk_tree_view_column_set_expand(column, TRUE);
643 harbaum 155 gtk_tree_view_column_set_sort_column_id(column, MEMBER_COL_NAME);
644 harbaum 76 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 harbaum 155 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 harbaum 76
655 harbaum 155
656 harbaum 76 /* 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 harbaum 155 tag_t *tags = osm_object_get_tags(&member->object);
668     char *id = osm_object_id_string(&member->object);
669 harbaum 76
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 harbaum 155 MEMBER_COL_TYPE, osm_object_type_string(&member->object),
679 harbaum 76 MEMBER_COL_ID, id,
680     MEMBER_COL_NAME, name,
681     MEMBER_COL_ROLE, member->role,
682 harbaum 155 MEMBER_COL_REF_ONLY, member->object.type >= NODE_ID,
683 harbaum 76 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 harbaum 314 #ifndef FREMANTLE_PANNABLE_AREA
693 harbaum 76 /* 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 harbaum 314 #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 harbaum 76
709     return vbox;
710     }
711    
712 harbaum 155 void relation_show_members(GtkWidget *parent, relation_t *relation) {
713 harbaum 76 member_context_t *mcontext = g_new0(member_context_t, 1);
714 harbaum 155 mcontext->relation = relation;
715 harbaum 76
716 harbaum 240 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 harbaum 76 if(!str)
719 harbaum 161 str = g_strdup_printf(_("Members of relation #" ITEM_ID_FORMAT),
720 harbaum 239 OSM_ID(mcontext->relation));
721 harbaum 76 else
722     str = g_strdup_printf(_("Members of relation \"%s\""), str);
723    
724     mcontext->dialog =
725 harbaum 167 misc_dialog_new(MISC_DIALOG_MEDIUM, str,
726     GTK_WINDOW(parent),
727     GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
728     NULL);
729 harbaum 76 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 harbaum 264 /* user clicked "members" button in relation list */
747 harbaum 155 static void on_relation_members(GtkWidget *but, relation_context_t *context) {
748     relation_t *sel = get_selected_relation(context);
749    
750 harbaum 264 if(sel) relation_show_members(context->dialog, sel);
751 harbaum 155 }
752    
753 harbaum 264 /* 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 harbaum 155
758 harbaum 264 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 harbaum 77 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 harbaum 153 if(!relation_info_dialog(context->dialog, context->appdata, relation)) {
777 harbaum 77 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 achadwick 99 char *name = relation_get_descriptive_name(relation);
785 harbaum 77
786 achadwick 99 guint num = osm_relation_members_num(relation);
787 harbaum 77
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 harbaum 239 RELATION_COL_ID, OSM_ID(relation),
793 harbaum 77 RELATION_COL_TYPE,
794 harbaum 240 osm_tag_get_by_key(OSM_TAG(relation), "type"),
795 harbaum 77 RELATION_COL_NAME, name,
796     RELATION_COL_MEMBERS, num,
797     RELATION_COL_DATA, relation,
798     -1);
799    
800 harbaum 148 gtk_tree_selection_select_iter(list_get_selection(context->list), &iter);
801 harbaum 77
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 harbaum 239 printf("edit relation #" ITEM_ID_FORMAT "\n", OSM_ID(sel));
814 harbaum 77
815 harbaum 153 if (!relation_info_dialog(context->dialog, context->appdata, sel))
816 achadwick 99 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 harbaum 239 RELATION_COL_ID, OSM_ID(sel),
837 harbaum 240 RELATION_COL_TYPE, osm_tag_get_by_key(OSM_TAG(sel), "type"),
838 achadwick 99 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 harbaum 148 list_focus_on(context->list, &iter, TRUE);
844 harbaum 77 }
845    
846 achadwick 99
847 harbaum 77 /* 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 harbaum 239 printf("remove relation #" ITEM_ID_FORMAT "\n", OSM_ID(sel));
853 harbaum 77
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 harbaum 148 GtkTreeSelection *selection = list_get_selection(context->list);
866 harbaum 77 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 harbaum 76 static GtkWidget *relation_list_widget(relation_context_t *context) {
876 harbaum 148 context->list = list_new(LIST_HILDON_WITH_HEADERS);
877 harbaum 76
878 harbaum 153 list_set_selection_function(context->list, relation_list_selection_func,
879     context);
880 harbaum 76
881 harbaum 148 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 harbaum 76
888     /* build and fill the store */
889     context->store = gtk_list_store_new(RELATION_NUM_COLS,
890 achadwick 99 G_TYPE_ITEM_ID_T, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_UINT,
891 harbaum 76 G_TYPE_POINTER);
892    
893 harbaum 148 list_set_store(context->list, context->store);
894 harbaum 76
895 achadwick 99 // 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 harbaum 76 GtkTreeIter iter;
901 harbaum 191 relation_t *relation = NULL;
902     relation_chain_t *rchain = NULL;
903 harbaum 76
904 harbaum 191 if(context->object)
905     rchain = osm_object_to_relation(context->appdata->osm, context->object);
906     else
907     relation = context->appdata->osm->relation;
908 harbaum 76
909 harbaum 191 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 harbaum 76 /* 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 harbaum 239 RELATION_COL_ID, OSM_ID(rel),
919 harbaum 76 RELATION_COL_TYPE,
920 harbaum 240 osm_tag_get_by_key(OSM_TAG(rel), "type"),
921 harbaum 76 RELATION_COL_NAME, name,
922     RELATION_COL_MEMBERS, num,
923 harbaum 191 RELATION_COL_DATA, rel,
924 harbaum 76 -1);
925 harbaum 191
926     if(relation) relation = relation->next;
927     if(rchain) rchain = rchain->next;
928 harbaum 76 }
929 harbaum 191
930     if(rchain)
931     osm_relation_chain_free(rchain);
932 harbaum 76
933     g_object_unref(context->store);
934    
935 harbaum 315 list_set_static_buttons(context->list, LIST_BTN_NEW | LIST_BTN_WIDE,
936 harbaum 266 G_CALLBACK(on_relation_add), G_CALLBACK(on_relation_edit),
937     G_CALLBACK(on_relation_remove), context);
938 harbaum 76
939 harbaum 148 list_set_user_buttons(context->list,
940 harbaum 177 LIST_BUTTON_USER0, _("Members"), G_CALLBACK(on_relation_members),
941 harbaum 264 LIST_BUTTON_USER1, _("Select"), G_CALLBACK(on_relation_select),
942 harbaum 148 0);
943 harbaum 76
944 harbaum 77 relation_list_selected(context, NULL);
945 harbaum 76
946 harbaum 148 return context->list;
947 harbaum 76 }
948    
949     /* a global view on all relations */
950 harbaum 191 void relation_list(GtkWidget *parent, appdata_t *appdata, object_t *object) {
951 harbaum 76 relation_context_t *context = g_new0(relation_context_t, 1);
952     context->appdata = appdata;
953    
954 harbaum 191 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 harbaum 76 context->dialog =
963 harbaum 191 misc_dialog_new(MISC_DIALOG_LARGE, str,
964     GTK_WINDOW(parent),
965 harbaum 167 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
966     NULL);
967 harbaum 191
968     g_free(str);
969 harbaum 76
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 harbaum 153
979 harbaum 76 gtk_widget_show_all(context->dialog);
980 harbaum 264 gtk_dialog_run(GTK_DIALOG(context->dialog));
981 harbaum 153
982 harbaum 76 gtk_widget_destroy(context->dialog);
983     g_free(context);
984     }
985 achadwick 99
986     // vim:et:ts=8:sw=2:sts=2:ai