/*
* Copyright (C) 2006, 2007 John Costigan.
*
* POI and GPS-Info code originally written by Cezary Jackiewicz.
*
* Default map data provided by http://www.openstreetmap.org/
*
* This file is part of Maemo Mapper.
*
* Maemo Mapper 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 3 of the License, or
* (at your option) any later version.
*
* Maemo Mapper 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 Maemo Mapper. If not, see .
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#ifndef LEGACY
# include
# include
# include
# include
#else
# include
# include
# include
# include
# include
#endif
#include "types.h"
#include "data.h"
#include "defines.h"
#include "display.h"
#include "gdk-pixbuf-rotate.h"
#include "gpx.h"
#include "main.h"
#include "path.h"
#include "poi.h"
#include "util.h"
typedef struct _RouteDownloadInfo RouteDownloadInfo;
struct _RouteDownloadInfo {
GtkWidget *rad_use_gps;
GtkWidget *rad_use_route;
GtkWidget *rad_use_text;
GtkWidget *chk_avoid_highways;
GtkWidget *chk_auto;
GtkWidget *txt_from;
GtkWidget *txt_to;
};
/* _near_point is the route point to which we are closest. */
static Point *_near_point = NULL;
/* _next_way is what we currently interpret to be the next waypoint. */
static WayPoint *_next_way;
static gint64 _next_way_dist_squared = INT64_MAX;
/* _next_wpt is the route point immediately following _next_way. */
static Point *_next_wpt = NULL;
static gint64 _next_wpt_dist_squared = INT64_MAX;
static gfloat _initial_distance_from_waypoint = -1.f;
static WayPoint *_initial_distance_waypoint = NULL;
static sqlite3 *_path_db = NULL;
static sqlite3_stmt *_track_stmt_select = NULL;
static sqlite3_stmt *_track_stmt_delete_path = NULL;
static sqlite3_stmt *_track_stmt_delete_way = NULL;
static sqlite3_stmt *_track_stmt_insert_path = NULL;
static sqlite3_stmt *_track_stmt_insert_way = NULL;
static sqlite3_stmt *_route_stmt_select = NULL;
static sqlite3_stmt *_route_stmt_delete_path = NULL;
static sqlite3_stmt *_route_stmt_delete_way = NULL;
static sqlite3_stmt *_route_stmt_insert_path = NULL;
static sqlite3_stmt *_route_stmt_insert_way = NULL;
static sqlite3_stmt *_path_stmt_trans_begin = NULL;
static sqlite3_stmt *_path_stmt_trans_commit = NULL;
static sqlite3_stmt *_path_stmt_trans_rollback = NULL;
static gchar *_last_spoken_phrase;
void
path_resize(Path *path, gint size)
{
printf("%s()\n", __PRETTY_FUNCTION__);
if(path->head + size != path->cap)
{
Point *old_head = path->head;
WayPoint *curr;
path->head = g_renew(Point, old_head, size);
path->cap = path->head + size;
if(path->head != old_head)
{
path->tail = path->head + (path->tail - old_head);
/* Adjust all of the waypoints. */
for(curr = path->whead - 1; curr++ != path->wtail; )
curr->point = path->head + (curr->point - old_head);
}
}
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
void
path_wresize(Path *path, gint wsize)
{
printf("%s()\n", __PRETTY_FUNCTION__);
if(path->whead + wsize != path->wcap)
{
WayPoint *old_whead = path->whead;
path->whead = g_renew(WayPoint, old_whead, wsize);
path->wtail = path->whead + (path->wtail - old_whead);
path->wcap = path->whead + wsize;
}
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
static void
read_path_from_db(Path *path, sqlite3_stmt *select_stmt)
{
printf("%s()\n", __PRETTY_FUNCTION__);
MACRO_PATH_INIT(*path);
while(SQLITE_ROW == sqlite3_step(select_stmt))
{
const gchar *desc;
MACRO_PATH_INCREMENT_TAIL(*path);
path->tail->unitx = sqlite3_column_int(select_stmt, 0);
path->tail->unity = sqlite3_column_int(select_stmt, 1);
path->tail->time = sqlite3_column_int(select_stmt, 2);
path->tail->altitude = sqlite3_column_int(select_stmt, 3);
desc = sqlite3_column_text(select_stmt, 4);
if(desc)
{
MACRO_PATH_INCREMENT_WTAIL(*path);
path->wtail->point = path->tail;
path->wtail->desc = g_strdup(desc);
}
}
sqlite3_reset(select_stmt);
/* If the last point isn't null, then add another null point. */
if(path->tail->unity)
{
MACRO_PATH_INCREMENT_TAIL(*path);
*path->tail = _point_null;
}
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
/* Returns the new next_update_index. */
static gint
write_path_to_db(Path *path,
sqlite3_stmt *delete_path_stmt,
sqlite3_stmt *delete_way_stmt,
sqlite3_stmt *insert_path_stmt,
sqlite3_stmt *insert_way_stmt,
gint index_last_saved)
{
Point *curr;
WayPoint *wcurr;
gint num;
gboolean success = TRUE;
printf("%s(%d)\n", __PRETTY_FUNCTION__, index_last_saved);
/* Start transaction. */
sqlite3_step(_path_stmt_trans_begin);
sqlite3_reset(_path_stmt_trans_begin);
if(index_last_saved == 0)
{
/* Replace the whole thing, so delete the table first. */
if(SQLITE_DONE != sqlite3_step(delete_way_stmt)
|| SQLITE_DONE != sqlite3_step(delete_path_stmt))
{
gchar buffer[BUFFER_SIZE];
snprintf(buffer, sizeof(buffer), "%s\n%s",
_("Failed to write to path database. "
"Tracks and routes may not be saved."),
sqlite3_errmsg(_path_db));
popup_error(_window, buffer);
success = FALSE;
}
sqlite3_reset(delete_way_stmt);
sqlite3_reset(delete_path_stmt);
}
for(num = index_last_saved, curr = path->head + num, wcurr = path->whead;
success && ++curr <= path->tail; ++num)
{
/* If this is the last point, and it is null, don't write it. */
if(curr == path->tail && !curr->unity)
break;
/* Insert the path point. */
if(SQLITE_OK != sqlite3_bind_int(insert_path_stmt, 1, curr->unitx)
|| SQLITE_OK != sqlite3_bind_int(insert_path_stmt, 2, curr->unity)
|| SQLITE_OK != sqlite3_bind_int(insert_path_stmt, 3, curr->time)
|| SQLITE_OK != sqlite3_bind_int(insert_path_stmt, 4, curr->altitude)
|| SQLITE_DONE != sqlite3_step(insert_path_stmt))
{
gchar buffer[BUFFER_SIZE];
snprintf(buffer, sizeof(buffer), "%s\n%s",
_("Failed to write to path database. "
"Tracks and routes may not be saved."),
sqlite3_errmsg(_path_db));
popup_error(_window, buffer);
success = FALSE;
}
sqlite3_reset(insert_path_stmt);
/* Now, check if curr is a waypoint. */
if(success && wcurr <= path->wtail && wcurr->point == curr)
{
gint num = sqlite3_last_insert_rowid(_path_db);
if(SQLITE_OK != sqlite3_bind_int(insert_way_stmt, 1, num)
|| SQLITE_OK != sqlite3_bind_text(insert_way_stmt, 2, wcurr->desc,
-1, SQLITE_STATIC)
|| SQLITE_DONE != sqlite3_step(insert_way_stmt))
{
gchar buffer[BUFFER_SIZE];
snprintf(buffer, sizeof(buffer), "%s\n%s",
_("Failed to write to path database. "
"Tracks and routes may not be saved."),
sqlite3_errmsg(_path_db));
popup_error(_window, buffer);
success = FALSE;
}
sqlite3_reset(insert_way_stmt);
wcurr++;
}
}
if(success)
{
sqlite3_step(_path_stmt_trans_commit);
sqlite3_reset(_path_stmt_trans_commit);
vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
return num;
}
else
{
sqlite3_step(_path_stmt_trans_rollback);
sqlite3_reset(_path_stmt_trans_rollback);
vprintf("%s(): return 0\n", __PRETTY_FUNCTION__);
return index_last_saved;
}
}
void
path_save_route_to_db()
{
if(_path_db)
{
write_path_to_db(&_route,
_route_stmt_delete_path,
_route_stmt_delete_way,
_route_stmt_insert_path,
_route_stmt_insert_way,
0);
}
}
static void
path_save_track_to_db()
{
if(_path_db)
{
write_path_to_db(&_track,
_track_stmt_delete_path,
_track_stmt_delete_way,
_track_stmt_insert_path,
_track_stmt_insert_way,
0);
}
}
static void
path_update_track_in_db()
{
if(_path_db)
{
_track_index_last_saved = write_path_to_db(&_track,
_track_stmt_delete_path,
_track_stmt_delete_way,
_track_stmt_insert_path,
_track_stmt_insert_way,
_track_index_last_saved);
}
}
/**
* 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;
gint64 near_dist_squared;
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_squared with the new distance from
* _near_point. */
near = _near_point;
near_dist_squared = DISTANCE_SQUARED(_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)
{
gint64 dist_squared = DISTANCE_SQUARED(_pos, *curr);
if(dist_squared <= near_dist_squared)
{
near = curr;
near_dist_squared = dist_squared;
}
else if(quick)
break;
}
}
/* Update _near_point. */
_near_point = near;
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_SQUARED(_pos, *near) > _next_way_dist_squared
&& DISTANCE_SQUARED(_pos, *_next_wpt)
< _next_wpt_dist_squared))))
{
wnext = wcurr + 1;
}
else
break;
}
if(wnext == _route.wtail && (wnext->point < near
|| (wnext->point == near && quick
&& (_next_wpt
&& (DISTANCE_SQUARED(_pos, *near) > _next_way_dist_squared
&&DISTANCE_SQUARED(_pos, *_next_wpt)
< _next_wpt_dist_squared)))))
{
_next_way = NULL;
_next_wpt = NULL;
_next_way_dist_squared = INT64_MAX;
_next_wpt_dist_squared = INT64_MAX;
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_squared = DISTANCE_SQUARED(_pos, *wnext->point);
if(_next_wpt)
_next_wpt_dist_squared = DISTANCE_SQUARED(_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.
*/
void
route_find_nearest_point()
{
printf("%s()\n", __PRETTY_FUNCTION__);
/* Initialize _near_point to first non-zero point. */
_near_point = _route.head;
while(!_near_point->unity && _near_point != _route.tail)
_near_point++;
/* Initialize _next_way. */
if(_route.wtail < _route.whead
|| (_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_squared = INT64_MAX;
/* Initialize _next_wpt. */
_next_wpt = NULL;
_next_wpt_dist_squared = INT64_MAX;
route_update_nears(FALSE);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
/**
* Show the distance from the current GPS location to the given point,
* following the route. If point is NULL, then the distance is shown to the
* next waypoint.
*/
gboolean
route_show_distance_to(Point *point)
{
gchar buffer[80];
gdouble lat1, lon1, lat2, lon2;
gdouble sum = 0.0;
printf("%s()\n", __PRETTY_FUNCTION__);
/* If point is NULL, use the next waypoint. */
if(point == NULL && _next_way)
point = _next_way->point;
/* If point is still NULL, return an error. */
if(point == NULL)
{
printf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
return FALSE;
}
unit2latlon(_pos.unitx, _pos.unity, lat1, lon1);
if(point > _near_point)
{
Point *curr;
/* Skip _near_point in case we have already passed it. */
for(curr = _near_point + 1; curr <= point; ++curr)
{
if(curr->unity)
{
unit2latlon(curr->unitx, curr->unity, lat2, lon2);
sum += calculate_distance(lat1, lon1, lat2, lon2);
lat1 = lat2;
lon1 = lon2;
}
}
}
else if(point < _near_point)
{
Point *curr;
/* Skip _near_point in case we have already passed it. */
for(curr = _near_point - 1; curr >= point; --curr)
{
if(curr->unity)
{
unit2latlon(curr->unitx, curr->unity, lat2, lon2);
sum += calculate_distance(lat1, lon1, lat2, lon2);
lat1 = lat2;
lon1 = lon2;
}
}
}
else
{
/* Waypoint _is_ the nearest point. */
unit2latlon(_near_point->unitx, _near_point->unity, lat2, lon2);
sum += calculate_distance(lat1, lon1, lat2, lon2);
}
snprintf(buffer, sizeof(buffer), "%s: %.02f %s", _("Distance"),
sum * UNITS_CONVERT[_units], UNITS_ENUM_TEXT[_units]);
MACRO_BANNER_SHOW_INFO(_window, buffer);
return TRUE;
printf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
}
void
route_show_distance_to_next()
{
printf("%s()\n", __PRETTY_FUNCTION__);
if(!route_show_distance_to(NULL))
{
MACRO_BANNER_SHOW_INFO(_window, _("There is no next waypoint."));
}
printf("%s(): return\n", __PRETTY_FUNCTION__);
}
void
route_show_distance_to_last()
{
printf("%s()\n", __PRETTY_FUNCTION__);
if(_route.head != _route.tail)
{
/* Find last non-zero point. */
Point *p;
for(p = _route.tail; !p->unity; p--) { }
route_show_distance_to(p);
}
else
{
MACRO_BANNER_SHOW_INFO(_window, _("The current route is empty."));
}
printf("%s(): return\n", __PRETTY_FUNCTION__);
}
static void
track_show_distance_from(Point *point)
{
gchar buffer[80];
gdouble lat1, lon1, lat2, lon2;
gdouble sum = 0.0;
Point *curr;
unit2latlon(_pos.unitx, _pos.unity, lat1, lon1);
/* Skip _track.tail because that should be _pos. */
for(curr = _track.tail; curr > point; --curr)
{
if(curr->unity)
{
unit2latlon(curr->unitx, curr->unity, lat2, lon2);
sum += calculate_distance(lat1, lon1, lat2, lon2);
lat1 = lat2;
lon1 = lon2;
}
}
snprintf(buffer, sizeof(buffer), "%s: %.02f %s", _("Distance"),
sum * UNITS_CONVERT[_units], UNITS_ENUM_TEXT[_units]);
MACRO_BANNER_SHOW_INFO(_window, buffer);
}
void
track_show_distance_from_last()
{
printf("%s()\n", __PRETTY_FUNCTION__);
/* Find last zero point. */
if(_track.head != _track.tail)
{
Point *point;
/* Find last zero point. */
for(point = _track.tail; point->unity; point--) { }
track_show_distance_from(point);
}
else
{
MACRO_BANNER_SHOW_INFO(_window, _("The current track is empty."));
}
printf("%s(): return\n", __PRETTY_FUNCTION__);
}
void
track_show_distance_from_first()
{
printf("%s()\n", __PRETTY_FUNCTION__);
/* Find last zero point. */
if(_track.head != _track.tail)
track_show_distance_from(_track.head);
else
{
MACRO_BANNER_SHOW_INFO(_window, _("The current track is empty."));
}
printf("%s(): return\n", __PRETTY_FUNCTION__);
}
static gboolean
route_download_and_setup(GtkWidget *parent, const gchar *source_url,
const gchar *from, const gchar *to,
gboolean avoid_highways, gint replace_policy)
{
gchar *from_escaped;
gchar *to_escaped;
gchar *buffer;
gchar *bytes;
gint size;
GnomeVFSResult vfs_result;
printf("%s(%s, %s)\n", __PRETTY_FUNCTION__, from, to);
from_escaped = gnome_vfs_escape_string(from);
to_escaped = gnome_vfs_escape_string(to);
buffer = g_strdup_printf(source_url, from_escaped, to_escaped);
g_free(from_escaped);
g_free(to_escaped);
if(avoid_highways)
{
gchar *old = buffer;
buffer = g_strconcat(old, "&avoid_highways=on", NULL);
g_free(old);
}
/* Attempt to download the route from the server. */
vfs_result = gnome_vfs_read_entire_file(buffer, &size, &bytes);
g_free (buffer);
if(vfs_result != GNOME_VFS_OK)
{
g_free(bytes);
popup_error(parent, gnome_vfs_result_to_string(vfs_result));
vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
return FALSE;
}
if(strncmp(bytes, "unity
|| ((xdiff = _pos.unitx - _track.tail->unitx), /* comma op */
(ydiff = _pos.unity - _track.tail->unity), /* comma op */
/* Check if xdiff or ydiff are huge. */
((abs(xdiff) >> 12) || (abs(ydiff) >> 12)
/* Okay, let's see if we've moved enough to justify adding
* to the track. It depends on our error. I'd like to
* make the threshold roughly linear with respect to the
* P/HDOP (completely arbitrary, I know), but I also
* want to keep the threshold at a minimum of 2
* zoom-level-4 pixel, and I want dop's of less than 2 to
* also have a 1-pixel threshold. I also throw in some
* PDOP into the mix, just for fun. */
|| ((dopcand = 8 * (_gps.pdop - 6 +(_gps.hdop*_gps.hdop))),
((xdiff * xdiff) + (ydiff * ydiff)
>= (MAX(2, dopcand) << 8))))))
{
/* We moved enough to actually register a move. */
ret = TRUE;
/* Update the nearest-waypoint data. */
if(_route.head != _route.tail
&& (newly_fixed ? (route_find_nearest_point(), TRUE)
: route_update_nears(TRUE)))
{
/* Nearest waypoint has changed - re-render paths. */
map_render_paths();
MACRO_QUEUE_DRAW_AREA();
}
if(_enable_tracking)
{
if(_show_paths & 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[COLORABLE_TRACK],
_gc[COLORABLE_TRACK_BREAK],
_track.tail->unitx, _track.tail->unity,
_pos.unitx, _pos.unity);
if(_track.tail->unity)
{
unit2screen(_track.tail->unitx, _track.tail->unity,
tx1, ty1);
unit2screen(_pos.unitx, _pos.unity, tx2, ty2);
gtk_widget_queue_draw_area(_map_widget,
MIN(tx1, tx2) - _draw_width,
MIN(ty1, ty2) - _draw_width,
abs(tx1 - tx2) + (2 * _draw_width),
abs(ty1 - ty2) + (2 * _draw_width));
}
}
MACRO_PATH_INCREMENT_TAIL(_track);
*_track.tail = _pos;
}
/* Calculate distance to route. (point to line) */
if(_near_point)
{
gint route_x1, route_x2, route_y1, route_y2;
gint64 route_dist_squared_1 = INT64_MAX;
gint64 route_dist_squared_2 = INT64_MAX;
gfloat slope;
route_x1 = _near_point->unitx;
route_y1 = _near_point->unity;
/* Try previous point first. */
if(_near_point != _route.head && _near_point[-1].unity)
{
route_x2 = _near_point[-1].unitx;
route_y2 = _near_point[-1].unity;
slope = (gfloat)(route_y2 - route_y1)
/ (gfloat)(route_x2 - route_x1);
if(route_x1 == route_x2)
{
/* Vertical line special case. */
route_dist_squared_1 = (_pos.unitx - route_x1)
* (_pos.unitx - route_x1);
}
else
{
route_dist_squared_1 = abs((slope * _pos.unitx)
- _pos.unity + (route_y1 - (slope * route_x1)));
route_dist_squared_1 =
route_dist_squared_1 * route_dist_squared_1
/ ((slope * slope) + 1);
}
}
if(_near_point != _route.tail && _near_point[1].unity)
{
route_x2 = _near_point[1].unitx;
route_y2 = _near_point[1].unity;
slope = (gfloat)(route_y2 - route_y1)
/ (gfloat)(route_x2 - route_x1);
if(route_x1 == route_x2)
{
/* Vertical line special case. */
route_dist_squared_2 = (_pos.unitx - route_x1)
* (_pos.unitx - route_x1);
}
else
{
route_dist_squared_2 = abs((slope * _pos.unitx)
- _pos.unity + (route_y1 - (slope * route_x1)));
route_dist_squared_2 =
route_dist_squared_2 * route_dist_squared_2
/ ((slope * slope) + 1);
}
}
/* Check if our distance from the route is large. */
if(MIN(route_dist_squared_1, route_dist_squared_2)
> (2000 * 2000))
{
/* Prevent announcments from occurring. */
announce_thres_unsquared = INT_MAX;
if(_autoroute_data.enabled && !_autoroute_data.in_progress)
{
MACRO_BANNER_SHOW_INFO(_window,
_("Recalculating directions..."));
_autoroute_data.in_progress = TRUE;
show_directions = FALSE;
g_idle_add((GSourceFunc)auto_route_dl_idle, NULL);
}
else
{
/* Reset the route to try and find the nearest point.*/
path_reset_route();
}
}
}
/* Keep the display on. */
moving = TRUE;
}
if(_initial_distance_waypoint
&& (_next_way != _initial_distance_waypoint
|| _next_way_dist_squared > (_initial_distance_from_waypoint
* _initial_distance_from_waypoint)))
{
/* We've moved on to the next waypoint, or we're really far from
* the current waypoint. */
if(_waypoint_banner)
{
gtk_widget_destroy(_waypoint_banner);
_waypoint_banner = NULL;
}
_initial_distance_from_waypoint = -1.f;
_initial_distance_waypoint = NULL;
}
/* Check if we should announce upcoming waypoints. */
if(_enable_announce
&& (_initial_distance_waypoint || _next_way_dist_squared
< (announce_thres_unsquared * announce_thres_unsquared)))
{
if(show_directions)
{
if(!_initial_distance_waypoint)
{
/* First time we're close enough to this waypoint. */
if(_enable_voice
/* And that we haven't already announced it. */
&& strcmp(_next_way->desc, _last_spoken_phrase))
{
g_free(_last_spoken_phrase);
_last_spoken_phrase = g_strdup(_next_way->desc);
if(!fork())
{
/* We are the fork child. Synthesize the voice. */
hildon_play_system_sound(
"/usr/share/sounds/ui-information_note.wav");
sleep(1);
printf("%s %s\n", _voice_synth_path,
_last_spoken_phrase);
execl(_voice_synth_path, basename(_voice_synth_path),
"-t", _last_spoken_phrase, (char *)NULL);
/* No good? Try to launch it with /bin/sh */
execl("/bin/sh", "sh", _voice_synth_path,
"-t", _last_spoken_phrase, (char *)NULL);
/* Still no good? Oh well... */
exit(0);
}
}
_initial_distance_from_waypoint
= sqrtf(_next_way_dist_squared);
_initial_distance_waypoint = _next_way;
if(_next_wpt && _next_wpt->unity != 0)
{
/* Create a banner for us the show progress. */
_waypoint_banner = hildon_banner_show_progress(
_window, NULL, _next_way->desc);
}
else
{
/* This is the last point in a segment, i.e.
* "Arrive at ..." - just announce. */
MACRO_BANNER_SHOW_INFO(_window, _next_way->desc);
}
}
else if(_waypoint_banner);
{
/* We're already close to this waypoint. */
gdouble fraction = 1.f - (sqrtf(_next_way_dist_squared)
/ _initial_distance_from_waypoint);
BOUND(fraction, 0.f, 1.f);
hildon_banner_set_fraction(
HILDON_BANNER(_waypoint_banner), fraction);
}
}
approaching_waypoint = TRUE;
}
else if(_next_way_dist_squared > 2 * (_initial_distance_from_waypoint
* _initial_distance_from_waypoint))
{
/* We're too far away now - destroy the banner. */
}
UNBLANK_SCREEN(moving, approaching_waypoint);
/* Maybe update the track database. */
{
static time_t last_track_db_update = 0;
if(!time || (time - last_track_db_update > 60
&& _track.tail - _track.head + 1 > _track_index_last_saved))
{
path_update_track_in_db();
last_track_db_update = time;
}
}
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
return ret;
}
void
track_clear()
{
GtkWidget *confirm;
confirm = hildon_note_new_confirmation(GTK_WINDOW(_window),
_("Really clear the track?"));
if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm))) {
MACRO_PATH_FREE(_track);
MACRO_PATH_INIT(_track);
path_save_track_to_db();
map_force_redraw();
}
gtk_widget_destroy(confirm);
}
void
track_insert_break(gboolean temporary)
{
printf("%s()\n", __PRETTY_FUNCTION__);
if(_track.tail->unity)
{
gint x1, y1;
unit2screen(_track.tail->unitx, _track.tail->unity, x1, y1);
MACRO_PATH_INCREMENT_TAIL(_track);
*_track.tail = _point_null;
/* To mark a "waypoint" in a track, we'll add a (0, 0) point and then
* another instance of the most recent track point. */
if(temporary)
{
MACRO_PATH_INCREMENT_TAIL(_track);
*_track.tail = _track.tail[-2];
}
/** Instead of calling map_render_paths(), we'll just add the waypoint
* ourselves. */
/* Make sure this circle will be visible. */
if((x1 < _view_width_pixels) && (y1 < _view_height_pixels))
gdk_draw_arc(_map_pixmap, _gc[COLORABLE_TRACK_BREAK],
FALSE, /* FALSE: not filled. */
x1 - _draw_width,
y1 - _draw_width,
2 * _draw_width,
2 * _draw_width,
0, /* start at 0 degrees. */
360 * 64);
}
/* Update the track database. */
path_update_track_in_db();
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
/**
* Cancel the current auto-route.
*/
void
cancel_autoroute()
{
printf("%s()\n", __PRETTY_FUNCTION__);
if(_autoroute_data.enabled)
{
_autoroute_data.enabled = FALSE;
g_free(_autoroute_data.source_url);
_autoroute_data.source_url = NULL;
g_free(_autoroute_data.dest);
_autoroute_data.dest = NULL;
_autoroute_data.in_progress = FALSE;
}
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
WayPoint *
find_nearest_waypoint(gint unitx, gint unity)
{
WayPoint *wcurr;
WayPoint *wnear;
gint64 nearest_squared;
Point pos = { unitx, unity, 0, INT_MIN };
printf("%s()\n", __PRETTY_FUNCTION__);
wcurr = wnear = _route.whead;
if(wcurr && wcurr <= _route.wtail)
{
nearest_squared = DISTANCE_SQUARED(pos, *(wcurr->point));
wnear = _route.whead;
while(++wcurr <= _route.wtail)
{
gint64 test_squared = DISTANCE_SQUARED(pos, *(wcurr->point));
if(test_squared < nearest_squared)
{
wnear = wcurr;
nearest_squared = test_squared;
}
}
/* Only use the waypoint if it is within a 6*_draw_width square drawn
* around the position. This is consistent with select_poi(). */
if(abs(unitx - wnear->point->unitx) < pixel2unit(3 * _draw_width)
&& abs(unity - wnear->point->unity) < pixel2unit(3 * _draw_width))
return wnear;
}
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
return NULL;
}
static gboolean
origin_type_selected(GtkWidget *toggle, RouteDownloadInfo *oti)
{
printf("%s()\n", __PRETTY_FUNCTION__);
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
{
gtk_widget_set_sensitive(oti->txt_from, toggle == oti->rad_use_text);
gtk_widget_set_sensitive(oti->chk_auto, toggle == oti->rad_use_gps);
}
vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
return TRUE;
}
static gboolean
route_download_swap(GtkWidget *button, RouteDownloadInfo *oti)
{
gchar *old_origin;
printf("%s()\n", __PRETTY_FUNCTION__);
/* Save the old origin. */
old_origin = g_strdup(gtk_entry_get_text(GTK_ENTRY(oti->txt_from)));
/* Set the origin text field equal to the current destination. */
gtk_entry_set_text(GTK_ENTRY(oti->txt_from),
gtk_entry_get_text(GTK_ENTRY(oti->txt_to)));
/* Set the contents of the destination text field. */
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti->rad_use_gps)))
{
/* "Use GPS Location" is enabled - set Destination to GPS Location */
gchar buffer[80];
gchar strlat[32];
gchar strlon[32];
g_ascii_formatd(strlat, 32, "%.06f", _gps.lat);
g_ascii_formatd(strlon, 32, "%.06f", _gps.lon);
snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
gtk_entry_set_text(GTK_ENTRY(oti->txt_to), buffer);
}
else if(gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(oti->rad_use_route)))
{
/* "Use End of Route" is enabled - set Destination to start of route */
gchar buffer[80];
gchar strlat[32];
gchar strlon[32];
Point *p;
gdouble lat, lon;
/* Use first non-zero route point. */
for(p = _route.head; !p->unity; p++) { }
unit2latlon(p->unitx, p->unity, lat, lon);
g_ascii_formatd(strlat, 32, "%.06f", lat);
g_ascii_formatd(strlon, 32, "%.06f", lon);
snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
gtk_entry_set_text(GTK_ENTRY(oti->txt_to), buffer);
}
else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti->rad_use_text)))
{
/* "Origin" is enabled - just use the text. */
gtk_entry_set_text(GTK_ENTRY(oti->txt_to), old_origin);
}
g_free(old_origin);
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oti->rad_use_text), TRUE);
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.
*/
gboolean
route_download(gchar *to)
{
static GtkWidget *dialog = NULL;
static GtkWidget *table = NULL;
static GtkWidget *label = NULL;
static GtkWidget *txt_source_url = NULL;
static GtkWidget *btn_swap = NULL;
static RouteDownloadInfo oti;
printf("%s()\n", __PRETTY_FUNCTION__);
conic_recommend_connected();
if(dialog == NULL)
{
GtkEntryCompletion *from_comp;
GtkEntryCompletion *to_comp;
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);
/* Enable the help button. */
#ifndef LEGACY
hildon_help_dialog_help_enable(
#else
ossohelp_dialog_help_enable(
#endif
GTK_DIALOG(dialog), HELP_ID_DOWNROUTE, _osso);
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
table = gtk_table_new(2, 5, FALSE), TRUE, TRUE, 0);
/* Source URL. */
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Source URL")),
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),
txt_source_url = gtk_entry_new(),
1, 5, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4);
gtk_entry_set_width_chars(GTK_ENTRY(txt_source_url), 25);
/* Use GPS Location. */
gtk_table_attach(GTK_TABLE(table),
oti.rad_use_gps = gtk_radio_button_new_with_label(NULL,
_("Use GPS Location")),
0, 2, 1, 2, GTK_FILL, 0, 2, 4);
/* Use End of Route. */
gtk_table_attach(GTK_TABLE(table),
oti.rad_use_route = gtk_radio_button_new_with_label_from_widget(
GTK_RADIO_BUTTON(oti.rad_use_gps), _("Use End of Route")),
0, 2, 2, 3, GTK_FILL, 0, 2, 4);
gtk_table_attach(GTK_TABLE(table),
gtk_vseparator_new(),
2, 3, 1, 3, GTK_FILL, GTK_FILL, 2,4);
/* Auto. */
gtk_table_attach(GTK_TABLE(table),
oti.chk_auto = gtk_check_button_new_with_label(
_("Auto-Update")),
3, 5, 1, 2, GTK_FILL, 0, 2, 4);
/* Avoid Highways. */
gtk_table_attach(GTK_TABLE(table),
oti.chk_avoid_highways = gtk_check_button_new_with_label(
_("Avoid Highways")),
3, 5, 2, 3, GTK_FILL, 0, 2, 4);
/* Origin. */
gtk_table_attach(GTK_TABLE(table),
oti.rad_use_text = gtk_radio_button_new_with_label_from_widget(
GTK_RADIO_BUTTON(oti.rad_use_gps), _("Origin")),
0, 1, 3, 4, GTK_FILL, 0, 2, 4);
gtk_table_attach(GTK_TABLE(table),
oti.txt_from = gtk_entry_new(),
1, 4, 3, 4, GTK_EXPAND | GTK_FILL, 0, 2, 4);
gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_from), 25);
#ifdef MAEMO_CHANGES
#ifndef LEGACY
g_object_set(G_OBJECT(oti.txt_from), "hildon-input-mode",
HILDON_GTK_INPUT_MODE_FULL, NULL);
#else
g_object_set(G_OBJECT(oti.txt_from), HILDON_AUTOCAP, FALSE, NULL);
#endif
#endif
/* Destination. */
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Destination")),
0, 1, 4, 5, GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
oti.txt_to = gtk_entry_new(),
1, 4, 4, 5, GTK_EXPAND | GTK_FILL, 0, 2, 4);
gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_to), 25);
#ifdef MAEMO_CHANGES
#ifndef LEGACY
g_object_set(G_OBJECT(oti.txt_to), "hildon-input-mode",
HILDON_GTK_INPUT_MODE_FULL, NULL);
#else
g_object_set(G_OBJECT(oti.txt_to), HILDON_AUTOCAP, FALSE, NULL);
#endif
#endif
/* Swap button. */
gtk_table_attach(GTK_TABLE(table),
btn_swap = gtk_button_new_with_label("Swap"),
4, 5, 3, 5, GTK_FILL, GTK_FILL, 2, 4);
/* Set up auto-completion. */
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);
gtk_entry_set_completion(GTK_ENTRY(oti.txt_from), from_comp);
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);
gtk_entry_set_completion(GTK_ENTRY(oti.txt_to), to_comp);
g_signal_connect(G_OBJECT(oti.rad_use_gps), "toggled",
G_CALLBACK(origin_type_selected), &oti);
g_signal_connect(G_OBJECT(oti.rad_use_route), "toggled",
G_CALLBACK(origin_type_selected), &oti);
g_signal_connect(G_OBJECT(oti.rad_use_text), "toggled",
G_CALLBACK(origin_type_selected), &oti);
g_signal_connect(G_OBJECT(btn_swap), "clicked",
G_CALLBACK(route_download_swap), &oti);
gtk_widget_set_sensitive(oti.chk_auto, FALSE);
}
/* Initialize fields. */
gtk_entry_set_text(GTK_ENTRY(txt_source_url), _route_dl_url);
if(to)
gtk_entry_set_text(GTK_ENTRY(oti.txt_to), to);
/* Use "End of Route" by default if they have a route. */
if(_route.head != _route.tail)
{
/* There is no route, so make it the default. */
gtk_widget_set_sensitive(oti.rad_use_route, TRUE);
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(oti.rad_use_route), TRUE);
gtk_widget_grab_focus(oti.rad_use_route);
}
/* Else use "GPS Location" if they have GPS enabled. */
else
{
/* There is no route, so desensitize "Use End of Route." */
gtk_widget_set_sensitive(oti.rad_use_route, FALSE);
if(_enable_gps)
{
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(oti.rad_use_gps), TRUE);
gtk_widget_grab_focus(oti.rad_use_gps);
}
/* Else use text. */
else
{
gtk_toggle_button_set_active(
GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
gtk_widget_grab_focus(oti.txt_from);
}
}
gtk_widget_show_all(dialog);
while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
{
gchar buffer[BUFFER_SIZE];
const gchar *source_url, *from, *to;
gboolean avoid_highways;
source_url = gtk_entry_get_text(GTK_ENTRY(txt_source_url));
if(!strlen(source_url))
{
popup_error(dialog, _("Please specify a source URL."));
continue;
}
else
{
g_free(_route_dl_url);
_route_dl_url = g_strdup(source_url);
}
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti.rad_use_gps)))
{
gchar strlat[32];
gchar strlon[32];
g_ascii_formatd(strlat, 32, "%.06f", _gps.lat);
g_ascii_formatd(strlon, 32, "%.06f", _gps.lon);
snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
from = buffer;
}
else if(gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(oti.rad_use_route)))
{
gchar strlat[32];
gchar strlon[32];
Point *p;
gdouble lat, lon;
/* Use last non-zero route point. */
for(p = _route.tail; !p->unity; p--) { }
unit2latlon(p->unitx, p->unity, lat, lon);
g_ascii_formatd(strlat, 32, "%.06f", lat);
g_ascii_formatd(strlon, 32, "%.06f", lon);
snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
from = buffer;
}
else
{
from = gtk_entry_get_text(GTK_ENTRY(oti.txt_from));
}
if(!strlen(from))
{
popup_error(dialog, _("Please specify a start location."));
continue;
}
to = gtk_entry_get_text(GTK_ENTRY(oti.txt_to));
if(!strlen(to))
{
popup_error(dialog, _("Please specify an end location."));
continue;
}
avoid_highways = gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(oti.chk_avoid_highways));
if(route_download_and_setup(dialog, source_url, from, to,
gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(oti.chk_avoid_highways)),
(gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(oti.rad_use_gps)) ? 0 : 1)))
{
GtkTreeIter iter;
/* Cancel any autoroute that might be occurring. */
cancel_autoroute();
if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti.chk_auto)))
{
_autoroute_data.source_url = g_strdup(source_url);
_autoroute_data.dest = g_strdup(to);
_autoroute_data.avoid_highways = avoid_highways;
_autoroute_data.enabled = TRUE;
}
/* Save Origin in Route Locations list if not from GPS. */
if(gtk_toggle_button_get_active(
GTK_TOGGLE_BUTTON(oti.rad_use_text))
&& !g_slist_find_custom(_loc_list, from,
(GCompareFunc)strcmp))
{
_loc_list = g_slist_prepend(_loc_list, g_strdup(from));
gtk_list_store_insert_with_values(_loc_model, &iter,
INT_MAX, 0, from, -1);
}
/* Save Destination in Route Locations list. */
if(!g_slist_find_custom(_loc_list, to,
(GCompareFunc)strcmp))
{
_loc_list = g_slist_prepend(_loc_list, g_strdup(to));
gtk_list_store_insert_with_values(_loc_model, &iter,
INT_MAX, 0, to, -1);
}
/* Success! Get out of the while loop. */
break;
}
/* else let them try again. */
}
gtk_widget_hide(dialog); /* Destroying causes a crash (!?!?!??!) */
vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
return TRUE;
}
void
route_add_way_dialog(gint unitx, gint unity)
{
gdouble lat, lon;
gchar tmp1[LL_FMT_LEN], tmp2[LL_FMT_LEN], *p_latlon;
static GtkWidget *dialog = NULL;
static GtkWidget *table = NULL;
static GtkWidget *label = NULL;
static GtkWidget *label_lat_lon = NULL;
static GtkWidget *txt_scroll = NULL;
static GtkWidget *txt_desc = NULL;
static int last_deg_format = 0;
printf("%s()\n", __PRETTY_FUNCTION__);
unit2latlon(unitx, unity, lat, lon);
gint fallback_deg_format = _degformat;
if(!coord_system_check_lat_lon (lat, lon, &fallback_deg_format))
{
last_deg_format = _degformat;
_degformat = fallback_deg_format;
if(dialog != NULL) gtk_widget_destroy(dialog);
dialog = NULL;
}
else if(_degformat != last_deg_format)
{
last_deg_format = _degformat;
if(dialog != NULL) gtk_widget_destroy(dialog);
dialog = NULL;
}
if(dialog == NULL)
{
dialog = gtk_dialog_new_with_buttons(_("Add Waypoint"),
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, 2, FALSE), TRUE, TRUE, 0);
if(DEG_FORMAT_ENUM_TEXT[_degformat].field_2_in_use)
p_latlon = g_strdup_printf("%s, %s",
DEG_FORMAT_ENUM_TEXT[_degformat].short_field_1,
DEG_FORMAT_ENUM_TEXT[_degformat].short_field_2);
else
p_latlon = g_strdup_printf("%s", DEG_FORMAT_ENUM_TEXT[_degformat].short_field_1);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(p_latlon),
0, 1, 0, 1, GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
g_free(p_latlon);
gtk_table_attach(GTK_TABLE(table),
label_lat_lon = gtk_label_new(""),
1, 2, 0, 1, GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(label_lat_lon), 0.0f, 0.5f);
gtk_table_attach(GTK_TABLE(table),
label = gtk_label_new(_("Description")),
0, 1, 1, 2, GTK_FILL, 0, 2, 4);
gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
txt_scroll = gtk_scrolled_window_new(NULL, NULL);
gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scroll),
GTK_SHADOW_IN);
gtk_table_attach(GTK_TABLE(table),
txt_scroll,
1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scroll),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
txt_desc = gtk_text_view_new ();
gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(txt_desc), GTK_WRAP_WORD);
gtk_container_add(GTK_CONTAINER(txt_scroll), txt_desc);
gtk_widget_set_size_request(GTK_WIDGET(txt_scroll), 400, 60);
}
format_lat_lon(lat, lon, tmp1, tmp2);
if(DEG_FORMAT_ENUM_TEXT[_degformat].field_2_in_use)
p_latlon = g_strdup_printf("%s, %s", tmp1, tmp2);
else
p_latlon = g_strdup_printf("%s", tmp1);
gtk_label_set_text(GTK_LABEL(label_lat_lon), p_latlon);
g_free(p_latlon);
gtk_text_buffer_set_text(
gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt_desc)), "", 0);
gtk_widget_show_all(dialog);
while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
{
GtkTextBuffer *tbuf;
GtkTextIter ti1, ti2;
gchar *desc;
tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt_desc));
gtk_text_buffer_get_iter_at_offset(tbuf, &ti1, 0);
gtk_text_buffer_get_end_iter(tbuf, &ti2);
desc = gtk_text_buffer_get_text(tbuf, &ti1, &ti2, TRUE);
if(*desc)
{
/* There's a description. Add a waypoint. */
MACRO_PATH_INCREMENT_TAIL(_route);
_route.tail->unitx = unitx;
_route.tail->unity = unity;
_route.tail->time = 0;
_route.tail->altitude = 0;
MACRO_PATH_INCREMENT_WTAIL(_route);
_route.wtail->point = _route.tail;
_route.wtail->desc
= gtk_text_buffer_get_text(tbuf, &ti1, &ti2, TRUE);
}
else
{
GtkWidget *confirm;
g_free(desc);
confirm = hildon_note_new_confirmation(GTK_WINDOW(dialog),
_("Creating a \"waypoint\" with no description actually "
"adds a break point. Is that what you want?"));
if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
{
/* There's no description. Add a break by adding a (0, 0)
* point (if necessary), and then the ordinary route point. */
if(_route.tail->unity)
{
MACRO_PATH_INCREMENT_TAIL(_route);
*_route.tail = _point_null;
}
MACRO_PATH_INCREMENT_TAIL(_route);
_route.tail->unitx = unitx;
_route.tail->unity = unity;
_route.tail->time = 0;
_route.tail->altitude = 0;
gtk_widget_destroy(confirm);
}
else
{
gtk_widget_destroy(confirm);
continue;
}
}
route_find_nearest_point();
map_render_paths();
MACRO_QUEUE_DRAW_AREA();
break;
}
gtk_widget_hide(dialog);
_degformat = last_deg_format;
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
WayPoint*
path_get_next_way()
{
return _next_way;
}
void
path_init()
{
gchar *settings_dir;
printf("%s()\n", __PRETTY_FUNCTION__);
/* Initialize settings_dir. */
settings_dir = gnome_vfs_expand_initial_tilde(CONFIG_DIR_NAME);
g_mkdir_with_parents(settings_dir, 0700);
/* Open path database. */
{
gchar *path_db_file;
path_db_file = gnome_vfs_uri_make_full_from_relative(
settings_dir, CONFIG_PATH_DB_FILE);
if(!path_db_file || SQLITE_OK != sqlite3_open(path_db_file, &_path_db)
/* Open worked. Now create tables, failing if they already exist. */
|| (sqlite3_exec(_path_db,
"create table route_path ("
"num integer primary key, "
"unitx integer, "
"unity integer, "
"time integer, "
"altitude integer)"
";"
"create table route_way ("
"route_point primary key, "
"description text)"
";"
"create table track_path ("
"num integer primary key, "
"unitx integer, "
"unity integer, "
"time integer, "
"altitude integer)"
";"
"create table track_way ("
"track_point primary key, "
"description text)",
NULL, NULL, NULL), FALSE) /* !! Comma operator !! */
/* Create prepared statements - failure here is bad! */
|| SQLITE_OK != sqlite3_prepare(_path_db,
"select unitx, unity, time, altitude, description "
"from route_path left join route_way on "
"route_path.num = route_way.route_point "
"order by route_path.num",
-1, &_route_stmt_select, NULL)
|| SQLITE_OK != sqlite3_prepare(_path_db,
"select unitx, unity, time, altitude, description "
"from track_path left join track_way on "
"track_path.num = track_way.track_point "
"order by track_path.num",
-1, &_track_stmt_select, NULL)
|| SQLITE_OK != sqlite3_prepare(_path_db,
"delete from route_path",
-1, &_route_stmt_delete_path, NULL)
|| SQLITE_OK != sqlite3_prepare(_path_db,
"delete from route_way",
-1, &_route_stmt_delete_way, NULL)
|| SQLITE_OK != sqlite3_prepare(_path_db,
"insert into route_path "
"(num, unitx, unity, time, altitude) "
"values (NULL, ?, ?, ?, ?)",
-1, &_route_stmt_insert_path, NULL)
|| SQLITE_OK != sqlite3_prepare(_path_db,
"insert into route_way (route_point, description) "
"values (?, ?)",
-1, &_route_stmt_insert_way, NULL)
|| SQLITE_OK != sqlite3_prepare(_path_db,
"delete from track_path",
-1, &_track_stmt_delete_path, NULL)
|| SQLITE_OK != sqlite3_prepare(_path_db,
"delete from track_way",
-1, &_track_stmt_delete_way, NULL)
|| SQLITE_OK != sqlite3_prepare(_path_db,
"insert into track_path "
"(num, unitx, unity, time, altitude) "
"values (NULL, ?, ?, ?, ?)",
-1, &_track_stmt_insert_path, NULL)
|| SQLITE_OK != sqlite3_prepare(_path_db,
"insert into track_way (track_point, description) "
"values (?, ?)",
-1, &_track_stmt_insert_way, NULL)
|| SQLITE_OK != sqlite3_prepare(_path_db, "begin transaction",
-1, &_path_stmt_trans_begin, NULL)
|| SQLITE_OK != sqlite3_prepare(_path_db, "commit transaction",
-1, &_path_stmt_trans_commit, NULL)
|| SQLITE_OK != sqlite3_prepare(_path_db, "rollback transaction",
-1, &_path_stmt_trans_rollback, NULL))
{
gchar buffer[BUFFER_SIZE];
snprintf(buffer, sizeof(buffer), "%s\n%s",
_("Failed to open path database. "
"Tracks and routes will not be saved."),
sqlite3_errmsg(_path_db));
sqlite3_close(_path_db);
_path_db = NULL;
popup_error(_window, buffer);
}
else
{
read_path_from_db(&_route, _route_stmt_select);
read_path_from_db(&_track, _track_stmt_select);
_track_index_last_saved = _track.tail - _track.head - 1;
}
g_free(path_db_file);
}
g_free(settings_dir);
_last_spoken_phrase = g_strdup("");
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}
void
path_destroy()
{
printf("%s()\n", __PRETTY_FUNCTION__);
/* Save paths. */
if(_track.tail->unity)
track_insert_break(FALSE);
path_update_track_in_db();
path_save_route_to_db();
if(_path_db)
{
sqlite3_close(_path_db);
_path_db = NULL;
}
MACRO_PATH_FREE(_track);
MACRO_PATH_FREE(_route);
vprintf("%s(): return\n", __PRETTY_FUNCTION__);
}