Move find_viewport() and find_image() to skin_parser.c where the reset of the linked...
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
blob51f86e568681af103b2f10418375e42d03d3bcda
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 "statusbar.h"
35 #include "scrollbar.h"
36 #include "screen_access.h"
38 #ifdef HAVE_LCD_BITMAP
39 #include "peakmeter.h"
40 /* Image stuff */
41 #include "bmp.h"
42 #ifdef HAVE_ALBUMART
43 #include "albumart.h"
44 #endif
45 #endif
47 #include "cuesheet.h"
48 #if CONFIG_CODEC == SWCODEC
49 #include "playback.h"
50 #endif
51 #include "backdrop.h"
52 #include "viewport.h"
55 #include "wps_internals.h"
56 #include "skin_engine.h"
57 #include "statusbar-skinned.h"
59 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode);
62 /* TODO: maybe move this whole function into wps.c instead ? */
63 bool gui_wps_display(struct gui_wps *gwps)
65 struct screen *display = gwps->display;
67 /* Update the values in the first (default) viewport - in case the user
68 has modified the statusbar or colour settings */
69 #if LCD_DEPTH > 1
70 if (display->depth > 1)
72 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, gwps->data)->vp;
73 vp->fg_pattern = display->get_foreground();
74 vp->bg_pattern = display->get_background();
76 #endif
77 display->backdrop_show(BACKDROP_SKIN_WPS);
78 return skin_redraw(gwps, WPS_REFRESH_ALL);
81 /* update a skinned screen, update_type is WPS_REFRESH_* values.
82 * Usually it should only be WPS_REFRESH_NON_STATIC
83 * A full update will be done if required (state.do_full_update == true)
85 bool skin_update(struct gui_wps *gwps, unsigned int update_type)
87 bool retval;
88 /* This maybe shouldnt be here, but while the skin is only used to
89 * display the music screen this is better than whereever we are being
90 * called from. This is also safe for skined screen which dont use the id3 */
91 struct mp3entry *id3 = gwps->state->id3;
92 bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
93 gwps->sync_data->do_full_update |= cuesheet_update;
95 retval = skin_redraw(gwps, gwps->sync_data->do_full_update ?
96 WPS_REFRESH_ALL : update_type);
97 return retval;
100 #ifdef HAVE_LCD_BITMAP
102 void skin_statusbar_changed(struct gui_wps *skin)
104 if (!skin)
105 return;
106 struct wps_data *data = skin->data;
107 const struct screen *display = skin->display;
108 const int screen = display->screen_type;
110 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, data)->vp;
111 viewport_set_fullscreen(vp, screen);
113 if (data->wps_sb_tag)
114 { /* fix up the default viewport */
115 if (data->show_sb_on_wps)
117 if (statusbar_position(screen) != STATUSBAR_OFF)
118 return; /* vp is fixed already */
120 vp->y = STATUSBAR_HEIGHT;
121 vp->height = display->lcdheight - STATUSBAR_HEIGHT;
123 else
125 if (statusbar_position(screen) == STATUSBAR_OFF)
126 return; /* vp is fixed already */
127 vp->y = vp->x = 0;
128 vp->height = display->lcdheight;
129 vp->width = display->lcdwidth;
134 static void draw_progressbar(struct gui_wps *gwps,
135 struct skin_viewport *wps_vp)
137 struct screen *display = gwps->display;
138 struct wps_state *state = gwps->state;
139 struct progressbar *pb = wps_vp->pb;
140 struct mp3entry *id3 = state->id3;
141 int y = pb->y;
143 if (y < 0)
145 int line_height = font_get(wps_vp->vp.font)->height;
146 /* center the pb in the line, but only if the line is higher than the pb */
147 int center = (line_height-pb->height)/2;
148 /* if Y was not set calculate by font height,Y is -line_number-1 */
149 y = (-y -1)*line_height + (0 > center ? 0 : center);
152 int elapsed, length;
153 if (id3)
155 elapsed = id3->elapsed;
156 length = id3->length;
158 else
160 elapsed = 0;
161 length = 0;
164 if (pb->have_bitmap_pb)
165 gui_bitmap_scrollbar_draw(display, pb->bm,
166 pb->x, y, pb->width, pb->bm.height,
167 length ? length : 1, 0,
168 length ? elapsed + state->ff_rewind_count : 0,
169 HORIZONTAL);
170 else
171 gui_scrollbar_draw(display, pb->x, y, pb->width, pb->height,
172 length ? length : 1, 0,
173 length ? elapsed + state->ff_rewind_count : 0,
174 HORIZONTAL);
175 #ifdef AB_REPEAT_ENABLE
176 if ( ab_repeat_mode_enabled() && length != 0 )
177 ab_draw_markers(display, length,
178 pb->x, pb->x + pb->width, y, pb->height);
179 #endif
181 if (id3 && id3->cuesheet)
182 cue_draw_markers(display, state->id3->cuesheet, length,
183 pb->x, pb->x + pb->width, y+1, pb->height-2);
186 /* clears the area where the image was shown */
187 static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
189 if(!gwps)
190 return;
191 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
192 gwps->display->fillrect(img->x, img->y, img->bm.width, img->subimage_height);
193 gwps->display->set_drawmode(DRMODE_SOLID);
196 static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage)
198 struct screen *display = gwps->display;
199 if(img->always_display)
200 display->set_drawmode(DRMODE_FG);
201 else
202 display->set_drawmode(DRMODE_SOLID);
204 #if LCD_DEPTH > 1
205 if(img->bm.format == FORMAT_MONO) {
206 #endif
207 display->mono_bitmap_part(img->bm.data,
208 0, img->subimage_height * subimage,
209 img->bm.width, img->x,
210 img->y, img->bm.width,
211 img->subimage_height);
212 #if LCD_DEPTH > 1
213 } else {
214 display->transparent_bitmap_part((fb_data *)img->bm.data,
215 0, img->subimage_height * subimage,
216 STRIDE(display->screen_type,
217 img->bm.width, img->bm.height),
218 img->x, img->y, img->bm.width,
219 img->subimage_height);
221 #endif
224 static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
226 if(!gwps || !gwps->data || !gwps->display)
227 return;
229 struct wps_data *data = gwps->data;
230 struct screen *display = gwps->display;
231 struct skin_token_list *list = data->images;
233 while (list)
235 struct gui_img *img = (struct gui_img*)list->token->value.data;
236 if (img->loaded)
238 if (img->display >= 0)
240 wps_draw_image(gwps, img, img->display);
242 else if (img->always_display && img->vp == vp)
244 wps_draw_image(gwps, img, 0);
247 list = list->next;
249 #ifdef HAVE_ALBUMART
250 /* now draw the AA */
251 if (data->albumart && data->albumart->vp == vp
252 && data->albumart->draw)
254 draw_album_art(gwps, playback_current_aa_hid(data->playback_aa_slot),
255 false);
256 data->albumart->draw = false;
258 #endif
260 display->set_drawmode(DRMODE_SOLID);
263 #else /* HAVE_LCD_CHARCELL */
265 static bool draw_player_progress(struct gui_wps *gwps)
267 struct wps_state *state = gwps->state;
268 struct screen *display = gwps->display;
269 unsigned char progress_pattern[7];
270 int pos = 0;
271 int i;
273 int elapsed, length;
274 if (LIKELY(state->id3))
276 elapsed = state->id3->elapsed;
277 length = state->id3->length;
279 else
281 elapsed = 0;
282 length = 0;
285 if (length)
286 pos = 36 * (elapsed + state->ff_rewind_count) / length;
288 for (i = 0; i < 7; i++, pos -= 5)
290 if (pos <= 0)
291 progress_pattern[i] = 0x1fu;
292 else if (pos >= 5)
293 progress_pattern[i] = 0x00u;
294 else
295 progress_pattern[i] = 0x1fu >> pos;
298 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
299 return true;
302 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
304 static const unsigned char numbers[10][4] = {
305 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
306 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
307 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
308 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
309 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
310 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
311 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
312 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
313 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
314 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
317 struct wps_state *state = gwps->state;
318 struct screen *display = gwps->display;
319 struct wps_data *data = gwps->data;
320 unsigned char progress_pattern[7];
321 char timestr[10];
322 int time;
323 int time_idx = 0;
324 int pos = 0;
325 int pat_idx = 1;
326 int digit, i, j;
327 bool softchar;
329 int elapsed, length;
330 if (LIKELY(state->id3))
332 elapsed = state->id3->elapsed;
333 length = state->id3->length;
335 else
337 elapsed = 0;
338 length = 0;
341 if (buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
342 return;
344 time = elapsed + state->ff_rewind_count;
345 if (length)
346 pos = 55 * time / length;
348 memset(timestr, 0, sizeof(timestr));
349 format_time(timestr, sizeof(timestr)-2, time);
350 timestr[strlen(timestr)] = ':'; /* always safe */
352 for (i = 0; i < 11; i++, pos -= 5)
354 softchar = false;
355 memset(progress_pattern, 0, sizeof(progress_pattern));
357 if ((digit = timestr[time_idx]))
359 softchar = true;
360 digit -= '0';
362 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
364 memcpy(progress_pattern, numbers[digit], 4);
365 time_idx += 2;
367 else /* tens, shifted right */
369 for (j = 0; j < 4; j++)
370 progress_pattern[j] = numbers[digit][j] >> 1;
372 if (time_idx > 0) /* not the first group, add colon in front */
374 progress_pattern[1] |= 0x10u;
375 progress_pattern[3] |= 0x10u;
377 time_idx++;
380 if (pos >= 5)
381 progress_pattern[5] = progress_pattern[6] = 0x1fu;
384 if (pos > 0 && pos < 5)
386 softchar = true;
387 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
390 if (softchar && pat_idx < 8)
392 display->define_pattern(data->wps_progress_pat[pat_idx],
393 progress_pattern);
394 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
395 pat_idx++;
397 else if (pos <= 0)
398 buf = utf8encode(' ', buf);
399 else
400 buf = utf8encode(0xe115, buf); /* 2/7 _ */
402 *buf = '\0';
405 #endif /* HAVE_LCD_CHARCELL */
407 /* Return the index to the end token for the conditional token at index.
408 The conditional token can be either a start token or a separator
409 (i.e. option) token.
411 static int find_conditional_end(struct wps_data *data, int index)
413 int ret = index;
414 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
415 ret = data->tokens[ret].value.i;
417 /* ret now is the index to the end token for the conditional. */
418 return ret;
421 /* Evaluate the conditional that is at *token_index and return whether a skip
422 has ocurred. *token_index is updated with the new position.
424 static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
426 if (!gwps)
427 return false;
429 struct wps_data *data = gwps->data;
431 int i, cond_end;
432 int cond_index = *token_index;
433 char result[128];
434 const char *value;
435 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
436 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
438 /* treat ?xx<true> constructs as if they had 2 options. */
439 if (num_options < 2)
440 num_options = 2;
442 int intval = num_options;
443 /* get_token_value needs to know the number of options in the enum */
444 value = get_token_value(gwps, &data->tokens[cond_index + 1],
445 result, sizeof(result), &intval);
447 /* intval is now the number of the enum option we want to read,
448 starting from 1. If intval is -1, we check if value is empty. */
449 if (intval == -1)
450 intval = (value && *value) ? 1 : num_options;
451 else if (intval > num_options || intval < 1)
452 intval = num_options;
454 data->tokens[cond_index].value.i = (intval << 8) + num_options;
456 /* skip to the appropriate enum case */
457 int next = cond_index + 2;
458 for (i = 1; i < intval; i++)
460 next = data->tokens[next].value.i;
462 *token_index = next;
464 if (prev_val == intval)
466 /* Same conditional case as previously. Return without clearing the
467 pictures */
468 return false;
471 cond_end = find_conditional_end(data, cond_index + 2);
472 for (i = cond_index + 3; i < cond_end; i++)
474 #ifdef HAVE_LCD_BITMAP
475 /* clear all pictures in the conditional and nested ones */
476 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
477 clear_image_pos(gwps, find_image(data->tokens[i].value.i&0xFF, gwps->data));
478 #endif
479 #ifdef HAVE_ALBUMART
480 if (data->albumart && data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
482 draw_album_art(gwps,
483 playback_current_aa_hid(data->playback_aa_slot), true);
484 data->albumart->draw = false;
486 #endif
489 return true;
493 /* Read a (sub)line to the given alignment format buffer.
494 linebuf is the buffer where the data is actually stored.
495 align is the alignment format that'll be used to display the text.
496 The return value indicates whether the line needs to be updated.
498 static bool get_line(struct gui_wps *gwps,
499 struct skin_subline *subline,
500 struct align_pos *align,
501 char *linebuf,
502 int linebuf_size)
504 struct wps_data *data = gwps->data;
506 char temp_buf[128];
507 char *buf = linebuf; /* will always point to the writing position */
508 char *linebuf_end = linebuf + linebuf_size - 1;
509 bool update = false;
510 int i;
512 /* alignment-related variables */
513 int cur_align;
514 char* cur_align_start;
515 cur_align_start = buf;
516 cur_align = WPS_ALIGN_LEFT;
517 align->left = NULL;
518 align->center = NULL;
519 align->right = NULL;
520 /* Process all tokens of the desired subline */
521 for (i = subline->first_token_idx;
522 i <= subline->last_token_idx; i++)
524 switch(data->tokens[i].type)
526 case WPS_TOKEN_CONDITIONAL:
527 /* place ourselves in the right conditional case */
528 update |= evaluate_conditional(gwps, &i);
529 break;
531 case WPS_TOKEN_CONDITIONAL_OPTION:
532 /* we've finished in the curent conditional case,
533 skip to the end of the conditional structure */
534 i = find_conditional_end(data, i);
535 break;
537 #ifdef HAVE_LCD_BITMAP
538 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
540 char n = data->tokens[i].value.i & 0xFF;
541 int subimage = data->tokens[i].value.i >> 8;
542 struct gui_img *img = find_image(n, data);
544 if (img && img->loaded)
545 img->display = subimage;
546 break;
548 #endif
550 case WPS_TOKEN_ALIGN_LEFT:
551 case WPS_TOKEN_ALIGN_CENTER:
552 case WPS_TOKEN_ALIGN_RIGHT:
553 /* remember where the current aligned text started */
554 switch (cur_align)
556 case WPS_ALIGN_LEFT:
557 align->left = cur_align_start;
558 break;
560 case WPS_ALIGN_CENTER:
561 align->center = cur_align_start;
562 break;
564 case WPS_ALIGN_RIGHT:
565 align->right = cur_align_start;
566 break;
568 /* start a new alignment */
569 switch (data->tokens[i].type)
571 case WPS_TOKEN_ALIGN_LEFT:
572 cur_align = WPS_ALIGN_LEFT;
573 break;
574 case WPS_TOKEN_ALIGN_CENTER:
575 cur_align = WPS_ALIGN_CENTER;
576 break;
577 case WPS_TOKEN_ALIGN_RIGHT:
578 cur_align = WPS_ALIGN_RIGHT;
579 break;
580 default:
581 break;
583 *buf++ = 0;
584 cur_align_start = buf;
585 break;
586 case WPS_VIEWPORT_ENABLE:
588 char label = data->tokens[i].value.i;
589 char temp = VP_DRAW_HIDEABLE;
590 /* viewports are allowed to share id's so find and enable
591 * all of them */
592 struct skin_token_list *list = data->viewports;
593 while (list)
595 struct skin_viewport *vp =
596 (struct skin_viewport *)list->token->value.data;
597 if (vp->label == label)
599 if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
600 temp |= VP_DRAW_WASHIDDEN;
601 vp->hidden_flags = temp;
603 list = list->next;
606 break;
607 default:
609 /* get the value of the tag and copy it to the buffer */
610 const char *value = get_token_value(gwps, &data->tokens[i],
611 temp_buf, sizeof(temp_buf), NULL);
612 if (value)
614 update = true;
615 while (*value && (buf < linebuf_end))
616 *buf++ = *value++;
618 break;
623 /* close the current alignment */
624 switch (cur_align)
626 case WPS_ALIGN_LEFT:
627 align->left = cur_align_start;
628 break;
630 case WPS_ALIGN_CENTER:
631 align->center = cur_align_start;
632 break;
634 case WPS_ALIGN_RIGHT:
635 align->right = cur_align_start;
636 break;
639 return update;
641 static void get_subline_timeout(struct gui_wps *gwps, struct skin_subline *subline)
643 struct wps_data *data = gwps->data;
644 int i;
645 subline->time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
647 for (i = subline->first_token_idx;
648 i <= subline->last_token_idx; i++)
650 switch(data->tokens[i].type)
652 case WPS_TOKEN_CONDITIONAL:
653 /* place ourselves in the right conditional case */
654 evaluate_conditional(gwps, &i);
655 break;
657 case WPS_TOKEN_CONDITIONAL_OPTION:
658 /* we've finished in the curent conditional case,
659 skip to the end of the conditional structure */
660 i = find_conditional_end(data, i);
661 break;
663 case WPS_TOKEN_SUBLINE_TIMEOUT:
664 subline->time_mult = data->tokens[i].value.i;
665 break;
667 default:
668 break;
673 /* Calculates which subline should be displayed for the specified line
674 Returns true iff the subline must be refreshed */
675 static bool update_curr_subline(struct gui_wps *gwps, struct skin_line *line)
677 /* shortcut this whole thing if we need to reset the line completly */
678 if (line->curr_subline == NULL)
680 line->subline_expire_time = current_tick;
681 line->curr_subline = &line->sublines;
682 if (!line->curr_subline->next)
684 line->subline_expire_time += 100*HZ;
686 else
688 get_subline_timeout(gwps, line->curr_subline);
689 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
691 return true;
693 /* if time to advance to next sub-line */
694 if (TIME_AFTER(current_tick, line->subline_expire_time - 1))
696 /* if there is only one subline, there is no need to search for a new one */
697 if (&line->sublines == line->curr_subline &&
698 line->curr_subline->next == NULL)
700 line->subline_expire_time += 100 * HZ;
701 return false;
703 if (line->curr_subline->next)
704 line->curr_subline = line->curr_subline->next;
705 else
706 line->curr_subline = &line->sublines;
707 get_subline_timeout(gwps, line->curr_subline);
708 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
709 return true;
711 return false;
714 /* Display a line appropriately according to its alignment format.
715 format_align contains the text, separated between left, center and right.
716 line is the index of the line on the screen.
717 scroll indicates whether the line is a scrolling one or not.
719 static void write_line(struct screen *display,
720 struct align_pos *format_align,
721 int line,
722 bool scroll)
724 int left_width = 0, left_xpos;
725 int center_width = 0, center_xpos;
726 int right_width = 0, right_xpos;
727 int ypos;
728 int space_width;
729 int string_height;
730 int scroll_width;
732 /* calculate different string sizes and positions */
733 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
734 if (format_align->left != 0) {
735 display->getstringsize((unsigned char *)format_align->left,
736 &left_width, &string_height);
739 if (format_align->right != 0) {
740 display->getstringsize((unsigned char *)format_align->right,
741 &right_width, &string_height);
744 if (format_align->center != 0) {
745 display->getstringsize((unsigned char *)format_align->center,
746 &center_width, &string_height);
749 left_xpos = 0;
750 right_xpos = (display->getwidth() - right_width);
751 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
753 scroll_width = display->getwidth() - left_xpos;
755 /* Checks for overlapping strings.
756 If needed the overlapping strings will be merged, separated by a
757 space */
759 /* CASE 1: left and centered string overlap */
760 /* there is a left string, need to merge left and center */
761 if ((left_width != 0 && center_width != 0) &&
762 (left_xpos + left_width + space_width > center_xpos)) {
763 /* replace the former separator '\0' of left and
764 center string with a space */
765 *(--format_align->center) = ' ';
766 /* calculate the new width and position of the merged string */
767 left_width = left_width + space_width + center_width;
768 /* there is no centered string anymore */
769 center_width = 0;
771 /* there is no left string, move center to left */
772 if ((left_width == 0 && center_width != 0) &&
773 (left_xpos + left_width > center_xpos)) {
774 /* move the center string to the left string */
775 format_align->left = format_align->center;
776 /* calculate the new width and position of the string */
777 left_width = center_width;
778 /* there is no centered string anymore */
779 center_width = 0;
782 /* CASE 2: centered and right string overlap */
783 /* there is a right string, need to merge center and right */
784 if ((center_width != 0 && right_width != 0) &&
785 (center_xpos + center_width + space_width > right_xpos)) {
786 /* replace the former separator '\0' of center and
787 right string with a space */
788 *(--format_align->right) = ' ';
789 /* move the center string to the right after merge */
790 format_align->right = format_align->center;
791 /* calculate the new width and position of the merged string */
792 right_width = center_width + space_width + right_width;
793 right_xpos = (display->getwidth() - right_width);
794 /* there is no centered string anymore */
795 center_width = 0;
797 /* there is no right string, move center to right */
798 if ((center_width != 0 && right_width == 0) &&
799 (center_xpos + center_width > right_xpos)) {
800 /* move the center string to the right string */
801 format_align->right = format_align->center;
802 /* calculate the new width and position of the string */
803 right_width = center_width;
804 right_xpos = (display->getwidth() - right_width);
805 /* there is no centered string anymore */
806 center_width = 0;
809 /* CASE 3: left and right overlap
810 There is no center string anymore, either there never
811 was one or it has been merged in case 1 or 2 */
812 /* there is a left string, need to merge left and right */
813 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
814 (left_xpos + left_width + space_width > right_xpos)) {
815 /* replace the former separator '\0' of left and
816 right string with a space */
817 *(--format_align->right) = ' ';
818 /* calculate the new width and position of the string */
819 left_width = left_width + space_width + right_width;
820 /* there is no right string anymore */
821 right_width = 0;
823 /* there is no left string, move right to left */
824 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
825 (left_width > right_xpos)) {
826 /* move the right string to the left string */
827 format_align->left = format_align->right;
828 /* calculate the new width and position of the string */
829 left_width = right_width;
830 /* there is no right string anymore */
831 right_width = 0;
834 ypos = (line * string_height);
837 if (scroll && ((left_width > scroll_width) ||
838 (center_width > scroll_width) ||
839 (right_width > scroll_width)))
841 display->puts_scroll(0, line,
842 (unsigned char *)format_align->left);
844 else
846 #ifdef HAVE_LCD_BITMAP
847 /* clear the line first */
848 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
849 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
850 display->set_drawmode(DRMODE_SOLID);
851 #endif
853 /* Nasty hack: we output an empty scrolling string,
854 which will reset the scroller for that line */
855 display->puts_scroll(0, line, (unsigned char *)"");
857 /* print aligned strings */
858 if (left_width != 0)
860 display->putsxy(left_xpos, ypos,
861 (unsigned char *)format_align->left);
863 if (center_width != 0)
865 display->putsxy(center_xpos, ypos,
866 (unsigned char *)format_align->center);
868 if (right_width != 0)
870 display->putsxy(right_xpos, ypos,
871 (unsigned char *)format_align->right);
876 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
878 struct wps_data *data = gwps->data;
879 struct screen *display = gwps->display;
881 if (!data || !display || !gwps->state)
882 return false;
884 unsigned flags;
885 char linebuf[MAX_PATH];
887 struct align_pos align;
888 align.left = NULL;
889 align.center = NULL;
890 align.right = NULL;
893 struct skin_token_list *viewport_list;
895 bool update_line, new_subline_refresh;
897 #ifdef HAVE_LCD_BITMAP
899 /* to find out wether the peak meter is enabled we
900 assume it wasn't until we find a line that contains
901 the peak meter. We can't use peak_meter_enabled itself
902 because that would mean to turn off the meter thread
903 temporarily. (That shouldn't matter unless yield
904 or sleep is called but who knows...)
906 bool enable_pm = false;
908 #endif
910 /* reset to first subline if refresh all flag is set */
911 if (refresh_mode == WPS_REFRESH_ALL)
913 struct skin_line *line;
914 struct skin_viewport *skin_viewport = find_viewport(VP_DEFAULT_LABEL, data);
916 if (!(skin_viewport->hidden_flags & VP_NEVER_VISIBLE))
918 display->set_viewport(&skin_viewport->vp);
919 display->clear_viewport();
922 for (viewport_list = data->viewports;
923 viewport_list; viewport_list = viewport_list->next)
925 skin_viewport =
926 (struct skin_viewport *)viewport_list->token->value.data;
927 for(line = skin_viewport->lines; line; line = line->next)
929 line->curr_subline = NULL;
934 #ifdef HAVE_LCD_CHARCELLS
935 int i;
936 for (i = 0; i < 8; i++)
938 if (data->wps_progress_pat[i] == 0)
939 data->wps_progress_pat[i] = display->get_locked_pattern();
941 #endif
943 /* disable any viewports which are conditionally displayed */
944 for (viewport_list = data->viewports;
945 viewport_list; viewport_list = viewport_list->next)
947 struct skin_viewport *skin_viewport =
948 (struct skin_viewport *)viewport_list->token->value.data;
949 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
951 continue;
953 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
955 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
956 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
957 else
958 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
961 int viewport_count = 0;
962 for (viewport_list = data->viewports;
963 viewport_list; viewport_list = viewport_list->next, viewport_count++)
965 struct skin_viewport *skin_viewport =
966 (struct skin_viewport *)viewport_list->token->value.data;
967 unsigned vp_refresh_mode = refresh_mode;
969 display->set_viewport(&skin_viewport->vp);
971 int hidden_vp = 0;
973 #ifdef HAVE_LCD_BITMAP
974 /* Set images to not to be displayed */
975 struct skin_token_list *imglist = data->images;
976 while (imglist)
978 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
979 img->display = -1;
980 imglist = imglist->next;
982 #endif
983 /* dont redraw the viewport if its disabled */
984 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
985 { /* don't draw anything into this one */
986 vp_refresh_mode = 0; hidden_vp = true;
988 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
990 if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
991 display->scroll_stop(&skin_viewport->vp);
992 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
993 continue;
995 else if (((skin_viewport->hidden_flags&
996 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
997 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
999 vp_refresh_mode = WPS_REFRESH_ALL;
1000 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
1003 if (vp_refresh_mode == WPS_REFRESH_ALL)
1005 display->clear_viewport();
1008 /* loop over the lines for this viewport */
1009 struct skin_line *line;
1010 int line_count = 0;
1012 for (line = skin_viewport->lines; line; line = line->next, line_count++)
1014 struct skin_subline *subline;
1015 memset(linebuf, 0, sizeof(linebuf));
1016 update_line = false;
1018 /* get current subline for the line */
1019 new_subline_refresh = update_curr_subline(gwps, line);
1020 subline = line->curr_subline;
1021 flags = line->curr_subline->line_type;
1023 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1024 || new_subline_refresh || hidden_vp)
1026 /* get_line tells us if we need to update the line */
1027 update_line = get_line(gwps, subline,
1028 &align, linebuf, sizeof(linebuf));
1030 #ifdef HAVE_LCD_BITMAP
1031 /* peakmeter */
1032 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1034 /* the peakmeter should be alone on its line */
1035 update_line = false;
1037 int h = font_get(skin_viewport->vp.font)->height;
1038 int peak_meter_y = line_count* h;
1040 /* The user might decide to have the peak meter in the last
1041 line so that it is only displayed if no status bar is
1042 visible. If so we neither want do draw nor enable the
1043 peak meter. */
1044 if (peak_meter_y + h <= skin_viewport->vp.y+skin_viewport->vp.height) {
1045 /* found a line with a peak meter -> remember that we must
1046 enable it later */
1047 enable_pm = true;
1048 peak_meter_enabled = true;
1049 peak_meter_screen(gwps->display, 0, peak_meter_y,
1050 MIN(h, skin_viewport->vp.y+skin_viewport->vp.height - peak_meter_y));
1052 else
1054 peak_meter_enabled = false;
1058 #else /* HAVE_LCD_CHARCELL */
1060 /* progressbar */
1061 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1063 if (data->full_line_progressbar)
1064 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1065 else
1066 draw_player_progress(gwps);
1068 #endif
1070 if (update_line && !hidden_vp &&
1071 /* conditionals clear the line which means if the %Vd is put into the default
1072 viewport there will be a blank line.
1073 To get around this we dont allow any actual drawing to happen in the
1074 deault vp if other vp's are defined */
1075 ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
1076 !viewport_list->next))
1078 if (flags & WPS_REFRESH_SCROLL)
1080 /* if the line is a scrolling one we don't want to update
1081 too often, so that it has the time to scroll */
1082 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1083 write_line(display, &align, line_count, true);
1085 else
1086 write_line(display, &align, line_count, false);
1089 #ifdef HAVE_LCD_BITMAP
1090 /* progressbar */
1091 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1093 if (skin_viewport->pb)
1095 draw_progressbar(gwps, skin_viewport);
1098 /* Now display any images in this viewport */
1099 if (!hidden_vp)
1100 wps_display_images(gwps, &skin_viewport->vp);
1101 #endif
1104 #ifdef HAVE_LCD_BITMAP
1105 data->peak_meter_enabled = enable_pm;
1106 #endif
1108 if (refresh_mode & WPS_REFRESH_STATUSBAR)
1110 viewportmanager_set_statusbar(gwps->sync_data->statusbars);
1112 /* Restore the default viewport */
1113 display->set_viewport(NULL);
1115 display->update();
1117 return true;