make sure skins always draw with their backdrop, otherwise the backdrop only changes...
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
blobc1aebad87b6a4e66d6f116c6944b5ffcb5480994
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 #ifdef DEBUG
30 #include "debug.h"
31 #endif
32 #include "abrepeat.h"
33 #include "lang.h"
34 #include "language.h"
35 #include "statusbar.h"
36 #include "scrollbar.h"
37 #include "screen_access.h"
38 #include "playlist.h"
39 #include "audio.h"
41 #ifdef HAVE_LCD_BITMAP
42 #include "peakmeter.h"
43 /* Image stuff */
44 #include "bmp.h"
45 #ifdef HAVE_ALBUMART
46 #include "albumart.h"
47 #endif
48 #endif
50 #include "cuesheet.h"
51 #if CONFIG_CODEC == SWCODEC
52 #include "playback.h"
53 #endif
54 #include "backdrop.h"
55 #include "viewport.h"
58 #include "wps_internals.h"
59 #include "skin_engine.h"
60 #include "statusbar-skinned.h"
62 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode);
64 /* update a skinned screen, update_type is WPS_REFRESH_* values.
65 * Usually it should only be WPS_REFRESH_NON_STATIC
66 * A full update will be done if required (state.do_full_update == true)
68 bool skin_update(struct gui_wps *gwps, unsigned int update_type)
70 bool retval;
71 /* This maybe shouldnt be here, but while the skin is only used to
72 * display the music screen this is better than whereever we are being
73 * called from. This is also safe for skined screen which dont use the id3 */
74 struct mp3entry *id3 = gwps->state->id3;
75 bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
76 gwps->sync_data->do_full_update |= cuesheet_update;
78 retval = skin_redraw(gwps, gwps->sync_data->do_full_update ?
79 WPS_REFRESH_ALL : update_type);
80 return retval;
83 #ifdef HAVE_LCD_BITMAP
85 void skin_statusbar_changed(struct gui_wps *skin)
87 if (!skin)
88 return;
89 struct wps_data *data = skin->data;
90 const struct screen *display = skin->display;
91 const int screen = display->screen_type;
93 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, data)->vp;
94 viewport_set_defaults(vp, screen);
96 if (data->wps_sb_tag)
97 { /* fix up the default viewport */
98 if (data->show_sb_on_wps)
100 if (statusbar_position(screen) != STATUSBAR_OFF)
101 return; /* vp is fixed already */
103 vp->y = STATUSBAR_HEIGHT;
104 vp->height = display->lcdheight - STATUSBAR_HEIGHT;
106 else
108 if (statusbar_position(screen) == STATUSBAR_OFF)
109 return; /* vp is fixed already */
110 vp->y = vp->x = 0;
111 vp->height = display->lcdheight;
112 vp->width = display->lcdwidth;
117 static void draw_progressbar(struct gui_wps *gwps,
118 struct skin_viewport *wps_vp)
120 struct screen *display = gwps->display;
121 struct wps_state *state = gwps->state;
122 struct progressbar *pb = wps_vp->pb;
123 struct mp3entry *id3 = state->id3;
124 int y = pb->y;
126 if (y < 0)
128 int line_height = font_get(wps_vp->vp.font)->height;
129 /* center the pb in the line, but only if the line is higher than the pb */
130 int center = (line_height-pb->height)/2;
131 /* if Y was not set calculate by font height,Y is -line_number-1 */
132 y = (-y -1)*line_height + (0 > center ? 0 : center);
135 int elapsed, length;
136 if (id3)
138 elapsed = id3->elapsed;
139 length = id3->length;
141 else
143 elapsed = 0;
144 length = 0;
147 if (pb->have_bitmap_pb)
148 gui_bitmap_scrollbar_draw(display, pb->bm,
149 pb->x, y, pb->width, pb->bm.height,
150 length ? length : 1, 0,
151 length ? elapsed + state->ff_rewind_count : 0,
152 HORIZONTAL);
153 else
154 gui_scrollbar_draw(display, pb->x, y, pb->width, pb->height,
155 length ? length : 1, 0,
156 length ? elapsed + state->ff_rewind_count : 0,
157 HORIZONTAL);
158 #ifdef AB_REPEAT_ENABLE
159 if ( ab_repeat_mode_enabled() && length != 0 )
160 ab_draw_markers(display, length,
161 pb->x, pb->x + pb->width, y, pb->height);
162 #endif
164 if (id3 && id3->cuesheet)
165 cue_draw_markers(display, state->id3->cuesheet, length,
166 pb->x, pb->x + pb->width, y+1, pb->height-2);
168 bool audio_peek_track(struct mp3entry* id3, int offset);
169 static void draw_playlist_viewer_list(struct gui_wps *gwps,
170 struct playlistviewer *viewer)
172 struct wps_state *state = gwps->state;
173 int lines = viewport_get_nb_lines(viewer->vp);
174 int line_height = font_get(viewer->vp->font)->height;
175 int cur_playlist_pos = playlist_get_display_index();
176 int start_item = MAX(0, cur_playlist_pos + viewer->start_offset);
177 int i;
178 struct wps_token token;
180 struct mp3entry *pid3;
181 #if CONFIG_CODEC == SWCODEC
182 struct mp3entry id3;
183 #endif
184 char buf[MAX_PATH*2], tempbuf[MAX_PATH];
185 unsigned int buf_used = 0;
188 gwps->display->set_viewport(viewer->vp);
189 for(i=start_item; (i-start_item)<lines && i<playlist_amount(); i++)
191 if (i == cur_playlist_pos)
193 pid3 = state->id3;
195 else if (i == cur_playlist_pos+1)
197 pid3 = state->nid3;
199 #if CONFIG_CODEC == SWCODEC
200 else if ((i>cur_playlist_pos) && audio_peek_track(&id3, i-cur_playlist_pos))
202 pid3 = &id3;
204 #endif
205 else
207 pid3 = NULL;
210 int line = pid3 ? TRACK_HAS_INFO : TRACK_HAS_NO_INFO;
211 int j = 0, cur_string = 0;
212 char *filename = playlist_peek(i-cur_playlist_pos);
213 buf[0] = '\0';
214 buf_used = 0;
215 while (j < viewer->lines[line].count && (buf_used<sizeof(buf)))
217 const char *out = NULL;
218 token.type = viewer->lines[line].tokens[j];
219 token.value.i = 0;
220 token.next = false;
221 out = get_id3_token(&token, pid3, tempbuf, sizeof(tempbuf), -1, NULL);
222 if (out)
224 snprintf(&buf[buf_used], sizeof(buf)-buf_used, "%s", out);
225 buf_used += strlen(out);
226 j++;
227 continue;
229 switch (viewer->lines[line].tokens[j])
231 case WPS_TOKEN_STRING:
232 case WPS_TOKEN_CHARACTER:
233 snprintf(tempbuf, sizeof(tempbuf), "%s",
234 viewer->lines[line].strings[cur_string]);
235 cur_string++;
236 break;
237 case WPS_TOKEN_PLAYLIST_POSITION:
238 snprintf(tempbuf, sizeof(tempbuf), "%d", i);
239 break;
240 case WPS_TOKEN_FILE_NAME:
241 get_dir(tempbuf, sizeof(tempbuf), filename, 0);
242 break;
243 case WPS_TOKEN_FILE_PATH:
244 snprintf(tempbuf, sizeof(tempbuf), "%s", filename);
245 break;
246 default:
247 tempbuf[0] = '\0';
248 break;
250 if (tempbuf[0])
252 snprintf(&buf[buf_used], sizeof(buf)-buf_used, "%s", tempbuf);
253 buf_used += strlen(tempbuf);
255 j++;
258 if (viewer->lines[line].scroll)
260 gwps->display->puts_scroll(0, (i-start_item), buf );
262 else
264 gwps->display->putsxy(0, (i-start_item)*line_height, buf );
270 /* clears the area where the image was shown */
271 static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
273 if(!gwps)
274 return;
275 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
276 gwps->display->fillrect(img->x, img->y, img->bm.width, img->subimage_height);
277 gwps->display->set_drawmode(DRMODE_SOLID);
280 static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage)
282 struct screen *display = gwps->display;
283 if(img->always_display)
284 display->set_drawmode(DRMODE_FG);
285 else
286 display->set_drawmode(DRMODE_SOLID);
288 #if LCD_DEPTH > 1
289 if(img->bm.format == FORMAT_MONO) {
290 #endif
291 display->mono_bitmap_part(img->bm.data,
292 0, img->subimage_height * subimage,
293 img->bm.width, img->x,
294 img->y, img->bm.width,
295 img->subimage_height);
296 #if LCD_DEPTH > 1
297 } else {
298 display->transparent_bitmap_part((fb_data *)img->bm.data,
299 0, img->subimage_height * subimage,
300 STRIDE(display->screen_type,
301 img->bm.width, img->bm.height),
302 img->x, img->y, img->bm.width,
303 img->subimage_height);
305 #endif
308 static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
310 if(!gwps || !gwps->data || !gwps->display)
311 return;
313 struct wps_data *data = gwps->data;
314 struct screen *display = gwps->display;
315 struct skin_token_list *list = data->images;
317 while (list)
319 struct gui_img *img = (struct gui_img*)list->token->value.data;
320 if (img->loaded)
322 if (img->display >= 0)
324 wps_draw_image(gwps, img, img->display);
326 else if (img->always_display && img->vp == vp)
328 wps_draw_image(gwps, img, 0);
331 list = list->next;
333 #ifdef HAVE_ALBUMART
334 /* now draw the AA */
335 if (data->albumart && data->albumart->vp == vp
336 && data->albumart->draw)
338 draw_album_art(gwps, playback_current_aa_hid(data->playback_aa_slot),
339 false);
340 data->albumart->draw = false;
342 #endif
344 display->set_drawmode(DRMODE_SOLID);
347 #else /* HAVE_LCD_CHARCELL */
349 static bool draw_player_progress(struct gui_wps *gwps)
351 struct wps_state *state = gwps->state;
352 struct screen *display = gwps->display;
353 unsigned char progress_pattern[7];
354 int pos = 0;
355 int i;
357 int elapsed, length;
358 if (LIKELY(state->id3))
360 elapsed = state->id3->elapsed;
361 length = state->id3->length;
363 else
365 elapsed = 0;
366 length = 0;
369 if (length)
370 pos = 36 * (elapsed + state->ff_rewind_count) / length;
372 for (i = 0; i < 7; i++, pos -= 5)
374 if (pos <= 0)
375 progress_pattern[i] = 0x1fu;
376 else if (pos >= 5)
377 progress_pattern[i] = 0x00u;
378 else
379 progress_pattern[i] = 0x1fu >> pos;
382 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
383 return true;
386 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
388 static const unsigned char numbers[10][4] = {
389 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
390 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
391 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
392 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
393 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
394 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
395 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
396 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
397 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
398 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
401 struct wps_state *state = gwps->state;
402 struct screen *display = gwps->display;
403 struct wps_data *data = gwps->data;
404 unsigned char progress_pattern[7];
405 char timestr[10];
406 int time;
407 int time_idx = 0;
408 int pos = 0;
409 int pat_idx = 1;
410 int digit, i, j;
411 bool softchar;
413 int elapsed, length;
414 if (LIKELY(state->id3))
416 elapsed = state->id3->elapsed;
417 length = state->id3->length;
419 else
421 elapsed = 0;
422 length = 0;
425 if (buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
426 return;
428 time = elapsed + state->ff_rewind_count;
429 if (length)
430 pos = 55 * time / length;
432 memset(timestr, 0, sizeof(timestr));
433 format_time(timestr, sizeof(timestr)-2, time);
434 timestr[strlen(timestr)] = ':'; /* always safe */
436 for (i = 0; i < 11; i++, pos -= 5)
438 softchar = false;
439 memset(progress_pattern, 0, sizeof(progress_pattern));
441 if ((digit = timestr[time_idx]))
443 softchar = true;
444 digit -= '0';
446 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
448 memcpy(progress_pattern, numbers[digit], 4);
449 time_idx += 2;
451 else /* tens, shifted right */
453 for (j = 0; j < 4; j++)
454 progress_pattern[j] = numbers[digit][j] >> 1;
456 if (time_idx > 0) /* not the first group, add colon in front */
458 progress_pattern[1] |= 0x10u;
459 progress_pattern[3] |= 0x10u;
461 time_idx++;
464 if (pos >= 5)
465 progress_pattern[5] = progress_pattern[6] = 0x1fu;
468 if (pos > 0 && pos < 5)
470 softchar = true;
471 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
474 if (softchar && pat_idx < 8)
476 display->define_pattern(data->wps_progress_pat[pat_idx],
477 progress_pattern);
478 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
479 pat_idx++;
481 else if (pos <= 0)
482 buf = utf8encode(' ', buf);
483 else
484 buf = utf8encode(0xe115, buf); /* 2/7 _ */
486 *buf = '\0';
489 #endif /* HAVE_LCD_CHARCELL */
491 /* Return the index to the end token for the conditional token at index.
492 The conditional token can be either a start token or a separator
493 (i.e. option) token.
495 static int find_conditional_end(struct wps_data *data, int index)
497 int ret = index;
498 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
499 ret = data->tokens[ret].value.i;
501 /* ret now is the index to the end token for the conditional. */
502 return ret;
505 /* Evaluate the conditional that is at *token_index and return whether a skip
506 has ocurred. *token_index is updated with the new position.
508 static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
510 if (!gwps)
511 return false;
513 struct wps_data *data = gwps->data;
515 int i, cond_end;
516 int cond_index = *token_index;
517 char result[128];
518 const char *value;
519 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
520 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
522 /* treat ?xx<true> constructs as if they had 2 options. */
523 if (num_options < 2)
524 num_options = 2;
526 int intval = num_options;
527 /* get_token_value needs to know the number of options in the enum */
528 value = get_token_value(gwps, &data->tokens[cond_index + 1],
529 result, sizeof(result), &intval);
531 /* intval is now the number of the enum option we want to read,
532 starting from 1. If intval is -1, we check if value is empty. */
533 if (intval == -1)
534 intval = (value && *value) ? 1 : num_options;
535 else if (intval > num_options || intval < 1)
536 intval = num_options;
538 data->tokens[cond_index].value.i = (intval << 8) + num_options;
540 /* skip to the appropriate enum case */
541 int next = cond_index + 2;
542 for (i = 1; i < intval; i++)
544 next = data->tokens[next].value.i;
546 *token_index = next;
548 if (prev_val == intval)
550 /* Same conditional case as previously. Return without clearing the
551 pictures */
552 return false;
555 cond_end = find_conditional_end(data, cond_index + 2);
556 for (i = cond_index + 3; i < cond_end; i++)
558 #ifdef HAVE_LCD_BITMAP
559 /* clear all pictures in the conditional and nested ones */
560 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
561 clear_image_pos(gwps, find_image(data->tokens[i].value.i&0xFF, gwps->data));
562 #endif
563 #ifdef HAVE_ALBUMART
564 if (data->albumart && data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
566 draw_album_art(gwps,
567 playback_current_aa_hid(data->playback_aa_slot), true);
568 data->albumart->draw = false;
570 #endif
573 return true;
577 /* Read a (sub)line to the given alignment format buffer.
578 linebuf is the buffer where the data is actually stored.
579 align is the alignment format that'll be used to display the text.
580 The return value indicates whether the line needs to be updated.
582 static bool get_line(struct gui_wps *gwps,
583 struct skin_subline *subline,
584 struct align_pos *align,
585 char *linebuf,
586 int linebuf_size)
588 struct wps_data *data = gwps->data;
590 char temp_buf[128];
591 char *buf = linebuf; /* will always point to the writing position */
592 char *linebuf_end = linebuf + linebuf_size - 1;
593 bool update = false;
594 int i;
596 /* alignment-related variables */
597 int cur_align;
598 char* cur_align_start;
599 cur_align_start = buf;
600 cur_align = WPS_ALIGN_LEFT;
601 align->left = NULL;
602 align->center = NULL;
603 align->right = NULL;
604 /* Process all tokens of the desired subline */
605 for (i = subline->first_token_idx;
606 i <= subline->last_token_idx; i++)
608 switch(data->tokens[i].type)
610 case WPS_TOKEN_CONDITIONAL:
611 /* place ourselves in the right conditional case */
612 update |= evaluate_conditional(gwps, &i);
613 break;
615 case WPS_TOKEN_CONDITIONAL_OPTION:
616 /* we've finished in the curent conditional case,
617 skip to the end of the conditional structure */
618 i = find_conditional_end(data, i);
619 break;
621 #ifdef HAVE_LCD_BITMAP
622 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
624 char n = data->tokens[i].value.i & 0xFF;
625 int subimage = data->tokens[i].value.i >> 8;
626 struct gui_img *img = find_image(n, data);
628 if (img && img->loaded)
629 img->display = subimage;
630 break;
632 #endif
634 case WPS_TOKEN_ALIGN_LEFT:
635 case WPS_TOKEN_ALIGN_LEFT_RTL:
636 case WPS_TOKEN_ALIGN_CENTER:
637 case WPS_TOKEN_ALIGN_RIGHT:
638 case WPS_TOKEN_ALIGN_RIGHT_RTL:
639 /* remember where the current aligned text started */
640 switch (cur_align)
642 case WPS_ALIGN_LEFT:
643 align->left = cur_align_start;
644 break;
646 case WPS_ALIGN_CENTER:
647 align->center = cur_align_start;
648 break;
650 case WPS_ALIGN_RIGHT:
651 align->right = cur_align_start;
652 break;
654 /* start a new alignment */
655 switch (data->tokens[i].type)
657 case WPS_TOKEN_ALIGN_LEFT:
658 cur_align = WPS_ALIGN_LEFT;
659 break;
660 case WPS_TOKEN_ALIGN_LEFT_RTL:
661 cur_align = lang_is_rtl() ? WPS_ALIGN_RIGHT :
662 WPS_ALIGN_LEFT;
663 break;
664 case WPS_TOKEN_ALIGN_CENTER:
665 cur_align = WPS_ALIGN_CENTER;
666 break;
667 case WPS_TOKEN_ALIGN_RIGHT:
668 cur_align = WPS_ALIGN_RIGHT;
669 break;
670 case WPS_TOKEN_ALIGN_RIGHT_RTL:
671 cur_align = lang_is_rtl() ? WPS_ALIGN_LEFT :
672 WPS_ALIGN_RIGHT;
673 break;
674 default:
675 break;
677 *buf++ = 0;
678 cur_align_start = buf;
679 break;
680 case WPS_VIEWPORT_ENABLE:
682 char label = data->tokens[i].value.i;
683 char temp = VP_DRAW_HIDEABLE;
684 /* viewports are allowed to share id's so find and enable
685 * all of them */
686 struct skin_token_list *list = data->viewports;
687 while (list)
689 struct skin_viewport *vp =
690 (struct skin_viewport *)list->token->value.data;
691 if (vp->label == label)
693 if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
694 temp |= VP_DRAW_WASHIDDEN;
695 vp->hidden_flags = temp;
697 list = list->next;
700 break;
701 #ifdef HAVE_LCD_BITMAP
702 case WPS_VIEWPORT_CUSTOMLIST:
703 draw_playlist_viewer_list(gwps, data->tokens[i].value.data);
704 break;
705 #endif
706 default:
708 /* get the value of the tag and copy it to the buffer */
709 const char *value = get_token_value(gwps, &data->tokens[i],
710 temp_buf, sizeof(temp_buf), NULL);
711 if (value)
713 update = true;
714 while (*value && (buf < linebuf_end))
715 *buf++ = *value++;
717 break;
722 /* close the current alignment */
723 switch (cur_align)
725 case WPS_ALIGN_LEFT:
726 align->left = cur_align_start;
727 break;
729 case WPS_ALIGN_CENTER:
730 align->center = cur_align_start;
731 break;
733 case WPS_ALIGN_RIGHT:
734 align->right = cur_align_start;
735 break;
738 return update;
740 static void get_subline_timeout(struct gui_wps *gwps, struct skin_subline *subline)
742 struct wps_data *data = gwps->data;
743 int i;
744 subline->time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
746 for (i = subline->first_token_idx;
747 i <= subline->last_token_idx; i++)
749 switch(data->tokens[i].type)
751 case WPS_TOKEN_CONDITIONAL:
752 /* place ourselves in the right conditional case */
753 evaluate_conditional(gwps, &i);
754 break;
756 case WPS_TOKEN_CONDITIONAL_OPTION:
757 /* we've finished in the curent conditional case,
758 skip to the end of the conditional structure */
759 i = find_conditional_end(data, i);
760 break;
762 case WPS_TOKEN_SUBLINE_TIMEOUT:
763 subline->time_mult = data->tokens[i].value.i;
764 break;
766 default:
767 break;
772 /* Calculates which subline should be displayed for the specified line
773 Returns true iff the subline must be refreshed */
774 static bool update_curr_subline(struct gui_wps *gwps, struct skin_line *line)
776 /* shortcut this whole thing if we need to reset the line completly */
777 if (line->curr_subline == NULL)
779 line->subline_expire_time = current_tick;
780 line->curr_subline = &line->sublines;
781 if (!line->curr_subline->next)
783 line->subline_expire_time += 100*HZ;
785 else
787 get_subline_timeout(gwps, line->curr_subline);
788 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
790 return true;
792 /* if time to advance to next sub-line */
793 if (TIME_AFTER(current_tick, line->subline_expire_time - 1))
795 /* if there is only one subline, there is no need to search for a new one */
796 if (&line->sublines == line->curr_subline &&
797 line->curr_subline->next == NULL)
799 line->subline_expire_time += 100 * HZ;
800 return false;
802 if (line->curr_subline->next)
803 line->curr_subline = line->curr_subline->next;
804 else
805 line->curr_subline = &line->sublines;
806 get_subline_timeout(gwps, line->curr_subline);
807 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
808 return true;
810 return false;
813 /* Display a line appropriately according to its alignment format.
814 format_align contains the text, separated between left, center and right.
815 line is the index of the line on the screen.
816 scroll indicates whether the line is a scrolling one or not.
818 static void write_line(struct screen *display,
819 struct align_pos *format_align,
820 int line,
821 bool scroll)
823 int left_width = 0, left_xpos;
824 int center_width = 0, center_xpos;
825 int right_width = 0, right_xpos;
826 int ypos;
827 int space_width;
828 int string_height;
829 int scroll_width;
831 /* calculate different string sizes and positions */
832 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
833 if (format_align->left != 0) {
834 display->getstringsize((unsigned char *)format_align->left,
835 &left_width, &string_height);
838 if (format_align->right != 0) {
839 display->getstringsize((unsigned char *)format_align->right,
840 &right_width, &string_height);
843 if (format_align->center != 0) {
844 display->getstringsize((unsigned char *)format_align->center,
845 &center_width, &string_height);
848 left_xpos = 0;
849 right_xpos = (display->getwidth() - right_width);
850 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
852 scroll_width = display->getwidth() - left_xpos;
854 /* Checks for overlapping strings.
855 If needed the overlapping strings will be merged, separated by a
856 space */
858 /* CASE 1: left and centered string overlap */
859 /* there is a left string, need to merge left and center */
860 if ((left_width != 0 && center_width != 0) &&
861 (left_xpos + left_width + space_width > center_xpos)) {
862 /* replace the former separator '\0' of left and
863 center string with a space */
864 *(--format_align->center) = ' ';
865 /* calculate the new width and position of the merged string */
866 left_width = left_width + space_width + center_width;
867 /* there is no centered string anymore */
868 center_width = 0;
870 /* there is no left string, move center to left */
871 if ((left_width == 0 && center_width != 0) &&
872 (left_xpos + left_width > center_xpos)) {
873 /* move the center string to the left string */
874 format_align->left = format_align->center;
875 /* calculate the new width and position of the string */
876 left_width = center_width;
877 /* there is no centered string anymore */
878 center_width = 0;
881 /* CASE 2: centered and right string overlap */
882 /* there is a right string, need to merge center and right */
883 if ((center_width != 0 && right_width != 0) &&
884 (center_xpos + center_width + space_width > right_xpos)) {
885 /* replace the former separator '\0' of center and
886 right string with a space */
887 *(--format_align->right) = ' ';
888 /* move the center string to the right after merge */
889 format_align->right = format_align->center;
890 /* calculate the new width and position of the merged string */
891 right_width = center_width + space_width + right_width;
892 right_xpos = (display->getwidth() - right_width);
893 /* there is no centered string anymore */
894 center_width = 0;
896 /* there is no right string, move center to right */
897 if ((center_width != 0 && right_width == 0) &&
898 (center_xpos + center_width > right_xpos)) {
899 /* move the center string to the right string */
900 format_align->right = format_align->center;
901 /* calculate the new width and position of the string */
902 right_width = center_width;
903 right_xpos = (display->getwidth() - right_width);
904 /* there is no centered string anymore */
905 center_width = 0;
908 /* CASE 3: left and right overlap
909 There is no center string anymore, either there never
910 was one or it has been merged in case 1 or 2 */
911 /* there is a left string, need to merge left and right */
912 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
913 (left_xpos + left_width + space_width > right_xpos)) {
914 /* replace the former separator '\0' of left and
915 right string with a space */
916 *(--format_align->right) = ' ';
917 /* calculate the new width and position of the string */
918 left_width = left_width + space_width + right_width;
919 /* there is no right string anymore */
920 right_width = 0;
922 /* there is no left string, move right to left */
923 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
924 (left_width > right_xpos)) {
925 /* move the right string to the left string */
926 format_align->left = format_align->right;
927 /* calculate the new width and position of the string */
928 left_width = right_width;
929 /* there is no right string anymore */
930 right_width = 0;
933 ypos = (line * string_height);
936 if (scroll && ((left_width > scroll_width) ||
937 (center_width > scroll_width) ||
938 (right_width > scroll_width)))
940 display->puts_scroll(0, line,
941 (unsigned char *)format_align->left);
943 else
945 #ifdef HAVE_LCD_BITMAP
946 /* clear the line first */
947 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
948 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
949 display->set_drawmode(DRMODE_SOLID);
950 #endif
952 /* Nasty hack: we output an empty scrolling string,
953 which will reset the scroller for that line */
954 display->puts_scroll(0, line, (unsigned char *)"");
956 /* print aligned strings */
957 if (left_width != 0)
959 display->putsxy(left_xpos, ypos,
960 (unsigned char *)format_align->left);
962 if (center_width != 0)
964 display->putsxy(center_xpos, ypos,
965 (unsigned char *)format_align->center);
967 if (right_width != 0)
969 display->putsxy(right_xpos, ypos,
970 (unsigned char *)format_align->right);
975 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
977 struct wps_data *data = gwps->data;
978 struct screen *display = gwps->display;
980 if (!data || !display || !gwps->state)
981 return false;
983 unsigned flags;
984 char linebuf[MAX_PATH];
986 struct align_pos align;
987 align.left = NULL;
988 align.center = NULL;
989 align.right = NULL;
992 struct skin_token_list *viewport_list;
994 bool update_line, new_subline_refresh;
996 #ifdef HAVE_LCD_BITMAP
998 /* to find out wether the peak meter is enabled we
999 assume it wasn't until we find a line that contains
1000 the peak meter. We can't use peak_meter_enabled itself
1001 because that would mean to turn off the meter thread
1002 temporarily. (That shouldn't matter unless yield
1003 or sleep is called but who knows...)
1005 bool enable_pm = false;
1007 #endif
1009 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
1010 display->backdrop_show(data->backdrop);
1011 #endif
1014 /* reset to first subline if refresh all flag is set */
1015 if (refresh_mode == WPS_REFRESH_ALL)
1017 struct skin_line *line;
1018 struct skin_viewport *skin_viewport = find_viewport(VP_DEFAULT_LABEL, data);
1020 if (!(skin_viewport->hidden_flags & VP_NEVER_VISIBLE))
1022 display->set_viewport(&skin_viewport->vp);
1023 display->clear_viewport();
1026 for (viewport_list = data->viewports;
1027 viewport_list; viewport_list = viewport_list->next)
1029 skin_viewport =
1030 (struct skin_viewport *)viewport_list->token->value.data;
1031 for(line = skin_viewport->lines; line; line = line->next)
1033 line->curr_subline = NULL;
1038 #ifdef HAVE_LCD_CHARCELLS
1039 int i;
1040 for (i = 0; i < 8; i++)
1042 if (data->wps_progress_pat[i] == 0)
1043 data->wps_progress_pat[i] = display->get_locked_pattern();
1045 #endif
1047 /* disable any viewports which are conditionally displayed.
1048 * If we are only refreshing the peak meter then don't change the viewport
1049 * enabled flags as this will stop scrolling. viewports cant be
1050 * toggled in this refresh mode anyway (FS#10215)*/
1051 if (refresh_mode != WPS_REFRESH_PEAK_METER)
1053 for (viewport_list = data->viewports;
1054 viewport_list; viewport_list = viewport_list->next)
1056 struct skin_viewport *skin_viewport =
1057 (struct skin_viewport *)viewport_list->token->value.data;
1058 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1060 continue;
1062 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
1064 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
1065 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1066 else
1067 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
1071 int viewport_count = 0;
1072 for (viewport_list = data->viewports;
1073 viewport_list; viewport_list = viewport_list->next, viewport_count++)
1075 struct skin_viewport *skin_viewport =
1076 (struct skin_viewport *)viewport_list->token->value.data;
1077 unsigned vp_refresh_mode = refresh_mode;
1079 display->set_viewport(&skin_viewport->vp);
1081 int hidden_vp = 0;
1083 #ifdef HAVE_LCD_BITMAP
1084 /* Set images to not to be displayed */
1085 struct skin_token_list *imglist = data->images;
1086 while (imglist)
1088 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
1089 img->display = -1;
1090 imglist = imglist->next;
1092 #endif
1093 /* dont redraw the viewport if its disabled */
1094 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1095 { /* don't draw anything into this one */
1096 vp_refresh_mode = 0; hidden_vp = true;
1098 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
1100 if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
1101 display->scroll_stop(&skin_viewport->vp);
1102 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1103 continue;
1105 else if (((skin_viewport->hidden_flags&
1106 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
1107 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
1109 vp_refresh_mode = WPS_REFRESH_ALL;
1110 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
1113 if (vp_refresh_mode == WPS_REFRESH_ALL)
1115 display->clear_viewport();
1118 /* loop over the lines for this viewport */
1119 struct skin_line *line;
1120 int line_count = 0;
1122 for (line = skin_viewport->lines; line; line = line->next, line_count++)
1124 struct skin_subline *subline;
1125 memset(linebuf, 0, sizeof(linebuf));
1126 update_line = false;
1128 /* get current subline for the line */
1129 new_subline_refresh = update_curr_subline(gwps, line);
1130 subline = line->curr_subline;
1131 flags = line->curr_subline->line_type;
1133 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1134 || new_subline_refresh || hidden_vp)
1136 /* get_line tells us if we need to update the line */
1137 update_line = get_line(gwps, subline,
1138 &align, linebuf, sizeof(linebuf));
1140 #ifdef HAVE_LCD_BITMAP
1141 /* peakmeter */
1142 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1144 /* the peakmeter should be alone on its line */
1145 update_line = false;
1147 int h = font_get(skin_viewport->vp.font)->height;
1148 int peak_meter_y = line_count* h;
1150 /* The user might decide to have the peak meter in the last
1151 line so that it is only displayed if no status bar is
1152 visible. If so we neither want do draw nor enable the
1153 peak meter. */
1154 if (peak_meter_y + h <= skin_viewport->vp.y+skin_viewport->vp.height) {
1155 /* found a line with a peak meter -> remember that we must
1156 enable it later */
1157 enable_pm = true;
1158 peak_meter_enabled = true;
1159 peak_meter_screen(gwps->display, 0, peak_meter_y,
1160 MIN(h, skin_viewport->vp.y+skin_viewport->vp.height - peak_meter_y));
1162 else
1164 peak_meter_enabled = false;
1168 #else /* HAVE_LCD_CHARCELL */
1170 /* progressbar */
1171 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1173 if (data->full_line_progressbar)
1174 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1175 else
1176 draw_player_progress(gwps);
1178 #endif
1180 if (update_line && !hidden_vp &&
1181 /* conditionals clear the line which means if the %Vd is put into the default
1182 viewport there will be a blank line.
1183 To get around this we dont allow any actual drawing to happen in the
1184 deault vp if other vp's are defined */
1185 ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
1186 !viewport_list->next))
1188 if (flags & WPS_REFRESH_SCROLL)
1190 /* if the line is a scrolling one we don't want to update
1191 too often, so that it has the time to scroll */
1192 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1193 write_line(display, &align, line_count, true);
1195 else
1196 write_line(display, &align, line_count, false);
1199 #ifdef HAVE_LCD_BITMAP
1200 /* progressbar */
1201 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1203 if (skin_viewport->pb)
1205 draw_progressbar(gwps, skin_viewport);
1208 /* Now display any images in this viewport */
1209 if (!hidden_vp)
1210 wps_display_images(gwps, &skin_viewport->vp);
1211 #endif
1214 #ifdef HAVE_LCD_BITMAP
1215 data->peak_meter_enabled = enable_pm;
1216 #endif
1217 /* Restore the default viewport */
1218 display->set_viewport(NULL);
1220 display->update();
1222 return true;