Parent Directory | Revision Log
more geotoad
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 | #include <sqlite3.h> | ||
22 | |||
23 | #include <libxml/parser.h> | ||
24 | #include <libxml/tree.h> | ||
25 | |||
26 | #if !defined(LIBXML_TREE_ENABLED) || !defined(LIBXML_OUTPUT_ENABLED) | ||
27 | #error "libxml doesn't support required tree or output" | ||
28 | #endif | ||
29 | |||
30 | typedef struct { | ||
31 | GtkWidget *dialog; | ||
32 | appdata_t *appdata; | ||
33 | GtkWidget *path_label, *ignore_found; | ||
34 | GtkWidget *info_label; | ||
35 | gpx_t *gpx; | ||
36 | } export_context_t; | ||
37 | |||
38 | /* add a cache to the chain of caches to be exported */ | ||
39 | static void chain_cache(gpx_t *gpx, cache_t *cache) { | ||
40 | cache_t **cur = &gpx->cache; | ||
41 | |||
42 | /* search end of chain */ | ||
43 | while(*cur) { | ||
44 | if(strcmp(cache->id, (*cur)->id) == 0) | ||
45 | return; | ||
46 | |||
47 | cur = &(*cur)->next; | ||
48 | } | ||
49 | |||
50 | *cur = malloc(sizeof(cache_t)); | ||
51 | if(!(*cur)) return; | ||
52 | |||
53 | memcpy(*cur, cache, sizeof(cache_t)); | ||
54 | (*cur)->next = NULL; | ||
55 | } | ||
56 | |||
57 | static gpx_t *export_list_create(appdata_t *appdata) { | ||
58 | gpx_t *gpx = g_new0(gpx_t,1); | ||
59 | if(!gpx) return gpx; | ||
60 | |||
61 | gpx_t *lgpx = appdata->gpx; | ||
62 | while(lgpx) { | ||
63 | /* make sure all notes are loaded */ | ||
64 | if(!lgpx->notes_loaded) { | ||
65 | notes_load_all(appdata, lgpx); | ||
66 | lgpx->notes_loaded = TRUE; | ||
67 | } | ||
68 | |||
69 | cache_t *cache = lgpx->cache; | ||
70 | while(cache) { | ||
71 | if(cache->notes && cache->notes->override) | ||
72 | chain_cache(gpx, cache); | ||
73 | |||
74 | cache = cache->next; | ||
75 | } | ||
76 | lgpx = lgpx->next; | ||
77 | } | ||
78 | |||
79 | return gpx; | ||
80 | } | ||
81 | |||
82 | static void export_list_free(gpx_t *gpx) { | ||
83 | printf("freeing export list\n"); | ||
84 | |||
85 | cache_t *cache = gpx->cache; | ||
86 | while(cache) { | ||
87 | cache_t *tmp = cache; | ||
88 | cache = cache->next; | ||
89 | free(tmp); | ||
90 | } | ||
91 | free(gpx); | ||
92 | } | ||
93 | |||
94 | static int export_list_count(appdata_t *appdata, gpx_t *gpx) { | ||
95 | int cnt = 0; | ||
96 | |||
97 | cache_t *cache = gpx->cache; | ||
98 | while(cache) { | ||
99 | if(!appdata->garmin_ign_found || | ||
100 | (appdata->garmin_ign_found && !cache->notes->found)) | ||
101 | cnt++; | ||
102 | |||
103 | cache = cache->next; | ||
104 | } | ||
105 | return cnt; | ||
106 | } | ||
107 | |||
108 | static void on_browse(GtkWidget *widget, gpointer data) { | ||
109 | GtkWidget *dialog; | ||
110 | |||
111 | export_context_t *context = (export_context_t*)data; | ||
112 | |||
113 | printf("Browse\n"); | ||
114 | |||
115 | #ifdef USE_MAEMO | ||
116 | dialog = hildon_file_chooser_dialog_new(GTK_WINDOW(context->dialog), | ||
117 | GTK_FILE_CHOOSER_ACTION_SAVE); | ||
118 | #else | ||
119 | dialog = gtk_file_chooser_dialog_new(_("Save garmin GPX"), | ||
120 | GTK_WINDOW(context->dialog), | ||
121 | GTK_FILE_CHOOSER_ACTION_SAVE, | ||
122 | GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, | ||
123 | harbaum | 198 | GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, |
124 | harbaum | 1 | NULL); |
125 | #endif | ||
126 | |||
127 | printf("set filename <%s>\n", context->appdata->garmin_path); | ||
128 | |||
129 | if(!g_file_test(context->appdata->garmin_path, G_FILE_TEST_EXISTS)) { | ||
130 | char *last_sep = strrchr(context->appdata->garmin_path, '/'); | ||
131 | if(last_sep) { | ||
132 | *last_sep = 0; // seperate path from file | ||
133 | |||
134 | /* the user just created a new document */ | ||
135 | gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog), | ||
136 | context->appdata->garmin_path); | ||
137 | gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), last_sep+1); | ||
138 | |||
139 | /* restore full filename */ | ||
140 | *last_sep = '/'; | ||
141 | } | ||
142 | } else | ||
143 | gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog), | ||
144 | context->appdata->garmin_path); | ||
145 | |||
146 | if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_FM_OK) { | ||
147 | gchar *name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); | ||
148 | if(name) { | ||
149 | free(context->appdata->garmin_path); | ||
150 | context->appdata->garmin_path = strdup(name); | ||
151 | gtk_label_set_text(GTK_LABEL(context->path_label), | ||
152 | context->appdata->garmin_path); | ||
153 | } | ||
154 | } | ||
155 | |||
156 | gtk_widget_destroy(dialog); | ||
157 | } | ||
158 | |||
159 | /* Our usual callback function */ | ||
160 | static void export_update(GtkWidget *widget, gpointer data) { | ||
161 | export_context_t *context = (export_context_t *)data; | ||
162 | |||
163 | if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(context->ignore_found))) | ||
164 | context->appdata->garmin_ign_found = TRUE; | ||
165 | else | ||
166 | context->appdata->garmin_ign_found = FALSE; | ||
167 | |||
168 | char str[256]; | ||
169 | snprintf(str, sizeof(str), | ||
170 | _("This will export the overridden coordinates of %d " | ||
171 | "caches into a GPX waypoint file suitable for garmin " | ||
172 | "GPS units."), export_list_count(context->appdata, context->gpx)); | ||
173 | |||
174 | gtk_label_set_text(GTK_LABEL(context->info_label), str); | ||
175 | } | ||
176 | |||
177 | void garmin_export(appdata_t *appdata) { | ||
178 | char *old_garmin_path = strdup(appdata->garmin_path); | ||
179 | |||
180 | export_context_t context; | ||
181 | memset(&context, 0, sizeof(export_context_t)); | ||
182 | context.appdata = appdata; | ||
183 | context.gpx = export_list_create(appdata); | ||
184 | |||
185 | printf("export garmin data\n"); | ||
186 | |||
187 | /* --------- do confirmation dialog ----------- */ | ||
188 | context.dialog = gtk_dialog_new_with_buttons(_("Garmin waypoint export"), | ||
189 | GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL, | ||
190 | GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, | ||
191 | GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, | ||
192 | NULL); | ||
193 | |||
194 | #if defined(USE_MAEMO) && defined(HILDON_HELP) | ||
195 | hildon_help_dialog_help_enable(GTK_DIALOG(context.dialog), | ||
196 | HELP_ID_GARMIN, appdata->osso_context); | ||
197 | #endif | ||
198 | |||
199 | GtkWidget *vbox = gtk_vbox_new(FALSE,2); | ||
200 | |||
201 | gtk_box_pack_start_defaults(GTK_BOX(vbox), | ||
202 | context.ignore_found = gtk_check_button_new_with_label( | ||
203 | _("Ignore found caches"))); | ||
204 | gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(context.ignore_found), | ||
205 | appdata->garmin_ign_found); | ||
206 | /* Connect the "clicked" signal of the button to our callback */ | ||
207 | gtk_signal_connect (GTK_OBJECT(context.ignore_found), "clicked", | ||
208 | GTK_SIGNAL_FUNC(export_update), (gpointer)&context); | ||
209 | |||
210 | context.info_label = gtk_label_new(""); | ||
211 | gtk_label_set_line_wrap_mode(GTK_LABEL(context.info_label),PANGO_WRAP_WORD); | ||
212 | gtk_label_set_line_wrap(GTK_LABEL(context.info_label), TRUE); | ||
213 | gtk_misc_set_alignment(GTK_MISC(context.info_label), 0.f, 0.5f); | ||
214 | gtk_box_pack_start_defaults(GTK_BOX(vbox), context.info_label); | ||
215 | |||
216 | export_update(NULL, &context); | ||
217 | |||
218 | /* ------------------ path/file ------------------ */ | ||
219 | gtk_box_pack_start_defaults(GTK_BOX(vbox), gtk_hseparator_new()); | ||
220 | |||
221 | GtkWidget *hbox = gtk_hbox_new(FALSE, 0); | ||
222 | GtkWidget *label = gtk_label_new(_("Export to:")); | ||
223 | gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE,0); | ||
224 | gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f); | ||
225 | harbaum | 7 | GtkWidget *button = gtk_button_new_with_label(_("Browse")); |
226 | harbaum | 1 | gtk_signal_connect(GTK_OBJECT(button), "clicked", |
227 | GTK_SIGNAL_FUNC(on_browse), (gpointer)&context); | ||
228 | gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE,0); | ||
229 | gtk_box_pack_start_defaults(GTK_BOX(vbox), hbox); | ||
230 | |||
231 | context.path_label = gtk_label_new(appdata->garmin_path); | ||
232 | gtk_misc_set_alignment(GTK_MISC(context.path_label), 0.f, 0.5f); | ||
233 | gtk_label_set_ellipsize(GTK_LABEL(context.path_label), | ||
234 | PANGO_ELLIPSIZE_MIDDLE); | ||
235 | gtk_box_pack_start_defaults(GTK_BOX(vbox), context.path_label); | ||
236 | |||
237 | /* ----------------------------------- */ | ||
238 | |||
239 | gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context.dialog)->vbox), vbox); | ||
240 | |||
241 | gtk_widget_show_all(context.dialog); | ||
242 | |||
243 | if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(context.dialog))) { | ||
244 | /* remove existing database */ | ||
245 | remove(appdata->garmin_path); | ||
246 | |||
247 | if(checkdir(appdata->garmin_path) != 0) | ||
248 | errorf(_("Unable to access or create output directory!")); | ||
249 | else { | ||
250 | |||
251 | if(checkdir(appdata->garmin_path) != 0) { | ||
252 | errorf(_("Unable to create export path!")); | ||
253 | } else { | ||
254 | |||
255 | LIBXML_TEST_VERSION; | ||
256 | |||
257 | xmlDocPtr doc = xmlNewDoc(BAD_CAST "1.0"); | ||
258 | xmlNodePtr root_node = xmlNewNode(NULL, BAD_CAST "gpx"); | ||
259 | xmlDocSetRootElement(doc, root_node); | ||
260 | |||
261 | /* add all waypoints */ | ||
262 | cache_t *cache = context.gpx->cache; | ||
263 | while(cache) { | ||
264 | notes_t *notes = cache->notes; | ||
265 | g_assert(notes); | ||
266 | |||
267 | if(!appdata->garmin_ign_found || | ||
268 | (appdata->garmin_ign_found && !notes->found)) { | ||
269 | |||
270 | xmlNodePtr wpt_node = | ||
271 | xmlNewChild(root_node, NULL, BAD_CAST "wpt", NULL); | ||
272 | |||
273 | /* make sure no invalid position gets saved */ | ||
274 | char str[32]; | ||
275 | g_ascii_dtostr(str, sizeof(str), notes->pos.lat); | ||
276 | xmlNewProp(wpt_node, BAD_CAST "lat", BAD_CAST str); | ||
277 | g_ascii_dtostr(str, sizeof(str), notes->pos.lon); | ||
278 | xmlNewProp(wpt_node, BAD_CAST "lon", BAD_CAST str); | ||
279 | |||
280 | int len = strlen(cache->id) + strlen(" - ") + | ||
281 | strlen(cache->name) + 1; | ||
282 | char *name = malloc(len); | ||
283 | snprintf(name, len, "%s - %s", cache->id, cache->name); | ||
284 | xmlNewChild(wpt_node, NULL, BAD_CAST "name", BAD_CAST name); | ||
285 | free(name); | ||
286 | xmlNewChild(wpt_node, NULL, BAD_CAST "sym", BAD_CAST "Pin, Blue"); | ||
287 | xmlNewChild(wpt_node, NULL, BAD_CAST "desc", BAD_CAST notes->text); | ||
288 | } | ||
289 | |||
290 | cache = cache->next; | ||
291 | } | ||
292 | |||
293 | /* write everything and free it */ | ||
294 | printf("writing %s\n", appdata->garmin_path); | ||
295 | xmlSaveFormatFileEnc(appdata->garmin_path, doc, "UTF-8", 1); | ||
296 | xmlFreeDoc(doc); | ||
297 | xmlCleanupParser(); | ||
298 | } | ||
299 | } | ||
300 | } else { | ||
301 | /* restore old garmin_path, in case it has been altered */ | ||
302 | /* but not been used */ | ||
303 | free(appdata->garmin_path); | ||
304 | appdata->garmin_path = strdup(old_garmin_path); | ||
305 | } | ||
306 | |||
307 | gtk_widget_destroy(context.dialog); | ||
308 | export_list_free(context.gpx); | ||
309 | free(old_garmin_path); | ||
310 | } |