FS#10898 - Add a playlist viewer to the WPS. http://www.rockbox.org/wiki/CustomWPS...
[kugel-rb.git] / apps / gui / skin_engine / skin_display.c
blobe781d433721349dfc32324cadbc14ba2cee493e1
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2002-2007 Björn Stenberg
11 * Copyright (C) 2007-2008 Nicolas Pennequin
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
22 #include "config.h"
23 #include <stdio.h>
24 #include <string.h>
25 #include "misc.h"
26 #include "font.h"
27 #include "system.h"
28 #include "rbunicode.h"
29 #ifdef DEBUG
30 #include "debug.h"
31 #endif
32 #include "abrepeat.h"
33 #include "lang.h"
34 #include "language.h"
35 #include "statusbar.h"
36 #include "scrollbar.h"
37 #include "screen_access.h"
38 #include "playlist.h"
39 #include "playback.h"
41 #ifdef HAVE_LCD_BITMAP
42 #include "peakmeter.h"
43 /* Image stuff */
44 #include "bmp.h"
45 #ifdef HAVE_ALBUMART
46 #include "albumart.h"
47 #endif
48 #endif
50 #include "cuesheet.h"
51 #if CONFIG_CODEC == SWCODEC
52 #include "playback.h"
53 #endif
54 #include "backdrop.h"
55 #include "viewport.h"
58 #include "wps_internals.h"
59 #include "skin_engine.h"
60 #include "statusbar-skinned.h"
62 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode);
64 /* update a skinned screen, update_type is WPS_REFRESH_* values.
65 * Usually it should only be WPS_REFRESH_NON_STATIC
66 * A full update will be done if required (state.do_full_update == true)
68 bool skin_update(struct gui_wps *gwps, unsigned int update_type)
70 bool retval;
71 /* This maybe shouldnt be here, but while the skin is only used to
72 * display the music screen this is better than whereever we are being
73 * called from. This is also safe for skined screen which dont use the id3 */
74 struct mp3entry *id3 = gwps->state->id3;
75 bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
76 gwps->sync_data->do_full_update |= cuesheet_update;
78 retval = skin_redraw(gwps, gwps->sync_data->do_full_update ?
79 WPS_REFRESH_ALL : update_type);
80 return retval;
83 #ifdef HAVE_LCD_BITMAP
85 void skin_statusbar_changed(struct gui_wps *skin)
87 if (!skin)
88 return;
89 struct wps_data *data = skin->data;
90 const struct screen *display = skin->display;
91 const int screen = display->screen_type;
93 struct viewport *vp = &find_viewport(VP_DEFAULT_LABEL, data)->vp;
94 viewport_set_defaults(vp, screen);
96 if (data->wps_sb_tag)
97 { /* fix up the default viewport */
98 if (data->show_sb_on_wps)
100 if (statusbar_position(screen) != STATUSBAR_OFF)
101 return; /* vp is fixed already */
103 vp->y = STATUSBAR_HEIGHT;
104 vp->height = display->lcdheight - STATUSBAR_HEIGHT;
106 else
108 if (statusbar_position(screen) == STATUSBAR_OFF)
109 return; /* vp is fixed already */
110 vp->y = vp->x = 0;
111 vp->height = display->lcdheight;
112 vp->width = display->lcdwidth;
117 static void draw_progressbar(struct gui_wps *gwps,
118 struct skin_viewport *wps_vp)
120 struct screen *display = gwps->display;
121 struct wps_state *state = gwps->state;
122 struct progressbar *pb = wps_vp->pb;
123 struct mp3entry *id3 = state->id3;
124 int y = pb->y;
126 if (y < 0)
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);
135 int elapsed, length;
136 if (id3)
138 elapsed = id3->elapsed;
139 length = id3->length;
141 else
143 elapsed = 0;
144 length = 0;
147 if (pb->have_bitmap_pb)
148 gui_bitmap_scrollbar_draw(display, pb->bm,
149 pb->x, y, pb->width, pb->bm.height,
150 length ? length : 1, 0,
151 length ? elapsed + state->ff_rewind_count : 0,
152 HORIZONTAL);
153 else
154 gui_scrollbar_draw(display, pb->x, y, pb->width, pb->height,
155 length ? length : 1, 0,
156 length ? elapsed + state->ff_rewind_count : 0,
157 HORIZONTAL);
158 #ifdef AB_REPEAT_ENABLE
159 if ( ab_repeat_mode_enabled() && length != 0 )
160 ab_draw_markers(display, length,
161 pb->x, pb->x + pb->width, y, pb->height);
162 #endif
164 if (id3 && id3->cuesheet)
165 cue_draw_markers(display, state->id3->cuesheet, length,
166 pb->x, pb->x + pb->width, y+1, pb->height-2);
168 bool audio_peek_track(struct mp3entry* id3, int offset);
169 static void draw_playlist_viewer_list(struct gui_wps *gwps,
170 struct playlistviewer *viewer)
172 int lines = viewport_get_nb_lines(viewer->vp);
173 int line_height = font_get(viewer->vp->font)->height;
174 int cur_playlist_pos = playlist_get_display_index();
175 int start_item = MAX(0, cur_playlist_pos + viewer->start_offset);
176 int i;
178 struct mp3entry *pid3, id3;
179 char buf[MAX_PATH*2], tempbuf[MAX_PATH];
182 gwps->display->set_viewport(viewer->vp);
183 for(i=start_item; (i-start_item)<lines && i<playlist_amount(); i++)
185 if (i == cur_playlist_pos)
187 pid3 = audio_current_track();
189 else if (i == cur_playlist_pos+1)
191 pid3 = audio_next_track();
193 else if ((i>cur_playlist_pos) && audio_peek_track(&id3, i-cur_playlist_pos))
195 pid3 = &id3;
197 else
198 pid3 = NULL;
200 int line = pid3 ? TRACK_HAS_INFO : TRACK_HAS_NO_INFO;
201 int token = 0, cur_string = 0;
202 char *filename = playlist_peek(i-cur_playlist_pos);
203 buf[0] = '\0';
204 while (token < viewer->lines[line].count)
206 switch (viewer->lines[line].tokens[token])
208 case WPS_TOKEN_STRING:
209 case WPS_TOKEN_CHARACTER:
210 strcat(buf, viewer->lines[line].strings[cur_string++]);
211 break;
212 case WPS_TOKEN_PLAYLIST_POSITION:
213 snprintf(tempbuf, sizeof(tempbuf), "%d", i);
214 strcat(buf, tempbuf);
215 break;
216 case WPS_TOKEN_FILE_NAME:
217 get_dir(tempbuf, sizeof(tempbuf), filename, 0);
218 strcat(buf, tempbuf);
219 break;
220 case WPS_TOKEN_FILE_PATH:
221 strcat(buf, filename);
222 break;
223 case WPS_TOKEN_METADATA_ARTIST:
224 if (pid3)
225 strcat(buf, pid3->artist ? pid3->artist : "");
226 break;
227 case WPS_TOKEN_METADATA_TRACK_TITLE:
228 if (pid3)
229 strcat(buf, pid3->title ? pid3->title : "");
230 break;
231 case WPS_TOKEN_TRACK_LENGTH:
232 if (pid3)
234 format_time(tempbuf, sizeof(tempbuf), pid3->length);
235 strcat(buf, tempbuf);
237 break;
239 default:
240 break;
242 token++;
245 if (viewer->lines[line].scroll)
247 gwps->display->puts_scroll(0, (i-start_item), buf );
249 else
251 gwps->display->putsxy(0, (i-start_item)*line_height, buf );
257 /* clears the area where the image was shown */
258 static void clear_image_pos(struct gui_wps *gwps, struct gui_img *img)
260 if(!gwps)
261 return;
262 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
263 gwps->display->fillrect(img->x, img->y, img->bm.width, img->subimage_height);
264 gwps->display->set_drawmode(DRMODE_SOLID);
267 static void wps_draw_image(struct gui_wps *gwps, struct gui_img *img, int subimage)
269 struct screen *display = gwps->display;
270 if(img->always_display)
271 display->set_drawmode(DRMODE_FG);
272 else
273 display->set_drawmode(DRMODE_SOLID);
275 #if LCD_DEPTH > 1
276 if(img->bm.format == FORMAT_MONO) {
277 #endif
278 display->mono_bitmap_part(img->bm.data,
279 0, img->subimage_height * subimage,
280 img->bm.width, img->x,
281 img->y, img->bm.width,
282 img->subimage_height);
283 #if LCD_DEPTH > 1
284 } else {
285 display->transparent_bitmap_part((fb_data *)img->bm.data,
286 0, img->subimage_height * subimage,
287 STRIDE(display->screen_type,
288 img->bm.width, img->bm.height),
289 img->x, img->y, img->bm.width,
290 img->subimage_height);
292 #endif
295 static void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
297 if(!gwps || !gwps->data || !gwps->display)
298 return;
300 struct wps_data *data = gwps->data;
301 struct screen *display = gwps->display;
302 struct skin_token_list *list = data->images;
304 while (list)
306 struct gui_img *img = (struct gui_img*)list->token->value.data;
307 if (img->loaded)
309 if (img->display >= 0)
311 wps_draw_image(gwps, img, img->display);
313 else if (img->always_display && img->vp == vp)
315 wps_draw_image(gwps, img, 0);
318 list = list->next;
320 #ifdef HAVE_ALBUMART
321 /* now draw the AA */
322 if (data->albumart && data->albumart->vp == vp
323 && data->albumart->draw)
325 draw_album_art(gwps, playback_current_aa_hid(data->playback_aa_slot),
326 false);
327 data->albumart->draw = false;
329 #endif
331 display->set_drawmode(DRMODE_SOLID);
334 #else /* HAVE_LCD_CHARCELL */
336 static bool draw_player_progress(struct gui_wps *gwps)
338 struct wps_state *state = gwps->state;
339 struct screen *display = gwps->display;
340 unsigned char progress_pattern[7];
341 int pos = 0;
342 int i;
344 int elapsed, length;
345 if (LIKELY(state->id3))
347 elapsed = state->id3->elapsed;
348 length = state->id3->length;
350 else
352 elapsed = 0;
353 length = 0;
356 if (length)
357 pos = 36 * (elapsed + state->ff_rewind_count) / length;
359 for (i = 0; i < 7; i++, pos -= 5)
361 if (pos <= 0)
362 progress_pattern[i] = 0x1fu;
363 else if (pos >= 5)
364 progress_pattern[i] = 0x00u;
365 else
366 progress_pattern[i] = 0x1fu >> pos;
369 display->define_pattern(gwps->data->wps_progress_pat[0], progress_pattern);
370 return true;
373 static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
375 static const unsigned char numbers[10][4] = {
376 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
377 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
378 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
379 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
380 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
381 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
382 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
383 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
384 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
385 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
388 struct wps_state *state = gwps->state;
389 struct screen *display = gwps->display;
390 struct wps_data *data = gwps->data;
391 unsigned char progress_pattern[7];
392 char timestr[10];
393 int time;
394 int time_idx = 0;
395 int pos = 0;
396 int pat_idx = 1;
397 int digit, i, j;
398 bool softchar;
400 int elapsed, length;
401 if (LIKELY(state->id3))
403 elapsed = state->id3->elapsed;
404 length = state->id3->length;
406 else
408 elapsed = 0;
409 length = 0;
412 if (buf_size < 34) /* worst case: 11x UTF-8 char + \0 */
413 return;
415 time = elapsed + state->ff_rewind_count;
416 if (length)
417 pos = 55 * time / length;
419 memset(timestr, 0, sizeof(timestr));
420 format_time(timestr, sizeof(timestr)-2, time);
421 timestr[strlen(timestr)] = ':'; /* always safe */
423 for (i = 0; i < 11; i++, pos -= 5)
425 softchar = false;
426 memset(progress_pattern, 0, sizeof(progress_pattern));
428 if ((digit = timestr[time_idx]))
430 softchar = true;
431 digit -= '0';
433 if (timestr[time_idx + 1] == ':') /* ones, left aligned */
435 memcpy(progress_pattern, numbers[digit], 4);
436 time_idx += 2;
438 else /* tens, shifted right */
440 for (j = 0; j < 4; j++)
441 progress_pattern[j] = numbers[digit][j] >> 1;
443 if (time_idx > 0) /* not the first group, add colon in front */
445 progress_pattern[1] |= 0x10u;
446 progress_pattern[3] |= 0x10u;
448 time_idx++;
451 if (pos >= 5)
452 progress_pattern[5] = progress_pattern[6] = 0x1fu;
455 if (pos > 0 && pos < 5)
457 softchar = true;
458 progress_pattern[5] = progress_pattern[6] = (~0x1fu >> pos) & 0x1fu;
461 if (softchar && pat_idx < 8)
463 display->define_pattern(data->wps_progress_pat[pat_idx],
464 progress_pattern);
465 buf = utf8encode(data->wps_progress_pat[pat_idx], buf);
466 pat_idx++;
468 else if (pos <= 0)
469 buf = utf8encode(' ', buf);
470 else
471 buf = utf8encode(0xe115, buf); /* 2/7 _ */
473 *buf = '\0';
476 #endif /* HAVE_LCD_CHARCELL */
478 /* Return the index to the end token for the conditional token at index.
479 The conditional token can be either a start token or a separator
480 (i.e. option) token.
482 static int find_conditional_end(struct wps_data *data, int index)
484 int ret = index;
485 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END)
486 ret = data->tokens[ret].value.i;
488 /* ret now is the index to the end token for the conditional. */
489 return ret;
492 /* Evaluate the conditional that is at *token_index and return whether a skip
493 has ocurred. *token_index is updated with the new position.
495 static bool evaluate_conditional(struct gui_wps *gwps, int *token_index)
497 if (!gwps)
498 return false;
500 struct wps_data *data = gwps->data;
502 int i, cond_end;
503 int cond_index = *token_index;
504 char result[128];
505 const char *value;
506 unsigned char num_options = data->tokens[cond_index].value.i & 0xFF;
507 unsigned char prev_val = (data->tokens[cond_index].value.i & 0xFF00) >> 8;
509 /* treat ?xx<true> constructs as if they had 2 options. */
510 if (num_options < 2)
511 num_options = 2;
513 int intval = num_options;
514 /* get_token_value needs to know the number of options in the enum */
515 value = get_token_value(gwps, &data->tokens[cond_index + 1],
516 result, sizeof(result), &intval);
518 /* intval is now the number of the enum option we want to read,
519 starting from 1. If intval is -1, we check if value is empty. */
520 if (intval == -1)
521 intval = (value && *value) ? 1 : num_options;
522 else if (intval > num_options || intval < 1)
523 intval = num_options;
525 data->tokens[cond_index].value.i = (intval << 8) + num_options;
527 /* skip to the appropriate enum case */
528 int next = cond_index + 2;
529 for (i = 1; i < intval; i++)
531 next = data->tokens[next].value.i;
533 *token_index = next;
535 if (prev_val == intval)
537 /* Same conditional case as previously. Return without clearing the
538 pictures */
539 return false;
542 cond_end = find_conditional_end(data, cond_index + 2);
543 for (i = cond_index + 3; i < cond_end; i++)
545 #ifdef HAVE_LCD_BITMAP
546 /* clear all pictures in the conditional and nested ones */
547 if (data->tokens[i].type == WPS_TOKEN_IMAGE_PRELOAD_DISPLAY)
548 clear_image_pos(gwps, find_image(data->tokens[i].value.i&0xFF, gwps->data));
549 #endif
550 #ifdef HAVE_ALBUMART
551 if (data->albumart && data->tokens[i].type == WPS_TOKEN_ALBUMART_DISPLAY)
553 draw_album_art(gwps,
554 playback_current_aa_hid(data->playback_aa_slot), true);
555 data->albumart->draw = false;
557 #endif
560 return true;
564 /* Read a (sub)line to the given alignment format buffer.
565 linebuf is the buffer where the data is actually stored.
566 align is the alignment format that'll be used to display the text.
567 The return value indicates whether the line needs to be updated.
569 static bool get_line(struct gui_wps *gwps,
570 struct skin_subline *subline,
571 struct align_pos *align,
572 char *linebuf,
573 int linebuf_size)
575 struct wps_data *data = gwps->data;
577 char temp_buf[128];
578 char *buf = linebuf; /* will always point to the writing position */
579 char *linebuf_end = linebuf + linebuf_size - 1;
580 bool update = false;
581 int i;
583 /* alignment-related variables */
584 int cur_align;
585 char* cur_align_start;
586 cur_align_start = buf;
587 cur_align = WPS_ALIGN_LEFT;
588 align->left = NULL;
589 align->center = NULL;
590 align->right = NULL;
591 /* Process all tokens of the desired subline */
592 for (i = subline->first_token_idx;
593 i <= subline->last_token_idx; i++)
595 switch(data->tokens[i].type)
597 case WPS_TOKEN_CONDITIONAL:
598 /* place ourselves in the right conditional case */
599 update |= evaluate_conditional(gwps, &i);
600 break;
602 case WPS_TOKEN_CONDITIONAL_OPTION:
603 /* we've finished in the curent conditional case,
604 skip to the end of the conditional structure */
605 i = find_conditional_end(data, i);
606 break;
608 #ifdef HAVE_LCD_BITMAP
609 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
611 char n = data->tokens[i].value.i & 0xFF;
612 int subimage = data->tokens[i].value.i >> 8;
613 struct gui_img *img = find_image(n, data);
615 if (img && img->loaded)
616 img->display = subimage;
617 break;
619 #endif
621 case WPS_TOKEN_ALIGN_LEFT:
622 case WPS_TOKEN_ALIGN_LEFT_RTL:
623 case WPS_TOKEN_ALIGN_CENTER:
624 case WPS_TOKEN_ALIGN_RIGHT:
625 case WPS_TOKEN_ALIGN_RIGHT_RTL:
626 /* remember where the current aligned text started */
627 switch (cur_align)
629 case WPS_ALIGN_LEFT:
630 align->left = cur_align_start;
631 break;
633 case WPS_ALIGN_CENTER:
634 align->center = cur_align_start;
635 break;
637 case WPS_ALIGN_RIGHT:
638 align->right = cur_align_start;
639 break;
641 /* start a new alignment */
642 switch (data->tokens[i].type)
644 case WPS_TOKEN_ALIGN_LEFT:
645 cur_align = WPS_ALIGN_LEFT;
646 break;
647 case WPS_TOKEN_ALIGN_LEFT_RTL:
648 cur_align = lang_is_rtl() ? WPS_ALIGN_RIGHT :
649 WPS_ALIGN_LEFT;
650 break;
651 case WPS_TOKEN_ALIGN_CENTER:
652 cur_align = WPS_ALIGN_CENTER;
653 break;
654 case WPS_TOKEN_ALIGN_RIGHT:
655 cur_align = WPS_ALIGN_RIGHT;
656 break;
657 case WPS_TOKEN_ALIGN_RIGHT_RTL:
658 cur_align = lang_is_rtl() ? WPS_ALIGN_LEFT :
659 WPS_ALIGN_RIGHT;
660 break;
661 default:
662 break;
664 *buf++ = 0;
665 cur_align_start = buf;
666 break;
667 case WPS_VIEWPORT_ENABLE:
669 char label = data->tokens[i].value.i;
670 char temp = VP_DRAW_HIDEABLE;
671 /* viewports are allowed to share id's so find and enable
672 * all of them */
673 struct skin_token_list *list = data->viewports;
674 while (list)
676 struct skin_viewport *vp =
677 (struct skin_viewport *)list->token->value.data;
678 if (vp->label == label)
680 if (vp->hidden_flags&VP_DRAW_WASHIDDEN)
681 temp |= VP_DRAW_WASHIDDEN;
682 vp->hidden_flags = temp;
684 list = list->next;
687 break;
688 #ifdef HAVE_LCD_BITMAP
689 case WPS_VIEWPORT_CUSTOMLIST:
690 draw_playlist_viewer_list(gwps, data->tokens[i].value.data);
691 break;
692 #endif
693 default:
695 /* get the value of the tag and copy it to the buffer */
696 const char *value = get_token_value(gwps, &data->tokens[i],
697 temp_buf, sizeof(temp_buf), NULL);
698 if (value)
700 update = true;
701 while (*value && (buf < linebuf_end))
702 *buf++ = *value++;
704 break;
709 /* close the current alignment */
710 switch (cur_align)
712 case WPS_ALIGN_LEFT:
713 align->left = cur_align_start;
714 break;
716 case WPS_ALIGN_CENTER:
717 align->center = cur_align_start;
718 break;
720 case WPS_ALIGN_RIGHT:
721 align->right = cur_align_start;
722 break;
725 return update;
727 static void get_subline_timeout(struct gui_wps *gwps, struct skin_subline *subline)
729 struct wps_data *data = gwps->data;
730 int i;
731 subline->time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
733 for (i = subline->first_token_idx;
734 i <= subline->last_token_idx; i++)
736 switch(data->tokens[i].type)
738 case WPS_TOKEN_CONDITIONAL:
739 /* place ourselves in the right conditional case */
740 evaluate_conditional(gwps, &i);
741 break;
743 case WPS_TOKEN_CONDITIONAL_OPTION:
744 /* we've finished in the curent conditional case,
745 skip to the end of the conditional structure */
746 i = find_conditional_end(data, i);
747 break;
749 case WPS_TOKEN_SUBLINE_TIMEOUT:
750 subline->time_mult = data->tokens[i].value.i;
751 break;
753 default:
754 break;
759 /* Calculates which subline should be displayed for the specified line
760 Returns true iff the subline must be refreshed */
761 static bool update_curr_subline(struct gui_wps *gwps, struct skin_line *line)
763 /* shortcut this whole thing if we need to reset the line completly */
764 if (line->curr_subline == NULL)
766 line->subline_expire_time = current_tick;
767 line->curr_subline = &line->sublines;
768 if (!line->curr_subline->next)
770 line->subline_expire_time += 100*HZ;
772 else
774 get_subline_timeout(gwps, line->curr_subline);
775 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
777 return true;
779 /* if time to advance to next sub-line */
780 if (TIME_AFTER(current_tick, line->subline_expire_time - 1))
782 /* if there is only one subline, there is no need to search for a new one */
783 if (&line->sublines == line->curr_subline &&
784 line->curr_subline->next == NULL)
786 line->subline_expire_time += 100 * HZ;
787 return false;
789 if (line->curr_subline->next)
790 line->curr_subline = line->curr_subline->next;
791 else
792 line->curr_subline = &line->sublines;
793 get_subline_timeout(gwps, line->curr_subline);
794 line->subline_expire_time += TIMEOUT_UNIT*line->curr_subline->time_mult;
795 return true;
797 return false;
800 /* Display a line appropriately according to its alignment format.
801 format_align contains the text, separated between left, center and right.
802 line is the index of the line on the screen.
803 scroll indicates whether the line is a scrolling one or not.
805 static void write_line(struct screen *display,
806 struct align_pos *format_align,
807 int line,
808 bool scroll)
810 int left_width = 0, left_xpos;
811 int center_width = 0, center_xpos;
812 int right_width = 0, right_xpos;
813 int ypos;
814 int space_width;
815 int string_height;
816 int scroll_width;
818 /* calculate different string sizes and positions */
819 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
820 if (format_align->left != 0) {
821 display->getstringsize((unsigned char *)format_align->left,
822 &left_width, &string_height);
825 if (format_align->right != 0) {
826 display->getstringsize((unsigned char *)format_align->right,
827 &right_width, &string_height);
830 if (format_align->center != 0) {
831 display->getstringsize((unsigned char *)format_align->center,
832 &center_width, &string_height);
835 left_xpos = 0;
836 right_xpos = (display->getwidth() - right_width);
837 center_xpos = (display->getwidth() + left_xpos - center_width) / 2;
839 scroll_width = display->getwidth() - left_xpos;
841 /* Checks for overlapping strings.
842 If needed the overlapping strings will be merged, separated by a
843 space */
845 /* CASE 1: left and centered string overlap */
846 /* there is a left string, need to merge left and center */
847 if ((left_width != 0 && center_width != 0) &&
848 (left_xpos + left_width + space_width > center_xpos)) {
849 /* replace the former separator '\0' of left and
850 center string with a space */
851 *(--format_align->center) = ' ';
852 /* calculate the new width and position of the merged string */
853 left_width = left_width + space_width + center_width;
854 /* there is no centered string anymore */
855 center_width = 0;
857 /* there is no left string, move center to left */
858 if ((left_width == 0 && center_width != 0) &&
859 (left_xpos + left_width > center_xpos)) {
860 /* move the center string to the left string */
861 format_align->left = format_align->center;
862 /* calculate the new width and position of the string */
863 left_width = center_width;
864 /* there is no centered string anymore */
865 center_width = 0;
868 /* CASE 2: centered and right string overlap */
869 /* there is a right string, need to merge center and right */
870 if ((center_width != 0 && right_width != 0) &&
871 (center_xpos + center_width + space_width > right_xpos)) {
872 /* replace the former separator '\0' of center and
873 right string with a space */
874 *(--format_align->right) = ' ';
875 /* move the center string to the right after merge */
876 format_align->right = format_align->center;
877 /* calculate the new width and position of the merged string */
878 right_width = center_width + space_width + right_width;
879 right_xpos = (display->getwidth() - right_width);
880 /* there is no centered string anymore */
881 center_width = 0;
883 /* there is no right string, move center to right */
884 if ((center_width != 0 && right_width == 0) &&
885 (center_xpos + center_width > right_xpos)) {
886 /* move the center string to the right string */
887 format_align->right = format_align->center;
888 /* calculate the new width and position of the string */
889 right_width = center_width;
890 right_xpos = (display->getwidth() - right_width);
891 /* there is no centered string anymore */
892 center_width = 0;
895 /* CASE 3: left and right overlap
896 There is no center string anymore, either there never
897 was one or it has been merged in case 1 or 2 */
898 /* there is a left string, need to merge left and right */
899 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
900 (left_xpos + left_width + space_width > right_xpos)) {
901 /* replace the former separator '\0' of left and
902 right string with a space */
903 *(--format_align->right) = ' ';
904 /* calculate the new width and position of the string */
905 left_width = left_width + space_width + right_width;
906 /* there is no right string anymore */
907 right_width = 0;
909 /* there is no left string, move right to left */
910 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
911 (left_width > right_xpos)) {
912 /* move the right string to the left string */
913 format_align->left = format_align->right;
914 /* calculate the new width and position of the string */
915 left_width = right_width;
916 /* there is no right string anymore */
917 right_width = 0;
920 ypos = (line * string_height);
923 if (scroll && ((left_width > scroll_width) ||
924 (center_width > scroll_width) ||
925 (right_width > scroll_width)))
927 display->puts_scroll(0, line,
928 (unsigned char *)format_align->left);
930 else
932 #ifdef HAVE_LCD_BITMAP
933 /* clear the line first */
934 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
935 display->fillrect(left_xpos, ypos, display->getwidth(), string_height);
936 display->set_drawmode(DRMODE_SOLID);
937 #endif
939 /* Nasty hack: we output an empty scrolling string,
940 which will reset the scroller for that line */
941 display->puts_scroll(0, line, (unsigned char *)"");
943 /* print aligned strings */
944 if (left_width != 0)
946 display->putsxy(left_xpos, ypos,
947 (unsigned char *)format_align->left);
949 if (center_width != 0)
951 display->putsxy(center_xpos, ypos,
952 (unsigned char *)format_align->center);
954 if (right_width != 0)
956 display->putsxy(right_xpos, ypos,
957 (unsigned char *)format_align->right);
962 static bool skin_redraw(struct gui_wps *gwps, unsigned refresh_mode)
964 struct wps_data *data = gwps->data;
965 struct screen *display = gwps->display;
967 if (!data || !display || !gwps->state)
968 return false;
970 unsigned flags;
971 char linebuf[MAX_PATH];
973 struct align_pos align;
974 align.left = NULL;
975 align.center = NULL;
976 align.right = NULL;
979 struct skin_token_list *viewport_list;
981 bool update_line, new_subline_refresh;
983 #ifdef HAVE_LCD_BITMAP
985 /* to find out wether the peak meter is enabled we
986 assume it wasn't until we find a line that contains
987 the peak meter. We can't use peak_meter_enabled itself
988 because that would mean to turn off the meter thread
989 temporarily. (That shouldn't matter unless yield
990 or sleep is called but who knows...)
992 bool enable_pm = false;
994 #endif
996 /* reset to first subline if refresh all flag is set */
997 if (refresh_mode == WPS_REFRESH_ALL)
999 struct skin_line *line;
1000 struct skin_viewport *skin_viewport = find_viewport(VP_DEFAULT_LABEL, data);
1002 if (!(skin_viewport->hidden_flags & VP_NEVER_VISIBLE))
1004 display->set_viewport(&skin_viewport->vp);
1005 display->clear_viewport();
1008 for (viewport_list = data->viewports;
1009 viewport_list; viewport_list = viewport_list->next)
1011 skin_viewport =
1012 (struct skin_viewport *)viewport_list->token->value.data;
1013 for(line = skin_viewport->lines; line; line = line->next)
1015 line->curr_subline = NULL;
1020 #ifdef HAVE_LCD_CHARCELLS
1021 int i;
1022 for (i = 0; i < 8; i++)
1024 if (data->wps_progress_pat[i] == 0)
1025 data->wps_progress_pat[i] = display->get_locked_pattern();
1027 #endif
1029 /* disable any viewports which are conditionally displayed.
1030 * If we are only refreshing the peak meter then don't change the viewport
1031 * enabled flags as this will stop scrolling. viewports cant be
1032 * toggled in this refresh mode anyway (FS#10215)*/
1033 if (refresh_mode != WPS_REFRESH_PEAK_METER)
1035 for (viewport_list = data->viewports;
1036 viewport_list; viewport_list = viewport_list->next)
1038 struct skin_viewport *skin_viewport =
1039 (struct skin_viewport *)viewport_list->token->value.data;
1040 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1042 continue;
1044 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
1046 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
1047 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1048 else
1049 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
1053 int viewport_count = 0;
1054 for (viewport_list = data->viewports;
1055 viewport_list; viewport_list = viewport_list->next, viewport_count++)
1057 struct skin_viewport *skin_viewport =
1058 (struct skin_viewport *)viewport_list->token->value.data;
1059 unsigned vp_refresh_mode = refresh_mode;
1061 display->set_viewport(&skin_viewport->vp);
1063 int hidden_vp = 0;
1065 #ifdef HAVE_LCD_BITMAP
1066 /* Set images to not to be displayed */
1067 struct skin_token_list *imglist = data->images;
1068 while (imglist)
1070 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
1071 img->display = -1;
1072 imglist = imglist->next;
1074 #endif
1075 /* dont redraw the viewport if its disabled */
1076 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
1077 { /* don't draw anything into this one */
1078 vp_refresh_mode = 0; hidden_vp = true;
1080 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
1082 if (!(skin_viewport->hidden_flags&VP_DRAW_WASHIDDEN))
1083 display->scroll_stop(&skin_viewport->vp);
1084 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
1085 continue;
1087 else if (((skin_viewport->hidden_flags&
1088 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
1089 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
1091 vp_refresh_mode = WPS_REFRESH_ALL;
1092 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
1095 if (vp_refresh_mode == WPS_REFRESH_ALL)
1097 display->clear_viewport();
1100 /* loop over the lines for this viewport */
1101 struct skin_line *line;
1102 int line_count = 0;
1104 for (line = skin_viewport->lines; line; line = line->next, line_count++)
1106 struct skin_subline *subline;
1107 memset(linebuf, 0, sizeof(linebuf));
1108 update_line = false;
1110 /* get current subline for the line */
1111 new_subline_refresh = update_curr_subline(gwps, line);
1112 subline = line->curr_subline;
1113 flags = line->curr_subline->line_type;
1115 if (vp_refresh_mode == WPS_REFRESH_ALL || (flags & vp_refresh_mode)
1116 || new_subline_refresh || hidden_vp)
1118 /* get_line tells us if we need to update the line */
1119 update_line = get_line(gwps, subline,
1120 &align, linebuf, sizeof(linebuf));
1122 #ifdef HAVE_LCD_BITMAP
1123 /* peakmeter */
1124 if (flags & vp_refresh_mode & WPS_REFRESH_PEAK_METER)
1126 /* the peakmeter should be alone on its line */
1127 update_line = false;
1129 int h = font_get(skin_viewport->vp.font)->height;
1130 int peak_meter_y = line_count* h;
1132 /* The user might decide to have the peak meter in the last
1133 line so that it is only displayed if no status bar is
1134 visible. If so we neither want do draw nor enable the
1135 peak meter. */
1136 if (peak_meter_y + h <= skin_viewport->vp.y+skin_viewport->vp.height) {
1137 /* found a line with a peak meter -> remember that we must
1138 enable it later */
1139 enable_pm = true;
1140 peak_meter_enabled = true;
1141 peak_meter_screen(gwps->display, 0, peak_meter_y,
1142 MIN(h, skin_viewport->vp.y+skin_viewport->vp.height - peak_meter_y));
1144 else
1146 peak_meter_enabled = false;
1150 #else /* HAVE_LCD_CHARCELL */
1152 /* progressbar */
1153 if (flags & vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1155 if (data->full_line_progressbar)
1156 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
1157 else
1158 draw_player_progress(gwps);
1160 #endif
1162 if (update_line && !hidden_vp &&
1163 /* conditionals clear the line which means if the %Vd is put into the default
1164 viewport there will be a blank line.
1165 To get around this we dont allow any actual drawing to happen in the
1166 deault vp if other vp's are defined */
1167 ((skin_viewport->label != VP_DEFAULT_LABEL && viewport_list->next) ||
1168 !viewport_list->next))
1170 if (flags & WPS_REFRESH_SCROLL)
1172 /* if the line is a scrolling one we don't want to update
1173 too often, so that it has the time to scroll */
1174 if ((vp_refresh_mode & WPS_REFRESH_SCROLL) || new_subline_refresh)
1175 write_line(display, &align, line_count, true);
1177 else
1178 write_line(display, &align, line_count, false);
1181 #ifdef HAVE_LCD_BITMAP
1182 /* progressbar */
1183 if (vp_refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1185 if (skin_viewport->pb)
1187 draw_progressbar(gwps, skin_viewport);
1190 /* Now display any images in this viewport */
1191 if (!hidden_vp)
1192 wps_display_images(gwps, &skin_viewport->vp);
1193 #endif
1196 #ifdef HAVE_LCD_BITMAP
1197 data->peak_meter_enabled = enable_pm;
1198 #endif
1199 /* Restore the default viewport */
1200 display->set_viewport(NULL);
1202 display->update();
1204 return true;