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
&& id3
&& id3
->length
)
190 #ifdef AB_REPEAT_ENABLE
191 if (ab_repeat_mode_enabled())
192 ab_draw_markers(display
, id3
->length
,
193 pb
->x
, y
, pb
->width
, height
);
197 cue_draw_markers(display
, id3
->cuesheet
, id3
->length
,
198 pb
->x
, y
+1, pb
->width
, height
-2);
202 static void draw_playlist_viewer_list(struct gui_wps
*gwps
,
203 struct playlistviewer
*viewer
)
205 struct wps_state
*state
= gwps
->state
;
206 int lines
= viewport_get_nb_lines(viewer
->vp
);
207 int line_height
= font_get(viewer
->vp
->font
)->height
;
211 struct wps_token token
;
212 int x
, length
, alignment
= WPS_TOKEN_ALIGN_LEFT
;
214 struct mp3entry
*pid3
;
215 char buf
[MAX_PATH
*2], tempbuf
[MAX_PATH
];
216 const char *filename
;
218 if (current_screen() == GO_TO_FM
)
220 cur_pos
= radio_current_preset();
221 start_item
= cur_pos
+ viewer
->start_offset
;
222 max
= start_item
+radio_preset_count();
227 cur_pos
= playlist_get_display_index();
228 max
= playlist_amount()+1;
229 start_item
= MAX(0, cur_pos
+ viewer
->start_offset
);
232 gwps
->display
->set_viewport(viewer
->vp
);
233 for(i
=start_item
; (i
-start_item
)<lines
&& i
<max
; i
++)
237 if (current_screen() == GO_TO_FM
)
240 line
= TRACK_HAS_INFO
;
246 filename
= playlist_peek(i
-cur_pos
);
251 else if (i
== cur_pos
+1)
255 #if CONFIG_CODEC == SWCODEC
258 #ifdef HAVE_TC_RAMCACHE
259 if (tagcache_fill_tags(&viewer
->tempid3
, filename
))
261 pid3
= &viewer
->tempid3
;
265 if (!audio_peek_track(&pid3
, i
-cur_pos
))
273 line
= pid3
? TRACK_HAS_INFO
: TRACK_HAS_NO_INFO
;
275 int j
= 0, cur_string
= 0;
276 unsigned int line_len
= 0;
278 while (j
< viewer
->lines
[line
].count
&& line_len
< sizeof(buf
))
280 const char *out
= NULL
;
281 token
.type
= viewer
->lines
[line
].tokens
[j
];
284 out
= get_id3_token(&token
, pid3
, tempbuf
, sizeof(tempbuf
), -1, NULL
);
287 out
= get_radio_token(&token
, i
-cur_pos
,
288 tempbuf
, sizeof(tempbuf
), -1, NULL
);
292 line_len
= strlcat(buf
, out
, sizeof(buf
));
297 switch (viewer
->lines
[line
].tokens
[j
])
299 case WPS_TOKEN_ALIGN_CENTER
:
300 case WPS_TOKEN_ALIGN_LEFT
:
301 case WPS_TOKEN_ALIGN_LEFT_RTL
:
302 case WPS_TOKEN_ALIGN_RIGHT
:
303 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
304 alignment
= viewer
->lines
[line
].tokens
[j
];
307 case WPS_TOKEN_STRING
:
308 case WPS_TOKEN_CHARACTER
:
309 snprintf(tempbuf
, sizeof(tempbuf
), "%s",
310 viewer
->lines
[line
].strings
[cur_string
]);
313 case WPS_TOKEN_PLAYLIST_POSITION
:
314 snprintf(tempbuf
, sizeof(tempbuf
), "%d", i
);
316 case WPS_TOKEN_FILE_NAME
:
317 get_dir(tempbuf
, sizeof(tempbuf
), filename
, 0);
319 case WPS_TOKEN_FILE_PATH
:
320 snprintf(tempbuf
, sizeof(tempbuf
), "%s", filename
);
328 line_len
= strlcat(buf
, tempbuf
, sizeof(buf
));
333 int vpwidth
= viewer
->vp
->width
;
334 length
= gwps
->display
->getstringsize(buf
, NULL
, NULL
);
335 if (viewer
->lines
[line
].scroll
&& length
>= vpwidth
)
337 gwps
->display
->puts_scroll(0, (i
-start_item
), buf
);
341 if (length
>= vpwidth
)
347 case WPS_TOKEN_ALIGN_CENTER
:
348 x
= (vpwidth
-length
)/2;
350 case WPS_TOKEN_ALIGN_LEFT_RTL
:
351 if (lang_is_rtl() && VP_IS_RTL(viewer
->vp
))
353 x
= vpwidth
- length
;
356 case WPS_TOKEN_ALIGN_LEFT
:
359 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
360 if (lang_is_rtl() && VP_IS_RTL(viewer
->vp
))
365 case WPS_TOKEN_ALIGN_RIGHT
:
366 x
= vpwidth
- length
;
373 gwps
->display
->putsxy(x
, (i
-start_item
)*line_height
, buf
);
379 /* clears the area where the image was shown */
380 static void clear_image_pos(struct gui_wps
*gwps
, struct gui_img
*img
)
384 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
385 gwps
->display
->fillrect(img
->x
, img
->y
, img
->bm
.width
, img
->subimage_height
);
386 gwps
->display
->set_drawmode(DRMODE_SOLID
);
389 static void wps_draw_image(struct gui_wps
*gwps
, struct gui_img
*img
, int subimage
)
391 struct screen
*display
= gwps
->display
;
392 if(img
->always_display
)
393 display
->set_drawmode(DRMODE_FG
);
395 display
->set_drawmode(DRMODE_SOLID
);
398 if(img
->bm
.format
== FORMAT_MONO
) {
400 display
->mono_bitmap_part(img
->bm
.data
,
401 0, img
->subimage_height
* subimage
,
402 img
->bm
.width
, img
->x
,
403 img
->y
, img
->bm
.width
,
404 img
->subimage_height
);
407 display
->transparent_bitmap_part((fb_data
*)img
->bm
.data
,
408 0, img
->subimage_height
* subimage
,
409 STRIDE(display
->screen_type
,
410 img
->bm
.width
, img
->bm
.height
),
411 img
->x
, img
->y
, img
->bm
.width
,
412 img
->subimage_height
);
417 static void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
419 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
422 struct wps_data
*data
= gwps
->data
;
423 struct screen
*display
= gwps
->display
;
424 struct skin_token_list
*list
= data
->images
;
428 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
431 if (img
->display
>= 0)
433 wps_draw_image(gwps
, img
, img
->display
);
435 else if (img
->always_display
&& img
->vp
== vp
)
437 wps_draw_image(gwps
, img
, 0);
443 /* now draw the AA */
444 if (data
->albumart
&& data
->albumart
->vp
== vp
445 && data
->albumart
->draw
)
447 int handle
= playback_current_aa_hid(data
->playback_aa_slot
);
449 if (in_radio_screen())
451 struct dim dim
= {data
->albumart
->width
, data
->albumart
->height
};
452 handle
= radio_get_art_hid(&dim
);
455 draw_album_art(gwps
, handle
, false);
456 data
->albumart
->draw
= false;
460 display
->set_drawmode(DRMODE_SOLID
);
463 #else /* HAVE_LCD_CHARCELL */
465 static bool draw_player_progress(struct gui_wps
*gwps
)
467 struct wps_state
*state
= gwps
->state
;
468 struct screen
*display
= gwps
->display
;
469 unsigned char progress_pattern
[7];
474 if (LIKELY(state
->id3
))
476 elapsed
= state
->id3
->elapsed
;
477 length
= state
->id3
->length
;
486 pos
= 36 * (elapsed
+ state
->ff_rewind_count
) / length
;
488 for (i
= 0; i
< 7; i
++, pos
-= 5)
491 progress_pattern
[i
] = 0x1fu
;
493 progress_pattern
[i
] = 0x00u
;
495 progress_pattern
[i
] = 0x1fu
>> pos
;
498 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
502 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
504 static const unsigned char numbers
[10][4] = {
505 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
506 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
507 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
508 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
509 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
510 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
511 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
512 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
513 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
514 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
517 struct wps_state
*state
= gwps
->state
;
518 struct screen
*display
= gwps
->display
;
519 struct wps_data
*data
= gwps
->data
;
520 unsigned char progress_pattern
[7];
530 if (LIKELY(state
->id3
))
532 elapsed
= state
->id3
->elapsed
;
533 length
= state
->id3
->length
;
541 if (buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
544 time
= elapsed
+ state
->ff_rewind_count
;
546 pos
= 55 * time
/ length
;
548 memset(timestr
, 0, sizeof(timestr
));
549 format_time(timestr
, sizeof(timestr
)-2, time
);
550 timestr
[strlen(timestr
)] = ':'; /* always safe */
552 for (i
= 0; i
< 11; i
++, pos
-= 5)
555 memset(progress_pattern
, 0, sizeof(progress_pattern
));
557 if ((digit
= timestr
[time_idx
]))
562 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
564 memcpy(progress_pattern
, numbers
[digit
], 4);
567 else /* tens, shifted right */
569 for (j
= 0; j
< 4; j
++)
570 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
572 if (time_idx
> 0) /* not the first group, add colon in front */
574 progress_pattern
[1] |= 0x10u
;
575 progress_pattern
[3] |= 0x10u
;
581 progress_pattern
[5] = progress_pattern
[6] = 0x1fu
;
584 if (pos
> 0 && pos
< 5)
587 progress_pattern
[5] = progress_pattern
[6] = (~0x1fu
>> pos
) & 0x1fu
;
590 if (softchar
&& pat_idx
< 8)
592 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
594 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
598 buf
= utf8encode(' ', buf
);
600 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
605 #endif /* HAVE_LCD_CHARCELL */
607 /* Return the index to the end token for the conditional token at index.
608 The conditional token can be either a start token or a separator
611 static int find_conditional_end(struct wps_data
*data
, int index
)
614 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
615 ret
= data
->tokens
[ret
].value
.i
;
617 /* ret now is the index to the end token for the conditional. */
621 /* Evaluate the conditional that is at *token_index and return whether a skip
622 has ocurred. *token_index is updated with the new position.
624 static bool evaluate_conditional(struct gui_wps
*gwps
, int *token_index
)
629 struct wps_data
*data
= gwps
->data
;
632 int cond_index
= *token_index
;
635 unsigned char num_options
= data
->tokens
[cond_index
].value
.i
& 0xFF;
636 unsigned char prev_val
= (data
->tokens
[cond_index
].value
.i
& 0xFF00) >> 8;
638 /* treat ?xx<true> constructs as if they had 2 options. */
642 int intval
= num_options
;
643 /* get_token_value needs to know the number of options in the enum */
644 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
645 result
, sizeof(result
), &intval
);
647 /* intval is now the number of the enum option we want to read,
648 starting from 1. If intval is -1, we check if value is empty. */
650 intval
= (value
&& *value
) ? 1 : num_options
;
651 else if (intval
> num_options
|| intval
< 1)
652 intval
= num_options
;
654 data
->tokens
[cond_index
].value
.i
= (intval
<< 8) + num_options
;
656 /* skip to the appropriate enum case */
657 int next
= cond_index
+ 2;
658 for (i
= 1; i
< intval
; i
++)
660 next
= data
->tokens
[next
].value
.i
;
664 if (prev_val
== intval
)
666 /* Same conditional case as previously. Return without clearing the
671 cond_end
= find_conditional_end(data
, cond_index
+ 2);
672 for (i
= cond_index
+ 3; i
< cond_end
; i
++)
674 #ifdef HAVE_LCD_BITMAP
675 /* clear all pictures in the conditional and nested ones */
676 if (data
->tokens
[i
].type
== WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
)
677 clear_image_pos(gwps
, find_image(data
->tokens
[i
].value
.i
&0xFF, data
));
678 else if (data
->tokens
[i
].type
== WPS_TOKEN_VOLUMEBAR
||
679 data
->tokens
[i
].type
== WPS_TOKEN_PROGRESSBAR
||
680 data
->tokens
[i
].type
== WPS_TOKEN_BATTERY_PERCENTBAR
)
682 struct progressbar
*bar
= (struct progressbar
*)data
->tokens
[i
].value
.data
;
685 else if (data
->tokens
[i
].type
== WPS_TOKEN_PEAKMETER
)
687 data
->peak_meter_enabled
= false;
691 if (data
->albumart
&& data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
694 playback_current_aa_hid(data
->playback_aa_slot
), true);
695 data
->albumart
->draw
= false;
704 /* Read a (sub)line to the given alignment format buffer.
705 linebuf is the buffer where the data is actually stored.
706 align is the alignment format that'll be used to display the text.
707 The return value indicates whether the line needs to be updated.
709 static bool get_line(struct gui_wps
*gwps
,
710 struct skin_subline
*subline
,
711 struct align_pos
*align
,
714 unsigned refresh_mode
)
716 struct wps_data
*data
= gwps
->data
;
719 char *buf
= linebuf
; /* will always point to the writing position */
720 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
723 (void)refresh_mode
; /* silence warning on charcell */
725 /* alignment-related variables */
727 char* cur_align_start
;
728 cur_align_start
= buf
;
729 cur_align
= WPS_ALIGN_LEFT
;
731 align
->center
= NULL
;
733 /* Process all tokens of the desired subline */
734 for (i
= subline
->first_token_idx
;
735 i
<= subline
->last_token_idx
; i
++)
737 switch(data
->tokens
[i
].type
)
739 case WPS_TOKEN_CONDITIONAL
:
740 /* place ourselves in the right conditional case */
741 update
|= evaluate_conditional(gwps
, &i
);
744 case WPS_TOKEN_CONDITIONAL_OPTION
:
745 /* we've finished in the curent conditional case,
746 skip to the end of the conditional structure */
747 i
= find_conditional_end(data
, i
);
750 #ifdef HAVE_LCD_BITMAP
751 case WPS_TOKEN_PEAKMETER
:
752 data
->peak_meter_enabled
= true;
754 case WPS_TOKEN_VOLUMEBAR
:
755 case WPS_TOKEN_BATTERY_PERCENTBAR
:
756 case WPS_TOKEN_PROGRESSBAR
:
758 struct progressbar
*bar
= (struct progressbar
*)data
->tokens
[i
].value
.data
;
762 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
764 char n
= data
->tokens
[i
].value
.i
& 0xFF;
765 int subimage
= data
->tokens
[i
].value
.i
>> 8;
766 struct gui_img
*img
= find_image(n
, data
);
768 if (img
&& img
->loaded
)
769 img
->display
= subimage
;
772 case WPS_TOKEN_DRAW_INBUILTBAR
:
773 gui_statusbar_draw(&(statusbars
.statusbars
[gwps
->display
->screen_type
]),
774 refresh_mode
== WPS_REFRESH_ALL
,
775 data
->tokens
[i
].value
.data
);
779 case WPS_TOKEN_ALIGN_LEFT
:
780 case WPS_TOKEN_ALIGN_LEFT_RTL
:
781 case WPS_TOKEN_ALIGN_CENTER
:
782 case WPS_TOKEN_ALIGN_RIGHT
:
783 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
784 /* remember where the current aligned text started */
788 align
->left
= cur_align_start
;
791 case WPS_ALIGN_CENTER
:
792 align
->center
= cur_align_start
;
795 case WPS_ALIGN_RIGHT
:
796 align
->right
= cur_align_start
;
799 /* start a new alignment */
800 switch (data
->tokens
[i
].type
)
802 case WPS_TOKEN_ALIGN_LEFT
:
803 cur_align
= WPS_ALIGN_LEFT
;
805 case WPS_TOKEN_ALIGN_LEFT_RTL
:
806 cur_align
= lang_is_rtl() ? WPS_ALIGN_RIGHT
:
809 case WPS_TOKEN_ALIGN_CENTER
:
810 cur_align
= WPS_ALIGN_CENTER
;
812 case WPS_TOKEN_ALIGN_RIGHT
:
813 cur_align
= WPS_ALIGN_RIGHT
;
815 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
816 cur_align
= lang_is_rtl() ? WPS_ALIGN_LEFT
:
823 cur_align_start
= buf
;
825 case WPS_VIEWPORT_ENABLE
:
827 char label
= data
->tokens
[i
].value
.i
;
828 char temp
= VP_DRAW_HIDEABLE
;
829 /* viewports are allowed to share id's so find and enable
831 struct skin_token_list
*list
= data
->viewports
;
834 struct skin_viewport
*vp
=
835 (struct skin_viewport
*)list
->token
->value
.data
;
836 if (vp
->label
== label
)
838 if (vp
->hidden_flags
&VP_DRAW_WASHIDDEN
)
839 temp
|= VP_DRAW_WASHIDDEN
;
840 vp
->hidden_flags
= temp
;
846 #ifdef HAVE_LCD_BITMAP
847 case WPS_TOKEN_UIVIEWPORT_ENABLE
:
848 sb_set_info_vp(gwps
->display
->screen_type
,
849 data
->tokens
[i
].value
.i
|VP_INFO_LABEL
);
851 case WPS_VIEWPORT_CUSTOMLIST
:
852 draw_playlist_viewer_list(gwps
, data
->tokens
[i
].value
.data
);
857 /* get the value of the tag and copy it to the buffer */
858 const char *value
= get_token_value(gwps
, &data
->tokens
[i
],
859 temp_buf
, sizeof(temp_buf
), NULL
);
863 while (*value
&& (buf
< linebuf_end
))
871 /* close the current alignment */
875 align
->left
= cur_align_start
;
878 case WPS_ALIGN_CENTER
:
879 align
->center
= cur_align_start
;
882 case WPS_ALIGN_RIGHT
:
883 align
->right
= cur_align_start
;
889 static void get_subline_timeout(struct gui_wps
*gwps
, struct skin_subline
*subline
)
891 struct wps_data
*data
= gwps
->data
;
893 subline
->time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
895 for (i
= subline
->first_token_idx
;
896 i
<= subline
->last_token_idx
; i
++)
898 switch(data
->tokens
[i
].type
)
900 case WPS_TOKEN_CONDITIONAL
:
901 /* place ourselves in the right conditional case */
902 evaluate_conditional(gwps
, &i
);
905 case WPS_TOKEN_CONDITIONAL_OPTION
:
906 /* we've finished in the curent conditional case,
907 skip to the end of the conditional structure */
908 i
= find_conditional_end(data
, i
);
911 case WPS_TOKEN_SUBLINE_TIMEOUT
:
912 subline
->time_mult
= data
->tokens
[i
].value
.i
;
921 /* Calculates which subline should be displayed for the specified line
922 Returns true iff the subline must be refreshed */
923 static bool update_curr_subline(struct gui_wps
*gwps
, struct skin_line
*line
)
925 /* shortcut this whole thing if we need to reset the line completly */
926 if (line
->curr_subline
== NULL
)
928 line
->subline_expire_time
= current_tick
;
929 line
->curr_subline
= &line
->sublines
;
930 if (!line
->curr_subline
->next
)
932 line
->subline_expire_time
+= 100*HZ
;
936 get_subline_timeout(gwps
, line
->curr_subline
);
937 line
->subline_expire_time
+= TIMEOUT_UNIT
*line
->curr_subline
->time_mult
;
941 /* if time to advance to next sub-line */
942 if (TIME_AFTER(current_tick
, line
->subline_expire_time
- 1))
944 /* if there is only one subline, there is no need to search for a new one */
945 if (&line
->sublines
== line
->curr_subline
&&
946 line
->curr_subline
->next
== NULL
)
948 line
->subline_expire_time
+= 100 * HZ
;
951 if (line
->curr_subline
->next
)
952 line
->curr_subline
= line
->curr_subline
->next
;
954 line
->curr_subline
= &line
->sublines
;
955 get_subline_timeout(gwps
, line
->curr_subline
);
956 line
->subline_expire_time
= current_tick
+ TIMEOUT_UNIT
*line
->curr_subline
->time_mult
;
962 /* Display a line appropriately according to its alignment format.
963 format_align contains the text, separated between left, center and right.
964 line is the index of the line on the screen.
965 scroll indicates whether the line is a scrolling one or not.
967 static void write_line(struct screen
*display
,
968 struct align_pos
*format_align
,
972 int left_width
= 0, left_xpos
;
973 int center_width
= 0, center_xpos
;
974 int right_width
= 0, right_xpos
;
980 /* calculate different string sizes and positions */
981 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
982 if (format_align
->left
!= 0) {
983 display
->getstringsize((unsigned char *)format_align
->left
,
984 &left_width
, &string_height
);
987 if (format_align
->right
!= 0) {
988 display
->getstringsize((unsigned char *)format_align
->right
,
989 &right_width
, &string_height
);
992 if (format_align
->center
!= 0) {
993 display
->getstringsize((unsigned char *)format_align
->center
,
994 ¢er_width
, &string_height
);
998 right_xpos
= (display
->getwidth() - right_width
);
999 center_xpos
= (display
->getwidth() + left_xpos
- center_width
) / 2;
1001 scroll_width
= display
->getwidth() - left_xpos
;
1003 /* Checks for overlapping strings.
1004 If needed the overlapping strings will be merged, separated by a
1007 /* CASE 1: left and centered string overlap */
1008 /* there is a left string, need to merge left and center */
1009 if ((left_width
!= 0 && center_width
!= 0) &&
1010 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
1011 /* replace the former separator '\0' of left and
1012 center string with a space */
1013 *(--format_align
->center
) = ' ';
1014 /* calculate the new width and position of the merged string */
1015 left_width
= left_width
+ space_width
+ center_width
;
1016 /* there is no centered string anymore */
1019 /* there is no left string, move center to left */
1020 if ((left_width
== 0 && center_width
!= 0) &&
1021 (left_xpos
+ left_width
> center_xpos
)) {
1022 /* move the center string to the left string */
1023 format_align
->left
= format_align
->center
;
1024 /* calculate the new width and position of the string */
1025 left_width
= center_width
;
1026 /* there is no centered string anymore */
1030 /* CASE 2: centered and right string overlap */
1031 /* there is a right string, need to merge center and right */
1032 if ((center_width
!= 0 && right_width
!= 0) &&
1033 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
1034 /* replace the former separator '\0' of center and
1035 right string with a space */
1036 *(--format_align
->right
) = ' ';
1037 /* move the center string to the right after merge */
1038 format_align
->right
= format_align
->center
;
1039 /* calculate the new width and position of the merged string */
1040 right_width
= center_width
+ space_width
+ right_width
;
1041 right_xpos
= (display
->getwidth() - right_width
);
1042 /* there is no centered string anymore */
1045 /* there is no right string, move center to right */
1046 if ((center_width
!= 0 && right_width
== 0) &&
1047 (center_xpos
+ center_width
> right_xpos
)) {
1048 /* move the center string to the right string */
1049 format_align
->right
= format_align
->center
;
1050 /* calculate the new width and position of the string */
1051 right_width
= center_width
;
1052 right_xpos
= (display
->getwidth() - right_width
);
1053 /* there is no centered string anymore */
1057 /* CASE 3: left and right overlap
1058 There is no center string anymore, either there never
1059 was one or it has been merged in case 1 or 2 */
1060 /* there is a left string, need to merge left and right */
1061 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
1062 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
1063 /* replace the former separator '\0' of left and
1064 right string with a space */
1065 *(--format_align
->right
) = ' ';
1066 /* calculate the new width and position of the string */
1067 left_width
= left_width
+ space_width
+ right_width
;
1068 /* there is no right string anymore */
1071 /* there is no left string, move right to left */
1072 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
1073 (left_width
> right_xpos
)) {
1074 /* move the right string to the left string */
1075 format_align
->left
= format_align
->right
;
1076 /* calculate the new width and position of the string */
1077 left_width
= right_width
;
1078 /* there is no right string anymore */
1082 ypos
= (line
* string_height
);
1085 if (scroll
&& ((left_width
> scroll_width
) ||
1086 (center_width
> scroll_width
) ||
1087 (right_width
> scroll_width
)))
1089 display
->puts_scroll(0, line
,
1090 (unsigned char *)format_align
->left
);
1094 #ifdef HAVE_LCD_BITMAP
1095 /* clear the line first */
1096 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1097 display
->fillrect(left_xpos
, ypos
, display
->getwidth(), string_height
);
1098 display
->set_drawmode(DRMODE_SOLID
);
1101 /* Nasty hack: we output an empty scrolling string,
1102 which will reset the scroller for that line */
1103 display
->puts_scroll(0, line
, (unsigned char *)"");
1105 /* print aligned strings */
1106 if (left_width
!= 0)
1108 display
->putsxy(left_xpos
, ypos
,
1109 (unsigned char *)format_align
->left
);
1111 if (center_width
!= 0)
1113 display
->putsxy(center_xpos
, ypos
,
1114 (unsigned char *)format_align
->center
);
1116 if (right_width
!= 0)
1118 display
->putsxy(right_xpos
, ypos
,
1119 (unsigned char *)format_align
->right
);
1124 static bool skin_redraw(struct gui_wps
*gwps
, unsigned refresh_mode
)
1126 struct wps_data
*data
= gwps
->data
;
1127 struct screen
*display
= gwps
->display
;
1129 if (!data
|| !display
|| !gwps
->state
)
1133 char linebuf
[MAX_PATH
];
1135 struct align_pos align
;
1137 align
.center
= NULL
;
1141 struct skin_token_list
*viewport_list
;
1143 bool update_line
, new_subline_refresh
;
1145 /* reset to first subline if refresh all flag is set */
1146 if (refresh_mode
== WPS_REFRESH_ALL
)
1148 struct skin_line
*line
;
1149 struct skin_viewport
*skin_viewport
= find_viewport(VP_DEFAULT_LABEL
, data
);
1151 if (!(skin_viewport
->hidden_flags
& VP_NEVER_VISIBLE
))
1153 display
->set_viewport(&skin_viewport
->vp
);
1154 display
->clear_viewport();
1157 for (viewport_list
= data
->viewports
;
1158 viewport_list
; viewport_list
= viewport_list
->next
)
1161 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1162 for(line
= skin_viewport
->lines
; line
; line
= line
->next
)
1164 line
->curr_subline
= NULL
;
1169 #ifdef HAVE_LCD_CHARCELLS
1171 for (i
= 0; i
< 8; i
++)
1173 if (data
->wps_progress_pat
[i
] == 0)
1174 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1178 /* disable any viewports which are conditionally displayed.
1179 * If we are only refreshing the peak meter then don't change the viewport
1180 * enabled flags as this will stop scrolling. viewports cant be
1181 * toggled in this refresh mode anyway (FS#10215)*/
1182 if (refresh_mode
!= WPS_REFRESH_PEAK_METER
)
1184 for (viewport_list
= data
->viewports
;
1185 viewport_list
; viewport_list
= viewport_list
->next
)
1187 struct skin_viewport
*skin_viewport
=
1188 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1189 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
1193 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDEABLE
)
1195 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
)
1196 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1198 skin_viewport
->hidden_flags
|= VP_DRAW_HIDDEN
;
1202 int viewport_count
= 0;
1203 for (viewport_list
= data
->viewports
;
1204 viewport_list
; viewport_list
= viewport_list
->next
, viewport_count
++)
1206 struct skin_viewport
*skin_viewport
=
1207 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1208 unsigned vp_refresh_mode
= refresh_mode
;
1210 display
->set_viewport(&skin_viewport
->vp
);
1214 #ifdef HAVE_LCD_BITMAP
1215 /* Set images to not to be displayed */
1216 struct skin_token_list
*imglist
= data
->images
;
1219 struct gui_img
*img
= (struct gui_img
*)imglist
->token
->value
.data
;
1221 imglist
= imglist
->next
;
1224 /* dont redraw the viewport if its disabled */
1225 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
1226 { /* don't draw anything into this one */
1227 vp_refresh_mode
= 0; hidden_vp
= true;
1229 else if ((skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
))
1231 if (!(skin_viewport
->hidden_flags
&VP_DRAW_WASHIDDEN
))
1232 display
->scroll_stop(&skin_viewport
->vp
);
1233 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1236 else if (((skin_viewport
->hidden_flags
&
1237 (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
))
1238 == (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
)))
1240 vp_refresh_mode
= WPS_REFRESH_ALL
;
1241 skin_viewport
->hidden_flags
= VP_DRAW_HIDEABLE
;
1244 if (vp_refresh_mode
== WPS_REFRESH_ALL
)
1246 display
->clear_viewport();
1249 /* loop over the lines for this viewport */
1250 struct skin_line
*line
;
1253 for (line
= skin_viewport
->lines
; line
; line
= line
->next
, line_count
++)
1255 struct skin_subline
*subline
;
1256 memset(linebuf
, 0, sizeof(linebuf
));
1257 update_line
= false;
1259 /* get current subline for the line */
1260 new_subline_refresh
= update_curr_subline(gwps
, line
);
1261 subline
= line
->curr_subline
;
1262 flags
= line
->curr_subline
->line_type
;
1264 if (vp_refresh_mode
== WPS_REFRESH_ALL
|| (flags
& vp_refresh_mode
)
1265 || new_subline_refresh
|| hidden_vp
)
1267 /* get_line tells us if we need to update the line */
1268 update_line
= get_line(gwps
, subline
, &align
,
1269 linebuf
, sizeof(linebuf
), vp_refresh_mode
);
1271 #ifdef HAVE_LCD_BITMAP
1273 if (flags
& vp_refresh_mode
& WPS_REFRESH_PEAK_METER
)
1275 if (!data
->peak_meter_enabled
)
1277 peak_meter_enable(false);
1281 /* the peakmeter should be alone on its line */
1282 update_line
= false;
1284 int h
= font_get(skin_viewport
->vp
.font
)->height
;
1285 int peak_meter_y
= line_count
* h
;
1287 /* The user might decide to have the peak meter in the last
1288 line so that it is only displayed if no status bar is
1289 visible. If so we neither want do draw nor enable the
1291 if (peak_meter_y
+ h
<= skin_viewport
->vp
.y
+skin_viewport
->vp
.height
) {
1292 peak_meter_enable(true);
1293 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
1294 MIN(h
, skin_viewport
->vp
.y
+skin_viewport
->vp
.height
- peak_meter_y
));
1299 #else /* HAVE_LCD_CHARCELL */
1302 if (flags
& vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1304 if (data
->full_line_progressbar
)
1305 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
1307 draw_player_progress(gwps
);
1311 if (update_line
&& !hidden_vp
&&
1312 /* conditionals clear the line which means if the %Vd is put into the default
1313 viewport there will be a blank line.
1314 To get around this we dont allow any actual drawing to happen in the
1315 deault vp if other vp's are defined */
1316 ((skin_viewport
->label
!= VP_DEFAULT_LABEL
&& viewport_list
->next
) ||
1317 !viewport_list
->next
))
1319 if (flags
& WPS_REFRESH_SCROLL
)
1321 /* if the line is a scrolling one we don't want to update
1322 too often, so that it has the time to scroll */
1323 if ((vp_refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
1324 write_line(display
, &align
, line_count
, true);
1327 write_line(display
, &align
, line_count
, false);
1330 #ifdef HAVE_LCD_BITMAP
1332 if (vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1334 struct skin_token_list
*bar
= gwps
->data
->progressbars
;
1337 struct progressbar
*thisbar
= (struct progressbar
*)bar
->token
->value
.data
;
1338 if (thisbar
->vp
== &skin_viewport
->vp
&& thisbar
->draw
)
1340 draw_progressbar(gwps
, thisbar
);
1345 /* Now display any images in this viewport */
1347 wps_display_images(gwps
, &skin_viewport
->vp
);
1351 /* Restore the default viewport */
1352 display
->set_viewport(NULL
);
1359 bool skin_has_sbs(enum screen_type screen
, struct wps_data
*data
)
1364 #ifdef HAVE_LCD_BITMAP
1365 if (data
->wps_sb_tag
)
1366 draw
= data
->show_sb_on_wps
;
1367 else if (statusbar_position(screen
) != STATUSBAR_OFF
)
1373 /* do the button loop as often as required for the peak meters to update
1374 * with a good refresh rate.
1375 * gwps is really gwps[NB_SCREENS]! don't wrap this if FOR_NB_SCREENS()
1377 int skin_wait_for_action(struct gui_wps
*gwps
, int context
, int timeout
)
1379 (void)gwps
; /* silence charcell warning */
1380 int button
= ACTION_NONE
;
1381 #ifdef HAVE_LCD_BITMAP
1383 /* when the peak meter is enabled we want to have a
1384 few extra updates to make it look smooth. On the
1385 other hand we don't want to waste energy if it
1390 if(gwps
[i
].data
->peak_meter_enabled
)
1395 long next_refresh
= current_tick
;
1396 long next_big_refresh
= current_tick
+ timeout
;
1397 button
= BUTTON_NONE
;
1398 while (TIME_BEFORE(current_tick
, next_big_refresh
)) {
1399 button
= get_action(context
,TIMEOUT_NOBLOCK
);
1400 if (button
!= ACTION_NONE
) {
1404 sleep(0); /* Sleep until end of current tick. */
1406 if (TIME_AFTER(current_tick
, next_refresh
)) {
1409 if(gwps
[i
].data
->peak_meter_enabled
)
1410 skin_update(&gwps
[i
], WPS_REFRESH_PEAK_METER
);
1411 next_refresh
+= HZ
/ PEAK_METER_FPS
;
1418 /* The peak meter is disabled
1419 -> no additional screen updates needed */
1423 button
= get_action(context
, timeout
);