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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 94 - (hide annotations)
Thu Sep 3 11:04:05 2009 UTC (14 years, 8 months ago) by harbaum
File MIME type: text/plain
File size: 27366 byte(s)
OSD position fix
1 harbaum 71 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
2     /* vim:set et sw=4 ts=4 cino=t0,(0: */
3 harbaum 70 /*
4     * Copyright (C) Till Harbaum 2009 <till@harbaum.org>
5     *
6     * osm-gps-map is free software: you can redistribute it and/or modify it
7     * under the terms of the GNU General Public License as published by the
8     * Free Software Foundation, either version 3 of the License, or
9     * (at your option) any later version.
10     *
11     * osm-gps-map is distributed in the hope that it will be useful, but
12     * WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14     * See the GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License along
17     * with this program. If not, see <http://www.gnu.org/licenses/>.
18     */
19    
20 harbaum 73 #include "config.h"
21     #include <stdlib.h> // abs
22 harbaum 87 #include <math.h> // M_PI/cos()
23 harbaum 70
24 harbaum 71 /* parameters that can be overwritten from the config file: */
25 harbaum 77 /* OSD_DIAMETER */
26     /* OSD_X, OSD_Y */
27 harbaum 71
28     #ifndef USE_CAIRO
29     #error "OSD control display lacks a non-cairo implementation!"
30 harbaum 70 #endif
31    
32 harbaum 71 #include <cairo.h>
33    
34     #include "osm-gps-map.h"
35 harbaum 73 #include "osm-gps-map-osd-classic.h"
36 harbaum 71
37 harbaum 70 //the osd controls
38     typedef struct {
39 harbaum 74 /* the offscreen representation of the OSD */
40     cairo_surface_t *overlay;
41 harbaum 86
42 harbaum 88 #ifdef OSD_SOURCE_SEL
43 harbaum 87 /* values to handle the "source" menu */
44 harbaum 86 cairo_surface_t *map_source;
45     gboolean expanded;
46     gint shift, dir, count;
47     gint handler_id;
48 harbaum 87 gint width, height;
49 harbaum 88 #endif
50 harbaum 87
51 harbaum 70 } osd_priv_t;
52    
53     /* position and extent of bounding box */
54 harbaum 77 #ifndef OSD_X
55 harbaum 70 #define OSD_X (10)
56 harbaum 77 #endif
57    
58     #ifndef OSD_Y
59 harbaum 70 #define OSD_Y (10)
60 harbaum 77 #endif
61 harbaum 70
62     /* parameters of the direction shape */
63 harbaum 77 #ifndef OSD_DIAMETER
64 harbaum 70 #define D_RAD (30) // diameter of dpad
65     #else
66 harbaum 77 #define D_RAD (OSD_DIAMETER)
67 harbaum 70 #endif
68     #define D_TIP (4*D_RAD/5) // distance of arrow tip from dpad center
69     #define D_LEN (D_RAD/4) // length of arrow
70     #define D_WID (D_LEN) // width of arrow
71    
72     /* parameters of the "zoom" pad */
73     #define Z_STEP (D_RAD/4) // distance between dpad and zoom
74     #define Z_RAD (D_RAD/2) // radius of "caps" of zoom bar
75    
76 harbaum 74 #ifdef OSD_SHADOW_ENABLE
77 harbaum 70 /* shadow also depends on control size */
78     #define OSD_SHADOW (D_RAD/6)
79 harbaum 74 #else
80     #define OSD_SHADOW (0)
81     #endif
82 harbaum 70
83 harbaum 77 /* normally the GPS button is in the center of the dpad. if there's */
84     /* no dpad it will go into the zoom area */
85     #if defined(OSD_GPS_BUTTON) && defined(OSD_NO_DPAD)
86     #define Z_GPS 1
87     #else
88     #define Z_GPS 0
89     #endif
90    
91 harbaum 70 /* total width and height of controls incl. shadow */
92 harbaum 77 #define OSD_W (2*D_RAD + OSD_SHADOW + Z_GPS * 2 * Z_RAD)
93     #if !Z_GPS
94 harbaum 70 #define OSD_H (2*D_RAD + Z_STEP + 2*Z_RAD + OSD_SHADOW)
95 harbaum 77 #else
96     #define OSD_H (2*Z_RAD + OSD_SHADOW)
97     #endif
98 harbaum 70
99 harbaum 74 #ifdef OSD_SHADOW_ENABLE
100 harbaum 70 #define OSD_LBL_SHADOW (OSD_SHADOW/2)
101 harbaum 74 #endif
102 harbaum 70
103 harbaum 77 #define Z_TOP ((1-Z_GPS) * (2 * D_RAD + Z_STEP))
104    
105 harbaum 70 #define Z_MID (Z_TOP + Z_RAD)
106     #define Z_BOT (Z_MID + Z_RAD)
107     #define Z_LEFT (Z_RAD)
108 harbaum 77 #define Z_RIGHT (2 * D_RAD - Z_RAD + Z_GPS * 2 * Z_RAD)
109     #define Z_CENTER ((Z_RIGHT + Z_LEFT)/2)
110 harbaum 70
111     /* create the cairo shape used for the zoom buttons */
112     static void
113 harbaum 86 osd_zoom_shape(cairo_t *cr, gint x, gint y)
114 harbaum 71 {
115 harbaum 70 cairo_move_to (cr, x+Z_LEFT, y+Z_TOP);
116     cairo_line_to (cr, x+Z_RIGHT, y+Z_TOP);
117     cairo_arc (cr, x+Z_RIGHT, y+Z_MID, Z_RAD, -M_PI/2, M_PI/2);
118     cairo_line_to (cr, x+Z_LEFT, y+Z_BOT);
119     cairo_arc (cr, x+Z_LEFT, y+Z_MID, Z_RAD, M_PI/2, -M_PI/2);
120     }
121    
122 harbaum 86 /* ------------------- color/shadow functions ----------------- */
123    
124     #ifndef OSD_COLOR
125     /* if no color has been specified we just use the gdks default colors */
126     static void
127     osd_labels(cairo_t *cr, gint width, gboolean enabled,
128     GdkColor *fg, GdkColor *disabled) {
129     if(enabled) gdk_cairo_set_source_color(cr, fg);
130     else gdk_cairo_set_source_color(cr, disabled);
131     cairo_set_line_width (cr, width);
132     }
133     #else
134     static void
135     osd_labels(cairo_t *cr, gint width, gboolean enabled) {
136     if(enabled) cairo_set_source_rgb (cr, OSD_COLOR);
137     else cairo_set_source_rgb (cr, OSD_COLOR_DISABLED);
138     cairo_set_line_width (cr, width);
139     }
140     #endif
141    
142     #ifdef OSD_SHADOW_ENABLE
143     static void
144     osd_labels_shadow(cairo_t *cr, gint width, gboolean enabled) {
145     cairo_set_source_rgba (cr, 0, 0, 0, enabled?0.3:0.15);
146     cairo_set_line_width (cr, width);
147     }
148     #endif
149    
150 harbaum 76 #ifndef OSD_NO_DPAD
151 harbaum 70 /* create the cairo shape used for the dpad */
152     static void
153 harbaum 86 osd_dpad_shape(cairo_t *cr, gint x, gint y)
154 harbaum 71 {
155 harbaum 70 cairo_arc (cr, x+D_RAD, y+D_RAD, D_RAD, 0, 2 * M_PI);
156     }
157 harbaum 76 #endif
158 harbaum 70
159 harbaum 86 #ifdef OSD_SHADOW_ENABLE
160     static void
161     osd_shape_shadow(cairo_t *cr) {
162     cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
163     cairo_fill (cr);
164     cairo_stroke (cr);
165     }
166     #endif
167    
168     #ifndef OSD_COLOR
169     /* if no color has been specified we just use the gdks default colors */
170     static void
171     osd_shape(cairo_t *cr, GdkColor *bg, GdkColor *fg) {
172     gdk_cairo_set_source_color(cr, bg);
173     cairo_fill_preserve (cr);
174     gdk_cairo_set_source_color(cr, fg);
175     cairo_set_line_width (cr, 1);
176     cairo_stroke (cr);
177     }
178     #else
179     static void
180     osd_shape(cairo_t *cr) {
181     cairo_set_source_rgb (cr, OSD_COLOR_BG);
182     cairo_fill_preserve (cr);
183     cairo_set_source_rgb (cr, OSD_COLOR);
184     cairo_set_line_width (cr, 1);
185     cairo_stroke (cr);
186     }
187     #endif
188    
189    
190 harbaum 70 static gboolean
191     osm_gps_map_in_circle(gint x, gint y, gint cx, gint cy, gint rad)
192     {
193     return( pow(cx - x, 2) + pow(cy - y, 2) < rad * rad);
194     }
195    
196 harbaum 76 #ifndef OSD_NO_DPAD
197 harbaum 70 /* check whether x/y is within the dpad */
198     static osd_button_t
199 harbaum 86 osd_check_dpad(gint x, gint y)
200 harbaum 70 {
201     /* within entire dpad circle */
202 harbaum 77 if( osm_gps_map_in_circle(x, y, D_RAD, D_RAD, D_RAD))
203 harbaum 70 {
204     /* convert into position relative to dpads centre */
205 harbaum 77 x -= D_RAD;
206     y -= D_RAD;
207 harbaum 70
208 harbaum 76 #ifdef OSD_GPS_BUTTON
209 harbaum 70 /* check for dpad center goes here! */
210     if( osm_gps_map_in_circle(x, y, 0, 0, D_RAD/3))
211     return OSD_GPS;
212 harbaum 76 #endif
213 harbaum 70
214     if( y < 0 && abs(x) < abs(y))
215     return OSD_UP;
216    
217     if( y > 0 && abs(x) < abs(y))
218     return OSD_DOWN;
219    
220     if( x < 0 && abs(y) < abs(x))
221     return OSD_LEFT;
222    
223     if( x > 0 && abs(y) < abs(x))
224     return OSD_RIGHT;
225    
226     return OSD_BG;
227     }
228     return OSD_NONE;
229     }
230 harbaum 76 #endif
231 harbaum 70
232     /* check whether x/y is within the zoom pads */
233     static osd_button_t
234 harbaum 86 osd_check_zoom(gint x, gint y) {
235 harbaum 77 if( x > 0 && x < OSD_W && y > Z_TOP && y < Z_BOT) {
236 harbaum 70
237     /* within circle around (-) label */
238 harbaum 77 if( osm_gps_map_in_circle(x, y, Z_LEFT, Z_MID, Z_RAD))
239 harbaum 70 return OSD_OUT;
240    
241 harbaum 77 /* within circle around (+) label */
242     if( osm_gps_map_in_circle(x, y, Z_RIGHT, Z_MID, Z_RAD))
243     return OSD_IN;
244    
245     #if Z_GPS == 1
246     /* within square around center */
247     if( x > Z_CENTER - Z_RAD && x < Z_CENTER + Z_RAD)
248     return OSD_GPS;
249     #endif
250    
251 harbaum 70 /* between center of (-) button and center of entire zoom control area */
252 harbaum 77 if(x > OSD_LEFT && x < D_RAD)
253 harbaum 70 return OSD_OUT;
254    
255     /* between center of (+) button and center of entire zoom control area */
256 harbaum 77 if(x < OSD_RIGHT && x > D_RAD)
257 harbaum 70 return OSD_IN;
258     }
259    
260     return OSD_NONE;
261     }
262    
263 harbaum 88 #ifdef OSD_SOURCE_SEL
264    
265 harbaum 86 /* place source selection at right border */
266     #define OSD_S_RAD (Z_RAD)
267     #define OSD_S_X (-OSD_X)
268     #define OSD_S_Y (OSD_Y)
269     #define OSD_S_PW (2 * Z_RAD)
270     #define OSD_S_W (OSD_S_PW)
271     #define OSD_S_PH (2 * Z_RAD)
272     #define OSD_S_H (OSD_S_PH + OSD_SHADOW)
273    
274 harbaum 87 /* size of usable area when expanded */
275     #define OSD_S_AREA_W (priv->width)
276     #define OSD_S_AREA_H (priv->height)
277 harbaum 86 #define OSD_S_EXP_W (OSD_S_PW + OSD_S_AREA_W + OSD_SHADOW)
278     #define OSD_S_EXP_H (OSD_S_AREA_H + OSD_SHADOW)
279    
280     /* internal value to draw the arrow on the "puller" */
281     #define OSD_S_D0 (OSD_S_RAD/2)
282 harbaum 87 #ifndef OSD_FONT_SIZE
283     #define OSD_FONT_SIZE 16.0
284     #endif
285     #define OSD_TEXT_BORDER (OSD_FONT_SIZE/2)
286     #define OSD_TEXT_SKIP (OSD_FONT_SIZE/8)
287 harbaum 86
288 harbaum 88 /* draw the shape of the source selection OSD, either only the puller (not expanded) */
289     /* or the entire menu incl. the puller (expanded) */
290 harbaum 86 static void
291     osd_source_shape(osd_priv_t *priv, cairo_t *cr, gint x, gint y) {
292     if(!priv->expanded) {
293     /* just draw the puller */
294     cairo_move_to (cr, x + OSD_S_PW, y + OSD_S_PH);
295     cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2);
296     cairo_line_to (cr, x + OSD_S_PW, y);
297     } else {
298     /* draw the puller and the area itself */
299     cairo_move_to (cr, x + OSD_S_PW + OSD_S_AREA_W, y + OSD_S_AREA_H);
300     cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_AREA_H);
301     if(OSD_S_Y > 0) {
302     cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_PH);
303     cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2);
304     } else {
305     cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_AREA_H-OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2);
306     cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_AREA_H - OSD_S_PH);
307     cairo_line_to (cr, x + OSD_S_PW, y);
308     }
309     cairo_line_to (cr, x + OSD_S_PW + OSD_S_AREA_W, y);
310     cairo_close_path (cr);
311     }
312     }
313    
314     static void
315 harbaum 87 osd_source_content(osm_gps_map_osd_t *osd, cairo_t *cr, gint offset) {
316     osd_priv_t *priv = (osd_priv_t*)osd->priv;
317 harbaum 86
318 harbaum 87 int py = offset + OSD_S_RAD - OSD_S_D0;
319    
320 harbaum 86 if(!priv->expanded) {
321     /* draw the "puller" open (<) arrow */
322 harbaum 87 cairo_move_to (cr, offset + OSD_S_RAD + OSD_S_D0/2, py);
323 harbaum 86 cairo_rel_line_to (cr, -OSD_S_D0, +OSD_S_D0);
324     cairo_rel_line_to (cr, +OSD_S_D0, +OSD_S_D0);
325     } else {
326     if(OSD_S_Y < 0)
327 harbaum 87 py += OSD_S_AREA_H - OSD_S_PH;
328 harbaum 86
329     /* draw the "puller" close (>) arrow */
330 harbaum 87 cairo_move_to (cr, offset + OSD_S_RAD - OSD_S_D0/2, py);
331 harbaum 86 cairo_rel_line_to (cr, +OSD_S_D0, +OSD_S_D0);
332     cairo_rel_line_to (cr, -OSD_S_D0, +OSD_S_D0);
333 harbaum 87 cairo_stroke(cr);
334    
335     /* don't draw a shadow for the text content */
336     if(offset == 1) {
337     gint source;
338     g_object_get(osd->widget, "map-source", &source, NULL);
339    
340     cairo_select_font_face (cr, "Sans",
341     CAIRO_FONT_SLANT_NORMAL,
342     CAIRO_FONT_WEIGHT_BOLD);
343     cairo_set_font_size (cr, OSD_FONT_SIZE);
344    
345 harbaum 89 int i, step = (priv->height - 2*OSD_TEXT_BORDER) /
346     OSM_GPS_MAP_SOURCE_LAST;
347 harbaum 87 for(i=OSM_GPS_MAP_SOURCE_NULL+1;i<=OSM_GPS_MAP_SOURCE_LAST;i++) {
348     cairo_text_extents_t extents;
349     const char *src = osm_gps_map_source_get_friendly_name(i);
350     cairo_text_extents (cr, src, &extents);
351    
352     int x = offset + OSD_S_PW + OSD_TEXT_BORDER;
353     int y = offset + step * (i-1) + OSD_TEXT_BORDER;
354    
355     /* draw filled rectangle if selected */
356     if(source == i) {
357     cairo_rectangle(cr, x - OSD_TEXT_BORDER/2,
358     y - OSD_TEXT_SKIP,
359     priv->width - OSD_TEXT_BORDER,
360     step + OSD_TEXT_SKIP);
361     cairo_fill(cr);
362    
363     /* temprarily draw with background color */
364     #ifndef OSD_COLOR
365     GdkColor bg = osd->widget->style->bg[GTK_STATE_NORMAL];
366     gdk_cairo_set_source_color(cr, &bg);
367     #else
368     cairo_set_source_rgb (cr, OSD_COLOR_BG);
369     #endif
370     }
371    
372     cairo_move_to (cr, x, y + OSD_TEXT_SKIP - extents.y_bearing);
373     cairo_show_text (cr, src);
374    
375     /* restore color */
376     if(source == i) {
377     #ifndef OSD_COLOR
378     GdkColor fg = osd->widget->style->fg[GTK_STATE_NORMAL];
379     gdk_cairo_set_source_color(cr, &fg);
380     #else
381     cairo_set_source_rgb (cr, OSD_COLOR);
382     #endif
383     }
384     }
385     }
386 harbaum 86 }
387     }
388    
389     static void
390     osd_render_source_sel(osm_gps_map_osd_t *osd) {
391     osd_priv_t *priv = (osd_priv_t*)osd->priv;
392    
393     #ifndef OSD_COLOR
394     GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL];
395     GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL];
396     GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE];
397     #endif
398    
399     /* draw source selector */
400     cairo_t *cr = cairo_create(priv->map_source);
401     cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
402     cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
403     cairo_paint(cr);
404     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
405    
406     #ifdef OSD_SHADOW_ENABLE
407     osd_source_shape(priv, cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
408     osd_shape_shadow(cr);
409     #endif
410    
411     osd_source_shape(priv, cr, 1, 1);
412     #ifndef OSD_COLOR
413     osd_shape(cr, &bg, &fg);
414     #else
415     osd_shape(cr);
416     #endif
417    
418     #ifdef OSD_SHADOW_ENABLE
419     osd_labels_shadow(cr, Z_RAD/3, TRUE);
420 harbaum 87 osd_source_content(osd, cr, 1+OSD_LBL_SHADOW);
421     cairo_stroke (cr);
422 harbaum 86 #endif
423     #ifndef OSD_COLOR
424     osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
425     #else
426     osd_labels(cr, Z_RAD/3, TRUE);
427     #endif
428 harbaum 87 osd_source_content(osd, cr, 1);
429     cairo_stroke (cr);
430 harbaum 86
431     cairo_destroy(cr);
432     }
433    
434 harbaum 89 /* re-allocate the buffer used to draw the menu. This is used */
435     /* to collapse/expand the buffer */
436 harbaum 86 static void
437     osd_source_reallocate(osm_gps_map_osd_t *osd) {
438     osd_priv_t *priv = (osd_priv_t*)osd->priv;
439    
440     /* re-allocate offscreen bitmap */
441     g_assert (priv->map_source);
442    
443     int w = OSD_S_W, h = OSD_S_H;
444     if(priv->expanded) {
445 harbaum 87 /* ... and right of it the waypoint id */
446     cairo_text_extents_t extents;
447    
448     /* determine content size */
449     cairo_t *cr = cairo_create(priv->map_source);
450     cairo_select_font_face (cr, "Sans",
451     CAIRO_FONT_SLANT_NORMAL,
452     CAIRO_FONT_WEIGHT_BOLD);
453     cairo_set_font_size (cr, OSD_FONT_SIZE);
454    
455     /* calculate menu size */
456     int i, max_h = 0, max_w = 0;
457     for(i=OSM_GPS_MAP_SOURCE_NULL+1;i<=OSM_GPS_MAP_SOURCE_LAST;i++) {
458     const char *src = osm_gps_map_source_get_friendly_name(i);
459     cairo_text_extents (cr, src, &extents);
460    
461     if(extents.width > max_w) max_w = extents.width;
462     if(extents.height > max_h) max_h = extents.height;
463     }
464     cairo_destroy(cr);
465    
466     priv->width = max_w + 2*OSD_TEXT_BORDER;
467     priv->height = OSM_GPS_MAP_SOURCE_LAST *
468     (max_h + 2*OSD_TEXT_SKIP) + 2*OSD_TEXT_BORDER;
469    
470 harbaum 86 w = OSD_S_EXP_W;
471     h = OSD_S_EXP_H;
472     }
473    
474 harbaum 87 cairo_surface_destroy(priv->map_source);
475 harbaum 86 priv->map_source =
476     cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w+2, h+2);
477    
478     osd_render_source_sel(osd);
479    
480     }
481    
482     #define OSD_HZ 15
483 harbaum 87 #define OSD_TIME 500
484 harbaum 86
485     static gboolean osd_source_animate(gpointer data) {
486     osm_gps_map_osd_t *osd = (osm_gps_map_osd_t*)data;
487     osd_priv_t *priv = (osd_priv_t*)osd->priv;
488     int diff = OSD_S_EXP_W - OSD_S_W - OSD_S_X;
489     gboolean done = FALSE;
490     priv->count += priv->dir;
491    
492     /* shifting in */
493     if(priv->dir < 0) {
494     if(priv->count <= 0) {
495     priv->count = 0;
496     done = TRUE;
497     }
498     } else {
499     if(priv->count >= 1000) {
500     priv->expanded = FALSE;
501     osd_source_reallocate(osd);
502    
503     priv->count = 1000;
504     done = TRUE;
505     }
506     }
507    
508    
509     /* count runs linearly from 0 to 1000, map this nicely onto a position */
510    
511     /* nicer sinoid mapping */
512     float m = 0.5-cos(priv->count * M_PI / 1000.0)/2;
513     priv->shift = (osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X) +
514     m * diff;
515    
516     osm_gps_map_repaint(OSM_GPS_MAP(osd->widget));
517    
518     if(done)
519     priv->handler_id = 0;
520    
521     return !done;
522     }
523    
524     /* switch between expand and collapse mode of source selection */
525     static void
526     osd_source_toggle(osm_gps_map_osd_t *osd)
527     {
528     osd_priv_t *priv = (osd_priv_t*)osd->priv;
529    
530     /* ignore clicks while animation is running */
531     if(priv->handler_id)
532     return;
533    
534     /* expand immediately, collapse is handle at the end of the collapse animation */
535     if(!priv->expanded) {
536     priv->expanded = TRUE;
537     osd_source_reallocate(osd);
538    
539     priv->count = 1000;
540     priv->shift = osd->widget->allocation.width - OSD_S_W;
541     priv->dir = -1000/OSD_HZ;
542     } else {
543     priv->count = 0;
544     priv->shift = osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
545     priv->dir = +1000/OSD_HZ;
546     }
547    
548     priv->handler_id = gtk_timeout_add(OSD_TIME/OSD_HZ, osd_source_animate, osd);
549     }
550    
551 harbaum 89 /* check if the user clicked inside the source selection area */
552 harbaum 70 static osd_button_t
553 harbaum 86 osd_source_check(osm_gps_map_osd_t *osd, gint x, gint y) {
554     osd_priv_t *priv = (osd_priv_t*)osd->priv;
555    
556     if(!priv->expanded)
557     x -= osd->widget->allocation.width - OSD_S_W;
558     else
559     x -= osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
560    
561     if(OSD_S_Y > 0)
562     y -= OSD_S_Y;
563     else
564     y -= osd->widget->allocation.height - OSD_S_PH + OSD_S_Y;
565    
566     /* within square around puller? */
567     if(y > 0 && y < OSD_S_PH && x > 0 && x < OSD_S_PW) {
568     /* really within puller shape? */
569     if(x > Z_RAD || osm_gps_map_in_circle(x, y, Z_RAD, Z_RAD, Z_RAD)) {
570     /* expand source selector */
571     osd_source_toggle(osd);
572    
573     /* tell upper layers that user clicked some background element */
574     /* of the OSD */
575     return OSD_BG;
576     }
577     }
578 harbaum 88
579     /* check for clicks into data area */
580 harbaum 89 if(priv->expanded && !priv->handler_id) {
581 harbaum 94 /* re-adjust from puller top to content top */
582     if(OSD_S_Y < 0)
583     y += OSD_S_EXP_H - OSD_S_PH;
584    
585 harbaum 88 if(x > OSD_S_PW &&
586     x < OSD_S_PW + OSD_S_EXP_W &&
587     y > 0 &&
588     y < OSD_S_EXP_H) {
589 harbaum 94
590 harbaum 89 int step = (priv->height - 2*OSD_TEXT_BORDER)
591     / OSM_GPS_MAP_SOURCE_LAST;
592 harbaum 88
593 harbaum 89 y -= OSD_TEXT_BORDER - OSD_TEXT_SKIP;
594     y /= step;
595     y += 1;
596    
597     gint old = 0;
598     g_object_get(osd->widget, "map-source", &old, NULL);
599    
600     if(y > OSM_GPS_MAP_SOURCE_NULL &&
601     y <= OSM_GPS_MAP_SOURCE_LAST &&
602     old != y) {
603     g_object_set(osd->widget, "map-source", y, NULL);
604    
605     osd_render_source_sel(osd);
606     osm_gps_map_repaint(OSM_GPS_MAP(osd->widget));
607     }
608    
609     /* return "clicked in OSD background" to prevent further */
610     /* processing by application */
611 harbaum 88 return OSD_BG;
612     }
613     }
614    
615 harbaum 86 return OSD_NONE;
616     }
617 harbaum 88 #endif // OSD_SOURCE_SEL
618 harbaum 86
619     static osd_button_t
620     osd_check(osm_gps_map_osd_t *osd, gint x, gint y) {
621 harbaum 70 osd_button_t but = OSD_NONE;
622    
623 harbaum 86 #ifdef OSD_SOURCE_SEL
624     /* the source selection area is handles internally */
625     but = osd_source_check(osd, x, y);
626     if(but != OSD_NONE)
627     return but;
628     #endif
629    
630 harbaum 77 x -= OSD_X;
631     y -= OSD_Y;
632    
633     if(OSD_X < 0)
634     x -= (osd->widget->allocation.width - OSD_W);
635    
636     if(OSD_Y < 0)
637     y -= (osd->widget->allocation.height - OSD_H);
638    
639 harbaum 70 /* first do a rough test for the OSD area. */
640     /* this is just to avoid an unnecessary detailed test */
641 harbaum 77 if(x > 0 && x < OSD_W && y > 0 && y < OSD_H) {
642 harbaum 76 #ifndef OSD_NO_DPAD
643 harbaum 86 but = osd_check_dpad(x, y);
644 harbaum 76 #endif
645 harbaum 70
646     if(but == OSD_NONE)
647 harbaum 86 but = osd_check_zoom(x, y);
648 harbaum 70 }
649    
650     return but;
651     }
652    
653 harbaum 76 #ifndef OSD_NO_DPAD
654 harbaum 70 static void
655 harbaum 86 osd_dpad_labels(cairo_t *cr, gint x, gint y) {
656 harbaum 70 /* move reference to dpad center */
657     x += D_RAD;
658     y += D_RAD;
659    
660     const static gint offset[][3][2] = {
661     /* left arrow/triangle */
662     { { -D_TIP+D_LEN, -D_WID }, { -D_LEN, D_WID }, { +D_LEN, D_WID } },
663     /* right arrow/triangle */
664     { { +D_TIP-D_LEN, -D_WID }, { +D_LEN, D_WID }, { -D_LEN, D_WID } },
665     /* top arrow/triangle */
666     { { -D_WID, -D_TIP+D_LEN }, { D_WID, -D_LEN }, { D_WID, +D_LEN } },
667     /* bottom arrow/triangle */
668     { { -D_WID, +D_TIP-D_LEN }, { D_WID, +D_LEN }, { D_WID, -D_LEN } }
669     };
670    
671     int i;
672     for(i=0;i<4;i++) {
673     cairo_move_to (cr, x + offset[i][0][0], y + offset[i][0][1]);
674     cairo_rel_line_to (cr, offset[i][1][0], offset[i][1][1]);
675     cairo_rel_line_to (cr, offset[i][2][0], offset[i][2][1]);
676     }
677     }
678 harbaum 76 #endif
679 harbaum 70
680 harbaum 76 #ifdef OSD_GPS_BUTTON
681     /* draw the satellite dish icon in the center of the dpad */
682 harbaum 77 #define GPS_V0 (D_RAD/7)
683 harbaum 70 #define GPS_V1 (D_RAD/10)
684     #define GPS_V2 (D_RAD/5)
685    
686     /* draw a satellite receiver dish */
687 harbaum 77 /* this is either drawn in the center of the dpad (if present) */
688     /* or in the middle of the zoom area */
689 harbaum 70 static void
690 harbaum 86 osd_dpad_gps(cairo_t *cr, gint x, gint y) {
691 harbaum 70 /* move reference to dpad center */
692 harbaum 77 x += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD * 3;
693     y += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD + GPS_V0;
694 harbaum 70
695     cairo_move_to (cr, x-GPS_V0, y+GPS_V0);
696     cairo_rel_line_to (cr, +GPS_V0, -GPS_V0);
697     cairo_rel_line_to (cr, +GPS_V0, +GPS_V0);
698     cairo_close_path (cr);
699    
700     cairo_move_to (cr, x+GPS_V1-GPS_V2, y-2*GPS_V2);
701     cairo_curve_to (cr, x-GPS_V2, y, x+GPS_V1, y+GPS_V1, x+GPS_V1+GPS_V2, y);
702     cairo_close_path (cr);
703    
704     x += GPS_V1;
705     cairo_move_to (cr, x, y-GPS_V2);
706     cairo_rel_line_to (cr, +GPS_V1, -GPS_V1);
707     }
708 harbaum 76 #endif
709 harbaum 70
710     #define Z_LEN (2*Z_RAD/3)
711    
712     static void
713 harbaum 86 osd_zoom_labels(cairo_t *cr, gint x, gint y) {
714 harbaum 70 cairo_move_to (cr, x + Z_LEFT - Z_LEN, y + Z_MID);
715     cairo_line_to (cr, x + Z_LEFT + Z_LEN, y + Z_MID);
716    
717     cairo_move_to (cr, x + Z_RIGHT, y + Z_MID - Z_LEN);
718     cairo_line_to (cr, x + Z_RIGHT, y + Z_MID + Z_LEN);
719     cairo_move_to (cr, x + Z_RIGHT - Z_LEN, y + Z_MID);
720     cairo_line_to (cr, x + Z_RIGHT + Z_LEN, y + Z_MID);
721     }
722    
723     static void
724 harbaum 86 osd_render(osm_gps_map_osd_t *osd) {
725     osd_priv_t *priv = (osd_priv_t*)osd->priv;
726 harbaum 70
727 harbaum 86 #ifndef OSD_COLOR
728     GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL];
729     GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL];
730     GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE];
731 harbaum 74 #endif
732 harbaum 70
733     /* first fill with transparency */
734 harbaum 73 cairo_t *cr = cairo_create(priv->overlay);
735 harbaum 70 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
736     cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
737     cairo_paint(cr);
738     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
739    
740     /* --------- draw zoom and dpad shape shadow ----------- */
741 harbaum 74 #ifdef OSD_SHADOW_ENABLE
742 harbaum 86 osd_zoom_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
743     osd_shape_shadow(cr);
744 harbaum 76 #ifndef OSD_NO_DPAD
745 harbaum 86 osd_dpad_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
746     osd_shape_shadow(cr);
747 harbaum 74 #endif
748 harbaum 76 #endif
749 harbaum 70
750     /* --------- draw zoom and dpad shape ----------- */
751    
752 harbaum 86 osd_zoom_shape(cr, 1, 1);
753 harbaum 74 #ifndef OSD_COLOR
754 harbaum 86 osd_shape(cr, &bg, &fg);
755 harbaum 74 #else
756 harbaum 86 osd_shape(cr);
757 harbaum 74 #endif
758 harbaum 76 #ifndef OSD_NO_DPAD
759 harbaum 86 osd_dpad_shape(cr, 1, 1);
760 harbaum 74 #ifndef OSD_COLOR
761 harbaum 86 osd_shape(cr, &bg, &fg);
762 harbaum 74 #else
763 harbaum 86 osd_shape(cr);
764 harbaum 74 #endif
765 harbaum 76 #endif
766 harbaum 70
767     /* --------- draw zoom and dpad labels --------- */
768    
769 harbaum 74 #ifdef OSD_SHADOW_ENABLE
770 harbaum 87 osd_labels_shadow(cr, Z_RAD/3, TRUE);
771 harbaum 86 osd_zoom_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
772 harbaum 76 #ifndef OSD_NO_DPAD
773 harbaum 86 osd_dpad_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
774 harbaum 76 #endif
775 harbaum 87 cairo_stroke(cr);
776 harbaum 76 #ifdef OSD_GPS_BUTTON
777 harbaum 87 osd_labels_shadow(cr, Z_RAD/6, osd->cb != NULL);
778 harbaum 86 osd_dpad_gps(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
779 harbaum 87 cairo_stroke(cr);
780 harbaum 74 #endif
781 harbaum 76 #endif
782 harbaum 70
783 harbaum 74 #ifndef OSD_COLOR
784 harbaum 86 osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
785 harbaum 74 #else
786 harbaum 86 osd_labels(cr, Z_RAD/3, TRUE);
787 harbaum 74 #endif
788 harbaum 87 osd_zoom_labels(cr, 1, 1);
789     #ifndef OSD_NO_DPAD
790     osd_dpad_labels(cr, 1, 1);
791     #endif
792     cairo_stroke(cr);
793    
794 harbaum 74 #ifndef OSD_COLOR
795 harbaum 86 osd_labels(cr, Z_RAD/6, osd->cb != NULL, &fg, &da);
796 harbaum 74 #else
797 harbaum 86 osd_labels(cr, Z_RAD/6, osd->cb != NULL);
798 harbaum 74 #endif
799 harbaum 87 #ifdef OSD_GPS_BUTTON
800     osd_dpad_gps(cr, 1, 1);
801 harbaum 76 #endif
802 harbaum 87 cairo_stroke(cr);
803 harbaum 70
804     cairo_destroy(cr);
805 harbaum 86
806 harbaum 88 #ifdef OSD_SOURCE_SEL
807 harbaum 86 osd_render_source_sel(osd);
808 harbaum 88 #endif
809 harbaum 70 }
810    
811     static void
812 harbaum 86 osd_draw(osm_gps_map_osd_t *osd, GdkDrawable *drawable)
813 harbaum 70 {
814 harbaum 73 osd_priv_t *priv = (osd_priv_t*)osd->priv;
815 harbaum 70
816     /* OSD itself uses some off-screen rendering, so check if the */
817     /* offscreen buffer is present and create it if not */
818 harbaum 73 if(!priv->overlay) {
819 harbaum 70 /* create overlay ... */
820 harbaum 73 priv->overlay =
821 harbaum 86 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W+2, OSD_H+2);
822    
823 harbaum 88 #ifdef OSD_SOURCE_SEL
824 harbaum 86 /* the initial OSD state is alway not-expanded */
825     priv->map_source =
826     cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
827     OSD_S_W+2, OSD_S_H+2);
828 harbaum 88 #endif
829 harbaum 86
830 harbaum 70 /* ... and render it */
831 harbaum 86 osd_render(osd);
832 harbaum 70 }
833    
834     // now draw this onto the original context
835 harbaum 75 cairo_t *cr = gdk_cairo_create(drawable);
836 harbaum 77
837     int x = OSD_X, y = OSD_Y;
838     if(OSD_X < 0)
839     x = osd->widget->allocation.width - OSD_W + OSD_X;
840    
841     if(OSD_Y < 0)
842     y = osd->widget->allocation.height - OSD_H + OSD_Y;
843    
844     cairo_set_source_surface(cr, priv->overlay, x, y);
845 harbaum 70 cairo_paint(cr);
846 harbaum 86
847     #ifdef OSD_SOURCE_SEL
848     if(!priv->handler_id) {
849     /* the OSD source selection is not being animated */
850     if(!priv->expanded)
851     x = osd->widget->allocation.width - OSD_S_W;
852     else
853     x = osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
854     } else
855     x = priv->shift;
856    
857     y = OSD_S_Y;
858     if(OSD_S_Y < 0) {
859     if(!priv->expanded)
860     y = osd->widget->allocation.height - OSD_S_H + OSD_S_Y;
861     else
862     y = osd->widget->allocation.height - OSD_S_EXP_H + OSD_S_Y;
863     }
864    
865     cairo_set_source_surface(cr, priv->map_source, x, y);
866     cairo_paint(cr);
867     #endif
868    
869 harbaum 70 cairo_destroy(cr);
870     }
871    
872     static void
873 harbaum 86 osd_free(osm_gps_map_osd_t *osd)
874 harbaum 70 {
875 harbaum 73 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
876 harbaum 70
877 harbaum 88 if (priv->overlay)
878     cairo_surface_destroy(priv->overlay);
879    
880     #ifdef OSD_SOURCE_SEL
881 harbaum 86 if(priv->handler_id)
882     gtk_timeout_remove(priv->handler_id);
883    
884     if (priv->map_source)
885     cairo_surface_destroy(priv->map_source);
886 harbaum 88 #endif
887 harbaum 86
888 harbaum 73 g_free(priv);
889     }
890    
891 harbaum 87 static gboolean
892     osd_busy(osm_gps_map_osd_t *osd)
893     {
894 harbaum 88 #ifdef OSD_SOURCE_SEL
895 harbaum 87 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
896     return (priv->handler_id != 0);
897 harbaum 88 #else
898     return FALSE;
899     #endif
900 harbaum 87 }
901    
902 harbaum 73 static osm_gps_map_osd_t osd_classic = {
903 harbaum 88 .widget = NULL,
904    
905 harbaum 86 .draw = osd_draw,
906     .check = osd_check,
907     .render = osd_render,
908     .free = osd_free,
909 harbaum 87 .busy = osd_busy,
910 harbaum 73
911     .cb = NULL,
912     .data = NULL,
913    
914     .priv = NULL
915     };
916    
917     /* this is the only function that's externally visible */
918     void
919     osm_gps_map_osd_classic_init(OsmGpsMap *map)
920     {
921     osd_priv_t *priv = osd_classic.priv = g_new0(osd_priv_t, 1);
922    
923     osd_classic.priv = priv;
924    
925     osm_gps_map_register_osd(map, &osd_classic);
926     }
927    
928 harbaum 76 #ifdef OSD_GPS_BUTTON
929 harbaum 74 /* below are osd specific functions which aren't used by osm-gps-map */
930     /* but instead are to be used by the main application */
931 harbaum 76 void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdCallback cb,
932     gpointer data) {
933 harbaum 73 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
934     g_return_if_fail (osd);
935    
936     osd->cb = cb;
937     osd->data = data;
938    
939 harbaum 70 /* this may have changed the state of the gps button */
940     /* we thus re-render the overlay */
941 harbaum 73 osd->render(osd);
942 harbaum 70
943 harbaum 73 osm_gps_map_redraw(map);
944 harbaum 70 }
945 harbaum 76 #endif
946 harbaum 86
947     osd_button_t
948     osm_gps_map_osd_check(OsmGpsMap *map, gint x, gint y) {
949     osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
950     g_return_val_if_fail (osd, OSD_NONE);
951    
952     return osd_check(osd, x, y);
953     }