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 "settings_list.h"
29 #include "rbunicode.h"
34 #include "powermgmt.h"
37 #ifdef HAVE_LCD_CHARCELLS
41 #include "mp3_playback.h"
45 #include "scrollbar.h"
48 #ifdef HAVE_LCD_BITMAP
49 #include "peakmeter.h"
58 #if CONFIG_CODEC == SWCODEC
65 #include "wps_internals.h"
66 #include "skin_engine.h"
68 static bool skin_redraw(struct gui_wps
*gwps
, unsigned refresh_mode
);
71 /* initial setup of wps_data */
72 void skin_data_init(struct wps_data
*wps_data
)
74 #ifdef HAVE_LCD_BITMAP
75 wps_data
->wps_sb_tag
= false;
76 wps_data
->show_sb_on_wps
= false;
77 wps_data
->peak_meter_enabled
= false;
78 wps_data
->images
= NULL
;
79 wps_data
->progressbars
= NULL
;
81 #else /* HAVE_LCD_CHARCELLS */
83 for (i
= 0; i
< 8; i
++)
85 wps_data
->wps_progress_pat
[i
] = 0;
87 wps_data
->full_line_progressbar
= false;
89 wps_data
->button_time_volume
= 0;
90 wps_data
->wps_loaded
= false;
93 /* TODO: maybe move this whole function into wps.c instead ? */
94 bool gui_wps_display(struct gui_wps
*gwps
)
96 struct screen
*display
= gwps
->display
;
98 /* Update the values in the first (default) viewport - in case the user
99 has modified the statusbar or colour settings */
101 if (display
->depth
> 1)
103 struct viewport
*vp
= &find_viewport(VP_DEFAULT_LABEL
, gwps
->data
)->vp
;
104 vp
->fg_pattern
= display
->get_foreground();
105 vp
->bg_pattern
= display
->get_background();
108 display
->clear_display();
109 display
->backdrop_show(BACKDROP_SKIN_WPS
);
110 return skin_redraw(gwps
, WPS_REFRESH_ALL
);
113 /* update a skinned screen, update_type is WPS_REFRESH_* values.
114 * Usually it should only be WPS_REFRESH_NON_STATIC
115 * A full update will be done if required (state.do_full_update == true)
117 bool skin_update(struct gui_wps
*gwps
, unsigned int update_type
)
120 /* This maybe shouldnt be here, but while the skin is only used to
121 * display the music screen this is better than whereever we are being
122 * called from. This is also safe for skined screen which dont use the id3 */
123 struct mp3entry
*id3
= gwps
->state
->id3
;
124 bool cuesheet_update
= (id3
!= NULL
? cuesheet_subtrack_changed(id3
) : false);
125 gwps
->state
->do_full_update
= cuesheet_update
|| gwps
->state
->do_full_update
;
127 retval
= skin_redraw(gwps
, gwps
->state
->do_full_update
?
128 WPS_REFRESH_ALL
: update_type
);
133 #ifdef HAVE_LCD_BITMAP
135 static void draw_progressbar(struct gui_wps
*gwps
,
136 struct skin_viewport
*wps_vp
)
138 struct screen
*display
= gwps
->display
;
139 struct wps_state
*state
= gwps
->state
;
140 struct progressbar
*pb
= wps_vp
->pb
;
145 int line_height
= font_get(wps_vp
->vp
.font
)->height
;
146 /* center the pb in the line, but only if the line is higher than the pb */
147 int center
= (line_height
-pb
->height
)/2;
148 /* if Y was not set calculate by font height,Y is -line_number-1 */
149 y
= (-y
-1)*line_height
+ (0 > center
? 0 : center
);
152 if (pb
->have_bitmap_pb
)
153 gui_bitmap_scrollbar_draw(display
, pb
->bm
,
154 pb
->x
, y
, pb
->width
, pb
->bm
.height
,
155 state
->id3
->length
? state
->id3
->length
: 1, 0,
156 state
->id3
->length
? state
->id3
->elapsed
157 + state
->ff_rewind_count
: 0,
160 gui_scrollbar_draw(display
, pb
->x
, y
, pb
->width
, pb
->height
,
161 state
->id3
->length
? state
->id3
->length
: 1, 0,
162 state
->id3
->length
? state
->id3
->elapsed
163 + state
->ff_rewind_count
: 0,
165 #ifdef AB_REPEAT_ENABLE
166 if ( ab_repeat_mode_enabled() && state
->id3
->length
!= 0 )
167 ab_draw_markers(display
, state
->id3
->length
,
168 pb
->x
, pb
->x
+ pb
->width
, y
, pb
->height
);
171 if (state
->id3
->cuesheet
)
172 cue_draw_markers(display
, state
->id3
->cuesheet
, state
->id3
->length
,
173 pb
->x
, pb
->x
+ pb
->width
, y
+1, pb
->height
-2);
176 /* clears the area where the image was shown */
177 static void clear_image_pos(struct gui_wps
*gwps
, struct gui_img
*img
)
181 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
182 gwps
->display
->fillrect(img
->x
, img
->y
, img
->bm
.width
, img
->subimage_height
);
183 gwps
->display
->set_drawmode(DRMODE_SOLID
);
186 static void wps_draw_image(struct gui_wps
*gwps
, struct gui_img
*img
, int subimage
)
188 struct screen
*display
= gwps
->display
;
189 if(img
->always_display
)
190 display
->set_drawmode(DRMODE_FG
);
192 display
->set_drawmode(DRMODE_SOLID
);
195 if(img
->bm
.format
== FORMAT_MONO
) {
197 display
->mono_bitmap_part(img
->bm
.data
,
198 0, img
->subimage_height
* subimage
,
199 img
->bm
.width
, img
->x
,
200 img
->y
, img
->bm
.width
,
201 img
->subimage_height
);
204 display
->transparent_bitmap_part((fb_data
*)img
->bm
.data
,
205 0, img
->subimage_height
* subimage
,
206 img
->bm
.width
, img
->x
,
207 img
->y
, img
->bm
.width
,
208 img
->subimage_height
);
213 static void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
215 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
218 struct wps_data
*data
= gwps
->data
;
219 struct screen
*display
= gwps
->display
;
220 struct skin_token_list
*list
= data
->images
;
224 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
227 if (img
->display
>= 0)
229 wps_draw_image(gwps
, img
, img
->display
);
231 else if (img
->always_display
&& img
->vp
== vp
)
233 wps_draw_image(gwps
, img
, 0);
238 display
->set_drawmode(DRMODE_SOLID
);
241 #else /* HAVE_LCD_CHARCELL */
243 static bool draw_player_progress(struct gui_wps
*gwps
)
245 struct wps_state
*state
= gwps
->state
;
246 struct screen
*display
= gwps
->display
;
247 unsigned char progress_pattern
[7];
254 if (state
->id3
->length
)
255 pos
= 36 * (state
->id3
->elapsed
+ state
->ff_rewind_count
)
256 / state
->id3
->length
;
258 for (i
= 0; i
< 7; i
++, pos
-= 5)
261 progress_pattern
[i
] = 0x1fu
;
263 progress_pattern
[i
] = 0x00u
;
265 progress_pattern
[i
] = 0x1fu
>> pos
;
268 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
272 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
274 static const unsigned char numbers
[10][4] = {
275 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
276 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
277 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
278 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
279 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
280 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
281 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
282 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
283 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
284 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
287 struct wps_state
*state
= gwps
->state
;
288 struct screen
*display
= gwps
->display
;
289 struct wps_data
*data
= gwps
->data
;
290 unsigned char progress_pattern
[7];
299 if (!state
->id3
|| buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
302 time
= state
->id3
->elapsed
+ state
->ff_rewind_count
;
303 if (state
->id3
->length
)
304 pos
= 55 * time
/ state
->id3
->length
;
306 memset(timestr
, 0, sizeof(timestr
));
307 format_time(timestr
, sizeof(timestr
)-2, time
);
308 timestr
[strlen(timestr
)] = ':'; /* always safe */
310 for (i
= 0; i
< 11; i
++, pos
-= 5)
313 memset(progress_pattern
, 0, sizeof(progress_pattern
));
315 if ((digit
= timestr
[time_idx
]))
320 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
322 memcpy(progress_pattern
, numbers
[digit
], 4);
325 else /* tens, shifted right */
327 for (j
= 0; j
< 4; j
++)
328 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
330 if (time_idx
> 0) /* not the first group, add colon in front */
332 progress_pattern
[1] |= 0x10u
;
333 progress_pattern
[3] |= 0x10u
;
339 progress_pattern
[5] = progress_pattern
[6] = 0x1fu
;
342 if (pos
> 0 && pos
< 5)
345 progress_pattern
[5] = progress_pattern
[6] = (~0x1fu
>> pos
) & 0x1fu
;
348 if (softchar
&& pat_idx
< 8)
350 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
352 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
356 buf
= utf8encode(' ', buf
);
358 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
363 #endif /* HAVE_LCD_CHARCELL */
365 /* Returns the index of the subline in the subline array
366 line - 0-based line number
367 subline - 0-based subline number within the line
369 static int subline_index(struct wps_data
*data
, int line
, int subline
)
371 return data
->lines
[line
].first_subline_idx
+ subline
;
374 /* Returns the index of the first subline's token in the token array
375 line - 0-based line number
376 subline - 0-based subline number within the line
378 static int first_token_index(struct wps_data
*data
, int line
, int subline
)
380 int first_subline_idx
= data
->lines
[line
].first_subline_idx
;
381 return data
->sublines
[first_subline_idx
+ subline
].first_token_idx
;
384 int skin_last_token_index(struct wps_data
*data
, int line
, int subline
)
386 int first_subline_idx
= data
->lines
[line
].first_subline_idx
;
387 int idx
= first_subline_idx
+ subline
;
388 if (idx
< data
->num_sublines
- 1)
390 /* This subline ends where the next begins */
391 return data
->sublines
[idx
+1].first_token_idx
- 1;
395 /* The last subline goes to the end */
396 return data
->num_tokens
- 1;
400 /* Return the index to the end token for the conditional token at index.
401 The conditional token can be either a start token or a separator
404 static int find_conditional_end(struct wps_data
*data
, int index
)
407 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
408 ret
= data
->tokens
[ret
].value
.i
;
410 /* ret now is the index to the end token for the conditional. */
414 /* Evaluate the conditional that is at *token_index and return whether a skip
415 has ocurred. *token_index is updated with the new position.
417 static bool evaluate_conditional(struct gui_wps
*gwps
, int *token_index
)
422 struct wps_data
*data
= gwps
->data
;
425 int cond_index
= *token_index
;
428 unsigned char num_options
= data
->tokens
[cond_index
].value
.i
& 0xFF;
429 unsigned char prev_val
= (data
->tokens
[cond_index
].value
.i
& 0xFF00) >> 8;
431 /* treat ?xx<true> constructs as if they had 2 options. */
435 int intval
= num_options
;
436 /* get_token_value needs to know the number of options in the enum */
437 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
438 result
, sizeof(result
), &intval
);
440 /* intval is now the number of the enum option we want to read,
441 starting from 1. If intval is -1, we check if value is empty. */
443 intval
= (value
&& *value
) ? 1 : num_options
;
444 else if (intval
> num_options
|| intval
< 1)
445 intval
= num_options
;
447 data
->tokens
[cond_index
].value
.i
= (intval
<< 8) + num_options
;
449 /* skip to the appropriate enum case */
450 int next
= cond_index
+ 2;
451 for (i
= 1; i
< intval
; i
++)
453 next
= data
->tokens
[next
].value
.i
;
457 if (prev_val
== intval
)
459 /* Same conditional case as previously. Return without clearing the
464 cond_end
= find_conditional_end(data
, cond_index
+ 2);
465 for (i
= cond_index
+ 3; i
< cond_end
; i
++)
467 #ifdef HAVE_LCD_BITMAP
468 /* clear all pictures in the conditional and nested ones */
469 if (data
->tokens
[i
].type
== WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
)
470 clear_image_pos(gwps
, find_image(data
->tokens
[i
].value
.i
&0xFF, gwps
->data
));
473 if (data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
474 draw_album_art(gwps
, audio_current_aa_hid(), true);
480 #ifdef HAVE_LCD_BITMAP
481 struct gui_img
* find_image(char label
, struct wps_data
*data
)
483 struct skin_token_list
*list
= data
->images
;
486 struct gui_img
*img
= (struct gui_img
*)list
->token
->value
.data
;
487 if (img
->label
== label
)
495 struct skin_viewport
* find_viewport(char label
, struct wps_data
*data
)
497 struct skin_token_list
*list
= data
->viewports
;
500 struct skin_viewport
*vp
= (struct skin_viewport
*)list
->token
->value
.data
;
501 if (vp
->label
== label
)
509 /* Read a (sub)line to the given alignment format buffer.
510 linebuf is the buffer where the data is actually stored.
511 align is the alignment format that'll be used to display the text.
512 The return value indicates whether the line needs to be updated.
514 static bool get_line(struct gui_wps
*gwps
,
515 int line
, int subline
,
516 struct align_pos
*align
,
520 struct wps_data
*data
= gwps
->data
;
523 char *buf
= linebuf
; /* will always point to the writing position */
524 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
525 int i
, last_token_idx
;
528 /* alignment-related variables */
530 char* cur_align_start
;
531 cur_align_start
= buf
;
532 cur_align
= WPS_ALIGN_LEFT
;
534 align
->center
= NULL
;
537 /* Process all tokens of the desired subline */
538 last_token_idx
= skin_last_token_index(data
, line
, subline
);
539 for (i
= first_token_index(data
, line
, subline
);
540 i
<= last_token_idx
; i
++)
542 switch(data
->tokens
[i
].type
)
544 case WPS_TOKEN_CONDITIONAL
:
545 /* place ourselves in the right conditional case */
546 update
|= evaluate_conditional(gwps
, &i
);
549 case WPS_TOKEN_CONDITIONAL_OPTION
:
550 /* we've finished in the curent conditional case,
551 skip to the end of the conditional structure */
552 i
= find_conditional_end(data
, i
);
555 #ifdef HAVE_LCD_BITMAP
556 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
558 char n
= data
->tokens
[i
].value
.i
& 0xFF;
559 int subimage
= data
->tokens
[i
].value
.i
>> 8;
560 struct gui_img
*img
= find_image(n
, data
);
562 if (img
&& img
->loaded
)
563 img
->display
= subimage
;
568 case WPS_TOKEN_ALIGN_LEFT
:
569 case WPS_TOKEN_ALIGN_CENTER
:
570 case WPS_TOKEN_ALIGN_RIGHT
:
571 /* remember where the current aligned text started */
575 align
->left
= cur_align_start
;
578 case WPS_ALIGN_CENTER
:
579 align
->center
= cur_align_start
;
582 case WPS_ALIGN_RIGHT
:
583 align
->right
= cur_align_start
;
586 /* start a new alignment */
587 switch (data
->tokens
[i
].type
)
589 case WPS_TOKEN_ALIGN_LEFT
:
590 cur_align
= WPS_ALIGN_LEFT
;
592 case WPS_TOKEN_ALIGN_CENTER
:
593 cur_align
= WPS_ALIGN_CENTER
;
595 case WPS_TOKEN_ALIGN_RIGHT
:
596 cur_align
= WPS_ALIGN_RIGHT
;
602 cur_align_start
= buf
;
604 case WPS_VIEWPORT_ENABLE
:
606 char label
= data
->tokens
[i
].value
.i
;
607 char temp
= VP_DRAW_HIDEABLE
;
608 /* viewports are allowed to share id's so find and enable
610 struct skin_token_list
*list
= data
->viewports
;
613 struct skin_viewport
*vp
=
614 (struct skin_viewport
*)list
->token
->value
.data
;
615 if (vp
->label
== label
)
617 if (vp
->hidden_flags
&VP_DRAW_WASHIDDEN
)
618 temp
|= VP_DRAW_WASHIDDEN
;
619 vp
->hidden_flags
= temp
;
627 /* get the value of the tag and copy it to the buffer */
628 const char *value
= get_token_value(gwps
, &data
->tokens
[i
],
629 temp_buf
, sizeof(temp_buf
), NULL
);
633 while (*value
&& (buf
< linebuf_end
))
641 /* close the current alignment */
645 align
->left
= cur_align_start
;
648 case WPS_ALIGN_CENTER
:
649 align
->center
= cur_align_start
;
652 case WPS_ALIGN_RIGHT
:
653 align
->right
= cur_align_start
;
660 static void get_subline_timeout(struct gui_wps
*gwps
, int line
, int subline
)
662 struct wps_data
*data
= gwps
->data
;
664 int subline_idx
= subline_index(data
, line
, subline
);
665 int last_token_idx
= skin_last_token_index(data
, line
, subline
);
667 data
->sublines
[subline_idx
].time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
669 for (i
= first_token_index(data
, line
, subline
);
670 i
<= last_token_idx
; i
++)
672 switch(data
->tokens
[i
].type
)
674 case WPS_TOKEN_CONDITIONAL
:
675 /* place ourselves in the right conditional case */
676 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 case WPS_TOKEN_SUBLINE_TIMEOUT
:
686 data
->sublines
[subline_idx
].time_mult
= data
->tokens
[i
].value
.i
;
695 /* Calculates which subline should be displayed for the specified line
696 Returns true iff the subline must be refreshed */
697 static bool update_curr_subline(struct gui_wps
*gwps
, int line
)
699 struct wps_data
*data
= gwps
->data
;
701 int search
, search_start
, num_sublines
;
703 bool new_subline_refresh
;
704 bool only_one_subline
;
706 num_sublines
= data
->lines
[line
].num_sublines
;
707 reset_subline
= (data
->lines
[line
].curr_subline
== SUBLINE_RESET
);
708 new_subline_refresh
= false;
709 only_one_subline
= false;
711 /* if time to advance to next sub-line */
712 if (TIME_AFTER(current_tick
, data
->lines
[line
].subline_expire_time
- 1) ||
715 /* search all sublines until the next subline with time > 0
716 is found or we get back to the subline we started with */
720 search_start
= data
->lines
[line
].curr_subline
;
722 for (search
= 0; search
< num_sublines
; search
++)
724 data
->lines
[line
].curr_subline
++;
726 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
727 if (data
->lines
[line
].curr_subline
== num_sublines
)
729 if (data
->lines
[line
].curr_subline
== 1)
730 only_one_subline
= true;
731 data
->lines
[line
].curr_subline
= 0;
734 /* if back where we started after search or
735 only one subline is defined on the line */
737 (data
->lines
[line
].curr_subline
== search_start
)) ||
740 /* no other subline with a time > 0 exists */
741 data
->lines
[line
].subline_expire_time
= (reset_subline
?
743 data
->lines
[line
].subline_expire_time
) + 100 * HZ
;
748 /* get initial time multiplier for this subline */
749 get_subline_timeout(gwps
, line
, data
->lines
[line
].curr_subline
);
751 int subline_idx
= subline_index(data
, line
,
752 data
->lines
[line
].curr_subline
);
754 /* only use this subline if subline time > 0 */
755 if (data
->sublines
[subline_idx
].time_mult
> 0)
757 new_subline_refresh
= true;
758 data
->lines
[line
].subline_expire_time
= (reset_subline
?
759 current_tick
: data
->lines
[line
].subline_expire_time
) +
760 TIMEOUT_UNIT
*data
->sublines
[subline_idx
].time_mult
;
767 return new_subline_refresh
;
770 /* Display a line appropriately according to its alignment format.
771 format_align contains the text, separated between left, center and right.
772 line is the index of the line on the screen.
773 scroll indicates whether the line is a scrolling one or not.
775 static void write_line(struct screen
*display
,
776 struct align_pos
*format_align
,
780 int left_width
= 0, left_xpos
;
781 int center_width
= 0, center_xpos
;
782 int right_width
= 0, right_xpos
;
788 /* calculate different string sizes and positions */
789 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
790 if (format_align
->left
!= 0) {
791 display
->getstringsize((unsigned char *)format_align
->left
,
792 &left_width
, &string_height
);
795 if (format_align
->right
!= 0) {
796 display
->getstringsize((unsigned char *)format_align
->right
,
797 &right_width
, &string_height
);
800 if (format_align
->center
!= 0) {
801 display
->getstringsize((unsigned char *)format_align
->center
,
802 ¢er_width
, &string_height
);
806 right_xpos
= (display
->getwidth() - right_width
);
807 center_xpos
= (display
->getwidth() + left_xpos
- center_width
) / 2;
809 scroll_width
= display
->getwidth() - left_xpos
;
811 /* Checks for overlapping strings.
812 If needed the overlapping strings will be merged, separated by a
815 /* CASE 1: left and centered string overlap */
816 /* there is a left string, need to merge left and center */
817 if ((left_width
!= 0 && center_width
!= 0) &&
818 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
819 /* replace the former separator '\0' of left and
820 center string with a space */
821 *(--format_align
->center
) = ' ';
822 /* calculate the new width and position of the merged string */
823 left_width
= left_width
+ space_width
+ center_width
;
824 /* there is no centered string anymore */
827 /* there is no left string, move center to left */
828 if ((left_width
== 0 && center_width
!= 0) &&
829 (left_xpos
+ left_width
> center_xpos
)) {
830 /* move the center string to the left string */
831 format_align
->left
= format_align
->center
;
832 /* calculate the new width and position of the string */
833 left_width
= center_width
;
834 /* there is no centered string anymore */
838 /* CASE 2: centered and right string overlap */
839 /* there is a right string, need to merge center and right */
840 if ((center_width
!= 0 && right_width
!= 0) &&
841 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
842 /* replace the former separator '\0' of center and
843 right string with a space */
844 *(--format_align
->right
) = ' ';
845 /* move the center string to the right after merge */
846 format_align
->right
= format_align
->center
;
847 /* calculate the new width and position of the merged string */
848 right_width
= center_width
+ space_width
+ right_width
;
849 right_xpos
= (display
->getwidth() - right_width
);
850 /* there is no centered string anymore */
853 /* there is no right string, move center to right */
854 if ((center_width
!= 0 && right_width
== 0) &&
855 (center_xpos
+ center_width
> right_xpos
)) {
856 /* move the center string to the right string */
857 format_align
->right
= format_align
->center
;
858 /* calculate the new width and position of the string */
859 right_width
= center_width
;
860 right_xpos
= (display
->getwidth() - right_width
);
861 /* there is no centered string anymore */
865 /* CASE 3: left and right overlap
866 There is no center string anymore, either there never
867 was one or it has been merged in case 1 or 2 */
868 /* there is a left string, need to merge left and right */
869 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
870 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
871 /* replace the former separator '\0' of left and
872 right string with a space */
873 *(--format_align
->right
) = ' ';
874 /* calculate the new width and position of the string */
875 left_width
= left_width
+ space_width
+ right_width
;
876 /* there is no right string anymore */
879 /* there is no left string, move right to left */
880 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
881 (left_width
> right_xpos
)) {
882 /* move the right string to the left string */
883 format_align
->left
= format_align
->right
;
884 /* calculate the new width and position of the string */
885 left_width
= right_width
;
886 /* there is no right string anymore */
890 ypos
= (line
* string_height
);
893 if (scroll
&& ((left_width
> scroll_width
) ||
894 (center_width
> scroll_width
) ||
895 (right_width
> scroll_width
)))
897 display
->puts_scroll(0, line
,
898 (unsigned char *)format_align
->left
);
902 #ifdef HAVE_LCD_BITMAP
903 /* clear the line first */
904 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
905 display
->fillrect(left_xpos
, ypos
, display
->getwidth(), string_height
);
906 display
->set_drawmode(DRMODE_SOLID
);
909 /* Nasty hack: we output an empty scrolling string,
910 which will reset the scroller for that line */
911 display
->puts_scroll(0, line
, (unsigned char *)"");
913 /* print aligned strings */
916 display
->putsxy(left_xpos
, ypos
,
917 (unsigned char *)format_align
->left
);
919 if (center_width
!= 0)
921 display
->putsxy(center_xpos
, ypos
,
922 (unsigned char *)format_align
->center
);
924 if (right_width
!= 0)
926 display
->putsxy(right_xpos
, ypos
,
927 (unsigned char *)format_align
->right
);
932 static bool skin_redraw(struct gui_wps
*gwps
, unsigned refresh_mode
)
934 struct wps_data
*data
= gwps
->data
;
935 struct screen
*display
= gwps
->display
;
936 struct wps_state
*state
= gwps
->state
;
938 if (!data
|| !state
|| !display
)
941 struct mp3entry
*id3
= state
->id3
;
946 int line
, i
, subline_idx
;
948 char linebuf
[MAX_PATH
];
950 struct align_pos align
;
955 bool update_line
, new_subline_refresh
;
957 #ifdef HAVE_LCD_BITMAP
959 /* to find out wether the peak meter is enabled we
960 assume it wasn't until we find a line that contains
961 the peak meter. We can't use peak_meter_enabled itself
962 because that would mean to turn off the meter thread
963 temporarily. (That shouldn't matter unless yield
964 or sleep is called but who knows...)
966 bool enable_pm
= false;
970 /* reset to first subline if refresh all flag is set */
971 if (refresh_mode
== WPS_REFRESH_ALL
)
973 display
->set_viewport(&find_viewport(VP_DEFAULT_LABEL
, data
)->vp
);
974 display
->clear_viewport();
976 for (i
= 0; i
<= data
->num_lines
; i
++)
978 data
->lines
[i
].curr_subline
= SUBLINE_RESET
;
982 #ifdef HAVE_LCD_CHARCELLS
983 for (i
= 0; i
< 8; i
++)
985 if (data
->wps_progress_pat
[i
] == 0)
986 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
990 /* disable any viewports which are conditionally displayed */
991 struct skin_token_list
*viewport_list
;
992 for (viewport_list
= data
->viewports
;
993 viewport_list
; viewport_list
= viewport_list
->next
)
995 struct skin_viewport
*skin_viewport
=
996 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
997 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDEABLE
)
999 if (skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
)
1000 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1002 skin_viewport
->hidden_flags
|= VP_DRAW_HIDDEN
;
1006 for (viewport_list
= data
->viewports
;
1007 viewport_list
; viewport_list
= viewport_list
->next
)
1009 struct skin_viewport
*skin_viewport
=
1010 (struct skin_viewport
*)viewport_list
->token
->value
.data
;
1011 unsigned vp_refresh_mode
= refresh_mode
;
1012 display
->set_viewport(&skin_viewport
->vp
);
1014 #ifdef HAVE_LCD_BITMAP
1015 /* Set images to not to be displayed */
1016 struct skin_token_list
*imglist
= data
->images
;
1019 struct gui_img
*img
= (struct gui_img
*)imglist
->token
->value
.data
;
1021 imglist
= imglist
->next
;
1024 /* dont redraw the viewport if its disabled */
1025 if ((skin_viewport
->hidden_flags
&VP_DRAW_HIDDEN
))
1027 if (!(skin_viewport
->hidden_flags
&VP_DRAW_WASHIDDEN
))
1028 display
->scroll_stop(&skin_viewport
->vp
);
1029 skin_viewport
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1032 else if (((skin_viewport
->hidden_flags
&
1033 (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
))
1034 == (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
)))
1036 vp_refresh_mode
= WPS_REFRESH_ALL
;
1037 skin_viewport
->hidden_flags
= VP_DRAW_HIDEABLE
;
1039 if (vp_refresh_mode
== WPS_REFRESH_ALL
)
1041 display
->clear_viewport();
1044 for (line
= skin_viewport
->first_line
;
1045 line
<= skin_viewport
->last_line
; line
++)
1047 memset(linebuf
, 0, sizeof(linebuf
));
1048 update_line
= false;
1050 /* get current subline for the line */
1051 new_subline_refresh
= update_curr_subline(gwps
, line
);
1053 subline_idx
= subline_index(data
, line
,
1054 data
->lines
[line
].curr_subline
);
1055 flags
= data
->sublines
[subline_idx
].line_type
;
1057 if (vp_refresh_mode
== WPS_REFRESH_ALL
|| (flags
& vp_refresh_mode
)
1058 || new_subline_refresh
)
1060 /* get_line tells us if we need to update the line */
1061 update_line
= get_line(gwps
, line
, data
->lines
[line
].curr_subline
,
1062 &align
, linebuf
, sizeof(linebuf
));
1064 #ifdef HAVE_LCD_BITMAP
1066 if (flags
& vp_refresh_mode
& WPS_REFRESH_PEAK_METER
)
1068 /* the peakmeter should be alone on its line */
1069 update_line
= false;
1071 int h
= font_get(skin_viewport
->vp
.font
)->height
;
1072 int peak_meter_y
= (line
- skin_viewport
->first_line
)* h
;
1074 /* The user might decide to have the peak meter in the last
1075 line so that it is only displayed if no status bar is
1076 visible. If so we neither want do draw nor enable the
1078 if (peak_meter_y
+ h
<= display
->getheight()) {
1079 /* found a line with a peak meter -> remember that we must
1082 peak_meter_enabled
= true;
1083 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
1084 MIN(h
, display
->getheight() - peak_meter_y
));
1088 peak_meter_enabled
= false;
1092 #else /* HAVE_LCD_CHARCELL */
1095 if (flags
& vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1097 if (data
->full_line_progressbar
)
1098 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
1100 draw_player_progress(gwps
);
1105 /* conditionals clear the line which means if the %Vd is put into the default
1106 viewport there will be a blank line.
1107 To get around this we dont allow any actual drawing to happen in the
1108 deault vp if other vp's are defined */
1109 ((skin_viewport
->label
!= VP_DEFAULT_LABEL
&& viewport_list
->next
) ||
1110 !viewport_list
->next
))
1112 if (flags
& WPS_REFRESH_SCROLL
)
1114 /* if the line is a scrolling one we don't want to update
1115 too often, so that it has the time to scroll */
1116 if ((vp_refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
1117 write_line(display
, &align
, line
- skin_viewport
->first_line
, true);
1120 write_line(display
, &align
, line
- skin_viewport
->first_line
, false);
1124 #ifdef HAVE_LCD_BITMAP
1126 if (vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1128 if (skin_viewport
->pb
)
1130 draw_progressbar(gwps
, skin_viewport
);
1133 /* Now display any images in this viewport */
1134 wps_display_images(gwps
, &skin_viewport
->vp
);
1138 #ifdef HAVE_LCD_BITMAP
1139 data
->peak_meter_enabled
= enable_pm
;
1142 if (refresh_mode
& WPS_REFRESH_STATUSBAR
)
1144 gwps_draw_statusbars();
1146 /* Restore the default viewport */
1147 display
->set_viewport(NULL
);