Contents of /trunk/src/relation_edit.c

Parent Directory Parent Directory | Revision Log Revision Log


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