conditional viewports are allowed to share labels.. so make sure they are all toggled...
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
blob85cc65d4107eb358f927dc2dbdeb2d249ece75be
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;
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;
141 int y = pb->y;
143 if (y < 0)
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,
158 HORIZONTAL);
159 else
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,
164 HORIZONTAL);
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);
169 #endif
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)
179 if(!gwps)
180 return;
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);
191 else
192 display->set_drawmode(DRMODE_SOLID);
194 #if LCD_DEPTH > 1
195 if(img->bm.format == FORMAT_MONO) {
196 #endif
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);
202 #if LCD_DEPTH > 1
203 } else {
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);
210 #endif
213 static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
215 if(!gwps || !gwps->data || !gwps->display)
216 return;
218 struct wps_data *data = gwps->data;
219 struct screen *display = gwps->display;
220 struct skin_token_list *list = data->images;
222 while (list)
224 struct gui_img *img = (struct gui_img*)list->token->value.data;
225 if (img->loaded)
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);
236 list = list->next;
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];
248 int pos = 0;
249 int i;
251 if (!state->id3)
252 return false;
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)
260 if (pos <= 0)
261 progress_pattern[i] = 0x1fu;
262 else if (pos >= 5)
263 progress_pattern[i] = 0x00u;
264 else
265 progress_pattern[i] = 0x1fu >> pos;
268 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
269 return true;
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];
291 char timestr[10];
292 int time;
293 int time_idx = 0;
294 int pos = 0;
295 int pat_idx = 1;
296 int digit, i, j;
297 bool softchar;
299 if (!state->id3 || buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
300 return;
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)
312 softchar = false;
313 memset(progress_pattern, 0, sizeof(progress_pattern));
315 if ((digit = timestr[time_idx]))
317 softchar = true;
318 digit -= '0';
320 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
322 memcpy(progress_pattern, numbers[digit], 4);
323 time_idx += 2;
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;
335 time_idx++;
338 if (pos >= 5)
339 progress_pattern[5] = progress_pattern[6] = 0x1fu;
342 if (pos > 0 && pos < 5)
344 softchar = true;
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],
351 progress_pattern);
352 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
353 pat_idx++;
355 else if (pos <= 0)
356 buf = utf8encode(' ', buf);
357 else
358 buf = utf8encode(0xe115, buf); /* 2/7 _ */
360 *buf = '\0';
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;
393 else
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
402 (i.e. option) token.
404 static int find_conditional_end(struct wps_data *data, int index)
406 int ret = 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. */
411 return ret;
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)
419 if (!gwps)
420 return false;
422 struct wps_data *data = gwps->data;
424 int i, cond_end;
425 int cond_index = *token_index;
426 char result[128];
427 const char *value;
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. */
432 if (num_options < 2)
433 num_options = 2;
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. */
442 if (intval == -1)
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;
455 *token_index = next;
457 if (prev_val == intval)
459 /* Same conditional case as previously. Return without clearing the
460 pictures */
461 return false;
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));
471 #endif
472 #ifdef HAVE_ALBUMART
473 if (data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
474 draw_album_art(gwps, audio_current_aa_hid(), true);
475 #endif
478 return 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;
484 while (list)
486 struct gui_img *img = (struct gui_img *)list->token->value.data;
487 if (img->label == label)
488 return img;
489 list = list->next;
491 return NULL;
493 #endif
495 struct skin_viewport* find_viewport(char label, struct wps_data *data)
497 struct skin_token_list *list = data->viewports;
498 while (list)
500 struct skin_viewport *vp = (struct skin_viewport *)list->token->value.data;
501 if (vp->label == label)
502 return vp;
503 list = list->next;
505 return NULL;
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,
517 char *linebuf,
518 int linebuf_size)
520 struct wps_data *data = gwps->data;
522 char temp_buf[128];
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;
526 bool update = false;
528 /* alignment-related variables */
529 int cur_align;
530 char* cur_align_start;
531 cur_align_start = buf;
532 cur_align = WPS_ALIGN_LEFT;
533 align->left = NULL;
534 align->center = NULL;
535 align->right = 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);
547 break;
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);
553 break;
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;
564 break;
566 #endif
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 */
572 switch (cur_align)
574 case WPS_ALIGN_LEFT:
575 align->left = cur_align_start;
576 break;
578 case WPS_ALIGN_CENTER:
579 align->center = cur_align_start;
580 break;
582 case WPS_ALIGN_RIGHT:
583 align->right = cur_align_start;
584 break;
586 /* start a new alignment */
587 switch (data->tokens[i].type)
589 case WPS_TOKEN_ALIGN_LEFT:
590 cur_align = WPS_ALIGN_LEFT;
591 break;
592 case WPS_TOKEN_ALIGN_CENTER:
593 cur_align = WPS_ALIGN_CENTER;
594 break;
595 case WPS_TOKEN_ALIGN_RIGHT:
596 cur_align = WPS_ALIGN_RIGHT;
597 break;
598 default:
599 break;
601 *buf++ = 0;
602 cur_align_start = buf;
603 break;
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
609 * all of them */
610 struct skin_token_list *list = data->viewports;
611 while (list)
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;
621 list = list->next;
624 break;
625 default:
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);
630 if (value)
632 update = true;
633 while (*value && (buf < linebuf_end))
634 *buf++ = *value++;
636 break;
641 /* close the current alignment */
642 switch (cur_align)
644 case WPS_ALIGN_LEFT:
645 align->left = cur_align_start;
646 break;
648 case WPS_ALIGN_CENTER:
649 align->center = cur_align_start;
650 break;
652 case WPS_ALIGN_RIGHT:
653 align->right = cur_align_start;
654 break;
657 return update;
660 static void get_subline_timeout(struct gui_wps *gwps, int line, int subline)
662 struct wps_data *data = gwps->data;
663 int i;
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);
677 break;
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);
683 break;
685 case WPS_TOKEN_SUBLINE_TIMEOUT:
686 data->sublines[subline_idx].time_mult = data->tokens[i].value.i;
687 break;
689 default:
690 break;
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;
702 bool reset_subline;
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) ||
713 reset_subline)
715 /* search all sublines until the next subline with time > 0
716 is found or we get back to the subline we started with */
717 if (reset_subline)
718 search_start = 0;
719 else
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 */
736 if (((search > 0) &&
737 (data->lines[line].curr_subline == search_start)) ||
738 only_one_subline)
740 /* no other subline with a time > 0 exists */
741 data->lines[line].subline_expire_time = (reset_subline ?
742 current_tick :
743 data->lines[line].subline_expire_time) + 100 * HZ;
744 break;
746 else
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;
761 break;
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,
777 int line,
778 bool scroll)
780 int left_width = 0, left_xpos;
781 int center_width = 0, center_xpos;
782 int right_width = 0, right_xpos;
783 int ypos;
784 int space_width;
785 int string_height;
786 int scroll_width;
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 &center_width, &string_height);
805 left_xpos = 0;
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
813 space */
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 */
825 center_width = 0;
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 */
835 center_width = 0;
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 */
851 center_width = 0;
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 */
862 center_width = 0;
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 */
877 right_width = 0;
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 */
887 right_width = 0;
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);
900 else
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);
907 #endif
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 */
914 if (left_width != 0)
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)
939 return false;
941 struct mp3entry *id3 = state->id3;
943 if (!id3)
944 return false;
946 int line, i, subline_idx;
947 unsigned flags;
948 char linebuf[MAX_PATH];
950 struct align_pos align;
951 align.left = NULL;
952 align.center = NULL;
953 align.right = NULL;
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;
968 #endif
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();
988 #endif
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;
1001 else
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;
1017 while (imglist)
1019 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
1020 img->display = -1;
1021 imglist = imglist->next;
1023 #endif
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;
1030 continue;
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
1065 /* peakmeter */
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
1077 peak meter. */
1078 if (peak_meter_y + h <= display->getheight()) {
1079 /* found a line with a peak meter -> remember that we must
1080 enable it later */
1081 enable_pm = true;
1082 peak_meter_enabled = true;
1083 peak_meter_screen(gwps->display, 0, peak_meter_y,
1084 MIN(h, display->getheight() - peak_meter_y));
1086 else
1088 peak_meter_enabled = false;
1092 #else /* HAVE_LCD_CHARCELL */
1094 /* progressbar */
1095 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1097 if (data->full_line_progressbar)
1098 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1099 else
1100 draw_player_progress(gwps);
1102 #endif
1104 if (update_line &&
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);
1119 else
1120 write_line(display, &align, line - skin_viewport->first_line, false);
1124 #ifdef HAVE_LCD_BITMAP
1125 /* progressbar */
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);
1135 #endif
1138 #ifdef HAVE_LCD_BITMAP
1139 data->peak_meter_enabled = enable_pm;
1140 #endif
1142 if (refresh_mode & WPS_REFRESH_STATUSBAR)
1144 gwps_draw_statusbars();
1146 /* Restore the default viewport */
1147 display->set_viewport(NULL);
1149 display->update();
1151 return true;