Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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