Contents of /trunk/src/mm_poi.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 233 - (hide annotations)
Wed Dec 9 19:45:36 2009 UTC (14 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 17164 byte(s)
File selection fremantleized
1 harbaum 1 /*
2     * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3     *
4     * This file is part of GPXView.
5     *
6     * GPXView is free software: you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation, either version 3 of the License, or
9     * (at your option) any later version.
10     *
11     * GPXView is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with GPXView. If not, see <http://www.gnu.org/licenses/>.
18     */
19    
20     #include "gpxview.h"
21    
22     #include <math.h>
23     #include <sqlite3.h>
24    
25     static const char *cache_type_icons[] = {
26     "traditional", "multi", "mystery", "virtual",
27     "webcam", "event", "letterbox", "earthcache",
28     "wherigo", "megaevent", "cito", "" };
29    
30     static const char *cache_type_str[] = {
31     "Traditional", "Multi", "Mystery","Virtual", "Webcam", "Event",
32     "Letterbox", "Earthcache", "Wherigo","Megaevent", "CITO",""};
33    
34     static const char *cache_type_desc[] = {
35     "Traditional Caches", "Multi-Caches", "Unknown Caches",
36     "Virtual Caches", "Webcam Caches", "Event Caches",
37     "Letterbox Hybrids", "Earthcaches", "Wherigo Caches",
38     "Mega-Event Caches", "Cache In Trash Out Events",""};
39    
40     static const char *mode_str[] = {
41     "???", "the cache listing", "the list of caches", "all GPX files" };
42    
43     static int callback(void *NotUsed, int argc, char **argv, char **azColName){
44     int i;
45     NotUsed=0;
46    
47     for(i=0; i<argc; i++){
48     printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
49     }
50     printf("\n");
51     return 0;
52     }
53    
54     char *escape(char *str) {
55     if(!str) return NULL;
56    
57     char *result = malloc(strlen(str)+1);
58     char *p = result;
59    
60     while(*str) {
61     if(*str != '\'') *p++=*str++;
62     else { *p++='`'; str++; }
63     }
64     *p = 0;
65    
66     return result;
67     }
68    
69     /* export a single cache to the database */
70     static void cache_export(sqlite3 *db, int id, cache_t *cache) {
71     char *zErrMsg = 0;
72     char cmd[256];
73    
74     // printf("exporting %s: %s\n", cache->id, cache->name);
75    
76     char *desc = escape(cache->id); // short_description
77     if(!desc) desc = strdup(_("<none>"));
78    
79     char *name = escape(cache->name);
80     if(!name) desc = strdup(_("<none>"));
81    
82     pos_t pos = gpx_cache_pos(cache);
83     char strlon[32], strlat[32];
84     g_ascii_dtostr(strlat, sizeof(strlat), pos.lat);
85     g_ascii_dtostr(strlon, sizeof(strlon), pos.lon);
86    
87     snprintf(cmd, sizeof(cmd),
88     "insert into poi values (%d,%s,%s,'%s','%s',%d)",
89     id, strlat, strlon, name, desc, cache->type+12);
90    
91     if(sqlite3_exec(db,cmd, callback, 0, &zErrMsg) != SQLITE_OK)
92     errorf(_("Creating POI entry:\n\n%s"), zErrMsg);
93    
94     if(desc) free(desc);
95     if(name) free(name);
96     }
97    
98     /* add a cache to the chain of caches to be exported */
99     static void chain_cache(gpx_t *gpx, cache_t *cache) {
100     cache_t **cur = &gpx->cache;
101    
102     /* search end of chain */
103     while(*cur) {
104     if(strcmp(cache->id, (*cur)->id) == 0)
105     return;
106    
107     cur = &(*cur)->next;
108     }
109    
110     *cur = malloc(sizeof(cache_t));
111     if(!(*cur)) return;
112    
113     memcpy(*cur, cache, sizeof(cache_t));
114     (*cur)->next = NULL;
115     }
116    
117     static gpx_t *export_list_create(appdata_t *appdata, int *mode) {
118     gpx_t *gpx = g_new0(gpx_t,1);
119     *mode = 0;
120    
121     if(!gpx) return gpx;
122    
123     #ifdef USE_MAEMO
124     if(appdata->cur_cache) {
125     printf("cache display, exporting cache only!\n");
126     /* appdata->cur_cache */
127     chain_cache(gpx, appdata->cur_cache);
128     *mode = 1;
129     } else if(appdata->search_results) {
130     printf("search result display, exporting entire search result\n");
131     cache_t *cache = appdata->search_results->cache;
132     while(cache) { chain_cache(gpx, cache); cache = cache->next; }
133     *mode = 2;
134     } else if(appdata->cur_gpx) {
135     printf("cachelist display, exporting entire cachelist\n");
136     cache_t *cache = appdata->cur_gpx->cache;
137     while(cache) { chain_cache(gpx, cache); cache = cache->next; }
138     *mode = 2;
139     } else
140     #endif
141     {
142     printf("gpxlist display, exporting all caches\n");
143     gpx_t *lgpx = appdata->gpx;
144     while(lgpx) {
145     /* make sure notes are imported */
146     if(!lgpx->notes_loaded) {
147     notes_load_all(appdata, lgpx);
148     lgpx->notes_loaded = TRUE;
149     }
150    
151     cache_t *cache = lgpx->cache;
152     while(cache) { chain_cache(gpx, cache); cache = cache->next; }
153     lgpx = lgpx->next;
154     }
155     *mode = 3;
156     }
157    
158     return gpx;
159     }
160    
161     static void export_list_free(gpx_t *gpx) {
162     printf("freeing export list\n");
163    
164     cache_t *cache = gpx->cache;
165     while(cache) {
166     cache_t *tmp = cache;
167     cache = cache->next;
168     free(tmp);
169     }
170     free(gpx);
171     }
172    
173     static int export_list_count(appdata_t *appdata, gpx_t *gpx) {
174     pos_t *refpos = get_pos(appdata);
175     int cnt = 0;
176    
177     cache_t *cache = gpx->cache;
178     while(cache) {
179     /* no disabled/archived if requested */
180     if(!(appdata->mmpoi_dont_export_disabled &&
181     (!cache->available || cache->archived))) {
182    
183     /* no found if requested */
184     if(!(appdata->mmpoi_dont_export_found &&
185     cache->notes && cache->notes->found)) {
186    
187     if(appdata->mmpoi_use_radius && !isnan(appdata->mmpoi_radius)) {
188     /* count all caches within given radius around current position */
189     if(gpx_pos_get_distance(*refpos, gpx_cache_pos(cache), 0) <
190     appdata->mmpoi_radius)
191     cnt++;
192     } else
193     cnt++;
194     }
195    
196     }
197    
198     cache = cache->next;
199     }
200     return cnt;
201     }
202    
203     typedef struct {
204     GtkWidget *use_radius;
205     GtkWidget *no_found;
206     GtkWidget *no_disabled;
207     GtkWidget *entry;
208     GtkWidget *info_label;
209     GtkWidget *dialog;
210     appdata_t *appdata;
211     guint handler_id;
212     int mode;
213     gpx_t *gpx;
214     #ifdef USE_MAEMO
215     GtkWidget *launch;
216     #endif
217     } export_context_t;
218    
219     static gboolean export_list_update(gpointer data) {
220     char str[256];
221     export_context_t *context = (export_context_t*)data;
222    
223     printf("updating list ...\n");
224    
225     int cnum = export_list_count(context->appdata, context->gpx);
226     printf("About to export %d caches\n", cnum);
227    
228     snprintf(str, sizeof(str),
229     _("This will export %d caches from %s."),
230     cnum, _(mode_str[context->mode]));
231    
232     gtk_label_set_text(GTK_LABEL(context->info_label), str);
233    
234     return FALSE;
235     }
236    
237     /* Our usual callback function */
238     static void export_update(GtkWidget *widget, gpointer data) {
239     export_context_t *context = (export_context_t *)data;
240    
241 harbaum 233 if(check_button_get_active(context->use_radius)) {
242 harbaum 1 textbox_enable(context->entry);
243     context->appdata->mmpoi_use_radius = TRUE;
244     } else {
245     textbox_disable(context->entry);
246     context->appdata->mmpoi_use_radius = FALSE;
247     }
248    
249     context->appdata->mmpoi_dont_export_found =
250 harbaum 233 toggle_button_get_active(context->no_found);
251 harbaum 1
252     context->appdata->mmpoi_dont_export_disabled =
253 harbaum 233 toggle_button_get_active(context->no_disabled);
254 harbaum 1
255     export_list_update(context);
256     }
257    
258     static void export_radius_modified(GtkWidget *widget, gpointer data ) {
259     export_context_t *context = (export_context_t*)data;
260    
261 harbaum 233 context->appdata->mmpoi_radius =
262     dist_entry_get(context->entry, context->appdata->imperial);
263 harbaum 1
264     printf("Distance is %.2f km\n", context->appdata->mmpoi_radius);
265    
266     if(context->handler_id) {
267     printf("resetting running timer\n");
268     gtk_timeout_remove(context->handler_id);
269     context->handler_id = 0;
270     }
271    
272     if(!isnan(context->appdata->mmpoi_radius))
273     context->handler_id =
274     gtk_timeout_add(1000, export_list_update, context);
275     }
276    
277     /* Our usual callback function */
278     static void on_cancel(GtkWidget *widget, gpointer data) {
279     int *flag = (int*)data;
280     *flag = TRUE;
281     }
282    
283     void mmpoi_export(appdata_t *appdata) {
284     sqlite3 *db;
285     char *zErrMsg = 0;
286     char cmd[256];
287     int rc, i;
288     char *old_mmpoi_path = strdup(appdata->mmpoi_path);
289    
290     export_context_t context;
291     memset(&context, 0, sizeof(export_context_t));
292     context.appdata = appdata;
293    
294     printf("export poi\n");
295    
296     /* first create a new faked gpx file containing all caches to */
297     /* be exported */
298     context.gpx = export_list_create(appdata, &context.mode);
299     if(!context.gpx) {
300     errorf(_("Out of memory"));
301     return;
302     }
303    
304     /* --------- do confirmation dialog ----------- */
305     context.dialog = gtk_dialog_new_with_buttons(_("Maemo Mapper POI export"),
306     GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
307     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
308     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
309     NULL);
310    
311     #if defined(USE_MAEMO) && defined(HILDON_HELP)
312     hildon_help_dialog_help_enable(GTK_DIALOG(context.dialog),
313     HELP_ID_EXPORT, appdata->osso_context);
314     #endif
315    
316     GtkWidget *vbox = gtk_vbox_new(FALSE,0);
317    
318     /* ------------------ radius limit gui ------------------ */
319    
320     GtkWidget *label, *hbox = gtk_hbox_new(FALSE,2);
321 harbaum 233 context.use_radius = check_button_new_with_label(
322 harbaum 1 _("Limit export radius to:"));
323     context.entry = dist_entry_new(appdata->mmpoi_radius, appdata->imperial);
324    
325     gtk_box_pack_start_defaults(GTK_BOX(hbox), context.use_radius);
326     gtk_box_pack_start_defaults(GTK_BOX(hbox), context.entry);
327     gtk_box_pack_start_defaults(GTK_BOX(vbox), hbox);
328    
329 harbaum 233 check_button_set_active(context.use_radius, appdata->mmpoi_use_radius);
330 harbaum 1
331     /* Connect the "clicked" signal of the button to our callback */
332     gtk_signal_connect (GTK_OBJECT(context.use_radius), "clicked",
333     GTK_SIGNAL_FUNC(export_update), (gpointer)&context);
334    
335     g_signal_connect(G_OBJECT(context.entry), "changed",
336     G_CALLBACK(export_radius_modified), (gpointer)&context);
337    
338     /* ------------------ don't export found/disabled/archived -------------- */
339     hbox = gtk_hbox_new(FALSE,2);
340    
341     label = gtk_label_new(_("Don't export"));
342     gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f);
343 harbaum 233 GtkWidget *ihbox = gtk_hbox_new(FALSE,0);
344     context.no_found = toggle_button_new_with_label(_("found"));
345     context.no_disabled = toggle_button_new_with_label(_("disabled/archived"));
346 harbaum 1
347 harbaum 233 toggle_button_set_active(context.no_found,
348     appdata->mmpoi_dont_export_found);
349 harbaum 1
350 harbaum 233 toggle_button_set_active(context.no_disabled,
351     appdata->mmpoi_dont_export_disabled);
352 harbaum 1
353     gtk_box_pack_start_defaults(GTK_BOX(hbox), label);
354 harbaum 233 gtk_box_pack_start_defaults(GTK_BOX(ihbox), context.no_found);
355     gtk_box_pack_start_defaults(GTK_BOX(ihbox), context.no_disabled);
356     gtk_box_pack_start_defaults(GTK_BOX(hbox), ihbox);
357 harbaum 1
358     gtk_signal_connect (GTK_OBJECT(context.no_found), "clicked",
359     GTK_SIGNAL_FUNC(export_update), (gpointer)&context);
360     gtk_signal_connect (GTK_OBJECT(context.no_disabled), "clicked",
361     GTK_SIGNAL_FUNC(export_update), (gpointer)&context);
362    
363     gtk_box_pack_start_defaults(GTK_BOX(vbox),hbox);
364    
365     /* ------------------ info text -------------- */
366    
367     context.info_label = gtk_label_new("");
368     gtk_label_set_line_wrap_mode(GTK_LABEL(context.info_label), PANGO_WRAP_WORD);
369     gtk_label_set_line_wrap(GTK_LABEL(context.info_label), TRUE);
370     gtk_misc_set_alignment(GTK_MISC(context.info_label), 0.f, 0.5f);
371     gtk_box_pack_start_defaults(GTK_BOX(vbox), context.info_label);
372    
373     export_update(NULL, &context);
374    
375     #ifdef USE_MAEMO
376     gtk_box_pack_start_defaults(GTK_BOX(vbox),
377 harbaum 233 context.launch = check_button_new_with_label(
378 harbaum 1 _("Launch Maemo Mapper after export")));
379 harbaum 233 check_button_set_active(context.launch, !appdata->mmpoi_dontlaunch);
380 harbaum 1 #endif
381    
382     /* ------------------ path/file ------------------ */
383     gtk_box_pack_start_defaults(GTK_BOX(vbox), gtk_hseparator_new());
384    
385 harbaum 233 gtk_box_pack_start_defaults(GTK_BOX(vbox),
386     export_file(_("Save POI database"), &appdata->mmpoi_path));
387 harbaum 1
388     /* ------------------ info ------------------ */
389    
390     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context.dialog)->vbox), vbox);
391    
392     gtk_widget_show_all(context.dialog);
393    
394     if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(context.dialog))) {
395     #ifdef USE_MAEMO
396     appdata->mmpoi_dontlaunch =
397 harbaum 233 !check_button_get_active(context.launch);
398 harbaum 1 #endif
399    
400     /* remove existing database */
401     remove(appdata->mmpoi_path);
402    
403     if(checkdir(appdata->mmpoi_path) != 0)
404     errorf(_("Unable to access or create output directory!"));
405     else {
406    
407     /* build a progress dialog which can even be stopped */
408    
409     GtkWidget *pbar, *wait_dialog = gtk_dialog_new();
410     gtk_dialog_set_has_separator(GTK_DIALOG(wait_dialog), FALSE);
411     gtk_window_set_default_size(GTK_WINDOW(wait_dialog), 250, 100);
412 harbaum 7 gtk_window_set_title(GTK_WINDOW(wait_dialog), _("Exporting"));
413 harbaum 1 gtk_window_set_modal(GTK_WINDOW(wait_dialog), TRUE);
414     gtk_window_set_transient_for(GTK_WINDOW(wait_dialog),
415     GTK_WINDOW(context.dialog));
416     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(wait_dialog)->vbox),
417     pbar = gtk_progress_bar_new());
418    
419 harbaum 233 GtkWidget *button = button_new_with_label(_("Cancel"));
420 harbaum 1 int cancelled = 0;
421     gtk_signal_connect(GTK_OBJECT(button), "clicked",
422     GTK_SIGNAL_FUNC(on_cancel), (gpointer)&cancelled);
423     gtk_container_add(GTK_CONTAINER(GTK_DIALOG(wait_dialog)->action_area),
424     button);
425    
426     gtk_widget_show_all(wait_dialog);
427    
428     /* ---------------------- copy icons --------------------- */
429     for(i=0;cache_type_icons[i][0];i++) {
430     char *src = malloc(strlen(ICONPATH)+strlen(cache_type_icons[i])+
431 harbaum 179 strlen("cache_type_.png")+1);
432     sprintf(src, ICONPATH "cache_type_%s.png", cache_type_icons[i]);
433 harbaum 1 if(!g_file_test(src, G_FILE_TEST_EXISTS))
434 harbaum 179 sprintf(src, "./icons/cache_type_%s.png", cache_type_icons[i]);
435 harbaum 1
436     if(g_file_test(src, G_FILE_TEST_EXISTS)) {
437     char *dest = malloc(strlen(appdata->mmpoi_path)+
438     strlen(cache_type_icons[i])+strlen(".jpg")+1);
439     strcpy(dest, appdata->mmpoi_path);
440     *(strrchr(dest, '/')+1) = 0;
441     sprintf(dest+strlen(dest), "%s.jpg", cache_type_icons[i]);
442    
443     /* read source file */
444     FILE *file = fopen(src, "rb");
445     fseek(file, 0, SEEK_END);
446     int len = ftell(file);
447     fseek(file, 0, SEEK_SET);
448     char *buffer = malloc(len);
449     fread(buffer, 1l, len, file);
450     fclose(file);
451    
452     /* write destination file */
453     file = fopen(dest, "wb");
454     fwrite(buffer, 1l, len, file);
455     fclose(file);
456    
457     free(buffer);
458     free(dest);
459     }
460    
461     free(src);
462     }
463    
464     /* ---------------------- database export --------------------- */
465    
466     printf("exporting to %s\n", appdata->mmpoi_path);
467     rc = sqlite3_open(appdata->mmpoi_path, &db);
468     if( rc ){
469     errorf(_("Can't open SQL database:\n\n%s"), sqlite3_errmsg(db));
470     sqlite3_close(db);
471     export_list_free(context.gpx);
472     return;
473     }
474    
475     /* create the table */
476     rc = sqlite3_exec(db, "create table category (cat_id integer PRIMARY "
477     "KEY, label text, desc text, enabled integer)",
478     callback, 0, &zErrMsg);
479     if( rc!=SQLITE_OK ) {
480     errorf(_("Creating cathegory table:\n\n%s"), zErrMsg);
481     sqlite3_close(db);
482     export_list_free(context.gpx);
483     return;
484     }
485    
486     /* create all types */
487     i = 0;
488     while(cache_type_str[i][0]) {
489     snprintf(cmd, sizeof(cmd),
490     "insert into category values (%d, '%s', '%s', 1)",
491     12+i, cache_type_str[i], cache_type_desc[i]);
492    
493     rc = sqlite3_exec(db, cmd, callback, 0, &zErrMsg);
494     if( rc != SQLITE_OK ){
495     errorf(_("Creating cathegory:\n\n%s"), zErrMsg);
496     sqlite3_close(db);
497     export_list_free(context.gpx);
498     return;
499     }
500     i++;
501     }
502    
503     rc = sqlite3_exec(db, "create table poi (poi_id integer PRIMARY KEY, "
504     "lat real, lon real, label text, desc text, "
505     "cat_id integer)", callback, 0, &zErrMsg);
506     if( rc!=SQLITE_OK ){
507     errorf(_("Creating POI table:\n\n%s"), zErrMsg);
508     sqlite3_close(db);
509     export_list_free(context.gpx);
510     return;
511     }
512    
513     pos_t *refpos = get_pos(appdata);
514    
515     /* export the caches */
516     int id = 1;
517     cache_t *cache = context.gpx->cache;
518     int total = export_list_count(appdata, context.gpx);
519    
520     while(cache && !cancelled) {
521    
522     /* no disabled/archived if requested */
523     if(!(appdata->mmpoi_dont_export_disabled &&
524     (!cache->available || cache->archived))) {
525    
526     /* no found if requested */
527     if(!(appdata->mmpoi_dont_export_found &&
528     cache->notes && cache->notes->found)) {
529    
530     if(appdata->mmpoi_use_radius && !isnan(appdata->mmpoi_radius)) {
531     /* count all caches within given radius around current position */
532     if(gpx_pos_get_distance(*refpos, gpx_cache_pos(cache), 0) <
533     appdata->mmpoi_radius) {
534     printf("%d exporting %s\n", id, cache->id);
535     cache_export(db, id++, cache);
536     }
537     } else {
538     printf("%d exporting %s\n", id, cache->id);
539     cache_export(db, id++, cache);
540     }
541     }
542     }
543     cache = cache->next;
544    
545     gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pbar),
546     (float)(id-2)/(float)total);
547    
548     while(gtk_events_pending ())
549     gtk_main_iteration ();
550     }
551     sqlite3_close(db);
552    
553     gtk_widget_destroy(wait_dialog);
554    
555     #ifdef USE_MAEMO
556     if(!cancelled && !appdata->mmpoi_dontlaunch)
557     dbus_mm_set_position(appdata, NULL);
558     #endif
559     }
560     } else {
561     /* restore old mmpoi_path, in case it has been altered but not been used */
562     free(appdata->mmpoi_path);
563     appdata->mmpoi_path = strdup(old_mmpoi_path);
564     }
565    
566     gtk_widget_destroy(context.dialog);
567    
568     if(context.handler_id) {
569     printf("removing timer\n");
570     gtk_timeout_remove(context.handler_id);
571     }
572    
573     export_list_free(context.gpx);
574    
575     free(old_mmpoi_path);
576     }