]> git.itanic.dy.fi Git - maemo-mapper/blob - src/poi.c
Added AGPS set/override, and fixed seg fault in Add POI.
[maemo-mapper] / src / poi.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 <string.h>
31 #include <math.h>
32
33 #ifndef LEGACY
34 #    include <hildon/hildon-help.h>
35 #    include <hildon/hildon-note.h>
36 #    include <hildon/hildon-file-chooser-dialog.h>
37 #    include <hildon/hildon-number-editor.h>
38 #    include <hildon/hildon-banner.h>
39 #else
40 #    include <osso-helplib.h>
41 #    include <hildon-widgets/hildon-note.h>
42 #    include <hildon-widgets/hildon-file-chooser-dialog.h>
43 #    include <hildon-widgets/hildon-number-editor.h>
44 #    include <hildon-widgets/hildon-banner.h>
45 #    include <hildon-widgets/hildon-input-mode-hint.h>
46 #endif
47
48 #include <sqlite3.h>
49
50 #include "types.h"
51 #include "data.h"
52 #include "defines.h"
53
54 #include "display.h"
55 #include "gdk-pixbuf-rotate.h"
56 #include "gpx.h"
57 #include "main.h"
58 #include "poi.h"
59 #include "util.h"
60
61 static sqlite3 *_poi_db = NULL;
62 static sqlite3_stmt *_stmt_browse_poi = NULL;
63 static sqlite3_stmt *_stmt_browsecat_poi = NULL;
64 static sqlite3_stmt *_stmt_select_poi = NULL;
65 static sqlite3_stmt *_stmt_select_nearest_poi = NULL;
66 static sqlite3_stmt *_stmt_insert_poi = NULL;
67 static sqlite3_stmt *_stmt_update_poi = NULL;
68 static sqlite3_stmt *_stmt_delete_poi = NULL;
69 static sqlite3_stmt *_stmt_delete_poi_by_catid = NULL;
70 static sqlite3_stmt *_stmt_nextlabel_poi = NULL;
71 static sqlite3_stmt *_stmt_select_cat = NULL;
72 static sqlite3_stmt *_stmt_insert_cat = NULL;
73 static sqlite3_stmt *_stmt_update_cat = NULL;
74 static sqlite3_stmt *_stmt_delete_cat = NULL;
75 static sqlite3_stmt *_stmt_toggle_cat = NULL;
76 static sqlite3_stmt *_stmt_selall_cat = NULL;
77
78 typedef struct _PoiListInfo PoiListInfo;
79 struct _PoiListInfo
80 {
81     GtkWidget *dialog;
82     GtkWidget *dialog2;
83     GtkTreeViewColumn *select_column;
84     GtkWidget *tree_view;
85     gboolean select_all;
86 };
87
88 typedef struct _OriginToggleInfo OriginToggleInfo;
89 struct _OriginToggleInfo {
90     GtkWidget *rad_use_gps;
91     GtkWidget *rad_use_route;
92     GtkWidget *rad_use_text;
93     GtkWidget *txt_origin;
94     GtkWidget *txt_query;
95 };
96
97 typedef struct _PoiCategoryEditInfo PoiCategoryEditInfo;
98 struct _PoiCategoryEditInfo
99 {
100     GtkWidget *dialog;
101     GtkWidget *cmb_category;
102     gint cat_id;
103     GtkWidget *tree_view;
104 };
105
106 /** Data used during action: add or edit category/poi **/
107 typedef struct _DeletePOI DeletePOI;
108 struct _DeletePOI {
109     GtkWidget *dialog;
110     gchar *txt_label;
111     gint id;
112     gboolean deleted;
113 };
114
115 void
116 poi_db_connect()
117 {
118     gchar buffer[100];
119     gchar **pszResult;
120     gint nRow, nColumn;
121     gchar *db_dirname = NULL;
122     printf("%s()\n", __PRETTY_FUNCTION__);
123
124     if(_poi_db)
125     {
126         sqlite3_close(_poi_db);
127         _poi_db = NULL;
128     }
129
130     if(!_poi_db_filename)
131     {
132         /* Do nothing. */
133     }
134     else if(NULL == (db_dirname = g_path_get_dirname(_poi_db_filename))
135             || (g_mkdir_with_parents(db_dirname, 0755), /* comma operator */
136                 (SQLITE_OK != (sqlite3_open(_poi_db_filename, &_poi_db)))))
137     {
138         gchar buffer2[BUFFER_SIZE];
139         snprintf(buffer2, sizeof(buffer2),
140                 "%s: %s", _("Error with POI database"),
141                 sqlite3_errmsg(_poi_db));
142         sqlite3_close(_poi_db);
143         _poi_db = NULL;
144         popup_error(_window, buffer2);
145     }
146     else if(SQLITE_OK != sqlite3_get_table(_poi_db,
147                 "select label from poi limit 1",
148                 &pszResult, &nRow, &nColumn, NULL))
149     {
150         gchar *create_sql = sqlite3_mprintf(
151                 "create table poi (poi_id integer PRIMARY KEY, lat real, "
152                 "lon real, label text, desc text, cat_id integer);"
153                 "create table category (cat_id integer PRIMARY KEY,"
154                 "label text, desc text, enabled integer);"
155                 /* Add some default categories... */
156                 "insert into category (label, desc, enabled) "
157                     "values ('%q', '%q', 1); "
158                 "insert into category (label, desc, enabled) "
159                     "values ('%q', '%q', 1); "
160                 "insert into category (label, desc, enabled) "
161                     "values ('%q', '%q', 1); "
162                 "insert into category (label, desc, enabled) "
163                     "values ('%q', '%q', 1); "
164                 "insert into category (label, desc, enabled) "
165                     "values ('%q', '%q', 1); "
166                 "insert into category (label, desc, enabled) "
167                     "values ('%q', '%q', 1); "
168                 "insert into category (label, desc, enabled) "
169                     "values ('%q', '%q', 1); "
170                 "insert into category (label, desc, enabled) "
171                     "values ('%q', '%q', 1); "
172                 "insert into category (label, desc, enabled) "
173                     "values ('%q', '%q', 1); "
174                 "insert into category (label, desc, enabled) "
175                     "values ('%q', '%q', 1); "
176                 "insert into category (label, desc, enabled) "
177                     "values ('%q', '%q', 1); ",
178                 _("Service Station"),
179                 _("Stations for purchasing fuel for vehicles."),
180                 _("Residence"),
181                 _("Houses, apartments, or other residences of import."),
182                 _("Restaurant"),
183                 _("Places to eat or drink."),
184                 _("Shopping/Services"),
185                 _("Places to shop or acquire services."),
186                 _("Recreation"),
187                 _("Indoor or Outdoor places to have fun."),
188                 _("Transportation"),
189                 _("Bus stops, airports, train stations, etc."),
190                 _("Lodging"),
191                 _("Places to stay temporarily or for the night."),
192                 _("School"),
193                 _("Elementary schools, college campuses, etc."),
194                 _("Business"),
195                 _("General places of business."),
196                 _("Landmark"),
197                 _("General landmarks."),
198                 _("Other"),
199                 _("Miscellaneous category for everything else."));
200
201         if(SQLITE_OK != sqlite3_exec(_poi_db, create_sql, NULL, NULL, NULL)
202                 && (SQLITE_OK != sqlite3_get_table(_poi_db,
203                         "select label from poi limit 1",
204                         &pszResult, &nRow, &nColumn, NULL)))
205         {
206             snprintf(buffer, sizeof(buffer), "%s:\n%s",
207                     _("Failed to open or create database"),
208                     sqlite3_errmsg(_poi_db));
209             sqlite3_close(_poi_db);
210             _poi_db = NULL;
211             popup_error(_window, buffer);
212         }
213     }
214     else
215         sqlite3_free_table(pszResult);
216
217     g_free(db_dirname);
218
219     if(_poi_db)
220     {
221         /* Prepare our SQL statements. */
222         /* browse poi */
223         sqlite3_prepare(_poi_db,
224                         "select p.poi_id, p.cat_id, p.lat, p.lon,"
225                         " p.label, p.desc, c.label"
226                         " from poi p inner join category c"
227                         "   on p.cat_id = c.cat_id"
228                         " where c.enabled = 1"
229                         " and p.label like $QUERY or p.desc like $QUERY"
230                         " order by (($LAT - p.lat) * ($LAT - p.lat) "
231                                  "+ ($LON - p.lon) * ($LON - p.lon)) DESC "
232                         " limit 100",
233                         -1, &_stmt_browse_poi, NULL);
234
235         /* browse poi by category */
236         sqlite3_prepare(_poi_db,
237                         "select p.poi_id, p.cat_id, p.lat, p.lon,"
238                         " p.label, p.desc, c.label"
239                         " from poi p inner join category c"
240                         "   on p.cat_id = c.cat_id"
241                         " where c.enabled = 1"
242                         " and p.cat_id = $CATID"
243                         " and ( p.label like $QUERY or p.desc like $QUERY )"
244                         " order by (($LAT - p.lat) * ($LAT - p.lat) "
245                                  "+ ($LON - p.lon) * ($LON - p.lon)) DESC"
246                         " limit 100",
247                         -1, &_stmt_browsecat_poi, NULL);
248
249         /* Prepare our SQL statements. */
250         /* select from poi */
251         sqlite3_prepare(_poi_db,
252                         "select p.lat, p.lon, p.poi_id, p.label, p.desc,"
253                         " p.cat_id, c.label, c.desc"
254                         " from poi p inner join category c"
255                         "    on p.cat_id = c.cat_id"
256                         " where c.enabled = 1"
257                         " and p.lat between ? and ? "
258                         " and p.lon between ? and ? ",
259                         -1, &_stmt_select_poi, NULL);
260
261         /* select nearest pois */
262         sqlite3_prepare(_poi_db,
263                         "select p.poi_id, p.cat_id, p.lat, p.lon,"
264                         " p.label, p.desc, c.label"
265                         " from poi p inner join category c"
266                         "   on p.cat_id = c.cat_id"
267                         " where c.enabled = 1"
268                         " order by (($LAT - p.lat) * ($LAT - p.lat) "
269                                  "+ ($LON - p.lon) * ($LON - p.lon)) limit 1",
270                         -1, &_stmt_select_nearest_poi, NULL);
271
272         /* insert poi */
273         sqlite3_prepare(_poi_db,
274                             "insert into poi (lat, lon, label, desc, cat_id)"
275                             " values (?, ?, ?, ?, ?)",
276                         -1, &_stmt_insert_poi, NULL);
277         /* update poi */
278         sqlite3_prepare(_poi_db,
279                             "update poi set lat = ?, lon = ?, "
280                             "label = ?, desc = ?, cat_id = ? where poi_id = ?",
281                         -1, &_stmt_update_poi, NULL);
282         /* delete from poi */
283         sqlite3_prepare(_poi_db,
284                         " delete from poi where poi_id = ?",
285                         -1, &_stmt_delete_poi, NULL);
286         /* delete from poi by cat_id */
287         sqlite3_prepare(_poi_db,
288                         "delete from poi where cat_id = ?",
289                         -1, &_stmt_delete_poi_by_catid, NULL);
290         /* get next poilabel */
291         sqlite3_prepare(_poi_db,
292                         "select ifnull(max(poi_id) + 1,1) from poi",
293                         -1, &_stmt_nextlabel_poi, NULL);
294
295         /* select from category */
296         sqlite3_prepare(_poi_db,
297                         "select c.label, c.desc, c.enabled"
298                         " from category c where c.cat_id = ?",
299                         -1, &_stmt_select_cat, NULL);
300         /* insert into category */
301         sqlite3_prepare(_poi_db,
302                         "insert into category (label, desc, enabled)"
303                         " values (?, ?, ?)",
304                         -1, &_stmt_insert_cat, NULL);
305         /* update category */
306         sqlite3_prepare(_poi_db,
307                         "update category set label = ?, desc = ?,"
308                         " enabled = ? where cat_id = ?",
309                         -1, &_stmt_update_cat, NULL);
310         /* delete from category */
311         sqlite3_prepare(_poi_db,
312                         "delete from category where cat_id = ?",
313                         -1, &_stmt_delete_cat, NULL);
314         /* enable category */
315         sqlite3_prepare(_poi_db,
316                         "update category set enabled = ?"
317                         " where cat_id = ?",
318                         -1, &_stmt_toggle_cat, NULL);
319         /* select all category */
320         sqlite3_prepare(_poi_db,
321                         "select c.cat_id, c.label, c.desc, c.enabled,"
322                         " count(p.poi_id)"
323                         " from category c"
324                         " left outer join poi p on c.cat_id = p.cat_id"
325                         " group by c.cat_id, c.label, c.desc, c.enabled "
326                         " order by c.label",
327                         -1, &_stmt_selall_cat, NULL);
328     }
329
330     _poi_enabled = _poi_db != NULL;
331
332     gtk_widget_set_sensitive(_menu_poi_item, _poi_enabled);
333     gtk_widget_set_sensitive(_cmenu_loc_add_poi_item, _poi_enabled);
334     gtk_widget_set_sensitive(_cmenu_loc_download_poi_item, _poi_enabled);
335     gtk_widget_set_sensitive(_cmenu_loc_browse_poi_item, _poi_enabled);
336     gtk_widget_set_sensitive(_cmenu_way_add_poi_item, _poi_enabled);
337     gtk_widget_set_sensitive(_cmenu_poi_submenu, _poi_enabled);
338
339     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
340 }
341
342 gboolean
343 get_nearest_poi(gint unitx, gint unity, PoiInfo *poi)
344 {
345     printf("%s(%d, %d)\n", __PRETTY_FUNCTION__, unitx, unity);
346     gboolean result;
347     gdouble lat, lon;
348     unit2latlon(unitx, unity, lat, lon);
349
350     if(SQLITE_OK == sqlite3_bind_double(_stmt_select_nearest_poi, 1, lat)
351     && SQLITE_OK == sqlite3_bind_double(_stmt_select_nearest_poi, 2, lon)
352         && SQLITE_ROW == sqlite3_step(_stmt_select_nearest_poi))
353     {
354         poi->poi_id = sqlite3_column_int(_stmt_select_nearest_poi, 0);
355         poi->cat_id = sqlite3_column_int(_stmt_select_nearest_poi, 1);
356         poi->lat = sqlite3_column_double(_stmt_select_nearest_poi, 2);
357         poi->lon = sqlite3_column_double(_stmt_select_nearest_poi, 3);
358         poi->label =g_strdup(sqlite3_column_text(_stmt_select_nearest_poi, 4));
359         poi->desc = g_strdup(sqlite3_column_text(_stmt_select_nearest_poi, 5));
360         poi->clabel=g_strdup(sqlite3_column_text(_stmt_select_nearest_poi, 6));
361         result = TRUE;
362     }
363     else
364         result = FALSE;
365     sqlite3_reset(_stmt_select_nearest_poi);
366     vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, result);
367     return result;
368 }
369
370 gboolean
371 select_poi(gint unitx, gint unity, PoiInfo *poi, gboolean quick)
372 {
373     gint x, y;
374     gdouble lat1, lon1, lat2, lon2;
375     static GtkWidget *dialog = NULL;
376     static GtkWidget *list = NULL;
377     static GtkWidget *sw = NULL;
378     static GtkTreeViewColumn *column = NULL;
379     static GtkCellRenderer *renderer = NULL;
380     GtkListStore *store = NULL;
381     GtkTreeIter iter;
382     gboolean selected = FALSE;
383     gchar tmp1[LL_FMT_LEN], tmp2[LL_FMT_LEN];
384     gint num_cats = 0;
385     printf("%s()\n", __PRETTY_FUNCTION__);
386
387     x = unitx - pixel2unit(3 * _draw_width);
388     y = unity + pixel2unit(3 * _draw_width);
389     unit2latlon(x, y, lat1, lon1);
390
391     x = unitx + pixel2unit(3 * _draw_width);
392     y = unity - pixel2unit(3 * _draw_width);
393     unit2latlon(x, y, lat2, lon2);
394
395     if(SQLITE_OK != sqlite3_bind_double(_stmt_select_poi, 1, lat1) ||
396           SQLITE_OK != sqlite3_bind_double(_stmt_select_poi, 2, lat2) ||
397           SQLITE_OK != sqlite3_bind_double(_stmt_select_poi, 3, lon1) ||
398           SQLITE_OK != sqlite3_bind_double(_stmt_select_poi, 4, lon2))
399     {
400         g_printerr("Failed to bind values for _stmt_select_poi\n");
401         return FALSE;
402     }
403
404     /* Initialize store. */
405     store = gtk_list_store_new(POI_NUM_COLUMNS,
406                                G_TYPE_BOOLEAN,/* Selected */
407                                G_TYPE_INT,    /* POI ID */
408                                G_TYPE_INT,    /* Category ID */
409                                G_TYPE_DOUBLE,  /* Latitude */
410                                G_TYPE_DOUBLE,  /* Longitude */
411                                G_TYPE_STRING, /* Lat/Lon */
412                                G_TYPE_FLOAT,  /* Bearing */
413                                G_TYPE_FLOAT,  /* Distance */
414                                G_TYPE_STRING, /* POI Label */
415                                G_TYPE_STRING, /* POI Desc. */
416                                G_TYPE_STRING);/* Category Label */
417
418     while(SQLITE_ROW == sqlite3_step(_stmt_select_poi))
419     {
420         gdouble lat, lon;
421         lat = sqlite3_column_double(_stmt_select_poi, 0);
422         lon = sqlite3_column_double(_stmt_select_poi, 1);
423         lat_format(lat, tmp1);
424         lon_format(lon, tmp2);
425         gtk_list_store_append(store, &iter);
426         gtk_list_store_set(store, &iter,
427                 POI_POIID, sqlite3_column_int(_stmt_select_poi, 2),
428                 POI_CATID, sqlite3_column_int(_stmt_select_poi, 5),
429                 POI_LAT, lat,
430                 POI_LON, lon,
431                 POI_LATLON, g_strdup_printf("%s, %s", tmp1, tmp2),
432                 POI_LABEL, sqlite3_column_text(_stmt_select_poi, 3),
433                 POI_DESC, sqlite3_column_text(_stmt_select_poi, 4),
434                 POI_CLABEL, sqlite3_column_text(_stmt_select_poi, 6),
435                 -1);
436         num_cats++;
437     }
438     sqlite3_reset(_stmt_select_poi);
439
440     switch(num_cats)
441     {
442         case 0:
443             g_object_unref(G_OBJECT(store));
444             if(!quick)
445             {
446                 MACRO_BANNER_SHOW_INFO(_window, _("No POIs found."));
447             }
448             return FALSE;
449             break;
450         case 1:
451             /* iter is still set to the most-recently added POI. */
452             gtk_tree_model_get(GTK_TREE_MODEL(store),
453                 &iter,
454                 POI_POIID, &(poi->poi_id),
455                 POI_CATID, &(poi->cat_id),
456                 POI_LAT, &(poi->lat),
457                 POI_LON, &(poi->lon),
458                 POI_LABEL, &(poi->label),
459                 POI_DESC, &(poi->desc),
460                 POI_CLABEL, &(poi->clabel),
461                 -1);
462             g_object_unref(G_OBJECT(store));
463             return TRUE;
464             break;
465         default:
466             if(quick)
467             {
468                 g_object_unref(G_OBJECT(store));
469                 return get_nearest_poi(unitx, unity, poi);
470             }
471     }
472
473     /* There are at least 2 matching POI's - let the user select one. */
474     if(dialog == NULL)
475     {
476         dialog = gtk_dialog_new_with_buttons(_("Select POI"),
477                 GTK_WINDOW(_window), GTK_DIALOG_MODAL,
478                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
479                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
480                 NULL);
481
482         gtk_window_set_default_size(GTK_WINDOW(dialog), 500, 300);
483
484         sw = gtk_scrolled_window_new (NULL, NULL);
485         gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
486                 GTK_SHADOW_ETCHED_IN);
487         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
488                 GTK_POLICY_NEVER,
489                 GTK_POLICY_AUTOMATIC);
490         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
491                 sw, TRUE, TRUE, 0);
492
493         list = gtk_tree_view_new();
494         gtk_container_add(GTK_CONTAINER(sw), list);
495
496         gtk_tree_selection_set_mode(
497                 gtk_tree_view_get_selection(GTK_TREE_VIEW(list)),
498                 GTK_SELECTION_SINGLE);
499         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(list), TRUE);
500
501         renderer = gtk_cell_renderer_text_new();
502         column = gtk_tree_view_column_new_with_attributes(
503                 _("Location"), renderer, "text", POI_LATLON, NULL);
504         gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
505
506         renderer = gtk_cell_renderer_text_new();
507         column = gtk_tree_view_column_new_with_attributes(
508                 _("Label"), renderer, "text", POI_LABEL, NULL);
509         gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
510
511         renderer = gtk_cell_renderer_text_new();
512         column = gtk_tree_view_column_new_with_attributes(
513                 _("Category"), renderer, "text", POI_CLABEL, NULL);
514         gtk_tree_view_append_column(GTK_TREE_VIEW(list), column);
515     }
516
517     gtk_tree_view_set_model(GTK_TREE_VIEW(list), GTK_TREE_MODEL(store));
518     g_object_unref(G_OBJECT(store));
519
520     gtk_widget_show_all(dialog);
521
522     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
523     {
524         if(gtk_tree_selection_get_selected(
525                     gtk_tree_view_get_selection(GTK_TREE_VIEW(list)),
526                     NULL, &iter))
527         {
528             gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
529                 POI_POIID, &(poi->poi_id),
530                 POI_CATID, &(poi->cat_id),
531                 POI_LAT, &(poi->lat),
532                 POI_LON, &(poi->lon),
533                 POI_LABEL, &(poi->label),
534                 POI_DESC, &(poi->desc),
535                 POI_CLABEL, &(poi->clabel),
536                 -1);
537             selected = TRUE;
538             break;
539         }
540         else
541             popup_error(dialog, _("Select one POI from the list."));
542     }
543
544     map_force_redraw();
545
546     gtk_widget_hide(dialog);
547
548     vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, selected);
549     return selected;
550 }
551
552 static gboolean
553 category_delete(GtkWidget *widget, DeletePOI *dpoi)
554 {
555     GtkWidget *confirm;
556     gint i;
557     gchar *buffer;
558     printf("%s()\n", __PRETTY_FUNCTION__);
559
560     buffer = g_strdup_printf("%s\n\t%s\n%s",
561             _("Delete category?"),
562             dpoi->txt_label,
563             _("WARNING: All POIs in that category will also be deleted!"));
564     confirm = hildon_note_new_confirmation(GTK_WINDOW(dpoi->dialog), buffer);
565     g_free(buffer);
566     i = gtk_dialog_run(GTK_DIALOG(confirm));
567     gtk_widget_destroy(GTK_WIDGET(confirm));
568
569     if(i == GTK_RESPONSE_OK)
570     {
571         /* delete dpoi->poi_id */
572         if(SQLITE_OK != sqlite3_bind_int(_stmt_delete_poi_by_catid, 1,
573                     dpoi->id) ||
574            SQLITE_DONE != sqlite3_step(_stmt_delete_poi_by_catid))
575         {
576             MACRO_BANNER_SHOW_INFO(dpoi->dialog, _("Error deleting POI"));
577             sqlite3_reset(_stmt_delete_poi_by_catid);
578             return FALSE;
579         }
580         sqlite3_reset(_stmt_delete_poi_by_catid);
581
582         if(SQLITE_OK != sqlite3_bind_int(_stmt_delete_cat, 1, dpoi->id) ||
583            SQLITE_DONE != sqlite3_step(_stmt_delete_cat))
584         {
585             MACRO_BANNER_SHOW_INFO(dpoi->dialog, _("Error deleting category"));
586             sqlite3_reset(_stmt_delete_cat);
587             return FALSE;
588         }
589         sqlite3_reset(_stmt_delete_cat);
590
591         map_force_redraw();
592     }
593     gtk_widget_destroy(confirm);
594
595     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
596     return TRUE;
597 }
598
599 gboolean
600 category_edit_dialog(GtkWidget *parent, gint cat_id)
601 {
602     gchar *cat_label = NULL, *cat_desc = NULL;
603     gint cat_enabled;
604     GtkWidget *dialog;
605     GtkWidget *table;
606     GtkWidget *label;
607     GtkWidget *txt_label;
608     GtkWidget *txt_desc;
609     GtkWidget *btn_delete = NULL;
610     GtkWidget *txt_scroll;
611     GtkWidget *chk_enabled;
612     GtkTextBuffer *desc_txt;
613     GtkTextIter begin, end;
614     gboolean results = TRUE;
615     DeletePOI dpoi = {NULL, NULL, 0};
616     printf("%s()\n", __PRETTY_FUNCTION__);
617
618     if(cat_id > 0)
619     {
620         if(SQLITE_OK != sqlite3_bind_double(_stmt_select_cat, 1, cat_id) ||
621            SQLITE_ROW != sqlite3_step(_stmt_select_cat))
622         {
623             vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
624             sqlite3_reset(_stmt_select_cat);
625             return FALSE;
626         }
627
628         cat_label = g_strdup(sqlite3_column_text(_stmt_select_cat, 0));
629         cat_desc = g_strdup(sqlite3_column_text(_stmt_select_cat, 1));
630         cat_enabled = sqlite3_column_int(_stmt_select_cat, 2);
631
632         sqlite3_reset(_stmt_select_cat);
633
634         dialog = gtk_dialog_new_with_buttons(_("Edit Category"),
635             GTK_WINDOW(parent), GTK_DIALOG_MODAL,
636             GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
637             NULL);
638
639         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
640                 btn_delete = gtk_button_new_with_label(_("Delete...")));
641
642         dpoi.dialog = dialog;
643         dpoi.txt_label = g_strdup(cat_label);
644         dpoi.id = cat_id;
645         dpoi.deleted = FALSE;
646
647         g_signal_connect(G_OBJECT(btn_delete), "clicked",
648                           G_CALLBACK(category_delete), &dpoi);
649
650         gtk_dialog_add_button(GTK_DIALOG(dialog),
651                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT);
652     }
653     else
654     {
655         cat_enabled = 1;
656         cat_label = g_strdup("");
657         cat_id = -1;
658         cat_desc = g_strdup("");
659
660         dialog = gtk_dialog_new_with_buttons(_("Add Category"),
661             GTK_WINDOW(parent), GTK_DIALOG_MODAL,
662             GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
663             GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
664             NULL);
665     }
666
667     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
668             table = gtk_table_new(6, 4, FALSE), TRUE, TRUE, 0);
669
670     gtk_table_attach(GTK_TABLE(table),
671             label = gtk_label_new(_("Label")),
672             0, 1, 0, 1, GTK_FILL, 0, 2, 4);
673     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
674     gtk_table_attach(GTK_TABLE(table),
675             txt_label = gtk_entry_new(),
676             1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4);
677
678     gtk_table_attach(GTK_TABLE(table),
679             label = gtk_label_new(_("Description")),
680             0, 1, 1, 2, GTK_FILL, 0, 2, 4);
681     gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
682
683     txt_scroll = gtk_scrolled_window_new(NULL, NULL);
684     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scroll),
685                                    GTK_SHADOW_IN);
686     gtk_table_attach(GTK_TABLE(table),
687             txt_scroll,
688             1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4);
689
690     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scroll),
691                                  GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
692
693     txt_desc = gtk_text_view_new();
694     gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(txt_desc), GTK_WRAP_WORD);
695
696     gtk_container_add(GTK_CONTAINER(txt_scroll), txt_desc);
697     gtk_widget_set_size_request(GTK_WIDGET(txt_scroll), 400, 60);
698
699     desc_txt = gtk_text_view_get_buffer(GTK_TEXT_VIEW(txt_desc));
700
701     gtk_table_attach(GTK_TABLE(table),
702             chk_enabled = gtk_check_button_new_with_label(
703                 _("Enabled")),
704             0, 2, 2, 3, GTK_EXPAND | GTK_FILL, 0, 2, 4);
705
706     /* label */
707     gtk_entry_set_text(GTK_ENTRY(txt_label), cat_label);
708
709     /* desc */
710     gtk_text_buffer_set_text(desc_txt, cat_desc, -1);
711
712     /* enabled */
713     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(chk_enabled),
714             (cat_enabled == 1 ? TRUE : FALSE));
715
716     g_free(cat_label);
717     cat_label = NULL;
718     g_free(cat_desc);
719     cat_desc = NULL;
720
721     gtk_widget_show_all(dialog);
722
723     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
724     {
725         if(strlen(gtk_entry_get_text(GTK_ENTRY(txt_label))))
726             cat_label = g_strdup(gtk_entry_get_text(GTK_ENTRY(txt_label)));
727         else
728         {
729             popup_error(dialog, _("Please specify a name for the category."));
730             continue;
731         }
732
733         gtk_text_buffer_get_iter_at_offset(desc_txt, &begin,0 );
734         gtk_text_buffer_get_end_iter (desc_txt, &end);
735         cat_desc = gtk_text_buffer_get_text(desc_txt, &begin, &end, TRUE);
736
737         cat_enabled = (gtk_toggle_button_get_active(
738                 GTK_TOGGLE_BUTTON(chk_enabled)) ? 1 : 0);
739
740         if(cat_id > 0)
741         {
742             /* edit category */
743             if(SQLITE_OK != sqlite3_bind_text(_stmt_update_cat, 1, cat_label,
744                         -1, g_free) ||
745                SQLITE_OK != sqlite3_bind_text(_stmt_update_cat, 2, cat_desc,
746                         -1, g_free) ||
747                SQLITE_OK != sqlite3_bind_int(_stmt_update_cat, 3,cat_enabled)||
748                SQLITE_OK != sqlite3_bind_int(_stmt_update_cat, 4, cat_id) ||
749                SQLITE_DONE != sqlite3_step(_stmt_update_cat))
750             {
751                 MACRO_BANNER_SHOW_INFO(parent,_("Error updating category"));
752                 results = FALSE;
753             }
754             sqlite3_reset(_stmt_update_cat);
755         }
756         else
757         {
758             /* add category */
759             if(SQLITE_OK != sqlite3_bind_text(_stmt_insert_cat, 1, cat_label,
760                         -1, g_free) ||
761                SQLITE_OK != sqlite3_bind_text(_stmt_insert_cat, 2, cat_desc,
762                         -1, g_free) ||
763                SQLITE_OK != sqlite3_bind_int(_stmt_insert_cat, 3,cat_enabled)||
764                SQLITE_DONE != sqlite3_step(_stmt_insert_cat))
765             {
766                 MACRO_BANNER_SHOW_INFO(parent, _("Error adding category"));
767                 results = FALSE;
768             }
769             sqlite3_reset(_stmt_insert_cat);
770         }
771         break;
772     }
773
774     g_free(dpoi.txt_label);
775
776     g_object_unref (desc_txt);
777
778     if(results)
779         map_force_redraw();
780
781     gtk_widget_hide(dialog);
782
783     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
784     return results;
785 }
786
787 static void
788 category_toggled(GtkCellRendererToggle *cell, gchar *path, GtkListStore *data)
789 {
790     GtkTreeIter iter;
791     gboolean cat_enabled;
792     gint cat_id;
793     printf("%s()\n", __PRETTY_FUNCTION__);
794
795     GtkTreeModel *model = GTK_TREE_MODEL(data);
796     if( !gtk_tree_model_get_iter_from_string(model, &iter, path) )
797         return;
798
799     gtk_tree_model_get(model, &iter,
800             CAT_ENABLED, &cat_enabled,
801             CAT_ID, &cat_id,
802             -1);
803
804     cat_enabled ^= 1;
805
806     if(SQLITE_OK != sqlite3_bind_int(_stmt_toggle_cat, 1, cat_enabled) ||
807        SQLITE_OK != sqlite3_bind_int(_stmt_toggle_cat, 2, cat_id) ||
808        SQLITE_DONE != sqlite3_step(_stmt_toggle_cat))
809     {
810         MACRO_BANNER_SHOW_INFO(_window, _("Error updating Category"));
811     }
812     else
813     {
814         gtk_list_store_set(GTK_LIST_STORE(model), &iter,
815                    CAT_ENABLED, cat_enabled, -1);
816         map_force_redraw();
817     }
818
819     sqlite3_reset(_stmt_toggle_cat);
820
821     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
822 }
823
824 static GtkListStore*
825 generate_store()
826 {
827     GtkTreeIter iter;
828     GtkListStore *store;
829     printf("%s()\n", __PRETTY_FUNCTION__);
830
831     store = gtk_list_store_new(CAT_NUM_COLUMNS,
832                                G_TYPE_UINT,
833                                G_TYPE_BOOLEAN,
834                                G_TYPE_STRING,
835                                G_TYPE_STRING,
836                                G_TYPE_UINT);
837
838     while(SQLITE_ROW == sqlite3_step(_stmt_selall_cat))
839     {
840         gtk_list_store_append(store, &iter);
841         gtk_list_store_set(store, &iter,
842                 CAT_ID, sqlite3_column_int(_stmt_selall_cat, 0),
843                 CAT_ENABLED, sqlite3_column_int(_stmt_selall_cat, 3),
844                 CAT_LABEL, sqlite3_column_text(_stmt_selall_cat, 1),
845                 CAT_DESC, sqlite3_column_text(_stmt_selall_cat, 2),
846                 CAT_POI_CNT, sqlite3_column_int(_stmt_selall_cat, 4),
847                 -1);
848     }
849     sqlite3_reset(_stmt_selall_cat);
850
851     vprintf("%s(): return %p\n", __PRETTY_FUNCTION__, store);
852     return store;
853 }
854
855 static gboolean
856 category_add(GtkWidget *widget, PoiCategoryEditInfo *pcedit)
857 {
858     GtkListStore *store;
859     printf("%s()\n", __PRETTY_FUNCTION__);
860
861     if(category_edit_dialog(pcedit->dialog, 0))
862     {
863         store = generate_store();
864         gtk_tree_view_set_model(
865                 GTK_TREE_VIEW(pcedit->tree_view),
866                 GTK_TREE_MODEL(store));
867         g_object_unref(G_OBJECT(store));
868     }
869     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
870     return TRUE;
871 }
872
873 static gboolean
874 category_edit(GtkWidget *widget, PoiCategoryEditInfo *pcedit)
875 {
876     GtkTreeIter iter;
877     GtkTreeModel *store;
878     GtkTreeSelection *selection;
879     printf("%s()\n", __PRETTY_FUNCTION__);
880
881     store = gtk_tree_view_get_model(GTK_TREE_VIEW(pcedit->tree_view));
882     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pcedit->tree_view));
883     if(gtk_tree_selection_get_selected(selection, &store, &iter))
884     {
885         GValue val;
886         memset(&val, 0, sizeof(val));
887         gtk_tree_model_get_value(store, &iter, 0, &val);
888         if(category_edit_dialog(pcedit->dialog, g_value_get_uint(&val)))
889         {
890             GtkListStore *new_store = generate_store();
891             gtk_tree_view_set_model(
892                     GTK_TREE_VIEW(pcedit->tree_view),
893                     GTK_TREE_MODEL(new_store));
894             g_object_unref(G_OBJECT(new_store));
895         }
896     }
897     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
898     return TRUE;
899 }
900
901 gboolean
902 category_list_dialog(GtkWidget *parent)
903 {
904     static GtkWidget *dialog = NULL;
905     static GtkWidget *tree_view = NULL;
906     static GtkWidget *sw = NULL;
907     static GtkWidget *btn_edit = NULL;
908     static GtkWidget *btn_add = NULL;
909     static GtkTreeViewColumn *column = NULL;
910     static GtkCellRenderer *renderer = NULL;
911     static GtkListStore *store;
912     static PoiCategoryEditInfo pcedit;
913     printf("%s()\n", __PRETTY_FUNCTION__);
914
915     store = generate_store();
916
917     if(!store)
918         return TRUE;
919
920     dialog = gtk_dialog_new_with_buttons(_("POI Categories"),
921             GTK_WINDOW(parent), GTK_DIALOG_MODAL,
922             GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
923             NULL);
924
925     /* Enable the help button. */
926 #ifndef LEGACY
927     hildon_help_dialog_help_enable(
928 #else
929     ossohelp_dialog_help_enable(
930 #endif
931             GTK_DIALOG(dialog), HELP_ID_POICAT, _osso);
932
933     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
934             btn_edit = gtk_button_new_with_label(_("Edit...")));
935
936     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
937             btn_add = gtk_button_new_with_label(_("Add...")));
938
939     sw = gtk_scrolled_window_new(NULL, NULL);
940     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (sw),
941                   GTK_POLICY_NEVER,
942                   GTK_POLICY_AUTOMATIC);
943     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
944             sw, TRUE, TRUE, 0);
945
946     tree_view = gtk_tree_view_new();
947     /* Maemo-related? */
948     g_object_set(tree_view, "allow-checkbox-mode", FALSE, NULL);
949     gtk_container_add (GTK_CONTAINER (sw), tree_view);
950
951     gtk_tree_selection_set_mode(
952             gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)),
953             GTK_SELECTION_SINGLE);
954     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), TRUE);
955
956     renderer = gtk_cell_renderer_text_new();
957     column = gtk_tree_view_column_new_with_attributes(
958             _("ID"), renderer, "text", CAT_ID, NULL);
959     gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
960     gtk_tree_view_column_set_max_width (column, 1);
961
962     renderer = gtk_cell_renderer_toggle_new();
963     g_signal_connect (renderer, "toggled",
964             G_CALLBACK (category_toggled), store);
965     column = gtk_tree_view_column_new_with_attributes(
966             _("Enabled"), renderer, "active", CAT_ENABLED, NULL);
967     gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
968
969     renderer = gtk_cell_renderer_text_new();
970     column = gtk_tree_view_column_new_with_attributes(
971             _("Label"), renderer, "text", CAT_LABEL, NULL);
972     gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
973
974     renderer = gtk_cell_renderer_text_new();
975     column = gtk_tree_view_column_new_with_attributes(
976             _("Description"), renderer, "text", CAT_DESC, NULL);
977     gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
978
979     renderer = gtk_cell_renderer_text_new();
980     column = gtk_tree_view_column_new_with_attributes(
981             _("# POIs"), renderer, "text", CAT_POI_CNT, NULL);
982     gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
983
984     gtk_window_set_default_size(GTK_WINDOW(dialog), -1, 400);
985
986     pcedit.dialog = dialog;
987     pcedit.tree_view = tree_view;
988
989     g_signal_connect(G_OBJECT(btn_edit), "clicked",
990             G_CALLBACK(category_edit), &pcedit);
991
992     g_signal_connect(G_OBJECT(btn_add), "clicked",
993             G_CALLBACK(category_add), &pcedit);
994
995     gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view), GTK_TREE_MODEL(store));
996     g_object_unref(G_OBJECT(store));
997
998     gtk_widget_show_all(dialog);
999
1000     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
1001     {
1002         break;
1003     }
1004
1005     gtk_widget_destroy(dialog);
1006
1007     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1008     return TRUE;
1009 }
1010
1011 static gboolean
1012 poi_delete(GtkWidget *widget, DeletePOI *dpoi)
1013 {
1014     GtkWidget *confirm;
1015     gint i;
1016     gchar *buffer;
1017     printf("%s()\n", __PRETTY_FUNCTION__);
1018
1019     buffer = g_strdup_printf("%s\n%s", _("Delete POI?"), dpoi->txt_label);
1020     confirm = hildon_note_new_confirmation(GTK_WINDOW(dpoi->dialog), buffer);
1021     g_free(buffer);
1022     i = gtk_dialog_run(GTK_DIALOG(confirm));
1023     gtk_widget_destroy(GTK_WIDGET(confirm));
1024
1025     if(i == GTK_RESPONSE_OK)
1026     {
1027         if(SQLITE_OK != sqlite3_bind_int(_stmt_delete_poi, 1, dpoi->id) ||
1028            SQLITE_DONE != sqlite3_step(_stmt_delete_poi))
1029         {
1030             MACRO_BANNER_SHOW_INFO(dpoi->dialog, _("Error deleting POI"));
1031         }
1032         else
1033         {
1034             dpoi->deleted = TRUE;
1035             gtk_widget_hide(dpoi->dialog);
1036             map_force_redraw();
1037         }
1038         sqlite3_reset(_stmt_delete_poi);
1039     }
1040
1041     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1042     return TRUE;
1043 }
1044
1045 static gboolean
1046 poi_populate_categories(GtkListStore *store, gint cat_id,
1047         GtkTreeIter *out_active)
1048 {
1049     gboolean has_active = FALSE;
1050     printf("%s()\n", __PRETTY_FUNCTION__);
1051
1052     gtk_list_store_clear(store);
1053
1054     while(SQLITE_ROW == sqlite3_step(_stmt_selall_cat))
1055     {
1056         GtkTreeIter iter;
1057         gint cid = sqlite3_column_int(_stmt_selall_cat, 0);
1058         const gchar *clab = sqlite3_column_text(_stmt_selall_cat, 1);
1059
1060         gtk_list_store_append(store, &iter);
1061         gtk_list_store_set(store, &iter, 0, cid, 1, clab, -1);
1062
1063         if(cid == cat_id || !has_active)
1064         {
1065             if(out_active)
1066                 *out_active = iter;
1067             has_active = TRUE;
1068         }
1069     }
1070     sqlite3_reset(_stmt_selall_cat);
1071
1072     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1073     return has_active;
1074 }
1075
1076 static gboolean
1077 poi_edit_cat(GtkWidget *widget, PoiCategoryEditInfo *data)
1078 {
1079     printf("%s()\n", __PRETTY_FUNCTION__);
1080     if(category_list_dialog(data->dialog))
1081     {
1082         GtkTreeIter active;
1083         if(poi_populate_categories(GTK_LIST_STORE(gtk_combo_box_get_model(
1084                         GTK_COMBO_BOX(data->cmb_category))),
1085                 data->cat_id, &active))
1086         {
1087             gtk_combo_box_set_active_iter(
1088                     GTK_COMBO_BOX(data->cmb_category), &active);
1089         }
1090     }
1091     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1092     return TRUE;
1093 }
1094
1095 static GtkWidget*
1096 poi_create_cat_combo()
1097 {
1098     GtkWidget *cmb_category;
1099     GtkTreeModel *model;
1100     printf("%s()\n", __PRETTY_FUNCTION__);
1101
1102     model = GTK_TREE_MODEL(gtk_list_store_new(2,
1103                 G_TYPE_INT,      /* Category ID */
1104                 G_TYPE_STRING)); /* Category Label */
1105     cmb_category = gtk_combo_box_new_with_model(model);
1106     g_object_unref(model);
1107
1108     /* Set up the view for the combo box. */
1109     {
1110         GtkCellRenderer *renderer;
1111         GtkTreeIter active;
1112         renderer = gtk_cell_renderer_text_new();
1113         gtk_cell_layout_pack_start(
1114                 GTK_CELL_LAYOUT(cmb_category), renderer, TRUE);
1115         gtk_cell_layout_set_attributes(
1116                 GTK_CELL_LAYOUT(cmb_category), renderer, "text", 1, NULL);
1117
1118         poi_populate_categories(GTK_LIST_STORE(gtk_combo_box_get_model(
1119                         GTK_COMBO_BOX(cmb_category))), -1, &active);
1120     }
1121     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1122     return cmb_category;
1123 }
1124
1125 gboolean
1126 poi_add_dialog(GtkWidget *parent, gint unitx, gint unity)
1127 {
1128     static PoiInfo poi;
1129     static GtkWidget *dialog;
1130     static GtkWidget *table;
1131     static GtkWidget *label;
1132     static GtkWidget *txt_label;
1133     static GtkWidget *txt_lat;
1134     static GtkWidget *txt_lon;
1135     static GtkWidget *cmb_category;
1136     static GtkWidget *txt_desc;
1137     static GtkWidget *btn_catedit;
1138     static GtkWidget *hbox;
1139     static GtkWidget *txt_scroll;
1140     static GtkTextBuffer *desc_txt;
1141     static GtkTextIter begin, end;
1142     static DeletePOI dpoi = {NULL, NULL, 0};
1143     static PoiCategoryEditInfo pcedit;
1144     printf("%s()\n", __PRETTY_FUNCTION__);
1145
1146     if(dialog == NULL)
1147     {
1148         dialog = gtk_dialog_new_with_buttons(_("Add POI"),
1149             GTK_WINDOW(parent), GTK_DIALOG_MODAL,
1150             GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1151             GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1152             NULL);
1153
1154         /* Set the lat/lon strings. */
1155         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1156                 table = gtk_table_new(6, 4, FALSE), TRUE, TRUE, 0);
1157
1158         gtk_table_attach(GTK_TABLE(table),
1159                 label = gtk_label_new(_("Lat")),
1160                 0, 1, 0, 1, GTK_FILL, 0, 2, 0);
1161         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1162         gtk_table_attach(GTK_TABLE(table),
1163                 txt_lat = gtk_entry_new(),
1164                 1, 2, 0, 1, GTK_FILL, 0, 2, 0);
1165
1166         gtk_table_attach(GTK_TABLE(table),
1167                 label = gtk_label_new(_("Lon")),
1168                 2, 3, 0, 1, GTK_FILL, 0, 2, 0);
1169         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1170         gtk_table_attach(GTK_TABLE(table),
1171                 txt_lon = gtk_entry_new(),
1172                 3, 4, 0, 1, GTK_FILL, 0, 2, 0);
1173
1174         gtk_table_attach(GTK_TABLE(table),
1175                 label = gtk_label_new(_("Label")),
1176                 0, 1, 1, 2, GTK_FILL, 0, 2, 0);
1177         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1178         gtk_table_attach(GTK_TABLE(table),
1179                 txt_label = gtk_entry_new(),
1180                 1, 4, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 0);
1181
1182         gtk_table_attach(GTK_TABLE(table),
1183                 label = gtk_label_new(_("Category")),
1184                 0, 1, 3, 4, GTK_FILL, 0, 2, 0);
1185         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1186         gtk_table_attach(GTK_TABLE(table),
1187                 hbox = gtk_hbox_new(FALSE, 4),
1188                 1, 4, 3, 4, GTK_EXPAND | GTK_FILL, 0, 2, 0);
1189         gtk_box_pack_start(GTK_BOX(hbox),
1190                 cmb_category = poi_create_cat_combo(),
1191                 FALSE, FALSE, 0);
1192
1193         gtk_box_pack_start(GTK_BOX(hbox),
1194                 btn_catedit = gtk_button_new_with_label(
1195                     _("Edit Categories...")),
1196                 FALSE, FALSE, 0);
1197
1198         gtk_table_attach(GTK_TABLE(table),
1199                 label = gtk_label_new(_("Description")),
1200                 0, 1, 5, 6, GTK_FILL, GTK_FILL, 2, 0);
1201         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.0f);
1202
1203         txt_scroll = gtk_scrolled_window_new(NULL, NULL);
1204         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scroll),
1205                 GTK_SHADOW_IN);
1206         gtk_table_attach(GTK_TABLE(table),
1207                 txt_scroll,
1208                 1, 4, 5, 6, GTK_EXPAND | GTK_FILL, 0, 2, 0);
1209
1210         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scroll),
1211                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1212
1213         txt_desc = gtk_text_view_new ();
1214         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(txt_desc), GTK_WRAP_WORD);
1215
1216         gtk_container_add(GTK_CONTAINER(txt_scroll), txt_desc);
1217         gtk_widget_set_size_request(GTK_WIDGET(txt_scroll), 550, 120);
1218
1219         desc_txt = gtk_text_view_get_buffer(GTK_TEXT_VIEW (txt_desc));
1220
1221         g_signal_connect(G_OBJECT(btn_catedit), "clicked",
1222                 G_CALLBACK(poi_edit_cat), &pcedit);
1223     }
1224
1225     poi.poi_id = -1;
1226     poi.cat_id = -1;
1227     poi.clabel = NULL;
1228     poi.desc = g_strdup("");
1229     unit2latlon(unitx, unity, poi.lat, poi.lon);
1230
1231     /* Lat/Lon */
1232     {
1233         gchar tmp1[LL_FMT_LEN], tmp2[LL_FMT_LEN];
1234
1235         lat_format(poi.lat, tmp1);
1236         lon_format(poi.lon, tmp2);
1237
1238         gtk_entry_set_text(GTK_ENTRY(txt_lat), tmp1);
1239         gtk_entry_set_text(GTK_ENTRY(txt_lon), tmp2);
1240     }
1241
1242     /* Label */
1243     if(SQLITE_ROW == sqlite3_step(_stmt_nextlabel_poi))
1244         poi.label = g_strdup_printf("Point%06d",
1245                 sqlite3_column_int(_stmt_nextlabel_poi, 0));
1246     else
1247         poi.label = g_strdup("");
1248     sqlite3_reset(_stmt_nextlabel_poi);
1249     gtk_entry_set_text(GTK_ENTRY(txt_label), poi.label);
1250
1251     /* POI Desc. */
1252     gtk_text_buffer_set_text(desc_txt, "", -1);
1253
1254     /* Category. */
1255     {
1256         GtkTreeIter iter;
1257         gint cat_id = -1;
1258         gboolean had_cat_id = FALSE;
1259
1260         if(gtk_combo_box_get_active_iter(
1261                 GTK_COMBO_BOX(cmb_category), &iter))
1262         {
1263             gtk_tree_model_get(
1264                     gtk_combo_box_get_model(GTK_COMBO_BOX(cmb_category)),&iter,
1265                     0, &cat_id,
1266                     -1);
1267             had_cat_id = TRUE;
1268         }
1269         gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(
1270                         GTK_COMBO_BOX(cmb_category))));
1271         if(poi_populate_categories(GTK_LIST_STORE(gtk_combo_box_get_model(
1272                     GTK_COMBO_BOX(cmb_category))), cat_id, &iter)
1273                 && had_cat_id)
1274         {
1275             gtk_combo_box_set_active_iter(GTK_COMBO_BOX(cmb_category), &iter);
1276         }
1277     }
1278
1279     pcedit.dialog = dialog;
1280     pcedit.cmb_category = cmb_category;
1281     pcedit.cat_id = poi.cat_id;
1282
1283     gtk_widget_show_all(dialog);
1284
1285     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
1286     {
1287         GtkTreeIter iter;
1288         const gchar *text;
1289         gchar *error_check;
1290
1291         text = gtk_entry_get_text(GTK_ENTRY(txt_lat));
1292         poi.lat = strdmstod(text, &error_check);
1293         if(text == error_check || poi.lat < -90. || poi.lat > 90.) {
1294             popup_error(dialog, _("Invalid Latitude"));
1295             continue;
1296         }
1297
1298         text = gtk_entry_get_text(GTK_ENTRY(txt_lon));
1299         poi.lon = strdmstod(text, &error_check);
1300         if(text == error_check || poi.lon < -180. || poi.lon > 180.) {
1301             popup_error(dialog, _("Invalid Longitude"));
1302             continue;
1303         }
1304
1305         if(strlen(gtk_entry_get_text(GTK_ENTRY(txt_label))))
1306         {
1307             if(poi.label)
1308                 g_free(poi.label);
1309             poi.label = g_strdup(gtk_entry_get_text(GTK_ENTRY(txt_label)));
1310         }
1311         else
1312         {
1313             popup_error(dialog, _("Please specify a name."));
1314             continue;
1315         }
1316
1317         if(!gtk_combo_box_get_active_iter(
1318                 GTK_COMBO_BOX(cmb_category), &iter))
1319         {
1320             popup_error(dialog, _("Please specify a category."));
1321             continue;
1322         }
1323
1324         gtk_text_buffer_get_iter_at_offset(desc_txt, &begin,0 );
1325         gtk_text_buffer_get_end_iter (desc_txt, &end);
1326         if(poi.desc)
1327             g_free(poi.desc);
1328         poi.desc = gtk_text_buffer_get_text(desc_txt, &begin, &end, TRUE);
1329
1330         if(poi.clabel)
1331             g_free(poi.clabel);
1332         gtk_tree_model_get(
1333                 gtk_combo_box_get_model(GTK_COMBO_BOX(cmb_category)), &iter,
1334                 0, &poi.cat_id,
1335                 1, &poi.clabel,
1336                 -1);
1337
1338         /* add poi */
1339         if(SQLITE_OK != sqlite3_bind_double(_stmt_insert_poi, 1, poi.lat)
1340         || SQLITE_OK != sqlite3_bind_double(_stmt_insert_poi, 2, poi.lon)
1341         || SQLITE_OK != sqlite3_bind_text(_stmt_insert_poi, 3, poi.label,
1342                -1, SQLITE_STATIC)
1343         || SQLITE_OK != sqlite3_bind_text(_stmt_insert_poi, 4, poi.desc,
1344                -1, SQLITE_STATIC)
1345         || SQLITE_OK != sqlite3_bind_int(_stmt_insert_poi, 5, poi.cat_id)
1346         || SQLITE_DONE != sqlite3_step(_stmt_insert_poi))
1347         {
1348             MACRO_BANNER_SHOW_INFO(parent, _("Error adding POI"));
1349         }
1350
1351         sqlite3_reset(_stmt_insert_poi);
1352
1353         /* We're done. */
1354         break;
1355     }
1356
1357     g_free(poi.label);
1358     g_free(poi.desc);
1359     g_free(dpoi.txt_label);
1360
1361     map_force_redraw();
1362
1363     gtk_widget_hide(dialog);
1364
1365     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1366     return !dpoi.deleted;
1367 }
1368
1369 gboolean
1370 poi_view_dialog(GtkWidget *parent, PoiInfo *poi)
1371 {
1372     GtkTreeIter iter;
1373     static GtkWidget *dialog;
1374     static GtkWidget *table;
1375     static GtkWidget *label;
1376     static GtkWidget *txt_label;
1377     static GtkWidget *txt_lat;
1378     static GtkWidget *txt_lon;
1379     static GtkWidget *cmb_category;
1380     static GtkWidget *txt_desc;
1381     static GtkWidget *btn_delete = NULL;
1382     static GtkWidget *btn_catedit;
1383     static GtkWidget *hbox;
1384     static GtkWidget *txt_scroll;
1385     static GtkTextBuffer *desc_txt;
1386     static GtkTextIter begin, end;
1387     static DeletePOI dpoi = {NULL, NULL, 0};
1388     static PoiCategoryEditInfo pcedit;
1389     printf("%s()\n", __PRETTY_FUNCTION__);
1390
1391     if(dialog == NULL)
1392     {
1393         dialog = gtk_dialog_new_with_buttons(_("Edit POI"),
1394             GTK_WINDOW(parent), GTK_DIALOG_MODAL,
1395             GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1396             NULL);
1397
1398         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
1399                 btn_delete = gtk_button_new_with_label(_("Delete...")));
1400
1401         gtk_dialog_add_button(GTK_DIALOG(dialog),
1402                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT);
1403
1404         /* Set the lat/lon strings. */
1405         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1406                 table = gtk_table_new(6, 4, FALSE), TRUE, TRUE, 0);
1407
1408         gtk_table_attach(GTK_TABLE(table),
1409                 label = gtk_label_new(_("Lat")),
1410                 0, 1, 0, 1, GTK_FILL, 0, 2, 0);
1411         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1412         gtk_table_attach(GTK_TABLE(table),
1413                 txt_lat = gtk_entry_new(),
1414                 1, 2, 0, 1, GTK_FILL, 0, 2, 0);
1415
1416         gtk_table_attach(GTK_TABLE(table),
1417                 label = gtk_label_new(_("Lon")),
1418                 2, 3, 0, 1, GTK_FILL, 0, 2, 0);
1419         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1420         gtk_table_attach(GTK_TABLE(table),
1421                 txt_lon = gtk_entry_new(),
1422                 3, 4, 0, 1, GTK_FILL, 0, 2, 0);
1423
1424         gtk_table_attach(GTK_TABLE(table),
1425                 label = gtk_label_new(_("Label")),
1426                 0, 1, 1, 2, GTK_FILL, 0, 2, 0);
1427         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1428         gtk_table_attach(GTK_TABLE(table),
1429                 txt_label = gtk_entry_new(),
1430                 1, 4, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 0);
1431
1432         gtk_table_attach(GTK_TABLE(table),
1433                 label = gtk_label_new(_("Category")),
1434                 0, 1, 3, 4, GTK_FILL, 0, 2, 0);
1435         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1436         gtk_table_attach(GTK_TABLE(table),
1437                 hbox = gtk_hbox_new(FALSE, 4),
1438                 1, 4, 3, 4, GTK_EXPAND | GTK_FILL, 0, 2, 0);
1439         gtk_box_pack_start(GTK_BOX(hbox),
1440                 cmb_category = poi_create_cat_combo(),
1441                 FALSE, FALSE, 0);
1442
1443         gtk_box_pack_start(GTK_BOX(hbox),
1444                 btn_catedit = gtk_button_new_with_label(
1445                     _("Edit Categories...")),
1446                 FALSE, FALSE, 0);
1447
1448         gtk_table_attach(GTK_TABLE(table),
1449                 label = gtk_label_new(_("Description")),
1450                 0, 1, 5, 6, GTK_FILL, GTK_FILL, 2, 0);
1451         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.0f);
1452
1453         txt_scroll = gtk_scrolled_window_new(NULL, NULL);
1454         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scroll),
1455                 GTK_SHADOW_IN);
1456         gtk_table_attach(GTK_TABLE(table),
1457                 txt_scroll,
1458                 1, 4, 5, 6, GTK_EXPAND | GTK_FILL, 0, 2, 0);
1459
1460         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scroll),
1461                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1462
1463         txt_desc = gtk_text_view_new ();
1464         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(txt_desc), GTK_WRAP_WORD);
1465
1466         gtk_container_add(GTK_CONTAINER(txt_scroll), txt_desc);
1467         gtk_widget_set_size_request(GTK_WIDGET(txt_scroll), 550, 120);
1468
1469         desc_txt = gtk_text_view_get_buffer (GTK_TEXT_VIEW (txt_desc));
1470
1471         g_signal_connect(G_OBJECT(btn_delete), "clicked",
1472                           G_CALLBACK(poi_delete), &dpoi);
1473
1474         g_signal_connect(G_OBJECT(btn_catedit), "clicked",
1475                 G_CALLBACK(poi_edit_cat), &pcedit);
1476     }
1477
1478     dpoi.dialog = dialog;
1479     dpoi.txt_label = g_strdup(poi->label);
1480     dpoi.id = poi->poi_id;
1481     dpoi.deleted = FALSE;
1482
1483     /* Lat/Lon */
1484     {
1485         gchar tmp1[LL_FMT_LEN], tmp2[LL_FMT_LEN];
1486
1487         lat_format(poi->lat, tmp1);
1488         lon_format(poi->lon, tmp2);
1489
1490         gtk_entry_set_text(GTK_ENTRY(txt_lat), tmp1);
1491         gtk_entry_set_text(GTK_ENTRY(txt_lon), tmp2);
1492     }
1493
1494     /* label */
1495     gtk_entry_set_text(GTK_ENTRY(txt_label), poi->label);
1496
1497     /* poi_desc */
1498     gtk_text_buffer_set_text(desc_txt, poi->desc, -1);
1499
1500     /* Category. */
1501     gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(
1502                     GTK_COMBO_BOX(cmb_category))));
1503     if(poi_populate_categories(GTK_LIST_STORE(gtk_combo_box_get_model(
1504                 GTK_COMBO_BOX(cmb_category))), poi->cat_id, &iter))
1505         gtk_combo_box_set_active_iter(GTK_COMBO_BOX(cmb_category), &iter);
1506
1507     /* Connect Signals */
1508     pcedit.dialog = dialog;
1509     pcedit.cmb_category = cmb_category;
1510     pcedit.cat_id = poi->cat_id;
1511
1512     gtk_widget_show_all(dialog);
1513
1514     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
1515     {
1516         const gchar *text;
1517         gchar *error_check;
1518
1519         text = gtk_entry_get_text(GTK_ENTRY(txt_lat));
1520         poi->lat = strdmstod(text, &error_check);
1521         if(text == error_check || poi->lat < -90. || poi->lat > 90.) {
1522             popup_error(dialog, _("Invalid Latitude"));
1523             continue;
1524         }
1525
1526         text = gtk_entry_get_text(GTK_ENTRY(txt_lon));
1527         poi->lon = strdmstod(text, &error_check);
1528         if(text == error_check || poi->lon < -180. || poi->lon > 180.) {
1529             popup_error(dialog, _("Invalid Longitude"));
1530             continue;
1531         }
1532
1533         if(strlen(gtk_entry_get_text(GTK_ENTRY(txt_label))))
1534         {
1535             if(poi->label)
1536                 g_free(poi->label);
1537             poi->label = g_strdup(gtk_entry_get_text(GTK_ENTRY(txt_label)));
1538         }
1539         else
1540         {
1541             popup_error(dialog, _("Please specify a name."));
1542             continue;
1543         }
1544
1545         if(!gtk_combo_box_get_active_iter(
1546                 GTK_COMBO_BOX(cmb_category), &iter))
1547         {
1548             popup_error(dialog, _("Please specify a category."));
1549             continue;
1550         }
1551
1552         gtk_text_buffer_get_iter_at_offset(desc_txt, &begin,0 );
1553         gtk_text_buffer_get_end_iter (desc_txt, &end);
1554         if(poi->desc)
1555             g_free(poi->desc);
1556         poi->desc = gtk_text_buffer_get_text(desc_txt, &begin, &end, TRUE);
1557
1558         if(poi->clabel)
1559             g_free(poi->clabel);
1560         gtk_tree_model_get(
1561                 gtk_combo_box_get_model(GTK_COMBO_BOX(cmb_category)), &iter,
1562                 0, &poi->cat_id,
1563                 1, &poi->clabel,
1564                 -1);
1565
1566         /* edit poi */
1567         if(SQLITE_OK != sqlite3_bind_double(
1568                     _stmt_update_poi, 1, poi->lat) ||
1569            SQLITE_OK != sqlite3_bind_double(
1570                _stmt_update_poi, 2, poi->lon) ||
1571            SQLITE_OK != sqlite3_bind_text(_stmt_update_poi, 3, poi->label,
1572                     -1, SQLITE_STATIC) ||
1573            SQLITE_OK != sqlite3_bind_text(_stmt_update_poi, 4, poi->desc,
1574                -1, SQLITE_STATIC) ||
1575            SQLITE_OK != sqlite3_bind_int(
1576                _stmt_update_poi, 5, poi->cat_id) ||
1577            SQLITE_OK != sqlite3_bind_int(
1578                _stmt_update_poi, 6, poi->poi_id) ||
1579            SQLITE_DONE != sqlite3_step(_stmt_update_poi))
1580         {
1581             MACRO_BANNER_SHOW_INFO(parent, _("Error updating POI"));
1582         }
1583
1584         sqlite3_reset(_stmt_update_poi);
1585
1586         /* We're done. */
1587         break;
1588     }
1589
1590     g_free(dpoi.txt_label);
1591
1592     map_force_redraw();
1593
1594     gtk_widget_hide(dialog); /* Destroying causes a crash.... ??? */
1595
1596     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1597     return !dpoi.deleted;
1598 }
1599
1600 static gint
1601 poi_list_insert(GtkWidget *parent, GList *poi_list, GtkComboBox *cmb_category)
1602 {
1603     gint default_cat_id;
1604     gchar *default_cat_label;
1605     gint num_inserts = 0;
1606     GList *curr;
1607     GtkTreeIter iter;
1608     printf("%s()\n", __PRETTY_FUNCTION__);
1609
1610     /* Get defaults from the given GtkComboBox */
1611     if(!gtk_combo_box_get_active_iter(
1612             GTK_COMBO_BOX(cmb_category), &iter))
1613     {
1614         vprintf("%s(): return 0\n", __PRETTY_FUNCTION__);
1615         return 0;
1616     }
1617     gtk_tree_model_get(
1618             gtk_combo_box_get_model(GTK_COMBO_BOX(cmb_category)),
1619             &iter,
1620             0, &default_cat_id,
1621             1, &default_cat_label,
1622             -1);
1623
1624     /* Iterate through the data model and import as desired. */
1625     for(curr = poi_list; curr; )
1626     {
1627         PoiInfo *poi = curr->data;
1628         if(
1629         (    SQLITE_OK != sqlite3_bind_double(_stmt_insert_poi, 1, poi->lat)
1630           || SQLITE_OK != sqlite3_bind_double(_stmt_insert_poi, 2, poi->lon)
1631           || SQLITE_OK != sqlite3_bind_text(_stmt_insert_poi, 3, poi->label,
1632              -1, SQLITE_STATIC)
1633           || SQLITE_OK != sqlite3_bind_text(_stmt_insert_poi, 4, poi->desc,
1634              -1, SQLITE_STATIC)
1635           || SQLITE_OK != sqlite3_bind_int(_stmt_insert_poi, 5,
1636               poi->cat_id = default_cat_id)
1637           || SQLITE_DONE != sqlite3_step(_stmt_insert_poi)
1638         ))
1639         {
1640             /* Failure. */
1641             GList *tmp = curr->next;
1642             if(poi->label)
1643                 g_free(poi->label);
1644             if(poi->desc)
1645                 g_free(poi->desc);
1646             g_slice_free(PoiInfo, poi);
1647             poi_list = g_list_delete_link(poi_list, curr);
1648             curr = tmp;
1649         }
1650         else
1651         {
1652             /* Success. */
1653             ++num_inserts;
1654             if(default_cat_label)
1655                 poi->clabel = g_strdup(default_cat_label);
1656             poi->poi_id = sqlite3_last_insert_rowid(_poi_db);
1657             curr = curr->next;
1658         }
1659         sqlite3_reset(_stmt_insert_poi);
1660     }
1661
1662     if(num_inserts)
1663     {
1664         gchar buffer[BUFFER_SIZE];
1665         map_force_redraw();
1666         snprintf(buffer, sizeof(buffer), "%d %s", num_inserts,
1667            _("POIs were added to the POI database.  The following screen will "
1668                "allow you to modify or delete any of the new POIs."));
1669         popup_error(parent, buffer);
1670     }
1671     else
1672     {
1673         popup_error(parent, _("No POIs were found."));
1674     }
1675
1676     if(default_cat_label)
1677         g_free(default_cat_label);
1678
1679     vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, num_inserts);
1680     return num_inserts;
1681 }
1682
1683 static void
1684 poi_list_free(GList *poi_list)
1685 {
1686     GList *curr;
1687     printf("%s()\n", __PRETTY_FUNCTION__);
1688
1689     for(curr = poi_list; curr; curr = curr->next)
1690     {
1691         PoiInfo *poi_info = curr->data;
1692         if(poi_info)
1693         {
1694             if(poi_info->label)
1695                 g_free(poi_info->label);
1696             if(poi_info->desc)
1697                 g_free(poi_info->desc);
1698             if(poi_info->clabel)
1699                 g_free(poi_info->clabel);
1700             g_slice_free(PoiInfo, poi_info);
1701         }
1702     }
1703
1704     g_list_free(poi_list);
1705
1706     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1707 }
1708
1709 static void
1710 poi_list_bearing_cell_data_func(
1711         GtkTreeViewColumn *tree_column,
1712         GtkCellRenderer *cell,
1713         GtkTreeModel *tree_model,
1714         GtkTreeIter *iter)
1715 {
1716     gchar buffer[80];
1717     gfloat f;
1718     vprintf("%s()\n", __PRETTY_FUNCTION__);
1719
1720     gtk_tree_model_get(tree_model, iter, POI_BEARING, &f, -1);
1721     snprintf(buffer, sizeof(buffer), "%.1f", f);
1722     g_object_set(cell, "text", buffer, NULL);
1723
1724     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1725 }
1726
1727 static void
1728 poi_list_distance_cell_data_func(
1729         GtkTreeViewColumn *tree_column,
1730         GtkCellRenderer *cell,
1731         GtkTreeModel *tree_model,
1732         GtkTreeIter *iter)
1733 {
1734     gchar buffer[80];
1735     gfloat f;
1736     vprintf("%s()\n", __PRETTY_FUNCTION__);
1737
1738     gtk_tree_model_get(tree_model, iter, POI_DISTANCE, &f, -1);
1739     snprintf(buffer, sizeof(buffer), "%.2f", f);
1740     g_object_set(cell, "text", buffer, NULL);
1741
1742     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1743 }
1744
1745 static gboolean
1746 poi_list_row_selected(GtkCellRendererToggle *renderer,
1747         gchar *path_string, GtkTreeModel *tree_model)
1748 {
1749     GtkTreeIter iter;
1750     vprintf("%s()\n", __PRETTY_FUNCTION__);
1751
1752     if(gtk_tree_model_get_iter_from_string(tree_model, &iter, path_string))
1753     {
1754         gboolean old_value;
1755         gtk_tree_model_get(tree_model, &iter, POI_SELECTED, &old_value, -1);
1756         gtk_list_store_set(GTK_LIST_STORE(tree_model), &iter,
1757                 POI_SELECTED, !old_value,
1758                 -1);
1759     }
1760
1761     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1762     return TRUE;
1763 }
1764
1765 static gboolean
1766 poi_list_set_category(GtkWidget *widget, PoiListInfo *pli)
1767 {
1768     static GtkWidget *dialog = NULL;
1769     static GtkWidget *cmb_category = NULL;
1770     static GtkWidget *btn_catedit = NULL;
1771     static PoiCategoryEditInfo pcedit;
1772     printf("%s()\n", __PRETTY_FUNCTION__);
1773
1774     if(dialog == NULL)
1775     {
1776         GtkWidget *hbox;
1777         GtkWidget *label;
1778
1779         dialog = gtk_dialog_new_with_buttons(_("Set Category..."),
1780                 GTK_WINDOW(pli->dialog2), GTK_DIALOG_MODAL,
1781                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1782                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1783                 NULL);
1784
1785         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1786                 hbox = gtk_hbox_new(FALSE, 4), FALSE, FALSE, 4);
1787
1788         gtk_box_pack_start(GTK_BOX(hbox),
1789                 label = gtk_label_new(_("Category")),
1790                 FALSE, FALSE, 0);
1791
1792         gtk_box_pack_start(GTK_BOX(hbox),
1793                 cmb_category = poi_create_cat_combo(),
1794                 FALSE, FALSE, 4);
1795
1796         gtk_box_pack_start(GTK_BOX(hbox),
1797                 btn_catedit = gtk_button_new_with_label(
1798                     _("Edit Categories...")),
1799                 FALSE, FALSE, 0);
1800
1801         /* Connect Signals */
1802         pcedit.dialog = dialog;
1803         pcedit.cmb_category = cmb_category;
1804         pcedit.cat_id = -1;
1805         g_signal_connect(G_OBJECT(btn_catedit), "clicked",
1806                 G_CALLBACK(poi_edit_cat), &pcedit);
1807     }
1808
1809     gtk_widget_show_all(dialog);
1810
1811     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
1812     {
1813         GtkTreeIter iter;
1814         GtkListStore *store;
1815         gint cat_id;
1816         const gchar *cat_label;
1817
1818         /* Get the text of the chosen category. */
1819         if(!gtk_combo_box_get_active_iter(
1820                 GTK_COMBO_BOX(cmb_category), &iter))
1821         {
1822             popup_error(dialog, _("Please specify a category."));
1823             continue;
1824         }
1825
1826         gtk_tree_model_get(
1827                 gtk_combo_box_get_model(GTK_COMBO_BOX(cmb_category)),
1828                 &iter,
1829                 0, &cat_id,
1830                 1, &cat_label,
1831                 -1);
1832
1833         /* Iterate through the data store and categorize as desired. */
1834         store = GTK_LIST_STORE(gtk_tree_view_get_model(
1835                     GTK_TREE_VIEW(pli->tree_view)));
1836         if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) do
1837         {
1838             PoiInfo poi;
1839             gboolean selected;
1840
1841             memset(&poi, 0, sizeof(poi));
1842
1843             gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1844                     POI_SELECTED, &selected,
1845                     POI_POIID, &(poi.poi_id),
1846                     POI_LAT, &(poi.lat),
1847                     POI_LON, &(poi.lon),
1848                     POI_LABEL, &(poi.label),
1849                     POI_DESC, &(poi.desc),
1850                     -1);
1851
1852             if(selected)
1853             {
1854                 gtk_list_store_set(store, &iter,
1855                     POI_CATID, cat_id,
1856                     POI_CLABEL, cat_label,
1857                     -1);
1858                 /* edit poi */
1859                 if(SQLITE_OK != sqlite3_bind_double(
1860                             _stmt_update_poi, 1, poi.lat) ||
1861                    SQLITE_OK != sqlite3_bind_double(
1862                        _stmt_update_poi, 2, poi.lon) ||
1863                    SQLITE_OK != sqlite3_bind_text(_stmt_update_poi,
1864                        3, poi.label, -1, SQLITE_STATIC) ||
1865                    SQLITE_OK != sqlite3_bind_text(_stmt_update_poi,
1866                        4, poi.desc, -1, SQLITE_STATIC) ||
1867                    SQLITE_OK != sqlite3_bind_int(
1868                        _stmt_update_poi, 5, cat_id) ||
1869                    SQLITE_OK != sqlite3_bind_int(
1870                        _stmt_update_poi, 6, poi.poi_id) ||
1871                    SQLITE_DONE != sqlite3_step(_stmt_update_poi))
1872                 {
1873                     MACRO_BANNER_SHOW_INFO(pli->dialog2,
1874                             _("Error updating POI"));
1875                 }
1876                 sqlite3_reset(_stmt_update_poi);
1877             }
1878         } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
1879
1880         break;
1881     }
1882
1883     map_force_redraw();
1884     gtk_widget_hide(dialog);
1885
1886     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1887     return TRUE;
1888 }
1889
1890 static gboolean
1891 poi_list_select_all(GtkTreeViewColumn *column, PoiListInfo *pli)
1892 {
1893     GtkTreeIter iter;
1894     GtkListStore *store;
1895     printf("%s()\n", __PRETTY_FUNCTION__);
1896
1897     /* Iterate through the data store and select as desired. */
1898     store = GTK_LIST_STORE(gtk_tree_view_get_model(
1899                 GTK_TREE_VIEW(pli->tree_view)));
1900     if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) do
1901     {
1902         gtk_list_store_set(store, &iter,
1903             POI_SELECTED, pli->select_all,
1904             -1);
1905     } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
1906
1907     pli->select_all = !pli->select_all;
1908
1909     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1910     return TRUE;
1911 }
1912
1913 static gboolean
1914 poi_list_view(GtkWidget *widget, PoiListInfo *pli)
1915 {
1916     GtkTreeIter iter;
1917     GtkTreeSelection *selection;
1918     GtkListStore *store;
1919     printf("%s()\n", __PRETTY_FUNCTION__);
1920
1921     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pli->tree_view));
1922     store = GTK_LIST_STORE(gtk_tree_view_get_model(
1923                 GTK_TREE_VIEW(pli->tree_view)));
1924
1925     /* Iterate through the data store and import as desired. */
1926     if(gtk_tree_selection_get_selected(selection, NULL, &iter))
1927     {
1928         PoiInfo poi;
1929         memset(&poi, 0, sizeof(poi));
1930
1931         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1932                 POI_POIID, &(poi.poi_id),
1933                 POI_CATID, &(poi.cat_id),
1934                 POI_LAT, &(poi.lat),
1935                 POI_LON, &(poi.lon),
1936                 POI_LABEL, &(poi.label),
1937                 POI_DESC, &(poi.desc),
1938                 POI_CLABEL, &(poi.clabel),
1939                 -1);
1940
1941         if(poi_view_dialog(pli->dialog, &poi))
1942         {
1943             gtk_list_store_set(store, &iter,
1944                     POI_POIID, poi.poi_id,
1945                     POI_CATID, poi.cat_id,
1946                     POI_LAT, poi.lat,
1947                     POI_LON, poi.lon,
1948                     POI_LABEL, poi.label,
1949                     POI_DESC, poi.desc,
1950                     POI_CLABEL, poi.clabel,
1951                     -1);
1952         }
1953         else
1954         {
1955             /* POI was deleted. */
1956             gtk_list_store_remove(store, &iter);
1957         }
1958     }
1959
1960     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1961     return TRUE;
1962 }
1963
1964 static void
1965 poi_list_row_activated(GtkTreeView *tree_view, GtkTreePath *path,
1966         GtkTreeViewColumn *column, PoiListInfo *pli)
1967 {
1968     printf("%s()\n", __PRETTY_FUNCTION__);
1969
1970     if(column != pli->select_column)
1971         poi_list_view(GTK_WIDGET(tree_view), pli);
1972
1973     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1974 }
1975
1976 static gboolean
1977 poi_list_goto(GtkWidget *widget, PoiListInfo *pli)
1978 {
1979     GtkTreeIter iter;
1980     GtkTreeSelection *selection;
1981     GtkListStore *store;
1982     printf("%s()\n", __PRETTY_FUNCTION__);
1983
1984     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pli->tree_view));
1985     store = GTK_LIST_STORE(gtk_tree_view_get_model(
1986                 GTK_TREE_VIEW(pli->tree_view)));
1987
1988     /* Iterate through the data store and import as desired. */
1989     if(gtk_tree_selection_get_selected(selection, NULL, &iter))
1990     {
1991         gdouble lat, lon;
1992         Point unit;
1993
1994         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1995                 POI_LAT, &lat,
1996                 POI_LON, &lon,
1997                 -1);
1998
1999         latlon2unit(lat, lon, unit.unitx, unit.unity);
2000
2001         if(_center_mode > 0)
2002             gtk_check_menu_item_set_active(
2003                     GTK_CHECK_MENU_ITEM(_menu_view_ac_none_item), TRUE);
2004
2005         map_center_unit(unit);
2006     }
2007
2008     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2009     return TRUE;
2010 }
2011
2012 static gboolean
2013 poi_list_delete(GtkWidget *widget, PoiListInfo *pli)
2014 {
2015     GtkWidget *confirm;
2016     printf("%s()\n", __PRETTY_FUNCTION__);
2017
2018     confirm = hildon_note_new_confirmation(
2019             GTK_WINDOW(pli->dialog2), _("Delete selected POI?"));
2020
2021     if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
2022     {
2023         GtkTreeIter iter;
2024         GtkListStore *store;
2025         gboolean already_next;
2026         gboolean must_iterate;;
2027
2028         /* Iterate through the data store and import as desired. */
2029         store = GTK_LIST_STORE(gtk_tree_view_get_model(
2030                     GTK_TREE_VIEW(pli->tree_view)));
2031         if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) do
2032         {
2033             gboolean selected;
2034             must_iterate = TRUE;
2035             already_next = FALSE;
2036             gint poi_id;
2037             gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2038                 POI_SELECTED, &selected,
2039                 POI_POIID, &poi_id,
2040                 -1);
2041             if(selected)
2042             {
2043                 /* Delete POI. */
2044                 if(SQLITE_OK != sqlite3_bind_int(_stmt_delete_poi, 1, poi_id)
2045                 || SQLITE_DONE != sqlite3_step(_stmt_delete_poi))
2046                 {
2047                     MACRO_BANNER_SHOW_INFO(pli->dialog2,
2048                             _("Error deleting POI"));
2049                 }
2050                 else
2051                 {
2052                     already_next = gtk_list_store_remove(store, &iter);
2053                     must_iterate = FALSE;
2054                 }
2055                 sqlite3_reset(_stmt_delete_poi);
2056             }
2057         } while(already_next || (must_iterate
2058                 && gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)));
2059     }
2060
2061     map_force_redraw();
2062
2063     gtk_widget_destroy(confirm);
2064
2065     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2066     return TRUE;
2067 }
2068
2069 static gboolean
2070 poi_list_export_gpx(GtkWidget *widget, PoiListInfo *pli)
2071 {
2072     GnomeVFSHandle *handle;
2073     printf("%s()\n", __PRETTY_FUNCTION__);
2074
2075     if(display_open_file(GTK_WINDOW(pli->dialog2), NULL, &handle, NULL,
2076                 NULL, NULL, GTK_FILE_CHOOSER_ACTION_SAVE))
2077     {
2078         gint num_exported = gpx_poi_write(
2079                gtk_tree_view_get_model(GTK_TREE_VIEW(pli->tree_view)), handle);
2080         if(num_exported >= 0)
2081         {
2082             gchar buffer[80];
2083             snprintf(buffer, sizeof(buffer), "%d %s\n", num_exported,
2084                     _("POIs Exported"));
2085             MACRO_BANNER_SHOW_INFO(pli->dialog2, buffer);
2086         }
2087         else
2088             popup_error(pli->dialog2, _("Error writing GPX file."));
2089         gnome_vfs_close(handle);
2090     }
2091
2092     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2093     return TRUE;
2094 }
2095
2096 static gboolean
2097 poi_list_manage_checks(GtkWidget *widget, PoiListInfo *pli)
2098 {
2099     GtkWidget *btn_category;
2100     GtkWidget *btn_delete;
2101     GtkWidget *btn_export_gpx;
2102
2103     printf("%s()\n", __PRETTY_FUNCTION__);
2104
2105     pli->dialog2 = gtk_dialog_new_with_buttons(_("Checked POI Actions..."),
2106             GTK_WINDOW(pli->dialog), GTK_DIALOG_MODAL,
2107             NULL);
2108
2109     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(pli->dialog2)->vbox),
2110             gtk_label_new(_("Select an operation to perform\n"
2111                             "on the POIs that you checked\n"
2112                             "in the POI list.")),
2113                 FALSE, FALSE, 4);
2114
2115     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(pli->dialog2)->vbox),
2116             btn_category = gtk_button_new_with_label(_("Set Category...")),
2117                 FALSE, FALSE, 4);
2118
2119     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(pli->dialog2)->vbox),
2120             btn_delete = gtk_button_new_with_label(_("Delete...")),
2121                 FALSE, FALSE, 4);
2122
2123     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(pli->dialog2)->vbox),
2124             btn_export_gpx = gtk_button_new_with_label(
2125                 _("Export to GPX...")),
2126                 FALSE, FALSE, 4);
2127
2128     gtk_dialog_add_button(GTK_DIALOG(pli->dialog2),
2129             GTK_STOCK_CLOSE, GTK_RESPONSE_ACCEPT);
2130
2131     g_signal_connect(G_OBJECT(btn_category), "clicked",
2132             G_CALLBACK(poi_list_set_category), pli);
2133
2134     g_signal_connect(G_OBJECT(btn_delete), "clicked",
2135             G_CALLBACK(poi_list_delete), pli);
2136
2137     g_signal_connect(G_OBJECT(btn_export_gpx), "clicked",
2138             G_CALLBACK(poi_list_export_gpx), pli);
2139
2140     gtk_widget_show_all(pli->dialog2);
2141
2142     gtk_dialog_run(GTK_DIALOG(pli->dialog2));
2143
2144     gtk_widget_destroy(pli->dialog2);
2145     pli->dialog2 = NULL;
2146
2147     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2148     return TRUE;
2149 }
2150
2151 static gboolean
2152 poi_list_dialog(GtkWidget *parent, gint unitx, gint unity, GList *poi_list)
2153 {
2154     static PoiListInfo pli = { NULL, NULL };
2155     static GtkWidget *scroller;
2156     static GtkWidget *btn_goto;
2157     static GtkWidget *btn_edit;
2158     static GtkWidget *btn_manage_checks;
2159     static GtkListStore *store;
2160     GtkTreeIter iter;
2161     GList *curr;
2162     gdouble src_lat, src_lon;
2163     printf("%s()\n", __PRETTY_FUNCTION__);
2164
2165     if(pli.dialog == NULL)
2166     {
2167         GtkCellRenderer *renderer;
2168         GtkTreeViewColumn *column;
2169
2170         pli.dialog = gtk_dialog_new_with_buttons(_("POI List"),
2171             GTK_WINDOW(parent), GTK_DIALOG_MODAL,
2172                 NULL);
2173
2174         store = gtk_list_store_new(POI_NUM_COLUMNS,
2175                                    G_TYPE_BOOLEAN,/* Selected */
2176                                    G_TYPE_INT,    /* POI ID */
2177                                    G_TYPE_INT,    /* Category ID */
2178                                    G_TYPE_DOUBLE, /* Latitude */
2179                                    G_TYPE_DOUBLE, /* Longitude */
2180                                    G_TYPE_STRING, /* Lat/Lon */
2181                                    G_TYPE_FLOAT,  /* Bearing */
2182                                    G_TYPE_FLOAT,  /* Distance */
2183                                    G_TYPE_STRING, /* POI Label */
2184                                    G_TYPE_STRING, /* POI Desc. */
2185                                    G_TYPE_STRING);/* Category Label */
2186
2187         /* Set up the tree view. */
2188         pli.tree_view = gtk_tree_view_new();
2189         g_object_set(G_OBJECT(pli.tree_view),
2190                 "allow-checkbox-mode", FALSE, NULL);
2191
2192         gtk_tree_selection_set_mode(
2193                 gtk_tree_view_get_selection(GTK_TREE_VIEW(pli.tree_view)),
2194                 GTK_SELECTION_SINGLE);
2195         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(pli.tree_view), TRUE);
2196
2197         renderer = gtk_cell_renderer_toggle_new();
2198         gtk_cell_renderer_toggle_set_active(GTK_CELL_RENDERER_TOGGLE(renderer),
2199                 TRUE);
2200         g_signal_connect(G_OBJECT(renderer), "toggled",
2201                 G_CALLBACK(poi_list_row_selected), store);
2202         pli.select_column = gtk_tree_view_column_new_with_attributes(
2203                 "*", renderer, "active", POI_SELECTED, NULL);
2204         gtk_tree_view_append_column(GTK_TREE_VIEW(pli.tree_view),
2205                 pli.select_column);
2206         gtk_tree_view_column_set_clickable(pli.select_column, TRUE);
2207         g_signal_connect(G_OBJECT(pli.select_column), "clicked",
2208                 G_CALLBACK(poi_list_select_all), &pli);
2209
2210         renderer = gtk_cell_renderer_combo_new();
2211         column = gtk_tree_view_column_new_with_attributes(
2212                 _("Category"), renderer, "text", POI_CLABEL, NULL);
2213         gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_GROW_ONLY);
2214         gtk_tree_view_column_set_sort_column_id(column, POI_CLABEL);
2215         gtk_tree_view_append_column(GTK_TREE_VIEW(pli.tree_view), column);
2216
2217         renderer = gtk_cell_renderer_text_new();
2218         g_object_set(renderer, "xalign", 1.f, NULL);
2219         column = gtk_tree_view_column_new_with_attributes(
2220                 _("Dist."), renderer, "text", POI_DISTANCE, NULL);
2221         gtk_tree_view_column_set_cell_data_func(column, renderer,
2222                 (GtkTreeCellDataFunc)poi_list_distance_cell_data_func,
2223                 NULL, NULL);
2224         gtk_tree_view_column_set_sort_column_id(column, POI_DISTANCE);
2225         gtk_tree_view_append_column(GTK_TREE_VIEW(pli.tree_view), column);
2226
2227         renderer = gtk_cell_renderer_text_new();
2228         g_object_set(renderer, "xalign", 1.f, NULL);
2229         column = gtk_tree_view_column_new_with_attributes(
2230                 _("Bear."), renderer, "text", POI_BEARING, NULL);
2231         gtk_tree_view_column_set_cell_data_func(column, renderer,
2232                 (GtkTreeCellDataFunc)poi_list_bearing_cell_data_func,
2233                 NULL, NULL);
2234         gtk_tree_view_column_set_sort_column_id(column, POI_BEARING);
2235         gtk_tree_view_append_column(GTK_TREE_VIEW(pli.tree_view), column);
2236
2237         renderer = gtk_cell_renderer_text_new();
2238         column = gtk_tree_view_column_new_with_attributes(
2239                 _("Label"), renderer, "text", POI_LABEL, NULL);
2240         gtk_tree_view_column_set_sort_column_id(column, POI_LABEL);
2241         gtk_tree_view_append_column(GTK_TREE_VIEW(pli.tree_view), column);
2242
2243         g_signal_connect(G_OBJECT(pli.tree_view), "row-activated",
2244                 G_CALLBACK(poi_list_row_activated), &pli);
2245
2246         gtk_tree_view_set_model(GTK_TREE_VIEW(pli.tree_view),
2247                 GTK_TREE_MODEL(store));
2248         g_object_unref(G_OBJECT(store));
2249
2250         /* Enable the help button. */
2251 #ifndef LEGACY
2252         hildon_help_dialog_help_enable(
2253 #else
2254         ossohelp_dialog_help_enable(
2255 #endif
2256                 GTK_DIALOG(pli.dialog), HELP_ID_POILIST, _osso);
2257
2258         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(pli.dialog)->action_area),
2259                 btn_goto = gtk_button_new_with_label(_("Go to")));
2260
2261         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(pli.dialog)->action_area),
2262                 btn_edit = gtk_button_new_with_label(_("Edit...")));
2263
2264         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(pli.dialog)->action_area),
2265                 btn_manage_checks = gtk_button_new_with_label(
2266                     _("Checked POI Actions...")));
2267
2268         gtk_dialog_add_button(GTK_DIALOG(pli.dialog),
2269                 GTK_STOCK_CLOSE, GTK_RESPONSE_ACCEPT);
2270
2271         gtk_window_set_default_size(GTK_WINDOW(pli.dialog), 500, 400);
2272
2273         scroller = gtk_scrolled_window_new (NULL, NULL);
2274         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroller),
2275                 GTK_SHADOW_ETCHED_IN);
2276         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
2277                 GTK_POLICY_NEVER,
2278                 GTK_POLICY_AUTOMATIC);
2279         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(pli.dialog)->vbox),
2280                 scroller, TRUE, TRUE, 0);
2281
2282         gtk_container_add(GTK_CONTAINER(scroller), pli.tree_view);
2283
2284         g_signal_connect(G_OBJECT(btn_goto), "clicked",
2285                 G_CALLBACK(poi_list_goto), &pli);
2286
2287         g_signal_connect(G_OBJECT(btn_edit), "clicked",
2288                 G_CALLBACK(poi_list_view), &pli);
2289
2290         g_signal_connect(G_OBJECT(btn_manage_checks), "clicked",
2291                 G_CALLBACK(poi_list_manage_checks), &pli);
2292     }
2293
2294     /* Initialize the tree store. */
2295
2296     gtk_list_store_clear(store);
2297     pli.select_all = FALSE;
2298
2299     unit2latlon(unitx, unity, src_lat, src_lon);
2300
2301     for(curr = poi_list; curr; curr = curr->next)
2302     {
2303         PoiInfo *poi_info = curr->data;
2304         gchar tmp1[LL_FMT_LEN], tmp2[LL_FMT_LEN];
2305
2306         printf("poi: (%f, %f, %s, %s)\n",
2307                 poi_info->lat, poi_info->lon,
2308                 poi_info->label, poi_info->desc);
2309
2310         lat_format(poi_info->lat, tmp1);
2311         lon_format(poi_info->lon, tmp2);
2312
2313         gtk_list_store_append(store, &iter);
2314         gtk_list_store_set(store, &iter,
2315                 POI_SELECTED, TRUE,
2316                 POI_POIID, poi_info->poi_id,
2317                 POI_LAT, poi_info->lat,
2318                 POI_LON, poi_info->lon,
2319                 POI_BEARING, calculate_bearing(src_lat, src_lon,
2320                     poi_info->lat, poi_info->lon),
2321                 POI_DISTANCE, calculate_distance(src_lat,src_lon,
2322                     poi_info->lat, poi_info->lon) * UNITS_CONVERT[_units],
2323                 POI_LABEL, poi_info->label,
2324                 POI_DESC, poi_info->desc,
2325                 POI_CATID, poi_info->cat_id,
2326                 POI_CLABEL, poi_info->clabel,
2327                 -1);
2328     }
2329
2330     gtk_widget_show_all(pli.dialog);
2331
2332     GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(pli.dialog));
2333
2334     map_force_redraw();
2335
2336     gtk_widget_hide(pli.dialog);
2337
2338     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2339     return TRUE;
2340 }
2341
2342 gboolean
2343 poi_import_dialog(gint unitx, gint unity)
2344 {
2345     GtkWidget *dialog = NULL;
2346     gboolean success = FALSE;
2347     printf("%s()\n", __PRETTY_FUNCTION__);
2348
2349     dialog = hildon_file_chooser_dialog_new(GTK_WINDOW(_window),
2350             GTK_FILE_CHOOSER_ACTION_OPEN);
2351
2352     gtk_widget_show_all(dialog);
2353
2354     while(!success && gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
2355     {
2356         gchar *file_uri_str = NULL;
2357         gchar *bytes = NULL;
2358         gint size;
2359         GnomeVFSResult vfs_result;
2360         GList *poi_list = NULL;
2361
2362         file_uri_str = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
2363
2364         /* Parse the given file as GPX. */
2365         if(GNOME_VFS_OK != (vfs_result = gnome_vfs_read_entire_file(
2366                         file_uri_str, &size, &bytes)))
2367         {
2368             popup_error(dialog, gnome_vfs_result_to_string(vfs_result));
2369         }
2370         else if(gpx_poi_parse(bytes, size, &poi_list))
2371         {
2372             static GtkWidget *cat_dialog = NULL;
2373             static GtkWidget *cmb_category = NULL;
2374             static GtkWidget *btn_catedit = NULL;
2375             static PoiCategoryEditInfo pcedit;
2376
2377             if(!cat_dialog)
2378             {
2379                 GtkWidget *hbox;
2380                 GtkWidget *label;
2381                 cat_dialog = gtk_dialog_new_with_buttons(_("Default Category"),
2382                         GTK_WINDOW(dialog), GTK_DIALOG_MODAL,
2383                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2384                         GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2385                         NULL);
2386
2387                 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cat_dialog)->vbox),
2388                         hbox = gtk_hbox_new(FALSE, 4), FALSE, FALSE, 4);
2389
2390                 gtk_box_pack_start(GTK_BOX(hbox),
2391                         label = gtk_label_new(_("Category")),
2392                         FALSE, FALSE, 0);
2393
2394                 gtk_box_pack_start(GTK_BOX(hbox),
2395                         cmb_category = poi_create_cat_combo(),
2396                         FALSE, FALSE, 4);
2397
2398                 gtk_box_pack_start(GTK_BOX(hbox),
2399                         btn_catedit = gtk_button_new_with_label(
2400                             _("Edit Categories...")),
2401                         FALSE, FALSE, 0);
2402
2403                 /* Connect Signals */
2404                 pcedit.dialog = dialog;
2405                 pcedit.cmb_category = cmb_category;
2406                 pcedit.cat_id = -1;
2407                 g_signal_connect(G_OBJECT(btn_catedit), "clicked",
2408                         G_CALLBACK(poi_edit_cat), &pcedit);
2409             }
2410
2411             gtk_widget_show_all(cat_dialog);
2412
2413             while(GTK_RESPONSE_ACCEPT ==gtk_dialog_run(GTK_DIALOG(cat_dialog)))
2414             {
2415                 if(gtk_combo_box_get_active(GTK_COMBO_BOX(cmb_category)) == -1)
2416                 {
2417                     popup_error(dialog,
2418                             _("Please specify a default category."));
2419                     continue;
2420                 }
2421
2422                 /* Insert the POIs into the database. */
2423                 gint num_inserts = poi_list_insert(dialog,
2424                         poi_list, GTK_COMBO_BOX(cmb_category));
2425
2426                 if(num_inserts)
2427                 {
2428                     /* Hide the dialogs. */
2429                     gtk_widget_hide(cat_dialog);
2430
2431                     /* Create a new dialog with the results. */
2432                     poi_list_dialog(dialog, unitx, unity, poi_list);
2433                     success = TRUE;
2434                 }
2435                 break;
2436             }
2437
2438             gtk_widget_hide(cat_dialog);
2439
2440             poi_list_free(poi_list);
2441         }
2442         else
2443             popup_error(dialog, _("Error parsing GPX file."));
2444
2445         g_free(file_uri_str);
2446         g_free(bytes);
2447     }
2448
2449     /* Hide the dialog. */
2450     gtk_widget_destroy(dialog);
2451
2452     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2453     return success;
2454 }
2455
2456 static gboolean
2457 poi_download_cat_selected(GtkComboBox *cmb_category, GtkEntry *txt_query)
2458 {
2459     GtkTreeIter iter;
2460     printf("%s()\n", __PRETTY_FUNCTION__);
2461
2462     if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(cmb_category), &iter))
2463     {
2464         gchar buffer[BUFFER_SIZE];
2465         GtkWidget *confirm = NULL;
2466         gchar *category = NULL;
2467
2468         gtk_tree_model_get(
2469                 gtk_combo_box_get_model(GTK_COMBO_BOX(cmb_category)), &iter,
2470                 1, &category,
2471                 -1);
2472
2473         if(*gtk_entry_get_text(txt_query))
2474         {
2475             snprintf(buffer, sizeof(buffer), "%s\n  %s",
2476                     _("Overwrite query with the following text?"), category);
2477             confirm = hildon_note_new_confirmation(GTK_WINDOW(_window),buffer);
2478
2479         }
2480
2481         if(confirm == NULL
2482                 || GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
2483             gtk_entry_set_text(txt_query, category);
2484
2485         if(confirm)
2486             gtk_widget_destroy(confirm);
2487     }
2488
2489     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2490     return TRUE;
2491 }
2492
2493
2494 static gboolean
2495 origin_type_selected(GtkWidget *toggle, OriginToggleInfo *oti)
2496 {
2497     printf("%s()\n", __PRETTY_FUNCTION__);
2498
2499     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
2500         gtk_widget_set_sensitive(oti->txt_origin, toggle == oti->rad_use_text);
2501
2502     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2503     return TRUE;
2504 }
2505
2506 gboolean
2507 poi_download_dialog(gint unitx, gint unity)
2508 {
2509     static GtkWidget *dialog = NULL;
2510     static GtkWidget *hbox = NULL;
2511     static GtkWidget *table = NULL;
2512     static GtkWidget *table2 = NULL;
2513     static GtkWidget *label = NULL;
2514     static GtkWidget *num_page = NULL;
2515     static GtkWidget *txt_source_url = NULL;
2516     static OriginToggleInfo oti;
2517     static GtkWidget *cmb_category;
2518     printf("%s()\n", __PRETTY_FUNCTION__);
2519
2520     conic_recommend_connected();
2521
2522     if(!dialog)
2523     {
2524         GtkEntryCompletion *origin_comp;
2525
2526         dialog = gtk_dialog_new_with_buttons(_("Download POIs"),
2527                 GTK_WINDOW(_window), GTK_DIALOG_MODAL,
2528                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2529                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2530                 NULL);
2531
2532         /* Enable the help button. */
2533 #ifndef LEGACY
2534         hildon_help_dialog_help_enable(
2535 #else
2536         ossohelp_dialog_help_enable(
2537 #endif
2538                 GTK_DIALOG(dialog), HELP_ID_DOWNPOI, _osso);
2539
2540         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2541                 table = gtk_table_new(4, 4, FALSE), TRUE, TRUE, 0);
2542
2543         /* Source URL. */
2544         gtk_table_attach(GTK_TABLE(table),
2545                 hbox = gtk_hbox_new(FALSE, 4),
2546                 0, 4, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4);
2547         gtk_box_pack_start(GTK_BOX(hbox),
2548                 label = gtk_label_new(_("Source URL")), FALSE, TRUE, 4);
2549         gtk_box_pack_start(GTK_BOX(hbox),
2550                 txt_source_url = gtk_entry_new(), TRUE, TRUE, 4);
2551
2552         /* Auto. */
2553         gtk_table_attach(GTK_TABLE(table),
2554                 oti.rad_use_gps = gtk_radio_button_new_with_label(NULL,
2555                     _("Use GPS Location")),
2556                 0, 1, 1, 2, GTK_FILL, 0, 2, 4);
2557
2558         /* Use End of Route. */
2559         gtk_table_attach(GTK_TABLE(table),
2560                oti.rad_use_route = gtk_radio_button_new_with_label_from_widget(
2561                    GTK_RADIO_BUTTON(oti.rad_use_gps), _("Use End of Route")),
2562                0, 1, 2, 3, GTK_FILL, 0, 2, 4);
2563
2564
2565         gtk_table_attach(GTK_TABLE(table),
2566                 gtk_vseparator_new(),
2567                 1, 2, 1, 3, GTK_FILL, GTK_FILL, 2,4);
2568
2569         /* Category. */
2570         gtk_table_attach(GTK_TABLE(table),
2571                 label = gtk_label_new(_("Category")),
2572                 2, 3, 1, 2, GTK_FILL, 0, 2, 4);
2573         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2574         gtk_table_attach(GTK_TABLE(table),
2575                 cmb_category = poi_create_cat_combo(),
2576                 3, 4, 1, 2, GTK_FILL, 0, 2, 4);
2577
2578         /* Page. */
2579         gtk_table_attach(GTK_TABLE(table),
2580                 label = gtk_label_new(_("Page")),
2581                 2, 3, 2, 3, GTK_FILL, 0, 2, 4);
2582         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2583         gtk_table_attach(GTK_TABLE(table),
2584                 num_page = hildon_number_editor_new(1, 999),
2585                 3, 4, 2, 3, GTK_FILL, 0, 2, 4);
2586
2587
2588         /* Another table for the Origin and Query. */
2589         gtk_table_attach(GTK_TABLE(table),
2590                 table2 = gtk_table_new(2, 2, FALSE),
2591                 0, 4, 3, 4, GTK_EXPAND | GTK_FILL, 0, 2, 4);
2592
2593         /* Origin. */
2594         gtk_table_attach(GTK_TABLE(table2),
2595                 oti.rad_use_text = gtk_radio_button_new_with_label_from_widget(
2596                     GTK_RADIO_BUTTON(oti.rad_use_gps), _("Origin")),
2597                 0, 1, 0, 1, GTK_FILL, 0, 2, 4);
2598         gtk_table_attach(GTK_TABLE(table2),
2599                 oti.txt_origin = gtk_entry_new(),
2600                 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4);
2601         gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_origin), 25);
2602 #ifdef MAEMO_CHANGES
2603 #ifndef LEGACY
2604         g_object_set(G_OBJECT(oti.txt_origin), "hildon-input-mode",
2605                 HILDON_GTK_INPUT_MODE_FULL, NULL);
2606 #else
2607         g_object_set(G_OBJECT(oti.txt_origin), HILDON_AUTOCAP, FALSE, NULL);
2608 #endif
2609 #endif
2610
2611         /* Query. */
2612         gtk_table_attach(GTK_TABLE(table2),
2613                 label = gtk_label_new(_("Query")),
2614                 0, 1, 1, 2, GTK_FILL, 0, 2, 4);
2615         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2616         gtk_table_attach(GTK_TABLE(table2),
2617                 oti.txt_query = gtk_entry_new(),
2618                 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4);
2619         gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_query), 25);
2620 #ifdef MAEMO_CHANGES
2621 #ifndef LEGACY
2622         g_object_set(G_OBJECT(oti.txt_query), "hildon-input-mode",
2623                 HILDON_GTK_INPUT_MODE_FULL, NULL);
2624 #else
2625         g_object_set(G_OBJECT(oti.txt_query), HILDON_AUTOCAP, FALSE, NULL);
2626 #endif
2627 #endif
2628
2629         /* Set up auto-completion. */
2630         origin_comp = gtk_entry_completion_new();
2631         gtk_entry_completion_set_model(origin_comp,GTK_TREE_MODEL(_loc_model));
2632         gtk_entry_completion_set_text_column(origin_comp, 0);
2633         gtk_entry_set_completion(GTK_ENTRY(oti.txt_origin), origin_comp);
2634
2635         g_signal_connect(G_OBJECT(oti.rad_use_gps), "toggled",
2636                           G_CALLBACK(origin_type_selected), &oti);
2637         g_signal_connect(G_OBJECT(oti.rad_use_route), "toggled",
2638                           G_CALLBACK(origin_type_selected), &oti);
2639         g_signal_connect(G_OBJECT(oti.rad_use_text), "toggled",
2640                           G_CALLBACK(origin_type_selected), &oti);
2641
2642         g_signal_connect(G_OBJECT(cmb_category), "changed",
2643                 G_CALLBACK(poi_download_cat_selected), oti.txt_query);
2644     }
2645
2646     /* Initialize fields. */
2647
2648     hildon_number_editor_set_value(HILDON_NUMBER_EDITOR(num_page), 1);
2649
2650     gtk_entry_set_text(GTK_ENTRY(txt_source_url), _poi_dl_url);
2651     if(unity != 0)
2652     {
2653         gchar buffer[80];
2654         gchar strlat[32];
2655         gchar strlon[32];
2656         gdouble lat, lon;
2657
2658         unit2latlon(unitx, unity, lat, lon);
2659
2660         g_ascii_formatd(strlat, 32, "%.06f", lat);
2661         g_ascii_formatd(strlon, 32, "%.06f", lon);
2662         snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
2663
2664         gtk_entry_set_text(GTK_ENTRY(oti.txt_origin), buffer);
2665         gtk_toggle_button_set_active(
2666                 GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
2667     }
2668     /* Else use "End of Route" by default if they have a route. */
2669     else if(_route.head != _route.tail)
2670     {
2671         /* There is no route, so make it the default. */
2672         gtk_widget_set_sensitive(oti.rad_use_route, TRUE);
2673         gtk_toggle_button_set_active(
2674                 GTK_TOGGLE_BUTTON(oti.rad_use_route), TRUE);
2675         gtk_widget_grab_focus(oti.rad_use_route);
2676     }
2677     /* Else use "GPS Location" if they have GPS enabled. */
2678     else
2679     {
2680         /* There is no route, so desensitize "Use End of Route." */
2681         gtk_widget_set_sensitive(oti.rad_use_route, FALSE);
2682         if(_enable_gps)
2683         {
2684             gtk_toggle_button_set_active(
2685                     GTK_TOGGLE_BUTTON(oti.rad_use_gps), TRUE);
2686             gtk_widget_grab_focus(oti.rad_use_gps);
2687         }
2688         /* Else use text. */
2689         else
2690         {
2691             gtk_toggle_button_set_active(
2692                     GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
2693             gtk_widget_grab_focus(oti.txt_origin);
2694         }
2695     }
2696
2697     gtk_widget_show_all(dialog);
2698
2699     while(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
2700     {
2701         gchar origin_buffer[BUFFER_SIZE];
2702         const gchar *source_url, *origin, *query;
2703         gchar *file_uri_str = NULL;
2704         gchar *bytes = NULL;
2705         gint size;
2706         GnomeVFSResult vfs_result;
2707         GList *poi_list = NULL;
2708
2709         source_url = gtk_entry_get_text(GTK_ENTRY(txt_source_url));
2710         if(!strlen(source_url))
2711         {
2712             popup_error(dialog, _("Please specify a source URL."));
2713             continue;
2714         }
2715         else
2716         {
2717             g_free(_poi_dl_url);
2718             _poi_dl_url = g_strdup(source_url);
2719         }
2720
2721         if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti.rad_use_gps)))
2722         {
2723             gchar strlat[32];
2724             gchar strlon[32];
2725             latlon2unit(_gps.lat, _gps.lon, unitx, unity);
2726             g_ascii_formatd(strlat, 32, "%.06f", _gps.lat);
2727             g_ascii_formatd(strlon, 32, "%.06f", _gps.lon);
2728             snprintf(origin_buffer, sizeof(origin_buffer),
2729                     "%s, %s", strlat, strlon);
2730             origin = origin_buffer;
2731         }
2732         else if(gtk_toggle_button_get_active(
2733                     GTK_TOGGLE_BUTTON(oti.rad_use_route)))
2734         {
2735             gchar strlat[32];
2736             gchar strlon[32];
2737             Point *p;
2738             gdouble lat, lon;
2739
2740             /* Use last non-zero route point. */
2741             for(p = _route.tail; !p->unity; p--) { }
2742
2743             unitx = p->unitx;
2744             unity = p->unity;
2745             unit2latlon(p->unitx, p->unity, lat, lon);
2746             g_ascii_formatd(strlat, 32, "%.06f", lat);
2747             g_ascii_formatd(strlon, 32, "%.06f", lon);
2748             snprintf(origin_buffer, sizeof(origin_buffer),
2749                     "%s, %s", strlat, strlon);
2750             origin = origin_buffer;
2751         }
2752         else
2753         {
2754             Point porig;
2755             origin = gtk_entry_get_text(GTK_ENTRY(oti.txt_origin));
2756             if(*origin)
2757             {
2758                 porig = locate_address(dialog, origin);
2759                 if(!porig.unity)
2760                     continue;
2761             }
2762         }
2763
2764         if(!*origin)
2765         {
2766             popup_error(dialog, _("Please specify an origin."));
2767             continue;
2768         }
2769
2770         if(gtk_combo_box_get_active(GTK_COMBO_BOX(cmb_category)) == -1)
2771         {
2772             popup_error(dialog, _("Please specify a default category."));
2773             continue;
2774         }
2775
2776         query = gtk_entry_get_text(GTK_ENTRY(oti.txt_query));
2777         if(!strlen(query))
2778         {
2779             popup_error(dialog, _("Please specify a query."));
2780             continue;
2781         }
2782
2783         /* Construct the URL. */
2784         {
2785             gchar *origin_escaped;
2786             gchar *query_escaped;
2787
2788             origin_escaped = gnome_vfs_escape_string(origin);
2789             query_escaped = gnome_vfs_escape_string(query);
2790             file_uri_str = g_strdup_printf(
2791                     source_url, origin_escaped, query_escaped,
2792                     hildon_number_editor_get_value(
2793                         HILDON_NUMBER_EDITOR(num_page)));
2794             g_free(origin_escaped);
2795             g_free(query_escaped);
2796         }
2797
2798         /* Parse the given file as GPX. */
2799         if(GNOME_VFS_OK != (vfs_result = gnome_vfs_read_entire_file(
2800                         file_uri_str, &size, &bytes)))
2801         {
2802             popup_error(dialog, gnome_vfs_result_to_string(vfs_result));
2803         }
2804         else if(strncmp(bytes, "<?xml", strlen("<?xml")))
2805         {
2806             /* Not an XML document - must be bad locations. */
2807             popup_error(dialog, _("Invalid origin or query."));
2808             printf("bytes: %s\n", bytes);
2809         }
2810         else if(gpx_poi_parse(bytes, size, &poi_list))
2811         {
2812             /* Insert the POIs into the database. */
2813             gint num_inserts = poi_list_insert(dialog, poi_list,
2814                     GTK_COMBO_BOX(cmb_category));
2815
2816             if(num_inserts)
2817             {
2818                 /* Create a new dialog with the results. */
2819                 poi_list_dialog(dialog, unitx, unity, poi_list);
2820             }
2821
2822             poi_list_free(poi_list);
2823         }
2824         else
2825             popup_error(dialog, _("Error parsing GPX file."));
2826
2827         g_free(file_uri_str);
2828         g_free(bytes);
2829
2830         /* Increment the page number for them. */
2831         hildon_number_editor_set_value(HILDON_NUMBER_EDITOR(num_page),
2832             hildon_number_editor_get_value(HILDON_NUMBER_EDITOR(num_page)) +1);
2833     }
2834
2835     /* Hide the dialog. */
2836     gtk_widget_hide(dialog);
2837
2838     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2839     return TRUE;
2840 }
2841
2842 gboolean
2843 poi_browse_dialog(gint unitx, gint unity)
2844 {
2845     static GtkWidget *dialog = NULL;
2846     static GtkWidget *table = NULL;
2847     static GtkWidget *table2 = NULL;
2848     static GtkWidget *label = NULL;
2849     static GtkWidget *cmb_category = NULL;
2850     static OriginToggleInfo oti;
2851     printf("%s()\n", __PRETTY_FUNCTION__);
2852
2853     if(!dialog)
2854     {
2855         GtkEntryCompletion *origin_comp;
2856
2857         dialog = gtk_dialog_new_with_buttons(_("Browse POIs"),
2858                 GTK_WINDOW(_window), GTK_DIALOG_MODAL,
2859                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2860                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2861                 NULL);
2862
2863         /* Enable the help button. */
2864 #ifndef LEGACY
2865         hildon_help_dialog_help_enable(
2866 #else
2867         ossohelp_dialog_help_enable(
2868 #endif
2869                 GTK_DIALOG(dialog), HELP_ID_BROWSEPOI, _osso);
2870
2871         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2872                 table = gtk_table_new(3, 4, FALSE), TRUE, TRUE, 0);
2873
2874         /* Auto. */
2875         gtk_table_attach(GTK_TABLE(table),
2876                 oti.rad_use_gps = gtk_radio_button_new_with_label(NULL,
2877                     _("Use GPS Location")),
2878                 0, 1, 0, 1, GTK_FILL, 0, 2, 4);
2879
2880         /* Use End of Route. */
2881         gtk_table_attach(GTK_TABLE(table),
2882                oti.rad_use_route = gtk_radio_button_new_with_label_from_widget(
2883                    GTK_RADIO_BUTTON(oti.rad_use_gps), _("Use End of Route")),
2884                0, 1, 1, 2, GTK_FILL, 0, 2, 4);
2885
2886         gtk_table_attach(GTK_TABLE(table),
2887                 gtk_vseparator_new(),
2888                 1, 2, 0, 2, GTK_FILL, GTK_FILL, 2, 4);
2889
2890         /* Category. */
2891         gtk_table_attach(GTK_TABLE(table),
2892                 label = gtk_label_new(_("Category")),
2893                 2, 3, 0, 1, GTK_FILL, 0, 2, 4);
2894         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2895         gtk_table_attach(GTK_TABLE(table),
2896                 cmb_category = poi_create_cat_combo(),
2897                 3, 4, 0, 1, GTK_FILL, 0, 2, 4);
2898         /* Add an extra, "<any>" category. */
2899         {
2900             GtkTreeIter iter;
2901             GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(
2902                         GTK_COMBO_BOX(cmb_category)));
2903             gtk_list_store_prepend(store, &iter);
2904             gtk_list_store_set(store, &iter, 0, -1, 1, "<any>", -1);
2905             gtk_combo_box_set_active_iter(GTK_COMBO_BOX(cmb_category), &iter);
2906         }
2907
2908
2909         /* Another table for the Origin and Query. */
2910         gtk_table_attach(GTK_TABLE(table),
2911                 table2 = gtk_table_new(2, 2, FALSE),
2912                 0, 4, 2, 3, GTK_EXPAND | GTK_FILL, 0, 2, 4);
2913
2914         /* Origin. */
2915         gtk_table_attach(GTK_TABLE(table2),
2916                 oti.rad_use_text = gtk_radio_button_new_with_label_from_widget(
2917                     GTK_RADIO_BUTTON(oti.rad_use_gps), _("Origin")),
2918                 0, 1, 0, 1, GTK_FILL, 0, 2, 4);
2919         gtk_table_attach(GTK_TABLE(table2),
2920                 oti.txt_origin = gtk_entry_new(),
2921                 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4);
2922         gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_origin), 25);
2923 #ifdef MAEMO_CHANGES
2924 #ifndef LEGACY
2925         g_object_set(G_OBJECT(oti.txt_origin), "hildon-input-mode",
2926                 HILDON_GTK_INPUT_MODE_FULL, NULL);
2927 #else
2928         g_object_set(G_OBJECT(oti.txt_origin), HILDON_AUTOCAP, FALSE, NULL);
2929 #endif
2930 #endif
2931
2932         /* Destination. */
2933         gtk_table_attach(GTK_TABLE(table2),
2934                 label = gtk_label_new(_("Query")),
2935                 0, 1, 1, 2, GTK_FILL, 0, 2, 4);
2936         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2937         gtk_table_attach(GTK_TABLE(table2),
2938                 oti.txt_query = gtk_entry_new(),
2939                 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4);
2940         gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_query), 25);
2941 #ifdef MAEMO_CHANGES
2942 #ifndef LEGACY
2943         g_object_set(G_OBJECT(oti.txt_query), "hildon-input-mode",
2944                 HILDON_GTK_INPUT_MODE_FULL, NULL);
2945 #else
2946         g_object_set(G_OBJECT(oti.txt_query), HILDON_AUTOCAP, FALSE, NULL);
2947 #endif
2948 #endif
2949
2950         /* Set up auto-completion. */
2951         origin_comp = gtk_entry_completion_new();
2952         gtk_entry_completion_set_model(origin_comp,GTK_TREE_MODEL(_loc_model));
2953         gtk_entry_completion_set_text_column(origin_comp, 0);
2954         gtk_entry_set_completion(GTK_ENTRY(oti.txt_origin), origin_comp);
2955
2956         g_signal_connect(G_OBJECT(oti.rad_use_gps), "toggled",
2957                           G_CALLBACK(origin_type_selected), &oti);
2958         g_signal_connect(G_OBJECT(oti.rad_use_route), "toggled",
2959                           G_CALLBACK(origin_type_selected), &oti);
2960         g_signal_connect(G_OBJECT(oti.rad_use_text), "toggled",
2961                           G_CALLBACK(origin_type_selected), &oti);
2962     }
2963
2964     /* Initialize fields. */
2965
2966     if(unity != 0)
2967     {
2968         gchar buffer[80];
2969         gchar strlat[32];
2970         gchar strlon[32];
2971         gdouble lat, lon;
2972
2973         unit2latlon(unitx, unity, lat, lon);
2974
2975         g_ascii_formatd(strlat, 32, "%.06f", lat);
2976         g_ascii_formatd(strlon, 32, "%.06f", lon);
2977         snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
2978
2979         gtk_entry_set_text(GTK_ENTRY(oti.txt_origin), buffer);
2980         gtk_toggle_button_set_active(
2981                 GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
2982     }
2983     /* Else use "End of Route" by default if they have a route. */
2984     else if(_route.head != _route.tail)
2985     {
2986         /* There is no route, so make it the default. */
2987         gtk_widget_set_sensitive(oti.rad_use_route, TRUE);
2988         gtk_toggle_button_set_active(
2989                 GTK_TOGGLE_BUTTON(oti.rad_use_route), TRUE);
2990         gtk_widget_grab_focus(oti.rad_use_route);
2991     }
2992     /* Else use "GPS Location" if they have GPS enabled. */
2993     else
2994     {
2995         /* There is no route, so desensitize "Use End of Route." */
2996         gtk_widget_set_sensitive(oti.rad_use_route, FALSE);
2997         if(_enable_gps)
2998         {
2999             gtk_toggle_button_set_active(
3000                     GTK_TOGGLE_BUTTON(oti.rad_use_gps), TRUE);
3001             gtk_widget_grab_focus(oti.rad_use_gps);
3002         }
3003         /* Else use text. */
3004         else
3005         {
3006             gtk_toggle_button_set_active(
3007                     GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
3008             gtk_widget_grab_focus(oti.txt_origin);
3009         }
3010     }
3011
3012     gtk_widget_show_all(dialog);
3013
3014     while(gtk_dialog_run(GTK_DIALOG(dialog)) ==GTK_RESPONSE_ACCEPT)
3015     {
3016         gchar buffer[BUFFER_SIZE];
3017         const gchar *origin, *query;
3018         gdouble lat, lon;
3019         GList *poi_list = NULL;
3020         gint cat_id;
3021         gboolean is_cat = FALSE;
3022         sqlite3_stmt *stmt;
3023
3024         if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti.rad_use_gps)))
3025         {
3026             gchar strlat[32];
3027             gchar strlon[32];
3028             latlon2unit(_gps.lat, _gps.lon, unitx, unity);
3029             g_ascii_formatd(strlat, 32, "%.06f", _gps.lat);
3030             g_ascii_formatd(strlon, 32, "%.06f", _gps.lon);
3031             snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
3032             origin = buffer;
3033         }
3034         else if(gtk_toggle_button_get_active(
3035                     GTK_TOGGLE_BUTTON(oti.rad_use_route)))
3036         {
3037             gchar strlat[32];
3038             gchar strlon[32];
3039             Point *p;
3040             gdouble lat, lon;
3041
3042             /* Use last non-zero route point. */
3043             for(p = _route.tail; !p->unity; p--) { }
3044
3045             unitx = p->unitx;
3046             unity = p->unity;
3047             unit2latlon(p->unitx, p->unity, lat, lon);
3048             g_ascii_formatd(strlat, 32, "%.06f", lat);
3049             g_ascii_formatd(strlon, 32, "%.06f", lon);
3050             snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
3051             origin = buffer;
3052         }
3053         else
3054         {
3055             Point porig;
3056             origin = gtk_entry_get_text(GTK_ENTRY(oti.txt_origin));
3057             porig = locate_address(dialog, origin);
3058             if(!porig.unity)
3059                 continue;
3060         }
3061
3062         if(!strlen(origin))
3063         {
3064             popup_error(dialog, _("Please specify an origin."));
3065             continue;
3066         }
3067
3068         /* Check if we're doing a category search. */
3069         {
3070             GtkTreeIter iter;
3071             if(gtk_combo_box_get_active_iter(
3072                     GTK_COMBO_BOX(cmb_category), &iter))
3073             {
3074                 gtk_tree_model_get(
3075                         gtk_combo_box_get_model(GTK_COMBO_BOX(cmb_category)),
3076                         &iter, 0, &cat_id, -1);
3077                 if(cat_id >= 0)
3078                 {
3079                     is_cat = TRUE;
3080                 }
3081             }
3082         }
3083
3084         query = g_strdup_printf("%%%s%%",
3085                 gtk_entry_get_text(GTK_ENTRY(oti.txt_query)));
3086
3087         unit2latlon(unitx, unity, lat, lon);
3088
3089         if(is_cat)
3090         {
3091             if(SQLITE_OK != sqlite3_bind_int(_stmt_browsecat_poi, 1, cat_id) ||
3092                SQLITE_OK != sqlite3_bind_text(_stmt_browsecat_poi, 2, query,
3093                    -1, g_free) ||
3094                SQLITE_OK != sqlite3_bind_double(_stmt_browsecat_poi, 3, lat) ||
3095                SQLITE_OK != sqlite3_bind_double(_stmt_browsecat_poi, 4, lon))
3096             {
3097                 g_printerr("Failed to bind values for _stmt_browsecat_poi\n");
3098                 continue;
3099             }
3100             stmt = _stmt_browsecat_poi;
3101         }
3102         else
3103         {
3104             if(SQLITE_OK != sqlite3_bind_text(_stmt_browse_poi, 1, query,
3105                         -1, g_free) ||
3106                SQLITE_OK != sqlite3_bind_double(_stmt_browse_poi, 2, lat) ||
3107                SQLITE_OK != sqlite3_bind_double(_stmt_browse_poi, 3, lon))
3108             {
3109                 g_printerr("Failed to bind values for _stmt_browse_poi\n");
3110                 continue;
3111             }
3112             stmt = _stmt_browse_poi;
3113         }
3114
3115         while(SQLITE_ROW == sqlite3_step(stmt))
3116         {
3117             PoiInfo *poi = g_slice_new(PoiInfo);
3118             poi->poi_id = sqlite3_column_int(stmt, 0);
3119             poi->cat_id = sqlite3_column_int(stmt, 1);
3120             poi->lat = sqlite3_column_double(stmt, 2);
3121             poi->lon = sqlite3_column_double(stmt, 3);
3122             poi->label =g_strdup(sqlite3_column_text(stmt, 4));
3123             poi->desc = g_strdup(sqlite3_column_text(stmt, 5));
3124             poi->clabel=g_strdup(sqlite3_column_text(stmt, 6));
3125             poi_list = g_list_prepend(poi_list, poi);
3126         }
3127         sqlite3_reset(stmt);
3128
3129         if(poi_list)
3130         {
3131             /* Create a new dialog with the results. */
3132             poi_list_dialog(dialog, unitx, unity, poi_list);
3133             poi_list_free(poi_list);
3134         }
3135         else
3136             popup_error(dialog, _("No POIs found."));
3137     }
3138
3139     map_force_redraw();
3140
3141     /* Hide the dialog. */
3142     gtk_widget_hide(dialog);
3143
3144     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
3145     return TRUE;
3146 }
3147
3148 /**
3149  * Render all the POI data.  This should be done before rendering track data.
3150  */
3151 void
3152 map_render_poi()
3153 {
3154     gint unitx, unity;
3155     gdouble lat1, lat2, lon1, lon2;
3156     gchar buffer[100];
3157     gint poix, poiy;
3158     GdkPixbuf *pixbuf = NULL;
3159     GError *error = NULL;
3160     printf("%s()\n", __PRETTY_FUNCTION__);
3161
3162     if(_poi_db && _poi_zoom > _zoom)
3163     {
3164         gint diag_offset = pixel2unit(MAX(_view_width_pixels,
3165                     _view_height_pixels) / 2);
3166         buf2unit(0, _view_height_pixels, unitx, unity);
3167         unitx = _center.unitx - diag_offset;
3168         unity = _center.unity + diag_offset;
3169         unit2latlon(unitx, unity, lat1, lon1);
3170         unitx = _center.unitx + diag_offset;
3171         unity = _center.unity - diag_offset;
3172         unit2latlon(unitx, unity, lat2, lon2);
3173
3174         if(SQLITE_OK != sqlite3_bind_double(_stmt_select_poi, 1, lat1) ||
3175            SQLITE_OK != sqlite3_bind_double(_stmt_select_poi, 2, lat2) ||
3176            SQLITE_OK != sqlite3_bind_double(_stmt_select_poi, 3, lon1) ||
3177            SQLITE_OK != sqlite3_bind_double(_stmt_select_poi, 4, lon2))
3178         {
3179             g_printerr("Failed to bind values for _stmt_select_poi\n");
3180             return;
3181         }
3182
3183         while(SQLITE_ROW == sqlite3_step(_stmt_select_poi))
3184         {
3185             lat1 = sqlite3_column_double(_stmt_select_poi, 0);
3186             lon1 = sqlite3_column_double(_stmt_select_poi, 1);
3187             gchar *poi_label = g_utf8_strdown(sqlite3_column_text(
3188                     _stmt_select_poi, 3), -1);
3189             gchar *cat_label = g_utf8_strdown(sqlite3_column_text(
3190                     _stmt_select_poi, 6), -1);
3191
3192             latlon2unit(lat1, lon1, unitx, unity);
3193             unit2buf(unitx, unity, poix, poiy);
3194
3195             /* Try to get icon for specific POI first. */
3196             snprintf(buffer, sizeof(buffer), "%s/%s.jpg",
3197                     _poi_db_dirname, poi_label);
3198             pixbuf = gdk_pixbuf_new_from_file(buffer, &error);
3199             if(error)
3200             {
3201                 /* No icon for specific POI - try for category. */
3202                 error = NULL;
3203                 snprintf(buffer, sizeof(buffer), "%s/%s.jpg",
3204                         _poi_db_dirname, cat_label);
3205                 pixbuf = gdk_pixbuf_new_from_file(buffer, &error);
3206             }
3207             if(error)
3208             {
3209                 /* No icon for POI or for category.
3210                  * Try default POI icon file. */
3211                 error = NULL;
3212                 snprintf(buffer, sizeof(buffer), "%s/poi.jpg",
3213                         _poi_db_dirname);
3214                 pixbuf = gdk_pixbuf_new_from_file(buffer, &error);
3215             }
3216             if(error)
3217             {
3218                 /* No icon for POI or for category or default POI icon file.
3219                    Draw default purple square. */
3220                 error = NULL;
3221                 gdk_draw_rectangle(_map_pixmap, _gc[COLORABLE_POI], TRUE,
3222                         poix - (gint)(1.5f * _draw_width),
3223                         poiy - (gint)(1.5f * _draw_width),
3224                         3 * _draw_width,
3225                         3 * _draw_width);
3226             }
3227             else
3228             {
3229                 /* We found an icon to draw. */
3230                 gdk_draw_pixbuf(
3231                         _map_pixmap,
3232                         _gc[COLORABLE_POI],
3233                         pixbuf,
3234                         0, 0,
3235                         poix - gdk_pixbuf_get_width(pixbuf) / 2,
3236                         poiy - gdk_pixbuf_get_height(pixbuf) / 2,
3237                         -1,-1,
3238                         GDK_RGB_DITHER_NONE, 0, 0);
3239                 g_object_unref(pixbuf);
3240             }
3241
3242             g_free(poi_label);
3243             g_free(cat_label);
3244         }
3245         sqlite3_reset(_stmt_select_poi);
3246     }
3247
3248     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
3249 }
3250
3251 void
3252 poi_destroy()
3253 {
3254     printf("%s()\n", __PRETTY_FUNCTION__);
3255
3256     if(_poi_db) 
3257     { 
3258         sqlite3_close(_poi_db); 
3259         _poi_db = NULL; 
3260     }
3261
3262     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
3263 }