Contents of /trunk/src/main.c

Parent Directory Parent Directory | Revision Log Revision Log


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