]> git.itanic.dy.fi Git - maemo-mapper/blob - src/poi.c
dfa5decee743cc4d5b14e7e2eaaed4d81144be0b
[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 poi_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     if(dialog == NULL)
919     {
920         dialog = gtk_dialog_new_with_buttons(_("POI Categories"),
921                 GTK_WINDOW(parent), GTK_DIALOG_MODAL,
922                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
923                 NULL);
924
925         /* Enable the help button. */
926 #ifndef LEGACY
927         hildon_help_dialog_help_enable(
928 #else
929         ossohelp_dialog_help_enable(
930 #endif
931                 GTK_DIALOG(dialog), HELP_ID_POICAT, _osso);
932
933         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
934                 btn_edit = gtk_button_new_with_label(_("Edit...")));
935
936         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->action_area),
937                 btn_add = gtk_button_new_with_label(_("Add...")));
938
939         sw = gtk_scrolled_window_new(NULL, NULL);
940         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW (sw),
941                       GTK_POLICY_NEVER,
942                       GTK_POLICY_AUTOMATIC);
943         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
944                 sw, TRUE, TRUE, 0);
945
946         tree_view = gtk_tree_view_new();
947         /* Maemo-related? */
948         g_object_set(tree_view, "allow-checkbox-mode", FALSE, NULL);
949         gtk_container_add (GTK_CONTAINER (sw), tree_view);
950
951         gtk_tree_selection_set_mode(
952                 gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view)),
953                 GTK_SELECTION_SINGLE);
954         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(tree_view), TRUE);
955
956         renderer = gtk_cell_renderer_text_new();
957         column = gtk_tree_view_column_new_with_attributes(
958                 _("ID"), renderer, "text", CAT_ID, NULL);
959         gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
960         gtk_tree_view_column_set_max_width (column, 1);
961
962         renderer = gtk_cell_renderer_toggle_new();
963         g_signal_connect (renderer, "toggled",
964                 G_CALLBACK (category_toggled), store);
965         column = gtk_tree_view_column_new_with_attributes(
966                 _("Enabled"), renderer, "active", CAT_ENABLED, NULL);
967         gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
968
969         renderer = gtk_cell_renderer_text_new();
970         column = gtk_tree_view_column_new_with_attributes(
971                 _("Label"), renderer, "text", CAT_LABEL, NULL);
972         gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
973
974         renderer = gtk_cell_renderer_text_new();
975         column = gtk_tree_view_column_new_with_attributes(
976                 _("Description"), renderer, "text", CAT_DESC, NULL);
977         gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
978
979         renderer = gtk_cell_renderer_text_new();
980         column = gtk_tree_view_column_new_with_attributes(
981                 _("# POIs"), renderer, "text", CAT_POI_CNT, NULL);
982         gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
983
984         gtk_window_set_default_size(GTK_WINDOW(dialog), -1, 400);
985
986         pcedit.dialog = dialog;
987         pcedit.tree_view = tree_view;
988
989         g_signal_connect(G_OBJECT(btn_edit), "clicked",
990                 G_CALLBACK(category_edit), &pcedit);
991
992         g_signal_connect(G_OBJECT(btn_add), "clicked",
993                 G_CALLBACK(category_add), &pcedit);
994     }
995
996     gtk_tree_view_set_model(GTK_TREE_VIEW(tree_view), GTK_TREE_MODEL(store));
997     g_object_unref(G_OBJECT(store));
998
999     gtk_widget_show_all(dialog);
1000
1001     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
1002     {
1003         break;
1004     }
1005
1006     gtk_widget_hide(dialog);
1007
1008     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1009     return TRUE;
1010 }
1011
1012 static gboolean
1013 poi_delete(GtkWidget *widget, DeletePOI *dpoi)
1014 {
1015     GtkWidget *confirm;
1016     gint i;
1017     gchar *buffer;
1018     printf("%s()\n", __PRETTY_FUNCTION__);
1019
1020     buffer = g_strdup_printf("%s\n%s", _("Delete POI?"), dpoi->txt_label);
1021     confirm = hildon_note_new_confirmation(GTK_WINDOW(dpoi->dialog), buffer);
1022     g_free(buffer);
1023     i = gtk_dialog_run(GTK_DIALOG(confirm));
1024     gtk_widget_destroy(GTK_WIDGET(confirm));
1025
1026     if(i == GTK_RESPONSE_OK)
1027     {
1028         if(SQLITE_OK != sqlite3_bind_int(_stmt_delete_poi, 1, dpoi->id) ||
1029            SQLITE_DONE != sqlite3_step(_stmt_delete_poi))
1030         {
1031             MACRO_BANNER_SHOW_INFO(dpoi->dialog, _("Error deleting POI"));
1032         }
1033         else
1034         {
1035             dpoi->deleted = TRUE;
1036             gtk_widget_hide(dpoi->dialog);
1037             map_force_redraw();
1038         }
1039         sqlite3_reset(_stmt_delete_poi);
1040     }
1041
1042     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1043     return TRUE;
1044 }
1045
1046 static gboolean
1047 poi_populate_categories(GtkListStore *store, gint cat_id,
1048         GtkTreeIter *out_active)
1049 {
1050     gboolean has_active = FALSE;
1051     printf("%s()\n", __PRETTY_FUNCTION__);
1052
1053     gtk_list_store_clear(store);
1054
1055     while(SQLITE_ROW == sqlite3_step(_stmt_selall_cat))
1056     {
1057         GtkTreeIter iter;
1058         gint cid = sqlite3_column_int(_stmt_selall_cat, 0);
1059         const gchar *clab = sqlite3_column_text(_stmt_selall_cat, 1);
1060
1061         gtk_list_store_append(store, &iter);
1062         gtk_list_store_set(store, &iter, 0, cid, 1, clab, -1);
1063
1064         if(cid == cat_id || !has_active)
1065         {
1066             if(out_active)
1067                 *out_active = iter;
1068             has_active = TRUE;
1069         }
1070     }
1071     sqlite3_reset(_stmt_selall_cat);
1072
1073     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1074     return has_active;
1075 }
1076
1077 static gboolean
1078 poi_edit_cat(GtkWidget *widget, PoiCategoryEditInfo *data)
1079 {
1080     printf("%s()\n", __PRETTY_FUNCTION__);
1081     if(category_list_dialog(data->dialog))
1082     {
1083         GtkTreeIter active;
1084         if(poi_populate_categories(GTK_LIST_STORE(gtk_combo_box_get_model(
1085                         GTK_COMBO_BOX(data->cmb_category))),
1086                 data->cat_id, &active))
1087         {
1088             gtk_combo_box_set_active_iter(
1089                     GTK_COMBO_BOX(data->cmb_category), &active);
1090         }
1091     }
1092     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1093     return TRUE;
1094 }
1095
1096 static GtkWidget*
1097 poi_create_cat_combo()
1098 {
1099     GtkWidget *cmb_category;
1100     GtkTreeModel *model;
1101     printf("%s()\n", __PRETTY_FUNCTION__);
1102
1103     model = GTK_TREE_MODEL(gtk_list_store_new(2,
1104                 G_TYPE_INT,      /* Category ID */
1105                 G_TYPE_STRING)); /* Category Label */
1106     cmb_category = gtk_combo_box_new_with_model(model);
1107     g_object_unref(model);
1108
1109     /* Set up the view for the combo box. */
1110     {
1111         GtkCellRenderer *renderer;
1112         GtkTreeIter active;
1113         renderer = gtk_cell_renderer_text_new();
1114         gtk_cell_layout_pack_start(
1115                 GTK_CELL_LAYOUT(cmb_category), renderer, TRUE);
1116         gtk_cell_layout_set_attributes(
1117                 GTK_CELL_LAYOUT(cmb_category), renderer, "text", 1, NULL);
1118
1119         poi_populate_categories(GTK_LIST_STORE(gtk_combo_box_get_model(
1120                         GTK_COMBO_BOX(cmb_category))), -1, &active);
1121     }
1122     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1123     return cmb_category;
1124 }
1125
1126 gboolean
1127 poi_add_dialog(GtkWidget *parent, gint unitx, gint unity)
1128 {
1129     gchar buffer[16];
1130     static PoiInfo poi;
1131     static GtkWidget *dialog;
1132     static GtkWidget *table;
1133     static GtkWidget *label;
1134     static GtkWidget *txt_label;
1135     static GtkWidget *txt_lat;
1136     static GtkWidget *txt_lon;
1137     static GtkWidget *cmb_category;
1138     static GtkWidget *txt_desc;
1139     static GtkWidget *btn_catedit;
1140     static GtkWidget *hbox;
1141     static GtkWidget *txt_scroll;
1142     static GtkTextBuffer *desc_txt;
1143     static GtkTextIter begin, end;
1144     static DeletePOI dpoi = {NULL, NULL, 0};
1145     static PoiCategoryEditInfo pcedit;
1146     printf("%s()\n", __PRETTY_FUNCTION__);
1147
1148     if(dialog == NULL)
1149     {
1150         dialog = gtk_dialog_new_with_buttons(_("Add POI"),
1151             GTK_WINDOW(parent), GTK_DIALOG_MODAL,
1152             GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1153             GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1154             NULL);
1155
1156         /* Set the lat/lon strings. */
1157         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1158                 table = gtk_table_new(6, 4, FALSE), TRUE, TRUE, 0);
1159
1160         gtk_table_attach(GTK_TABLE(table),
1161                 label = gtk_label_new(_("Lat")),
1162                 0, 1, 0, 1, GTK_FILL, 0, 2, 0);
1163         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1164         gtk_table_attach(GTK_TABLE(table),
1165                 txt_lat = gtk_entry_new(),
1166                 1, 2, 0, 1, GTK_FILL, 0, 2, 0);
1167
1168         gtk_table_attach(GTK_TABLE(table),
1169                 label = gtk_label_new(_("Lon")),
1170                 2, 3, 0, 1, GTK_FILL, 0, 2, 0);
1171         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1172         gtk_table_attach(GTK_TABLE(table),
1173                 txt_lon = gtk_entry_new(),
1174                 3, 4, 0, 1, GTK_FILL, 0, 2, 0);
1175
1176         gtk_table_attach(GTK_TABLE(table),
1177                 label = gtk_label_new(_("Label")),
1178                 0, 1, 1, 2, GTK_FILL, 0, 2, 0);
1179         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1180         gtk_table_attach(GTK_TABLE(table),
1181                 txt_label = gtk_entry_new(),
1182                 1, 4, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 0);
1183
1184         gtk_table_attach(GTK_TABLE(table),
1185                 label = gtk_label_new(_("Category")),
1186                 0, 1, 3, 4, GTK_FILL, 0, 2, 0);
1187         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
1188         gtk_table_attach(GTK_TABLE(table),
1189                 hbox = gtk_hbox_new(FALSE, 4),
1190                 1, 4, 3, 4, GTK_EXPAND | GTK_FILL, 0, 2, 0);
1191         gtk_box_pack_start(GTK_BOX(hbox),
1192                 cmb_category = poi_create_cat_combo(),
1193                 FALSE, FALSE, 0);
1194
1195         gtk_box_pack_start(GTK_BOX(hbox),
1196                 btn_catedit = gtk_button_new_with_label(
1197                     _("Edit Categories...")),
1198                 FALSE, FALSE, 0);
1199
1200         gtk_table_attach(GTK_TABLE(table),
1201                 label = gtk_label_new(_("Description")),
1202                 0, 1, 5, 6, GTK_FILL, GTK_FILL, 2, 0);
1203         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.0f);
1204
1205         txt_scroll = gtk_scrolled_window_new(NULL, NULL);
1206         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(txt_scroll),
1207                 GTK_SHADOW_IN);
1208         gtk_table_attach(GTK_TABLE(table),
1209                 txt_scroll,
1210                 1, 4, 5, 6, GTK_EXPAND | GTK_FILL, 0, 2, 0);
1211
1212         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(txt_scroll),
1213                 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1214
1215         txt_desc = gtk_text_view_new ();
1216         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(txt_desc), GTK_WRAP_WORD);
1217
1218         gtk_container_add(GTK_CONTAINER(txt_scroll), txt_desc);
1219         gtk_widget_set_size_request(GTK_WIDGET(txt_scroll), 550, 120);
1220
1221         desc_txt = gtk_text_view_get_buffer(GTK_TEXT_VIEW (txt_desc));
1222
1223         g_signal_connect(G_OBJECT(btn_catedit), "clicked",
1224                 G_CALLBACK(poi_edit_cat), &pcedit);
1225     }
1226
1227     poi.poi_id = -1;
1228     poi.cat_id = -1;
1229     poi.clabel = NULL;
1230     poi.desc = g_strdup("");
1231     unit2latlon(unitx, unity, poi.lat, poi.lon);
1232
1233     /* Lat/Lon */
1234     snprintf(buffer, sizeof(buffer), "%.06f", poi.lat);
1235     gtk_entry_set_text(GTK_ENTRY(txt_lat), buffer);
1236     snprintf(buffer, sizeof(buffer), "%.06f", poi.lon);
1237     gtk_entry_set_text(GTK_ENTRY(txt_lon), buffer);
1238
1239     /* Label */
1240     if(SQLITE_ROW == sqlite3_step(_stmt_nextlabel_poi))
1241         poi.label = g_strdup_printf("Point%06d",
1242                 sqlite3_column_int(_stmt_nextlabel_poi, 0));
1243     else
1244         poi.label = g_strdup("");
1245     sqlite3_reset(_stmt_nextlabel_poi);
1246     gtk_entry_set_text(GTK_ENTRY(txt_label), poi.label);
1247
1248     /* POI Desc. */
1249     gtk_text_buffer_set_text(desc_txt, "", -1);
1250
1251     /* Category. */
1252     {
1253         GtkTreeIter iter;
1254         gint cat_id = -1;
1255         gboolean had_cat_id = FALSE;
1256
1257         if(gtk_combo_box_get_active_iter(
1258                 GTK_COMBO_BOX(cmb_category), &iter))
1259         {
1260             gtk_tree_model_get(
1261                     gtk_combo_box_get_model(GTK_COMBO_BOX(cmb_category)),&iter,
1262                     0, &cat_id,
1263                     -1);
1264             had_cat_id = TRUE;
1265         }
1266         gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(
1267                         GTK_COMBO_BOX(cmb_category))));
1268         if(poi_populate_categories(GTK_LIST_STORE(gtk_combo_box_get_model(
1269                     GTK_COMBO_BOX(cmb_category))), cat_id, &iter)
1270                 && had_cat_id)
1271         {
1272             gtk_combo_box_set_active_iter(GTK_COMBO_BOX(cmb_category), &iter);
1273         }
1274     }
1275
1276     pcedit.dialog = dialog;
1277     pcedit.cmb_category = cmb_category;
1278     pcedit.cat_id = poi.cat_id;
1279
1280     gtk_widget_show_all(dialog);
1281
1282     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
1283     {
1284         GtkTreeIter iter;
1285         const gchar *text;
1286         gchar *error_check;
1287
1288         text = gtk_entry_get_text(GTK_ENTRY(txt_lat));
1289         poi.lat = strdmstod(text, &error_check);
1290         if(text == error_check || poi.lat < -90. || poi.lat > 90.) {
1291             popup_error(dialog, _("Invalid Latitude"));
1292             continue;
1293         }
1294
1295         text = gtk_entry_get_text(GTK_ENTRY(txt_lon));
1296         poi.lon = strdmstod(text, &error_check);
1297         if(text == error_check || poi.lon < -180. || poi.lon > 180.) {
1298             popup_error(dialog, _("Invalid Longitude"));
1299             continue;
1300         }
1301
1302         if(strlen(gtk_entry_get_text(GTK_ENTRY(txt_label))))
1303         {
1304             if(poi.label)
1305                 g_free(poi.label);
1306             poi.label = g_strdup(gtk_entry_get_text(GTK_ENTRY(txt_label)));
1307         }
1308         else
1309         {
1310             popup_error(dialog, _("Please specify a name."));
1311             continue;
1312         }
1313
1314         if(!gtk_combo_box_get_active_iter(
1315                 GTK_COMBO_BOX(cmb_category), &iter))
1316         {
1317             popup_error(dialog, _("Please specify a category."));
1318             continue;
1319         }
1320
1321         gtk_text_buffer_get_iter_at_offset(desc_txt, &begin,0 );
1322         gtk_text_buffer_get_end_iter (desc_txt, &end);
1323         if(poi.desc)
1324             g_free(poi.desc);
1325         poi.desc = gtk_text_buffer_get_text(desc_txt, &begin, &end, TRUE);
1326
1327         if(poi.clabel)
1328             g_free(poi.clabel);
1329         gtk_tree_model_get(
1330                 gtk_combo_box_get_model(GTK_COMBO_BOX(cmb_category)), &iter,
1331                 0, &poi.cat_id,
1332                 1, &poi.clabel,
1333                 -1);
1334
1335         /* add poi */
1336         if(SQLITE_OK != sqlite3_bind_double(_stmt_insert_poi, 1, poi.lat)
1337         || SQLITE_OK != sqlite3_bind_double(_stmt_insert_poi, 2, poi.lon)
1338         || SQLITE_OK != sqlite3_bind_text(_stmt_insert_poi, 3, poi.label,
1339                -1, g_free)
1340         || SQLITE_OK != sqlite3_bind_text(_stmt_insert_poi, 4, poi.desc,
1341                -1, g_free)
1342         || SQLITE_OK != sqlite3_bind_int(_stmt_insert_poi, 5, poi.cat_id)
1343         || SQLITE_DONE != sqlite3_step(_stmt_insert_poi))
1344         {
1345             MACRO_BANNER_SHOW_INFO(parent, _("Error adding POI"));
1346         }
1347
1348         sqlite3_reset(_stmt_insert_poi);
1349
1350         /* We're done. */
1351         break;
1352     }
1353
1354     g_free(poi.label);
1355     g_free(poi.desc);
1356     g_free(dpoi.txt_label);
1357
1358     map_force_redraw();
1359
1360     gtk_widget_hide(dialog);
1361
1362     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1363     return !dpoi.deleted;
1364 }
1365
1366 gboolean
1367 poi_view_dialog(GtkWidget *parent, PoiInfo *poi)
1368 {
1369     gchar buffer[16];
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     snprintf(buffer, sizeof(buffer), "%.06f", poi->lat);
1483     gtk_entry_set_text(GTK_ENTRY(txt_lat), buffer);
1484     snprintf(buffer, sizeof(buffer), "%.06f", poi->lon);
1485     gtk_entry_set_text(GTK_ENTRY(txt_lon), buffer);
1486
1487     /* label */
1488     gtk_entry_set_text(GTK_ENTRY(txt_label), poi->label);
1489
1490     /* poi_desc */
1491     gtk_text_buffer_set_text(desc_txt, poi->desc, -1);
1492
1493     /* Category. */
1494     gtk_list_store_clear(GTK_LIST_STORE(gtk_combo_box_get_model(
1495                     GTK_COMBO_BOX(cmb_category))));
1496     if(poi_populate_categories(GTK_LIST_STORE(gtk_combo_box_get_model(
1497                 GTK_COMBO_BOX(cmb_category))), poi->cat_id, &iter))
1498         gtk_combo_box_set_active_iter(GTK_COMBO_BOX(cmb_category), &iter);
1499
1500     /* Connect Signals */
1501     pcedit.dialog = dialog;
1502     pcedit.cmb_category = cmb_category;
1503     pcedit.cat_id = poi->cat_id;
1504
1505     gtk_widget_show_all(dialog);
1506
1507     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
1508     {
1509         const gchar *text;
1510         gchar *error_check;
1511
1512         text = gtk_entry_get_text(GTK_ENTRY(txt_lat));
1513         poi->lat = strdmstod(text, &error_check);
1514         if(text == error_check || poi->lat < -90. || poi->lat > 90.) {
1515             popup_error(dialog, _("Invalid Latitude"));
1516             continue;
1517         }
1518
1519         text = gtk_entry_get_text(GTK_ENTRY(txt_lon));
1520         poi->lon = strdmstod(text, &error_check);
1521         if(text == error_check || poi->lon < -180. || poi->lon > 180.) {
1522             popup_error(dialog, _("Invalid Longitude"));
1523             continue;
1524         }
1525
1526         if(strlen(gtk_entry_get_text(GTK_ENTRY(txt_label))))
1527         {
1528             if(poi->label)
1529                 g_free(poi->label);
1530             poi->label = g_strdup(gtk_entry_get_text(GTK_ENTRY(txt_label)));
1531         }
1532         else
1533         {
1534             popup_error(dialog, _("Please specify a name."));
1535             continue;
1536         }
1537
1538         if(!gtk_combo_box_get_active_iter(
1539                 GTK_COMBO_BOX(cmb_category), &iter))
1540         {
1541             popup_error(dialog, _("Please specify a category."));
1542             continue;
1543         }
1544
1545         gtk_text_buffer_get_iter_at_offset(desc_txt, &begin,0 );
1546         gtk_text_buffer_get_end_iter (desc_txt, &end);
1547         if(poi->desc)
1548             g_free(poi->desc);
1549         poi->desc = gtk_text_buffer_get_text(desc_txt, &begin, &end, TRUE);
1550
1551         if(poi->clabel)
1552             g_free(poi->clabel);
1553         gtk_tree_model_get(
1554                 gtk_combo_box_get_model(GTK_COMBO_BOX(cmb_category)), &iter,
1555                 0, &poi->cat_id,
1556                 1, &poi->clabel,
1557                 -1);
1558
1559         /* edit poi */
1560         if(SQLITE_OK != sqlite3_bind_double(
1561                     _stmt_update_poi, 1, poi->lat) ||
1562            SQLITE_OK != sqlite3_bind_double(
1563                _stmt_update_poi, 2, poi->lon) ||
1564            SQLITE_OK != sqlite3_bind_text(_stmt_update_poi, 3, poi->label,
1565                     -1, SQLITE_STATIC) ||
1566            SQLITE_OK != sqlite3_bind_text(_stmt_update_poi, 4, poi->desc,
1567                -1, g_free) ||
1568            SQLITE_OK != sqlite3_bind_int(
1569                _stmt_update_poi, 5, poi->cat_id) ||
1570            SQLITE_OK != sqlite3_bind_int(
1571                _stmt_update_poi, 6, poi->poi_id) ||
1572            SQLITE_DONE != sqlite3_step(_stmt_update_poi))
1573         {
1574             MACRO_BANNER_SHOW_INFO(parent, _("Error updating POI"));
1575         }
1576
1577         sqlite3_reset(_stmt_update_poi);
1578
1579         /* We're done. */
1580         break;
1581     }
1582
1583     g_free(dpoi.txt_label);
1584
1585     map_force_redraw();
1586
1587     gtk_widget_hide(dialog); /* Destroying causes a crash.... ??? */
1588
1589     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1590     return !dpoi.deleted;
1591 }
1592
1593 static gint
1594 poi_list_insert(GtkWidget *parent, GList *poi_list, GtkComboBox *cmb_category)
1595 {
1596     gint default_cat_id;
1597     gchar *default_cat_label;
1598     gint num_inserts = 0;
1599     GList *curr;
1600     GtkTreeIter iter;
1601     printf("%s()\n", __PRETTY_FUNCTION__);
1602
1603     /* Get defaults from the given GtkComboBox */
1604     if(!gtk_combo_box_get_active_iter(
1605             GTK_COMBO_BOX(cmb_category), &iter))
1606     {
1607         vprintf("%s(): return 0\n", __PRETTY_FUNCTION__);
1608         return 0;
1609     }
1610     gtk_tree_model_get(
1611             gtk_combo_box_get_model(GTK_COMBO_BOX(cmb_category)),
1612             &iter,
1613             0, &default_cat_id,
1614             1, &default_cat_label,
1615             -1);
1616
1617     /* Iterate through the data model and import as desired. */
1618     for(curr = poi_list; curr; )
1619     {
1620         PoiInfo *poi = curr->data;
1621         if(
1622         (    SQLITE_OK != sqlite3_bind_double(_stmt_insert_poi, 1, poi->lat)
1623           || SQLITE_OK != sqlite3_bind_double(_stmt_insert_poi, 2, poi->lon)
1624           || SQLITE_OK != sqlite3_bind_text(_stmt_insert_poi, 3, poi->label,
1625              -1, SQLITE_STATIC)
1626           || SQLITE_OK != sqlite3_bind_text(_stmt_insert_poi, 4, poi->desc,
1627              -1, SQLITE_STATIC)
1628           || SQLITE_OK != sqlite3_bind_int(_stmt_insert_poi, 5,
1629               poi->cat_id = default_cat_id)
1630           || SQLITE_DONE != sqlite3_step(_stmt_insert_poi)
1631         ))
1632         {
1633             /* Failure. */
1634             GList *tmp = curr->next;
1635             if(poi->label)
1636                 g_free(poi->label);
1637             if(poi->desc)
1638                 g_free(poi->desc);
1639             g_slice_free(PoiInfo, poi);
1640             poi_list = g_list_delete_link(poi_list, curr);
1641             curr = tmp;
1642         }
1643         else
1644         {
1645             /* Success. */
1646             ++num_inserts;
1647             if(default_cat_label)
1648                 poi->clabel = g_strdup(default_cat_label);
1649             poi->poi_id = sqlite3_last_insert_rowid(_poi_db);
1650             curr = curr->next;
1651         }
1652         sqlite3_reset(_stmt_insert_poi);
1653     }
1654
1655     if(num_inserts)
1656     {
1657         gchar buffer[BUFFER_SIZE];
1658         map_force_redraw();
1659         snprintf(buffer, sizeof(buffer), "%d %s", num_inserts,
1660            _("POIs were added to the POI database.  The following screen will "
1661                "allow you to modify or delete any of the new POIs."));
1662         popup_error(parent, buffer);
1663     }
1664     else
1665     {
1666         popup_error(parent, _("No POIs were found."));
1667     }
1668
1669     if(default_cat_label)
1670         g_free(default_cat_label);
1671
1672     vprintf("%s(): return %d\n", __PRETTY_FUNCTION__, num_inserts);
1673     return num_inserts;
1674 }
1675
1676 static void
1677 poi_list_free(GList *poi_list)
1678 {
1679     GList *curr;
1680     printf("%s()\n", __PRETTY_FUNCTION__);
1681
1682     for(curr = poi_list; curr; curr = curr->next)
1683     {
1684         PoiInfo *poi_info = curr->data;
1685         if(poi_info)
1686         {
1687             if(poi_info->label)
1688                 g_free(poi_info->label);
1689             if(poi_info->desc)
1690                 g_free(poi_info->desc);
1691             if(poi_info->clabel)
1692                 g_free(poi_info->clabel);
1693             g_slice_free(PoiInfo, poi_info);
1694         }
1695     }
1696
1697     g_list_free(poi_list);
1698
1699     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1700 }
1701
1702 static void
1703 poi_list_bearing_cell_data_func(
1704         GtkTreeViewColumn *tree_column,
1705         GtkCellRenderer *cell,
1706         GtkTreeModel *tree_model,
1707         GtkTreeIter *iter)
1708 {
1709     gchar buffer[80];
1710     gfloat f;
1711     vprintf("%s()\n", __PRETTY_FUNCTION__);
1712
1713     gtk_tree_model_get(tree_model, iter, POI_BEARING, &f, -1);
1714     snprintf(buffer, sizeof(buffer), "%.1f", f);
1715     g_object_set(cell, "text", buffer, NULL);
1716
1717     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1718 }
1719
1720 static void
1721 poi_list_distance_cell_data_func(
1722         GtkTreeViewColumn *tree_column,
1723         GtkCellRenderer *cell,
1724         GtkTreeModel *tree_model,
1725         GtkTreeIter *iter)
1726 {
1727     gchar buffer[80];
1728     gfloat f;
1729     vprintf("%s()\n", __PRETTY_FUNCTION__);
1730
1731     gtk_tree_model_get(tree_model, iter, POI_DISTANCE, &f, -1);
1732     snprintf(buffer, sizeof(buffer), "%.2f", f);
1733     g_object_set(cell, "text", buffer, NULL);
1734
1735     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
1736 }
1737
1738 static gboolean
1739 poi_list_row_selected(GtkCellRendererToggle *renderer,
1740         gchar *path_string, GtkTreeModel *tree_model)
1741 {
1742     GtkTreeIter iter;
1743     vprintf("%s()\n", __PRETTY_FUNCTION__);
1744
1745     if(gtk_tree_model_get_iter_from_string(tree_model, &iter, path_string))
1746     {
1747         gboolean old_value;
1748         gtk_tree_model_get(tree_model, &iter, POI_SELECTED, &old_value, -1);
1749         gtk_list_store_set(GTK_LIST_STORE(tree_model), &iter,
1750                 POI_SELECTED, !old_value,
1751                 -1);
1752     }
1753
1754     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1755     return TRUE;
1756 }
1757
1758 static gboolean
1759 poi_list_set_category(GtkWidget *widget, PoiListInfo *pli)
1760 {
1761     static GtkWidget *dialog = NULL;
1762     static GtkWidget *cmb_category = NULL;
1763     static GtkWidget *btn_catedit = NULL;
1764     static PoiCategoryEditInfo pcedit;
1765     printf("%s()\n", __PRETTY_FUNCTION__);
1766
1767     if(dialog == NULL)
1768     {
1769         GtkWidget *hbox;
1770         GtkWidget *label;
1771
1772         dialog = gtk_dialog_new_with_buttons(_("Set Category..."),
1773                 GTK_WINDOW(pli->dialog2), GTK_DIALOG_MODAL,
1774                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1775                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1776                 NULL);
1777
1778         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1779                 hbox = gtk_hbox_new(FALSE, 4), FALSE, FALSE, 4);
1780
1781         gtk_box_pack_start(GTK_BOX(hbox),
1782                 label = gtk_label_new(_("Category")),
1783                 FALSE, FALSE, 0);
1784
1785         gtk_box_pack_start(GTK_BOX(hbox),
1786                 cmb_category = poi_create_cat_combo(),
1787                 FALSE, FALSE, 4);
1788
1789         gtk_box_pack_start(GTK_BOX(hbox),
1790                 btn_catedit = gtk_button_new_with_label(
1791                     _("Edit Categories...")),
1792                 FALSE, FALSE, 0);
1793
1794         /* Connect Signals */
1795         pcedit.dialog = dialog;
1796         pcedit.cmb_category = cmb_category;
1797         pcedit.cat_id = -1;
1798         g_signal_connect(G_OBJECT(btn_catedit), "clicked",
1799                 G_CALLBACK(poi_edit_cat), &pcedit);
1800     }
1801
1802     gtk_widget_show_all(dialog);
1803
1804     while(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
1805     {
1806         GtkTreeIter iter;
1807         GtkListStore *store;
1808         gint cat_id;
1809         const gchar *cat_label;
1810
1811         /* Get the text of the chosen category. */
1812         if(!gtk_combo_box_get_active_iter(
1813                 GTK_COMBO_BOX(cmb_category), &iter))
1814         {
1815             popup_error(dialog, _("Please specify a category."));
1816             continue;
1817         }
1818
1819         gtk_tree_model_get(
1820                 gtk_combo_box_get_model(GTK_COMBO_BOX(cmb_category)),
1821                 &iter,
1822                 0, &cat_id,
1823                 1, &cat_label,
1824                 -1);
1825
1826         /* Iterate through the data store and categorize as desired. */
1827         store = GTK_LIST_STORE(gtk_tree_view_get_model(
1828                     GTK_TREE_VIEW(pli->tree_view)));
1829         if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) do
1830         {
1831             PoiInfo poi;
1832             gboolean selected;
1833
1834             memset(&poi, 0, sizeof(poi));
1835
1836             gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1837                     POI_SELECTED, &selected,
1838                     POI_POIID, &(poi.poi_id),
1839                     POI_LAT, &(poi.lat),
1840                     POI_LON, &(poi.lon),
1841                     POI_LABEL, &(poi.label),
1842                     POI_DESC, &(poi.desc),
1843                     -1);
1844
1845             if(selected)
1846             {
1847                 gtk_list_store_set(store, &iter,
1848                     POI_CATID, cat_id,
1849                     POI_CLABEL, cat_label,
1850                     -1);
1851                 /* edit poi */
1852                 if(SQLITE_OK != sqlite3_bind_double(
1853                             _stmt_update_poi, 1, poi.lat) ||
1854                    SQLITE_OK != sqlite3_bind_double(
1855                        _stmt_update_poi, 2, poi.lon) ||
1856                    SQLITE_OK != sqlite3_bind_text(_stmt_update_poi,
1857                        3, poi.label, -1, SQLITE_STATIC) ||
1858                    SQLITE_OK != sqlite3_bind_text(_stmt_update_poi,
1859                        4, poi.desc, -1, SQLITE_STATIC) ||
1860                    SQLITE_OK != sqlite3_bind_int(
1861                        _stmt_update_poi, 5, cat_id) ||
1862                    SQLITE_OK != sqlite3_bind_int(
1863                        _stmt_update_poi, 6, poi.poi_id) ||
1864                    SQLITE_DONE != sqlite3_step(_stmt_update_poi))
1865                 {
1866                     MACRO_BANNER_SHOW_INFO(pli->dialog2,
1867                             _("Error updating POI"));
1868                 }
1869                 sqlite3_reset(_stmt_update_poi);
1870             }
1871         } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
1872
1873         break;
1874     }
1875
1876     map_force_redraw();
1877     gtk_widget_hide(dialog);
1878
1879     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1880     return TRUE;
1881 }
1882
1883 static gboolean
1884 poi_list_select_all(GtkTreeViewColumn *column, PoiListInfo *pli)
1885 {
1886     GtkTreeIter iter;
1887     GtkListStore *store;
1888     printf("%s()\n", __PRETTY_FUNCTION__);
1889
1890     /* Iterate through the data store and select as desired. */
1891     store = GTK_LIST_STORE(gtk_tree_view_get_model(
1892                 GTK_TREE_VIEW(pli->tree_view)));
1893     if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) do
1894     {
1895         gtk_list_store_set(store, &iter,
1896             POI_SELECTED, pli->select_all,
1897             -1);
1898     } while(gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter));
1899
1900     pli->select_all = !pli->select_all;
1901
1902     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1903     return TRUE;
1904 }
1905
1906 static gboolean
1907 poi_list_view(GtkWidget *widget, PoiListInfo *pli)
1908 {
1909     GtkTreeIter iter;
1910     GtkTreeSelection *selection;
1911     GtkListStore *store;
1912     printf("%s()\n", __PRETTY_FUNCTION__);
1913
1914     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pli->tree_view));
1915     store = GTK_LIST_STORE(gtk_tree_view_get_model(
1916                 GTK_TREE_VIEW(pli->tree_view)));
1917
1918     /* Iterate through the data store and import as desired. */
1919     if(gtk_tree_selection_get_selected(selection, NULL, &iter))
1920     {
1921         PoiInfo poi;
1922         memset(&poi, 0, sizeof(poi));
1923
1924         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1925                 POI_POIID, &(poi.poi_id),
1926                 POI_CATID, &(poi.cat_id),
1927                 POI_LAT, &(poi.lat),
1928                 POI_LON, &(poi.lon),
1929                 POI_LABEL, &(poi.label),
1930                 POI_DESC, &(poi.desc),
1931                 POI_CLABEL, &(poi.clabel),
1932                 -1);
1933
1934         if(poi_view_dialog(pli->dialog, &poi))
1935         {
1936             gtk_list_store_set(store, &iter,
1937                     POI_POIID, poi.poi_id,
1938                     POI_CATID, poi.cat_id,
1939                     POI_LAT, poi.lat,
1940                     POI_LON, poi.lon,
1941                     POI_LABEL, poi.label,
1942                     POI_DESC, poi.desc,
1943                     POI_CLABEL, poi.clabel,
1944                     -1);
1945         }
1946         else
1947         {
1948             /* POI was deleted. */
1949             gtk_list_store_remove(store, &iter);
1950         }
1951     }
1952
1953     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1954     return TRUE;
1955 }
1956
1957 static void
1958 poi_list_row_activated(GtkTreeView *tree_view, GtkTreePath *path,
1959         GtkTreeViewColumn *column, PoiListInfo *pli)
1960 {
1961     printf("%s()\n", __PRETTY_FUNCTION__);
1962
1963     if(column != pli->select_column)
1964         poi_list_view(GTK_WIDGET(tree_view), pli);
1965
1966     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
1967 }
1968
1969 static gboolean
1970 poi_list_goto(GtkWidget *widget, PoiListInfo *pli)
1971 {
1972     GtkTreeIter iter;
1973     GtkTreeSelection *selection;
1974     GtkListStore *store;
1975     printf("%s()\n", __PRETTY_FUNCTION__);
1976
1977     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(pli->tree_view));
1978     store = GTK_LIST_STORE(gtk_tree_view_get_model(
1979                 GTK_TREE_VIEW(pli->tree_view)));
1980
1981     /* Iterate through the data store and import as desired. */
1982     if(gtk_tree_selection_get_selected(selection, NULL, &iter))
1983     {
1984         gdouble lat, lon;
1985         Point unit;
1986
1987         gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1988                 POI_LAT, &lat,
1989                 POI_LON, &lon,
1990                 -1);
1991
1992         latlon2unit(lat, lon, unit.unitx, unit.unity);
1993
1994         if(_center_mode > 0)
1995             gtk_check_menu_item_set_active(
1996                     GTK_CHECK_MENU_ITEM(_menu_view_ac_none_item), TRUE);
1997
1998         map_center_unit(unit);
1999     }
2000
2001     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2002     return TRUE;
2003 }
2004
2005 static gboolean
2006 poi_list_delete(GtkWidget *widget, PoiListInfo *pli)
2007 {
2008     GtkWidget *confirm;
2009     printf("%s()\n", __PRETTY_FUNCTION__);
2010
2011     confirm = hildon_note_new_confirmation(
2012             GTK_WINDOW(pli->dialog2), _("Delete selected POI?"));
2013
2014     if(GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
2015     {
2016         GtkTreeIter iter;
2017         GtkListStore *store;
2018         gboolean already_next;
2019         gboolean must_iterate;;
2020
2021         /* Iterate through the data store and import as desired. */
2022         store = GTK_LIST_STORE(gtk_tree_view_get_model(
2023                     GTK_TREE_VIEW(pli->tree_view)));
2024         if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) do
2025         {
2026             gboolean selected;
2027             must_iterate = TRUE;
2028             already_next = FALSE;
2029             gint poi_id;
2030             gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2031                 POI_SELECTED, &selected,
2032                 POI_POIID, &poi_id,
2033                 -1);
2034             if(selected)
2035             {
2036                 /* Delete POI. */
2037                 if(SQLITE_OK != sqlite3_bind_int(_stmt_delete_poi, 1, poi_id)
2038                 || SQLITE_DONE != sqlite3_step(_stmt_delete_poi))
2039                 {
2040                     MACRO_BANNER_SHOW_INFO(pli->dialog2,
2041                             _("Error deleting POI"));
2042                 }
2043                 else
2044                 {
2045                     already_next = gtk_list_store_remove(store, &iter);
2046                     must_iterate = FALSE;
2047                 }
2048                 sqlite3_reset(_stmt_delete_poi);
2049             }
2050         } while(already_next || (must_iterate
2051                 && gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter)));
2052     }
2053
2054     map_force_redraw();
2055
2056     gtk_widget_destroy(confirm);
2057
2058     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2059     return TRUE;
2060 }
2061
2062 static gboolean
2063 poi_list_export_gpx(GtkWidget *widget, PoiListInfo *pli)
2064 {
2065     GnomeVFSHandle *handle;
2066     printf("%s()\n", __PRETTY_FUNCTION__);
2067
2068     if(display_open_file(pli->dialog2, NULL, &handle, NULL, NULL, NULL,
2069                     GTK_FILE_CHOOSER_ACTION_SAVE))
2070     {
2071         gint num_exported = gpx_poi_write(
2072                gtk_tree_view_get_model(GTK_TREE_VIEW(pli->tree_view)), handle);
2073         if(num_exported >= 0)
2074         {
2075             gchar buffer[80];
2076             snprintf(buffer, sizeof(buffer), "%d %s\n", num_exported,
2077                     _("POIs Exported"));
2078             MACRO_BANNER_SHOW_INFO(pli->dialog2, buffer);
2079         }
2080         else
2081             popup_error(pli->dialog2, _("Error writing GPX file."));
2082         gnome_vfs_close(handle);
2083     }
2084
2085     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2086     return TRUE;
2087 }
2088
2089 static gboolean
2090 poi_list_manage_checks(GtkWidget *widget, PoiListInfo *pli)
2091 {
2092     GtkWidget *btn_category;
2093     GtkWidget *btn_delete;
2094     GtkWidget *btn_export_gpx;
2095
2096     printf("%s()\n", __PRETTY_FUNCTION__);
2097
2098     pli->dialog2 = gtk_dialog_new_with_buttons(_("Checked POI Actions..."),
2099             GTK_WINDOW(pli->dialog), GTK_DIALOG_MODAL,
2100             NULL);
2101
2102     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(pli->dialog2)->vbox),
2103             gtk_label_new(_("Select an operation to perform\n"
2104                             "on the POIs that you checked\n"
2105                             "in the POI list.")),
2106                 FALSE, FALSE, 4);
2107
2108     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(pli->dialog2)->vbox),
2109             btn_category = gtk_button_new_with_label(_("Set Category...")),
2110                 FALSE, FALSE, 4);
2111
2112     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(pli->dialog2)->vbox),
2113             btn_delete = gtk_button_new_with_label(_("Delete...")),
2114                 FALSE, FALSE, 4);
2115
2116     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(pli->dialog2)->vbox),
2117             btn_export_gpx = gtk_button_new_with_label(
2118                 _("Export to GPX...")),
2119                 FALSE, FALSE, 4);
2120
2121     gtk_dialog_add_button(GTK_DIALOG(pli->dialog2),
2122             GTK_STOCK_CLOSE, GTK_RESPONSE_ACCEPT);
2123
2124     g_signal_connect(G_OBJECT(btn_category), "clicked",
2125             G_CALLBACK(poi_list_set_category), pli);
2126
2127     g_signal_connect(G_OBJECT(btn_delete), "clicked",
2128             G_CALLBACK(poi_list_delete), pli);
2129
2130     g_signal_connect(G_OBJECT(btn_export_gpx), "clicked",
2131             G_CALLBACK(poi_list_export_gpx), pli);
2132
2133     gtk_widget_show_all(pli->dialog2);
2134
2135     gtk_dialog_run(GTK_DIALOG(pli->dialog2));
2136
2137     gtk_widget_destroy(pli->dialog2);
2138     pli->dialog2 = NULL;
2139
2140     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2141     return TRUE;
2142 }
2143
2144 static gboolean
2145 poi_list_dialog(GtkWidget *parent, gint unitx, gint unity, GList *poi_list)
2146 {
2147     static PoiListInfo pli = { NULL, NULL };
2148     static GtkWidget *scroller;
2149     static GtkWidget *btn_goto;
2150     static GtkWidget *btn_edit;
2151     static GtkWidget *btn_manage_checks;
2152     static GtkListStore *store;
2153     GtkTreeIter iter;
2154     GList *curr;
2155     gdouble src_lat, src_lon;
2156     printf("%s()\n", __PRETTY_FUNCTION__);
2157
2158     if(pli.dialog == NULL)
2159     {
2160         GtkCellRenderer *renderer;
2161         GtkTreeViewColumn *column;
2162
2163         pli.dialog = gtk_dialog_new_with_buttons(_("POI List"),
2164             GTK_WINDOW(parent), GTK_DIALOG_MODAL,
2165                 NULL);
2166
2167         store = gtk_list_store_new(POI_NUM_COLUMNS,
2168                                    G_TYPE_BOOLEAN,/* Selected */
2169                                    G_TYPE_INT,    /* POI ID */
2170                                    G_TYPE_INT,    /* Category ID */
2171                                    G_TYPE_DOUBLE, /* Latitude */
2172                                    G_TYPE_DOUBLE, /* Longitude */
2173                                    G_TYPE_STRING, /* Lat/Lon */
2174                                    G_TYPE_FLOAT,  /* Bearing */
2175                                    G_TYPE_FLOAT,  /* Distance */
2176                                    G_TYPE_STRING, /* POI Label */
2177                                    G_TYPE_STRING, /* POI Desc. */
2178                                    G_TYPE_STRING);/* Category Label */
2179
2180         /* Set up the tree view. */
2181         pli.tree_view = gtk_tree_view_new();
2182         g_object_set(G_OBJECT(pli.tree_view),
2183                 "allow-checkbox-mode", FALSE, NULL);
2184
2185         gtk_tree_selection_set_mode(
2186                 gtk_tree_view_get_selection(GTK_TREE_VIEW(pli.tree_view)),
2187                 GTK_SELECTION_SINGLE);
2188         gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(pli.tree_view), TRUE);
2189
2190         renderer = gtk_cell_renderer_toggle_new();
2191         gtk_cell_renderer_toggle_set_active(GTK_CELL_RENDERER_TOGGLE(renderer),
2192                 TRUE);
2193         g_signal_connect(G_OBJECT(renderer), "toggled",
2194                 G_CALLBACK(poi_list_row_selected), store);
2195         pli.select_column = gtk_tree_view_column_new_with_attributes(
2196                 "*", renderer, "active", POI_SELECTED, NULL);
2197         gtk_tree_view_append_column(GTK_TREE_VIEW(pli.tree_view),
2198                 pli.select_column);
2199         gtk_tree_view_column_set_clickable(pli.select_column, TRUE);
2200         g_signal_connect(G_OBJECT(pli.select_column), "clicked",
2201                 G_CALLBACK(poi_list_select_all), &pli);
2202
2203         renderer = gtk_cell_renderer_combo_new();
2204         column = gtk_tree_view_column_new_with_attributes(
2205                 _("Category"), renderer, "text", POI_CLABEL, NULL);
2206         gtk_tree_view_column_set_sizing(column,GTK_TREE_VIEW_COLUMN_GROW_ONLY);
2207         gtk_tree_view_column_set_sort_column_id(column, POI_CLABEL);
2208         gtk_tree_view_append_column(GTK_TREE_VIEW(pli.tree_view), column);
2209
2210         renderer = gtk_cell_renderer_text_new();
2211         g_object_set(renderer, "xalign", 1.f, NULL);
2212         column = gtk_tree_view_column_new_with_attributes(
2213                 _("Dist."), renderer, "text", POI_DISTANCE, NULL);
2214         gtk_tree_view_column_set_cell_data_func(column, renderer,
2215                 (GtkTreeCellDataFunc)poi_list_distance_cell_data_func,
2216                 NULL, NULL);
2217         gtk_tree_view_column_set_sort_column_id(column, POI_DISTANCE);
2218         gtk_tree_view_append_column(GTK_TREE_VIEW(pli.tree_view), column);
2219
2220         renderer = gtk_cell_renderer_text_new();
2221         g_object_set(renderer, "xalign", 1.f, NULL);
2222         column = gtk_tree_view_column_new_with_attributes(
2223                 _("Bear."), renderer, "text", POI_BEARING, NULL);
2224         gtk_tree_view_column_set_cell_data_func(column, renderer,
2225                 (GtkTreeCellDataFunc)poi_list_bearing_cell_data_func,
2226                 NULL, NULL);
2227         gtk_tree_view_column_set_sort_column_id(column, POI_BEARING);
2228         gtk_tree_view_append_column(GTK_TREE_VIEW(pli.tree_view), column);
2229
2230         renderer = gtk_cell_renderer_text_new();
2231         column = gtk_tree_view_column_new_with_attributes(
2232                 _("Label"), renderer, "text", POI_LABEL, NULL);
2233         gtk_tree_view_column_set_sort_column_id(column, POI_LABEL);
2234         gtk_tree_view_append_column(GTK_TREE_VIEW(pli.tree_view), column);
2235
2236         g_signal_connect(G_OBJECT(pli.tree_view), "row-activated",
2237                 G_CALLBACK(poi_list_row_activated), &pli);
2238
2239         gtk_tree_view_set_model(GTK_TREE_VIEW(pli.tree_view),
2240                 GTK_TREE_MODEL(store));
2241         g_object_unref(G_OBJECT(store));
2242
2243         /* Enable the help button. */
2244 #ifndef LEGACY
2245         hildon_help_dialog_help_enable(
2246 #else
2247         ossohelp_dialog_help_enable(
2248 #endif
2249                 GTK_DIALOG(pli.dialog), HELP_ID_POILIST, _osso);
2250
2251         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(pli.dialog)->action_area),
2252                 btn_goto = gtk_button_new_with_label(_("Go to")));
2253
2254         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(pli.dialog)->action_area),
2255                 btn_edit = gtk_button_new_with_label(_("Edit...")));
2256
2257         gtk_container_add(GTK_CONTAINER(GTK_DIALOG(pli.dialog)->action_area),
2258                 btn_manage_checks = gtk_button_new_with_label(
2259                     _("Checked POI Actions...")));
2260
2261         gtk_dialog_add_button(GTK_DIALOG(pli.dialog),
2262                 GTK_STOCK_CLOSE, GTK_RESPONSE_ACCEPT);
2263
2264         gtk_window_set_default_size(GTK_WINDOW(pli.dialog), 500, 400);
2265
2266         scroller = gtk_scrolled_window_new (NULL, NULL);
2267         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroller),
2268                 GTK_SHADOW_ETCHED_IN);
2269         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroller),
2270                 GTK_POLICY_NEVER,
2271                 GTK_POLICY_AUTOMATIC);
2272         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(pli.dialog)->vbox),
2273                 scroller, TRUE, TRUE, 0);
2274
2275         gtk_container_add(GTK_CONTAINER(scroller), pli.tree_view);
2276
2277         g_signal_connect(G_OBJECT(btn_goto), "clicked",
2278                 G_CALLBACK(poi_list_goto), &pli);
2279
2280         g_signal_connect(G_OBJECT(btn_edit), "clicked",
2281                 G_CALLBACK(poi_list_view), &pli);
2282
2283         g_signal_connect(G_OBJECT(btn_manage_checks), "clicked",
2284                 G_CALLBACK(poi_list_manage_checks), &pli);
2285     }
2286
2287     /* Initialize the tree store. */
2288
2289     gtk_list_store_clear(store);
2290     pli.select_all = FALSE;
2291
2292     unit2latlon(unitx, unity, src_lat, src_lon);
2293
2294     for(curr = poi_list; curr; curr = curr->next)
2295     {
2296         PoiInfo *poi_info = curr->data;
2297         gchar tmp1[LL_FMT_LEN], tmp2[LL_FMT_LEN];
2298
2299         printf("poi: (%f, %f, %s, %s)\n",
2300                 poi_info->lat, poi_info->lon,
2301                 poi_info->label, poi_info->desc);
2302
2303         lat_format(poi_info->lat, tmp1);
2304         lon_format(poi_info->lon, tmp2);
2305
2306         gtk_list_store_append(store, &iter);
2307         gtk_list_store_set(store, &iter,
2308                 POI_SELECTED, TRUE,
2309                 POI_POIID, poi_info->poi_id,
2310                 POI_LAT, poi_info->lat,
2311                 POI_LON, poi_info->lon,
2312                 POI_BEARING, calculate_bearing(src_lat, src_lon,
2313                     poi_info->lat, poi_info->lon),
2314                 POI_DISTANCE, calculate_distance(src_lat,src_lon,
2315                     poi_info->lat, poi_info->lon) * UNITS_CONVERT[_units],
2316                 POI_LABEL, poi_info->label,
2317                 POI_DESC, poi_info->desc,
2318                 POI_CATID, poi_info->cat_id,
2319                 POI_CLABEL, poi_info->clabel,
2320                 -1);
2321     }
2322
2323     gtk_widget_show_all(pli.dialog);
2324
2325     GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(pli.dialog));
2326
2327     map_force_redraw();
2328
2329     gtk_widget_hide(pli.dialog);
2330
2331     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2332     return TRUE;
2333 }
2334
2335 gboolean
2336 poi_import_dialog(gint unitx, gint unity)
2337 {
2338     GtkWidget *dialog = NULL;
2339     gboolean success = FALSE;
2340     printf("%s()\n", __PRETTY_FUNCTION__);
2341
2342     dialog = hildon_file_chooser_dialog_new(GTK_WINDOW(_window),
2343             GTK_FILE_CHOOSER_ACTION_OPEN);
2344
2345     gtk_widget_show_all(dialog);
2346
2347     while(!success && gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_OK)
2348     {
2349         gchar *file_uri_str = NULL;
2350         gchar *bytes = NULL;
2351         gint size;
2352         GnomeVFSResult vfs_result;
2353         GList *poi_list = NULL;
2354
2355         file_uri_str = gtk_file_chooser_get_uri(GTK_FILE_CHOOSER(dialog));
2356
2357         /* Parse the given file as GPX. */
2358         if(GNOME_VFS_OK != (vfs_result = gnome_vfs_read_entire_file(
2359                         file_uri_str, &size, &bytes)))
2360         {
2361             popup_error(dialog, gnome_vfs_result_to_string(vfs_result));
2362         }
2363         else if(gpx_poi_parse(bytes, size, &poi_list))
2364         {
2365             static GtkWidget *cat_dialog = NULL;
2366             static GtkWidget *cmb_category = NULL;
2367             static GtkWidget *btn_catedit = NULL;
2368             static PoiCategoryEditInfo pcedit;
2369
2370             if(!cat_dialog)
2371             {
2372                 GtkWidget *hbox;
2373                 GtkWidget *label;
2374                 cat_dialog = gtk_dialog_new_with_buttons(_("Default Category"),
2375                         GTK_WINDOW(dialog), GTK_DIALOG_MODAL,
2376                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2377                         GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2378                         NULL);
2379
2380                 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cat_dialog)->vbox),
2381                         hbox = gtk_hbox_new(FALSE, 4), FALSE, FALSE, 4);
2382
2383                 gtk_box_pack_start(GTK_BOX(hbox),
2384                         label = gtk_label_new(_("Category")),
2385                         FALSE, FALSE, 0);
2386
2387                 gtk_box_pack_start(GTK_BOX(hbox),
2388                         cmb_category = poi_create_cat_combo(),
2389                         FALSE, FALSE, 4);
2390
2391                 gtk_box_pack_start(GTK_BOX(hbox),
2392                         btn_catedit = gtk_button_new_with_label(
2393                             _("Edit Categories...")),
2394                         FALSE, FALSE, 0);
2395
2396                 /* Connect Signals */
2397                 pcedit.dialog = dialog;
2398                 pcedit.cmb_category = cmb_category;
2399                 pcedit.cat_id = -1;
2400                 g_signal_connect(G_OBJECT(btn_catedit), "clicked",
2401                         G_CALLBACK(poi_edit_cat), &pcedit);
2402             }
2403
2404             gtk_widget_show_all(cat_dialog);
2405
2406             while(GTK_RESPONSE_ACCEPT ==gtk_dialog_run(GTK_DIALOG(cat_dialog)))
2407             {
2408                 if(gtk_combo_box_get_active(GTK_COMBO_BOX(cmb_category)) == -1)
2409                 {
2410                     popup_error(dialog,
2411                             _("Please specify a default category."));
2412                     continue;
2413                 }
2414
2415                 /* Insert the POIs into the database. */
2416                 gint num_inserts = poi_list_insert(dialog,
2417                         poi_list, GTK_COMBO_BOX(cmb_category));
2418
2419                 if(num_inserts)
2420                 {
2421                     /* Hide the dialogs. */
2422                     gtk_widget_hide(cat_dialog);
2423
2424                     /* Create a new dialog with the results. */
2425                     poi_list_dialog(dialog, unitx, unity, poi_list);
2426                     success = TRUE;
2427                 }
2428                 break;
2429             }
2430
2431             gtk_widget_hide(cat_dialog);
2432
2433             poi_list_free(poi_list);
2434         }
2435         else
2436             popup_error(dialog, _("Error parsing GPX file."));
2437
2438         g_free(file_uri_str);
2439         g_free(bytes);
2440     }
2441
2442     /* Hide the dialog. */
2443     gtk_widget_destroy(dialog);
2444
2445     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2446     return success;
2447 }
2448
2449 static gboolean
2450 poi_download_cat_selected(GtkComboBox *cmb_category, GtkEntry *txt_query)
2451 {
2452     GtkTreeIter iter;
2453     printf("%s()\n", __PRETTY_FUNCTION__);
2454
2455     if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(cmb_category), &iter))
2456     {
2457         gchar buffer[BUFFER_SIZE];
2458         GtkWidget *confirm = NULL;
2459         gchar *category = NULL;
2460
2461         gtk_tree_model_get(
2462                 gtk_combo_box_get_model(GTK_COMBO_BOX(cmb_category)), &iter,
2463                 1, &category,
2464                 -1);
2465
2466         if(*gtk_entry_get_text(txt_query))
2467         {
2468             snprintf(buffer, sizeof(buffer), "%s\n  %s",
2469                     _("Overwrite query with the following text?"), category);
2470             confirm = hildon_note_new_confirmation(GTK_WINDOW(_window),buffer);
2471
2472         }
2473
2474         if(confirm == NULL
2475                 || GTK_RESPONSE_OK == gtk_dialog_run(GTK_DIALOG(confirm)))
2476             gtk_entry_set_text(txt_query, category);
2477
2478         if(confirm)
2479             gtk_widget_destroy(confirm);
2480     }
2481
2482     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2483     return TRUE;
2484 }
2485
2486
2487 static gboolean
2488 origin_type_selected(GtkWidget *toggle, OriginToggleInfo *oti)
2489 {
2490     printf("%s()\n", __PRETTY_FUNCTION__);
2491
2492     if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle)))
2493         gtk_widget_set_sensitive(oti->txt_origin, toggle == oti->rad_use_text);
2494
2495     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2496     return TRUE;
2497 }
2498
2499 gboolean
2500 poi_download_dialog(gint unitx, gint unity)
2501 {
2502     static GtkWidget *dialog = NULL;
2503     static GtkWidget *hbox = NULL;
2504     static GtkWidget *table = NULL;
2505     static GtkWidget *table2 = NULL;
2506     static GtkWidget *label = NULL;
2507     static GtkWidget *num_page = NULL;
2508     static GtkWidget *txt_source_url = NULL;
2509     static OriginToggleInfo oti;
2510     static GtkWidget *cmb_category;
2511     printf("%s()\n", __PRETTY_FUNCTION__);
2512
2513     conic_recommend_connected();
2514
2515     if(!dialog)
2516     {
2517         GtkEntryCompletion *origin_comp;
2518
2519         dialog = gtk_dialog_new_with_buttons(_("Download POIs"),
2520                 GTK_WINDOW(_window), GTK_DIALOG_MODAL,
2521                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2522                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2523                 NULL);
2524
2525         /* Enable the help button. */
2526 #ifndef LEGACY
2527         hildon_help_dialog_help_enable(
2528 #else
2529         ossohelp_dialog_help_enable(
2530 #endif
2531                 GTK_DIALOG(dialog), HELP_ID_DOWNPOI, _osso);
2532
2533         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2534                 table = gtk_table_new(4, 4, FALSE), TRUE, TRUE, 0);
2535
2536         /* Source URL. */
2537         gtk_table_attach(GTK_TABLE(table),
2538                 hbox = gtk_hbox_new(FALSE, 4),
2539                 0, 4, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4);
2540         gtk_box_pack_start(GTK_BOX(hbox),
2541                 label = gtk_label_new(_("Source URL")), FALSE, TRUE, 4);
2542         gtk_box_pack_start(GTK_BOX(hbox),
2543                 txt_source_url = gtk_entry_new(), TRUE, TRUE, 4);
2544
2545         /* Auto. */
2546         gtk_table_attach(GTK_TABLE(table),
2547                 oti.rad_use_gps = gtk_radio_button_new_with_label(NULL,
2548                     _("Use GPS Location")),
2549                 0, 1, 1, 2, GTK_FILL, 0, 2, 4);
2550
2551         /* Use End of Route. */
2552         gtk_table_attach(GTK_TABLE(table),
2553                oti.rad_use_route = gtk_radio_button_new_with_label_from_widget(
2554                    GTK_RADIO_BUTTON(oti.rad_use_gps), _("Use End of Route")),
2555                0, 1, 2, 3, GTK_FILL, 0, 2, 4);
2556
2557
2558         gtk_table_attach(GTK_TABLE(table),
2559                 gtk_vseparator_new(),
2560                 1, 2, 1, 3, GTK_FILL, GTK_FILL, 2,4);
2561
2562         /* Category. */
2563         gtk_table_attach(GTK_TABLE(table),
2564                 label = gtk_label_new(_("Category")),
2565                 2, 3, 1, 2, GTK_FILL, 0, 2, 4);
2566         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2567         gtk_table_attach(GTK_TABLE(table),
2568                 cmb_category = poi_create_cat_combo(),
2569                 3, 4, 1, 2, GTK_FILL, 0, 2, 4);
2570
2571         /* Page. */
2572         gtk_table_attach(GTK_TABLE(table),
2573                 label = gtk_label_new(_("Page")),
2574                 2, 3, 2, 3, GTK_FILL, 0, 2, 4);
2575         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2576         gtk_table_attach(GTK_TABLE(table),
2577                 num_page = hildon_number_editor_new(1, 999),
2578                 3, 4, 2, 3, GTK_FILL, 0, 2, 4);
2579
2580
2581         /* Another table for the Origin and Query. */
2582         gtk_table_attach(GTK_TABLE(table),
2583                 table2 = gtk_table_new(2, 2, FALSE),
2584                 0, 4, 3, 4, GTK_EXPAND | GTK_FILL, 0, 2, 4);
2585
2586         /* Origin. */
2587         gtk_table_attach(GTK_TABLE(table2),
2588                 oti.rad_use_text = gtk_radio_button_new_with_label_from_widget(
2589                     GTK_RADIO_BUTTON(oti.rad_use_gps), _("Origin")),
2590                 0, 1, 0, 1, GTK_FILL, 0, 2, 4);
2591         gtk_table_attach(GTK_TABLE(table2),
2592                 oti.txt_origin = gtk_entry_new(),
2593                 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4);
2594         gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_origin), 25);
2595 #ifndef LEGACY
2596         g_object_set(G_OBJECT(oti.txt_origin), "hildon-input-mode",
2597                 HILDON_GTK_INPUT_MODE_FULL, NULL);
2598 #else
2599         g_object_set(G_OBJECT(oti.txt_origin), HILDON_AUTOCAP, FALSE, NULL);
2600 #endif
2601
2602         /* Query. */
2603         gtk_table_attach(GTK_TABLE(table2),
2604                 label = gtk_label_new(_("Query")),
2605                 0, 1, 1, 2, GTK_FILL, 0, 2, 4);
2606         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2607         gtk_table_attach(GTK_TABLE(table2),
2608                 oti.txt_query = gtk_entry_new(),
2609                 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4);
2610         gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_query), 25);
2611 #ifndef LEGACY
2612         g_object_set(G_OBJECT(oti.txt_query), "hildon-input-mode",
2613                 HILDON_GTK_INPUT_MODE_FULL, NULL);
2614 #else
2615         g_object_set(G_OBJECT(oti.txt_query), HILDON_AUTOCAP, FALSE, NULL);
2616 #endif
2617
2618         /* Set up auto-completion. */
2619         origin_comp = gtk_entry_completion_new();
2620         gtk_entry_completion_set_model(origin_comp,GTK_TREE_MODEL(_loc_model));
2621         gtk_entry_completion_set_text_column(origin_comp, 0);
2622         gtk_entry_set_completion(GTK_ENTRY(oti.txt_origin), origin_comp);
2623
2624         g_signal_connect(G_OBJECT(oti.rad_use_gps), "toggled",
2625                           G_CALLBACK(origin_type_selected), &oti);
2626         g_signal_connect(G_OBJECT(oti.rad_use_route), "toggled",
2627                           G_CALLBACK(origin_type_selected), &oti);
2628         g_signal_connect(G_OBJECT(oti.rad_use_text), "toggled",
2629                           G_CALLBACK(origin_type_selected), &oti);
2630
2631         g_signal_connect(G_OBJECT(cmb_category), "changed",
2632                 G_CALLBACK(poi_download_cat_selected), oti.txt_query);
2633     }
2634
2635     /* Initialize fields. */
2636
2637     hildon_number_editor_set_value(HILDON_NUMBER_EDITOR(num_page), 1);
2638
2639     gtk_entry_set_text(GTK_ENTRY(txt_source_url), _poi_dl_url);
2640     if(unity != 0)
2641     {
2642         gchar buffer[80];
2643         gchar strlat[32];
2644         gchar strlon[32];
2645         gdouble lat, lon;
2646
2647         unit2latlon(unitx, unity, lat, lon);
2648
2649         g_ascii_formatd(strlat, 32, "%.06f", lat);
2650         g_ascii_formatd(strlon, 32, "%.06f", lon);
2651         snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
2652
2653         gtk_entry_set_text(GTK_ENTRY(oti.txt_origin), buffer);
2654         gtk_toggle_button_set_active(
2655                 GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
2656     }
2657     /* Else use "End of Route" by default if they have a route. */
2658     else if(_route.head != _route.tail)
2659     {
2660         /* There is no route, so make it the default. */
2661         gtk_widget_set_sensitive(oti.rad_use_route, TRUE);
2662         gtk_toggle_button_set_active(
2663                 GTK_TOGGLE_BUTTON(oti.rad_use_route), TRUE);
2664         gtk_widget_grab_focus(oti.rad_use_route);
2665     }
2666     /* Else use "GPS Location" if they have GPS enabled. */
2667     else
2668     {
2669         /* There is no route, so desensitize "Use End of Route." */
2670         gtk_widget_set_sensitive(oti.rad_use_route, FALSE);
2671         if(_enable_gps)
2672         {
2673             gtk_toggle_button_set_active(
2674                     GTK_TOGGLE_BUTTON(oti.rad_use_gps), TRUE);
2675             gtk_widget_grab_focus(oti.rad_use_gps);
2676         }
2677         /* Else use text. */
2678         else
2679         {
2680             gtk_toggle_button_set_active(
2681                     GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
2682             gtk_widget_grab_focus(oti.txt_origin);
2683         }
2684     }
2685
2686     gtk_widget_show_all(dialog);
2687
2688     while(gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT)
2689     {
2690         gchar origin_buffer[BUFFER_SIZE];
2691         const gchar *source_url, *origin, *query;
2692         gchar *file_uri_str = NULL;
2693         gchar *bytes = NULL;
2694         gint size;
2695         GnomeVFSResult vfs_result;
2696         GList *poi_list = NULL;
2697
2698         source_url = gtk_entry_get_text(GTK_ENTRY(txt_source_url));
2699         if(!strlen(source_url))
2700         {
2701             popup_error(dialog, _("Please specify a source URL."));
2702             continue;
2703         }
2704         else
2705         {
2706             g_free(_poi_dl_url);
2707             _poi_dl_url = g_strdup(source_url);
2708         }
2709
2710         if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti.rad_use_gps)))
2711         {
2712             gchar strlat[32];
2713             gchar strlon[32];
2714             latlon2unit(_gps.lat, _gps.lon, unitx, unity);
2715             g_ascii_formatd(strlat, 32, "%.06f", _gps.lat);
2716             g_ascii_formatd(strlon, 32, "%.06f", _gps.lon);
2717             snprintf(origin_buffer, sizeof(origin_buffer),
2718                     "%s, %s", strlat, strlon);
2719             origin = origin_buffer;
2720         }
2721         else if(gtk_toggle_button_get_active(
2722                     GTK_TOGGLE_BUTTON(oti.rad_use_route)))
2723         {
2724             gchar strlat[32];
2725             gchar strlon[32];
2726             Point *p;
2727             gdouble lat, lon;
2728
2729             /* Use last non-zero route point. */
2730             for(p = _route.tail; !p->unity; p--) { }
2731
2732             unitx = p->unitx;
2733             unity = p->unity;
2734             unit2latlon(p->unitx, p->unity, lat, lon);
2735             g_ascii_formatd(strlat, 32, "%.06f", lat);
2736             g_ascii_formatd(strlon, 32, "%.06f", lon);
2737             snprintf(origin_buffer, sizeof(origin_buffer),
2738                     "%s, %s", strlat, strlon);
2739             origin = origin_buffer;
2740         }
2741         else
2742         {
2743             Point porig;
2744             origin = gtk_entry_get_text(GTK_ENTRY(oti.txt_origin));
2745             if(*origin)
2746             {
2747                 porig = locate_address(dialog, origin);
2748                 if(!porig.unity)
2749                     continue;
2750             }
2751         }
2752
2753         if(!*origin)
2754         {
2755             popup_error(dialog, _("Please specify an origin."));
2756             continue;
2757         }
2758
2759         if(gtk_combo_box_get_active(GTK_COMBO_BOX(cmb_category)) == -1)
2760         {
2761             popup_error(dialog, _("Please specify a default category."));
2762             continue;
2763         }
2764
2765         query = gtk_entry_get_text(GTK_ENTRY(oti.txt_query));
2766         if(!strlen(query))
2767         {
2768             popup_error(dialog, _("Please specify a query."));
2769             continue;
2770         }
2771
2772         /* Construct the URL. */
2773         {
2774             gchar *origin_escaped;
2775             gchar *query_escaped;
2776
2777             origin_escaped = gnome_vfs_escape_string(origin);
2778             query_escaped = gnome_vfs_escape_string(query);
2779             file_uri_str = g_strdup_printf(
2780                     source_url, origin_escaped, query_escaped,
2781                     hildon_number_editor_get_value(
2782                         HILDON_NUMBER_EDITOR(num_page)));
2783             g_free(origin_escaped);
2784             g_free(query_escaped);
2785         }
2786
2787         /* Parse the given file as GPX. */
2788         if(GNOME_VFS_OK != (vfs_result = gnome_vfs_read_entire_file(
2789                         file_uri_str, &size, &bytes)))
2790         {
2791             popup_error(dialog, gnome_vfs_result_to_string(vfs_result));
2792         }
2793         else if(strncmp(bytes, "<?xml", strlen("<?xml")))
2794         {
2795             /* Not an XML document - must be bad locations. */
2796             popup_error(dialog, _("Invalid origin or query."));
2797             printf("bytes: %s\n", bytes);
2798         }
2799         else if(gpx_poi_parse(bytes, size, &poi_list))
2800         {
2801             /* Insert the POIs into the database. */
2802             gint num_inserts = poi_list_insert(dialog, poi_list,
2803                     GTK_COMBO_BOX(cmb_category));
2804
2805             if(num_inserts)
2806             {
2807                 /* Create a new dialog with the results. */
2808                 poi_list_dialog(dialog, unitx, unity, poi_list);
2809             }
2810
2811             poi_list_free(poi_list);
2812         }
2813         else
2814             popup_error(dialog, _("Error parsing GPX file."));
2815
2816         g_free(file_uri_str);
2817         g_free(bytes);
2818
2819         /* Increment the page number for them. */
2820         hildon_number_editor_set_value(HILDON_NUMBER_EDITOR(num_page),
2821             hildon_number_editor_get_value(HILDON_NUMBER_EDITOR(num_page)) +1);
2822     }
2823
2824     /* Hide the dialog. */
2825     gtk_widget_hide(dialog);
2826
2827     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
2828     return TRUE;
2829 }
2830
2831 gboolean
2832 poi_browse_dialog(gint unitx, gint unity)
2833 {
2834     static GtkWidget *dialog = NULL;
2835     static GtkWidget *table = NULL;
2836     static GtkWidget *table2 = NULL;
2837     static GtkWidget *label = NULL;
2838     static GtkWidget *cmb_category = NULL;
2839     static OriginToggleInfo oti;
2840     printf("%s()\n", __PRETTY_FUNCTION__);
2841
2842     if(!dialog)
2843     {
2844         GtkEntryCompletion *origin_comp;
2845
2846         dialog = gtk_dialog_new_with_buttons(_("Browse POIs"),
2847                 GTK_WINDOW(_window), GTK_DIALOG_MODAL,
2848                 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2849                 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
2850                 NULL);
2851
2852         /* Enable the help button. */
2853 #ifndef LEGACY
2854         hildon_help_dialog_help_enable(
2855 #else
2856         ossohelp_dialog_help_enable(
2857 #endif
2858                 GTK_DIALOG(dialog), HELP_ID_BROWSEPOI, _osso);
2859
2860         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
2861                 table = gtk_table_new(3, 4, FALSE), TRUE, TRUE, 0);
2862
2863         /* Auto. */
2864         gtk_table_attach(GTK_TABLE(table),
2865                 oti.rad_use_gps = gtk_radio_button_new_with_label(NULL,
2866                     _("Use GPS Location")),
2867                 0, 1, 0, 1, GTK_FILL, 0, 2, 4);
2868
2869         /* Use End of Route. */
2870         gtk_table_attach(GTK_TABLE(table),
2871                oti.rad_use_route = gtk_radio_button_new_with_label_from_widget(
2872                    GTK_RADIO_BUTTON(oti.rad_use_gps), _("Use End of Route")),
2873                0, 1, 1, 2, GTK_FILL, 0, 2, 4);
2874
2875         gtk_table_attach(GTK_TABLE(table),
2876                 gtk_vseparator_new(),
2877                 1, 2, 0, 2, GTK_FILL, GTK_FILL, 2, 4);
2878
2879         /* Category. */
2880         gtk_table_attach(GTK_TABLE(table),
2881                 label = gtk_label_new(_("Category")),
2882                 2, 3, 0, 1, GTK_FILL, 0, 2, 4);
2883         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2884         gtk_table_attach(GTK_TABLE(table),
2885                 cmb_category = poi_create_cat_combo(),
2886                 3, 4, 0, 1, GTK_FILL, 0, 2, 4);
2887         /* Add an extra, "<any>" category. */
2888         {
2889             GtkTreeIter iter;
2890             GtkListStore *store = GTK_LIST_STORE(gtk_combo_box_get_model(
2891                         GTK_COMBO_BOX(cmb_category)));
2892             gtk_list_store_prepend(store, &iter);
2893             gtk_list_store_set(store, &iter, 0, -1, 1, "<any>", -1);
2894             gtk_combo_box_set_active_iter(GTK_COMBO_BOX(cmb_category), &iter);
2895         }
2896
2897
2898         /* Another table for the Origin and Query. */
2899         gtk_table_attach(GTK_TABLE(table),
2900                 table2 = gtk_table_new(2, 2, FALSE),
2901                 0, 4, 2, 3, GTK_EXPAND | GTK_FILL, 0, 2, 4);
2902
2903         /* Origin. */
2904         gtk_table_attach(GTK_TABLE(table2),
2905                 oti.rad_use_text = gtk_radio_button_new_with_label_from_widget(
2906                     GTK_RADIO_BUTTON(oti.rad_use_gps), _("Origin")),
2907                 0, 1, 0, 1, GTK_FILL, 0, 2, 4);
2908         gtk_table_attach(GTK_TABLE(table2),
2909                 oti.txt_origin = gtk_entry_new(),
2910                 1, 2, 0, 1, GTK_EXPAND | GTK_FILL, 0, 2, 4);
2911         gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_origin), 25);
2912 #ifndef LEGACY
2913         g_object_set(G_OBJECT(oti.txt_origin), "hildon-input-mode",
2914                 HILDON_GTK_INPUT_MODE_FULL, NULL);
2915 #else
2916         g_object_set(G_OBJECT(oti.txt_origin), HILDON_AUTOCAP, FALSE, NULL);
2917 #endif
2918
2919         /* Destination. */
2920         gtk_table_attach(GTK_TABLE(table2),
2921                 label = gtk_label_new(_("Query")),
2922                 0, 1, 1, 2, GTK_FILL, 0, 2, 4);
2923         gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
2924         gtk_table_attach(GTK_TABLE(table2),
2925                 oti.txt_query = gtk_entry_new(),
2926                 1, 2, 1, 2, GTK_EXPAND | GTK_FILL, 0, 2, 4);
2927         gtk_entry_set_width_chars(GTK_ENTRY(oti.txt_query), 25);
2928 #ifndef LEGACY
2929         g_object_set(G_OBJECT(oti.txt_query), "hildon-input-mode",
2930                 HILDON_GTK_INPUT_MODE_FULL, NULL);
2931 #else
2932         g_object_set(G_OBJECT(oti.txt_query), HILDON_AUTOCAP, FALSE, NULL);
2933 #endif
2934
2935         /* Set up auto-completion. */
2936         origin_comp = gtk_entry_completion_new();
2937         gtk_entry_completion_set_model(origin_comp,GTK_TREE_MODEL(_loc_model));
2938         gtk_entry_completion_set_text_column(origin_comp, 0);
2939         gtk_entry_set_completion(GTK_ENTRY(oti.txt_origin), origin_comp);
2940
2941         g_signal_connect(G_OBJECT(oti.rad_use_gps), "toggled",
2942                           G_CALLBACK(origin_type_selected), &oti);
2943         g_signal_connect(G_OBJECT(oti.rad_use_route), "toggled",
2944                           G_CALLBACK(origin_type_selected), &oti);
2945         g_signal_connect(G_OBJECT(oti.rad_use_text), "toggled",
2946                           G_CALLBACK(origin_type_selected), &oti);
2947     }
2948
2949     /* Initialize fields. */
2950
2951     if(unity != 0)
2952     {
2953         gchar buffer[80];
2954         gchar strlat[32];
2955         gchar strlon[32];
2956         gdouble lat, lon;
2957
2958         unit2latlon(unitx, unity, lat, lon);
2959
2960         g_ascii_formatd(strlat, 32, "%.06f", lat);
2961         g_ascii_formatd(strlon, 32, "%.06f", lon);
2962         snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
2963
2964         gtk_entry_set_text(GTK_ENTRY(oti.txt_origin), buffer);
2965         gtk_toggle_button_set_active(
2966                 GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
2967     }
2968     /* Else use "End of Route" by default if they have a route. */
2969     else if(_route.head != _route.tail)
2970     {
2971         /* There is no route, so make it the default. */
2972         gtk_widget_set_sensitive(oti.rad_use_route, TRUE);
2973         gtk_toggle_button_set_active(
2974                 GTK_TOGGLE_BUTTON(oti.rad_use_route), TRUE);
2975         gtk_widget_grab_focus(oti.rad_use_route);
2976     }
2977     /* Else use "GPS Location" if they have GPS enabled. */
2978     else
2979     {
2980         /* There is no route, so desensitize "Use End of Route." */
2981         gtk_widget_set_sensitive(oti.rad_use_route, FALSE);
2982         if(_enable_gps)
2983         {
2984             gtk_toggle_button_set_active(
2985                     GTK_TOGGLE_BUTTON(oti.rad_use_gps), TRUE);
2986             gtk_widget_grab_focus(oti.rad_use_gps);
2987         }
2988         /* Else use text. */
2989         else
2990         {
2991             gtk_toggle_button_set_active(
2992                     GTK_TOGGLE_BUTTON(oti.rad_use_text), TRUE);
2993             gtk_widget_grab_focus(oti.txt_origin);
2994         }
2995     }
2996
2997     gtk_widget_show_all(dialog);
2998
2999     while(gtk_dialog_run(GTK_DIALOG(dialog)) ==GTK_RESPONSE_ACCEPT)
3000     {
3001         gchar buffer[BUFFER_SIZE];
3002         const gchar *origin, *query;
3003         gdouble lat, lon;
3004         GList *poi_list = NULL;
3005         gint cat_id;
3006         gboolean is_cat = FALSE;
3007         sqlite3_stmt *stmt;
3008
3009         if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(oti.rad_use_gps)))
3010         {
3011             gchar strlat[32];
3012             gchar strlon[32];
3013             latlon2unit(_gps.lat, _gps.lon, unitx, unity);
3014             g_ascii_formatd(strlat, 32, "%.06f", _gps.lat);
3015             g_ascii_formatd(strlon, 32, "%.06f", _gps.lon);
3016             snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
3017             origin = buffer;
3018         }
3019         else if(gtk_toggle_button_get_active(
3020                     GTK_TOGGLE_BUTTON(oti.rad_use_route)))
3021         {
3022             gchar strlat[32];
3023             gchar strlon[32];
3024             Point *p;
3025             gdouble lat, lon;
3026
3027             /* Use last non-zero route point. */
3028             for(p = _route.tail; !p->unity; p--) { }
3029
3030             unitx = p->unitx;
3031             unity = p->unity;
3032             unit2latlon(p->unitx, p->unity, lat, lon);
3033             g_ascii_formatd(strlat, 32, "%.06f", lat);
3034             g_ascii_formatd(strlon, 32, "%.06f", lon);
3035             snprintf(buffer, sizeof(buffer), "%s, %s", strlat, strlon);
3036             origin = buffer;
3037         }
3038         else
3039         {
3040             Point porig;
3041             origin = gtk_entry_get_text(GTK_ENTRY(oti.txt_origin));
3042             porig = locate_address(dialog, origin);
3043             if(!porig.unity)
3044                 continue;
3045         }
3046
3047         if(!strlen(origin))
3048         {
3049             popup_error(dialog, _("Please specify an origin."));
3050             continue;
3051         }
3052
3053         /* Check if we're doing a category search. */
3054         {
3055             GtkTreeIter iter;
3056             if(gtk_combo_box_get_active_iter(
3057                     GTK_COMBO_BOX(cmb_category), &iter))
3058             {
3059                 gtk_tree_model_get(
3060                         gtk_combo_box_get_model(GTK_COMBO_BOX(cmb_category)),
3061                         &iter, 0, &cat_id, -1);
3062                 if(cat_id >= 0)
3063                 {
3064                     is_cat = TRUE;
3065                 }
3066             }
3067         }
3068
3069         query = g_strdup_printf("%%%s%%",
3070                 gtk_entry_get_text(GTK_ENTRY(oti.txt_query)));
3071
3072         unit2latlon(unitx, unity, lat, lon);
3073
3074         if(is_cat)
3075         {
3076             if(SQLITE_OK != sqlite3_bind_int(_stmt_browsecat_poi, 1, cat_id) ||
3077                SQLITE_OK != sqlite3_bind_text(_stmt_browsecat_poi, 2, query,
3078                    -1, g_free) ||
3079                SQLITE_OK != sqlite3_bind_double(_stmt_browsecat_poi, 3, lat) ||
3080                SQLITE_OK != sqlite3_bind_double(_stmt_browsecat_poi, 4, lon))
3081             {
3082                 g_printerr("Failed to bind values for _stmt_browsecat_poi\n");
3083                 continue;
3084             }
3085             stmt = _stmt_browsecat_poi;
3086         }
3087         else
3088         {
3089             if(SQLITE_OK != sqlite3_bind_text(_stmt_browse_poi, 1, query,
3090                         -1, g_free) ||
3091                SQLITE_OK != sqlite3_bind_double(_stmt_browse_poi, 2, lat) ||
3092                SQLITE_OK != sqlite3_bind_double(_stmt_browse_poi, 3, lon))
3093             {
3094                 g_printerr("Failed to bind values for _stmt_browse_poi\n");
3095                 continue;
3096             }
3097             stmt = _stmt_browse_poi;
3098         }
3099
3100         while(SQLITE_ROW == sqlite3_step(stmt))
3101         {
3102             PoiInfo *poi = g_slice_new(PoiInfo);
3103             poi->poi_id = sqlite3_column_int(stmt, 0);
3104             poi->cat_id = sqlite3_column_int(stmt, 1);
3105             poi->lat = sqlite3_column_double(stmt, 2);
3106             poi->lon = sqlite3_column_double(stmt, 3);
3107             poi->label =g_strdup(sqlite3_column_text(stmt, 4));
3108             poi->desc = g_strdup(sqlite3_column_text(stmt, 5));
3109             poi->clabel=g_strdup(sqlite3_column_text(stmt, 6));
3110             poi_list = g_list_prepend(poi_list, poi);
3111         }
3112         sqlite3_reset(stmt);
3113
3114         if(poi_list)
3115         {
3116             /* Create a new dialog with the results. */
3117             poi_list_dialog(dialog, unitx, unity, poi_list);
3118             poi_list_free(poi_list);
3119         }
3120         else
3121             popup_error(dialog, _("No POIs found."));
3122     }
3123
3124     map_force_redraw();
3125
3126     /* Hide the dialog. */
3127     gtk_widget_hide(dialog);
3128
3129     vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);
3130     return TRUE;
3131 }
3132
3133 /**
3134  * Render all the POI data.  This should be done before rendering track data.
3135  */
3136 void
3137 map_render_poi()
3138 {
3139     gint unitx, unity;
3140     gdouble lat1, lat2, lon1, lon2;
3141     gchar buffer[100];
3142     gint poix, poiy;
3143     GdkPixbuf *pixbuf = NULL;
3144     GError *error = NULL;
3145     printf("%s()\n", __PRETTY_FUNCTION__);
3146
3147     if(_poi_db && _poi_zoom > _zoom)
3148     {
3149         gint diag_offset = pixel2unit(MAX(_view_width_pixels,
3150                     _view_height_pixels) / 2);
3151         buf2unit(0, _view_height_pixels, unitx, unity);
3152         unitx = _center.unitx - diag_offset;
3153         unity = _center.unity + diag_offset;
3154         unit2latlon(unitx, unity, lat1, lon1);
3155         unitx = _center.unitx + diag_offset;
3156         unity = _center.unity - diag_offset;
3157         unit2latlon(unitx, unity, lat2, lon2);
3158
3159         if(SQLITE_OK != sqlite3_bind_double(_stmt_select_poi, 1, lat1) ||
3160            SQLITE_OK != sqlite3_bind_double(_stmt_select_poi, 2, lat2) ||
3161            SQLITE_OK != sqlite3_bind_double(_stmt_select_poi, 3, lon1) ||
3162            SQLITE_OK != sqlite3_bind_double(_stmt_select_poi, 4, lon2))
3163         {
3164             g_printerr("Failed to bind values for _stmt_select_poi\n");
3165             return;
3166         }
3167
3168         while(SQLITE_ROW == sqlite3_step(_stmt_select_poi))
3169         {
3170             lat1 = sqlite3_column_double(_stmt_select_poi, 0);
3171             lon1 = sqlite3_column_double(_stmt_select_poi, 1);
3172             gchar *poi_label = g_utf8_strdown(sqlite3_column_text(
3173                     _stmt_select_poi, 3), -1);
3174             gchar *cat_label = g_utf8_strdown(sqlite3_column_text(
3175                     _stmt_select_poi, 6), -1);
3176
3177             latlon2unit(lat1, lon1, unitx, unity);
3178             unit2buf(unitx, unity, poix, poiy);
3179
3180             /* Try to get icon for specific POI first. */
3181             snprintf(buffer, sizeof(buffer), "%s/%s.jpg",
3182                     _poi_db_dirname, poi_label);
3183             pixbuf = gdk_pixbuf_new_from_file(buffer, &error);
3184             if(error)
3185             {
3186                 /* No icon for specific POI - try for category. */
3187                 error = NULL;
3188                 snprintf(buffer, sizeof(buffer), "%s/%s.jpg",
3189                         _poi_db_dirname, cat_label);
3190                 pixbuf = gdk_pixbuf_new_from_file(buffer, &error);
3191             }
3192             if(error)
3193             {
3194                 /* No icon for POI or for category.
3195                  * Try default POI icon file. */
3196                 error = NULL;
3197                 snprintf(buffer, sizeof(buffer), "%s/poi.jpg",
3198                         _poi_db_dirname);
3199                 pixbuf = gdk_pixbuf_new_from_file(buffer, &error);
3200             }
3201             if(error)
3202             {
3203                 /* No icon for POI or for category or default POI icon file.
3204                    Draw default purple square. */
3205                 error = NULL;
3206                 gdk_draw_rectangle(_map_pixmap, _gc[COLORABLE_POI], TRUE,
3207                         poix - (gint)(1.5f * _draw_width),
3208                         poiy - (gint)(1.5f * _draw_width),
3209                         3 * _draw_width,
3210                         3 * _draw_width);
3211             }
3212             else
3213             {
3214                 /* We found an icon to draw. */
3215                 gdk_draw_pixbuf(
3216                         _map_pixmap,
3217                         _gc[COLORABLE_POI],
3218                         pixbuf,
3219                         0, 0,
3220                         poix - gdk_pixbuf_get_width(pixbuf) / 2,
3221                         poiy - gdk_pixbuf_get_height(pixbuf) / 2,
3222                         -1,-1,
3223                         GDK_RGB_DITHER_NONE, 0, 0);
3224                 g_object_unref(pixbuf);
3225             }
3226
3227             g_free(poi_label);
3228             g_free(cat_label);
3229         }
3230         sqlite3_reset(_stmt_select_poi);
3231     }
3232
3233     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
3234 }
3235
3236 void
3237 poi_destroy()
3238 {
3239     printf("%s()\n", __PRETTY_FUNCTION__);
3240
3241     if(_poi_db) 
3242     { 
3243         sqlite3_close(_poi_db); 
3244         _poi_db = NULL; 
3245     }
3246
3247     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
3248 }