/* * This file is part of maemo-mapper * * Copyright (C) 2006 John Costigan. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #define _GNU_SOURCE #define _(String) gettext(String) #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* BELOW: for getting input from the GPS receiver. */ #include #include #include #include #include /**************************************************************************** * BELOW: DEFINES *********************************************************** ****************************************************************************/ #ifndef DEBUG #define printf(...) #endif /* Set the below if to determine whether to get verbose output. */ #if 0 #define vprintf printf #else #define vprintf(...) #endif #define BOUND(x, a, b) { \ if((x) < (a)) \ (x) = (a); \ else if((x) > (b)) \ (x) = (b); \ } #define PI (3.14159265358979323846f) /** MAX_ZOOM defines the largest map zoom level we will download. * (MAX_ZOOM - 1) is the largest map zoom level that the user can zoom to. */ #define MAX_ZOOM 16 #define TILE_SIZE_PIXELS (256) #define TILE_SIZE_P2 (8) #define TILE_PIXBUF_STRIDE (768) #define BUF_WIDTH_TILES (4) #define BUF_HEIGHT_TILES (3) #define BUF_WIDTH_PIXELS (1024) #define BUF_HEIGHT_PIXELS (768) #define ARRAY_CHUNK_SIZE (1024) #define WORLD_SIZE_UNITS (2 << (MAX_ZOOM + TILE_SIZE_P2)) #define tile2grid(tile) ((tile) << 3) #define grid2tile(grid) ((grid) >> 3) #define tile2pixel(tile) ((tile) << 8) #define pixel2tile(pixel) ((pixel) >> 8) #define tile2unit(tile) ((tile) << (8 + _zoom)) #define unit2tile(unit) ((unit) >> (8 + _zoom)) #define tile2zunit(tile, zoom) ((tile) << (8 + zoom)) #define unit2ztile(unit, zoom) ((unit) >> (8 + zoom)) #define grid2pixel(grid) ((grid) << 5) #define pixel2grid(pixel) ((pixel) >> 5) #define grid2unit(grid) ((grid) << (5 + _zoom)) #define unit2grid(unit) ((unit) >> (5 + _zoom)) #define pixel2unit(pixel) ((pixel) << _zoom) #define unit2pixel(pixel) ((pixel) >> _zoom) #define unit2bufx(unit) (unit2pixel(unit) - tile2pixel(_base_tilex)) #define bufx2unit(x) (pixel2unit(x) + tile2unit(_base_tilex)) #define unit2bufy(unit) (unit2pixel(unit) - tile2pixel(_base_tiley)) #define bufy2unit(y) (pixel2unit(y) + tile2unit(_base_tiley)) #define unit2x(unit) (unit2pixel(unit) - tile2pixel(_base_tilex) - _offsetx) #define x2unit(x) (pixel2unit(x + _offsetx) + tile2unit(_base_tilex)) #define unit2y(unit) (unit2pixel(unit) - tile2pixel(_base_tiley) - _offsety) #define y2unit(y) (pixel2unit(y + _offsety) + tile2unit(_base_tiley)) #define leadx2unit() (_pos.unitx + (_lead_ratio) * pixel2unit(_vel_offsetx)) #define leady2unit() (_pos.unity + (0.6f*_lead_ratio)*pixel2unit(_vel_offsety)) #define tile2punit(tile, zoom) ((((tile) << 1) + 1) << (6 + zoom)) #define unit2ptile(unit) ((unit) >> (6 + _zoom)) /* Pans are done two "grids" at a time, or 64 pixels. */ #define PAN_UNITS (grid2unit(2)) #define INITIAL_DOWNLOAD_RETRIES (2) #define UNITS_KM _("km") #define UNITS_MI _("mi.") #define UNITS_NM _("n.m.") #define GCONF_KEY_PREFIX "/apps/maemo/maemo-mapper" #define GCONF_KEY_RCVR_MAC GCONF_KEY_PREFIX"/receiver_mac" #define GCONF_KEY_RCVR_CHAN GCONF_KEY_PREFIX"/receiver_channel" #define GCONF_KEY_MAP_URI_FORMAT GCONF_KEY_PREFIX"/map_uri_format" #define GCONF_KEY_MAP_URI_FORMAT_1 GCONF_KEY_PREFIX"/map_uri_format_1" #define GCONF_KEY_MAP_URI_FORMAT_2 GCONF_KEY_PREFIX"/map_uri_format_2" #define GCONF_KEY_MAP_ZOOM_STEPS GCONF_KEY_PREFIX"/map_zoom_steps" #define GCONF_KEY_MAP_DIR_NAME GCONF_KEY_PREFIX"/map_cache_dir" #define GCONF_KEY_MAP_DIR_NAME_1 GCONF_KEY_PREFIX"/map_cache_dir_1" #define GCONF_KEY_MAP_DIR_NAME_2 GCONF_KEY_PREFIX"/map_cache_dir_2" #define GCONF_KEY_MAP_SETTING GCONF_KEY_PREFIX"/map_setting" #define GCONF_KEY_AUTO_DOWNLOAD GCONF_KEY_PREFIX"/auto_download" #define GCONF_KEY_CENTER_SENSITIVITY GCONF_KEY_PREFIX"/center_sensitivity" #define GCONF_KEY_ANNOUNCE_NOTICE GCONF_KEY_PREFIX"/announce_notice" #define GCONF_KEY_DRAW_LINE_WIDTH GCONF_KEY_PREFIX"/draw_line_width" #define GCONF_KEY_ENABLE_VOICE GCONF_KEY_PREFIX"/enable_voice" #define GCONF_KEY_VOICE_SPEED GCONF_KEY_PREFIX"/voice_speed" #define GCONF_KEY_VOICE_PITCH GCONF_KEY_PREFIX"/voice_pitch" #define GCONF_KEY_ALWAYS_KEEP_ON GCONF_KEY_PREFIX"/always_keep_on" #define GCONF_KEY_UNITS GCONF_KEY_PREFIX"/units" #define GCONF_KEY_COLOR_MARK GCONF_KEY_PREFIX"/color_mark" #define GCONF_KEY_COLOR_MARK_VELOCITY GCONF_KEY_PREFIX"/color_mark_velocity" #define GCONF_KEY_COLOR_MARK_OLD GCONF_KEY_PREFIX"/color_mark_old" #define GCONF_KEY_COLOR_TRACK GCONF_KEY_PREFIX"/color_track" #define GCONF_KEY_COLOR_TRACK_BREAK GCONF_KEY_PREFIX"/color_track_break" #define GCONF_KEY_COLOR_ROUTE GCONF_KEY_PREFIX"/color_route" #define GCONF_KEY_COLOR_ROUTE_WAY GCONF_KEY_PREFIX"/color_route_way" #define GCONF_KEY_COLOR_ROUTE_NEXTWAY GCONF_KEY_PREFIX"/color_route_nextway" #define GCONF_KEY_AUTOCENTER_MODE GCONF_KEY_PREFIX"/autocenter_mode" #define GCONF_KEY_LEAD_AMOUNT GCONF_KEY_PREFIX"/lead_amount" #define GCONF_KEY_LAT GCONF_KEY_PREFIX"/last_latitude" #define GCONF_KEY_LON GCONF_KEY_PREFIX"/last_longitude" #define GCONF_KEY_CENTER_LAT GCONF_KEY_PREFIX"/center_latitude" #define GCONF_KEY_CENTER_LON GCONF_KEY_PREFIX"/center_longitude" #define GCONF_KEY_ZOOM GCONF_KEY_PREFIX"/zoom" #define GCONF_KEY_ROUTEDIR GCONF_KEY_PREFIX"/route_directory" #define GCONF_KEY_TRACKFILE GCONF_KEY_PREFIX"/track_file" #define GCONF_KEY_SHOWTRACKS GCONF_KEY_PREFIX"/show_tracks" #define GCONF_KEY_SHOWROUTES GCONF_KEY_PREFIX"/show_routes" #define GCONF_KEY_SHOWVELVEC GCONF_KEY_PREFIX"/show_velocity_vector" #define GCONF_KEY_ENABLE_GPS GCONF_KEY_PREFIX"/enable_gps" #define GCONF_KEY_ROUTE_LOCATIONS GCONF_KEY_PREFIX"/route_locations" #define GCONF_KEY_DISCONNECT_ON_COVER "/system/osso/connectivity/IAP/disconnect_on_cover" #define XML_DATE_FORMAT "%FT%T" #define XML_TRKSEG_HEADER ( \ "\n" \ "\n" \ " \n" \ " \n" \ ) #define XML_TRKSEG_FOOTER ( \ " \n" \ " \n" \ "\n" \ ) #define HELP_ID_PREFIX "help_maemomapper_" #define HELP_ID_INTRO HELP_ID_PREFIX"intro" #define MACRO_RECALC_CENTER(center_unitx, center_unity) { \ switch(_center_mode) \ { \ case CENTER_LEAD: \ center_unitx = leadx2unit(); \ center_unity = leady2unit(); \ break; \ case CENTER_LATLON: \ center_unitx = _pos.unitx; \ center_unity = _pos.unity; \ break; \ default: \ center_unitx = _center.unitx; \ center_unity = _center.unity; \ ; \ } \ }; #define EARTH_RADIUS_KM (6371.f) #define EARTH_RADIUS_MI (3958.75587f) #define EARTH_RADIUS_NM (3440.06479f) #define MERCATOR_SPAN (-6.28318377773622f) #define MERCATOR_TOP (3.14159188886811f) #define latlon2unit(lat, lon, unitx, unity) { \ gfloat tmp; \ unitx = (lon + 180.f) * (WORLD_SIZE_UNITS / 360.f) + 0.5f; \ tmp = sinf(lat * (PI / 180.f)); \ unity = 0.5f + (WORLD_SIZE_UNITS / MERCATOR_SPAN) \ * (logf((1.f + tmp) / (1.f - tmp)) * 0.5f - MERCATOR_TOP); \ } #define unit2latlon(unitx, unity, lat, lon) { \ (lon) = ((unitx) * (360.f / WORLD_SIZE_UNITS)) - 180.f; \ (lat) = (360.f * (atanf(expf(((unity) \ * (MERCATOR_SPAN / WORLD_SIZE_UNITS)) \ + MERCATOR_TOP)))) * (1.f / PI) - 90.f; \ } #define MACRO_RECALC_OFFSET() { \ _offsetx = grid2pixel( \ unit2grid(_center.unitx) \ - _screen_grids_halfwidth \ - tile2grid(_base_tilex)); \ _offsety = grid2pixel( \ unit2grid(_center.unity) \ - _screen_grids_halfheight \ - tile2grid(_base_tiley)); \ } #define MACRO_RECALC_FOCUS_BASE() { \ _focus.unitx = x2unit(_screen_width_pixels * _center_ratio / 20); \ _focus.unity = y2unit(_screen_height_pixels * _center_ratio / 20); \ } #define MACRO_RECALC_FOCUS_SIZE() { \ _focus_unitwidth = pixel2unit( \ (10 - _center_ratio) * _screen_width_pixels / 10); \ _focus_unitheight = pixel2unit( \ (10 - _center_ratio) * _screen_height_pixels / 10); \ } #define MACRO_RECALC_CENTER_BOUNDS() { \ _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth)); \ _min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight)); \ _max_center.unitx = WORLD_SIZE_UNITS-grid2unit(_screen_grids_halfwidth) - 1; \ _max_center.unity = WORLD_SIZE_UNITS-grid2unit(_screen_grids_halfheight)- 1; \ } #define MACRO_INIT_TRACK(track) { \ (track).head = (track).tail = g_new(TrackPoint, ARRAY_CHUNK_SIZE); \ *((track).tail) = _track_null; \ (track).cap = (track).head + ARRAY_CHUNK_SIZE; \ } #define MACRO_CLEAR_TRACK(track) if((track).head) { \ g_free((track).head); \ (track).head = (track).tail = (track).cap = NULL; \ } #define MACRO_INIT_ROUTE(route) { \ (route).head = (route).tail = g_new(Point, ARRAY_CHUNK_SIZE); \ *((route).tail) = _pos_null; \ (route).cap = (route).head + ARRAY_CHUNK_SIZE; \ (route).whead = g_new(WayPoint, ARRAY_CHUNK_SIZE); \ (route).wtail = (route).whead - 1; \ (route).wcap = (route).whead + ARRAY_CHUNK_SIZE; \ } #define MACRO_CLEAR_ROUTE(route) if((route).head) { \ WayPoint *curr; \ g_free((route).head); \ (route).head = (route).tail = (route).cap = NULL; \ for(curr = (route).whead - 1; curr++ != (route).wtail; ) \ g_free(curr->desc); \ g_free((route).whead); \ (route).whead = (route).wtail = (route).wcap = NULL; \ } #define DISTANCE_SQUARED(a, b) \ ((((gint)((b).unitx) - (a).unitx) * ((gint)((b).unitx) - (a).unitx)) \ + (((gint)((b).unity) - (a).unity) * ((gint)((b).unity) - (a).unity))) #define DISTANCE_ROUGH(a, b) \ (abs((gint)((b).unitx) - (a).unitx) \ + abs((gint)((b).unity) - (a).unity)) #define MACRO_QUEUE_DRAW_AREA() \ gtk_widget_queue_draw_area( \ _map_widget, \ 0, 0, \ _screen_width_pixels, \ _screen_height_pixels) #define KEEP_DISPLAY_ON() { \ /* Note that the flag means keep on ONLY when fullscreen. */ \ if(_always_keep_on || _fullscreen) \ { \ osso_display_state_on(_osso); \ osso_display_blanking_pause(_osso); \ } \ } #define TRACKS_MASK 0x00000001 #define ROUTES_MASK 0x00000002 /**************************************************************************** * ABOVE: DEFINES *********************************************************** ****************************************************************************/ /**************************************************************************** * BELOW: TYPEDEFS ********************************************************** ****************************************************************************/ /** This enumerated type defines the possible connection states. */ typedef enum { /** The receiver is "off", meaning that either the bluetooth radio is * off or the user has requested not to connect to the GPS receiver. * No gtk_banner is visible. */ RCVR_OFF, /** The connection with the receiver is down. A gtk_banner is visible with * the text, "Connecting to GPS receiver". */ RCVR_DOWN, /** The connection with the receiver is up, but a GPS fix is not available. * A gtk_banner is visible with the text, "(Re-)Establishing GPS fix". */ RCVR_UP, /** The connection with the receiver is up and a GPS fix IS available. * No gtk_banner is visible. */ RCVR_FIXED } ConnState; /** Possible center modes. The "WAS" modes imply no current center mode; * they only hint at what the last center mode was, so that it can be * recalled. */ typedef enum { CENTER_WAS_LATLON = -2, CENTER_WAS_LEAD = -1, CENTER_LEAD = 1, CENTER_LATLON = 2 } CenterMode; /** This enum defines the states of the SAX parsing state machine. */ typedef enum { START, INSIDE_GPX, INSIDE_PATH, INSIDE_PATH_SEGMENT, INSIDE_PATH_POINT, INSIDE_PATH_POINT_TIME, INSIDE_PATH_POINT_DESC, FINISH, UNKNOWN, ERROR, } SaxState; /** A general definition of a point in the Maemo Mapper unit system. */ typedef struct _Point Point; struct _Point { guint unitx; guint unity; }; /** A TrackPoint, which is a Point with a time. */ typedef struct _TrackPoint TrackPoint; struct _TrackPoint { Point point; time_t time; }; /** A WayPoint, which is a Point with a description. */ typedef struct _WayPoint WayPoint; struct _WayPoint { Point *point; gchar *desc; }; /** A Track is a set of timed TrackPoints. */ typedef struct _Track Track; struct _Track { TrackPoint *head; /* points to first element in array; NULL if empty. */ TrackPoint *tail; /* points to last element in array. */ TrackPoint *cap; /* points after last slot in array. */ }; /** A Route is a set of untimed Points and WayPoints. */ typedef struct _Route Route; struct _Route { Point *head; /* points to first element in array; NULL if empty. */ Point *tail; /* points to last element in array. */ Point *cap; /* points after last slot in array. */ WayPoint *whead; /* points to first element in array; NULL if empty. */ WayPoint *wtail; /* points to last element in array. */ WayPoint *wcap; /* points after last slot in array. */ }; typedef enum _PathType PathType; enum _PathType { TRACK, ROUTE, }; typedef struct _Path Path; struct _Path { union { Track track; Route route; } path; PathType path_type; }; /** Data used during the SAX parsing operation. */ typedef struct _SaxData SaxData; struct _SaxData { Path path; SaxState state; SaxState prev_state; guint unknown_depth; gboolean at_least_one_trkpt; GString *chars; }; /** Data used during the asynchronous progress update phase of automatic map * downloading. */ typedef struct _ProgressUpdateInfo ProgressUpdateInfo; struct _ProgressUpdateInfo { GnomeVFSAsyncHandle *handle; GList *src_list; GList *dest_list; guint tilex, tiley, zoom; /* for refresh. */ gint retries_left; guint hash; }; /** Data used during the asynchronous automatic route downloading operation. */ typedef struct _AutoRouteDownloadData AutoRouteDownloadData; struct _AutoRouteDownloadData { gboolean enabled; gboolean in_progress; gchar *dest; GnomeVFSAsyncHandle *handle; gchar *bytes; guint bytes_read; guint bytes_maxsize; }; /**************************************************************************** * ABOVE: TYPEDEFS ********************************************************** ****************************************************************************/ /**************************************************************************** * BELOW: DATA ************************************************************** ****************************************************************************/ /** The main GtkWindow of the application. */ static HildonProgram *_program = NULL; /** The main GtkContainer of the application. */ static GtkWidget *_window = NULL; /** The main OSSO context of the application. */ static osso_context_t *_osso = NULL; /** The widget that provides the visual display of the map. */ static GtkWidget *_map_widget = NULL; /** The backing pixmap of _map_widget. */ static GdkPixmap *_map_pixmap = NULL; /** The file descriptor of our connection with the GPS receiver. */ static gint _fd = -1; /** The GIOChannel through which communication with the GPS receiver is * performed. */ static GIOChannel *_channel = NULL; /** The Source ID of the connect watch. */ static guint _connect_sid = 0; /** The Source ID of the error watch. */ static guint _error_sid = 0; /** The Source ID of the input watch. */ static guint _input_sid = 0; /** The Source ID of the "Connect Later" idle. */ static guint _clater_sid = 0; /** GPS data. */ static gfloat _pos_lat = 0.f; static gfloat _pos_lon = 0.f; static Point _pos = {0, 0}; static const Point _pos_null = {0, 0}; static const TrackPoint _track_null = { { 0, 0 }, 0}; static gfloat _speed = 0; static gfloat _heading = 0; static gint _vel_offsetx = 0; static gint _vel_offsety = 0; /** The current connection state. */ static ConnState _conn_state = RCVR_OFF; /** VARIABLES FOR MAINTAINING STATE OF THE CURRENT VIEW. */ /** The "zoom" level defines the resolution of a pixel, from 0 to MAX_ZOOM. * Each pixel in the current view is exactly (1 << _zoom) "units" wide. */ static guint _zoom = 3; /* zoom level, from 0 to MAX_ZOOM. */ static Point _center = {-1, -1}; /* current center location, X. */ /** The "base tile" is the upper-left tile in the pixmap. */ static guint _base_tilex = -5; static guint _base_tiley = -5; /** The "offset" defines the upper-left corner of the visible portion of the * 1024x768 pixmap. */ static guint _offsetx = -1; static guint _offsety = -1; /** CACHED SCREEN INFORMATION THAT IS DEPENDENT ON THE CURRENT VIEW. */ static guint _screen_grids_halfwidth = 0; static guint _screen_grids_halfheight = 0; static guint _screen_width_pixels = 0; static guint _screen_height_pixels = 0; static Point _focus = {-1, -1}; static guint _focus_unitwidth = 0; static guint _focus_unitheight = 0; static Point _min_center = {-1, -1}; static Point _max_center = {-1, -1}; static guint _world_size_tiles = -1; /** VARIABLES FOR ACCESSING THE LOCATION/BOUNDS OF THE CURRENT MARK. */ static gint _mark_x1 = -1; static gint _mark_x2 = -1; static gint _mark_y1 = -1; static gint _mark_y2 = -1; static gint _mark_minx = -1; static gint _mark_miny = -1; static gint _mark_width = -1; static gint _mark_height = -1; /** The current track and route. */ static Track _track; static Route _route; /** Data for tracking waypoints for the purpose of announcement. */ /* _near_point is the route point to which we are closest. */ static Point *_near_point = NULL; static guint _near_point_dist_rough = -1; /* _next_way is what we currently interpret to be the next waypoint. */ static WayPoint *_next_way = NULL; static guint _next_way_dist_rough = -1; static gchar *_last_spoken_phrase = NULL; /* _next_wpt is the route point immediately following _next_way. */ static Point *_next_wpt = NULL; static guint _next_wpt_dist_rough = -1; static WayPoint *_visible_way_first = NULL; static WayPoint *_visible_way_last = NULL; static guint _key_zoom_new = 0; static guint _key_zoom_timeout_sid = 0; /** THE GdkGC OBJECTS USED FOR DRAWING. */ static GdkGC *_gc_mark = NULL; static GdkGC *_gc_mark_velocity = NULL; static GdkGC *_gc_mark_old = NULL; static GdkGC *_gc_track = NULL; static GdkGC *_gc_track_break = NULL; static GdkGC *_gc_route = NULL; static GdkGC *_gc_route_way = NULL; static GdkGC *_gc_route_nextway = NULL; static GdkColor _color_mark = {0, 0, 0, 0}; static GdkColor _color_mark_velocity = {0, 0, 0, 0}; static GdkColor _color_mark_old = {0, 0, 0, 0}; static GdkColor _color_track = {0, 0, 0, 0}; static GdkColor _color_track_break = {0, 0, 0, 0}; static GdkColor _color_route = {0, 0, 0, 0}; static GdkColor _color_route_way = {0, 0, 0, 0}; static GdkColor _color_route_nextway = {0, 0, 0, 0}; static GdkColor DEFAULT_COLOR_MARK = {0, 0x0000, 0x0000, 0xc000}; static GdkColor DEFAULT_COLOR_MARK_VELOCITY = {0, 0x6000, 0x6000, 0xf800}; static GdkColor DEFAULT_COLOR_MARK_OLD = {0, 0x8000, 0x8000, 0x8000}; static GdkColor DEFAULT_COLOR_TRACK = {0, 0xe000, 0x0000, 0x0000}; static GdkColor DEFAULT_COLOR_TRACK_BREAK = {0, 0xa000, 0x0000, 0x0000}; static GdkColor DEFAULT_COLOR_ROUTE = {0, 0x0000, 0xa000, 0x0000}; static GdkColor DEFAULT_COLOR_ROUTE_WAY = {0, 0x0000, 0x8000, 0x0000}; static GdkColor DEFAULT_COLOR_ROUTE_NEXTWAY = {0, 0x0000, 0x6000, 0x0000}; /** MAIN MENU ITEMS. */ static GtkWidget *_menu_route_download_item = NULL; static GtkWidget *_menu_route_open_item = NULL; static GtkWidget *_menu_route_save_item = NULL; static GtkWidget *_menu_route_reset_item = NULL; static GtkWidget *_menu_route_clear_item = NULL; static GtkWidget *_menu_track_open_item = NULL; static GtkWidget *_menu_track_save_item = NULL; static GtkWidget *_menu_track_mark_way_item = NULL; static GtkWidget *_menu_track_clear_item = NULL; static GtkWidget *_menu_maps_dlroute_item = NULL; static GtkWidget *_menu_maps_dlarea_item = NULL; static GtkWidget *_menu_auto_download_item = NULL; static GtkWidget *_menu_show_tracks_item = NULL; static GtkWidget *_menu_show_routes_item = NULL; static GtkWidget *_menu_show_velvec_item = NULL; static GtkWidget *_menu_ac_latlon_item = NULL; static GtkWidget *_menu_ac_lead_item = NULL; static GtkWidget *_menu_ac_none_item = NULL; static GtkWidget *_menu_fullscreen_item = NULL; static GtkWidget *_menu_enable_gps_item = NULL; static GtkWidget *_menu_settings_item = NULL; static GtkWidget *_menu_help_item = NULL; static GtkWidget *_menu_close_item = NULL; /** CONTEXT MENU ITEMS. */ static guint _cmenu_position_x; static guint _cmenu_position_y; static GtkWidget *_cmenu_loc_show_latlon_item = NULL; static GtkWidget *_cmenu_loc_clip_latlon_item = NULL; static GtkWidget *_cmenu_loc_route_to_item = NULL; static GtkWidget *_cmenu_loc_distance_to_item = NULL; static GtkWidget *_cmenu_way_show_latlon_item = NULL; static GtkWidget *_cmenu_way_show_desc_item = NULL; static GtkWidget *_cmenu_way_clip_latlon_item = NULL; static GtkWidget *_cmenu_way_clip_desc_item = NULL; static GtkWidget *_cmenu_way_route_to_item = NULL; static GtkWidget *_cmenu_way_distance_to_item = NULL; static GtkWidget *_cmenu_way_delete_item = NULL; /** BANNERS. */ GtkWidget *_connect_banner = NULL; GtkWidget *_fix_banner = NULL; GtkWidget *_download_banner = NULL; /** DOWNLOAD PROGRESS. */ static guint _num_downloads = 0; static guint _curr_download = 0; static GHashTable *_downloads_hash = NULL; /** CONFIGURATION INFORMATION. */ static struct sockaddr_rc _rcvr_addr = { 0 }; static gchar *_rcvr_mac = NULL; static gchar *_map_dir_name = NULL; static gchar *_map_dir_name_1 = NULL; static gchar *_map_dir_name_2 = NULL; static gchar *_map_uri_format = NULL; static gchar *_map_uri_format_1 = NULL; static gchar *_map_uri_format_2 = NULL; static guint _map_setting = 1; static gchar *_route_dir_uri = NULL; static gchar *_track_file_uri = NULL; static CenterMode _center_mode = CENTER_LEAD; static gboolean _fullscreen = FALSE; static gboolean _enable_gps = FALSE; static gint _show_tracks = 0; static gboolean _show_velvec = TRUE; static gboolean _auto_download = FALSE; static guint _zoom_steps = 2; static guint _lead_ratio = 5; static guint _center_ratio = 7; static guint _draw_line_width = 5; static guint _announce_notice_ratio = 6; static gboolean _enable_voice = TRUE; static gboolean _always_keep_on = FALSE; static gdouble _voice_speed = 1.0; static gint _voice_pitch = 0; static GSList *_loc_list; static GtkListStore *_loc_model; static gchar *_units = NULL; /** The singleton auto-route-download data. */ static AutoRouteDownloadData _autoroute_data; static gchar XML_TZONE[7]; /**************************************************************************** * ABOVE: DATA ************************************************************** ****************************************************************************/ /**************************************************************************** * BELOW: CALLBACK DECLARATIONS ********************************************* ****************************************************************************/ static gint dbus_cb_default(const gchar *interface, const gchar *method, GArray *arguments, gpointer data, osso_rpc_t *retval); static void osso_cb_hw_state(osso_hw_state_t *state, gpointer data); static gboolean window_cb_key_press(GtkWidget* widget, GdkEventKey *event); static gboolean window_cb_key_release(GtkWidget* widget, GdkEventKey *event); static gboolean map_cb_configure(GtkWidget *widget, GdkEventConfigure *event); static gboolean map_cb_expose(GtkWidget *widget, GdkEventExpose *event); static gboolean map_cb_button_press(GtkWidget *widget, GdkEventButton *event); static gboolean map_cb_button_release(GtkWidget *widget, GdkEventButton *event); static gboolean channel_cb_error(GIOChannel *src, GIOCondition condition, gpointer data); static gboolean channel_cb_connect(GIOChannel *src, GIOCondition condition, gpointer data); static gboolean channel_cb_input(GIOChannel *src, GIOCondition condition, gpointer data); static gboolean menu_cb_route_download(GtkAction *action); static gboolean menu_cb_route_open(GtkAction *action); static gboolean menu_cb_route_save(GtkAction *action); static gboolean menu_cb_route_reset(GtkAction *action); static gboolean menu_cb_route_clear(GtkAction *action); static gboolean menu_cb_track_open(GtkAction *action); static gboolean menu_cb_track_save(GtkAction *action); static gboolean menu_cb_track_mark_way(GtkAction *action); static gboolean menu_cb_track_clear(GtkAction *action); static gboolean menu_cb_show_tracks(GtkAction *action); static gboolean menu_cb_show_routes(GtkAction *action); static gboolean menu_cb_show_velvec(GtkAction *action); static gboolean menu_cb_maps_dlroute(GtkAction *action); static gboolean menu_cb_maps_dlarea(GtkAction *action); static gboolean menu_cb_auto_download(GtkAction *action); static gboolean menu_cb_ac_latlon(GtkAction *action); static gboolean menu_cb_ac_lead(GtkAction *action); static gboolean menu_cb_ac_none(GtkAction *action); static gboolean cmenu_cb_loc_show_latlon(GtkAction *action); static gboolean cmenu_cb_loc_clip_latlon(GtkAction *action); static gboolean cmenu_cb_loc_route_to(GtkAction *action); static gboolean cmenu_cb_loc_distance_to(GtkAction *action); static gboolean cmenu_cb_way_show_latlon(GtkAction *action); static gboolean cmenu_cb_way_show_desc(GtkAction *action); static gboolean cmenu_cb_way_clip_latlon(GtkAction *action); static gboolean cmenu_cb_way_clip_desc(GtkAction *action); static gboolean cmenu_cb_way_route_to(GtkAction *action); static gboolean cmenu_cb_way_distance_to(GtkAction *action); static gboolean cmenu_cb_way_delete(GtkAction *action); static gboolean menu_cb_fullscreen(GtkAction *action); static gboolean menu_cb_enable_gps(GtkAction *action); static gboolean menu_cb_settings(GtkAction *action); static gboolean menu_cb_help(GtkAction *action); static gint map_download_cb_async(GnomeVFSAsyncHandle *handle, GnomeVFSXferProgressInfo *info, ProgressUpdateInfo *pui); static gboolean auto_route_dl_idle_refresh(); static gboolean auto_route_dl_idle(); /**************************************************************************** * ABOVE: CALLBACK DECLARATIONS ********************************************* ****************************************************************************/ /**************************************************************************** * BELOW: ROUTINES ********************************************************** ****************************************************************************/ static gfloat calculate_distance(gfloat lat1, gfloat lon1, gfloat lat2, gfloat lon2) { gfloat dlat, dlon, slat, slon, a; /* Convert to radians. */ lat1 *= (PI / 180.f); lon1 *= (PI / 180.f); lat2 *= (PI / 180.f); lon2 *= (PI / 180.f); dlat = lat2 - lat1; dlon = lon2 - lon1; slat = sinf(dlat / 2.f); slon = sinf(dlon / 2.f); a = (slat * slat) + (cosf(lat1) * cosf(lat2) * slon * slon); return (2.f * atan2f(sqrtf(a), sqrtf(1.f - a))) * (!strcmp(_units, UNITS_MI) ? EARTH_RADIUS_MI : !strcmp(_units, UNITS_NM) ? EARTH_RADIUS_NM : EARTH_RADIUS_KM); } /** * Pop up a modal dialog box with simple error information in it. */ static void popup_error(GtkWidget *window, const gchar *error) { GtkWidget *dialog; printf("%s(\"%s\")\n", __PRETTY_FUNCTION__, error); dialog = hildon_note_new_information(GTK_WINDOW(window), error); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Set the connection state. This function controls all connection-related * banners. */ static void set_conn_state(ConnState new_conn_state) { printf("%s(%d)\n", __PRETTY_FUNCTION__, new_conn_state); switch(_conn_state = new_conn_state) { case RCVR_OFF: case RCVR_FIXED: if(_connect_banner) { gtk_widget_destroy(_connect_banner); _connect_banner = NULL; } if(_fix_banner) { gtk_widget_destroy(_fix_banner); _fix_banner = NULL; } break; case RCVR_DOWN: if(_fix_banner) { gtk_widget_destroy(_fix_banner); _fix_banner = NULL; } if(!_connect_banner) _connect_banner = hildon_banner_show_animation( _window, NULL, _("Searching for GPS receiver")); break; case RCVR_UP: if(_connect_banner) { gtk_widget_destroy(_connect_banner); _connect_banner = NULL; } if(!_fix_banner) _fix_banner = hildon_banner_show_progress( _window, NULL, _("Establishing GPS fix")); break; default: ; /* to quell warning. */ } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Updates _near_point, _next_way, and _next_wpt. If quick is FALSE (as * it is when this function is called from route_find_nearest_point), then * the entire list (starting from _near_point) is searched. Otherwise, we * stop searching when we find a point that is farther away. */ static gboolean route_update_nears(gboolean quick) { gboolean ret = FALSE; Point *curr, *near; WayPoint *wcurr, *wnext; guint near_dist_rough; printf("%s(%d)\n", __PRETTY_FUNCTION__, quick); /* If we have waypoints (_next_way != NULL), then determine the "next * waypoint", which is defined as the waypoint after the nearest point, * UNLESS we've passed that waypoint, in which case the waypoint after * that waypoint becomes the "next" waypoint. */ if(_next_way) { /* First, set near_dist_rough with the new distance from _near_point. */ near = _near_point; near_dist_rough = DISTANCE_ROUGH(_pos, *near); /* Now, search _route for a closer point. If quick is TRUE, then we'll * only search forward, only as long as we keep finding closer points. */ for(curr = _near_point; curr++ != _route.tail; ) { if(curr->unity) { guint dist_rough = DISTANCE_ROUGH(_pos, *curr); if(dist_rough <= near_dist_rough) { near = curr; near_dist_rough = dist_rough; } else if(quick) break; } } /* Update _near_point. */ _near_point = near; _near_point_dist_rough = near_dist_rough; for(wnext = wcurr = _next_way; wcurr != _route.wtail; wcurr++) { if(wcurr->point < near /* Okay, this else if expression warrants explanation. If the * nearest track point happens to be a waypoint, then we want to * check if we have "passed" that waypoint. To check this, we * test the distance from _pos to the waypoint and from _pos to * _next_wpt, and if the former is increasing and the latter is * decreasing, then we have passed the waypoint, and thus we * should skip it. Note that if there is no _next_wpt, then * there is no next waypoint, so we do not skip it in that case. */ || (wcurr->point == near && quick && (_next_wpt && (DISTANCE_ROUGH(_pos, *near) > _next_way_dist_rough && DISTANCE_ROUGH(_pos, *_next_wpt) < _next_wpt_dist_rough)))) wnext = wcurr + 1; else break; } if(wnext == _route.wtail && (wnext->point < near || (wnext->point == near && quick && (!_next_wpt || (DISTANCE_ROUGH(_pos, *near) > _next_way_dist_rough &&DISTANCE_ROUGH(_pos, *_next_wpt) < _next_wpt_dist_rough))))) { _next_way = NULL; _next_wpt = NULL; _next_way_dist_rough = -1; _next_wpt_dist_rough = -1; ret = TRUE; } /* Only update _next_way (and consequently _next_wpt) if _next_way is * different, and record that fact for return. */ else { if(!quick || _next_way != wnext) { _next_way = wnext; _next_wpt = wnext->point; if(_next_wpt == _route.tail) _next_wpt = NULL; else { while(!(++_next_wpt)->unity) { if(_next_wpt == _route.tail) { _next_wpt = NULL; break; } } } ret = TRUE; } _next_way_dist_rough = DISTANCE_ROUGH(_pos, *wnext->point); if(_next_wpt) _next_wpt_dist_rough = DISTANCE_ROUGH(_pos, *_next_wpt); } } vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, ret); return ret; } /** * Reset the _near_point data by searching the entire route for the nearest * route point and waypoint. */ static void route_find_nearest_point() { printf("%s()\n", __PRETTY_FUNCTION__); /* Initialize _near_point. */ _near_point = _route.head; while(!_near_point->unity && _near_point != _route.tail) _near_point++; /* Initialize _next_way. */ if(_route.wtail == _route.whead - 1 || (_autoroute_data.enabled && _route.wtail == _route.whead)) _next_way = NULL; else /* We have at least one waypoint. */ _next_way = (_autoroute_data.enabled ? _route.whead + 1 : _route.whead); _next_way_dist_rough = -1; /* Initialize _next_wpt. */ _next_wpt = NULL; _next_wpt_dist_rough = -1; route_update_nears(FALSE); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Render a single track line to _map_pixmap. If either point on the line * is a break (defined as unity == 0), a circle is drawn at the other point. * IT IS AN ERROR FOR BOTH POINTS TO INDICATE A BREAK. */ static void map_render_segment(GdkGC *gc_norm, GdkGC *gc_alt, guint unitx1, guint unity1, guint unitx2, guint unity2) { vprintf("%s()\n", __PRETTY_FUNCTION__); if(!unity1) { guint x2, y2; x2 = unit2bufx(unitx2); y2 = unit2bufy(unity2); /* Make sure this circle will be visible. */ if((x2 < BUF_WIDTH_PIXELS) && (y2 < BUF_HEIGHT_PIXELS)) gdk_draw_arc(_map_pixmap, gc_alt, FALSE, /* FALSE: not filled. */ x2 - _draw_line_width, y2 - _draw_line_width, 2 * _draw_line_width, 2 * _draw_line_width, 0, /* start at 0 degrees. */ 360 * 64); } else if(!unity2) { guint x1, y1; x1 = unit2bufx(unitx1); y1 = unit2bufy(unity1); /* Make sure this circle will be visible. */ if((x1 < BUF_WIDTH_PIXELS) && ((unsigned)y1 < BUF_HEIGHT_PIXELS)) gdk_draw_arc(_map_pixmap, gc_alt, FALSE, /* FALSE: not filled. */ x1 - _draw_line_width, y1 - _draw_line_width, 2 * _draw_line_width, 2 * _draw_line_width, 0, /* start at 0 degrees. */ 360 * 64); } else { gint x1, y1, x2, y2; x1 = unit2bufx(unitx1); y1 = unit2bufy(unity1); x2 = unit2bufx(unitx2); y2 = unit2bufy(unity2); /* Make sure this line could possibly be visible. */ if(!((x1 > BUF_WIDTH_PIXELS && x2 > BUF_WIDTH_PIXELS) || (x1 < 0 && x2 < 0) || (y1 > BUF_HEIGHT_PIXELS && y2 > BUF_HEIGHT_PIXELS) || (y1 < 0 && y2 < 0))) gdk_draw_line(_map_pixmap, gc_norm, x1, y1, x2, y2); } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Render all track data onto the _map_pixmap. Note that this does not * clear the pixmap of previous track data (use map_force_redraw() for * that), and also note that this method does not queue any redraws, so it * is up to the caller to decide which part of the track really needs to be * redrawn. */ static void map_render_track() { TrackPoint *curr; printf("%s()\n", __PRETTY_FUNCTION__); for(curr = _track.head; curr != _track.tail; curr++) map_render_segment(_gc_track, _gc_track_break, curr->point.unitx, curr->point.unity, curr[1].point.unitx, curr[1].point.unity); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Render all track data onto the _map_pixmap. Note that this does not * clear the pixmap of previous track data (use map_force_redraw() for * that), and also note that this method does not queue any redraws, so it * is up to the caller to decide which part of the track really needs to be * redrawn. */ static void map_render_route() { Point *curr; WayPoint *wcurr; printf("%s()\n", __PRETTY_FUNCTION__); _visible_way_first = _visible_way_last = NULL; for(curr = _route.head, wcurr = _route.whead; curr != _route.tail; curr++) { if(wcurr <= _route.wtail && curr == wcurr->point) { guint x1 = unit2bufx(wcurr->point->unitx); guint y1 = unit2bufy(wcurr->point->unity); if((x1 < BUF_WIDTH_PIXELS) && (y1 < BUF_HEIGHT_PIXELS)) { gdk_draw_arc(_map_pixmap, (wcurr==_next_way ? _gc_route_nextway : _gc_route_way), FALSE, /* FALSE: not filled. */ x1 - _draw_line_width, y1 - _draw_line_width, 2 * _draw_line_width, 2 * _draw_line_width, 0, /* start at 0 degrees. */ 360 * 64); if(!_visible_way_first) _visible_way_first = wcurr; _visible_way_last = wcurr; } wcurr++; if(!curr[1].unity) continue; } map_render_segment(_gc_route, _gc_route_way, curr->unitx, curr->unity, curr[1].unitx, curr[1].unity); } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } static void map_render_paths() { printf("%s()\n", __PRETTY_FUNCTION__); if((_show_tracks & ROUTES_MASK) && _route.head) { map_render_route(); if(_next_way == NULL) { guint x1 = unit2bufx(_route.tail[-1].unitx); guint y1 = unit2bufy(_route.tail[-1].unity); if((x1 < BUF_WIDTH_PIXELS) && (y1 < BUF_HEIGHT_PIXELS)) { gdk_draw_arc(_map_pixmap, _gc_route_nextway, FALSE, /* FALSE: not filled. */ x1 - _draw_line_width, y1 - _draw_line_width, 2 * _draw_line_width, 2 * _draw_line_width, 0, /* start at 0 degrees. */ 360 * 64); } } } if(_show_tracks & TRACKS_MASK) map_render_track(); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } static void track_resize(Track *track, guint size) { printf("%s()\n", __PRETTY_FUNCTION__); if(track->head + size != track->cap) { TrackPoint *old_head = track->head; track->head = g_renew(TrackPoint, old_head, size); track->cap = track->head + size; if(track->head != old_head) track->tail = track->head + (track->tail - old_head); } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } static void route_resize(Route *route, guint size) { printf("%s()\n", __PRETTY_FUNCTION__); if(route->head + size != route->cap) { Point *old_head = route->head; WayPoint *curr; route->head = g_renew(Point, old_head, size); route->cap = route->head + size; if(route->head != old_head) { route->tail = route->head + (route->tail - old_head); /* Adjust all of the waypoints. */ for(curr = route->whead - 1; curr++ != route->wtail; ) curr->point = route->head + (curr->point - old_head); } } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } static void route_wresize(Route *route, guint wsize) { printf("%s()\n", __PRETTY_FUNCTION__); if(route->whead + wsize != route->wcap) { WayPoint *old_whead = route->whead; route->whead = g_renew(WayPoint, old_whead, wsize); route->wtail = route->whead + (route->wtail - old_whead); route->wcap = route->whead + wsize; } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } #define MACRO_TRACK_INCREMENT_TAIL(track) { \ if(++(track).tail == (track).cap) \ track_resize(&(track), (track).cap - (track).head + ARRAY_CHUNK_SIZE); \ } #define MACRO_ROUTE_INCREMENT_TAIL(route) { \ if(++(route).tail == (route).cap) \ route_resize(&(route), (route).cap - (route).head + ARRAY_CHUNK_SIZE); \ } #define MACRO_ROUTE_INCREMENT_WTAIL(route) { \ if(++(route).wtail == (route).wcap) \ route_wresize(&(route), \ (route).wcap - (route).whead + ARRAY_CHUNK_SIZE); \ } /** * Add a point to the _track list. This function is slightly overloaded, * since it is what houses the check for "have we moved * significantly": it also initiates the re-calculation of the _near_point * data, as well as calling osso_display_state_on() when we have the focus. * * If a non-zero time is given, then the current position (as taken from the * _pos variable) is appended to _track with the given time. If time is zero, * then _pos_null is appended to _track with time zero (this produces a "break" * in the track). */ static void track_add(time_t time, gboolean newly_fixed) { Point pos = (time == 0 ? _pos_null : _pos); printf("%s(%u, %u)\n", __PRETTY_FUNCTION__, pos.unitx, pos.unity); if(abs((gint)pos.unitx - _track.tail->point.unitx) > _draw_line_width || abs((gint)pos.unity - _track.tail->point.unity) > _draw_line_width) { if(time && _route.head && (newly_fixed ? (route_find_nearest_point(), TRUE) : route_update_nears(TRUE))) { map_render_paths(); MACRO_QUEUE_DRAW_AREA(); } if(_show_tracks & TRACKS_MASK) { /* Instead of calling map_render_paths(), we'll draw the new line * ourselves and call gtk_widget_queue_draw_area(). */ gint tx1, ty1, tx2, ty2; map_render_segment(_gc_track, _gc_track_break, _track.tail->point.unitx, _track.tail->point.unity, pos.unitx, pos.unity); if(time && _track.tail->point.unity) { tx1 = unit2x(_track.tail->point.unitx); ty1 = unit2y(_track.tail->point.unity); tx2 = unit2x(pos.unitx); ty2 = unit2y(pos.unity); gtk_widget_queue_draw_area(_map_widget, MIN(tx1, tx2) - _draw_line_width, MIN(ty1, ty2) - _draw_line_width, abs(tx1 - tx2) + (2 * _draw_line_width), abs(ty1 - ty2) + (2 * _draw_line_width)); } } MACRO_TRACK_INCREMENT_TAIL(_track); _track.tail->point = pos; _track.tail->time = time; if(_autoroute_data.enabled && !_autoroute_data.in_progress && _near_point_dist_rough > 400) { _autoroute_data.in_progress = TRUE; g_idle_add((GSourceFunc)auto_route_dl_idle, NULL); } /* Keep the display on. */ KEEP_DISPLAY_ON(); } /* Check if we should announce upcoming waypoints. */ if(time && _next_way_dist_rough < (10 + (guint)_speed) * _announce_notice_ratio * 5) { if(_enable_voice && strcmp(_next_way->desc, _last_spoken_phrase)) { g_free(_last_spoken_phrase); _last_spoken_phrase = g_strdup(_next_way->desc); printf("Sending to flite: %s\n", _last_spoken_phrase); hildon_play_system_sound( "/usr/share/sounds/ui-information_note.wav"); osso_rpc_async_run(_osso, "com.nokia.flite", "/com/nokia/flite", "com.nokia.flite", "flite_tts", NULL, NULL, DBUS_TYPE_UINT32, getpid(), DBUS_TYPE_STRING, _last_spoken_phrase, DBUS_TYPE_UINT32, 18, DBUS_TYPE_DOUBLE, _voice_speed, DBUS_TYPE_DOUBLE, _voice_pitch, DBUS_TYPE_STRING, g_strdup("cmu_us_kal"), DBUS_TYPE_INVALID); } hildon_banner_show_information(_window, NULL, _next_way->desc); } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Disconnect from the receiver. This method cleans up any and everything * that might be associated with the receiver. */ static void rcvr_disconnect() { printf("%s()\n", __PRETTY_FUNCTION__); /* Remove watches. */ if(_clater_sid) { g_source_remove(_clater_sid); _clater_sid = 0; } if(_error_sid) { g_source_remove(_error_sid); _error_sid = 0; } if(_connect_sid) { g_source_remove(_connect_sid); _connect_sid = 0; } if(_input_sid) { g_source_remove(_input_sid); _input_sid = 0; } /* Destroy the GIOChannel object. */ if(_channel) { g_io_channel_shutdown(_channel, FALSE, NULL); g_io_channel_unref(_channel); _channel = NULL; } /* Close the file descriptor. */ if(_fd != -1) { close(_fd); _fd = -1; } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } static void rcvr_connect_later(); /* Forward declaration. */ /** * Connect to the receiver. * This method assumes that _fd is -1 and _channel is NULL. If unsure, call * rcvr_disconnect() first. * Since this is an idle function, this function returns whether or not it * should be called again, which is always FALSE. */ static gboolean rcvr_connect_now() { printf("%s(%d)\n", __PRETTY_FUNCTION__, _conn_state); if(_conn_state == RCVR_DOWN && _rcvr_mac) { #ifndef DEBUG /* Create the file descriptor. */ if(*_rcvr_mac == '/') _fd = open(_rcvr_mac, O_RDONLY); else _fd = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM); /* If file descriptor creation failed, try again later. Note that * there is no need to call rcvr_disconnect() because the file * descriptor creation is the first step, so if it fails, there's * nothing to clean up. */ if(_fd == -1) rcvr_connect_later(); else { _channel = g_io_channel_unix_new(_fd); g_io_channel_set_flags(_channel, G_IO_FLAG_NONBLOCK, NULL); _error_sid = g_io_add_watch_full(_channel, G_PRIORITY_HIGH_IDLE, G_IO_ERR | G_IO_HUP, channel_cb_error, NULL, NULL); _connect_sid = g_io_add_watch_full(_channel, G_PRIORITY_HIGH_IDLE, G_IO_OUT, channel_cb_connect, NULL, NULL); if(*_rcvr_mac != '/' && connect(_fd, (struct sockaddr*)&_rcvr_addr, sizeof(_rcvr_addr)) && errno != EAGAIN) { /* Connection failed. Disconnect and try again later. */ rcvr_disconnect(); rcvr_connect_later(); } } #else /* We're in DEBUG mode, so instead of connecting, skip to FIXED. */ set_conn_state(RCVR_FIXED); #endif } _clater_sid = 0; vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } /** * Place a request to connect about 1 second after the function is called. */ static void rcvr_connect_later() { printf("%s()\n", __PRETTY_FUNCTION__); _clater_sid = g_timeout_add(1000, (GSourceFunc)rcvr_connect_now, NULL); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Convert the float lat/lon/speed/heading data into integer units. */ static void integerize_data() { gfloat tmp; printf("%s()\n", __PRETTY_FUNCTION__); latlon2unit(_pos_lat, _pos_lon, _pos.unitx, _pos.unity); tmp = (_heading * (1.f / 180.f)) * PI; _vel_offsetx = (gint)(floorf(_speed * sinf(tmp) + 0.5f)); _vel_offsety = -(gint)(floorf(_speed * cosf(tmp) + 0.5f)); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Update all GdkGC objects to reflect the current _draw_line_width. */ #define UPDATE_GC(gc) \ gdk_gc_set_line_attributes(gc, \ _draw_line_width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); static void update_gcs() { printf("%s()\n", __PRETTY_FUNCTION__); gdk_color_alloc(gtk_widget_get_colormap(_map_widget), &_color_mark); gdk_color_alloc(gtk_widget_get_colormap(_map_widget), &_color_mark_velocity); gdk_color_alloc(gtk_widget_get_colormap(_map_widget), &_color_mark_old); gdk_color_alloc(gtk_widget_get_colormap(_map_widget), &_color_track); gdk_color_alloc(gtk_widget_get_colormap(_map_widget), &_color_track_break); gdk_color_alloc(gtk_widget_get_colormap(_map_widget), &_color_route); gdk_color_alloc(gtk_widget_get_colormap(_map_widget), &_color_route_way); gdk_color_alloc(gtk_widget_get_colormap(_map_widget), &_color_route_nextway); if(_gc_mark) { g_object_unref(_gc_mark); g_object_unref(_gc_mark_velocity); g_object_unref(_gc_mark_old); g_object_unref(_gc_track); g_object_unref(_gc_track_break); g_object_unref(_gc_route); g_object_unref(_gc_route_way); g_object_unref(_gc_route_nextway); } /* _gc_mark is used to draw the mark when data is current. */ _gc_mark = gdk_gc_new(_map_pixmap); gdk_gc_set_foreground(_gc_mark, &_color_mark); gdk_gc_set_line_attributes(_gc_mark, _draw_line_width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); /* _gc_mark_old is used to draw the mark when data is old. */ _gc_mark_old = gdk_gc_new(_map_pixmap); gdk_gc_set_foreground(_gc_mark_old, &_color_mark_old); gdk_gc_set_line_attributes(_gc_mark_old, _draw_line_width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); /* _gc_mark_velocity is used to draw the vel_current line. */ _gc_mark_velocity = gdk_gc_new(_map_pixmap); gdk_gc_set_foreground(_gc_mark_velocity, &_color_mark_velocity); gdk_gc_set_line_attributes(_gc_mark_velocity, _draw_line_width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); /* _gc_track is used to draw the track line. */ _gc_track = gdk_gc_new(_map_pixmap); gdk_gc_set_foreground(_gc_track, &_color_track); gdk_gc_set_line_attributes(_gc_track, _draw_line_width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); /* _gc_track_break is used to draw the track_break dots. */ _gc_track_break = gdk_gc_new(_map_pixmap); gdk_gc_set_foreground(_gc_track_break, &_color_track_break); gdk_gc_set_line_attributes(_gc_track_break, _draw_line_width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); /* _gc_route is used to draw the route line. */ _gc_route = gdk_gc_new(_map_pixmap); gdk_gc_set_foreground(_gc_route, &_color_route); gdk_gc_set_line_attributes(_gc_route, _draw_line_width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); /* _way_gc is used to draw the waypoint dots. */ _gc_route_way = gdk_gc_new(_map_pixmap); gdk_gc_set_foreground(_gc_route_way, &_color_route_way); gdk_gc_set_line_attributes(_gc_route_way, _draw_line_width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); /* _gc_route_nextway is used to draw the next_way labels. */ _gc_route_nextway = gdk_gc_new(_map_pixmap); gdk_gc_set_foreground(_gc_route_nextway, &_color_route_nextway); gdk_gc_set_line_attributes(_gc_route_nextway, _draw_line_width, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Change the map cache directory and update dependent variables. */ static gboolean config_set_map_dir_name(gchar *new_map_dir_name) { GnomeVFSURI *map_dir_uri; gboolean retval = FALSE; printf("%s()\n", __PRETTY_FUNCTION__); /* Rest of the function devoted to making sure the directory exists. */ map_dir_uri = gnome_vfs_uri_new(new_map_dir_name); if(!gnome_vfs_uri_exists(map_dir_uri)) { GnomeVFSURI *parent, *curr_uri; GList *list = NULL; list = g_list_prepend(list, curr_uri = map_dir_uri); while(GNOME_VFS_ERROR_NOT_FOUND == gnome_vfs_make_directory_for_uri( parent = gnome_vfs_uri_get_parent(curr_uri), 0755)) list = g_list_prepend(list, curr_uri = parent); while(list != NULL) { retval = (GNOME_VFS_OK == gnome_vfs_make_directory_for_uri( (GnomeVFSURI*)list->data, 0755)); gnome_vfs_uri_unref((GnomeVFSURI*)list->data); list = g_list_remove(list, list->data); } /* Retval now equals result of last make-dir attempt. */ } else retval = TRUE; if(retval) { if(_map_dir_name) g_free(_map_dir_name); _map_dir_name = new_map_dir_name; } vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, retval); return retval; } /** * Save all configuration data to GCONF. */ static void config_save() { GConfClient *gconf_client = gconf_client_get_default(); gchar buffer[16]; printf("%s()\n", __PRETTY_FUNCTION__); if(!gconf_client) { popup_error(_window, _("Failed to initialize GConf. Settings were not saved.")); return; } /* Save Receiver MAC from GConf. */ if(_rcvr_mac) gconf_client_set_string(gconf_client, GCONF_KEY_RCVR_MAC, _rcvr_mac, NULL); else gconf_client_unset(gconf_client, GCONF_KEY_RCVR_MAC, NULL); /* Save Receiver Channel to GConf. */ gconf_client_set_int(gconf_client, GCONF_KEY_RCVR_CHAN, _rcvr_addr.rc_channel, NULL); /* Save Map Download URI Format. */ if(_map_uri_format) gconf_client_set_string(gconf_client, GCONF_KEY_MAP_URI_FORMAT, _map_uri_format, NULL); if(_map_uri_format_1) gconf_client_set_string(gconf_client, GCONF_KEY_MAP_URI_FORMAT_1, _map_uri_format_1, NULL); if(_map_uri_format_2) gconf_client_set_string(gconf_client, GCONF_KEY_MAP_URI_FORMAT_2, _map_uri_format_2, NULL); /* Save Map Download Zoom Steps. */ gconf_client_set_int(gconf_client, GCONF_KEY_MAP_ZOOM_STEPS, _zoom_steps, NULL); /* Save Map Cache Directory. */ if(_map_dir_name) gconf_client_set_string(gconf_client, GCONF_KEY_MAP_DIR_NAME, _map_dir_name, NULL); if(_map_dir_name_1) gconf_client_set_string(gconf_client, GCONF_KEY_MAP_DIR_NAME_1, _map_dir_name_1, NULL); if(_map_dir_name_2) gconf_client_set_string(gconf_client, GCONF_KEY_MAP_DIR_NAME_2, _map_dir_name_2, NULL); /* Save setting 1/2 */ gconf_client_set_int(gconf_client, GCONF_KEY_MAP_SETTING, _map_setting, NULL); /* Save Auto-Download. */ gconf_client_set_bool(gconf_client, GCONF_KEY_AUTO_DOWNLOAD, _auto_download, NULL); /* Save Auto-Center Sensitivity. */ gconf_client_set_int(gconf_client, GCONF_KEY_CENTER_SENSITIVITY, _center_ratio, NULL); /* Save Auto-Center Lead Amount. */ gconf_client_set_int(gconf_client, GCONF_KEY_LEAD_AMOUNT, _lead_ratio, NULL); /* Save Draw Line Width. */ gconf_client_set_int(gconf_client, GCONF_KEY_DRAW_LINE_WIDTH, _draw_line_width, NULL); /* Save Announce Advance Notice Ratio. */ gconf_client_set_int(gconf_client, GCONF_KEY_ANNOUNCE_NOTICE, _announce_notice_ratio, NULL); /* Save Enable Voice flag. */ gconf_client_set_bool(gconf_client, GCONF_KEY_ENABLE_VOICE, _enable_voice, NULL); /* Save Keep On When Fullscreen flag. */ gconf_client_set_bool(gconf_client, GCONF_KEY_ALWAYS_KEEP_ON, _always_keep_on, NULL); /* Save Units. */ gconf_client_set_string(gconf_client, GCONF_KEY_UNITS, _units, NULL); /* Save last saved latitude. */ gconf_client_set_float(gconf_client, GCONF_KEY_LAT, _pos_lat, NULL); /* Save last saved longitude. */ gconf_client_set_float(gconf_client, GCONF_KEY_LON, _pos_lon, NULL); /* Save last center point. */ { gfloat center_lat, center_lon; unit2latlon(_center.unitx, _center.unity, center_lat, center_lon); /* Save last center latitude. */ gconf_client_set_float(gconf_client, GCONF_KEY_CENTER_LAT, center_lat, NULL); /* Save last center longitude. */ gconf_client_set_float(gconf_client, GCONF_KEY_CENTER_LON, center_lon, NULL); } /* Save last Zoom Level. */ gconf_client_set_int(gconf_client, GCONF_KEY_ZOOM, _zoom, NULL); /* Save Route Directory. */ if(_route_dir_uri) gconf_client_set_string(gconf_client, GCONF_KEY_ROUTEDIR, _route_dir_uri, NULL); /* Save Last Track File. */ if(_track_file_uri) gconf_client_set_string(gconf_client, GCONF_KEY_TRACKFILE, _track_file_uri, NULL); /* Save Auto-Center Mode. */ gconf_client_set_int(gconf_client, GCONF_KEY_AUTOCENTER_MODE, _center_mode, NULL); /* Save Show Tracks flag. */ gconf_client_set_bool(gconf_client, GCONF_KEY_SHOWTRACKS, _show_tracks & TRACKS_MASK, NULL); /* Save Show Routes flag. */ gconf_client_set_bool(gconf_client, GCONF_KEY_SHOWROUTES, _show_tracks & ROUTES_MASK, NULL); /* Save Show Velocity Vector flag. */ gconf_client_set_bool(gconf_client, GCONF_KEY_SHOWVELVEC, _show_velvec, NULL); /* Save Enable GPS flag. */ gconf_client_set_bool(gconf_client, GCONF_KEY_ENABLE_GPS, _enable_gps, NULL); /* Save Route Locations. */ gconf_client_set_list(gconf_client, GCONF_KEY_ROUTE_LOCATIONS, GCONF_VALUE_STRING, _loc_list, NULL); /* Save Colors. */ sprintf(buffer, "#%02x%02x%02x", _color_mark.red >> 8, _color_mark.green >> 8, _color_mark.blue >> 8); gconf_client_set_string(gconf_client, GCONF_KEY_COLOR_MARK, buffer, NULL); sprintf(buffer, "#%02x%02x%02x", _color_mark_velocity.red >> 8, _color_mark_velocity.green >> 8, _color_mark_velocity.blue >> 8); gconf_client_set_string(gconf_client, GCONF_KEY_COLOR_MARK_VELOCITY, buffer, NULL); sprintf(buffer, "#%02x%02x%02x", _color_mark_old.red >> 8, _color_mark_old.green >> 8, _color_mark_old.blue >> 8); gconf_client_set_string(gconf_client, GCONF_KEY_COLOR_MARK_OLD, buffer, NULL); sprintf(buffer, "#%02x%02x%02x", _color_track.red >> 8, _color_track.green >> 8, _color_track.blue >> 8); gconf_client_set_string(gconf_client, GCONF_KEY_COLOR_TRACK, buffer, NULL); sprintf(buffer, "#%02x%02x%02x", _color_track_break.red >> 8, _color_track_break.green >> 8, _color_track_break.blue >> 8); gconf_client_set_string(gconf_client, GCONF_KEY_COLOR_TRACK_BREAK, buffer, NULL); sprintf(buffer, "#%02x%02x%02x", _color_route.red >> 8, _color_route.green >> 8, _color_route.blue >> 8); gconf_client_set_string(gconf_client, GCONF_KEY_COLOR_ROUTE, buffer, NULL); sprintf(buffer, "#%02x%02x%02x", _color_route_way.red >> 8, _color_route_way.green >> 8, _color_route_way.blue >> 8); gconf_client_set_string(gconf_client, GCONF_KEY_COLOR_ROUTE_WAY, buffer, NULL); sprintf(buffer, "#%02x%02x%02x", _color_route_nextway.red >> 8, _color_route_nextway.green >> 8, _color_route_nextway.blue >> 8); gconf_client_set_string(gconf_client, GCONF_KEY_COLOR_ROUTE_NEXTWAY, buffer, NULL); g_object_unref(gconf_client); hildon_banner_show_information(_window, NULL, "Config saved."); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } static void force_min_visible_bars(HildonControlbar *control_bar, gint num_bars) { GValue val; memset(&val, 0, sizeof(val)); g_value_init(&val, G_TYPE_INT); g_value_set_int(&val, num_bars); g_object_set_property(G_OBJECT(control_bar), "minimum-visible-bars", &val); } typedef struct _ScanInfo ScanInfo; struct _ScanInfo { GtkWidget *settings_dialog; GtkWidget *txt_rcvr_mac; GtkWidget *scan_dialog; GtkWidget *banner; GtkListStore *store; guint sid; }; static gboolean scan_bluetooth_idle(ScanInfo *scan_info) { gint devid, num_rsp; GtkTreeIter iter; inquiry_info *ii = NULL; devid = hci_get_route(NULL); ii = (inquiry_info*)malloc(255 * sizeof(inquiry_info)); num_rsp = hci_inquiry(devid, 4, 255, NULL, &ii, IREQ_CACHE_FLUSH); if(num_rsp < 0) { gtk_widget_destroy(scan_info->banner); gtk_widget_hide(scan_info->scan_dialog); popup_error(scan_info->settings_dialog, _("An error occurred while scanning.")); } else if(num_rsp == 0) { gtk_widget_destroy(scan_info->banner); gtk_widget_hide(scan_info->scan_dialog); popup_error(scan_info->settings_dialog, _("No bluetooth devices found.")); } else { guint i; gint sock = hci_open_dev(devid); for(i = 0; i < num_rsp; i++) { gchar addr[19] = { 0 }; gchar name[256] = { 0 }; ba2str(&ii[i].bdaddr, addr); memset(name, 0, sizeof(name)); if(hci_read_remote_name(sock, &ii[i].bdaddr, sizeof(name), name, 0)) strcpy(name, _("Unknown")); gtk_list_store_append(scan_info->store, &iter); gtk_list_store_set(scan_info->store, &iter, 0, g_strdup(addr), 1, g_strdup(name), -1); } close(sock); gtk_widget_destroy(scan_info->banner); } free(ii); scan_info->sid = 0; return FALSE; } /** * Scan for all bluetooth devices. This method can take a few seconds, * during which the UI will freeze. */ static gboolean scan_bluetooth(GtkWidget *widget, ScanInfo *scan_info) { /* Do an hci_inquiry for our boy. */ GtkWidget *dialog; GtkWidget *lst_devices; GtkTreeViewColumn *column; GtkCellRenderer *renderer; printf("%s()\n", __PRETTY_FUNCTION__); dialog = gtk_dialog_new_with_buttons(_("Select Bluetooth Device"), GTK_WINDOW(scan_info->settings_dialog), GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); scan_info->scan_dialog = dialog; scan_info->banner = hildon_banner_show_animation(dialog, NULL, _("Scanning Bluetooth Devices")); scan_info->store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); scan_info->sid = g_idle_add((GSourceFunc)scan_bluetooth_idle, scan_info); gtk_window_set_default_size(GTK_WINDOW(dialog), 500, 300); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), lst_devices = gtk_tree_view_new_with_model( GTK_TREE_MODEL(scan_info->store)), TRUE, TRUE, 0); g_object_unref(G_OBJECT(scan_info->store)); gtk_tree_selection_set_mode( gtk_tree_view_get_selection(GTK_TREE_VIEW(lst_devices)), GTK_SELECTION_SINGLE); gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(lst_devices), TRUE); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _("MAC"), renderer, "text", 0); gtk_tree_view_append_column(GTK_TREE_VIEW(lst_devices), column); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes( _("Description"), renderer, "text", 1); gtk_tree_view_append_column(GTK_TREE_VIEW(lst_devices), column); gtk_widget_show_all(dialog); while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) { GtkTreeIter iter; if(gtk_tree_selection_get_selected( gtk_tree_view_get_selection(GTK_TREE_VIEW(lst_devices)), NULL, &iter)) { gchar *mac; gtk_tree_model_get(GTK_TREE_MODEL(scan_info->store), &iter, 0, &mac, -1); gtk_entry_set_text(GTK_ENTRY(scan_info->txt_rcvr_mac), mac); break; } else popup_error(dialog, _("Please select a bluetooth device from the list.")); } if(scan_info->sid) g_source_remove(scan_info->sid); gtk_widget_destroy(dialog); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } typedef struct _BrowseInfo BrowseInfo; struct _BrowseInfo { GtkWidget *dialog; GtkWidget *txt_map_dir_name; }; static gboolean settings_dialog_browse(GtkWidget *widget, BrowseInfo *browse_info) { GtkWidget *dialog; printf("%s()\n", __PRETTY_FUNCTION__); dialog = GTK_WIDGET( hildon_file_chooser_dialog_new(GTK_WINDOW(browse_info->dialog), GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)); gtk_file_chooser_set_local_only(GTK_FILE_CHOOSER(dialog), TRUE); gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), gtk_entry_get_text(GTK_ENTRY(browse_info->txt_map_dir_name))); if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(dialog))) { gchar *filename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog)); gtk_entry_set_text(GTK_ENTRY(browse_info->txt_map_dir_name), filename); g_free(filename); } gtk_widget_destroy(dialog); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } typedef struct _ColorsDialogInfo ColorsDialogInfo; struct _ColorsDialogInfo { GtkWidget *col_mark; GtkWidget *col_mark_velocity; GtkWidget *col_mark_old; GtkWidget *col_track; GtkWidget *col_track_break; GtkWidget *col_route; GtkWidget *col_route_way; GtkWidget *col_route_nextway; }; static gboolean settings_dialog_colors_reset(GtkWidget *widget, ColorsDialogInfo *cdi) { hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi->col_mark), &DEFAULT_COLOR_MARK); hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi->col_mark_velocity), &DEFAULT_COLOR_MARK_VELOCITY); hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi->col_mark_old), &DEFAULT_COLOR_MARK_OLD); hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi->col_track), &DEFAULT_COLOR_TRACK); hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi->col_track_break), &DEFAULT_COLOR_TRACK_BREAK); hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi->col_route), &DEFAULT_COLOR_ROUTE); hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi->col_route_way), &DEFAULT_COLOR_ROUTE_WAY); hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi->col_route_nextway), &DEFAULT_COLOR_ROUTE_NEXTWAY); return TRUE; } static gboolean settings_dialog_colors(GtkWidget *widget, GtkWidget *parent) { GtkWidget *dialog; GtkWidget *table; GtkWidget *label; GtkWidget *btn_defaults; ColorsDialogInfo cdi; printf("%s()\n", __PRETTY_FUNCTION__); dialog = gtk_dialog_new_with_buttons(_("Colors"), GTK_WINDOW(parent), GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), btn_defaults = gtk_button_new_with_label(_("Defaults"))); g_signal_connect(G_OBJECT(btn_defaults), "clicked", G_CALLBACK(settings_dialog_colors_reset), &cdi); gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), table = gtk_table_new(4, 3, FALSE), TRUE, TRUE, 0); /* Mark. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("GPS Mark")), 0, 1, 0, 1, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), cdi.col_mark = hildon_color_button_new(), 1, 2, 0, 1, 0, 0, 2, 4); hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi.col_mark), &_color_mark); gtk_table_attach(GTK_TABLE(table), cdi.col_mark_velocity = hildon_color_button_new(), 2, 3, 0, 1, 0, 0, 2, 4); hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi.col_mark_velocity), &_color_mark_velocity); gtk_table_attach(GTK_TABLE(table), cdi.col_mark_old = hildon_color_button_new(), 3, 4, 0, 1, 0, 0, 2, 4); hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi.col_mark_old), &_color_mark_old); /* Track. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Track")), 0, 1, 1, 2, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), cdi.col_track = hildon_color_button_new(), 1, 2, 1, 2, 0, 0, 2, 4); hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi.col_track), &_color_track); gtk_table_attach(GTK_TABLE(table), cdi.col_track_break = hildon_color_button_new(), 2, 3, 1, 2, 0, 0, 2, 4); hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi.col_track_break), &_color_track_break); /* Route. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Route")), 0, 1, 2, 3, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), cdi.col_route = hildon_color_button_new(), 1, 2, 2, 3, 0, 0, 2, 4); hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi.col_route), &_color_route); gtk_table_attach(GTK_TABLE(table), cdi.col_route_way = hildon_color_button_new(), 2, 3, 2, 3, 0, 0, 2, 4); hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi.col_route_way), &_color_route_way); gtk_table_attach(GTK_TABLE(table), cdi.col_route_nextway = hildon_color_button_new(), 3, 4, 2, 3, 0, 0, 2, 4); hildon_color_button_set_color( HILDON_COLOR_BUTTON(cdi.col_route_nextway), &_color_route_nextway); gtk_widget_show_all(dialog); while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) { GdkColor *color; color = hildon_color_button_get_color( HILDON_COLOR_BUTTON(cdi.col_mark)); _color_mark = *color; color = hildon_color_button_get_color( HILDON_COLOR_BUTTON(cdi.col_mark_velocity)); _color_mark_velocity = *color; color = hildon_color_button_get_color( HILDON_COLOR_BUTTON(cdi.col_mark_old)); _color_mark_old = *color; color = hildon_color_button_get_color( HILDON_COLOR_BUTTON(cdi.col_track)); _color_track = *color; color = hildon_color_button_get_color( HILDON_COLOR_BUTTON(cdi.col_track_break)); _color_track_break = *color; color = hildon_color_button_get_color( HILDON_COLOR_BUTTON(cdi.col_route)); _color_route = *color; color = hildon_color_button_get_color( HILDON_COLOR_BUTTON(cdi.col_route_way)); _color_route_way = *color; color = hildon_color_button_get_color( HILDON_COLOR_BUTTON(cdi.col_route_nextway)); _color_route_nextway = *color; update_gcs(); break; } gtk_widget_destroy(dialog); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } /** * Bring up the Settings dialog. Return TRUE if and only if the recever * information has changed (MAC or channel). */ static gboolean settings_dialog() { GtkWidget *dialog; GtkWidget *notebook; GtkWidget *table; GtkWidget *hbox; GtkWidget *hbox2; GtkWidget *label; GtkWidget *txt_rcvr_mac; GtkWidget *num_rcvr_chan; GtkWidget *txt_map_uri_format_1; GtkWidget *txt_map_uri_format_2; GtkWidget *txt_map_dir_name_1; GtkWidget *txt_map_dir_name_2; GtkWidget *num_zoom_steps; GtkWidget *num_center_ratio; GtkWidget *num_lead_ratio; GtkWidget *num_announce_notice; GtkWidget *chk_enable_voice; GtkWidget *num_voice_speed; GtkWidget *num_voice_pitch; GtkWidget *num_draw_line_width; GtkWidget *chk_always_keep_on; GtkWidget *cmb_units; GtkWidget *btn_scan; GtkWidget *btn_browse_1; GtkWidget *btn_browse_2; GtkWidget *btn_colors; BrowseInfo browse_info_1 = {0, 0}; BrowseInfo browse_info_2 = {0, 0}; ScanInfo scan_info = {0}; gboolean rcvr_changed = FALSE; printf("%s()\n", __PRETTY_FUNCTION__); dialog = gtk_dialog_new_with_buttons(_("Maemo Mapper Settings"), GTK_WINDOW(_window), GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), btn_colors = gtk_button_new_with_label(_("Colors..."))); gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), notebook = gtk_notebook_new(), TRUE, TRUE, 0); /* Receiver page. */ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), table = gtk_table_new(2, 3, FALSE), label = gtk_label_new(_("GPS"))); /* Receiver MAC Address. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("MAC")), 0, 1, 0, 1, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), hbox = gtk_hbox_new(FALSE, 4), 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4); gtk_box_pack_start(GTK_BOX(hbox), txt_rcvr_mac = gtk_entry_new(), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), btn_scan = gtk_button_new_with_label(_("Scan...")), FALSE, FALSE, 0); /* Receiver Channel. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Channel")), 0, 1, 1, 2, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), label = gtk_alignment_new(0.f, 0.5f, 0.f, 0.f), 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4); gtk_container_add(GTK_CONTAINER(label), num_rcvr_chan = hildon_number_editor_new(0, 255)); /* Note!. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new( _("Note: \"Channel\" refers to the device side!")), 0, 2, 2, 3, GTK_FILL, 0, 2, 4); gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); gtk_misc_set_alignment(GTK_MISC(label), 0.5f, 0.5f); /* Maps page. */ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), table = gtk_table_new(2, 3, FALSE), label = gtk_label_new(_("Maps"))); /* Map download URI 1. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("URI Format 1")), 1, 2, 0, 1, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), txt_map_uri_format_1 = gtk_entry_new(), 2, 3, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4); /* Map Directory 1. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Cache Dir. 1")), 1, 2, 1, 2, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), hbox = gtk_hbox_new(FALSE, 4), 2, 3, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4); gtk_box_pack_start(GTK_BOX(hbox), txt_map_dir_name_1 = gtk_entry_new(), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), btn_browse_1 = gtk_button_new_with_label(_("Browse...")), FALSE, FALSE, 0); /* Map download URI 2. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("URI Format 2")), 1, 2, 2, 3, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), txt_map_uri_format_2 = gtk_entry_new(), 2, 3, 2, 3, GTK_EXPAND | GTK_FILL, 0, 2, 4); /* Map Directory 2. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Cache Dir. 2")), 1, 2, 3, 4, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), hbox = gtk_hbox_new(FALSE, 4), 2, 3, 3, 4, GTK_EXPAND | GTK_FILL, 0, 2, 4); gtk_box_pack_start(GTK_BOX(hbox), txt_map_dir_name_2 = gtk_entry_new(), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), btn_browse_2 = gtk_button_new_with_label(_("Browse...")), FALSE, FALSE, 0); /* Radio-Button 1/2*/ GtkWidget *radio1, *radio2, *entry; radio1 = gtk_radio_button_new_with_label(NULL, "1"); radio2 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(radio1), "2"); gtk_table_attach(GTK_TABLE(table), radio1, 0, 1, 0, 2, GTK_FILL, 0, 2, 4); gtk_table_attach(GTK_TABLE(table), radio2, 0, 1, 2, 4, GTK_EXPAND | GTK_FILL, 0, 2, 4); if (_map_setting == 1) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio1), TRUE); else gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(radio2), TRUE); /* Zoom Steps. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Zoom Steps")), 1, 2, 4, 5, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), label = gtk_alignment_new(0.f, 0.5f, 0.f, 0.f), 2, 3, 4, 5, GTK_EXPAND | GTK_FILL, 0, 2, 4); gtk_container_add(GTK_CONTAINER(label), num_zoom_steps = hildon_controlbar_new()); hildon_controlbar_set_range(HILDON_CONTROLBAR(num_zoom_steps), 1, 4); force_min_visible_bars(HILDON_CONTROLBAR(num_zoom_steps), 1); /* Auto-Center page. */ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), table = gtk_table_new(2, 2, FALSE), label = gtk_label_new(_("Auto-Center"))); /* Auto-Center Sensitivity. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Sensitivity")), 0, 1, 0, 1, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), label = gtk_alignment_new(0.f, 0.5f, 0.f, 0.f), 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4); gtk_container_add(GTK_CONTAINER(label), num_center_ratio = hildon_controlbar_new()); hildon_controlbar_set_range(HILDON_CONTROLBAR(num_center_ratio), 1, 10); force_min_visible_bars(HILDON_CONTROLBAR(num_center_ratio), 1); /* Lead Amount. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Lead Amount")), 0, 1, 1, 2, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), label = gtk_alignment_new(0.f, 0.5f, 0.f, 0.f), 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4); gtk_container_add(GTK_CONTAINER(label), num_lead_ratio = hildon_controlbar_new()); hildon_controlbar_set_range(HILDON_CONTROLBAR(num_lead_ratio), 1, 10); force_min_visible_bars(HILDON_CONTROLBAR(num_lead_ratio), 1); /* Announcement. */ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), table = gtk_table_new(2, 3, FALSE), label = gtk_label_new(_("Announce"))); /* Announcement Advance Notice. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Advance Notice")), 0, 1, 0, 1, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), num_announce_notice = hildon_controlbar_new(), 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4); hildon_controlbar_set_range(HILDON_CONTROLBAR(num_announce_notice), 1, 20); force_min_visible_bars(HILDON_CONTROLBAR(num_announce_notice), 1); /* Enable Voice. */ gtk_table_attach(GTK_TABLE(table), chk_enable_voice = gtk_check_button_new_with_label( _("Enable Voice Synthesis (requires flite)")), 0, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk_enable_voice), _enable_voice); /* Voice Speed and Pitch. */ gtk_table_attach(GTK_TABLE(table), hbox = gtk_hbox_new(FALSE, 12), 0, 2, 2, 3, 0, 0, 2, 6); gtk_box_pack_start(GTK_BOX(hbox), hbox2 = gtk_hbox_new(FALSE, 4), TRUE, TRUE, 4); gtk_box_pack_start(GTK_BOX(hbox2), label = gtk_label_new(_("Speed")), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox2), num_voice_speed = hildon_controlbar_new(), TRUE, TRUE, 0); hildon_controlbar_set_range(HILDON_CONTROLBAR(num_voice_speed), 1, 10); force_min_visible_bars(HILDON_CONTROLBAR(num_voice_speed), 1); gtk_box_pack_start(GTK_BOX(hbox), hbox2 = gtk_hbox_new(FALSE, 4), TRUE, TRUE, 4); gtk_box_pack_start(GTK_BOX(hbox2), label = gtk_label_new(_("Pitch")), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox2), num_voice_pitch = hildon_controlbar_new(), TRUE, TRUE, 0); hildon_controlbar_set_range(HILDON_CONTROLBAR(num_voice_pitch), -2, 8); force_min_visible_bars(HILDON_CONTROLBAR(num_voice_pitch), 1); /* Misc. page. */ gtk_notebook_append_page(GTK_NOTEBOOK(notebook), table = gtk_table_new(2, 3, FALSE), label = gtk_label_new(_("Misc."))); /* Line Width. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Line Width")), 0, 1, 0, 1, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), num_draw_line_width = hildon_controlbar_new(), 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4); hildon_controlbar_set_range(HILDON_CONTROLBAR(num_draw_line_width), 1, 20); force_min_visible_bars(HILDON_CONTROLBAR(num_draw_line_width), 1); /* Keep Display On Only When Fullscreen. */ gtk_table_attach(GTK_TABLE(table), chk_always_keep_on = gtk_check_button_new_with_label( _("Keep Display On Only in Fullscreen Mode")), 0, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4); /* Units. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Units")), 0, 1, 2, 3, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), label = gtk_alignment_new(0.f, 0.5f, 0.f, 0.f), 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, 0, 2, 4); gtk_container_add(GTK_CONTAINER(label), cmb_units = gtk_combo_box_new_text()); gtk_combo_box_append_text(GTK_COMBO_BOX(cmb_units), UNITS_KM); gtk_combo_box_append_text(GTK_COMBO_BOX(cmb_units), UNITS_MI); gtk_combo_box_append_text(GTK_COMBO_BOX(cmb_units), UNITS_NM); /* Connect signals. */ scan_info.settings_dialog = dialog; scan_info.txt_rcvr_mac = txt_rcvr_mac; g_signal_connect(G_OBJECT(btn_scan), "clicked", G_CALLBACK(scan_bluetooth), &scan_info); browse_info_1.dialog = dialog; browse_info_1.txt_map_dir_name = txt_map_dir_name_1; g_signal_connect(G_OBJECT(btn_browse_1), "clicked", G_CALLBACK(settings_dialog_browse), &browse_info_1); browse_info_2.dialog = dialog; browse_info_2.txt_map_dir_name = txt_map_dir_name_2; g_signal_connect(G_OBJECT(btn_browse_2), "clicked", G_CALLBACK(settings_dialog_browse), &browse_info_2); g_signal_connect(G_OBJECT(btn_colors), "clicked", G_CALLBACK(settings_dialog_colors), dialog); /* Initialize fields. */ if(_rcvr_mac) gtk_entry_set_text(GTK_ENTRY(txt_rcvr_mac), _rcvr_mac); hildon_number_editor_set_value(HILDON_NUMBER_EDITOR(num_rcvr_chan), _rcvr_addr.rc_channel); if(_map_uri_format_1) gtk_entry_set_text(GTK_ENTRY(txt_map_uri_format_1), _map_uri_format_1); if(_map_uri_format_2) gtk_entry_set_text(GTK_ENTRY(txt_map_uri_format_2), _map_uri_format_2); hildon_controlbar_set_value(HILDON_CONTROLBAR(num_zoom_steps), _zoom_steps); if(_map_dir_name_1) gtk_entry_set_text(GTK_ENTRY(txt_map_dir_name_1), _map_dir_name_1); if(_map_dir_name_2) gtk_entry_set_text(GTK_ENTRY(txt_map_dir_name_2), _map_dir_name_2); hildon_controlbar_set_value(HILDON_CONTROLBAR(num_center_ratio), _center_ratio); hildon_controlbar_set_value(HILDON_CONTROLBAR(num_lead_ratio), _lead_ratio); hildon_controlbar_set_value(HILDON_CONTROLBAR(num_announce_notice), _announce_notice_ratio); hildon_controlbar_set_value(HILDON_CONTROLBAR(num_voice_speed), (gint)(_voice_speed * 3 + 0.5)); hildon_controlbar_set_value(HILDON_CONTROLBAR(num_voice_pitch), _voice_pitch); hildon_controlbar_set_value(HILDON_CONTROLBAR(num_draw_line_width), _draw_line_width); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk_always_keep_on), !_always_keep_on); gtk_combo_box_set_active(GTK_COMBO_BOX(cmb_units), !strcmp(_units, UNITS_MI) ? 1 : !strcmp(_units, UNITS_NM) ? 2 : 0); gtk_widget_show_all(dialog); while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) { /* Set _rcvr_mac if necessary. */ if(!*gtk_entry_get_text(GTK_ENTRY(txt_rcvr_mac))) { /* User specified no rcvr mac - set _rcvr_mac to NULL. */ if(_rcvr_mac) { g_free(_rcvr_mac); _rcvr_mac = NULL; rcvr_changed = TRUE; } if(_enable_gps) { gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_enable_gps_item), FALSE); popup_error(dialog, _("No GPS Receiver MAC Provided.\n" "GPS Disabled.")); rcvr_changed = TRUE; } } else if(!_rcvr_mac || strcmp(_rcvr_mac, gtk_entry_get_text(GTK_ENTRY(txt_rcvr_mac)))) { /* User specified a new rcvr mac. */ if(_rcvr_mac) g_free(_rcvr_mac); _rcvr_mac = g_strdup(gtk_entry_get_text(GTK_ENTRY(txt_rcvr_mac))); str2ba(_rcvr_mac, &_rcvr_addr.rc_bdaddr); rcvr_changed = TRUE; } if(_rcvr_addr.rc_channel != hildon_number_editor_get_value( HILDON_NUMBER_EDITOR(num_rcvr_chan))) { _rcvr_addr.rc_channel = hildon_number_editor_get_value( HILDON_NUMBER_EDITOR(num_rcvr_chan)); rcvr_changed = TRUE; } if(_map_uri_format) g_free(_map_uri_format); if(_map_uri_format_1) g_free(_map_uri_format_1); if(strlen(gtk_entry_get_text(GTK_ENTRY(txt_map_uri_format_1)))) _map_uri_format_1 = g_strdup(gtk_entry_get_text( GTK_ENTRY(txt_map_uri_format_1))); else _map_uri_format_1 = NULL; if(_map_uri_format_2) g_free(_map_uri_format_2); if(strlen(gtk_entry_get_text(GTK_ENTRY(txt_map_uri_format_2)))) _map_uri_format_2 = g_strdup(gtk_entry_get_text( GTK_ENTRY(txt_map_uri_format_2))); else _map_uri_format_2 = NULL; if(_map_dir_name) g_free(_map_dir_name); if(_map_dir_name_1) g_free(_map_dir_name_1); if(strlen(gtk_entry_get_text(GTK_ENTRY(txt_map_dir_name_1)))) _map_dir_name_1 = g_strdup(gtk_entry_get_text(GTK_ENTRY(txt_map_dir_name_1))); else _map_dir_name_1 = NULL; if(_map_dir_name_2) g_free(_map_dir_name_2); if(strlen(gtk_entry_get_text(GTK_ENTRY(txt_map_dir_name_2)))) _map_dir_name_2 = g_strdup(gtk_entry_get_text(GTK_ENTRY(txt_map_dir_name_2))); else _map_dir_name_2 = NULL; if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(radio1))) { printf("setting 1\n"); _map_setting = 1; if(strlen(_map_uri_format_1)) _map_uri_format = g_strdup(_map_uri_format_1); else _map_uri_format = NULL; if(strlen(_map_dir_name_1)) _map_dir_name = g_strdup(_map_dir_name_1); else _map_dir_name = NULL; } else { printf("setting 2\n"); _map_setting = 2; if(strlen(_map_uri_format_2)) _map_uri_format = g_strdup(_map_uri_format_2); else _map_uri_format = NULL; if(strlen(_map_dir_name_2)) _map_dir_name = g_strdup(_map_dir_name_2); else _map_dir_name = NULL; } if(!config_set_map_dir_name(gnome_vfs_expand_initial_tilde(_map_dir_name))) { popup_error(_window, "Could not create Map Cache directory."); continue; } _zoom_steps = hildon_controlbar_get_value( HILDON_CONTROLBAR(num_zoom_steps)); _center_ratio = hildon_controlbar_get_value( HILDON_CONTROLBAR(num_center_ratio)); _lead_ratio = hildon_controlbar_get_value( HILDON_CONTROLBAR(num_lead_ratio)); _draw_line_width = hildon_controlbar_get_value( HILDON_CONTROLBAR(num_draw_line_width)); _always_keep_on = !gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(chk_always_keep_on)); g_free(_units); _units = gtk_combo_box_get_active_text(GTK_COMBO_BOX(cmb_units)); _announce_notice_ratio = hildon_controlbar_get_value( HILDON_CONTROLBAR(num_announce_notice)); _voice_speed = hildon_controlbar_get_value( HILDON_CONTROLBAR(num_voice_speed)) / 3.0; _voice_pitch = hildon_controlbar_get_value( HILDON_CONTROLBAR(num_voice_pitch)); _enable_voice = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(chk_enable_voice)); update_gcs(); config_save(); break; } gtk_widget_hide(dialog); /* Destroying causes a crash (!?!?!??!) */ vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, rcvr_changed); return rcvr_changed; } /** * Initialize all configuration from GCONF. This should not be called more * than once during execution. */ static void config_init() { GConfValue *value; GConfClient *gconf_client = gconf_client_get_default(); gchar *str; printf("%s()\n", __PRETTY_FUNCTION__); if(!gconf_client) { popup_error(_window, _("Failed to initialize GConf. Quitting.")); exit(1); } /* Get Receiver MAC from GConf. Default is scanned via hci_inquiry. */ { _rcvr_mac = gconf_client_get_string( gconf_client, GCONF_KEY_RCVR_MAC, NULL); if(_rcvr_mac) str2ba(_rcvr_mac, &_rcvr_addr.rc_bdaddr); } /* Get Receiver Channel from GConf. Default is 1. */ _rcvr_addr.rc_family = AF_BLUETOOTH; _rcvr_addr.rc_channel = gconf_client_get_int(gconf_client, GCONF_KEY_RCVR_CHAN, NULL); if(_rcvr_addr.rc_channel < 1) _rcvr_addr.rc_channel = 1; /* Get Map Download URI Format. Default is NULL. */ _map_uri_format = gconf_client_get_string(gconf_client, GCONF_KEY_MAP_URI_FORMAT, NULL); _map_uri_format_1 = gconf_client_get_string(gconf_client, GCONF_KEY_MAP_URI_FORMAT_1, NULL); _map_uri_format_2 = gconf_client_get_string(gconf_client, GCONF_KEY_MAP_URI_FORMAT_2, NULL); /* Get Map Cache Directories. */ _map_dir_name_1 = gconf_client_get_string(gconf_client, GCONF_KEY_MAP_DIR_NAME_1, NULL); _map_dir_name_2 = gconf_client_get_string(gconf_client, GCONF_KEY_MAP_DIR_NAME_2, NULL); /* Get Map setting 1/2 */ _map_setting = gconf_client_get_int(gconf_client, GCONF_KEY_MAP_SETTING, NULL); if (_map_setting < 1) _map_setting = 1; if (_map_setting > 2) _map_setting = 2; /* Get Map Cache Directory. Default is "~/apps/maemo-mapper". */ { gchar *tmp; if (_map_setting == 1) { tmp = g_strdup(_map_dir_name_1); _map_uri_format=g_strdup(_map_uri_format_1); } else { tmp = g_strdup(_map_dir_name_2); _map_uri_format=g_strdup(_map_uri_format_2); } if(!tmp) tmp = g_strdup("~/apps/maemo-mapper"); if(!config_set_map_dir_name(gnome_vfs_expand_initial_tilde(tmp))) { popup_error(_window, _("Could not create Map Cache directory.\n" "Please set a valid Map Cache directory in the Settings" " dialog box.")); } g_free(tmp); } /* Get Map Download Zoom Steps. Default is 2. */ _zoom_steps = gconf_client_get_int(gconf_client, GCONF_KEY_MAP_ZOOM_STEPS, NULL); if(!_zoom_steps) _zoom_steps = 2; /* Get Auto-Download. Default is FALSE. */ _auto_download = gconf_client_get_bool(gconf_client, GCONF_KEY_AUTO_DOWNLOAD, NULL); /* Get Center Ratio - Default is 3. */ _center_ratio = gconf_client_get_int(gconf_client, GCONF_KEY_CENTER_SENSITIVITY, NULL); if(!_center_ratio) _center_ratio = 7; /* Get Lead Ratio - Default is 5. */ _lead_ratio = gconf_client_get_int(gconf_client, GCONF_KEY_LEAD_AMOUNT, NULL); if(!_lead_ratio) _lead_ratio = 5; /* Get Draw Line Width- Default is 5. */ _draw_line_width = gconf_client_get_int(gconf_client, GCONF_KEY_DRAW_LINE_WIDTH, NULL); if(!_draw_line_width) _draw_line_width = 5; /* Get Announce Advance Notice - Default is 30. */ value = gconf_client_get(gconf_client, GCONF_KEY_ANNOUNCE_NOTICE, NULL); if(value) { _announce_notice_ratio = gconf_value_get_int(value); gconf_value_free(value); } else _announce_notice_ratio = 6; /* Get Enable Voice flag. Default is TRUE. */ value = gconf_client_get(gconf_client, GCONF_KEY_ENABLE_VOICE, NULL); if(value) { _enable_voice = gconf_value_get_bool(value); gconf_value_free(value); } else _enable_voice = TRUE; /* Get Voice Speed - Default is 1.0. */ value = gconf_client_get(gconf_client, GCONF_KEY_VOICE_SPEED, NULL); if(value) { _voice_speed = gconf_value_get_float(value); gconf_value_free(value); } else _voice_speed = 1.0; /* Get Voice Speed - Default is 0. */ value = gconf_client_get(gconf_client, GCONF_KEY_VOICE_PITCH, NULL); if(value) { _voice_pitch = gconf_value_get_int(value); gconf_value_free(value); } else _voice_pitch = 3; /* Get Always Keep On flag. Default is FALSE. */ _always_keep_on = gconf_client_get_bool(gconf_client, GCONF_KEY_ALWAYS_KEEP_ON, NULL); /* Get Always Keep On flag. Default is FALSE. */ _units = gconf_client_get_string(gconf_client, GCONF_KEY_UNITS, NULL); if(!_units) _units = g_strdup(UNITS_KM); /* Get last saved latitude. Default is 0. */ _pos_lat = gconf_client_get_float(gconf_client, GCONF_KEY_LAT, NULL); /* Get last saved longitude. Default is somewhere in Midwest. */ value = gconf_client_get(gconf_client, GCONF_KEY_LON, NULL); _pos_lon = gconf_client_get_float(gconf_client, GCONF_KEY_LON, NULL); /* Get last center point. */ { gfloat center_lat, center_lon; /* Get last saved latitude. Default is last saved latitude. */ value = gconf_client_get(gconf_client, GCONF_KEY_CENTER_LAT, NULL); if(value) { center_lat = gconf_value_get_float(value); gconf_value_free(value); } else center_lat = _pos_lat; /* Get last saved longitude. Default is last saved longitude. */ value = gconf_client_get(gconf_client, GCONF_KEY_CENTER_LON, NULL); if(value) { center_lon = gconf_value_get_float(value); gconf_value_free(value); } else center_lon = _pos_lon; latlon2unit(center_lat, center_lon, _center.unitx, _center.unity); } /* Get last Zoom Level. Default is 16. */ value = gconf_client_get(gconf_client, GCONF_KEY_ZOOM, NULL); if(value) { _zoom = gconf_value_get_int(value); gconf_value_free(value); } else _zoom = 16; BOUND(_zoom, 0, MAX_ZOOM - 1); _world_size_tiles = unit2tile(WORLD_SIZE_UNITS); /* Speed and Heading are always initialized as 0. */ _speed = 0.f; _heading = 0.f; /* Get Route Directory. Default is NULL. */ _route_dir_uri = gconf_client_get_string(gconf_client, GCONF_KEY_ROUTEDIR, NULL); /* Get Last Track File. Default is NULL. */ _track_file_uri = gconf_client_get_string(gconf_client, GCONF_KEY_TRACKFILE, NULL); /* Get Auto-Center Mode. Default is CENTER_LEAD. */ value = gconf_client_get(gconf_client, GCONF_KEY_AUTOCENTER_MODE, NULL); if(value) { _center_mode = gconf_value_get_int(value); gconf_value_free(value); } else _center_mode = CENTER_LEAD; /* Get Show Tracks flag. Default is TRUE. */ value = gconf_client_get(gconf_client, GCONF_KEY_SHOWTRACKS, NULL); if(value) { _show_tracks |= (gconf_value_get_bool(value) ? TRACKS_MASK : 0); gconf_value_free(value); } else _show_tracks |= TRACKS_MASK; /* Get Show Routes flag. Default is TRUE. */ value = gconf_client_get(gconf_client, GCONF_KEY_SHOWROUTES, NULL); if(value) { _show_tracks |= (gconf_value_get_bool(value) ? ROUTES_MASK : 0); gconf_value_free(value); } else _show_tracks |= ROUTES_MASK; /* Get Show Velocity Vector flag. Default is TRUE. */ value = gconf_client_get(gconf_client, GCONF_KEY_SHOWVELVEC, NULL); if(value) { _show_velvec = gconf_value_get_bool(value); gconf_value_free(value); } else _show_velvec = TRUE; /* Get Enable GPS flag. Default is TRUE. */ value = gconf_client_get(gconf_client, GCONF_KEY_ENABLE_GPS, NULL); if(value) { _enable_gps = gconf_value_get_bool(value); gconf_value_free(value); } else _enable_gps = TRUE; /* Initialize _conn_state based on _enable_gps. */ _conn_state = (_enable_gps ? RCVR_DOWN : RCVR_OFF); /* Load the route locations. */ { GSList *curr; _loc_list = gconf_client_get_list(gconf_client, GCONF_KEY_ROUTE_LOCATIONS, GCONF_VALUE_STRING, NULL); _loc_model = gtk_list_store_new(1, G_TYPE_STRING); for(curr = _loc_list; curr != NULL; curr = curr->next) { GtkTreeIter iter; gtk_list_store_insert_with_values(_loc_model, &iter, INT_MAX, 0, curr->data, -1); } } /* Initialize colors. */ str = gconf_client_get_string(gconf_client, GCONF_KEY_COLOR_MARK, NULL); if(!str || !gdk_color_parse(str, &_color_mark)) _color_mark = DEFAULT_COLOR_MARK; str = gconf_client_get_string(gconf_client, GCONF_KEY_COLOR_MARK_VELOCITY, NULL); if(!str || !gdk_color_parse(str, &_color_mark_velocity)) _color_mark_velocity = DEFAULT_COLOR_MARK_VELOCITY; str = gconf_client_get_string(gconf_client, GCONF_KEY_COLOR_MARK_OLD, NULL); if(!str || !gdk_color_parse(str, &_color_mark_old)) _color_mark_old = DEFAULT_COLOR_MARK_OLD; str = gconf_client_get_string(gconf_client, GCONF_KEY_COLOR_TRACK, NULL); if(!str || !gdk_color_parse(str, &_color_track)) _color_track = DEFAULT_COLOR_TRACK; str = gconf_client_get_string(gconf_client, GCONF_KEY_COLOR_TRACK_BREAK, NULL); if(!str || !gdk_color_parse(str, &_color_track_break)) _color_track_break = DEFAULT_COLOR_TRACK_BREAK; str = gconf_client_get_string(gconf_client, GCONF_KEY_COLOR_ROUTE, NULL); if(!str || !gdk_color_parse(str, &_color_route)) _color_route = DEFAULT_COLOR_ROUTE; str = gconf_client_get_string(gconf_client, GCONF_KEY_COLOR_ROUTE_WAY, NULL); if(!str || !gdk_color_parse(str, &_color_route_way)) _color_route_way = DEFAULT_COLOR_ROUTE_WAY; str = gconf_client_get_string(gconf_client, GCONF_KEY_COLOR_ROUTE_NEXTWAY, NULL); if(!str || !gdk_color_parse(str, &_color_route_nextway)) _color_route_nextway = DEFAULT_COLOR_ROUTE_NEXTWAY; g_object_unref(gconf_client); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Create the menu items needed for the drop down menu. */ static void menu_init() { /* Create needed handles. */ GtkMenu *menu; GtkWidget *submenu; GtkWidget *menu_item; printf("%s()\n", __PRETTY_FUNCTION__); /* Get the menu of our view. */ menu = GTK_MENU(gtk_menu_new()); /* Create the menu items. */ /* The "Routes" submenu. */ gtk_menu_append(menu, menu_item = gtk_menu_item_new_with_label(_("Route"))); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu = gtk_menu_new()); gtk_menu_append(submenu, _menu_route_open_item = gtk_menu_item_new_with_label(_("Open..."))); gtk_menu_append(submenu, _menu_route_download_item = gtk_menu_item_new_with_label(_("Download..."))); gtk_menu_append(submenu, _menu_route_save_item = gtk_menu_item_new_with_label(_("Save..."))); gtk_menu_append(submenu, _menu_route_reset_item = gtk_menu_item_new_with_label(_("Reset"))); gtk_menu_append(submenu, _menu_route_clear_item = gtk_menu_item_new_with_label(_("Clear"))); /* The "Track" submenu. */ gtk_menu_append(menu, menu_item = gtk_menu_item_new_with_label(_("Track"))); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu = gtk_menu_new()); gtk_menu_append(submenu, _menu_track_open_item = gtk_menu_item_new_with_label(_("Open..."))); gtk_menu_append(submenu, _menu_track_save_item = gtk_menu_item_new_with_label(_("Save..."))); gtk_menu_append(submenu, _menu_track_mark_way_item = gtk_menu_item_new_with_label(_("Insert Breakpoint"))); gtk_menu_append(submenu, _menu_track_clear_item = gtk_menu_item_new_with_label(_("Clear"))); gtk_menu_append(menu, menu_item = gtk_menu_item_new_with_label(_("Maps"))); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu = gtk_menu_new()); gtk_menu_append(submenu, _menu_maps_dlroute_item = gtk_menu_item_new_with_label(_("Download Along Route"))); gtk_menu_append(submenu, _menu_maps_dlarea_item = gtk_menu_item_new_with_label(_("Download Area..."))); gtk_menu_append(submenu, _menu_auto_download_item = gtk_check_menu_item_new_with_label(_("Auto-Download"))); gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_auto_download_item), _auto_download); gtk_menu_append(menu, gtk_separator_menu_item_new()); gtk_menu_append(menu, menu_item = gtk_menu_item_new_with_label(_("Show"))); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu = gtk_menu_new()); gtk_menu_append(submenu, _menu_show_routes_item = gtk_check_menu_item_new_with_label(_("Route"))); gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_show_routes_item), _show_tracks & ROUTES_MASK); gtk_menu_append(submenu, _menu_show_tracks_item = gtk_check_menu_item_new_with_label(_("Track"))); gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_show_tracks_item), _show_tracks & TRACKS_MASK); gtk_menu_append(submenu, _menu_show_velvec_item = gtk_check_menu_item_new_with_label(_("Velocity Vector"))); gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_show_velvec_item), _show_velvec); gtk_menu_append(menu, menu_item = gtk_menu_item_new_with_label(_("Auto-Center"))); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu = gtk_menu_new()); gtk_menu_append(submenu, _menu_ac_latlon_item = gtk_radio_menu_item_new_with_label(NULL, _("Lat/Lon"))); gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_ac_latlon_item), _center_mode == CENTER_LATLON); gtk_menu_append(submenu, _menu_ac_lead_item = gtk_radio_menu_item_new_with_label_from_widget( GTK_RADIO_MENU_ITEM(_menu_ac_latlon_item), _("Lead"))); gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_ac_lead_item), _center_mode == CENTER_LEAD); gtk_menu_append(submenu, _menu_ac_none_item = gtk_radio_menu_item_new_with_label_from_widget( GTK_RADIO_MENU_ITEM(_menu_ac_latlon_item), _("None"))); gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_ac_none_item), _center_mode < 0); gtk_menu_append(menu, _menu_fullscreen_item = gtk_check_menu_item_new_with_label(_("Full Screen"))); gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_fullscreen_item), _fullscreen); gtk_menu_append(menu, _menu_enable_gps_item = gtk_check_menu_item_new_with_label(_("Enable GPS"))); gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_enable_gps_item), _enable_gps); gtk_menu_append(menu, gtk_separator_menu_item_new()); gtk_menu_append(menu, _menu_settings_item = gtk_menu_item_new_with_label(_("Settings..."))); gtk_menu_append(menu, gtk_separator_menu_item_new()); gtk_menu_append(menu, _menu_help_item = gtk_menu_item_new_with_label(_("Help"))); gtk_menu_append(menu, _menu_close_item = gtk_menu_item_new_with_label(_("Close"))); /* We need to show menu items. */ gtk_widget_show_all(GTK_WIDGET(menu)); hildon_window_set_menu(HILDON_WINDOW(_window), menu); /* Connect the signals. */ g_signal_connect(G_OBJECT(_menu_route_open_item), "activate", G_CALLBACK(menu_cb_route_open), NULL); g_signal_connect(G_OBJECT(_menu_route_download_item), "activate", G_CALLBACK(menu_cb_route_download), NULL); g_signal_connect(G_OBJECT(_menu_route_save_item), "activate", G_CALLBACK(menu_cb_route_save), NULL); g_signal_connect(G_OBJECT(_menu_route_reset_item), "activate", G_CALLBACK(menu_cb_route_reset), NULL); g_signal_connect(G_OBJECT(_menu_route_clear_item), "activate", G_CALLBACK(menu_cb_route_clear), NULL); g_signal_connect(G_OBJECT(_menu_track_open_item), "activate", G_CALLBACK(menu_cb_track_open), NULL); g_signal_connect(G_OBJECT(_menu_track_save_item), "activate", G_CALLBACK(menu_cb_track_save), NULL); g_signal_connect(G_OBJECT(_menu_track_mark_way_item), "activate", G_CALLBACK(menu_cb_track_mark_way), NULL); g_signal_connect(G_OBJECT(_menu_track_clear_item), "activate", G_CALLBACK(menu_cb_track_clear), NULL); g_signal_connect(G_OBJECT(_menu_show_tracks_item), "toggled", G_CALLBACK(menu_cb_show_tracks), NULL); g_signal_connect(G_OBJECT(_menu_show_routes_item), "toggled", G_CALLBACK(menu_cb_show_routes), NULL); g_signal_connect(G_OBJECT(_menu_show_velvec_item), "toggled", G_CALLBACK(menu_cb_show_velvec), NULL); g_signal_connect(G_OBJECT(_menu_maps_dlroute_item), "activate", G_CALLBACK(menu_cb_maps_dlroute), NULL); g_signal_connect(G_OBJECT(_menu_maps_dlarea_item), "activate", G_CALLBACK(menu_cb_maps_dlarea), NULL); g_signal_connect(G_OBJECT(_menu_ac_latlon_item), "toggled", G_CALLBACK(menu_cb_ac_latlon), NULL); g_signal_connect(G_OBJECT(_menu_ac_lead_item), "toggled", G_CALLBACK(menu_cb_ac_lead), NULL); g_signal_connect(G_OBJECT(_menu_ac_none_item), "toggled", G_CALLBACK(menu_cb_ac_none), NULL); g_signal_connect(G_OBJECT(_menu_fullscreen_item), "toggled", G_CALLBACK(menu_cb_fullscreen), NULL); g_signal_connect(G_OBJECT(_menu_enable_gps_item), "toggled", G_CALLBACK(menu_cb_enable_gps), NULL); g_signal_connect(G_OBJECT(_menu_auto_download_item), "toggled", G_CALLBACK(menu_cb_auto_download), NULL); g_signal_connect(G_OBJECT(_menu_settings_item), "activate", G_CALLBACK(menu_cb_settings), NULL); g_signal_connect(G_OBJECT(_menu_help_item), "activate", G_CALLBACK(menu_cb_help), NULL); g_signal_connect(G_OBJECT(_menu_close_item), "activate", G_CALLBACK(gtk_main_quit), NULL); /* Setup the context menu. */ menu = GTK_MENU(gtk_menu_new()); /* Setup the map context menu. */ gtk_menu_append(menu, menu_item = gtk_menu_item_new_with_label(_("Location"))); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu = gtk_menu_new()); /* Setup the map context menu. */ gtk_menu_append(submenu, _cmenu_loc_show_latlon_item = gtk_menu_item_new_with_label(_("Show Lat/Lon"))); gtk_menu_append(submenu, _cmenu_loc_clip_latlon_item = gtk_menu_item_new_with_label(_("Copy Lat/Lon to Clipboard"))); gtk_menu_append(submenu, gtk_separator_menu_item_new()); gtk_menu_append(submenu, _cmenu_loc_route_to_item = gtk_menu_item_new_with_label(_("Download Route to..."))); gtk_menu_append(submenu, _cmenu_loc_distance_to_item = gtk_menu_item_new_with_label(_("Show Distance to"))); /* Setup the waypoint context menu. */ gtk_menu_append(menu, menu_item = gtk_menu_item_new_with_label(_("Waypoint"))); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu = gtk_menu_new()); gtk_menu_append(submenu, _cmenu_way_show_latlon_item = gtk_menu_item_new_with_label(_("Show Lat/Lon"))); gtk_menu_append(submenu, _cmenu_way_show_desc_item = gtk_menu_item_new_with_label(_("Show Description"))); gtk_menu_append(submenu, _cmenu_way_clip_latlon_item = gtk_menu_item_new_with_label(_("Copy Lat/Lon to Clipboard"))); gtk_menu_append(submenu, _cmenu_way_clip_desc_item = gtk_menu_item_new_with_label(_("Copy Description to Clipboard"))); gtk_menu_append(submenu, gtk_separator_menu_item_new()); gtk_menu_append(submenu, _cmenu_way_route_to_item = gtk_menu_item_new_with_label(_("Download Route to..."))); gtk_menu_append(submenu, _cmenu_way_distance_to_item = gtk_menu_item_new_with_label(_("Show Distance to"))); gtk_menu_append(submenu, _cmenu_way_delete_item = gtk_menu_item_new_with_label(_("Delete"))); /* Connect signals for context menu. */ g_signal_connect(G_OBJECT(_cmenu_loc_show_latlon_item), "activate", G_CALLBACK(cmenu_cb_loc_show_latlon), NULL); g_signal_connect(G_OBJECT(_cmenu_loc_clip_latlon_item), "activate", G_CALLBACK(cmenu_cb_loc_clip_latlon), NULL); g_signal_connect(G_OBJECT(_cmenu_loc_route_to_item), "activate", G_CALLBACK(cmenu_cb_loc_route_to), NULL); g_signal_connect(G_OBJECT(_cmenu_loc_distance_to_item), "activate", G_CALLBACK(cmenu_cb_loc_distance_to), NULL); g_signal_connect(G_OBJECT(_cmenu_way_show_latlon_item), "activate", G_CALLBACK(cmenu_cb_way_show_latlon), NULL); g_signal_connect(G_OBJECT(_cmenu_way_show_desc_item), "activate", G_CALLBACK(cmenu_cb_way_show_desc), NULL); g_signal_connect(G_OBJECT(_cmenu_way_clip_latlon_item), "activate", G_CALLBACK(cmenu_cb_way_clip_latlon), NULL); g_signal_connect(G_OBJECT(_cmenu_way_clip_desc_item), "activate", G_CALLBACK(cmenu_cb_way_clip_desc), NULL); g_signal_connect(G_OBJECT(_cmenu_way_route_to_item), "activate", G_CALLBACK(cmenu_cb_way_route_to), NULL); g_signal_connect(G_OBJECT(_cmenu_way_distance_to_item), "activate", G_CALLBACK(cmenu_cb_way_distance_to), NULL); g_signal_connect(G_OBJECT(_cmenu_way_delete_item), "activate", G_CALLBACK(cmenu_cb_way_delete), NULL); gtk_widget_show_all(GTK_WIDGET(menu)); gtk_widget_tap_and_hold_setup(_map_widget, GTK_WIDGET(menu), NULL, 0); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Call gtk_window_present() on Maemo Mapper. This also checks the * configuration and brings up the Settings dialog if the GPS Receiver is * not set up, the first time it is called. */ static gboolean window_present() { static gint been_here = 0; printf("%s()\n", __PRETTY_FUNCTION__); if(!been_here++) { /* Set connection state first, to avoid going into this if twice. */ if(_rcvr_mac || !_enable_gps || settings_dialog()) { gtk_widget_show_all(_window); /* Connect to receiver. */ if(_enable_gps) rcvr_connect_now(); } else gtk_main_quit(); } gtk_window_present(GTK_WINDOW(_window)); /* Re-enable any banners that might have been up. */ { ConnState old_state = _conn_state; set_conn_state(RCVR_OFF); set_conn_state(old_state); } vprintf("%s(): return\n", __PRETTY_FUNCTION__); return FALSE; } /** * Get the hash value of a ProgressUpdateInfo object. This is trivial, since * the hash is generated and stored when the object is created. */ static guint download_hashfunc(ProgressUpdateInfo *pui) { vprintf("%s()\n", __PRETTY_FUNCTION__); vprintf("%s(): return\n", __PRETTY_FUNCTION__); return pui->hash; } /** * Return whether or not two ProgressUpdateInfo objects are equal. They * are equal if they are downloading the same tile. */ static gboolean download_equalfunc( ProgressUpdateInfo *pui1, ProgressUpdateInfo *pui2) { vprintf("%s()\n", __PRETTY_FUNCTION__); vprintf("%s(): return\n", __PRETTY_FUNCTION__); return pui1->tilex == pui2->tilex && pui1->tiley == pui2->tiley && pui1->zoom == pui2->zoom; } /** * Draw the current mark (representing the current GPS location) onto * _map_widget. This method does not queue the draw area. */ static void map_draw_mark() { printf("%s()\n", __PRETTY_FUNCTION__); if(_enable_gps) { gdk_draw_arc( _map_widget->window, _conn_state == RCVR_FIXED ? _gc_mark : _gc_mark_old, FALSE, /* not filled. */ _mark_x1 - _draw_line_width, _mark_y1 - _draw_line_width, 2 * _draw_line_width, 2 * _draw_line_width, 0, 360 * 64); gdk_draw_line( _map_widget->window, _conn_state == RCVR_FIXED ? (_show_velvec ? _gc_mark_velocity : _gc_mark) : _gc_mark_old, _mark_x1, _mark_y1, _mark_x2, _mark_y2); } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * "Set" the mark, which translates the current GPS position into on-screen * units in preparation for drawing the mark with map_draw_mark(). */ static void map_set_mark() { printf("%s()\n", __PRETTY_FUNCTION__); _mark_x1 = unit2x(_pos.unitx); _mark_y1 = unit2y(_pos.unity); _mark_x2 = _mark_x1 + (_show_velvec ? _vel_offsetx : 0); _mark_y2 = _mark_y1 + (_show_velvec ? _vel_offsety : 0); _mark_minx = MIN(_mark_x1, _mark_x2) - (2 * _draw_line_width); _mark_miny = MIN(_mark_y1, _mark_y2) - (2 * _draw_line_width); _mark_width = abs(_mark_x1 - _mark_x2) + (4 * _draw_line_width); _mark_height = abs(_mark_y1 - _mark_y2) + (4 * _draw_line_width); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Do an in-place scaling of a pixbuf's pixels at the given ratio from the * given source location. It would have been nice if gdk_pixbuf provided * this method, but I guess it's not general-purpose enough. */ static void map_pixbuf_scale_inplace(guchar *pixels, guint ratio_p2, guint src_x, guint src_y) { guint dest_x = 0, dest_y = 0, dest_dim = TILE_SIZE_PIXELS; vprintf("%s(%d, %d, %d)\n", __PRETTY_FUNCTION__, ratio_p2, src_x, src_y); /* Sweep through the entire dest area, copying as necessary, but * DO NOT OVERWRITE THE SOURCE AREA. We'll copy it afterward. */ do { guint src_dim = dest_dim >> ratio_p2; guint src_endx = src_x - dest_x + src_dim; gint x, y; for(y = dest_dim - 1; y >= 0; y--) { guint src_offset_y, dest_offset_y; src_offset_y = (src_y + (y >> ratio_p2)) * TILE_PIXBUF_STRIDE; dest_offset_y = (dest_y + y) * TILE_PIXBUF_STRIDE; x = dest_dim - 1; if((unsigned)(dest_y + y - src_y) < src_dim && (unsigned)(dest_x + x - src_x) < src_dim) x -= src_dim; for(; x >= 0; x--) { guint src_offset, dest_offset; src_offset = src_offset_y + (src_x + (x >> ratio_p2)) * 3; dest_offset = dest_offset_y + (dest_x + x) * 3; pixels[dest_offset + 0] = pixels[src_offset + 0]; pixels[dest_offset + 1] = pixels[src_offset + 1]; pixels[dest_offset + 2] = pixels[src_offset + 2]; if((unsigned)(dest_y + y - src_y) < src_dim && x == src_endx) x -= src_dim; } } /* Reuse src_dim and src_endx to store new src_x and src_y. */ src_dim = src_x + ((src_x - dest_x) >> ratio_p2); src_endx = src_y + ((src_y - dest_y) >> ratio_p2); dest_x = src_x; dest_y = src_y; src_x = src_dim; src_y = src_endx; } while((dest_dim >>= ratio_p2) > 1); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Free a ProgressUpdateInfo data structure that was allocated during the * auto-map-download process. */ static void progress_update_info_free(ProgressUpdateInfo *pui) { vprintf("%s()\n", __PRETTY_FUNCTION__); gnome_vfs_uri_unref((GnomeVFSURI*)pui->src_list->data); g_list_free(pui->src_list); gnome_vfs_uri_unref((GnomeVFSURI*)pui->dest_list->data); g_list_free(pui->dest_list); g_free(pui); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Given the xyz coordinates of our map coordinate system, write the qrst * quadtree coordinates to buffer. */ static void map_convert_coords_to_quadtree_string(int x, int y, int zoomlevel,gchar *buffer) { static const gchar *const quadrant = "qrts"; vprintf("%s()\n", __PRETTY_FUNCTION__); gchar *ptr = buffer; int n; *ptr++ = 't'; for (n = 16 - zoomlevel; n >= 0; n--) { int xbit = (x >> n) & 1; int ybit = (y >> n) & 1; *ptr++ = quadrant[xbit + 2 * ybit]; } *ptr++ = '\0'; vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Construct the URL that we should fetch, based on the current URI format. * This method works differently depending on if a "%s" string is present in * the URI format, since that would indicate a quadtree-based map coordinate * system. */ static void map_construct_url(gchar *buffer, guint tilex, guint tiley, guint zoom) { vprintf("%s()\n", __PRETTY_FUNCTION__); if(strstr(_map_uri_format, "%s")) { /* This is a satellite-map URI. */ gchar location[MAX_ZOOM + 2]; map_convert_coords_to_quadtree_string(tilex, tiley, zoom, location); sprintf(buffer, _map_uri_format, location); } else /* This is a street-map URI. */ sprintf(buffer, _map_uri_format, tilex, tiley, zoom); printf("URI=%s\n", buffer); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Initiate a download of the given xyz coordinates using the given buffer * as the URL. If the map already exists on disk, or if we are already * downloading the map, then this method does nothing. */ static gboolean map_initiate_download(guint tilex, guint tiley, guint zoom, guint retries) { gchar buffer[1024]; GnomeVFSURI *src, *dest; GList *src_list, *dest_list; gint priority; ProgressUpdateInfo *pui; vprintf("%s(%u, %u, %u, %u)\n", __PRETTY_FUNCTION__, tilex, tiley, zoom, retries); pui = g_new(ProgressUpdateInfo, 1); pui->hash = tilex + (tiley << 12) + (zoom << 24); if(g_hash_table_lookup(_downloads_hash, pui)) { /* Already downloading - return FALSE. */ g_free(pui); return FALSE; } sprintf(buffer, "%s/%u/%u/%u.jpg", _map_dir_name, zoom, tilex, tiley); vprintf("buffer=$s\n", buffer); dest = gnome_vfs_uri_new(buffer); if(gnome_vfs_uri_exists(dest)) { /* Already downloaded - return FALSE. */ gnome_vfs_uri_unref(dest); g_free(pui); return FALSE; } /* Priority is based on proximity to _center.unitx - lower number means * higher priority, so the further we are, the higher the number. */ priority = GNOME_VFS_PRIORITY_MIN + unit2ptile(abs(tile2punit(tilex, zoom) - _center.unitx) + abs(tile2punit(tiley, zoom) - _center.unity)); BOUND(priority, GNOME_VFS_PRIORITY_MIN, GNOME_VFS_PRIORITY_MAX); map_construct_url(buffer, tilex, tiley, zoom); src = gnome_vfs_uri_new(buffer); src_list = g_list_prepend(src_list = NULL, src); dest_list = g_list_prepend(dest_list = NULL, dest); pui->tilex = tilex; pui->tiley = tiley; pui->zoom = zoom; pui->src_list = src_list; pui->dest_list = dest_list; pui->retries_left = retries; /* Initiate asynchronous download. */ if(GNOME_VFS_OK != gnome_vfs_async_xfer( &pui->handle, src_list, dest_list, GNOME_VFS_XFER_USE_UNIQUE_NAMES, /* needed for dupe detect. */ GNOME_VFS_XFER_ERROR_MODE_QUERY, GNOME_VFS_XFER_OVERWRITE_MODE_QUERY, priority, (GnomeVFSAsyncXferProgressCallback)map_download_cb_async, pui, (GnomeVFSXferProgressCallback)gtk_true, NULL)) { progress_update_info_free(pui); return FALSE; } g_hash_table_insert(_downloads_hash, pui, pui); if(!_num_downloads++ && !_download_banner) _download_banner = hildon_banner_show_progress( _window, NULL, _("Downloading maps")); vprintf("%s(): return\n", __PRETTY_FUNCTION__); return TRUE; } static void map_render_tile(guint tilex, guint tiley, guint destx, guint desty, gboolean fast_fail) { gchar buffer[1024]; GdkPixbuf *pixbuf = NULL; GError *error = NULL; guint zoff; if(tilex < _world_size_tiles && tiley < _world_size_tiles) { /* The tile is possible. */ vprintf("%s(%u, %u, %u, %u)\n", __PRETTY_FUNCTION__, tilex, tiley, destx, desty); sprintf(buffer, "%s/%u/%u/%u.jpg", _map_dir_name, _zoom, tilex, tiley); pixbuf = gdk_pixbuf_new_from_file(buffer, &error); if(error) { pixbuf = NULL; error = NULL; } if(!pixbuf && !fast_fail && _auto_download && _map_uri_format && !(_zoom % _zoom_steps)) { map_initiate_download(tilex, tiley, _zoom, INITIAL_DOWNLOAD_RETRIES); fast_fail = TRUE; } for(zoff = 1; !pixbuf && (_zoom + zoff) <= MAX_ZOOM; zoff += 1) { /* Attempt to blit a wider map. */ sprintf(buffer, "%s/%u/%u/%u.jpg", _map_dir_name, _zoom + zoff, (tilex >> zoff), (tiley >> zoff)); vprintf("buffer=%s\n", buffer); pixbuf = gdk_pixbuf_new_from_file(buffer, &error); if(error) { pixbuf = NULL; error = NULL; } if(pixbuf) { map_pixbuf_scale_inplace(gdk_pixbuf_get_pixels(pixbuf), zoff, (tilex - ((tilex >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff), (tiley - ((tiley >> zoff) << zoff)) << (TILE_SIZE_P2 - zoff)); } else { if(_auto_download && _map_uri_format && !((_zoom + zoff) % _zoom_steps)) { if(!fast_fail) map_initiate_download( tilex >> zoff, tiley >> zoff, _zoom + zoff, INITIAL_DOWNLOAD_RETRIES); fast_fail = TRUE; } } } } if(!pixbuf) pixbuf = gdk_pixbuf_new( GDK_COLORSPACE_RGB, FALSE, /* no alpha. */ 8, /* 8 bits per sample. */ TILE_SIZE_PIXELS, TILE_SIZE_PIXELS); if(pixbuf) { gdk_draw_pixbuf( _map_pixmap, _gc_mark, pixbuf, 0, 0, destx, desty, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS, GDK_RGB_DITHER_NONE, 0, 0); g_object_unref(pixbuf); } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Force a redraw of the entire _map_pixmap, including fetching the * background maps from disk and redrawing the tracks on top of them. */ void map_force_redraw() { guint new_x, new_y; printf("%s()\n", __PRETTY_FUNCTION__); for(new_y = 0; new_y < BUF_HEIGHT_TILES; ++new_y) for(new_x = 0; new_x < BUF_WIDTH_TILES; ++new_x) { map_render_tile( _base_tilex + new_x, _base_tiley + new_y, new_x * TILE_SIZE_PIXELS, new_y * TILE_SIZE_PIXELS, FALSE); } map_render_paths(); MACRO_QUEUE_DRAW_AREA(); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Set the current zoom level. If the given zoom level is the same as the * current zoom level, or if the new zoom is invalid * (not MIN_ZOOM <= new_zoom < MAX_ZOOM), then this method does nothing. */ void map_set_zoom(guint new_zoom) { printf("%s(%d)\n", __PRETTY_FUNCTION__, _zoom); /* Note that, since new_zoom is a guint and MIN_ZOOM is 0, this if * condition also checks for new_zoom >= MIN_ZOOM. */ if(new_zoom > (MAX_ZOOM - 1) || new_zoom == _zoom) return; _zoom = new_zoom; _world_size_tiles = unit2tile(WORLD_SIZE_UNITS); /* If we're leading, update the center to reflect new zoom level. */ MACRO_RECALC_CENTER(_center.unitx, _center.unity); /* Update center bounds to reflect new zoom level. */ _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth)); _min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight)); _max_center.unitx = WORLD_SIZE_UNITS-grid2unit(_screen_grids_halfwidth) - 1; _max_center.unity = WORLD_SIZE_UNITS-grid2unit(_screen_grids_halfheight)- 1; BOUND(_center.unitx, _min_center.unitx, _max_center.unitx); BOUND(_center.unity, _min_center.unity, _max_center.unity); _base_tilex = grid2tile((gint)pixel2grid( (gint)unit2pixel((gint)_center.unitx)) - (gint)_screen_grids_halfwidth); _base_tiley = grid2tile(pixel2grid( unit2pixel(_center.unity)) - _screen_grids_halfheight); /* New zoom level, so we can't reuse the old buffer's pixels. */ /* Update state variables. */ MACRO_RECALC_OFFSET(); MACRO_RECALC_FOCUS_BASE(); MACRO_RECALC_FOCUS_SIZE(); map_set_mark(); map_force_redraw(); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Center the view on the given unitx/unity. */ static void map_center_unit(guint new_center_unitx, guint new_center_unity) { gint new_base_tilex, new_base_tiley; guint new_x, new_y; guint j, k, base_new_x, base_old_x, old_x, old_y, iox, ioy; printf("%s(%d, %d)\n", __PRETTY_FUNCTION__, new_center_unitx, new_center_unity); /* Assure that _center.unitx/y are bounded. */ BOUND(new_center_unitx, _min_center.unitx, _max_center.unitx); BOUND(new_center_unity, _min_center.unity, _max_center.unity); _center.unitx = new_center_unitx; _center.unity = new_center_unity; new_base_tilex = grid2tile((gint)pixel2grid( (gint)unit2pixel((gint)_center.unitx)) - (gint)_screen_grids_halfwidth); new_base_tiley = grid2tile(pixel2grid( unit2pixel(_center.unity)) - _screen_grids_halfheight); /* Same zoom level, so it's likely that we can reuse some of the old * buffer's pixels. */ if(new_base_tilex != _base_tilex || new_base_tiley != _base_tiley) { /* If copying from old parts to new parts, we need to make sure we * don't overwrite the old parts when copying, so set up new_x, * new_y, old_x, old_y, iox, and ioy with that in mind. */ if(new_base_tiley < _base_tiley) { /* New is lower than old - start at bottom and go up. */ new_y = BUF_HEIGHT_TILES - 1; ioy = -1; } else { /* New is higher than old - start at top and go down. */ new_y = 0; ioy = 1; } if(new_base_tilex < _base_tilex) { /* New is righter than old - start at right and go left. */ base_new_x = BUF_WIDTH_TILES - 1; iox = -1; } else { /* New is lefter than old - start at left and go right. */ base_new_x = 0; iox = 1; } /* Iterate over the y tile values. */ old_y = new_y + new_base_tiley - _base_tiley; base_old_x = base_new_x + new_base_tilex - _base_tilex; _base_tilex = new_base_tilex; _base_tiley = new_base_tiley; for(j = 0; j < BUF_HEIGHT_TILES; ++j, new_y += ioy, old_y += ioy) { new_x = base_new_x; old_x = base_old_x; /* Iterate over the x tile values. */ for(k = 0; k < BUF_WIDTH_TILES; ++k, new_x += iox, old_x += iox) { /* Can we get this grid block from the old buffer?. */ if(old_x >= 0 && old_x < BUF_WIDTH_TILES && old_y >= 0 && old_y < BUF_HEIGHT_TILES) { /* Copy from old buffer to new buffer. */ gdk_draw_drawable( _map_pixmap, _gc_mark, _map_pixmap, old_x * TILE_SIZE_PIXELS, old_y * TILE_SIZE_PIXELS, new_x * TILE_SIZE_PIXELS, new_y * TILE_SIZE_PIXELS, TILE_SIZE_PIXELS, TILE_SIZE_PIXELS); } else { map_render_tile( new_base_tilex + new_x, new_base_tiley + new_y, new_x * TILE_SIZE_PIXELS, new_y * TILE_SIZE_PIXELS, FALSE); } } } map_render_paths(); } MACRO_RECALC_OFFSET(); MACRO_RECALC_FOCUS_BASE(); map_set_mark(); MACRO_QUEUE_DRAW_AREA(); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Pan the view by the given number of units in the X and Y directions. */ void map_pan(gint delta_unitx, gint delta_unity) { printf("%s(%d, %d)\n", __PRETTY_FUNCTION__, delta_unitx, delta_unity); if(_center_mode > 0) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM( _menu_ac_none_item), TRUE); map_center_unit( _center.unitx + delta_unitx, _center.unity + delta_unity); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Initiate a move of the mark from the old location to the current * location. This function queues the draw area of the old mark (to force * drawing of the background map), then updates the mark, then queus the * draw area of the new mark. */ static void map_move_mark() { printf("%s()\n", __PRETTY_FUNCTION__); /* Just queue the old and new draw areas. */ gtk_widget_queue_draw_area(_map_widget, _mark_minx, _mark_miny, _mark_width, _mark_height); map_set_mark(); gtk_widget_queue_draw_area(_map_widget, _mark_minx, _mark_miny, _mark_width, _mark_height); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Make sure the mark is up-to-date. This function triggers a panning of * the view if the mark is appropriately close to the edge of the view. */ static void refresh_mark() { printf("%s()\n", __PRETTY_FUNCTION__); guint new_center_unitx; guint new_center_unity; MACRO_RECALC_CENTER(new_center_unitx, new_center_unity); if((new_center_unitx - _focus.unitx) < _focus_unitwidth && (new_center_unity - _focus.unity) < _focus_unitheight) /* We're not changing the view - just move the mark. */ map_move_mark(); else map_center_unit(new_center_unitx, new_center_unity); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Handle a start tag in the parsing of a GPX file. */ #define MACRO_SET_UNKNOWN() { \ data->prev_state = data->state; \ data->state = UNKNOWN; \ data->unknown_depth = 1; \ } static void gpx_start_element(SaxData *data, const xmlChar *name, const xmlChar **attrs) { vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name); switch(data->state) { case ERROR: break; case START: if(!strcmp((gchar*)name, "gpx")) data->state = INSIDE_GPX; else MACRO_SET_UNKNOWN(); break; case INSIDE_GPX: if(!strcmp((gchar*)name, "trk")) data->state = INSIDE_PATH; else MACRO_SET_UNKNOWN(); break; case INSIDE_PATH: if(!strcmp((gchar*)name, "trkseg")) { data->state = INSIDE_PATH_SEGMENT; data->at_least_one_trkpt = FALSE; } else MACRO_SET_UNKNOWN(); break; case INSIDE_PATH_SEGMENT: if(!strcmp((gchar*)name, "trkpt")) { const xmlChar **curr_attr; gchar *error_check; gfloat lat = 0.f, lon = 0.f; gboolean has_lat, has_lon; has_lat = FALSE; has_lon = FALSE; for(curr_attr = attrs; *curr_attr != NULL; ) { const gchar *attr_name = *curr_attr++; const gchar *attr_val = *curr_attr++; if(!strcmp(attr_name, "lat")) { lat = g_ascii_strtod(attr_val, &error_check); if(error_check != attr_val) has_lat = TRUE; } else if(!strcmp(attr_name, "lon")) { lon = g_ascii_strtod(attr_val, &error_check); if(error_check != attr_val) has_lon = TRUE; } } if(has_lat && has_lon) { if(data->path.path_type == TRACK) { MACRO_TRACK_INCREMENT_TAIL(data->path.path.track); latlon2unit(lat, lon, data->path.path.track.tail->point.unitx, data->path.path.track.tail->point.unity); data->path.path.track.tail->time = 0; } else { MACRO_ROUTE_INCREMENT_TAIL(data->path.path.route); latlon2unit(lat, lon, data->path.path.route.tail->unitx, data->path.path.route.tail->unity); } data->state = INSIDE_PATH_POINT; } else data->state = ERROR; } else MACRO_SET_UNKNOWN(); break; case INSIDE_PATH_POINT: /* only parse time for tracks */ if(data->path.path_type == TRACK && !strcmp((gchar*)name, "time")) data->state = INSIDE_PATH_POINT_TIME; /* only parse description for routes */ else if(data->path.path_type == ROUTE && !strcmp((gchar*)name, "desc")) data->state = INSIDE_PATH_POINT_DESC; else MACRO_SET_UNKNOWN(); break; case UNKNOWN: data->unknown_depth++; break; default: ; } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Handle an end tag in the parsing of a GPX file. */ static void gpx_end_element(SaxData *data, const xmlChar *name) { vprintf("%s(%s)\n", __PRETTY_FUNCTION__, name); switch(data->state) { case ERROR: break; case START: data->state = ERROR; break; case INSIDE_GPX: if(!strcmp((gchar*)name, "gpx")) data->state = FINISH; else data->state = ERROR; break; case INSIDE_PATH: if(!strcmp((gchar*)name, "trk")) data->state = INSIDE_GPX; else data->state = ERROR; break; case INSIDE_PATH_SEGMENT: if(!strcmp((gchar*)name, "trkseg")) { if(data->at_least_one_trkpt) { if(data->path.path_type == TRACK) { MACRO_TRACK_INCREMENT_TAIL(data->path.path.track); *data->path.path.track.tail = _track_null; } else { MACRO_ROUTE_INCREMENT_TAIL(data->path.path.route); *data->path.path.route.tail = _pos_null; } } data->state = INSIDE_PATH; } else data->state = ERROR; break; case INSIDE_PATH_POINT: if(!strcmp((gchar*)name, "trkpt")) { data->state = INSIDE_PATH_SEGMENT; data->at_least_one_trkpt = TRUE; } else data->state = ERROR; break; case INSIDE_PATH_POINT_TIME: /* only parse time for tracks */ if(!strcmp((gchar*)name, "time")) { struct tm time; gchar *ptr; if(NULL == (ptr = strptime(data->chars->str, XML_DATE_FORMAT, &time))) /* Failed to parse dateTime format. */ data->state = ERROR; else { /* Parse was successful. Now we have to parse timezone. * From here on, if there is an error, I just assume local * timezone. Yes, this is not proper XML, but I don't * care. */ gchar *error_check; /* First, set time in "local" time zone. */ data->path.path.track.tail->time = (mktime(&time)); /* Now, skip inconsequential characters */ while(*ptr && *ptr != 'Z' && *ptr != '-' && *ptr != '+') ptr++; /* Check if we ran to the end of the string. */ if(*ptr) { /* Next character is either 'Z', '-', or '+' */ if(*ptr == 'Z') /* Zulu (UTC) time. Undo the local time zone's * offset. */ data->path.path.track.tail->time += time.tm_gmtoff; else { /* Not Zulu (UTC). Must parse hours and minutes. */ gint offhours = strtol(ptr, &error_check, 10); if(error_check != ptr && *(ptr = error_check) == ':') { /* Parse of hours worked. Check minutes. */ gint offmins = strtol(ptr + 1, &error_check, 10); if(error_check != (ptr + 1)) { /* Parse of minutes worked. Calculate. */ data->path.path.track.tail->time += (time.tm_gmtoff - (offhours * 60 * 60 + offmins * 60)); } } } } /* Successfully parsed dateTime. */ data->state = INSIDE_PATH_POINT; } g_string_free(data->chars, TRUE); data->chars = g_string_new(""); } else data->state = ERROR; break; case INSIDE_PATH_POINT_DESC: /* only parse description for routes */ if(!strcmp((gchar*)name, "desc")) { MACRO_ROUTE_INCREMENT_WTAIL(data->path.path.route); data->path.path.route.wtail->point = data->path.path.route.tail; data->path.path.route.wtail->desc = g_string_free(data->chars, FALSE); data->chars = g_string_new(""); data->state = INSIDE_PATH_POINT; } else data->state = ERROR; break; case UNKNOWN: if(!--data->unknown_depth) data->state = data->prev_state; else data->state = ERROR; break; default: ; } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Handle char data in the parsing of a GPX file. */ static void gpx_chars(SaxData *data, const xmlChar *ch, int len) { guint i; vprintf("%s()\n", __PRETTY_FUNCTION__); switch(data->state) { case ERROR: case UNKNOWN: break; case INSIDE_PATH_POINT_TIME: case INSIDE_PATH_POINT_DESC: for (i = 0; i < len; i++) data->chars = g_string_append_c(data->chars, ch[i]); vprintf("%s\n", data->chars->str); break; default: break; } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Handle an entity in the parsing of a GPX file. We don't do anything * special here. */ static xmlEntityPtr gpx_get_entity(SaxData *data, const xmlChar *name) { vprintf("%s()\n", __PRETTY_FUNCTION__); vprintf("%s(): return\n", __PRETTY_FUNCTION__); return xmlGetPredefinedEntity(name); } /** * Handle an error in the parsing of a GPX file. */ static void gpx_error(SaxData *data, const gchar *msg, ...) { vprintf("%s()\n", __PRETTY_FUNCTION__); vprintf("%s(): return\n", __PRETTY_FUNCTION__); data->state = ERROR; } /** * Parse the given character buffer of the given size, replacing the given * Track's pointers with pointers to new arrays depending on the given * policy, adding extra_bins slots in the arrays for new data. * * policy_old should be negative to indicate that the existing data should * be prepended to the new GPX data, positive to indicate the opposite, and * zero to indicate that we should throw away the old data. * * When importing tracks, we *prepend* the GPX data and provide extra_bins. * When importing routes, we *append* in the case of regular routes, and we * *replace* in the case of automatic routing. Routes get no extra bins. */ static gboolean parse_track_gpx(gchar *buffer, gint size, gint policy_old) { SaxData data; xmlSAXHandler sax_handler; printf("%s()\n", __PRETTY_FUNCTION__); data.path.path_type = TRACK; MACRO_INIT_TRACK(data.path.path.track); data.state = START; data.chars = g_string_new(""); memset(&sax_handler, 0, sizeof(sax_handler)); sax_handler.characters = (charactersSAXFunc)gpx_chars; sax_handler.startElement = (startElementSAXFunc)gpx_start_element; sax_handler.endElement = (endElementSAXFunc)gpx_end_element; sax_handler.entityDecl = (entityDeclSAXFunc)gpx_get_entity; sax_handler.warning = (warningSAXFunc)gpx_error; sax_handler.error = (errorSAXFunc)gpx_error; sax_handler.fatalError = (fatalErrorSAXFunc)gpx_error; xmlSAXUserParseMemory(&sax_handler, &data, buffer, size); g_string_free(data.chars, TRUE); if(data.state != FINISH) { vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } /* Successful parsing - replace given Track structure. */ if(policy_old && _track.head) { TrackPoint *src_first; Track *src, *dest; if(policy_old > 0) { /* Append to current track. */ src = &data.path.path.track; dest = &_track; } else { /* Prepend to current track. */ src = &_track; dest = &data.path.path.track; } /* Find src_first non-zero point. */ for(src_first = src->head - 1; src_first++ != src->tail; ) if(src_first->point.unity) break; /* Append track points from src to dest. */ if(src->tail >= src_first) { guint num_dest_points = dest->tail - dest->head + 1; guint num_src_points = src->tail - src_first + 1; /* Adjust dest->tail to be able to fit src track data * plus room for more track data. */ track_resize(dest, num_dest_points + num_src_points + ARRAY_CHUNK_SIZE); memcpy(dest->tail + 1, src_first, num_src_points * sizeof(TrackPoint)); dest->tail += num_src_points; } MACRO_CLEAR_TRACK(*src); if(policy_old < 0) _track = *dest; } else { MACRO_CLEAR_TRACK(_track); /* Overwrite with data.track. */ _track = data.path.path.track; track_resize(&_track, _track.tail - _track.head + 1 + ARRAY_CHUNK_SIZE); } vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean parse_route_gpx(gchar *buffer, gint size, gint policy_old) { SaxData data; xmlSAXHandler sax_handler; printf("%s()\n", __PRETTY_FUNCTION__); data.path.path_type = ROUTE; MACRO_INIT_ROUTE(data.path.path.route); data.state = START; data.chars = g_string_new(""); memset(&sax_handler, 0, sizeof(sax_handler)); sax_handler.characters = (charactersSAXFunc)gpx_chars; sax_handler.startElement = (startElementSAXFunc)gpx_start_element; sax_handler.endElement = (endElementSAXFunc)gpx_end_element; sax_handler.entityDecl = (entityDeclSAXFunc)gpx_get_entity; sax_handler.warning = (warningSAXFunc)gpx_error; sax_handler.error = (errorSAXFunc)gpx_error; sax_handler.fatalError = (fatalErrorSAXFunc)gpx_error; xmlSAXUserParseMemory(&sax_handler, &data, buffer, size); g_string_free(data.chars, TRUE); if(data.state != FINISH) { vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } if(policy_old && _route.head) { Point *src_first; Route *src, *dest; if(policy_old > 0) { /* Append to current route. */ src = &data.path.path.route; dest = &_route; } else { /* Prepend to current route. */ src = &_route; dest = &data.path.path.route; } /* Find src_first non-zero point. */ for(src_first = src->head - 1; src_first++ != src->tail; ) if(src_first->unity) break; /* Append route points from src to dest. */ if(src->tail >= src_first) { WayPoint *curr; guint num_dest_points = dest->tail - dest->head + 1; guint num_src_points = src->tail - src_first + 1; /* Adjust dest->tail to be able to fit src route data * plus room for more route data. */ route_resize(dest, num_dest_points + num_src_points); memcpy(dest->tail + 1, src_first, num_src_points * sizeof(Point)); dest->tail += num_src_points; /* Append waypoints from src to dest->. */ route_wresize(dest, (dest->wtail - dest->whead) + (src->wtail - src->whead) + 2); for(curr = src->whead - 1; curr++ != src->wtail; ) { (++(dest->wtail))->point = dest->head + num_dest_points + (curr->point - src_first); dest->wtail->desc = curr->desc; } } /* Kill old route - don't use MACRO_CLEAR_ROUTE(), because that * would free the string desc's that we just moved to data.route. */ g_free(src->head); g_free(src->whead); if(policy_old < 0) _route = *dest; } else { MACRO_CLEAR_ROUTE(_route); /* Overwrite with data.route. */ _route = data.path.path.route; route_resize(&_route, _route.tail - _route.head + 1); } vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } /** * This is a multi-purpose function for allowing the user to select a file * for either reading or writing. If chooser_action is * GTK_FILE_CHOOSER_ACTION_OPEN, then bytes_out and size_out must be * non-NULL. If chooser_action is GTK_FILE_CHOOSER_ACTION_SAVE, then * handle_out must be non-NULL. Either dir or file (or both) can be NULL. * This function returns TRUE if a file was successfully opened. */ static gboolean open_file(gchar **bytes_out, GnomeVFSHandle **handle_out, gint *size_out, gchar **dir, gchar **file, GtkFileChooserAction chooser_action) { GtkWidget *dialog; gboolean success = FALSE; printf("%s()\n", __PRETTY_FUNCTION__); dialog = hildon_file_chooser_dialog_new(GTK_WINDOW(_window),chooser_action); if(dir && *dir) gtk_file_chooser_set_current_folder_uri( GTK_FILE_CHOOSER(dialog), *dir); if(file && *file) { GValue val; gtk_file_chooser_set_uri( GTK_FILE_CHOOSER(dialog), *file); if(chooser_action == GTK_FILE_CHOOSER_ACTION_SAVE) { /* Work around a bug in HildonFileChooserDialog. */ memset(&val, 0, sizeof(val)); g_value_init(&val, G_TYPE_BOOLEAN); g_value_set_boolean(&val, FALSE); g_object_set_property(G_OBJECT(dialog), "autonaming", &val); gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), strrchr(*file, '/') + 1); } } gtk_widget_show_all(GTK_WIDGET(dialog)); while(!success && gtk_dialog_run(GTK_DIALOG(dialog))==GTK_RESPONSE_OK) { gchar *file_uri_str; GnomeVFSResult vfs_result; /* Get the selected filename. */ file_uri_str = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog)); if((chooser_action == GTK_FILE_CHOOSER_ACTION_OPEN && (GNOME_VFS_OK != (vfs_result = gnome_vfs_read_entire_file( file_uri_str, size_out, bytes_out)))) || (chooser_action == GTK_FILE_CHOOSER_ACTION_SAVE && GNOME_VFS_OK != (vfs_result = gnome_vfs_create( handle_out, file_uri_str, GNOME_VFS_OPEN_WRITE, FALSE, 0664)))) { gchar buffer[1024]; sprintf(buffer, "%s %s:\n%s", _("Failed to open file for"), chooser_action == GTK_FILE_CHOOSER_ACTION_OPEN ? _("reading") : _("writing"), gnome_vfs_result_to_string(vfs_result)); popup_error(dialog, buffer); } else success = TRUE; g_free(file_uri_str); } if(success) { /* Success!. */ if(dir) { if(*dir) g_free(*dir); *dir = gtk_file_chooser_get_current_folder_uri( GTK_FILE_CHOOSER(dialog)); } /* If desired, save the file for later. */ if(file) { if(*file) g_free(*file); *file = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog)); } } gtk_widget_destroy(dialog); vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, success); return success; } /** * Refresh the view based on the fact that the route has been automatically * updated. */ static gboolean auto_route_dl_idle_refresh() { /* Make sure we're still supposed to do work. */ vprintf("%s()\n", __PRETTY_FUNCTION__); if(!_autoroute_data.enabled) _autoroute_data.handle = NULL; else { if(parse_route_gpx(_autoroute_data.bytes, _autoroute_data.bytes_read,0)) { /* Find the nearest route point, if we're connected. */ route_find_nearest_point(); map_force_redraw(); } } _autoroute_data.in_progress = FALSE; vprintf("%s(): return\n", __PRETTY_FUNCTION__); return FALSE; } /** * Read the data provided by the given handle as GPX data, updating the * auto-route with that data. */ static void auto_route_dl_cb_read(GnomeVFSAsyncHandle *handle, GnomeVFSResult result, gpointer bytes, GnomeVFSFileSize bytes_requested, GnomeVFSFileSize bytes_read) { vprintf("%s(%s)\n", __PRETTY_FUNCTION__,gnome_vfs_result_to_string(result)); _autoroute_data.bytes_read += bytes_read; if(result == GNOME_VFS_OK) { /* Expand bytes and continue reading. */ if(_autoroute_data.bytes_read * 2 > _autoroute_data.bytes_maxsize) { _autoroute_data.bytes = g_renew(gchar, _autoroute_data.bytes, 2 * _autoroute_data.bytes_read); _autoroute_data.bytes_maxsize = 2 * _autoroute_data.bytes_read; } gnome_vfs_async_read(_autoroute_data.handle, _autoroute_data.bytes + _autoroute_data.bytes_read, _autoroute_data.bytes_maxsize - _autoroute_data.bytes_read, (GnomeVFSAsyncReadCallback)auto_route_dl_cb_read, NULL); } else g_idle_add((GSourceFunc)auto_route_dl_idle_refresh, NULL); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Open the given handle in preparation for reading GPX data. */ static void auto_route_dl_cb_open(GnomeVFSAsyncHandle *handle, GnomeVFSResult result) { vprintf("%s()\n", __PRETTY_FUNCTION__); if(result == GNOME_VFS_OK) gnome_vfs_async_read(_autoroute_data.handle, _autoroute_data.bytes, _autoroute_data.bytes_maxsize, (GnomeVFSAsyncReadCallback)auto_route_dl_cb_read, NULL); else { _autoroute_data.in_progress = FALSE; _autoroute_data.handle = NULL; } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * This function is periodically run to download updated auto-route data * from the route source. */ static gboolean auto_route_dl_idle() { gchar buffer[1024]; vprintf("%s(%f, %f, %s)\n", __PRETTY_FUNCTION__, _pos_lat, _pos_lon, _autoroute_data.dest); sprintf(buffer,"http://www.gnuite.com/cgi-bin/gpx.cgi?saddr=%f,%f&daddr=%s", _pos_lat, _pos_lon, _autoroute_data.dest); _autoroute_data.bytes_read = 0; gnome_vfs_async_open(&_autoroute_data.handle, buffer, GNOME_VFS_OPEN_READ, GNOME_VFS_PRIORITY_DEFAULT, (GnomeVFSAsyncOpenCallback)auto_route_dl_cb_open, NULL); vprintf("%s(): return\n", __PRETTY_FUNCTION__); return FALSE; } /** * Cancel the current auto-route. */ static void cancel_autoroute() { printf("%s()\n", __PRETTY_FUNCTION__); if(_autoroute_data.enabled) { _autoroute_data.enabled = FALSE; if(_autoroute_data.dest) { g_free(_autoroute_data.dest); _autoroute_data.dest = NULL; } if(_autoroute_data.handle) { gnome_vfs_async_cancel(_autoroute_data.handle); _autoroute_data.handle = NULL; } if(_autoroute_data.bytes) { g_free(_autoroute_data.bytes); _autoroute_data.bytes = NULL; } _autoroute_data.in_progress = FALSE; } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Save state and destroy all non-UI elements created by this program in * preparation for exiting. */ static void maemo_mapper_destroy(void) { printf("%s()\n", __PRETTY_FUNCTION__); config_save(); rcvr_disconnect(); /* _program and widgets have already been destroyed. */ MACRO_CLEAR_TRACK(_track); if(_route.head) MACRO_CLEAR_TRACK(_route); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /** * Initialize everything required in preparation for calling gtk_main(). */ static void maemo_mapper_init(gint argc, gchar **argv) { printf("%s()\n", __PRETTY_FUNCTION__); config_init(); /* Initialize _program. */ _program = HILDON_PROGRAM(hildon_program_get_instance()); g_set_application_name("Maemo Mapper"); /* Initialize _window. */ _window = GTK_WIDGET(hildon_window_new()); hildon_program_add_window(_program, HILDON_WINDOW(_window)); /* Create and add widgets and supporting data. */ _map_widget = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(_window), _map_widget); gtk_widget_realize(_map_widget); _map_pixmap = gdk_pixmap_new( _map_widget->window, BUF_WIDTH_PIXELS, BUF_HEIGHT_PIXELS, -1); /* -1: use bit depth of widget->window. */ /* Connect signals. */ g_signal_connect(G_OBJECT(_window), "destroy", G_CALLBACK(gtk_main_quit), NULL); g_signal_connect(G_OBJECT(_window), "key_press_event", G_CALLBACK(window_cb_key_press), NULL); g_signal_connect(G_OBJECT(_window), "key_release_event", G_CALLBACK(window_cb_key_release), NULL); g_signal_connect(G_OBJECT(_map_widget), "configure_event", G_CALLBACK(map_cb_configure), NULL); g_signal_connect(G_OBJECT(_map_widget), "expose_event", G_CALLBACK(map_cb_expose), NULL); g_signal_connect(G_OBJECT(_map_widget), "button_press_event", G_CALLBACK(map_cb_button_press), NULL); g_signal_connect(G_OBJECT(_map_widget), "button_release_event", G_CALLBACK(map_cb_button_release), NULL); gtk_widget_add_events(_map_widget, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); osso_hw_set_event_cb(_osso, NULL, osso_cb_hw_state, NULL); gnome_vfs_async_set_job_limit(24); /* Initialize data. */ /* set XML_TZONE */ { time_t time1; struct tm time2; time1 = time(NULL); localtime_r(&time1, &time2); sprintf(XML_TZONE, "%+03ld:%02ld", (time2.tm_gmtoff / 60 / 60), (time2.tm_gmtoff / 60) % 60); } memset(&_track, 0, sizeof(_track)); memset(&_route, 0, sizeof(_route)); _last_spoken_phrase = g_strdup(""); /* Set up track array. */ MACRO_INIT_TRACK(_track); _downloads_hash = g_hash_table_new((GHashFunc)download_hashfunc, (GEqualFunc)download_equalfunc); memset(&_autoroute_data, 0, sizeof(_autoroute_data)); integerize_data(); /* Initialize our line styles. */ update_gcs(); menu_init(); /* If present, attempt to load the file specified on the command line. */ if(argc > 1) { GnomeVFSResult vfs_result; gint size; gchar *buffer; gchar *file_uri; /* Get the selected filename. */ file_uri = gnome_vfs_make_uri_from_shell_arg(argv[1]); if(GNOME_VFS_OK != (vfs_result = gnome_vfs_read_entire_file( file_uri, &size, &buffer))) { gchar buffer[1024]; sprintf(buffer, "%s %s:\n%s", _("Failed to open file for"), _("reading"), gnome_vfs_result_to_string(vfs_result)); popup_error(_window, buffer); } else { /* If auto is enabled, append the route, otherwise replace it. */ if(parse_route_gpx(buffer, size, 0)) hildon_banner_show_information( _window, NULL, _("Route Opened")); else popup_error(_window, _("Error parsing GPX file.")); g_free(buffer); } g_free(file_uri); } gtk_idle_add((GSourceFunc)window_present, NULL); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } /**************************************************************************** * ABOVE: ROUTINES ********************************************************** ****************************************************************************/ /**************************************************************************** * BELOW: MAIN ************************************************************** ****************************************************************************/ gint main(gint argc, gchar *argv[]) { printf("%s()\n", __PRETTY_FUNCTION__); g_thread_init(NULL); /* Initialize _osso. */ _osso = osso_initialize("com.gnuite.maemo_mapper", VERSION, TRUE, NULL); if(!_osso) { fprintf(stderr, "osso_initialize failed.\n"); return 1; } gtk_init(&argc, &argv); /* Init gconf. */ g_type_init(); gconf_init(argc, argv, NULL); gnome_vfs_init(); maemo_mapper_init(argc, argv); if(OSSO_OK != osso_rpc_set_default_cb_f(_osso, dbus_cb_default, NULL)) { fprintf(stderr, "osso_rpc_set_default_cb_f failed.\n"); return 1; } gtk_main(); maemo_mapper_destroy(); osso_deinitialize(_osso); vprintf("%s(): return\n", __PRETTY_FUNCTION__); return 0; } /**************************************************************************** * ABOVE: MAIN ************************************************************** ****************************************************************************/ /**************************************************************************** * BELOW: CALLBACKS ********************************************************* ****************************************************************************/ static gint dbus_cb_default(const gchar *interface, const gchar *method, GArray *arguments, gpointer data, osso_rpc_t *retval) { printf("%s()\n", __PRETTY_FUNCTION__); if(!strcmp(method, "top_application")) gtk_idle_add((GSourceFunc)window_present, NULL); retval->type = DBUS_TYPE_INVALID; vprintf("%s(): return\n", __PRETTY_FUNCTION__); return OSSO_OK; } static void osso_cb_hw_state(osso_hw_state_t *state, gpointer data) { static gboolean _must_save_data = FALSE; static gboolean _must_switch_off = TRUE; printf("%s()\n", __PRETTY_FUNCTION__); if(state->system_inactivity_ind) { if(_must_save_data) _must_save_data = FALSE; else if(_conn_state > RCVR_OFF) { GConfClient *gconf_client = gconf_client_get_default(); if (gconf_client) { GConfValue *value=gconf_client_get(gconf_client, GCONF_KEY_DISCONNECT_ON_COVER, NULL); if (value) { if (gconf_value_get_bool(value)) _must_switch_off=TRUE; else _must_switch_off=FALSE; gconf_value_free(value); } g_object_unref(gconf_client); } if (_must_switch_off) { set_conn_state(RCVR_OFF); rcvr_disconnect(); track_add(0, FALSE); } /* Pretend the autoroute is in progress to avoid download. */ if(_autoroute_data.enabled) _autoroute_data.in_progress = TRUE; } } else if(state->save_unsaved_data_ind) { config_save(); _must_save_data = TRUE; } else if(_conn_state == RCVR_OFF && _enable_gps) { set_conn_state(RCVR_DOWN); rcvr_connect_later(); if(_autoroute_data.enabled) _autoroute_data.in_progress = TRUE; } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } static gboolean key_zoom_timeout() { if(_key_zoom_new < _zoom) { /* We're currently zooming in (_zoom is decreasing). */ if(_key_zoom_new) /* We can zoom some more. Hurray! */ _key_zoom_new--; else /* We can't zoom anymore. Booooo! */ return FALSE; } else { /* We're currently zooming out (_zoom is increasing). */ if(_key_zoom_new < (MAX_ZOOM - 1)) /* We can zoom some more. Hurray! */ _key_zoom_new++; else /* We can't zoom anymore. Booooo! */ return FALSE; } /* We can zoom more - tell them how much they're zooming. */ { gchar buffer[32]; sprintf(buffer, "%s %d", _("Zoom to Level"), _key_zoom_new); hildon_banner_show_information(_window, NULL, buffer); } return TRUE; } static gboolean window_cb_key_press(GtkWidget* widget, GdkEventKey *event) { printf("%s()\n", __PRETTY_FUNCTION__); switch (event->keyval) { case HILDON_HARDKEY_UP: map_pan(0, -PAN_UNITS); return TRUE; case HILDON_HARDKEY_DOWN: map_pan(0, PAN_UNITS); return TRUE; case HILDON_HARDKEY_LEFT: map_pan(-PAN_UNITS, 0); return TRUE; case HILDON_HARDKEY_RIGHT: map_pan(PAN_UNITS, 0); return TRUE; case HILDON_HARDKEY_SELECT: switch(_center_mode) { case CENTER_LATLON: case CENTER_WAS_LEAD: gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM( _menu_ac_lead_item), TRUE); break; case CENTER_LEAD: case CENTER_WAS_LATLON: gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM( _menu_ac_latlon_item), TRUE); break; } return TRUE; case HILDON_HARDKEY_FULLSCREEN: gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM( _menu_fullscreen_item), !_fullscreen); return TRUE; case HILDON_HARDKEY_INCREASE: case HILDON_HARDKEY_DECREASE: if(!_key_zoom_timeout_sid) { _key_zoom_new = _zoom + (event->keyval == HILDON_HARDKEY_INCREASE ? -1 : 1); /* Remember, _key_zoom_new is unsigned. */ if(_key_zoom_new < MAX_ZOOM) { gchar buffer[80]; sprintf(buffer, "%s %d", _("Zoom to Level"), _key_zoom_new); hildon_banner_show_information(_window, NULL, buffer); _key_zoom_timeout_sid = g_timeout_add( 500, key_zoom_timeout, NULL); } } return TRUE; case HILDON_HARDKEY_ESC: { if (_map_uri_format) g_free(_map_uri_format); if (_map_dir_name) g_free(_map_dir_name); if (_map_setting == 2) { printf("setting 1\n"); _map_setting = 1; if(strlen(_map_uri_format_1)) _map_uri_format = g_strdup(_map_uri_format_1); else _map_uri_format = NULL; if(strlen(_map_dir_name_1)) _map_dir_name = g_strdup(_map_dir_name_1); else _map_dir_name = NULL; } else { printf("setting 2\n"); _map_setting = 2; if(strlen(_map_uri_format_2)) _map_uri_format = g_strdup(_map_uri_format_2); else _map_uri_format = NULL; if(strlen(_map_dir_name_2)) _map_dir_name = g_strdup(_map_dir_name_2); else _map_dir_name = NULL; } if (!config_set_map_dir_name(gnome_vfs_expand_initial_tilde(_map_dir_name))) popup_error(_window, "Could not create Map Cache directory."); map_force_redraw(); config_save(); } return TRUE; default: return FALSE; } } static gboolean window_cb_key_release(GtkWidget* widget, GdkEventKey *event) { printf("%s()\n", __PRETTY_FUNCTION__); switch (event->keyval) { case HILDON_HARDKEY_INCREASE: case HILDON_HARDKEY_DECREASE: if(_key_zoom_timeout_sid) { g_source_remove(_key_zoom_timeout_sid); _key_zoom_timeout_sid = 0; map_set_zoom(_key_zoom_new); } return TRUE; default: return FALSE; } } static gboolean map_cb_configure(GtkWidget *widget, GdkEventConfigure *event) { printf("%s(%d, %d)\n", __PRETTY_FUNCTION__, _map_widget->allocation.width, _map_widget->allocation.height); _screen_width_pixels = _map_widget->allocation.width; _screen_height_pixels = _map_widget->allocation.height; _screen_grids_halfwidth = pixel2grid(_screen_width_pixels) / 2; _screen_grids_halfheight = pixel2grid(_screen_height_pixels) / 2; MACRO_RECALC_FOCUS_BASE(); MACRO_RECALC_FOCUS_SIZE(); _min_center.unitx = pixel2unit(grid2pixel(_screen_grids_halfwidth)); _min_center.unity = pixel2unit(grid2pixel(_screen_grids_halfheight)); _max_center.unitx = WORLD_SIZE_UNITS-grid2unit(_screen_grids_halfwidth) - 1; _max_center.unity = WORLD_SIZE_UNITS-grid2unit(_screen_grids_halfheight)- 1; map_center_unit(_center.unitx, _center.unity); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean map_cb_expose(GtkWidget *widget, GdkEventExpose *event) { printf("%s(%d, %d, %d, %d)\n", __PRETTY_FUNCTION__, event->area.x, event->area.y, event->area.width, event->area.height); gdk_draw_drawable( _map_widget->window, _gc_mark, _map_pixmap, event->area.x + _offsetx, event->area.y + _offsety, event->area.x, event->area.y, event->area.width, event->area.height); map_draw_mark(); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean map_cb_button_press(GtkWidget *widget, GdkEventButton *event) { printf("%s()\n", __PRETTY_FUNCTION__); _cmenu_position_x = event->x + 0.5; _cmenu_position_y = event->y + 0.5; /* Return FALSE to allow context menu to work. */ vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } static gboolean map_cb_button_release(GtkWidget *widget, GdkEventButton *event) { printf("%s()\n", __PRETTY_FUNCTION__); #ifdef DEBUG if(event->button != 1) { _pos.unitx = x2unit((gint)(event->x + 0.5)); _pos.unity = y2unit((gint)(event->y + 0.5)); unit2latlon(_pos.unitx, _pos.unity, _pos_lat, _pos_lon); _speed = 20.f; integerize_data(); track_add(time(NULL), FALSE); refresh_mark(); } else #endif { if(_center_mode > 0) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM( _menu_ac_none_item), TRUE); map_center_unit( x2unit((gint)(event->x + 0.5)), y2unit((gint)(event->y + 0.5))); } /* Return FALSE to avoid context menu (if it hasn't popped up already). */ vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } static gboolean channel_cb_error(GIOChannel *src, GIOCondition condition, gpointer data) { printf("%s(%d)\n", __PRETTY_FUNCTION__, condition); /* An error has occurred - re-connect(). */ rcvr_disconnect(); track_add(0, FALSE); if(_conn_state > RCVR_OFF) { set_conn_state(RCVR_DOWN); rcvr_connect_now(); } vprintf("%s(): return\n", __PRETTY_FUNCTION__); return FALSE; } static gboolean channel_cb_connect(GIOChannel *src, GIOCondition condition, gpointer data) { int error, size = sizeof(error); printf("%s(%d)\n", __PRETTY_FUNCTION__, condition); if(*_rcvr_mac != '/' && (getsockopt(_fd, SOL_SOCKET, SO_ERROR, &error, &size) || error)) { printf("%s(): Error connecting to receiver; retrying...\n", __PRETTY_FUNCTION__); /* Try again. */ rcvr_disconnect(); rcvr_connect_later(); } else { printf("%s(): Connected to receiver!\n", __PRETTY_FUNCTION__); set_conn_state(RCVR_UP); _input_sid = g_io_add_watch_full(_channel, G_PRIORITY_HIGH_IDLE, G_IO_IN | G_IO_PRI, channel_cb_input, NULL, NULL); } _connect_sid = 0; vprintf("%s(): return\n", __PRETTY_FUNCTION__); return FALSE; } #define MACRO_PARSE_INT(tofill, str) { \ gchar *error_check; \ (tofill) = strtol((str), &error_check, 10); \ if(error_check == (str)) \ { \ fprintf(stderr, "Failed to parse string as int: %s\n", str); \ hildon_banner_show_information(_window, NULL, \ _("Invalid NMEA input from receiver!")); \ return; \ } \ } #define MACRO_PARSE_FLOAT(tofill, str) { \ gchar *error_check; \ (tofill) = g_ascii_strtod((str), &error_check); \ if(error_check == (str)) \ { \ fprintf(stderr, "Failed to parse string as float: %s\n", str); \ hildon_banner_show_information(_window, NULL, \ _("Invalid NMEA input from receiver!")); \ return; \ } \ } static void channel_parse_rmc(gchar *sentence) { /* Recommended Minimum Navigation Information C * 1) UTC Time * 2) Status, V=Navigation receiver warning A=Valid * 3) Latitude * 4) N or S * 5) Longitude * 6) E or W * 7) Speed over ground, knots * 8) Track made good, degrees true * 9) Date, ddmmyy * 10) Magnetic Variation, degrees * 11) E or W * 12) FAA mode indicator (NMEA 2.3 and later) * 13) Checksum */ gchar *token, *dpoint; gdouble tmpd; guint tmpi; gboolean newly_fixed = FALSE; vprintf("%s(): %s", __PRETTY_FUNCTION__, sentence); #define DELIM "," /* Skip time. */ token = strsep(&sentence, DELIM); token = strsep(&sentence, DELIM); /* Token is now Status. */ if(*token != 'A') { /* Data is invalid - not enough satellites?. */ if(_conn_state != RCVR_UP) { set_conn_state(RCVR_UP); track_add(0, FALSE); } } else { /* Data is valid. */ if(_conn_state != RCVR_FIXED) { newly_fixed = TRUE; set_conn_state(RCVR_FIXED); } } /* Parse the latitude. */ token = strsep(&sentence, DELIM); if(*token) { dpoint = strchr(token, '.'); MACRO_PARSE_FLOAT(tmpd, dpoint - 2); dpoint[-2] = '\0'; MACRO_PARSE_INT(tmpi, token); _pos_lat = tmpi + (tmpd * (1.0 / 60.0)); } /* Parse N or S. */ token = strsep(&sentence, DELIM); if(*token && token[0] == 'S') _pos_lat = -_pos_lat; /* Parse the longitude. */ token = strsep(&sentence, DELIM); if(*token) { dpoint = strchr(token, '.'); MACRO_PARSE_FLOAT(tmpd, dpoint - 2); dpoint[-2] = '\0'; MACRO_PARSE_INT(tmpi, token); _pos_lon = tmpi + (tmpd * (1.0 / 60.0)); } /* Parse E or W. */ token = strsep(&sentence, DELIM); if(*token && token[0] == 'W') _pos_lon = -_pos_lon; /* Parse speed over ground, knots. */ token = strsep(&sentence, DELIM); if(*token) MACRO_PARSE_FLOAT(_speed, token); /* Parse heading, degrees from true north. */ token = strsep(&sentence, DELIM); if(*token) MACRO_PARSE_FLOAT(_heading, token); /* Translate data into integers. */ integerize_data(); /* Add new data to track. */ if(_conn_state == RCVR_FIXED) track_add(time(NULL), newly_fixed); /* Move mark to new location. */ refresh_mark(); vprintf("%s(): return\n", __PRETTY_FUNCTION__); } static void channel_parse_gsv(gchar *sentence) { /* Must be GSV - Satellites in view * 1) total number of messages * 2) message number * 3) satellites in view * 4) satellite number * 5) elevation in degrees (0-90) * 6) azimuth in degrees to true north (0-359) * 7) SNR in dB (0-99) * more satellite infos like 4)-7) * n) checksum */ gchar *token; guint msgcnt, nummsgs; static guint running_total = 0; static guint num_sats_used = 0; vprintf("%s(): %s", __PRETTY_FUNCTION__, sentence); /* Parse number of messages. */ token = strsep(&sentence, DELIM); if(!sentence) return; /* because this is an invalid sentence. */ MACRO_PARSE_INT(nummsgs, token); /* Parse message number. */ token = strsep(&sentence, DELIM); if(!sentence) return; /* because this is an invalid sentence. */ MACRO_PARSE_INT(msgcnt, token); /* Skip number of satellites in view. */ token = strsep(&sentence, DELIM); /* Loop until there are no more satellites to parse. */ while(sentence) { guint snr; /* Skip satellite number. */ token = strsep(&sentence, DELIM); if(!sentence) break; /* because this is an invalid sentence. */ /* Skip elevation in degrees (0-90). */ token = strsep(&sentence, DELIM); if(!sentence) break; /* because this is an invalid sentence. */ /* Skip azimuth in degrees to true north (0-359). */ token = strsep(&sentence, DELIM); if(!sentence) break; /* because this is an invalid sentence. */ /* Parse SNR. */ token = strsep(&sentence, DELIM); if((snr = atoi(token))) { /* Snr is non-zero - add to total and count as used. */ running_total += snr; num_sats_used++; } } if(msgcnt == nummsgs) { /* This is the last message. Calculate signal strength. */ if(num_sats_used) { gdouble fraction = running_total * sqrt(num_sats_used) / num_sats_used / 100.0; BOUND(fraction, 0.0, 1.0); running_total = 0; num_sats_used = 0; hildon_banner_set_fraction(HILDON_BANNER(_fix_banner), fraction); } /* Keep awake while they watch the progress bar. */ KEEP_DISPLAY_ON(); } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } static gboolean channel_cb_input(GIOChannel *src, GIOCondition condition, gpointer data) { gchar *sentence; vprintf("%s(%d)\n", __PRETTY_FUNCTION__, condition); while(G_IO_STATUS_NORMAL == g_io_channel_read_line( _channel, &sentence, NULL, NULL, NULL) && sentence) { gchar *sptr = sentence + 1; guint csum = 0; while(*sptr && *sptr != '*') csum ^= *sptr++; if (!*sptr || csum == strtol(sptr+1, NULL, 16)) { if(*sptr) *sptr = '\0'; /* take checksum out of the sentence. */ if(!strncmp(sentence + 3, "RMC", 3)) channel_parse_rmc(sentence + 7); else if(_conn_state == RCVR_UP && !strncmp(sentence + 3, "GSV", 3)) channel_parse_gsv(sentence + 7); } else { printf("%s: Bad checksum in NMEA sentence:\n%s\n", __PRETTY_FUNCTION__, sentence); } g_free(sentence); } vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean gps_toggled_from(GtkToggleButton *chk_gps, GtkWidget *txt_from) { gchar buffer[80]; gchar strlat[32]; gchar strlon[32]; printf("%s()\n", __PRETTY_FUNCTION__); g_ascii_formatd(strlat, 80, "%.06f", _pos_lat); g_ascii_formatd(strlon, 80, "%.06f", _pos_lon); sprintf(buffer, "%s, %s", strlat, strlon); gtk_widget_set_sensitive(txt_from, !gtk_toggle_button_get_active(chk_gps)); gtk_entry_set_text(GTK_ENTRY(txt_from), buffer); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean gps_toggled_auto(GtkToggleButton *chk_gps, GtkWidget *chk_auto) { printf("%s()\n", __PRETTY_FUNCTION__); gtk_widget_set_sensitive(chk_auto, gtk_toggle_button_get_active(chk_gps)); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } /** * Display a dialog box to the user asking them to download a route. The * "From" and "To" textfields may be initialized using the first two * parameters. The third parameter, if set to TRUE, will cause the "Use GPS * Location" checkbox to be enabled, which automatically sets the "From" to the * current GPS position (this overrides any value that may have been passed as * the "To" initializer). * None of the passed strings are freed - that is left to the caller, and it is * safe to free either string as soon as this function returns. */ static gboolean route_download(gchar *from, gchar *to, gboolean from_here) { GtkWidget *dialog; GtkWidget *table; GtkWidget *label; GtkWidget *chk_gps; GtkWidget *chk_auto; GtkWidget *txt_from; GtkWidget *txt_to; GtkWidget *hbox; GtkEntryCompletion *from_comp; GtkEntryCompletion *to_comp; gchar *bytes = NULL; gint size; printf("%s()\n", __PRETTY_FUNCTION__); dialog = gtk_dialog_new_with_buttons(_("Download Route"), GTK_WINDOW(_window), GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, NULL); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), table = gtk_table_new(2, 4, FALSE), TRUE, TRUE, 0); from_comp = gtk_entry_completion_new(); gtk_entry_completion_set_model(from_comp, GTK_TREE_MODEL(_loc_model)); gtk_entry_completion_set_text_column(from_comp, 0); to_comp = gtk_entry_completion_new(); gtk_entry_completion_set_model(to_comp, GTK_TREE_MODEL(_loc_model)); gtk_entry_completion_set_text_column(to_comp, 0); /* Auto. */ gtk_table_attach(GTK_TABLE(table), hbox = gtk_hbox_new(FALSE, 6), 0, 2, 0, 1, 0, 0, 2, 4); gtk_box_pack_start(GTK_BOX(hbox), chk_gps = gtk_check_button_new_with_label( _("Use GPS Location")), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(hbox), chk_auto = gtk_check_button_new_with_label( _("Auto-Update")), TRUE, TRUE, 0); gtk_widget_set_sensitive(chk_auto, FALSE); /* Origin. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Origin")), 0, 1, 1, 2, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), txt_from = gtk_entry_new(), 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4); gtk_entry_set_completion(GTK_ENTRY(txt_from), from_comp); gtk_entry_set_width_chars(GTK_ENTRY(txt_from), 25); /* Destination. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Destination")), 0, 1, 2, 3, GTK_FILL, 0, 2, 4); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), txt_to = gtk_entry_new(), 1, 2, 2, 3, GTK_EXPAND | GTK_FILL, 0, 2, 4); gtk_entry_set_completion(GTK_ENTRY(txt_to), to_comp); gtk_entry_set_width_chars(GTK_ENTRY(txt_to), 25); g_signal_connect(G_OBJECT(chk_gps), _("toggled"), G_CALLBACK(gps_toggled_from), txt_from); g_signal_connect(G_OBJECT(chk_gps), _("toggled"), G_CALLBACK(gps_toggled_auto), chk_auto); /* Initialize fields. */ gtk_entry_set_text(GTK_ENTRY(txt_from), (from ? from : "")); gtk_entry_set_text(GTK_ENTRY(txt_to), (to ? to : "")); if(from_here) gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk_gps), TRUE); gtk_widget_show_all(dialog); while(!bytes && GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) { GnomeVFSResult vfs_result; gchar buffer[1024]; const gchar *from, *to; gchar *from_escaped, *to_escaped; from = gtk_entry_get_text(GTK_ENTRY(txt_from)); if(!strlen(from)) { popup_error(dialog, _("Please specify a start location.")); continue; } to = gtk_entry_get_text(GTK_ENTRY(txt_to)); if(!strlen(to)) { popup_error(dialog, _("Please specify an end location.")); continue; } from_escaped = gnome_vfs_escape_string(from); to_escaped = gnome_vfs_escape_string(to); sprintf(buffer, "http://www.gnuite.com/cgi-bin/gpx.cgi?" "saddr=%s&daddr=%s", from_escaped, to_escaped); g_free(from_escaped); g_free(to_escaped); if(GNOME_VFS_OK != (vfs_result = gnome_vfs_read_entire_file( buffer, &size, &bytes))) { gchar buffer[1024]; sprintf(buffer, "%s:\n%s", _("Failed to connect to GPX Directions server"), gnome_vfs_result_to_string(vfs_result)); popup_error(dialog, buffer); } else if(strncmp(bytes, "point.unity) break; /* Write the header. */ WRITE_STRING(XML_TRKSEG_HEADER); /* Curr points to first non-zero point. */ for(curr--; curr++ != _track.tail; ) { gfloat lat, lon; if(curr->point.unity) { gchar buffer[80]; gchar strlat[80], strlon[80]; if(trkseg_break) { /* First trkpt of the segment - write trkseg header. */ WRITE_STRING(" \n" /* Write trkseg header. */ " \n"); trkseg_break = FALSE; } unit2latlon(curr->point.unitx, curr->point.unity, lat, lon); g_ascii_formatd(strlat, 80, "%.06f", lat); g_ascii_formatd(strlon, 80, "%.06f", lon); sprintf(buffer, " time) { WRITE_STRING(">\n \n" " \n"); } else WRITE_STRING("/>\n"); } else trkseg_break = TRUE; } /* Write the footer. */ WRITE_STRING(XML_TRKSEG_FOOTER); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean write_route_gpx(GnomeVFSHandle *handle) { Point *curr; WayPoint *wcurr; gboolean trkseg_break = FALSE; /* Find first non-zero point. */ for(curr = _route.head-1, wcurr = _route.whead; curr++ != _route.tail; ) if(curr->unity) break; else if(curr == wcurr->point) wcurr++; /* Write the header. */ WRITE_STRING(XML_TRKSEG_HEADER); /* Curr points to first non-zero point. */ for(curr--; curr++ != _route.tail; ) { gfloat lat, lon; if(curr->unity) { gchar buffer[80]; gchar strlat[80], strlon[80]; if(trkseg_break) { /* First trkpt of the segment - write trkseg header. */ WRITE_STRING(" \n" " \n"); trkseg_break = FALSE; } unit2latlon(curr->unitx, curr->unity, lat, lon); g_ascii_formatd(strlat, 80, "%.06f", lat); g_ascii_formatd(strlon, 80, "%.06f", lon); sprintf(buffer, " point) sprintf(buffer + strlen(buffer), ">%s\n", wcurr++->desc); else strcat(buffer, "/>\n"); WRITE_STRING(buffer); } else trkseg_break = TRUE; } /* Write the footer. */ WRITE_STRING(XML_TRKSEG_FOOTER); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_track_save(GtkAction *action) { GnomeVFSHandle *handle; printf("%s()\n", __PRETTY_FUNCTION__); if(open_file(NULL, &handle, NULL, NULL, &_track_file_uri, GTK_FILE_CHOOSER_ACTION_SAVE)) { if(write_track_gpx(handle)) hildon_banner_show_information(_window, NULL, _("Track Saved")); else popup_error(_window, _("Error writing GPX file.")); gnome_vfs_close(handle); } vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_track_mark_way(GtkAction *action) { printf("%s()\n", __PRETTY_FUNCTION__); if(_track.tail->point.unity) { guint x1, y1; /* To mark a "waypoint" in a track, we'll add a (0, 0) point and then * another instance of the most recent track point. */ MACRO_TRACK_INCREMENT_TAIL(_track); *_track.tail = _track_null; MACRO_TRACK_INCREMENT_TAIL(_track); *_track.tail = _track.tail[2]; /** Instead of calling map_render_paths(), we'll just add the waypoint * ourselves. */ x1 = unit2bufx(_track.tail->point.unitx); y1 = unit2bufy(_track.tail->point.unity); /* Make sure this circle will be visible. */ if((x1 < BUF_WIDTH_PIXELS) && ((unsigned)y1 < BUF_HEIGHT_PIXELS)) gdk_draw_arc(_map_pixmap, _gc_track_break, FALSE, /* FALSE: not filled. */ x1 - _draw_line_width, y1 - _draw_line_width, 2 * _draw_line_width, 2 * _draw_line_width, 0, /* start at 0 degrees. */ 360 * 64); } vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_route_save(GtkAction *action) { GnomeVFSHandle *handle; printf("%s()\n", __PRETTY_FUNCTION__); if(!_route.head) { popup_error(_window, "No route is loaded."); return TRUE; } if(open_file(NULL, &handle, NULL, &_route_dir_uri, NULL, GTK_FILE_CHOOSER_ACTION_SAVE)) { if(write_route_gpx(handle)) hildon_banner_show_information(_window, NULL, _("Route Saved")); else popup_error(_window, _("Error writing GPX file.")); gnome_vfs_close(handle); } vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_track_clear(GtkAction *action) { printf("%s()\n", __PRETTY_FUNCTION__); _track.tail = _track.head; map_force_redraw(); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_show_tracks(GtkAction *action) { printf("%s()\n", __PRETTY_FUNCTION__); _show_tracks ^= TRACKS_MASK; if(gtk_check_menu_item_get_active( GTK_CHECK_MENU_ITEM(_menu_show_tracks_item))) { _show_tracks |= TRACKS_MASK; map_render_paths(); MACRO_QUEUE_DRAW_AREA(); hildon_banner_show_information( _window, NULL, _("Tracks are now shown")); } else { _show_tracks &= ~TRACKS_MASK; map_force_redraw(); hildon_banner_show_information( _window, NULL, _("Tracks are now hidden")); } vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_show_routes(GtkAction *action) { printf("%s()\n", __PRETTY_FUNCTION__); if(gtk_check_menu_item_get_active( GTK_CHECK_MENU_ITEM(_menu_show_routes_item))) { _show_tracks |= ROUTES_MASK; map_render_paths(); MACRO_QUEUE_DRAW_AREA(); hildon_banner_show_information( _window, NULL, _("Routes are now shown")); } else { _show_tracks &= ~ROUTES_MASK; map_force_redraw(); hildon_banner_show_information( _window, NULL, "Routes are now hidden"); } vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_show_velvec(GtkAction *action) { printf("%s()\n", __PRETTY_FUNCTION__); _show_velvec = gtk_check_menu_item_get_active( GTK_CHECK_MENU_ITEM(_menu_show_velvec_item)); map_move_mark(); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_ac_lead(GtkAction *action) { guint new_center_unitx, new_center_unity; printf("%s()\n", __PRETTY_FUNCTION__); _center_mode = CENTER_LEAD; hildon_banner_show_information(_window, NULL, _("Auto-Center Mode: Lead")); MACRO_RECALC_CENTER(new_center_unitx, new_center_unity); map_center_unit(new_center_unitx, new_center_unity); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_ac_latlon(GtkAction *action) { guint new_center_unitx, new_center_unity; printf("%s()\n", __PRETTY_FUNCTION__); _center_mode = CENTER_LATLON; hildon_banner_show_information(_window, NULL, _("Auto-Center Mode: Lat/Lon")); MACRO_RECALC_CENTER(new_center_unitx, new_center_unity); map_center_unit(new_center_unitx, new_center_unity); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_ac_none(GtkAction *action) { printf("%s()\n", __PRETTY_FUNCTION__); _center_mode = -_center_mode; hildon_banner_show_information(_window, NULL, _("Auto-Center Off")); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_maps_dlroute(GtkAction *action) { guint last_tilex, last_tiley; Point *curr; printf("%s()\n", __PRETTY_FUNCTION__); if(!_route.head) { popup_error(_window, "No route is loaded."); return TRUE; } last_tilex = 0; last_tiley = 0; for(curr = _route.head - 1; ++curr != _route.tail; ) { if(curr->unity) { guint tilex = unit2tile(curr->unitx); guint tiley = unit2tile(curr->unity); if(tilex != last_tilex || tiley != last_tiley) { guint minx, miny, maxx, maxy, x, y; if(last_tiley != 0) { minx = MIN(tilex, last_tilex) - 2; miny = MIN(tiley, last_tiley) - 2; maxx = MAX(tilex, last_tilex) + 2; maxy = MAX(tiley, last_tiley) + 2; } else { minx = tilex - 2; miny = tiley - 2; maxx = tilex + 2; maxy = tiley + 2; } for(x = minx; x <= maxx; x++) for(y = miny; y <= maxy; y++) map_initiate_download(x, y, _zoom, INITIAL_DOWNLOAD_RETRIES); last_tilex = tilex; last_tiley = tiley; } } } vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } typedef struct _DlAreaInfo DlAreaInfo; struct _DlAreaInfo { GtkWidget *notebook; GtkWidget *txt_topleft_lat; GtkWidget *txt_topleft_lon; GtkWidget *txt_botright_lat; GtkWidget *txt_botright_lon; GtkWidget *chk_zoom_levels[MAX_ZOOM]; }; static void dlarea_clear(GtkWidget *widget, DlAreaInfo *dlarea_info) { guint i; printf("%s()\n", __PRETTY_FUNCTION__); if(gtk_notebook_get_current_page(GTK_NOTEBOOK(dlarea_info->notebook))) /* This is the second page (the "Zoom" page) - clear the checks. */ for(i = 0; i < MAX_ZOOM; i++) gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(dlarea_info->chk_zoom_levels[i]), FALSE); else { /* This is the first page (the "Area" page) - clear the text fields. */ gtk_entry_set_text(GTK_ENTRY(dlarea_info->txt_topleft_lat), ""); gtk_entry_set_text(GTK_ENTRY(dlarea_info->txt_topleft_lon), ""); gtk_entry_set_text(GTK_ENTRY(dlarea_info->txt_botright_lat), ""); gtk_entry_set_text(GTK_ENTRY(dlarea_info->txt_botright_lon), ""); } vprintf("%s(): return\n", __PRETTY_FUNCTION__); } static gboolean menu_cb_maps_dlarea(GtkAction *action) { GtkWidget *dialog; GtkWidget *table; GtkWidget *label; GtkWidget *button; GtkWidget *lbl_gps_lat; GtkWidget *lbl_gps_lon; GtkWidget *lbl_center_lat; GtkWidget *lbl_center_lon; DlAreaInfo dlarea_info; gchar buffer[80]; gfloat lat, lon; guint i; printf("%s()\n", __PRETTY_FUNCTION__); dialog = gtk_dialog_new_with_buttons(_("Download Maps by Area"), GTK_WINDOW(_window), GTK_DIALOG_MODAL, GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL); gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area), button = gtk_button_new_with_label(_("Clear"))); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(dlarea_clear), &dlarea_info); gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT); gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), dlarea_info.notebook = gtk_notebook_new(), TRUE, TRUE, 0); gtk_notebook_append_page(GTK_NOTEBOOK(dlarea_info.notebook), table = gtk_table_new(2, 3, FALSE), label = gtk_label_new(_("Area"))); /* Clear button and Label Columns. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Latitude")), 1, 2, 0, 1, GTK_FILL, 0, 4, 0); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Longitude")), 2, 3, 0, 1, GTK_FILL, 0, 4, 0); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); /* GPS. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("GPS Location")), 0, 1, 1, 2, GTK_FILL, 0, 4, 0); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), lbl_gps_lat = gtk_label_new(""), 1, 2, 1, 2, GTK_FILL, 0, 4, 0); gtk_label_set_selectable(GTK_LABEL(lbl_gps_lat), TRUE); gtk_misc_set_alignment(GTK_MISC(lbl_gps_lat), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), lbl_gps_lon = gtk_label_new(""), 2, 3, 1, 2, GTK_FILL, 0, 4, 0); gtk_label_set_selectable(GTK_LABEL(lbl_gps_lon), TRUE); gtk_misc_set_alignment(GTK_MISC(lbl_gps_lon), 1.f, 0.5f); /* Center. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("View Center")), 0, 1, 2, 3, GTK_FILL, 0, 4, 0); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), lbl_center_lat = gtk_label_new(""), 1, 2, 2, 3, GTK_FILL, 0, 4, 0); gtk_label_set_selectable(GTK_LABEL(lbl_center_lat), TRUE); gtk_misc_set_alignment(GTK_MISC(lbl_center_lat), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), lbl_center_lon = gtk_label_new(""), 2, 3, 2, 3, GTK_FILL, 0, 4, 0); gtk_label_set_selectable(GTK_LABEL(lbl_center_lon), TRUE); gtk_misc_set_alignment(GTK_MISC(lbl_center_lon), 1.f, 0.5f); /* default values for Top Left and Bottom Right are defined by the * rectangle of the current and the previous Center */ /* Top Left. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Top-Left")), 0, 1, 3, 4, GTK_FILL, 0, 4, 0); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), dlarea_info.txt_topleft_lat = gtk_entry_new(), 1, 2, 3, 4, GTK_EXPAND | GTK_FILL, 0, 4, 0); gtk_entry_set_alignment(GTK_ENTRY(dlarea_info.txt_topleft_lat), 1.f); g_object_set(G_OBJECT(dlarea_info.txt_topleft_lat), "input-mode", HILDON_INPUT_MODE_HINT_NUMERIC | HILDON_INPUT_MODE_HINT_NUMERICSPECIAL, NULL); gtk_table_attach(GTK_TABLE(table), dlarea_info.txt_topleft_lon = gtk_entry_new(), 2, 3, 3, 4, GTK_EXPAND | GTK_FILL, 0, 4, 0); gtk_entry_set_alignment(GTK_ENTRY(dlarea_info.txt_topleft_lon), 1.f); g_object_set(G_OBJECT(dlarea_info.txt_topleft_lon), "input-mode", HILDON_INPUT_MODE_HINT_NUMERIC | HILDON_INPUT_MODE_HINT_NUMERICSPECIAL, NULL); /* Bottom Right. */ gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Bottom-Right")), 0, 1, 4, 5, GTK_FILL, 0, 4, 0); gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f); gtk_table_attach(GTK_TABLE(table), dlarea_info.txt_botright_lat = gtk_entry_new(), 1, 2, 4, 5, GTK_EXPAND | GTK_FILL, 0, 4, 0); gtk_entry_set_alignment(GTK_ENTRY(dlarea_info.txt_botright_lat), 1.f); g_object_set(G_OBJECT(dlarea_info.txt_botright_lat), "input-mode", HILDON_INPUT_MODE_HINT_NUMERIC | HILDON_INPUT_MODE_HINT_NUMERICSPECIAL, NULL); gtk_table_attach(GTK_TABLE(table), dlarea_info.txt_botright_lon = gtk_entry_new(), 2, 3, 4, 5, GTK_EXPAND | GTK_FILL, 0, 4, 0); gtk_entry_set_alignment(GTK_ENTRY(dlarea_info.txt_botright_lon), 1.f); g_object_set(G_OBJECT(dlarea_info.txt_botright_lon), "input-mode", HILDON_INPUT_MODE_HINT_NUMERIC | HILDON_INPUT_MODE_HINT_NUMERICSPECIAL, NULL); gtk_notebook_append_page(GTK_NOTEBOOK(dlarea_info.notebook), table = gtk_table_new(5, 5, FALSE), label = gtk_label_new(_("Zoom"))); gtk_table_attach(GTK_TABLE(table), label = gtk_label_new( _("Zoom Levels to Download: (0 -> most detail)")), 0, 5, 0, 1, GTK_FILL, 0, 4, 0); gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f); for(i = 0; i < MAX_ZOOM; i++) { sprintf(buffer, "%d", i); gtk_table_attach(GTK_TABLE(table), dlarea_info.chk_zoom_levels[i] = gtk_check_button_new_with_label(buffer), i % 5, i % 5 + 1, i / 5 + 1, i / 5 + 2, GTK_EXPAND | GTK_FILL, 0, 4, 0); } /* Initialize fields. Do no use g_ascii_formatd; these strings will be * output (and parsed) as locale-dependent. */ sprintf(buffer, "%.06f", _pos_lat); gtk_label_set_text(GTK_LABEL(lbl_gps_lat), buffer); sprintf(buffer, "%.06f", _pos_lon); gtk_label_set_text(GTK_LABEL(lbl_gps_lon), buffer); unit2latlon(_center.unitx, _center.unity, lat, lon); sprintf(buffer, "%.06f", lat); gtk_label_set_text(GTK_LABEL(lbl_center_lat), buffer); sprintf(buffer, "%.06f", lon); gtk_label_set_text(GTK_LABEL(lbl_center_lon), buffer); /* Initialize to the bounds of the screen. */ unit2latlon(x2unit(0), y2unit(0), lat, lon); sprintf(buffer, "%.06f", lat); gtk_entry_set_text(GTK_ENTRY(dlarea_info.txt_topleft_lat), buffer); sprintf(buffer, "%.06f", lon); gtk_entry_set_text(GTK_ENTRY(dlarea_info.txt_topleft_lon), buffer); unit2latlon(x2unit(_screen_width_pixels), y2unit(_screen_height_pixels), lat, lon); sprintf(buffer, "%.06f", lat); gtk_entry_set_text(GTK_ENTRY(dlarea_info.txt_botright_lat), buffer); sprintf(buffer, "%.06f", lon); gtk_entry_set_text(GTK_ENTRY(dlarea_info.txt_botright_lon), buffer); for(i = 0; i < MAX_ZOOM; i++) gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(dlarea_info.chk_zoom_levels[i]), FALSE); gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(dlarea_info.chk_zoom_levels[_zoom]), TRUE); gtk_widget_show_all(dialog); while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) { const gchar *text; gchar *error_check; gfloat start_lat, start_lon, end_lat, end_lon; guint start_unitx, start_unity, end_unitx, end_unity; guint num_maps = 0; GtkWidget *confirm; text = gtk_entry_get_text(GTK_ENTRY(dlarea_info.txt_topleft_lat)); start_lat = strtof(text, &error_check); if(text == error_check) { popup_error(dialog, _("Invalid Top-Left Latitude")); continue; } text = gtk_entry_get_text(GTK_ENTRY(dlarea_info.txt_topleft_lon)); start_lon = strtof(text, &error_check); if(text == error_check) { popup_error(dialog, _("Invalid Top-Left Longitude")); continue; } text = gtk_entry_get_text(GTK_ENTRY(dlarea_info.txt_botright_lat)); end_lat = strtof(text, &error_check); if(text == error_check) { popup_error(dialog, _("Invalid Bottom-Right Latitude")); continue; } text = gtk_entry_get_text(GTK_ENTRY(dlarea_info.txt_botright_lon)); end_lon = strtof(text, &error_check); if(text == error_check) { popup_error(dialog,_("Invalid Bottom-Right Longitude")); continue; } latlon2unit(start_lat, start_lon, start_unitx, start_unity); latlon2unit(end_lat, end_lon, end_unitx, end_unity); /* Swap if they specified flipped lats or lons. */ if(start_unitx > end_unitx) { guint swap = start_unitx; start_unitx = end_unitx; end_unitx = swap; } if(start_unity > end_unity) { guint swap = start_unity; start_unity = end_unity; end_unity = swap; } /* First, get the number of maps to download. */ for(i = 0; i < MAX_ZOOM; i++) { if(gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(dlarea_info.chk_zoom_levels[i]))) { guint start_tilex, start_tiley, end_tilex, end_tiley; start_tilex = unit2ztile(start_unitx, i); start_tiley = unit2ztile(start_unity, i); end_tilex = unit2ztile(end_unitx, i); end_tiley = unit2ztile(end_unity, i); num_maps += (end_tilex - start_tilex + 1) * (end_tiley - start_tiley + 1); } } text = gtk_entry_get_text(GTK_ENTRY(dlarea_info.txt_topleft_lat)); sprintf(buffer, "%s %d %s\n(%s %.2f MB)\n", _("Confirm download of"), num_maps, _("maps"), _("up to about"), num_maps * (strstr(_map_uri_format, "%s") ? 18e-3 : 6e-3)); text = gtk_entry_get_text(GTK_ENTRY(dlarea_info.txt_topleft_lat)); confirm = hildon_note_new_confirmation(GTK_WINDOW(dialog), buffer); text = gtk_entry_get_text(GTK_ENTRY(dlarea_info.txt_topleft_lat)); if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm))) { for(i = 0; i < MAX_ZOOM; i++) { if(gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(dlarea_info.chk_zoom_levels[i]))) { guint start_tilex, start_tiley, end_tilex, end_tiley; guint tilex, tiley; start_tilex = unit2ztile(start_unitx, i); start_tiley = unit2ztile(start_unity, i); end_tilex = unit2ztile(end_unitx, i); end_tiley = unit2ztile(end_unity, i); for(tiley = start_tiley; tiley <= end_tiley; tiley++) for(tilex = start_tilex; tilex <= end_tilex; tilex++) map_initiate_download(tilex, tiley, i, INITIAL_DOWNLOAD_RETRIES); } } gtk_widget_destroy(confirm); break; } gtk_widget_destroy(confirm); } gtk_widget_hide(dialog); /* Destroying causes a crash (!?!?!??!) */ vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_fullscreen(GtkAction *action) { printf("%s()\n", __PRETTY_FUNCTION__); if((_fullscreen = gtk_check_menu_item_get_active( GTK_CHECK_MENU_ITEM(_menu_fullscreen_item)))) gtk_window_fullscreen(GTK_WINDOW(_window)); else gtk_window_unfullscreen(GTK_WINDOW(_window)); gtk_idle_add((GSourceFunc)window_present, NULL); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_enable_gps(GtkAction *action) { printf("%s()\n", __PRETTY_FUNCTION__); if((_enable_gps = gtk_check_menu_item_get_active( GTK_CHECK_MENU_ITEM(_menu_enable_gps_item)))) { if(_rcvr_mac) { set_conn_state(RCVR_DOWN); rcvr_connect_now(); } else { popup_error(_window, _("Cannot enable GPS until a GPS Receiver MAC " "is set in the Settings dialog box.")); gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(_menu_enable_gps_item), FALSE); } } else { if(_conn_state > RCVR_OFF) set_conn_state(RCVR_OFF); rcvr_disconnect(); track_add(0, FALSE); } map_move_mark(); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_auto_download(GtkAction *action) { printf("%s()\n", __PRETTY_FUNCTION__); if((_auto_download = gtk_check_menu_item_get_active( GTK_CHECK_MENU_ITEM(_menu_auto_download_item)))) map_force_redraw(); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_settings(GtkAction *action) { printf("%s()\n", __PRETTY_FUNCTION__); if(settings_dialog()) { /* Settings have changed - reconnect to receiver. */ if(_enable_gps) { set_conn_state(RCVR_DOWN); rcvr_disconnect(); rcvr_connect_now(); } } MACRO_RECALC_FOCUS_BASE(); MACRO_RECALC_FOCUS_SIZE(); map_force_redraw(); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean menu_cb_help(GtkAction *action) { printf("%s()\n", __PRETTY_FUNCTION__); ossohelp_show(_osso, HELP_ID_INTRO, OSSO_HELP_SHOW_DIALOG); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean map_download_idle_refresh(ProgressUpdateInfo *pui) { vprintf("%s(%p)\n", __PRETTY_FUNCTION__, pui); /* Remove pui from hash now in case we need to retry. */ g_hash_table_remove(_downloads_hash, pui); /* Test if download succeeded. */ if(gnome_vfs_uri_exists((GnomeVFSURI*)pui->dest_list->data)) { gint zoom_diff = pui->zoom - _zoom; /* Only refresh at same or "lower" (more detailed) zoom level. */ if(zoom_diff >= 0) { /* If zoom has changed since we first put in the request for this * tile, then we may have to update more than one tile. */ guint tilex, tiley, tilex_end, tiley_end; for(tilex = pui->tilex << zoom_diff, tilex_end = tilex + (1 << zoom_diff); tilex < tilex_end; tilex++) { for(tiley = pui->tiley<retries_left--) map_initiate_download(pui->tilex, pui->tiley, pui->zoom, pui->retries_left); progress_update_info_free(pui); if(++_curr_download == _num_downloads) { gtk_widget_destroy(_download_banner); _download_banner = NULL; _num_downloads = _curr_download = 0; } else hildon_banner_set_fraction(HILDON_BANNER(_download_banner), _curr_download / (double)_num_downloads); vprintf("%s(): return\n", __PRETTY_FUNCTION__); return FALSE; } static gint map_download_cb_async(GnomeVFSAsyncHandle *handle, GnomeVFSXferProgressInfo *info, ProgressUpdateInfo*pui) { vprintf("%s(%p, %d, %d, %d, %s)\n", __PRETTY_FUNCTION__, pui->handle, info->status, info->vfs_status, info->phase, info->target_name ? info->target_name + 7 : info->target_name); if(info->phase == GNOME_VFS_XFER_PHASE_COMPLETED) g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)map_download_idle_refresh, pui, NULL); else if(info->status != GNOME_VFS_XFER_PROGRESS_STATUS_OK) { if(info->vfs_status == GNOME_VFS_ERROR_NOT_FOUND) { /* Directory doesn't exist yet - create it, then we'll retry */ gchar buffer[1024]; sprintf(buffer, "%s/%u", _map_dir_name, pui->zoom); gnome_vfs_make_directory(buffer, 0775); sprintf(buffer, "%s/%u/%u", _map_dir_name, pui->zoom, pui->tilex); gnome_vfs_make_directory(buffer, 0775); } vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__); return FALSE; } vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean cmenu_cb_loc_show_latlon(GtkAction *action) { gchar buffer[80]; guint unitx, unity; gfloat lat, lon; printf("%s()\n", __PRETTY_FUNCTION__); unitx = x2unit(_cmenu_position_x); unity = y2unit(_cmenu_position_y); unit2latlon(unitx, unity, lat, lon); sprintf(buffer, "%s: %.06f\n" "%s: %.06f", _("Latitude"), lat, _("Longitude"), lon); hildon_banner_show_information(_window, NULL, buffer); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean cmenu_cb_loc_clip_latlon(GtkAction *action) { gchar buffer[80]; guint unitx, unity; gfloat lat, lon; printf("%s()\n", __PRETTY_FUNCTION__); unitx = x2unit(_cmenu_position_x); unity = y2unit(_cmenu_position_y); unit2latlon(unitx, unity, lat, lon); sprintf(buffer, "%.06f, %.06f", lat, lon); gtk_clipboard_set_text( gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), buffer, -1); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean cmenu_cb_loc_route_to(GtkAction *action) { gchar buffer[80]; gchar strlat[32]; gchar strlon[32]; guint unitx, unity; gfloat lat, lon; printf("%s()\n", __PRETTY_FUNCTION__); unitx = x2unit(_cmenu_position_x); unity = y2unit(_cmenu_position_y); unit2latlon(unitx, unity, lat, lon); g_ascii_formatd(strlat, 32, "%.06f", lat); g_ascii_formatd(strlon, 32, "%.06f", lon); sprintf(buffer, "%s, %s", strlat, strlon); route_download(NULL, buffer, TRUE); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean cmenu_cb_loc_distance_to(GtkAction *action) { gchar buffer[80]; guint unitx, unity; gfloat lat, lon; printf("%s()\n", __PRETTY_FUNCTION__); unitx = x2unit(_cmenu_position_x); unity = y2unit(_cmenu_position_y); unit2latlon(unitx, unity, lat, lon); sprintf(buffer, "%s: %.02f %s", _("Distance to Location"), calculate_distance(_pos_lat, _pos_lon, lat, lon), _units); hildon_banner_show_information(_window, NULL, buffer); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static WayPoint * find_nearest_visible_waypoint(guint unitx, guint unity) { WayPoint *wcurr; WayPoint *wnear; guint nearest_squared; Point pos = { unitx, unity }; wcurr = wnear = _visible_way_first; if(wcurr && wcurr != _visible_way_last) { nearest_squared = DISTANCE_SQUARED(pos, *(wcurr->point)); while(wcurr++ != _visible_way_last) { guint test_squared = DISTANCE_SQUARED(pos, *(wcurr->point)); if(test_squared < nearest_squared) { wnear = wcurr; nearest_squared = test_squared; } } } return wnear; } static gboolean cmenu_cb_way_show_latlon(GtkAction *action) { gchar buffer[80]; gfloat lat, lon; WayPoint *way; printf("%s()\n", __PRETTY_FUNCTION__); way = find_nearest_visible_waypoint( x2unit(_cmenu_position_x), y2unit(_cmenu_position_y)); if(way) { unit2latlon(way->point->unitx, way->point->unity, lat, lon); sprintf(buffer, "%s: %.06f\n" "%s: %.06f", _("Latitude"), lat, _("Longitude"), lon); hildon_banner_show_information(_window, NULL, buffer); } else hildon_banner_show_information(_window, NULL, _("No waypoints are visible.")); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean cmenu_cb_way_show_desc(GtkAction *action) { WayPoint *way; printf("%s()\n", __PRETTY_FUNCTION__); way = find_nearest_visible_waypoint( x2unit(_cmenu_position_x), y2unit(_cmenu_position_y)); if(way) hildon_banner_show_information(_window, NULL, way->desc); else hildon_banner_show_information(_window, NULL, _("No waypoints are visible.")); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean cmenu_cb_way_clip_latlon(GtkAction *action) { gchar buffer[80]; gfloat lat, lon; WayPoint *way; printf("%s()\n", __PRETTY_FUNCTION__); way = find_nearest_visible_waypoint( x2unit(_cmenu_position_x), y2unit(_cmenu_position_y)); if(way) { unit2latlon(way->point->unitx, way->point->unity, lat, lon); sprintf(buffer, "%s: %.06f\n" "%s: %.06f", _("Latitude"), lat, _("Longitude"), lon); gtk_clipboard_set_text( gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), buffer, -1); } else hildon_banner_show_information(_window, NULL, _("No waypoints are visible.")); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean cmenu_cb_way_clip_desc(GtkAction *action) { WayPoint *way; printf("%s()\n", __PRETTY_FUNCTION__); way = find_nearest_visible_waypoint( x2unit(_cmenu_position_x), y2unit(_cmenu_position_y)); if(way) gtk_clipboard_set_text( gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), way->desc, -1); else hildon_banner_show_information(_window, NULL, _("No waypoints are visible.")); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean cmenu_cb_way_route_to(GtkAction *action) { WayPoint *way; printf("%s()\n", __PRETTY_FUNCTION__); way = find_nearest_visible_waypoint( x2unit(_cmenu_position_x), y2unit(_cmenu_position_y)); if(way) { gchar buffer[80]; gchar strlat[32]; gchar strlon[32]; gfloat lat, lon; printf("%s()\n", __PRETTY_FUNCTION__); unit2latlon(way->point->unitx, way->point->unity, lat, lon); g_ascii_formatd(strlat, 32, "%.06f", lat); g_ascii_formatd(strlon, 32, "%.06f", lon); sprintf(buffer, "%s, %s", strlat, strlon); route_download(NULL, buffer, TRUE); } else hildon_banner_show_information(_window, NULL, _("No waypoints are visible.")); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean cmenu_cb_way_distance_to(GtkAction *action) { WayPoint *way; printf("%s()\n", __PRETTY_FUNCTION__); way = find_nearest_visible_waypoint( x2unit(_cmenu_position_x), y2unit(_cmenu_position_y)); if(way) { gchar buffer[80]; gfloat lat, lon; printf("%s()\n", __PRETTY_FUNCTION__); unit2latlon(way->point->unitx, way->point->unity, lat, lon); sprintf(buffer, "%s: %.02f %s", _("Distance to Waypoint"), calculate_distance(_pos_lat, _pos_lon, lat, lon), _units); hildon_banner_show_information(_window, NULL, buffer); } else hildon_banner_show_information(_window, NULL, _("No waypoints are visible.")); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } static gboolean cmenu_cb_way_delete(GtkAction *action) { WayPoint *way; printf("%s()\n", __PRETTY_FUNCTION__); way = find_nearest_visible_waypoint( x2unit(_cmenu_position_x), y2unit(_cmenu_position_y)); if(way) { gchar buffer[1024]; GtkWidget *confirm; sprintf(buffer, "%s:\n%s\n", _("Confirm delete of waypoint"), way->desc); confirm = hildon_note_new_confirmation(GTK_WINDOW(_window), buffer); if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm))) { while(way++ != _route.wtail) way[-1] = *way; _route.wtail--; map_force_redraw(); } gtk_widget_destroy(confirm); } else hildon_banner_show_information(_window, NULL, _("No waypoints are visible.")); vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__); return TRUE; } /**************************************************************************** * ABOVE: CALLBACKS ********************************************************* ****************************************************************************/