Do some #ifdef'ing to make the Player happy.
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
bloba6cab63829475c029b60881fdd434a04b4fc6390
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
22 #include "font.h"
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdlib.h>
26 #include "system.h"
27 #include "settings.h"
28 #include "settings_list.h"
29 #include "rbunicode.h"
30 #include "rtc.h"
31 #include "audio.h"
32 #include "status.h"
33 #include "power.h"
34 #include "powermgmt.h"
35 #include "sound.h"
36 #include "debug.h"
37 #ifdef HAVE_LCD_CHARCELLS
38 #include "hwcompat.h"
39 #endif
40 #include "abrepeat.h"
41 #include "mp3_playback.h"
42 #include "lang.h"
43 #include "misc.h"
44 #include "splash.h"
45 #include "scrollbar.h"
46 #include "led.h"
47 #include "lcd.h"
48 #ifdef HAVE_LCD_BITMAP
49 #include "peakmeter.h"
50 /* Image stuff */
51 #include "bmp.h"
52 #include "albumart.h"
53 #endif
54 #include "dsp.h"
55 #include "action.h"
56 #include "cuesheet.h"
57 #include "playlist.h"
58 #if CONFIG_CODEC == SWCODEC
59 #include "playback.h"
60 #endif
61 #include "backdrop.h"
62 #include "viewport.h"
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;
80 /* progress bars */
81 #else /* HAVE_LCD_CHARCELLS */
82 int i;
83 for (i = 0; i < 8; i++)
85 wps_data->wps_progress_pat[i] = 0;
87 wps_data->full_line_progressbar = false;
88 #endif
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 */
100 #if LCD_DEPTH > 1
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();
107 #endif
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)
119 bool retval;
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);
129 return retval;
132 #ifdef HAVE_LCD_BITMAP
134 void skin_statusbar_changed(struct gui_wps *skin)
136 if (!skin)
137 return;
138 struct wps_data *data = skin->data;
139 const struct screen *display = skin->display;
141 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, data)->vp;
142 viewport_set_fullscreen(vp, display->screen_type);
144 if (data->wps_sb_tag)
145 { /* fix up the default viewport */
146 if (data->show_sb_on_wps)
148 bool bar_at_top =
149 statusbar_position(display->screen_type) != STATUSBAR_BOTTOM;
151 vp->y = bar_at_top?STATUSBAR_HEIGHT:0;
152 vp->height = display->lcdheight - STATUSBAR_HEIGHT;
154 else
156 vp->y = 0;
157 vp->height = display->lcdheight;
164 static void draw_progressbar(struct gui_wps *gwps,
165 struct skin_viewport *wps_vp)
167 struct screen *display = gwps->display;
168 struct wps_state *state = gwps->state;
169 struct progressbar *pb = wps_vp->pb;
170 int y = pb->y;
172 if (y < 0)
174 int line_height = font_get(wps_vp->vp.font)->height;
175 /* center the pb in the line, but only if the line is higher than the pb */
176 int center = (line_height-pb->height)/2;
177 /* if Y was not set calculate by font height,Y is -line_number-1 */
178 y = (-y -1)*line_height + (0 > center ? 0 : center);
181 if (pb->have_bitmap_pb)
182 gui_bitmap_scrollbar_draw(display, pb->bm,
183 pb->x, y, pb->width, pb->bm.height,
184 state->id3->length ? state->id3->length : 1, 0,
185 state->id3->length ? state->id3->elapsed
186 + state->ff_rewind_count : 0,
187 HORIZONTAL);
188 else
189 gui_scrollbar_draw(display, pb->x, y, pb->width, pb->height,
190 state->id3->length ? state->id3->length : 1, 0,
191 state->id3->length ? state->id3->elapsed
192 + state->ff_rewind_count : 0,
193 HORIZONTAL);
194 #ifdef AB_REPEAT_ENABLE
195 if ( ab_repeat_mode_enabled() && state->id3->length != 0 )
196 ab_draw_markers(display, state->id3->length,
197 pb->x, pb->x + pb->width, y, pb->height);
198 #endif
200 if (state->id3->cuesheet)
201 cue_draw_markers(display, state->id3->cuesheet, state->id3->length,
202 pb->x, pb->x + pb->width, y+1, pb->height-2);
205 /* clears the area where the image was shown */
206 static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
208 if(!gwps)
209 return;
210 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
211 gwps->display->fillrect(img->x, img->y, img->bm.width, img->subimage_height);
212 gwps->display->set_drawmode(DRMODE_SOLID);
215 static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage)
217 struct screen *display = gwps->display;
218 if(img->always_display)
219 display->set_drawmode(DRMODE_FG);
220 else
221 display->set_drawmode(DRMODE_SOLID);
223 #if LCD_DEPTH > 1
224 if(img->bm.format == FORMAT_MONO) {
225 #endif
226 display->mono_bitmap_part(img->bm.data,
227 0, img->subimage_height * subimage,
228 img->bm.width, img->x,
229 img->y, img->bm.width,
230 img->subimage_height);
231 #if LCD_DEPTH > 1
232 } else {
233 display->transparent_bitmap_part((fb_data *)img->bm.data,
234 0, img->subimage_height * subimage,
235 img->bm.width, img->x,
236 img->y, img->bm.width,
237 img->subimage_height);
239 #endif
242 static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
244 if(!gwps || !gwps->data || !gwps->display)
245 return;
247 struct wps_data *data = gwps->data;
248 struct screen *display = gwps->display;
249 struct skin_token_list *list = data->images;
251 while (list)
253 struct gui_img *img = (struct gui_img*)list->token->value.data;
254 if (img->loaded)
256 if (img->display >= 0)
258 wps_draw_image(gwps, img, img->display);
260 else if (img->always_display && img->vp == vp)
262 wps_draw_image(gwps, img, 0);
265 list = list->next;
267 display->set_drawmode(DRMODE_SOLID);
270 #else /* HAVE_LCD_CHARCELL */
272 static bool draw_player_progress(struct gui_wps *gwps)
274 struct wps_state *state = gwps->state;
275 struct screen *display = gwps->display;
276 unsigned char progress_pattern[7];
277 int pos = 0;
278 int i;
280 if (!state->id3)
281 return false;
283 if (state->id3->length)
284 pos = 36 * (state->id3->elapsed + state->ff_rewind_count)
285 / state->id3->length;
287 for (i = 0; i < 7; i++, pos -= 5)
289 if (pos <= 0)
290 progress_pattern[i] = 0x1fu;
291 else if (pos >= 5)
292 progress_pattern[i] = 0x00u;
293 else
294 progress_pattern[i] = 0x1fu >> pos;
297 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
298 return true;
301 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
303 static const unsigned char numbers[10][4] = {
304 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
305 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
306 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
307 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
308 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
309 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
310 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
311 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
312 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
313 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
316 struct wps_state *state = gwps->state;
317 struct screen *display = gwps->display;
318 struct wps_data *data = gwps->data;
319 unsigned char progress_pattern[7];
320 char timestr[10];
321 int time;
322 int time_idx = 0;
323 int pos = 0;
324 int pat_idx = 1;
325 int digit, i, j;
326 bool softchar;
328 if (!state->id3 || buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
329 return;
331 time = state->id3->elapsed + state->ff_rewind_count;
332 if (state->id3->length)
333 pos = 55 * time / state->id3->length;
335 memset(timestr, 0, sizeof(timestr));
336 format_time(timestr, sizeof(timestr)-2, time);
337 timestr[strlen(timestr)] = ':'; /* always safe */
339 for (i = 0; i < 11; i++, pos -= 5)
341 softchar = false;
342 memset(progress_pattern, 0, sizeof(progress_pattern));
344 if ((digit = timestr[time_idx]))
346 softchar = true;
347 digit -= '0';
349 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
351 memcpy(progress_pattern, numbers[digit], 4);
352 time_idx += 2;
354 else /* tens, shifted right */
356 for (j = 0; j < 4; j++)
357 progress_pattern[j] = numbers[digit][j] >> 1;
359 if (time_idx > 0) /* not the first group, add colon in front */
361 progress_pattern[1] |= 0x10u;
362 progress_pattern[3] |= 0x10u;
364 time_idx++;
367 if (pos >= 5)
368 progress_pattern[5] = progress_pattern[6] = 0x1fu;
371 if (pos > 0 && pos < 5)
373 softchar = true;
374 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
377 if (softchar && pat_idx < 8)
379 display->define_pattern(data->wps_progress_pat[pat_idx],
380 progress_pattern);
381 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
382 pat_idx++;
384 else if (pos <= 0)
385 buf = utf8encode(' ', buf);
386 else
387 buf = utf8encode(0xe115, buf); /* 2/7 _ */
389 *buf = '\0';
392 #endif /* HAVE_LCD_CHARCELL */
394 /* Returns the index of the subline in the subline array
395 line - 0-based line number
396 subline - 0-based subline number within the line
398 static int subline_index(struct wps_data *data, int line, int subline)
400 return data->lines[line].first_subline_idx + subline;
403 /* Returns the index of the first subline's token in the token array
404 line - 0-based line number
405 subline - 0-based subline number within the line
407 static int first_token_index(struct wps_data *data, int line, int subline)
409 int first_subline_idx = data->lines[line].first_subline_idx;
410 return data->sublines[first_subline_idx + subline].first_token_idx;
413 int skin_last_token_index(struct wps_data *data, int line, int subline)
415 int first_subline_idx = data->lines[line].first_subline_idx;
416 int idx = first_subline_idx + subline;
417 if (idx < data->num_sublines - 1)
419 /* This subline ends where the next begins */
420 return data->sublines[idx+1].first_token_idx - 1;
422 else
424 /* The last subline goes to the end */
425 return data->num_tokens - 1;
429 /* Return the index to the end token for the conditional token at index.
430 The conditional token can be either a start token or a separator
431 (i.e. option) token.
433 static int find_conditional_end(struct wps_data *data, int index)
435 int ret = index;
436 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
437 ret = data->tokens[ret].value.i;
439 /* ret now is the index to the end token for the conditional. */
440 return ret;
443 /* Evaluate the conditional that is at *token_index and return whether a skip
444 has ocurred. *token_index is updated with the new position.
446 static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
448 if (!gwps)
449 return false;
451 struct wps_data *data = gwps->data;
453 int i, cond_end;
454 int cond_index = *token_index;
455 char result[128];
456 const char *value;
457 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
458 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
460 /* treat ?xx<true> constructs as if they had 2 options. */
461 if (num_options < 2)
462 num_options = 2;
464 int intval = num_options;
465 /* get_token_value needs to know the number of options in the enum */
466 value = get_token_value(gwps, &data->tokens[cond_index + 1],
467 result, sizeof(result), &intval);
469 /* intval is now the number of the enum option we want to read,
470 starting from 1. If intval is -1, we check if value is empty. */
471 if (intval == -1)
472 intval = (value && *value) ? 1 : num_options;
473 else if (intval > num_options || intval < 1)
474 intval = num_options;
476 data->tokens[cond_index].value.i = (intval << 8) + num_options;
478 /* skip to the appropriate enum case */
479 int next = cond_index + 2;
480 for (i = 1; i < intval; i++)
482 next = data->tokens[next].value.i;
484 *token_index = next;
486 if (prev_val == intval)
488 /* Same conditional case as previously. Return without clearing the
489 pictures */
490 return false;
493 cond_end = find_conditional_end(data, cond_index + 2);
494 for (i = cond_index + 3; i < cond_end; i++)
496 #ifdef HAVE_LCD_BITMAP
497 /* clear all pictures in the conditional and nested ones */
498 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
499 clear_image_pos(gwps, find_image(data->tokens[i].value.i&0xFF, gwps->data));
500 #endif
501 #ifdef HAVE_ALBUMART
502 if (data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
503 draw_album_art(gwps, audio_current_aa_hid(), true);
504 #endif
507 return true;
509 #ifdef HAVE_LCD_BITMAP
510 struct gui_img* find_image(char label, struct wps_data *data)
512 struct skin_token_list *list = data->images;
513 while (list)
515 struct gui_img *img = (struct gui_img *)list->token->value.data;
516 if (img->label == label)
517 return img;
518 list = list->next;
520 return NULL;
522 #endif
524 struct skin_viewport* find_viewport(char label, struct wps_data *data)
526 struct skin_token_list *list = data->viewports;
527 while (list)
529 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
530 if (vp->label == label)
531 return vp;
532 list = list->next;
534 return NULL;
538 /* Read a (sub)line to the given alignment format buffer.
539 linebuf is the buffer where the data is actually stored.
540 align is the alignment format that'll be used to display the text.
541 The return value indicates whether the line needs to be updated.
543 static bool get_line(struct gui_wps *gwps,
544 int line, int subline,
545 struct align_pos *align,
546 char *linebuf,
547 int linebuf_size)
549 struct wps_data *data = gwps->data;
551 char temp_buf[128];
552 char *buf = linebuf; /* will always point to the writing position */
553 char *linebuf_end = linebuf + linebuf_size - 1;
554 int i, last_token_idx;
555 bool update = false;
557 /* alignment-related variables */
558 int cur_align;
559 char* cur_align_start;
560 cur_align_start = buf;
561 cur_align = WPS_ALIGN_LEFT;
562 align->left = NULL;
563 align->center = NULL;
564 align->right = NULL;
566 /* Process all tokens of the desired subline */
567 last_token_idx = skin_last_token_index(data, line, subline);
568 for (i = first_token_index(data, line, subline);
569 i <= last_token_idx; i++)
571 switch(data->tokens[i].type)
573 case WPS_TOKEN_CONDITIONAL:
574 /* place ourselves in the right conditional case */
575 update |= evaluate_conditional(gwps, &i);
576 break;
578 case WPS_TOKEN_CONDITIONAL_OPTION:
579 /* we've finished in the curent conditional case,
580 skip to the end of the conditional structure */
581 i = find_conditional_end(data, i);
582 break;
584 #ifdef HAVE_LCD_BITMAP
585 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
587 char n = data->tokens[i].value.i & 0xFF;
588 int subimage = data->tokens[i].value.i >> 8;
589 struct gui_img *img = find_image(n, data);
591 if (img && img->loaded)
592 img->display = subimage;
593 break;
595 #endif
597 case WPS_TOKEN_ALIGN_LEFT:
598 case WPS_TOKEN_ALIGN_CENTER:
599 case WPS_TOKEN_ALIGN_RIGHT:
600 /* remember where the current aligned text started */
601 switch (cur_align)
603 case WPS_ALIGN_LEFT:
604 align->left = cur_align_start;
605 break;
607 case WPS_ALIGN_CENTER:
608 align->center = cur_align_start;
609 break;
611 case WPS_ALIGN_RIGHT:
612 align->right = cur_align_start;
613 break;
615 /* start a new alignment */
616 switch (data->tokens[i].type)
618 case WPS_TOKEN_ALIGN_LEFT:
619 cur_align = WPS_ALIGN_LEFT;
620 break;
621 case WPS_TOKEN_ALIGN_CENTER:
622 cur_align = WPS_ALIGN_CENTER;
623 break;
624 case WPS_TOKEN_ALIGN_RIGHT:
625 cur_align = WPS_ALIGN_RIGHT;
626 break;
627 default:
628 break;
630 *buf++ = 0;
631 cur_align_start = buf;
632 break;
633 case WPS_VIEWPORT_ENABLE:
635 char label = data->tokens[i].value.i;
636 char temp = VP_DRAW_HIDEABLE;
637 /* viewports are allowed to share id's so find and enable
638 * all of them */
639 struct skin_token_list *list = data->viewports;
640 while (list)
642 struct skin_viewport *vp =
643 (struct skin_viewport *)list->token->value.data;
644 if (vp->label == label)
646 if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
647 temp |= VP_DRAW_WASHIDDEN;
648 vp->hidden_flags = temp;
650 list = list->next;
653 break;
654 default:
656 /* get the value of the tag and copy it to the buffer */
657 const char *value = get_token_value(gwps, &data->tokens[i],
658 temp_buf, sizeof(temp_buf), NULL);
659 if (value)
661 update = true;
662 while (*value && (buf < linebuf_end))
663 *buf++ = *value++;
665 break;
670 /* close the current alignment */
671 switch (cur_align)
673 case WPS_ALIGN_LEFT:
674 align->left = cur_align_start;
675 break;
677 case WPS_ALIGN_CENTER:
678 align->center = cur_align_start;
679 break;
681 case WPS_ALIGN_RIGHT:
682 align->right = cur_align_start;
683 break;
686 return update;
689 static void get_subline_timeout(struct gui_wps *gwps, int line, int subline)
691 struct wps_data *data = gwps->data;
692 int i;
693 int subline_idx = subline_index(data, line, subline);
694 int last_token_idx = skin_last_token_index(data, line, subline);
696 data->sublines[subline_idx].time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
698 for (i = first_token_index(data, line, subline);
699 i <= last_token_idx; i++)
701 switch(data->tokens[i].type)
703 case WPS_TOKEN_CONDITIONAL:
704 /* place ourselves in the right conditional case */
705 evaluate_conditional(gwps, &i);
706 break;
708 case WPS_TOKEN_CONDITIONAL_OPTION:
709 /* we've finished in the curent conditional case,
710 skip to the end of the conditional structure */
711 i = find_conditional_end(data, i);
712 break;
714 case WPS_TOKEN_SUBLINE_TIMEOUT:
715 data->sublines[subline_idx].time_mult = data->tokens[i].value.i;
716 break;
718 default:
719 break;
724 /* Calculates which subline should be displayed for the specified line
725 Returns true iff the subline must be refreshed */
726 static bool update_curr_subline(struct gui_wps *gwps, int line)
728 struct wps_data *data = gwps->data;
730 int search, search_start, num_sublines;
731 bool reset_subline;
732 bool new_subline_refresh;
733 bool only_one_subline;
735 num_sublines = data->lines[line].num_sublines;
736 reset_subline = (data->lines[line].curr_subline == SUBLINE_RESET);
737 new_subline_refresh = false;
738 only_one_subline = false;
740 /* if time to advance to next sub-line */
741 if (TIME_AFTER(current_tick, data->lines[line].subline_expire_time - 1) ||
742 reset_subline)
744 /* search all sublines until the next subline with time > 0
745 is found or we get back to the subline we started with */
746 if (reset_subline)
747 search_start = 0;
748 else
749 search_start = data->lines[line].curr_subline;
751 for (search = 0; search < num_sublines; search++)
753 data->lines[line].curr_subline++;
755 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
756 if (data->lines[line].curr_subline == num_sublines)
758 if (data->lines[line].curr_subline == 1)
759 only_one_subline = true;
760 data->lines[line].curr_subline = 0;
763 /* if back where we started after search or
764 only one subline is defined on the line */
765 if (((search > 0) &&
766 (data->lines[line].curr_subline == search_start)) ||
767 only_one_subline)
769 /* no other subline with a time > 0 exists */
770 data->lines[line].subline_expire_time = (reset_subline ?
771 current_tick :
772 data->lines[line].subline_expire_time) + 100 * HZ;
773 break;
775 else
777 /* get initial time multiplier for this subline */
778 get_subline_timeout(gwps, line, data->lines[line].curr_subline);
780 int subline_idx = subline_index(data, line,
781 data->lines[line].curr_subline);
783 /* only use this subline if subline time > 0 */
784 if (data->sublines[subline_idx].time_mult > 0)
786 new_subline_refresh = true;
787 data->lines[line].subline_expire_time = (reset_subline ?
788 current_tick : data->lines[line].subline_expire_time) +
789 TIMEOUT_UNIT*data->sublines[subline_idx].time_mult;
790 break;
796 return new_subline_refresh;
799 /* Display a line appropriately according to its alignment format.
800 format_align contains the text, separated between left, center and right.
801 line is the index of the line on the screen.
802 scroll indicates whether the line is a scrolling one or not.
804 static void write_line(struct screen *display,
805 struct align_pos *format_align,
806 int line,
807 bool scroll)
809 int left_width = 0, left_xpos;
810 int center_width = 0, center_xpos;
811 int right_width = 0, right_xpos;
812 int ypos;
813 int space_width;
814 int string_height;
815 int scroll_width;
817 /* calculate different string sizes and positions */
818 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
819 if (format_align->left != 0) {
820 display->getstringsize((unsigned char *)format_align->left,
821 &left_width, &string_height);
824 if (format_align->right != 0) {
825 display->getstringsize((unsigned char *)format_align->right,
826 &right_width, &string_height);
829 if (format_align->center != 0) {
830 display->getstringsize((unsigned char *)format_align->center,
831 &center_width, &string_height);
834 left_xpos = 0;
835 right_xpos = (display->getwidth() - right_width);
836 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
838 scroll_width = display->getwidth() - left_xpos;
840 /* Checks for overlapping strings.
841 If needed the overlapping strings will be merged, separated by a
842 space */
844 /* CASE 1: left and centered string overlap */
845 /* there is a left string, need to merge left and center */
846 if ((left_width != 0 && center_width != 0) &&
847 (left_xpos + left_width + space_width > center_xpos)) {
848 /* replace the former separator '\0' of left and
849 center string with a space */
850 *(--format_align->center) = ' ';
851 /* calculate the new width and position of the merged string */
852 left_width = left_width + space_width + center_width;
853 /* there is no centered string anymore */
854 center_width = 0;
856 /* there is no left string, move center to left */
857 if ((left_width == 0 && center_width != 0) &&
858 (left_xpos + left_width > center_xpos)) {
859 /* move the center string to the left string */
860 format_align->left = format_align->center;
861 /* calculate the new width and position of the string */
862 left_width = center_width;
863 /* there is no centered string anymore */
864 center_width = 0;
867 /* CASE 2: centered and right string overlap */
868 /* there is a right string, need to merge center and right */
869 if ((center_width != 0 && right_width != 0) &&
870 (center_xpos + center_width + space_width > right_xpos)) {
871 /* replace the former separator '\0' of center and
872 right string with a space */
873 *(--format_align->right) = ' ';
874 /* move the center string to the right after merge */
875 format_align->right = format_align->center;
876 /* calculate the new width and position of the merged string */
877 right_width = center_width + space_width + right_width;
878 right_xpos = (display->getwidth() - right_width);
879 /* there is no centered string anymore */
880 center_width = 0;
882 /* there is no right string, move center to right */
883 if ((center_width != 0 && right_width == 0) &&
884 (center_xpos + center_width > right_xpos)) {
885 /* move the center string to the right string */
886 format_align->right = format_align->center;
887 /* calculate the new width and position of the string */
888 right_width = center_width;
889 right_xpos = (display->getwidth() - right_width);
890 /* there is no centered string anymore */
891 center_width = 0;
894 /* CASE 3: left and right overlap
895 There is no center string anymore, either there never
896 was one or it has been merged in case 1 or 2 */
897 /* there is a left string, need to merge left and right */
898 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
899 (left_xpos + left_width + space_width > right_xpos)) {
900 /* replace the former separator '\0' of left and
901 right string with a space */
902 *(--format_align->right) = ' ';
903 /* calculate the new width and position of the string */
904 left_width = left_width + space_width + right_width;
905 /* there is no right string anymore */
906 right_width = 0;
908 /* there is no left string, move right to left */
909 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
910 (left_width > right_xpos)) {
911 /* move the right string to the left string */
912 format_align->left = format_align->right;
913 /* calculate the new width and position of the string */
914 left_width = right_width;
915 /* there is no right string anymore */
916 right_width = 0;
919 ypos = (line * string_height);
922 if (scroll && ((left_width > scroll_width) ||
923 (center_width > scroll_width) ||
924 (right_width > scroll_width)))
926 display->puts_scroll(0, line,
927 (unsigned char *)format_align->left);
929 else
931 #ifdef HAVE_LCD_BITMAP
932 /* clear the line first */
933 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
934 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
935 display->set_drawmode(DRMODE_SOLID);
936 #endif
938 /* Nasty hack: we output an empty scrolling string,
939 which will reset the scroller for that line */
940 display->puts_scroll(0, line, (unsigned char *)"");
942 /* print aligned strings */
943 if (left_width != 0)
945 display->putsxy(left_xpos, ypos,
946 (unsigned char *)format_align->left);
948 if (center_width != 0)
950 display->putsxy(center_xpos, ypos,
951 (unsigned char *)format_align->center);
953 if (right_width != 0)
955 display->putsxy(right_xpos, ypos,
956 (unsigned char *)format_align->right);
961 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
963 struct wps_data *data = gwps->data;
964 struct screen *display = gwps->display;
965 struct wps_state *state = gwps->state;
967 if (!data || !state || !display)
968 return false;
970 struct mp3entry *id3 = state->id3;
972 if (!id3)
973 return false;
975 int line, i, subline_idx;
976 unsigned flags;
977 char linebuf[MAX_PATH];
979 struct align_pos align;
980 align.left = NULL;
981 align.center = NULL;
982 align.right = NULL;
984 bool update_line, new_subline_refresh;
986 #ifdef HAVE_LCD_BITMAP
988 /* to find out wether the peak meter is enabled we
989 assume it wasn't until we find a line that contains
990 the peak meter. We can't use peak_meter_enabled itself
991 because that would mean to turn off the meter thread
992 temporarily. (That shouldn't matter unless yield
993 or sleep is called but who knows...)
995 bool enable_pm = false;
997 #endif
999 /* reset to first subline if refresh all flag is set */
1000 if (refresh_mode == WPS_REFRESH_ALL)
1002 display->set_viewport(&find_viewport(VP_DEFAULT_LABEL, data)->vp);
1003 display->clear_viewport();
1005 for (i = 0; i <= data->num_lines; i++)
1007 data->lines[i].curr_subline = SUBLINE_RESET;
1011 #ifdef HAVE_LCD_CHARCELLS
1012 for (i = 0; i < 8; i++)
1014 if (data->wps_progress_pat[i] == 0)
1015 data->wps_progress_pat[i] = display->get_locked_pattern();
1017 #endif
1019 /* disable any viewports which are conditionally displayed */
1020 struct skin_token_list *viewport_list;
1021 for (viewport_list = data->viewports;
1022 viewport_list; viewport_list = viewport_list->next)
1024 struct skin_viewport *skin_viewport =
1025 (struct skin_viewport *)viewport_list->token->value.data;
1026 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
1028 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
1029 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1030 else
1031 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
1035 for (viewport_list = data->viewports;
1036 viewport_list; viewport_list = viewport_list->next)
1038 struct skin_viewport *skin_viewport =
1039 (struct skin_viewport *)viewport_list->token->value.data;
1040 unsigned vp_refresh_mode = refresh_mode;
1041 display->set_viewport(&skin_viewport->vp);
1043 #ifdef HAVE_LCD_BITMAP
1044 /* Set images to not to be displayed */
1045 struct skin_token_list *imglist = data->images;
1046 while (imglist)
1048 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
1049 img->display = -1;
1050 imglist = imglist->next;
1052 #endif
1053 /* dont redraw the viewport if its disabled */
1054 if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
1056 if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
1057 display->scroll_stop(&skin_viewport->vp);
1058 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1059 continue;
1061 else if (((skin_viewport->hidden_flags&
1062 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
1063 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
1065 vp_refresh_mode = WPS_REFRESH_ALL;
1066 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
1068 if (vp_refresh_mode == WPS_REFRESH_ALL)
1070 display->clear_viewport();
1073 for (line = skin_viewport->first_line;
1074 line <= skin_viewport->last_line; line++)
1076 memset(linebuf, 0, sizeof(linebuf));
1077 update_line = false;
1079 /* get current subline for the line */
1080 new_subline_refresh = update_curr_subline(gwps, line);
1082 subline_idx = subline_index(data, line,
1083 data->lines[line].curr_subline);
1084 flags = data->sublines[subline_idx].line_type;
1086 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1087 || new_subline_refresh)
1089 /* get_line tells us if we need to update the line */
1090 update_line = get_line(gwps, line, data->lines[line].curr_subline,
1091 &align, linebuf, sizeof(linebuf));
1093 #ifdef HAVE_LCD_BITMAP
1094 /* peakmeter */
1095 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1097 /* the peakmeter should be alone on its line */
1098 update_line = false;
1100 int h = font_get(skin_viewport->vp.font)->height;
1101 int peak_meter_y = (line - skin_viewport->first_line)* h;
1103 /* The user might decide to have the peak meter in the last
1104 line so that it is only displayed if no status bar is
1105 visible. If so we neither want do draw nor enable the
1106 peak meter. */
1107 if (peak_meter_y + h <= display->getheight()) {
1108 /* found a line with a peak meter -> remember that we must
1109 enable it later */
1110 enable_pm = true;
1111 peak_meter_enabled = true;
1112 peak_meter_screen(gwps->display, 0, peak_meter_y,
1113 MIN(h, display->getheight() - peak_meter_y));
1115 else
1117 peak_meter_enabled = false;
1121 #else /* HAVE_LCD_CHARCELL */
1123 /* progressbar */
1124 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1126 if (data->full_line_progressbar)
1127 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1128 else
1129 draw_player_progress(gwps);
1131 #endif
1133 if (update_line &&
1134 /* conditionals clear the line which means if the %Vd is put into the default
1135 viewport there will be a blank line.
1136 To get around this we dont allow any actual drawing to happen in the
1137 deault vp if other vp's are defined */
1138 ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
1139 !viewport_list->next))
1141 if (flags & WPS_REFRESH_SCROLL)
1143 /* if the line is a scrolling one we don't want to update
1144 too often, so that it has the time to scroll */
1145 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1146 write_line(display, &align, line - skin_viewport->first_line, true);
1148 else
1149 write_line(display, &align, line - skin_viewport->first_line, false);
1153 #ifdef HAVE_LCD_BITMAP
1154 /* progressbar */
1155 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1157 if (skin_viewport->pb)
1159 draw_progressbar(gwps, skin_viewport);
1162 /* Now display any images in this viewport */
1163 wps_display_images(gwps, &skin_viewport->vp);
1164 #endif
1167 #ifdef HAVE_LCD_BITMAP
1168 data->peak_meter_enabled = enable_pm;
1169 #endif
1171 if (refresh_mode & WPS_REFRESH_STATUSBAR)
1173 viewportmanager_set_statusbar(*gwps->statusbars);
1175 /* Restore the default viewport */
1176 display->set_viewport(NULL);
1178 display->update();
1180 return true;