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

Parent Directory Parent Directory | Revision Log Revision Log


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