New skin tags: %Vs() to set the text style and %Vg() to get the viewports gradient...
[maemo-rb.git] / apps / gui / skin_engine / skin_parser.c
blobb7bb045411a05effa4700b24c90c9dc88557beee
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 "file.h"
28 #include "misc.h"
29 #include "plugin.h"
30 #include "viewport.h"
32 #include "skin_buffer.h"
33 #include "skin_parser.h"
34 #include "tag_table.h"
36 #ifdef __PCTOOL__
37 #ifdef WPSEDITOR
38 #include "proxy.h"
39 #include "sysfont.h"
40 #else
41 #include "action.h"
42 #include "checkwps.h"
43 #include "audio.h"
44 #define lang_is_rtl() (false)
45 #define DEBUGF printf
46 #endif /*WPSEDITOR*/
47 #else
48 #include "debug.h"
49 #include "language.h"
50 #endif /*__PCTOOL__*/
52 #include <ctype.h>
53 #include <stdbool.h>
54 #include "font.h"
56 #include "wps_internals.h"
57 #include "skin_engine.h"
58 #include "settings.h"
59 #include "settings_list.h"
60 #if CONFIG_TUNER
61 #include "radio.h"
62 #include "tuner.h"
63 #endif
64 #include "skin_fonts.h"
66 #ifdef HAVE_LCD_BITMAP
67 #include "bmp.h"
68 #endif
70 #ifdef HAVE_ALBUMART
71 #include "playback.h"
72 #endif
74 #include "backdrop.h"
75 #include "statusbar-skinned.h"
77 #define WPS_ERROR_INVALID_PARAM -1
80 static bool isdefault(struct skin_tag_parameter *param)
82 return param->type == DEFAULT;
86 /* which screen are we parsing for? */
87 static enum screen_type curr_screen;
89 /* the current viewport */
90 static struct skin_element *curr_viewport_element;
91 static struct skin_viewport *curr_vp;
93 static struct line *curr_line;
95 static int follow_lang_direction = 0;
97 typedef int (*parse_function)(struct skin_element *element,
98 struct wps_token *token,
99 struct wps_data *wps_data);
101 #ifdef HAVE_LCD_BITMAP
102 /* add a skin_token_list item to the list chain. ALWAYS appended because some of the
103 * chains require the order to be kept.
105 static void add_to_ll_chain(struct skin_token_list **list, struct skin_token_list *item)
107 if (*list == NULL)
108 *list = item;
109 else
111 struct skin_token_list *t = *list;
112 while (t->next)
113 t = t->next;
114 t->next = item;
118 #endif
121 void *skin_find_item(const char *label, enum skin_find_what what,
122 struct wps_data *data)
124 const char *itemlabel = NULL;
125 union {
126 struct skin_token_list *linkedlist;
127 struct skin_element *vplist;
128 } list = {NULL};
129 bool isvplist = false;
130 void *ret = NULL;
131 switch (what)
133 case SKIN_FIND_UIVP:
134 case SKIN_FIND_VP:
135 list.vplist = data->tree;
136 isvplist = true;
137 break;
138 #ifdef HAVE_LCD_BITMAP
139 case SKIN_FIND_IMAGE:
140 list.linkedlist = data->images;
141 break;
142 #endif
143 #ifdef HAVE_TOUCHSCREEN
144 case SKIN_FIND_TOUCHREGION:
145 list.linkedlist = data->touchregions;
146 break;
147 #endif
148 #ifdef HAVE_SKIN_VARIABLES
149 case SKIN_VARIABLE:
150 list.linkedlist = data->skinvars;
151 break;
152 #endif
155 while (list.linkedlist)
157 bool skip = false;
158 switch (what)
160 case SKIN_FIND_UIVP:
161 case SKIN_FIND_VP:
162 ret = list.vplist->data;
163 itemlabel = ((struct skin_viewport *)ret)->label;
164 skip = !(((struct skin_viewport *)ret)->is_infovp ==
165 (what==SKIN_FIND_UIVP));
166 break;
167 #ifdef HAVE_LCD_BITMAP
168 case SKIN_FIND_IMAGE:
169 ret = list.linkedlist->token->value.data;
170 itemlabel = ((struct gui_img *)ret)->label;
171 break;
172 #endif
173 #ifdef HAVE_TOUCHSCREEN
174 case SKIN_FIND_TOUCHREGION:
175 ret = list.linkedlist->token->value.data;
176 itemlabel = ((struct touchregion *)ret)->label;
177 break;
178 #endif
179 #ifdef HAVE_SKIN_VARIABLES
180 case SKIN_VARIABLE:
181 ret = list.linkedlist->token->value.data;
182 itemlabel = ((struct skin_var *)ret)->label;
183 break;
184 #endif
187 if (!skip && itemlabel && !strcmp(itemlabel, label))
188 return ret;
190 if (isvplist)
191 list.vplist = list.vplist->next;
192 else
193 list.linkedlist = list.linkedlist->next;
195 return NULL;
198 #ifdef HAVE_LCD_BITMAP
200 /* create and init a new wpsll item.
201 * passing NULL to token will alloc a new one.
202 * You should only pass NULL for the token when the token type (table above)
203 * is WPS_NO_TOKEN which means it is not stored automatically in the skins token array
205 static struct skin_token_list *new_skin_token_list_item(struct wps_token *token,
206 void* token_data)
208 struct skin_token_list *llitem =
209 (struct skin_token_list *)skin_buffer_alloc(sizeof(struct skin_token_list));
210 if (!token)
211 token = (struct wps_token*)skin_buffer_alloc(sizeof(struct wps_token));
212 if (!llitem || !token)
213 return NULL;
214 llitem->next = NULL;
215 llitem->token = token;
216 if (token_data)
217 llitem->token->value.data = token_data;
218 return llitem;
221 static int parse_statusbar_tags(struct skin_element* element,
222 struct wps_token *token,
223 struct wps_data *wps_data)
225 (void)element;
226 if (token->type == SKIN_TOKEN_DRAW_INBUILTBAR)
228 token->value.data = (void*)&curr_vp->vp;
230 else
232 struct skin_element *def_vp = wps_data->tree;
233 struct skin_viewport *default_vp = def_vp->data;
234 if (def_vp->params_count == 0)
236 wps_data->wps_sb_tag = true;
237 wps_data->show_sb_on_wps = (token->type == SKIN_TOKEN_ENABLE_THEME);
239 if (wps_data->show_sb_on_wps)
241 viewport_set_defaults(&default_vp->vp, curr_screen);
243 else
245 viewport_set_fullscreen(&default_vp->vp, curr_screen);
247 #ifdef HAVE_REMOTE_LCD
248 /* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
249 * This parser requires font 1 to always be the UI font,
250 * so force it back to FONT_UI and handle the screen number at the end */
251 default_vp->vp.font = FONT_UI;
252 #endif
254 return 0;
257 static int get_image_id(int c)
259 if(c >= 'a' && c <= 'z')
260 return c - 'a';
261 else if(c >= 'A' && c <= 'Z')
262 return c - 'A' + 26;
263 else
264 return -1;
267 char *get_image_filename(const char *start, const char* bmpdir,
268 char *buf, int buf_size)
270 snprintf(buf, buf_size, "%s/%s", bmpdir, start);
272 return buf;
275 static int parse_image_display(struct skin_element *element,
276 struct wps_token *token,
277 struct wps_data *wps_data)
279 char *label = element->params[0].data.text;
280 char sublabel = '\0';
281 int subimage;
282 struct gui_img *img;
283 struct image_display *id = skin_buffer_alloc(sizeof(struct image_display));
285 if (element->params_count == 1 && strlen(label) <= 2)
287 /* backwards compatability. Allow %xd(Aa) to still work */
288 sublabel = label[1];
289 label[1] = '\0';
291 /* sanity check */
292 img = skin_find_item(label, SKIN_FIND_IMAGE, wps_data);
293 if (!img || !id)
295 return WPS_ERROR_INVALID_PARAM;
297 id->label = label;
298 id->offset = 0;
299 id->token = NULL;
300 if (img->using_preloaded_icons)
302 token->type = SKIN_TOKEN_IMAGE_DISPLAY_LISTICON;
305 if (element->params_count > 1)
307 if (element->params[1].type == CODE)
308 id->token = element->params[1].data.code->data;
309 /* specify a number. 1 being the first subimage (i.e top) NOT 0 */
310 else if (element->params[1].type == INTEGER)
311 id->subimage = element->params[1].data.number - 1;
312 if (element->params_count > 2)
313 id->offset = element->params[2].data.number;
315 else
317 if ((subimage = get_image_id(sublabel)) != -1)
319 if (subimage >= img->num_subimages)
320 return WPS_ERROR_INVALID_PARAM;
321 id->subimage = subimage;
322 } else {
323 id->subimage = 0;
326 token->value.data = id;
327 return 0;
330 static int parse_image_load(struct skin_element *element,
331 struct wps_token *token,
332 struct wps_data *wps_data)
334 const char* filename;
335 const char* id;
336 int x,y;
337 struct gui_img *img;
339 /* format: %x(n,filename.bmp,x,y)
340 or %xl(n,filename.bmp,x,y)
341 or %xl(n,filename.bmp,x,y,num_subimages)
344 id = element->params[0].data.text;
345 filename = element->params[1].data.text;
346 x = element->params[2].data.number;
347 y = element->params[3].data.number;
349 /* check the image number and load state */
350 if(skin_find_item(id, SKIN_FIND_IMAGE, wps_data))
352 /* Invalid image ID */
353 return WPS_ERROR_INVALID_PARAM;
355 img = (struct gui_img*)skin_buffer_alloc(sizeof(struct gui_img));
356 if (!img)
357 return WPS_ERROR_INVALID_PARAM;
358 /* save a pointer to the filename */
359 img->bm.data = (char*)filename;
360 img->label = id;
361 img->x = x;
362 img->y = y;
363 img->num_subimages = 1;
364 img->always_display = false;
365 img->display = -1;
366 img->using_preloaded_icons = false;
368 /* save current viewport */
369 img->vp = &curr_vp->vp;
371 if (token->type == SKIN_TOKEN_IMAGE_DISPLAY)
373 img->always_display = true;
375 else if (element->params_count == 5)
377 img->num_subimages = element->params[4].data.number;
378 if (img->num_subimages <= 0)
379 return WPS_ERROR_INVALID_PARAM;
382 if (!strcmp(img->bm.data, "__list_icons__"))
384 img->num_subimages = Icon_Last_Themeable;
385 img->using_preloaded_icons = true;
388 struct skin_token_list *item =
389 (struct skin_token_list *)new_skin_token_list_item(NULL, img);
390 if (!item)
391 return WPS_ERROR_INVALID_PARAM;
392 add_to_ll_chain(&wps_data->images, item);
394 return 0;
396 struct skin_font {
397 int id; /* the id from font_load */
398 char *name; /* filename without path and extension */
399 int glyphs; /* how many glyphs to reserve room for */
401 static struct skin_font skinfonts[MAXUSERFONTS];
402 static int parse_font_load(struct skin_element *element,
403 struct wps_token *token,
404 struct wps_data *wps_data)
406 (void)wps_data; (void)token;
407 int id = element->params[0].data.number;
408 char *filename = element->params[1].data.text;
409 int glyphs;
410 char *ptr;
412 if(element->params_count > 2)
413 glyphs = element->params[2].data.number;
414 else
415 glyphs = GLYPHS_TO_CACHE;
416 #if defined(DEBUG) || defined(SIMULATOR)
417 if (skinfonts[id-FONT_FIRSTUSERFONT].name != NULL)
419 DEBUGF("font id %d already being used\n", id);
421 #endif
422 /* make sure the filename contains .fnt,
423 * we dont actually use it, but require it anyway */
424 ptr = strchr(filename, '.');
425 if (!ptr || strncmp(ptr, ".fnt", 4))
426 return WPS_ERROR_INVALID_PARAM;
427 skinfonts[id-FONT_FIRSTUSERFONT].id = -1;
428 skinfonts[id-FONT_FIRSTUSERFONT].name = filename;
429 skinfonts[id-FONT_FIRSTUSERFONT].glyphs = glyphs;
431 return 0;
435 #ifdef HAVE_LCD_BITMAP
437 static int parse_playlistview(struct skin_element *element,
438 struct wps_token *token,
439 struct wps_data *wps_data)
441 (void)wps_data;
442 struct playlistviewer *viewer =
443 (struct playlistviewer *)skin_buffer_alloc(sizeof(struct playlistviewer));
444 if (!viewer)
445 return WPS_ERROR_INVALID_PARAM;
446 viewer->vp = &curr_vp->vp;
447 viewer->show_icons = true;
448 viewer->start_offset = element->params[0].data.number;
449 viewer->line = element->params[1].data.code;
451 token->value.data = (void*)viewer;
453 return 0;
455 #endif
456 #ifdef HAVE_LCD_COLOR
457 static int parse_viewport_gradient_setup(struct skin_element *element,
458 struct wps_token *token,
459 struct wps_data *wps_data)
461 (void)wps_data;
462 struct gradient_config *cfg;
463 if (element->params_count < 2) /* only start and end are required */
464 return 1;
465 cfg = (struct gradient_config *)skin_buffer_alloc(sizeof(struct gradient_config));
466 if (!cfg)
467 return 1;
468 if (!parse_color(curr_screen, element->params[0].data.text, &cfg->start) ||
469 !parse_color(curr_screen, element->params[1].data.text, &cfg->end))
470 return 1;
471 if (element->params_count > 2)
473 if (!parse_color(curr_screen, element->params[2].data.text, &cfg->text))
474 return 1;
476 else
478 cfg->text = curr_vp->vp.fg_pattern;
481 token->value.data = cfg;
482 return 0;
484 #endif
485 static int parse_viewporttextstyle(struct skin_element *element,
486 struct wps_token *token,
487 struct wps_data *wps_data)
489 (void)wps_data;
490 int style;
491 char *mode = element->params[0].data.text;
492 unsigned colour;
494 if (!strcmp(mode, "invert"))
496 style = STYLE_INVERT;
498 else if (!strcmp(mode, "colour") || !strcmp(mode, "color"))
500 if (element->params_count < 2 ||
501 !parse_color(curr_screen, element->params[1].data.text, &colour))
502 return 1;
503 style = STYLE_COLORED|(STYLE_COLOR_MASK&colour);
505 #ifdef HAVE_LCD_COLOR
506 else if (!strcmp(mode, "gradient"))
508 int num_lines;
509 if (element->params_count < 2)
510 num_lines = 1;
511 else /* atoi() instead of using a number in the parser is because [si]
512 * will select the number for something which looks like a colour
513 * making the "colour" case (above) harder to parse */
514 num_lines = atoi(element->params[1].data.text);
515 style = STYLE_GRADIENT|NUMLN_PACK(num_lines)|CURLN_PACK(0);
517 #endif
518 else if (!strcmp(mode, "clear"))
520 style = STYLE_DEFAULT;
522 else
523 return 1;
524 token->value.l = style;
525 return 0;
528 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
530 static int parse_viewportcolour(struct skin_element *element,
531 struct wps_token *token,
532 struct wps_data *wps_data)
534 (void)wps_data;
535 struct skin_tag_parameter *param = element->params;
536 struct viewport_colour *colour =
537 (struct viewport_colour *)skin_buffer_alloc(sizeof(struct viewport_colour));
538 if (!colour)
539 return -1;
540 if (isdefault(param))
542 colour->colour = get_viewport_default_colour(curr_screen,
543 token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR);
545 else
547 if (!parse_color(curr_screen, param->data.text, &colour->colour))
548 return -1;
550 colour->vp = &curr_vp->vp;
551 token->value.data = colour;
552 if (element->line == curr_viewport_element->line)
554 if (token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR)
556 curr_vp->start_fgcolour = colour->colour;
557 curr_vp->vp.fg_pattern = colour->colour;
559 else
561 curr_vp->start_bgcolour = colour->colour;
562 curr_vp->vp.bg_pattern = colour->colour;
565 return 0;
568 static int parse_image_special(struct skin_element *element,
569 struct wps_token *token,
570 struct wps_data *wps_data)
572 (void)wps_data; /* kill warning */
573 (void)token;
575 #if LCD_DEPTH > 1
576 char *filename;
577 if (token->type == SKIN_TOKEN_IMAGE_BACKDROP)
579 if (isdefault(&element->params[0]))
581 filename = "-";
583 else
585 filename = element->params[0].data.text;
586 /* format: %X(filename.bmp) or %X(d) */
587 if (!strcmp(filename, "d"))
588 filename = NULL;
590 wps_data->backdrop = filename;
592 #endif
594 return 0;
596 #endif
598 #endif /* HAVE_LCD_BITMAP */
600 static int parse_setting_and_lang(struct skin_element *element,
601 struct wps_token *token,
602 struct wps_data *wps_data)
604 /* NOTE: both the string validations that happen in here will
605 * automatically PASS on checkwps because its too hard to get
606 * settings_list.c and english.lang built for it.
607 * If that ever changes remove the #ifndef __PCTOOL__'s here
609 (void)wps_data;
610 char *temp = element->params[0].data.text;
611 int i;
613 if (token->type == SKIN_TOKEN_TRANSLATEDSTRING)
615 #ifndef __PCTOOL__
616 i = lang_english_to_id(temp);
617 if (i < 0)
618 return WPS_ERROR_INVALID_PARAM;
619 #endif
621 else
623 /* Find the setting */
624 for (i=0; i<nb_settings; i++)
625 if (settings[i].cfg_name &&
626 !strcmp(settings[i].cfg_name, temp))
627 break;
628 #ifndef __PCTOOL__
629 if (i == nb_settings)
630 return WPS_ERROR_INVALID_PARAM;
631 #endif
633 /* Store the setting number */
634 token->value.i = i;
635 return 0;
637 static int parse_logical_if(struct skin_element *element,
638 struct wps_token *token,
639 struct wps_data *wps_data)
641 (void)wps_data;
642 char *op = element->params[1].data.text;
643 struct logical_if *lif = skin_buffer_alloc(sizeof(struct logical_if));
644 if (!lif)
645 return -1;
646 token->value.data = lif;
647 lif->token = element->params[0].data.code->data;
649 if (!strncmp(op, "=", 1))
650 lif->op = IF_EQUALS;
651 else if (!strncmp(op, "!=", 2))
652 lif->op = IF_NOTEQUALS;
653 else if (!strncmp(op, ">=", 2))
654 lif->op = IF_GREATERTHAN_EQ;
655 else if (!strncmp(op, "<=", 2))
656 lif->op = IF_LESSTHAN_EQ;
657 else if (!strncmp(op, ">", 2))
658 lif->op = IF_GREATERTHAN;
659 else if (!strncmp(op, "<", 1))
660 lif->op = IF_LESSTHAN;
662 memcpy(&lif->operand, &element->params[2], sizeof(lif->operand));
663 if (element->params_count > 3)
664 lif->num_options = element->params[3].data.number;
665 else
666 lif->num_options = TOKEN_VALUE_ONLY;
667 return 0;
671 static int parse_timeout_tag(struct skin_element *element,
672 struct wps_token *token,
673 struct wps_data *wps_data)
675 (void)wps_data;
676 int val = 0;
677 if (element->params_count == 0)
679 switch (token->type)
681 case SKIN_TOKEN_SUBLINE_TIMEOUT:
682 return -1;
683 case SKIN_TOKEN_BUTTON_VOLUME:
684 case SKIN_TOKEN_TRACK_STARTING:
685 case SKIN_TOKEN_TRACK_ENDING:
686 val = 10;
687 break;
688 default:
689 break;
692 else
693 val = element->params[0].data.number;
694 token->value.i = val * TIMEOUT_UNIT;
695 return 0;
698 static int parse_progressbar_tag(struct skin_element* element,
699 struct wps_token *token,
700 struct wps_data *wps_data)
702 #ifdef HAVE_LCD_BITMAP
703 struct progressbar *pb;
704 struct viewport *vp = &curr_vp->vp;
705 struct skin_tag_parameter *param = element->params;
706 int curr_param = 0;
707 char *image_filename = NULL;
709 if (element->params_count == 0 &&
710 element->tag->type != SKIN_TOKEN_PROGRESSBAR)
711 return 0; /* nothing to do */
712 pb = (struct progressbar*)skin_buffer_alloc(sizeof(struct progressbar));
714 token->value.data = pb;
716 if (!pb)
717 return WPS_ERROR_INVALID_PARAM;
718 pb->vp = vp;
719 pb->follow_lang_direction = follow_lang_direction > 0;
720 pb->nofill = false;
721 pb->nobar = false;
722 pb->image = NULL;
723 pb->slider = NULL;
724 pb->backdrop = NULL;
725 pb->invert_fill_direction = false;
726 pb->horizontal = true;
728 if (element->params_count == 0)
730 pb->x = 0;
731 pb->width = vp->width;
732 pb->height = SYSFONT_HEIGHT-2;
733 pb->y = -1; /* Will be computed during the rendering */
734 pb->type = element->tag->type;
735 return 0;
738 /* (x, y, width, height, ...) */
739 if (!isdefault(param))
740 pb->x = param->data.number;
741 else
742 pb->x = 0;
743 param++;
745 if (!isdefault(param))
746 pb->y = param->data.number;
747 else
748 pb->y = -1; /* computed at rendering */
749 param++;
751 if (!isdefault(param))
752 pb->width = param->data.number;
753 else
754 pb->width = vp->width - pb->x;
755 param++;
757 if (!isdefault(param))
759 /* A zero height makes no sense - reject it */
760 if (param->data.number == 0)
761 return WPS_ERROR_INVALID_PARAM;
763 pb->height = param->data.number;
765 else
767 if (vp->font > FONT_UI)
768 pb->height = -1; /* calculate at display time */
769 else
771 #ifndef __PCTOOL__
772 pb->height = font_get(vp->font)->height;
773 #else
774 pb->height = 8;
775 #endif
778 /* optional params, first is the image filename if it isnt recognised as a keyword */
780 curr_param = 4;
781 if (isdefault(&element->params[curr_param]))
783 param++;
784 curr_param++;
787 pb->horizontal = pb->width > pb->height;
788 while (curr_param < element->params_count)
790 param++;
791 if (!strcmp(param->data.text, "invert"))
792 pb->invert_fill_direction = true;
793 else if (!strcmp(param->data.text, "nofill"))
794 pb->nofill = true;
795 else if (!strcmp(param->data.text, "nobar"))
796 pb->nobar = true;
797 else if (!strcmp(param->data.text, "slider"))
799 if (curr_param+1 < element->params_count)
801 curr_param++;
802 param++;
803 pb->slider = skin_find_item(param->data.text,
804 SKIN_FIND_IMAGE, wps_data);
806 else /* option needs the next param */
807 return -1;
809 else if (!strcmp(param->data.text, "image"))
811 if (curr_param+1 < element->params_count)
813 curr_param++;
814 param++;
815 image_filename = param->data.text;
818 else /* option needs the next param */
819 return -1;
821 else if (!strcmp(param->data.text, "backdrop"))
823 if (curr_param+1 < element->params_count)
825 curr_param++;
826 param++;
827 pb->backdrop = skin_find_item(param->data.text,
828 SKIN_FIND_IMAGE, wps_data);
831 else /* option needs the next param */
832 return -1;
834 else if (!strcmp(param->data.text, "vertical"))
836 pb->horizontal = false;
837 if (isdefault(&element->params[3]))
838 pb->height = vp->height - pb->y;
840 else if (!strcmp(param->data.text, "horizontal"))
841 pb->horizontal = true;
842 else if (curr_param == 4)
843 image_filename = param->data.text;
845 curr_param++;
848 if (image_filename)
850 pb->image = skin_find_item(image_filename, SKIN_FIND_IMAGE, wps_data);
851 if (!pb->image) /* load later */
853 struct gui_img* img = (struct gui_img*)skin_buffer_alloc(sizeof(struct gui_img));
854 if (!img)
855 return WPS_ERROR_INVALID_PARAM;
856 /* save a pointer to the filename */
857 img->bm.data = (char*)image_filename;
858 img->label = image_filename;
859 img->x = 0;
860 img->y = 0;
861 img->num_subimages = 1;
862 img->always_display = false;
863 img->display = -1;
864 img->using_preloaded_icons = false;
865 img->vp = &curr_vp->vp;
866 struct skin_token_list *item =
867 (struct skin_token_list *)new_skin_token_list_item(NULL, img);
868 if (!item)
869 return WPS_ERROR_INVALID_PARAM;
870 add_to_ll_chain(&wps_data->images, item);
871 pb->image = img;
875 if (token->type == SKIN_TOKEN_VOLUME)
876 token->type = SKIN_TOKEN_VOLUMEBAR;
877 else if (token->type == SKIN_TOKEN_BATTERY_PERCENT)
878 token->type = SKIN_TOKEN_BATTERY_PERCENTBAR;
879 else if (token->type == SKIN_TOKEN_TUNER_RSSI)
880 token->type = SKIN_TOKEN_TUNER_RSSI_BAR;
881 else if (token->type == SKIN_TOKEN_PEAKMETER_LEFT)
882 token->type = SKIN_TOKEN_PEAKMETER_LEFTBAR;
883 else if (token->type == SKIN_TOKEN_PEAKMETER_RIGHT)
884 token->type = SKIN_TOKEN_PEAKMETER_RIGHTBAR;
885 pb->type = token->type;
887 return 0;
889 #else
890 (void)element;
891 if (token->type == SKIN_TOKEN_PROGRESSBAR ||
892 token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR)
894 wps_data->full_line_progressbar =
895 token->type == SKIN_TOKEN_PLAYER_PROGRESSBAR;
897 return 0;
899 #endif
902 #ifdef HAVE_ALBUMART
903 static int parse_albumart_load(struct skin_element* element,
904 struct wps_token *token,
905 struct wps_data *wps_data)
907 struct dim dimensions;
908 int albumart_slot;
909 bool swap_for_rtl = lang_is_rtl() && follow_lang_direction;
910 struct skin_albumart *aa =
911 (struct skin_albumart *)skin_buffer_alloc(sizeof(struct skin_albumart));
912 (void)token; /* silence warning */
913 if (!aa)
914 return -1;
916 /* reset albumart info in wps */
917 aa->width = -1;
918 aa->height = -1;
919 aa->xalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
920 aa->yalign = WPS_ALBUMART_ALIGN_CENTER; /* default */
922 aa->x = element->params[0].data.number;
923 aa->y = element->params[1].data.number;
924 aa->width = element->params[2].data.number;
925 aa->height = element->params[3].data.number;
927 aa->vp = &curr_vp->vp;
928 aa->draw_handle = -1;
930 /* if we got here, we parsed everything ok .. ! */
931 if (aa->width < 0)
932 aa->width = 0;
933 else if (aa->width > LCD_WIDTH)
934 aa->width = LCD_WIDTH;
936 if (aa->height < 0)
937 aa->height = 0;
938 else if (aa->height > LCD_HEIGHT)
939 aa->height = LCD_HEIGHT;
941 if (swap_for_rtl)
942 aa->x = LCD_WIDTH - (aa->x + aa->width);
944 aa->state = WPS_ALBUMART_LOAD;
945 wps_data->albumart = aa;
947 dimensions.width = aa->width;
948 dimensions.height = aa->height;
950 albumart_slot = playback_claim_aa_slot(&dimensions);
952 if (0 <= albumart_slot)
953 wps_data->playback_aa_slot = albumart_slot;
955 if (element->params_count > 4 && !isdefault(&element->params[4]))
957 switch (*element->params[4].data.text)
959 case 'l':
960 case 'L':
961 if (swap_for_rtl)
962 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
963 else
964 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
965 break;
966 case 'c':
967 case 'C':
968 aa->xalign = WPS_ALBUMART_ALIGN_CENTER;
969 break;
970 case 'r':
971 case 'R':
972 if (swap_for_rtl)
973 aa->xalign = WPS_ALBUMART_ALIGN_LEFT;
974 else
975 aa->xalign = WPS_ALBUMART_ALIGN_RIGHT;
976 break;
979 if (element->params_count > 5 && !isdefault(&element->params[5]))
981 switch (*element->params[5].data.text)
983 case 't':
984 case 'T':
985 aa->yalign = WPS_ALBUMART_ALIGN_TOP;
986 break;
987 case 'c':
988 case 'C':
989 aa->yalign = WPS_ALBUMART_ALIGN_CENTER;
990 break;
991 case 'b':
992 case 'B':
993 aa->yalign = WPS_ALBUMART_ALIGN_BOTTOM;
994 break;
997 return 0;
1000 #endif /* HAVE_ALBUMART */
1001 #ifdef HAVE_SKIN_VARIABLES
1002 static struct skin_var* find_or_add_var(const char* label,
1003 struct wps_data *data)
1005 struct skin_var* ret = skin_find_item(label, SKIN_VARIABLE, data);
1006 if (!ret)
1008 ret = (struct skin_var*)skin_buffer_alloc(sizeof(struct skin_var));
1009 if (!ret)
1010 return NULL;
1011 ret->label = label;
1012 ret->value = 1;
1013 ret->last_changed = 0xffff;
1014 struct skin_token_list *item = new_skin_token_list_item(NULL, ret);
1015 if (!item)
1016 return NULL;
1017 add_to_ll_chain(&data->skinvars, item);
1019 return ret;
1021 static int parse_skinvar( struct skin_element *element,
1022 struct wps_token *token,
1023 struct wps_data *wps_data)
1025 const char* label = element->params[0].data.text;
1026 struct skin_var* var = find_or_add_var(label, wps_data);
1027 if (!var)
1028 return WPS_ERROR_INVALID_PARAM;
1029 switch (token->type)
1031 case SKIN_TOKEN_VAR_GETVAL:
1032 token->value.data = var;
1033 break;
1034 case SKIN_TOKEN_VAR_SET:
1036 struct skin_var_changer *data =
1037 (struct skin_var_changer*)skin_buffer_alloc(
1038 sizeof(struct skin_var_changer));
1039 if (!data)
1040 return WPS_ERROR_INVALID_PARAM;
1041 data->var = var;
1042 data->newval = element->params[2].data.number;
1043 data->max = 0;
1044 if (!strcmp(element->params[1].data.text, "set"))
1045 data->direct = true;
1046 else if (!strcmp(element->params[1].data.text, "inc"))
1048 data->direct = false;
1050 else if (!strcmp(element->params[1].data.text, "dec"))
1052 data->direct = false;
1053 data->newval *= -1;
1055 if (element->params_count > 3)
1056 data->max = element->params[3].data.number;
1057 token->value.data = data;
1059 break;
1060 case SKIN_TOKEN_VAR_TIMEOUT:
1062 struct skin_var_lastchange *data =
1063 (struct skin_var_lastchange*)skin_buffer_alloc(
1064 sizeof(struct skin_var_lastchange));
1065 if (!data)
1066 return WPS_ERROR_INVALID_PARAM;
1067 data->var = var;
1068 data->timeout = 10;
1069 if (element->params_count > 1)
1070 data->timeout = element->params[1].data.number;
1071 data->timeout *= TIMEOUT_UNIT;
1072 token->value.data = data;
1074 break;
1075 default: /* kill the warning */
1076 break;
1078 return 0;
1080 #endif /* HAVE_SKIN_VARIABLES */
1081 #ifdef HAVE_TOUCHSCREEN
1082 static int parse_lasttouch(struct skin_element *element,
1083 struct wps_token *token,
1084 struct wps_data *wps_data)
1086 struct touchregion_lastpress *data =
1087 (struct touchregion_lastpress*)skin_buffer_alloc(
1088 sizeof(struct touchregion_lastpress));
1089 int i;
1090 if (!data)
1091 return WPS_ERROR_INVALID_PARAM;
1092 data->region = NULL;
1093 data->timeout = 10;
1095 for (i=0; i<element->params_count; i++)
1097 if (element->params[i].type == STRING)
1098 data->region = skin_find_item(element->params[i].data.text,
1099 SKIN_FIND_TOUCHREGION, wps_data);
1100 else if (element->params[i].type == INTEGER ||
1101 element->params[i].type == DECIMAL)
1102 data->timeout = element->params[i].data.number;
1105 data->timeout *= TIMEOUT_UNIT;
1106 token->value.data = data;
1107 return 0;
1110 struct touchaction {const char* s; int action;};
1111 static const struct touchaction touchactions[] = {
1112 /* generic actions, convert to screen actions on use */
1113 {"none", ACTION_TOUCHSCREEN}, {"lock", ACTION_TOUCH_SOFTLOCK },
1114 {"prev", ACTION_STD_PREV }, {"next", ACTION_STD_NEXT },
1115 {"rwd", ACTION_STD_PREVREPEAT }, {"ffwd", ACTION_STD_NEXTREPEAT },
1116 {"hotkey", ACTION_STD_HOTKEY}, {"select", ACTION_STD_OK },
1117 {"menu", ACTION_STD_MENU }, {"cancel", ACTION_STD_CANCEL },
1118 {"contextmenu", ACTION_STD_CONTEXT},{"quickscreen", ACTION_STD_QUICKSCREEN },
1120 /* list/tree actions */
1121 { "resumeplayback", ACTION_TREE_WPS}, /* returns to previous music, WPS/FM */
1122 /* not really WPS specific, but no equivilant ACTION_STD_* */
1123 {"voldown", ACTION_WPS_VOLDOWN}, {"volup", ACTION_WPS_VOLUP},
1124 {"mute", ACTION_TOUCH_MUTE },
1126 /* generic settings changers */
1127 {"setting_inc", ACTION_SETTINGS_INC}, {"setting_dec", ACTION_SETTINGS_DEC},
1128 {"setting_set", ACTION_SETTINGS_SET},
1130 /* WPS specific actions */
1131 {"wps_prev", ACTION_WPS_SKIPPREV }, {"wps_next", ACTION_WPS_SKIPNEXT },
1132 {"browse", ACTION_WPS_BROWSE },
1133 {"play", ACTION_WPS_PLAY }, {"stop", ACTION_WPS_STOP },
1134 {"shuffle", ACTION_TOUCH_SHUFFLE }, {"repmode", ACTION_TOUCH_REPMODE },
1135 {"pitch", ACTION_WPS_PITCHSCREEN}, {"playlist", ACTION_WPS_VIEW_PLAYLIST },
1137 #if CONFIG_TUNER
1138 /* FM screen actions */
1139 /* Also allow browse, play, stop from WPS codes */
1140 {"mode", ACTION_FM_MODE }, {"record", ACTION_FM_RECORD },
1141 {"presets", ACTION_FM_PRESET},
1142 #endif
1145 static int touchregion_setup_setting(struct skin_element *element, int param_no,
1146 struct touchregion *region)
1148 int p = param_no;
1149 char *name = element->params[p++].data.text;
1150 int j;
1151 /* Find the setting */
1152 for (j=0; j<nb_settings; j++)
1153 if (settings[j].cfg_name &&
1154 !strcmp(settings[j].cfg_name, name))
1155 break;
1156 if (j==nb_settings)
1157 return WPS_ERROR_INVALID_PARAM;
1158 region->setting_data.setting = (void*)&settings[j];
1159 if (region->action == ACTION_SETTINGS_SET)
1161 char* text;
1162 int temp;
1163 struct touchsetting *setting =
1164 &region->setting_data;
1165 if (element->params_count < p+1)
1166 return -1;
1167 #ifndef __PCTOOL__
1168 text = element->params[p++].data.text;
1169 switch (settings[j].flags&F_T_MASK)
1171 case F_T_CUSTOM:
1172 setting->value.text = text;
1173 break;
1174 case F_T_INT:
1175 case F_T_UINT:
1176 if (settings[j].cfg_vals == NULL)
1178 setting->value.number = atoi(text);
1180 else if (cfg_string_to_int(j, &temp, text))
1182 if (settings[j].flags&F_TABLE_SETTING)
1183 setting->value.number =
1184 settings[j].table_setting->values[temp];
1185 else
1186 setting->value.number = temp;
1188 else
1189 return -1;
1190 break;
1191 case F_T_BOOL:
1192 if (cfg_string_to_int(j, &temp, text))
1194 setting->value.number = temp;
1196 else
1197 return -1;
1198 break;
1199 default:
1200 return -1;
1202 #endif /* __PCTOOL__ */
1204 return p-param_no;
1207 static int parse_touchregion(struct skin_element *element,
1208 struct wps_token *token,
1209 struct wps_data *wps_data)
1211 (void)token;
1212 unsigned i, imax;
1213 int p;
1214 struct touchregion *region = NULL;
1215 const char *action;
1216 const char pb_string[] = "progressbar";
1217 const char vol_string[] = "volume";
1219 /* format: %T([label,], x,y,width,height,action[, ...])
1220 * if action starts with & the area must be held to happen
1224 region = (struct touchregion*)skin_buffer_alloc(sizeof(struct touchregion));
1225 if (!region)
1226 return WPS_ERROR_INVALID_PARAM;
1228 /* should probably do some bounds checking here with the viewport... but later */
1229 region->action = ACTION_NONE;
1231 if (element->params[0].type == STRING)
1233 region->label = element->params[0].data.text;
1234 p = 1;
1235 /* "[SI]III[SI]|SS" is the param list. There MUST be 4 numbers
1236 * followed by at least one string. Verify that here */
1237 if (element->params_count < 6 ||
1238 element->params[4].type != INTEGER)
1239 return WPS_ERROR_INVALID_PARAM;
1241 else
1243 region->label = NULL;
1244 p = 0;
1247 region->x = element->params[p++].data.number;
1248 region->y = element->params[p++].data.number;
1249 region->width = element->params[p++].data.number;
1250 region->height = element->params[p++].data.number;
1251 region->wvp = curr_vp;
1252 region->armed = false;
1253 region->reverse_bar = false;
1254 region->value = 0;
1255 region->last_press = 0xffff;
1256 region->press_length = PRESS;
1257 region->allow_while_locked = false;
1258 action = element->params[p++].data.text;
1260 /* figure out the action */
1261 if(!strcmp(pb_string, action))
1262 region->action = ACTION_TOUCH_SCROLLBAR;
1263 else if(!strcmp(vol_string, action))
1264 region->action = ACTION_TOUCH_VOLUME;
1265 else
1267 imax = ARRAYLEN(touchactions);
1268 for (i = 0; i < imax; i++)
1270 /* try to match with one of our touchregion screens */
1271 if (!strcmp(touchactions[i].s, action))
1273 region->action = touchactions[i].action;
1274 if (region->action == ACTION_SETTINGS_INC ||
1275 region->action == ACTION_SETTINGS_DEC ||
1276 region->action == ACTION_SETTINGS_SET)
1278 int val;
1279 if (element->params_count < p+1)
1280 return WPS_ERROR_INVALID_PARAM;
1281 val = touchregion_setup_setting(element, p, region);
1282 if (val < 0)
1283 return WPS_ERROR_INVALID_PARAM;
1284 p += val;
1286 break;
1289 if (region->action == ACTION_NONE)
1290 return WPS_ERROR_INVALID_PARAM;
1292 while (p < element->params_count)
1294 char* param = element->params[p++].data.text;
1295 if (!strcmp(param, "allow_while_locked"))
1296 region->allow_while_locked = true;
1297 else if (!strcmp(param, "reverse_bar"))
1298 region->reverse_bar = true;
1299 else if (!strcmp(param, "repeat_press"))
1300 region->press_length = REPEAT;
1301 else if (!strcmp(param, "long_press"))
1302 region->press_length = LONG_PRESS;
1304 struct skin_token_list *item = new_skin_token_list_item(NULL, region);
1305 if (!item)
1306 return WPS_ERROR_INVALID_PARAM;
1307 add_to_ll_chain(&wps_data->touchregions, item);
1309 if (region->action == ACTION_TOUCH_MUTE)
1311 region->value = global_settings.volume;
1315 return 0;
1317 #endif
1319 static bool check_feature_tag(const int type)
1321 switch (type)
1323 case SKIN_TOKEN_RTC_PRESENT:
1324 #if CONFIG_RTC
1325 return true;
1326 #else
1327 return false;
1328 #endif
1329 case SKIN_TOKEN_HAVE_RECORDING:
1330 #ifdef HAVE_RECORDING
1331 return true;
1332 #else
1333 return false;
1334 #endif
1335 case SKIN_TOKEN_HAVE_TUNER:
1336 #if CONFIG_TUNER
1337 if (radio_hardware_present())
1338 return true;
1339 #endif
1340 return false;
1341 case SKIN_TOKEN_HAVE_TOUCH:
1342 #ifdef HAVE_TOUCHSCREEN
1343 return true;
1344 #else
1345 return false;
1346 #endif
1348 #if CONFIG_TUNER
1349 case SKIN_TOKEN_HAVE_RDS:
1350 #ifdef HAVE_RDS_CAP
1351 return true;
1352 #else
1353 return false;
1354 #endif /* HAVE_RDS_CAP */
1355 #endif /* CONFIG_TUNER */
1356 default: /* not a tag we care about, just don't skip */
1357 return true;
1362 * initial setup of wps_data; does reset everything
1363 * except fields which need to survive, i.e.
1366 static void skin_data_reset(struct wps_data *wps_data)
1368 wps_data->tree = NULL;
1369 #ifdef HAVE_LCD_BITMAP
1370 wps_data->images = NULL;
1371 #endif
1372 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1373 if (wps_data->backdrop_id >= 0)
1374 skin_backdrop_unload(wps_data->backdrop_id);
1375 wps_data->backdrop = NULL;
1376 #endif
1377 #ifdef HAVE_TOUCHSCREEN
1378 wps_data->touchregions = NULL;
1379 #endif
1380 #ifdef HAVE_SKIN_VARIABLES
1381 wps_data->skinvars = NULL;
1382 #endif
1383 #ifdef HAVE_ALBUMART
1384 wps_data->albumart = NULL;
1385 if (wps_data->playback_aa_slot >= 0)
1387 playback_release_aa_slot(wps_data->playback_aa_slot);
1388 wps_data->playback_aa_slot = -1;
1390 #endif
1392 #ifdef HAVE_LCD_BITMAP
1393 wps_data->peak_meter_enabled = false;
1394 wps_data->wps_sb_tag = false;
1395 wps_data->show_sb_on_wps = false;
1396 #else /* HAVE_LCD_CHARCELLS */
1397 /* progress bars */
1398 int i;
1399 for (i = 0; i < 8; i++)
1401 wps_data->wps_progress_pat[i] = 0;
1403 wps_data->full_line_progressbar = false;
1404 #endif
1405 wps_data->wps_loaded = false;
1408 #ifdef HAVE_LCD_BITMAP
1409 static bool load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
1411 (void)wps_data; /* only needed for remote targets */
1412 char img_path[MAX_PATH];
1413 int fd;
1414 get_image_filename(bitmap->data, bmpdir,
1415 img_path, sizeof(img_path));
1417 /* load the image */
1418 int format;
1419 #ifdef HAVE_REMOTE_LCD
1420 if (curr_screen == SCREEN_REMOTE)
1421 format = FORMAT_ANY|FORMAT_REMOTE;
1422 else
1423 #endif
1424 format = FORMAT_ANY|FORMAT_TRANSPARENT;
1426 fd = open(img_path, O_RDONLY);
1427 if (fd < 0)
1429 DEBUGF("Couldn't open %s\n", img_path);
1430 return false;
1432 size_t buf_size = read_bmp_fd(fd, bitmap, 0,
1433 format|FORMAT_RETURN_SIZE, NULL);
1434 char* imgbuf = (char*)skin_buffer_alloc(buf_size);
1435 if (!imgbuf)
1437 #ifndef APPLICATION
1438 DEBUGF("Not enough skin buffer: need %zd more.\n",
1439 buf_size - skin_buffer_freespace());
1440 #endif
1441 close(fd);
1442 return NULL;
1444 lseek(fd, 0, SEEK_SET);
1445 bitmap->data = imgbuf;
1446 int ret = read_bmp_fd(fd, bitmap, buf_size, format, NULL);
1448 close(fd);
1449 if (ret > 0)
1451 return true;
1453 else
1455 /* Abort if we can't load an image */
1456 DEBUGF("Couldn't load '%s'\n", img_path);
1457 return false;
1461 static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
1463 struct skin_token_list *list;
1464 bool retval = true; /* return false if a single image failed to load */
1466 /* regular images */
1467 list = wps_data->images;
1468 while (list)
1470 struct gui_img *img = (struct gui_img*)list->token->value.data;
1471 if (img->bm.data)
1473 if (img->using_preloaded_icons)
1475 img->loaded = true;
1476 list->token->type = SKIN_TOKEN_IMAGE_DISPLAY_LISTICON;
1478 else
1480 img->loaded = load_skin_bmp(wps_data, &img->bm, bmpdir);
1481 if (img->loaded)
1482 img->subimage_height = img->bm.height / img->num_subimages;
1483 else
1484 retval = false;
1487 list = list->next;
1490 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1491 wps_data->backdrop_id = skin_backdrop_assign(wps_data->backdrop, bmpdir, curr_screen);
1492 #endif /* has backdrop support */
1493 return retval;
1496 static bool skin_load_fonts(struct wps_data *data)
1498 /* don't spit out after the first failue to aid debugging */
1499 bool success = true;
1500 struct skin_element *vp_list;
1501 int font_id;
1502 /* walk though each viewport and assign its font */
1503 for(vp_list = data->tree; vp_list; vp_list = vp_list->next)
1505 /* first, find the viewports that have a non-sys/ui-font font */
1506 struct skin_viewport *skin_vp =
1507 (struct skin_viewport*)vp_list->data;
1508 struct viewport *vp = &skin_vp->vp;
1511 if (vp->font <= FONT_UI)
1512 { /* the usual case -> built-in fonts */
1513 #ifdef HAVE_REMOTE_LCD
1514 if (vp->font == FONT_UI)
1515 vp->font += curr_screen;
1516 #endif
1517 continue;
1519 font_id = vp->font;
1521 /* now find the corresponding skin_font */
1522 struct skin_font *font = &skinfonts[font_id-FONT_FIRSTUSERFONT];
1523 if (!font->name)
1525 if (success)
1527 DEBUGF("font %d not specified\n", font_id);
1529 success = false;
1530 continue;
1533 /* load the font - will handle loading the same font again if
1534 * multiple viewports use the same */
1535 if (font->id < 0)
1537 char *dot = strchr(font->name, '.');
1538 *dot = '\0';
1539 font->id = skin_font_load(font->name,
1540 skinfonts[font_id-FONT_FIRSTUSERFONT].glyphs);
1543 if (font->id < 0)
1545 DEBUGF("Unable to load font %d: '%s.fnt'\n",
1546 font_id, font->name);
1547 font->name = NULL; /* to stop trying to load it again if we fail */
1548 success = false;
1549 font->name = NULL;
1550 continue;
1553 /* finally, assign the font_id to the viewport */
1554 vp->font = font->id;
1556 return success;
1559 #endif /* HAVE_LCD_BITMAP */
1560 static int convert_viewport(struct wps_data *data, struct skin_element* element)
1562 struct skin_viewport *skin_vp =
1563 (struct skin_viewport *)skin_buffer_alloc(sizeof(struct skin_viewport));
1564 struct screen *display = &screens[curr_screen];
1566 if (!skin_vp)
1567 return CALLBACK_ERROR;
1569 skin_vp->hidden_flags = 0;
1570 skin_vp->label = NULL;
1571 skin_vp->is_infovp = false;
1572 element->data = skin_vp;
1573 curr_vp = skin_vp;
1574 curr_viewport_element = element;
1576 viewport_set_defaults(&skin_vp->vp, curr_screen);
1577 #ifdef HAVE_REMOTE_LCD
1578 /* viewport_set_defaults() sets the font to FONT_UI+curr_screen.
1579 * This parser requires font 1 to always be the UI font,
1580 * so force it back to FONT_UI and handle the screen number at the end */
1581 skin_vp->vp.font = FONT_UI;
1582 #endif
1584 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1585 skin_vp->start_fgcolour = skin_vp->vp.fg_pattern;
1586 skin_vp->start_bgcolour = skin_vp->vp.bg_pattern;
1587 #endif
1588 #ifdef HAVE_LCD_COLOR
1589 skin_vp->start_gradient.start = skin_vp->vp.lss_pattern;
1590 skin_vp->start_gradient.end = skin_vp->vp.lse_pattern;
1591 skin_vp->start_gradient.text = skin_vp->vp.lst_pattern;
1592 #endif
1595 struct skin_tag_parameter *param = element->params;
1596 if (element->params_count == 0) /* default viewport */
1598 if (!data->tree) /* first viewport in the skin */
1599 data->tree = element;
1600 skin_vp->label = VP_DEFAULT_LABEL;
1601 return CALLBACK_OK;
1604 if (element->params_count == 6)
1606 if (element->tag->type == SKIN_TOKEN_UIVIEWPORT_LOAD)
1608 skin_vp->is_infovp = true;
1609 if (isdefault(param))
1611 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
1612 skin_vp->label = VP_DEFAULT_LABEL;
1614 else
1616 skin_vp->hidden_flags = VP_NEVER_VISIBLE;
1617 skin_vp->label = param->data.text;
1620 else
1622 skin_vp->hidden_flags = VP_DRAW_HIDEABLE|VP_DRAW_HIDDEN;
1623 skin_vp->label = param->data.text;
1625 param++;
1627 /* x */
1628 if (!isdefault(param))
1630 skin_vp->vp.x = param->data.number;
1631 if (param->data.number < 0)
1632 skin_vp->vp.x += display->lcdwidth;
1634 param++;
1635 /* y */
1636 if (!isdefault(param))
1638 skin_vp->vp.y = param->data.number;
1639 if (param->data.number < 0)
1640 skin_vp->vp.y += display->lcdheight;
1642 param++;
1643 /* width */
1644 if (!isdefault(param))
1646 skin_vp->vp.width = param->data.number;
1647 if (param->data.number < 0)
1648 skin_vp->vp.width = (skin_vp->vp.width + display->lcdwidth) - skin_vp->vp.x;
1650 else
1652 skin_vp->vp.width = display->lcdwidth - skin_vp->vp.x;
1654 param++;
1655 /* height */
1656 if (!isdefault(param))
1658 skin_vp->vp.height = param->data.number;
1659 if (param->data.number < 0)
1660 skin_vp->vp.height = (skin_vp->vp.height + display->lcdheight) - skin_vp->vp.y;
1662 else
1664 skin_vp->vp.height = display->lcdheight - skin_vp->vp.y;
1666 param++;
1667 #ifdef HAVE_LCD_BITMAP
1668 /* font */
1669 if (!isdefault(param))
1671 skin_vp->vp.font = param->data.number;
1673 #endif
1674 if ((unsigned) skin_vp->vp.x >= (unsigned) display->lcdwidth ||
1675 skin_vp->vp.width + skin_vp->vp.x > display->lcdwidth ||
1676 (unsigned) skin_vp->vp.y >= (unsigned) display->lcdheight ||
1677 skin_vp->vp.height + skin_vp->vp.y > display->lcdheight)
1678 return CALLBACK_ERROR;
1680 return CALLBACK_OK;
1683 static int skin_element_callback(struct skin_element* element, void* data)
1685 struct wps_data *wps_data = (struct wps_data *)data;
1686 struct wps_token *token;
1687 parse_function function = NULL;
1689 switch (element->type)
1691 /* IMPORTANT: element params are shared, so copy them if needed
1692 * or use then NOW, dont presume they have a long lifespan
1694 case TAG:
1696 token = (struct wps_token*)skin_buffer_alloc(sizeof(struct wps_token));
1697 memset(token, 0, sizeof(*token));
1698 token->type = element->tag->type;
1700 if (element->tag->flags&SKIN_RTC_REFRESH)
1702 #if CONFIG_RTC
1703 curr_line->update_mode |= SKIN_REFRESH_DYNAMIC;
1704 #else
1705 curr_line->update_mode |= SKIN_REFRESH_STATIC;
1706 #endif
1708 else
1709 curr_line->update_mode |= element->tag->flags&SKIN_REFRESH_ALL;
1711 element->data = token;
1713 /* Some tags need special handling for the tag, so add them here */
1714 switch (token->type)
1716 case SKIN_TOKEN_ALIGN_LANGDIRECTION:
1717 follow_lang_direction = 2;
1718 break;
1719 case SKIN_TOKEN_LOGICAL_IF:
1720 function = parse_logical_if;
1721 break;
1722 case SKIN_TOKEN_PROGRESSBAR:
1723 case SKIN_TOKEN_VOLUME:
1724 case SKIN_TOKEN_BATTERY_PERCENT:
1725 case SKIN_TOKEN_PLAYER_PROGRESSBAR:
1726 case SKIN_TOKEN_PEAKMETER_LEFT:
1727 case SKIN_TOKEN_PEAKMETER_RIGHT:
1728 #ifdef HAVE_RADIO_RSSI
1729 case SKIN_TOKEN_TUNER_RSSI:
1730 #endif
1731 function = parse_progressbar_tag;
1732 break;
1733 case SKIN_TOKEN_SUBLINE_TIMEOUT:
1734 case SKIN_TOKEN_BUTTON_VOLUME:
1735 case SKIN_TOKEN_TRACK_STARTING:
1736 case SKIN_TOKEN_TRACK_ENDING:
1737 function = parse_timeout_tag;
1738 break;
1739 #ifdef HAVE_LCD_BITMAP
1740 case SKIN_TOKEN_DISABLE_THEME:
1741 case SKIN_TOKEN_ENABLE_THEME:
1742 case SKIN_TOKEN_DRAW_INBUILTBAR:
1743 function = parse_statusbar_tags;
1744 break;
1745 case SKIN_TOKEN_LIST_TITLE_TEXT:
1746 #ifndef __PCTOOL__
1747 sb_skin_has_title(curr_screen);
1748 #endif
1749 break;
1750 #endif
1751 case SKIN_TOKEN_FILE_DIRECTORY:
1752 token->value.i = element->params[0].data.number;
1753 break;
1754 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
1755 case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
1756 case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
1757 function = parse_viewportcolour;
1758 break;
1759 case SKIN_TOKEN_IMAGE_BACKDROP:
1760 function = parse_image_special;
1761 break;
1762 #endif
1763 case SKIN_TOKEN_VIEWPORT_TEXTSTYLE:
1764 function = parse_viewporttextstyle;
1765 break;
1766 #ifdef HAVE_LCD_COLOR
1767 case SKIN_TOKEN_VIEWPORT_GRADIENT_SETUP:
1768 function = parse_viewport_gradient_setup;
1769 break;
1770 #endif
1771 case SKIN_TOKEN_TRANSLATEDSTRING:
1772 case SKIN_TOKEN_SETTING:
1773 function = parse_setting_and_lang;
1774 break;
1775 #ifdef HAVE_LCD_BITMAP
1776 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
1777 function = parse_playlistview;
1778 break;
1779 case SKIN_TOKEN_LOAD_FONT:
1780 function = parse_font_load;
1781 break;
1782 case SKIN_TOKEN_VIEWPORT_ENABLE:
1783 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
1784 token->value.data = element->params[0].data.text;
1785 break;
1786 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
1787 function = parse_image_display;
1788 break;
1789 case SKIN_TOKEN_IMAGE_PRELOAD:
1790 case SKIN_TOKEN_IMAGE_DISPLAY:
1791 function = parse_image_load;
1792 break;
1793 #endif
1794 #ifdef HAVE_TOUCHSCREEN
1795 case SKIN_TOKEN_TOUCHREGION:
1796 function = parse_touchregion;
1797 break;
1798 case SKIN_TOKEN_LASTTOUCH:
1799 function = parse_lasttouch;
1800 break;
1801 #endif
1802 #ifdef HAVE_ALBUMART
1803 case SKIN_TOKEN_ALBUMART_DISPLAY:
1804 if (wps_data->albumart)
1805 wps_data->albumart->vp = &curr_vp->vp;
1806 break;
1807 case SKIN_TOKEN_ALBUMART_LOAD:
1808 function = parse_albumart_load;
1809 break;
1810 #endif
1811 #ifdef HAVE_SKIN_VARIABLES
1812 case SKIN_TOKEN_VAR_SET:
1813 case SKIN_TOKEN_VAR_GETVAL:
1814 case SKIN_TOKEN_VAR_TIMEOUT:
1815 function = parse_skinvar;
1816 break;
1817 #endif
1818 default:
1819 break;
1821 if (function)
1823 if (function(element, token, wps_data) < 0)
1824 return CALLBACK_ERROR;
1826 /* tags that start with 'F', 'I' or 'D' are for the next file */
1827 if ( *(element->tag->name) == 'I' || *(element->tag->name) == 'F' ||
1828 *(element->tag->name) == 'D')
1829 token->next = true;
1830 if (follow_lang_direction > 0 )
1831 follow_lang_direction--;
1832 break;
1834 case VIEWPORT:
1835 return convert_viewport(wps_data, element);
1836 case LINE:
1838 struct line *line =
1839 (struct line *)skin_buffer_alloc(sizeof(struct line));
1840 line->update_mode = SKIN_REFRESH_STATIC;
1841 curr_line = line;
1842 element->data = line;
1844 break;
1845 case LINE_ALTERNATOR:
1847 struct line_alternator *alternator =
1848 (struct line_alternator *)skin_buffer_alloc(sizeof(struct line_alternator));
1849 alternator->current_line = 0;
1850 #ifndef __PCTOOL__
1851 alternator->next_change_tick = current_tick;
1852 #endif
1853 element->data = alternator;
1855 break;
1856 case CONDITIONAL:
1858 struct conditional *conditional =
1859 (struct conditional *)skin_buffer_alloc(sizeof(struct conditional));
1860 conditional->last_value = -1;
1861 conditional->token = element->data;
1862 element->data = conditional;
1863 if (!check_feature_tag(element->tag->type))
1865 return FEATURE_NOT_AVAILABLE;
1867 return CALLBACK_OK;
1869 case TEXT:
1870 curr_line->update_mode |= SKIN_REFRESH_STATIC;
1871 break;
1872 default:
1873 break;
1875 return CALLBACK_OK;
1878 /* to setup up the wps-data from a format-buffer (isfile = false)
1879 from a (wps-)file (isfile = true)*/
1880 bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
1881 const char *buf, bool isfile)
1883 char *wps_buffer = NULL;
1884 if (!wps_data || !buf)
1885 return false;
1886 #ifdef HAVE_ALBUMART
1887 int status;
1888 struct mp3entry *curtrack;
1889 long offset;
1890 struct skin_albumart old_aa = {.state = WPS_ALBUMART_NONE};
1891 if (wps_data->albumart)
1893 old_aa.state = wps_data->albumart->state;
1894 old_aa.height = wps_data->albumart->height;
1895 old_aa.width = wps_data->albumart->width;
1897 #endif
1898 #ifdef HAVE_LCD_BITMAP
1899 int i;
1900 for (i=0;i<MAXUSERFONTS;i++)
1902 skinfonts[i].id = -1;
1903 skinfonts[i].name = NULL;
1905 #endif
1906 #ifdef DEBUG_SKIN_ENGINE
1907 if (isfile && debug_wps)
1909 DEBUGF("\n=====================\nLoading '%s'\n=====================\n", buf);
1911 #endif
1914 skin_data_reset(wps_data);
1915 wps_data->wps_loaded = false;
1916 curr_screen = screen;
1917 curr_line = NULL;
1918 curr_vp = NULL;
1919 curr_viewport_element = NULL;
1921 if (isfile)
1923 int fd = open_utf8(buf, O_RDONLY);
1925 if (fd < 0)
1926 return false;
1928 /* get buffer space from the plugin buffer */
1929 size_t buffersize = 0;
1930 wps_buffer = (char *)plugin_get_buffer(&buffersize);
1932 if (!wps_buffer)
1933 return false;
1935 /* copy the file's content to the buffer for parsing,
1936 ensuring that every line ends with a newline char. */
1937 unsigned int start = 0;
1938 while(read_line(fd, wps_buffer + start, buffersize - start) > 0)
1940 start += strlen(wps_buffer + start);
1941 if (start < buffersize - 1)
1943 wps_buffer[start++] = '\n';
1944 wps_buffer[start] = 0;
1947 close(fd);
1948 if (start <= 0)
1949 return false;
1951 else
1953 wps_buffer = (char*)buf;
1955 #if LCD_DEPTH > 1 || defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
1956 wps_data->backdrop = "-";
1957 wps_data->backdrop_id = -1;
1958 #endif
1959 /* parse the skin source */
1960 #ifndef APPLICATION
1961 skin_buffer_save_position();
1962 #endif
1963 wps_data->tree = skin_parse(wps_buffer, skin_element_callback, wps_data);
1964 if (!wps_data->tree) {
1965 skin_data_reset(wps_data);
1966 #ifndef APPLICATION
1967 skin_buffer_restore_position();
1968 #endif
1969 return false;
1972 #ifdef HAVE_LCD_BITMAP
1973 char bmpdir[MAX_PATH];
1974 if (isfile)
1976 /* get the bitmap dir */
1977 char *dot = strrchr(buf, '.');
1978 strlcpy(bmpdir, buf, dot - buf + 1);
1980 else
1982 snprintf(bmpdir, MAX_PATH, "%s", BACKDROP_DIR);
1984 /* load the bitmaps that were found by the parsing */
1985 if (!load_skin_bitmaps(wps_data, bmpdir) ||
1986 !skin_load_fonts(wps_data))
1988 skin_data_reset(wps_data);
1989 #ifndef APPLICATION
1990 skin_buffer_restore_position();
1991 #endif
1992 return false;
1994 #endif
1995 #if defined(HAVE_ALBUMART) && !defined(__PCTOOL__)
1996 status = audio_status();
1997 if (status & AUDIO_STATUS_PLAY)
1999 struct skin_albumart *aa = wps_data->albumart;
2000 if (aa && ((aa->state && !old_aa.state) ||
2001 (aa->state &&
2002 (((old_aa.height != aa->height) ||
2003 (old_aa.width != aa->width))))))
2005 curtrack = audio_current_track();
2006 offset = curtrack->offset;
2007 audio_stop();
2008 if (!(status & AUDIO_STATUS_PAUSE))
2009 audio_play(offset);
2012 #endif
2013 wps_data->wps_loaded = true;
2014 #ifdef DEBUG_SKIN_ENGINE
2015 // if (isfile && debug_wps)
2016 // debug_skin_usage();
2017 #endif
2018 return true;