1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002-2007 Björn Stenberg
11 * Copyright (C) 2007-2008 Nicolas Pennequin
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
28 #include "rbunicode.h"
36 #include "statusbar.h"
38 #include "scrollbar.h"
39 #include "screen_access.h"
43 #ifdef HAVE_LCD_BITMAP
44 #include "peakmeter.h"
53 #if CONFIG_CODEC == SWCODEC
60 #include "wps_internals.h"
61 #include "skin_engine.h"
62 #include "statusbar-skinned.h"
64 static bool skin_redraw(struct gui_wps
*gwps
, unsigned refresh_mode
);
66 /* update a skinned screen, update_type is WPS_REFRESH_* values.
67 * Usually it should only be WPS_REFRESH_NON_STATIC
68 * A full update will be done if required (state.do_full_update == true)
70 bool skin_update(struct gui_wps
*gwps
, unsigned int update_type
)
73 /* This maybe shouldnt be here, but while the skin is only used to
74 * display the music screen this is better than whereever we are being
75 * called from. This is also safe for skined screen which dont use the id3 */
76 struct mp3entry
*id3
= gwps
->state
->id3
;
77 bool cuesheet_update
= (id3
!= NULL
? cuesheet_subtrack_changed(id3
) : false);
78 gwps
->sync_data
->do_full_update
|= cuesheet_update
;
80 retval
= skin_redraw(gwps
, gwps
->sync_data
->do_full_update
?
81 WPS_REFRESH_ALL
: update_type
);
85 #ifdef HAVE_LCD_BITMAP
87 void skin_statusbar_changed(struct gui_wps
*skin
)
91 struct wps_data
*data
= skin
->data
;
92 const struct screen
*display
= skin
->display
;
93 const int screen
= display
->screen_type
;
95 struct viewport
*vp
= &find_viewport(VP_DEFAULT_LABEL
, data
)->vp
;
96 viewport_set_defaults(vp
, screen
);
99 { /* fix up the default viewport */
100 if (data
->show_sb_on_wps
)
102 if (statusbar_position(screen
) != STATUSBAR_OFF
)
103 return; /* vp is fixed already */
105 vp
->y
= STATUSBAR_HEIGHT
;
106 vp
->height
= display
->lcdheight
- STATUSBAR_HEIGHT
;
110 if (statusbar_position(screen
) == STATUSBAR_OFF
)
111 return; /* vp is fixed already */
113 vp
->height
= display
->lcdheight
;
114 vp
->width
= display
->lcdwidth
;
119 static void draw_progressbar(struct gui_wps
*gwps
,
120 struct progressbar
*pb
)
122 struct screen
*display
= gwps
->display
;
123 struct viewport
*vp
= pb
->vp
;
124 struct wps_state
*state
= gwps
->state
;
125 struct mp3entry
*id3
= state
->id3
;
126 int y
= pb
->y
, height
= pb
->height
;
127 unsigned long length
, elapsed
;
130 height
= font_get(vp
->font
)->height
;
134 int line_height
= font_get(vp
->font
)->height
;
135 /* center the pb in the line, but only if the line is higher than the pb */
136 int center
= (line_height
-height
)/2;
137 /* if Y was not set calculate by font height,Y is -line_number-1 */
138 y
= (-y
-1)*line_height
+ (0 > center
? 0 : center
);
141 if (pb
->type
== WPS_TOKEN_VOLUMEBAR
)
143 int minvol
= sound_min(SOUND_VOLUME
);
144 int maxvol
= sound_max(SOUND_VOLUME
);
145 length
= maxvol
-minvol
;
146 elapsed
= global_settings
.volume
-minvol
;
148 else if (id3
&& id3
->length
)
150 length
= id3
->length
;
151 elapsed
= id3
->elapsed
+ state
->ff_rewind_count
;
159 if (pb
->have_bitmap_pb
)
160 gui_bitmap_scrollbar_draw(display
, pb
->bm
,
161 pb
->x
, y
, pb
->width
, pb
->bm
.height
,
162 length
, 0, elapsed
, HORIZONTAL
);
164 gui_scrollbar_draw(display
, pb
->x
, y
, pb
->width
, height
,
165 length
, 0, elapsed
, HORIZONTAL
);
167 if (pb
->type
== WPS_TOKEN_PROGRESSBAR
&& id3
&& id3
->length
)
169 #ifdef AB_REPEAT_ENABLE
170 if (ab_repeat_mode_enabled())
171 ab_draw_markers(display
, id3
->length
,
172 pb
->x
, y
, pb
->width
, height
);
176 cue_draw_markers(display
, id3
->cuesheet
, id3
->length
,
177 pb
->x
, y
+1, pb
->width
, height
-2);
181 bool audio_peek_track(struct mp3entry
* id3
, int offset
);
182 static void draw_playlist_viewer_list(struct gui_wps
*gwps
,
183 struct playlistviewer
*viewer
)
185 struct wps_state
*state
= gwps
->state
;
186 int lines
= viewport_get_nb_lines(viewer
->vp
);
187 int line_height
= font_get(viewer
->vp
->font
)->height
;
188 int cur_playlist_pos
= playlist_get_display_index();
189 int start_item
= MAX(0, cur_playlist_pos
+ viewer
->start_offset
);
191 struct wps_token token
;
192 int x
, length
, alignment
= WPS_TOKEN_ALIGN_LEFT
;
194 struct mp3entry
*pid3
;
195 #if CONFIG_CODEC == SWCODEC
198 char buf
[MAX_PATH
*2], tempbuf
[MAX_PATH
];
199 unsigned int buf_used
= 0;
201 gwps
->display
->set_viewport(viewer
->vp
);
202 for(i
=start_item
; (i
-start_item
)<lines
&& i
<playlist_amount(); i
++)
204 if (i
== cur_playlist_pos
)
208 else if (i
== cur_playlist_pos
+1)
212 #if CONFIG_CODEC == SWCODEC
213 else if ((i
>cur_playlist_pos
) && audio_peek_track(&id3
, i
-cur_playlist_pos
))
223 int line
= pid3
? TRACK_HAS_INFO
: TRACK_HAS_NO_INFO
;
224 int j
= 0, cur_string
= 0;
225 char *filename
= playlist_peek(i
-cur_playlist_pos
);
228 while (j
< viewer
->lines
[line
].count
&& (buf_used
<sizeof(buf
)))
230 const char *out
= NULL
;
231 token
.type
= viewer
->lines
[line
].tokens
[j
];
234 out
= get_id3_token(&token
, pid3
, tempbuf
, sizeof(tempbuf
), -1, NULL
);
237 snprintf(&buf
[buf_used
], sizeof(buf
)-buf_used
, "%s", out
);
238 buf_used
+= strlen(out
);
242 switch (viewer
->lines
[line
].tokens
[j
])
244 case WPS_TOKEN_ALIGN_CENTER
:
245 case WPS_TOKEN_ALIGN_LEFT
:
246 case WPS_TOKEN_ALIGN_LEFT_RTL
:
247 case WPS_TOKEN_ALIGN_RIGHT
:
248 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
249 alignment
= viewer
->lines
[line
].tokens
[j
];
252 case WPS_TOKEN_STRING
:
253 case WPS_TOKEN_CHARACTER
:
254 snprintf(tempbuf
, sizeof(tempbuf
), "%s",
255 viewer
->lines
[line
].strings
[cur_string
]);
258 case WPS_TOKEN_PLAYLIST_POSITION
:
259 snprintf(tempbuf
, sizeof(tempbuf
), "%d", i
);
261 case WPS_TOKEN_FILE_NAME
:
262 get_dir(tempbuf
, sizeof(tempbuf
), filename
, 0);
264 case WPS_TOKEN_FILE_PATH
:
265 snprintf(tempbuf
, sizeof(tempbuf
), "%s", filename
);
273 snprintf(&buf
[buf_used
], sizeof(buf
)-buf_used
, "%s", tempbuf
);
274 buf_used
+= strlen(tempbuf
);
279 int vpwidth
= viewer
->vp
->width
;
280 length
= gwps
->display
->getstringsize(buf
, NULL
, NULL
);
281 if (viewer
->lines
[line
].scroll
&& length
>= vpwidth
)
283 gwps
->display
->puts_scroll(0, (i
-start_item
), buf
);
287 if (length
>= vpwidth
)
293 case WPS_TOKEN_ALIGN_CENTER
:
294 x
= (vpwidth
-length
)/2;
296 case WPS_TOKEN_ALIGN_LEFT_RTL
:
297 if (lang_is_rtl() && VP_IS_RTL(viewer
->vp
))
299 x
= vpwidth
- length
;
302 case WPS_TOKEN_ALIGN_LEFT
:
305 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
306 if (lang_is_rtl() && VP_IS_RTL(viewer
->vp
))
311 case WPS_TOKEN_ALIGN_RIGHT
:
312 x
= vpwidth
- length
;
319 gwps
->display
->putsxy(x
, (i
-start_item
)*line_height
, buf
);
325 /* clears the area where the image was shown */
326 static void clear_image_pos(struct gui_wps
*gwps
, struct gui_img
*img
)
330 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
331 gwps
->display
->fillrect(img
->x
, img
->y
, img
->bm
.width
, img
->subimage_height
);
332 gwps
->display
->set_drawmode(DRMODE_SOLID
);
335 static void wps_draw_image(struct gui_wps
*gwps
, struct gui_img
*img
, int subimage
)
337 struct screen
*display
= gwps
->display
;
338 if(img
->always_display
)
339 display
->set_drawmode(DRMODE_FG
);
341 display
->set_drawmode(DRMODE_SOLID
);
344 if(img
->bm
.format
== FORMAT_MONO
) {
346 display
->mono_bitmap_part(img
->bm
.data
,
347 0, img
->subimage_height
* subimage
,
348 img
->bm
.width
, img
->x
,
349 img
->y
, img
->bm
.width
,
350 img
->subimage_height
);
353 display
->transparent_bitmap_part((fb_data
*)img
->bm
.data
,
354 0, img
->subimage_height
* subimage
,
355 STRIDE(display
->screen_type
,
356 img
->bm
.width
, img
->bm
.height
),
357 img
->x
, img
->y
, img
->bm
.width
,
358 img
->subimage_height
);
363 static void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
365 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
368 struct wps_data
*data
= gwps
->data
;
369 struct screen
*display
= gwps
->display
;
370 struct skin_token_list
*list
= data
->images
;
374 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
377 if (img
->display
>= 0)
379 wps_draw_image(gwps
, img
, img
->display
);
381 else if (img
->always_display
&& img
->vp
== vp
)
383 wps_draw_image(gwps
, img
, 0);
389 /* now draw the AA */
390 if (data
->albumart
&& data
->albumart
->vp
== vp
391 && data
->albumart
->draw
)
393 draw_album_art(gwps
, playback_current_aa_hid(data
->playback_aa_slot
),
395 data
->albumart
->draw
= false;
399 display
->set_drawmode(DRMODE_SOLID
);
402 #else /* HAVE_LCD_CHARCELL */
404 static bool draw_player_progress(struct gui_wps
*gwps
)
406 struct wps_state
*state
= gwps
->state
;
407 struct screen
*display
= gwps
->display
;
408 unsigned char progress_pattern
[7];
413 if (LIKELY(state
->id3
))
415 elapsed
= state
->id3
->elapsed
;
416 length
= state
->id3
->length
;
425 pos
= 36 * (elapsed
+ state
->ff_rewind_count
) / length
;
427 for (i
= 0; i
< 7; i
++, pos
-= 5)
430 progress_pattern
[i
] = 0x1fu
;
432 progress_pattern
[i
] = 0x00u
;
434 progress_pattern
[i
] = 0x1fu
>> pos
;
437 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
441 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
443 static const unsigned char numbers
[10][4] = {
444 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
445 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
446 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
447 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
448 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
449 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
450 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
451 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
452 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
453 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
456 struct wps_state
*state
= gwps
->state
;
457 struct screen
*display
= gwps
->display
;
458 struct wps_data
*data
= gwps
->data
;
459 unsigned char progress_pattern
[7];
469 if (LIKELY(state
->id3
))
471 elapsed
= state
->id3
->elapsed
;
472 length
= state
->id3
->length
;
480 if (buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
483 time
= elapsed
+ state
->ff_rewind_count
;
485 pos
= 55 * time
/ length
;
487 memset(timestr
, 0, sizeof(timestr
));
488 format_time(timestr
, sizeof(timestr
)-2, time
);
489 timestr
[strlen(timestr
)] = ':'; /* always safe */
491 for (i
= 0; i
< 11; i
++, pos
-= 5)
494 memset(progress_pattern
, 0, sizeof(progress_pattern
));
496 if ((digit
= timestr
[time_idx
]))
501 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
503 memcpy(progress_pattern
, numbers
[digit
], 4);
506 else /* tens, shifted right */
508 for (j
= 0; j
< 4; j
++)
509 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
511 if (time_idx
> 0) /* not the first group, add colon in front */
513 progress_pattern
[1] |= 0x10u
;
514 progress_pattern
[3] |= 0x10u
;
520 progress_pattern
[5] = progress_pattern
[6] = 0x1fu
;
523 if (pos
> 0 && pos
< 5)
526 progress_pattern
[5] = progress_pattern
[6] = (~0x1fu
>> pos
) & 0x1fu
;
529 if (softchar
&& pat_idx
< 8)
531 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
533 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
537 buf
= utf8encode(' ', buf
);
539 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
544 #endif /* HAVE_LCD_CHARCELL */
546 /* Return the index to the end token for the conditional token at index.
547 The conditional token can be either a start token or a separator
550 static int find_conditional_end(struct wps_data
*data
, int index
)
553 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
554 ret
= data
->tokens
[ret
].value
.i
;
556 /* ret now is the index to the end token for the conditional. */
560 /* Evaluate the conditional that is at *token_index and return whether a skip
561 has ocurred. *token_index is updated with the new position.
563 static bool evaluate_conditional(struct gui_wps
*gwps
, int *token_index
)
568 struct wps_data
*data
= gwps
->data
;
571 int cond_index
= *token_index
;
574 unsigned char num_options
= data
->tokens
[cond_index
].value
.i
& 0xFF;
575 unsigned char prev_val
= (data
->tokens
[cond_index
].value
.i
& 0xFF00) >> 8;
577 /* treat ?xx<true> constructs as if they had 2 options. */
581 int intval
= num_options
;
582 /* get_token_value needs to know the number of options in the enum */
583 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
584 result
, sizeof(result
), &intval
);
586 /* intval is now the number of the enum option we want to read,
587 starting from 1. If intval is -1, we check if value is empty. */
589 intval
= (value
&& *value
) ? 1 : num_options
;
590 else if (intval
> num_options
|| intval
< 1)
591 intval
= num_options
;
593 data
->tokens
[cond_index
].value
.i
= (intval
<< 8) + num_options
;
595 /* skip to the appropriate enum case */
596 int next
= cond_index
+ 2;
597 for (i
= 1; i
< intval
; i
++)
599 next
= data
->tokens
[next
].value
.i
;
603 if (prev_val
== intval
)
605 /* Same conditional case as previously. Return without clearing the
610 cond_end
= find_conditional_end(data
, cond_index
+ 2);
611 for (i
= cond_index
+ 3; i
< cond_end
; i
++)
613 #ifdef HAVE_LCD_BITMAP
614 /* clear all pictures in the conditional and nested ones */
615 if (data
->tokens
[i
].type
== WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
)
616 clear_image_pos(gwps
, find_image(data
->tokens
[i
].value
.i
&0xFF, data
));
619 if (data
->albumart
&& data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
622 playback_current_aa_hid(data
->playback_aa_slot
), true);
623 data
->albumart
->draw
= false;
632 /* Read a (sub)line to the given alignment format buffer.
633 linebuf is the buffer where the data is actually stored.
634 align is the alignment format that'll be used to display the text.
635 The return value indicates whether the line needs to be updated.
637 static bool get_line(struct gui_wps
*gwps
,
638 struct skin_subline
*subline
,
639 struct align_pos
*align
,
642 unsigned refresh_mode
)
644 struct wps_data
*data
= gwps
->data
;
647 char *buf
= linebuf
; /* will always point to the writing position */
648 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
651 (void)refresh_mode
; /* silence warning on charcell */
653 /* alignment-related variables */
655 char* cur_align_start
;
656 cur_align_start
= buf
;
657 cur_align
= WPS_ALIGN_LEFT
;
659 align
->center
= NULL
;
661 /* Process all tokens of the desired subline */
662 for (i
= subline
->first_token_idx
;
663 i
<= subline
->last_token_idx
; i
++)
665 switch(data
->tokens
[i
].type
)
667 case WPS_TOKEN_CONDITIONAL
:
668 /* place ourselves in the right conditional case */
669 update
|= evaluate_conditional(gwps
, &i
);
672 case WPS_TOKEN_CONDITIONAL_OPTION
:
673 /* we've finished in the curent conditional case,
674 skip to the end of the conditional structure */
675 i
= find_conditional_end(data
, i
);
678 #ifdef HAVE_LCD_BITMAP
679 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
681 char n
= data
->tokens
[i
].value
.i
& 0xFF;
682 int subimage
= data
->tokens
[i
].value
.i
>> 8;
683 struct gui_img
*img
= find_image(n
, data
);
685 if (img
&& img
->loaded
)
686 img
->display
= subimage
;
689 case WPS_TOKEN_DRAW_INBUILTBAR
:
690 gui_statusbar_draw(&(statusbars
.statusbars
[gwps
->display
->screen_type
]),
691 refresh_mode
== WPS_REFRESH_ALL
,
692 data
->tokens
[i
].value
.data
);
696 case WPS_TOKEN_ALIGN_LEFT
:
697 case WPS_TOKEN_ALIGN_LEFT_RTL
:
698 case WPS_TOKEN_ALIGN_CENTER
:
699 case WPS_TOKEN_ALIGN_RIGHT
:
700 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
701 /* remember where the current aligned text started */
705 align
->left
= cur_align_start
;
708 case WPS_ALIGN_CENTER
:
709 align
->center
= cur_align_start
;
712 case WPS_ALIGN_RIGHT
:
713 align
->right
= cur_align_start
;
716 /* start a new alignment */
717 switch (data
->tokens
[i
].type
)
719 case WPS_TOKEN_ALIGN_LEFT
:
720 cur_align
= WPS_ALIGN_LEFT
;
722 case WPS_TOKEN_ALIGN_LEFT_RTL
:
723 cur_align
= lang_is_rtl() ? WPS_ALIGN_RIGHT
:
726 case WPS_TOKEN_ALIGN_CENTER
:
727 cur_align
= WPS_ALIGN_CENTER
;
729 case WPS_TOKEN_ALIGN_RIGHT
:
730 cur_align
= WPS_ALIGN_RIGHT
;
732 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
733 cur_align
= lang_is_rtl() ? WPS_ALIGN_LEFT
:
740 cur_align_start
= buf
;
742 case WPS_VIEWPORT_ENABLE
:
744 char label
= data
->tokens
[i
].value
.i
;
745 char temp
= VP_DRAW_HIDEABLE
;
746 /* viewports are allowed to share id's so find and enable
748 struct skin_token_list
*list
= data
->viewports
;
751 struct skin_viewport
*vp
=
752 (struct skin_viewport
*)list
->token
->value
.data
;
753 if (vp
->label
== label
)
755 if (vp
->hidden_flags
&VP_DRAW_WASHIDDEN
)
756 temp
|= VP_DRAW_WASHIDDEN
;
757 vp
->hidden_flags
= temp
;
763 #ifdef HAVE_LCD_BITMAP
764 case WPS_VIEWPORT_CUSTOMLIST
:
765 draw_playlist_viewer_list(gwps
, data
->tokens
[i
].value
.data
);
770 /* get the value of the tag and copy it to the buffer */
771 const char *value
= get_token_value(gwps
, &data
->tokens
[i
],
772 temp_buf
, sizeof(temp_buf
), NULL
);
776 while (*value
&& (buf
< linebuf_end
))
784 /* close the current alignment */
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
;
802 static void get_subline_timeout(struct gui_wps
*gwps
, struct skin_subline
*subline
)
804 struct wps_data
*data
= gwps
->data
;
806 subline
->time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
808 for (i
= subline
->first_token_idx
;
809 i
<= subline
->last_token_idx
; i
++)
811 switch(data
->tokens
[i
].type
)
813 case WPS_TOKEN_CONDITIONAL
:
814 /* place ourselves in the right conditional case */
815 evaluate_conditional(gwps
, &i
);
818 case WPS_TOKEN_CONDITIONAL_OPTION
:
819 /* we've finished in the curent conditional case,
820 skip to the end of the conditional structure */
821 i
= find_conditional_end(data
, i
);
824 case WPS_TOKEN_SUBLINE_TIMEOUT
:
825 subline
->time_mult
= data
->tokens
[i
].value
.i
;
834 /* Calculates which subline should be displayed for the specified line
835 Returns true iff the subline must be refreshed */
836 static bool update_curr_subline(struct gui_wps
*gwps
, struct skin_line
*line
)
838 /* shortcut this whole thing if we need to reset the line completly */
839 if (line
->curr_subline
== NULL
)
841 line
->subline_expire_time
= current_tick
;
842 line
->curr_subline
= &line
->sublines
;
843 if (!line
->curr_subline
->next
)
845 line
->subline_expire_time
+= 100*HZ
;
849 get_subline_timeout(gwps
, line
->curr_subline
);
850 line
->subline_expire_time
+= TIMEOUT_UNIT
*line
->curr_subline
->time_mult
;
854 /* if time to advance to next sub-line */
855 if (TIME_AFTER(current_tick
, line
->subline_expire_time
- 1))
857 /* if there is only one subline, there is no need to search for a new one */
858 if (&line
->sublines
== line
->curr_subline
&&
859 line
->curr_subline
->next
== NULL
)
861 line
->subline_expire_time
+= 100 * HZ
;
864 if (line
->curr_subline
->next
)
865 line
->curr_subline
= line
->curr_subline
->next
;
867 line
->curr_subline
= &line
->sublines
;
868 get_subline_timeout(gwps
, line
->curr_subline
);
869 line
->subline_expire_time
= current_tick
+ TIMEOUT_UNIT
*line
->curr_subline
->time_mult
;
875 /* Display a line appropriately according to its alignment format.
876 format_align contains the text, separated between left, center and right.
877 line is the index of the line on the screen.
878 scroll indicates whether the line is a scrolling one or not.
880 static void write_line(struct screen
*display
,
881 struct align_pos
*format_align
,
885 int left_width
= 0, left_xpos
;
886 int center_width
= 0, center_xpos
;
887 int right_width
= 0, right_xpos
;
893 /* calculate different string sizes and positions */
894 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
895 if (format_align
->left
!= 0) {
896 display
->getstringsize((unsigned char *)format_align
->left
,
897 &left_width
, &string_height
);
900 if (format_align
->right
!= 0) {
901 display
->getstringsize((unsigned char *)format_align
->right
,
902 &right_width
, &string_height
);
905 if (format_align
->center
!= 0) {
906 display
->getstringsize((unsigned char *)format_align
->center
,
907 ¢er_width
, &string_height
);
911 right_xpos
= (display
->getwidth() - right_width
);
912 center_xpos
= (display
->getwidth() + left_xpos
- center_width
) / 2;
914 scroll_width
= display
->getwidth() - left_xpos
;
916 /* Checks for overlapping strings.
917 If needed the overlapping strings will be merged, separated by a
920 /* CASE 1: left and centered string overlap */
921 /* there is a left string, need to merge left and center */
922 if ((left_width
!= 0 && center_width
!= 0) &&
923 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
924 /* replace the former separator '\0' of left and
925 center string with a space */
926 *(--format_align
->center
) = ' ';
927 /* calculate the new width and position of the merged string */
928 left_width
= left_width
+ space_width
+ center_width
;
929 /* there is no centered string anymore */
932 /* there is no left string, move center to left */
933 if ((left_width
== 0 && center_width
!= 0) &&
934 (left_xpos
+ left_width
> center_xpos
)) {
935 /* move the center string to the left string */
936 format_align
->left
= format_align
->center
;
937 /* calculate the new width and position of the string */
938 left_width
= center_width
;
939 /* there is no centered string anymore */
943 /* CASE 2: centered and right string overlap */
944 /* there is a right string, need to merge center and right */
945 if ((center_width
!= 0 && right_width
!= 0) &&
946 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
947 /* replace the former separator '\0' of center and
948 right string with a space */
949 *(--format_align
->right
) = ' ';
950 /* move the center string to the right after merge */
951 format_align
->right
= format_align
->center
;
952 /* calculate the new width and position of the merged string */
953 right_width
= center_width
+ space_width
+ right_width
;
954 right_xpos
= (display
->getwidth() - right_width
);
955 /* there is no centered string anymore */
958 /* there is no right string, move center to right */
959 if ((center_width
!= 0 && right_width
== 0) &&
960 (center_xpos
+ center_width
> right_xpos
)) {
961 /* move the center string to the right string */
962 format_align
->right
= format_align
->center
;
963 /* calculate the new width and position of the string */
964 right_width
= center_width
;
965 right_xpos
= (display
->getwidth() - right_width
);
966 /* there is no centered string anymore */
970 /* CASE 3: left and right overlap
971 There is no center string anymore, either there never
972 was one or it has been merged in case 1 or 2 */
973 /* there is a left string, need to merge left and right */
974 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
975 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
976 /* replace the former separator '\0' of left and
977 right string with a space */
978 *(--format_align
->right
) = ' ';
979 /* calculate the new width and position of the string */
980 left_width
= left_width
+ space_width
+ right_width
;
981 /* there is no right string anymore */
984 /* there is no left string, move right to left */
985 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
986 (left_width
> right_xpos
)) {
987 /* move the right string to the left string */
988 format_align
->left
= format_align
->right
;
989 /* calculate the new width and position of the string */
990 left_width
= right_width
;
991 /* there is no right string anymore */
995 ypos
= (line
* string_height
);
998 if (scroll
&& ((left_width
> scroll_width
) ||
999 (center_width
> scroll_width
) ||
1000 (right_width
> scroll_width
)))
1002 display
->puts_scroll(0, line
,
1003 (unsigned char *)format_align
->left
);
1007 #ifdef HAVE_LCD_BITMAP
1008 /* clear the line first */
1009 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1010 display
->fillrect(left_xpos
, ypos
, display
->getwidth(), string_height
);
1011 display
->set_drawmode(DRMODE_SOLID
);
1014 /* Nasty hack: we output an empty scrolling string,
1015 which will reset the scroller for that line */
1016 display
->puts_scroll(0, line
, (unsigned char *)"");
1018 /* print aligned strings */
1019 if (left_width
!= 0)
1021 display
->putsxy(left_xpos
, ypos
,
1022 (unsigned char *)format_align
->left
);
1024 if (center_width
!= 0)
1026 display
->putsxy(center_xpos
, ypos
,
1027 (unsigned char *)format_align
->center
);
1029 if (right_width
!= 0)
1031 display
->putsxy(right_xpos
, ypos
,
1032 (unsigned char *)format_align
->right
);
1037 static bool skin_redraw(struct gui_wps
*gwps
, unsigned refresh_mode
)
1039 struct wps_data
*data
= gwps
->data
;
1040 struct screen
*display
= gwps
->display
;
1042 if (!data
|| !display
|| !gwps
->state
)
1046 char linebuf
[MAX_PATH
];
1048 struct align_pos align
;
1050 align
.center
= NULL
;
1054 struct skin_token_list
*viewport_list
;
1056 bool update_line
, new_subline_refresh
;
1058 #ifdef HAVE_LCD_BITMAP
1060 /* to find out wether the peak meter is enabled we
1061 assume it wasn't until we find a line that contains
1062 the peak meter. We can't use peak_meter_enabled itself
1063 because that would mean to turn off the meter thread
1064 temporarily. (That shouldn't matter unless yield
1065 or sleep is called but who knows...)
1067 bool enable_pm
= false;
1071 /* reset to first subline if refresh all flag is set */
1072 if (refresh_mode
== WPS_REFRESH_ALL
)
1074 struct skin_line
*line
;
1075 struct skin_viewport
*skin_viewport
= find_viewport(VP_DEFAULT_LABEL
, data
);
1077 if (!(skin_viewport
->hidden_flags
& VP_NEVER_VISIBLE
))
1079 display
->set_viewport(&skin_viewport
->vp
);
1080 display
->clear_viewport();
1083 for (viewport_list
= data
->viewports
;
1084 viewport_list
; viewport_list
= viewport_list
->next
)
1087 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1088 for(line
= skin_viewport
->lines
; line
; line
= line
->next
)
1090 line
->curr_subline
= NULL
;
1095 #ifdef HAVE_LCD_CHARCELLS
1097 for (i
= 0; i
< 8; i
++)
1099 if (data
->wps_progress_pat
[i
] == 0)
1100 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1104 /* disable any viewports which are conditionally displayed.
1105 * If we are only refreshing the peak meter then don't change the viewport
1106 * enabled flags as this will stop scrolling. viewports cant be
1107 * toggled in this refresh mode anyway (FS#10215)*/
1108 if (refresh_mode
!= WPS_REFRESH_PEAK_METER
)
1110 for (viewport_list
= data
->viewports
;
1111 viewport_list
; viewport_list
= viewport_list
->next
)
1113 struct skin_viewport
*skin_viewport
=
1114 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1115 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
1119 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDEABLE
)
1121 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
)
1122 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1124 skin_viewport
->hidden_flags
|= VP_DRAW_HIDDEN
;
1128 int viewport_count
= 0;
1129 for (viewport_list
= data
->viewports
;
1130 viewport_list
; viewport_list
= viewport_list
->next
, viewport_count
++)
1132 struct skin_viewport
*skin_viewport
=
1133 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1134 unsigned vp_refresh_mode
= refresh_mode
;
1136 display
->set_viewport(&skin_viewport
->vp
);
1140 #ifdef HAVE_LCD_BITMAP
1141 /* Set images to not to be displayed */
1142 struct skin_token_list
*imglist
= data
->images
;
1145 struct gui_img
*img
= (struct gui_img
*)imglist
->token
->value
.data
;
1147 imglist
= imglist
->next
;
1150 /* dont redraw the viewport if its disabled */
1151 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
1152 { /* don't draw anything into this one */
1153 vp_refresh_mode
= 0; hidden_vp
= true;
1155 else if ((skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
))
1157 if (!(skin_viewport
->hidden_flags
&VP_DRAW_WASHIDDEN
))
1158 display
->scroll_stop(&skin_viewport
->vp
);
1159 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1162 else if (((skin_viewport
->hidden_flags
&
1163 (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
))
1164 == (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
)))
1166 vp_refresh_mode
= WPS_REFRESH_ALL
;
1167 skin_viewport
->hidden_flags
= VP_DRAW_HIDEABLE
;
1170 if (vp_refresh_mode
== WPS_REFRESH_ALL
)
1172 display
->clear_viewport();
1175 /* loop over the lines for this viewport */
1176 struct skin_line
*line
;
1179 for (line
= skin_viewport
->lines
; line
; line
= line
->next
, line_count
++)
1181 struct skin_subline
*subline
;
1182 memset(linebuf
, 0, sizeof(linebuf
));
1183 update_line
= false;
1185 /* get current subline for the line */
1186 new_subline_refresh
= update_curr_subline(gwps
, line
);
1187 subline
= line
->curr_subline
;
1188 flags
= line
->curr_subline
->line_type
;
1190 if (vp_refresh_mode
== WPS_REFRESH_ALL
|| (flags
& vp_refresh_mode
)
1191 || new_subline_refresh
|| hidden_vp
)
1193 /* get_line tells us if we need to update the line */
1194 update_line
= get_line(gwps
, subline
, &align
,
1195 linebuf
, sizeof(linebuf
), vp_refresh_mode
);
1197 #ifdef HAVE_LCD_BITMAP
1199 if (flags
& vp_refresh_mode
& WPS_REFRESH_PEAK_METER
)
1201 /* the peakmeter should be alone on its line */
1202 update_line
= false;
1204 int h
= font_get(skin_viewport
->vp
.font
)->height
;
1205 int peak_meter_y
= line_count
* h
;
1207 /* The user might decide to have the peak meter in the last
1208 line so that it is only displayed if no status bar is
1209 visible. If so we neither want do draw nor enable the
1211 if (peak_meter_y
+ h
<= skin_viewport
->vp
.y
+skin_viewport
->vp
.height
) {
1212 /* found a line with a peak meter -> remember that we must
1215 peak_meter_enabled
= true;
1216 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
1217 MIN(h
, skin_viewport
->vp
.y
+skin_viewport
->vp
.height
- peak_meter_y
));
1221 peak_meter_enabled
= false;
1225 #else /* HAVE_LCD_CHARCELL */
1228 if (flags
& vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1230 if (data
->full_line_progressbar
)
1231 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
1233 draw_player_progress(gwps
);
1237 if (update_line
&& !hidden_vp
&&
1238 /* conditionals clear the line which means if the %Vd is put into the default
1239 viewport there will be a blank line.
1240 To get around this we dont allow any actual drawing to happen in the
1241 deault vp if other vp's are defined */
1242 ((skin_viewport
->label
!= VP_DEFAULT_LABEL
&& viewport_list
->next
) ||
1243 !viewport_list
->next
))
1245 if (flags
& WPS_REFRESH_SCROLL
)
1247 /* if the line is a scrolling one we don't want to update
1248 too often, so that it has the time to scroll */
1249 if ((vp_refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
1250 write_line(display
, &align
, line_count
, true);
1253 write_line(display
, &align
, line_count
, false);
1256 #ifdef HAVE_LCD_BITMAP
1258 if (vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1260 struct skin_token_list
*bar
= gwps
->data
->progressbars
;
1263 struct progressbar
*thisbar
= (struct progressbar
*)bar
->token
->value
.data
;
1264 if (thisbar
->vp
== &skin_viewport
->vp
)
1266 draw_progressbar(gwps
, thisbar
);
1271 /* Now display any images in this viewport */
1273 wps_display_images(gwps
, &skin_viewport
->vp
);
1277 #ifdef HAVE_LCD_BITMAP
1278 data
->peak_meter_enabled
= enable_pm
;
1280 /* Restore the default viewport */
1281 display
->set_viewport(NULL
);