Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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