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

Parent Directory Parent Directory | Revision Log Revision Log


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