]> git.itanic.dy.fi Git - maemo-mapper/blob - src/path.c
7f31690c752856d8574f8624c99284eb55d6139b
[maemo-mapper] / src / path.c
1 /*
2  * Copyright (C) 2006, 2007 John Costigan.
3  *
4  * POI and GPS-Info code originally written by Cezary Jackiewicz.
5  *
6  * Default map data provided by http://www.openstreetmap.org/
7  *
8  * This file is part of Maemo Mapper.
9  *
10  * Maemo Mapper is free software: you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation, either version 3 of the License, or
13  * (at your option) any later version.
14  *
15  * Maemo Mapper is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Maemo Mapper.  If not, see <http://www.gnu.org/licenses/>.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #    include "config.h"
26 #endif
27
28 #define _GNU_SOURCE
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <math.h>
33 #include <bt-dbus.h>
34 #include <sqlite3.h>
35
36 #ifndef LEGACY
37 #    include <hildon/hildon-help.h>
38 #    include <hildon/hildon-note.h>
39 #    include <hildon/hildon-banner.h>
40 #    include <hildon/hildon-sound.h>
41 #else
42 #    include <osso-helplib.h>
43 #    include <hildon-widgets/hildon-note.h>
44 #    include <hildon-widgets/hildon-banner.h>
45 #    include <hildon-widgets/hildon-system-sound.h>
46 #    include <hildon-widgets/hildon-input-mode-hint.h>
47 #endif
48
49 #include "types.h"
50 #include "data.h"
51 #include "defines.h"
52
53 #include "display.h"
54 #include "gdk-pixbuf-rotate.h"
55 #include "gpx.h"
56 #include "main.h"
57 #include "path.h"
58 #include "poi.h"
59 #include "util.h"
60
61 typedef struct _RouteDownloadInfo RouteDownloadInfo;
62 struct _RouteDownloadInfo {
63     GtkWidget *rad_use_gps;
64     GtkWidget *rad_use_route;
65     GtkWidget *rad_use_text;
66     GtkWidget *chk_avoid_highways;
67     GtkWidget *chk_auto;
68     GtkWidget *txt_from;
69     GtkWidget *txt_to;
70 };
71
72 /* _near_point is the route point to which we are closest. */
73 static Point *_near_point = NULL;
74
75 /* _next_way is what we currently interpret to be the next waypoint. */
76 static WayPoint *_next_way;
77 static gint64 _next_way_dist_squared = INT64_MAX;
78
79 /* _next_wpt is the route point immediately following _next_way. */
80 static Point *_next_wpt = NULL;
81 static gint64 _next_wpt_dist_squared = INT64_MAX;
82
83 static gfloat _initial_distance_from_waypoint = -1.f;
84 static WayPoint *_initial_distance_waypoint = NULL;
85
86 static sqlite3 *_path_db = NULL;
87 static sqlite3_stmt *_track_stmt_select = NULL;
88 static sqlite3_stmt *_track_stmt_delete_path = NULL;
89 static sqlite3_stmt *_track_stmt_delete_way = NULL;
90 static sqlite3_stmt *_track_stmt_insert_path = NULL;
91 static sqlite3_stmt *_track_stmt_insert_way = NULL;
92 static sqlite3_stmt *_route_stmt_select = NULL;
93 static sqlite3_stmt *_route_stmt_delete_path = NULL;
94 static sqlite3_stmt *_route_stmt_delete_way = NULL;
95 static sqlite3_stmt *_route_stmt_insert_path = NULL;
96 static sqlite3_stmt *_route_stmt_insert_way = NULL;
97 static sqlite3_stmt *_path_stmt_trans_begin = NULL;
98 static sqlite3_stmt *_path_stmt_trans_commit = NULL;
99 static sqlite3_stmt *_path_stmt_trans_rollback = NULL;
100
101 static gchar *_last_spoken_phrase;
102
103 void
104 path_resize(Path *path, gint size)
105 {
106     printf("%s()\n", __PRETTY_FUNCTION__);
107
108     if(path->head + size != path->cap)
109     {
110         Point *old_head = path->head;
111         WayPoint *curr;
112         path->head = g_renew(Point, old_head, size);
113         path->cap = path->head + size;
114         if(path->head != old_head)
115         {
116             path->tail = path->head + (path->tail - old_head);
117
118             /* Adjust all of the waypoints. */
119             for(curr = path->whead - 1; curr++ != path->wtail; )
120                 curr->point = path->head + (curr->point - old_head);
121         }
122     }
123
124     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
125 }
126
127 void
128 path_wresize(Path *path, gint wsize)
129 {
130     printf("%s()\n", __PRETTY_FUNCTION__);
131
132     if(path->whead + wsize != path->wcap)
133     {
134         WayPoint *old_whead = path->whead;
135         path->whead = g_renew(WayPoint, old_whead, wsize);
136         path->wtail = path->whead + (path->wtail - old_whead);
137         path->wcap = path->whead + wsize;
138     }
139
140     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
141 }
142
143 static void
144 read_path_from_db(Path *path, sqlite3_stmt *select_stmt)
145 {
146     printf("%s()\n", __PRETTY_FUNCTION__);
147
148     MACRO_PATH_INIT(*path);
149     while(SQLITE_ROW == sqlite3_step(select_stmt))
150     {
151         const gchar *desc;
152
153         MACRO_PATH_INCREMENT_TAIL(*path);
154         path->tail->unitx = sqlite3_column_int(select_stmt, 0);
155         path->tail->unity = sqlite3_column_int(select_stmt, 1);
156         path->tail->time = sqlite3_column_int(select_stmt, 2);
157         path->tail->altitude = sqlite3_column_int(select_stmt, 3);
158
159         desc = sqlite3_column_text(select_stmt, 4);
160         if(desc)
161         {
162             MACRO_PATH_INCREMENT_WTAIL(*path);
163             path->wtail->point = path->tail;
164             path->wtail->desc = g_strdup(desc);
165         }
166     }
167     sqlite3_reset(select_stmt);
168
169     /* If the last point isn't null, then add another null point. */
170     if(path->tail->unity)
171     {
172         MACRO_PATH_INCREMENT_TAIL(*path);
173         *path->tail = _point_null;
174     }
175
176     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
177 }
178
179 /* Returns the new next_update_index. */
180 static gint
181 write_path_to_db(Path *path,
182         sqlite3_stmt *delete_path_stmt,
183         sqlite3_stmt *delete_way_stmt,
184         sqlite3_stmt *insert_path_stmt,
185         sqlite3_stmt *insert_way_stmt,
186         gint index_last_saved)
187 {
188     Point *curr;
189     WayPoint *wcurr;
190     gint num;
191     gboolean success = TRUE;
192     printf("%s(%d)\n", __PRETTY_FUNCTION__, index_last_saved);
193
194     /* Start transaction. */
195     sqlite3_step(_path_stmt_trans_begin);
196     sqlite3_reset(_path_stmt_trans_begin);
197
198     if(index_last_saved == 0)
199     {
200         /* Replace the whole thing, so delete the table first. */
201         if(SQLITE_DONE != sqlite3_step(delete_way_stmt)
202                 || SQLITE_DONE != sqlite3_step(delete_path_stmt))
203         {
204             gchar buffer[BUFFER_SIZE];
205             snprintf(buffer, sizeof(buffer), "%s\n%s",
206                     _("Failed to write to path database. "
207                         "Tracks and routes may not be saved."),
208                     sqlite3_errmsg(_path_db));
209             popup_error(_window, buffer);
210             success = FALSE;
211         }
212         sqlite3_reset(delete_way_stmt);
213         sqlite3_reset(delete_path_stmt);
214     }
215
216     for(num = index_last_saved, curr = path->head + num, wcurr = path->whead;
217             success && ++curr <= path->tail; ++num)
218     {
219         /* If this is the last point, and it is null, don't write it. */
220         if(curr == path->tail && !curr->unity)
221             break;
222
223         /* Insert the path point. */
224         if(SQLITE_OK != sqlite3_bind_int(insert_path_stmt, 1, curr->unitx)
225         || SQLITE_OK != sqlite3_bind_int(insert_path_stmt, 2, curr->unity)
226         || SQLITE_OK != sqlite3_bind_int(insert_path_stmt, 3, curr->time)
227         || SQLITE_OK != sqlite3_bind_int(insert_path_stmt, 4, curr->altitude)
228         || SQLITE_DONE != sqlite3_step(insert_path_stmt))
229         {
230             gchar buffer[BUFFER_SIZE];
231             snprintf(buffer, sizeof(buffer), "%s\n%s",
232                     _("Failed to write to path database. "
233                         "Tracks and routes may not be saved."),
234                     sqlite3_errmsg(_path_db));
235             popup_error(_window, buffer);
236             success = FALSE;
237         }
238         sqlite3_reset(insert_path_stmt);
239
240         /* Now, check if curr is a waypoint. */
241         if(success && wcurr <= path->wtail && wcurr->point == curr)
242         {
243             gint num = sqlite3_last_insert_rowid(_path_db);
244             if(SQLITE_OK != sqlite3_bind_int(insert_way_stmt, 1, num)
245             || SQLITE_OK != sqlite3_bind_text(insert_way_stmt, 2, wcurr->desc,
246                 -1, SQLITE_STATIC)
247             || SQLITE_DONE != sqlite3_step(insert_way_stmt))
248             {
249                 gchar buffer[BUFFER_SIZE];
250                 snprintf(buffer, sizeof(buffer), "%s\n%s",
251                         _("Failed to write to path database. "
252                             "Tracks and routes may not be saved."),
253                         sqlite3_errmsg(_path_db));
254                 popup_error(_window, buffer);
255                 success = FALSE;
256             }
257             sqlite3_reset(insert_way_stmt);
258             wcurr++;
259         }
260     }
261     if(success)
262     {
263         sqlite3_step(_path_stmt_trans_commit);
264         sqlite3_reset(_path_stmt_trans_commit);
265         vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
266         return num;
267     }
268     else
269     {
270         sqlite3_step(_path_stmt_trans_rollback);
271         sqlite3_reset(_path_stmt_trans_rollback);
272         vprintf("%s(): return 0\n", __PRETTY_FUNCTION__);
273         return index_last_saved;
274     }
275 }
276
277 void
278 path_save_route_to_db()
279 {
280     if(_path_db)
281     {
282         write_path_to_db(&_route,
283                           _route_stmt_delete_path,
284                           _route_stmt_delete_way,
285                           _route_stmt_insert_path,
286                           _route_stmt_insert_way,
287                           0);
288     }
289 }
290
291 static void
292 path_save_track_to_db()
293 {
294     if(_path_db)
295     {
296         write_path_to_db(&_track,
297                           _track_stmt_delete_path,
298                           _track_stmt_delete_way,
299                           _track_stmt_insert_path,
300                           _track_stmt_insert_way,
301                           0);
302     }
303 }
304
305 static void
306 path_update_track_in_db()
307 {
308     if(_path_db)
309     {
310         _track_index_last_saved = write_path_to_db(&_track,
311                           _track_stmt_delete_path,
312                           _track_stmt_delete_way,
313                           _track_stmt_insert_path,
314                           _track_stmt_insert_way,
315                           _track_index_last_saved);
316     }
317 }
318
319 /**
320  * Updates _near_point, _next_way, and _next_wpt.  If quick is FALSE (as
321  * it is when this function is called from route_find_nearest_point), then
322  * the entire list (starting from _near_point) is searched.  Otherwise, we
323  * stop searching when we find a point that is farther away.
324  */
325 static gboolean
326 route_update_nears(gboolean quick)
327 {
328     gboolean ret = FALSE;
329     Point *curr, *near;
330     WayPoint *wcurr, *wnext;
331     gint64 near_dist_squared;
332     printf("%s(%d)\n", __PRETTY_FUNCTION__, quick);
333
334     /* If we have waypoints (_next_way != NULL), then determine the "next
335      * waypoint", which is defined as the waypoint after the nearest point,
336      * UNLESS we've passed that waypoint, in which case the waypoint after
337      * that waypoint becomes the "next" waypoint. */
338     if(_next_way)
339     {
340         /* First, set near_dist_squared with the new distance from
341          * _near_point. */
342         near = _near_point;
343         near_dist_squared = DISTANCE_SQUARED(_pos, *near);
344
345         /* Now, search _route for a closer point.  If quick is TRUE, then we'll
346          * only search forward, only as long as we keep finding closer points.
347          */
348         for(curr = _near_point; curr++ != _route.tail; )
349         {
350             if(curr->unity)
351             {
352                 gint64 dist_squared = DISTANCE_SQUARED(_pos, *curr);
353                 if(dist_squared <= near_dist_squared)
354                 {
355                     near = curr;
356                     near_dist_squared = dist_squared;
357                 }
358                 else if(quick)
359                     break;
360             }
361         }
362
363         /* Update _near_point. */
364         _near_point = near;
365
366         for(wnext = wcurr = _next_way; wcurr < _route.wtail; wcurr++)
367         {
368             if(wcurr->point < near
369             /* Okay, this else if expression warrants explanation.  If the
370              * nearest track point happens to be a waypoint, then we want to
371              * check if we have "passed" that waypoint.  To check this, we
372              * test the distance from _pos to the waypoint and from _pos to
373              * _next_wpt, and if the former is increasing and the latter is
374              * decreasing, then we have passed the waypoint, and thus we
375              * should skip it.  Note that if there is no _next_wpt, then
376              * there is no next waypoint, so we do not skip it in that case. */
377                 || (wcurr->point == near && quick
378                     && (_next_wpt
379                      && (DISTANCE_SQUARED(_pos, *near) > _next_way_dist_squared
380                       && DISTANCE_SQUARED(_pos, *_next_wpt)
381                                                    < _next_wpt_dist_squared))))
382             {
383                 wnext = wcurr + 1;
384             }
385             else
386                 break;
387         }
388
389         if(wnext == _route.wtail && (wnext->point < near
390                 || (wnext->point == near && quick
391                     && (_next_wpt
392                      && (DISTANCE_SQUARED(_pos, *near) > _next_way_dist_squared
393                       &&DISTANCE_SQUARED(_pos, *_next_wpt)
394                                                  < _next_wpt_dist_squared)))))
395         {
396             _next_way = NULL;
397             _next_wpt = NULL;
398             _next_way_dist_squared = INT64_MAX;
399             _next_wpt_dist_squared = INT64_MAX;
400             ret = TRUE;
401         }
402         /* Only update _next_way (and consequently _next_wpt) if _next_way is
403          * different, and record that fact for return. */
404         else
405         {
406             if(!quick || _next_way != wnext)
407             {
408                 _next_way = wnext;
409                 _next_wpt = wnext->point;
410                 if(_next_wpt == _route.tail)
411                     _next_wpt = NULL;
412                 else
413                 {
414                     while(!(++_next_wpt)->unity)
415                     {
416                         if(_next_wpt == _route.tail)
417                         {
418                             _next_wpt = NULL;
419                             break;
420                         }
421                     }
422                 }
423                 ret = TRUE;
424             }
425             _next_way_dist_squared = DISTANCE_SQUARED(_pos, *wnext->point);
426             if(_next_wpt)
427                 _next_wpt_dist_squared = DISTANCE_SQUARED(_pos, *_next_wpt);
428         }
429     }
430
431     vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, ret);
432     return ret;
433 }
434
435 /**
436  * Reset the _near_point data by searching the entire route for the nearest
437  * route point and waypoint.
438  */
439 void
440 route_find_nearest_point()
441 {
442     printf("%s()\n", __PRETTY_FUNCTION__);
443
444     /* Initialize _near_point to first non-zero point. */
445     _near_point = _route.head;
446     while(!_near_point->unity && _near_point != _route.tail)
447         _near_point++;
448
449     /* Initialize _next_way. */
450     if(_route.wtail < _route.whead
451             || (_autoroute_data.enabled && _route.wtail == _route.whead))
452         _next_way = NULL;
453     else
454         /* We have at least one waypoint. */
455         _next_way = _autoroute_data.enabled ? _route.whead + 1 : _route.whead;
456     _next_way_dist_squared = INT64_MAX;
457
458     /* Initialize _next_wpt. */
459     _next_wpt = NULL;
460     _next_wpt_dist_squared = INT64_MAX;
461
462     route_update_nears(FALSE);
463
464     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
465 }
466
467 /**
468  * Show the distance from the current GPS location to the given point,
469  * following the route.  If point is NULL, then the distance is shown to the
470  * next waypoint.
471  */
472 gboolean
473 route_show_distance_to(Point *point)
474 {
475     gchar buffer[80];
476     gdouble lat1, lon1, lat2, lon2;
477     gdouble sum = 0.0;
478     printf("%s()\n", __PRETTY_FUNCTION__);
479
480     /* If point is NULL, use the next waypoint. */
481     if(point == NULL && _next_way)
482         point = _next_way->point;
483
484     /* If point is still NULL, return an error. */
485     if(point == NULL)
486     {
487         printf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
488         return FALSE;
489     }
490
491     unit2latlon(_pos.unitx, _pos.unity, lat1, lon1);
492     if(point > _near_point)
493     {
494         Point *curr;
495         /* Skip _near_point in case we have already passed it. */
496         for(curr = _near_point + 1; curr <= point; ++curr)
497         {
498             if(curr->unity)
499             {
500                 unit2latlon(curr->unitx, curr->unity, lat2, lon2);
501                 sum += calculate_distance(lat1, lon1, lat2, lon2);
502                 lat1 = lat2;
503                 lon1 = lon2;
504             }
505         }
506     }
507     else if(point < _near_point)
508     {
509         Point *curr;
510         /* Skip _near_point in case we have already passed it. */
511         for(curr = _near_point - 1; curr >= point; --curr)
512         {
513             if(curr->unity)
514             {
515                 unit2latlon(curr->unitx, curr->unity, lat2, lon2);
516                 sum += calculate_distance(lat1, lon1, lat2, lon2);
517                 lat1 = lat2;
518                 lon1 = lon2;
519             }
520         }
521     }
522     else
523     {
524         /* Waypoint _is_ the nearest point. */
525         unit2latlon(_near_point->unitx, _near_point->unity, lat2, lon2);
526         sum += calculate_distance(lat1, lon1, lat2, lon2);
527     }
528
529     snprintf(buffer, sizeof(buffer), "%s: %.02f %s", _("Distance"),
530             sum * UNITS_CONVERT[_units], UNITS_ENUM_TEXT[_units]);
531     MACRO_BANNER_SHOW_INFO(_window, buffer);
532
533     return TRUE;
534     printf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
535 }
536
537 void
538 route_show_distance_to_next()
539 {
540     printf("%s()\n", __PRETTY_FUNCTION__);
541
542     if(!route_show_distance_to(NULL))
543     {
544         MACRO_BANNER_SHOW_INFO(_window, _("There is no next waypoint."));
545     }
546     printf("%s(): return\n", __PRETTY_FUNCTION__);
547 }
548
549 void
550 route_show_distance_to_last()
551 {
552     printf("%s()\n", __PRETTY_FUNCTION__);
553
554     if(_route.head != _route.tail)
555     {
556         /* Find last non-zero point. */
557         Point *p;
558         for(p = _route.tail; !p->unity; p--) { }
559         route_show_distance_to(p);
560     }
561     else
562     {
563         MACRO_BANNER_SHOW_INFO(_window, _("The current route is empty."));
564     }
565     printf("%s(): return\n", __PRETTY_FUNCTION__);
566 }
567
568 static void
569 track_show_distance_from(Point *point)
570 {
571     gchar buffer[80];
572     gdouble lat1, lon1, lat2, lon2;
573     gdouble sum = 0.0;
574     Point *curr;
575     unit2latlon(_pos.unitx, _pos.unity, lat1, lon1);
576
577     /* Skip _track.tail because that should be _pos. */
578     for(curr = _track.tail; curr > point; --curr)
579     {
580         if(curr->unity)
581         {
582             unit2latlon(curr->unitx, curr->unity, lat2, lon2);
583             sum += calculate_distance(lat1, lon1, lat2, lon2);
584             lat1 = lat2;
585             lon1 = lon2;
586         }
587     }
588
589     snprintf(buffer, sizeof(buffer), "%s: %.02f %s", _("Distance"),
590             sum * UNITS_CONVERT[_units], UNITS_ENUM_TEXT[_units]);
591     MACRO_BANNER_SHOW_INFO(_window, buffer);
592 }
593
594 void
595 track_show_distance_from_last()
596 {
597     printf("%s()\n", __PRETTY_FUNCTION__);
598
599     /* Find last zero point. */
600     if(_track.head != _track.tail)
601     {
602         Point *point;
603         /* Find last zero point. */
604         for(point = _track.tail; point->unity; point--) { }
605         track_show_distance_from(point);
606     }
607     else
608     {
609         MACRO_BANNER_SHOW_INFO(_window, _("The current track is empty."));
610     }
611     printf("%s(): return\n", __PRETTY_FUNCTION__);
612 }
613
614 void
615 track_show_distance_from_first()
616 {
617     printf("%s()\n", __PRETTY_FUNCTION__);
618
619     /* Find last zero point. */
620     if(_track.head != _track.tail)
621         track_show_distance_from(_track.head);
622     else
623     {
624         MACRO_BANNER_SHOW_INFO(_window, _("The current track is empty."));
625     }
626     printf("%s(): return\n", __PRETTY_FUNCTION__);
627 }
628
629 static gboolean
630 route_download_and_setup(GtkWidget *parent, const gchar *source_url,
631         const gchar *from, const gchar *to,
632         gboolean avoid_highways, gint replace_policy)
633 {
634     gchar *from_escaped;
635     gchar *to_escaped;
636     gchar *buffer;
637     gchar *bytes;
638     gint size;
639     GnomeVFSResult vfs_result;
640     printf("%s(%s, %s)\n", __PRETTY_FUNCTION__, from, to);
641
642     from_escaped = gnome_vfs_escape_string(from);
643     to_escaped = gnome_vfs_escape_string(to);
644     buffer = g_strdup_printf(source_url, from_escaped, to_escaped);
645     g_free(from_escaped);
646     g_free(to_escaped);
647
648     if(avoid_highways)
649     {
650         gchar *old = buffer;
651         buffer = g_strconcat(old, "&avoid_highways=on", NULL);
652         g_free(old);
653     }
654
655     /* Attempt to download the route from the server. */
656     vfs_result = gnome_vfs_read_entire_file(buffer, &size, &bytes);
657     g_free (buffer);
658
659     if(vfs_result != GNOME_VFS_OK)
660     {
661         g_free(bytes);
662         popup_error(parent, gnome_vfs_result_to_string(vfs_result));
663         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
664         return FALSE;
665     }
666
667     if(strncmp(bytes, "<?xml", strlen("<?xml")))
668     {
669         /* Not an XML document - must be bad locations. */
670         popup_error(parent,
671                 _("Invalid source or destination."));
672         g_free(bytes);
673         vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
674         return FALSE;
675     }
676     /* Else, if GPS is enabled, replace the route, otherwise append it. */
677     else if(gpx_path_parse(&_route, bytes, size, replace_policy))
678     {
679         path_save_route_to_db();
680
681         /* Find the nearest route point, if we're connected. */
682         route_find_nearest_point();
683
684         map_force_redraw();
685
686         MACRO_BANNER_SHOW_INFO(_window, _("Route Downloaded"));
687         g_free(bytes);
688
689         vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
690         return TRUE;
691     }
692     popup_error(parent, _("Error parsing GPX file."));
693     g_free(bytes);
694
695     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
696     return FALSE;
697 }
698
699 /**
700  * This function is periodically run to download updated auto-route data
701  * from the route source.
702  */
703 static gboolean
704 auto_route_dl_idle()
705 {
706     gchar latstr[32], lonstr[32], latlonstr[80];
707     printf("%s(%f, %f, %s)\n", __PRETTY_FUNCTION__,
708             _gps.lat, _gps.lon, _autoroute_data.dest);
709
710     g_ascii_dtostr(latstr, 32, _gps.lat);
711     g_ascii_dtostr(lonstr, 32, _gps.lon);
712     snprintf(latlonstr, sizeof(latlonstr), "%s, %s", latstr, lonstr);
713
714     if(!route_download_and_setup(_window, _autoroute_data.source_url, latlonstr,
715             _autoroute_data.dest, _autoroute_data.avoid_highways,0))
716         cancel_autoroute();
717
718     _autoroute_data.in_progress = FALSE;
719
720     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
721     return FALSE;
722 }
723
724 void
725 path_reset_route()
726 {
727     printf("%s()\n", __PRETTY_FUNCTION__);
728
729     route_find_nearest_point();
730     MACRO_MAP_RENDER_DATA();
731     MACRO_QUEUE_DRAW_AREA();
732
733     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
734 }
735
736 /**
737  * Add a point to the _track list.  This function is slightly overloaded,
738  * since it is what houses the check for "have we moved
739  * significantly": it also initiates the re-calculation of the _near_point
740  * data, as well as calling osso_display_state_on() when we have the focus.
741  *
742  * If a non-zero time is given, then the current position (as taken from the
743  * _pos variable) is appended to _track with the given time.  If time is zero,
744  * then _point_null is appended to _track with time zero (this produces a
745  * "break" in the track).
746  */
747 gboolean
748 track_add(time_t time, gboolean newly_fixed)
749 {
750     gboolean show_directions = TRUE;
751     gint announce_thres_unsquared;
752     gboolean ret = FALSE;
753     printf("%s(%d, %d, %d, %d)\n", __PRETTY_FUNCTION__,
754             (guint)time, newly_fixed, _pos.unitx, _pos.unity);
755
756     gboolean moving = FALSE;
757     gboolean approaching_waypoint = FALSE;
758     gint xdiff, ydiff, dopcand;
759
760     announce_thres_unsquared = (20+_gps.speed) * _announce_notice_ratio*32;
761
762     if(!_track.tail->unity
763             || ((xdiff = _pos.unitx - _track.tail->unitx), /* comma op */
764                 (ydiff = _pos.unity - _track.tail->unity), /* comma op */
765                 /* Check if xdiff or ydiff are huge. */
766                 ((abs(xdiff) >> 12) || (abs(ydiff) >> 12)
767                 /* Okay, let's see if we've moved enough to justify adding
768                  * to the track.  It depends on our error.  I'd like to
769                  * make the threshold roughly linear with respect to the
770                  * P/HDOP (completely arbitrary, I know), but I also
771                  * want to keep the threshold at a minimum of 2
772                  * zoom-level-4 pixel, and I want dop's of less than 2 to
773                  * also have a 1-pixel threshold.  I also throw in some
774                  * PDOP into the mix, just for fun. */
775                 || ((dopcand = 8 * (_gps.pdop - 6 +(_gps.hdop*_gps.hdop))),
776                     ((xdiff * xdiff) + (ydiff * ydiff)
777                          >= (MAX(2, dopcand) << 8))))))
778     {
779         /* We moved enough to actually register a move. */
780         ret = TRUE;
781
782         /* Update the nearest-waypoint data. */
783         if(_route.head != _route.tail
784                 && (newly_fixed ? (route_find_nearest_point(), TRUE)
785                                 : route_update_nears(TRUE)))
786         {
787             /* Nearest waypoint has changed - re-render paths. */
788             map_render_paths();
789             MACRO_QUEUE_DRAW_AREA();
790         }
791
792         if(_enable_tracking)
793         {
794             if(_show_paths & TRACKS_MASK)
795             {
796                 /* Instead of calling map_render_paths(), we'll draw the new
797                  * line ourselves and call gtk_widget_queue_draw_area(). */
798                 gint tx1, ty1, tx2, ty2;
799                 map_render_segment(_gc[COLORABLE_TRACK],
800                         _gc[COLORABLE_TRACK_BREAK],
801                         _track.tail->unitx, _track.tail->unity,
802                         _pos.unitx, _pos.unity);
803                 if(_track.tail->unity)
804                 {
805                     unit2screen(_track.tail->unitx, _track.tail->unity,
806                             tx1, ty1);
807                     unit2screen(_pos.unitx, _pos.unity, tx2, ty2);
808                     gtk_widget_queue_draw_area(_map_widget,
809                             MIN(tx1, tx2) - _draw_width,
810                             MIN(ty1, ty2) - _draw_width,
811                             abs(tx1 - tx2) + (2 * _draw_width),
812                             abs(ty1 - ty2) + (2 * _draw_width));
813                 }
814             }
815
816             MACRO_PATH_INCREMENT_TAIL(_track);
817             *_track.tail = _pos;
818         }
819
820         /* Calculate distance to route. (point to line) */
821         if(_near_point)
822         {
823             gint route_x1, route_x2, route_y1, route_y2;
824             gint64 route_dist_squared_1 = INT64_MAX;
825             gint64 route_dist_squared_2 = INT64_MAX;
826             gfloat slope;
827
828             route_x1 = _near_point->unitx;
829             route_y1 = _near_point->unity;
830
831             /* Try previous point first. */
832             if(_near_point != _route.head && _near_point[-1].unity)
833             {
834                 route_x2 = _near_point[-1].unitx;
835                 route_y2 = _near_point[-1].unity;
836                 slope = (gfloat)(route_y2 - route_y1)
837                     / (gfloat)(route_x2 - route_x1);
838
839                 if(route_x1 == route_x2)
840                 {
841                     /* Vertical line special case. */
842                     route_dist_squared_1 = (_pos.unitx - route_x1)
843                         * (_pos.unitx - route_x1);
844                 }
845                 else
846                 {
847                     route_dist_squared_1 = abs((slope * _pos.unitx)
848                         - _pos.unity + (route_y1 - (slope * route_x1)));
849                     route_dist_squared_1 =
850                         route_dist_squared_1 * route_dist_squared_1
851                         / ((slope * slope) + 1);
852                 }
853             }
854             if(_near_point != _route.tail && _near_point[1].unity)
855             {
856                 route_x2 = _near_point[1].unitx;
857                 route_y2 = _near_point[1].unity;
858                 slope = (gfloat)(route_y2 - route_y1)
859                     / (gfloat)(route_x2 - route_x1);
860
861                 if(route_x1 == route_x2)
862                 {
863                     /* Vertical line special case. */
864                     route_dist_squared_2 = (_pos.unitx - route_x1)
865                         * (_pos.unitx - route_x1);
866                 }
867                 else
868                 {
869                     route_dist_squared_2 = abs((slope * _pos.unitx)
870                         - _pos.unity + (route_y1 - (slope * route_x1)));
871                     route_dist_squared_2 =
872                         route_dist_squared_2 * route_dist_squared_2
873                         / ((slope * slope) + 1);
874                 }
875             }
876
877             /* Check if our distance from the route is large. */
878             if(MIN(route_dist_squared_1, route_dist_squared_2)
879                     > (2000 * 2000))
880             {
881                 /* Prevent announcments from occurring. */
882                 announce_thres_unsquared = INT_MAX;
883
884                 if(_autoroute_data.enabled && !_autoroute_data.in_progress)
885                 {
886                     MACRO_BANNER_SHOW_INFO(_window,
887                             _("Recalculating directions..."));
888                     _autoroute_data.in_progress = TRUE;
889                     show_directions = FALSE;
890                     g_idle_add((GSourceFunc)auto_route_dl_idle, NULL);
891                 }
892                 else
893                 {
894                     /* Reset the route to try and find the nearest point.*/
895                     path_reset_route();
896                 }
897             }
898         }
899
900         /* Keep the display on. */
901         moving = TRUE;
902     }
903
904     if(_initial_distance_waypoint
905            && (_next_way != _initial_distance_waypoint
906            ||  _next_way_dist_squared > (_initial_distance_from_waypoint
907                                        * _initial_distance_from_waypoint)))
908     {
909         /* We've moved on to the next waypoint, or we're really far from
910          * the current waypoint. */
911         if(_waypoint_banner)
912         {
913             gtk_widget_destroy(_waypoint_banner);
914             _waypoint_banner = NULL;
915         }
916         _initial_distance_from_waypoint = -1.f;
917         _initial_distance_waypoint = NULL;
918     }
919
920     /* Check if we should announce upcoming waypoints. */
921     if(_enable_announce
922             && (_initial_distance_waypoint || _next_way_dist_squared
923                 < (announce_thres_unsquared * announce_thres_unsquared)))
924     {
925         if(show_directions)
926         {
927             if(!_initial_distance_waypoint)
928             {
929                 /* First time we're close enough to this waypoint. */
930                 if(_enable_voice
931                         /* And that we haven't already announced it. */
932                         && strcmp(_next_way->desc, _last_spoken_phrase))
933                 {
934                     g_free(_last_spoken_phrase);
935                     _last_spoken_phrase = g_strdup(_next_way->desc);
936                     if(!fork())
937                     {
938                         /* We are the fork child.  Synthesize the voice. */
939                         hildon_play_system_sound(
940                             "/usr/share/sounds/ui-information_note.wav");
941                         sleep(1);
942                         printf("%s %s\n", _voice_synth_path,
943                                 _last_spoken_phrase);
944                         execl(_voice_synth_path, basename(_voice_synth_path),
945                                 "-t", _last_spoken_phrase, (char *)NULL);
946                         /* No good?  Try to launch it with /bin/sh */
947                         execl("/bin/sh", "sh", _voice_synth_path,
948                                 "-t", _last_spoken_phrase, (char *)NULL);
949                         /* Still no good? Oh well... */
950                         exit(0);
951                     }
952                 }
953                 _initial_distance_from_waypoint
954                     = sqrtf(_next_way_dist_squared);
955                 _initial_distance_waypoint = _next_way;
956                 if(_next_wpt && _next_wpt->unity != 0)
957                 {
958                     /* Create a banner for us the show progress. */
959                     _waypoint_banner = hildon_banner_show_progress(
960                             _window, NULL, _next_way->desc);
961                 }
962                 else
963                 {
964                     /* This is the last point in a segment, i.e.
965                      * "Arrive at ..." - just announce. */
966                     MACRO_BANNER_SHOW_INFO(_window, _next_way->desc);
967                 }
968             }
969             else if(_waypoint_banner);
970             {
971                 /* We're already close to this waypoint. */
972                 gdouble fraction = 1.f - (sqrtf(_next_way_dist_squared)
973                         / _initial_distance_from_waypoint);
974                 BOUND(fraction, 0.f, 1.f);
975                 hildon_banner_set_fraction(
976                         HILDON_BANNER(_waypoint_banner), fraction);
977             }
978         }
979         approaching_waypoint = TRUE;
980     }
981     else if(_next_way_dist_squared > 2 * (_initial_distance_from_waypoint
982                                         * _initial_distance_from_waypoint))
983     {
984         /* We're too far away now - destroy the banner. */
985     }
986
987     UNBLANK_SCREEN(moving, approaching_waypoint);
988
989     /* Maybe update the track database. */
990     {
991         static time_t last_track_db_update = 0;
992         if(!time || (time - last_track_db_update > 60
993                 && _track.tail - _track.head + 1 > _track_index_last_saved))
994         {
995             path_update_track_in_db();
996             last_track_db_update = time;
997         }
998     }
999
1000     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1001     return ret;
1002 }
1003
1004 void
1005 track_clear()
1006 {
1007     GtkWidget *confirm;
1008
1009     confirm = hildon_note_new_confirmation(GTK_WINDOW(_window),
1010                             _("Really clear the track?"));
1011
1012     if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm))) {
1013         MACRO_PATH_FREE(_track);
1014         MACRO_PATH_INIT(_track);
1015         path_save_track_to_db();
1016         map_force_redraw();
1017     }
1018
1019     gtk_widget_destroy(confirm);
1020 }
1021
1022 void
1023 track_insert_break(gboolean temporary)
1024 {
1025     printf("%s()\n", __PRETTY_FUNCTION__);
1026
1027     if(_track.tail->unity)
1028     {
1029         gint x1, y1;
1030         unit2screen(_track.tail->unitx, _track.tail->unity, x1, y1);
1031
1032         MACRO_PATH_INCREMENT_TAIL(_track);
1033         *_track.tail = _point_null;
1034
1035         /* To mark a "waypoint" in a track, we'll add a (0, 0) point and then
1036          * another instance of the most recent track point. */
1037         if(temporary)
1038         {
1039             MACRO_PATH_INCREMENT_TAIL(_track);
1040             *_track.tail = _track.tail[-2];
1041         }
1042
1043         /** Instead of calling map_render_paths(), we'll just add the waypoint
1044          * ourselves. */
1045         /* Make sure this circle will be visible. */
1046         if((x1 < _view_width_pixels) && (y1 < _view_height_pixels))
1047             gdk_draw_arc(_map_pixmap, _gc[COLORABLE_TRACK_BREAK],
1048                     FALSE, /* FALSE: not filled. */
1049                     x1 - _draw_width,
1050                     y1 - _draw_width,
1051                     2 * _draw_width,
1052                     2 * _draw_width,
1053                     0, /* start at 0 degrees. */
1054                     360 * 64);
1055     }
1056
1057     /* Update the track database. */
1058     path_update_track_in_db();
1059
1060     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1061 }
1062
1063 /**
1064  * Cancel the current auto-route.
1065  */
1066 void
1067 cancel_autoroute()
1068 {
1069     printf("%s()\n", __PRETTY_FUNCTION__);
1070
1071     if(_autoroute_data.enabled)
1072     {
1073         _autoroute_data.enabled = FALSE;
1074         g_free(_autoroute_data.source_url);
1075         _autoroute_data.source_url = NULL;
1076         g_free(_autoroute_data.dest);
1077         _autoroute_data.dest = NULL;
1078         _autoroute_data.in_progress = FALSE;
1079     }
1080
1081     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1082 }
1083
1084 WayPoint *
1085 find_nearest_waypoint(gint unitx, gint unity)
1086 {
1087     WayPoint *wcurr;
1088     WayPoint *wnear;
1089     gint64 nearest_squared;
1090     Point pos = { unitx, unity, 0, INT_MIN };
1091     printf("%s()\n", __PRETTY_FUNCTION__);
1092
1093     wcurr = wnear = _route.whead;
1094     if(wcurr && wcurr <= _route.wtail)
1095     {
1096         nearest_squared = DISTANCE_SQUARED(pos, *(wcurr->point));
1097
1098         wnear = _route.whead;
1099         while(++wcurr <=  _route.wtail)
1100         {
1101             gint64 test_squared = DISTANCE_SQUARED(pos, *(wcurr->point));
1102             if(test_squared < nearest_squared)
1103             {
1104                 wnear = wcurr;
1105                 nearest_squared = test_squared;
1106             }
1107         }
1108
1109         /* Only use the waypoint if it is within a 6*_draw_width square drawn
1110          * around the position. This is consistent with select_poi(). */
1111         if(abs(unitx - wnear->point->unitx) < pixel2unit(3 * _draw_width)
1112             && abs(unity - wnear->point->unity) < pixel2unit(3 * _draw_width))
1113             return wnear;
1114     }
1115
1116     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1117     return NULL;
1118 }
1119
1120 static gboolean
1121 origin_type_selected(GtkWidget *toggle, RouteDownloadInfo *oti)
1122 {
1123     printf("%s()\n", __PRETTY_FUNCTION__);
1124
1125     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
1126     {
1127         gtk_widget_set_sensitive(oti->txt_from, toggle == oti->rad_use_text);
1128         gtk_widget_set_sensitive(oti->chk_auto, toggle == oti->rad_use_gps);
1129     }
1130     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1131     return TRUE;
1132 }
1133
1134 static gboolean
1135 route_download_swap(GtkWidget *button, RouteDownloadInfo *oti)
1136 {
1137     gchar *old_origin;
1138     printf("%s()\n", __PRETTY_FUNCTION__);
1139
1140     /* Save the old origin. */
1141     old_origin = g_strdup(gtk_entry_get_text(GTK_ENTRY(oti->txt_from)));
1142
1143     /* Set the origin text field equal to the current destination. */
1144     gtk_entry_set_text(GTK_ENTRY(oti->txt_from),
1145             gtk_entry_get_text(GTK_ENTRY(oti->txt_to)));
1146
1147     /* Set the contents of the destination text field. */
1148     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti->rad_use_gps)))
1149     {
1150         /* "Use GPS Location" is enabled - set Destination to GPS Location */
1151         gchar buffer[80];
1152         gchar strlat[32];
1153         gchar strlon[32];
1154         g_ascii_formatd(strlat, 32, "%.06f", _gps.lat);
1155         g_ascii_formatd(strlon, 32, "%.06f", _gps.lon);
1156         snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
1157         gtk_entry_set_text(GTK_ENTRY(oti->txt_to), buffer);
1158     }
1159     else if(gtk_toggle_button_get_active(
1160                 GTK_TOGGLE_BUTTON(oti->rad_use_route)))
1161     {
1162         /* "Use End of Route" is enabled - set Destination to start of route */
1163         gchar buffer[80];
1164         gchar strlat[32];
1165         gchar strlon[32];
1166         Point *p;
1167         gdouble lat, lon;
1168
1169         /* Use first non-zero route point. */
1170         for(p = _route.head; !p->unity; p++) { }
1171
1172         unit2latlon(p->unitx, p->unity, lat, lon);
1173         g_ascii_formatd(strlat, 32, "%.06f", lat);
1174         g_ascii_formatd(strlon, 32, "%.06f", lon);
1175         snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
1176         gtk_entry_set_text(GTK_ENTRY(oti->txt_to), buffer);
1177     }
1178     else if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti->rad_use_text)))
1179     {
1180         /* "Origin" is enabled - just use the text. */
1181         gtk_entry_set_text(GTK_ENTRY(oti->txt_to), old_origin);
1182     }
1183
1184     g_free(old_origin);
1185     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(oti->rad_use_text), TRUE);
1186
1187     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1188     return TRUE;
1189 }
1190
1191 /**
1192  * Display a dialog box to the user asking them to download a route.  The
1193  * "From" and "To" textfields may be initialized using the first two
1194  * parameters.  The third parameter, if set to TRUE, will cause the "Use GPS
1195  * Location" checkbox to be enabled, which automatically sets the "From" to the
1196  * current GPS position (this overrides any value that may have been passed as
1197  * the "To" initializer).
1198  * None of the passed strings are freed - that is left to the caller, and it is
1199  * safe to free either string as soon as this function returns.
1200  */
1201 gboolean
1202 route_download(gchar *to)
1203 {
1204     static GtkWidget *dialog = NULL;
1205     static GtkWidget *table = NULL;
1206     static GtkWidget *label = NULL;
1207     static GtkWidget *txt_source_url = NULL;
1208     static GtkWidget *btn_swap = NULL;
1209     static RouteDownloadInfo oti;
1210     printf("%s()\n", __PRETTY_FUNCTION__);
1211
1212     conic_recommend_connected();
1213
1214     if(dialog == NULL)
1215     {
1216         GtkEntryCompletion *from_comp;
1217         GtkEntryCompletion *to_comp;
1218         dialog = gtk_dialog_new_with_buttons(_("Download Route"),
1219                 GTK_WINDOW(_window), GTK_DIALOG_MODAL,
1220                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1221                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1222                 NULL);
1223
1224         /* Enable the help button. */
1225 #ifndef LEGACY
1226         hildon_help_dialog_help_enable(
1227 #else
1228         ossohelp_dialog_help_enable(
1229 #endif
1230                 GTK_DIALOG(dialog), HELP_ID_DOWNROUTE, _osso);
1231
1232         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1233                 table = gtk_table_new(2, 5, FALSE), TRUE, TRUE, 0);
1234
1235         /* Source URL. */
1236         gtk_table_attach(GTK_TABLE(table),
1237                 label = gtk_label_new(_("Source URL")),
1238                 0, 1, 0, 1, GTK_FILL, 0, 2, 4);
1239         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1240         gtk_table_attach(GTK_TABLE(table),
1241                 txt_source_url = gtk_entry_new(),
1242                 1, 5, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4);
1243         gtk_entry_set_width_chars(GTK_ENTRY(txt_source_url), 25);
1244
1245         /* Use GPS Location. */
1246         gtk_table_attach(GTK_TABLE(table),
1247                 oti.rad_use_gps = gtk_radio_button_new_with_label(NULL,
1248                     _("Use GPS Location")),
1249                 0, 2, 1, 2, GTK_FILL, 0, 2, 4);
1250
1251         /* Use End of Route. */
1252         gtk_table_attach(GTK_TABLE(table),
1253                oti.rad_use_route = gtk_radio_button_new_with_label_from_widget(
1254                    GTK_RADIO_BUTTON(oti.rad_use_gps), _("Use End of Route")),
1255                0, 2, 2, 3, GTK_FILL, 0, 2, 4);
1256
1257         gtk_table_attach(GTK_TABLE(table),
1258                 gtk_vseparator_new(),
1259                 2, 3, 1, 3, GTK_FILL, GTK_FILL, 2,4);
1260
1261         /* Auto. */
1262         gtk_table_attach(GTK_TABLE(table),
1263                 oti.chk_auto = gtk_check_button_new_with_label(
1264                     _("Auto-Update")),
1265                 3, 5, 1, 2, GTK_FILL, 0, 2, 4);
1266
1267         /* Avoid Highways. */
1268         gtk_table_attach(GTK_TABLE(table),
1269                 oti.chk_avoid_highways = gtk_check_button_new_with_label(
1270                     _("Avoid Highways")),
1271                 3, 5, 2, 3, GTK_FILL, 0, 2, 4);
1272
1273
1274         /* Origin. */
1275         gtk_table_attach(GTK_TABLE(table),
1276                 oti.rad_use_text = gtk_radio_button_new_with_label_from_widget(
1277                     GTK_RADIO_BUTTON(oti.rad_use_gps), _("Origin")),
1278                 0, 1, 3, 4, GTK_FILL, 0, 2, 4);
1279         gtk_table_attach(GTK_TABLE(table),
1280                 oti.txt_from = gtk_entry_new(),
1281                 1, 4, 3, 4, GTK_EXPAND | GTK_FILL, 0, 2, 4);
1282         gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_from), 25);
1283 #ifdef MAEMO_CHANGES
1284 #ifndef LEGACY
1285         g_object_set(G_OBJECT(oti.txt_from), "hildon-input-mode",
1286                                 HILDON_GTK_INPUT_MODE_FULL, NULL);
1287 #else
1288         g_object_set(G_OBJECT(oti.txt_from), HILDON_AUTOCAP, FALSE, NULL);
1289 #endif
1290 #endif
1291
1292         /* Destination. */
1293         gtk_table_attach(GTK_TABLE(table),
1294                 label = gtk_label_new(_("Destination")),
1295                 0, 1, 4, 5, GTK_FILL, 0, 2, 4);
1296         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1297         gtk_table_attach(GTK_TABLE(table),
1298                 oti.txt_to = gtk_entry_new(),
1299                 1, 4, 4, 5, GTK_EXPAND | GTK_FILL, 0, 2, 4);
1300         gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_to), 25);
1301 #ifdef MAEMO_CHANGES
1302 #ifndef LEGACY
1303         g_object_set(G_OBJECT(oti.txt_to), "hildon-input-mode",
1304                                 HILDON_GTK_INPUT_MODE_FULL, NULL);
1305 #else
1306         g_object_set(G_OBJECT(oti.txt_to), HILDON_AUTOCAP, FALSE, NULL);
1307 #endif
1308 #endif
1309
1310         /* Swap button. */
1311         gtk_table_attach(GTK_TABLE(table),
1312                 btn_swap = gtk_button_new_with_label("Swap"),
1313                 4, 5, 3, 5, GTK_FILL, GTK_FILL, 2, 4);
1314
1315         /* Set up auto-completion. */
1316         from_comp = gtk_entry_completion_new();
1317         gtk_entry_completion_set_model(from_comp, GTK_TREE_MODEL(_loc_model));
1318         gtk_entry_completion_set_text_column(from_comp, 0);
1319         gtk_entry_set_completion(GTK_ENTRY(oti.txt_from), from_comp);
1320
1321         to_comp = gtk_entry_completion_new();
1322         gtk_entry_completion_set_model(to_comp, GTK_TREE_MODEL(_loc_model));
1323         gtk_entry_completion_set_text_column(to_comp, 0);
1324         gtk_entry_set_completion(GTK_ENTRY(oti.txt_to), to_comp);
1325
1326
1327         g_signal_connect(G_OBJECT(oti.rad_use_gps), "toggled",
1328                           G_CALLBACK(origin_type_selected), &oti);
1329         g_signal_connect(G_OBJECT(oti.rad_use_route), "toggled",
1330                           G_CALLBACK(origin_type_selected), &oti);
1331         g_signal_connect(G_OBJECT(oti.rad_use_text), "toggled",
1332                           G_CALLBACK(origin_type_selected), &oti);
1333         g_signal_connect(G_OBJECT(btn_swap), "clicked",
1334                           G_CALLBACK(route_download_swap), &oti);
1335
1336         gtk_widget_set_sensitive(oti.chk_auto, FALSE);
1337     }
1338
1339     /* Initialize fields. */
1340
1341     gtk_entry_set_text(GTK_ENTRY(txt_source_url), _route_dl_url);
1342     if(to)
1343         gtk_entry_set_text(GTK_ENTRY(oti.txt_to), to);
1344
1345     /* Use "End of Route" by default if they have a route. */
1346     if(_route.head != _route.tail)
1347     {
1348         /* There is no route, so make it the default. */
1349         gtk_widget_set_sensitive(oti.rad_use_route, TRUE);
1350         gtk_toggle_button_set_active(
1351                 GTK_TOGGLE_BUTTON(oti.rad_use_route), TRUE);
1352         gtk_widget_grab_focus(oti.rad_use_route);
1353     }
1354     /* Else use "GPS Location" if they have GPS enabled. */
1355     else
1356     {
1357         /* There is no route, so desensitize "Use End of Route." */
1358         gtk_widget_set_sensitive(oti.rad_use_route, FALSE);
1359         if(_enable_gps)
1360         {
1361             gtk_toggle_button_set_active(
1362                     GTK_TOGGLE_BUTTON(oti.rad_use_gps), TRUE);
1363             gtk_widget_grab_focus(oti.rad_use_gps);
1364         }
1365         /* Else use text. */
1366         else
1367         {
1368             gtk_toggle_button_set_active(
1369                     GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
1370             gtk_widget_grab_focus(oti.txt_from);
1371         }
1372     }
1373
1374     gtk_widget_show_all(dialog);
1375
1376     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
1377     {
1378         gchar buffer[BUFFER_SIZE];
1379         const gchar *source_url, *from, *to;
1380         gboolean avoid_highways;
1381
1382         source_url = gtk_entry_get_text(GTK_ENTRY(txt_source_url));
1383         if(!strlen(source_url))
1384         {
1385             popup_error(dialog, _("Please specify a source URL."));
1386             continue;
1387         }
1388         else
1389         {
1390             g_free(_route_dl_url);
1391             _route_dl_url = g_strdup(source_url);
1392         }
1393
1394         if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti.rad_use_gps)))
1395         {
1396             gchar strlat[32];
1397             gchar strlon[32];
1398             g_ascii_formatd(strlat, 32, "%.06f", _gps.lat);
1399             g_ascii_formatd(strlon, 32, "%.06f", _gps.lon);
1400             snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
1401             from = buffer;
1402         }
1403         else if(gtk_toggle_button_get_active(
1404                     GTK_TOGGLE_BUTTON(oti.rad_use_route)))
1405         {
1406             gchar strlat[32];
1407             gchar strlon[32];
1408             Point *p;
1409             gdouble lat, lon;
1410
1411             /* Use last non-zero route point. */
1412             for(p = _route.tail; !p->unity; p--) { }
1413
1414             unit2latlon(p->unitx, p->unity, lat, lon);
1415             g_ascii_formatd(strlat, 32, "%.06f", lat);
1416             g_ascii_formatd(strlon, 32, "%.06f", lon);
1417             snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
1418             from = buffer;
1419         }
1420         else
1421         {
1422             from = gtk_entry_get_text(GTK_ENTRY(oti.txt_from));
1423         }
1424
1425         if(!strlen(from))
1426         {
1427             popup_error(dialog, _("Please specify a start location."));
1428             continue;
1429         }
1430
1431         to = gtk_entry_get_text(GTK_ENTRY(oti.txt_to));
1432         if(!strlen(to))
1433         {
1434             popup_error(dialog, _("Please specify an end location."));
1435             continue;
1436         }
1437
1438         avoid_highways = gtk_toggle_button_get_active(
1439                 GTK_TOGGLE_BUTTON(oti.chk_avoid_highways));
1440         if(route_download_and_setup(dialog, source_url, from, to,
1441                 gtk_toggle_button_get_active(
1442                     GTK_TOGGLE_BUTTON(oti.chk_avoid_highways)),
1443                 (gtk_toggle_button_get_active(
1444                     GTK_TOGGLE_BUTTON(oti.rad_use_gps)) ? 0 : 1)))
1445         {
1446             GtkTreeIter iter;
1447
1448             /* Cancel any autoroute that might be occurring. */
1449             cancel_autoroute();
1450
1451             if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti.chk_auto)))
1452             {
1453                 _autoroute_data.source_url = g_strdup(source_url);
1454                 _autoroute_data.dest = g_strdup(to);
1455                 _autoroute_data.avoid_highways = avoid_highways;
1456                 _autoroute_data.enabled = TRUE;
1457             }
1458
1459             /* Save Origin in Route Locations list if not from GPS. */
1460             if(gtk_toggle_button_get_active(
1461                         GTK_TOGGLE_BUTTON(oti.rad_use_text))
1462                 && !g_slist_find_custom(_loc_list, from,
1463                             (GCompareFunc)strcmp))
1464             {
1465                 _loc_list = g_slist_prepend(_loc_list, g_strdup(from));
1466                 gtk_list_store_insert_with_values(_loc_model, &iter,
1467                         INT_MAX, 0, from, -1);
1468             }
1469
1470             /* Save Destination in Route Locations list. */
1471             if(!g_slist_find_custom(_loc_list, to,
1472                         (GCompareFunc)strcmp))
1473             {
1474                 _loc_list = g_slist_prepend(_loc_list, g_strdup(to));
1475                 gtk_list_store_insert_with_values(_loc_model, &iter,
1476                         INT_MAX, 0, to, -1);
1477             }
1478
1479             /* Success! Get out of the while loop. */
1480             break;
1481         }
1482         /* else let them try again. */
1483     }
1484
1485     gtk_widget_hide(dialog); /* Destroying causes a crash (!?!?!??!) */
1486
1487     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1488     return TRUE;
1489 }
1490
1491 void
1492 route_add_way_dialog(gint unitx, gint unity)
1493 {
1494     gdouble lat, lon;
1495     gchar tmp1[LL_FMT_LEN], tmp2[LL_FMT_LEN], *p_latlon;
1496     static GtkWidget *dialog = NULL;
1497     static GtkWidget *table = NULL;
1498     static GtkWidget *label = NULL;
1499     static GtkWidget *label_lat_lon = NULL;
1500     static GtkWidget *txt_scroll = NULL;
1501     static GtkWidget *txt_desc = NULL;
1502     static int last_deg_format = 0;
1503     
1504     printf("%s()\n", __PRETTY_FUNCTION__);
1505
1506     unit2latlon(unitx, unity, lat, lon);
1507     
1508     gint fallback_deg_format = _degformat;
1509     
1510     if(!coord_system_check_lat_lon (lat, lon, &fallback_deg_format))
1511     {
1512         last_deg_format = _degformat;
1513         _degformat = fallback_deg_format;
1514         
1515         if(dialog != NULL) gtk_widget_destroy(dialog);
1516         dialog = NULL;
1517     }
1518     else if(_degformat != last_deg_format)
1519     {
1520         last_deg_format = _degformat;
1521         
1522                 if(dialog != NULL) gtk_widget_destroy(dialog);
1523         dialog = NULL;
1524     }
1525     
1526     
1527     if(dialog == NULL)
1528     {
1529         dialog = gtk_dialog_new_with_buttons(_("Add Waypoint"),
1530                 GTK_WINDOW(_window), GTK_DIALOG_MODAL,
1531                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1532                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1533                 NULL);
1534
1535         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1536                 table = gtk_table_new(2, 2, FALSE), TRUE, TRUE, 0);
1537
1538         
1539         
1540         if(DEG_FORMAT_ENUM_TEXT[_degformat].field_2_in_use)
1541                 p_latlon = g_strdup_printf("%s, %s",
1542                                 DEG_FORMAT_ENUM_TEXT[_degformat].short_field_1,
1543                                 DEG_FORMAT_ENUM_TEXT[_degformat].short_field_2);
1544         else
1545                 p_latlon = g_strdup_printf("%s", DEG_FORMAT_ENUM_TEXT[_degformat].short_field_1);
1546         
1547         gtk_table_attach(GTK_TABLE(table),
1548                 label = gtk_label_new(p_latlon),
1549                 0, 1, 0, 1, GTK_FILL, 0, 2, 4);
1550         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1551
1552         g_free(p_latlon);
1553         
1554         gtk_table_attach(GTK_TABLE(table),
1555                 label_lat_lon = gtk_label_new(_("") ),
1556                 1, 2, 0, 1, GTK_FILL, 0, 2, 4);
1557         gtk_misc_set_alignment(GTK_MISC(label_lat_lon), 0.0f, 0.5f);
1558         
1559
1560         gtk_table_attach(GTK_TABLE(table),
1561                 label = gtk_label_new(_("Description")),
1562                 0, 1, 1, 2, GTK_FILL, 0, 2, 4);
1563         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1564
1565         txt_scroll = gtk_scrolled_window_new(NULL, NULL);
1566         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scroll),
1567                                        GTK_SHADOW_IN);
1568         gtk_table_attach(GTK_TABLE(table),
1569                 txt_scroll,
1570                 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4);
1571
1572         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scroll),
1573                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1574
1575         txt_desc = gtk_text_view_new ();
1576         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(txt_desc), GTK_WRAP_WORD);
1577
1578         gtk_container_add(GTK_CONTAINER(txt_scroll), txt_desc);
1579         gtk_widget_set_size_request(GTK_WIDGET(txt_scroll), 400, 60);
1580     }
1581
1582     format_lat_lon(lat, lon, tmp1, tmp2);
1583     
1584     if(DEG_FORMAT_ENUM_TEXT[_degformat].field_2_in_use)
1585         p_latlon = g_strdup_printf("%s, %s", tmp1, tmp2);
1586     else
1587         p_latlon = g_strdup_printf("%s", tmp1);
1588     
1589     
1590     gtk_label_set_text(GTK_LABEL(label_lat_lon), p_latlon);
1591     g_free(p_latlon);
1592     
1593     gtk_text_buffer_set_text(
1594             gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt_desc)), "", 0);
1595
1596     gtk_widget_show_all(dialog);
1597
1598     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
1599     {
1600         GtkTextBuffer *tbuf;
1601         GtkTextIter ti1, ti2;
1602         gchar *desc;
1603
1604         tbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt_desc));
1605         gtk_text_buffer_get_iter_at_offset(tbuf, &ti1, 0);
1606         gtk_text_buffer_get_end_iter(tbuf, &ti2);
1607         desc = gtk_text_buffer_get_text(tbuf, &ti1, &ti2, TRUE);
1608
1609         if(*desc)
1610         {
1611             /* There's a description.  Add a waypoint. */
1612             MACRO_PATH_INCREMENT_TAIL(_route);
1613             _route.tail->unitx = unitx;
1614             _route.tail->unity = unity;
1615             _route.tail->time = 0;
1616             _route.tail->altitude = 0;
1617
1618             MACRO_PATH_INCREMENT_WTAIL(_route);
1619             _route.wtail->point = _route.tail;
1620             _route.wtail->desc
1621                 = gtk_text_buffer_get_text(tbuf, &ti1, &ti2, TRUE);
1622         }
1623         else
1624         {
1625             GtkWidget *confirm;
1626
1627             g_free(desc);
1628
1629             confirm = hildon_note_new_confirmation(GTK_WINDOW(dialog),
1630                     _("Creating a \"waypoint\" with no description actually "
1631                         "adds a break point.  Is that what you want?"));
1632
1633             if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
1634             {
1635                 /* There's no description.  Add a break by adding a (0, 0)
1636                  * point (if necessary), and then the ordinary route point. */
1637                 if(_route.tail->unity)
1638                 {
1639                     MACRO_PATH_INCREMENT_TAIL(_route);
1640                     *_route.tail = _point_null;
1641                 }
1642
1643                 MACRO_PATH_INCREMENT_TAIL(_route);
1644                 _route.tail->unitx = unitx;
1645                 _route.tail->unity = unity;
1646                 _route.tail->time = 0;
1647                 _route.tail->altitude = 0;
1648
1649
1650                 gtk_widget_destroy(confirm);
1651             }
1652             else
1653             {
1654                 gtk_widget_destroy(confirm);
1655                 continue;
1656             }
1657         }
1658
1659         route_find_nearest_point();
1660         map_render_paths();
1661         MACRO_QUEUE_DRAW_AREA();
1662         break;
1663     }
1664     gtk_widget_hide(dialog);
1665     
1666     _degformat = last_deg_format;
1667     
1668     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1669 }
1670
1671 WayPoint*
1672 path_get_next_way()
1673 {
1674     return _next_way;
1675 }
1676
1677 void
1678 path_init()
1679 {
1680     gchar *settings_dir;
1681     printf("%s()\n", __PRETTY_FUNCTION__);
1682
1683     /* Initialize settings_dir. */
1684     settings_dir = gnome_vfs_expand_initial_tilde(CONFIG_DIR_NAME);
1685     g_mkdir_with_parents(settings_dir, 0700);
1686
1687     /* Open path database. */
1688     {   
1689         gchar *path_db_file;
1690
1691         path_db_file = gnome_vfs_uri_make_full_from_relative(
1692                 settings_dir, CONFIG_PATH_DB_FILE);
1693
1694         if(!path_db_file || SQLITE_OK != sqlite3_open(path_db_file, &_path_db)
1695         /* Open worked. Now create tables, failing if they already exist. */
1696         || (sqlite3_exec(_path_db,
1697                     "create table route_path ("
1698                     "num integer primary key, "
1699                     "unitx integer, "
1700                     "unity integer, "
1701                     "time integer, "
1702                     "altitude integer)"
1703                     ";"
1704                     "create table route_way ("
1705                     "route_point primary key, "
1706                     "description text)"
1707                     ";"
1708                     "create table track_path ("
1709                     "num integer primary key, "
1710                     "unitx integer, "
1711                     "unity integer, "
1712                     "time integer, "
1713                     "altitude integer)"
1714                     ";"
1715                     "create table track_way ("
1716                     "track_point primary key, "
1717                     "description text)",
1718                     NULL, NULL, NULL), FALSE) /* !! Comma operator !! */
1719             /* Create prepared statements - failure here is bad! */
1720             || SQLITE_OK != sqlite3_prepare(_path_db,
1721                     "select unitx, unity, time, altitude, description "
1722                     "from route_path left join route_way on "
1723                     "route_path.num = route_way.route_point "
1724                     "order by route_path.num",
1725                     -1, &_route_stmt_select, NULL)
1726             || SQLITE_OK != sqlite3_prepare(_path_db,
1727                     "select unitx, unity, time, altitude, description "
1728                     "from track_path left join track_way on "
1729                     "track_path.num = track_way.track_point "
1730                     "order by track_path.num",
1731                     -1, &_track_stmt_select, NULL)
1732             || SQLITE_OK != sqlite3_prepare(_path_db,
1733                     "delete from route_path",
1734                     -1, &_route_stmt_delete_path, NULL)
1735             || SQLITE_OK != sqlite3_prepare(_path_db,
1736                     "delete from route_way",
1737                     -1, &_route_stmt_delete_way, NULL)
1738             || SQLITE_OK != sqlite3_prepare(_path_db,
1739                     "insert into route_path "
1740                     "(num, unitx, unity, time, altitude) "
1741                     "values (NULL, ?, ?, ?, ?)",
1742                     -1, &_route_stmt_insert_path, NULL)
1743             || SQLITE_OK != sqlite3_prepare(_path_db,
1744                     "insert into route_way (route_point, description) "
1745                     "values (?, ?)",
1746                     -1, &_route_stmt_insert_way, NULL)
1747             || SQLITE_OK != sqlite3_prepare(_path_db,
1748                     "delete from track_path",
1749                     -1, &_track_stmt_delete_path, NULL)
1750             || SQLITE_OK != sqlite3_prepare(_path_db,
1751                     "delete from track_way",
1752                     -1, &_track_stmt_delete_way, NULL)
1753             || SQLITE_OK != sqlite3_prepare(_path_db,
1754                     "insert into track_path "
1755                     "(num, unitx, unity, time, altitude) "
1756                     "values (NULL, ?, ?, ?, ?)",
1757                     -1, &_track_stmt_insert_path, NULL)
1758             || SQLITE_OK != sqlite3_prepare(_path_db,
1759                     "insert into track_way (track_point, description) "
1760                     "values (?, ?)",
1761                     -1, &_track_stmt_insert_way, NULL)
1762             || SQLITE_OK != sqlite3_prepare(_path_db, "begin transaction",
1763                     -1, &_path_stmt_trans_begin, NULL)
1764             || SQLITE_OK != sqlite3_prepare(_path_db, "commit transaction",
1765                     -1, &_path_stmt_trans_commit, NULL)
1766             || SQLITE_OK != sqlite3_prepare(_path_db, "rollback transaction",
1767                     -1, &_path_stmt_trans_rollback, NULL))
1768         {   
1769             gchar buffer[BUFFER_SIZE];
1770             snprintf(buffer, sizeof(buffer), "%s\n%s",
1771                     _("Failed to open path database. "
1772                         "Tracks and routes will not be saved."),
1773                     sqlite3_errmsg(_path_db));
1774             sqlite3_close(_path_db);
1775             _path_db = NULL;
1776             popup_error(_window, buffer);
1777         }
1778         else
1779         {   
1780             read_path_from_db(&_route, _route_stmt_select);
1781             read_path_from_db(&_track, _track_stmt_select);
1782             _track_index_last_saved = _track.tail - _track.head - 1;
1783         }
1784         g_free(path_db_file);
1785     }
1786
1787     g_free(settings_dir);
1788
1789     _last_spoken_phrase = g_strdup("");
1790
1791     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1792 }
1793
1794 void
1795 path_destroy()
1796 {
1797     printf("%s()\n", __PRETTY_FUNCTION__);
1798
1799     /* Save paths. */
1800     if(_track.tail->unity)
1801         track_insert_break(FALSE);
1802     path_update_track_in_db();
1803     path_save_route_to_db();
1804
1805     if(_path_db)
1806     {   
1807         sqlite3_close(_path_db);
1808         _path_db = NULL;
1809     }
1810
1811     MACRO_PATH_FREE(_track);
1812     MACRO_PATH_FREE(_route);
1813
1814     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1815 }