Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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