Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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