--- src/map.c 2008/12/09 20:06:06 1 +++ trunk/src/map.c 2009/05/02 21:36:51 173 @@ -30,17 +30,23 @@ tag_t *tag = NULL; char *str = NULL; - switch(map_item->type) { - case MAP_TYPE_NODE: + switch(map_item->object.type) { + case NODE: item_str = "Node"; - id = map_item->node->id; - tag = map_item->node->tag; + id = map_item->object.node->id; + tag = map_item->object.node->tag; break; - case MAP_TYPE_WAY: + case WAY: item_str = "Way"; - id = map_item->way->id; - tag = map_item->way->tag; + id = map_item->object.way->id; + tag = map_item->object.way->tag; + break; + + case RELATION: + item_str = "Relation"; + id = map_item->object.relation->id; + tag = map_item->object.relation->tag; break; default: @@ -53,9 +59,18 @@ if(id == ID_ILLEGAL) str = g_strdup_printf(_("Unknown item")); else { - str = g_strdup_printf("%s #%ld", item_str, id); + str = g_strdup_printf("%s #" ITEM_ID_FORMAT, item_str, id); /* add some tags ... */ + /* + * XXX Should we just try to present only the name or the ref (or the + * alt_name, old_name, whatever) here? Hurling a load of tags in the + * user's face in some unpredictable, uninformative order isn't very + * friendly. + * + * Actually, a tag_short_desc() function would be useful in dialogs + * nd user messages too. + */ while(tag) { if(!collision && info_tag_key_collision(tags, tag)) collision = TRUE; @@ -123,8 +138,8 @@ g_assert(!map->highlight); - map_item->type = MAP_TYPE_NODE; - map_item->node = node; + map_item->object.type = NODE; + map_item->object.node = node; map_item->highlight = FALSE; /* node may not have any visible representation at all */ @@ -137,7 +152,7 @@ icon_bar_map_item_selected(appdata, map_item, TRUE); /* highlight node */ - gint x = map_item->node->lpos.x, y = map_item->node->lpos.y; + gint x = map_item->object.node->lpos.x, y = map_item->object.node->lpos.y; /* create a copy of this map item and mark it as being a highlight */ map_item_t *new_map_item = g_new0(map_item_t, 1); @@ -146,23 +161,25 @@ float radius = map->style->highlight.width + map->style->node.radius; if(!node->ways) radius += map->style->node.border_radius; - if(node->icon_buf && map->style->icon.enable) { - gint w = gdk_pixbuf_get_width(map_item->node->icon_buf); - gint h = gdk_pixbuf_get_height(map_item->node->icon_buf); + if(node->icon_buf && map->style->icon.enable && + !appdata->settings->no_icons) { + gint w = gdk_pixbuf_get_width(map_item->object.node->icon_buf); + gint h = gdk_pixbuf_get_height(map_item->object.node->icon_buf); /* icons are technically square, so a radius slightly bigger */ /* than sqrt(2)*MAX(w,h) should fit nicely */ - radius = 0.75 * map->style->icon.scale * ((w>h)?w:h); + radius = 0.75 * map->style->icon.scale * ((w>h)?w:h); } + radius *= map->state->detail; map_hl_circle_new(map, CANVAS_GROUP_NODES_HL, new_map_item, x, y, radius, map->style->highlight.color); - + if(!map_item->item) { /* and draw a fake node */ new_map_item = g_new0(map_item_t, 1); memcpy(new_map_item, map_item, sizeof(map_item_t)); new_map_item->highlight = TRUE; - map_hl_circle_new(map, CANVAS_GROUP_NODES_HL, new_map_item, + map_hl_circle_new(map, CANVAS_GROUP_NODES_IHL, new_map_item, x, y, map->style->node.radius, map->style->highlight.node_color); } @@ -174,32 +191,33 @@ g_assert(!map->highlight); - map_item->type = MAP_TYPE_WAY; - map_item->way = way; + map_item->object.type = WAY; + map_item->object.way = way; map_item->highlight = FALSE; map_item->item = way->map_item_chain->map_item->item; map_statusbar(map, map_item); icon_bar_map_item_selected(appdata, map_item, TRUE); gtk_widget_set_sensitive(appdata->menu_item_map_hide_sel, TRUE); - - gint arrow_width = (map_item->way->draw.flags & OSM_DRAW_FLAG_BG)? - map->style->highlight.width + map_item->way->draw.bg.width/2: - map->style->highlight.width + map_item->way->draw.width/2; - node_chain_t *node_chain = map_item->way->node_chain; + gint arrow_width = ((map_item->object.way->draw.flags & OSM_DRAW_FLAG_BG)? + map->style->highlight.width + map_item->object.way->draw.bg.width/2: + map->style->highlight.width + map_item->object.way->draw.width/2) + * map->state->detail; + + node_chain_t *node_chain = map_item->object.way->node_chain; node_t *last = NULL; while(node_chain) { map_item_t item; - item.type = MAP_TYPE_NODE; - item.node = node_chain->node; + item.object.type = NODE; + item.object.node = node_chain->node; /* draw an arrow between every two nodes */ if(last) { /* create a new map item for every arrow */ map_item_t *new_map_item = g_new0(map_item_t, 1); - new_map_item->type = MAP_TYPE_WAY; - new_map_item->way = way; + new_map_item->object.type = WAY; + new_map_item->object.way = way; new_map_item->highlight = TRUE; struct { float x, y;} center, diff; @@ -208,6 +226,8 @@ diff.x = node_chain->node->lpos.x - last->lpos.x; diff.y = node_chain->node->lpos.y - last->lpos.y; + /* only draw arrow if there's sufficient space */ + /* TODO: what if there's not enough space anywhere? */ float len = sqrt(pow(diff.x, 2)+pow(diff.y, 2)); if(len > map->style->highlight.arrow_limit*arrow_width) { len /= arrow_width; @@ -222,7 +242,7 @@ points->coords[2*2+0] = center.x - diff.y - diff.x; points->coords[2*2+1] = center.y + diff.x - diff.y; - map_hl_polygon_new(map, CANVAS_GROUP_NODES_HL, new_map_item, + map_hl_polygon_new(map, CANVAS_GROUP_WAYS_DIR, new_map_item, points, map->style->highlight.arrow_color); canvas_points_free(points); @@ -233,15 +253,15 @@ /* create a new map item for every node */ map_item_t *new_map_item = g_new0(map_item_t, 1); - new_map_item->type = MAP_TYPE_NODE; - new_map_item->node = node_chain->node; + new_map_item->object.type = NODE; + new_map_item->object.node = node_chain->node; new_map_item->highlight = TRUE; gint x = node_chain->node->lpos.x; gint y = node_chain->node->lpos.y; - map_hl_circle_new(map, CANVAS_GROUP_NODES_HL, new_map_item, - x, y, map->style->node.radius, + map_hl_circle_new(map, CANVAS_GROUP_NODES_IHL, new_map_item, + x, y, map->style->node.radius * map->state->detail, map->style->highlight.node_color); } @@ -257,7 +277,7 @@ canvas_points_t *points = canvas_points_new(nodes); int node = 0; - node_chain = map_item->way->node_chain; + node_chain = map_item->object.way->node_chain; while(node_chain) { canvas_point_set_pos(points, node++, &node_chain->node->lpos); node_chain = node_chain->next; @@ -269,26 +289,107 @@ new_map_item->highlight = TRUE; map_hl_polyline_new(map, CANVAS_GROUP_WAYS_HL, new_map_item, points, - (map_item->way->draw.flags & OSM_DRAW_FLAG_BG)? - 2*map->style->highlight.width + map_item->way->draw.bg.width: - 2*map->style->highlight.width + map_item->way->draw.width, - map->style->highlight.color); - + ((map_item->object.way->draw.flags & OSM_DRAW_FLAG_BG)? + 2*map->style->highlight.width + map_item->object.way->draw.bg.width: + 2*map->style->highlight.width + map_item->object.way->draw.width) + * map->state->detail, map->style->highlight.color); + canvas_points_free(points); } } -static void map_item_select(appdata_t *appdata, map_item_t *map_item) { - switch(map_item->type) { - case MAP_TYPE_NODE: - map_node_select(appdata, map_item->node); +void map_relation_select(appdata_t *appdata, relation_t *relation) { + map_t *map = appdata->map; + + printf("highlighting relation "ITEM_ID_FORMAT"\n", relation->id); + + g_assert(!map->highlight); + map_highlight_t **hl = &map->highlight; + + map_item_t *map_item = &map->selected; + map_item->object.type = RELATION; + map_item->object.relation = relation; + map_item->highlight = FALSE; + map_item->item = NULL; + + map_statusbar(map, map_item); + icon_bar_map_item_selected(appdata, map_item, TRUE); + + /* process all members */ + member_t *member = relation->member; + while(member) { + canvas_item_t *item = NULL; + + switch(member->object.type) { + + case NODE: { + node_t *node = member->object.node; + printf(" -> node "ITEM_ID_FORMAT"\n", node->id); + + item = canvas_circle_new(map->canvas, CANVAS_GROUP_NODES_HL, + node->lpos.x, node->lpos.y, + map->style->highlight.width + map->style->node.radius, + 0, map->style->highlight.color, NO_COLOR); + } break; + + case WAY: { + way_t *way = member->object.way; + /* a way needs at least 2 points to be drawn */ + guint nodes = osm_way_number_of_nodes(way); + if(nodes > 1) { + + /* allocate space for nodes */ + canvas_points_t *points = canvas_points_new(nodes); + + int node = 0; + node_chain_t *node_chain = way->node_chain; + while(node_chain) { + canvas_point_set_pos(points, node++, &node_chain->node->lpos); + node_chain = node_chain->next; + } + + if(way->draw.flags & OSM_DRAW_FLAG_AREA) + item = canvas_polygon_new(map->canvas, CANVAS_GROUP_WAYS_HL, points, 0, 0, + map->style->highlight.color); + else + item = canvas_polyline_new(map->canvas, CANVAS_GROUP_WAYS_HL, points, + (way->draw.flags & OSM_DRAW_FLAG_BG)? + 2*map->style->highlight.width + way->draw.bg.width: + 2*map->style->highlight.width + way->draw.width, + map->style->highlight.color); + + canvas_points_free(points); + } } break; + + default: + break; + } + + /* attach item to item chain */ + if(item) { + *hl = g_new0(map_highlight_t, 1); + (*hl)->item = item; + hl = &(*hl)->next; + } + + member = member->next; + } +} + +static void map_object_select(appdata_t *appdata, object_t *object) { + switch(object->type) { + case NODE: + map_node_select(appdata, object->node); + break; + case WAY: + map_way_select(appdata, object->way); break; - case MAP_TYPE_WAY: - map_way_select(appdata, map_item->way); + case RELATION: + map_relation_select(appdata, object->relation); break; default: - g_assert((map_item->type == MAP_TYPE_NODE)|| - (map_item->type == MAP_TYPE_WAY)); + g_assert((object->type == NODE)||(object->type == RELATION)|| + (object->type == WAY)); break; } } @@ -296,18 +397,18 @@ void map_item_deselect(appdata_t *appdata) { /* save tags for "last" function in info dialog */ - if(appdata->map->selected.type == MAP_TYPE_NODE) { + if(appdata->map->selected.object.type == NODE) { if(appdata->map->last_node_tags) osm_tags_free(appdata->map->last_node_tags); appdata->map->last_node_tags = - osm_tags_copy(appdata->map->selected.node->tag, FALSE); - } else if(appdata->map->selected.type == MAP_TYPE_WAY) { + osm_tags_copy(appdata->map->selected.object.node->tag, FALSE); + } else if(appdata->map->selected.object.type == WAY) { if(appdata->map->last_way_tags) osm_tags_free(appdata->map->last_way_tags); appdata->map->last_way_tags = - osm_tags_copy(appdata->map->selected.way->tag, FALSE); + osm_tags_copy(appdata->map->selected.object.way->tag, FALSE); } /* remove statusbar message */ @@ -321,7 +422,7 @@ map_hl_remove(appdata); /* forget about selection */ - appdata->map->selected.type = MAP_TYPE_ILLEGAL; + appdata->map->selected.object.type = ILLEGAL; } /* called whenever a map item is to be destroyed */ @@ -333,10 +434,10 @@ #ifdef DESTROY_WAIT_FOR_GTK /* remove item from nodes/ways map_item_chain */ map_item_chain_t **chain = NULL; - if(map_item->type == MAP_TYPE_NODE) - chain = &map_item->node->map_item_chain; - else if(map_item->type == MAP_TYPE_WAY) - chain = &map_item->way->map_item_chain; + if(map_item->object.type == NODE) + chain = &map_item->object.node->map_item_chain; + else if(map_item->object.type == WAY) + chain = &map_item->object.way->map_item_chain; /* there must be a chain with content, otherwise things are broken */ g_assert(chain); @@ -363,22 +464,25 @@ gint width, canvas_color_t fill, canvas_color_t border) { map_item_t *map_item = g_new0(map_item_t, 1); - map_item->type = MAP_TYPE_NODE; - map_item->node = node; + map_item->object.type = NODE; + map_item->object.node = node; - if(!node->icon_buf || !map->style->icon.enable) - map_item->item = canvas_circle_new(map, CANVAS_GROUP_NODES, + if(!node->icon_buf || !map->style->icon.enable || + map->appdata->settings->no_icons) + map_item->item = canvas_circle_new(map->canvas, CANVAS_GROUP_NODES, node->lpos.x, node->lpos.y, radius, width, fill, border); else - map_item->item = canvas_image_new(map, CANVAS_GROUP_NODES, + map_item->item = canvas_image_new(map->canvas, CANVAS_GROUP_NODES, node->icon_buf, - node->lpos.x - map->style->icon.scale/2 * + node->lpos.x - map->style->icon.scale/2 * map->state->detail * gdk_pixbuf_get_width(node->icon_buf), - node->lpos.y - map->style->icon.scale/2 * + node->lpos.y - map->style->icon.scale/2 * map->state->detail * gdk_pixbuf_get_height(node->icon_buf), - map->style->icon.scale,map->style->icon.scale); + map->state->detail * map->style->icon.scale, + map->state->detail * map->style->icon.scale); - canvas_item_set_zoom_max(map_item->item, node->zoom_max); + canvas_item_set_zoom_max(map_item->item, + node->zoom_max / (2 * map->state->detail)); /* attach map_item to nodes map_item_chain */ map_item_chain_t **chain = &node->map_item_chain; @@ -400,9 +504,9 @@ gint width, canvas_color_t fill, canvas_color_t border) { map_item_t *map_item = g_new0(map_item_t, 1); - map_item->type = MAP_TYPE_WAY; - map_item->way = way; - map_item->item = canvas_circle_new(map, CANVAS_GROUP_WAYS, + map_item->object.type = WAY; + map_item->object.way = way; + map_item->item = canvas_circle_new(map->canvas, CANVAS_GROUP_WAYS, way->node_chain->node->lpos.x, way->node_chain->node->lpos.y, radius, width, fill, border); @@ -426,21 +530,27 @@ way_t *way, canvas_points_t *points, gint width, canvas_color_t color, canvas_color_t fill_color) { map_item_t *map_item = g_new0(map_item_t, 1); - map_item->type = MAP_TYPE_WAY; - map_item->way = way; + map_item->object.type = WAY; + map_item->object.way = way; if(way->draw.flags & OSM_DRAW_FLAG_AREA) { - if(map->style->area.opaque) - map_item->item = canvas_polygon_new(map, group, points, + if(map->style->area.color & 0xff) + map_item->item = canvas_polygon_new(map->canvas, group, points, width, color, fill_color); else - map_item->item = canvas_polyline_new(map, group, points, + map_item->item = canvas_polyline_new(map->canvas, group, points, width, color); } else { - map_item->item = canvas_polyline_new(map, group, points, width, color); + map_item->item = canvas_polyline_new(map->canvas, group, points, width, color); } - canvas_item_set_zoom_max(map_item->item, way->draw.zoom_max); + canvas_item_set_zoom_max(map_item->item, + way->draw.zoom_max / (2 * map->state->detail)); + + /* a ways outline itself is never dashed */ + if (group != CANVAS_GROUP_WAYS_OL) + if (way->draw.dashed) + canvas_item_set_dashed(map_item->item, width, way->draw.dash_length); /* attach map_item to ways map_item_chain */ map_item_chain_t **chain = &way->map_item_chain; @@ -458,7 +568,7 @@ void map_show_node(map_t *map, node_t *node) { map_node_new(map, node, map->style->node.radius, 0, - RGB2CANVAS(map->style->node.color), 0); + map->style->node.color, 0); } void map_way_draw(map_t *map, way_t *way) { @@ -473,7 +583,7 @@ if(nodes == 1) { /* draw a single dot where this single node is */ map_way_single_new(map, way, map->style->node.radius, 0, - RGB2CANVAS(map->style->node.color), 0); + map->style->node.color, 0); } else { canvas_points_t *points = canvas_points_new(nodes); @@ -485,16 +595,24 @@ } /* draw way */ + float width = way->draw.width * map->state->detail; + if(way->draw.flags & OSM_DRAW_FLAG_AREA) { map_way_new(map, CANVAS_GROUP_POLYGONS, way, points, - way->draw.width, way->draw.color, way->draw.area.color); + width, way->draw.color, way->draw.area.color); } else { - map_way_new(map, CANVAS_GROUP_WAYS, way, points, - way->draw.width, way->draw.color, NO_COLOR); - - if(way->draw.flags & OSM_DRAW_FLAG_BG) + + if(way->draw.flags & OSM_DRAW_FLAG_BG) { + map_way_new(map, CANVAS_GROUP_WAYS_INT, way, points, + width, way->draw.color, NO_COLOR); + map_way_new(map, CANVAS_GROUP_WAYS_OL, way, points, - way->draw.bg.width, way->draw.bg.color, NO_COLOR); + way->draw.bg.width * map->state->detail, + way->draw.bg.color, NO_COLOR); + + } else + map_way_new(map, CANVAS_GROUP_WAYS, way, points, + width, way->draw.color, NO_COLOR); } canvas_points_free(points); } @@ -507,61 +625,60 @@ if(!node->ways) map_node_new(map, node, - map->style->node.radius, - map->style->node.border_radius, - RGBA2CANVAS(map->style->node.fill_color, - map->style->node.has_fill_color?0xff:0x00), - RGB2CANVAS(map->style->node.color)); + map->style->node.radius * map->state->detail, + map->style->node.border_radius * map->state->detail, + map->style->node.fill_color, + map->style->node.color); else if(map->style->node.show_untagged || osm_node_has_tag(node)) map_node_new(map, node, - map->style->node.radius, 0, - RGB2CANVAS(map->style->node.color), 0); + map->style->node.radius * map->state->detail, 0, + map->style->node.color, 0); } static void map_item_draw(map_t *map, map_item_t *map_item) { - switch(map_item->type) { - case MAP_TYPE_NODE: - map_node_draw(map, map_item->node); + switch(map_item->object.type) { + case NODE: + map_node_draw(map, map_item->object.node); break; - case MAP_TYPE_WAY: - map_way_draw(map, map_item->way); + case WAY: + map_way_draw(map, map_item->object.way); break; default: - g_assert((map_item->type == MAP_TYPE_NODE) || - (map_item->type == MAP_TYPE_WAY)); + g_assert((map_item->object.type == NODE) || + (map_item->object.type == WAY)); } } static void map_item_remove(map_t *map, map_item_t *map_item) { map_item_chain_t **chainP = NULL; - switch(map_item->type) { - case MAP_TYPE_NODE: - chainP = &map_item->node->map_item_chain; + switch(map_item->object.type) { + case NODE: + chainP = &map_item->object.node->map_item_chain; break; - case MAP_TYPE_WAY: - chainP = &map_item->way->map_item_chain; + case WAY: + chainP = &map_item->object.way->map_item_chain; break; default: - g_assert((map_item->type == MAP_TYPE_NODE) || - (map_item->type == MAP_TYPE_WAY)); + g_assert((map_item->object.type == NODE) || + (map_item->object.type == WAY)); } map_item_chain_destroy(chainP); } static void map_item_init(style_t *style, map_item_t *map_item) { - switch (map_item->type){ - case MAP_TYPE_WAY: - josm_elemstyles_colorize_way(style, map_item->way); + switch (map_item->object.type){ + case WAY: + josm_elemstyles_colorize_way(style, map_item->object.way); break; - case MAP_TYPE_NODE: - josm_elemstyles_colorize_node(style, map_item->node); + case NODE: + josm_elemstyles_colorize_node(style, map_item->object.node); break; default: - g_assert((map_item->type == MAP_TYPE_NODE) || - (map_item->type == MAP_TYPE_WAY)); + g_assert((map_item->object.type == NODE) || + (map_item->object.type == WAY)); } } @@ -570,7 +687,7 @@ /* check if the item to be redrawn is the selected one */ gboolean is_selected = FALSE; - if(map_item->ptr == appdata->map->selected.ptr) { + if(map_item->object.ptr == appdata->map->selected.object.ptr) { map_item_deselect(appdata); is_selected = TRUE; } @@ -581,7 +698,7 @@ /* restore selection if there was one */ if(is_selected) - map_item_select(appdata, &item); + map_object_select(appdata, &item.object); } static void map_frisket_rectangle(canvas_points_t *points, @@ -598,31 +715,34 @@ canvas_points_t *points = canvas_points_new(5); /* don't draw frisket at all if it's completely transparent */ - if(map->style->frisket.opaque) { - elemstyle_color_t color = - (map->style->background.color<<8) | map->style->frisket.opaque; + if(map->style->frisket.color & 0xff) { + elemstyle_color_t color = map->style->frisket.color; float mult = map->style->frisket.mult; /* top rectangle */ map_frisket_rectangle(points, mult*bounds->min.x, mult*bounds->max.x, mult*bounds->min.y, bounds->min.y); - canvas_polygon_new(map, CANVAS_GROUP_NODES, points, 1, NO_COLOR, color); + canvas_polygon_new(map->canvas, CANVAS_GROUP_FRISKET, points, + 1, NO_COLOR, color); /* bottom rectangle */ map_frisket_rectangle(points, mult*bounds->min.x, mult*bounds->max.x, bounds->max.y, mult*bounds->max.y); - canvas_polygon_new(map, CANVAS_GROUP_NODES, points, 1, NO_COLOR, color); + canvas_polygon_new(map->canvas, CANVAS_GROUP_FRISKET, points, + 1, NO_COLOR, color); /* left rectangle */ map_frisket_rectangle(points, mult*bounds->min.x, bounds->min.x, mult*bounds->min.y, mult*bounds->max.y); - canvas_polygon_new(map, CANVAS_GROUP_NODES, points, 1, NO_COLOR, color); + canvas_polygon_new(map->canvas, CANVAS_GROUP_FRISKET, points, + 1, NO_COLOR, color); /* right rectangle */ map_frisket_rectangle(points, bounds->max.x, mult*bounds->max.x, mult*bounds->min.y, mult*bounds->max.y); - canvas_polygon_new(map, CANVAS_GROUP_NODES, points, 1, NO_COLOR, color); + canvas_polygon_new(map->canvas, CANVAS_GROUP_FRISKET, points, + 1, NO_COLOR, color); } @@ -633,7 +753,7 @@ bounds->min.x-ew2, bounds->max.x+ew2, bounds->min.y-ew2, bounds->max.y+ew2); - canvas_polyline_new(map, CANVAS_GROUP_NODES, points, + canvas_polyline_new(map->canvas, CANVAS_GROUP_FRISKET, points, map->style->frisket.border.width, map->style->frisket.border.color); @@ -765,15 +885,15 @@ return NULL; } - if(map_item->highlight) + if(map_item->highlight) printf(" item is highlight\n"); - switch(map_item->type) { - case MAP_TYPE_NODE: - printf(" item is node #%ld\n", map_item->node->id); + switch(map_item->object.type) { + case NODE: + printf(" item is node #"ITEM_ID_FORMAT"\n", map_item->object.node->id); break; - case MAP_TYPE_WAY: - printf(" item is way #%ld\n", map_item->way->id); + case WAY: + printf(" item is way #"ITEM_ID_FORMAT"\n", map_item->object.way->id); break; default: printf(" unknown item\n"); @@ -784,7 +904,7 @@ } /* get the real item (no highlight) at x, y */ -static map_item_t *map_real_item_at(map_t *map, gint x, gint y) { +map_item_t *map_real_item_at(map_t *map, gint x, gint y) { map_item_t *map_item = map_item_at(map, x, y); /* no item or already a real one */ @@ -792,27 +912,29 @@ /* get the item (parent) this item is the highlight of */ map_item_t *parent = NULL; - switch(map_item->type) { + switch(map_item->object.type) { - case MAP_TYPE_NODE: - if(map_item->node->map_item_chain) - parent = map_item->node->map_item_chain->map_item; + case NODE: + if(map_item->object.node->map_item_chain) + parent = map_item->object.node->map_item_chain->map_item; if(parent) - printf(" using parent item node #%ld\n", parent->node->id); + printf(" using parent item node #" ITEM_ID_FORMAT "\n", + parent->object.node->id); break; - case MAP_TYPE_WAY: - if(map_item->way->map_item_chain) - parent = map_item->way->map_item_chain->map_item; + case WAY: + if(map_item->object.way->map_item_chain) + parent = map_item->object.way->map_item_chain->map_item; if(parent) - printf(" using parent item way #%ld\n", parent->way->id); + printf(" using parent item way #" ITEM_ID_FORMAT "\n", + parent->object.way->id); break; default: - g_assert((map_item->type == MAP_TYPE_NODE) || - (map_item->type == MAP_TYPE_WAY)); + g_assert((map_item->object.type == NODE) || + (map_item->object.type == WAY)); break; } @@ -824,39 +946,39 @@ return map_item; } - - -#ifdef USE_GOOCANVAS - /* Limitations on the amount by which we can scroll. Keeps part of the * map visible at all times */ -static void map_limit_scroll(map_t *map, gint *sx, gint *sy) { - gdouble zoom = goo_canvas_get_scale(GOO_CANVAS(map->canvas)); +static void map_limit_scroll(map_t *map, canvas_unit_t unit, + gint *sx, gint *sy) { - gint sx_cu = *sx / zoom; - gint sy_cu = *sy / zoom; - - // Canvas viewport dimensions - GtkAllocation *a = >K_WIDGET(map->canvas)->allocation; - gint aw_cu = a->width / zoom; - gint ah_cu = a->height / zoom; - - // Data rect minimum and maximum - gint min_x, min_y, max_x, max_y; - min_x = map->appdata->osm->bounds->min.x; - min_y = map->appdata->osm->bounds->min.y; - max_x = map->appdata->osm->bounds->max.x; - max_y = map->appdata->osm->bounds->max.y; - - // limit stops - prevent scrolling beyond these - gint min_sy_cu = 0.95*(min_y - ah_cu); - gint min_sx_cu = 0.95*(min_x - aw_cu); - gint max_sy_cu = 0.95*(max_y); - gint max_sx_cu = 0.95*(max_x); - if (sy_cu < min_sy_cu) { *sy = min_sy_cu*zoom; } - if (sx_cu < min_sx_cu) { *sx = min_sx_cu*zoom; } - if (sy_cu > max_sy_cu) { *sy = max_sy_cu*zoom; } - if (sx_cu > max_sx_cu) { *sx = max_sx_cu*zoom; } + /* get scale factor for pixel->meter conversion. set to 1 if */ + /* given coordinates are already in meters */ + gdouble scale = (unit == CANVAS_UNIT_METER)?1.0:canvas_get_zoom(map->canvas); + + /* convert pixels to meters if necessary */ + gdouble sx_cu = *sx / scale; + gdouble sy_cu = *sy / scale; + + /* get size of visible area in canvas units (meters) */ + gint aw_cu = canvas_get_viewport_width(map->canvas, CANVAS_UNIT_METER); + gint ah_cu = canvas_get_viewport_height(map->canvas, CANVAS_UNIT_METER); + + // Data rect minimum and maximum + gint min_x, min_y, max_x, max_y; + min_x = map->appdata->osm->bounds->min.x; + min_y = map->appdata->osm->bounds->min.y; + max_x = map->appdata->osm->bounds->max.x; + max_y = map->appdata->osm->bounds->max.y; + + // limit stops - prevent scrolling beyond these + gint min_sy_cu = 0.95*(min_y - ah_cu); + gint min_sx_cu = 0.95*(min_x - aw_cu); + gint max_sy_cu = 0.95*(max_y); + gint max_sx_cu = 0.95*(max_x); + if (sy_cu < min_sy_cu) { *sy = min_sy_cu * scale; } + if (sx_cu < min_sx_cu) { *sx = min_sx_cu * scale; } + if (sy_cu > max_sy_cu) { *sy = max_sy_cu * scale; } + if (sx_cu > max_sx_cu) { *sx = max_sx_cu * scale; } } @@ -870,10 +992,12 @@ max_x = map->appdata->osm->bounds->max.x; max_y = map->appdata->osm->bounds->max.y; - // Canvas viewport dimensions - GtkAllocation *a = >K_WIDGET(map->canvas)->allocation; - gint ah_cu = a->height / *zoom; - gint aw_cu = a->width / *zoom; + /* get size of visible area in pixels and convert to meters of intended */ + /* zoom by deviding by zoom (which is basically pix/m) */ + gint aw_cu = + canvas_get_viewport_width(map->canvas, CANVAS_UNIT_PIXEL) / *zoom; + gint ah_cu = + canvas_get_viewport_height(map->canvas, CANVAS_UNIT_PIXEL) / *zoom; gdouble oldzoom = *zoom; if (ah_cu < aw_cu) { @@ -898,94 +1022,87 @@ } -#if 0 -/* Scroll the map a little towards the centre from where it is right now. - * This is used as a cute recentring trick when the map is at its outer - * scroll limit. */ -static void map_scroll_towards_centre(map_t *map, gdouble amt) { - gint sx, sy, sx_orig, sy_orig; - canvas_get_scroll_offsets(map->canvas, &sx, &sy); - gdouble zoom = goo_canvas_get_scale(GOO_CANVAS(map->canvas)); - sx_orig=sx; - sy_orig=sy; - - // Work in canvas units - gdouble sx_cu = sx / zoom; - gdouble sy_cu = sy / zoom; - - // Map bounds - gdouble bmin_x_cu, bmin_y_cu, bmax_x_cu, bmax_y_cu; - bmin_x_cu = map->appdata->osm->bounds->min.x; - bmin_y_cu = map->appdata->osm->bounds->min.y; - bmax_x_cu = map->appdata->osm->bounds->max.x; - bmax_y_cu = map->appdata->osm->bounds->max.y; - - // Canvas viewport dimensions - GtkAllocation *a = >K_WIDGET(map->canvas)->allocation; - gdouble ah_cu = a->height / zoom; - gdouble aw_cu = a->width / zoom; - - // Scroll offsets that would recentre the map - gdouble centre_sx_cu, centre_sy_cu; - centre_sx_cu = - (aw_cu/2); - centre_sy_cu = - (ah_cu/2); - - // Move towards centre by a given fraction of the whole map - if (sx_cu > centre_sx_cu) { - sx_cu -= ((bmax_x_cu - bmin_x_cu) * amt); - if (sx_cu < centre_sx_cu) { - printf("force-centre-x\n"); - sx_cu = centre_sx_cu; - } - } - if (sx_cu < centre_sx_cu) { - sx_cu += ((bmax_x_cu - bmin_x_cu) * amt); - if (sx_cu > centre_sx_cu) { - printf("force-centre-x\n"); - sx_cu = centre_sx_cu; - } - } +/* + * Scroll the map to a point if that point is currently offscreen. + */ +void map_scroll_to_if_offscreen(map_t *map, lpos_t *lpos) { - if (sy_cu > centre_sy_cu) { - sy_cu -= ((bmax_y_cu - bmin_y_cu) * amt); - if (sy_cu < centre_sy_cu) { - printf("force-centre-y\n"); - sy_cu = centre_sy_cu; - } - } - if (sy_cu < centre_sy_cu) { - sy_cu += ((bmax_y_cu - bmin_y_cu) * amt); - if (sy_cu > centre_sy_cu) { - printf("force-centre-y\n"); - sy_cu = centre_sy_cu; - } - } + // Ignore anything outside the working area + if (!(map && map->appdata && map->appdata->osm)) { + return; + } + gint min_x, min_y, max_x, max_y; + min_x = map->appdata->osm->bounds->min.x; + min_y = map->appdata->osm->bounds->min.y; + max_x = map->appdata->osm->bounds->max.x; + max_y = map->appdata->osm->bounds->max.y; + if ( (lpos->x > max_x) || (lpos->x < min_x) + || (lpos->y > max_y) || (lpos->y < min_y)) { + printf("cannot scroll to (%d, %d): outside the working area\n", + lpos->x, lpos->y); + return; + } + + // Viewport dimensions in canvas space + + /* get size of visible area in canvas units (meters) */ + gdouble pix_per_meter = canvas_get_zoom(map->canvas); + gdouble aw = canvas_get_viewport_width(map->canvas, CANVAS_UNIT_METER); + gdouble ah = canvas_get_viewport_height(map->canvas, CANVAS_UNIT_METER); + + // Is the point still onscreen? + gboolean vert_recentre_needed = FALSE; + gboolean horiz_recentre_needed = FALSE; + gint sx, sy; + canvas_scroll_get(map->canvas, CANVAS_UNIT_PIXEL, &sx, &sy); + gint viewport_left = (sx/pix_per_meter); + gint viewport_right = (sx/pix_per_meter)+aw; + gint viewport_top = (sy/pix_per_meter); + gint viewport_bottom = (sy/pix_per_meter)+ah; + if (lpos->x > viewport_right) { + printf("** off right edge (%d > %d)\n", lpos->x, viewport_right); + horiz_recentre_needed = TRUE; + } + if (lpos->x < viewport_left) { + printf("** off left edge (%d < %d)\n", lpos->x, viewport_left); + horiz_recentre_needed = TRUE; + } + if (lpos->y > viewport_bottom) { + printf("** off bottom edge (%d > %d)\n", lpos->y, viewport_bottom); + vert_recentre_needed = TRUE; + } + if (lpos->y < viewport_top) { + printf("** off top edge (%d < %d)\n", lpos->y, viewport_top); + vert_recentre_needed = TRUE; + } + + if (horiz_recentre_needed || vert_recentre_needed) { + gint new_sx, new_sy; + + // Just centre both at once + new_sx = pix_per_meter * (lpos->x - (aw/2)); + new_sy = pix_per_meter * (lpos->y - (ah/2)); - // Back to pixels for setting the scroll - sx = (gint)(sx_cu * zoom); - sy = (gint)(sy_cu * zoom); - canvas_scroll_to(map->canvas, sx, sy); - map->state->scroll_offset.x = sx; - map->state->scroll_offset.y = sy; + map_limit_scroll(map, CANVAS_UNIT_PIXEL, &new_sx, &new_sy); + canvas_scroll_to(map->canvas, CANVAS_UNIT_PIXEL, new_sx, new_sy); + } } -#endif // #if 0 -#endif // #ifdef USE_GOOCANVAS /* Deselects the current way or node if its zoom_max * means that it's not going to render at the current map zoom. */ void map_deselect_if_zoom_below_zoom_max(map_t *map) { - if (map->selected.type == MAP_TYPE_WAY) { + if (map->selected.object.type == WAY) { printf("will deselect way if zoomed below %f\n", - map->selected.way->draw.zoom_max); - if (map->state->zoom < map->selected.way->draw.zoom_max) { + map->selected.object.way->draw.zoom_max); + if (map->state->zoom < map->selected.object.way->draw.zoom_max) { printf(" deselecting way!\n"); map_item_deselect(map->appdata); } } - else if (map->selected.type == MAP_TYPE_NODE) { + else if (map->selected.object.type == NODE) { printf("will deselect node if zoomed below %f\n", - map->selected.node->zoom_max); - if (map->state->zoom < map->selected.node->zoom_max) { + map->selected.object.node->zoom_max); + if (map->state->zoom < map->selected.object.node->zoom_max) { printf(" deselecting node!\n"); map_item_deselect(map->appdata); } @@ -995,31 +1112,27 @@ void map_set_zoom(map_t *map, double zoom, gboolean update_scroll_offsets) { gboolean at_zoom_limit = 0; -#ifdef USE_GOOCANVAS at_zoom_limit = map_limit_zoom(map, &zoom); -#endif + map->state->zoom = zoom; canvas_set_zoom(map->canvas, map->state->zoom); map_deselect_if_zoom_below_zoom_max(map); - if (update_scroll_offsets) { + if(update_scroll_offsets) { if (!at_zoom_limit) { /* zooming affects the scroll offsets */ gint sx, sy; - canvas_get_scroll_offsets(map->canvas, &sx, &sy); -#ifdef USE_GOOCANVAS - map_limit_scroll(map, &sx, &sy); - canvas_scroll_to(map->canvas, sx, sy); // keep the map visible -#endif - map->state->scroll_offset.x = sx; - map->state->scroll_offset.y = sy; - } -#ifdef USE_GOOCANVAS - else { - // map_scroll_towards_centre(map, 0.20); + canvas_scroll_get(map->canvas, CANVAS_UNIT_PIXEL, &sx, &sy); + map_limit_scroll(map, CANVAS_UNIT_PIXEL, &sx, &sy); + + // keep the map visible + canvas_scroll_to(map->canvas, CANVAS_UNIT_PIXEL, sx, sy); } -#endif + + canvas_scroll_get(map->canvas, CANVAS_UNIT_METER, + &map->state->scroll_offset.x, + &map->state->scroll_offset.y); } } @@ -1044,17 +1157,9 @@ static gboolean distance_above(map_t *map, gint x, gint y, gint limit) { gint sx, sy; -#ifdef USE_GNOMECANVAS - gnome_canvas_get_scroll_offsets(GNOME_CANVAS(map->canvas), &sx, &sy); - - /* add offsets generated by mouse within map and map scrolling */ - sx = (x-map->pen_down.at.x) + (map->pen_down.so.x-sx); - sy = (y-map->pen_down.at.y) + (map->pen_down.so.y-sy); -#else /* add offsets generated by mouse within map and map scrolling */ sx = (x-map->pen_down.at.x); sy = (y-map->pen_down.at.y); -#endif return(sx*sx + sy*sy > limit*limit); } @@ -1063,32 +1168,31 @@ static void map_do_scroll(map_t *map, gint x, gint y) { gint sx, sy; - canvas_get_scroll_offsets(map->canvas, &sx, &sy); + canvas_scroll_get(map->canvas, CANVAS_UNIT_PIXEL, &sx, &sy); sx -= x-map->pen_down.at.x; sy -= y-map->pen_down.at.y; -#ifdef USE_GOOCANVAS - map_limit_scroll(map, &sx, &sy); -#endif - canvas_scroll_to(map->canvas, sx, sy); - map->state->scroll_offset.x = sx; - map->state->scroll_offset.y = sy; + map_limit_scroll(map, CANVAS_UNIT_PIXEL, &sx, &sy); + canvas_scroll_to(map->canvas, CANVAS_UNIT_PIXEL, sx, sy); + + canvas_scroll_get(map->canvas, CANVAS_UNIT_METER, + &map->state->scroll_offset.x, + &map->state->scroll_offset.y); } /* scroll a certain step */ static void map_do_scroll_step(map_t *map, gint x, gint y) { gint sx, sy; - canvas_get_scroll_offsets(map->canvas, &sx, &sy); + canvas_scroll_get(map->canvas, CANVAS_UNIT_PIXEL, &sx, &sy); sx += x; sy += y; -#ifdef USE_GOOCANVAS - map_limit_scroll(map, &sx, &sy); -#endif - canvas_scroll_to(map->canvas, sx, sy); - map->state->scroll_offset.x = sx; - map->state->scroll_offset.y = sy; -} + map_limit_scroll(map, CANVAS_UNIT_PIXEL, &sx, &sy); + canvas_scroll_to(map->canvas, CANVAS_UNIT_PIXEL, sx, sy); + canvas_scroll_get(map->canvas, CANVAS_UNIT_METER, + &map->state->scroll_offset.x, + &map->state->scroll_offset.y); +} gboolean map_item_is_selected_node(map_t *map, map_item_t *map_item) { printf("check if item is a selected node\n"); @@ -1098,33 +1202,33 @@ return FALSE; } - if(map->selected.type == MAP_TYPE_ILLEGAL) { + if(map->selected.object.type == ILLEGAL) { printf(" nothing is selected\n"); return FALSE; } /* clicked the highlight directly */ - if(map_item->type != MAP_TYPE_NODE) { + if(map_item->object.type != NODE) { printf(" didn't click node\n"); return FALSE; } - if(map->selected.type == MAP_TYPE_NODE) { + if(map->selected.object.type == NODE) { printf(" selected item is a node\n"); - if(map_item->node == map->selected.node) { + if(map_item->object.node == map->selected.object.node) { printf(" requested item is a selected node\n"); return TRUE; } printf(" but it's not the requested one\n"); return FALSE; - } else if(map->selected.type == MAP_TYPE_WAY) { + } else if(map->selected.object.type == WAY) { printf(" selected item is a way\n"); - node_chain_t *node_chain = map->selected.way->node_chain; + node_chain_t *node_chain = map->selected.object.way->node_chain; while(node_chain) { - if(node_chain->node == map_item->node) { + if(node_chain->node == map_item->object.node) { printf(" requested item is part of selected way\n"); return TRUE; } @@ -1152,21 +1256,21 @@ return FALSE; } - if(map->selected.type == MAP_TYPE_ILLEGAL) { + if(map->selected.object.type == ILLEGAL) { printf(" nothing is selected\n"); return FALSE; } /* clicked the highlight directly */ - if(map_item->type != MAP_TYPE_WAY) { + if(map_item->object.type != WAY) { printf(" didn't click way\n"); return FALSE; } - if(map->selected.type == MAP_TYPE_WAY) { + if(map->selected.object.type == WAY) { printf(" selected item is a way\n"); - if(map_item->way == map->selected.way) { + if(map_item->object.way == map->selected.object.way) { printf(" requested item is a selected way\n"); return TRUE; } @@ -1181,18 +1285,18 @@ void map_highlight_refresh(appdata_t *appdata) { map_t *map = appdata->map; - map_item_t old = map->selected; + object_t old = map->selected.object; printf("type to refresh is %d\n", old.type); - if(old.type == MAP_TYPE_ILLEGAL) + if(old.type == ILLEGAL) return; map_item_deselect(appdata); - map_item_select(appdata, &old); + map_object_select(appdata, &old); } void map_way_delete(appdata_t *appdata, way_t *way) { - printf("deleting way #%ld from map and osm\n", way->id); + printf("deleting way #" ITEM_ID_FORMAT " from map and osm\n", way->id); /* remove it visually from the screen */ map_item_chain_destroy(&way->map_item_chain); @@ -1216,25 +1320,25 @@ /* problem: on_item may be the highlight itself! So store it! */ map_item_t map_item; if(map->pen_down.on_item) map_item = *map->pen_down.on_item; - else map_item.type = MAP_TYPE_ILLEGAL; + else map_item.object.type = ILLEGAL; /* if we aready have something selected, then de-select it */ map_item_deselect(appdata); /* select the clicked item (if there was one) */ - if(map_item.type != MAP_TYPE_ILLEGAL) { - switch(map_item.type) { - case MAP_TYPE_NODE: - map_node_select(appdata, map_item.node); + if(map_item.object.type != ILLEGAL) { + switch(map_item.object.type) { + case NODE: + map_node_select(appdata, map_item.object.node); break; - case MAP_TYPE_WAY: - map_way_select(appdata, map_item.way); + case WAY: + map_way_select(appdata, map_item.object.way); break; default: - g_assert((map_item.type == MAP_TYPE_NODE) || - (map_item.type == MAP_TYPE_WAY)); + g_assert((map_item.object.type == NODE) || + (map_item.object.type == WAY)); break; } } @@ -1254,8 +1358,8 @@ /* in idle mode the dragged node is not highlighted */ case MAP_ACTION_IDLE: g_assert(map->pen_down.on_item); - g_assert(map->pen_down.on_item->type == MAP_TYPE_NODE); - cur_node = map->pen_down.on_item->node; + g_assert(map->pen_down.on_item->object.type == NODE); + cur_node = map->pen_down.on_item->object.node; break; default: @@ -1307,12 +1411,6 @@ map->pen_down.at.y = y; map->pen_down.drag = FALSE; // don't assume drag yet -#ifdef USE_GNOMECANVAS - /* save initial scroll offset */ - gnome_canvas_get_scroll_offsets(GNOME_CANVAS(map->canvas), - &map->pen_down.so.x, &map->pen_down.so.y); -#endif - /* determine wether this press was on an item */ map->pen_down.on_item = map_real_item_at(map, x, y); @@ -1384,11 +1482,11 @@ map_item_t old_sel = map->selected; map_handle_click(map->appdata, map); - if((old_sel.type != MAP_TYPE_ILLEGAL) && - (old_sel.type == map->selected.type) && - (old_sel.ptr == map->selected.ptr)) { + if((old_sel.object.type != ILLEGAL) && + (old_sel.object.type == map->selected.object.type) && + (old_sel.object.ptr == map->selected.object.ptr)) { printf("re-selected same item of type %d, " - "pushing it to the bottom\n", old_sel.type); + "pushing it to the bottom\n", old_sel.object.type); if(!map->selected.item) { printf(" item has no visible representation to push\n"); @@ -1505,14 +1603,14 @@ /* reduce update frequency on hildon to keep screen update fluid */ static guint32 last_time = 0; - if(event->time - last_time < 100) return FALSE; + if(event->time - last_time < 250) return FALSE; last_time = event->time; #endif if(!map->pen_down.is) return FALSE; -#ifdef USE_GNOMECANVAS +#ifndef USE_GOOCANVAS /* handle hints, hints are handled by goocanvas directly */ if(event->is_hint) gdk_window_get_pointer(event->window, &x, &y, &state); @@ -1549,7 +1647,7 @@ case MAP_ACTION_NODE_ADD: map_hl_cursor_draw(map, x, y, FALSE, map->style->node.radius); break; - + case MAP_ACTION_WAY_ADD: map_hl_cursor_draw(map, x, y, FALSE, map->style->node.radius); map_touchnode_update(appdata, x, y); @@ -1650,18 +1748,39 @@ return FALSE; } +void map_state_reset(map_state_t *state) { + if(!state) return; + + state->zoom = 0.25; + state->detail = 1.0; + + /* todo: try to scroll to center of screen */ + state->scroll_offset.x = 0; + state->scroll_offset.y = 0; +} + +map_state_t *map_state_new(void) { + map_state_t *state = g_new0(map_state_t, 1); + map_state_reset(state); + return state; +} + GtkWidget *map_new(appdata_t *appdata) { map_t *map = appdata->map = g_new0(map_t, 1); map->style = style_load(appdata, appdata->settings->style); + if(!map->style) { + errorf(NULL, _("Unable to load valid style, terminating.")); + g_free(map); + return NULL; + } if(appdata->project && appdata->project->map_state) { printf("Using projects map state\n"); map->state = appdata->project->map_state; } else { printf("Creating new map state\n"); - map->state = g_new0(map_state_t, 1); - map->state->zoom = 0.25; + map->state = map_state_new(); } map->state->refcount++; @@ -1671,56 +1790,31 @@ map->appdata = appdata; map->action.type = MAP_ACTION_IDLE; -#ifdef USE_GNOMECANVAS - map->canvas = gnome_canvas_new_aa(); // _aa - - /* create the groups */ - canvas_group_t group; - for(group = 0; group < CANVAS_GROUPS; group++) - map->group[group] = gnome_canvas_item_new( - gnome_canvas_root(GNOME_CANVAS(map->canvas)), - GNOME_TYPE_CANVAS_GROUP, - NULL); - - gtk_widget_modify_bg(map->canvas, GTK_STATE_NORMAL, - &map->canvas->style->white); - -#else - map->canvas = goo_canvas_new(); - - g_object_set(G_OBJECT(map->canvas), "anchor", GTK_ANCHOR_CENTER, NULL); - g_object_set(G_OBJECT(map->canvas), "background-color-rgb", - map->style->background.color, NULL); - - GooCanvasItem *root = goo_canvas_get_root_item(GOO_CANVAS(map->canvas)); - - /* create the groups */ - canvas_group_t group; - for(group = 0; group < CANVAS_GROUPS; group++) - map->group[group] = goo_canvas_group_new(root, NULL); + map->canvas = canvas_new(map->style->background.color); + canvas_set_antialias(map->canvas, !appdata->settings->no_antialias); -#endif + GtkWidget *canvas_widget = canvas_get_widget(map->canvas); - gtk_widget_set_events(map->canvas, + gtk_widget_set_events(canvas_widget, GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK); - gtk_signal_connect(GTK_OBJECT(map->canvas), + gtk_signal_connect(GTK_OBJECT(canvas_widget), "button_press_event", G_CALLBACK(map_button_event), appdata); - gtk_signal_connect(GTK_OBJECT(map->canvas), + gtk_signal_connect(GTK_OBJECT(canvas_widget), "button_release_event", G_CALLBACK(map_button_event), appdata); - gtk_signal_connect(GTK_OBJECT(map->canvas), + gtk_signal_connect(GTK_OBJECT(canvas_widget), "motion_notify_event", G_CALLBACK(map_motion_notify_event), appdata); - gtk_signal_connect(GTK_OBJECT(map->canvas), + gtk_signal_connect(GTK_OBJECT(canvas_widget), "scroll_event", G_CALLBACK(map_scroll_event), appdata); - gtk_signal_connect(GTK_OBJECT(map->canvas), + gtk_signal_connect(GTK_OBJECT(canvas_widget), "destroy", G_CALLBACK(map_destroy_event), appdata); - return map->canvas; + return canvas_widget; } void map_init(appdata_t *appdata) { @@ -1739,34 +1833,17 @@ mult*appdata->osm->bounds->max.x, mult*appdata->osm->bounds->max.y); - printf("restore scroll offsets %d/%d\n", + printf("restore scroll position %d/%d\n", map->state->scroll_offset.x, map->state->scroll_offset.y); - canvas_scroll_to(map->canvas, - map->state->scroll_offset.x, map->state->scroll_offset.y); + map_limit_scroll(map, CANVAS_UNIT_METER, + &map->state->scroll_offset.x, &map->state->scroll_offset.y); + canvas_scroll_to(map->canvas, CANVAS_UNIT_METER, + map->state->scroll_offset.x, map->state->scroll_offset.y); } -void map_item_set_flags(map_item_t *map_item, int set, int clr) { - - switch(map_item->type) { - case MAP_TYPE_NODE: - map_item->node->flags |= set; - map_item->node->flags &= ~clr; - break; - - case MAP_TYPE_WAY: - map_item->way->flags |= set; - map_item->way->flags &= ~clr; - break; - - default: - g_assert(0); - break; - } -} - -void map_clear(appdata_t *appdata, gint layer_mask) { +void map_clear(appdata_t *appdata, gint group_mask) { map_t *map = appdata->map; printf("freeing map contents\n"); @@ -1776,32 +1853,15 @@ /* remove a possibly existing highlight */ map_item_deselect(appdata); - canvas_group_t group; - for(group=0;groupgroup[group]); - - /* and create an empty new one */ - map->group[group] = gnome_canvas_item_new( - gnome_canvas_root(GNOME_CANVAS(map->canvas)), - GNOME_TYPE_CANVAS_GROUP, - NULL); -#else - if(layer_mask & (1<group[group]); - printf("Removing %d children from layer %d\n", children, group); - while(children--) - goo_canvas_item_remove_child(map->group[group], children); - } -#endif - } + canvas_erase(map->canvas, group_mask); } void map_paint(appdata_t *appdata) { map_t *map = appdata->map; + /* user may have changed antialias settings */ + canvas_set_antialias(map->canvas, !appdata->settings->no_antialias); + josm_elemstyles_colorize_world(map->style, appdata->osm); map_draw(map, appdata->osm); } @@ -1833,8 +1893,8 @@ /* remember if there was a way selected */ way_t *way_sel = NULL; - if(appdata->map->selected.type == MAP_TYPE_WAY) - way_sel = appdata->map->selected.way; + if(appdata->map->selected.object.type == WAY) + way_sel = appdata->map->selected.object.way; map_item_deselect(appdata); map_edit_way_add_begin(appdata->map, way_sel); @@ -1933,13 +1993,16 @@ /* deleting the selected item de-selects it ... */ map_item_deselect(appdata); - switch(item.type) { - case MAP_TYPE_NODE: - printf("request to delete node #%ld\n", item.node->id); + undo_remember_delete(appdata, &item.object); + + switch(item.object.type) { + case NODE: + printf("request to delete node #" ITEM_ID_FORMAT "\n", + item.object.node->id); /* check if this node is part of a way with two nodes only. */ /* we cannot delete this as this would also delete the way */ - way_chain_t *way_chain = osm_node_to_way(appdata->osm, item.node); + way_chain_t *way_chain = osm_node_to_way(appdata->osm, item.object.node); if(way_chain) { gboolean short_way = FALSE; @@ -1965,12 +2028,12 @@ } /* remove it visually from the screen */ - map_item_chain_destroy(&item.node->map_item_chain); + map_item_chain_destroy(&item.object.node->map_item_chain); /* and mark it "deleted" in the database */ - osm_node_remove_from_relation(appdata->osm, item.node); + osm_node_remove_from_relation(appdata->osm, item.object.node); way_chain_t *chain = osm_node_delete(appdata->osm, - &appdata->icon, item.node, FALSE, TRUE); + &appdata->icon, item.object.node, FALSE, TRUE); /* redraw all affected ways */ while(chain) { @@ -1983,8 +2046,8 @@ map_way_delete(appdata, chain->way); } else { map_item_t item; - item.type = MAP_TYPE_WAY; - item.way = chain->way; + item.object.type = WAY; + item.object.way = chain->way; map_item_redraw(appdata, &item); } @@ -1995,78 +2058,209 @@ break; - case MAP_TYPE_WAY: - printf("request to delete way #%ld\n", item.way->id); - map_way_delete(appdata, item.way); + case WAY: + printf("request to delete way #" ITEM_ID_FORMAT "\n", item.object.way->id); + map_way_delete(appdata, item.object.way); break; default: - g_assert((item.type == MAP_TYPE_NODE) || - (item.type == MAP_TYPE_WAY)); + g_assert((item.object.type == NODE) || + (item.object.type == WAY)); break; } } /* ----------------------- track related stuff ----------------------- */ +static gboolean track_pos2lpos(bounds_t *bounds, pos_t *pos, lpos_t *lpos) { + pos2lpos(bounds, pos, lpos); + + /* check if point is within bounds */ + return ((lpos->x >= bounds->min.x) && (lpos->x <= bounds->max.x) && + (lpos->y >= bounds->min.y) && (lpos->y <= bounds->max.y)); +} + void map_track_draw_seg(map_t *map, track_seg_t *seg) { + bounds_t *bounds = map->appdata->osm->bounds; + /* a track_seg needs at least 2 points to be drawn */ guint pnum = track_seg_points(seg); printf("seg of length %d\n", pnum); - if(pnum == 1) { - g_assert(!seg->item); + if(!pnum) + return; + + /* nothing should have been drawn by now ... */ + g_assert(!seg->item_chain); + + track_item_chain_t **itemP = &seg->item_chain; + track_point_t *track_point = seg->track_point; + while(track_point) { + lpos_t lpos; + + /* skip all points not on screen */ + track_point_t *last = NULL; + while(track_point && !track_pos2lpos(bounds, &track_point->pos, &lpos)) { + last = track_point; + track_point = track_point->next; + } - seg->item = canvas_circle_new(map, CANVAS_GROUP_TRACK, - seg->track_point->lpos.x, seg->track_point->lpos.y, - map->style->track.width/2.0, 0, map->style->track.color, NO_COLOR); - } + int visible = 0; + + /* count nodes that _are_ on screen */ + track_point_t *tmp = track_point; + while(tmp && track_pos2lpos(bounds, &tmp->pos, &lpos)) { + tmp = tmp->next; + visible++; + } + + /* actually start drawing with the last position that was offscreen */ + /* so the track nicely enters the viewing area */ + if(last) { + track_point = last; + visible++; + } + + /* also use last one that's offscreen to nicely leave the visible area */ + if(tmp && tmp->next) + visible++; - if(pnum > 1) { - /* allocate space for nodes */ - canvas_points_t *points = canvas_points_new(pnum); + canvas_points_t *points = canvas_points_new(visible); - int point = 0; - track_point_t *track_point = seg->track_point; - while(track_point) { - points->coords[point++] = track_point->lpos.x; - points->coords[point++] = track_point->lpos.y; + printf("visible are %d\n", visible); + int point; + for(point=0;pointpos, &lpos); + + points->coords[2*point+0] = lpos.x; + points->coords[2*point+1] = lpos.y; track_point = track_point->next; } - - /* there may be a circle (one point line) */ - if(seg->item) - canvas_item_destroy(seg->item); - seg->item = canvas_polyline_new(map, CANVAS_GROUP_TRACK, - points, map->style->track.width, map->style->track.color); + *itemP = g_new0(track_item_chain_t, 1); + (*itemP)->item = canvas_polyline_new(map->canvas, CANVAS_GROUP_TRACK, + points, map->style->track.width, map->style->track.color); + itemP = &(*itemP)->next; canvas_points_free(points); } } +/* update the last visible fragment of this segment since a */ +/* gps position may have been added */ void map_track_update_seg(map_t *map, track_seg_t *seg) { + bounds_t *bounds = map->appdata->osm->bounds; + + printf("-- APPENDING TO TRACK --\n"); + /* a track_seg needs at least 2 points to be drawn */ guint pnum = track_seg_points(seg); printf("seg of length %d\n", pnum); - if(pnum > 1) { + /* there are two cases: either the second last point was on screen */ + /* or it wasn't. We'll have to start a new screen item if the latter */ + /* is the case */ + + /* search last point */ + track_point_t *begin = seg->track_point, *second_last = seg->track_point; + lpos_t lpos; + while(second_last && second_last->next && second_last->next->next) { + if(!track_pos2lpos(bounds, &second_last->pos, &lpos)) + begin = second_last; + + second_last = second_last->next; + } + track_point_t *last = second_last->next; + + /* since we are updating an existing track, it sure has at least two */ + /* points, second_last must be valid and its "next" (last) also */ + g_assert(second_last); + g_assert(last); + + /* check if the last and second_last points are visible */ + gboolean last_is_visible = + track_pos2lpos(bounds, &last->pos, &lpos); + gboolean second_last_is_visible = + track_pos2lpos(bounds, &second_last->pos, &lpos); + + /* if both are invisible, then nothing has changed on screen */ + if(!last_is_visible && !second_last_is_visible) { + printf("second_last and last entry are invisible -> doing nothing\n"); + return; + } + + /* search last element in item chain */ + track_item_chain_t *item = seg->item_chain; + while(item && item->next) + item = item->next; + + if(second_last_is_visible) { + /* there must be something already on the screen and there must */ + /* be visible nodes in the chain */ + g_assert(item); + g_assert(begin); + + printf("second_last is visible -> append\n"); - /* allocate space for nodes */ - canvas_points_t *points = canvas_points_new(pnum); + /* count points to be placed */ + int npoints = 0; + track_point_t *tmp = begin; + while(tmp) { + tmp = tmp->next; + npoints++; + } + + printf("updating last segment to %d points\n", npoints); + + canvas_points_t *points = canvas_points_new(npoints); + + gint point = 0; + while(begin) { + track_pos2lpos(bounds, &begin->pos, &lpos); + canvas_point_set_pos(points, point++, &lpos); + begin = begin->next; + } - int point = 0; - track_point_t *track_point = seg->track_point; - while(track_point) { - canvas_point_set_pos(points, point++, &track_point->lpos); - track_point = track_point->next; + canvas_item_set_points(item->item, points); + canvas_points_free(points); + + } else { + printf("second last is invisible -> start new screen segment\n"); + + /* the search for the "begin" ends with the second_last item */ + /* verify the last one also */ + if(begin->next && !track_pos2lpos(bounds, &begin->next->pos, &lpos)) + begin = begin->next; + + item->next = g_new0(track_item_chain_t, 1); + item = item->next; + + /* count points to be placed */ + int npoints = 0; + track_point_t *tmp = begin; + while(tmp) { + tmp = tmp->next; + npoints++; } + + printf("attaching new segment with %d points\n", npoints); + + canvas_points_t *points = canvas_points_new(npoints); + + gint point = 0; + while(begin) { + track_pos2lpos(bounds, &begin->pos, &lpos); + canvas_point_set_pos(points, point++, &lpos); + begin = begin->next; + } + + item->item = canvas_polyline_new(map->canvas, CANVAS_GROUP_TRACK, + points, map->style->track.width, map->style->track.color); - g_assert(seg->item); - canvas_item_set_points(seg->item, points); canvas_points_free(points); } + } void map_track_draw(map_t *map, track_t *track) { @@ -2089,25 +2283,33 @@ /* remove all segments */ track_seg_t *seg = track->track_seg; while(seg) { - if(seg->item) { - canvas_item_destroy(seg->item); - seg->item = NULL; + track_item_chain_t *item = seg->item_chain; + while(item) { + track_item_chain_t *next = item->next; + canvas_item_destroy(item->item); + item = next; } - + + seg->item_chain = NULL; seg = seg->next; } } -void map_track_pos(appdata_t *appdata, lpos_t *lpos) { +void map_track_pos(appdata_t *appdata, pos_t *pos) { if(appdata->track.gps_item) { canvas_item_destroy(appdata->track.gps_item); appdata->track.gps_item = NULL; } - if(lpos) - appdata->track.gps_item = canvas_circle_new(appdata->map, CANVAS_GROUP_GPS, - lpos->x, lpos->y, appdata->map->style->track.width/2.0, 0, - RGB2CANVAS(appdata->map->style->track.gps_color), NO_COLOR); + if(pos) { + lpos_t lpos; + pos2lpos(appdata->osm->bounds, pos, &lpos); + + appdata->track.gps_item = + canvas_circle_new(appdata->map->canvas, CANVAS_GROUP_GPS, + lpos.x, lpos.y, appdata->map->style->track.width/2.0, 0, + appdata->map->style->track.gps_color, NO_COLOR); + } } /* ------------------- map background ------------------ */ @@ -2148,7 +2350,7 @@ map->bg.scale.y = (float)(bounds->max.y - bounds->min.y)/ (float)gdk_pixbuf_get_height(map->bg.pix); - map->bg.item = canvas_image_new(map, CANVAS_GROUP_BG, map->bg.pix, + map->bg.item = canvas_image_new(map->canvas, CANVAS_GROUP_BG, map->bg.pix, bounds->min.x, bounds->min.y, map->bg.scale.x, map->bg.scale.y); canvas_item_destroy_connect(map->bg.item, @@ -2162,13 +2364,13 @@ map_t *map = appdata->map; if(!map) return; - if(map->selected.type != MAP_TYPE_WAY) { + if(map->selected.object.type != WAY) { printf("selected item is not a way\n"); return; } - way_t *way = map->selected.way; - printf("hiding way #%ld\n", way->id); + way_t *way = map->selected.object.way; + printf("hiding way #" ITEM_ID_FORMAT "\n", way->id); map_item_deselect(appdata); way->flags |= OSM_FLAG_HIDDEN; @@ -2193,3 +2395,38 @@ gtk_widget_set_sensitive(appdata->menu_item_map_show_all, FALSE); } + +static void map_detail_change(map_t *map, float detail) { + appdata_t *appdata = map->appdata; + + /* deselecting anything allows us not to care about automatic deselection */ + /* as well as items becoming invisible by the detail change */ + map_item_deselect(appdata); + + map->state->detail = detail; + printf("changing detail factor to %f\n", map->state->detail); + + banner_busy_start(appdata, 1, "Redrawing..."); + map_clear(appdata, MAP_LAYER_OBJECTS_ONLY); + map_paint(appdata); + banner_busy_stop(appdata); +} + +#define DETAIL_STEP 1.5 + +void map_detail_increase(map_t *map) { + if(!map) return; + map_detail_change(map, map->state->detail * DETAIL_STEP); +} + +void map_detail_decrease(map_t *map) { + if(!map) return; + map_detail_change(map, map->state->detail / DETAIL_STEP); +} + +void map_detail_normal(map_t *map) { + if(!map) return; + map_detail_change(map, 1.0); +} + +// vim:et:ts=8:sw=2:sts=2:ai