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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 110 - (hide 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 harbaum 71 /* -*- 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 harbaum 70 /*
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 harbaum 73 #include "config.h"
21     #include <stdlib.h> // abs
22 harbaum 87 #include <math.h> // M_PI/cos()
23 harbaum 70
24 harbaum 71 /* parameters that can be overwritten from the config file: */
25 harbaum 77 /* OSD_DIAMETER */
26     /* OSD_X, OSD_Y */
27 harbaum 71
28     #ifndef USE_CAIRO
29     #error "OSD control display lacks a non-cairo implementation!"
30 harbaum 70 #endif
31    
32 harbaum 71 #include <cairo.h>
33    
34     #include "osm-gps-map.h"
35 harbaum 73 #include "osm-gps-map-osd-classic.h"
36 harbaum 71
37 harbaum 70 //the osd controls
38     typedef struct {
39 harbaum 74 /* the offscreen representation of the OSD */
40 harbaum 108 struct {
41     cairo_surface_t *surface;
42     gboolean rendered;
43     #ifdef OSD_GPS_BUTTON
44     gboolean gps_enabled;
45     #endif
46     } controls;
47 harbaum 86
48 harbaum 98 #ifdef OSD_SCALE
49 harbaum 110 struct {
50     cairo_surface_t *surface;
51     int zoom;
52     } scale;
53 harbaum 98 #endif
54 harbaum 110
55 harbaum 105 #ifdef OSD_CROSSHAIR
56 harbaum 110 struct {
57     cairo_surface_t *surface;
58     gboolean rendered;
59     } crosshair;
60 harbaum 105 #endif
61    
62 harbaum 106 #ifdef OSD_COORDINATES
63 harbaum 110 struct {
64     cairo_surface_t *surface;
65     float lat, lon;
66     } coordinates;
67 harbaum 106 #endif
68    
69 harbaum 88 #ifdef OSD_SOURCE_SEL
70 harbaum 110 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 harbaum 88 #endif
79 harbaum 87
80 harbaum 70 } osd_priv_t;
81    
82     /* position and extent of bounding box */
83 harbaum 77 #ifndef OSD_X
84 harbaum 70 #define OSD_X (10)
85 harbaum 77 #endif
86    
87     #ifndef OSD_Y
88 harbaum 70 #define OSD_Y (10)
89 harbaum 77 #endif
90 harbaum 70
91     /* parameters of the direction shape */
92 harbaum 77 #ifndef OSD_DIAMETER
93 harbaum 70 #define D_RAD (30) // diameter of dpad
94     #else
95 harbaum 77 #define D_RAD (OSD_DIAMETER)
96 harbaum 70 #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 harbaum 74 #ifdef OSD_SHADOW_ENABLE
106 harbaum 70 /* shadow also depends on control size */
107     #define OSD_SHADOW (D_RAD/6)
108 harbaum 74 #else
109     #define OSD_SHADOW (0)
110     #endif
111 harbaum 70
112 harbaum 77 /* 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 harbaum 70 /* total width and height of controls incl. shadow */
121 harbaum 77 #define OSD_W (2*D_RAD + OSD_SHADOW + Z_GPS * 2 * Z_RAD)
122     #if !Z_GPS
123 harbaum 70 #define OSD_H (2*D_RAD + Z_STEP + 2*Z_RAD + OSD_SHADOW)
124 harbaum 77 #else
125     #define OSD_H (2*Z_RAD + OSD_SHADOW)
126     #endif
127 harbaum 70
128 harbaum 74 #ifdef OSD_SHADOW_ENABLE
129 harbaum 70 #define OSD_LBL_SHADOW (OSD_SHADOW/2)
130 harbaum 74 #endif
131 harbaum 70
132 harbaum 77 #define Z_TOP ((1-Z_GPS) * (2 * D_RAD + Z_STEP))
133    
134 harbaum 70 #define Z_MID (Z_TOP + Z_RAD)
135     #define Z_BOT (Z_MID + Z_RAD)
136     #define Z_LEFT (Z_RAD)
137 harbaum 77 #define Z_RIGHT (2 * D_RAD - Z_RAD + Z_GPS * 2 * Z_RAD)
138     #define Z_CENTER ((Z_RIGHT + Z_LEFT)/2)
139 harbaum 70
140     /* create the cairo shape used for the zoom buttons */
141     static void
142 harbaum 86 osd_zoom_shape(cairo_t *cr, gint x, gint y)
143 harbaum 71 {
144 harbaum 70 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 harbaum 86 /* ------------------- 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 harbaum 76 #ifndef OSD_NO_DPAD
180 harbaum 70 /* create the cairo shape used for the dpad */
181     static void
182 harbaum 86 osd_dpad_shape(cairo_t *cr, gint x, gint y)
183 harbaum 71 {
184 harbaum 70 cairo_arc (cr, x+D_RAD, y+D_RAD, D_RAD, 0, 2 * M_PI);
185     }
186 harbaum 76 #endif
187 harbaum 70
188 harbaum 86 #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 harbaum 70 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 harbaum 76 #ifndef OSD_NO_DPAD
226 harbaum 70 /* check whether x/y is within the dpad */
227     static osd_button_t
228 harbaum 86 osd_check_dpad(gint x, gint y)
229 harbaum 70 {
230     /* within entire dpad circle */
231 harbaum 77 if( osm_gps_map_in_circle(x, y, D_RAD, D_RAD, D_RAD))
232 harbaum 70 {
233     /* convert into position relative to dpads centre */
234 harbaum 77 x -= D_RAD;
235     y -= D_RAD;
236 harbaum 70
237 harbaum 76 #ifdef OSD_GPS_BUTTON
238 harbaum 70 /* 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 harbaum 76 #endif
242 harbaum 70
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 harbaum 76 #endif
260 harbaum 70
261     /* check whether x/y is within the zoom pads */
262     static osd_button_t
263 harbaum 86 osd_check_zoom(gint x, gint y) {
264 harbaum 77 if( x > 0 && x < OSD_W && y > Z_TOP && y < Z_BOT) {
265 harbaum 70
266     /* within circle around (-) label */
267 harbaum 77 if( osm_gps_map_in_circle(x, y, Z_LEFT, Z_MID, Z_RAD))
268 harbaum 70 return OSD_OUT;
269    
270 harbaum 77 /* 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 harbaum 70 /* between center of (-) button and center of entire zoom control area */
281 harbaum 77 if(x > OSD_LEFT && x < D_RAD)
282 harbaum 70 return OSD_OUT;
283    
284     /* between center of (+) button and center of entire zoom control area */
285 harbaum 77 if(x < OSD_RIGHT && x > D_RAD)
286 harbaum 70 return OSD_IN;
287     }
288    
289     return OSD_NONE;
290     }
291    
292 harbaum 88 #ifdef OSD_SOURCE_SEL
293    
294 harbaum 86 /* 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 harbaum 87 /* size of usable area when expanded */
304 harbaum 110 #define OSD_S_AREA_W (priv->source_sel.width)
305     #define OSD_S_AREA_H (priv->source_sel.height)
306 harbaum 86 #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 harbaum 87 #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 harbaum 86
317 harbaum 88 /* draw the shape of the source selection OSD, either only the puller (not expanded) */
318     /* or the entire menu incl. the puller (expanded) */
319 harbaum 86 static void
320     osd_source_shape(osd_priv_t *priv, cairo_t *cr, gint x, gint y) {
321 harbaum 110 if(!priv->source_sel.expanded) {
322 harbaum 86 /* 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 harbaum 87 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 harbaum 86
347 harbaum 87 int py = offset + OSD_S_RAD - OSD_S_D0;
348    
349 harbaum 110 if(!priv->source_sel.expanded) {
350 harbaum 86 /* draw the "puller" open (<) arrow */
351 harbaum 87 cairo_move_to (cr, offset + OSD_S_RAD + OSD_S_D0/2, py);
352 harbaum 86 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 harbaum 87 py += OSD_S_AREA_H - OSD_S_PH;
357 harbaum 86
358     /* draw the "puller" close (>) arrow */
359 harbaum 87 cairo_move_to (cr, offset + OSD_S_RAD - OSD_S_D0/2, py);
360 harbaum 86 cairo_rel_line_to (cr, +OSD_S_D0, +OSD_S_D0);
361     cairo_rel_line_to (cr, -OSD_S_D0, +OSD_S_D0);
362 harbaum 87 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 harbaum 110 int i, step = (priv->source_sel.height - 2*OSD_TEXT_BORDER) /
375 harbaum 89 OSM_GPS_MAP_SOURCE_LAST;
376 harbaum 87 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 harbaum 110 priv->source_sel.width - OSD_TEXT_BORDER,
389 harbaum 87 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 harbaum 86 }
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 harbaum 110 cairo_t *cr = cairo_create(priv->source_sel.surface);
430 harbaum 86 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 harbaum 87 osd_source_content(osd, cr, 1+OSD_LBL_SHADOW);
450     cairo_stroke (cr);
451 harbaum 86 #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 harbaum 87 osd_source_content(osd, cr, 1);
458     cairo_stroke (cr);
459 harbaum 86
460     cairo_destroy(cr);
461     }
462    
463 harbaum 89 /* re-allocate the buffer used to draw the menu. This is used */
464     /* to collapse/expand the buffer */
465 harbaum 86 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 harbaum 110 g_assert (priv->source_sel.surface);
471 harbaum 86
472     int w = OSD_S_W, h = OSD_S_H;
473 harbaum 110 if(priv->source_sel.expanded) {
474 harbaum 87 cairo_text_extents_t extents;
475    
476     /* determine content size */
477 harbaum 110 cairo_t *cr = cairo_create(priv->source_sel.surface);
478 harbaum 87 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 harbaum 110 priv->source_sel.width = max_w + 2*OSD_TEXT_BORDER;
495     priv->source_sel.height = OSM_GPS_MAP_SOURCE_LAST *
496 harbaum 87 (max_h + 2*OSD_TEXT_SKIP) + 2*OSD_TEXT_BORDER;
497    
498 harbaum 86 w = OSD_S_EXP_W;
499     h = OSD_S_EXP_H;
500     }
501    
502 harbaum 110 cairo_surface_destroy(priv->source_sel.surface);
503     priv->source_sel.surface =
504 harbaum 86 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 harbaum 87 #define OSD_TIME 500
511 harbaum 86
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 harbaum 110 priv->source_sel.count += priv->source_sel.dir;
518 harbaum 86
519     /* shifting in */
520 harbaum 110 if(priv->source_sel.dir < 0) {
521     if(priv->source_sel.count <= 0) {
522     priv->source_sel.count = 0;
523 harbaum 86 done = TRUE;
524     }
525     } else {
526 harbaum 110 if(priv->source_sel.count >= 1000) {
527     priv->source_sel.expanded = FALSE;
528 harbaum 86 osd_source_reallocate(osd);
529    
530 harbaum 110 priv->source_sel.count = 1000;
531 harbaum 86 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 harbaum 110 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 harbaum 86 m * diff;
542    
543     osm_gps_map_repaint(OSM_GPS_MAP(osd->widget));
544    
545     if(done)
546 harbaum 110 priv->source_sel.handler_id = 0;
547 harbaum 86
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 harbaum 110 if(priv->source_sel.handler_id)
559 harbaum 86 return;
560    
561     /* expand immediately, collapse is handle at the end of the collapse animation */
562 harbaum 110 if(!priv->source_sel.expanded) {
563     priv->source_sel.expanded = TRUE;
564 harbaum 86 osd_source_reallocate(osd);
565    
566 harbaum 110 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 harbaum 86 } else {
570 harbaum 110 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 harbaum 86 }
574    
575 harbaum 110 priv->source_sel.handler_id = gtk_timeout_add(OSD_TIME/OSD_HZ, osd_source_animate, osd);
576 harbaum 86 }
577    
578 harbaum 89 /* check if the user clicked inside the source selection area */
579 harbaum 70 static osd_button_t
580 harbaum 86 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 harbaum 110 if(!priv->source_sel.expanded)
584 harbaum 86 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 harbaum 88
606     /* check for clicks into data area */
607 harbaum 110 if(priv->source_sel.expanded && !priv->source_sel.handler_id) {
608 harbaum 94 /* 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 harbaum 88 if(x > OSD_S_PW &&
613     x < OSD_S_PW + OSD_S_EXP_W &&
614     y > 0 &&
615     y < OSD_S_EXP_H) {
616 harbaum 94
617 harbaum 110 int step = (priv->source_sel.height - 2*OSD_TEXT_BORDER)
618 harbaum 89 / OSM_GPS_MAP_SOURCE_LAST;
619 harbaum 88
620 harbaum 89 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 harbaum 88 return OSD_BG;
639     }
640     }
641    
642 harbaum 86 return OSD_NONE;
643     }
644 harbaum 88 #endif // OSD_SOURCE_SEL
645 harbaum 86
646     static osd_button_t
647     osd_check(osm_gps_map_osd_t *osd, gint x, gint y) {
648 harbaum 70 osd_button_t but = OSD_NONE;
649    
650 harbaum 86 #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 harbaum 77 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 harbaum 70 /* first do a rough test for the OSD area. */
667     /* this is just to avoid an unnecessary detailed test */
668 harbaum 77 if(x > 0 && x < OSD_W && y > 0 && y < OSD_H) {
669 harbaum 76 #ifndef OSD_NO_DPAD
670 harbaum 86 but = osd_check_dpad(x, y);
671 harbaum 76 #endif
672 harbaum 70
673     if(but == OSD_NONE)
674 harbaum 86 but = osd_check_zoom(x, y);
675 harbaum 70 }
676    
677     return but;
678     }
679    
680 harbaum 76 #ifndef OSD_NO_DPAD
681 harbaum 70 static void
682 harbaum 86 osd_dpad_labels(cairo_t *cr, gint x, gint y) {
683 harbaum 70 /* 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 harbaum 76 #endif
706 harbaum 70
707 harbaum 76 #ifdef OSD_GPS_BUTTON
708     /* draw the satellite dish icon in the center of the dpad */
709 harbaum 77 #define GPS_V0 (D_RAD/7)
710 harbaum 70 #define GPS_V1 (D_RAD/10)
711     #define GPS_V2 (D_RAD/5)
712    
713     /* draw a satellite receiver dish */
714 harbaum 77 /* this is either drawn in the center of the dpad (if present) */
715     /* or in the middle of the zoom area */
716 harbaum 70 static void
717 harbaum 86 osd_dpad_gps(cairo_t *cr, gint x, gint y) {
718 harbaum 70 /* move reference to dpad center */
719 harbaum 77 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 harbaum 70
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 harbaum 76 #endif
736 harbaum 70
737     #define Z_LEN (2*Z_RAD/3)
738    
739     static void
740 harbaum 86 osd_zoom_labels(cairo_t *cr, gint x, gint y) {
741 harbaum 70 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 harbaum 106 #ifdef OSD_COORDINATES
751    
752 harbaum 107 #ifndef OSD_COORDINATES_FONT_SIZE
753     #define OSD_COORDINATES_FONT_SIZE 12
754     #endif
755    
756 harbaum 108 #define OSD_COORDINATES_OFFSET (OSD_COORDINATES_FONT_SIZE/6)
757 harbaum 107
758 harbaum 108 #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 harbaum 107 /* 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 harbaum 106 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 harbaum 107 /* get current map position */
823     gfloat lat, lon;
824     g_object_get(osd->widget, "latitude", &lat, "longitude", &lon, NULL);
825    
826 harbaum 108 /* check if position has changed enough to require redraw */
827 harbaum 110 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 harbaum 108 return;
832    
833 harbaum 110 priv->coordinates.lat = lat;
834     priv->coordinates.lon = lon;
835 harbaum 108
836     /* first fill with light transparency */
837 harbaum 110 cairo_t *cr = cairo_create(priv->coordinates.surface);
838 harbaum 106 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
839 harbaum 108 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.5);
840 harbaum 106 cairo_paint(cr);
841     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
842    
843 harbaum 107 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 harbaum 108 cairo_move_to (cr,
858     (OSD_COORDINATES_W - lat_extents.width)/2,
859 harbaum 107 OSD_COORDINATES_OFFSET - lat_extents.y_bearing);
860     cairo_text_path (cr, latitude);
861     cairo_move_to (cr,
862 harbaum 108 (OSD_COORDINATES_W - lon_extents.width)/2,
863 harbaum 107 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 harbaum 108 (OSD_COORDINATES_W - lat_extents.width)/2,
871 harbaum 107 OSD_COORDINATES_OFFSET - lat_extents.y_bearing);
872     cairo_show_text (cr, latitude);
873     cairo_move_to (cr,
874 harbaum 108 (OSD_COORDINATES_W - lon_extents.width)/2,
875 harbaum 107 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 harbaum 106 cairo_destroy(cr);
883     }
884     #endif // OSD_COORDINATES
885    
886 harbaum 105 #ifdef OSD_CROSSHAIR
887    
888     #ifndef OSD_CROSSHAIR_RADIUS
889 harbaum 106 #define OSD_CROSSHAIR_RADIUS 10
890 harbaum 105 #endif
891    
892 harbaum 106 #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 harbaum 105
897     static void
898 harbaum 106 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 harbaum 105 osd_render_crosshair(osm_gps_map_osd_t *osd)
921     {
922     osd_priv_t *priv = (osd_priv_t*)osd->priv;
923    
924 harbaum 110 if(priv->crosshair.rendered)
925     return;
926    
927     priv->crosshair.rendered = TRUE;
928    
929 harbaum 105 /* first fill with transparency */
930 harbaum 110 cairo_t *cr = cairo_create(priv->crosshair.surface);
931 harbaum 105 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
932 harbaum 106 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
933 harbaum 105 cairo_paint(cr);
934     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
935    
936 harbaum 106 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 harbaum 105 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 harbaum 103 /* 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 harbaum 99
965 harbaum 70 static void
966 harbaum 98 osd_render_scale(osm_gps_map_osd_t *osd)
967     {
968 harbaum 86 osd_priv_t *priv = (osd_priv_t*)osd->priv;
969 harbaum 103
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 harbaum 110 if(zoom == priv->scale.zoom)
974 harbaum 103 return;
975    
976 harbaum 110 priv->scale.zoom = zoom;
977 harbaum 103
978 harbaum 99 float m_per_pix = osm_gps_map_get_scale(OSM_GPS_MAP(osd->widget));
979 harbaum 70
980 harbaum 98 /* first fill with transparency */
981 harbaum 110 cairo_t *cr = cairo_create(priv->scale.surface);
982 harbaum 98 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
983 harbaum 100 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
984 harbaum 103 // pink for testing: cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.2);
985 harbaum 98 cairo_paint(cr);
986     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
987    
988     /* determine the size of the scale width in meters */
989 harbaum 103 float width = (OSD_SCALE_W-OSD_SCALE_FONT_SIZE/6) * m_per_pix;
990 harbaum 102
991 harbaum 98 /* scale this to useful values */
992     int exp = logf(width)*M_LOG10E;
993     int mant = width/pow(10,exp);
994 harbaum 99 int width_metric = mant * pow(10,exp);
995 harbaum 103 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 harbaum 99 width_metric /= m_per_pix;
1001    
1002 harbaum 102 /* 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 harbaum 99
1008 harbaum 102 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 harbaum 103 /* 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 harbaum 102
1028 harbaum 103 /* convert back to pixels */
1029     width_imp *= imp_scale;
1030     width_imp /= m_per_pix;
1031    
1032 harbaum 99 cairo_select_font_face (cr, "Sans",
1033     CAIRO_FONT_SLANT_NORMAL,
1034     CAIRO_FONT_WEIGHT_BOLD);
1035 harbaum 102 cairo_set_font_size (cr, OSD_SCALE_FONT_SIZE);
1036 harbaum 99 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 harbaum 100 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1042 harbaum 103 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 harbaum 100 cairo_text_path (cr, dist_str);
1045     cairo_stroke (cr);
1046 harbaum 103 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 harbaum 100
1051     cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
1052 harbaum 103 cairo_move_to (cr, 2*OSD_SCALE_FD, OSD_SCALE_H2-OSD_SCALE_FD);
1053 harbaum 99 cairo_show_text (cr, dist_str);
1054 harbaum 103 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 harbaum 99
1058 harbaum 103 g_free(dist_str);
1059     g_free(dist_str_imp);
1060    
1061 harbaum 99 /* 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 harbaum 103 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 harbaum 99 cairo_rel_line_to (cr, width_metric, 0);
1068 harbaum 103 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1069 harbaum 99 cairo_stroke(cr);
1070 harbaum 103 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 harbaum 99
1076     /* draw black line */
1077     cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
1078 harbaum 103 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 harbaum 99 cairo_rel_line_to (cr, width_metric, 0);
1082 harbaum 103 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1083 harbaum 99 cairo_stroke(cr);
1084 harbaum 103 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 harbaum 99
1090 harbaum 98 cairo_destroy(cr);
1091     }
1092 harbaum 105 #endif
1093 harbaum 98
1094     static void
1095 harbaum 108 osd_render_controls(osm_gps_map_osd_t *osd)
1096 harbaum 98 {
1097     osd_priv_t *priv = (osd_priv_t*)osd->priv;
1098    
1099 harbaum 108 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 harbaum 110 priv->controls.rendered = TRUE;
1110 harbaum 108
1111 harbaum 86 #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 harbaum 74 #endif
1116 harbaum 70
1117     /* first fill with transparency */
1118 harbaum 108 cairo_t *cr = cairo_create(priv->controls.surface);
1119 harbaum 70 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 harbaum 74 #ifdef OSD_SHADOW_ENABLE
1126 harbaum 86 osd_zoom_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
1127     osd_shape_shadow(cr);
1128 harbaum 76 #ifndef OSD_NO_DPAD
1129 harbaum 86 osd_dpad_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
1130     osd_shape_shadow(cr);
1131 harbaum 74 #endif
1132 harbaum 76 #endif
1133 harbaum 70
1134     /* --------- draw zoom and dpad shape ----------- */
1135    
1136 harbaum 86 osd_zoom_shape(cr, 1, 1);
1137 harbaum 74 #ifndef OSD_COLOR
1138 harbaum 86 osd_shape(cr, &bg, &fg);
1139 harbaum 74 #else
1140 harbaum 86 osd_shape(cr);
1141 harbaum 74 #endif
1142 harbaum 76 #ifndef OSD_NO_DPAD
1143 harbaum 86 osd_dpad_shape(cr, 1, 1);
1144 harbaum 74 #ifndef OSD_COLOR
1145 harbaum 86 osd_shape(cr, &bg, &fg);
1146 harbaum 74 #else
1147 harbaum 86 osd_shape(cr);
1148 harbaum 74 #endif
1149 harbaum 76 #endif
1150 harbaum 70
1151     /* --------- draw zoom and dpad labels --------- */
1152    
1153 harbaum 74 #ifdef OSD_SHADOW_ENABLE
1154 harbaum 87 osd_labels_shadow(cr, Z_RAD/3, TRUE);
1155 harbaum 86 osd_zoom_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1156 harbaum 76 #ifndef OSD_NO_DPAD
1157 harbaum 86 osd_dpad_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1158 harbaum 76 #endif
1159 harbaum 87 cairo_stroke(cr);
1160 harbaum 76 #ifdef OSD_GPS_BUTTON
1161 harbaum 87 osd_labels_shadow(cr, Z_RAD/6, osd->cb != NULL);
1162 harbaum 86 osd_dpad_gps(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1163 harbaum 87 cairo_stroke(cr);
1164 harbaum 74 #endif
1165 harbaum 76 #endif
1166 harbaum 70
1167 harbaum 74 #ifndef OSD_COLOR
1168 harbaum 86 osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
1169 harbaum 74 #else
1170 harbaum 86 osd_labels(cr, Z_RAD/3, TRUE);
1171 harbaum 74 #endif
1172 harbaum 87 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 harbaum 74 #ifndef OSD_COLOR
1179 harbaum 86 osd_labels(cr, Z_RAD/6, osd->cb != NULL, &fg, &da);
1180 harbaum 74 #else
1181 harbaum 86 osd_labels(cr, Z_RAD/6, osd->cb != NULL);
1182 harbaum 74 #endif
1183 harbaum 87 #ifdef OSD_GPS_BUTTON
1184     osd_dpad_gps(cr, 1, 1);
1185 harbaum 76 #endif
1186 harbaum 87 cairo_stroke(cr);
1187 harbaum 70
1188     cairo_destroy(cr);
1189 harbaum 108 }
1190 harbaum 86
1191 harbaum 108 static void
1192     osd_render(osm_gps_map_osd_t *osd)
1193     {
1194     osd_render_controls(osd);
1195    
1196 harbaum 88 #ifdef OSD_SOURCE_SEL
1197 harbaum 86 osd_render_source_sel(osd);
1198 harbaum 88 #endif
1199 harbaum 98
1200     #ifdef OSD_SCALE
1201     osd_render_scale(osd);
1202     #endif
1203 harbaum 105
1204     #ifdef OSD_CROSSHAIR
1205     osd_render_crosshair(osd);
1206     #endif
1207 harbaum 106
1208     #ifdef OSD_COORDINATES
1209     osd_render_coordinates(osd);
1210     #endif
1211 harbaum 70 }
1212    
1213     static void
1214 harbaum 86 osd_draw(osm_gps_map_osd_t *osd, GdkDrawable *drawable)
1215 harbaum 70 {
1216 harbaum 73 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1217 harbaum 70
1218     /* OSD itself uses some off-screen rendering, so check if the */
1219     /* offscreen buffer is present and create it if not */
1220 harbaum 108 if(!priv->controls.surface) {
1221 harbaum 70 /* create overlay ... */
1222 harbaum 108 priv->controls.surface =
1223 harbaum 86 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W+2, OSD_H+2);
1224    
1225 harbaum 108 priv->controls.rendered = FALSE;
1226     #ifdef OSD_GPS_BUTTON
1227     priv->controls.gps_enabled = FALSE;
1228     #endif
1229    
1230 harbaum 88 #ifdef OSD_SOURCE_SEL
1231 harbaum 86 /* the initial OSD state is alway not-expanded */
1232 harbaum 110 priv->source_sel.surface =
1233 harbaum 86 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1234     OSD_S_W+2, OSD_S_H+2);
1235 harbaum 88 #endif
1236 harbaum 86
1237 harbaum 98 #ifdef OSD_SCALE
1238 harbaum 110 priv->scale.surface =
1239 harbaum 98 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1240 harbaum 103 OSD_SCALE_W, OSD_SCALE_H);
1241 harbaum 110 priv->scale.zoom = -1;
1242 harbaum 98 #endif
1243    
1244 harbaum 105 #ifdef OSD_CROSSHAIR
1245 harbaum 110 priv->crosshair.surface =
1246 harbaum 105 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1247     OSD_CROSSHAIR_W, OSD_CROSSHAIR_H);
1248 harbaum 110 priv->crosshair.rendered = FALSE;
1249 harbaum 105 #endif
1250    
1251 harbaum 106 #ifdef OSD_COORDINATES
1252 harbaum 110 priv->coordinates.surface =
1253 harbaum 106 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1254     OSD_COORDINATES_W, OSD_COORDINATES_H);
1255 harbaum 108
1256 harbaum 110 priv->coordinates.lat = priv->coordinates.lon = OSM_GPS_MAP_INVALID;
1257 harbaum 106 #endif
1258    
1259 harbaum 70 /* ... and render it */
1260 harbaum 86 osd_render(osd);
1261 harbaum 70 }
1262    
1263     // now draw this onto the original context
1264 harbaum 75 cairo_t *cr = gdk_cairo_create(drawable);
1265 harbaum 77
1266 harbaum 106 int x, y;
1267 harbaum 77
1268 harbaum 106 #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 harbaum 77
1274 harbaum 110 cairo_set_source_surface(cr, priv->scale.surface, x, y);
1275 harbaum 106 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 harbaum 110 cairo_set_source_surface(cr, priv->crosshair.surface, x, y);
1283 harbaum 106 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 harbaum 110 cairo_set_source_surface(cr, priv->coordinates.surface, x, y);
1293 harbaum 106 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 harbaum 108 cairo_set_source_surface(cr, priv->controls.surface, x, y);
1305 harbaum 70 cairo_paint(cr);
1306 harbaum 86
1307     #ifdef OSD_SOURCE_SEL
1308 harbaum 110 if(!priv->source_sel.handler_id) {
1309 harbaum 86 /* the OSD source selection is not being animated */
1310 harbaum 110 if(!priv->source_sel.expanded)
1311 harbaum 86 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 harbaum 110 x = priv->source_sel.shift;
1316 harbaum 86
1317     y = OSD_S_Y;
1318     if(OSD_S_Y < 0) {
1319 harbaum 110 if(!priv->source_sel.expanded)
1320 harbaum 86 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 harbaum 110 cairo_set_source_surface(cr, priv->source_sel.surface, x, y);
1326 harbaum 86 cairo_paint(cr);
1327     #endif
1328    
1329 harbaum 70 cairo_destroy(cr);
1330     }
1331    
1332     static void
1333 harbaum 86 osd_free(osm_gps_map_osd_t *osd)
1334 harbaum 70 {
1335 harbaum 73 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
1336 harbaum 70
1337 harbaum 108 if (priv->controls.surface)
1338     cairo_surface_destroy(priv->controls.surface);
1339 harbaum 88
1340     #ifdef OSD_SOURCE_SEL
1341 harbaum 110 if(priv->source_sel.handler_id)
1342     gtk_timeout_remove(priv->source_sel.handler_id);
1343 harbaum 86
1344 harbaum 110 if (priv->source_sel.surface)
1345     cairo_surface_destroy(priv->source_sel.surface);
1346 harbaum 88 #endif
1347 harbaum 86
1348 harbaum 98 #ifdef OSD_SCALE
1349 harbaum 110 if (priv->scale.surface)
1350     cairo_surface_destroy(priv->scale.surface);
1351 harbaum 98 #endif
1352    
1353 harbaum 105 #ifdef OSD_CROSSHAIR
1354 harbaum 110 if (priv->crosshair.surface)
1355     cairo_surface_destroy(priv->crosshair.surface);
1356 harbaum 105 #endif
1357    
1358 harbaum 106 #ifdef OSD_COORDINATES
1359 harbaum 110 if (priv->coordinates.surface)
1360     cairo_surface_destroy(priv->coordinates.surface);
1361 harbaum 106 #endif
1362    
1363 harbaum 73 g_free(priv);
1364     }
1365    
1366 harbaum 87 static gboolean
1367     osd_busy(osm_gps_map_osd_t *osd)
1368     {
1369 harbaum 88 #ifdef OSD_SOURCE_SEL
1370 harbaum 87 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
1371 harbaum 110 return (priv->source_sel.handler_id != 0);
1372 harbaum 88 #else
1373     return FALSE;
1374     #endif
1375 harbaum 87 }
1376    
1377 harbaum 73 static osm_gps_map_osd_t osd_classic = {
1378 harbaum 88 .widget = NULL,
1379    
1380 harbaum 86 .draw = osd_draw,
1381     .check = osd_check,
1382     .render = osd_render,
1383     .free = osd_free,
1384 harbaum 87 .busy = osd_busy,
1385 harbaum 73
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 harbaum 76 #ifdef OSD_GPS_BUTTON
1404 harbaum 74 /* 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 harbaum 76 void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdCallback cb,
1407     gpointer data) {
1408 harbaum 73 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 harbaum 70 /* this may have changed the state of the gps button */
1415     /* we thus re-render the overlay */
1416 harbaum 73 osd->render(osd);
1417 harbaum 70
1418 harbaum 73 osm_gps_map_redraw(map);
1419 harbaum 70 }
1420 harbaum 76 #endif
1421 harbaum 86
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     }