Fix a bug where the conditional use of %C (i.e. %?C) caused the album art to draw...
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
blob762b7f78a6d480188a944c4f9de8294e6f1b4ee8
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
480 return true;
482 #ifdef HAVE_LCD_BITMAP
483 struct gui_img* find_image(char label, struct wps_data *data)
485 struct skin_token_list *list = data->images;
486 while (list)
488 struct gui_img *img = (struct gui_img *)list->token->value.data;
489 if (img->label == label)
490 return img;
491 list = list->next;
493 return NULL;
495 #endif
497 struct skin_viewport* find_viewport(char label, struct wps_data *data)
499 struct skin_token_list *list = data->viewports;
500 while (list)
502 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
503 if (vp->label == label)
504 return vp;
505 list = list->next;
507 return NULL;
511 /* Read a (sub)line to the given alignment format buffer.
512 linebuf is the buffer where the data is actually stored.
513 align is the alignment format that'll be used to display the text.
514 The return value indicates whether the line needs to be updated.
516 static bool get_line(struct gui_wps *gwps,
517 struct skin_subline *subline,
518 struct align_pos *align,
519 char *linebuf,
520 int linebuf_size)
522 struct wps_data *data = gwps->data;
524 char temp_buf[128];
525 char *buf = linebuf; /* will always point to the writing position */
526 char *linebuf_end = linebuf + linebuf_size - 1;
527 bool update = false;
528 int i;
530 /* alignment-related variables */
531 int cur_align;
532 char* cur_align_start;
533 cur_align_start = buf;
534 cur_align = WPS_ALIGN_LEFT;
535 align->left = NULL;
536 align->center = NULL;
537 align->right = NULL;
538 /* Process all tokens of the desired subline */
539 for (i = subline->first_token_idx;
540 i <= subline->last_token_idx; i++)
542 switch(data->tokens[i].type)
544 case WPS_TOKEN_CONDITIONAL:
545 /* place ourselves in the right conditional case */
546 update |= evaluate_conditional(gwps, &i);
547 break;
549 case WPS_TOKEN_CONDITIONAL_OPTION:
550 /* we've finished in the curent conditional case,
551 skip to the end of the conditional structure */
552 i = find_conditional_end(data, i);
553 break;
555 #ifdef HAVE_LCD_BITMAP
556 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
558 char n = data->tokens[i].value.i & 0xFF;
559 int subimage = data->tokens[i].value.i >> 8;
560 struct gui_img *img = find_image(n, data);
562 if (img && img->loaded)
563 img->display = subimage;
564 break;
566 #endif
568 case WPS_TOKEN_ALIGN_LEFT:
569 case WPS_TOKEN_ALIGN_CENTER:
570 case WPS_TOKEN_ALIGN_RIGHT:
571 /* remember where the current aligned text started */
572 switch (cur_align)
574 case WPS_ALIGN_LEFT:
575 align->left = cur_align_start;
576 break;
578 case WPS_ALIGN_CENTER:
579 align->center = cur_align_start;
580 break;
582 case WPS_ALIGN_RIGHT:
583 align->right = cur_align_start;
584 break;
586 /* start a new alignment */
587 switch (data->tokens[i].type)
589 case WPS_TOKEN_ALIGN_LEFT:
590 cur_align = WPS_ALIGN_LEFT;
591 break;
592 case WPS_TOKEN_ALIGN_CENTER:
593 cur_align = WPS_ALIGN_CENTER;
594 break;
595 case WPS_TOKEN_ALIGN_RIGHT:
596 cur_align = WPS_ALIGN_RIGHT;
597 break;
598 default:
599 break;
601 *buf++ = 0;
602 cur_align_start = buf;
603 break;
604 case WPS_VIEWPORT_ENABLE:
606 char label = data->tokens[i].value.i;
607 char temp = VP_DRAW_HIDEABLE;
608 /* viewports are allowed to share id's so find and enable
609 * all of them */
610 struct skin_token_list *list = data->viewports;
611 while (list)
613 struct skin_viewport *vp =
614 (struct skin_viewport *)list->token->value.data;
615 if (vp->label == label)
617 if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
618 temp |= VP_DRAW_WASHIDDEN;
619 vp->hidden_flags = temp;
621 list = list->next;
624 break;
625 #ifdef HAVE_ALBUMART
626 case WPS_TOKEN_ALBUMART_DISPLAY:
627 if (data->albumart)
629 data->albumart->draw = true;
631 break;
632 #endif
633 default:
635 /* get the value of the tag and copy it to the buffer */
636 const char *value = get_token_value(gwps, &data->tokens[i],
637 temp_buf, sizeof(temp_buf), NULL);
638 if (value)
640 update = true;
641 while (*value && (buf < linebuf_end))
642 *buf++ = *value++;
644 break;
649 /* close the current alignment */
650 switch (cur_align)
652 case WPS_ALIGN_LEFT:
653 align->left = cur_align_start;
654 break;
656 case WPS_ALIGN_CENTER:
657 align->center = cur_align_start;
658 break;
660 case WPS_ALIGN_RIGHT:
661 align->right = cur_align_start;
662 break;
665 return update;
667 static void get_subline_timeout(struct gui_wps *gwps, struct skin_subline *subline)
669 struct wps_data *data = gwps->data;
670 int i;
671 subline->time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
673 for (i = subline->first_token_idx;
674 i <= subline->last_token_idx; i++)
676 switch(data->tokens[i].type)
678 case WPS_TOKEN_CONDITIONAL:
679 /* place ourselves in the right conditional case */
680 evaluate_conditional(gwps, &i);
681 break;
683 case WPS_TOKEN_CONDITIONAL_OPTION:
684 /* we've finished in the curent conditional case,
685 skip to the end of the conditional structure */
686 i = find_conditional_end(data, i);
687 break;
689 case WPS_TOKEN_SUBLINE_TIMEOUT:
690 subline->time_mult = data->tokens[i].value.i;
691 break;
693 default:
694 break;
699 /* Calculates which subline should be displayed for the specified line
700 Returns true iff the subline must be refreshed */
701 static bool update_curr_subline(struct gui_wps *gwps, struct skin_line *line)
703 /* shortcut this whole thing if we need to reset the line completly */
704 if (line->curr_subline == NULL)
706 line->subline_expire_time = current_tick;
707 line->curr_subline = &line->sublines;
708 if (!line->curr_subline->next)
710 line->subline_expire_time += 100*HZ;
712 else
714 get_subline_timeout(gwps, line->curr_subline);
715 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
717 return true;
719 /* if time to advance to next sub-line */
720 if (TIME_AFTER(current_tick, line->subline_expire_time - 1))
722 /* if there is only one subline, there is no need to search for a new one */
723 if (&line->sublines == line->curr_subline &&
724 line->curr_subline->next == NULL)
726 line->subline_expire_time += 100 * HZ;
727 return true;
729 if (line->curr_subline->next)
730 line->curr_subline = line->curr_subline->next;
731 else
732 line->curr_subline = &line->sublines;
733 get_subline_timeout(gwps, line->curr_subline);
734 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
735 return true;
737 return false;
740 /* Display a line appropriately according to its alignment format.
741 format_align contains the text, separated between left, center and right.
742 line is the index of the line on the screen.
743 scroll indicates whether the line is a scrolling one or not.
745 static void write_line(struct screen *display,
746 struct align_pos *format_align,
747 int line,
748 bool scroll)
750 int left_width = 0, left_xpos;
751 int center_width = 0, center_xpos;
752 int right_width = 0, right_xpos;
753 int ypos;
754 int space_width;
755 int string_height;
756 int scroll_width;
758 /* calculate different string sizes and positions */
759 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
760 if (format_align->left != 0) {
761 display->getstringsize((unsigned char *)format_align->left,
762 &left_width, &string_height);
765 if (format_align->right != 0) {
766 display->getstringsize((unsigned char *)format_align->right,
767 &right_width, &string_height);
770 if (format_align->center != 0) {
771 display->getstringsize((unsigned char *)format_align->center,
772 &center_width, &string_height);
775 left_xpos = 0;
776 right_xpos = (display->getwidth() - right_width);
777 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
779 scroll_width = display->getwidth() - left_xpos;
781 /* Checks for overlapping strings.
782 If needed the overlapping strings will be merged, separated by a
783 space */
785 /* CASE 1: left and centered string overlap */
786 /* there is a left string, need to merge left and center */
787 if ((left_width != 0 && center_width != 0) &&
788 (left_xpos + left_width + space_width > center_xpos)) {
789 /* replace the former separator '\0' of left and
790 center string with a space */
791 *(--format_align->center) = ' ';
792 /* calculate the new width and position of the merged string */
793 left_width = left_width + space_width + center_width;
794 /* there is no centered string anymore */
795 center_width = 0;
797 /* there is no left string, move center to left */
798 if ((left_width == 0 && center_width != 0) &&
799 (left_xpos + left_width > center_xpos)) {
800 /* move the center string to the left string */
801 format_align->left = format_align->center;
802 /* calculate the new width and position of the string */
803 left_width = center_width;
804 /* there is no centered string anymore */
805 center_width = 0;
808 /* CASE 2: centered and right string overlap */
809 /* there is a right string, need to merge center and right */
810 if ((center_width != 0 && right_width != 0) &&
811 (center_xpos + center_width + space_width > right_xpos)) {
812 /* replace the former separator '\0' of center and
813 right string with a space */
814 *(--format_align->right) = ' ';
815 /* move the center string to the right after merge */
816 format_align->right = format_align->center;
817 /* calculate the new width and position of the merged string */
818 right_width = center_width + space_width + right_width;
819 right_xpos = (display->getwidth() - right_width);
820 /* there is no centered string anymore */
821 center_width = 0;
823 /* there is no right string, move center to right */
824 if ((center_width != 0 && right_width == 0) &&
825 (center_xpos + center_width > right_xpos)) {
826 /* move the center string to the right string */
827 format_align->right = format_align->center;
828 /* calculate the new width and position of the string */
829 right_width = center_width;
830 right_xpos = (display->getwidth() - right_width);
831 /* there is no centered string anymore */
832 center_width = 0;
835 /* CASE 3: left and right overlap
836 There is no center string anymore, either there never
837 was one or it has been merged in case 1 or 2 */
838 /* there is a left string, need to merge left and right */
839 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
840 (left_xpos + left_width + space_width > right_xpos)) {
841 /* replace the former separator '\0' of left and
842 right string with a space */
843 *(--format_align->right) = ' ';
844 /* calculate the new width and position of the string */
845 left_width = left_width + space_width + right_width;
846 /* there is no right string anymore */
847 right_width = 0;
849 /* there is no left string, move right to left */
850 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
851 (left_width > right_xpos)) {
852 /* move the right string to the left string */
853 format_align->left = format_align->right;
854 /* calculate the new width and position of the string */
855 left_width = right_width;
856 /* there is no right string anymore */
857 right_width = 0;
860 ypos = (line * string_height);
863 if (scroll && ((left_width > scroll_width) ||
864 (center_width > scroll_width) ||
865 (right_width > scroll_width)))
867 display->puts_scroll(0, line,
868 (unsigned char *)format_align->left);
870 else
872 #ifdef HAVE_LCD_BITMAP
873 /* clear the line first */
874 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
875 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
876 display->set_drawmode(DRMODE_SOLID);
877 #endif
879 /* Nasty hack: we output an empty scrolling string,
880 which will reset the scroller for that line */
881 display->puts_scroll(0, line, (unsigned char *)"");
883 /* print aligned strings */
884 if (left_width != 0)
886 display->putsxy(left_xpos, ypos,
887 (unsigned char *)format_align->left);
889 if (center_width != 0)
891 display->putsxy(center_xpos, ypos,
892 (unsigned char *)format_align->center);
894 if (right_width != 0)
896 display->putsxy(right_xpos, ypos,
897 (unsigned char *)format_align->right);
902 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
904 struct wps_data *data = gwps->data;
905 struct screen *display = gwps->display;
906 struct wps_state *state = gwps->state;
908 if (!data || !state || !display)
909 return false;
911 struct mp3entry *id3 = state->id3;
913 if (!id3)
914 return false;
916 unsigned flags;
917 char linebuf[MAX_PATH];
919 struct align_pos align;
920 align.left = NULL;
921 align.center = NULL;
922 align.right = NULL;
925 struct skin_token_list *viewport_list;
927 bool update_line, new_subline_refresh;
929 #ifdef HAVE_LCD_BITMAP
931 /* to find out wether the peak meter is enabled we
932 assume it wasn't until we find a line that contains
933 the peak meter. We can't use peak_meter_enabled itself
934 because that would mean to turn off the meter thread
935 temporarily. (That shouldn't matter unless yield
936 or sleep is called but who knows...)
938 bool enable_pm = false;
940 #endif
942 /* reset to first subline if refresh all flag is set */
943 if (refresh_mode == WPS_REFRESH_ALL)
945 struct skin_line *line;
947 display->set_viewport(&find_viewport(VP_DEFAULT_LABEL, data)->vp);
948 display->clear_viewport();
950 for (viewport_list = data->viewports;
951 viewport_list; viewport_list = viewport_list->next)
953 struct skin_viewport *skin_viewport =
954 (struct skin_viewport *)viewport_list->token->value.data;
955 for(line = skin_viewport->lines; line; line = line->next)
957 line->curr_subline = NULL;
962 #ifdef HAVE_LCD_CHARCELLS
963 int i;
964 for (i = 0; i < 8; i++)
966 if (data->wps_progress_pat[i] == 0)
967 data->wps_progress_pat[i] = display->get_locked_pattern();
969 #endif
971 /* disable any viewports which are conditionally displayed */
972 for (viewport_list = data->viewports;
973 viewport_list; viewport_list = viewport_list->next)
975 struct skin_viewport *skin_viewport =
976 (struct skin_viewport *)viewport_list->token->value.data;
977 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
979 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
980 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
981 else
982 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
985 int viewport_count = 0;
986 for (viewport_list = data->viewports;
987 viewport_list; viewport_list = viewport_list->next, viewport_count++)
989 struct skin_viewport *skin_viewport =
990 (struct skin_viewport *)viewport_list->token->value.data;
991 unsigned vp_refresh_mode = refresh_mode;
992 display->set_viewport(&skin_viewport->vp);
994 #ifdef HAVE_LCD_BITMAP
995 /* Set images to not to be displayed */
996 struct skin_token_list *imglist = data->images;
997 while (imglist)
999 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
1000 img->display = -1;
1001 imglist = imglist->next;
1003 #endif
1004 /* dont redraw the viewport if its disabled */
1005 if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
1007 if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
1008 display->scroll_stop(&skin_viewport->vp);
1009 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1010 continue;
1012 else if (((skin_viewport->hidden_flags&
1013 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
1014 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
1016 vp_refresh_mode = WPS_REFRESH_ALL;
1017 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
1019 if (vp_refresh_mode == WPS_REFRESH_ALL)
1021 display->clear_viewport();
1024 /* loop over the lines for this viewport */
1025 struct skin_line *line;
1026 int line_count = 0;
1028 for (line = skin_viewport->lines; line; line = line->next, line_count++)
1030 struct skin_subline *subline;
1031 memset(linebuf, 0, sizeof(linebuf));
1032 update_line = false;
1034 /* get current subline for the line */
1035 new_subline_refresh = update_curr_subline(gwps, line);
1036 subline = line->curr_subline;
1037 flags = line->curr_subline->line_type;
1039 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1040 || new_subline_refresh)
1042 /* get_line tells us if we need to update the line */
1043 update_line = get_line(gwps, subline,
1044 &align, linebuf, sizeof(linebuf));
1046 #ifdef HAVE_LCD_BITMAP
1047 /* peakmeter */
1048 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1050 /* the peakmeter should be alone on its line */
1051 update_line = false;
1053 int h = font_get(skin_viewport->vp.font)->height;
1054 int peak_meter_y = line_count* h;
1056 /* The user might decide to have the peak meter in the last
1057 line so that it is only displayed if no status bar is
1058 visible. If so we neither want do draw nor enable the
1059 peak meter. */
1060 if (peak_meter_y + h <= skin_viewport->vp.y+skin_viewport->vp.height) {
1061 /* found a line with a peak meter -> remember that we must
1062 enable it later */
1063 enable_pm = true;
1064 peak_meter_enabled = true;
1065 peak_meter_screen(gwps->display, 0, peak_meter_y,
1066 MIN(h, skin_viewport->vp.y+skin_viewport->vp.height - peak_meter_y));
1068 else
1070 peak_meter_enabled = false;
1074 #else /* HAVE_LCD_CHARCELL */
1076 /* progressbar */
1077 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1079 if (data->full_line_progressbar)
1080 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1081 else
1082 draw_player_progress(gwps);
1084 #endif
1086 if (update_line &&
1087 /* conditionals clear the line which means if the %Vd is put into the default
1088 viewport there will be a blank line.
1089 To get around this we dont allow any actual drawing to happen in the
1090 deault vp if other vp's are defined */
1091 ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
1092 !viewport_list->next))
1094 if (flags & WPS_REFRESH_SCROLL)
1096 /* if the line is a scrolling one we don't want to update
1097 too often, so that it has the time to scroll */
1098 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1099 write_line(display, &align, line_count, true);
1101 else
1102 write_line(display, &align, line_count, false);
1106 #ifdef HAVE_LCD_BITMAP
1107 /* progressbar */
1108 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1110 if (skin_viewport->pb)
1112 draw_progressbar(gwps, skin_viewport);
1115 /* Now display any images in this viewport */
1116 wps_display_images(gwps, &skin_viewport->vp);
1117 #endif
1120 #ifdef HAVE_LCD_BITMAP
1121 data->peak_meter_enabled = enable_pm;
1122 #endif
1124 if (refresh_mode & WPS_REFRESH_STATUSBAR)
1126 viewportmanager_set_statusbar(*gwps->statusbars);
1128 /* Restore the default viewport */
1129 display->set_viewport(NULL);
1131 display->update();
1133 return true;