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

Parent Directory Parent Directory | Revision Log Revision Log


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