Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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