Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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