fix red
[maemo-rb.git] / apps / gui / skin_engine / skin_parser.c
blobd6091f9441de8c21d7c6aeb885bb0c2b6055009c
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2007 Nicolas Pennequin, Dan Everton, Matthias Mohr
11 * 2010 Jonathan Gordon
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include "config.h"
27 #include "core_alloc.h"
28 #include "file.h"
29 #include "misc.h"
30 #include "plugin.h"
31 #include "viewport.h"
33 #include "skin_buffer.h"
34 #include "skin_parser.h"
35 #include "tag_table.h"
37 #ifdef __PCTOOL__
38 #ifdef WPSEDITOR
39 #include "proxy.h"
40 #include "sysfont.h"
41 #else
42 #include "action.h"
43 #include "checkwps.h"
44 #include "audio.h"
45 #define lang_is_rtl() (false)
46 #define DEBUGF printf
47 #endif /*WPSEDITOR*/
48 #else
49 #include "debug.h"
50 #include "language.h"
51 #endif /*__PCTOOL__*/
53 #include <ctype.h>
54 #include <stdbool.h>
55 #include "font.h"
57 #include "wps_internals.h"
58 #include "skin_engine.h"
59 #include "settings.h"
60 #include "settings_list.h"
61 #if CONFIG_TUNER
62 #include "radio.h"
63 #include "tuner.h"
64 #endif
65 #include "skin_fonts.h"
67 #ifdef HAVE_LCD_BITMAP
68 #include "bmp.h"
69 #endif
71 #ifdef HAVE_ALBUMART
72 #include "playback.h"
73 #endif
75 #include "backdrop.h"
76 #include "statusbar-skinned.h"
78 #define WPS_ERROR_INVALID_PARAM -1
81 static bool isdefault(struct skin_tag_parameter *param)
83 return param->type == DEFAULT;
87 /* which screen are we parsing for? */
88 static enum screen_type curr_screen;
90 /* the current viewport */
91 static struct skin_element *curr_viewport_element;
92 static struct skin_viewport *curr_vp;
94 static struct line *curr_line;
96 static int follow_lang_direction = 0;
98 typedef int (*parse_function)(struct skin_element *element,
99 struct wps_token *token,
100 struct wps_data *wps_data);
102 #ifdef HAVE_LCD_BITMAP
103 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
104 * chains require the order to be kept.
106 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
108 if (*list == NULL)
109 *list = item;
110 else
112 struct skin_token_list *t = *list;
113 while (t->next)
114 t = t->next;
115 t->next = item;
119 #endif
122 void *skin_find_item(const char *label, enum skin_find_what what,
123 struct wps_data *data)
125 const char *itemlabel = NULL;
126 union {
127 struct skin_token_list *linkedlist;
128 struct skin_element *vplist;
129 } list = {NULL};
130 bool isvplist = false;
131 void *ret = NULL;
132 switch (what)
134 case SKIN_FIND_UIVP:
135 case SKIN_FIND_VP:
136 list.vplist = data->tree;
137 isvplist = true;
138 break;
139 #ifdef HAVE_LCD_BITMAP
140 case SKIN_FIND_IMAGE:
141 list.linkedlist = data->images;
142 break;
143 #endif
144 #ifdef HAVE_TOUCHSCREEN
145 case SKIN_FIND_TOUCHREGION:
146 list.linkedlist = data->touchregions;
147 break;
148 #endif
149 #ifdef HAVE_SKIN_VARIABLES
150 case SKIN_VARIABLE:
151 list.linkedlist = data->skinvars;
152 break;
153 #endif
156 while (list.linkedlist)
158 bool skip = false;
159 switch (what)
161 case SKIN_FIND_UIVP:
162 case SKIN_FIND_VP:
163 ret = list.vplist->data;
164 itemlabel = ((struct skin_viewport *)ret)->label;
165 skip = !(((struct skin_viewport *)ret)->is_infovp ==
166 (what==SKIN_FIND_UIVP));
167 break;
168 #ifdef HAVE_LCD_BITMAP
169 case SKIN_FIND_IMAGE:
170 ret = list.linkedlist->token->value.data;
171 itemlabel = ((struct gui_img *)ret)->label;
172 break;
173 #endif
174 #ifdef HAVE_TOUCHSCREEN
175 case SKIN_FIND_TOUCHREGION:
176 ret = list.linkedlist->token->value.data;
177 itemlabel = ((struct touchregion *)ret)->label;
178 break;
179 #endif
180 #ifdef HAVE_SKIN_VARIABLES
181 case SKIN_VARIABLE:
182 ret = list.linkedlist->token->value.data;
183 itemlabel = ((struct skin_var *)ret)->label;
184 break;
185 #endif
188 if (!skip && itemlabel && !strcmp(itemlabel, label))
189 return ret;
191 if (isvplist)
192 list.vplist = list.vplist->next;
193 else
194 list.linkedlist = list.linkedlist->next;
196 return NULL;
199 #ifdef HAVE_LCD_BITMAP
201 /* create and init a new wpsll item.
202 * passing NULL to token will alloc a new one.
203 * You should only pass NULL for the token when the token type (table above)
204 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
206 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
207 void* token_data)
209 struct skin_token_list *llitem =
210 (struct skin_token_list *)skin_buffer_alloc(sizeof(struct skin_token_list));
211 if (!token)
212 token = (struct wps_token*)skin_buffer_alloc(sizeof(struct wps_token));
213 if (!llitem || !token)
214 return NULL;
215 llitem->next = NULL;
216 llitem->token = token;
217 if (token_data)
218 llitem->token->value.data = token_data;
219 return llitem;
222 static int parse_statusbar_tags(struct skin_element* element,
223 struct wps_token *token,
224 struct wps_data *wps_data)
226 (void)element;
227 if (token->type == SKIN_TOKEN_DRAW_INBUILTBAR)
229 token->value.data = (void*)&curr_vp->vp;
231 else
233 struct skin_element *def_vp = wps_data->tree;
234 struct skin_viewport *default_vp = def_vp->data;
235 if (def_vp->params_count == 0)
237 wps_data->wps_sb_tag = true;
238 wps_data->show_sb_on_wps = (token->type == SKIN_TOKEN_ENABLE_THEME);
240 if (wps_data->show_sb_on_wps)
242 viewport_set_defaults(&default_vp->vp, curr_screen);
244 else
246 viewport_set_fullscreen(&default_vp->vp, curr_screen);
248 #ifdef HAVE_REMOTE_LCD
249 /* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
250 * This parser requires font 1 to always be the UI font,
251 * so force it back to FONT_UI and handle the screen number at the end */
252 default_vp->vp.font = FONT_UI;
253 #endif
255 return 0;
258 static int get_image_id(int c)
260 if(c >= 'a' && c <= 'z')
261 return c - 'a';
262 else if(c >= 'A' && c <= 'Z')
263 return c - 'A' + 26;
264 else
265 return -1;
268 char *get_image_filename(const char *start, const char* bmpdir,
269 char *buf, int buf_size)
271 snprintf(buf, buf_size, "%s/%s", bmpdir, start);
273 return buf;
276 static int parse_image_display(struct skin_element *element,
277 struct wps_token *token,
278 struct wps_data *wps_data)
280 char *label = element->params[0].data.text;
281 char sublabel = '\0';
282 int subimage;
283 struct gui_img *img;
284 struct image_display *id = skin_buffer_alloc(sizeof(struct image_display));
286 if (element->params_count == 1 && strlen(label) <= 2)
288 /* backwards compatability. Allow %xd(Aa) to still work */
289 sublabel = label[1];
290 label[1] = '\0';
292 /* sanity check */
293 img = skin_find_item(label, SKIN_FIND_IMAGE, wps_data);
294 if (!img || !id)
296 return WPS_ERROR_INVALID_PARAM;
298 id->label = label;
299 id->offset = 0;
300 id->token = NULL;
301 if (img->using_preloaded_icons)
303 token->type = SKIN_TOKEN_IMAGE_DISPLAY_LISTICON;
306 if (element->params_count > 1)
308 if (element->params[1].type == CODE)
309 id->token = element->params[1].data.code->data;
310 /* specify a number. 1 being the first subimage (i.e top) NOT 0 */
311 else if (element->params[1].type == INTEGER)
312 id->subimage = element->params[1].data.number - 1;
313 if (element->params_count > 2)
314 id->offset = element->params[2].data.number;
316 else
318 if ((subimage = get_image_id(sublabel)) != -1)
320 if (subimage >= img->num_subimages)
321 return WPS_ERROR_INVALID_PARAM;
322 id->subimage = subimage;
323 } else {
324 id->subimage = 0;
327 token->value.data = id;
328 return 0;
331 static int parse_image_load(struct skin_element *element,
332 struct wps_token *token,
333 struct wps_data *wps_data)
335 const char* filename;
336 const char* id;
337 int x,y;
338 struct gui_img *img;
340 /* format: %x(n,filename.bmp,x,y)
341 or %xl(n,filename.bmp,x,y)
342 or %xl(n,filename.bmp,x,y,num_subimages)
345 id = element->params[0].data.text;
346 filename = element->params[1].data.text;
347 x = element->params[2].data.number;
348 y = element->params[3].data.number;
350 /* check the image number and load state */
351 if(skin_find_item(id, SKIN_FIND_IMAGE, wps_data))
353 /* Invalid image ID */
354 return WPS_ERROR_INVALID_PARAM;
356 img = (struct gui_img*)skin_buffer_alloc(sizeof(struct gui_img));
357 if (!img)
358 return WPS_ERROR_INVALID_PARAM;
359 /* save a pointer to the filename */
360 img->bm.data = (char*)filename;
361 img->label = id;
362 img->x = x;
363 img->y = y;
364 img->num_subimages = 1;
365 img->always_display = false;
366 img->display = -1;
367 img->using_preloaded_icons = false;
368 img->buflib_handle = -1;
370 /* save current viewport */
371 img->vp = &curr_vp->vp;
373 if (token->type == SKIN_TOKEN_IMAGE_DISPLAY)
375 img->always_display = true;
377 else if (element->params_count == 5)
379 img->num_subimages = element->params[4].data.number;
380 if (img->num_subimages <= 0)
381 return WPS_ERROR_INVALID_PARAM;
384 if (!strcmp(img->bm.data, "__list_icons__"))
386 img->num_subimages = Icon_Last_Themeable;
387 img->using_preloaded_icons = true;
390 struct skin_token_list *item =
391 (struct skin_token_list *)new_skin_token_list_item(NULL, img);
392 if (!item)
393 return WPS_ERROR_INVALID_PARAM;
394 add_to_ll_chain(&wps_data->images, item);
396 return 0;
398 struct skin_font {
399 int id; /* the id from font_load */
400 char *name; /* filename without path and extension */
401 int glyphs; /* how many glyphs to reserve room for */
403 static struct skin_font skinfonts[MAXUSERFONTS];
404 static int parse_font_load(struct skin_element *element,
405 struct wps_token *token,
406 struct wps_data *wps_data)
408 (void)wps_data; (void)token;
409 int id = element->params[0].data.number;
410 char *filename = element->params[1].data.text;
411 int glyphs;
412 char *ptr;
414 if(element->params_count > 2)
415 glyphs = element->params[2].data.number;
416 else
417 glyphs = GLYPHS_TO_CACHE;
418 #if defined(DEBUG) || defined(SIMULATOR)
419 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
421 DEBUGF("font id %d already being used\n", id);
423 #endif
424 /* make sure the filename contains .fnt,
425 * we dont actually use it, but require it anyway */
426 ptr = strchr(filename, '.');
427 if (!ptr || strncmp(ptr, ".fnt", 4))
428 return WPS_ERROR_INVALID_PARAM;
429 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
430 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
431 skinfonts[id-FONT_FIRSTUSERFONT].glyphs = glyphs;
433 return 0;
437 #ifdef HAVE_LCD_BITMAP
439 static int parse_playlistview(struct skin_element *element,
440 struct wps_token *token,
441 struct wps_data *wps_data)
443 (void)wps_data;
444 struct playlistviewer *viewer =
445 (struct playlistviewer *)skin_buffer_alloc(sizeof(struct playlistviewer));
446 if (!viewer)
447 return WPS_ERROR_INVALID_PARAM;
448 viewer->vp = &curr_vp->vp;
449 viewer->show_icons = true;
450 viewer->start_offset = element->params[0].data.number;
451 viewer->line = element->params[1].data.code;
453 token->value.data = (void*)viewer;
455 return 0;
457 #endif
458 #ifdef HAVE_LCD_COLOR
459 static int parse_viewport_gradient_setup(struct skin_element *element,
460 struct wps_token *token,
461 struct wps_data *wps_data)
463 (void)wps_data;
464 struct gradient_config *cfg;
465 if (element->params_count < 2) /* only start and end are required */
466 return 1;
467 cfg = (struct gradient_config *)skin_buffer_alloc(sizeof(struct gradient_config));
468 if (!cfg)
469 return 1;
470 if (!parse_color(curr_screen, element->params[0].data.text, &cfg->start) ||
471 !parse_color(curr_screen, element->params[1].data.text, &cfg->end))
472 return 1;
473 if (element->params_count > 2)
475 if (!parse_color(curr_screen, element->params[2].data.text, &cfg->text))
476 return 1;
478 else
480 cfg->text = curr_vp->vp.fg_pattern;
483 token->value.data = cfg;
484 return 0;
486 #endif
488 static int parse_listitem(struct skin_element *element,
489 struct wps_token *token,
490 struct wps_data *wps_data)
492 (void)wps_data;
493 struct listitem *li = (struct listitem *)skin_buffer_alloc(sizeof(struct listitem));
494 if (!li)
495 return 1;
496 token->value.data = li;
497 if (element->params_count == 0)
498 li->offset = 0;
499 else
501 li->offset = element->params[0].data.number;
502 if (element->params_count > 1)
503 li->wrap = strcasecmp(element->params[1].data.text, "nowrap") != 0;
504 else
505 li->wrap = true;
507 return 0;
510 static int parse_listitemviewport(struct skin_element *element,
511 struct wps_token *token,
512 struct wps_data *wps_data)
514 #ifndef __PCTOOL__
515 struct listitem_viewport_cfg *cfg =
516 (struct listitem_viewport_cfg *)skin_buffer_alloc(
517 sizeof(struct listitem_viewport_cfg));
518 if (!cfg)
519 return -1;
520 cfg->data = wps_data;
521 cfg->tile = false;
522 cfg->label = element->params[0].data.text;
523 cfg->width = -1;
524 cfg->height = -1;
525 if (!isdefault(&element->params[1]))
526 cfg->width = element->params[1].data.number;
527 if (!isdefault(&element->params[2]))
528 cfg->height = element->params[2].data.number;
529 if (element->params_count > 3 &&
530 !strcmp(element->params[3].data.text, "tile"))
531 cfg->tile = true;
532 token->value.data = (void*)cfg;
533 #endif
534 return 0;
537 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
538 static int parse_viewporttextstyle(struct skin_element *element,
539 struct wps_token *token,
540 struct wps_data *wps_data)
542 (void)wps_data;
543 int style;
544 char *mode = element->params[0].data.text;
545 unsigned colour;
547 if (!strcmp(mode, "invert"))
549 style = STYLE_INVERT;
551 else if (!strcmp(mode, "colour") || !strcmp(mode, "color"))
553 if (element->params_count < 2 ||
554 !parse_color(curr_screen, element->params[1].data.text, &colour))
555 return 1;
556 style = STYLE_COLORED|(STYLE_COLOR_MASK&colour);
558 #ifdef HAVE_LCD_COLOR
559 else if (!strcmp(mode, "gradient"))
561 int num_lines;
562 if (element->params_count < 2)
563 num_lines = 1;
564 else /* atoi() instead of using a number in the parser is because [si]
565 * will select the number for something which looks like a colour
566 * making the "colour" case (above) harder to parse */
567 num_lines = atoi(element->params[1].data.text);
568 style = STYLE_GRADIENT|NUMLN_PACK(num_lines)|CURLN_PACK(0);
570 #endif
571 else if (!strcmp(mode, "clear"))
573 style = STYLE_DEFAULT;
575 else
576 return 1;
577 token->value.l = style;
578 return 0;
581 static int parse_viewportcolour(struct skin_element *element,
582 struct wps_token *token,
583 struct wps_data *wps_data)
585 (void)wps_data;
586 struct skin_tag_parameter *param = element->params;
587 struct viewport_colour *colour =
588 (struct viewport_colour *)skin_buffer_alloc(sizeof(struct viewport_colour));
589 if (!colour)
590 return -1;
591 if (isdefault(param))
593 colour->colour = get_viewport_default_colour(curr_screen,
594 token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR);
596 else
598 if (!parse_color(curr_screen, param->data.text, &colour->colour))
599 return -1;
601 colour->vp = &curr_vp->vp;
602 token->value.data = colour;
603 if (element->line == curr_viewport_element->line)
605 if (token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR)
607 curr_vp->start_fgcolour = colour->colour;
608 curr_vp->vp.fg_pattern = colour->colour;
610 else
612 curr_vp->start_bgcolour = colour->colour;
613 curr_vp->vp.bg_pattern = colour->colour;
616 return 0;
619 static int parse_image_special(struct skin_element *element,
620 struct wps_token *token,
621 struct wps_data *wps_data)
623 (void)wps_data; /* kill warning */
624 (void)token;
626 #if LCD_DEPTH > 1
627 char *filename;
628 if (token->type == SKIN_TOKEN_IMAGE_BACKDROP)
630 if (isdefault(&element->params[0]))
632 filename = "-";
634 else
636 filename = element->params[0].data.text;
637 /* format: %X(filename.bmp) or %X(d) */
638 if (!strcmp(filename, "d"))
639 filename = NULL;
641 wps_data->backdrop = filename;
643 #endif
645 return 0;
647 #endif
649 #endif /* HAVE_LCD_BITMAP */
651 static int parse_setting_and_lang(struct skin_element *element,
652 struct wps_token *token,
653 struct wps_data *wps_data)
655 /* NOTE: both the string validations that happen in here will
656 * automatically PASS on checkwps because its too hard to get
657 * settings_list.c and english.lang built for it.
658 * If that ever changes remove the #ifndef __PCTOOL__'s here
660 (void)wps_data;
661 char *temp = element->params[0].data.text;
662 int i;
664 if (token->type == SKIN_TOKEN_TRANSLATEDSTRING)
666 #ifndef __PCTOOL__
667 i = lang_english_to_id(temp);
668 if (i < 0)
669 return WPS_ERROR_INVALID_PARAM;
670 #endif
672 else
674 #ifndef __PCTOOL__
675 if (find_setting_by_cfgname(temp, &i) == NULL)
676 return WPS_ERROR_INVALID_PARAM;
677 #endif
679 /* Store the setting number */
680 token->value.i = i;
681 return 0;
683 static int parse_logical_if(struct skin_element *element,
684 struct wps_token *token,
685 struct wps_data *wps_data)
687 (void)wps_data;
688 char *op = element->params[1].data.text;
689 struct logical_if *lif = skin_buffer_alloc(sizeof(struct logical_if));
690 if (!lif)
691 return -1;
692 token->value.data = lif;
693 lif->token = element->params[0].data.code->data;
695 if (!strncmp(op, "=", 1))
696 lif->op = IF_EQUALS;
697 else if (!strncmp(op, "!=", 2))
698 lif->op = IF_NOTEQUALS;
699 else if (!strncmp(op, ">=", 2))
700 lif->op = IF_GREATERTHAN_EQ;
701 else if (!strncmp(op, "<=", 2))
702 lif->op = IF_LESSTHAN_EQ;
703 else if (!strncmp(op, ">", 2))
704 lif->op = IF_GREATERTHAN;
705 else if (!strncmp(op, "<", 1))
706 lif->op = IF_LESSTHAN;
708 memcpy(&lif->operand, &element->params[2], sizeof(lif->operand));
709 if (element->params_count > 3)
710 lif->num_options = element->params[3].data.number;
711 else
712 lif->num_options = TOKEN_VALUE_ONLY;
713 return 0;
717 static int parse_timeout_tag(struct skin_element *element,
718 struct wps_token *token,
719 struct wps_data *wps_data)
721 (void)wps_data;
722 int val = 0;
723 if (element->params_count == 0)
725 switch (token->type)
727 case SKIN_TOKEN_SUBLINE_TIMEOUT:
728 return -1;
729 case SKIN_TOKEN_BUTTON_VOLUME:
730 case SKIN_TOKEN_TRACK_STARTING:
731 case SKIN_TOKEN_TRACK_ENDING:
732 val = 10;
733 break;
734 default:
735 break;
738 else
739 val = element->params[0].data.number;
740 token->value.i = val * TIMEOUT_UNIT;
741 return 0;
744 static int parse_substring_tag(struct skin_element* element,
745 struct wps_token *token,
746 struct wps_data *wps_data)
748 (void)wps_data;
749 struct substring *ss = (struct substring*)skin_buffer_alloc(sizeof(struct substring));
750 if (!ss)
751 return 1;
752 ss->start = element->params[0].data.number;
753 if (element->params[1].type == DEFAULT)
754 ss->length = -1;
755 else
756 ss->length = element->params[1].data.number;
757 ss->token = element->params[2].data.code->data;
758 token->value.data = ss;
759 return 0;
762 static int parse_progressbar_tag(struct skin_element* element,
763 struct wps_token *token,
764 struct wps_data *wps_data)
766 #ifdef HAVE_LCD_BITMAP
767 struct progressbar *pb;
768 struct viewport *vp = &curr_vp->vp;
769 struct skin_tag_parameter *param = element->params;
770 int curr_param = 0;
771 char *image_filename = NULL;
773 if (element->params_count == 0 &&
774 element->tag->type != SKIN_TOKEN_PROGRESSBAR)
775 return 0; /* nothing to do */
776 pb = (struct progressbar*)skin_buffer_alloc(sizeof(struct progressbar));
778 token->value.data = pb;
780 if (!pb)
781 return WPS_ERROR_INVALID_PARAM;
782 pb->vp = vp;
783 pb->follow_lang_direction = follow_lang_direction > 0;
784 pb->nofill = false;
785 pb->nobar = false;
786 pb->image = NULL;
787 pb->slider = NULL;
788 pb->backdrop = NULL;
789 pb->invert_fill_direction = false;
790 pb->horizontal = true;
792 if (element->params_count == 0)
794 pb->x = 0;
795 pb->width = vp->width;
796 pb->height = SYSFONT_HEIGHT-2;
797 pb->y = -1; /* Will be computed during the rendering */
798 pb->type = element->tag->type;
799 return 0;
802 /* (x, y, width, height, ...) */
803 if (!isdefault(param))
804 pb->x = param->data.number;
805 else
806 pb->x = 0;
807 param++;
809 if (!isdefault(param))
810 pb->y = param->data.number;
811 else
812 pb->y = -1; /* computed at rendering */
813 param++;
815 if (!isdefault(param))
816 pb->width = param->data.number;
817 else
818 pb->width = vp->width - pb->x;
819 param++;
821 if (!isdefault(param))
823 /* A zero height makes no sense - reject it */
824 if (param->data.number == 0)
825 return WPS_ERROR_INVALID_PARAM;
827 pb->height = param->data.number;
829 else
831 if (vp->font > FONT_UI)
832 pb->height = -1; /* calculate at display time */
833 else
835 #ifndef __PCTOOL__
836 pb->height = font_get(vp->font)->height;
837 #else
838 pb->height = 8;
839 #endif
842 /* optional params, first is the image filename if it isnt recognised as a keyword */
844 curr_param = 4;
845 if (isdefault(&element->params[curr_param]))
847 param++;
848 curr_param++;
851 pb->horizontal = pb->width > pb->height;
852 while (curr_param < element->params_count)
854 param++;
855 if (!strcmp(param->data.text, "invert"))
856 pb->invert_fill_direction = true;
857 else if (!strcmp(param->data.text, "nofill"))
858 pb->nofill = true;
859 else if (!strcmp(param->data.text, "nobar"))
860 pb->nobar = true;
861 else if (!strcmp(param->data.text, "slider"))
863 if (curr_param+1 < element->params_count)
865 curr_param++;
866 param++;
867 pb->slider = skin_find_item(param->data.text,
868 SKIN_FIND_IMAGE, wps_data);
870 else /* option needs the next param */
871 return -1;
873 else if (!strcmp(param->data.text, "image"))
875 if (curr_param+1 < element->params_count)
877 curr_param++;
878 param++;
879 image_filename = param->data.text;
882 else /* option needs the next param */
883 return -1;
885 else if (!strcmp(param->data.text, "backdrop"))
887 if (curr_param+1 < element->params_count)
889 curr_param++;
890 param++;
891 pb->backdrop = skin_find_item(param->data.text,
892 SKIN_FIND_IMAGE, wps_data);
895 else /* option needs the next param */
896 return -1;
898 else if (!strcmp(param->data.text, "vertical"))
900 pb->horizontal = false;
901 if (isdefault(&element->params[3]))
902 pb->height = vp->height - pb->y;
904 else if (!strcmp(param->data.text, "horizontal"))
905 pb->horizontal = true;
906 else if (curr_param == 4)
907 image_filename = param->data.text;
909 curr_param++;
912 if (image_filename)
914 pb->image = skin_find_item(image_filename, SKIN_FIND_IMAGE, wps_data);
915 if (!pb->image) /* load later */
917 struct gui_img* img = (struct gui_img*)skin_buffer_alloc(sizeof(struct gui_img));
918 if (!img)
919 return WPS_ERROR_INVALID_PARAM;
920 /* save a pointer to the filename */
921 img->bm.data = (char*)image_filename;
922 img->label = image_filename;
923 img->x = 0;
924 img->y = 0;
925 img->num_subimages = 1;
926 img->always_display = false;
927 img->display = -1;
928 img->using_preloaded_icons = false;
929 img->buflib_handle = -1;
930 img->vp = &curr_vp->vp;
931 struct skin_token_list *item =
932 (struct skin_token_list *)new_skin_token_list_item(NULL, img);
933 if (!item)
934 return WPS_ERROR_INVALID_PARAM;
935 add_to_ll_chain(&wps_data->images, item);
936 pb->image = img;
940 if (token->type == SKIN_TOKEN_VOLUME)
941 token->type = SKIN_TOKEN_VOLUMEBAR;
942 else if (token->type == SKIN_TOKEN_BATTERY_PERCENT)
943 token->type = SKIN_TOKEN_BATTERY_PERCENTBAR;
944 else if (token->type == SKIN_TOKEN_TUNER_RSSI)
945 token->type = SKIN_TOKEN_TUNER_RSSI_BAR;
946 else if (token->type == SKIN_TOKEN_PEAKMETER_LEFT)
947 token->type = SKIN_TOKEN_PEAKMETER_LEFTBAR;
948 else if (token->type == SKIN_TOKEN_PEAKMETER_RIGHT)
949 token->type = SKIN_TOKEN_PEAKMETER_RIGHTBAR;
950 else if (token->type == SKIN_TOKEN_LIST_NEEDS_SCROLLBAR)
951 token->type = SKIN_TOKEN_LIST_SCROLLBAR;
952 pb->type = token->type;
954 return 0;
956 #else
957 (void)element;
958 if (token->type == SKIN_TOKEN_PROGRESSBAR ||
959 token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR)
961 wps_data->full_line_progressbar =
962 token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR;
964 return 0;
966 #endif
969 #ifdef HAVE_ALBUMART
970 static int parse_albumart_load(struct skin_element* element,
971 struct wps_token *token,
972 struct wps_data *wps_data)
974 struct dim dimensions;
975 int albumart_slot;
976 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
977 struct skin_albumart *aa =
978 (struct skin_albumart *)skin_buffer_alloc(sizeof(struct skin_albumart));
979 (void)token; /* silence warning */
980 if (!aa)
981 return -1;
983 /* reset albumart info in wps */
984 aa->width = -1;
985 aa->height = -1;
986 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
987 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
989 aa->x = element->params[0].data.number;
990 aa->y = element->params[1].data.number;
991 aa->width = element->params[2].data.number;
992 aa->height = element->params[3].data.number;
994 aa->vp = &curr_vp->vp;
995 aa->draw_handle = -1;
997 /* if we got here, we parsed everything ok .. ! */
998 if (aa->width < 0)
999 aa->width = 0;
1000 else if (aa->width > LCD_WIDTH)
1001 aa->width = LCD_WIDTH;
1003 if (aa->height < 0)
1004 aa->height = 0;
1005 else if (aa->height > LCD_HEIGHT)
1006 aa->height = LCD_HEIGHT;
1008 if (swap_for_rtl)
1009 aa->x = LCD_WIDTH - (aa->x + aa->width);
1011 aa->state = WPS_ALBUMART_LOAD;
1012 wps_data->albumart = aa;
1014 dimensions.width = aa->width;
1015 dimensions.height = aa->height;
1017 albumart_slot = playback_claim_aa_slot(&dimensions);
1019 if (0 <= albumart_slot)
1020 wps_data->playback_aa_slot = albumart_slot;
1022 if (element->params_count > 4 && !isdefault(&element->params[4]))
1024 switch (*element->params[4].data.text)
1026 case 'l':
1027 case 'L':
1028 if (swap_for_rtl)
1029 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1030 else
1031 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1032 break;
1033 case 'c':
1034 case 'C':
1035 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
1036 break;
1037 case 'r':
1038 case 'R':
1039 if (swap_for_rtl)
1040 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
1041 else
1042 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
1043 break;
1046 if (element->params_count > 5 && !isdefault(&element->params[5]))
1048 switch (*element->params[5].data.text)
1050 case 't':
1051 case 'T':
1052 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
1053 break;
1054 case 'c':
1055 case 'C':
1056 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
1057 break;
1058 case 'b':
1059 case 'B':
1060 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
1061 break;
1064 return 0;
1067 #endif /* HAVE_ALBUMART */
1068 #ifdef HAVE_SKIN_VARIABLES
1069 static struct skin_var* find_or_add_var(const char* label,
1070 struct wps_data *data)
1072 struct skin_var* ret = skin_find_item(label, SKIN_VARIABLE, data);
1073 if (!ret)
1075 ret = (struct skin_var*)skin_buffer_alloc(sizeof(struct skin_var));
1076 if (!ret)
1077 return NULL;
1078 ret->label = label;
1079 ret->value = 1;
1080 ret->last_changed = 0xffff;
1081 struct skin_token_list *item = new_skin_token_list_item(NULL, ret);
1082 if (!item)
1083 return NULL;
1084 add_to_ll_chain(&data->skinvars, item);
1086 return ret;
1088 static int parse_skinvar( struct skin_element *element,
1089 struct wps_token *token,
1090 struct wps_data *wps_data)
1092 const char* label = element->params[0].data.text;
1093 struct skin_var* var = find_or_add_var(label, wps_data);
1094 if (!var)
1095 return WPS_ERROR_INVALID_PARAM;
1096 switch (token->type)
1098 case SKIN_TOKEN_VAR_GETVAL:
1099 token->value.data = var;
1100 break;
1101 case SKIN_TOKEN_VAR_SET:
1103 struct skin_var_changer *data =
1104 (struct skin_var_changer*)skin_buffer_alloc(
1105 sizeof(struct skin_var_changer));
1106 if (!data)
1107 return WPS_ERROR_INVALID_PARAM;
1108 data->var = var;
1109 data->newval = element->params[2].data.number;
1110 data->max = 0;
1111 if (!strcmp(element->params[1].data.text, "set"))
1112 data->direct = true;
1113 else if (!strcmp(element->params[1].data.text, "inc"))
1115 data->direct = false;
1117 else if (!strcmp(element->params[1].data.text, "dec"))
1119 data->direct = false;
1120 data->newval *= -1;
1122 if (element->params_count > 3)
1123 data->max = element->params[3].data.number;
1124 token->value.data = data;
1126 break;
1127 case SKIN_TOKEN_VAR_TIMEOUT:
1129 struct skin_var_lastchange *data =
1130 (struct skin_var_lastchange*)skin_buffer_alloc(
1131 sizeof(struct skin_var_lastchange));
1132 if (!data)
1133 return WPS_ERROR_INVALID_PARAM;
1134 data->var = var;
1135 data->timeout = 10;
1136 if (element->params_count > 1)
1137 data->timeout = element->params[1].data.number;
1138 data->timeout *= TIMEOUT_UNIT;
1139 token->value.data = data;
1141 break;
1142 default: /* kill the warning */
1143 break;
1145 return 0;
1147 #endif /* HAVE_SKIN_VARIABLES */
1148 #ifdef HAVE_TOUCHSCREEN
1149 static int parse_lasttouch(struct skin_element *element,
1150 struct wps_token *token,
1151 struct wps_data *wps_data)
1153 struct touchregion_lastpress *data =
1154 (struct touchregion_lastpress*)skin_buffer_alloc(
1155 sizeof(struct touchregion_lastpress));
1156 int i;
1157 if (!data)
1158 return WPS_ERROR_INVALID_PARAM;
1159 data->region = NULL;
1160 data->timeout = 10;
1162 for (i=0; i<element->params_count; i++)
1164 if (element->params[i].type == STRING)
1165 data->region = skin_find_item(element->params[i].data.text,
1166 SKIN_FIND_TOUCHREGION, wps_data);
1167 else if (element->params[i].type == INTEGER ||
1168 element->params[i].type == DECIMAL)
1169 data->timeout = element->params[i].data.number;
1172 data->timeout *= TIMEOUT_UNIT;
1173 token->value.data = data;
1174 return 0;
1177 struct touchaction {const char* s; int action;};
1178 static const struct touchaction touchactions[] = {
1179 /* generic actions, convert to screen actions on use */
1180 {"none", ACTION_TOUCHSCREEN}, {"lock", ACTION_TOUCH_SOFTLOCK },
1181 {"prev", ACTION_STD_PREV }, {"next", ACTION_STD_NEXT },
1182 {"rwd", ACTION_STD_PREVREPEAT }, {"ffwd", ACTION_STD_NEXTREPEAT },
1183 {"hotkey", ACTION_STD_HOTKEY}, {"select", ACTION_STD_OK },
1184 {"menu", ACTION_STD_MENU }, {"cancel", ACTION_STD_CANCEL },
1185 {"contextmenu", ACTION_STD_CONTEXT},{"quickscreen", ACTION_STD_QUICKSCREEN },
1187 /* list/tree actions */
1188 { "resumeplayback", ACTION_TREE_WPS}, /* returns to previous music, WPS/FM */
1189 /* not really WPS specific, but no equivilant ACTION_STD_* */
1190 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1191 {"mute", ACTION_TOUCH_MUTE },
1193 /* generic settings changers */
1194 {"setting_inc", ACTION_SETTINGS_INC}, {"setting_dec", ACTION_SETTINGS_DEC},
1195 {"setting_set", ACTION_SETTINGS_SET},
1197 /* WPS specific actions */
1198 {"wps_prev", ACTION_WPS_SKIPPREV }, {"wps_next", ACTION_WPS_SKIPNEXT },
1199 {"browse", ACTION_WPS_BROWSE },
1200 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1201 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1202 {"pitch", ACTION_WPS_PITCHSCREEN}, {"playlist", ACTION_WPS_VIEW_PLAYLIST },
1204 #if CONFIG_TUNER
1205 /* FM screen actions */
1206 /* Also allow browse, play, stop from WPS codes */
1207 {"mode", ACTION_FM_MODE }, {"record", ACTION_FM_RECORD },
1208 {"presets", ACTION_FM_PRESET},
1209 #endif
1212 static int touchregion_setup_setting(struct skin_element *element, int param_no,
1213 struct touchregion *region)
1215 #ifndef __PCTOOL__
1216 int p = param_no;
1217 char *name = element->params[p++].data.text;
1218 int j;
1220 region->setting_data.setting = find_setting_by_cfgname(name, &j);
1221 if (region->setting_data.setting == NULL)
1222 return WPS_ERROR_INVALID_PARAM;
1224 if (region->action == ACTION_SETTINGS_SET)
1226 char* text;
1227 int temp;
1228 struct touchsetting *setting =
1229 &region->setting_data;
1230 if (element->params_count < p+1)
1231 return -1;
1233 text = element->params[p++].data.text;
1234 switch (settings[j].flags&F_T_MASK)
1236 case F_T_CUSTOM:
1237 setting->value.text = text;
1238 break;
1239 case F_T_INT:
1240 case F_T_UINT:
1241 if (settings[j].cfg_vals == NULL)
1243 setting->value.number = atoi(text);
1245 else if (cfg_string_to_int(j, &temp, text))
1247 if (settings[j].flags&F_TABLE_SETTING)
1248 setting->value.number =
1249 settings[j].table_setting->values[temp];
1250 else
1251 setting->value.number = temp;
1253 else
1254 return -1;
1255 break;
1256 case F_T_BOOL:
1257 if (cfg_string_to_int(j, &temp, text))
1259 setting->value.number = temp;
1261 else
1262 return -1;
1263 break;
1264 default:
1265 return -1;
1268 return p-param_no;
1269 #endif /* __PCTOOL__ */
1270 return 0;
1273 static int parse_touchregion(struct skin_element *element,
1274 struct wps_token *token,
1275 struct wps_data *wps_data)
1277 (void)token;
1278 unsigned i, imax;
1279 int p;
1280 struct touchregion *region = NULL;
1281 const char *action;
1282 const char pb_string[] = "progressbar";
1283 const char vol_string[] = "volume";
1285 /* format: %T([label,], x,y,width,height,action[, ...])
1286 * if action starts with & the area must be held to happen
1290 region = (struct touchregion*)skin_buffer_alloc(sizeof(struct touchregion));
1291 if (!region)
1292 return WPS_ERROR_INVALID_PARAM;
1294 /* should probably do some bounds checking here with the viewport... but later */
1295 region->action = ACTION_NONE;
1297 if (element->params[0].type == STRING)
1299 region->label = element->params[0].data.text;
1300 p = 1;
1301 /* "[SI]III[SI]|SS" is the param list. There MUST be 4 numbers
1302 * followed by at least one string. Verify that here */
1303 if (element->params_count < 6 ||
1304 element->params[4].type != INTEGER)
1305 return WPS_ERROR_INVALID_PARAM;
1307 else
1309 region->label = NULL;
1310 p = 0;
1313 region->x = element->params[p++].data.number;
1314 region->y = element->params[p++].data.number;
1315 region->width = element->params[p++].data.number;
1316 region->height = element->params[p++].data.number;
1317 region->wvp = curr_vp;
1318 region->armed = false;
1319 region->reverse_bar = false;
1320 region->value = 0;
1321 region->last_press = 0xffff;
1322 region->press_length = PRESS;
1323 region->allow_while_locked = false;
1324 action = element->params[p++].data.text;
1326 /* figure out the action */
1327 if(!strcmp(pb_string, action))
1328 region->action = ACTION_TOUCH_SCROLLBAR;
1329 else if(!strcmp(vol_string, action))
1330 region->action = ACTION_TOUCH_VOLUME;
1331 else
1333 imax = ARRAYLEN(touchactions);
1334 for (i = 0; i < imax; i++)
1336 /* try to match with one of our touchregion screens */
1337 if (!strcmp(touchactions[i].s, action))
1339 region->action = touchactions[i].action;
1340 if (region->action == ACTION_SETTINGS_INC ||
1341 region->action == ACTION_SETTINGS_DEC ||
1342 region->action == ACTION_SETTINGS_SET)
1344 int val;
1345 if (element->params_count < p+1)
1346 return WPS_ERROR_INVALID_PARAM;
1347 val = touchregion_setup_setting(element, p, region);
1348 if (val < 0)
1349 return WPS_ERROR_INVALID_PARAM;
1350 p += val;
1352 break;
1355 if (region->action == ACTION_NONE)
1356 return WPS_ERROR_INVALID_PARAM;
1358 while (p < element->params_count)
1360 char* param = element->params[p++].data.text;
1361 if (!strcmp(param, "allow_while_locked"))
1362 region->allow_while_locked = true;
1363 else if (!strcmp(param, "reverse_bar"))
1364 region->reverse_bar = true;
1365 else if (!strcmp(param, "repeat_press"))
1366 region->press_length = REPEAT;
1367 else if (!strcmp(param, "long_press"))
1368 region->press_length = LONG_PRESS;
1370 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1371 if (!item)
1372 return WPS_ERROR_INVALID_PARAM;
1373 add_to_ll_chain(&wps_data->touchregions, item);
1375 if (region->action == ACTION_TOUCH_MUTE)
1377 region->value = global_settings.volume;
1381 return 0;
1383 #endif
1385 static bool check_feature_tag(const int type)
1387 switch (type)
1389 case SKIN_TOKEN_RTC_PRESENT:
1390 #if CONFIG_RTC
1391 return true;
1392 #else
1393 return false;
1394 #endif
1395 case SKIN_TOKEN_HAVE_RECORDING:
1396 #ifdef HAVE_RECORDING
1397 return true;
1398 #else
1399 return false;
1400 #endif
1401 case SKIN_TOKEN_HAVE_TUNER:
1402 #if CONFIG_TUNER
1403 if (radio_hardware_present())
1404 return true;
1405 #endif
1406 return false;
1407 case SKIN_TOKEN_HAVE_TOUCH:
1408 #ifdef HAVE_TOUCHSCREEN
1409 return true;
1410 #else
1411 return false;
1412 #endif
1414 #if CONFIG_TUNER
1415 case SKIN_TOKEN_HAVE_RDS:
1416 #ifdef HAVE_RDS_CAP
1417 return true;
1418 #else
1419 return false;
1420 #endif /* HAVE_RDS_CAP */
1421 #endif /* CONFIG_TUNER */
1422 default: /* not a tag we care about, just don't skip */
1423 return true;
1428 * initial setup of wps_data; does reset everything
1429 * except fields which need to survive, i.e.
1432 static void skin_data_reset(struct wps_data *wps_data)
1434 #ifdef HAVE_LCD_BITMAP
1435 #ifndef __PCTOOL__
1436 struct skin_token_list *list = wps_data->images;
1437 while (list)
1439 struct gui_img *img = (struct gui_img*)list->token->value.data;
1440 if (img->buflib_handle > 0)
1441 core_free(img->buflib_handle);
1442 list = list->next;
1444 #endif
1445 wps_data->images = NULL;
1446 #endif
1447 wps_data->tree = NULL;
1448 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1449 if (wps_data->backdrop_id >= 0)
1450 skin_backdrop_unload(wps_data->backdrop_id);
1451 wps_data->backdrop = NULL;
1452 #endif
1453 #ifdef HAVE_TOUCHSCREEN
1454 wps_data->touchregions = NULL;
1455 #endif
1456 #ifdef HAVE_SKIN_VARIABLES
1457 wps_data->skinvars = NULL;
1458 #endif
1459 #ifdef HAVE_ALBUMART
1460 wps_data->albumart = NULL;
1461 if (wps_data->playback_aa_slot >= 0)
1463 playback_release_aa_slot(wps_data->playback_aa_slot);
1464 wps_data->playback_aa_slot = -1;
1466 #endif
1468 #ifdef HAVE_LCD_BITMAP
1469 wps_data->peak_meter_enabled = false;
1470 wps_data->wps_sb_tag = false;
1471 wps_data->show_sb_on_wps = false;
1472 #else /* HAVE_LCD_CHARCELLS */
1473 /* progress bars */
1474 int i;
1475 for (i = 0; i < 8; i++)
1477 wps_data->wps_progress_pat[i] = 0;
1479 wps_data->full_line_progressbar = false;
1480 #endif
1481 wps_data->wps_loaded = false;
1484 #ifdef HAVE_LCD_BITMAP
1485 #ifndef __PCTOOL__
1486 static int currently_loading_handle = -1;
1487 static int buflib_move_callback(int handle, void* current, void* new)
1489 (void)current;
1490 (void)new;
1491 if (handle == currently_loading_handle)
1492 return BUFLIB_CB_CANNOT_MOVE;
1493 return BUFLIB_CB_OK;
1495 static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL};
1496 static void lock_handle(int handle)
1498 currently_loading_handle = handle;
1500 static void unlock_handle(void)
1502 currently_loading_handle = -1;
1504 #endif
1506 static int load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1508 (void)wps_data; /* only needed for remote targets */
1509 char img_path[MAX_PATH];
1510 int fd;
1511 int handle;
1512 get_image_filename(bitmap->data, bmpdir,
1513 img_path, sizeof(img_path));
1515 /* load the image */
1516 int format;
1517 #ifdef HAVE_REMOTE_LCD
1518 if (curr_screen == SCREEN_REMOTE)
1519 format = FORMAT_ANY|FORMAT_REMOTE;
1520 else
1521 #endif
1522 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1524 fd = open(img_path, O_RDONLY);
1525 if (fd < 0)
1527 DEBUGF("Couldn't open %s\n", img_path);
1528 return fd;
1530 #ifndef __PCTOOL__
1531 size_t buf_size = read_bmp_fd(fd, bitmap, 0,
1532 format|FORMAT_RETURN_SIZE, NULL);
1533 handle = core_alloc_ex(bitmap->data, buf_size, &buflib_ops);
1534 if (handle < 0)
1536 #ifndef APPLICATION
1537 DEBUGF("Not enough skin buffer: need %zd more.\n",
1538 buf_size - skin_buffer_freespace());
1539 #endif
1540 close(fd);
1541 return handle;
1543 lseek(fd, 0, SEEK_SET);
1544 lock_handle(handle);
1545 bitmap->data = core_get_data(handle);
1546 int ret = read_bmp_fd(fd, bitmap, buf_size, format, NULL);
1547 bitmap->data = NULL; /* do this to force a crash later if the
1548 caller doesnt call core_get_data() */
1549 unlock_handle();
1550 close(fd);
1551 if (ret > 0)
1553 return handle;
1555 else
1557 /* Abort if we can't load an image */
1558 DEBUGF("Couldn't load '%s'\n", img_path);
1559 core_free(handle);
1560 return -1;
1562 #else /* !__PCTOOL__ */
1563 close(fd);
1564 return 1;
1565 #endif
1568 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1570 struct skin_token_list *list;
1571 bool retval = true; /* return false if a single image failed to load */
1573 /* regular images */
1574 list = wps_data->images;
1575 while (list)
1577 struct gui_img *img = (struct gui_img*)list->token->value.data;
1578 if (img->bm.data)
1580 if (img->using_preloaded_icons)
1582 img->loaded = true;
1583 list->token->type = SKIN_TOKEN_IMAGE_DISPLAY_LISTICON;
1585 else
1587 img->buflib_handle = load_skin_bmp(wps_data, &img->bm, bmpdir);
1588 img->loaded = img->buflib_handle >= 0;
1589 if (img->loaded)
1590 img->subimage_height = img->bm.height / img->num_subimages;
1591 else
1592 retval = false;
1595 list = list->next;
1598 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1599 wps_data->backdrop_id = skin_backdrop_assign(wps_data->backdrop, bmpdir, curr_screen);
1600 #endif /* has backdrop support */
1601 return retval;
1604 static bool skin_load_fonts(struct wps_data *data)
1606 /* don't spit out after the first failue to aid debugging */
1607 bool success = true;
1608 struct skin_element *vp_list;
1609 int font_id;
1610 /* walk though each viewport and assign its font */
1611 for(vp_list = data->tree; vp_list; vp_list = vp_list->next)
1613 /* first, find the viewports that have a non-sys/ui-font font */
1614 struct skin_viewport *skin_vp =
1615 (struct skin_viewport*)vp_list->data;
1616 struct viewport *vp = &skin_vp->vp;
1619 if (vp->font <= FONT_UI)
1620 { /* the usual case -> built-in fonts */
1621 #ifdef HAVE_REMOTE_LCD
1622 if (vp->font == FONT_UI)
1623 vp->font += curr_screen;
1624 #endif
1625 continue;
1627 font_id = vp->font;
1629 /* now find the corresponding skin_font */
1630 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
1631 if (!font->name)
1633 if (success)
1635 DEBUGF("font %d not specified\n", font_id);
1637 success = false;
1638 continue;
1641 /* load the font - will handle loading the same font again if
1642 * multiple viewports use the same */
1643 if (font->id < 0)
1645 char *dot = strchr(font->name, '.');
1646 *dot = '\0';
1647 font->id = skin_font_load(font->name,
1648 skinfonts[font_id-FONT_FIRSTUSERFONT].glyphs);
1651 if (font->id < 0)
1653 DEBUGF("Unable to load font %d: '%s.fnt'\n",
1654 font_id, font->name);
1655 font->name = NULL; /* to stop trying to load it again if we fail */
1656 success = false;
1657 font->name = NULL;
1658 continue;
1661 /* finally, assign the font_id to the viewport */
1662 vp->font = font->id;
1664 return success;
1667 #endif /* HAVE_LCD_BITMAP */
1668 static int convert_viewport(struct wps_data *data, struct skin_element* element)
1670 struct skin_viewport *skin_vp =
1671 (struct skin_viewport *)skin_buffer_alloc(sizeof(struct skin_viewport));
1672 struct screen *display = &screens[curr_screen];
1674 if (!skin_vp)
1675 return CALLBACK_ERROR;
1677 skin_vp->hidden_flags = 0;
1678 skin_vp->label = NULL;
1679 skin_vp->is_infovp = false;
1680 element->data = skin_vp;
1681 curr_vp = skin_vp;
1682 curr_viewport_element = element;
1684 viewport_set_defaults(&skin_vp->vp, curr_screen);
1685 #ifdef HAVE_REMOTE_LCD
1686 /* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
1687 * This parser requires font 1 to always be the UI font,
1688 * so force it back to FONT_UI and handle the screen number at the end */
1689 skin_vp->vp.font = FONT_UI;
1690 #endif
1692 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1693 skin_vp->start_fgcolour = skin_vp->vp.fg_pattern;
1694 skin_vp->start_bgcolour = skin_vp->vp.bg_pattern;
1695 #endif
1696 #ifdef HAVE_LCD_COLOR
1697 skin_vp->start_gradient.start = skin_vp->vp.lss_pattern;
1698 skin_vp->start_gradient.end = skin_vp->vp.lse_pattern;
1699 skin_vp->start_gradient.text = skin_vp->vp.lst_pattern;
1700 #endif
1703 struct skin_tag_parameter *param = element->params;
1704 if (element->params_count == 0) /* default viewport */
1706 if (!data->tree) /* first viewport in the skin */
1707 data->tree = element;
1708 skin_vp->label = VP_DEFAULT_LABEL;
1709 return CALLBACK_OK;
1712 if (element->params_count == 6)
1714 if (element->tag->type == SKIN_TOKEN_UIVIEWPORT_LOAD)
1716 skin_vp->is_infovp = true;
1717 if (isdefault(param))
1719 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
1720 skin_vp->label = VP_DEFAULT_LABEL;
1722 else
1724 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
1725 skin_vp->label = param->data.text;
1728 else
1730 skin_vp->hidden_flags = VP_DRAW_HIDEABLE|VP_DRAW_HIDDEN;
1731 skin_vp->label = param->data.text;
1733 param++;
1735 /* x */
1736 if (!isdefault(param))
1738 skin_vp->vp.x = param->data.number;
1739 if (param->data.number < 0)
1740 skin_vp->vp.x += display->lcdwidth;
1742 param++;
1743 /* y */
1744 if (!isdefault(param))
1746 skin_vp->vp.y = param->data.number;
1747 if (param->data.number < 0)
1748 skin_vp->vp.y += display->lcdheight;
1750 param++;
1751 /* width */
1752 if (!isdefault(param))
1754 skin_vp->vp.width = param->data.number;
1755 if (param->data.number < 0)
1756 skin_vp->vp.width = (skin_vp->vp.width + display->lcdwidth) - skin_vp->vp.x;
1758 else
1760 skin_vp->vp.width = display->lcdwidth - skin_vp->vp.x;
1762 param++;
1763 /* height */
1764 if (!isdefault(param))
1766 skin_vp->vp.height = param->data.number;
1767 if (param->data.number < 0)
1768 skin_vp->vp.height = (skin_vp->vp.height + display->lcdheight) - skin_vp->vp.y;
1770 else
1772 skin_vp->vp.height = display->lcdheight - skin_vp->vp.y;
1774 param++;
1775 #ifdef HAVE_LCD_BITMAP
1776 /* font */
1777 if (!isdefault(param))
1779 skin_vp->vp.font = param->data.number;
1781 #endif
1782 if ((unsigned) skin_vp->vp.x >= (unsigned) display->lcdwidth ||
1783 skin_vp->vp.width + skin_vp->vp.x > display->lcdwidth ||
1784 (unsigned) skin_vp->vp.y >= (unsigned) display->lcdheight ||
1785 skin_vp->vp.height + skin_vp->vp.y > display->lcdheight)
1786 return CALLBACK_ERROR;
1788 return CALLBACK_OK;
1791 static int skin_element_callback(struct skin_element* element, void* data)
1793 struct wps_data *wps_data = (struct wps_data *)data;
1794 struct wps_token *token;
1795 parse_function function = NULL;
1797 switch (element->type)
1799 /* IMPORTANT: element params are shared, so copy them if needed
1800 * or use then NOW, dont presume they have a long lifespan
1802 case TAG:
1804 token = (struct wps_token*)skin_buffer_alloc(sizeof(struct wps_token));
1805 memset(token, 0, sizeof(*token));
1806 token->type = element->tag->type;
1808 if (element->tag->flags&SKIN_RTC_REFRESH)
1810 #if CONFIG_RTC
1811 curr_line->update_mode |= SKIN_REFRESH_DYNAMIC;
1812 #else
1813 curr_line->update_mode |= SKIN_REFRESH_STATIC;
1814 #endif
1816 else
1817 curr_line->update_mode |= element->tag->flags&SKIN_REFRESH_ALL;
1819 element->data = token;
1821 /* Some tags need special handling for the tag, so add them here */
1822 switch (token->type)
1824 case SKIN_TOKEN_ALIGN_LANGDIRECTION:
1825 follow_lang_direction = 2;
1826 break;
1827 case SKIN_TOKEN_LOGICAL_IF:
1828 function = parse_logical_if;
1829 break;
1830 case SKIN_TOKEN_SUBSTRING:
1831 function = parse_substring_tag;
1832 break;
1833 case SKIN_TOKEN_PROGRESSBAR:
1834 case SKIN_TOKEN_VOLUME:
1835 case SKIN_TOKEN_BATTERY_PERCENT:
1836 case SKIN_TOKEN_PLAYER_PROGRESSBAR:
1837 case SKIN_TOKEN_PEAKMETER_LEFT:
1838 case SKIN_TOKEN_PEAKMETER_RIGHT:
1839 case SKIN_TOKEN_LIST_NEEDS_SCROLLBAR:
1840 #ifdef HAVE_RADIO_RSSI
1841 case SKIN_TOKEN_TUNER_RSSI:
1842 #endif
1843 function = parse_progressbar_tag;
1844 break;
1845 case SKIN_TOKEN_SUBLINE_TIMEOUT:
1846 case SKIN_TOKEN_BUTTON_VOLUME:
1847 case SKIN_TOKEN_TRACK_STARTING:
1848 case SKIN_TOKEN_TRACK_ENDING:
1849 function = parse_timeout_tag;
1850 break;
1851 #ifdef HAVE_LCD_BITMAP
1852 case SKIN_TOKEN_LIST_ITEM_TEXT:
1853 case SKIN_TOKEN_LIST_ITEM_ICON:
1854 function = parse_listitem;
1855 break;
1856 case SKIN_TOKEN_DISABLE_THEME:
1857 case SKIN_TOKEN_ENABLE_THEME:
1858 case SKIN_TOKEN_DRAW_INBUILTBAR:
1859 function = parse_statusbar_tags;
1860 break;
1861 case SKIN_TOKEN_LIST_TITLE_TEXT:
1862 #ifndef __PCTOOL__
1863 sb_skin_has_title(curr_screen);
1864 #endif
1865 break;
1866 #endif
1867 case SKIN_TOKEN_FILE_DIRECTORY:
1868 token->value.i = element->params[0].data.number;
1869 break;
1870 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1871 case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
1872 case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
1873 function = parse_viewportcolour;
1874 break;
1875 case SKIN_TOKEN_IMAGE_BACKDROP:
1876 function = parse_image_special;
1877 break;
1878 case SKIN_TOKEN_VIEWPORT_TEXTSTYLE:
1879 function = parse_viewporttextstyle;
1880 break;
1881 #endif
1882 #ifdef HAVE_LCD_COLOR
1883 case SKIN_TOKEN_VIEWPORT_GRADIENT_SETUP:
1884 function = parse_viewport_gradient_setup;
1885 break;
1886 #endif
1887 case SKIN_TOKEN_TRANSLATEDSTRING:
1888 case SKIN_TOKEN_SETTING:
1889 function = parse_setting_and_lang;
1890 break;
1891 #ifdef HAVE_LCD_BITMAP
1892 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
1893 function = parse_playlistview;
1894 break;
1895 case SKIN_TOKEN_LOAD_FONT:
1896 function = parse_font_load;
1897 break;
1898 case SKIN_TOKEN_VIEWPORT_ENABLE:
1899 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
1900 token->value.data = element->params[0].data.text;
1901 break;
1902 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
1903 function = parse_image_display;
1904 break;
1905 case SKIN_TOKEN_IMAGE_PRELOAD:
1906 case SKIN_TOKEN_IMAGE_DISPLAY:
1907 function = parse_image_load;
1908 break;
1909 case SKIN_TOKEN_LIST_ITEM_CFG:
1910 function = parse_listitemviewport;
1911 break;
1912 #endif
1913 #ifdef HAVE_TOUCHSCREEN
1914 case SKIN_TOKEN_TOUCHREGION:
1915 function = parse_touchregion;
1916 break;
1917 case SKIN_TOKEN_LASTTOUCH:
1918 function = parse_lasttouch;
1919 break;
1920 #endif
1921 #ifdef HAVE_ALBUMART
1922 case SKIN_TOKEN_ALBUMART_DISPLAY:
1923 if (wps_data->albumart)
1924 wps_data->albumart->vp = &curr_vp->vp;
1925 break;
1926 case SKIN_TOKEN_ALBUMART_LOAD:
1927 function = parse_albumart_load;
1928 break;
1929 #endif
1930 #ifdef HAVE_SKIN_VARIABLES
1931 case SKIN_TOKEN_VAR_SET:
1932 case SKIN_TOKEN_VAR_GETVAL:
1933 case SKIN_TOKEN_VAR_TIMEOUT:
1934 function = parse_skinvar;
1935 break;
1936 #endif
1937 default:
1938 break;
1940 if (function)
1942 if (function(element, token, wps_data) < 0)
1943 return CALLBACK_ERROR;
1945 /* tags that start with 'F', 'I' or 'D' are for the next file */
1946 if ( *(element->tag->name) == 'I' || *(element->tag->name) == 'F' ||
1947 *(element->tag->name) == 'D')
1948 token->next = true;
1949 if (follow_lang_direction > 0 )
1950 follow_lang_direction--;
1951 break;
1953 case VIEWPORT:
1954 return convert_viewport(wps_data, element);
1955 case LINE:
1957 struct line *line =
1958 (struct line *)skin_buffer_alloc(sizeof(struct line));
1959 line->update_mode = SKIN_REFRESH_STATIC;
1960 curr_line = line;
1961 element->data = line;
1963 break;
1964 case LINE_ALTERNATOR:
1966 struct line_alternator *alternator =
1967 (struct line_alternator *)skin_buffer_alloc(sizeof(struct line_alternator));
1968 alternator->current_line = 0;
1969 #ifndef __PCTOOL__
1970 alternator->next_change_tick = current_tick;
1971 #endif
1972 element->data = alternator;
1974 break;
1975 case CONDITIONAL:
1977 struct conditional *conditional =
1978 (struct conditional *)skin_buffer_alloc(sizeof(struct conditional));
1979 conditional->last_value = -1;
1980 conditional->token = element->data;
1981 element->data = conditional;
1982 if (!check_feature_tag(element->tag->type))
1984 return FEATURE_NOT_AVAILABLE;
1986 return CALLBACK_OK;
1988 case TEXT:
1989 curr_line->update_mode |= SKIN_REFRESH_STATIC;
1990 break;
1991 default:
1992 break;
1994 return CALLBACK_OK;
1997 /* to setup up the wps-data from a format-buffer (isfile = false)
1998 from a (wps-)file (isfile = true)*/
1999 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
2000 const char *buf, bool isfile)
2002 char *wps_buffer = NULL;
2003 if (!wps_data || !buf)
2004 return false;
2005 #ifdef HAVE_ALBUMART
2006 int status;
2007 struct mp3entry *curtrack;
2008 long offset;
2009 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
2010 if (wps_data->albumart)
2012 old_aa.state = wps_data->albumart->state;
2013 old_aa.height = wps_data->albumart->height;
2014 old_aa.width = wps_data->albumart->width;
2016 #endif
2017 #ifdef HAVE_LCD_BITMAP
2018 int i;
2019 for (i=0;i<MAXUSERFONTS;i++)
2021 skinfonts[i].id = -1;
2022 skinfonts[i].name = NULL;
2024 #endif
2025 #ifdef DEBUG_SKIN_ENGINE
2026 if (isfile && debug_wps)
2028 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
2030 #endif
2033 skin_data_reset(wps_data);
2034 wps_data->wps_loaded = false;
2035 curr_screen = screen;
2036 curr_line = NULL;
2037 curr_vp = NULL;
2038 curr_viewport_element = NULL;
2040 if (isfile)
2042 int fd = open_utf8(buf, O_RDONLY);
2044 if (fd < 0)
2045 return false;
2047 /* get buffer space from the plugin buffer */
2048 size_t buffersize = 0;
2049 wps_buffer = (char *)plugin_get_buffer(&buffersize);
2051 if (!wps_buffer)
2052 return false;
2054 /* copy the file's content to the buffer for parsing,
2055 ensuring that every line ends with a newline char. */
2056 unsigned int start = 0;
2057 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
2059 start += strlen(wps_buffer + start);
2060 if (start < buffersize - 1)
2062 wps_buffer[start++] = '\n';
2063 wps_buffer[start] = 0;
2066 close(fd);
2067 if (start <= 0)
2068 return false;
2070 else
2072 wps_buffer = (char*)buf;
2074 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
2075 wps_data->backdrop = "-";
2076 wps_data->backdrop_id = -1;
2077 #endif
2078 /* parse the skin source */
2079 #ifndef APPLICATION
2080 skin_buffer_save_position();
2081 #endif
2082 wps_data->tree = skin_parse(wps_buffer, skin_element_callback, wps_data);
2083 if (!wps_data->tree) {
2084 skin_data_reset(wps_data);
2085 #ifndef APPLICATION
2086 skin_buffer_restore_position();
2087 #endif
2088 return false;
2091 #ifdef HAVE_LCD_BITMAP
2092 char bmpdir[MAX_PATH];
2093 if (isfile)
2095 /* get the bitmap dir */
2096 char *dot = strrchr(buf, '.');
2097 strlcpy(bmpdir, buf, dot - buf + 1);
2099 else
2101 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
2103 /* load the bitmaps that were found by the parsing */
2104 if (!load_skin_bitmaps(wps_data, bmpdir) ||
2105 !skin_load_fonts(wps_data))
2107 skin_data_reset(wps_data);
2108 #ifndef APPLICATION
2109 skin_buffer_restore_position();
2110 #endif
2111 return false;
2113 #endif
2114 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
2115 status = audio_status();
2116 if (status & AUDIO_STATUS_PLAY)
2118 struct skin_albumart *aa = wps_data->albumart;
2119 if (aa && ((aa->state && !old_aa.state) ||
2120 (aa->state &&
2121 (((old_aa.height != aa->height) ||
2122 (old_aa.width != aa->width))))))
2124 curtrack = audio_current_track();
2125 offset = curtrack->offset;
2126 audio_stop();
2127 if (!(status & AUDIO_STATUS_PAUSE))
2128 audio_play(offset);
2131 #endif
2132 wps_data->wps_loaded = true;
2133 #ifdef DEBUG_SKIN_ENGINE
2134 // if (isfile && debug_wps)
2135 // debug_skin_usage();
2136 #endif
2137 return true;