Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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