Move this call a bit up so that wpses without backdrop show none properly (the main...
[kugel-rb.git] / apps / gui / skin_engine / wps_display.c
blob6a94c6a946b2780de8eac67cf53098bac9b9ea67
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 gui_wps_redraw(struct gui_wps *gwps, unsigned refresh_mode);
71 bool gui_wps_display(struct gui_wps *gwps)
73 struct screen *display = gwps->display;
75 /* Update the values in the first (default) viewport - in case the user
76 has modified the statusbar or colour settings */
77 #if LCD_DEPTH > 1
78 if (display->depth > 1)
80 gwps->data->viewports[0].vp.fg_pattern = display->get_foreground();
81 gwps->data->viewports[0].vp.bg_pattern = display->get_background();
83 #endif
84 display->clear_display();
85 display->backdrop_show(BACKDROP_SKIN_WPS);
86 return gui_wps_redraw(gwps, WPS_REFRESH_ALL);
89 /* update a skinned screen, update_type is WPS_REFRESH_* values.
90 * Usually it should only be WPS_REFRESH_NON_STATIC
91 * A full update will be done if required (state.do_full_update == true)
93 bool skin_update(struct gui_wps *gwps, unsigned int update_type)
95 bool retval;
96 /* This maybe shouldnt be here, but while the skin is only used to
97 * display the music screen this is better than whereever we are being
98 * called from. This is also safe for skined screen which dont use the id3 */
99 struct mp3entry *id3 = gwps->state->id3;
100 bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
101 gwps->state->do_full_update = cuesheet_update || gwps->state->do_full_update;
103 retval = gui_wps_redraw(gwps, gwps->state->do_full_update ?
104 WPS_REFRESH_ALL : update_type);
105 return retval;
109 #ifdef HAVE_LCD_BITMAP
111 static void draw_progressbar(struct gui_wps *gwps,
112 struct wps_viewport *wps_vp)
114 struct screen *display = gwps->display;
115 struct wps_state *state = gwps->state;
116 struct progressbar *pb = wps_vp->pb;
117 int y = pb->y;
119 if (y < 0)
121 int line_height = font_get(wps_vp->vp.font)->height;
122 /* center the pb in the line, but only if the line is higher than the pb */
123 int center = (line_height-pb->height)/2;
124 /* if Y was not set calculate by font height,Y is -line_number-1 */
125 y = (-y -1)*line_height + (0 > center ? 0 : center);
128 if (pb->have_bitmap_pb)
129 gui_bitmap_scrollbar_draw(display, pb->bm,
130 pb->x, y, pb->width, pb->bm.height,
131 state->id3->length ? state->id3->length : 1, 0,
132 state->id3->length ? state->id3->elapsed
133 + state->ff_rewind_count : 0,
134 HORIZONTAL);
135 else
136 gui_scrollbar_draw(display, pb->x, y, pb->width, pb->height,
137 state->id3->length ? state->id3->length : 1, 0,
138 state->id3->length ? state->id3->elapsed
139 + state->ff_rewind_count : 0,
140 HORIZONTAL);
141 #ifdef AB_REPEAT_ENABLE
142 if ( ab_repeat_mode_enabled() && state->id3->length != 0 )
143 ab_draw_markers(display, state->id3->length,
144 pb->x, pb->x + pb->width, y, pb->height);
145 #endif
147 if (state->id3->cuesheet)
148 cue_draw_markers(display, state->id3->cuesheet, state->id3->length,
149 pb->x, pb->x + pb->width, y+1, pb->height-2);
152 /* clears the area where the image was shown */
153 static void clear_image_pos(struct gui_wps *gwps, int n)
155 if(!gwps)
156 return;
157 struct wps_data *data = gwps->data;
158 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
159 gwps->display->fillrect(data->img[n].x, data->img[n].y,
160 data->img[n].bm.width, data->img[n].subimage_height);
161 gwps->display->set_drawmode(DRMODE_SOLID);
164 static void wps_draw_image(struct gui_wps *gwps, int n, int subimage)
166 struct screen *display = gwps->display;
167 struct wps_data *data = gwps->data;
168 if(data->img[n].always_display)
169 display->set_drawmode(DRMODE_FG);
170 else
171 display->set_drawmode(DRMODE_SOLID);
173 #if LCD_DEPTH > 1
174 if(data->img[n].bm.format == FORMAT_MONO) {
175 #endif
176 display->mono_bitmap_part(data->img[n].bm.data,
177 0, data->img[n].subimage_height * subimage,
178 data->img[n].bm.width, data->img[n].x,
179 data->img[n].y, data->img[n].bm.width,
180 data->img[n].subimage_height);
181 #if LCD_DEPTH > 1
182 } else {
183 display->transparent_bitmap_part((fb_data *)data->img[n].bm.data,
184 0, data->img[n].subimage_height * subimage,
185 data->img[n].bm.width, data->img[n].x,
186 data->img[n].y, data->img[n].bm.width,
187 data->img[n].subimage_height);
189 #endif
192 static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
194 if(!gwps || !gwps->data || !gwps->display)
195 return;
197 int n;
198 struct wps_data *data = gwps->data;
199 struct screen *display = gwps->display;
201 for (n = 0; n < MAX_IMAGES; n++)
203 if (data->img[n].loaded)
205 if (data->img[n].display >= 0)
207 wps_draw_image(gwps, n, data->img[n].display);
208 } else if (data->img[n].always_display && data->img[n].vp == vp)
210 wps_draw_image(gwps, n, 0);
214 display->set_drawmode(DRMODE_SOLID);
217 #else /* HAVE_LCD_CHARCELL */
219 static bool draw_player_progress(struct gui_wps *gwps)
221 struct wps_state *state = gwps->state;
222 struct screen *display = gwps->display;
223 unsigned char progress_pattern[7];
224 int pos = 0;
225 int i;
227 if (!state->id3)
228 return false;
230 if (state->id3->length)
231 pos = 36 * (state->id3->elapsed + state->ff_rewind_count)
232 / state->id3->length;
234 for (i = 0; i < 7; i++, pos -= 5)
236 if (pos <= 0)
237 progress_pattern[i] = 0x1fu;
238 else if (pos >= 5)
239 progress_pattern[i] = 0x00u;
240 else
241 progress_pattern[i] = 0x1fu >> pos;
244 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
245 return true;
248 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
250 static const unsigned char numbers[10][4] = {
251 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
252 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
253 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
254 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
255 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
256 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
257 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
258 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
259 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
260 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
263 struct wps_state *state = gwps->state;
264 struct screen *display = gwps->display;
265 struct wps_data *data = gwps->data;
266 unsigned char progress_pattern[7];
267 char timestr[10];
268 int time;
269 int time_idx = 0;
270 int pos = 0;
271 int pat_idx = 1;
272 int digit, i, j;
273 bool softchar;
275 if (!state->id3 || buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
276 return;
278 time = state->id3->elapsed + state->ff_rewind_count;
279 if (state->id3->length)
280 pos = 55 * time / state->id3->length;
282 memset(timestr, 0, sizeof(timestr));
283 format_time(timestr, sizeof(timestr)-2, time);
284 timestr[strlen(timestr)] = ':'; /* always safe */
286 for (i = 0; i < 11; i++, pos -= 5)
288 softchar = false;
289 memset(progress_pattern, 0, sizeof(progress_pattern));
291 if ((digit = timestr[time_idx]))
293 softchar = true;
294 digit -= '0';
296 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
298 memcpy(progress_pattern, numbers[digit], 4);
299 time_idx += 2;
301 else /* tens, shifted right */
303 for (j = 0; j < 4; j++)
304 progress_pattern[j] = numbers[digit][j] >> 1;
306 if (time_idx > 0) /* not the first group, add colon in front */
308 progress_pattern[1] |= 0x10u;
309 progress_pattern[3] |= 0x10u;
311 time_idx++;
314 if (pos >= 5)
315 progress_pattern[5] = progress_pattern[6] = 0x1fu;
318 if (pos > 0 && pos < 5)
320 softchar = true;
321 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
324 if (softchar && pat_idx < 8)
326 display->define_pattern(data->wps_progress_pat[pat_idx],
327 progress_pattern);
328 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
329 pat_idx++;
331 else if (pos <= 0)
332 buf = utf8encode(' ', buf);
333 else
334 buf = utf8encode(0xe115, buf); /* 2/7 _ */
336 *buf = '\0';
339 #endif /* HAVE_LCD_CHARCELL */
341 /* Return the index to the end token for the conditional token at index.
342 The conditional token can be either a start token or a separator
343 (i.e. option) token.
345 static int find_conditional_end(struct wps_data *data, int index)
347 int ret = index;
348 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
349 ret = data->tokens[ret].value.i;
351 /* ret now is the index to the end token for the conditional. */
352 return ret;
355 /* Evaluate the conditional that is at *token_index and return whether a skip
356 has ocurred. *token_index is updated with the new position.
358 static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
360 if (!gwps)
361 return false;
363 struct wps_data *data = gwps->data;
365 int i, cond_end;
366 int cond_index = *token_index;
367 char result[128];
368 const char *value;
369 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
370 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
372 /* treat ?xx<true> constructs as if they had 2 options. */
373 if (num_options < 2)
374 num_options = 2;
376 int intval = num_options;
377 /* get_token_value needs to know the number of options in the enum */
378 value = get_token_value(gwps, &data->tokens[cond_index + 1],
379 result, sizeof(result), &intval);
381 /* intval is now the number of the enum option we want to read,
382 starting from 1. If intval is -1, we check if value is empty. */
383 if (intval == -1)
384 intval = (value && *value) ? 1 : num_options;
385 else if (intval > num_options || intval < 1)
386 intval = num_options;
388 data->tokens[cond_index].value.i = (intval << 8) + num_options;
390 /* skip to the appropriate enum case */
391 int next = cond_index + 2;
392 for (i = 1; i < intval; i++)
394 next = data->tokens[next].value.i;
396 *token_index = next;
398 if (prev_val == intval)
400 /* Same conditional case as previously. Return without clearing the
401 pictures */
402 return false;
405 cond_end = find_conditional_end(data, cond_index + 2);
406 for (i = cond_index + 3; i < cond_end; i++)
408 #ifdef HAVE_LCD_BITMAP
409 /* clear all pictures in the conditional and nested ones */
410 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
411 clear_image_pos(gwps, data->tokens[i].value.i & 0xFF);
412 #endif
413 #ifdef HAVE_ALBUMART
414 if (data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
415 draw_album_art(gwps, audio_current_aa_hid(), true);
416 #endif
419 return true;
422 /* Read a (sub)line to the given alignment format buffer.
423 linebuf is the buffer where the data is actually stored.
424 align is the alignment format that'll be used to display the text.
425 The return value indicates whether the line needs to be updated.
427 static bool get_line(struct gui_wps *gwps,
428 int line, int subline,
429 struct align_pos *align,
430 char *linebuf,
431 int linebuf_size)
433 struct wps_data *data = gwps->data;
435 char temp_buf[128];
436 char *buf = linebuf; /* will always point to the writing position */
437 char *linebuf_end = linebuf + linebuf_size - 1;
438 int i, last_token_idx;
439 bool update = false;
441 /* alignment-related variables */
442 int cur_align;
443 char* cur_align_start;
444 cur_align_start = buf;
445 cur_align = WPS_ALIGN_LEFT;
446 align->left = NULL;
447 align->center = NULL;
448 align->right = NULL;
450 /* Process all tokens of the desired subline */
451 last_token_idx = wps_last_token_index(data, line, subline);
452 for (i = wps_first_token_index(data, line, subline);
453 i <= last_token_idx; i++)
455 switch(data->tokens[i].type)
457 case WPS_TOKEN_CONDITIONAL:
458 /* place ourselves in the right conditional case */
459 update |= evaluate_conditional(gwps, &i);
460 break;
462 case WPS_TOKEN_CONDITIONAL_OPTION:
463 /* we've finished in the curent conditional case,
464 skip to the end of the conditional structure */
465 i = find_conditional_end(data, i);
466 break;
468 #ifdef HAVE_LCD_BITMAP
469 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
471 struct gui_img *img = data->img;
472 int n = data->tokens[i].value.i & 0xFF;
473 int subimage = data->tokens[i].value.i >> 8;
475 if (n >= 0 && n < MAX_IMAGES && img[n].loaded)
476 img[n].display = subimage;
477 break;
479 #endif
481 case WPS_TOKEN_ALIGN_LEFT:
482 case WPS_TOKEN_ALIGN_CENTER:
483 case WPS_TOKEN_ALIGN_RIGHT:
484 /* remember where the current aligned text started */
485 switch (cur_align)
487 case WPS_ALIGN_LEFT:
488 align->left = cur_align_start;
489 break;
491 case WPS_ALIGN_CENTER:
492 align->center = cur_align_start;
493 break;
495 case WPS_ALIGN_RIGHT:
496 align->right = cur_align_start;
497 break;
499 /* start a new alignment */
500 switch (data->tokens[i].type)
502 case WPS_TOKEN_ALIGN_LEFT:
503 cur_align = WPS_ALIGN_LEFT;
504 break;
505 case WPS_TOKEN_ALIGN_CENTER:
506 cur_align = WPS_ALIGN_CENTER;
507 break;
508 case WPS_TOKEN_ALIGN_RIGHT:
509 cur_align = WPS_ALIGN_RIGHT;
510 break;
511 default:
512 break;
514 *buf++ = 0;
515 cur_align_start = buf;
516 break;
517 case WPS_VIEWPORT_ENABLE:
519 char label = data->tokens[i].value.i;
520 int j;
521 char temp = VP_DRAW_HIDEABLE;
522 for(j=0;j<data->num_viewports;j++)
524 temp = VP_DRAW_HIDEABLE;
525 if ((data->viewports[j].hidden_flags&VP_DRAW_HIDEABLE) &&
526 (data->viewports[j].label == label))
528 if (data->viewports[j].hidden_flags&VP_DRAW_WASHIDDEN)
529 temp |= VP_DRAW_WASHIDDEN;
530 data->viewports[j].hidden_flags = temp;
534 break;
535 default:
537 /* get the value of the tag and copy it to the buffer */
538 const char *value = get_token_value(gwps, &data->tokens[i],
539 temp_buf, sizeof(temp_buf), NULL);
540 if (value)
542 update = true;
543 while (*value && (buf < linebuf_end))
544 *buf++ = *value++;
546 break;
551 /* close the current alignment */
552 switch (cur_align)
554 case WPS_ALIGN_LEFT:
555 align->left = cur_align_start;
556 break;
558 case WPS_ALIGN_CENTER:
559 align->center = cur_align_start;
560 break;
562 case WPS_ALIGN_RIGHT:
563 align->right = cur_align_start;
564 break;
567 return update;
570 static void get_subline_timeout(struct gui_wps *gwps, int line, int subline)
572 struct wps_data *data = gwps->data;
573 int i;
574 int subline_idx = wps_subline_index(data, line, subline);
575 int last_token_idx = wps_last_token_index(data, line, subline);
577 data->sublines[subline_idx].time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
579 for (i = wps_first_token_index(data, line, subline);
580 i <= last_token_idx; i++)
582 switch(data->tokens[i].type)
584 case WPS_TOKEN_CONDITIONAL:
585 /* place ourselves in the right conditional case */
586 evaluate_conditional(gwps, &i);
587 break;
589 case WPS_TOKEN_CONDITIONAL_OPTION:
590 /* we've finished in the curent conditional case,
591 skip to the end of the conditional structure */
592 i = find_conditional_end(data, i);
593 break;
595 case WPS_TOKEN_SUBLINE_TIMEOUT:
596 data->sublines[subline_idx].time_mult = data->tokens[i].value.i;
597 break;
599 default:
600 break;
605 /* Calculates which subline should be displayed for the specified line
606 Returns true iff the subline must be refreshed */
607 static bool update_curr_subline(struct gui_wps *gwps, int line)
609 struct wps_data *data = gwps->data;
611 int search, search_start, num_sublines;
612 bool reset_subline;
613 bool new_subline_refresh;
614 bool only_one_subline;
616 num_sublines = data->lines[line].num_sublines;
617 reset_subline = (data->lines[line].curr_subline == SUBLINE_RESET);
618 new_subline_refresh = false;
619 only_one_subline = false;
621 /* if time to advance to next sub-line */
622 if (TIME_AFTER(current_tick, data->lines[line].subline_expire_time - 1) ||
623 reset_subline)
625 /* search all sublines until the next subline with time > 0
626 is found or we get back to the subline we started with */
627 if (reset_subline)
628 search_start = 0;
629 else
630 search_start = data->lines[line].curr_subline;
632 for (search = 0; search < num_sublines; search++)
634 data->lines[line].curr_subline++;
636 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
637 if (data->lines[line].curr_subline == num_sublines)
639 if (data->lines[line].curr_subline == 1)
640 only_one_subline = true;
641 data->lines[line].curr_subline = 0;
644 /* if back where we started after search or
645 only one subline is defined on the line */
646 if (((search > 0) &&
647 (data->lines[line].curr_subline == search_start)) ||
648 only_one_subline)
650 /* no other subline with a time > 0 exists */
651 data->lines[line].subline_expire_time = (reset_subline ?
652 current_tick :
653 data->lines[line].subline_expire_time) + 100 * HZ;
654 break;
656 else
658 /* get initial time multiplier for this subline */
659 get_subline_timeout(gwps, line, data->lines[line].curr_subline);
661 int subline_idx = wps_subline_index(data, line,
662 data->lines[line].curr_subline);
664 /* only use this subline if subline time > 0 */
665 if (data->sublines[subline_idx].time_mult > 0)
667 new_subline_refresh = true;
668 data->lines[line].subline_expire_time = (reset_subline ?
669 current_tick : data->lines[line].subline_expire_time) +
670 TIMEOUT_UNIT*data->sublines[subline_idx].time_mult;
671 break;
677 return new_subline_refresh;
680 /* Display a line appropriately according to its alignment format.
681 format_align contains the text, separated between left, center and right.
682 line is the index of the line on the screen.
683 scroll indicates whether the line is a scrolling one or not.
685 static void write_line(struct screen *display,
686 struct align_pos *format_align,
687 int line,
688 bool scroll)
690 int left_width = 0, left_xpos;
691 int center_width = 0, center_xpos;
692 int right_width = 0, right_xpos;
693 int ypos;
694 int space_width;
695 int string_height;
696 int scroll_width;
698 /* calculate different string sizes and positions */
699 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
700 if (format_align->left != 0) {
701 display->getstringsize((unsigned char *)format_align->left,
702 &left_width, &string_height);
705 if (format_align->right != 0) {
706 display->getstringsize((unsigned char *)format_align->right,
707 &right_width, &string_height);
710 if (format_align->center != 0) {
711 display->getstringsize((unsigned char *)format_align->center,
712 &center_width, &string_height);
715 left_xpos = 0;
716 right_xpos = (display->getwidth() - right_width);
717 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
719 scroll_width = display->getwidth() - left_xpos;
721 /* Checks for overlapping strings.
722 If needed the overlapping strings will be merged, separated by a
723 space */
725 /* CASE 1: left and centered string overlap */
726 /* there is a left string, need to merge left and center */
727 if ((left_width != 0 && center_width != 0) &&
728 (left_xpos + left_width + space_width > center_xpos)) {
729 /* replace the former separator '\0' of left and
730 center string with a space */
731 *(--format_align->center) = ' ';
732 /* calculate the new width and position of the merged string */
733 left_width = left_width + space_width + center_width;
734 /* there is no centered string anymore */
735 center_width = 0;
737 /* there is no left string, move center to left */
738 if ((left_width == 0 && center_width != 0) &&
739 (left_xpos + left_width > center_xpos)) {
740 /* move the center string to the left string */
741 format_align->left = format_align->center;
742 /* calculate the new width and position of the string */
743 left_width = center_width;
744 /* there is no centered string anymore */
745 center_width = 0;
748 /* CASE 2: centered and right string overlap */
749 /* there is a right string, need to merge center and right */
750 if ((center_width != 0 && right_width != 0) &&
751 (center_xpos + center_width + space_width > right_xpos)) {
752 /* replace the former separator '\0' of center and
753 right string with a space */
754 *(--format_align->right) = ' ';
755 /* move the center string to the right after merge */
756 format_align->right = format_align->center;
757 /* calculate the new width and position of the merged string */
758 right_width = center_width + space_width + right_width;
759 right_xpos = (display->getwidth() - right_width);
760 /* there is no centered string anymore */
761 center_width = 0;
763 /* there is no right string, move center to right */
764 if ((center_width != 0 && right_width == 0) &&
765 (center_xpos + center_width > right_xpos)) {
766 /* move the center string to the right string */
767 format_align->right = format_align->center;
768 /* calculate the new width and position of the string */
769 right_width = center_width;
770 right_xpos = (display->getwidth() - right_width);
771 /* there is no centered string anymore */
772 center_width = 0;
775 /* CASE 3: left and right overlap
776 There is no center string anymore, either there never
777 was one or it has been merged in case 1 or 2 */
778 /* there is a left string, need to merge left and right */
779 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
780 (left_xpos + left_width + space_width > right_xpos)) {
781 /* replace the former separator '\0' of left and
782 right string with a space */
783 *(--format_align->right) = ' ';
784 /* calculate the new width and position of the string */
785 left_width = left_width + space_width + right_width;
786 /* there is no right string anymore */
787 right_width = 0;
789 /* there is no left string, move right to left */
790 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
791 (left_width > right_xpos)) {
792 /* move the right string to the left string */
793 format_align->left = format_align->right;
794 /* calculate the new width and position of the string */
795 left_width = right_width;
796 /* there is no right string anymore */
797 right_width = 0;
800 ypos = (line * string_height);
803 if (scroll && ((left_width > scroll_width) ||
804 (center_width > scroll_width) ||
805 (right_width > scroll_width)))
807 display->puts_scroll(0, line,
808 (unsigned char *)format_align->left);
810 else
812 #ifdef HAVE_LCD_BITMAP
813 /* clear the line first */
814 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
815 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
816 display->set_drawmode(DRMODE_SOLID);
817 #endif
819 /* Nasty hack: we output an empty scrolling string,
820 which will reset the scroller for that line */
821 display->puts_scroll(0, line, (unsigned char *)"");
823 /* print aligned strings */
824 if (left_width != 0)
826 display->putsxy(left_xpos, ypos,
827 (unsigned char *)format_align->left);
829 if (center_width != 0)
831 display->putsxy(center_xpos, ypos,
832 (unsigned char *)format_align->center);
834 if (right_width != 0)
836 display->putsxy(right_xpos, ypos,
837 (unsigned char *)format_align->right);
842 static bool gui_wps_redraw(struct gui_wps *gwps, unsigned refresh_mode)
844 struct wps_data *data = gwps->data;
845 struct screen *display = gwps->display;
846 struct wps_state *state = gwps->state;
848 if (!data || !state || !display)
849 return false;
851 struct mp3entry *id3 = state->id3;
853 if (!id3)
854 return false;
856 int v, line, i, subline_idx;
857 unsigned flags;
858 char linebuf[MAX_PATH];
860 struct align_pos align;
861 align.left = NULL;
862 align.center = NULL;
863 align.right = NULL;
865 bool update_line, new_subline_refresh;
867 #ifdef HAVE_LCD_BITMAP
869 /* to find out wether the peak meter is enabled we
870 assume it wasn't until we find a line that contains
871 the peak meter. We can't use peak_meter_enabled itself
872 because that would mean to turn off the meter thread
873 temporarily. (That shouldn't matter unless yield
874 or sleep is called but who knows...)
876 bool enable_pm = false;
878 #endif
880 /* reset to first subline if refresh all flag is set */
881 if (refresh_mode == WPS_REFRESH_ALL)
883 display->set_viewport(&data->viewports[0].vp);
884 display->clear_viewport();
886 for (i = 0; i <= data->num_lines; i++)
888 data->lines[i].curr_subline = SUBLINE_RESET;
892 #ifdef HAVE_LCD_CHARCELLS
893 for (i = 0; i < 8; i++)
895 if (data->wps_progress_pat[i] == 0)
896 data->wps_progress_pat[i] = display->get_locked_pattern();
898 #endif
900 /* disable any viewports which are conditionally displayed */
901 for (v = 0; v < data->num_viewports; v++)
903 if (data->viewports[v].hidden_flags&VP_DRAW_HIDEABLE)
905 if (data->viewports[v].hidden_flags&VP_DRAW_HIDDEN)
906 data->viewports[v].hidden_flags |= VP_DRAW_WASHIDDEN;
907 else
908 data->viewports[v].hidden_flags |= VP_DRAW_HIDDEN;
911 for (v = 0; v < data->num_viewports; v++)
913 struct wps_viewport *wps_vp = &(data->viewports[v]);
914 unsigned vp_refresh_mode = refresh_mode;
915 display->set_viewport(&wps_vp->vp);
917 #ifdef HAVE_LCD_BITMAP
918 /* Set images to not to be displayed */
919 for (i = 0; i < MAX_IMAGES; i++)
921 data->img[i].display = -1;
923 #endif
924 /* dont redraw the viewport if its disabled */
925 if ((wps_vp->hidden_flags&VP_DRAW_HIDDEN))
927 if (!(wps_vp->hidden_flags&VP_DRAW_WASHIDDEN))
928 display->scroll_stop(&wps_vp->vp);
929 wps_vp->hidden_flags |= VP_DRAW_WASHIDDEN;
930 continue;
932 else if (((wps_vp->hidden_flags&
933 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
934 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
936 vp_refresh_mode = WPS_REFRESH_ALL;
937 wps_vp->hidden_flags = VP_DRAW_HIDEABLE;
939 if (vp_refresh_mode == WPS_REFRESH_ALL)
941 display->clear_viewport();
944 for (line = wps_vp->first_line;
945 line <= wps_vp->last_line; line++)
947 memset(linebuf, 0, sizeof(linebuf));
948 update_line = false;
950 /* get current subline for the line */
951 new_subline_refresh = update_curr_subline(gwps, line);
953 subline_idx = wps_subline_index(data, line,
954 data->lines[line].curr_subline);
955 flags = data->sublines[subline_idx].line_type;
957 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
958 || new_subline_refresh)
960 /* get_line tells us if we need to update the line */
961 update_line = get_line(gwps, line, data->lines[line].curr_subline,
962 &align, linebuf, sizeof(linebuf));
964 #ifdef HAVE_LCD_BITMAP
965 /* peakmeter */
966 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
968 /* the peakmeter should be alone on its line */
969 update_line = false;
971 int h = font_get(wps_vp->vp.font)->height;
972 int peak_meter_y = (line - wps_vp->first_line)* h;
974 /* The user might decide to have the peak meter in the last
975 line so that it is only displayed if no status bar is
976 visible. If so we neither want do draw nor enable the
977 peak meter. */
978 if (peak_meter_y + h <= display->getheight()) {
979 /* found a line with a peak meter -> remember that we must
980 enable it later */
981 enable_pm = true;
982 peak_meter_enabled = true;
983 peak_meter_screen(gwps->display, 0, peak_meter_y,
984 MIN(h, display->getheight() - peak_meter_y));
986 else
988 peak_meter_enabled = false;
992 #else /* HAVE_LCD_CHARCELL */
994 /* progressbar */
995 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
997 if (data->full_line_progressbar)
998 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
999 else
1000 draw_player_progress(gwps);
1002 #endif
1004 if (update_line &&
1005 /* conditionals clear the line which means if the %Vd is put into the default
1006 viewport there will be a blank line.
1007 To get around this we dont allow any actual drawing to happen in the
1008 deault vp if other vp's are defined */
1009 ((data->num_viewports>1 && v!=0) || data->num_viewports == 1))
1011 if (flags & WPS_REFRESH_SCROLL)
1013 /* if the line is a scrolling one we don't want to update
1014 too often, so that it has the time to scroll */
1015 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1016 write_line(display, &align, line - wps_vp->first_line, true);
1018 else
1019 write_line(display, &align, line - wps_vp->first_line, false);
1023 #ifdef HAVE_LCD_BITMAP
1024 /* progressbar */
1025 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1027 if (wps_vp->pb)
1029 draw_progressbar(gwps, wps_vp);
1032 /* Now display any images in this viewport */
1033 wps_display_images(gwps, &wps_vp->vp);
1034 #endif
1037 #ifdef HAVE_LCD_BITMAP
1038 data->peak_meter_enabled = enable_pm;
1039 #endif
1041 if (refresh_mode & WPS_REFRESH_STATUSBAR)
1043 gwps_draw_statusbars();
1045 /* Restore the default viewport */
1046 display->set_viewport(NULL);
1048 display->update();
1050 return true;