1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002-2007 Björn Stenberg
11 * Copyright (C) 2007-2008 Nicolas Pennequin
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
28 #include "rbunicode.h"
35 #include "statusbar.h"
36 #include "scrollbar.h"
37 #include "screen_access.h"
41 #ifdef HAVE_LCD_BITMAP
42 #include "peakmeter.h"
51 #if CONFIG_CODEC == SWCODEC
58 #include "wps_internals.h"
59 #include "skin_engine.h"
60 #include "statusbar-skinned.h"
62 static bool skin_redraw(struct gui_wps
*gwps
, unsigned refresh_mode
);
64 /* update a skinned screen, update_type is WPS_REFRESH_* values.
65 * Usually it should only be WPS_REFRESH_NON_STATIC
66 * A full update will be done if required (state.do_full_update == true)
68 bool skin_update(struct gui_wps
*gwps
, unsigned int update_type
)
71 /* This maybe shouldnt be here, but while the skin is only used to
72 * display the music screen this is better than whereever we are being
73 * called from. This is also safe for skined screen which dont use the id3 */
74 struct mp3entry
*id3
= gwps
->state
->id3
;
75 bool cuesheet_update
= (id3
!= NULL
? cuesheet_subtrack_changed(id3
) : false);
76 gwps
->sync_data
->do_full_update
|= cuesheet_update
;
78 retval
= skin_redraw(gwps
, gwps
->sync_data
->do_full_update
?
79 WPS_REFRESH_ALL
: update_type
);
83 #ifdef HAVE_LCD_BITMAP
85 void skin_statusbar_changed(struct gui_wps
*skin
)
89 struct wps_data
*data
= skin
->data
;
90 const struct screen
*display
= skin
->display
;
91 const int screen
= display
->screen_type
;
93 struct viewport
*vp
= &find_viewport(VP_DEFAULT_LABEL
, data
)->vp
;
94 viewport_set_defaults(vp
, screen
);
97 { /* fix up the default viewport */
98 if (data
->show_sb_on_wps
)
100 if (statusbar_position(screen
) != STATUSBAR_OFF
)
101 return; /* vp is fixed already */
103 vp
->y
= STATUSBAR_HEIGHT
;
104 vp
->height
= display
->lcdheight
- STATUSBAR_HEIGHT
;
108 if (statusbar_position(screen
) == STATUSBAR_OFF
)
109 return; /* vp is fixed already */
111 vp
->height
= display
->lcdheight
;
112 vp
->width
= display
->lcdwidth
;
117 static void draw_progressbar(struct gui_wps
*gwps
,
118 struct skin_viewport
*wps_vp
)
120 struct screen
*display
= gwps
->display
;
121 struct wps_state
*state
= gwps
->state
;
122 struct progressbar
*pb
= wps_vp
->pb
;
123 struct mp3entry
*id3
= state
->id3
;
125 int height
= pb
->height
;
127 if (pb
->height
< 0 && !pb
->have_bitmap_pb
)
128 height
= font_get(wps_vp
->vp
.font
)->height
;
132 int line_height
= font_get(wps_vp
->vp
.font
)->height
;
133 /* center the pb in the line, but only if the line is higher than the pb */
134 int center
= (line_height
-pb
->height
)/2;
135 /* if Y was not set calculate by font height,Y is -line_number-1 */
136 y
= (-y
-1)*line_height
+ (0 > center
? 0 : center
);
142 elapsed
= id3
->elapsed
;
143 length
= id3
->length
;
151 if (pb
->have_bitmap_pb
)
152 gui_bitmap_scrollbar_draw(display
, pb
->bm
,
153 pb
->x
, y
, pb
->width
, pb
->bm
.height
,
154 length
? length
: 1, 0,
155 length
? elapsed
+ state
->ff_rewind_count
: 0,
158 gui_scrollbar_draw(display
, pb
->x
, y
, pb
->width
, height
,
159 length
? length
: 1, 0,
160 length
? elapsed
+ state
->ff_rewind_count
: 0,
162 #ifdef AB_REPEAT_ENABLE
163 if ( ab_repeat_mode_enabled() && length
!= 0 )
164 ab_draw_markers(display
, length
,
165 pb
->x
, pb
->x
+ pb
->width
, y
, height
);
168 if (id3
&& id3
->cuesheet
)
169 cue_draw_markers(display
, state
->id3
->cuesheet
, length
,
170 pb
->x
, pb
->x
+ pb
->width
, y
+1, height
-2);
172 bool audio_peek_track(struct mp3entry
* id3
, int offset
);
173 static void draw_playlist_viewer_list(struct gui_wps
*gwps
,
174 struct playlistviewer
*viewer
)
176 struct wps_state
*state
= gwps
->state
;
177 int lines
= viewport_get_nb_lines(viewer
->vp
);
178 int line_height
= font_get(viewer
->vp
->font
)->height
;
179 int cur_playlist_pos
= playlist_get_display_index();
180 int start_item
= MAX(0, cur_playlist_pos
+ viewer
->start_offset
);
182 struct wps_token token
;
183 int x
, length
, alignment
= WPS_TOKEN_ALIGN_LEFT
;
185 struct mp3entry
*pid3
;
186 #if CONFIG_CODEC == SWCODEC
189 char buf
[MAX_PATH
*2], tempbuf
[MAX_PATH
];
190 unsigned int buf_used
= 0;
193 gwps
->display
->set_viewport(viewer
->vp
);
194 for(i
=start_item
; (i
-start_item
)<lines
&& i
<playlist_amount(); i
++)
196 if (i
== cur_playlist_pos
)
200 else if (i
== cur_playlist_pos
+1)
204 #if CONFIG_CODEC == SWCODEC
205 else if ((i
>cur_playlist_pos
) && audio_peek_track(&id3
, i
-cur_playlist_pos
))
215 int line
= pid3
? TRACK_HAS_INFO
: TRACK_HAS_NO_INFO
;
216 int j
= 0, cur_string
= 0;
217 char *filename
= playlist_peek(i
-cur_playlist_pos
);
220 while (j
< viewer
->lines
[line
].count
&& (buf_used
<sizeof(buf
)))
222 const char *out
= NULL
;
223 token
.type
= viewer
->lines
[line
].tokens
[j
];
226 out
= get_id3_token(&token
, pid3
, tempbuf
, sizeof(tempbuf
), -1, NULL
);
229 snprintf(&buf
[buf_used
], sizeof(buf
)-buf_used
, "%s", out
);
230 buf_used
+= strlen(out
);
234 switch (viewer
->lines
[line
].tokens
[j
])
236 case WPS_TOKEN_ALIGN_CENTER
:
237 case WPS_TOKEN_ALIGN_LEFT
:
238 case WPS_TOKEN_ALIGN_LEFT_RTL
:
239 case WPS_TOKEN_ALIGN_RIGHT
:
240 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
241 alignment
= viewer
->lines
[line
].tokens
[j
];
244 case WPS_TOKEN_STRING
:
245 case WPS_TOKEN_CHARACTER
:
246 snprintf(tempbuf
, sizeof(tempbuf
), "%s",
247 viewer
->lines
[line
].strings
[cur_string
]);
250 case WPS_TOKEN_PLAYLIST_POSITION
:
251 snprintf(tempbuf
, sizeof(tempbuf
), "%d", i
);
253 case WPS_TOKEN_FILE_NAME
:
254 get_dir(tempbuf
, sizeof(tempbuf
), filename
, 0);
256 case WPS_TOKEN_FILE_PATH
:
257 snprintf(tempbuf
, sizeof(tempbuf
), "%s", filename
);
265 snprintf(&buf
[buf_used
], sizeof(buf
)-buf_used
, "%s", tempbuf
);
266 buf_used
+= strlen(tempbuf
);
271 int vpwidth
= viewer
->vp
->width
;
272 length
= gwps
->display
->getstringsize(buf
, NULL
, NULL
);
273 if (viewer
->lines
[line
].scroll
&& length
>= vpwidth
)
275 gwps
->display
->puts_scroll(0, (i
-start_item
), buf
);
279 if (length
>= vpwidth
)
285 case WPS_TOKEN_ALIGN_CENTER
:
286 x
= (vpwidth
-length
)/2;
288 case WPS_TOKEN_ALIGN_LEFT_RTL
:
289 if (lang_is_rtl() && VP_IS_RTL(viewer
->vp
))
291 x
= vpwidth
- length
;
294 case WPS_TOKEN_ALIGN_LEFT
:
297 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
298 if (lang_is_rtl() && VP_IS_RTL(viewer
->vp
))
303 case WPS_TOKEN_ALIGN_RIGHT
:
304 x
= vpwidth
- length
;
311 gwps
->display
->putsxy(x
, (i
-start_item
)*line_height
, buf
);
317 /* clears the area where the image was shown */
318 static void clear_image_pos(struct gui_wps
*gwps
, struct gui_img
*img
)
322 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
323 gwps
->display
->fillrect(img
->x
, img
->y
, img
->bm
.width
, img
->subimage_height
);
324 gwps
->display
->set_drawmode(DRMODE_SOLID
);
327 static void wps_draw_image(struct gui_wps
*gwps
, struct gui_img
*img
, int subimage
)
329 struct screen
*display
= gwps
->display
;
330 if(img
->always_display
)
331 display
->set_drawmode(DRMODE_FG
);
333 display
->set_drawmode(DRMODE_SOLID
);
336 if(img
->bm
.format
== FORMAT_MONO
) {
338 display
->mono_bitmap_part(img
->bm
.data
,
339 0, img
->subimage_height
* subimage
,
340 img
->bm
.width
, img
->x
,
341 img
->y
, img
->bm
.width
,
342 img
->subimage_height
);
345 display
->transparent_bitmap_part((fb_data
*)img
->bm
.data
,
346 0, img
->subimage_height
* subimage
,
347 STRIDE(display
->screen_type
,
348 img
->bm
.width
, img
->bm
.height
),
349 img
->x
, img
->y
, img
->bm
.width
,
350 img
->subimage_height
);
355 static void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
357 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
360 struct wps_data
*data
= gwps
->data
;
361 struct screen
*display
= gwps
->display
;
362 struct skin_token_list
*list
= data
->images
;
366 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
369 if (img
->display
>= 0)
371 wps_draw_image(gwps
, img
, img
->display
);
373 else if (img
->always_display
&& img
->vp
== vp
)
375 wps_draw_image(gwps
, img
, 0);
381 /* now draw the AA */
382 if (data
->albumart
&& data
->albumart
->vp
== vp
383 && data
->albumart
->draw
)
385 draw_album_art(gwps
, playback_current_aa_hid(data
->playback_aa_slot
),
387 data
->albumart
->draw
= false;
391 display
->set_drawmode(DRMODE_SOLID
);
394 #else /* HAVE_LCD_CHARCELL */
396 static bool draw_player_progress(struct gui_wps
*gwps
)
398 struct wps_state
*state
= gwps
->state
;
399 struct screen
*display
= gwps
->display
;
400 unsigned char progress_pattern
[7];
405 if (LIKELY(state
->id3
))
407 elapsed
= state
->id3
->elapsed
;
408 length
= state
->id3
->length
;
417 pos
= 36 * (elapsed
+ state
->ff_rewind_count
) / length
;
419 for (i
= 0; i
< 7; i
++, pos
-= 5)
422 progress_pattern
[i
] = 0x1fu
;
424 progress_pattern
[i
] = 0x00u
;
426 progress_pattern
[i
] = 0x1fu
>> pos
;
429 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
433 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
435 static const unsigned char numbers
[10][4] = {
436 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
437 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
438 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
439 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
440 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
441 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
442 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
443 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
444 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
445 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
448 struct wps_state
*state
= gwps
->state
;
449 struct screen
*display
= gwps
->display
;
450 struct wps_data
*data
= gwps
->data
;
451 unsigned char progress_pattern
[7];
461 if (LIKELY(state
->id3
))
463 elapsed
= state
->id3
->elapsed
;
464 length
= state
->id3
->length
;
472 if (buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
475 time
= elapsed
+ state
->ff_rewind_count
;
477 pos
= 55 * time
/ length
;
479 memset(timestr
, 0, sizeof(timestr
));
480 format_time(timestr
, sizeof(timestr
)-2, time
);
481 timestr
[strlen(timestr
)] = ':'; /* always safe */
483 for (i
= 0; i
< 11; i
++, pos
-= 5)
486 memset(progress_pattern
, 0, sizeof(progress_pattern
));
488 if ((digit
= timestr
[time_idx
]))
493 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
495 memcpy(progress_pattern
, numbers
[digit
], 4);
498 else /* tens, shifted right */
500 for (j
= 0; j
< 4; j
++)
501 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
503 if (time_idx
> 0) /* not the first group, add colon in front */
505 progress_pattern
[1] |= 0x10u
;
506 progress_pattern
[3] |= 0x10u
;
512 progress_pattern
[5] = progress_pattern
[6] = 0x1fu
;
515 if (pos
> 0 && pos
< 5)
518 progress_pattern
[5] = progress_pattern
[6] = (~0x1fu
>> pos
) & 0x1fu
;
521 if (softchar
&& pat_idx
< 8)
523 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
525 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
529 buf
= utf8encode(' ', buf
);
531 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
536 #endif /* HAVE_LCD_CHARCELL */
538 /* Return the index to the end token for the conditional token at index.
539 The conditional token can be either a start token or a separator
542 static int find_conditional_end(struct wps_data
*data
, int index
)
545 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
546 ret
= data
->tokens
[ret
].value
.i
;
548 /* ret now is the index to the end token for the conditional. */
552 /* Evaluate the conditional that is at *token_index and return whether a skip
553 has ocurred. *token_index is updated with the new position.
555 static bool evaluate_conditional(struct gui_wps
*gwps
, int *token_index
)
560 struct wps_data
*data
= gwps
->data
;
563 int cond_index
= *token_index
;
566 unsigned char num_options
= data
->tokens
[cond_index
].value
.i
& 0xFF;
567 unsigned char prev_val
= (data
->tokens
[cond_index
].value
.i
& 0xFF00) >> 8;
569 /* treat ?xx<true> constructs as if they had 2 options. */
573 int intval
= num_options
;
574 /* get_token_value needs to know the number of options in the enum */
575 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
576 result
, sizeof(result
), &intval
);
578 /* intval is now the number of the enum option we want to read,
579 starting from 1. If intval is -1, we check if value is empty. */
581 intval
= (value
&& *value
) ? 1 : num_options
;
582 else if (intval
> num_options
|| intval
< 1)
583 intval
= num_options
;
585 data
->tokens
[cond_index
].value
.i
= (intval
<< 8) + num_options
;
587 /* skip to the appropriate enum case */
588 int next
= cond_index
+ 2;
589 for (i
= 1; i
< intval
; i
++)
591 next
= data
->tokens
[next
].value
.i
;
595 if (prev_val
== intval
)
597 /* Same conditional case as previously. Return without clearing the
602 cond_end
= find_conditional_end(data
, cond_index
+ 2);
603 for (i
= cond_index
+ 3; i
< cond_end
; i
++)
605 #ifdef HAVE_LCD_BITMAP
606 /* clear all pictures in the conditional and nested ones */
607 if (data
->tokens
[i
].type
== WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
)
608 clear_image_pos(gwps
, find_image(data
->tokens
[i
].value
.i
&0xFF, data
));
611 if (data
->albumart
&& data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
614 playback_current_aa_hid(data
->playback_aa_slot
), true);
615 data
->albumart
->draw
= false;
624 /* Read a (sub)line to the given alignment format buffer.
625 linebuf is the buffer where the data is actually stored.
626 align is the alignment format that'll be used to display the text.
627 The return value indicates whether the line needs to be updated.
629 static bool get_line(struct gui_wps
*gwps
,
630 struct skin_subline
*subline
,
631 struct align_pos
*align
,
634 unsigned refresh_mode
)
636 struct wps_data
*data
= gwps
->data
;
639 char *buf
= linebuf
; /* will always point to the writing position */
640 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
643 (void)refresh_mode
; /* silence warning on charcell */
645 /* alignment-related variables */
647 char* cur_align_start
;
648 cur_align_start
= buf
;
649 cur_align
= WPS_ALIGN_LEFT
;
651 align
->center
= NULL
;
653 /* Process all tokens of the desired subline */
654 for (i
= subline
->first_token_idx
;
655 i
<= subline
->last_token_idx
; i
++)
657 switch(data
->tokens
[i
].type
)
659 case WPS_TOKEN_CONDITIONAL
:
660 /* place ourselves in the right conditional case */
661 update
|= evaluate_conditional(gwps
, &i
);
664 case WPS_TOKEN_CONDITIONAL_OPTION
:
665 /* we've finished in the curent conditional case,
666 skip to the end of the conditional structure */
667 i
= find_conditional_end(data
, i
);
670 #ifdef HAVE_LCD_BITMAP
671 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
673 char n
= data
->tokens
[i
].value
.i
& 0xFF;
674 int subimage
= data
->tokens
[i
].value
.i
>> 8;
675 struct gui_img
*img
= find_image(n
, data
);
677 if (img
&& img
->loaded
)
678 img
->display
= subimage
;
681 case WPS_TOKEN_DRAW_INBUILTBAR
:
682 gui_statusbar_draw(&(statusbars
.statusbars
[gwps
->display
->screen_type
]),
683 refresh_mode
== WPS_REFRESH_ALL
,
684 data
->tokens
[i
].value
.data
);
688 case WPS_TOKEN_ALIGN_LEFT
:
689 case WPS_TOKEN_ALIGN_LEFT_RTL
:
690 case WPS_TOKEN_ALIGN_CENTER
:
691 case WPS_TOKEN_ALIGN_RIGHT
:
692 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
693 /* remember where the current aligned text started */
697 align
->left
= cur_align_start
;
700 case WPS_ALIGN_CENTER
:
701 align
->center
= cur_align_start
;
704 case WPS_ALIGN_RIGHT
:
705 align
->right
= cur_align_start
;
708 /* start a new alignment */
709 switch (data
->tokens
[i
].type
)
711 case WPS_TOKEN_ALIGN_LEFT
:
712 cur_align
= WPS_ALIGN_LEFT
;
714 case WPS_TOKEN_ALIGN_LEFT_RTL
:
715 cur_align
= lang_is_rtl() ? WPS_ALIGN_RIGHT
:
718 case WPS_TOKEN_ALIGN_CENTER
:
719 cur_align
= WPS_ALIGN_CENTER
;
721 case WPS_TOKEN_ALIGN_RIGHT
:
722 cur_align
= WPS_ALIGN_RIGHT
;
724 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
725 cur_align
= lang_is_rtl() ? WPS_ALIGN_LEFT
:
732 cur_align_start
= buf
;
734 case WPS_VIEWPORT_ENABLE
:
736 char label
= data
->tokens
[i
].value
.i
;
737 char temp
= VP_DRAW_HIDEABLE
;
738 /* viewports are allowed to share id's so find and enable
740 struct skin_token_list
*list
= data
->viewports
;
743 struct skin_viewport
*vp
=
744 (struct skin_viewport
*)list
->token
->value
.data
;
745 if (vp
->label
== label
)
747 if (vp
->hidden_flags
&VP_DRAW_WASHIDDEN
)
748 temp
|= VP_DRAW_WASHIDDEN
;
749 vp
->hidden_flags
= temp
;
755 #ifdef HAVE_LCD_BITMAP
756 case WPS_VIEWPORT_CUSTOMLIST
:
757 draw_playlist_viewer_list(gwps
, data
->tokens
[i
].value
.data
);
762 /* get the value of the tag and copy it to the buffer */
763 const char *value
= get_token_value(gwps
, &data
->tokens
[i
],
764 temp_buf
, sizeof(temp_buf
), NULL
);
768 while (*value
&& (buf
< linebuf_end
))
776 /* close the current alignment */
780 align
->left
= cur_align_start
;
783 case WPS_ALIGN_CENTER
:
784 align
->center
= cur_align_start
;
787 case WPS_ALIGN_RIGHT
:
788 align
->right
= cur_align_start
;
794 static void get_subline_timeout(struct gui_wps
*gwps
, struct skin_subline
*subline
)
796 struct wps_data
*data
= gwps
->data
;
798 subline
->time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
800 for (i
= subline
->first_token_idx
;
801 i
<= subline
->last_token_idx
; i
++)
803 switch(data
->tokens
[i
].type
)
805 case WPS_TOKEN_CONDITIONAL
:
806 /* place ourselves in the right conditional case */
807 evaluate_conditional(gwps
, &i
);
810 case WPS_TOKEN_CONDITIONAL_OPTION
:
811 /* we've finished in the curent conditional case,
812 skip to the end of the conditional structure */
813 i
= find_conditional_end(data
, i
);
816 case WPS_TOKEN_SUBLINE_TIMEOUT
:
817 subline
->time_mult
= data
->tokens
[i
].value
.i
;
826 /* Calculates which subline should be displayed for the specified line
827 Returns true iff the subline must be refreshed */
828 static bool update_curr_subline(struct gui_wps
*gwps
, struct skin_line
*line
)
830 /* shortcut this whole thing if we need to reset the line completly */
831 if (line
->curr_subline
== NULL
)
833 line
->subline_expire_time
= current_tick
;
834 line
->curr_subline
= &line
->sublines
;
835 if (!line
->curr_subline
->next
)
837 line
->subline_expire_time
+= 100*HZ
;
841 get_subline_timeout(gwps
, line
->curr_subline
);
842 line
->subline_expire_time
+= TIMEOUT_UNIT
*line
->curr_subline
->time_mult
;
846 /* if time to advance to next sub-line */
847 if (TIME_AFTER(current_tick
, line
->subline_expire_time
- 1))
849 /* if there is only one subline, there is no need to search for a new one */
850 if (&line
->sublines
== line
->curr_subline
&&
851 line
->curr_subline
->next
== NULL
)
853 line
->subline_expire_time
+= 100 * HZ
;
856 if (line
->curr_subline
->next
)
857 line
->curr_subline
= line
->curr_subline
->next
;
859 line
->curr_subline
= &line
->sublines
;
860 get_subline_timeout(gwps
, line
->curr_subline
);
861 line
->subline_expire_time
= current_tick
+ TIMEOUT_UNIT
*line
->curr_subline
->time_mult
;
867 /* Display a line appropriately according to its alignment format.
868 format_align contains the text, separated between left, center and right.
869 line is the index of the line on the screen.
870 scroll indicates whether the line is a scrolling one or not.
872 static void write_line(struct screen
*display
,
873 struct align_pos
*format_align
,
877 int left_width
= 0, left_xpos
;
878 int center_width
= 0, center_xpos
;
879 int right_width
= 0, right_xpos
;
885 /* calculate different string sizes and positions */
886 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
887 if (format_align
->left
!= 0) {
888 display
->getstringsize((unsigned char *)format_align
->left
,
889 &left_width
, &string_height
);
892 if (format_align
->right
!= 0) {
893 display
->getstringsize((unsigned char *)format_align
->right
,
894 &right_width
, &string_height
);
897 if (format_align
->center
!= 0) {
898 display
->getstringsize((unsigned char *)format_align
->center
,
899 ¢er_width
, &string_height
);
903 right_xpos
= (display
->getwidth() - right_width
);
904 center_xpos
= (display
->getwidth() + left_xpos
- center_width
) / 2;
906 scroll_width
= display
->getwidth() - left_xpos
;
908 /* Checks for overlapping strings.
909 If needed the overlapping strings will be merged, separated by a
912 /* CASE 1: left and centered string overlap */
913 /* there is a left string, need to merge left and center */
914 if ((left_width
!= 0 && center_width
!= 0) &&
915 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
916 /* replace the former separator '\0' of left and
917 center string with a space */
918 *(--format_align
->center
) = ' ';
919 /* calculate the new width and position of the merged string */
920 left_width
= left_width
+ space_width
+ center_width
;
921 /* there is no centered string anymore */
924 /* there is no left string, move center to left */
925 if ((left_width
== 0 && center_width
!= 0) &&
926 (left_xpos
+ left_width
> center_xpos
)) {
927 /* move the center string to the left string */
928 format_align
->left
= format_align
->center
;
929 /* calculate the new width and position of the string */
930 left_width
= center_width
;
931 /* there is no centered string anymore */
935 /* CASE 2: centered and right string overlap */
936 /* there is a right string, need to merge center and right */
937 if ((center_width
!= 0 && right_width
!= 0) &&
938 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
939 /* replace the former separator '\0' of center and
940 right string with a space */
941 *(--format_align
->right
) = ' ';
942 /* move the center string to the right after merge */
943 format_align
->right
= format_align
->center
;
944 /* calculate the new width and position of the merged string */
945 right_width
= center_width
+ space_width
+ right_width
;
946 right_xpos
= (display
->getwidth() - right_width
);
947 /* there is no centered string anymore */
950 /* there is no right string, move center to right */
951 if ((center_width
!= 0 && right_width
== 0) &&
952 (center_xpos
+ center_width
> right_xpos
)) {
953 /* move the center string to the right string */
954 format_align
->right
= format_align
->center
;
955 /* calculate the new width and position of the string */
956 right_width
= center_width
;
957 right_xpos
= (display
->getwidth() - right_width
);
958 /* there is no centered string anymore */
962 /* CASE 3: left and right overlap
963 There is no center string anymore, either there never
964 was one or it has been merged in case 1 or 2 */
965 /* there is a left string, need to merge left and right */
966 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
967 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
968 /* replace the former separator '\0' of left and
969 right string with a space */
970 *(--format_align
->right
) = ' ';
971 /* calculate the new width and position of the string */
972 left_width
= left_width
+ space_width
+ right_width
;
973 /* there is no right string anymore */
976 /* there is no left string, move right to left */
977 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
978 (left_width
> right_xpos
)) {
979 /* move the right string to the left string */
980 format_align
->left
= format_align
->right
;
981 /* calculate the new width and position of the string */
982 left_width
= right_width
;
983 /* there is no right string anymore */
987 ypos
= (line
* string_height
);
990 if (scroll
&& ((left_width
> scroll_width
) ||
991 (center_width
> scroll_width
) ||
992 (right_width
> scroll_width
)))
994 display
->puts_scroll(0, line
,
995 (unsigned char *)format_align
->left
);
999 #ifdef HAVE_LCD_BITMAP
1000 /* clear the line first */
1001 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1002 display
->fillrect(left_xpos
, ypos
, display
->getwidth(), string_height
);
1003 display
->set_drawmode(DRMODE_SOLID
);
1006 /* Nasty hack: we output an empty scrolling string,
1007 which will reset the scroller for that line */
1008 display
->puts_scroll(0, line
, (unsigned char *)"");
1010 /* print aligned strings */
1011 if (left_width
!= 0)
1013 display
->putsxy(left_xpos
, ypos
,
1014 (unsigned char *)format_align
->left
);
1016 if (center_width
!= 0)
1018 display
->putsxy(center_xpos
, ypos
,
1019 (unsigned char *)format_align
->center
);
1021 if (right_width
!= 0)
1023 display
->putsxy(right_xpos
, ypos
,
1024 (unsigned char *)format_align
->right
);
1029 static bool skin_redraw(struct gui_wps
*gwps
, unsigned refresh_mode
)
1031 struct wps_data
*data
= gwps
->data
;
1032 struct screen
*display
= gwps
->display
;
1034 if (!data
|| !display
|| !gwps
->state
)
1038 char linebuf
[MAX_PATH
];
1040 struct align_pos align
;
1042 align
.center
= NULL
;
1046 struct skin_token_list
*viewport_list
;
1048 bool update_line
, new_subline_refresh
;
1050 #ifdef HAVE_LCD_BITMAP
1052 /* to find out wether the peak meter is enabled we
1053 assume it wasn't until we find a line that contains
1054 the peak meter. We can't use peak_meter_enabled itself
1055 because that would mean to turn off the meter thread
1056 temporarily. (That shouldn't matter unless yield
1057 or sleep is called but who knows...)
1059 bool enable_pm
= false;
1063 /* reset to first subline if refresh all flag is set */
1064 if (refresh_mode
== WPS_REFRESH_ALL
)
1066 struct skin_line
*line
;
1067 struct skin_viewport
*skin_viewport
= find_viewport(VP_DEFAULT_LABEL
, data
);
1069 if (!(skin_viewport
->hidden_flags
& VP_NEVER_VISIBLE
))
1071 display
->set_viewport(&skin_viewport
->vp
);
1072 display
->clear_viewport();
1075 for (viewport_list
= data
->viewports
;
1076 viewport_list
; viewport_list
= viewport_list
->next
)
1079 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1080 for(line
= skin_viewport
->lines
; line
; line
= line
->next
)
1082 line
->curr_subline
= NULL
;
1087 #ifdef HAVE_LCD_CHARCELLS
1089 for (i
= 0; i
< 8; i
++)
1091 if (data
->wps_progress_pat
[i
] == 0)
1092 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1096 /* disable any viewports which are conditionally displayed.
1097 * If we are only refreshing the peak meter then don't change the viewport
1098 * enabled flags as this will stop scrolling. viewports cant be
1099 * toggled in this refresh mode anyway (FS#10215)*/
1100 if (refresh_mode
!= WPS_REFRESH_PEAK_METER
)
1102 for (viewport_list
= data
->viewports
;
1103 viewport_list
; viewport_list
= viewport_list
->next
)
1105 struct skin_viewport
*skin_viewport
=
1106 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1107 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
1111 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDEABLE
)
1113 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
)
1114 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1116 skin_viewport
->hidden_flags
|= VP_DRAW_HIDDEN
;
1120 int viewport_count
= 0;
1121 for (viewport_list
= data
->viewports
;
1122 viewport_list
; viewport_list
= viewport_list
->next
, viewport_count
++)
1124 struct skin_viewport
*skin_viewport
=
1125 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1126 unsigned vp_refresh_mode
= refresh_mode
;
1128 display
->set_viewport(&skin_viewport
->vp
);
1132 #ifdef HAVE_LCD_BITMAP
1133 /* Set images to not to be displayed */
1134 struct skin_token_list
*imglist
= data
->images
;
1137 struct gui_img
*img
= (struct gui_img
*)imglist
->token
->value
.data
;
1139 imglist
= imglist
->next
;
1142 /* dont redraw the viewport if its disabled */
1143 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
1144 { /* don't draw anything into this one */
1145 vp_refresh_mode
= 0; hidden_vp
= true;
1147 else if ((skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
))
1149 if (!(skin_viewport
->hidden_flags
&VP_DRAW_WASHIDDEN
))
1150 display
->scroll_stop(&skin_viewport
->vp
);
1151 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1154 else if (((skin_viewport
->hidden_flags
&
1155 (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
))
1156 == (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
)))
1158 vp_refresh_mode
= WPS_REFRESH_ALL
;
1159 skin_viewport
->hidden_flags
= VP_DRAW_HIDEABLE
;
1162 if (vp_refresh_mode
== WPS_REFRESH_ALL
)
1164 display
->clear_viewport();
1167 /* loop over the lines for this viewport */
1168 struct skin_line
*line
;
1171 for (line
= skin_viewport
->lines
; line
; line
= line
->next
, line_count
++)
1173 struct skin_subline
*subline
;
1174 memset(linebuf
, 0, sizeof(linebuf
));
1175 update_line
= false;
1177 /* get current subline for the line */
1178 new_subline_refresh
= update_curr_subline(gwps
, line
);
1179 subline
= line
->curr_subline
;
1180 flags
= line
->curr_subline
->line_type
;
1182 if (vp_refresh_mode
== WPS_REFRESH_ALL
|| (flags
& vp_refresh_mode
)
1183 || new_subline_refresh
|| hidden_vp
)
1185 /* get_line tells us if we need to update the line */
1186 update_line
= get_line(gwps
, subline
, &align
,
1187 linebuf
, sizeof(linebuf
), vp_refresh_mode
);
1189 #ifdef HAVE_LCD_BITMAP
1191 if (flags
& vp_refresh_mode
& WPS_REFRESH_PEAK_METER
)
1193 /* the peakmeter should be alone on its line */
1194 update_line
= false;
1196 int h
= font_get(skin_viewport
->vp
.font
)->height
;
1197 int peak_meter_y
= line_count
* h
;
1199 /* The user might decide to have the peak meter in the last
1200 line so that it is only displayed if no status bar is
1201 visible. If so we neither want do draw nor enable the
1203 if (peak_meter_y
+ h
<= skin_viewport
->vp
.y
+skin_viewport
->vp
.height
) {
1204 /* found a line with a peak meter -> remember that we must
1207 peak_meter_enabled
= true;
1208 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
1209 MIN(h
, skin_viewport
->vp
.y
+skin_viewport
->vp
.height
- peak_meter_y
));
1213 peak_meter_enabled
= false;
1217 #else /* HAVE_LCD_CHARCELL */
1220 if (flags
& vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1222 if (data
->full_line_progressbar
)
1223 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
1225 draw_player_progress(gwps
);
1229 if (update_line
&& !hidden_vp
&&
1230 /* conditionals clear the line which means if the %Vd is put into the default
1231 viewport there will be a blank line.
1232 To get around this we dont allow any actual drawing to happen in the
1233 deault vp if other vp's are defined */
1234 ((skin_viewport
->label
!= VP_DEFAULT_LABEL
&& viewport_list
->next
) ||
1235 !viewport_list
->next
))
1237 if (flags
& WPS_REFRESH_SCROLL
)
1239 /* if the line is a scrolling one we don't want to update
1240 too often, so that it has the time to scroll */
1241 if ((vp_refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
1242 write_line(display
, &align
, line_count
, true);
1245 write_line(display
, &align
, line_count
, false);
1248 #ifdef HAVE_LCD_BITMAP
1250 if (vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1252 if (skin_viewport
->pb
)
1254 draw_progressbar(gwps
, skin_viewport
);
1257 /* Now display any images in this viewport */
1259 wps_display_images(gwps
, &skin_viewport
->vp
);
1263 #ifdef HAVE_LCD_BITMAP
1264 data
->peak_meter_enabled
= enable_pm
;
1266 /* Restore the default viewport */
1267 display
->set_viewport(NULL
);