Replace limiter with dynamic range compressor
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
blobe24ab6639350ac3ae1e0c66cf0f1b3d69e4c9716
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 "font.h"
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include "system.h"
27 #include "settings.h"
28 #include "settings_list.h"
29 #include "rbunicode.h"
30 #include "rtc.h"
31 #include "audio.h"
32 #include "status.h"
33 #include "power.h"
34 #include "powermgmt.h"
35 #include "sound.h"
36 #include "debug.h"
37 #ifdef HAVE_LCD_CHARCELLS
38 #include "hwcompat.h"
39 #endif
40 #include "abrepeat.h"
41 #include "mp3_playback.h"
42 #include "lang.h"
43 #include "misc.h"
44 #include "splash.h"
45 #include "scrollbar.h"
46 #include "led.h"
47 #include "lcd.h"
48 #ifdef HAVE_LCD_BITMAP
49 #include "peakmeter.h"
50 /* Image stuff */
51 #include "bmp.h"
52 #include "albumart.h"
53 #endif
54 #include "dsp.h"
55 #include "action.h"
56 #include "cuesheet.h"
57 #include "playlist.h"
58 #if CONFIG_CODEC == SWCODEC
59 #include "playback.h"
60 #endif
61 #include "backdrop.h"
62 #include "viewport.h"
65 #include "wps_internals.h"
66 #include "skin_engine.h"
68 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode);
71 /* initial setup of wps_data */
72 void skin_data_init(struct wps_data *wps_data)
74 #ifdef HAVE_LCD_BITMAP
75 wps_data->wps_sb_tag = false;
76 wps_data->show_sb_on_wps = false;
77 wps_data->peak_meter_enabled = false;
78 wps_data->images = NULL;
79 wps_data->progressbars = NULL;
80 #ifdef HAVE_ALBUMART
81 wps_data->albumart = NULL;
82 #endif
83 /* progress bars */
84 #else /* HAVE_LCD_CHARCELLS */
85 int i;
86 for (i = 0; i < 8; i++)
88 wps_data->wps_progress_pat[i] = 0;
90 wps_data->full_line_progressbar = false;
91 #endif
92 wps_data->button_time_volume = 0;
93 wps_data->wps_loaded = false;
96 /* TODO: maybe move this whole function into wps.c instead ? */
97 bool gui_wps_display(struct gui_wps *gwps)
99 struct screen *display = gwps->display;
101 /* Update the values in the first (default) viewport - in case the user
102 has modified the statusbar or colour settings */
103 #if LCD_DEPTH > 1
104 if (display->depth > 1)
106 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, gwps->data)->vp;
107 vp->fg_pattern = display->get_foreground();
108 vp->bg_pattern = display->get_background();
110 #endif
111 display->clear_display();
112 display->backdrop_show(BACKDROP_SKIN_WPS);
113 return skin_redraw(gwps, WPS_REFRESH_ALL);
116 /* update a skinned screen, update_type is WPS_REFRESH_* values.
117 * Usually it should only be WPS_REFRESH_NON_STATIC
118 * A full update will be done if required (state.do_full_update == true)
120 bool skin_update(struct gui_wps *gwps, unsigned int update_type)
122 bool retval;
123 /* This maybe shouldnt be here, but while the skin is only used to
124 * display the music screen this is better than whereever we are being
125 * called from. This is also safe for skined screen which dont use the id3 */
126 struct mp3entry *id3 = gwps->state->id3;
127 bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
128 gwps->state->do_full_update = cuesheet_update || gwps->state->do_full_update;
130 retval = skin_redraw(gwps, gwps->state->do_full_update ?
131 WPS_REFRESH_ALL : update_type);
132 return retval;
135 #ifdef HAVE_LCD_BITMAP
137 void skin_statusbar_changed(struct gui_wps *skin)
139 if (!skin)
140 return;
141 struct wps_data *data = skin->data;
142 const struct screen *display = skin->display;
144 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, data)->vp;
145 viewport_set_fullscreen(vp, display->screen_type);
147 if (data->wps_sb_tag)
148 { /* fix up the default viewport */
149 if (data->show_sb_on_wps)
151 bool bar_at_top =
152 statusbar_position(display->screen_type) != STATUSBAR_BOTTOM;
154 vp->y = bar_at_top?STATUSBAR_HEIGHT:0;
155 vp->height = display->lcdheight - STATUSBAR_HEIGHT;
157 else
159 vp->y = 0;
160 vp->height = display->lcdheight;
167 static void draw_progressbar(struct gui_wps *gwps,
168 struct skin_viewport *wps_vp)
170 struct screen *display = gwps->display;
171 struct wps_state *state = gwps->state;
172 struct progressbar *pb = wps_vp->pb;
173 int y = pb->y;
175 if (y < 0)
177 int line_height = font_get(wps_vp->vp.font)->height;
178 /* center the pb in the line, but only if the line is higher than the pb */
179 int center = (line_height-pb->height)/2;
180 /* if Y was not set calculate by font height,Y is -line_number-1 */
181 y = (-y -1)*line_height + (0 > center ? 0 : center);
184 if (pb->have_bitmap_pb)
185 gui_bitmap_scrollbar_draw(display, pb->bm,
186 pb->x, y, pb->width, pb->bm.height,
187 state->id3->length ? state->id3->length : 1, 0,
188 state->id3->length ? state->id3->elapsed
189 + state->ff_rewind_count : 0,
190 HORIZONTAL);
191 else
192 gui_scrollbar_draw(display, pb->x, y, pb->width, pb->height,
193 state->id3->length ? state->id3->length : 1, 0,
194 state->id3->length ? state->id3->elapsed
195 + state->ff_rewind_count : 0,
196 HORIZONTAL);
197 #ifdef AB_REPEAT_ENABLE
198 if ( ab_repeat_mode_enabled() && state->id3->length != 0 )
199 ab_draw_markers(display, state->id3->length,
200 pb->x, pb->x + pb->width, y, pb->height);
201 #endif
203 if (state->id3->cuesheet)
204 cue_draw_markers(display, state->id3->cuesheet, state->id3->length,
205 pb->x, pb->x + pb->width, y+1, pb->height-2);
208 /* clears the area where the image was shown */
209 static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
211 if(!gwps)
212 return;
213 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
214 gwps->display->fillrect(img->x, img->y, img->bm.width, img->subimage_height);
215 gwps->display->set_drawmode(DRMODE_SOLID);
218 static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage)
220 struct screen *display = gwps->display;
221 if(img->always_display)
222 display->set_drawmode(DRMODE_FG);
223 else
224 display->set_drawmode(DRMODE_SOLID);
226 #if LCD_DEPTH > 1
227 if(img->bm.format == FORMAT_MONO) {
228 #endif
229 display->mono_bitmap_part(img->bm.data,
230 0, img->subimage_height * subimage,
231 img->bm.width, img->x,
232 img->y, img->bm.width,
233 img->subimage_height);
234 #if LCD_DEPTH > 1
235 } else {
236 display->transparent_bitmap_part((fb_data *)img->bm.data,
237 0, img->subimage_height * subimage,
238 STRIDE(display->screen_type,
239 img->bm.width, img->bm.height),
240 img->x, img->y, img->bm.width,
241 img->subimage_height);
243 #endif
246 static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
248 if(!gwps || !gwps->data || !gwps->display)
249 return;
251 struct wps_data *data = gwps->data;
252 struct screen *display = gwps->display;
253 struct skin_token_list *list = data->images;
255 while (list)
257 struct gui_img *img = (struct gui_img*)list->token->value.data;
258 if (img->loaded)
260 if (img->display >= 0)
262 wps_draw_image(gwps, img, img->display);
264 else if (img->always_display && img->vp == vp)
266 wps_draw_image(gwps, img, 0);
269 list = list->next;
271 #ifdef HAVE_ALBUMART
272 /* now draw the AA */
273 if (data->albumart && data->albumart->vp == vp && data->albumart->draw)
275 draw_album_art(gwps, audio_current_aa_hid(), false);
277 #endif
279 display->set_drawmode(DRMODE_SOLID);
282 #else /* HAVE_LCD_CHARCELL */
284 static bool draw_player_progress(struct gui_wps *gwps)
286 struct wps_state *state = gwps->state;
287 struct screen *display = gwps->display;
288 unsigned char progress_pattern[7];
289 int pos = 0;
290 int i;
292 if (!state->id3)
293 return false;
295 if (state->id3->length)
296 pos = 36 * (state->id3->elapsed + state->ff_rewind_count)
297 / state->id3->length;
299 for (i = 0; i < 7; i++, pos -= 5)
301 if (pos <= 0)
302 progress_pattern[i] = 0x1fu;
303 else if (pos >= 5)
304 progress_pattern[i] = 0x00u;
305 else
306 progress_pattern[i] = 0x1fu >> pos;
309 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
310 return true;
313 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
315 static const unsigned char numbers[10][4] = {
316 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
317 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
318 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
319 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
320 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
321 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
322 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
323 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
324 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
325 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
328 struct wps_state *state = gwps->state;
329 struct screen *display = gwps->display;
330 struct wps_data *data = gwps->data;
331 unsigned char progress_pattern[7];
332 char timestr[10];
333 int time;
334 int time_idx = 0;
335 int pos = 0;
336 int pat_idx = 1;
337 int digit, i, j;
338 bool softchar;
340 if (!state->id3 || buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
341 return;
343 time = state->id3->elapsed + state->ff_rewind_count;
344 if (state->id3->length)
345 pos = 55 * time / state->id3->length;
347 memset(timestr, 0, sizeof(timestr));
348 format_time(timestr, sizeof(timestr)-2, time);
349 timestr[strlen(timestr)] = ':'; /* always safe */
351 for (i = 0; i < 11; i++, pos -= 5)
353 softchar = false;
354 memset(progress_pattern, 0, sizeof(progress_pattern));
356 if ((digit = timestr[time_idx]))
358 softchar = true;
359 digit -= '0';
361 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
363 memcpy(progress_pattern, numbers[digit], 4);
364 time_idx += 2;
366 else /* tens, shifted right */
368 for (j = 0; j < 4; j++)
369 progress_pattern[j] = numbers[digit][j] >> 1;
371 if (time_idx > 0) /* not the first group, add colon in front */
373 progress_pattern[1] |= 0x10u;
374 progress_pattern[3] |= 0x10u;
376 time_idx++;
379 if (pos >= 5)
380 progress_pattern[5] = progress_pattern[6] = 0x1fu;
383 if (pos > 0 && pos < 5)
385 softchar = true;
386 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
389 if (softchar && pat_idx < 8)
391 display->define_pattern(data->wps_progress_pat[pat_idx],
392 progress_pattern);
393 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
394 pat_idx++;
396 else if (pos <= 0)
397 buf = utf8encode(' ', buf);
398 else
399 buf = utf8encode(0xe115, buf); /* 2/7 _ */
401 *buf = '\0';
404 #endif /* HAVE_LCD_CHARCELL */
406 /* Return the index to the end token for the conditional token at index.
407 The conditional token can be either a start token or a separator
408 (i.e. option) token.
410 static int find_conditional_end(struct wps_data *data, int index)
412 int ret = index;
413 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
414 ret = data->tokens[ret].value.i;
416 /* ret now is the index to the end token for the conditional. */
417 return ret;
420 /* Evaluate the conditional that is at *token_index and return whether a skip
421 has ocurred. *token_index is updated with the new position.
423 static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
425 if (!gwps)
426 return false;
428 struct wps_data *data = gwps->data;
430 int i, cond_end;
431 int cond_index = *token_index;
432 char result[128];
433 const char *value;
434 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
435 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
437 /* treat ?xx<true> constructs as if they had 2 options. */
438 if (num_options < 2)
439 num_options = 2;
441 int intval = num_options;
442 /* get_token_value needs to know the number of options in the enum */
443 value = get_token_value(gwps, &data->tokens[cond_index + 1],
444 result, sizeof(result), &intval);
446 /* intval is now the number of the enum option we want to read,
447 starting from 1. If intval is -1, we check if value is empty. */
448 if (intval == -1)
449 intval = (value && *value) ? 1 : num_options;
450 else if (intval > num_options || intval < 1)
451 intval = num_options;
453 data->tokens[cond_index].value.i = (intval << 8) + num_options;
455 /* skip to the appropriate enum case */
456 int next = cond_index + 2;
457 for (i = 1; i < intval; i++)
459 next = data->tokens[next].value.i;
461 *token_index = next;
463 if (prev_val == intval)
465 /* Same conditional case as previously. Return without clearing the
466 pictures */
467 return false;
470 cond_end = find_conditional_end(data, cond_index + 2);
471 for (i = cond_index + 3; i < cond_end; i++)
473 #ifdef HAVE_LCD_BITMAP
474 /* clear all pictures in the conditional and nested ones */
475 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
476 clear_image_pos(gwps, find_image(data->tokens[i].value.i&0xFF, gwps->data));
477 #endif
478 #ifdef HAVE_ALBUMART
479 if (data->albumart && data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
481 draw_album_art(gwps, audio_current_aa_hid(), true);
482 data->albumart->draw = false;
484 #endif
487 return true;
489 #ifdef HAVE_LCD_BITMAP
490 struct gui_img* find_image(char label, struct wps_data *data)
492 struct skin_token_list *list = data->images;
493 while (list)
495 struct gui_img *img = (struct gui_img *)list->token->value.data;
496 if (img->label == label)
497 return img;
498 list = list->next;
500 return NULL;
502 #endif
504 struct skin_viewport* find_viewport(char label, struct wps_data *data)
506 struct skin_token_list *list = data->viewports;
507 while (list)
509 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
510 if (vp->label == label)
511 return vp;
512 list = list->next;
514 return NULL;
518 /* Read a (sub)line to the given alignment format buffer.
519 linebuf is the buffer where the data is actually stored.
520 align is the alignment format that'll be used to display the text.
521 The return value indicates whether the line needs to be updated.
523 static bool get_line(struct gui_wps *gwps,
524 struct skin_subline *subline,
525 struct align_pos *align,
526 char *linebuf,
527 int linebuf_size)
529 struct wps_data *data = gwps->data;
531 char temp_buf[128];
532 char *buf = linebuf; /* will always point to the writing position */
533 char *linebuf_end = linebuf + linebuf_size - 1;
534 bool update = false;
535 int i;
537 /* alignment-related variables */
538 int cur_align;
539 char* cur_align_start;
540 cur_align_start = buf;
541 cur_align = WPS_ALIGN_LEFT;
542 align->left = NULL;
543 align->center = NULL;
544 align->right = NULL;
545 /* Process all tokens of the desired subline */
546 for (i = subline->first_token_idx;
547 i <= subline->last_token_idx; i++)
549 switch(data->tokens[i].type)
551 case WPS_TOKEN_CONDITIONAL:
552 /* place ourselves in the right conditional case */
553 update |= evaluate_conditional(gwps, &i);
554 break;
556 case WPS_TOKEN_CONDITIONAL_OPTION:
557 /* we've finished in the curent conditional case,
558 skip to the end of the conditional structure */
559 i = find_conditional_end(data, i);
560 break;
562 #ifdef HAVE_LCD_BITMAP
563 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
565 char n = data->tokens[i].value.i & 0xFF;
566 int subimage = data->tokens[i].value.i >> 8;
567 struct gui_img *img = find_image(n, data);
569 if (img && img->loaded)
570 img->display = subimage;
571 break;
573 #endif
575 case WPS_TOKEN_ALIGN_LEFT:
576 case WPS_TOKEN_ALIGN_CENTER:
577 case WPS_TOKEN_ALIGN_RIGHT:
578 /* remember where the current aligned text started */
579 switch (cur_align)
581 case WPS_ALIGN_LEFT:
582 align->left = cur_align_start;
583 break;
585 case WPS_ALIGN_CENTER:
586 align->center = cur_align_start;
587 break;
589 case WPS_ALIGN_RIGHT:
590 align->right = cur_align_start;
591 break;
593 /* start a new alignment */
594 switch (data->tokens[i].type)
596 case WPS_TOKEN_ALIGN_LEFT:
597 cur_align = WPS_ALIGN_LEFT;
598 break;
599 case WPS_TOKEN_ALIGN_CENTER:
600 cur_align = WPS_ALIGN_CENTER;
601 break;
602 case WPS_TOKEN_ALIGN_RIGHT:
603 cur_align = WPS_ALIGN_RIGHT;
604 break;
605 default:
606 break;
608 *buf++ = 0;
609 cur_align_start = buf;
610 break;
611 case WPS_VIEWPORT_ENABLE:
613 char label = data->tokens[i].value.i;
614 char temp = VP_DRAW_HIDEABLE;
615 /* viewports are allowed to share id's so find and enable
616 * all of them */
617 struct skin_token_list *list = data->viewports;
618 while (list)
620 struct skin_viewport *vp =
621 (struct skin_viewport *)list->token->value.data;
622 if (vp->label == label)
624 if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
625 temp |= VP_DRAW_WASHIDDEN;
626 vp->hidden_flags = temp;
628 list = list->next;
631 break;
632 default:
634 /* get the value of the tag and copy it to the buffer */
635 const char *value = get_token_value(gwps, &data->tokens[i],
636 temp_buf, sizeof(temp_buf), NULL);
637 if (value)
639 update = true;
640 while (*value && (buf < linebuf_end))
641 *buf++ = *value++;
643 break;
648 /* close the current alignment */
649 switch (cur_align)
651 case WPS_ALIGN_LEFT:
652 align->left = cur_align_start;
653 break;
655 case WPS_ALIGN_CENTER:
656 align->center = cur_align_start;
657 break;
659 case WPS_ALIGN_RIGHT:
660 align->right = cur_align_start;
661 break;
664 return update;
666 static void get_subline_timeout(struct gui_wps *gwps, struct skin_subline *subline)
668 struct wps_data *data = gwps->data;
669 int i;
670 subline->time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
672 for (i = subline->first_token_idx;
673 i <= subline->last_token_idx; i++)
675 switch(data->tokens[i].type)
677 case WPS_TOKEN_CONDITIONAL:
678 /* place ourselves in the right conditional case */
679 evaluate_conditional(gwps, &i);
680 break;
682 case WPS_TOKEN_CONDITIONAL_OPTION:
683 /* we've finished in the curent conditional case,
684 skip to the end of the conditional structure */
685 i = find_conditional_end(data, i);
686 break;
688 case WPS_TOKEN_SUBLINE_TIMEOUT:
689 subline->time_mult = data->tokens[i].value.i;
690 break;
692 default:
693 break;
698 /* Calculates which subline should be displayed for the specified line
699 Returns true iff the subline must be refreshed */
700 static bool update_curr_subline(struct gui_wps *gwps, struct skin_line *line)
702 /* shortcut this whole thing if we need to reset the line completly */
703 if (line->curr_subline == NULL)
705 line->subline_expire_time = current_tick;
706 line->curr_subline = &line->sublines;
707 if (!line->curr_subline->next)
709 line->subline_expire_time += 100*HZ;
711 else
713 get_subline_timeout(gwps, line->curr_subline);
714 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
716 return true;
718 /* if time to advance to next sub-line */
719 if (TIME_AFTER(current_tick, line->subline_expire_time - 1))
721 /* if there is only one subline, there is no need to search for a new one */
722 if (&line->sublines == line->curr_subline &&
723 line->curr_subline->next == NULL)
725 line->subline_expire_time += 100 * HZ;
726 return true;
728 if (line->curr_subline->next)
729 line->curr_subline = line->curr_subline->next;
730 else
731 line->curr_subline = &line->sublines;
732 get_subline_timeout(gwps, line->curr_subline);
733 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
734 return true;
736 return false;
739 /* Display a line appropriately according to its alignment format.
740 format_align contains the text, separated between left, center and right.
741 line is the index of the line on the screen.
742 scroll indicates whether the line is a scrolling one or not.
744 static void write_line(struct screen *display,
745 struct align_pos *format_align,
746 int line,
747 bool scroll)
749 int left_width = 0, left_xpos;
750 int center_width = 0, center_xpos;
751 int right_width = 0, right_xpos;
752 int ypos;
753 int space_width;
754 int string_height;
755 int scroll_width;
757 /* calculate different string sizes and positions */
758 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
759 if (format_align->left != 0) {
760 display->getstringsize((unsigned char *)format_align->left,
761 &left_width, &string_height);
764 if (format_align->right != 0) {
765 display->getstringsize((unsigned char *)format_align->right,
766 &right_width, &string_height);
769 if (format_align->center != 0) {
770 display->getstringsize((unsigned char *)format_align->center,
771 &center_width, &string_height);
774 left_xpos = 0;
775 right_xpos = (display->getwidth() - right_width);
776 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
778 scroll_width = display->getwidth() - left_xpos;
780 /* Checks for overlapping strings.
781 If needed the overlapping strings will be merged, separated by a
782 space */
784 /* CASE 1: left and centered string overlap */
785 /* there is a left string, need to merge left and center */
786 if ((left_width != 0 && center_width != 0) &&
787 (left_xpos + left_width + space_width > center_xpos)) {
788 /* replace the former separator '\0' of left and
789 center string with a space */
790 *(--format_align->center) = ' ';
791 /* calculate the new width and position of the merged string */
792 left_width = left_width + space_width + center_width;
793 /* there is no centered string anymore */
794 center_width = 0;
796 /* there is no left string, move center to left */
797 if ((left_width == 0 && center_width != 0) &&
798 (left_xpos + left_width > center_xpos)) {
799 /* move the center string to the left string */
800 format_align->left = format_align->center;
801 /* calculate the new width and position of the string */
802 left_width = center_width;
803 /* there is no centered string anymore */
804 center_width = 0;
807 /* CASE 2: centered and right string overlap */
808 /* there is a right string, need to merge center and right */
809 if ((center_width != 0 && right_width != 0) &&
810 (center_xpos + center_width + space_width > right_xpos)) {
811 /* replace the former separator '\0' of center and
812 right string with a space */
813 *(--format_align->right) = ' ';
814 /* move the center string to the right after merge */
815 format_align->right = format_align->center;
816 /* calculate the new width and position of the merged string */
817 right_width = center_width + space_width + right_width;
818 right_xpos = (display->getwidth() - right_width);
819 /* there is no centered string anymore */
820 center_width = 0;
822 /* there is no right string, move center to right */
823 if ((center_width != 0 && right_width == 0) &&
824 (center_xpos + center_width > right_xpos)) {
825 /* move the center string to the right string */
826 format_align->right = format_align->center;
827 /* calculate the new width and position of the string */
828 right_width = center_width;
829 right_xpos = (display->getwidth() - right_width);
830 /* there is no centered string anymore */
831 center_width = 0;
834 /* CASE 3: left and right overlap
835 There is no center string anymore, either there never
836 was one or it has been merged in case 1 or 2 */
837 /* there is a left string, need to merge left and right */
838 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
839 (left_xpos + left_width + space_width > right_xpos)) {
840 /* replace the former separator '\0' of left and
841 right string with a space */
842 *(--format_align->right) = ' ';
843 /* calculate the new width and position of the string */
844 left_width = left_width + space_width + right_width;
845 /* there is no right string anymore */
846 right_width = 0;
848 /* there is no left string, move right to left */
849 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
850 (left_width > right_xpos)) {
851 /* move the right string to the left string */
852 format_align->left = format_align->right;
853 /* calculate the new width and position of the string */
854 left_width = right_width;
855 /* there is no right string anymore */
856 right_width = 0;
859 ypos = (line * string_height);
862 if (scroll && ((left_width > scroll_width) ||
863 (center_width > scroll_width) ||
864 (right_width > scroll_width)))
866 display->puts_scroll(0, line,
867 (unsigned char *)format_align->left);
869 else
871 #ifdef HAVE_LCD_BITMAP
872 /* clear the line first */
873 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
874 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
875 display->set_drawmode(DRMODE_SOLID);
876 #endif
878 /* Nasty hack: we output an empty scrolling string,
879 which will reset the scroller for that line */
880 display->puts_scroll(0, line, (unsigned char *)"");
882 /* print aligned strings */
883 if (left_width != 0)
885 display->putsxy(left_xpos, ypos,
886 (unsigned char *)format_align->left);
888 if (center_width != 0)
890 display->putsxy(center_xpos, ypos,
891 (unsigned char *)format_align->center);
893 if (right_width != 0)
895 display->putsxy(right_xpos, ypos,
896 (unsigned char *)format_align->right);
901 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
903 struct wps_data *data = gwps->data;
904 struct screen *display = gwps->display;
905 struct wps_state *state = gwps->state;
907 if (!data || !state || !display)
908 return false;
910 struct mp3entry *id3 = state->id3;
912 if (!id3)
913 return false;
915 unsigned flags;
916 char linebuf[MAX_PATH];
918 struct align_pos align;
919 align.left = NULL;
920 align.center = NULL;
921 align.right = NULL;
924 struct skin_token_list *viewport_list;
926 bool update_line, new_subline_refresh;
928 #ifdef HAVE_LCD_BITMAP
930 /* to find out wether the peak meter is enabled we
931 assume it wasn't until we find a line that contains
932 the peak meter. We can't use peak_meter_enabled itself
933 because that would mean to turn off the meter thread
934 temporarily. (That shouldn't matter unless yield
935 or sleep is called but who knows...)
937 bool enable_pm = false;
939 #endif
941 /* reset to first subline if refresh all flag is set */
942 if (refresh_mode == WPS_REFRESH_ALL)
944 struct skin_line *line;
946 display->set_viewport(&find_viewport(VP_DEFAULT_LABEL, data)->vp);
947 display->clear_viewport();
949 for (viewport_list = data->viewports;
950 viewport_list; viewport_list = viewport_list->next)
952 struct skin_viewport *skin_viewport =
953 (struct skin_viewport *)viewport_list->token->value.data;
954 for(line = skin_viewport->lines; line; line = line->next)
956 line->curr_subline = NULL;
961 #ifdef HAVE_LCD_CHARCELLS
962 int i;
963 for (i = 0; i < 8; i++)
965 if (data->wps_progress_pat[i] == 0)
966 data->wps_progress_pat[i] = display->get_locked_pattern();
968 #endif
970 /* disable any viewports which are conditionally displayed */
971 for (viewport_list = data->viewports;
972 viewport_list; viewport_list = viewport_list->next)
974 struct skin_viewport *skin_viewport =
975 (struct skin_viewport *)viewport_list->token->value.data;
976 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
978 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
979 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
980 else
981 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
984 int viewport_count = 0;
985 for (viewport_list = data->viewports;
986 viewport_list; viewport_list = viewport_list->next, viewport_count++)
988 struct skin_viewport *skin_viewport =
989 (struct skin_viewport *)viewport_list->token->value.data;
990 unsigned vp_refresh_mode = refresh_mode;
991 display->set_viewport(&skin_viewport->vp);
993 #ifdef HAVE_LCD_BITMAP
994 /* Set images to not to be displayed */
995 struct skin_token_list *imglist = data->images;
996 while (imglist)
998 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
999 img->display = -1;
1000 imglist = imglist->next;
1002 #endif
1003 /* dont redraw the viewport if its disabled */
1004 if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
1006 if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
1007 display->scroll_stop(&skin_viewport->vp);
1008 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1009 continue;
1011 else if (((skin_viewport->hidden_flags&
1012 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
1013 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
1015 vp_refresh_mode = WPS_REFRESH_ALL;
1016 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
1018 if (vp_refresh_mode == WPS_REFRESH_ALL)
1020 display->clear_viewport();
1023 /* loop over the lines for this viewport */
1024 struct skin_line *line;
1025 int line_count = 0;
1027 for (line = skin_viewport->lines; line; line = line->next, line_count++)
1029 struct skin_subline *subline;
1030 memset(linebuf, 0, sizeof(linebuf));
1031 update_line = false;
1033 /* get current subline for the line */
1034 new_subline_refresh = update_curr_subline(gwps, line);
1035 subline = line->curr_subline;
1036 flags = line->curr_subline->line_type;
1038 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1039 || new_subline_refresh)
1041 /* get_line tells us if we need to update the line */
1042 update_line = get_line(gwps, subline,
1043 &align, linebuf, sizeof(linebuf));
1045 #ifdef HAVE_LCD_BITMAP
1046 /* peakmeter */
1047 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1049 /* the peakmeter should be alone on its line */
1050 update_line = false;
1052 int h = font_get(skin_viewport->vp.font)->height;
1053 int peak_meter_y = line_count* h;
1055 /* The user might decide to have the peak meter in the last
1056 line so that it is only displayed if no status bar is
1057 visible. If so we neither want do draw nor enable the
1058 peak meter. */
1059 if (peak_meter_y + h <= skin_viewport->vp.y+skin_viewport->vp.height) {
1060 /* found a line with a peak meter -> remember that we must
1061 enable it later */
1062 enable_pm = true;
1063 peak_meter_enabled = true;
1064 peak_meter_screen(gwps->display, 0, peak_meter_y,
1065 MIN(h, skin_viewport->vp.y+skin_viewport->vp.height - peak_meter_y));
1067 else
1069 peak_meter_enabled = false;
1073 #else /* HAVE_LCD_CHARCELL */
1075 /* progressbar */
1076 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1078 if (data->full_line_progressbar)
1079 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1080 else
1081 draw_player_progress(gwps);
1083 #endif
1085 if (update_line &&
1086 /* conditionals clear the line which means if the %Vd is put into the default
1087 viewport there will be a blank line.
1088 To get around this we dont allow any actual drawing to happen in the
1089 deault vp if other vp's are defined */
1090 ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
1091 !viewport_list->next))
1093 if (flags & WPS_REFRESH_SCROLL)
1095 /* if the line is a scrolling one we don't want to update
1096 too often, so that it has the time to scroll */
1097 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1098 write_line(display, &align, line_count, true);
1100 else
1101 write_line(display, &align, line_count, false);
1105 #ifdef HAVE_LCD_BITMAP
1106 /* progressbar */
1107 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1109 if (skin_viewport->pb)
1111 draw_progressbar(gwps, skin_viewport);
1114 /* Now display any images in this viewport */
1115 wps_display_images(gwps, &skin_viewport->vp);
1116 #endif
1119 #ifdef HAVE_LCD_BITMAP
1120 data->peak_meter_enabled = enable_pm;
1121 #endif
1123 if (refresh_mode & WPS_REFRESH_STATUSBAR)
1125 viewportmanager_set_statusbar(*gwps->statusbars);
1127 /* Restore the default viewport */
1128 display->set_viewport(NULL);
1130 display->update();
1132 return true;