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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 88 - (show annotations)
Mon Aug 31 14:22:28 2009 UTC (14 years, 8 months ago) by harbaum
File MIME type: text/plain
File size: 26658 byte(s)
OSD adjustments
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 #ifdef OSD_SOURCE_SEL
43 /* values to handle the "source" menu */
44 cairo_surface_t *map_source;
45 gboolean expanded;
46 gint shift, dir, count;
47 gint handler_id;
48 gint width, height;
49 #endif
50
51 } osd_priv_t;
52
53 /* position and extent of bounding box */
54 #ifndef OSD_X
55 #define OSD_X (10)
56 #endif
57
58 #ifndef OSD_Y
59 #define OSD_Y (10)
60 #endif
61
62 /* parameters of the direction shape */
63 #ifndef OSD_DIAMETER
64 #define D_RAD (30) // diameter of dpad
65 #else
66 #define D_RAD (OSD_DIAMETER)
67 #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 #ifdef OSD_SHADOW_ENABLE
77 /* shadow also depends on control size */
78 #define OSD_SHADOW (D_RAD/6)
79 #else
80 #define OSD_SHADOW (0)
81 #endif
82
83 /* 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 /* total width and height of controls incl. shadow */
92 #define OSD_W (2*D_RAD + OSD_SHADOW + Z_GPS * 2 * Z_RAD)
93 #if !Z_GPS
94 #define OSD_H (2*D_RAD + Z_STEP + 2*Z_RAD + OSD_SHADOW)
95 #else
96 #define OSD_H (2*Z_RAD + OSD_SHADOW)
97 #endif
98
99 #ifdef OSD_SHADOW_ENABLE
100 #define OSD_LBL_SHADOW (OSD_SHADOW/2)
101 #endif
102
103 #define Z_TOP ((1-Z_GPS) * (2 * D_RAD + Z_STEP))
104
105 #define Z_MID (Z_TOP + Z_RAD)
106 #define Z_BOT (Z_MID + Z_RAD)
107 #define Z_LEFT (Z_RAD)
108 #define Z_RIGHT (2 * D_RAD - Z_RAD + Z_GPS * 2 * Z_RAD)
109 #define Z_CENTER ((Z_RIGHT + Z_LEFT)/2)
110
111 /* create the cairo shape used for the zoom buttons */
112 static void
113 osd_zoom_shape(cairo_t *cr, gint x, gint y)
114 {
115 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 /* ------------------- 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 #ifndef OSD_NO_DPAD
151 /* create the cairo shape used for the dpad */
152 static void
153 osd_dpad_shape(cairo_t *cr, gint x, gint y)
154 {
155 cairo_arc (cr, x+D_RAD, y+D_RAD, D_RAD, 0, 2 * M_PI);
156 }
157 #endif
158
159 #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 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 #ifndef OSD_NO_DPAD
197 /* check whether x/y is within the dpad */
198 static osd_button_t
199 osd_check_dpad(gint x, gint y)
200 {
201 /* within entire dpad circle */
202 if( osm_gps_map_in_circle(x, y, D_RAD, D_RAD, D_RAD))
203 {
204 /* convert into position relative to dpads centre */
205 x -= D_RAD;
206 y -= D_RAD;
207
208 #ifdef OSD_GPS_BUTTON
209 /* 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 #endif
213
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 #endif
231
232 /* check whether x/y is within the zoom pads */
233 static osd_button_t
234 osd_check_zoom(gint x, gint y) {
235 if( x > 0 && x < OSD_W && y > Z_TOP && y < Z_BOT) {
236
237 /* within circle around (-) label */
238 if( osm_gps_map_in_circle(x, y, Z_LEFT, Z_MID, Z_RAD))
239 return OSD_OUT;
240
241 /* 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 /* between center of (-) button and center of entire zoom control area */
252 if(x > OSD_LEFT && x < D_RAD)
253 return OSD_OUT;
254
255 /* between center of (+) button and center of entire zoom control area */
256 if(x < OSD_RIGHT && x > D_RAD)
257 return OSD_IN;
258 }
259
260 return OSD_NONE;
261 }
262
263 #ifdef OSD_SOURCE_SEL
264
265 /* 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 /* size of usable area when expanded */
275 #define OSD_S_AREA_W (priv->width)
276 #define OSD_S_AREA_H (priv->height)
277 #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 #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
288 /* draw the shape of the source selection OSD, either only the puller (not expanded) */
289 /* or the entire menu incl. the puller (expanded) */
290 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 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
318 int py = offset + OSD_S_RAD - OSD_S_D0;
319
320 if(!priv->expanded) {
321 /* draw the "puller" open (<) arrow */
322 cairo_move_to (cr, offset + OSD_S_RAD + OSD_S_D0/2, py);
323 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 py += OSD_S_AREA_H - OSD_S_PH;
328
329 /* draw the "puller" close (>) arrow */
330 cairo_move_to (cr, offset + OSD_S_RAD - OSD_S_D0/2, py);
331 cairo_rel_line_to (cr, +OSD_S_D0, +OSD_S_D0);
332 cairo_rel_line_to (cr, -OSD_S_D0, +OSD_S_D0);
333 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 int i, step = (priv->height - 2*OSD_TEXT_BORDER)
346 / OSM_GPS_MAP_SOURCE_LAST;
347 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 }
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 osd_source_content(osd, cr, 1+OSD_LBL_SHADOW);
421 cairo_stroke (cr);
422 #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 osd_source_content(osd, cr, 1);
429 cairo_stroke (cr);
430
431 cairo_destroy(cr);
432 }
433
434 static void
435 osd_source_reallocate(osm_gps_map_osd_t *osd) {
436 osd_priv_t *priv = (osd_priv_t*)osd->priv;
437
438 /* re-allocate offscreen bitmap */
439 g_assert (priv->map_source);
440
441 int w = OSD_S_W, h = OSD_S_H;
442 if(priv->expanded) {
443 /* ... and right of it the waypoint id */
444 cairo_text_extents_t extents;
445
446 /* determine content size */
447 cairo_t *cr = cairo_create(priv->map_source);
448 cairo_select_font_face (cr, "Sans",
449 CAIRO_FONT_SLANT_NORMAL,
450 CAIRO_FONT_WEIGHT_BOLD);
451 cairo_set_font_size (cr, OSD_FONT_SIZE);
452
453 /* calculate menu size */
454 int i, max_h = 0, max_w = 0;
455 for(i=OSM_GPS_MAP_SOURCE_NULL+1;i<=OSM_GPS_MAP_SOURCE_LAST;i++) {
456 const char *src = osm_gps_map_source_get_friendly_name(i);
457 cairo_text_extents (cr, src, &extents);
458
459 // printf("Source %d: %s = %f %f\n", i, src,
460 // extents.width, extents.height);
461
462 if(extents.width > max_w) max_w = extents.width;
463 if(extents.height > max_h) max_h = extents.height;
464 }
465 cairo_destroy(cr);
466
467 priv->width = max_w + 2*OSD_TEXT_BORDER;
468 priv->height = OSM_GPS_MAP_SOURCE_LAST *
469 (max_h + 2*OSD_TEXT_SKIP) + 2*OSD_TEXT_BORDER;
470
471 w = OSD_S_EXP_W;
472 h = OSD_S_EXP_H;
473 }
474
475 cairo_surface_destroy(priv->map_source);
476 priv->map_source =
477 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w+2, h+2);
478
479 osd_render_source_sel(osd);
480
481 }
482
483 #define OSD_HZ 15
484 #define OSD_TIME 500
485
486 static gboolean osd_source_animate(gpointer data) {
487 osm_gps_map_osd_t *osd = (osm_gps_map_osd_t*)data;
488 osd_priv_t *priv = (osd_priv_t*)osd->priv;
489 int diff = OSD_S_EXP_W - OSD_S_W - OSD_S_X;
490 gboolean done = FALSE;
491 priv->count += priv->dir;
492
493 /* shifting in */
494 if(priv->dir < 0) {
495 if(priv->count <= 0) {
496 priv->count = 0;
497 done = TRUE;
498 }
499 } else {
500 if(priv->count >= 1000) {
501 priv->expanded = FALSE;
502 osd_source_reallocate(osd);
503
504 priv->count = 1000;
505 done = TRUE;
506 }
507 }
508
509
510 /* count runs linearly from 0 to 1000, map this nicely onto a position */
511
512 /* simple linear mapping */
513 // priv->shift = (osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X) +
514 // (diff * priv->count)/1000;
515
516 /* nicer sinoid mapping */
517 float m = 0.5-cos(priv->count * M_PI / 1000.0)/2;
518 priv->shift = (osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X) +
519 m * diff;
520
521 osm_gps_map_repaint(OSM_GPS_MAP(osd->widget));
522
523 if(done)
524 priv->handler_id = 0;
525
526 return !done;
527 }
528
529 /* switch between expand and collapse mode of source selection */
530 static void
531 osd_source_toggle(osm_gps_map_osd_t *osd)
532 {
533 osd_priv_t *priv = (osd_priv_t*)osd->priv;
534
535 /* ignore clicks while animation is running */
536 if(priv->handler_id)
537 return;
538
539 /* expand immediately, collapse is handle at the end of the collapse animation */
540 if(!priv->expanded) {
541 priv->expanded = TRUE;
542 osd_source_reallocate(osd);
543
544 priv->count = 1000;
545 priv->shift = osd->widget->allocation.width - OSD_S_W;
546 priv->dir = -1000/OSD_HZ;
547 } else {
548 priv->count = 0;
549 priv->shift = osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
550 priv->dir = +1000/OSD_HZ;
551 }
552
553 priv->handler_id = gtk_timeout_add(OSD_TIME/OSD_HZ, osd_source_animate, osd);
554 }
555
556 static osd_button_t
557 osd_source_check(osm_gps_map_osd_t *osd, gint x, gint y) {
558 osd_priv_t *priv = (osd_priv_t*)osd->priv;
559
560 if(!priv->expanded)
561 x -= osd->widget->allocation.width - OSD_S_W;
562 else
563 x -= osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
564
565 if(OSD_S_Y > 0)
566 y -= OSD_S_Y;
567 else
568 y -= osd->widget->allocation.height - OSD_S_PH + OSD_S_Y;
569
570 /* within square around puller? */
571 if(y > 0 && y < OSD_S_PH && x > 0 && x < OSD_S_PW) {
572 /* really within puller shape? */
573 if(x > Z_RAD || osm_gps_map_in_circle(x, y, Z_RAD, Z_RAD, Z_RAD)) {
574 /* expand source selector */
575 osd_source_toggle(osd);
576
577 /* tell upper layers that user clicked some background element */
578 /* of the OSD */
579 return OSD_BG;
580 }
581 }
582
583 /* check for clicks into data area */
584 if(priv->expanded) {
585 if(x > OSD_S_PW &&
586 x < OSD_S_PW + OSD_S_EXP_W &&
587 y > 0 &&
588 y < OSD_S_EXP_H) {
589
590 printf("in OSD source menu area\n");
591
592 return OSD_BG;
593 }
594 }
595
596 return OSD_NONE;
597 }
598 #endif // OSD_SOURCE_SEL
599
600 static osd_button_t
601 osd_check(osm_gps_map_osd_t *osd, gint x, gint y) {
602 osd_button_t but = OSD_NONE;
603
604 #ifdef OSD_SOURCE_SEL
605 /* the source selection area is handles internally */
606 but = osd_source_check(osd, x, y);
607 if(but != OSD_NONE)
608 return but;
609 #endif
610
611 x -= OSD_X;
612 y -= OSD_Y;
613
614 if(OSD_X < 0)
615 x -= (osd->widget->allocation.width - OSD_W);
616
617 if(OSD_Y < 0)
618 y -= (osd->widget->allocation.height - OSD_H);
619
620 /* first do a rough test for the OSD area. */
621 /* this is just to avoid an unnecessary detailed test */
622 if(x > 0 && x < OSD_W && y > 0 && y < OSD_H) {
623 #ifndef OSD_NO_DPAD
624 but = osd_check_dpad(x, y);
625 #endif
626
627 if(but == OSD_NONE)
628 but = osd_check_zoom(x, y);
629 }
630
631 return but;
632 }
633
634 #ifndef OSD_NO_DPAD
635 static void
636 osd_dpad_labels(cairo_t *cr, gint x, gint y) {
637 /* move reference to dpad center */
638 x += D_RAD;
639 y += D_RAD;
640
641 const static gint offset[][3][2] = {
642 /* left arrow/triangle */
643 { { -D_TIP+D_LEN, -D_WID }, { -D_LEN, D_WID }, { +D_LEN, D_WID } },
644 /* right arrow/triangle */
645 { { +D_TIP-D_LEN, -D_WID }, { +D_LEN, D_WID }, { -D_LEN, D_WID } },
646 /* top arrow/triangle */
647 { { -D_WID, -D_TIP+D_LEN }, { D_WID, -D_LEN }, { D_WID, +D_LEN } },
648 /* bottom arrow/triangle */
649 { { -D_WID, +D_TIP-D_LEN }, { D_WID, +D_LEN }, { D_WID, -D_LEN } }
650 };
651
652 int i;
653 for(i=0;i<4;i++) {
654 cairo_move_to (cr, x + offset[i][0][0], y + offset[i][0][1]);
655 cairo_rel_line_to (cr, offset[i][1][0], offset[i][1][1]);
656 cairo_rel_line_to (cr, offset[i][2][0], offset[i][2][1]);
657 }
658 }
659 #endif
660
661 #ifdef OSD_GPS_BUTTON
662 /* draw the satellite dish icon in the center of the dpad */
663 #define GPS_V0 (D_RAD/7)
664 #define GPS_V1 (D_RAD/10)
665 #define GPS_V2 (D_RAD/5)
666
667 /* draw a satellite receiver dish */
668 /* this is either drawn in the center of the dpad (if present) */
669 /* or in the middle of the zoom area */
670 static void
671 osd_dpad_gps(cairo_t *cr, gint x, gint y) {
672 /* move reference to dpad center */
673 x += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD * 3;
674 y += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD + GPS_V0;
675
676 cairo_move_to (cr, x-GPS_V0, y+GPS_V0);
677 cairo_rel_line_to (cr, +GPS_V0, -GPS_V0);
678 cairo_rel_line_to (cr, +GPS_V0, +GPS_V0);
679 cairo_close_path (cr);
680
681 cairo_move_to (cr, x+GPS_V1-GPS_V2, y-2*GPS_V2);
682 cairo_curve_to (cr, x-GPS_V2, y, x+GPS_V1, y+GPS_V1, x+GPS_V1+GPS_V2, y);
683 cairo_close_path (cr);
684
685 x += GPS_V1;
686 cairo_move_to (cr, x, y-GPS_V2);
687 cairo_rel_line_to (cr, +GPS_V1, -GPS_V1);
688 }
689 #endif
690
691 #define Z_LEN (2*Z_RAD/3)
692
693 static void
694 osd_zoom_labels(cairo_t *cr, gint x, gint y) {
695 cairo_move_to (cr, x + Z_LEFT - Z_LEN, y + Z_MID);
696 cairo_line_to (cr, x + Z_LEFT + Z_LEN, y + Z_MID);
697
698 cairo_move_to (cr, x + Z_RIGHT, y + Z_MID - Z_LEN);
699 cairo_line_to (cr, x + Z_RIGHT, y + Z_MID + Z_LEN);
700 cairo_move_to (cr, x + Z_RIGHT - Z_LEN, y + Z_MID);
701 cairo_line_to (cr, x + Z_RIGHT + Z_LEN, y + Z_MID);
702 }
703
704 static void
705 osd_render(osm_gps_map_osd_t *osd) {
706 osd_priv_t *priv = (osd_priv_t*)osd->priv;
707
708 #ifndef OSD_COLOR
709 GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL];
710 GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL];
711 GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE];
712 #endif
713
714 /* first fill with transparency */
715 cairo_t *cr = cairo_create(priv->overlay);
716 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
717 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
718 cairo_paint(cr);
719 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
720
721 /* --------- draw zoom and dpad shape shadow ----------- */
722 #ifdef OSD_SHADOW_ENABLE
723 osd_zoom_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
724 osd_shape_shadow(cr);
725 #ifndef OSD_NO_DPAD
726 osd_dpad_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
727 osd_shape_shadow(cr);
728 #endif
729 #endif
730
731 /* --------- draw zoom and dpad shape ----------- */
732
733 osd_zoom_shape(cr, 1, 1);
734 #ifndef OSD_COLOR
735 osd_shape(cr, &bg, &fg);
736 #else
737 osd_shape(cr);
738 #endif
739 #ifndef OSD_NO_DPAD
740 osd_dpad_shape(cr, 1, 1);
741 #ifndef OSD_COLOR
742 osd_shape(cr, &bg, &fg);
743 #else
744 osd_shape(cr);
745 #endif
746 #endif
747
748 /* --------- draw zoom and dpad labels --------- */
749
750 #ifdef OSD_SHADOW_ENABLE
751 osd_labels_shadow(cr, Z_RAD/3, TRUE);
752 osd_zoom_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
753 #ifndef OSD_NO_DPAD
754 osd_dpad_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
755 #endif
756 cairo_stroke(cr);
757 #ifdef OSD_GPS_BUTTON
758 osd_labels_shadow(cr, Z_RAD/6, osd->cb != NULL);
759 osd_dpad_gps(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
760 cairo_stroke(cr);
761 #endif
762 #endif
763
764 #ifndef OSD_COLOR
765 osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
766 #else
767 osd_labels(cr, Z_RAD/3, TRUE);
768 #endif
769 osd_zoom_labels(cr, 1, 1);
770 #ifndef OSD_NO_DPAD
771 osd_dpad_labels(cr, 1, 1);
772 #endif
773 cairo_stroke(cr);
774
775 #ifndef OSD_COLOR
776 osd_labels(cr, Z_RAD/6, osd->cb != NULL, &fg, &da);
777 #else
778 osd_labels(cr, Z_RAD/6, osd->cb != NULL);
779 #endif
780 #ifdef OSD_GPS_BUTTON
781 osd_dpad_gps(cr, 1, 1);
782 #endif
783 cairo_stroke(cr);
784
785 cairo_destroy(cr);
786
787 #ifdef OSD_SOURCE_SEL
788 osd_render_source_sel(osd);
789 #endif
790 }
791
792 static void
793 osd_draw(osm_gps_map_osd_t *osd, GdkDrawable *drawable)
794 {
795 osd_priv_t *priv = (osd_priv_t*)osd->priv;
796
797 /* OSD itself uses some off-screen rendering, so check if the */
798 /* offscreen buffer is present and create it if not */
799 if(!priv->overlay) {
800 /* create overlay ... */
801 priv->overlay =
802 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W+2, OSD_H+2);
803
804 #ifdef OSD_SOURCE_SEL
805 /* the initial OSD state is alway not-expanded */
806 priv->map_source =
807 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
808 OSD_S_W+2, OSD_S_H+2);
809 #endif
810
811 /* ... and render it */
812 osd_render(osd);
813 }
814
815 // now draw this onto the original context
816 cairo_t *cr = gdk_cairo_create(drawable);
817
818 int x = OSD_X, y = OSD_Y;
819 if(OSD_X < 0)
820 x = osd->widget->allocation.width - OSD_W + OSD_X;
821
822 if(OSD_Y < 0)
823 y = osd->widget->allocation.height - OSD_H + OSD_Y;
824
825 cairo_set_source_surface(cr, priv->overlay, x, y);
826 cairo_paint(cr);
827
828 #ifdef OSD_SOURCE_SEL
829 if(!priv->handler_id) {
830 /* the OSD source selection is not being animated */
831 if(!priv->expanded)
832 x = osd->widget->allocation.width - OSD_S_W;
833 else
834 x = osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
835 } else
836 x = priv->shift;
837
838 y = OSD_S_Y;
839 if(OSD_S_Y < 0) {
840 if(!priv->expanded)
841 y = osd->widget->allocation.height - OSD_S_H + OSD_S_Y;
842 else
843 y = osd->widget->allocation.height - OSD_S_EXP_H + OSD_S_Y;
844 }
845
846 cairo_set_source_surface(cr, priv->map_source, x, y);
847 cairo_paint(cr);
848 #endif
849
850 cairo_destroy(cr);
851 }
852
853 static void
854 osd_free(osm_gps_map_osd_t *osd)
855 {
856 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
857
858 if (priv->overlay)
859 cairo_surface_destroy(priv->overlay);
860
861 #ifdef OSD_SOURCE_SEL
862 if(priv->handler_id)
863 gtk_timeout_remove(priv->handler_id);
864
865 if (priv->map_source)
866 cairo_surface_destroy(priv->map_source);
867 #endif
868
869 g_free(priv);
870 }
871
872 static gboolean
873 osd_busy(osm_gps_map_osd_t *osd)
874 {
875 #ifdef OSD_SOURCE_SEL
876 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
877 return (priv->handler_id != 0);
878 #else
879 return FALSE;
880 #endif
881 }
882
883 static osm_gps_map_osd_t osd_classic = {
884 .widget = NULL,
885
886 .draw = osd_draw,
887 .check = osd_check,
888 .render = osd_render,
889 .free = osd_free,
890 .busy = osd_busy,
891
892 .cb = NULL,
893 .data = NULL,
894
895 .priv = NULL
896 };
897
898 /* this is the only function that's externally visible */
899 void
900 osm_gps_map_osd_classic_init(OsmGpsMap *map)
901 {
902 osd_priv_t *priv = osd_classic.priv = g_new0(osd_priv_t, 1);
903
904 osd_classic.priv = priv;
905
906 osm_gps_map_register_osd(map, &osd_classic);
907 }
908
909 #ifdef OSD_GPS_BUTTON
910 /* below are osd specific functions which aren't used by osm-gps-map */
911 /* but instead are to be used by the main application */
912 void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdCallback cb,
913 gpointer data) {
914 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
915 g_return_if_fail (osd);
916
917 osd->cb = cb;
918 osd->data = data;
919
920 /* this may have changed the state of the gps button */
921 /* we thus re-render the overlay */
922 osd->render(osd);
923
924 osm_gps_map_redraw(map);
925 }
926 #endif
927
928 osd_button_t
929 osm_gps_map_osd_check(OsmGpsMap *map, gint x, gint y) {
930 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
931 g_return_val_if_fail (osd, OSD_NONE);
932
933 return osd_check(osd, x, y);
934 }