Add "elfzip" target to make which creates a zip of all elf files, as mapzip does...
[maemo-rb.git] / apps / gui / skin_engine / skin_render.c
blob424e0c29cc374b143961640feeeda2b2b3047f6b
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id: skin_parser.c 26752 2010-06-10 21:22:16Z bieber $
10 * Copyright (C) 2010 Jonathan Gordon
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <stdbool.h>
26 #include <ctype.h>
27 #include "strlcat.h"
29 #include "config.h"
30 #include "kernel.h"
31 #ifdef HAVE_ALBUMART
32 #include "albumart.h"
33 #endif
34 #include "skin_display.h"
35 #include "skin_engine.h"
36 #include "skin_parser.h"
37 #include "tag_table.h"
38 #include "skin_scan.h"
39 #if CONFIG_TUNER
40 #include "radio.h"
41 #endif
42 #include "viewport.h"
43 #include "cuesheet.h"
44 #include "language.h"
45 #include "playback.h"
46 #include "playlist.h"
47 #include "root_menu.h"
48 #include "misc.h"
51 #define MAX_LINE 1024
53 struct skin_draw_info {
54 struct gui_wps *gwps;
55 struct skin_viewport *skin_vp;
56 int line_number;
57 unsigned long refresh_type;
59 char* cur_align_start;
60 struct align_pos align;
61 bool no_line_break;
62 bool line_scrolls;
63 bool force_redraw;
65 char *buf;
66 size_t buf_size;
68 int offset; /* used by the playlist viewer */
71 typedef bool (*skin_render_func)(struct skin_element* alternator, struct skin_draw_info *info);
72 bool skin_render_alternator(struct skin_element* alternator, struct skin_draw_info *info);
74 #ifdef HAVE_LCD_BITMAP
75 static void skin_render_playlistviewer(struct playlistviewer* viewer,
76 struct gui_wps *gwps,
77 struct skin_viewport* skin_viewport,
78 unsigned long refresh_type);
79 #endif
81 static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
82 struct skin_element *element, struct viewport* vp)
84 #ifndef HAVE_LCD_BITMAP
85 (void)vp; /* silence warnings */
86 (void)info;
87 #endif
88 struct wps_token *token = (struct wps_token *)element->data;
90 #ifdef HAVE_LCD_BITMAP
91 struct wps_data *data = gwps->data;
92 bool do_refresh = (element->tag->flags & info->refresh_type) > 0;
93 #endif
94 switch (token->type)
96 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
97 case SKIN_TOKEN_VIEWPORT_FGCOLOUR:
99 struct viewport_colour *col = token->value.data;
100 col->vp->fg_pattern = col->colour;
102 break;
103 case SKIN_TOKEN_VIEWPORT_BGCOLOUR:
105 struct viewport_colour *col = token->value.data;
106 col->vp->bg_pattern = col->colour;
108 break;
109 #endif
110 case SKIN_TOKEN_VIEWPORT_ENABLE:
112 char *label = token->value.data;
113 char temp = VP_DRAW_HIDEABLE;
114 struct skin_element *viewport = gwps->data->tree;
115 while (viewport)
117 struct skin_viewport *skinvp = (struct skin_viewport*)viewport->data;
118 if (skinvp->label && !skinvp->is_infovp &&
119 !strcmp(skinvp->label, label))
121 if (skinvp->hidden_flags&VP_DRAW_HIDDEN)
123 temp |= VP_DRAW_WASHIDDEN;
125 skinvp->hidden_flags = temp;
127 viewport = viewport->next;
130 break;
131 #ifdef HAVE_LCD_BITMAP
132 case SKIN_TOKEN_UIVIEWPORT_ENABLE:
133 sb_set_info_vp(gwps->display->screen_type,
134 token->value.data);
135 break;
136 case SKIN_TOKEN_PEAKMETER:
137 data->peak_meter_enabled = true;
138 if (do_refresh)
139 draw_peakmeters(gwps, info->line_number, vp);
140 break;
141 #endif
142 #ifdef HAVE_LCD_BITMAP
143 case SKIN_TOKEN_PEAKMETER_LEFTBAR:
144 case SKIN_TOKEN_PEAKMETER_RIGHTBAR:
145 data->peak_meter_enabled = true;
146 /* fall through to the progressbar code */
147 #endif
148 case SKIN_TOKEN_VOLUMEBAR:
149 case SKIN_TOKEN_BATTERY_PERCENTBAR:
150 #ifdef HAVE_LCD_BITMAP
151 case SKIN_TOKEN_PROGRESSBAR:
152 case SKIN_TOKEN_TUNER_RSSI_BAR:
154 struct progressbar *bar = (struct progressbar*)token->value.data;
155 if (do_refresh)
156 draw_progressbar(gwps, info->line_number, bar);
158 #endif
159 break;
160 #ifdef HAVE_LCD_BITMAP
161 case SKIN_TOKEN_IMAGE_DISPLAY_LISTICON:
162 case SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY:
164 struct image_display *id = token->value.data;
165 const char* label = id->label;
166 struct gui_img *img = find_image(label, data);
167 if (img && img->loaded)
169 if (id->token == NULL)
171 img->display = id->subimage;
173 else
175 char buf[16];
176 const char *out;
177 int a = img->num_subimages;
178 out = get_token_value(gwps, id->token, info->offset,
179 buf, sizeof(buf), &a);
181 /* NOTE: get_token_value() returns values starting at 1! */
182 if (a == -1)
183 a = (out && *out) ? 1 : 2;
184 if (token->type == SKIN_TOKEN_IMAGE_DISPLAY_LISTICON)
185 a -= 2; /* 2 is added in statusbar-skinned.c! */
186 else
187 a--;
188 a += id->offset;
190 /* Clear the image, as in conditionals */
191 clear_image_pos(gwps, img);
193 /* If the token returned a value which is higher than
194 * the amount of subimages, don't draw it. */
195 if (a >= 0 && a < img->num_subimages)
197 img->display = a;
201 break;
203 #ifdef HAVE_ALBUMART
204 case SKIN_TOKEN_ALBUMART_DISPLAY:
205 /* now draw the AA */
206 if (do_refresh && data->albumart)
208 int handle = playback_current_aa_hid(data->playback_aa_slot);
209 #if CONFIG_TUNER
210 if (in_radio_screen() || (get_radio_status() != FMRADIO_OFF))
212 struct dim dim = {data->albumart->width, data->albumart->height};
213 handle = radio_get_art_hid(&dim);
215 #endif
216 data->albumart->draw_handle = handle;
218 break;
219 #endif
220 case SKIN_TOKEN_DRAW_INBUILTBAR:
221 gui_statusbar_draw(&(statusbars.statusbars[gwps->display->screen_type]),
222 info->refresh_type == SKIN_REFRESH_ALL,
223 token->value.data);
224 break;
225 case SKIN_TOKEN_VIEWPORT_CUSTOMLIST:
226 if (do_refresh)
227 skin_render_playlistviewer(token->value.data, gwps,
228 info->skin_vp, info->refresh_type);
229 break;
231 #endif /* HAVE_LCD_BITMAP */
232 default:
233 return false;
235 return true;
240 static void do_tags_in_hidden_conditional(struct skin_element* branch,
241 struct skin_draw_info *info)
243 #ifdef HAVE_LCD_BITMAP
244 struct gui_wps *gwps = info->gwps;
245 struct wps_data *data = gwps->data;
246 #endif
247 /* Tags here are ones which need to be "turned off" or cleared
248 * if they are in a conditional branch which isnt being used */
249 if (branch->type == LINE_ALTERNATOR)
251 int i;
252 for (i=0; i<branch->children_count; i++)
254 do_tags_in_hidden_conditional(branch->children[i], info);
257 else if (branch->type == LINE && branch->children_count)
259 struct skin_element *child = branch->children[0];
260 struct wps_token *token;
261 while (child)
263 if (child->type == CONDITIONAL)
265 int i;
266 for (i=0; i<child->children_count; i++)
268 do_tags_in_hidden_conditional(child->children[i], info);
270 child = child->next;
271 continue;
273 else if (child->type != TAG || !child->data)
275 child = child->next;
276 continue;
278 token = (struct wps_token *)child->data;
279 #ifdef HAVE_LCD_BITMAP
280 /* clear all pictures in the conditional and nested ones */
281 if (token->type == SKIN_TOKEN_IMAGE_PRELOAD_DISPLAY)
283 struct image_display *id = token->value.data;
284 struct gui_img *img = find_image(id->label, data);
285 clear_image_pos(gwps, img);
287 else if (token->type == SKIN_TOKEN_PEAKMETER)
289 data->peak_meter_enabled = false;
291 else if (token->type == SKIN_TOKEN_VIEWPORT_ENABLE)
293 char *label = token->value.data;
294 struct skin_element *viewport;
295 for (viewport = data->tree;
296 viewport;
297 viewport = viewport->next)
299 struct skin_viewport *skin_viewport = (struct skin_viewport*)viewport->data;
300 if (skin_viewport->label && strcmp(skin_viewport->label, label))
301 continue;
302 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
304 continue;
306 if (skin_viewport->hidden_flags&VP_DRAW_HIDEABLE)
308 if (skin_viewport->hidden_flags&VP_DRAW_HIDDEN)
309 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
310 else
312 gwps->display->set_viewport(&skin_viewport->vp);
313 gwps->display->clear_viewport();
314 gwps->display->scroll_stop(&skin_viewport->vp);
315 gwps->display->set_viewport(&info->skin_vp->vp);
316 skin_viewport->hidden_flags |= VP_DRAW_HIDDEN;
321 #endif
322 #ifdef HAVE_ALBUMART
323 else if (data->albumart && token->type == SKIN_TOKEN_ALBUMART_DISPLAY)
325 draw_album_art(gwps,
326 playback_current_aa_hid(data->playback_aa_slot), true);
328 #endif
329 child = child->next;
334 static void fix_line_alignment(struct skin_draw_info *info, struct skin_element *element)
336 struct align_pos *align = &info->align;
337 char *cur_pos = info->cur_align_start + strlen(info->cur_align_start);
338 switch (element->tag->type)
340 case SKIN_TOKEN_ALIGN_LEFT:
341 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
342 align->left = cur_pos;
343 info->cur_align_start = cur_pos;
344 break;
345 case SKIN_TOKEN_ALIGN_LEFT_RTL:
346 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
347 if (lang_is_rtl())
348 align->right = cur_pos;
349 else
350 align->left = cur_pos;
351 info->cur_align_start = cur_pos;
352 break;
353 case SKIN_TOKEN_ALIGN_CENTER:
354 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
355 align->center = cur_pos;
356 info->cur_align_start = cur_pos;
357 break;
358 case SKIN_TOKEN_ALIGN_RIGHT:
359 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
360 align->right = cur_pos;
361 info->cur_align_start = cur_pos;
362 break;
363 case SKIN_TOKEN_ALIGN_RIGHT_RTL:
364 *cur_pos = '\0'; cur_pos++; *cur_pos = '\0';
365 if (lang_is_rtl())
366 align->left = cur_pos;
367 else
368 align->right = cur_pos;
369 info->cur_align_start = cur_pos;
370 break;
371 default:
372 break;
376 /* Draw a LINE element onto the display */
377 static bool skin_render_line(struct skin_element* line, struct skin_draw_info *info)
379 bool needs_update = false;
380 int last_value, value;
382 if (line->children_count == 0)
383 return false; /* empty line, do nothing */
385 struct skin_element *child = line->children[0];
386 struct conditional *conditional;
387 skin_render_func func = skin_render_line;
388 int old_refresh_mode = info->refresh_type;
389 while (child)
391 switch (child->type)
393 case CONDITIONAL:
394 conditional = (struct conditional*)child->data;
395 last_value = conditional->last_value;
396 value = evaluate_conditional(info->gwps, info->offset,
397 conditional, child->children_count);
398 conditional->last_value = value;
399 if (child->children_count == 1)
401 /* special handling so
402 * %?aa<true> and %?<true|false> need special handlng here */
404 if (value == -1) /* tag is false */
406 /* we are in a false branch of a %?aa<true> conditional */
407 if (last_value == 0)
408 do_tags_in_hidden_conditional(child->children[0], info);
409 break;
412 else
414 if (last_value >= 0 && value != last_value && last_value < child->children_count)
415 do_tags_in_hidden_conditional(child->children[last_value], info);
417 if (child->children[value]->type == LINE_ALTERNATOR)
419 func = skin_render_alternator;
421 else if (child->children[value]->type == LINE)
422 func = skin_render_line;
424 if (value != last_value)
426 info->refresh_type = SKIN_REFRESH_ALL;
427 info->force_redraw = true;
430 if (func(child->children[value], info))
431 needs_update = true;
432 else
433 needs_update = needs_update || (last_value != value);
435 info->refresh_type = old_refresh_mode;
436 break;
437 case TAG:
438 if (child->tag->flags & NOBREAK)
439 info->no_line_break = true;
440 if (child->tag->type == SKIN_TOKEN_SUBLINE_SCROLL)
441 info->line_scrolls = true;
443 fix_line_alignment(info, child);
445 if (!child->data)
447 break;
449 if (!do_non_text_tags(info->gwps, info, child, &info->skin_vp->vp))
451 static char tempbuf[128];
452 const char *value = get_token_value(info->gwps, child->data,
453 info->offset, tempbuf,
454 sizeof(tempbuf), NULL);
455 if (value)
457 #if CONFIG_RTC
458 if (child->tag->flags&SKIN_RTC_REFRESH)
459 needs_update = needs_update || info->refresh_type&SKIN_REFRESH_DYNAMIC;
460 #endif
461 needs_update = needs_update ||
462 ((child->tag->flags&info->refresh_type)!=0);
463 strlcat(info->cur_align_start, value,
464 info->buf_size - (info->cur_align_start-info->buf));
467 break;
468 case TEXT:
469 strlcat(info->cur_align_start, child->data,
470 info->buf_size - (info->cur_align_start-info->buf));
471 needs_update = needs_update ||
472 (info->refresh_type&SKIN_REFRESH_STATIC) != 0;
473 break;
474 case COMMENT:
475 default:
476 break;
479 child = child->next;
481 return needs_update;
484 static int get_subline_timeout(struct gui_wps *gwps, struct skin_element* line)
486 struct skin_element *element=line;
487 struct wps_token *token;
488 int retval = DEFAULT_SUBLINE_TIME_MULTIPLIER*TIMEOUT_UNIT;
489 if (element->type == LINE)
491 if (element->children_count == 0)
492 return retval; /* empty line, so force redraw */
493 element = element->children[0];
495 while (element)
497 if (element->type == TAG &&
498 element->tag->type == SKIN_TOKEN_SUBLINE_TIMEOUT )
500 token = element->data;
501 return token->value.i;
503 else if (element->type == CONDITIONAL)
505 struct conditional *conditional = element->data;
506 int val = evaluate_conditional(gwps, 0, conditional,
507 element->children_count);
508 if (val >= 0)
510 retval = get_subline_timeout(gwps, element->children[val]);
511 if (retval >= 0)
512 return retval;
515 element = element->next;
517 return retval;
520 bool skin_render_alternator(struct skin_element* element, struct skin_draw_info *info)
522 bool changed_lines = false;
523 struct line_alternator *alternator = (struct line_alternator*)element->data;
524 unsigned old_refresh = info->refresh_type;
525 if (info->refresh_type == SKIN_REFRESH_ALL)
527 alternator->current_line = element->children_count-1;
528 changed_lines = true;
530 else if (TIME_AFTER(current_tick, alternator->next_change_tick))
532 changed_lines = true;
535 if (changed_lines)
537 struct skin_element *current_line = element->children[alternator->current_line];
538 int start = alternator->current_line;
539 int try_line = start;
540 bool suitable = false;
541 int rettimeout = DEFAULT_SUBLINE_TIME_MULTIPLIER*TIMEOUT_UNIT;
543 /* find a subline which has at least one token in it,
544 * and that line doesnt have a timeout set to 0 through conditionals */
545 do {
546 try_line++;
547 if (try_line >= element->children_count)
548 try_line = 0;
549 if (element->children[try_line]->children_count != 0)
551 current_line = element->children[try_line];
552 rettimeout = get_subline_timeout(info->gwps,
553 current_line->children[0]);
554 if (rettimeout > 0)
556 suitable = true;
560 while (try_line != start && !suitable);
562 if (suitable)
564 alternator->current_line = try_line;
565 alternator->next_change_tick = current_tick + rettimeout;
568 info->refresh_type = SKIN_REFRESH_ALL;
569 info->force_redraw = true;
571 bool ret = skin_render_line(element->children[alternator->current_line], info);
572 info->refresh_type = old_refresh;
573 return changed_lines || ret;
576 static void skin_render_viewport(struct skin_element* viewport, struct gui_wps *gwps,
577 struct skin_viewport* skin_viewport, unsigned long refresh_type)
579 struct screen *display = gwps->display;
580 char linebuf[MAX_LINE];
581 skin_render_func func = skin_render_line;
582 struct skin_element* line = viewport;
583 struct skin_draw_info info = {
584 .gwps = gwps,
585 .buf = linebuf,
586 .buf_size = sizeof(linebuf),
587 .line_number = 0,
588 .no_line_break = false,
589 .line_scrolls = false,
590 .refresh_type = refresh_type,
591 .skin_vp = skin_viewport,
592 .offset = 0
595 struct align_pos * align = &info.align;
596 bool needs_update;
597 #ifdef HAVE_LCD_BITMAP
598 /* Set images to not to be displayed */
599 struct skin_token_list *imglist = gwps->data->images;
600 while (imglist)
602 struct gui_img *img = (struct gui_img *)imglist->token->value.data;
603 img->display = -1;
604 imglist = imglist->next;
606 #endif
608 while (line)
610 linebuf[0] = '\0';
611 info.no_line_break = false;
612 info.line_scrolls = false;
613 info.force_redraw = false;
615 info.cur_align_start = info.buf;
616 align->left = info.buf;
617 align->center = NULL;
618 align->right = NULL;
621 if (line->type == LINE_ALTERNATOR)
622 func = skin_render_alternator;
623 else if (line->type == LINE)
624 func = skin_render_line;
626 needs_update = func(line, &info);
627 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && (LCD_REMOTE_DEPTH > 1))
628 if (skin_viewport->vp.fg_pattern != skin_viewport->start_fgcolour ||
629 skin_viewport->vp.bg_pattern != skin_viewport->start_bgcolour)
631 /* 2bit lcd drivers need lcd_set_viewport() to be called to change
632 * the colour, 16bit doesnt. But doing this makes static text
633 * get the new colour also */
634 needs_update = true;
635 display->set_viewport(&skin_viewport->vp);
637 #endif
638 /* only update if the line needs to be, and there is something to write */
639 if (refresh_type && needs_update)
641 if (info.line_scrolls)
643 /* if the line is a scrolling one we don't want to update
644 too often, so that it has the time to scroll */
645 if ((refresh_type & SKIN_REFRESH_SCROLL) || info.force_redraw)
646 write_line(display, align, info.line_number, true);
648 else
649 write_line(display, align, info.line_number, false);
651 if (!info.no_line_break)
652 info.line_number++;
653 line = line->next;
655 #ifdef HAVE_LCD_BITMAP
656 wps_display_images(gwps, &skin_viewport->vp);
657 #endif
660 void skin_render(struct gui_wps *gwps, unsigned refresh_mode)
662 struct wps_data *data = gwps->data;
663 struct screen *display = gwps->display;
665 struct skin_element* viewport = data->tree;
666 struct skin_viewport* skin_viewport;
668 int old_refresh_mode = refresh_mode;
670 #ifdef HAVE_LCD_CHARCELLS
671 int i;
672 for (i = 0; i < 8; i++)
674 if (data->wps_progress_pat[i] == 0)
675 data->wps_progress_pat[i] = display->get_locked_pattern();
677 #endif
678 viewport = data->tree;
679 skin_viewport = (struct skin_viewport *)viewport->data;
680 if (skin_viewport->label && viewport->next &&
681 !strcmp(skin_viewport->label,VP_DEFAULT_LABEL))
682 refresh_mode = 0;
684 for (viewport = data->tree;
685 viewport;
686 viewport = viewport->next)
688 /* SETUP */
689 skin_viewport = (struct skin_viewport*)viewport->data;
690 unsigned vp_refresh_mode = refresh_mode;
691 #if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
692 skin_viewport->vp.fg_pattern = skin_viewport->start_fgcolour;
693 skin_viewport->vp.bg_pattern = skin_viewport->start_bgcolour;
694 #endif
696 /* dont redraw the viewport if its disabled */
697 if (skin_viewport->hidden_flags&VP_NEVER_VISIBLE)
698 { /* don't draw anything into this one */
699 vp_refresh_mode = 0;
701 else if ((skin_viewport->hidden_flags&VP_DRAW_HIDDEN))
703 skin_viewport->hidden_flags |= VP_DRAW_WASHIDDEN;
704 continue;
706 else if (((skin_viewport->hidden_flags&
707 (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE))
708 == (VP_DRAW_WASHIDDEN|VP_DRAW_HIDEABLE)))
710 vp_refresh_mode = SKIN_REFRESH_ALL;
711 skin_viewport->hidden_flags = VP_DRAW_HIDEABLE;
714 display->set_viewport(&skin_viewport->vp);
715 if ((vp_refresh_mode&SKIN_REFRESH_ALL) == SKIN_REFRESH_ALL)
717 display->clear_viewport();
719 /* render */
720 if (viewport->children_count)
721 skin_render_viewport(viewport->children[0], gwps,
722 skin_viewport, vp_refresh_mode);
723 refresh_mode = old_refresh_mode;
726 /* Restore the default viewport */
727 display->set_viewport(NULL);
728 display->update();
731 #ifdef HAVE_LCD_BITMAP
732 static __attribute__((noinline)) void skin_render_playlistviewer(struct playlistviewer* viewer,
733 struct gui_wps *gwps,
734 struct skin_viewport* skin_viewport,
735 unsigned long refresh_type)
737 struct screen *display = gwps->display;
738 char linebuf[MAX_LINE];
739 skin_render_func func = skin_render_line;
740 struct skin_element* line;
741 struct skin_draw_info info = {
742 .gwps = gwps,
743 .buf = linebuf,
744 .buf_size = sizeof(linebuf),
745 .line_number = 0,
746 .no_line_break = false,
747 .line_scrolls = false,
748 .refresh_type = refresh_type,
749 .skin_vp = skin_viewport,
750 .offset = viewer->start_offset
753 struct align_pos * align = &info.align;
754 bool needs_update;
755 int cur_pos, start_item, max;
756 int nb_lines = viewport_get_nb_lines(viewer->vp);
757 #if CONFIG_TUNER
758 if (current_screen() == GO_TO_FM)
760 cur_pos = radio_current_preset();
761 start_item = cur_pos + viewer->start_offset;
762 max = start_item+radio_preset_count();
764 else
765 #endif
767 struct cuesheet *cue = skin_get_global_state()->id3 ?
768 skin_get_global_state()->id3->cuesheet : NULL;
769 cur_pos = playlist_get_display_index();
770 max = playlist_amount()+1;
771 if (cue)
772 max += cue->track_count;
773 start_item = MAX(0, cur_pos + viewer->start_offset);
775 if (max-start_item > nb_lines)
776 max = start_item + nb_lines;
778 line = viewer->line;
779 while (start_item < max)
781 linebuf[0] = '\0';
782 info.no_line_break = false;
783 info.line_scrolls = false;
784 info.force_redraw = false;
786 info.cur_align_start = info.buf;
787 align->left = info.buf;
788 align->center = NULL;
789 align->right = NULL;
792 if (line->type == LINE_ALTERNATOR)
793 func = skin_render_alternator;
794 else if (line->type == LINE)
795 func = skin_render_line;
797 needs_update = func(line, &info);
799 /* only update if the line needs to be, and there is something to write */
800 if (refresh_type && needs_update)
802 if (info.line_scrolls)
804 /* if the line is a scrolling one we don't want to update
805 too often, so that it has the time to scroll */
806 if ((refresh_type & SKIN_REFRESH_SCROLL) || info.force_redraw)
807 write_line(display, align, info.line_number, true);
809 else
810 write_line(display, align, info.line_number, false);
812 info.line_number++;
813 info.offset++;
814 start_item++;
817 #endif