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
63 #include "root_menu.h"
66 #include "wps_internals.h"
67 #include "skin_engine.h"
68 #include "statusbar-skinned.h"
70 static bool skin_redraw(struct gui_wps
*gwps
, unsigned refresh_mode
);
72 /* update a skinned screen, update_type is WPS_REFRESH_* values.
73 * Usually it should only be WPS_REFRESH_NON_STATIC
74 * A full update will be done if required (state.do_full_update == true)
76 bool skin_update(struct gui_wps
*gwps
, unsigned int update_type
)
79 /* This maybe shouldnt be here, but while the skin is only used to
80 * display the music screen this is better than whereever we are being
81 * called from. This is also safe for skined screen which dont use the id3 */
82 struct mp3entry
*id3
= gwps
->state
->id3
;
83 bool cuesheet_update
= (id3
!= NULL
? cuesheet_subtrack_changed(id3
) : false);
84 gwps
->sync_data
->do_full_update
|= cuesheet_update
;
86 retval
= skin_redraw(gwps
, gwps
->sync_data
->do_full_update
?
87 WPS_REFRESH_ALL
: update_type
);
91 #ifdef HAVE_LCD_BITMAP
93 void skin_statusbar_changed(struct gui_wps
*skin
)
97 struct wps_data
*data
= skin
->data
;
98 const struct screen
*display
= skin
->display
;
99 const int screen
= display
->screen_type
;
101 struct viewport
*vp
= &find_viewport(VP_DEFAULT_LABEL
, data
)->vp
;
102 viewport_set_defaults(vp
, screen
);
104 if (data
->wps_sb_tag
)
105 { /* fix up the default viewport */
106 if (data
->show_sb_on_wps
)
108 if (statusbar_position(screen
) != STATUSBAR_OFF
)
109 return; /* vp is fixed already */
111 vp
->y
= STATUSBAR_HEIGHT
;
112 vp
->height
= display
->lcdheight
- STATUSBAR_HEIGHT
;
116 if (statusbar_position(screen
) == STATUSBAR_OFF
)
117 return; /* vp is fixed already */
119 vp
->height
= display
->lcdheight
;
120 vp
->width
= display
->lcdwidth
;
125 static void draw_progressbar(struct gui_wps
*gwps
,
126 struct progressbar
*pb
)
128 struct screen
*display
= gwps
->display
;
129 struct viewport
*vp
= pb
->vp
;
130 struct wps_state
*state
= gwps
->state
;
131 struct mp3entry
*id3
= state
->id3
;
132 int y
= pb
->y
, height
= pb
->height
;
133 unsigned long length
, elapsed
;
136 height
= font_get(vp
->font
)->height
;
140 int line_height
= font_get(vp
->font
)->height
;
141 /* center the pb in the line, but only if the line is higher than the pb */
142 int center
= (line_height
-height
)/2;
143 /* if Y was not set calculate by font height,Y is -line_number-1 */
144 y
= (-y
-1)*line_height
+ (0 > center
? 0 : center
);
147 if (pb
->type
== WPS_TOKEN_VOLUMEBAR
)
149 int minvol
= sound_min(SOUND_VOLUME
);
150 int maxvol
= sound_max(SOUND_VOLUME
);
151 length
= maxvol
-minvol
;
152 elapsed
= global_settings
.volume
-minvol
;
154 else if (pb
->type
== WPS_TOKEN_BATTERY_PERCENTBAR
)
157 elapsed
= battery_level();
159 else if (id3
&& id3
->length
)
161 length
= id3
->length
;
162 elapsed
= id3
->elapsed
+ state
->ff_rewind_count
;
165 else if (in_radio_screen())
167 int min
= fm_region_data
[global_settings
.fm_region
].freq_min
;
168 elapsed
= radio_current_frequency() - min
;
169 length
= fm_region_data
[global_settings
.fm_region
].freq_max
- min
;
178 if (pb
->have_bitmap_pb
)
179 gui_bitmap_scrollbar_draw(display
, pb
->bm
,
180 pb
->x
, y
, pb
->width
, pb
->bm
.height
,
181 length
, 0, elapsed
, HORIZONTAL
);
183 gui_scrollbar_draw(display
, pb
->x
, y
, pb
->width
, height
,
184 length
, 0, elapsed
, HORIZONTAL
);
186 if (pb
->type
== WPS_TOKEN_PROGRESSBAR
&& id3
&& id3
->length
)
188 #ifdef AB_REPEAT_ENABLE
189 if (ab_repeat_mode_enabled())
190 ab_draw_markers(display
, id3
->length
,
191 pb
->x
, y
, pb
->width
, height
);
195 cue_draw_markers(display
, id3
->cuesheet
, id3
->length
,
196 pb
->x
, y
+1, pb
->width
, height
-2);
200 static void draw_playlist_viewer_list(struct gui_wps
*gwps
,
201 struct playlistviewer
*viewer
)
203 struct wps_state
*state
= gwps
->state
;
204 int lines
= viewport_get_nb_lines(viewer
->vp
);
205 int line_height
= font_get(viewer
->vp
->font
)->height
;
209 struct wps_token token
;
210 int x
, length
, alignment
= WPS_TOKEN_ALIGN_LEFT
;
212 struct mp3entry
*pid3
;
213 char buf
[MAX_PATH
*2], tempbuf
[MAX_PATH
];
214 const char *filename
;
216 if (current_screen() == GO_TO_FM
)
218 cur_pos
= radio_current_preset();
219 count
= radio_preset_count();
224 cur_pos
= playlist_get_display_index();
225 count
= playlist_amount()+1;
227 start_item
= MAX(0, cur_pos
+ viewer
->start_offset
);
229 gwps
->display
->set_viewport(viewer
->vp
);
230 for(i
=start_item
; (i
-start_item
)<lines
&& i
<count
; i
++)
234 if (current_screen() == GO_TO_FM
)
237 line
= TRACK_HAS_INFO
;
243 filename
= playlist_peek(i
-cur_pos
);
248 else if (i
== cur_pos
+1)
252 #if CONFIG_CODEC == SWCODEC
255 #ifdef HAVE_TC_RAMCACHE
256 if (tagcache_fill_tags(&viewer
->tempid3
, filename
))
258 pid3
= &viewer
->tempid3
;
262 if (!audio_peek_track(&pid3
, i
-cur_pos
))
270 line
= pid3
? TRACK_HAS_INFO
: TRACK_HAS_NO_INFO
;
272 int j
= 0, cur_string
= 0;
273 unsigned int line_len
= 0;
275 while (j
< viewer
->lines
[line
].count
&& line_len
< sizeof(buf
))
277 const char *out
= NULL
;
278 token
.type
= viewer
->lines
[line
].tokens
[j
];
281 out
= get_id3_token(&token
, pid3
, tempbuf
, sizeof(tempbuf
), -1, NULL
);
284 out
= get_radio_token(&token
, i
-cur_pos
,
285 tempbuf
, sizeof(tempbuf
), -1, NULL
);
289 line_len
= strlcat(buf
, out
, sizeof(buf
));
294 switch (viewer
->lines
[line
].tokens
[j
])
296 case WPS_TOKEN_ALIGN_CENTER
:
297 case WPS_TOKEN_ALIGN_LEFT
:
298 case WPS_TOKEN_ALIGN_LEFT_RTL
:
299 case WPS_TOKEN_ALIGN_RIGHT
:
300 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
301 alignment
= viewer
->lines
[line
].tokens
[j
];
304 case WPS_TOKEN_STRING
:
305 case WPS_TOKEN_CHARACTER
:
306 snprintf(tempbuf
, sizeof(tempbuf
), "%s",
307 viewer
->lines
[line
].strings
[cur_string
]);
310 case WPS_TOKEN_PLAYLIST_POSITION
:
311 snprintf(tempbuf
, sizeof(tempbuf
), "%d", i
);
313 case WPS_TOKEN_FILE_NAME
:
314 get_dir(tempbuf
, sizeof(tempbuf
), filename
, 0);
316 case WPS_TOKEN_FILE_PATH
:
317 snprintf(tempbuf
, sizeof(tempbuf
), "%s", filename
);
325 line_len
= strlcat(buf
, tempbuf
, sizeof(buf
));
330 int vpwidth
= viewer
->vp
->width
;
331 length
= gwps
->display
->getstringsize(buf
, NULL
, NULL
);
332 if (viewer
->lines
[line
].scroll
&& length
>= vpwidth
)
334 gwps
->display
->puts_scroll(0, (i
-start_item
), buf
);
338 if (length
>= vpwidth
)
344 case WPS_TOKEN_ALIGN_CENTER
:
345 x
= (vpwidth
-length
)/2;
347 case WPS_TOKEN_ALIGN_LEFT_RTL
:
348 if (lang_is_rtl() && VP_IS_RTL(viewer
->vp
))
350 x
= vpwidth
- length
;
353 case WPS_TOKEN_ALIGN_LEFT
:
356 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
357 if (lang_is_rtl() && VP_IS_RTL(viewer
->vp
))
362 case WPS_TOKEN_ALIGN_RIGHT
:
363 x
= vpwidth
- length
;
370 gwps
->display
->putsxy(x
, (i
-start_item
)*line_height
, buf
);
376 /* clears the area where the image was shown */
377 static void clear_image_pos(struct gui_wps
*gwps
, struct gui_img
*img
)
381 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
382 gwps
->display
->fillrect(img
->x
, img
->y
, img
->bm
.width
, img
->subimage_height
);
383 gwps
->display
->set_drawmode(DRMODE_SOLID
);
386 static void wps_draw_image(struct gui_wps
*gwps
, struct gui_img
*img
, int subimage
)
388 struct screen
*display
= gwps
->display
;
389 if(img
->always_display
)
390 display
->set_drawmode(DRMODE_FG
);
392 display
->set_drawmode(DRMODE_SOLID
);
395 if(img
->bm
.format
== FORMAT_MONO
) {
397 display
->mono_bitmap_part(img
->bm
.data
,
398 0, img
->subimage_height
* subimage
,
399 img
->bm
.width
, img
->x
,
400 img
->y
, img
->bm
.width
,
401 img
->subimage_height
);
404 display
->transparent_bitmap_part((fb_data
*)img
->bm
.data
,
405 0, img
->subimage_height
* subimage
,
406 STRIDE(display
->screen_type
,
407 img
->bm
.width
, img
->bm
.height
),
408 img
->x
, img
->y
, img
->bm
.width
,
409 img
->subimage_height
);
414 static void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
416 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
419 struct wps_data
*data
= gwps
->data
;
420 struct screen
*display
= gwps
->display
;
421 struct skin_token_list
*list
= data
->images
;
425 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
428 if (img
->display
>= 0)
430 wps_draw_image(gwps
, img
, img
->display
);
432 else if (img
->always_display
&& img
->vp
== vp
)
434 wps_draw_image(gwps
, img
, 0);
440 /* now draw the AA */
441 if (data
->albumart
&& data
->albumart
->vp
== vp
442 && data
->albumart
->draw
)
444 draw_album_art(gwps
, playback_current_aa_hid(data
->playback_aa_slot
),
446 data
->albumart
->draw
= false;
450 display
->set_drawmode(DRMODE_SOLID
);
453 #else /* HAVE_LCD_CHARCELL */
455 static bool draw_player_progress(struct gui_wps
*gwps
)
457 struct wps_state
*state
= gwps
->state
;
458 struct screen
*display
= gwps
->display
;
459 unsigned char progress_pattern
[7];
464 if (LIKELY(state
->id3
))
466 elapsed
= state
->id3
->elapsed
;
467 length
= state
->id3
->length
;
476 pos
= 36 * (elapsed
+ state
->ff_rewind_count
) / length
;
478 for (i
= 0; i
< 7; i
++, pos
-= 5)
481 progress_pattern
[i
] = 0x1fu
;
483 progress_pattern
[i
] = 0x00u
;
485 progress_pattern
[i
] = 0x1fu
>> pos
;
488 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
492 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
494 static const unsigned char numbers
[10][4] = {
495 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
496 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
497 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
498 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
499 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
500 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
501 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
502 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
503 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
504 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
507 struct wps_state
*state
= gwps
->state
;
508 struct screen
*display
= gwps
->display
;
509 struct wps_data
*data
= gwps
->data
;
510 unsigned char progress_pattern
[7];
520 if (LIKELY(state
->id3
))
522 elapsed
= state
->id3
->elapsed
;
523 length
= state
->id3
->length
;
531 if (buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
534 time
= elapsed
+ state
->ff_rewind_count
;
536 pos
= 55 * time
/ length
;
538 memset(timestr
, 0, sizeof(timestr
));
539 format_time(timestr
, sizeof(timestr
)-2, time
);
540 timestr
[strlen(timestr
)] = ':'; /* always safe */
542 for (i
= 0; i
< 11; i
++, pos
-= 5)
545 memset(progress_pattern
, 0, sizeof(progress_pattern
));
547 if ((digit
= timestr
[time_idx
]))
552 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
554 memcpy(progress_pattern
, numbers
[digit
], 4);
557 else /* tens, shifted right */
559 for (j
= 0; j
< 4; j
++)
560 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
562 if (time_idx
> 0) /* not the first group, add colon in front */
564 progress_pattern
[1] |= 0x10u
;
565 progress_pattern
[3] |= 0x10u
;
571 progress_pattern
[5] = progress_pattern
[6] = 0x1fu
;
574 if (pos
> 0 && pos
< 5)
577 progress_pattern
[5] = progress_pattern
[6] = (~0x1fu
>> pos
) & 0x1fu
;
580 if (softchar
&& pat_idx
< 8)
582 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
584 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
588 buf
= utf8encode(' ', buf
);
590 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
595 #endif /* HAVE_LCD_CHARCELL */
597 /* Return the index to the end token for the conditional token at index.
598 The conditional token can be either a start token or a separator
601 static int find_conditional_end(struct wps_data
*data
, int index
)
604 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
605 ret
= data
->tokens
[ret
].value
.i
;
607 /* ret now is the index to the end token for the conditional. */
611 /* Evaluate the conditional that is at *token_index and return whether a skip
612 has ocurred. *token_index is updated with the new position.
614 static bool evaluate_conditional(struct gui_wps
*gwps
, int *token_index
)
619 struct wps_data
*data
= gwps
->data
;
622 int cond_index
= *token_index
;
625 unsigned char num_options
= data
->tokens
[cond_index
].value
.i
& 0xFF;
626 unsigned char prev_val
= (data
->tokens
[cond_index
].value
.i
& 0xFF00) >> 8;
628 /* treat ?xx<true> constructs as if they had 2 options. */
632 int intval
= num_options
;
633 /* get_token_value needs to know the number of options in the enum */
634 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
635 result
, sizeof(result
), &intval
);
637 /* intval is now the number of the enum option we want to read,
638 starting from 1. If intval is -1, we check if value is empty. */
640 intval
= (value
&& *value
) ? 1 : num_options
;
641 else if (intval
> num_options
|| intval
< 1)
642 intval
= num_options
;
644 data
->tokens
[cond_index
].value
.i
= (intval
<< 8) + num_options
;
646 /* skip to the appropriate enum case */
647 int next
= cond_index
+ 2;
648 for (i
= 1; i
< intval
; i
++)
650 next
= data
->tokens
[next
].value
.i
;
654 if (prev_val
== intval
)
656 /* Same conditional case as previously. Return without clearing the
661 cond_end
= find_conditional_end(data
, cond_index
+ 2);
662 for (i
= cond_index
+ 3; i
< cond_end
; i
++)
664 #ifdef HAVE_LCD_BITMAP
665 /* clear all pictures in the conditional and nested ones */
666 if (data
->tokens
[i
].type
== WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
)
667 clear_image_pos(gwps
, find_image(data
->tokens
[i
].value
.i
&0xFF, data
));
668 else if (data
->tokens
[i
].type
== WPS_TOKEN_VOLUMEBAR
||
669 data
->tokens
[i
].type
== WPS_TOKEN_PROGRESSBAR
||
670 data
->tokens
[i
].type
== WPS_TOKEN_BATTERY_PERCENTBAR
)
672 struct progressbar
*bar
= (struct progressbar
*)data
->tokens
[i
].value
.data
;
675 else if (data
->tokens
[i
].type
== WPS_TOKEN_PEAKMETER
)
677 data
->peak_meter_enabled
= false;
681 if (data
->albumart
&& data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
684 playback_current_aa_hid(data
->playback_aa_slot
), true);
685 data
->albumart
->draw
= false;
694 /* Read a (sub)line to the given alignment format buffer.
695 linebuf is the buffer where the data is actually stored.
696 align is the alignment format that'll be used to display the text.
697 The return value indicates whether the line needs to be updated.
699 static bool get_line(struct gui_wps
*gwps
,
700 struct skin_subline
*subline
,
701 struct align_pos
*align
,
704 unsigned refresh_mode
)
706 struct wps_data
*data
= gwps
->data
;
709 char *buf
= linebuf
; /* will always point to the writing position */
710 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
713 (void)refresh_mode
; /* silence warning on charcell */
715 /* alignment-related variables */
717 char* cur_align_start
;
718 cur_align_start
= buf
;
719 cur_align
= WPS_ALIGN_LEFT
;
721 align
->center
= NULL
;
723 /* Process all tokens of the desired subline */
724 for (i
= subline
->first_token_idx
;
725 i
<= subline
->last_token_idx
; i
++)
727 switch(data
->tokens
[i
].type
)
729 case WPS_TOKEN_CONDITIONAL
:
730 /* place ourselves in the right conditional case */
731 update
|= evaluate_conditional(gwps
, &i
);
734 case WPS_TOKEN_CONDITIONAL_OPTION
:
735 /* we've finished in the curent conditional case,
736 skip to the end of the conditional structure */
737 i
= find_conditional_end(data
, i
);
740 #ifdef HAVE_LCD_BITMAP
741 case WPS_TOKEN_PEAKMETER
:
742 data
->peak_meter_enabled
= true;
744 case WPS_TOKEN_VOLUMEBAR
:
745 case WPS_TOKEN_BATTERY_PERCENTBAR
:
746 case WPS_TOKEN_PROGRESSBAR
:
748 struct progressbar
*bar
= (struct progressbar
*)data
->tokens
[i
].value
.data
;
752 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
754 char n
= data
->tokens
[i
].value
.i
& 0xFF;
755 int subimage
= data
->tokens
[i
].value
.i
>> 8;
756 struct gui_img
*img
= find_image(n
, data
);
758 if (img
&& img
->loaded
)
759 img
->display
= subimage
;
762 case WPS_TOKEN_DRAW_INBUILTBAR
:
763 gui_statusbar_draw(&(statusbars
.statusbars
[gwps
->display
->screen_type
]),
764 refresh_mode
== WPS_REFRESH_ALL
,
765 data
->tokens
[i
].value
.data
);
769 case WPS_TOKEN_ALIGN_LEFT
:
770 case WPS_TOKEN_ALIGN_LEFT_RTL
:
771 case WPS_TOKEN_ALIGN_CENTER
:
772 case WPS_TOKEN_ALIGN_RIGHT
:
773 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
774 /* remember where the current aligned text started */
778 align
->left
= cur_align_start
;
781 case WPS_ALIGN_CENTER
:
782 align
->center
= cur_align_start
;
785 case WPS_ALIGN_RIGHT
:
786 align
->right
= cur_align_start
;
789 /* start a new alignment */
790 switch (data
->tokens
[i
].type
)
792 case WPS_TOKEN_ALIGN_LEFT
:
793 cur_align
= WPS_ALIGN_LEFT
;
795 case WPS_TOKEN_ALIGN_LEFT_RTL
:
796 cur_align
= lang_is_rtl() ? WPS_ALIGN_RIGHT
:
799 case WPS_TOKEN_ALIGN_CENTER
:
800 cur_align
= WPS_ALIGN_CENTER
;
802 case WPS_TOKEN_ALIGN_RIGHT
:
803 cur_align
= WPS_ALIGN_RIGHT
;
805 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
806 cur_align
= lang_is_rtl() ? WPS_ALIGN_LEFT
:
813 cur_align_start
= buf
;
815 case WPS_VIEWPORT_ENABLE
:
817 char label
= data
->tokens
[i
].value
.i
;
818 char temp
= VP_DRAW_HIDEABLE
;
819 /* viewports are allowed to share id's so find and enable
821 struct skin_token_list
*list
= data
->viewports
;
824 struct skin_viewport
*vp
=
825 (struct skin_viewport
*)list
->token
->value
.data
;
826 if (vp
->label
== label
)
828 if (vp
->hidden_flags
&VP_DRAW_WASHIDDEN
)
829 temp
|= VP_DRAW_WASHIDDEN
;
830 vp
->hidden_flags
= temp
;
836 #ifdef HAVE_LCD_BITMAP
837 case WPS_TOKEN_UIVIEWPORT_ENABLE
:
838 sb_set_info_vp(gwps
->display
->screen_type
,
839 data
->tokens
[i
].value
.i
|VP_INFO_LABEL
);
841 case WPS_VIEWPORT_CUSTOMLIST
:
842 draw_playlist_viewer_list(gwps
, data
->tokens
[i
].value
.data
);
847 /* get the value of the tag and copy it to the buffer */
848 const char *value
= get_token_value(gwps
, &data
->tokens
[i
],
849 temp_buf
, sizeof(temp_buf
), NULL
);
853 while (*value
&& (buf
< linebuf_end
))
861 /* close the current alignment */
865 align
->left
= cur_align_start
;
868 case WPS_ALIGN_CENTER
:
869 align
->center
= cur_align_start
;
872 case WPS_ALIGN_RIGHT
:
873 align
->right
= cur_align_start
;
879 static void get_subline_timeout(struct gui_wps
*gwps
, struct skin_subline
*subline
)
881 struct wps_data
*data
= gwps
->data
;
883 subline
->time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
885 for (i
= subline
->first_token_idx
;
886 i
<= subline
->last_token_idx
; i
++)
888 switch(data
->tokens
[i
].type
)
890 case WPS_TOKEN_CONDITIONAL
:
891 /* place ourselves in the right conditional case */
892 evaluate_conditional(gwps
, &i
);
895 case WPS_TOKEN_CONDITIONAL_OPTION
:
896 /* we've finished in the curent conditional case,
897 skip to the end of the conditional structure */
898 i
= find_conditional_end(data
, i
);
901 case WPS_TOKEN_SUBLINE_TIMEOUT
:
902 subline
->time_mult
= data
->tokens
[i
].value
.i
;
911 /* Calculates which subline should be displayed for the specified line
912 Returns true iff the subline must be refreshed */
913 static bool update_curr_subline(struct gui_wps
*gwps
, struct skin_line
*line
)
915 /* shortcut this whole thing if we need to reset the line completly */
916 if (line
->curr_subline
== NULL
)
918 line
->subline_expire_time
= current_tick
;
919 line
->curr_subline
= &line
->sublines
;
920 if (!line
->curr_subline
->next
)
922 line
->subline_expire_time
+= 100*HZ
;
926 get_subline_timeout(gwps
, line
->curr_subline
);
927 line
->subline_expire_time
+= TIMEOUT_UNIT
*line
->curr_subline
->time_mult
;
931 /* if time to advance to next sub-line */
932 if (TIME_AFTER(current_tick
, line
->subline_expire_time
- 1))
934 /* if there is only one subline, there is no need to search for a new one */
935 if (&line
->sublines
== line
->curr_subline
&&
936 line
->curr_subline
->next
== NULL
)
938 line
->subline_expire_time
+= 100 * HZ
;
941 if (line
->curr_subline
->next
)
942 line
->curr_subline
= line
->curr_subline
->next
;
944 line
->curr_subline
= &line
->sublines
;
945 get_subline_timeout(gwps
, line
->curr_subline
);
946 line
->subline_expire_time
= current_tick
+ TIMEOUT_UNIT
*line
->curr_subline
->time_mult
;
952 /* Display a line appropriately according to its alignment format.
953 format_align contains the text, separated between left, center and right.
954 line is the index of the line on the screen.
955 scroll indicates whether the line is a scrolling one or not.
957 static void write_line(struct screen
*display
,
958 struct align_pos
*format_align
,
962 int left_width
= 0, left_xpos
;
963 int center_width
= 0, center_xpos
;
964 int right_width
= 0, right_xpos
;
970 /* calculate different string sizes and positions */
971 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
972 if (format_align
->left
!= 0) {
973 display
->getstringsize((unsigned char *)format_align
->left
,
974 &left_width
, &string_height
);
977 if (format_align
->right
!= 0) {
978 display
->getstringsize((unsigned char *)format_align
->right
,
979 &right_width
, &string_height
);
982 if (format_align
->center
!= 0) {
983 display
->getstringsize((unsigned char *)format_align
->center
,
984 ¢er_width
, &string_height
);
988 right_xpos
= (display
->getwidth() - right_width
);
989 center_xpos
= (display
->getwidth() + left_xpos
- center_width
) / 2;
991 scroll_width
= display
->getwidth() - left_xpos
;
993 /* Checks for overlapping strings.
994 If needed the overlapping strings will be merged, separated by a
997 /* CASE 1: left and centered string overlap */
998 /* there is a left string, need to merge left and center */
999 if ((left_width
!= 0 && center_width
!= 0) &&
1000 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
1001 /* replace the former separator '\0' of left and
1002 center string with a space */
1003 *(--format_align
->center
) = ' ';
1004 /* calculate the new width and position of the merged string */
1005 left_width
= left_width
+ space_width
+ center_width
;
1006 /* there is no centered string anymore */
1009 /* there is no left string, move center to left */
1010 if ((left_width
== 0 && center_width
!= 0) &&
1011 (left_xpos
+ left_width
> center_xpos
)) {
1012 /* move the center string to the left string */
1013 format_align
->left
= format_align
->center
;
1014 /* calculate the new width and position of the string */
1015 left_width
= center_width
;
1016 /* there is no centered string anymore */
1020 /* CASE 2: centered and right string overlap */
1021 /* there is a right string, need to merge center and right */
1022 if ((center_width
!= 0 && right_width
!= 0) &&
1023 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
1024 /* replace the former separator '\0' of center and
1025 right string with a space */
1026 *(--format_align
->right
) = ' ';
1027 /* move the center string to the right after merge */
1028 format_align
->right
= format_align
->center
;
1029 /* calculate the new width and position of the merged string */
1030 right_width
= center_width
+ space_width
+ right_width
;
1031 right_xpos
= (display
->getwidth() - right_width
);
1032 /* there is no centered string anymore */
1035 /* there is no right string, move center to right */
1036 if ((center_width
!= 0 && right_width
== 0) &&
1037 (center_xpos
+ center_width
> right_xpos
)) {
1038 /* move the center string to the right string */
1039 format_align
->right
= format_align
->center
;
1040 /* calculate the new width and position of the string */
1041 right_width
= center_width
;
1042 right_xpos
= (display
->getwidth() - right_width
);
1043 /* there is no centered string anymore */
1047 /* CASE 3: left and right overlap
1048 There is no center string anymore, either there never
1049 was one or it has been merged in case 1 or 2 */
1050 /* there is a left string, need to merge left and right */
1051 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
1052 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
1053 /* replace the former separator '\0' of left and
1054 right string with a space */
1055 *(--format_align
->right
) = ' ';
1056 /* calculate the new width and position of the string */
1057 left_width
= left_width
+ space_width
+ right_width
;
1058 /* there is no right string anymore */
1061 /* there is no left string, move right to left */
1062 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
1063 (left_width
> right_xpos
)) {
1064 /* move the right string to the left string */
1065 format_align
->left
= format_align
->right
;
1066 /* calculate the new width and position of the string */
1067 left_width
= right_width
;
1068 /* there is no right string anymore */
1072 ypos
= (line
* string_height
);
1075 if (scroll
&& ((left_width
> scroll_width
) ||
1076 (center_width
> scroll_width
) ||
1077 (right_width
> scroll_width
)))
1079 display
->puts_scroll(0, line
,
1080 (unsigned char *)format_align
->left
);
1084 #ifdef HAVE_LCD_BITMAP
1085 /* clear the line first */
1086 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1087 display
->fillrect(left_xpos
, ypos
, display
->getwidth(), string_height
);
1088 display
->set_drawmode(DRMODE_SOLID
);
1091 /* Nasty hack: we output an empty scrolling string,
1092 which will reset the scroller for that line */
1093 display
->puts_scroll(0, line
, (unsigned char *)"");
1095 /* print aligned strings */
1096 if (left_width
!= 0)
1098 display
->putsxy(left_xpos
, ypos
,
1099 (unsigned char *)format_align
->left
);
1101 if (center_width
!= 0)
1103 display
->putsxy(center_xpos
, ypos
,
1104 (unsigned char *)format_align
->center
);
1106 if (right_width
!= 0)
1108 display
->putsxy(right_xpos
, ypos
,
1109 (unsigned char *)format_align
->right
);
1114 static bool skin_redraw(struct gui_wps
*gwps
, unsigned refresh_mode
)
1116 struct wps_data
*data
= gwps
->data
;
1117 struct screen
*display
= gwps
->display
;
1119 if (!data
|| !display
|| !gwps
->state
)
1123 char linebuf
[MAX_PATH
];
1125 struct align_pos align
;
1127 align
.center
= NULL
;
1131 struct skin_token_list
*viewport_list
;
1133 bool update_line
, new_subline_refresh
;
1135 /* reset to first subline if refresh all flag is set */
1136 if (refresh_mode
== WPS_REFRESH_ALL
)
1138 struct skin_line
*line
;
1139 struct skin_viewport
*skin_viewport
= find_viewport(VP_DEFAULT_LABEL
, data
);
1141 if (!(skin_viewport
->hidden_flags
& VP_NEVER_VISIBLE
))
1143 display
->set_viewport(&skin_viewport
->vp
);
1144 display
->clear_viewport();
1147 for (viewport_list
= data
->viewports
;
1148 viewport_list
; viewport_list
= viewport_list
->next
)
1151 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1152 for(line
= skin_viewport
->lines
; line
; line
= line
->next
)
1154 line
->curr_subline
= NULL
;
1159 #ifdef HAVE_LCD_CHARCELLS
1161 for (i
= 0; i
< 8; i
++)
1163 if (data
->wps_progress_pat
[i
] == 0)
1164 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1168 /* disable any viewports which are conditionally displayed.
1169 * If we are only refreshing the peak meter then don't change the viewport
1170 * enabled flags as this will stop scrolling. viewports cant be
1171 * toggled in this refresh mode anyway (FS#10215)*/
1172 if (refresh_mode
!= WPS_REFRESH_PEAK_METER
)
1174 for (viewport_list
= data
->viewports
;
1175 viewport_list
; viewport_list
= viewport_list
->next
)
1177 struct skin_viewport
*skin_viewport
=
1178 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1179 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
1183 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDEABLE
)
1185 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
)
1186 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1188 skin_viewport
->hidden_flags
|= VP_DRAW_HIDDEN
;
1192 int viewport_count
= 0;
1193 for (viewport_list
= data
->viewports
;
1194 viewport_list
; viewport_list
= viewport_list
->next
, viewport_count
++)
1196 struct skin_viewport
*skin_viewport
=
1197 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1198 unsigned vp_refresh_mode
= refresh_mode
;
1200 display
->set_viewport(&skin_viewport
->vp
);
1204 #ifdef HAVE_LCD_BITMAP
1205 /* Set images to not to be displayed */
1206 struct skin_token_list
*imglist
= data
->images
;
1209 struct gui_img
*img
= (struct gui_img
*)imglist
->token
->value
.data
;
1211 imglist
= imglist
->next
;
1214 /* dont redraw the viewport if its disabled */
1215 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
1216 { /* don't draw anything into this one */
1217 vp_refresh_mode
= 0; hidden_vp
= true;
1219 else if ((skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
))
1221 if (!(skin_viewport
->hidden_flags
&VP_DRAW_WASHIDDEN
))
1222 display
->scroll_stop(&skin_viewport
->vp
);
1223 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1226 else if (((skin_viewport
->hidden_flags
&
1227 (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
))
1228 == (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
)))
1230 vp_refresh_mode
= WPS_REFRESH_ALL
;
1231 skin_viewport
->hidden_flags
= VP_DRAW_HIDEABLE
;
1234 if (vp_refresh_mode
== WPS_REFRESH_ALL
)
1236 display
->clear_viewport();
1239 /* loop over the lines for this viewport */
1240 struct skin_line
*line
;
1243 for (line
= skin_viewport
->lines
; line
; line
= line
->next
, line_count
++)
1245 struct skin_subline
*subline
;
1246 memset(linebuf
, 0, sizeof(linebuf
));
1247 update_line
= false;
1249 /* get current subline for the line */
1250 new_subline_refresh
= update_curr_subline(gwps
, line
);
1251 subline
= line
->curr_subline
;
1252 flags
= line
->curr_subline
->line_type
;
1254 if (vp_refresh_mode
== WPS_REFRESH_ALL
|| (flags
& vp_refresh_mode
)
1255 || new_subline_refresh
|| hidden_vp
)
1257 /* get_line tells us if we need to update the line */
1258 update_line
= get_line(gwps
, subline
, &align
,
1259 linebuf
, sizeof(linebuf
), vp_refresh_mode
);
1261 #ifdef HAVE_LCD_BITMAP
1263 if (flags
& vp_refresh_mode
& WPS_REFRESH_PEAK_METER
)
1265 if (!data
->peak_meter_enabled
)
1267 peak_meter_enable(false);
1271 /* the peakmeter should be alone on its line */
1272 update_line
= false;
1274 int h
= font_get(skin_viewport
->vp
.font
)->height
;
1275 int peak_meter_y
= line_count
* h
;
1277 /* The user might decide to have the peak meter in the last
1278 line so that it is only displayed if no status bar is
1279 visible. If so we neither want do draw nor enable the
1281 if (peak_meter_y
+ h
<= skin_viewport
->vp
.y
+skin_viewport
->vp
.height
) {
1282 peak_meter_enable(true);
1283 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
1284 MIN(h
, skin_viewport
->vp
.y
+skin_viewport
->vp
.height
- peak_meter_y
));
1289 #else /* HAVE_LCD_CHARCELL */
1292 if (flags
& vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1294 if (data
->full_line_progressbar
)
1295 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
1297 draw_player_progress(gwps
);
1301 if (update_line
&& !hidden_vp
&&
1302 /* conditionals clear the line which means if the %Vd is put into the default
1303 viewport there will be a blank line.
1304 To get around this we dont allow any actual drawing to happen in the
1305 deault vp if other vp's are defined */
1306 ((skin_viewport
->label
!= VP_DEFAULT_LABEL
&& viewport_list
->next
) ||
1307 !viewport_list
->next
))
1309 if (flags
& WPS_REFRESH_SCROLL
)
1311 /* if the line is a scrolling one we don't want to update
1312 too often, so that it has the time to scroll */
1313 if ((vp_refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
1314 write_line(display
, &align
, line_count
, true);
1317 write_line(display
, &align
, line_count
, false);
1320 #ifdef HAVE_LCD_BITMAP
1322 if (vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1324 struct skin_token_list
*bar
= gwps
->data
->progressbars
;
1327 struct progressbar
*thisbar
= (struct progressbar
*)bar
->token
->value
.data
;
1328 if (thisbar
->vp
== &skin_viewport
->vp
&& thisbar
->draw
)
1330 draw_progressbar(gwps
, thisbar
);
1335 /* Now display any images in this viewport */
1337 wps_display_images(gwps
, &skin_viewport
->vp
);
1341 /* Restore the default viewport */
1342 display
->set_viewport(NULL
);
1349 bool skin_has_sbs(enum screen_type screen
, struct wps_data
*data
)
1353 #ifdef HAVE_LCD_BITMAP
1354 if (data
->wps_sb_tag
)
1355 draw
= data
->show_sb_on_wps
;
1356 else if (statusbar_position(screen
) != STATUSBAR_OFF
)
1362 /* do the button loop as often as required for the peak meters to update
1363 * with a good refresh rate.
1364 * gwps is really gwps[NB_SCREENS]! don't wrap this if FOR_NB_SCREENS()
1366 int skin_wait_for_action(struct gui_wps
*gwps
, int context
, int timeout
)
1368 #ifdef HAVE_LCD_BITMAP
1370 int button
= ACTION_NONE
;
1371 /* when the peak meter is enabled we want to have a
1372 few extra updates to make it look smooth. On the
1373 other hand we don't want to waste energy if it
1378 if(gwps
[i
].data
->peak_meter_enabled
)
1383 long next_refresh
= current_tick
;
1384 long next_big_refresh
= current_tick
+ timeout
;
1385 button
= BUTTON_NONE
;
1386 while (TIME_BEFORE(current_tick
, next_big_refresh
)) {
1387 button
= get_action(context
,TIMEOUT_NOBLOCK
);
1388 if (button
!= ACTION_NONE
) {
1392 sleep(0); /* Sleep until end of current tick. */
1394 if (TIME_AFTER(current_tick
, next_refresh
)) {
1397 if(gwps
[i
].data
->peak_meter_enabled
)
1398 skin_update(&gwps
[i
], WPS_REFRESH_PEAK_METER
);
1399 next_refresh
+= HZ
/ PEAK_METER_FPS
;
1406 /* The peak meter is disabled
1407 -> no additional screen updates needed */
1411 button
= get_action(context
, timeout
);