1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
28 #include "settings_list.h"
29 #include "rbunicode.h"
34 #include "powermgmt.h"
37 #ifdef HAVE_LCD_CHARCELLS
41 #include "mp3_playback.h"
45 #include "scrollbar.h"
48 #ifdef HAVE_LCD_BITMAP
49 #include "peakmeter.h"
58 #if CONFIG_CODEC == SWCODEC
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 */
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();
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
)
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
);
110 #ifdef HAVE_LCD_BITMAP
112 void skin_statusbar_changed(struct gui_wps
*skin
)
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
)
127 statusbar_position(display
->screen_type
) != STATUSBAR_BOTTOM
;
129 vp
->y
= bar_at_top
?STATUSBAR_HEIGHT
:0;
130 vp
->height
= display
->lcdheight
- STATUSBAR_HEIGHT
;
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
;
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
);
163 elapsed
= id3
->elapsed
;
164 length
= id3
->length
;
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,
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,
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
);
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
)
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
);
210 display
->set_drawmode(DRMODE_SOLID
);
213 if(img
->bm
.format
== FORMAT_MONO
) {
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
);
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
);
232 static void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
234 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
237 struct wps_data
*data
= gwps
->data
;
238 struct screen
*display
= gwps
->display
;
239 struct skin_token_list
*list
= data
->images
;
243 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
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);
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
),
264 data
->albumart
->draw
= false;
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];
282 if (LIKELY(state
->id3
))
284 elapsed
= state
->id3
->elapsed
;
285 length
= state
->id3
->length
;
294 pos
= 36 * (elapsed
+ state
->ff_rewind_count
) / length
;
296 for (i
= 0; i
< 7; i
++, pos
-= 5)
299 progress_pattern
[i
] = 0x1fu
;
301 progress_pattern
[i
] = 0x00u
;
303 progress_pattern
[i
] = 0x1fu
>> pos
;
306 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
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];
338 if (LIKELY(state
->id3
))
340 elapsed
= id3
->elapsed
;
341 length
= id3
->length
;
349 if (buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
352 time
= elapsed
+ state
->ff_rewind_count
;
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)
363 memset(progress_pattern
, 0, sizeof(progress_pattern
));
365 if ((digit
= timestr
[time_idx
]))
370 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
372 memcpy(progress_pattern
, numbers
[digit
], 4);
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
;
389 progress_pattern
[5] = progress_pattern
[6] = 0x1fu
;
392 if (pos
> 0 && pos
< 5)
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
],
402 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
406 buf
= utf8encode(' ', buf
);
408 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
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
419 static int find_conditional_end(struct wps_data
*data
, int 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. */
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
)
437 struct wps_data
*data
= gwps
->data
;
440 int cond_index
= *token_index
;
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. */
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. */
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
;
472 if (prev_val
== intval
)
474 /* Same conditional case as previously. Return without clearing the
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,
489 clear_image_pos(gwps
, tmp
);
493 if (data
->albumart
&& data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
496 playback_current_aa_hid(data
->playback_aa_slot
), true);
497 data
->albumart
->draw
= false;
504 #ifdef HAVE_LCD_BITMAP
505 struct gui_img
* find_image(char label
, struct wps_data
*data
)
508 struct gui_img
*ret
= NULL
;
509 struct skin_token_list
*list
= data
->images
;
512 DEBUGF("%s >> requesting image (id: %d)\n", __func__
, n
);
513 DEBUGF("%s >> first list data (p: %p\n", __func__
, data
->images
);
517 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
518 if (img
->label
== label
)
525 if (!list && data->debug)
526 DEBUGF("failed to find: %s\n", img->bm.data);
532 DEBUGF("%s >> returning %p\n", __func__
, ret
);
537 struct skin_viewport
* find_viewport(char label
, struct wps_data
*data
)
539 struct skin_token_list
*list
= data
->viewports
;
542 struct skin_viewport
*vp
= (struct skin_viewport
*)list
->token
->value
.data
;
543 if (vp
->label
== label
)
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
,
561 struct wps_data
*data
= gwps
->data
;
564 char *buf
= linebuf
; /* will always point to the writing position */
565 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
569 /* alignment-related variables */
571 char* cur_align_start
;
572 cur_align_start
= buf
;
573 cur_align
= WPS_ALIGN_LEFT
;
575 align
->center
= 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
);
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
);
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
;
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 */
614 align
->left
= cur_align_start
;
617 case WPS_ALIGN_CENTER
:
618 align
->center
= cur_align_start
;
621 case WPS_ALIGN_RIGHT
:
622 align
->right
= cur_align_start
;
625 /* start a new alignment */
626 switch (data
->tokens
[i
].type
)
628 case WPS_TOKEN_ALIGN_LEFT
:
629 cur_align
= WPS_ALIGN_LEFT
;
631 case WPS_TOKEN_ALIGN_CENTER
:
632 cur_align
= WPS_ALIGN_CENTER
;
634 case WPS_TOKEN_ALIGN_RIGHT
:
635 cur_align
= WPS_ALIGN_RIGHT
;
641 cur_align_start
= buf
;
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
649 struct skin_token_list
*list
= data
->viewports
;
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
;
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
);
672 while (*value
&& (buf
< linebuf_end
))
680 /* close the current alignment */
684 align
->left
= cur_align_start
;
687 case WPS_ALIGN_CENTER
:
688 align
->center
= cur_align_start
;
691 case WPS_ALIGN_RIGHT
:
692 align
->right
= cur_align_start
;
698 static void get_subline_timeout(struct gui_wps
*gwps
, struct skin_subline
*subline
)
700 struct wps_data
*data
= gwps
->data
;
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
);
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
);
720 case WPS_TOKEN_SUBLINE_TIMEOUT
:
721 subline
->time_mult
= data
->tokens
[i
].value
.i
;
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
;
745 get_subline_timeout(gwps
, line
->curr_subline
);
746 line
->subline_expire_time
+= TIMEOUT_UNIT
*line
->curr_subline
->time_mult
;
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
;
760 if (line
->curr_subline
->next
)
761 line
->curr_subline
= line
->curr_subline
->next
;
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
;
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
,
781 int left_width
= 0, left_xpos
;
782 int center_width
= 0, center_xpos
;
783 int right_width
= 0, right_xpos
;
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 ¢er_width
, &string_height
);
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
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 */
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 */
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 */
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 */
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 */
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 */
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
);
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
);
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 */
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
)
942 char linebuf
[MAX_PATH
];
944 struct align_pos align
;
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;
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
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();
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
;
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
;
1024 struct gui_img
*img
= (struct gui_img
*)imglist
->token
->value
.data
;
1026 imglist
= imglist
->next
;
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
;
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
;
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
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
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
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
));
1095 peak_meter_enabled
= false;
1099 #else /* HAVE_LCD_CHARCELL */
1102 if (flags
& vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1104 if (data
->full_line_progressbar
)
1105 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
1107 draw_player_progress(gwps
);
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);
1127 write_line(display
, &align
, line_count
, false);
1131 #ifdef HAVE_LCD_BITMAP
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
);
1145 #ifdef HAVE_LCD_BITMAP
1146 data
->peak_meter_enabled
= enable_pm
;
1149 if (refresh_mode
& WPS_REFRESH_STATUSBAR
)
1151 viewportmanager_set_statusbar(*gwps
->statusbars
);
1153 /* Restore the default viewport */
1154 display
->set_viewport(NULL
);