--- trunk/src/osm-gps-map.c 2009/08/16 19:29:01 57 +++ trunk/src/osm-gps-map.c 2009/08/18 14:32:45 61 @@ -99,8 +99,24 @@ gboolean gps_valid; //a balloon with additional info - coord_t *balloon; - gboolean balloon_valid; + struct { + coord_t *coo; + gboolean valid; + OsmGpsMapRect_t rect; + OsmGpsMapBalloonCallback cb; + gpointer data; + } balloon; + +#ifdef ENABLE_OSD + //the osd controls + struct { + GdkPixmap *backup; + gint backup_x, backup_y; + + + // GdkPixbuf *pixbuf; + } osd; +#endif //additional images or tracks added to the map GSList *tracks; @@ -639,9 +655,13 @@ /* most visual effects are hardcoded by now, but may be made */ /* available via properties later */ +#define BALLOON_AREA_WIDTH 290 +#define BALLOON_AREA_HEIGHT 75 + #define BALLOON_CORNER_RADIUS 20 -#define BALLOON_WIDTH 150 -#define BALLOON_HEIGHT 75 +#define BALLOON_BORDER (BALLOON_CORNER_RADIUS/4) +#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 @@ -649,6 +669,9 @@ #define BALLOON_SHADOW 5 #define BALLOON_SHADOW_TRANSPARENCY 0.2 +#define CLOSE_BUTTON_RADIUS (BALLOON_CORNER_RADIUS/3) + + /* draw the bubble shape. this is used twice, once for the shape and once */ /* for the shadow */ static void @@ -658,7 +681,7 @@ cairo_move_to (cr, x0, y0 + BALLOON_CORNER_RADIUS); cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + BALLOON_CORNER_RADIUS, y0); if(!bottom) { - /* insert bottom/left pointer */ + /* insert top pointer */ cairo_line_to (cr, px1, y0); cairo_line_to (cr, px, py); cairo_line_to (cr, px0, y0); @@ -669,7 +692,7 @@ cairo_line_to (cr, x1 , y1 - BALLOON_CORNER_RADIUS); cairo_curve_to (cr, x1, y1, x1, y1, x1 - BALLOON_CORNER_RADIUS, y1); if(bottom) { - /* insert bottom/left pointer */ + /* insert bottom pointer */ cairo_line_to (cr, px0, y1); cairo_line_to (cr, px, py); cairo_line_to (cr, px1, y1); @@ -681,18 +704,17 @@ cairo_close_path (cr); } -/* http://cairographics.org/samples/ */ static void osm_gps_map_draw_balloon_int (OsmGpsMap *map) { OsmGpsMapPrivate *priv = map->priv; - if (priv->balloon_valid) { + if (priv->balloon.valid) { /* ------- convert given coordinate into screen position --------- */ - int x0 = lon2pixel(priv->map_zoom, priv->balloon->rlon) - + int x0 = lon2pixel(priv->map_zoom, priv->balloon.coo->rlon) - priv->map_x + EXTRA_BORDER; - int y0 = lat2pixel(priv->map_zoom, priv->balloon->rlat) - + int y0 = lat2pixel(priv->map_zoom, priv->balloon.coo->rlat) - priv->map_y + EXTRA_BORDER; /* check position of this relative to screen center to determine */ @@ -721,6 +743,12 @@ /* 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; + #ifdef USE_CAIRO cairo_t *cr = gdk_cairo_create(priv->pixmap); @@ -750,29 +778,41 @@ /* ---------- draw close button --------- */ - int cx = x1 - BALLOON_CORNER_RADIUS*3/4; - int cy = y0 + BALLOON_CORNER_RADIUS*3/4; - int crad = BALLOON_CORNER_RADIUS/3; + int cx = x1 - BALLOON_BORDER - CLOSE_BUTTON_RADIUS; + int cy = y0 + BALLOON_BORDER + CLOSE_BUTTON_RADIUS; + int crad = CLOSE_BUTTON_RADIUS; cairo_arc (cr, cx, cy, crad, 0, 2 * M_PI); cairo_set_source_rgba (cr, 0.8, 0, 0, 1.0); cairo_fill_preserve (cr); cairo_set_source_rgba (cr, 0.3, 0, 0, 1.0); - cairo_set_line_width (cr, 1); + cairo_set_line_width (cr, 2); cairo_stroke(cr); - int cs = crad/2; cairo_set_source_rgba (cr, 1, 1, 1, 1.0); cairo_set_line_width (cr, 3); cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); - cairo_move_to (cr, cx - cs, cy - cs); - cairo_line_to (cr, cx + cs, cy + cs); + cairo_move_to (cr, cx - crad/2, cy - crad/2); + cairo_line_to (cr, cx + crad/2, cy + crad/2); cairo_stroke (cr); - cairo_move_to (cr, cx + cs, cy - cs); - cairo_line_to (cr, cx - cs, cy + cs); + cairo_move_to (cr, cx + crad/2, cy - crad/2); + cairo_line_to (cr, cx - crad/2, cy + crad/2); 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); + gtk_widget_queue_draw_area (GTK_WIDGET(map), x0, y0, BALLOON_WIDTH, BALLOON_HEIGHT + POINTER_HEIGHT); @@ -782,6 +822,37 @@ } } +/* the user clicked into the balloons main area. handle this */ +static void +osm_gps_map_handle_balloon_click(OsmGpsMap *map, gint x, gint y) +{ + OsmGpsMapPrivate *priv = map->priv; + + if (!priv->balloon.valid) + return; + + /* check if the close button was clicked */ + if ((x > priv->balloon.rect.w - 2*CLOSE_BUTTON_RADIUS) && + (x < priv->balloon.rect.w) && + (y > 0) && (y < 2*CLOSE_BUTTON_RADIUS)) { + + priv->balloon.valid = FALSE; + osm_gps_map_map_redraw_idle(map); + } +} + +/* return true if balloon is being displayed and if */ +/* the given coordinate is within this balloon */ +static gboolean +osm_gps_map_in_balloon(OsmGpsMapPrivate *priv, gint x, gint y) +{ + return (priv->balloon.valid && + (x > priv->balloon.rect.x) && + (x < priv->balloon.rect.x + priv->balloon.rect.w) && + (y > priv->balloon.rect.y) && + (y < priv->balloon.rect.y + priv->balloon.rect.h)); +} + static void osm_gps_map_blit_tile(OsmGpsMap *map, GdkPixbuf *pixbuf, int offset_x, int offset_y) { @@ -1336,6 +1407,147 @@ g_hash_table_foreach_remove(priv->tile_cache, osm_gps_map_purge_cache_check, priv); } +#ifdef ENABLE_OSD +/* position and extent of bounding box */ +#define OSD_X (10) +#define OSD_Y (10) +#define OSD_W (80+5) +#define OSD_H (120+5) + +static void +osm_gps_map_draw_osd_controls (OsmGpsMap *map, gint xoffset, gint yoffset) +{ + /* xyz */ + OsmGpsMapPrivate *priv = map->priv; + + /* backup previous contents */ + if(!priv->osd.backup) + priv->osd.backup = gdk_pixmap_new(priv->pixmap, OSD_W, OSD_H, -1); + + 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, y, 0, 0, OSD_W, OSD_H); + priv->osd.backup_x = x; + priv->osd.backup_y = y; + +#if 0 + /* create pixbuf for osd */ + if(!priv->osd.pixbuf) + priv->osd.pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, + TRUE, 8, OSD_W, OSD_H); + cairo_surface_t *surface = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W, OSD_H); + + /* fill with transparency */ + { + cairo_t *cr = cairo_create(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_destroy(cr); + } +#endif + + +#ifdef USE_CAIRO + // cairo_t *cr = cairo_create(surface); + cairo_t *cr = gdk_cairo_create(priv->pixmap); + +#define RAD 40 +#define TIP 35 +#define LEN 15 +#define WID 15 + + /* --------- the direction "pad" shape and shadow ----------- */ + cairo_arc (cr, x+RAD+5, y+RAD+5, RAD, 0, 2 * M_PI); + cairo_set_source_rgba (cr, 0, 0, 0, 0.2); + cairo_fill (cr); + cairo_stroke (cr); + + cairo_arc (cr, x+RAD, y+RAD, RAD, 0, 2 * M_PI); + cairo_set_source_rgb (cr, 1, 1, 1); + cairo_fill_preserve (cr); + cairo_set_source_rgb (cr, 0.6, 0.6, 1); + cairo_set_line_width (cr, 1); + cairo_stroke (cr); + + /* ---------- the zoom pad shape and shadow -------------- */ + cairo_move_to (cr, x, y+2*RAD); + cairo_line_to (cr, x+2*RAD-10, y+2*RAD); + cairo_curve_to (cr, x+2*RAD, y+2*RAD, + x+2*RAD, y+3*RAD, + x+2*RAD-10, y+3*RAD); + cairo_close_path (cr); + + + cairo_set_source_rgba (cr, 1, 1, 1, 1.0); + cairo_fill (cr); + cairo_stroke (cr); + + + /* left arrow/triangle */ + cairo_move_to (cr, x+RAD-TIP, y+RAD); + cairo_rel_line_to (cr, +LEN, -WID/2); + cairo_rel_line_to (cr, 0, +WID); + cairo_rel_line_to (cr, -LEN, -WID/2); + cairo_close_path (cr); + + /* right arrow/triangle */ + cairo_move_to (cr, x+RAD+TIP, y+RAD); + cairo_rel_line_to (cr, -LEN, -WID/2); + cairo_rel_line_to (cr, 0, +WID); + cairo_rel_line_to (cr, +LEN, -WID/2); + cairo_close_path (cr); + + /* top arrow/triangle */ + cairo_move_to (cr, x+RAD, y+RAD-TIP); + cairo_rel_line_to (cr, -WID/2, +LEN); + cairo_rel_line_to (cr, +WID, 0); + cairo_rel_line_to (cr, -WID/2, -LEN); + cairo_close_path (cr); + + /* bottom arrow/triangle */ + cairo_move_to (cr, x+RAD, y+RAD+TIP); + cairo_rel_line_to (cr, -WID/2, -LEN); + cairo_rel_line_to (cr, +WID, 0); + cairo_rel_line_to (cr, -WID/2, +LEN); + cairo_close_path (cr); + + cairo_set_source_rgb (cr, 0.6, 0.6, 1); + cairo_fill_preserve (cr); + cairo_set_line_width (cr, 0); + cairo_set_source_rgba (cr, 0, 0, 0, 1); + cairo_stroke (cr); + + + cairo_destroy(cr); + +#else +#warning "OSD control display lacks a non-cairo implementation!" +#endif +} + +static void +osm_gps_map_osd_restore (OsmGpsMap *map) +{ + OsmGpsMapPrivate *priv = map->priv; + + /* 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, OSD_H); + } +} + +#endif + static gboolean osm_gps_map_map_redraw (OsmGpsMap *map) { @@ -1367,6 +1579,9 @@ osm_gps_map_draw_gps_point(map); osm_gps_map_print_images(map); osm_gps_map_draw_balloon_int(map); +#ifdef ENABLE_OSD + osm_gps_map_draw_osd_controls(map, 0, 0); +#endif //osm_gps_map_osd_speed(map, 1.5); osm_gps_map_purge_cache(map); @@ -1398,8 +1613,13 @@ priv->gps = g_new0(coord_t, 1); priv->gps_valid = FALSE; - priv->balloon = g_new0(coord_t, 1); - priv->balloon_valid = FALSE; + priv->balloon.coo = g_new0(coord_t, 1); + priv->balloon.valid = FALSE; + priv->balloon.cb = NULL; + +#ifdef ENABLE_OSD + priv->osd.backup = NULL; +#endif priv->tracks = NULL; priv->images = NULL; @@ -1559,7 +1779,12 @@ g_source_remove (priv->idle_map_redraw); g_free(priv->gps); - g_free(priv->balloon); + g_free(priv->balloon.coo); + +#ifdef ENABLE_OSD + if (priv->osd.backup) + g_object_unref(priv->osd.backup); +#endif G_OBJECT_CLASS (osm_gps_map_parent_class)->dispose (object); } @@ -1776,6 +2001,15 @@ { OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget); + /* 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; + } + priv->drag_counter = 0; priv->drag_start_mouse_x = (int) event->x; priv->drag_start_mouse_y = (int) event->y; @@ -1790,6 +2024,20 @@ { OsmGpsMapPrivate *priv = OSM_GPS_MAP_PRIVATE(widget); + /* 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); + return FALSE; + } + + if (priv->drag_counter < 0) + return FALSE; + if (priv->dragging) { priv->dragging = FALSE; @@ -1807,7 +2055,7 @@ priv->drag_mouse_dx = 0; priv->drag_mouse_dy = 0; - priv->drag_counter = 0; + priv->drag_counter = -1; return FALSE; } @@ -1832,6 +2080,9 @@ if (!(state & GDK_BUTTON1_MASK)) return FALSE; + if (priv->drag_counter < 0) + return FALSE; + priv->drag_counter++; // we havent dragged more than 6 pixels @@ -1846,6 +2097,16 @@ priv->drag_mouse_dx = x - priv->drag_start_mouse_x; priv->drag_mouse_dy = y - priv->drag_start_mouse_y; +#ifdef ENABLE_OSD + /* undo OSD */ + osm_gps_map_osd_restore (OSM_GPS_MAP(widget)); + + /* draw new OSD */ + osm_gps_map_draw_osd_controls (OSM_GPS_MAP(widget), + -priv->drag_mouse_dx, + -priv->drag_mouse_dy); +#endif + gdk_draw_drawable ( widget->window, widget->style->fg_gc[GTK_WIDGET_STATE (widget)], @@ -1934,10 +2195,22 @@ 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 + EXTRA_BORDER, + event->area.y + EXTRA_BORDER, event->area.x, event->area.y, event->area.width, event->area.height); +#ifdef ENABLE_OSD_OVL + /* TODO: intersect with area */ + if (priv->osd.pixbuf) + { + // gdk_draw_drawable (widget->window, + // widget->style->fg_gc[GTK_WIDGET_STATE (widget)], + // priv->osd.pixbuf, 0, 0, + // OSD_X, OSD_Y, OSD_W, OSD_H); + } +#endif + return FALSE; } @@ -2710,7 +2983,8 @@ } void -osm_gps_map_draw_balloon (OsmGpsMap *map, float latitude, float longitude) +osm_gps_map_draw_balloon (OsmGpsMap *map, float latitude, float longitude, + OsmGpsMapBalloonCallback cb, gpointer data) { OsmGpsMapPrivate *priv; @@ -2720,9 +2994,12 @@ g_return_if_fail (OSM_IS_GPS_MAP (map)); priv = map->priv; - priv->balloon->rlat = deg2rad(latitude); - priv->balloon->rlon = deg2rad(longitude); - priv->balloon_valid = TRUE; + priv->balloon.coo->rlat = deg2rad(latitude); + priv->balloon.coo->rlon = deg2rad(longitude); + priv->balloon.valid = TRUE; + + priv->balloon.cb = cb; + priv->balloon.data = data; // this redraws the map osm_gps_map_map_redraw_idle(map); @@ -2736,7 +3013,7 @@ g_return_if_fail (OSM_IS_GPS_MAP (map)); priv = map->priv; - priv->balloon_valid = FALSE; + priv->balloon.valid = FALSE; osm_gps_map_map_redraw_idle(map); }