Contents of /trunk/src/geotoad.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 200 - (hide annotations)
Thu Nov 19 13:57:17 2009 UTC (14 years, 6 months ago) by harbaum
File MIME type: text/plain
File size: 16482 byte(s)
geotoad stuff
1 harbaum 193 /*
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     #include <fcntl.h>
23     #include <sys/types.h>
24     #include <sys/wait.h>
25     #include <errno.h>
26 harbaum 198 #include <math.h>
27 harbaum 193
28 harbaum 198 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR >= 5)
29     #include <hildon/hildon-entry.h>
30     #endif
31    
32 harbaum 196 #define GEOTOAD "/usr/bin/geotoad"
33    
34 harbaum 195 #define COLOR_ERR "red"
35     #define COLOR_OK "darkgreen"
36     #define COLOR_SYSTEM "darkblue"
37 harbaum 194
38 harbaum 195 #define BUFFER_SIZE 256
39 harbaum 193
40     typedef struct {
41 harbaum 194 appdata_t *appdata;
42    
43 harbaum 198 GtkWidget *dialog;
44    
45 harbaum 193 char buf[BUFFER_SIZE];
46     int bused;
47    
48     /** gdk input tag for async IO */
49     gint stdout_tag, stderr_tag;
50     gint stdin_fd, stdout_fd, stderr_fd;
51    
52 harbaum 194 struct log_s {
53     GtkTextBuffer *buffer;
54     GtkWidget *view;
55     } log;
56 harbaum 196
57 harbaum 198 GtkWidget *username, *password, *filename;
58     GtkWidget *lat, *lon, *dst;
59 harbaum 194
60 harbaum 193 } gt_context_t;
61    
62 harbaum 194 static void appendf(struct log_s *log, char *colname,
63     const char *fmt, ...) {
64     va_list args;
65     va_start( args, fmt );
66     char *buf = g_strdup_vprintf(fmt, args);
67     va_end( args );
68    
69 harbaum 195 printf("append: %s", buf);
70    
71 harbaum 194 GtkTextTag *tag = NULL;
72     if(colname)
73     tag = gtk_text_buffer_create_tag(log->buffer, NULL,
74     "foreground", colname,
75     NULL);
76    
77     GtkTextIter end;
78     gtk_text_buffer_get_end_iter(log->buffer, &end);
79     if(tag)
80     gtk_text_buffer_insert_with_tags(log->buffer, &end, buf, -1, tag, NULL);
81     else
82     gtk_text_buffer_insert(log->buffer, &end, buf, -1);
83    
84     g_free(buf);
85    
86 harbaum 195 gtk_text_buffer_get_end_iter(log->buffer, &end);
87 harbaum 194 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(log->view),
88     &end, 0.0, FALSE, 0, 0);
89     }
90    
91 harbaum 193 /* watch child process and receive events */
92     static void child_state_cb(GPid pid, gint status, gpointer data) {
93 harbaum 195 gt_context_t *context = (gt_context_t*)data;
94 harbaum 193
95     puts("child state");
96    
97     if(WIFEXITED(status)) {
98 harbaum 194 printf("child exited with return code %d\n", WEXITSTATUS(status));
99     } else
100     printf("child failed\n");
101 harbaum 193
102     puts("gt exited");
103    
104 harbaum 195 appendf(&context->log, COLOR_SYSTEM, "GeoToad finished\n");
105    
106     appendf(&context->log, COLOR_SYSTEM, "TODO: free context!!!\n");
107     // printf("freeing context\n");
108     // g_free(context);
109    
110 harbaum 193 /* Reap child if needed. */
111     waitpid (pid, NULL, WNOHANG);
112     }
113    
114 harbaum 195 static gboolean child_input_cb(GIOChannel *source,
115     GIOCondition condition,
116     gpointer data) {
117 harbaum 193 gt_context_t *context = (gt_context_t*)data;
118 harbaum 195 int fd = g_io_channel_unix_get_fd(source);
119 harbaum 193 ssize_t bytes;
120    
121 harbaum 195 g_assert(condition == G_IO_IN);
122 harbaum 193
123     /* append to current buffer content */
124     while( (bytes = read(fd, context->buf+context->bused,
125     sizeof(context->buf) - context->bused - 1)) > 0) {
126     context->bused += bytes;
127     context->buf[context->bused]='\0';
128    
129     /* consume line by line */
130     gchar *ptr = context->buf;
131    
132     /* parse as long as the buffer contains a newline */
133     while( strchr(ptr, '\n') != NULL) {
134     char *p = strchr(ptr, '\n');
135     *p = '\0';
136    
137 harbaum 195 char *color = NULL;
138     if(strstr(ptr, "Saved to ") != NULL)
139     color = COLOR_OK;
140 harbaum 193
141 harbaum 195 appendf(&context->log, color, "%s\n", ptr);
142    
143 harbaum 193 ptr = p+1;
144     }
145    
146     /* move remaining bytes down the buffer */
147     memmove(context->buf, ptr, sizeof(context->buf)-(ptr-context->buf));
148     context->bused -= ptr - context->buf;
149    
150     /* check if buffer is full but doesn't contain any newline */
151     if(context->bused >= sizeof(context->buf)-1) {
152     printf("ERROR: reply buffer overflow\n");
153     context->bused = 0;
154     }
155     }
156    
157 harbaum 195 return TRUE;
158 harbaum 193 }
159    
160 harbaum 200 static void arg_free(gpointer data, gpointer user_data) {
161     if(data) g_free(data);
162     }
163    
164     static void arg_dsp(gpointer data, gpointer user_data) {
165     gt_context_t *context = (gt_context_t*)user_data;
166    
167     if(data)
168     appendf(&context->log, COLOR_SYSTEM, "%s\n", data);
169     }
170    
171 harbaum 194 static void run(gt_context_t *context) {
172 harbaum 200 char str[8];
173    
174 harbaum 193 /* setup context */
175     context->bused = 0;
176     context->stdout_tag = -1;
177     context->stderr_tag = -1;
178     context->stdin_fd = -1;
179     context->stderr_fd = -1;
180     context->stderr_fd = -1;
181    
182 harbaum 194 /* build list of arguments to call geotoad */
183 harbaum 193 GPtrArray *gt_argv = g_ptr_array_new();
184 harbaum 200 g_ptr_array_add (gt_argv, g_strdup_printf(GEOTOAD));
185     g_ascii_dtostr(str, sizeof(str), context->appdata->gt.distance);
186     g_ptr_array_add (gt_argv, g_strdup_printf("--distanceMax=%s", str));
187     g_ptr_array_add (gt_argv, g_strdup_printf("--output=%s", context->appdata->gt.filename));
188     g_ptr_array_add (gt_argv, g_strdup_printf("--password=%s", context->appdata->gt.password));
189     g_ptr_array_add (gt_argv, g_strdup_printf("--queryType=coord"));
190     g_ptr_array_add (gt_argv, g_strdup_printf("--user=%s", context->appdata->gt.username));
191 harbaum 194
192     /* check if we need to add proxy config */
193     char *proxy = NULL;
194     if(context->appdata->proxy && context->appdata->proxy->host) {
195     if(context->appdata->proxy->use_authentication &&
196     context->appdata->proxy->authentication_user &&
197     context->appdata->proxy->authentication_password)
198     proxy = g_strdup_printf("--proxy=http://%s:%s@%s:%d",
199     context->appdata->proxy->authentication_user,
200     context->appdata->proxy->authentication_password,
201     context->appdata->proxy->host,
202     context->appdata->proxy->port);
203     else
204     proxy = g_strdup_printf("--proxy=http://%s:%d",
205     context->appdata->proxy->host,
206     context->appdata->proxy->port);
207    
208     g_ptr_array_add (gt_argv, proxy);
209     }
210    
211 harbaum 200 g_ptr_array_add (gt_argv, g_strdup_printf("N49 00.000 E008 23.000"));
212 harbaum 193 g_ptr_array_add (gt_argv, NULL);
213 harbaum 194
214 harbaum 200 /* show all entries */
215     g_ptr_array_foreach(gt_argv, arg_dsp, context);
216    
217 harbaum 193 GError *error=NULL;
218     GPid pid;
219     GSource *gt_watch;
220    
221     if (!g_spawn_async_with_pipes (NULL, /* CWD */
222     (char **) gt_argv->pdata, /* argv */
223     NULL, /* envp */
224     G_SPAWN_DO_NOT_REAP_CHILD, /* flags */
225     NULL, /* child setup */
226     NULL, /* user data */
227     &pid,
228     &context->stdin_fd,
229     &context->stdout_fd,
230     &context->stderr_fd,
231     &error)) {
232 harbaum 200 g_ptr_array_foreach(gt_argv, arg_free, NULL);
233 harbaum 193 g_ptr_array_free(gt_argv, TRUE);
234 harbaum 194 appendf(&context->log, COLOR_ERR,
235 harbaum 195 _("GeoToad failed to start!\n%s\n"), error->message);
236 harbaum 193 g_error_free(error);
237     return;
238     }
239 harbaum 195
240 harbaum 200 g_ptr_array_foreach(gt_argv, arg_free, NULL);
241 harbaum 193 g_ptr_array_free (gt_argv, TRUE);
242    
243     gt_watch = g_child_watch_source_new(pid);
244 harbaum 195 g_source_set_callback(gt_watch, (GSourceFunc) child_state_cb, context, NULL);
245 harbaum 193
246     g_source_attach(gt_watch, NULL);
247     g_source_unref(gt_watch);
248    
249     /* make nonblocking */
250     if(fcntl(context->stdout_fd, F_SETFL, O_NONBLOCK) == -1)
251     perror("fcntl failed");
252    
253     if(fcntl(context->stderr_fd, F_SETFL, O_NONBLOCK) == -1)
254     perror("fcntl failed");
255    
256 harbaum 195 GIOChannel *ioc = g_io_channel_unix_new(context->stdout_fd);
257     g_io_channel_set_close_on_unref (ioc, TRUE);
258     g_io_channel_set_encoding (ioc, NULL, NULL);
259     g_io_add_watch(ioc, G_IO_IN, child_input_cb, context);
260     g_io_channel_unref(ioc);
261    
262     // ioc = g_io_channel_unix_new(context->stderr_fd);
263     // g_io_add_watch(ioc, G_IO_IN, child_input_cb, context);
264     // g_io_channel_unref(ioc);
265 harbaum 193 }
266 harbaum 194
267 harbaum 196 /* show text window and display output of running geotoad */
268     static void gui_run(gt_context_t *context) {
269     GtkWidget *dialog = gtk_dialog_new_with_buttons(_("GeoToad - Run"),
270     GTK_WINDOW(context->appdata->window),
271 harbaum 194 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
272     GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
273     NULL);
274    
275     gtk_window_set_default_size(GTK_WINDOW(dialog), 640, 480);
276    
277     #ifndef USE_PANNABLE_AREA
278     GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
279     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
280     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
281     #else
282     GtkWidget *pannable_area = hildon_pannable_area_new();
283     #endif
284    
285     context->log.buffer = gtk_text_buffer_new(NULL);
286    
287     #ifndef USE_HILDON_TEXT_VIEW
288     context->log.view = gtk_text_view_new_with_buffer(context->log.buffer);
289     #else
290     context->log.view = hildon_text_view_new();
291     hildon_text_view_set_buffer(HILDON_TEXT_VIEW(context->log.view),
292     context->log.buffer);
293     #endif
294    
295     #ifndef USE_PANNABLE_AREA
296     gtk_container_add(GTK_CONTAINER(scrolled_window), context->log.view);
297     gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(scrolled_window),
298     GTK_SHADOW_IN);
299    
300     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
301     scrolled_window, TRUE, TRUE, 0);
302     #else
303     gtk_container_add(GTK_CONTAINER(pannable_area), context->log.view);
304     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox),
305     pannable_area, TRUE, TRUE, 0);
306     #endif
307    
308     gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
309    
310     gtk_widget_show_all(dialog);
311    
312 harbaum 195 appendf(&context->log, COLOR_SYSTEM, "Running GeoToad\n");
313 harbaum 194 run(context);
314    
315     gtk_dialog_run(GTK_DIALOG(dialog));
316    
317     gtk_widget_destroy(dialog);
318     }
319 harbaum 196
320 harbaum 198 static void on_browse(GtkWidget *widget, gpointer data) {
321     gt_context_t *context = (gt_context_t*)data;
322    
323     printf("Browse %p\n", context->dialog);
324    
325     #ifdef USE_MAEMO
326     GtkWidget *dialog = hildon_file_chooser_dialog_new(GTK_WINDOW(context->dialog),
327     GTK_FILE_CHOOSER_ACTION_SAVE);
328     #else
329     GtkWidget *dialog = gtk_file_chooser_dialog_new(_("Save GPX file"),
330     GTK_WINDOW(context->dialog),
331     GTK_FILE_CHOOSER_ACTION_SAVE,
332     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
333     GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT,
334     NULL);
335     #endif
336    
337     printf("set filename <%s>\n", context->appdata->gt.filename);
338    
339     if(!g_file_test(context->appdata->gt.filename, G_FILE_TEST_EXISTS)) {
340     char *last_sep = strrchr(context->appdata->gt.filename, '/');
341     if(last_sep) {
342     *last_sep = 0; // seperate path from file
343    
344     /* the user just created a new document */
345     gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
346     context->appdata->gt.filename);
347     gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog), last_sep+1);
348    
349     /* restore full filename */
350     *last_sep = '/';
351     }
352     } else
353     gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(dialog),
354     context->appdata->gt.filename);
355    
356     if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_FM_OK) {
357     gchar *name = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog));
358     gtk_label_set_text(GTK_LABEL(context->filename), name);
359     }
360    
361     gtk_widget_destroy (dialog);
362     }
363    
364 harbaum 196 static gboolean gui_setup(gt_context_t *context) {
365 harbaum 198 appdata_t *appdata = context->appdata;
366 harbaum 196 gboolean ok = FALSE;
367    
368 harbaum 198 /* if no filename has been setup yet, create one */
369     if(!appdata->gt.filename && appdata->path) {
370     printf("creating path\n");
371     appdata->gt.filename =
372     g_strdup_printf("%s/gtoad.gpx", appdata->path);
373     }
374 harbaum 196
375 harbaum 198 context->dialog = gtk_dialog_new_with_buttons(_("GeoToad - Setup"),
376     GTK_WINDOW(appdata->window),
377     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
378     GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
379     GTK_STOCK_OK, GTK_RESPONSE_OK,
380     NULL);
381    
382     /* ------------------- Coordinates ------------------------- */
383 harbaum 196 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
384 harbaum 198
385 harbaum 196 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
386 harbaum 199 gtk_box_pack_start_defaults(GTK_BOX(vbox), left_label_new(_("Position:")));
387 harbaum 198 gtk_box_pack_start_defaults(GTK_BOX(vbox), left_label_new(_("Distance:")));
388     gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
389    
390     /* setup default positions */
391     pos_t *refpos = get_pos(appdata);
392     if((isnan(appdata->gt.lat) || isnan(appdata->gt.lat)) && refpos) {
393     appdata->gt.lat = refpos->lat;
394     appdata->gt.lon = refpos->lon;
395     }
396    
397     vbox = gtk_vbox_new(FALSE, 0);
398 harbaum 199 GtkWidget *ihbox = gtk_hbox_new(FALSE, 0);
399 harbaum 198 context->lat = lat_entry_new(appdata->gt.lat);
400 harbaum 199 gtk_box_pack_start_defaults(GTK_BOX(ihbox), context->lat);
401     context->lon = lon_entry_new(appdata->gt.lon);
402     gtk_box_pack_start_defaults(GTK_BOX(ihbox), context->lon);
403     gtk_box_pack_start_defaults(GTK_BOX(vbox), ihbox);
404 harbaum 200 float dst = appdata->gt.distance; // distance is given in kilometers
405     if(appdata->imperial) dst /= 1.609344;
406     context->dst = dist_entry_new(dst, appdata->imperial);
407 harbaum 198 gtk_box_pack_start_defaults(GTK_BOX(vbox), context->dst);
408 harbaum 196 gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox);
409 harbaum 198
410     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox), hbox);
411    
412     /* ------------------- file name ------------------------- */
413     hbox = gtk_hbox_new(FALSE, 0);
414    
415     context->filename = gtk_label_new(appdata->gt.filename);
416     gtk_misc_set_alignment(GTK_MISC(context->filename), 0.f, 0.5f);
417     gtk_label_set_ellipsize(GTK_LABEL(context->filename), PANGO_ELLIPSIZE_MIDDLE);
418     gtk_box_pack_start_defaults(GTK_BOX(hbox), context->filename);
419    
420     GtkWidget *button = gtk_button_new_with_label(_("Browse"));
421 harbaum 199 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
422     hildon_gtk_widget_set_theme_size(button,
423     (HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH));
424     #endif
425 harbaum 198 gtk_signal_connect(GTK_OBJECT(button), "clicked",
426     GTK_SIGNAL_FUNC(on_browse), context);
427     gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
428    
429     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox), hbox);
430    
431    
432     /* ------------------- Username/Password ------------------------- */
433     hbox = gtk_hbox_new(FALSE, 0);
434     vbox = gtk_vbox_new(FALSE, 0);
435     gtk_box_pack_start_defaults(GTK_BOX(vbox), left_label_new(_("Username:")));
436     gtk_box_pack_start_defaults(GTK_BOX(vbox), left_label_new(_("Password:")));
437     gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
438 harbaum 196
439     vbox = gtk_vbox_new(FALSE, 0);
440     #if !defined(USE_MAEMO) || (MAEMO_VERSION_MAJOR < 5)
441     context->username = gtk_entry_new();
442     context->password = gtk_entry_new();
443     #else
444     context->username = hildon_entry_new(HILDON_SIZE_AUTO);
445     hildon_gtk_entry_set_input_mode(GTK_ENTRY(context->username),
446     HILDON_GTK_INPUT_MODE_FULL);
447     context->password = hildon_entry_new(HILDON_SIZE_AUTO);
448     hildon_gtk_entry_set_input_mode(GTK_ENTRY(context->password),
449     HILDON_GTK_INPUT_MODE_FULL);
450     #endif
451     gtk_entry_set_visibility(GTK_ENTRY(context->password), FALSE);
452    
453     /* set saved defaults */
454 harbaum 198 if(appdata->gt.username)
455 harbaum 196 gtk_entry_set_text(GTK_ENTRY(context->username),
456 harbaum 198 appdata->gt.username);
457 harbaum 196
458 harbaum 198 if(appdata->gt.password)
459 harbaum 196 gtk_entry_set_text(GTK_ENTRY(context->password),
460 harbaum 198 appdata->gt.password);
461 harbaum 196
462     gtk_box_pack_start_defaults(GTK_BOX(vbox), context->username);
463     gtk_box_pack_start_defaults(GTK_BOX(vbox), context->password);
464     gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox);
465    
466 harbaum 198 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox), hbox);
467 harbaum 196
468 harbaum 198 gtk_dialog_set_default_response(GTK_DIALOG(context->dialog), GTK_RESPONSE_OK);
469 harbaum 196
470 harbaum 198 gtk_widget_show_all(context->dialog);
471 harbaum 196
472 harbaum 198 if(gtk_dialog_run(GTK_DIALOG(context->dialog)) == GTK_RESPONSE_OK) {
473 harbaum 196
474 harbaum 200 /* parse coordinates */
475     /* ... */
476    
477 harbaum 196 /* save values */
478 harbaum 198 if(appdata->gt.username) g_free(appdata->gt.username);
479     appdata->gt.username =
480 harbaum 196 g_strdup(gtk_entry_get_text(GTK_ENTRY(context->username)));
481    
482 harbaum 198 if(appdata->gt.password) g_free(appdata->gt.password);
483     appdata->gt.password =
484 harbaum 196 g_strdup(gtk_entry_get_text(GTK_ENTRY(context->password)));
485    
486 harbaum 198 if(appdata->gt.filename) g_free(appdata->gt.filename);
487     appdata->gt.filename =
488     g_strdup(gtk_label_get_text(GTK_LABEL(context->filename)));
489    
490 harbaum 200 /* get distance in kilometers */
491     appdata->gt.distance = dist_get(context->dst, FALSE);
492    
493 harbaum 196 ok = TRUE;
494     }
495    
496 harbaum 198 gtk_widget_destroy(context->dialog);
497 harbaum 196
498     return ok;
499     }
500    
501     void geotoad(appdata_t *appdata) {
502     if(!geotoad_available()) {
503     errorf(_("GeoToad is not installed on this device.\n"
504     "You need to install it in order to be able to use it."));
505     return;
506     }
507    
508     gt_context_t *context = g_new0(gt_context_t, 1);
509     context->appdata = appdata;
510    
511     printf("geoToad\n");
512    
513     if(gui_setup(context))
514     gui_run(context);
515     }
516    
517     gboolean geotoad_available(void) {
518     /* before doing anything make sure geotoad is installed */
519     return g_file_test(GEOTOAD, G_FILE_TEST_IS_EXECUTABLE);
520     }
521    
522