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"
30 #include "powermgmt.h"
37 #include "statusbar.h"
39 #include "scrollbar.h"
40 #include "screen_access.h"
44 #ifdef HAVE_LCD_BITMAP
45 #include "peakmeter.h"
54 #if CONFIG_CODEC == SWCODEC
61 #include "wps_internals.h"
62 #include "skin_engine.h"
63 #include "statusbar-skinned.h"
65 static bool skin_redraw(struct gui_wps
*gwps
, unsigned refresh_mode
);
67 /* update a skinned screen, update_type is WPS_REFRESH_* values.
68 * Usually it should only be WPS_REFRESH_NON_STATIC
69 * A full update will be done if required (state.do_full_update == true)
71 bool skin_update(struct gui_wps
*gwps
, unsigned int update_type
)
74 /* This maybe shouldnt be here, but while the skin is only used to
75 * display the music screen this is better than whereever we are being
76 * called from. This is also safe for skined screen which dont use the id3 */
77 struct mp3entry
*id3
= gwps
->state
->id3
;
78 bool cuesheet_update
= (id3
!= NULL
? cuesheet_subtrack_changed(id3
) : false);
79 gwps
->sync_data
->do_full_update
|= cuesheet_update
;
81 retval
= skin_redraw(gwps
, gwps
->sync_data
->do_full_update
?
82 WPS_REFRESH_ALL
: update_type
);
86 #ifdef HAVE_LCD_BITMAP
88 void skin_statusbar_changed(struct gui_wps
*skin
)
92 struct wps_data
*data
= skin
->data
;
93 const struct screen
*display
= skin
->display
;
94 const int screen
= display
->screen_type
;
96 struct viewport
*vp
= &find_viewport(VP_DEFAULT_LABEL
, data
)->vp
;
97 viewport_set_defaults(vp
, screen
);
100 { /* fix up the default viewport */
101 if (data
->show_sb_on_wps
)
103 if (statusbar_position(screen
) != STATUSBAR_OFF
)
104 return; /* vp is fixed already */
106 vp
->y
= STATUSBAR_HEIGHT
;
107 vp
->height
= display
->lcdheight
- STATUSBAR_HEIGHT
;
111 if (statusbar_position(screen
) == STATUSBAR_OFF
)
112 return; /* vp is fixed already */
114 vp
->height
= display
->lcdheight
;
115 vp
->width
= display
->lcdwidth
;
120 static void draw_progressbar(struct gui_wps
*gwps
,
121 struct progressbar
*pb
)
123 struct screen
*display
= gwps
->display
;
124 struct viewport
*vp
= pb
->vp
;
125 struct wps_state
*state
= gwps
->state
;
126 struct mp3entry
*id3
= state
->id3
;
127 int y
= pb
->y
, height
= pb
->height
;
128 unsigned long length
, elapsed
;
131 height
= font_get(vp
->font
)->height
;
135 int line_height
= font_get(vp
->font
)->height
;
136 /* center the pb in the line, but only if the line is higher than the pb */
137 int center
= (line_height
-height
)/2;
138 /* if Y was not set calculate by font height,Y is -line_number-1 */
139 y
= (-y
-1)*line_height
+ (0 > center
? 0 : center
);
142 if (pb
->type
== WPS_TOKEN_VOLUMEBAR
)
144 int minvol
= sound_min(SOUND_VOLUME
);
145 int maxvol
= sound_max(SOUND_VOLUME
);
146 length
= maxvol
-minvol
;
147 elapsed
= global_settings
.volume
-minvol
;
149 else if (pb
->type
== WPS_TOKEN_BATTERY_PERCENTBAR
)
152 elapsed
= battery_level();
154 else if (id3
&& id3
->length
)
156 length
= id3
->length
;
157 elapsed
= id3
->elapsed
+ state
->ff_rewind_count
;
165 if (pb
->have_bitmap_pb
)
166 gui_bitmap_scrollbar_draw(display
, pb
->bm
,
167 pb
->x
, y
, pb
->width
, pb
->bm
.height
,
168 length
, 0, elapsed
, HORIZONTAL
);
170 gui_scrollbar_draw(display
, pb
->x
, y
, pb
->width
, height
,
171 length
, 0, elapsed
, HORIZONTAL
);
173 if (pb
->type
== WPS_TOKEN_PROGRESSBAR
&& id3
&& id3
->length
)
175 #ifdef AB_REPEAT_ENABLE
176 if (ab_repeat_mode_enabled())
177 ab_draw_markers(display
, id3
->length
,
178 pb
->x
, y
, pb
->width
, height
);
182 cue_draw_markers(display
, id3
->cuesheet
, id3
->length
,
183 pb
->x
, y
+1, pb
->width
, height
-2);
187 static void draw_playlist_viewer_list(struct gui_wps
*gwps
,
188 struct playlistviewer
*viewer
)
190 struct wps_state
*state
= gwps
->state
;
191 int lines
= viewport_get_nb_lines(viewer
->vp
);
192 int line_height
= font_get(viewer
->vp
->font
)->height
;
193 int cur_playlist_pos
= playlist_get_display_index();
194 int start_item
= MAX(0, cur_playlist_pos
+ viewer
->start_offset
);
196 struct wps_token token
;
197 int x
, length
, alignment
= WPS_TOKEN_ALIGN_LEFT
;
199 struct mp3entry
*pid3
;
200 char buf
[MAX_PATH
*2], tempbuf
[MAX_PATH
];
202 gwps
->display
->set_viewport(viewer
->vp
);
203 for(i
=start_item
; (i
-start_item
)<lines
&& i
<=playlist_amount(); i
++)
205 if (i
== cur_playlist_pos
)
209 else if (i
== cur_playlist_pos
+1)
213 #if CONFIG_CODEC == SWCODEC
214 else if (i
>cur_playlist_pos
)
216 if (!audio_peek_track(&pid3
, i
-cur_playlist_pos
))
225 int line
= pid3
? TRACK_HAS_INFO
: TRACK_HAS_NO_INFO
;
226 int j
= 0, cur_string
= 0;
227 char *filename
= playlist_peek(i
-cur_playlist_pos
);
228 unsigned int line_len
= 0;
230 while (j
< viewer
->lines
[line
].count
&& line_len
< sizeof(buf
))
232 const char *out
= NULL
;
233 token
.type
= viewer
->lines
[line
].tokens
[j
];
236 out
= get_id3_token(&token
, pid3
, tempbuf
, sizeof(tempbuf
), -1, NULL
);
239 line_len
= strlcat(buf
, out
, sizeof(buf
));
243 switch (viewer
->lines
[line
].tokens
[j
])
245 case WPS_TOKEN_ALIGN_CENTER
:
246 case WPS_TOKEN_ALIGN_LEFT
:
247 case WPS_TOKEN_ALIGN_LEFT_RTL
:
248 case WPS_TOKEN_ALIGN_RIGHT
:
249 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
250 alignment
= viewer
->lines
[line
].tokens
[j
];
253 case WPS_TOKEN_STRING
:
254 case WPS_TOKEN_CHARACTER
:
255 snprintf(tempbuf
, sizeof(tempbuf
), "%s",
256 viewer
->lines
[line
].strings
[cur_string
]);
259 case WPS_TOKEN_PLAYLIST_POSITION
:
260 snprintf(tempbuf
, sizeof(tempbuf
), "%d", i
);
262 case WPS_TOKEN_FILE_NAME
:
263 get_dir(tempbuf
, sizeof(tempbuf
), filename
, 0);
265 case WPS_TOKEN_FILE_PATH
:
266 snprintf(tempbuf
, sizeof(tempbuf
), "%s", filename
);
274 line_len
= strlcat(buf
, tempbuf
, sizeof(buf
));
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
));
617 else if (data
->tokens
[i
].type
== WPS_TOKEN_VOLUMEBAR
||
618 data
->tokens
[i
].type
== WPS_TOKEN_PROGRESSBAR
||
619 data
->tokens
[i
].type
== WPS_TOKEN_BATTERY_PERCENTBAR
)
621 struct progressbar
*bar
= (struct progressbar
*)data
->tokens
[i
].value
.data
;
626 if (data
->albumart
&& data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
629 playback_current_aa_hid(data
->playback_aa_slot
), true);
630 data
->albumart
->draw
= false;
639 /* Read a (sub)line to the given alignment format buffer.
640 linebuf is the buffer where the data is actually stored.
641 align is the alignment format that'll be used to display the text.
642 The return value indicates whether the line needs to be updated.
644 static bool get_line(struct gui_wps
*gwps
,
645 struct skin_subline
*subline
,
646 struct align_pos
*align
,
649 unsigned refresh_mode
)
651 struct wps_data
*data
= gwps
->data
;
654 char *buf
= linebuf
; /* will always point to the writing position */
655 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
658 (void)refresh_mode
; /* silence warning on charcell */
660 /* alignment-related variables */
662 char* cur_align_start
;
663 cur_align_start
= buf
;
664 cur_align
= WPS_ALIGN_LEFT
;
666 align
->center
= NULL
;
668 /* Process all tokens of the desired subline */
669 for (i
= subline
->first_token_idx
;
670 i
<= subline
->last_token_idx
; i
++)
672 switch(data
->tokens
[i
].type
)
674 case WPS_TOKEN_CONDITIONAL
:
675 /* place ourselves in the right conditional case */
676 update
|= evaluate_conditional(gwps
, &i
);
679 case WPS_TOKEN_CONDITIONAL_OPTION
:
680 /* we've finished in the curent conditional case,
681 skip to the end of the conditional structure */
682 i
= find_conditional_end(data
, i
);
685 #ifdef HAVE_LCD_BITMAP
686 case WPS_TOKEN_VOLUMEBAR
:
687 case WPS_TOKEN_BATTERY_PERCENTBAR
:
688 case WPS_TOKEN_PROGRESSBAR
:
690 struct progressbar
*bar
= (struct progressbar
*)data
->tokens
[i
].value
.data
;
694 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
696 char n
= data
->tokens
[i
].value
.i
& 0xFF;
697 int subimage
= data
->tokens
[i
].value
.i
>> 8;
698 struct gui_img
*img
= find_image(n
, data
);
700 if (img
&& img
->loaded
)
701 img
->display
= subimage
;
704 case WPS_TOKEN_DRAW_INBUILTBAR
:
705 gui_statusbar_draw(&(statusbars
.statusbars
[gwps
->display
->screen_type
]),
706 refresh_mode
== WPS_REFRESH_ALL
,
707 data
->tokens
[i
].value
.data
);
711 case WPS_TOKEN_ALIGN_LEFT
:
712 case WPS_TOKEN_ALIGN_LEFT_RTL
:
713 case WPS_TOKEN_ALIGN_CENTER
:
714 case WPS_TOKEN_ALIGN_RIGHT
:
715 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
716 /* remember where the current aligned text started */
720 align
->left
= cur_align_start
;
723 case WPS_ALIGN_CENTER
:
724 align
->center
= cur_align_start
;
727 case WPS_ALIGN_RIGHT
:
728 align
->right
= cur_align_start
;
731 /* start a new alignment */
732 switch (data
->tokens
[i
].type
)
734 case WPS_TOKEN_ALIGN_LEFT
:
735 cur_align
= WPS_ALIGN_LEFT
;
737 case WPS_TOKEN_ALIGN_LEFT_RTL
:
738 cur_align
= lang_is_rtl() ? WPS_ALIGN_RIGHT
:
741 case WPS_TOKEN_ALIGN_CENTER
:
742 cur_align
= WPS_ALIGN_CENTER
;
744 case WPS_TOKEN_ALIGN_RIGHT
:
745 cur_align
= WPS_ALIGN_RIGHT
;
747 case WPS_TOKEN_ALIGN_RIGHT_RTL
:
748 cur_align
= lang_is_rtl() ? WPS_ALIGN_LEFT
:
755 cur_align_start
= buf
;
757 case WPS_VIEWPORT_ENABLE
:
759 char label
= data
->tokens
[i
].value
.i
;
760 char temp
= VP_DRAW_HIDEABLE
;
761 /* viewports are allowed to share id's so find and enable
763 struct skin_token_list
*list
= data
->viewports
;
766 struct skin_viewport
*vp
=
767 (struct skin_viewport
*)list
->token
->value
.data
;
768 if (vp
->label
== label
)
770 if (vp
->hidden_flags
&VP_DRAW_WASHIDDEN
)
771 temp
|= VP_DRAW_WASHIDDEN
;
772 vp
->hidden_flags
= temp
;
778 #ifdef HAVE_LCD_BITMAP
779 case WPS_VIEWPORT_CUSTOMLIST
:
780 draw_playlist_viewer_list(gwps
, data
->tokens
[i
].value
.data
);
785 /* get the value of the tag and copy it to the buffer */
786 const char *value
= get_token_value(gwps
, &data
->tokens
[i
],
787 temp_buf
, sizeof(temp_buf
), NULL
);
791 while (*value
&& (buf
< linebuf_end
))
799 /* close the current alignment */
803 align
->left
= cur_align_start
;
806 case WPS_ALIGN_CENTER
:
807 align
->center
= cur_align_start
;
810 case WPS_ALIGN_RIGHT
:
811 align
->right
= cur_align_start
;
817 static void get_subline_timeout(struct gui_wps
*gwps
, struct skin_subline
*subline
)
819 struct wps_data
*data
= gwps
->data
;
821 subline
->time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
823 for (i
= subline
->first_token_idx
;
824 i
<= subline
->last_token_idx
; i
++)
826 switch(data
->tokens
[i
].type
)
828 case WPS_TOKEN_CONDITIONAL
:
829 /* place ourselves in the right conditional case */
830 evaluate_conditional(gwps
, &i
);
833 case WPS_TOKEN_CONDITIONAL_OPTION
:
834 /* we've finished in the curent conditional case,
835 skip to the end of the conditional structure */
836 i
= find_conditional_end(data
, i
);
839 case WPS_TOKEN_SUBLINE_TIMEOUT
:
840 subline
->time_mult
= data
->tokens
[i
].value
.i
;
849 /* Calculates which subline should be displayed for the specified line
850 Returns true iff the subline must be refreshed */
851 static bool update_curr_subline(struct gui_wps
*gwps
, struct skin_line
*line
)
853 /* shortcut this whole thing if we need to reset the line completly */
854 if (line
->curr_subline
== NULL
)
856 line
->subline_expire_time
= current_tick
;
857 line
->curr_subline
= &line
->sublines
;
858 if (!line
->curr_subline
->next
)
860 line
->subline_expire_time
+= 100*HZ
;
864 get_subline_timeout(gwps
, line
->curr_subline
);
865 line
->subline_expire_time
+= TIMEOUT_UNIT
*line
->curr_subline
->time_mult
;
869 /* if time to advance to next sub-line */
870 if (TIME_AFTER(current_tick
, line
->subline_expire_time
- 1))
872 /* if there is only one subline, there is no need to search for a new one */
873 if (&line
->sublines
== line
->curr_subline
&&
874 line
->curr_subline
->next
== NULL
)
876 line
->subline_expire_time
+= 100 * HZ
;
879 if (line
->curr_subline
->next
)
880 line
->curr_subline
= line
->curr_subline
->next
;
882 line
->curr_subline
= &line
->sublines
;
883 get_subline_timeout(gwps
, line
->curr_subline
);
884 line
->subline_expire_time
= current_tick
+ TIMEOUT_UNIT
*line
->curr_subline
->time_mult
;
890 /* Display a line appropriately according to its alignment format.
891 format_align contains the text, separated between left, center and right.
892 line is the index of the line on the screen.
893 scroll indicates whether the line is a scrolling one or not.
895 static void write_line(struct screen
*display
,
896 struct align_pos
*format_align
,
900 int left_width
= 0, left_xpos
;
901 int center_width
= 0, center_xpos
;
902 int right_width
= 0, right_xpos
;
908 /* calculate different string sizes and positions */
909 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
910 if (format_align
->left
!= 0) {
911 display
->getstringsize((unsigned char *)format_align
->left
,
912 &left_width
, &string_height
);
915 if (format_align
->right
!= 0) {
916 display
->getstringsize((unsigned char *)format_align
->right
,
917 &right_width
, &string_height
);
920 if (format_align
->center
!= 0) {
921 display
->getstringsize((unsigned char *)format_align
->center
,
922 ¢er_width
, &string_height
);
926 right_xpos
= (display
->getwidth() - right_width
);
927 center_xpos
= (display
->getwidth() + left_xpos
- center_width
) / 2;
929 scroll_width
= display
->getwidth() - left_xpos
;
931 /* Checks for overlapping strings.
932 If needed the overlapping strings will be merged, separated by a
935 /* CASE 1: left and centered string overlap */
936 /* there is a left string, need to merge left and center */
937 if ((left_width
!= 0 && center_width
!= 0) &&
938 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
939 /* replace the former separator '\0' of left and
940 center string with a space */
941 *(--format_align
->center
) = ' ';
942 /* calculate the new width and position of the merged string */
943 left_width
= left_width
+ space_width
+ center_width
;
944 /* there is no centered string anymore */
947 /* there is no left string, move center to left */
948 if ((left_width
== 0 && center_width
!= 0) &&
949 (left_xpos
+ left_width
> center_xpos
)) {
950 /* move the center string to the left string */
951 format_align
->left
= format_align
->center
;
952 /* calculate the new width and position of the string */
953 left_width
= center_width
;
954 /* there is no centered string anymore */
958 /* CASE 2: centered and right string overlap */
959 /* there is a right string, need to merge center and right */
960 if ((center_width
!= 0 && right_width
!= 0) &&
961 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
962 /* replace the former separator '\0' of center and
963 right string with a space */
964 *(--format_align
->right
) = ' ';
965 /* move the center string to the right after merge */
966 format_align
->right
= format_align
->center
;
967 /* calculate the new width and position of the merged string */
968 right_width
= center_width
+ space_width
+ right_width
;
969 right_xpos
= (display
->getwidth() - right_width
);
970 /* there is no centered string anymore */
973 /* there is no right string, move center to right */
974 if ((center_width
!= 0 && right_width
== 0) &&
975 (center_xpos
+ center_width
> right_xpos
)) {
976 /* move the center string to the right string */
977 format_align
->right
= format_align
->center
;
978 /* calculate the new width and position of the string */
979 right_width
= center_width
;
980 right_xpos
= (display
->getwidth() - right_width
);
981 /* there is no centered string anymore */
985 /* CASE 3: left and right overlap
986 There is no center string anymore, either there never
987 was one or it has been merged in case 1 or 2 */
988 /* there is a left string, need to merge left and right */
989 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
990 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
991 /* replace the former separator '\0' of left and
992 right string with a space */
993 *(--format_align
->right
) = ' ';
994 /* calculate the new width and position of the string */
995 left_width
= left_width
+ space_width
+ right_width
;
996 /* there is no right string anymore */
999 /* there is no left string, move right to left */
1000 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
1001 (left_width
> right_xpos
)) {
1002 /* move the right string to the left string */
1003 format_align
->left
= format_align
->right
;
1004 /* calculate the new width and position of the string */
1005 left_width
= right_width
;
1006 /* there is no right string anymore */
1010 ypos
= (line
* string_height
);
1013 if (scroll
&& ((left_width
> scroll_width
) ||
1014 (center_width
> scroll_width
) ||
1015 (right_width
> scroll_width
)))
1017 display
->puts_scroll(0, line
,
1018 (unsigned char *)format_align
->left
);
1022 #ifdef HAVE_LCD_BITMAP
1023 /* clear the line first */
1024 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1025 display
->fillrect(left_xpos
, ypos
, display
->getwidth(), string_height
);
1026 display
->set_drawmode(DRMODE_SOLID
);
1029 /* Nasty hack: we output an empty scrolling string,
1030 which will reset the scroller for that line */
1031 display
->puts_scroll(0, line
, (unsigned char *)"");
1033 /* print aligned strings */
1034 if (left_width
!= 0)
1036 display
->putsxy(left_xpos
, ypos
,
1037 (unsigned char *)format_align
->left
);
1039 if (center_width
!= 0)
1041 display
->putsxy(center_xpos
, ypos
,
1042 (unsigned char *)format_align
->center
);
1044 if (right_width
!= 0)
1046 display
->putsxy(right_xpos
, ypos
,
1047 (unsigned char *)format_align
->right
);
1052 static bool skin_redraw(struct gui_wps
*gwps
, unsigned refresh_mode
)
1054 struct wps_data
*data
= gwps
->data
;
1055 struct screen
*display
= gwps
->display
;
1057 if (!data
|| !display
|| !gwps
->state
)
1061 char linebuf
[MAX_PATH
];
1063 struct align_pos align
;
1065 align
.center
= NULL
;
1069 struct skin_token_list
*viewport_list
;
1071 bool update_line
, new_subline_refresh
;
1073 #ifdef HAVE_LCD_BITMAP
1075 /* to find out wether the peak meter is enabled we
1076 assume it wasn't until we find a line that contains
1077 the peak meter. We can't use peak_meter_enabled itself
1078 because that would mean to turn off the meter thread
1079 temporarily. (That shouldn't matter unless yield
1080 or sleep is called but who knows...)
1082 bool enable_pm
= false;
1086 /* reset to first subline if refresh all flag is set */
1087 if (refresh_mode
== WPS_REFRESH_ALL
)
1089 struct skin_line
*line
;
1090 struct skin_viewport
*skin_viewport
= find_viewport(VP_DEFAULT_LABEL
, data
);
1092 if (!(skin_viewport
->hidden_flags
& VP_NEVER_VISIBLE
))
1094 display
->set_viewport(&skin_viewport
->vp
);
1095 display
->clear_viewport();
1098 for (viewport_list
= data
->viewports
;
1099 viewport_list
; viewport_list
= viewport_list
->next
)
1102 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1103 for(line
= skin_viewport
->lines
; line
; line
= line
->next
)
1105 line
->curr_subline
= NULL
;
1110 #ifdef HAVE_LCD_CHARCELLS
1112 for (i
= 0; i
< 8; i
++)
1114 if (data
->wps_progress_pat
[i
] == 0)
1115 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1119 /* disable any viewports which are conditionally displayed.
1120 * If we are only refreshing the peak meter then don't change the viewport
1121 * enabled flags as this will stop scrolling. viewports cant be
1122 * toggled in this refresh mode anyway (FS#10215)*/
1123 if (refresh_mode
!= WPS_REFRESH_PEAK_METER
)
1125 for (viewport_list
= data
->viewports
;
1126 viewport_list
; viewport_list
= viewport_list
->next
)
1128 struct skin_viewport
*skin_viewport
=
1129 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1130 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
1134 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDEABLE
)
1136 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
)
1137 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1139 skin_viewport
->hidden_flags
|= VP_DRAW_HIDDEN
;
1143 int viewport_count
= 0;
1144 for (viewport_list
= data
->viewports
;
1145 viewport_list
; viewport_list
= viewport_list
->next
, viewport_count
++)
1147 struct skin_viewport
*skin_viewport
=
1148 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1149 unsigned vp_refresh_mode
= refresh_mode
;
1151 display
->set_viewport(&skin_viewport
->vp
);
1155 #ifdef HAVE_LCD_BITMAP
1156 /* Set images to not to be displayed */
1157 struct skin_token_list
*imglist
= data
->images
;
1160 struct gui_img
*img
= (struct gui_img
*)imglist
->token
->value
.data
;
1162 imglist
= imglist
->next
;
1165 /* dont redraw the viewport if its disabled */
1166 if (skin_viewport
->hidden_flags
&VP_NEVER_VISIBLE
)
1167 { /* don't draw anything into this one */
1168 vp_refresh_mode
= 0; hidden_vp
= true;
1170 else if ((skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
))
1172 if (!(skin_viewport
->hidden_flags
&VP_DRAW_WASHIDDEN
))
1173 display
->scroll_stop(&skin_viewport
->vp
);
1174 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1177 else if (((skin_viewport
->hidden_flags
&
1178 (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
))
1179 == (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
)))
1181 vp_refresh_mode
= WPS_REFRESH_ALL
;
1182 skin_viewport
->hidden_flags
= VP_DRAW_HIDEABLE
;
1185 if (vp_refresh_mode
== WPS_REFRESH_ALL
)
1187 display
->clear_viewport();
1190 /* loop over the lines for this viewport */
1191 struct skin_line
*line
;
1194 for (line
= skin_viewport
->lines
; line
; line
= line
->next
, line_count
++)
1196 struct skin_subline
*subline
;
1197 memset(linebuf
, 0, sizeof(linebuf
));
1198 update_line
= false;
1200 /* get current subline for the line */
1201 new_subline_refresh
= update_curr_subline(gwps
, line
);
1202 subline
= line
->curr_subline
;
1203 flags
= line
->curr_subline
->line_type
;
1205 if (vp_refresh_mode
== WPS_REFRESH_ALL
|| (flags
& vp_refresh_mode
)
1206 || new_subline_refresh
|| hidden_vp
)
1208 /* get_line tells us if we need to update the line */
1209 update_line
= get_line(gwps
, subline
, &align
,
1210 linebuf
, sizeof(linebuf
), vp_refresh_mode
);
1212 #ifdef HAVE_LCD_BITMAP
1214 if (flags
& vp_refresh_mode
& WPS_REFRESH_PEAK_METER
)
1216 /* the peakmeter should be alone on its line */
1217 update_line
= false;
1219 int h
= font_get(skin_viewport
->vp
.font
)->height
;
1220 int peak_meter_y
= line_count
* h
;
1222 /* The user might decide to have the peak meter in the last
1223 line so that it is only displayed if no status bar is
1224 visible. If so we neither want do draw nor enable the
1226 if (peak_meter_y
+ h
<= skin_viewport
->vp
.y
+skin_viewport
->vp
.height
) {
1227 /* found a line with a peak meter -> remember that we must
1230 peak_meter_enabled
= true;
1231 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
1232 MIN(h
, skin_viewport
->vp
.y
+skin_viewport
->vp
.height
- peak_meter_y
));
1236 peak_meter_enabled
= false;
1240 #else /* HAVE_LCD_CHARCELL */
1243 if (flags
& vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1245 if (data
->full_line_progressbar
)
1246 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
1248 draw_player_progress(gwps
);
1252 if (update_line
&& !hidden_vp
&&
1253 /* conditionals clear the line which means if the %Vd is put into the default
1254 viewport there will be a blank line.
1255 To get around this we dont allow any actual drawing to happen in the
1256 deault vp if other vp's are defined */
1257 ((skin_viewport
->label
!= VP_DEFAULT_LABEL
&& viewport_list
->next
) ||
1258 !viewport_list
->next
))
1260 if (flags
& WPS_REFRESH_SCROLL
)
1262 /* if the line is a scrolling one we don't want to update
1263 too often, so that it has the time to scroll */
1264 if ((vp_refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
1265 write_line(display
, &align
, line_count
, true);
1268 write_line(display
, &align
, line_count
, false);
1271 #ifdef HAVE_LCD_BITMAP
1273 if (vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1275 struct skin_token_list
*bar
= gwps
->data
->progressbars
;
1278 struct progressbar
*thisbar
= (struct progressbar
*)bar
->token
->value
.data
;
1279 if (thisbar
->vp
== &skin_viewport
->vp
&& thisbar
->draw
)
1281 draw_progressbar(gwps
, thisbar
);
1286 /* Now display any images in this viewport */
1288 wps_display_images(gwps
, &skin_viewport
->vp
);
1292 #ifdef HAVE_LCD_BITMAP
1293 data
->peak_meter_enabled
= enable_pm
;
1295 /* Restore the default viewport */
1296 display
->set_viewport(NULL
);