Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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