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