Contents of /trunk/src/map.c

Parent Directory Parent Directory | Revision Log Revision Log


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