Fix FS#11058 - conditionl viewports cause sublines to go a bit crazy
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
blobd610fe4a4d7f8432f50dcf6164a90776f8bb8909
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.h>
25 #include "misc.h"
26 #include "font.h"
27 #include "system.h"
28 #include "rbunicode.h"
29 #ifdef DEBUG
30 #include "debug.h"
31 #endif
32 #include "abrepeat.h"
33 #include "lang.h"
34 #include "language.h"
35 #include "statusbar.h"
36 #include "scrollbar.h"
37 #include "screen_access.h"
38 #include "playlist.h"
39 #include "audio.h"
41 #ifdef HAVE_LCD_BITMAP
42 #include "peakmeter.h"
43 /* Image stuff */
44 #include "bmp.h"
45 #ifdef HAVE_ALBUMART
46 #include "albumart.h"
47 #endif
48 #endif
50 #include "cuesheet.h"
51 #if CONFIG_CODEC == SWCODEC
52 #include "playback.h"
53 #endif
54 #include "backdrop.h"
55 #include "viewport.h"
58 #include "wps_internals.h"
59 #include "skin_engine.h"
60 #include "statusbar-skinned.h"
62 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode);
64 /* update a skinned screen, update_type is WPS_REFRESH_* values.
65 * Usually it should only be WPS_REFRESH_NON_STATIC
66 * A full update will be done if required (state.do_full_update == true)
68 bool skin_update(struct gui_wps *gwps, unsigned int update_type)
70 bool retval;
71 /* This maybe shouldnt be here, but while the skin is only used to
72 * display the music screen this is better than whereever we are being
73 * called from. This is also safe for skined screen which dont use the id3 */
74 struct mp3entry *id3 = gwps->state->id3;
75 bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
76 gwps->sync_data->do_full_update |= cuesheet_update;
78 retval = skin_redraw(gwps, gwps->sync_data->do_full_update ?
79 WPS_REFRESH_ALL : update_type);
80 return retval;
83 #ifdef HAVE_LCD_BITMAP
85 void skin_statusbar_changed(struct gui_wps *skin)
87 if (!skin)
88 return;
89 struct wps_data *data = skin->data;
90 const struct screen *display = skin->display;
91 const int screen = display->screen_type;
93 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, data)->vp;
94 viewport_set_defaults(vp, screen);
96 if (data->wps_sb_tag)
97 { /* fix up the default viewport */
98 if (data->show_sb_on_wps)
100 if (statusbar_position(screen) != STATUSBAR_OFF)
101 return; /* vp is fixed already */
103 vp->y = STATUSBAR_HEIGHT;
104 vp->height = display->lcdheight - STATUSBAR_HEIGHT;
106 else
108 if (statusbar_position(screen) == STATUSBAR_OFF)
109 return; /* vp is fixed already */
110 vp->y = vp->x = 0;
111 vp->height = display->lcdheight;
112 vp->width = display->lcdwidth;
117 static void draw_progressbar(struct gui_wps *gwps,
118 struct skin_viewport *wps_vp)
120 struct screen *display = gwps->display;
121 struct wps_state *state = gwps->state;
122 struct progressbar *pb = wps_vp->pb;
123 struct mp3entry *id3 = state->id3;
124 int y = pb->y;
125 int height = pb->height;
127 if (pb->height < 0 && !pb->have_bitmap_pb)
128 height = font_get(wps_vp->vp.font)->height;
130 if (y < 0)
132 int line_height = font_get(wps_vp->vp.font)->height;
133 /* center the pb in the line, but only if the line is higher than the pb */
134 int center = (line_height-pb->height)/2;
135 /* if Y was not set calculate by font height,Y is -line_number-1 */
136 y = (-y -1)*line_height + (0 > center ? 0 : center);
139 int elapsed, length;
140 if (id3)
142 elapsed = id3->elapsed;
143 length = id3->length;
145 else
147 elapsed = 0;
148 length = 0;
151 if (pb->have_bitmap_pb)
152 gui_bitmap_scrollbar_draw(display, pb->bm,
153 pb->x, y, pb->width, pb->bm.height,
154 length ? length : 1, 0,
155 length ? elapsed + state->ff_rewind_count : 0,
156 HORIZONTAL);
157 else
158 gui_scrollbar_draw(display, pb->x, y, pb->width, height,
159 length ? length : 1, 0,
160 length ? elapsed + state->ff_rewind_count : 0,
161 HORIZONTAL);
162 #ifdef AB_REPEAT_ENABLE
163 if ( ab_repeat_mode_enabled() && length != 0 )
164 ab_draw_markers(display, length,
165 pb->x, pb->x + pb->width, y, height);
166 #endif
168 if (id3 && id3->cuesheet)
169 cue_draw_markers(display, state->id3->cuesheet, length,
170 pb->x, pb->x + pb->width, y+1, height-2);
172 bool audio_peek_track(struct mp3entry* id3, int offset);
173 static void draw_playlist_viewer_list(struct gui_wps *gwps,
174 struct playlistviewer *viewer)
176 struct wps_state *state = gwps->state;
177 int lines = viewport_get_nb_lines(viewer->vp);
178 int line_height = font_get(viewer->vp->font)->height;
179 int cur_playlist_pos = playlist_get_display_index();
180 int start_item = MAX(0, cur_playlist_pos + viewer->start_offset);
181 int i;
182 struct wps_token token;
183 int x, length, alignment = WPS_TOKEN_ALIGN_LEFT;
185 struct mp3entry *pid3;
186 #if CONFIG_CODEC == SWCODEC
187 struct mp3entry id3;
188 #endif
189 char buf[MAX_PATH*2], tempbuf[MAX_PATH];
190 unsigned int buf_used = 0;
193 gwps->display->set_viewport(viewer->vp);
194 for(i=start_item; (i-start_item)<lines && i<playlist_amount(); i++)
196 if (i == cur_playlist_pos)
198 pid3 = state->id3;
200 else if (i == cur_playlist_pos+1)
202 pid3 = state->nid3;
204 #if CONFIG_CODEC == SWCODEC
205 else if ((i>cur_playlist_pos) && audio_peek_track(&id3, i-cur_playlist_pos))
207 pid3 = &id3;
209 #endif
210 else
212 pid3 = NULL;
215 int line = pid3 ? TRACK_HAS_INFO : TRACK_HAS_NO_INFO;
216 int j = 0, cur_string = 0;
217 char *filename = playlist_peek(i-cur_playlist_pos);
218 buf[0] = '\0';
219 buf_used = 0;
220 while (j < viewer->lines[line].count && (buf_used<sizeof(buf)))
222 const char *out = NULL;
223 token.type = viewer->lines[line].tokens[j];
224 token.value.i = 0;
225 token.next = false;
226 out = get_id3_token(&token, pid3, tempbuf, sizeof(tempbuf), -1, NULL);
227 if (out)
229 snprintf(&buf[buf_used], sizeof(buf)-buf_used, "%s", out);
230 buf_used += strlen(out);
231 j++;
232 continue;
234 switch (viewer->lines[line].tokens[j])
236 case WPS_TOKEN_ALIGN_CENTER:
237 case WPS_TOKEN_ALIGN_LEFT:
238 case WPS_TOKEN_ALIGN_LEFT_RTL:
239 case WPS_TOKEN_ALIGN_RIGHT:
240 case WPS_TOKEN_ALIGN_RIGHT_RTL:
241 alignment = viewer->lines[line].tokens[j];
242 tempbuf[0] = '\0';
243 break;
244 case WPS_TOKEN_STRING:
245 case WPS_TOKEN_CHARACTER:
246 snprintf(tempbuf, sizeof(tempbuf), "%s",
247 viewer->lines[line].strings[cur_string]);
248 cur_string++;
249 break;
250 case WPS_TOKEN_PLAYLIST_POSITION:
251 snprintf(tempbuf, sizeof(tempbuf), "%d", i);
252 break;
253 case WPS_TOKEN_FILE_NAME:
254 get_dir(tempbuf, sizeof(tempbuf), filename, 0);
255 break;
256 case WPS_TOKEN_FILE_PATH:
257 snprintf(tempbuf, sizeof(tempbuf), "%s", filename);
258 break;
259 default:
260 tempbuf[0] = '\0';
261 break;
263 if (tempbuf[0])
265 snprintf(&buf[buf_used], sizeof(buf)-buf_used, "%s", tempbuf);
266 buf_used += strlen(tempbuf);
268 j++;
271 int vpwidth = viewer->vp->width;
272 length = gwps->display->getstringsize(buf, NULL, NULL);
273 if (viewer->lines[line].scroll && length >= vpwidth)
275 gwps->display->puts_scroll(0, (i-start_item), buf );
277 else
279 if (length >= vpwidth)
280 x = 0;
281 else
283 switch (alignment)
285 case WPS_TOKEN_ALIGN_CENTER:
286 x = (vpwidth-length)/2;
287 break;
288 case WPS_TOKEN_ALIGN_LEFT_RTL:
289 if (lang_is_rtl() && VP_IS_RTL(viewer->vp))
291 x = vpwidth - length;
292 break;
294 case WPS_TOKEN_ALIGN_LEFT:
295 x = 0;
296 break;
297 case WPS_TOKEN_ALIGN_RIGHT_RTL:
298 if (lang_is_rtl() && VP_IS_RTL(viewer->vp))
300 x = 0;
301 break;
303 case WPS_TOKEN_ALIGN_RIGHT:
304 x = vpwidth - length;
305 break;
306 default:
307 x = 0;
308 break;
311 gwps->display->putsxy(x, (i-start_item)*line_height, buf );
317 /* clears the area where the image was shown */
318 static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
320 if(!gwps)
321 return;
322 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
323 gwps->display->fillrect(img->x, img->y, img->bm.width, img->subimage_height);
324 gwps->display->set_drawmode(DRMODE_SOLID);
327 static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage)
329 struct screen *display = gwps->display;
330 if(img->always_display)
331 display->set_drawmode(DRMODE_FG);
332 else
333 display->set_drawmode(DRMODE_SOLID);
335 #if LCD_DEPTH > 1
336 if(img->bm.format == FORMAT_MONO) {
337 #endif
338 display->mono_bitmap_part(img->bm.data,
339 0, img->subimage_height * subimage,
340 img->bm.width, img->x,
341 img->y, img->bm.width,
342 img->subimage_height);
343 #if LCD_DEPTH > 1
344 } else {
345 display->transparent_bitmap_part((fb_data *)img->bm.data,
346 0, img->subimage_height * subimage,
347 STRIDE(display->screen_type,
348 img->bm.width, img->bm.height),
349 img->x, img->y, img->bm.width,
350 img->subimage_height);
352 #endif
355 static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
357 if(!gwps || !gwps->data || !gwps->display)
358 return;
360 struct wps_data *data = gwps->data;
361 struct screen *display = gwps->display;
362 struct skin_token_list *list = data->images;
364 while (list)
366 struct gui_img *img = (struct gui_img*)list->token->value.data;
367 if (img->loaded)
369 if (img->display >= 0)
371 wps_draw_image(gwps, img, img->display);
373 else if (img->always_display && img->vp == vp)
375 wps_draw_image(gwps, img, 0);
378 list = list->next;
380 #ifdef HAVE_ALBUMART
381 /* now draw the AA */
382 if (data->albumart && data->albumart->vp == vp
383 && data->albumart->draw)
385 draw_album_art(gwps, playback_current_aa_hid(data->playback_aa_slot),
386 false);
387 data->albumart->draw = false;
389 #endif
391 display->set_drawmode(DRMODE_SOLID);
394 #else /* HAVE_LCD_CHARCELL */
396 static bool draw_player_progress(struct gui_wps *gwps)
398 struct wps_state *state = gwps->state;
399 struct screen *display = gwps->display;
400 unsigned char progress_pattern[7];
401 int pos = 0;
402 int i;
404 int elapsed, length;
405 if (LIKELY(state->id3))
407 elapsed = state->id3->elapsed;
408 length = state->id3->length;
410 else
412 elapsed = 0;
413 length = 0;
416 if (length)
417 pos = 36 * (elapsed + state->ff_rewind_count) / length;
419 for (i = 0; i < 7; i++, pos -= 5)
421 if (pos <= 0)
422 progress_pattern[i] = 0x1fu;
423 else if (pos >= 5)
424 progress_pattern[i] = 0x00u;
425 else
426 progress_pattern[i] = 0x1fu >> pos;
429 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
430 return true;
433 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
435 static const unsigned char numbers[10][4] = {
436 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
437 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
438 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
439 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
440 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
441 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
442 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
443 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
444 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
445 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
448 struct wps_state *state = gwps->state;
449 struct screen *display = gwps->display;
450 struct wps_data *data = gwps->data;
451 unsigned char progress_pattern[7];
452 char timestr[10];
453 int time;
454 int time_idx = 0;
455 int pos = 0;
456 int pat_idx = 1;
457 int digit, i, j;
458 bool softchar;
460 int elapsed, length;
461 if (LIKELY(state->id3))
463 elapsed = state->id3->elapsed;
464 length = state->id3->length;
466 else
468 elapsed = 0;
469 length = 0;
472 if (buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
473 return;
475 time = elapsed + state->ff_rewind_count;
476 if (length)
477 pos = 55 * time / length;
479 memset(timestr, 0, sizeof(timestr));
480 format_time(timestr, sizeof(timestr)-2, time);
481 timestr[strlen(timestr)] = ':'; /* always safe */
483 for (i = 0; i < 11; i++, pos -= 5)
485 softchar = false;
486 memset(progress_pattern, 0, sizeof(progress_pattern));
488 if ((digit = timestr[time_idx]))
490 softchar = true;
491 digit -= '0';
493 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
495 memcpy(progress_pattern, numbers[digit], 4);
496 time_idx += 2;
498 else /* tens, shifted right */
500 for (j = 0; j < 4; j++)
501 progress_pattern[j] = numbers[digit][j] >> 1;
503 if (time_idx > 0) /* not the first group, add colon in front */
505 progress_pattern[1] |= 0x10u;
506 progress_pattern[3] |= 0x10u;
508 time_idx++;
511 if (pos >= 5)
512 progress_pattern[5] = progress_pattern[6] = 0x1fu;
515 if (pos > 0 && pos < 5)
517 softchar = true;
518 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
521 if (softchar && pat_idx < 8)
523 display->define_pattern(data->wps_progress_pat[pat_idx],
524 progress_pattern);
525 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
526 pat_idx++;
528 else if (pos <= 0)
529 buf = utf8encode(' ', buf);
530 else
531 buf = utf8encode(0xe115, buf); /* 2/7 _ */
533 *buf = '\0';
536 #endif /* HAVE_LCD_CHARCELL */
538 /* Return the index to the end token for the conditional token at index.
539 The conditional token can be either a start token or a separator
540 (i.e. option) token.
542 static int find_conditional_end(struct wps_data *data, int index)
544 int ret = index;
545 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
546 ret = data->tokens[ret].value.i;
548 /* ret now is the index to the end token for the conditional. */
549 return ret;
552 /* Evaluate the conditional that is at *token_index and return whether a skip
553 has ocurred. *token_index is updated with the new position.
555 static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
557 if (!gwps)
558 return false;
560 struct wps_data *data = gwps->data;
562 int i, cond_end;
563 int cond_index = *token_index;
564 char result[128];
565 const char *value;
566 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
567 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
569 /* treat ?xx<true> constructs as if they had 2 options. */
570 if (num_options < 2)
571 num_options = 2;
573 int intval = num_options;
574 /* get_token_value needs to know the number of options in the enum */
575 value = get_token_value(gwps, &data->tokens[cond_index + 1],
576 result, sizeof(result), &intval);
578 /* intval is now the number of the enum option we want to read,
579 starting from 1. If intval is -1, we check if value is empty. */
580 if (intval == -1)
581 intval = (value && *value) ? 1 : num_options;
582 else if (intval > num_options || intval < 1)
583 intval = num_options;
585 data->tokens[cond_index].value.i = (intval << 8) + num_options;
587 /* skip to the appropriate enum case */
588 int next = cond_index + 2;
589 for (i = 1; i < intval; i++)
591 next = data->tokens[next].value.i;
593 *token_index = next;
595 if (prev_val == intval)
597 /* Same conditional case as previously. Return without clearing the
598 pictures */
599 return false;
602 cond_end = find_conditional_end(data, cond_index + 2);
603 for (i = cond_index + 3; i < cond_end; i++)
605 #ifdef HAVE_LCD_BITMAP
606 /* clear all pictures in the conditional and nested ones */
607 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
608 clear_image_pos(gwps, find_image(data->tokens[i].value.i&0xFF, data));
609 #endif
610 #ifdef HAVE_ALBUMART
611 if (data->albumart && data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
613 draw_album_art(gwps,
614 playback_current_aa_hid(data->playback_aa_slot), true);
615 data->albumart->draw = false;
617 #endif
620 return true;
624 /* Read a (sub)line to the given alignment format buffer.
625 linebuf is the buffer where the data is actually stored.
626 align is the alignment format that'll be used to display the text.
627 The return value indicates whether the line needs to be updated.
629 static bool get_line(struct gui_wps *gwps,
630 struct skin_subline *subline,
631 struct align_pos *align,
632 char *linebuf,
633 int linebuf_size)
635 struct wps_data *data = gwps->data;
637 char temp_buf[128];
638 char *buf = linebuf; /* will always point to the writing position */
639 char *linebuf_end = linebuf + linebuf_size - 1;
640 bool update = false;
641 int i;
643 /* alignment-related variables */
644 int cur_align;
645 char* cur_align_start;
646 cur_align_start = buf;
647 cur_align = WPS_ALIGN_LEFT;
648 align->left = NULL;
649 align->center = NULL;
650 align->right = NULL;
651 /* Process all tokens of the desired subline */
652 for (i = subline->first_token_idx;
653 i <= subline->last_token_idx; i++)
655 switch(data->tokens[i].type)
657 case WPS_TOKEN_CONDITIONAL:
658 /* place ourselves in the right conditional case */
659 update |= evaluate_conditional(gwps, &i);
660 break;
662 case WPS_TOKEN_CONDITIONAL_OPTION:
663 /* we've finished in the curent conditional case,
664 skip to the end of the conditional structure */
665 i = find_conditional_end(data, i);
666 break;
668 #ifdef HAVE_LCD_BITMAP
669 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
671 char n = data->tokens[i].value.i & 0xFF;
672 int subimage = data->tokens[i].value.i >> 8;
673 struct gui_img *img = find_image(n, data);
675 if (img && img->loaded)
676 img->display = subimage;
677 break;
679 #endif
681 case WPS_TOKEN_ALIGN_LEFT:
682 case WPS_TOKEN_ALIGN_LEFT_RTL:
683 case WPS_TOKEN_ALIGN_CENTER:
684 case WPS_TOKEN_ALIGN_RIGHT:
685 case WPS_TOKEN_ALIGN_RIGHT_RTL:
686 /* remember where the current aligned text started */
687 switch (cur_align)
689 case WPS_ALIGN_LEFT:
690 align->left = cur_align_start;
691 break;
693 case WPS_ALIGN_CENTER:
694 align->center = cur_align_start;
695 break;
697 case WPS_ALIGN_RIGHT:
698 align->right = cur_align_start;
699 break;
701 /* start a new alignment */
702 switch (data->tokens[i].type)
704 case WPS_TOKEN_ALIGN_LEFT:
705 cur_align = WPS_ALIGN_LEFT;
706 break;
707 case WPS_TOKEN_ALIGN_LEFT_RTL:
708 cur_align = lang_is_rtl() ? WPS_ALIGN_RIGHT :
709 WPS_ALIGN_LEFT;
710 break;
711 case WPS_TOKEN_ALIGN_CENTER:
712 cur_align = WPS_ALIGN_CENTER;
713 break;
714 case WPS_TOKEN_ALIGN_RIGHT:
715 cur_align = WPS_ALIGN_RIGHT;
716 break;
717 case WPS_TOKEN_ALIGN_RIGHT_RTL:
718 cur_align = lang_is_rtl() ? WPS_ALIGN_LEFT :
719 WPS_ALIGN_RIGHT;
720 break;
721 default:
722 break;
724 *buf++ = 0;
725 cur_align_start = buf;
726 break;
727 case WPS_VIEWPORT_ENABLE:
729 char label = data->tokens[i].value.i;
730 char temp = VP_DRAW_HIDEABLE;
731 /* viewports are allowed to share id's so find and enable
732 * all of them */
733 struct skin_token_list *list = data->viewports;
734 while (list)
736 struct skin_viewport *vp =
737 (struct skin_viewport *)list->token->value.data;
738 if (vp->label == label)
740 if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
741 temp |= VP_DRAW_WASHIDDEN;
742 vp->hidden_flags = temp;
744 list = list->next;
747 break;
748 #ifdef HAVE_LCD_BITMAP
749 case WPS_VIEWPORT_CUSTOMLIST:
750 draw_playlist_viewer_list(gwps, data->tokens[i].value.data);
751 break;
752 #endif
753 default:
755 /* get the value of the tag and copy it to the buffer */
756 const char *value = get_token_value(gwps, &data->tokens[i],
757 temp_buf, sizeof(temp_buf), NULL);
758 if (value)
760 update = true;
761 while (*value && (buf < linebuf_end))
762 *buf++ = *value++;
764 break;
769 /* close the current alignment */
770 switch (cur_align)
772 case WPS_ALIGN_LEFT:
773 align->left = cur_align_start;
774 break;
776 case WPS_ALIGN_CENTER:
777 align->center = cur_align_start;
778 break;
780 case WPS_ALIGN_RIGHT:
781 align->right = cur_align_start;
782 break;
785 return update;
787 static void get_subline_timeout(struct gui_wps *gwps, struct skin_subline *subline)
789 struct wps_data *data = gwps->data;
790 int i;
791 subline->time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
793 for (i = subline->first_token_idx;
794 i <= subline->last_token_idx; i++)
796 switch(data->tokens[i].type)
798 case WPS_TOKEN_CONDITIONAL:
799 /* place ourselves in the right conditional case */
800 evaluate_conditional(gwps, &i);
801 break;
803 case WPS_TOKEN_CONDITIONAL_OPTION:
804 /* we've finished in the curent conditional case,
805 skip to the end of the conditional structure */
806 i = find_conditional_end(data, i);
807 break;
809 case WPS_TOKEN_SUBLINE_TIMEOUT:
810 subline->time_mult = data->tokens[i].value.i;
811 break;
813 default:
814 break;
819 /* Calculates which subline should be displayed for the specified line
820 Returns true iff the subline must be refreshed */
821 static bool update_curr_subline(struct gui_wps *gwps, struct skin_line *line)
823 /* shortcut this whole thing if we need to reset the line completly */
824 if (line->curr_subline == NULL)
826 line->subline_expire_time = current_tick;
827 line->curr_subline = &line->sublines;
828 if (!line->curr_subline->next)
830 line->subline_expire_time += 100*HZ;
832 else
834 get_subline_timeout(gwps, line->curr_subline);
835 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
837 return true;
839 /* if time to advance to next sub-line */
840 if (TIME_AFTER(current_tick, line->subline_expire_time - 1))
842 /* if there is only one subline, there is no need to search for a new one */
843 if (&line->sublines == line->curr_subline &&
844 line->curr_subline->next == NULL)
846 line->subline_expire_time += 100 * HZ;
847 return false;
849 if (line->curr_subline->next)
850 line->curr_subline = line->curr_subline->next;
851 else
852 line->curr_subline = &line->sublines;
853 get_subline_timeout(gwps, line->curr_subline);
854 line->subline_expire_time = current_tick + TIMEOUT_UNIT*line->curr_subline->time_mult;
855 return true;
857 return false;
860 /* Display a line appropriately according to its alignment format.
861 format_align contains the text, separated between left, center and right.
862 line is the index of the line on the screen.
863 scroll indicates whether the line is a scrolling one or not.
865 static void write_line(struct screen *display,
866 struct align_pos *format_align,
867 int line,
868 bool scroll)
870 int left_width = 0, left_xpos;
871 int center_width = 0, center_xpos;
872 int right_width = 0, right_xpos;
873 int ypos;
874 int space_width;
875 int string_height;
876 int scroll_width;
878 /* calculate different string sizes and positions */
879 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
880 if (format_align->left != 0) {
881 display->getstringsize((unsigned char *)format_align->left,
882 &left_width, &string_height);
885 if (format_align->right != 0) {
886 display->getstringsize((unsigned char *)format_align->right,
887 &right_width, &string_height);
890 if (format_align->center != 0) {
891 display->getstringsize((unsigned char *)format_align->center,
892 &center_width, &string_height);
895 left_xpos = 0;
896 right_xpos = (display->getwidth() - right_width);
897 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
899 scroll_width = display->getwidth() - left_xpos;
901 /* Checks for overlapping strings.
902 If needed the overlapping strings will be merged, separated by a
903 space */
905 /* CASE 1: left and centered string overlap */
906 /* there is a left string, need to merge left and center */
907 if ((left_width != 0 && center_width != 0) &&
908 (left_xpos + left_width + space_width > center_xpos)) {
909 /* replace the former separator '\0' of left and
910 center string with a space */
911 *(--format_align->center) = ' ';
912 /* calculate the new width and position of the merged string */
913 left_width = left_width + space_width + center_width;
914 /* there is no centered string anymore */
915 center_width = 0;
917 /* there is no left string, move center to left */
918 if ((left_width == 0 && center_width != 0) &&
919 (left_xpos + left_width > center_xpos)) {
920 /* move the center string to the left string */
921 format_align->left = format_align->center;
922 /* calculate the new width and position of the string */
923 left_width = center_width;
924 /* there is no centered string anymore */
925 center_width = 0;
928 /* CASE 2: centered and right string overlap */
929 /* there is a right string, need to merge center and right */
930 if ((center_width != 0 && right_width != 0) &&
931 (center_xpos + center_width + space_width > right_xpos)) {
932 /* replace the former separator '\0' of center and
933 right string with a space */
934 *(--format_align->right) = ' ';
935 /* move the center string to the right after merge */
936 format_align->right = format_align->center;
937 /* calculate the new width and position of the merged string */
938 right_width = center_width + space_width + right_width;
939 right_xpos = (display->getwidth() - right_width);
940 /* there is no centered string anymore */
941 center_width = 0;
943 /* there is no right string, move center to right */
944 if ((center_width != 0 && right_width == 0) &&
945 (center_xpos + center_width > right_xpos)) {
946 /* move the center string to the right string */
947 format_align->right = format_align->center;
948 /* calculate the new width and position of the string */
949 right_width = center_width;
950 right_xpos = (display->getwidth() - right_width);
951 /* there is no centered string anymore */
952 center_width = 0;
955 /* CASE 3: left and right overlap
956 There is no center string anymore, either there never
957 was one or it has been merged in case 1 or 2 */
958 /* there is a left string, need to merge left and right */
959 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
960 (left_xpos + left_width + space_width > right_xpos)) {
961 /* replace the former separator '\0' of left and
962 right string with a space */
963 *(--format_align->right) = ' ';
964 /* calculate the new width and position of the string */
965 left_width = left_width + space_width + right_width;
966 /* there is no right string anymore */
967 right_width = 0;
969 /* there is no left string, move right to left */
970 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
971 (left_width > right_xpos)) {
972 /* move the right string to the left string */
973 format_align->left = format_align->right;
974 /* calculate the new width and position of the string */
975 left_width = right_width;
976 /* there is no right string anymore */
977 right_width = 0;
980 ypos = (line * string_height);
983 if (scroll && ((left_width > scroll_width) ||
984 (center_width > scroll_width) ||
985 (right_width > scroll_width)))
987 display->puts_scroll(0, line,
988 (unsigned char *)format_align->left);
990 else
992 #ifdef HAVE_LCD_BITMAP
993 /* clear the line first */
994 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
995 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
996 display->set_drawmode(DRMODE_SOLID);
997 #endif
999 /* Nasty hack: we output an empty scrolling string,
1000 which will reset the scroller for that line */
1001 display->puts_scroll(0, line, (unsigned char *)"");
1003 /* print aligned strings */
1004 if (left_width != 0)
1006 display->putsxy(left_xpos, ypos,
1007 (unsigned char *)format_align->left);
1009 if (center_width != 0)
1011 display->putsxy(center_xpos, ypos,
1012 (unsigned char *)format_align->center);
1014 if (right_width != 0)
1016 display->putsxy(right_xpos, ypos,
1017 (unsigned char *)format_align->right);
1022 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
1024 struct wps_data *data = gwps->data;
1025 struct screen *display = gwps->display;
1027 if (!data || !display || !gwps->state)
1028 return false;
1030 unsigned flags;
1031 char linebuf[MAX_PATH];
1033 struct align_pos align;
1034 align.left = NULL;
1035 align.center = NULL;
1036 align.right = NULL;
1039 struct skin_token_list *viewport_list;
1041 bool update_line, new_subline_refresh;
1043 #ifdef HAVE_LCD_BITMAP
1045 /* to find out wether the peak meter is enabled we
1046 assume it wasn't until we find a line that contains
1047 the peak meter. We can't use peak_meter_enabled itself
1048 because that would mean to turn off the meter thread
1049 temporarily. (That shouldn't matter unless yield
1050 or sleep is called but who knows...)
1052 bool enable_pm = false;
1054 #endif
1056 /* reset to first subline if refresh all flag is set */
1057 if (refresh_mode == WPS_REFRESH_ALL)
1059 struct skin_line *line;
1060 struct skin_viewport *skin_viewport = find_viewport(VP_DEFAULT_LABEL, data);
1062 if (!(skin_viewport->hidden_flags & VP_NEVER_VISIBLE))
1064 display->set_viewport(&skin_viewport->vp);
1065 display->clear_viewport();
1068 for (viewport_list = data->viewports;
1069 viewport_list; viewport_list = viewport_list->next)
1071 skin_viewport =
1072 (struct skin_viewport *)viewport_list->token->value.data;
1073 for(line = skin_viewport->lines; line; line = line->next)
1075 line->curr_subline = NULL;
1080 #ifdef HAVE_LCD_CHARCELLS
1081 int i;
1082 for (i = 0; i < 8; i++)
1084 if (data->wps_progress_pat[i] == 0)
1085 data->wps_progress_pat[i] = display->get_locked_pattern();
1087 #endif
1089 /* disable any viewports which are conditionally displayed.
1090 * If we are only refreshing the peak meter then don't change the viewport
1091 * enabled flags as this will stop scrolling. viewports cant be
1092 * toggled in this refresh mode anyway (FS#10215)*/
1093 if (refresh_mode != WPS_REFRESH_PEAK_METER)
1095 for (viewport_list = data->viewports;
1096 viewport_list; viewport_list = viewport_list->next)
1098 struct skin_viewport *skin_viewport =
1099 (struct skin_viewport *)viewport_list->token->value.data;
1100 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1102 continue;
1104 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
1106 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
1107 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1108 else
1109 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
1113 int viewport_count = 0;
1114 for (viewport_list = data->viewports;
1115 viewport_list; viewport_list = viewport_list->next, viewport_count++)
1117 struct skin_viewport *skin_viewport =
1118 (struct skin_viewport *)viewport_list->token->value.data;
1119 unsigned vp_refresh_mode = refresh_mode;
1121 display->set_viewport(&skin_viewport->vp);
1123 int hidden_vp = 0;
1125 #ifdef HAVE_LCD_BITMAP
1126 /* Set images to not to be displayed */
1127 struct skin_token_list *imglist = data->images;
1128 while (imglist)
1130 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
1131 img->display = -1;
1132 imglist = imglist->next;
1134 #endif
1135 /* dont redraw the viewport if its disabled */
1136 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1137 { /* don't draw anything into this one */
1138 vp_refresh_mode = 0; hidden_vp = true;
1140 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
1142 if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
1143 display->scroll_stop(&skin_viewport->vp);
1144 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1145 continue;
1147 else if (((skin_viewport->hidden_flags&
1148 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
1149 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
1151 vp_refresh_mode = WPS_REFRESH_ALL;
1152 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
1155 if (vp_refresh_mode == WPS_REFRESH_ALL)
1157 display->clear_viewport();
1160 /* loop over the lines for this viewport */
1161 struct skin_line *line;
1162 int line_count = 0;
1164 for (line = skin_viewport->lines; line; line = line->next, line_count++)
1166 struct skin_subline *subline;
1167 memset(linebuf, 0, sizeof(linebuf));
1168 update_line = false;
1170 /* get current subline for the line */
1171 new_subline_refresh = update_curr_subline(gwps, line);
1172 subline = line->curr_subline;
1173 flags = line->curr_subline->line_type;
1175 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1176 || new_subline_refresh || hidden_vp)
1178 /* get_line tells us if we need to update the line */
1179 update_line = get_line(gwps, subline,
1180 &align, linebuf, sizeof(linebuf));
1182 #ifdef HAVE_LCD_BITMAP
1183 /* peakmeter */
1184 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1186 /* the peakmeter should be alone on its line */
1187 update_line = false;
1189 int h = font_get(skin_viewport->vp.font)->height;
1190 int peak_meter_y = line_count* h;
1192 /* The user might decide to have the peak meter in the last
1193 line so that it is only displayed if no status bar is
1194 visible. If so we neither want do draw nor enable the
1195 peak meter. */
1196 if (peak_meter_y + h <= skin_viewport->vp.y+skin_viewport->vp.height) {
1197 /* found a line with a peak meter -> remember that we must
1198 enable it later */
1199 enable_pm = true;
1200 peak_meter_enabled = true;
1201 peak_meter_screen(gwps->display, 0, peak_meter_y,
1202 MIN(h, skin_viewport->vp.y+skin_viewport->vp.height - peak_meter_y));
1204 else
1206 peak_meter_enabled = false;
1210 #else /* HAVE_LCD_CHARCELL */
1212 /* progressbar */
1213 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1215 if (data->full_line_progressbar)
1216 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1217 else
1218 draw_player_progress(gwps);
1220 #endif
1222 if (update_line && !hidden_vp &&
1223 /* conditionals clear the line which means if the %Vd is put into the default
1224 viewport there will be a blank line.
1225 To get around this we dont allow any actual drawing to happen in the
1226 deault vp if other vp's are defined */
1227 ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
1228 !viewport_list->next))
1230 if (flags & WPS_REFRESH_SCROLL)
1232 /* if the line is a scrolling one we don't want to update
1233 too often, so that it has the time to scroll */
1234 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1235 write_line(display, &align, line_count, true);
1237 else
1238 write_line(display, &align, line_count, false);
1241 #ifdef HAVE_LCD_BITMAP
1242 /* progressbar */
1243 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1245 if (skin_viewport->pb)
1247 draw_progressbar(gwps, skin_viewport);
1250 /* Now display any images in this viewport */
1251 if (!hidden_vp)
1252 wps_display_images(gwps, &skin_viewport->vp);
1253 #endif
1256 #ifdef HAVE_LCD_BITMAP
1257 data->peak_meter_enabled = enable_pm;
1258 #endif
1259 /* Restore the default viewport */
1260 display->set_viewport(NULL);
1262 display->update();
1264 return true;