Contents of /trunk/src/relation_edit.c

Parent Directory Parent Directory | Revision Log Revision Log


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