Contents of /trunk/src/geotoad.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 204 - (show annotations)
Mon Nov 23 18:32:06 2009 UTC (14 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 16464 byte(s)
Map jump function
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 #include <fcntl.h>
23 #include <sys/types.h>
24 #include <sys/wait.h>
25 #include <errno.h>
26 #include <math.h>
27
28 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR >= 5)
29 #include <hildon/hildon-entry.h>
30 #endif
31
32 #define GEOTOAD "/usr/bin/geotoad"
33
34 #define COLOR_ERR "red"
35 #define COLOR_OK "darkgreen"
36 #define COLOR_SYSTEM "darkblue"
37
38 #define BUFFER_SIZE 256
39
40 typedef struct {
41 appdata_t *appdata;
42
43 GtkWidget *dialog;
44
45 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 struct log_s {
53 GtkTextBuffer *buffer;
54 GtkWidget *view;
55 } log;
56
57 GtkWidget *username, *password, *filename;
58 GtkWidget *lat, *lon, *dst;
59
60 } gt_context_t;
61
62 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 printf("append: %s", buf);
70
71 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 gtk_text_buffer_get_end_iter(log->buffer, &end);
87 gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(log->view),
88 &end, 0.0, FALSE, 0, 0);
89 }
90
91 /* watch child process and receive events */
92 static void child_state_cb(GPid pid, gint status, gpointer data) {
93 gt_context_t *context = (gt_context_t*)data;
94
95 puts("child state");
96
97 if(WIFEXITED(status)) {
98 printf("child exited with return code %d\n", WEXITSTATUS(status));
99 } else
100 printf("child failed\n");
101
102 puts("gt exited");
103
104 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 /* Reap child if needed. */
111 waitpid (pid, NULL, WNOHANG);
112 }
113
114 static gboolean child_input_cb(GIOChannel *source,
115 GIOCondition condition,
116 gpointer data) {
117 gt_context_t *context = (gt_context_t*)data;
118 int fd = g_io_channel_unix_get_fd(source);
119 ssize_t bytes;
120
121 g_assert(condition == G_IO_IN);
122
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 char *color = NULL;
138 if(strstr(ptr, "Saved to ") != NULL)
139 color = COLOR_OK;
140
141 appendf(&context->log, color, "%s\n", ptr);
142
143 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 return TRUE;
158 }
159
160 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 static void run(gt_context_t *context) {
172 char str[8];
173
174 /* 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 /* build list of arguments to call geotoad */
183 GPtrArray *gt_argv = g_ptr_array_new();
184 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->username));
191
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 g_ptr_array_add (gt_argv, g_strdup_printf("N49 00.000 E008 23.000"));
212 g_ptr_array_add (gt_argv, NULL);
213
214 /* show all entries */
215 g_ptr_array_foreach(gt_argv, arg_dsp, context);
216
217 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 g_ptr_array_foreach(gt_argv, arg_free, NULL);
233 g_ptr_array_free(gt_argv, TRUE);
234 appendf(&context->log, COLOR_ERR,
235 _("GeoToad failed to start!\n%s\n"), error->message);
236 g_error_free(error);
237 return;
238 }
239
240 g_ptr_array_foreach(gt_argv, arg_free, NULL);
241 g_ptr_array_free (gt_argv, TRUE);
242
243 gt_watch = g_child_watch_source_new(pid);
244 g_source_set_callback(gt_watch, (GSourceFunc) child_state_cb, context, NULL);
245
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 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 }
266
267 /* 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 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 appendf(&context->log, COLOR_SYSTEM, "Running GeoToad\n");
313 run(context);
314
315 gtk_dialog_run(GTK_DIALOG(dialog));
316
317 gtk_widget_destroy(dialog);
318 }
319
320 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 static gboolean gui_setup(gt_context_t *context) {
365 appdata_t *appdata = context->appdata;
366 gboolean ok = FALSE;
367
368 /* 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
375 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 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
384
385 GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
386 gtk_box_pack_start_defaults(GTK_BOX(vbox), left_label_new(_("Position:")));
387 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 GtkWidget *ihbox = gtk_hbox_new(FALSE, 0);
399 context->lat = lat_entry_new(appdata->gt.lat);
400 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 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 gtk_box_pack_start_defaults(GTK_BOX(vbox), context->dst);
408 gtk_box_pack_start_defaults(GTK_BOX(hbox), vbox);
409
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 #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 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
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 if(appdata->username)
455 gtk_entry_set_text(GTK_ENTRY(context->username),
456 appdata->username);
457
458 if(appdata->gt.password)
459 gtk_entry_set_text(GTK_ENTRY(context->password),
460 appdata->gt.password);
461
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 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox), hbox);
467
468 gtk_dialog_set_default_response(GTK_DIALOG(context->dialog), GTK_RESPONSE_OK);
469
470 gtk_widget_show_all(context->dialog);
471
472 if(gtk_dialog_run(GTK_DIALOG(context->dialog)) == GTK_RESPONSE_OK) {
473
474 /* parse coordinates */
475 /* ... */
476
477 /* save values */
478 if(appdata->username) g_free(appdata->username);
479 appdata->username =
480 g_strdup(gtk_entry_get_text(GTK_ENTRY(context->username)));
481
482 if(appdata->gt.password) g_free(appdata->gt.password);
483 appdata->gt.password =
484 g_strdup(gtk_entry_get_text(GTK_ENTRY(context->password)));
485
486 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 /* get distance in kilometers */
491 appdata->gt.distance = dist_get(context->dst, FALSE);
492
493 ok = TRUE;
494 }
495
496 gtk_widget_destroy(context->dialog);
497
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