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

Parent Directory Parent Directory | Revision Log Revision Log


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