drop the playlist viewer stack usage by reading straiught off the buffer instead...
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
blob687c611a49b6100a528745f006e470c4a80640c9
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 bool audio_peek_track(struct mp3entry** id3, int offset);
182 static void draw_playlist_viewer_list(struct gui_wps *gwps,
183 struct playlistviewer *viewer)
185 struct wps_state *state = gwps->state;
186 int lines = viewport_get_nb_lines(viewer->vp);
187 int line_height = font_get(viewer->vp->font)->height;
188 int cur_playlist_pos = playlist_get_display_index();
189 int start_item = MAX(0, cur_playlist_pos + viewer->start_offset);
190 int i;
191 struct wps_token token;
192 int x, length, alignment = WPS_TOKEN_ALIGN_LEFT;
194 struct mp3entry *pid3;
195 char buf[MAX_PATH*2], tempbuf[MAX_PATH];
197 gwps->display->set_viewport(viewer->vp);
198 for(i=start_item; (i-start_item)<lines && i<=playlist_amount(); i++)
200 if (i == cur_playlist_pos)
202 pid3 = state->id3;
204 else if (i == cur_playlist_pos+1)
206 pid3 = state->nid3;
208 #if CONFIG_CODEC == SWCODEC
209 else if (i>cur_playlist_pos)
211 if (!audio_peek_track(&pid3, i-cur_playlist_pos))
212 pid3 = NULL;
214 #endif
215 else
217 pid3 = NULL;
220 int line = pid3 ? TRACK_HAS_INFO : TRACK_HAS_NO_INFO;
221 int j = 0, cur_string = 0;
222 char *filename = playlist_peek(i-cur_playlist_pos);
223 unsigned int line_len = 0;
224 buf[0] = '\0';
225 while (j < viewer->lines[line].count && line_len < sizeof(buf))
227 const char *out = NULL;
228 token.type = viewer->lines[line].tokens[j];
229 token.value.i = 0;
230 token.next = false;
231 out = get_id3_token(&token, pid3, tempbuf, sizeof(tempbuf), -1, NULL);
232 if (out)
234 line_len = strlcat(buf, out, sizeof(buf));
235 j++;
236 continue;
238 switch (viewer->lines[line].tokens[j])
240 case WPS_TOKEN_ALIGN_CENTER:
241 case WPS_TOKEN_ALIGN_LEFT:
242 case WPS_TOKEN_ALIGN_LEFT_RTL:
243 case WPS_TOKEN_ALIGN_RIGHT:
244 case WPS_TOKEN_ALIGN_RIGHT_RTL:
245 alignment = viewer->lines[line].tokens[j];
246 tempbuf[0] = '\0';
247 break;
248 case WPS_TOKEN_STRING:
249 case WPS_TOKEN_CHARACTER:
250 snprintf(tempbuf, sizeof(tempbuf), "%s",
251 viewer->lines[line].strings[cur_string]);
252 cur_string++;
253 break;
254 case WPS_TOKEN_PLAYLIST_POSITION:
255 snprintf(tempbuf, sizeof(tempbuf), "%d", i);
256 break;
257 case WPS_TOKEN_FILE_NAME:
258 get_dir(tempbuf, sizeof(tempbuf), filename, 0);
259 break;
260 case WPS_TOKEN_FILE_PATH:
261 snprintf(tempbuf, sizeof(tempbuf), "%s", filename);
262 break;
263 default:
264 tempbuf[0] = '\0';
265 break;
267 if (tempbuf[0])
269 line_len = strlcat(buf, tempbuf, sizeof(buf));
271 j++;
274 int vpwidth = viewer->vp->width;
275 length = gwps->display->getstringsize(buf, NULL, NULL);
276 if (viewer->lines[line].scroll && length >= vpwidth)
278 gwps->display->puts_scroll(0, (i-start_item), buf );
280 else
282 if (length >= vpwidth)
283 x = 0;
284 else
286 switch (alignment)
288 case WPS_TOKEN_ALIGN_CENTER:
289 x = (vpwidth-length)/2;
290 break;
291 case WPS_TOKEN_ALIGN_LEFT_RTL:
292 if (lang_is_rtl() && VP_IS_RTL(viewer->vp))
294 x = vpwidth - length;
295 break;
297 case WPS_TOKEN_ALIGN_LEFT:
298 x = 0;
299 break;
300 case WPS_TOKEN_ALIGN_RIGHT_RTL:
301 if (lang_is_rtl() && VP_IS_RTL(viewer->vp))
303 x = 0;
304 break;
306 case WPS_TOKEN_ALIGN_RIGHT:
307 x = vpwidth - length;
308 break;
309 default:
310 x = 0;
311 break;
314 gwps->display->putsxy(x, (i-start_item)*line_height, buf );
320 /* clears the area where the image was shown */
321 static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
323 if(!gwps)
324 return;
325 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
326 gwps->display->fillrect(img->x, img->y, img->bm.width, img->subimage_height);
327 gwps->display->set_drawmode(DRMODE_SOLID);
330 static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage)
332 struct screen *display = gwps->display;
333 if(img->always_display)
334 display->set_drawmode(DRMODE_FG);
335 else
336 display->set_drawmode(DRMODE_SOLID);
338 #if LCD_DEPTH > 1
339 if(img->bm.format == FORMAT_MONO) {
340 #endif
341 display->mono_bitmap_part(img->bm.data,
342 0, img->subimage_height * subimage,
343 img->bm.width, img->x,
344 img->y, img->bm.width,
345 img->subimage_height);
346 #if LCD_DEPTH > 1
347 } else {
348 display->transparent_bitmap_part((fb_data *)img->bm.data,
349 0, img->subimage_height * subimage,
350 STRIDE(display->screen_type,
351 img->bm.width, img->bm.height),
352 img->x, img->y, img->bm.width,
353 img->subimage_height);
355 #endif
358 static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
360 if(!gwps || !gwps->data || !gwps->display)
361 return;
363 struct wps_data *data = gwps->data;
364 struct screen *display = gwps->display;
365 struct skin_token_list *list = data->images;
367 while (list)
369 struct gui_img *img = (struct gui_img*)list->token->value.data;
370 if (img->loaded)
372 if (img->display >= 0)
374 wps_draw_image(gwps, img, img->display);
376 else if (img->always_display && img->vp == vp)
378 wps_draw_image(gwps, img, 0);
381 list = list->next;
383 #ifdef HAVE_ALBUMART
384 /* now draw the AA */
385 if (data->albumart && data->albumart->vp == vp
386 && data->albumart->draw)
388 draw_album_art(gwps, playback_current_aa_hid(data->playback_aa_slot),
389 false);
390 data->albumart->draw = false;
392 #endif
394 display->set_drawmode(DRMODE_SOLID);
397 #else /* HAVE_LCD_CHARCELL */
399 static bool draw_player_progress(struct gui_wps *gwps)
401 struct wps_state *state = gwps->state;
402 struct screen *display = gwps->display;
403 unsigned char progress_pattern[7];
404 int pos = 0;
405 int i;
407 int elapsed, length;
408 if (LIKELY(state->id3))
410 elapsed = state->id3->elapsed;
411 length = state->id3->length;
413 else
415 elapsed = 0;
416 length = 0;
419 if (length)
420 pos = 36 * (elapsed + state->ff_rewind_count) / length;
422 for (i = 0; i < 7; i++, pos -= 5)
424 if (pos <= 0)
425 progress_pattern[i] = 0x1fu;
426 else if (pos >= 5)
427 progress_pattern[i] = 0x00u;
428 else
429 progress_pattern[i] = 0x1fu >> pos;
432 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
433 return true;
436 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
438 static const unsigned char numbers[10][4] = {
439 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
440 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
441 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
442 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
443 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
444 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
445 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
446 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
447 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
448 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
451 struct wps_state *state = gwps->state;
452 struct screen *display = gwps->display;
453 struct wps_data *data = gwps->data;
454 unsigned char progress_pattern[7];
455 char timestr[10];
456 int time;
457 int time_idx = 0;
458 int pos = 0;
459 int pat_idx = 1;
460 int digit, i, j;
461 bool softchar;
463 int elapsed, length;
464 if (LIKELY(state->id3))
466 elapsed = state->id3->elapsed;
467 length = state->id3->length;
469 else
471 elapsed = 0;
472 length = 0;
475 if (buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
476 return;
478 time = elapsed + state->ff_rewind_count;
479 if (length)
480 pos = 55 * time / length;
482 memset(timestr, 0, sizeof(timestr));
483 format_time(timestr, sizeof(timestr)-2, time);
484 timestr[strlen(timestr)] = ':'; /* always safe */
486 for (i = 0; i < 11; i++, pos -= 5)
488 softchar = false;
489 memset(progress_pattern, 0, sizeof(progress_pattern));
491 if ((digit = timestr[time_idx]))
493 softchar = true;
494 digit -= '0';
496 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
498 memcpy(progress_pattern, numbers[digit], 4);
499 time_idx += 2;
501 else /* tens, shifted right */
503 for (j = 0; j < 4; j++)
504 progress_pattern[j] = numbers[digit][j] >> 1;
506 if (time_idx > 0) /* not the first group, add colon in front */
508 progress_pattern[1] |= 0x10u;
509 progress_pattern[3] |= 0x10u;
511 time_idx++;
514 if (pos >= 5)
515 progress_pattern[5] = progress_pattern[6] = 0x1fu;
518 if (pos > 0 && pos < 5)
520 softchar = true;
521 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
524 if (softchar && pat_idx < 8)
526 display->define_pattern(data->wps_progress_pat[pat_idx],
527 progress_pattern);
528 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
529 pat_idx++;
531 else if (pos <= 0)
532 buf = utf8encode(' ', buf);
533 else
534 buf = utf8encode(0xe115, buf); /* 2/7 _ */
536 *buf = '\0';
539 #endif /* HAVE_LCD_CHARCELL */
541 /* Return the index to the end token for the conditional token at index.
542 The conditional token can be either a start token or a separator
543 (i.e. option) token.
545 static int find_conditional_end(struct wps_data *data, int index)
547 int ret = index;
548 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
549 ret = data->tokens[ret].value.i;
551 /* ret now is the index to the end token for the conditional. */
552 return ret;
555 /* Evaluate the conditional that is at *token_index and return whether a skip
556 has ocurred. *token_index is updated with the new position.
558 static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
560 if (!gwps)
561 return false;
563 struct wps_data *data = gwps->data;
565 int i, cond_end;
566 int cond_index = *token_index;
567 char result[128];
568 const char *value;
569 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
570 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
572 /* treat ?xx<true> constructs as if they had 2 options. */
573 if (num_options < 2)
574 num_options = 2;
576 int intval = num_options;
577 /* get_token_value needs to know the number of options in the enum */
578 value = get_token_value(gwps, &data->tokens[cond_index + 1],
579 result, sizeof(result), &intval);
581 /* intval is now the number of the enum option we want to read,
582 starting from 1. If intval is -1, we check if value is empty. */
583 if (intval == -1)
584 intval = (value && *value) ? 1 : num_options;
585 else if (intval > num_options || intval < 1)
586 intval = num_options;
588 data->tokens[cond_index].value.i = (intval << 8) + num_options;
590 /* skip to the appropriate enum case */
591 int next = cond_index + 2;
592 for (i = 1; i < intval; i++)
594 next = data->tokens[next].value.i;
596 *token_index = next;
598 if (prev_val == intval)
600 /* Same conditional case as previously. Return without clearing the
601 pictures */
602 return false;
605 cond_end = find_conditional_end(data, cond_index + 2);
606 for (i = cond_index + 3; i < cond_end; i++)
608 #ifdef HAVE_LCD_BITMAP
609 /* clear all pictures in the conditional and nested ones */
610 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
611 clear_image_pos(gwps, find_image(data->tokens[i].value.i&0xFF, data));
612 #endif
613 #ifdef HAVE_ALBUMART
614 if (data->albumart && data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
616 draw_album_art(gwps,
617 playback_current_aa_hid(data->playback_aa_slot), true);
618 data->albumart->draw = false;
620 #endif
623 return true;
627 /* Read a (sub)line to the given alignment format buffer.
628 linebuf is the buffer where the data is actually stored.
629 align is the alignment format that'll be used to display the text.
630 The return value indicates whether the line needs to be updated.
632 static bool get_line(struct gui_wps *gwps,
633 struct skin_subline *subline,
634 struct align_pos *align,
635 char *linebuf,
636 int linebuf_size,
637 unsigned refresh_mode)
639 struct wps_data *data = gwps->data;
641 char temp_buf[128];
642 char *buf = linebuf; /* will always point to the writing position */
643 char *linebuf_end = linebuf + linebuf_size - 1;
644 bool update = false;
645 int i;
646 (void)refresh_mode; /* silence warning on charcell */
648 /* alignment-related variables */
649 int cur_align;
650 char* cur_align_start;
651 cur_align_start = buf;
652 cur_align = WPS_ALIGN_LEFT;
653 align->left = NULL;
654 align->center = NULL;
655 align->right = NULL;
656 /* Process all tokens of the desired subline */
657 for (i = subline->first_token_idx;
658 i <= subline->last_token_idx; i++)
660 switch(data->tokens[i].type)
662 case WPS_TOKEN_CONDITIONAL:
663 /* place ourselves in the right conditional case */
664 update |= evaluate_conditional(gwps, &i);
665 break;
667 case WPS_TOKEN_CONDITIONAL_OPTION:
668 /* we've finished in the curent conditional case,
669 skip to the end of the conditional structure */
670 i = find_conditional_end(data, i);
671 break;
673 #ifdef HAVE_LCD_BITMAP
674 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
676 char n = data->tokens[i].value.i & 0xFF;
677 int subimage = data->tokens[i].value.i >> 8;
678 struct gui_img *img = find_image(n, data);
680 if (img && img->loaded)
681 img->display = subimage;
682 break;
684 case WPS_TOKEN_DRAW_INBUILTBAR:
685 gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
686 refresh_mode == WPS_REFRESH_ALL,
687 data->tokens[i].value.data);
688 break;
689 #endif
691 case WPS_TOKEN_ALIGN_LEFT:
692 case WPS_TOKEN_ALIGN_LEFT_RTL:
693 case WPS_TOKEN_ALIGN_CENTER:
694 case WPS_TOKEN_ALIGN_RIGHT:
695 case WPS_TOKEN_ALIGN_RIGHT_RTL:
696 /* remember where the current aligned text started */
697 switch (cur_align)
699 case WPS_ALIGN_LEFT:
700 align->left = cur_align_start;
701 break;
703 case WPS_ALIGN_CENTER:
704 align->center = cur_align_start;
705 break;
707 case WPS_ALIGN_RIGHT:
708 align->right = cur_align_start;
709 break;
711 /* start a new alignment */
712 switch (data->tokens[i].type)
714 case WPS_TOKEN_ALIGN_LEFT:
715 cur_align = WPS_ALIGN_LEFT;
716 break;
717 case WPS_TOKEN_ALIGN_LEFT_RTL:
718 cur_align = lang_is_rtl() ? WPS_ALIGN_RIGHT :
719 WPS_ALIGN_LEFT;
720 break;
721 case WPS_TOKEN_ALIGN_CENTER:
722 cur_align = WPS_ALIGN_CENTER;
723 break;
724 case WPS_TOKEN_ALIGN_RIGHT:
725 cur_align = WPS_ALIGN_RIGHT;
726 break;
727 case WPS_TOKEN_ALIGN_RIGHT_RTL:
728 cur_align = lang_is_rtl() ? WPS_ALIGN_LEFT :
729 WPS_ALIGN_RIGHT;
730 break;
731 default:
732 break;
734 *buf++ = 0;
735 cur_align_start = buf;
736 break;
737 case WPS_VIEWPORT_ENABLE:
739 char label = data->tokens[i].value.i;
740 char temp = VP_DRAW_HIDEABLE;
741 /* viewports are allowed to share id's so find and enable
742 * all of them */
743 struct skin_token_list *list = data->viewports;
744 while (list)
746 struct skin_viewport *vp =
747 (struct skin_viewport *)list->token->value.data;
748 if (vp->label == label)
750 if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
751 temp |= VP_DRAW_WASHIDDEN;
752 vp->hidden_flags = temp;
754 list = list->next;
757 break;
758 #ifdef HAVE_LCD_BITMAP
759 case WPS_VIEWPORT_CUSTOMLIST:
760 draw_playlist_viewer_list(gwps, data->tokens[i].value.data);
761 break;
762 #endif
763 default:
765 /* get the value of the tag and copy it to the buffer */
766 const char *value = get_token_value(gwps, &data->tokens[i],
767 temp_buf, sizeof(temp_buf), NULL);
768 if (value)
770 update = true;
771 while (*value && (buf < linebuf_end))
772 *buf++ = *value++;
774 break;
779 /* close the current alignment */
780 switch (cur_align)
782 case WPS_ALIGN_LEFT:
783 align->left = cur_align_start;
784 break;
786 case WPS_ALIGN_CENTER:
787 align->center = cur_align_start;
788 break;
790 case WPS_ALIGN_RIGHT:
791 align->right = cur_align_start;
792 break;
795 return update;
797 static void get_subline_timeout(struct gui_wps *gwps, struct skin_subline *subline)
799 struct wps_data *data = gwps->data;
800 int i;
801 subline->time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
803 for (i = subline->first_token_idx;
804 i <= subline->last_token_idx; i++)
806 switch(data->tokens[i].type)
808 case WPS_TOKEN_CONDITIONAL:
809 /* place ourselves in the right conditional case */
810 evaluate_conditional(gwps, &i);
811 break;
813 case WPS_TOKEN_CONDITIONAL_OPTION:
814 /* we've finished in the curent conditional case,
815 skip to the end of the conditional structure */
816 i = find_conditional_end(data, i);
817 break;
819 case WPS_TOKEN_SUBLINE_TIMEOUT:
820 subline->time_mult = data->tokens[i].value.i;
821 break;
823 default:
824 break;
829 /* Calculates which subline should be displayed for the specified line
830 Returns true iff the subline must be refreshed */
831 static bool update_curr_subline(struct gui_wps *gwps, struct skin_line *line)
833 /* shortcut this whole thing if we need to reset the line completly */
834 if (line->curr_subline == NULL)
836 line->subline_expire_time = current_tick;
837 line->curr_subline = &line->sublines;
838 if (!line->curr_subline->next)
840 line->subline_expire_time += 100*HZ;
842 else
844 get_subline_timeout(gwps, line->curr_subline);
845 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
847 return true;
849 /* if time to advance to next sub-line */
850 if (TIME_AFTER(current_tick, line->subline_expire_time - 1))
852 /* if there is only one subline, there is no need to search for a new one */
853 if (&line->sublines == line->curr_subline &&
854 line->curr_subline->next == NULL)
856 line->subline_expire_time += 100 * HZ;
857 return false;
859 if (line->curr_subline->next)
860 line->curr_subline = line->curr_subline->next;
861 else
862 line->curr_subline = &line->sublines;
863 get_subline_timeout(gwps, line->curr_subline);
864 line->subline_expire_time = current_tick + TIMEOUT_UNIT*line->curr_subline->time_mult;
865 return true;
867 return false;
870 /* Display a line appropriately according to its alignment format.
871 format_align contains the text, separated between left, center and right.
872 line is the index of the line on the screen.
873 scroll indicates whether the line is a scrolling one or not.
875 static void write_line(struct screen *display,
876 struct align_pos *format_align,
877 int line,
878 bool scroll)
880 int left_width = 0, left_xpos;
881 int center_width = 0, center_xpos;
882 int right_width = 0, right_xpos;
883 int ypos;
884 int space_width;
885 int string_height;
886 int scroll_width;
888 /* calculate different string sizes and positions */
889 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
890 if (format_align->left != 0) {
891 display->getstringsize((unsigned char *)format_align->left,
892 &left_width, &string_height);
895 if (format_align->right != 0) {
896 display->getstringsize((unsigned char *)format_align->right,
897 &right_width, &string_height);
900 if (format_align->center != 0) {
901 display->getstringsize((unsigned char *)format_align->center,
902 &center_width, &string_height);
905 left_xpos = 0;
906 right_xpos = (display->getwidth() - right_width);
907 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
909 scroll_width = display->getwidth() - left_xpos;
911 /* Checks for overlapping strings.
912 If needed the overlapping strings will be merged, separated by a
913 space */
915 /* CASE 1: left and centered string overlap */
916 /* there is a left string, need to merge left and center */
917 if ((left_width != 0 && center_width != 0) &&
918 (left_xpos + left_width + space_width > center_xpos)) {
919 /* replace the former separator '\0' of left and
920 center string with a space */
921 *(--format_align->center) = ' ';
922 /* calculate the new width and position of the merged string */
923 left_width = left_width + space_width + center_width;
924 /* there is no centered string anymore */
925 center_width = 0;
927 /* there is no left string, move center to left */
928 if ((left_width == 0 && center_width != 0) &&
929 (left_xpos + left_width > center_xpos)) {
930 /* move the center string to the left string */
931 format_align->left = format_align->center;
932 /* calculate the new width and position of the string */
933 left_width = center_width;
934 /* there is no centered string anymore */
935 center_width = 0;
938 /* CASE 2: centered and right string overlap */
939 /* there is a right string, need to merge center and right */
940 if ((center_width != 0 && right_width != 0) &&
941 (center_xpos + center_width + space_width > right_xpos)) {
942 /* replace the former separator '\0' of center and
943 right string with a space */
944 *(--format_align->right) = ' ';
945 /* move the center string to the right after merge */
946 format_align->right = format_align->center;
947 /* calculate the new width and position of the merged string */
948 right_width = center_width + space_width + right_width;
949 right_xpos = (display->getwidth() - right_width);
950 /* there is no centered string anymore */
951 center_width = 0;
953 /* there is no right string, move center to right */
954 if ((center_width != 0 && right_width == 0) &&
955 (center_xpos + center_width > right_xpos)) {
956 /* move the center string to the right string */
957 format_align->right = format_align->center;
958 /* calculate the new width and position of the string */
959 right_width = center_width;
960 right_xpos = (display->getwidth() - right_width);
961 /* there is no centered string anymore */
962 center_width = 0;
965 /* CASE 3: left and right overlap
966 There is no center string anymore, either there never
967 was one or it has been merged in case 1 or 2 */
968 /* there is a left string, need to merge left and right */
969 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
970 (left_xpos + left_width + space_width > right_xpos)) {
971 /* replace the former separator '\0' of left and
972 right string with a space */
973 *(--format_align->right) = ' ';
974 /* calculate the new width and position of the string */
975 left_width = left_width + space_width + right_width;
976 /* there is no right string anymore */
977 right_width = 0;
979 /* there is no left string, move right to left */
980 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
981 (left_width > right_xpos)) {
982 /* move the right string to the left string */
983 format_align->left = format_align->right;
984 /* calculate the new width and position of the string */
985 left_width = right_width;
986 /* there is no right string anymore */
987 right_width = 0;
990 ypos = (line * string_height);
993 if (scroll && ((left_width > scroll_width) ||
994 (center_width > scroll_width) ||
995 (right_width > scroll_width)))
997 display->puts_scroll(0, line,
998 (unsigned char *)format_align->left);
1000 else
1002 #ifdef HAVE_LCD_BITMAP
1003 /* clear the line first */
1004 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1005 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
1006 display->set_drawmode(DRMODE_SOLID);
1007 #endif
1009 /* Nasty hack: we output an empty scrolling string,
1010 which will reset the scroller for that line */
1011 display->puts_scroll(0, line, (unsigned char *)"");
1013 /* print aligned strings */
1014 if (left_width != 0)
1016 display->putsxy(left_xpos, ypos,
1017 (unsigned char *)format_align->left);
1019 if (center_width != 0)
1021 display->putsxy(center_xpos, ypos,
1022 (unsigned char *)format_align->center);
1024 if (right_width != 0)
1026 display->putsxy(right_xpos, ypos,
1027 (unsigned char *)format_align->right);
1032 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
1034 struct wps_data *data = gwps->data;
1035 struct screen *display = gwps->display;
1037 if (!data || !display || !gwps->state)
1038 return false;
1040 unsigned flags;
1041 char linebuf[MAX_PATH];
1043 struct align_pos align;
1044 align.left = NULL;
1045 align.center = NULL;
1046 align.right = NULL;
1049 struct skin_token_list *viewport_list;
1051 bool update_line, new_subline_refresh;
1053 #ifdef HAVE_LCD_BITMAP
1055 /* to find out wether the peak meter is enabled we
1056 assume it wasn't until we find a line that contains
1057 the peak meter. We can't use peak_meter_enabled itself
1058 because that would mean to turn off the meter thread
1059 temporarily. (That shouldn't matter unless yield
1060 or sleep is called but who knows...)
1062 bool enable_pm = false;
1064 #endif
1066 /* reset to first subline if refresh all flag is set */
1067 if (refresh_mode == WPS_REFRESH_ALL)
1069 struct skin_line *line;
1070 struct skin_viewport *skin_viewport = find_viewport(VP_DEFAULT_LABEL, data);
1072 if (!(skin_viewport->hidden_flags & VP_NEVER_VISIBLE))
1074 display->set_viewport(&skin_viewport->vp);
1075 display->clear_viewport();
1078 for (viewport_list = data->viewports;
1079 viewport_list; viewport_list = viewport_list->next)
1081 skin_viewport =
1082 (struct skin_viewport *)viewport_list->token->value.data;
1083 for(line = skin_viewport->lines; line; line = line->next)
1085 line->curr_subline = NULL;
1090 #ifdef HAVE_LCD_CHARCELLS
1091 int i;
1092 for (i = 0; i < 8; i++)
1094 if (data->wps_progress_pat[i] == 0)
1095 data->wps_progress_pat[i] = display->get_locked_pattern();
1097 #endif
1099 /* disable any viewports which are conditionally displayed.
1100 * If we are only refreshing the peak meter then don't change the viewport
1101 * enabled flags as this will stop scrolling. viewports cant be
1102 * toggled in this refresh mode anyway (FS#10215)*/
1103 if (refresh_mode != WPS_REFRESH_PEAK_METER)
1105 for (viewport_list = data->viewports;
1106 viewport_list; viewport_list = viewport_list->next)
1108 struct skin_viewport *skin_viewport =
1109 (struct skin_viewport *)viewport_list->token->value.data;
1110 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1112 continue;
1114 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
1116 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
1117 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1118 else
1119 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
1123 int viewport_count = 0;
1124 for (viewport_list = data->viewports;
1125 viewport_list; viewport_list = viewport_list->next, viewport_count++)
1127 struct skin_viewport *skin_viewport =
1128 (struct skin_viewport *)viewport_list->token->value.data;
1129 unsigned vp_refresh_mode = refresh_mode;
1131 display->set_viewport(&skin_viewport->vp);
1133 int hidden_vp = 0;
1135 #ifdef HAVE_LCD_BITMAP
1136 /* Set images to not to be displayed */
1137 struct skin_token_list *imglist = data->images;
1138 while (imglist)
1140 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
1141 img->display = -1;
1142 imglist = imglist->next;
1144 #endif
1145 /* dont redraw the viewport if its disabled */
1146 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1147 { /* don't draw anything into this one */
1148 vp_refresh_mode = 0; hidden_vp = true;
1150 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
1152 if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
1153 display->scroll_stop(&skin_viewport->vp);
1154 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1155 continue;
1157 else if (((skin_viewport->hidden_flags&
1158 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
1159 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
1161 vp_refresh_mode = WPS_REFRESH_ALL;
1162 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
1165 if (vp_refresh_mode == WPS_REFRESH_ALL)
1167 display->clear_viewport();
1170 /* loop over the lines for this viewport */
1171 struct skin_line *line;
1172 int line_count = 0;
1174 for (line = skin_viewport->lines; line; line = line->next, line_count++)
1176 struct skin_subline *subline;
1177 memset(linebuf, 0, sizeof(linebuf));
1178 update_line = false;
1180 /* get current subline for the line */
1181 new_subline_refresh = update_curr_subline(gwps, line);
1182 subline = line->curr_subline;
1183 flags = line->curr_subline->line_type;
1185 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1186 || new_subline_refresh || hidden_vp)
1188 /* get_line tells us if we need to update the line */
1189 update_line = get_line(gwps, subline, &align,
1190 linebuf, sizeof(linebuf), vp_refresh_mode);
1192 #ifdef HAVE_LCD_BITMAP
1193 /* peakmeter */
1194 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1196 /* the peakmeter should be alone on its line */
1197 update_line = false;
1199 int h = font_get(skin_viewport->vp.font)->height;
1200 int peak_meter_y = line_count* h;
1202 /* The user might decide to have the peak meter in the last
1203 line so that it is only displayed if no status bar is
1204 visible. If so we neither want do draw nor enable the
1205 peak meter. */
1206 if (peak_meter_y + h <= skin_viewport->vp.y+skin_viewport->vp.height) {
1207 /* found a line with a peak meter -> remember that we must
1208 enable it later */
1209 enable_pm = true;
1210 peak_meter_enabled = true;
1211 peak_meter_screen(gwps->display, 0, peak_meter_y,
1212 MIN(h, skin_viewport->vp.y+skin_viewport->vp.height - peak_meter_y));
1214 else
1216 peak_meter_enabled = false;
1220 #else /* HAVE_LCD_CHARCELL */
1222 /* progressbar */
1223 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1225 if (data->full_line_progressbar)
1226 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1227 else
1228 draw_player_progress(gwps);
1230 #endif
1232 if (update_line && !hidden_vp &&
1233 /* conditionals clear the line which means if the %Vd is put into the default
1234 viewport there will be a blank line.
1235 To get around this we dont allow any actual drawing to happen in the
1236 deault vp if other vp's are defined */
1237 ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
1238 !viewport_list->next))
1240 if (flags & WPS_REFRESH_SCROLL)
1242 /* if the line is a scrolling one we don't want to update
1243 too often, so that it has the time to scroll */
1244 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1245 write_line(display, &align, line_count, true);
1247 else
1248 write_line(display, &align, line_count, false);
1251 #ifdef HAVE_LCD_BITMAP
1252 /* progressbar */
1253 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1255 struct skin_token_list *bar = gwps->data->progressbars;
1256 while (bar)
1258 struct progressbar *thisbar = (struct progressbar*)bar->token->value.data;
1259 if (thisbar->vp == &skin_viewport->vp)
1261 draw_progressbar(gwps, thisbar);
1263 bar = bar->next;
1266 /* Now display any images in this viewport */
1267 if (!hidden_vp)
1268 wps_display_images(gwps, &skin_viewport->vp);
1269 #endif
1272 #ifdef HAVE_LCD_BITMAP
1273 data->peak_meter_enabled = enable_pm;
1274 #endif
1275 /* Restore the default viewport */
1276 display->set_viewport(NULL);
1278 display->update();
1280 return true;