Parent Directory | Revision Log
more geotoad
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 | GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, |
124 | 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 | GtkWidget *button = gtk_button_new_with_label(_("Browse")); |
226 | 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 | } |