Contents of /trunk/src/osm-gps-map-osd-classic.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 280 - (show annotations)
Fri May 21 19:28:16 2010 UTC (13 years, 11 months ago) by harbaum
File MIME type: text/plain
File size: 72338 byte(s)
Map widget update
1 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
2 /* vim:set et sw=4 ts=4 cino=t0,(0: */
3 /*
4 * Copyright (C) Till Harbaum 2009 <till@harbaum.org>
5 *
6 * osm-gps-map is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * osm-gps-map is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "config.h"
21
22 #include <stdlib.h> // abs
23 #include <string.h>
24 #include <math.h> // M_PI/cos()
25
26 /* parameters that can be overwritten from the config file: */
27 /* OSD_DIAMETER */
28 /* OSD_X, OSD_Y */
29
30 #include <cairo.h>
31
32 #include "osm-gps-map.h"
33 #include "converter.h"
34 #include "osm-gps-map-osd-classic.h"
35
36 static OsmGpsMapSource_t map_sources[] = {
37 OSM_GPS_MAP_SOURCE_OPENSTREETMAP,
38 OSM_GPS_MAP_SOURCE_OPENSTREETMAP_RENDERER,
39 OSM_GPS_MAP_SOURCE_OPENCYCLEMAP,
40 OSM_GPS_MAP_SOURCE_OSM_PUBLIC_TRANSPORT,
41 OSM_GPS_MAP_SOURCE_GOOGLE_STREET,
42 OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_STREET,
43 OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_SATELLITE,
44 OSM_GPS_MAP_SOURCE_VIRTUAL_EARTH_HYBRID
45 };
46 static int num_map_sources = sizeof(map_sources)/sizeof(map_sources[0]);
47
48 #ifdef OSD_DOUBLEPIXEL
49 #define OSD_DPIX_EXTRA 1
50 #define OSD_DPIX_SKIP (OSD_FONT_SIZE/2)
51 #else
52 #define OSD_DPIX_EXTRA 0
53 #define OSD_DPIX_SKIP 0
54 #endif
55
56 //the osd controls
57 typedef struct {
58 /* the offscreen representation of the OSD */
59 struct {
60 cairo_surface_t *surface;
61 gboolean rendered;
62 #ifdef OSD_GPS_BUTTON
63 gboolean gps_enabled;
64 #endif
65 } controls;
66
67 #ifdef OSD_BALLOON
68 //a balloon with additional info
69 struct {
70 cairo_surface_t *surface;
71 int orientation, offset_x, offset_y;
72
73 gboolean just_created;
74 float lat, lon;
75 OsmGpsMapRect_t rect;
76
77 /* function called to have the user app draw the contents */
78 OsmGpsMapBalloonCallback cb;
79 gpointer data;
80 } balloon;
81 #endif
82
83 #ifdef OSD_SCALE
84 struct {
85 cairo_surface_t *surface;
86 int zoom;
87 } scale;
88 #endif
89
90 #ifdef OSD_CROSSHAIR
91 struct {
92 cairo_surface_t *surface;
93 gboolean rendered;
94 } crosshair;
95 #endif
96
97 #ifdef OSD_NAV
98 struct {
99 cairo_surface_t *surface;
100 float lat, lon;
101 char *name;
102 gboolean imperial, mode; // display distance imperial/metric
103 int click_sep;
104 } nav;
105 #endif
106
107 #ifdef OSD_HEARTRATE
108 struct {
109 cairo_surface_t *surface;
110 gint rate;
111 gboolean ok;
112 } hr;
113 #endif
114
115 #ifdef OSD_COORDINATES
116 struct {
117 cairo_surface_t *surface;
118 float lat, lon;
119 } coordinates;
120 #endif
121
122 #ifdef OSD_SOURCE_SEL
123 struct {
124 /* values to handle the "source" menu */
125 cairo_surface_t *surface;
126 gboolean expanded;
127 gint shift, dir, count;
128 gint handler_id;
129 gint width, height;
130 gboolean rendered;
131 gint max_h;
132 } source_sel;
133 #endif
134
135 } osd_priv_t;
136
137 #ifdef OSD_BALLOON
138 /* most visual effects are hardcoded by now, but may be made */
139 /* available via properties later */
140 #ifndef BALLOON_AREA_WIDTH
141 #define BALLOON_AREA_WIDTH 290
142 #endif
143 #ifndef BALLOON_AREA_HEIGHT
144 #define BALLOON_AREA_HEIGHT 75
145 #endif
146 #ifndef BALLOON_CORNER_RADIUS
147 #define BALLOON_CORNER_RADIUS 10
148 #endif
149
150 #define BALLOON_BORDER (BALLOON_CORNER_RADIUS/2)
151 #define BALLOON_WIDTH (priv->balloon.rect.w + 2 * BALLOON_BORDER)
152 #define BALLOON_HEIGHT (priv->balloon.rect.h + 2 * BALLOON_BORDER)
153 #define BALLOON_TRANSPARENCY 0.8
154 #define POINTER_HEIGHT 20
155 #define POINTER_FOOT_WIDTH 20
156 #define POINTER_OFFSET (BALLOON_CORNER_RADIUS*3/4)
157 #define BALLOON_SHADOW (BALLOON_CORNER_RADIUS/2)
158 #define BALLOON_SHADOW_TRANSPARENCY 0.2
159
160 #define BALLOON_W (BALLOON_WIDTH + BALLOON_SHADOW)
161 #define BALLOON_H (BALLOON_HEIGHT + POINTER_HEIGHT + BALLOON_SHADOW)
162
163 /* draw the bubble shape. this is used twice, once for the shape and once */
164 /* for the shadow */
165 static void
166 osm_gps_map_draw_balloon_shape (cairo_t *cr, int x0, int y0, int x1, int y1,
167 gboolean bottom, int px, int py, int px0, int px1) {
168
169 cairo_move_to (cr, x0, y0 + BALLOON_CORNER_RADIUS);
170 cairo_arc (cr, x0 + BALLOON_CORNER_RADIUS, y0 + BALLOON_CORNER_RADIUS,
171 BALLOON_CORNER_RADIUS, -M_PI, -M_PI/2);
172 if(!bottom) {
173 /* insert top pointer */
174 cairo_line_to (cr, px1, y0);
175 cairo_line_to (cr, px, py);
176 cairo_line_to (cr, px0, y0);
177 }
178
179 cairo_line_to (cr, x1 - BALLOON_CORNER_RADIUS, y0);
180 cairo_arc (cr, x1 - BALLOON_CORNER_RADIUS, y0 + BALLOON_CORNER_RADIUS,
181 BALLOON_CORNER_RADIUS, -M_PI/2, 0);
182 cairo_line_to (cr, x1 , y1 - BALLOON_CORNER_RADIUS);
183 cairo_arc (cr, x1 - BALLOON_CORNER_RADIUS, y1 - BALLOON_CORNER_RADIUS,
184 BALLOON_CORNER_RADIUS, 0, M_PI/2);
185 if(bottom) {
186 /* insert bottom pointer */
187 cairo_line_to (cr, px0, y1);
188 cairo_line_to (cr, px, py);
189 cairo_line_to (cr, px1, y1);
190 }
191
192 cairo_line_to (cr, x0 + BALLOON_CORNER_RADIUS, y1);
193 cairo_arc (cr, x0 + BALLOON_CORNER_RADIUS, y1 - BALLOON_CORNER_RADIUS,
194 BALLOON_CORNER_RADIUS, M_PI/2, M_PI);
195
196 cairo_close_path (cr);
197 }
198
199 static void
200 osd_render_balloon(osm_gps_map_osd_t *osd) {
201 osd_priv_t *priv = (osd_priv_t*)osd->priv;
202
203 if(!priv->balloon.surface)
204 return;
205 /* get zoom */
206 gint zoom;
207 g_object_get(OSM_GPS_MAP(osd->widget), "zoom", &zoom, NULL);
208
209 /* ------- convert given coordinate into screen position --------- */
210 gint xs, ys;
211 osm_gps_map_geographic_to_screen (OSM_GPS_MAP(osd->widget),
212 priv->balloon.lat, priv->balloon.lon,
213 &xs, &ys);
214
215 gint x0 = 1, y0 = 1;
216
217 /* check position of this relative to screen center to determine */
218 /* pointer direction ... */
219 int pointer_x, pointer_x0, pointer_x1;
220 int pointer_y;
221
222 /* ... and calculate position */
223 int orientation = 0;
224 if(xs > osd->widget->allocation.width/2) {
225 priv->balloon.offset_x = -BALLOON_WIDTH + POINTER_OFFSET;
226 pointer_x = x0 - priv->balloon.offset_x;
227 pointer_x0 = pointer_x - (BALLOON_CORNER_RADIUS - POINTER_OFFSET);
228 pointer_x1 = pointer_x0 - POINTER_FOOT_WIDTH;
229 orientation |= 1;
230 } else {
231 priv->balloon.offset_x = -POINTER_OFFSET;
232 pointer_x = x0 - priv->balloon.offset_x;
233 pointer_x1 = pointer_x + (BALLOON_CORNER_RADIUS - POINTER_OFFSET);
234 pointer_x0 = pointer_x1 + POINTER_FOOT_WIDTH;
235 }
236
237 gboolean bottom = FALSE;
238 if(ys > osd->widget->allocation.height/2) {
239 priv->balloon.offset_y = -BALLOON_HEIGHT - POINTER_HEIGHT;
240 pointer_y = y0 - priv->balloon.offset_y;
241 bottom = TRUE;
242 orientation |= 2;
243 } else {
244 priv->balloon.offset_y = 0;
245 pointer_y = y0 - priv->balloon.offset_y;
246 y0 += POINTER_HEIGHT;
247 }
248
249 /* if required orientation equals current one, then don't render */
250 /* anything */
251 if(orientation == priv->balloon.orientation)
252 return;
253
254 priv->balloon.orientation = orientation;
255
256 /* calculate bottom/right of box */
257 int x1 = x0 + priv->balloon.rect.w + 2*BALLOON_BORDER;
258 int y1 = y0 + priv->balloon.rect.h + 2*BALLOON_BORDER;
259
260 /* save balloon screen coordinates for later use */
261 priv->balloon.rect.x = x0 + BALLOON_BORDER;
262 priv->balloon.rect.y = y0 + BALLOON_BORDER;
263
264 g_assert(priv->balloon.surface);
265 cairo_t *cr = cairo_create(priv->balloon.surface);
266 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
267 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
268 cairo_paint(cr);
269 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
270
271 /* --------- draw shadow --------------- */
272 osm_gps_map_draw_balloon_shape (cr,
273 x0 + BALLOON_SHADOW, y0 + BALLOON_SHADOW,
274 x1 + BALLOON_SHADOW, y1 + BALLOON_SHADOW,
275 bottom, pointer_x, pointer_y,
276 pointer_x0 + BALLOON_SHADOW, pointer_x1 + BALLOON_SHADOW);
277
278 cairo_set_source_rgba (cr, 0, 0, 0, BALLOON_SHADOW_TRANSPARENCY);
279 cairo_fill_preserve (cr);
280 cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
281 cairo_set_line_width (cr, 0);
282 cairo_stroke (cr);
283
284 /* --------- draw main shape ----------- */
285 osm_gps_map_draw_balloon_shape (cr, x0, y0, x1, y1,
286 bottom, pointer_x, pointer_y, pointer_x0, pointer_x1);
287
288 cairo_set_source_rgba (cr, 1, 1, 1, BALLOON_TRANSPARENCY);
289 cairo_fill_preserve (cr);
290 cairo_set_source_rgba (cr, 0, 0, 0, BALLOON_TRANSPARENCY);
291 cairo_set_line_width (cr, 1);
292 cairo_stroke (cr);
293
294 if (priv->balloon.cb) {
295 osm_gps_map_balloon_event_t event;
296
297 /* clip in case application tries to draw in */
298 /* exceed of the balloon */
299 cairo_rectangle (cr, priv->balloon.rect.x, priv->balloon.rect.y,
300 priv->balloon.rect.w, priv->balloon.rect.h);
301 cairo_clip (cr);
302 cairo_new_path (cr); /* current path is not consumed by cairo_clip */
303
304 /* request the application to draw the balloon contents */
305 event.type = OSM_GPS_MAP_BALLOON_EVENT_TYPE_DRAW;
306 event.data.draw.rect = &priv->balloon.rect;
307 event.data.draw.cr = cr;
308
309 priv->balloon.cb(&event, priv->balloon.data);
310 }
311
312 cairo_destroy(cr);
313 }
314
315 #define OSD_STATE_DOWN 0
316 #define OSD_STATE_UP 1
317 #define OSD_STATE_CHECK 2
318
319 /* return true if balloon is being displayed and if */
320 /* the given coordinate is within this balloon */
321 static gboolean
322 osd_balloon_check(osm_gps_map_osd_t *osd, gboolean click, gint state, gint x, gint y)
323 {
324 osd_priv_t *priv = (osd_priv_t*)osd->priv;
325
326 if(!priv->balloon.surface)
327 return FALSE;
328
329 gint xs, ys;
330 osm_gps_map_geographic_to_screen (OSM_GPS_MAP(osd->widget),
331 priv->balloon.lat, priv->balloon.lon,
332 &xs, &ys);
333
334 xs += priv->balloon.rect.x + priv->balloon.offset_x;
335 ys += priv->balloon.rect.y + priv->balloon.offset_y;
336
337 gboolean is_in =
338 (x > xs) && (x < xs + priv->balloon.rect.w) &&
339 (y > ys) && (y < ys + priv->balloon.rect.h);
340
341 /* is this a real click or is the application just checking for something? */
342 if(click) {
343
344 /* handle the fact that the balloon may have been created by the */
345 /* button down event */
346 if(!is_in && state == OSD_STATE_UP && !priv->balloon.just_created) {
347 /* the user actually clicked outside the balloon */
348
349 /* close the balloon! */
350 osm_gps_map_osd_clear_balloon (OSM_GPS_MAP(osd->widget));
351
352 /* and inform application about this */
353 if(priv->balloon.cb) {
354 osm_gps_map_balloon_event_t event;
355 event.type = OSM_GPS_MAP_BALLOON_EVENT_TYPE_REMOVED;
356 priv->balloon.cb(&event, priv->balloon.data);
357 }
358
359 }
360
361 if(is_in && priv->balloon.cb) {
362 osm_gps_map_balloon_event_t event;
363
364 /* notify application of click */
365 event.type = OSM_GPS_MAP_BALLOON_EVENT_TYPE_CLICK;
366 event.data.click.x = x - xs;
367 event.data.click.y = y - ys;
368 event.data.click.down = (state == OSD_STATE_DOWN);
369
370 priv->balloon.cb(&event, priv->balloon.data);
371 }
372 }
373 return is_in;
374 }
375
376 void osm_gps_map_osd_clear_balloon (OsmGpsMap *map) {
377 g_return_if_fail (OSM_IS_GPS_MAP (map));
378
379 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
380 g_return_if_fail (osd);
381
382 osd_priv_t *priv = (osd_priv_t*)osd->priv;
383 g_return_if_fail (priv);
384
385 if(priv->balloon.surface) {
386 cairo_surface_destroy(priv->balloon.surface);
387 priv->balloon.surface = NULL;
388 priv->balloon.lat = OSM_GPS_MAP_INVALID;
389 priv->balloon.lon = OSM_GPS_MAP_INVALID;
390 }
391 osm_gps_map_redraw(map);
392 }
393
394 void
395 osm_gps_map_osd_draw_balloon (OsmGpsMap *map, float latitude, float longitude,
396 OsmGpsMapBalloonCallback cb, gpointer data) {
397 g_return_if_fail (OSM_IS_GPS_MAP (map));
398
399 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
400 g_return_if_fail (osd);
401
402 osd_priv_t *priv = (osd_priv_t*)osd->priv;
403 g_return_if_fail (priv);
404
405 osm_gps_map_osd_clear_balloon (map);
406
407 priv->balloon.lat = latitude;
408 priv->balloon.lon = longitude;
409 priv->balloon.cb = cb;
410 priv->balloon.data = data;
411 priv->balloon.just_created = TRUE;
412 priv->balloon.orientation = -1;
413 priv->balloon.rect.w = 0;
414 priv->balloon.rect.h = 0;
415
416 /* set default size and call app callback */
417 if(!priv->balloon.rect.w || !priv->balloon.rect.h) {
418 osm_gps_map_balloon_event_t event;
419
420 priv->balloon.rect.w = BALLOON_AREA_WIDTH;
421 priv->balloon.rect.h = BALLOON_AREA_HEIGHT;
422
423 /* create some temporary surface, so the callback can */
424 /* e.g. determine text extents etc */
425 cairo_surface_t *surface =
426 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10,10);
427
428 event.type = OSM_GPS_MAP_BALLOON_EVENT_TYPE_SIZE_REQUEST;
429 event.data.draw.rect = &priv->balloon.rect;
430 event.data.draw.cr = cairo_create(surface);
431 priv->balloon.cb(&event, priv->balloon.data);
432
433 cairo_destroy(event.data.draw.cr);
434 cairo_surface_destroy(surface);
435 }
436
437 /* allocate balloon surface */
438 priv->balloon.surface =
439 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
440 BALLOON_W+2, BALLOON_H+2);
441
442 osd_render_balloon(osd);
443
444 osm_gps_map_redraw(map);
445 }
446
447 #endif // OSD_BALLOON
448
449 /* position and extent of bounding box */
450 #ifndef OSD_X
451 #define OSD_X (10)
452 #endif
453
454 #ifndef OSD_Y
455 #define OSD_Y (10)
456 #endif
457
458 /* parameters of the direction shape */
459 #ifndef OSD_DIAMETER
460 #define D_RAD (30) // diameter of dpad
461 #else
462 #define D_RAD (OSD_DIAMETER)
463 #endif
464 #define D_TIP (4*D_RAD/5) // distance of arrow tip from dpad center
465 #define D_LEN (D_RAD/4) // length of arrow
466 #define D_WID (D_LEN) // width of arrow
467
468 /* parameters of the "zoom" pad */
469 #define Z_STEP (D_RAD/4) // distance between dpad and zoom
470 #define Z_RAD (D_RAD/2) // radius of "caps" of zoom bar
471
472 #ifdef OSD_SHADOW_ENABLE
473 /* shadow also depends on control size */
474 #define OSD_SHADOW (D_RAD/6)
475 #else
476 #define OSD_SHADOW (0)
477 #endif
478
479 /* normally the GPS button is in the center of the dpad. if there's */
480 /* no dpad it will go into the zoom area */
481 #if defined(OSD_GPS_BUTTON) && defined(OSD_NO_DPAD)
482 #define Z_GPS 1
483 #else
484 #define Z_GPS 0
485 #endif
486
487 /* total width and height of controls incl. shadow */
488 #define OSD_W (2*D_RAD + OSD_SHADOW + Z_GPS * 2 * Z_RAD)
489 #if !Z_GPS
490 #define OSD_H (2*D_RAD + Z_STEP + 2*Z_RAD + OSD_SHADOW)
491 #else
492 #define OSD_H (2*Z_RAD + OSD_SHADOW)
493 #endif
494
495 #ifdef OSD_SHADOW_ENABLE
496 #define OSD_LBL_SHADOW (OSD_SHADOW/2)
497 #endif
498
499 #define Z_TOP ((1-Z_GPS) * (2 * D_RAD + Z_STEP))
500
501 #define Z_MID (Z_TOP + Z_RAD)
502 #define Z_BOT (Z_MID + Z_RAD)
503 #define Z_LEFT (Z_RAD)
504 #define Z_RIGHT (2 * D_RAD - Z_RAD + Z_GPS * 2 * Z_RAD)
505 #define Z_CENTER ((Z_RIGHT + Z_LEFT)/2)
506
507 /* create the cairo shape used for the zoom buttons */
508 static void
509 osd_zoom_shape(cairo_t *cr, gint x, gint y)
510 {
511 cairo_move_to (cr, x+Z_LEFT, y+Z_TOP);
512 cairo_line_to (cr, x+Z_RIGHT, y+Z_TOP);
513 cairo_arc (cr, x+Z_RIGHT, y+Z_MID, Z_RAD, -M_PI/2, M_PI/2);
514 cairo_line_to (cr, x+Z_LEFT, y+Z_BOT);
515 cairo_arc (cr, x+Z_LEFT, y+Z_MID, Z_RAD, M_PI/2, -M_PI/2);
516 }
517
518 /* ------------------- color/shadow functions ----------------- */
519
520 #ifndef OSD_COLOR
521 /* if no color has been specified we just use the gdks default colors */
522 static void
523 osd_labels(cairo_t *cr, gint width, gboolean enabled,
524 GdkColor *fg, GdkColor *disabled) {
525 if(enabled) gdk_cairo_set_source_color(cr, fg);
526 else gdk_cairo_set_source_color(cr, disabled);
527 cairo_set_line_width (cr, width);
528 }
529 #else
530 static void
531 osd_labels(cairo_t *cr, gint width, gboolean enabled) {
532 if(enabled) cairo_set_source_rgb (cr, OSD_COLOR);
533 else cairo_set_source_rgb (cr, OSD_COLOR_DISABLED);
534 cairo_set_line_width (cr, width);
535 }
536 #endif
537
538 #ifdef OSD_SHADOW_ENABLE
539 static void
540 osd_labels_shadow(cairo_t *cr, gint width, gboolean enabled) {
541 cairo_set_source_rgba (cr, 0, 0, 0, enabled?0.3:0.15);
542 cairo_set_line_width (cr, width);
543 }
544 #endif
545
546 #ifndef OSD_NO_DPAD
547 /* create the cairo shape used for the dpad */
548 static void
549 osd_dpad_shape(cairo_t *cr, gint x, gint y)
550 {
551 cairo_arc (cr, x+D_RAD, y+D_RAD, D_RAD, 0, 2 * M_PI);
552 }
553 #endif
554
555 #ifdef OSD_SHADOW_ENABLE
556 static void
557 osd_shape_shadow(cairo_t *cr) {
558 cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
559 cairo_fill (cr);
560 cairo_stroke (cr);
561 }
562 #endif
563
564 #ifndef OSD_COLOR
565 /* if no color has been specified we just use the gdks default colors */
566 static void
567 osd_shape(cairo_t *cr, GdkColor *bg, GdkColor *fg) {
568 gdk_cairo_set_source_color(cr, bg);
569 cairo_fill_preserve (cr);
570 gdk_cairo_set_source_color(cr, fg);
571 cairo_set_line_width (cr, 1);
572 cairo_stroke (cr);
573 }
574 #else
575 static void
576 osd_shape(cairo_t *cr) {
577 cairo_set_source_rgba (cr, OSD_COLOR_BG);
578 cairo_fill_preserve (cr);
579 cairo_set_source_rgb (cr, OSD_COLOR);
580 cairo_set_line_width (cr, 1);
581 cairo_stroke (cr);
582 }
583 #endif
584
585
586 static gboolean
587 osm_gps_map_in_circle(gint x, gint y, gint cx, gint cy, gint rad)
588 {
589 return( pow(cx - x, 2) + pow(cy - y, 2) < rad * rad);
590 }
591
592 #ifndef OSD_NO_DPAD
593 /* check whether x/y is within the dpad */
594 static osd_button_t
595 osd_check_dpad(gint x, gint y)
596 {
597 /* within entire dpad circle */
598 if( osm_gps_map_in_circle(x, y, D_RAD, D_RAD, D_RAD))
599 {
600 /* convert into position relative to dpads centre */
601 x -= D_RAD;
602 y -= D_RAD;
603
604 #ifdef OSD_GPS_BUTTON
605 /* check for dpad center goes here! */
606 if( osm_gps_map_in_circle(x, y, 0, 0, D_RAD/3))
607 return OSD_GPS;
608 #endif
609
610 if( y < 0 && abs(x) < abs(y))
611 return OSD_UP;
612
613 if( y > 0 && abs(x) < abs(y))
614 return OSD_DOWN;
615
616 if( x < 0 && abs(y) < abs(x))
617 return OSD_LEFT;
618
619 if( x > 0 && abs(y) < abs(x))
620 return OSD_RIGHT;
621
622 return OSD_BG;
623 }
624 return OSD_NONE;
625 }
626 #endif
627
628 /* check whether x/y is within the zoom pads */
629 static osd_button_t
630 osd_check_zoom(gint x, gint y) {
631 if( x > 0 && x < OSD_W && y > Z_TOP && y < Z_BOT) {
632
633 /* within circle around (-) label */
634 if( osm_gps_map_in_circle(x, y, Z_LEFT, Z_MID, Z_RAD))
635 return OSD_OUT;
636
637 /* within circle around (+) label */
638 if( osm_gps_map_in_circle(x, y, Z_RIGHT, Z_MID, Z_RAD))
639 return OSD_IN;
640
641 #if Z_GPS == 1
642 /* within square around center */
643 if( x > Z_CENTER - Z_RAD && x < Z_CENTER + Z_RAD)
644 return OSD_GPS;
645 #endif
646
647 /* between center of (-) button and center of entire zoom control area */
648 if(x > OSD_LEFT && x < D_RAD)
649 return OSD_OUT;
650
651 /* between center of (+) button and center of entire zoom control area */
652 if(x < OSD_RIGHT && x > D_RAD)
653 return OSD_IN;
654 }
655
656 return OSD_NONE;
657 }
658
659 #ifdef OSD_SOURCE_SEL
660
661 /* place source selection at right border */
662 #define OSD_S_RAD (Z_RAD)
663 #define OSD_S_X (-OSD_X)
664 #define OSD_S_Y (OSD_Y)
665 #define OSD_S_PW (2 * Z_RAD)
666 #define OSD_S_W (OSD_S_PW)
667 #define OSD_S_PH (2 * Z_RAD)
668 #define OSD_S_H (OSD_S_PH + OSD_SHADOW)
669
670 /* size of usable area when expanded */
671 #define OSD_S_AREA_W (priv->source_sel.width)
672 #define OSD_S_AREA_H (priv->source_sel.height)
673 #define OSD_S_EXP_W (OSD_S_PW + OSD_S_AREA_W + OSD_SHADOW)
674 #define OSD_S_EXP_H (OSD_S_AREA_H + OSD_SHADOW)
675
676 /* internal value to draw the arrow on the "puller" */
677 #define OSD_S_D0 (OSD_S_RAD/2)
678 #ifndef OSD_FONT_SIZE
679 #define OSD_FONT_SIZE (16.0)
680 #endif
681 #define OSD_TEXT_BORDER (OSD_FONT_SIZE/2)
682 #define OSD_TEXT_SKIP (OSD_FONT_SIZE/8)
683
684 /* draw the shape of the source selection OSD, either only the puller (not expanded) */
685 /* or the entire menu incl. the puller (expanded) */
686 static void
687 osd_source_shape(osd_priv_t *priv, cairo_t *cr, gint x, gint y) {
688 if(!priv->source_sel.expanded) {
689 /* just draw the puller */
690 cairo_move_to (cr, x + OSD_S_PW, y + OSD_S_PH);
691 cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2);
692 cairo_line_to (cr, x + OSD_S_PW, y);
693 } else {
694 /* draw the puller and the area itself */
695 cairo_move_to (cr, x + OSD_S_PW + OSD_S_AREA_W, y + OSD_S_AREA_H);
696 cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_AREA_H);
697 if(OSD_S_Y > 0) {
698 cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_PH);
699 cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2);
700 } else {
701 cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_AREA_H-OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2);
702 cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_AREA_H - OSD_S_PH);
703 cairo_line_to (cr, x + OSD_S_PW, y);
704 }
705 cairo_line_to (cr, x + OSD_S_PW + OSD_S_AREA_W, y);
706 cairo_close_path (cr);
707 }
708 }
709
710 static void
711 osd_source_content(osm_gps_map_osd_t *osd, cairo_t *cr, gint offset) {
712 osd_priv_t *priv = (osd_priv_t*)osd->priv;
713
714 int py = offset + OSD_S_RAD - OSD_S_D0;
715
716 if(!priv->source_sel.expanded) {
717 /* draw the "puller" open (<) arrow */
718 cairo_move_to (cr, offset + OSD_S_RAD + OSD_S_D0/2, py);
719 cairo_rel_line_to (cr, -OSD_S_D0, +OSD_S_D0);
720 cairo_rel_line_to (cr, +OSD_S_D0, +OSD_S_D0);
721 } else {
722 if(OSD_S_Y < 0)
723 py += OSD_S_AREA_H - OSD_S_PH;
724
725 /* draw the "puller" close (>) arrow */
726 cairo_move_to (cr, offset + OSD_S_RAD - OSD_S_D0/2, py);
727 cairo_rel_line_to (cr, +OSD_S_D0, +OSD_S_D0);
728 cairo_rel_line_to (cr, -OSD_S_D0, +OSD_S_D0);
729 cairo_stroke(cr);
730
731 /* don't draw a shadow for the text content */
732 if(offset == 1) {
733 gint source;
734 g_object_get(osd->widget, "map-source", &source, NULL);
735
736 cairo_select_font_face (cr, "Sans",
737 CAIRO_FONT_SLANT_NORMAL,
738 CAIRO_FONT_WEIGHT_BOLD);
739 cairo_set_font_size (cr, OSD_FONT_SIZE);
740
741 int i, step = (priv->source_sel.height - 2*OSD_TEXT_BORDER - OSD_DPIX_SKIP) /
742 (num_map_sources + OSD_DPIX_EXTRA);
743 for(i=0;i<num_map_sources + OSD_DPIX_EXTRA;i++) {
744 cairo_text_extents_t extents;
745 const char *src = NULL;
746 #if OSD_DPIX_EXTRA > 0
747 /* draw "double pixel" check button */
748 const char *dpix = _("Double Pixel");
749 if(i == num_map_sources) {
750 src = dpix;
751 } else
752 #endif
753 src = osm_gps_map_source_get_friendly_name( map_sources[i] );
754
755 cairo_text_extents (cr, src, &extents);
756
757 int x = offset + OSD_S_PW + OSD_TEXT_BORDER;
758 int y = offset + step * i + OSD_TEXT_BORDER;
759
760 #if OSD_DPIX_EXTRA > 0
761 if(i == num_map_sources) {
762 int skip3 = OSD_DPIX_SKIP/3;
763
764 gboolean dpix;
765 g_object_get(osd->widget, "double-pixel", &dpix, NULL);
766
767 cairo_set_line_width (cr, skip3);
768
769 /* draw seperator line */
770 cairo_move_to (cr, x, y + skip3);
771 cairo_rel_line_to (cr, OSD_S_AREA_W - 2*OSD_TEXT_BORDER, 0);
772 cairo_stroke(cr);
773
774 y += OSD_DPIX_SKIP;
775
776 /* draw check box */
777 cairo_move_to (cr, x, y);
778 cairo_rel_line_to (cr, OSD_FONT_SIZE, 0);
779 cairo_rel_line_to (cr, 0, OSD_FONT_SIZE);
780 cairo_rel_line_to (cr, -OSD_FONT_SIZE, 0);
781 cairo_rel_line_to (cr, 0, -OSD_FONT_SIZE);
782 cairo_stroke(cr);
783
784 /* draw check if needed */
785 if(dpix) {
786 int space = OSD_FONT_SIZE/4;
787 cairo_move_to (cr, x + space, y + space);
788 cairo_rel_line_to (cr, OSD_FONT_SIZE - 2*space, OSD_FONT_SIZE - 2*space);
789 cairo_stroke(cr);
790
791 cairo_move_to (cr, x + OSD_FONT_SIZE - space, y + space);
792 cairo_rel_line_to (cr, -(OSD_FONT_SIZE - 2*space), OSD_FONT_SIZE - 2*space);
793 cairo_stroke(cr);
794 }
795
796 x += 1.5 * OSD_FONT_SIZE;
797 }
798 #endif
799
800 /* draw filled rectangle if selected */
801 if(source == map_sources[i]) {
802 cairo_rectangle(cr, x - OSD_TEXT_BORDER/2,
803 y - OSD_TEXT_SKIP,
804 priv->source_sel.width - OSD_TEXT_BORDER,
805 step + OSD_TEXT_SKIP);
806 cairo_fill(cr);
807
808 /* temprarily draw with background color */
809 #ifndef OSD_COLOR
810 GdkColor bg = osd->widget->style->bg[GTK_STATE_NORMAL];
811 gdk_cairo_set_source_color(cr, &bg);
812 #else
813 cairo_set_source_rgba (cr, OSD_COLOR_BG);
814 #endif
815 }
816
817 cairo_move_to (cr, x, y + OSD_TEXT_SKIP - extents.y_bearing);
818 cairo_show_text (cr, src);
819
820 /* restore color */
821 if(source == map_sources[i]) {
822 #ifndef OSD_COLOR
823 GdkColor fg = osd->widget->style->fg[GTK_STATE_NORMAL];
824 gdk_cairo_set_source_color(cr, &fg);
825 #else
826 cairo_set_source_rgb (cr, OSD_COLOR);
827 #endif
828 }
829 }
830 }
831 }
832 }
833
834 static void
835 osd_render_source_sel(osm_gps_map_osd_t *osd, gboolean force_rerender) {
836 osd_priv_t *priv = (osd_priv_t*)osd->priv;
837
838 if(!priv->source_sel.surface ||
839 (priv->source_sel.rendered && !force_rerender))
840 return;
841
842 priv->source_sel.rendered = TRUE;
843
844 #ifndef OSD_COLOR
845 GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL];
846 GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL];
847 GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE];
848 #endif
849
850 /* draw source selector */
851 g_assert(priv->source_sel.surface);
852 cairo_t *cr = cairo_create(priv->source_sel.surface);
853 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
854 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
855 cairo_paint(cr);
856 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
857
858 #ifdef OSD_SHADOW_ENABLE
859 osd_source_shape(priv, cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
860 osd_shape_shadow(cr);
861 #endif
862
863 osd_source_shape(priv, cr, 1, 1);
864 #ifndef OSD_COLOR
865 osd_shape(cr, &bg, &fg);
866 #else
867 osd_shape(cr);
868 #endif
869
870 #ifdef OSD_SHADOW_ENABLE
871 osd_labels_shadow(cr, Z_RAD/3, TRUE);
872 osd_source_content(osd, cr, 1+OSD_LBL_SHADOW);
873 cairo_stroke (cr);
874 #endif
875 #ifndef OSD_COLOR
876 osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
877 #else
878 osd_labels(cr, Z_RAD/3, TRUE);
879 #endif
880 osd_source_content(osd, cr, 1);
881 cairo_stroke (cr);
882
883 cairo_destroy(cr);
884 }
885
886 /* re-allocate the buffer used to draw the menu. This is used */
887 /* to collapse/expand the buffer */
888 static void
889 osd_source_reallocate(osm_gps_map_osd_t *osd) {
890 osd_priv_t *priv = (osd_priv_t*)osd->priv;
891
892 /* re-allocate offscreen bitmap */
893 g_assert (priv->source_sel.surface);
894
895 int w = OSD_S_W, h = OSD_S_H;
896 if(priv->source_sel.expanded) {
897 cairo_text_extents_t extents;
898
899 /* determine content size */
900 g_assert(priv->source_sel.surface);
901 cairo_t *cr = cairo_create(priv->source_sel.surface);
902 cairo_select_font_face (cr, "Sans",
903 CAIRO_FONT_SLANT_NORMAL,
904 CAIRO_FONT_WEIGHT_BOLD);
905 cairo_set_font_size (cr, OSD_FONT_SIZE);
906
907 /* calculate menu size */
908 int i, max_w = 0;
909 priv->source_sel.max_h = 0;
910 for(i=0;i<num_map_sources;i++) {
911 const char *src = osm_gps_map_source_get_friendly_name( map_sources[i] );
912 cairo_text_extents (cr, src, &extents);
913
914 if(extents.width > max_w) max_w = extents.width;
915 if(extents.height > priv->source_sel.max_h) priv->source_sel.max_h = extents.height;
916 }
917 cairo_destroy(cr);
918
919 priv->source_sel.width = max_w + 2*OSD_TEXT_BORDER;
920 priv->source_sel.height = (num_map_sources + OSD_DPIX_EXTRA) *
921 (priv->source_sel.max_h + 2*OSD_TEXT_SKIP) + 2*OSD_TEXT_BORDER + OSD_DPIX_SKIP;
922
923 w = OSD_S_EXP_W;
924 h = OSD_S_EXP_H;
925 }
926
927 cairo_surface_destroy(priv->source_sel.surface);
928 priv->source_sel.surface =
929 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w+2, h+2);
930
931 osd_render_source_sel(osd, TRUE);
932 }
933
934 #define OSD_HZ 15
935 #define OSD_TIME 500
936
937 static gboolean osd_source_animate(gpointer data) {
938 osm_gps_map_osd_t *osd = (osm_gps_map_osd_t*)data;
939 osd_priv_t *priv = (osd_priv_t*)osd->priv;
940 int diff = OSD_S_EXP_W - OSD_S_W - OSD_S_X;
941 gboolean done = FALSE;
942 priv->source_sel.count += priv->source_sel.dir;
943
944 /* shifting in */
945 if(priv->source_sel.dir < 0) {
946 if(priv->source_sel.count <= 0) {
947 priv->source_sel.count = 0;
948 done = TRUE;
949 }
950 } else {
951 if(priv->source_sel.count >= 1000) {
952 priv->source_sel.expanded = FALSE;
953 osd_source_reallocate(osd);
954
955 priv->source_sel.count = 1000;
956 done = TRUE;
957 }
958 }
959
960
961 /* count runs linearly from 0 to 1000, map this nicely onto a position */
962
963 /* nice sinoid mapping */
964 float m = 0.5-cos(priv->source_sel.count * M_PI / 1000.0)/2;
965 priv->source_sel.shift =
966 (osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X) +
967 m * diff;
968
969 /* make sure the screen is updated */
970 osm_gps_map_repaint(OSM_GPS_MAP(osd->widget));
971
972 /* stop animation if done */
973 if(done)
974 priv->source_sel.handler_id = 0;
975
976 return !done;
977 }
978
979 /* switch between expand and collapse mode of source selection */
980 static void
981 osd_source_toggle(osm_gps_map_osd_t *osd)
982 {
983 osd_priv_t *priv = (osd_priv_t*)osd->priv;
984
985 /* ignore clicks while animation is running */
986 if(priv->source_sel.handler_id)
987 return;
988
989 /* expand immediately, collapse is handle at the end of the */
990 /* collapse animation */
991 if(!priv->source_sel.expanded) {
992 priv->source_sel.expanded = TRUE;
993 osd_source_reallocate(osd);
994
995 priv->source_sel.count = 1000;
996 priv->source_sel.shift = osd->widget->allocation.width - OSD_S_W;
997 priv->source_sel.dir = -1000/OSD_HZ;
998 } else {
999 priv->source_sel.count = 0;
1000 priv->source_sel.shift = osd->widget->allocation.width -
1001 OSD_S_EXP_W + OSD_S_X;
1002 priv->source_sel.dir = +1000/OSD_HZ;
1003 }
1004
1005 /* start timer to handle animation */
1006 priv->source_sel.handler_id = gtk_timeout_add(OSD_TIME/OSD_HZ,
1007 osd_source_animate, osd);
1008 }
1009
1010 /* check if the user clicked inside the source selection area */
1011 static osd_button_t
1012 osd_source_check(osm_gps_map_osd_t *osd, gint state, gint x, gint y) {
1013 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1014
1015 if(!priv->source_sel.expanded)
1016 x -= osd->widget->allocation.width - OSD_S_W;
1017 else
1018 x -= osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
1019
1020 if(OSD_S_Y > 0)
1021 y -= OSD_S_Y;
1022 else
1023 y -= osd->widget->allocation.height - OSD_S_PH + OSD_S_Y;
1024
1025 /* within square around puller? */
1026 if(y > 0 && y < OSD_S_PH && x > 0 && x < OSD_S_PW) {
1027 /* really within puller shape? */
1028 if(x > Z_RAD || osm_gps_map_in_circle(x, y, Z_RAD, Z_RAD, Z_RAD)) {
1029 /* expand source selector */
1030 if(state == OSD_STATE_DOWN)
1031 osd_source_toggle(osd);
1032
1033 /* tell upper layers that user clicked some background element */
1034 /* of the OSD */
1035 return OSD_BG;
1036 }
1037 }
1038
1039 /* check for clicks into data area */
1040 if(priv->source_sel.expanded && !priv->source_sel.handler_id) {
1041 /* re-adjust from puller top to content top */
1042 if(OSD_S_Y < 0)
1043 y += OSD_S_EXP_H - OSD_S_PH;
1044
1045 if(x > OSD_S_PW &&
1046 x < OSD_S_PW + OSD_S_EXP_W &&
1047 y > 0 &&
1048 y < OSD_S_EXP_H) {
1049
1050 int step = (priv->source_sel.height - 2*OSD_TEXT_BORDER - OSD_DPIX_SKIP)
1051 / (num_map_sources + OSD_DPIX_EXTRA);
1052
1053 y -= OSD_TEXT_BORDER - OSD_TEXT_SKIP;
1054 int py = y / step;
1055
1056 if(state == OSD_STATE_DOWN) {
1057 gint old = 0;
1058 g_object_get(osd->widget, "map-source", &old, NULL);
1059
1060 if(py >= 0 &&
1061 py < num_map_sources &&
1062 old != map_sources[py]) {
1063 g_object_set(osd->widget, "map-source", map_sources[py], NULL);
1064
1065 osd_render_source_sel(osd, TRUE);
1066 osm_gps_map_repaint(OSM_GPS_MAP(osd->widget));
1067 osm_gps_map_redraw(OSM_GPS_MAP(osd->widget));
1068 }
1069
1070 #if OSD_DPIX_EXTRA > 0
1071 if(py >= num_map_sources) {
1072 y -= num_map_sources * (priv->source_sel.max_h + 2*OSD_TEXT_SKIP ) +
1073 OSD_TEXT_SKIP + OSD_DPIX_SKIP;
1074 if(y >= 0) {
1075 gboolean dpix = 0;
1076 g_object_get(osd->widget, "double-pixel", &dpix, NULL);
1077 g_object_set(osd->widget, "double-pixel", !dpix, NULL);
1078 osd_render_source_sel(osd, TRUE);
1079 osm_gps_map_repaint(OSM_GPS_MAP(osd->widget));
1080 osm_gps_map_redraw(OSM_GPS_MAP(osd->widget));
1081 }
1082 }
1083 #endif
1084 }
1085
1086 /* return "clicked in OSD background" to prevent further */
1087 /* processing by application */
1088 return OSD_BG;
1089 }
1090 }
1091
1092 return OSD_NONE;
1093 }
1094 #endif // OSD_SOURCE_SEL
1095
1096 #ifndef OSD_NO_DPAD
1097 static void
1098 osd_dpad_labels(cairo_t *cr, gint x, gint y) {
1099 /* move reference to dpad center */
1100 x += D_RAD;
1101 y += D_RAD;
1102
1103 const static gint offset[][3][2] = {
1104 /* left arrow/triangle */
1105 { { -D_TIP+D_LEN, -D_WID }, { -D_LEN, D_WID }, { +D_LEN, D_WID } },
1106 /* right arrow/triangle */
1107 { { +D_TIP-D_LEN, -D_WID }, { +D_LEN, D_WID }, { -D_LEN, D_WID } },
1108 /* top arrow/triangle */
1109 { { -D_WID, -D_TIP+D_LEN }, { D_WID, -D_LEN }, { D_WID, +D_LEN } },
1110 /* bottom arrow/triangle */
1111 { { -D_WID, +D_TIP-D_LEN }, { D_WID, +D_LEN }, { D_WID, -D_LEN } }
1112 };
1113
1114 int i;
1115 for(i=0;i<4;i++) {
1116 cairo_move_to (cr, x + offset[i][0][0], y + offset[i][0][1]);
1117 cairo_rel_line_to (cr, offset[i][1][0], offset[i][1][1]);
1118 cairo_rel_line_to (cr, offset[i][2][0], offset[i][2][1]);
1119 }
1120 }
1121 #endif
1122
1123 #ifdef OSD_GPS_BUTTON
1124 /* draw the satellite dish icon in the center of the dpad */
1125 #define GPS_V0 (D_RAD/7)
1126 #define GPS_V1 (D_RAD/10)
1127 #define GPS_V2 (D_RAD/5)
1128
1129 /* draw a satellite receiver dish */
1130 /* this is either drawn in the center of the dpad (if present) */
1131 /* or in the middle of the zoom area */
1132 static void
1133 osd_dpad_gps(cairo_t *cr, gint x, gint y) {
1134 /* move reference to dpad center */
1135 x += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD * 3;
1136 y += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD + GPS_V0;
1137
1138 cairo_move_to (cr, x-GPS_V0, y+GPS_V0);
1139 cairo_rel_line_to (cr, +GPS_V0, -GPS_V0);
1140 cairo_rel_line_to (cr, +GPS_V0, +GPS_V0);
1141 cairo_close_path (cr);
1142
1143 cairo_move_to (cr, x+GPS_V1-GPS_V2, y-2*GPS_V2);
1144 cairo_curve_to (cr, x-GPS_V2, y, x+GPS_V1, y+GPS_V1, x+GPS_V1+GPS_V2, y);
1145 cairo_close_path (cr);
1146
1147 x += GPS_V1;
1148 cairo_move_to (cr, x, y-GPS_V2);
1149 cairo_rel_line_to (cr, +GPS_V1, -GPS_V1);
1150 }
1151 #endif
1152
1153 #define Z_LEN (2*Z_RAD/3)
1154
1155 static void
1156 osd_zoom_labels(cairo_t *cr, gint x, gint y) {
1157 cairo_move_to (cr, x + Z_LEFT - Z_LEN, y + Z_MID);
1158 cairo_line_to (cr, x + Z_LEFT + Z_LEN, y + Z_MID);
1159
1160 cairo_move_to (cr, x + Z_RIGHT, y + Z_MID - Z_LEN);
1161 cairo_line_to (cr, x + Z_RIGHT, y + Z_MID + Z_LEN);
1162 cairo_move_to (cr, x + Z_RIGHT - Z_LEN, y + Z_MID);
1163 cairo_line_to (cr, x + Z_RIGHT + Z_LEN, y + Z_MID);
1164 }
1165
1166 #ifdef OSD_COORDINATES
1167
1168 #ifndef OSD_COORDINATES_FONT_SIZE
1169 #define OSD_COORDINATES_FONT_SIZE (12.0)
1170 #endif
1171
1172 #define OSD_COORDINATES_OFFSET (OSD_COORDINATES_FONT_SIZE/6)
1173
1174 #define OSD_COORDINATES_W (8*OSD_COORDINATES_FONT_SIZE+2*OSD_COORDINATES_OFFSET)
1175 #define OSD_COORDINATES_H (2*OSD_COORDINATES_FONT_SIZE+2*OSD_COORDINATES_OFFSET+OSD_COORDINATES_FONT_SIZE/4)
1176
1177 /* these can be overwritten with versions that support */
1178 /* localization */
1179 #ifndef OSD_COORDINATES_CHR_N
1180 #define OSD_COORDINATES_CHR_N "N"
1181 #endif
1182 #ifndef OSD_COORDINATES_CHR_S
1183 #define OSD_COORDINATES_CHR_S "S"
1184 #endif
1185 #ifndef OSD_COORDINATES_CHR_E
1186 #define OSD_COORDINATES_CHR_E "E"
1187 #endif
1188 #ifndef OSD_COORDINATES_CHR_W
1189 #define OSD_COORDINATES_CHR_W "W"
1190 #endif
1191
1192
1193
1194 /* this is the classic geocaching notation */
1195 static char
1196 *osd_latitude_str(float latitude) {
1197 char *c = OSD_COORDINATES_CHR_N;
1198 float integral, fractional;
1199
1200 if(isnan(latitude))
1201 return NULL;
1202
1203 if(latitude < 0) {
1204 latitude = fabs(latitude);
1205 c = OSD_COORDINATES_CHR_S;
1206 }
1207
1208 fractional = modff(latitude, &integral);
1209
1210 return g_strdup_printf("%s %02d° %06.3f'",
1211 c, (int)integral, fractional*60.0);
1212 }
1213
1214 static char
1215 *osd_longitude_str(float longitude) {
1216 char *c = OSD_COORDINATES_CHR_E;
1217 float integral, fractional;
1218
1219 if(isnan(longitude))
1220 return NULL;
1221
1222 if(longitude < 0) {
1223 longitude = fabs(longitude);
1224 c = OSD_COORDINATES_CHR_W;
1225 }
1226
1227 fractional = modff(longitude, &integral);
1228
1229 return g_strdup_printf("%s %03d° %06.3f'",
1230 c, (int)integral, fractional*60.0);
1231 }
1232
1233 /* render a string at the given screen position */
1234 static int
1235 osd_render_centered_text(cairo_t *cr, int y, int width, char *text) {
1236 if(!text) return y;
1237
1238 char *p = g_malloc(strlen(text)+4); // space for "...\n"
1239 strcpy(p, text);
1240
1241 cairo_text_extents_t extents;
1242 memset(&extents, 0, sizeof(cairo_text_extents_t));
1243 cairo_text_extents (cr, p, &extents);
1244 g_assert(extents.width != 0.0);
1245
1246 /* check if text needs to be truncated */
1247 int trunc_at = strlen(text);
1248 while(extents.width > width) {
1249
1250 /* cut off all utf8 multibyte remains so the actual */
1251 /* truncation only deals with one byte */
1252 while((p[trunc_at-1] & 0xc0) == 0x80) {
1253 trunc_at--;
1254 g_assert(trunc_at > 0);
1255 }
1256
1257 trunc_at--;
1258 g_assert(trunc_at > 0);
1259
1260 strcpy(p+trunc_at, "...");
1261 cairo_text_extents (cr, p, &extents);
1262 }
1263
1264 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1265 cairo_set_line_width (cr, OSD_COORDINATES_FONT_SIZE/6);
1266 cairo_move_to (cr, (width - extents.width)/2, y - extents.y_bearing);
1267 cairo_text_path (cr, p);
1268 cairo_stroke (cr);
1269
1270 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
1271 cairo_move_to (cr, (width - extents.width)/2, y - extents.y_bearing);
1272 cairo_show_text (cr, p);
1273
1274 g_free(p);
1275
1276 /* skip + 1/5 line */
1277 return y + 6*OSD_COORDINATES_FONT_SIZE/5;
1278 }
1279
1280 static void
1281 osd_render_coordinates(osm_gps_map_osd_t *osd)
1282 {
1283 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1284
1285 if(!priv->coordinates.surface)
1286 return;
1287
1288 /* get current map position */
1289 gfloat lat, lon;
1290 g_object_get(osd->widget, "latitude", &lat, "longitude", &lon, NULL);
1291
1292 /* check if position has changed enough to require redraw */
1293 if(!isnan(priv->coordinates.lat) && !isnan(priv->coordinates.lon))
1294 /* 1/60000 == 1/1000 minute */
1295 if((fabsf(lat - priv->coordinates.lat) < 1/60000) &&
1296 (fabsf(lon - priv->coordinates.lon) < 1/60000))
1297 return;
1298
1299 priv->coordinates.lat = lat;
1300 priv->coordinates.lon = lon;
1301
1302 /* first fill with transparency */
1303
1304 g_assert(priv->coordinates.surface);
1305 cairo_t *cr = cairo_create(priv->coordinates.surface);
1306 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1307 // cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.5);
1308 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
1309 cairo_paint(cr);
1310 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1311
1312 cairo_select_font_face (cr, "Sans",
1313 CAIRO_FONT_SLANT_NORMAL,
1314 CAIRO_FONT_WEIGHT_BOLD);
1315 cairo_set_font_size (cr, OSD_COORDINATES_FONT_SIZE);
1316
1317 char *latitude = osd_latitude_str(lat);
1318 char *longitude = osd_longitude_str(lon);
1319
1320 int y = OSD_COORDINATES_OFFSET;
1321 y = osd_render_centered_text(cr, y, OSD_COORDINATES_W, latitude);
1322 y = osd_render_centered_text(cr, y, OSD_COORDINATES_W, longitude);
1323
1324 g_free(latitude);
1325 g_free(longitude);
1326
1327 cairo_destroy(cr);
1328 }
1329 #endif // OSD_COORDINATES
1330
1331 #ifdef OSD_NAV
1332 #define OSD_NAV_W (8*OSD_COORDINATES_FONT_SIZE+2*OSD_COORDINATES_OFFSET)
1333 #define OSD_NAV_H (11*OSD_COORDINATES_FONT_SIZE)
1334
1335 /* http://mathforum.org/library/drmath/view/55417.html */
1336 static float get_bearing(float lat1, float lon1, float lat2, float lon2) {
1337 return atan2( sin(lon2 - lon1) * cos(lat2),
1338 cos(lat1) * sin(lat2) -
1339 sin(lat1) * cos(lat2) * cos(lon2 - lon1));
1340 }
1341
1342 /* http://mathforum.org/library/drmath/view/51722.html */
1343 static float get_distance(float lat1, float lon1, float lat2, float lon2) {
1344 float aob = acos(cos(lat1) * cos(lat2) * cos(lon2 - lon1) +
1345 sin(lat1) * sin(lat2));
1346
1347 return(aob * 6371000.0); /* great circle radius in meters */
1348 }
1349
1350 static void
1351 osd_render_nav(osm_gps_map_osd_t *osd)
1352 {
1353 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1354
1355 if(!priv->nav.surface || isnan(priv->nav.lat) || isnan(priv->nav.lon))
1356 return;
1357
1358 /* first fill with transparency */
1359 g_assert(priv->nav.surface);
1360 cairo_t *cr = cairo_create(priv->nav.surface);
1361 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1362 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
1363 cairo_paint(cr);
1364 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1365
1366 cairo_select_font_face (cr, "Sans",
1367 CAIRO_FONT_SLANT_NORMAL,
1368 CAIRO_FONT_WEIGHT_BOLD);
1369 cairo_set_font_size (cr, OSD_COORDINATES_FONT_SIZE);
1370
1371 char *latitude = osd_latitude_str(priv->nav.lat);
1372 char *longitude = osd_longitude_str(priv->nav.lon);
1373
1374 int y = OSD_COORDINATES_OFFSET;
1375 y = osd_render_centered_text(cr, y, OSD_NAV_W, priv->nav.name);
1376 y = osd_render_centered_text(cr, y, OSD_NAV_W, latitude);
1377 y = osd_render_centered_text(cr, y, OSD_NAV_W, longitude);
1378
1379 g_free(latitude);
1380 g_free(longitude);
1381
1382 /* everything below this is the compass. we need to know this */
1383 /* to differ between coordinate clicks and compass clicks */
1384 priv->nav.click_sep = y;
1385
1386 /* draw the compass */
1387 int radius = (OSD_NAV_H - y - 5*OSD_COORDINATES_FONT_SIZE/4)/2;
1388 if(radius > OSD_NAV_W/2)
1389 radius = OSD_NAV_W/2;
1390
1391 int x = OSD_NAV_W/2+1;
1392 y += radius;
1393
1394 cairo_stroke (cr);
1395
1396 /* draw background */
1397 cairo_arc(cr, x, y, radius, 0, 2*M_PI);
1398 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
1399 cairo_fill_preserve (cr);
1400 cairo_set_source_rgb (cr, 0, 0, 0);
1401 cairo_set_line_width (cr, 1);
1402 cairo_stroke (cr);
1403
1404 /* draw pointer */
1405 #define ARROW_WIDTH 0.3
1406 #define ARROW_LENGTH 0.7
1407
1408 coord_t mpos, *pos = osm_gps_map_get_gps (OSM_GPS_MAP(osd->widget));
1409 if(priv->nav.mode) {
1410 gfloat lat, lon;
1411 g_object_get(osd->widget, "latitude", &lat, "longitude", &lon, NULL);
1412 mpos.rlat = deg2rad(lat);
1413 mpos.rlon = deg2rad(lon);
1414 pos = &mpos;
1415 }
1416
1417 if(pos) {
1418 float arot = get_bearing(pos->rlat, pos->rlon,
1419 deg2rad(priv->nav.lat), deg2rad(priv->nav.lon));
1420
1421 cairo_move_to(cr,
1422 x + radius * ARROW_LENGTH * sin(arot),
1423 y + radius * ARROW_LENGTH * -cos(arot));
1424
1425 cairo_line_to(cr,
1426 x + radius * -ARROW_LENGTH * sin(arot+ARROW_WIDTH),
1427 y + radius * -ARROW_LENGTH * -cos(arot+ARROW_WIDTH));
1428
1429 cairo_line_to(cr,
1430 x + radius * -0.5 * ARROW_LENGTH * sin(arot),
1431 y + radius * -0.5 * ARROW_LENGTH * -cos(arot));
1432
1433 cairo_line_to(cr,
1434 x + radius * -ARROW_LENGTH * sin(arot-ARROW_WIDTH),
1435 y + radius * -ARROW_LENGTH * -cos(arot-ARROW_WIDTH));
1436
1437 cairo_close_path(cr);
1438 if(priv->nav.mode)
1439 cairo_set_source_rgb (cr, 0, 0, 0);
1440 else
1441 cairo_set_source_rgb (cr, 0, 0, 0.8);
1442
1443 cairo_fill (cr);
1444
1445 y += radius + OSD_COORDINATES_FONT_SIZE/4;
1446
1447 float dist = get_distance(pos->rlat, pos->rlon,
1448 deg2rad(priv->nav.lat), deg2rad(priv->nav.lon));
1449
1450 char *dist_str = NULL;
1451 if(!priv->nav.imperial) {
1452 /* metric is easy ... */
1453 if(dist<1000)
1454 dist_str = g_strdup_printf("%u m", (int)dist);
1455 else
1456 dist_str = g_strdup_printf("%.1f km", dist/1000);
1457 } else {
1458 /* and now the hard part: scale for useful imperial values :-( */
1459 /* try to convert to feet, 1ft == 0.3048 m */
1460
1461 if(dist/(3*0.3048) >= 1760.0) /* more than 1760 yard? */
1462 dist_str = g_strdup_printf("%.1f mi", dist/(0.3048*3*1760.0));
1463 else if(dist/0.3048 >= 100) /* more than 100 feet? */
1464 dist_str = g_strdup_printf("%.1f yd", dist/(0.3048*3));
1465 else
1466 dist_str = g_strdup_printf("%.0f ft", dist/0.3048);
1467 }
1468
1469 y = osd_render_centered_text(cr, y, OSD_NAV_W, dist_str);
1470 g_free(dist_str);
1471 }
1472
1473 cairo_destroy(cr);
1474 }
1475
1476 /* check if the user clicked inside the source selection area */
1477 static osd_button_t
1478 osd_nav_check(osm_gps_map_osd_t *osd, gint state, gint x, gint y) {
1479 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1480
1481 if(!priv->nav.surface || state == OSD_STATE_DOWN)
1482 return OSD_NONE;
1483
1484 x -= OSD_X;
1485 if(OSD_X < 0)
1486 x -= (osd->widget->allocation.width - OSD_NAV_W);
1487
1488 y -= (osd->widget->allocation.height - OSD_NAV_H)/2;
1489
1490 if(x >= 0 && y >= 0 && x <= OSD_NAV_W && y <= OSD_NAV_H) {
1491 if(y < priv->nav.click_sep)
1492 osm_gps_map_set_center(OSM_GPS_MAP(osd->widget),
1493 priv->nav.lat, priv->nav.lon);
1494 else {
1495 priv->nav.mode = !priv->nav.mode;
1496 osm_gps_map_redraw(OSM_GPS_MAP(osd->widget));
1497 }
1498 }
1499
1500 return OSD_NONE;
1501 }
1502
1503 void osm_gps_map_osd_clear_nav (OsmGpsMap *map) {
1504 g_return_if_fail (OSM_IS_GPS_MAP (map));
1505
1506 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1507 g_return_if_fail (osd);
1508
1509 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1510 g_return_if_fail (priv);
1511
1512 if(priv->nav.surface) {
1513 cairo_surface_destroy(priv->nav.surface);
1514 priv->nav.surface = NULL;
1515 priv->nav.lat = OSM_GPS_MAP_INVALID;
1516 priv->nav.lon = OSM_GPS_MAP_INVALID;
1517 if(priv->nav.name) g_free(priv->nav.name);
1518 }
1519 osm_gps_map_redraw(map);
1520 }
1521
1522 void
1523 osm_gps_map_osd_draw_nav (OsmGpsMap *map, gboolean imperial,
1524 float latitude, float longitude, char *name) {
1525 g_return_if_fail (OSM_IS_GPS_MAP (map));
1526
1527 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1528 g_return_if_fail (osd);
1529
1530 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1531 g_return_if_fail (priv);
1532
1533 osm_gps_map_osd_clear_nav (map);
1534
1535 /* allocate balloon surface */
1536 priv->nav.surface =
1537 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1538 OSD_NAV_W+2, OSD_NAV_H+2);
1539
1540 priv->nav.lat = latitude;
1541 priv->nav.lon = longitude;
1542 priv->nav.name = g_strdup(name);
1543 priv->nav.imperial = imperial;
1544
1545 osd_render_nav(osd);
1546
1547 osm_gps_map_redraw(map);
1548 }
1549
1550 #endif // OSD_NAV
1551
1552 #ifdef OSD_HEARTRATE
1553 #ifndef OSD_HR_FONT_SIZE
1554 #define OSD_HR_FONT_SIZE 60
1555 #endif
1556 #define OSD_HR_W 3*OSD_HR_FONT_SIZE
1557 #define OSD_HR_H OSD_HR_FONT_SIZE
1558
1559 #ifndef OSD_HR_Y
1560 #define OSD_HR_Y OSD_Y
1561 #endif
1562
1563 static void
1564 osd_render_heart_shape(cairo_t *cr, gint x, gint y, gint s) {
1565 cairo_move_to (cr, x-s, y-s/4);
1566 cairo_curve_to (cr, x-s, y-s, x, y-s, x, y-s/4);
1567 cairo_curve_to (cr, x, y-s, x+s, y-s, x+s, y-s/4);
1568 cairo_curve_to (cr, x+s, y+s/2, x, y+3*s/4, x, y+s);
1569 cairo_curve_to (cr, x, y+3*s/4, x-s, y+s/2, x-s, y-s/4);
1570 }
1571
1572 static void
1573 osd_render_heart(cairo_t *cr, gint x, gint y, gint s, gboolean ok) {
1574 /* xyz */
1575
1576 cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
1577
1578 osd_render_heart_shape(cr, x, y, s);
1579 cairo_set_source_rgb (cr, 1, 1, 1);
1580 cairo_set_line_width (cr, s/2);
1581 cairo_stroke(cr);
1582
1583 osd_render_heart_shape(cr, x, y, s);
1584 if(ok) cairo_set_source_rgb (cr, 1, 0, 0);
1585 else cairo_set_source_rgb (cr, 0.8, 0.8, 0.8);
1586 cairo_fill_preserve (cr);
1587 cairo_set_line_width (cr, s/5);
1588
1589 cairo_stroke(cr);
1590 }
1591
1592 static void
1593 osd_render_hr(osm_gps_map_osd_t *osd)
1594 {
1595 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1596
1597 if(!priv->hr.surface)
1598 return;
1599
1600 /* first fill with transparency */
1601 g_assert(priv->hr.surface);
1602 cairo_t *cr = cairo_create(priv->hr.surface);
1603 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1604 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
1605 cairo_paint(cr);
1606 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1607
1608 cairo_select_font_face (cr, "Sans",
1609 CAIRO_FONT_SLANT_NORMAL,
1610 CAIRO_FONT_WEIGHT_BOLD);
1611 cairo_set_font_size (cr, OSD_HR_FONT_SIZE);
1612
1613 char str[5];
1614 if(priv->hr.rate > 0)
1615 snprintf(str, 4, "%03u", priv->hr.rate);
1616 else if(priv->hr.rate == OSD_HR_INVALID)
1617 strcpy(str, "?");
1618 else if(priv->hr.rate == OSD_HR_ERROR)
1619 strcpy(str, "Err");
1620 else
1621 g_assert(priv->hr.rate != OSD_HR_NONE);
1622
1623 cairo_text_extents_t extents;
1624 cairo_text_extents (cr, str, &extents);
1625
1626 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1627 cairo_set_line_width (cr, OSD_HR_FONT_SIZE/10);
1628
1629 cairo_move_to (cr, OSD_HR_W/2 - extents.width/2 + OSD_HR_FONT_SIZE/4,
1630 OSD_HR_H/2 - extents.y_bearing/2);
1631 cairo_text_path (cr, str);
1632 cairo_stroke (cr);
1633
1634 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
1635 cairo_move_to (cr, OSD_HR_W/2 - extents.width/2 + OSD_HR_FONT_SIZE/4,
1636 OSD_HR_H/2 - extents.y_bearing/2);
1637 cairo_show_text (cr, str);
1638
1639 osd_render_heart(cr, OSD_HR_W/2 - extents.width/2 - OSD_HR_FONT_SIZE/8,
1640 OSD_HR_H/2, OSD_HR_FONT_SIZE/5, priv->hr.ok);
1641
1642
1643 cairo_destroy(cr);
1644 }
1645
1646 void
1647 osm_gps_map_osd_draw_hr (OsmGpsMap *map, gboolean ok, gint rate) {
1648 g_return_if_fail (OSM_IS_GPS_MAP (map));
1649
1650 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1651 g_return_if_fail (osd);
1652
1653 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1654 g_return_if_fail (priv);
1655
1656 /* allocate heart rate surface */
1657 if(rate != OSD_HR_NONE && !priv->hr.surface)
1658 priv->hr.surface =
1659 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1660 OSD_HR_W, OSD_HR_H);
1661
1662 if(rate == OSD_HR_NONE && priv->hr.surface) {
1663 cairo_surface_destroy(priv->hr.surface);
1664 priv->hr.surface = NULL;
1665 }
1666
1667 if(priv->hr.rate != rate || priv->hr.ok != ok) {
1668 priv->hr.rate = rate;
1669 priv->hr.ok = ok;
1670 osd_render_hr(osd);
1671 }
1672
1673 osm_gps_map_redraw(map);
1674 }
1675
1676 #endif // OSD_HEARTRATE
1677
1678 static osd_button_t
1679 osd_check_int(osm_gps_map_osd_t *osd, gboolean click, gint state, gint x, gint y) {
1680 osd_button_t but = OSD_NONE;
1681
1682 #ifdef OSD_BALLOON
1683 if(state == OSD_STATE_DOWN) {
1684 /* needed to handle balloons that are created at click */
1685 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1686 priv->balloon.just_created = FALSE;
1687 }
1688 #endif
1689
1690 #ifdef OSD_SOURCE_SEL
1691 /* the source selection area is handles internally */
1692 but = osd_source_check(osd, state, x, y);
1693 #endif
1694
1695 #ifdef OSD_NAV
1696 if(but == OSD_NONE) {
1697 /* the source selection area is handles internally */
1698 but = osd_nav_check(osd, state, x, y);
1699 }
1700 #endif
1701
1702 if(but == OSD_NONE) {
1703 gint mx = x - OSD_X;
1704 gint my = y - OSD_Y;
1705
1706 if(OSD_X < 0)
1707 mx -= (osd->widget->allocation.width - OSD_W);
1708
1709 if(OSD_Y < 0)
1710 my -= (osd->widget->allocation.height - OSD_H);
1711
1712 /* first do a rough test for the OSD area. */
1713 /* this is just to avoid an unnecessary detailed test */
1714 if(mx > 0 && mx < OSD_W && my > 0 && my < OSD_H) {
1715 #ifndef OSD_NO_DPAD
1716 but = osd_check_dpad(mx, my);
1717 #endif
1718 }
1719
1720 if(but == OSD_NONE)
1721 but = osd_check_zoom(mx, my);
1722 }
1723
1724 #ifdef OSD_BALLOON
1725 if(but == OSD_NONE) {
1726 /* check if user clicked into balloon */
1727 if(osd_balloon_check(osd, click, state, x, y))
1728 but = OSD_BG;
1729 }
1730 #endif
1731
1732 return but;
1733 }
1734
1735
1736 #ifdef OSD_CROSSHAIR
1737
1738 #ifndef OSD_CROSSHAIR_RADIUS
1739 #define OSD_CROSSHAIR_RADIUS 10
1740 #endif
1741
1742 #define OSD_CROSSHAIR_TICK (OSD_CROSSHAIR_RADIUS/2)
1743 #define OSD_CROSSHAIR_BORDER (OSD_CROSSHAIR_TICK + OSD_CROSSHAIR_RADIUS/4)
1744 #define OSD_CROSSHAIR_W ((OSD_CROSSHAIR_RADIUS+OSD_CROSSHAIR_BORDER)*2)
1745 #define OSD_CROSSHAIR_H ((OSD_CROSSHAIR_RADIUS+OSD_CROSSHAIR_BORDER)*2)
1746
1747 static void
1748 osd_render_crosshair_shape(cairo_t *cr) {
1749 cairo_arc (cr, OSD_CROSSHAIR_W/2, OSD_CROSSHAIR_H/2,
1750 OSD_CROSSHAIR_RADIUS, 0, 2*M_PI);
1751
1752 cairo_move_to (cr, OSD_CROSSHAIR_W/2 - OSD_CROSSHAIR_RADIUS,
1753 OSD_CROSSHAIR_H/2);
1754 cairo_rel_line_to (cr, -OSD_CROSSHAIR_TICK, 0);
1755 cairo_move_to (cr, OSD_CROSSHAIR_W/2 + OSD_CROSSHAIR_RADIUS,
1756 OSD_CROSSHAIR_H/2);
1757 cairo_rel_line_to (cr, OSD_CROSSHAIR_TICK, 0);
1758
1759 cairo_move_to (cr, OSD_CROSSHAIR_W/2,
1760 OSD_CROSSHAIR_H/2 - OSD_CROSSHAIR_RADIUS);
1761 cairo_rel_line_to (cr, 0, -OSD_CROSSHAIR_TICK);
1762 cairo_move_to (cr, OSD_CROSSHAIR_W/2,
1763 OSD_CROSSHAIR_H/2 + OSD_CROSSHAIR_RADIUS);
1764 cairo_rel_line_to (cr, 0, OSD_CROSSHAIR_TICK);
1765
1766 cairo_stroke (cr);
1767 }
1768
1769 static void
1770 osd_render_crosshair(osm_gps_map_osd_t *osd)
1771 {
1772 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1773
1774 if(!priv->crosshair.surface || priv->crosshair.rendered)
1775 return;
1776
1777 priv->crosshair.rendered = TRUE;
1778
1779 /* first fill with transparency */
1780 g_assert(priv->crosshair.surface);
1781 cairo_t *cr = cairo_create(priv->crosshair.surface);
1782 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1783 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
1784 cairo_paint(cr);
1785 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1786
1787 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1788
1789 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.5);
1790 cairo_set_line_width (cr, OSD_CROSSHAIR_RADIUS/2);
1791 osd_render_crosshair_shape(cr);
1792
1793 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1794 cairo_set_line_width (cr, OSD_CROSSHAIR_RADIUS/4);
1795 osd_render_crosshair_shape(cr);
1796
1797 cairo_destroy(cr);
1798 }
1799 #endif
1800
1801 #ifdef OSD_SCALE
1802
1803 #ifndef OSD_SCALE_FONT_SIZE
1804 #define OSD_SCALE_FONT_SIZE (12.0)
1805 #endif
1806 #define OSD_SCALE_W (10*OSD_SCALE_FONT_SIZE)
1807 #define OSD_SCALE_H (5*OSD_SCALE_FONT_SIZE/2)
1808
1809 /* various parameters used to create the scale */
1810 #define OSD_SCALE_H2 (OSD_SCALE_H/2)
1811 #define OSD_SCALE_TICK (2*OSD_SCALE_FONT_SIZE/3)
1812 #define OSD_SCALE_M (OSD_SCALE_H2 - OSD_SCALE_TICK)
1813 #define OSD_SCALE_I (OSD_SCALE_H2 + OSD_SCALE_TICK)
1814 #define OSD_SCALE_FD (OSD_SCALE_FONT_SIZE/4)
1815
1816 static void
1817 osd_render_scale(osm_gps_map_osd_t *osd)
1818 {
1819 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1820
1821 if(!priv->scale.surface)
1822 return;
1823
1824 /* this only needs to be rendered if the zoom has changed */
1825 gint zoom;
1826 g_object_get(OSM_GPS_MAP(osd->widget), "zoom", &zoom, NULL);
1827 if(zoom == priv->scale.zoom)
1828 return;
1829
1830 priv->scale.zoom = zoom;
1831
1832 float m_per_pix = osm_gps_map_get_scale(OSM_GPS_MAP(osd->widget));
1833
1834 /* first fill with transparency */
1835 g_assert(priv->scale.surface);
1836 cairo_t *cr = cairo_create(priv->scale.surface);
1837 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1838 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
1839 // pink for testing: cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.2);
1840 cairo_paint(cr);
1841 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1842
1843 /* determine the size of the scale width in meters */
1844 float width = (OSD_SCALE_W-OSD_SCALE_FONT_SIZE/6) * m_per_pix;
1845
1846 /* scale this to useful values */
1847 int exp = logf(width)*M_LOG10E;
1848 int mant = width/pow(10,exp);
1849 int width_metric = mant * pow(10,exp);
1850 char *dist_str = NULL;
1851 if(width_metric<1000)
1852 dist_str = g_strdup_printf("%u m", width_metric);
1853 else
1854 dist_str = g_strdup_printf("%u km", width_metric/1000);
1855 width_metric /= m_per_pix;
1856
1857 /* and now the hard part: scale for useful imperial values :-( */
1858 /* try to convert to feet, 1ft == 0.3048 m */
1859 width /= 0.3048;
1860 float imp_scale = 0.3048;
1861 char *dist_imp_unit = "ft";
1862
1863 if(width >= 100) {
1864 /* 1yd == 3 feet */
1865 width /= 3.0;
1866 imp_scale *= 3.0;
1867 dist_imp_unit = "yd";
1868
1869 if(width >= 1760.0) {
1870 /* 1mi == 1760 yd */
1871 width /= 1760.0;
1872 imp_scale *= 1760.0;
1873 dist_imp_unit = "mi";
1874 }
1875 }
1876
1877 /* also convert this to full tens/hundreds */
1878 exp = logf(width)*M_LOG10E;
1879 mant = width/pow(10,exp);
1880 int width_imp = mant * pow(10,exp);
1881 char *dist_str_imp = g_strdup_printf("%u %s", width_imp, dist_imp_unit);
1882
1883 /* convert back to pixels */
1884 width_imp *= imp_scale;
1885 width_imp /= m_per_pix;
1886
1887 cairo_select_font_face (cr, "Sans",
1888 CAIRO_FONT_SLANT_NORMAL,
1889 CAIRO_FONT_WEIGHT_BOLD);
1890 cairo_set_font_size (cr, OSD_SCALE_FONT_SIZE);
1891 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
1892
1893 cairo_text_extents_t extents;
1894 cairo_text_extents (cr, dist_str, &extents);
1895
1896 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1897 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/6);
1898 cairo_move_to (cr, 2*OSD_SCALE_FD, OSD_SCALE_H2-OSD_SCALE_FD);
1899 cairo_text_path (cr, dist_str);
1900 cairo_stroke (cr);
1901 cairo_move_to (cr, 2*OSD_SCALE_FD,
1902 OSD_SCALE_H2+OSD_SCALE_FD + extents.height);
1903 cairo_text_path (cr, dist_str_imp);
1904 cairo_stroke (cr);
1905
1906 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
1907 cairo_move_to (cr, 2*OSD_SCALE_FD, OSD_SCALE_H2-OSD_SCALE_FD);
1908 cairo_show_text (cr, dist_str);
1909 cairo_move_to (cr, 2*OSD_SCALE_FD,
1910 OSD_SCALE_H2+OSD_SCALE_FD + extents.height);
1911 cairo_show_text (cr, dist_str_imp);
1912
1913 g_free(dist_str);
1914 g_free(dist_str_imp);
1915
1916 /* draw white line */
1917 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1918 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
1919 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/3);
1920 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_M);
1921 cairo_rel_line_to (cr, 0, OSD_SCALE_TICK);
1922 cairo_rel_line_to (cr, width_metric, 0);
1923 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1924 cairo_stroke(cr);
1925 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_I);
1926 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1927 cairo_rel_line_to (cr, width_imp, 0);
1928 cairo_rel_line_to (cr, 0, +OSD_SCALE_TICK);
1929 cairo_stroke(cr);
1930
1931 /* draw black line */
1932 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
1933 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/6);
1934 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_M);
1935 cairo_rel_line_to (cr, 0, OSD_SCALE_TICK);
1936 cairo_rel_line_to (cr, width_metric, 0);
1937 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1938 cairo_stroke(cr);
1939 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_I);
1940 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1941 cairo_rel_line_to (cr, width_imp, 0);
1942 cairo_rel_line_to (cr, 0, +OSD_SCALE_TICK);
1943 cairo_stroke(cr);
1944
1945 cairo_destroy(cr);
1946 }
1947 #endif
1948
1949 static void
1950 osd_render_controls(osm_gps_map_osd_t *osd)
1951 {
1952 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1953
1954 if(!priv->controls.surface)
1955 return;
1956
1957 if(priv->controls.rendered
1958 #ifdef OSD_GPS_BUTTON
1959 && (priv->controls.gps_enabled == (osd->cb != NULL))
1960 #endif
1961 )
1962 return;
1963
1964 #ifdef OSD_GPS_BUTTON
1965 priv->controls.gps_enabled = (osd->cb != NULL);
1966 #endif
1967 priv->controls.rendered = TRUE;
1968
1969 #ifndef OSD_COLOR
1970 GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL];
1971 GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL];
1972 GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE];
1973 #endif
1974
1975 /* first fill with transparency */
1976 g_assert(priv->controls.surface);
1977 cairo_t *cr = cairo_create(priv->controls.surface);
1978 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1979 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
1980 cairo_paint(cr);
1981 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1982
1983 /* --------- draw zoom and dpad shape shadow ----------- */
1984 #ifdef OSD_SHADOW_ENABLE
1985 osd_zoom_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
1986 osd_shape_shadow(cr);
1987 #ifndef OSD_NO_DPAD
1988 osd_dpad_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
1989 osd_shape_shadow(cr);
1990 #endif
1991 #endif
1992
1993 /* --------- draw zoom and dpad shape ----------- */
1994
1995 osd_zoom_shape(cr, 1, 1);
1996 #ifndef OSD_COLOR
1997 osd_shape(cr, &bg, &fg);
1998 #else
1999 osd_shape(cr);
2000 #endif
2001 #ifndef OSD_NO_DPAD
2002 osd_dpad_shape(cr, 1, 1);
2003 #ifndef OSD_COLOR
2004 osd_shape(cr, &bg, &fg);
2005 #else
2006 osd_shape(cr);
2007 #endif
2008 #endif
2009
2010 /* --------- draw zoom and dpad labels --------- */
2011
2012 #ifdef OSD_SHADOW_ENABLE
2013 osd_labels_shadow(cr, Z_RAD/3, TRUE);
2014 osd_zoom_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
2015 #ifndef OSD_NO_DPAD
2016 osd_dpad_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
2017 #endif
2018 cairo_stroke(cr);
2019 #ifdef OSD_GPS_BUTTON
2020 osd_labels_shadow(cr, Z_RAD/6, osd->cb != NULL);
2021 osd_dpad_gps(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
2022 cairo_stroke(cr);
2023 #endif
2024 #endif
2025
2026 #ifndef OSD_COLOR
2027 osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
2028 #else
2029 osd_labels(cr, Z_RAD/3, TRUE);
2030 #endif
2031 osd_zoom_labels(cr, 1, 1);
2032 #ifndef OSD_NO_DPAD
2033 osd_dpad_labels(cr, 1, 1);
2034 #endif
2035 cairo_stroke(cr);
2036
2037 #ifndef OSD_COLOR
2038 osd_labels(cr, Z_RAD/6, osd->cb != NULL, &fg, &da);
2039 #else
2040 osd_labels(cr, Z_RAD/6, osd->cb != NULL);
2041 #endif
2042 #ifdef OSD_GPS_BUTTON
2043 osd_dpad_gps(cr, 1, 1);
2044 #endif
2045 cairo_stroke(cr);
2046
2047 cairo_destroy(cr);
2048 }
2049
2050 static void
2051 osd_render(osm_gps_map_osd_t *osd)
2052 {
2053 /* this function is actually called pretty often since the */
2054 /* OSD contents may have changed (due to a coordinate/zoom change). */
2055 /* The different OSD parts have to make sure that they don't */
2056 /* render unneccessarily often and thus waste CPU power */
2057
2058 osd_render_controls(osd);
2059
2060 #ifdef OSD_SOURCE_SEL
2061 osd_render_source_sel(osd, FALSE);
2062 #endif
2063
2064 #ifdef OSD_SCALE
2065 osd_render_scale(osd);
2066 #endif
2067
2068 #ifdef OSD_CROSSHAIR
2069 osd_render_crosshair(osd);
2070 #endif
2071
2072 #ifdef OSD_NAV
2073 osd_render_nav(osd);
2074 #endif
2075
2076 #ifdef OSD_COORDINATES
2077 osd_render_coordinates(osd);
2078 #endif
2079 }
2080
2081 static void
2082 osd_draw(osm_gps_map_osd_t *osd, GdkDrawable *drawable)
2083 {
2084 osd_priv_t *priv = (osd_priv_t*)osd->priv;
2085
2086 /* OSD itself uses some off-screen rendering, so check if the */
2087 /* offscreen buffer is present and create it if not */
2088 if(!priv->controls.surface) {
2089 /* create overlay ... */
2090 priv->controls.surface =
2091 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W+2, OSD_H+2);
2092
2093 priv->controls.rendered = FALSE;
2094 #ifdef OSD_GPS_BUTTON
2095 priv->controls.gps_enabled = FALSE;
2096 #endif
2097
2098 #ifdef OSD_SOURCE_SEL
2099 /* the initial OSD state is alway not-expanded */
2100 priv->source_sel.surface =
2101 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
2102 OSD_S_W+2, OSD_S_H+2);
2103 priv->source_sel.rendered = FALSE;
2104 #endif
2105
2106 #ifdef OSD_SCALE
2107 priv->scale.surface =
2108 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
2109 OSD_SCALE_W, OSD_SCALE_H);
2110 priv->scale.zoom = -1;
2111 #endif
2112
2113 #ifdef OSD_CROSSHAIR
2114 priv->crosshair.surface =
2115 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
2116 OSD_CROSSHAIR_W, OSD_CROSSHAIR_H);
2117 priv->crosshair.rendered = FALSE;
2118 #endif
2119
2120 #ifdef OSD_COORDINATES
2121 priv->coordinates.surface =
2122 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
2123 OSD_COORDINATES_W, OSD_COORDINATES_H);
2124
2125 priv->coordinates.lat = priv->coordinates.lon = OSM_GPS_MAP_INVALID;
2126 #endif
2127
2128 /* ... and render it */
2129 osd_render(osd);
2130 }
2131
2132 // now draw this onto the original context
2133 cairo_t *cr = gdk_cairo_create(drawable);
2134
2135 gint x, y;
2136
2137 #ifdef OSD_SCALE
2138 x = OSD_X;
2139 y = -OSD_Y;
2140 if(x < 0) x += osd->widget->allocation.width - OSD_SCALE_W;
2141 if(y < 0) y += osd->widget->allocation.height - OSD_SCALE_H;
2142
2143 cairo_set_source_surface(cr, priv->scale.surface, x, y);
2144 cairo_paint(cr);
2145 #endif
2146
2147 #ifdef OSD_CROSSHAIR
2148 x = (osd->widget->allocation.width - OSD_CROSSHAIR_W)/2;
2149 y = (osd->widget->allocation.height - OSD_CROSSHAIR_H)/2;
2150
2151 cairo_set_source_surface(cr, priv->crosshair.surface, x, y);
2152 cairo_paint(cr);
2153 #endif
2154
2155 #ifdef OSD_NAV
2156 if(priv->nav.surface) {
2157 x = OSD_X;
2158 if(x < 0) x += osd->widget->allocation.width - OSD_NAV_W;
2159 y = (osd->widget->allocation.height - OSD_NAV_H)/2;
2160
2161 cairo_set_source_surface(cr, priv->nav.surface, x, y);
2162 cairo_paint(cr);
2163 }
2164 #endif
2165
2166 #ifdef OSD_COORDINATES
2167 x = -OSD_X;
2168 y = -OSD_Y;
2169 if(x < 0) x += osd->widget->allocation.width - OSD_COORDINATES_W;
2170 if(y < 0) y += osd->widget->allocation.height - OSD_COORDINATES_H;
2171
2172 cairo_set_source_surface(cr, priv->coordinates.surface, x, y);
2173 cairo_paint(cr);
2174 #endif
2175
2176 #ifdef OSD_HEARTRATE
2177 if(priv->hr.surface) {
2178 x = (osd->widget->allocation.width - OSD_HR_W)/2;
2179 y = OSD_HR_Y;
2180 if(y < 0) y += osd->widget->allocation.height - OSD_COORDINATES_H;
2181
2182 cairo_set_source_surface(cr, priv->hr.surface, x, y);
2183 cairo_paint(cr);
2184 }
2185 #endif
2186
2187 #ifdef OSD_BALLOON
2188 if(priv->balloon.surface) {
2189
2190 /* convert given lat lon into screen coordinates */
2191 gint x, y;
2192 osm_gps_map_geographic_to_screen (OSM_GPS_MAP(osd->widget),
2193 priv->balloon.lat, priv->balloon.lon,
2194 &x, &y);
2195
2196 /* check if balloon needs to be rerendered */
2197 osd_render_balloon(osd);
2198
2199 cairo_set_source_surface(cr, priv->balloon.surface,
2200 x + priv->balloon.offset_x,
2201 y + priv->balloon.offset_y);
2202 cairo_paint(cr);
2203 }
2204 #endif
2205
2206 x = OSD_X;
2207 if(x < 0)
2208 x += osd->widget->allocation.width - OSD_W;
2209
2210 y = OSD_Y;
2211 if(y < 0)
2212 y += osd->widget->allocation.height - OSD_H;
2213
2214 cairo_set_source_surface(cr, priv->controls.surface, x, y);
2215 cairo_paint(cr);
2216
2217 #ifdef OSD_SOURCE_SEL
2218 if(!priv->source_sel.handler_id) {
2219 /* the OSD source selection is not being animated */
2220 if(!priv->source_sel.expanded)
2221 x = osd->widget->allocation.width - OSD_S_W;
2222 else
2223 x = osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
2224 } else
2225 x = priv->source_sel.shift;
2226
2227 y = OSD_S_Y;
2228 if(OSD_S_Y < 0) {
2229 if(!priv->source_sel.expanded)
2230 y = osd->widget->allocation.height - OSD_S_H + OSD_S_Y;
2231 else
2232 y = osd->widget->allocation.height - OSD_S_EXP_H + OSD_S_Y;
2233 }
2234
2235 cairo_set_source_surface(cr, priv->source_sel.surface, x, y);
2236 cairo_paint(cr);
2237 #endif
2238
2239 cairo_destroy(cr);
2240 }
2241
2242 static void
2243 osd_free(osm_gps_map_osd_t *osd)
2244 {
2245 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
2246
2247 if (priv->controls.surface)
2248 cairo_surface_destroy(priv->controls.surface);
2249
2250 #ifdef OSD_SOURCE_SEL
2251 if(priv->source_sel.handler_id)
2252 gtk_timeout_remove(priv->source_sel.handler_id);
2253
2254 if (priv->source_sel.surface)
2255 cairo_surface_destroy(priv->source_sel.surface);
2256 #endif
2257
2258 #ifdef OSD_SCALE
2259 if (priv->scale.surface)
2260 cairo_surface_destroy(priv->scale.surface);
2261 #endif
2262
2263 #ifdef OSD_CROSSHAIR
2264 if (priv->crosshair.surface)
2265 cairo_surface_destroy(priv->crosshair.surface);
2266 #endif
2267
2268 #ifdef OSD_NAV
2269 if (priv->nav.surface)
2270 cairo_surface_destroy(priv->nav.surface);
2271 #endif
2272
2273 #ifdef OSD_COORDINATES
2274 if (priv->coordinates.surface)
2275 cairo_surface_destroy(priv->coordinates.surface);
2276 #endif
2277
2278 #ifdef OSD_BALLOON
2279 if (priv->balloon.surface)
2280 cairo_surface_destroy(priv->balloon.surface);
2281 #endif
2282
2283 #ifdef OSD_HEARTRATE
2284 if (priv->hr.surface)
2285 cairo_surface_destroy(priv->hr.surface);
2286 #endif
2287
2288 g_free(priv);
2289 osd->priv = NULL;
2290 }
2291
2292 static gboolean
2293 osd_busy(osm_gps_map_osd_t *osd)
2294 {
2295 #ifdef OSD_SOURCE_SEL
2296 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
2297 return (priv->source_sel.handler_id != 0);
2298 #else
2299 return FALSE;
2300 #endif
2301 }
2302
2303 static osd_button_t
2304 osd_check(osm_gps_map_osd_t *osd, gboolean down, gint x, gint y) {
2305 return osd_check_int(osd, TRUE, down?OSD_STATE_DOWN:OSD_STATE_UP, x, y);
2306 }
2307
2308 static osm_gps_map_osd_t osd_classic = {
2309 .widget = NULL,
2310
2311 .draw = osd_draw,
2312 .check = osd_check,
2313 .render = osd_render,
2314 .free = osd_free,
2315 .busy = osd_busy,
2316
2317 .cb = NULL,
2318 .data = NULL,
2319
2320 .priv = NULL
2321 };
2322
2323 /* this is the only function that's externally visible */
2324 void
2325 osm_gps_map_osd_classic_init(OsmGpsMap *map)
2326 {
2327 osd_priv_t *priv = g_new0(osd_priv_t, 1);
2328
2329 /* reset entries to default value */
2330 osd_classic.widget = NULL;
2331 osd_classic.cb = NULL;
2332 osd_classic.data = NULL;
2333 osd_classic.priv = priv;
2334
2335 #ifdef OSD_BALLOON
2336 priv->balloon.lat = OSM_GPS_MAP_INVALID;
2337 priv->balloon.lon = OSM_GPS_MAP_INVALID;
2338 #endif
2339
2340 #ifdef OSD_HEARTRATE
2341 priv->hr.rate = OSD_HR_NONE;
2342 #endif
2343
2344 osm_gps_map_register_osd(map, &osd_classic);
2345 }
2346
2347 #ifdef OSD_GPS_BUTTON
2348 /* below are osd specific functions which aren't used by osm-gps-map */
2349 /* but instead are to be used by the main application */
2350 void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdCallback cb,
2351 gpointer data) {
2352 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
2353 g_return_if_fail (osd);
2354
2355 osd->cb = cb;
2356 osd->data = data;
2357
2358 /* this may have changed the state of the gps button */
2359 /* we thus re-render the overlay */
2360 osd->render(osd);
2361
2362 osm_gps_map_redraw(map);
2363 }
2364 #endif
2365
2366 osd_button_t
2367 osm_gps_map_osd_check(OsmGpsMap *map, gint x, gint y) {
2368 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
2369 g_return_val_if_fail (osd, OSD_NONE);
2370
2371 return osd_check_int(osd, FALSE, OSD_STATE_CHECK, x, y);
2372 }