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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 123 - (show annotations)
Wed Sep 23 11:23:45 2009 UTC (14 years, 7 months ago) by harbaum
File MIME type: text/plain
File size: 56973 byte(s)
OSD handling
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 #ifndef USE_CAIRO
29 #error "OSD control display lacks a non-cairo implementation!"
30 #endif
31
32 #include <cairo.h>
33
34 #include "osm-gps-map.h"
35 #include "converter.h"
36 #include "osm-gps-map-osd-classic.h"
37
38 //the osd controls
39 typedef struct {
40 /* the offscreen representation of the OSD */
41 struct {
42 cairo_surface_t *surface;
43 gboolean rendered;
44 #ifdef OSD_GPS_BUTTON
45 gboolean gps_enabled;
46 #endif
47 } controls;
48
49 #ifdef OSD_BALLOON
50 //a balloon with additional info
51 struct {
52 cairo_surface_t *surface;
53 int orientation, offset_x, offset_y;
54
55 gboolean just_created;
56 float lat, lon;
57 OsmGpsMapRect_t rect;
58
59 /* function called to have the user app draw the contents */
60 OsmGpsMapBalloonCallback cb;
61 gpointer data;
62 } balloon;
63 #endif
64
65 #ifdef OSD_SCALE
66 struct {
67 cairo_surface_t *surface;
68 int zoom;
69 } scale;
70 #endif
71
72 #ifdef OSD_CROSSHAIR
73 struct {
74 cairo_surface_t *surface;
75 gboolean rendered;
76 } crosshair;
77 #endif
78
79 #ifdef OSD_NAV
80 struct {
81 cairo_surface_t *surface;
82 float lat, lon;
83 char *name;
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_text(cairo_t *cr, int x, int y, char *text) {
1134 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1135 cairo_set_line_width (cr, OSD_COORDINATES_FONT_SIZE/6);
1136 cairo_move_to (cr, x, y);
1137 cairo_text_path (cr, text);
1138 cairo_stroke (cr);
1139
1140 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
1141 cairo_move_to (cr, x, y);
1142 cairo_show_text (cr, text);
1143 }
1144
1145 static void
1146 osd_render_coordinates(osm_gps_map_osd_t *osd)
1147 {
1148 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1149
1150 /* get current map position */
1151 gfloat lat, lon;
1152 g_object_get(osd->widget, "latitude", &lat, "longitude", &lon, NULL);
1153
1154 /* check if position has changed enough to require redraw */
1155 if(!isnan(priv->coordinates.lat) && !isnan(priv->coordinates.lon))
1156 /* 1/60000 == 1/1000 minute */
1157 if((fabsf(lat - priv->coordinates.lat) < 1/60000) &&
1158 (fabsf(lon - priv->coordinates.lon) < 1/60000))
1159 return;
1160
1161 priv->coordinates.lat = lat;
1162 priv->coordinates.lon = lon;
1163
1164 /* first fill with transparency */
1165 cairo_t *cr = cairo_create(priv->coordinates.surface);
1166 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1167 // cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.5);
1168 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
1169 cairo_paint(cr);
1170 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1171
1172 cairo_select_font_face (cr, "Sans",
1173 CAIRO_FONT_SLANT_NORMAL,
1174 CAIRO_FONT_WEIGHT_BOLD);
1175 cairo_set_font_size (cr, OSD_COORDINATES_FONT_SIZE);
1176
1177 char *latitude = osd_latitude_str(lat);
1178 char *longitude = osd_longitude_str(lon);
1179
1180 cairo_text_extents_t lat_extents, lon_extents;
1181 cairo_text_extents (cr, latitude, &lat_extents);
1182 cairo_text_extents (cr, longitude, &lon_extents);
1183
1184 osd_render_text(cr,
1185 (OSD_COORDINATES_W - lat_extents.width)/2,
1186 OSD_COORDINATES_OFFSET - lat_extents.y_bearing,
1187 latitude);
1188
1189 osd_render_text(cr,
1190 (OSD_COORDINATES_W - lon_extents.width)/2,
1191 OSD_COORDINATES_OFFSET - lon_extents.y_bearing +
1192 OSD_COORDINATES_FONT_SIZE,
1193 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 (8*OSD_COORDINATES_FONT_SIZE+2*OSD_COORDINATES_OFFSET)
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 if(!priv->nav.surface || isnan(priv->nav.lat) || isnan(priv->nav.lon))
1212 return;
1213
1214 /* first fill with transparency */
1215 cairo_t *cr = cairo_create(priv->nav.surface);
1216 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1217 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.2);
1218 cairo_paint(cr);
1219 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1220
1221 cairo_select_font_face (cr, "Sans",
1222 CAIRO_FONT_SLANT_NORMAL,
1223 CAIRO_FONT_WEIGHT_BOLD);
1224 cairo_set_font_size (cr, OSD_COORDINATES_FONT_SIZE);
1225
1226 char *latitude = osd_latitude_str(priv->nav.lat);
1227 char *longitude = osd_longitude_str(priv->nav.lon);
1228
1229 cairo_text_extents_t lat_extents, lon_extents, name_extents;
1230 cairo_text_extents (cr, priv->nav.name, &name_extents);
1231 cairo_text_extents (cr, latitude, &lat_extents);
1232 cairo_text_extents (cr, longitude, &lon_extents);
1233
1234 osd_render_text(cr,
1235 (OSD_NAV_W - name_extents.width)/2,
1236 OSD_COORDINATES_OFFSET - name_extents.y_bearing,
1237 priv->nav.name);
1238
1239 osd_render_text(cr,
1240 (OSD_NAV_W - lat_extents.width)/2,
1241 OSD_COORDINATES_OFFSET - lat_extents.y_bearing +
1242 OSD_COORDINATES_FONT_SIZE,
1243 latitude);
1244
1245 osd_render_text(cr,
1246 (OSD_NAV_W - lon_extents.width)/2,
1247 OSD_COORDINATES_OFFSET - lon_extents.y_bearing +
1248 2 * OSD_COORDINATES_FONT_SIZE,
1249 longitude);
1250
1251 g_free(latitude);
1252 g_free(longitude);
1253
1254 cairo_destroy(cr);
1255 }
1256
1257 void osm_gps_map_osd_clear_nav (OsmGpsMap *map) {
1258 g_return_if_fail (OSM_IS_GPS_MAP (map));
1259
1260 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1261 g_return_if_fail (osd);
1262
1263 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1264 g_return_if_fail (priv);
1265
1266 if(priv->nav.surface) {
1267 cairo_surface_destroy(priv->nav.surface);
1268 priv->nav.surface = NULL;
1269 priv->nav.lat = OSM_GPS_MAP_INVALID;
1270 priv->nav.lon = OSM_GPS_MAP_INVALID;
1271 if(priv->nav.name) g_free(priv->nav.name);
1272 }
1273 osm_gps_map_redraw(map);
1274 }
1275
1276 void
1277 osm_gps_map_osd_draw_nav (OsmGpsMap *map, float latitude, float longitude,
1278 char *name) {
1279 g_return_if_fail (OSM_IS_GPS_MAP (map));
1280
1281 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1282 g_return_if_fail (osd);
1283
1284 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1285 g_return_if_fail (priv);
1286
1287 osm_gps_map_osd_clear_nav (map);
1288
1289 /* allocate balloon surface */
1290 priv->nav.surface =
1291 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1292 OSD_NAV_W+2, OSD_NAV_H+2);
1293
1294 priv->nav.lat = latitude;
1295 priv->nav.lon = longitude;
1296 priv->nav.name = g_strdup(name);
1297
1298 osd_render_nav(osd);
1299
1300 osm_gps_map_redraw(map);
1301 }
1302
1303 #endif // OSD_NAV
1304
1305
1306 #ifdef OSD_CROSSHAIR
1307
1308 #ifndef OSD_CROSSHAIR_RADIUS
1309 #define OSD_CROSSHAIR_RADIUS 10
1310 #endif
1311
1312 #define OSD_CROSSHAIR_TICK (OSD_CROSSHAIR_RADIUS/2)
1313 #define OSD_CROSSHAIR_BORDER (OSD_CROSSHAIR_TICK + OSD_CROSSHAIR_RADIUS/4)
1314 #define OSD_CROSSHAIR_W ((OSD_CROSSHAIR_RADIUS+OSD_CROSSHAIR_BORDER)*2)
1315 #define OSD_CROSSHAIR_H ((OSD_CROSSHAIR_RADIUS+OSD_CROSSHAIR_BORDER)*2)
1316
1317 static void
1318 osd_render_crosshair_shape(cairo_t *cr) {
1319 cairo_arc (cr, OSD_CROSSHAIR_W/2, OSD_CROSSHAIR_H/2,
1320 OSD_CROSSHAIR_RADIUS, 0, 2*M_PI);
1321
1322 cairo_move_to (cr, OSD_CROSSHAIR_W/2 - OSD_CROSSHAIR_RADIUS,
1323 OSD_CROSSHAIR_H/2);
1324 cairo_rel_line_to (cr, -OSD_CROSSHAIR_TICK, 0);
1325 cairo_move_to (cr, OSD_CROSSHAIR_W/2 + OSD_CROSSHAIR_RADIUS,
1326 OSD_CROSSHAIR_H/2);
1327 cairo_rel_line_to (cr, OSD_CROSSHAIR_TICK, 0);
1328
1329 cairo_move_to (cr, OSD_CROSSHAIR_W/2,
1330 OSD_CROSSHAIR_H/2 - OSD_CROSSHAIR_RADIUS);
1331 cairo_rel_line_to (cr, 0, -OSD_CROSSHAIR_TICK);
1332 cairo_move_to (cr, OSD_CROSSHAIR_W/2,
1333 OSD_CROSSHAIR_H/2 + OSD_CROSSHAIR_RADIUS);
1334 cairo_rel_line_to (cr, 0, OSD_CROSSHAIR_TICK);
1335
1336 cairo_stroke (cr);
1337 }
1338
1339 static void
1340 osd_render_crosshair(osm_gps_map_osd_t *osd)
1341 {
1342 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1343
1344 if(priv->crosshair.rendered)
1345 return;
1346
1347 priv->crosshair.rendered = TRUE;
1348
1349 /* first fill with transparency */
1350 cairo_t *cr = cairo_create(priv->crosshair.surface);
1351 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1352 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
1353 cairo_paint(cr);
1354 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1355
1356 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1357
1358 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.5);
1359 cairo_set_line_width (cr, OSD_CROSSHAIR_RADIUS/2);
1360 osd_render_crosshair_shape(cr);
1361
1362 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1363 cairo_set_line_width (cr, OSD_CROSSHAIR_RADIUS/4);
1364 osd_render_crosshair_shape(cr);
1365
1366 cairo_destroy(cr);
1367 }
1368 #endif
1369
1370 #ifdef OSD_SCALE
1371
1372 #ifndef OSD_SCALE_FONT_SIZE
1373 #define OSD_SCALE_FONT_SIZE 12
1374 #endif
1375 #define OSD_SCALE_W (10*OSD_SCALE_FONT_SIZE)
1376 #define OSD_SCALE_H (5*OSD_SCALE_FONT_SIZE/2)
1377
1378 /* various parameters used to create the scale */
1379 #define OSD_SCALE_H2 (OSD_SCALE_H/2)
1380 #define OSD_SCALE_TICK (2*OSD_SCALE_FONT_SIZE/3)
1381 #define OSD_SCALE_M (OSD_SCALE_H2 - OSD_SCALE_TICK)
1382 #define OSD_SCALE_I (OSD_SCALE_H2 + OSD_SCALE_TICK)
1383 #define OSD_SCALE_FD (OSD_SCALE_FONT_SIZE/4)
1384
1385 static void
1386 osd_render_scale(osm_gps_map_osd_t *osd)
1387 {
1388 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1389
1390 /* this only needs to be rendered if the zoom has changed */
1391 gint zoom;
1392 g_object_get(OSM_GPS_MAP(osd->widget), "zoom", &zoom, NULL);
1393 if(zoom == priv->scale.zoom)
1394 return;
1395
1396 priv->scale.zoom = zoom;
1397
1398 float m_per_pix = osm_gps_map_get_scale(OSM_GPS_MAP(osd->widget));
1399
1400 /* first fill with transparency */
1401 cairo_t *cr = cairo_create(priv->scale.surface);
1402 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1403 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
1404 // pink for testing: cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.2);
1405 cairo_paint(cr);
1406 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1407
1408 /* determine the size of the scale width in meters */
1409 float width = (OSD_SCALE_W-OSD_SCALE_FONT_SIZE/6) * m_per_pix;
1410
1411 /* scale this to useful values */
1412 int exp = logf(width)*M_LOG10E;
1413 int mant = width/pow(10,exp);
1414 int width_metric = mant * pow(10,exp);
1415 char *dist_str = NULL;
1416 if(width_metric<1000)
1417 dist_str = g_strdup_printf("%u m", width_metric);
1418 else
1419 dist_str = g_strdup_printf("%u km", width_metric/1000);
1420 width_metric /= m_per_pix;
1421
1422 /* and now the hard part: scale for useful imperial values :-( */
1423 /* try to convert to feet, 1ft == 0.3048 m */
1424 width /= 0.3048;
1425 float imp_scale = 0.3048;
1426 char *dist_imp_unit = "ft";
1427
1428 if(width >= 100) {
1429 /* 1yd == 3 feet */
1430 width /= 3.0;
1431 imp_scale *= 3.0;
1432 dist_imp_unit = "yd";
1433
1434 if(width >= 1760.0) {
1435 /* 1mi == 1760 yd */
1436 width /= 1760.0;
1437 imp_scale *= 1760.0;
1438 dist_imp_unit = "mi";
1439 }
1440 }
1441
1442 /* also convert this to full tens/hundreds */
1443 exp = logf(width)*M_LOG10E;
1444 mant = width/pow(10,exp);
1445 int width_imp = mant * pow(10,exp);
1446 char *dist_str_imp = g_strdup_printf("%u %s", width_imp, dist_imp_unit);
1447
1448 /* convert back to pixels */
1449 width_imp *= imp_scale;
1450 width_imp /= m_per_pix;
1451
1452 cairo_select_font_face (cr, "Sans",
1453 CAIRO_FONT_SLANT_NORMAL,
1454 CAIRO_FONT_WEIGHT_BOLD);
1455 cairo_set_font_size (cr, OSD_SCALE_FONT_SIZE);
1456 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
1457
1458 cairo_text_extents_t extents;
1459 cairo_text_extents (cr, dist_str, &extents);
1460
1461 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1462 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/6);
1463 cairo_move_to (cr, 2*OSD_SCALE_FD, OSD_SCALE_H2-OSD_SCALE_FD);
1464 cairo_text_path (cr, dist_str);
1465 cairo_stroke (cr);
1466 cairo_move_to (cr, 2*OSD_SCALE_FD,
1467 OSD_SCALE_H2+OSD_SCALE_FD + extents.height);
1468 cairo_text_path (cr, dist_str_imp);
1469 cairo_stroke (cr);
1470
1471 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
1472 cairo_move_to (cr, 2*OSD_SCALE_FD, OSD_SCALE_H2-OSD_SCALE_FD);
1473 cairo_show_text (cr, dist_str);
1474 cairo_move_to (cr, 2*OSD_SCALE_FD,
1475 OSD_SCALE_H2+OSD_SCALE_FD + extents.height);
1476 cairo_show_text (cr, dist_str_imp);
1477
1478 g_free(dist_str);
1479 g_free(dist_str_imp);
1480
1481 /* draw white line */
1482 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1483 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
1484 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/3);
1485 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_M);
1486 cairo_rel_line_to (cr, 0, OSD_SCALE_TICK);
1487 cairo_rel_line_to (cr, width_metric, 0);
1488 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1489 cairo_stroke(cr);
1490 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_I);
1491 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1492 cairo_rel_line_to (cr, width_imp, 0);
1493 cairo_rel_line_to (cr, 0, +OSD_SCALE_TICK);
1494 cairo_stroke(cr);
1495
1496 /* draw black line */
1497 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
1498 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/6);
1499 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_M);
1500 cairo_rel_line_to (cr, 0, OSD_SCALE_TICK);
1501 cairo_rel_line_to (cr, width_metric, 0);
1502 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1503 cairo_stroke(cr);
1504 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_I);
1505 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1506 cairo_rel_line_to (cr, width_imp, 0);
1507 cairo_rel_line_to (cr, 0, +OSD_SCALE_TICK);
1508 cairo_stroke(cr);
1509
1510 cairo_destroy(cr);
1511 }
1512 #endif
1513
1514 static void
1515 osd_render_controls(osm_gps_map_osd_t *osd)
1516 {
1517 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1518
1519 if(priv->controls.rendered
1520 #ifdef OSD_GPS_BUTTON
1521 && (priv->controls.gps_enabled == (osd->cb != NULL))
1522 #endif
1523 )
1524 return;
1525
1526 #ifdef OSD_GPS_BUTTON
1527 priv->controls.gps_enabled = (osd->cb != NULL);
1528 #endif
1529 priv->controls.rendered = TRUE;
1530
1531 #ifndef OSD_COLOR
1532 GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL];
1533 GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL];
1534 GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE];
1535 #endif
1536
1537 /* first fill with transparency */
1538 cairo_t *cr = cairo_create(priv->controls.surface);
1539 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1540 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
1541 cairo_paint(cr);
1542 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1543
1544 /* --------- draw zoom and dpad shape shadow ----------- */
1545 #ifdef OSD_SHADOW_ENABLE
1546 osd_zoom_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
1547 osd_shape_shadow(cr);
1548 #ifndef OSD_NO_DPAD
1549 osd_dpad_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
1550 osd_shape_shadow(cr);
1551 #endif
1552 #endif
1553
1554 /* --------- draw zoom and dpad shape ----------- */
1555
1556 osd_zoom_shape(cr, 1, 1);
1557 #ifndef OSD_COLOR
1558 osd_shape(cr, &bg, &fg);
1559 #else
1560 osd_shape(cr);
1561 #endif
1562 #ifndef OSD_NO_DPAD
1563 osd_dpad_shape(cr, 1, 1);
1564 #ifndef OSD_COLOR
1565 osd_shape(cr, &bg, &fg);
1566 #else
1567 osd_shape(cr);
1568 #endif
1569 #endif
1570
1571 /* --------- draw zoom and dpad labels --------- */
1572
1573 #ifdef OSD_SHADOW_ENABLE
1574 osd_labels_shadow(cr, Z_RAD/3, TRUE);
1575 osd_zoom_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1576 #ifndef OSD_NO_DPAD
1577 osd_dpad_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1578 #endif
1579 cairo_stroke(cr);
1580 #ifdef OSD_GPS_BUTTON
1581 osd_labels_shadow(cr, Z_RAD/6, osd->cb != NULL);
1582 osd_dpad_gps(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1583 cairo_stroke(cr);
1584 #endif
1585 #endif
1586
1587 #ifndef OSD_COLOR
1588 osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
1589 #else
1590 osd_labels(cr, Z_RAD/3, TRUE);
1591 #endif
1592 osd_zoom_labels(cr, 1, 1);
1593 #ifndef OSD_NO_DPAD
1594 osd_dpad_labels(cr, 1, 1);
1595 #endif
1596 cairo_stroke(cr);
1597
1598 #ifndef OSD_COLOR
1599 osd_labels(cr, Z_RAD/6, osd->cb != NULL, &fg, &da);
1600 #else
1601 osd_labels(cr, Z_RAD/6, osd->cb != NULL);
1602 #endif
1603 #ifdef OSD_GPS_BUTTON
1604 osd_dpad_gps(cr, 1, 1);
1605 #endif
1606 cairo_stroke(cr);
1607
1608 cairo_destroy(cr);
1609 }
1610
1611 static void
1612 osd_render(osm_gps_map_osd_t *osd)
1613 {
1614 /* this function is actually called pretty often since the */
1615 /* OSD contents may have changed (due to a coordinate/zoom change). */
1616 /* The different OSD parts have to make sure that they don't */
1617 /* render unneccessarily often and thus waste CPU power */
1618
1619 osd_render_controls(osd);
1620
1621 #ifdef OSD_SOURCE_SEL
1622 osd_render_source_sel(osd, FALSE);
1623 #endif
1624
1625 #ifdef OSD_SCALE
1626 osd_render_scale(osd);
1627 #endif
1628
1629 #ifdef OSD_CROSSHAIR
1630 osd_render_crosshair(osd);
1631 #endif
1632
1633 #ifdef OSD_NAV
1634 osd_render_nav(osd);
1635 #endif
1636
1637 #ifdef OSD_COORDINATES
1638 osd_render_coordinates(osd);
1639 #endif
1640 }
1641
1642 static void
1643 osd_draw(osm_gps_map_osd_t *osd, GdkDrawable *drawable)
1644 {
1645 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1646
1647 /* OSD itself uses some off-screen rendering, so check if the */
1648 /* offscreen buffer is present and create it if not */
1649 if(!priv->controls.surface) {
1650 /* create overlay ... */
1651 priv->controls.surface =
1652 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W+2, OSD_H+2);
1653
1654 priv->controls.rendered = FALSE;
1655 #ifdef OSD_GPS_BUTTON
1656 priv->controls.gps_enabled = FALSE;
1657 #endif
1658
1659 #ifdef OSD_SOURCE_SEL
1660 /* the initial OSD state is alway not-expanded */
1661 priv->source_sel.surface =
1662 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1663 OSD_S_W+2, OSD_S_H+2);
1664 priv->source_sel.rendered = FALSE;
1665 #endif
1666
1667 #ifdef OSD_SCALE
1668 priv->scale.surface =
1669 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1670 OSD_SCALE_W, OSD_SCALE_H);
1671 priv->scale.zoom = -1;
1672 #endif
1673
1674 #ifdef OSD_CROSSHAIR
1675 priv->crosshair.surface =
1676 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1677 OSD_CROSSHAIR_W, OSD_CROSSHAIR_H);
1678 priv->crosshair.rendered = FALSE;
1679 #endif
1680
1681 #ifdef OSD_COORDINATES
1682 priv->coordinates.surface =
1683 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1684 OSD_COORDINATES_W, OSD_COORDINATES_H);
1685
1686 priv->coordinates.lat = priv->coordinates.lon = OSM_GPS_MAP_INVALID;
1687 #endif
1688
1689 /* ... and render it */
1690 osd_render(osd);
1691 }
1692
1693 // now draw this onto the original context
1694 cairo_t *cr = gdk_cairo_create(drawable);
1695
1696 gint x, y;
1697
1698 #ifdef OSD_SCALE
1699 x = OSD_X;
1700 y = -OSD_Y;
1701 if(x < 0) x += osd->widget->allocation.width - OSD_SCALE_W;
1702 if(y < 0) y += osd->widget->allocation.height - OSD_SCALE_H;
1703
1704 cairo_set_source_surface(cr, priv->scale.surface, x, y);
1705 cairo_paint(cr);
1706 #endif
1707
1708 #ifdef OSD_CROSSHAIR
1709 x = (osd->widget->allocation.width - OSD_CROSSHAIR_W)/2;
1710 y = (osd->widget->allocation.height - OSD_CROSSHAIR_H)/2;
1711
1712 cairo_set_source_surface(cr, priv->crosshair.surface, x, y);
1713 cairo_paint(cr);
1714 #endif
1715
1716 #ifdef OSD_NAV
1717 if(priv->nav.surface) {
1718 x = OSD_X;
1719 if(x < 0) x += osd->widget->allocation.width - OSD_NAV_W;
1720 y = (osd->widget->allocation.height - OSD_NAV_H)/2;
1721
1722 cairo_set_source_surface(cr, priv->nav.surface, x, y);
1723 cairo_paint(cr);
1724 }
1725 #endif
1726
1727 #ifdef OSD_COORDINATES
1728 x = -OSD_X;
1729 y = -OSD_Y;
1730 if(x < 0) x += osd->widget->allocation.width - OSD_COORDINATES_W;
1731 if(y < 0) y += osd->widget->allocation.height - OSD_COORDINATES_H;
1732
1733 cairo_set_source_surface(cr, priv->coordinates.surface, x, y);
1734 cairo_paint(cr);
1735 #endif
1736
1737 #ifdef OSD_BALLOON
1738 if(priv->balloon.surface) {
1739
1740 /* convert given lat lon into screen coordinates */
1741 gint x, y;
1742 osm_gps_map_geographic_to_screen (OSM_GPS_MAP(osd->widget),
1743 priv->balloon.lat, priv->balloon.lon,
1744 &x, &y);
1745
1746 /* check if balloon needs to be rerendered */
1747 osd_render_balloon(osd);
1748
1749 cairo_set_source_surface(cr, priv->balloon.surface,
1750 x + priv->balloon.offset_x,
1751 y + priv->balloon.offset_y);
1752 cairo_paint(cr);
1753 }
1754 #endif
1755
1756 x = OSD_X;
1757 if(x < 0)
1758 x += osd->widget->allocation.width - OSD_W;
1759
1760 y = OSD_Y;
1761 if(y < 0)
1762 y += osd->widget->allocation.height - OSD_H;
1763
1764 cairo_set_source_surface(cr, priv->controls.surface, x, y);
1765 cairo_paint(cr);
1766
1767 #ifdef OSD_SOURCE_SEL
1768 if(!priv->source_sel.handler_id) {
1769 /* the OSD source selection is not being animated */
1770 if(!priv->source_sel.expanded)
1771 x = osd->widget->allocation.width - OSD_S_W;
1772 else
1773 x = osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
1774 } else
1775 x = priv->source_sel.shift;
1776
1777 y = OSD_S_Y;
1778 if(OSD_S_Y < 0) {
1779 if(!priv->source_sel.expanded)
1780 y = osd->widget->allocation.height - OSD_S_H + OSD_S_Y;
1781 else
1782 y = osd->widget->allocation.height - OSD_S_EXP_H + OSD_S_Y;
1783 }
1784
1785 cairo_set_source_surface(cr, priv->source_sel.surface, x, y);
1786 cairo_paint(cr);
1787 #endif
1788
1789 cairo_destroy(cr);
1790 }
1791
1792 static void
1793 osd_free(osm_gps_map_osd_t *osd)
1794 {
1795 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
1796
1797 if (priv->controls.surface)
1798 cairo_surface_destroy(priv->controls.surface);
1799
1800 #ifdef OSD_SOURCE_SEL
1801 if(priv->source_sel.handler_id)
1802 gtk_timeout_remove(priv->source_sel.handler_id);
1803
1804 if (priv->source_sel.surface)
1805 cairo_surface_destroy(priv->source_sel.surface);
1806 #endif
1807
1808 #ifdef OSD_SCALE
1809 if (priv->scale.surface)
1810 cairo_surface_destroy(priv->scale.surface);
1811 #endif
1812
1813 #ifdef OSD_CROSSHAIR
1814 if (priv->crosshair.surface)
1815 cairo_surface_destroy(priv->crosshair.surface);
1816 #endif
1817
1818 #ifdef OSD_NAV
1819 if (priv->nav.surface)
1820 cairo_surface_destroy(priv->nav.surface);
1821 #endif
1822
1823 #ifdef OSD_COORDINATES
1824 if (priv->coordinates.surface)
1825 cairo_surface_destroy(priv->coordinates.surface);
1826 #endif
1827
1828 #ifdef OSD_BALLOON
1829 if (priv->balloon.surface)
1830 cairo_surface_destroy(priv->balloon.surface);
1831 #endif
1832
1833 g_free(priv);
1834 }
1835
1836 static gboolean
1837 osd_busy(osm_gps_map_osd_t *osd)
1838 {
1839 #ifdef OSD_SOURCE_SEL
1840 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
1841 return (priv->source_sel.handler_id != 0);
1842 #else
1843 return FALSE;
1844 #endif
1845 }
1846
1847 static osm_gps_map_osd_t osd_classic = {
1848 .widget = NULL,
1849
1850 .draw = osd_draw,
1851 .check = osd_check,
1852 .render = osd_render,
1853 .free = osd_free,
1854 .busy = osd_busy,
1855
1856 .cb = NULL,
1857 .data = NULL,
1858
1859 .priv = NULL
1860 };
1861
1862 /* this is the only function that's externally visible */
1863 void
1864 osm_gps_map_osd_classic_init(OsmGpsMap *map)
1865 {
1866 osd_priv_t *priv = osd_classic.priv = g_new0(osd_priv_t, 1);
1867
1868 #ifdef OSD_BALLOON
1869 priv->balloon.lat = OSM_GPS_MAP_INVALID;
1870 priv->balloon.lon = OSM_GPS_MAP_INVALID;
1871 #endif
1872
1873 osd_classic.priv = priv;
1874
1875 osm_gps_map_register_osd(map, &osd_classic);
1876 }
1877
1878 #ifdef OSD_GPS_BUTTON
1879 /* below are osd specific functions which aren't used by osm-gps-map */
1880 /* but instead are to be used by the main application */
1881 void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdCallback cb,
1882 gpointer data) {
1883 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1884 g_return_if_fail (osd);
1885
1886 osd->cb = cb;
1887 osd->data = data;
1888
1889 /* this may have changed the state of the gps button */
1890 /* we thus re-render the overlay */
1891 osd->render(osd);
1892
1893 osm_gps_map_redraw(map);
1894 }
1895 #endif
1896
1897 osd_button_t
1898 osm_gps_map_osd_check(OsmGpsMap *map, gint x, gint y) {
1899 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1900 g_return_val_if_fail (osd, OSD_NONE);
1901
1902 return osd_check(osd, TRUE, x, y);
1903 }