Contents of /trunk/src/mm_poi.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 233 - (show annotations)
Wed Dec 9 19:45:36 2009 UTC (14 years, 4 months ago) by harbaum
File MIME type: text/plain
File size: 17164 byte(s)
File selection fremantleized
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 if(check_button_get_active(context->use_radius)) {
242 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 toggle_button_get_active(context->no_found);
251
252 context->appdata->mmpoi_dont_export_disabled =
253 toggle_button_get_active(context->no_disabled);
254
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 context->appdata->mmpoi_radius =
262 dist_entry_get(context->entry, context->appdata->imperial);
263
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 context.use_radius = check_button_new_with_label(
322 _("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 check_button_set_active(context.use_radius, appdata->mmpoi_use_radius);
330
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 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
347 toggle_button_set_active(context.no_found,
348 appdata->mmpoi_dont_export_found);
349
350 toggle_button_set_active(context.no_disabled,
351 appdata->mmpoi_dont_export_disabled);
352
353 gtk_box_pack_start_defaults(GTK_BOX(hbox), label);
354 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
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 context.launch = check_button_new_with_label(
378 _("Launch Maemo Mapper after export")));
379 check_button_set_active(context.launch, !appdata->mmpoi_dontlaunch);
380 #endif
381
382 /* ------------------ path/file ------------------ */
383 gtk_box_pack_start_defaults(GTK_BOX(vbox), gtk_hseparator_new());
384
385 gtk_box_pack_start_defaults(GTK_BOX(vbox),
386 export_file(_("Save POI database"), &appdata->mmpoi_path));
387
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 !check_button_get_active(context.launch);
398 #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 gtk_window_set_title(GTK_WINDOW(wait_dialog), _("Exporting"));
413 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 GtkWidget *button = button_new_with_label(_("Cancel"));
420 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 strlen("cache_type_.png")+1);
432 sprintf(src, ICONPATH "cache_type_%s.png", cache_type_icons[i]);
433 if(!g_file_test(src, G_FILE_TEST_EXISTS))
434 sprintf(src, "./icons/cache_type_%s.png", cache_type_icons[i]);
435
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 }