--- trunk/src/osm-gps-map-osd-classic.c 2009/09/14 12:16:08 111 +++ trunk/src/osm-gps-map-osd-classic.c 2009/10/12 20:27:55 133 @@ -19,6 +19,7 @@ #include "config.h" #include // abs +#include #include // M_PI/cos() /* parameters that can be overwritten from the config file: */ @@ -32,6 +33,7 @@ #include #include "osm-gps-map.h" +#include "converter.h" #include "osm-gps-map-osd-classic.h" //the osd controls @@ -45,6 +47,22 @@ #endif } controls; +#ifdef OSD_BALLOON + //a balloon with additional info + struct { + cairo_surface_t *surface; + int orientation, offset_x, offset_y; + + gboolean just_created; + float lat, lon; + OsmGpsMapRect_t rect; + + /* function called to have the user app draw the contents */ + OsmGpsMapBalloonCallback cb; + gpointer data; + } balloon; +#endif + #ifdef OSD_SCALE struct { cairo_surface_t *surface; @@ -59,6 +77,15 @@ } crosshair; #endif +#ifdef OSD_NAV + struct { + cairo_surface_t *surface; + float lat, lon; + char *name; + gboolean imperial; // display distance imperial/metric + } nav; +#endif + #ifdef OSD_COORDINATES struct { cairo_surface_t *surface; @@ -80,6 +107,264 @@ } osd_priv_t; +#ifdef OSD_BALLOON +/* most visual effects are hardcoded by now, but may be made */ +/* available via properties later */ +#ifndef BALLOON_AREA_WIDTH +#define BALLOON_AREA_WIDTH 290 +#endif +#ifndef BALLOON_AREA_HEIGHT +#define BALLOON_AREA_HEIGHT 75 +#endif +#ifndef BALLOON_CORNER_RADIUS +#define BALLOON_CORNER_RADIUS 10 +#endif + +#define BALLOON_BORDER (BALLOON_CORNER_RADIUS/2) +#define BALLOON_WIDTH (BALLOON_AREA_WIDTH + 2 * BALLOON_BORDER) +#define BALLOON_HEIGHT (BALLOON_AREA_HEIGHT + 2 * BALLOON_BORDER) +#define BALLOON_TRANSPARENCY 0.8 +#define POINTER_HEIGHT 20 +#define POINTER_FOOT_WIDTH 20 +#define POINTER_OFFSET (BALLOON_CORNER_RADIUS*3/4) +#define BALLOON_SHADOW (BALLOON_CORNER_RADIUS/2) +#define BALLOON_SHADOW_TRANSPARENCY 0.2 + +#define BALLOON_W (BALLOON_WIDTH + BALLOON_SHADOW) +#define BALLOON_H (BALLOON_HEIGHT + POINTER_HEIGHT + BALLOON_SHADOW) + +#define CLOSE_BUTTON_RADIUS (BALLOON_CORNER_RADIUS) + + +/* draw the bubble shape. this is used twice, once for the shape and once */ +/* for the shadow */ +static void +osm_gps_map_draw_balloon_shape (cairo_t *cr, int x0, int y0, int x1, int y1, + gboolean bottom, int px, int py, int px0, int px1) { + + cairo_move_to (cr, x0, y0 + BALLOON_CORNER_RADIUS); + cairo_arc (cr, x0 + BALLOON_CORNER_RADIUS, y0 + BALLOON_CORNER_RADIUS, + BALLOON_CORNER_RADIUS, -M_PI, -M_PI/2); + if(!bottom) { + /* insert top pointer */ + cairo_line_to (cr, px1, y0); + cairo_line_to (cr, px, py); + cairo_line_to (cr, px0, y0); + } + + cairo_line_to (cr, x1 - BALLOON_CORNER_RADIUS, y0); + cairo_arc (cr, x1 - BALLOON_CORNER_RADIUS, y0 + BALLOON_CORNER_RADIUS, + BALLOON_CORNER_RADIUS, -M_PI/2, 0); + cairo_line_to (cr, x1 , y1 - BALLOON_CORNER_RADIUS); + cairo_arc (cr, x1 - BALLOON_CORNER_RADIUS, y1 - BALLOON_CORNER_RADIUS, + BALLOON_CORNER_RADIUS, 0, M_PI/2); + if(bottom) { + /* insert bottom pointer */ + cairo_line_to (cr, px0, y1); + cairo_line_to (cr, px, py); + cairo_line_to (cr, px1, y1); + } + + cairo_line_to (cr, x0 + BALLOON_CORNER_RADIUS, y1); + cairo_arc (cr, x0 + BALLOON_CORNER_RADIUS, y1 - BALLOON_CORNER_RADIUS, + BALLOON_CORNER_RADIUS, M_PI/2, M_PI); + + cairo_close_path (cr); +} + +static void +osd_render_balloon(osm_gps_map_osd_t *osd) { + osd_priv_t *priv = (osd_priv_t*)osd->priv; + + /* get zoom */ + gint zoom; + g_object_get(OSM_GPS_MAP(osd->widget), "zoom", &zoom, NULL); + + /* ------- convert given coordinate into screen position --------- */ + gint xs, ys; + osm_gps_map_geographic_to_screen (OSM_GPS_MAP(osd->widget), + priv->balloon.lat, priv->balloon.lon, + &xs, &ys); + + gint x0 = 1, y0 = 1; + + /* check position of this relative to screen center to determine */ + /* pointer direction ... */ + int pointer_x, pointer_x0, pointer_x1; + int pointer_y; + + /* ... and calculate position */ + int orientation = 0; + if(xs > osd->widget->allocation.width/2) { + priv->balloon.offset_x = -BALLOON_WIDTH + POINTER_OFFSET; + pointer_x = x0 - priv->balloon.offset_x; + pointer_x0 = pointer_x - (BALLOON_CORNER_RADIUS - POINTER_OFFSET); + pointer_x1 = pointer_x0 - POINTER_FOOT_WIDTH; + orientation |= 1; + } else { + priv->balloon.offset_x = -POINTER_OFFSET; + pointer_x = x0 - priv->balloon.offset_x; + pointer_x1 = pointer_x + (BALLOON_CORNER_RADIUS - POINTER_OFFSET); + pointer_x0 = pointer_x1 + POINTER_FOOT_WIDTH; + } + + gboolean bottom = FALSE; + if(ys > osd->widget->allocation.height/2) { + priv->balloon.offset_y = -BALLOON_HEIGHT - POINTER_HEIGHT; + pointer_y = y0 - priv->balloon.offset_y; + bottom = TRUE; + orientation |= 2; + } else { + priv->balloon.offset_y = 0; + pointer_y = y0 - priv->balloon.offset_y; + y0 += POINTER_HEIGHT; + } + + /* if required orientation equals current one, then don't render */ + /* anything */ + if(orientation == priv->balloon.orientation) + return; + + priv->balloon.orientation = orientation; + + /* calculate bottom/right of box */ + int x1 = x0 + BALLOON_WIDTH, y1 = y0 + BALLOON_HEIGHT; + + /* save balloon screen coordinates for later use */ + priv->balloon.rect.x = x0 + BALLOON_BORDER; + priv->balloon.rect.y = y0 + BALLOON_BORDER; + priv->balloon.rect.w = x1 - x0 - 2*BALLOON_BORDER; + priv->balloon.rect.h = y1 - y0 - 2*BALLOON_BORDER; + + cairo_t *cr = cairo_create(priv->balloon.surface); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); + cairo_paint(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + + /* --------- draw shadow --------------- */ + osm_gps_map_draw_balloon_shape (cr, + x0 + BALLOON_SHADOW, y0 + BALLOON_SHADOW, + x1 + BALLOON_SHADOW, y1 + BALLOON_SHADOW, + bottom, pointer_x, pointer_y, + pointer_x0 + BALLOON_SHADOW, pointer_x1 + BALLOON_SHADOW); + + cairo_set_source_rgba (cr, 0, 0, 0, BALLOON_SHADOW_TRANSPARENCY); + cairo_fill_preserve (cr); + cairo_set_source_rgba (cr, 1, 0, 0, 1.0); + cairo_set_line_width (cr, 0); + cairo_stroke (cr); + + /* --------- draw main shape ----------- */ + osm_gps_map_draw_balloon_shape (cr, x0, y0, x1, y1, + bottom, pointer_x, pointer_y, pointer_x0, pointer_x1); + + cairo_set_source_rgba (cr, 1, 1, 1, BALLOON_TRANSPARENCY); + cairo_fill_preserve (cr); + cairo_set_source_rgba (cr, 0, 0, 0, BALLOON_TRANSPARENCY); + cairo_set_line_width (cr, 1); + cairo_stroke (cr); + + if (priv->balloon.cb) { + /* clip in case application tries to draw in */ + /* exceed of the balloon */ + cairo_rectangle (cr, priv->balloon.rect.x, priv->balloon.rect.y, + priv->balloon.rect.w, priv->balloon.rect.h); + cairo_clip (cr); + cairo_new_path (cr); /* current path is not + consumed by cairo_clip() */ + + priv->balloon.cb(cr, &priv->balloon.rect, priv->balloon.data); + } + + cairo_destroy(cr); +} + +/* return true if balloon is being displayed and if */ +/* the given coordinate is within this balloon */ +static gboolean +osd_balloon_check(osm_gps_map_osd_t *osd, gboolean down, gint x, gint y) +{ + osd_priv_t *priv = (osd_priv_t*)osd->priv; + + if(!priv->balloon.surface) + return FALSE; + + gint xs, ys; + osm_gps_map_geographic_to_screen (OSM_GPS_MAP(osd->widget), + priv->balloon.lat, priv->balloon.lon, + &xs, &ys); + + xs += priv->balloon.rect.x + priv->balloon.offset_x; + ys += priv->balloon.rect.y + priv->balloon.offset_y; + + gboolean is_in = + (x > xs) && (x < xs + priv->balloon.rect.w) && + (y > ys) && (y < ys + priv->balloon.rect.h); + + /* handle the fact that the balloon may have been created by the */ + /* button down event */ + if(!is_in && !down && !priv->balloon.just_created) { + /* the user actually clicked outside the balloon */ + + /* close the balloon! */ + osm_gps_map_osd_clear_balloon (OSM_GPS_MAP(osd->widget)); + } + + return is_in; +} + +void osm_gps_map_osd_clear_balloon (OsmGpsMap *map) { + g_return_if_fail (OSM_IS_GPS_MAP (map)); + + osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map); + g_return_if_fail (osd); + + osd_priv_t *priv = (osd_priv_t*)osd->priv; + g_return_if_fail (priv); + + if(priv->balloon.surface) { + cairo_surface_destroy(priv->balloon.surface); + priv->balloon.surface = NULL; + priv->balloon.lat = OSM_GPS_MAP_INVALID; + priv->balloon.lon = OSM_GPS_MAP_INVALID; + } + osm_gps_map_redraw(map); +} + +void +osm_gps_map_osd_draw_balloon (OsmGpsMap *map, float latitude, float longitude, + OsmGpsMapBalloonCallback cb, gpointer data) { + g_return_if_fail (OSM_IS_GPS_MAP (map)); + + osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map); + g_return_if_fail (osd); + + osd_priv_t *priv = (osd_priv_t*)osd->priv; + g_return_if_fail (priv); + + osm_gps_map_osd_clear_balloon (map); + + /* allocate balloon surface */ + priv->balloon.surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + BALLOON_W+2, BALLOON_H+2); + + priv->balloon.lat = latitude; + priv->balloon.lon = longitude; + priv->balloon.cb = cb; + priv->balloon.data = data; + priv->balloon.just_created = TRUE; + + priv->balloon.orientation = -1; + + osd_render_balloon(osd); + + osm_gps_map_redraw(map); +} + +#endif // OSD_BALLOON + /* position and extent of bounding box */ #ifndef OSD_X #define OSD_X (10) @@ -590,7 +875,7 @@ /* check if the user clicked inside the source selection area */ static osd_button_t -osd_source_check(osm_gps_map_osd_t *osd, gint x, gint y) { +osd_source_check(osm_gps_map_osd_t *osd, gboolean down, gint x, gint y) { osd_priv_t *priv = (osd_priv_t*)osd->priv; if(!priv->source_sel.expanded) @@ -608,7 +893,8 @@ /* really within puller shape? */ if(x > Z_RAD || osm_gps_map_in_circle(x, y, Z_RAD, Z_RAD, Z_RAD)) { /* expand source selector */ - osd_source_toggle(osd); + if(down) + osd_source_toggle(osd); /* tell upper layers that user clicked some background element */ /* of the OSD */ @@ -634,16 +920,18 @@ y /= step; y += 1; - gint old = 0; - g_object_get(osd->widget, "map-source", &old, NULL); - - if(y > OSM_GPS_MAP_SOURCE_NULL && - y <= OSM_GPS_MAP_SOURCE_LAST && - old != y) { - g_object_set(osd->widget, "map-source", y, NULL); - - osd_render_source_sel(osd, TRUE); - osm_gps_map_repaint(OSM_GPS_MAP(osd->widget)); + if(down) { + gint old = 0; + g_object_get(osd->widget, "map-source", &old, NULL); + + if(y > OSM_GPS_MAP_SOURCE_NULL && + y <= OSM_GPS_MAP_SOURCE_LAST && + old != y) { + g_object_set(osd->widget, "map-source", y, NULL); + + osd_render_source_sel(osd, TRUE); + osm_gps_map_repaint(OSM_GPS_MAP(osd->widget)); + } } /* return "clicked in OSD background" to prevent further */ @@ -657,36 +945,52 @@ #endif // OSD_SOURCE_SEL static osd_button_t -osd_check(osm_gps_map_osd_t *osd, gint x, gint y) { +osd_check(osm_gps_map_osd_t *osd, gboolean down, gint x, gint y) { osd_button_t but = OSD_NONE; +#ifdef OSD_BALLOON + if(down) { + /* needed to handle balloons that are created at click */ + osd_priv_t *priv = (osd_priv_t*)osd->priv; + priv->balloon.just_created = FALSE; + } +#endif + #ifdef OSD_SOURCE_SEL /* the source selection area is handles internally */ - but = osd_source_check(osd, x, y); - if(but != OSD_NONE) - return but; + but = osd_source_check(osd, down, x, y); #endif - - x -= OSD_X; - y -= OSD_Y; - - if(OSD_X < 0) - x -= (osd->widget->allocation.width - OSD_W); - - if(OSD_Y < 0) - y -= (osd->widget->allocation.height - OSD_H); - - /* first do a rough test for the OSD area. */ - /* this is just to avoid an unnecessary detailed test */ - if(x > 0 && x < OSD_W && y > 0 && y < OSD_H) { + + if(but == OSD_NONE) { + gint mx = x - OSD_X; + gint my = y - OSD_Y; + + if(OSD_X < 0) + mx -= (osd->widget->allocation.width - OSD_W); + + if(OSD_Y < 0) + my -= (osd->widget->allocation.height - OSD_H); + + /* first do a rough test for the OSD area. */ + /* this is just to avoid an unnecessary detailed test */ + if(mx > 0 && mx < OSD_W && my > 0 && my < OSD_H) { #ifndef OSD_NO_DPAD - but = osd_check_dpad(x, y); + but = osd_check_dpad(mx, my); #endif + } if(but == OSD_NONE) - but = osd_check_zoom(x, y); + but = osd_check_zoom(mx, my); } +#ifdef OSD_BALLOON + if(but == OSD_NONE) { + /* check if user clicked into balloon */ + if(osd_balloon_check(osd, down, x, y)) + but = OSD_BG; + } +#endif + return but; } @@ -769,7 +1073,7 @@ #define OSD_COORDINATES_OFFSET (OSD_COORDINATES_FONT_SIZE/6) #define OSD_COORDINATES_W (8*OSD_COORDINATES_FONT_SIZE+2*OSD_COORDINATES_OFFSET) -#define OSD_COORDINATES_H (2*OSD_COORDINATES_FONT_SIZE+OSD_COORDINATES_OFFSET) +#define OSD_COORDINATES_H (2*OSD_COORDINATES_FONT_SIZE+2*OSD_COORDINATES_OFFSET+OSD_COORDINATES_FONT_SIZE/4) /* these can be overwritten with versions that support */ /* localization */ @@ -827,6 +1131,37 @@ c, (int)integral, fractional*60.0); } +/* render a string at the given screen position */ +static int +osd_render_centered_text(cairo_t *cr, int y, int width, char *text) { + char *p = g_strdup(text); + cairo_text_extents_t extents; + cairo_text_extents (cr, p, &extents); + + /* check if text needs to be truncated */ + int len = strlen(text)-2; + while(extents.width > width) { + len--; + strcpy(p+len, "..."); + cairo_text_extents (cr, p, &extents); + } + + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_set_line_width (cr, OSD_COORDINATES_FONT_SIZE/6); + cairo_move_to (cr, (width - extents.width)/2, y - extents.y_bearing); + cairo_text_path (cr, p); + cairo_stroke (cr); + + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cairo_move_to (cr, (width - extents.width)/2, y - extents.y_bearing); + cairo_show_text (cr, p); + + g_free(p); + + /* skip + 1/4 line */ + return y + 5*OSD_COORDINATES_FONT_SIZE/4; +} + static void osd_render_coordinates(osm_gps_map_osd_t *osd) { @@ -862,40 +1197,196 @@ char *latitude = osd_latitude_str(lat); char *longitude = osd_longitude_str(lon); - cairo_text_extents_t lat_extents, lon_extents; - cairo_text_extents (cr, latitude, &lat_extents); - cairo_text_extents (cr, longitude, &lon_extents); + int y = OSD_COORDINATES_OFFSET; + y = osd_render_centered_text(cr, y, OSD_COORDINATES_W, latitude); + y = osd_render_centered_text(cr, y, OSD_COORDINATES_W, longitude); + + g_free(latitude); + g_free(longitude); - cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); - cairo_set_line_width (cr, OSD_COORDINATES_FONT_SIZE/6); - cairo_move_to (cr, - (OSD_COORDINATES_W - lat_extents.width)/2, - OSD_COORDINATES_OFFSET - lat_extents.y_bearing); - cairo_text_path (cr, latitude); - cairo_move_to (cr, - (OSD_COORDINATES_W - lon_extents.width)/2, - OSD_COORDINATES_OFFSET - lon_extents.y_bearing + - OSD_COORDINATES_FONT_SIZE); - cairo_text_path (cr, longitude); - cairo_stroke (cr); + cairo_destroy(cr); +} +#endif // OSD_COORDINATES - cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); - cairo_move_to (cr, - (OSD_COORDINATES_W - lat_extents.width)/2, - OSD_COORDINATES_OFFSET - lat_extents.y_bearing); - cairo_show_text (cr, latitude); - cairo_move_to (cr, - (OSD_COORDINATES_W - lon_extents.width)/2, - OSD_COORDINATES_OFFSET - lon_extents.y_bearing + - OSD_COORDINATES_FONT_SIZE); - cairo_show_text (cr, longitude); +#ifdef OSD_NAV +#define OSD_NAV_W (8*OSD_COORDINATES_FONT_SIZE+2*OSD_COORDINATES_OFFSET) +#define OSD_NAV_H (11*OSD_COORDINATES_FONT_SIZE) +/* http://mathforum.org/library/drmath/view/55417.html */ +static float get_bearing(float lat1, float lon1, float lat2, float lon2) { + return atan2( sin(lon2 - lon1) * cos(lat2), + cos(lat1) * sin(lat2) - + sin(lat1) * cos(lat2) * cos(lon2 - lon1)); +} + +/* http://mathforum.org/library/drmath/view/51722.html */ +static float get_distance(float lat1, float lon1, float lat2, float lon2) { + float aob = acos(cos(lat1) * cos(lat2) * cos(lon2 - lon1) + + sin(lat1) * sin(lat2)); + + // return(aob * 3959.0); /* great circle radius in miles */ + + return(aob * 6371000.0); /* great circle radius in meters */ +} + +static void +osd_render_nav(osm_gps_map_osd_t *osd) +{ + osd_priv_t *priv = (osd_priv_t*)osd->priv; + + if(!priv->nav.surface || isnan(priv->nav.lat) || isnan(priv->nav.lon)) + return; + + /* first fill with transparency */ + cairo_t *cr = cairo_create(priv->nav.surface); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0); + cairo_paint(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + + cairo_select_font_face (cr, "Sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, OSD_COORDINATES_FONT_SIZE); + + char *latitude = osd_latitude_str(priv->nav.lat); + char *longitude = osd_longitude_str(priv->nav.lon); + + int y = OSD_COORDINATES_OFFSET; + y = osd_render_centered_text(cr, y, OSD_NAV_W, priv->nav.name); + y = osd_render_centered_text(cr, y, OSD_NAV_W, latitude); + y = osd_render_centered_text(cr, y, OSD_NAV_W, longitude); + g_free(latitude); g_free(longitude); + /* draw the compass */ + int radius = (OSD_NAV_H - y - 5*OSD_COORDINATES_FONT_SIZE/4)/2; + if(radius > OSD_NAV_W/2) + radius = OSD_NAV_W/2; + + int x = OSD_NAV_W/2+1; + y += radius; + + cairo_stroke (cr); + + /* draw background */ + cairo_arc(cr, x, y, radius, 0, 2*M_PI); + cairo_set_source_rgba (cr, 1, 1, 1, 0.5); + cairo_fill_preserve (cr); + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_set_line_width (cr, 1); + cairo_stroke (cr); + + /* draw pointer */ +#define ARROW_WIDTH 0.3 +#define ARROW_LENGTH 0.7 + + coord_t *gps = osm_gps_map_get_gps (OSM_GPS_MAP(osd->widget)); + if(gps) { + float arot = get_bearing(gps->rlat, gps->rlon, + deg2rad(priv->nav.lat), deg2rad(priv->nav.lon)); + + cairo_move_to(cr, + x + radius * ARROW_LENGTH * sin(arot), + y + radius * ARROW_LENGTH * -cos(arot)); + + cairo_line_to(cr, + x + radius * -ARROW_LENGTH * sin(arot+ARROW_WIDTH), + y + radius * -ARROW_LENGTH * -cos(arot+ARROW_WIDTH)); + + cairo_line_to(cr, + x + radius * -0.5 * ARROW_LENGTH * sin(arot), + y + radius * -0.5 * ARROW_LENGTH * -cos(arot)); + + cairo_line_to(cr, + x + radius * -ARROW_LENGTH * sin(arot-ARROW_WIDTH), + y + radius * -ARROW_LENGTH * -cos(arot-ARROW_WIDTH)); + + cairo_close_path(cr); + cairo_set_source_rgb (cr, 0, 0, 0); + cairo_fill (cr); + + y += radius + OSD_COORDINATES_FONT_SIZE/4; + + float dist = get_distance(gps->rlat, gps->rlon, + deg2rad(priv->nav.lat), deg2rad(priv->nav.lon)); + + char *dist_str = NULL; + if(!priv->nav.imperial) { + /* metric is easy ... */ + if(dist<1000) + dist_str = g_strdup_printf("%u m", (int)dist); + else + dist_str = g_strdup_printf("%.1f km", dist/1000); + } else { + /* and now the hard part: scale for useful imperial values :-( */ + /* try to convert to feet, 1ft == 0.3048 m */ + + if(dist/(3*0.3048) >= 1760.0) /* more than 1760 yard? */ + dist_str = g_strdup_printf("%.1f mi", dist/(0.3048*3*1760.0)); + else if(dist/0.3048 >= 100) /* more than 100 feet? */ + dist_str = g_strdup_printf("%.1f yd", dist/(0.3048*3)); + else + dist_str = g_strdup_printf("%.0f ft", dist/0.3048); + } + + y = osd_render_centered_text(cr, y, OSD_NAV_W, dist_str); + g_free(dist_str); + } + cairo_destroy(cr); } -#endif // OSD_COORDINATES + +void osm_gps_map_osd_clear_nav (OsmGpsMap *map) { + g_return_if_fail (OSM_IS_GPS_MAP (map)); + + osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map); + g_return_if_fail (osd); + + osd_priv_t *priv = (osd_priv_t*)osd->priv; + g_return_if_fail (priv); + + if(priv->nav.surface) { + cairo_surface_destroy(priv->nav.surface); + priv->nav.surface = NULL; + priv->nav.lat = OSM_GPS_MAP_INVALID; + priv->nav.lon = OSM_GPS_MAP_INVALID; + if(priv->nav.name) g_free(priv->nav.name); + } + osm_gps_map_redraw(map); +} + +void +osm_gps_map_osd_draw_nav (OsmGpsMap *map, gboolean imperial, + float latitude, float longitude, char *name) { + g_return_if_fail (OSM_IS_GPS_MAP (map)); + + osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map); + g_return_if_fail (osd); + + osd_priv_t *priv = (osd_priv_t*)osd->priv; + g_return_if_fail (priv); + + osm_gps_map_osd_clear_nav (map); + + /* allocate balloon surface */ + priv->nav.surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + OSD_NAV_W+2, OSD_NAV_H+2); + + priv->nav.lat = latitude; + priv->nav.lon = longitude; + priv->nav.name = g_strdup(name); + priv->nav.imperial = imperial; + + osd_render_nav(osd); + + osm_gps_map_redraw(map); +} + +#endif // OSD_NAV + #ifdef OSD_CROSSHAIR @@ -1224,6 +1715,10 @@ osd_render_crosshair(osd); #endif +#ifdef OSD_NAV + osd_render_nav(osd); +#endif + #ifdef OSD_COORDINATES osd_render_coordinates(osd); #endif @@ -1283,7 +1778,7 @@ // now draw this onto the original context cairo_t *cr = gdk_cairo_create(drawable); - int x, y; + gint x, y; #ifdef OSD_SCALE x = OSD_X; @@ -1303,6 +1798,17 @@ cairo_paint(cr); #endif +#ifdef OSD_NAV + if(priv->nav.surface) { + x = OSD_X; + if(x < 0) x += osd->widget->allocation.width - OSD_NAV_W; + y = (osd->widget->allocation.height - OSD_NAV_H)/2; + + cairo_set_source_surface(cr, priv->nav.surface, x, y); + cairo_paint(cr); + } +#endif + #ifdef OSD_COORDINATES x = -OSD_X; y = -OSD_Y; @@ -1313,6 +1819,25 @@ cairo_paint(cr); #endif +#ifdef OSD_BALLOON + if(priv->balloon.surface) { + + /* convert given lat lon into screen coordinates */ + gint x, y; + osm_gps_map_geographic_to_screen (OSM_GPS_MAP(osd->widget), + priv->balloon.lat, priv->balloon.lon, + &x, &y); + + /* check if balloon needs to be rerendered */ + osd_render_balloon(osd); + + cairo_set_source_surface(cr, priv->balloon.surface, + x + priv->balloon.offset_x, + y + priv->balloon.offset_y); + cairo_paint(cr); + } +#endif + x = OSD_X; if(x < 0) x += osd->widget->allocation.width - OSD_W; @@ -1375,11 +1900,21 @@ cairo_surface_destroy(priv->crosshair.surface); #endif +#ifdef OSD_NAV + if (priv->nav.surface) + cairo_surface_destroy(priv->nav.surface); +#endif + #ifdef OSD_COORDINATES if (priv->coordinates.surface) cairo_surface_destroy(priv->coordinates.surface); #endif +#ifdef OSD_BALLOON + if (priv->balloon.surface) + cairo_surface_destroy(priv->balloon.surface); +#endif + g_free(priv); } @@ -1415,6 +1950,11 @@ { osd_priv_t *priv = osd_classic.priv = g_new0(osd_priv_t, 1); +#ifdef OSD_BALLOON + priv->balloon.lat = OSM_GPS_MAP_INVALID; + priv->balloon.lon = OSM_GPS_MAP_INVALID; +#endif + osd_classic.priv = priv; osm_gps_map_register_osd(map, &osd_classic); @@ -1444,5 +1984,5 @@ osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map); g_return_val_if_fail (osd, OSD_NONE); - return osd_check(osd, x, y); + return osd_check(osd, TRUE, x, y); }