Fix:Core:Better callback for osd initialisation
[navit-package] / navit / osd / core / osd_core.c
1 /**
2  * Navit, a modular navigation system.
3  * Copyright (C) 2005-2008 Navit Team
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * version 2 as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA  02110-1301, USA.
18  */
19
20 #include <math.h>
21 #include <stdio.h>
22 #include <glib.h>
23 #include <time.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include "config.h"
27 #include "item.h"
28 #include "point.h"
29 #include "coord.h"
30 #include "graphics.h"
31 #include "transform.h"
32 #include "route.h"
33 #include "navit.h"
34 #include "plugin.h"
35 #include "debug.h"
36 #include "callback.h"
37 #include "color.h"
38 #include "vehicle.h"
39 #include "navigation.h"
40 #include "track.h"
41 #include "map.h"
42 #include "file.h"
43 #include "attr.h"
44 #include "command.h"
45 #include "navit_nls.h"
46 #include "messages.h"
47 #include "vehicleprofile.h"
48 #include "roadprofile.h"
49 #include "osd.h"
50 #include "speech.h"
51
52 struct compass {
53         struct osd_item osd_item;
54         int width;
55         struct graphics_gc *green;
56 };
57
58 static void
59 transform_rotate(struct point *center, int angle, struct point *p,
60                  int count)
61 {
62         int i, x, y;
63         double dx, dy;
64         for (i = 0; i < count; i++) {
65                 dx = sin(M_PI * angle / 180.0);
66                 dy = cos(M_PI * angle / 180.0);
67                 x = dy * p->x - dx * p->y;
68                 y = dx * p->x + dy * p->y;
69
70                 p->x = center->x + x;
71                 p->y = center->y + y;
72                 p++;
73         }
74 }
75
76 static void
77 handle(struct graphics *gr, struct graphics_gc *gc, struct point *p, int r,
78        int dir)
79 {
80         struct point ph[3];
81         int l = r * 0.4;
82
83         ph[0].x = 0;
84         ph[0].y = r;
85         ph[1].x = 0;
86         ph[1].y = -r;
87         transform_rotate(p, dir, ph, 2);
88         graphics_draw_lines(gr, gc, ph, 2);
89         ph[0].x = -l;
90         ph[0].y = -r + l;
91         ph[1].x = 0;
92         ph[1].y = -r;
93         ph[2].x = l;
94         ph[2].y = -r + l;
95         transform_rotate(p, dir, ph, 3);
96         graphics_draw_lines(gr, gc, ph, 3);
97 }
98
99 static char *
100 format_distance(double distance, char *sep)
101 {
102         if (distance >= 100000)
103                 return g_strdup_printf("%.0f%skm", distance / 1000, sep);
104         else if (distance >= 10000)
105                 return g_strdup_printf("%.1f%skm", distance / 1000, sep);
106         else if (distance >= 300)
107                 return g_strdup_printf("%.0f%sm", round(distance / 25) * 25, sep);
108         else if (distance >= 50)
109                 return g_strdup_printf("%.0f%sm", round(distance / 10) * 10, sep);
110         else if (distance >= 10)
111                 return g_strdup_printf("%.0f%sm", distance, sep);
112         else
113                 return g_strdup_printf("%.1f%sm", distance, sep);
114 }
115
116 static char * 
117 format_time(struct tm *tm, int days)
118 {
119         if (days)
120                 return g_strdup_printf("%d+%02d:%02d", days, tm->tm_hour, tm->tm_min);
121         else
122                 return g_strdup_printf("%02d:%02d", tm->tm_hour, tm->tm_min);
123 }
124
125 static char * 
126 format_speed(double speed, char *sep)
127 {
128         return g_strdup_printf("%.0f%skm/h", speed, sep);
129 }
130
131 /*static char *
132 format_float(double num)
133 {
134         return g_strdup_printf("%f", num);
135 }*/
136
137 static char *
138 format_float_0(double num)
139 {
140         return g_strdup_printf("%.0f", num);
141 }
142
143 static void
144 osd_compass_draw(struct compass *this, struct navit *nav,
145                  struct vehicle *v)
146 {
147         struct point p,bbox[4];
148         struct attr attr_dir, destination_attr, position_attr;
149         double dir, vdir = 0;
150         char *buffer;
151         struct coord c1, c2;
152         enum projection pro;
153
154         osd_std_draw(&this->osd_item);
155         p.x = this->osd_item.w/2;
156         p.y = this->osd_item.w/2;
157         graphics_draw_circle(this->osd_item.gr,
158                              this->osd_item.graphic_fg_white, &p, this->osd_item.w*5/6);
159         if (v) {
160                 if (vehicle_get_attr(v, attr_position_direction, &attr_dir, NULL)) {
161                         vdir = *attr_dir.u.numd;
162                         handle(this->osd_item.gr, this->osd_item.graphic_fg_white, &p, this->osd_item.w/3, -vdir);
163                 }
164
165                 if (navit_get_attr(nav, attr_destination, &destination_attr, NULL)
166                     && vehicle_get_attr(v, attr_position_coord_geo,&position_attr, NULL)) {
167                         pro = destination_attr.u.pcoord->pro;
168                         transform_from_geo(pro, position_attr.u.coord_geo, &c1);
169                         c2.x = destination_attr.u.pcoord->x;
170                         c2.y = destination_attr.u.pcoord->y;
171                         dir = atan2(c2.x - c1.x, c2.y - c1.y) * 180.0 / M_PI;
172                         dir -= vdir;
173                         handle(this->osd_item.gr, this->green, &p, this->osd_item.w/3, dir);
174                         buffer=format_distance(transform_distance(pro, &c1, &c2),"");
175                         graphics_get_text_bbox(this->osd_item.gr, this->osd_item.font, buffer, 0x10000, 0, bbox, 0);
176                         p.x=(this->osd_item.w-bbox[2].x)/2;
177                         p.y = this->osd_item.h-this->osd_item.h/10;
178                         graphics_draw_text(this->osd_item.gr, this->green, NULL, this->osd_item.font, buffer, &p, 0x10000, 0);
179                         g_free(buffer);
180                 }
181         }
182         graphics_draw_mode(this->osd_item.gr, draw_mode_end);
183 }
184
185
186
187 static void
188 osd_compass_init(struct compass *this, struct navit *nav)
189 {
190         struct color c;
191
192         osd_set_std_graphic(nav, &this->osd_item, (struct osd_priv *)this);
193
194         this->green = graphics_gc_new(this->osd_item.gr);
195         c.r = 0;
196         c.g = 65535;
197         c.b = 0;
198         c.a = 65535;
199         graphics_gc_set_foreground(this->green, &c);
200         graphics_gc_set_linewidth(this->green, this->width);
201         graphics_gc_set_linewidth(this->osd_item.graphic_fg_white, this->width);
202
203         navit_add_callback(nav, callback_new_attr_1(callback_cast(osd_compass_draw), attr_position_coord_geo, this));
204
205         osd_compass_draw(this, nav, NULL);
206 }
207
208 static struct osd_priv *
209 osd_compass_new(struct navit *nav, struct osd_methods *meth,
210                 struct attr **attrs)
211 {
212         struct compass *this = g_new0(struct compass, 1);
213         struct attr *attr;
214         this->osd_item.p.x = 20;
215         this->osd_item.p.y = 20;
216         this->osd_item.w = 60;
217         this->osd_item.h = 80;
218         this->osd_item.navit = nav;
219         this->osd_item.font_size = 200;
220         this->osd_item.meth.draw = osd_draw_cast(osd_compass_draw);
221         osd_set_std_attr(attrs, &this->osd_item, 2);
222         attr = attr_search(attrs, NULL, attr_width);
223         this->width=attr ? attr->u.num : 2;
224         navit_add_callback(nav, callback_new_attr_1(callback_cast(osd_compass_init), attr_graphics_ready, this));
225         return (struct osd_priv *) this;
226 }
227
228 struct osd_button {
229         int use_overlay;
230         struct osd_item item;
231         struct callback *draw_cb,*navit_init_cb;
232         struct graphics_image *img;
233         char *src;
234 };
235
236 static void
237 osd_button_draw(struct osd_button *this, struct navit *nav)
238 {
239         struct point bp = this->item.p;
240         osd_wrap_point(&bp, nav);
241         graphics_draw_image(this->item.gr, this->item.graphic_bg, &bp, this->img);
242 }
243
244 static void
245 osd_button_init(struct osd_button *this, struct navit *nav)
246 {
247         struct graphics *gra = navit_get_graphics(nav);
248         dbg(1, "enter\n");
249         this->img = graphics_image_new(gra, this->src);
250         if (!this->img) {
251                 dbg(1, "failed to load '%s'\n", this->src);
252                 return;
253         }
254         if (!this->item.w)
255                 this->item.w=this->img->width;
256         if (!this->item.h)
257                 this->item.h=this->img->height;
258         if (this->use_overlay) {
259                 struct graphics_image *img;
260                 struct point p;
261                 osd_set_std_graphic(nav, &this->item, (struct osd_priv *)this);
262                 img=graphics_image_new(this->item.gr, this->src);
263                 p.x=(this->item.w-this->img->width)/2;
264                 p.y=(this->item.h-this->img->height)/2;
265                 osd_std_draw(&this->item);
266                 graphics_draw_image(this->item.gr, this->item.graphic_bg, &p, img);
267                 graphics_draw_mode(this->item.gr, draw_mode_end);
268                 graphics_image_free(this->item.gr, img);
269         } else {
270                 this->item.configured=1;
271                 this->item.gr=gra;
272                 this->item.graphic_bg=graphics_gc_new(this->item.gr);
273                 graphics_add_callback(gra, this->draw_cb=callback_new_attr_2(callback_cast(osd_button_draw), attr_postdraw, this, nav));
274         }
275         navit_add_callback(nav, this->navit_init_cb = callback_new_attr_1(callback_cast (osd_std_click), attr_button, &this->item));
276 }
277
278 static struct osd_priv *
279 osd_button_new(struct navit *nav, struct osd_methods *meth,
280                struct attr **attrs)
281 {
282         struct osd_button *this = g_new0(struct osd_button, 1);
283         struct attr *attr;
284
285         this->item.navit = nav;
286         this->item.meth.draw = osd_draw_cast(osd_button_draw);
287
288         osd_set_std_attr(attrs, &this->item, 1);
289
290         attr=attr_search(attrs, NULL, attr_use_overlay);
291         if (attr)
292                 this->use_overlay=attr->u.num;
293         if (!this->item.command) {
294                 dbg(0, "no command\n");
295                 goto error;
296         }
297         attr = attr_search(attrs, NULL, attr_src);
298         if (!attr) {
299                 dbg(0, "no src\n");
300                 goto error;
301         }
302
303         this->src = graphics_icon_path(attr->u.str);
304
305         navit_add_callback(nav, this->navit_init_cb = callback_new_attr_1(callback_cast (osd_button_init), attr_graphics_ready, this));
306
307         return (struct osd_priv *) this;
308       error:
309         g_free(this);
310         return NULL;
311 }
312
313 struct nav_next_turn {
314         struct osd_item osd_item;
315         char *test_text;
316         char *icon_src;
317         int icon_h, icon_w, active;
318         char *last_name;
319         int level;
320 };
321
322 static void
323 osd_nav_next_turn_draw(struct nav_next_turn *this, struct navit *navit,
324                        struct vehicle *v)
325 {
326         struct point p;
327         int do_draw = 0;
328         struct navigation *nav = NULL;
329         struct map *map = NULL;
330         struct map_rect *mr = NULL;
331         struct item *item = NULL;
332         struct graphics_image *gr_image;
333         char *image;
334         char *name = "unknown";
335         int level = this->level;
336
337         if (navit)
338                 nav = navit_get_navigation(navit);
339         if (nav)
340                 map = navigation_get_map(nav);
341         if (map)
342                 mr = map_rect_new(map, NULL);
343         if (mr)
344                 while ((item = map_rect_get_item(mr))
345                        && (item->type == type_nav_position || item->type == type_nav_none || level-- > 0));
346         if (item) {
347                 name = item_to_name(item->type);
348                 dbg(1, "name=%s\n", name);
349                 if (this->active != 1 || this->last_name != name) {
350                         this->active = 1;
351                         this->last_name = name;
352                         do_draw = 1;
353                 }
354         } else {
355                 if (this->active != 0) {
356                         this->active = 0;
357                         do_draw = 1;
358                 }
359         }
360         if (mr)
361                 map_rect_destroy(mr);
362
363         if (do_draw) {
364                 osd_std_draw(&this->osd_item);
365                 if (this->active) {
366                         image = g_strdup_printf(this->icon_src, name);
367                         dbg(1, "image=%s\n", image);
368                         gr_image =
369                             graphics_image_new_scaled(this->osd_item.gr,
370                                                       image, this->icon_w,
371                                                       this->icon_h);
372                         if (!gr_image) {
373                                 g_free(image);
374                                 image = graphics_icon_path("unknown.xpm");
375                                 gr_image =
376                                     graphics_image_new_scaled(this->
377                                                               osd_item.gr,
378                                                               image,
379                                                               this->icon_w,
380                                                               this->
381                                                               icon_h);
382                         }
383                         dbg(1, "gr_image=%p\n", gr_image);
384                         if (gr_image) {
385                                 p.x =
386                                     (this->osd_item.w -
387                                      gr_image->width) / 2;
388                                 p.y =
389                                     (this->osd_item.h -
390                                      gr_image->height) / 2;
391                                 graphics_draw_image(this->osd_item.gr,
392                                                     this->osd_item.
393                                                     graphic_fg_white, &p,
394                                                     gr_image);
395                                 graphics_image_free(this->osd_item.gr,
396                                                     gr_image);
397                         }
398                         g_free(image);
399                 }
400                 graphics_draw_mode(this->osd_item.gr, draw_mode_end);
401         }
402 }
403
404 static void
405 osd_nav_next_turn_init(struct nav_next_turn *this, struct navit *nav)
406 {
407         osd_set_std_graphic(nav, &this->osd_item, (struct osd_priv *)this);
408         navit_add_callback(nav, callback_new_attr_1(callback_cast(osd_nav_next_turn_draw), attr_position_coord_geo, this));
409         navit_add_callback(nav, callback_new_attr_1(callback_cast(osd_std_click), attr_button, &this->osd_item));
410         osd_nav_next_turn_draw(this, nav, NULL);
411 }
412
413 static struct osd_priv *
414 osd_nav_next_turn_new(struct navit *nav, struct osd_methods *meth,
415                       struct attr **attrs)
416 {
417         struct nav_next_turn *this = g_new0(struct nav_next_turn, 1);
418         struct attr *attr;
419
420         this->osd_item.p.x = 20;
421         this->osd_item.p.y = -80;
422         this->osd_item.w = 70;
423         this->osd_item.navit = nav;
424         this->osd_item.h = 70;
425         this->osd_item.font_size = 200;
426         this->osd_item.meth.draw = osd_draw_cast(osd_nav_next_turn_draw);
427         osd_set_std_attr(attrs, &this->osd_item, 0);
428
429         this->icon_w = -1;
430         this->icon_h = -1;
431         this->active = -1;
432         this->level  = 0;
433
434         attr = attr_search(attrs, NULL, attr_icon_w);
435         if (attr)
436                 this->icon_w = attr->u.num;
437
438         attr = attr_search(attrs, NULL, attr_icon_h);
439         if (attr)
440                 this->icon_h = attr->u.num;
441
442         attr = attr_search(attrs, NULL, attr_icon_src);
443         if (attr) {
444                 struct file_wordexp *we;
445                 char **array;
446                 we = file_wordexp_new(attr->u.str);
447                 array = file_wordexp_get_array(we);
448                 this->icon_src = graphics_icon_path(array[0]);
449                 file_wordexp_destroy(we);
450         } else {
451                 this->icon_src = graphics_icon_path("%s_wh.svg");
452         }
453         
454         attr = attr_search(attrs, NULL, attr_level);
455         if (attr)
456                 this->level=attr->u.num;
457
458         navit_add_callback(nav, callback_new_attr_1(callback_cast(osd_nav_next_turn_init), attr_graphics_ready, this));
459         return (struct osd_priv *) this;
460 }
461
462 struct nav_toggle_announcer
463 {
464         int w,h;
465         struct callback *navit_init_cb;
466         struct osd_item item;
467         char *icon_src;
468         int icon_h, icon_w, active, last_state;
469 };
470
471 static void
472 osd_nav_toggle_announcer_draw(struct nav_toggle_announcer *this, struct navit *navit, struct vehicle *v)
473 {
474         struct point p;
475         int do_draw = 0;
476         struct graphics_image *gr_image;
477         char *path;
478         char *gui_sound_off = "gui_sound_off";
479         char *gui_sound_on = "gui_sound";
480     struct attr attr, speechattr;
481
482     if (this->last_state == -1)
483     {
484         if (!navit_get_attr(navit, attr_speech, &speechattr, NULL))
485             if (!speech_get_attr(speechattr.u.speech, attr_active, &attr, NULL))
486                 attr.u.num = 1;
487         this->active = attr.u.num;
488     } else
489         this->active = !this->active;
490
491     if(this->active != this->last_state)
492     {
493         this->last_state = this->active;
494         do_draw = 1;
495     }
496
497         if (do_draw)
498     {
499                 graphics_draw_mode(this->item.gr, draw_mode_begin);
500                 p.x = 0;
501                 p.y = 0;
502                 graphics_draw_rectangle(this->item.gr, this->item.graphic_bg, &p, this->item.w, this->item.h);
503
504                 if (this->active)
505             path = g_strdup_printf(this->icon_src, gui_sound_on);
506         else
507             path = g_strdup_printf(this->icon_src, gui_sound_off);
508         
509         gr_image = graphics_image_new_scaled(this->item.gr, path, this->icon_w, this->icon_h);
510         if (!gr_image)
511         {
512             g_free(path);
513             path = graphics_icon_path("unknown.xpm");
514             gr_image = graphics_image_new_scaled(this->item.gr, path, this->icon_w, this->icon_h);
515         }
516         
517         dbg(1, "gr_image=%p\n", gr_image);
518         
519         if (gr_image)
520         {
521             p.x = (this->item.w - gr_image->width) / 2;
522             p.y = (this->item.h - gr_image->height) / 2;
523             graphics_draw_image(this->item.gr, this->item.graphic_fg_white, &p, gr_image);
524             graphics_image_free(this->item.gr, gr_image);
525         }
526         
527         g_free(path);
528                 graphics_draw_mode(this->item.gr, draw_mode_end);
529         }
530 }
531
532 static void
533 osd_nav_toggle_announcer_init(struct nav_toggle_announcer *this, struct navit *nav)
534 {
535         osd_set_std_graphic(nav, &this->item, (struct osd_priv *)this);
536         navit_add_callback(nav, callback_new_attr_1(callback_cast(osd_nav_toggle_announcer_draw), attr_speech, this));
537     navit_add_callback(nav, this->navit_init_cb = callback_new_attr_1(callback_cast(osd_std_click), attr_button, &this->item));
538         osd_nav_toggle_announcer_draw(this, nav, NULL);
539 }
540
541 static struct osd_priv *
542 osd_nav_toggle_announcer_new(struct navit *nav, struct osd_methods *meth, struct attr **attrs)
543 {
544         struct nav_toggle_announcer *this = g_new0(struct nav_toggle_announcer, 1);
545     struct attr *attr;
546     char *command = "announcer_toggle()";
547
548         this->item.w = 48;
549         this->item.h = 48;
550         this->item.p.x = -64;
551         this->item.navit = nav;
552         this->item.p.y = 76;
553         this->item.meth.draw = osd_draw_cast(osd_nav_toggle_announcer_draw);
554
555         osd_set_std_attr(attrs, &this->item, 0);
556
557         this->icon_w = -1;
558         this->icon_h = -1;
559     this->last_state = -1;
560
561     attr = attr_search(attrs, NULL, attr_icon_src);
562         if (attr) {
563                 struct file_wordexp *we;
564                 char **array;
565                 we = file_wordexp_new(attr->u.str);
566                 array = file_wordexp_get_array(we);
567                 this->icon_src = g_strdup(array[0]);
568                 file_wordexp_destroy(we);
569         } else
570                 this->icon_src = graphics_icon_path("%s_32.xpm");
571
572     this->item.command = g_strdup(command);
573
574         navit_add_callback(nav, callback_new_attr_1(callback_cast(osd_nav_toggle_announcer_init), attr_graphics_ready, this));
575         return (struct osd_priv *) this;
576 }
577
578 struct osd_speed_warner {
579         struct osd_item item;
580         struct graphics_gc *red;
581         int width;
582         int active;
583         int d;
584 };
585
586 static void
587 osd_speed_warner_draw(struct osd_speed_warner *this, struct navit *navit, struct vehicle *v)
588 {
589         struct point p[4];
590         char *text="60";
591
592         osd_std_draw(&this->item);
593         p[0].x=this->item.w/2-this->d/4;
594         p[0].y=this->item.h/2-this->d/4;
595         graphics_draw_rectangle(this->item.gr, this->item.graphic_fg_white, p, this->d/2, this->d/2);
596         p[0].x=this->item.w/2;
597         p[0].y=this->item.h/2;
598         graphics_draw_circle(this->item.gr, this->item.graphic_fg_white, p, this->d/2);
599         graphics_draw_circle(this->item.gr, this->red, p, this->d-this->width*2);
600         graphics_get_text_bbox(this->item.gr, this->item.font, text, 0x10000, 0, p, 0);
601         p[0].x=(this->item.w-p[2].x)/2;
602         p[0].y=(this->item.h+p[2].y)/2-p[2].y;
603         graphics_draw_text(this->item.gr, this->item.graphic_fg_text, NULL, this->item.font, text, p, 0x10000, 0);
604         graphics_draw_mode(this->item.gr, draw_mode_end);
605 }
606
607 static void
608 osd_speed_warner_init(struct osd_speed_warner *this, struct navit *nav)
609 {
610         osd_set_std_graphic(nav, &this->item, (struct osd_priv *)this);
611         navit_add_callback(nav, callback_new_attr_1(callback_cast(osd_speed_warner_draw), attr_position_coord_geo, this));
612         this->red=graphics_gc_new(this->item.gr);
613         graphics_gc_set_foreground(this->red, &(struct color ){0xffff,0,0,0xffff});
614         graphics_gc_set_linewidth(this->red, this->width);
615         graphics_gc_set_linewidth(this->item.graphic_fg_white, this->d/4+2);
616         osd_speed_warner_draw(this, nav, NULL);
617 }
618
619 static struct osd_priv *
620 osd_speed_warner_new(struct navit *nav, struct osd_methods *meth, struct attr **attrs)
621 {
622         struct osd_speed_warner *this=g_new0(struct osd_speed_warner, 1);
623         this->item.p.x=-80;
624         this->item.p.y=20;
625         this->item.w=60;
626         this->item.navit = nav;
627         this->item.h=60;
628         this->active=-1;
629         this->item.meth.draw = osd_draw_cast(osd_speed_warner_draw);
630         osd_set_std_attr(attrs, &this->item, 2);
631         this->d=this->item.w;
632         if (this->item.h < this->d)
633                 this->d=this->item.h;
634         this->width=this->d/10;
635         navit_add_callback(nav, callback_new_attr_1(callback_cast(osd_speed_warner_init), attr_graphics_ready, this));
636         return (struct osd_priv *) this;
637 }
638
639 struct osd_text {
640         struct osd_item osd_item;
641         int active;
642         char *text;
643         int align;
644         char last_text[16];
645 };
646
647 static char *
648 osd_text_format_attr(struct attr *attr, char *format)
649 {
650         struct tm tm, text_tm, text_tm0;
651         time_t textt;
652         int days=0;
653         char buffer[1024];
654
655         switch (attr->type) {
656         case attr_position_speed:
657                 return format_speed(*attr->u.numd,"");
658         case attr_position_height:
659         case attr_position_direction:
660                 return format_float_0(*attr->u.numd);
661         case attr_position_magnetic_direction:
662                 return g_strdup_printf("%d",attr->u.num);
663         case attr_position_coord_geo:
664                 coord_format(attr->u.coord_geo->lat,attr->u.coord_geo->lng,DEGREES_MINUTES_SECONDS,buffer,sizeof(buffer));
665                 return g_strdup(buffer);
666         case attr_destination_time:
667                 if (!format || (strcmp(format,"arrival") && strcmp(format,"remaining")))
668                         break;
669                 textt = time(NULL);
670                 tm = *localtime(&textt);
671                 if (!strcmp(format,"remaining")) {
672                         textt-=tm.tm_hour*3600+tm.tm_min*60+tm.tm_sec;
673                         tm = *localtime(&textt);
674                 }
675                 textt += attr->u.num / 10;
676                 text_tm = *localtime(&textt);
677                 if (tm.tm_year != text_tm.tm_year || tm.tm_mon != text_tm.tm_mon || tm.tm_mday != text_tm.tm_mday) {
678                         text_tm0 = text_tm;
679                         text_tm0.tm_sec = 0;
680                         text_tm0.tm_min = 0;
681                         text_tm0.tm_hour = 0;
682                         tm.tm_sec = 0;
683                         tm.tm_min = 0;
684                         tm.tm_hour = 0;
685                         days = (mktime(&text_tm0) - mktime(&tm) + 43200) / 86400;
686                 }
687                 return format_time(&text_tm, days);
688         case attr_length:
689         case attr_destination_length:
690                 if (!format)
691                         break;
692                 if (!strcmp(format,"named"))
693                         return format_distance(attr->u.num,"");
694                 if (!strcmp(format,"value") || !strcmp(format,"unit")) {
695                         char *ret,*tmp=format_distance(attr->u.num," ");
696                         char *pos=strchr(tmp,' ');
697                         if (! pos)
698                                 return tmp;
699                         *pos++='\0';
700                         if (!strcmp(format,"value"))
701                                 return tmp;
702                         ret=g_strdup(pos);
703                         g_free(tmp);
704                         return ret;
705                 }
706         default:
707                 break;
708         }
709         return attr_to_text(attr, NULL, 1);
710 }
711
712 static char *
713 osd_text_split(char *in, char **index)
714 {
715         char *pos;
716         int len;
717         if (index)
718                 *index=NULL;
719         len=strcspn(in,"[.");
720         in+=len;
721         switch (in[0]) {
722         case '\0':
723                 return in;
724         case '.':
725                 *in++='\0';
726                 return in;
727         case '[':
728                 if (!index)
729                         return NULL;
730                 *in++='\0';
731                 *index=in;
732                 pos=strchr(in,']');
733                 if (pos) {
734                         *pos++='\0';
735                         if (*pos == '.') {
736                                 *pos++='\0';
737                         }
738                         return pos;
739                 }
740                 return NULL;
741         }
742         return NULL;
743 }
744
745 static void
746 osd_text_draw(struct osd_text *this, struct navit *navit, struct vehicle *v)
747 {
748         struct point p, p2[4];
749         char *str,*next,*last,*start,*end,*key,*subkey,*index,*value;
750         int do_draw = 0;
751         struct attr attr, vehicle_attr, maxspeed_attr;
752         struct navigation *nav = NULL;
753         struct tracking *tracking = NULL;
754         struct route *route = NULL;
755         struct map *nav_map = NULL;
756         struct map_rect *nav_mr = NULL;
757         struct item *item;
758         int offset,lines;
759         int height=this->osd_item.font_size*13/256;
760         int yspacing=height/2;
761         int xspacing=height/4;
762         enum attr_type attr_type;
763
764         vehicle_attr.u.vehicle=NULL;
765         do_draw=1;
766         str=g_strdup(this->text);
767         while ((start=strstr(str, "${"))) {
768                 item=NULL;
769                 end=strstr(str,"}");
770                 if (! end)
771                         break;
772                 *end++='\0';
773                 value=NULL;
774                 key=start+2;
775                 subkey=osd_text_split(key,NULL);
776                 if (!strcmp(key,"navigation") && subkey) {
777                         if (navit && !nav)
778                                 nav = navit_get_navigation(navit);
779                         if (nav && !nav_map)
780                                 nav_map = navigation_get_map(nav);
781                         if (nav_map) 
782                                 key=osd_text_split(subkey,&index);
783                         if (nav_map && !strcmp(subkey,"item")) {
784                                 if (nav_map)
785                                         nav_mr = map_rect_new(nav_map, NULL);
786                                 if (nav_mr) {
787                                         item = map_rect_get_item(nav_mr);
788                                 }
789                                 offset=0;
790                                 if (index)
791                                         offset=atoi(index);
792                                 while (item) {
793                                         if (item->type == type_nav_none) 
794                                                 item=map_rect_get_item(nav_mr);
795                                         else if (!offset)
796                                                 break;
797                                         else {
798                                                 offset--;
799                                                 item=map_rect_get_item(nav_mr);
800                                         }
801                                 }
802                                 if (item) {
803                                         dbg(1,"name %s\n", item_to_name(item->type));
804                                         subkey=osd_text_split(key,&index);
805                                         attr_type=attr_from_name(key);
806                                         dbg(1,"type %s\n", attr_to_name(attr_type));
807                                         if (item_attr_get(item, attr_type, &attr)) 
808                                                 value=osd_text_format_attr(&attr, index);
809                                         else
810                                                 dbg(1,"failed\n");
811                                 }
812                         }
813                 } else if (!strcmp(key,"tracking") && subkey) {
814                         if (navit) {
815                                 tracking = navit_get_tracking(navit);
816                                 route = navit_get_route(navit);
817                         }
818                         if (tracking) {
819                                 key=osd_text_split(subkey,&index);
820                                 if (!strcmp(subkey, "item") && key) {
821                                         item=tracking_get_current_item(tracking);
822                                         if (item && !strcmp(key,"route_speed")) {
823                                                 double routespeed = -1;
824                                                 int *flags=tracking_get_current_flags(tracking);
825                                                 if (flags && (*flags & AF_SPEED_LIMIT) && tracking_get_attr(tracking, attr_maxspeed, &maxspeed_attr, NULL)) {
826                                                         routespeed = maxspeed_attr.u.num;
827                                                         value = format_speed(routespeed, "");
828                                                 } 
829
830                                                 if (routespeed == -1) {
831                                                         struct vehicleprofile *prof=navit_get_vehicleprofile(navit);
832                                                         struct roadprofile *rprof=NULL;
833                                                         if (prof)
834                                                                 rprof=vehicleprofile_get_roadprofile(prof, item->type);
835                                                         if (rprof) {
836                                                                 routespeed=rprof->speed;
837                                                                 value=format_speed(routespeed,"");
838                                                         }
839                                                 }
840                                         } else if (item) {
841                                                 attr_type=attr_from_name(key);
842                                                 if (tracking_get_attr(tracking, attr_type, &attr, NULL)) 
843                                                         value=osd_text_format_attr(&attr, index);
844                                         }
845                                 }
846                         }
847                 } else if (!strcmp(key,"vehicle") && subkey) {
848                         if (navit && !vehicle_attr.u.vehicle) {
849                                 navit_get_attr(navit, attr_vehicle, &vehicle_attr, NULL);
850                         }
851                         if (vehicle_attr.u.vehicle) {
852                                 key=osd_text_split(subkey,&index);
853                                 attr_type=attr_from_name(subkey);
854                                 if (vehicle_get_attr(vehicle_attr.u.vehicle, attr_type, &attr, NULL)) {
855                                         value=osd_text_format_attr(&attr, index);
856                                 }
857                         }
858                                 
859                 } else if (!strcmp(key,"navit") && subkey) {
860                         if (navit) {
861                                 key = osd_text_split(subkey, &index);
862                                 if (!strcmp(subkey,"messages")) {
863                                         struct message *msg;
864                                         int len,offset;
865                                         char *tmp;
866                                         
867                                         msg = navit_get_messages(navit);
868                                         len = 0;
869                                         while (msg) {
870                                                 len+= strlen(msg->text) + 2;
871
872                                                 msg = msg->next;
873                                         }
874
875                                         value = g_malloc(len +1);
876
877                                         msg = navit_get_messages(navit);
878                                         offset = 0;
879                                         while (msg) {
880                                                 tmp = g_stpcpy((value+offset), msg->text);
881                                                 g_stpcpy(tmp, "\\n");
882                                                 offset += strlen(msg->text) + 2;
883
884                                                 msg = msg->next;
885                                         }
886
887                                         value[len] = '\0';
888                                 }
889                         }
890                 }
891                 *start='\0';
892                 next=g_strdup_printf("%s%s%s",str,value ? value:" ",end);
893                 g_free(value);
894                 g_free(str);
895                 str=next;
896         }
897         lines=0;
898         next=str;
899         last=str;
900         while ((next=strstr(next, "\\n"))) {
901                 last = next;
902                 lines++;
903                 next++;
904         }
905
906         while (*last) {
907                 if (! g_ascii_isspace(*last)) {
908                         lines++;
909                         break;
910                 }
911                 last++;
912         }
913
914         dbg(1,"this->align=%d\n", this->align);
915         switch (this->align & 51) {
916         case 1:
917                 p.y=0;
918                 break;
919         case 2:
920                 p.y=(this->osd_item.h-lines*(height+yspacing)-yspacing);
921                 break;
922         case 16: // Grow from top to bottom
923                 p.y = 0;
924                 if (lines != 0) {
925                         this->osd_item.h = (lines-1) * (height+yspacing) + height;
926                 } else {
927                         this->osd_item.h = 0;
928                 }
929
930                 if (do_draw) {
931                         osd_std_resize(&this->osd_item);
932                 }
933         default:
934                 p.y=(this->osd_item.h-lines*(height+yspacing)-yspacing)/2;
935         }
936         if (do_draw) {
937                 osd_std_draw(&this->osd_item);
938                 while (str) {
939                         next=strstr(str, "\\n");
940                         if (next) {
941                                 *next='\0';
942                                 next+=2;
943                         }
944                         graphics_get_text_bbox(this->osd_item.gr,
945                                                this->osd_item.font,
946                                                str, 0x10000,
947                                                0x0, p2, 0);
948                         switch (this->align & 12) {
949                         case 4:
950                                 p.x=xspacing;
951                                 break;
952                         case 8:
953                                 p.x=this->osd_item.w-(p2[2].x-p2[0].x)-xspacing;
954                                 break;
955                         default:
956                                 p.x = ((p2[0].x - p2[2].x) / 2) + (this->osd_item.w / 2);
957                         }
958                         p.y += height+yspacing;
959                         graphics_draw_text(this->osd_item.gr,
960                                            this->osd_item.graphic_fg_text,
961                                            NULL, this->osd_item.font,
962                                            str, &p, 0x10000,
963                                            0);
964                         str=next;
965                 }
966                 graphics_draw_mode(this->osd_item.gr, draw_mode_end);
967         }
968
969 }
970
971 static void
972 osd_text_init(struct osd_text *this, struct navit *nav)
973 {
974
975         osd_set_std_graphic(nav, &this->osd_item, (struct osd_priv *)this);
976         navit_add_callback(nav, callback_new_attr_1(callback_cast(osd_text_draw), attr_position_coord_geo, this));
977         navit_add_callback(nav, callback_new_attr_1(callback_cast(osd_std_click), attr_button, &this->osd_item));
978         osd_text_draw(this, nav, NULL);
979
980 }
981
982 static struct osd_priv *
983 osd_text_new(struct navit *nav, struct osd_methods *meth,
984             struct attr **attrs)
985 {
986         struct osd_text *this = g_new0(struct osd_text, 1);
987         struct attr *attr;
988
989         this->osd_item.p.x = -80;
990         this->osd_item.p.y = 20;
991         this->osd_item.w = 60;
992         this->osd_item.h = 20;
993         this->osd_item.navit = nav;
994         this->osd_item.font_size = 200;
995         this->osd_item.meth.draw = osd_draw_cast(osd_text_draw);
996         osd_set_std_attr(attrs, &this->osd_item, 2);
997
998         this->active = -1;
999
1000         attr = attr_search(attrs, NULL, attr_label);
1001         if (attr)
1002                 this->text = g_strdup(attr->u.str);
1003         else
1004                 this->text = NULL;
1005         attr = attr_search(attrs, NULL, attr_align);
1006         if (attr)
1007                 this->align=attr->u.num;
1008
1009         navit_add_callback(nav, callback_new_attr_1(callback_cast(osd_text_init), attr_graphics_ready, this));
1010         return (struct osd_priv *) this;
1011 }
1012
1013 struct gps_status {
1014         struct osd_item osd_item;
1015         char *icon_src;
1016         int icon_h, icon_w, active;
1017         int strength;
1018 };
1019
1020 static void
1021 osd_gps_status_draw(struct gps_status *this, struct navit *navit,
1022                        struct vehicle *v)
1023 {
1024         struct point p;
1025         int do_draw = 0;
1026         struct graphics_image *gr_image;
1027         char *image;
1028         struct attr attr, vehicle_attr;
1029         int strength=-1;
1030
1031         if (navit && navit_get_attr(navit, attr_vehicle, &vehicle_attr, NULL)) {
1032                 if (vehicle_get_attr(vehicle_attr.u.vehicle, attr_position_fix_type, &attr, NULL)) {
1033                         switch(attr.u.num) {
1034                         case 1:
1035                         case 2:
1036                                 strength=2;
1037                                 if (vehicle_get_attr(vehicle_attr.u.vehicle, attr_position_sats_used, &attr, NULL)) {
1038                                         dbg(1,"num=%d\n", attr.u.num);
1039                                         if (attr.u.num >= 3) 
1040                                                 strength=attr.u.num-1;
1041                                         if (strength > 5)
1042                                                 strength=5;
1043                                         if (strength > 3) {
1044                                                 if (vehicle_get_attr(vehicle_attr.u.vehicle, attr_position_hdop, &attr, NULL)) {
1045                                                         if (*attr.u.numd > 2.0 && strength > 4)
1046                                                                 strength=4;
1047                                                         if (*attr.u.numd > 4.0 && strength > 3)
1048                                                                 strength=3;
1049                                                 }
1050                                         }
1051                                 }
1052                                 break;
1053                         default:
1054                                 strength=1;
1055                         }
1056                 }
1057         }       
1058         if (this->strength != strength) {
1059                 this->strength=strength;
1060                 do_draw=1;
1061         }
1062         if (do_draw) {
1063                 osd_std_draw(&this->osd_item);
1064                 if (this->active) {
1065                         image = g_strdup_printf(this->icon_src, strength);
1066                         gr_image = graphics_image_new_scaled(this->osd_item.gr, image, this->icon_w, this->icon_h);
1067                         if (gr_image) {
1068                                 p.x = (this->osd_item.w - gr_image->width) / 2;
1069                                 p.y = (this->osd_item.h - gr_image->height) / 2;
1070                                 graphics_draw_image(this->osd_item.gr, this->osd_item.  graphic_fg_white, &p, gr_image);
1071                                 graphics_image_free(this->osd_item.gr, gr_image);
1072                         }
1073                         g_free(image);
1074                 }
1075                 graphics_draw_mode(this->osd_item.gr, draw_mode_end);
1076         }
1077 }
1078
1079 static void
1080 osd_gps_status_init(struct gps_status *this, struct navit *nav)
1081 {
1082         osd_set_std_graphic(nav, &this->osd_item, (struct osd_priv *)this);
1083         navit_add_callback(nav, callback_new_attr_1(callback_cast(osd_gps_status_draw), attr_position_coord_geo, this));
1084         osd_gps_status_draw(this, nav, NULL);
1085 }
1086
1087 static struct osd_priv *
1088 osd_gps_status_new(struct navit *nav, struct osd_methods *meth,
1089                       struct attr **attrs)
1090 {
1091         struct gps_status *this = g_new0(struct gps_status, 1);
1092         struct attr *attr;
1093
1094         this->osd_item.p.x = 20;
1095         this->osd_item.p.y = -80;
1096         this->osd_item.w = 60;
1097         this->osd_item.navit = nav;
1098         this->osd_item.h = 40;
1099         this->osd_item.font_size = 200;
1100         this->osd_item.meth.draw = osd_draw_cast(osd_gps_status_draw);
1101         osd_set_std_attr(attrs, &this->osd_item, 0);
1102
1103         this->icon_w = -1;
1104         this->icon_h = -1;
1105         this->active = -1;
1106         this->strength = -2;
1107
1108         attr = attr_search(attrs, NULL, attr_icon_w);
1109         if (attr)
1110                 this->icon_w = attr->u.num;
1111
1112         attr = attr_search(attrs, NULL, attr_icon_h);
1113         if (attr)
1114                 this->icon_h = attr->u.num;
1115
1116         attr = attr_search(attrs, NULL, attr_icon_src);
1117         if (attr) {
1118                 struct file_wordexp *we;
1119                 char **array;
1120                 we = file_wordexp_new(attr->u.str);
1121                 array = file_wordexp_get_array(we);
1122                 this->icon_src = g_strdup(array[0]);
1123                 file_wordexp_destroy(we);
1124         } else
1125                 this->icon_src = graphics_icon_path("gui_strength_%d_32_32.png");
1126
1127         navit_add_callback(nav, callback_new_attr_1(callback_cast(osd_gps_status_init), attr_graphics_ready, this));
1128         return (struct osd_priv *) this;
1129 }
1130
1131
1132 struct volume {
1133         struct osd_item osd_item;
1134         char *icon_src;
1135         int icon_h, icon_w, active;
1136         int strength;
1137         struct callback *click_cb;
1138 };
1139
1140 static void
1141 osd_volume_draw(struct volume *this, struct navit *navit)
1142 {
1143         struct point p;
1144         struct graphics_image *gr_image;
1145         char *image;
1146
1147         osd_std_draw(&this->osd_item);
1148         if (this->active) {
1149                 image = g_strdup_printf(this->icon_src, this->strength);
1150                 gr_image = graphics_image_new_scaled(this->osd_item.gr, image, this->icon_w, this->icon_h);
1151                 if (gr_image) {
1152                         p.x = (this->osd_item.w - gr_image->width) / 2;
1153                         p.y = (this->osd_item.h - gr_image->height) / 2;
1154                         graphics_draw_image(this->osd_item.gr, this->osd_item.  graphic_fg_white, &p, gr_image);
1155                         graphics_image_free(this->osd_item.gr, gr_image);
1156                 }
1157                 g_free(image);
1158         }
1159         graphics_draw_mode(this->osd_item.gr, draw_mode_end);
1160 }
1161
1162 static void
1163 osd_volume_click(struct volume *this, struct navit *nav, int pressed, int button, struct point *p)
1164 {
1165         struct point bp = this->osd_item.p;
1166         osd_wrap_point(&bp, nav);
1167         if ((p->x < bp.x || p->y < bp.y || p->x > bp.x + this->osd_item.w || p->y > bp.y + this->osd_item.h) && !this->osd_item.pressed)
1168                 return;
1169         navit_ignore_button(nav);
1170         if (pressed) {
1171                 if (p->y - bp.y < this->osd_item.h/2)
1172                         this->strength++;
1173                 else
1174                         this->strength--;
1175                 if (this->strength < 0)
1176                         this->strength=0;
1177                 if (this->strength > 5)
1178                         this->strength=5;
1179                 osd_volume_draw(this, nav);
1180         }
1181 }
1182 static void
1183 osd_volume_init(struct volume *this, struct navit *nav)
1184 {
1185         osd_set_std_graphic(nav, &this->osd_item, (struct osd_priv *)this);
1186         navit_add_callback(nav, this->click_cb = callback_new_attr_1(callback_cast (osd_volume_click), attr_button, this));
1187         osd_volume_draw(this, nav);
1188 }
1189
1190 static struct osd_priv *
1191 osd_volume_new(struct navit *nav, struct osd_methods *meth,
1192                       struct attr **attrs)
1193 {
1194         struct volume *this = g_new0(struct volume, 1);
1195         struct attr *attr;
1196
1197         this->osd_item.p.x = 20;
1198         this->osd_item.p.y = -80;
1199         this->osd_item.w = 60;
1200         this->osd_item.navit = nav;
1201         this->osd_item.h = 40;
1202         this->osd_item.font_size = 200;
1203         this->osd_item.meth.draw = osd_draw_cast(osd_volume_draw);
1204         osd_set_std_attr(attrs, &this->osd_item, 0);
1205
1206         this->icon_w = -1;
1207         this->icon_h = -1;
1208         this->active = -1;
1209         this->strength = -1;
1210
1211         attr = attr_search(attrs, NULL, attr_icon_w);
1212         if (attr)
1213                 this->icon_w = attr->u.num;
1214
1215         attr = attr_search(attrs, NULL, attr_icon_h);
1216         if (attr)
1217                 this->icon_h = attr->u.num;
1218
1219         attr = attr_search(attrs, NULL, attr_icon_src);
1220         if (attr) {
1221                 struct file_wordexp *we;
1222                 char **array;
1223                 we = file_wordexp_new(attr->u.str);
1224                 array = file_wordexp_get_array(we);
1225                 this->icon_src = g_strdup(array[0]);
1226                 file_wordexp_destroy(we);
1227         } else
1228                 this->icon_src = graphics_icon_path("gui_strength_%d_32_32.png");
1229
1230         navit_add_callback(nav, callback_new_attr_1(callback_cast(osd_volume_init), attr_graphics_ready, this));
1231         return (struct osd_priv *) this;
1232 }
1233
1234
1235 void
1236 plugin_init(void)
1237 {
1238         plugin_register_osd_type("compass", osd_compass_new);
1239         plugin_register_osd_type("navigation_next_turn", osd_nav_next_turn_new);
1240         plugin_register_osd_type("button", osd_button_new);
1241         plugin_register_osd_type("toggle_announcer", osd_nav_toggle_announcer_new);
1242         plugin_register_osd_type("speed_warner", osd_speed_warner_new);
1243         plugin_register_osd_type("text", osd_text_new);
1244         plugin_register_osd_type("gps_status", osd_gps_status_new);
1245         plugin_register_osd_type("volume", osd_volume_new);
1246 }