Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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