Move c/h files implementing/defining standard library stuff into a new libc directory...
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
blobd3169be43a33054a51b577c1a8bbf15a1c9b66ca
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002-2007 Björn Stenberg
11 * Copyright (C) 2007-2008 Nicolas Pennequin
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 ****************************************************************************/
22 #include "config.h"
23 #include <stdio.h>
24 #include "string-extra.h"
25 #include "misc.h"
26 #include "font.h"
27 #include "system.h"
28 #include "rbunicode.h"
29 #include "sound.h"
30 #include "powermgmt.h"
31 #ifdef DEBUG
32 #include "debug.h"
33 #endif
34 #include "abrepeat.h"
35 #include "lang.h"
36 #include "language.h"
37 #include "statusbar.h"
38 #include "settings.h"
39 #include "scrollbar.h"
40 #include "screen_access.h"
41 #include "playlist.h"
42 #include "audio.h"
43 #include "tagcache.h"
45 #ifdef HAVE_LCD_BITMAP
46 #include "peakmeter.h"
47 /* Image stuff */
48 #include "bmp.h"
49 #ifdef HAVE_ALBUMART
50 #include "albumart.h"
51 #endif
52 #endif
54 #include "cuesheet.h"
55 #if CONFIG_CODEC == SWCODEC
56 #include "playback.h"
57 #endif
58 #include "backdrop.h"
59 #include "viewport.h"
62 #include "wps_internals.h"
63 #include "skin_engine.h"
64 #include "statusbar-skinned.h"
66 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode);
68 /* update a skinned screen, update_type is WPS_REFRESH_* values.
69 * Usually it should only be WPS_REFRESH_NON_STATIC
70 * A full update will be done if required (state.do_full_update == true)
72 bool skin_update(struct gui_wps *gwps, unsigned int update_type)
74 bool retval;
75 /* This maybe shouldnt be here, but while the skin is only used to
76 * display the music screen this is better than whereever we are being
77 * called from. This is also safe for skined screen which dont use the id3 */
78 struct mp3entry *id3 = gwps->state->id3;
79 bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
80 gwps->sync_data->do_full_update |= cuesheet_update;
82 retval = skin_redraw(gwps, gwps->sync_data->do_full_update ?
83 WPS_REFRESH_ALL : update_type);
84 return retval;
87 #ifdef HAVE_LCD_BITMAP
89 void skin_statusbar_changed(struct gui_wps *skin)
91 if (!skin)
92 return;
93 struct wps_data *data = skin->data;
94 const struct screen *display = skin->display;
95 const int screen = display->screen_type;
97 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, data)->vp;
98 viewport_set_defaults(vp, screen);
100 if (data->wps_sb_tag)
101 { /* fix up the default viewport */
102 if (data->show_sb_on_wps)
104 if (statusbar_position(screen) != STATUSBAR_OFF)
105 return; /* vp is fixed already */
107 vp->y = STATUSBAR_HEIGHT;
108 vp->height = display->lcdheight - STATUSBAR_HEIGHT;
110 else
112 if (statusbar_position(screen) == STATUSBAR_OFF)
113 return; /* vp is fixed already */
114 vp->y = vp->x = 0;
115 vp->height = display->lcdheight;
116 vp->width = display->lcdwidth;
121 static void draw_progressbar(struct gui_wps *gwps,
122 struct progressbar *pb)
124 struct screen *display = gwps->display;
125 struct viewport *vp = pb->vp;
126 struct wps_state *state = gwps->state;
127 struct mp3entry *id3 = state->id3;
128 int y = pb->y, height = pb->height;
129 unsigned long length, elapsed;
131 if (height < 0)
132 height = font_get(vp->font)->height;
134 if (y < 0)
136 int line_height = font_get(vp->font)->height;
137 /* center the pb in the line, but only if the line is higher than the pb */
138 int center = (line_height-height)/2;
139 /* if Y was not set calculate by font height,Y is -line_number-1 */
140 y = (-y -1)*line_height + (0 > center ? 0 : center);
143 if (pb->type == WPS_TOKEN_VOLUMEBAR)
145 int minvol = sound_min(SOUND_VOLUME);
146 int maxvol = sound_max(SOUND_VOLUME);
147 length = maxvol-minvol;
148 elapsed = global_settings.volume-minvol;
150 else if (pb->type == WPS_TOKEN_BATTERY_PERCENTBAR)
152 length = 100;
153 elapsed = battery_level();
155 else if (id3 && id3->length)
157 length = id3->length;
158 elapsed = id3->elapsed + state->ff_rewind_count;
160 else
162 length = 1;
163 elapsed = 0;
166 if (pb->have_bitmap_pb)
167 gui_bitmap_scrollbar_draw(display, pb->bm,
168 pb->x, y, pb->width, pb->bm.height,
169 length, 0, elapsed, HORIZONTAL);
170 else
171 gui_scrollbar_draw(display, pb->x, y, pb->width, height,
172 length, 0, elapsed, HORIZONTAL);
174 if (pb->type == WPS_TOKEN_PROGRESSBAR && id3 && id3->length)
176 #ifdef AB_REPEAT_ENABLE
177 if (ab_repeat_mode_enabled())
178 ab_draw_markers(display, id3->length,
179 pb->x, y, pb->width, height);
180 #endif
182 if (id3->cuesheet)
183 cue_draw_markers(display, id3->cuesheet, id3->length,
184 pb->x, y+1, pb->width, height-2);
188 static void draw_playlist_viewer_list(struct gui_wps *gwps,
189 struct playlistviewer *viewer)
191 struct wps_state *state = gwps->state;
192 int lines = viewport_get_nb_lines(viewer->vp);
193 int line_height = font_get(viewer->vp->font)->height;
194 int cur_playlist_pos = playlist_get_display_index();
195 int start_item = MAX(0, cur_playlist_pos + viewer->start_offset);
196 int i;
197 struct wps_token token;
198 int x, length, alignment = WPS_TOKEN_ALIGN_LEFT;
200 struct mp3entry *pid3;
201 char buf[MAX_PATH*2], tempbuf[MAX_PATH];
202 char *filename;
204 gwps->display->set_viewport(viewer->vp);
205 for(i=start_item; (i-start_item)<lines && i<=playlist_amount(); i++)
207 filename = playlist_peek(i-cur_playlist_pos);
208 if (i == cur_playlist_pos)
210 pid3 = state->id3;
212 else if (i == cur_playlist_pos+1)
214 pid3 = state->nid3;
216 #if CONFIG_CODEC == SWCODEC
217 else if (i>cur_playlist_pos)
219 #ifdef HAVE_TC_RAMCACHE
220 if (tagcache_fill_tags(&viewer->tempid3, filename))
222 pid3 = &viewer->tempid3;
224 else
225 #endif
226 if (!audio_peek_track(&pid3, i-cur_playlist_pos))
227 pid3 = NULL;
229 #endif
230 else
232 pid3 = NULL;
235 int line = pid3 ? TRACK_HAS_INFO : TRACK_HAS_NO_INFO;
236 int j = 0, cur_string = 0;
237 unsigned int line_len = 0;
238 buf[0] = '\0';
239 while (j < viewer->lines[line].count && line_len < sizeof(buf))
241 const char *out = NULL;
242 token.type = viewer->lines[line].tokens[j];
243 token.value.i = 0;
244 token.next = false;
245 out = get_id3_token(&token, pid3, tempbuf, sizeof(tempbuf), -1, NULL);
246 if (out)
248 line_len = strlcat(buf, out, sizeof(buf));
249 j++;
250 continue;
252 switch (viewer->lines[line].tokens[j])
254 case WPS_TOKEN_ALIGN_CENTER:
255 case WPS_TOKEN_ALIGN_LEFT:
256 case WPS_TOKEN_ALIGN_LEFT_RTL:
257 case WPS_TOKEN_ALIGN_RIGHT:
258 case WPS_TOKEN_ALIGN_RIGHT_RTL:
259 alignment = viewer->lines[line].tokens[j];
260 tempbuf[0] = '\0';
261 break;
262 case WPS_TOKEN_STRING:
263 case WPS_TOKEN_CHARACTER:
264 snprintf(tempbuf, sizeof(tempbuf), "%s",
265 viewer->lines[line].strings[cur_string]);
266 cur_string++;
267 break;
268 case WPS_TOKEN_PLAYLIST_POSITION:
269 snprintf(tempbuf, sizeof(tempbuf), "%d", i);
270 break;
271 case WPS_TOKEN_FILE_NAME:
272 get_dir(tempbuf, sizeof(tempbuf), filename, 0);
273 break;
274 case WPS_TOKEN_FILE_PATH:
275 snprintf(tempbuf, sizeof(tempbuf), "%s", filename);
276 break;
277 default:
278 tempbuf[0] = '\0';
279 break;
281 if (tempbuf[0])
283 line_len = strlcat(buf, tempbuf, sizeof(buf));
285 j++;
288 int vpwidth = viewer->vp->width;
289 length = gwps->display->getstringsize(buf, NULL, NULL);
290 if (viewer->lines[line].scroll && length >= vpwidth)
292 gwps->display->puts_scroll(0, (i-start_item), buf );
294 else
296 if (length >= vpwidth)
297 x = 0;
298 else
300 switch (alignment)
302 case WPS_TOKEN_ALIGN_CENTER:
303 x = (vpwidth-length)/2;
304 break;
305 case WPS_TOKEN_ALIGN_LEFT_RTL:
306 if (lang_is_rtl() && VP_IS_RTL(viewer->vp))
308 x = vpwidth - length;
309 break;
311 case WPS_TOKEN_ALIGN_LEFT:
312 x = 0;
313 break;
314 case WPS_TOKEN_ALIGN_RIGHT_RTL:
315 if (lang_is_rtl() && VP_IS_RTL(viewer->vp))
317 x = 0;
318 break;
320 case WPS_TOKEN_ALIGN_RIGHT:
321 x = vpwidth - length;
322 break;
323 default:
324 x = 0;
325 break;
328 gwps->display->putsxy(x, (i-start_item)*line_height, buf );
334 /* clears the area where the image was shown */
335 static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
337 if(!gwps)
338 return;
339 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
340 gwps->display->fillrect(img->x, img->y, img->bm.width, img->subimage_height);
341 gwps->display->set_drawmode(DRMODE_SOLID);
344 static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage)
346 struct screen *display = gwps->display;
347 if(img->always_display)
348 display->set_drawmode(DRMODE_FG);
349 else
350 display->set_drawmode(DRMODE_SOLID);
352 #if LCD_DEPTH > 1
353 if(img->bm.format == FORMAT_MONO) {
354 #endif
355 display->mono_bitmap_part(img->bm.data,
356 0, img->subimage_height * subimage,
357 img->bm.width, img->x,
358 img->y, img->bm.width,
359 img->subimage_height);
360 #if LCD_DEPTH > 1
361 } else {
362 display->transparent_bitmap_part((fb_data *)img->bm.data,
363 0, img->subimage_height * subimage,
364 STRIDE(display->screen_type,
365 img->bm.width, img->bm.height),
366 img->x, img->y, img->bm.width,
367 img->subimage_height);
369 #endif
372 static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
374 if(!gwps || !gwps->data || !gwps->display)
375 return;
377 struct wps_data *data = gwps->data;
378 struct screen *display = gwps->display;
379 struct skin_token_list *list = data->images;
381 while (list)
383 struct gui_img *img = (struct gui_img*)list->token->value.data;
384 if (img->loaded)
386 if (img->display >= 0)
388 wps_draw_image(gwps, img, img->display);
390 else if (img->always_display && img->vp == vp)
392 wps_draw_image(gwps, img, 0);
395 list = list->next;
397 #ifdef HAVE_ALBUMART
398 /* now draw the AA */
399 if (data->albumart && data->albumart->vp == vp
400 && data->albumart->draw)
402 draw_album_art(gwps, playback_current_aa_hid(data->playback_aa_slot),
403 false);
404 data->albumart->draw = false;
406 #endif
408 display->set_drawmode(DRMODE_SOLID);
411 #else /* HAVE_LCD_CHARCELL */
413 static bool draw_player_progress(struct gui_wps *gwps)
415 struct wps_state *state = gwps->state;
416 struct screen *display = gwps->display;
417 unsigned char progress_pattern[7];
418 int pos = 0;
419 int i;
421 int elapsed, length;
422 if (LIKELY(state->id3))
424 elapsed = state->id3->elapsed;
425 length = state->id3->length;
427 else
429 elapsed = 0;
430 length = 0;
433 if (length)
434 pos = 36 * (elapsed + state->ff_rewind_count) / length;
436 for (i = 0; i < 7; i++, pos -= 5)
438 if (pos <= 0)
439 progress_pattern[i] = 0x1fu;
440 else if (pos >= 5)
441 progress_pattern[i] = 0x00u;
442 else
443 progress_pattern[i] = 0x1fu >> pos;
446 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
447 return true;
450 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
452 static const unsigned char numbers[10][4] = {
453 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
454 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
455 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
456 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
457 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
458 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
459 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
460 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
461 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
462 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
465 struct wps_state *state = gwps->state;
466 struct screen *display = gwps->display;
467 struct wps_data *data = gwps->data;
468 unsigned char progress_pattern[7];
469 char timestr[10];
470 int time;
471 int time_idx = 0;
472 int pos = 0;
473 int pat_idx = 1;
474 int digit, i, j;
475 bool softchar;
477 int elapsed, length;
478 if (LIKELY(state->id3))
480 elapsed = state->id3->elapsed;
481 length = state->id3->length;
483 else
485 elapsed = 0;
486 length = 0;
489 if (buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
490 return;
492 time = elapsed + state->ff_rewind_count;
493 if (length)
494 pos = 55 * time / length;
496 memset(timestr, 0, sizeof(timestr));
497 format_time(timestr, sizeof(timestr)-2, time);
498 timestr[strlen(timestr)] = ':'; /* always safe */
500 for (i = 0; i < 11; i++, pos -= 5)
502 softchar = false;
503 memset(progress_pattern, 0, sizeof(progress_pattern));
505 if ((digit = timestr[time_idx]))
507 softchar = true;
508 digit -= '0';
510 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
512 memcpy(progress_pattern, numbers[digit], 4);
513 time_idx += 2;
515 else /* tens, shifted right */
517 for (j = 0; j < 4; j++)
518 progress_pattern[j] = numbers[digit][j] >> 1;
520 if (time_idx > 0) /* not the first group, add colon in front */
522 progress_pattern[1] |= 0x10u;
523 progress_pattern[3] |= 0x10u;
525 time_idx++;
528 if (pos >= 5)
529 progress_pattern[5] = progress_pattern[6] = 0x1fu;
532 if (pos > 0 && pos < 5)
534 softchar = true;
535 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
538 if (softchar && pat_idx < 8)
540 display->define_pattern(data->wps_progress_pat[pat_idx],
541 progress_pattern);
542 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
543 pat_idx++;
545 else if (pos <= 0)
546 buf = utf8encode(' ', buf);
547 else
548 buf = utf8encode(0xe115, buf); /* 2/7 _ */
550 *buf = '\0';
553 #endif /* HAVE_LCD_CHARCELL */
555 /* Return the index to the end token for the conditional token at index.
556 The conditional token can be either a start token or a separator
557 (i.e. option) token.
559 static int find_conditional_end(struct wps_data *data, int index)
561 int ret = index;
562 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
563 ret = data->tokens[ret].value.i;
565 /* ret now is the index to the end token for the conditional. */
566 return ret;
569 /* Evaluate the conditional that is at *token_index and return whether a skip
570 has ocurred. *token_index is updated with the new position.
572 static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
574 if (!gwps)
575 return false;
577 struct wps_data *data = gwps->data;
579 int i, cond_end;
580 int cond_index = *token_index;
581 char result[128];
582 const char *value;
583 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
584 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
586 /* treat ?xx<true> constructs as if they had 2 options. */
587 if (num_options < 2)
588 num_options = 2;
590 int intval = num_options;
591 /* get_token_value needs to know the number of options in the enum */
592 value = get_token_value(gwps, &data->tokens[cond_index + 1],
593 result, sizeof(result), &intval);
595 /* intval is now the number of the enum option we want to read,
596 starting from 1. If intval is -1, we check if value is empty. */
597 if (intval == -1)
598 intval = (value && *value) ? 1 : num_options;
599 else if (intval > num_options || intval < 1)
600 intval = num_options;
602 data->tokens[cond_index].value.i = (intval << 8) + num_options;
604 /* skip to the appropriate enum case */
605 int next = cond_index + 2;
606 for (i = 1; i < intval; i++)
608 next = data->tokens[next].value.i;
610 *token_index = next;
612 if (prev_val == intval)
614 /* Same conditional case as previously. Return without clearing the
615 pictures */
616 return false;
619 cond_end = find_conditional_end(data, cond_index + 2);
620 for (i = cond_index + 3; i < cond_end; i++)
622 #ifdef HAVE_LCD_BITMAP
623 /* clear all pictures in the conditional and nested ones */
624 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
625 clear_image_pos(gwps, find_image(data->tokens[i].value.i&0xFF, data));
626 else if (data->tokens[i].type == WPS_TOKEN_VOLUMEBAR ||
627 data->tokens[i].type == WPS_TOKEN_PROGRESSBAR ||
628 data->tokens[i].type == WPS_TOKEN_BATTERY_PERCENTBAR )
630 struct progressbar *bar = (struct progressbar*)data->tokens[i].value.data;
631 bar->draw = false;
633 #endif
634 #ifdef HAVE_ALBUMART
635 if (data->albumart && data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
637 draw_album_art(gwps,
638 playback_current_aa_hid(data->playback_aa_slot), true);
639 data->albumart->draw = false;
641 #endif
644 return true;
648 /* Read a (sub)line to the given alignment format buffer.
649 linebuf is the buffer where the data is actually stored.
650 align is the alignment format that'll be used to display the text.
651 The return value indicates whether the line needs to be updated.
653 static bool get_line(struct gui_wps *gwps,
654 struct skin_subline *subline,
655 struct align_pos *align,
656 char *linebuf,
657 int linebuf_size,
658 unsigned refresh_mode)
660 struct wps_data *data = gwps->data;
662 char temp_buf[128];
663 char *buf = linebuf; /* will always point to the writing position */
664 char *linebuf_end = linebuf + linebuf_size - 1;
665 bool update = false;
666 int i;
667 (void)refresh_mode; /* silence warning on charcell */
669 /* alignment-related variables */
670 int cur_align;
671 char* cur_align_start;
672 cur_align_start = buf;
673 cur_align = WPS_ALIGN_LEFT;
674 align->left = NULL;
675 align->center = NULL;
676 align->right = NULL;
677 /* Process all tokens of the desired subline */
678 for (i = subline->first_token_idx;
679 i <= subline->last_token_idx; i++)
681 switch(data->tokens[i].type)
683 case WPS_TOKEN_CONDITIONAL:
684 /* place ourselves in the right conditional case */
685 update |= evaluate_conditional(gwps, &i);
686 break;
688 case WPS_TOKEN_CONDITIONAL_OPTION:
689 /* we've finished in the curent conditional case,
690 skip to the end of the conditional structure */
691 i = find_conditional_end(data, i);
692 break;
694 #ifdef HAVE_LCD_BITMAP
695 case WPS_TOKEN_VOLUMEBAR:
696 case WPS_TOKEN_BATTERY_PERCENTBAR:
697 case WPS_TOKEN_PROGRESSBAR:
699 struct progressbar *bar = (struct progressbar*)data->tokens[i].value.data;
700 bar->draw = true;
702 break;
703 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
705 char n = data->tokens[i].value.i & 0xFF;
706 int subimage = data->tokens[i].value.i >> 8;
707 struct gui_img *img = find_image(n, data);
709 if (img && img->loaded)
710 img->display = subimage;
711 break;
713 case WPS_TOKEN_DRAW_INBUILTBAR:
714 gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
715 refresh_mode == WPS_REFRESH_ALL,
716 data->tokens[i].value.data);
717 break;
718 #endif
720 case WPS_TOKEN_ALIGN_LEFT:
721 case WPS_TOKEN_ALIGN_LEFT_RTL:
722 case WPS_TOKEN_ALIGN_CENTER:
723 case WPS_TOKEN_ALIGN_RIGHT:
724 case WPS_TOKEN_ALIGN_RIGHT_RTL:
725 /* remember where the current aligned text started */
726 switch (cur_align)
728 case WPS_ALIGN_LEFT:
729 align->left = cur_align_start;
730 break;
732 case WPS_ALIGN_CENTER:
733 align->center = cur_align_start;
734 break;
736 case WPS_ALIGN_RIGHT:
737 align->right = cur_align_start;
738 break;
740 /* start a new alignment */
741 switch (data->tokens[i].type)
743 case WPS_TOKEN_ALIGN_LEFT:
744 cur_align = WPS_ALIGN_LEFT;
745 break;
746 case WPS_TOKEN_ALIGN_LEFT_RTL:
747 cur_align = lang_is_rtl() ? WPS_ALIGN_RIGHT :
748 WPS_ALIGN_LEFT;
749 break;
750 case WPS_TOKEN_ALIGN_CENTER:
751 cur_align = WPS_ALIGN_CENTER;
752 break;
753 case WPS_TOKEN_ALIGN_RIGHT:
754 cur_align = WPS_ALIGN_RIGHT;
755 break;
756 case WPS_TOKEN_ALIGN_RIGHT_RTL:
757 cur_align = lang_is_rtl() ? WPS_ALIGN_LEFT :
758 WPS_ALIGN_RIGHT;
759 break;
760 default:
761 break;
763 *buf++ = 0;
764 cur_align_start = buf;
765 break;
766 case WPS_VIEWPORT_ENABLE:
768 char label = data->tokens[i].value.i;
769 char temp = VP_DRAW_HIDEABLE;
770 /* viewports are allowed to share id's so find and enable
771 * all of them */
772 struct skin_token_list *list = data->viewports;
773 while (list)
775 struct skin_viewport *vp =
776 (struct skin_viewport *)list->token->value.data;
777 if (vp->label == label)
779 if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
780 temp |= VP_DRAW_WASHIDDEN;
781 vp->hidden_flags = temp;
783 list = list->next;
786 break;
787 #ifdef HAVE_LCD_BITMAP
788 case WPS_TOKEN_UIVIEWPORT_ENABLE:
789 sb_set_info_vp(gwps->display->screen_type,
790 data->tokens[i].value.i|VP_INFO_LABEL);
791 break;
792 case WPS_VIEWPORT_CUSTOMLIST:
793 draw_playlist_viewer_list(gwps, data->tokens[i].value.data);
794 break;
795 #endif
796 default:
798 /* get the value of the tag and copy it to the buffer */
799 const char *value = get_token_value(gwps, &data->tokens[i],
800 temp_buf, sizeof(temp_buf), NULL);
801 if (value)
803 update = true;
804 while (*value && (buf < linebuf_end))
805 *buf++ = *value++;
807 break;
812 /* close the current alignment */
813 switch (cur_align)
815 case WPS_ALIGN_LEFT:
816 align->left = cur_align_start;
817 break;
819 case WPS_ALIGN_CENTER:
820 align->center = cur_align_start;
821 break;
823 case WPS_ALIGN_RIGHT:
824 align->right = cur_align_start;
825 break;
828 return update;
830 static void get_subline_timeout(struct gui_wps *gwps, struct skin_subline *subline)
832 struct wps_data *data = gwps->data;
833 int i;
834 subline->time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
836 for (i = subline->first_token_idx;
837 i <= subline->last_token_idx; i++)
839 switch(data->tokens[i].type)
841 case WPS_TOKEN_CONDITIONAL:
842 /* place ourselves in the right conditional case */
843 evaluate_conditional(gwps, &i);
844 break;
846 case WPS_TOKEN_CONDITIONAL_OPTION:
847 /* we've finished in the curent conditional case,
848 skip to the end of the conditional structure */
849 i = find_conditional_end(data, i);
850 break;
852 case WPS_TOKEN_SUBLINE_TIMEOUT:
853 subline->time_mult = data->tokens[i].value.i;
854 break;
856 default:
857 break;
862 /* Calculates which subline should be displayed for the specified line
863 Returns true iff the subline must be refreshed */
864 static bool update_curr_subline(struct gui_wps *gwps, struct skin_line *line)
866 /* shortcut this whole thing if we need to reset the line completly */
867 if (line->curr_subline == NULL)
869 line->subline_expire_time = current_tick;
870 line->curr_subline = &line->sublines;
871 if (!line->curr_subline->next)
873 line->subline_expire_time += 100*HZ;
875 else
877 get_subline_timeout(gwps, line->curr_subline);
878 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
880 return true;
882 /* if time to advance to next sub-line */
883 if (TIME_AFTER(current_tick, line->subline_expire_time - 1))
885 /* if there is only one subline, there is no need to search for a new one */
886 if (&line->sublines == line->curr_subline &&
887 line->curr_subline->next == NULL)
889 line->subline_expire_time += 100 * HZ;
890 return false;
892 if (line->curr_subline->next)
893 line->curr_subline = line->curr_subline->next;
894 else
895 line->curr_subline = &line->sublines;
896 get_subline_timeout(gwps, line->curr_subline);
897 line->subline_expire_time = current_tick + TIMEOUT_UNIT*line->curr_subline->time_mult;
898 return true;
900 return false;
903 /* Display a line appropriately according to its alignment format.
904 format_align contains the text, separated between left, center and right.
905 line is the index of the line on the screen.
906 scroll indicates whether the line is a scrolling one or not.
908 static void write_line(struct screen *display,
909 struct align_pos *format_align,
910 int line,
911 bool scroll)
913 int left_width = 0, left_xpos;
914 int center_width = 0, center_xpos;
915 int right_width = 0, right_xpos;
916 int ypos;
917 int space_width;
918 int string_height;
919 int scroll_width;
921 /* calculate different string sizes and positions */
922 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
923 if (format_align->left != 0) {
924 display->getstringsize((unsigned char *)format_align->left,
925 &left_width, &string_height);
928 if (format_align->right != 0) {
929 display->getstringsize((unsigned char *)format_align->right,
930 &right_width, &string_height);
933 if (format_align->center != 0) {
934 display->getstringsize((unsigned char *)format_align->center,
935 &center_width, &string_height);
938 left_xpos = 0;
939 right_xpos = (display->getwidth() - right_width);
940 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
942 scroll_width = display->getwidth() - left_xpos;
944 /* Checks for overlapping strings.
945 If needed the overlapping strings will be merged, separated by a
946 space */
948 /* CASE 1: left and centered string overlap */
949 /* there is a left string, need to merge left and center */
950 if ((left_width != 0 && center_width != 0) &&
951 (left_xpos + left_width + space_width > center_xpos)) {
952 /* replace the former separator '\0' of left and
953 center string with a space */
954 *(--format_align->center) = ' ';
955 /* calculate the new width and position of the merged string */
956 left_width = left_width + space_width + center_width;
957 /* there is no centered string anymore */
958 center_width = 0;
960 /* there is no left string, move center to left */
961 if ((left_width == 0 && center_width != 0) &&
962 (left_xpos + left_width > center_xpos)) {
963 /* move the center string to the left string */
964 format_align->left = format_align->center;
965 /* calculate the new width and position of the string */
966 left_width = center_width;
967 /* there is no centered string anymore */
968 center_width = 0;
971 /* CASE 2: centered and right string overlap */
972 /* there is a right string, need to merge center and right */
973 if ((center_width != 0 && right_width != 0) &&
974 (center_xpos + center_width + space_width > right_xpos)) {
975 /* replace the former separator '\0' of center and
976 right string with a space */
977 *(--format_align->right) = ' ';
978 /* move the center string to the right after merge */
979 format_align->right = format_align->center;
980 /* calculate the new width and position of the merged string */
981 right_width = center_width + space_width + right_width;
982 right_xpos = (display->getwidth() - right_width);
983 /* there is no centered string anymore */
984 center_width = 0;
986 /* there is no right string, move center to right */
987 if ((center_width != 0 && right_width == 0) &&
988 (center_xpos + center_width > right_xpos)) {
989 /* move the center string to the right string */
990 format_align->right = format_align->center;
991 /* calculate the new width and position of the string */
992 right_width = center_width;
993 right_xpos = (display->getwidth() - right_width);
994 /* there is no centered string anymore */
995 center_width = 0;
998 /* CASE 3: left and right overlap
999 There is no center string anymore, either there never
1000 was one or it has been merged in case 1 or 2 */
1001 /* there is a left string, need to merge left and right */
1002 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
1003 (left_xpos + left_width + space_width > right_xpos)) {
1004 /* replace the former separator '\0' of left and
1005 right string with a space */
1006 *(--format_align->right) = ' ';
1007 /* calculate the new width and position of the string */
1008 left_width = left_width + space_width + right_width;
1009 /* there is no right string anymore */
1010 right_width = 0;
1012 /* there is no left string, move right to left */
1013 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
1014 (left_width > right_xpos)) {
1015 /* move the right string to the left string */
1016 format_align->left = format_align->right;
1017 /* calculate the new width and position of the string */
1018 left_width = right_width;
1019 /* there is no right string anymore */
1020 right_width = 0;
1023 ypos = (line * string_height);
1026 if (scroll && ((left_width > scroll_width) ||
1027 (center_width > scroll_width) ||
1028 (right_width > scroll_width)))
1030 display->puts_scroll(0, line,
1031 (unsigned char *)format_align->left);
1033 else
1035 #ifdef HAVE_LCD_BITMAP
1036 /* clear the line first */
1037 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1038 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
1039 display->set_drawmode(DRMODE_SOLID);
1040 #endif
1042 /* Nasty hack: we output an empty scrolling string,
1043 which will reset the scroller for that line */
1044 display->puts_scroll(0, line, (unsigned char *)"");
1046 /* print aligned strings */
1047 if (left_width != 0)
1049 display->putsxy(left_xpos, ypos,
1050 (unsigned char *)format_align->left);
1052 if (center_width != 0)
1054 display->putsxy(center_xpos, ypos,
1055 (unsigned char *)format_align->center);
1057 if (right_width != 0)
1059 display->putsxy(right_xpos, ypos,
1060 (unsigned char *)format_align->right);
1065 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
1067 struct wps_data *data = gwps->data;
1068 struct screen *display = gwps->display;
1070 if (!data || !display || !gwps->state)
1071 return false;
1073 unsigned flags;
1074 char linebuf[MAX_PATH];
1076 struct align_pos align;
1077 align.left = NULL;
1078 align.center = NULL;
1079 align.right = NULL;
1082 struct skin_token_list *viewport_list;
1084 bool update_line, new_subline_refresh;
1086 #ifdef HAVE_LCD_BITMAP
1088 /* to find out wether the peak meter is enabled we
1089 assume it wasn't until we find a line that contains
1090 the peak meter. We can't use peak_meter_enabled itself
1091 because that would mean to turn off the meter thread
1092 temporarily. (That shouldn't matter unless yield
1093 or sleep is called but who knows...)
1095 bool enable_pm = false;
1097 #endif
1099 /* reset to first subline if refresh all flag is set */
1100 if (refresh_mode == WPS_REFRESH_ALL)
1102 struct skin_line *line;
1103 struct skin_viewport *skin_viewport = find_viewport(VP_DEFAULT_LABEL, data);
1105 if (!(skin_viewport->hidden_flags & VP_NEVER_VISIBLE))
1107 display->set_viewport(&skin_viewport->vp);
1108 display->clear_viewport();
1111 for (viewport_list = data->viewports;
1112 viewport_list; viewport_list = viewport_list->next)
1114 skin_viewport =
1115 (struct skin_viewport *)viewport_list->token->value.data;
1116 for(line = skin_viewport->lines; line; line = line->next)
1118 line->curr_subline = NULL;
1123 #ifdef HAVE_LCD_CHARCELLS
1124 int i;
1125 for (i = 0; i < 8; i++)
1127 if (data->wps_progress_pat[i] == 0)
1128 data->wps_progress_pat[i] = display->get_locked_pattern();
1130 #endif
1132 /* disable any viewports which are conditionally displayed.
1133 * If we are only refreshing the peak meter then don't change the viewport
1134 * enabled flags as this will stop scrolling. viewports cant be
1135 * toggled in this refresh mode anyway (FS#10215)*/
1136 if (refresh_mode != WPS_REFRESH_PEAK_METER)
1138 for (viewport_list = data->viewports;
1139 viewport_list; viewport_list = viewport_list->next)
1141 struct skin_viewport *skin_viewport =
1142 (struct skin_viewport *)viewport_list->token->value.data;
1143 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1145 continue;
1147 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
1149 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
1150 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1151 else
1152 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
1156 int viewport_count = 0;
1157 for (viewport_list = data->viewports;
1158 viewport_list; viewport_list = viewport_list->next, viewport_count++)
1160 struct skin_viewport *skin_viewport =
1161 (struct skin_viewport *)viewport_list->token->value.data;
1162 unsigned vp_refresh_mode = refresh_mode;
1164 display->set_viewport(&skin_viewport->vp);
1166 int hidden_vp = 0;
1168 #ifdef HAVE_LCD_BITMAP
1169 /* Set images to not to be displayed */
1170 struct skin_token_list *imglist = data->images;
1171 while (imglist)
1173 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
1174 img->display = -1;
1175 imglist = imglist->next;
1177 #endif
1178 /* dont redraw the viewport if its disabled */
1179 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1180 { /* don't draw anything into this one */
1181 vp_refresh_mode = 0; hidden_vp = true;
1183 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
1185 if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
1186 display->scroll_stop(&skin_viewport->vp);
1187 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1188 continue;
1190 else if (((skin_viewport->hidden_flags&
1191 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
1192 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
1194 vp_refresh_mode = WPS_REFRESH_ALL;
1195 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
1198 if (vp_refresh_mode == WPS_REFRESH_ALL)
1200 display->clear_viewport();
1203 /* loop over the lines for this viewport */
1204 struct skin_line *line;
1205 int line_count = 0;
1207 for (line = skin_viewport->lines; line; line = line->next, line_count++)
1209 struct skin_subline *subline;
1210 memset(linebuf, 0, sizeof(linebuf));
1211 update_line = false;
1213 /* get current subline for the line */
1214 new_subline_refresh = update_curr_subline(gwps, line);
1215 subline = line->curr_subline;
1216 flags = line->curr_subline->line_type;
1218 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1219 || new_subline_refresh || hidden_vp)
1221 /* get_line tells us if we need to update the line */
1222 update_line = get_line(gwps, subline, &align,
1223 linebuf, sizeof(linebuf), vp_refresh_mode);
1225 #ifdef HAVE_LCD_BITMAP
1226 /* peakmeter */
1227 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1229 /* the peakmeter should be alone on its line */
1230 update_line = false;
1232 int h = font_get(skin_viewport->vp.font)->height;
1233 int peak_meter_y = line_count* h;
1235 /* The user might decide to have the peak meter in the last
1236 line so that it is only displayed if no status bar is
1237 visible. If so we neither want do draw nor enable the
1238 peak meter. */
1239 if (peak_meter_y + h <= skin_viewport->vp.y+skin_viewport->vp.height) {
1240 /* found a line with a peak meter -> remember that we must
1241 enable it later */
1242 enable_pm = true;
1243 peak_meter_enabled = true;
1244 peak_meter_screen(gwps->display, 0, peak_meter_y,
1245 MIN(h, skin_viewport->vp.y+skin_viewport->vp.height - peak_meter_y));
1247 else
1249 peak_meter_enabled = false;
1253 #else /* HAVE_LCD_CHARCELL */
1255 /* progressbar */
1256 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1258 if (data->full_line_progressbar)
1259 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1260 else
1261 draw_player_progress(gwps);
1263 #endif
1265 if (update_line && !hidden_vp &&
1266 /* conditionals clear the line which means if the %Vd is put into the default
1267 viewport there will be a blank line.
1268 To get around this we dont allow any actual drawing to happen in the
1269 deault vp if other vp's are defined */
1270 ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
1271 !viewport_list->next))
1273 if (flags & WPS_REFRESH_SCROLL)
1275 /* if the line is a scrolling one we don't want to update
1276 too often, so that it has the time to scroll */
1277 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1278 write_line(display, &align, line_count, true);
1280 else
1281 write_line(display, &align, line_count, false);
1284 #ifdef HAVE_LCD_BITMAP
1285 /* progressbar */
1286 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1288 struct skin_token_list *bar = gwps->data->progressbars;
1289 while (bar)
1291 struct progressbar *thisbar = (struct progressbar*)bar->token->value.data;
1292 if (thisbar->vp == &skin_viewport->vp && thisbar->draw)
1294 draw_progressbar(gwps, thisbar);
1296 bar = bar->next;
1299 /* Now display any images in this viewport */
1300 if (!hidden_vp)
1301 wps_display_images(gwps, &skin_viewport->vp);
1302 #endif
1305 #ifdef HAVE_LCD_BITMAP
1306 data->peak_meter_enabled = enable_pm;
1307 #endif
1308 /* Restore the default viewport */
1309 display->set_viewport(NULL);
1311 display->update();
1313 return true;