Parent Directory | Revision Log
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 | /* --------------------- misc local helper functions ---------------- */ |
23 | |
24 | static void transfer_relations(osm_t *osm, way_t *dst, way_t *src) { |
25 | |
26 | /* transfer relation memberships from the src way to the dst one */ |
27 | relation_chain_t *rchain = osm_way_to_relation(osm, src); |
28 | |
29 | while(rchain) { |
30 | relation_chain_t *next = rchain->next; |
31 | printf("way #%ld is part of relation #%ld\n", src->id, |
32 | rchain->relation->id); |
33 | |
34 | /* make new member of the same relation */ |
35 | |
36 | /* walk member chain. save role of way if its being found. */ |
37 | member_t **member = &rchain->relation->member; |
38 | char *role = NULL; |
39 | while(*member) { |
40 | /* save role of way */ |
41 | if(((*member)->type == WAY) && ((*member)->way == src)) |
42 | role = (*member)->role; |
43 | member = &(*member)->next; |
44 | } |
45 | |
46 | printf(" adding way #%ld to relation\n", dst->id); |
47 | *member = g_new0(member_t, 1); |
48 | (*member)->type = WAY; |
49 | (*member)->way = dst; |
50 | if(role) (*member)->role = g_strdup(role); |
51 | member = &(*member)->next; |
52 | |
53 | rchain->relation->flags |= OSM_FLAG_DIRTY; |
54 | |
55 | g_free(rchain); |
56 | rchain = next; |
57 | } |
58 | } |
59 | |
60 | |
61 | /* combine tags from src to dst and combine them in a useful manner */ |
62 | /* erase all source tags */ |
63 | static gboolean combine_tags(tag_t **dst, tag_t *src) { |
64 | gboolean conflict = FALSE; |
65 | tag_t *dst_orig = *dst; |
66 | |
67 | /* ---------- transfer tags from way[1] to way[0] ----------- */ |
68 | while(*dst) dst = &((*dst)->next); /* find end of target tag list */ |
69 | while(src) { |
70 | /* check if same key but with different value is present, */ |
71 | /* ignoring the created_by tags */ |
72 | if(!osm_is_creator_tag(src) && |
73 | osm_tag_key_other_value_present(dst_orig, src)) |
74 | conflict = TRUE; |
75 | |
76 | /* don't copy "created_by" and "source" tag or tags that already */ |
77 | /* exist in identical form */ |
78 | if(osm_is_creator_tag(src) || |
79 | osm_tag_key_and_value_present(dst_orig, src)) { |
80 | tag_t *next = src->next; |
81 | osm_tag_free(src); |
82 | src = next; |
83 | } else { |
84 | *dst = src; |
85 | src = src->next; |
86 | dst = &((*dst)->next); |
87 | *dst = NULL; |
88 | } |
89 | } |
90 | return conflict; |
91 | } |
92 | |
93 | /* -------------------------- way_add ----------------------- */ |
94 | |
95 | void map_edit_way_add_begin(map_t *map, way_t *way_sel) { |
96 | if(way_sel) printf("previously selected way is #%ld\n", way_sel->id); |
97 | |
98 | g_assert(!map->action.way); |
99 | map->action.way = osm_way_new(); |
100 | map->action.extending = NULL; |
101 | } |
102 | |
103 | void map_edit_way_add_segment(map_t *map, gint x, gint y) { |
104 | |
105 | /* convert mouse position to canvas (world) position */ |
106 | canvas_window2world(map->canvas, x, y, &x, &y); |
107 | |
108 | /* check if this was a double click. This is the case if */ |
109 | /* the last node placed is less than 5 pixels from the current */ |
110 | /* position */ |
111 | node_t *lnode = osm_way_get_last_node(map->action.way); |
112 | if(lnode && (map->state->zoom * sqrt((lnode->lpos.x-x)*(lnode->lpos.x-x)+ |
113 | (lnode->lpos.y-y)*(lnode->lpos.y-y))) < 5) { |
114 | #if 0 |
115 | printf("detected double click -> simulate ok click\n"); |
116 | map_hl_touchnode_clear(map); |
117 | map_action_ok(map->appdata); |
118 | #else |
119 | printf("detected double click -> ignore it as accidential\n"); |
120 | #endif |
121 | } else { |
122 | |
123 | /* use the existing node if one was touched */ |
124 | node_t *node = map_hl_touchnode_get_node(map); |
125 | if(node) { |
126 | printf(" re-using node #%ld\n", node->id); |
127 | map_hl_touchnode_clear(map); |
128 | |
129 | g_assert(map->action.way); |
130 | |
131 | /* check whether this node is first or last one of a different way */ |
132 | way_t *touch_way = NULL; |
133 | way_chain_t *way_chain = osm_node_to_way(map->appdata->osm, node); |
134 | while(way_chain) { |
135 | way_chain_t *next = way_chain->next; |
136 | |
137 | if(node == osm_way_get_last_node(way_chain->way)) { |
138 | printf(" way #%ld ends with this node\n", way_chain->way->id); |
139 | if(!touch_way) touch_way = way_chain->way; |
140 | } |
141 | |
142 | if(node == osm_way_get_first_node(way_chain->way)) { |
143 | printf(" way #%ld starts with this node\n", way_chain->way->id); |
144 | if(!touch_way) touch_way = way_chain->way; |
145 | } |
146 | |
147 | g_free(way_chain); |
148 | way_chain = next; |
149 | } |
150 | |
151 | /* remeber this way as this may be the last node placed */ |
152 | /* and we might want to join this with this other way */ |
153 | map->action.ends_on = touch_way; |
154 | |
155 | /* is this the first node the user places? */ |
156 | if(!map->action.way->node_chain) { |
157 | map->action.extending = touch_way; |
158 | |
159 | if(map->action.extending) { |
160 | if(!yes_no_f(GTK_WIDGET(map->appdata->window), |
161 | map->appdata, MISC_AGAIN_ID_EXTEND_WAY, 0, |
162 | _("Extend way?"), |
163 | _("Do you want to extend the way present at this location?"))) |
164 | map->action.extending = NULL; |
165 | else |
166 | /* there are immediately enough nodes for a valid way */ |
167 | icon_bar_map_cancel_ok(map->appdata, TRUE, TRUE); |
168 | } |
169 | } |
170 | |
171 | } else { |
172 | /* the current way doesn't end on another way if we are just placing */ |
173 | /* a new node */ |
174 | map->action.ends_on = NULL; |
175 | |
176 | if(!osm_position_within_bounds(map->appdata->osm, x, y)) |
177 | map_outside_error(map->appdata); |
178 | else |
179 | node = osm_node_new(map->appdata->osm, x, y); |
180 | } |
181 | |
182 | if(node) { |
183 | g_assert(map->action.way); |
184 | osm_way_append_node(map->action.way, node); |
185 | |
186 | switch(osm_node_chain_length(map->action.way->node_chain)) { |
187 | case 1: |
188 | /* replace "place first node..." message */ |
189 | statusbar_set(map->appdata, _("Place next node of way"), FALSE); |
190 | break; |
191 | |
192 | case 2: |
193 | /* two nodes are enough for a valid way */ |
194 | icon_bar_map_cancel_ok(map->appdata, TRUE, TRUE); |
195 | break; |
196 | |
197 | default: |
198 | break; |
199 | } |
200 | |
201 | /* remove prior version of this way */ |
202 | map_item_chain_destroy(&map->action.way->map_item_chain); |
203 | |
204 | /* draw current way */ |
205 | josm_elemstyles_colorize_way(map->style, map->action.way); |
206 | map_way_draw(map, map->action.way); |
207 | } |
208 | } |
209 | } |
210 | |
211 | void map_edit_way_add_cancel(map_t *map) { |
212 | |
213 | printf(" removing temporary way\n"); |
214 | g_assert(map->action.way); |
215 | |
216 | /* remove all nodes that have been created for this way */ |
217 | /* (their way count will be 0 after removing the way) */ |
218 | node_chain_t *chain = map->action.way->node_chain; |
219 | while(chain) { |
220 | node_chain_t *next = chain->next; |
221 | |
222 | printf(" node #%ld (used by %d)\n", |
223 | chain->node->id, chain->node->ways); |
224 | |
225 | chain->node->ways--; |
226 | if(!chain->node->ways && (chain->node->id == ID_ILLEGAL)) { |
227 | printf(" -> freeing temp node\n"); |
228 | osm_node_free(&map->appdata->icon, chain->node); |
229 | } |
230 | g_free(chain); |
231 | chain = next; |
232 | } |
233 | map->action.way->node_chain = NULL; |
234 | |
235 | /* remove ways visual representation */ |
236 | map_item_chain_destroy(&map->action.way->map_item_chain); |
237 | |
238 | osm_way_free(map->action.way); |
239 | map->action.way = NULL; |
240 | } |
241 | |
242 | void map_edit_way_add_ok(map_t *map) { |
243 | g_assert(map->action.way); |
244 | |
245 | /* transfer all nodes that have been created for this way */ |
246 | /* into the node chain */ |
247 | |
248 | /* (their way count will be 0 after removing the way) */ |
249 | node_chain_t *chain = map->action.way->node_chain; |
250 | while(chain) { |
251 | printf(" node #%ld (used by %d)\n", |
252 | chain->node->id, chain->node->ways); |
253 | |
254 | /* a node may have been a stand-alone node before, so remove its */ |
255 | /* visual representation as its now drawn as part of the way */ |
256 | /* (if at all) */ |
257 | if(chain->node->id != ID_ILLEGAL) |
258 | map_item_chain_destroy(&chain->node->map_item_chain); |
259 | |
260 | map_node_draw(map, chain->node); |
261 | |
262 | /* we can be sure that no node gets inserted twice (even if twice in */ |
263 | /* the ways chain) because it gets assigned a non-ID_ILLEGAL id when */ |
264 | /* being moved to the osm node chain */ |
265 | if(chain->node->id == ID_ILLEGAL) |
266 | osm_node_attach(map->appdata->osm, chain->node); |
267 | |
268 | chain = chain->next; |
269 | } |
270 | |
271 | /* attach to existing way if the user requested so */ |
272 | gboolean reverse = FALSE; |
273 | if(map->action.extending) { |
274 | node_t *nfirst = map->action.way->node_chain->node; |
275 | |
276 | printf(" request to extend way #%ld\n", map->action.extending->id); |
277 | |
278 | if(osm_way_get_first_node(map->action.extending) == nfirst) { |
279 | printf(" need to prepend\n"); |
280 | osm_way_reverse(map->action.extending); |
281 | reverse = TRUE; |
282 | } else if(osm_way_get_last_node(map->action.extending) == nfirst) |
283 | printf(" need to append\n"); |
284 | |
285 | /* search end of way to be extended */ |
286 | node_chain_t *chain = map->action.extending->node_chain; |
287 | g_assert(chain); |
288 | while(chain->next) chain = chain->next; |
289 | |
290 | /* skip first node of new way as its the same as the last one of the */ |
291 | /* way we are attaching it to */ |
292 | chain->next = map->action.way->node_chain->next; |
293 | |
294 | /* terminate new way afer first node */ |
295 | map->action.way->node_chain->next = NULL; |
296 | |
297 | /* erase and free new way (now only containing the first node anymore) */ |
298 | map_item_chain_destroy(&map->action.way->map_item_chain); |
299 | osm_way_free(map->action.way); |
300 | |
301 | map->action.way = map->action.extending; |
302 | map->action.way->flags |= OSM_FLAG_DIRTY; |
303 | |
304 | /* and undo reversion of required */ |
305 | if(reverse) |
306 | osm_way_reverse(map->action.way); |
307 | |
308 | } else { |
309 | /* now move the way itself into the main data structure */ |
310 | osm_way_attach(map->appdata->osm, map->action.way); |
311 | } |
312 | |
313 | /* we might already be working on the "ends_on" way as we may */ |
314 | /* be extending it. Joining the same way doesn't make sense. */ |
315 | if(map->action.ends_on && (map->action.ends_on == map->action.way)) { |
316 | printf(" the new way ends on itself -> don't join itself\n"); |
317 | map->action.ends_on = NULL; |
318 | } |
319 | |
320 | if(map->action.ends_on) |
321 | if(!yes_no_f(GTK_WIDGET(map->appdata->window), |
322 | map->appdata, MISC_AGAIN_ID_EXTEND_WAY_END, 0, |
323 | _("Join way?"), |
324 | _("Do you want to join the way present at this location?"))) |
325 | map->action.ends_on = NULL; |
326 | |
327 | if(map->action.ends_on) { |
328 | printf(" this new way ends on another way\n"); |
329 | |
330 | /* If reverse is true the node in question is the first one */ |
331 | /* of the newly created way. Thus it is reversed again before */ |
332 | /* attaching and the result is finally reversed once more */ |
333 | |
334 | /* this is slightly more complex as this time two full tagged */ |
335 | /* ways may be involved as the new way may be an extended existing */ |
336 | /* way being connected to another way. This happens if you connect */ |
337 | /* two existing ways using a new way between them */ |
338 | |
339 | if (reverse) osm_way_reverse(map->action.way); |
340 | |
341 | /* and open dialog to resolve tag collisions if necessary */ |
342 | if(combine_tags(&map->action.way->tag, map->action.ends_on->tag)) |
343 | messagef(GTK_WIDGET(map->appdata->window), _("Way tag conflict"), |
344 | _("The resulting way contains some conflicting tags. " |
345 | "Please solve these.")); |
346 | |
347 | map->action.ends_on->tag = NULL; |
348 | |
349 | /* make way member of all relations ends_on already is */ |
350 | transfer_relations(map->appdata->osm, map->action.way, map->action.ends_on); |
351 | |
352 | /* check if we have to reverse (again?) to match the way order */ |
353 | if(osm_way_get_last_node(map->action.ends_on) == |
354 | osm_way_get_last_node(map->action.way)) { |
355 | |
356 | printf(" need to prepend ends_on\n"); |
357 | |
358 | /* need to reverse ends_on way */ |
359 | osm_way_reverse(map->action.ends_on); |
360 | reverse = !reverse; |
361 | } |
362 | |
363 | /* search end of node chain */ |
364 | node_chain_t *chain = map->action.way->node_chain; |
365 | g_assert(chain); |
366 | while(chain->next) chain = chain->next; |
367 | |
368 | /* attach ends_on way to way but omit first node */ |
369 | chain->next = map->action.ends_on->node_chain->next; |
370 | map->action.ends_on->node_chain->next = NULL; |
371 | |
372 | /* erase and free ends_on (now only containing the first node anymore) */ |
373 | map_way_delete(map->appdata, map->action.ends_on); |
374 | |
375 | if(reverse) osm_way_reverse(map->action.way); |
376 | } |
377 | |
378 | /* remove prior version of this way */ |
379 | map_item_chain_destroy(&map->action.way->map_item_chain); |
380 | |
381 | /* draw the updated way */ |
382 | map_way_draw(map, map->action.way); |
383 | |
384 | map_way_select(map->appdata, map->action.way); |
385 | |
386 | map->action.way = NULL; |
387 | |
388 | /* let the user specify some tags for the new way */ |
389 | info_dialog(GTK_WIDGET(map->appdata->window), map->appdata, NULL); |
390 | } |
391 | |
392 | /* -------------------------- way_node_add ----------------------- */ |
393 | |
394 | void map_edit_way_node_add_highlight(map_t *map, map_item_t *item, |
395 | gint x, gint y) { |
396 | if(map_item_is_selected_way(map, item)) { |
397 | gint nx, ny; |
398 | canvas_window2world(map->canvas, x, y, &nx, &ny); |
399 | if(canvas_item_get_segment(item->item, nx, ny) >= 0) |
400 | map_hl_cursor_draw(map, x, y, FALSE, map->style->node.radius); |
401 | } |
402 | } |
403 | |
404 | void map_edit_way_node_add(map_t *map, gint x, gint y) { |
405 | /* check if we are still hovering above the selected way */ |
406 | map_item_t *item = map_item_at(map, x, y); |
407 | if(item && map_item_is_selected_way(map, item)) { |
408 | /* convert mouse position to canvas (world) position */ |
409 | canvas_window2world(map->canvas, x, y, &x, &y); |
410 | gint insert_after = canvas_item_get_segment(item->item, x, y)+1; |
411 | if(insert_after > 0) { |
412 | /* create new node */ |
413 | node_t* node = osm_node_new(map->appdata->osm, x, y); |
414 | osm_node_attach(map->appdata->osm, node); |
415 | |
416 | /* insert it into ways chain of nodes */ |
417 | way_t *way = item->object.way; |
418 | |
419 | /* search correct position */ |
420 | node_chain_t **chain = &way->node_chain; |
421 | while(insert_after--) { |
422 | g_assert(*chain); |
423 | chain = &(*chain)->next; |
424 | } |
425 | |
426 | /* and actually do the insertion */ |
427 | node_chain_t *new_chain_item = g_new0(node_chain_t, 1); |
428 | new_chain_item->node = node; |
429 | new_chain_item->next = *chain; |
430 | *chain = new_chain_item; |
431 | |
432 | /* clear selection */ |
433 | map_item_deselect(map->appdata); |
434 | |
435 | /* remove prior version of this way */ |
436 | map_item_chain_destroy(&way->map_item_chain); |
437 | |
438 | /* draw the updated way */ |
439 | map_way_draw(map, way); |
440 | |
441 | /* remember that this node is contained in one way */ |
442 | node->ways=1; |
443 | |
444 | /* and that the way needs to be uploaded */ |
445 | way->flags |= OSM_FLAG_DIRTY; |
446 | |
447 | /* put gui into idle state */ |
448 | map_action_set(map->appdata, MAP_ACTION_IDLE); |
449 | |
450 | /* and redo it */ |
451 | map_way_select(map->appdata, way); |
452 | } |
453 | } |
454 | } |
455 | |
456 | /* -------------------------- way_node_cut ----------------------- */ |
457 | |
458 | void map_edit_way_cut_highlight(map_t *map, map_item_t *item, gint x, gint y) { |
459 | |
460 | if(map_item_is_selected_way(map, item)) { |
461 | gint nx, ny, seg; |
462 | canvas_window2world(map->canvas, x, y, &nx, &ny); |
463 | seg = canvas_item_get_segment(item->item, nx, ny); |
464 | if(seg >= 0) { |
465 | gint x0, y0, x1, y1; |
466 | canvas_item_get_segment_pos(item->item, seg, &x0, &y0, &x1, &y1); |
467 | |
468 | gint width = (item->object.way->draw.flags & |
469 | OSM_DRAW_FLAG_BG)? |
470 | 2*item->object.way->draw.bg.width: |
471 | 3*item->object.way->draw.width; |
472 | map_hl_segment_draw(map, width, x0, y0, x1, y1); |
473 | } |
474 | } else if(map_item_is_selected_node(map, item)) { |
475 | node_t *nfirst = osm_way_get_first_node(map->selected.object.way); |
476 | node_t *nlast = osm_way_get_last_node(map->selected.object.way); |
477 | |
478 | /* cutting a way at its first or last node doesn't make much sense ... */ |
479 | if((nfirst != item->object.node) && (nlast != item->object.node)) |
480 | map_hl_cursor_draw(map, item->object.node->lpos.x, item->object.node->lpos.y, |
481 | TRUE, 2*map->style->node.radius); |
482 | } |
483 | } |
484 | |
485 | /* cut the currently selected way at the current cursor position */ |
486 | void map_edit_way_cut(map_t *map, gint x, gint y) { |
487 | |
488 | /* check if we are still hovering above the selected way */ |
489 | map_item_t *item = map_item_at(map, x, y); |
490 | if(item && (map_item_is_selected_way(map, item) || |
491 | map_item_is_selected_node(map, item))) { |
492 | gboolean cut_at_node = map_item_is_selected_node(map, item); |
493 | |
494 | /* convert mouse position to canvas (world) position */ |
495 | canvas_window2world(map->canvas, x, y, &x, &y); |
496 | |
497 | gint cut_at = -1; |
498 | way_t *way = NULL; |
499 | if(cut_at_node) { |
500 | printf(" cut at node\n"); |
501 | |
502 | /* node must not be first or last node of way */ |
503 | g_assert(map->selected.object.type == WAY); |
504 | |
505 | if((osm_way_get_first_node(map->selected.object.way) != |
506 | item->object.node) && |
507 | (osm_way_get_last_node(map->selected.object.way) != |
508 | item->object.node)) { |
509 | way = map->selected.object.way; |
510 | |
511 | cut_at = 0; |
512 | node_chain_t *chain = way->node_chain; |
513 | while(chain && chain->node != item->object.node) { |
514 | chain = chain->next; |
515 | cut_at++; |
516 | } |
517 | |
518 | } else |
519 | printf(" won't cut as it's last or first node\n"); |
520 | |
521 | } else { |
522 | printf(" cut at segment\n"); |
523 | cut_at = canvas_item_get_segment(item->item, x, y); |
524 | if(cut_at >= 0) way = item->object.way; |
525 | } |
526 | |
527 | if(way) { |
528 | /* create a duplicate of the currently selected way */ |
529 | way_t *new = osm_way_new(); |
530 | |
531 | /* if this is a closed way, reorder (rotate) it, so the */ |
532 | /* place to cut is adjecent to the begin/end of the way. */ |
533 | /* this prevents a cut polygon to be split into two ways */ |
534 | g_assert(way->node_chain); |
535 | if(way->node_chain->node == osm_way_get_last_node(way)) { |
536 | printf("CLOSED WAY -> rotate by %d\n", cut_at); |
537 | osm_way_rotate(way, cut_at); |
538 | cut_at = 0; |
539 | } |
540 | |
541 | /* ------------ copy all tags ------------- */ |
542 | new->tag = osm_tags_copy(way->tag, TRUE); |
543 | |
544 | /* ---- transfer relation membership from way to new ----- */ |
545 | transfer_relations(map->appdata->osm, new, way); |
546 | |
547 | /* move parts of node_chain to the new way */ |
548 | printf(" moving everthing after segment %d to new way\n", cut_at); |
549 | |
550 | node_chain_t *chain = way->node_chain; |
551 | while(cut_at--) { |
552 | g_assert(chain); |
553 | chain = chain->next; |
554 | } |
555 | |
556 | /* attach remaining nodes to new way */ |
557 | new->node_chain = chain->next; |
558 | |
559 | /* terminate remainig chain on old way */ |
560 | chain->next = NULL; |
561 | |
562 | /* if we cut at a node, this node is now part of both ways. so */ |
563 | /* create a copy of the last node of the old way and prepend it to */ |
564 | /* the new way */ |
565 | if(cut_at_node) { |
566 | node_chain_t *first = g_new0(node_chain_t, 1); |
567 | first->next = new->node_chain; |
568 | first->node = osm_way_get_last_node(way); |
569 | first->node->ways++; |
570 | new->node_chain = first; |
571 | } |
572 | |
573 | /* now move the way itself into the main data structure */ |
574 | osm_way_attach(map->appdata->osm, new); |
575 | |
576 | /* clear selection */ |
577 | map_item_deselect(map->appdata); |
578 | |
579 | /* remove prior version of this way */ |
580 | printf("remove visible version of way #%ld\n", way->id); |
581 | map_item_chain_destroy(&way->map_item_chain); |
582 | |
583 | /* swap chains of the old way is to be destroyed due to a lack */ |
584 | /* of nodes */ |
585 | if(osm_way_number_of_nodes(way) < 2) { |
586 | printf("swapping ways to avoid destruction of original way\n"); |
587 | node_chain_t *tmp = way->node_chain; |
588 | way->node_chain = new->node_chain; |
589 | new->node_chain = tmp; |
590 | } |
591 | |
592 | /* the way may still only consist of a single node. */ |
593 | /* remove it then */ |
594 | if(osm_way_number_of_nodes(way) < 2) { |
595 | printf("original way has less than 2 nodes left, deleting it\n"); |
596 | map_way_delete(map->appdata, way); |
597 | item = NULL; |
598 | } else { |
599 | printf("original way still has %d nodes\n", |
600 | osm_way_number_of_nodes(way)); |
601 | |
602 | /* draw the updated old way */ |
603 | josm_elemstyles_colorize_way(map->style, way); |
604 | map_way_draw(map, way); |
605 | |
606 | /* remember that the way needs to be uploaded */ |
607 | way->flags |= OSM_FLAG_DIRTY; |
608 | } |
609 | |
610 | if(osm_way_number_of_nodes(new) < 2) { |
611 | printf("new way has less than 2 nodes, deleting it\n"); |
612 | map_way_delete(map->appdata, new); |
613 | new = NULL; |
614 | } else { |
615 | |
616 | /* colorize the new way before drawing */ |
617 | josm_elemstyles_colorize_way(map->style, new); |
618 | map_way_draw(map, new); |
619 | } |
620 | |
621 | /* put gui into idle state */ |
622 | map_action_set(map->appdata, MAP_ACTION_IDLE); |
623 | |
624 | /* and redo selection if way still exists */ |
625 | if(item) |
626 | map_way_select(map->appdata, way); |
627 | else if(new) |
628 | map_way_select(map->appdata, new); |
629 | } |
630 | } |
631 | } |
632 | |
633 | void map_edit_node_move(appdata_t *appdata, map_item_t *map_item, |
634 | gint ex, gint ey) { |
635 | |
636 | map_t *map = appdata->map; |
637 | osm_t *osm = appdata->osm; |
638 | |
639 | g_assert(map_item->object.type == NODE); |
640 | node_t *node = map_item->object.node; |
641 | |
642 | printf("released dragged node #%ld\n", node->id); |
643 | printf(" was at %d %d (%f %f)\n", |
644 | node->lpos.x, node->lpos.y, |
645 | node->pos.lat, node->pos.lon); |
646 | |
647 | |
648 | /* check if it was dropped onto another node */ |
649 | node_t *touchnode = map_hl_touchnode_get_node(map); |
650 | gboolean joined_with_touchnode = FALSE; |
651 | |
652 | if(touchnode) { |
653 | map_hl_touchnode_clear(map); |
654 | |
655 | printf(" dropped onto node #%ld\n", touchnode->id); |
656 | |
657 | if(yes_no_f(GTK_WIDGET(appdata->window), |
658 | appdata, MISC_AGAIN_ID_JOIN_NODES, 0, |
659 | _("Join nodes?"), |
660 | _("Do you want to join the dragged node with the one " |
661 | "you dropped it on?"))) { |
662 | |
663 | /* the touchnode vanishes and is replaced by the node the */ |
664 | /* user dropped onto it */ |
665 | joined_with_touchnode = TRUE; |
666 | |
667 | /* use touchnodes position */ |
668 | node->lpos = touchnode->lpos; |
669 | node->pos = touchnode->pos; |
670 | |
671 | way_t *way = appdata->osm->way; |
672 | while(way) { |
673 | node_chain_t *chain = way->node_chain; |
674 | while(chain) { |
675 | if(chain->node == touchnode) { |
676 | printf(" found node in way #%ld\n", way->id); |
677 | |
678 | /* replace by node */ |
679 | chain->node = node; |
680 | |
681 | /* and adjust way references of both nodes */ |
682 | node->ways++; |
683 | touchnode->ways--; |
684 | |
685 | way->flags |= OSM_FLAG_DIRTY; |
686 | } |
687 | chain = chain->next; |
688 | } |
689 | way = way->next; |
690 | } |
691 | |
692 | /* replace node in relations */ |
693 | relation_t *relation = appdata->osm->relation; |
694 | while(relation) { |
695 | member_t *member = relation->member; |
696 | while(member) { |
697 | if(member->type == NODE && member->node == touchnode) { |
698 | printf(" found node in relation #%ld\n", relation->id); |
699 | |
700 | /* replace by node */ |
701 | member->node = node; |
702 | |
703 | relation->flags |= OSM_FLAG_DIRTY; |
704 | } |
705 | member = member->next; |
706 | } |
707 | relation = relation->next; |
708 | } |
709 | |
710 | gboolean conflict = combine_tags(&node->tag, touchnode->tag); |
711 | touchnode->tag = NULL; |
712 | |
713 | /* touchnode must not have any references to ways anymore */ |
714 | g_assert(!touchnode->ways); |
715 | |
716 | /* delete touchnode */ |
717 | /* remove it visually from the screen */ |
718 | map_item_chain_destroy(&touchnode->map_item_chain); |
719 | |
720 | /* and remove it from the data structures */ |
721 | osm_node_remove_from_relation(appdata->osm, touchnode); |
722 | osm_node_delete(appdata->osm, &appdata->icon, touchnode, FALSE, TRUE); |
723 | |
724 | /* and open dialog to resolve tag collisions if necessary */ |
725 | if(conflict) |
726 | messagef(GTK_WIDGET(appdata->window), _("Node tag conflict"), |
727 | _("The resulting node contains some conflicting tags. " |
728 | "Please solve these.")); |
729 | |
730 | /* check whether this will also join two ways */ |
731 | printf(" checking if node is end of way\n"); |
732 | guint ways2join_cnt = 0; |
733 | way_t *ways2join[2] = { NULL, NULL }; |
734 | way = appdata->osm->way; |
735 | while(way) { |
736 | if(osm_way_ends_with_node(way, node)) { |
737 | if(ways2join_cnt < 2) |
738 | ways2join[ways2join_cnt] = way; |
739 | |
740 | printf(" way #%ld ends with this node\n", way->id); |
741 | ways2join_cnt++; |
742 | } |
743 | way = way->next; |
744 | } |
745 | |
746 | if(ways2join_cnt > 2) { |
747 | messagef(GTK_WIDGET(appdata->window), _("Too many ways to join"), |
748 | _("More than two ways now end on this node. Joining more " |
749 | "than two ways is not yet implemented, sorry")); |
750 | |
751 | } else if(ways2join_cnt == 2) { |
752 | if(yes_no_f(GTK_WIDGET(appdata->window), |
753 | appdata, MISC_AGAIN_ID_JOIN_WAYS, 0, |
754 | _("Join ways?"), |
755 | _("Do you want to join the dragged way with the one " |
756 | "you dropped it on?"))) { |
757 | |
758 | printf(" about to join ways #%ld and #%ld\n", |
759 | ways2join[0]->id, ways2join[1]->id); |
760 | |
761 | /* way[1] gets destroyed and attached to way[0] */ |
762 | /* so check if way[1] is selected and exchainge ways then */ |
763 | /* so that way may stay selected */ |
764 | if((map->selected.object.type == WAY) && |
765 | (map->selected.object.way == ways2join[1])) { |
766 | printf(" swapping ways to keep selected one alive\n"); |
767 | way_t *tmp = ways2join[1]; |
768 | ways2join[1] = ways2join[0]; |
769 | ways2join[0] = tmp; |
770 | } |
771 | |
772 | /* take all nodes from way[1] and append them to way[0] */ |
773 | /* check if we have to append or prepend to way[0] */ |
774 | gboolean reverse = FALSE; |
775 | if(ways2join[0]->node_chain->node == node) { |
776 | /* make "prepend" to be "append" by reversing way[0] */ |
777 | printf(" target prepend -> reverse\n"); |
778 | reverse = TRUE; |
779 | osm_way_reverse(ways2join[0]); |
780 | } |
781 | |
782 | /* verify the common node is last in the target way */ |
783 | node_chain_t *chain = ways2join[0]->node_chain; |
784 | while(chain->next) chain = chain->next; |
785 | g_assert(chain->node == node); |
786 | g_assert(!chain->next); |
787 | |
788 | /* common node must be first in the chain to attach */ |
789 | if(ways2join[1]->node_chain->node != node) { |
790 | printf(" source reverse\n"); |
791 | osm_way_reverse(ways2join[1]); |
792 | } |
793 | |
794 | /* verify the common node is first in the source way */ |
795 | g_assert(ways2join[1]->node_chain->node == node); |
796 | |
797 | /* finally append source chain to target */ |
798 | g_assert(!chain->next); |
799 | chain->next = ways2join[1]->node_chain->next; |
800 | |
801 | ways2join[1]->node_chain->next = NULL; |
802 | |
803 | /* transfer tags from touchnode to node */ |
804 | |
805 | /* ---------- transfer tags from way[1] to way[0] ----------- */ |
806 | gboolean conflict = |
807 | combine_tags(&ways2join[0]->tag, ways2join[1]->tag); |
808 | ways2join[1]->tag = NULL; |
809 | |
810 | /* ---- transfer relation membership from way[1] to way[0] ----- */ |
811 | relation_chain_t *rchain = |
812 | osm_way_to_relation(appdata->osm, ways2join[1]); |
813 | |
814 | while(rchain) { |
815 | relation_chain_t *next = rchain->next; |
816 | printf("way[1] is part of relation #%ld\n", rchain->relation->id); |
817 | |
818 | /* make way[0] member of the same relation */ |
819 | |
820 | /* walk member chain. save role of way[1] if its being found. */ |
821 | /* end search either at end of chain or if way[0] was foind */ |
822 | /* as it's already a member of that relation */ |
823 | member_t **member = &rchain->relation->member; |
824 | char *role = NULL; |
825 | while(*member && |
826 | !(((*member)->type == WAY) && |
827 | ((*member)->way == ways2join[0]))) { |
828 | |
829 | /* save role of way[1] */ |
830 | if(((*member)->type == WAY) && ((*member)->way == ways2join[0])) |
831 | role = (*member)->role; |
832 | |
833 | member = &(*member)->next; |
834 | } |
835 | |
836 | if(*member) |
837 | printf(" both ways were members of this relation\n"); |
838 | else { |
839 | printf(" adding way[0] to relation\n"); |
840 | *member = g_new0(member_t, 1); |
841 | (*member)->type = WAY; |
842 | (*member)->way = ways2join[0]; |
843 | if(role) (*member)->role = g_strdup(role); |
844 | member = &(*member)->next; |
845 | |
846 | rchain->relation->flags |= OSM_FLAG_DIRTY; |
847 | } |
848 | |
849 | g_free(rchain); |
850 | rchain = next; |
851 | } |
852 | |
853 | |
854 | /* and open dialog to resolve tag collisions if necessary */ |
855 | if(conflict) |
856 | messagef(GTK_WIDGET(appdata->window), _("Way tag conflict"), |
857 | _("The resulting way contains some conflicting tags. " |
858 | "Please solve these.")); |
859 | |
860 | ways2join[0]->flags |= OSM_FLAG_DIRTY; |
861 | map_way_delete(appdata, ways2join[1]); |
862 | } |
863 | } |
864 | } |
865 | } |
866 | |
867 | /* the node either wasn't dropped into another one (touchnode) or */ |
868 | /* the user didn't want to join the nodes */ |
869 | if(!joined_with_touchnode) { |
870 | |
871 | /* finally update dragged nodes position */ |
872 | |
873 | /* convert mouse position to canvas (world) position */ |
874 | gint x, y; |
875 | canvas_window2world(map->canvas, ex, ey, &x, &y); |
876 | if(!osm_position_within_bounds(appdata->osm, x, y)) { |
877 | map_outside_error(appdata); |
878 | return; |
879 | } |
880 | |
881 | node->lpos.x = x; |
882 | node->lpos.y = y; |
883 | |
884 | /* convert screen position to lat/lon */ |
885 | lpos2pos(osm->bounds, &node->lpos, &node->pos); |
886 | |
887 | /* convert pos back to lpos to see rounding errors */ |
888 | pos2lpos(osm->bounds, &node->pos, &node->lpos); |
889 | |
890 | printf(" now at %d %d (%f %f)\n", |
891 | node->lpos.x, node->lpos.y, node->pos.lat, node->pos.lon); |
892 | } |
893 | |
894 | /* now update the visual representation of the node */ |
895 | |
896 | map_item_chain_destroy(&node->map_item_chain); |
897 | map_node_draw(map, node); |
898 | |
899 | /* visually update ways, node is part of */ |
900 | way_t *way = osm->way; |
901 | while(way) { |
902 | if(osm_node_in_way(way, node)) { |
903 | printf(" node is part of way #%ld, redraw!\n", way->id); |
904 | |
905 | /* remove prior version of this way */ |
906 | map_item_chain_destroy(&way->map_item_chain); |
907 | |
908 | /* draw current way */ |
909 | josm_elemstyles_colorize_way(map->style, way); |
910 | map_way_draw(map, way); |
911 | } |
912 | |
913 | way = way->next; |
914 | } |
915 | |
916 | /* and mark the node as dirty */ |
917 | node->flags |= OSM_FLAG_DIRTY; |
918 | |
919 | /* update highlight */ |
920 | map_highlight_refresh(appdata); |
921 | } |
922 | |
923 | /* -------------------------- way_reverse ----------------------- */ |
924 | |
925 | /* called from the "reverse" icon */ |
926 | void map_edit_way_reverse(appdata_t *appdata) { |
927 | /* work on local copy since de-selecting destroys the selection */ |
928 | map_item_t item = appdata->map->selected; |
929 | |
930 | /* deleting the selected item de-selects it ... */ |
931 | map_item_deselect(appdata); |
932 | |
933 | g_assert(item.object.type == WAY); |
934 | |
935 | osm_way_reverse(item.object.way); |
936 | guint n_tags_flipped = |
937 | osm_way_reverse_direction_sensitive_tags(item.object.way); |
938 | guint n_roles_flipped = |
939 | osm_way_reverse_direction_sensitive_roles(appdata->osm, item.object.way); |
940 | |
941 | item.object.way->flags |= OSM_FLAG_DIRTY; |
942 | map_way_select(appdata, item.object.way); |
943 | |
944 | // Flash a message about any side-effects |
945 | char *msg = NULL; |
946 | if (n_tags_flipped && !n_roles_flipped) { |
947 | msg = g_strdup_printf(ngettext("%d tag updated", "%d tags updated", |
948 | n_tags_flipped), |
949 | n_tags_flipped); |
950 | } |
951 | else if (!n_tags_flipped && n_roles_flipped) { |
952 | msg = g_strdup_printf(ngettext("%d relation updated", |
953 | "%d relations updated", |
954 | n_roles_flipped), |
955 | n_roles_flipped); |
956 | } |
957 | else if (n_tags_flipped && n_roles_flipped) { |
958 | char *msg1 = g_strdup_printf(ngettext("%d tag", "%d tags", |
959 | n_tags_flipped), |
960 | n_tags_flipped); |
961 | char *msg2 = g_strdup_printf(ngettext("%d relation", "%d relations", |
962 | n_roles_flipped), |
963 | n_roles_flipped); |
964 | msg = g_strdup_printf(_("%s & %s updated"), msg1, msg2); |
965 | g_free(msg1); |
966 | g_free(msg2); |
967 | } |
968 | if (msg) { |
969 | banner_show_info(appdata, msg); |
970 | g_free(msg); |
971 | } |
972 | } |
973 | |
974 | // vim:et:ts=8:sw=2:sts=2:ai |