Make battery level (%bl) work with the bmp bars drawer like %pb and %pv
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
bloba0b34976836bdd875a3f4988d6295e7af568e6ae
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 #include "powermgmt.h"
31 #ifdef DEBUG
32 #include "debug.h"
33 #endif
34 #include "abrepeat.h"
35 #include "lang.h"
36 #include "language.h"
37 #include "statusbar.h"
38 #include "settings.h"
39 #include "scrollbar.h"
40 #include "screen_access.h"
41 #include "playlist.h"
42 #include "audio.h"
44 #ifdef HAVE_LCD_BITMAP
45 #include "peakmeter.h"
46 /* Image stuff */
47 #include "bmp.h"
48 #ifdef HAVE_ALBUMART
49 #include "albumart.h"
50 #endif
51 #endif
53 #include "cuesheet.h"
54 #if CONFIG_CODEC == SWCODEC
55 #include "playback.h"
56 #endif
57 #include "backdrop.h"
58 #include "viewport.h"
61 #include "wps_internals.h"
62 #include "skin_engine.h"
63 #include "statusbar-skinned.h"
65 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode);
67 /* update a skinned screen, update_type is WPS_REFRESH_* values.
68 * Usually it should only be WPS_REFRESH_NON_STATIC
69 * A full update will be done if required (state.do_full_update == true)
71 bool skin_update(struct gui_wps *gwps, unsigned int update_type)
73 bool retval;
74 /* This maybe shouldnt be here, but while the skin is only used to
75 * display the music screen this is better than whereever we are being
76 * called from. This is also safe for skined screen which dont use the id3 */
77 struct mp3entry *id3 = gwps->state->id3;
78 bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
79 gwps->sync_data->do_full_update |= cuesheet_update;
81 retval = skin_redraw(gwps, gwps->sync_data->do_full_update ?
82 WPS_REFRESH_ALL : update_type);
83 return retval;
86 #ifdef HAVE_LCD_BITMAP
88 void skin_statusbar_changed(struct gui_wps *skin)
90 if (!skin)
91 return;
92 struct wps_data *data = skin->data;
93 const struct screen *display = skin->display;
94 const int screen = display->screen_type;
96 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, data)->vp;
97 viewport_set_defaults(vp, screen);
99 if (data->wps_sb_tag)
100 { /* fix up the default viewport */
101 if (data->show_sb_on_wps)
103 if (statusbar_position(screen) != STATUSBAR_OFF)
104 return; /* vp is fixed already */
106 vp->y = STATUSBAR_HEIGHT;
107 vp->height = display->lcdheight - STATUSBAR_HEIGHT;
109 else
111 if (statusbar_position(screen) == STATUSBAR_OFF)
112 return; /* vp is fixed already */
113 vp->y = vp->x = 0;
114 vp->height = display->lcdheight;
115 vp->width = display->lcdwidth;
120 static void draw_progressbar(struct gui_wps *gwps,
121 struct progressbar *pb)
123 struct screen *display = gwps->display;
124 struct viewport *vp = pb->vp;
125 struct wps_state *state = gwps->state;
126 struct mp3entry *id3 = state->id3;
127 int y = pb->y, height = pb->height;
128 unsigned long length, elapsed;
130 if (height < 0)
131 height = font_get(vp->font)->height;
133 if (y < 0)
135 int line_height = font_get(vp->font)->height;
136 /* center the pb in the line, but only if the line is higher than the pb */
137 int center = (line_height-height)/2;
138 /* if Y was not set calculate by font height,Y is -line_number-1 */
139 y = (-y -1)*line_height + (0 > center ? 0 : center);
142 if (pb->type == WPS_TOKEN_VOLUMEBAR)
144 int minvol = sound_min(SOUND_VOLUME);
145 int maxvol = sound_max(SOUND_VOLUME);
146 length = maxvol-minvol;
147 elapsed = global_settings.volume-minvol;
149 else if (pb->type == WPS_TOKEN_BATTERY_PERCENTBAR)
151 length = 100;
152 elapsed = battery_level();
154 else if (id3 && id3->length)
156 length = id3->length;
157 elapsed = id3->elapsed + state->ff_rewind_count;
159 else
161 length = 1;
162 elapsed = 0;
165 if (pb->have_bitmap_pb)
166 gui_bitmap_scrollbar_draw(display, pb->bm,
167 pb->x, y, pb->width, pb->bm.height,
168 length, 0, elapsed, HORIZONTAL);
169 else
170 gui_scrollbar_draw(display, pb->x, y, pb->width, height,
171 length, 0, elapsed, HORIZONTAL);
173 if (pb->type == WPS_TOKEN_PROGRESSBAR && id3 && id3->length)
175 #ifdef AB_REPEAT_ENABLE
176 if (ab_repeat_mode_enabled())
177 ab_draw_markers(display, id3->length,
178 pb->x, y, pb->width, height);
179 #endif
181 if (id3->cuesheet)
182 cue_draw_markers(display, id3->cuesheet, id3->length,
183 pb->x, y+1, pb->width, height-2);
187 static void draw_playlist_viewer_list(struct gui_wps *gwps,
188 struct playlistviewer *viewer)
190 struct wps_state *state = gwps->state;
191 int lines = viewport_get_nb_lines(viewer->vp);
192 int line_height = font_get(viewer->vp->font)->height;
193 int cur_playlist_pos = playlist_get_display_index();
194 int start_item = MAX(0, cur_playlist_pos + viewer->start_offset);
195 int i;
196 struct wps_token token;
197 int x, length, alignment = WPS_TOKEN_ALIGN_LEFT;
199 struct mp3entry *pid3;
200 char buf[MAX_PATH*2], tempbuf[MAX_PATH];
202 gwps->display->set_viewport(viewer->vp);
203 for(i=start_item; (i-start_item)<lines && i<=playlist_amount(); i++)
205 if (i == cur_playlist_pos)
207 pid3 = state->id3;
209 else if (i == cur_playlist_pos+1)
211 pid3 = state->nid3;
213 #if CONFIG_CODEC == SWCODEC
214 else if (i>cur_playlist_pos)
216 if (!audio_peek_track(&pid3, i-cur_playlist_pos))
217 pid3 = NULL;
219 #endif
220 else
222 pid3 = NULL;
225 int line = pid3 ? TRACK_HAS_INFO : TRACK_HAS_NO_INFO;
226 int j = 0, cur_string = 0;
227 char *filename = playlist_peek(i-cur_playlist_pos);
228 unsigned int line_len = 0;
229 buf[0] = '\0';
230 while (j < viewer->lines[line].count && line_len < sizeof(buf))
232 const char *out = NULL;
233 token.type = viewer->lines[line].tokens[j];
234 token.value.i = 0;
235 token.next = false;
236 out = get_id3_token(&token, pid3, tempbuf, sizeof(tempbuf), -1, NULL);
237 if (out)
239 line_len = strlcat(buf, out, sizeof(buf));
240 j++;
241 continue;
243 switch (viewer->lines[line].tokens[j])
245 case WPS_TOKEN_ALIGN_CENTER:
246 case WPS_TOKEN_ALIGN_LEFT:
247 case WPS_TOKEN_ALIGN_LEFT_RTL:
248 case WPS_TOKEN_ALIGN_RIGHT:
249 case WPS_TOKEN_ALIGN_RIGHT_RTL:
250 alignment = viewer->lines[line].tokens[j];
251 tempbuf[0] = '\0';
252 break;
253 case WPS_TOKEN_STRING:
254 case WPS_TOKEN_CHARACTER:
255 snprintf(tempbuf, sizeof(tempbuf), "%s",
256 viewer->lines[line].strings[cur_string]);
257 cur_string++;
258 break;
259 case WPS_TOKEN_PLAYLIST_POSITION:
260 snprintf(tempbuf, sizeof(tempbuf), "%d", i);
261 break;
262 case WPS_TOKEN_FILE_NAME:
263 get_dir(tempbuf, sizeof(tempbuf), filename, 0);
264 break;
265 case WPS_TOKEN_FILE_PATH:
266 snprintf(tempbuf, sizeof(tempbuf), "%s", filename);
267 break;
268 default:
269 tempbuf[0] = '\0';
270 break;
272 if (tempbuf[0])
274 line_len = strlcat(buf, tempbuf, sizeof(buf));
276 j++;
279 int vpwidth = viewer->vp->width;
280 length = gwps->display->getstringsize(buf, NULL, NULL);
281 if (viewer->lines[line].scroll && length >= vpwidth)
283 gwps->display->puts_scroll(0, (i-start_item), buf );
285 else
287 if (length >= vpwidth)
288 x = 0;
289 else
291 switch (alignment)
293 case WPS_TOKEN_ALIGN_CENTER:
294 x = (vpwidth-length)/2;
295 break;
296 case WPS_TOKEN_ALIGN_LEFT_RTL:
297 if (lang_is_rtl() && VP_IS_RTL(viewer->vp))
299 x = vpwidth - length;
300 break;
302 case WPS_TOKEN_ALIGN_LEFT:
303 x = 0;
304 break;
305 case WPS_TOKEN_ALIGN_RIGHT_RTL:
306 if (lang_is_rtl() && VP_IS_RTL(viewer->vp))
308 x = 0;
309 break;
311 case WPS_TOKEN_ALIGN_RIGHT:
312 x = vpwidth - length;
313 break;
314 default:
315 x = 0;
316 break;
319 gwps->display->putsxy(x, (i-start_item)*line_height, buf );
325 /* clears the area where the image was shown */
326 static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
328 if(!gwps)
329 return;
330 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
331 gwps->display->fillrect(img->x, img->y, img->bm.width, img->subimage_height);
332 gwps->display->set_drawmode(DRMODE_SOLID);
335 static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage)
337 struct screen *display = gwps->display;
338 if(img->always_display)
339 display->set_drawmode(DRMODE_FG);
340 else
341 display->set_drawmode(DRMODE_SOLID);
343 #if LCD_DEPTH > 1
344 if(img->bm.format == FORMAT_MONO) {
345 #endif
346 display->mono_bitmap_part(img->bm.data,
347 0, img->subimage_height * subimage,
348 img->bm.width, img->x,
349 img->y, img->bm.width,
350 img->subimage_height);
351 #if LCD_DEPTH > 1
352 } else {
353 display->transparent_bitmap_part((fb_data *)img->bm.data,
354 0, img->subimage_height * subimage,
355 STRIDE(display->screen_type,
356 img->bm.width, img->bm.height),
357 img->x, img->y, img->bm.width,
358 img->subimage_height);
360 #endif
363 static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
365 if(!gwps || !gwps->data || !gwps->display)
366 return;
368 struct wps_data *data = gwps->data;
369 struct screen *display = gwps->display;
370 struct skin_token_list *list = data->images;
372 while (list)
374 struct gui_img *img = (struct gui_img*)list->token->value.data;
375 if (img->loaded)
377 if (img->display >= 0)
379 wps_draw_image(gwps, img, img->display);
381 else if (img->always_display && img->vp == vp)
383 wps_draw_image(gwps, img, 0);
386 list = list->next;
388 #ifdef HAVE_ALBUMART
389 /* now draw the AA */
390 if (data->albumart && data->albumart->vp == vp
391 && data->albumart->draw)
393 draw_album_art(gwps, playback_current_aa_hid(data->playback_aa_slot),
394 false);
395 data->albumart->draw = false;
397 #endif
399 display->set_drawmode(DRMODE_SOLID);
402 #else /* HAVE_LCD_CHARCELL */
404 static bool draw_player_progress(struct gui_wps *gwps)
406 struct wps_state *state = gwps->state;
407 struct screen *display = gwps->display;
408 unsigned char progress_pattern[7];
409 int pos = 0;
410 int i;
412 int elapsed, length;
413 if (LIKELY(state->id3))
415 elapsed = state->id3->elapsed;
416 length = state->id3->length;
418 else
420 elapsed = 0;
421 length = 0;
424 if (length)
425 pos = 36 * (elapsed + state->ff_rewind_count) / length;
427 for (i = 0; i < 7; i++, pos -= 5)
429 if (pos <= 0)
430 progress_pattern[i] = 0x1fu;
431 else if (pos >= 5)
432 progress_pattern[i] = 0x00u;
433 else
434 progress_pattern[i] = 0x1fu >> pos;
437 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
438 return true;
441 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
443 static const unsigned char numbers[10][4] = {
444 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
445 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
446 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
447 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
448 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
449 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
450 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
451 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
452 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
453 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
456 struct wps_state *state = gwps->state;
457 struct screen *display = gwps->display;
458 struct wps_data *data = gwps->data;
459 unsigned char progress_pattern[7];
460 char timestr[10];
461 int time;
462 int time_idx = 0;
463 int pos = 0;
464 int pat_idx = 1;
465 int digit, i, j;
466 bool softchar;
468 int elapsed, length;
469 if (LIKELY(state->id3))
471 elapsed = state->id3->elapsed;
472 length = state->id3->length;
474 else
476 elapsed = 0;
477 length = 0;
480 if (buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
481 return;
483 time = elapsed + state->ff_rewind_count;
484 if (length)
485 pos = 55 * time / length;
487 memset(timestr, 0, sizeof(timestr));
488 format_time(timestr, sizeof(timestr)-2, time);
489 timestr[strlen(timestr)] = ':'; /* always safe */
491 for (i = 0; i < 11; i++, pos -= 5)
493 softchar = false;
494 memset(progress_pattern, 0, sizeof(progress_pattern));
496 if ((digit = timestr[time_idx]))
498 softchar = true;
499 digit -= '0';
501 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
503 memcpy(progress_pattern, numbers[digit], 4);
504 time_idx += 2;
506 else /* tens, shifted right */
508 for (j = 0; j < 4; j++)
509 progress_pattern[j] = numbers[digit][j] >> 1;
511 if (time_idx > 0) /* not the first group, add colon in front */
513 progress_pattern[1] |= 0x10u;
514 progress_pattern[3] |= 0x10u;
516 time_idx++;
519 if (pos >= 5)
520 progress_pattern[5] = progress_pattern[6] = 0x1fu;
523 if (pos > 0 && pos < 5)
525 softchar = true;
526 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
529 if (softchar && pat_idx < 8)
531 display->define_pattern(data->wps_progress_pat[pat_idx],
532 progress_pattern);
533 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
534 pat_idx++;
536 else if (pos <= 0)
537 buf = utf8encode(' ', buf);
538 else
539 buf = utf8encode(0xe115, buf); /* 2/7 _ */
541 *buf = '\0';
544 #endif /* HAVE_LCD_CHARCELL */
546 /* Return the index to the end token for the conditional token at index.
547 The conditional token can be either a start token or a separator
548 (i.e. option) token.
550 static int find_conditional_end(struct wps_data *data, int index)
552 int ret = index;
553 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
554 ret = data->tokens[ret].value.i;
556 /* ret now is the index to the end token for the conditional. */
557 return ret;
560 /* Evaluate the conditional that is at *token_index and return whether a skip
561 has ocurred. *token_index is updated with the new position.
563 static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
565 if (!gwps)
566 return false;
568 struct wps_data *data = gwps->data;
570 int i, cond_end;
571 int cond_index = *token_index;
572 char result[128];
573 const char *value;
574 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
575 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
577 /* treat ?xx<true> constructs as if they had 2 options. */
578 if (num_options < 2)
579 num_options = 2;
581 int intval = num_options;
582 /* get_token_value needs to know the number of options in the enum */
583 value = get_token_value(gwps, &data->tokens[cond_index + 1],
584 result, sizeof(result), &intval);
586 /* intval is now the number of the enum option we want to read,
587 starting from 1. If intval is -1, we check if value is empty. */
588 if (intval == -1)
589 intval = (value && *value) ? 1 : num_options;
590 else if (intval > num_options || intval < 1)
591 intval = num_options;
593 data->tokens[cond_index].value.i = (intval << 8) + num_options;
595 /* skip to the appropriate enum case */
596 int next = cond_index + 2;
597 for (i = 1; i < intval; i++)
599 next = data->tokens[next].value.i;
601 *token_index = next;
603 if (prev_val == intval)
605 /* Same conditional case as previously. Return without clearing the
606 pictures */
607 return false;
610 cond_end = find_conditional_end(data, cond_index + 2);
611 for (i = cond_index + 3; i < cond_end; i++)
613 #ifdef HAVE_LCD_BITMAP
614 /* clear all pictures in the conditional and nested ones */
615 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
616 clear_image_pos(gwps, find_image(data->tokens[i].value.i&0xFF, data));
617 else if (data->tokens[i].type == WPS_TOKEN_VOLUMEBAR ||
618 data->tokens[i].type == WPS_TOKEN_PROGRESSBAR ||
619 data->tokens[i].type == WPS_TOKEN_BATTERY_PERCENTBAR )
621 struct progressbar *bar = (struct progressbar*)data->tokens[i].value.data;
622 bar->draw = false;
624 #endif
625 #ifdef HAVE_ALBUMART
626 if (data->albumart && data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
628 draw_album_art(gwps,
629 playback_current_aa_hid(data->playback_aa_slot), true);
630 data->albumart->draw = false;
632 #endif
635 return true;
639 /* Read a (sub)line to the given alignment format buffer.
640 linebuf is the buffer where the data is actually stored.
641 align is the alignment format that'll be used to display the text.
642 The return value indicates whether the line needs to be updated.
644 static bool get_line(struct gui_wps *gwps,
645 struct skin_subline *subline,
646 struct align_pos *align,
647 char *linebuf,
648 int linebuf_size,
649 unsigned refresh_mode)
651 struct wps_data *data = gwps->data;
653 char temp_buf[128];
654 char *buf = linebuf; /* will always point to the writing position */
655 char *linebuf_end = linebuf + linebuf_size - 1;
656 bool update = false;
657 int i;
658 (void)refresh_mode; /* silence warning on charcell */
660 /* alignment-related variables */
661 int cur_align;
662 char* cur_align_start;
663 cur_align_start = buf;
664 cur_align = WPS_ALIGN_LEFT;
665 align->left = NULL;
666 align->center = NULL;
667 align->right = NULL;
668 /* Process all tokens of the desired subline */
669 for (i = subline->first_token_idx;
670 i <= subline->last_token_idx; i++)
672 switch(data->tokens[i].type)
674 case WPS_TOKEN_CONDITIONAL:
675 /* place ourselves in the right conditional case */
676 update |= evaluate_conditional(gwps, &i);
677 break;
679 case WPS_TOKEN_CONDITIONAL_OPTION:
680 /* we've finished in the curent conditional case,
681 skip to the end of the conditional structure */
682 i = find_conditional_end(data, i);
683 break;
685 #ifdef HAVE_LCD_BITMAP
686 case WPS_TOKEN_VOLUMEBAR:
687 case WPS_TOKEN_BATTERY_PERCENTBAR:
688 case WPS_TOKEN_PROGRESSBAR:
690 struct progressbar *bar = (struct progressbar*)data->tokens[i].value.data;
691 bar->draw = true;
693 break;
694 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
696 char n = data->tokens[i].value.i & 0xFF;
697 int subimage = data->tokens[i].value.i >> 8;
698 struct gui_img *img = find_image(n, data);
700 if (img && img->loaded)
701 img->display = subimage;
702 break;
704 case WPS_TOKEN_DRAW_INBUILTBAR:
705 gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
706 refresh_mode == WPS_REFRESH_ALL,
707 data->tokens[i].value.data);
708 break;
709 #endif
711 case WPS_TOKEN_ALIGN_LEFT:
712 case WPS_TOKEN_ALIGN_LEFT_RTL:
713 case WPS_TOKEN_ALIGN_CENTER:
714 case WPS_TOKEN_ALIGN_RIGHT:
715 case WPS_TOKEN_ALIGN_RIGHT_RTL:
716 /* remember where the current aligned text started */
717 switch (cur_align)
719 case WPS_ALIGN_LEFT:
720 align->left = cur_align_start;
721 break;
723 case WPS_ALIGN_CENTER:
724 align->center = cur_align_start;
725 break;
727 case WPS_ALIGN_RIGHT:
728 align->right = cur_align_start;
729 break;
731 /* start a new alignment */
732 switch (data->tokens[i].type)
734 case WPS_TOKEN_ALIGN_LEFT:
735 cur_align = WPS_ALIGN_LEFT;
736 break;
737 case WPS_TOKEN_ALIGN_LEFT_RTL:
738 cur_align = lang_is_rtl() ? WPS_ALIGN_RIGHT :
739 WPS_ALIGN_LEFT;
740 break;
741 case WPS_TOKEN_ALIGN_CENTER:
742 cur_align = WPS_ALIGN_CENTER;
743 break;
744 case WPS_TOKEN_ALIGN_RIGHT:
745 cur_align = WPS_ALIGN_RIGHT;
746 break;
747 case WPS_TOKEN_ALIGN_RIGHT_RTL:
748 cur_align = lang_is_rtl() ? WPS_ALIGN_LEFT :
749 WPS_ALIGN_RIGHT;
750 break;
751 default:
752 break;
754 *buf++ = 0;
755 cur_align_start = buf;
756 break;
757 case WPS_VIEWPORT_ENABLE:
759 char label = data->tokens[i].value.i;
760 char temp = VP_DRAW_HIDEABLE;
761 /* viewports are allowed to share id's so find and enable
762 * all of them */
763 struct skin_token_list *list = data->viewports;
764 while (list)
766 struct skin_viewport *vp =
767 (struct skin_viewport *)list->token->value.data;
768 if (vp->label == label)
770 if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
771 temp |= VP_DRAW_WASHIDDEN;
772 vp->hidden_flags = temp;
774 list = list->next;
777 break;
778 #ifdef HAVE_LCD_BITMAP
779 case WPS_VIEWPORT_CUSTOMLIST:
780 draw_playlist_viewer_list(gwps, data->tokens[i].value.data);
781 break;
782 #endif
783 default:
785 /* get the value of the tag and copy it to the buffer */
786 const char *value = get_token_value(gwps, &data->tokens[i],
787 temp_buf, sizeof(temp_buf), NULL);
788 if (value)
790 update = true;
791 while (*value && (buf < linebuf_end))
792 *buf++ = *value++;
794 break;
799 /* close the current alignment */
800 switch (cur_align)
802 case WPS_ALIGN_LEFT:
803 align->left = cur_align_start;
804 break;
806 case WPS_ALIGN_CENTER:
807 align->center = cur_align_start;
808 break;
810 case WPS_ALIGN_RIGHT:
811 align->right = cur_align_start;
812 break;
815 return update;
817 static void get_subline_timeout(struct gui_wps *gwps, struct skin_subline *subline)
819 struct wps_data *data = gwps->data;
820 int i;
821 subline->time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
823 for (i = subline->first_token_idx;
824 i <= subline->last_token_idx; i++)
826 switch(data->tokens[i].type)
828 case WPS_TOKEN_CONDITIONAL:
829 /* place ourselves in the right conditional case */
830 evaluate_conditional(gwps, &i);
831 break;
833 case WPS_TOKEN_CONDITIONAL_OPTION:
834 /* we've finished in the curent conditional case,
835 skip to the end of the conditional structure */
836 i = find_conditional_end(data, i);
837 break;
839 case WPS_TOKEN_SUBLINE_TIMEOUT:
840 subline->time_mult = data->tokens[i].value.i;
841 break;
843 default:
844 break;
849 /* Calculates which subline should be displayed for the specified line
850 Returns true iff the subline must be refreshed */
851 static bool update_curr_subline(struct gui_wps *gwps, struct skin_line *line)
853 /* shortcut this whole thing if we need to reset the line completly */
854 if (line->curr_subline == NULL)
856 line->subline_expire_time = current_tick;
857 line->curr_subline = &line->sublines;
858 if (!line->curr_subline->next)
860 line->subline_expire_time += 100*HZ;
862 else
864 get_subline_timeout(gwps, line->curr_subline);
865 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
867 return true;
869 /* if time to advance to next sub-line */
870 if (TIME_AFTER(current_tick, line->subline_expire_time - 1))
872 /* if there is only one subline, there is no need to search for a new one */
873 if (&line->sublines == line->curr_subline &&
874 line->curr_subline->next == NULL)
876 line->subline_expire_time += 100 * HZ;
877 return false;
879 if (line->curr_subline->next)
880 line->curr_subline = line->curr_subline->next;
881 else
882 line->curr_subline = &line->sublines;
883 get_subline_timeout(gwps, line->curr_subline);
884 line->subline_expire_time = current_tick + TIMEOUT_UNIT*line->curr_subline->time_mult;
885 return true;
887 return false;
890 /* Display a line appropriately according to its alignment format.
891 format_align contains the text, separated between left, center and right.
892 line is the index of the line on the screen.
893 scroll indicates whether the line is a scrolling one or not.
895 static void write_line(struct screen *display,
896 struct align_pos *format_align,
897 int line,
898 bool scroll)
900 int left_width = 0, left_xpos;
901 int center_width = 0, center_xpos;
902 int right_width = 0, right_xpos;
903 int ypos;
904 int space_width;
905 int string_height;
906 int scroll_width;
908 /* calculate different string sizes and positions */
909 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
910 if (format_align->left != 0) {
911 display->getstringsize((unsigned char *)format_align->left,
912 &left_width, &string_height);
915 if (format_align->right != 0) {
916 display->getstringsize((unsigned char *)format_align->right,
917 &right_width, &string_height);
920 if (format_align->center != 0) {
921 display->getstringsize((unsigned char *)format_align->center,
922 &center_width, &string_height);
925 left_xpos = 0;
926 right_xpos = (display->getwidth() - right_width);
927 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
929 scroll_width = display->getwidth() - left_xpos;
931 /* Checks for overlapping strings.
932 If needed the overlapping strings will be merged, separated by a
933 space */
935 /* CASE 1: left and centered string overlap */
936 /* there is a left string, need to merge left and center */
937 if ((left_width != 0 && center_width != 0) &&
938 (left_xpos + left_width + space_width > center_xpos)) {
939 /* replace the former separator '\0' of left and
940 center string with a space */
941 *(--format_align->center) = ' ';
942 /* calculate the new width and position of the merged string */
943 left_width = left_width + space_width + center_width;
944 /* there is no centered string anymore */
945 center_width = 0;
947 /* there is no left string, move center to left */
948 if ((left_width == 0 && center_width != 0) &&
949 (left_xpos + left_width > center_xpos)) {
950 /* move the center string to the left string */
951 format_align->left = format_align->center;
952 /* calculate the new width and position of the string */
953 left_width = center_width;
954 /* there is no centered string anymore */
955 center_width = 0;
958 /* CASE 2: centered and right string overlap */
959 /* there is a right string, need to merge center and right */
960 if ((center_width != 0 && right_width != 0) &&
961 (center_xpos + center_width + space_width > right_xpos)) {
962 /* replace the former separator '\0' of center and
963 right string with a space */
964 *(--format_align->right) = ' ';
965 /* move the center string to the right after merge */
966 format_align->right = format_align->center;
967 /* calculate the new width and position of the merged string */
968 right_width = center_width + space_width + right_width;
969 right_xpos = (display->getwidth() - right_width);
970 /* there is no centered string anymore */
971 center_width = 0;
973 /* there is no right string, move center to right */
974 if ((center_width != 0 && right_width == 0) &&
975 (center_xpos + center_width > right_xpos)) {
976 /* move the center string to the right string */
977 format_align->right = format_align->center;
978 /* calculate the new width and position of the string */
979 right_width = center_width;
980 right_xpos = (display->getwidth() - right_width);
981 /* there is no centered string anymore */
982 center_width = 0;
985 /* CASE 3: left and right overlap
986 There is no center string anymore, either there never
987 was one or it has been merged in case 1 or 2 */
988 /* there is a left string, need to merge left and right */
989 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
990 (left_xpos + left_width + space_width > right_xpos)) {
991 /* replace the former separator '\0' of left and
992 right string with a space */
993 *(--format_align->right) = ' ';
994 /* calculate the new width and position of the string */
995 left_width = left_width + space_width + right_width;
996 /* there is no right string anymore */
997 right_width = 0;
999 /* there is no left string, move right to left */
1000 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
1001 (left_width > right_xpos)) {
1002 /* move the right string to the left string */
1003 format_align->left = format_align->right;
1004 /* calculate the new width and position of the string */
1005 left_width = right_width;
1006 /* there is no right string anymore */
1007 right_width = 0;
1010 ypos = (line * string_height);
1013 if (scroll && ((left_width > scroll_width) ||
1014 (center_width > scroll_width) ||
1015 (right_width > scroll_width)))
1017 display->puts_scroll(0, line,
1018 (unsigned char *)format_align->left);
1020 else
1022 #ifdef HAVE_LCD_BITMAP
1023 /* clear the line first */
1024 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1025 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
1026 display->set_drawmode(DRMODE_SOLID);
1027 #endif
1029 /* Nasty hack: we output an empty scrolling string,
1030 which will reset the scroller for that line */
1031 display->puts_scroll(0, line, (unsigned char *)"");
1033 /* print aligned strings */
1034 if (left_width != 0)
1036 display->putsxy(left_xpos, ypos,
1037 (unsigned char *)format_align->left);
1039 if (center_width != 0)
1041 display->putsxy(center_xpos, ypos,
1042 (unsigned char *)format_align->center);
1044 if (right_width != 0)
1046 display->putsxy(right_xpos, ypos,
1047 (unsigned char *)format_align->right);
1052 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
1054 struct wps_data *data = gwps->data;
1055 struct screen *display = gwps->display;
1057 if (!data || !display || !gwps->state)
1058 return false;
1060 unsigned flags;
1061 char linebuf[MAX_PATH];
1063 struct align_pos align;
1064 align.left = NULL;
1065 align.center = NULL;
1066 align.right = NULL;
1069 struct skin_token_list *viewport_list;
1071 bool update_line, new_subline_refresh;
1073 #ifdef HAVE_LCD_BITMAP
1075 /* to find out wether the peak meter is enabled we
1076 assume it wasn't until we find a line that contains
1077 the peak meter. We can't use peak_meter_enabled itself
1078 because that would mean to turn off the meter thread
1079 temporarily. (That shouldn't matter unless yield
1080 or sleep is called but who knows...)
1082 bool enable_pm = false;
1084 #endif
1086 /* reset to first subline if refresh all flag is set */
1087 if (refresh_mode == WPS_REFRESH_ALL)
1089 struct skin_line *line;
1090 struct skin_viewport *skin_viewport = find_viewport(VP_DEFAULT_LABEL, data);
1092 if (!(skin_viewport->hidden_flags & VP_NEVER_VISIBLE))
1094 display->set_viewport(&skin_viewport->vp);
1095 display->clear_viewport();
1098 for (viewport_list = data->viewports;
1099 viewport_list; viewport_list = viewport_list->next)
1101 skin_viewport =
1102 (struct skin_viewport *)viewport_list->token->value.data;
1103 for(line = skin_viewport->lines; line; line = line->next)
1105 line->curr_subline = NULL;
1110 #ifdef HAVE_LCD_CHARCELLS
1111 int i;
1112 for (i = 0; i < 8; i++)
1114 if (data->wps_progress_pat[i] == 0)
1115 data->wps_progress_pat[i] = display->get_locked_pattern();
1117 #endif
1119 /* disable any viewports which are conditionally displayed.
1120 * If we are only refreshing the peak meter then don't change the viewport
1121 * enabled flags as this will stop scrolling. viewports cant be
1122 * toggled in this refresh mode anyway (FS#10215)*/
1123 if (refresh_mode != WPS_REFRESH_PEAK_METER)
1125 for (viewport_list = data->viewports;
1126 viewport_list; viewport_list = viewport_list->next)
1128 struct skin_viewport *skin_viewport =
1129 (struct skin_viewport *)viewport_list->token->value.data;
1130 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1132 continue;
1134 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
1136 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
1137 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1138 else
1139 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
1143 int viewport_count = 0;
1144 for (viewport_list = data->viewports;
1145 viewport_list; viewport_list = viewport_list->next, viewport_count++)
1147 struct skin_viewport *skin_viewport =
1148 (struct skin_viewport *)viewport_list->token->value.data;
1149 unsigned vp_refresh_mode = refresh_mode;
1151 display->set_viewport(&skin_viewport->vp);
1153 int hidden_vp = 0;
1155 #ifdef HAVE_LCD_BITMAP
1156 /* Set images to not to be displayed */
1157 struct skin_token_list *imglist = data->images;
1158 while (imglist)
1160 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
1161 img->display = -1;
1162 imglist = imglist->next;
1164 #endif
1165 /* dont redraw the viewport if its disabled */
1166 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1167 { /* don't draw anything into this one */
1168 vp_refresh_mode = 0; hidden_vp = true;
1170 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
1172 if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
1173 display->scroll_stop(&skin_viewport->vp);
1174 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1175 continue;
1177 else if (((skin_viewport->hidden_flags&
1178 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
1179 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
1181 vp_refresh_mode = WPS_REFRESH_ALL;
1182 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
1185 if (vp_refresh_mode == WPS_REFRESH_ALL)
1187 display->clear_viewport();
1190 /* loop over the lines for this viewport */
1191 struct skin_line *line;
1192 int line_count = 0;
1194 for (line = skin_viewport->lines; line; line = line->next, line_count++)
1196 struct skin_subline *subline;
1197 memset(linebuf, 0, sizeof(linebuf));
1198 update_line = false;
1200 /* get current subline for the line */
1201 new_subline_refresh = update_curr_subline(gwps, line);
1202 subline = line->curr_subline;
1203 flags = line->curr_subline->line_type;
1205 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1206 || new_subline_refresh || hidden_vp)
1208 /* get_line tells us if we need to update the line */
1209 update_line = get_line(gwps, subline, &align,
1210 linebuf, sizeof(linebuf), vp_refresh_mode);
1212 #ifdef HAVE_LCD_BITMAP
1213 /* peakmeter */
1214 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1216 /* the peakmeter should be alone on its line */
1217 update_line = false;
1219 int h = font_get(skin_viewport->vp.font)->height;
1220 int peak_meter_y = line_count* h;
1222 /* The user might decide to have the peak meter in the last
1223 line so that it is only displayed if no status bar is
1224 visible. If so we neither want do draw nor enable the
1225 peak meter. */
1226 if (peak_meter_y + h <= skin_viewport->vp.y+skin_viewport->vp.height) {
1227 /* found a line with a peak meter -> remember that we must
1228 enable it later */
1229 enable_pm = true;
1230 peak_meter_enabled = true;
1231 peak_meter_screen(gwps->display, 0, peak_meter_y,
1232 MIN(h, skin_viewport->vp.y+skin_viewport->vp.height - peak_meter_y));
1234 else
1236 peak_meter_enabled = false;
1240 #else /* HAVE_LCD_CHARCELL */
1242 /* progressbar */
1243 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1245 if (data->full_line_progressbar)
1246 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1247 else
1248 draw_player_progress(gwps);
1250 #endif
1252 if (update_line && !hidden_vp &&
1253 /* conditionals clear the line which means if the %Vd is put into the default
1254 viewport there will be a blank line.
1255 To get around this we dont allow any actual drawing to happen in the
1256 deault vp if other vp's are defined */
1257 ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
1258 !viewport_list->next))
1260 if (flags & WPS_REFRESH_SCROLL)
1262 /* if the line is a scrolling one we don't want to update
1263 too often, so that it has the time to scroll */
1264 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1265 write_line(display, &align, line_count, true);
1267 else
1268 write_line(display, &align, line_count, false);
1271 #ifdef HAVE_LCD_BITMAP
1272 /* progressbar */
1273 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1275 struct skin_token_list *bar = gwps->data->progressbars;
1276 while (bar)
1278 struct progressbar *thisbar = (struct progressbar*)bar->token->value.data;
1279 if (thisbar->vp == &skin_viewport->vp && thisbar->draw)
1281 draw_progressbar(gwps, thisbar);
1283 bar = bar->next;
1286 /* Now display any images in this viewport */
1287 if (!hidden_vp)
1288 wps_display_images(gwps, &skin_viewport->vp);
1289 #endif
1292 #ifdef HAVE_LCD_BITMAP
1293 data->peak_meter_enabled = enable_pm;
1294 #endif
1295 /* Restore the default viewport */
1296 display->set_viewport(NULL);
1298 display->update();
1300 return true;