Contents of /trunk/src/relation_edit.c

Parent Directory Parent Directory | Revision Log Revision Log


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