Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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