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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 123 - (hide annotations)
Wed Sep 23 11:23:45 2009 UTC (14 years, 7 months ago) by harbaum
File MIME type: text/plain
File size: 56973 byte(s)
OSD handling
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 112 #include "converter.h"
36 harbaum 73 #include "osm-gps-map-osd-classic.h"
37 harbaum 71
38 harbaum 70 //the osd controls
39     typedef struct {
40 harbaum 74 /* the offscreen representation of the OSD */
41 harbaum 108 struct {
42     cairo_surface_t *surface;
43     gboolean rendered;
44     #ifdef OSD_GPS_BUTTON
45     gboolean gps_enabled;
46     #endif
47     } controls;
48 harbaum 86
49 harbaum 112 #ifdef OSD_BALLOON
50     //a balloon with additional info
51     struct {
52     cairo_surface_t *surface;
53 harbaum 113 int orientation, offset_x, offset_y;
54 harbaum 112
55 harbaum 114 gboolean just_created;
56 harbaum 112 float lat, lon;
57     OsmGpsMapRect_t rect;
58    
59     /* function called to have the user app draw the contents */
60     OsmGpsMapBalloonCallback cb;
61     gpointer data;
62     } balloon;
63     #endif
64    
65 harbaum 98 #ifdef OSD_SCALE
66 harbaum 110 struct {
67     cairo_surface_t *surface;
68     int zoom;
69     } scale;
70 harbaum 98 #endif
71 harbaum 110
72 harbaum 105 #ifdef OSD_CROSSHAIR
73 harbaum 110 struct {
74     cairo_surface_t *surface;
75     gboolean rendered;
76     } crosshair;
77 harbaum 105 #endif
78    
79 harbaum 122 #ifdef OSD_NAV
80     struct {
81     cairo_surface_t *surface;
82 harbaum 123 float lat, lon;
83     char *name;
84 harbaum 122 } 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 harbaum 123 osd_render_text(cairo_t *cr, int x, int y, char *text) {
1134     cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1135     cairo_set_line_width (cr, OSD_COORDINATES_FONT_SIZE/6);
1136     cairo_move_to (cr, x, y);
1137     cairo_text_path (cr, text);
1138     cairo_stroke (cr);
1139    
1140     cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
1141     cairo_move_to (cr, x, y);
1142     cairo_show_text (cr, text);
1143     }
1144    
1145     static void
1146 harbaum 106 osd_render_coordinates(osm_gps_map_osd_t *osd)
1147     {
1148     osd_priv_t *priv = (osd_priv_t*)osd->priv;
1149    
1150 harbaum 107 /* get current map position */
1151     gfloat lat, lon;
1152     g_object_get(osd->widget, "latitude", &lat, "longitude", &lon, NULL);
1153    
1154 harbaum 108 /* check if position has changed enough to require redraw */
1155 harbaum 110 if(!isnan(priv->coordinates.lat) && !isnan(priv->coordinates.lon))
1156     /* 1/60000 == 1/1000 minute */
1157     if((fabsf(lat - priv->coordinates.lat) < 1/60000) &&
1158     (fabsf(lon - priv->coordinates.lon) < 1/60000))
1159 harbaum 108 return;
1160    
1161 harbaum 110 priv->coordinates.lat = lat;
1162     priv->coordinates.lon = lon;
1163 harbaum 108
1164 harbaum 111 /* first fill with transparency */
1165 harbaum 110 cairo_t *cr = cairo_create(priv->coordinates.surface);
1166 harbaum 106 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1167 harbaum 111 // cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.5);
1168     cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
1169 harbaum 106 cairo_paint(cr);
1170     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1171    
1172 harbaum 107 cairo_select_font_face (cr, "Sans",
1173     CAIRO_FONT_SLANT_NORMAL,
1174     CAIRO_FONT_WEIGHT_BOLD);
1175     cairo_set_font_size (cr, OSD_COORDINATES_FONT_SIZE);
1176    
1177     char *latitude = osd_latitude_str(lat);
1178     char *longitude = osd_longitude_str(lon);
1179    
1180     cairo_text_extents_t lat_extents, lon_extents;
1181     cairo_text_extents (cr, latitude, &lat_extents);
1182     cairo_text_extents (cr, longitude, &lon_extents);
1183 harbaum 123
1184     osd_render_text(cr,
1185     (OSD_COORDINATES_W - lat_extents.width)/2,
1186     OSD_COORDINATES_OFFSET - lat_extents.y_bearing,
1187     latitude);
1188    
1189     osd_render_text(cr,
1190     (OSD_COORDINATES_W - lon_extents.width)/2,
1191     OSD_COORDINATES_OFFSET - lon_extents.y_bearing +
1192     OSD_COORDINATES_FONT_SIZE,
1193     longitude);
1194    
1195 harbaum 107 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 harbaum 123 #define OSD_NAV_W (8*OSD_COORDINATES_FONT_SIZE+2*OSD_COORDINATES_OFFSET)
1204 harbaum 122 #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 harbaum 123 if(!priv->nav.surface || isnan(priv->nav.lat) || isnan(priv->nav.lon))
1212     return;
1213    
1214 harbaum 122 /* first fill with transparency */
1215     cairo_t *cr = cairo_create(priv->nav.surface);
1216     cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1217     cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.2);
1218     cairo_paint(cr);
1219     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1220    
1221 harbaum 123 cairo_select_font_face (cr, "Sans",
1222     CAIRO_FONT_SLANT_NORMAL,
1223     CAIRO_FONT_WEIGHT_BOLD);
1224     cairo_set_font_size (cr, OSD_COORDINATES_FONT_SIZE);
1225    
1226     char *latitude = osd_latitude_str(priv->nav.lat);
1227     char *longitude = osd_longitude_str(priv->nav.lon);
1228    
1229     cairo_text_extents_t lat_extents, lon_extents, name_extents;
1230     cairo_text_extents (cr, priv->nav.name, &name_extents);
1231     cairo_text_extents (cr, latitude, &lat_extents);
1232     cairo_text_extents (cr, longitude, &lon_extents);
1233    
1234     osd_render_text(cr,
1235     (OSD_NAV_W - name_extents.width)/2,
1236     OSD_COORDINATES_OFFSET - name_extents.y_bearing,
1237     priv->nav.name);
1238    
1239     osd_render_text(cr,
1240     (OSD_NAV_W - lat_extents.width)/2,
1241     OSD_COORDINATES_OFFSET - lat_extents.y_bearing +
1242     OSD_COORDINATES_FONT_SIZE,
1243     latitude);
1244    
1245     osd_render_text(cr,
1246     (OSD_NAV_W - lon_extents.width)/2,
1247     OSD_COORDINATES_OFFSET - lon_extents.y_bearing +
1248     2 * OSD_COORDINATES_FONT_SIZE,
1249     longitude);
1250    
1251     g_free(latitude);
1252     g_free(longitude);
1253    
1254 harbaum 122 cairo_destroy(cr);
1255     }
1256    
1257 harbaum 123 void osm_gps_map_osd_clear_nav (OsmGpsMap *map) {
1258     g_return_if_fail (OSM_IS_GPS_MAP (map));
1259    
1260     osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1261     g_return_if_fail (osd);
1262    
1263     osd_priv_t *priv = (osd_priv_t*)osd->priv;
1264     g_return_if_fail (priv);
1265    
1266     if(priv->nav.surface) {
1267     cairo_surface_destroy(priv->nav.surface);
1268     priv->nav.surface = NULL;
1269     priv->nav.lat = OSM_GPS_MAP_INVALID;
1270     priv->nav.lon = OSM_GPS_MAP_INVALID;
1271     if(priv->nav.name) g_free(priv->nav.name);
1272     }
1273     osm_gps_map_redraw(map);
1274     }
1275    
1276     void
1277     osm_gps_map_osd_draw_nav (OsmGpsMap *map, float latitude, float longitude,
1278     char *name) {
1279     g_return_if_fail (OSM_IS_GPS_MAP (map));
1280    
1281     osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1282     g_return_if_fail (osd);
1283    
1284     osd_priv_t *priv = (osd_priv_t*)osd->priv;
1285     g_return_if_fail (priv);
1286    
1287     osm_gps_map_osd_clear_nav (map);
1288    
1289     /* allocate balloon surface */
1290     priv->nav.surface =
1291     cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1292     OSD_NAV_W+2, OSD_NAV_H+2);
1293    
1294     priv->nav.lat = latitude;
1295     priv->nav.lon = longitude;
1296     priv->nav.name = g_strdup(name);
1297    
1298     osd_render_nav(osd);
1299    
1300     osm_gps_map_redraw(map);
1301     }
1302    
1303 harbaum 122 #endif // OSD_NAV
1304    
1305    
1306 harbaum 105 #ifdef OSD_CROSSHAIR
1307    
1308     #ifndef OSD_CROSSHAIR_RADIUS
1309 harbaum 106 #define OSD_CROSSHAIR_RADIUS 10
1310 harbaum 105 #endif
1311    
1312 harbaum 106 #define OSD_CROSSHAIR_TICK (OSD_CROSSHAIR_RADIUS/2)
1313     #define OSD_CROSSHAIR_BORDER (OSD_CROSSHAIR_TICK + OSD_CROSSHAIR_RADIUS/4)
1314     #define OSD_CROSSHAIR_W ((OSD_CROSSHAIR_RADIUS+OSD_CROSSHAIR_BORDER)*2)
1315     #define OSD_CROSSHAIR_H ((OSD_CROSSHAIR_RADIUS+OSD_CROSSHAIR_BORDER)*2)
1316 harbaum 105
1317     static void
1318 harbaum 106 osd_render_crosshair_shape(cairo_t *cr) {
1319     cairo_arc (cr, OSD_CROSSHAIR_W/2, OSD_CROSSHAIR_H/2,
1320     OSD_CROSSHAIR_RADIUS, 0, 2*M_PI);
1321    
1322     cairo_move_to (cr, OSD_CROSSHAIR_W/2 - OSD_CROSSHAIR_RADIUS,
1323     OSD_CROSSHAIR_H/2);
1324     cairo_rel_line_to (cr, -OSD_CROSSHAIR_TICK, 0);
1325     cairo_move_to (cr, OSD_CROSSHAIR_W/2 + OSD_CROSSHAIR_RADIUS,
1326     OSD_CROSSHAIR_H/2);
1327     cairo_rel_line_to (cr, OSD_CROSSHAIR_TICK, 0);
1328    
1329     cairo_move_to (cr, OSD_CROSSHAIR_W/2,
1330     OSD_CROSSHAIR_H/2 - OSD_CROSSHAIR_RADIUS);
1331     cairo_rel_line_to (cr, 0, -OSD_CROSSHAIR_TICK);
1332     cairo_move_to (cr, OSD_CROSSHAIR_W/2,
1333     OSD_CROSSHAIR_H/2 + OSD_CROSSHAIR_RADIUS);
1334     cairo_rel_line_to (cr, 0, OSD_CROSSHAIR_TICK);
1335    
1336     cairo_stroke (cr);
1337     }
1338    
1339     static void
1340 harbaum 105 osd_render_crosshair(osm_gps_map_osd_t *osd)
1341     {
1342     osd_priv_t *priv = (osd_priv_t*)osd->priv;
1343    
1344 harbaum 110 if(priv->crosshair.rendered)
1345     return;
1346    
1347     priv->crosshair.rendered = TRUE;
1348    
1349 harbaum 105 /* first fill with transparency */
1350 harbaum 110 cairo_t *cr = cairo_create(priv->crosshair.surface);
1351 harbaum 105 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1352 harbaum 106 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
1353 harbaum 105 cairo_paint(cr);
1354     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1355    
1356 harbaum 106 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1357    
1358     cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.5);
1359     cairo_set_line_width (cr, OSD_CROSSHAIR_RADIUS/2);
1360     osd_render_crosshair_shape(cr);
1361    
1362     cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
1363     cairo_set_line_width (cr, OSD_CROSSHAIR_RADIUS/4);
1364     osd_render_crosshair_shape(cr);
1365    
1366 harbaum 105 cairo_destroy(cr);
1367     }
1368     #endif
1369    
1370     #ifdef OSD_SCALE
1371    
1372     #ifndef OSD_SCALE_FONT_SIZE
1373     #define OSD_SCALE_FONT_SIZE 12
1374     #endif
1375     #define OSD_SCALE_W (10*OSD_SCALE_FONT_SIZE)
1376     #define OSD_SCALE_H (5*OSD_SCALE_FONT_SIZE/2)
1377    
1378 harbaum 103 /* various parameters used to create the scale */
1379     #define OSD_SCALE_H2 (OSD_SCALE_H/2)
1380     #define OSD_SCALE_TICK (2*OSD_SCALE_FONT_SIZE/3)
1381     #define OSD_SCALE_M (OSD_SCALE_H2 - OSD_SCALE_TICK)
1382     #define OSD_SCALE_I (OSD_SCALE_H2 + OSD_SCALE_TICK)
1383     #define OSD_SCALE_FD (OSD_SCALE_FONT_SIZE/4)
1384 harbaum 99
1385 harbaum 70 static void
1386 harbaum 98 osd_render_scale(osm_gps_map_osd_t *osd)
1387     {
1388 harbaum 86 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1389 harbaum 103
1390     /* this only needs to be rendered if the zoom has changed */
1391     gint zoom;
1392     g_object_get(OSM_GPS_MAP(osd->widget), "zoom", &zoom, NULL);
1393 harbaum 110 if(zoom == priv->scale.zoom)
1394 harbaum 103 return;
1395    
1396 harbaum 110 priv->scale.zoom = zoom;
1397 harbaum 103
1398 harbaum 99 float m_per_pix = osm_gps_map_get_scale(OSM_GPS_MAP(osd->widget));
1399 harbaum 70
1400 harbaum 98 /* first fill with transparency */
1401 harbaum 110 cairo_t *cr = cairo_create(priv->scale.surface);
1402 harbaum 98 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1403 harbaum 100 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
1404 harbaum 103 // pink for testing: cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.2);
1405 harbaum 98 cairo_paint(cr);
1406     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1407    
1408     /* determine the size of the scale width in meters */
1409 harbaum 103 float width = (OSD_SCALE_W-OSD_SCALE_FONT_SIZE/6) * m_per_pix;
1410 harbaum 102
1411 harbaum 98 /* scale this to useful values */
1412     int exp = logf(width)*M_LOG10E;
1413     int mant = width/pow(10,exp);
1414 harbaum 99 int width_metric = mant * pow(10,exp);
1415 harbaum 103 char *dist_str = NULL;
1416     if(width_metric<1000)
1417     dist_str = g_strdup_printf("%u m", width_metric);
1418     else
1419     dist_str = g_strdup_printf("%u km", width_metric/1000);
1420 harbaum 99 width_metric /= m_per_pix;
1421    
1422 harbaum 102 /* and now the hard part: scale for useful imperial values :-( */
1423     /* try to convert to feet, 1ft == 0.3048 m */
1424     width /= 0.3048;
1425     float imp_scale = 0.3048;
1426     char *dist_imp_unit = "ft";
1427 harbaum 99
1428 harbaum 102 if(width >= 100) {
1429     /* 1yd == 3 feet */
1430     width /= 3.0;
1431     imp_scale *= 3.0;
1432     dist_imp_unit = "yd";
1433    
1434     if(width >= 1760.0) {
1435     /* 1mi == 1760 yd */
1436     width /= 1760.0;
1437     imp_scale *= 1760.0;
1438     dist_imp_unit = "mi";
1439     }
1440     }
1441    
1442 harbaum 103 /* also convert this to full tens/hundreds */
1443     exp = logf(width)*M_LOG10E;
1444     mant = width/pow(10,exp);
1445     int width_imp = mant * pow(10,exp);
1446     char *dist_str_imp = g_strdup_printf("%u %s", width_imp, dist_imp_unit);
1447 harbaum 102
1448 harbaum 103 /* convert back to pixels */
1449     width_imp *= imp_scale;
1450     width_imp /= m_per_pix;
1451    
1452 harbaum 99 cairo_select_font_face (cr, "Sans",
1453     CAIRO_FONT_SLANT_NORMAL,
1454     CAIRO_FONT_WEIGHT_BOLD);
1455 harbaum 102 cairo_set_font_size (cr, OSD_SCALE_FONT_SIZE);
1456 harbaum 99 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
1457    
1458     cairo_text_extents_t extents;
1459     cairo_text_extents (cr, dist_str, &extents);
1460    
1461 harbaum 100 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1462 harbaum 103 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/6);
1463     cairo_move_to (cr, 2*OSD_SCALE_FD, OSD_SCALE_H2-OSD_SCALE_FD);
1464 harbaum 100 cairo_text_path (cr, dist_str);
1465     cairo_stroke (cr);
1466 harbaum 103 cairo_move_to (cr, 2*OSD_SCALE_FD,
1467     OSD_SCALE_H2+OSD_SCALE_FD + extents.height);
1468     cairo_text_path (cr, dist_str_imp);
1469     cairo_stroke (cr);
1470 harbaum 100
1471     cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
1472 harbaum 103 cairo_move_to (cr, 2*OSD_SCALE_FD, OSD_SCALE_H2-OSD_SCALE_FD);
1473 harbaum 99 cairo_show_text (cr, dist_str);
1474 harbaum 103 cairo_move_to (cr, 2*OSD_SCALE_FD,
1475     OSD_SCALE_H2+OSD_SCALE_FD + extents.height);
1476     cairo_show_text (cr, dist_str_imp);
1477 harbaum 99
1478 harbaum 103 g_free(dist_str);
1479     g_free(dist_str_imp);
1480    
1481 harbaum 99 /* draw white line */
1482     cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1483     cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
1484 harbaum 103 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/3);
1485     cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_M);
1486     cairo_rel_line_to (cr, 0, OSD_SCALE_TICK);
1487 harbaum 99 cairo_rel_line_to (cr, width_metric, 0);
1488 harbaum 103 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1489 harbaum 99 cairo_stroke(cr);
1490 harbaum 103 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_I);
1491     cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1492     cairo_rel_line_to (cr, width_imp, 0);
1493     cairo_rel_line_to (cr, 0, +OSD_SCALE_TICK);
1494     cairo_stroke(cr);
1495 harbaum 99
1496     /* draw black line */
1497     cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
1498 harbaum 103 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/6);
1499     cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_M);
1500     cairo_rel_line_to (cr, 0, OSD_SCALE_TICK);
1501 harbaum 99 cairo_rel_line_to (cr, width_metric, 0);
1502 harbaum 103 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1503 harbaum 99 cairo_stroke(cr);
1504 harbaum 103 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_I);
1505     cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1506     cairo_rel_line_to (cr, width_imp, 0);
1507     cairo_rel_line_to (cr, 0, +OSD_SCALE_TICK);
1508     cairo_stroke(cr);
1509 harbaum 99
1510 harbaum 98 cairo_destroy(cr);
1511     }
1512 harbaum 105 #endif
1513 harbaum 98
1514     static void
1515 harbaum 108 osd_render_controls(osm_gps_map_osd_t *osd)
1516 harbaum 98 {
1517     osd_priv_t *priv = (osd_priv_t*)osd->priv;
1518    
1519 harbaum 108 if(priv->controls.rendered
1520     #ifdef OSD_GPS_BUTTON
1521     && (priv->controls.gps_enabled == (osd->cb != NULL))
1522     #endif
1523     )
1524     return;
1525    
1526     #ifdef OSD_GPS_BUTTON
1527     priv->controls.gps_enabled = (osd->cb != NULL);
1528     #endif
1529 harbaum 110 priv->controls.rendered = TRUE;
1530 harbaum 108
1531 harbaum 86 #ifndef OSD_COLOR
1532     GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL];
1533     GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL];
1534     GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE];
1535 harbaum 74 #endif
1536 harbaum 70
1537     /* first fill with transparency */
1538 harbaum 108 cairo_t *cr = cairo_create(priv->controls.surface);
1539 harbaum 70 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1540     cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
1541     cairo_paint(cr);
1542     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1543    
1544     /* --------- draw zoom and dpad shape shadow ----------- */
1545 harbaum 74 #ifdef OSD_SHADOW_ENABLE
1546 harbaum 86 osd_zoom_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
1547     osd_shape_shadow(cr);
1548 harbaum 76 #ifndef OSD_NO_DPAD
1549 harbaum 86 osd_dpad_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
1550     osd_shape_shadow(cr);
1551 harbaum 74 #endif
1552 harbaum 76 #endif
1553 harbaum 70
1554     /* --------- draw zoom and dpad shape ----------- */
1555    
1556 harbaum 86 osd_zoom_shape(cr, 1, 1);
1557 harbaum 74 #ifndef OSD_COLOR
1558 harbaum 86 osd_shape(cr, &bg, &fg);
1559 harbaum 74 #else
1560 harbaum 86 osd_shape(cr);
1561 harbaum 74 #endif
1562 harbaum 76 #ifndef OSD_NO_DPAD
1563 harbaum 86 osd_dpad_shape(cr, 1, 1);
1564 harbaum 74 #ifndef OSD_COLOR
1565 harbaum 86 osd_shape(cr, &bg, &fg);
1566 harbaum 74 #else
1567 harbaum 86 osd_shape(cr);
1568 harbaum 74 #endif
1569 harbaum 76 #endif
1570 harbaum 70
1571     /* --------- draw zoom and dpad labels --------- */
1572    
1573 harbaum 74 #ifdef OSD_SHADOW_ENABLE
1574 harbaum 87 osd_labels_shadow(cr, Z_RAD/3, TRUE);
1575 harbaum 86 osd_zoom_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1576 harbaum 76 #ifndef OSD_NO_DPAD
1577 harbaum 86 osd_dpad_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1578 harbaum 76 #endif
1579 harbaum 87 cairo_stroke(cr);
1580 harbaum 76 #ifdef OSD_GPS_BUTTON
1581 harbaum 87 osd_labels_shadow(cr, Z_RAD/6, osd->cb != NULL);
1582 harbaum 86 osd_dpad_gps(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1583 harbaum 87 cairo_stroke(cr);
1584 harbaum 74 #endif
1585 harbaum 76 #endif
1586 harbaum 70
1587 harbaum 74 #ifndef OSD_COLOR
1588 harbaum 86 osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
1589 harbaum 74 #else
1590 harbaum 86 osd_labels(cr, Z_RAD/3, TRUE);
1591 harbaum 74 #endif
1592 harbaum 87 osd_zoom_labels(cr, 1, 1);
1593     #ifndef OSD_NO_DPAD
1594     osd_dpad_labels(cr, 1, 1);
1595     #endif
1596     cairo_stroke(cr);
1597    
1598 harbaum 74 #ifndef OSD_COLOR
1599 harbaum 86 osd_labels(cr, Z_RAD/6, osd->cb != NULL, &fg, &da);
1600 harbaum 74 #else
1601 harbaum 86 osd_labels(cr, Z_RAD/6, osd->cb != NULL);
1602 harbaum 74 #endif
1603 harbaum 87 #ifdef OSD_GPS_BUTTON
1604     osd_dpad_gps(cr, 1, 1);
1605 harbaum 76 #endif
1606 harbaum 87 cairo_stroke(cr);
1607 harbaum 70
1608     cairo_destroy(cr);
1609 harbaum 108 }
1610 harbaum 86
1611 harbaum 108 static void
1612     osd_render(osm_gps_map_osd_t *osd)
1613     {
1614 harbaum 111 /* this function is actually called pretty often since the */
1615     /* OSD contents may have changed (due to a coordinate/zoom change). */
1616     /* The different OSD parts have to make sure that they don't */
1617     /* render unneccessarily often and thus waste CPU power */
1618    
1619 harbaum 108 osd_render_controls(osd);
1620    
1621 harbaum 88 #ifdef OSD_SOURCE_SEL
1622 harbaum 111 osd_render_source_sel(osd, FALSE);
1623 harbaum 88 #endif
1624 harbaum 98
1625     #ifdef OSD_SCALE
1626     osd_render_scale(osd);
1627     #endif
1628 harbaum 105
1629     #ifdef OSD_CROSSHAIR
1630     osd_render_crosshair(osd);
1631     #endif
1632 harbaum 106
1633 harbaum 122 #ifdef OSD_NAV
1634     osd_render_nav(osd);
1635     #endif
1636    
1637 harbaum 106 #ifdef OSD_COORDINATES
1638     osd_render_coordinates(osd);
1639     #endif
1640 harbaum 70 }
1641    
1642     static void
1643 harbaum 86 osd_draw(osm_gps_map_osd_t *osd, GdkDrawable *drawable)
1644 harbaum 70 {
1645 harbaum 73 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1646 harbaum 70
1647     /* OSD itself uses some off-screen rendering, so check if the */
1648     /* offscreen buffer is present and create it if not */
1649 harbaum 108 if(!priv->controls.surface) {
1650 harbaum 70 /* create overlay ... */
1651 harbaum 108 priv->controls.surface =
1652 harbaum 86 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W+2, OSD_H+2);
1653    
1654 harbaum 108 priv->controls.rendered = FALSE;
1655     #ifdef OSD_GPS_BUTTON
1656     priv->controls.gps_enabled = FALSE;
1657     #endif
1658    
1659 harbaum 88 #ifdef OSD_SOURCE_SEL
1660 harbaum 86 /* the initial OSD state is alway not-expanded */
1661 harbaum 110 priv->source_sel.surface =
1662 harbaum 86 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1663     OSD_S_W+2, OSD_S_H+2);
1664 harbaum 111 priv->source_sel.rendered = FALSE;
1665 harbaum 88 #endif
1666 harbaum 86
1667 harbaum 98 #ifdef OSD_SCALE
1668 harbaum 110 priv->scale.surface =
1669 harbaum 98 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1670 harbaum 103 OSD_SCALE_W, OSD_SCALE_H);
1671 harbaum 110 priv->scale.zoom = -1;
1672 harbaum 98 #endif
1673    
1674 harbaum 105 #ifdef OSD_CROSSHAIR
1675 harbaum 110 priv->crosshair.surface =
1676 harbaum 105 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1677     OSD_CROSSHAIR_W, OSD_CROSSHAIR_H);
1678 harbaum 110 priv->crosshair.rendered = FALSE;
1679 harbaum 105 #endif
1680    
1681 harbaum 106 #ifdef OSD_COORDINATES
1682 harbaum 110 priv->coordinates.surface =
1683 harbaum 106 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1684     OSD_COORDINATES_W, OSD_COORDINATES_H);
1685 harbaum 108
1686 harbaum 110 priv->coordinates.lat = priv->coordinates.lon = OSM_GPS_MAP_INVALID;
1687 harbaum 106 #endif
1688    
1689 harbaum 70 /* ... and render it */
1690 harbaum 86 osd_render(osd);
1691 harbaum 70 }
1692    
1693     // now draw this onto the original context
1694 harbaum 75 cairo_t *cr = gdk_cairo_create(drawable);
1695 harbaum 77
1696 harbaum 113 gint x, y;
1697 harbaum 77
1698 harbaum 106 #ifdef OSD_SCALE
1699     x = OSD_X;
1700     y = -OSD_Y;
1701     if(x < 0) x += osd->widget->allocation.width - OSD_SCALE_W;
1702     if(y < 0) y += osd->widget->allocation.height - OSD_SCALE_H;
1703 harbaum 77
1704 harbaum 110 cairo_set_source_surface(cr, priv->scale.surface, x, y);
1705 harbaum 106 cairo_paint(cr);
1706     #endif
1707    
1708     #ifdef OSD_CROSSHAIR
1709     x = (osd->widget->allocation.width - OSD_CROSSHAIR_W)/2;
1710     y = (osd->widget->allocation.height - OSD_CROSSHAIR_H)/2;
1711    
1712 harbaum 110 cairo_set_source_surface(cr, priv->crosshair.surface, x, y);
1713 harbaum 106 cairo_paint(cr);
1714     #endif
1715    
1716 harbaum 122 #ifdef OSD_NAV
1717 harbaum 123 if(priv->nav.surface) {
1718     x = OSD_X;
1719     if(x < 0) x += osd->widget->allocation.width - OSD_NAV_W;
1720     y = (osd->widget->allocation.height - OSD_NAV_H)/2;
1721    
1722     cairo_set_source_surface(cr, priv->nav.surface, x, y);
1723     cairo_paint(cr);
1724     }
1725 harbaum 122 #endif
1726    
1727 harbaum 106 #ifdef OSD_COORDINATES
1728     x = -OSD_X;
1729     y = -OSD_Y;
1730     if(x < 0) x += osd->widget->allocation.width - OSD_COORDINATES_W;
1731     if(y < 0) y += osd->widget->allocation.height - OSD_COORDINATES_H;
1732    
1733 harbaum 110 cairo_set_source_surface(cr, priv->coordinates.surface, x, y);
1734 harbaum 106 cairo_paint(cr);
1735     #endif
1736    
1737 harbaum 113 #ifdef OSD_BALLOON
1738     if(priv->balloon.surface) {
1739    
1740     /* convert given lat lon into screen coordinates */
1741     gint x, y;
1742     osm_gps_map_geographic_to_screen (OSM_GPS_MAP(osd->widget),
1743     priv->balloon.lat, priv->balloon.lon,
1744     &x, &y);
1745    
1746     /* check if balloon needs to be rerendered */
1747     osd_render_balloon(osd);
1748    
1749     cairo_set_source_surface(cr, priv->balloon.surface,
1750     x + priv->balloon.offset_x,
1751     y + priv->balloon.offset_y);
1752     cairo_paint(cr);
1753     }
1754     #endif
1755    
1756 harbaum 106 x = OSD_X;
1757     if(x < 0)
1758     x += osd->widget->allocation.width - OSD_W;
1759    
1760     y = OSD_Y;
1761     if(y < 0)
1762     y += osd->widget->allocation.height - OSD_H;
1763    
1764 harbaum 108 cairo_set_source_surface(cr, priv->controls.surface, x, y);
1765 harbaum 70 cairo_paint(cr);
1766 harbaum 86
1767     #ifdef OSD_SOURCE_SEL
1768 harbaum 110 if(!priv->source_sel.handler_id) {
1769 harbaum 86 /* the OSD source selection is not being animated */
1770 harbaum 110 if(!priv->source_sel.expanded)
1771 harbaum 86 x = osd->widget->allocation.width - OSD_S_W;
1772     else
1773     x = osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
1774     } else
1775 harbaum 110 x = priv->source_sel.shift;
1776 harbaum 86
1777     y = OSD_S_Y;
1778     if(OSD_S_Y < 0) {
1779 harbaum 110 if(!priv->source_sel.expanded)
1780 harbaum 86 y = osd->widget->allocation.height - OSD_S_H + OSD_S_Y;
1781     else
1782     y = osd->widget->allocation.height - OSD_S_EXP_H + OSD_S_Y;
1783     }
1784    
1785 harbaum 110 cairo_set_source_surface(cr, priv->source_sel.surface, x, y);
1786 harbaum 86 cairo_paint(cr);
1787     #endif
1788    
1789 harbaum 70 cairo_destroy(cr);
1790     }
1791    
1792     static void
1793 harbaum 86 osd_free(osm_gps_map_osd_t *osd)
1794 harbaum 70 {
1795 harbaum 73 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
1796 harbaum 70
1797 harbaum 108 if (priv->controls.surface)
1798     cairo_surface_destroy(priv->controls.surface);
1799 harbaum 88
1800     #ifdef OSD_SOURCE_SEL
1801 harbaum 110 if(priv->source_sel.handler_id)
1802     gtk_timeout_remove(priv->source_sel.handler_id);
1803 harbaum 86
1804 harbaum 110 if (priv->source_sel.surface)
1805     cairo_surface_destroy(priv->source_sel.surface);
1806 harbaum 88 #endif
1807 harbaum 86
1808 harbaum 98 #ifdef OSD_SCALE
1809 harbaum 110 if (priv->scale.surface)
1810     cairo_surface_destroy(priv->scale.surface);
1811 harbaum 98 #endif
1812    
1813 harbaum 105 #ifdef OSD_CROSSHAIR
1814 harbaum 110 if (priv->crosshair.surface)
1815     cairo_surface_destroy(priv->crosshair.surface);
1816 harbaum 105 #endif
1817    
1818 harbaum 122 #ifdef OSD_NAV
1819     if (priv->nav.surface)
1820     cairo_surface_destroy(priv->nav.surface);
1821     #endif
1822    
1823 harbaum 106 #ifdef OSD_COORDINATES
1824 harbaum 110 if (priv->coordinates.surface)
1825     cairo_surface_destroy(priv->coordinates.surface);
1826 harbaum 106 #endif
1827    
1828 harbaum 112 #ifdef OSD_BALLOON
1829     if (priv->balloon.surface)
1830     cairo_surface_destroy(priv->balloon.surface);
1831     #endif
1832    
1833 harbaum 73 g_free(priv);
1834     }
1835    
1836 harbaum 87 static gboolean
1837     osd_busy(osm_gps_map_osd_t *osd)
1838     {
1839 harbaum 88 #ifdef OSD_SOURCE_SEL
1840 harbaum 87 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
1841 harbaum 110 return (priv->source_sel.handler_id != 0);
1842 harbaum 88 #else
1843     return FALSE;
1844     #endif
1845 harbaum 87 }
1846    
1847 harbaum 73 static osm_gps_map_osd_t osd_classic = {
1848 harbaum 88 .widget = NULL,
1849    
1850 harbaum 86 .draw = osd_draw,
1851     .check = osd_check,
1852     .render = osd_render,
1853     .free = osd_free,
1854 harbaum 87 .busy = osd_busy,
1855 harbaum 73
1856     .cb = NULL,
1857     .data = NULL,
1858    
1859     .priv = NULL
1860     };
1861    
1862     /* this is the only function that's externally visible */
1863     void
1864     osm_gps_map_osd_classic_init(OsmGpsMap *map)
1865     {
1866     osd_priv_t *priv = osd_classic.priv = g_new0(osd_priv_t, 1);
1867    
1868 harbaum 112 #ifdef OSD_BALLOON
1869     priv->balloon.lat = OSM_GPS_MAP_INVALID;
1870     priv->balloon.lon = OSM_GPS_MAP_INVALID;
1871     #endif
1872    
1873 harbaum 73 osd_classic.priv = priv;
1874    
1875     osm_gps_map_register_osd(map, &osd_classic);
1876     }
1877    
1878 harbaum 76 #ifdef OSD_GPS_BUTTON
1879 harbaum 74 /* below are osd specific functions which aren't used by osm-gps-map */
1880     /* but instead are to be used by the main application */
1881 harbaum 76 void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdCallback cb,
1882     gpointer data) {
1883 harbaum 73 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1884     g_return_if_fail (osd);
1885    
1886     osd->cb = cb;
1887     osd->data = data;
1888    
1889 harbaum 70 /* this may have changed the state of the gps button */
1890     /* we thus re-render the overlay */
1891 harbaum 73 osd->render(osd);
1892 harbaum 70
1893 harbaum 73 osm_gps_map_redraw(map);
1894 harbaum 70 }
1895 harbaum 76 #endif
1896 harbaum 86
1897     osd_button_t
1898     osm_gps_map_osd_check(OsmGpsMap *map, gint x, gint y) {
1899     osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1900     g_return_val_if_fail (osd, OSD_NONE);
1901    
1902 harbaum 114 return osd_check(osd, TRUE, x, y);
1903 harbaum 86 }