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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 122 - (hide annotations)
Mon Sep 21 13:15:25 2009 UTC (14 years, 7 months ago) by harbaum
File MIME type: text/plain
File size: 54632 byte(s)
OSD nav begun
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 harbaum 122 #define OSD_NAV
29    
30 harbaum 71 #ifndef USE_CAIRO
31     #error "OSD control display lacks a non-cairo implementation!"
32 harbaum 70 #endif
33    
34 harbaum 71 #include <cairo.h>
35    
36     #include "osm-gps-map.h"
37 harbaum 112 #include "converter.h"
38 harbaum 73 #include "osm-gps-map-osd-classic.h"
39 harbaum 71
40 harbaum 70 //the osd controls
41     typedef struct {
42 harbaum 74 /* the offscreen representation of the OSD */
43 harbaum 108 struct {
44     cairo_surface_t *surface;
45     gboolean rendered;
46     #ifdef OSD_GPS_BUTTON
47     gboolean gps_enabled;
48     #endif
49     } controls;
50 harbaum 86
51 harbaum 112 #ifdef OSD_BALLOON
52     //a balloon with additional info
53     struct {
54     cairo_surface_t *surface;
55 harbaum 113 int orientation, offset_x, offset_y;
56 harbaum 112
57 harbaum 114 gboolean just_created;
58 harbaum 112 float lat, lon;
59     OsmGpsMapRect_t rect;
60    
61     /* function called to have the user app draw the contents */
62     OsmGpsMapBalloonCallback cb;
63     gpointer data;
64     } balloon;
65     #endif
66    
67 harbaum 98 #ifdef OSD_SCALE
68 harbaum 110 struct {
69     cairo_surface_t *surface;
70     int zoom;
71     } scale;
72 harbaum 98 #endif
73 harbaum 110
74 harbaum 105 #ifdef OSD_CROSSHAIR
75 harbaum 110 struct {
76     cairo_surface_t *surface;
77     gboolean rendered;
78     } crosshair;
79 harbaum 105 #endif
80    
81 harbaum 122 #ifdef OSD_NAV
82     struct {
83     cairo_surface_t *surface;
84     } nav;
85     #endif
86    
87 harbaum 106 #ifdef OSD_COORDINATES
88 harbaum 110 struct {
89     cairo_surface_t *surface;
90     float lat, lon;
91     } coordinates;
92 harbaum 106 #endif
93    
94 harbaum 88 #ifdef OSD_SOURCE_SEL
95 harbaum 110 struct {
96     /* values to handle the "source" menu */
97     cairo_surface_t *surface;
98     gboolean expanded;
99     gint shift, dir, count;
100     gint handler_id;
101     gint width, height;
102 harbaum 111 gboolean rendered;
103 harbaum 110 } source_sel;
104 harbaum 88 #endif
105 harbaum 87
106 harbaum 70 } osd_priv_t;
107    
108 harbaum 112 #ifdef OSD_BALLOON
109     /* most visual effects are hardcoded by now, but may be made */
110     /* available via properties later */
111     #ifndef BALLOON_AREA_WIDTH
112     #define BALLOON_AREA_WIDTH 290
113     #endif
114     #ifndef BALLOON_AREA_HEIGHT
115     #define BALLOON_AREA_HEIGHT 75
116     #endif
117     #ifndef BALLOON_CORNER_RADIUS
118     #define BALLOON_CORNER_RADIUS 10
119     #endif
120    
121     #define BALLOON_BORDER (BALLOON_CORNER_RADIUS/2)
122     #define BALLOON_WIDTH (BALLOON_AREA_WIDTH + 2 * BALLOON_BORDER)
123     #define BALLOON_HEIGHT (BALLOON_AREA_HEIGHT + 2 * BALLOON_BORDER)
124     #define BALLOON_TRANSPARENCY 0.8
125     #define POINTER_HEIGHT 20
126     #define POINTER_FOOT_WIDTH 20
127     #define POINTER_OFFSET (BALLOON_CORNER_RADIUS*3/4)
128     #define BALLOON_SHADOW (BALLOON_CORNER_RADIUS/2)
129     #define BALLOON_SHADOW_TRANSPARENCY 0.2
130    
131 harbaum 113 #define BALLOON_W (BALLOON_WIDTH + BALLOON_SHADOW)
132     #define BALLOON_H (BALLOON_HEIGHT + POINTER_HEIGHT + BALLOON_SHADOW)
133    
134 harbaum 112 #define CLOSE_BUTTON_RADIUS (BALLOON_CORNER_RADIUS)
135    
136    
137     /* draw the bubble shape. this is used twice, once for the shape and once */
138     /* for the shadow */
139     static void
140     osm_gps_map_draw_balloon_shape (cairo_t *cr, int x0, int y0, int x1, int y1,
141     gboolean bottom, int px, int py, int px0, int px1) {
142    
143     cairo_move_to (cr, x0, y0 + BALLOON_CORNER_RADIUS);
144     cairo_arc (cr, x0 + BALLOON_CORNER_RADIUS, y0 + BALLOON_CORNER_RADIUS,
145     BALLOON_CORNER_RADIUS, -M_PI, -M_PI/2);
146     if(!bottom) {
147     /* insert top pointer */
148     cairo_line_to (cr, px1, y0);
149     cairo_line_to (cr, px, py);
150     cairo_line_to (cr, px0, y0);
151     }
152    
153     cairo_line_to (cr, x1 - BALLOON_CORNER_RADIUS, y0);
154     cairo_arc (cr, x1 - BALLOON_CORNER_RADIUS, y0 + BALLOON_CORNER_RADIUS,
155     BALLOON_CORNER_RADIUS, -M_PI/2, 0);
156     cairo_line_to (cr, x1 , y1 - BALLOON_CORNER_RADIUS);
157     cairo_arc (cr, x1 - BALLOON_CORNER_RADIUS, y1 - BALLOON_CORNER_RADIUS,
158     BALLOON_CORNER_RADIUS, 0, M_PI/2);
159     if(bottom) {
160     /* insert bottom pointer */
161     cairo_line_to (cr, px0, y1);
162     cairo_line_to (cr, px, py);
163     cairo_line_to (cr, px1, y1);
164     }
165    
166     cairo_line_to (cr, x0 + BALLOON_CORNER_RADIUS, y1);
167     cairo_arc (cr, x0 + BALLOON_CORNER_RADIUS, y1 - BALLOON_CORNER_RADIUS,
168     BALLOON_CORNER_RADIUS, M_PI/2, M_PI);
169    
170     cairo_close_path (cr);
171     }
172    
173     static void
174     osd_render_balloon(osm_gps_map_osd_t *osd) {
175     osd_priv_t *priv = (osd_priv_t*)osd->priv;
176    
177     /* get zoom */
178     gint zoom;
179     g_object_get(OSM_GPS_MAP(osd->widget), "zoom", &zoom, NULL);
180    
181     /* ------- convert given coordinate into screen position --------- */
182 harbaum 113 gint xs, ys;
183 harbaum 112 osm_gps_map_geographic_to_screen (OSM_GPS_MAP(osd->widget),
184     priv->balloon.lat, priv->balloon.lon,
185 harbaum 113 &xs, &ys);
186 harbaum 112
187 harbaum 113 gint x0 = 1, y0 = 1;
188    
189 harbaum 112 /* check position of this relative to screen center to determine */
190     /* pointer direction ... */
191 harbaum 113 int pointer_x, pointer_x0, pointer_x1;
192     int pointer_y;
193 harbaum 112
194     /* ... and calculate position */
195 harbaum 113 int orientation = 0;
196     if(xs > osd->widget->allocation.width/2) {
197     priv->balloon.offset_x = -BALLOON_WIDTH + POINTER_OFFSET;
198     pointer_x = x0 - priv->balloon.offset_x;
199 harbaum 112 pointer_x0 = pointer_x - (BALLOON_CORNER_RADIUS - POINTER_OFFSET);
200     pointer_x1 = pointer_x0 - POINTER_FOOT_WIDTH;
201 harbaum 113 orientation |= 1;
202 harbaum 112 } else {
203 harbaum 113 priv->balloon.offset_x = -POINTER_OFFSET;
204     pointer_x = x0 - priv->balloon.offset_x;
205 harbaum 112 pointer_x1 = pointer_x + (BALLOON_CORNER_RADIUS - POINTER_OFFSET);
206     pointer_x0 = pointer_x1 + POINTER_FOOT_WIDTH;
207     }
208    
209     gboolean bottom = FALSE;
210 harbaum 113 if(ys > osd->widget->allocation.height/2) {
211     priv->balloon.offset_y = -BALLOON_HEIGHT - POINTER_HEIGHT;
212     pointer_y = y0 - priv->balloon.offset_y;
213 harbaum 112 bottom = TRUE;
214 harbaum 113 orientation |= 2;
215     } else {
216     priv->balloon.offset_y = 0;
217     pointer_y = y0 - priv->balloon.offset_y;
218 harbaum 112 y0 += POINTER_HEIGHT;
219 harbaum 113 }
220 harbaum 112
221 harbaum 113 /* if required orientation equals current one, then don't render */
222     /* anything */
223     if(orientation == priv->balloon.orientation)
224     return;
225    
226     priv->balloon.orientation = orientation;
227    
228 harbaum 112 /* calculate bottom/right of box */
229     int x1 = x0 + BALLOON_WIDTH, y1 = y0 + BALLOON_HEIGHT;
230    
231     /* save balloon screen coordinates for later use */
232     priv->balloon.rect.x = x0 + BALLOON_BORDER;
233     priv->balloon.rect.y = y0 + BALLOON_BORDER;
234     priv->balloon.rect.w = x1 - x0 - 2*BALLOON_BORDER;
235     priv->balloon.rect.h = y1 - y0 - 2*BALLOON_BORDER;
236    
237     cairo_t *cr = cairo_create(priv->balloon.surface);
238 harbaum 113 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
239     cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
240     cairo_paint(cr);
241     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
242 harbaum 112
243     /* --------- draw shadow --------------- */
244     osm_gps_map_draw_balloon_shape (cr,
245     x0 + BALLOON_SHADOW, y0 + BALLOON_SHADOW,
246     x1 + BALLOON_SHADOW, y1 + BALLOON_SHADOW,
247     bottom, pointer_x, pointer_y,
248     pointer_x0 + BALLOON_SHADOW, pointer_x1 + BALLOON_SHADOW);
249    
250     cairo_set_source_rgba (cr, 0, 0, 0, BALLOON_SHADOW_TRANSPARENCY);
251     cairo_fill_preserve (cr);
252     cairo_set_source_rgba (cr, 1, 0, 0, 1.0);
253     cairo_set_line_width (cr, 0);
254     cairo_stroke (cr);
255    
256     /* --------- draw main shape ----------- */
257     osm_gps_map_draw_balloon_shape (cr, x0, y0, x1, y1,
258     bottom, pointer_x, pointer_y, pointer_x0, pointer_x1);
259    
260     cairo_set_source_rgba (cr, 1, 1, 1, BALLOON_TRANSPARENCY);
261     cairo_fill_preserve (cr);
262     cairo_set_source_rgba (cr, 0, 0, 0, BALLOON_TRANSPARENCY);
263     cairo_set_line_width (cr, 1);
264     cairo_stroke (cr);
265    
266     if (priv->balloon.cb) {
267     /* clip in case application tries to draw in */
268     /* exceed of the balloon */
269     cairo_rectangle (cr, priv->balloon.rect.x, priv->balloon.rect.y,
270     priv->balloon.rect.w, priv->balloon.rect.h);
271     cairo_clip (cr);
272     cairo_new_path (cr); /* current path is not
273     consumed by cairo_clip() */
274    
275     priv->balloon.cb(cr, &priv->balloon.rect, priv->balloon.data);
276     }
277 harbaum 113
278 harbaum 112 cairo_destroy(cr);
279     }
280    
281     /* return true if balloon is being displayed and if */
282     /* the given coordinate is within this balloon */
283     static gboolean
284 harbaum 114 osd_balloon_check(osm_gps_map_osd_t *osd, gboolean down, gint x, gint y)
285 harbaum 112 {
286 harbaum 113 osd_priv_t *priv = (osd_priv_t*)osd->priv;
287 harbaum 112
288 harbaum 113 if(!priv->balloon.surface)
289 harbaum 112 return FALSE;
290    
291 harbaum 113 gint xs, ys;
292     osm_gps_map_geographic_to_screen (OSM_GPS_MAP(osd->widget),
293     priv->balloon.lat, priv->balloon.lon,
294     &xs, &ys);
295 harbaum 112
296 harbaum 113 xs += priv->balloon.rect.x + priv->balloon.offset_x;
297     ys += priv->balloon.rect.y + priv->balloon.offset_y;
298 harbaum 112
299 harbaum 114 gboolean is_in =
300     (x > xs) && (x < xs + priv->balloon.rect.w) &&
301     (y > ys) && (y < ys + priv->balloon.rect.h);
302    
303 harbaum 120 /* handle the fact that the balloon may have been created by the */
304     /* button down event */
305 harbaum 114 if(!is_in && !down && !priv->balloon.just_created) {
306     /* the user actually clicked outside the balloon */
307    
308     /* close the balloon! */
309     osm_gps_map_osd_clear_balloon (OSM_GPS_MAP(osd->widget));
310     }
311    
312     return is_in;
313 harbaum 113 }
314 harbaum 112
315 harbaum 113 void osm_gps_map_osd_clear_balloon (OsmGpsMap *map) {
316 harbaum 112 g_return_if_fail (OSM_IS_GPS_MAP (map));
317    
318 harbaum 113 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
319     g_return_if_fail (osd);
320 harbaum 112
321 harbaum 113 osd_priv_t *priv = (osd_priv_t*)osd->priv;
322     g_return_if_fail (priv);
323 harbaum 112
324 harbaum 113 if(priv->balloon.surface) {
325     cairo_surface_destroy(priv->balloon.surface);
326     priv->balloon.surface = NULL;
327     priv->balloon.lat = OSM_GPS_MAP_INVALID;
328     priv->balloon.lon = OSM_GPS_MAP_INVALID;
329     }
330     osm_gps_map_redraw(map);
331 harbaum 112 }
332    
333     void
334 harbaum 113 osm_gps_map_osd_draw_balloon (OsmGpsMap *map, float latitude, float longitude,
335     OsmGpsMapBalloonCallback cb, gpointer data) {
336 harbaum 112 g_return_if_fail (OSM_IS_GPS_MAP (map));
337    
338 harbaum 113 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
339     g_return_if_fail (osd);
340 harbaum 112
341 harbaum 113 osd_priv_t *priv = (osd_priv_t*)osd->priv;
342     g_return_if_fail (priv);
343 harbaum 112
344 harbaum 113 osm_gps_map_osd_clear_balloon (map);
345    
346     /* allocate balloon surface */
347     priv->balloon.surface =
348     cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
349     BALLOON_W+2, BALLOON_H+2);
350    
351     priv->balloon.lat = latitude;
352     priv->balloon.lon = longitude;
353     priv->balloon.cb = cb;
354     priv->balloon.data = data;
355 harbaum 114 priv->balloon.just_created = TRUE;
356 harbaum 113
357     priv->balloon.orientation = -1;
358    
359     osd_render_balloon(osd);
360    
361     osm_gps_map_redraw(map);
362 harbaum 112 }
363    
364     #endif // OSD_BALLOON
365    
366 harbaum 70 /* position and extent of bounding box */
367 harbaum 77 #ifndef OSD_X
368 harbaum 70 #define OSD_X (10)
369 harbaum 77 #endif
370    
371     #ifndef OSD_Y
372 harbaum 70 #define OSD_Y (10)
373 harbaum 77 #endif
374 harbaum 70
375     /* parameters of the direction shape */
376 harbaum 77 #ifndef OSD_DIAMETER
377 harbaum 70 #define D_RAD (30) // diameter of dpad
378     #else
379 harbaum 77 #define D_RAD (OSD_DIAMETER)
380 harbaum 70 #endif
381     #define D_TIP (4*D_RAD/5) // distance of arrow tip from dpad center
382     #define D_LEN (D_RAD/4) // length of arrow
383     #define D_WID (D_LEN) // width of arrow
384    
385     /* parameters of the "zoom" pad */
386     #define Z_STEP (D_RAD/4) // distance between dpad and zoom
387     #define Z_RAD (D_RAD/2) // radius of "caps" of zoom bar
388    
389 harbaum 74 #ifdef OSD_SHADOW_ENABLE
390 harbaum 70 /* shadow also depends on control size */
391     #define OSD_SHADOW (D_RAD/6)
392 harbaum 74 #else
393     #define OSD_SHADOW (0)
394     #endif
395 harbaum 70
396 harbaum 77 /* normally the GPS button is in the center of the dpad. if there's */
397     /* no dpad it will go into the zoom area */
398     #if defined(OSD_GPS_BUTTON) && defined(OSD_NO_DPAD)
399     #define Z_GPS 1
400     #else
401     #define Z_GPS 0
402     #endif
403    
404 harbaum 70 /* total width and height of controls incl. shadow */
405 harbaum 77 #define OSD_W (2*D_RAD + OSD_SHADOW + Z_GPS * 2 * Z_RAD)
406     #if !Z_GPS
407 harbaum 70 #define OSD_H (2*D_RAD + Z_STEP + 2*Z_RAD + OSD_SHADOW)
408 harbaum 77 #else
409     #define OSD_H (2*Z_RAD + OSD_SHADOW)
410     #endif
411 harbaum 70
412 harbaum 74 #ifdef OSD_SHADOW_ENABLE
413 harbaum 70 #define OSD_LBL_SHADOW (OSD_SHADOW/2)
414 harbaum 74 #endif
415 harbaum 70
416 harbaum 77 #define Z_TOP ((1-Z_GPS) * (2 * D_RAD + Z_STEP))
417    
418 harbaum 70 #define Z_MID (Z_TOP + Z_RAD)
419     #define Z_BOT (Z_MID + Z_RAD)
420     #define Z_LEFT (Z_RAD)
421 harbaum 77 #define Z_RIGHT (2 * D_RAD - Z_RAD + Z_GPS * 2 * Z_RAD)
422     #define Z_CENTER ((Z_RIGHT + Z_LEFT)/2)
423 harbaum 70
424     /* create the cairo shape used for the zoom buttons */
425     static void
426 harbaum 86 osd_zoom_shape(cairo_t *cr, gint x, gint y)
427 harbaum 71 {
428 harbaum 70 cairo_move_to (cr, x+Z_LEFT, y+Z_TOP);
429     cairo_line_to (cr, x+Z_RIGHT, y+Z_TOP);
430     cairo_arc (cr, x+Z_RIGHT, y+Z_MID, Z_RAD, -M_PI/2, M_PI/2);
431     cairo_line_to (cr, x+Z_LEFT, y+Z_BOT);
432     cairo_arc (cr, x+Z_LEFT, y+Z_MID, Z_RAD, M_PI/2, -M_PI/2);
433     }
434    
435 harbaum 86 /* ------------------- color/shadow functions ----------------- */
436    
437     #ifndef OSD_COLOR
438     /* if no color has been specified we just use the gdks default colors */
439     static void
440     osd_labels(cairo_t *cr, gint width, gboolean enabled,
441     GdkColor *fg, GdkColor *disabled) {
442     if(enabled) gdk_cairo_set_source_color(cr, fg);
443     else gdk_cairo_set_source_color(cr, disabled);
444     cairo_set_line_width (cr, width);
445     }
446     #else
447     static void
448     osd_labels(cairo_t *cr, gint width, gboolean enabled) {
449     if(enabled) cairo_set_source_rgb (cr, OSD_COLOR);
450     else cairo_set_source_rgb (cr, OSD_COLOR_DISABLED);
451     cairo_set_line_width (cr, width);
452     }
453     #endif
454    
455     #ifdef OSD_SHADOW_ENABLE
456     static void
457     osd_labels_shadow(cairo_t *cr, gint width, gboolean enabled) {
458     cairo_set_source_rgba (cr, 0, 0, 0, enabled?0.3:0.15);
459     cairo_set_line_width (cr, width);
460     }
461     #endif
462    
463 harbaum 76 #ifndef OSD_NO_DPAD
464 harbaum 70 /* create the cairo shape used for the dpad */
465     static void
466 harbaum 86 osd_dpad_shape(cairo_t *cr, gint x, gint y)
467 harbaum 71 {
468 harbaum 70 cairo_arc (cr, x+D_RAD, y+D_RAD, D_RAD, 0, 2 * M_PI);
469     }
470 harbaum 76 #endif
471 harbaum 70
472 harbaum 86 #ifdef OSD_SHADOW_ENABLE
473     static void
474     osd_shape_shadow(cairo_t *cr) {
475     cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
476     cairo_fill (cr);
477     cairo_stroke (cr);
478     }
479     #endif
480    
481     #ifndef OSD_COLOR
482     /* if no color has been specified we just use the gdks default colors */
483     static void
484     osd_shape(cairo_t *cr, GdkColor *bg, GdkColor *fg) {
485     gdk_cairo_set_source_color(cr, bg);
486     cairo_fill_preserve (cr);
487     gdk_cairo_set_source_color(cr, fg);
488     cairo_set_line_width (cr, 1);
489     cairo_stroke (cr);
490     }
491     #else
492     static void
493     osd_shape(cairo_t *cr) {
494     cairo_set_source_rgb (cr, OSD_COLOR_BG);
495     cairo_fill_preserve (cr);
496     cairo_set_source_rgb (cr, OSD_COLOR);
497     cairo_set_line_width (cr, 1);
498     cairo_stroke (cr);
499     }
500     #endif
501    
502    
503 harbaum 70 static gboolean
504     osm_gps_map_in_circle(gint x, gint y, gint cx, gint cy, gint rad)
505     {
506     return( pow(cx - x, 2) + pow(cy - y, 2) < rad * rad);
507     }
508    
509 harbaum 76 #ifndef OSD_NO_DPAD
510 harbaum 70 /* check whether x/y is within the dpad */
511     static osd_button_t
512 harbaum 86 osd_check_dpad(gint x, gint y)
513 harbaum 70 {
514     /* within entire dpad circle */
515 harbaum 77 if( osm_gps_map_in_circle(x, y, D_RAD, D_RAD, D_RAD))
516 harbaum 70 {
517     /* convert into position relative to dpads centre */
518 harbaum 77 x -= D_RAD;
519     y -= D_RAD;
520 harbaum 70
521 harbaum 76 #ifdef OSD_GPS_BUTTON
522 harbaum 70 /* check for dpad center goes here! */
523     if( osm_gps_map_in_circle(x, y, 0, 0, D_RAD/3))
524     return OSD_GPS;
525 harbaum 76 #endif
526 harbaum 70
527     if( y < 0 && abs(x) < abs(y))
528     return OSD_UP;
529    
530     if( y > 0 && abs(x) < abs(y))
531     return OSD_DOWN;
532    
533     if( x < 0 && abs(y) < abs(x))
534     return OSD_LEFT;
535    
536     if( x > 0 && abs(y) < abs(x))
537     return OSD_RIGHT;
538    
539     return OSD_BG;
540     }
541     return OSD_NONE;
542     }
543 harbaum 76 #endif
544 harbaum 70
545     /* check whether x/y is within the zoom pads */
546     static osd_button_t
547 harbaum 86 osd_check_zoom(gint x, gint y) {
548 harbaum 77 if( x > 0 && x < OSD_W && y > Z_TOP && y < Z_BOT) {
549 harbaum 70
550     /* within circle around (-) label */
551 harbaum 77 if( osm_gps_map_in_circle(x, y, Z_LEFT, Z_MID, Z_RAD))
552 harbaum 70 return OSD_OUT;
553    
554 harbaum 77 /* within circle around (+) label */
555     if( osm_gps_map_in_circle(x, y, Z_RIGHT, Z_MID, Z_RAD))
556     return OSD_IN;
557    
558     #if Z_GPS == 1
559     /* within square around center */
560     if( x > Z_CENTER - Z_RAD && x < Z_CENTER + Z_RAD)
561     return OSD_GPS;
562     #endif
563    
564 harbaum 70 /* between center of (-) button and center of entire zoom control area */
565 harbaum 77 if(x > OSD_LEFT && x < D_RAD)
566 harbaum 70 return OSD_OUT;
567    
568     /* between center of (+) button and center of entire zoom control area */
569 harbaum 77 if(x < OSD_RIGHT && x > D_RAD)
570 harbaum 70 return OSD_IN;
571     }
572    
573     return OSD_NONE;
574     }
575    
576 harbaum 88 #ifdef OSD_SOURCE_SEL
577    
578 harbaum 86 /* place source selection at right border */
579     #define OSD_S_RAD (Z_RAD)
580     #define OSD_S_X (-OSD_X)
581     #define OSD_S_Y (OSD_Y)
582     #define OSD_S_PW (2 * Z_RAD)
583     #define OSD_S_W (OSD_S_PW)
584     #define OSD_S_PH (2 * Z_RAD)
585     #define OSD_S_H (OSD_S_PH + OSD_SHADOW)
586    
587 harbaum 87 /* size of usable area when expanded */
588 harbaum 110 #define OSD_S_AREA_W (priv->source_sel.width)
589     #define OSD_S_AREA_H (priv->source_sel.height)
590 harbaum 86 #define OSD_S_EXP_W (OSD_S_PW + OSD_S_AREA_W + OSD_SHADOW)
591     #define OSD_S_EXP_H (OSD_S_AREA_H + OSD_SHADOW)
592    
593     /* internal value to draw the arrow on the "puller" */
594     #define OSD_S_D0 (OSD_S_RAD/2)
595 harbaum 87 #ifndef OSD_FONT_SIZE
596     #define OSD_FONT_SIZE 16.0
597     #endif
598     #define OSD_TEXT_BORDER (OSD_FONT_SIZE/2)
599     #define OSD_TEXT_SKIP (OSD_FONT_SIZE/8)
600 harbaum 86
601 harbaum 88 /* draw the shape of the source selection OSD, either only the puller (not expanded) */
602     /* or the entire menu incl. the puller (expanded) */
603 harbaum 86 static void
604     osd_source_shape(osd_priv_t *priv, cairo_t *cr, gint x, gint y) {
605 harbaum 110 if(!priv->source_sel.expanded) {
606 harbaum 86 /* just draw the puller */
607     cairo_move_to (cr, x + OSD_S_PW, y + OSD_S_PH);
608     cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2);
609     cairo_line_to (cr, x + OSD_S_PW, y);
610     } else {
611     /* draw the puller and the area itself */
612     cairo_move_to (cr, x + OSD_S_PW + OSD_S_AREA_W, y + OSD_S_AREA_H);
613     cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_AREA_H);
614     if(OSD_S_Y > 0) {
615     cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_PH);
616     cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2);
617     } else {
618     cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_AREA_H-OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2);
619     cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_AREA_H - OSD_S_PH);
620     cairo_line_to (cr, x + OSD_S_PW, y);
621     }
622     cairo_line_to (cr, x + OSD_S_PW + OSD_S_AREA_W, y);
623     cairo_close_path (cr);
624     }
625     }
626    
627     static void
628 harbaum 87 osd_source_content(osm_gps_map_osd_t *osd, cairo_t *cr, gint offset) {
629     osd_priv_t *priv = (osd_priv_t*)osd->priv;
630 harbaum 86
631 harbaum 87 int py = offset + OSD_S_RAD - OSD_S_D0;
632    
633 harbaum 110 if(!priv->source_sel.expanded) {
634 harbaum 86 /* draw the "puller" open (<) arrow */
635 harbaum 87 cairo_move_to (cr, offset + OSD_S_RAD + OSD_S_D0/2, py);
636 harbaum 86 cairo_rel_line_to (cr, -OSD_S_D0, +OSD_S_D0);
637     cairo_rel_line_to (cr, +OSD_S_D0, +OSD_S_D0);
638     } else {
639     if(OSD_S_Y < 0)
640 harbaum 87 py += OSD_S_AREA_H - OSD_S_PH;
641 harbaum 86
642     /* draw the "puller" close (>) arrow */
643 harbaum 87 cairo_move_to (cr, offset + OSD_S_RAD - OSD_S_D0/2, py);
644 harbaum 86 cairo_rel_line_to (cr, +OSD_S_D0, +OSD_S_D0);
645     cairo_rel_line_to (cr, -OSD_S_D0, +OSD_S_D0);
646 harbaum 87 cairo_stroke(cr);
647    
648     /* don't draw a shadow for the text content */
649     if(offset == 1) {
650     gint source;
651     g_object_get(osd->widget, "map-source", &source, NULL);
652    
653     cairo_select_font_face (cr, "Sans",
654     CAIRO_FONT_SLANT_NORMAL,
655     CAIRO_FONT_WEIGHT_BOLD);
656     cairo_set_font_size (cr, OSD_FONT_SIZE);
657    
658 harbaum 110 int i, step = (priv->source_sel.height - 2*OSD_TEXT_BORDER) /
659 harbaum 89 OSM_GPS_MAP_SOURCE_LAST;
660 harbaum 87 for(i=OSM_GPS_MAP_SOURCE_NULL+1;i<=OSM_GPS_MAP_SOURCE_LAST;i++) {
661     cairo_text_extents_t extents;
662     const char *src = osm_gps_map_source_get_friendly_name(i);
663     cairo_text_extents (cr, src, &extents);
664    
665     int x = offset + OSD_S_PW + OSD_TEXT_BORDER;
666     int y = offset + step * (i-1) + OSD_TEXT_BORDER;
667    
668     /* draw filled rectangle if selected */
669     if(source == i) {
670     cairo_rectangle(cr, x - OSD_TEXT_BORDER/2,
671     y - OSD_TEXT_SKIP,
672 harbaum 110 priv->source_sel.width - OSD_TEXT_BORDER,
673 harbaum 87 step + OSD_TEXT_SKIP);
674     cairo_fill(cr);
675    
676     /* temprarily draw with background color */
677     #ifndef OSD_COLOR
678     GdkColor bg = osd->widget->style->bg[GTK_STATE_NORMAL];
679     gdk_cairo_set_source_color(cr, &bg);
680     #else
681     cairo_set_source_rgb (cr, OSD_COLOR_BG);
682     #endif
683     }
684    
685     cairo_move_to (cr, x, y + OSD_TEXT_SKIP - extents.y_bearing);
686     cairo_show_text (cr, src);
687    
688     /* restore color */
689     if(source == i) {
690     #ifndef OSD_COLOR
691     GdkColor fg = osd->widget->style->fg[GTK_STATE_NORMAL];
692     gdk_cairo_set_source_color(cr, &fg);
693     #else
694     cairo_set_source_rgb (cr, OSD_COLOR);
695     #endif
696     }
697     }
698     }
699 harbaum 86 }
700     }
701    
702     static void
703 harbaum 111 osd_render_source_sel(osm_gps_map_osd_t *osd, gboolean force_rerender) {
704 harbaum 86 osd_priv_t *priv = (osd_priv_t*)osd->priv;
705    
706 harbaum 111 if(priv->source_sel.rendered && !force_rerender)
707     return;
708    
709     priv->source_sel.rendered = TRUE;
710    
711 harbaum 86 #ifndef OSD_COLOR
712     GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL];
713     GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL];
714     GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE];
715     #endif
716    
717     /* draw source selector */
718 harbaum 110 cairo_t *cr = cairo_create(priv->source_sel.surface);
719 harbaum 86 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
720     cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
721     cairo_paint(cr);
722     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
723    
724     #ifdef OSD_SHADOW_ENABLE
725     osd_source_shape(priv, cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
726     osd_shape_shadow(cr);
727     #endif
728    
729     osd_source_shape(priv, cr, 1, 1);
730     #ifndef OSD_COLOR
731     osd_shape(cr, &bg, &fg);
732     #else
733     osd_shape(cr);
734     #endif
735    
736     #ifdef OSD_SHADOW_ENABLE
737     osd_labels_shadow(cr, Z_RAD/3, TRUE);
738 harbaum 87 osd_source_content(osd, cr, 1+OSD_LBL_SHADOW);
739     cairo_stroke (cr);
740 harbaum 86 #endif
741     #ifndef OSD_COLOR
742     osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
743     #else
744     osd_labels(cr, Z_RAD/3, TRUE);
745     #endif
746 harbaum 87 osd_source_content(osd, cr, 1);
747     cairo_stroke (cr);
748 harbaum 86
749     cairo_destroy(cr);
750     }
751    
752 harbaum 89 /* re-allocate the buffer used to draw the menu. This is used */
753     /* to collapse/expand the buffer */
754 harbaum 86 static void
755     osd_source_reallocate(osm_gps_map_osd_t *osd) {
756     osd_priv_t *priv = (osd_priv_t*)osd->priv;
757    
758     /* re-allocate offscreen bitmap */
759 harbaum 110 g_assert (priv->source_sel.surface);
760 harbaum 86
761     int w = OSD_S_W, h = OSD_S_H;
762 harbaum 110 if(priv->source_sel.expanded) {
763 harbaum 87 cairo_text_extents_t extents;
764    
765     /* determine content size */
766 harbaum 110 cairo_t *cr = cairo_create(priv->source_sel.surface);
767 harbaum 87 cairo_select_font_face (cr, "Sans",
768     CAIRO_FONT_SLANT_NORMAL,
769     CAIRO_FONT_WEIGHT_BOLD);
770     cairo_set_font_size (cr, OSD_FONT_SIZE);
771    
772     /* calculate menu size */
773     int i, max_h = 0, max_w = 0;
774     for(i=OSM_GPS_MAP_SOURCE_NULL+1;i<=OSM_GPS_MAP_SOURCE_LAST;i++) {
775     const char *src = osm_gps_map_source_get_friendly_name(i);
776     cairo_text_extents (cr, src, &extents);
777    
778     if(extents.width > max_w) max_w = extents.width;
779     if(extents.height > max_h) max_h = extents.height;
780     }
781     cairo_destroy(cr);
782    
783 harbaum 110 priv->source_sel.width = max_w + 2*OSD_TEXT_BORDER;
784     priv->source_sel.height = OSM_GPS_MAP_SOURCE_LAST *
785 harbaum 87 (max_h + 2*OSD_TEXT_SKIP) + 2*OSD_TEXT_BORDER;
786    
787 harbaum 86 w = OSD_S_EXP_W;
788     h = OSD_S_EXP_H;
789     }
790    
791 harbaum 110 cairo_surface_destroy(priv->source_sel.surface);
792     priv->source_sel.surface =
793 harbaum 86 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w+2, h+2);
794    
795 harbaum 111 osd_render_source_sel(osd, TRUE);
796 harbaum 86 }
797    
798     #define OSD_HZ 15
799 harbaum 87 #define OSD_TIME 500
800 harbaum 86
801     static gboolean osd_source_animate(gpointer data) {
802     osm_gps_map_osd_t *osd = (osm_gps_map_osd_t*)data;
803     osd_priv_t *priv = (osd_priv_t*)osd->priv;
804     int diff = OSD_S_EXP_W - OSD_S_W - OSD_S_X;
805     gboolean done = FALSE;
806 harbaum 110 priv->source_sel.count += priv->source_sel.dir;
807 harbaum 86
808     /* shifting in */
809 harbaum 110 if(priv->source_sel.dir < 0) {
810     if(priv->source_sel.count <= 0) {
811     priv->source_sel.count = 0;
812 harbaum 86 done = TRUE;
813     }
814     } else {
815 harbaum 110 if(priv->source_sel.count >= 1000) {
816     priv->source_sel.expanded = FALSE;
817 harbaum 86 osd_source_reallocate(osd);
818    
819 harbaum 110 priv->source_sel.count = 1000;
820 harbaum 86 done = TRUE;
821     }
822     }
823    
824    
825     /* count runs linearly from 0 to 1000, map this nicely onto a position */
826    
827 harbaum 111 /* nice sinoid mapping */
828 harbaum 110 float m = 0.5-cos(priv->source_sel.count * M_PI / 1000.0)/2;
829 harbaum 111 priv->source_sel.shift =
830     (osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X) +
831 harbaum 86 m * diff;
832    
833 harbaum 111 /* make sure the screen is updated */
834 harbaum 86 osm_gps_map_repaint(OSM_GPS_MAP(osd->widget));
835    
836 harbaum 111 /* stop animation if done */
837 harbaum 86 if(done)
838 harbaum 110 priv->source_sel.handler_id = 0;
839 harbaum 86
840     return !done;
841     }
842    
843     /* switch between expand and collapse mode of source selection */
844     static void
845     osd_source_toggle(osm_gps_map_osd_t *osd)
846     {
847     osd_priv_t *priv = (osd_priv_t*)osd->priv;
848    
849     /* ignore clicks while animation is running */
850 harbaum 110 if(priv->source_sel.handler_id)
851 harbaum 86 return;
852    
853 harbaum 111 /* expand immediately, collapse is handle at the end of the */
854     /* collapse animation */
855 harbaum 110 if(!priv->source_sel.expanded) {
856     priv->source_sel.expanded = TRUE;
857 harbaum 86 osd_source_reallocate(osd);
858    
859 harbaum 110 priv->source_sel.count = 1000;
860     priv->source_sel.shift = osd->widget->allocation.width - OSD_S_W;
861     priv->source_sel.dir = -1000/OSD_HZ;
862 harbaum 86 } else {
863 harbaum 110 priv->source_sel.count = 0;
864 harbaum 111 priv->source_sel.shift = osd->widget->allocation.width -
865     OSD_S_EXP_W + OSD_S_X;
866 harbaum 110 priv->source_sel.dir = +1000/OSD_HZ;
867 harbaum 86 }
868    
869 harbaum 111 /* start timer to handle animation */
870     priv->source_sel.handler_id = gtk_timeout_add(OSD_TIME/OSD_HZ,
871     osd_source_animate, osd);
872 harbaum 86 }
873    
874 harbaum 89 /* check if the user clicked inside the source selection area */
875 harbaum 70 static osd_button_t
876 harbaum 114 osd_source_check(osm_gps_map_osd_t *osd, gboolean down, gint x, gint y) {
877 harbaum 86 osd_priv_t *priv = (osd_priv_t*)osd->priv;
878    
879 harbaum 110 if(!priv->source_sel.expanded)
880 harbaum 86 x -= osd->widget->allocation.width - OSD_S_W;
881     else
882     x -= osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
883    
884     if(OSD_S_Y > 0)
885     y -= OSD_S_Y;
886     else
887     y -= osd->widget->allocation.height - OSD_S_PH + OSD_S_Y;
888    
889     /* within square around puller? */
890     if(y > 0 && y < OSD_S_PH && x > 0 && x < OSD_S_PW) {
891     /* really within puller shape? */
892     if(x > Z_RAD || osm_gps_map_in_circle(x, y, Z_RAD, Z_RAD, Z_RAD)) {
893     /* expand source selector */
894 harbaum 114 if(down)
895     osd_source_toggle(osd);
896 harbaum 86
897     /* tell upper layers that user clicked some background element */
898     /* of the OSD */
899     return OSD_BG;
900     }
901     }
902 harbaum 88
903     /* check for clicks into data area */
904 harbaum 110 if(priv->source_sel.expanded && !priv->source_sel.handler_id) {
905 harbaum 94 /* re-adjust from puller top to content top */
906     if(OSD_S_Y < 0)
907     y += OSD_S_EXP_H - OSD_S_PH;
908    
909 harbaum 88 if(x > OSD_S_PW &&
910     x < OSD_S_PW + OSD_S_EXP_W &&
911     y > 0 &&
912     y < OSD_S_EXP_H) {
913 harbaum 94
914 harbaum 110 int step = (priv->source_sel.height - 2*OSD_TEXT_BORDER)
915 harbaum 89 / OSM_GPS_MAP_SOURCE_LAST;
916 harbaum 88
917 harbaum 89 y -= OSD_TEXT_BORDER - OSD_TEXT_SKIP;
918     y /= step;
919     y += 1;
920    
921 harbaum 114 if(down) {
922     gint old = 0;
923     g_object_get(osd->widget, "map-source", &old, NULL);
924 harbaum 89
925 harbaum 114 if(y > OSM_GPS_MAP_SOURCE_NULL &&
926     y <= OSM_GPS_MAP_SOURCE_LAST &&
927     old != y) {
928     g_object_set(osd->widget, "map-source", y, NULL);
929    
930     osd_render_source_sel(osd, TRUE);
931     osm_gps_map_repaint(OSM_GPS_MAP(osd->widget));
932     }
933 harbaum 89 }
934    
935     /* return "clicked in OSD background" to prevent further */
936     /* processing by application */
937 harbaum 88 return OSD_BG;
938     }
939     }
940    
941 harbaum 86 return OSD_NONE;
942     }
943 harbaum 88 #endif // OSD_SOURCE_SEL
944 harbaum 86
945     static osd_button_t
946 harbaum 114 osd_check(osm_gps_map_osd_t *osd, gboolean down, gint x, gint y) {
947 harbaum 70 osd_button_t but = OSD_NONE;
948    
949 harbaum 113 #ifdef OSD_BALLOON
950 harbaum 114 if(down) {
951     /* needed to handle balloons that are created at click */
952     osd_priv_t *priv = (osd_priv_t*)osd->priv;
953     priv->balloon.just_created = FALSE;
954     }
955 harbaum 113 #endif
956    
957 harbaum 86 #ifdef OSD_SOURCE_SEL
958     /* the source selection area is handles internally */
959 harbaum 114 but = osd_source_check(osd, down, x, y);
960 harbaum 86 #endif
961 harbaum 114
962     if(but == OSD_NONE) {
963 harbaum 120 gint mx = x - OSD_X;
964     gint my = y - OSD_Y;
965 harbaum 114
966     if(OSD_X < 0)
967 harbaum 120 mx -= (osd->widget->allocation.width - OSD_W);
968 harbaum 114
969     if(OSD_Y < 0)
970 harbaum 120 my -= (osd->widget->allocation.height - OSD_H);
971 harbaum 114
972     /* first do a rough test for the OSD area. */
973     /* this is just to avoid an unnecessary detailed test */
974 harbaum 120 if(mx > 0 && mx < OSD_W && my > 0 && my < OSD_H) {
975 harbaum 76 #ifndef OSD_NO_DPAD
976 harbaum 120 but = osd_check_dpad(mx, my);
977 harbaum 76 #endif
978 harbaum 114 }
979 harbaum 70
980     if(but == OSD_NONE)
981 harbaum 120 but = osd_check_zoom(mx, my);
982 harbaum 70 }
983    
984 harbaum 114 #ifdef OSD_BALLOON
985     if(but == OSD_NONE) {
986     /* check if user clicked into balloon */
987     if(osd_balloon_check(osd, down, x, y))
988     but = OSD_BG;
989     }
990     #endif
991    
992 harbaum 70 return but;
993     }
994    
995 harbaum 76 #ifndef OSD_NO_DPAD
996 harbaum 70 static void
997 harbaum 86 osd_dpad_labels(cairo_t *cr, gint x, gint y) {
998 harbaum 70 /* move reference to dpad center */
999     x += D_RAD;
1000     y += D_RAD;
1001    
1002     const static gint offset[][3][2] = {
1003     /* left arrow/triangle */
1004     { { -D_TIP+D_LEN, -D_WID }, { -D_LEN, D_WID }, { +D_LEN, D_WID } },
1005     /* right arrow/triangle */
1006     { { +D_TIP-D_LEN, -D_WID }, { +D_LEN, D_WID }, { -D_LEN, D_WID } },
1007     /* top arrow/triangle */
1008     { { -D_WID, -D_TIP+D_LEN }, { D_WID, -D_LEN }, { D_WID, +D_LEN } },
1009     /* bottom arrow/triangle */
1010     { { -D_WID, +D_TIP-D_LEN }, { D_WID, +D_LEN }, { D_WID, -D_LEN } }
1011     };
1012    
1013     int i;
1014     for(i=0;i<4;i++) {
1015     cairo_move_to (cr, x + offset[i][0][0], y + offset[i][0][1]);
1016     cairo_rel_line_to (cr, offset[i][1][0], offset[i][1][1]);
1017     cairo_rel_line_to (cr, offset[i][2][0], offset[i][2][1]);
1018     }
1019     }
1020 harbaum 76 #endif
1021 harbaum 70
1022 harbaum 76 #ifdef OSD_GPS_BUTTON
1023     /* draw the satellite dish icon in the center of the dpad */
1024 harbaum 77 #define GPS_V0 (D_RAD/7)
1025 harbaum 70 #define GPS_V1 (D_RAD/10)
1026     #define GPS_V2 (D_RAD/5)
1027    
1028     /* draw a satellite receiver dish */
1029 harbaum 77 /* this is either drawn in the center of the dpad (if present) */
1030     /* or in the middle of the zoom area */
1031 harbaum 70 static void
1032 harbaum 86 osd_dpad_gps(cairo_t *cr, gint x, gint y) {
1033 harbaum 70 /* move reference to dpad center */
1034 harbaum 77 x += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD * 3;
1035     y += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD + GPS_V0;
1036 harbaum 70
1037     cairo_move_to (cr, x-GPS_V0, y+GPS_V0);
1038     cairo_rel_line_to (cr, +GPS_V0, -GPS_V0);
1039     cairo_rel_line_to (cr, +GPS_V0, +GPS_V0);
1040     cairo_close_path (cr);
1041    
1042     cairo_move_to (cr, x+GPS_V1-GPS_V2, y-2*GPS_V2);
1043     cairo_curve_to (cr, x-GPS_V2, y, x+GPS_V1, y+GPS_V1, x+GPS_V1+GPS_V2, y);
1044     cairo_close_path (cr);
1045    
1046     x += GPS_V1;
1047     cairo_move_to (cr, x, y-GPS_V2);
1048     cairo_rel_line_to (cr, +GPS_V1, -GPS_V1);
1049     }
1050 harbaum 76 #endif
1051 harbaum 70
1052     #define Z_LEN (2*Z_RAD/3)
1053    
1054     static void
1055 harbaum 86 osd_zoom_labels(cairo_t *cr, gint x, gint y) {
1056 harbaum 70 cairo_move_to (cr, x + Z_LEFT - Z_LEN, y + Z_MID);
1057     cairo_line_to (cr, x + Z_LEFT + Z_LEN, y + Z_MID);
1058    
1059     cairo_move_to (cr, x + Z_RIGHT, y + Z_MID - Z_LEN);
1060     cairo_line_to (cr, x + Z_RIGHT, y + Z_MID + Z_LEN);
1061     cairo_move_to (cr, x + Z_RIGHT - Z_LEN, y + Z_MID);
1062     cairo_line_to (cr, x + Z_RIGHT + Z_LEN, y + Z_MID);
1063     }
1064    
1065 harbaum 106 #ifdef OSD_COORDINATES
1066    
1067 harbaum 107 #ifndef OSD_COORDINATES_FONT_SIZE
1068     #define OSD_COORDINATES_FONT_SIZE 12
1069     #endif
1070    
1071 harbaum 108 #define OSD_COORDINATES_OFFSET (OSD_COORDINATES_FONT_SIZE/6)
1072 harbaum 107
1073 harbaum 108 #define OSD_COORDINATES_W (8*OSD_COORDINATES_FONT_SIZE+2*OSD_COORDINATES_OFFSET)
1074     #define OSD_COORDINATES_H (2*OSD_COORDINATES_FONT_SIZE+OSD_COORDINATES_OFFSET)
1075    
1076 harbaum 107 /* these can be overwritten with versions that support */
1077     /* localization */
1078     #ifndef OSD_COORDINATES_CHR_N
1079     #define OSD_COORDINATES_CHR_N "N"
1080     #endif
1081     #ifndef OSD_COORDINATES_CHR_S
1082     #define OSD_COORDINATES_CHR_S "S"
1083     #endif
1084     #ifndef OSD_COORDINATES_CHR_E
1085     #define OSD_COORDINATES_CHR_E "E"
1086     #endif
1087     #ifndef OSD_COORDINATES_CHR_W
1088     #define OSD_COORDINATES_CHR_W "W"
1089     #endif
1090    
1091    
1092    
1093     /* this is the classic geocaching notation */
1094     static char
1095     *osd_latitude_str(float latitude) {
1096     char *c = OSD_COORDINATES_CHR_N;
1097     float integral, fractional;
1098    
1099     if(isnan(latitude))
1100     return NULL;
1101    
1102     if(latitude < 0) {
1103     latitude = fabs(latitude);
1104     c = OSD_COORDINATES_CHR_S;
1105     }
1106    
1107     fractional = modff(latitude, &integral);
1108    
1109     return g_strdup_printf("%s %02d° %06.3f'",
1110     c, (int)integral, fractional*60.0);
1111     }
1112    
1113     static char
1114     *osd_longitude_str(float longitude) {
1115     char *c = OSD_COORDINATES_CHR_E;
1116     float integral, fractional;
1117    
1118     if(isnan(longitude))
1119     return NULL;
1120    
1121     if(longitude < 0) {
1122     longitude = fabs(longitude);
1123     c = OSD_COORDINATES_CHR_W;
1124     }
1125    
1126     fractional = modff(longitude, &integral);
1127    
1128     return g_strdup_printf("%s %03d° %06.3f'",
1129     c, (int)integral, fractional*60.0);
1130     }
1131    
1132 harbaum 106 static void
1133     osd_render_coordinates(osm_gps_map_osd_t *osd)
1134     {
1135     osd_priv_t *priv = (osd_priv_t*)osd->priv;
1136    
1137 harbaum 107 /* get current map position */
1138     gfloat lat, lon;
1139     g_object_get(osd->widget, "latitude", &lat, "longitude", &lon, NULL);
1140    
1141 harbaum 108 /* check if position has changed enough to require redraw */
1142 harbaum 110 if(!isnan(priv->coordinates.lat) && !isnan(priv->coordinates.lon))
1143     /* 1/60000 == 1/1000 minute */
1144     if((fabsf(lat - priv->coordinates.lat) < 1/60000) &&
1145     (fabsf(lon - priv->coordinates.lon) < 1/60000))
1146 harbaum 108 return;
1147    
1148 harbaum 110 priv->coordinates.lat = lat;
1149     priv->coordinates.lon = lon;
1150 harbaum 108
1151 harbaum 111 /* first fill with transparency */
1152 harbaum 110 cairo_t *cr = cairo_create(priv->coordinates.surface);
1153 harbaum 106 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1154 harbaum 111 // cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.5);
1155     cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
1156 harbaum 106 cairo_paint(cr);
1157     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1158    
1159 harbaum 107 cairo_select_font_face (cr, "Sans",
1160     CAIRO_FONT_SLANT_NORMAL,
1161     CAIRO_FONT_WEIGHT_BOLD);
1162     cairo_set_font_size (cr, OSD_COORDINATES_FONT_SIZE);
1163    
1164     char *latitude = osd_latitude_str(lat);
1165     char *longitude = osd_longitude_str(lon);
1166    
1167     cairo_text_extents_t lat_extents, lon_extents;
1168     cairo_text_extents (cr, latitude, &lat_extents);
1169     cairo_text_extents (cr, longitude, &lon_extents);
1170    
1171     cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1172     cairo_set_line_width (cr, OSD_COORDINATES_FONT_SIZE/6);
1173 harbaum 108 cairo_move_to (cr,
1174     (OSD_COORDINATES_W - lat_extents.width)/2,
1175 harbaum 107 OSD_COORDINATES_OFFSET - lat_extents.y_bearing);
1176     cairo_text_path (cr, latitude);
1177     cairo_move_to (cr,
1178 harbaum 108 (OSD_COORDINATES_W - lon_extents.width)/2,
1179 harbaum 107 OSD_COORDINATES_OFFSET - lon_extents.y_bearing +
1180     OSD_COORDINATES_FONT_SIZE);
1181     cairo_text_path (cr, longitude);
1182     cairo_stroke (cr);
1183    
1184     cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
1185     cairo_move_to (cr,
1186 harbaum 108 (OSD_COORDINATES_W - lat_extents.width)/2,
1187 harbaum 107 OSD_COORDINATES_OFFSET - lat_extents.y_bearing);
1188     cairo_show_text (cr, latitude);
1189     cairo_move_to (cr,
1190 harbaum 108 (OSD_COORDINATES_W - lon_extents.width)/2,
1191 harbaum 107 OSD_COORDINATES_OFFSET - lon_extents.y_bearing +
1192     OSD_COORDINATES_FONT_SIZE);
1193     cairo_show_text (cr, longitude);
1194    
1195     g_free(latitude);
1196     g_free(longitude);
1197    
1198 harbaum 106 cairo_destroy(cr);
1199     }
1200     #endif // OSD_COORDINATES
1201    
1202 harbaum 122 #ifdef OSD_NAV
1203     #define OSD_NAV_W (100)
1204     #define OSD_NAV_H (100)
1205    
1206     static void
1207     osd_render_nav(osm_gps_map_osd_t *osd)
1208     {
1209     osd_priv_t *priv = (osd_priv_t*)osd->priv;
1210    
1211     /* first fill with transparency */
1212     cairo_t *cr = cairo_create(priv->nav.surface);
1213     cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1214     cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.2);
1215     cairo_paint(cr);
1216     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1217    
1218     cairo_destroy(cr);
1219     }
1220    
1221     #endif // OSD_NAV
1222    
1223    
1224 harbaum 105 #ifdef OSD_CROSSHAIR
1225    
1226     #ifndef OSD_CROSSHAIR_RADIUS
1227 harbaum 106 #define OSD_CROSSHAIR_RADIUS 10
1228 harbaum 105 #endif
1229    
1230 harbaum 106 #define OSD_CROSSHAIR_TICK (OSD_CROSSHAIR_RADIUS/2)
1231     #define OSD_CROSSHAIR_BORDER (OSD_CROSSHAIR_TICK + OSD_CROSSHAIR_RADIUS/4)
1232     #define OSD_CROSSHAIR_W ((OSD_CROSSHAIR_RADIUS+OSD_CROSSHAIR_BORDER)*2)
1233     #define OSD_CROSSHAIR_H ((OSD_CROSSHAIR_RADIUS+OSD_CROSSHAIR_BORDER)*2)
1234 harbaum 105
1235     static void
1236 harbaum 106 osd_render_crosshair_shape(cairo_t *cr) {
1237     cairo_arc (cr, OSD_CROSSHAIR_W/2, OSD_CROSSHAIR_H/2,
1238     OSD_CROSSHAIR_RADIUS, 0, 2*M_PI);
1239    
1240     cairo_move_to (cr, OSD_CROSSHAIR_W/2 - OSD_CROSSHAIR_RADIUS,
1241     OSD_CROSSHAIR_H/2);
1242     cairo_rel_line_to (cr, -OSD_CROSSHAIR_TICK, 0);
1243     cairo_move_to (cr, OSD_CROSSHAIR_W/2 + OSD_CROSSHAIR_RADIUS,
1244     OSD_CROSSHAIR_H/2);
1245     cairo_rel_line_to (cr, OSD_CROSSHAIR_TICK, 0);
1246    
1247     cairo_move_to (cr, OSD_CROSSHAIR_W/2,
1248     OSD_CROSSHAIR_H/2 - OSD_CROSSHAIR_RADIUS);
1249     cairo_rel_line_to (cr, 0, -OSD_CROSSHAIR_TICK);
1250     cairo_move_to (cr, OSD_CROSSHAIR_W/2,
1251     OSD_CROSSHAIR_H/2 + OSD_CROSSHAIR_RADIUS);
1252     cairo_rel_line_to (cr, 0, OSD_CROSSHAIR_TICK);
1253    
1254     cairo_stroke (cr);
1255     }
1256    
1257     static void
1258 harbaum 105 osd_render_crosshair(osm_gps_map_osd_t *osd)
1259     {
1260     osd_priv_t *priv = (osd_priv_t*)osd->priv;
1261    
1262 harbaum 110 if(priv->crosshair.rendered)
1263     return;
1264    
1265     priv->crosshair.rendered = TRUE;
1266    
1267 harbaum 105 /* first fill with transparency */
1268 harbaum 110 cairo_t *cr = cairo_create(priv->crosshair.surface);
1269 harbaum 105 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1270 harbaum 106 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
1271 harbaum 105 cairo_paint(cr);
1272     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1273    
1274 harbaum 106 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1275    
1276     cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.5);
1277     cairo_set_line_width (cr, OSD_CROSSHAIR_RADIUS/2);
1278     osd_render_crosshair_shape(cr);
1279    
1280     cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1281     cairo_set_line_width (cr, OSD_CROSSHAIR_RADIUS/4);
1282     osd_render_crosshair_shape(cr);
1283    
1284 harbaum 105 cairo_destroy(cr);
1285     }
1286     #endif
1287    
1288     #ifdef OSD_SCALE
1289    
1290     #ifndef OSD_SCALE_FONT_SIZE
1291     #define OSD_SCALE_FONT_SIZE 12
1292     #endif
1293     #define OSD_SCALE_W (10*OSD_SCALE_FONT_SIZE)
1294     #define OSD_SCALE_H (5*OSD_SCALE_FONT_SIZE/2)
1295    
1296 harbaum 103 /* various parameters used to create the scale */
1297     #define OSD_SCALE_H2 (OSD_SCALE_H/2)
1298     #define OSD_SCALE_TICK (2*OSD_SCALE_FONT_SIZE/3)
1299     #define OSD_SCALE_M (OSD_SCALE_H2 - OSD_SCALE_TICK)
1300     #define OSD_SCALE_I (OSD_SCALE_H2 + OSD_SCALE_TICK)
1301     #define OSD_SCALE_FD (OSD_SCALE_FONT_SIZE/4)
1302 harbaum 99
1303 harbaum 70 static void
1304 harbaum 98 osd_render_scale(osm_gps_map_osd_t *osd)
1305     {
1306 harbaum 86 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1307 harbaum 103
1308     /* this only needs to be rendered if the zoom has changed */
1309     gint zoom;
1310     g_object_get(OSM_GPS_MAP(osd->widget), "zoom", &zoom, NULL);
1311 harbaum 110 if(zoom == priv->scale.zoom)
1312 harbaum 103 return;
1313    
1314 harbaum 110 priv->scale.zoom = zoom;
1315 harbaum 103
1316 harbaum 99 float m_per_pix = osm_gps_map_get_scale(OSM_GPS_MAP(osd->widget));
1317 harbaum 70
1318 harbaum 98 /* first fill with transparency */
1319 harbaum 110 cairo_t *cr = cairo_create(priv->scale.surface);
1320 harbaum 98 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1321 harbaum 100 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
1322 harbaum 103 // pink for testing: cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.2);
1323 harbaum 98 cairo_paint(cr);
1324     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1325    
1326     /* determine the size of the scale width in meters */
1327 harbaum 103 float width = (OSD_SCALE_W-OSD_SCALE_FONT_SIZE/6) * m_per_pix;
1328 harbaum 102
1329 harbaum 98 /* scale this to useful values */
1330     int exp = logf(width)*M_LOG10E;
1331     int mant = width/pow(10,exp);
1332 harbaum 99 int width_metric = mant * pow(10,exp);
1333 harbaum 103 char *dist_str = NULL;
1334     if(width_metric<1000)
1335     dist_str = g_strdup_printf("%u m", width_metric);
1336     else
1337     dist_str = g_strdup_printf("%u km", width_metric/1000);
1338 harbaum 99 width_metric /= m_per_pix;
1339    
1340 harbaum 102 /* and now the hard part: scale for useful imperial values :-( */
1341     /* try to convert to feet, 1ft == 0.3048 m */
1342     width /= 0.3048;
1343     float imp_scale = 0.3048;
1344     char *dist_imp_unit = "ft";
1345 harbaum 99
1346 harbaum 102 if(width >= 100) {
1347     /* 1yd == 3 feet */
1348     width /= 3.0;
1349     imp_scale *= 3.0;
1350     dist_imp_unit = "yd";
1351    
1352     if(width >= 1760.0) {
1353     /* 1mi == 1760 yd */
1354     width /= 1760.0;
1355     imp_scale *= 1760.0;
1356     dist_imp_unit = "mi";
1357     }
1358     }
1359    
1360 harbaum 103 /* also convert this to full tens/hundreds */
1361     exp = logf(width)*M_LOG10E;
1362     mant = width/pow(10,exp);
1363     int width_imp = mant * pow(10,exp);
1364     char *dist_str_imp = g_strdup_printf("%u %s", width_imp, dist_imp_unit);
1365 harbaum 102
1366 harbaum 103 /* convert back to pixels */
1367     width_imp *= imp_scale;
1368     width_imp /= m_per_pix;
1369    
1370 harbaum 99 cairo_select_font_face (cr, "Sans",
1371     CAIRO_FONT_SLANT_NORMAL,
1372     CAIRO_FONT_WEIGHT_BOLD);
1373 harbaum 102 cairo_set_font_size (cr, OSD_SCALE_FONT_SIZE);
1374 harbaum 99 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
1375    
1376     cairo_text_extents_t extents;
1377     cairo_text_extents (cr, dist_str, &extents);
1378    
1379 harbaum 100 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1380 harbaum 103 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/6);
1381     cairo_move_to (cr, 2*OSD_SCALE_FD, OSD_SCALE_H2-OSD_SCALE_FD);
1382 harbaum 100 cairo_text_path (cr, dist_str);
1383     cairo_stroke (cr);
1384 harbaum 103 cairo_move_to (cr, 2*OSD_SCALE_FD,
1385     OSD_SCALE_H2+OSD_SCALE_FD + extents.height);
1386     cairo_text_path (cr, dist_str_imp);
1387     cairo_stroke (cr);
1388 harbaum 100
1389     cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
1390 harbaum 103 cairo_move_to (cr, 2*OSD_SCALE_FD, OSD_SCALE_H2-OSD_SCALE_FD);
1391 harbaum 99 cairo_show_text (cr, dist_str);
1392 harbaum 103 cairo_move_to (cr, 2*OSD_SCALE_FD,
1393     OSD_SCALE_H2+OSD_SCALE_FD + extents.height);
1394     cairo_show_text (cr, dist_str_imp);
1395 harbaum 99
1396 harbaum 103 g_free(dist_str);
1397     g_free(dist_str_imp);
1398    
1399 harbaum 99 /* draw white line */
1400     cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1401     cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
1402 harbaum 103 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/3);
1403     cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_M);
1404     cairo_rel_line_to (cr, 0, OSD_SCALE_TICK);
1405 harbaum 99 cairo_rel_line_to (cr, width_metric, 0);
1406 harbaum 103 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1407 harbaum 99 cairo_stroke(cr);
1408 harbaum 103 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_I);
1409     cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1410     cairo_rel_line_to (cr, width_imp, 0);
1411     cairo_rel_line_to (cr, 0, +OSD_SCALE_TICK);
1412     cairo_stroke(cr);
1413 harbaum 99
1414     /* draw black line */
1415     cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
1416 harbaum 103 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/6);
1417     cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_M);
1418     cairo_rel_line_to (cr, 0, OSD_SCALE_TICK);
1419 harbaum 99 cairo_rel_line_to (cr, width_metric, 0);
1420 harbaum 103 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1421 harbaum 99 cairo_stroke(cr);
1422 harbaum 103 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_I);
1423     cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1424     cairo_rel_line_to (cr, width_imp, 0);
1425     cairo_rel_line_to (cr, 0, +OSD_SCALE_TICK);
1426     cairo_stroke(cr);
1427 harbaum 99
1428 harbaum 98 cairo_destroy(cr);
1429     }
1430 harbaum 105 #endif
1431 harbaum 98
1432     static void
1433 harbaum 108 osd_render_controls(osm_gps_map_osd_t *osd)
1434 harbaum 98 {
1435     osd_priv_t *priv = (osd_priv_t*)osd->priv;
1436    
1437 harbaum 108 if(priv->controls.rendered
1438     #ifdef OSD_GPS_BUTTON
1439     && (priv->controls.gps_enabled == (osd->cb != NULL))
1440     #endif
1441     )
1442     return;
1443    
1444     #ifdef OSD_GPS_BUTTON
1445     priv->controls.gps_enabled = (osd->cb != NULL);
1446     #endif
1447 harbaum 110 priv->controls.rendered = TRUE;
1448 harbaum 108
1449 harbaum 86 #ifndef OSD_COLOR
1450     GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL];
1451     GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL];
1452     GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE];
1453 harbaum 74 #endif
1454 harbaum 70
1455     /* first fill with transparency */
1456 harbaum 108 cairo_t *cr = cairo_create(priv->controls.surface);
1457 harbaum 70 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1458     cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
1459     cairo_paint(cr);
1460     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1461    
1462     /* --------- draw zoom and dpad shape shadow ----------- */
1463 harbaum 74 #ifdef OSD_SHADOW_ENABLE
1464 harbaum 86 osd_zoom_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
1465     osd_shape_shadow(cr);
1466 harbaum 76 #ifndef OSD_NO_DPAD
1467 harbaum 86 osd_dpad_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
1468     osd_shape_shadow(cr);
1469 harbaum 74 #endif
1470 harbaum 76 #endif
1471 harbaum 70
1472     /* --------- draw zoom and dpad shape ----------- */
1473    
1474 harbaum 86 osd_zoom_shape(cr, 1, 1);
1475 harbaum 74 #ifndef OSD_COLOR
1476 harbaum 86 osd_shape(cr, &bg, &fg);
1477 harbaum 74 #else
1478 harbaum 86 osd_shape(cr);
1479 harbaum 74 #endif
1480 harbaum 76 #ifndef OSD_NO_DPAD
1481 harbaum 86 osd_dpad_shape(cr, 1, 1);
1482 harbaum 74 #ifndef OSD_COLOR
1483 harbaum 86 osd_shape(cr, &bg, &fg);
1484 harbaum 74 #else
1485 harbaum 86 osd_shape(cr);
1486 harbaum 74 #endif
1487 harbaum 76 #endif
1488 harbaum 70
1489     /* --------- draw zoom and dpad labels --------- */
1490    
1491 harbaum 74 #ifdef OSD_SHADOW_ENABLE
1492 harbaum 87 osd_labels_shadow(cr, Z_RAD/3, TRUE);
1493 harbaum 86 osd_zoom_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1494 harbaum 76 #ifndef OSD_NO_DPAD
1495 harbaum 86 osd_dpad_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1496 harbaum 76 #endif
1497 harbaum 87 cairo_stroke(cr);
1498 harbaum 76 #ifdef OSD_GPS_BUTTON
1499 harbaum 87 osd_labels_shadow(cr, Z_RAD/6, osd->cb != NULL);
1500 harbaum 86 osd_dpad_gps(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1501 harbaum 87 cairo_stroke(cr);
1502 harbaum 74 #endif
1503 harbaum 76 #endif
1504 harbaum 70
1505 harbaum 74 #ifndef OSD_COLOR
1506 harbaum 86 osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
1507 harbaum 74 #else
1508 harbaum 86 osd_labels(cr, Z_RAD/3, TRUE);
1509 harbaum 74 #endif
1510 harbaum 87 osd_zoom_labels(cr, 1, 1);
1511     #ifndef OSD_NO_DPAD
1512     osd_dpad_labels(cr, 1, 1);
1513     #endif
1514     cairo_stroke(cr);
1515    
1516 harbaum 74 #ifndef OSD_COLOR
1517 harbaum 86 osd_labels(cr, Z_RAD/6, osd->cb != NULL, &fg, &da);
1518 harbaum 74 #else
1519 harbaum 86 osd_labels(cr, Z_RAD/6, osd->cb != NULL);
1520 harbaum 74 #endif
1521 harbaum 87 #ifdef OSD_GPS_BUTTON
1522     osd_dpad_gps(cr, 1, 1);
1523 harbaum 76 #endif
1524 harbaum 87 cairo_stroke(cr);
1525 harbaum 70
1526     cairo_destroy(cr);
1527 harbaum 108 }
1528 harbaum 86
1529 harbaum 108 static void
1530     osd_render(osm_gps_map_osd_t *osd)
1531     {
1532 harbaum 111 /* this function is actually called pretty often since the */
1533     /* OSD contents may have changed (due to a coordinate/zoom change). */
1534     /* The different OSD parts have to make sure that they don't */
1535     /* render unneccessarily often and thus waste CPU power */
1536    
1537 harbaum 108 osd_render_controls(osd);
1538    
1539 harbaum 88 #ifdef OSD_SOURCE_SEL
1540 harbaum 111 osd_render_source_sel(osd, FALSE);
1541 harbaum 88 #endif
1542 harbaum 98
1543     #ifdef OSD_SCALE
1544     osd_render_scale(osd);
1545     #endif
1546 harbaum 105
1547     #ifdef OSD_CROSSHAIR
1548     osd_render_crosshair(osd);
1549     #endif
1550 harbaum 106
1551 harbaum 122 #ifdef OSD_NAV
1552     osd_render_nav(osd);
1553     #endif
1554    
1555 harbaum 106 #ifdef OSD_COORDINATES
1556     osd_render_coordinates(osd);
1557     #endif
1558 harbaum 70 }
1559    
1560     static void
1561 harbaum 86 osd_draw(osm_gps_map_osd_t *osd, GdkDrawable *drawable)
1562 harbaum 70 {
1563 harbaum 73 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1564 harbaum 70
1565     /* OSD itself uses some off-screen rendering, so check if the */
1566     /* offscreen buffer is present and create it if not */
1567 harbaum 108 if(!priv->controls.surface) {
1568 harbaum 70 /* create overlay ... */
1569 harbaum 108 priv->controls.surface =
1570 harbaum 86 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W+2, OSD_H+2);
1571    
1572 harbaum 108 priv->controls.rendered = FALSE;
1573     #ifdef OSD_GPS_BUTTON
1574     priv->controls.gps_enabled = FALSE;
1575     #endif
1576    
1577 harbaum 88 #ifdef OSD_SOURCE_SEL
1578 harbaum 86 /* the initial OSD state is alway not-expanded */
1579 harbaum 110 priv->source_sel.surface =
1580 harbaum 86 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1581     OSD_S_W+2, OSD_S_H+2);
1582 harbaum 111 priv->source_sel.rendered = FALSE;
1583 harbaum 88 #endif
1584 harbaum 86
1585 harbaum 98 #ifdef OSD_SCALE
1586 harbaum 110 priv->scale.surface =
1587 harbaum 98 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1588 harbaum 103 OSD_SCALE_W, OSD_SCALE_H);
1589 harbaum 110 priv->scale.zoom = -1;
1590 harbaum 98 #endif
1591    
1592 harbaum 105 #ifdef OSD_CROSSHAIR
1593 harbaum 110 priv->crosshair.surface =
1594 harbaum 105 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1595     OSD_CROSSHAIR_W, OSD_CROSSHAIR_H);
1596 harbaum 110 priv->crosshair.rendered = FALSE;
1597 harbaum 105 #endif
1598    
1599 harbaum 122 #ifdef OSD_NAV
1600     priv->nav.surface =
1601     cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1602     OSD_NAV_W, OSD_NAV_H);
1603     #endif
1604    
1605 harbaum 106 #ifdef OSD_COORDINATES
1606 harbaum 110 priv->coordinates.surface =
1607 harbaum 106 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1608     OSD_COORDINATES_W, OSD_COORDINATES_H);
1609 harbaum 108
1610 harbaum 110 priv->coordinates.lat = priv->coordinates.lon = OSM_GPS_MAP_INVALID;
1611 harbaum 106 #endif
1612    
1613 harbaum 70 /* ... and render it */
1614 harbaum 86 osd_render(osd);
1615 harbaum 70 }
1616    
1617     // now draw this onto the original context
1618 harbaum 75 cairo_t *cr = gdk_cairo_create(drawable);
1619 harbaum 77
1620 harbaum 113 gint x, y;
1621 harbaum 77
1622 harbaum 106 #ifdef OSD_SCALE
1623     x = OSD_X;
1624     y = -OSD_Y;
1625     if(x < 0) x += osd->widget->allocation.width - OSD_SCALE_W;
1626     if(y < 0) y += osd->widget->allocation.height - OSD_SCALE_H;
1627 harbaum 77
1628 harbaum 110 cairo_set_source_surface(cr, priv->scale.surface, x, y);
1629 harbaum 106 cairo_paint(cr);
1630     #endif
1631    
1632     #ifdef OSD_CROSSHAIR
1633     x = (osd->widget->allocation.width - OSD_CROSSHAIR_W)/2;
1634     y = (osd->widget->allocation.height - OSD_CROSSHAIR_H)/2;
1635    
1636 harbaum 110 cairo_set_source_surface(cr, priv->crosshair.surface, x, y);
1637 harbaum 106 cairo_paint(cr);
1638     #endif
1639    
1640 harbaum 122 #ifdef OSD_NAV
1641     x = OSD_X;
1642     if(x < 0) x += osd->widget->allocation.width - OSD_NAV_W;
1643     y = (osd->widget->allocation.height - OSD_NAV_H)/2;
1644    
1645     cairo_set_source_surface(cr, priv->nav.surface, x, y);
1646     cairo_paint(cr);
1647     #endif
1648    
1649 harbaum 106 #ifdef OSD_COORDINATES
1650     x = -OSD_X;
1651     y = -OSD_Y;
1652     if(x < 0) x += osd->widget->allocation.width - OSD_COORDINATES_W;
1653     if(y < 0) y += osd->widget->allocation.height - OSD_COORDINATES_H;
1654    
1655 harbaum 110 cairo_set_source_surface(cr, priv->coordinates.surface, x, y);
1656 harbaum 106 cairo_paint(cr);
1657     #endif
1658    
1659 harbaum 113 #ifdef OSD_BALLOON
1660     if(priv->balloon.surface) {
1661    
1662     /* convert given lat lon into screen coordinates */
1663     gint x, y;
1664     osm_gps_map_geographic_to_screen (OSM_GPS_MAP(osd->widget),
1665     priv->balloon.lat, priv->balloon.lon,
1666     &x, &y);
1667    
1668     /* check if balloon needs to be rerendered */
1669     osd_render_balloon(osd);
1670    
1671     cairo_set_source_surface(cr, priv->balloon.surface,
1672     x + priv->balloon.offset_x,
1673     y + priv->balloon.offset_y);
1674     cairo_paint(cr);
1675     }
1676     #endif
1677    
1678 harbaum 106 x = OSD_X;
1679     if(x < 0)
1680     x += osd->widget->allocation.width - OSD_W;
1681    
1682     y = OSD_Y;
1683     if(y < 0)
1684     y += osd->widget->allocation.height - OSD_H;
1685    
1686 harbaum 108 cairo_set_source_surface(cr, priv->controls.surface, x, y);
1687 harbaum 70 cairo_paint(cr);
1688 harbaum 86
1689     #ifdef OSD_SOURCE_SEL
1690 harbaum 110 if(!priv->source_sel.handler_id) {
1691 harbaum 86 /* the OSD source selection is not being animated */
1692 harbaum 110 if(!priv->source_sel.expanded)
1693 harbaum 86 x = osd->widget->allocation.width - OSD_S_W;
1694     else
1695     x = osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
1696     } else
1697 harbaum 110 x = priv->source_sel.shift;
1698 harbaum 86
1699     y = OSD_S_Y;
1700     if(OSD_S_Y < 0) {
1701 harbaum 110 if(!priv->source_sel.expanded)
1702 harbaum 86 y = osd->widget->allocation.height - OSD_S_H + OSD_S_Y;
1703     else
1704     y = osd->widget->allocation.height - OSD_S_EXP_H + OSD_S_Y;
1705     }
1706    
1707 harbaum 110 cairo_set_source_surface(cr, priv->source_sel.surface, x, y);
1708 harbaum 86 cairo_paint(cr);
1709     #endif
1710    
1711 harbaum 70 cairo_destroy(cr);
1712     }
1713    
1714     static void
1715 harbaum 86 osd_free(osm_gps_map_osd_t *osd)
1716 harbaum 70 {
1717 harbaum 73 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
1718 harbaum 70
1719 harbaum 108 if (priv->controls.surface)
1720     cairo_surface_destroy(priv->controls.surface);
1721 harbaum 88
1722     #ifdef OSD_SOURCE_SEL
1723 harbaum 110 if(priv->source_sel.handler_id)
1724     gtk_timeout_remove(priv->source_sel.handler_id);
1725 harbaum 86
1726 harbaum 110 if (priv->source_sel.surface)
1727     cairo_surface_destroy(priv->source_sel.surface);
1728 harbaum 88 #endif
1729 harbaum 86
1730 harbaum 98 #ifdef OSD_SCALE
1731 harbaum 110 if (priv->scale.surface)
1732     cairo_surface_destroy(priv->scale.surface);
1733 harbaum 98 #endif
1734    
1735 harbaum 105 #ifdef OSD_CROSSHAIR
1736 harbaum 110 if (priv->crosshair.surface)
1737     cairo_surface_destroy(priv->crosshair.surface);
1738 harbaum 105 #endif
1739    
1740 harbaum 122 #ifdef OSD_NAV
1741     if (priv->nav.surface)
1742     cairo_surface_destroy(priv->nav.surface);
1743     #endif
1744    
1745 harbaum 106 #ifdef OSD_COORDINATES
1746 harbaum 110 if (priv->coordinates.surface)
1747     cairo_surface_destroy(priv->coordinates.surface);
1748 harbaum 106 #endif
1749    
1750 harbaum 112 #ifdef OSD_BALLOON
1751     if (priv->balloon.surface)
1752     cairo_surface_destroy(priv->balloon.surface);
1753     #endif
1754    
1755 harbaum 73 g_free(priv);
1756     }
1757    
1758 harbaum 87 static gboolean
1759     osd_busy(osm_gps_map_osd_t *osd)
1760     {
1761 harbaum 88 #ifdef OSD_SOURCE_SEL
1762 harbaum 87 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
1763 harbaum 110 return (priv->source_sel.handler_id != 0);
1764 harbaum 88 #else
1765     return FALSE;
1766     #endif
1767 harbaum 87 }
1768    
1769 harbaum 73 static osm_gps_map_osd_t osd_classic = {
1770 harbaum 88 .widget = NULL,
1771    
1772 harbaum 86 .draw = osd_draw,
1773     .check = osd_check,
1774     .render = osd_render,
1775     .free = osd_free,
1776 harbaum 87 .busy = osd_busy,
1777 harbaum 73
1778     .cb = NULL,
1779     .data = NULL,
1780    
1781     .priv = NULL
1782     };
1783    
1784     /* this is the only function that's externally visible */
1785     void
1786     osm_gps_map_osd_classic_init(OsmGpsMap *map)
1787     {
1788     osd_priv_t *priv = osd_classic.priv = g_new0(osd_priv_t, 1);
1789    
1790 harbaum 112 #ifdef OSD_BALLOON
1791     priv->balloon.lat = OSM_GPS_MAP_INVALID;
1792     priv->balloon.lon = OSM_GPS_MAP_INVALID;
1793     #endif
1794    
1795 harbaum 73 osd_classic.priv = priv;
1796    
1797     osm_gps_map_register_osd(map, &osd_classic);
1798     }
1799    
1800 harbaum 76 #ifdef OSD_GPS_BUTTON
1801 harbaum 74 /* below are osd specific functions which aren't used by osm-gps-map */
1802     /* but instead are to be used by the main application */
1803 harbaum 76 void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdCallback cb,
1804     gpointer data) {
1805 harbaum 73 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1806     g_return_if_fail (osd);
1807    
1808     osd->cb = cb;
1809     osd->data = data;
1810    
1811 harbaum 70 /* this may have changed the state of the gps button */
1812     /* we thus re-render the overlay */
1813 harbaum 73 osd->render(osd);
1814 harbaum 70
1815 harbaum 73 osm_gps_map_redraw(map);
1816 harbaum 70 }
1817 harbaum 76 #endif
1818 harbaum 86
1819     osd_button_t
1820     osm_gps_map_osd_check(OsmGpsMap *map, gint x, gint y) {
1821     osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1822     g_return_val_if_fail (osd, OSD_NONE);
1823    
1824 harbaum 114 return osd_check(osd, TRUE, x, y);
1825 harbaum 86 }