Contents of /trunk/src/geotoad.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 223 - (show annotations)
Tue Dec 1 20:03:51 2009 UTC (14 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 20943 byte(s)
Various pickers
1 /*
2 * Copyright (C) 2009 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 #define __USE_GNU
23 #include <string.h>
24
25 #include <fcntl.h>
26 #include <sys/types.h>
27 #include <sys/wait.h>
28 #include <errno.h>
29 #include <math.h>
30
31 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR >= 5)
32 #include <hildon/hildon-entry.h>
33 #endif
34
35 #define GEOTOAD "/usr/bin/geotoad"
36
37 #define COLOR_ERR "red"
38 #define COLOR_OK "darkgreen"
39 #define COLOR_SYSTEM "darkblue"
40
41 #define BUFFER_SIZE 256
42
43 typedef enum {
44 GT_STATE_ILLEGAL = 0,
45 GT_STATE_STARTED,
46 GT_STATE_PREMATURE_END,
47 GT_STATE_SAVE_FOUND,
48 } gt_state_t;
49
50 typedef struct {
51 appdata_t *appdata;
52
53 GPtrArray *argv;
54 gchar *info, *color;
55 GMutex *update_mutex;
56 GCond *update_cond;
57
58 gt_state_t state;
59
60 GtkWidget *dialog;
61
62 long pid;
63
64 struct log_s {
65 GtkTextBuffer *buffer;
66 GtkWidget *view;
67 } log;
68
69 GtkWidget *username, *password, *filename;
70 GtkWidget *lat, *lon, *dst;
71
72 int use_cnt;
73
74 } gt_context_t;
75
76 static void arg_free(gpointer data, gpointer user_data) {
77 if(data) g_free(data);
78 }
79
80 static void context_free(gt_context_t *context) {
81 context->use_cnt--;
82
83 if(context->use_cnt > 0)
84 printf("still in use by %d, keeping context\n", context->use_cnt);
85 else {
86 printf("freeing context\n");
87
88 if(context->info) g_free(context->info);
89
90 if(context->argv) {
91 g_ptr_array_foreach(context->argv, arg_free, NULL);
92 g_ptr_array_free (context->argv, TRUE);
93 }
94
95 g_free(context);
96 }
97 }
98
99 static void appendf(struct log_s *log, char *colname,
100 const char *fmt, ...) {
101 va_list args;
102 va_start( args, fmt );
103 char *buf = g_strdup_vprintf(fmt, args);
104 va_end( args );
105
106 // printf("append: %s", buf);
107
108 GtkTextTag *tag = NULL;
109 if(colname)
110 tag = gtk_text_buffer_create_tag(log->buffer, NULL,
111 "foreground", colname,
112 NULL);
113
114 GtkTextIter end;
115 gtk_text_buffer_get_end_iter(log->buffer, &end);
116 if(tag)
117 gtk_text_buffer_insert_with_tags(log->buffer, &end, buf, -1, tag, NULL);
118 else
119 gtk_text_buffer_insert(log->buffer, &end, buf, -1);
120
121 g_free(buf);
122
123 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(log->view),
124 &end, 0.0, TRUE, 1.0, 1.0);
125 }
126
127 // This function receives a requst from a worker thread asking to
128 // update the gui with the required info.
129 gboolean cb_update_job(gt_context_t *context) {
130 if (context->info) {
131
132 /* check if client reports "saved to ..." */
133 if(strstr(context->info, "Saved to "))
134 context->state = GT_STATE_SAVE_FOUND;
135
136 appendf(&context->log, context->color, context->info);
137
138 g_free(context->info);
139 context->info = NULL;
140 }
141
142 // Indicate that the update is done
143 g_mutex_lock(context->update_mutex);
144 g_cond_signal(context->update_cond);
145 g_mutex_unlock(context->update_mutex);
146
147 return FALSE;
148 }
149
150 // A helper function run in the job thread receiving the string that
151 // should be displayed in the textview.
152 void append_parentf(gt_context_t *context, char *colname,
153 const char *fmt, ...) {
154 va_list args;
155 va_start( args, fmt );
156 context->info = g_strdup_vprintf(fmt, args);
157 va_end( args );
158
159 if(colname)
160 context->color = colname;
161 else
162 context->color = NULL;
163
164 // Lock mutex to make sure that we will receive the condition signal
165 g_mutex_lock(context->update_mutex);
166
167 g_idle_add((GSourceFunc)cb_update_job, context);
168
169 // Wait for cb_update_job to tell me that the update is done
170 g_cond_wait(context->update_cond, context->update_mutex);
171 g_mutex_unlock(context->update_mutex);
172 }
173
174 /* custom version of popen to get access to the pid and to be able */
175 /* to slaughter the child process */
176 static FILE *gt_popen(gt_context_t *context, char **args,
177 const char *type, long *tid) {
178 int p[2];
179 FILE *fp;
180
181 if (*type != 'r' && *type != 'w')
182 return NULL;
183
184 if (pipe(p) < 0)
185 return NULL;
186
187 if ((*tid = fork()) > 0) { /* then we are the parent */
188 if (*type == 'r') {
189 close(p[1]);
190 fp = fdopen(p[0], type);
191 } else {
192 close(p[0]);
193 fp = fdopen(p[1], type);
194 }
195
196 return fp;
197 } else if (*tid == 0) { /* we're the child */
198
199 /* make our thread id the process group leader */
200 setpgid(0, 0);
201
202 if (*type == 'r') {
203 fflush(stdout);
204 fflush(stderr);
205 close(1);
206 if (dup(p[1]) < 0)
207 perror("dup of write side of pipe failed");
208 close(2);
209 if (dup(p[1]) < 0)
210 perror("dup of write side of pipe failed");
211 } else {
212 close(0);
213 if (dup(p[0]) < 0)
214 perror("dup of read side of pipe failed");
215 }
216
217 close(p[0]); /* close since we dup()'ed what we needed */
218 close(p[1]);
219
220 execve(args[0], args, NULL);
221
222 /* this printf will actually be redirected through the pipe */
223 printf("Error: Failed to execute!\n");
224 exit(1);
225 } else { /* we're having major problems... */
226 close(p[0]);
227 close(p[1]);
228
229 /* this printf will actually be redirected through the pipe??? No! */
230 printf("Error: Failed to fork!\n");
231 }
232
233 return NULL;
234 }
235
236 // The thread entry point. It will do the job, send the data to the
237 // GUI and self destruct when it is done.
238 static gpointer thread_worker(gt_context_t *context)
239 {
240 FILE *fh;
241 GIOStatus status;
242 GError *error = NULL;
243 gsize length;
244 gsize terminator_pos;
245 gchar *str_return;
246
247 fh = gt_popen(context, (char**)(context->argv->pdata),"r", &context->pid);
248 if(!fh) {
249 printf("fail free\n");
250 context_free(context);
251 // g_thread_exit(NULL);
252 return NULL;
253 }
254
255 /* the client is running */
256 printf("client inc use_cnt\n");
257 context->use_cnt++;
258
259 /* switch to line buffered mode */
260 if(setvbuf(fh, NULL, _IOLBF, 0))
261 perror("setvbuf(_IOLBF)");
262
263 GIOChannel *gh = g_io_channel_unix_new(fileno(fh));
264
265 while( (status = g_io_channel_read_line(gh,
266 &str_return,
267 &length,
268 &terminator_pos,
269 &error)) == G_IO_STATUS_NORMAL) {
270 char *color = NULL;
271 if(strstr(str_return, "Saved to ")) color = COLOR_OK;
272 if(strcasestr(str_return, "error")) color = COLOR_ERR;
273
274 append_parentf(context, color, str_return);
275 g_free(str_return);
276 }
277
278 g_io_channel_unref(gh);
279 pclose(fh);
280 append_parentf(context, COLOR_SYSTEM, "Job done!");
281
282 context_free(context);
283
284 g_thread_exit(NULL);
285 return NULL;
286 }
287
288
289 static void arg_dsp(gpointer data, gpointer user_data) {
290 gt_context_t *context = (gt_context_t*)user_data;
291
292 if(data)
293 appendf(&context->log, COLOR_SYSTEM, "%s\n", data);
294 }
295
296 static void run(gt_context_t *context) {
297 GError *error = NULL;
298 char str[8];
299
300 /* build list of arguments to call geotoad */
301 context->argv = g_ptr_array_new();
302 g_ptr_array_add (context->argv, g_strdup_printf(GEOTOAD));
303 g_ascii_dtostr(str, sizeof(str), context->appdata->gt.distance);
304 g_ptr_array_add (context->argv,
305 g_strdup_printf("--distanceMax=%s", str));
306 g_ptr_array_add (context->argv,
307 g_strdup_printf("--output=%s", context->appdata->gt.filename));
308 g_ptr_array_add (context->argv,
309 g_strdup_printf("--password=%s", context->appdata->gt.password));
310 g_ptr_array_add (context->argv,
311 g_strdup_printf("--queryType=coord"));
312 g_ptr_array_add (context->argv,
313 g_strdup_printf("--user=%s", context->appdata->username));
314
315 /* check if we need to add proxy config */
316 char *proxy = NULL;
317
318 if(context->appdata->proxy && context->appdata->proxy->host) {
319 if(context->appdata->proxy->use_authentication &&
320 context->appdata->proxy->authentication_user &&
321 context->appdata->proxy->authentication_password)
322 proxy = g_strdup_printf("--proxy=http://%s:%s@%s:%d",
323 context->appdata->proxy->authentication_user,
324 context->appdata->proxy->authentication_password,
325 context->appdata->proxy->host,
326 context->appdata->proxy->port);
327 else
328 proxy = g_strdup_printf("--proxy=http://%s:%d",
329 context->appdata->proxy->host,
330 context->appdata->proxy->port);
331
332 } else {
333 /* use environment settings if preset (for scratchbox) */
334 const char *proxy_env = g_getenv("http_proxy");
335 if(proxy_env)
336 proxy = g_strdup_printf("--proxy=%s", proxy_env);
337 }
338
339 if(proxy)
340 g_ptr_array_add (context->argv, proxy);
341
342 /* convert coordinates into simple ascii format */
343 char n = (context->appdata->gt.lat >= 0)?'N':'S';
344 char e = (context->appdata->gt.lon >= 0)?'E':'W';
345 float lat = fabs(context->appdata->gt.lat);
346 float lon = fabs(context->appdata->gt.lon);
347 float lat_mint, lat_int, lat_frac = modff(lat, &lat_int);
348 float lon_mint, lon_int, lon_frac = modff(lon, &lon_int);
349 lat_frac = modff(lat_frac*60.0, &lat_mint);
350 lon_frac = modff(lon_frac*60.0, &lon_mint);
351
352 g_ptr_array_add (context->argv,
353 g_strdup_printf("%c%02u %02u.%03u %c%03u %02u.%03u",
354 n, (int)lat_int, (int)lat_mint, (int)(lat_frac*1000.0+0.5),
355 e, (int)lon_int, (int)lon_mint, (int)(lon_frac*1000.0+0.5)));
356
357 g_ptr_array_add (context->argv, NULL);
358
359 /* show all entries */
360 g_ptr_array_foreach(context->argv, arg_dsp, context);
361
362 g_thread_create((GThreadFunc)thread_worker, context, FALSE, &error);
363 if (error) {
364 appendf(&context->log, COLOR_ERR, "Error: %s\n", error->message);
365 g_error_free(error);
366 }
367 }
368
369 /* show text window and display output of running geotoad */
370 static void gui_run(gt_context_t *context) {
371 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("GeoToad - Run"),
372 GTK_WINDOW(context->appdata->window),
373 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
374 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
375 NULL);
376
377 gtk_window_set_default_size(GTK_WINDOW(dialog), 640, 480);
378
379 #ifndef USE_PANNABLE_AREA
380 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
381 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
382 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
383 #else
384 GtkWidget *pannable_area = hildon_pannable_area_new();
385 #endif
386
387 context->log.buffer = gtk_text_buffer_new(NULL);
388
389 #ifndef USE_HILDON_TEXT_VIEW
390 context->log.view = gtk_text_view_new_with_buffer(context->log.buffer);
391 #else
392 context->log.view = hildon_text_view_new();
393 hildon_text_view_set_buffer(HILDON_TEXT_VIEW(context->log.view),
394 context->log.buffer);
395 #endif
396
397 #ifndef USE_PANNABLE_AREA
398 gtk_container_add(GTK_CONTAINER(scrolled_window), context->log.view);
399 gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(scrolled_window),
400 GTK_SHADOW_IN);
401
402 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
403 scrolled_window, TRUE, TRUE, 0);
404 #else
405 gtk_container_add(GTK_CONTAINER(pannable_area), context->log.view);
406 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
407 pannable_area, TRUE, TRUE, 0);
408 #endif
409
410 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
411
412 gtk_widget_show_all(dialog);
413
414 appendf(&context->log, COLOR_SYSTEM, "Running GeoToad\n");
415 run(context);
416
417 gtk_dialog_run(GTK_DIALOG(dialog));
418
419 gtk_widget_destroy(dialog);
420 }
421
422 static void on_browse(GtkWidget *widget, gpointer data) {
423 gt_context_t *context = (gt_context_t*)data;
424
425 printf("Browse %p\n", context->dialog);
426
427 #ifdef USE_MAEMO
428 GtkWidget *dialog = hildon_file_chooser_dialog_new(GTK_WINDOW(context->dialog),
429 GTK_FILE_CHOOSER_ACTION_SAVE);
430 #else
431 GtkWidget *dialog = gtk_file_chooser_dialog_new(_("Save GPX file"),
432 GTK_WINDOW(context->dialog),
433 GTK_FILE_CHOOSER_ACTION_SAVE,
434 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
435 GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
436 NULL);
437 #endif
438
439 printf("set filename <%s>\n", context->appdata->gt.filename);
440
441 if(!g_file_test(context->appdata->gt.filename, G_FILE_TEST_EXISTS)) {
442 char *last_sep = strrchr(context->appdata->gt.filename, '/');
443 if(last_sep) {
444 *last_sep = 0; // seperate path from file
445
446 /* the user just created a new document */
447 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
448 context->appdata->gt.filename);
449 gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), last_sep+1);
450
451 /* restore full filename */
452 *last_sep = '/';
453 }
454 } else
455 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),
456 context->appdata->gt.filename);
457
458 if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_FM_OK) {
459 gchar *name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
460 gtk_label_set_text(GTK_LABEL(context->filename), name);
461 }
462
463 gtk_widget_destroy (dialog);
464 }
465
466 static gboolean gui_setup(gt_context_t *context) {
467 appdata_t *appdata = context->appdata;
468 gboolean ok = FALSE;
469
470 /* if no filename has been setup yet, create one */
471 if(!appdata->gt.filename && appdata->path) {
472 printf("creating path\n");
473 appdata->gt.filename =
474 g_strdup_printf("%s/gtoad.gpx", appdata->path);
475 }
476
477 context->dialog = gtk_dialog_new_with_buttons(_("GeoToad - Setup"),
478 GTK_WINDOW(appdata->window),
479 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
480 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
481 GTK_STOCK_OK, GTK_RESPONSE_OK,
482 NULL);
483
484 /* ------------------- Coordinates ------------------------- */
485 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
486
487 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
488 gtk_box_pack_start_defaults(GTK_BOX(vbox), left_label_new(_("Position:")));
489 gtk_box_pack_start_defaults(GTK_BOX(vbox), left_label_new(_("Distance:")));
490 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
491
492 /* setup default positions */
493 pos_t *refpos = get_pos(appdata);
494 if((isnan(appdata->gt.lat) || isnan(appdata->gt.lat)) && refpos) {
495 appdata->gt.lat = refpos->lat;
496 appdata->gt.lon = refpos->lon;
497 }
498
499 vbox = gtk_vbox_new(FALSE, 0);
500 GtkWidget *ihbox = gtk_hbox_new(FALSE, 0);
501 context->lat = lat_entry_new(appdata->gt.lat);
502 gtk_box_pack_start_defaults(GTK_BOX(ihbox), context->lat);
503 context->lon = lon_entry_new(appdata->gt.lon);
504 gtk_box_pack_start_defaults(GTK_BOX(ihbox), context->lon);
505 GtkWidget *popup = preset_coordinate_picker(appdata, context->lat, context->lon);
506 gtk_box_pack_start_defaults(GTK_BOX(ihbox), popup);
507 gtk_box_pack_start_defaults(GTK_BOX(vbox), ihbox);
508 float dst = appdata->gt.distance; // distance is given in kilometers
509 if(appdata->imperial) dst /= 1.609344;
510 context->dst = dist_entry_new(dst, appdata->imperial);
511 gtk_box_pack_start_defaults(GTK_BOX(vbox), context->dst);
512 gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox);
513
514 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox), hbox);
515
516 /* ------------------- file name ------------------------- */
517 hbox = gtk_hbox_new(FALSE, 0);
518
519 context->filename = gtk_label_new(appdata->gt.filename);
520 gtk_misc_set_alignment(GTK_MISC(context->filename), 0.f, 0.5f);
521 gtk_label_set_ellipsize(GTK_LABEL(context->filename), PANGO_ELLIPSIZE_MIDDLE);
522 gtk_box_pack_start_defaults(GTK_BOX(hbox), context->filename);
523
524 GtkWidget *button = gtk_button_new_with_label(_("Browse"));
525 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
526 hildon_gtk_widget_set_theme_size(button,
527 (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
528 #endif
529 gtk_signal_connect(GTK_OBJECT(button), "clicked",
530 GTK_SIGNAL_FUNC(on_browse), context);
531 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
532
533 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox), hbox);
534
535
536 /* ------------------- Username/Password ------------------------- */
537 hbox = gtk_hbox_new(FALSE, 0);
538 vbox = gtk_vbox_new(FALSE, 0);
539 gtk_box_pack_start_defaults(GTK_BOX(vbox), left_label_new(_("Username:")));
540 gtk_box_pack_start_defaults(GTK_BOX(vbox), left_label_new(_("Password:")));
541 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
542
543 vbox = gtk_vbox_new(FALSE, 0);
544 context->username = entry_new();
545 context->password = entry_new();
546 gtk_entry_set_visibility(GTK_ENTRY(context->password), FALSE);
547
548 /* set saved defaults */
549 if(appdata->username)
550 gtk_entry_set_text(GTK_ENTRY(context->username),
551 appdata->username);
552
553 if(appdata->gt.password)
554 gtk_entry_set_text(GTK_ENTRY(context->password),
555 appdata->gt.password);
556
557 gtk_box_pack_start_defaults(GTK_BOX(vbox), context->username);
558 gtk_box_pack_start_defaults(GTK_BOX(vbox), context->password);
559 gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox);
560
561 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox), hbox);
562
563 gtk_dialog_set_default_response(GTK_DIALOG(context->dialog), GTK_RESPONSE_OK);
564
565 gtk_widget_show_all(context->dialog);
566
567 if(gtk_dialog_run(GTK_DIALOG(context->dialog)) == GTK_RESPONSE_OK) {
568
569 /* parse coordinates */
570 appdata->gt.lat = lat_entry_get(context->lat);
571 appdata->gt.lon = lon_entry_get(context->lon);
572
573 /* save values */
574 if(appdata->username) g_free(appdata->username);
575 appdata->username =
576 g_strdup(gtk_entry_get_text(GTK_ENTRY(context->username)));
577
578 if(appdata->gt.password) g_free(appdata->gt.password);
579 appdata->gt.password =
580 g_strdup(gtk_entry_get_text(GTK_ENTRY(context->password)));
581
582 if(appdata->gt.filename) g_free(appdata->gt.filename);
583 appdata->gt.filename =
584 g_strdup(gtk_label_get_text(GTK_LABEL(context->filename)));
585
586 /* get distance in kilometers */
587 appdata->gt.distance = dist_entry_get(context->dst, FALSE);
588
589
590 /* check for valid entries */
591 if(isnan(appdata->gt.lat) || isnan(appdata->gt.lon) ||
592 isnan(appdata->gt.distance) || !appdata->gt.filename ||
593 !appdata->username || !appdata->gt.password)
594 errorf(_("The GeoToad setup is not complete."));
595 else
596 ok = TRUE;
597 }
598
599 gtk_widget_destroy(context->dialog);
600
601 return ok;
602 }
603
604 void geotoad(appdata_t *appdata) {
605 if(!geotoad_available()) {
606 errorf(_("GeoToad is not installed on this device.\n"
607 "You need to install it in order to be able to use it."));
608 return;
609 }
610
611 gt_context_t *context = g_new0(gt_context_t, 1);
612 context->appdata = appdata;
613 context->use_cnt++; // parent still uses this
614
615 context->update_mutex = g_mutex_new();
616 context->update_cond = g_cond_new();
617
618 if(gui_setup(context))
619 gui_run(context);
620
621 /* continue to process if something has actually been saved */
622 if(context->state == GT_STATE_SAVE_FOUND) {
623 /* download seems to be successful. Make sure the GUI is */
624 /* updated if required */
625
626 gpx_t **gpx = &(appdata->gpx);
627 while(*gpx && strcmp((*gpx)->filename, appdata->gt.filename))
628 gpx = &(*gpx)->next;
629
630 /* return main GUI to GPX list */
631 #ifdef USE_BREAD_CRUMB_TRAIL
632 while(appdata->cur_gpx)
633 hildon_bread_crumb_trail_pop(HILDON_BREAD_CRUMB_TRAIL(appdata->bct));
634 #elif defined(BCT)
635 while(appdata->cur_gpx)
636 bct_pop(appdata->bct);
637 #else
638 HildonWindowStack *stack = hildon_window_stack_get_default();
639 gint num = hildon_window_stack_size(stack)-1;
640 while(num--) {
641 GtkWidget *top = hildon_window_stack_peek(stack);
642 gtk_widget_destroy(top);
643 }
644 #endif
645
646 /* replace an existing entry or add to end of list */
647 if(*gpx) {
648 GtkTreeIter iter;
649 g_assert(gpxlist_find(appdata, &iter, *gpx));
650
651 gpx_t *next = (*gpx)->next;
652
653 gpx_free(*gpx);
654 *gpx = gpx_parse(NULL, appdata->gt.filename, appdata->username);
655 (*gpx)->next = next;
656
657 /* update gpxlist */
658 gpxlist_set(appdata->gpxstore, &iter, *gpx);
659
660 /* select that row */
661 GtkTreeSelection *selection =
662 gtk_tree_view_get_selection(GTK_TREE_VIEW(appdata->gpxview));
663 gtk_tree_selection_select_iter(selection, &iter);
664 GtkTreePath *path =
665 gtk_tree_model_get_path(GTK_TREE_MODEL(appdata->gpxstore), &iter);
666 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(appdata->gpxview),
667 path, NULL, TRUE, 0.0, 0.0);
668 gtk_tree_path_free(path);
669 } else {
670 gpx_t *new = gpx_parse(NULL, appdata->gt.filename, appdata->username);
671 if(new) gpxlist_add(appdata, new);
672 }
673 }
674
675 printf("main context free\n");
676 context_free(context);
677 }
678
679 gboolean geotoad_available(void) {
680 /* before doing anything make sure geotoad is installed */
681 return g_file_test(GEOTOAD, G_FILE_TEST_IS_EXECUTABLE);
682 }
683
684