Fix a few potential redraw problems with the custom statusbar and wps fighting for...
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
blob13751a9b1cc2594245b73015764cf8fa49fd5ea6
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"
67 #include "statusbar-skinned.h"
69 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode);
72 /* TODO: maybe move this whole function into wps.c instead ? */
73 bool gui_wps_display(struct gui_wps *gwps)
75 struct screen *display = gwps->display;
77 /* Update the values in the first (default) viewport - in case the user
78 has modified the statusbar or colour settings */
79 #if LCD_DEPTH > 1
80 if (display->depth > 1)
82 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, gwps->data)->vp;
83 vp->fg_pattern = display->get_foreground();
84 vp->bg_pattern = display->get_background();
86 #endif
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->sync_data->do_full_update |= cuesheet_update;
105 retval = skin_redraw(gwps, gwps->sync_data->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;
118 const int screen = display->screen_type;
120 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, data)->vp;
121 viewport_set_fullscreen(vp, screen);
123 if (data->wps_sb_tag)
124 { /* fix up the default viewport */
125 if (data->show_sb_on_wps)
127 if (statusbar_position(screen) != STATUSBAR_OFF)
128 return; /* vp is fixed already */
130 vp->y = STATUSBAR_HEIGHT;
131 vp->height = display->lcdheight - STATUSBAR_HEIGHT;
133 else
135 if (statusbar_position(screen) == STATUSBAR_OFF)
136 return; /* vp is fixed already */
137 vp->y = vp->x = 0;
138 vp->height = display->lcdheight;
139 vp->width = display->lcdwidth;
144 static void draw_progressbar(struct gui_wps *gwps,
145 struct skin_viewport *wps_vp)
147 struct screen *display = gwps->display;
148 struct wps_state *state = gwps->state;
149 struct progressbar *pb = wps_vp->pb;
150 struct mp3entry *id3 = state->id3;
151 int y = pb->y;
153 if (y < 0)
155 int line_height = font_get(wps_vp->vp.font)->height;
156 /* center the pb in the line, but only if the line is higher than the pb */
157 int center = (line_height-pb->height)/2;
158 /* if Y was not set calculate by font height,Y is -line_number-1 */
159 y = (-y -1)*line_height + (0 > center ? 0 : center);
162 int elapsed, length;
163 if (id3)
165 elapsed = id3->elapsed;
166 length = id3->length;
168 else
170 elapsed = 0;
171 length = 0;
174 if (pb->have_bitmap_pb)
175 gui_bitmap_scrollbar_draw(display, pb->bm,
176 pb->x, y, pb->width, pb->bm.height,
177 length ? length : 1, 0,
178 length ? elapsed + state->ff_rewind_count : 0,
179 HORIZONTAL);
180 else
181 gui_scrollbar_draw(display, pb->x, y, pb->width, pb->height,
182 length ? length : 1, 0,
183 length ? elapsed + state->ff_rewind_count : 0,
184 HORIZONTAL);
185 #ifdef AB_REPEAT_ENABLE
186 if ( ab_repeat_mode_enabled() && length != 0 )
187 ab_draw_markers(display, length,
188 pb->x, pb->x + pb->width, y, pb->height);
189 #endif
191 if (id3 && id3->cuesheet)
192 cue_draw_markers(display, state->id3->cuesheet, length,
193 pb->x, pb->x + pb->width, y+1, pb->height-2);
196 /* clears the area where the image was shown */
197 static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
199 if(!gwps)
200 return;
201 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
202 gwps->display->fillrect(img->x, img->y, img->bm.width, img->subimage_height);
203 gwps->display->set_drawmode(DRMODE_SOLID);
206 static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage)
208 struct screen *display = gwps->display;
209 if(img->always_display)
210 display->set_drawmode(DRMODE_FG);
211 else
212 display->set_drawmode(DRMODE_SOLID);
214 #if LCD_DEPTH > 1
215 if(img->bm.format == FORMAT_MONO) {
216 #endif
217 display->mono_bitmap_part(img->bm.data,
218 0, img->subimage_height * subimage,
219 img->bm.width, img->x,
220 img->y, img->bm.width,
221 img->subimage_height);
222 #if LCD_DEPTH > 1
223 } else {
224 display->transparent_bitmap_part((fb_data *)img->bm.data,
225 0, img->subimage_height * subimage,
226 STRIDE(display->screen_type,
227 img->bm.width, img->bm.height),
228 img->x, img->y, img->bm.width,
229 img->subimage_height);
231 #endif
234 static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
236 if(!gwps || !gwps->data || !gwps->display)
237 return;
239 struct wps_data *data = gwps->data;
240 struct screen *display = gwps->display;
241 struct skin_token_list *list = data->images;
243 while (list)
245 struct gui_img *img = (struct gui_img*)list->token->value.data;
246 if (img->loaded)
248 if (img->display >= 0)
250 wps_draw_image(gwps, img, img->display);
252 else if (img->always_display && img->vp == vp)
254 wps_draw_image(gwps, img, 0);
257 list = list->next;
259 #ifdef HAVE_ALBUMART
260 /* now draw the AA */
261 if (data->albumart && data->albumart->vp == vp
262 && data->albumart->draw)
264 draw_album_art(gwps, playback_current_aa_hid(data->playback_aa_slot),
265 false);
266 data->albumart->draw = false;
268 #endif
270 display->set_drawmode(DRMODE_SOLID);
273 #else /* HAVE_LCD_CHARCELL */
275 static bool draw_player_progress(struct gui_wps *gwps)
277 struct wps_state *state = gwps->state;
278 struct screen *display = gwps->display;
279 unsigned char progress_pattern[7];
280 int pos = 0;
281 int i;
283 int elapsed, length;
284 if (LIKELY(state->id3))
286 elapsed = state->id3->elapsed;
287 length = state->id3->length;
289 else
291 elapsed = 0;
292 length = 0;
295 if (length)
296 pos = 36 * (elapsed + state->ff_rewind_count) / length;
298 for (i = 0; i < 7; i++, pos -= 5)
300 if (pos <= 0)
301 progress_pattern[i] = 0x1fu;
302 else if (pos >= 5)
303 progress_pattern[i] = 0x00u;
304 else
305 progress_pattern[i] = 0x1fu >> pos;
308 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
309 return true;
312 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
314 static const unsigned char numbers[10][4] = {
315 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
316 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
317 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
318 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
319 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
320 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
321 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
322 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
323 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
324 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
327 struct wps_state *state = gwps->state;
328 struct screen *display = gwps->display;
329 struct wps_data *data = gwps->data;
330 unsigned char progress_pattern[7];
331 char timestr[10];
332 int time;
333 int time_idx = 0;
334 int pos = 0;
335 int pat_idx = 1;
336 int digit, i, j;
337 bool softchar;
339 int elapsed, length;
340 if (LIKELY(state->id3))
342 elapsed = state->id3->elapsed;
343 length = state->id3->length;
345 else
347 elapsed = 0;
348 length = 0;
351 if (buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
352 return;
354 time = elapsed + state->ff_rewind_count;
355 if (length)
356 pos = 55 * time / length;
358 memset(timestr, 0, sizeof(timestr));
359 format_time(timestr, sizeof(timestr)-2, time);
360 timestr[strlen(timestr)] = ':'; /* always safe */
362 for (i = 0; i < 11; i++, pos -= 5)
364 softchar = false;
365 memset(progress_pattern, 0, sizeof(progress_pattern));
367 if ((digit = timestr[time_idx]))
369 softchar = true;
370 digit -= '0';
372 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
374 memcpy(progress_pattern, numbers[digit], 4);
375 time_idx += 2;
377 else /* tens, shifted right */
379 for (j = 0; j < 4; j++)
380 progress_pattern[j] = numbers[digit][j] >> 1;
382 if (time_idx > 0) /* not the first group, add colon in front */
384 progress_pattern[1] |= 0x10u;
385 progress_pattern[3] |= 0x10u;
387 time_idx++;
390 if (pos >= 5)
391 progress_pattern[5] = progress_pattern[6] = 0x1fu;
394 if (pos > 0 && pos < 5)
396 softchar = true;
397 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
400 if (softchar && pat_idx < 8)
402 display->define_pattern(data->wps_progress_pat[pat_idx],
403 progress_pattern);
404 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
405 pat_idx++;
407 else if (pos <= 0)
408 buf = utf8encode(' ', buf);
409 else
410 buf = utf8encode(0xe115, buf); /* 2/7 _ */
412 *buf = '\0';
415 #endif /* HAVE_LCD_CHARCELL */
417 /* Return the index to the end token for the conditional token at index.
418 The conditional token can be either a start token or a separator
419 (i.e. option) token.
421 static int find_conditional_end(struct wps_data *data, int index)
423 int ret = index;
424 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
425 ret = data->tokens[ret].value.i;
427 /* ret now is the index to the end token for the conditional. */
428 return ret;
431 /* Evaluate the conditional that is at *token_index and return whether a skip
432 has ocurred. *token_index is updated with the new position.
434 static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
436 if (!gwps)
437 return false;
439 struct wps_data *data = gwps->data;
441 int i, cond_end;
442 int cond_index = *token_index;
443 char result[128];
444 const char *value;
445 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
446 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
448 /* treat ?xx<true> constructs as if they had 2 options. */
449 if (num_options < 2)
450 num_options = 2;
452 int intval = num_options;
453 /* get_token_value needs to know the number of options in the enum */
454 value = get_token_value(gwps, &data->tokens[cond_index + 1],
455 result, sizeof(result), &intval);
457 /* intval is now the number of the enum option we want to read,
458 starting from 1. If intval is -1, we check if value is empty. */
459 if (intval == -1)
460 intval = (value && *value) ? 1 : num_options;
461 else if (intval > num_options || intval < 1)
462 intval = num_options;
464 data->tokens[cond_index].value.i = (intval << 8) + num_options;
466 /* skip to the appropriate enum case */
467 int next = cond_index + 2;
468 for (i = 1; i < intval; i++)
470 next = data->tokens[next].value.i;
472 *token_index = next;
474 if (prev_val == intval)
476 /* Same conditional case as previously. Return without clearing the
477 pictures */
478 return false;
481 cond_end = find_conditional_end(data, cond_index + 2);
482 for (i = cond_index + 3; i < cond_end; i++)
484 #ifdef HAVE_LCD_BITMAP
485 /* clear all pictures in the conditional and nested ones */
486 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
487 clear_image_pos(gwps, find_image(data->tokens[i].value.i&0xFF, gwps->data));
488 #endif
489 #ifdef HAVE_ALBUMART
490 if (data->albumart && data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
492 draw_album_art(gwps,
493 playback_current_aa_hid(data->playback_aa_slot), true);
494 data->albumart->draw = false;
496 #endif
499 return true;
502 #ifdef HAVE_LCD_BITMAP
503 struct gui_img* find_image(char label, struct wps_data *data)
505 struct skin_token_list *list = data->images;
506 while (list)
508 struct gui_img *img = (struct gui_img *)list->token->value.data;
509 if (img->label == label)
510 return img;
511 list = list->next;
513 return NULL;
515 #endif
517 struct skin_viewport* find_viewport(char label, struct wps_data *data)
519 struct skin_token_list *list = data->viewports;
520 while (list)
522 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
523 if (vp->label == label)
524 return vp;
525 list = list->next;
527 return NULL;
531 /* Read a (sub)line to the given alignment format buffer.
532 linebuf is the buffer where the data is actually stored.
533 align is the alignment format that'll be used to display the text.
534 The return value indicates whether the line needs to be updated.
536 static bool get_line(struct gui_wps *gwps,
537 struct skin_subline *subline,
538 struct align_pos *align,
539 char *linebuf,
540 int linebuf_size)
542 struct wps_data *data = gwps->data;
544 char temp_buf[128];
545 char *buf = linebuf; /* will always point to the writing position */
546 char *linebuf_end = linebuf + linebuf_size - 1;
547 bool update = false;
548 int i;
550 /* alignment-related variables */
551 int cur_align;
552 char* cur_align_start;
553 cur_align_start = buf;
554 cur_align = WPS_ALIGN_LEFT;
555 align->left = NULL;
556 align->center = NULL;
557 align->right = NULL;
558 /* Process all tokens of the desired subline */
559 for (i = subline->first_token_idx;
560 i <= subline->last_token_idx; i++)
562 switch(data->tokens[i].type)
564 case WPS_TOKEN_CONDITIONAL:
565 /* place ourselves in the right conditional case */
566 update |= evaluate_conditional(gwps, &i);
567 break;
569 case WPS_TOKEN_CONDITIONAL_OPTION:
570 /* we've finished in the curent conditional case,
571 skip to the end of the conditional structure */
572 i = find_conditional_end(data, i);
573 break;
575 #ifdef HAVE_LCD_BITMAP
576 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
578 char n = data->tokens[i].value.i & 0xFF;
579 int subimage = data->tokens[i].value.i >> 8;
580 struct gui_img *img = find_image(n, data);
582 if (img && img->loaded)
583 img->display = subimage;
584 break;
586 #endif
588 case WPS_TOKEN_ALIGN_LEFT:
589 case WPS_TOKEN_ALIGN_CENTER:
590 case WPS_TOKEN_ALIGN_RIGHT:
591 /* remember where the current aligned text started */
592 switch (cur_align)
594 case WPS_ALIGN_LEFT:
595 align->left = cur_align_start;
596 break;
598 case WPS_ALIGN_CENTER:
599 align->center = cur_align_start;
600 break;
602 case WPS_ALIGN_RIGHT:
603 align->right = cur_align_start;
604 break;
606 /* start a new alignment */
607 switch (data->tokens[i].type)
609 case WPS_TOKEN_ALIGN_LEFT:
610 cur_align = WPS_ALIGN_LEFT;
611 break;
612 case WPS_TOKEN_ALIGN_CENTER:
613 cur_align = WPS_ALIGN_CENTER;
614 break;
615 case WPS_TOKEN_ALIGN_RIGHT:
616 cur_align = WPS_ALIGN_RIGHT;
617 break;
618 default:
619 break;
621 *buf++ = 0;
622 cur_align_start = buf;
623 break;
624 case WPS_VIEWPORT_ENABLE:
626 char label = data->tokens[i].value.i;
627 char temp = VP_DRAW_HIDEABLE;
628 /* viewports are allowed to share id's so find and enable
629 * all of them */
630 struct skin_token_list *list = data->viewports;
631 while (list)
633 struct skin_viewport *vp =
634 (struct skin_viewport *)list->token->value.data;
635 if (vp->label == label)
637 if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
638 temp |= VP_DRAW_WASHIDDEN;
639 vp->hidden_flags = temp;
641 list = list->next;
644 break;
645 default:
647 /* get the value of the tag and copy it to the buffer */
648 const char *value = get_token_value(gwps, &data->tokens[i],
649 temp_buf, sizeof(temp_buf), NULL);
650 if (value)
652 update = true;
653 while (*value && (buf < linebuf_end))
654 *buf++ = *value++;
656 break;
661 /* close the current alignment */
662 switch (cur_align)
664 case WPS_ALIGN_LEFT:
665 align->left = cur_align_start;
666 break;
668 case WPS_ALIGN_CENTER:
669 align->center = cur_align_start;
670 break;
672 case WPS_ALIGN_RIGHT:
673 align->right = cur_align_start;
674 break;
677 return update;
679 static void get_subline_timeout(struct gui_wps *gwps, struct skin_subline *subline)
681 struct wps_data *data = gwps->data;
682 int i;
683 subline->time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
685 for (i = subline->first_token_idx;
686 i <= subline->last_token_idx; i++)
688 switch(data->tokens[i].type)
690 case WPS_TOKEN_CONDITIONAL:
691 /* place ourselves in the right conditional case */
692 evaluate_conditional(gwps, &i);
693 break;
695 case WPS_TOKEN_CONDITIONAL_OPTION:
696 /* we've finished in the curent conditional case,
697 skip to the end of the conditional structure */
698 i = find_conditional_end(data, i);
699 break;
701 case WPS_TOKEN_SUBLINE_TIMEOUT:
702 subline->time_mult = data->tokens[i].value.i;
703 break;
705 default:
706 break;
711 /* Calculates which subline should be displayed for the specified line
712 Returns true iff the subline must be refreshed */
713 static bool update_curr_subline(struct gui_wps *gwps, struct skin_line *line)
715 /* shortcut this whole thing if we need to reset the line completly */
716 if (line->curr_subline == NULL)
718 line->subline_expire_time = current_tick;
719 line->curr_subline = &line->sublines;
720 if (!line->curr_subline->next)
722 line->subline_expire_time += 100*HZ;
724 else
726 get_subline_timeout(gwps, line->curr_subline);
727 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
729 return true;
731 /* if time to advance to next sub-line */
732 if (TIME_AFTER(current_tick, line->subline_expire_time - 1))
734 /* if there is only one subline, there is no need to search for a new one */
735 if (&line->sublines == line->curr_subline &&
736 line->curr_subline->next == NULL)
738 line->subline_expire_time += 100 * HZ;
739 return false;
741 if (line->curr_subline->next)
742 line->curr_subline = line->curr_subline->next;
743 else
744 line->curr_subline = &line->sublines;
745 get_subline_timeout(gwps, line->curr_subline);
746 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
747 return true;
749 return false;
752 /* Display a line appropriately according to its alignment format.
753 format_align contains the text, separated between left, center and right.
754 line is the index of the line on the screen.
755 scroll indicates whether the line is a scrolling one or not.
757 static void write_line(struct screen *display,
758 struct align_pos *format_align,
759 int line,
760 bool scroll)
762 int left_width = 0, left_xpos;
763 int center_width = 0, center_xpos;
764 int right_width = 0, right_xpos;
765 int ypos;
766 int space_width;
767 int string_height;
768 int scroll_width;
770 /* calculate different string sizes and positions */
771 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
772 if (format_align->left != 0) {
773 display->getstringsize((unsigned char *)format_align->left,
774 &left_width, &string_height);
777 if (format_align->right != 0) {
778 display->getstringsize((unsigned char *)format_align->right,
779 &right_width, &string_height);
782 if (format_align->center != 0) {
783 display->getstringsize((unsigned char *)format_align->center,
784 &center_width, &string_height);
787 left_xpos = 0;
788 right_xpos = (display->getwidth() - right_width);
789 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
791 scroll_width = display->getwidth() - left_xpos;
793 /* Checks for overlapping strings.
794 If needed the overlapping strings will be merged, separated by a
795 space */
797 /* CASE 1: left and centered string overlap */
798 /* there is a left string, need to merge left and center */
799 if ((left_width != 0 && center_width != 0) &&
800 (left_xpos + left_width + space_width > center_xpos)) {
801 /* replace the former separator '\0' of left and
802 center string with a space */
803 *(--format_align->center) = ' ';
804 /* calculate the new width and position of the merged string */
805 left_width = left_width + space_width + center_width;
806 /* there is no centered string anymore */
807 center_width = 0;
809 /* there is no left string, move center to left */
810 if ((left_width == 0 && center_width != 0) &&
811 (left_xpos + left_width > center_xpos)) {
812 /* move the center string to the left string */
813 format_align->left = format_align->center;
814 /* calculate the new width and position of the string */
815 left_width = center_width;
816 /* there is no centered string anymore */
817 center_width = 0;
820 /* CASE 2: centered and right string overlap */
821 /* there is a right string, need to merge center and right */
822 if ((center_width != 0 && right_width != 0) &&
823 (center_xpos + center_width + space_width > right_xpos)) {
824 /* replace the former separator '\0' of center and
825 right string with a space */
826 *(--format_align->right) = ' ';
827 /* move the center string to the right after merge */
828 format_align->right = format_align->center;
829 /* calculate the new width and position of the merged string */
830 right_width = center_width + space_width + right_width;
831 right_xpos = (display->getwidth() - right_width);
832 /* there is no centered string anymore */
833 center_width = 0;
835 /* there is no right string, move center to right */
836 if ((center_width != 0 && right_width == 0) &&
837 (center_xpos + center_width > right_xpos)) {
838 /* move the center string to the right string */
839 format_align->right = format_align->center;
840 /* calculate the new width and position of the string */
841 right_width = center_width;
842 right_xpos = (display->getwidth() - right_width);
843 /* there is no centered string anymore */
844 center_width = 0;
847 /* CASE 3: left and right overlap
848 There is no center string anymore, either there never
849 was one or it has been merged in case 1 or 2 */
850 /* there is a left string, need to merge left and right */
851 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
852 (left_xpos + left_width + space_width > right_xpos)) {
853 /* replace the former separator '\0' of left and
854 right string with a space */
855 *(--format_align->right) = ' ';
856 /* calculate the new width and position of the string */
857 left_width = left_width + space_width + right_width;
858 /* there is no right string anymore */
859 right_width = 0;
861 /* there is no left string, move right to left */
862 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
863 (left_width > right_xpos)) {
864 /* move the right string to the left string */
865 format_align->left = format_align->right;
866 /* calculate the new width and position of the string */
867 left_width = right_width;
868 /* there is no right string anymore */
869 right_width = 0;
872 ypos = (line * string_height);
875 if (scroll && ((left_width > scroll_width) ||
876 (center_width > scroll_width) ||
877 (right_width > scroll_width)))
879 display->puts_scroll(0, line,
880 (unsigned char *)format_align->left);
882 else
884 #ifdef HAVE_LCD_BITMAP
885 /* clear the line first */
886 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
887 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
888 display->set_drawmode(DRMODE_SOLID);
889 #endif
891 /* Nasty hack: we output an empty scrolling string,
892 which will reset the scroller for that line */
893 display->puts_scroll(0, line, (unsigned char *)"");
895 /* print aligned strings */
896 if (left_width != 0)
898 display->putsxy(left_xpos, ypos,
899 (unsigned char *)format_align->left);
901 if (center_width != 0)
903 display->putsxy(center_xpos, ypos,
904 (unsigned char *)format_align->center);
906 if (right_width != 0)
908 display->putsxy(right_xpos, ypos,
909 (unsigned char *)format_align->right);
914 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
916 struct wps_data *data = gwps->data;
917 struct screen *display = gwps->display;
919 if (!data || !display || !gwps->state)
920 return false;
922 unsigned flags;
923 char linebuf[MAX_PATH];
925 struct align_pos align;
926 align.left = NULL;
927 align.center = NULL;
928 align.right = NULL;
931 struct skin_token_list *viewport_list;
933 bool update_line, new_subline_refresh;
935 #ifdef HAVE_LCD_BITMAP
937 /* to find out wether the peak meter is enabled we
938 assume it wasn't until we find a line that contains
939 the peak meter. We can't use peak_meter_enabled itself
940 because that would mean to turn off the meter thread
941 temporarily. (That shouldn't matter unless yield
942 or sleep is called but who knows...)
944 bool enable_pm = false;
946 #endif
948 /* reset to first subline if refresh all flag is set */
949 if (refresh_mode == WPS_REFRESH_ALL)
951 struct skin_line *line;
952 struct skin_viewport *skin_viewport = find_viewport(VP_DEFAULT_LABEL, data);
954 if (!(skin_viewport->hidden_flags & VP_NEVER_VISIBLE))
956 display->set_viewport(&skin_viewport->vp);
957 display->clear_viewport();
960 for (viewport_list = data->viewports;
961 viewport_list; viewport_list = viewport_list->next)
963 skin_viewport =
964 (struct skin_viewport *)viewport_list->token->value.data;
965 for(line = skin_viewport->lines; line; line = line->next)
967 line->curr_subline = NULL;
972 #ifdef HAVE_LCD_CHARCELLS
973 int i;
974 for (i = 0; i < 8; i++)
976 if (data->wps_progress_pat[i] == 0)
977 data->wps_progress_pat[i] = display->get_locked_pattern();
979 #endif
981 /* disable any viewports which are conditionally displayed */
982 for (viewport_list = data->viewports;
983 viewport_list; viewport_list = viewport_list->next)
985 struct skin_viewport *skin_viewport =
986 (struct skin_viewport *)viewport_list->token->value.data;
987 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
989 continue;
991 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
993 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
994 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
995 else
996 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
999 int viewport_count = 0;
1000 for (viewport_list = data->viewports;
1001 viewport_list; viewport_list = viewport_list->next, viewport_count++)
1003 struct skin_viewport *skin_viewport =
1004 (struct skin_viewport *)viewport_list->token->value.data;
1005 unsigned vp_refresh_mode = refresh_mode;
1007 display->set_viewport(&skin_viewport->vp);
1009 int hidden_vp = 0;
1011 #ifdef HAVE_LCD_BITMAP
1012 /* Set images to not to be displayed */
1013 struct skin_token_list *imglist = data->images;
1014 while (imglist)
1016 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
1017 img->display = -1;
1018 imglist = imglist->next;
1020 #endif
1021 /* dont redraw the viewport if its disabled */
1022 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1023 { /* don't draw anything into this one */
1024 vp_refresh_mode = 0; hidden_vp = true;
1026 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
1028 if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
1029 display->scroll_stop(&skin_viewport->vp);
1030 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1031 continue;
1033 else if (((skin_viewport->hidden_flags&
1034 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
1035 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
1037 vp_refresh_mode = WPS_REFRESH_ALL;
1038 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
1041 if (vp_refresh_mode == WPS_REFRESH_ALL)
1043 display->clear_viewport();
1046 /* loop over the lines for this viewport */
1047 struct skin_line *line;
1048 int line_count = 0;
1050 for (line = skin_viewport->lines; line; line = line->next, line_count++)
1052 struct skin_subline *subline;
1053 memset(linebuf, 0, sizeof(linebuf));
1054 update_line = false;
1056 /* get current subline for the line */
1057 new_subline_refresh = update_curr_subline(gwps, line);
1058 subline = line->curr_subline;
1059 flags = line->curr_subline->line_type;
1061 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1062 || new_subline_refresh || hidden_vp)
1064 /* get_line tells us if we need to update the line */
1065 update_line = get_line(gwps, subline,
1066 &align, linebuf, sizeof(linebuf));
1068 #ifdef HAVE_LCD_BITMAP
1069 /* peakmeter */
1070 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1072 /* the peakmeter should be alone on its line */
1073 update_line = false;
1075 int h = font_get(skin_viewport->vp.font)->height;
1076 int peak_meter_y = line_count* h;
1078 /* The user might decide to have the peak meter in the last
1079 line so that it is only displayed if no status bar is
1080 visible. If so we neither want do draw nor enable the
1081 peak meter. */
1082 if (peak_meter_y + h <= skin_viewport->vp.y+skin_viewport->vp.height) {
1083 /* found a line with a peak meter -> remember that we must
1084 enable it later */
1085 enable_pm = true;
1086 peak_meter_enabled = true;
1087 peak_meter_screen(gwps->display, 0, peak_meter_y,
1088 MIN(h, skin_viewport->vp.y+skin_viewport->vp.height - peak_meter_y));
1090 else
1092 peak_meter_enabled = false;
1096 #else /* HAVE_LCD_CHARCELL */
1098 /* progressbar */
1099 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1101 if (data->full_line_progressbar)
1102 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1103 else
1104 draw_player_progress(gwps);
1106 #endif
1108 if (update_line && !hidden_vp &&
1109 /* conditionals clear the line which means if the %Vd is put into the default
1110 viewport there will be a blank line.
1111 To get around this we dont allow any actual drawing to happen in the
1112 deault vp if other vp's are defined */
1113 ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
1114 !viewport_list->next))
1116 if (flags & WPS_REFRESH_SCROLL)
1118 /* if the line is a scrolling one we don't want to update
1119 too often, so that it has the time to scroll */
1120 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1121 write_line(display, &align, line_count, true);
1123 else
1124 write_line(display, &align, line_count, false);
1127 #ifdef HAVE_LCD_BITMAP
1128 /* progressbar */
1129 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1131 if (skin_viewport->pb)
1133 draw_progressbar(gwps, skin_viewport);
1136 /* Now display any images in this viewport */
1137 if (!hidden_vp)
1138 wps_display_images(gwps, &skin_viewport->vp);
1139 #endif
1142 #ifdef HAVE_LCD_BITMAP
1143 data->peak_meter_enabled = enable_pm;
1144 #endif
1146 if (refresh_mode & WPS_REFRESH_STATUSBAR)
1148 viewportmanager_set_statusbar(gwps->sync_data->statusbars);
1150 /* Restore the default viewport */
1151 display->set_viewport(NULL);
1153 display->update();
1155 return true;