Move audio_peek_track declaration from skin_display.c to audio.h
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
blob827d90c630aecd428426d29288e6ba85511f4142
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 #include "sound.h"
30 #ifdef DEBUG
31 #include "debug.h"
32 #endif
33 #include "abrepeat.h"
34 #include "lang.h"
35 #include "language.h"
36 #include "statusbar.h"
37 #include "settings.h"
38 #include "scrollbar.h"
39 #include "screen_access.h"
40 #include "playlist.h"
41 #include "audio.h"
43 #ifdef HAVE_LCD_BITMAP
44 #include "peakmeter.h"
45 /* Image stuff */
46 #include "bmp.h"
47 #ifdef HAVE_ALBUMART
48 #include "albumart.h"
49 #endif
50 #endif
52 #include "cuesheet.h"
53 #if CONFIG_CODEC == SWCODEC
54 #include "playback.h"
55 #endif
56 #include "backdrop.h"
57 #include "viewport.h"
60 #include "wps_internals.h"
61 #include "skin_engine.h"
62 #include "statusbar-skinned.h"
64 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode);
66 /* update a skinned screen, update_type is WPS_REFRESH_* values.
67 * Usually it should only be WPS_REFRESH_NON_STATIC
68 * A full update will be done if required (state.do_full_update == true)
70 bool skin_update(struct gui_wps *gwps, unsigned int update_type)
72 bool retval;
73 /* This maybe shouldnt be here, but while the skin is only used to
74 * display the music screen this is better than whereever we are being
75 * called from. This is also safe for skined screen which dont use the id3 */
76 struct mp3entry *id3 = gwps->state->id3;
77 bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
78 gwps->sync_data->do_full_update |= cuesheet_update;
80 retval = skin_redraw(gwps, gwps->sync_data->do_full_update ?
81 WPS_REFRESH_ALL : update_type);
82 return retval;
85 #ifdef HAVE_LCD_BITMAP
87 void skin_statusbar_changed(struct gui_wps *skin)
89 if (!skin)
90 return;
91 struct wps_data *data = skin->data;
92 const struct screen *display = skin->display;
93 const int screen = display->screen_type;
95 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, data)->vp;
96 viewport_set_defaults(vp, screen);
98 if (data->wps_sb_tag)
99 { /* fix up the default viewport */
100 if (data->show_sb_on_wps)
102 if (statusbar_position(screen) != STATUSBAR_OFF)
103 return; /* vp is fixed already */
105 vp->y = STATUSBAR_HEIGHT;
106 vp->height = display->lcdheight - STATUSBAR_HEIGHT;
108 else
110 if (statusbar_position(screen) == STATUSBAR_OFF)
111 return; /* vp is fixed already */
112 vp->y = vp->x = 0;
113 vp->height = display->lcdheight;
114 vp->width = display->lcdwidth;
119 static void draw_progressbar(struct gui_wps *gwps,
120 struct progressbar *pb)
122 struct screen *display = gwps->display;
123 struct viewport *vp = pb->vp;
124 struct wps_state *state = gwps->state;
125 struct mp3entry *id3 = state->id3;
126 int y = pb->y, height = pb->height;
127 unsigned long length, elapsed;
129 if (height < 0)
130 height = font_get(vp->font)->height;
132 if (y < 0)
134 int line_height = font_get(vp->font)->height;
135 /* center the pb in the line, but only if the line is higher than the pb */
136 int center = (line_height-height)/2;
137 /* if Y was not set calculate by font height,Y is -line_number-1 */
138 y = (-y -1)*line_height + (0 > center ? 0 : center);
141 if (pb->type == WPS_TOKEN_VOLUMEBAR)
143 int minvol = sound_min(SOUND_VOLUME);
144 int maxvol = sound_max(SOUND_VOLUME);
145 length = maxvol-minvol;
146 elapsed = global_settings.volume-minvol;
148 else if (id3 && id3->length)
150 length = id3->length;
151 elapsed = id3->elapsed + state->ff_rewind_count;
153 else
155 length = 1;
156 elapsed = 0;
159 if (pb->have_bitmap_pb)
160 gui_bitmap_scrollbar_draw(display, pb->bm,
161 pb->x, y, pb->width, pb->bm.height,
162 length, 0, elapsed, HORIZONTAL);
163 else
164 gui_scrollbar_draw(display, pb->x, y, pb->width, height,
165 length, 0, elapsed, HORIZONTAL);
167 if (pb->type == WPS_TOKEN_PROGRESSBAR && id3 && id3->length)
169 #ifdef AB_REPEAT_ENABLE
170 if (ab_repeat_mode_enabled())
171 ab_draw_markers(display, id3->length,
172 pb->x, y, pb->width, height);
173 #endif
175 if (id3->cuesheet)
176 cue_draw_markers(display, id3->cuesheet, id3->length,
177 pb->x, y+1, pb->width, height-2);
181 static void draw_playlist_viewer_list(struct gui_wps *gwps,
182 struct playlistviewer *viewer)
184 struct wps_state *state = gwps->state;
185 int lines = viewport_get_nb_lines(viewer->vp);
186 int line_height = font_get(viewer->vp->font)->height;
187 int cur_playlist_pos = playlist_get_display_index();
188 int start_item = MAX(0, cur_playlist_pos + viewer->start_offset);
189 int i;
190 struct wps_token token;
191 int x, length, alignment = WPS_TOKEN_ALIGN_LEFT;
193 struct mp3entry *pid3;
194 char buf[MAX_PATH*2], tempbuf[MAX_PATH];
196 gwps->display->set_viewport(viewer->vp);
197 for(i=start_item; (i-start_item)<lines && i<=playlist_amount(); i++)
199 if (i == cur_playlist_pos)
201 pid3 = state->id3;
203 else if (i == cur_playlist_pos+1)
205 pid3 = state->nid3;
207 #if CONFIG_CODEC == SWCODEC
208 else if (i>cur_playlist_pos)
210 if (!audio_peek_track(&pid3, i-cur_playlist_pos))
211 pid3 = NULL;
213 #endif
214 else
216 pid3 = NULL;
219 int line = pid3 ? TRACK_HAS_INFO : TRACK_HAS_NO_INFO;
220 int j = 0, cur_string = 0;
221 char *filename = playlist_peek(i-cur_playlist_pos);
222 unsigned int line_len = 0;
223 buf[0] = '\0';
224 while (j < viewer->lines[line].count && line_len < sizeof(buf))
226 const char *out = NULL;
227 token.type = viewer->lines[line].tokens[j];
228 token.value.i = 0;
229 token.next = false;
230 out = get_id3_token(&token, pid3, tempbuf, sizeof(tempbuf), -1, NULL);
231 if (out)
233 line_len = strlcat(buf, out, sizeof(buf));
234 j++;
235 continue;
237 switch (viewer->lines[line].tokens[j])
239 case WPS_TOKEN_ALIGN_CENTER:
240 case WPS_TOKEN_ALIGN_LEFT:
241 case WPS_TOKEN_ALIGN_LEFT_RTL:
242 case WPS_TOKEN_ALIGN_RIGHT:
243 case WPS_TOKEN_ALIGN_RIGHT_RTL:
244 alignment = viewer->lines[line].tokens[j];
245 tempbuf[0] = '\0';
246 break;
247 case WPS_TOKEN_STRING:
248 case WPS_TOKEN_CHARACTER:
249 snprintf(tempbuf, sizeof(tempbuf), "%s",
250 viewer->lines[line].strings[cur_string]);
251 cur_string++;
252 break;
253 case WPS_TOKEN_PLAYLIST_POSITION:
254 snprintf(tempbuf, sizeof(tempbuf), "%d", i);
255 break;
256 case WPS_TOKEN_FILE_NAME:
257 get_dir(tempbuf, sizeof(tempbuf), filename, 0);
258 break;
259 case WPS_TOKEN_FILE_PATH:
260 snprintf(tempbuf, sizeof(tempbuf), "%s", filename);
261 break;
262 default:
263 tempbuf[0] = '\0';
264 break;
266 if (tempbuf[0])
268 line_len = strlcat(buf, tempbuf, sizeof(buf));
270 j++;
273 int vpwidth = viewer->vp->width;
274 length = gwps->display->getstringsize(buf, NULL, NULL);
275 if (viewer->lines[line].scroll && length >= vpwidth)
277 gwps->display->puts_scroll(0, (i-start_item), buf );
279 else
281 if (length >= vpwidth)
282 x = 0;
283 else
285 switch (alignment)
287 case WPS_TOKEN_ALIGN_CENTER:
288 x = (vpwidth-length)/2;
289 break;
290 case WPS_TOKEN_ALIGN_LEFT_RTL:
291 if (lang_is_rtl() && VP_IS_RTL(viewer->vp))
293 x = vpwidth - length;
294 break;
296 case WPS_TOKEN_ALIGN_LEFT:
297 x = 0;
298 break;
299 case WPS_TOKEN_ALIGN_RIGHT_RTL:
300 if (lang_is_rtl() && VP_IS_RTL(viewer->vp))
302 x = 0;
303 break;
305 case WPS_TOKEN_ALIGN_RIGHT:
306 x = vpwidth - length;
307 break;
308 default:
309 x = 0;
310 break;
313 gwps->display->putsxy(x, (i-start_item)*line_height, buf );
319 /* clears the area where the image was shown */
320 static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
322 if(!gwps)
323 return;
324 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
325 gwps->display->fillrect(img->x, img->y, img->bm.width, img->subimage_height);
326 gwps->display->set_drawmode(DRMODE_SOLID);
329 static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage)
331 struct screen *display = gwps->display;
332 if(img->always_display)
333 display->set_drawmode(DRMODE_FG);
334 else
335 display->set_drawmode(DRMODE_SOLID);
337 #if LCD_DEPTH > 1
338 if(img->bm.format == FORMAT_MONO) {
339 #endif
340 display->mono_bitmap_part(img->bm.data,
341 0, img->subimage_height * subimage,
342 img->bm.width, img->x,
343 img->y, img->bm.width,
344 img->subimage_height);
345 #if LCD_DEPTH > 1
346 } else {
347 display->transparent_bitmap_part((fb_data *)img->bm.data,
348 0, img->subimage_height * subimage,
349 STRIDE(display->screen_type,
350 img->bm.width, img->bm.height),
351 img->x, img->y, img->bm.width,
352 img->subimage_height);
354 #endif
357 static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
359 if(!gwps || !gwps->data || !gwps->display)
360 return;
362 struct wps_data *data = gwps->data;
363 struct screen *display = gwps->display;
364 struct skin_token_list *list = data->images;
366 while (list)
368 struct gui_img *img = (struct gui_img*)list->token->value.data;
369 if (img->loaded)
371 if (img->display >= 0)
373 wps_draw_image(gwps, img, img->display);
375 else if (img->always_display && img->vp == vp)
377 wps_draw_image(gwps, img, 0);
380 list = list->next;
382 #ifdef HAVE_ALBUMART
383 /* now draw the AA */
384 if (data->albumart && data->albumart->vp == vp
385 && data->albumart->draw)
387 draw_album_art(gwps, playback_current_aa_hid(data->playback_aa_slot),
388 false);
389 data->albumart->draw = false;
391 #endif
393 display->set_drawmode(DRMODE_SOLID);
396 #else /* HAVE_LCD_CHARCELL */
398 static bool draw_player_progress(struct gui_wps *gwps)
400 struct wps_state *state = gwps->state;
401 struct screen *display = gwps->display;
402 unsigned char progress_pattern[7];
403 int pos = 0;
404 int i;
406 int elapsed, length;
407 if (LIKELY(state->id3))
409 elapsed = state->id3->elapsed;
410 length = state->id3->length;
412 else
414 elapsed = 0;
415 length = 0;
418 if (length)
419 pos = 36 * (elapsed + state->ff_rewind_count) / length;
421 for (i = 0; i < 7; i++, pos -= 5)
423 if (pos <= 0)
424 progress_pattern[i] = 0x1fu;
425 else if (pos >= 5)
426 progress_pattern[i] = 0x00u;
427 else
428 progress_pattern[i] = 0x1fu >> pos;
431 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
432 return true;
435 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
437 static const unsigned char numbers[10][4] = {
438 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
439 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
440 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
441 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
442 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
443 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
444 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
445 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
446 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
447 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
450 struct wps_state *state = gwps->state;
451 struct screen *display = gwps->display;
452 struct wps_data *data = gwps->data;
453 unsigned char progress_pattern[7];
454 char timestr[10];
455 int time;
456 int time_idx = 0;
457 int pos = 0;
458 int pat_idx = 1;
459 int digit, i, j;
460 bool softchar;
462 int elapsed, length;
463 if (LIKELY(state->id3))
465 elapsed = state->id3->elapsed;
466 length = state->id3->length;
468 else
470 elapsed = 0;
471 length = 0;
474 if (buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
475 return;
477 time = elapsed + state->ff_rewind_count;
478 if (length)
479 pos = 55 * time / length;
481 memset(timestr, 0, sizeof(timestr));
482 format_time(timestr, sizeof(timestr)-2, time);
483 timestr[strlen(timestr)] = ':'; /* always safe */
485 for (i = 0; i < 11; i++, pos -= 5)
487 softchar = false;
488 memset(progress_pattern, 0, sizeof(progress_pattern));
490 if ((digit = timestr[time_idx]))
492 softchar = true;
493 digit -= '0';
495 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
497 memcpy(progress_pattern, numbers[digit], 4);
498 time_idx += 2;
500 else /* tens, shifted right */
502 for (j = 0; j < 4; j++)
503 progress_pattern[j] = numbers[digit][j] >> 1;
505 if (time_idx > 0) /* not the first group, add colon in front */
507 progress_pattern[1] |= 0x10u;
508 progress_pattern[3] |= 0x10u;
510 time_idx++;
513 if (pos >= 5)
514 progress_pattern[5] = progress_pattern[6] = 0x1fu;
517 if (pos > 0 && pos < 5)
519 softchar = true;
520 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
523 if (softchar && pat_idx < 8)
525 display->define_pattern(data->wps_progress_pat[pat_idx],
526 progress_pattern);
527 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
528 pat_idx++;
530 else if (pos <= 0)
531 buf = utf8encode(' ', buf);
532 else
533 buf = utf8encode(0xe115, buf); /* 2/7 _ */
535 *buf = '\0';
538 #endif /* HAVE_LCD_CHARCELL */
540 /* Return the index to the end token for the conditional token at index.
541 The conditional token can be either a start token or a separator
542 (i.e. option) token.
544 static int find_conditional_end(struct wps_data *data, int index)
546 int ret = index;
547 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
548 ret = data->tokens[ret].value.i;
550 /* ret now is the index to the end token for the conditional. */
551 return ret;
554 /* Evaluate the conditional that is at *token_index and return whether a skip
555 has ocurred. *token_index is updated with the new position.
557 static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
559 if (!gwps)
560 return false;
562 struct wps_data *data = gwps->data;
564 int i, cond_end;
565 int cond_index = *token_index;
566 char result[128];
567 const char *value;
568 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
569 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
571 /* treat ?xx<true> constructs as if they had 2 options. */
572 if (num_options < 2)
573 num_options = 2;
575 int intval = num_options;
576 /* get_token_value needs to know the number of options in the enum */
577 value = get_token_value(gwps, &data->tokens[cond_index + 1],
578 result, sizeof(result), &intval);
580 /* intval is now the number of the enum option we want to read,
581 starting from 1. If intval is -1, we check if value is empty. */
582 if (intval == -1)
583 intval = (value && *value) ? 1 : num_options;
584 else if (intval > num_options || intval < 1)
585 intval = num_options;
587 data->tokens[cond_index].value.i = (intval << 8) + num_options;
589 /* skip to the appropriate enum case */
590 int next = cond_index + 2;
591 for (i = 1; i < intval; i++)
593 next = data->tokens[next].value.i;
595 *token_index = next;
597 if (prev_val == intval)
599 /* Same conditional case as previously. Return without clearing the
600 pictures */
601 return false;
604 cond_end = find_conditional_end(data, cond_index + 2);
605 for (i = cond_index + 3; i < cond_end; i++)
607 #ifdef HAVE_LCD_BITMAP
608 /* clear all pictures in the conditional and nested ones */
609 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
610 clear_image_pos(gwps, find_image(data->tokens[i].value.i&0xFF, data));
611 #endif
612 #ifdef HAVE_ALBUMART
613 if (data->albumart && data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
615 draw_album_art(gwps,
616 playback_current_aa_hid(data->playback_aa_slot), true);
617 data->albumart->draw = false;
619 #endif
622 return true;
626 /* Read a (sub)line to the given alignment format buffer.
627 linebuf is the buffer where the data is actually stored.
628 align is the alignment format that'll be used to display the text.
629 The return value indicates whether the line needs to be updated.
631 static bool get_line(struct gui_wps *gwps,
632 struct skin_subline *subline,
633 struct align_pos *align,
634 char *linebuf,
635 int linebuf_size,
636 unsigned refresh_mode)
638 struct wps_data *data = gwps->data;
640 char temp_buf[128];
641 char *buf = linebuf; /* will always point to the writing position */
642 char *linebuf_end = linebuf + linebuf_size - 1;
643 bool update = false;
644 int i;
645 (void)refresh_mode; /* silence warning on charcell */
647 /* alignment-related variables */
648 int cur_align;
649 char* cur_align_start;
650 cur_align_start = buf;
651 cur_align = WPS_ALIGN_LEFT;
652 align->left = NULL;
653 align->center = NULL;
654 align->right = NULL;
655 /* Process all tokens of the desired subline */
656 for (i = subline->first_token_idx;
657 i <= subline->last_token_idx; i++)
659 switch(data->tokens[i].type)
661 case WPS_TOKEN_CONDITIONAL:
662 /* place ourselves in the right conditional case */
663 update |= evaluate_conditional(gwps, &i);
664 break;
666 case WPS_TOKEN_CONDITIONAL_OPTION:
667 /* we've finished in the curent conditional case,
668 skip to the end of the conditional structure */
669 i = find_conditional_end(data, i);
670 break;
672 #ifdef HAVE_LCD_BITMAP
673 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
675 char n = data->tokens[i].value.i & 0xFF;
676 int subimage = data->tokens[i].value.i >> 8;
677 struct gui_img *img = find_image(n, data);
679 if (img && img->loaded)
680 img->display = subimage;
681 break;
683 case WPS_TOKEN_DRAW_INBUILTBAR:
684 gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
685 refresh_mode == WPS_REFRESH_ALL,
686 data->tokens[i].value.data);
687 break;
688 #endif
690 case WPS_TOKEN_ALIGN_LEFT:
691 case WPS_TOKEN_ALIGN_LEFT_RTL:
692 case WPS_TOKEN_ALIGN_CENTER:
693 case WPS_TOKEN_ALIGN_RIGHT:
694 case WPS_TOKEN_ALIGN_RIGHT_RTL:
695 /* remember where the current aligned text started */
696 switch (cur_align)
698 case WPS_ALIGN_LEFT:
699 align->left = cur_align_start;
700 break;
702 case WPS_ALIGN_CENTER:
703 align->center = cur_align_start;
704 break;
706 case WPS_ALIGN_RIGHT:
707 align->right = cur_align_start;
708 break;
710 /* start a new alignment */
711 switch (data->tokens[i].type)
713 case WPS_TOKEN_ALIGN_LEFT:
714 cur_align = WPS_ALIGN_LEFT;
715 break;
716 case WPS_TOKEN_ALIGN_LEFT_RTL:
717 cur_align = lang_is_rtl() ? WPS_ALIGN_RIGHT :
718 WPS_ALIGN_LEFT;
719 break;
720 case WPS_TOKEN_ALIGN_CENTER:
721 cur_align = WPS_ALIGN_CENTER;
722 break;
723 case WPS_TOKEN_ALIGN_RIGHT:
724 cur_align = WPS_ALIGN_RIGHT;
725 break;
726 case WPS_TOKEN_ALIGN_RIGHT_RTL:
727 cur_align = lang_is_rtl() ? WPS_ALIGN_LEFT :
728 WPS_ALIGN_RIGHT;
729 break;
730 default:
731 break;
733 *buf++ = 0;
734 cur_align_start = buf;
735 break;
736 case WPS_VIEWPORT_ENABLE:
738 char label = data->tokens[i].value.i;
739 char temp = VP_DRAW_HIDEABLE;
740 /* viewports are allowed to share id's so find and enable
741 * all of them */
742 struct skin_token_list *list = data->viewports;
743 while (list)
745 struct skin_viewport *vp =
746 (struct skin_viewport *)list->token->value.data;
747 if (vp->label == label)
749 if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
750 temp |= VP_DRAW_WASHIDDEN;
751 vp->hidden_flags = temp;
753 list = list->next;
756 break;
757 #ifdef HAVE_LCD_BITMAP
758 case WPS_VIEWPORT_CUSTOMLIST:
759 draw_playlist_viewer_list(gwps, data->tokens[i].value.data);
760 break;
761 #endif
762 default:
764 /* get the value of the tag and copy it to the buffer */
765 const char *value = get_token_value(gwps, &data->tokens[i],
766 temp_buf, sizeof(temp_buf), NULL);
767 if (value)
769 update = true;
770 while (*value && (buf < linebuf_end))
771 *buf++ = *value++;
773 break;
778 /* close the current alignment */
779 switch (cur_align)
781 case WPS_ALIGN_LEFT:
782 align->left = cur_align_start;
783 break;
785 case WPS_ALIGN_CENTER:
786 align->center = cur_align_start;
787 break;
789 case WPS_ALIGN_RIGHT:
790 align->right = cur_align_start;
791 break;
794 return update;
796 static void get_subline_timeout(struct gui_wps *gwps, struct skin_subline *subline)
798 struct wps_data *data = gwps->data;
799 int i;
800 subline->time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
802 for (i = subline->first_token_idx;
803 i <= subline->last_token_idx; i++)
805 switch(data->tokens[i].type)
807 case WPS_TOKEN_CONDITIONAL:
808 /* place ourselves in the right conditional case */
809 evaluate_conditional(gwps, &i);
810 break;
812 case WPS_TOKEN_CONDITIONAL_OPTION:
813 /* we've finished in the curent conditional case,
814 skip to the end of the conditional structure */
815 i = find_conditional_end(data, i);
816 break;
818 case WPS_TOKEN_SUBLINE_TIMEOUT:
819 subline->time_mult = data->tokens[i].value.i;
820 break;
822 default:
823 break;
828 /* Calculates which subline should be displayed for the specified line
829 Returns true iff the subline must be refreshed */
830 static bool update_curr_subline(struct gui_wps *gwps, struct skin_line *line)
832 /* shortcut this whole thing if we need to reset the line completly */
833 if (line->curr_subline == NULL)
835 line->subline_expire_time = current_tick;
836 line->curr_subline = &line->sublines;
837 if (!line->curr_subline->next)
839 line->subline_expire_time += 100*HZ;
841 else
843 get_subline_timeout(gwps, line->curr_subline);
844 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
846 return true;
848 /* if time to advance to next sub-line */
849 if (TIME_AFTER(current_tick, line->subline_expire_time - 1))
851 /* if there is only one subline, there is no need to search for a new one */
852 if (&line->sublines == line->curr_subline &&
853 line->curr_subline->next == NULL)
855 line->subline_expire_time += 100 * HZ;
856 return false;
858 if (line->curr_subline->next)
859 line->curr_subline = line->curr_subline->next;
860 else
861 line->curr_subline = &line->sublines;
862 get_subline_timeout(gwps, line->curr_subline);
863 line->subline_expire_time = current_tick + TIMEOUT_UNIT*line->curr_subline->time_mult;
864 return true;
866 return false;
869 /* Display a line appropriately according to its alignment format.
870 format_align contains the text, separated between left, center and right.
871 line is the index of the line on the screen.
872 scroll indicates whether the line is a scrolling one or not.
874 static void write_line(struct screen *display,
875 struct align_pos *format_align,
876 int line,
877 bool scroll)
879 int left_width = 0, left_xpos;
880 int center_width = 0, center_xpos;
881 int right_width = 0, right_xpos;
882 int ypos;
883 int space_width;
884 int string_height;
885 int scroll_width;
887 /* calculate different string sizes and positions */
888 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
889 if (format_align->left != 0) {
890 display->getstringsize((unsigned char *)format_align->left,
891 &left_width, &string_height);
894 if (format_align->right != 0) {
895 display->getstringsize((unsigned char *)format_align->right,
896 &right_width, &string_height);
899 if (format_align->center != 0) {
900 display->getstringsize((unsigned char *)format_align->center,
901 &center_width, &string_height);
904 left_xpos = 0;
905 right_xpos = (display->getwidth() - right_width);
906 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
908 scroll_width = display->getwidth() - left_xpos;
910 /* Checks for overlapping strings.
911 If needed the overlapping strings will be merged, separated by a
912 space */
914 /* CASE 1: left and centered string overlap */
915 /* there is a left string, need to merge left and center */
916 if ((left_width != 0 && center_width != 0) &&
917 (left_xpos + left_width + space_width > center_xpos)) {
918 /* replace the former separator '\0' of left and
919 center string with a space */
920 *(--format_align->center) = ' ';
921 /* calculate the new width and position of the merged string */
922 left_width = left_width + space_width + center_width;
923 /* there is no centered string anymore */
924 center_width = 0;
926 /* there is no left string, move center to left */
927 if ((left_width == 0 && center_width != 0) &&
928 (left_xpos + left_width > center_xpos)) {
929 /* move the center string to the left string */
930 format_align->left = format_align->center;
931 /* calculate the new width and position of the string */
932 left_width = center_width;
933 /* there is no centered string anymore */
934 center_width = 0;
937 /* CASE 2: centered and right string overlap */
938 /* there is a right string, need to merge center and right */
939 if ((center_width != 0 && right_width != 0) &&
940 (center_xpos + center_width + space_width > right_xpos)) {
941 /* replace the former separator '\0' of center and
942 right string with a space */
943 *(--format_align->right) = ' ';
944 /* move the center string to the right after merge */
945 format_align->right = format_align->center;
946 /* calculate the new width and position of the merged string */
947 right_width = center_width + space_width + right_width;
948 right_xpos = (display->getwidth() - right_width);
949 /* there is no centered string anymore */
950 center_width = 0;
952 /* there is no right string, move center to right */
953 if ((center_width != 0 && right_width == 0) &&
954 (center_xpos + center_width > right_xpos)) {
955 /* move the center string to the right string */
956 format_align->right = format_align->center;
957 /* calculate the new width and position of the string */
958 right_width = center_width;
959 right_xpos = (display->getwidth() - right_width);
960 /* there is no centered string anymore */
961 center_width = 0;
964 /* CASE 3: left and right overlap
965 There is no center string anymore, either there never
966 was one or it has been merged in case 1 or 2 */
967 /* there is a left string, need to merge left and right */
968 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
969 (left_xpos + left_width + space_width > right_xpos)) {
970 /* replace the former separator '\0' of left and
971 right string with a space */
972 *(--format_align->right) = ' ';
973 /* calculate the new width and position of the string */
974 left_width = left_width + space_width + right_width;
975 /* there is no right string anymore */
976 right_width = 0;
978 /* there is no left string, move right to left */
979 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
980 (left_width > right_xpos)) {
981 /* move the right string to the left string */
982 format_align->left = format_align->right;
983 /* calculate the new width and position of the string */
984 left_width = right_width;
985 /* there is no right string anymore */
986 right_width = 0;
989 ypos = (line * string_height);
992 if (scroll && ((left_width > scroll_width) ||
993 (center_width > scroll_width) ||
994 (right_width > scroll_width)))
996 display->puts_scroll(0, line,
997 (unsigned char *)format_align->left);
999 else
1001 #ifdef HAVE_LCD_BITMAP
1002 /* clear the line first */
1003 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1004 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
1005 display->set_drawmode(DRMODE_SOLID);
1006 #endif
1008 /* Nasty hack: we output an empty scrolling string,
1009 which will reset the scroller for that line */
1010 display->puts_scroll(0, line, (unsigned char *)"");
1012 /* print aligned strings */
1013 if (left_width != 0)
1015 display->putsxy(left_xpos, ypos,
1016 (unsigned char *)format_align->left);
1018 if (center_width != 0)
1020 display->putsxy(center_xpos, ypos,
1021 (unsigned char *)format_align->center);
1023 if (right_width != 0)
1025 display->putsxy(right_xpos, ypos,
1026 (unsigned char *)format_align->right);
1031 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
1033 struct wps_data *data = gwps->data;
1034 struct screen *display = gwps->display;
1036 if (!data || !display || !gwps->state)
1037 return false;
1039 unsigned flags;
1040 char linebuf[MAX_PATH];
1042 struct align_pos align;
1043 align.left = NULL;
1044 align.center = NULL;
1045 align.right = NULL;
1048 struct skin_token_list *viewport_list;
1050 bool update_line, new_subline_refresh;
1052 #ifdef HAVE_LCD_BITMAP
1054 /* to find out wether the peak meter is enabled we
1055 assume it wasn't until we find a line that contains
1056 the peak meter. We can't use peak_meter_enabled itself
1057 because that would mean to turn off the meter thread
1058 temporarily. (That shouldn't matter unless yield
1059 or sleep is called but who knows...)
1061 bool enable_pm = false;
1063 #endif
1065 /* reset to first subline if refresh all flag is set */
1066 if (refresh_mode == WPS_REFRESH_ALL)
1068 struct skin_line *line;
1069 struct skin_viewport *skin_viewport = find_viewport(VP_DEFAULT_LABEL, data);
1071 if (!(skin_viewport->hidden_flags & VP_NEVER_VISIBLE))
1073 display->set_viewport(&skin_viewport->vp);
1074 display->clear_viewport();
1077 for (viewport_list = data->viewports;
1078 viewport_list; viewport_list = viewport_list->next)
1080 skin_viewport =
1081 (struct skin_viewport *)viewport_list->token->value.data;
1082 for(line = skin_viewport->lines; line; line = line->next)
1084 line->curr_subline = NULL;
1089 #ifdef HAVE_LCD_CHARCELLS
1090 int i;
1091 for (i = 0; i < 8; i++)
1093 if (data->wps_progress_pat[i] == 0)
1094 data->wps_progress_pat[i] = display->get_locked_pattern();
1096 #endif
1098 /* disable any viewports which are conditionally displayed.
1099 * If we are only refreshing the peak meter then don't change the viewport
1100 * enabled flags as this will stop scrolling. viewports cant be
1101 * toggled in this refresh mode anyway (FS#10215)*/
1102 if (refresh_mode != WPS_REFRESH_PEAK_METER)
1104 for (viewport_list = data->viewports;
1105 viewport_list; viewport_list = viewport_list->next)
1107 struct skin_viewport *skin_viewport =
1108 (struct skin_viewport *)viewport_list->token->value.data;
1109 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1111 continue;
1113 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
1115 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
1116 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1117 else
1118 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
1122 int viewport_count = 0;
1123 for (viewport_list = data->viewports;
1124 viewport_list; viewport_list = viewport_list->next, viewport_count++)
1126 struct skin_viewport *skin_viewport =
1127 (struct skin_viewport *)viewport_list->token->value.data;
1128 unsigned vp_refresh_mode = refresh_mode;
1130 display->set_viewport(&skin_viewport->vp);
1132 int hidden_vp = 0;
1134 #ifdef HAVE_LCD_BITMAP
1135 /* Set images to not to be displayed */
1136 struct skin_token_list *imglist = data->images;
1137 while (imglist)
1139 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
1140 img->display = -1;
1141 imglist = imglist->next;
1143 #endif
1144 /* dont redraw the viewport if its disabled */
1145 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1146 { /* don't draw anything into this one */
1147 vp_refresh_mode = 0; hidden_vp = true;
1149 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
1151 if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
1152 display->scroll_stop(&skin_viewport->vp);
1153 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1154 continue;
1156 else if (((skin_viewport->hidden_flags&
1157 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
1158 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
1160 vp_refresh_mode = WPS_REFRESH_ALL;
1161 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
1164 if (vp_refresh_mode == WPS_REFRESH_ALL)
1166 display->clear_viewport();
1169 /* loop over the lines for this viewport */
1170 struct skin_line *line;
1171 int line_count = 0;
1173 for (line = skin_viewport->lines; line; line = line->next, line_count++)
1175 struct skin_subline *subline;
1176 memset(linebuf, 0, sizeof(linebuf));
1177 update_line = false;
1179 /* get current subline for the line */
1180 new_subline_refresh = update_curr_subline(gwps, line);
1181 subline = line->curr_subline;
1182 flags = line->curr_subline->line_type;
1184 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1185 || new_subline_refresh || hidden_vp)
1187 /* get_line tells us if we need to update the line */
1188 update_line = get_line(gwps, subline, &align,
1189 linebuf, sizeof(linebuf), vp_refresh_mode);
1191 #ifdef HAVE_LCD_BITMAP
1192 /* peakmeter */
1193 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1195 /* the peakmeter should be alone on its line */
1196 update_line = false;
1198 int h = font_get(skin_viewport->vp.font)->height;
1199 int peak_meter_y = line_count* h;
1201 /* The user might decide to have the peak meter in the last
1202 line so that it is only displayed if no status bar is
1203 visible. If so we neither want do draw nor enable the
1204 peak meter. */
1205 if (peak_meter_y + h <= skin_viewport->vp.y+skin_viewport->vp.height) {
1206 /* found a line with a peak meter -> remember that we must
1207 enable it later */
1208 enable_pm = true;
1209 peak_meter_enabled = true;
1210 peak_meter_screen(gwps->display, 0, peak_meter_y,
1211 MIN(h, skin_viewport->vp.y+skin_viewport->vp.height - peak_meter_y));
1213 else
1215 peak_meter_enabled = false;
1219 #else /* HAVE_LCD_CHARCELL */
1221 /* progressbar */
1222 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1224 if (data->full_line_progressbar)
1225 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1226 else
1227 draw_player_progress(gwps);
1229 #endif
1231 if (update_line && !hidden_vp &&
1232 /* conditionals clear the line which means if the %Vd is put into the default
1233 viewport there will be a blank line.
1234 To get around this we dont allow any actual drawing to happen in the
1235 deault vp if other vp's are defined */
1236 ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
1237 !viewport_list->next))
1239 if (flags & WPS_REFRESH_SCROLL)
1241 /* if the line is a scrolling one we don't want to update
1242 too often, so that it has the time to scroll */
1243 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1244 write_line(display, &align, line_count, true);
1246 else
1247 write_line(display, &align, line_count, false);
1250 #ifdef HAVE_LCD_BITMAP
1251 /* progressbar */
1252 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1254 struct skin_token_list *bar = gwps->data->progressbars;
1255 while (bar)
1257 struct progressbar *thisbar = (struct progressbar*)bar->token->value.data;
1258 if (thisbar->vp == &skin_viewport->vp)
1260 draw_progressbar(gwps, thisbar);
1262 bar = bar->next;
1265 /* Now display any images in this viewport */
1266 if (!hidden_vp)
1267 wps_display_images(gwps, &skin_viewport->vp);
1268 #endif
1271 #ifdef HAVE_LCD_BITMAP
1272 data->peak_meter_enabled = enable_pm;
1273 #endif
1274 /* Restore the default viewport */
1275 display->set_viewport(NULL);
1277 display->update();
1279 return true;