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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 89 - (show 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 /* -*- 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 /* re-allocate the buffer used to draw the menu. This is used */
435 /* to collapse/expand the buffer */
436 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 /* ... 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 w = OSD_S_EXP_W;
471 h = OSD_S_EXP_H;
472 }
473
474 cairo_surface_destroy(priv->map_source);
475 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 #define OSD_TIME 500
484
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 /* check if the user clicked inside the source selection area */
552 static osd_button_t
553 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
579 /* check for clicks into data area */
580 if(priv->expanded && !priv->handler_id) {
581 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 int step = (priv->height - 2*OSD_TEXT_BORDER)
587 / OSM_GPS_MAP_SOURCE_LAST;
588
589 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 return OSD_BG;
608 }
609 }
610
611 return OSD_NONE;
612 }
613 #endif // OSD_SOURCE_SEL
614
615 static osd_button_t
616 osd_check(osm_gps_map_osd_t *osd, gint x, gint y) {
617 osd_button_t but = OSD_NONE;
618
619 #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 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 /* first do a rough test for the OSD area. */
636 /* this is just to avoid an unnecessary detailed test */
637 if(x > 0 && x < OSD_W && y > 0 && y < OSD_H) {
638 #ifndef OSD_NO_DPAD
639 but = osd_check_dpad(x, y);
640 #endif
641
642 if(but == OSD_NONE)
643 but = osd_check_zoom(x, y);
644 }
645
646 return but;
647 }
648
649 #ifndef OSD_NO_DPAD
650 static void
651 osd_dpad_labels(cairo_t *cr, gint x, gint y) {
652 /* 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 #endif
675
676 #ifdef OSD_GPS_BUTTON
677 /* draw the satellite dish icon in the center of the dpad */
678 #define GPS_V0 (D_RAD/7)
679 #define GPS_V1 (D_RAD/10)
680 #define GPS_V2 (D_RAD/5)
681
682 /* draw a satellite receiver dish */
683 /* this is either drawn in the center of the dpad (if present) */
684 /* or in the middle of the zoom area */
685 static void
686 osd_dpad_gps(cairo_t *cr, gint x, gint y) {
687 /* move reference to dpad center */
688 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
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 #endif
705
706 #define Z_LEN (2*Z_RAD/3)
707
708 static void
709 osd_zoom_labels(cairo_t *cr, gint x, gint y) {
710 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 osd_render(osm_gps_map_osd_t *osd) {
721 osd_priv_t *priv = (osd_priv_t*)osd->priv;
722
723 #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 #endif
728
729 /* first fill with transparency */
730 cairo_t *cr = cairo_create(priv->overlay);
731 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 #ifdef OSD_SHADOW_ENABLE
738 osd_zoom_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
739 osd_shape_shadow(cr);
740 #ifndef OSD_NO_DPAD
741 osd_dpad_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
742 osd_shape_shadow(cr);
743 #endif
744 #endif
745
746 /* --------- draw zoom and dpad shape ----------- */
747
748 osd_zoom_shape(cr, 1, 1);
749 #ifndef OSD_COLOR
750 osd_shape(cr, &bg, &fg);
751 #else
752 osd_shape(cr);
753 #endif
754 #ifndef OSD_NO_DPAD
755 osd_dpad_shape(cr, 1, 1);
756 #ifndef OSD_COLOR
757 osd_shape(cr, &bg, &fg);
758 #else
759 osd_shape(cr);
760 #endif
761 #endif
762
763 /* --------- draw zoom and dpad labels --------- */
764
765 #ifdef OSD_SHADOW_ENABLE
766 osd_labels_shadow(cr, Z_RAD/3, TRUE);
767 osd_zoom_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
768 #ifndef OSD_NO_DPAD
769 osd_dpad_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
770 #endif
771 cairo_stroke(cr);
772 #ifdef OSD_GPS_BUTTON
773 osd_labels_shadow(cr, Z_RAD/6, osd->cb != NULL);
774 osd_dpad_gps(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
775 cairo_stroke(cr);
776 #endif
777 #endif
778
779 #ifndef OSD_COLOR
780 osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
781 #else
782 osd_labels(cr, Z_RAD/3, TRUE);
783 #endif
784 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 #ifndef OSD_COLOR
791 osd_labels(cr, Z_RAD/6, osd->cb != NULL, &fg, &da);
792 #else
793 osd_labels(cr, Z_RAD/6, osd->cb != NULL);
794 #endif
795 #ifdef OSD_GPS_BUTTON
796 osd_dpad_gps(cr, 1, 1);
797 #endif
798 cairo_stroke(cr);
799
800 cairo_destroy(cr);
801
802 #ifdef OSD_SOURCE_SEL
803 osd_render_source_sel(osd);
804 #endif
805 }
806
807 static void
808 osd_draw(osm_gps_map_osd_t *osd, GdkDrawable *drawable)
809 {
810 osd_priv_t *priv = (osd_priv_t*)osd->priv;
811
812 /* OSD itself uses some off-screen rendering, so check if the */
813 /* offscreen buffer is present and create it if not */
814 if(!priv->overlay) {
815 /* create overlay ... */
816 priv->overlay =
817 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W+2, OSD_H+2);
818
819 #ifdef OSD_SOURCE_SEL
820 /* 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 #endif
825
826 /* ... and render it */
827 osd_render(osd);
828 }
829
830 // now draw this onto the original context
831 cairo_t *cr = gdk_cairo_create(drawable);
832
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 cairo_paint(cr);
842
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 cairo_destroy(cr);
866 }
867
868 static void
869 osd_free(osm_gps_map_osd_t *osd)
870 {
871 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
872
873 if (priv->overlay)
874 cairo_surface_destroy(priv->overlay);
875
876 #ifdef OSD_SOURCE_SEL
877 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 #endif
883
884 g_free(priv);
885 }
886
887 static gboolean
888 osd_busy(osm_gps_map_osd_t *osd)
889 {
890 #ifdef OSD_SOURCE_SEL
891 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
892 return (priv->handler_id != 0);
893 #else
894 return FALSE;
895 #endif
896 }
897
898 static osm_gps_map_osd_t osd_classic = {
899 .widget = NULL,
900
901 .draw = osd_draw,
902 .check = osd_check,
903 .render = osd_render,
904 .free = osd_free,
905 .busy = osd_busy,
906
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 #ifdef OSD_GPS_BUTTON
925 /* 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 void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdCallback cb,
928 gpointer data) {
929 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 /* this may have changed the state of the gps button */
936 /* we thus re-render the overlay */
937 osd->render(osd);
938
939 osm_gps_map_redraw(map);
940 }
941 #endif
942
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 }