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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 89 - (hide annotations)
Tue Sep 1 11:16:30 2009 UTC (14 years, 8 months ago) by harbaum
File MIME type: text/plain
File size: 27233 byte(s)
OSD source selection working
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 88 if(x > OSD_S_PW &&
582     x < OSD_S_PW + OSD_S_EXP_W &&
583     y > 0 &&
584     y < OSD_S_EXP_H) {
585    
586 harbaum 89 int step = (priv->height - 2*OSD_TEXT_BORDER)
587     / OSM_GPS_MAP_SOURCE_LAST;
588 harbaum 88
589 harbaum 89 y -= OSD_TEXT_BORDER - OSD_TEXT_SKIP;
590     y /= step;
591     y += 1;
592    
593     gint old = 0;
594     g_object_get(osd->widget, "map-source", &old, NULL);
595    
596     if(y > OSM_GPS_MAP_SOURCE_NULL &&
597     y <= OSM_GPS_MAP_SOURCE_LAST &&
598     old != y) {
599     g_object_set(osd->widget, "map-source", y, NULL);
600    
601     osd_render_source_sel(osd);
602     osm_gps_map_repaint(OSM_GPS_MAP(osd->widget));
603     }
604    
605     /* return "clicked in OSD background" to prevent further */
606     /* processing by application */
607 harbaum 88 return OSD_BG;
608     }
609     }
610    
611 harbaum 86 return OSD_NONE;
612     }
613 harbaum 88 #endif // OSD_SOURCE_SEL
614 harbaum 86
615     static osd_button_t
616     osd_check(osm_gps_map_osd_t *osd, gint x, gint y) {
617 harbaum 70 osd_button_t but = OSD_NONE;
618    
619 harbaum 86 #ifdef OSD_SOURCE_SEL
620     /* the source selection area is handles internally */
621     but = osd_source_check(osd, x, y);
622     if(but != OSD_NONE)
623     return but;
624     #endif
625    
626 harbaum 77 x -= OSD_X;
627     y -= OSD_Y;
628    
629     if(OSD_X < 0)
630     x -= (osd->widget->allocation.width - OSD_W);
631    
632     if(OSD_Y < 0)
633     y -= (osd->widget->allocation.height - OSD_H);
634    
635 harbaum 70 /* first do a rough test for the OSD area. */
636     /* this is just to avoid an unnecessary detailed test */
637 harbaum 77 if(x > 0 && x < OSD_W && y > 0 && y < OSD_H) {
638 harbaum 76 #ifndef OSD_NO_DPAD
639 harbaum 86 but = osd_check_dpad(x, y);
640 harbaum 76 #endif
641 harbaum 70
642     if(but == OSD_NONE)
643 harbaum 86 but = osd_check_zoom(x, y);
644 harbaum 70 }
645    
646     return but;
647     }
648    
649 harbaum 76 #ifndef OSD_NO_DPAD
650 harbaum 70 static void
651 harbaum 86 osd_dpad_labels(cairo_t *cr, gint x, gint y) {
652 harbaum 70 /* move reference to dpad center */
653     x += D_RAD;
654     y += D_RAD;
655    
656     const static gint offset[][3][2] = {
657     /* left arrow/triangle */
658     { { -D_TIP+D_LEN, -D_WID }, { -D_LEN, D_WID }, { +D_LEN, D_WID } },
659     /* right arrow/triangle */
660     { { +D_TIP-D_LEN, -D_WID }, { +D_LEN, D_WID }, { -D_LEN, D_WID } },
661     /* top arrow/triangle */
662     { { -D_WID, -D_TIP+D_LEN }, { D_WID, -D_LEN }, { D_WID, +D_LEN } },
663     /* bottom arrow/triangle */
664     { { -D_WID, +D_TIP-D_LEN }, { D_WID, +D_LEN }, { D_WID, -D_LEN } }
665     };
666    
667     int i;
668     for(i=0;i<4;i++) {
669     cairo_move_to (cr, x + offset[i][0][0], y + offset[i][0][1]);
670     cairo_rel_line_to (cr, offset[i][1][0], offset[i][1][1]);
671     cairo_rel_line_to (cr, offset[i][2][0], offset[i][2][1]);
672     }
673     }
674 harbaum 76 #endif
675 harbaum 70
676 harbaum 76 #ifdef OSD_GPS_BUTTON
677     /* draw the satellite dish icon in the center of the dpad */
678 harbaum 77 #define GPS_V0 (D_RAD/7)
679 harbaum 70 #define GPS_V1 (D_RAD/10)
680     #define GPS_V2 (D_RAD/5)
681    
682     /* draw a satellite receiver dish */
683 harbaum 77 /* this is either drawn in the center of the dpad (if present) */
684     /* or in the middle of the zoom area */
685 harbaum 70 static void
686 harbaum 86 osd_dpad_gps(cairo_t *cr, gint x, gint y) {
687 harbaum 70 /* move reference to dpad center */
688 harbaum 77 x += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD * 3;
689     y += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD + GPS_V0;
690 harbaum 70
691     cairo_move_to (cr, x-GPS_V0, y+GPS_V0);
692     cairo_rel_line_to (cr, +GPS_V0, -GPS_V0);
693     cairo_rel_line_to (cr, +GPS_V0, +GPS_V0);
694     cairo_close_path (cr);
695    
696     cairo_move_to (cr, x+GPS_V1-GPS_V2, y-2*GPS_V2);
697     cairo_curve_to (cr, x-GPS_V2, y, x+GPS_V1, y+GPS_V1, x+GPS_V1+GPS_V2, y);
698     cairo_close_path (cr);
699    
700     x += GPS_V1;
701     cairo_move_to (cr, x, y-GPS_V2);
702     cairo_rel_line_to (cr, +GPS_V1, -GPS_V1);
703     }
704 harbaum 76 #endif
705 harbaum 70
706     #define Z_LEN (2*Z_RAD/3)
707    
708     static void
709 harbaum 86 osd_zoom_labels(cairo_t *cr, gint x, gint y) {
710 harbaum 70 cairo_move_to (cr, x + Z_LEFT - Z_LEN, y + Z_MID);
711     cairo_line_to (cr, x + Z_LEFT + Z_LEN, y + Z_MID);
712    
713     cairo_move_to (cr, x + Z_RIGHT, y + Z_MID - Z_LEN);
714     cairo_line_to (cr, x + Z_RIGHT, y + Z_MID + Z_LEN);
715     cairo_move_to (cr, x + Z_RIGHT - Z_LEN, y + Z_MID);
716     cairo_line_to (cr, x + Z_RIGHT + Z_LEN, y + Z_MID);
717     }
718    
719     static void
720 harbaum 86 osd_render(osm_gps_map_osd_t *osd) {
721     osd_priv_t *priv = (osd_priv_t*)osd->priv;
722 harbaum 70
723 harbaum 86 #ifndef OSD_COLOR
724     GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL];
725     GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL];
726     GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE];
727 harbaum 74 #endif
728 harbaum 70
729     /* first fill with transparency */
730 harbaum 73 cairo_t *cr = cairo_create(priv->overlay);
731 harbaum 70 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
732     cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
733     cairo_paint(cr);
734     cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
735    
736     /* --------- draw zoom and dpad shape shadow ----------- */
737 harbaum 74 #ifdef OSD_SHADOW_ENABLE
738 harbaum 86 osd_zoom_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
739     osd_shape_shadow(cr);
740 harbaum 76 #ifndef OSD_NO_DPAD
741 harbaum 86 osd_dpad_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
742     osd_shape_shadow(cr);
743 harbaum 74 #endif
744 harbaum 76 #endif
745 harbaum 70
746     /* --------- draw zoom and dpad shape ----------- */
747    
748 harbaum 86 osd_zoom_shape(cr, 1, 1);
749 harbaum 74 #ifndef OSD_COLOR
750 harbaum 86 osd_shape(cr, &bg, &fg);
751 harbaum 74 #else
752 harbaum 86 osd_shape(cr);
753 harbaum 74 #endif
754 harbaum 76 #ifndef OSD_NO_DPAD
755 harbaum 86 osd_dpad_shape(cr, 1, 1);
756 harbaum 74 #ifndef OSD_COLOR
757 harbaum 86 osd_shape(cr, &bg, &fg);
758 harbaum 74 #else
759 harbaum 86 osd_shape(cr);
760 harbaum 74 #endif
761 harbaum 76 #endif
762 harbaum 70
763     /* --------- draw zoom and dpad labels --------- */
764    
765 harbaum 74 #ifdef OSD_SHADOW_ENABLE
766 harbaum 87 osd_labels_shadow(cr, Z_RAD/3, TRUE);
767 harbaum 86 osd_zoom_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
768 harbaum 76 #ifndef OSD_NO_DPAD
769 harbaum 86 osd_dpad_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
770 harbaum 76 #endif
771 harbaum 87 cairo_stroke(cr);
772 harbaum 76 #ifdef OSD_GPS_BUTTON
773 harbaum 87 osd_labels_shadow(cr, Z_RAD/6, osd->cb != NULL);
774 harbaum 86 osd_dpad_gps(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
775 harbaum 87 cairo_stroke(cr);
776 harbaum 74 #endif
777 harbaum 76 #endif
778 harbaum 70
779 harbaum 74 #ifndef OSD_COLOR
780 harbaum 86 osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
781 harbaum 74 #else
782 harbaum 86 osd_labels(cr, Z_RAD/3, TRUE);
783 harbaum 74 #endif
784 harbaum 87 osd_zoom_labels(cr, 1, 1);
785     #ifndef OSD_NO_DPAD
786     osd_dpad_labels(cr, 1, 1);
787     #endif
788     cairo_stroke(cr);
789    
790 harbaum 74 #ifndef OSD_COLOR
791 harbaum 86 osd_labels(cr, Z_RAD/6, osd->cb != NULL, &fg, &da);
792 harbaum 74 #else
793 harbaum 86 osd_labels(cr, Z_RAD/6, osd->cb != NULL);
794 harbaum 74 #endif
795 harbaum 87 #ifdef OSD_GPS_BUTTON
796     osd_dpad_gps(cr, 1, 1);
797 harbaum 76 #endif
798 harbaum 87 cairo_stroke(cr);
799 harbaum 70
800     cairo_destroy(cr);
801 harbaum 86
802 harbaum 88 #ifdef OSD_SOURCE_SEL
803 harbaum 86 osd_render_source_sel(osd);
804 harbaum 88 #endif
805 harbaum 70 }
806    
807     static void
808 harbaum 86 osd_draw(osm_gps_map_osd_t *osd, GdkDrawable *drawable)
809 harbaum 70 {
810 harbaum 73 osd_priv_t *priv = (osd_priv_t*)osd->priv;
811 harbaum 70
812     /* OSD itself uses some off-screen rendering, so check if the */
813     /* offscreen buffer is present and create it if not */
814 harbaum 73 if(!priv->overlay) {
815 harbaum 70 /* create overlay ... */
816 harbaum 73 priv->overlay =
817 harbaum 86 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W+2, OSD_H+2);
818    
819 harbaum 88 #ifdef OSD_SOURCE_SEL
820 harbaum 86 /* the initial OSD state is alway not-expanded */
821     priv->map_source =
822     cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
823     OSD_S_W+2, OSD_S_H+2);
824 harbaum 88 #endif
825 harbaum 86
826 harbaum 70 /* ... and render it */
827 harbaum 86 osd_render(osd);
828 harbaum 70 }
829    
830     // now draw this onto the original context
831 harbaum 75 cairo_t *cr = gdk_cairo_create(drawable);
832 harbaum 77
833     int x = OSD_X, y = OSD_Y;
834     if(OSD_X < 0)
835     x = osd->widget->allocation.width - OSD_W + OSD_X;
836    
837     if(OSD_Y < 0)
838     y = osd->widget->allocation.height - OSD_H + OSD_Y;
839    
840     cairo_set_source_surface(cr, priv->overlay, x, y);
841 harbaum 70 cairo_paint(cr);
842 harbaum 86
843     #ifdef OSD_SOURCE_SEL
844     if(!priv->handler_id) {
845     /* the OSD source selection is not being animated */
846     if(!priv->expanded)
847     x = osd->widget->allocation.width - OSD_S_W;
848     else
849     x = osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
850     } else
851     x = priv->shift;
852    
853     y = OSD_S_Y;
854     if(OSD_S_Y < 0) {
855     if(!priv->expanded)
856     y = osd->widget->allocation.height - OSD_S_H + OSD_S_Y;
857     else
858     y = osd->widget->allocation.height - OSD_S_EXP_H + OSD_S_Y;
859     }
860    
861     cairo_set_source_surface(cr, priv->map_source, x, y);
862     cairo_paint(cr);
863     #endif
864    
865 harbaum 70 cairo_destroy(cr);
866     }
867    
868     static void
869 harbaum 86 osd_free(osm_gps_map_osd_t *osd)
870 harbaum 70 {
871 harbaum 73 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
872 harbaum 70
873 harbaum 88 if (priv->overlay)
874     cairo_surface_destroy(priv->overlay);
875    
876     #ifdef OSD_SOURCE_SEL
877 harbaum 86 if(priv->handler_id)
878     gtk_timeout_remove(priv->handler_id);
879    
880     if (priv->map_source)
881     cairo_surface_destroy(priv->map_source);
882 harbaum 88 #endif
883 harbaum 86
884 harbaum 73 g_free(priv);
885     }
886    
887 harbaum 87 static gboolean
888     osd_busy(osm_gps_map_osd_t *osd)
889     {
890 harbaum 88 #ifdef OSD_SOURCE_SEL
891 harbaum 87 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
892     return (priv->handler_id != 0);
893 harbaum 88 #else
894     return FALSE;
895     #endif
896 harbaum 87 }
897    
898 harbaum 73 static osm_gps_map_osd_t osd_classic = {
899 harbaum 88 .widget = NULL,
900    
901 harbaum 86 .draw = osd_draw,
902     .check = osd_check,
903     .render = osd_render,
904     .free = osd_free,
905 harbaum 87 .busy = osd_busy,
906 harbaum 73
907     .cb = NULL,
908     .data = NULL,
909    
910     .priv = NULL
911     };
912    
913     /* this is the only function that's externally visible */
914     void
915     osm_gps_map_osd_classic_init(OsmGpsMap *map)
916     {
917     osd_priv_t *priv = osd_classic.priv = g_new0(osd_priv_t, 1);
918    
919     osd_classic.priv = priv;
920    
921     osm_gps_map_register_osd(map, &osd_classic);
922     }
923    
924 harbaum 76 #ifdef OSD_GPS_BUTTON
925 harbaum 74 /* below are osd specific functions which aren't used by osm-gps-map */
926     /* but instead are to be used by the main application */
927 harbaum 76 void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdCallback cb,
928     gpointer data) {
929 harbaum 73 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
930     g_return_if_fail (osd);
931    
932     osd->cb = cb;
933     osd->data = data;
934    
935 harbaum 70 /* this may have changed the state of the gps button */
936     /* we thus re-render the overlay */
937 harbaum 73 osd->render(osd);
938 harbaum 70
939 harbaum 73 osm_gps_map_redraw(map);
940 harbaum 70 }
941 harbaum 76 #endif
942 harbaum 86
943     osd_button_t
944     osm_gps_map_osd_check(OsmGpsMap *map, gint x, gint y) {
945     osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
946     g_return_val_if_fail (osd, OSD_NONE);
947    
948     return osd_check(osd, x, y);
949     }