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