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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 151 - (hide annotations)
Fri Oct 30 20:09:13 2009 UTC (14 years, 6 months ago) by harbaum
File MIME type: text/plain
File size: 25619 byte(s)
Semi-transparent map icons
1 harbaum 33 /*
2 harbaum 55 * Copyright (C) 2008-2009 Till Harbaum <till@harbaum.org>.
3 harbaum 33 *
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 harbaum 93 /*
21     * http://topo.geofabrik.de/relief/${z}/${x}/${y}.png 8-15
22     * http://topo.geofabrik.de/trail/${z}/${x}/${y}.png 8-15
23     */
24    
25 harbaum 151 /*
26     * TODO:
27     * - draw caches only once
28     * - prefer duplicates from selected files
29     * - make semi-transparent caches selectable
30     */
31    
32 harbaum 33 #include "gpxview.h"
33 harbaum 61 #include "converter.h"
34 harbaum 34 #include <math.h> // for isnan
35 harbaum 33
36     #ifdef ENABLE_OSM_GPS_MAP
37     #include "osm-gps-map.h"
38 harbaum 73 #include "osm-gps-map-osd-classic.h"
39 harbaum 33 #endif
40    
41 harbaum 77 #if defined(USE_MAEMO) && (MAEMO_VERSION_MAJOR == 5)
42     #include <gdk/gdkx.h>
43     #include <X11/Xatom.h>
44     #endif
45    
46 harbaum 98 /* default values */
47 harbaum 84 #define MAP_SOURCE OSM_GPS_MAP_SOURCE_OPENCYCLEMAP
48 harbaum 48 #define GPS_DEFAULT_ZOOM 13
49    
50 harbaum 34 #define PROXY_KEY "/system/http_proxy/"
51    
52     static const char *get_proxy_uri(appdata_t *appdata) {
53     static char proxy_buffer[64] = "";
54 harbaum 33
55     /* use environment settings if preset */
56     const char *proxy = g_getenv("http_proxy");
57     if(proxy) {
58     printf("http_proxy: %s\n", proxy);
59     return proxy;
60     }
61    
62 harbaum 34 /* ------------- get proxy settings -------------------- */
63     if(gconf_client_get_bool(appdata->gconf_client,
64     PROXY_KEY "use_http_proxy", NULL)) {
65 harbaum 33
66 harbaum 34 /* we can savely ignore things like "ignore_hosts" since we */
67     /* are pretty sure not inside the net of one of our map renderers */
68     /* (unless the user works at google :-) */
69    
70     /* get basic settings */
71     char *host =
72     gconf_client_get_string(appdata->gconf_client, PROXY_KEY "host", NULL);
73     if(host) {
74     int port =
75     gconf_client_get_int(appdata->gconf_client, PROXY_KEY "port", NULL);
76 harbaum 33
77 harbaum 34 snprintf(proxy_buffer, sizeof(proxy_buffer),
78     "http://%s:%u", host, port);
79 harbaum 33
80 harbaum 34 g_free(host);
81     }
82 harbaum 35 return proxy_buffer;
83 harbaum 34 }
84    
85 harbaum 35 return NULL;
86 harbaum 33 }
87    
88 harbaum 66 static void
89 harbaum 73 cb_map_gps(osd_button_t but, map_context_t *context) {
90 harbaum 143
91 harbaum 73 if(but == OSD_GPS) {
92     pos_t *refpos = get_pos(context->appdata);
93     if(refpos && !isnan(refpos->lat) && !isnan(refpos->lon)) {
94 harbaum 81 gint zoom;
95     g_object_get(OSM_GPS_MAP(context->widget), "zoom", &zoom, NULL);
96     if(zoom < 10)
97     osm_gps_map_set_mapcenter(OSM_GPS_MAP(context->widget),
98     refpos->lat, refpos->lon, GPS_DEFAULT_ZOOM);
99     else
100     osm_gps_map_set_center(OSM_GPS_MAP(context->widget),
101     refpos->lat, refpos->lon);
102    
103 harbaum 77 /* re-enable centering */
104     g_object_set(context->widget, "auto-center", TRUE, NULL);
105 harbaum 73 } else {
106     /* no coordinates given: display the entire world */
107     osm_gps_map_set_mapcenter(OSM_GPS_MAP(context->widget),
108     0.0, 0.0, 1);
109     }
110 harbaum 34 }
111 harbaum 33 }
112    
113 harbaum 55 static int dist2pixel(map_context_t *context, float km, float lat) {
114     return 1000.0*km/osm_gps_map_get_scale(OSM_GPS_MAP(context->widget));
115     }
116    
117 harbaum 33 static gboolean map_gps_update(gpointer data) {
118     map_context_t *context = (map_context_t*)data;
119 harbaum 148 static gboolean goto_is_enabled = FALSE;
120 harbaum 33
121 harbaum 51 /* get reference position ... */
122 harbaum 34 pos_t *refpos = get_pos(context->appdata);
123     gboolean ok = (refpos!= NULL) && !isnan(refpos->lat) && !isnan(refpos->lon);
124 harbaum 33
125 harbaum 51 /* ... and enable "goto" button if it's valid */
126 harbaum 148 if(ok != goto_is_enabled) {
127     osm_gps_map_osd_enable_gps (OSM_GPS_MAP(context->widget),
128     OSM_GPS_MAP_OSD_CALLBACK(ok?cb_map_gps:NULL), context);
129     goto_is_enabled = ok;
130     }
131 harbaum 34
132 harbaum 51 if(ok) {
133 harbaum 53 float heading = NAN;
134 harbaum 54 int radius = 0;
135 harbaum 51
136 harbaum 53 if(context->appdata->use_gps) {
137     heading = gps_get_heading(context->appdata);
138    
139     /* get error */
140     float eph = gps_get_eph(context->appdata);
141 harbaum 55 if(!isnan(eph))
142     radius = dist2pixel(context, eph/1000, refpos->lat);
143 harbaum 53 }
144    
145 harbaum 148 /* TODO: in order to save energy: only draw if state actually changed */
146    
147 harbaum 53 g_object_set(context->widget, "gps-track-highlight-radius", radius, NULL);
148     osm_gps_map_draw_gps(OSM_GPS_MAP(context->widget),
149 harbaum 51 refpos->lat, refpos->lon, heading);
150     } else
151     osm_gps_map_clear_gps(OSM_GPS_MAP(context->widget));
152    
153 harbaum 33 return TRUE;
154     }
155    
156 harbaum 151 static void map_draw_cache(GtkWidget *map, cache_t *cache, gboolean semi) {
157     GdkPixbuf *icon = icon_get(semi?ICON_CACHE_TYPE_SEMI:ICON_CACHE_TYPE,
158     cache->type);
159 harbaum 143
160     /* check if there's also an overwritten coordinate */
161     if(cache->notes && cache->notes->override) {
162     GdkPixbuf *over = icon_get(ICON_MISC, 1);
163    
164     osm_gps_map_add_image(OSM_GPS_MAP(map),
165     cache->notes->pos.lat, cache->notes->pos.lon, icon);
166    
167     osm_gps_map_add_image(OSM_GPS_MAP(map),
168     cache->notes->pos.lat, cache->notes->pos.lon, over);
169     } else {
170     if(!isnan(cache->pos.lat) && !isnan(cache->pos.lon))
171     osm_gps_map_add_image(OSM_GPS_MAP(map),
172     cache->pos.lat, cache->pos.lon, icon);
173     }
174     }
175    
176 harbaum 151 static void map_draw_gpx(appdata_t *appdata,
177     GtkWidget *map, gpx_t *gpx,
178     cache_t *nav, gboolean semi) {
179 harbaum 143 if(!gpx->notes_loaded) {
180     notes_load_all(appdata, gpx);
181     gpx->notes_loaded = TRUE;
182     }
183    
184     cache_t *cache = gpx->cache;
185     while(cache) {
186 harbaum 151 /* if nav is given draw all other caches semitransparent. */
187     /* if nav is not given do what semi sais */
188     map_draw_cache(map, cache, nav?(cache != nav):semi);
189 harbaum 143 cache = cache->next;
190     }
191     }
192    
193     /* draw geocaches and set window title */
194     static void map_setup(map_context_t *context) {
195     char *name = NULL;
196    
197     if(!context->appdata->cur_gpx && !context->appdata->cur_cache) {
198     if(context->state != MAP_ALL) {
199     printf("map_setup(ALL)\n");
200    
201     #ifdef OSD_NAV
202     /* no navigation in this mode */
203     osm_gps_map_osd_clear_nav (OSM_GPS_MAP(context->widget));
204     #endif
205    
206 harbaum 151 /* clear all existing cache images */
207 harbaum 143 osm_gps_map_clear_images (OSM_GPS_MAP(context->widget));
208    
209 harbaum 151 /* draw all geocaches and none is semi-transparent */
210 harbaum 143 gpx_t *gpx = context->appdata->gpx;
211     while(gpx) {
212 harbaum 151 map_draw_gpx(context->appdata, context->widget, gpx, NULL, FALSE);
213 harbaum 143 gpx = gpx->next;
214     }
215     name = g_strdup(_("all"));
216     context->state = MAP_ALL;
217     }
218     } else if(!context->appdata->cur_cache) {
219     if(context->state != MAP_GPX) {
220     printf("map_setup(GPX)\n");
221    
222     #ifdef OSD_NAV
223     /* no navigation in this mode */
224     osm_gps_map_osd_clear_nav (OSM_GPS_MAP(context->widget));
225     #endif
226    
227     /* clear all existing ccahe images */
228     osm_gps_map_clear_images (OSM_GPS_MAP(context->widget));
229    
230 harbaum 151 /* draw all geocaches and all other gpx files are semi-transparent */
231     gpx_t *gpx = context->appdata->gpx;
232     while(gpx) {
233     map_draw_gpx(context->appdata, context->widget, gpx, NULL,
234     gpx != context->appdata->cur_gpx);
235     gpx = gpx->next;
236     }
237    
238 harbaum 143 name = g_strdup(context->appdata->cur_gpx->name);
239     context->state = MAP_GPX;
240     }
241     } else {
242     cache_t *cache = context->appdata->cur_cache;
243    
244     printf("map_setp(CACHE)\n");
245    
246     /* no balloons in this mode */
247     context->balloon = NULL;
248     osm_gps_map_osd_clear_balloon (OSM_GPS_MAP(context->widget));
249    
250     /* clear all existing ccahe images */
251     osm_gps_map_clear_images (OSM_GPS_MAP(context->widget));
252    
253 harbaum 151 /* draw all geocaches and all but selected one are semi-transparent */
254     gpx_t *gpx = context->appdata->gpx;
255     while(gpx) {
256     map_draw_gpx(context->appdata, context->widget, gpx, cache, FALSE);
257     gpx = gpx->next;
258     }
259    
260 harbaum 143 name = g_strdup(cache->name);
261     context->state = MAP_CACHE;
262    
263     /* navigation in this mode! */
264     pos_t cpos = gpx_cache_pos(cache);
265    
266     #ifdef OSD_NAV
267     osm_gps_map_osd_draw_nav (OSM_GPS_MAP(context->widget),
268     context->appdata->imperial,
269     cpos.lat, cpos.lon, cache->name);
270     #else
271     #warning OSD_NAV not defined!
272     #endif
273     }
274    
275     if(name) {
276     char *title = g_strdup_printf(_("Map - %s"), name);
277     g_free(name);
278    
279     gtk_window_set_title(GTK_WINDOW(context->window), title);
280    
281     g_free(title);
282     } else
283     printf("map_setup(keep)\n");
284     }
285    
286 harbaum 35 static gboolean on_map_configure(GtkWidget *widget,
287     GdkEventConfigure *event,
288     map_context_t *context) {
289 harbaum 33
290 harbaum 143 /* for some reason there's a configure event with 1/1 */
291     /* on diablo. We just ignore this! */
292 harbaum 85
293 harbaum 143 printf("on_map_configure %d %d\n",
294     widget->allocation.width,
295     widget->allocation.height);
296    
297     if(!context->map_complete &&
298     (widget->allocation.width > 100) &&
299     (widget->allocation.height > 100)) {
300    
301     /* setup cache state */
302     map_setup(context);
303    
304 harbaum 85 /* set default values if they are invalid */
305     if(!context->appdata->map.zoom ||
306     isnan(context->appdata->map.pos.lat) ||
307     isnan(context->appdata->map.pos.lon)) {
308     printf("no valid map position found\n");
309    
310     pos_t *refpos = get_pos(context->appdata);
311     if(refpos && !isnan(refpos->lat) && !isnan(refpos->lon)) {
312 harbaum 143 printf("use refpos\n");
313    
314 harbaum 85 /* use gps position if present */
315     context->appdata->map.pos = *refpos;
316     context->appdata->map.zoom = GPS_DEFAULT_ZOOM;
317     } else {
318 harbaum 143 printf("use zero pos\n");
319    
320 harbaum 85 /* use world map otherwise */
321     context->appdata->map.pos.lat = 0.0;
322     context->appdata->map.pos.lon = 0.0;
323     context->appdata->map.zoom = 1;
324     }
325     }
326 harbaum 48
327 harbaum 85 /* jump to initial position */
328 harbaum 143 printf("osm_gps_map_set_mapcenter(%f,%f,%d)\n",
329     context->appdata->map.pos.lat,
330     context->appdata->map.pos.lon,
331     context->appdata->map.zoom);
332    
333 harbaum 85 osm_gps_map_set_mapcenter(OSM_GPS_MAP(context->widget),
334     context->appdata->map.pos.lat,
335     context->appdata->map.pos.lon,
336     context->appdata->map.zoom);
337     context->map_complete = TRUE;
338 harbaum 48 }
339 harbaum 35
340     return FALSE;
341     }
342    
343 harbaum 41 static void
344     map_cachelist_nearest(cache_t *cache, pos_t *pos,
345     cache_t **result, float *distance) {
346 harbaum 144
347 harbaum 41 while(cache) {
348 harbaum 120 pos_t cpos = gpx_cache_pos(cache);
349    
350 harbaum 41 float dist =
351 harbaum 120 pow(cpos.lat - pos->lat, 2) +
352     pow(cpos.lon - pos->lon, 2);
353 harbaum 41
354     if(!(dist > *distance)) {
355     *result = cache;
356     *distance = dist;
357     }
358    
359     cache = cache->next;
360     }
361     }
362    
363     static cache_t *map_closest(map_context_t *context, pos_t *pos) {
364     cache_t *result = NULL;
365     float distance = NAN;
366    
367 harbaum 120 if(!context->appdata->cur_gpx && !context->appdata->cur_cache) {
368 harbaum 41 /* search all geocaches */
369     gpx_t *gpx = context->appdata->gpx;
370     while(gpx) {
371     map_cachelist_nearest(gpx->cache, pos, &result, &distance);
372     gpx = gpx->next;
373     }
374 harbaum 120 } else if(context->appdata->cur_gpx) {
375 harbaum 41 map_cachelist_nearest(context->appdata->cur_gpx->cache,
376     pos, &result, &distance);
377 harbaum 120 } else
378     result = context->appdata->cur_gpx->cache;
379 harbaum 41
380     return result;
381     }
382    
383     /* translate between osm-gps-map positions and gpxview ones */
384     pos_t coord2pos(coord_t coo) {
385     pos_t pos;
386 harbaum 61 pos.lat = rad2deg(coo.rlat);
387     pos.lon = rad2deg(coo.rlon);
388 harbaum 41 return pos;
389     }
390    
391 harbaum 47 #define CLICK_FUZZ (24)
392 harbaum 42
393 harbaum 41 static gboolean
394     on_map_button_press_event(GtkWidget *widget,
395     GdkEventButton *event, map_context_t *context) {
396 harbaum 143
397 harbaum 41 OsmGpsMap *map = OSM_GPS_MAP(context->widget);
398    
399 harbaum 86 /* check if we actually clicked parts of the OSD */
400 harbaum 144 if(osm_gps_map_osd_check(map, event->x, event->y) != OSD_NONE)
401 harbaum 86 return FALSE;
402    
403 harbaum 44 /* got a press event without release event? eat it! */
404     if(context->press_on != NULL) {
405     printf("PRESS: already\n");
406 harbaum 86 return FALSE;
407 harbaum 44 }
408    
409 harbaum 41 pos_t pos =
410 harbaum 42 coord2pos(osm_gps_map_get_co_ordinates(map, event->x, event->y));
411 harbaum 41
412 harbaum 42 cache_t *nearest = map_closest(context, &pos);
413     if(nearest) {
414 harbaum 120 pos_t cpos = gpx_cache_pos(nearest);
415    
416     float dist = gpx_pos_get_distance(pos, cpos, FALSE);
417     if(dist2pixel(context, dist, cpos.lat) < CLICK_FUZZ)
418 harbaum 42 context->press_on = nearest;
419     }
420 harbaum 44
421 harbaum 41 return FALSE;
422     }
423    
424 harbaum 58 static void
425     cairo_draw_pixbuf(cairo_t *cr, GdkPixbuf *buf, gint x, gint y) {
426     /* convert the pixbuf into something cairo can handle */
427    
428     // Create a new ImageSurface
429     cairo_surface_t *image_surface =
430     cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
431     gdk_pixbuf_get_width(buf),
432     gdk_pixbuf_get_height(buf));
433    
434     // Create the new Context for the ImageSurface
435     cairo_t *context = cairo_create(image_surface);
436    
437     // Draw the image on the new Context
438     gdk_cairo_set_source_pixbuf(context, buf, 0.0, 0.0);
439     cairo_paint(context);
440    
441     // now draw this onto the original context
442     cairo_set_source_surface(cr, image_surface, x, y);
443 harbaum 60
444 harbaum 58 cairo_paint(cr);
445     }
446    
447 harbaum 65 #ifndef BIG_BALLOONS
448 harbaum 60 #define LINE_SKIP 7
449 harbaum 65 #else
450     #define LINE_SKIP 12
451     #endif
452 harbaum 59
453 harbaum 58 static void
454 harbaum 136 balloon_cb(osm_gps_map_balloon_event_t *event, gpointer data) {
455 harbaum 144 printf("balloon event: ");
456 harbaum 143
457 harbaum 136 map_context_t *context = (map_context_t*)data;
458     cache_t *cache = context->balloon;
459 harbaum 58
460 harbaum 136 if(event->type == OSM_GPS_MAP_BALLOON_EVENT_TYPE_DRAW) {
461     printf("draw\n");
462    
463 harbaum 58 #if 0
464 harbaum 136 /* draw pink background to check clipping */
465     cairo_rectangle (event->data.draw.cr,
466     event->data.draw.rect->x-20, event->data.draw.rect->y-20,
467     event->data.draw.rect->w+40, event->data.draw.rect->h+40);
468     cairo_set_source_rgba (event->data.draw.cr, 1, 0, 0, 0.3);
469     cairo_fill_preserve (event->data.draw.cr);
470     cairo_set_line_width (event->data.draw.cr, 0);
471     cairo_stroke (event->data.draw.cr);
472 harbaum 58 #endif
473 harbaum 136
474     /* leave a little border top and left */
475     gint x = event->data.draw.rect->x, y = event->data.draw.rect->y;
476    
477     /* draw the cache type icon ... */
478     GdkPixbuf *icon = icon_get(ICON_CACHE_TYPE, cache->type);
479     cairo_draw_pixbuf(event->data.draw.cr, icon, x, y);
480    
481     if(cache->notes && cache->notes->override) {
482     GdkPixbuf *over = icon_get(ICON_MISC, 1);
483     cairo_draw_pixbuf(event->data.draw.cr, over, x, y);
484     }
485    
486     /* ... and right of it the waypoint id */
487     cairo_text_extents_t extents;
488    
489     if(cache->id) {
490     cairo_select_font_face (event->data.draw.cr, "Sans",
491     CAIRO_FONT_SLANT_NORMAL,
492     CAIRO_FONT_WEIGHT_BOLD);
493    
494 harbaum 65 #ifndef BIG_BALLOONS
495 harbaum 136 cairo_set_font_size (event->data.draw.cr, 20.0);
496 harbaum 65 #else
497 harbaum 136 cairo_set_font_size (event->data.draw.cr, 36.0);
498 harbaum 65 #endif
499 harbaum 136
500     cairo_text_extents (event->data.draw.cr, cache->id, &extents);
501    
502     /* display id right of icon vertically centered */
503     x += gdk_pixbuf_get_width(icon) + 5;
504     y += (gdk_pixbuf_get_height(icon) + extents.height)/2;
505     cairo_move_to (event->data.draw.cr, x, y);
506     cairo_set_source_rgba (event->data.draw.cr, 0, 0, 0, 1);
507     cairo_show_text (event->data.draw.cr, cache->id);
508     cairo_stroke (event->data.draw.cr);
509    
510     y += (gdk_pixbuf_get_height(icon) - extents.height)/2 + LINE_SKIP;
511     } else
512     y += gdk_pixbuf_get_height(icon);
513    
514     /* return to the left border and below icon/text */
515     x = event->data.draw.rect->x;
516    
517     /* everything from here uses the same font */
518     cairo_select_font_face (event->data.draw.cr, "Sans", CAIRO_FONT_SLANT_NORMAL,
519     CAIRO_FONT_WEIGHT_NORMAL);
520 harbaum 65 #ifndef BIG_BALLOONS
521 harbaum 136 cairo_set_font_size (event->data.draw.cr, 14.0);
522 harbaum 65 #else
523 harbaum 136 cairo_set_font_size (event->data.draw.cr, 22.0);
524 harbaum 65 #endif
525 harbaum 136
526     if(cache->name) {
527     /* draw cache name */
528     cairo_text_extents (event->data.draw.cr, cache->name, &extents);
529     y += extents.height;
530     cairo_move_to (event->data.draw.cr, x, y);
531     cairo_set_source_rgba (event->data.draw.cr, 0, 0, 0, 1);
532     cairo_show_text (event->data.draw.cr, cache->name);
533     cairo_stroke (event->data.draw.cr);
534    
535     /* return to the left border and below text */
536     y += LINE_SKIP;
537     x = event->data.draw.rect->x;
538     }
539    
540     if(cache->terrain) {
541     /* draw cache rating */
542     const char *terrain = "Terrain:";
543     icon = icon_get(ICON_STARS, (int)(cache->terrain*2-2));
544     cairo_text_extents (event->data.draw.cr, _(terrain), &extents);
545     y += (gdk_pixbuf_get_height(icon) + extents.height)/2;
546    
547     /* draw "Terrain:" string */
548     cairo_move_to (event->data.draw.cr, x, y);
549     cairo_set_source_rgba (event->data.draw.cr, 0, 0, 0, 1);
550     cairo_show_text (event->data.draw.cr, _(terrain));
551     cairo_stroke (event->data.draw.cr);
552     x += extents.width + 2;
553    
554     /* draw terrain stars */
555     cairo_draw_pixbuf(event->data.draw.cr, icon, x, y -
556     (gdk_pixbuf_get_height(icon) + extents.height)/2);
557    
558     x += gdk_pixbuf_get_width(icon) + LINE_SKIP;
559 harbaum 60 y -= (gdk_pixbuf_get_height(icon) + extents.height)/2;
560 harbaum 136 }
561 harbaum 60
562 harbaum 136 if(cache->difficulty) {
563     const char *difficulty = "Difficulty:";
564     cairo_text_extents (event->data.draw.cr, _(difficulty), &extents);
565     y += (gdk_pixbuf_get_height(icon) + extents.height)/2;
566    
567     /* draw "Difficulty:" string */
568     cairo_move_to (event->data.draw.cr, x, y);
569     cairo_set_source_rgba (event->data.draw.cr, 0, 0, 0, 1);
570     cairo_show_text (event->data.draw.cr, _(difficulty));
571     cairo_stroke (event->data.draw.cr);
572     x += extents.width + 2;
573    
574     icon = icon_get(ICON_STARS, (int)(cache->difficulty*2-2));
575     cairo_draw_pixbuf(event->data.draw.cr, icon, x, y -
576 harbaum 60 (gdk_pixbuf_get_height(icon) + extents.height)/2);
577 harbaum 136 }
578     } else if(event->type == OSM_GPS_MAP_BALLOON_EVENT_TYPE_CLICK) {
579     printf("click %s event at %d %d\n",
580     event->data.click.down?"down":"up",
581     event->data.click.x, event->data.click.y);
582    
583 harbaum 142 /* make the main screen jump to that cache */
584     if(!event->data.click.down) {
585     if(context->appdata->cur_cache) {
586     printf("ERROR: no current cache should be visible!\n");
587     } else {
588     gpx_t *is_in = NULL;
589    
590     if(!context->appdata->cur_gpx) {
591     printf("click while in \"all\" view\n");
592    
593     /* we first need to figure out which gpx file this cache */
594     /* is in so we can open it first */
595     gpx_t *gpx = context->appdata->gpx;
596     while(gpx && !is_in) {
597     cache_t *cur = gpx->cache;
598     while(cur && !is_in) {
599     if(cur == cache)
600     is_in = gpx;
601     cur = cur->next;
602     }
603     gpx = gpx->next;
604     }
605    
606     if(is_in)
607     gpxlist_goto_cachelist(context->appdata, is_in);
608    
609     } else
610     /* the simple case: there already is an open gpx file and */
611     /* we just jump into the "cache" view */
612     is_in = context->appdata->cur_gpx;
613    
614     if(is_in) {
615     printf("selecting %s in %s\n",
616     cache->id,
617     context->appdata->cur_gpx->name);
618    
619     cachelist_goto_cache(context->appdata, cache);
620    
621     /* give focus to main screen (important for maemo) */
622     printf("raising main window\n");
623     gtk_window_present(GTK_WINDOW(context->appdata->window));
624     }
625     }
626     }
627 harbaum 136 } else if(event->type == OSM_GPS_MAP_BALLOON_EVENT_TYPE_REMOVED) {
628     printf("removed\n");
629     context->balloon = NULL;
630 harbaum 60 }
631 harbaum 58 }
632    
633 harbaum 41 static gboolean
634     on_map_button_release_event(GtkWidget *widget,
635     GdkEventButton *event, map_context_t *context) {
636 harbaum 48 OsmGpsMap *map = OSM_GPS_MAP(context->widget);
637    
638 harbaum 133 /* in "MAP_CACHE" state only one cache is visible */
639     /* and the map is in navigation mode. the balloon is */
640     /* pretty useless there */
641     if(context->press_on && (context->state != MAP_CACHE)) {
642    
643 harbaum 57 coord_t coo;
644     coo = osm_gps_map_get_co_ordinates(map, event->x, event->y);
645    
646 harbaum 42 pos_t pos =
647     coord2pos(osm_gps_map_get_co_ordinates(map, event->x, event->y));
648 harbaum 41
649 harbaum 42 cache_t *nearest = map_closest(context, &pos);
650     if(nearest && nearest == context->press_on) {
651 harbaum 120 pos_t cpos = gpx_cache_pos(nearest);
652    
653     float dist = gpx_pos_get_distance(pos, cpos, FALSE);
654     if(dist2pixel(context, dist, cpos.lat) < CLICK_FUZZ) {
655 harbaum 57
656 harbaum 136 context->balloon = nearest;
657 harbaum 120 osm_gps_map_osd_draw_balloon(map, cpos.lat, cpos.lon,
658 harbaum 136 balloon_cb, context);
659 harbaum 57 }
660 harbaum 42 }
661 harbaum 44 context->press_on = NULL;
662 harbaum 48 } else {
663     /* save new map position */
664     gfloat lat, lon;
665     g_object_get(map, "latitude", &lat, "longitude", &lon, NULL);
666     context->appdata->map.pos.lat = lat;
667     context->appdata->map.pos.lon = lon;
668 harbaum 41 }
669    
670     return FALSE;
671     }
672    
673 harbaum 56 static void on_window_destroy(GtkWidget *widget, map_context_t *context) {
674     appdata_t *appdata = context->appdata;
675    
676 harbaum 48 /* save map parameters */
677     OsmGpsMap *map = OSM_GPS_MAP(context->widget);
678     gint zoom;
679     g_object_get(map, "zoom", &zoom, NULL);
680     context->appdata->map.zoom = zoom;
681 harbaum 44
682 harbaum 48 gfloat lat, lon;
683     g_object_get(map, "latitude", &lat, "longitude", &lon, NULL);
684     context->appdata->map.pos.lat = lat;
685     context->appdata->map.pos.lon = lon;
686    
687 harbaum 89 gint source;
688     g_object_get(map, "map-source", &source, NULL);
689     context->appdata->map.source = source;
690    
691 harbaum 40 #if MAEMO_VERSION_MAJOR == 5
692     /* restore cur_view */
693     context->appdata->cur_view = context->old_view;
694 harbaum 56 #endif
695 harbaum 40
696     gtk_timeout_remove(context->handler_id);
697 harbaum 149 gps_unregister_callback(appdata, context->cb_id);
698 harbaum 56
699 harbaum 40 g_free(context);
700 harbaum 56 appdata->map.context = NULL;
701 harbaum 40 }
702    
703 harbaum 77 #if (MAEMO_VERSION_MAJOR == 5) && !defined(__i386__)
704     /* get access to zoom buttons */
705     static void
706     on_window_realize(GtkWidget *widget, gpointer data) {
707     if (widget->window) {
708     unsigned char value = 1;
709     Atom hildon_zoom_key_atom =
710     gdk_x11_get_xatom_by_name("_HILDON_ZOOM_KEY_ATOM"),
711     integer_atom = gdk_x11_get_xatom_by_name("INTEGER");
712     Display *dpy =
713     GDK_DISPLAY_XDISPLAY(gdk_drawable_get_display(widget->window));
714     Window w = GDK_WINDOW_XID(widget->window);
715    
716     XChangeProperty(dpy, w, hildon_zoom_key_atom,
717     integer_atom, 8, PropModeReplace, &value, 1);
718     }
719     }
720     #endif
721    
722 harbaum 143 /* on maemo a window is either on top or completely invisible. this */
723     /* means that we only need to update the map window if its raised. */
724     /* on ordinary desktops this is different and we always update */
725 harbaum 125
726 harbaum 129 static gboolean on_focus_in(GtkWidget *widget, GdkEventFocus *event,
727     gpointer data) {
728 harbaum 133 printf("map got focus\n");
729 harbaum 130 map_setup((map_context_t*)data);
730 harbaum 129 return FALSE;
731     }
732    
733     void map_update(appdata_t *appdata) {
734 harbaum 143 printf("map_update\n");
735 harbaum 130 #ifndef USE_MAEMO
736     if(appdata->map.context)
737     map_setup(appdata->map.context);
738     #endif
739 harbaum 129 }
740    
741 harbaum 149 /* callback called by the gps layer whenever gps state changes */
742     static void
743     gps_callback(struct gps_state *state, gpointer data) {
744     printf("map: gps callback\n");
745     }
746    
747 harbaum 33 void map(appdata_t *appdata) {
748 harbaum 56 map_context_t *context = NULL;
749    
750     /* if the map window already exists, just raise it */
751     if(appdata->map.context) {
752 harbaum 125 printf("using existing map!\n");
753 harbaum 56 gtk_window_present(GTK_WINDOW(appdata->map.context->window));
754 harbaum 125 map_setup(appdata->map.context);
755 harbaum 56 return;
756     }
757    
758     context = appdata->map.context = g_new0(map_context_t, 1);
759 harbaum 40 context->appdata = appdata;
760 harbaum 85 context->map_complete = FALSE;
761 harbaum 130 context->state = MAP_NONE;
762 harbaum 33
763 harbaum 77 /* cleanup old (pre 0.8.7) path if it exists */
764     char *old_path = g_strdup_printf("%s/map/", appdata->image_path);
765     if(g_file_test(old_path, G_FILE_TEST_IS_DIR)) {
766     printf("old file path %s exists\n", old_path);
767     rmdir_recursive(old_path);
768     }
769    
770     /* It is recommanded that all applications share these same */
771     /* map path, so data is only cached once. The path should be: */
772     /* ~/.osm-gps-map on standard PC (users home) */
773     /* /home/user/.osm-gps-map on Maemo5 (ext3 on internal card) */
774     /* /media/mmc2/osm-gps-map on Maemo4 (vfat on internal card) */
775     #if !defined(USE_MAEMO)
776     char *p = getenv("HOME");
777     if(!p) p = "/tmp";
778     char *path = g_strdup_printf("%s/.osm-gps-map", p);
779     #else
780     #if MAEMO_VERSION_MAJOR == 5
781     char *path = g_strdup("/home/user/.osm-gps-map");
782     #else
783     char *path = g_strdup("/media/mmc2/osm-gps-map");
784     #endif
785     #endif
786    
787 harbaum 41 const char *proxy = get_proxy_uri(appdata);
788    
789 harbaum 89 gint source = context->appdata->map.source;
790     if(!source) source = MAP_SOURCE;
791    
792 harbaum 41 context->widget = g_object_new(OSM_TYPE_GPS_MAP,
793 harbaum 89 "map-source", source,
794 harbaum 55 "tile-cache", path,
795     "auto-center", FALSE,
796     "record-trip-history", FALSE,
797     "show-trip-history", FALSE,
798     proxy?"proxy-uri":NULL, proxy,
799 harbaum 41 NULL);
800    
801     g_free(path);
802    
803 harbaum 73 osm_gps_map_osd_classic_init(OSM_GPS_MAP(context->widget));
804 harbaum 125
805 harbaum 41 #ifdef USE_MAEMO
806 harbaum 125 /* we don't use a stackable window here on fremantle, since */
807     /* this leaves the main window independent from the map and */
808     /* the user can e.g. still navigate the main menu */
809     context->window = hildon_window_new();
810 harbaum 41
811 harbaum 125 #if (MAEMO_VERSION_MAJOR == 5) && !defined(__i386__)
812 harbaum 77 g_signal_connect(G_OBJECT(context->window), "realize",
813     G_CALLBACK(on_window_realize), NULL);
814     #endif // MAEMO_VERSION
815 harbaum 40 #else
816 harbaum 56 context->window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
817     #endif
818 harbaum 33
819     #ifndef USE_MAEMO
820 harbaum 56 gtk_window_set_default_size(GTK_WINDOW(context->window), 640, 480);
821 harbaum 33 #endif
822    
823 harbaum 129 g_signal_connect(G_OBJECT(context->widget), "focus-in-event",
824     G_CALLBACK(on_focus_in), context);
825    
826 harbaum 41 g_signal_connect(G_OBJECT(context->widget), "configure-event",
827     G_CALLBACK(on_map_configure), context);
828 harbaum 33
829 harbaum 41 g_signal_connect(G_OBJECT(context->widget), "button-press-event",
830     G_CALLBACK(on_map_button_press_event), context);
831 harbaum 33
832 harbaum 40 g_signal_connect(G_OBJECT(context->widget), "button-release-event",
833     G_CALLBACK(on_map_button_release_event), context);
834 harbaum 33
835     /* install handler for timed updates of the gps button */
836 harbaum 40 context->handler_id = gtk_timeout_add(1000, map_gps_update, context);
837 harbaum 149 context->cb_id = gps_register_callback(appdata, gps_callback, context);
838 harbaum 33
839 harbaum 40 #if MAEMO_VERSION_MAJOR == 5
840     /* prevent some of the main screen things */
841     context->old_view = appdata->cur_view;
842     appdata->cur_view = NULL;
843 harbaum 56 #endif
844 harbaum 40
845 harbaum 56 g_signal_connect(G_OBJECT(context->window), "destroy",
846 harbaum 40 G_CALLBACK(on_window_destroy), context);
847    
848 harbaum 63 gtk_container_add(GTK_CONTAINER(context->window), context->widget);
849 harbaum 56 gtk_widget_show_all(GTK_WIDGET(context->window));
850 harbaum 33 }