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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 41 - (show 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 /*
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 #include <math.h> // for isnan
22
23 #ifdef ENABLE_OSM_GPS_MAP
24 #include "osm-gps-map.h"
25 #endif
26
27 typedef struct {
28 appdata_t *appdata;
29 GtkWidget *widget;
30 GtkWidget *zoomin, *zoomout, *gps;
31 gint handler_id;
32 #if MAEMO_VERSION_MAJOR == 5
33 GtkWidget *old_view;
34 #endif
35 } map_context_t;
36
37 #define PROXY_KEY "/system/http_proxy/"
38
39 static const char *get_proxy_uri(appdata_t *appdata) {
40 static char proxy_buffer[64] = "";
41
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 /* ------------- get proxy settings -------------------- */
50 if(gconf_client_get_bool(appdata->gconf_client,
51 PROXY_KEY "use_http_proxy", NULL)) {
52
53 /* 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
64 snprintf(proxy_buffer, sizeof(proxy_buffer),
65 "http://%s:%u", host, port);
66
67 g_free(host);
68 }
69 return proxy_buffer;
70 }
71
72 return NULL;
73 }
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 pos_t *refpos = get_pos(context->appdata);
101 if(refpos && !isnan(refpos->lat) && !isnan(refpos->lon)) {
102 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 }
109
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 pos_t *refpos = get_pos(context->appdata);
130 gboolean ok = (refpos!= NULL) && !isnan(refpos->lat) && !isnan(refpos->lon);
131
132 /* get reference position and go there */
133 gtk_widget_set_sensitive(context->gps, ok);
134
135 return TRUE;
136 }
137
138 static gboolean on_map_configure(GtkWidget *widget,
139 GdkEventConfigure *event,
140 map_context_t *context) {
141
142 cb_map_gps(NULL, context);
143
144 return FALSE;
145 }
146
147 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 /* 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 #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 void map(appdata_t *appdata) {
411 map_context_t *context = g_new0(map_context_t, 1);
412 context->appdata = appdata;
413
414 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 #if MAEMO_VERSION_MAJOR == 5
449 GtkWidget *window = hildon_stackable_window_new();
450 gtk_window_set_title(GTK_WINDOW(window), title);
451 #else
452 GtkWidget *dialog = gtk_dialog_new_with_buttons(title,
453 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 #endif
464
465 g_free(title);
466
467 g_signal_connect(G_OBJECT(context->widget), "configure-event",
468 G_CALLBACK(on_map_configure), context);
469
470 g_signal_connect(G_OBJECT(context->widget), "button-press-event",
471 G_CALLBACK(on_map_button_press_event), context);
472
473 g_signal_connect(G_OBJECT(context->widget), "button-release-event",
474 G_CALLBACK(on_map_button_release_event), context);
475
476 gtk_box_pack_start_defaults(GTK_BOX(hbox), context->widget);
477 /* zoom button box */
478 GtkWidget *vbox = gtk_vbox_new(FALSE,0);
479
480 context->zoomin =
481 map_add_button(GTK_STOCK_ZOOM_IN, G_CALLBACK(cb_map_zoomin),
482 context, _("Zoom in"));
483 gtk_box_pack_start(GTK_BOX(vbox), context->zoomin, FALSE, FALSE, 0);
484
485 context->zoomout =
486 map_add_button(GTK_STOCK_ZOOM_OUT, G_CALLBACK(cb_map_zoomout),
487 context, _("Zoom out"));
488 gtk_box_pack_start(GTK_BOX(vbox), context->zoomout, FALSE, FALSE, 0);
489
490 context->gps =
491 map_add_button(GTK_STOCK_HOME, G_CALLBACK(cb_map_gps),
492 context, _("Jump to GPS position"));
493 gtk_widget_set_sensitive(context->gps, FALSE);
494 /* install handler for timed updates of the gps button */
495 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
498 gtk_box_pack_start(GTK_BOX(hbox), vbox, FALSE, FALSE, 0);
499
500 #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 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 gtk_timeout_remove(context->handler_id);
517 gtk_widget_destroy(dialog);
518 g_free(context);
519 #endif
520 }