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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 108 - (show annotations)
Fri Sep 11 19:20:55 2009 UTC (14 years, 8 months ago) by harbaum
File MIME type: text/plain
File size: 40965 byte(s)
Coordinate OSD finished
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 struct {
41 cairo_surface_t *surface;
42 gboolean rendered;
43 #ifdef OSD_GPS_BUTTON
44 gboolean gps_enabled;
45 #endif
46 } controls;
47
48 #ifdef OSD_SCALE
49 cairo_surface_t *scale;
50 int scale_zoom;
51 #endif
52
53 #ifdef OSD_CROSSHAIR
54 cairo_surface_t *crosshair;
55 #endif
56
57 #ifdef OSD_COORDINATES
58 cairo_surface_t *coordinates;
59 float coo_lat, coo_lon;
60 #endif
61
62 #ifdef OSD_SOURCE_SEL
63 /* values to handle the "source" menu */
64 cairo_surface_t *map_source;
65 gboolean expanded;
66 gint shift, dir, count;
67 gint handler_id;
68 gint width, height;
69 #endif
70
71 } osd_priv_t;
72
73 /* position and extent of bounding box */
74 #ifndef OSD_X
75 #define OSD_X (10)
76 #endif
77
78 #ifndef OSD_Y
79 #define OSD_Y (10)
80 #endif
81
82 /* parameters of the direction shape */
83 #ifndef OSD_DIAMETER
84 #define D_RAD (30) // diameter of dpad
85 #else
86 #define D_RAD (OSD_DIAMETER)
87 #endif
88 #define D_TIP (4*D_RAD/5) // distance of arrow tip from dpad center
89 #define D_LEN (D_RAD/4) // length of arrow
90 #define D_WID (D_LEN) // width of arrow
91
92 /* parameters of the "zoom" pad */
93 #define Z_STEP (D_RAD/4) // distance between dpad and zoom
94 #define Z_RAD (D_RAD/2) // radius of "caps" of zoom bar
95
96 #ifdef OSD_SHADOW_ENABLE
97 /* shadow also depends on control size */
98 #define OSD_SHADOW (D_RAD/6)
99 #else
100 #define OSD_SHADOW (0)
101 #endif
102
103 /* normally the GPS button is in the center of the dpad. if there's */
104 /* no dpad it will go into the zoom area */
105 #if defined(OSD_GPS_BUTTON) && defined(OSD_NO_DPAD)
106 #define Z_GPS 1
107 #else
108 #define Z_GPS 0
109 #endif
110
111 /* total width and height of controls incl. shadow */
112 #define OSD_W (2*D_RAD + OSD_SHADOW + Z_GPS * 2 * Z_RAD)
113 #if !Z_GPS
114 #define OSD_H (2*D_RAD + Z_STEP + 2*Z_RAD + OSD_SHADOW)
115 #else
116 #define OSD_H (2*Z_RAD + OSD_SHADOW)
117 #endif
118
119 #ifdef OSD_SHADOW_ENABLE
120 #define OSD_LBL_SHADOW (OSD_SHADOW/2)
121 #endif
122
123 #define Z_TOP ((1-Z_GPS) * (2 * D_RAD + Z_STEP))
124
125 #define Z_MID (Z_TOP + Z_RAD)
126 #define Z_BOT (Z_MID + Z_RAD)
127 #define Z_LEFT (Z_RAD)
128 #define Z_RIGHT (2 * D_RAD - Z_RAD + Z_GPS * 2 * Z_RAD)
129 #define Z_CENTER ((Z_RIGHT + Z_LEFT)/2)
130
131 /* create the cairo shape used for the zoom buttons */
132 static void
133 osd_zoom_shape(cairo_t *cr, gint x, gint y)
134 {
135 cairo_move_to (cr, x+Z_LEFT, y+Z_TOP);
136 cairo_line_to (cr, x+Z_RIGHT, y+Z_TOP);
137 cairo_arc (cr, x+Z_RIGHT, y+Z_MID, Z_RAD, -M_PI/2, M_PI/2);
138 cairo_line_to (cr, x+Z_LEFT, y+Z_BOT);
139 cairo_arc (cr, x+Z_LEFT, y+Z_MID, Z_RAD, M_PI/2, -M_PI/2);
140 }
141
142 /* ------------------- color/shadow functions ----------------- */
143
144 #ifndef OSD_COLOR
145 /* if no color has been specified we just use the gdks default colors */
146 static void
147 osd_labels(cairo_t *cr, gint width, gboolean enabled,
148 GdkColor *fg, GdkColor *disabled) {
149 if(enabled) gdk_cairo_set_source_color(cr, fg);
150 else gdk_cairo_set_source_color(cr, disabled);
151 cairo_set_line_width (cr, width);
152 }
153 #else
154 static void
155 osd_labels(cairo_t *cr, gint width, gboolean enabled) {
156 if(enabled) cairo_set_source_rgb (cr, OSD_COLOR);
157 else cairo_set_source_rgb (cr, OSD_COLOR_DISABLED);
158 cairo_set_line_width (cr, width);
159 }
160 #endif
161
162 #ifdef OSD_SHADOW_ENABLE
163 static void
164 osd_labels_shadow(cairo_t *cr, gint width, gboolean enabled) {
165 cairo_set_source_rgba (cr, 0, 0, 0, enabled?0.3:0.15);
166 cairo_set_line_width (cr, width);
167 }
168 #endif
169
170 #ifndef OSD_NO_DPAD
171 /* create the cairo shape used for the dpad */
172 static void
173 osd_dpad_shape(cairo_t *cr, gint x, gint y)
174 {
175 cairo_arc (cr, x+D_RAD, y+D_RAD, D_RAD, 0, 2 * M_PI);
176 }
177 #endif
178
179 #ifdef OSD_SHADOW_ENABLE
180 static void
181 osd_shape_shadow(cairo_t *cr) {
182 cairo_set_source_rgba (cr, 0, 0, 0, 0.2);
183 cairo_fill (cr);
184 cairo_stroke (cr);
185 }
186 #endif
187
188 #ifndef OSD_COLOR
189 /* if no color has been specified we just use the gdks default colors */
190 static void
191 osd_shape(cairo_t *cr, GdkColor *bg, GdkColor *fg) {
192 gdk_cairo_set_source_color(cr, bg);
193 cairo_fill_preserve (cr);
194 gdk_cairo_set_source_color(cr, fg);
195 cairo_set_line_width (cr, 1);
196 cairo_stroke (cr);
197 }
198 #else
199 static void
200 osd_shape(cairo_t *cr) {
201 cairo_set_source_rgb (cr, OSD_COLOR_BG);
202 cairo_fill_preserve (cr);
203 cairo_set_source_rgb (cr, OSD_COLOR);
204 cairo_set_line_width (cr, 1);
205 cairo_stroke (cr);
206 }
207 #endif
208
209
210 static gboolean
211 osm_gps_map_in_circle(gint x, gint y, gint cx, gint cy, gint rad)
212 {
213 return( pow(cx - x, 2) + pow(cy - y, 2) < rad * rad);
214 }
215
216 #ifndef OSD_NO_DPAD
217 /* check whether x/y is within the dpad */
218 static osd_button_t
219 osd_check_dpad(gint x, gint y)
220 {
221 /* within entire dpad circle */
222 if( osm_gps_map_in_circle(x, y, D_RAD, D_RAD, D_RAD))
223 {
224 /* convert into position relative to dpads centre */
225 x -= D_RAD;
226 y -= D_RAD;
227
228 #ifdef OSD_GPS_BUTTON
229 /* check for dpad center goes here! */
230 if( osm_gps_map_in_circle(x, y, 0, 0, D_RAD/3))
231 return OSD_GPS;
232 #endif
233
234 if( y < 0 && abs(x) < abs(y))
235 return OSD_UP;
236
237 if( y > 0 && abs(x) < abs(y))
238 return OSD_DOWN;
239
240 if( x < 0 && abs(y) < abs(x))
241 return OSD_LEFT;
242
243 if( x > 0 && abs(y) < abs(x))
244 return OSD_RIGHT;
245
246 return OSD_BG;
247 }
248 return OSD_NONE;
249 }
250 #endif
251
252 /* check whether x/y is within the zoom pads */
253 static osd_button_t
254 osd_check_zoom(gint x, gint y) {
255 if( x > 0 && x < OSD_W && y > Z_TOP && y < Z_BOT) {
256
257 /* within circle around (-) label */
258 if( osm_gps_map_in_circle(x, y, Z_LEFT, Z_MID, Z_RAD))
259 return OSD_OUT;
260
261 /* within circle around (+) label */
262 if( osm_gps_map_in_circle(x, y, Z_RIGHT, Z_MID, Z_RAD))
263 return OSD_IN;
264
265 #if Z_GPS == 1
266 /* within square around center */
267 if( x > Z_CENTER - Z_RAD && x < Z_CENTER + Z_RAD)
268 return OSD_GPS;
269 #endif
270
271 /* between center of (-) button and center of entire zoom control area */
272 if(x > OSD_LEFT && x < D_RAD)
273 return OSD_OUT;
274
275 /* between center of (+) button and center of entire zoom control area */
276 if(x < OSD_RIGHT && x > D_RAD)
277 return OSD_IN;
278 }
279
280 return OSD_NONE;
281 }
282
283 #ifdef OSD_SOURCE_SEL
284
285 /* place source selection at right border */
286 #define OSD_S_RAD (Z_RAD)
287 #define OSD_S_X (-OSD_X)
288 #define OSD_S_Y (OSD_Y)
289 #define OSD_S_PW (2 * Z_RAD)
290 #define OSD_S_W (OSD_S_PW)
291 #define OSD_S_PH (2 * Z_RAD)
292 #define OSD_S_H (OSD_S_PH + OSD_SHADOW)
293
294 /* size of usable area when expanded */
295 #define OSD_S_AREA_W (priv->width)
296 #define OSD_S_AREA_H (priv->height)
297 #define OSD_S_EXP_W (OSD_S_PW + OSD_S_AREA_W + OSD_SHADOW)
298 #define OSD_S_EXP_H (OSD_S_AREA_H + OSD_SHADOW)
299
300 /* internal value to draw the arrow on the "puller" */
301 #define OSD_S_D0 (OSD_S_RAD/2)
302 #ifndef OSD_FONT_SIZE
303 #define OSD_FONT_SIZE 16.0
304 #endif
305 #define OSD_TEXT_BORDER (OSD_FONT_SIZE/2)
306 #define OSD_TEXT_SKIP (OSD_FONT_SIZE/8)
307
308 /* draw the shape of the source selection OSD, either only the puller (not expanded) */
309 /* or the entire menu incl. the puller (expanded) */
310 static void
311 osd_source_shape(osd_priv_t *priv, cairo_t *cr, gint x, gint y) {
312 if(!priv->expanded) {
313 /* just draw the puller */
314 cairo_move_to (cr, x + OSD_S_PW, y + OSD_S_PH);
315 cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2);
316 cairo_line_to (cr, x + OSD_S_PW, y);
317 } else {
318 /* draw the puller and the area itself */
319 cairo_move_to (cr, x + OSD_S_PW + OSD_S_AREA_W, y + OSD_S_AREA_H);
320 cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_AREA_H);
321 if(OSD_S_Y > 0) {
322 cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_PH);
323 cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2);
324 } else {
325 cairo_arc (cr, x+OSD_S_RAD, y+OSD_S_AREA_H-OSD_S_RAD, OSD_S_RAD, M_PI/2, -M_PI/2);
326 cairo_line_to (cr, x + OSD_S_PW, y + OSD_S_AREA_H - OSD_S_PH);
327 cairo_line_to (cr, x + OSD_S_PW, y);
328 }
329 cairo_line_to (cr, x + OSD_S_PW + OSD_S_AREA_W, y);
330 cairo_close_path (cr);
331 }
332 }
333
334 static void
335 osd_source_content(osm_gps_map_osd_t *osd, cairo_t *cr, gint offset) {
336 osd_priv_t *priv = (osd_priv_t*)osd->priv;
337
338 int py = offset + OSD_S_RAD - OSD_S_D0;
339
340 if(!priv->expanded) {
341 /* draw the "puller" open (<) arrow */
342 cairo_move_to (cr, offset + OSD_S_RAD + OSD_S_D0/2, py);
343 cairo_rel_line_to (cr, -OSD_S_D0, +OSD_S_D0);
344 cairo_rel_line_to (cr, +OSD_S_D0, +OSD_S_D0);
345 } else {
346 if(OSD_S_Y < 0)
347 py += OSD_S_AREA_H - OSD_S_PH;
348
349 /* draw the "puller" close (>) arrow */
350 cairo_move_to (cr, offset + OSD_S_RAD - OSD_S_D0/2, py);
351 cairo_rel_line_to (cr, +OSD_S_D0, +OSD_S_D0);
352 cairo_rel_line_to (cr, -OSD_S_D0, +OSD_S_D0);
353 cairo_stroke(cr);
354
355 /* don't draw a shadow for the text content */
356 if(offset == 1) {
357 gint source;
358 g_object_get(osd->widget, "map-source", &source, NULL);
359
360 cairo_select_font_face (cr, "Sans",
361 CAIRO_FONT_SLANT_NORMAL,
362 CAIRO_FONT_WEIGHT_BOLD);
363 cairo_set_font_size (cr, OSD_FONT_SIZE);
364
365 int i, step = (priv->height - 2*OSD_TEXT_BORDER) /
366 OSM_GPS_MAP_SOURCE_LAST;
367 for(i=OSM_GPS_MAP_SOURCE_NULL+1;i<=OSM_GPS_MAP_SOURCE_LAST;i++) {
368 cairo_text_extents_t extents;
369 const char *src = osm_gps_map_source_get_friendly_name(i);
370 cairo_text_extents (cr, src, &extents);
371
372 int x = offset + OSD_S_PW + OSD_TEXT_BORDER;
373 int y = offset + step * (i-1) + OSD_TEXT_BORDER;
374
375 /* draw filled rectangle if selected */
376 if(source == i) {
377 cairo_rectangle(cr, x - OSD_TEXT_BORDER/2,
378 y - OSD_TEXT_SKIP,
379 priv->width - OSD_TEXT_BORDER,
380 step + OSD_TEXT_SKIP);
381 cairo_fill(cr);
382
383 /* temprarily draw with background color */
384 #ifndef OSD_COLOR
385 GdkColor bg = osd->widget->style->bg[GTK_STATE_NORMAL];
386 gdk_cairo_set_source_color(cr, &bg);
387 #else
388 cairo_set_source_rgb (cr, OSD_COLOR_BG);
389 #endif
390 }
391
392 cairo_move_to (cr, x, y + OSD_TEXT_SKIP - extents.y_bearing);
393 cairo_show_text (cr, src);
394
395 /* restore color */
396 if(source == i) {
397 #ifndef OSD_COLOR
398 GdkColor fg = osd->widget->style->fg[GTK_STATE_NORMAL];
399 gdk_cairo_set_source_color(cr, &fg);
400 #else
401 cairo_set_source_rgb (cr, OSD_COLOR);
402 #endif
403 }
404 }
405 }
406 }
407 }
408
409 static void
410 osd_render_source_sel(osm_gps_map_osd_t *osd) {
411 osd_priv_t *priv = (osd_priv_t*)osd->priv;
412
413 #ifndef OSD_COLOR
414 GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL];
415 GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL];
416 GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE];
417 #endif
418
419 /* draw source selector */
420 cairo_t *cr = cairo_create(priv->map_source);
421 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
422 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
423 cairo_paint(cr);
424 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
425
426 #ifdef OSD_SHADOW_ENABLE
427 osd_source_shape(priv, cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
428 osd_shape_shadow(cr);
429 #endif
430
431 osd_source_shape(priv, cr, 1, 1);
432 #ifndef OSD_COLOR
433 osd_shape(cr, &bg, &fg);
434 #else
435 osd_shape(cr);
436 #endif
437
438 #ifdef OSD_SHADOW_ENABLE
439 osd_labels_shadow(cr, Z_RAD/3, TRUE);
440 osd_source_content(osd, cr, 1+OSD_LBL_SHADOW);
441 cairo_stroke (cr);
442 #endif
443 #ifndef OSD_COLOR
444 osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
445 #else
446 osd_labels(cr, Z_RAD/3, TRUE);
447 #endif
448 osd_source_content(osd, cr, 1);
449 cairo_stroke (cr);
450
451 cairo_destroy(cr);
452 }
453
454 /* re-allocate the buffer used to draw the menu. This is used */
455 /* to collapse/expand the buffer */
456 static void
457 osd_source_reallocate(osm_gps_map_osd_t *osd) {
458 osd_priv_t *priv = (osd_priv_t*)osd->priv;
459
460 /* re-allocate offscreen bitmap */
461 g_assert (priv->map_source);
462
463 int w = OSD_S_W, h = OSD_S_H;
464 if(priv->expanded) {
465 cairo_text_extents_t extents;
466
467 /* determine content size */
468 cairo_t *cr = cairo_create(priv->map_source);
469 cairo_select_font_face (cr, "Sans",
470 CAIRO_FONT_SLANT_NORMAL,
471 CAIRO_FONT_WEIGHT_BOLD);
472 cairo_set_font_size (cr, OSD_FONT_SIZE);
473
474 /* calculate menu size */
475 int i, max_h = 0, max_w = 0;
476 for(i=OSM_GPS_MAP_SOURCE_NULL+1;i<=OSM_GPS_MAP_SOURCE_LAST;i++) {
477 const char *src = osm_gps_map_source_get_friendly_name(i);
478 cairo_text_extents (cr, src, &extents);
479
480 if(extents.width > max_w) max_w = extents.width;
481 if(extents.height > max_h) max_h = extents.height;
482 }
483 cairo_destroy(cr);
484
485 priv->width = max_w + 2*OSD_TEXT_BORDER;
486 priv->height = OSM_GPS_MAP_SOURCE_LAST *
487 (max_h + 2*OSD_TEXT_SKIP) + 2*OSD_TEXT_BORDER;
488
489 w = OSD_S_EXP_W;
490 h = OSD_S_EXP_H;
491 }
492
493 cairo_surface_destroy(priv->map_source);
494 priv->map_source =
495 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w+2, h+2);
496
497 osd_render_source_sel(osd);
498 }
499
500 #define OSD_HZ 15
501 #define OSD_TIME 500
502
503 static gboolean osd_source_animate(gpointer data) {
504 osm_gps_map_osd_t *osd = (osm_gps_map_osd_t*)data;
505 osd_priv_t *priv = (osd_priv_t*)osd->priv;
506 int diff = OSD_S_EXP_W - OSD_S_W - OSD_S_X;
507 gboolean done = FALSE;
508 priv->count += priv->dir;
509
510 /* shifting in */
511 if(priv->dir < 0) {
512 if(priv->count <= 0) {
513 priv->count = 0;
514 done = TRUE;
515 }
516 } else {
517 if(priv->count >= 1000) {
518 priv->expanded = FALSE;
519 osd_source_reallocate(osd);
520
521 priv->count = 1000;
522 done = TRUE;
523 }
524 }
525
526
527 /* count runs linearly from 0 to 1000, map this nicely onto a position */
528
529 /* nicer sinoid mapping */
530 float m = 0.5-cos(priv->count * M_PI / 1000.0)/2;
531 priv->shift = (osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X) +
532 m * diff;
533
534 osm_gps_map_repaint(OSM_GPS_MAP(osd->widget));
535
536 if(done)
537 priv->handler_id = 0;
538
539 return !done;
540 }
541
542 /* switch between expand and collapse mode of source selection */
543 static void
544 osd_source_toggle(osm_gps_map_osd_t *osd)
545 {
546 osd_priv_t *priv = (osd_priv_t*)osd->priv;
547
548 /* ignore clicks while animation is running */
549 if(priv->handler_id)
550 return;
551
552 /* expand immediately, collapse is handle at the end of the collapse animation */
553 if(!priv->expanded) {
554 priv->expanded = TRUE;
555 osd_source_reallocate(osd);
556
557 priv->count = 1000;
558 priv->shift = osd->widget->allocation.width - OSD_S_W;
559 priv->dir = -1000/OSD_HZ;
560 } else {
561 priv->count = 0;
562 priv->shift = osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
563 priv->dir = +1000/OSD_HZ;
564 }
565
566 priv->handler_id = gtk_timeout_add(OSD_TIME/OSD_HZ, osd_source_animate, osd);
567 }
568
569 /* check if the user clicked inside the source selection area */
570 static osd_button_t
571 osd_source_check(osm_gps_map_osd_t *osd, gint x, gint y) {
572 osd_priv_t *priv = (osd_priv_t*)osd->priv;
573
574 if(!priv->expanded)
575 x -= osd->widget->allocation.width - OSD_S_W;
576 else
577 x -= osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
578
579 if(OSD_S_Y > 0)
580 y -= OSD_S_Y;
581 else
582 y -= osd->widget->allocation.height - OSD_S_PH + OSD_S_Y;
583
584 /* within square around puller? */
585 if(y > 0 && y < OSD_S_PH && x > 0 && x < OSD_S_PW) {
586 /* really within puller shape? */
587 if(x > Z_RAD || osm_gps_map_in_circle(x, y, Z_RAD, Z_RAD, Z_RAD)) {
588 /* expand source selector */
589 osd_source_toggle(osd);
590
591 /* tell upper layers that user clicked some background element */
592 /* of the OSD */
593 return OSD_BG;
594 }
595 }
596
597 /* check for clicks into data area */
598 if(priv->expanded && !priv->handler_id) {
599 /* re-adjust from puller top to content top */
600 if(OSD_S_Y < 0)
601 y += OSD_S_EXP_H - OSD_S_PH;
602
603 if(x > OSD_S_PW &&
604 x < OSD_S_PW + OSD_S_EXP_W &&
605 y > 0 &&
606 y < OSD_S_EXP_H) {
607
608 int step = (priv->height - 2*OSD_TEXT_BORDER)
609 / OSM_GPS_MAP_SOURCE_LAST;
610
611 y -= OSD_TEXT_BORDER - OSD_TEXT_SKIP;
612 y /= step;
613 y += 1;
614
615 gint old = 0;
616 g_object_get(osd->widget, "map-source", &old, NULL);
617
618 if(y > OSM_GPS_MAP_SOURCE_NULL &&
619 y <= OSM_GPS_MAP_SOURCE_LAST &&
620 old != y) {
621 g_object_set(osd->widget, "map-source", y, NULL);
622
623 osd_render_source_sel(osd);
624 osm_gps_map_repaint(OSM_GPS_MAP(osd->widget));
625 }
626
627 /* return "clicked in OSD background" to prevent further */
628 /* processing by application */
629 return OSD_BG;
630 }
631 }
632
633 return OSD_NONE;
634 }
635 #endif // OSD_SOURCE_SEL
636
637 static osd_button_t
638 osd_check(osm_gps_map_osd_t *osd, gint x, gint y) {
639 osd_button_t but = OSD_NONE;
640
641 #ifdef OSD_SOURCE_SEL
642 /* the source selection area is handles internally */
643 but = osd_source_check(osd, x, y);
644 if(but != OSD_NONE)
645 return but;
646 #endif
647
648 x -= OSD_X;
649 y -= OSD_Y;
650
651 if(OSD_X < 0)
652 x -= (osd->widget->allocation.width - OSD_W);
653
654 if(OSD_Y < 0)
655 y -= (osd->widget->allocation.height - OSD_H);
656
657 /* first do a rough test for the OSD area. */
658 /* this is just to avoid an unnecessary detailed test */
659 if(x > 0 && x < OSD_W && y > 0 && y < OSD_H) {
660 #ifndef OSD_NO_DPAD
661 but = osd_check_dpad(x, y);
662 #endif
663
664 if(but == OSD_NONE)
665 but = osd_check_zoom(x, y);
666 }
667
668 return but;
669 }
670
671 #ifndef OSD_NO_DPAD
672 static void
673 osd_dpad_labels(cairo_t *cr, gint x, gint y) {
674 /* move reference to dpad center */
675 x += D_RAD;
676 y += D_RAD;
677
678 const static gint offset[][3][2] = {
679 /* left arrow/triangle */
680 { { -D_TIP+D_LEN, -D_WID }, { -D_LEN, D_WID }, { +D_LEN, D_WID } },
681 /* right arrow/triangle */
682 { { +D_TIP-D_LEN, -D_WID }, { +D_LEN, D_WID }, { -D_LEN, D_WID } },
683 /* top arrow/triangle */
684 { { -D_WID, -D_TIP+D_LEN }, { D_WID, -D_LEN }, { D_WID, +D_LEN } },
685 /* bottom arrow/triangle */
686 { { -D_WID, +D_TIP-D_LEN }, { D_WID, +D_LEN }, { D_WID, -D_LEN } }
687 };
688
689 int i;
690 for(i=0;i<4;i++) {
691 cairo_move_to (cr, x + offset[i][0][0], y + offset[i][0][1]);
692 cairo_rel_line_to (cr, offset[i][1][0], offset[i][1][1]);
693 cairo_rel_line_to (cr, offset[i][2][0], offset[i][2][1]);
694 }
695 }
696 #endif
697
698 #ifdef OSD_GPS_BUTTON
699 /* draw the satellite dish icon in the center of the dpad */
700 #define GPS_V0 (D_RAD/7)
701 #define GPS_V1 (D_RAD/10)
702 #define GPS_V2 (D_RAD/5)
703
704 /* draw a satellite receiver dish */
705 /* this is either drawn in the center of the dpad (if present) */
706 /* or in the middle of the zoom area */
707 static void
708 osd_dpad_gps(cairo_t *cr, gint x, gint y) {
709 /* move reference to dpad center */
710 x += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD * 3;
711 y += (1-Z_GPS) * D_RAD + Z_GPS * Z_RAD + GPS_V0;
712
713 cairo_move_to (cr, x-GPS_V0, y+GPS_V0);
714 cairo_rel_line_to (cr, +GPS_V0, -GPS_V0);
715 cairo_rel_line_to (cr, +GPS_V0, +GPS_V0);
716 cairo_close_path (cr);
717
718 cairo_move_to (cr, x+GPS_V1-GPS_V2, y-2*GPS_V2);
719 cairo_curve_to (cr, x-GPS_V2, y, x+GPS_V1, y+GPS_V1, x+GPS_V1+GPS_V2, y);
720 cairo_close_path (cr);
721
722 x += GPS_V1;
723 cairo_move_to (cr, x, y-GPS_V2);
724 cairo_rel_line_to (cr, +GPS_V1, -GPS_V1);
725 }
726 #endif
727
728 #define Z_LEN (2*Z_RAD/3)
729
730 static void
731 osd_zoom_labels(cairo_t *cr, gint x, gint y) {
732 cairo_move_to (cr, x + Z_LEFT - Z_LEN, y + Z_MID);
733 cairo_line_to (cr, x + Z_LEFT + Z_LEN, y + Z_MID);
734
735 cairo_move_to (cr, x + Z_RIGHT, y + Z_MID - Z_LEN);
736 cairo_line_to (cr, x + Z_RIGHT, y + Z_MID + Z_LEN);
737 cairo_move_to (cr, x + Z_RIGHT - Z_LEN, y + Z_MID);
738 cairo_line_to (cr, x + Z_RIGHT + Z_LEN, y + Z_MID);
739 }
740
741 #ifdef OSD_COORDINATES
742
743 #ifndef OSD_COORDINATES_FONT_SIZE
744 #define OSD_COORDINATES_FONT_SIZE 12
745 #endif
746
747 #define OSD_COORDINATES_OFFSET (OSD_COORDINATES_FONT_SIZE/6)
748
749 #define OSD_COORDINATES_W (8*OSD_COORDINATES_FONT_SIZE+2*OSD_COORDINATES_OFFSET)
750 #define OSD_COORDINATES_H (2*OSD_COORDINATES_FONT_SIZE+OSD_COORDINATES_OFFSET)
751
752 /* these can be overwritten with versions that support */
753 /* localization */
754 #ifndef OSD_COORDINATES_CHR_N
755 #define OSD_COORDINATES_CHR_N "N"
756 #endif
757 #ifndef OSD_COORDINATES_CHR_S
758 #define OSD_COORDINATES_CHR_S "S"
759 #endif
760 #ifndef OSD_COORDINATES_CHR_E
761 #define OSD_COORDINATES_CHR_E "E"
762 #endif
763 #ifndef OSD_COORDINATES_CHR_W
764 #define OSD_COORDINATES_CHR_W "W"
765 #endif
766
767
768
769 /* this is the classic geocaching notation */
770 static char
771 *osd_latitude_str(float latitude) {
772 char *c = OSD_COORDINATES_CHR_N;
773 float integral, fractional;
774
775 if(isnan(latitude))
776 return NULL;
777
778 if(latitude < 0) {
779 latitude = fabs(latitude);
780 c = OSD_COORDINATES_CHR_S;
781 }
782
783 fractional = modff(latitude, &integral);
784
785 return g_strdup_printf("%s %02d° %06.3f'",
786 c, (int)integral, fractional*60.0);
787 }
788
789 static char
790 *osd_longitude_str(float longitude) {
791 char *c = OSD_COORDINATES_CHR_E;
792 float integral, fractional;
793
794 if(isnan(longitude))
795 return NULL;
796
797 if(longitude < 0) {
798 longitude = fabs(longitude);
799 c = OSD_COORDINATES_CHR_W;
800 }
801
802 fractional = modff(longitude, &integral);
803
804 return g_strdup_printf("%s %03d° %06.3f'",
805 c, (int)integral, fractional*60.0);
806 }
807
808 static void
809 osd_render_coordinates(osm_gps_map_osd_t *osd)
810 {
811 osd_priv_t *priv = (osd_priv_t*)osd->priv;
812
813 /* get current map position */
814 gfloat lat, lon;
815 g_object_get(osd->widget, "latitude", &lat, "longitude", &lon, NULL);
816
817 /* check if position has changed enough to require redraw */
818 if(!isnan(priv->coo_lat) && !isnan(priv->coo_lon))
819 if((fabsf(lat - priv->coo_lat) < 1/60000) && /* 1/60000 == 1/1000 minute */
820 (fabsf(lon - priv->coo_lon) < 1/60000))
821 return;
822
823 priv->coo_lat = lat;
824 priv->coo_lon = lon;
825
826 /* first fill with light transparency */
827 cairo_t *cr = cairo_create(priv->coordinates);
828 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
829 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 0.5);
830 cairo_paint(cr);
831 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
832
833 cairo_select_font_face (cr, "Sans",
834 CAIRO_FONT_SLANT_NORMAL,
835 CAIRO_FONT_WEIGHT_BOLD);
836 cairo_set_font_size (cr, OSD_COORDINATES_FONT_SIZE);
837
838 char *latitude = osd_latitude_str(lat);
839 char *longitude = osd_longitude_str(lon);
840
841 cairo_text_extents_t lat_extents, lon_extents;
842 cairo_text_extents (cr, latitude, &lat_extents);
843 cairo_text_extents (cr, longitude, &lon_extents);
844
845 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
846 cairo_set_line_width (cr, OSD_COORDINATES_FONT_SIZE/6);
847 cairo_move_to (cr,
848 (OSD_COORDINATES_W - lat_extents.width)/2,
849 OSD_COORDINATES_OFFSET - lat_extents.y_bearing);
850 cairo_text_path (cr, latitude);
851 cairo_move_to (cr,
852 (OSD_COORDINATES_W - lon_extents.width)/2,
853 OSD_COORDINATES_OFFSET - lon_extents.y_bearing +
854 OSD_COORDINATES_FONT_SIZE);
855 cairo_text_path (cr, longitude);
856 cairo_stroke (cr);
857
858 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
859 cairo_move_to (cr,
860 (OSD_COORDINATES_W - lat_extents.width)/2,
861 OSD_COORDINATES_OFFSET - lat_extents.y_bearing);
862 cairo_show_text (cr, latitude);
863 cairo_move_to (cr,
864 (OSD_COORDINATES_W - lon_extents.width)/2,
865 OSD_COORDINATES_OFFSET - lon_extents.y_bearing +
866 OSD_COORDINATES_FONT_SIZE);
867 cairo_show_text (cr, longitude);
868
869 g_free(latitude);
870 g_free(longitude);
871
872 cairo_destroy(cr);
873 }
874 #endif // OSD_COORDINATES
875
876 #ifdef OSD_CROSSHAIR
877
878 #ifndef OSD_CROSSHAIR_RADIUS
879 #define OSD_CROSSHAIR_RADIUS 10
880 #endif
881
882 #define OSD_CROSSHAIR_TICK (OSD_CROSSHAIR_RADIUS/2)
883 #define OSD_CROSSHAIR_BORDER (OSD_CROSSHAIR_TICK + OSD_CROSSHAIR_RADIUS/4)
884 #define OSD_CROSSHAIR_W ((OSD_CROSSHAIR_RADIUS+OSD_CROSSHAIR_BORDER)*2)
885 #define OSD_CROSSHAIR_H ((OSD_CROSSHAIR_RADIUS+OSD_CROSSHAIR_BORDER)*2)
886
887 static void
888 osd_render_crosshair_shape(cairo_t *cr) {
889 cairo_arc (cr, OSD_CROSSHAIR_W/2, OSD_CROSSHAIR_H/2,
890 OSD_CROSSHAIR_RADIUS, 0, 2*M_PI);
891
892 cairo_move_to (cr, OSD_CROSSHAIR_W/2 - OSD_CROSSHAIR_RADIUS,
893 OSD_CROSSHAIR_H/2);
894 cairo_rel_line_to (cr, -OSD_CROSSHAIR_TICK, 0);
895 cairo_move_to (cr, OSD_CROSSHAIR_W/2 + OSD_CROSSHAIR_RADIUS,
896 OSD_CROSSHAIR_H/2);
897 cairo_rel_line_to (cr, OSD_CROSSHAIR_TICK, 0);
898
899 cairo_move_to (cr, OSD_CROSSHAIR_W/2,
900 OSD_CROSSHAIR_H/2 - OSD_CROSSHAIR_RADIUS);
901 cairo_rel_line_to (cr, 0, -OSD_CROSSHAIR_TICK);
902 cairo_move_to (cr, OSD_CROSSHAIR_W/2,
903 OSD_CROSSHAIR_H/2 + OSD_CROSSHAIR_RADIUS);
904 cairo_rel_line_to (cr, 0, OSD_CROSSHAIR_TICK);
905
906 cairo_stroke (cr);
907 }
908
909 static void
910 osd_render_crosshair(osm_gps_map_osd_t *osd)
911 {
912 osd_priv_t *priv = (osd_priv_t*)osd->priv;
913
914 /* first fill with transparency */
915 cairo_t *cr = cairo_create(priv->crosshair);
916 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
917 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 0.0);
918 // cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.2);
919 cairo_paint(cr);
920 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
921
922 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
923
924 cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.5);
925 cairo_set_line_width (cr, OSD_CROSSHAIR_RADIUS/2);
926 osd_render_crosshair_shape(cr);
927
928 cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.5);
929 cairo_set_line_width (cr, OSD_CROSSHAIR_RADIUS/4);
930 osd_render_crosshair_shape(cr);
931
932 cairo_destroy(cr);
933 }
934 #endif
935
936 #ifdef OSD_SCALE
937
938 #ifndef OSD_SCALE_FONT_SIZE
939 #define OSD_SCALE_FONT_SIZE 12
940 #endif
941 #define OSD_SCALE_W (10*OSD_SCALE_FONT_SIZE)
942 #define OSD_SCALE_H (5*OSD_SCALE_FONT_SIZE/2)
943
944 /* various parameters used to create the scale */
945 #define OSD_SCALE_H2 (OSD_SCALE_H/2)
946 #define OSD_SCALE_TICK (2*OSD_SCALE_FONT_SIZE/3)
947 #define OSD_SCALE_M (OSD_SCALE_H2 - OSD_SCALE_TICK)
948 #define OSD_SCALE_I (OSD_SCALE_H2 + OSD_SCALE_TICK)
949 #define OSD_SCALE_FD (OSD_SCALE_FONT_SIZE/4)
950
951 static void
952 osd_render_scale(osm_gps_map_osd_t *osd)
953 {
954 osd_priv_t *priv = (osd_priv_t*)osd->priv;
955
956 /* this only needs to be rendered if the zoom has changed */
957 gint zoom;
958 g_object_get(OSM_GPS_MAP(osd->widget), "zoom", &zoom, NULL);
959 if(zoom == priv->scale_zoom)
960 return;
961
962 priv->scale_zoom = zoom;
963
964 float m_per_pix = osm_gps_map_get_scale(OSM_GPS_MAP(osd->widget));
965
966 /* first fill with transparency */
967 cairo_t *cr = cairo_create(priv->scale);
968 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
969 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
970 // pink for testing: cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.2);
971 cairo_paint(cr);
972 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
973
974 /* determine the size of the scale width in meters */
975 float width = (OSD_SCALE_W-OSD_SCALE_FONT_SIZE/6) * m_per_pix;
976
977 /* scale this to useful values */
978 int exp = logf(width)*M_LOG10E;
979 int mant = width/pow(10,exp);
980 int width_metric = mant * pow(10,exp);
981 char *dist_str = NULL;
982 if(width_metric<1000)
983 dist_str = g_strdup_printf("%u m", width_metric);
984 else
985 dist_str = g_strdup_printf("%u km", width_metric/1000);
986 width_metric /= m_per_pix;
987
988 /* and now the hard part: scale for useful imperial values :-( */
989 /* try to convert to feet, 1ft == 0.3048 m */
990 width /= 0.3048;
991 float imp_scale = 0.3048;
992 char *dist_imp_unit = "ft";
993
994 if(width >= 100) {
995 /* 1yd == 3 feet */
996 width /= 3.0;
997 imp_scale *= 3.0;
998 dist_imp_unit = "yd";
999
1000 if(width >= 1760.0) {
1001 /* 1mi == 1760 yd */
1002 width /= 1760.0;
1003 imp_scale *= 1760.0;
1004 dist_imp_unit = "mi";
1005 }
1006 }
1007
1008 /* also convert this to full tens/hundreds */
1009 exp = logf(width)*M_LOG10E;
1010 mant = width/pow(10,exp);
1011 int width_imp = mant * pow(10,exp);
1012 char *dist_str_imp = g_strdup_printf("%u %s", width_imp, dist_imp_unit);
1013
1014 /* convert back to pixels */
1015 width_imp *= imp_scale;
1016 width_imp /= m_per_pix;
1017
1018 cairo_select_font_face (cr, "Sans",
1019 CAIRO_FONT_SLANT_NORMAL,
1020 CAIRO_FONT_WEIGHT_BOLD);
1021 cairo_set_font_size (cr, OSD_SCALE_FONT_SIZE);
1022 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
1023
1024 cairo_text_extents_t extents;
1025 cairo_text_extents (cr, dist_str, &extents);
1026
1027 cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
1028 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/6);
1029 cairo_move_to (cr, 2*OSD_SCALE_FD, OSD_SCALE_H2-OSD_SCALE_FD);
1030 cairo_text_path (cr, dist_str);
1031 cairo_stroke (cr);
1032 cairo_move_to (cr, 2*OSD_SCALE_FD,
1033 OSD_SCALE_H2+OSD_SCALE_FD + extents.height);
1034 cairo_text_path (cr, dist_str_imp);
1035 cairo_stroke (cr);
1036
1037 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
1038 cairo_move_to (cr, 2*OSD_SCALE_FD, OSD_SCALE_H2-OSD_SCALE_FD);
1039 cairo_show_text (cr, dist_str);
1040 cairo_move_to (cr, 2*OSD_SCALE_FD,
1041 OSD_SCALE_H2+OSD_SCALE_FD + extents.height);
1042 cairo_show_text (cr, dist_str_imp);
1043
1044 g_free(dist_str);
1045 g_free(dist_str_imp);
1046
1047 /* draw white line */
1048 cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
1049 cairo_set_source_rgba(cr, 1.0, 1.0, 1.0, 1.0);
1050 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/3);
1051 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_M);
1052 cairo_rel_line_to (cr, 0, OSD_SCALE_TICK);
1053 cairo_rel_line_to (cr, width_metric, 0);
1054 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1055 cairo_stroke(cr);
1056 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_I);
1057 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1058 cairo_rel_line_to (cr, width_imp, 0);
1059 cairo_rel_line_to (cr, 0, +OSD_SCALE_TICK);
1060 cairo_stroke(cr);
1061
1062 /* draw black line */
1063 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
1064 cairo_set_line_width (cr, OSD_SCALE_FONT_SIZE/6);
1065 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_M);
1066 cairo_rel_line_to (cr, 0, OSD_SCALE_TICK);
1067 cairo_rel_line_to (cr, width_metric, 0);
1068 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1069 cairo_stroke(cr);
1070 cairo_move_to (cr, OSD_SCALE_FONT_SIZE/6, OSD_SCALE_I);
1071 cairo_rel_line_to (cr, 0, -OSD_SCALE_TICK);
1072 cairo_rel_line_to (cr, width_imp, 0);
1073 cairo_rel_line_to (cr, 0, +OSD_SCALE_TICK);
1074 cairo_stroke(cr);
1075
1076 cairo_destroy(cr);
1077 }
1078 #endif
1079
1080 static void
1081 osd_render_controls(osm_gps_map_osd_t *osd)
1082 {
1083 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1084
1085 if(priv->controls.rendered
1086 #ifdef OSD_GPS_BUTTON
1087 && (priv->controls.gps_enabled == (osd->cb != NULL))
1088 #endif
1089 )
1090 return;
1091
1092 #ifdef OSD_GPS_BUTTON
1093 priv->controls.gps_enabled = (osd->cb != NULL);
1094 #endif
1095
1096 #ifndef OSD_COLOR
1097 GdkColor bg = GTK_WIDGET(osd->widget)->style->bg[GTK_STATE_NORMAL];
1098 GdkColor fg = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_NORMAL];
1099 GdkColor da = GTK_WIDGET(osd->widget)->style->fg[GTK_STATE_INSENSITIVE];
1100 #endif
1101
1102 /* first fill with transparency */
1103 cairo_t *cr = cairo_create(priv->controls.surface);
1104 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
1105 cairo_set_source_rgba(cr, 1.0, 0.0, 0.0, 0.0);
1106 cairo_paint(cr);
1107 cairo_set_operator(cr, CAIRO_OPERATOR_OVER);
1108
1109 /* --------- draw zoom and dpad shape shadow ----------- */
1110 #ifdef OSD_SHADOW_ENABLE
1111 osd_zoom_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
1112 osd_shape_shadow(cr);
1113 #ifndef OSD_NO_DPAD
1114 osd_dpad_shape(cr, 1+OSD_SHADOW, 1+OSD_SHADOW);
1115 osd_shape_shadow(cr);
1116 #endif
1117 #endif
1118
1119 /* --------- draw zoom and dpad shape ----------- */
1120
1121 osd_zoom_shape(cr, 1, 1);
1122 #ifndef OSD_COLOR
1123 osd_shape(cr, &bg, &fg);
1124 #else
1125 osd_shape(cr);
1126 #endif
1127 #ifndef OSD_NO_DPAD
1128 osd_dpad_shape(cr, 1, 1);
1129 #ifndef OSD_COLOR
1130 osd_shape(cr, &bg, &fg);
1131 #else
1132 osd_shape(cr);
1133 #endif
1134 #endif
1135
1136 /* --------- draw zoom and dpad labels --------- */
1137
1138 #ifdef OSD_SHADOW_ENABLE
1139 osd_labels_shadow(cr, Z_RAD/3, TRUE);
1140 osd_zoom_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1141 #ifndef OSD_NO_DPAD
1142 osd_dpad_labels(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1143 #endif
1144 cairo_stroke(cr);
1145 #ifdef OSD_GPS_BUTTON
1146 osd_labels_shadow(cr, Z_RAD/6, osd->cb != NULL);
1147 osd_dpad_gps(cr, 1+OSD_LBL_SHADOW, 1+OSD_LBL_SHADOW);
1148 cairo_stroke(cr);
1149 #endif
1150 #endif
1151
1152 #ifndef OSD_COLOR
1153 osd_labels(cr, Z_RAD/3, TRUE, &fg, &da);
1154 #else
1155 osd_labels(cr, Z_RAD/3, TRUE);
1156 #endif
1157 osd_zoom_labels(cr, 1, 1);
1158 #ifndef OSD_NO_DPAD
1159 osd_dpad_labels(cr, 1, 1);
1160 #endif
1161 cairo_stroke(cr);
1162
1163 #ifndef OSD_COLOR
1164 osd_labels(cr, Z_RAD/6, osd->cb != NULL, &fg, &da);
1165 #else
1166 osd_labels(cr, Z_RAD/6, osd->cb != NULL);
1167 #endif
1168 #ifdef OSD_GPS_BUTTON
1169 osd_dpad_gps(cr, 1, 1);
1170 #endif
1171 cairo_stroke(cr);
1172
1173 cairo_destroy(cr);
1174 }
1175
1176 static void
1177 osd_render(osm_gps_map_osd_t *osd)
1178 {
1179 osd_render_controls(osd);
1180
1181 #ifdef OSD_SOURCE_SEL
1182 osd_render_source_sel(osd);
1183 #endif
1184
1185 #ifdef OSD_SCALE
1186 osd_render_scale(osd);
1187 #endif
1188
1189 #ifdef OSD_CROSSHAIR
1190 osd_render_crosshair(osd);
1191 #endif
1192
1193 #ifdef OSD_COORDINATES
1194 osd_render_coordinates(osd);
1195 #endif
1196 }
1197
1198 static void
1199 osd_draw(osm_gps_map_osd_t *osd, GdkDrawable *drawable)
1200 {
1201 osd_priv_t *priv = (osd_priv_t*)osd->priv;
1202
1203 /* OSD itself uses some off-screen rendering, so check if the */
1204 /* offscreen buffer is present and create it if not */
1205 if(!priv->controls.surface) {
1206 /* create overlay ... */
1207 priv->controls.surface =
1208 cairo_image_surface_create(CAIRO_FORMAT_ARGB32, OSD_W+2, OSD_H+2);
1209
1210 priv->controls.rendered = FALSE;
1211 #ifdef OSD_GPS_BUTTON
1212 priv->controls.gps_enabled = FALSE;
1213 #endif
1214
1215 #ifdef OSD_SOURCE_SEL
1216 /* the initial OSD state is alway not-expanded */
1217 priv->map_source =
1218 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1219 OSD_S_W+2, OSD_S_H+2);
1220 #endif
1221
1222 #ifdef OSD_SCALE
1223 priv->scale =
1224 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1225 OSD_SCALE_W, OSD_SCALE_H);
1226 priv->scale_zoom = -1;
1227 #endif
1228
1229 #ifdef OSD_CROSSHAIR
1230 priv->crosshair =
1231 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1232 OSD_CROSSHAIR_W, OSD_CROSSHAIR_H);
1233 #endif
1234
1235 #ifdef OSD_COORDINATES
1236 priv->coordinates =
1237 cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
1238 OSD_COORDINATES_W, OSD_COORDINATES_H);
1239
1240 priv->coo_lat = priv->coo_lon = OSM_GPS_MAP_INVALID;
1241 #endif
1242
1243 /* ... and render it */
1244 osd_render(osd);
1245 }
1246
1247 // now draw this onto the original context
1248 cairo_t *cr = gdk_cairo_create(drawable);
1249
1250 int x, y;
1251
1252 #ifdef OSD_SCALE
1253 x = OSD_X;
1254 y = -OSD_Y;
1255 if(x < 0) x += osd->widget->allocation.width - OSD_SCALE_W;
1256 if(y < 0) y += osd->widget->allocation.height - OSD_SCALE_H;
1257
1258 cairo_set_source_surface(cr, priv->scale, x, y);
1259 cairo_paint(cr);
1260 #endif
1261
1262 #ifdef OSD_CROSSHAIR
1263 x = (osd->widget->allocation.width - OSD_CROSSHAIR_W)/2;
1264 y = (osd->widget->allocation.height - OSD_CROSSHAIR_H)/2;
1265
1266 cairo_set_source_surface(cr, priv->crosshair, x, y);
1267 cairo_paint(cr);
1268 #endif
1269
1270 #ifdef OSD_COORDINATES
1271 x = -OSD_X;
1272 y = -OSD_Y;
1273 if(x < 0) x += osd->widget->allocation.width - OSD_COORDINATES_W;
1274 if(y < 0) y += osd->widget->allocation.height - OSD_COORDINATES_H;
1275
1276 cairo_set_source_surface(cr, priv->coordinates, x, y);
1277 cairo_paint(cr);
1278 #endif
1279
1280 x = OSD_X;
1281 if(x < 0)
1282 x += osd->widget->allocation.width - OSD_W;
1283
1284 y = OSD_Y;
1285 if(y < 0)
1286 y += osd->widget->allocation.height - OSD_H;
1287
1288 cairo_set_source_surface(cr, priv->controls.surface, x, y);
1289 cairo_paint(cr);
1290
1291 #ifdef OSD_SOURCE_SEL
1292 if(!priv->handler_id) {
1293 /* the OSD source selection is not being animated */
1294 if(!priv->expanded)
1295 x = osd->widget->allocation.width - OSD_S_W;
1296 else
1297 x = osd->widget->allocation.width - OSD_S_EXP_W + OSD_S_X;
1298 } else
1299 x = priv->shift;
1300
1301 y = OSD_S_Y;
1302 if(OSD_S_Y < 0) {
1303 if(!priv->expanded)
1304 y = osd->widget->allocation.height - OSD_S_H + OSD_S_Y;
1305 else
1306 y = osd->widget->allocation.height - OSD_S_EXP_H + OSD_S_Y;
1307 }
1308
1309 cairo_set_source_surface(cr, priv->map_source, x, y);
1310 cairo_paint(cr);
1311 #endif
1312
1313 cairo_destroy(cr);
1314 }
1315
1316 static void
1317 osd_free(osm_gps_map_osd_t *osd)
1318 {
1319 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
1320
1321 if (priv->controls.surface)
1322 cairo_surface_destroy(priv->controls.surface);
1323
1324 #ifdef OSD_SOURCE_SEL
1325 if(priv->handler_id)
1326 gtk_timeout_remove(priv->handler_id);
1327
1328 if (priv->map_source)
1329 cairo_surface_destroy(priv->map_source);
1330 #endif
1331
1332 #ifdef OSD_SCALE
1333 if (priv->scale)
1334 cairo_surface_destroy(priv->scale);
1335 #endif
1336
1337 #ifdef OSD_CROSSHAIR
1338 if (priv->crosshair)
1339 cairo_surface_destroy(priv->crosshair);
1340 #endif
1341
1342 #ifdef OSD_COORDINATES
1343 if (priv->coordinates)
1344 cairo_surface_destroy(priv->coordinates);
1345 #endif
1346
1347 g_free(priv);
1348 }
1349
1350 static gboolean
1351 osd_busy(osm_gps_map_osd_t *osd)
1352 {
1353 #ifdef OSD_SOURCE_SEL
1354 osd_priv_t *priv = (osd_priv_t *)(osd->priv);
1355 return (priv->handler_id != 0);
1356 #else
1357 return FALSE;
1358 #endif
1359 }
1360
1361 static osm_gps_map_osd_t osd_classic = {
1362 .widget = NULL,
1363
1364 .draw = osd_draw,
1365 .check = osd_check,
1366 .render = osd_render,
1367 .free = osd_free,
1368 .busy = osd_busy,
1369
1370 .cb = NULL,
1371 .data = NULL,
1372
1373 .priv = NULL
1374 };
1375
1376 /* this is the only function that's externally visible */
1377 void
1378 osm_gps_map_osd_classic_init(OsmGpsMap *map)
1379 {
1380 osd_priv_t *priv = osd_classic.priv = g_new0(osd_priv_t, 1);
1381
1382 osd_classic.priv = priv;
1383
1384 osm_gps_map_register_osd(map, &osd_classic);
1385 }
1386
1387 #ifdef OSD_GPS_BUTTON
1388 /* below are osd specific functions which aren't used by osm-gps-map */
1389 /* but instead are to be used by the main application */
1390 void osm_gps_map_osd_enable_gps (OsmGpsMap *map, OsmGpsMapOsdCallback cb,
1391 gpointer data) {
1392 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1393 g_return_if_fail (osd);
1394
1395 osd->cb = cb;
1396 osd->data = data;
1397
1398 /* this may have changed the state of the gps button */
1399 /* we thus re-render the overlay */
1400 osd->render(osd);
1401
1402 osm_gps_map_redraw(map);
1403 }
1404 #endif
1405
1406 osd_button_t
1407 osm_gps_map_osd_check(OsmGpsMap *map, gint x, gint y) {
1408 osm_gps_map_osd_t *osd = osm_gps_map_osd_get(map);
1409 g_return_val_if_fail (osd, OSD_NONE);
1410
1411 return osd_check(osd, x, y);
1412 }