Contents of /trunk/src/main.c

Parent Directory Parent Directory | Revision Log Revision Log


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