Contents of /trunk/src/relation_edit.c

Parent Directory Parent Directory | Revision Log Revision Log


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