Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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