Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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