Contents of /trunk/src/relation_edit.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 75 - (show annotations)
Thu Feb 12 19:58:20 2009 UTC (15 years, 3 months ago) by harbaum
File MIME type: text/plain
File size: 16350 byte(s)
WMS crash bug fix. Relation creation and deletion
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 static gboolean relation_add_item(GtkWidget *parent,
47 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 return FALSE;
125 }
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 return TRUE;
168 }
169
170 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, selected);
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 static void on_relation_add(GtkWidget *but, relitem_context_t *context) {
220 /* 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 }
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 /* remove the selected relation */
279 static void on_relation_remove(GtkWidget *but, relitem_context_t *context) {
280 relation_t *sel = get_selection(context);
281 if(!sel) return;
282
283 printf("remove relation #%ld\n", sel->id);
284
285 int members = 0;
286 member_t *member = sel->member;
287 while(member) {
288 members++;
289 member = member->next;
290 }
291
292 if(members)
293 if(!yes_no_f(context->dialog, NULL, 0, 0,
294 _("Delete non-empty relation?"),
295 _("This relation still has %d members. "
296 "Delete it anyway?"), members))
297 return;
298
299 /* first remove selected row from list */
300 GtkTreeIter iter;
301 GtkTreeSelection *selection =
302 gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view));
303 if(gtk_tree_selection_get_selected(selection, NULL, &iter))
304 gtk_list_store_remove(context->store, &iter);
305
306 /* then really delete it */
307 osm_relation_delete(context->appdata->osm, sel, FALSE);
308
309 relation_list_selected(context, FALSE);
310 }
311
312 static char *relitem_get_role_in_relation(relation_item_t *item, relation_t *relation) {
313 member_t *member = relation->member;
314 while(member) {
315 switch(member->type) {
316
317 case NODE:
318 if((item->type == NODE) && (item->node == member->node))
319 return member->role;
320 break;
321
322 case WAY:
323 if((item->type == WAY) && (item->way == member->way))
324 return member->role;
325 break;
326
327 default:
328 break;
329 }
330 member = member->next;
331 }
332 return NULL;
333 }
334
335 static void
336 relitem_toggled(GtkCellRendererToggle *cell, const gchar *path_str,
337 relitem_context_t *context) {
338 GtkTreePath *path;
339 GtkTreeIter iter;
340
341 path = gtk_tree_path_new_from_string(path_str);
342 gtk_tree_model_get_iter(GTK_TREE_MODEL(context->store), &iter, path);
343 gtk_tree_path_free(path);
344
345 /* get current enabled flag */
346 gboolean enabled;
347 relation_t *relation = NULL;
348 gtk_tree_model_get(GTK_TREE_MODEL(context->store), &iter,
349 RELITEM_COL_SELECTED, &enabled,
350 RELITEM_COL_DATA, &relation,
351 -1);
352
353 if(!enabled) {
354 printf("will now become be part of this relation\n");
355 if(relation_add_item(context->dialog, relation, context->item))
356 gtk_list_store_set(context->store, &iter,
357 RELITEM_COL_SELECTED, TRUE,
358 RELITEM_COL_ROLE,
359 relitem_get_role_in_relation(context->item, relation),
360 -1);
361 } else {
362 printf("item will not be part of this relation anymore\n");
363 relation_remove_item(relation, context->item);
364 gtk_list_store_set(context->store, &iter,
365 RELITEM_COL_SELECTED, FALSE,
366 RELITEM_COL_ROLE, NULL,
367 -1);
368 }
369 }
370
371 static gboolean relitem_is_in_relation(relation_item_t *item, relation_t *relation) {
372 member_t *member = relation->member;
373 while(member) {
374 switch(member->type) {
375
376 case NODE:
377 if((item->type == NODE) && (item->node == member->node))
378 return TRUE;
379 break;
380
381 case WAY:
382 if((item->type == WAY) && (item->way == member->way))
383 return TRUE;
384 break;
385
386 default:
387 break;
388 }
389 member = member->next;
390 }
391 return FALSE;
392 }
393
394 static GtkWidget *relation_list(relitem_context_t *context) {
395 GtkWidget *vbox = gtk_vbox_new(FALSE,3);
396 context->view = gtk_tree_view_new();
397
398 gtk_tree_selection_set_select_function(
399 gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view)),
400 relation_list_selection_func,
401 context, NULL);
402
403
404 /* --- "selected" column --- */
405 GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new();
406 g_signal_connect(renderer, "toggled", G_CALLBACK(relitem_toggled), context);
407 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),
408 -1, _(""), renderer,
409 "active", RELITEM_COL_SELECTED,
410 NULL);
411
412 /* --- "Type" column --- */
413 renderer = gtk_cell_renderer_text_new();
414 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),
415 -1, _("Type"), renderer, "text", RELITEM_COL_TYPE, NULL);
416
417 /* --- "Role" column --- */
418 renderer = gtk_cell_renderer_text_new();
419 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),
420 -1, _("Role"), renderer, "text", RELITEM_COL_ROLE, NULL);
421
422 /* --- "Name" column --- */
423 renderer = gtk_cell_renderer_text_new();
424 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
425 GtkTreeViewColumn *column =
426 gtk_tree_view_column_new_with_attributes(_("Name"), renderer,
427 "text", RELITEM_COL_NAME, NULL);
428 gtk_tree_view_column_set_expand(column, TRUE);
429 gtk_tree_view_insert_column(GTK_TREE_VIEW(context->view), column, -1);
430
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 gtk_tree_view_set_model(GTK_TREE_VIEW(context->view),
438 GTK_TREE_MODEL(context->store));
439
440 GtkTreeIter iter;
441 relation_t *relation = context->appdata->osm->relation;
442 while(relation) {
443 /* try to find something descriptive */
444 char *name = osm_tag_get_by_key(relation->tag, "name");
445 if(!name) name = osm_tag_get_by_key(relation->tag, "ref");
446
447 /* Append a row and fill in some data */
448 gtk_list_store_append(context->store, &iter);
449 gtk_list_store_set(context->store, &iter,
450 RELITEM_COL_SELECTED, relitem_is_in_relation(context->item, relation),
451 RELITEM_COL_TYPE, osm_tag_get_by_key(relation->tag, "type"),
452 RELITEM_COL_ROLE, relitem_get_role_in_relation(context->item, relation),
453 RELITEM_COL_NAME, name,
454 RELITEM_COL_DATA, relation,
455 -1);
456
457 relation = relation->next;
458 }
459
460 g_object_unref(context->store);
461
462 /* put it into a scrolled window */
463 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
464 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
465 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
466 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
467 GTK_SHADOW_ETCHED_IN);
468 gtk_container_add(GTK_CONTAINER(scrolled_window), context->view);
469
470 gtk_box_pack_start_defaults(GTK_BOX(vbox), scrolled_window);
471
472 /* ------- button box ------------ */
473
474 GtkWidget *hbox = gtk_hbox_new(TRUE,3);
475
476 context->but_add = gtk_button_new_with_label(_("Add..."));
477 // gtk_widget_set_sensitive(context->but_add, FALSE);
478 gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_add);
479 gtk_signal_connect(GTK_OBJECT(context->but_add), "clicked",
480 GTK_SIGNAL_FUNC(on_relation_add), context);
481
482 context->but_edit = gtk_button_new_with_label(_("Edit..."));
483 gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_edit);
484 gtk_signal_connect(GTK_OBJECT(context->but_edit), "clicked",
485 GTK_SIGNAL_FUNC(on_relation_edit), context);
486
487 context->but_remove = gtk_button_new_with_label(_("Remove"));
488 gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_remove);
489 gtk_signal_connect(GTK_OBJECT(context->but_remove), "clicked",
490 GTK_SIGNAL_FUNC(on_relation_remove), context);
491
492 relation_list_selected(context, FALSE);
493
494 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
495 return vbox;
496 }
497
498 void relation_add_dialog(appdata_t *appdata, relation_item_t *relitem) {
499 relitem_context_t *context = g_new0(relitem_context_t, 1);
500 map_t *map = appdata->map;
501 g_assert(map);
502
503 context->appdata = appdata;
504 context->item = relitem;
505
506 char *str = NULL;
507 switch(relitem->type) {
508 case NODE:
509 str = g_strdup_printf(_("Relations for node #%ld"), relitem->node->id);
510 break;
511 case WAY:
512 str = g_strdup_printf(_("Relations for way #%ld"), relitem->way->id);
513 break;
514 default:
515 g_assert((relitem->type == NODE) || (relitem->type == WAY));
516 break;
517 }
518
519 context->dialog = gtk_dialog_new_with_buttons(str,
520 GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
521 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
522 NULL);
523 g_free(str);
524
525 gtk_dialog_set_default_response(GTK_DIALOG(context->dialog),
526 GTK_RESPONSE_ACCEPT);
527
528 /* making the dialog a little wider makes it less "crowded" */
529 #ifdef USE_HILDON
530 gtk_window_set_default_size(GTK_WINDOW(context->dialog), 500, 300);
531 #else
532 gtk_window_set_default_size(GTK_WINDOW(context->dialog), 400, 200);
533 #endif
534 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
535 relation_list(context), TRUE, TRUE, 0);
536
537 /* ----------------------------------- */
538
539 gtk_widget_show_all(context->dialog);
540 gtk_dialog_run(GTK_DIALOG(context->dialog));
541 gtk_widget_destroy(context->dialog);
542
543 g_free(context);
544 }