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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 122 - (show annotations)
Mon Sep 21 13:15:25 2009 UTC (14 years, 7 months ago) by harbaum
File MIME type: text/plain
File size: 54632 byte(s)
OSD nav begun
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 #include <stdlib.h> // abs
22 #include <math.h> // M_PI/cos()
23
24 /* parameters that can be overwritten from the config file: */
25 /* OSD_DIAMETER */
26 /* OSD_X, OSD_Y */
27
28 #define OSD_NAV
29
30 #ifndef USE_CAIRO
31 #error "OSD control display lacks a non-cairo implementation!"
32 #endif
33
34 #include <cairo.h>
35
36 #include "osm-gps-map.h"
37 #include "converter.h"
38 #include "osm-gps-map-osd-classic.h"
39
40 //the osd controls
41 typedef struct {
42 /* the offscreen representation of the OSD */
43 struct {
44 cairo_surface_t *surface;
45 gboolean rendered;
46 #ifdef OSD_GPS_BUTTON
47 gboolean gps_enabled;
48 #endif
49 } controls;
50
51 #ifdef OSD_BALLOON
52 //a balloon with additional info
53 struct {
54 cairo_surface_t *surface;
55 int orientation, offset_x, offset_y;
56
57 gboolean just_created;
58 float lat, lon;
59 OsmGpsMapRect_t rect;
60
61 /* function called to have the user app draw the contents */
62 OsmGpsMapBalloonCallback cb;
63 gpointer data;
64 } balloon;
65 #endif
66
67 #ifdef OSD_SCALE
68 struct {
69 cairo_surface_t *surface;
70 int zoom;
71 } scale;
72 #endif
73
74 #ifdef OSD_CROSSHAIR
75 struct {
76 cairo_surface_t *surface;
77 gboolean rendered;
78 } crosshair;
79 #endif
80
81 #ifdef OSD_NAV
82 struct {
83 cairo_surface_t *surface;
84 } nav;
85 #endif
86
87 #ifdef OSD_COORDINATES
88 struct {
89 cairo_surface_t *surface;
90 float lat, lon;
91 } coordinates;
92 #endif
93
94 #ifdef OSD_SOURCE_SEL
95 struct {
96 /* values to handle the "source" menu */
97 cairo_surface_t *surface;
98 gboolean expanded;
99 gint shift, dir, count;
100 gint handler_id;
101 gint width, height;
102 gboolean rendered;
103 } source_sel;
104 #endif
105
106 } osd_priv_t;
107
108 #ifdef OSD_BALLOON
109 /* most visual effects are hardcoded by now, but may be made */
110 /* available via properties later */
111 #ifndef BALLOON_AREA_WIDTH
112 #define BALLOON_AREA_WIDTH 290
113 #endif
114 #ifndef BALLOON_AREA_HEIGHT
115 #define BALLOON_AREA_HEIGHT 75
116 #endif
117 #ifndef BALLOON_CORNER_RADIUS
118 #define BALLOON_CORNER_RADIUS 10
119 #endif
120
121 #define BALLOON_BORDER (BALLOON_CORNER_RADIUS/2)
122 #define BALLOON_WIDTH (BALLOON_AREA_WIDTH + 2 * BALLOON_BORDER)
123 #define BALLOON_HEIGHT (BALLOON_AREA_HEIGHT + 2 * BALLOON_BORDER)
124 #define BALLOON_TRANSPARENCY 0.8
125 #define POINTER_HEIGHT 20
126 #define POINTER_FOOT_WIDTH 20
127 #define POINTER_OFFSET (BALLOON_CORNER_RADIUS*3/4)
128 #define BALLOON_SHADOW (BALLOON_CORNER_RADIUS/2)
129 #define BALLOON_SHADOW_TRANSPARENCY 0.2
130
131 #define BALLOON_W (BALLOON_WIDTH + BALLOON_SHADOW)
132 #define BALLOON_H (BALLOON_HEIGHT + POINTER_HEIGHT + BALLOON_SHADOW)
133
134 #define CLOSE_BUTTON_RADIUS (BALLOON_CORNER_RADIUS)
135
136
137 /* draw the bubble shape. this is used twice, once for the shape and once */
138 /* for the shadow */
139 static void
140 osm_gps_map_draw_balloon_shape (cairo_t *cr, int x0, int y0, int x1, int y1,
141 gboolean bottom, int px, int py, int px0, int px1) {
142
143 cairo_move_to (cr, x0, y0 + BALLOON_CORNER_RADIUS);
144 cairo_arc (cr, x0 + BALLOON_CORNER_RADIUS, y0 + BALLOON_CORNER_RADIUS,
145 BALLOON_CORNER_RADIUS, -M_PI, -M_PI/2);
146 if(!bottom) {
147 /* insert top pointer */
148 cairo_line_to (cr, px1, y0);
149 cairo_line_to (cr, px, py);
150 cairo_line_to (cr, px0, y0);
151 }
152
153 cairo_line_to (cr, x1 - BALLOON_CORNER_RADIUS, y0);
154 cairo_arc (cr, x1 - BALLOON_CORNER_RADIUS, y0 + BALLOON_CORNER_RADIUS,
155 BALLOON_CORNER_RADIUS, -M_PI/2, 0);
156 cairo_line_to (cr, x1 , y1 - BALLOON_CORNER_RADIUS);
157 cairo_arc (cr, x1 - BALLOON_CORNER_RADIUS, y1 - BALLOON_CORNER_RADIUS,
158 BALLOON_CORNER_RADIUS, 0, M_PI/2);
159 if(bottom) {
160 /* insert bottom pointer */
161 cairo_line_to (cr, px0, y1);
162 cairo_line_to (cr, px, py);
163 cairo_line_to (cr, px1, y1);
164 }
165
166 cairo_line_to (cr, x0 + BALLOON_CORNER_RADIUS, y1);
167 cairo_arc (cr, x0 + BALLOON_CORNER_RADIUS, y1 - BALLOON_CORNER_RADIUS,
168 BALLOON_CORNER_RADIUS, M_PI/2, M_PI);
169
170 cairo_close_path (cr);
171 }
172
173 static void
174 osd_render_balloon(osm_gps_map_osd_t *osd) {
175 osd_priv_t *priv = (osd_priv_t*)osd->priv;
176
177 /* get zoom */
178 gint zoom;
179 g_object_get(OSM_GPS_MAP(osd->widget), "zoom", &zoom, NULL);
180
181 /* ------- convert given coordinate into screen position --------- */
182 gint xs, ys;
183 osm_gps_map_geographic_to_screen (OSM_GPS_MAP(osd->widget),
184 priv->balloon.lat, priv->balloon.lon,
185 &xs, &ys);
186
187 gint x0 = 1, y0 = 1;
188
189 /* check position of this relative to screen center to determine */
190 /* pointer direction ... */
191 int pointer_x, pointer_x0, pointer_x1;
192 int pointer_y;
193
194 /* ... and calculate position */
195 int orientation = 0;
196 if(xs > osd->widget->allocation.width/2) {
197 priv->balloon.offset_x = -BALLOON_WIDTH + POINTER_OFFSET;
198 pointer_x = x0 - priv->balloon.offset_x;
199 pointer_x0 = pointer_x - (BALLOON_CORNER_RADIUS - POINTER_OFFSET);
200 pointer_x1 = pointer_x0 - POINTER_FOOT_WIDTH;
201 orientation |= 1;
202 } else {
203 priv->balloon.offset_x = -POINTER_OFFSET;
204 pointer_x = x0 - priv->balloon.offset_x;
205 pointer_x1 = pointer_x + (BALLOON_CORNER_RADIUS - POINTER_OFFSET);
206 pointer_x0 = pointer_x1 + POINTER_FOOT_WIDTH;
207 }
208
209 gboolean bottom = FALSE;
210 if(ys > osd->widget->allocation.height/2) {
211 priv->balloon.offset_y = -BALLOON_HEIGHT - POINTER_HEIGHT;
212 pointer_y = y0 - priv->balloon.offset_y;
213 bottom = TRUE;
214 orientation |= 2;
215 } else {
216 priv->balloon.offset_y = 0;
217 pointer_y = y0 - priv->balloon.offset_y;
218 y0 += POINTER_HEIGHT;
219 }
220
221 /* if required orientation equals current one, then don't render */
222 /* anything */
223 if(orientation == priv->balloon.orientation)
224 return;
225
226 priv->balloon.orientation = orientation;
227
228 /* calculate bottom/right of box */
229 int x1 = x0 + BALLOON_WIDTH, y1 = y0 + BALLOON_HEIGHT;
230
231 /* save balloon screen coordinates for later use */
232 priv->balloon.rect.x = x0 + BALLOON_BORDER;
233 priv->balloon.rect.y = y0 + BALLOON_BORDER;
234 priv->balloon.rect.w = x1 - x0 - 2*BALLOON_BORDER;
235 priv->balloon.rect.h = y1 - y0 - 2*BALLOON_BORDER;
236
237 cairo_t *cr = cairo_create(priv->balloon.surface);
238 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
239 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
240 cairo_paint(cr);
241 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
242
243 /* --------- draw shadow --------------- */
244 osm_gps_map_draw_balloon_shape (cr,
245 x0 + BALLOON_SHADOW, y0 + BALLOON_SHADOW,
246 x1 + BALLOON_SHADOW, y1 + BALLOON_SHADOW,
247 bottom, pointer_x, pointer_y,
248 pointer_x0 + BALLOON_SHADOW, pointer_x1 + BALLOON_SHADOW);
249
250 cairo_set_source_rgba (cr, 0, 0, 0, BALLOON_SHADOW_TRANSPARENCY);
251 cairo_fill_preserve (cr);
252 cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
253 cairo_set_line_width (cr, 0);
254 cairo_stroke (cr);
255
256 /* --------- draw main shape ----------- */
257 osm_gps_map_draw_balloon_shape (cr, x0, y0, x1, y1,
258 bottom, pointer_x, pointer_y, pointer_x0, pointer_x1);
259
260 cairo_set_source_rgba (cr, 1, 1, 1, BALLOON_TRANSPARENCY);
261 cairo_fill_preserve (cr);
262 cairo_set_source_rgba (cr, 0, 0, 0, BALLOON_TRANSPARENCY);
263 cairo_set_line_width (cr, 1);
264 cairo_stroke (cr);
265
266 if (priv->balloon.cb) {
267 /* clip in case application tries to draw in */
268 /* exceed of the balloon */
269 cairo_rectangle (cr, priv->balloon.rect.x, priv->balloon.rect.y,
270 priv->balloon.rect.w, priv->balloon.rect.h);
271 cairo_clip (cr);
272 cairo_new_path (cr); /* current path is not
273 consumed by cairo_clip() */
274
275 priv->balloon.cb(cr, &priv->balloon.rect, priv->balloon.data);
276 }
277
278 cairo_destroy(cr);
279 }
280
281 /* return true if balloon is being displayed and if */
282 /* the given coordinate is within this balloon */
283 static gboolean
284 osd_balloon_check(osm_gps_map_osd_t *osd, gboolean down, gint x, gint y)
285 {
286 osd_priv_t *priv = (osd_priv_t*)osd->priv;
287
288 if(!priv->balloon.surface)
289 return FALSE;
290
291 gint xs, ys;
292 osm_gps_map_geographic_to_screen (OSM_GPS_MAP(osd->widget),
293 priv->balloon.lat, priv->balloon.lon,
294 &xs, &ys);
295
296 xs += priv->balloon.rect.x + priv->balloon.offset_x;
297 ys += priv->balloon.rect.y + priv->balloon.offset_y;
298
299 gboolean is_in =
300 (x > xs) && (x < xs + priv->balloon.rect.w) &&
301 (y > ys) && (y < ys + priv->balloon.rect.h);
302
303 /* handle the fact that the balloon may have been created by the */
304 /* button down event */
305 if(!is_in && !down && !priv->balloon.just_created) {
306 /* the user actually clicked outside the balloon */
307
308 /* close the balloon! */
309 osm_gps_map_osd_clear_balloon (OSM_GPS_MAP(osd->widget));
310 }
311
312 return is_in;
313 }
314
315 void osm_gps_map_osd_clear_balloon (OsmGpsMap *map) {
316 g_return_if_fail (OSM_IS_GPS_MAP (map));
317
318 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
319 g_return_if_fail (osd);
320
321 osd_priv_t *priv = (osd_priv_t*)osd->priv;
322 g_return_if_fail (priv);
323
324 if(priv->balloon.surface) {
325 cairo_surface_destroy(priv->balloon.surface);
326 priv->balloon.surface = NULL;
327 priv->balloon.lat = OSM_GPS_MAP_INVALID;
328 priv->balloon.lon = OSM_GPS_MAP_INVALID;
329 }
330 osm_gps_map_redraw(map);
331 }
332
333 void
334 osm_gps_map_osd_draw_balloon (OsmGpsMap *map, float latitude, float longitude,
335 OsmGpsMapBalloonCallback cb, gpointer data) {
336 g_return_if_fail (OSM_IS_GPS_MAP (map));
337
338 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
339 g_return_if_fail (osd);
340
341 osd_priv_t *priv = (osd_priv_t*)osd->priv;
342 g_return_if_fail (priv);
343
344 osm_gps_map_osd_clear_balloon (map);
345
346 /* allocate balloon surface */
347 priv->balloon.surface =
348 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
349 BALLOON_W+2, BALLOON_H+2);
350
351 priv->balloon.lat = latitude;
352 priv->balloon.lon = longitude;
353 priv->balloon.cb = cb;
354 priv->balloon.data = data;
355 priv->balloon.just_created = TRUE;
356
357 priv->balloon.orientation = -1;
358
359 osd_render_balloon(osd);
360
361 osm_gps_map_redraw(map);
362 }
363
364 #endif // OSD_BALLOON
365
366 /* position and extent of bounding box */
367 #ifndef OSD_X
368 #define OSD_X (10)
369 #endif
370
371 #ifndef OSD_Y
372 #define OSD_Y (10)
373 #endif
374
375 /* parameters of the direction shape */
376 #ifndef OSD_DIAMETER
377 #define D_RAD (30) // diameter of dpad
378 #else
379 #define D_RAD (OSD_DIAMETER)
380 #endif
381 #define D_TIP (4*D_RAD/5) // distance of arrow tip from dpad center
382 #define D_LEN (D_RAD/4) // length of arrow
383 #define D_WID (D_LEN) // width of arrow
384
385 /* parameters of the "zoom" pad */
386 #define Z_STEP (D_RAD/4) // distance between dpad and zoom
387 #define Z_RAD (D_RAD/2) // radius of "caps" of zoom bar
388
389 #ifdef OSD_SHADOW_ENABLE
390 /* shadow also depends on control size */
391 #define OSD_SHADOW (D_RAD/6)
392 #else
393 #define OSD_SHADOW (0)
394 #endif
395
396 /* normally the GPS button is in the center of the dpad. if there's */
397 /* no dpad it will go into the zoom area */
398 #if defined(OSD_GPS_BUTTON) && defined(OSD_NO_DPAD)
399 #define Z_GPS 1
400 #else
401 #define Z_GPS 0
402 #endif
403
404 /* total width and height of controls incl. shadow */
405 #define OSD_W (2*D_RAD + OSD_SHADOW + Z_GPS * 2 * Z_RAD)
406 #if !Z_GPS
407 #define OSD_H (2*D_RAD + Z_STEP + 2*Z_RAD + OSD_SHADOW)
408 #else
409 #define OSD_H (2*Z_RAD + OSD_SHADOW)
410 #endif
411
412 #ifdef OSD_SHADOW_ENABLE
413 #define OSD_LBL_SHADOW (OSD_SHADOW/2)
414 #endif
415
416 #define Z_TOP ((1-Z_GPS) * (2 * D_RAD + Z_STEP))
417
418 #define Z_MID (Z_TOP + Z_RAD)
419 #define Z_BOT (Z_MID + Z_RAD)
420 #define Z_LEFT (Z_RAD)
421 #define Z_RIGHT (2 * D_RAD - Z_RAD + Z_GPS * 2 * Z_RAD)
422 #define Z_CENTER ((Z_RIGHT + Z_LEFT)/2)
423
424 /* create the cairo shape used for the zoom buttons */
425 static void
426 osd_zoom_shape(cairo_t *cr, gint x, gint y)
427 {
428 cairo_move_to (cr, x+Z_LEFT, y+Z_TOP);
429 cairo_line_to (cr, x+Z_RIGHT, y+Z_TOP);
430 cairo_arc (cr, x+Z_RIGHT, y+Z_MID, Z_RAD, -M_PI/2, M_PI/2);
431 cairo_line_to (cr, x+Z_LEFT, y+Z_BOT);
432 cairo_arc (cr, x+Z_LEFT, y+Z_MID, Z_RAD, M_PI/2, -M_PI/2);
433 }
434
435 /* ------------------- color/shadow functions ----------------- */
436
437 #ifndef OSD_COLOR
438 /* if no color has been specified we just use the gdks default colors */
439 static void
440 osd_labels(cairo_t *cr, gint width, gboolean enabled,
441 GdkColor *fg, GdkColor *disabled) {
442 if(enabled) gdk_cairo_set_source_color(cr, fg);
443 else gdk_cairo_set_source_color(cr, disabled);
444 cairo_set_line_width (cr, width);
445 }
446 #else
447 static void
448 osd_labels(cairo_t *cr, gint width, gboolean enabled) {
449 if(enabled) cairo_set_source_rgb (cr, OSD_COLOR);
450 else cairo_set_source_rgb (cr, OSD_COLOR_DISABLED);
451 cairo_set_line_width (cr, width);
452 }
453 #endif
454
455 #ifdef OSD_SHADOW_ENABLE
456 static void
457 osd_labels_shadow(cairo_t *cr, gint width, gboolean enabled) {
458 cairo_set_source_rgba (cr, 0, 0, 0, enabled?0.3:0.15);
459 cairo_set_line_width (cr, width);
460 }
461 #endif
462
463 #ifndef OSD_NO_DPAD
464 /* create the cairo shape used for the dpad */
465 static void
466 osd_dpad_shape(cairo_t *cr, gint x, gint y)
467 {
468 cairo_arc (cr, x+D_RAD, y+D_RAD, D_RAD, 0, 2 * M_PI);
469 }
470 #endif
471
472 #ifdef OSD_SHADOW_ENABLE
473 static void
474 osd_shape_shadow(cairo_t *cr) {
475 cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
476 cairo_fill (cr);
477 cairo_stroke (cr);
478 }
479 #endif
480
481 #ifndef OSD_COLOR
482 /* if no color has been specified we just use the gdks default colors */
483 static void
484 osd_shape(cairo_t *cr, GdkColor *bg, GdkColor *fg) {
485 gdk_cairo_set_source_color(cr, bg);
486 cairo_fill_preserve (cr);
487 gdk_cairo_set_source_color(cr, fg);
488 cairo_set_line_width (cr, 1);
489 cairo_stroke (cr);
490 }
491 #else
492 static void
493 osd_shape(cairo_t *cr) {
494 cairo_set_source_rgb (cr, OSD_COLOR_BG);
495 cairo_fill_preserve (cr);
496 cairo_set_source_rgb (cr, OSD_COLOR);
497 cairo_set_line_width (cr, 1);
498 cairo_stroke (cr);
499 }
500 #endif
501
502
503 static gboolean
504 osm_gps_map_in_circle(gint x, gint y, gint cx, gint cy, gint rad)
505 {
506 return( pow(cx - x, 2) + pow(cy - y, 2) < rad * rad);
507 }
508
509 #ifndef OSD_NO_DPAD
510 /* check whether x/y is within the dpad */
511 static osd_button_t
512 osd_check_dpad(gint x, gint y)
513 {
514 /* within entire dpad circle */
515 if( osm_gps_map_in_circle(x, y, D_RAD, D_RAD, D_RAD))
516 {
517 /* convert into position relative to dpads centre */
518 x -= D_RAD;
519 y -= D_RAD;
520
521 #ifdef OSD_GPS_BUTTON
522 /* check for dpad center goes here! */
523 if( osm_gps_map_in_circle(x, y, 0, 0, D_RAD/3))
524 return OSD_GPS;
525 #endif
526
527 if( y < 0 && abs(x) < abs(y))
528 return OSD_UP;
529
530 if( y > 0 && abs(x) < abs(y))
531 return OSD_DOWN;
532
533 if( x < 0 && abs(y) < abs(x))
534 return OSD_LEFT;
535
536 if( x > 0 && abs(y) < abs(x))
537 return OSD_RIGHT;
538
539 return OSD_BG;
540 }
541 return OSD_NONE;
542 }
543 #endif
544
545 /* check whether x/y is within the zoom pads */
546 static osd_button_t
547 osd_check_zoom(gint x, gint y) {
548 if( x > 0 && x < OSD_W && y > Z_TOP && y < Z_BOT) {
549
550 /* within circle around (-) label */
551 if( osm_gps_map_in_circle(x, y, Z_LEFT, Z_MID, Z_RAD))
552 return OSD_OUT;
553
554 /* within circle around (+) label */
555 if( osm_gps_map_in_circle(x, y, Z_RIGHT, Z_MID, Z_RAD))
556 return OSD_IN;
557
558 #if Z_GPS == 1
559 /* within square around center */
560 if( x > Z_CENTER - Z_RAD && x < Z_CENTER + Z_RAD)
561 return OSD_GPS;
562 #endif
563
564 /* between center of (-) button and center of entire zoom control area */
565 if(x > OSD_LEFT && x < D_RAD)
566 return OSD_OUT;
567
568 /* between center of (+) button and center of entire zoom control area */
569 if(x < OSD_RIGHT && x > D_RAD)
570 return OSD_IN;
571 }
572
573 return OSD_NONE;
574 }
575
576 #ifdef OSD_SOURCE_SEL
577
578 /* place source selection at right border */
579 #define OSD_S_RAD (Z_RAD)
580 #define OSD_S_X (-OSD_X)
581 #define OSD_S_Y (OSD_Y)
582 #define OSD_S_PW (2 * Z_RAD)
583 #define OSD_S_W (OSD_S_PW)
584 #define OSD_S_PH (2 * Z_RAD)
585 #define OSD_S_H (OSD_S_PH + OSD_SHADOW)
586
587 /* size of usable area when expanded */
588 #define OSD_S_AREA_W (priv->source_sel.width)
589 #define OSD_S_AREA_H (priv->source_sel.height)
590 #define OSD_S_EXP_W (OSD_S_PW + OSD_S_AREA_W + OSD_SHADOW)
591 #define OSD_S_EXP_H (OSD_S_AREA_H + OSD_SHADOW)
592
593 /* internal value to draw the arrow on the "puller" */
594 #define OSD_S_D0 (OSD_S_RAD/2)
595 #ifndef OSD_FONT_SIZE
596 #define OSD_FONT_SIZE 16.0
597 #endif
598 #define OSD_TEXT_BORDER (OSD_FONT_SIZE/2)
599 #define OSD_TEXT_SKIP (OSD_FONT_SIZE/8)
600
601 /* draw the shape of the source selection OSD, either only the puller (not expanded) */
602 /* or the entire menu incl. the puller (expanded) */
603 static void
604 osd_source_shape(osd_priv_t *priv, cairo_t *cr, gint x, gint y) {
605 if(!priv->source_sel.expanded) {
606 /* just draw the puller */
607 cairo_move_to (cr, x + OSD_S_PW, y + OSD_S_PH);
608 cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2);
609 cairo_line_to (cr, x + OSD_S_PW, y);
610 } else {
611 /* draw the puller and the area itself */
612 cairo_move_to (cr, x + OSD_S_PW + OSD_S_AREA_W, y + OSD_S_AREA_H);
613 cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_AREA_H);
614 if(OSD_S_Y > 0) {
615 cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_PH);
616 cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2);
617 } else {
618 cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_AREA_H-OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2);
619 cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_AREA_H - OSD_S_PH);
620 cairo_line_to (cr, x + OSD_S_PW, y);
621 }
622 cairo_line_to (cr, x + OSD_S_PW + OSD_S_AREA_W, y);
623 cairo_close_path (cr);
624 }
625 }
626
627 static void
628 osd_source_content(osm_gps_map_osd_t *osd, cairo_t *cr, gint offset) {
629 osd_priv_t *priv = (osd_priv_t*)osd->priv;
630
631 int py = offset + OSD_S_RAD - OSD_S_D0;
632
633 if(!priv->source_sel.expanded) {
634 /* draw the "puller" open (<) arrow */
635 cairo_move_to (cr, offset + OSD_S_RAD + OSD_S_D0/2, py);
636 cairo_rel_line_to (cr, -OSD_S_D0, +OSD_S_D0);
637 cairo_rel_line_to (cr, +OSD_S_D0, +OSD_S_D0);
638 } else {
639 if(OSD_S_Y < 0)
640 py += OSD_S_AREA_H - OSD_S_PH;
641
642 /* draw the "puller" close (>) arrow */
643 cairo_move_to (cr, offset + OSD_S_RAD - OSD_S_D0/2, py);
644 cairo_rel_line_to (cr, +OSD_S_D0, +OSD_S_D0);
645 cairo_rel_line_to (cr, -OSD_S_D0, +OSD_S_D0);
646 cairo_stroke(cr);
647
648 /* don't draw a shadow for the text content */
649 if(offset == 1) {
650 gint source;
651 g_object_get(osd->widget, "map-source", &source, NULL);
652
653 cairo_select_font_face (cr, "Sans",
654 CAIRO_FONT_SLANT_NORMAL,
655 CAIRO_FONT_WEIGHT_BOLD);
656 cairo_set_font_size (cr, OSD_FONT_SIZE);
657
658 int i, step = (priv->source_sel.height - 2*OSD_TEXT_BORDER) /
659 OSM_GPS_MAP_SOURCE_LAST;
660 for(i=OSM_GPS_MAP_SOURCE_NULL+1;i<=OSM_GPS_MAP_SOURCE_LAST;i++) {
661 cairo_text_extents_t extents;
662 const char *src = osm_gps_map_source_get_friendly_name(i);
663 cairo_text_extents (cr, src, &extents);
664
665 int x = offset + OSD_S_PW + OSD_TEXT_BORDER;
666 int y = offset + step * (i-1) + OSD_TEXT_BORDER;
667
668 /* draw filled rectangle if selected */
669 if(source == i) {
670 cairo_rectangle(cr, x - OSD_TEXT_BORDER/2,
671 y - OSD_TEXT_SKIP,
672 priv->source_sel.width - OSD_TEXT_BORDER,
673 step + OSD_TEXT_SKIP);
674 cairo_fill(cr);
675
676 /* temprarily draw with background color */
677 #ifndef OSD_COLOR
678 GdkColor bg = osd->widget->style->bg[GTK_STATE_NORMAL];
679 gdk_cairo_set_source_color(cr, &bg);
680 #else
681 cairo_set_source_rgb (cr, OSD_COLOR_BG);
682 #endif
683 }
684
685 cairo_move_to (cr, x, y + OSD_TEXT_SKIP - extents.y_bearing);
686 cairo_show_text (cr, src);
687
688 /* restore color */
689 if(source == i) {
690 #ifndef OSD_COLOR
691 GdkColor fg = osd->widget->style->fg[GTK_STATE_NORMAL];
692 gdk_cairo_set_source_color(cr, &fg);
693 #else
694 cairo_set_source_rgb (cr, OSD_COLOR);
695 #endif
696 }
697 }
698 }
699 }
700 }
701
702 static void
703 osd_render_source_sel(osm_gps_map_osd_t *osd, gboolean force_rerender) {
704 osd_priv_t *priv = (osd_priv_t*)osd->priv;
705
706 if(priv->source_sel.rendered && !force_rerender)
707 return;
708
709 priv->source_sel.rendered = TRUE;
710
711 #ifndef OSD_COLOR
712 GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL];
713 GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL];
714 GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE];
715 #endif
716
717 /* draw source selector */
718 cairo_t *cr = cairo_create(priv->source_sel.surface);
719 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
720 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
721 cairo_paint(cr);
722 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
723
724 #ifdef OSD_SHADOW_ENABLE
725 osd_source_shape(priv, cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
726 osd_shape_shadow(cr);
727 #endif
728
729 osd_source_shape(priv, cr, 1, 1);
730 #ifndef OSD_COLOR
731 osd_shape(cr, &bg, &fg);
732 #else
733 osd_shape(cr);
734 #endif
735
736 #ifdef OSD_SHADOW_ENABLE
737 osd_labels_shadow(cr, Z_RAD/3, TRUE);
738 osd_source_content(osd, cr, 1+OSD_LBL_SHADOW);
739 cairo_stroke (cr);
740 #endif
741 #ifndef OSD_COLOR
742 osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
743 #else
744 osd_labels(cr, Z_RAD/3, TRUE);
745 #endif
746 osd_source_content(osd, cr, 1);
747 cairo_stroke (cr);
748
749 cairo_destroy(cr);
750 }
751
752 /* re-allocate the buffer used to draw the menu. This is used */
753 /* to collapse/expand the buffer */
754 static void
755 osd_source_reallocate(osm_gps_map_osd_t *osd) {
756 osd_priv_t *priv = (osd_priv_t*)osd->priv;
757
758 /* re-allocate offscreen bitmap */
759 g_assert (priv->source_sel.surface);
760
761 int w = OSD_S_W, h = OSD_S_H;
762 if(priv->source_sel.expanded) {
763 cairo_text_extents_t extents;
764
765 /* determine content size */
766 cairo_t *cr = cairo_create(priv->source_sel.surface);
767 cairo_select_font_face (cr, "Sans",
768 CAIRO_FONT_SLANT_NORMAL,
769 CAIRO_FONT_WEIGHT_BOLD);
770 cairo_set_font_size (cr, OSD_FONT_SIZE);
771
772 /* calculate menu size */
773 int i, max_h = 0, max_w = 0;
774 for(i=OSM_GPS_MAP_SOURCE_NULL+1;i<=OSM_GPS_MAP_SOURCE_LAST;i++) {
775 const char *src = osm_gps_map_source_get_friendly_name(i);
776 cairo_text_extents (cr, src, &extents);
777
778 if(extents.width > max_w) max_w = extents.width;
779 if(extents.height > max_h) max_h = extents.height;
780 }
781 cairo_destroy(cr);
782
783 priv->source_sel.width = max_w + 2*OSD_TEXT_BORDER;
784 priv->source_sel.height = OSM_GPS_MAP_SOURCE_LAST *
785 (max_h + 2*OSD_TEXT_SKIP) + 2*OSD_TEXT_BORDER;
786
787 w = OSD_S_EXP_W;
788 h = OSD_S_EXP_H;
789 }
790
791 cairo_surface_destroy(priv->source_sel.surface);
792 priv->source_sel.surface =
793 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w+2, h+2);
794
795 osd_render_source_sel(osd, TRUE);
796 }
797
798 #define OSD_HZ 15
799 #define OSD_TIME 500
800
801 static gboolean osd_source_animate(gpointer data) {
802 osm_gps_map_osd_t *osd = (osm_gps_map_osd_t*)data;
803 osd_priv_t *priv = (osd_priv_t*)osd->priv;
804 int diff = OSD_S_EXP_W - OSD_S_W - OSD_S_X;
805 gboolean done = FALSE;
806 priv->source_sel.count += priv->source_sel.dir;
807
808 /* shifting in */
809 if(priv->source_sel.dir < 0) {
810 if(priv->source_sel.count <= 0) {
811 priv->source_sel.count = 0;
812 done = TRUE;
813 }
814 } else {
815 if(priv->source_sel.count >= 1000) {
816 priv->source_sel.expanded = FALSE;
817 osd_source_reallocate(osd);
818
819 priv->source_sel.count = 1000;
820 done = TRUE;
821 }
822 }
823
824
825 /* count runs linearly from 0 to 1000, map this nicely onto a position */
826
827 /* nice sinoid mapping */
828 float m = 0.5-cos(priv->source_sel.count * M_PI / 1000.0)/2;
829 priv->source_sel.shift =
830 (osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X) +
831 m * diff;
832
833 /* make sure the screen is updated */
834 osm_gps_map_repaint(OSM_GPS_MAP(osd->widget));
835
836 /* stop animation if done */
837 if(done)
838 priv->source_sel.handler_id = 0;
839
840 return !done;
841 }
842
843 /* switch between expand and collapse mode of source selection */
844 static void
845 osd_source_toggle(osm_gps_map_osd_t *osd)
846 {
847 osd_priv_t *priv = (osd_priv_t*)osd->priv;
848
849 /* ignore clicks while animation is running */
850 if(priv->source_sel.handler_id)
851 return;
852
853 /* expand immediately, collapse is handle at the end of the */
854 /* collapse animation */
855 if(!priv->source_sel.expanded) {
856 priv->source_sel.expanded = TRUE;
857 osd_source_reallocate(osd);
858
859 priv->source_sel.count = 1000;
860 priv->source_sel.shift = osd->widget->allocation.width - OSD_S_W;
861 priv->source_sel.dir = -1000/OSD_HZ;
862 } else {
863 priv->source_sel.count = 0;
864 priv->source_sel.shift = osd->widget->allocation.width -
865 OSD_S_EXP_W + OSD_S_X;
866 priv->source_sel.dir = +1000/OSD_HZ;
867 }
868
869 /* start timer to handle animation */
870 priv->source_sel.handler_id = gtk_timeout_add(OSD_TIME/OSD_HZ,
871 osd_source_animate, osd);
872 }
873
874 /* check if the user clicked inside the source selection area */
875 static osd_button_t
876 osd_source_check(osm_gps_map_osd_t *osd, gboolean down, gint x, gint y) {
877 osd_priv_t *priv = (osd_priv_t*)osd->priv;
878
879 if(!priv->source_sel.expanded)
880 x -= osd->widget->allocation.width - OSD_S_W;
881 else
882 x -= osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
883
884 if(OSD_S_Y > 0)
885 y -= OSD_S_Y;
886 else
887 y -= osd->widget->allocation.height - OSD_S_PH + OSD_S_Y;
888
889 /* within square around puller? */
890 if(y > 0 && y < OSD_S_PH && x > 0 && x < OSD_S_PW) {
891 /* really within puller shape? */
892 if(x > Z_RAD || osm_gps_map_in_circle(x, y, Z_RAD, Z_RAD, Z_RAD)) {
893 /* expand source selector */
894 if(down)
895 osd_source_toggle(osd);
896
897 /* tell upper layers that user clicked some background element */
898 /* of the OSD */
899 return OSD_BG;
900 }
901 }
902
903 /* check for clicks into data area */
904 if(priv->source_sel.expanded && !priv->source_sel.handler_id) {
905 /* re-adjust from puller top to content top */
906 if(OSD_S_Y < 0)
907 y += OSD_S_EXP_H - OSD_S_PH;
908
909 if(x > OSD_S_PW &&
910 x < OSD_S_PW + OSD_S_EXP_W &&
911 y > 0 &&
912 y < OSD_S_EXP_H) {
913
914 int step = (priv->source_sel.height - 2*OSD_TEXT_BORDER)
915 / OSM_GPS_MAP_SOURCE_LAST;
916
917 y -= OSD_TEXT_BORDER - OSD_TEXT_SKIP;
918 y /= step;
919 y += 1;
920
921 if(down) {
922 gint old = 0;
923 g_object_get(osd->widget, "map-source", &old, NULL);
924
925 if(y > OSM_GPS_MAP_SOURCE_NULL &&
926 y <= OSM_GPS_MAP_SOURCE_LAST &&
927 old != y) {
928 g_object_set(osd->widget, "map-source", y, NULL);
929
930 osd_render_source_sel(osd, TRUE);
931 osm_gps_map_repaint(OSM_GPS_MAP(osd->widget));
932 }
933 }
934
935 /* return "clicked in OSD background" to prevent further */
936 /* processing by application */
937 return OSD_BG;
938 }
939 }
940
941 return OSD_NONE;
942 }
943 #endif // OSD_SOURCE_SEL
944
945 static osd_button_t
946 osd_check(osm_gps_map_osd_t *osd, gboolean down, gint x, gint y) {
947 osd_button_t but = OSD_NONE;
948
949 #ifdef OSD_BALLOON
950 if(down) {
951 /* needed to handle balloons that are created at click */
952 osd_priv_t *priv = (osd_priv_t*)osd->priv;
953 priv->balloon.just_created = FALSE;
954 }
955 #endif
956
957 #ifdef OSD_SOURCE_SEL
958 /* the source selection area is handles internally */
959 but = osd_source_check(osd, down, x, y);
960 #endif
961
962 if(but == OSD_NONE) {
963 gint mx = x - OSD_X;
964 gint my = y - OSD_Y;
965
966 if(OSD_X < 0)
967 mx -= (osd->widget->allocation.width - OSD_W);
968
969 if(OSD_Y < 0)
970 my -= (osd->widget->allocation.height - OSD_H);
971
972 /* first do a rough test for the OSD area. */
973 /* this is just to avoid an unnecessary detailed test */
974 if(mx > 0 && mx < OSD_W && my > 0 && my < OSD_H) {
975 #ifndef OSD_NO_DPAD
976 but = osd_check_dpad(mx, my);
977 #endif
978 }
979
980 if(but == OSD_NONE)
981 but = osd_check_zoom(mx, my);
982 }
983
984 #ifdef OSD_BALLOON
985 if(but == OSD_NONE) {
986 /* check if user clicked into balloon */
987 if(osd_balloon_check(osd, down, x, y))
988 but = OSD_BG;
989 }
990 #endif
991
992 return but;
993 }
994
995 #ifndef OSD_NO_DPAD
996 static void
997 osd_dpad_labels(cairo_t *cr, gint x, gint y) {
998 /* move reference to dpad center */
999 x += D_RAD;
1000 y += D_RAD;
1001
1002 const static gint offset[][3][2] = {
1003 /* left arrow/triangle */
1004 { { -D_TIP+D_LEN, -D_WID }, { -D_LEN, D_WID }, { +D_LEN, D_WID } },
1005 /* right arrow/triangle */
1006 { { +D_TIP-D_LEN, -D_WID }, { +D_LEN, D_WID }, { -D_LEN, D_WID } },
1007 /* top arrow/triangle */
1008 { { -D_WID, -D_TIP+D_LEN }, { D_WID, -D_LEN }, { D_WID, +D_LEN } },
1009 /* bottom arrow/triangle */
1010 { { -D_WID, +D_TIP-D_LEN }, { D_WID, +D_LEN }, { D_WID, -D_LEN } }
1011 };
1012
1013 int i;
1014 for(i=0;i<4;i++) {
1015 cairo_move_to (cr, x + offset[i][0][0], y + offset[i][0][1]);
1016 cairo_rel_line_to (cr, offset[i][1][0], offset[i][1][1]);
1017 cairo_rel_line_to (cr, offset[i][2][0], offset[i][2][1]);
1018 }
1019 }
1020 #endif
1021
1022 #ifdef OSD_GPS_BUTTON
1023 /* draw the satellite dish icon in the center of the dpad */
1024 #define GPS_V0 (D_RAD/7)
1025 #define GPS_V1 (D_RAD/10)
1026 #define GPS_V2 (D_RAD/5)
1027
1028 /* draw a satellite receiver dish */
1029 /* this is either drawn in the center of the dpad (if present) */
1030 /* or in the middle of the zoom area */
1031 static void
1032 osd_dpad_gps(cairo_t *cr, gint x, gint y) {
1033 /* move reference to dpad center */
1034 x += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD * 3;
1035 y += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD + GPS_V0;
1036
1037 cairo_move_to (cr, x-GPS_V0, y+GPS_V0);
1038 cairo_rel_line_to (cr, +GPS_V0, -GPS_V0);
1039 cairo_rel_line_to (cr, +GPS_V0, +GPS_V0);
1040 cairo_close_path (cr);
1041
1042 cairo_move_to (cr, x+GPS_V1-GPS_V2, y-2*GPS_V2);
1043 cairo_curve_to (cr, x-GPS_V2, y, x+GPS_V1, y+GPS_V1, x+GPS_V1+GPS_V2, y);
1044 cairo_close_path (cr);
1045
1046 x += GPS_V1;
1047 cairo_move_to (cr, x, y-GPS_V2);
1048 cairo_rel_line_to (cr, +GPS_V1, -GPS_V1);
1049 }
1050 #endif
1051
1052 #define Z_LEN (2*Z_RAD/3)
1053
1054 static void
1055 osd_zoom_labels(cairo_t *cr, gint x, gint y) {
1056 cairo_move_to (cr, x + Z_LEFT - Z_LEN, y + Z_MID);
1057 cairo_line_to (cr, x + Z_LEFT + Z_LEN, y + Z_MID);
1058
1059 cairo_move_to (cr, x + Z_RIGHT, y + Z_MID - Z_LEN);
1060 cairo_line_to (cr, x + Z_RIGHT, y + Z_MID + Z_LEN);
1061 cairo_move_to (cr, x + Z_RIGHT - Z_LEN, y + Z_MID);
1062 cairo_line_to (cr, x + Z_RIGHT + Z_LEN, y + Z_MID);
1063 }
1064
1065 #ifdef OSD_COORDINATES
1066
1067 #ifndef OSD_COORDINATES_FONT_SIZE
1068 #define OSD_COORDINATES_FONT_SIZE 12
1069 #endif
1070
1071 #define OSD_COORDINATES_OFFSET (OSD_COORDINATES_FONT_SIZE/6)
1072
1073 #define OSD_COORDINATES_W (8*OSD_COORDINATES_FONT_SIZE+2*OSD_COORDINATES_OFFSET)
1074 #define OSD_COORDINATES_H (2*OSD_COORDINATES_FONT_SIZE+OSD_COORDINATES_OFFSET)
1075
1076 /* these can be overwritten with versions that support */
1077 /* localization */
1078 #ifndef OSD_COORDINATES_CHR_N
1079 #define OSD_COORDINATES_CHR_N "N"
1080 #endif
1081 #ifndef OSD_COORDINATES_CHR_S
1082 #define OSD_COORDINATES_CHR_S "S"
1083 #endif
1084 #ifndef OSD_COORDINATES_CHR_E
1085 #define OSD_COORDINATES_CHR_E "E"
1086 #endif
1087 #ifndef OSD_COORDINATES_CHR_W
1088 #define OSD_COORDINATES_CHR_W "W"
1089 #endif
1090
1091
1092
1093 /* this is the classic geocaching notation */
1094 static char
1095 *osd_latitude_str(float latitude) {
1096 char *c = OSD_COORDINATES_CHR_N;
1097 float integral, fractional;
1098
1099 if(isnan(latitude))
1100 return NULL;
1101
1102 if(latitude < 0) {
1103 latitude = fabs(latitude);
1104 c = OSD_COORDINATES_CHR_S;
1105 }
1106
1107 fractional = modff(latitude, &integral);
1108
1109 return g_strdup_printf("%s %02d° %06.3f'",
1110 c, (int)integral, fractional*60.0);
1111 }
1112
1113 static char
1114 *osd_longitude_str(float longitude) {
1115 char *c = OSD_COORDINATES_CHR_E;
1116 float integral, fractional;
1117
1118 if(isnan(longitude))
1119 return NULL;
1120
1121 if(longitude < 0) {
1122 longitude = fabs(longitude);
1123 c = OSD_COORDINATES_CHR_W;
1124 }
1125
1126 fractional = modff(longitude, &integral);
1127
1128 return g_strdup_printf("%s %03d° %06.3f'",
1129 c, (int)integral, fractional*60.0);
1130 }
1131
1132 static void
1133 osd_render_coordinates(osm_gps_map_osd_t *osd)
1134 {
1135 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1136
1137 /* get current map position */
1138 gfloat lat, lon;
1139 g_object_get(osd->widget, "latitude", &lat, "longitude", &lon, NULL);
1140
1141 /* check if position has changed enough to require redraw */
1142 if(!isnan(priv->coordinates.lat) && !isnan(priv->coordinates.lon))
1143 /* 1/60000 == 1/1000 minute */
1144 if((fabsf(lat - priv->coordinates.lat) < 1/60000) &&
1145 (fabsf(lon - priv->coordinates.lon) < 1/60000))
1146 return;
1147
1148 priv->coordinates.lat = lat;
1149 priv->coordinates.lon = lon;
1150
1151 /* first fill with transparency */
1152 cairo_t *cr = cairo_create(priv->coordinates.surface);
1153 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1154 // cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.5);
1155 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
1156 cairo_paint(cr);
1157 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1158
1159 cairo_select_font_face (cr, "Sans",
1160 CAIRO_FONT_SLANT_NORMAL,
1161 CAIRO_FONT_WEIGHT_BOLD);
1162 cairo_set_font_size (cr, OSD_COORDINATES_FONT_SIZE);
1163
1164 char *latitude = osd_latitude_str(lat);
1165 char *longitude = osd_longitude_str(lon);
1166
1167 cairo_text_extents_t lat_extents, lon_extents;
1168 cairo_text_extents (cr, latitude, &lat_extents);
1169 cairo_text_extents (cr, longitude, &lon_extents);
1170
1171 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1172 cairo_set_line_width (cr, OSD_COORDINATES_FONT_SIZE/6);
1173 cairo_move_to (cr,
1174 (OSD_COORDINATES_W - lat_extents.width)/2,
1175 OSD_COORDINATES_OFFSET - lat_extents.y_bearing);
1176 cairo_text_path (cr, latitude);
1177 cairo_move_to (cr,
1178 (OSD_COORDINATES_W - lon_extents.width)/2,
1179 OSD_COORDINATES_OFFSET - lon_extents.y_bearing +
1180 OSD_COORDINATES_FONT_SIZE);
1181 cairo_text_path (cr, longitude);
1182 cairo_stroke (cr);
1183
1184 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
1185 cairo_move_to (cr,
1186 (OSD_COORDINATES_W - lat_extents.width)/2,
1187 OSD_COORDINATES_OFFSET - lat_extents.y_bearing);
1188 cairo_show_text (cr, latitude);
1189 cairo_move_to (cr,
1190 (OSD_COORDINATES_W - lon_extents.width)/2,
1191 OSD_COORDINATES_OFFSET - lon_extents.y_bearing +
1192 OSD_COORDINATES_FONT_SIZE);
1193 cairo_show_text (cr, longitude);
1194
1195 g_free(latitude);
1196 g_free(longitude);
1197
1198 cairo_destroy(cr);
1199 }
1200 #endif // OSD_COORDINATES
1201
1202 #ifdef OSD_NAV
1203 #define OSD_NAV_W (100)
1204 #define OSD_NAV_H (100)
1205
1206 static void
1207 osd_render_nav(osm_gps_map_osd_t *osd)
1208 {
1209 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1210
1211 /* first fill with transparency */
1212 cairo_t *cr = cairo_create(priv->nav.surface);
1213 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1214 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.2);
1215 cairo_paint(cr);
1216 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1217
1218 cairo_destroy(cr);
1219 }
1220
1221 #endif // OSD_NAV
1222
1223
1224 #ifdef OSD_CROSSHAIR
1225
1226 #ifndef OSD_CROSSHAIR_RADIUS
1227 #define OSD_CROSSHAIR_RADIUS 10
1228 #endif
1229
1230 #define OSD_CROSSHAIR_TICK (OSD_CROSSHAIR_RADIUS/2)
1231 #define OSD_CROSSHAIR_BORDER (OSD_CROSSHAIR_TICK + OSD_CROSSHAIR_RADIUS/4)
1232 #define OSD_CROSSHAIR_W ((OSD_CROSSHAIR_RADIUS+OSD_CROSSHAIR_BORDER)*2)
1233 #define OSD_CROSSHAIR_H ((OSD_CROSSHAIR_RADIUS+OSD_CROSSHAIR_BORDER)*2)
1234
1235 static void
1236 osd_render_crosshair_shape(cairo_t *cr) {
1237 cairo_arc (cr, OSD_CROSSHAIR_W/2, OSD_CROSSHAIR_H/2,
1238 OSD_CROSSHAIR_RADIUS, 0, 2*M_PI);
1239
1240 cairo_move_to (cr, OSD_CROSSHAIR_W/2 - OSD_CROSSHAIR_RADIUS,
1241 OSD_CROSSHAIR_H/2);
1242 cairo_rel_line_to (cr, -OSD_CROSSHAIR_TICK, 0);
1243 cairo_move_to (cr, OSD_CROSSHAIR_W/2 + OSD_CROSSHAIR_RADIUS,
1244 OSD_CROSSHAIR_H/2);
1245 cairo_rel_line_to (cr, OSD_CROSSHAIR_TICK, 0);
1246
1247 cairo_move_to (cr, OSD_CROSSHAIR_W/2,
1248 OSD_CROSSHAIR_H/2 - OSD_CROSSHAIR_RADIUS);
1249 cairo_rel_line_to (cr, 0, -OSD_CROSSHAIR_TICK);
1250 cairo_move_to (cr, OSD_CROSSHAIR_W/2,
1251 OSD_CROSSHAIR_H/2 + OSD_CROSSHAIR_RADIUS);
1252 cairo_rel_line_to (cr, 0, OSD_CROSSHAIR_TICK);
1253
1254 cairo_stroke (cr);
1255 }
1256
1257 static void
1258 osd_render_crosshair(osm_gps_map_osd_t *osd)
1259 {
1260 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1261
1262 if(priv->crosshair.rendered)
1263 return;
1264
1265 priv->crosshair.rendered = TRUE;
1266
1267 /* first fill with transparency */
1268 cairo_t *cr = cairo_create(priv->crosshair.surface);
1269 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1270 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
1271 cairo_paint(cr);
1272 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1273
1274 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1275
1276 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.5);
1277 cairo_set_line_width (cr, OSD_CROSSHAIR_RADIUS/2);
1278 osd_render_crosshair_shape(cr);
1279
1280 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1281 cairo_set_line_width (cr, OSD_CROSSHAIR_RADIUS/4);
1282 osd_render_crosshair_shape(cr);
1283
1284 cairo_destroy(cr);
1285 }
1286 #endif
1287
1288 #ifdef OSD_SCALE
1289
1290 #ifndef OSD_SCALE_FONT_SIZE
1291 #define OSD_SCALE_FONT_SIZE 12
1292 #endif
1293 #define OSD_SCALE_W (10*OSD_SCALE_FONT_SIZE)
1294 #define OSD_SCALE_H (5*OSD_SCALE_FONT_SIZE/2)
1295
1296 /* various parameters used to create the scale */
1297 #define OSD_SCALE_H2 (OSD_SCALE_H/2)
1298 #define OSD_SCALE_TICK (2*OSD_SCALE_FONT_SIZE/3)
1299 #define OSD_SCALE_M (OSD_SCALE_H2 - OSD_SCALE_TICK)
1300 #define OSD_SCALE_I (OSD_SCALE_H2 + OSD_SCALE_TICK)
1301 #define OSD_SCALE_FD (OSD_SCALE_FONT_SIZE/4)
1302
1303 static void
1304 osd_render_scale(osm_gps_map_osd_t *osd)
1305 {
1306 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1307
1308 /* this only needs to be rendered if the zoom has changed */
1309 gint zoom;
1310 g_object_get(OSM_GPS_MAP(osd->widget), "zoom", &zoom, NULL);
1311 if(zoom == priv->scale.zoom)
1312 return;
1313
1314 priv->scale.zoom = zoom;
1315
1316 float m_per_pix = osm_gps_map_get_scale(OSM_GPS_MAP(osd->widget));
1317
1318 /* first fill with transparency */
1319 cairo_t *cr = cairo_create(priv->scale.surface);
1320 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1321 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
1322 // pink for testing: cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.2);
1323 cairo_paint(cr);
1324 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1325
1326 /* determine the size of the scale width in meters */
1327 float width = (OSD_SCALE_W-OSD_SCALE_FONT_SIZE/6) * m_per_pix;
1328
1329 /* scale this to useful values */
1330 int exp = logf(width)*M_LOG10E;
1331 int mant = width/pow(10,exp);
1332 int width_metric = mant * pow(10,exp);
1333 char *dist_str = NULL;
1334 if(width_metric<1000)
1335 dist_str = g_strdup_printf("%u m", width_metric);
1336 else
1337 dist_str = g_strdup_printf("%u km", width_metric/1000);
1338 width_metric /= m_per_pix;
1339
1340 /* and now the hard part: scale for useful imperial values :-( */
1341 /* try to convert to feet, 1ft == 0.3048 m */
1342 width /= 0.3048;
1343 float imp_scale = 0.3048;
1344 char *dist_imp_unit = "ft";
1345
1346 if(width >= 100) {
1347 /* 1yd == 3 feet */
1348 width /= 3.0;
1349 imp_scale *= 3.0;
1350 dist_imp_unit = "yd";
1351
1352 if(width >= 1760.0) {
1353 /* 1mi == 1760 yd */
1354 width /= 1760.0;
1355 imp_scale *= 1760.0;
1356 dist_imp_unit = "mi";
1357 }
1358 }
1359
1360 /* also convert this to full tens/hundreds */
1361 exp = logf(width)*M_LOG10E;
1362 mant = width/pow(10,exp);
1363 int width_imp = mant * pow(10,exp);
1364 char *dist_str_imp = g_strdup_printf("%u %s", width_imp, dist_imp_unit);
1365
1366 /* convert back to pixels */
1367 width_imp *= imp_scale;
1368 width_imp /= m_per_pix;
1369
1370 cairo_select_font_face (cr, "Sans",
1371 CAIRO_FONT_SLANT_NORMAL,
1372 CAIRO_FONT_WEIGHT_BOLD);
1373 cairo_set_font_size (cr, OSD_SCALE_FONT_SIZE);
1374 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
1375
1376 cairo_text_extents_t extents;
1377 cairo_text_extents (cr, dist_str, &extents);
1378
1379 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1380 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/6);
1381 cairo_move_to (cr, 2*OSD_SCALE_FD, OSD_SCALE_H2-OSD_SCALE_FD);
1382 cairo_text_path (cr, dist_str);
1383 cairo_stroke (cr);
1384 cairo_move_to (cr, 2*OSD_SCALE_FD,
1385 OSD_SCALE_H2+OSD_SCALE_FD + extents.height);
1386 cairo_text_path (cr, dist_str_imp);
1387 cairo_stroke (cr);
1388
1389 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
1390 cairo_move_to (cr, 2*OSD_SCALE_FD, OSD_SCALE_H2-OSD_SCALE_FD);
1391 cairo_show_text (cr, dist_str);
1392 cairo_move_to (cr, 2*OSD_SCALE_FD,
1393 OSD_SCALE_H2+OSD_SCALE_FD + extents.height);
1394 cairo_show_text (cr, dist_str_imp);
1395
1396 g_free(dist_str);
1397 g_free(dist_str_imp);
1398
1399 /* draw white line */
1400 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1401 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
1402 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/3);
1403 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_M);
1404 cairo_rel_line_to (cr, 0, OSD_SCALE_TICK);
1405 cairo_rel_line_to (cr, width_metric, 0);
1406 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1407 cairo_stroke(cr);
1408 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_I);
1409 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1410 cairo_rel_line_to (cr, width_imp, 0);
1411 cairo_rel_line_to (cr, 0, +OSD_SCALE_TICK);
1412 cairo_stroke(cr);
1413
1414 /* draw black line */
1415 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
1416 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/6);
1417 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_M);
1418 cairo_rel_line_to (cr, 0, OSD_SCALE_TICK);
1419 cairo_rel_line_to (cr, width_metric, 0);
1420 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1421 cairo_stroke(cr);
1422 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_I);
1423 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1424 cairo_rel_line_to (cr, width_imp, 0);
1425 cairo_rel_line_to (cr, 0, +OSD_SCALE_TICK);
1426 cairo_stroke(cr);
1427
1428 cairo_destroy(cr);
1429 }
1430 #endif
1431
1432 static void
1433 osd_render_controls(osm_gps_map_osd_t *osd)
1434 {
1435 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1436
1437 if(priv->controls.rendered
1438 #ifdef OSD_GPS_BUTTON
1439 && (priv->controls.gps_enabled == (osd->cb != NULL))
1440 #endif
1441 )
1442 return;
1443
1444 #ifdef OSD_GPS_BUTTON
1445 priv->controls.gps_enabled = (osd->cb != NULL);
1446 #endif
1447 priv->controls.rendered = TRUE;
1448
1449 #ifndef OSD_COLOR
1450 GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL];
1451 GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL];
1452 GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE];
1453 #endif
1454
1455 /* first fill with transparency */
1456 cairo_t *cr = cairo_create(priv->controls.surface);
1457 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1458 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
1459 cairo_paint(cr);
1460 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1461
1462 /* --------- draw zoom and dpad shape shadow ----------- */
1463 #ifdef OSD_SHADOW_ENABLE
1464 osd_zoom_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
1465 osd_shape_shadow(cr);
1466 #ifndef OSD_NO_DPAD
1467 osd_dpad_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
1468 osd_shape_shadow(cr);
1469 #endif
1470 #endif
1471
1472 /* --------- draw zoom and dpad shape ----------- */
1473
1474 osd_zoom_shape(cr, 1, 1);
1475 #ifndef OSD_COLOR
1476 osd_shape(cr, &bg, &fg);
1477 #else
1478 osd_shape(cr);
1479 #endif
1480 #ifndef OSD_NO_DPAD
1481 osd_dpad_shape(cr, 1, 1);
1482 #ifndef OSD_COLOR
1483 osd_shape(cr, &bg, &fg);
1484 #else
1485 osd_shape(cr);
1486 #endif
1487 #endif
1488
1489 /* --------- draw zoom and dpad labels --------- */
1490
1491 #ifdef OSD_SHADOW_ENABLE
1492 osd_labels_shadow(cr, Z_RAD/3, TRUE);
1493 osd_zoom_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1494 #ifndef OSD_NO_DPAD
1495 osd_dpad_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1496 #endif
1497 cairo_stroke(cr);
1498 #ifdef OSD_GPS_BUTTON
1499 osd_labels_shadow(cr, Z_RAD/6, osd->cb != NULL);
1500 osd_dpad_gps(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1501 cairo_stroke(cr);
1502 #endif
1503 #endif
1504
1505 #ifndef OSD_COLOR
1506 osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
1507 #else
1508 osd_labels(cr, Z_RAD/3, TRUE);
1509 #endif
1510 osd_zoom_labels(cr, 1, 1);
1511 #ifndef OSD_NO_DPAD
1512 osd_dpad_labels(cr, 1, 1);
1513 #endif
1514 cairo_stroke(cr);
1515
1516 #ifndef OSD_COLOR
1517 osd_labels(cr, Z_RAD/6, osd->cb != NULL, &fg, &da);
1518 #else
1519 osd_labels(cr, Z_RAD/6, osd->cb != NULL);
1520 #endif
1521 #ifdef OSD_GPS_BUTTON
1522 osd_dpad_gps(cr, 1, 1);
1523 #endif
1524 cairo_stroke(cr);
1525
1526 cairo_destroy(cr);
1527 }
1528
1529 static void
1530 osd_render(osm_gps_map_osd_t *osd)
1531 {
1532 /* this function is actually called pretty often since the */
1533 /* OSD contents may have changed (due to a coordinate/zoom change). */
1534 /* The different OSD parts have to make sure that they don't */
1535 /* render unneccessarily often and thus waste CPU power */
1536
1537 osd_render_controls(osd);
1538
1539 #ifdef OSD_SOURCE_SEL
1540 osd_render_source_sel(osd, FALSE);
1541 #endif
1542
1543 #ifdef OSD_SCALE
1544 osd_render_scale(osd);
1545 #endif
1546
1547 #ifdef OSD_CROSSHAIR
1548 osd_render_crosshair(osd);
1549 #endif
1550
1551 #ifdef OSD_NAV
1552 osd_render_nav(osd);
1553 #endif
1554
1555 #ifdef OSD_COORDINATES
1556 osd_render_coordinates(osd);
1557 #endif
1558 }
1559
1560 static void
1561 osd_draw(osm_gps_map_osd_t *osd, GdkDrawable *drawable)
1562 {
1563 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1564
1565 /* OSD itself uses some off-screen rendering, so check if the */
1566 /* offscreen buffer is present and create it if not */
1567 if(!priv->controls.surface) {
1568 /* create overlay ... */
1569 priv->controls.surface =
1570 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W+2, OSD_H+2);
1571
1572 priv->controls.rendered = FALSE;
1573 #ifdef OSD_GPS_BUTTON
1574 priv->controls.gps_enabled = FALSE;
1575 #endif
1576
1577 #ifdef OSD_SOURCE_SEL
1578 /* the initial OSD state is alway not-expanded */
1579 priv->source_sel.surface =
1580 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1581 OSD_S_W+2, OSD_S_H+2);
1582 priv->source_sel.rendered = FALSE;
1583 #endif
1584
1585 #ifdef OSD_SCALE
1586 priv->scale.surface =
1587 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1588 OSD_SCALE_W, OSD_SCALE_H);
1589 priv->scale.zoom = -1;
1590 #endif
1591
1592 #ifdef OSD_CROSSHAIR
1593 priv->crosshair.surface =
1594 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1595 OSD_CROSSHAIR_W, OSD_CROSSHAIR_H);
1596 priv->crosshair.rendered = FALSE;
1597 #endif
1598
1599 #ifdef OSD_NAV
1600 priv->nav.surface =
1601 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1602 OSD_NAV_W, OSD_NAV_H);
1603 #endif
1604
1605 #ifdef OSD_COORDINATES
1606 priv->coordinates.surface =
1607 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1608 OSD_COORDINATES_W, OSD_COORDINATES_H);
1609
1610 priv->coordinates.lat = priv->coordinates.lon = OSM_GPS_MAP_INVALID;
1611 #endif
1612
1613 /* ... and render it */
1614 osd_render(osd);
1615 }
1616
1617 // now draw this onto the original context
1618 cairo_t *cr = gdk_cairo_create(drawable);
1619
1620 gint x, y;
1621
1622 #ifdef OSD_SCALE
1623 x = OSD_X;
1624 y = -OSD_Y;
1625 if(x < 0) x += osd->widget->allocation.width - OSD_SCALE_W;
1626 if(y < 0) y += osd->widget->allocation.height - OSD_SCALE_H;
1627
1628 cairo_set_source_surface(cr, priv->scale.surface, x, y);
1629 cairo_paint(cr);
1630 #endif
1631
1632 #ifdef OSD_CROSSHAIR
1633 x = (osd->widget->allocation.width - OSD_CROSSHAIR_W)/2;
1634 y = (osd->widget->allocation.height - OSD_CROSSHAIR_H)/2;
1635
1636 cairo_set_source_surface(cr, priv->crosshair.surface, x, y);
1637 cairo_paint(cr);
1638 #endif
1639
1640 #ifdef OSD_NAV
1641 x = OSD_X;
1642 if(x < 0) x += osd->widget->allocation.width - OSD_NAV_W;
1643 y = (osd->widget->allocation.height - OSD_NAV_H)/2;
1644
1645 cairo_set_source_surface(cr, priv->nav.surface, x, y);
1646 cairo_paint(cr);
1647 #endif
1648
1649 #ifdef OSD_COORDINATES
1650 x = -OSD_X;
1651 y = -OSD_Y;
1652 if(x < 0) x += osd->widget->allocation.width - OSD_COORDINATES_W;
1653 if(y < 0) y += osd->widget->allocation.height - OSD_COORDINATES_H;
1654
1655 cairo_set_source_surface(cr, priv->coordinates.surface, x, y);
1656 cairo_paint(cr);
1657 #endif
1658
1659 #ifdef OSD_BALLOON
1660 if(priv->balloon.surface) {
1661
1662 /* convert given lat lon into screen coordinates */
1663 gint x, y;
1664 osm_gps_map_geographic_to_screen (OSM_GPS_MAP(osd->widget),
1665 priv->balloon.lat, priv->balloon.lon,
1666 &x, &y);
1667
1668 /* check if balloon needs to be rerendered */
1669 osd_render_balloon(osd);
1670
1671 cairo_set_source_surface(cr, priv->balloon.surface,
1672 x + priv->balloon.offset_x,
1673 y + priv->balloon.offset_y);
1674 cairo_paint(cr);
1675 }
1676 #endif
1677
1678 x = OSD_X;
1679 if(x < 0)
1680 x += osd->widget->allocation.width - OSD_W;
1681
1682 y = OSD_Y;
1683 if(y < 0)
1684 y += osd->widget->allocation.height - OSD_H;
1685
1686 cairo_set_source_surface(cr, priv->controls.surface, x, y);
1687 cairo_paint(cr);
1688
1689 #ifdef OSD_SOURCE_SEL
1690 if(!priv->source_sel.handler_id) {
1691 /* the OSD source selection is not being animated */
1692 if(!priv->source_sel.expanded)
1693 x = osd->widget->allocation.width - OSD_S_W;
1694 else
1695 x = osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
1696 } else
1697 x = priv->source_sel.shift;
1698
1699 y = OSD_S_Y;
1700 if(OSD_S_Y < 0) {
1701 if(!priv->source_sel.expanded)
1702 y = osd->widget->allocation.height - OSD_S_H + OSD_S_Y;
1703 else
1704 y = osd->widget->allocation.height - OSD_S_EXP_H + OSD_S_Y;
1705 }
1706
1707 cairo_set_source_surface(cr, priv->source_sel.surface, x, y);
1708 cairo_paint(cr);
1709 #endif
1710
1711 cairo_destroy(cr);
1712 }
1713
1714 static void
1715 osd_free(osm_gps_map_osd_t *osd)
1716 {
1717 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
1718
1719 if (priv->controls.surface)
1720 cairo_surface_destroy(priv->controls.surface);
1721
1722 #ifdef OSD_SOURCE_SEL
1723 if(priv->source_sel.handler_id)
1724 gtk_timeout_remove(priv->source_sel.handler_id);
1725
1726 if (priv->source_sel.surface)
1727 cairo_surface_destroy(priv->source_sel.surface);
1728 #endif
1729
1730 #ifdef OSD_SCALE
1731 if (priv->scale.surface)
1732 cairo_surface_destroy(priv->scale.surface);
1733 #endif
1734
1735 #ifdef OSD_CROSSHAIR
1736 if (priv->crosshair.surface)
1737 cairo_surface_destroy(priv->crosshair.surface);
1738 #endif
1739
1740 #ifdef OSD_NAV
1741 if (priv->nav.surface)
1742 cairo_surface_destroy(priv->nav.surface);
1743 #endif
1744
1745 #ifdef OSD_COORDINATES
1746 if (priv->coordinates.surface)
1747 cairo_surface_destroy(priv->coordinates.surface);
1748 #endif
1749
1750 #ifdef OSD_BALLOON
1751 if (priv->balloon.surface)
1752 cairo_surface_destroy(priv->balloon.surface);
1753 #endif
1754
1755 g_free(priv);
1756 }
1757
1758 static gboolean
1759 osd_busy(osm_gps_map_osd_t *osd)
1760 {
1761 #ifdef OSD_SOURCE_SEL
1762 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
1763 return (priv->source_sel.handler_id != 0);
1764 #else
1765 return FALSE;
1766 #endif
1767 }
1768
1769 static osm_gps_map_osd_t osd_classic = {
1770 .widget = NULL,
1771
1772 .draw = osd_draw,
1773 .check = osd_check,
1774 .render = osd_render,
1775 .free = osd_free,
1776 .busy = osd_busy,
1777
1778 .cb = NULL,
1779 .data = NULL,
1780
1781 .priv = NULL
1782 };
1783
1784 /* this is the only function that's externally visible */
1785 void
1786 osm_gps_map_osd_classic_init(OsmGpsMap *map)
1787 {
1788 osd_priv_t *priv = osd_classic.priv = g_new0(osd_priv_t, 1);
1789
1790 #ifdef OSD_BALLOON
1791 priv->balloon.lat = OSM_GPS_MAP_INVALID;
1792 priv->balloon.lon = OSM_GPS_MAP_INVALID;
1793 #endif
1794
1795 osd_classic.priv = priv;
1796
1797 osm_gps_map_register_osd(map, &osd_classic);
1798 }
1799
1800 #ifdef OSD_GPS_BUTTON
1801 /* below are osd specific functions which aren't used by osm-gps-map */
1802 /* but instead are to be used by the main application */
1803 void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdCallback cb,
1804 gpointer data) {
1805 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1806 g_return_if_fail (osd);
1807
1808 osd->cb = cb;
1809 osd->data = data;
1810
1811 /* this may have changed the state of the gps button */
1812 /* we thus re-render the overlay */
1813 osd->render(osd);
1814
1815 osm_gps_map_redraw(map);
1816 }
1817 #endif
1818
1819 osd_button_t
1820 osm_gps_map_osd_check(OsmGpsMap *map, gint x, gint y) {
1821 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1822 g_return_val_if_fail (osd, OSD_NONE);
1823
1824 return osd_check(osd, TRUE, x, y);
1825 }