13 #include <glib/gstdio.h>
16 #include <libgnomevfs/gnome-vfs.h>
17 #include <curl/multi.h>
22 #include "ui-common.h"
24 #include "mapper-types.h"
27 #include "map-download.h"
31 #define DISTANCE_SQUARED(a, b) \
32 ((guint64)((((gint64)(b).unitx)-(a).unitx)*(((gint64)(b).unitx)-(a).unitx))\
33 + (guint64)((((gint64)(b).unity)-(a).unity)*(((gint64)(b).unity)-(a).unity)))
35 static gboolean show_directions=TRUE;
37 static WayPoint *announced_waypoint=NULL;
39 typedef struct _OriginToggleInfo OriginToggleInfo;
40 struct _OriginToggleInfo {
41 GtkWidget *rad_use_gps;
42 GtkWidget *rad_use_route;
43 GtkWidget *rad_use_text;
51 route_clear(Path *route)
56 g_return_val_if_fail(route, FALSE);
58 confirm=hildon_note_new_confirmation(GTK_WINDOW(_window), _("Really clear the route?"));
60 if (GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm))) {
61 route_cancel_autoroute(route, FALSE);
62 announced_waypoint=NULL;
64 path_find_nearest_point(route);
68 gtk_widget_destroy(confirm);
73 * Check if we should announce upcoming waypoints.
76 route_check_waypoint_announce(Path *route, GpsData *gps)
78 guint announce_thres_unsquared;
80 g_return_if_fail(route);
81 g_return_if_fail(gps);
83 if (!_announce_waypoints)
89 announce_thres_unsquared=(20+(guint)gps->speed)*_announce_notice_ratio*3;
91 if (route->next_way_dist_squared<(announce_thres_unsquared * announce_thres_unsquared) && route->next_way!=announced_waypoint) {
92 announce_waypoint(route->next_way->desc);
93 announced_waypoint=route->next_way;
99 * Check if we should re-calculate route
102 route_autoroute_check(Path *route)
104 g_return_if_fail(route);
106 if (_autoroute_data.enabled && !_autoroute_data.in_progress && route->near_point_dist_squared > 400) {
107 MACRO_BANNER_SHOW_INFO(_window, _("Recalculating directions..."));
108 _autoroute_data.in_progress = TRUE;
109 show_directions = FALSE;
110 g_idle_add((GSourceFunc)route_auto_route_dl_idle_cb, NULL);
115 route_open_file(Path *route)
120 g_return_val_if_fail(route, FALSE);
122 if (file_open_get_contents(&_route_dir_uri, &buffer, &size)) {
123 /* If auto is enabled, append the route, otherwise replace it. */
124 if (gpx_parse(route, buffer, size, _autoroute_data.enabled ? GPX_PATH_APPEND : GPX_PATH_NEW)) {
125 route_cancel_autoroute(route, FALSE);
127 MACRO_BANNER_SHOW_INFO(_window, _("Route Opened"));
128 /* Find the nearest route point, if we're connected. */
129 path_find_nearest_point(route);
130 route_set_destination_from_last(route, &_dest);
133 popup_error(_window, _("Error parsing GPX file."));
142 * Ask user to save route
145 route_save(Path *route)
147 GnomeVFSHandle *handle;
149 g_return_val_if_fail(route, FALSE);
151 if (route->head==route->tail) {
152 MACRO_BANNER_SHOW_INFO(_window, _("No route exist."));
156 if (file_save(&_route_dir_uri, &_route_dir_uri, &handle)) {
157 if (gpx_write(route, handle)) {
158 MACRO_BANNER_SHOW_INFO(_window, _("Route Saved"));
160 popup_error(_window, _("Error writing GPX file."));
162 gnome_vfs_close(handle);
172 route_origin_type_selected_cb(GtkWidget *toggle, OriginToggleInfo *oti)
174 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle))) {
175 if (toggle == oti->rad_use_gps) {
179 g_ascii_formatd(strlat, 32, "%.06f", _gps->data.lat);
180 g_ascii_formatd(strlon, 32, "%.06f", _gps->data.lon);
181 g_snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
182 gtk_entry_set_text(GTK_ENTRY(oti->txt_from), buffer);
183 } else if (toggle == oti->rad_use_route) {
190 p=path_find_last_point(oti->path);
192 unit2latlon(p->unitx, p->unity, &lat, &lon);
193 g_ascii_formatd(strlat, 32, "%.06f", lat);
194 g_ascii_formatd(strlon, 32, "%.06f", lon);
195 g_snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
196 gtk_entry_set_text(GTK_ENTRY(oti->txt_from), buffer);
199 gtk_widget_set_sensitive(oti->txt_from, toggle == oti->rad_use_text);
200 gtk_widget_set_sensitive(oti->chk_auto, toggle == oti->rad_use_gps);
206 * Cancel the current auto-route.
209 route_cancel_autoroute(Path *route, gboolean temporary)
211 if (_autoroute_data.enabled) {
213 _autoroute_data.enabled = FALSE;
215 g_free(_autoroute_data.dest);
216 _autoroute_data.dest = NULL;
218 g_free(_autoroute_data.src_str);
219 _autoroute_data.src_str = NULL;
222 if (_autoroute_data.curl_easy) {
224 curl_multi_remove_handle(_curl_multi, _autoroute_data.curl_easy);
225 curl_easy_cleanup(_autoroute_data.curl_easy);
226 _autoroute_data.curl_easy = NULL;
229 g_free(_autoroute_data.rdl_data.bytes);
230 _autoroute_data.rdl_data.bytes = NULL;
231 _autoroute_data.rdl_data.bytes_read = 0;
233 _autoroute_data.in_progress = FALSE;
238 * Read the data provided by the given handle as GPX data, updating the
239 * auto-route with that data.
242 route_dl_cb_read(void *ptr, size_t size, size_t nmemb, RouteDownloadData * rdl_data)
244 size_t old_size = rdl_data->bytes_read;
246 rdl_data->bytes_read += size * nmemb;
247 rdl_data->bytes = g_renew(gchar, rdl_data->bytes, rdl_data->bytes_read);
248 g_memmove(rdl_data->bytes + old_size, ptr, size * nmemb);
250 return (size * nmemb);
254 route_auto_route_dl_idle_cb()
256 gchar latstr[32], lonstr[32], *latlonstr;
258 g_ascii_dtostr(latstr, 32, _gps->data.lat);
259 g_ascii_dtostr(lonstr, 32, _gps->data.lon);
260 latlonstr = g_strdup_printf("%s,%s", latstr, lonstr);
261 _autoroute_data.src_str = g_strdup_printf(_route_dl_url, latlonstr, _autoroute_data.dest);
264 MACRO_CURL_EASY_INIT(_autoroute_data.curl_easy);
265 curl_easy_setopt(_autoroute_data.curl_easy, CURLOPT_URL, _autoroute_data.src_str);
266 curl_easy_setopt(_autoroute_data.curl_easy, CURLOPT_WRITEFUNCTION, route_dl_cb_read);
267 curl_easy_setopt(_autoroute_data.curl_easy, CURLOPT_WRITEDATA, &_autoroute_data.rdl_data);
268 curl_multi_add_handle(_curl_multi, _autoroute_data.curl_easy);
270 if (iap_is_connected() && !_curl_sid)
271 _curl_sid = g_timeout_add(100, (GSourceFunc)map_download_timeout, NULL);
273 _autoroute_data.in_progress = TRUE;
279 * Display a dialog box to the user asking them to download a route. The
280 * "From" and "To" textfields may be initialized using the first two
281 * parameters. The third parameter, if set to TRUE, will cause the "Use GPS
282 * Location" checkbox to be enabled, which automatically sets the "From" to the
283 * current GPS position (this overrides any value that may have been passed as
284 * the "To" initializer).
285 * None of the passed strings are freed - that is left to the caller, and it is
286 * safe to free either string as soon as this function returns.
289 route_download(Path *route, gchar *to)
294 GtkWidget *txt_source_url;
296 OriginToggleInfo oti;
297 GtkEntryCompletion *from_comp;
298 GtkEntryCompletion *to_comp;
303 dialog = gtk_dialog_new_with_buttons(_("Download Route"),
305 GTK_DIALOG_MODAL, GTK_STOCK_OK,
308 GTK_RESPONSE_REJECT, NULL);
310 help_dialog_help_enable(GTK_DIALOG(dialog), HELP_ID_DOWNROUTE);
311 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), table = gtk_table_new(2, 5, FALSE), TRUE, TRUE, 0);
313 from_comp = gtk_entry_completion_new();
314 gtk_entry_completion_set_model(from_comp, GTK_TREE_MODEL(_loc_model));
315 gtk_entry_completion_set_text_column(from_comp, 0);
316 to_comp = gtk_entry_completion_new();
317 gtk_entry_completion_set_model(to_comp, GTK_TREE_MODEL(_loc_model));
318 gtk_entry_completion_set_text_column(to_comp, 0);
321 gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Source URL")), 0, 1, 0, 1, GTK_FILL, 0, 2, 4);
322 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
323 gtk_table_attach(GTK_TABLE(table), txt_source_url = gtk_entry_new(), 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4);
324 gtk_entry_set_width_chars(GTK_ENTRY(txt_source_url), 25);
327 gtk_table_attach(GTK_TABLE(table), hbox = gtk_hbox_new(FALSE, 6), 0, 2, 1, 2, GTK_FILL, 0, 2, 4);
328 gtk_box_pack_start(GTK_BOX(hbox), oti.rad_use_gps = gtk_radio_button_new_with_label(NULL, _("Use GPS Location")), TRUE, TRUE, 0);
329 gtk_box_pack_start(GTK_BOX(hbox), oti.chk_auto = gtk_check_button_new_with_label(_("Auto-Update")), TRUE, TRUE, 0);
330 gtk_widget_set_sensitive(oti.chk_auto, FALSE);
332 /* Use End of Route. */
333 gtk_table_attach(GTK_TABLE(table), hbox = gtk_hbox_new(FALSE, 6), 0, 2, 2, 3, GTK_FILL, 0, 2, 4);
334 gtk_box_pack_start(GTK_BOX(hbox), oti.rad_use_route = gtk_radio_button_new_with_label_from_widget
335 (GTK_RADIO_BUTTON(oti.rad_use_gps), _("Use End of Route")), TRUE, TRUE, 0);
336 gtk_widget_set_sensitive(oti.rad_use_route, route->head != route->tail);
339 gtk_table_attach(GTK_TABLE(table), oti.rad_use_text = gtk_radio_button_new_with_label_from_widget
340 (GTK_RADIO_BUTTON(oti.rad_use_gps), _("Origin")), 0, 1, 3, 4, GTK_FILL, 0, 2, 4);
341 gtk_table_attach(GTK_TABLE(table), oti.txt_from = gtk_entry_new(), 1, 2, 3, 4, GTK_EXPAND | GTK_FILL, 0, 2, 4);
342 gtk_entry_set_completion(GTK_ENTRY(oti.txt_from), from_comp);
343 gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_from), 25);
346 gtk_table_attach(GTK_TABLE(table), label = gtk_label_new(_("Destination")), 0, 1, 4, 5, GTK_FILL, 0, 2, 4);
347 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
348 gtk_table_attach(GTK_TABLE(table), oti.txt_to = gtk_entry_new(), 1, 2, 4, 5, GTK_EXPAND | GTK_FILL, 0, 2, 4);
349 gtk_entry_set_completion(GTK_ENTRY(oti.txt_to), to_comp);
350 gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_to), 25);
352 g_signal_connect(G_OBJECT(oti.rad_use_gps), "toggled", G_CALLBACK(route_origin_type_selected_cb), &oti);
353 g_signal_connect(G_OBJECT(oti.rad_use_route), "toggled", G_CALLBACK(route_origin_type_selected_cb), &oti);
354 g_signal_connect(G_OBJECT(oti.rad_use_text), "toggled", G_CALLBACK(route_origin_type_selected_cb), &oti);
356 #if defined (WITH_HILDON) && defined (HILDON_AUTOCAP)
357 g_object_set(G_OBJECT(oti.txt_from), HILDON_AUTOCAP, FALSE, NULL);
358 g_object_set(G_OBJECT(oti.txt_to), HILDON_AUTOCAP, FALSE, NULL);
361 /* Initialize fields. */
362 gtk_entry_set_text(GTK_ENTRY(txt_source_url), _route_dl_url);
363 gtk_entry_set_text(GTK_ENTRY(oti.txt_to), (to ? to : ""));
365 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
367 if (route->head != route->tail) {
368 /* Use "End of Route" by default if they have a route. */
369 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oti.rad_use_route), TRUE);
370 gtk_widget_grab_focus(oti.rad_use_route);
371 } else if (_enable_gps) {
372 /* Else use "GPS Location" if they have GPS enabled. */
373 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oti.rad_use_gps), TRUE);
374 gtk_widget_grab_focus(oti.rad_use_gps);
377 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
378 gtk_widget_grab_focus(oti.txt_from);
381 gtk_widget_show_all(dialog);
383 while (GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) {
385 RouteDownloadData rdl_data = { 0, 0 };
386 gchar buffer[BUFFER_SIZE];
387 const gchar *source_url, *from, *to;
388 gchar *from_escaped, *to_escaped;
390 source_url = gtk_entry_get_text(GTK_ENTRY(txt_source_url));
391 if (!strlen(source_url)) {
392 popup_error(dialog, _("Please specify a source URL."));
395 g_free(_route_dl_url);
396 _route_dl_url = g_strdup(source_url);
399 from = gtk_entry_get_text(GTK_ENTRY(oti.txt_from));
401 popup_error(dialog, _("Please specify a start location."));
405 to = gtk_entry_get_text(GTK_ENTRY(oti.txt_to));
407 popup_error(dialog, _("Please specify an end location."));
411 from_escaped = gnome_vfs_escape_string(from);
412 to_escaped = gnome_vfs_escape_string(to);
413 g_snprintf(buffer, sizeof(buffer), source_url, from_escaped, to_escaped);
414 g_free(from_escaped);
419 /* Attempt to download the route from the server. */
420 MACRO_CURL_EASY_INIT(curl_easy);
421 curl_easy_setopt(curl_easy, CURLOPT_URL, buffer);
422 curl_easy_setopt(curl_easy, CURLOPT_WRITEFUNCTION, route_dl_cb_read);
423 curl_easy_setopt(curl_easy, CURLOPT_WRITEDATA, &rdl_data);
424 if (CURLE_OK != curl_easy_perform(curl_easy)) {
425 popup_error(dialog, _("Failed to connect to GPX Directions server"));
426 curl_easy_cleanup(curl_easy);
427 g_free(rdl_data.bytes);
428 /* Let them try again */
431 curl_easy_cleanup(curl_easy);
433 if (strncmp(rdl_data.bytes, "<?xml", strlen("<?xml"))) {
434 /* Not an XML document - must be bad locations. */
435 popup_error(dialog, _("Could not generate directions. Make sure your source and destination are valid."));
436 g_free(rdl_data.bytes);
437 /* Let them try again. */
439 /* Else, if GPS is enabled, replace the route, otherwise append it. */
440 else if (gpx_parse(route, rdl_data.bytes, rdl_data.bytes_read,
441 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti.rad_use_gps)) ? GPX_PATH_NEW : GPX_PATH_APPEND))) {
444 /* Find the nearest route point, if we're connected. */
445 path_find_nearest_point(route);
447 /* Cancel any autoroute that might be occurring. */
448 route_cancel_autoroute(route, FALSE);
450 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti.chk_auto))) {
451 /* Kick off a timeout to start the first update. */
452 _autoroute_data.dest = gnome_vfs_escape_string(to);
453 _autoroute_data.enabled = TRUE;
456 /* Save Origin in Route Locations list if not from GPS. */
457 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti.rad_use_gps)) && !g_slist_find_custom(_loc_list, from, (GCompareFunc) strcmp)) {
458 _loc_list = g_slist_prepend(_loc_list, g_strdup(from));
459 gtk_list_store_insert_with_values(_loc_model, &iter, INT_MAX, 0, from, -1);
462 /* Save Destination in Route Locations list. */
463 if (!g_slist_find_custom(_loc_list, to, (GCompareFunc) strcmp)) {
464 _loc_list = g_slist_prepend(_loc_list, g_strdup(to));
465 gtk_list_store_insert_with_values(_loc_model, &iter, INT_MAX, 0, to, -1);
468 MACRO_BANNER_SHOW_INFO(_window, _("Route Downloaded"));
469 g_free(rdl_data.bytes);
470 route_set_destination_from_last(route, &_dest);
472 /* Success! Get out of the while loop. */
476 popup_error(dialog, _("Error parsing route GPX data."));
477 g_free(rdl_data.bytes);
478 /* Let them try again. */
482 gtk_widget_hide(dialog); /* Destroying causes a crash (!?!?!??!) */
488 * Show the distance from the current GPS location to the given point,
489 * following the route. If point is NULL, then the distance is shown to the
493 route_show_distance_to(Path *route, Point *point)
499 g_return_val_if_fail(route, FALSE);
500 g_return_val_if_fail(point, FALSE);
502 unit2latlon(point->unitx, point->unity, &lat, &lon);
503 sum=path_get_distance_to(route, point, lat, lon);
504 g_snprintf(buffer, sizeof(buffer), "%s: %.02f %s", _("Distance"), sum * UNITS_CONVERT[_units], UNITS_TEXT[_units]);
505 MACRO_BANNER_SHOW_INFO(_window, buffer);
511 route_show_distance_to_next_waypoint(Path *route)
513 g_return_if_fail(route);
515 if (!route_show_distance_to(route, NULL))
516 MACRO_BANNER_SHOW_INFO(_window, _("There is no next waypoint."));
520 route_show_distance_to_last(Path *route)
522 g_return_if_fail(route);
524 if (route->head != route->tail) {
525 route_show_distance_to(route, path_find_last_point(route));
527 MACRO_BANNER_SHOW_INFO(_window, _("The current route is empty."));