Contents of /trunk/src/relation_edit.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 73 - (hide annotations)
Thu Feb 12 14:27:52 2009 UTC (15 years, 3 months ago) by harbaum
File MIME type: text/plain
File size: 15590 byte(s)
Create new relations, more intuitive role editing
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     relation_item_t *item;
26     appdata_t *appdata;
27     GtkWidget *dialog, *view;
28     GtkListStore *store;
29     GtkWidget *but_add, *but_edit, *but_remove;
30     } relitem_context_t;
31    
32     enum {
33     RELITEM_COL_SELECTED = 0,
34     RELITEM_COL_TYPE,
35     RELITEM_COL_ROLE,
36     RELITEM_COL_NAME,
37     RELITEM_COL_DATA,
38     RELITEM_NUM_COLS
39     };
40    
41     typedef struct role_chain_s {
42     char *role;
43     struct role_chain_s *next;
44     } role_chain_t;
45    
46 harbaum 73 static gboolean relation_add_item(GtkWidget *parent,
47 harbaum 1 relation_t *relation, relation_item_t *item) {
48     role_chain_t *chain = NULL, **chainP = &chain;
49    
50     printf("add item of type %d to relation #%ld\n",
51     item->type, relation->id);
52    
53     /* ask the user for the role of the new item in this relation */
54    
55     /* collect roles first */
56     member_t *member = relation->member;
57     while(member) {
58     if(member->role) {
59     /* check if this role has already been saved */
60     gboolean already_stored = FALSE;
61     role_chain_t *crole = chain;
62     while(crole) {
63     if(strcasecmp(crole->role, member->role) == 0) already_stored = TRUE;
64     crole = crole->next;
65     }
66    
67     /* not stored yet: attach it */
68     if(!already_stored) {
69     *chainP = g_new0(role_chain_t, 1);
70     (*chainP)->role = g_strdup(member->role);
71     chainP = &(*chainP)->next;
72     }
73     }
74     member = member->next;
75     }
76    
77     /* ------------------ role dialog ---------------- */
78     GtkWidget *dialog = gtk_dialog_new_with_buttons(_("Select role"),
79     GTK_WINDOW(parent), GTK_DIALOG_MODAL,
80     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
81     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
82     NULL);
83    
84     gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_ACCEPT);
85    
86     char *type = osm_tag_get_by_key(relation->tag, "type");
87    
88     char *info_str = NULL;
89     if(type) info_str = g_strdup_printf(_("In relation of type: %s"), type);
90     else info_str = g_strdup_printf(_("In relation #%ld"), relation->id);
91     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
92     gtk_label_new(info_str));
93     g_free(info_str);
94    
95     char *name = osm_tag_get_by_key(relation->tag, "name");
96     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     gtk_box_pack_start_defaults(GTK_BOX(hbox), gtk_label_new(_("Role:")));
102    
103     GtkWidget *entry = NULL;
104     if(chain) {
105     entry = gtk_combo_box_entry_new_text();
106    
107     /* fill combo box with presets */
108     while(chain) {
109     role_chain_t *next = chain->next;
110     gtk_combo_box_append_text(GTK_COMBO_BOX(entry), chain->role);
111     g_free(chain);
112     chain = next;
113     }
114     } else
115     entry = gtk_entry_new();
116    
117     gtk_box_pack_start_defaults(GTK_BOX(hbox), entry);
118     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
119    
120     gtk_widget_show_all(dialog);
121     if(GTK_RESPONSE_ACCEPT != gtk_dialog_run(GTK_DIALOG(dialog))) {
122     printf("user clicked cancel\n");
123     gtk_widget_destroy(dialog);
124 harbaum 73 return FALSE;
125 harbaum 1 }
126    
127     printf("user clicked ok\n");
128    
129     /* get role from dialog */
130     char *ptr = NULL;
131    
132     if(GTK_IS_COMBO_BOX(entry))
133     ptr = gtk_combo_box_get_active_text(GTK_COMBO_BOX(entry));
134     else
135     ptr = (char*)gtk_entry_get_text(GTK_ENTRY(entry));
136    
137     char *role = NULL;
138     if(ptr && strlen(ptr)) role = g_strdup(ptr);
139    
140     gtk_widget_destroy(dialog);
141    
142     /* search end of member chain */
143     member_t **memberP = &relation->member;
144     while(*memberP) memberP = &(*memberP)->next;
145    
146     /* create new member */
147     *memberP = g_new0(member_t, 1);
148     (*memberP)->type = item->type;
149     (*memberP)->role = role;
150     switch(item->type) {
151     case NODE:
152     (*memberP)->node = item->node;
153     break;
154     case WAY:
155     (*memberP)->way = item->way;
156     break;
157     case RELATION:
158     (*memberP)->relation = item->relation;
159     break;
160     default:
161     g_assert((item->type == NODE)||(item->type == WAY)||
162     (item->type == RELATION));
163     break;
164     }
165    
166     relation->flags |= OSM_FLAG_DIRTY;
167 harbaum 73 return TRUE;
168 harbaum 1 }
169    
170 harbaum 73 static void relation_remove_item(relation_t *relation, relation_item_t *item) {
171    
172     printf("remove item of type %d from relation #%ld\n",
173     item->type, relation->id);
174    
175     member_t **member = &relation->member;
176     while(*member) {
177     if(((*member)->type == item->type) &&
178     (((item->type == NODE) && (item->node == (*member)->node)) ||
179     ((item->type == WAY) && (item->way == (*member)->way)) ||
180     ((item->type == RELATION) && (item->relation == (*member)->relation)))) {
181    
182     member_t *next = (*member)->next;
183     osm_member_free(*member);
184     *member = next;
185    
186     relation->flags |= OSM_FLAG_DIRTY;
187    
188     return;
189     } else
190     member = &(*member)->next;
191     }
192     g_assert(0);
193     }
194    
195     static void relation_list_selected(relitem_context_t *context,
196     gboolean selected) {
197    
198     if(context->but_remove)
199     gtk_widget_set_sensitive(context->but_remove, FALSE);
200     if(context->but_edit)
201     gtk_widget_set_sensitive(context->but_edit, selected);
202     }
203    
204     static gboolean
205     relation_list_selection_func(GtkTreeSelection *selection, GtkTreeModel *model,
206     GtkTreePath *path, gboolean path_currently_selected,
207     gpointer userdata) {
208     relitem_context_t *context = (relitem_context_t*)userdata;
209     GtkTreeIter iter;
210    
211     if(gtk_tree_model_get_iter(model, &iter, path)) {
212     g_assert(gtk_tree_path_get_depth(path) == 1);
213     relation_list_selected(context, TRUE);
214     }
215    
216     return TRUE; /* allow selection state to change */
217     }
218    
219 harbaum 1 static void on_relation_add(GtkWidget *but, relitem_context_t *context) {
220 harbaum 73 /* create a new relation */
221    
222     relation_t *relation = osm_relation_new();
223     if(!info_dialog(context->dialog, context->appdata, relation)) {
224     printf("tag edit cancelled\n");
225     osm_relation_free(relation);
226     } else {
227     osm_relation_attach(context->appdata->osm, relation);
228    
229     /* add to list */
230    
231     /* append a row for the new data */
232     /* try to find something descriptive */
233     char *name = osm_tag_get_by_key(relation->tag, "name");
234     if(!name) name = osm_tag_get_by_key(relation->tag, "ref");
235    
236     GtkTreeIter iter;
237     gtk_list_store_append(context->store, &iter);
238     gtk_list_store_set(context->store, &iter,
239     RELITEM_COL_SELECTED, FALSE,
240     RELITEM_COL_TYPE,
241     osm_tag_get_by_key(relation->tag, "type"),
242     RELITEM_COL_NAME, name,
243     RELITEM_COL_DATA, relation,
244     -1);
245    
246     gtk_tree_selection_select_iter(gtk_tree_view_get_selection(
247     GTK_TREE_VIEW(context->view)), &iter);
248    
249     /* scroll to end */
250     // GtkAdjustment *adj = gtk_scrolled_window_get_vadjustment();
251     /* xyz */
252     }
253 harbaum 1 }
254    
255     static relation_t *get_selection(relitem_context_t *context) {
256     GtkTreeSelection *selection;
257     GtkTreeModel *model;
258     GtkTreeIter iter;
259    
260     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view));
261     if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
262     relation_t *relation;
263     gtk_tree_model_get(model, &iter, RELITEM_COL_DATA, &relation, -1);
264     return(relation);
265     }
266     return NULL;
267     }
268    
269     static void on_relation_edit(GtkWidget *but, relitem_context_t *context) {
270     relation_t *sel = get_selection(context);
271     if(!sel) return;
272    
273     printf("edit relation #%ld\n", sel->id);
274    
275     info_dialog(context->dialog, context->appdata, sel);
276     }
277    
278     static void on_relation_remove(GtkWidget *but, relitem_context_t *context) {
279     relation_t *sel = get_selection(context);
280     if(!sel) return;
281    
282     printf("remove relation #%ld\n", sel->id);
283     }
284    
285 harbaum 73 static char *relitem_get_role_in_relation(relation_item_t *item, relation_t *relation) {
286     member_t *member = relation->member;
287     while(member) {
288     switch(member->type) {
289    
290     case NODE:
291     if((item->type == NODE) && (item->node == member->node))
292     return member->role;
293     break;
294    
295     case WAY:
296     if((item->type == WAY) && (item->way == member->way))
297     return member->role;
298     break;
299    
300     default:
301     break;
302     }
303     member = member->next;
304     }
305     return NULL;
306     }
307    
308 harbaum 1 static void
309     relitem_toggled(GtkCellRendererToggle *cell, const gchar *path_str,
310     relitem_context_t *context) {
311     GtkTreePath *path;
312     GtkTreeIter iter;
313    
314     path = gtk_tree_path_new_from_string(path_str);
315     gtk_tree_model_get_iter(GTK_TREE_MODEL(context->store), &iter, path);
316 harbaum 73 gtk_tree_path_free(path);
317 harbaum 1
318     /* get current enabled flag */
319     gboolean enabled;
320 harbaum 73 relation_t *relation = NULL;
321 harbaum 1 gtk_tree_model_get(GTK_TREE_MODEL(context->store), &iter,
322 harbaum 73 RELITEM_COL_SELECTED, &enabled,
323     RELITEM_COL_DATA, &relation,
324     -1);
325 harbaum 1
326 harbaum 73 if(!enabled) {
327     printf("will now become be part of this relation\n");
328     if(relation_add_item(context->dialog, relation, context->item))
329     gtk_list_store_set(context->store, &iter,
330     RELITEM_COL_SELECTED, TRUE,
331     RELITEM_COL_ROLE,
332     relitem_get_role_in_relation(context->item, relation),
333     -1);
334     } else {
335     printf("item will not be part of this relation anymore\n");
336     relation_remove_item(relation, context->item);
337     gtk_list_store_set(context->store, &iter,
338     RELITEM_COL_SELECTED, FALSE,
339     RELITEM_COL_ROLE, NULL,
340     -1);
341     }
342 harbaum 1 }
343    
344     static gboolean relitem_is_in_relation(relation_item_t *item, relation_t *relation) {
345     member_t *member = relation->member;
346     while(member) {
347     switch(member->type) {
348    
349     case NODE:
350     if((item->type == NODE) && (item->node == member->node))
351     return TRUE;
352     break;
353    
354     case WAY:
355     if((item->type == WAY) && (item->way == member->way))
356     return TRUE;
357     break;
358    
359     default:
360     break;
361     }
362     member = member->next;
363     }
364     return FALSE;
365     }
366    
367     static GtkWidget *relation_list(relitem_context_t *context) {
368     GtkWidget *vbox = gtk_vbox_new(FALSE,3);
369     context->view = gtk_tree_view_new();
370    
371     gtk_tree_selection_set_select_function(
372     gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view)),
373     relation_list_selection_func,
374     context, NULL);
375    
376    
377     /* --- "selected" column --- */
378     GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new();
379     g_signal_connect(renderer, "toggled", G_CALLBACK(relitem_toggled), context);
380     gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),
381     -1, _(""), renderer,
382     "active", RELITEM_COL_SELECTED,
383     NULL);
384    
385     /* --- "Type" column --- */
386     renderer = gtk_cell_renderer_text_new();
387     gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),
388     -1, _("Type"), renderer, "text", RELITEM_COL_TYPE, NULL);
389    
390     /* --- "Role" column --- */
391     renderer = gtk_cell_renderer_text_new();
392     gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),
393     -1, _("Role"), renderer, "text", RELITEM_COL_ROLE, NULL);
394    
395     /* --- "Name" column --- */
396     renderer = gtk_cell_renderer_text_new();
397     g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
398     GtkTreeViewColumn *column =
399     gtk_tree_view_column_new_with_attributes(_("Name"), renderer,
400     "text", RELITEM_COL_NAME, NULL);
401     gtk_tree_view_column_set_expand(column, TRUE);
402     gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
403    
404    
405     /* build and fill the store */
406     context->store = gtk_list_store_new(RELITEM_NUM_COLS,
407     G_TYPE_BOOLEAN, G_TYPE_STRING, G_TYPE_STRING,
408     G_TYPE_STRING, G_TYPE_POINTER);
409    
410     gtk_tree_view_set_model(GTK_TREE_VIEW(context->view),
411     GTK_TREE_MODEL(context->store));
412    
413     GtkTreeIter iter;
414     relation_t *relation = context->appdata->osm->relation;
415     while(relation) {
416     /* try to find something descriptive */
417     char *name = osm_tag_get_by_key(relation->tag, "name");
418     if(!name) name = osm_tag_get_by_key(relation->tag, "ref");
419    
420     /* Append a row and fill in some data */
421     gtk_list_store_append(context->store, &iter);
422     gtk_list_store_set(context->store, &iter,
423     RELITEM_COL_SELECTED, relitem_is_in_relation(context->item, relation),
424     RELITEM_COL_TYPE, osm_tag_get_by_key(relation->tag, "type"),
425     RELITEM_COL_ROLE, relitem_get_role_in_relation(context->item, relation),
426     RELITEM_COL_NAME, name,
427     RELITEM_COL_DATA, relation,
428     -1);
429    
430     relation = relation->next;
431     }
432    
433     g_object_unref(context->store);
434    
435     /* put it into a scrolled window */
436     GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
437     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
438     GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
439     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
440     GTK_SHADOW_ETCHED_IN);
441     gtk_container_add(GTK_CONTAINER(scrolled_window), context->view);
442    
443     gtk_box_pack_start_defaults(GTK_BOX(vbox), scrolled_window);
444    
445     /* ------- button box ------------ */
446    
447     GtkWidget *hbox = gtk_hbox_new(TRUE,3);
448    
449     context->but_add = gtk_button_new_with_label(_("Add..."));
450 harbaum 73 // gtk_widget_set_sensitive(context->but_add, FALSE);
451 harbaum 1 gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_add);
452     gtk_signal_connect(GTK_OBJECT(context->but_add), "clicked",
453     GTK_SIGNAL_FUNC(on_relation_add), context);
454    
455     context->but_edit = gtk_button_new_with_label(_("Edit..."));
456     gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_edit);
457     gtk_signal_connect(GTK_OBJECT(context->but_edit), "clicked",
458     GTK_SIGNAL_FUNC(on_relation_edit), context);
459    
460     context->but_remove = gtk_button_new_with_label(_("Remove"));
461     gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_remove);
462     gtk_signal_connect(GTK_OBJECT(context->but_remove), "clicked",
463     GTK_SIGNAL_FUNC(on_relation_remove), context);
464    
465     relation_list_selected(context, FALSE);
466    
467     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
468     return vbox;
469     }
470    
471     void relation_add_dialog(appdata_t *appdata, relation_item_t *relitem) {
472     relitem_context_t *context = g_new0(relitem_context_t, 1);
473     map_t *map = appdata->map;
474     g_assert(map);
475    
476     context->appdata = appdata;
477     context->item = relitem;
478    
479     char *str = NULL;
480     switch(relitem->type) {
481     case NODE:
482     str = g_strdup_printf(_("Relations for node #%ld"), relitem->node->id);
483     break;
484     case WAY:
485     str = g_strdup_printf(_("Relations for way #%ld"), relitem->way->id);
486     break;
487     default:
488     g_assert((relitem->type == NODE) || (relitem->type == WAY));
489     break;
490     }
491    
492     context->dialog = gtk_dialog_new_with_buttons(str,
493     GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
494 harbaum 73 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
495 harbaum 1 NULL);
496     g_free(str);
497    
498     gtk_dialog_set_default_response(GTK_DIALOG(context->dialog),
499     GTK_RESPONSE_ACCEPT);
500    
501     /* making the dialog a little wider makes it less "crowded" */
502     #ifdef USE_HILDON
503     gtk_window_set_default_size(GTK_WINDOW(context->dialog), 500, 300);
504     #else
505     gtk_window_set_default_size(GTK_WINDOW(context->dialog), 400, 200);
506     #endif
507     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
508     relation_list(context), TRUE, TRUE, 0);
509    
510     /* ----------------------------------- */
511    
512     gtk_widget_show_all(context->dialog);
513 harbaum 73 gtk_dialog_run(GTK_DIALOG(context->dialog));
514 harbaum 1 gtk_widget_destroy(context->dialog);
515    
516     g_free(context);
517     }