shuld fix red and yellow
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
blobe991ba9aa0f9b59b846cf4e7bde54fa3aa74ae9d
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 #if CONFIG_CODEC == SWCODEC
196 struct mp3entry id3;
197 #endif
198 char buf[MAX_PATH*2], tempbuf[MAX_PATH];
199 unsigned int buf_used = 0;
201 gwps->display->set_viewport(viewer->vp);
202 for(i=start_item; (i-start_item)<lines && i<playlist_amount(); i++)
204 if (i == cur_playlist_pos)
206 pid3 = state->id3;
208 else if (i == cur_playlist_pos+1)
210 pid3 = state->nid3;
212 #if CONFIG_CODEC == SWCODEC
213 else if ((i>cur_playlist_pos) && audio_peek_track(&id3, i-cur_playlist_pos))
215 pid3 = &id3;
217 #endif
218 else
220 pid3 = NULL;
223 int line = pid3 ? TRACK_HAS_INFO : TRACK_HAS_NO_INFO;
224 int j = 0, cur_string = 0;
225 char *filename = playlist_peek(i-cur_playlist_pos);
226 buf[0] = '\0';
227 buf_used = 0;
228 while (j < viewer->lines[line].count && (buf_used<sizeof(buf)))
230 const char *out = NULL;
231 token.type = viewer->lines[line].tokens[j];
232 token.value.i = 0;
233 token.next = false;
234 out = get_id3_token(&token, pid3, tempbuf, sizeof(tempbuf), -1, NULL);
235 if (out)
237 snprintf(&buf[buf_used], sizeof(buf)-buf_used, "%s", out);
238 buf_used += strlen(out);
239 j++;
240 continue;
242 switch (viewer->lines[line].tokens[j])
244 case WPS_TOKEN_ALIGN_CENTER:
245 case WPS_TOKEN_ALIGN_LEFT:
246 case WPS_TOKEN_ALIGN_LEFT_RTL:
247 case WPS_TOKEN_ALIGN_RIGHT:
248 case WPS_TOKEN_ALIGN_RIGHT_RTL:
249 alignment = viewer->lines[line].tokens[j];
250 tempbuf[0] = '\0';
251 break;
252 case WPS_TOKEN_STRING:
253 case WPS_TOKEN_CHARACTER:
254 snprintf(tempbuf, sizeof(tempbuf), "%s",
255 viewer->lines[line].strings[cur_string]);
256 cur_string++;
257 break;
258 case WPS_TOKEN_PLAYLIST_POSITION:
259 snprintf(tempbuf, sizeof(tempbuf), "%d", i);
260 break;
261 case WPS_TOKEN_FILE_NAME:
262 get_dir(tempbuf, sizeof(tempbuf), filename, 0);
263 break;
264 case WPS_TOKEN_FILE_PATH:
265 snprintf(tempbuf, sizeof(tempbuf), "%s", filename);
266 break;
267 default:
268 tempbuf[0] = '\0';
269 break;
271 if (tempbuf[0])
273 snprintf(&buf[buf_used], sizeof(buf)-buf_used, "%s", tempbuf);
274 buf_used += strlen(tempbuf);
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 #endif
618 #ifdef HAVE_ALBUMART
619 if (data->albumart && data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
621 draw_album_art(gwps,
622 playback_current_aa_hid(data->playback_aa_slot), true);
623 data->albumart->draw = false;
625 #endif
628 return true;
632 /* Read a (sub)line to the given alignment format buffer.
633 linebuf is the buffer where the data is actually stored.
634 align is the alignment format that'll be used to display the text.
635 The return value indicates whether the line needs to be updated.
637 static bool get_line(struct gui_wps *gwps,
638 struct skin_subline *subline,
639 struct align_pos *align,
640 char *linebuf,
641 int linebuf_size,
642 unsigned refresh_mode)
644 struct wps_data *data = gwps->data;
646 char temp_buf[128];
647 char *buf = linebuf; /* will always point to the writing position */
648 char *linebuf_end = linebuf + linebuf_size - 1;
649 bool update = false;
650 int i;
651 (void)refresh_mode; /* silence warning on charcell */
653 /* alignment-related variables */
654 int cur_align;
655 char* cur_align_start;
656 cur_align_start = buf;
657 cur_align = WPS_ALIGN_LEFT;
658 align->left = NULL;
659 align->center = NULL;
660 align->right = NULL;
661 /* Process all tokens of the desired subline */
662 for (i = subline->first_token_idx;
663 i <= subline->last_token_idx; i++)
665 switch(data->tokens[i].type)
667 case WPS_TOKEN_CONDITIONAL:
668 /* place ourselves in the right conditional case */
669 update |= evaluate_conditional(gwps, &i);
670 break;
672 case WPS_TOKEN_CONDITIONAL_OPTION:
673 /* we've finished in the curent conditional case,
674 skip to the end of the conditional structure */
675 i = find_conditional_end(data, i);
676 break;
678 #ifdef HAVE_LCD_BITMAP
679 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
681 char n = data->tokens[i].value.i & 0xFF;
682 int subimage = data->tokens[i].value.i >> 8;
683 struct gui_img *img = find_image(n, data);
685 if (img && img->loaded)
686 img->display = subimage;
687 break;
689 case WPS_TOKEN_DRAW_INBUILTBAR:
690 gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
691 refresh_mode == WPS_REFRESH_ALL,
692 data->tokens[i].value.data);
693 break;
694 #endif
696 case WPS_TOKEN_ALIGN_LEFT:
697 case WPS_TOKEN_ALIGN_LEFT_RTL:
698 case WPS_TOKEN_ALIGN_CENTER:
699 case WPS_TOKEN_ALIGN_RIGHT:
700 case WPS_TOKEN_ALIGN_RIGHT_RTL:
701 /* remember where the current aligned text started */
702 switch (cur_align)
704 case WPS_ALIGN_LEFT:
705 align->left = cur_align_start;
706 break;
708 case WPS_ALIGN_CENTER:
709 align->center = cur_align_start;
710 break;
712 case WPS_ALIGN_RIGHT:
713 align->right = cur_align_start;
714 break;
716 /* start a new alignment */
717 switch (data->tokens[i].type)
719 case WPS_TOKEN_ALIGN_LEFT:
720 cur_align = WPS_ALIGN_LEFT;
721 break;
722 case WPS_TOKEN_ALIGN_LEFT_RTL:
723 cur_align = lang_is_rtl() ? WPS_ALIGN_RIGHT :
724 WPS_ALIGN_LEFT;
725 break;
726 case WPS_TOKEN_ALIGN_CENTER:
727 cur_align = WPS_ALIGN_CENTER;
728 break;
729 case WPS_TOKEN_ALIGN_RIGHT:
730 cur_align = WPS_ALIGN_RIGHT;
731 break;
732 case WPS_TOKEN_ALIGN_RIGHT_RTL:
733 cur_align = lang_is_rtl() ? WPS_ALIGN_LEFT :
734 WPS_ALIGN_RIGHT;
735 break;
736 default:
737 break;
739 *buf++ = 0;
740 cur_align_start = buf;
741 break;
742 case WPS_VIEWPORT_ENABLE:
744 char label = data->tokens[i].value.i;
745 char temp = VP_DRAW_HIDEABLE;
746 /* viewports are allowed to share id's so find and enable
747 * all of them */
748 struct skin_token_list *list = data->viewports;
749 while (list)
751 struct skin_viewport *vp =
752 (struct skin_viewport *)list->token->value.data;
753 if (vp->label == label)
755 if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
756 temp |= VP_DRAW_WASHIDDEN;
757 vp->hidden_flags = temp;
759 list = list->next;
762 break;
763 #ifdef HAVE_LCD_BITMAP
764 case WPS_VIEWPORT_CUSTOMLIST:
765 draw_playlist_viewer_list(gwps, data->tokens[i].value.data);
766 break;
767 #endif
768 default:
770 /* get the value of the tag and copy it to the buffer */
771 const char *value = get_token_value(gwps, &data->tokens[i],
772 temp_buf, sizeof(temp_buf), NULL);
773 if (value)
775 update = true;
776 while (*value && (buf < linebuf_end))
777 *buf++ = *value++;
779 break;
784 /* close the current alignment */
785 switch (cur_align)
787 case WPS_ALIGN_LEFT:
788 align->left = cur_align_start;
789 break;
791 case WPS_ALIGN_CENTER:
792 align->center = cur_align_start;
793 break;
795 case WPS_ALIGN_RIGHT:
796 align->right = cur_align_start;
797 break;
800 return update;
802 static void get_subline_timeout(struct gui_wps *gwps, struct skin_subline *subline)
804 struct wps_data *data = gwps->data;
805 int i;
806 subline->time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
808 for (i = subline->first_token_idx;
809 i <= subline->last_token_idx; i++)
811 switch(data->tokens[i].type)
813 case WPS_TOKEN_CONDITIONAL:
814 /* place ourselves in the right conditional case */
815 evaluate_conditional(gwps, &i);
816 break;
818 case WPS_TOKEN_CONDITIONAL_OPTION:
819 /* we've finished in the curent conditional case,
820 skip to the end of the conditional structure */
821 i = find_conditional_end(data, i);
822 break;
824 case WPS_TOKEN_SUBLINE_TIMEOUT:
825 subline->time_mult = data->tokens[i].value.i;
826 break;
828 default:
829 break;
834 /* Calculates which subline should be displayed for the specified line
835 Returns true iff the subline must be refreshed */
836 static bool update_curr_subline(struct gui_wps *gwps, struct skin_line *line)
838 /* shortcut this whole thing if we need to reset the line completly */
839 if (line->curr_subline == NULL)
841 line->subline_expire_time = current_tick;
842 line->curr_subline = &line->sublines;
843 if (!line->curr_subline->next)
845 line->subline_expire_time += 100*HZ;
847 else
849 get_subline_timeout(gwps, line->curr_subline);
850 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
852 return true;
854 /* if time to advance to next sub-line */
855 if (TIME_AFTER(current_tick, line->subline_expire_time - 1))
857 /* if there is only one subline, there is no need to search for a new one */
858 if (&line->sublines == line->curr_subline &&
859 line->curr_subline->next == NULL)
861 line->subline_expire_time += 100 * HZ;
862 return false;
864 if (line->curr_subline->next)
865 line->curr_subline = line->curr_subline->next;
866 else
867 line->curr_subline = &line->sublines;
868 get_subline_timeout(gwps, line->curr_subline);
869 line->subline_expire_time = current_tick + TIMEOUT_UNIT*line->curr_subline->time_mult;
870 return true;
872 return false;
875 /* Display a line appropriately according to its alignment format.
876 format_align contains the text, separated between left, center and right.
877 line is the index of the line on the screen.
878 scroll indicates whether the line is a scrolling one or not.
880 static void write_line(struct screen *display,
881 struct align_pos *format_align,
882 int line,
883 bool scroll)
885 int left_width = 0, left_xpos;
886 int center_width = 0, center_xpos;
887 int right_width = 0, right_xpos;
888 int ypos;
889 int space_width;
890 int string_height;
891 int scroll_width;
893 /* calculate different string sizes and positions */
894 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
895 if (format_align->left != 0) {
896 display->getstringsize((unsigned char *)format_align->left,
897 &left_width, &string_height);
900 if (format_align->right != 0) {
901 display->getstringsize((unsigned char *)format_align->right,
902 &right_width, &string_height);
905 if (format_align->center != 0) {
906 display->getstringsize((unsigned char *)format_align->center,
907 &center_width, &string_height);
910 left_xpos = 0;
911 right_xpos = (display->getwidth() - right_width);
912 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
914 scroll_width = display->getwidth() - left_xpos;
916 /* Checks for overlapping strings.
917 If needed the overlapping strings will be merged, separated by a
918 space */
920 /* CASE 1: left and centered string overlap */
921 /* there is a left string, need to merge left and center */
922 if ((left_width != 0 && center_width != 0) &&
923 (left_xpos + left_width + space_width > center_xpos)) {
924 /* replace the former separator '\0' of left and
925 center string with a space */
926 *(--format_align->center) = ' ';
927 /* calculate the new width and position of the merged string */
928 left_width = left_width + space_width + center_width;
929 /* there is no centered string anymore */
930 center_width = 0;
932 /* there is no left string, move center to left */
933 if ((left_width == 0 && center_width != 0) &&
934 (left_xpos + left_width > center_xpos)) {
935 /* move the center string to the left string */
936 format_align->left = format_align->center;
937 /* calculate the new width and position of the string */
938 left_width = center_width;
939 /* there is no centered string anymore */
940 center_width = 0;
943 /* CASE 2: centered and right string overlap */
944 /* there is a right string, need to merge center and right */
945 if ((center_width != 0 && right_width != 0) &&
946 (center_xpos + center_width + space_width > right_xpos)) {
947 /* replace the former separator '\0' of center and
948 right string with a space */
949 *(--format_align->right) = ' ';
950 /* move the center string to the right after merge */
951 format_align->right = format_align->center;
952 /* calculate the new width and position of the merged string */
953 right_width = center_width + space_width + right_width;
954 right_xpos = (display->getwidth() - right_width);
955 /* there is no centered string anymore */
956 center_width = 0;
958 /* there is no right string, move center to right */
959 if ((center_width != 0 && right_width == 0) &&
960 (center_xpos + center_width > right_xpos)) {
961 /* move the center string to the right string */
962 format_align->right = format_align->center;
963 /* calculate the new width and position of the string */
964 right_width = center_width;
965 right_xpos = (display->getwidth() - right_width);
966 /* there is no centered string anymore */
967 center_width = 0;
970 /* CASE 3: left and right overlap
971 There is no center string anymore, either there never
972 was one or it has been merged in case 1 or 2 */
973 /* there is a left string, need to merge left and right */
974 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
975 (left_xpos + left_width + space_width > right_xpos)) {
976 /* replace the former separator '\0' of left and
977 right string with a space */
978 *(--format_align->right) = ' ';
979 /* calculate the new width and position of the string */
980 left_width = left_width + space_width + right_width;
981 /* there is no right string anymore */
982 right_width = 0;
984 /* there is no left string, move right to left */
985 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
986 (left_width > right_xpos)) {
987 /* move the right string to the left string */
988 format_align->left = format_align->right;
989 /* calculate the new width and position of the string */
990 left_width = right_width;
991 /* there is no right string anymore */
992 right_width = 0;
995 ypos = (line * string_height);
998 if (scroll && ((left_width > scroll_width) ||
999 (center_width > scroll_width) ||
1000 (right_width > scroll_width)))
1002 display->puts_scroll(0, line,
1003 (unsigned char *)format_align->left);
1005 else
1007 #ifdef HAVE_LCD_BITMAP
1008 /* clear the line first */
1009 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1010 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
1011 display->set_drawmode(DRMODE_SOLID);
1012 #endif
1014 /* Nasty hack: we output an empty scrolling string,
1015 which will reset the scroller for that line */
1016 display->puts_scroll(0, line, (unsigned char *)"");
1018 /* print aligned strings */
1019 if (left_width != 0)
1021 display->putsxy(left_xpos, ypos,
1022 (unsigned char *)format_align->left);
1024 if (center_width != 0)
1026 display->putsxy(center_xpos, ypos,
1027 (unsigned char *)format_align->center);
1029 if (right_width != 0)
1031 display->putsxy(right_xpos, ypos,
1032 (unsigned char *)format_align->right);
1037 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
1039 struct wps_data *data = gwps->data;
1040 struct screen *display = gwps->display;
1042 if (!data || !display || !gwps->state)
1043 return false;
1045 unsigned flags;
1046 char linebuf[MAX_PATH];
1048 struct align_pos align;
1049 align.left = NULL;
1050 align.center = NULL;
1051 align.right = NULL;
1054 struct skin_token_list *viewport_list;
1056 bool update_line, new_subline_refresh;
1058 #ifdef HAVE_LCD_BITMAP
1060 /* to find out wether the peak meter is enabled we
1061 assume it wasn't until we find a line that contains
1062 the peak meter. We can't use peak_meter_enabled itself
1063 because that would mean to turn off the meter thread
1064 temporarily. (That shouldn't matter unless yield
1065 or sleep is called but who knows...)
1067 bool enable_pm = false;
1069 #endif
1071 /* reset to first subline if refresh all flag is set */
1072 if (refresh_mode == WPS_REFRESH_ALL)
1074 struct skin_line *line;
1075 struct skin_viewport *skin_viewport = find_viewport(VP_DEFAULT_LABEL, data);
1077 if (!(skin_viewport->hidden_flags & VP_NEVER_VISIBLE))
1079 display->set_viewport(&skin_viewport->vp);
1080 display->clear_viewport();
1083 for (viewport_list = data->viewports;
1084 viewport_list; viewport_list = viewport_list->next)
1086 skin_viewport =
1087 (struct skin_viewport *)viewport_list->token->value.data;
1088 for(line = skin_viewport->lines; line; line = line->next)
1090 line->curr_subline = NULL;
1095 #ifdef HAVE_LCD_CHARCELLS
1096 int i;
1097 for (i = 0; i < 8; i++)
1099 if (data->wps_progress_pat[i] == 0)
1100 data->wps_progress_pat[i] = display->get_locked_pattern();
1102 #endif
1104 /* disable any viewports which are conditionally displayed.
1105 * If we are only refreshing the peak meter then don't change the viewport
1106 * enabled flags as this will stop scrolling. viewports cant be
1107 * toggled in this refresh mode anyway (FS#10215)*/
1108 if (refresh_mode != WPS_REFRESH_PEAK_METER)
1110 for (viewport_list = data->viewports;
1111 viewport_list; viewport_list = viewport_list->next)
1113 struct skin_viewport *skin_viewport =
1114 (struct skin_viewport *)viewport_list->token->value.data;
1115 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1117 continue;
1119 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
1121 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
1122 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1123 else
1124 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
1128 int viewport_count = 0;
1129 for (viewport_list = data->viewports;
1130 viewport_list; viewport_list = viewport_list->next, viewport_count++)
1132 struct skin_viewport *skin_viewport =
1133 (struct skin_viewport *)viewport_list->token->value.data;
1134 unsigned vp_refresh_mode = refresh_mode;
1136 display->set_viewport(&skin_viewport->vp);
1138 int hidden_vp = 0;
1140 #ifdef HAVE_LCD_BITMAP
1141 /* Set images to not to be displayed */
1142 struct skin_token_list *imglist = data->images;
1143 while (imglist)
1145 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
1146 img->display = -1;
1147 imglist = imglist->next;
1149 #endif
1150 /* dont redraw the viewport if its disabled */
1151 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1152 { /* don't draw anything into this one */
1153 vp_refresh_mode = 0; hidden_vp = true;
1155 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
1157 if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
1158 display->scroll_stop(&skin_viewport->vp);
1159 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1160 continue;
1162 else if (((skin_viewport->hidden_flags&
1163 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
1164 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
1166 vp_refresh_mode = WPS_REFRESH_ALL;
1167 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
1170 if (vp_refresh_mode == WPS_REFRESH_ALL)
1172 display->clear_viewport();
1175 /* loop over the lines for this viewport */
1176 struct skin_line *line;
1177 int line_count = 0;
1179 for (line = skin_viewport->lines; line; line = line->next, line_count++)
1181 struct skin_subline *subline;
1182 memset(linebuf, 0, sizeof(linebuf));
1183 update_line = false;
1185 /* get current subline for the line */
1186 new_subline_refresh = update_curr_subline(gwps, line);
1187 subline = line->curr_subline;
1188 flags = line->curr_subline->line_type;
1190 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1191 || new_subline_refresh || hidden_vp)
1193 /* get_line tells us if we need to update the line */
1194 update_line = get_line(gwps, subline, &align,
1195 linebuf, sizeof(linebuf), vp_refresh_mode);
1197 #ifdef HAVE_LCD_BITMAP
1198 /* peakmeter */
1199 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1201 /* the peakmeter should be alone on its line */
1202 update_line = false;
1204 int h = font_get(skin_viewport->vp.font)->height;
1205 int peak_meter_y = line_count* h;
1207 /* The user might decide to have the peak meter in the last
1208 line so that it is only displayed if no status bar is
1209 visible. If so we neither want do draw nor enable the
1210 peak meter. */
1211 if (peak_meter_y + h <= skin_viewport->vp.y+skin_viewport->vp.height) {
1212 /* found a line with a peak meter -> remember that we must
1213 enable it later */
1214 enable_pm = true;
1215 peak_meter_enabled = true;
1216 peak_meter_screen(gwps->display, 0, peak_meter_y,
1217 MIN(h, skin_viewport->vp.y+skin_viewport->vp.height - peak_meter_y));
1219 else
1221 peak_meter_enabled = false;
1225 #else /* HAVE_LCD_CHARCELL */
1227 /* progressbar */
1228 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1230 if (data->full_line_progressbar)
1231 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1232 else
1233 draw_player_progress(gwps);
1235 #endif
1237 if (update_line && !hidden_vp &&
1238 /* conditionals clear the line which means if the %Vd is put into the default
1239 viewport there will be a blank line.
1240 To get around this we dont allow any actual drawing to happen in the
1241 deault vp if other vp's are defined */
1242 ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
1243 !viewport_list->next))
1245 if (flags & WPS_REFRESH_SCROLL)
1247 /* if the line is a scrolling one we don't want to update
1248 too often, so that it has the time to scroll */
1249 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1250 write_line(display, &align, line_count, true);
1252 else
1253 write_line(display, &align, line_count, false);
1256 #ifdef HAVE_LCD_BITMAP
1257 /* progressbar */
1258 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1260 struct skin_token_list *bar = gwps->data->progressbars;
1261 while (bar)
1263 struct progressbar *thisbar = (struct progressbar*)bar->token->value.data;
1264 if (thisbar->vp == &skin_viewport->vp)
1266 draw_progressbar(gwps, thisbar);
1268 bar = bar->next;
1271 /* Now display any images in this viewport */
1272 if (!hidden_vp)
1273 wps_display_images(gwps, &skin_viewport->vp);
1274 #endif
1277 #ifdef HAVE_LCD_BITMAP
1278 data->peak_meter_enabled = enable_pm;
1279 #endif
1280 /* Restore the default viewport */
1281 display->set_viewport(NULL);
1283 display->update();
1285 return true;