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

Parent Directory Parent Directory | Revision Log Revision Log


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