Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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