Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 235 - (show annotations)
Tue Jul 21 14:31:16 2009 UTC (14 years, 10 months ago) by harbaum
File MIME type: text/plain
File size: 69413 byte(s)
Some little work on undo framework
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 #include <gdk/gdkkeysyms.h>
23
24
25 #undef DESTROY_WAIT_FOR_GTK
26
27 static void map_statusbar(map_t *map, map_item_t *map_item) {
28 char *item_str = NULL;
29 item_id_t id = ID_ILLEGAL;
30 tag_t *tag = NULL;
31 char *str = NULL;
32
33 switch(map_item->object.type) {
34 case NODE:
35 item_str = "Node";
36 id = map_item->object.node->id;
37 tag = map_item->object.node->tag;
38 break;
39
40 case WAY:
41 item_str = "Way";
42 id = map_item->object.way->id;
43 tag = map_item->object.way->tag;
44 break;
45
46 case RELATION:
47 item_str = "Relation";
48 id = map_item->object.relation->id;
49 tag = map_item->object.relation->tag;
50 break;
51
52 default:
53 break;
54 }
55
56 gboolean collision = FALSE;
57 tag_t *tags = tag;
58 while(tag) {
59 if(!collision && info_tag_key_collision(tags, tag))
60 collision = TRUE;
61 tag = tag->next;
62 }
63
64 str = osm_object_get_speaking_name(&map_item->object);
65 statusbar_set(map->appdata, str, collision);
66 g_free(str);
67 }
68
69 void map_outside_error(appdata_t *appdata) {
70 errorf(GTK_WIDGET(appdata->window),
71 _("Items must not be placed outside the working area!"));
72 }
73
74 void map_item_chain_destroy(map_item_chain_t **chainP) {
75 if(!*chainP) {
76 printf("nothing to destroy!\n");
77 return;
78 }
79
80 #ifdef DESTROY_WAIT_FOR_GTK
81 map_item_chain_t *chain = *chainP;
82 while(chain) {
83 map_item_chain_t *next = chain->next;
84 canvas_item_destroy(chain->map_item->item);
85 chain = next;
86 }
87
88 /* wait until gtks event handling has actually destroyed this item */
89 printf("waiting for item destruction ");
90 while(gtk_events_pending() || *chainP) {
91 putchar('.');
92 gtk_main_iteration();
93 }
94 printf(" ok\n");
95
96 /* the callback routine connected to this item should have been */
97 /* called by now and it has set the chain to NULL */
98
99 #else
100 map_item_chain_t *chain = *chainP;
101 while(chain) {
102 map_item_chain_t *next = chain->next;
103 canvas_item_destroy(chain->map_item->item);
104
105 g_free(chain);
106 chain = next;
107 }
108 *chainP = NULL;
109 #endif
110 }
111
112 static void map_node_select(appdata_t *appdata, node_t *node) {
113 map_t *map = appdata->map;
114 map_item_t *map_item = &map->selected;
115
116 g_assert(!map->highlight);
117
118 map_item->object.type = NODE;
119 map_item->object.node = node;
120 map_item->highlight = FALSE;
121
122 /* node may not have any visible representation at all */
123 if(node->map_item_chain)
124 map_item->item = node->map_item_chain->map_item->item;
125 else
126 map_item->item = NULL;
127
128 map_statusbar(map, map_item);
129 icon_bar_map_item_selected(appdata, map_item, TRUE);
130
131 /* highlight node */
132 gint x = map_item->object.node->lpos.x, y = map_item->object.node->lpos.y;
133
134 /* create a copy of this map item and mark it as being a highlight */
135 map_item_t *new_map_item = g_new0(map_item_t, 1);
136 memcpy(new_map_item, map_item, sizeof(map_item_t));
137 new_map_item->highlight = TRUE;
138
139 float radius = map->style->highlight.width + map->style->node.radius;
140 if(!node->ways) radius += map->style->node.border_radius;
141 if(node->icon_buf && map->style->icon.enable &&
142 !appdata->settings->no_icons) {
143 gint w = gdk_pixbuf_get_width(map_item->object.node->icon_buf);
144 gint h = gdk_pixbuf_get_height(map_item->object.node->icon_buf);
145 /* icons are technically square, so a radius slightly bigger */
146 /* than sqrt(2)*MAX(w,h) should fit nicely */
147 radius = 0.75 * map->style->icon.scale * ((w>h)?w:h);
148 }
149
150 radius *= map->state->detail;
151 map_hl_circle_new(map, CANVAS_GROUP_NODES_HL, new_map_item,
152 x, y, radius, map->style->highlight.color);
153
154 if(!map_item->item) {
155 /* and draw a fake node */
156 new_map_item = g_new0(map_item_t, 1);
157 memcpy(new_map_item, map_item, sizeof(map_item_t));
158 new_map_item->highlight = TRUE;
159 map_hl_circle_new(map, CANVAS_GROUP_NODES_IHL, new_map_item,
160 x, y, map->style->node.radius,
161 map->style->highlight.node_color);
162 }
163 }
164
165 void map_way_select(appdata_t *appdata, way_t *way) {
166 map_t *map = appdata->map;
167 map_item_t *map_item = &map->selected;
168
169 g_assert(!map->highlight);
170
171 map_item->object.type = WAY;
172 map_item->object.way = way;
173 map_item->highlight = FALSE;
174 map_item->item = way->map_item_chain->map_item->item;
175
176 map_statusbar(map, map_item);
177 icon_bar_map_item_selected(appdata, map_item, TRUE);
178 gtk_widget_set_sensitive(appdata->menu_item_map_hide_sel, TRUE);
179
180 gint arrow_width = ((map_item->object.way->draw.flags & OSM_DRAW_FLAG_BG)?
181 map->style->highlight.width + map_item->object.way->draw.bg.width/2:
182 map->style->highlight.width + map_item->object.way->draw.width/2)
183 * map->state->detail;
184
185 node_chain_t *node_chain = map_item->object.way->node_chain;
186 node_t *last = NULL;
187 while(node_chain) {
188 map_item_t item;
189 item.object.type = NODE;
190 item.object.node = node_chain->node;
191
192 /* draw an arrow between every two nodes */
193 if(last) {
194 /* create a new map item for every arrow */
195 map_item_t *new_map_item = g_new0(map_item_t, 1);
196 new_map_item->object.type = WAY;
197 new_map_item->object.way = way;
198 new_map_item->highlight = TRUE;
199
200 struct { float x, y;} center, diff;
201 center.x = (last->lpos.x + node_chain->node->lpos.x)/2;
202 center.y = (last->lpos.y + node_chain->node->lpos.y)/2;
203 diff.x = node_chain->node->lpos.x - last->lpos.x;
204 diff.y = node_chain->node->lpos.y - last->lpos.y;
205
206 /* only draw arrow if there's sufficient space */
207 /* TODO: what if there's not enough space anywhere? */
208 float len = sqrt(pow(diff.x, 2)+pow(diff.y, 2));
209 if(len > map->style->highlight.arrow_limit*arrow_width) {
210 len /= arrow_width;
211 diff.x = diff.x / len;
212 diff.y = diff.y / len;
213
214 canvas_points_t *points = canvas_points_new(4);
215 points->coords[2*0+0] = points->coords[2*3+0] = center.x + diff.x;
216 points->coords[2*0+1] = points->coords[2*3+1] = center.y + diff.y;
217 points->coords[2*1+0] = center.x + diff.y - diff.x;
218 points->coords[2*1+1] = center.y - diff.x - diff.y;
219 points->coords[2*2+0] = center.x - diff.y - diff.x;
220 points->coords[2*2+1] = center.y + diff.x - diff.y;
221
222 map_hl_polygon_new(map, CANVAS_GROUP_WAYS_DIR, new_map_item,
223 points, map->style->highlight.arrow_color);
224
225 canvas_points_free(points);
226 }
227 }
228
229 if(!map_hl_item_is_highlighted(map, &item)) {
230
231 /* create a new map item for every node */
232 map_item_t *new_map_item = g_new0(map_item_t, 1);
233 new_map_item->object.type = NODE;
234 new_map_item->object.node = node_chain->node;
235 new_map_item->highlight = TRUE;
236
237 gint x = node_chain->node->lpos.x;
238 gint y = node_chain->node->lpos.y;
239
240 map_hl_circle_new(map, CANVAS_GROUP_NODES_IHL, new_map_item,
241 x, y, map->style->node.radius * map->state->detail,
242 map->style->highlight.node_color);
243 }
244
245 last = node_chain->node;
246 node_chain = node_chain->next;
247 }
248
249 /* a way needs at least 2 points to be drawn */
250 guint nodes = osm_way_number_of_nodes(way);
251 if(nodes > 1) {
252
253 /* allocate space for nodes */
254 canvas_points_t *points = canvas_points_new(nodes);
255
256 int node = 0;
257 node_chain = map_item->object.way->node_chain;
258 while(node_chain) {
259 canvas_point_set_pos(points, node++, &node_chain->node->lpos);
260 node_chain = node_chain->next;
261 }
262
263 /* create a copy of this map item and mark it as being a highlight */
264 map_item_t *new_map_item = g_new0(map_item_t, 1);
265 memcpy(new_map_item, map_item, sizeof(map_item_t));
266 new_map_item->highlight = TRUE;
267
268 map_hl_polyline_new(map, CANVAS_GROUP_WAYS_HL, new_map_item, points,
269 ((map_item->object.way->draw.flags & OSM_DRAW_FLAG_BG)?
270 2*map->style->highlight.width + map_item->object.way->draw.bg.width:
271 2*map->style->highlight.width + map_item->object.way->draw.width)
272 * map->state->detail, map->style->highlight.color);
273
274 canvas_points_free(points);
275 }
276 }
277
278 void map_relation_select(appdata_t *appdata, relation_t *relation) {
279 map_t *map = appdata->map;
280
281 printf("highlighting relation "ITEM_ID_FORMAT"\n", relation->id);
282
283 g_assert(!map->highlight);
284 map_highlight_t **hl = &map->highlight;
285
286 map_item_t *map_item = &map->selected;
287 map_item->object.type = RELATION;
288 map_item->object.relation = relation;
289 map_item->highlight = FALSE;
290 map_item->item = NULL;
291
292 map_statusbar(map, map_item);
293 icon_bar_map_item_selected(appdata, map_item, TRUE);
294
295 /* process all members */
296 member_t *member = relation->member;
297 while(member) {
298 canvas_item_t *item = NULL;
299
300 switch(member->object.type) {
301
302 case NODE: {
303 node_t *node = member->object.node;
304 printf(" -> node "ITEM_ID_FORMAT"\n", node->id);
305
306 item = canvas_circle_new(map->canvas, CANVAS_GROUP_NODES_HL,
307 node->lpos.x, node->lpos.y,
308 map->style->highlight.width + map->style->node.radius,
309 0, map->style->highlight.color, NO_COLOR);
310 } break;
311
312 case WAY: {
313 way_t *way = member->object.way;
314 /* a way needs at least 2 points to be drawn */
315 guint nodes = osm_way_number_of_nodes(way);
316 if(nodes > 1) {
317
318 /* allocate space for nodes */
319 canvas_points_t *points = canvas_points_new(nodes);
320
321 int node = 0;
322 node_chain_t *node_chain = way->node_chain;
323 while(node_chain) {
324 canvas_point_set_pos(points, node++, &node_chain->node->lpos);
325 node_chain = node_chain->next;
326 }
327
328 if(way->draw.flags & OSM_DRAW_FLAG_AREA)
329 item = canvas_polygon_new(map->canvas, CANVAS_GROUP_WAYS_HL, points, 0, 0,
330 map->style->highlight.color);
331 else
332 item = canvas_polyline_new(map->canvas, CANVAS_GROUP_WAYS_HL, points,
333 (way->draw.flags & OSM_DRAW_FLAG_BG)?
334 2*map->style->highlight.width + way->draw.bg.width:
335 2*map->style->highlight.width + way->draw.width,
336 map->style->highlight.color);
337
338 canvas_points_free(points);
339 } } break;
340
341 default:
342 break;
343 }
344
345 /* attach item to item chain */
346 if(item) {
347 *hl = g_new0(map_highlight_t, 1);
348 (*hl)->item = item;
349 hl = &(*hl)->next;
350 }
351
352 member = member->next;
353 }
354 }
355
356 static void map_object_select(appdata_t *appdata, object_t *object) {
357 switch(object->type) {
358 case NODE:
359 map_node_select(appdata, object->node);
360 break;
361 case WAY:
362 map_way_select(appdata, object->way);
363 break;
364 case RELATION:
365 map_relation_select(appdata, object->relation);
366 break;
367 default:
368 g_assert((object->type == NODE)||(object->type == RELATION)||
369 (object->type == WAY));
370 break;
371 }
372 }
373
374 void map_item_deselect(appdata_t *appdata) {
375
376 /* save tags for "last" function in info dialog */
377 if(appdata->map->selected.object.type == NODE) {
378 if(appdata->map->last_node_tags)
379 osm_tags_free(appdata->map->last_node_tags);
380
381 appdata->map->last_node_tags =
382 osm_tags_copy(appdata->map->selected.object.node->tag);
383 } else if(appdata->map->selected.object.type == WAY) {
384 if(appdata->map->last_way_tags)
385 osm_tags_free(appdata->map->last_way_tags);
386
387 appdata->map->last_way_tags =
388 osm_tags_copy(appdata->map->selected.object.way->tag);
389 }
390
391 /* remove statusbar message */
392 statusbar_set(appdata, NULL, FALSE);
393
394 /* disable/enable icons in icon bar */
395 icon_bar_map_item_selected(appdata, NULL, FALSE);
396 gtk_widget_set_sensitive(appdata->menu_item_map_hide_sel, FALSE);
397
398 /* remove highlight */
399 map_hl_remove(appdata);
400
401 /* forget about selection */
402 appdata->map->selected.object.type = ILLEGAL;
403 }
404
405 /* called whenever a map item is to be destroyed */
406 static gint map_item_destroy_event(GtkWidget *widget, gpointer data) {
407 map_item_t *map_item = (map_item_t*)data;
408
409 // printf("destroying map_item @ %p\n", map_item);
410
411 #ifdef DESTROY_WAIT_FOR_GTK
412 /* remove item from nodes/ways map_item_chain */
413 map_item_chain_t **chain = NULL;
414 if(map_item->object.type == NODE)
415 chain = &map_item->object.node->map_item_chain;
416 else if(map_item->object.type == WAY)
417 chain = &map_item->object.way->map_item_chain;
418
419 /* there must be a chain with content, otherwise things are broken */
420 g_assert(chain);
421 g_assert(*chain);
422
423 /* search current map_item, ... */
424 while(*chain && (*chain)->map_item != map_item)
425 chain = &(*chain)->next;
426
427 g_assert(*chain);
428
429 /* ... remove it from chain and free it */
430 map_item_chain_t *tmp = *chain;
431 *chain = (*chain)->next;
432
433 g_free(tmp);
434 #endif
435
436 g_free(map_item);
437 return FALSE;
438 }
439
440 static canvas_item_t *map_node_new(map_t *map, node_t *node, gint radius,
441 gint width, canvas_color_t fill, canvas_color_t border) {
442
443 map_item_t *map_item = g_new0(map_item_t, 1);
444 map_item->object.type = NODE;
445 map_item->object.node = node;
446
447 if(!node->icon_buf || !map->style->icon.enable ||
448 map->appdata->settings->no_icons)
449 map_item->item = canvas_circle_new(map->canvas, CANVAS_GROUP_NODES,
450 node->lpos.x, node->lpos.y, radius, width, fill, border);
451 else
452 map_item->item = canvas_image_new(map->canvas, CANVAS_GROUP_NODES,
453 node->icon_buf,
454 node->lpos.x - map->style->icon.scale/2 * map->state->detail *
455 gdk_pixbuf_get_width(node->icon_buf),
456 node->lpos.y - map->style->icon.scale/2 * map->state->detail *
457 gdk_pixbuf_get_height(node->icon_buf),
458 map->state->detail * map->style->icon.scale,
459 map->state->detail * map->style->icon.scale);
460
461 canvas_item_set_zoom_max(map_item->item,
462 node->zoom_max / (2 * map->state->detail));
463
464 /* attach map_item to nodes map_item_chain */
465 map_item_chain_t **chain = &node->map_item_chain;
466 while(*chain) chain = &(*chain)->next;
467 *chain = g_new0(map_item_chain_t, 1);
468 (*chain)->map_item = map_item;
469
470 canvas_item_set_user_data(map_item->item, map_item);
471
472 canvas_item_destroy_connect(map_item->item,
473 G_CALLBACK(map_item_destroy_event), map_item);
474
475 return map_item->item;
476 }
477
478 /* in the rare case that a way consists of only one node, it is */
479 /* drawn as a circle. This e.g. happens when drawing a new way */
480 static canvas_item_t *map_way_single_new(map_t *map, way_t *way, gint radius,
481 gint width, canvas_color_t fill, canvas_color_t border) {
482
483 map_item_t *map_item = g_new0(map_item_t, 1);
484 map_item->object.type = WAY;
485 map_item->object.way = way;
486 map_item->item = canvas_circle_new(map->canvas, CANVAS_GROUP_WAYS,
487 way->node_chain->node->lpos.x, way->node_chain->node->lpos.y,
488 radius, width, fill, border);
489
490 // TODO: decide: do we need canvas_item_set_zoom_max() here too?
491
492 /* attach map_item to nodes map_item_chain */
493 map_item_chain_t **chain = &way->map_item_chain;
494 while(*chain) chain = &(*chain)->next;
495 *chain = g_new0(map_item_chain_t, 1);
496 (*chain)->map_item = map_item;
497
498 canvas_item_set_user_data(map_item->item, map_item);
499
500 canvas_item_destroy_connect(map_item->item,
501 G_CALLBACK(map_item_destroy_event), map_item);
502
503 return map_item->item;
504 }
505
506 static canvas_item_t *map_way_new(map_t *map, canvas_group_t group,
507 way_t *way, canvas_points_t *points, gint width,
508 canvas_color_t color, canvas_color_t fill_color) {
509 map_item_t *map_item = g_new0(map_item_t, 1);
510 map_item->object.type = WAY;
511 map_item->object.way = way;
512
513 if(way->draw.flags & OSM_DRAW_FLAG_AREA) {
514 if(map->style->area.color & 0xff)
515 map_item->item = canvas_polygon_new(map->canvas, group, points,
516 width, color, fill_color);
517 else
518 map_item->item = canvas_polyline_new(map->canvas, group, points,
519 width, color);
520 } else {
521 map_item->item = canvas_polyline_new(map->canvas, group, points, width, color);
522 }
523
524 canvas_item_set_zoom_max(map_item->item,
525 way->draw.zoom_max / (2 * map->state->detail));
526
527 /* a ways outline itself is never dashed */
528 if (group != CANVAS_GROUP_WAYS_OL)
529 if (way->draw.dashed)
530 canvas_item_set_dashed(map_item->item, width, way->draw.dash_length);
531
532 /* attach map_item to ways map_item_chain */
533 map_item_chain_t **chain = &way->map_item_chain;
534 while(*chain) chain = &(*chain)->next;
535 *chain = g_new0(map_item_chain_t, 1);
536 (*chain)->map_item = map_item;
537
538 canvas_item_set_user_data(map_item->item, map_item);
539
540 canvas_item_destroy_connect(map_item->item,
541 G_CALLBACK(map_item_destroy_event), map_item);
542
543 return map_item->item;
544 }
545
546 void map_show_node(map_t *map, node_t *node) {
547 map_node_new(map, node, map->style->node.radius, 0,
548 map->style->node.color, 0);
549 }
550
551 void map_way_draw(map_t *map, way_t *way) {
552
553 /* don't draw a way that's not there anymore */
554 if(way->flags & (OSM_FLAG_DELETED | OSM_FLAG_HIDDEN))
555 return;
556
557 /* allocate space for nodes */
558 /* a way needs at least 2 points to be drawn */
559 guint nodes = osm_way_number_of_nodes(way);
560 if(nodes == 1) {
561 /* draw a single dot where this single node is */
562 map_way_single_new(map, way, map->style->node.radius, 0,
563 map->style->node.color, 0);
564 } else {
565 canvas_points_t *points = canvas_points_new(nodes);
566
567 int node = 0;
568 node_chain_t *node_chain = way->node_chain;
569 while(node_chain) {
570 canvas_point_set_pos(points, node++, &node_chain->node->lpos);
571 node_chain = node_chain->next;
572 }
573
574 /* draw way */
575 float width = way->draw.width * map->state->detail;
576
577 if(way->draw.flags & OSM_DRAW_FLAG_AREA) {
578 map_way_new(map, CANVAS_GROUP_POLYGONS, way, points,
579 width, way->draw.color, way->draw.area.color);
580 } else {
581
582 if(way->draw.flags & OSM_DRAW_FLAG_BG) {
583 map_way_new(map, CANVAS_GROUP_WAYS_INT, way, points,
584 width, way->draw.color, NO_COLOR);
585
586 map_way_new(map, CANVAS_GROUP_WAYS_OL, way, points,
587 way->draw.bg.width * map->state->detail,
588 way->draw.bg.color, NO_COLOR);
589
590 } else
591 map_way_new(map, CANVAS_GROUP_WAYS, way, points,
592 width, way->draw.color, NO_COLOR);
593 }
594 canvas_points_free(points);
595 }
596 }
597
598 void map_node_draw(map_t *map, node_t *node) {
599 /* don't draw a node that's not there anymore */
600 if(node->flags & OSM_FLAG_DELETED)
601 return;
602
603 if(!node->ways)
604 map_node_new(map, node,
605 map->style->node.radius * map->state->detail,
606 map->style->node.border_radius * map->state->detail,
607 map->style->node.fill_color,
608 map->style->node.color);
609
610 else if(map->style->node.show_untagged || osm_node_has_tag(node))
611 map_node_new(map, node,
612 map->style->node.radius * map->state->detail, 0,
613 map->style->node.color, 0);
614 }
615
616 static void map_item_draw(map_t *map, map_item_t *map_item) {
617 switch(map_item->object.type) {
618 case NODE:
619 map_node_draw(map, map_item->object.node);
620 break;
621 case WAY:
622 map_way_draw(map, map_item->object.way);
623 break;
624 default:
625 g_assert((map_item->object.type == NODE) ||
626 (map_item->object.type == WAY));
627 }
628 }
629
630 static void map_item_remove(map_t *map, map_item_t *map_item) {
631 map_item_chain_t **chainP = NULL;
632
633 switch(map_item->object.type) {
634 case NODE:
635 chainP = &map_item->object.node->map_item_chain;
636 break;
637 case WAY:
638 chainP = &map_item->object.way->map_item_chain;
639 break;
640 default:
641 g_assert((map_item->object.type == NODE) ||
642 (map_item->object.type == WAY));
643 }
644
645 map_item_chain_destroy(chainP);
646 }
647
648 static void map_item_init(style_t *style, map_item_t *map_item) {
649 switch (map_item->object.type){
650 case WAY:
651 josm_elemstyles_colorize_way(style, map_item->object.way);
652 break;
653 case NODE:
654 josm_elemstyles_colorize_node(style, map_item->object.node);
655 break;
656 default:
657 g_assert((map_item->object.type == NODE) ||
658 (map_item->object.type == WAY));
659 }
660 }
661
662 void map_item_redraw(appdata_t *appdata, map_item_t *map_item) {
663 map_item_t item = *map_item;
664
665 /* a relation cannot be redraws as it doesn't have a visual */
666 /* representation */
667 if(map_item->object.type == RELATION)
668 return;
669
670 /* check if the item to be redrawn is the selected one */
671 gboolean is_selected = FALSE;
672 if(map_item->object.ptr == appdata->map->selected.object.ptr) {
673 map_item_deselect(appdata);
674 is_selected = TRUE;
675 }
676
677 map_item_remove(appdata->map, &item);
678 map_item_init(appdata->map->style, &item);
679 map_item_draw(appdata->map, &item);
680
681 /* restore selection if there was one */
682 if(is_selected)
683 map_object_select(appdata, &item.object);
684 }
685
686 static void map_frisket_rectangle(canvas_points_t *points,
687 gint x0, gint x1, gint y0, gint y1) {
688 points->coords[2*0+0] = points->coords[2*3+0] = points->coords[2*4+0] = x0;
689 points->coords[2*1+0] = points->coords[2*2+0] = x1;
690 points->coords[2*0+1] = points->coords[2*1+1] = points->coords[2*4+1] = y0;
691 points->coords[2*2+1] = points->coords[2*3+1] = y1;
692 }
693
694 /* Draw the frisket area which masks off areas it'd be unsafe to edit,
695 * plus its inner edge marker line */
696 void map_frisket_draw(map_t *map, bounds_t *bounds) {
697 canvas_points_t *points = canvas_points_new(5);
698
699 /* don't draw frisket at all if it's completely transparent */
700 if(map->style->frisket.color & 0xff) {
701 elemstyle_color_t color = map->style->frisket.color;
702
703 float mult = map->style->frisket.mult;
704
705 /* top rectangle */
706 map_frisket_rectangle(points, mult*bounds->min.x, mult*bounds->max.x,
707 mult*bounds->min.y, bounds->min.y);
708 canvas_polygon_new(map->canvas, CANVAS_GROUP_FRISKET, points,
709 1, NO_COLOR, color);
710
711 /* bottom rectangle */
712 map_frisket_rectangle(points, mult*bounds->min.x, mult*bounds->max.x,
713 bounds->max.y, mult*bounds->max.y);
714 canvas_polygon_new(map->canvas, CANVAS_GROUP_FRISKET, points,
715 1, NO_COLOR, color);
716
717 /* left rectangle */
718 map_frisket_rectangle(points, mult*bounds->min.x, bounds->min.x,
719 mult*bounds->min.y, mult*bounds->max.y);
720 canvas_polygon_new(map->canvas, CANVAS_GROUP_FRISKET, points,
721 1, NO_COLOR, color);
722
723 /* right rectangle */
724 map_frisket_rectangle(points, bounds->max.x, mult*bounds->max.x,
725 mult*bounds->min.y, mult*bounds->max.y);
726 canvas_polygon_new(map->canvas, CANVAS_GROUP_FRISKET, points,
727 1, NO_COLOR, color);
728
729 }
730
731 if(map->style->frisket.border.present) {
732 // Edge marker line
733 gint ew2 = map->style->frisket.border.width/2;
734 map_frisket_rectangle(points,
735 bounds->min.x-ew2, bounds->max.x+ew2,
736 bounds->min.y-ew2, bounds->max.y+ew2);
737
738 canvas_polyline_new(map->canvas, CANVAS_GROUP_FRISKET, points,
739 map->style->frisket.border.width,
740 map->style->frisket.border.color);
741
742 }
743 canvas_points_free(points);
744 }
745
746 static void map_draw(map_t *map, osm_t *osm) {
747 g_assert(map->canvas);
748
749 printf("drawing ways ...\n");
750 way_t *way = osm->way;
751 while(way) {
752 map_way_draw(map, way);
753 way = way->next;
754 }
755
756 printf("drawing single nodes ...\n");
757 node_t *node = osm->node;
758 while(node) {
759 map_node_draw(map, node);
760 node = node->next;
761 }
762
763 printf("drawing frisket...\n");
764 map_frisket_draw(map, osm->bounds);
765 }
766
767 void map_state_free(map_state_t *state) {
768 if(!state) return;
769
770 /* free state of noone else references it */
771 if(state->refcount > 1)
772 state->refcount--;
773 else
774 g_free(state);
775 }
776
777 void map_free_map_item_chains(appdata_t *appdata) {
778 if(!appdata->osm) return;
779
780 #ifndef DESTROY_WAIT_FOR_GTK
781 printf(" DESTROY_WAIT_FOR_GTK not set, removing all chains now\n");
782
783 /* free all map_item_chains */
784 node_t *node = appdata->osm->node;
785 while(node) {
786 map_item_chain_t *chain = node->map_item_chain;
787 while(chain) {
788 map_item_chain_t *next = chain->next;
789 g_free(chain);
790 chain = next;
791 }
792 node->map_item_chain = NULL;
793 node = node->next;
794 }
795
796 way_t *way = appdata->osm->way;
797 while(way) {
798 map_item_chain_t *chain = way->map_item_chain;
799 while(chain) {
800 map_item_chain_t *next = chain->next;
801 g_free(chain);
802 chain = next;
803 }
804 way->map_item_chain = NULL;
805 way = way->next;
806 }
807 #endif
808 }
809
810 static gint map_destroy_event(GtkWidget *widget, gpointer data) {
811 appdata_t *appdata = (appdata_t*)data;
812 map_t *map = appdata->map;
813
814 printf("destroying entire map\n");
815
816 map_free_map_item_chains(appdata);
817
818 /* free buffered tags */
819 if(map->last_node_tags) osm_tags_free(map->last_node_tags);
820 if(map->last_way_tags) osm_tags_free(map->last_way_tags);
821
822 map_state_free(map->state);
823
824 if(map->style)
825 style_free(map->style);
826
827 /* destroy existing highlight */
828 if(map->highlight) {
829 printf("removing highlight\n");
830
831 map_highlight_t *hl = map->highlight;
832 while(hl) {
833 map_highlight_t *next = hl->next;
834 g_free(hl);
835 hl = next;
836 }
837 }
838
839 g_free(map);
840
841 appdata->map = NULL;
842
843 return FALSE;
844 }
845
846 /* get the item at position x, y */
847 map_item_t *map_item_at(map_t *map, gint x, gint y) {
848 printf("map check at %d/%d\n", x, y);
849
850 canvas_window2world(map->canvas, x, y, &x, &y);
851
852 printf("world check at %d/%d\n", x, y);
853
854 canvas_item_t *item = canvas_get_item_at(map->canvas, x, y);
855
856 if(!item) {
857 printf(" there's no item\n");
858 return NULL;
859 }
860
861 printf(" there's an item (%p)\n", item);
862
863 map_item_t *map_item = canvas_item_get_user_data(item);
864
865 if(!map_item) {
866 printf(" item has no user data!\n");
867 return NULL;
868 }
869
870 if(map_item->highlight)
871 printf(" item is highlight\n");
872
873 switch(map_item->object.type) {
874 case NODE:
875 printf(" item is node #"ITEM_ID_FORMAT"\n", map_item->object.node->id);
876 break;
877 case WAY:
878 printf(" item is way #"ITEM_ID_FORMAT"\n", map_item->object.way->id);
879 break;
880 default:
881 printf(" unknown item\n");
882 break;
883 }
884
885 return map_item;
886 }
887
888 /* get the real item (no highlight) at x, y */
889 map_item_t *map_real_item_at(map_t *map, gint x, gint y) {
890 map_item_t *map_item = map_item_at(map, x, y);
891
892 /* no item or already a real one */
893 if(!map_item || !map_item->highlight) return map_item;
894
895 /* get the item (parent) this item is the highlight of */
896 map_item_t *parent = NULL;
897 switch(map_item->object.type) {
898
899 case NODE:
900 if(map_item->object.node->map_item_chain)
901 parent = map_item->object.node->map_item_chain->map_item;
902
903 if(parent)
904 printf(" using parent item node #" ITEM_ID_FORMAT "\n",
905 parent->object.node->id);
906 break;
907
908 case WAY:
909 if(map_item->object.way->map_item_chain)
910 parent = map_item->object.way->map_item_chain->map_item;
911
912 if(parent)
913 printf(" using parent item way #" ITEM_ID_FORMAT "\n",
914 parent->object.way->id);
915 break;
916
917 default:
918 g_assert((map_item->object.type == NODE) ||
919 (map_item->object.type == WAY));
920 break;
921 }
922
923 if(parent)
924 map_item = parent;
925 else
926 printf(" no parent, working on highlight itself\n");
927
928 return map_item;
929 }
930
931 /* Limitations on the amount by which we can scroll. Keeps part of the
932 * map visible at all times */
933 static void map_limit_scroll(map_t *map, canvas_unit_t unit,
934 gint *sx, gint *sy) {
935
936 /* get scale factor for pixel->meter conversion. set to 1 if */
937 /* given coordinates are already in meters */
938 gdouble scale = (unit == CANVAS_UNIT_METER)?1.0:canvas_get_zoom(map->canvas);
939
940 /* convert pixels to meters if necessary */
941 gdouble sx_cu = *sx / scale;
942 gdouble sy_cu = *sy / scale;
943
944 /* get size of visible area in canvas units (meters) */
945 gint aw_cu = canvas_get_viewport_width(map->canvas, CANVAS_UNIT_METER);
946 gint ah_cu = canvas_get_viewport_height(map->canvas, CANVAS_UNIT_METER);
947
948 // Data rect minimum and maximum
949 gint min_x, min_y, max_x, max_y;
950 min_x = map->appdata->osm->bounds->min.x;
951 min_y = map->appdata->osm->bounds->min.y;
952 max_x = map->appdata->osm->bounds->max.x;
953 max_y = map->appdata->osm->bounds->max.y;
954
955 // limit stops - prevent scrolling beyond these
956 gint min_sy_cu = 0.95*(min_y - ah_cu);
957 gint min_sx_cu = 0.95*(min_x - aw_cu);
958 gint max_sy_cu = 0.95*(max_y);
959 gint max_sx_cu = 0.95*(max_x);
960 if (sy_cu < min_sy_cu) { *sy = min_sy_cu * scale; }
961 if (sx_cu < min_sx_cu) { *sx = min_sx_cu * scale; }
962 if (sy_cu > max_sy_cu) { *sy = max_sy_cu * scale; }
963 if (sx_cu > max_sx_cu) { *sx = max_sx_cu * scale; }
964 }
965
966
967 /* Limit a proposed zoom factor to sane ranges.
968 * Specifically the map is allowed to be no smaller than the viewport. */
969 static gboolean map_limit_zoom(map_t *map, gdouble *zoom) {
970 // Data rect minimum and maximum
971 gint min_x, min_y, max_x, max_y;
972 min_x = map->appdata->osm->bounds->min.x;
973 min_y = map->appdata->osm->bounds->min.y;
974 max_x = map->appdata->osm->bounds->max.x;
975 max_y = map->appdata->osm->bounds->max.y;
976
977 /* get size of visible area in pixels and convert to meters of intended */
978 /* zoom by deviding by zoom (which is basically pix/m) */
979 gint aw_cu =
980 canvas_get_viewport_width(map->canvas, CANVAS_UNIT_PIXEL) / *zoom;
981 gint ah_cu =
982 canvas_get_viewport_height(map->canvas, CANVAS_UNIT_PIXEL) / *zoom;
983
984 gdouble oldzoom = *zoom;
985 if (ah_cu < aw_cu) {
986 gint lim_h = ah_cu*0.95;
987 if (max_y-min_y < lim_h) {
988 gdouble corr = ((gdouble)max_y-min_y) / (gdouble)lim_h;
989 *zoom /= corr;
990 }
991 }
992 else {
993 gint lim_w = aw_cu*0.95;
994 if (max_x-min_x < lim_w) {
995 gdouble corr = ((gdouble)max_x-min_x) / (gdouble)lim_w;
996 *zoom /= corr;
997 }
998 }
999 if (*zoom != oldzoom) {
1000 printf("Can't zoom further out (%f)\n", *zoom);
1001 return 1;
1002 }
1003 return 0;
1004 }
1005
1006
1007 /*
1008 * Scroll the map to a point if that point is currently offscreen.
1009 */
1010 void map_scroll_to_if_offscreen(map_t *map, lpos_t *lpos) {
1011
1012 // Ignore anything outside the working area
1013 if (!(map && map->appdata && map->appdata->osm)) {
1014 return;
1015 }
1016 gint min_x, min_y, max_x, max_y;
1017 min_x = map->appdata->osm->bounds->min.x;
1018 min_y = map->appdata->osm->bounds->min.y;
1019 max_x = map->appdata->osm->bounds->max.x;
1020 max_y = map->appdata->osm->bounds->max.y;
1021 if ( (lpos->x > max_x) || (lpos->x < min_x)
1022 || (lpos->y > max_y) || (lpos->y < min_y)) {
1023 printf("cannot scroll to (%d, %d): outside the working area\n",
1024 lpos->x, lpos->y);
1025 return;
1026 }
1027
1028 // Viewport dimensions in canvas space
1029
1030 /* get size of visible area in canvas units (meters) */
1031 gdouble pix_per_meter = canvas_get_zoom(map->canvas);
1032 gdouble aw = canvas_get_viewport_width(map->canvas, CANVAS_UNIT_METER);
1033 gdouble ah = canvas_get_viewport_height(map->canvas, CANVAS_UNIT_METER);
1034
1035 // Is the point still onscreen?
1036 gboolean vert_recentre_needed = FALSE;
1037 gboolean horiz_recentre_needed = FALSE;
1038 gint sx, sy;
1039 canvas_scroll_get(map->canvas, CANVAS_UNIT_PIXEL, &sx, &sy);
1040 gint viewport_left = (sx/pix_per_meter);
1041 gint viewport_right = (sx/pix_per_meter)+aw;
1042 gint viewport_top = (sy/pix_per_meter);
1043 gint viewport_bottom = (sy/pix_per_meter)+ah;
1044 if (lpos->x > viewport_right) {
1045 printf("** off right edge (%d > %d)\n", lpos->x, viewport_right);
1046 horiz_recentre_needed = TRUE;
1047 }
1048 if (lpos->x < viewport_left) {
1049 printf("** off left edge (%d < %d)\n", lpos->x, viewport_left);
1050 horiz_recentre_needed = TRUE;
1051 }
1052 if (lpos->y > viewport_bottom) {
1053 printf("** off bottom edge (%d > %d)\n", lpos->y, viewport_bottom);
1054 vert_recentre_needed = TRUE;
1055 }
1056 if (lpos->y < viewport_top) {
1057 printf("** off top edge (%d < %d)\n", lpos->y, viewport_top);
1058 vert_recentre_needed = TRUE;
1059 }
1060
1061 if (horiz_recentre_needed || vert_recentre_needed) {
1062 gint new_sx, new_sy;
1063
1064 // Just centre both at once
1065 new_sx = pix_per_meter * (lpos->x - (aw/2));
1066 new_sy = pix_per_meter * (lpos->y - (ah/2));
1067
1068 map_limit_scroll(map, CANVAS_UNIT_PIXEL, &new_sx, &new_sy);
1069 canvas_scroll_to(map->canvas, CANVAS_UNIT_PIXEL, new_sx, new_sy);
1070 }
1071 }
1072
1073 /* Deselects the current way or node if its zoom_max
1074 * means that it's not going to render at the current map zoom. */
1075 void map_deselect_if_zoom_below_zoom_max(map_t *map) {
1076 if (map->selected.object.type == WAY) {
1077 printf("will deselect way if zoomed below %f\n",
1078 map->selected.object.way->draw.zoom_max);
1079 if (map->state->zoom < map->selected.object.way->draw.zoom_max) {
1080 printf(" deselecting way!\n");
1081 map_item_deselect(map->appdata);
1082 }
1083 }
1084 else if (map->selected.object.type == NODE) {
1085 printf("will deselect node if zoomed below %f\n",
1086 map->selected.object.node->zoom_max);
1087 if (map->state->zoom < map->selected.object.node->zoom_max) {
1088 printf(" deselecting node!\n");
1089 map_item_deselect(map->appdata);
1090 }
1091 }
1092 }
1093
1094 void map_set_zoom(map_t *map, double zoom,
1095 gboolean update_scroll_offsets) {
1096 gboolean at_zoom_limit = 0;
1097 at_zoom_limit = map_limit_zoom(map, &zoom);
1098
1099 map->state->zoom = zoom;
1100 canvas_set_zoom(map->canvas, map->state->zoom);
1101
1102 map_deselect_if_zoom_below_zoom_max(map);
1103
1104 if(update_scroll_offsets) {
1105 if (!at_zoom_limit) {
1106 /* zooming affects the scroll offsets */
1107 gint sx, sy;
1108 canvas_scroll_get(map->canvas, CANVAS_UNIT_PIXEL, &sx, &sy);
1109 map_limit_scroll(map, CANVAS_UNIT_PIXEL, &sx, &sy);
1110
1111 // keep the map visible
1112 canvas_scroll_to(map->canvas, CANVAS_UNIT_PIXEL, sx, sy);
1113 }
1114
1115 canvas_scroll_get(map->canvas, CANVAS_UNIT_METER,
1116 &map->state->scroll_offset.x,
1117 &map->state->scroll_offset.y);
1118 }
1119 }
1120
1121 static gboolean map_scroll_event(GtkWidget *widget, GdkEventScroll *event,
1122 gpointer data) {
1123 appdata_t *appdata = (appdata_t*)data;
1124
1125 if(!appdata->osm) return FALSE;
1126
1127 if(event->type == GDK_SCROLL && appdata->map && appdata->map->state) {
1128 if(event->direction)
1129 map_set_zoom(appdata->map,
1130 appdata->map->state->zoom / ZOOM_FACTOR_WHEEL, TRUE);
1131 else
1132 map_set_zoom(appdata->map,
1133 appdata->map->state->zoom * ZOOM_FACTOR_WHEEL, TRUE);
1134 }
1135
1136 return TRUE;
1137 }
1138
1139 static gboolean distance_above(map_t *map, gint x, gint y, gint limit) {
1140 gint sx, sy;
1141
1142 /* add offsets generated by mouse within map and map scrolling */
1143 sx = (x-map->pen_down.at.x);
1144 sy = (y-map->pen_down.at.y);
1145
1146 return(sx*sx + sy*sy > limit*limit);
1147 }
1148
1149 /* scroll with respect to two screen positions */
1150 static void map_do_scroll(map_t *map, gint x, gint y) {
1151 gint sx, sy;
1152
1153 canvas_scroll_get(map->canvas, CANVAS_UNIT_PIXEL, &sx, &sy);
1154 sx -= x-map->pen_down.at.x;
1155 sy -= y-map->pen_down.at.y;
1156 map_limit_scroll(map, CANVAS_UNIT_PIXEL, &sx, &sy);
1157 canvas_scroll_to(map->canvas, CANVAS_UNIT_PIXEL, sx, sy);
1158
1159 canvas_scroll_get(map->canvas, CANVAS_UNIT_METER,
1160 &map->state->scroll_offset.x,
1161 &map->state->scroll_offset.y);
1162 }
1163
1164
1165 /* scroll a certain step */
1166 static void map_do_scroll_step(map_t *map, gint x, gint y) {
1167 gint sx, sy;
1168 canvas_scroll_get(map->canvas, CANVAS_UNIT_PIXEL, &sx, &sy);
1169 sx += x;
1170 sy += y;
1171 map_limit_scroll(map, CANVAS_UNIT_PIXEL, &sx, &sy);
1172 canvas_scroll_to(map->canvas, CANVAS_UNIT_PIXEL, sx, sy);
1173
1174 canvas_scroll_get(map->canvas, CANVAS_UNIT_METER,
1175 &map->state->scroll_offset.x,
1176 &map->state->scroll_offset.y);
1177 }
1178
1179 gboolean map_item_is_selected_node(map_t *map, map_item_t *map_item) {
1180 printf("check if item is a selected node\n");
1181
1182 if(!map_item) {
1183 printf(" no item requested\n");
1184 return FALSE;
1185 }
1186
1187 if(map->selected.object.type == ILLEGAL) {
1188 printf(" nothing is selected\n");
1189 return FALSE;
1190 }
1191
1192 /* clicked the highlight directly */
1193 if(map_item->object.type != NODE) {
1194 printf(" didn't click node\n");
1195 return FALSE;
1196 }
1197
1198 if(map->selected.object.type == NODE) {
1199 printf(" selected item is a node\n");
1200
1201 if(map_item->object.node == map->selected.object.node) {
1202 printf(" requested item is a selected node\n");
1203 return TRUE;
1204 }
1205 printf(" but it's not the requested one\n");
1206 return FALSE;
1207
1208 } else if(map->selected.object.type == WAY) {
1209 printf(" selected item is a way\n");
1210
1211 node_chain_t *node_chain = map->selected.object.way->node_chain;
1212 while(node_chain) {
1213 if(node_chain->node == map_item->object.node) {
1214 printf(" requested item is part of selected way\n");
1215 return TRUE;
1216 }
1217 node_chain = node_chain->next;
1218 }
1219 printf(" but it doesn't include the requested node\n");
1220 return FALSE;
1221
1222 } else {
1223 printf(" selected item is unknown\n");
1224 return FALSE;
1225 }
1226
1227 g_assert(0);
1228 return TRUE;
1229 }
1230
1231 /* return true if the item given is the currenly selected way */
1232 /* also return false if nothing is selected or the selection is no way */
1233 gboolean map_item_is_selected_way(map_t *map, map_item_t *map_item) {
1234 printf("check if item is the selected way\n");
1235
1236 if(!map_item) {
1237 printf(" no item requested\n");
1238 return FALSE;
1239 }
1240
1241 if(map->selected.object.type == ILLEGAL) {
1242 printf(" nothing is selected\n");
1243 return FALSE;
1244 }
1245
1246 /* clicked the highlight directly */
1247 if(map_item->object.type != WAY) {
1248 printf(" didn't click way\n");
1249 return FALSE;
1250 }
1251
1252 if(map->selected.object.type == WAY) {
1253 printf(" selected item is a way\n");
1254
1255 if(map_item->object.way == map->selected.object.way) {
1256 printf(" requested item is a selected way\n");
1257 return TRUE;
1258 }
1259 printf(" but it's not the requested one\n");
1260 return FALSE;
1261 }
1262
1263 printf(" selected item is not a way\n");
1264 return FALSE;
1265 }
1266
1267
1268 void map_highlight_refresh(appdata_t *appdata) {
1269 map_t *map = appdata->map;
1270 object_t old = map->selected.object;
1271
1272 printf("type to refresh is %d\n", old.type);
1273 if(old.type == ILLEGAL)
1274 return;
1275
1276 map_item_deselect(appdata);
1277 map_object_select(appdata, &old);
1278 }
1279
1280 void map_way_delete(appdata_t *appdata, way_t *way) {
1281 printf("deleting way #" ITEM_ID_FORMAT " from map and osm\n", way->id);
1282
1283 /* remove it visually from the screen */
1284 map_item_chain_destroy(&way->map_item_chain);
1285
1286 /* also remove the visible representation of all nodes (nodes with tags) */
1287 node_chain_t *chain = way->node_chain;
1288 while(chain) {
1289 if(chain->node->map_item_chain)
1290 map_item_chain_destroy(&chain->node->map_item_chain);
1291
1292 chain = chain->next;
1293 }
1294
1295 /* and mark it "deleted" in the database */
1296 osm_way_remove_from_relation(appdata->osm, way);
1297
1298 puts("1");
1299 osm_way_delete(appdata->osm, &appdata->icon, way, FALSE);
1300 puts("2");
1301 }
1302
1303 static void map_handle_click(appdata_t *appdata, map_t *map) {
1304
1305 /* problem: on_item may be the highlight itself! So store it! */
1306 map_item_t map_item;
1307 if(map->pen_down.on_item) map_item = *map->pen_down.on_item;
1308 else map_item.object.type = ILLEGAL;
1309
1310 /* if we aready have something selected, then de-select it */
1311 map_item_deselect(appdata);
1312
1313 /* select the clicked item (if there was one) */
1314 if(map_item.object.type != ILLEGAL) {
1315 switch(map_item.object.type) {
1316 case NODE:
1317 map_node_select(appdata, map_item.object.node);
1318 break;
1319
1320 case WAY:
1321 map_way_select(appdata, map_item.object.way);
1322 break;
1323
1324 default:
1325 g_assert((map_item.object.type == NODE) ||
1326 (map_item.object.type == WAY));
1327 break;
1328 }
1329 }
1330 }
1331
1332 static void map_touchnode_update(appdata_t *appdata, gint x, gint y) {
1333 map_t *map = appdata->map;
1334
1335 map_hl_touchnode_clear(map);
1336
1337 node_t *cur_node = NULL;
1338
1339 /* the "current node" which is the one we are working on and which */
1340 /* should not be highlighted depends on the action */
1341 switch(map->action.type) {
1342
1343 /* in idle mode the dragged node is not highlighted */
1344 case MAP_ACTION_IDLE:
1345 g_assert(map->pen_down.on_item);
1346 g_assert(map->pen_down.on_item->object.type == NODE);
1347 cur_node = map->pen_down.on_item->object.node;
1348 break;
1349
1350 default:
1351 break;
1352 }
1353
1354
1355 /* check if we are close to one of the other nodes */
1356 canvas_window2world(appdata->map->canvas, x, y, &x, &y);
1357 node_t *node = appdata->osm->node;
1358 while(!map->touchnode && node) {
1359 /* don't highlight the dragged node itself and don't highlight */
1360 /* deleted ones */
1361 if((node != cur_node) && (!(node->flags & OSM_FLAG_DELETED))) {
1362 gint nx = abs(x - node->lpos.x);
1363 gint ny = abs(y - node->lpos.y);
1364
1365 if((nx < map->style->node.radius) && (ny < map->style->node.radius) &&
1366 (nx*nx + ny*ny < map->style->node.radius * map->style->node.radius))
1367 map_hl_touchnode_draw(map, node);
1368 }
1369 node = node->next;
1370 }
1371
1372 /* during way creation also nodes of the new way */
1373 /* need to be searched */
1374 if(!map->touchnode && map->action.way) {
1375 node_chain_t *chain = map->action.way->node_chain;
1376 while(!map->touchnode && chain && chain->next) {
1377 gint nx = abs(x - chain->node->lpos.x);
1378 gint ny = abs(y - chain->node->lpos.y);
1379
1380 if((nx < map->style->node.radius) && (ny < map->style->node.radius) &&
1381 (nx*nx + ny*ny < map->style->node.radius * map->style->node.radius))
1382 map_hl_touchnode_draw(map, chain->node);
1383
1384 chain = chain->next;
1385 }
1386 }
1387 }
1388
1389 static void map_button_press(map_t *map, gint x, gint y) {
1390
1391 printf("left button pressed\n");
1392 map->pen_down.is = TRUE;
1393
1394 /* save press position */
1395 map->pen_down.at.x = x;
1396 map->pen_down.at.y = y;
1397 map->pen_down.drag = FALSE; // don't assume drag yet
1398
1399 /* determine wether this press was on an item */
1400 map->pen_down.on_item = map_real_item_at(map, x, y);
1401
1402 /* check if the clicked item is a highlighted node as the user */
1403 /* might want to drag that */
1404 map->pen_down.on_selected_node = FALSE;
1405 if(map->pen_down.on_item)
1406 map->pen_down.on_selected_node =
1407 map_item_is_selected_node(map, map->pen_down.on_item);
1408
1409 /* button press */
1410 switch(map->action.type) {
1411
1412 case MAP_ACTION_WAY_NODE_ADD:
1413 map_edit_way_node_add_highlight(map, map->pen_down.on_item, x, y);
1414 break;
1415
1416 case MAP_ACTION_WAY_CUT:
1417 map_edit_way_cut_highlight(map, map->pen_down.on_item, x, y);
1418 break;
1419
1420 case MAP_ACTION_NODE_ADD:
1421 map_hl_cursor_draw(map, x, y, FALSE, map->style->node.radius);
1422 break;
1423
1424 case MAP_ACTION_WAY_ADD:
1425 map_hl_cursor_draw(map, x, y, FALSE, map->style->node.radius);
1426 map_touchnode_update(map->appdata, x, y);
1427 break;
1428
1429 default:
1430 break;
1431 }
1432 }
1433
1434 /* move the background image (wms data) during wms adjustment */
1435 static void map_bg_adjust(map_t *map, gint x, gint y) {
1436 g_assert(map->appdata);
1437 g_assert(map->appdata->osm);
1438 g_assert(map->appdata->osm->bounds);
1439
1440 x += map->appdata->osm->bounds->min.x + map->bg.offset.x -
1441 map->pen_down.at.x;
1442 y += map->appdata->osm->bounds->min.y + map->bg.offset.y -
1443 map->pen_down.at.y;
1444
1445 canvas_image_move(map->bg.item, x, y, map->bg.scale.x, map->bg.scale.y);
1446 }
1447
1448 static void map_button_release(map_t *map, gint x, gint y) {
1449 map->pen_down.is = FALSE;
1450
1451 /* before button release is handled */
1452 switch(map->action.type) {
1453 case MAP_ACTION_BG_ADJUST:
1454 map_bg_adjust(map, x, y);
1455 map->bg.offset.x += x - map->pen_down.at.x;
1456 map->bg.offset.y += y - map->pen_down.at.y;
1457 break;
1458
1459 case MAP_ACTION_IDLE:
1460 /* check if distance to press is above drag limit */
1461 if(!map->pen_down.drag)
1462 map->pen_down.drag = distance_above(map, x, y, MAP_DRAG_LIMIT);
1463
1464 if(!map->pen_down.drag) {
1465 printf("left button released after click\n");
1466
1467 map_item_t old_sel = map->selected;
1468 map_handle_click(map->appdata, map);
1469
1470 if((old_sel.object.type != ILLEGAL) &&
1471 (old_sel.object.type == map->selected.object.type) &&
1472 (old_sel.object.ptr == map->selected.object.ptr)) {
1473 printf("re-selected same item of type %d, "
1474 "pushing it to the bottom\n", old_sel.object.type);
1475
1476 if(!map->selected.item) {
1477 printf(" item has no visible representation to push\n");
1478 } else {
1479 canvas_item_to_bottom(map->selected.item);
1480
1481 /* update clicked item, to correctly handle the click */
1482 map->pen_down.on_item =
1483 map_real_item_at(map, map->pen_down.at.x, map->pen_down.at.y);
1484
1485 map_handle_click(map->appdata, map);
1486 }
1487 }
1488 } else {
1489 printf("left button released after drag\n");
1490
1491 /* just scroll if we didn't drag an selected item */
1492 if(!map->pen_down.on_selected_node)
1493 map_do_scroll(map, x, y);
1494 else {
1495 printf("released after dragging node\n");
1496 map_hl_cursor_clear(map);
1497
1498 /* now actually move the node */
1499 map_edit_node_move(map->appdata, map->pen_down.on_item, x, y);
1500 }
1501 }
1502 break;
1503
1504 case MAP_ACTION_NODE_ADD:
1505 printf("released after NODE ADD\n");
1506 map_hl_cursor_clear(map);
1507
1508 /* convert mouse position to canvas (world) position */
1509 canvas_window2world(map->canvas, x, y, &x, &y);
1510
1511 node_t *node = NULL;
1512 if(!osm_position_within_bounds(map->appdata->osm, x, y))
1513 map_outside_error(map->appdata);
1514 else {
1515 node = osm_node_new(map->appdata->osm, x, y);
1516 osm_node_attach(map->appdata->osm, node);
1517 map_node_draw(map, node);
1518 }
1519 map_action_set(map->appdata, MAP_ACTION_IDLE);
1520
1521 map_item_deselect(map->appdata);
1522
1523 if(node) {
1524 map_node_select(map->appdata, node);
1525
1526 /* let the user specify some tags for the new node */
1527 info_dialog(GTK_WIDGET(map->appdata->window), map->appdata, NULL);
1528 }
1529 break;
1530
1531 case MAP_ACTION_WAY_ADD:
1532 printf("released after WAY ADD\n");
1533 map_hl_cursor_clear(map);
1534
1535 map_edit_way_add_segment(map, x, y);
1536 break;
1537
1538 case MAP_ACTION_WAY_NODE_ADD:
1539 printf("released after WAY NODE ADD\n");
1540 map_hl_cursor_clear(map);
1541
1542 map_edit_way_node_add(map, x, y);
1543 break;
1544
1545 case MAP_ACTION_WAY_CUT:
1546 printf("released after WAY CUT\n");
1547 map_hl_cursor_clear(map);
1548
1549 map_edit_way_cut(map, x, y);
1550 break;
1551
1552
1553 default:
1554 break;
1555 }
1556 }
1557
1558 static gboolean map_button_event(GtkWidget *widget, GdkEventButton *event,
1559 gpointer data) {
1560 appdata_t *appdata = (appdata_t*)data;
1561 map_t *map = appdata->map;
1562
1563 if(!appdata->osm) return FALSE;
1564
1565 if(event->button == 1) {
1566 gint x = event->x, y = event->y;
1567
1568 if(event->type == GDK_BUTTON_PRESS)
1569 map_button_press(map, x, y);
1570
1571 if(event->type == GDK_BUTTON_RELEASE)
1572 map_button_release(map, x, y);
1573 }
1574
1575 return FALSE; /* forward to further processing */
1576 }
1577
1578 static gboolean map_motion_notify_event(GtkWidget *widget,
1579 GdkEventMotion *event, gpointer data) {
1580 appdata_t *appdata = (appdata_t*)data;
1581 map_t *map = appdata->map;
1582 gint x, y;
1583 GdkModifierType state;
1584
1585 if(!appdata->osm) return FALSE;
1586
1587 #ifdef USE_HILDON
1588 /* reduce update frequency on hildon to keep screen update fluid */
1589 static guint32 last_time = 0;
1590
1591 if(event->time - last_time < 250) return FALSE;
1592 last_time = event->time;
1593 #endif
1594
1595 if(!map->pen_down.is)
1596 return FALSE;
1597
1598 #ifndef USE_GOOCANVAS
1599 /* handle hints, hints are handled by goocanvas directly */
1600 if(event->is_hint)
1601 gdk_window_get_pointer(event->window, &x, &y, &state);
1602 else
1603 #endif
1604 {
1605 x = event->x;
1606 y = event->y;
1607 state = event->state;
1608 }
1609
1610 /* check if distance to press is above drag limit */
1611 if(!map->pen_down.drag)
1612 map->pen_down.drag = distance_above(map, x, y, MAP_DRAG_LIMIT);
1613
1614 /* drag */
1615 switch(map->action.type) {
1616 case MAP_ACTION_BG_ADJUST:
1617 map_bg_adjust(map, x, y);
1618 break;
1619
1620 case MAP_ACTION_IDLE:
1621 if(map->pen_down.drag) {
1622 /* just scroll if we didn't drag an selected item */
1623 if(!map->pen_down.on_selected_node)
1624 map_do_scroll(map, x, y);
1625 else {
1626 map_hl_cursor_draw(map, x, y, FALSE, map->style->node.radius);
1627 map_touchnode_update(appdata, x, y);
1628 }
1629 }
1630 break;
1631
1632 case MAP_ACTION_NODE_ADD:
1633 map_hl_cursor_draw(map, x, y, FALSE, map->style->node.radius);
1634 break;
1635
1636 case MAP_ACTION_WAY_ADD:
1637 map_hl_cursor_draw(map, x, y, FALSE, map->style->node.radius);
1638 map_touchnode_update(appdata, x, y);
1639 break;
1640
1641 case MAP_ACTION_WAY_NODE_ADD:
1642 map_hl_cursor_clear(map);
1643 map_item_t *item = map_item_at(map, x, y);
1644 if(item) map_edit_way_node_add_highlight(map, item, x, y);
1645 break;
1646
1647 case MAP_ACTION_WAY_CUT:
1648 map_hl_cursor_clear(map);
1649 item = map_item_at(map, x, y);
1650 if(item) map_edit_way_cut_highlight(map, item, x, y);
1651 break;
1652
1653 default:
1654 break;
1655 }
1656
1657
1658 return FALSE; /* forward to further processing */
1659 }
1660
1661 gboolean map_key_press_event(appdata_t *appdata, GdkEventKey *event) {
1662
1663 if(!appdata->osm) return FALSE;
1664
1665 /* map needs to be there to handle buttons */
1666 if(!appdata->map->canvas)
1667 return FALSE;
1668
1669 if(event->type == GDK_KEY_PRESS) {
1670 gdouble zoom = 0;
1671 switch(event->keyval) {
1672
1673 case GDK_Left:
1674 map_do_scroll_step(appdata->map, -50, 0);
1675 break;
1676
1677 case GDK_Right:
1678 map_do_scroll_step(appdata->map, +50, 0);
1679 break;
1680
1681 case GDK_Up:
1682 map_do_scroll_step(appdata->map, 0, -50);
1683 break;
1684
1685 case GDK_Down:
1686 map_do_scroll_step(appdata->map, 0, +50);
1687 break;
1688
1689 case GDK_Return: // same as HILDON_HARDKEY_SELECT
1690 /* if the ok button is enabled, call its function */
1691 if(GTK_WIDGET_FLAGS(appdata->iconbar->ok) & GTK_SENSITIVE)
1692 map_action_ok(appdata);
1693 /* otherwise if info is enabled call that */
1694 else if(GTK_WIDGET_FLAGS(appdata->iconbar->info) & GTK_SENSITIVE)
1695 info_dialog(GTK_WIDGET(appdata->window), appdata, NULL);
1696 break;
1697
1698 case GDK_Escape: // same as HILDON_HARDKEY_ESC
1699 /* if the cancel button is enabled, call its function */
1700 if(GTK_WIDGET_FLAGS(appdata->iconbar->cancel) & GTK_SENSITIVE)
1701 map_action_cancel(appdata);
1702 break;
1703
1704 #ifdef USE_HILDON
1705 case HILDON_HARDKEY_INCREASE:
1706 #else
1707 case '+':
1708 #endif
1709 zoom = appdata->map->state->zoom;
1710 zoom *= ZOOM_FACTOR_BUTTON;
1711 map_set_zoom(appdata->map, zoom, TRUE);
1712 printf("zoom is now %f (1:%d)\n", zoom, (int)zoom_to_scaledn(zoom));
1713 return TRUE;
1714 break;
1715
1716 #ifdef USE_HILDON
1717 case HILDON_HARDKEY_DECREASE:
1718 #else
1719 case '-':
1720 #endif
1721 zoom = appdata->map->state->zoom;
1722 zoom /= ZOOM_FACTOR_BUTTON;
1723 map_set_zoom(appdata->map, zoom, TRUE);
1724 printf("zoom is now %f (1:%d)\n", zoom, (int)zoom_to_scaledn(zoom));
1725 return TRUE;
1726 break;
1727
1728 default:
1729 printf("key event %d\n", event->keyval);
1730 break;
1731 }
1732 }
1733 return FALSE;
1734 }
1735
1736 void map_state_reset(map_state_t *state) {
1737 if(!state) return;
1738
1739 state->zoom = 0.25;
1740 state->detail = 1.0;
1741
1742 /* todo: try to scroll to center of screen */
1743 state->scroll_offset.x = 0;
1744 state->scroll_offset.y = 0;
1745 }
1746
1747 map_state_t *map_state_new(void) {
1748 map_state_t *state = g_new0(map_state_t, 1);
1749 map_state_reset(state);
1750 return state;
1751 }
1752
1753 GtkWidget *map_new(appdata_t *appdata) {
1754 map_t *map = appdata->map = g_new0(map_t, 1);
1755
1756 map->style = style_load(appdata, appdata->settings->style);
1757 if(!map->style) {
1758 errorf(NULL, _("Unable to load valid style, terminating."));
1759 g_free(map);
1760 return NULL;
1761 }
1762
1763 if(appdata->project && appdata->project->map_state) {
1764 printf("Using projects map state\n");
1765 map->state = appdata->project->map_state;
1766 } else {
1767 printf("Creating new map state\n");
1768 map->state = map_state_new();
1769 }
1770
1771 map->state->refcount++;
1772
1773 map->pen_down.at.x = -1;
1774 map->pen_down.at.y = -1;
1775 map->appdata = appdata;
1776 map->action.type = MAP_ACTION_IDLE;
1777
1778 map->canvas = canvas_new();
1779
1780 GtkWidget *canvas_widget = canvas_get_widget(map->canvas);
1781
1782 gtk_widget_set_events(canvas_widget,
1783 GDK_BUTTON_PRESS_MASK
1784 | GDK_BUTTON_RELEASE_MASK
1785 | GDK_SCROLL_MASK
1786 | GDK_POINTER_MOTION_MASK
1787 | GDK_POINTER_MOTION_HINT_MASK);
1788
1789 gtk_signal_connect(GTK_OBJECT(canvas_widget),
1790 "button_press_event", G_CALLBACK(map_button_event), appdata);
1791 gtk_signal_connect(GTK_OBJECT(canvas_widget),
1792 "button_release_event", G_CALLBACK(map_button_event), appdata);
1793 gtk_signal_connect(GTK_OBJECT(canvas_widget),
1794 "motion_notify_event", G_CALLBACK(map_motion_notify_event), appdata);
1795 gtk_signal_connect(GTK_OBJECT(canvas_widget),
1796 "scroll_event", G_CALLBACK(map_scroll_event), appdata);
1797
1798 gtk_signal_connect(GTK_OBJECT(canvas_widget),
1799 "destroy", G_CALLBACK(map_destroy_event), appdata);
1800
1801 return canvas_widget;
1802 }
1803
1804 void map_init(appdata_t *appdata) {
1805 map_t *map = appdata->map;
1806
1807 /* update canvas background color */
1808 canvas_set_background(map->canvas, map->style->background.color);
1809
1810 /* set initial zoom */
1811 map_set_zoom(map, map->state->zoom, FALSE);
1812 josm_elemstyles_colorize_world(map->style, appdata->osm);
1813
1814 map_draw(map, appdata->osm);
1815
1816 float mult = appdata->map->style->frisket.mult;
1817 canvas_set_bounds(map->canvas,
1818 mult*appdata->osm->bounds->min.x,
1819 mult*appdata->osm->bounds->min.y,
1820 mult*appdata->osm->bounds->max.x,
1821 mult*appdata->osm->bounds->max.y);
1822
1823 printf("restore scroll position %d/%d\n",
1824 map->state->scroll_offset.x, map->state->scroll_offset.y);
1825
1826 map_limit_scroll(map, CANVAS_UNIT_METER,
1827 &map->state->scroll_offset.x, &map->state->scroll_offset.y);
1828 canvas_scroll_to(map->canvas, CANVAS_UNIT_METER,
1829 map->state->scroll_offset.x, map->state->scroll_offset.y);
1830 }
1831
1832
1833 void map_clear(appdata_t *appdata, gint group_mask) {
1834 map_t *map = appdata->map;
1835
1836 printf("freeing map contents\n");
1837
1838 map_free_map_item_chains(appdata);
1839
1840 /* remove a possibly existing highlight */
1841 map_item_deselect(appdata);
1842
1843 canvas_erase(map->canvas, group_mask);
1844 }
1845
1846 void map_paint(appdata_t *appdata) {
1847 map_t *map = appdata->map;
1848
1849 josm_elemstyles_colorize_world(map->style, appdata->osm);
1850 map_draw(map, appdata->osm);
1851 }
1852
1853 /* called from several icons like e.g. "node_add" */
1854 void map_action_set(appdata_t *appdata, map_action_t action) {
1855 printf("map action set to %d\n", action);
1856
1857 appdata->map->action.type = action;
1858
1859 /* enable/disable ok/cancel buttons */
1860 // MAP_ACTION_IDLE=0, NODE_ADD, BG_ADJUST, WAY_ADD, WAY_NODE_ADD, WAY_CUT
1861 const gboolean ok_state[] = { FALSE, FALSE, TRUE, FALSE, FALSE, FALSE };
1862 const gboolean cancel_state[] = { FALSE, TRUE, TRUE, TRUE, TRUE, TRUE };
1863
1864 g_assert(MAP_ACTION_NUM == sizeof(ok_state)/sizeof(gboolean));
1865 g_assert(action < sizeof(ok_state)/sizeof(gboolean));
1866
1867 icon_bar_map_cancel_ok(appdata, cancel_state[action], ok_state[action]);
1868
1869 switch(action) {
1870 case MAP_ACTION_BG_ADJUST:
1871 /* an existing selection only causes confusion ... */
1872 map_item_deselect(appdata);
1873 break;
1874
1875 case MAP_ACTION_WAY_ADD:
1876 printf("starting new way\n");
1877
1878 /* remember if there was a way selected */
1879 way_t *way_sel = NULL;
1880 if(appdata->map->selected.object.type == WAY)
1881 way_sel = appdata->map->selected.object.way;
1882
1883 map_item_deselect(appdata);
1884 map_edit_way_add_begin(appdata->map, way_sel);
1885 break;
1886
1887 case MAP_ACTION_NODE_ADD:
1888 map_item_deselect(appdata);
1889 break;
1890
1891 default:
1892 break;
1893 }
1894
1895 icon_bar_map_action_idle(appdata, action == MAP_ACTION_IDLE);
1896 gtk_widget_set_sensitive(appdata->menu_item_wms_adjust,
1897 action == MAP_ACTION_IDLE);
1898
1899 const char *str_state[] = {
1900 NULL,
1901 _("Place a node"),
1902 _("Adjust background image position"),
1903 _("Place first node of new way"),
1904 _("Place node on selected way"),
1905 _("Select segment to cut way"),
1906 };
1907
1908 g_assert(MAP_ACTION_NUM == sizeof(str_state)/sizeof(char*));
1909
1910 statusbar_set(appdata, str_state[action], FALSE);
1911 }
1912
1913
1914 void map_action_cancel(appdata_t *appdata) {
1915 map_t *map = appdata->map;
1916
1917 switch(map->action.type) {
1918 case MAP_ACTION_WAY_ADD:
1919 map_edit_way_add_cancel(map);
1920 break;
1921
1922 case MAP_ACTION_BG_ADJUST:
1923 /* undo all changes to bg_offset */
1924 map->bg.offset.x = appdata->project->wms_offset.x;
1925 map->bg.offset.y = appdata->project->wms_offset.y;
1926
1927 gint x = appdata->osm->bounds->min.x + map->bg.offset.x;
1928 gint y = appdata->osm->bounds->min.y + map->bg.offset.y;
1929 canvas_image_move(map->bg.item, x, y, map->bg.scale.x, map->bg.scale.y);
1930 break;
1931
1932 default:
1933 break;
1934 }
1935
1936 map_action_set(appdata, MAP_ACTION_IDLE);
1937 }
1938
1939 void map_action_ok(appdata_t *appdata) {
1940 map_t *map = appdata->map;
1941
1942 /* reset action now as this erases the statusbar and some */
1943 /* of the actions may set it */
1944 map_action_t type = map->action.type;
1945 map_action_set(appdata, MAP_ACTION_IDLE);
1946
1947 switch(type) {
1948 case MAP_ACTION_WAY_ADD:
1949 map_edit_way_add_ok(map);
1950 break;
1951
1952 case MAP_ACTION_BG_ADJUST:
1953 /* save changes to bg_offset in project */
1954 appdata->project->wms_offset.x = map->bg.offset.x;
1955 appdata->project->wms_offset.y = map->bg.offset.y;
1956 break;
1957
1958 default:
1959 break;
1960 }
1961 }
1962
1963 /* called from icon "trash" */
1964 void map_delete_selected(appdata_t *appdata) {
1965 map_t *map = appdata->map;
1966
1967 if(!yes_no_f(GTK_WIDGET(appdata->window),
1968 appdata, MISC_AGAIN_ID_DELETE, MISC_AGAIN_FLAG_DONT_SAVE_NO,
1969 _("Delete selected object?"),
1970 _("Do you really want to delete the selected object?")))
1971 return;
1972
1973 /* work on local copy since de-selecting destroys the selection */
1974 map_item_t item = map->selected;
1975
1976 /* deleting the selected item de-selects it ... */
1977 map_item_deselect(appdata);
1978
1979 undo_open_new_state(appdata, UNDO_DELETE, &item.object);
1980
1981 switch(item.object.type) {
1982 case NODE:
1983 printf("request to delete node #" ITEM_ID_FORMAT "\n",
1984 item.object.node->id);
1985
1986 undo_append_object(appdata, UNDO_DELETE, &item.object);
1987
1988 /* check if this node is part of a way with two nodes only. */
1989 /* we cannot delete this as this would also delete the way */
1990 way_chain_t *way_chain = osm_node_to_way(appdata->osm, item.object.node);
1991 if(way_chain) {
1992 gboolean short_way = FALSE;
1993
1994 /* free the chain of ways */
1995 while(way_chain && !short_way) {
1996 way_chain_t *next = way_chain->next;
1997
1998 if(osm_way_number_of_nodes(way_chain->way) <= 2)
1999 short_way = TRUE;
2000
2001 g_free(way_chain);
2002 way_chain = next;
2003 }
2004
2005 if(short_way) {
2006 if(!yes_no_f(GTK_WIDGET(appdata->window), NULL, 0, 0,
2007 _("Delete node in short way(s)?"),
2008 _("Deleting this node will also delete one or more ways "
2009 "since they'll contain only one node afterwards. "
2010 "Do you really want this?")))
2011 return;
2012 }
2013 }
2014
2015 /* remove it visually from the screen */
2016 map_item_chain_destroy(&item.object.node->map_item_chain);
2017
2018 /* and mark it "deleted" in the database */
2019 osm_node_remove_from_relation(appdata->osm, item.object.node);
2020 way_chain_t *chain = osm_node_delete(appdata->osm,
2021 &appdata->icon, item.object.node, FALSE, TRUE);
2022
2023 /* redraw all affected ways */
2024 while(chain) {
2025 way_chain_t *next = chain->next;
2026
2027 if(osm_way_number_of_nodes(chain->way) == 1) {
2028 /* this way now only contains one node and thus isn't a valid */
2029 /* way anymore. So it'll also get deleted (which in turn may */
2030 /* cause other nodes to be deleted as well) */
2031 undo_append_way(appdata, UNDO_DELETE, chain->way);
2032 map_way_delete(appdata, chain->way);
2033 } else {
2034 map_item_t item;
2035 item.object.type = WAY;
2036 item.object.way = chain->way;
2037 undo_append_object(appdata, UNDO_MODIFY, &(item.object));
2038 map_item_redraw(appdata, &item);
2039 }
2040
2041 g_free(chain);
2042
2043 chain = next;
2044 }
2045
2046 break;
2047
2048 case WAY:
2049 printf("request to delete way #" ITEM_ID_FORMAT "\n", item.object.way->id);
2050 map_way_delete(appdata, item.object.way);
2051 break;
2052
2053 default:
2054 g_assert((item.object.type == NODE) ||
2055 (item.object.type == WAY));
2056 break;
2057 }
2058 undo_close_state(appdata);
2059 }
2060
2061 /* ----------------------- track related stuff ----------------------- */
2062
2063 static gboolean track_pos2lpos(bounds_t *bounds, pos_t *pos, lpos_t *lpos) {
2064 pos2lpos(bounds, pos, lpos);
2065
2066 /* check if point is within bounds */
2067 return ((lpos->x >= bounds->min.x) && (lpos->x <= bounds->max.x) &&
2068 (lpos->y >= bounds->min.y) && (lpos->y <= bounds->max.y));
2069 }
2070
2071 void map_track_draw_seg(map_t *map, track_seg_t *seg) {
2072 bounds_t *bounds = map->appdata->osm->bounds;
2073
2074 /* a track_seg needs at least 2 points to be drawn */
2075 guint pnum = track_seg_points(seg);
2076 printf("seg of length %d\n", pnum);
2077
2078 if(!pnum)
2079 return;
2080
2081 /* nothing should have been drawn by now ... */
2082 g_assert(!seg->item_chain);
2083
2084 track_item_chain_t **itemP = &seg->item_chain;
2085 track_point_t *track_point = seg->track_point;
2086 while(track_point) {
2087 lpos_t lpos;
2088
2089 /* skip all points not on screen */
2090 track_point_t *last = NULL;
2091 while(track_point && !track_pos2lpos(bounds, &track_point->pos, &lpos)) {
2092 last = track_point;
2093 track_point = track_point->next;
2094 }
2095
2096 int visible = 0;
2097
2098 /* count nodes that _are_ on screen */
2099 track_point_t *tmp = track_point;
2100 while(tmp && track_pos2lpos(bounds, &tmp->pos, &lpos)) {
2101 tmp = tmp->next;
2102 visible++;
2103 }
2104
2105 /* actually start drawing with the last position that was offscreen */
2106 /* so the track nicely enters the viewing area */
2107 if(last) {
2108 track_point = last;
2109 visible++;
2110 }
2111
2112 /* also use last one that's offscreen to nicely leave the visible area */
2113 if(tmp && tmp->next)
2114 visible++;
2115
2116 /* allocate space for nodes */
2117 canvas_points_t *points = canvas_points_new(visible);
2118
2119 printf("visible are %d\n", visible);
2120 int point;
2121 for(point=0;point<visible;point++) {
2122 track_pos2lpos(bounds, &track_point->pos, &lpos);
2123
2124 points->coords[2*point+0] = lpos.x;
2125 points->coords[2*point+1] = lpos.y;
2126 track_point = track_point->next;
2127 }
2128
2129 *itemP = g_new0(track_item_chain_t, 1);
2130 (*itemP)->item = canvas_polyline_new(map->canvas, CANVAS_GROUP_TRACK,
2131 points, map->style->track.width, map->style->track.color);
2132 itemP = &(*itemP)->next;
2133
2134 canvas_points_free(points);
2135 }
2136 }
2137
2138 /* update the last visible fragment of this segment since a */
2139 /* gps position may have been added */
2140 void map_track_update_seg(map_t *map, track_seg_t *seg) {
2141 bounds_t *bounds = map->appdata->osm->bounds;
2142
2143 printf("-- APPENDING TO TRACK --\n");
2144
2145 /* a track_seg needs at least 2 points to be drawn */
2146 guint pnum = track_seg_points(seg);
2147 printf("seg of length %d\n", pnum);
2148
2149 /* there are two cases: either the second last point was on screen */
2150 /* or it wasn't. We'll have to start a new screen item if the latter */
2151 /* is the case */
2152
2153 /* search last point */
2154 track_point_t *begin = seg->track_point, *second_last = seg->track_point;
2155 lpos_t lpos;
2156 while(second_last && second_last->next && second_last->next->next) {
2157 if(!track_pos2lpos(bounds, &second_last->pos, &lpos))
2158 begin = second_last;
2159
2160 second_last = second_last->next;
2161 }
2162 track_point_t *last = second_last->next;
2163
2164 /* since we are updating an existing track, it sure has at least two */
2165 /* points, second_last must be valid and its "next" (last) also */
2166 g_assert(second_last);
2167 g_assert(last);
2168
2169 /* check if the last and second_last points are visible */
2170 gboolean last_is_visible =
2171 track_pos2lpos(bounds, &last->pos, &lpos);
2172 gboolean second_last_is_visible =
2173 track_pos2lpos(bounds, &second_last->pos, &lpos);
2174
2175 /* if both are invisible, then nothing has changed on screen */
2176 if(!last_is_visible && !second_last_is_visible) {
2177 printf("second_last and last entry are invisible -> doing nothing\n");
2178 return;
2179 }
2180
2181 /* search last element in item chain */
2182 track_item_chain_t *item = seg->item_chain;
2183 while(item && item->next)
2184 item = item->next;
2185
2186 if(second_last_is_visible) {
2187 /* there must be something already on the screen and there must */
2188 /* be visible nodes in the chain */
2189 g_assert(item);
2190 g_assert(begin);
2191
2192 printf("second_last is visible -> append\n");
2193
2194 /* count points to be placed */
2195 int npoints = 0;
2196 track_point_t *tmp = begin;
2197 while(tmp) {
2198 tmp = tmp->next;
2199 npoints++;
2200 }
2201
2202 printf("updating last segment to %d points\n", npoints);
2203
2204 canvas_points_t *points = canvas_points_new(npoints);
2205
2206 gint point = 0;
2207 while(begin) {
2208 track_pos2lpos(bounds, &begin->pos, &lpos);
2209 canvas_point_set_pos(points, point++, &lpos);
2210 begin = begin->next;
2211 }
2212
2213 canvas_item_set_points(item->item, points);
2214 canvas_points_free(points);
2215
2216 } else {
2217 printf("second last is invisible -> start new screen segment\n");
2218
2219 /* the search for the "begin" ends with the second_last item */
2220 /* verify the last one also */
2221 if(begin->next && !track_pos2lpos(bounds, &begin->next->pos, &lpos))
2222 begin = begin->next;
2223
2224 item->next = g_new0(track_item_chain_t, 1);
2225 item = item->next;
2226
2227 /* count points to be placed */
2228 int npoints = 0;
2229 track_point_t *tmp = begin;
2230 while(tmp) {
2231 tmp = tmp->next;
2232 npoints++;
2233 }
2234
2235 printf("attaching new segment with %d points\n", npoints);
2236
2237 canvas_points_t *points = canvas_points_new(npoints);
2238
2239 gint point = 0;
2240 while(begin) {
2241 track_pos2lpos(bounds, &begin->pos, &lpos);
2242 canvas_point_set_pos(points, point++, &lpos);
2243 begin = begin->next;
2244 }
2245
2246 item->item = canvas_polyline_new(map->canvas, CANVAS_GROUP_TRACK,
2247 points, map->style->track.width, map->style->track.color);
2248
2249 canvas_points_free(points);
2250 }
2251
2252 }
2253
2254 void map_track_draw(map_t *map, track_t *track) {
2255 track_seg_t *seg = track->track_seg;
2256
2257 /* draw all segments */
2258 while(seg) {
2259 map_track_draw_seg(map, seg);
2260 seg = seg->next;
2261 }
2262 }
2263
2264 void map_track_remove(appdata_t *appdata) {
2265 track_t *track = appdata->track.track;
2266
2267 printf("removing track\n");
2268
2269 g_assert(track);
2270
2271 /* remove all segments */
2272 track_seg_t *seg = track->track_seg;
2273 while(seg) {
2274 track_item_chain_t *item = seg->item_chain;
2275 while(item) {
2276 track_item_chain_t *next = item->next;
2277 canvas_item_destroy(item->item);
2278 item = next;
2279 }
2280
2281 seg->item_chain = NULL;
2282 seg = seg->next;
2283 }
2284 }
2285
2286 void map_track_pos(appdata_t *appdata, pos_t *pos) {
2287 if(appdata->track.gps_item) {
2288 canvas_item_destroy(appdata->track.gps_item);
2289 appdata->track.gps_item = NULL;
2290 }
2291
2292 if(pos) {
2293 lpos_t lpos;
2294 pos2lpos(appdata->osm->bounds, pos, &lpos);
2295
2296 appdata->track.gps_item =
2297 canvas_circle_new(appdata->map->canvas, CANVAS_GROUP_GPS,
2298 lpos.x, lpos.y, appdata->map->style->track.width/2.0, 0,
2299 appdata->map->style->track.gps_color, NO_COLOR);
2300 }
2301 }
2302
2303 /* ------------------- map background ------------------ */
2304
2305 void map_remove_bg_image(map_t *map) {
2306 if(!map) return;
2307
2308 if(map->bg.item) {
2309 canvas_item_destroy(map->bg.item);
2310 map->bg.item = NULL;
2311 }
2312 }
2313
2314 static gint map_bg_item_destroy_event(GtkWidget *widget, gpointer data) {
2315 map_t *map = (map_t*)data;
2316
2317 /* destroying background item */
2318
2319 map->bg.item = NULL;
2320 if(map->bg.pix) {
2321 printf("destroying background item\n");
2322 gdk_pixbuf_unref(map->bg.pix);
2323 map->bg.pix = NULL;
2324 }
2325 return FALSE;
2326 }
2327
2328 void map_set_bg_image(map_t *map, char *filename) {
2329 bounds_t *bounds = map->appdata->osm->bounds;
2330
2331 map_remove_bg_image(map);
2332
2333 map->bg.pix = gdk_pixbuf_new_from_file(filename, NULL);
2334
2335 /* calculate required scale factor */
2336 map->bg.scale.x = (float)(bounds->max.x - bounds->min.x)/
2337 (float)gdk_pixbuf_get_width(map->bg.pix);
2338 map->bg.scale.y = (float)(bounds->max.y - bounds->min.y)/
2339 (float)gdk_pixbuf_get_height(map->bg.pix);
2340
2341 map->bg.item = canvas_image_new(map->canvas, CANVAS_GROUP_BG, map->bg.pix,
2342 bounds->min.x, bounds->min.y, map->bg.scale.x, map->bg.scale.y);
2343
2344 canvas_item_destroy_connect(map->bg.item,
2345 G_CALLBACK(map_bg_item_destroy_event), map);
2346 }
2347
2348
2349 /* -------- hide and show objects (for performance reasons) ------- */
2350
2351 void map_hide_selected(appdata_t *appdata) {
2352 map_t *map = appdata->map;
2353 if(!map) return;
2354
2355 if(map->selected.object.type != WAY) {
2356 printf("selected item is not a way\n");
2357 return;
2358 }
2359
2360 way_t *way = map->selected.object.way;
2361 printf("hiding way #" ITEM_ID_FORMAT "\n", way->id);
2362
2363 map_item_deselect(appdata);
2364 way->flags |= OSM_FLAG_HIDDEN;
2365 map_item_chain_destroy(&way->map_item_chain);
2366
2367 gtk_widget_set_sensitive(appdata->menu_item_map_show_all, TRUE);
2368 }
2369
2370 void map_show_all(appdata_t *appdata) {
2371 map_t *map = appdata->map;
2372 if(!map) return;
2373
2374 way_t *way = appdata->osm->way;
2375 while(way) {
2376 if(way->flags & OSM_FLAG_HIDDEN) {
2377 way->flags &= ~OSM_FLAG_HIDDEN;
2378 map_way_draw(map, way);
2379 }
2380
2381 way = way->next;
2382 }
2383
2384 gtk_widget_set_sensitive(appdata->menu_item_map_show_all, FALSE);
2385 }
2386
2387 static void map_detail_change(map_t *map, float detail) {
2388 appdata_t *appdata = map->appdata;
2389
2390 /* deselecting anything allows us not to care about automatic deselection */
2391 /* as well as items becoming invisible by the detail change */
2392 map_item_deselect(appdata);
2393
2394 map->state->detail = detail;
2395 printf("changing detail factor to %f\n", map->state->detail);
2396
2397 banner_busy_start(appdata, 1, _("Redrawing"));
2398 map_clear(appdata, MAP_LAYER_OBJECTS_ONLY);
2399 map_paint(appdata);
2400 banner_busy_stop(appdata);
2401 }
2402
2403 #define DETAIL_STEP 1.5
2404
2405 void map_detail_increase(map_t *map) {
2406 if(!map) return;
2407 map_detail_change(map, map->state->detail * DETAIL_STEP);
2408 }
2409
2410 void map_detail_decrease(map_t *map) {
2411 if(!map) return;
2412 map_detail_change(map, map->state->detail / DETAIL_STEP);
2413 }
2414
2415 void map_detail_normal(map_t *map) {
2416 if(!map) return;
2417 map_detail_change(map, 1.0);
2418 }
2419
2420 // vim:et:ts=8:sw=2:sts=2:ai