summaryrefslogtreecommitdiff
path: root/apps/gui/skin_engine/wps_display.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/gui/skin_engine/wps_display.c')
-rw-r--r--apps/gui/skin_engine/wps_display.c1058
1 files changed, 1058 insertions, 0 deletions
diff --git a/apps/gui/skin_engine/wps_display.c b/apps/gui/skin_engine/wps_display.c
new file mode 100644
index 0000000000..98050093c4
--- /dev/null
+++ b/apps/gui/skin_engine/wps_display.c
@@ -0,0 +1,1058 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002-2007 Björn Stenberg
11 * Copyright (C) 2007-2008 Nicolas Pennequin
12 *
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.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
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"
63
64
65#include "wps_internals.h"
66#include "skin_engine.h"
67
68static bool gui_wps_redraw(struct gui_wps *gwps, unsigned refresh_mode);
69
70
71bool gui_wps_display(struct gui_wps *gwps)
72{
73 struct screen *display = gwps->display;
74
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)
79 {
80 gwps->data->viewports[0].vp.fg_pattern = display->get_foreground();
81 gwps->data->viewports[0].vp.bg_pattern = display->get_background();
82 }
83#endif
84 display->clear_display();
85#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
86 if (display->screen_type == SCREEN_REMOTE)
87 show_remote_wps_backdrop();
88 else if (display->screen_type == SCREEN_MAIN)
89#endif
90#if LCD_DEPTH > 1
91 show_wps_backdrop();
92#endif
93 return gui_wps_redraw(gwps, WPS_REFRESH_ALL);
94}
95
96/* update a skinned screen, update_type is WPS_REFRESH_* values.
97 * Usually it should only be WPS_REFRESH_NON_STATIC
98 * A full update will be done if required (state.do_full_update == true)
99 */
100bool skin_update(struct gui_wps *gwps, unsigned int update_type)
101{
102 bool retval;
103 /* This maybe shouldnt be here, but while the skin is only used to
104 * display the music screen this is better than whereever we are being
105 * called from. This is also safe for skined screen which dont use the id3 */
106 struct mp3entry *id3 = gwps->state->id3;
107 bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
108 gwps->state->do_full_update = cuesheet_update || gwps->state->do_full_update;
109
110 retval = gui_wps_redraw(gwps, gwps->state->do_full_update ?
111 WPS_REFRESH_ALL : update_type);
112 return retval;
113}
114
115
116#ifdef HAVE_LCD_BITMAP
117
118static void draw_progressbar(struct gui_wps *gwps,
119 struct wps_viewport *wps_vp)
120 {
121 struct screen *display = gwps->display;
122 struct wps_state *state = gwps->state;
123 struct progressbar *pb = wps_vp->pb;
124 int y = pb->y;
125
126 if (y < 0)
127 {
128 int line_height = font_get(wps_vp->vp.font)->height;
129 /* center the pb in the line, but only if the line is higher than the pb */
130 int center = (line_height-pb->height)/2;
131 /* if Y was not set calculate by font height,Y is -line_number-1 */
132 y = (-y -1)*line_height + (0 > center ? 0 : center);
133 }
134
135 if (pb->have_bitmap_pb)
136 gui_bitmap_scrollbar_draw(display, pb->bm,
137 pb->x, y, pb->width, pb->bm.height,
138 state->id3->length ? state->id3->length : 1, 0,
139 state->id3->length ? state->id3->elapsed
140 + state->ff_rewind_count : 0,
141 HORIZONTAL);
142 else
143 gui_scrollbar_draw(display, pb->x, y, pb->width, pb->height,
144 state->id3->length ? state->id3->length : 1, 0,
145 state->id3->length ? state->id3->elapsed
146 + state->ff_rewind_count : 0,
147 HORIZONTAL);
148#ifdef AB_REPEAT_ENABLE
149 if ( ab_repeat_mode_enabled() && state->id3->length != 0 )
150 ab_draw_markers(display, state->id3->length,
151 pb->x, pb->x + pb->width, y, pb->height);
152#endif
153
154 if (state->id3->cuesheet)
155 cue_draw_markers(display, state->id3->cuesheet, state->id3->length,
156 pb->x, pb->x + pb->width, y+1, pb->height-2);
157}
158
159/* clears the area where the image was shown */
160static void clear_image_pos(struct gui_wps *gwps, int n)
161{
162 if(!gwps)
163 return;
164 struct wps_data *data = gwps->data;
165 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
166 gwps->display->fillrect(data->img[n].x, data->img[n].y,
167 data->img[n].bm.width, data->img[n].subimage_height);
168 gwps->display->set_drawmode(DRMODE_SOLID);
169}
170
171static void wps_draw_image(struct gui_wps *gwps, int n, int subimage)
172{
173 struct screen *display = gwps->display;
174 struct wps_data *data = gwps->data;
175 if(data->img[n].always_display)
176 display->set_drawmode(DRMODE_FG);
177 else
178 display->set_drawmode(DRMODE_SOLID);
179
180#if LCD_DEPTH > 1
181 if(data->img[n].bm.format == FORMAT_MONO) {
182#endif
183 display->mono_bitmap_part(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);
188#if LCD_DEPTH > 1
189 } else {
190 display->transparent_bitmap_part((fb_data *)data->img[n].bm.data,
191 0, data->img[n].subimage_height * subimage,
192 data->img[n].bm.width, data->img[n].x,
193 data->img[n].y, data->img[n].bm.width,
194 data->img[n].subimage_height);
195 }
196#endif
197}
198
199static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
200{
201 if(!gwps || !gwps->data || !gwps->display)
202 return;
203
204 int n;
205 struct wps_data *data = gwps->data;
206 struct screen *display = gwps->display;
207
208 for (n = 0; n < MAX_IMAGES; n++)
209 {
210 if (data->img[n].loaded)
211 {
212 if (data->img[n].display >= 0)
213 {
214 wps_draw_image(gwps, n, data->img[n].display);
215 } else if (data->img[n].always_display && data->img[n].vp == vp)
216 {
217 wps_draw_image(gwps, n, 0);
218 }
219 }
220 }
221 display->set_drawmode(DRMODE_SOLID);
222}
223
224#else /* HAVE_LCD_CHARCELL */
225
226static bool draw_player_progress(struct gui_wps *gwps)
227{
228 struct wps_state *state = gwps->state;
229 struct screen *display = gwps->display;
230 unsigned char progress_pattern[7];
231 int pos = 0;
232 int i;
233
234 if (!state->id3)
235 return false;
236
237 if (state->id3->length)
238 pos = 36 * (state->id3->elapsed + state->ff_rewind_count)
239 / state->id3->length;
240
241 for (i = 0; i < 7; i++, pos -= 5)
242 {
243 if (pos <= 0)
244 progress_pattern[i] = 0x1fu;
245 else if (pos >= 5)
246 progress_pattern[i] = 0x00u;
247 else
248 progress_pattern[i] = 0x1fu >> pos;
249 }
250
251 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
252 return true;
253}
254
255static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
256{
257 static const unsigned char numbers[10][4] = {
258 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
259 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
260 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
261 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
262 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
263 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
264 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
265 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
266 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
267 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
268 };
269
270 struct wps_state *state = gwps->state;
271 struct screen *display = gwps->display;
272 struct wps_data *data = gwps->data;
273 unsigned char progress_pattern[7];
274 char timestr[10];
275 int time;
276 int time_idx = 0;
277 int pos = 0;
278 int pat_idx = 1;
279 int digit, i, j;
280 bool softchar;
281
282 if (!state->id3 || buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
283 return;
284
285 time = state->id3->elapsed + state->ff_rewind_count;
286 if (state->id3->length)
287 pos = 55 * time / state->id3->length;
288
289 memset(timestr, 0, sizeof(timestr));
290 format_time(timestr, sizeof(timestr)-2, time);
291 timestr[strlen(timestr)] = ':'; /* always safe */
292
293 for (i = 0; i < 11; i++, pos -= 5)
294 {
295 softchar = false;
296 memset(progress_pattern, 0, sizeof(progress_pattern));
297
298 if ((digit = timestr[time_idx]))
299 {
300 softchar = true;
301 digit -= '0';
302
303 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
304 {
305 memcpy(progress_pattern, numbers[digit], 4);
306 time_idx += 2;
307 }
308 else /* tens, shifted right */
309 {
310 for (j = 0; j < 4; j++)
311 progress_pattern[j] = numbers[digit][j] >> 1;
312
313 if (time_idx > 0) /* not the first group, add colon in front */
314 {
315 progress_pattern[1] |= 0x10u;
316 progress_pattern[3] |= 0x10u;
317 }
318 time_idx++;
319 }
320
321 if (pos >= 5)
322 progress_pattern[5] = progress_pattern[6] = 0x1fu;
323 }
324
325 if (pos > 0 && pos < 5)
326 {
327 softchar = true;
328 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
329 }
330
331 if (softchar && pat_idx < 8)
332 {
333 display->define_pattern(data->wps_progress_pat[pat_idx],
334 progress_pattern);
335 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
336 pat_idx++;
337 }
338 else if (pos <= 0)
339 buf = utf8encode(' ', buf);
340 else
341 buf = utf8encode(0xe115, buf); /* 2/7 _ */
342 }
343 *buf = '\0';
344}
345
346#endif /* HAVE_LCD_CHARCELL */
347
348/* Return the index to the end token for the conditional token at index.
349 The conditional token can be either a start token or a separator
350 (i.e. option) token.
351*/
352static int find_conditional_end(struct wps_data *data, int index)
353{
354 int ret = index;
355 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
356 ret = data->tokens[ret].value.i;
357
358 /* ret now is the index to the end token for the conditional. */
359 return ret;
360}
361
362/* Evaluate the conditional that is at *token_index and return whether a skip
363 has ocurred. *token_index is updated with the new position.
364*/
365static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
366{
367 if (!gwps)
368 return false;
369
370 struct wps_data *data = gwps->data;
371
372 int i, cond_end;
373 int cond_index = *token_index;
374 char result[128];
375 const char *value;
376 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
377 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
378
379 /* treat ?xx<true> constructs as if they had 2 options. */
380 if (num_options < 2)
381 num_options = 2;
382
383 int intval = num_options;
384 /* get_token_value needs to know the number of options in the enum */
385 value = get_token_value(gwps, &data->tokens[cond_index + 1],
386 result, sizeof(result), &intval);
387
388 /* intval is now the number of the enum option we want to read,
389 starting from 1. If intval is -1, we check if value is empty. */
390 if (intval == -1)
391 intval = (value && *value) ? 1 : num_options;
392 else if (intval > num_options || intval < 1)
393 intval = num_options;
394
395 data->tokens[cond_index].value.i = (intval << 8) + num_options;
396
397 /* skip to the appropriate enum case */
398 int next = cond_index + 2;
399 for (i = 1; i < intval; i++)
400 {
401 next = data->tokens[next].value.i;
402 }
403 *token_index = next;
404
405 if (prev_val == intval)
406 {
407 /* Same conditional case as previously. Return without clearing the
408 pictures */
409 return false;
410 }
411
412 cond_end = find_conditional_end(data, cond_index + 2);
413 for (i = cond_index + 3; i < cond_end; i++)
414 {
415#ifdef HAVE_LCD_BITMAP
416 /* clear all pictures in the conditional and nested ones */
417 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
418 clear_image_pos(gwps, data->tokens[i].value.i & 0xFF);
419#endif
420#ifdef HAVE_ALBUMART
421 if (data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
422 draw_album_art(gwps, audio_current_aa_hid(), true);
423#endif
424 }
425
426 return true;
427}
428
429/* Read a (sub)line to the given alignment format buffer.
430 linebuf is the buffer where the data is actually stored.
431 align is the alignment format that'll be used to display the text.
432 The return value indicates whether the line needs to be updated.
433*/
434static bool get_line(struct gui_wps *gwps,
435 int line, int subline,
436 struct align_pos *align,
437 char *linebuf,
438 int linebuf_size)
439{
440 struct wps_data *data = gwps->data;
441
442 char temp_buf[128];
443 char *buf = linebuf; /* will always point to the writing position */
444 char *linebuf_end = linebuf + linebuf_size - 1;
445 int i, last_token_idx;
446 bool update = false;
447
448 /* alignment-related variables */
449 int cur_align;
450 char* cur_align_start;
451 cur_align_start = buf;
452 cur_align = WPS_ALIGN_LEFT;
453 align->left = NULL;
454 align->center = NULL;
455 align->right = NULL;
456
457 /* Process all tokens of the desired subline */
458 last_token_idx = wps_last_token_index(data, line, subline);
459 for (i = wps_first_token_index(data, line, subline);
460 i <= last_token_idx; i++)
461 {
462 switch(data->tokens[i].type)
463 {
464 case WPS_TOKEN_CONDITIONAL:
465 /* place ourselves in the right conditional case */
466 update |= evaluate_conditional(gwps, &i);
467 break;
468
469 case WPS_TOKEN_CONDITIONAL_OPTION:
470 /* we've finished in the curent conditional case,
471 skip to the end of the conditional structure */
472 i = find_conditional_end(data, i);
473 break;
474
475#ifdef HAVE_LCD_BITMAP
476 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
477 {
478 struct gui_img *img = data->img;
479 int n = data->tokens[i].value.i & 0xFF;
480 int subimage = data->tokens[i].value.i >> 8;
481
482 if (n >= 0 && n < MAX_IMAGES && img[n].loaded)
483 img[n].display = subimage;
484 break;
485 }
486#endif
487
488 case WPS_TOKEN_ALIGN_LEFT:
489 case WPS_TOKEN_ALIGN_CENTER:
490 case WPS_TOKEN_ALIGN_RIGHT:
491 /* remember where the current aligned text started */
492 switch (cur_align)
493 {
494 case WPS_ALIGN_LEFT:
495 align->left = cur_align_start;
496 break;
497
498 case WPS_ALIGN_CENTER:
499 align->center = cur_align_start;
500 break;
501
502 case WPS_ALIGN_RIGHT:
503 align->right = cur_align_start;
504 break;
505 }
506 /* start a new alignment */
507 switch (data->tokens[i].type)
508 {
509 case WPS_TOKEN_ALIGN_LEFT:
510 cur_align = WPS_ALIGN_LEFT;
511 break;
512 case WPS_TOKEN_ALIGN_CENTER:
513 cur_align = WPS_ALIGN_CENTER;
514 break;
515 case WPS_TOKEN_ALIGN_RIGHT:
516 cur_align = WPS_ALIGN_RIGHT;
517 break;
518 default:
519 break;
520 }
521 *buf++ = 0;
522 cur_align_start = buf;
523 break;
524 case WPS_VIEWPORT_ENABLE:
525 {
526 char label = data->tokens[i].value.i;
527 int j;
528 char temp = VP_DRAW_HIDEABLE;
529 for(j=0;j<data->num_viewports;j++)
530 {
531 temp = VP_DRAW_HIDEABLE;
532 if ((data->viewports[j].hidden_flags&VP_DRAW_HIDEABLE) &&
533 (data->viewports[j].label == label))
534 {
535 if (data->viewports[j].hidden_flags&VP_DRAW_WASHIDDEN)
536 temp |= VP_DRAW_WASHIDDEN;
537 data->viewports[j].hidden_flags = temp;
538 }
539 }
540 }
541 break;
542 default:
543 {
544 /* get the value of the tag and copy it to the buffer */
545 const char *value = get_token_value(gwps, &data->tokens[i],
546 temp_buf, sizeof(temp_buf), NULL);
547 if (value)
548 {
549 update = true;
550 while (*value && (buf < linebuf_end))
551 *buf++ = *value++;
552 }
553 break;
554 }
555 }
556 }
557
558 /* close the current alignment */
559 switch (cur_align)
560 {
561 case WPS_ALIGN_LEFT:
562 align->left = cur_align_start;
563 break;
564
565 case WPS_ALIGN_CENTER:
566 align->center = cur_align_start;
567 break;
568
569 case WPS_ALIGN_RIGHT:
570 align->right = cur_align_start;
571 break;
572 }
573
574 return update;
575}
576
577static void get_subline_timeout(struct gui_wps *gwps, int line, int subline)
578{
579 struct wps_data *data = gwps->data;
580 int i;
581 int subline_idx = wps_subline_index(data, line, subline);
582 int last_token_idx = wps_last_token_index(data, line, subline);
583
584 data->sublines[subline_idx].time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
585
586 for (i = wps_first_token_index(data, line, subline);
587 i <= last_token_idx; i++)
588 {
589 switch(data->tokens[i].type)
590 {
591 case WPS_TOKEN_CONDITIONAL:
592 /* place ourselves in the right conditional case */
593 evaluate_conditional(gwps, &i);
594 break;
595
596 case WPS_TOKEN_CONDITIONAL_OPTION:
597 /* we've finished in the curent conditional case,
598 skip to the end of the conditional structure */
599 i = find_conditional_end(data, i);
600 break;
601
602 case WPS_TOKEN_SUBLINE_TIMEOUT:
603 data->sublines[subline_idx].time_mult = data->tokens[i].value.i;
604 break;
605
606 default:
607 break;
608 }
609 }
610}
611
612/* Calculates which subline should be displayed for the specified line
613 Returns true iff the subline must be refreshed */
614static bool update_curr_subline(struct gui_wps *gwps, int line)
615{
616 struct wps_data *data = gwps->data;
617
618 int search, search_start, num_sublines;
619 bool reset_subline;
620 bool new_subline_refresh;
621 bool only_one_subline;
622
623 num_sublines = data->lines[line].num_sublines;
624 reset_subline = (data->lines[line].curr_subline == SUBLINE_RESET);
625 new_subline_refresh = false;
626 only_one_subline = false;
627
628 /* if time to advance to next sub-line */
629 if (TIME_AFTER(current_tick, data->lines[line].subline_expire_time - 1) ||
630 reset_subline)
631 {
632 /* search all sublines until the next subline with time > 0
633 is found or we get back to the subline we started with */
634 if (reset_subline)
635 search_start = 0;
636 else
637 search_start = data->lines[line].curr_subline;
638
639 for (search = 0; search < num_sublines; search++)
640 {
641 data->lines[line].curr_subline++;
642
643 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
644 if (data->lines[line].curr_subline == num_sublines)
645 {
646 if (data->lines[line].curr_subline == 1)
647 only_one_subline = true;
648 data->lines[line].curr_subline = 0;
649 }
650
651 /* if back where we started after search or
652 only one subline is defined on the line */
653 if (((search > 0) &&
654 (data->lines[line].curr_subline == search_start)) ||
655 only_one_subline)
656 {
657 /* no other subline with a time > 0 exists */
658 data->lines[line].subline_expire_time = (reset_subline ?
659 current_tick :
660 data->lines[line].subline_expire_time) + 100 * HZ;
661 break;
662 }
663 else
664 {
665 /* get initial time multiplier for this subline */
666 get_subline_timeout(gwps, line, data->lines[line].curr_subline);
667
668 int subline_idx = wps_subline_index(data, line,
669 data->lines[line].curr_subline);
670
671 /* only use this subline if subline time > 0 */
672 if (data->sublines[subline_idx].time_mult > 0)
673 {
674 new_subline_refresh = true;
675 data->lines[line].subline_expire_time = (reset_subline ?
676 current_tick : data->lines[line].subline_expire_time) +
677 TIMEOUT_UNIT*data->sublines[subline_idx].time_mult;
678 break;
679 }
680 }
681 }
682 }
683
684 return new_subline_refresh;
685}
686
687/* Display a line appropriately according to its alignment format.
688 format_align contains the text, separated between left, center and right.
689 line is the index of the line on the screen.
690 scroll indicates whether the line is a scrolling one or not.
691*/
692static void write_line(struct screen *display,
693 struct align_pos *format_align,
694 int line,
695 bool scroll)
696{
697 int left_width = 0, left_xpos;
698 int center_width = 0, center_xpos;
699 int right_width = 0, right_xpos;
700 int ypos;
701 int space_width;
702 int string_height;
703 int scroll_width;
704
705 /* calculate different string sizes and positions */
706 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
707 if (format_align->left != 0) {
708 display->getstringsize((unsigned char *)format_align->left,
709 &left_width, &string_height);
710 }
711
712 if (format_align->right != 0) {
713 display->getstringsize((unsigned char *)format_align->right,
714 &right_width, &string_height);
715 }
716
717 if (format_align->center != 0) {
718 display->getstringsize((unsigned char *)format_align->center,
719 &center_width, &string_height);
720 }
721
722 left_xpos = 0;
723 right_xpos = (display->getwidth() - right_width);
724 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
725
726 scroll_width = display->getwidth() - left_xpos;
727
728 /* Checks for overlapping strings.
729 If needed the overlapping strings will be merged, separated by a
730 space */
731
732 /* CASE 1: left and centered string overlap */
733 /* there is a left string, need to merge left and center */
734 if ((left_width != 0 && center_width != 0) &&
735 (left_xpos + left_width + space_width > center_xpos)) {
736 /* replace the former separator '\0' of left and
737 center string with a space */
738 *(--format_align->center) = ' ';
739 /* calculate the new width and position of the merged string */
740 left_width = left_width + space_width + center_width;
741 /* there is no centered string anymore */
742 center_width = 0;
743 }
744 /* there is no left string, move center to left */
745 if ((left_width == 0 && center_width != 0) &&
746 (left_xpos + left_width > center_xpos)) {
747 /* move the center string to the left string */
748 format_align->left = format_align->center;
749 /* calculate the new width and position of the string */
750 left_width = center_width;
751 /* there is no centered string anymore */
752 center_width = 0;
753 }
754
755 /* CASE 2: centered and right string overlap */
756 /* there is a right string, need to merge center and right */
757 if ((center_width != 0 && right_width != 0) &&
758 (center_xpos + center_width + space_width > right_xpos)) {
759 /* replace the former separator '\0' of center and
760 right string with a space */
761 *(--format_align->right) = ' ';
762 /* move the center string to the right after merge */
763 format_align->right = format_align->center;
764 /* calculate the new width and position of the merged string */
765 right_width = center_width + space_width + right_width;
766 right_xpos = (display->getwidth() - right_width);
767 /* there is no centered string anymore */
768 center_width = 0;
769 }
770 /* there is no right string, move center to right */
771 if ((center_width != 0 && right_width == 0) &&
772 (center_xpos + center_width > right_xpos)) {
773 /* move the center string to the right string */
774 format_align->right = format_align->center;
775 /* calculate the new width and position of the string */
776 right_width = center_width;
777 right_xpos = (display->getwidth() - right_width);
778 /* there is no centered string anymore */
779 center_width = 0;
780 }
781
782 /* CASE 3: left and right overlap
783 There is no center string anymore, either there never
784 was one or it has been merged in case 1 or 2 */
785 /* there is a left string, need to merge left and right */
786 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
787 (left_xpos + left_width + space_width > right_xpos)) {
788 /* replace the former separator '\0' of left and
789 right string with a space */
790 *(--format_align->right) = ' ';
791 /* calculate the new width and position of the string */
792 left_width = left_width + space_width + right_width;
793 /* there is no right string anymore */
794 right_width = 0;
795 }
796 /* there is no left string, move right to left */
797 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
798 (left_width > right_xpos)) {
799 /* move the right string to the left string */
800 format_align->left = format_align->right;
801 /* calculate the new width and position of the string */
802 left_width = right_width;
803 /* there is no right string anymore */
804 right_width = 0;
805 }
806
807 ypos = (line * string_height);
808
809
810 if (scroll && ((left_width > scroll_width) ||
811 (center_width > scroll_width) ||
812 (right_width > scroll_width)))
813 {
814 display->puts_scroll(0, line,
815 (unsigned char *)format_align->left);
816 }
817 else
818 {
819#ifdef HAVE_LCD_BITMAP
820 /* clear the line first */
821 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
822 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
823 display->set_drawmode(DRMODE_SOLID);
824#endif
825
826 /* Nasty hack: we output an empty scrolling string,
827 which will reset the scroller for that line */
828 display->puts_scroll(0, line, (unsigned char *)"");
829
830 /* print aligned strings */
831 if (left_width != 0)
832 {
833 display->putsxy(left_xpos, ypos,
834 (unsigned char *)format_align->left);
835 }
836 if (center_width != 0)
837 {
838 display->putsxy(center_xpos, ypos,
839 (unsigned char *)format_align->center);
840 }
841 if (right_width != 0)
842 {
843 display->putsxy(right_xpos, ypos,
844 (unsigned char *)format_align->right);
845 }
846 }
847}
848
849static bool gui_wps_redraw(struct gui_wps *gwps, unsigned refresh_mode)
850{
851 struct wps_data *data = gwps->data;
852 struct screen *display = gwps->display;
853 struct wps_state *state = gwps->state;
854
855 if (!data || !state || !display)
856 return false;
857
858 struct mp3entry *id3 = state->id3;
859
860 if (!id3)
861 return false;
862
863 int v, line, i, subline_idx;
864 unsigned flags;
865 char linebuf[MAX_PATH];
866
867 struct align_pos align;
868 align.left = NULL;
869 align.center = NULL;
870 align.right = NULL;
871
872 bool update_line, new_subline_refresh;
873
874#ifdef HAVE_LCD_BITMAP
875
876 /* to find out wether the peak meter is enabled we
877 assume it wasn't until we find a line that contains
878 the peak meter. We can't use peak_meter_enabled itself
879 because that would mean to turn off the meter thread
880 temporarily. (That shouldn't matter unless yield
881 or sleep is called but who knows...)
882 */
883 bool enable_pm = false;
884
885#endif
886
887 /* reset to first subline if refresh all flag is set */
888 if (refresh_mode == WPS_REFRESH_ALL)
889 {
890 display->set_viewport(&data->viewports[0].vp);
891 display->clear_viewport();
892
893 for (i = 0; i <= data->num_lines; i++)
894 {
895 data->lines[i].curr_subline = SUBLINE_RESET;
896 }
897 }
898
899#ifdef HAVE_LCD_CHARCELLS
900 for (i = 0; i < 8; i++)
901 {
902 if (data->wps_progress_pat[i] == 0)
903 data->wps_progress_pat[i] = display->get_locked_pattern();
904 }
905#endif
906
907 /* disable any viewports which are conditionally displayed */
908 for (v = 0; v < data->num_viewports; v++)
909 {
910 if (data->viewports[v].hidden_flags&VP_DRAW_HIDEABLE)
911 {
912 if (data->viewports[v].hidden_flags&VP_DRAW_HIDDEN)
913 data->viewports[v].hidden_flags |= VP_DRAW_WASHIDDEN;
914 else
915 data->viewports[v].hidden_flags |= VP_DRAW_HIDDEN;
916 }
917 }
918 for (v = 0; v < data->num_viewports; v++)
919 {
920 struct wps_viewport *wps_vp = &(data->viewports[v]);
921 unsigned vp_refresh_mode = refresh_mode;
922 display->set_viewport(&wps_vp->vp);
923
924#ifdef HAVE_LCD_BITMAP
925 /* Set images to not to be displayed */
926 for (i = 0; i < MAX_IMAGES; i++)
927 {
928 data->img[i].display = -1;
929 }
930#endif
931 /* dont redraw the viewport if its disabled */
932 if ((wps_vp->hidden_flags&VP_DRAW_HIDDEN))
933 {
934 if (!(wps_vp->hidden_flags&VP_DRAW_WASHIDDEN))
935 display->scroll_stop(&wps_vp->vp);
936 wps_vp->hidden_flags |= VP_DRAW_WASHIDDEN;
937 continue;
938 }
939 else if (((wps_vp->hidden_flags&
940 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
941 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
942 {
943 vp_refresh_mode = WPS_REFRESH_ALL;
944 wps_vp->hidden_flags = VP_DRAW_HIDEABLE;
945 }
946 if (vp_refresh_mode == WPS_REFRESH_ALL)
947 {
948 display->clear_viewport();
949 }
950
951 for (line = wps_vp->first_line;
952 line <= wps_vp->last_line; line++)
953 {
954 memset(linebuf, 0, sizeof(linebuf));
955 update_line = false;
956
957 /* get current subline for the line */
958 new_subline_refresh = update_curr_subline(gwps, line);
959
960 subline_idx = wps_subline_index(data, line,
961 data->lines[line].curr_subline);
962 flags = data->sublines[subline_idx].line_type;
963
964 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
965 || new_subline_refresh)
966 {
967 /* get_line tells us if we need to update the line */
968 update_line = get_line(gwps, line, data->lines[line].curr_subline,
969 &align, linebuf, sizeof(linebuf));
970 }
971#ifdef HAVE_LCD_BITMAP
972 /* peakmeter */
973 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
974 {
975 /* the peakmeter should be alone on its line */
976 update_line = false;
977
978 int h = font_get(wps_vp->vp.font)->height;
979 int peak_meter_y = (line - wps_vp->first_line)* h;
980
981 /* The user might decide to have the peak meter in the last
982 line so that it is only displayed if no status bar is
983 visible. If so we neither want do draw nor enable the
984 peak meter. */
985 if (peak_meter_y + h <= display->getheight()) {
986 /* found a line with a peak meter -> remember that we must
987 enable it later */
988 enable_pm = true;
989 peak_meter_enabled = true;
990 peak_meter_screen(gwps->display, 0, peak_meter_y,
991 MIN(h, display->getheight() - peak_meter_y));
992 }
993 else
994 {
995 peak_meter_enabled = false;
996 }
997 }
998
999#else /* HAVE_LCD_CHARCELL */
1000
1001 /* progressbar */
1002 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1003 {
1004 if (data->full_line_progressbar)
1005 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1006 else
1007 draw_player_progress(gwps);
1008 }
1009#endif
1010
1011 if (update_line &&
1012 /* conditionals clear the line which means if the %Vd is put into the default
1013 viewport there will be a blank line.
1014 To get around this we dont allow any actual drawing to happen in the
1015 deault vp if other vp's are defined */
1016 ((data->num_viewports>1 && v!=0) || data->num_viewports == 1))
1017 {
1018 if (flags & WPS_REFRESH_SCROLL)
1019 {
1020 /* if the line is a scrolling one we don't want to update
1021 too often, so that it has the time to scroll */
1022 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1023 write_line(display, &align, line - wps_vp->first_line, true);
1024 }
1025 else
1026 write_line(display, &align, line - wps_vp->first_line, false);
1027 }
1028 }
1029
1030#ifdef HAVE_LCD_BITMAP
1031 /* progressbar */
1032 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1033 {
1034 if (wps_vp->pb)
1035 {
1036 draw_progressbar(gwps, wps_vp);
1037 }
1038 }
1039 /* Now display any images in this viewport */
1040 wps_display_images(gwps, &wps_vp->vp);
1041#endif
1042 }
1043
1044#ifdef HAVE_LCD_BITMAP
1045 data->peak_meter_enabled = enable_pm;
1046#endif
1047
1048 if (refresh_mode & WPS_REFRESH_STATUSBAR)
1049 {
1050 gwps_draw_statusbars();
1051 }
1052 /* Restore the default viewport */
1053 display->set_viewport(NULL);
1054
1055 display->update();
1056
1057 return true;
1058}