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