Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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