Rework how progressbars are managed so you can have as many bars in a viewport as...
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
blobde070d830f1202c3c2ba2f0cb31fca4ad3c9f326
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 "scrollbar.h"
38 #include "screen_access.h"
39 #include "playlist.h"
40 #include "audio.h"
42 #ifdef HAVE_LCD_BITMAP
43 #include "peakmeter.h"
44 /* Image stuff */
45 #include "bmp.h"
46 #ifdef HAVE_ALBUMART
47 #include "albumart.h"
48 #endif
49 #endif
51 #include "cuesheet.h"
52 #if CONFIG_CODEC == SWCODEC
53 #include "playback.h"
54 #endif
55 #include "backdrop.h"
56 #include "viewport.h"
59 #include "wps_internals.h"
60 #include "skin_engine.h"
61 #include "statusbar-skinned.h"
63 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode);
65 /* update a skinned screen, update_type is WPS_REFRESH_* values.
66 * Usually it should only be WPS_REFRESH_NON_STATIC
67 * A full update will be done if required (state.do_full_update == true)
69 bool skin_update(struct gui_wps *gwps, unsigned int update_type)
71 bool retval;
72 /* This maybe shouldnt be here, but while the skin is only used to
73 * display the music screen this is better than whereever we are being
74 * called from. This is also safe for skined screen which dont use the id3 */
75 struct mp3entry *id3 = gwps->state->id3;
76 bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
77 gwps->sync_data->do_full_update |= cuesheet_update;
79 retval = skin_redraw(gwps, gwps->sync_data->do_full_update ?
80 WPS_REFRESH_ALL : update_type);
81 return retval;
84 #ifdef HAVE_LCD_BITMAP
86 void skin_statusbar_changed(struct gui_wps *skin)
88 if (!skin)
89 return;
90 struct wps_data *data = skin->data;
91 const struct screen *display = skin->display;
92 const int screen = display->screen_type;
94 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, data)->vp;
95 viewport_set_defaults(vp, screen);
97 if (data->wps_sb_tag)
98 { /* fix up the default viewport */
99 if (data->show_sb_on_wps)
101 if (statusbar_position(screen) != STATUSBAR_OFF)
102 return; /* vp is fixed already */
104 vp->y = STATUSBAR_HEIGHT;
105 vp->height = display->lcdheight - STATUSBAR_HEIGHT;
107 else
109 if (statusbar_position(screen) == STATUSBAR_OFF)
110 return; /* vp is fixed already */
111 vp->y = vp->x = 0;
112 vp->height = display->lcdheight;
113 vp->width = display->lcdwidth;
118 static void draw_progressbar(struct gui_wps *gwps,
119 struct progressbar *pb)
121 struct screen *display = gwps->display;
122 struct viewport *vp = pb->vp;
123 struct wps_state *state = gwps->state;
124 struct mp3entry *id3 = state->id3;
125 int y = pb->y, height = pb->height;
126 unsigned long length, elapsed;
128 if (height < 0)
129 height = font_get(vp->font)->height;
131 if (y < 0)
133 int line_height = font_get(vp->font)->height;
134 /* center the pb in the line, but only if the line is higher than the pb */
135 int center = (line_height-height)/2;
136 /* if Y was not set calculate by font height,Y is -line_number-1 */
137 y = (-y -1)*line_height + (0 > center ? 0 : center);
140 if (pb->type == WPS_TOKEN_VOLUMEBAR)
142 int minvol = sound_min(SOUND_VOLUME);
143 int maxvol = sound_max(SOUND_VOLUME);
144 length = maxvol-minvol;
145 elapsed = global_settings.volume-minvol;
147 else if (id3 && id3->length)
149 length = id3->length;
150 elapsed = id3->elapsed + state->ff_rewind_count;
152 else
154 length = 1;
155 elapsed = 0;
158 if (pb->have_bitmap_pb)
159 gui_bitmap_scrollbar_draw(display, pb->bm,
160 pb->x, y, pb->width, pb->bm.height,
161 length, 0, elapsed, HORIZONTAL);
162 else
163 gui_scrollbar_draw(display, pb->x, y, pb->width, height,
164 length, 0, elapsed, HORIZONTAL);
166 if (pb->type == WPS_TOKEN_PROGRESSBAR && id3 && id3->length)
168 #ifdef AB_REPEAT_ENABLE
169 if (ab_repeat_mode_enabled())
170 ab_draw_markers(display, id3->length,
171 pb->x, y, pb->width, height);
172 #endif
174 if (id3->cuesheet)
175 cue_draw_markers(display, id3->cuesheet, id3->length,
176 pb->x, y+1, pb->width, height-2);
180 bool audio_peek_track(struct mp3entry* id3, int offset);
181 static void draw_playlist_viewer_list(struct gui_wps *gwps,
182 struct playlistviewer *viewer)
184 struct wps_state *state = gwps->state;
185 int lines = viewport_get_nb_lines(viewer->vp);
186 int line_height = font_get(viewer->vp->font)->height;
187 int cur_playlist_pos = playlist_get_display_index();
188 int start_item = MAX(0, cur_playlist_pos + viewer->start_offset);
189 int i;
190 struct wps_token token;
191 int x, length, alignment = WPS_TOKEN_ALIGN_LEFT;
193 struct mp3entry *pid3;
194 #if CONFIG_CODEC == SWCODEC
195 struct mp3entry id3;
196 #endif
197 char buf[MAX_PATH*2], tempbuf[MAX_PATH];
198 unsigned int buf_used = 0;
200 gwps->display->set_viewport(viewer->vp);
201 for(i=start_item; (i-start_item)<lines && i<playlist_amount(); i++)
203 if (i == cur_playlist_pos)
205 pid3 = state->id3;
207 else if (i == cur_playlist_pos+1)
209 pid3 = state->nid3;
211 #if CONFIG_CODEC == SWCODEC
212 else if ((i>cur_playlist_pos) && audio_peek_track(&id3, i-cur_playlist_pos))
214 pid3 = &id3;
216 #endif
217 else
219 pid3 = NULL;
222 int line = pid3 ? TRACK_HAS_INFO : TRACK_HAS_NO_INFO;
223 int j = 0, cur_string = 0;
224 char *filename = playlist_peek(i-cur_playlist_pos);
225 buf[0] = '\0';
226 buf_used = 0;
227 while (j < viewer->lines[line].count && (buf_used<sizeof(buf)))
229 const char *out = NULL;
230 token.type = viewer->lines[line].tokens[j];
231 token.value.i = 0;
232 token.next = false;
233 out = get_id3_token(&token, pid3, tempbuf, sizeof(tempbuf), -1, NULL);
234 if (out)
236 snprintf(&buf[buf_used], sizeof(buf)-buf_used, "%s", out);
237 buf_used += strlen(out);
238 j++;
239 continue;
241 switch (viewer->lines[line].tokens[j])
243 case WPS_TOKEN_ALIGN_CENTER:
244 case WPS_TOKEN_ALIGN_LEFT:
245 case WPS_TOKEN_ALIGN_LEFT_RTL:
246 case WPS_TOKEN_ALIGN_RIGHT:
247 case WPS_TOKEN_ALIGN_RIGHT_RTL:
248 alignment = viewer->lines[line].tokens[j];
249 tempbuf[0] = '\0';
250 break;
251 case WPS_TOKEN_STRING:
252 case WPS_TOKEN_CHARACTER:
253 snprintf(tempbuf, sizeof(tempbuf), "%s",
254 viewer->lines[line].strings[cur_string]);
255 cur_string++;
256 break;
257 case WPS_TOKEN_PLAYLIST_POSITION:
258 snprintf(tempbuf, sizeof(tempbuf), "%d", i);
259 break;
260 case WPS_TOKEN_FILE_NAME:
261 get_dir(tempbuf, sizeof(tempbuf), filename, 0);
262 break;
263 case WPS_TOKEN_FILE_PATH:
264 snprintf(tempbuf, sizeof(tempbuf), "%s", filename);
265 break;
266 default:
267 tempbuf[0] = '\0';
268 break;
270 if (tempbuf[0])
272 snprintf(&buf[buf_used], sizeof(buf)-buf_used, "%s", tempbuf);
273 buf_used += strlen(tempbuf);
275 j++;
278 int vpwidth = viewer->vp->width;
279 length = gwps->display->getstringsize(buf, NULL, NULL);
280 if (viewer->lines[line].scroll && length >= vpwidth)
282 gwps->display->puts_scroll(0, (i-start_item), buf );
284 else
286 if (length >= vpwidth)
287 x = 0;
288 else
290 switch (alignment)
292 case WPS_TOKEN_ALIGN_CENTER:
293 x = (vpwidth-length)/2;
294 break;
295 case WPS_TOKEN_ALIGN_LEFT_RTL:
296 if (lang_is_rtl() && VP_IS_RTL(viewer->vp))
298 x = vpwidth - length;
299 break;
301 case WPS_TOKEN_ALIGN_LEFT:
302 x = 0;
303 break;
304 case WPS_TOKEN_ALIGN_RIGHT_RTL:
305 if (lang_is_rtl() && VP_IS_RTL(viewer->vp))
307 x = 0;
308 break;
310 case WPS_TOKEN_ALIGN_RIGHT:
311 x = vpwidth - length;
312 break;
313 default:
314 x = 0;
315 break;
318 gwps->display->putsxy(x, (i-start_item)*line_height, buf );
324 /* clears the area where the image was shown */
325 static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
327 if(!gwps)
328 return;
329 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
330 gwps->display->fillrect(img->x, img->y, img->bm.width, img->subimage_height);
331 gwps->display->set_drawmode(DRMODE_SOLID);
334 static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage)
336 struct screen *display = gwps->display;
337 if(img->always_display)
338 display->set_drawmode(DRMODE_FG);
339 else
340 display->set_drawmode(DRMODE_SOLID);
342 #if LCD_DEPTH > 1
343 if(img->bm.format == FORMAT_MONO) {
344 #endif
345 display->mono_bitmap_part(img->bm.data,
346 0, img->subimage_height * subimage,
347 img->bm.width, img->x,
348 img->y, img->bm.width,
349 img->subimage_height);
350 #if LCD_DEPTH > 1
351 } else {
352 display->transparent_bitmap_part((fb_data *)img->bm.data,
353 0, img->subimage_height * subimage,
354 STRIDE(display->screen_type,
355 img->bm.width, img->bm.height),
356 img->x, img->y, img->bm.width,
357 img->subimage_height);
359 #endif
362 static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
364 if(!gwps || !gwps->data || !gwps->display)
365 return;
367 struct wps_data *data = gwps->data;
368 struct screen *display = gwps->display;
369 struct skin_token_list *list = data->images;
371 while (list)
373 struct gui_img *img = (struct gui_img*)list->token->value.data;
374 if (img->loaded)
376 if (img->display >= 0)
378 wps_draw_image(gwps, img, img->display);
380 else if (img->always_display && img->vp == vp)
382 wps_draw_image(gwps, img, 0);
385 list = list->next;
387 #ifdef HAVE_ALBUMART
388 /* now draw the AA */
389 if (data->albumart && data->albumart->vp == vp
390 && data->albumart->draw)
392 draw_album_art(gwps, playback_current_aa_hid(data->playback_aa_slot),
393 false);
394 data->albumart->draw = false;
396 #endif
398 display->set_drawmode(DRMODE_SOLID);
401 #else /* HAVE_LCD_CHARCELL */
403 static bool draw_player_progress(struct gui_wps *gwps)
405 struct wps_state *state = gwps->state;
406 struct screen *display = gwps->display;
407 unsigned char progress_pattern[7];
408 int pos = 0;
409 int i;
411 int elapsed, length;
412 if (LIKELY(state->id3))
414 elapsed = state->id3->elapsed;
415 length = state->id3->length;
417 else
419 elapsed = 0;
420 length = 0;
423 if (length)
424 pos = 36 * (elapsed + state->ff_rewind_count) / length;
426 for (i = 0; i < 7; i++, pos -= 5)
428 if (pos <= 0)
429 progress_pattern[i] = 0x1fu;
430 else if (pos >= 5)
431 progress_pattern[i] = 0x00u;
432 else
433 progress_pattern[i] = 0x1fu >> pos;
436 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
437 return true;
440 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
442 static const unsigned char numbers[10][4] = {
443 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
444 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
445 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
446 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
447 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
448 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
449 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
450 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
451 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
452 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
455 struct wps_state *state = gwps->state;
456 struct screen *display = gwps->display;
457 struct wps_data *data = gwps->data;
458 unsigned char progress_pattern[7];
459 char timestr[10];
460 int time;
461 int time_idx = 0;
462 int pos = 0;
463 int pat_idx = 1;
464 int digit, i, j;
465 bool softchar;
467 int elapsed, length;
468 if (LIKELY(state->id3))
470 elapsed = state->id3->elapsed;
471 length = state->id3->length;
473 else
475 elapsed = 0;
476 length = 0;
479 if (buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
480 return;
482 time = elapsed + state->ff_rewind_count;
483 if (length)
484 pos = 55 * time / length;
486 memset(timestr, 0, sizeof(timestr));
487 format_time(timestr, sizeof(timestr)-2, time);
488 timestr[strlen(timestr)] = ':'; /* always safe */
490 for (i = 0; i < 11; i++, pos -= 5)
492 softchar = false;
493 memset(progress_pattern, 0, sizeof(progress_pattern));
495 if ((digit = timestr[time_idx]))
497 softchar = true;
498 digit -= '0';
500 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
502 memcpy(progress_pattern, numbers[digit], 4);
503 time_idx += 2;
505 else /* tens, shifted right */
507 for (j = 0; j < 4; j++)
508 progress_pattern[j] = numbers[digit][j] >> 1;
510 if (time_idx > 0) /* not the first group, add colon in front */
512 progress_pattern[1] |= 0x10u;
513 progress_pattern[3] |= 0x10u;
515 time_idx++;
518 if (pos >= 5)
519 progress_pattern[5] = progress_pattern[6] = 0x1fu;
522 if (pos > 0 && pos < 5)
524 softchar = true;
525 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
528 if (softchar && pat_idx < 8)
530 display->define_pattern(data->wps_progress_pat[pat_idx],
531 progress_pattern);
532 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
533 pat_idx++;
535 else if (pos <= 0)
536 buf = utf8encode(' ', buf);
537 else
538 buf = utf8encode(0xe115, buf); /* 2/7 _ */
540 *buf = '\0';
543 #endif /* HAVE_LCD_CHARCELL */
545 /* Return the index to the end token for the conditional token at index.
546 The conditional token can be either a start token or a separator
547 (i.e. option) token.
549 static int find_conditional_end(struct wps_data *data, int index)
551 int ret = index;
552 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
553 ret = data->tokens[ret].value.i;
555 /* ret now is the index to the end token for the conditional. */
556 return ret;
559 /* Evaluate the conditional that is at *token_index and return whether a skip
560 has ocurred. *token_index is updated with the new position.
562 static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
564 if (!gwps)
565 return false;
567 struct wps_data *data = gwps->data;
569 int i, cond_end;
570 int cond_index = *token_index;
571 char result[128];
572 const char *value;
573 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
574 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
576 /* treat ?xx<true> constructs as if they had 2 options. */
577 if (num_options < 2)
578 num_options = 2;
580 int intval = num_options;
581 /* get_token_value needs to know the number of options in the enum */
582 value = get_token_value(gwps, &data->tokens[cond_index + 1],
583 result, sizeof(result), &intval);
585 /* intval is now the number of the enum option we want to read,
586 starting from 1. If intval is -1, we check if value is empty. */
587 if (intval == -1)
588 intval = (value && *value) ? 1 : num_options;
589 else if (intval > num_options || intval < 1)
590 intval = num_options;
592 data->tokens[cond_index].value.i = (intval << 8) + num_options;
594 /* skip to the appropriate enum case */
595 int next = cond_index + 2;
596 for (i = 1; i < intval; i++)
598 next = data->tokens[next].value.i;
600 *token_index = next;
602 if (prev_val == intval)
604 /* Same conditional case as previously. Return without clearing the
605 pictures */
606 return false;
609 cond_end = find_conditional_end(data, cond_index + 2);
610 for (i = cond_index + 3; i < cond_end; i++)
612 #ifdef HAVE_LCD_BITMAP
613 /* clear all pictures in the conditional and nested ones */
614 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
615 clear_image_pos(gwps, find_image(data->tokens[i].value.i&0xFF, data));
616 #endif
617 #ifdef HAVE_ALBUMART
618 if (data->albumart && data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
620 draw_album_art(gwps,
621 playback_current_aa_hid(data->playback_aa_slot), true);
622 data->albumart->draw = false;
624 #endif
627 return true;
631 /* Read a (sub)line to the given alignment format buffer.
632 linebuf is the buffer where the data is actually stored.
633 align is the alignment format that'll be used to display the text.
634 The return value indicates whether the line needs to be updated.
636 static bool get_line(struct gui_wps *gwps,
637 struct skin_subline *subline,
638 struct align_pos *align,
639 char *linebuf,
640 int linebuf_size,
641 unsigned refresh_mode)
643 struct wps_data *data = gwps->data;
645 char temp_buf[128];
646 char *buf = linebuf; /* will always point to the writing position */
647 char *linebuf_end = linebuf + linebuf_size - 1;
648 bool update = false;
649 int i;
650 (void)refresh_mode; /* silence warning on charcell */
652 /* alignment-related variables */
653 int cur_align;
654 char* cur_align_start;
655 cur_align_start = buf;
656 cur_align = WPS_ALIGN_LEFT;
657 align->left = NULL;
658 align->center = NULL;
659 align->right = NULL;
660 /* Process all tokens of the desired subline */
661 for (i = subline->first_token_idx;
662 i <= subline->last_token_idx; i++)
664 switch(data->tokens[i].type)
666 case WPS_TOKEN_CONDITIONAL:
667 /* place ourselves in the right conditional case */
668 update |= evaluate_conditional(gwps, &i);
669 break;
671 case WPS_TOKEN_CONDITIONAL_OPTION:
672 /* we've finished in the curent conditional case,
673 skip to the end of the conditional structure */
674 i = find_conditional_end(data, i);
675 break;
677 #ifdef HAVE_LCD_BITMAP
678 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
680 char n = data->tokens[i].value.i & 0xFF;
681 int subimage = data->tokens[i].value.i >> 8;
682 struct gui_img *img = find_image(n, data);
684 if (img && img->loaded)
685 img->display = subimage;
686 break;
688 case WPS_TOKEN_DRAW_INBUILTBAR:
689 gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
690 refresh_mode == WPS_REFRESH_ALL,
691 data->tokens[i].value.data);
692 break;
693 #endif
695 case WPS_TOKEN_ALIGN_LEFT:
696 case WPS_TOKEN_ALIGN_LEFT_RTL:
697 case WPS_TOKEN_ALIGN_CENTER:
698 case WPS_TOKEN_ALIGN_RIGHT:
699 case WPS_TOKEN_ALIGN_RIGHT_RTL:
700 /* remember where the current aligned text started */
701 switch (cur_align)
703 case WPS_ALIGN_LEFT:
704 align->left = cur_align_start;
705 break;
707 case WPS_ALIGN_CENTER:
708 align->center = cur_align_start;
709 break;
711 case WPS_ALIGN_RIGHT:
712 align->right = cur_align_start;
713 break;
715 /* start a new alignment */
716 switch (data->tokens[i].type)
718 case WPS_TOKEN_ALIGN_LEFT:
719 cur_align = WPS_ALIGN_LEFT;
720 break;
721 case WPS_TOKEN_ALIGN_LEFT_RTL:
722 cur_align = lang_is_rtl() ? WPS_ALIGN_RIGHT :
723 WPS_ALIGN_LEFT;
724 break;
725 case WPS_TOKEN_ALIGN_CENTER:
726 cur_align = WPS_ALIGN_CENTER;
727 break;
728 case WPS_TOKEN_ALIGN_RIGHT:
729 cur_align = WPS_ALIGN_RIGHT;
730 break;
731 case WPS_TOKEN_ALIGN_RIGHT_RTL:
732 cur_align = lang_is_rtl() ? WPS_ALIGN_LEFT :
733 WPS_ALIGN_RIGHT;
734 break;
735 default:
736 break;
738 *buf++ = 0;
739 cur_align_start = buf;
740 break;
741 case WPS_VIEWPORT_ENABLE:
743 char label = data->tokens[i].value.i;
744 char temp = VP_DRAW_HIDEABLE;
745 /* viewports are allowed to share id's so find and enable
746 * all of them */
747 struct skin_token_list *list = data->viewports;
748 while (list)
750 struct skin_viewport *vp =
751 (struct skin_viewport *)list->token->value.data;
752 if (vp->label == label)
754 if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
755 temp |= VP_DRAW_WASHIDDEN;
756 vp->hidden_flags = temp;
758 list = list->next;
761 break;
762 #ifdef HAVE_LCD_BITMAP
763 case WPS_VIEWPORT_CUSTOMLIST:
764 draw_playlist_viewer_list(gwps, data->tokens[i].value.data);
765 break;
766 #endif
767 default:
769 /* get the value of the tag and copy it to the buffer */
770 const char *value = get_token_value(gwps, &data->tokens[i],
771 temp_buf, sizeof(temp_buf), NULL);
772 if (value)
774 update = true;
775 while (*value && (buf < linebuf_end))
776 *buf++ = *value++;
778 break;
783 /* close the current alignment */
784 switch (cur_align)
786 case WPS_ALIGN_LEFT:
787 align->left = cur_align_start;
788 break;
790 case WPS_ALIGN_CENTER:
791 align->center = cur_align_start;
792 break;
794 case WPS_ALIGN_RIGHT:
795 align->right = cur_align_start;
796 break;
799 return update;
801 static void get_subline_timeout(struct gui_wps *gwps, struct skin_subline *subline)
803 struct wps_data *data = gwps->data;
804 int i;
805 subline->time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
807 for (i = subline->first_token_idx;
808 i <= subline->last_token_idx; i++)
810 switch(data->tokens[i].type)
812 case WPS_TOKEN_CONDITIONAL:
813 /* place ourselves in the right conditional case */
814 evaluate_conditional(gwps, &i);
815 break;
817 case WPS_TOKEN_CONDITIONAL_OPTION:
818 /* we've finished in the curent conditional case,
819 skip to the end of the conditional structure */
820 i = find_conditional_end(data, i);
821 break;
823 case WPS_TOKEN_SUBLINE_TIMEOUT:
824 subline->time_mult = data->tokens[i].value.i;
825 break;
827 default:
828 break;
833 /* Calculates which subline should be displayed for the specified line
834 Returns true iff the subline must be refreshed */
835 static bool update_curr_subline(struct gui_wps *gwps, struct skin_line *line)
837 /* shortcut this whole thing if we need to reset the line completly */
838 if (line->curr_subline == NULL)
840 line->subline_expire_time = current_tick;
841 line->curr_subline = &line->sublines;
842 if (!line->curr_subline->next)
844 line->subline_expire_time += 100*HZ;
846 else
848 get_subline_timeout(gwps, line->curr_subline);
849 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
851 return true;
853 /* if time to advance to next sub-line */
854 if (TIME_AFTER(current_tick, line->subline_expire_time - 1))
856 /* if there is only one subline, there is no need to search for a new one */
857 if (&line->sublines == line->curr_subline &&
858 line->curr_subline->next == NULL)
860 line->subline_expire_time += 100 * HZ;
861 return false;
863 if (line->curr_subline->next)
864 line->curr_subline = line->curr_subline->next;
865 else
866 line->curr_subline = &line->sublines;
867 get_subline_timeout(gwps, line->curr_subline);
868 line->subline_expire_time = current_tick + TIMEOUT_UNIT*line->curr_subline->time_mult;
869 return true;
871 return false;
874 /* Display a line appropriately according to its alignment format.
875 format_align contains the text, separated between left, center and right.
876 line is the index of the line on the screen.
877 scroll indicates whether the line is a scrolling one or not.
879 static void write_line(struct screen *display,
880 struct align_pos *format_align,
881 int line,
882 bool scroll)
884 int left_width = 0, left_xpos;
885 int center_width = 0, center_xpos;
886 int right_width = 0, right_xpos;
887 int ypos;
888 int space_width;
889 int string_height;
890 int scroll_width;
892 /* calculate different string sizes and positions */
893 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
894 if (format_align->left != 0) {
895 display->getstringsize((unsigned char *)format_align->left,
896 &left_width, &string_height);
899 if (format_align->right != 0) {
900 display->getstringsize((unsigned char *)format_align->right,
901 &right_width, &string_height);
904 if (format_align->center != 0) {
905 display->getstringsize((unsigned char *)format_align->center,
906 &center_width, &string_height);
909 left_xpos = 0;
910 right_xpos = (display->getwidth() - right_width);
911 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
913 scroll_width = display->getwidth() - left_xpos;
915 /* Checks for overlapping strings.
916 If needed the overlapping strings will be merged, separated by a
917 space */
919 /* CASE 1: left and centered string overlap */
920 /* there is a left string, need to merge left and center */
921 if ((left_width != 0 && center_width != 0) &&
922 (left_xpos + left_width + space_width > center_xpos)) {
923 /* replace the former separator '\0' of left and
924 center string with a space */
925 *(--format_align->center) = ' ';
926 /* calculate the new width and position of the merged string */
927 left_width = left_width + space_width + center_width;
928 /* there is no centered string anymore */
929 center_width = 0;
931 /* there is no left string, move center to left */
932 if ((left_width == 0 && center_width != 0) &&
933 (left_xpos + left_width > center_xpos)) {
934 /* move the center string to the left string */
935 format_align->left = format_align->center;
936 /* calculate the new width and position of the string */
937 left_width = center_width;
938 /* there is no centered string anymore */
939 center_width = 0;
942 /* CASE 2: centered and right string overlap */
943 /* there is a right string, need to merge center and right */
944 if ((center_width != 0 && right_width != 0) &&
945 (center_xpos + center_width + space_width > right_xpos)) {
946 /* replace the former separator '\0' of center and
947 right string with a space */
948 *(--format_align->right) = ' ';
949 /* move the center string to the right after merge */
950 format_align->right = format_align->center;
951 /* calculate the new width and position of the merged string */
952 right_width = center_width + space_width + right_width;
953 right_xpos = (display->getwidth() - right_width);
954 /* there is no centered string anymore */
955 center_width = 0;
957 /* there is no right string, move center to right */
958 if ((center_width != 0 && right_width == 0) &&
959 (center_xpos + center_width > right_xpos)) {
960 /* move the center string to the right string */
961 format_align->right = format_align->center;
962 /* calculate the new width and position of the string */
963 right_width = center_width;
964 right_xpos = (display->getwidth() - right_width);
965 /* there is no centered string anymore */
966 center_width = 0;
969 /* CASE 3: left and right overlap
970 There is no center string anymore, either there never
971 was one or it has been merged in case 1 or 2 */
972 /* there is a left string, need to merge left and right */
973 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
974 (left_xpos + left_width + space_width > right_xpos)) {
975 /* replace the former separator '\0' of left and
976 right string with a space */
977 *(--format_align->right) = ' ';
978 /* calculate the new width and position of the string */
979 left_width = left_width + space_width + right_width;
980 /* there is no right string anymore */
981 right_width = 0;
983 /* there is no left string, move right to left */
984 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
985 (left_width > right_xpos)) {
986 /* move the right string to the left string */
987 format_align->left = format_align->right;
988 /* calculate the new width and position of the string */
989 left_width = right_width;
990 /* there is no right string anymore */
991 right_width = 0;
994 ypos = (line * string_height);
997 if (scroll && ((left_width > scroll_width) ||
998 (center_width > scroll_width) ||
999 (right_width > scroll_width)))
1001 display->puts_scroll(0, line,
1002 (unsigned char *)format_align->left);
1004 else
1006 #ifdef HAVE_LCD_BITMAP
1007 /* clear the line first */
1008 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1009 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
1010 display->set_drawmode(DRMODE_SOLID);
1011 #endif
1013 /* Nasty hack: we output an empty scrolling string,
1014 which will reset the scroller for that line */
1015 display->puts_scroll(0, line, (unsigned char *)"");
1017 /* print aligned strings */
1018 if (left_width != 0)
1020 display->putsxy(left_xpos, ypos,
1021 (unsigned char *)format_align->left);
1023 if (center_width != 0)
1025 display->putsxy(center_xpos, ypos,
1026 (unsigned char *)format_align->center);
1028 if (right_width != 0)
1030 display->putsxy(right_xpos, ypos,
1031 (unsigned char *)format_align->right);
1036 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
1038 struct wps_data *data = gwps->data;
1039 struct screen *display = gwps->display;
1041 if (!data || !display || !gwps->state)
1042 return false;
1044 unsigned flags;
1045 char linebuf[MAX_PATH];
1047 struct align_pos align;
1048 align.left = NULL;
1049 align.center = NULL;
1050 align.right = NULL;
1053 struct skin_token_list *viewport_list;
1055 bool update_line, new_subline_refresh;
1057 #ifdef HAVE_LCD_BITMAP
1059 /* to find out wether the peak meter is enabled we
1060 assume it wasn't until we find a line that contains
1061 the peak meter. We can't use peak_meter_enabled itself
1062 because that would mean to turn off the meter thread
1063 temporarily. (That shouldn't matter unless yield
1064 or sleep is called but who knows...)
1066 bool enable_pm = false;
1068 #endif
1070 /* reset to first subline if refresh all flag is set */
1071 if (refresh_mode == WPS_REFRESH_ALL)
1073 struct skin_line *line;
1074 struct skin_viewport *skin_viewport = find_viewport(VP_DEFAULT_LABEL, data);
1076 if (!(skin_viewport->hidden_flags & VP_NEVER_VISIBLE))
1078 display->set_viewport(&skin_viewport->vp);
1079 display->clear_viewport();
1082 for (viewport_list = data->viewports;
1083 viewport_list; viewport_list = viewport_list->next)
1085 skin_viewport =
1086 (struct skin_viewport *)viewport_list->token->value.data;
1087 for(line = skin_viewport->lines; line; line = line->next)
1089 line->curr_subline = NULL;
1094 #ifdef HAVE_LCD_CHARCELLS
1095 int i;
1096 for (i = 0; i < 8; i++)
1098 if (data->wps_progress_pat[i] == 0)
1099 data->wps_progress_pat[i] = display->get_locked_pattern();
1101 #endif
1103 /* disable any viewports which are conditionally displayed.
1104 * If we are only refreshing the peak meter then don't change the viewport
1105 * enabled flags as this will stop scrolling. viewports cant be
1106 * toggled in this refresh mode anyway (FS#10215)*/
1107 if (refresh_mode != WPS_REFRESH_PEAK_METER)
1109 for (viewport_list = data->viewports;
1110 viewport_list; viewport_list = viewport_list->next)
1112 struct skin_viewport *skin_viewport =
1113 (struct skin_viewport *)viewport_list->token->value.data;
1114 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1116 continue;
1118 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
1120 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
1121 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1122 else
1123 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
1127 int viewport_count = 0;
1128 for (viewport_list = data->viewports;
1129 viewport_list; viewport_list = viewport_list->next, viewport_count++)
1131 struct skin_viewport *skin_viewport =
1132 (struct skin_viewport *)viewport_list->token->value.data;
1133 unsigned vp_refresh_mode = refresh_mode;
1135 display->set_viewport(&skin_viewport->vp);
1137 int hidden_vp = 0;
1139 #ifdef HAVE_LCD_BITMAP
1140 /* Set images to not to be displayed */
1141 struct skin_token_list *imglist = data->images;
1142 while (imglist)
1144 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
1145 img->display = -1;
1146 imglist = imglist->next;
1148 #endif
1149 /* dont redraw the viewport if its disabled */
1150 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1151 { /* don't draw anything into this one */
1152 vp_refresh_mode = 0; hidden_vp = true;
1154 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
1156 if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
1157 display->scroll_stop(&skin_viewport->vp);
1158 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1159 continue;
1161 else if (((skin_viewport->hidden_flags&
1162 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
1163 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
1165 vp_refresh_mode = WPS_REFRESH_ALL;
1166 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
1169 if (vp_refresh_mode == WPS_REFRESH_ALL)
1171 display->clear_viewport();
1174 /* loop over the lines for this viewport */
1175 struct skin_line *line;
1176 int line_count = 0;
1178 for (line = skin_viewport->lines; line; line = line->next, line_count++)
1180 struct skin_subline *subline;
1181 memset(linebuf, 0, sizeof(linebuf));
1182 update_line = false;
1184 /* get current subline for the line */
1185 new_subline_refresh = update_curr_subline(gwps, line);
1186 subline = line->curr_subline;
1187 flags = line->curr_subline->line_type;
1189 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1190 || new_subline_refresh || hidden_vp)
1192 /* get_line tells us if we need to update the line */
1193 update_line = get_line(gwps, subline, &align,
1194 linebuf, sizeof(linebuf), vp_refresh_mode);
1196 #ifdef HAVE_LCD_BITMAP
1197 /* peakmeter */
1198 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1200 /* the peakmeter should be alone on its line */
1201 update_line = false;
1203 int h = font_get(skin_viewport->vp.font)->height;
1204 int peak_meter_y = line_count* h;
1206 /* The user might decide to have the peak meter in the last
1207 line so that it is only displayed if no status bar is
1208 visible. If so we neither want do draw nor enable the
1209 peak meter. */
1210 if (peak_meter_y + h <= skin_viewport->vp.y+skin_viewport->vp.height) {
1211 /* found a line with a peak meter -> remember that we must
1212 enable it later */
1213 enable_pm = true;
1214 peak_meter_enabled = true;
1215 peak_meter_screen(gwps->display, 0, peak_meter_y,
1216 MIN(h, skin_viewport->vp.y+skin_viewport->vp.height - peak_meter_y));
1218 else
1220 peak_meter_enabled = false;
1224 #else /* HAVE_LCD_CHARCELL */
1226 /* progressbar */
1227 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1229 if (data->full_line_progressbar)
1230 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1231 else
1232 draw_player_progress(gwps);
1234 #endif
1236 if (update_line && !hidden_vp &&
1237 /* conditionals clear the line which means if the %Vd is put into the default
1238 viewport there will be a blank line.
1239 To get around this we dont allow any actual drawing to happen in the
1240 deault vp if other vp's are defined */
1241 ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
1242 !viewport_list->next))
1244 if (flags & WPS_REFRESH_SCROLL)
1246 /* if the line is a scrolling one we don't want to update
1247 too often, so that it has the time to scroll */
1248 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1249 write_line(display, &align, line_count, true);
1251 else
1252 write_line(display, &align, line_count, false);
1255 #ifdef HAVE_LCD_BITMAP
1256 /* progressbar */
1257 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1259 struct skin_token_list *bar = gwps->data->progressbars;
1260 while (bar)
1262 struct progressbar *thisbar = (struct progressbar*)bar->token->value.data;
1263 if (thisbar->vp == &skin_viewport->vp)
1265 draw_progressbar(gwps, thisbar);
1267 bar = bar->next;
1270 /* Now display any images in this viewport */
1271 if (!hidden_vp)
1272 wps_display_images(gwps, &skin_viewport->vp);
1273 #endif
1276 #ifdef HAVE_LCD_BITMAP
1277 data->peak_meter_enabled = enable_pm;
1278 #endif
1279 /* Restore the default viewport */
1280 display->set_viewport(NULL);
1282 display->update();
1284 return true;