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

Parent Directory Parent Directory | Revision Log Revision Log


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