--- trunk/src/osm-gps-map-osd-classic.c 2009/08/20 19:17:23 70 +++ trunk/src/osm-gps-map-osd-classic.c 2009/09/16 20:04:38 114 @@ -1,3 +1,5 @@ +/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */ +/* vim:set et sw=4 ts=4 cino=t0,(0: */ /* * Copyright (C) Till Harbaum 2009 * @@ -15,41 +17,359 @@ * with this program. If not, see . */ -#include "config.h" +#include "config.h" +#include // abs +#include // M_PI/cos() + +/* parameters that can be overwritten from the config file: */ +/* OSD_DIAMETER */ +/* OSD_X, OSD_Y */ -#ifdef USE_CAIRO -#include +#ifndef USE_CAIRO +#error "OSD control display lacks a non-cairo implementation!" #endif +#include + +#include "osm-gps-map.h" +#include "converter.h" +#include "osm-gps-map-osd-classic.h" + //the osd controls typedef struct { - GdkPixmap *backup; - cairo_surface_t *overlay; - gint backup_x, backup_y; - OsmGpsMapOsdGpsCallback cb; - gpointer data; + /* the offscreen representation of the OSD */ + struct { + cairo_surface_t *surface; + gboolean rendered; +#ifdef OSD_GPS_BUTTON + gboolean gps_enabled; +#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; + int zoom; + } scale; +#endif + +#ifdef OSD_CROSSHAIR + struct { + cairo_surface_t *surface; + gboolean rendered; + } crosshair; +#endif + +#ifdef OSD_COORDINATES + struct { + cairo_surface_t *surface; + float lat, lon; + } coordinates; +#endif + +#ifdef OSD_SOURCE_SEL + struct { + /* values to handle the "source" menu */ + cairo_surface_t *surface; + gboolean expanded; + gint shift, dir, count; + gint handler_id; + gint width, height; + gboolean rendered; + } source_sel; +#endif + } osd_priv_t; -typedef struct { +#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; + + /* handle the fact that the balloon may have been created by the */ + /* button down event */ + + gboolean is_in = + (x > xs) && (x < xs + priv->balloon.rect.w) && + (y > ys) && (y < ys + priv->balloon.rect.h); + 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); - gpointer priv; -} osm_gps_map_osd_t; + /* 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) -#define OSD_Y (10) +#endif -#define OSD_COLOR 0.5, 0.5, 1 -#define OSD_COLOR_DISABLED 0.8, 0.8, 0.8 +#ifndef OSD_Y +#define OSD_Y (10) +#endif /* parameters of the direction shape */ -#ifndef OSM_GPS_MAP_OSD_DIAMETER +#ifndef OSD_DIAMETER #define D_RAD (30) // diameter of dpad #else -#define D_RAD (OSM_GPS_MAP_OSD_DIAMETER) +#define D_RAD (OSD_DIAMETER) #endif #define D_TIP (4*D_RAD/5) // distance of arrow tip from dpad center #define D_LEN (D_RAD/4) // length of arrow @@ -59,24 +379,45 @@ #define Z_STEP (D_RAD/4) // distance between dpad and zoom #define Z_RAD (D_RAD/2) // radius of "caps" of zoom bar +#ifdef OSD_SHADOW_ENABLE /* shadow also depends on control size */ #define OSD_SHADOW (D_RAD/6) +#else +#define OSD_SHADOW (0) +#endif + +/* normally the GPS button is in the center of the dpad. if there's */ +/* no dpad it will go into the zoom area */ +#if defined(OSD_GPS_BUTTON) && defined(OSD_NO_DPAD) +#define Z_GPS 1 +#else +#define Z_GPS 0 +#endif /* total width and height of controls incl. shadow */ -#define OSD_W (2*D_RAD + OSD_SHADOW) +#define OSD_W (2*D_RAD + OSD_SHADOW + Z_GPS * 2 * Z_RAD) +#if !Z_GPS #define OSD_H (2*D_RAD + Z_STEP + 2*Z_RAD + OSD_SHADOW) +#else +#define OSD_H (2*Z_RAD + OSD_SHADOW) +#endif +#ifdef OSD_SHADOW_ENABLE #define OSD_LBL_SHADOW (OSD_SHADOW/2) +#endif + +#define Z_TOP ((1-Z_GPS) * (2 * D_RAD + Z_STEP)) -#define Z_TOP (2 * D_RAD + Z_STEP) #define Z_MID (Z_TOP + Z_RAD) #define Z_BOT (Z_MID + Z_RAD) #define Z_LEFT (Z_RAD) -#define Z_RIGHT (2 * D_RAD - Z_RAD) +#define Z_RIGHT (2 * D_RAD - Z_RAD + Z_GPS * 2 * Z_RAD) +#define Z_CENTER ((Z_RIGHT + Z_LEFT)/2) /* create the cairo shape used for the zoom buttons */ static void -osm_gps_map_osd_zoom_shape(cairo_t *cr, gint x, gint y) { +osd_zoom_shape(cairo_t *cr, gint x, gint y) +{ cairo_move_to (cr, x+Z_LEFT, y+Z_TOP); cairo_line_to (cr, x+Z_RIGHT, y+Z_TOP); cairo_arc (cr, x+Z_RIGHT, y+Z_MID, Z_RAD, -M_PI/2, M_PI/2); @@ -84,23 +425,73 @@ cairo_arc (cr, x+Z_LEFT, y+Z_MID, Z_RAD, M_PI/2, -M_PI/2); } +/* ------------------- color/shadow functions ----------------- */ + +#ifndef OSD_COLOR +/* if no color has been specified we just use the gdks default colors */ +static void +osd_labels(cairo_t *cr, gint width, gboolean enabled, + GdkColor *fg, GdkColor *disabled) { + if(enabled) gdk_cairo_set_source_color(cr, fg); + else gdk_cairo_set_source_color(cr, disabled); + cairo_set_line_width (cr, width); +} +#else +static void +osd_labels(cairo_t *cr, gint width, gboolean enabled) { + if(enabled) cairo_set_source_rgb (cr, OSD_COLOR); + else cairo_set_source_rgb (cr, OSD_COLOR_DISABLED); + cairo_set_line_width (cr, width); +} +#endif + +#ifdef OSD_SHADOW_ENABLE +static void +osd_labels_shadow(cairo_t *cr, gint width, gboolean enabled) { + cairo_set_source_rgba (cr, 0, 0, 0, enabled?0.3:0.15); + cairo_set_line_width (cr, width); +} +#endif + +#ifndef OSD_NO_DPAD /* create the cairo shape used for the dpad */ static void -osm_gps_map_osd_dpad_shape(cairo_t *cr, gint x, gint y) { +osd_dpad_shape(cairo_t *cr, gint x, gint y) +{ cairo_arc (cr, x+D_RAD, y+D_RAD, D_RAD, 0, 2 * M_PI); } +#endif + +#ifdef OSD_SHADOW_ENABLE +static void +osd_shape_shadow(cairo_t *cr) { + cairo_set_source_rgba (cr, 0, 0, 0, 0.2); + cairo_fill (cr); + cairo_stroke (cr); +} +#endif + +#ifndef OSD_COLOR +/* if no color has been specified we just use the gdks default colors */ +static void +osd_shape(cairo_t *cr, GdkColor *bg, GdkColor *fg) { + gdk_cairo_set_source_color(cr, bg); + cairo_fill_preserve (cr); + gdk_cairo_set_source_color(cr, fg); + cairo_set_line_width (cr, 1); + cairo_stroke (cr); +} +#else +static void +osd_shape(cairo_t *cr) { + cairo_set_source_rgb (cr, OSD_COLOR_BG); + cairo_fill_preserve (cr); + cairo_set_source_rgb (cr, OSD_COLOR); + cairo_set_line_width (cr, 1); + cairo_stroke (cr); +} +#endif -typedef enum { - OSD_NONE = 0, - OSD_BG, - OSD_UP, - OSD_DOWN, - OSD_LEFT, - OSD_RIGHT, - OSD_IN, - OSD_OUT, - OSD_GPS -} osd_button_t; static gboolean osm_gps_map_in_circle(gint x, gint y, gint cx, gint cy, gint rad) @@ -108,20 +499,23 @@ return( pow(cx - x, 2) + pow(cy - y, 2) < rad * rad); } +#ifndef OSD_NO_DPAD /* check whether x/y is within the dpad */ static osd_button_t -osm_gps_map_osd_check_dpad(gint x, gint y) +osd_check_dpad(gint x, gint y) { /* within entire dpad circle */ - if( osm_gps_map_in_circle(x, y, OSD_X + D_RAD, OSD_Y + D_RAD, D_RAD)) + if( osm_gps_map_in_circle(x, y, D_RAD, D_RAD, D_RAD)) { /* convert into position relative to dpads centre */ - x -= (OSD_X + D_RAD); - y -= (OSD_Y + D_RAD); + x -= D_RAD; + y -= D_RAD; +#ifdef OSD_GPS_BUTTON /* check for dpad center goes here! */ if( osm_gps_map_in_circle(x, y, 0, 0, D_RAD/3)) return OSD_GPS; +#endif if( y < 0 && abs(x) < abs(y)) return OSD_UP; @@ -139,1802 +533,1240 @@ } return OSD_NONE; } +#endif /* check whether x/y is within the zoom pads */ static osd_button_t -osm_gps_map_osd_check_zoom(gint x, gint y) { - if( x > OSD_X && x < (OSD_X + OSD_W) && y > Z_TOP && y < (OSD_Y+Z_BOT)) { +osd_check_zoom(gint x, gint y) { + if( x > 0 && x < OSD_W && y > Z_TOP && y < Z_BOT) { /* within circle around (-) label */ - if( osm_gps_map_in_circle(x, y, OSD_X + Z_LEFT, OSD_Y + Z_MID, Z_RAD)) - return OSD_OUT; - - /* between center of (-) button and center of entire zoom control area */ - if(x > OSD_LEFT && x < OSD_X + D_RAD) + if( osm_gps_map_in_circle(x, y, Z_LEFT, Z_MID, Z_RAD)) return OSD_OUT; /* within circle around (+) label */ - if( osm_gps_map_in_circle(x, y, OSD_X + Z_RIGHT, OSD_Y + Z_MID, Z_RAD)) + if( osm_gps_map_in_circle(x, y, Z_RIGHT, Z_MID, Z_RAD)) return OSD_IN; +#if Z_GPS == 1 + /* within square around center */ + if( x > Z_CENTER - Z_RAD && x < Z_CENTER + Z_RAD) + return OSD_GPS; +#endif + + /* between center of (-) button and center of entire zoom control area */ + if(x > OSD_LEFT && x < D_RAD) + return OSD_OUT; + /* between center of (+) button and center of entire zoom control area */ - if(x < OSD_RIGHT && x > OSD_X + D_RAD) + if(x < OSD_RIGHT && x > D_RAD) return OSD_IN; } return OSD_NONE; } -static osd_button_t -osm_gps_map_osd_check(gint x, gint y) { - osd_button_t but = OSD_NONE; - - /* first do a rough test for the OSD area. */ - /* this is just to avoid an unnecessary detailed test */ - if(x > OSD_X && x < OSD_X + OSD_W && - y > OSD_Y && y < OSD_Y + OSD_H) { - but = osm_gps_map_osd_check_dpad(x, y); - - if(but == OSD_NONE) - but = osm_gps_map_osd_check_zoom(x, y); - } - - return but; -} - -static void -osm_gps_map_osd_shape_shadow(cairo_t *cr) { - cairo_set_source_rgba (cr, 0, 0, 0, 0.2); - cairo_fill (cr); - cairo_stroke (cr); -} +#ifdef OSD_SOURCE_SEL -static void -osm_gps_map_osd_shape(cairo_t *cr) { - cairo_set_source_rgb (cr, 1, 1, 1); - cairo_fill_preserve (cr); - cairo_set_source_rgb (cr, OSD_COLOR); - cairo_set_line_width (cr, 1); - cairo_stroke (cr); -} +/* place source selection at right border */ +#define OSD_S_RAD (Z_RAD) +#define OSD_S_X (-OSD_X) +#define OSD_S_Y (OSD_Y) +#define OSD_S_PW (2 * Z_RAD) +#define OSD_S_W (OSD_S_PW) +#define OSD_S_PH (2 * Z_RAD) +#define OSD_S_H (OSD_S_PH + OSD_SHADOW) + +/* size of usable area when expanded */ +#define OSD_S_AREA_W (priv->source_sel.width) +#define OSD_S_AREA_H (priv->source_sel.height) +#define OSD_S_EXP_W (OSD_S_PW + OSD_S_AREA_W + OSD_SHADOW) +#define OSD_S_EXP_H (OSD_S_AREA_H + OSD_SHADOW) + +/* internal value to draw the arrow on the "puller" */ +#define OSD_S_D0 (OSD_S_RAD/2) +#ifndef OSD_FONT_SIZE +#define OSD_FONT_SIZE 16.0 +#endif +#define OSD_TEXT_BORDER (OSD_FONT_SIZE/2) +#define OSD_TEXT_SKIP (OSD_FONT_SIZE/8) +/* draw the shape of the source selection OSD, either only the puller (not expanded) */ +/* or the entire menu incl. the puller (expanded) */ static void -osm_gps_map_osd_dpad_labels(cairo_t *cr, gint x, gint y) { - /* move reference to dpad center */ - x += D_RAD; - y += D_RAD; - - const static gint offset[][3][2] = { - /* left arrow/triangle */ - { { -D_TIP+D_LEN, -D_WID }, { -D_LEN, D_WID }, { +D_LEN, D_WID } }, - /* right arrow/triangle */ - { { +D_TIP-D_LEN, -D_WID }, { +D_LEN, D_WID }, { -D_LEN, D_WID } }, - /* top arrow/triangle */ - { { -D_WID, -D_TIP+D_LEN }, { D_WID, -D_LEN }, { D_WID, +D_LEN } }, - /* bottom arrow/triangle */ - { { -D_WID, +D_TIP-D_LEN }, { D_WID, +D_LEN }, { D_WID, -D_LEN } } - }; - - int i; - for(i=0;i<4;i++) { - cairo_move_to (cr, x + offset[i][0][0], y + offset[i][0][1]); - cairo_rel_line_to (cr, offset[i][1][0], offset[i][1][1]); - cairo_rel_line_to (cr, offset[i][2][0], offset[i][2][1]); +osd_source_shape(osd_priv_t *priv, cairo_t *cr, gint x, gint y) { + if(!priv->source_sel.expanded) { + /* just draw the puller */ + cairo_move_to (cr, x + OSD_S_PW, y + OSD_S_PH); + cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2); + cairo_line_to (cr, x + OSD_S_PW, y); + } else { + /* draw the puller and the area itself */ + cairo_move_to (cr, x + OSD_S_PW + OSD_S_AREA_W, y + OSD_S_AREA_H); + cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_AREA_H); + if(OSD_S_Y > 0) { + cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_PH); + cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2); + } else { + cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_AREA_H-OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2); + cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_AREA_H - OSD_S_PH); + cairo_line_to (cr, x + OSD_S_PW, y); + } + cairo_line_to (cr, x + OSD_S_PW + OSD_S_AREA_W, y); + cairo_close_path (cr); } } -/* draw the sattelite dish icon in the center of the dpad */ -#define GPS_V0 (D_RAD/8) -#define GPS_V1 (D_RAD/10) -#define GPS_V2 (D_RAD/5) - -/* draw a satellite receiver dish */ static void -osm_gps_map_osd_dpad_gps(cairo_t *cr, gint x, gint y) { - /* move reference to dpad center */ - x += D_RAD; - y += D_RAD + GPS_V0; +osd_source_content(osm_gps_map_osd_t *osd, cairo_t *cr, gint offset) { + osd_priv_t *priv = (osd_priv_t*)osd->priv; - cairo_move_to (cr, x-GPS_V0, y+GPS_V0); - cairo_rel_line_to (cr, +GPS_V0, -GPS_V0); - cairo_rel_line_to (cr, +GPS_V0, +GPS_V0); - cairo_close_path (cr); + int py = offset + OSD_S_RAD - OSD_S_D0; - cairo_move_to (cr, x+GPS_V1-GPS_V2, y-2*GPS_V2); - cairo_curve_to (cr, x-GPS_V2, y, x+GPS_V1, y+GPS_V1, x+GPS_V1+GPS_V2, y); - cairo_close_path (cr); - - x += GPS_V1; - cairo_move_to (cr, x, y-GPS_V2); - cairo_rel_line_to (cr, +GPS_V1, -GPS_V1); -} - -#define Z_LEN (2*Z_RAD/3) + if(!priv->source_sel.expanded) { + /* draw the "puller" open (<) arrow */ + cairo_move_to (cr, offset + OSD_S_RAD + OSD_S_D0/2, py); + cairo_rel_line_to (cr, -OSD_S_D0, +OSD_S_D0); + cairo_rel_line_to (cr, +OSD_S_D0, +OSD_S_D0); + } else { + if(OSD_S_Y < 0) + py += OSD_S_AREA_H - OSD_S_PH; + + /* draw the "puller" close (>) arrow */ + cairo_move_to (cr, offset + OSD_S_RAD - OSD_S_D0/2, py); + cairo_rel_line_to (cr, +OSD_S_D0, +OSD_S_D0); + cairo_rel_line_to (cr, -OSD_S_D0, +OSD_S_D0); + cairo_stroke(cr); + + /* don't draw a shadow for the text content */ + if(offset == 1) { + gint source; + g_object_get(osd->widget, "map-source", &source, NULL); + + cairo_select_font_face (cr, "Sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, OSD_FONT_SIZE); + + int i, step = (priv->source_sel.height - 2*OSD_TEXT_BORDER) / + OSM_GPS_MAP_SOURCE_LAST; + for(i=OSM_GPS_MAP_SOURCE_NULL+1;i<=OSM_GPS_MAP_SOURCE_LAST;i++) { + cairo_text_extents_t extents; + const char *src = osm_gps_map_source_get_friendly_name(i); + cairo_text_extents (cr, src, &extents); + + int x = offset + OSD_S_PW + OSD_TEXT_BORDER; + int y = offset + step * (i-1) + OSD_TEXT_BORDER; + + /* draw filled rectangle if selected */ + if(source == i) { + cairo_rectangle(cr, x - OSD_TEXT_BORDER/2, + y - OSD_TEXT_SKIP, + priv->source_sel.width - OSD_TEXT_BORDER, + step + OSD_TEXT_SKIP); + cairo_fill(cr); + + /* temprarily draw with background color */ +#ifndef OSD_COLOR + GdkColor bg = osd->widget->style->bg[GTK_STATE_NORMAL]; + gdk_cairo_set_source_color(cr, &bg); +#else + cairo_set_source_rgb (cr, OSD_COLOR_BG); +#endif + } -static void -osm_gps_map_osd_zoom_labels(cairo_t *cr, gint x, gint y) { - cairo_move_to (cr, x + Z_LEFT - Z_LEN, y + Z_MID); - cairo_line_to (cr, x + Z_LEFT + Z_LEN, y + Z_MID); + cairo_move_to (cr, x, y + OSD_TEXT_SKIP - extents.y_bearing); + cairo_show_text (cr, src); - cairo_move_to (cr, x + Z_RIGHT, y + Z_MID - Z_LEN); - cairo_line_to (cr, x + Z_RIGHT, y + Z_MID + Z_LEN); - cairo_move_to (cr, x + Z_RIGHT - Z_LEN, y + Z_MID); - cairo_line_to (cr, x + Z_RIGHT + Z_LEN, y + Z_MID); + /* restore color */ + if(source == i) { +#ifndef OSD_COLOR + GdkColor fg = osd->widget->style->fg[GTK_STATE_NORMAL]; + gdk_cairo_set_source_color(cr, &fg); +#else + cairo_set_source_rgb (cr, OSD_COLOR); +#endif + } + } + } + } } static void -osm_gps_map_osd_labels(cairo_t *cr, gint width, gboolean enabled) { - if(enabled) cairo_set_source_rgb (cr, OSD_COLOR); - else cairo_set_source_rgb (cr, OSD_COLOR_DISABLED); - cairo_set_line_width (cr, width); - cairo_stroke (cr); -} +osd_render_source_sel(osm_gps_map_osd_t *osd, gboolean force_rerender) { + osd_priv_t *priv = (osd_priv_t*)osd->priv; -static void -osm_gps_map_osd_labels_shadow(cairo_t *cr, gint width, gboolean enabled) { - cairo_set_source_rgba (cr, 0, 0, 0, enabled?0.3:0.15); - cairo_set_line_width (cr, width); - cairo_stroke (cr); -} + if(priv->source_sel.rendered && !force_rerender) + return; -static void -osm_gps_map_osd_render(OsmGpsMapPrivate *priv) { + priv->source_sel.rendered = TRUE; - /* first fill with transparency */ - cairo_t *cr = cairo_create(priv->osd.overlay); +#ifndef OSD_COLOR + GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL]; + GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL]; + GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE]; +#endif + + /* draw source selector */ + cairo_t *cr = cairo_create(priv->source_sel.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); - /* --------- draw zoom and dpad shape shadow ----------- */ - gint x = 0, y = 0; - - osm_gps_map_osd_zoom_shape(cr, x + OSD_SHADOW, y + OSD_SHADOW); - osm_gps_map_osd_shape_shadow(cr); - osm_gps_map_osd_dpad_shape(cr, x + OSD_SHADOW, y + OSD_SHADOW); - osm_gps_map_osd_shape_shadow(cr); - - /* --------- draw zoom and dpad shape ----------- */ +#ifdef OSD_SHADOW_ENABLE + osd_source_shape(priv, cr, 1+OSD_SHADOW, 1+OSD_SHADOW); + osd_shape_shadow(cr); +#endif - osm_gps_map_osd_zoom_shape(cr, x, y); - osm_gps_map_osd_shape(cr); - osm_gps_map_osd_dpad_shape(cr, x, y); - osm_gps_map_osd_shape(cr); + osd_source_shape(priv, cr, 1, 1); +#ifndef OSD_COLOR + osd_shape(cr, &bg, &fg); +#else + osd_shape(cr); +#endif - /* --------- draw zoom and dpad labels --------- */ +#ifdef OSD_SHADOW_ENABLE + osd_labels_shadow(cr, Z_RAD/3, TRUE); + osd_source_content(osd, cr, 1+OSD_LBL_SHADOW); + cairo_stroke (cr); +#endif +#ifndef OSD_COLOR + osd_labels(cr, Z_RAD/3, TRUE, &fg, &da); +#else + osd_labels(cr, Z_RAD/3, TRUE); +#endif + osd_source_content(osd, cr, 1); + cairo_stroke (cr); - osm_gps_map_osd_zoom_labels(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW); - osm_gps_map_osd_dpad_labels(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW); - osm_gps_map_osd_labels_shadow(cr, Z_RAD/3, TRUE); - osm_gps_map_osd_dpad_gps(cr, x + OSD_LBL_SHADOW, y + OSD_LBL_SHADOW); - osm_gps_map_osd_labels_shadow(cr, Z_RAD/6, priv->osd.cb != NULL); - - osm_gps_map_osd_zoom_labels(cr, x, y); - osm_gps_map_osd_dpad_labels(cr, x, y); - osm_gps_map_osd_labels(cr, Z_RAD/3, TRUE); - osm_gps_map_osd_dpad_gps(cr, x, y); - osm_gps_map_osd_labels(cr, Z_RAD/6, priv->osd.cb != NULL); - cairo_destroy(cr); } +/* re-allocate the buffer used to draw the menu. This is used */ +/* to collapse/expand the buffer */ static void -osm_gps_map_osd_draw_controls (OsmGpsMap *map, gint xoffset, gint yoffset) -{ - OsmGpsMapPrivate *priv = map->priv; - - /* backup previous contents */ - if(!priv->osd.backup) - priv->osd.backup = gdk_pixmap_new(priv->pixmap, OSD_W+2, OSD_H+2, -1); +osd_source_reallocate(osm_gps_map_osd_t *osd) { + osd_priv_t *priv = (osd_priv_t*)osd->priv; - gint x = OSD_X + EXTRA_BORDER + xoffset; - gint y = OSD_Y + EXTRA_BORDER + yoffset; - - /* create backup of background */ - gdk_draw_drawable(priv->osd.backup, - GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(GTK_WIDGET(map))], - priv->pixmap, x-1, y-1, 0, 0, OSD_W+2, OSD_H+2); - - priv->osd.backup_x = x-1; - priv->osd.backup_y = y-1; - - -#ifdef USE_CAIRO - /* OSD itself uses some off-screen rendering, so check if the */ - /* offscreen buffer is present and create it if not */ - if(!priv->osd.overlay) { - /* create overlay ... */ - priv->osd.overlay = - cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W, OSD_H); - /* ... and render it */ - osm_gps_map_osd_render(priv); - } - - // now draw this onto the original context - cairo_t *cr = gdk_cairo_create(priv->pixmap); - cairo_set_source_surface(cr, priv->osd.overlay, x, y); - cairo_paint(cr); - cairo_destroy(cr); + /* re-allocate offscreen bitmap */ + g_assert (priv->source_sel.surface); -#else -#warning "OSD control display lacks a non-cairo implementation!" -#endif -} + int w = OSD_S_W, h = OSD_S_H; + if(priv->source_sel.expanded) { + cairo_text_extents_t extents; + + /* determine content size */ + cairo_t *cr = cairo_create(priv->source_sel.surface); + cairo_select_font_face (cr, "Sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, OSD_FONT_SIZE); + + /* calculate menu size */ + int i, max_h = 0, max_w = 0; + for(i=OSM_GPS_MAP_SOURCE_NULL+1;i<=OSM_GPS_MAP_SOURCE_LAST;i++) { + const char *src = osm_gps_map_source_get_friendly_name(i); + cairo_text_extents (cr, src, &extents); -static void -osm_gps_map_osd_restore (OsmGpsMap *map) -{ - OsmGpsMapPrivate *priv = map->priv; + if(extents.width > max_w) max_w = extents.width; + if(extents.height > max_h) max_h = extents.height; + } + cairo_destroy(cr); + + priv->source_sel.width = max_w + 2*OSD_TEXT_BORDER; + priv->source_sel.height = OSM_GPS_MAP_SOURCE_LAST * + (max_h + 2*OSD_TEXT_SKIP) + 2*OSD_TEXT_BORDER; + + w = OSD_S_EXP_W; + h = OSD_S_EXP_H; + } + + cairo_surface_destroy(priv->source_sel.surface); + priv->source_sel.surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w+2, h+2); + + osd_render_source_sel(osd, TRUE); +} + +#define OSD_HZ 15 +#define OSD_TIME 500 + +static gboolean osd_source_animate(gpointer data) { + osm_gps_map_osd_t *osd = (osm_gps_map_osd_t*)data; + osd_priv_t *priv = (osd_priv_t*)osd->priv; + int diff = OSD_S_EXP_W - OSD_S_W - OSD_S_X; + gboolean done = FALSE; + priv->source_sel.count += priv->source_sel.dir; + + /* shifting in */ + if(priv->source_sel.dir < 0) { + if(priv->source_sel.count <= 0) { + priv->source_sel.count = 0; + done = TRUE; + } + } else { + if(priv->source_sel.count >= 1000) { + priv->source_sel.expanded = FALSE; + osd_source_reallocate(osd); - /* restore backup of previous contents */ - if(priv->osd.backup) { - /* create backup of background */ - gdk_draw_drawable(priv->pixmap, - GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(GTK_WIDGET(map))], - priv->osd.backup, 0, 0, - priv->osd.backup_x, priv->osd.backup_y, OSD_W+2, OSD_H+2); + priv->source_sel.count = 1000; + done = TRUE; + } } -} - -#endif - -static gboolean -osm_gps_map_map_redraw (OsmGpsMap *map) -{ - OsmGpsMapPrivate *priv = map->priv; - priv->idle_map_redraw = 0; - /* the motion_notify handler uses priv->pixmap to redraw the area; if we - * change it while we are dragging, we will end up showing it in the wrong - * place. This could be fixed by carefully recompute the coordinates, but - * for now it's easier just to disable redrawing the map while dragging */ - if (priv->dragging) - return FALSE; + /* count runs linearly from 0 to 1000, map this nicely onto a position */ - priv->redraw_cycle++; + /* nice sinoid mapping */ + float m = 0.5-cos(priv->source_sel.count * M_PI / 1000.0)/2; + priv->source_sel.shift = + (osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X) + + m * diff; - /* draw white background to initialise pixmap */ - gdk_draw_rectangle ( - priv->pixmap, - GTK_WIDGET(map)->style->white_gc, - TRUE, - 0, 0, - GTK_WIDGET(map)->allocation.width + EXTRA_BORDER * 2, - GTK_WIDGET(map)->allocation.height + EXTRA_BORDER * 2); - - osm_gps_map_fill_tiles_pixel(map); - - osm_gps_map_print_tracks(map); - osm_gps_map_draw_gps_point(map); - osm_gps_map_print_images(map); -#ifdef ENABLE_BALLOON - osm_gps_map_draw_balloon_int(map); -#endif -#ifdef ENABLE_OSD - osm_gps_map_osd_draw_controls(map, 0, 0); -#endif - - //osm_gps_map_osd_speed(map, 1.5); - osm_gps_map_purge_cache(map); - gtk_widget_queue_draw (GTK_WIDGET (map)); + /* make sure the screen is updated */ + osm_gps_map_repaint(OSM_GPS_MAP(osd->widget)); - return FALSE; -} + /* stop animation if done */ + if(done) + priv->source_sel.handler_id = 0; -static void -osm_gps_map_map_redraw_idle (OsmGpsMap *map) -{ - OsmGpsMapPrivate *priv = map->priv; - - if (priv->idle_map_redraw == 0) - priv->idle_map_redraw = g_idle_add ((GSourceFunc)osm_gps_map_map_redraw, map); + return !done; } +/* switch between expand and collapse mode of source selection */ static void -osm_gps_map_init (OsmGpsMap *object) +osd_source_toggle(osm_gps_map_osd_t *osd) { - OsmGpsMapPrivate *priv; - - priv = G_TYPE_INSTANCE_GET_PRIVATE (object, OSM_TYPE_GPS_MAP, OsmGpsMapPrivate); - object->priv = priv; - - priv->pixmap = NULL; - - priv->trip_history = NULL; - priv->gps = g_new0(coord_t, 1); - priv->gps_valid = FALSE; - -#ifdef ENABLE_BALLOON - priv->balloon.coo = g_new0(coord_t, 1); - priv->balloon.valid = FALSE; - priv->balloon.cb = NULL; -#endif - -#ifdef ENABLE_OSD - priv->osd.backup = NULL; - priv->osd.overlay = NULL; - priv->osd.cb = NULL; -#endif - - priv->tracks = NULL; - priv->images = NULL; + osd_priv_t *priv = (osd_priv_t*)osd->priv; - priv->drag_counter = 0; - priv->drag_mouse_dx = 0; - priv->drag_mouse_dy = 0; - priv->drag_start_mouse_x = 0; - priv->drag_start_mouse_y = 0; - - priv->uri_format = 0; - priv->the_google = FALSE; - - priv->map_source = -1; - -#ifndef LIBSOUP22 - //Change naumber of concurrent connections option? - priv->soup_session = soup_session_async_new_with_options( - SOUP_SESSION_USER_AGENT, - "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11", - NULL); -#else - /* libsoup-2.2 seems not to be able to set the user agent */ - priv->soup_session = soup_session_async_new(); -#endif - - //Hash table which maps tile d/l URIs to SoupMessage requests - priv->tile_queue = g_hash_table_new (g_str_hash, g_str_equal); - - //Some mapping providers (Google) have varying degrees of tiles at multiple - //zoom levels - priv->missing_tiles = g_hash_table_new (g_str_hash, g_str_equal); - - /* memory cache for most recently used tiles */ - priv->tile_cache = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify)cached_tile_free); - priv->max_tile_cache_size = 20; - - gtk_widget_add_events (GTK_WIDGET (object), - GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | - GDK_POINTER_MOTION_MASK | - GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK); - GTK_WIDGET_SET_FLAGS (object, GTK_CAN_FOCUS); + /* ignore clicks while animation is running */ + if(priv->source_sel.handler_id) + return; - g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_MASK, my_log_handler, NULL); + /* expand immediately, collapse is handle at the end of the */ + /* collapse animation */ + if(!priv->source_sel.expanded) { + priv->source_sel.expanded = TRUE; + osd_source_reallocate(osd); + + priv->source_sel.count = 1000; + priv->source_sel.shift = osd->widget->allocation.width - OSD_S_W; + priv->source_sel.dir = -1000/OSD_HZ; + } else { + priv->source_sel.count = 0; + priv->source_sel.shift = osd->widget->allocation.width - + OSD_S_EXP_W + OSD_S_X; + priv->source_sel.dir = +1000/OSD_HZ; + } + + /* start timer to handle animation */ + priv->source_sel.handler_id = gtk_timeout_add(OSD_TIME/OSD_HZ, + osd_source_animate, osd); } -#ifndef G_CHECKSUM_MD5 -/* simple hash algorithm hack if md5 is not present */ -static char *simple_hash(char *str) { - union { - char str[4]; - gulong val; - } hash = { .val = 0x55555555 }; +/* check if the user clicked inside the source selection area */ +static osd_button_t +osd_source_check(osm_gps_map_osd_t *osd, gboolean down, gint x, gint y) { + osd_priv_t *priv = (osd_priv_t*)osd->priv; - while(*str) { - hash.str[(int)str & 3] ^= *str; - str++; - } - return g_strdup_printf("%08lX", hash.val); -} -#endif + if(!priv->source_sel.expanded) + x -= osd->widget->allocation.width - OSD_S_W; + else + x -= osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X; -static GObject * -osm_gps_map_constructor (GType gtype, guint n_properties, GObjectConstructParam *properties) -{ - GObject *object; - OsmGpsMapPrivate *priv; - OsmGpsMap *map; - const char *uri; - - //Always chain up to the parent constructor - object = G_OBJECT_CLASS(osm_gps_map_parent_class)->constructor(gtype, n_properties, properties); - map = OSM_GPS_MAP(object); - priv = OSM_GPS_MAP_PRIVATE(object); - - //user can specify a map source ID, or a repo URI as the map source - uri = osm_gps_map_source_get_repo_uri(OSM_GPS_MAP_SOURCE_NULL); - if ( (priv->map_source == 0) || (strcmp(priv->repo_uri, uri) == 0) ) { - g_debug("Using null source"); - priv->map_source = OSM_GPS_MAP_SOURCE_NULL; - - priv->null_tile = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, 256, 256); - gdk_pixbuf_fill(priv->null_tile, 0xcccccc00); - } - else if (priv->map_source >= 0) { - //check if the source given is valid - uri = osm_gps_map_source_get_repo_uri(priv->map_source); - if (uri) { - g_debug("Setting map source from ID"); - g_free(priv->repo_uri); - - priv->repo_uri = g_strdup(uri); - priv->image_format = g_strdup( - osm_gps_map_source_get_image_format(priv->map_source)); - priv->max_zoom = osm_gps_map_source_get_max_zoom(priv->map_source); - priv->min_zoom = osm_gps_map_source_get_min_zoom(priv->map_source); + if(OSD_S_Y > 0) + y -= OSD_S_Y; + else + y -= osd->widget->allocation.height - OSD_S_PH + OSD_S_Y; + + /* within square around puller? */ + if(y > 0 && y < OSD_S_PH && x > 0 && x < OSD_S_PW) { + /* really within puller shape? */ + if(x > Z_RAD || osm_gps_map_in_circle(x, y, Z_RAD, Z_RAD, Z_RAD)) { + /* expand source selector */ + if(down) + osd_source_toggle(osd); + + /* tell upper layers that user clicked some background element */ + /* of the OSD */ + return OSD_BG; } } - if (!priv->cache_dir_is_full_path) { -#ifdef G_CHECKSUM_MD5 - char *md5 = g_compute_checksum_for_string (G_CHECKSUM_MD5, priv->repo_uri, -1); -#else - char *md5 = simple_hash(priv->repo_uri); -#endif + /* check for clicks into data area */ + if(priv->source_sel.expanded && !priv->source_sel.handler_id) { + /* re-adjust from puller top to content top */ + if(OSD_S_Y < 0) + y += OSD_S_EXP_H - OSD_S_PH; + + if(x > OSD_S_PW && + x < OSD_S_PW + OSD_S_EXP_W && + y > 0 && + y < OSD_S_EXP_H) { + + int step = (priv->source_sel.height - 2*OSD_TEXT_BORDER) + / OSM_GPS_MAP_SOURCE_LAST; + + y -= OSD_TEXT_BORDER - OSD_TEXT_SKIP; + y /= step; + y += 1; + + 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)); + } + } - if (priv->cache_dir) { - char *old = priv->cache_dir; - //the new cachedir is the given cache dir + the md5 of the repo_uri - priv->cache_dir = g_strdup_printf("%s%c%s", old, G_DIR_SEPARATOR, md5); - g_debug("Adjusting cache dir %s -> %s", old, priv->cache_dir); - g_free(old); - } else { - //the new cachedir is the current dir + the md5 of the repo_uri - priv->cache_dir = g_strdup(md5); + /* return "clicked in OSD background" to prevent further */ + /* processing by application */ + return OSD_BG; } - - g_free(md5); } - inspect_map_uri(map); - - return object; + return OSD_NONE; } +#endif // OSD_SOURCE_SEL -static void -osm_gps_map_dispose (GObject *object) -{ - OsmGpsMap *map = OSM_GPS_MAP(object); - OsmGpsMapPrivate *priv = map->priv; - - if (priv->is_disposed) - return; - - priv->is_disposed = TRUE; - - soup_session_abort(priv->soup_session); - g_object_unref(priv->soup_session); - - g_hash_table_destroy(priv->tile_queue); - g_hash_table_destroy(priv->missing_tiles); - g_hash_table_destroy(priv->tile_cache); - - osm_gps_map_free_images(map); - - if(priv->pixmap) - g_object_unref (priv->pixmap); - - if (priv->null_tile) - g_object_unref (priv->null_tile); - - if(priv->gc_map) - g_object_unref(priv->gc_map); - - if (priv->idle_map_redraw != 0) - g_source_remove (priv->idle_map_redraw); +static osd_button_t +osd_check(osm_gps_map_osd_t *osd, gboolean down, gint x, gint y) { + osd_button_t but = OSD_NONE; - g_free(priv->gps); +#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 ENABLE_BALLOON - g_free(priv->balloon.coo); +#ifdef OSD_SOURCE_SEL + /* the source selection area is handles internally */ + but = osd_source_check(osd, down, x, y); #endif + + if(but == OSD_NONE) { + 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) { +#ifndef OSD_NO_DPAD + but = osd_check_dpad(x, y); +#endif + } -#ifdef ENABLE_OSD - if (priv->osd.backup) - g_object_unref(priv->osd.backup); + if(but == OSD_NONE) + but = osd_check_zoom(x, y); + } - if (priv->osd.overlay) - cairo_surface_destroy(priv->osd.overlay); +#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 - - G_OBJECT_CLASS (osm_gps_map_parent_class)->dispose (object); + + return but; } +#ifndef OSD_NO_DPAD static void -osm_gps_map_finalize (GObject *object) -{ - OsmGpsMap *map = OSM_GPS_MAP(object); - OsmGpsMapPrivate *priv = map->priv; - - g_free(priv->cache_dir); - g_free(priv->repo_uri); - g_free(priv->image_format); +osd_dpad_labels(cairo_t *cr, gint x, gint y) { + /* move reference to dpad center */ + x += D_RAD; + y += D_RAD; - osm_gps_map_free_trip(map); - osm_gps_map_free_tracks(map); + const static gint offset[][3][2] = { + /* left arrow/triangle */ + { { -D_TIP+D_LEN, -D_WID }, { -D_LEN, D_WID }, { +D_LEN, D_WID } }, + /* right arrow/triangle */ + { { +D_TIP-D_LEN, -D_WID }, { +D_LEN, D_WID }, { -D_LEN, D_WID } }, + /* top arrow/triangle */ + { { -D_WID, -D_TIP+D_LEN }, { D_WID, -D_LEN }, { D_WID, +D_LEN } }, + /* bottom arrow/triangle */ + { { -D_WID, +D_TIP-D_LEN }, { D_WID, +D_LEN }, { D_WID, -D_LEN } } + }; - G_OBJECT_CLASS (osm_gps_map_parent_class)->finalize (object); + int i; + for(i=0;i<4;i++) { + cairo_move_to (cr, x + offset[i][0][0], y + offset[i][0][1]); + cairo_rel_line_to (cr, offset[i][1][0], offset[i][1][1]); + cairo_rel_line_to (cr, offset[i][2][0], offset[i][2][1]); + } } - -static void -osm_gps_map_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - g_return_if_fail (OSM_IS_GPS_MAP (object)); - OsmGpsMap *map = OSM_GPS_MAP(object); - OsmGpsMapPrivate *priv = map->priv; - - switch (prop_id) - { - case PROP_AUTO_CENTER: - priv->map_auto_center = g_value_get_boolean (value); - break; - case PROP_RECORD_TRIP_HISTORY: - priv->record_trip_history = g_value_get_boolean (value); - break; - case PROP_SHOW_TRIP_HISTORY: - priv->show_trip_history = g_value_get_boolean (value); - break; - case PROP_AUTO_DOWNLOAD: - priv->map_auto_download = g_value_get_boolean (value); - break; - case PROP_REPO_URI: - priv->repo_uri = g_value_dup_string (value); - break; - case PROP_PROXY_URI: - if ( g_value_get_string(value) ) { - priv->proxy_uri = g_value_dup_string (value); - g_debug("Setting proxy server: %s", priv->proxy_uri); - -#ifndef LIBSOUP22 - GValue val = {0}; - - SoupURI* uri = soup_uri_new(priv->proxy_uri); - g_value_init(&val, SOUP_TYPE_URI); - g_value_take_boxed(&val, uri); - - g_object_set_property(G_OBJECT(priv->soup_session),SOUP_SESSION_PROXY_URI,&val); -#else - SoupUri* uri = soup_uri_new(priv->proxy_uri); - g_object_set(G_OBJECT(priv->soup_session), SOUP_SESSION_PROXY_URI, uri, NULL); #endif - } else - priv->proxy_uri = NULL; - break; - case PROP_TILE_CACHE_DIR: - if ( g_value_get_string(value) ) - priv->cache_dir = g_value_dup_string (value); - break; - case PROP_TILE_CACHE_DIR_IS_FULL_PATH: - priv->cache_dir_is_full_path = g_value_get_boolean (value); - break; - case PROP_ZOOM: - priv->map_zoom = g_value_get_int (value); - break; - case PROP_MAX_ZOOM: - priv->max_zoom = g_value_get_int (value); - break; - case PROP_MIN_ZOOM: - priv->min_zoom = g_value_get_int (value); - break; - case PROP_MAP_X: - priv->map_x = g_value_get_int (value); - priv->center_coord_set = FALSE; - break; - case PROP_MAP_Y: - priv->map_y = g_value_get_int (value); - priv->center_coord_set = FALSE; - break; - case PROP_GPS_TRACK_WIDTH: - priv->ui_gps_track_width = g_value_get_int (value); - break; - case PROP_GPS_POINT_R1: - priv->ui_gps_point_inner_radius = g_value_get_int (value); - break; - case PROP_GPS_POINT_R2: - priv->ui_gps_point_outer_radius = g_value_get_int (value); - break; - case PROP_MAP_SOURCE: - priv->map_source = g_value_get_int (value); - break; - case PROP_IMAGE_FORMAT: - priv->image_format = g_value_dup_string (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} +#ifdef OSD_GPS_BUTTON +/* draw the satellite dish icon in the center of the dpad */ +#define GPS_V0 (D_RAD/7) +#define GPS_V1 (D_RAD/10) +#define GPS_V2 (D_RAD/5) +/* draw a satellite receiver dish */ +/* this is either drawn in the center of the dpad (if present) */ +/* or in the middle of the zoom area */ static void -osm_gps_map_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) -{ - g_return_if_fail (OSM_IS_GPS_MAP (object)); - float lat,lon; - OsmGpsMap *map = OSM_GPS_MAP(object); - OsmGpsMapPrivate *priv = map->priv; - - switch (prop_id) - { - case PROP_AUTO_CENTER: - g_value_set_boolean(value, priv->map_auto_center); - break; - case PROP_RECORD_TRIP_HISTORY: - g_value_set_boolean(value, priv->record_trip_history); - break; - case PROP_SHOW_TRIP_HISTORY: - g_value_set_boolean(value, priv->show_trip_history); - break; - case PROP_AUTO_DOWNLOAD: - g_value_set_boolean(value, priv->map_auto_download); - break; - case PROP_REPO_URI: - g_value_set_string(value, priv->repo_uri); - break; - case PROP_PROXY_URI: - g_value_set_string(value, priv->proxy_uri); - break; - case PROP_TILE_CACHE_DIR: - g_value_set_string(value, priv->cache_dir); - break; - case PROP_TILE_CACHE_DIR_IS_FULL_PATH: - g_value_set_boolean(value, priv->cache_dir_is_full_path); - break; - case PROP_ZOOM: - g_value_set_int(value, priv->map_zoom); - break; - case PROP_MAX_ZOOM: - g_value_set_int(value, priv->max_zoom); - break; - case PROP_MIN_ZOOM: - g_value_set_int(value, priv->min_zoom); - break; - case PROP_LATITUDE: - lat = pixel2lat(priv->map_zoom, - priv->map_y + (GTK_WIDGET(map)->allocation.height / 2)); - g_value_set_float(value, rad2deg(lat)); - break; - case PROP_LONGITUDE: - lon = pixel2lon(priv->map_zoom, - priv->map_x + (GTK_WIDGET(map)->allocation.width / 2)); - g_value_set_float(value, rad2deg(lon)); - break; - case PROP_MAP_X: - g_value_set_int(value, priv->map_x); - break; - case PROP_MAP_Y: - g_value_set_int(value, priv->map_y); - break; - case PROP_TILES_QUEUED: - g_value_set_int(value, g_hash_table_size(priv->tile_queue)); - break; - case PROP_GPS_TRACK_WIDTH: - g_value_set_int(value, priv->ui_gps_track_width); - break; - case PROP_GPS_POINT_R1: - g_value_set_int(value, priv->ui_gps_point_inner_radius); - break; - case PROP_GPS_POINT_R2: - g_value_set_int(value, priv->ui_gps_point_outer_radius); - break; - case PROP_MAP_SOURCE: - g_value_set_int(value, priv->map_source); - break; - case PROP_IMAGE_FORMAT: - g_value_set_string(value, priv->image_format); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} +osd_dpad_gps(cairo_t *cr, gint x, gint y) { + /* move reference to dpad center */ + x += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD * 3; + y += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD + GPS_V0; -static gboolean -osm_gps_map_scroll_event (GtkWidget *widget, GdkEventScroll *event) -{ - OsmGpsMap *map = OSM_GPS_MAP(widget); - OsmGpsMapPrivate *priv = map->priv; + cairo_move_to (cr, x-GPS_V0, y+GPS_V0); + cairo_rel_line_to (cr, +GPS_V0, -GPS_V0); + cairo_rel_line_to (cr, +GPS_V0, +GPS_V0); + cairo_close_path (cr); - if (event->direction == GDK_SCROLL_UP) - { - osm_gps_map_set_zoom(map, priv->map_zoom+1); - } - else - { - osm_gps_map_set_zoom(map, priv->map_zoom-1); - } + cairo_move_to (cr, x+GPS_V1-GPS_V2, y-2*GPS_V2); + cairo_curve_to (cr, x-GPS_V2, y, x+GPS_V1, y+GPS_V1, x+GPS_V1+GPS_V2, y); + cairo_close_path (cr); - return FALSE; + x += GPS_V1; + cairo_move_to (cr, x, y-GPS_V2); + cairo_rel_line_to (cr, +GPS_V1, -GPS_V1); } - -static gboolean -osm_gps_map_button_press (GtkWidget *widget, GdkEventButton *event) -{ - OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget); - -#ifdef ENABLE_BALLOON - /* don't drag if the user clicked within the balloon */ - if (osm_gps_map_in_balloon(priv, - event->x + EXTRA_BORDER, - event->y + EXTRA_BORDER)) - { - priv->drag_counter = -1; - return FALSE; - } #endif -#ifdef ENABLE_OSD - #define SCROLL_STEP 10 - - /* pressed inside OSD control? */ - osd_button_t but = osm_gps_map_osd_check(event->x, event->y); - if(but != OSD_NONE) - { - priv->drag_counter = -1; - - switch(but) { - case OSD_GPS: - priv->osd.cb(priv->osd.data); - break; - - case OSD_UP: - priv->map_y -= GTK_WIDGET(widget)->allocation.height/SCROLL_STEP; - priv->center_coord_set = FALSE; - break; - - case OSD_DOWN: - priv->map_y += GTK_WIDGET(widget)->allocation.height/SCROLL_STEP; - priv->center_coord_set = FALSE; - break; - - case OSD_LEFT: - priv->map_x -= GTK_WIDGET(widget)->allocation.width/SCROLL_STEP; - priv->center_coord_set = FALSE; - break; - - case OSD_RIGHT: - priv->map_x += GTK_WIDGET(widget)->allocation.width/SCROLL_STEP; - priv->center_coord_set = FALSE; - break; - - case OSD_IN: - osm_gps_map_set_zoom(OSM_GPS_MAP(widget), priv->map_zoom+1); - break; - - case OSD_OUT: - osm_gps_map_set_zoom(OSM_GPS_MAP(widget), priv->map_zoom-1); - break; - - default: - break; - } - - osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget)); - - return FALSE; - } -#endif +#define Z_LEN (2*Z_RAD/3) - priv->drag_counter = 0; - priv->drag_start_mouse_x = (int) event->x; - priv->drag_start_mouse_y = (int) event->y; - priv->drag_start_map_x = priv->map_x; - priv->drag_start_map_y = priv->map_y; +static void +osd_zoom_labels(cairo_t *cr, gint x, gint y) { + cairo_move_to (cr, x + Z_LEFT - Z_LEN, y + Z_MID); + cairo_line_to (cr, x + Z_LEFT + Z_LEN, y + Z_MID); - return FALSE; + cairo_move_to (cr, x + Z_RIGHT, y + Z_MID - Z_LEN); + cairo_line_to (cr, x + Z_RIGHT, y + Z_MID + Z_LEN); + cairo_move_to (cr, x + Z_RIGHT - Z_LEN, y + Z_MID); + cairo_line_to (cr, x + Z_RIGHT + Z_LEN, y + Z_MID); } -static gboolean -osm_gps_map_button_release (GtkWidget *widget, GdkEventButton *event) -{ - OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget); +#ifdef OSD_COORDINATES -#ifdef ENABLE_BALLOON - /* released inside the balloon? */ - if (osm_gps_map_in_balloon(priv, - event->x + EXTRA_BORDER, - event->y + EXTRA_BORDER)) - { - osm_gps_map_handle_balloon_click(OSM_GPS_MAP(widget), - event->x - priv->balloon.rect.x + EXTRA_BORDER, - event->y - priv->balloon.rect.y + EXTRA_BORDER); - } +#ifndef OSD_COORDINATES_FONT_SIZE +#define OSD_COORDINATES_FONT_SIZE 12 #endif - if (priv->dragging) - { - priv->dragging = FALSE; +#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) - priv->map_x = priv->drag_start_map_x; - priv->map_y = priv->drag_start_map_y; +/* these can be overwritten with versions that support */ +/* localization */ +#ifndef OSD_COORDINATES_CHR_N +#define OSD_COORDINATES_CHR_N "N" +#endif +#ifndef OSD_COORDINATES_CHR_S +#define OSD_COORDINATES_CHR_S "S" +#endif +#ifndef OSD_COORDINATES_CHR_E +#define OSD_COORDINATES_CHR_E "E" +#endif +#ifndef OSD_COORDINATES_CHR_W +#define OSD_COORDINATES_CHR_W "W" +#endif - priv->map_x += (priv->drag_start_mouse_x - (int) event->x); - priv->map_y += (priv->drag_start_mouse_y - (int) event->y); - priv->center_coord_set = FALSE; - osm_gps_map_map_redraw_idle(OSM_GPS_MAP(widget)); +/* this is the classic geocaching notation */ +static char +*osd_latitude_str(float latitude) { + char *c = OSD_COORDINATES_CHR_N; + float integral, fractional; + + if(isnan(latitude)) + return NULL; + + if(latitude < 0) { + latitude = fabs(latitude); + c = OSD_COORDINATES_CHR_S; } - priv->drag_mouse_dx = 0; - priv->drag_mouse_dy = 0; - priv->drag_counter = -1; - - return FALSE; + fractional = modff(latitude, &integral); + + return g_strdup_printf("%s %02d° %06.3f'", + c, (int)integral, fractional*60.0); } -static gboolean -osm_gps_map_expose (GtkWidget *widget, GdkEventExpose *event); - -static gboolean -osm_gps_map_motion_notify (GtkWidget *widget, GdkEventMotion *event) -{ - int x, y; - GdkModifierType state; - OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget); - - if (event->is_hint) - gdk_window_get_pointer (event->window, &x, &y, &state); - else - { - x = event->x; - y = event->y; - state = event->state; +static char +*osd_longitude_str(float longitude) { + char *c = OSD_COORDINATES_CHR_E; + float integral, fractional; + + if(isnan(longitude)) + return NULL; + + if(longitude < 0) { + longitude = fabs(longitude); + c = OSD_COORDINATES_CHR_W; } - // are we being dragged - if (!(state & GDK_BUTTON1_MASK)) - return FALSE; + fractional = modff(longitude, &integral); + + return g_strdup_printf("%s %03d° %06.3f'", + c, (int)integral, fractional*60.0); +} - if (priv->drag_counter < 0) - return FALSE; +static void +osd_render_coordinates(osm_gps_map_osd_t *osd) +{ + osd_priv_t *priv = (osd_priv_t*)osd->priv; - priv->drag_counter++; + /* get current map position */ + gfloat lat, lon; + g_object_get(osd->widget, "latitude", &lat, "longitude", &lon, NULL); + + /* check if position has changed enough to require redraw */ + if(!isnan(priv->coordinates.lat) && !isnan(priv->coordinates.lon)) + /* 1/60000 == 1/1000 minute */ + if((fabsf(lat - priv->coordinates.lat) < 1/60000) && + (fabsf(lon - priv->coordinates.lon) < 1/60000)) + return; - // we havent dragged more than 6 pixels - if (priv->drag_counter < 6) - return FALSE; + priv->coordinates.lat = lat; + priv->coordinates.lon = lon; + + /* first fill with transparency */ + cairo_t *cr = cairo_create(priv->coordinates.surface); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + // cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.5); + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0); + cairo_paint(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); -#ifdef USE_MAEMO - /* reduce update frequency on maemo to keep screen update fluid */ - static guint32 last_time = 0; + cairo_select_font_face (cr, "Sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, OSD_COORDINATES_FONT_SIZE); - if(event->time - last_time < 100) return FALSE; - last_time = event->time; -#endif + 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); + + 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); - priv->dragging = TRUE; + 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); - if (priv->map_auto_center) - g_object_set(G_OBJECT(widget), "auto-center", FALSE, NULL); + g_free(latitude); + g_free(longitude); - priv->drag_mouse_dx = x - priv->drag_start_mouse_x; - priv->drag_mouse_dy = y - priv->drag_start_mouse_y; + cairo_destroy(cr); +} +#endif // OSD_COORDINATES -#ifdef ENABLE_OSD - /* undo OSD */ - osm_gps_map_osd_restore (OSM_GPS_MAP(widget)); +#ifdef OSD_CROSSHAIR - /* draw new OSD */ - osm_gps_map_osd_draw_controls (OSM_GPS_MAP(widget), - -priv->drag_mouse_dx, - -priv->drag_mouse_dy); +#ifndef OSD_CROSSHAIR_RADIUS +#define OSD_CROSSHAIR_RADIUS 10 #endif - osm_gps_map_expose (widget, NULL); +#define OSD_CROSSHAIR_TICK (OSD_CROSSHAIR_RADIUS/2) +#define OSD_CROSSHAIR_BORDER (OSD_CROSSHAIR_TICK + OSD_CROSSHAIR_RADIUS/4) +#define OSD_CROSSHAIR_W ((OSD_CROSSHAIR_RADIUS+OSD_CROSSHAIR_BORDER)*2) +#define OSD_CROSSHAIR_H ((OSD_CROSSHAIR_RADIUS+OSD_CROSSHAIR_BORDER)*2) +static void +osd_render_crosshair_shape(cairo_t *cr) { + cairo_arc (cr, OSD_CROSSHAIR_W/2, OSD_CROSSHAIR_H/2, + OSD_CROSSHAIR_RADIUS, 0, 2*M_PI); + + cairo_move_to (cr, OSD_CROSSHAIR_W/2 - OSD_CROSSHAIR_RADIUS, + OSD_CROSSHAIR_H/2); + cairo_rel_line_to (cr, -OSD_CROSSHAIR_TICK, 0); + cairo_move_to (cr, OSD_CROSSHAIR_W/2 + OSD_CROSSHAIR_RADIUS, + OSD_CROSSHAIR_H/2); + cairo_rel_line_to (cr, OSD_CROSSHAIR_TICK, 0); + + cairo_move_to (cr, OSD_CROSSHAIR_W/2, + OSD_CROSSHAIR_H/2 - OSD_CROSSHAIR_RADIUS); + cairo_rel_line_to (cr, 0, -OSD_CROSSHAIR_TICK); + cairo_move_to (cr, OSD_CROSSHAIR_W/2, + OSD_CROSSHAIR_H/2 + OSD_CROSSHAIR_RADIUS); + cairo_rel_line_to (cr, 0, OSD_CROSSHAIR_TICK); - return FALSE; + cairo_stroke (cr); } -static gboolean -osm_gps_map_configure (GtkWidget *widget, GdkEventConfigure *event) +static void +osd_render_crosshair(osm_gps_map_osd_t *osd) { - OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget); + osd_priv_t *priv = (osd_priv_t*)osd->priv; - /* create pixmap */ - if (priv->pixmap) - g_object_unref (priv->pixmap); - - priv->pixmap = gdk_pixmap_new ( - widget->window, - widget->allocation.width + EXTRA_BORDER * 2, - widget->allocation.height + EXTRA_BORDER * 2, - -1); + if(priv->crosshair.rendered) + return; - /* and gc, used for clipping (I think......) */ - if(priv->gc_map) - g_object_unref(priv->gc_map); + priv->crosshair.rendered = TRUE; - priv->gc_map = gdk_gc_new(priv->pixmap); + /* first fill with transparency */ + cairo_t *cr = cairo_create(priv->crosshair.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); - osm_gps_map_map_redraw(OSM_GPS_MAP(widget)); + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + + cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.5); + cairo_set_line_width (cr, OSD_CROSSHAIR_RADIUS/2); + osd_render_crosshair_shape(cr); + + cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5); + cairo_set_line_width (cr, OSD_CROSSHAIR_RADIUS/4); + osd_render_crosshair_shape(cr); - return FALSE; + cairo_destroy(cr); } +#endif -static gboolean -osm_gps_map_expose (GtkWidget *widget, GdkEventExpose *event) -{ - OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget); +#ifdef OSD_SCALE - gdk_draw_drawable ( - widget->window, - widget->style->fg_gc[GTK_WIDGET_STATE (widget)], - priv->pixmap, - 0,0, - priv->drag_mouse_dx - EXTRA_BORDER, - priv->drag_mouse_dy - EXTRA_BORDER, - -1,-1); - - //Paint white outside of the map if dragging. Its less - //ugly than painting the corrupted map - if(priv->drag_mouse_dx>EXTRA_BORDER) { - gdk_draw_rectangle ( - widget->window, - widget->style->white_gc, - TRUE, - 0, 0, - priv->drag_mouse_dx - EXTRA_BORDER, - widget->allocation.height); - } - else if (-priv->drag_mouse_dx > EXTRA_BORDER) - { - gdk_draw_rectangle ( - widget->window, - widget->style->white_gc, - TRUE, - priv->drag_mouse_dx + widget->allocation.width + EXTRA_BORDER, 0, - -priv->drag_mouse_dx - EXTRA_BORDER, - widget->allocation.height); - } - - if (priv->drag_mouse_dy>EXTRA_BORDER) { - gdk_draw_rectangle ( - widget->window, - widget->style->white_gc, - TRUE, - 0, 0, - widget->allocation.width, - priv->drag_mouse_dy - EXTRA_BORDER); - } - else if (-priv->drag_mouse_dy > EXTRA_BORDER) - { - gdk_draw_rectangle ( - widget->window, - widget->style->white_gc, - TRUE, - 0, priv->drag_mouse_dy + widget->allocation.height + EXTRA_BORDER, - widget->allocation.width, - -priv->drag_mouse_dy - EXTRA_BORDER); - } -#if 0 - if(!priv->dragging) - gdk_draw_drawable ( - widget->window, - widget->style->fg_gc[GTK_WIDGET_STATE (widget)], - priv->pixmap, - event->area.x + EXTRA_BORDER, - event->area.y + EXTRA_BORDER, - event->area.x, event->area.y, - event->area.width, event->area.height); +#ifndef OSD_SCALE_FONT_SIZE +#define OSD_SCALE_FONT_SIZE 12 #endif - return FALSE; -} +#define OSD_SCALE_W (10*OSD_SCALE_FONT_SIZE) +#define OSD_SCALE_H (5*OSD_SCALE_FONT_SIZE/2) + +/* various parameters used to create the scale */ +#define OSD_SCALE_H2 (OSD_SCALE_H/2) +#define OSD_SCALE_TICK (2*OSD_SCALE_FONT_SIZE/3) +#define OSD_SCALE_M (OSD_SCALE_H2 - OSD_SCALE_TICK) +#define OSD_SCALE_I (OSD_SCALE_H2 + OSD_SCALE_TICK) +#define OSD_SCALE_FD (OSD_SCALE_FONT_SIZE/4) static void -osm_gps_map_class_init (OsmGpsMapClass *klass) +osd_render_scale(osm_gps_map_osd_t *osd) { - GObjectClass* object_class = G_OBJECT_CLASS (klass); - GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + osd_priv_t *priv = (osd_priv_t*)osd->priv; - g_type_class_add_private (klass, sizeof (OsmGpsMapPrivate)); + /* this only needs to be rendered if the zoom has changed */ + gint zoom; + g_object_get(OSM_GPS_MAP(osd->widget), "zoom", &zoom, NULL); + if(zoom == priv->scale.zoom) + return; - object_class->dispose = osm_gps_map_dispose; - object_class->finalize = osm_gps_map_finalize; - object_class->constructor = osm_gps_map_constructor; - object_class->set_property = osm_gps_map_set_property; - object_class->get_property = osm_gps_map_get_property; - - widget_class->expose_event = osm_gps_map_expose; - widget_class->configure_event = osm_gps_map_configure; - widget_class->button_press_event = osm_gps_map_button_press; - widget_class->button_release_event = osm_gps_map_button_release; - widget_class->motion_notify_event = osm_gps_map_motion_notify; - widget_class->scroll_event = osm_gps_map_scroll_event; - - g_object_class_install_property (object_class, - PROP_AUTO_CENTER, - g_param_spec_boolean ("auto-center", - "auto center", - "map auto center", - TRUE, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)); - - g_object_class_install_property (object_class, - PROP_RECORD_TRIP_HISTORY, - g_param_spec_boolean ("record-trip-history", - "record trip history", - "should all gps points be recorded in a trip history", - TRUE, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)); - - g_object_class_install_property (object_class, - PROP_SHOW_TRIP_HISTORY, - g_param_spec_boolean ("show-trip-history", - "show trip history", - "should the recorded trip history be shown on the map", - TRUE, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)); - - g_object_class_install_property (object_class, - PROP_AUTO_DOWNLOAD, - g_param_spec_boolean ("auto-download", - "auto download", - "map auto download", - TRUE, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)); - - g_object_class_install_property (object_class, - PROP_REPO_URI, - g_param_spec_string ("repo-uri", - "repo uri", - "map source tile repository uri", - OSM_REPO_URI, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (object_class, - PROP_PROXY_URI, - g_param_spec_string ("proxy-uri", - "proxy uri", - "http proxy uri on NULL", - NULL, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (object_class, - PROP_TILE_CACHE_DIR, - g_param_spec_string ("tile-cache", - "tile cache", - "osm local tile cache dir", - NULL, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (object_class, - PROP_TILE_CACHE_DIR_IS_FULL_PATH, - g_param_spec_boolean ("tile-cache-is-full-path", - "tile cache is full path", - "if true, the path passed to tile-cache is interpreted as the full cache path", - FALSE, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (object_class, - PROP_ZOOM, - g_param_spec_int ("zoom", - "zoom", - "zoom level", - MIN_ZOOM, /* minimum property value */ - MAX_ZOOM, /* maximum property value */ - 3, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (object_class, - PROP_MAX_ZOOM, - g_param_spec_int ("max-zoom", - "max zoom", - "maximum zoom level", - MIN_ZOOM, /* minimum property value */ - MAX_ZOOM, /* maximum property value */ - OSM_MAX_ZOOM, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (object_class, - PROP_MIN_ZOOM, - g_param_spec_int ("min-zoom", - "min zoom", - "minimum zoom level", - MIN_ZOOM, /* minimum property value */ - MAX_ZOOM, /* maximum property value */ - OSM_MIN_ZOOM, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (object_class, - PROP_LATITUDE, - g_param_spec_float ("latitude", - "latitude", - "latitude in degrees", - -90.0, /* minimum property value */ - 90.0, /* maximum property value */ - 0, - G_PARAM_READABLE)); - - g_object_class_install_property (object_class, - PROP_LONGITUDE, - g_param_spec_float ("longitude", - "longitude", - "longitude in degrees", - -180.0, /* minimum property value */ - 180.0, /* maximum property value */ - 0, - G_PARAM_READABLE)); - - g_object_class_install_property (object_class, - PROP_MAP_X, - g_param_spec_int ("map-x", - "map-x", - "initial map x location", - G_MININT, /* minimum property value */ - G_MAXINT, /* maximum property value */ - 890, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (object_class, - PROP_MAP_Y, - g_param_spec_int ("map-y", - "map-y", - "initial map y location", - G_MININT, /* minimum property value */ - G_MAXINT, /* maximum property value */ - 515, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (object_class, - PROP_TILES_QUEUED, - g_param_spec_int ("tiles-queued", - "tiles-queued", - "number of tiles currently waiting to download", - G_MININT, /* minimum property value */ - G_MAXINT, /* maximum property value */ - 0, - G_PARAM_READABLE)); - - g_object_class_install_property (object_class, - PROP_GPS_TRACK_WIDTH, - g_param_spec_int ("gps-track-width", - "gps-track-width", - "width of the lines drawn for the gps track", - 1, /* minimum property value */ - G_MAXINT, /* maximum property value */ - 4, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)); - - g_object_class_install_property (object_class, - PROP_GPS_POINT_R1, - g_param_spec_int ("gps-track-point-radius", - "gps-track-point-radius", - "radius of the gps point inner circle", - 0, /* minimum property value */ - G_MAXINT, /* maximum property value */ - 10, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)); - - g_object_class_install_property (object_class, - PROP_GPS_POINT_R2, - g_param_spec_int ("gps-track-highlight-radius", - "gps-track-highlight-radius", - "radius of the gps point highlight circle", - 0, /* minimum property value */ - G_MAXINT, /* maximum property value */ - 20, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT)); - - g_object_class_install_property (object_class, - PROP_MAP_SOURCE, - g_param_spec_int ("map-source", - "map source", - "map source ID", - -1, /* minimum property value */ - G_MAXINT, /* maximum property value */ - -1, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); - - g_object_class_install_property (object_class, - PROP_IMAGE_FORMAT, - g_param_spec_string ("image-format", - "image format", - "map source tile repository image format (jpg, png)", - OSM_IMAGE_FORMAT, - G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY)); -} + priv->scale.zoom = zoom; -const char* -osm_gps_map_source_get_friendly_name(OsmGpsMapSource_t source) -{ - switch(source) - { - case OSM_GPS_MAP_SOURCE_NULL: - return "None"; - case OSM_GPS_MAP_SOURCE_OPENSTREETMAP: - return "OpenStreetMap"; - case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER: - return "OpenStreetMap Renderer"; - case OSM_GPS_MAP_SOURCE_OPENAERIALMAP: - return "OpenAerialMap"; - case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE: - return "Maps-For-Free"; - case OSM_GPS_MAP_SOURCE_GOOGLE_STREET: - return "Google Maps"; - case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE: - return "Google Satellite"; - case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID: - return "Google Hybrid"; - case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET: - return "Virtual Earth"; - case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE: - return "Virtual Earth Satellite"; - case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID: - return "Virtual Earth Hybrid"; - case OSM_GPS_MAP_SOURCE_YAHOO_STREET: - return "Yahoo Maps"; - case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE: - return "Yahoo Satellite"; - case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID: - return "Yahoo Hybrid"; - default: - return NULL; - } - return NULL; -} - -//http://www.internettablettalk.com/forums/showthread.php?t=5209 -//https://garage.maemo.org/plugins/scmsvn/viewcvs.php/trunk/src/maps.c?root=maemo-mapper&view=markup -//http://www.ponies.me.uk/maps/GoogleTileUtils.java -//http://www.mgmaps.com/cache/MapTileCacher.perl -const char* -osm_gps_map_source_get_repo_uri(OsmGpsMapSource_t source) -{ - switch(source) - { - case OSM_GPS_MAP_SOURCE_NULL: - return "none://"; - case OSM_GPS_MAP_SOURCE_OPENSTREETMAP: - return OSM_REPO_URI; - case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER: - return "http://tah.openstreetmap.org/Tiles/tile/#Z/#X/#Y.png"; - case OSM_GPS_MAP_SOURCE_OPENAERIALMAP: - return "http://tile.openaerialmap.org/tiles/1.0.0/openaerialmap-900913/#Z/#X/#Y.jpg"; - case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE: - return "http://maps-for-free.com/layer/relief/z#Z/row#Y/#Z_#X-#Y.jpg"; - case OSM_GPS_MAP_SOURCE_GOOGLE_STREET: - return "http://mt#R.google.com/vt/v=w2.97&x=#X&y=#Y&z=#Z"; - case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE: - return "http://khm#R.google.com/kh?n=404&v=3&t=#Q"; - case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID: - return NULL; /* No longer working "http://mt#R.google.com/mt?n=404&v=w2t.99&x=#X&y=#Y&zoom=#S" */ - case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET: - return "http://a#R.ortho.tiles.virtualearth.net/tiles/r#W.jpeg?g=50"; - case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE: - return "http://a#R.ortho.tiles.virtualearth.net/tiles/a#W.jpeg?g=50"; - case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID: - return "http://a#R.ortho.tiles.virtualearth.net/tiles/h#W.jpeg?g=50"; - case OSM_GPS_MAP_SOURCE_YAHOO_STREET: - case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE: - case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID: - /* TODO: Implement signed Y, aka U - * http://us.maps3.yimg.com/aerial.maps.yimg.com/ximg?v=1.7&t=a&s=256&x=%d&y=%-d&z=%d - * x = tilex, - * y = (1 << (MAX_ZOOM - zoom)) - tiley - 1, - * z = zoom - (MAX_ZOOM - 17)); - */ - return NULL; - default: - return NULL; - } - return NULL; -} + float m_per_pix = osm_gps_map_get_scale(OSM_GPS_MAP(osd->widget)); -const char * -osm_gps_map_source_get_image_format(OsmGpsMapSource_t source) -{ - switch(source) { - case OSM_GPS_MAP_SOURCE_NULL: - case OSM_GPS_MAP_SOURCE_OPENSTREETMAP: - case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER: - return "png"; - case OSM_GPS_MAP_SOURCE_OPENAERIALMAP: - case OSM_GPS_MAP_SOURCE_GOOGLE_STREET: - case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID: - case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET: - case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE: - case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID: - case OSM_GPS_MAP_SOURCE_YAHOO_STREET: - case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE: - case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID: - case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE: - case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE: - return "jpg"; - default: - return "bin"; + /* first fill with transparency */ + cairo_t *cr = cairo_create(priv->scale.surface); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0); + // pink for testing: cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.2); + cairo_paint(cr); + cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + + /* determine the size of the scale width in meters */ + float width = (OSD_SCALE_W-OSD_SCALE_FONT_SIZE/6) * m_per_pix; + + /* scale this to useful values */ + int exp = logf(width)*M_LOG10E; + int mant = width/pow(10,exp); + int width_metric = mant * pow(10,exp); + char *dist_str = NULL; + if(width_metric<1000) + dist_str = g_strdup_printf("%u m", width_metric); + else + dist_str = g_strdup_printf("%u km", width_metric/1000); + width_metric /= m_per_pix; + + /* and now the hard part: scale for useful imperial values :-( */ + /* try to convert to feet, 1ft == 0.3048 m */ + width /= 0.3048; + float imp_scale = 0.3048; + char *dist_imp_unit = "ft"; + + if(width >= 100) { + /* 1yd == 3 feet */ + width /= 3.0; + imp_scale *= 3.0; + dist_imp_unit = "yd"; + + if(width >= 1760.0) { + /* 1mi == 1760 yd */ + width /= 1760.0; + imp_scale *= 1760.0; + dist_imp_unit = "mi"; + } } - return "bin"; -} + /* also convert this to full tens/hundreds */ + exp = logf(width)*M_LOG10E; + mant = width/pow(10,exp); + int width_imp = mant * pow(10,exp); + char *dist_str_imp = g_strdup_printf("%u %s", width_imp, dist_imp_unit); + + /* convert back to pixels */ + width_imp *= imp_scale; + width_imp /= m_per_pix; + + cairo_select_font_face (cr, "Sans", + CAIRO_FONT_SLANT_NORMAL, + CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size (cr, OSD_SCALE_FONT_SIZE); + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); + + cairo_text_extents_t extents; + cairo_text_extents (cr, dist_str, &extents); + + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/6); + cairo_move_to (cr, 2*OSD_SCALE_FD, OSD_SCALE_H2-OSD_SCALE_FD); + cairo_text_path (cr, dist_str); + cairo_stroke (cr); + cairo_move_to (cr, 2*OSD_SCALE_FD, + OSD_SCALE_H2+OSD_SCALE_FD + extents.height); + cairo_text_path (cr, dist_str_imp); + cairo_stroke (cr); -int -osm_gps_map_source_get_min_zoom(OsmGpsMapSource_t source) -{ - return 1; -} + cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); + cairo_move_to (cr, 2*OSD_SCALE_FD, OSD_SCALE_H2-OSD_SCALE_FD); + cairo_show_text (cr, dist_str); + cairo_move_to (cr, 2*OSD_SCALE_FD, + OSD_SCALE_H2+OSD_SCALE_FD + extents.height); + cairo_show_text (cr, dist_str_imp); + + g_free(dist_str); + g_free(dist_str_imp); + + /* draw white line */ + cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); + cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0); + cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/3); + cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_M); + cairo_rel_line_to (cr, 0, OSD_SCALE_TICK); + cairo_rel_line_to (cr, width_metric, 0); + cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK); + cairo_stroke(cr); + cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_I); + cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK); + cairo_rel_line_to (cr, width_imp, 0); + cairo_rel_line_to (cr, 0, +OSD_SCALE_TICK); + cairo_stroke(cr); + + /* draw black line */ + cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0); + cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/6); + cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_M); + cairo_rel_line_to (cr, 0, OSD_SCALE_TICK); + cairo_rel_line_to (cr, width_metric, 0); + cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK); + cairo_stroke(cr); + cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_I); + cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK); + cairo_rel_line_to (cr, width_imp, 0); + cairo_rel_line_to (cr, 0, +OSD_SCALE_TICK); + cairo_stroke(cr); -int -osm_gps_map_source_get_max_zoom(OsmGpsMapSource_t source) -{ - switch(source) { - case OSM_GPS_MAP_SOURCE_NULL: - return 18; - case OSM_GPS_MAP_SOURCE_OPENSTREETMAP: - return OSM_MAX_ZOOM; - case OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER: - case OSM_GPS_MAP_SOURCE_OPENAERIALMAP: - case OSM_GPS_MAP_SOURCE_GOOGLE_STREET: - case OSM_GPS_MAP_SOURCE_GOOGLE_HYBRID: - case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET: - case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE: - case OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID: - case OSM_GPS_MAP_SOURCE_YAHOO_STREET: - case OSM_GPS_MAP_SOURCE_YAHOO_SATELLITE: - case OSM_GPS_MAP_SOURCE_YAHOO_HYBRID: - return 17; - case OSM_GPS_MAP_SOURCE_MAPS_FOR_FREE: - return 11; - case OSM_GPS_MAP_SOURCE_GOOGLE_SATELLITE: - return 18; - default: - return 17; - } - return 17; + cairo_destroy(cr); } +#endif -void -osm_gps_map_download_maps (OsmGpsMap *map, coord_t *pt1, coord_t *pt2, int zoom_start, int zoom_end) +static void +osd_render_controls(osm_gps_map_osd_t *osd) { - int i,j,zoom,num_tiles; - OsmGpsMapPrivate *priv = map->priv; + osd_priv_t *priv = (osd_priv_t*)osd->priv; - if (pt1 && pt2) - { - gchar *filename; - num_tiles = 0; - zoom_end = CLAMP(zoom_end, priv->min_zoom, priv->max_zoom); - g_debug("Download maps: z:%d->%d",zoom_start, zoom_end); - - for(zoom=zoom_start; zoom<=zoom_end; zoom++) - { - int x1,y1,x2,y2; - - x1 = (int)floor((float)lon2pixel(zoom, pt1->rlon) / (float)TILESIZE); - y1 = (int)floor((float)lat2pixel(zoom, pt1->rlat) / (float)TILESIZE); - - x2 = (int)floor((float)lon2pixel(zoom, pt2->rlon) / (float)TILESIZE); - y2 = (int)floor((float)lat2pixel(zoom, pt2->rlat) / (float)TILESIZE); - - // loop x1-x2 - for(i=x1; i<=x2; i++) - { - // loop y1 - y2 - for(j=y1; j<=y2; j++) - { - // x = i, y = j - filename = g_strdup_printf("%s%c%d%c%d%c%d.%s", - priv->cache_dir, G_DIR_SEPARATOR, - zoom, G_DIR_SEPARATOR, - i, G_DIR_SEPARATOR, - j, - priv->image_format); - if (!g_file_test(filename, G_FILE_TEST_EXISTS)) - { - osm_gps_map_download_tile(map, zoom, i, j, FALSE); - num_tiles++; - } - g_free(filename); - } - } - g_debug("DL @Z:%d = %d tiles",zoom,num_tiles); - } - } -} + if(priv->controls.rendered +#ifdef OSD_GPS_BUTTON + && (priv->controls.gps_enabled == (osd->cb != NULL)) +#endif + ) + return; -void -osm_gps_map_get_bbox (OsmGpsMap *map, coord_t *pt1, coord_t *pt2) -{ - OsmGpsMapPrivate *priv = map->priv; +#ifdef OSD_GPS_BUTTON + priv->controls.gps_enabled = (osd->cb != NULL); +#endif + priv->controls.rendered = TRUE; - if (pt1 && pt2) { - pt1->rlat = pixel2lat(priv->map_zoom, priv->map_y); - pt1->rlon = pixel2lon(priv->map_zoom, priv->map_x); - pt2->rlat = pixel2lat(priv->map_zoom, priv->map_y + GTK_WIDGET(map)->allocation.height); - pt2->rlon = pixel2lon(priv->map_zoom, priv->map_x + GTK_WIDGET(map)->allocation.width); +#ifndef OSD_COLOR + GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL]; + GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL]; + GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE]; +#endif - g_debug("BBOX: %f %f %f %f", pt1->rlat, pt1->rlon, pt2->rlat, pt2->rlon); - } -} + /* first fill with transparency */ + cairo_t *cr = cairo_create(priv->controls.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); -void -osm_gps_map_set_mapcenter (OsmGpsMap *map, float latitude, float longitude, int zoom) -{ - osm_gps_map_set_center (map, latitude, longitude); - osm_gps_map_set_zoom (map, zoom); -} + /* --------- draw zoom and dpad shape shadow ----------- */ +#ifdef OSD_SHADOW_ENABLE + osd_zoom_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW); + osd_shape_shadow(cr); +#ifndef OSD_NO_DPAD + osd_dpad_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW); + osd_shape_shadow(cr); +#endif +#endif -void -osm_gps_map_set_center (OsmGpsMap *map, float latitude, float longitude) -{ - int pixel_x, pixel_y; - OsmGpsMapPrivate *priv; + /* --------- draw zoom and dpad shape ----------- */ - g_return_if_fail (OSM_IS_GPS_MAP (map)); - priv = map->priv; + osd_zoom_shape(cr, 1, 1); +#ifndef OSD_COLOR + osd_shape(cr, &bg, &fg); +#else + osd_shape(cr); +#endif +#ifndef OSD_NO_DPAD + osd_dpad_shape(cr, 1, 1); +#ifndef OSD_COLOR + osd_shape(cr, &bg, &fg); +#else + osd_shape(cr); +#endif +#endif - priv->center_rlat = deg2rad(latitude); - priv->center_rlon = deg2rad(longitude); - priv->center_coord_set = TRUE; + /* --------- draw zoom and dpad labels --------- */ - // pixel_x,y, offsets - pixel_x = lon2pixel(priv->map_zoom, priv->center_rlon); - pixel_y = lat2pixel(priv->map_zoom, priv->center_rlat); +#ifdef OSD_SHADOW_ENABLE + osd_labels_shadow(cr, Z_RAD/3, TRUE); + osd_zoom_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW); +#ifndef OSD_NO_DPAD + osd_dpad_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW); +#endif + cairo_stroke(cr); +#ifdef OSD_GPS_BUTTON + osd_labels_shadow(cr, Z_RAD/6, osd->cb != NULL); + osd_dpad_gps(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW); + cairo_stroke(cr); +#endif +#endif - priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2; - priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2; +#ifndef OSD_COLOR + osd_labels(cr, Z_RAD/3, TRUE, &fg, &da); +#else + osd_labels(cr, Z_RAD/3, TRUE); +#endif + osd_zoom_labels(cr, 1, 1); +#ifndef OSD_NO_DPAD + osd_dpad_labels(cr, 1, 1); +#endif + cairo_stroke(cr); - osm_gps_map_map_redraw_idle(map); +#ifndef OSD_COLOR + osd_labels(cr, Z_RAD/6, osd->cb != NULL, &fg, &da); +#else + osd_labels(cr, Z_RAD/6, osd->cb != NULL); +#endif +#ifdef OSD_GPS_BUTTON + osd_dpad_gps(cr, 1, 1); +#endif + cairo_stroke(cr); + + cairo_destroy(cr); } -int -osm_gps_map_set_zoom (OsmGpsMap *map, int zoom) +static void +osd_render(osm_gps_map_osd_t *osd) { - int zoom_old; - double factor = 0.0; - int width_center, height_center; - OsmGpsMapPrivate *priv; + /* this function is actually called pretty often since the */ + /* OSD contents may have changed (due to a coordinate/zoom change). */ + /* The different OSD parts have to make sure that they don't */ + /* render unneccessarily often and thus waste CPU power */ - g_return_val_if_fail (OSM_IS_GPS_MAP (map), 0); - priv = map->priv; + osd_render_controls(osd); - if (zoom != priv->map_zoom) - { - width_center = GTK_WIDGET(map)->allocation.width / 2; - height_center = GTK_WIDGET(map)->allocation.height / 2; +#ifdef OSD_SOURCE_SEL + osd_render_source_sel(osd, FALSE); +#endif - zoom_old = priv->map_zoom; - //constrain zoom min_zoom -> max_zoom - priv->map_zoom = CLAMP(zoom, priv->min_zoom, priv->max_zoom); - - if (priv->center_coord_set) - { - priv->map_x = lon2pixel(priv->map_zoom, priv->center_rlon) - width_center; - priv->map_y = lat2pixel(priv->map_zoom, priv->center_rlat) - height_center; - } - else - { - factor = exp(priv->map_zoom * M_LN2)/exp(zoom_old * M_LN2); - priv->map_x = ((priv->map_x + width_center) * factor) - width_center; - priv->map_y = ((priv->map_y + height_center) * factor) - height_center; - } +#ifdef OSD_SCALE + osd_render_scale(osd); +#endif - g_debug("Zoom changed from %d to %d factor:%f x:%d", - zoom_old, priv->map_zoom, factor, priv->map_x); +#ifdef OSD_CROSSHAIR + osd_render_crosshair(osd); +#endif - osm_gps_map_map_redraw_idle(map); - } - return priv->map_zoom; +#ifdef OSD_COORDINATES + osd_render_coordinates(osd); +#endif } -void -osm_gps_map_add_track (OsmGpsMap *map, GSList *track) +static void +osd_draw(osm_gps_map_osd_t *osd, GdkDrawable *drawable) { - OsmGpsMapPrivate *priv; - - g_return_if_fail (OSM_IS_GPS_MAP (map)); - priv = map->priv; - - if (track) { - priv->tracks = g_slist_append(priv->tracks, track); - osm_gps_map_map_redraw_idle(map); - } -} + osd_priv_t *priv = (osd_priv_t*)osd->priv; -void -osm_gps_map_clear_tracks (OsmGpsMap *map) -{ - g_return_if_fail (OSM_IS_GPS_MAP (map)); + /* OSD itself uses some off-screen rendering, so check if the */ + /* offscreen buffer is present and create it if not */ + if(!priv->controls.surface) { + /* create overlay ... */ + priv->controls.surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W+2, OSD_H+2); - osm_gps_map_free_tracks(map); - osm_gps_map_map_redraw_idle(map); -} + priv->controls.rendered = FALSE; +#ifdef OSD_GPS_BUTTON + priv->controls.gps_enabled = FALSE; +#endif -void -osm_gps_map_add_image (OsmGpsMap *map, float latitude, float longitude, GdkPixbuf *image) -{ - g_return_if_fail (OSM_IS_GPS_MAP (map)); +#ifdef OSD_SOURCE_SEL + /* the initial OSD state is alway not-expanded */ + priv->source_sel.surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + OSD_S_W+2, OSD_S_H+2); + priv->source_sel.rendered = FALSE; +#endif - if (image) { - OsmGpsMapPrivate *priv = map->priv; - image_t *im; +#ifdef OSD_SCALE + priv->scale.surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + OSD_SCALE_W, OSD_SCALE_H); + priv->scale.zoom = -1; +#endif - //cache w/h for speed, and add image to list - im = g_new0(image_t,1); - im->w = gdk_pixbuf_get_width(image); - im->h = gdk_pixbuf_get_height(image); - im->pt.rlat = deg2rad(latitude); - im->pt.rlon = deg2rad(longitude); +#ifdef OSD_CROSSHAIR + priv->crosshair.surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + OSD_CROSSHAIR_W, OSD_CROSSHAIR_H); + priv->crosshair.rendered = FALSE; +#endif - g_object_ref(image); - im->image = image; +#ifdef OSD_COORDINATES + priv->coordinates.surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, + OSD_COORDINATES_W, OSD_COORDINATES_H); - priv->images = g_slist_append(priv->images, im); + priv->coordinates.lat = priv->coordinates.lon = OSM_GPS_MAP_INVALID; +#endif - osm_gps_map_map_redraw_idle(map); + /* ... and render it */ + osd_render(osd); } -} -gboolean -osm_gps_map_remove_image (OsmGpsMap *map, GdkPixbuf *image) -{ - OsmGpsMapPrivate *priv = map->priv; - if (priv->images) { - GSList *list; - for(list = priv->images; list != NULL; list = list->next) - { - image_t *im = list->data; - if (im->image == image) - { - priv->images = g_slist_remove_link(priv->images, list); - g_object_unref(im->image); - g_free(im); - osm_gps_map_map_redraw_idle(map); - return TRUE; - } - } - } - return FALSE; -} + // now draw this onto the original context + cairo_t *cr = gdk_cairo_create(drawable); -void -osm_gps_map_clear_images (OsmGpsMap *map) -{ - g_return_if_fail (OSM_IS_GPS_MAP (map)); + gint x, y; - osm_gps_map_free_images(map); - osm_gps_map_map_redraw_idle(map); -} +#ifdef OSD_SCALE + x = OSD_X; + y = -OSD_Y; + if(x < 0) x += osd->widget->allocation.width - OSD_SCALE_W; + if(y < 0) y += osd->widget->allocation.height - OSD_SCALE_H; -void -osm_gps_map_osd_speed (OsmGpsMap *map, float speed) -{ - OsmGpsMapPrivate *priv; + cairo_set_source_surface(cr, priv->scale.surface, x, y); + cairo_paint(cr); +#endif - PangoContext *context = NULL; - PangoLayout *layout = NULL; - PangoFontDescription *desc = NULL; - - GdkColor color; - GdkGC *gc; - - gchar *buffer; - //static int x = 10, y = 10; - static int width = 0, height = 0; +#ifdef OSD_CROSSHAIR + x = (osd->widget->allocation.width - OSD_CROSSHAIR_W)/2; + y = (osd->widget->allocation.height - OSD_CROSSHAIR_H)/2; - g_return_if_fail (OSM_IS_GPS_MAP (map)); - priv = map->priv; + cairo_set_source_surface(cr, priv->crosshair.surface, x, y); + cairo_paint(cr); +#endif - buffer = g_strdup_printf("%.0f", speed); +#ifdef OSD_COORDINATES + x = -OSD_X; + y = -OSD_Y; + if(x < 0) x += osd->widget->allocation.width - OSD_COORDINATES_W; + if(y < 0) y += osd->widget->allocation.height - OSD_COORDINATES_H; - /* pango initialisation */ - context = gtk_widget_get_pango_context (GTK_WIDGET(map)); - layout = pango_layout_new (context); - desc = pango_font_description_new(); - - pango_font_description_set_size (desc, 40 * PANGO_SCALE); - pango_layout_set_font_description (layout, desc); - pango_layout_set_text (layout, buffer, strlen(buffer)); - - gc = gdk_gc_new (GTK_WIDGET(map)->window); - - color.red = (0 > 50) ? 0xffff : 0; - color.green = 0; - color.blue = 0; - - gdk_gc_set_rgb_fg_color (gc, &color); - - /* faster / less flicker alternative:*/ - gdk_draw_drawable ( - GTK_WIDGET(map)->window, - GTK_WIDGET(map)->style->fg_gc[GTK_WIDGET_STATE(map)], - priv->pixmap, - 0,0, - 0,0, - width+10,width+10); - - gdk_draw_layout(GTK_WIDGET(map)->window, - gc, - 0, 0, - layout); - - /* set width and height */ - pango_layout_get_pixel_size(layout, &width, &height); - - g_free(buffer); - pango_font_description_free (desc); - g_object_unref (layout); - g_object_unref (gc); -} + cairo_set_source_surface(cr, priv->coordinates.surface, x, y); + cairo_paint(cr); +#endif -void -osm_gps_map_draw_gps (OsmGpsMap *map, float latitude, float longitude, float heading) -{ - int pixel_x, pixel_y; - OsmGpsMapPrivate *priv; +#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); - g_return_if_fail (OSM_IS_GPS_MAP (map)); - priv = map->priv; + /* check if balloon needs to be rerendered */ + osd_render_balloon(osd); - priv->gps->rlat = deg2rad(latitude); - priv->gps->rlon = deg2rad(longitude); - priv->gps_valid = TRUE; - - // pixel_x,y, offsets - pixel_x = lon2pixel(priv->map_zoom, priv->gps->rlon); - pixel_y = lat2pixel(priv->map_zoom, priv->gps->rlat); - - //If trip marker add to list of gps points. - if (priv->record_trip_history) { - coord_t *tp = g_new0(coord_t,1); - tp->rlat = priv->gps->rlat; - tp->rlon = priv->gps->rlon; - priv->trip_history = g_slist_append(priv->trip_history, tp); - } - - // dont draw anything if we are dragging - if (priv->dragging) { - g_debug("Dragging"); - return; + cairo_set_source_surface(cr, priv->balloon.surface, + x + priv->balloon.offset_x, + y + priv->balloon.offset_y); + cairo_paint(cr); } +#endif - //Automatically center the map if the track approaches the edge - if(priv->map_auto_center) { - int x = pixel_x - priv->map_x; - int y = pixel_y - priv->map_y; - int width = GTK_WIDGET(map)->allocation.width; - int height = GTK_WIDGET(map)->allocation.height; - if( x < (width/2 - width/8) || x > (width/2 + width/8) || - y < (height/2 - height/8) || y > (height/2 + height/8)) { - - priv->map_x = pixel_x - GTK_WIDGET(map)->allocation.width/2; - priv->map_y = pixel_y - GTK_WIDGET(map)->allocation.height/2; - priv->center_coord_set = FALSE; - } - } + x = OSD_X; + if(x < 0) + x += osd->widget->allocation.width - OSD_W; - // this redraws the map (including the gps track, and adjusts the - // map center if it was changed - osm_gps_map_map_redraw_idle(map); -} + y = OSD_Y; + if(y < 0) + y += osd->widget->allocation.height - OSD_H; -void -osm_gps_map_clear_gps (OsmGpsMap *map) -{ - osm_gps_map_free_trip(map); - osm_gps_map_map_redraw_idle(map); -} + cairo_set_source_surface(cr, priv->controls.surface, x, y); + cairo_paint(cr); -coord_t -osm_gps_map_get_co_ordinates (OsmGpsMap *map, int pixel_x, int pixel_y) -{ - coord_t coord; - OsmGpsMapPrivate *priv = map->priv; +#ifdef OSD_SOURCE_SEL + if(!priv->source_sel.handler_id) { + /* the OSD source selection is not being animated */ + if(!priv->source_sel.expanded) + x = osd->widget->allocation.width - OSD_S_W; + else + x = osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X; + } else + x = priv->source_sel.shift; + + y = OSD_S_Y; + if(OSD_S_Y < 0) { + if(!priv->source_sel.expanded) + y = osd->widget->allocation.height - OSD_S_H + OSD_S_Y; + else + y = osd->widget->allocation.height - OSD_S_EXP_H + OSD_S_Y; + } - coord.rlat = pixel2lat(priv->map_zoom, priv->map_y + pixel_y); - coord.rlon = pixel2lon(priv->map_zoom, priv->map_x + pixel_x); - return coord; -} + cairo_set_source_surface(cr, priv->source_sel.surface, x, y); + cairo_paint(cr); +#endif -GtkWidget * -osm_gps_map_new (void) -{ - return g_object_new (OSM_TYPE_GPS_MAP, NULL); + cairo_destroy(cr); } -void -osm_gps_map_screen_to_geographic (OsmGpsMap *map, gint pixel_x, gint pixel_y, - gfloat *latitude, gfloat *longitude) +static void +osd_free(osm_gps_map_osd_t *osd) { - OsmGpsMapPrivate *priv; - - g_return_if_fail (OSM_IS_GPS_MAP (map)); - priv = map->priv; + osd_priv_t *priv = (osd_priv_t *)(osd->priv); - if (latitude) - *latitude = rad2deg(pixel2lat(priv->map_zoom, priv->map_y + pixel_y)); - if (longitude) - *longitude = rad2deg(pixel2lon(priv->map_zoom, priv->map_x + pixel_x)); -} + if (priv->controls.surface) + cairo_surface_destroy(priv->controls.surface); -void -osm_gps_map_geographic_to_screen (OsmGpsMap *map, - gfloat latitude, gfloat longitude, - gint *pixel_x, gint *pixel_y) -{ - OsmGpsMapPrivate *priv; +#ifdef OSD_SOURCE_SEL + if(priv->source_sel.handler_id) + gtk_timeout_remove(priv->source_sel.handler_id); - g_return_if_fail (OSM_IS_GPS_MAP (map)); - priv = map->priv; + if (priv->source_sel.surface) + cairo_surface_destroy(priv->source_sel.surface); +#endif - if (pixel_x) - *pixel_x = lon2pixel(priv->map_zoom, deg2rad(longitude)) - priv->map_x; - if (pixel_y) - *pixel_y = lat2pixel(priv->map_zoom, deg2rad(latitude)) - priv->map_y; -} +#ifdef OSD_SCALE + if (priv->scale.surface) + cairo_surface_destroy(priv->scale.surface); +#endif -void -osm_gps_map_scroll (OsmGpsMap *map, gint dx, gint dy) -{ - OsmGpsMapPrivate *priv; +#ifdef OSD_CROSSHAIR + if (priv->crosshair.surface) + cairo_surface_destroy(priv->crosshair.surface); +#endif - g_return_if_fail (OSM_IS_GPS_MAP (map)); - priv = map->priv; +#ifdef OSD_COORDINATES + if (priv->coordinates.surface) + cairo_surface_destroy(priv->coordinates.surface); +#endif - priv->center_coord_set = FALSE; - priv->map_x += dx; - priv->map_y += dy; +#ifdef OSD_BALLOON + if (priv->balloon.surface) + cairo_surface_destroy(priv->balloon.surface); +#endif - osm_gps_map_map_redraw_idle (map); + g_free(priv); } -float -osm_gps_map_get_scale(OsmGpsMap *map) +static gboolean +osd_busy(osm_gps_map_osd_t *osd) { - OsmGpsMapPrivate *priv; - - g_return_val_if_fail (OSM_IS_GPS_MAP (map), OSM_NAN); - priv = map->priv; - - return osm_gps_map_get_scale_at_point(priv->map_zoom, priv->center_rlat, priv->center_rlon); +#ifdef OSD_SOURCE_SEL + osd_priv_t *priv = (osd_priv_t *)(osd->priv); + return (priv->source_sel.handler_id != 0); +#else + return FALSE; +#endif } -#ifdef ENABLE_BALLOON -void -osm_gps_map_draw_balloon (OsmGpsMap *map, float latitude, float longitude, - OsmGpsMapBalloonCallback cb, gpointer data) -{ - OsmGpsMapPrivate *priv; - - /* remove and previously installed balloon */ - osm_gps_map_clear_balloon (map); - - g_return_if_fail (OSM_IS_GPS_MAP (map)); - priv = map->priv; +static osm_gps_map_osd_t osd_classic = { + .widget = NULL, - priv->balloon.coo->rlat = deg2rad(latitude); - priv->balloon.coo->rlon = deg2rad(longitude); - priv->balloon.valid = TRUE; + .draw = osd_draw, + .check = osd_check, + .render = osd_render, + .free = osd_free, + .busy = osd_busy, - priv->balloon.cb = cb; - priv->balloon.data = data; + .cb = NULL, + .data = NULL, - // this redraws the map - osm_gps_map_map_redraw_idle(map); -} + .priv = NULL +}; -void -osm_gps_map_clear_balloon (OsmGpsMap *map) +/* this is the only function that's externally visible */ +void +osm_gps_map_osd_classic_init(OsmGpsMap *map) { - OsmGpsMapPrivate *priv; - - g_return_if_fail (OSM_IS_GPS_MAP (map)); - priv = map->priv; + osd_priv_t *priv = osd_classic.priv = g_new0(osd_priv_t, 1); - priv->balloon.valid = FALSE; - - osm_gps_map_map_redraw_idle(map); -} +#ifdef OSD_BALLOON + priv->balloon.lat = OSM_GPS_MAP_INVALID; + priv->balloon.lon = OSM_GPS_MAP_INVALID; #endif -#ifdef ENABLE_OSD -void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdGpsCallback cb, gpointer data) { - OsmGpsMapPrivate *priv; + osd_classic.priv = priv; - g_return_if_fail (OSM_IS_GPS_MAP (map)); - priv = map->priv; + osm_gps_map_register_osd(map, &osd_classic); +} - priv->osd.cb = cb; - priv->osd.data = data; +#ifdef OSD_GPS_BUTTON +/* below are osd specific functions which aren't used by osm-gps-map */ +/* but instead are to be used by the main application */ +void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdCallback cb, + gpointer data) { + osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map); + g_return_if_fail (osd); + + osd->cb = cb; + osd->data = data; /* this may have changed the state of the gps button */ /* we thus re-render the overlay */ - osm_gps_map_osd_render(priv); + osd->render(osd); - osm_gps_map_map_redraw_idle(map); + osm_gps_map_redraw(map); } #endif + +osd_button_t +osm_gps_map_osd_check(OsmGpsMap *map, gint x, gint y) { + osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map); + g_return_val_if_fail (osd, OSD_NONE); + + return osd_check(osd, TRUE, x, y); +}