Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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