Contents of /trunk/src/map-tool.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 41 - (hide annotations)
Mon Aug 3 14:21:57 2009 UTC (14 years, 9 months ago) by harbaum
File MIME type: text/plain
File size: 14620 byte(s)
Cache map popup started
1 harbaum 33 /*
2     * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3     *
4     * This file is part of GPXView.
5     *
6     * GPXView is free software: you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation, either version 3 of the License, or
9     * (at your option) any later version.
10     *
11     * GPXView is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with GPXView. If not, see <http://www.gnu.org/licenses/>.
18     */
19    
20     #include "gpxview.h"
21 harbaum 34 #include <math.h> // for isnan
22 harbaum 33
23     #ifdef ENABLE_OSM_GPS_MAP
24     #include "osm-gps-map.h"
25     #endif
26    
27     typedef struct {
28 harbaum 34 appdata_t *appdata;
29 harbaum 33 GtkWidget *widget;
30     GtkWidget *zoomin, *zoomout, *gps;
31     gint handler_id;
32 harbaum 40 #if MAEMO_VERSION_MAJOR == 5
33     GtkWidget *old_view;
34     #endif
35 harbaum 33 } map_context_t;
36    
37 harbaum 34 #define PROXY_KEY "/system/http_proxy/"
38    
39     static const char *get_proxy_uri(appdata_t *appdata) {
40     static char proxy_buffer[64] = "";
41 harbaum 33
42     /* use environment settings if preset */
43     const char *proxy = g_getenv("http_proxy");
44     if(proxy) {
45     printf("http_proxy: %s\n", proxy);
46     return proxy;
47     }
48    
49 harbaum 34 /* ------------- get proxy settings -------------------- */
50     if(gconf_client_get_bool(appdata->gconf_client,
51     PROXY_KEY "use_http_proxy", NULL)) {
52 harbaum 33
53 harbaum 34 /* we can savely ignore things like "ignore_hosts" since we */
54     /* are pretty sure not inside the net of one of our map renderers */
55     /* (unless the user works at google :-) */
56    
57     /* get basic settings */
58     char *host =
59     gconf_client_get_string(appdata->gconf_client, PROXY_KEY "host", NULL);
60     if(host) {
61     int port =
62     gconf_client_get_int(appdata->gconf_client, PROXY_KEY "port", NULL);
63 harbaum 33
64 harbaum 34 snprintf(proxy_buffer, sizeof(proxy_buffer),
65     "http://%s:%u", host, port);
66 harbaum 33
67 harbaum 34 g_free(host);
68     }
69 harbaum 35 return proxy_buffer;
70 harbaum 34 }
71    
72 harbaum 35 return NULL;
73 harbaum 33 }
74    
75     static void map_zoom(map_context_t *context, int step) {
76     int zoom;
77     OsmGpsMap *map = OSM_GPS_MAP(context->widget);
78     g_object_get(map, "zoom", &zoom, NULL);
79     zoom = osm_gps_map_set_zoom(map, zoom+step);
80    
81     /* enable/disable zoom buttons as required */
82     gtk_widget_set_sensitive(context->zoomin, zoom<17);
83     gtk_widget_set_sensitive(context->zoomout, zoom>1);
84     }
85    
86     static gboolean
87     cb_map_zoomin(GtkButton *button, map_context_t *context) {
88     map_zoom(context, +1);
89     return FALSE;
90     }
91    
92     static gboolean
93     cb_map_zoomout(GtkButton *button, map_context_t *context) {
94     map_zoom(context, -1);
95     return FALSE;
96     }
97    
98     static gboolean
99     cb_map_gps(GtkButton *button, map_context_t *context) {
100 harbaum 34 pos_t *refpos = get_pos(context->appdata);
101     if(refpos && !isnan(refpos->lat) && !isnan(refpos->lon)) {
102 harbaum 35 osm_gps_map_set_mapcenter(OSM_GPS_MAP(context->widget),
103     refpos->lat, refpos->lon, 14);
104     } else {
105     /* no coordinates given: display the entire world */
106     osm_gps_map_set_mapcenter(OSM_GPS_MAP(context->widget),
107     0.0, 0.0, 1);
108 harbaum 34 }
109 harbaum 33
110     return FALSE;
111     }
112    
113     static GtkWidget
114     *map_add_button(const gchar *icon, GCallback cb, gpointer data,
115     char *tooltip) {
116     GtkWidget *button = gtk_button_new();
117     gtk_button_set_image(GTK_BUTTON(button),
118     gtk_image_new_from_stock(icon, GTK_ICON_SIZE_MENU));
119     g_signal_connect(button, "clicked", cb, data);
120     #ifndef USE_MAEMO
121     gtk_widget_set_tooltip_text(button, tooltip);
122     #endif
123     return button;
124     }
125    
126     static gboolean map_gps_update(gpointer data) {
127     map_context_t *context = (map_context_t*)data;
128    
129 harbaum 34 pos_t *refpos = get_pos(context->appdata);
130     gboolean ok = (refpos!= NULL) && !isnan(refpos->lat) && !isnan(refpos->lon);
131 harbaum 33
132 harbaum 34 /* get reference position and go there */
133     gtk_widget_set_sensitive(context->gps, ok);
134    
135 harbaum 33 return TRUE;
136     }
137    
138 harbaum 35 static gboolean on_map_configure(GtkWidget *widget,
139     GdkEventConfigure *event,
140     map_context_t *context) {
141 harbaum 33
142 harbaum 35 cb_map_gps(NULL, context);
143    
144     return FALSE;
145     }
146    
147 harbaum 38 static void map_draw_cachelist(GtkWidget *map, cache_t *cache) {
148     while(cache) {
149     GdkPixbuf *icon = icon_get(ICON_CACHE_TYPE, cache->type);
150    
151     osm_gps_map_add_image(OSM_GPS_MAP(map),
152     cache->pos.lat, cache->pos.lon, icon);
153    
154     cache = cache->next;
155     }
156     }
157    
158 harbaum 41 /* draw a nice popup */
159     typedef struct {
160     appdata_t *appdata;
161     GtkWidget *window;
162     GMainLoop *loop;
163     } popup_context_t;
164    
165    
166     #ifndef USE_HILDON
167     #define POPUP_WIDTH 300
168     #define POPUP_HEIGHT 100
169     #else
170     #define POPUP_WIDTH 600
171     #define POPUP_HEIGHT 200
172     #endif
173    
174     static gboolean
175     pointer_in_window(GtkWidget *widget, gint x_root, gint y_root) {
176     if(GTK_WIDGET_MAPPED(gtk_widget_get_toplevel(widget))) {
177     gint window_x, window_y;
178    
179     gdk_window_get_position(gtk_widget_get_toplevel(widget)->window,
180     &window_x, &window_y);
181    
182     if(x_root >= window_x && x_root < window_x + widget->allocation.width &&
183     y_root >= window_y && y_root < window_y + widget->allocation.height)
184     return TRUE;
185     }
186    
187     return FALSE;
188     }
189    
190     static gboolean
191     on_button_press_event(GtkWidget *widget,
192     GdkEventButton *event, popup_context_t *context) {
193     gboolean in = pointer_in_window(widget, event->x_root, event->y_root);
194    
195     printf("overlay button press(in = %d)\n", in);
196     return !in;
197     }
198    
199     static gboolean
200     on_button_release_event(GtkWidget *widget,
201     GdkEventButton *event, popup_context_t *context) {
202     gboolean in = pointer_in_window(widget, event->x_root, event->y_root);
203    
204     printf("overlay button release(in = %d)\n", in);
205    
206     if(!in) {
207     printf("destroying popup\n");
208     gtk_widget_destroy(gtk_widget_get_toplevel(widget));
209     }
210    
211     return !in;
212     }
213    
214     static void
215     shutdown_loop(popup_context_t *context) {
216     if(g_main_loop_is_running(context->loop))
217     g_main_loop_quit(context->loop);
218     }
219    
220     static gint
221     run_delete_handler(GtkWindow *window, GdkEventAny *event,
222     popup_context_t *context) {
223     shutdown_loop(context);
224     return TRUE; /* Do not destroy */
225     }
226    
227     static void
228     run_destroy_handler(GtkWindow *window, popup_context_t *context) {
229     /* shutdown_loop will be called by run_unmap_handler */
230     printf("popup destroyed\n");
231     }
232    
233     static void
234     run_unmap_handler(GtkWindow *window, popup_context_t *context) {
235     shutdown_loop(context);
236     }
237    
238     void cache_popup(appdata_t *appdata, cache_t *cache) {
239     popup_context_t context;
240     context.appdata = appdata;
241    
242     context.window = gtk_window_new(GTK_WINDOW_POPUP);
243     gtk_widget_realize(context.window);
244     gtk_window_set_default_size(GTK_WINDOW(context.window),
245     POPUP_WIDTH, POPUP_HEIGHT);
246     gtk_window_resize(GTK_WINDOW(context.window),
247     POPUP_WIDTH, POPUP_HEIGHT);
248     // gtk_window_set_resizable(GTK_WINDOW(context.window), FALSE);
249     gtk_window_set_transient_for(GTK_WINDOW(context.window),
250     GTK_WINDOW(appdata->window));
251     gtk_window_set_keep_above(GTK_WINDOW(context.window), TRUE);
252     gtk_window_set_destroy_with_parent(GTK_WINDOW(context.window), TRUE);
253     gtk_window_set_gravity(GTK_WINDOW(context.window), GDK_GRAVITY_STATIC);
254     gtk_window_set_modal(GTK_WINDOW(context.window), TRUE);
255    
256     /* connect events */
257     g_signal_connect(G_OBJECT(context.window), "button-press-event",
258     G_CALLBACK(on_button_press_event), &context);
259     g_signal_connect(G_OBJECT(context.window), "button-release-event",
260     G_CALLBACK(on_button_release_event), &context);
261     g_signal_connect(G_OBJECT(context.window), "delete-event",
262     G_CALLBACK(run_delete_handler), &context);
263     g_signal_connect(G_OBJECT(context.window), "destroy",
264     G_CALLBACK(run_destroy_handler), &context);
265     g_signal_connect(G_OBJECT(context.window), "unmap",
266     G_CALLBACK(run_unmap_handler), &context);
267    
268     gdk_pointer_grab(context.window->window, TRUE,
269     GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_MOTION_MASK,
270     NULL, NULL, GDK_CURRENT_TIME);
271     gtk_grab_add(context.window);
272    
273     // gint x, y;
274     // gdk_window_get_origin(button->window, &x, &y);
275    
276     // gtk_window_move(GTK_WINDOW(context.window),
277     // x + button->allocation.x,
278     // y + button->allocation.y - HEIGHT);
279    
280    
281     gtk_window_move(GTK_WINDOW(context.window),
282     100,
283     100);
284    
285     /* a frame with a vscale inside */
286     GtkWidget *frame = gtk_frame_new(NULL);
287     gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_OUT);
288    
289     gtk_container_add(GTK_CONTAINER(frame), gtk_label_new(cache->name));
290     gtk_container_add(GTK_CONTAINER(context.window), frame);
291    
292     gtk_widget_show_all(context.window);
293    
294     /* handle this popup until it's gone */
295    
296     context.loop = g_main_loop_new(NULL, FALSE);
297    
298     GDK_THREADS_LEAVE();
299     g_main_loop_run(context.loop);
300     GDK_THREADS_ENTER();
301    
302     g_main_loop_unref(context.loop);
303    
304     printf("cache popup removed\n");
305     }
306    
307     #define RAD2DEG(a) (((a)*180.0)/M_PI)
308    
309     static void
310     map_cachelist_nearest(cache_t *cache, pos_t *pos,
311     cache_t **result, float *distance) {
312     while(cache) {
313     float dist =
314     pow(cache->pos.lat - pos->lat, 2) +
315     pow(cache->pos.lon - pos->lon, 2);
316    
317     if(!(dist > *distance)) {
318     *result = cache;
319     *distance = dist;
320     }
321    
322     cache = cache->next;
323     }
324     }
325    
326     static cache_t *map_closest(map_context_t *context, pos_t *pos) {
327     cache_t *result = NULL;
328     float distance = NAN;
329    
330     #ifdef USE_MAEMO
331     if(!context->appdata->cur_gpx) {
332     #endif
333     /* search all geocaches */
334     gpx_t *gpx = context->appdata->gpx;
335     while(gpx) {
336     map_cachelist_nearest(gpx->cache, pos, &result, &distance);
337     gpx = gpx->next;
338     }
339     #ifdef USE_MAEMO
340     } else {
341     map_cachelist_nearest(context->appdata->cur_gpx->cache,
342     pos, &result, &distance);
343     }
344     #endif
345    
346     return result;
347     }
348    
349     /* translate between osm-gps-map positions and gpxview ones */
350     pos_t coord2pos(coord_t coo) {
351     pos_t pos;
352     pos.lat = RAD2DEG(coo.rlat);
353     pos.lon = RAD2DEG(coo.rlon);
354     return pos;
355     }
356    
357     static gboolean
358     on_map_button_press_event(GtkWidget *widget,
359     GdkEventButton *event, map_context_t *context) {
360     OsmGpsMap *map = OSM_GPS_MAP(context->widget);
361    
362     pos_t pos =
363     coord2pos(osm_gps_map_get_co_ordinates(map, (int)event->x, (int)event->y));
364    
365     printf("clicked at %f/%f\n", pos.lat, pos.lon);
366    
367     return FALSE;
368     }
369    
370     static gboolean
371     on_map_button_release_event(GtkWidget *widget,
372     GdkEventButton *event, map_context_t *context) {
373     OsmGpsMap *map = OSM_GPS_MAP(context->widget);
374    
375     pos_t pos =
376     coord2pos(osm_gps_map_get_co_ordinates(map, (int)event->x, (int)event->y));
377    
378     printf("released at %f/%f\n", pos.lat, pos.lon);
379    
380     /* return true if we clicked a cache */
381     /* ... */
382    
383     cache_t *nearest = map_closest(context, &pos);
384    
385     if(nearest) {
386     float dist = gpx_pos_get_distance(pos, nearest->pos, FALSE);
387    
388     printf("nearest = %s, distance = %fkm\n", nearest->name, dist);
389    
390     cache_popup(context->appdata, nearest);
391    
392     return TRUE;
393     }
394    
395     return FALSE;
396     }
397    
398 harbaum 40 #if MAEMO_VERSION_MAJOR == 5
399     static void on_window_destroy(GtkWidget *widget, map_context_t *context) {
400     printf("destroy map view\n");
401    
402     /* restore cur_view */
403     context->appdata->cur_view = context->old_view;
404    
405     gtk_timeout_remove(context->handler_id);
406     g_free(context);
407     }
408     #endif
409    
410 harbaum 33 void map(appdata_t *appdata) {
411 harbaum 40 map_context_t *context = g_new0(map_context_t, 1);
412     context->appdata = appdata;
413 harbaum 33
414 harbaum 41 GtkWidget *hbox = gtk_hbox_new(FALSE, 0);
415    
416     char *path = g_strdup_printf("%s/map/", appdata->image_path);
417     const char *proxy = get_proxy_uri(appdata);
418    
419     context->widget = g_object_new(OSM_TYPE_GPS_MAP,
420     "repo-uri", MAP_SOURCE_OPENSTREETMAP,
421     "tile-cache", path,
422     proxy?"proxy-uri":NULL, proxy,
423     NULL);
424    
425     g_free(path);
426    
427     char *name = NULL;
428     #ifdef USE_MAEMO
429     if(!appdata->cur_gpx) {
430     #endif
431     /* draw all geocaches */
432     gpx_t *gpx = appdata->gpx;
433     while(gpx) {
434     map_draw_cachelist(context->widget, gpx->cache);
435     gpx = gpx->next;
436     }
437     name = g_strdup(_("all geocaches"));
438     #ifdef USE_MAEMO
439     } else {
440     map_draw_cachelist(context->widget, appdata->cur_gpx->cache);
441     name = g_strdup(_("appdata->cur_gpx->name"));
442     }
443     #endif
444    
445     char *title = g_strdup_printf(_("Map - %s"), name);
446     g_free(name);
447    
448 harbaum 40 #if MAEMO_VERSION_MAJOR == 5
449     GtkWidget *window = hildon_stackable_window_new();
450 harbaum 41 gtk_window_set_title(GTK_WINDOW(window), title);
451 harbaum 40 #else
452 harbaum 41 GtkWidget *dialog = gtk_dialog_new_with_buttons(title,
453 harbaum 33 GTK_WINDOW(appdata->window),
454     GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
455     GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE,
456     NULL);
457    
458     #ifndef USE_MAEMO
459     gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 350);
460     #else
461     gtk_window_set_default_size(GTK_WINDOW(dialog), 800, 480);
462     #endif
463 harbaum 40 #endif
464 harbaum 33
465 harbaum 41 g_free(title);
466 harbaum 33
467 harbaum 41 g_signal_connect(G_OBJECT(context->widget), "configure-event",
468     G_CALLBACK(on_map_configure), context);
469 harbaum 33
470 harbaum 41 g_signal_connect(G_OBJECT(context->widget), "button-press-event",
471     G_CALLBACK(on_map_button_press_event), context);
472 harbaum 33
473 harbaum 40 g_signal_connect(G_OBJECT(context->widget), "button-release-event",
474     G_CALLBACK(on_map_button_release_event), context);
475 harbaum 33
476 harbaum 40 gtk_box_pack_start_defaults(GTK_BOX(hbox), context->widget);
477 harbaum 33 /* zoom button box */
478     GtkWidget *vbox = gtk_vbox_new(FALSE,0);
479    
480 harbaum 40 context->zoomin =
481 harbaum 33 map_add_button(GTK_STOCK_ZOOM_IN, G_CALLBACK(cb_map_zoomin),
482 harbaum 40 context, _("Zoom in"));
483     gtk_box_pack_start(GTK_BOX(vbox), context->zoomin, FALSE, FALSE, 0);
484 harbaum 33
485 harbaum 40 context->zoomout =
486 harbaum 33 map_add_button(GTK_STOCK_ZOOM_OUT, G_CALLBACK(cb_map_zoomout),
487 harbaum 40 context, _("Zoom out"));
488     gtk_box_pack_start(GTK_BOX(vbox), context->zoomout, FALSE, FALSE, 0);
489 harbaum 33
490 harbaum 40 context->gps =
491 harbaum 33 map_add_button(GTK_STOCK_HOME, G_CALLBACK(cb_map_gps),
492 harbaum 40 context, _("Jump to GPS position"));
493     gtk_widget_set_sensitive(context->gps, FALSE);
494 harbaum 33 /* install handler for timed updates of the gps button */
495 harbaum 40 context->handler_id = gtk_timeout_add(1000, map_gps_update, context);
496     gtk_box_pack_start(GTK_BOX(vbox), context->gps, FALSE, FALSE, 0);
497 harbaum 33
498     gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
499    
500 harbaum 40 #if MAEMO_VERSION_MAJOR == 5
501     /* prevent some of the main screen things */
502     context->old_view = appdata->cur_view;
503     appdata->cur_view = NULL;
504    
505     g_signal_connect(G_OBJECT(window), "destroy",
506     G_CALLBACK(on_window_destroy), context);
507    
508     gtk_container_add(GTK_CONTAINER(window), hbox);
509     gtk_widget_show_all(GTK_WIDGET(window));
510    
511     #else
512 harbaum 33 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox);
513     gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CLOSE);
514     gtk_widget_show_all(dialog);
515     gtk_dialog_run(GTK_DIALOG(dialog));
516 harbaum 40 gtk_timeout_remove(context->handler_id);
517 harbaum 33 gtk_widget_destroy(dialog);
518 harbaum 40 g_free(context);
519     #endif
520 harbaum 33 }