initial custom statusbar commit
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
blobef1c39c1c7878c2bcb0042d8179da50c46304955
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 /* TODO: maybe move this whole function into wps.c instead ? */
72 bool gui_wps_display(struct gui_wps *gwps)
74 struct screen *display = gwps->display;
76 /* Update the values in the first (default) viewport - in case the user
77 has modified the statusbar or colour settings */
78 #if LCD_DEPTH > 1
79 if (display->depth > 1)
81 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, gwps->data)->vp;
82 vp->fg_pattern = display->get_foreground();
83 vp->bg_pattern = display->get_background();
85 #endif
86 display->clear_display();
87 display->backdrop_show(BACKDROP_SKIN_WPS);
88 return skin_redraw(gwps, WPS_REFRESH_ALL);
91 /* update a skinned screen, update_type is WPS_REFRESH_* values.
92 * Usually it should only be WPS_REFRESH_NON_STATIC
93 * A full update will be done if required (state.do_full_update == true)
95 bool skin_update(struct gui_wps *gwps, unsigned int update_type)
97 bool retval;
98 /* This maybe shouldnt be here, but while the skin is only used to
99 * display the music screen this is better than whereever we are being
100 * called from. This is also safe for skined screen which dont use the id3 */
101 struct mp3entry *id3 = gwps->state->id3;
102 bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
103 gwps->state->do_full_update = cuesheet_update || gwps->state->do_full_update;
105 retval = skin_redraw(gwps, gwps->state->do_full_update ?
106 WPS_REFRESH_ALL : update_type);
107 return retval;
110 #ifdef HAVE_LCD_BITMAP
112 void skin_statusbar_changed(struct gui_wps *skin)
114 if (!skin)
115 return;
116 struct wps_data *data = skin->data;
117 const struct screen *display = skin->display;
119 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, data)->vp;
120 viewport_set_fullscreen(vp, display->screen_type);
122 if (data->wps_sb_tag)
123 { /* fix up the default viewport */
124 if (data->show_sb_on_wps)
126 bool bar_at_top =
127 statusbar_position(display->screen_type) != STATUSBAR_BOTTOM;
129 vp->y = bar_at_top?STATUSBAR_HEIGHT:0;
130 vp->height = display->lcdheight - STATUSBAR_HEIGHT;
132 else
134 vp->y = 0;
135 vp->height = display->lcdheight;
142 static void draw_progressbar(struct gui_wps *gwps,
143 struct skin_viewport *wps_vp)
145 struct screen *display = gwps->display;
146 struct wps_state *state = gwps->state;
147 struct progressbar *pb = wps_vp->pb;
148 struct mp3entry *id3 = state->id3;
149 int y = pb->y;
151 if (y < 0)
153 int line_height = font_get(wps_vp->vp.font)->height;
154 /* center the pb in the line, but only if the line is higher than the pb */
155 int center = (line_height-pb->height)/2;
156 /* if Y was not set calculate by font height,Y is -line_number-1 */
157 y = (-y -1)*line_height + (0 > center ? 0 : center);
160 int elapsed, length;
161 if (id3)
163 elapsed = id3->elapsed;
164 length = id3->length;
166 else
168 elapsed = 0;
169 length = 0;
172 if (pb->have_bitmap_pb)
173 gui_bitmap_scrollbar_draw(display, pb->bm,
174 pb->x, y, pb->width, pb->bm.height,
175 length ? length : 1, 0,
176 length ? elapsed + state->ff_rewind_count : 0,
177 HORIZONTAL);
178 else
179 gui_scrollbar_draw(display, pb->x, y, pb->width, pb->height,
180 length ? length : 1, 0,
181 length ? elapsed + state->ff_rewind_count : 0,
182 HORIZONTAL);
183 #ifdef AB_REPEAT_ENABLE
184 if ( ab_repeat_mode_enabled() && length != 0 )
185 ab_draw_markers(display, length,
186 pb->x, pb->x + pb->width, y, pb->height);
187 #endif
189 if (id3 && id3->cuesheet)
190 cue_draw_markers(display, state->id3->cuesheet, length,
191 pb->x, pb->x + pb->width, y+1, pb->height-2);
194 /* clears the area where the image was shown */
195 static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
197 if(!gwps)
198 return;
199 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
200 gwps->display->fillrect(img->x, img->y, img->bm.width, img->subimage_height);
201 gwps->display->set_drawmode(DRMODE_SOLID);
204 static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage)
206 struct screen *display = gwps->display;
207 if(img->always_display)
208 display->set_drawmode(DRMODE_FG);
209 else
210 display->set_drawmode(DRMODE_SOLID);
212 #if LCD_DEPTH > 1
213 if(img->bm.format == FORMAT_MONO) {
214 #endif
215 display->mono_bitmap_part(img->bm.data,
216 0, img->subimage_height * subimage,
217 img->bm.width, img->x,
218 img->y, img->bm.width,
219 img->subimage_height);
220 #if LCD_DEPTH > 1
221 } else {
222 display->transparent_bitmap_part((fb_data *)img->bm.data,
223 0, img->subimage_height * subimage,
224 STRIDE(display->screen_type,
225 img->bm.width, img->bm.height),
226 img->x, img->y, img->bm.width,
227 img->subimage_height);
229 #endif
232 static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
234 if(!gwps || !gwps->data || !gwps->display)
235 return;
237 struct wps_data *data = gwps->data;
238 struct screen *display = gwps->display;
239 struct skin_token_list *list = data->images;
241 while (list)
243 struct gui_img *img = (struct gui_img*)list->token->value.data;
244 if (img->loaded)
246 if (img->display >= 0)
248 wps_draw_image(gwps, img, img->display);
250 else if (img->always_display && img->vp == vp)
252 wps_draw_image(gwps, img, 0);
255 list = list->next;
257 #ifdef HAVE_ALBUMART
258 /* now draw the AA */
259 if (data->albumart && data->albumart->vp == vp
260 && data->albumart->draw)
262 draw_album_art(gwps, playback_current_aa_hid(data->playback_aa_slot),
263 false);
264 data->albumart->draw = false;
266 #endif
268 display->set_drawmode(DRMODE_SOLID);
271 #else /* HAVE_LCD_CHARCELL */
273 static bool draw_player_progress(struct gui_wps *gwps)
275 struct wps_state *state = gwps->state;
276 struct screen *display = gwps->display;
277 unsigned char progress_pattern[7];
278 int pos = 0;
279 int i;
281 int elapsed, length;
282 if (LIKELY(state->id3))
284 elapsed = state->id3->elapsed;
285 length = state->id3->length;
287 else
289 elapsed = 0;
290 length = 0;
293 if (length)
294 pos = 36 * (elapsed + state->ff_rewind_count) / length;
296 for (i = 0; i < 7; i++, pos -= 5)
298 if (pos <= 0)
299 progress_pattern[i] = 0x1fu;
300 else if (pos >= 5)
301 progress_pattern[i] = 0x00u;
302 else
303 progress_pattern[i] = 0x1fu >> pos;
306 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
307 return true;
310 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
312 static const unsigned char numbers[10][4] = {
313 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
314 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
315 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
316 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
317 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
318 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
319 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
320 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
321 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
322 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
325 struct wps_state *state = gwps->state;
326 struct screen *display = gwps->display;
327 struct wps_data *data = gwps->data;
328 unsigned char progress_pattern[7];
329 char timestr[10];
330 int time;
331 int time_idx = 0;
332 int pos = 0;
333 int pat_idx = 1;
334 int digit, i, j;
335 bool softchar;
337 int elapsed, length;
338 if (LIKELY(state->id3))
340 elapsed = id3->elapsed;
341 length = id3->length;
343 else
345 elapsed = 0;
346 length = 0;
349 if (buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
350 return;
352 time = elapsed + state->ff_rewind_count;
353 if (length)
354 pos = 55 * time / length;
356 memset(timestr, 0, sizeof(timestr));
357 format_time(timestr, sizeof(timestr)-2, time);
358 timestr[strlen(timestr)] = ':'; /* always safe */
360 for (i = 0; i < 11; i++, pos -= 5)
362 softchar = false;
363 memset(progress_pattern, 0, sizeof(progress_pattern));
365 if ((digit = timestr[time_idx]))
367 softchar = true;
368 digit -= '0';
370 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
372 memcpy(progress_pattern, numbers[digit], 4);
373 time_idx += 2;
375 else /* tens, shifted right */
377 for (j = 0; j < 4; j++)
378 progress_pattern[j] = numbers[digit][j] >> 1;
380 if (time_idx > 0) /* not the first group, add colon in front */
382 progress_pattern[1] |= 0x10u;
383 progress_pattern[3] |= 0x10u;
385 time_idx++;
388 if (pos >= 5)
389 progress_pattern[5] = progress_pattern[6] = 0x1fu;
392 if (pos > 0 && pos < 5)
394 softchar = true;
395 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
398 if (softchar && pat_idx < 8)
400 display->define_pattern(data->wps_progress_pat[pat_idx],
401 progress_pattern);
402 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
403 pat_idx++;
405 else if (pos <= 0)
406 buf = utf8encode(' ', buf);
407 else
408 buf = utf8encode(0xe115, buf); /* 2/7 _ */
410 *buf = '\0';
413 #endif /* HAVE_LCD_CHARCELL */
415 /* Return the index to the end token for the conditional token at index.
416 The conditional token can be either a start token or a separator
417 (i.e. option) token.
419 static int find_conditional_end(struct wps_data *data, int index)
421 int ret = index;
422 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
423 ret = data->tokens[ret].value.i;
425 /* ret now is the index to the end token for the conditional. */
426 return ret;
429 /* Evaluate the conditional that is at *token_index and return whether a skip
430 has ocurred. *token_index is updated with the new position.
432 static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
434 if (!gwps)
435 return false;
437 struct wps_data *data = gwps->data;
439 int i, cond_end;
440 int cond_index = *token_index;
441 char result[128];
442 const char *value;
443 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
444 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
446 /* treat ?xx<true> constructs as if they had 2 options. */
447 if (num_options < 2)
448 num_options = 2;
450 int intval = num_options;
451 /* get_token_value needs to know the number of options in the enum */
452 value = get_token_value(gwps, &data->tokens[cond_index + 1],
453 result, sizeof(result), &intval);
455 /* intval is now the number of the enum option we want to read,
456 starting from 1. If intval is -1, we check if value is empty. */
457 if (intval == -1)
458 intval = (value && *value) ? 1 : num_options;
459 else if (intval > num_options || intval < 1)
460 intval = num_options;
462 data->tokens[cond_index].value.i = (intval << 8) + num_options;
464 /* skip to the appropriate enum case */
465 int next = cond_index + 2;
466 for (i = 1; i < intval; i++)
468 next = data->tokens[next].value.i;
470 *token_index = next;
472 if (prev_val == intval)
474 /* Same conditional case as previously. Return without clearing the
475 pictures */
476 return false;
479 cond_end = find_conditional_end(data, cond_index + 2);
480 for (i = cond_index + 3; i < cond_end; i++)
482 #ifdef HAVE_LCD_BITMAP
483 /* clear all pictures in the conditional and nested ones */
484 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
486 struct gui_img *tmp = find_image(data->tokens[i].value.i&0xFF,
487 data);
488 if (tmp)
489 clear_image_pos(gwps, tmp);
491 #endif
492 #ifdef HAVE_ALBUMART
493 if (data->albumart && data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
495 draw_album_art(gwps,
496 playback_current_aa_hid(data->playback_aa_slot), true);
497 data->albumart->draw = false;
499 #endif
502 return true;
504 #ifdef HAVE_LCD_BITMAP
505 struct gui_img* find_image(char label, struct wps_data *data)
507 static int i = 0;
508 struct gui_img *ret = NULL;
509 struct skin_token_list *list = data->images;
510 if (data->debug)
512 DEBUGF("%s >> requesting image (id: %d)\n", __func__, n);
513 DEBUGF("%s >> first list data (p: %p\n", __func__, data->images);
515 while (list)
517 struct gui_img *img = (struct gui_img *)list->token->value.data;
518 if (img->label == label)
520 i = 0;
521 ret = img; goto end;
523 list = list->next;
525 if (!list && data->debug)
526 DEBUGF("failed to find: %s\n", img->bm.data);
529 i = 0;
530 end:
531 if (data->debug)
532 DEBUGF("%s >> returning %p\n", __func__, ret);
533 return ret;
535 #endif
537 struct skin_viewport* find_viewport(char label, struct wps_data *data)
539 struct skin_token_list *list = data->viewports;
540 while (list)
542 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
543 if (vp->label == label)
544 return vp;
545 list = list->next;
547 return NULL;
550 /* Read a (sub)line to the given alignment format buffer.
551 linebuf is the buffer where the data is actually stored.
552 align is the alignment format that'll be used to display the text.
553 The return value indicates whether the line needs to be updated.
555 static bool get_line(struct gui_wps *gwps,
556 struct skin_subline *subline,
557 struct align_pos *align,
558 char *linebuf,
559 int linebuf_size)
561 struct wps_data *data = gwps->data;
563 char temp_buf[128];
564 char *buf = linebuf; /* will always point to the writing position */
565 char *linebuf_end = linebuf + linebuf_size - 1;
566 bool update = false;
567 int i;
569 /* alignment-related variables */
570 int cur_align;
571 char* cur_align_start;
572 cur_align_start = buf;
573 cur_align = WPS_ALIGN_LEFT;
574 align->left = NULL;
575 align->center = NULL;
576 align->right = NULL;
577 /* Process all tokens of the desired subline */
578 for (i = subline->first_token_idx;
579 i <= subline->last_token_idx; i++)
581 switch(data->tokens[i].type)
583 case WPS_TOKEN_CONDITIONAL:
584 /* place ourselves in the right conditional case */
585 update |= evaluate_conditional(gwps, &i);
586 break;
588 case WPS_TOKEN_CONDITIONAL_OPTION:
589 /* we've finished in the curent conditional case,
590 skip to the end of the conditional structure */
591 i = find_conditional_end(data, i);
592 break;
594 #ifdef HAVE_LCD_BITMAP
595 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
597 char n = data->tokens[i].value.i & 0xFF;
598 int subimage = data->tokens[i].value.i >> 8;
599 struct gui_img *img = find_image(n, data);
601 if (img && img->loaded)
602 img->display = subimage;
603 break;
605 #endif
607 case WPS_TOKEN_ALIGN_LEFT:
608 case WPS_TOKEN_ALIGN_CENTER:
609 case WPS_TOKEN_ALIGN_RIGHT:
610 /* remember where the current aligned text started */
611 switch (cur_align)
613 case WPS_ALIGN_LEFT:
614 align->left = cur_align_start;
615 break;
617 case WPS_ALIGN_CENTER:
618 align->center = cur_align_start;
619 break;
621 case WPS_ALIGN_RIGHT:
622 align->right = cur_align_start;
623 break;
625 /* start a new alignment */
626 switch (data->tokens[i].type)
628 case WPS_TOKEN_ALIGN_LEFT:
629 cur_align = WPS_ALIGN_LEFT;
630 break;
631 case WPS_TOKEN_ALIGN_CENTER:
632 cur_align = WPS_ALIGN_CENTER;
633 break;
634 case WPS_TOKEN_ALIGN_RIGHT:
635 cur_align = WPS_ALIGN_RIGHT;
636 break;
637 default:
638 break;
640 *buf++ = 0;
641 cur_align_start = buf;
642 break;
643 case WPS_VIEWPORT_ENABLE:
645 char label = data->tokens[i].value.i;
646 char temp = VP_DRAW_HIDEABLE;
647 /* viewports are allowed to share id's so find and enable
648 * all of them */
649 struct skin_token_list *list = data->viewports;
650 while (list)
652 struct skin_viewport *vp =
653 (struct skin_viewport *)list->token->value.data;
654 if (vp->label == label)
656 if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
657 temp |= VP_DRAW_WASHIDDEN;
658 vp->hidden_flags = temp;
660 list = list->next;
663 break;
664 default:
666 /* get the value of the tag and copy it to the buffer */
667 const char *value = get_token_value(gwps, &data->tokens[i],
668 temp_buf, sizeof(temp_buf), NULL);
669 if (value)
671 update = true;
672 while (*value && (buf < linebuf_end))
673 *buf++ = *value++;
675 break;
680 /* close the current alignment */
681 switch (cur_align)
683 case WPS_ALIGN_LEFT:
684 align->left = cur_align_start;
685 break;
687 case WPS_ALIGN_CENTER:
688 align->center = cur_align_start;
689 break;
691 case WPS_ALIGN_RIGHT:
692 align->right = cur_align_start;
693 break;
696 return update;
698 static void get_subline_timeout(struct gui_wps *gwps, struct skin_subline *subline)
700 struct wps_data *data = gwps->data;
701 int i;
702 subline->time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
704 for (i = subline->first_token_idx;
705 i <= subline->last_token_idx; i++)
707 switch(data->tokens[i].type)
709 case WPS_TOKEN_CONDITIONAL:
710 /* place ourselves in the right conditional case */
711 evaluate_conditional(gwps, &i);
712 break;
714 case WPS_TOKEN_CONDITIONAL_OPTION:
715 /* we've finished in the curent conditional case,
716 skip to the end of the conditional structure */
717 i = find_conditional_end(data, i);
718 break;
720 case WPS_TOKEN_SUBLINE_TIMEOUT:
721 subline->time_mult = data->tokens[i].value.i;
722 break;
724 default:
725 break;
730 /* Calculates which subline should be displayed for the specified line
731 Returns true iff the subline must be refreshed */
732 static bool update_curr_subline(struct gui_wps *gwps, struct skin_line *line)
734 /* shortcut this whole thing if we need to reset the line completly */
735 if (line->curr_subline == NULL)
737 line->subline_expire_time = current_tick;
738 line->curr_subline = &line->sublines;
739 if (!line->curr_subline->next)
741 line->subline_expire_time += 100*HZ;
743 else
745 get_subline_timeout(gwps, line->curr_subline);
746 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
748 return true;
750 /* if time to advance to next sub-line */
751 if (TIME_AFTER(current_tick, line->subline_expire_time - 1))
753 /* if there is only one subline, there is no need to search for a new one */
754 if (&line->sublines == line->curr_subline &&
755 line->curr_subline->next == NULL)
757 line->subline_expire_time += 100 * HZ;
758 return false;
760 if (line->curr_subline->next)
761 line->curr_subline = line->curr_subline->next;
762 else
763 line->curr_subline = &line->sublines;
764 get_subline_timeout(gwps, line->curr_subline);
765 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
766 return true;
768 return false;
771 /* Display a line appropriately according to its alignment format.
772 format_align contains the text, separated between left, center and right.
773 line is the index of the line on the screen.
774 scroll indicates whether the line is a scrolling one or not.
776 static void write_line(struct screen *display,
777 struct align_pos *format_align,
778 int line,
779 bool scroll)
781 int left_width = 0, left_xpos;
782 int center_width = 0, center_xpos;
783 int right_width = 0, right_xpos;
784 int ypos;
785 int space_width;
786 int string_height;
787 int scroll_width;
789 /* calculate different string sizes and positions */
790 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
791 if (format_align->left != 0) {
792 display->getstringsize((unsigned char *)format_align->left,
793 &left_width, &string_height);
796 if (format_align->right != 0) {
797 display->getstringsize((unsigned char *)format_align->right,
798 &right_width, &string_height);
801 if (format_align->center != 0) {
802 display->getstringsize((unsigned char *)format_align->center,
803 &center_width, &string_height);
806 left_xpos = 0;
807 right_xpos = (display->getwidth() - right_width);
808 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
810 scroll_width = display->getwidth() - left_xpos;
812 /* Checks for overlapping strings.
813 If needed the overlapping strings will be merged, separated by a
814 space */
816 /* CASE 1: left and centered string overlap */
817 /* there is a left string, need to merge left and center */
818 if ((left_width != 0 && center_width != 0) &&
819 (left_xpos + left_width + space_width > center_xpos)) {
820 /* replace the former separator '\0' of left and
821 center string with a space */
822 *(--format_align->center) = ' ';
823 /* calculate the new width and position of the merged string */
824 left_width = left_width + space_width + center_width;
825 /* there is no centered string anymore */
826 center_width = 0;
828 /* there is no left string, move center to left */
829 if ((left_width == 0 && center_width != 0) &&
830 (left_xpos + left_width > center_xpos)) {
831 /* move the center string to the left string */
832 format_align->left = format_align->center;
833 /* calculate the new width and position of the string */
834 left_width = center_width;
835 /* there is no centered string anymore */
836 center_width = 0;
839 /* CASE 2: centered and right string overlap */
840 /* there is a right string, need to merge center and right */
841 if ((center_width != 0 && right_width != 0) &&
842 (center_xpos + center_width + space_width > right_xpos)) {
843 /* replace the former separator '\0' of center and
844 right string with a space */
845 *(--format_align->right) = ' ';
846 /* move the center string to the right after merge */
847 format_align->right = format_align->center;
848 /* calculate the new width and position of the merged string */
849 right_width = center_width + space_width + right_width;
850 right_xpos = (display->getwidth() - right_width);
851 /* there is no centered string anymore */
852 center_width = 0;
854 /* there is no right string, move center to right */
855 if ((center_width != 0 && right_width == 0) &&
856 (center_xpos + center_width > right_xpos)) {
857 /* move the center string to the right string */
858 format_align->right = format_align->center;
859 /* calculate the new width and position of the string */
860 right_width = center_width;
861 right_xpos = (display->getwidth() - right_width);
862 /* there is no centered string anymore */
863 center_width = 0;
866 /* CASE 3: left and right overlap
867 There is no center string anymore, either there never
868 was one or it has been merged in case 1 or 2 */
869 /* there is a left string, need to merge left and right */
870 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
871 (left_xpos + left_width + space_width > right_xpos)) {
872 /* replace the former separator '\0' of left and
873 right string with a space */
874 *(--format_align->right) = ' ';
875 /* calculate the new width and position of the string */
876 left_width = left_width + space_width + right_width;
877 /* there is no right string anymore */
878 right_width = 0;
880 /* there is no left string, move right to left */
881 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
882 (left_width > right_xpos)) {
883 /* move the right string to the left string */
884 format_align->left = format_align->right;
885 /* calculate the new width and position of the string */
886 left_width = right_width;
887 /* there is no right string anymore */
888 right_width = 0;
891 ypos = (line * string_height);
894 if (scroll && ((left_width > scroll_width) ||
895 (center_width > scroll_width) ||
896 (right_width > scroll_width)))
898 display->puts_scroll(0, line,
899 (unsigned char *)format_align->left);
901 else
903 #ifdef HAVE_LCD_BITMAP
904 /* clear the line first */
905 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
906 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
907 display->set_drawmode(DRMODE_SOLID);
908 #endif
910 /* Nasty hack: we output an empty scrolling string,
911 which will reset the scroller for that line */
912 display->puts_scroll(0, line, (unsigned char *)"");
914 /* print aligned strings */
915 if (left_width != 0)
917 display->putsxy(left_xpos, ypos,
918 (unsigned char *)format_align->left);
920 if (center_width != 0)
922 display->putsxy(center_xpos, ypos,
923 (unsigned char *)format_align->center);
925 if (right_width != 0)
927 display->putsxy(right_xpos, ypos,
928 (unsigned char *)format_align->right);
933 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
935 struct wps_data *data = gwps->data;
936 struct screen *display = gwps->display;
938 if (!data || !display || !gwps->state)
939 return false;
941 unsigned flags;
942 char linebuf[MAX_PATH];
944 struct align_pos align;
945 align.left = NULL;
946 align.center = NULL;
947 align.right = NULL;
950 struct skin_token_list *viewport_list;
952 bool update_line, new_subline_refresh;
954 #ifdef HAVE_LCD_BITMAP
956 /* to find out wether the peak meter is enabled we
957 assume it wasn't until we find a line that contains
958 the peak meter. We can't use peak_meter_enabled itself
959 because that would mean to turn off the meter thread
960 temporarily. (That shouldn't matter unless yield
961 or sleep is called but who knows...)
963 bool enable_pm = false;
965 #endif
967 /* reset to first subline if refresh all flag is set */
968 if (refresh_mode == WPS_REFRESH_ALL)
970 struct skin_line *line;
972 display->set_viewport(&find_viewport(VP_DEFAULT_LABEL, data)->vp);
973 display->clear_viewport();
975 for (viewport_list = data->viewports;
976 viewport_list; viewport_list = viewport_list->next)
978 struct skin_viewport *skin_viewport =
979 (struct skin_viewport *)viewport_list->token->value.data;
980 for(line = skin_viewport->lines; line; line = line->next)
982 line->curr_subline = NULL;
987 #ifdef HAVE_LCD_CHARCELLS
988 int i;
989 for (i = 0; i < 8; i++)
991 if (data->wps_progress_pat[i] == 0)
992 data->wps_progress_pat[i] = display->get_locked_pattern();
994 #endif
996 /* disable any viewports which are conditionally displayed */
997 for (viewport_list = data->viewports;
998 viewport_list; viewport_list = viewport_list->next)
1000 struct skin_viewport *skin_viewport =
1001 (struct skin_viewport *)viewport_list->token->value.data;
1002 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
1004 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
1005 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1006 else
1007 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
1010 int viewport_count = 0;
1011 for (viewport_list = data->viewports;
1012 viewport_list; viewport_list = viewport_list->next, viewport_count++)
1014 struct skin_viewport *skin_viewport =
1015 (struct skin_viewport *)viewport_list->token->value.data;
1016 unsigned vp_refresh_mode = refresh_mode;
1017 display->set_viewport(&skin_viewport->vp);
1019 #ifdef HAVE_LCD_BITMAP
1020 /* Set images to not to be displayed */
1021 struct skin_token_list *imglist = data->images;
1022 while (imglist)
1024 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
1025 img->display = -1;
1026 imglist = imglist->next;
1028 #endif
1029 /* dont redraw the viewport if its disabled */
1030 if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
1032 if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
1033 display->scroll_stop(&skin_viewport->vp);
1034 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1035 continue;
1037 else if (((skin_viewport->hidden_flags&
1038 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
1039 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
1041 vp_refresh_mode = WPS_REFRESH_ALL;
1042 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
1044 if (vp_refresh_mode == WPS_REFRESH_ALL)
1046 display->clear_viewport();
1049 /* loop over the lines for this viewport */
1050 struct skin_line *line;
1051 int line_count = 0;
1053 for (line = skin_viewport->lines; line; line = line->next, line_count++)
1055 struct skin_subline *subline;
1056 memset(linebuf, 0, sizeof(linebuf));
1057 update_line = false;
1059 /* get current subline for the line */
1060 new_subline_refresh = update_curr_subline(gwps, line);
1061 subline = line->curr_subline;
1062 flags = line->curr_subline->line_type;
1064 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1065 || new_subline_refresh)
1067 /* get_line tells us if we need to update the line */
1068 update_line = get_line(gwps, subline,
1069 &align, linebuf, sizeof(linebuf));
1071 #ifdef HAVE_LCD_BITMAP
1072 /* peakmeter */
1073 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1075 /* the peakmeter should be alone on its line */
1076 update_line = false;
1078 int h = font_get(skin_viewport->vp.font)->height;
1079 int peak_meter_y = line_count* h;
1081 /* The user might decide to have the peak meter in the last
1082 line so that it is only displayed if no status bar is
1083 visible. If so we neither want do draw nor enable the
1084 peak meter. */
1085 if (peak_meter_y + h <= skin_viewport->vp.y+skin_viewport->vp.height) {
1086 /* found a line with a peak meter -> remember that we must
1087 enable it later */
1088 enable_pm = true;
1089 peak_meter_enabled = true;
1090 peak_meter_screen(gwps->display, 0, peak_meter_y,
1091 MIN(h, skin_viewport->vp.y+skin_viewport->vp.height - peak_meter_y));
1093 else
1095 peak_meter_enabled = false;
1099 #else /* HAVE_LCD_CHARCELL */
1101 /* progressbar */
1102 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1104 if (data->full_line_progressbar)
1105 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1106 else
1107 draw_player_progress(gwps);
1109 #endif
1111 if (update_line &&
1112 /* conditionals clear the line which means if the %Vd is put into the default
1113 viewport there will be a blank line.
1114 To get around this we dont allow any actual drawing to happen in the
1115 deault vp if other vp's are defined */
1116 ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
1117 !viewport_list->next))
1119 if (flags & WPS_REFRESH_SCROLL)
1121 /* if the line is a scrolling one we don't want to update
1122 too often, so that it has the time to scroll */
1123 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1124 write_line(display, &align, line_count, true);
1126 else
1127 write_line(display, &align, line_count, false);
1131 #ifdef HAVE_LCD_BITMAP
1132 /* progressbar */
1133 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1135 if (skin_viewport->pb)
1137 draw_progressbar(gwps, skin_viewport);
1140 /* Now display any images in this viewport */
1141 wps_display_images(gwps, &skin_viewport->vp);
1142 #endif
1145 #ifdef HAVE_LCD_BITMAP
1146 data->peak_meter_enabled = enable_pm;
1147 #endif
1149 if (refresh_mode & WPS_REFRESH_STATUSBAR)
1151 viewportmanager_set_statusbar(*gwps->statusbars);
1153 /* Restore the default viewport */
1154 display->set_viewport(NULL);
1156 display->update();
1158 return true;