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