Contents of /trunk/src/main.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 165 - (show annotations)
Sun Nov 8 20:32:55 2009 UTC (14 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 71001 byte(s)
Lots of Fremantle'ization
1 /*
2 * This file is part of GPXView.
3 *
4 * GPXView is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * GPXView is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with GPXView. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #include <stdio.h>
19 #include <string.h>
20 #include <math.h>
21
22 #include <curl/curl.h>
23
24 #include <time.h>
25 #include <sys/time.h>
26
27 #include "gpxview.h"
28 #include "custom_size_renderer.h"
29 #include "custom_rating_renderer.h"
30 #include "custom_type_renderer.h"
31
32 #ifdef USE_MAEMO
33 #include <hildon/hildon-banner.h>
34 #if MAEMO_VERSION_MAJOR >= 5
35 #include <hildon/hildon-note.h>
36 #endif
37 #endif
38
39 #include <locale.h>
40
41 extern char *strcasestr (__const char *__haystack, __const char *__needle);
42
43 #if defined(USE_BREAD_CRUMB_TRAIL) || defined(BCT)
44 static void crumb_add(appdata_t *appdata, char *name, int level,
45 gpointer user_data);
46
47 enum {
48 CRUMB_GPXLIST = 0,
49 CRUMB_CACHELIST,
50 CRUMB_SEARCH_GLOBAL,
51 CRUMB_SEARCH_GPX,
52 CRUMB_CACHE,
53 };
54 #endif
55
56 /* size of first buffer malloc; start small to exercise grow routines */
57 #define FIRSTSIZE 1
58
59 void errorf(const char *fmt, ...) {
60 va_list args;
61 char *buf = NULL;
62 size_t bufsize;
63 char *newbuf;
64 size_t nextsize = 0;
65 int outsize;
66
67 bufsize = 0;
68 for (;;) {
69 if (bufsize == 0) {
70 if ((buf = (char *)malloc(FIRSTSIZE)) == NULL)
71 return;
72
73 bufsize = 1;
74 } else if ((newbuf = (char *)realloc(buf, nextsize)) != NULL) {
75 buf = newbuf;
76 bufsize = nextsize;
77 } else {
78 free(buf);
79 return;
80 }
81
82 va_start(args, fmt);
83 outsize = vsnprintf(buf, bufsize, fmt, args);
84 va_end(args);
85
86 if (outsize == -1) {
87 nextsize = bufsize * 2;
88 } else if (outsize == bufsize) {
89 nextsize = bufsize * 2;
90 } else if (outsize > bufsize) {
91 nextsize = outsize + 2; // Linux!
92 } else if (outsize == bufsize - 1) {
93 nextsize = bufsize * 2;
94 } else {
95 /* Output was not truncated */
96 break;
97 }
98 }
99
100 #if !defined(USE_MAEMO) || (MAEMO_VERSION_MAJOR < 5)
101 GtkWidget *dialog = gtk_message_dialog_new(
102 GTK_WINDOW(NULL),
103 GTK_DIALOG_DESTROY_WITH_PARENT,
104 GTK_MESSAGE_ERROR,
105 GTK_BUTTONS_CLOSE, buf);
106
107 gtk_window_set_title(GTK_WINDOW(dialog), _("ERROR"));
108 #else
109 GtkWidget *dialog =
110 hildon_note_new_information(GTK_WINDOW(NULL), buf);
111 #endif
112
113 gtk_dialog_run(GTK_DIALOG(dialog));
114 gtk_widget_destroy(dialog);
115
116 free(buf);
117 }
118
119 gpx_t *choose_file(appdata_t *appdata, gboolean whole_dir) {
120 GtkWidget *dialog;
121 gpx_t *gpx = NULL;
122
123 #ifdef USE_MAEMO
124 dialog = hildon_file_chooser_dialog_new(GTK_WINDOW(appdata->window),
125 whole_dir?GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER :
126 GTK_FILE_CHOOSER_ACTION_OPEN);
127
128 #ifdef HILDON_HELP
129 hildon_help_dialog_help_enable(GTK_DIALOG(dialog),
130 HELP_ID_IMPORT, appdata->osso_context);
131 #endif
132 #else
133 dialog = gtk_file_chooser_dialog_new (whole_dir?_("Import directory"):
134 _("Import file"),
135 GTK_WINDOW(appdata->window),
136 whole_dir?GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER :
137 GTK_FILE_CHOOSER_ACTION_OPEN,
138 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
139 GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
140 NULL);
141 #endif
142
143 /* use path if one is present */
144 if(appdata->path)
145 gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog),
146 appdata->path);
147
148 gtk_widget_show_all (GTK_WIDGET(dialog));
149 if (gtk_dialog_run (GTK_DIALOG(dialog)) == GTK_FM_OK) {
150 char *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
151
152 if(filename) {
153 gpx_dialog_t *dialog = gpx_busy_dialog_new(GTK_WIDGET(appdata->window));
154
155 if(!whole_dir)
156 gpx = gpx_parse(dialog, filename);
157 else {
158 /* cur trailing '/' if present */
159 if(strlastchr(filename) == '/')
160 filename[strlen(filename)] = 0;
161
162 gpx = gpx_parse_dir(dialog, filename);
163 }
164
165 gpx_busy_dialog_destroy(dialog);
166
167 /* save path if gpx was successfully loaded */
168 if(gpx) {
169 char *r = strrchr(filename, '/');
170
171 /* there is a delimiter, use everything left of it as path */
172 if(r && !whole_dir) {
173 *r = 0;
174 if(appdata->path) free(appdata->path);
175 appdata->path = strdup(filename);
176 /* restore path ... just in case ... */
177 *r = '/';
178 }
179
180 if(whole_dir)
181 appdata->path = strdup(filename);
182 } else
183 errorf(_("Load error"));
184
185 g_free (filename);
186 } else {
187 #ifndef USE_MAEMO
188 errorf(_("Error accessing the file."));
189 #else
190 errorf(_("Error accessing the file. This may happen if the file "
191 "resides on a remote file system. Please copy the file onto "
192 "the device (e.g. onto the memory card) and try again."));
193 #endif
194 }
195 }
196
197 gtk_widget_destroy (dialog);
198
199 return gpx;
200 }
201
202 /******************** begin of cachelist ********************/
203
204 enum {
205 CACHELIST_COL_TYPE = 0,
206 CACHELIST_COL_ID,
207 CACHELIST_COL_NAME,
208 CACHELIST_COL_SIZE,
209 CACHELIST_COL_RATING,
210 CACHELIST_COL_BEARING,
211 CACHELIST_COL_DISTANCE,
212 CACHELIST_COL_DATA,
213 CACHELIST_COL_AVAIL,
214 CACHELIST_COL_ARCHIVE,
215 CACHELIST_NUM_COLS
216 } ;
217
218 void cachelist_goto_cache(appdata_t *appdata, cache_t *cache) {
219 #if !defined(USE_BREAD_CRUMB_TRAIL) && !defined(BCT)
220 cache_dialog(appdata, cache);
221 #else
222 gtk_container_remove(GTK_CONTAINER(appdata->vbox), appdata->cur_view);
223 appdata->cur_view = cache_view(appdata, cache);
224 gtk_box_pack_start_defaults(GTK_BOX(appdata->vbox), appdata->cur_view);
225 gtk_widget_show_all(appdata->vbox);
226
227 crumb_add(appdata, cache->name, CRUMB_CACHE, cache);
228 #endif
229 }
230
231 static void cachelist_view_onRowActivated(GtkTreeView *treeview,
232 GtkTreePath *path,
233 GtkTreeViewColumn *col,
234 gpointer userdata) {
235 appdata_t *appdata = (appdata_t*)userdata;
236 GtkTreeIter iter;
237 GtkTreeModel *model = gtk_tree_view_get_model(treeview);
238
239 #ifdef USE_MAEMO
240 /* check if a cache is already selected and ignore click if yes */
241 /* (was probably a double click) */
242 if(appdata->cur_cache) return;
243 #endif
244
245 if(gtk_tree_model_get_iter(model, &iter, path)) {
246 cache_t *cache;
247 gtk_tree_model_get(model, &iter, CACHELIST_COL_DATA, &cache, -1);
248 cachelist_goto_cache(appdata, cache);
249 }
250 }
251
252 typedef struct {
253 appdata_t *appdata;
254 GtkTreePath *path;
255 gboolean done;
256 } cachelist_expose_t;
257
258 static gboolean cachelist_expose(GtkWidget *widget, GdkEventExpose *event,
259 gpointer user_data) {
260 cachelist_expose_t *ce = (cachelist_expose_t*)user_data;
261
262 if(event->type == GDK_EXPOSE) {
263 if(ce->path && !ce->done) {
264 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(widget),
265 ce->path, NULL, TRUE, 0.5, 0.5);
266 gtk_tree_path_free(ce->path);
267 ce->done = TRUE;
268 }
269 }
270
271 return FALSE;
272 }
273
274 static void cachelist_destroy(GtkWidget *widget, gpointer user_data) {
275 cachelist_expose_t *ce = (cachelist_expose_t*)user_data;
276
277 printf("cachelist timer removed\n");
278 g_assert(ce->appdata->cachelist_handler_id);
279 gtk_timeout_remove(ce->appdata->cachelist_handler_id);
280 ce->appdata->cachelist_handler_id = 0;
281
282 free(user_data);
283 }
284
285 #define CACHELIST_UPDATE_TIMEOUT (30000)
286
287 static GtkWidget *cachelist_create(appdata_t *appdata, gpx_t *gpx,
288 cache_t *sel_cache);
289
290 void cachelist_redraw(appdata_t *appdata) {
291 if(!appdata->cur_view) {
292 printf("cachelist redraw: no active view\n");
293 return;
294 }
295
296 g_assert(!appdata->cur_cache);
297 int redraw = 0;
298 if(appdata->search_results)
299 redraw = 1;
300 else {
301 if(appdata->cur_gpx)
302 redraw = 2; // redraw cachelist
303 }
304
305 if(redraw) {
306 GtkWidget *container = appdata->vbox;
307
308 #ifdef USE_STACKABLE_WINDOW
309 HildonWindowStack *stack = hildon_window_stack_get_default();
310 container = hildon_window_stack_peek(stack);
311 #endif
312
313 gtk_container_remove(GTK_CONTAINER(container), appdata->cur_view);
314 switch(redraw) {
315 case 1:
316 appdata->cur_view = cachelist_create(appdata,
317 appdata->search_results, NULL);
318 break;
319 case 2:
320 appdata->cur_view = cachelist_create(appdata,
321 appdata->cur_gpx, NULL);
322 break;
323 }
324
325 #ifdef USE_STACKABLE_WINDOW
326 if(container != appdata->vbox)
327 gtk_container_add(GTK_CONTAINER(container), appdata->cur_view);
328 else
329 #endif
330 gtk_box_pack_start_defaults(GTK_BOX(container), appdata->cur_view);
331
332 gtk_widget_show_all(container);
333 }
334 }
335
336
337 static gboolean cachelist_update(gpointer data) {
338
339 printf("cachelist timer fired!\n");
340
341 appdata_t *appdata = (appdata_t*)data;
342
343 if(appdata->cur_cache)
344 return TRUE;
345
346 #ifdef USE_MAEMO
347 if(appdata->cachelist_disable_screensaver)
348 if (osso_display_blanking_pause(appdata->osso_context) != OSSO_OK)
349 fprintf(stderr, "error with display blank\n");
350 #endif
351
352 if(appdata->cachelist_update)
353 cachelist_redraw(appdata);
354
355 return TRUE;
356 }
357
358 static void cachelist_timer_reset(appdata_t *appdata) {
359 printf("cachelist timer reset\n");
360 g_assert(appdata->cachelist_handler_id);
361 gtk_timeout_remove(appdata->cachelist_handler_id);
362 appdata->cachelist_handler_id =
363 gtk_timeout_add(CACHELIST_UPDATE_TIMEOUT, cachelist_update, appdata);
364 }
365
366 static gboolean cachelist_update_reset0(GtkWidget *widget,
367 GdkEventButton *event,
368 gpointer user_data) {
369 cachelist_timer_reset((appdata_t*)user_data);
370 return FALSE;
371 }
372
373 static void cachelist_update_reset1(GtkAdjustment *adj,
374 gpointer user_data) {
375 cachelist_timer_reset((appdata_t*)user_data);
376 }
377
378 static GtkWidget *cachelist_create(appdata_t *appdata, gpx_t *gpx,
379 cache_t *sel_cache) {
380 GtkCellRenderer *renderer;
381 GtkWidget *view;
382 GtkListStore *store;
383 GtkTreeIter iter;
384
385 if(!gpx->notes_loaded) {
386 notes_load_all(appdata, gpx);
387 gpx->notes_loaded = TRUE;
388 }
389
390 appdata->cur_items = appdata->cachelist_items;
391
392 /* first sort the caches */
393 pos_t *refpos = get_pos(appdata);
394 gpx_sort(gpx, GPX_SORT_BY_DISTANCE, refpos);
395
396 view = gtk_tree_view_new();
397
398 /* --- "Type" column --- */
399 renderer = custom_cell_renderer_type_new();
400 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
401 -1, "Type", renderer, "type", CACHELIST_COL_TYPE, NULL);
402
403 /* --- "Id" column --- */
404 if(appdata->cachelist_items & CACHELIST_ITEM_ID) {
405 renderer = gtk_cell_renderer_text_new();
406 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
407 -1, "Id", renderer, "text", CACHELIST_COL_ID, NULL);
408 }
409
410 /* --- "Name" column --- */
411 renderer = gtk_cell_renderer_text_new();
412 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
413
414 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
415 "Name", renderer, "text", CACHELIST_COL_NAME, NULL);
416 gtk_tree_view_column_set_expand(column, TRUE);
417 gtk_tree_view_insert_column(GTK_TREE_VIEW(view), column, -1);
418
419 g_object_set(renderer, "foreground", "#ff0000", NULL );
420 gtk_tree_view_column_add_attribute(column, renderer, "strikethrough",
421 CACHELIST_COL_AVAIL);
422 gtk_tree_view_column_add_attribute(column, renderer,
423 "foreground-set", CACHELIST_COL_ARCHIVE);
424
425 /* --- "Size" column --- */
426 if(appdata->cachelist_items & CACHELIST_ITEM_SIZE) {
427 renderer = custom_cell_renderer_size_new();
428 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
429 -1, "Size", renderer, "size", CACHELIST_COL_SIZE, NULL);
430 }
431
432 /* --- "Rating" column --- */
433 if(appdata->cachelist_items & CACHELIST_ITEM_RATING) {
434 renderer = custom_cell_renderer_rating_new();
435 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
436 -1, "Rating", renderer, "rating", CACHELIST_COL_RATING, NULL);
437 }
438
439 /* --- "Bearing" column --- */
440 renderer = gtk_cell_renderer_pixbuf_new();
441 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
442 -1, "Bearing", renderer, "pixbuf", CACHELIST_COL_BEARING, NULL);
443
444 /* --- "Distance" column --- */
445 renderer = gtk_cell_renderer_text_new();
446 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
447 -1, "Distance", renderer, "text", CACHELIST_COL_DISTANCE, NULL);
448
449 store = gtk_list_store_new(CACHELIST_NUM_COLS, G_TYPE_INT,
450 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT,
451 G_TYPE_INT, GDK_TYPE_PIXBUF, G_TYPE_STRING,
452 G_TYPE_POINTER, G_TYPE_BOOLEAN,
453 G_TYPE_BOOLEAN);
454
455 GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
456
457 GtkTreeIter sel_iter;
458 gboolean sel_iter_valid = FALSE;
459 GtkTreePath *path = NULL;
460 cache_t *cache = gpx->cache;
461 while(cache) {
462 char str[32];
463 gpx_pos_get_distance_str(str, sizeof(str),
464 *refpos, gpx_cache_pos(cache), appdata->imperial);
465
466 int dint = (int)(cache->difficulty*2-2);
467 if(dint < 0) dint = 0;
468 if(dint > 8) dint = 8;
469
470 int tint = (int)(cache->terrain*2-2);
471 if(tint < 0) tint = 0;
472 if(tint > 8) tint = 8;
473
474 /* cache type includes "solved" flag in lowest bit */
475 int type = (cache->type<<8) +
476 (cache->notes?4:0) +
477 ((cache->notes && cache->notes->override)?1:0) +
478 ((cache->notes && cache->notes->found)?2:0);
479
480 if((!(type & 2)) || !appdata->cachelist_hide_found) {
481
482 /* Append a row and fill in some data */
483 gtk_list_store_append (store, &iter);
484
485 gtk_list_store_set(store, &iter,
486 CACHELIST_COL_TYPE, type,
487 CACHELIST_COL_ID, cache->id,
488 CACHELIST_COL_NAME, cache->name,
489 CACHELIST_COL_SIZE, cache->container,
490 CACHELIST_COL_RATING, 100 * dint + tint,
491 CACHELIST_COL_BEARING,
492 icon_bearing(*refpos, gpx_cache_pos(cache)),
493 CACHELIST_COL_DISTANCE, str,
494 CACHELIST_COL_DATA, cache,
495 CACHELIST_COL_AVAIL, !cache->available ||
496 cache->archived,
497 CACHELIST_COL_ARCHIVE, cache->archived,
498 -1);
499
500 if(cache == sel_cache) {
501 sel_iter = iter;
502 sel_iter_valid = TRUE;
503 }
504 }
505
506 cache = cache->next;
507 }
508
509 gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
510 g_object_unref(store);
511
512 if(sel_iter_valid) {
513 gtk_tree_selection_select_iter(sel, &sel_iter);
514 path = gtk_tree_model_get_path(GTK_TREE_MODEL(store), &sel_iter);
515 }
516
517 /* make list react on clicks */
518 g_signal_connect(view, "row-activated",
519 (GCallback)cachelist_view_onRowActivated, appdata);
520
521 cachelist_expose_t *ce = malloc(sizeof(cachelist_expose_t));
522 ce->appdata = appdata;
523 ce->path = path;
524 ce->done = FALSE;
525
526 g_signal_connect(view, "expose-event",
527 (GCallback)cachelist_expose, ce);
528 g_signal_connect(view, "destroy",
529 (GCallback)cachelist_destroy, ce);
530
531 /* put this inside a scrolled view */
532 #ifndef USE_PANNABLE_AREA
533 GtkWidget *scrolled_window = gtk_scrolled_window_new (NULL, NULL);
534 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
535 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
536 gtk_container_add(GTK_CONTAINER(scrolled_window), view);
537 #else
538 GtkWidget *pannable_area = hildon_pannable_area_new();
539
540 gtk_container_add(GTK_CONTAINER(pannable_area), view);
541 #endif
542
543 /* add a timer for automatic update */
544 g_assert(!appdata->cachelist_handler_id);
545 appdata->cachelist_handler_id =
546 gtk_timeout_add(CACHELIST_UPDATE_TIMEOUT, cachelist_update, appdata);
547
548 /* update timer is being reset if the user scrolls or selects */
549 g_signal_connect(view, "button-press-event",
550 (GCallback)cachelist_update_reset0, appdata);
551
552 #ifndef USE_PANNABLE_AREA
553 g_signal_connect(gtk_scrolled_window_get_vadjustment(
554 GTK_SCROLLED_WINDOW(scrolled_window)),
555 "value-changed",
556 (GCallback)cachelist_update_reset1, appdata);
557
558 return scrolled_window;
559 #else
560 g_signal_connect(hildon_pannable_area_get_vadjustment(
561 HILDON_PANNABLE_AREA(pannable_area)),
562 "value-changed",
563 (GCallback)cachelist_update_reset1, appdata);
564
565
566 return pannable_area;
567 #endif
568 }
569
570 #ifndef USE_MAEMO
571 void cachelist_dialog(appdata_t *appdata, gpx_t *gpx) {
572 GtkWidget *dialog =
573 gtk_dialog_new_with_buttons(gpx->name, GTK_WINDOW(appdata->window),
574 GTK_DIALOG_NO_SEPARATOR | GTK_DIALOG_MODAL |
575 GTK_DIALOG_DESTROY_WITH_PARENT,
576 GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
577 NULL);
578
579 gtk_window_set_default_size(GTK_WINDOW(dialog), DIALOG_WIDTH, DIALOG_HEIGHT);
580
581 gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox),
582 cachelist_create(appdata, gpx, NULL));
583
584 gtk_widget_show_all(dialog);
585
586 gtk_dialog_run(GTK_DIALOG(dialog));
587 gtk_widget_destroy(dialog);
588 }
589 #else
590 #ifdef USE_STACKABLE_WINDOW
591 static void search_result_free(gpx_t *result);
592
593 void on_cachelist_destroy(GtkWidget *widget, appdata_t *appdata) {
594 if(appdata->search_results) {
595 search_result_free(appdata->search_results);
596 appdata->search_results = NULL;
597 }
598 appdata->cur_gpx = NULL;
599
600 #ifdef ENABLE_OSM_GPS_MAP
601 map_update(appdata);
602 #endif
603
604 /* restore cur_view */
605 appdata->cur_view = g_object_get_data(G_OBJECT(widget), "cur_view");
606 }
607
608 void cachelist_dialog(appdata_t *appdata, gpx_t *gpx) {
609 GtkWidget *window = hildon_stackable_window_new();
610
611 /* store last "cur_view" in window */
612 g_object_set_data(G_OBJECT(window), "cur_view", appdata->cur_view);
613
614 appdata->cur_gpx = gpx;
615 char *title = g_strdup_printf("%s - GPXView", gpx->name);
616 gtk_window_set_title(GTK_WINDOW(window), title);
617 g_free(title);
618
619 appdata->cur_view = cachelist_create(appdata, gpx, NULL);
620 gtk_container_add(GTK_CONTAINER(window), appdata->cur_view);
621
622
623 hildon_window_set_app_menu(HILDON_WINDOW(window),
624 menu_create(appdata, MENU_CACHELIST));
625
626 g_signal_connect(G_OBJECT(window), "destroy",
627 G_CALLBACK(on_cachelist_destroy), appdata);
628
629 gtk_widget_show_all(window);
630
631 #ifdef ENABLE_OSM_GPS_MAP
632 map_update(appdata);
633 #endif
634 }
635 #endif
636 #endif
637
638 /******************** end of cachelist ********************/
639
640 /******************** begin of gpxlist ********************/
641
642 enum {
643 GPXLIST_COL_ICON = 0,
644 GPXLIST_COL_FILENAME,
645 GPXLIST_COL_NAME,
646 GPXLIST_COL_DATE,
647 GPXLIST_COL_CACHES,
648 GPXLIST_COL_OPEN,
649 #ifdef USE_PANNABLE_AREA
650 GPXLIST_COL_DELETE,
651 #endif
652 GPXLIST_COL_DATA,
653 GPXLIST_NUM_COLS
654 } ;
655
656 static GdkPixbuf *gpx_icon_get(gpx_t *gpx) {
657 if(gpx->filename && g_file_test(gpx->filename, G_FILE_TEST_IS_DIR))
658 return icon_get(ICON_FILE, 1);
659
660 if(gpx->filename&& !strcasecmp(gpx->filename+strlen(gpx->filename)-4,".zip"))
661 return icon_get(ICON_FILE, 2);
662
663 return icon_get(ICON_FILE, 0);
664 }
665
666 static void gpxlist_set(GtkListStore *store, GtkTreeIter *iter, gpx_t *gpx) {
667 char date_str[32], cnum[32];
668
669 if(gpx->year && gpx->month && gpx->day) {
670 GDate *date = g_date_new_dmy(gpx->day, gpx->month, gpx->year);
671 g_date_strftime(date_str, sizeof(date_str), "%x", date);
672 g_date_free(date);
673 } else
674 strcpy(date_str, "---");
675
676 char *fname = strrchr(gpx->filename, '/');
677 if(!fname) fname = gpx->filename;
678 else fname++; /* skip '/' */
679
680 snprintf(cnum, sizeof(cnum), "%d", gpx_total_caches(gpx));
681
682 /* Append a row and fill in some data */
683 gtk_list_store_set(store, iter,
684 GPXLIST_COL_ICON, gpx_icon_get(gpx),
685 GPXLIST_COL_FILENAME, fname,
686 GPXLIST_COL_NAME, gpx->name,
687 GPXLIST_COL_DATE, gpx->closed?NULL:date_str,
688 GPXLIST_COL_OPEN, !gpx->closed,
689 GPXLIST_COL_CACHES, gpx->closed?NULL:cnum,
690 #ifdef USE_PANNABLE_AREA
691 GPXLIST_COL_DELETE, icon_get(ICON_MISC, 7),
692 #endif
693 GPXLIST_COL_DATA, gpx,
694 -1);
695 }
696
697 static void gpxlist_remove(appdata_t *appdata,
698 GtkListStore *store, GtkTreeIter *iter,
699 gpx_t *gpx) {
700
701 printf("removing %s\n", gpx->name);
702
703 /* de-chain */
704 gpx_t **prev = &appdata->gpx;
705 while(*prev != gpx) prev = &((*prev)->next);
706 *prev = gpx->next;
707
708 /* remove gconf entry if file was closed */
709 gconf_remove_closed_name(appdata, gpx->filename);
710
711 /* free gpx itself */
712 gpx_free(gpx);
713
714 /* and remove from store */
715 gtk_list_store_remove(store, iter);
716 }
717
718 static void gpxlist_close(appdata_t *appdata,
719 GtkListStore *store, GtkTreeIter *iter,
720 gpx_t *gpx) {
721
722 printf("closing %s\n", gpx->name);
723
724 g_assert(!gpx->closed);
725 gpx->closed = TRUE;
726
727 /* free all associated caches */
728 gpx_free_caches(gpx);
729
730 /* update entry */
731 gpxlist_set(store, iter, gpx);
732
733 /* save name in gconf so we know this has been closed */
734 gconf_save_closed_name(appdata, gpx->filename, gpx->name);
735 }
736
737 void gpxlist_goto_cachelist(appdata_t *appdata, gpx_t *gpx) {
738 #if !defined(USE_BREAD_CRUMB_TRAIL) && !defined(BCT)
739 #ifdef USE_STACKABLE_WINDOW
740 if(!appdata->cur_gpx)
741 #endif
742 cachelist_dialog(appdata, gpx);
743 #ifdef USE_STACKABLE_WINDOW
744 else
745 printf("selected gpx, but cachelist window already present\n");
746 #endif
747 #else
748 gtk_container_remove(GTK_CONTAINER(appdata->vbox), appdata->cur_view);
749 appdata->cur_view = cachelist_create(appdata, gpx, NULL);
750 gtk_box_pack_start_defaults(GTK_BOX(appdata->vbox), appdata->cur_view);
751 gtk_widget_show_all(appdata->vbox);
752
753 crumb_add(appdata, gpx->name, CRUMB_CACHELIST, gpx);
754 #endif
755 }
756
757 static void gpxlist_view_onRowActivated(GtkTreeView *treeview,
758 GtkTreePath *path,
759 GtkTreeViewColumn *col,
760 gpointer userdata) {
761 appdata_t *appdata = (appdata_t*)userdata;
762 GtkTreeIter iter;
763 GtkTreeModel *model = gtk_tree_view_get_model(treeview);
764
765 #ifdef USE_MAEMO
766 /* check if a cache is already selected and ignore click if yes */
767 /* (was probably a double click) */
768 if(appdata->cur_gpx) return;
769 #endif
770
771 if (gtk_tree_model_get_iter(model, &iter, path)) {
772 gpx_t *gpx;
773 gtk_tree_model_get(model, &iter, GPXLIST_COL_DATA, &gpx, -1);
774
775 #ifdef USE_PANNABLE_AREA
776 /* get name of column the user clicked on */
777 const char *col_name = NULL;
778 if(col) col_name = gtk_tree_view_column_get_title(col);
779
780 if(col_name && !strcmp(col_name, "Del")) {
781 printf("clicked delete\n");
782
783 #if !defined(USE_MAEMO) || (MAEMO_VERSION_MAJOR < 5)
784 /* ask user what he wants */
785 GtkWidget *dialog = gtk_message_dialog_new(
786 GTK_WINDOW(appdata->window),
787 GTK_DIALOG_DESTROY_WITH_PARENT,
788 GTK_MESSAGE_QUESTION,
789 GTK_BUTTONS_CANCEL,
790 _("Do you want to close this entry only or do "
791 "you want to remove it completely from the list?"));
792
793 gtk_dialog_add_buttons(GTK_DIALOG(dialog),
794 _("Remove"), 1,
795 _("Close"), 2,
796 NULL);
797
798 gtk_window_set_title(GTK_WINDOW(dialog), _("Close or remove entry?"));
799 #else
800
801 GtkWidget *dialog =
802 gtk_dialog_new_with_buttons(_("Close or remove entry?"),
803 GTK_WINDOW(appdata->window),
804 GTK_DIALOG_DESTROY_WITH_PARENT,
805 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
806 _("Remove"), 1,
807 _("Close"), 2,
808 NULL);
809
810 GtkWidget *content_area =
811 gtk_dialog_get_content_area (GTK_DIALOG (dialog));
812
813 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
814
815 gtk_box_pack_start(GTK_BOX(hbox),
816 gtk_image_new_from_stock( GTK_STOCK_DIALOG_QUESTION,
817 GTK_ICON_SIZE_DIALOG),
818 FALSE, FALSE, 0);
819
820 GtkWidget *label = gtk_label_new(
821 _("Do you want to close this entry only or do "
822 "you want to remove it completely from the list?"));
823
824 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
825 gtk_label_set_line_wrap_mode(GTK_LABEL(label), PANGO_WRAP_WORD);
826
827 gtk_box_pack_start_defaults(GTK_BOX(hbox), label);
828 gtk_container_add (GTK_CONTAINER (content_area), hbox);
829
830 gtk_widget_show_all (dialog);
831 #endif
832
833 if(gpx->closed)
834 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), 2, FALSE);
835
836 /* set the active flag again if the user answered "no" */
837 switch(gtk_dialog_run(GTK_DIALOG(dialog))) {
838
839 case 1:
840 gpxlist_remove(appdata, GTK_LIST_STORE(model), &iter, gpx);
841 break;
842
843 case 2:
844 gpxlist_close(appdata, GTK_LIST_STORE(model), &iter, gpx);
845 break;
846
847 default:
848 break;
849 }
850
851 gtk_widget_destroy(dialog);
852
853 } else
854 #endif
855 {
856
857 /* this gpx file may be closed. Since the user definitely wants */
858 /* to use it, we just open it again */
859 if(gpx->closed) {
860 gpx_dialog_t *dialog =
861 gpx_busy_dialog_new(GTK_WIDGET(appdata->window));
862 gpx_t *new = NULL;
863
864 if(g_file_test(gpx->filename, G_FILE_TEST_IS_DIR))
865 new = gpx_parse_dir(dialog, gpx->filename);
866 else
867 new = gpx_parse(dialog, gpx->filename);
868
869 if(new) {
870 gpx_t **prev = &(appdata->gpx);
871 while(*prev && *prev != gpx)
872 prev = &(*prev)->next;
873
874 /* this entry _must_ be in the list */
875 g_assert(*prev);
876
877 /* replace gpx entry with the new "open" one */
878 (*prev) = new;
879 new->next = gpx->next;
880 gpx->next = NULL;
881
882 /* free old closed one */
883 gpx_free(gpx);
884
885 gpx = new;
886
887 /* finally update the visible list */
888 gpxlist_set(appdata->gpxstore, &iter, gpx);
889
890 /* and remove gconf entry */
891 gconf_remove_closed_name(appdata, gpx->filename);
892
893 #ifndef USE_PANNABLE_AREA
894 gtk_widget_set_sensitive(appdata->menu_close, TRUE);
895 #endif
896 } else {
897 printf("unable to reopen file %s\n", gpx->filename);
898 return;
899 }
900
901 gpx_busy_dialog_destroy(dialog);
902 }
903
904 gpxlist_goto_cachelist(appdata, gpx);
905 }
906 }
907 }
908
909 #ifndef USE_PANNABLE_AREA
910 static gboolean
911 view_selection_func(GtkTreeSelection *selection, GtkTreeModel *model,
912 GtkTreePath *path, gboolean path_currently_selected,
913 gpointer userdata) {
914 appdata_t *appdata = (appdata_t*)userdata;
915 GtkTreeIter iter;
916
917 if(gtk_tree_model_get_iter(model, &iter, path)) {
918 gpx_t *gpx;
919 gtk_tree_model_get(model, &iter, GPXLIST_COL_DATA, &gpx, -1);
920
921 gtk_widget_set_sensitive(appdata->menu_remove, !path_currently_selected);
922
923 if(!gpx->closed)
924 gtk_widget_set_sensitive(appdata->menu_close, !path_currently_selected);
925 }
926
927 return TRUE; /* allow selection state to change */
928 }
929 #endif
930
931 static GtkWidget *gpxlist_create_view_and_model(appdata_t *appdata,
932 gpx_t *sel_gpx) {
933 gpx_t *gpx = appdata->gpx;
934 GtkCellRenderer *renderer;
935
936 /* saved displayed items */
937 appdata->cur_items = appdata->gpxlist_items;
938
939 #ifndef USE_PANNABLE_AREA
940 /* nothing selected yet */
941 gtk_widget_set_sensitive(appdata->menu_remove, FALSE);
942 gtk_widget_set_sensitive(appdata->menu_close, FALSE);
943 #endif
944
945 appdata->gpxview = gtk_tree_view_new ();
946
947 GtkTreeSelection *selection =
948 gtk_tree_view_get_selection(GTK_TREE_VIEW(appdata->gpxview));
949 #ifndef USE_PANNABLE_AREA
950 gtk_tree_selection_set_select_function(selection, view_selection_func,
951 appdata, NULL);
952 #endif
953
954 /* --- "Icon" column --- */
955 renderer = gtk_cell_renderer_pixbuf_new();
956 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(appdata->gpxview),
957 -1, "Icon", renderer,
958 "pixbuf", GPXLIST_COL_ICON,
959 #ifdef USE_PANNABLE_AREA
960 /* at least one entry needs to be sensitive. */
961 /* This is the delete icon if the PANNABLE_AREA is used */
962 "sensitive", GPXLIST_COL_OPEN,
963 #endif
964 NULL);
965
966 /* --- "FileName" column --- */
967 if(appdata->gpxlist_items & GPXLIST_ITEM_FILENAME) {
968 renderer = gtk_cell_renderer_text_new();
969 gtk_tree_view_insert_column_with_attributes(
970 GTK_TREE_VIEW(appdata->gpxview),
971 -1, "Filename", renderer,
972 "text", GPXLIST_COL_FILENAME,
973 "sensitive", GPXLIST_COL_OPEN,
974 NULL);
975 }
976
977 /* --- "Name" column --- */
978 renderer = gtk_cell_renderer_text_new();
979 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
980
981 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
982 "Name", renderer,
983 "text", GPXLIST_COL_NAME,
984 "sensitive", GPXLIST_COL_OPEN,
985 NULL);
986 gtk_tree_view_column_set_expand(column, TRUE);
987 gtk_tree_view_insert_column(GTK_TREE_VIEW(appdata->gpxview), column, -1);
988
989 /* --- "Date" column --- */
990 if(appdata->gpxlist_items & GPXLIST_ITEM_DATE) {
991 renderer = gtk_cell_renderer_text_new();
992 g_object_set(renderer, "xalign", 1.0, NULL );
993 gtk_tree_view_insert_column_with_attributes(
994 GTK_TREE_VIEW(appdata->gpxview),
995 -1, "Date", renderer,
996 "text", GPXLIST_COL_DATE,
997 "sensitive", GPXLIST_COL_OPEN,
998 NULL);
999 }
1000
1001 /* --- "Number of caches" column --- */
1002 if(appdata->gpxlist_items & GPXLIST_ITEM_CNUM) {
1003 renderer = gtk_cell_renderer_text_new();
1004 g_object_set(renderer, "xalign", 1.0, NULL );
1005 gtk_tree_view_insert_column_with_attributes(
1006 GTK_TREE_VIEW(appdata->gpxview),
1007 -1, "#Caches", renderer,
1008 "text", GPXLIST_COL_CACHES,
1009 "sensitive", GPXLIST_COL_OPEN,
1010 NULL);
1011 }
1012
1013 #ifdef USE_PANNABLE_AREA
1014 /* --- "Delete" column --- */
1015 renderer = gtk_cell_renderer_pixbuf_new();
1016 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(appdata->gpxview),
1017 -1, "Del", renderer,
1018 "pixbuf", GPXLIST_COL_DELETE,
1019 // "sensitive", GPXLIST_COL_OPEN,
1020 NULL);
1021 #endif
1022
1023 /* build and fill the store */
1024 appdata->gpxstore = gtk_list_store_new(GPXLIST_NUM_COLS, GDK_TYPE_PIXBUF,
1025 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
1026 G_TYPE_STRING, G_TYPE_BOOLEAN,
1027 #ifdef USE_PANNABLE_AREA
1028 GDK_TYPE_PIXBUF,
1029 #endif
1030 G_TYPE_POINTER);
1031
1032 GtkTreePath *path = NULL;
1033 GtkTreeIter sel_iter;
1034 gboolean sel_iter_valid = FALSE;
1035 while(gpx) {
1036 GtkTreeIter iter;
1037 gtk_list_store_append(appdata->gpxstore, &iter);
1038 gpxlist_set(appdata->gpxstore, &iter, gpx);
1039
1040 if(gpx == sel_gpx) {
1041 sel_iter = iter;
1042 sel_iter_valid = TRUE;
1043 }
1044
1045 gpx = gpx->next;
1046 }
1047
1048 gtk_tree_view_set_model(GTK_TREE_VIEW(appdata->gpxview),
1049 GTK_TREE_MODEL(appdata->gpxstore));
1050
1051 g_object_unref(appdata->gpxstore);
1052
1053 if(sel_iter_valid) {
1054 gtk_tree_selection_select_iter(selection, &sel_iter);
1055 path = gtk_tree_model_get_path(GTK_TREE_MODEL(appdata->gpxstore),
1056 &sel_iter);
1057 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW(appdata->gpxview),
1058 path, NULL, TRUE, 0.0, 0.0);
1059 gtk_tree_path_free(path);
1060 }
1061
1062 /* make list react on clicks */
1063 g_signal_connect(appdata->gpxview, "row-activated",
1064 (GCallback)gpxlist_view_onRowActivated, appdata);
1065
1066 /* put this inside a scrolled view */
1067 #ifndef USE_PANNABLE_AREA
1068 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1069 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1070 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1071 gtk_container_add(GTK_CONTAINER(scrolled_window), appdata->gpxview);
1072
1073 return scrolled_window;
1074 #else
1075 GtkWidget *pannable_area = hildon_pannable_area_new();
1076 gtk_container_add(GTK_CONTAINER(pannable_area), appdata->gpxview);
1077
1078 return pannable_area;
1079 #endif
1080 }
1081
1082 /* add last entry in gpx list to visual representation */
1083 static void gpxlist_add(appdata_t *appdata, gpx_t *new) {
1084 GtkTreeIter iter;
1085
1086 gtk_list_store_append(appdata->gpxstore, &iter);
1087 gpxlist_set(appdata->gpxstore, &iter, new);
1088
1089 /* and attach entry to end of list */
1090 gpx_t **gpx = &appdata->gpx;
1091 while(*gpx) gpx = &((*gpx)->next);
1092 *gpx = new;
1093 }
1094
1095 /******************** end of gpxlist ********************/
1096
1097 /******************** begin of menu *********************/
1098
1099 typedef struct {
1100 appdata_t *appdata;
1101 GtkWidget *dialog;
1102 } about_context_t;
1103
1104 #ifdef ENABLE_BROWSER_INTERFACE
1105 void on_paypal_button_clicked(GtkButton *button, about_context_t *context) {
1106 gtk_dialog_response(GTK_DIALOG(context->dialog), GTK_RESPONSE_ACCEPT);
1107 browser_url(context->appdata,
1108 "https://www.paypal.com/cgi-bin/webscr"
1109 "?cmd=_s-xclick&hosted_button_id=7400558");
1110 }
1111 #endif
1112
1113 static void
1114 cb_menu_about(GtkWidget *window, gpointer data) {
1115 about_context_t context;
1116
1117 context.appdata = (appdata_t *)data;
1118
1119 #ifdef ENABLE_LIBLOCATION
1120 char *uses = "uses liblocation";
1121 #elif defined(ENABLE_GPSBT)
1122 char *uses = "uses gpsbt and gpsd";
1123 #else
1124 char *uses = "uses gpsd";
1125 #endif
1126
1127 const gchar *authors[] = {
1128 "Till Harbaum <till@harbaum.org>",
1129 "John Stowers <john.stowers@gmail.com>",
1130 "GCVote: Guido Wegener <guido.wegener@gmx.de>",
1131 NULL };
1132
1133 context.dialog = g_object_new(GTK_TYPE_ABOUT_DIALOG,
1134 "name", "GPXView",
1135 "version", VERSION,
1136 "copyright", _("Copyright 2008-2009"),
1137 "authors", authors,
1138 "website", _("http://www.harbaum.org/till/maemo"),
1139 "comments", _(uses),
1140 NULL);
1141
1142 #ifdef ENABLE_BROWSER_INTERFACE
1143 /* add a way to donate to the project */
1144 GtkWidget *alignment = gtk_alignment_new(0.5, 0, 0, 0);
1145
1146 GtkWidget *hbox = gtk_hbox_new(FALSE, 8);
1147 gtk_box_pack_start(GTK_BOX(hbox),
1148 gtk_label_new(_("Do you like GPXView?")),
1149 FALSE, FALSE, 0);
1150
1151 GtkWidget *button = gtk_button_new();
1152 gtk_button_set_image(GTK_BUTTON(button),
1153 icon_get_widget(ICON_MISC, 8));
1154 gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
1155 g_signal_connect(button, "clicked",
1156 G_CALLBACK(on_paypal_button_clicked), &context);
1157 gtk_box_pack_start(GTK_BOX(hbox), button, FALSE, FALSE, 0);
1158
1159 gtk_container_add(GTK_CONTAINER(alignment), hbox);
1160 gtk_box_pack_start_defaults(GTK_BOX((GTK_DIALOG(context.dialog))->vbox),
1161 alignment);
1162
1163 gtk_widget_show_all(alignment);
1164 #endif
1165
1166 gtk_dialog_run(GTK_DIALOG(context.dialog));
1167 gtk_widget_destroy(context.dialog);
1168 }
1169
1170 #if defined(USE_MAEMO) && defined(HILDON_HELP)
1171 static void
1172 cb_menu_help(GtkWidget *window, gpointer data) {
1173 appdata_t *appdata = (appdata_t*)data;
1174
1175 hildon_help_show(appdata->osso_context, HELP_ID_INTRO, 0);
1176 }
1177 #endif
1178
1179 static void
1180 cb_menu_add(GtkWidget *window, gpointer data) {
1181 appdata_t *appdata = (appdata_t *)data;
1182
1183 gpx_t *new = choose_file(appdata, FALSE);
1184 if(new) gpxlist_add(appdata, new);
1185 }
1186
1187 static void
1188 cb_menu_adddir(GtkWidget *window, gpointer data) {
1189 appdata_t *appdata = (appdata_t *)data;
1190
1191 gpx_t *new = choose_file(appdata, TRUE);
1192 if(new) gpxlist_add(appdata, new);
1193 }
1194
1195 #ifndef USE_PANNABLE_AREA
1196 static void
1197 cb_menu_close(GtkWidget *window, gpointer data) {
1198 appdata_t *appdata = (appdata_t *)data;
1199 GtkTreeSelection *selection;
1200 GtkTreeModel *model;
1201 GtkTreeIter iter;
1202
1203 printf("selected close\n");
1204
1205 /* the entry cannot be closed again */
1206 gtk_widget_set_sensitive(appdata->menu_close, FALSE);
1207
1208 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(appdata->gpxview));
1209
1210 printf("gpxlist close\n");
1211
1212 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
1213 gpx_t *gpx = NULL;
1214 gtk_tree_model_get(model, &iter, GPXLIST_COL_DATA, &gpx, -1);
1215
1216 if(gpx) gpxlist_close(appdata, GTK_LIST_STORE(model), &iter, gpx);
1217 } else {
1218 g_print ("no row selected.\n");
1219 }
1220 }
1221
1222 static void
1223 cb_menu_remove(GtkWidget *window, gpointer data) {
1224 appdata_t *appdata = (appdata_t *)data;
1225
1226 /* disable menu item */
1227 gtk_widget_set_sensitive(appdata->menu_remove, FALSE);
1228 gtk_widget_set_sensitive(appdata->menu_close, FALSE);
1229
1230 GtkTreeModel *model;
1231 GtkTreeIter iter;
1232 GtkTreeSelection *selection =
1233 gtk_tree_view_get_selection(GTK_TREE_VIEW(appdata->gpxview));
1234
1235 printf("gpxlist remove\n");
1236
1237 if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
1238 gpx_t *gpx = NULL;
1239 gtk_tree_model_get(model, &iter, GPXLIST_COL_DATA, &gpx, -1);
1240
1241 if(gpx) gpxlist_remove(appdata, GTK_LIST_STORE(model), &iter, gpx);
1242 } else {
1243 g_print ("no row selected.\n");
1244 }
1245 }
1246
1247 #endif // !USE_PANNABLE_AREA
1248
1249 static void search_result_free(gpx_t *result) {
1250 printf("freeing search results\n");
1251
1252 /* free found chain */
1253 cache_t *cache = result->cache;
1254 while(cache) {
1255 cache_t *next = cache->next;
1256 free(cache);
1257 cache = next;
1258 }
1259 free(result->name);
1260 free(result);
1261 }
1262
1263 #define MAX_HITS 50
1264
1265 static time_t localize_time(time_t in) {
1266 time_t ret;
1267 char *tz;
1268 struct tm *tm = localtime(&in);
1269
1270 tz = getenv("TZ");
1271 setenv("TZ", "", 1);
1272 tzset();
1273 ret = mktime(tm);
1274 if (tz)
1275 setenv("TZ", tz, 1);
1276 else
1277 unsetenv("TZ");
1278 tzset();
1279 return ret;
1280 }
1281
1282 static int days_ago(time_t in) {
1283 int day_in = localize_time(in) / (60*60*24);
1284 int day_now = localize_time(time(NULL)) / (60*60*24);
1285
1286 return day_now - day_in;
1287 }
1288
1289 gpx_t *search_do(appdata_t *appdata, gpx_t *gpx, char *phrase,
1290 int what, gboolean local) {
1291 /* walk through all caches */
1292
1293 int hits = 0;
1294 gpx_t *found = malloc(sizeof(gpx_t));
1295 memset(found, 0, sizeof(gpx_t));
1296 cache_t **cacheP = &(found->cache);
1297
1298 if(what & SEARCH_FINDS) {
1299 time_t loc_now = localize_time(time(NULL));
1300 printf("now: %ld days since 1/1/1970, days hour is %ld\n",
1301 loc_now/(60*60*24), loc_now%(60*60*24)/(60*60));
1302 }
1303
1304 while(gpx && hits < MAX_HITS) {
1305
1306 /* we need all notes ... */
1307 if(what & SEARCH_FINDS) {
1308 notes_load_all(appdata, gpx);
1309 gpx->notes_loaded = TRUE;
1310 }
1311
1312 cache_t *cache = gpx->cache;
1313
1314 while(cache && hits < MAX_HITS) {
1315 gboolean hit = FALSE;
1316
1317 if(what & SEARCH_FINDS) {
1318 if(cache->notes && cache->notes->found ) {
1319 int days = days_ago(cache->notes->ftime);
1320
1321 if(cache->id)
1322 printf("find of %s is %d days ago\n", cache->id, days);
1323
1324 if(days <= appdata->search_days)
1325 hit = 1;
1326 }
1327 } else if(cache->id && (what & SEARCH_ID) &&
1328 strcasestr(cache->id, phrase))
1329 hit = 1;
1330 else if(cache->name && (what & SEARCH_NAME) &&
1331 strcasestr(cache->name, phrase))
1332 hit = 1;
1333 else if(cache->short_description && (what & SEARCH_DESC) &&
1334 strcasestr(cache->short_description, phrase))
1335 hit = 1;
1336 else if(cache->long_description && (what & SEARCH_DESC) &&
1337 strcasestr(cache->long_description, phrase))
1338 hit = 1;
1339 else if(cache->owner && cache->owner->name && (what & SEARCH_OWNER) &&
1340 strcasestr(cache->owner->name, phrase))
1341 hit = 1;
1342
1343 if(hit) {
1344 /* chain a copy of this cache structure into the found list */
1345 *cacheP = malloc(sizeof(cache_t));
1346 memcpy(*cacheP, cache, sizeof(cache_t));
1347 (*cacheP)->next = NULL;
1348 cacheP = &((*cacheP)->next);
1349 hits++;
1350 }
1351 cache = cache->next;
1352 }
1353
1354 if(!local) gpx = gpx->next;
1355 else gpx = NULL; /* local search within one gpx only */
1356 }
1357
1358 found->name = strdup(_("Search results"));
1359
1360 return found;
1361 }
1362
1363 typedef struct {
1364 appdata_t *appdata;
1365 GtkWidget *entry, *spinner;
1366 GtkWidget *in_id, *in_name, *in_desc, *in_owner, *in_finds;
1367 } search_context_t;
1368
1369 static void callback_finds_toggled(GtkWidget *widget, gpointer data ) {
1370 search_context_t *context = (search_context_t*)data;
1371
1372 gboolean in_finds = gtk_toggle_button_get_active(
1373 GTK_TOGGLE_BUTTON(context->in_finds));
1374
1375 gtk_widget_set_sensitive(context->entry, !in_finds);
1376 gtk_widget_set_sensitive(context->in_id, !in_finds);
1377 gtk_widget_set_sensitive(context->in_name, !in_finds);
1378 gtk_widget_set_sensitive(context->in_desc, !in_finds);
1379 gtk_widget_set_sensitive(context->in_owner, !in_finds);
1380 gtk_widget_set_sensitive(context->spinner, in_finds);
1381 }
1382
1383 static void
1384 cb_menu_search(GtkWidget *window, gpointer data) {
1385 appdata_t *appdata = (appdata_t *)data;
1386
1387 search_context_t context;
1388 memset(&context, 0, sizeof(search_context_t));
1389 context.appdata = appdata;
1390
1391 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("Enter search phrase"),
1392 GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
1393 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1394 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1395 NULL);
1396
1397 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1398 gtk_label_new(_("Search in:")));
1399
1400 GtkWidget *table = gtk_table_new(2, 2, TRUE);
1401 gtk_table_set_col_spacing(GTK_TABLE(table), 0, 8);
1402
1403 context.in_id = gtk_check_button_new_with_label(_("Waypoint IDs"));
1404 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(context.in_id),
1405 appdata->search & SEARCH_ID);
1406 gtk_table_attach_defaults(GTK_TABLE(table), context.in_id, 0, 1, 0, 1);
1407
1408 context.in_name = gtk_check_button_new_with_label(_("Names"));
1409 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(context.in_name),
1410 appdata->search & SEARCH_NAME);
1411 gtk_table_attach_defaults(GTK_TABLE(table), context.in_name, 1, 2, 0, 1);
1412
1413 context.in_desc = gtk_check_button_new_with_label(_("Descriptions"));
1414 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(context.in_desc),
1415 appdata->search & SEARCH_DESC);
1416 gtk_table_attach_defaults(GTK_TABLE(table), context.in_desc, 0, 1, 1, 2);
1417
1418 context.in_owner = gtk_check_button_new_with_label(_("Owner"));
1419 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(context.in_owner),
1420 appdata->search & SEARCH_OWNER);
1421 gtk_table_attach_defaults(GTK_TABLE(table), context.in_owner, 1, 2, 1, 2);
1422
1423 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table);
1424
1425 /* -------------------------------------------------------------- */
1426
1427 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1428 gtk_label_new(_("Search for:")));
1429 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1430 context.entry = gtk_entry_new());
1431 if(appdata->search_str)
1432 gtk_entry_set_text(GTK_ENTRY(context.entry), appdata->search_str);
1433
1434 /* -------------------------------------------------------------- */
1435
1436 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1437 gtk_hseparator_new());
1438
1439 GtkWidget *hbox = gtk_hbox_new(FALSE, 5);
1440
1441 context.in_finds = gtk_check_button_new_with_label(_("Search finds for"));
1442 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(context.in_finds),
1443 appdata->search & SEARCH_FINDS);
1444 gtk_box_pack_start_defaults(GTK_BOX(hbox), context.in_finds);
1445 g_signal_connect(G_OBJECT(context.in_finds), "toggled",
1446 G_CALLBACK(callback_finds_toggled), &context);
1447
1448 #ifndef USE_MAEMO
1449 GtkObject *adj = gtk_adjustment_new(appdata->search_days, 0, 99, 1, 10, 10);
1450 context.spinner = gtk_spin_button_new(GTK_ADJUSTMENT(adj), 1, 0);
1451 #else
1452 context.spinner = hildon_number_editor_new(0, 99);
1453 hildon_number_editor_set_value(HILDON_NUMBER_EDITOR(context.spinner),
1454 appdata->search_days);
1455 #endif
1456 gtk_box_pack_start_defaults(GTK_BOX(hbox), context.spinner);
1457
1458 gtk_box_pack_start_defaults(GTK_BOX(hbox), gtk_label_new(_("days")));
1459
1460 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
1461
1462 /* -------------------------------------------------------------- */
1463
1464 gtk_widget_show_all(dialog);
1465 callback_finds_toggled(NULL, &context);
1466
1467 if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) {
1468 char *p = strdup(gtk_entry_get_text(GTK_ENTRY(context.entry)));
1469
1470 /* update saved search string */
1471 if(appdata->search_str) free(appdata->search_str);
1472 if(strlen(p) > 0)
1473 appdata->search_str = strdup(p);
1474
1475 #ifndef USE_MAEMO
1476 appdata->search_days = gtk_spin_button_get_value_as_int(
1477 GTK_SPIN_BUTTON(context.spinner));
1478 #else
1479 appdata->search_days = hildon_number_editor_get_value(
1480 HILDON_NUMBER_EDITOR(context.spinner));
1481 #endif
1482
1483 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(context.in_finds)))
1484 appdata->search |= SEARCH_FINDS;
1485 else
1486 appdata->search &= ~SEARCH_FINDS;
1487
1488 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(context.in_id)))
1489 appdata->search |= SEARCH_ID;
1490 else
1491 appdata->search &= ~SEARCH_ID;
1492
1493 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(context.in_name)))
1494 appdata->search |= SEARCH_NAME;
1495 else
1496 appdata->search &= ~SEARCH_NAME;
1497
1498 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(context.in_desc)))
1499 appdata->search |= SEARCH_DESC;
1500 else
1501 appdata->search &= ~SEARCH_DESC;
1502
1503 if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(context.in_owner)))
1504 appdata->search |= SEARCH_OWNER;
1505 else
1506 appdata->search &= ~SEARCH_OWNER;
1507
1508 gtk_widget_destroy(dialog);
1509
1510 /* don't search if we are asked to search for nothing */
1511 if(((appdata->search & (SEARCH_ID|SEARCH_NAME|SEARCH_DESC|SEARCH_OWNER)) &&
1512 strlen(p) > 0) || (appdata->search & SEARCH_FINDS)) {
1513
1514 printf("Search for %s (flags = %x)...\n", p, appdata->search);
1515
1516 #if !defined(USE_BREAD_CRUMB_TRAIL) && !defined(BCT)
1517 gpx_t *found =
1518 search_do(appdata, appdata->gpx, p, appdata->search, FALSE);
1519
1520 /* do search result dialog here ... */
1521 cachelist_dialog(appdata, found);
1522 #ifndef USE_STACKABLE_WINDOW
1523 search_result_free(found);
1524 #else
1525 appdata->search_results = found;
1526 #endif
1527 #else
1528 gpx_t *found = NULL;
1529
1530 if(appdata->cur_gpx)
1531 found = search_do(appdata, appdata->cur_gpx, p, appdata->search, TRUE);
1532 else
1533 found = search_do(appdata, appdata->gpx, p, appdata->search, FALSE);
1534
1535 gtk_container_remove(GTK_CONTAINER(appdata->vbox), appdata->cur_view);
1536 appdata->cur_view = cachelist_create(appdata, found, NULL);
1537 gtk_box_pack_start_defaults(GTK_BOX(appdata->vbox), appdata->cur_view);
1538 gtk_widget_show_all(appdata->vbox);
1539 crumb_add(appdata, found->name,
1540 appdata->cur_gpx?CRUMB_SEARCH_GPX:CRUMB_SEARCH_GLOBAL, found);
1541 #endif
1542 } else
1543 printf("No valid search: \"%s\" with flags %x!\n", p, appdata->search);
1544
1545 free(p);
1546 } else
1547 gtk_widget_destroy(dialog);
1548 }
1549
1550 static void on_window_destroy (GtkWidget *widget, gpointer data);
1551
1552 #ifndef USE_MAEMO
1553 static void
1554 cb_menu_quit(GtkWidget *window, gpointer data) {
1555 on_window_destroy(window, data);
1556 }
1557 #endif
1558
1559 #ifndef NO_COPY_N_PASTE
1560 static void
1561 cb_menu_cut(GtkWidget *widget, gpointer data) {
1562 appdata_t *appdata = (appdata_t*)data;
1563
1564 if(appdata->active_buffer) {
1565 if(GTK_WIDGET_TYPE(appdata->active_buffer) == GTK_TYPE_TEXT_BUFFER) {
1566 gtk_text_buffer_cut_clipboard(GTK_TEXT_BUFFER(appdata->active_buffer),
1567 appdata->clipboard, TRUE);
1568 } else
1569 printf("cut: ERROR, not a text buffer\n");
1570 } else
1571 printf("cut: ERROR, no active buffer\n");
1572 }
1573
1574 static void
1575 cb_menu_copy(GtkWidget *widget, gpointer data) {
1576 appdata_t *appdata = (appdata_t*)data;
1577
1578 if(appdata->active_buffer) {
1579 if(GTK_WIDGET_TYPE(appdata->active_buffer) == GTK_TYPE_TEXT_BUFFER) {
1580 gtk_text_buffer_copy_clipboard(GTK_TEXT_BUFFER(appdata->active_buffer),
1581 appdata->clipboard);
1582 } else if(GTK_WIDGET_TYPE(appdata->active_buffer) == gtk_html_get_type()) {
1583 printf("copy from html buffer\n");
1584 html_copy_to_clipboard(appdata);
1585 } else
1586 printf("copy: ERROR, not a text nor a html buffer\n");
1587 } else
1588 printf("copy: ERROR, no active buffer\n");
1589 }
1590
1591 static void
1592 cb_menu_paste(GtkWidget *widget, gpointer data) {
1593 appdata_t *appdata = (appdata_t*)data;
1594
1595 if(appdata->active_buffer) {
1596 if(GTK_WIDGET_TYPE(appdata->active_buffer) == GTK_TYPE_TEXT_BUFFER) {
1597 gtk_text_buffer_paste_clipboard(GTK_TEXT_BUFFER(appdata->active_buffer),
1598 appdata->clipboard, NULL, TRUE);
1599 } else
1600 printf("paste: ERROR, not a text buffer\n");
1601 } else
1602 printf("paste: ERROR, no active buffer\n");
1603 }
1604 #endif
1605
1606 static void
1607 cb_menu_export_log(GtkWidget *widget, gpointer data) {
1608 appdata_t *appdata = (appdata_t*)data;
1609 notes_log_export(appdata);
1610 }
1611
1612 #ifdef ENABLE_MAEMO_MAPPER
1613 static void
1614 cb_menu_export_mmpoi(GtkWidget *widget, gpointer data) {
1615 appdata_t *appdata = (appdata_t*)data;
1616 mmpoi_export(appdata);
1617 }
1618 #endif
1619
1620 static void
1621 cb_menu_export_garmin(GtkWidget *widget, gpointer data) {
1622 appdata_t *appdata = (appdata_t*)data;
1623 garmin_export(appdata);
1624 }
1625
1626 #ifdef ENABLE_OSM_GPS_MAP
1627 static void
1628 cb_menu_map(GtkWidget *window, gpointer data) {
1629 map((appdata_t *)data);
1630 }
1631 #endif
1632
1633 static void
1634 cb_menu_geomath(GtkWidget *window, gpointer data) {
1635 geomath_dialog((appdata_t *)data);
1636 }
1637
1638 static void
1639 cb_menu_geotext(GtkWidget *window, gpointer data) {
1640 geotext_dialog((appdata_t *)data);
1641 }
1642
1643 static void
1644 cb_menu_precpos(GtkWidget *window, gpointer data) {
1645 precise_position((appdata_t *)data);
1646 }
1647
1648 #ifdef USE_STACKABLE_WINDOW
1649 typedef struct {
1650 char *label, *desc;
1651 GtkSignalFunc activate_cb;
1652 } menu_entry_t;
1653
1654 typedef struct {
1655 const char *title;
1656 const menu_entry_t *menu;
1657 int len;
1658 } submenu_t;
1659
1660 #define COLUMNS 1
1661
1662 void on_submenu_entry_clicked(GtkButton *button, GtkWidget *menu) {
1663
1664 /* force closing of submenu dialog */
1665 gtk_dialog_response(GTK_DIALOG(menu), GTK_RESPONSE_NONE);
1666 gtk_widget_hide(menu);
1667
1668 /* let gtk clean up */
1669 while(gtk_events_pending())
1670 gtk_main_iteration();
1671 }
1672
1673 static GtkWidget *app_submenu_create(appdata_t *appdata,
1674 const submenu_t *submenu) {
1675
1676 /* create a oridinary dialog box */
1677 GtkWidget *dialog = gtk_dialog_new();
1678 gtk_window_set_title(GTK_WINDOW(dialog), _(submenu->title));
1679 gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
1680 gtk_window_set_transient_for(GTK_WINDOW(dialog),
1681 GTK_WINDOW(appdata->window));
1682 gtk_dialog_set_has_separator(GTK_DIALOG(dialog), FALSE);
1683
1684 GtkWidget *table = gtk_table_new(submenu->len/COLUMNS, COLUMNS, TRUE);
1685 int x = 0, y = 0;
1686
1687 const menu_entry_t *menu_entries = submenu->menu;
1688 while(menu_entries->label) {
1689 GtkWidget *button = NULL;
1690
1691 button = hildon_button_new_with_text(
1692 HILDON_SIZE_FINGER_HEIGHT | HILDON_SIZE_AUTO_WIDTH,
1693 HILDON_BUTTON_ARRANGEMENT_VERTICAL,
1694 _(menu_entries->label), _(menu_entries->desc));
1695
1696 /* try to center both texts */
1697 hildon_button_set_title_alignment(HILDON_BUTTON(button), 0.5, 0.5);
1698 hildon_button_set_value_alignment(HILDON_BUTTON(button), 0.5, 0.5);
1699
1700 g_signal_connect(button, "clicked",
1701 G_CALLBACK(on_submenu_entry_clicked), dialog);
1702
1703 g_signal_connect(button, "clicked",
1704 menu_entries->activate_cb, appdata);
1705
1706 gtk_table_attach_defaults(GTK_TABLE(table), button, x, x+1, y, y+1);
1707
1708 x++;
1709 if(x == COLUMNS) { x = 0; y++; }
1710
1711 menu_entries++;
1712 }
1713
1714 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table);
1715
1716 return dialog;
1717 }
1718
1719 /* popup the dialog shaped submenu */
1720 static void submenu_popup(GtkWidget *menu) {
1721 gtk_widget_show_all(menu);
1722 gtk_dialog_run(GTK_DIALOG(menu));
1723 gtk_widget_hide(menu);
1724 }
1725
1726 static void submenu_cleanup(GtkWidget *menu) {
1727 gtk_widget_destroy(menu);
1728 }
1729
1730 static const menu_entry_t submenu_export_entries[] = {
1731 #ifdef ENABLE_MAEMO_MAPPER
1732 { "Export to Maemo Mapper" , "Save a Maemo Mapper POI file",
1733 G_CALLBACK(cb_menu_export_mmpoi) },
1734 #endif
1735 { "Export Field Notes", "Save a Garmin Field Notes file",
1736 G_CALLBACK(cb_menu_export_log) },
1737 { "Export Garmin GPX", "Save modified waypoints in GPX file",
1738 G_CALLBACK(cb_menu_export_garmin) },
1739 { NULL, NULL, NULL }
1740 };
1741
1742 static const submenu_t submenu_export = {
1743 "Export", submenu_export_entries,
1744 sizeof(submenu_export_entries)/sizeof(menu_entry_t)-1
1745 };
1746
1747 /* the export submenu */
1748 void on_export_clicked(GtkButton *button, appdata_t *appdata) {
1749 if(!appdata->export_menu)
1750 appdata->export_menu = app_submenu_create(appdata, &submenu_export);
1751
1752 submenu_popup(appdata->export_menu);
1753 }
1754
1755 static const menu_entry_t submenu_tools_entries[] = {
1756 { "Geomath", "Geocoordinate calculation",
1757 G_CALLBACK(cb_menu_geomath) },
1758 { "Geotext", "Text analysis",
1759 G_CALLBACK(cb_menu_geotext) },
1760 { "Precise Position", "Calculate a precise GPS position",
1761 G_CALLBACK(cb_menu_precpos) },
1762 { NULL, NULL, NULL }
1763 };
1764
1765 static const submenu_t submenu_tools = {
1766 "Tools", submenu_tools_entries,
1767 sizeof(submenu_tools_entries)/sizeof(menu_entry_t)-1
1768 };
1769
1770 /* the tools submenu */
1771 void on_tools_clicked(GtkButton *button, appdata_t *appdata) {
1772 if(!appdata->tools_menu)
1773 appdata->tools_menu = app_submenu_create(appdata, &submenu_tools);
1774
1775 submenu_popup(appdata->tools_menu);
1776 }
1777
1778 HildonAppMenu *menu_create(appdata_t *appdata, int mode) {
1779 GtkWidget *button;
1780 HildonAppMenu *menu = HILDON_APP_MENU(hildon_app_menu_new());
1781
1782 /* ------- */
1783 button = gtk_button_new_with_label(_("About"));
1784 g_signal_connect_after(button, "clicked",
1785 G_CALLBACK(cb_menu_about), appdata);
1786 hildon_app_menu_append(menu, GTK_BUTTON(button));
1787
1788 button = gtk_button_new_with_label(_("Settings"));
1789 g_signal_connect_after(button, "clicked", G_CALLBACK(cb_menu_settings),
1790 appdata);
1791 hildon_app_menu_append(menu, GTK_BUTTON(button));
1792
1793 if(mode == MENU_GPXLIST) {
1794 button = gtk_button_new_with_label(_("Import file"));
1795 g_signal_connect_after(button, "clicked",
1796 G_CALLBACK(cb_menu_add), appdata);
1797 hildon_app_menu_append(menu, GTK_BUTTON(button));
1798
1799 button = gtk_button_new_with_label(_("Import directory"));
1800 g_signal_connect_after(button, "clicked",
1801 G_CALLBACK(cb_menu_adddir), appdata);
1802 hildon_app_menu_append(menu, GTK_BUTTON(button));
1803
1804 button = gtk_button_new_with_label(_("Export"));
1805 g_signal_connect_after(button, "clicked",
1806 G_CALLBACK(on_export_clicked), appdata);
1807 hildon_app_menu_append(menu, GTK_BUTTON(button));
1808
1809 button = gtk_button_new_with_label(_("Search"));
1810 g_signal_connect_after(button, "clicked",
1811 G_CALLBACK(cb_menu_search), appdata);
1812 hildon_app_menu_append(menu, GTK_BUTTON(button));
1813 }
1814
1815 button = gtk_button_new_with_label(_("Tools"));
1816 g_signal_connect_after(button, "clicked",
1817 G_CALLBACK(on_tools_clicked), appdata);
1818 hildon_app_menu_append(menu, GTK_BUTTON(button));
1819
1820 #ifdef ENABLE_OSM_GPS_MAP
1821 button = gtk_button_new_with_label(_("Map"));
1822 g_signal_connect_after(button, "clicked",
1823 G_CALLBACK(cb_menu_map), appdata);
1824 hildon_app_menu_append(menu, GTK_BUTTON(button));
1825 #endif
1826
1827 #ifdef HILDON_HELP
1828 button = gtk_button_new_with_label(_("Help"));
1829 g_signal_connect_after(button, "clicked",
1830 G_CALLBACK(cb_menu_help), appdata);
1831 hildon_app_menu_append(menu, GTK_BUTTON(button));
1832 #endif
1833
1834 gtk_widget_show_all(GTK_WIDGET(menu));
1835
1836 return menu;
1837 }
1838 #else
1839
1840 void menu_create(appdata_t *appdata) {
1841 GtkWidget *menu, *item;
1842 menu = gtk_menu_new();
1843
1844 #if defined(USE_BREAD_CRUMB_TRAIL) || defined(BCT)
1845 appdata->menu_import =
1846 #endif
1847 item = gtk_menu_item_new_with_label(_("Import"));
1848 gtk_menu_append(GTK_MENU_SHELL(menu), item);
1849 GtkWidget *submenu = gtk_menu_new();
1850 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
1851
1852 item = gtk_menu_item_new_with_label( _("File") );
1853 gtk_menu_append(GTK_MENU_SHELL(submenu), item);
1854 g_signal_connect(item, "activate", GTK_SIGNAL_FUNC(cb_menu_add), appdata);
1855
1856 item = gtk_menu_item_new_with_label( _("Directory") );
1857 gtk_menu_append(GTK_MENU_SHELL(submenu), item);
1858 g_signal_connect(item, "activate", GTK_SIGNAL_FUNC(cb_menu_adddir), appdata);
1859
1860 #ifndef USE_PANNABLE_AREA
1861 gtk_menu_append(GTK_MENU_SHELL(submenu), gtk_separator_menu_item_new());
1862
1863 appdata->menu_close =
1864 item = gtk_menu_item_new_with_label( _("Close") );
1865 gtk_menu_append(GTK_MENU_SHELL(submenu), item);
1866 g_signal_connect(item, "activate", GTK_SIGNAL_FUNC(cb_menu_close), appdata);
1867
1868 appdata->menu_remove =
1869 item = gtk_menu_item_new_with_label( _("Remove") );
1870 gtk_menu_append(GTK_MENU_SHELL(submenu), item);
1871 g_signal_connect(item, "activate", GTK_SIGNAL_FUNC(cb_menu_remove), appdata);
1872 #endif
1873
1874 #if defined(USE_BREAD_CRUMB_TRAIL) || defined(BCT)
1875 appdata->menu_export =
1876 #endif
1877 item = gtk_menu_item_new_with_label(_("Export"));
1878 gtk_menu_append(GTK_MENU_SHELL(menu), item);
1879 submenu = gtk_menu_new();
1880 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
1881
1882 #ifdef ENABLE_MAEMO_MAPPER
1883 item = gtk_menu_item_new_with_label( _("Maemo Mapper POI") );
1884 gtk_menu_append(GTK_MENU_SHELL(submenu), item);
1885 g_signal_connect(item, "activate",
1886 GTK_SIGNAL_FUNC(cb_menu_export_mmpoi), appdata);
1887 #endif
1888
1889 item = gtk_menu_item_new_with_label( _("Garmin Field Notes") );
1890 gtk_menu_append(GTK_MENU_SHELL(submenu), item);
1891 g_signal_connect(item, "activate",
1892 GTK_SIGNAL_FUNC(cb_menu_export_log), appdata);
1893
1894 item = gtk_menu_item_new_with_label( _("Garmin GPX") );
1895 gtk_menu_append(GTK_MENU_SHELL(submenu), item);
1896 g_signal_connect(item, "activate",
1897 GTK_SIGNAL_FUNC(cb_menu_export_garmin), appdata);
1898
1899 #if defined(USE_BREAD_CRUMB_TRAIL) || defined(BCT)
1900 appdata->menu_search =
1901 #endif
1902 item = gtk_menu_item_new_with_label( _("Search") );
1903 gtk_menu_append(GTK_MENU_SHELL(menu), item);
1904 g_signal_connect(item, "activate", GTK_SIGNAL_FUNC(cb_menu_search), appdata);
1905
1906 gtk_menu_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
1907
1908 /* ----------- copy'n paste submenu ----------------- */
1909 #ifndef NO_COPY_N_PASTE
1910 item = gtk_menu_item_new_with_label(_("Edit"));
1911 gtk_menu_append(GTK_MENU_SHELL(menu), item);
1912 submenu = gtk_menu_new();
1913 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
1914
1915 appdata->menu_cut = item = gtk_menu_item_new_with_label( _("Cut") );
1916 gtk_widget_set_sensitive(item, FALSE);
1917 gtk_menu_append(GTK_MENU_SHELL(submenu), item);
1918 g_signal_connect(item, "activate", GTK_SIGNAL_FUNC(cb_menu_cut), appdata);
1919 appdata->menu_copy = item = gtk_menu_item_new_with_label( _("Copy") );
1920 gtk_widget_set_sensitive(item, FALSE);
1921 gtk_menu_append(GTK_MENU_SHELL(submenu), item);
1922 g_signal_connect(item, "activate", GTK_SIGNAL_FUNC(cb_menu_copy), appdata);
1923 appdata->menu_paste = item = gtk_menu_item_new_with_label( _("Paste") );
1924 gtk_widget_set_sensitive(item, FALSE);
1925 gtk_menu_append(GTK_MENU_SHELL(submenu), item);
1926 g_signal_connect(item, "activate", GTK_SIGNAL_FUNC(cb_menu_paste), appdata);
1927 #endif
1928
1929 item = gtk_menu_item_new_with_label( _("Settings") );
1930 gtk_menu_append(GTK_MENU_SHELL(menu), item);
1931 g_signal_connect(item, "activate", GTK_SIGNAL_FUNC(cb_menu_settings),
1932 appdata);
1933
1934 gtk_menu_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
1935
1936 #ifdef ENABLE_OSM_GPS_MAP
1937 item = gtk_menu_item_new_with_label( _("Map") );
1938 gtk_menu_append(GTK_MENU_SHELL(menu), item);
1939 g_signal_connect(item, "activate",
1940 GTK_SIGNAL_FUNC(cb_menu_map), appdata);
1941 #endif
1942
1943 item = gtk_menu_item_new_with_label(_("Tools"));
1944 gtk_menu_append(GTK_MENU_SHELL(menu), item);
1945 submenu = gtk_menu_new();
1946 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu);
1947
1948 item = gtk_menu_item_new_with_label( _("Geomath") );
1949 gtk_menu_append(GTK_MENU_SHELL(submenu), item);
1950 g_signal_connect(item, "activate",
1951 GTK_SIGNAL_FUNC(cb_menu_geomath), appdata);
1952
1953 item = gtk_menu_item_new_with_label( _("Geotext") );
1954 gtk_menu_append(GTK_MENU_SHELL(submenu), item);
1955 g_signal_connect(item, "activate",
1956 GTK_SIGNAL_FUNC(cb_menu_geotext), appdata);
1957
1958 item = gtk_menu_item_new_with_label( _("Precise Position") );
1959 gtk_menu_append(GTK_MENU_SHELL(submenu), item);
1960 g_signal_connect(item, "activate",
1961 GTK_SIGNAL_FUNC(cb_menu_precpos), appdata);
1962
1963 gtk_menu_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
1964
1965 #if defined(USE_MAEMO) && defined(HILDON_HELP)
1966 item = gtk_menu_item_new_with_label( _("Help") );
1967 gtk_menu_append(GTK_MENU_SHELL(menu), item);
1968 g_signal_connect(item, "activate", GTK_SIGNAL_FUNC(cb_menu_help), appdata);
1969 #endif
1970
1971 item = gtk_menu_item_new_with_label( _("About") );
1972 gtk_menu_append(GTK_MENU_SHELL(menu), item);
1973 g_signal_connect(item, "activate", GTK_SIGNAL_FUNC(cb_menu_about), appdata);
1974
1975 #ifndef USE_MAEMO
1976 item = gtk_menu_item_new_with_label( _("Quit") );
1977 gtk_menu_append(GTK_MENU_SHELL(menu), item);
1978 g_signal_connect(item, "activate", GTK_SIGNAL_FUNC(cb_menu_quit), appdata);
1979 #endif
1980
1981 #ifdef USE_MAEMO
1982 hildon_window_set_menu(appdata->window, GTK_MENU(menu));
1983 #else
1984 /* attach ordinary gtk menu */
1985 GtkWidget *menu_bar = gtk_menu_bar_new();
1986
1987 GtkWidget *root_menu = gtk_menu_item_new_with_label (_("Menu"));
1988 gtk_widget_show(root_menu);
1989
1990 gtk_menu_bar_append(menu_bar, root_menu);
1991 gtk_menu_item_set_submenu(GTK_MENU_ITEM (root_menu), menu);
1992
1993 gtk_widget_show(menu_bar);
1994 gtk_box_pack_start(GTK_BOX(appdata->vbox), menu_bar, 0, 0, 0);
1995 #endif
1996 }
1997 #endif
1998
1999 /********************* end of menu **********************/
2000
2001 void cleanup(appdata_t *appdata) {
2002 gpx_free_all(appdata->gpx);
2003 if(appdata->path) free(appdata->path);
2004 if(appdata->image_path) free(appdata->image_path);
2005 if(appdata->search_str) free(appdata->search_str);
2006
2007 #ifdef USE_STACKABLE_WINDOW
2008 if(appdata->export_menu) submenu_cleanup(appdata->export_menu);
2009 if(appdata->tools_menu) submenu_cleanup(appdata->tools_menu);
2010 #endif
2011
2012 gnome_vfs_shutdown();
2013 icons_free();
2014 gps_release(appdata);
2015
2016 #ifdef USE_MAEMO
2017 if(appdata->search_results) {
2018 printf("freeing pending search\n");
2019 search_result_free(appdata->search_results);
2020 }
2021
2022 if(appdata->osso_context)
2023 osso_deinitialize(appdata->osso_context);
2024
2025 appdata->program = NULL;
2026 #endif
2027
2028 /* free chain of locations */
2029 location_t *loc = appdata->location;
2030 while(loc) {
2031 location_t *next = loc->next;
2032 if(loc->name) free(loc->name);
2033 free(loc);
2034 loc = next;
2035 }
2036
2037 puts("everything is gone");
2038 }
2039
2040 static void on_window_destroy (GtkWidget *widget, gpointer data) {
2041 appdata_t *appdata = (appdata_t*)data;
2042
2043 gconf_save_state(appdata);
2044 gtk_main_quit();
2045 appdata->window = NULL;
2046 }
2047
2048 gboolean on_window_key_press(GtkWidget *widget,
2049 GdkEventKey *event, appdata_t *appdata) {
2050 int handled = FALSE;
2051
2052 // printf("key event %d\n", event->keyval);
2053
2054 switch(event->keyval) {
2055 #ifdef USE_MAEMO
2056
2057 #ifdef HILDON_HARDKEY_INCREASE
2058 case HILDON_HARDKEY_INCREASE:
2059 html_zoom(appdata, TRUE);
2060 handled = TRUE;
2061 break;
2062 #endif
2063
2064 #ifdef HILDON_HARDKEY_DECREASE
2065 case HILDON_HARDKEY_DECREASE:
2066 html_zoom(appdata, FALSE);
2067 handled = TRUE;
2068 break;
2069 #endif
2070
2071 #ifdef HILDON_HARDKEY_FULLSCREEN
2072 case HILDON_HARDKEY_FULLSCREEN:
2073 {
2074 appdata->fullscreen = !appdata->fullscreen;
2075
2076 if(appdata->fullscreen)
2077 gtk_window_fullscreen(GTK_WINDOW(appdata->window));
2078 else
2079 gtk_window_unfullscreen(GTK_WINDOW(appdata->window));
2080
2081 handled = TRUE;
2082 }
2083 break;
2084 #endif
2085
2086 #else
2087 case '+':
2088 printf("zoom+\n");
2089 html_zoom(appdata, TRUE);
2090 handled = TRUE;
2091 break;
2092 case '-':
2093 printf("zoom-\n");
2094 html_zoom(appdata, FALSE);
2095 handled = TRUE;
2096 break;
2097 #endif
2098 }
2099
2100 return handled;
2101 }
2102
2103 #if defined(USE_BREAD_CRUMB_TRAIL) || defined(BCT)
2104 typedef struct {
2105 int level;
2106 appdata_t *appdata;
2107 gpointer data;
2108 } crumb_t;
2109
2110 static void
2111 crumb_back(gpointer data) {
2112 crumb_t *crumb = (crumb_t*)data;
2113 printf("crumb_back called with %d\n", crumb->level);
2114
2115 /* don't do anything if main window has already been destroyed */
2116 if(!crumb->appdata->window) {
2117 printf("Main window gone ...\n");
2118 return;
2119 }
2120
2121 /* whatever is being displayed: we don't need it anymore */
2122 gtk_container_remove(GTK_CONTAINER(crumb->appdata->vbox),
2123 crumb->appdata->cur_view);
2124
2125 /* back from cache to cachelist */
2126 if(crumb->level == CRUMB_CACHE) {
2127 gpx_t *gpx = crumb->appdata->search_results;
2128
2129 if(!gpx) {
2130 gtk_widget_set_sensitive(crumb->appdata->menu_search, TRUE);
2131 gtk_widget_set_sensitive(crumb->appdata->menu_export, TRUE);
2132 printf("no search data found, return to gpx\n");
2133 gpx = crumb->appdata->cur_gpx;
2134 } else
2135 printf("returning to search result\n");
2136
2137 g_assert(gpx != NULL);
2138
2139 crumb->appdata->cur_view = cachelist_create(crumb->appdata, gpx,
2140 crumb->appdata->cur_cache);
2141
2142 /* returning from cache view: invalidate cache reference */
2143 crumb->appdata->cur_cache = NULL;
2144
2145 gtk_box_pack_start_defaults(GTK_BOX(crumb->appdata->vbox),
2146 crumb->appdata->cur_view);
2147 }
2148
2149 if(crumb->level == CRUMB_SEARCH_GPX) {
2150 printf("returning from a local search!\n");
2151
2152 g_assert((gpx_t*)crumb->data == crumb->appdata->search_results);
2153
2154 search_result_free((gpx_t*)crumb->data);
2155 crumb->appdata->search_results = NULL;
2156
2157 gtk_widget_set_sensitive(crumb->appdata->menu_search, TRUE);
2158
2159 crumb->appdata->cur_view = cachelist_create(crumb->appdata,
2160 crumb->appdata->cur_gpx, NULL);
2161 gtk_box_pack_start_defaults(GTK_BOX(crumb->appdata->vbox),
2162 crumb->appdata->cur_view);
2163 }
2164
2165 /* back from cachelist to gpxlist */
2166 if((crumb->level == CRUMB_CACHELIST) ||
2167 (crumb->level == CRUMB_SEARCH_GLOBAL)) {
2168
2169 crumb->appdata->cur_view = gpxlist_create_view_and_model(
2170 crumb->appdata, crumb->appdata->cur_gpx);
2171
2172 /* returning from cachelist/global search view: */
2173 /* invalidate gpx reference */
2174 crumb->appdata->cur_gpx = NULL;
2175
2176 gtk_box_pack_start_defaults(GTK_BOX(crumb->appdata->vbox),
2177 crumb->appdata->cur_view);
2178
2179 if((crumb->level == CRUMB_SEARCH_GLOBAL) ||
2180 (crumb->level == CRUMB_SEARCH_GPX)) {
2181 g_assert((gpx_t*)crumb->data == crumb->appdata->search_results);
2182
2183 search_result_free((gpx_t*)crumb->data);
2184 crumb->appdata->search_results = NULL;
2185 }
2186
2187 /* enable gpxlist related menu entries */
2188 gtk_widget_set_sensitive(crumb->appdata->menu_import, TRUE);
2189 gtk_widget_set_sensitive(crumb->appdata->menu_search, TRUE);
2190 gtk_widget_set_sensitive(crumb->appdata->menu_export, TRUE);
2191 }
2192
2193 gtk_widget_show_all(crumb->appdata->vbox);
2194 g_free(data);
2195
2196 #ifdef ENABLE_OSM_GPS_MAP
2197 map_update(crumb->appdata);
2198 #endif
2199 }
2200
2201 static void crumb_add(appdata_t *appdata, char *name, int level,
2202 gpointer user_data) {
2203 crumb_t *crumb = malloc(sizeof(crumb_t));
2204 crumb->level = level;
2205 crumb->appdata = appdata;
2206 crumb->data = user_data;
2207
2208 printf("crumb_add with level %d\n", level);
2209
2210 /* save that we are working on search results */
2211 if((level == CRUMB_SEARCH_GLOBAL) ||
2212 (level == CRUMB_SEARCH_GPX)) {
2213 appdata->search_results = (gpx_t*)user_data;
2214
2215 /* searches cannot be nested */
2216 gtk_widget_set_sensitive(appdata->menu_search, FALSE);
2217 }
2218
2219 /* save "path" pointers in appdata */
2220 if(crumb->level == CRUMB_CACHELIST)
2221 appdata->cur_gpx = (gpx_t*)user_data;
2222
2223 if(crumb->level == CRUMB_CACHE) {
2224 appdata->cur_cache = (cache_t*)user_data;
2225 /* the cache view cannot be searched */
2226 gtk_widget_set_sensitive(appdata->menu_search, FALSE);
2227 gtk_widget_set_sensitive(appdata->menu_export, FALSE);
2228 }
2229
2230 if(level != CRUMB_GPXLIST) {
2231 /* disable gpxlist related menu entries */
2232 gtk_widget_set_sensitive(appdata->menu_import, FALSE);
2233 #ifndef USE_PANNABLE_AREA
2234 gtk_widget_set_sensitive(appdata->menu_remove, FALSE);
2235 gtk_widget_set_sensitive(appdata->menu_close, FALSE);
2236 #endif
2237 }
2238
2239 #ifdef USE_BREAD_CRUMB_TRAIL
2240 hildon_bread_crumb_trail_push_text(HILDON_BREAD_CRUMB_TRAIL(appdata->bct),
2241 name, crumb, (GDestroyNotify)crumb_back);
2242 #else
2243 bct_push_text(appdata->bct, name, crumb, (GDestroyNotify)crumb_back);
2244 #endif
2245
2246 #ifdef ENABLE_OSM_GPS_MAP
2247 map_update(appdata);
2248 #endif
2249 }
2250 #endif // USE_BREAD_CRUMB_TRAIL
2251
2252 void main_after_settings_redraw(appdata_t *appdata, int flags) {
2253 printf("main after settings redraw\n");
2254
2255 if(!appdata->cur_view) {
2256 printf("no active view\n");
2257 return;
2258 }
2259
2260 /* a cache screen cannot be changed from the settings and thus doesn't */
2261 /* need to be redrawn */
2262 if(appdata->cur_cache) {
2263 printf("No redraw in cache view required\n");
2264 return;
2265 }
2266
2267 int redraw = 0; // nothing to redraw
2268
2269 if(appdata->search_results) {
2270 if((appdata->cur_items != appdata->cachelist_items) || flags)
2271 redraw = 1;
2272 } else {
2273 if(!appdata->cur_gpx) {
2274 if(appdata->cur_items != appdata->gpxlist_items)
2275 redraw = 2; // redraw gpxlist
2276 } else {
2277 if((appdata->cur_items != appdata->cachelist_items) || flags)
2278 redraw = 3; // redraw cachelist
2279 }
2280 }
2281
2282 if(redraw) {
2283 GtkWidget *container = appdata->vbox;
2284
2285 #ifdef USE_STACKABLE_WINDOW
2286 HildonWindowStack *stack = hildon_window_stack_get_default();
2287 container = hildon_window_stack_peek(stack);
2288 #endif
2289
2290 gtk_container_remove(GTK_CONTAINER(container), appdata->cur_view);
2291 switch(redraw) {
2292 case 1:
2293 appdata->cur_view = cachelist_create(appdata,
2294 appdata->search_results, NULL);
2295 break;
2296 case 2:
2297 appdata->cur_view = gpxlist_create_view_and_model(appdata, NULL);
2298 break;
2299 case 3:
2300 appdata->cur_view = cachelist_create(appdata,
2301 appdata->cur_gpx, NULL);
2302 break;
2303 }
2304
2305 #ifdef USE_STACKABLE_WINDOW
2306 if(container != appdata->vbox)
2307 gtk_container_add(GTK_CONTAINER(container), appdata->cur_view);
2308 else
2309 #endif
2310 gtk_box_pack_start_defaults(GTK_BOX(container), appdata->cur_view);
2311
2312 gtk_widget_show_all(container);
2313 }
2314 }
2315
2316 int main(int argc, char *argv[]) {
2317 appdata_t appdata;
2318
2319 /* init appdata */
2320 memset(&appdata, 0, sizeof(appdata));
2321
2322 printf("Using locale for %s in %s\n", PACKAGE, LOCALEDIR);
2323
2324 setlocale(LC_ALL, "");
2325 bindtextdomain(PACKAGE, LOCALEDIR);
2326 bind_textdomain_codeset(PACKAGE, "UTF-8");
2327 textdomain(PACKAGE);
2328
2329 /* prepare thread system */
2330 g_thread_init(NULL);
2331
2332 gtk_init (&argc, &argv);
2333
2334 curl_global_init(CURL_GLOBAL_ALL);
2335
2336 #ifdef USE_MAEMO
2337 printf("Installing osso context for \"org.harbaum." APP "\"\n");
2338 appdata.osso_context = osso_initialize("org.harbaum."APP,
2339 VERSION, TRUE, NULL);
2340 if(appdata.osso_context == NULL) {
2341 fprintf(stderr, "error initiating osso context\n");
2342 }
2343
2344 #ifdef ENABLE_MAEMO_MAPPER
2345 dbus_register(&appdata);
2346 #endif
2347 #endif
2348
2349 icons_init();
2350
2351 if(!gnome_vfs_init()) {
2352 g_error("Gnome VFS init failed\n");
2353 }
2354
2355 #ifdef USE_MAEMO
2356 /* Create the hildon program and setup the title */
2357 appdata.program = HILDON_PROGRAM(hildon_program_get_instance());
2358 g_set_application_name("GPXView");
2359
2360 /* Create HildonWindow and set it to HildonProgram */
2361 #ifdef USE_STACKABLE_WINDOW
2362 appdata.window = HILDON_WINDOW(hildon_stackable_window_new());
2363 #else
2364 appdata.window = HILDON_WINDOW(hildon_window_new());
2365 #endif
2366 hildon_program_add_window(appdata.program, appdata.window);
2367 #else
2368 /* Create a Window. */
2369 appdata.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
2370 /* Set a decent default size for the window. */
2371 gtk_window_set_default_size(GTK_WINDOW(appdata.window), 640, 480);
2372 #endif
2373
2374 #if MAEMO_VERSION_MAJOR == 5
2375 gtk_window_set_title(GTK_WINDOW(appdata.window), "GPXView");
2376 #endif
2377
2378 g_signal_connect(G_OBJECT(appdata.window), "destroy",
2379 G_CALLBACK(on_window_destroy), &appdata);
2380
2381 g_signal_connect(G_OBJECT(appdata.window), "key_press_event",
2382 G_CALLBACK(on_window_key_press), &appdata);
2383
2384 /* prepare clipboard */
2385 appdata.clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
2386 gtk_clipboard_set_can_store(appdata.clipboard, NULL, 0);
2387
2388 appdata.vbox = gtk_vbox_new(FALSE, 2);
2389 gtk_container_add(GTK_CONTAINER(appdata.window), appdata.vbox);
2390 #ifndef USE_STACKABLE_WINDOW
2391 menu_create(&appdata);
2392 #else
2393 hildon_window_set_app_menu(HILDON_WINDOW(appdata.window),
2394 menu_create(&appdata, MENU_GPXLIST));
2395 #endif
2396
2397 #ifdef USE_BREAD_CRUMB_TRAIL
2398 appdata.bct = hildon_bread_crumb_trail_new();
2399
2400 gtk_box_pack_start(GTK_BOX(appdata.vbox), appdata.bct, FALSE,FALSE,0);
2401
2402 hildon_bread_crumb_trail_clear(HILDON_BREAD_CRUMB_TRAIL(appdata.bct));
2403 #else
2404 #ifdef BCT
2405 /* on non-hildon machines we use some custom made breadcrumbtrail */
2406 /* replacement */
2407 appdata.bct = bct_new();
2408 gtk_box_pack_start(GTK_BOX(appdata.vbox), appdata.bct, FALSE,FALSE,0);
2409 #endif
2410 #endif
2411
2412 #if defined(USE_BREAD_CRUMB_TRAIL) || defined(BCT)
2413 crumb_add(&appdata, "GPX", CRUMB_GPXLIST, NULL);
2414 #endif
2415
2416 /* wait for main gui to appear */
2417 gtk_widget_show_all(GTK_WIDGET(appdata.window));
2418 while(gtk_events_pending())
2419 gtk_main_iteration();
2420
2421 appdata.gconf_client = gconf_client_get_default();
2422 gconf_load_state(&appdata);
2423 gps_init(&appdata);
2424
2425 appdata.cur_view = gpxlist_create_view_and_model(&appdata, NULL);
2426 gtk_box_pack_start_defaults(GTK_BOX(appdata.vbox), appdata.cur_view);
2427
2428 gtk_widget_show_all(GTK_WIDGET(appdata.window));
2429 gtk_main();
2430
2431 cleanup(&appdata);
2432
2433 return 0;
2434 }