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 ****************************************************************************/
24 #include "string-extra.h"
28 #include "rbunicode.h"
30 #include "powermgmt.h"
38 #include "statusbar.h"
40 #include "scrollbar.h"
41 #include "screen_access.h"
46 #ifdef HAVE_LCD_BITMAP
47 #include "peakmeter.h"
56 #if CONFIG_CODEC == SWCODEC
65 #include "root_menu.h"
68 #include "wps_internals.h"
69 #include "skin_engine.h"
70 #include "statusbar-skinned.h"
72 static bool skin_redraw(struct gui_wps
*gwps
, unsigned refresh_mode
);
74 /* update a skinned screen, update_type is WPS_REFRESH_* values.
75 * Usually it should only be WPS_REFRESH_NON_STATIC
76 * A full update will be done if required (state.do_full_update == true)
78 bool skin_update(struct gui_wps
*gwps
, unsigned int update_type
)
81 /* This maybe shouldnt be here, but while the skin is only used to
82 * display the music screen this is better than whereever we are being
83 * called from. This is also safe for skined screen which dont use the id3 */
84 struct mp3entry
*id3
= gwps
->state
->id3
;
85 bool cuesheet_update
= (id3
!= NULL
? cuesheet_subtrack_changed(id3
) : false);
86 gwps
->sync_data
->do_full_update
|= cuesheet_update
;
88 retval
= skin_redraw(gwps
, gwps
->sync_data
->do_full_update
?
89 WPS_REFRESH_ALL
: update_type
);
93 #ifdef HAVE_LCD_BITMAP
95 void skin_statusbar_changed(struct gui_wps
*skin
)
99 struct wps_data
*data
= skin
->data
;
100 const struct screen
*display
= skin
->display
;
101 const int screen
= display
->screen_type
;
103 struct viewport
*vp
= &find_viewport(VP_DEFAULT_LABEL
, data
)->vp
;
104 viewport_set_defaults(vp
, screen
);
106 if (data
->wps_sb_tag
)
107 { /* fix up the default viewport */
108 if (data
->show_sb_on_wps
)
110 if (statusbar_position(screen
) != STATUSBAR_OFF
)
111 return; /* vp is fixed already */
113 vp
->y
= STATUSBAR_HEIGHT
;
114 vp
->height
= display
->lcdheight
- STATUSBAR_HEIGHT
;
118 if (statusbar_position(screen
) == STATUSBAR_OFF
)
119 return; /* vp is fixed already */
121 vp
->height
= display
->lcdheight
;
122 vp
->width
= display
->lcdwidth
;
127 static void draw_progressbar(struct gui_wps
*gwps
,
128 struct progressbar
*pb
)
130 struct screen
*display
= gwps
->display
;
131 struct viewport
*vp
= pb
->vp
;
132 struct wps_state
*state
= gwps
->state
;
133 struct mp3entry
*id3
= state
->id3
;
134 int y
= pb
->y
, height
= pb
->height
;
135 unsigned long length
, elapsed
;
138 height
= font_get(vp
->font
)->height
;
142 int line_height
= font_get(vp
->font
)->height
;
143 /* center the pb in the line, but only if the line is higher than the pb */
144 int center
= (line_height
-height
)/2;
145 /* if Y was not set calculate by font height,Y is -line_number-1 */
146 y
= (-y
-1)*line_height
+ (0 > center
? 0 : center
);
149 if (pb
->type
== WPS_TOKEN_VOLUMEBAR
)
151 int minvol
= sound_min(SOUND_VOLUME
);
152 int maxvol
= sound_max(SOUND_VOLUME
);
153 length
= maxvol
-minvol
;
154 elapsed
= global_settings
.volume
-minvol
;
156 else if (pb
->type
== WPS_TOKEN_BATTERY_PERCENTBAR
)
159 elapsed
= battery_level();
162 else if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF
))
164 int min
= fm_region_data
[global_settings
.fm_region
].freq_min
;
165 elapsed
= radio_current_frequency() - min
;
166 length
= fm_region_data
[global_settings
.fm_region
].freq_max
- min
;
169 else if (id3
&& id3
->length
)
171 length
= id3
->length
;
172 elapsed
= id3
->elapsed
+ state
->ff_rewind_count
;
180 if (pb
->have_bitmap_pb
)
181 gui_bitmap_scrollbar_draw(display
, &pb
->bm
,
182 pb
->x
, y
, pb
->width
, pb
->bm
.height
,
183 length
, 0, elapsed
, HORIZONTAL
);
185 gui_scrollbar_draw(display
, pb
->x
, y
, pb
->width
, height
,
186 length
, 0, elapsed
, HORIZONTAL
);
188 if (pb
->type
== WPS_TOKEN_PROGRESSBAR
)
190 if (id3
&& id3
->length
)
192 #ifdef AB_REPEAT_ENABLE
193 if (ab_repeat_mode_enabled())
194 ab_draw_markers(display
, id3
->length
,
195 pb
->x
, y
, pb
->width
, height
);
199 cue_draw_markers(display
, id3
->cuesheet
, id3
->length
,
200 pb
->x
, y
+1, pb
->width
, height
-2);
202 #if 0 /* disable for now CONFIG_TUNER */
203 else if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF
))
205 presets_draw_markers(display
, pb
->x
, y
, pb
->width
, height
);
211 static void draw_playlist_viewer_list(struct gui_wps
*gwps
,
212 struct playlistviewer
*viewer
)
214 struct wps_state
*state
= gwps
->state
;
215 int lines
= viewport_get_nb_lines(viewer
->vp
);
216 int line_height
= font_get(viewer
->vp
->font
)->height
;
220 struct wps_token token
;
221 int x
, length
, alignment
= WPS_TOKEN_ALIGN_LEFT
;
223 struct mp3entry
*pid3
;
224 char buf
[MAX_PATH
*2], tempbuf
[MAX_PATH
];
225 const char *filename
;
227 if (current_screen() == GO_TO_FM
)
229 cur_pos
= radio_current_preset();
230 start_item
= cur_pos
+ viewer
->start_offset
;
231 max
= start_item
+radio_preset_count();
236 cur_pos
= playlist_get_display_index();
237 max
= playlist_amount()+1;
238 start_item
= MAX(0, cur_pos
+ viewer
->start_offset
);
241 gwps
->display
->set_viewport(viewer
->vp
);
242 for(i
=start_item
; (i
-start_item
)<lines
&& i
<max
; i
++)
246 if (current_screen() == GO_TO_FM
)
249 line
= TRACK_HAS_INFO
;
255 filename
= playlist_peek(i
-cur_pos
);
260 else if (i
== cur_pos
+1)
264 #if CONFIG_CODEC == SWCODEC
267 #ifdef HAVE_TC_RAMCACHE
268 if (tagcache_fill_tags(&viewer
->tempid3
, filename
))
270 pid3
= &viewer
->tempid3
;
274 if (!audio_peek_track(&pid3
, i
-cur_pos
))
282 line
= pid3
? TRACK_HAS_INFO
: TRACK_HAS_NO_INFO
;
284 int j
= 0, cur_string
= 0;
285 unsigned int line_len
= 0;
287 while (j
< viewer
->lines
[line
].count
&& line_len
< sizeof(buf
))
289 const char *out
= NULL
;
290 token
.type
= viewer
->lines
[line
].tokens
[j
];
293 out
= get_id3_token(&token
, pid3
, tempbuf
, sizeof(tempbuf
), -1, NULL
);
296 out
= get_radio_token(&token
, i
-cur_pos
,
297 tempbuf
, sizeof(tempbuf
), -1, NULL
);
301 line_len
= strlcat(buf
, out
, sizeof(buf
));
306 switch (viewer
->lines
[line
].tokens
[j
])
308 case WPS_TOKEN_ALIGN_CENTER
:
309 case WPS_TOKEN_ALIGN_LEFT
:
310 case WPS_TOKEN_ALIGN_LEFT_RTL
:
311 case WPS_TOKEN_ALIGN_RIGHT
:
312 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
313 alignment
= viewer
->lines
[line
].tokens
[j
];
316 case WPS_TOKEN_STRING
:
317 case WPS_TOKEN_CHARACTER
:
318 snprintf(tempbuf
, sizeof(tempbuf
), "%s",
319 viewer
->lines
[line
].strings
[cur_string
]);
322 case WPS_TOKEN_PLAYLIST_POSITION
:
323 snprintf(tempbuf
, sizeof(tempbuf
), "%d", i
);
325 case WPS_TOKEN_FILE_NAME
:
326 get_dir(tempbuf
, sizeof(tempbuf
), filename
, 0);
328 case WPS_TOKEN_FILE_PATH
:
329 snprintf(tempbuf
, sizeof(tempbuf
), "%s", filename
);
337 line_len
= strlcat(buf
, tempbuf
, sizeof(buf
));
342 int vpwidth
= viewer
->vp
->width
;
343 length
= gwps
->display
->getstringsize(buf
, NULL
, NULL
);
344 if (viewer
->lines
[line
].scroll
&& length
>= vpwidth
)
346 gwps
->display
->puts_scroll(0, (i
-start_item
), buf
);
350 if (length
>= vpwidth
)
356 case WPS_TOKEN_ALIGN_CENTER
:
357 x
= (vpwidth
-length
)/2;
359 case WPS_TOKEN_ALIGN_LEFT_RTL
:
360 if (lang_is_rtl() && VP_IS_RTL(viewer
->vp
))
362 x
= vpwidth
- length
;
365 case WPS_TOKEN_ALIGN_LEFT
:
368 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
369 if (lang_is_rtl() && VP_IS_RTL(viewer
->vp
))
374 case WPS_TOKEN_ALIGN_RIGHT
:
375 x
= vpwidth
- length
;
382 gwps
->display
->putsxy(x
, (i
-start_item
)*line_height
, buf
);
388 /* clears the area where the image was shown */
389 static void clear_image_pos(struct gui_wps
*gwps
, struct gui_img
*img
)
393 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
394 gwps
->display
->fillrect(img
->x
, img
->y
, img
->bm
.width
, img
->subimage_height
);
395 gwps
->display
->set_drawmode(DRMODE_SOLID
);
398 static void wps_draw_image(struct gui_wps
*gwps
, struct gui_img
*img
, int subimage
)
400 struct screen
*display
= gwps
->display
;
401 if(img
->always_display
)
402 display
->set_drawmode(DRMODE_FG
);
404 display
->set_drawmode(DRMODE_SOLID
);
407 if(img
->bm
.format
== FORMAT_MONO
) {
409 display
->mono_bitmap_part(img
->bm
.data
,
410 0, img
->subimage_height
* subimage
,
411 img
->bm
.width
, img
->x
,
412 img
->y
, img
->bm
.width
,
413 img
->subimage_height
);
416 display
->transparent_bitmap_part((fb_data
*)img
->bm
.data
,
417 0, img
->subimage_height
* subimage
,
418 STRIDE(display
->screen_type
,
419 img
->bm
.width
, img
->bm
.height
),
420 img
->x
, img
->y
, img
->bm
.width
,
421 img
->subimage_height
);
426 static void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
428 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
431 struct wps_data
*data
= gwps
->data
;
432 struct screen
*display
= gwps
->display
;
433 struct skin_token_list
*list
= data
->images
;
437 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
440 if (img
->display
>= 0)
442 wps_draw_image(gwps
, img
, img
->display
);
444 else if (img
->always_display
&& img
->vp
== vp
)
446 wps_draw_image(gwps
, img
, 0);
452 /* now draw the AA */
453 if (data
->albumart
&& data
->albumart
->vp
== vp
454 && data
->albumart
->draw
)
456 int handle
= playback_current_aa_hid(data
->playback_aa_slot
);
458 if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF
))
460 struct dim dim
= {data
->albumart
->width
, data
->albumart
->height
};
461 handle
= radio_get_art_hid(&dim
);
464 draw_album_art(gwps
, handle
, false);
465 data
->albumart
->draw
= false;
469 display
->set_drawmode(DRMODE_SOLID
);
472 #else /* HAVE_LCD_CHARCELL */
474 static bool draw_player_progress(struct gui_wps
*gwps
)
476 struct wps_state
*state
= gwps
->state
;
477 struct screen
*display
= gwps
->display
;
478 unsigned char progress_pattern
[7];
483 if (LIKELY(state
->id3
))
485 elapsed
= state
->id3
->elapsed
;
486 length
= state
->id3
->length
;
495 pos
= 36 * (elapsed
+ state
->ff_rewind_count
) / length
;
497 for (i
= 0; i
< 7; i
++, pos
-= 5)
500 progress_pattern
[i
] = 0x1fu
;
502 progress_pattern
[i
] = 0x00u
;
504 progress_pattern
[i
] = 0x1fu
>> pos
;
507 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
511 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
513 static const unsigned char numbers
[10][4] = {
514 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
515 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
516 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
517 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
518 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
519 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
520 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
521 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
522 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
523 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
526 struct wps_state
*state
= gwps
->state
;
527 struct screen
*display
= gwps
->display
;
528 struct wps_data
*data
= gwps
->data
;
529 unsigned char progress_pattern
[7];
539 if (LIKELY(state
->id3
))
541 elapsed
= state
->id3
->elapsed
;
542 length
= state
->id3
->length
;
550 if (buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
553 time
= elapsed
+ state
->ff_rewind_count
;
555 pos
= 55 * time
/ length
;
557 memset(timestr
, 0, sizeof(timestr
));
558 format_time(timestr
, sizeof(timestr
)-2, time
);
559 timestr
[strlen(timestr
)] = ':'; /* always safe */
561 for (i
= 0; i
< 11; i
++, pos
-= 5)
564 memset(progress_pattern
, 0, sizeof(progress_pattern
));
566 if ((digit
= timestr
[time_idx
]))
571 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
573 memcpy(progress_pattern
, numbers
[digit
], 4);
576 else /* tens, shifted right */
578 for (j
= 0; j
< 4; j
++)
579 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
581 if (time_idx
> 0) /* not the first group, add colon in front */
583 progress_pattern
[1] |= 0x10u
;
584 progress_pattern
[3] |= 0x10u
;
590 progress_pattern
[5] = progress_pattern
[6] = 0x1fu
;
593 if (pos
> 0 && pos
< 5)
596 progress_pattern
[5] = progress_pattern
[6] = (~0x1fu
>> pos
) & 0x1fu
;
599 if (softchar
&& pat_idx
< 8)
601 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
603 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
607 buf
= utf8encode(' ', buf
);
609 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
614 #endif /* HAVE_LCD_CHARCELL */
616 /* Return the index to the end token for the conditional token at index.
617 The conditional token can be either a start token or a separator
620 static int find_conditional_end(struct wps_data
*data
, int index
)
623 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
624 ret
= data
->tokens
[ret
].value
.i
;
626 /* ret now is the index to the end token for the conditional. */
630 /* Evaluate the conditional that is at *token_index and return whether a skip
631 has ocurred. *token_index is updated with the new position.
633 static bool evaluate_conditional(struct gui_wps
*gwps
, int *token_index
)
638 struct wps_data
*data
= gwps
->data
;
641 int cond_index
= *token_index
;
644 unsigned char num_options
= data
->tokens
[cond_index
].value
.i
& 0xFF;
645 unsigned char prev_val
= (data
->tokens
[cond_index
].value
.i
& 0xFF00) >> 8;
647 /* treat ?xx<true> constructs as if they had 2 options. */
651 int intval
= num_options
;
652 /* get_token_value needs to know the number of options in the enum */
653 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
654 result
, sizeof(result
), &intval
);
656 /* intval is now the number of the enum option we want to read,
657 starting from 1. If intval is -1, we check if value is empty. */
659 intval
= (value
&& *value
) ? 1 : num_options
;
660 else if (intval
> num_options
|| intval
< 1)
661 intval
= num_options
;
663 data
->tokens
[cond_index
].value
.i
= (intval
<< 8) + num_options
;
665 /* skip to the appropriate enum case */
666 int next
= cond_index
+ 2;
667 for (i
= 1; i
< intval
; i
++)
669 next
= data
->tokens
[next
].value
.i
;
673 if (prev_val
== intval
)
675 /* Same conditional case as previously. Return without clearing the
680 cond_end
= find_conditional_end(data
, cond_index
+ 2);
681 for (i
= cond_index
+ 3; i
< cond_end
; i
++)
683 #ifdef HAVE_LCD_BITMAP
684 /* clear all pictures in the conditional and nested ones */
685 if (data
->tokens
[i
].type
== WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
)
686 clear_image_pos(gwps
, find_image(data
->tokens
[i
].value
.i
&0xFF, data
));
687 else if (data
->tokens
[i
].type
== WPS_TOKEN_VOLUMEBAR
||
688 data
->tokens
[i
].type
== WPS_TOKEN_PROGRESSBAR
||
689 data
->tokens
[i
].type
== WPS_TOKEN_BATTERY_PERCENTBAR
)
691 struct progressbar
*bar
= (struct progressbar
*)data
->tokens
[i
].value
.data
;
694 else if (data
->tokens
[i
].type
== WPS_TOKEN_PEAKMETER
)
696 data
->peak_meter_enabled
= false;
700 if (data
->albumart
&& data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
703 playback_current_aa_hid(data
->playback_aa_slot
), true);
704 data
->albumart
->draw
= false;
713 /* Read a (sub)line to the given alignment format buffer.
714 linebuf is the buffer where the data is actually stored.
715 align is the alignment format that'll be used to display the text.
716 The return value indicates whether the line needs to be updated.
718 static bool get_line(struct gui_wps
*gwps
,
719 struct skin_subline
*subline
,
720 struct align_pos
*align
,
723 unsigned refresh_mode
)
725 struct wps_data
*data
= gwps
->data
;
728 char *buf
= linebuf
; /* will always point to the writing position */
729 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
732 (void)refresh_mode
; /* silence warning on charcell */
734 /* alignment-related variables */
736 char* cur_align_start
;
737 cur_align_start
= buf
;
738 cur_align
= WPS_ALIGN_LEFT
;
740 align
->center
= NULL
;
742 /* Process all tokens of the desired subline */
743 for (i
= subline
->first_token_idx
;
744 i
<= subline
->last_token_idx
; i
++)
746 switch(data
->tokens
[i
].type
)
748 case WPS_TOKEN_CONDITIONAL
:
749 /* place ourselves in the right conditional case */
750 update
|= evaluate_conditional(gwps
, &i
);
753 case WPS_TOKEN_CONDITIONAL_OPTION
:
754 /* we've finished in the curent conditional case,
755 skip to the end of the conditional structure */
756 i
= find_conditional_end(data
, i
);
759 #ifdef HAVE_LCD_BITMAP
760 case WPS_TOKEN_PEAKMETER
:
761 data
->peak_meter_enabled
= true;
763 case WPS_TOKEN_VOLUMEBAR
:
764 case WPS_TOKEN_BATTERY_PERCENTBAR
:
765 case WPS_TOKEN_PROGRESSBAR
:
767 struct progressbar
*bar
= (struct progressbar
*)data
->tokens
[i
].value
.data
;
771 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
773 char n
= data
->tokens
[i
].value
.i
& 0xFF;
774 int subimage
= data
->tokens
[i
].value
.i
>> 8;
775 struct gui_img
*img
= find_image(n
, data
);
777 if (img
&& img
->loaded
)
778 img
->display
= subimage
;
781 case WPS_TOKEN_DRAW_INBUILTBAR
:
782 gui_statusbar_draw(&(statusbars
.statusbars
[gwps
->display
->screen_type
]),
783 refresh_mode
== WPS_REFRESH_ALL
,
784 data
->tokens
[i
].value
.data
);
788 case WPS_TOKEN_ALIGN_LEFT
:
789 case WPS_TOKEN_ALIGN_LEFT_RTL
:
790 case WPS_TOKEN_ALIGN_CENTER
:
791 case WPS_TOKEN_ALIGN_RIGHT
:
792 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
793 /* remember where the current aligned text started */
797 align
->left
= cur_align_start
;
800 case WPS_ALIGN_CENTER
:
801 align
->center
= cur_align_start
;
804 case WPS_ALIGN_RIGHT
:
805 align
->right
= cur_align_start
;
808 /* start a new alignment */
809 switch (data
->tokens
[i
].type
)
811 case WPS_TOKEN_ALIGN_LEFT
:
812 cur_align
= WPS_ALIGN_LEFT
;
814 case WPS_TOKEN_ALIGN_LEFT_RTL
:
815 cur_align
= lang_is_rtl() ? WPS_ALIGN_RIGHT
:
818 case WPS_TOKEN_ALIGN_CENTER
:
819 cur_align
= WPS_ALIGN_CENTER
;
821 case WPS_TOKEN_ALIGN_RIGHT
:
822 cur_align
= WPS_ALIGN_RIGHT
;
824 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
825 cur_align
= lang_is_rtl() ? WPS_ALIGN_LEFT
:
832 cur_align_start
= buf
;
834 case WPS_VIEWPORT_ENABLE
:
836 char label
= data
->tokens
[i
].value
.i
;
837 char temp
= VP_DRAW_HIDEABLE
;
838 /* viewports are allowed to share id's so find and enable
840 struct skin_token_list
*list
= data
->viewports
;
843 struct skin_viewport
*vp
=
844 (struct skin_viewport
*)list
->token
->value
.data
;
845 if (vp
->label
== label
)
847 if (vp
->hidden_flags
&VP_DRAW_WASHIDDEN
)
848 temp
|= VP_DRAW_WASHIDDEN
;
849 vp
->hidden_flags
= temp
;
855 #ifdef HAVE_LCD_BITMAP
856 case WPS_TOKEN_UIVIEWPORT_ENABLE
:
857 sb_set_info_vp(gwps
->display
->screen_type
,
858 data
->tokens
[i
].value
.i
|VP_INFO_LABEL
);
860 case WPS_VIEWPORT_CUSTOMLIST
:
861 draw_playlist_viewer_list(gwps
, data
->tokens
[i
].value
.data
);
866 /* get the value of the tag and copy it to the buffer */
867 const char *value
= get_token_value(gwps
, &data
->tokens
[i
],
868 temp_buf
, sizeof(temp_buf
), NULL
);
872 while (*value
&& (buf
< linebuf_end
))
880 /* close the current alignment */
884 align
->left
= cur_align_start
;
887 case WPS_ALIGN_CENTER
:
888 align
->center
= cur_align_start
;
891 case WPS_ALIGN_RIGHT
:
892 align
->right
= cur_align_start
;
898 static void get_subline_timeout(struct gui_wps
*gwps
, struct skin_subline
*subline
)
900 struct wps_data
*data
= gwps
->data
;
902 subline
->time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
904 for (i
= subline
->first_token_idx
;
905 i
<= subline
->last_token_idx
; i
++)
907 switch(data
->tokens
[i
].type
)
909 case WPS_TOKEN_CONDITIONAL
:
910 /* place ourselves in the right conditional case */
911 evaluate_conditional(gwps
, &i
);
914 case WPS_TOKEN_CONDITIONAL_OPTION
:
915 /* we've finished in the curent conditional case,
916 skip to the end of the conditional structure */
917 i
= find_conditional_end(data
, i
);
920 case WPS_TOKEN_SUBLINE_TIMEOUT
:
921 subline
->time_mult
= data
->tokens
[i
].value
.i
;
930 /* Calculates which subline should be displayed for the specified line
931 Returns true iff the subline must be refreshed */
932 static bool update_curr_subline(struct gui_wps
*gwps
, struct skin_line
*line
)
934 /* shortcut this whole thing if we need to reset the line completly */
935 if (line
->curr_subline
== NULL
)
937 line
->subline_expire_time
= current_tick
;
938 line
->curr_subline
= &line
->sublines
;
939 if (!line
->curr_subline
->next
)
941 line
->subline_expire_time
+= 100*HZ
;
945 get_subline_timeout(gwps
, line
->curr_subline
);
946 line
->subline_expire_time
+= TIMEOUT_UNIT
*line
->curr_subline
->time_mult
;
950 /* if time to advance to next sub-line */
951 if (TIME_AFTER(current_tick
, line
->subline_expire_time
- 1))
953 /* if there is only one subline, there is no need to search for a new one */
954 if (&line
->sublines
== line
->curr_subline
&&
955 line
->curr_subline
->next
== NULL
)
957 line
->subline_expire_time
+= 100 * HZ
;
960 if (line
->curr_subline
->next
)
961 line
->curr_subline
= line
->curr_subline
->next
;
963 line
->curr_subline
= &line
->sublines
;
964 get_subline_timeout(gwps
, line
->curr_subline
);
965 line
->subline_expire_time
= current_tick
+ TIMEOUT_UNIT
*line
->curr_subline
->time_mult
;
971 /* Display a line appropriately according to its alignment format.
972 format_align contains the text, separated between left, center and right.
973 line is the index of the line on the screen.
974 scroll indicates whether the line is a scrolling one or not.
976 static void write_line(struct screen
*display
,
977 struct align_pos
*format_align
,
981 int left_width
= 0, left_xpos
;
982 int center_width
= 0, center_xpos
;
983 int right_width
= 0, right_xpos
;
989 /* calculate different string sizes and positions */
990 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
991 if (format_align
->left
!= 0) {
992 display
->getstringsize((unsigned char *)format_align
->left
,
993 &left_width
, &string_height
);
996 if (format_align
->right
!= 0) {
997 display
->getstringsize((unsigned char *)format_align
->right
,
998 &right_width
, &string_height
);
1001 if (format_align
->center
!= 0) {
1002 display
->getstringsize((unsigned char *)format_align
->center
,
1003 ¢er_width
, &string_height
);
1007 right_xpos
= (display
->getwidth() - right_width
);
1008 center_xpos
= (display
->getwidth() + left_xpos
- center_width
) / 2;
1010 scroll_width
= display
->getwidth() - left_xpos
;
1012 /* Checks for overlapping strings.
1013 If needed the overlapping strings will be merged, separated by a
1016 /* CASE 1: left and centered string overlap */
1017 /* there is a left string, need to merge left and center */
1018 if ((left_width
!= 0 && center_width
!= 0) &&
1019 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
1020 /* replace the former separator '\0' of left and
1021 center string with a space */
1022 *(--format_align
->center
) = ' ';
1023 /* calculate the new width and position of the merged string */
1024 left_width
= left_width
+ space_width
+ center_width
;
1025 /* there is no centered string anymore */
1028 /* there is no left string, move center to left */
1029 if ((left_width
== 0 && center_width
!= 0) &&
1030 (left_xpos
+ left_width
> center_xpos
)) {
1031 /* move the center string to the left string */
1032 format_align
->left
= format_align
->center
;
1033 /* calculate the new width and position of the string */
1034 left_width
= center_width
;
1035 /* there is no centered string anymore */
1039 /* CASE 2: centered and right string overlap */
1040 /* there is a right string, need to merge center and right */
1041 if ((center_width
!= 0 && right_width
!= 0) &&
1042 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
1043 /* replace the former separator '\0' of center and
1044 right string with a space */
1045 *(--format_align
->right
) = ' ';
1046 /* move the center string to the right after merge */
1047 format_align
->right
= format_align
->center
;
1048 /* calculate the new width and position of the merged string */
1049 right_width
= center_width
+ space_width
+ right_width
;
1050 right_xpos
= (display
->getwidth() - right_width
);
1051 /* there is no centered string anymore */
1054 /* there is no right string, move center to right */
1055 if ((center_width
!= 0 && right_width
== 0) &&
1056 (center_xpos
+ center_width
> right_xpos
)) {
1057 /* move the center string to the right string */
1058 format_align
->right
= format_align
->center
;
1059 /* calculate the new width and position of the string */
1060 right_width
= center_width
;
1061 right_xpos
= (display
->getwidth() - right_width
);
1062 /* there is no centered string anymore */
1066 /* CASE 3: left and right overlap
1067 There is no center string anymore, either there never
1068 was one or it has been merged in case 1 or 2 */
1069 /* there is a left string, need to merge left and right */
1070 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
1071 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
1072 /* replace the former separator '\0' of left and
1073 right string with a space */
1074 *(--format_align
->right
) = ' ';
1075 /* calculate the new width and position of the string */
1076 left_width
= left_width
+ space_width
+ right_width
;
1077 /* there is no right string anymore */
1080 /* there is no left string, move right to left */
1081 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
1082 (left_width
> right_xpos
)) {
1083 /* move the right string to the left string */
1084 format_align
->left
= format_align
->right
;
1085 /* calculate the new width and position of the string */
1086 left_width
= right_width
;
1087 /* there is no right string anymore */
1091 ypos
= (line
* string_height
);
1094 if (scroll
&& ((left_width
> scroll_width
) ||
1095 (center_width
> scroll_width
) ||
1096 (right_width
> scroll_width
)))
1098 display
->puts_scroll(0, line
,
1099 (unsigned char *)format_align
->left
);
1103 #ifdef HAVE_LCD_BITMAP
1104 /* clear the line first */
1105 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1106 display
->fillrect(left_xpos
, ypos
, display
->getwidth(), string_height
);
1107 display
->set_drawmode(DRMODE_SOLID
);
1110 /* Nasty hack: we output an empty scrolling string,
1111 which will reset the scroller for that line */
1112 display
->puts_scroll(0, line
, (unsigned char *)"");
1114 /* print aligned strings */
1115 if (left_width
!= 0)
1117 display
->putsxy(left_xpos
, ypos
,
1118 (unsigned char *)format_align
->left
);
1120 if (center_width
!= 0)
1122 display
->putsxy(center_xpos
, ypos
,
1123 (unsigned char *)format_align
->center
);
1125 if (right_width
!= 0)
1127 display
->putsxy(right_xpos
, ypos
,
1128 (unsigned char *)format_align
->right
);
1133 static bool skin_redraw(struct gui_wps
*gwps
, unsigned refresh_mode
)
1135 struct wps_data
*data
= gwps
->data
;
1136 struct screen
*display
= gwps
->display
;
1138 if (!data
|| !display
|| !gwps
->state
)
1142 char linebuf
[MAX_PATH
];
1144 struct align_pos align
;
1146 align
.center
= NULL
;
1150 struct skin_token_list
*viewport_list
;
1152 bool update_line
, new_subline_refresh
;
1154 /* reset to first subline if refresh all flag is set */
1155 if (refresh_mode
== WPS_REFRESH_ALL
)
1157 struct skin_line
*line
;
1158 struct skin_viewport
*skin_viewport
= find_viewport(VP_DEFAULT_LABEL
, data
);
1160 if (!(skin_viewport
->hidden_flags
& VP_NEVER_VISIBLE
))
1162 display
->set_viewport(&skin_viewport
->vp
);
1163 display
->clear_viewport();
1166 for (viewport_list
= data
->viewports
;
1167 viewport_list
; viewport_list
= viewport_list
->next
)
1170 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1171 for(line
= skin_viewport
->lines
; line
; line
= line
->next
)
1173 line
->curr_subline
= NULL
;
1178 #ifdef HAVE_LCD_CHARCELLS
1180 for (i
= 0; i
< 8; i
++)
1182 if (data
->wps_progress_pat
[i
] == 0)
1183 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1187 /* disable any viewports which are conditionally displayed.
1188 * If we are only refreshing the peak meter then don't change the viewport
1189 * enabled flags as this will stop scrolling. viewports cant be
1190 * toggled in this refresh mode anyway (FS#10215)*/
1191 if (refresh_mode
!= WPS_REFRESH_PEAK_METER
)
1193 for (viewport_list
= data
->viewports
;
1194 viewport_list
; viewport_list
= viewport_list
->next
)
1196 struct skin_viewport
*skin_viewport
=
1197 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1198 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
1202 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDEABLE
)
1204 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
)
1205 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1207 skin_viewport
->hidden_flags
|= VP_DRAW_HIDDEN
;
1211 int viewport_count
= 0;
1212 for (viewport_list
= data
->viewports
;
1213 viewport_list
; viewport_list
= viewport_list
->next
, viewport_count
++)
1215 struct skin_viewport
*skin_viewport
=
1216 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1217 unsigned vp_refresh_mode
= refresh_mode
;
1219 display
->set_viewport(&skin_viewport
->vp
);
1223 #ifdef HAVE_LCD_BITMAP
1224 /* Set images to not to be displayed */
1225 struct skin_token_list
*imglist
= data
->images
;
1228 struct gui_img
*img
= (struct gui_img
*)imglist
->token
->value
.data
;
1230 imglist
= imglist
->next
;
1233 /* dont redraw the viewport if its disabled */
1234 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
1235 { /* don't draw anything into this one */
1236 vp_refresh_mode
= 0; hidden_vp
= true;
1238 else if ((skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
))
1240 if (!(skin_viewport
->hidden_flags
&VP_DRAW_WASHIDDEN
))
1241 display
->scroll_stop(&skin_viewport
->vp
);
1242 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1245 else if (((skin_viewport
->hidden_flags
&
1246 (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
))
1247 == (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
)))
1249 vp_refresh_mode
= WPS_REFRESH_ALL
;
1250 skin_viewport
->hidden_flags
= VP_DRAW_HIDEABLE
;
1253 if (vp_refresh_mode
== WPS_REFRESH_ALL
)
1255 display
->clear_viewport();
1258 /* loop over the lines for this viewport */
1259 struct skin_line
*line
;
1262 for (line
= skin_viewport
->lines
; line
; line
= line
->next
, line_count
++)
1264 struct skin_subline
*subline
;
1265 memset(linebuf
, 0, sizeof(linebuf
));
1266 update_line
= false;
1268 /* get current subline for the line */
1269 new_subline_refresh
= update_curr_subline(gwps
, line
);
1270 subline
= line
->curr_subline
;
1271 flags
= line
->curr_subline
->line_type
;
1273 if (vp_refresh_mode
== WPS_REFRESH_ALL
|| (flags
& vp_refresh_mode
)
1274 || new_subline_refresh
|| hidden_vp
)
1276 /* get_line tells us if we need to update the line */
1277 update_line
= get_line(gwps
, subline
, &align
,
1278 linebuf
, sizeof(linebuf
), vp_refresh_mode
);
1280 #ifdef HAVE_LCD_BITMAP
1282 if (flags
& vp_refresh_mode
& WPS_REFRESH_PEAK_METER
)
1284 if (!data
->peak_meter_enabled
)
1286 peak_meter_enable(false);
1290 /* the peakmeter should be alone on its line */
1291 update_line
= false;
1293 int h
= font_get(skin_viewport
->vp
.font
)->height
;
1294 int peak_meter_y
= line_count
* h
;
1296 /* The user might decide to have the peak meter in the last
1297 line so that it is only displayed if no status bar is
1298 visible. If so we neither want do draw nor enable the
1300 if (peak_meter_y
+ h
<= skin_viewport
->vp
.y
+skin_viewport
->vp
.height
) {
1301 peak_meter_enable(true);
1302 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
1303 MIN(h
, skin_viewport
->vp
.y
+skin_viewport
->vp
.height
- peak_meter_y
));
1308 #else /* HAVE_LCD_CHARCELL */
1311 if (flags
& vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1313 if (data
->full_line_progressbar
)
1314 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
1316 draw_player_progress(gwps
);
1320 if (update_line
&& !hidden_vp
&&
1321 /* conditionals clear the line which means if the %Vd is put into the default
1322 viewport there will be a blank line.
1323 To get around this we dont allow any actual drawing to happen in the
1324 deault vp if other vp's are defined */
1325 ((skin_viewport
->label
!= VP_DEFAULT_LABEL
&& viewport_list
->next
) ||
1326 !viewport_list
->next
))
1328 if (flags
& WPS_REFRESH_SCROLL
)
1330 /* if the line is a scrolling one we don't want to update
1331 too often, so that it has the time to scroll */
1332 if ((vp_refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
1333 write_line(display
, &align
, line_count
, true);
1336 write_line(display
, &align
, line_count
, false);
1339 #ifdef HAVE_LCD_BITMAP
1341 if (vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1343 struct skin_token_list
*bar
= gwps
->data
->progressbars
;
1346 struct progressbar
*thisbar
= (struct progressbar
*)bar
->token
->value
.data
;
1347 if (thisbar
->vp
== &skin_viewport
->vp
&& thisbar
->draw
)
1349 draw_progressbar(gwps
, thisbar
);
1354 /* Now display any images in this viewport */
1356 wps_display_images(gwps
, &skin_viewport
->vp
);
1360 /* Restore the default viewport */
1361 display
->set_viewport(NULL
);
1368 bool skin_has_sbs(enum screen_type screen
, struct wps_data
*data
)
1373 #ifdef HAVE_LCD_BITMAP
1374 if (data
->wps_sb_tag
)
1375 draw
= data
->show_sb_on_wps
;
1376 else if (statusbar_position(screen
) != STATUSBAR_OFF
)
1382 /* do the button loop as often as required for the peak meters to update
1383 * with a good refresh rate.
1384 * gwps is really gwps[NB_SCREENS]! don't wrap this if FOR_NB_SCREENS()
1386 int skin_wait_for_action(struct gui_wps
*gwps
, int context
, int timeout
)
1388 (void)gwps
; /* silence charcell warning */
1389 int button
= ACTION_NONE
;
1390 #ifdef HAVE_LCD_BITMAP
1392 /* when the peak meter is enabled we want to have a
1393 few extra updates to make it look smooth. On the
1394 other hand we don't want to waste energy if it
1399 if(gwps
[i
].data
->peak_meter_enabled
)
1404 long next_refresh
= current_tick
;
1405 long next_big_refresh
= current_tick
+ timeout
;
1406 button
= BUTTON_NONE
;
1407 while (TIME_BEFORE(current_tick
, next_big_refresh
)) {
1408 button
= get_action(context
,TIMEOUT_NOBLOCK
);
1409 if (button
!= ACTION_NONE
) {
1413 sleep(0); /* Sleep until end of current tick. */
1415 if (TIME_AFTER(current_tick
, next_refresh
)) {
1418 if(gwps
[i
].data
->peak_meter_enabled
)
1419 skin_update(&gwps
[i
], WPS_REFRESH_PEAK_METER
);
1420 next_refresh
+= HZ
/ PEAK_METER_FPS
;
1427 /* The peak meter is disabled
1428 -> no additional screen updates needed */
1432 button
= get_action(context
, timeout
);