Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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