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

Parent Directory Parent Directory | Revision Log Revision Log


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