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

Parent Directory Parent Directory | Revision Log Revision Log


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