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 "rbunicode.h"
36 #include "statusbar.h"
38 #include "scrollbar.h"
39 #include "screen_access.h"
43 #ifdef HAVE_LCD_BITMAP
44 #include "peakmeter.h"
53 #if CONFIG_CODEC == SWCODEC
60 #include "wps_internals.h"
61 #include "skin_engine.h"
62 #include "statusbar-skinned.h"
64 static bool skin_redraw(struct gui_wps
*gwps
, unsigned refresh_mode
);
66 /* update a skinned screen, update_type is WPS_REFRESH_* values.
67 * Usually it should only be WPS_REFRESH_NON_STATIC
68 * A full update will be done if required (state.do_full_update == true)
70 bool skin_update(struct gui_wps
*gwps
, unsigned int update_type
)
73 /* This maybe shouldnt be here, but while the skin is only used to
74 * display the music screen this is better than whereever we are being
75 * called from. This is also safe for skined screen which dont use the id3 */
76 struct mp3entry
*id3
= gwps
->state
->id3
;
77 bool cuesheet_update
= (id3
!= NULL
? cuesheet_subtrack_changed(id3
) : false);
78 gwps
->sync_data
->do_full_update
|= cuesheet_update
;
80 retval
= skin_redraw(gwps
, gwps
->sync_data
->do_full_update
?
81 WPS_REFRESH_ALL
: update_type
);
85 #ifdef HAVE_LCD_BITMAP
87 void skin_statusbar_changed(struct gui_wps
*skin
)
91 struct wps_data
*data
= skin
->data
;
92 const struct screen
*display
= skin
->display
;
93 const int screen
= display
->screen_type
;
95 struct viewport
*vp
= &find_viewport(VP_DEFAULT_LABEL
, data
)->vp
;
96 viewport_set_defaults(vp
, screen
);
99 { /* fix up the default viewport */
100 if (data
->show_sb_on_wps
)
102 if (statusbar_position(screen
) != STATUSBAR_OFF
)
103 return; /* vp is fixed already */
105 vp
->y
= STATUSBAR_HEIGHT
;
106 vp
->height
= display
->lcdheight
- STATUSBAR_HEIGHT
;
110 if (statusbar_position(screen
) == STATUSBAR_OFF
)
111 return; /* vp is fixed already */
113 vp
->height
= display
->lcdheight
;
114 vp
->width
= display
->lcdwidth
;
119 static void draw_progressbar(struct gui_wps
*gwps
,
120 struct progressbar
*pb
)
122 struct screen
*display
= gwps
->display
;
123 struct viewport
*vp
= pb
->vp
;
124 struct wps_state
*state
= gwps
->state
;
125 struct mp3entry
*id3
= state
->id3
;
126 int y
= pb
->y
, height
= pb
->height
;
127 unsigned long length
, elapsed
;
130 height
= font_get(vp
->font
)->height
;
134 int line_height
= font_get(vp
->font
)->height
;
135 /* center the pb in the line, but only if the line is higher than the pb */
136 int center
= (line_height
-height
)/2;
137 /* if Y was not set calculate by font height,Y is -line_number-1 */
138 y
= (-y
-1)*line_height
+ (0 > center
? 0 : center
);
141 if (pb
->type
== WPS_TOKEN_VOLUMEBAR
)
143 int minvol
= sound_min(SOUND_VOLUME
);
144 int maxvol
= sound_max(SOUND_VOLUME
);
145 length
= maxvol
-minvol
;
146 elapsed
= global_settings
.volume
-minvol
;
148 else if (id3
&& id3
->length
)
150 length
= id3
->length
;
151 elapsed
= id3
->elapsed
+ state
->ff_rewind_count
;
159 if (pb
->have_bitmap_pb
)
160 gui_bitmap_scrollbar_draw(display
, pb
->bm
,
161 pb
->x
, y
, pb
->width
, pb
->bm
.height
,
162 length
, 0, elapsed
, HORIZONTAL
);
164 gui_scrollbar_draw(display
, pb
->x
, y
, pb
->width
, height
,
165 length
, 0, elapsed
, HORIZONTAL
);
167 if (pb
->type
== WPS_TOKEN_PROGRESSBAR
&& id3
&& id3
->length
)
169 #ifdef AB_REPEAT_ENABLE
170 if (ab_repeat_mode_enabled())
171 ab_draw_markers(display
, id3
->length
,
172 pb
->x
, y
, pb
->width
, height
);
176 cue_draw_markers(display
, id3
->cuesheet
, id3
->length
,
177 pb
->x
, y
+1, pb
->width
, height
-2);
181 static void draw_playlist_viewer_list(struct gui_wps
*gwps
,
182 struct playlistviewer
*viewer
)
184 struct wps_state
*state
= gwps
->state
;
185 int lines
= viewport_get_nb_lines(viewer
->vp
);
186 int line_height
= font_get(viewer
->vp
->font
)->height
;
187 int cur_playlist_pos
= playlist_get_display_index();
188 int start_item
= MAX(0, cur_playlist_pos
+ viewer
->start_offset
);
190 struct wps_token token
;
191 int x
, length
, alignment
= WPS_TOKEN_ALIGN_LEFT
;
193 struct mp3entry
*pid3
;
194 char buf
[MAX_PATH
*2], tempbuf
[MAX_PATH
];
196 gwps
->display
->set_viewport(viewer
->vp
);
197 for(i
=start_item
; (i
-start_item
)<lines
&& i
<=playlist_amount(); i
++)
199 if (i
== cur_playlist_pos
)
203 else if (i
== cur_playlist_pos
+1)
207 #if CONFIG_CODEC == SWCODEC
208 else if (i
>cur_playlist_pos
)
210 if (!audio_peek_track(&pid3
, i
-cur_playlist_pos
))
219 int line
= pid3
? TRACK_HAS_INFO
: TRACK_HAS_NO_INFO
;
220 int j
= 0, cur_string
= 0;
221 char *filename
= playlist_peek(i
-cur_playlist_pos
);
222 unsigned int line_len
= 0;
224 while (j
< viewer
->lines
[line
].count
&& line_len
< sizeof(buf
))
226 const char *out
= NULL
;
227 token
.type
= viewer
->lines
[line
].tokens
[j
];
230 out
= get_id3_token(&token
, pid3
, tempbuf
, sizeof(tempbuf
), -1, NULL
);
233 line_len
= strlcat(buf
, out
, sizeof(buf
));
237 switch (viewer
->lines
[line
].tokens
[j
])
239 case WPS_TOKEN_ALIGN_CENTER
:
240 case WPS_TOKEN_ALIGN_LEFT
:
241 case WPS_TOKEN_ALIGN_LEFT_RTL
:
242 case WPS_TOKEN_ALIGN_RIGHT
:
243 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
244 alignment
= viewer
->lines
[line
].tokens
[j
];
247 case WPS_TOKEN_STRING
:
248 case WPS_TOKEN_CHARACTER
:
249 snprintf(tempbuf
, sizeof(tempbuf
), "%s",
250 viewer
->lines
[line
].strings
[cur_string
]);
253 case WPS_TOKEN_PLAYLIST_POSITION
:
254 snprintf(tempbuf
, sizeof(tempbuf
), "%d", i
);
256 case WPS_TOKEN_FILE_NAME
:
257 get_dir(tempbuf
, sizeof(tempbuf
), filename
, 0);
259 case WPS_TOKEN_FILE_PATH
:
260 snprintf(tempbuf
, sizeof(tempbuf
), "%s", filename
);
268 line_len
= strlcat(buf
, tempbuf
, sizeof(buf
));
273 int vpwidth
= viewer
->vp
->width
;
274 length
= gwps
->display
->getstringsize(buf
, NULL
, NULL
);
275 if (viewer
->lines
[line
].scroll
&& length
>= vpwidth
)
277 gwps
->display
->puts_scroll(0, (i
-start_item
), buf
);
281 if (length
>= vpwidth
)
287 case WPS_TOKEN_ALIGN_CENTER
:
288 x
= (vpwidth
-length
)/2;
290 case WPS_TOKEN_ALIGN_LEFT_RTL
:
291 if (lang_is_rtl() && VP_IS_RTL(viewer
->vp
))
293 x
= vpwidth
- length
;
296 case WPS_TOKEN_ALIGN_LEFT
:
299 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
300 if (lang_is_rtl() && VP_IS_RTL(viewer
->vp
))
305 case WPS_TOKEN_ALIGN_RIGHT
:
306 x
= vpwidth
- length
;
313 gwps
->display
->putsxy(x
, (i
-start_item
)*line_height
, buf
);
319 /* clears the area where the image was shown */
320 static void clear_image_pos(struct gui_wps
*gwps
, struct gui_img
*img
)
324 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
325 gwps
->display
->fillrect(img
->x
, img
->y
, img
->bm
.width
, img
->subimage_height
);
326 gwps
->display
->set_drawmode(DRMODE_SOLID
);
329 static void wps_draw_image(struct gui_wps
*gwps
, struct gui_img
*img
, int subimage
)
331 struct screen
*display
= gwps
->display
;
332 if(img
->always_display
)
333 display
->set_drawmode(DRMODE_FG
);
335 display
->set_drawmode(DRMODE_SOLID
);
338 if(img
->bm
.format
== FORMAT_MONO
) {
340 display
->mono_bitmap_part(img
->bm
.data
,
341 0, img
->subimage_height
* subimage
,
342 img
->bm
.width
, img
->x
,
343 img
->y
, img
->bm
.width
,
344 img
->subimage_height
);
347 display
->transparent_bitmap_part((fb_data
*)img
->bm
.data
,
348 0, img
->subimage_height
* subimage
,
349 STRIDE(display
->screen_type
,
350 img
->bm
.width
, img
->bm
.height
),
351 img
->x
, img
->y
, img
->bm
.width
,
352 img
->subimage_height
);
357 static void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
359 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
362 struct wps_data
*data
= gwps
->data
;
363 struct screen
*display
= gwps
->display
;
364 struct skin_token_list
*list
= data
->images
;
368 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
371 if (img
->display
>= 0)
373 wps_draw_image(gwps
, img
, img
->display
);
375 else if (img
->always_display
&& img
->vp
== vp
)
377 wps_draw_image(gwps
, img
, 0);
383 /* now draw the AA */
384 if (data
->albumart
&& data
->albumart
->vp
== vp
385 && data
->albumart
->draw
)
387 draw_album_art(gwps
, playback_current_aa_hid(data
->playback_aa_slot
),
389 data
->albumart
->draw
= false;
393 display
->set_drawmode(DRMODE_SOLID
);
396 #else /* HAVE_LCD_CHARCELL */
398 static bool draw_player_progress(struct gui_wps
*gwps
)
400 struct wps_state
*state
= gwps
->state
;
401 struct screen
*display
= gwps
->display
;
402 unsigned char progress_pattern
[7];
407 if (LIKELY(state
->id3
))
409 elapsed
= state
->id3
->elapsed
;
410 length
= state
->id3
->length
;
419 pos
= 36 * (elapsed
+ state
->ff_rewind_count
) / length
;
421 for (i
= 0; i
< 7; i
++, pos
-= 5)
424 progress_pattern
[i
] = 0x1fu
;
426 progress_pattern
[i
] = 0x00u
;
428 progress_pattern
[i
] = 0x1fu
>> pos
;
431 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
435 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
437 static const unsigned char numbers
[10][4] = {
438 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
439 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
440 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
441 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
442 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
443 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
444 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
445 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
446 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
447 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
450 struct wps_state
*state
= gwps
->state
;
451 struct screen
*display
= gwps
->display
;
452 struct wps_data
*data
= gwps
->data
;
453 unsigned char progress_pattern
[7];
463 if (LIKELY(state
->id3
))
465 elapsed
= state
->id3
->elapsed
;
466 length
= state
->id3
->length
;
474 if (buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
477 time
= elapsed
+ state
->ff_rewind_count
;
479 pos
= 55 * time
/ length
;
481 memset(timestr
, 0, sizeof(timestr
));
482 format_time(timestr
, sizeof(timestr
)-2, time
);
483 timestr
[strlen(timestr
)] = ':'; /* always safe */
485 for (i
= 0; i
< 11; i
++, pos
-= 5)
488 memset(progress_pattern
, 0, sizeof(progress_pattern
));
490 if ((digit
= timestr
[time_idx
]))
495 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
497 memcpy(progress_pattern
, numbers
[digit
], 4);
500 else /* tens, shifted right */
502 for (j
= 0; j
< 4; j
++)
503 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
505 if (time_idx
> 0) /* not the first group, add colon in front */
507 progress_pattern
[1] |= 0x10u
;
508 progress_pattern
[3] |= 0x10u
;
514 progress_pattern
[5] = progress_pattern
[6] = 0x1fu
;
517 if (pos
> 0 && pos
< 5)
520 progress_pattern
[5] = progress_pattern
[6] = (~0x1fu
>> pos
) & 0x1fu
;
523 if (softchar
&& pat_idx
< 8)
525 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
527 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
531 buf
= utf8encode(' ', buf
);
533 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
538 #endif /* HAVE_LCD_CHARCELL */
540 /* Return the index to the end token for the conditional token at index.
541 The conditional token can be either a start token or a separator
544 static int find_conditional_end(struct wps_data
*data
, int index
)
547 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
548 ret
= data
->tokens
[ret
].value
.i
;
550 /* ret now is the index to the end token for the conditional. */
554 /* Evaluate the conditional that is at *token_index and return whether a skip
555 has ocurred. *token_index is updated with the new position.
557 static bool evaluate_conditional(struct gui_wps
*gwps
, int *token_index
)
562 struct wps_data
*data
= gwps
->data
;
565 int cond_index
= *token_index
;
568 unsigned char num_options
= data
->tokens
[cond_index
].value
.i
& 0xFF;
569 unsigned char prev_val
= (data
->tokens
[cond_index
].value
.i
& 0xFF00) >> 8;
571 /* treat ?xx<true> constructs as if they had 2 options. */
575 int intval
= num_options
;
576 /* get_token_value needs to know the number of options in the enum */
577 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
578 result
, sizeof(result
), &intval
);
580 /* intval is now the number of the enum option we want to read,
581 starting from 1. If intval is -1, we check if value is empty. */
583 intval
= (value
&& *value
) ? 1 : num_options
;
584 else if (intval
> num_options
|| intval
< 1)
585 intval
= num_options
;
587 data
->tokens
[cond_index
].value
.i
= (intval
<< 8) + num_options
;
589 /* skip to the appropriate enum case */
590 int next
= cond_index
+ 2;
591 for (i
= 1; i
< intval
; i
++)
593 next
= data
->tokens
[next
].value
.i
;
597 if (prev_val
== intval
)
599 /* Same conditional case as previously. Return without clearing the
604 cond_end
= find_conditional_end(data
, cond_index
+ 2);
605 for (i
= cond_index
+ 3; i
< cond_end
; i
++)
607 #ifdef HAVE_LCD_BITMAP
608 /* clear all pictures in the conditional and nested ones */
609 if (data
->tokens
[i
].type
== WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
)
610 clear_image_pos(gwps
, find_image(data
->tokens
[i
].value
.i
&0xFF, data
));
613 if (data
->albumart
&& data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
616 playback_current_aa_hid(data
->playback_aa_slot
), true);
617 data
->albumart
->draw
= false;
626 /* Read a (sub)line to the given alignment format buffer.
627 linebuf is the buffer where the data is actually stored.
628 align is the alignment format that'll be used to display the text.
629 The return value indicates whether the line needs to be updated.
631 static bool get_line(struct gui_wps
*gwps
,
632 struct skin_subline
*subline
,
633 struct align_pos
*align
,
636 unsigned refresh_mode
)
638 struct wps_data
*data
= gwps
->data
;
641 char *buf
= linebuf
; /* will always point to the writing position */
642 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
645 (void)refresh_mode
; /* silence warning on charcell */
647 /* alignment-related variables */
649 char* cur_align_start
;
650 cur_align_start
= buf
;
651 cur_align
= WPS_ALIGN_LEFT
;
653 align
->center
= NULL
;
655 /* Process all tokens of the desired subline */
656 for (i
= subline
->first_token_idx
;
657 i
<= subline
->last_token_idx
; i
++)
659 switch(data
->tokens
[i
].type
)
661 case WPS_TOKEN_CONDITIONAL
:
662 /* place ourselves in the right conditional case */
663 update
|= evaluate_conditional(gwps
, &i
);
666 case WPS_TOKEN_CONDITIONAL_OPTION
:
667 /* we've finished in the curent conditional case,
668 skip to the end of the conditional structure */
669 i
= find_conditional_end(data
, i
);
672 #ifdef HAVE_LCD_BITMAP
673 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
675 char n
= data
->tokens
[i
].value
.i
& 0xFF;
676 int subimage
= data
->tokens
[i
].value
.i
>> 8;
677 struct gui_img
*img
= find_image(n
, data
);
679 if (img
&& img
->loaded
)
680 img
->display
= subimage
;
683 case WPS_TOKEN_DRAW_INBUILTBAR
:
684 gui_statusbar_draw(&(statusbars
.statusbars
[gwps
->display
->screen_type
]),
685 refresh_mode
== WPS_REFRESH_ALL
,
686 data
->tokens
[i
].value
.data
);
690 case WPS_TOKEN_ALIGN_LEFT
:
691 case WPS_TOKEN_ALIGN_LEFT_RTL
:
692 case WPS_TOKEN_ALIGN_CENTER
:
693 case WPS_TOKEN_ALIGN_RIGHT
:
694 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
695 /* remember where the current aligned text started */
699 align
->left
= cur_align_start
;
702 case WPS_ALIGN_CENTER
:
703 align
->center
= cur_align_start
;
706 case WPS_ALIGN_RIGHT
:
707 align
->right
= cur_align_start
;
710 /* start a new alignment */
711 switch (data
->tokens
[i
].type
)
713 case WPS_TOKEN_ALIGN_LEFT
:
714 cur_align
= WPS_ALIGN_LEFT
;
716 case WPS_TOKEN_ALIGN_LEFT_RTL
:
717 cur_align
= lang_is_rtl() ? WPS_ALIGN_RIGHT
:
720 case WPS_TOKEN_ALIGN_CENTER
:
721 cur_align
= WPS_ALIGN_CENTER
;
723 case WPS_TOKEN_ALIGN_RIGHT
:
724 cur_align
= WPS_ALIGN_RIGHT
;
726 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
727 cur_align
= lang_is_rtl() ? WPS_ALIGN_LEFT
:
734 cur_align_start
= buf
;
736 case WPS_VIEWPORT_ENABLE
:
738 char label
= data
->tokens
[i
].value
.i
;
739 char temp
= VP_DRAW_HIDEABLE
;
740 /* viewports are allowed to share id's so find and enable
742 struct skin_token_list
*list
= data
->viewports
;
745 struct skin_viewport
*vp
=
746 (struct skin_viewport
*)list
->token
->value
.data
;
747 if (vp
->label
== label
)
749 if (vp
->hidden_flags
&VP_DRAW_WASHIDDEN
)
750 temp
|= VP_DRAW_WASHIDDEN
;
751 vp
->hidden_flags
= temp
;
757 #ifdef HAVE_LCD_BITMAP
758 case WPS_VIEWPORT_CUSTOMLIST
:
759 draw_playlist_viewer_list(gwps
, data
->tokens
[i
].value
.data
);
764 /* get the value of the tag and copy it to the buffer */
765 const char *value
= get_token_value(gwps
, &data
->tokens
[i
],
766 temp_buf
, sizeof(temp_buf
), NULL
);
770 while (*value
&& (buf
< linebuf_end
))
778 /* close the current alignment */
782 align
->left
= cur_align_start
;
785 case WPS_ALIGN_CENTER
:
786 align
->center
= cur_align_start
;
789 case WPS_ALIGN_RIGHT
:
790 align
->right
= cur_align_start
;
796 static void get_subline_timeout(struct gui_wps
*gwps
, struct skin_subline
*subline
)
798 struct wps_data
*data
= gwps
->data
;
800 subline
->time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
802 for (i
= subline
->first_token_idx
;
803 i
<= subline
->last_token_idx
; i
++)
805 switch(data
->tokens
[i
].type
)
807 case WPS_TOKEN_CONDITIONAL
:
808 /* place ourselves in the right conditional case */
809 evaluate_conditional(gwps
, &i
);
812 case WPS_TOKEN_CONDITIONAL_OPTION
:
813 /* we've finished in the curent conditional case,
814 skip to the end of the conditional structure */
815 i
= find_conditional_end(data
, i
);
818 case WPS_TOKEN_SUBLINE_TIMEOUT
:
819 subline
->time_mult
= data
->tokens
[i
].value
.i
;
828 /* Calculates which subline should be displayed for the specified line
829 Returns true iff the subline must be refreshed */
830 static bool update_curr_subline(struct gui_wps
*gwps
, struct skin_line
*line
)
832 /* shortcut this whole thing if we need to reset the line completly */
833 if (line
->curr_subline
== NULL
)
835 line
->subline_expire_time
= current_tick
;
836 line
->curr_subline
= &line
->sublines
;
837 if (!line
->curr_subline
->next
)
839 line
->subline_expire_time
+= 100*HZ
;
843 get_subline_timeout(gwps
, line
->curr_subline
);
844 line
->subline_expire_time
+= TIMEOUT_UNIT
*line
->curr_subline
->time_mult
;
848 /* if time to advance to next sub-line */
849 if (TIME_AFTER(current_tick
, line
->subline_expire_time
- 1))
851 /* if there is only one subline, there is no need to search for a new one */
852 if (&line
->sublines
== line
->curr_subline
&&
853 line
->curr_subline
->next
== NULL
)
855 line
->subline_expire_time
+= 100 * HZ
;
858 if (line
->curr_subline
->next
)
859 line
->curr_subline
= line
->curr_subline
->next
;
861 line
->curr_subline
= &line
->sublines
;
862 get_subline_timeout(gwps
, line
->curr_subline
);
863 line
->subline_expire_time
= current_tick
+ TIMEOUT_UNIT
*line
->curr_subline
->time_mult
;
869 /* Display a line appropriately according to its alignment format.
870 format_align contains the text, separated between left, center and right.
871 line is the index of the line on the screen.
872 scroll indicates whether the line is a scrolling one or not.
874 static void write_line(struct screen
*display
,
875 struct align_pos
*format_align
,
879 int left_width
= 0, left_xpos
;
880 int center_width
= 0, center_xpos
;
881 int right_width
= 0, right_xpos
;
887 /* calculate different string sizes and positions */
888 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
889 if (format_align
->left
!= 0) {
890 display
->getstringsize((unsigned char *)format_align
->left
,
891 &left_width
, &string_height
);
894 if (format_align
->right
!= 0) {
895 display
->getstringsize((unsigned char *)format_align
->right
,
896 &right_width
, &string_height
);
899 if (format_align
->center
!= 0) {
900 display
->getstringsize((unsigned char *)format_align
->center
,
901 ¢er_width
, &string_height
);
905 right_xpos
= (display
->getwidth() - right_width
);
906 center_xpos
= (display
->getwidth() + left_xpos
- center_width
) / 2;
908 scroll_width
= display
->getwidth() - left_xpos
;
910 /* Checks for overlapping strings.
911 If needed the overlapping strings will be merged, separated by a
914 /* CASE 1: left and centered string overlap */
915 /* there is a left string, need to merge left and center */
916 if ((left_width
!= 0 && center_width
!= 0) &&
917 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
918 /* replace the former separator '\0' of left and
919 center string with a space */
920 *(--format_align
->center
) = ' ';
921 /* calculate the new width and position of the merged string */
922 left_width
= left_width
+ space_width
+ center_width
;
923 /* there is no centered string anymore */
926 /* there is no left string, move center to left */
927 if ((left_width
== 0 && center_width
!= 0) &&
928 (left_xpos
+ left_width
> center_xpos
)) {
929 /* move the center string to the left string */
930 format_align
->left
= format_align
->center
;
931 /* calculate the new width and position of the string */
932 left_width
= center_width
;
933 /* there is no centered string anymore */
937 /* CASE 2: centered and right string overlap */
938 /* there is a right string, need to merge center and right */
939 if ((center_width
!= 0 && right_width
!= 0) &&
940 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
941 /* replace the former separator '\0' of center and
942 right string with a space */
943 *(--format_align
->right
) = ' ';
944 /* move the center string to the right after merge */
945 format_align
->right
= format_align
->center
;
946 /* calculate the new width and position of the merged string */
947 right_width
= center_width
+ space_width
+ right_width
;
948 right_xpos
= (display
->getwidth() - right_width
);
949 /* there is no centered string anymore */
952 /* there is no right string, move center to right */
953 if ((center_width
!= 0 && right_width
== 0) &&
954 (center_xpos
+ center_width
> right_xpos
)) {
955 /* move the center string to the right string */
956 format_align
->right
= format_align
->center
;
957 /* calculate the new width and position of the string */
958 right_width
= center_width
;
959 right_xpos
= (display
->getwidth() - right_width
);
960 /* there is no centered string anymore */
964 /* CASE 3: left and right overlap
965 There is no center string anymore, either there never
966 was one or it has been merged in case 1 or 2 */
967 /* there is a left string, need to merge left and right */
968 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
969 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
970 /* replace the former separator '\0' of left and
971 right string with a space */
972 *(--format_align
->right
) = ' ';
973 /* calculate the new width and position of the string */
974 left_width
= left_width
+ space_width
+ right_width
;
975 /* there is no right string anymore */
978 /* there is no left string, move right to left */
979 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
980 (left_width
> right_xpos
)) {
981 /* move the right string to the left string */
982 format_align
->left
= format_align
->right
;
983 /* calculate the new width and position of the string */
984 left_width
= right_width
;
985 /* there is no right string anymore */
989 ypos
= (line
* string_height
);
992 if (scroll
&& ((left_width
> scroll_width
) ||
993 (center_width
> scroll_width
) ||
994 (right_width
> scroll_width
)))
996 display
->puts_scroll(0, line
,
997 (unsigned char *)format_align
->left
);
1001 #ifdef HAVE_LCD_BITMAP
1002 /* clear the line first */
1003 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1004 display
->fillrect(left_xpos
, ypos
, display
->getwidth(), string_height
);
1005 display
->set_drawmode(DRMODE_SOLID
);
1008 /* Nasty hack: we output an empty scrolling string,
1009 which will reset the scroller for that line */
1010 display
->puts_scroll(0, line
, (unsigned char *)"");
1012 /* print aligned strings */
1013 if (left_width
!= 0)
1015 display
->putsxy(left_xpos
, ypos
,
1016 (unsigned char *)format_align
->left
);
1018 if (center_width
!= 0)
1020 display
->putsxy(center_xpos
, ypos
,
1021 (unsigned char *)format_align
->center
);
1023 if (right_width
!= 0)
1025 display
->putsxy(right_xpos
, ypos
,
1026 (unsigned char *)format_align
->right
);
1031 static bool skin_redraw(struct gui_wps
*gwps
, unsigned refresh_mode
)
1033 struct wps_data
*data
= gwps
->data
;
1034 struct screen
*display
= gwps
->display
;
1036 if (!data
|| !display
|| !gwps
->state
)
1040 char linebuf
[MAX_PATH
];
1042 struct align_pos align
;
1044 align
.center
= NULL
;
1048 struct skin_token_list
*viewport_list
;
1050 bool update_line
, new_subline_refresh
;
1052 #ifdef HAVE_LCD_BITMAP
1054 /* to find out wether the peak meter is enabled we
1055 assume it wasn't until we find a line that contains
1056 the peak meter. We can't use peak_meter_enabled itself
1057 because that would mean to turn off the meter thread
1058 temporarily. (That shouldn't matter unless yield
1059 or sleep is called but who knows...)
1061 bool enable_pm
= false;
1065 /* reset to first subline if refresh all flag is set */
1066 if (refresh_mode
== WPS_REFRESH_ALL
)
1068 struct skin_line
*line
;
1069 struct skin_viewport
*skin_viewport
= find_viewport(VP_DEFAULT_LABEL
, data
);
1071 if (!(skin_viewport
->hidden_flags
& VP_NEVER_VISIBLE
))
1073 display
->set_viewport(&skin_viewport
->vp
);
1074 display
->clear_viewport();
1077 for (viewport_list
= data
->viewports
;
1078 viewport_list
; viewport_list
= viewport_list
->next
)
1081 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1082 for(line
= skin_viewport
->lines
; line
; line
= line
->next
)
1084 line
->curr_subline
= NULL
;
1089 #ifdef HAVE_LCD_CHARCELLS
1091 for (i
= 0; i
< 8; i
++)
1093 if (data
->wps_progress_pat
[i
] == 0)
1094 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1098 /* disable any viewports which are conditionally displayed.
1099 * If we are only refreshing the peak meter then don't change the viewport
1100 * enabled flags as this will stop scrolling. viewports cant be
1101 * toggled in this refresh mode anyway (FS#10215)*/
1102 if (refresh_mode
!= WPS_REFRESH_PEAK_METER
)
1104 for (viewport_list
= data
->viewports
;
1105 viewport_list
; viewport_list
= viewport_list
->next
)
1107 struct skin_viewport
*skin_viewport
=
1108 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1109 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
1113 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDEABLE
)
1115 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
)
1116 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1118 skin_viewport
->hidden_flags
|= VP_DRAW_HIDDEN
;
1122 int viewport_count
= 0;
1123 for (viewport_list
= data
->viewports
;
1124 viewport_list
; viewport_list
= viewport_list
->next
, viewport_count
++)
1126 struct skin_viewport
*skin_viewport
=
1127 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1128 unsigned vp_refresh_mode
= refresh_mode
;
1130 display
->set_viewport(&skin_viewport
->vp
);
1134 #ifdef HAVE_LCD_BITMAP
1135 /* Set images to not to be displayed */
1136 struct skin_token_list
*imglist
= data
->images
;
1139 struct gui_img
*img
= (struct gui_img
*)imglist
->token
->value
.data
;
1141 imglist
= imglist
->next
;
1144 /* dont redraw the viewport if its disabled */
1145 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
1146 { /* don't draw anything into this one */
1147 vp_refresh_mode
= 0; hidden_vp
= true;
1149 else if ((skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
))
1151 if (!(skin_viewport
->hidden_flags
&VP_DRAW_WASHIDDEN
))
1152 display
->scroll_stop(&skin_viewport
->vp
);
1153 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1156 else if (((skin_viewport
->hidden_flags
&
1157 (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
))
1158 == (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
)))
1160 vp_refresh_mode
= WPS_REFRESH_ALL
;
1161 skin_viewport
->hidden_flags
= VP_DRAW_HIDEABLE
;
1164 if (vp_refresh_mode
== WPS_REFRESH_ALL
)
1166 display
->clear_viewport();
1169 /* loop over the lines for this viewport */
1170 struct skin_line
*line
;
1173 for (line
= skin_viewport
->lines
; line
; line
= line
->next
, line_count
++)
1175 struct skin_subline
*subline
;
1176 memset(linebuf
, 0, sizeof(linebuf
));
1177 update_line
= false;
1179 /* get current subline for the line */
1180 new_subline_refresh
= update_curr_subline(gwps
, line
);
1181 subline
= line
->curr_subline
;
1182 flags
= line
->curr_subline
->line_type
;
1184 if (vp_refresh_mode
== WPS_REFRESH_ALL
|| (flags
& vp_refresh_mode
)
1185 || new_subline_refresh
|| hidden_vp
)
1187 /* get_line tells us if we need to update the line */
1188 update_line
= get_line(gwps
, subline
, &align
,
1189 linebuf
, sizeof(linebuf
), vp_refresh_mode
);
1191 #ifdef HAVE_LCD_BITMAP
1193 if (flags
& vp_refresh_mode
& WPS_REFRESH_PEAK_METER
)
1195 /* the peakmeter should be alone on its line */
1196 update_line
= false;
1198 int h
= font_get(skin_viewport
->vp
.font
)->height
;
1199 int peak_meter_y
= line_count
* h
;
1201 /* The user might decide to have the peak meter in the last
1202 line so that it is only displayed if no status bar is
1203 visible. If so we neither want do draw nor enable the
1205 if (peak_meter_y
+ h
<= skin_viewport
->vp
.y
+skin_viewport
->vp
.height
) {
1206 /* found a line with a peak meter -> remember that we must
1209 peak_meter_enabled
= true;
1210 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
1211 MIN(h
, skin_viewport
->vp
.y
+skin_viewport
->vp
.height
- peak_meter_y
));
1215 peak_meter_enabled
= false;
1219 #else /* HAVE_LCD_CHARCELL */
1222 if (flags
& vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1224 if (data
->full_line_progressbar
)
1225 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
1227 draw_player_progress(gwps
);
1231 if (update_line
&& !hidden_vp
&&
1232 /* conditionals clear the line which means if the %Vd is put into the default
1233 viewport there will be a blank line.
1234 To get around this we dont allow any actual drawing to happen in the
1235 deault vp if other vp's are defined */
1236 ((skin_viewport
->label
!= VP_DEFAULT_LABEL
&& viewport_list
->next
) ||
1237 !viewport_list
->next
))
1239 if (flags
& WPS_REFRESH_SCROLL
)
1241 /* if the line is a scrolling one we don't want to update
1242 too often, so that it has the time to scroll */
1243 if ((vp_refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
1244 write_line(display
, &align
, line_count
, true);
1247 write_line(display
, &align
, line_count
, false);
1250 #ifdef HAVE_LCD_BITMAP
1252 if (vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1254 struct skin_token_list
*bar
= gwps
->data
->progressbars
;
1257 struct progressbar
*thisbar
= (struct progressbar
*)bar
->token
->value
.data
;
1258 if (thisbar
->vp
== &skin_viewport
->vp
)
1260 draw_progressbar(gwps
, thisbar
);
1265 /* Now display any images in this viewport */
1267 wps_display_images(gwps
, &skin_viewport
->vp
);
1271 #ifdef HAVE_LCD_BITMAP
1272 data
->peak_meter_enabled
= enable_pm
;
1274 /* Restore the default viewport */
1275 display
->set_viewport(NULL
);