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