Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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