summaryrefslogtreecommitdiff
path: root/apps/gui/gwps-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/gui/gwps-common.c')
-rw-r--r--apps/gui/gwps-common.c3644
1 files changed, 1474 insertions, 2170 deletions
diff --git a/apps/gui/gwps-common.c b/apps/gui/gwps-common.c
index bce213ab4d..2bd2e4c617 100644
--- a/apps/gui/gwps-common.c
+++ b/apps/gui/gwps-common.c
@@ -7,7 +7,7 @@
7 * \/ \/ \/ \/ \/ 7 * \/ \/ \/ \/ \/
8 * $Id$ 8 * $Id$
9 * 9 *
10 * Copyright (C) 2002 Björn Stenberg 10 * Copyright (C) 2007 Nicolas Pennequin
11 * 11 *
12 * All files in this archive are subject to the GNU General Public License. 12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement. 13 * See the file COPYING in the source tree root for full license agreement.
@@ -56,16 +56,11 @@
56#include "action.h" 56#include "action.h"
57#include "cuesheet.h" 57#include "cuesheet.h"
58 58
59#ifdef HAVE_LCD_CHARCELLS
60static bool draw_player_progress(struct gui_wps *gwps);
61static void draw_player_fullbar(struct gui_wps *gwps,
62 char* buf, int buf_size);
63#endif
64
65#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */ 59#define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
66 /* 3% of 30min file == 54s step size */ 60 /* 3% of 30min file == 54s step size */
67#define MIN_FF_REWIND_STEP 500 61#define MIN_FF_REWIND_STEP 500
68 62
63#if 0
69/* Skip leading UTF-8 BOM, if present. */ 64/* Skip leading UTF-8 BOM, if present. */
70static char* skip_utf8_bom(char* buf) 65static char* skip_utf8_bom(char* buf)
71{ 66{
@@ -78,287 +73,14 @@ static char* skip_utf8_bom(char* buf)
78 73
79 return buf; 74 return buf;
80} 75}
81
82/*
83 * returns the image_id between
84 * a..z and A..Z
85 */
86#ifdef HAVE_LCD_BITMAP
87static int get_image_id(int c)
88{
89 if(c >= 'a' && c <= 'z')
90 return c - 'a';
91 else if(c >= 'A' && c <= 'Z')
92 return c - 'A' + 26;
93 else
94 return -1;
95}
96#endif
97
98/*
99 * parse the given buffer for following static tags:
100 * %x - load image for always display
101 * %X - load backdrop image
102 * %xl - preload image
103 * %we - enable statusbar on wps regardless of the global setting
104 * %wd - disable statusbar on wps regardless of the global setting
105 * and also for:
106 * # - a comment line
107 *
108 * it returns true if one of these tags is found and handled
109 * false otherwise
110 */
111bool wps_data_preload_tags(struct wps_data *data, char *buf,
112 const char *bmpdir, size_t bmpdirlen)
113{
114 if(!data || !buf) return false;
115
116 char c;
117#ifndef HAVE_LCD_BITMAP
118 /* no bitmap-lcd == no bitmap loading */
119 (void)bmpdir;
120 (void)bmpdirlen;
121#endif
122 buf = skip_utf8_bom(buf);
123
124 if(*buf == '#')
125 return true;
126 if('%' != *buf)
127 return false;
128 buf++;
129
130 c = *buf;
131 switch (c)
132 {
133#ifdef HAVE_LCD_BITMAP
134 case 'w':
135 /*
136 * if tag found then return because these two tags must be on
137 * must be on their own line
138 */
139 if(*(buf+1) == 'd' || *(buf+1) == 'e')
140 {
141 data->wps_sb_tag = true;
142 if( *(buf+1) == 'e' )
143 data->show_sb_on_wps = true;
144 return true;
145 }
146 break;
147
148#if LCD_DEPTH > 1
149 case 'X':
150 /* Backdrop image - must be the same size as the LCD */
151 {
152 char *ptr = buf+2;
153 char *pos = NULL;
154 char imgname[MAX_PATH];
155
156 /* format: %X|filename.bmp| */
157
158 /* get filename */
159 pos = strchr(ptr, '|');
160 if ((pos - ptr) <
161 (int)sizeof(imgname)-ROCKBOX_DIR_LEN-2)
162 {
163 memcpy(imgname, bmpdir, bmpdirlen);
164 imgname[bmpdirlen] = '/';
165 memcpy(&imgname[bmpdirlen+1],
166 ptr, pos - ptr);
167 imgname[bmpdirlen+1+pos-ptr] = 0;
168 }
169 else
170 {
171 /* filename too long */
172 imgname[0] = 0;
173 }
174
175 /* load the image */
176 return load_wps_backdrop(imgname);
177 }
178
179 break;
180#endif
181
182 case 'P':
183 /* progress bar image */
184 {
185 int ret = 0;
186 char *ptr = buf+2;
187 char *pos = NULL;
188 char imgname[MAX_PATH];
189
190 /* format: %P|filename.bmp| */
191 {
192 /* get filename */
193 pos = strchr(ptr, '|');
194 if ((pos - ptr) <
195 (int)sizeof(imgname)-ROCKBOX_DIR_LEN-2)
196 {
197 memcpy(imgname, bmpdir, bmpdirlen);
198 imgname[bmpdirlen] = '/';
199 memcpy(&imgname[bmpdirlen+1],
200 ptr, pos - ptr);
201 imgname[bmpdirlen+1+pos-ptr] = 0;
202 }
203 else
204 /* filename too long */
205 imgname[0] = 0;
206
207 ptr = pos+1;
208
209 /* load the image */
210 data->progressbar.bm.data=data->img_buf_ptr;
211 ret = read_bmp_file(imgname, &data->progressbar.bm,
212 data->img_buf_free,
213 FORMAT_ANY|FORMAT_TRANSPARENT);
214
215 if (ret > 0)
216 {
217#if LCD_DEPTH == 16
218 if (ret % 2) ret++;
219 /* Always consume an even number of bytes */
220#endif
221
222 data->img_buf_ptr += ret;
223 data->img_buf_free -= ret;
224
225 if (data->progressbar.bm.width <= LCD_WIDTH) {
226 data->progressbar.have_bitmap_pb=true;
227 return true;
228 } else
229 return false;
230 }
231
232 }
233 }
234
235 break;
236
237 case 'x':
238 /* Preload images so the %xd# tag can display it */
239 {
240 int ret = 0;
241 int n;
242 char *ptr = buf+1;
243 char *pos = NULL;
244 char imgname[MAX_PATH];
245 char qual = *ptr;
246
247 if (qual == 'l' || qual == '|') /* format:
248 %x|n|filename.bmp|x|y|
249 or
250 %xl|n|filename.bmp|x|y|
251 */
252 {
253 ptr = strchr(ptr, '|') + 1;
254 pos = strchr(ptr, '|');
255 if (pos)
256 {
257 /* get the image ID */
258 n = get_image_id(*ptr);
259
260 if(n < 0 || n >= MAX_IMAGES)
261 {
262 /* Skip the rest of the line */
263 while(*buf != '\n')
264 buf++;
265 return false;
266 }
267 ptr = pos+1;
268
269 /* check the image number and load state */
270 if (data->img[n].loaded)
271 {
272 /* Skip the rest of the line */
273 while(*buf != '\n')
274 buf++;
275 return false;
276 }
277 else
278 {
279 /* get filename */
280 pos = strchr(ptr, '|');
281
282 if (pos == NULL)
283 return false;
284
285 if ((pos - ptr) <
286 (int)sizeof(imgname)-ROCKBOX_DIR_LEN-2)
287 {
288 memcpy(imgname, bmpdir, bmpdirlen);
289 imgname[bmpdirlen] = '/';
290 memcpy(&imgname[bmpdirlen+1],
291 ptr, pos - ptr);
292 imgname[bmpdirlen+1+pos-ptr] = 0;
293 }
294 else
295 /* filename too long */
296 imgname[0] = 0;
297
298 ptr = pos+1;
299
300 /* get x-position */
301 pos = strchr(ptr, '|');
302 if (pos)
303 data->img[n].x = atoi(ptr);
304 else
305 {
306 /* weird syntax, bail out */
307 buf++;
308 return false;
309 }
310
311 /* get y-position */
312 ptr = pos+1;
313 pos = strchr(ptr, '|');
314 if (pos)
315 data->img[n].y = atoi(ptr);
316 else
317 {
318 /* weird syntax, bail out */
319 buf++;
320 return false;
321 }
322
323 /* load the image */
324 data->img[n].bm.data = data->img_buf_ptr;
325 ret = read_bmp_file(imgname, &data->img[n].bm,
326 data->img_buf_free,
327 FORMAT_ANY|FORMAT_TRANSPARENT);
328
329 if (ret > 0)
330 {
331#if LCD_DEPTH == 16
332 if (ret % 2) ret++;
333 /* Always consume an even number of bytes */
334#endif 76#endif
335 77
336 data->img_buf_ptr += ret;
337 data->img_buf_free -= ret;
338 data->img[n].loaded = true;
339 if(qual == '|')
340 data->img[n].always_display = true;
341 }
342 return true;
343 }
344 }
345 }
346 }
347
348 break;
349#endif
350 }
351 /* no of these tags found */
352 return false;
353}
354
355
356/* draws the statusbar on the given wps-screen */ 78/* draws the statusbar on the given wps-screen */
357#ifdef HAVE_LCD_BITMAP 79#ifdef HAVE_LCD_BITMAP
358static void gui_wps_statusbar_draw(struct gui_wps *wps, bool force) 80void gui_wps_statusbar_draw(struct gui_wps *wps, bool force)
359{ 81{
360 bool draw = global_settings.statusbar; 82 bool draw = global_settings.statusbar;
361 83
362 if(wps->data->wps_sb_tag 84 if(wps->data->wps_sb_tag
363 && wps->data->show_sb_on_wps) 85 && wps->data->show_sb_on_wps)
364 draw = true; 86 draw = true;
@@ -372,1235 +94,449 @@ static void gui_wps_statusbar_draw(struct gui_wps *wps, bool force)
372 gui_statusbar_draw((wps)->statusbar, (force)) 94 gui_statusbar_draw((wps)->statusbar, (force))
373#endif 95#endif
374 96
375/* Extract a part from a path. 97/* fades the volume */
376 * 98void fade(bool fade_in)
377 * buf - buffer extract part to.
378 * buf_size - size of buffer.
379 * path - path to extract from.
380 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
381 * parent of parent, etc.
382 *
383 * Returns buf if the desired level was found, NULL otherwise.
384 */
385static char* get_dir(char* buf, int buf_size, const char* path, int level)
386{ 99{
387 const char* sep; 100 int fp_global_vol = global_settings.volume << 8;
388 const char* last_sep; 101 int fp_min_vol = sound_min(SOUND_VOLUME) << 8;
389 int len; 102 int fp_step = (fp_global_vol - fp_min_vol) / 30;
390 103
391 sep = path + strlen(path); 104 if (fade_in) {
392 last_sep = sep; 105 /* fade in */
106 int fp_volume = fp_min_vol;
393 107
394 while (sep > path) 108 /* zero out the sound */
395 { 109 sound_set_volume(fp_min_vol >> 8);
396 if ('/' == *(--sep))
397 {
398 if (!level)
399 {
400 break;
401 }
402 110
403 level--; 111 sleep(HZ/10); /* let audio thread run */
404 last_sep = sep - 1; 112 audio_resume();
113
114 while (fp_volume < fp_global_vol - fp_step) {
115 fp_volume += fp_step;
116 sound_set_volume(fp_volume >> 8);
117 sleep(1);
405 } 118 }
119 sound_set_volume(global_settings.volume);
406 } 120 }
121 else {
122 /* fade out */
123 int fp_volume = fp_global_vol;
407 124
408 if (level || (last_sep <= sep)) 125 while (fp_volume > fp_min_vol + fp_step) {
409 { 126 fp_volume -= fp_step;
410 return NULL; 127 sound_set_volume(fp_volume >> 8);
411 } 128 sleep(1);
129 }
130 audio_pause();
131#ifndef SIMULATOR
132 /* let audio thread run and wait for the mas to run out of data */
133 while (!mp3_pause_done())
134#endif
135 sleep(HZ/10);
412 136
413 len = MIN(last_sep - sep, buf_size - 1); 137 /* reset volume to what it was before the fade */
414 strncpy(buf, sep + 1, len); 138 sound_set_volume(global_settings.volume);
415 buf[len] = 0; 139 }
416 return buf;
417} 140}
418 141
419/* Get the tag specified by the two characters at fmt. 142/* set volume */
420 * 143void setvol(void)
421 * cid3 - ID3 data to get tag values from.
422 * nid3 - next-song ID3 data to get tag values from.
423 * tag - string (of two characters) specifying the tag to get.
424 * buf - buffer to certain tags, such as track number, play time or
425 * directory name.
426 * buf_size - size of buffer.
427 * flags - returns the type of the line. See constants i wps-display.h
428 *
429 * Returns the tag. NULL indicates the tag wasn't available.
430 */
431static char* get_tag(struct wps_data* wps_data,
432 struct mp3entry* cid3,
433 struct mp3entry* nid3,
434 const char* tag,
435 char* buf,
436 int buf_size,
437 unsigned char* tag_len,
438 unsigned short* subline_time_mult,
439 unsigned char* flags,
440 int *intval)
441{ 144{
442 struct mp3entry *id3 = cid3; /* default to current song */ 145 if (global_settings.volume < sound_min(SOUND_VOLUME))
443 int limit = *intval; 146 global_settings.volume = sound_min(SOUND_VOLUME);
444#ifndef HAVE_LCD_CHARCELLS 147 if (global_settings.volume > sound_max(SOUND_VOLUME))
445 (void)wps_data; 148 global_settings.volume = sound_max(SOUND_VOLUME);
446#endif 149 sound_set_volume(global_settings.volume);
447 if ((0 == tag[0]) || (0 == tag[1])) 150 settings_save();
448 { 151}
449 *tag_len = 0; 152/* return true if screen restore is needed
450 return NULL; 153 return false otherwise
451 } 154*/
452 155bool update_onvol_change(struct gui_wps * gwps)
453 *tag_len = 2; 156{
454 157 gui_wps_statusbar_draw(gwps, false);
455 *intval = 0; 158 gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);
456
457 switch (tag[0])
458 {
459 case 'I': /* ID3 Information */
460 id3 = nid3; /* display next-song data */
461 *flags |= WPS_REFRESH_DYNAMIC;
462 if(!id3)
463 return NULL; /* no such info (yet) */
464 /* fall-through */
465 case 'i': /* ID3 Information */
466 *flags |= WPS_REFRESH_STATIC;
467 switch (tag[1])
468 {
469 case 't': /* ID3 Title */
470 return id3->title;
471
472 case 'a': /* ID3 Artist */
473 return id3->artist;
474
475 case 'n': /* ID3 Track Number */
476 if (id3->track_string)
477 return id3->track_string;
478
479 if (id3->tracknum) {
480 snprintf(buf, buf_size, "%d", id3->tracknum);
481 return buf;
482 }
483 return NULL;
484
485 case 'd': /* ID3 Album/Disc */
486 return id3->album;
487
488 case 'c': /* ID3 Composer */
489 return id3->composer;
490
491 case 'C': /* ID3 Comment */
492 return id3->comment;
493
494 case 'A': /* ID3 Albumartist */
495 return id3->albumartist;
496
497 case 'y': /* year */
498 if( id3->year_string )
499 return id3->year_string;
500
501 if (id3->year) {
502 snprintf(buf, buf_size, "%d", id3->year);
503 return buf;
504 }
505 return NULL;
506
507 case 'g': /* genre */
508 return id3->genre_string;
509
510 case 'v': /* id3 version */
511 switch (id3->id3version)
512 {
513 case ID3_VER_1_0:
514 return "1";
515
516 case ID3_VER_1_1:
517 return "1.1";
518
519 case ID3_VER_2_2:
520 return "2.2";
521
522 case ID3_VER_2_3:
523 return "2.3";
524
525 case ID3_VER_2_4:
526 return "2.4";
527
528 default:
529 return NULL;
530 }
531 }
532 break;
533
534 case 'F': /* File Information */
535 id3 = nid3;
536 *flags |= WPS_REFRESH_DYNAMIC;
537 if(!id3)
538 return NULL; /* no such info (yet) */
539 /* fall-through */
540 case 'f': /* File Information */
541 *flags |= WPS_REFRESH_STATIC;
542 switch(tag[1])
543 {
544 case 'v': /* VBR file? */
545 return id3->vbr ? "(avg)" : NULL;
546
547 case 'b': /* File Bitrate */
548 if(id3->bitrate)
549 snprintf(buf, buf_size, "%d", id3->bitrate);
550 else
551 snprintf(buf, buf_size, "?");
552 return buf;
553
554 case 'f': /* File Frequency */
555 snprintf(buf, buf_size, "%ld", id3->frequency);
556 return buf;
557
558 case 'p': /* File Path */
559 return id3->path;
560
561 case 'm': /* File Name - With Extension */
562 return get_dir(buf, buf_size, id3->path, 0);
563
564 case 'n': /* File Name */
565 if (get_dir(buf, buf_size, id3->path, 0))
566 {
567 /* Remove extension */
568 char* sep = strrchr(buf, '.');
569
570 if (NULL != sep)
571 {
572 *sep = 0;
573 }
574
575 return buf;
576 }
577 else
578 {
579 return NULL;
580 }
581
582 case 's': /* File Size (in kilobytes) */
583 snprintf(buf, buf_size, "%ld", id3->filesize / 1024);
584 return buf;
585
586 case 'c': /* File Codec */
587 if(id3->codectype == AFMT_UNKNOWN)
588 *intval = AFMT_NUM_CODECS;
589 else
590 *intval = id3->codectype;
591 return id3_get_codec(id3);
592 }
593 break;
594 159
595 case 'p': /* Playlist/Song Information */
596 switch(tag[1])
597 {
598 case 'b': /* progress bar */
599 *flags |= WPS_REFRESH_PLAYER_PROGRESS;
600#ifdef HAVE_LCD_CHARCELLS 160#ifdef HAVE_LCD_CHARCELLS
601 char *end = utf8encode(wps_data->wps_progress_pat[0], buf); 161 gui_splash(gwps->display, 0, "Vol: %3d dB",
602 *end = '\0'; 162 sound_val2phys(SOUND_VOLUME, global_settings.volume));
603 wps_data->full_line_progressbar=0; 163 return true;
604 return buf;
605#else
606 /* default values : */
607 wps_data->progress_top = -1;
608 wps_data->progress_height = 6;
609 wps_data->progress_start = 0;
610 wps_data->progress_end = 0;
611
612 char *prev=strchr(tag, '|');
613 if (prev) {
614 char *p=strchr(prev+1, '|');
615 if (p) {
616 wps_data->progress_height=atoi(++prev);
617 prev=strchr(prev, '|');
618 p=strchr(++p, '|');
619 if (p) {
620 wps_data->progress_start=atoi(++prev);
621 prev=strchr(prev, '|');
622 p=strchr(++p, '|');
623 if (p) {
624 wps_data->progress_end=atoi(++prev);
625 prev=strchr(prev, '|');
626 p=strchr(++p, '|');
627 if(p)
628 wps_data->progress_top = atoi(++prev);
629 }
630
631 if (wps_data->progress_height<3)
632 wps_data->progress_height=3;
633 if (wps_data->progress_end<wps_data->progress_start+3)
634 wps_data->progress_end=0;
635 }
636 }
637 }
638 return "\x01";
639#endif
640 case 'f': /* full-line progress bar */
641#ifdef HAVE_LCD_CHARCELLS
642 if(is_new_player()) {
643 *flags |= WPS_REFRESH_PLAYER_PROGRESS;
644 *flags |= WPS_REFRESH_DYNAMIC;
645 wps_data->full_line_progressbar=1;
646 /* we need 11 characters (full line) for
647 progress-bar */
648 snprintf(buf, buf_size, " ");
649 }
650 else
651 {
652 /* Tell the user if we have an OldPlayer */
653 snprintf(buf, buf_size, " <Old LCD> ");
654 }
655 return buf;
656#endif
657 case 'p': /* Playlist Position */
658 *flags |= WPS_REFRESH_STATIC;
659 snprintf(buf, buf_size, "%d",
660 playlist_get_display_index());
661 return buf;
662
663 case 'n': /* Playlist Name (without path) */
664 *flags |= WPS_REFRESH_STATIC;
665 return playlist_name(NULL, buf, buf_size);
666
667 case 'e': /* Playlist Total Entries */
668 *flags |= WPS_REFRESH_STATIC;
669 snprintf(buf, buf_size, "%d", playlist_amount());
670 return buf;
671
672 case 'c': /* Current Time in Song */
673 *flags |= WPS_REFRESH_DYNAMIC;
674 format_time(buf, buf_size,
675 id3->elapsed + wps_state.ff_rewind_count);
676 return buf;
677
678 case 'r': /* Remaining Time in Song */
679 *flags |= WPS_REFRESH_DYNAMIC;
680 format_time(buf, buf_size,
681 id3->length - id3->elapsed -
682 wps_state.ff_rewind_count);
683 return buf;
684
685 case 't': /* Total Time */
686 *flags |= WPS_REFRESH_STATIC;
687 format_time(buf, buf_size, id3->length);
688 return buf;
689
690#ifdef HAVE_LCD_BITMAP
691 case 'm': /* Peak Meter */
692 *flags |= WPS_REFRESH_PEAK_METER;
693 return "\x01";
694#endif 164#endif
695 case 's': /* shuffle */ 165 return false;
696 *flags |= WPS_REFRESH_DYNAMIC; 166}
697 if ( global_settings.playlist_shuffle )
698 return "s";
699 else
700 return NULL;
701 break;
702
703 case 'v': /* volume */
704 *flags |= WPS_REFRESH_DYNAMIC;
705 snprintf(buf, buf_size, "%d", global_settings.volume);
706 *intval = limit * (global_settings.volume
707 - sound_min(SOUND_VOLUME))
708 / (sound_max(SOUND_VOLUME)
709 - sound_min(SOUND_VOLUME)) + 1;
710 return buf;
711
712 }
713 break;
714 167
715#if (CONFIG_CODEC == SWCODEC) 168bool ffwd_rew(int button)
716 case 'S': /* DSP/Equalizer/Sound settings */ 169{
717 switch (tag[1]) 170 static const int ff_rew_steps[] = {
718 { 171 1000, 2000, 3000, 4000,
719 case 'p': /* pitch */ 172 5000, 6000, 8000, 10000,
720 *intval = sound_get_pitch(); 173 15000, 20000, 25000, 30000,
721 snprintf(buf, buf_size, "%d.%d", 174 45000, 60000
722 *intval / 10, *intval % 10); 175 };
723 return buf;
724 }
725 break;
726#endif
727
728 case 'm':
729 switch (tag[1])
730 {
731 case 'm': /* playback repeat mode */
732 *flags |= WPS_REFRESH_DYNAMIC;
733 *intval = global_settings.repeat_mode + 1;
734 snprintf(buf, buf_size, "%d", *intval);
735 return buf;
736
737 /* playback status */
738 case 'p': /* play */
739 *flags |= WPS_REFRESH_DYNAMIC;
740 int status = audio_status();
741 *intval = 1;
742 if (status == AUDIO_STATUS_PLAY && \
743 !(status & AUDIO_STATUS_PAUSE))
744 *intval = 2;
745 if (audio_status() & AUDIO_STATUS_PAUSE && \
746 (! status_get_ffmode()))
747 *intval = 3;
748 if (status_get_ffmode() == STATUS_FASTFORWARD)
749 *intval = 4;
750 if (status_get_ffmode() == STATUS_FASTBACKWARD)
751 *intval = 5;
752 snprintf(buf, buf_size, "%d", *intval);
753 return buf;
754 176
755#ifdef HAS_BUTTON_HOLD 177 unsigned int step = 0; /* current ff/rewind step */
756 case 'h': /* hold */ 178 unsigned int max_step = 0; /* maximum ff/rewind step */
757 *flags |= WPS_REFRESH_DYNAMIC; 179 int ff_rewind_count = 0; /* current ff/rewind count (in ticks) */
758 if (button_hold()) 180 int direction = -1; /* forward=1 or backward=-1 */
759 return "h"; 181 long accel_tick = 0; /* next time at which to bump the step size */
760 else 182 bool exit = false;
761 return NULL; 183 bool usb = false;
762#endif 184 int i = 0;
763#ifdef HAS_REMOTE_BUTTON_HOLD
764 case 'r': /* remote hold */
765 *flags |= WPS_REFRESH_DYNAMIC;
766 if (remote_button_hold())
767 return "r";
768 else
769 return NULL;
770#endif
771 }
772 break;
773 185
774 case 'b': /* battery info */ 186 if (button == ACTION_NONE)
775 *flags |= WPS_REFRESH_DYNAMIC; 187 {
776 switch (tag[1]) 188 status_set_ffmode(0);
777 { 189 return usb;
778 case 'l': /* battery level */ 190 }
191 while (!exit)
192 {
193 switch ( button )
194 {
195 case ACTION_WPS_SEEKFWD:
196 direction = 1;
197 case ACTION_WPS_SEEKBACK:
198 if (wps_state.ff_rewind)
779 { 199 {
780 int l = battery_level(); 200 if (direction == 1)
781 limit = MAX(limit, 2);
782 if (l > -1)
783 { 201 {
784 snprintf(buf, buf_size, "%d", l); 202 /* fast forwarding, calc max step relative to end */
785 /* First enum is used for "unknown level". */ 203 max_step = (wps_state.id3->length -
786 *intval = (limit - 1) * l / 100 + 1 + 1; 204 (wps_state.id3->elapsed +
205 ff_rewind_count)) *
206 FF_REWIND_MAX_PERCENT / 100;
787 } 207 }
788 else 208 else
789 { 209 {
790 *intval = 1; 210 /* rewinding, calc max step relative to start */
791 return "?"; 211 max_step = (wps_state.id3->elapsed + ff_rewind_count) *
212 FF_REWIND_MAX_PERCENT / 100;
792 } 213 }
793 return buf;
794 }
795 214
796 case 'v': /* battery voltage */ 215 max_step = MAX(max_step, MIN_FF_REWIND_STEP);
797 {
798 unsigned int v = battery_voltage();
799 snprintf(buf, buf_size, "%d.%02d", v/100, v%100);
800 return buf;
801 }
802 216
803 case 't': /* estimated battery time */ 217 if (step > max_step)
804 { 218 step = max_step;
805 int t = battery_time();
806 if (t >= 0)
807 snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60);
808 else
809 strncpy(buf, "?h ?m", buf_size);
810 return buf;
811 }
812 219
813 case 's': /* sleep timer */ 220 ff_rewind_count += step * direction;
814 { 221
815 if (get_sleep_timer() == 0) 222 if (global_settings.ff_rewind_accel != 0 &&
816 { 223 current_tick >= accel_tick)
817 return NULL; 224 {
818 } 225 step *= 2;
819 else 226 accel_tick = current_tick +
820 { 227 global_settings.ff_rewind_accel*HZ;
821 format_time(buf, buf_size, \ 228 }
822 get_sleep_timer() * 1000);
823 return buf;
824 }
825 } 229 }
826 230 else
827#if CONFIG_CHARGING
828 case 'p': /* External power plugged in? */
829 { 231 {
830 if(charger_input_state==CHARGER) 232 if ( (audio_status() & AUDIO_STATUS_PLAY) &&
831 return "p"; 233 wps_state.id3 && wps_state.id3->length )
832 else 234 {
833 return NULL; 235 if (!wps_state.paused)
834 } 236#if (CONFIG_CODEC == SWCODEC)
237 audio_pre_ff_rewind();
238#else
239 audio_pause();
835#endif 240#endif
836#if CONFIG_CHARGING >= CHARGING_MONITOR 241#if CONFIG_KEYPAD == PLAYER_PAD
837 case 'c': /* Charging */ 242 FOR_NB_SCREENS(i)
838 { 243 gui_wps[i].display->stop_scroll();
839 if (charge_state == CHARGING || charge_state == TOPOFF) {
840 return "c";
841 } else {
842 return NULL;
843 }
844 }
845#endif 244#endif
846 } 245 if (direction > 0)
847 break; 246 status_set_ffmode(STATUS_FASTFORWARD);
247 else
248 status_set_ffmode(STATUS_FASTBACKWARD);
848 249
849#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD) 250 wps_state.ff_rewind = true;
850 case 'l': /* VIRTUAL_LED */
851 {
852 switch(tag[1])
853 {
854 case 'h': /* Only one we have so far HDD LED */
855 *flags |= WPS_REFRESH_DYNAMIC;
856 if(led_read(HZ/2))
857 return "h";
858 else
859 return NULL;
860 }
861 }
862 break;
863#endif
864 251
865 case 'D': /* Directory path information */ 252 step = ff_rew_steps[global_settings.ff_rewind_min_step];
866 id3 = nid3; /* next song please! */
867 *flags |= WPS_REFRESH_DYNAMIC;
868 if(!id3)
869 return NULL; /* no such info (yet) */
870 /* fall-through */
871 case 'd': /* Directory path information */
872 {
873 int level = tag[1] - '0';
874 *flags |= WPS_REFRESH_STATIC;
875 /* d1 through d9 */
876 if ((0 < level) && (9 > level))
877 {
878 return get_dir(buf, buf_size, id3->path, level);
879 }
880 }
881 break;
882 253
883 case 't': /* set sub line time multiplier */ 254 accel_tick = current_tick +
884 { 255 global_settings.ff_rewind_accel*HZ;
885 int d = 1;
886 int time_mult = 0;
887 bool have_point = false;
888 bool have_tenth = false;
889
890 while (((tag[d] >= '0') &&
891 (tag[d] <= '9')) ||
892 (tag[d] == '.'))
893 {
894 if (tag[d] != '.')
895 {
896 time_mult = time_mult * 10;
897 time_mult = time_mult + tag[d] - '0';
898 if (have_point)
899 {
900 have_tenth = true;
901 d++;
902 break;
903 }
904 } 256 }
905 else 257 else
906 { 258 break;
907 have_point = true;
908 }
909 d++;
910 } 259 }
911 260
912 if (have_tenth == false) 261 if (direction > 0) {
913 time_mult *= 10; 262 if ((wps_state.id3->elapsed + ff_rewind_count) >
263 wps_state.id3->length)
264 ff_rewind_count = wps_state.id3->length -
265 wps_state.id3->elapsed;
266 }
267 else {
268 if ((int)(wps_state.id3->elapsed + ff_rewind_count) < 0)
269 ff_rewind_count = -wps_state.id3->elapsed;
270 }
914 271
915 *subline_time_mult = time_mult; 272 FOR_NB_SCREENS(i)
916 *tag_len = d; 273 gui_wps_refresh(&gui_wps[i],
274 (wps_state.wps_time_countup == false)?
275 ff_rewind_count:-ff_rewind_count,
276 WPS_REFRESH_PLAYER_PROGRESS |
277 WPS_REFRESH_DYNAMIC);
917 278
918 buf[0] = 0; 279 break;
919 return buf;
920 }
921 break;
922 case 'r': /* Runtime database Information and Replaygain */
923 switch(tag[1])
924 {
925 case 'p': /* Playcount */
926 *flags |= WPS_REFRESH_DYNAMIC;
927 *intval = cid3->playcount+1;
928 snprintf(buf, buf_size, "%ld", cid3->playcount);
929 return buf;
930 case 'r': /* Rating */
931 *flags |= WPS_REFRESH_DYNAMIC;
932 *intval = cid3->rating+1;
933 snprintf(buf, buf_size, "%d", cid3->rating);
934 return buf;
935#if CONFIG_CODEC == SWCODEC
936 case 'g': /* ReplayGain */
937 *flags |= WPS_REFRESH_STATIC;
938 if (global_settings.replaygain == 0)
939 *intval = 1; /* off */
940 else
941 {
942 int type = get_replaygain_mode(
943 id3->track_gain_string != NULL,
944 id3->album_gain_string != NULL);
945
946 if (type < 0)
947 *intval = 6; /* no tag */
948 else
949 *intval = type + 2;
950
951 if (global_settings.replaygain_type == REPLAYGAIN_SHUFFLE)
952 *intval += 2;
953 }
954 280
955 switch (*intval) 281 case ACTION_WPS_STOPSEEK:
956 { 282 wps_state.id3->elapsed = wps_state.id3->elapsed+ff_rewind_count;
957 case 1: 283 audio_ff_rewind(wps_state.id3->elapsed);
958 case 6: 284 ff_rewind_count = 0;
959 return "+0.00 dB"; 285 wps_state.ff_rewind = false;
960 break; 286 status_set_ffmode(0);
961 case 2: 287#if (CONFIG_CODEC != SWCODEC)
962 case 4: 288 if (!wps_state.paused)
963 strncpy(buf, id3->track_gain_string, buf_size); 289 audio_resume();
964 break;
965 case 3:
966 case 5:
967 strncpy(buf, id3->album_gain_string, buf_size);
968 break;
969 }
970 return buf;
971#endif 290#endif
972 } 291#ifdef HAVE_LCD_CHARCELLS
973 break; 292 gui_wps_display();
974#if CONFIG_RTC
975 case 'c': /* Real Time Clock display */
976 *flags |= WPS_REFRESH_DYNAMIC;
977 {
978 int value;
979 char *format = 0;
980 char *bufptr = buf;
981 struct tm* tm = get_time();
982 int i;
983 for (i=1;/*break*/;i++) {
984 switch(tag[i])
985 {
986 case 'a': /* abbreviated weekday name (Sun..Sat) */
987 value = tm->tm_wday;
988 if (value > 6 || value < 0) continue;
989 value = snprintf(
990 bufptr,buf_size,"%s",str(dayname[value]));
991 bufptr += value;
992 buf_size -= value;
993 continue;
994 case 'b': /* abbreviated month name (Jan..Dec) */
995 value = tm->tm_mon;
996 if (value > 11 || value < 0) continue;
997 value = snprintf(
998 bufptr,buf_size,"%s",str(monthname[value]));
999 bufptr += value;
1000 buf_size -= value;
1001 continue;
1002 case 'd': /* day of month (01..31) */
1003 value = tm->tm_mday;
1004 if (value > 31 || value < 1) continue;
1005 format = "%02d";
1006 break;
1007 case 'e': /* day of month, blank padded ( 1..31) */
1008 value = tm->tm_mday;
1009 if (value > 31 || value < 1) continue;
1010 format = "%2d";
1011 break;
1012 case 'H': /* hour (00..23) */
1013 value = tm->tm_hour;
1014 if (value > 23) continue;
1015 format = "%02d";
1016 break;
1017 case 'k': /* hour ( 0..23) */
1018 value = tm->tm_hour;
1019 if (value > 23) continue;
1020 format = "%2d";
1021 break;
1022 case 'I': /* hour (01..12) */
1023 value = tm->tm_hour;
1024 if (value > 23) continue;
1025 value %= 12;
1026 if (value == 0) value = 12;
1027 format = "%02d";
1028 break;
1029 case 'l': /* hour ( 1..12) */
1030 value = tm->tm_hour;
1031 if (value > 23 || value < 0) continue;
1032 value %= 12;
1033 if (value == 0) value = 12;
1034 format = "%2d";
1035 break;
1036 case 'm': /* month (01..12) */
1037 value = tm->tm_mon;
1038 if (value > 11 || value < 0) continue;
1039 value++;
1040 format = "%02d";
1041 break;
1042 case 'M': /* minute (00..59) */
1043 value = tm->tm_min;
1044 if (value > 59 || value < 0) continue;
1045 format = "%02d";
1046 break;
1047 case 'S': /* second (00..59) */
1048 value = tm->tm_sec;
1049 if (value > 59 || value < 0) continue;
1050 format = "%02d";
1051 break;
1052 case 'y': /* last two digits of year (00..99) */
1053 value = tm->tm_year;
1054 value %= 100;
1055 format = "%02d";
1056 break;
1057 case 'Y': /* year (1970...) */
1058 value = tm->tm_year;
1059 if (value > 199 || value < 100) continue;
1060 value += 1900;
1061 format = "%04d";
1062 break;
1063 case 'p': /* upper case AM or PM indicator */
1064 if (tm->tm_hour/12 == 0) format = "AM";
1065 else format = "PM";
1066 snprintf(bufptr,buf_size,"%s",format);
1067 bufptr += 2;
1068 buf_size -= 2;
1069 continue;
1070 case 'P': /* lower case am or pm indicator */
1071 if (tm->tm_hour/12 == 0) format = "am";
1072 else format = "pm";
1073 snprintf(bufptr,buf_size,"%s",format);
1074 bufptr += 2;
1075 buf_size -= 2;
1076 continue;
1077 case 'u': /* day of week (1..7); 1 is Monday */
1078 value = tm->tm_wday;
1079 if (value < 0 || value > 6) continue;
1080 value++;
1081 format = "%1d";
1082 break;
1083 case 'w': /* day of week (0..6); 0 is Sunday */
1084 value = tm->tm_wday;
1085 if (value < 0 || value > 6) continue;
1086 format = "%1d";
1087 break;
1088 default:
1089 if (tag[i] == 'c') {
1090 i++;
1091 value = -1;
1092 break;
1093 } else if (tag[i] == '\n') {
1094 value = -1;
1095 break;
1096 }
1097 snprintf(bufptr,buf_size,"%c",tag[i]);
1098 bufptr++;
1099 buf_size--;
1100 continue;
1101 } /* switch */
1102 if (value < 0) break;
1103
1104 value = snprintf(bufptr, buf_size, format, value);
1105 bufptr += value;
1106 buf_size -= value;
1107 } /* while */
1108 *tag_len = i;
1109 return buf;
1110 }
1111#endif /* CONFIG_RTC */
1112#if CONFIG_CODEC == SWCODEC
1113 case 'x':
1114 *flags |= WPS_REFRESH_DYNAMIC;
1115 switch(tag[1])
1116 {
1117 case 'd': /* crossfeed */
1118 if(global_settings.crossfeed)
1119 return "d";
1120 else
1121 return NULL;
1122 case 'f': /* crossfade */
1123 *intval = global_settings.crossfade+1;
1124 snprintf(buf, buf_size, "%d", global_settings.crossfade);
1125 return buf;
1126 }
1127 break;
1128#endif 293#endif
1129 } 294 exit = true;
1130 return NULL; 295 break;
1131}
1132 296
1133#ifdef HAVE_LCD_BITMAP 297 default:
1134/* clears the area where the image was shown */ 298 if(default_event_handler(button) == SYS_USB_CONNECTED) {
1135static void clear_image_pos(struct gui_wps *gwps, int n) 299 status_set_ffmode(0);
1136{ 300 usb = true;
1137 if(!gwps) 301 exit = true;
1138 return; 302 }
1139 struct wps_data *data = gwps->data; 303 break;
1140 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); 304 }
1141 gwps->display->fillrect(data->img[n].x, data->img[n].y, 305 if (!exit)
1142 data->img[n].bm.width, data->img[n].bm.height); 306 button = get_action(CONTEXT_WPS,TIMEOUT_BLOCK);
1143 gwps->display->set_drawmode(DRMODE_SOLID); 307 }
308 action_signalscreenchange();
309 return usb;
1144} 310}
1145#endif
1146 311
1147/* Skip to the end of the current %? conditional. 312bool gui_wps_display(void)
1148 *
1149 * fmt - string to skip it. Should point to somewhere after the leading
1150 * "<" char (and before or at the last ">").
1151 * num - number of |'s to skip, or 0 to skip to the end (the ">").
1152 * enums - If not NULL, set to the number of |'s found in the current
1153 * conditional (sub-conditionals are ignored). num should be 0
1154 * to find all |'s.
1155 *
1156 * Returns the new position in fmt.
1157 */
1158static const char* skip_conditional(struct gui_wps *gwps, const char* fmt,
1159 int num, int *enums)
1160{ 313{
1161 int level = 1; 314 int i;
1162 int count = num; 315 if (!wps_state.id3 && !(audio_status() & AUDIO_STATUS_PLAY))
1163 const char *last_alternative = NULL; 316 {
317 global_status.resume_index = -1;
1164#ifdef HAVE_LCD_BITMAP 318#ifdef HAVE_LCD_BITMAP
1165 struct wps_data *data = NULL; 319 gui_syncstatusbar_draw(&statusbars, true);
1166 int last_x=-1, last_y=-1, last_w=-1, last_h=-1;
1167 if(gwps)
1168 data = gwps->data;
1169 if (enums)
1170 *enums = 0;
1171#else
1172 (void)gwps;
1173#endif 320#endif
1174 while (*fmt) 321 gui_syncsplash(HZ, str(LANG_END_PLAYLIST_RECORDER));
322 return true;
323 }
324 else
1175 { 325 {
1176 switch (*fmt++) 326 FOR_NB_SCREENS(i)
1177 { 327 {
1178 case '%': 328 gui_wps[i].display->clear_display();
1179#ifdef HAVE_LCD_BITMAP 329 if (!gui_wps[i].data->wps_loaded) {
1180 if(data && *(fmt) == 'x' && *(fmt+1) == 'd' ) 330 if ( !gui_wps[i].data->num_tokens ) {
1181 { 331 /* set the default wps for the main-screen */
1182 fmt +=2; 332 if(i == 0)
1183 int n = *fmt;
1184 if(n >= 'a' && n <= 'z')
1185 n -= 'a';
1186 if(n >= 'A' && n <= 'Z')
1187 n = n - 'A' + 26;
1188 if(last_x != data->img[n].x || last_y != data->img[n].y
1189 || last_w != data->img[n].bm.width
1190 || last_h != data->img[n].bm.height)
1191 { 333 {
1192 last_x = data->img[n].x; 334#ifdef HAVE_LCD_BITMAP
1193 last_y = data->img[n].y; 335#if LCD_DEPTH > 1
1194 last_w = data->img[n].bm.width; 336 unload_wps_backdrop();
1195 last_h = data->img[n].bm.height; 337#endif
1196 clear_image_pos(gwps,n); 338 wps_data_load(gui_wps[i].data,
1197 } 339 "%s%?it<%?in<%in. |>%it|%fn>\n"
1198 } 340 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
341 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
342 "\n"
343 "%al%pc/%pt%ar[%pp:%pe]\n"
344 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
345 "%pb\n"
346 "%pm\n", false);
347#else
348 wps_data_load(gui_wps[i].data,
349 "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
350 "%pc%?ps<*|/>%pt\n", false);
1199#endif 351#endif
1200 break;
1201
1202 case '|':
1203 if(1 == level) {
1204 if (enums)
1205 (*enums)++;
1206 last_alternative = fmt;
1207 if(num) {
1208 count--;
1209 if(count == 0)
1210 return fmt;
1211 continue;
1212 }
1213 }
1214 continue;
1215
1216 case '>':
1217 if (0 == --level)
1218 {
1219 /* We're just skipping to the end */
1220 if(num == 0)
1221 return fmt;
1222
1223 /* If we are parsing an enum, we'll return the selected
1224 item. If there weren't enough items in the enum, we'll
1225 return the last one found. */
1226 if(count && last_alternative)
1227 {
1228 return last_alternative;
1229 } 352 }
1230 return fmt - 1; 353#if NB_SCREENS == 2
354 /* set the default wps for the remote-screen */
355 else if(i == 1)
356 {
357 wps_data_load(gui_wps[i].data,
358 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
359 "%s%?it<%?in<%in. |>%it|%fn>\n"
360 "%al%pc/%pt%ar[%pp:%pe]\n"
361 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
362 "%pb", false);
363 }
364#endif
1231 } 365 }
1232 continue; 366 }
1233
1234 default:
1235 continue;
1236 }
1237
1238 switch (*fmt++)
1239 {
1240 case 0:
1241 case '%':
1242 case '|':
1243 case '<':
1244 case '>':
1245 break;
1246
1247 case '?':
1248 while (*fmt && ('<' != *fmt))
1249 fmt++;
1250
1251 if ('<' == *fmt)
1252 fmt++;
1253
1254 level++;
1255 break;
1256
1257 default:
1258 break;
1259 } 367 }
1260 } 368 }
1261 369 yield();
1262 return fmt; 370 FOR_NB_SCREENS(i)
371 {
372 gui_wps_refresh(&gui_wps[i], 0, WPS_REFRESH_ALL);
373 }
374 return false;
1263} 375}
1264 376
1265/* Generate the display based on id3 information and format string. 377bool update(struct gui_wps *gwps)
1266 *
1267 * buf - char buffer to write the display to.
1268 * buf_size - the size of buffer.
1269 * id3 - the ID3 data to format with.
1270 * nid3 - the ID3 data of the next song (might by NULL)
1271 * fmt - format description.
1272 * flags - returns the type of the line. See constants i wps-display.h
1273 */
1274static void format_display(struct gui_wps *gwps, char* buf,
1275 int buf_size,
1276 struct mp3entry* id3,
1277 struct mp3entry* nid3, /* next song's id3 */
1278 const char* fmt,
1279 struct align_pos* align,
1280 unsigned short* subline_time_mult,
1281 unsigned char* flags)
1282{ 378{
1283 char temp_buf[128]; 379 bool track_changed = audio_has_changed_track();
1284 char* buf_start = buf; 380 bool retcode = false;
1285 char* buf_end = buf + buf_size - 1; /* Leave room for end null */
1286 char* value = NULL;
1287 int level = 0;
1288 unsigned char tag_length;
1289 int intval;
1290 int cur_align;
1291 char* cur_align_start;
1292#ifdef HAVE_LCD_BITMAP
1293 struct gui_img *img = gwps->data->img;
1294 int n;
1295#endif
1296
1297 cur_align_start = buf;
1298 cur_align = WPS_ALIGN_LEFT;
1299 *subline_time_mult = DEFAULT_SUBLINE_TIME_MULTIPLIER;
1300
1301 align->left = 0;
1302 align->center = 0;
1303 align->right = 0;
1304 381
1305 while (fmt && *fmt && buf < buf_end) 382 gwps->state->nid3 = audio_next_track();
383 if (track_changed)
1306 { 384 {
1307 switch (*fmt) 385 gwps->display->stop_scroll();
1308 { 386 gwps->state->id3 = audio_current_track();
1309 case '%':
1310 ++fmt;
1311 break;
1312
1313 case '|':
1314 case '>':
1315 if (level > 0)
1316 {
1317 fmt = skip_conditional(NULL, fmt, 0, NULL);
1318 level--;
1319 continue;
1320 }
1321 /* Else fall through */
1322
1323 default:
1324 *buf++ = *fmt++;
1325 continue;
1326 }
1327 387
1328 switch (*fmt) 388 if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type
389 && strcmp(gwps->state->id3->path, curr_cue->audio_filename))
1329 { 390 {
1330 case 0: 391 /* the current cuesheet isn't the right one any more */
1331 *buf++ = '%';
1332 break;
1333 case 'a':
1334 ++fmt;
1335 /* remember where the current aligned text started */
1336 switch (cur_align)
1337 {
1338 case WPS_ALIGN_LEFT:
1339 align->left = cur_align_start;
1340 break;
1341 392
1342 case WPS_ALIGN_CENTER: 393 if (!strcmp(gwps->state->id3->path, temp_cue->audio_filename)) {
1343 align->center = cur_align_start; 394 /* We have the new cuesheet in memory (temp_cue),
1344 break; 395 let's make it the current one ! */
396 memcpy(curr_cue, temp_cue, sizeof(struct cuesheet));
397 }
398 else {
399 /* We need to parse the new cuesheet */
1345 400
1346 case WPS_ALIGN_RIGHT: 401 char cuepath[MAX_PATH];
1347 align->right = cur_align_start; 402 strncpy(cuepath, gwps->state->id3->path, MAX_PATH);
1348 break; 403 char *dot = strrchr(cuepath, '.');
1349 } 404 strcpy(dot, ".cue");
1350 /* start a new alignment */
1351 switch (*fmt)
1352 {
1353 case 'l':
1354 cur_align = WPS_ALIGN_LEFT;
1355 break;
1356 case 'c':
1357 cur_align = WPS_ALIGN_CENTER;
1358 break;
1359 case 'r':
1360 cur_align = WPS_ALIGN_RIGHT;
1361 break;
1362 }
1363 *buf++=0;
1364 cur_align_start = buf;
1365 ++fmt;
1366 break;
1367 case 's':
1368 *flags |= WPS_REFRESH_SCROLL;
1369 ++fmt;
1370 break;
1371 405
1372 case 'x': /* image support */ 406 if (parse_cuesheet(cuepath, curr_cue))
1373#ifdef HAVE_LCD_BITMAP
1374 if ('d' == *(fmt+1) )
1375 { 407 {
1376 fmt+=2; 408 gwps->state->id3->cuesheet_type = 1;
1377 409 strcpy(curr_cue->audio_filename, gwps->state->id3->path);
1378 /* get the image ID */
1379 n = *fmt;
1380 if(n >= 'a' && n <= 'z')
1381 n -= 'a';
1382 if(n >= 'A' && n <= 'Z')
1383 n = n - 'A' + 26;
1384 if (n >= 0 && n < MAX_IMAGES && img[n].loaded) {
1385 img[n].display = true;
1386 }
1387 } 410 }
411 }
1388 412
1389#endif 413 cue_spoof_id3(curr_cue, gwps->state->id3);
1390 fmt++; 414 }
1391 break;
1392
1393
1394 case '%':
1395 case '|':
1396 case '<':
1397 case '>':
1398 case ';':
1399 *buf++ = *fmt++;
1400 break;
1401
1402 case '?':
1403 fmt++;
1404 /* Get number of "|" chars in the current conditional;
1405 * used by get_tag when calculating levels.
1406 */
1407 skip_conditional(gwps, fmt, 0, &intval);
1408 value = get_tag(gwps->data, id3, nid3, fmt, temp_buf,
1409 sizeof(temp_buf),&tag_length,
1410 subline_time_mult, flags, &intval);
1411
1412 while (*fmt && ('<' != *fmt))
1413 fmt++;
1414
1415 if ('<' == *fmt)
1416 fmt++;
1417
1418 /* No value, so skip to else part, using a sufficiently high
1419 value to "hit" the last part of the conditional */
1420 if ((!value) || (!strlen(value)))
1421 fmt = skip_conditional(NULL, fmt, 1000, NULL);
1422 else
1423 if(intval > 1) /* enum */
1424 fmt = skip_conditional(NULL, fmt, intval - 1, NULL);
1425
1426 level++;
1427 break;
1428
1429 default:
1430 intval = 1;
1431 value = get_tag(gwps->data, id3, nid3, fmt, temp_buf,
1432 sizeof(temp_buf), &tag_length,
1433 subline_time_mult, flags,&intval);
1434 fmt += tag_length;
1435 415
1436 if (value) 416 if (gui_wps_display())
1437 { 417 retcode = true;
1438 while (*value && (buf < buf_end)) 418 else{
1439 *buf++ = *value++; 419 gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL);
1440 }
1441 } 420 }
421
422 if (gwps->state->id3)
423 memcpy(gwps->state->current_track_path, gwps->state->id3->path,
424 sizeof(gwps->state->current_track_path));
1442 } 425 }
1443 426
1444 /* remember where the current aligned text started */ 427 if (gwps->state->id3)
1445 switch (cur_align)
1446 { 428 {
1447 case WPS_ALIGN_LEFT: 429 if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type
1448 align->left = cur_align_start; 430 && (gwps->state->id3->elapsed < curr_cue->curr_track->offset
1449 break; 431 || (curr_cue->curr_track_idx < curr_cue->track_count - 1
432 && gwps->state->id3->elapsed >= (curr_cue->curr_track+1)->offset)))
433 {
434 /* We've changed tracks within the cuesheet :
435 we need to update the ID3 info and refresh the WPS */
1450 436
1451 case WPS_ALIGN_CENTER: 437 cue_find_current_track(curr_cue, gwps->state->id3->elapsed);
1452 align->center = cur_align_start; 438 cue_spoof_id3(curr_cue, gwps->state->id3);
1453 break;
1454 439
1455 case WPS_ALIGN_RIGHT: 440 gwps->display->stop_scroll();
1456 align->right = cur_align_start; 441 if (gui_wps_display())
1457 break; 442 retcode = true;
443 else
444 gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL);
445 }
446 else
447 gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);
1458 } 448 }
1459 449
1460 *buf = 0; 450 gui_wps_statusbar_draw(gwps, false);
1461
1462 /* if resulting line is an empty line, set the subline time to 0 */
1463 if (buf - buf_start == 0)
1464 *subline_time_mult = 0;
1465 451
1466 /* If no flags have been set, the line didn't contain any format codes. 452 return retcode;
1467 We still want to refresh it. */
1468 if(*flags == 0)
1469 *flags = WPS_REFRESH_STATIC;
1470} 453}
1471 454
1472/* fades the volume */ 455
1473void fade(bool fade_in) 456void display_keylock_text(bool locked)
1474{ 457{
1475 int fp_global_vol = global_settings.volume << 8; 458 char* s;
1476 int fp_min_vol = sound_min(SOUND_VOLUME) << 8; 459 int i;
1477 int fp_step = (fp_global_vol - fp_min_vol) / 30; 460 FOR_NB_SCREENS(i)
461 gui_wps[i].display->stop_scroll();
1478 462
1479 if (fade_in) { 463#ifdef HAVE_LCD_CHARCELLS
1480 /* fade in */ 464 if(locked)
1481 int fp_volume = fp_min_vol; 465 s = str(LANG_KEYLOCK_ON_PLAYER);
466 else
467 s = str(LANG_KEYLOCK_OFF_PLAYER);
468#else
469 if(locked)
470 s = str(LANG_KEYLOCK_ON_RECORDER);
471 else
472 s = str(LANG_KEYLOCK_OFF_RECORDER);
473#endif
474 gui_syncsplash(HZ, s);
475}
1482 476
1483 /* zero out the sound */ 477#ifdef HAVE_LCD_BITMAP
1484 sound_set_volume(fp_min_vol >> 8);
1485 478
1486 sleep(HZ/10); /* let audio thread run */ 479static void draw_progressbar(struct gui_wps *gwps, int line)
1487 audio_resume(); 480{
1488 481 struct wps_data *data = gwps->data;
1489 while (fp_volume < fp_global_vol - fp_step) { 482 struct screen *display = gwps->display;
1490 fp_volume += fp_step; 483 struct wps_state *state = gwps->state;
1491 sound_set_volume(fp_volume >> 8); 484 int h = font_get(FONT_UI)->height;
1492 sleep(1);
1493 }
1494 sound_set_volume(global_settings.volume);
1495 }
1496 else {
1497 /* fade out */
1498 int fp_volume = fp_global_vol;
1499 485
1500 while (fp_volume > fp_min_vol + fp_step) { 486 int sb_y;
1501 fp_volume -= fp_step; 487 if (data->progress_top < 0)
1502 sound_set_volume(fp_volume >> 8); 488 sb_y = line*h + display->getymargin() +
1503 sleep(1); 489 ((h > data->progress_height + 1)
1504 } 490 ? (h - data->progress_height) / 2 : 1);
1505 audio_pause(); 491 else
1506#ifndef SIMULATOR 492 sb_y = data->progress_top;
1507 /* let audio thread run and wait for the mas to run out of data */ 493
1508 while (!mp3_pause_done()) 494 if (!data->progress_end)
495 data->progress_end=display->width;
496
497 if (gwps->data->progressbar.have_bitmap_pb)
498 gui_bitmap_scrollbar_draw(display, data->progressbar.bm,
499 data->progress_start, sb_y,
500 data->progress_end-data->progress_start,
501 data->progressbar.bm.height,
502 state->id3->length ? state->id3->length : 1, 0,
503 state->id3->length ? state->id3->elapsed
504 + state->ff_rewind_count : 0,
505 HORIZONTAL);
506 else
507 gui_scrollbar_draw(display, data->progress_start, sb_y,
508 data->progress_end-data->progress_start,
509 data->progress_height,
510 state->id3->length ? state->id3->length : 1, 0,
511 state->id3->length ? state->id3->elapsed
512 + state->ff_rewind_count : 0,
513 HORIZONTAL);
514
515#ifdef AB_REPEAT_ENABLE
516 if ( ab_repeat_mode_enabled() )
517 ab_draw_markers(display, state->id3->length,
518 data->progress_start, data->progress_end, sb_y,
519 data->progress_height);
1509#endif 520#endif
1510 sleep(HZ/10);
1511 521
1512 /* reset volume to what it was before the fade */ 522 if ( cuesheet_is_enabled() && state->id3->cuesheet_type )
1513 sound_set_volume(global_settings.volume); 523 cue_draw_markers(display, state->id3->length,
1514 } 524 data->progress_start, data->progress_end,
525 sb_y+1, data->progress_height-2);
1515} 526}
1516 527
1517/* Set format string to use for WPS, splitting it into lines */ 528/* clears the area where the image was shown */
1518void gui_wps_format(struct wps_data *data) 529static void clear_image_pos(struct gui_wps *gwps, int n)
1519{ 530{
1520 char* buf = data->format_buffer; 531 if(!gwps)
1521 char* start_of_line = data->format_buffer;
1522 int line = 0;
1523 int subline;
1524 char c;
1525 if(!data)
1526 return; 532 return;
1527 533 struct wps_data *data = gwps->data;
1528 for (line=0; line<WPS_MAX_LINES; line++) 534 gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1529 { 535 gwps->display->fillrect(data->img[n].x, data->img[n].y,
1530 for (subline=0; subline<WPS_MAX_SUBLINES; subline++) 536 data->img[n].bm.width, data->img[n].bm.height);
1531 { 537 gwps->display->set_drawmode(DRMODE_SOLID);
1532 data->format_lines[line][subline] = 0;
1533 data->time_mult[line][subline] = 0;
1534 }
1535 data->subline_expire_time[line] = 0;
1536 data->curr_subline[line] = SUBLINE_RESET;
1537 }
1538
1539 line = 0;
1540 subline = 0;
1541 buf = skip_utf8_bom(buf);
1542 data->format_lines[line][subline] = buf;
1543
1544 while ((*buf) && (line < WPS_MAX_LINES))
1545 {
1546 c = *buf;
1547
1548 switch (c)
1549 {
1550 /*
1551 * skip % sequences so "%;" doesn't start a new subline
1552 * don't skip %x lines (pre-load bitmaps)
1553 */
1554 case '%':
1555 buf++;
1556 break;
1557
1558 case '\r': /* CR */
1559 *buf = 0;
1560 break;
1561
1562 case '\n': /* LF */
1563 *buf = 0;
1564
1565 if (*start_of_line != '#') /* A comment? */
1566 line++;
1567
1568 if (line < WPS_MAX_LINES)
1569 {
1570 /* the next line starts on the next byte */
1571 subline = 0;
1572 data->format_lines[line][subline] = buf+1;
1573 start_of_line = data->format_lines[line][subline];
1574 }
1575 break;
1576
1577 case ';': /* start a new subline */
1578 *buf = 0;
1579 subline++;
1580 if (subline < WPS_MAX_SUBLINES)
1581 {
1582 data->format_lines[line][subline] = buf+1;
1583 }
1584 else /* exceeded max sublines, skip rest of line */
1585 {
1586 while (*(++buf))
1587 {
1588 if ((*buf == '\r') || (*buf == '\n'))
1589 {
1590 break;
1591 }
1592 }
1593 buf--;
1594 subline = 0;
1595 }
1596 break;
1597 }
1598 buf++;
1599 }
1600} 538}
1601 539
1602#ifdef HAVE_LCD_BITMAP
1603/* Display images */
1604static void wps_draw_image(struct gui_wps *gwps, int n) 540static void wps_draw_image(struct gui_wps *gwps, int n)
1605{ 541{
1606 struct screen *display = gwps->display; 542 struct screen *display = gwps->display;
@@ -1625,509 +561,29 @@ static void wps_draw_image(struct gui_wps *gwps, int n)
1625 } 561 }
1626#endif 562#endif
1627} 563}
1628static void wps_display_images(struct gui_wps *gwps, bool always)
1629{
1630 if(!gwps || !gwps->data || !gwps->display) return;
1631 int n;
1632 struct wps_data *data = gwps->data;
1633 struct screen *display = gwps->display;
1634 for (n = 0; n < MAX_IMAGES; n++) {
1635 if (data->img[n].loaded) {
1636 if( (!always && data->img[n].display)
1637 || (always && data->img[n].always_display) )
1638 wps_draw_image(gwps, n);
1639 }
1640 }
1641 display->set_drawmode(DRMODE_SOLID);
1642}
1643#endif
1644 564
1645#if 0 /* currently unused */ 565static void wps_display_images(struct gui_wps *gwps)
1646void gui_wps_reset(struct gui_wps *gui_wps)
1647{ 566{
1648 if(!gui_wps || !gui_wps->data) 567 if(!gwps || !gwps->data || !gwps->display)
1649 return; 568 return;
1650 gui_wps->data->wps_loaded = false;
1651 memset(&gui_wps->data->format_buffer, 0,
1652 sizeof(gui_wps->data->format_buffer));
1653}
1654#endif
1655 569
1656bool gui_wps_refresh(struct gui_wps *gwps, int ffwd_offset, 570 int n;
1657 unsigned char refresh_mode)
1658{
1659 char buf[MAX_PATH];
1660 unsigned char flags;
1661 int i;
1662 bool update_line;
1663 bool only_one_subline;
1664 bool new_subline_refresh;
1665 bool reset_subline;
1666 int search;
1667 int search_start;
1668 struct align_pos format_align;
1669 struct wps_data *data = gwps->data; 571 struct wps_data *data = gwps->data;
1670 struct wps_state *state = gwps->state;
1671 struct screen *display = gwps->display; 572 struct screen *display = gwps->display;
1672 if(!gwps || !data || !state || !display)
1673 {
1674 return false;
1675 }
1676#ifdef HAVE_LCD_BITMAP
1677 int h = font_get(FONT_UI)->height;
1678 int offset = 0;
1679 gui_wps_statusbar_draw(gwps, true);
1680 if(data->wps_sb_tag && data->show_sb_on_wps)
1681 offset = STATUSBAR_HEIGHT;
1682 else if ( global_settings.statusbar && !data->wps_sb_tag)
1683 offset = STATUSBAR_HEIGHT;
1684
1685 /* to find out wether the peak meter is enabled we
1686 assume it wasn't until we find a line that contains
1687 the peak meter. We can't use peak_meter_enabled itself
1688 because that would mean to turn off the meter thread
1689 temporarily. (That shouldn't matter unless yield
1690 or sleep is called but who knows...)
1691 */
1692 bool enable_pm = false;
1693
1694 /* Set images to not to be displayed */
1695 for (i = 0; i < MAX_IMAGES; i++) {
1696 data->img[i].display = false;
1697 }
1698#endif
1699 /* reset to first subline if refresh all flag is set */
1700 if (refresh_mode == WPS_REFRESH_ALL)
1701 {
1702 for (i=0; i<WPS_MAX_LINES; i++)
1703 {
1704 data->curr_subline[i] = SUBLINE_RESET;
1705 }
1706 }
1707
1708#ifdef HAVE_LCD_CHARCELLS
1709 for (i=0; i<8; i++) {
1710 if (data->wps_progress_pat[i]==0)
1711 data->wps_progress_pat[i]=display->get_locked_pattern();
1712 }
1713#endif
1714
1715 if (!state->id3)
1716 {
1717 display->stop_scroll();
1718 return false;
1719 }
1720
1721 state->ff_rewind_count = ffwd_offset;
1722 573
1723 for (i = 0; i < WPS_MAX_LINES; i++) 574 for (n = 0; n < MAX_IMAGES; n++)
1724 { 575 {
1725 reset_subline = (data->curr_subline[i] == SUBLINE_RESET); 576 if (data->img[n].loaded &&
1726 new_subline_refresh = false; 577 (data->img[n].display || data->img[n].always_display))
1727 only_one_subline = false;
1728
1729 /* if time to advance to next sub-line */
1730 if (TIME_AFTER(current_tick, data->subline_expire_time[i] - 1) ||
1731 reset_subline)
1732 { 578 {
1733 /* search all sublines until the next subline with time > 0 579 wps_draw_image(gwps, n);
1734 is found or we get back to the subline we started with */
1735 if (reset_subline)
1736 search_start = 0;
1737 else
1738 search_start = data->curr_subline[i];
1739 for (search=0; search<WPS_MAX_SUBLINES; search++)
1740 {
1741 data->curr_subline[i]++;
1742
1743 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
1744 if ((!data->format_lines[i][data->curr_subline[i]]) ||
1745 (data->curr_subline[i] == WPS_MAX_SUBLINES))
1746 {
1747 if (data->curr_subline[i] == 1)
1748 only_one_subline = true;
1749 data->curr_subline[i] = 0;
1750 }
1751
1752 /* if back where we started after search or
1753 only one subline is defined on the line */
1754 if (((search > 0) && (data->curr_subline[i] == search_start)) ||
1755 only_one_subline)
1756 {
1757 /* no other subline with a time > 0 exists */
1758 data->subline_expire_time[i] = (reset_subline?
1759 current_tick : data->subline_expire_time[i]) + 100 * HZ;
1760 break;
1761 }
1762 else
1763 {
1764 /* get initial time multiplier and
1765 line type flags for this subline */
1766 format_display(gwps, buf, sizeof(buf),
1767 state->id3, state->nid3,
1768 data->format_lines[i][data->curr_subline[i]],
1769 &format_align,
1770 &data->time_mult[i][data->curr_subline[i]],
1771 &data->line_type[i][data->curr_subline[i]]);
1772
1773 /* only use this subline if subline time > 0 */
1774 if (data->time_mult[i][data->curr_subline[i]] > 0)
1775 {
1776 new_subline_refresh = true;
1777 data->subline_expire_time[i] = (reset_subline?
1778 current_tick : data->subline_expire_time[i]) +
1779 BASE_SUBLINE_TIME * data->time_mult[i][data->curr_subline[i]];
1780 break;
1781 }
1782 }
1783 }
1784
1785 }
1786
1787 update_line = false;
1788
1789 if ( !data->format_lines[i][data->curr_subline[i]] )
1790 break;
1791
1792 if ((data->line_type[i][data->curr_subline[i]] & refresh_mode) ||
1793 (refresh_mode == WPS_REFRESH_ALL) ||
1794 new_subline_refresh)
1795 {
1796 flags = 0;
1797 int left_width, left_xpos;
1798 int center_width, center_xpos;
1799 int right_width, right_xpos;
1800 int space_width;
1801 int string_height;
1802 int ypos;
1803
1804 format_display(gwps, buf, sizeof(buf),
1805 state->id3, state->nid3,
1806 data->format_lines[i][data->curr_subline[i]],
1807 &format_align,
1808 &data->time_mult[i][data->curr_subline[i]],
1809 &flags);
1810 data->line_type[i][data->curr_subline[i]] = flags;
1811
1812#ifdef HAVE_LCD_BITMAP
1813 /* progress */
1814 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1815 {
1816 int sb_y;
1817 if (data->progress_top == -1)
1818 sb_y = i*h + offset + ((h > data->progress_height + 1)
1819 ? (h - data->progress_height) / 2 : 1);
1820 else
1821 sb_y = data->progress_top;
1822
1823 if (!data->progress_end)
1824 data->progress_end=display->width;
1825
1826 if (gwps->data->progressbar.have_bitmap_pb)
1827 gui_bitmap_scrollbar_draw(display, data->progressbar.bm,
1828 data->progress_start, sb_y,
1829 data->progress_end-data->progress_start,
1830 data->progressbar.bm.height,
1831 state->id3->length?state->id3->length:1, 0,
1832 state->id3->length?state->id3->elapsed + state->ff_rewind_count:0,
1833 HORIZONTAL);
1834 else
1835 gui_scrollbar_draw(display, data->progress_start, sb_y,
1836 data->progress_end-data->progress_start,
1837 data->progress_height,
1838 state->id3->length?state->id3->length:1, 0,
1839 state->id3->length?state->id3->elapsed + state->ff_rewind_count:0,
1840 HORIZONTAL);
1841#ifdef AB_REPEAT_ENABLE
1842 if ( ab_repeat_mode_enabled() )
1843 ab_draw_markers(display, state->id3->length,
1844 data->progress_start, data->progress_end, sb_y,
1845 data->progress_height);
1846#endif
1847
1848 if (cuesheet_is_enabled() && state->id3->cuesheet_type)
1849 {
1850 cue_draw_markers(display, state->id3->length,
1851 data->progress_start, data->progress_end,
1852 sb_y+1, data->progress_height-2);
1853 }
1854
1855 update_line = true;
1856 }
1857 if (flags & refresh_mode & WPS_REFRESH_PEAK_METER) {
1858 /* peak meter */
1859 int peak_meter_y;
1860
1861 update_line = true;
1862 peak_meter_y = i * h + offset;
1863
1864 /* The user might decide to have the peak meter in the last
1865 line so that it is only displayed if no status bar is
1866 visible. If so we neither want do draw nor enable the
1867 peak meter. */
1868 if (peak_meter_y + h <= display->height) {
1869 /* found a line with a peak meter -> remember that we must
1870 enable it later */
1871 enable_pm = true;
1872 peak_meter_screen(gwps->display, 0, peak_meter_y,
1873 MIN(h, display->height - peak_meter_y));
1874 }
1875 }
1876#else
1877 /* progress */
1878 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS) {
1879 if (data->full_line_progressbar)
1880 draw_player_fullbar(gwps, buf, sizeof(buf));
1881 else
1882 draw_player_progress(gwps);
1883 }
1884#endif
1885 /* calculate different string sizes and positions */
1886 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
1887 if (format_align.left != 0) {
1888 display->getstringsize((unsigned char *)format_align.left,
1889 &left_width, &string_height);
1890 }
1891 else {
1892 left_width = 0;
1893 }
1894 left_xpos = 0;
1895
1896 if (format_align.center != 0) {
1897 display->getstringsize((unsigned char *)format_align.center,
1898 &center_width, &string_height);
1899 }
1900 else {
1901 center_width = 0;
1902 }
1903 center_xpos=(display->width - center_width) / 2;
1904
1905 if (format_align.right != 0) {
1906 display->getstringsize((unsigned char *)format_align.right,
1907 &right_width, &string_height);
1908 }
1909 else {
1910 right_width = 0;
1911 }
1912 right_xpos = (display->width - right_width);
1913
1914 /* Checks for overlapping strings.
1915 If needed the overlapping strings will be merged, separated by a
1916 space */
1917
1918 /* CASE 1: left and centered string overlap */
1919 /* there is a left string, need to merge left and center */
1920 if ((left_width != 0 && center_width != 0) &&
1921 (left_xpos + left_width + space_width > center_xpos)) {
1922 /* replace the former separator '\0' of left and
1923 center string with a space */
1924 *(--format_align.center) = ' ';
1925 /* calculate the new width and position of the merged string */
1926 left_width = left_width + space_width + center_width;
1927 left_xpos = 0;
1928 /* there is no centered string anymore */
1929 center_width = 0;
1930 }
1931 /* there is no left string, move center to left */
1932 if ((left_width == 0 && center_width != 0) &&
1933 (left_xpos + left_width > center_xpos)) {
1934 /* move the center string to the left string */
1935 format_align.left = format_align.center;
1936 /* calculate the new width and position of the string */
1937 left_width = center_width;
1938 left_xpos = 0;
1939 /* there is no centered string anymore */
1940 center_width = 0;
1941 }
1942
1943 /* CASE 2: centered and right string overlap */
1944 /* there is a right string, need to merge center and right */
1945 if ((center_width != 0 && right_width != 0) &&
1946 (center_xpos + center_width + space_width > right_xpos)) {
1947 /* replace the former separator '\0' of center and
1948 right string with a space */
1949 *(--format_align.right) = ' ';
1950 /* move the center string to the right after merge */
1951 format_align.right = format_align.center;
1952 /* calculate the new width and position of the merged string */
1953 right_width = center_width + space_width + right_width;
1954 right_xpos = (display->width - right_width);
1955 /* there is no centered string anymore */
1956 center_width = 0;
1957 }
1958 /* there is no right string, move center to right */
1959 if ((center_width != 0 && right_width == 0) &&
1960 (center_xpos + center_width > right_xpos)) {
1961 /* move the center string to the right string */
1962 format_align.right = format_align.center;
1963 /* calculate the new width and position of the string */
1964 right_width = center_width;
1965 right_xpos = (display->width - right_width);
1966 /* there is no centered string anymore */
1967 center_width = 0;
1968 }
1969
1970 /* CASE 3: left and right overlap
1971 There is no center string anymore, either there never
1972 was one or it has been merged in case 1 or 2 */
1973 /* there is a left string, need to merge left and right */
1974 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
1975 (left_xpos + left_width + space_width > right_xpos)) {
1976 /* replace the former separator '\0' of left and
1977 right string with a space */
1978 *(--format_align.right) = ' ';
1979 /* calculate the new width and position of the string */
1980 left_width = left_width + space_width + right_width;
1981 left_xpos = 0;
1982 /* there is no right string anymore */
1983 right_width = 0;
1984 }
1985 /* there is no left string, move right to left */
1986 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
1987 (left_xpos + left_width > right_xpos)) {
1988 /* move the right string to the left string */
1989 format_align.left = format_align.right;
1990 /* calculate the new width and position of the string */
1991 left_width = right_width;
1992 left_xpos = 0;
1993 /* there is no right string anymore */
1994 right_width = 0;
1995 }
1996
1997 if (flags & WPS_REFRESH_SCROLL) {
1998
1999 /* scroll line */
2000 if ((refresh_mode & WPS_REFRESH_SCROLL) ||
2001 new_subline_refresh) {
2002
2003 ypos = (i*string_height)+display->getymargin();
2004 update_line = true;
2005
2006 if (left_width>display->width) {
2007 display->puts_scroll(0, i,
2008 (unsigned char *)format_align.left);
2009 } else {
2010 /* clear the line first */
2011#ifdef HAVE_LCD_BITMAP
2012 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
2013 display->fillrect(0, ypos, display->width, string_height);
2014 display->set_drawmode(DRMODE_SOLID);
2015#endif
2016 /* Nasty hack: we output an empty scrolling string,
2017 which will reset the scroller for that line */
2018 display->puts_scroll(0, i, (unsigned char *)"");
2019
2020 /* print aligned strings */
2021 if (left_width != 0)
2022 {
2023 display->putsxy(left_xpos, ypos,
2024 (unsigned char *)format_align.left);
2025 }
2026 if (center_width != 0)
2027 {
2028 display->putsxy(center_xpos, ypos,
2029 (unsigned char *)format_align.center);
2030 }
2031 if (right_width != 0)
2032 {
2033 display->putsxy(right_xpos, ypos,
2034 (unsigned char *)format_align.right);
2035 }
2036 }
2037 }
2038 }
2039 else if (flags & (WPS_REFRESH_DYNAMIC | WPS_REFRESH_STATIC))
2040 {
2041 /* dynamic / static line */
2042 if ((refresh_mode & (WPS_REFRESH_DYNAMIC|WPS_REFRESH_STATIC)) ||
2043 new_subline_refresh)
2044 {
2045 ypos = (i*string_height)+display->getymargin();
2046 update_line = true;
2047
2048#ifdef HAVE_LCD_BITMAP
2049 /* clear the line first */
2050 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
2051 display->fillrect(0, ypos, display->width, string_height);
2052 display->set_drawmode(DRMODE_SOLID);
2053#endif
2054
2055 /* Nasty hack: we output an empty scrolling string,
2056 which will reset the scroller for that line */
2057 display->puts_scroll(0, i, (unsigned char *)"");
2058
2059 /* print aligned strings */
2060 if (left_width != 0)
2061 {
2062 display->putsxy(left_xpos, ypos,
2063 (unsigned char *)format_align.left);
2064 }
2065 if (center_width != 0)
2066 {
2067 display->putsxy(center_xpos, ypos,
2068 (unsigned char *)format_align.center);
2069 }
2070 if (right_width != 0)
2071 {
2072 display->putsxy(right_xpos, ypos,
2073 (unsigned char *)format_align.right);
2074 }
2075 }
2076 }
2077 }
2078#ifdef HAVE_LCD_BITMAP
2079 if (update_line) {
2080 wps_display_images(gwps,false);
2081 } 580 }
2082#endif
2083 }
2084
2085#ifdef HAVE_LCD_BITMAP
2086 /* Display all images */
2087 wps_display_images(gwps,true);
2088 display->update();
2089 /* Now we know wether the peak meter is used.
2090 So we can enable / disable the peak meter thread */
2091 data->peak_meter_enabled = enable_pm;
2092#endif
2093
2094#if CONFIG_BACKLIGHT
2095 if (global_settings.caption_backlight && state->id3) {
2096 /* turn on backlight n seconds before track ends, and turn it off n
2097 seconds into the new track. n == backlight_timeout, or 5s */
2098 int n = backlight_timeout_value[global_settings.backlight_timeout]
2099 * 1000;
2100
2101 if ( n < 1000 )
2102 n = 5000; /* use 5s if backlight is always on or off */
2103
2104 if (((state->id3->elapsed < 1000) ||
2105 ((state->id3->length - state->id3->elapsed) < (unsigned)n)) &&
2106 (state->paused == false))
2107 backlight_on();
2108 } 581 }
2109#endif 582 display->set_drawmode(DRMODE_SOLID);
2110#ifdef HAVE_REMOTE_LCD
2111 if (global_settings.remote_caption_backlight && state->id3) {
2112 /* turn on remote backlight n seconds before track ends, and turn it
2113 off n seconds into the new track. n == remote_backlight_timeout,
2114 or 5s */
2115 int n = backlight_timeout_value[global_settings.remote_backlight_timeout]
2116 * 1000;
2117
2118 if ( n < 1000 )
2119 n = 5000; /* use 5s if backlight is always on or off */
2120
2121 if (((state->id3->elapsed < 1000) ||
2122 ((state->id3->length - state->id3->elapsed) < (unsigned)n)) &&
2123 (state->paused == false))
2124 remote_backlight_on();
2125 }
2126#endif
2127 return true;
2128} 583}
2129 584
2130#ifdef HAVE_LCD_CHARCELLS 585#else /* HAVE_LCD_CHARCELL */
586
2131static bool draw_player_progress(struct gui_wps *gwps) 587static bool draw_player_progress(struct gui_wps *gwps)
2132{ 588{
2133 char player_progressbar[7]; 589 char player_progressbar[7];
@@ -2150,7 +606,7 @@ static bool draw_player_progress(struct gui_wps *gwps)
2150 songpos = ((state->id3->elapsed - state->ff_rewind_count) * 36) / 606 songpos = ((state->id3->elapsed - state->ff_rewind_count) * 36) /
2151 state->id3->length; 607 state->id3->length;
2152 else 608 else
2153 songpos = ((state->id3->elapsed + state->ff_rewind_count) * 36) / 609 songpos = ((state->id3->elapsed + state->ff_rewind_count) * 36) /
2154 state->id3->length; 610 state->id3->length;
2155 } 611 }
2156 for (i=0; i < songpos; i++) 612 for (i=0; i < songpos; i++)
@@ -2215,10 +671,10 @@ static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
2215 songpos = 55; 671 songpos = 55;
2216 else { 672 else {
2217 if(state->wps_time_countup == false) 673 if(state->wps_time_countup == false)
2218 songpos = ((state->id3->elapsed - state->ff_rewind_count) * 55) / 674 songpos = ((state->id3->elapsed - state->ff_rewind_count) * 55) /
2219 state->id3->length; 675 state->id3->length;
2220 else 676 else
2221 songpos = ((state->id3->elapsed + state->ff_rewind_count) * 55) / 677 songpos = ((state->id3->elapsed + state->ff_rewind_count) * 55) /
2222 state->id3->length; 678 state->id3->length;
2223 } 679 }
2224 680
@@ -2304,342 +760,1190 @@ static void draw_player_fullbar(struct gui_wps *gwps, char* buf, int buf_size)
2304 *buf = '\0'; 760 *buf = '\0';
2305 } 761 }
2306} 762}
2307#endif
2308 763
2309/* set volume */ 764#endif /* HAVE_LCD_CHARCELL */
2310void setvol(void) 765
766/* Extract a part from a path.
767 *
768 * buf - buffer extract part to.
769 * buf_size - size of buffer.
770 * path - path to extract from.
771 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
772 * parent of parent, etc.
773 *
774 * Returns buf if the desired level was found, NULL otherwise.
775 */
776static char* get_dir(char* buf, int buf_size, const char* path, int level)
2311{ 777{
2312 if (global_settings.volume < sound_min(SOUND_VOLUME)) 778 const char* sep;
2313 global_settings.volume = sound_min(SOUND_VOLUME); 779 const char* last_sep;
2314 if (global_settings.volume > sound_max(SOUND_VOLUME)) 780 int len;
2315 global_settings.volume = sound_max(SOUND_VOLUME); 781
2316 sound_set_volume(global_settings.volume); 782 sep = path + strlen(path);
2317 settings_save(); 783 last_sep = sep;
784
785 while (sep > path)
786 {
787 if ('/' == *(--sep))
788 {
789 if (!level)
790 break;
791
792 level--;
793 last_sep = sep - 1;
794 }
795 }
796
797 if (level || (last_sep <= sep))
798 return NULL;
799
800 len = MIN(last_sep - sep, buf_size - 1);
801 strncpy(buf, sep + 1, len);
802 buf[len] = 0;
803 return buf;
2318} 804}
2319/* return true if screen restore is needed 805
2320 return false otherwise 806/* Return the tag found at index i and write its value in buf.
807 The return value is buf if the tag had a value, or NULL if not.
808
809 intval is used with enums: when this function is called, it should contain
810 the number of options in the enum. When this function returns, it will
811 contain the enum case we are actually in.
812 When not treating an enum, intval should be NULL.
2321*/ 813*/
2322bool update_onvol_change(struct gui_wps * gwps) 814static char *get_tag(struct gui_wps *gwps,
815 int i,
816 char *buf,
817 int buf_size,
818 int *intval)
2323{ 819{
2324 gui_wps_statusbar_draw(gwps, false); 820 if (!gwps)
2325 gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC); 821 return NULL;
2326 822
2327#ifdef HAVE_LCD_CHARCELLS 823 struct wps_data *data = gwps->data;
2328 gui_splash(gwps->display, 0, "Vol: %3d dB", 824 struct wps_state *state = gwps->state;
2329 sound_val2phys(SOUND_VOLUME, global_settings.volume));
2330 return true;
2331#endif
2332 return false;
2333}
2334 825
2335bool ffwd_rew(int button) 826 if (!data || !state)
2336{ 827 return NULL;
2337 static const int ff_rew_steps[] = {
2338 1000, 2000, 3000, 4000,
2339 5000, 6000, 8000, 10000,
2340 15000, 20000, 25000, 30000,
2341 45000, 60000
2342 };
2343 828
2344 unsigned int step = 0; /* current ff/rewind step */ 829 struct mp3entry *id3;
2345 unsigned int max_step = 0; /* maximum ff/rewind step */
2346 int ff_rewind_count = 0; /* current ff/rewind count (in ticks) */
2347 int direction = -1; /* forward=1 or backward=-1 */
2348 long accel_tick = 0; /* next time at which to bump the step size */
2349 bool exit = false;
2350 bool usb = false;
2351 int i = 0;
2352 830
2353 if (button == ACTION_NONE) 831 if (data->tokens[i].next)
2354 { 832 id3 = state->nid3;
2355 status_set_ffmode(0); 833 else
2356 return usb; 834 id3 = state->id3;
2357 } 835
2358 while (!exit) 836 if (!id3)
837 return NULL;
838
839 int limit = 1;
840 if (intval)
841 limit = *intval;
842
843#if CONFIG_RTC
844 static struct tm* tm;
845#endif
846
847 switch (data->tokens[i].type)
2359 { 848 {
2360 switch ( button ) 849 case WPS_TOKEN_CHARACTER:
2361 { 850 return &(data->tokens[i].value.c);
2362 case ACTION_WPS_SEEKFWD: 851
2363 direction = 1; 852 case WPS_TOKEN_STRING:
2364 case ACTION_WPS_SEEKBACK: 853 return data->strings[data->tokens[i].value.i];
2365 if (wps_state.ff_rewind) 854
2366 { 855 case WPS_TOKEN_TRACK_TIME_ELAPSED:
2367 if (direction == 1) 856 format_time(buf, buf_size,
2368 { 857 id3->elapsed + state->ff_rewind_count);
2369 /* fast forwarding, calc max step relative to end */ 858 return buf;
2370 max_step = (wps_state.id3->length - 859
2371 (wps_state.id3->elapsed + 860 case WPS_TOKEN_TRACK_TIME_REMAINING:
2372 ff_rewind_count)) * 861 format_time(buf, buf_size,
2373 FF_REWIND_MAX_PERCENT / 100; 862 id3->length - id3->elapsed -
2374 } 863 state->ff_rewind_count);
2375 else 864 return buf;
2376 { 865
2377 /* rewinding, calc max step relative to start */ 866 case WPS_TOKEN_TRACK_LENGTH:
2378 max_step = (wps_state.id3->elapsed + ff_rewind_count) * 867 format_time(buf, buf_size, id3->length);
2379 FF_REWIND_MAX_PERCENT / 100; 868 return buf;
2380 } 869
870 case WPS_TOKEN_PLAYLIST_ENTRIES:
871 snprintf(buf, buf_size, "%d", playlist_amount());
872 return buf;
873
874 case WPS_TOKEN_PLAYLIST_NAME:
875 return playlist_name(NULL, buf, buf_size);
876
877 case WPS_TOKEN_PLAYLIST_POSITION:
878 snprintf(buf, buf_size, "%d",
879 playlist_get_display_index());
880 return buf;
881
882 case WPS_TOKEN_PLAYLIST_SHUFFLE:
883 if ( global_settings.playlist_shuffle )
884 return "s";
885 else
886 return NULL;
887 break;
2381 888
2382 max_step = MAX(max_step, MIN_FF_REWIND_STEP); 889 case WPS_TOKEN_VOLUME:
890 snprintf(buf, buf_size, "%d", global_settings.volume);
891 if (intval)
892 {
893 *intval = limit * (global_settings.volume
894 - sound_min(SOUND_VOLUME))
895 / (sound_max(SOUND_VOLUME)
896 - sound_min(SOUND_VOLUME)) + 1;
897 }
898 return buf;
2383 899
2384 if (step > max_step) 900 case WPS_TOKEN_METADATA_ARTIST:
2385 step = max_step; 901 return id3->artist;
2386 902
2387 ff_rewind_count += step * direction; 903 case WPS_TOKEN_METADATA_COMPOSER:
904 return id3->composer;
2388 905
2389 if (global_settings.ff_rewind_accel != 0 && 906 case WPS_TOKEN_METADATA_ALBUM:
2390 current_tick >= accel_tick) 907 return id3->album;
2391 {
2392 step *= 2;
2393 accel_tick = current_tick +
2394 global_settings.ff_rewind_accel*HZ;
2395 }
2396 }
2397 else
2398 {
2399 if ( (audio_status() & AUDIO_STATUS_PLAY) &&
2400 wps_state.id3 && wps_state.id3->length )
2401 {
2402 if (!wps_state.paused)
2403#if (CONFIG_CODEC == SWCODEC)
2404 audio_pre_ff_rewind();
2405#else
2406 audio_pause();
2407#endif
2408#if CONFIG_KEYPAD == PLAYER_PAD
2409 FOR_NB_SCREENS(i)
2410 gui_wps[i].display->stop_scroll();
2411#endif
2412 if (direction > 0)
2413 status_set_ffmode(STATUS_FASTFORWARD);
2414 else
2415 status_set_ffmode(STATUS_FASTBACKWARD);
2416 908
2417 wps_state.ff_rewind = true; 909 case WPS_TOKEN_METADATA_ALBUM_ARTIST:
910 return id3->albumartist;
2418 911
2419 step = ff_rew_steps[global_settings.ff_rewind_min_step]; 912 case WPS_TOKEN_METADATA_GENRE:
913 return id3->genre_string;
2420 914
2421 accel_tick = current_tick + 915 case WPS_TOKEN_METADATA_TRACK_NUMBER:
2422 global_settings.ff_rewind_accel*HZ; 916 if (id3->track_string)
2423 } 917 return id3->track_string;
2424 else
2425 break;
2426 }
2427 918
2428 if (direction > 0) { 919 if (id3->tracknum) {
2429 if ((wps_state.id3->elapsed + ff_rewind_count) > 920 snprintf(buf, buf_size, "%d", id3->tracknum);
2430 wps_state.id3->length) 921 return buf;
2431 ff_rewind_count = wps_state.id3->length - 922 }
2432 wps_state.id3->elapsed; 923 return NULL;
924
925 case WPS_TOKEN_METADATA_TRACK_TITLE:
926 return id3->title;
927
928 case WPS_TOKEN_METADATA_VERSION:
929 switch (id3->id3version)
930 {
931 case ID3_VER_1_0:
932 return "1";
933
934 case ID3_VER_1_1:
935 return "1.1";
936
937 case ID3_VER_2_2:
938 return "2.2";
939
940 case ID3_VER_2_3:
941 return "2.3";
942
943 case ID3_VER_2_4:
944 return "2.4";
945
946 default:
947 return NULL;
948 }
949
950 case WPS_TOKEN_METADATA_YEAR:
951 if( id3->year_string )
952 return id3->year_string;
953
954 if (id3->year) {
955 snprintf(buf, buf_size, "%d", id3->year);
956 return buf;
957 }
958 return NULL;
959
960 case WPS_TOKEN_METADATA_COMMENT:
961 return id3->comment;
962
963 case WPS_TOKEN_FILE_BITRATE:
964 if(id3->bitrate)
965 snprintf(buf, buf_size, "%d", id3->bitrate);
966 else
967 snprintf(buf, buf_size, "?");
968 return buf;
969
970 case WPS_TOKEN_FILE_CODEC:
971 if (intval)
972 {
973 if(id3->codectype == AFMT_UNKNOWN)
974 *intval = AFMT_NUM_CODECS;
975 else
976 *intval = id3->codectype;
977 }
978 return id3_get_codec(id3);
979
980 case WPS_TOKEN_FILE_FREQUENCY:
981 snprintf(buf, buf_size, "%ld", id3->frequency);
982 return buf;
983
984 case WPS_TOKEN_FILE_NAME:
985 if (get_dir(buf, buf_size, id3->path, 0)) {
986 /* Remove extension */
987 char* sep = strrchr(buf, '.');
988 if (NULL != sep) {
989 *sep = 0;
2433 } 990 }
2434 else { 991 return buf;
2435 if ((int)(wps_state.id3->elapsed + ff_rewind_count) < 0) 992 }
2436 ff_rewind_count = -wps_state.id3->elapsed; 993 else {
994 return NULL;
995 }
996
997 case WPS_TOKEN_FILE_NAME_WITH_EXTENSION:
998 return get_dir(buf, buf_size, id3->path, 0);
999
1000 case WPS_TOKEN_FILE_PATH:
1001 return id3->path;
1002
1003 case WPS_TOKEN_FILE_SIZE:
1004 snprintf(buf, buf_size, "%ld", id3->filesize / 1024);
1005 return buf;
1006
1007 case WPS_TOKEN_FILE_VBR:
1008 return id3->vbr ? "(avg)" : NULL;
1009
1010 case WPS_TOKEN_FILE_DIRECTORY:
1011 return get_dir(buf, buf_size, id3->path, data->tokens[i].value.i);
1012
1013 case WPS_TOKEN_BATTERY_PERCENT:
1014 {
1015 int l = battery_level();
1016
1017 if (intval)
1018 {
1019 limit = MAX(limit, 2);
1020 if (l > -1) {
1021 /* First enum is used for "unknown level". */
1022 *intval = (limit - 1) * l / 100 + 2;
1023 } else {
1024 *intval = 1;
2437 } 1025 }
1026 }
2438 1027
2439 FOR_NB_SCREENS(i) 1028 if (l > -1) {
2440 gui_wps_refresh(&gui_wps[i], 1029 snprintf(buf, buf_size, "%d", l);
2441 (wps_state.wps_time_countup == false)? 1030 return buf;
2442 ff_rewind_count:-ff_rewind_count, 1031 } else {
2443 WPS_REFRESH_PLAYER_PROGRESS | 1032 return "?";
2444 WPS_REFRESH_DYNAMIC); 1033 }
1034 }
2445 1035
2446 break; 1036 case WPS_TOKEN_BATTERY_VOLTS:
1037 {
1038 unsigned int v = battery_voltage();
1039 snprintf(buf, buf_size, "%d.%02d", v/100, v%100);
1040 return buf;
1041 }
2447 1042
2448 case ACTION_WPS_STOPSEEK: 1043 case WPS_TOKEN_BATTERY_TIME:
2449 wps_state.id3->elapsed = wps_state.id3->elapsed+ff_rewind_count; 1044 {
2450 audio_ff_rewind(wps_state.id3->elapsed); 1045 int t = battery_time();
2451 ff_rewind_count = 0; 1046 if (t >= 0)
2452 wps_state.ff_rewind = false; 1047 snprintf(buf, buf_size, "%dh %dm", t / 60, t % 60);
2453 status_set_ffmode(0); 1048 else
2454#if (CONFIG_CODEC != SWCODEC) 1049 strncpy(buf, "?h ?m", buf_size);
2455 if (!wps_state.paused) 1050 return buf;
2456 audio_resume(); 1051 }
1052
1053#if CONFIG_CHARGING
1054 case WPS_TOKEN_BATTERY_CHARGER_CONNECTED:
1055 {
1056 if(charger_input_state==CHARGER)
1057 return "p";
1058 else
1059 return NULL;
1060 }
1061#endif
1062#if CONFIG_CHARGING >= CHARGING_MONITOR
1063 case WPS_TOKEN_BATTERY_CHARGING:
1064 {
1065 if (charge_state == CHARGING || charge_state == TOPOFF) {
1066 return "c";
1067 } else {
1068 return NULL;
1069 }
1070 }
1071#endif
1072
1073 case WPS_TOKEN_PLAYBACK_STATUS:
1074 {
1075 int status = audio_status();
1076 int mode = 1;
1077 if (status == AUDIO_STATUS_PLAY && \
1078 !(status & AUDIO_STATUS_PAUSE))
1079 mode = 2;
1080 if (audio_status() & AUDIO_STATUS_PAUSE && \
1081 (! status_get_ffmode()))
1082 mode = 3;
1083 if (status_get_ffmode() == STATUS_FASTFORWARD)
1084 mode = 4;
1085 if (status_get_ffmode() == STATUS_FASTBACKWARD)
1086 mode = 5;
1087
1088 if (intval) {
1089 *intval = mode;
1090 }
1091
1092 snprintf(buf, buf_size, "%d", mode);
1093 return buf;
1094 }
1095
1096 case WPS_TOKEN_REPEAT_MODE:
1097 if (intval)
1098 *intval = global_settings.repeat_mode + 1;
1099 snprintf(buf, buf_size, "%d", *intval);
1100 return buf;
1101
1102#if CONFIG_RTC
1103 case WPS_TOKEN_RTC:
1104 tm = get_time();
1105 return NULL;
1106
1107 case WPS_TOKEN_RTC_DAY_OF_MONTH:
1108 /* d: day of month (01..31) */
1109 if (tm->tm_mday > 31 || tm->tm_mday < 1) return NULL;
1110 snprintf(buf, buf_size, "%02d", tm->tm_mday);
1111 return buf;
1112
1113 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED:
1114 /* e: day of month, blank padded ( 1..31) */
1115 if (tm->tm_mday > 31 || tm->tm_mday < 1) return NULL;
1116 snprintf(buf, buf_size, "%2d", tm->tm_mday);
1117 return buf;
1118
1119 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED:
1120 /* H: hour (00..23) */
1121 if (tm->tm_hour > 23) return NULL;
1122 snprintf(buf, buf_size, "%02d", tm->tm_hour);
1123 return buf;
1124
1125 case WPS_TOKEN_RTC_HOUR_24:
1126 /* k: hour ( 0..23) */
1127 if (tm->tm_hour > 23) return NULL;
1128 snprintf(buf, buf_size, "%2d", tm->tm_hour);
1129 return buf;
1130
1131 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED:
1132 /* I: hour (01..12) */
1133 if (tm->tm_hour > 23) return NULL;
1134 snprintf(buf, buf_size, "%02d",
1135 (tm->tm_hour % 12 == 0) ? 12 : tm->tm_hour % 12);
1136 return buf;
1137
1138 case WPS_TOKEN_RTC_HOUR_12:
1139 /* l: hour ( 1..12) */
1140 if (tm->tm_hour > 23) return NULL;
1141 snprintf(buf, buf_size, "%2d",
1142 (tm->tm_hour % 12 == 0) ? 12 : tm->tm_hour % 12);
1143 return buf;
1144
1145 case WPS_TOKEN_RTC_MONTH:
1146 /* m: month (01..12) */
1147 if (tm->tm_mon > 11 || tm->tm_mon < 0) return NULL;
1148 snprintf(buf, buf_size, "%02d", tm->tm_mon + 1);
1149 return buf;
1150
1151 case WPS_TOKEN_RTC_MINUTE:
1152 /* M: minute (00..59) */
1153 if (tm->tm_min > 59 || tm->tm_min < 0) return NULL;
1154 snprintf(buf, buf_size, "%02d", tm->tm_min);
1155 return buf;
1156
1157 case WPS_TOKEN_RTC_SECOND:
1158 /* S: second (00..59) */
1159 if (tm->tm_sec > 59 || tm->tm_sec < 0) return NULL;
1160 snprintf(buf, buf_size, "%02d", tm->tm_sec);
1161 return buf;
1162
1163 case WPS_TOKEN_RTC_YEAR_2_DIGITS:
1164 /* y: last two digits of year (00..99) */
1165 snprintf(buf, buf_size, "%02d", tm->tm_year % 100);
1166 return buf;
1167
1168 case WPS_TOKEN_RTC_YEAR_4_DIGITS:
1169 /* Y: year (1970...) */
1170 if (tm->tm_year > 199 || tm->tm_year < 100) return NULL;
1171 snprintf(buf, buf_size, "%04d", tm->tm_year + 1900);
1172 return buf;
1173
1174 case WPS_TOKEN_RTC_AM_PM_UPPER:
1175 /* p: upper case AM or PM indicator */
1176 snprintf(buf, buf_size, (tm->tm_hour/12 == 0) ? "AM" : "PM");
1177 return buf;
1178
1179 case WPS_TOKEN_RTC_AM_PM_LOWER:
1180 /* P: lower case am or pm indicator */
1181 snprintf(buf, buf_size, (tm->tm_hour/12 == 0) ? "am" : "pm");
1182 return buf;
1183
1184 case WPS_TOKEN_RTC_WEEKDAY_NAME:
1185 /* a: abbreviated weekday name (Sun..Sat) */
1186 if (tm->tm_wday > 6 || tm->tm_wday < 0) return NULL;
1187 snprintf(buf, buf_size, "%s",str(dayname[tm->tm_wday]));
1188 return buf;
1189
1190 case WPS_TOKEN_RTC_MONTH_NAME:
1191 /* b: abbreviated month name (Jan..Dec) */
1192 if (tm->tm_mon > 11 || tm->tm_mon < 0) return NULL;
1193 snprintf(buf, buf_size, "%s",str(monthname[tm->tm_mon]));
1194 return buf;
1195
1196 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON:
1197 /* u: day of week (1..7); 1 is Monday */
1198 if (tm->tm_wday > 6 || tm->tm_wday < 0) return NULL;
1199 snprintf(buf, buf_size, "%1d", tm->tm_wday + 1);
1200 return buf;
1201
1202 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN:
1203 /* w: day of week (0..6); 0 is Sunday */
1204 if (tm->tm_wday > 6 || tm->tm_wday < 0) return NULL;
1205 snprintf(buf, buf_size, "%1d", tm->tm_wday);
1206 return buf;
2457#endif 1207#endif
1208
2458#ifdef HAVE_LCD_CHARCELLS 1209#ifdef HAVE_LCD_CHARCELLS
2459 gui_wps_display(); 1210 case WPS_TOKEN_PROGRESSBAR:
1211 {
1212 char *end = utf8encode(data->wps_progress_pat[0], buf);
1213 *end = '\0';
1214 return buf;
1215 }
1216
1217 case WPS_TOKEN_PLAYER_PROGRESSBAR:
1218 if(is_new_player())
1219 {
1220 /* we need 11 characters (full line) for
1221 progress-bar */
1222 snprintf(buf, buf_size, " ");
1223 }
1224 else
1225 {
1226 /* Tell the user if we have an OldPlayer */
1227 snprintf(buf, buf_size, " <Old LCD> ");
1228 }
1229 return buf;
2460#endif 1230#endif
2461 exit = true;
2462 break;
2463 1231
2464 default: 1232 case WPS_TOKEN_DATABASE_PLAYCOUNT:
2465 if(default_event_handler(button) == SYS_USB_CONNECTED) { 1233 if (intval) {
2466 status_set_ffmode(0); 1234 *intval = id3->playcount + 1;
2467 usb = true; 1235 }
2468 exit = true; 1236 snprintf(buf, buf_size, "%ld", id3->playcount);
2469 } 1237 return buf;
2470 break; 1238
1239 case WPS_TOKEN_DATABASE_RATING:
1240 if (intval) {
1241 *intval = id3->rating + 1;
1242 }
1243 snprintf(buf, buf_size, "%d", id3->rating);
1244 return buf;
1245
1246#if (CONFIG_CODEC == SWCODEC)
1247 case WPS_TOKEN_REPLAYGAIN:
1248 {
1249 int val;
1250
1251 if (global_settings.replaygain == 0)
1252 val = 1; /* off */
1253 else
1254 {
1255 int type =
1256 get_replaygain_mode(id3->track_gain_string != NULL,
1257 id3->album_gain_string != NULL);
1258 if (type < 0)
1259 val = 6; /* no tag */
1260 else
1261 val = type + 2;
1262
1263 if (global_settings.replaygain_type == REPLAYGAIN_SHUFFLE)
1264 val += 2;
1265 }
1266
1267 if (intval)
1268 *intval = val;
1269
1270 switch (val)
1271 {
1272 case 1:
1273 case 6:
1274 return "+0.00 dB";
1275 break;
1276 case 2:
1277 case 4:
1278 strncpy(buf, id3->track_gain_string, buf_size);
1279 break;
1280 case 3:
1281 case 5:
1282 strncpy(buf, id3->album_gain_string, buf_size);
1283 break;
1284 }
1285 return buf;
2471 } 1286 }
2472 if (!exit) 1287
2473 button = get_action(CONTEXT_WPS,TIMEOUT_BLOCK); 1288 case WPS_TOKEN_SOUND_PITCH:
1289 snprintf(buf, buf_size, "%d.%d",
1290 *intval / 10, *intval % 10);
1291 return buf;
1292
1293#endif
1294
1295#ifdef HAS_BUTTON_HOLD
1296 case WPS_TOKEN_MAIN_HOLD:
1297 if (button_hold())
1298 return "h";
1299 else
1300 return NULL;
1301#endif
1302#ifdef HAS_REMOTE_BUTTON_HOLD
1303 case WPS_TOKEN_REMOTE_HOLD:
1304 if (remote_button_hold())
1305 return "r";
1306 else
1307 return NULL;
1308#endif
1309
1310#if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
1311 case WPS_TOKEN_VLED_HDD:
1312 if(led_read(HZ/2))
1313 return "h";
1314 else
1315 return NULL;
1316#endif
1317
1318 default:
1319 return NULL;
2474 } 1320 }
2475 action_signalscreenchange();
2476 return usb;
2477} 1321}
2478 1322
2479bool gui_wps_display(void) 1323/* Return the index to the end token for the conditional token at index.
1324 The conditional token can be either a start token or a separator
1325 (i.e. option) token.
1326*/
1327static int find_conditional_end(struct wps_data *data, int index)
2480{ 1328{
2481 int i; 1329 int type = data->tokens[index].type;
2482 if (!wps_state.id3 && !(audio_status() & AUDIO_STATUS_PLAY)) 1330
1331 if (type != WPS_TOKEN_CONDITIONAL_START
1332 && type != WPS_TOKEN_CONDITIONAL_OPTION)
2483 { 1333 {
2484 global_status.resume_index = -1; 1334 /* this function should only be used with "index" pointing to a
2485#ifdef HAVE_LCD_CHARCELLS 1335 WPS_TOKEN_CONDITIONAL_START or a WPS_TOKEN_CONDITIONAL_OPTION */
2486 gui_syncsplash(HZ, str(LANG_END_PLAYLIST_PLAYER)); 1336 return index + 1;
2487#else
2488 gui_syncstatusbar_draw(&statusbars, true);
2489 gui_syncsplash(HZ, str(LANG_END_PLAYLIST_RECORDER));
2490#endif
2491 return true;
2492 } 1337 }
2493 else 1338
1339 int ret = index;
1340 do
1341 ret = data->tokens[ret].value.i;
1342 while (data->tokens[ret].type != WPS_TOKEN_CONDITIONAL_END);
1343
1344 /* ret now is the index to the end token for the conditional. */
1345 return ret;
1346}
1347
1348/* Return the index of the appropriate case for the conditional
1349 that starts at cond_index.
1350*/
1351static int evaluate_conditional(struct gui_wps *gwps, int cond_index)
1352{
1353 if (!gwps)
1354 return 0;
1355
1356 struct wps_data *data = gwps->data;
1357
1358 int ret;
1359 int num_options = data->tokens[cond_index].value.i;
1360 char result[128], *value;
1361 int cond_start = cond_index;
1362
1363 /* find the index of the conditional start token */
1364 while (data->tokens[cond_start].type != WPS_TOKEN_CONDITIONAL_START
1365 && cond_start < data->num_tokens)
1366 cond_start++;
1367
1368 if (num_options > 2) /* enum */
2494 { 1369 {
2495 FOR_NB_SCREENS(i) 1370 int intval = num_options;
1371 /* get_tag needs to know the number of options in the enum */
1372 get_tag(gwps, cond_index + 1, result, sizeof(result), &intval);
1373 /* intval is now the number of the enum option we want to read,
1374 starting from 1 */
1375 if (intval > num_options || intval < 1)
1376 intval = num_options;
1377
1378 int next = cond_start;
1379 int i;
1380 for (i = 1; i < intval; i++)
2496 { 1381 {
2497 gui_wps[i].display->clear_display(); 1382 next = data->tokens[next].value.i;
2498 if (!gui_wps[i].data->wps_loaded) { 1383 }
2499 if ( !gui_wps[i].data->format_buffer[0] ) { 1384 ret = next;
2500 /* set the default wps for the main-screen */ 1385 }
2501 if(i == 0) 1386 else /* %?xx<true|false> or %?<true> */
2502 { 1387 {
1388 value = get_tag(gwps, cond_index + 1, result, sizeof(result), NULL);
1389 ret = value ? cond_start : data->tokens[cond_start].value.i;
1390 }
1391
2503#ifdef HAVE_LCD_BITMAP 1392#ifdef HAVE_LCD_BITMAP
2504#if LCD_DEPTH > 1 1393 /* clear all pictures in the conditional */
2505 unload_wps_backdrop(); 1394 int i;
2506#endif 1395 for (i=0; i < MAX_IMAGES; i++)
2507 wps_data_load(gui_wps[i].data, 1396 {
2508 "%s%?it<%?in<%in. |>%it|%fn>\n" 1397 if (data->img[i].cond_index == cond_index)
2509 "%s%?ia<%ia|%?d2<%d2|(root)>>\n" 1398 clear_image_pos(gwps, i);
2510 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n" 1399 }
2511 "\n"
2512 "%al%pc/%pt%ar[%pp:%pe]\n"
2513 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
2514 "%pb\n"
2515 "%pm\n", false);
2516#else
2517 wps_data_load(gui_wps[i].data,
2518 "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
2519 "%pc%?ps<*|/>%pt\n", false);
2520#endif 1400#endif
2521 } 1401
2522#if NB_SCREENS == 2 1402 return ret;
2523 /* set the default wps for the remote-screen */ 1403}
2524 else if(i == 1) 1404
2525 { 1405/* Read a (sub)line to the given alignment format buffer.
2526 wps_data_load(gui_wps[i].data, 1406 linebuf is the buffer where the data is actually stored.
2527 "%s%?ia<%ia|%?d2<%d2|(root)>>\n" 1407 align is the alignment format that'll be used to display the text.
2528 "%s%?it<%?in<%in. |>%it|%fn>\n" 1408 The return value indicates whether the line needs to be updated.
2529 "%al%pc/%pt%ar[%pp:%pe]\n" 1409*/
2530 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n" 1410static bool get_line(struct gui_wps *gwps,
2531 "%pb", false); 1411 int line, int subline,
2532 } 1412 struct align_pos *align,
1413 char *linebuf,
1414 int linebuf_size)
1415{
1416 struct wps_data *data = gwps->data;
1417
1418 char temp_buf[128];
1419 char *buf = linebuf; /* will always point to the writing position */
1420 char *linebuf_end = linebuf + linebuf_size - 1;
1421 bool update = false;
1422
1423 /* alignment-related variables */
1424 int cur_align;
1425 char* cur_align_start;
1426 cur_align_start = buf;
1427 cur_align = WPS_ALIGN_LEFT;
1428 align->left = 0;
1429 align->center = 0;
1430 align->right = 0;
1431
1432 /* start at the beginning of the current (sub)line */
1433 int i = data->format_lines[line][subline];
1434
1435 while (data->tokens[i].type != WPS_TOKEN_EOL
1436 && data->tokens[i].type != WPS_TOKEN_SUBLINE_SEPARATOR
1437 && i < data->num_tokens)
1438 {
1439 switch(data->tokens[i].type)
1440 {
1441 case WPS_TOKEN_CONDITIONAL:
1442 /* place ourselves in the right conditional case */
1443 i = evaluate_conditional(gwps, i);
1444 break;
1445
1446 case WPS_TOKEN_CONDITIONAL_OPTION:
1447 /* we've finished in the curent conditional case,
1448 skip to the end of the conditional structure */
1449 i = find_conditional_end(data, i);
1450 break;
1451
1452#ifdef HAVE_LCD_BITMAP
1453 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY:
1454 {
1455 struct gui_img *img = data->img;
1456 int n = data->tokens[i].value.i;
1457 if (n >= 0 && n < MAX_IMAGES && img[n].loaded)
1458 img[n].display = true;
1459 break;
1460 }
2533#endif 1461#endif
1462
1463 case WPS_TOKEN_ALIGN_LEFT:
1464 case WPS_TOKEN_ALIGN_CENTER:
1465 case WPS_TOKEN_ALIGN_RIGHT:
1466 /* remember where the current aligned text started */
1467 switch (cur_align)
1468 {
1469 case WPS_ALIGN_LEFT:
1470 align->left = cur_align_start;
1471 break;
1472
1473 case WPS_ALIGN_CENTER:
1474 align->center = cur_align_start;
1475 break;
1476
1477 case WPS_ALIGN_RIGHT:
1478 align->right = cur_align_start;
1479 break;
2534 } 1480 }
1481 /* start a new alignment */
1482 switch (data->tokens[i].type)
1483 {
1484 case WPS_TOKEN_ALIGN_LEFT:
1485 cur_align = WPS_ALIGN_LEFT;
1486 break;
1487 case WPS_TOKEN_ALIGN_CENTER:
1488 cur_align = WPS_ALIGN_CENTER;
1489 break;
1490 case WPS_TOKEN_ALIGN_RIGHT:
1491 cur_align = WPS_ALIGN_RIGHT;
1492 break;
1493 default:
1494 break;
1495 }
1496 *buf++ = 0;
1497 cur_align_start = buf;
1498 break;
1499
1500 default:
1501 {
1502 /* get the value of the tag and copy it to the buffer */
1503 char *value = get_tag(gwps, i, temp_buf,
1504 sizeof(temp_buf), NULL);
1505 if (value)
1506 {
1507 update = true;
1508 while (*value && (buf < linebuf_end))
1509 *buf++ = *value++;
1510 }
1511 break;
2535 } 1512 }
2536 } 1513 }
1514 i++;
2537 } 1515 }
2538 yield(); 1516
2539 FOR_NB_SCREENS(i) 1517 /* close the current alignment */
1518 switch (cur_align)
2540 { 1519 {
2541 gui_wps_refresh(&gui_wps[i], 0, WPS_REFRESH_ALL); 1520 case WPS_ALIGN_LEFT:
1521 align->left = cur_align_start;
1522 break;
1523
1524 case WPS_ALIGN_CENTER:
1525 align->center = cur_align_start;
1526 break;
1527
1528 case WPS_ALIGN_RIGHT:
1529 align->right = cur_align_start;
1530 break;
2542 } 1531 }
2543 return false; 1532
1533 return update;
2544} 1534}
2545 1535
2546bool update(struct gui_wps *gwps) 1536/* Calculate which subline should be displayed for each line */
1537static bool get_curr_subline(struct wps_data *data, int line)
2547{ 1538{
2548 bool track_changed = audio_has_changed_track(); 1539 int search, search_start;
2549 bool retcode = false; 1540 bool reset_subline;
1541 bool new_subline_refresh;
1542 bool only_one_subline;
2550 1543
2551 gwps->state->nid3 = audio_next_track(); 1544 reset_subline = (data->curr_subline[line] == SUBLINE_RESET);
2552 if (track_changed) 1545 new_subline_refresh = false;
1546 only_one_subline = false;
1547
1548 /* if time to advance to next sub-line */
1549 if (TIME_AFTER(current_tick, data->subline_expire_time[line] - 1) ||
1550 reset_subline)
2553 { 1551 {
2554 gwps->display->stop_scroll(); 1552 /* search all sublines until the next subline with time > 0
2555 gwps->state->id3 = audio_current_track(); 1553 is found or we get back to the subline we started with */
1554 if (reset_subline)
1555 search_start = 0;
1556 else
1557 search_start = data->curr_subline[line];
2556 1558
2557 if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type 1559 for (search = 0; search < WPS_MAX_SUBLINES; search++)
2558 && strcmp(gwps->state->id3->path, curr_cue->audio_filename))
2559 { 1560 {
2560 /* the current cuesheet isn't the right one any more */ 1561 data->curr_subline[line]++;
2561 1562
2562 if (!strcmp(gwps->state->id3->path, temp_cue->audio_filename)) { 1563 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
2563 /* We have the new cuesheet in memory (temp_cue), 1564 if ((!data->format_lines[line][data->curr_subline[line]]) ||
2564 let's make it the current one ! */ 1565 (data->curr_subline[line] == WPS_MAX_SUBLINES))
2565 memcpy(curr_cue, temp_cue, sizeof(struct cuesheet)); 1566 {
1567 if (data->curr_subline[line] == 1)
1568 only_one_subline = true;
1569 data->curr_subline[line] = 0;
2566 } 1570 }
2567 else {
2568 /* We need to parse the new cuesheet */
2569 1571
2570 char cuepath[MAX_PATH]; 1572 /* if back where we started after search or
2571 strncpy(cuepath, gwps->state->id3->path, MAX_PATH); 1573 only one subline is defined on the line */
2572 char *dot = strrchr(cuepath, '.'); 1574 if (((search > 0) && (data->curr_subline[line] == search_start)) ||
2573 strcpy(dot, ".cue"); 1575 only_one_subline)
2574 1576 {
2575 if (parse_cuesheet(cuepath, curr_cue)) 1577 /* no other subline with a time > 0 exists */
1578 data->subline_expire_time[line] = (reset_subline?
1579 current_tick : data->subline_expire_time[line]) + 100 * HZ;
1580 break;
1581 }
1582 else
1583 {
1584 /* only use this subline if subline time > 0 */
1585 if (data->time_mult[line][data->curr_subline[line]] > 0)
2576 { 1586 {
2577 gwps->state->id3->cuesheet_type = 1; 1587 new_subline_refresh = true;
2578 strcpy(curr_cue->audio_filename, gwps->state->id3->path); 1588 data->subline_expire_time[line] = (reset_subline ?
1589 current_tick : data->subline_expire_time[line]) +
1590 BASE_SUBLINE_TIME * data->time_mult[line][data->curr_subline[line]];
1591 break;
2579 } 1592 }
2580 } 1593 }
1594 }
1595 }
2581 1596
2582 cue_spoof_id3(curr_cue, gwps->state->id3); 1597 return new_subline_refresh;
1598}
1599
1600/* Display a line appropriately according to its alignment format.
1601 format_align contains the text, separated between left, center and right.
1602 line is the index of the line on the screen.
1603 scroll indicates whether the line is a scrolling one or not.
1604*/
1605static void write_line(struct screen *display,
1606 struct align_pos *format_align,
1607 int line,
1608 bool scroll)
1609{
1610
1611 int left_width, left_xpos;
1612 int center_width, center_xpos;
1613 int right_width, right_xpos;
1614 int ypos;
1615 int space_width;
1616 int string_height;
1617
1618 /* calculate different string sizes and positions */
1619 display->getstringsize((unsigned char *)" ", &space_width, &string_height);
1620 if (format_align->left != 0) {
1621 display->getstringsize((unsigned char *)format_align->left,
1622 &left_width, &string_height);
1623 }
1624 else {
1625 left_width = 0;
1626 }
1627 left_xpos = 0;
1628
1629 if (format_align->center != 0) {
1630 display->getstringsize((unsigned char *)format_align->center,
1631 &center_width, &string_height);
1632 }
1633 else {
1634 center_width = 0;
1635 }
1636 center_xpos=(display->width - center_width) / 2;
1637
1638 if (format_align->right != 0) {
1639 display->getstringsize((unsigned char *)format_align->right,
1640 &right_width, &string_height);
1641 }
1642 else {
1643 right_width = 0;
1644 }
1645 right_xpos = (display->width - right_width);
1646
1647 /* Checks for overlapping strings.
1648 If needed the overlapping strings will be merged, separated by a
1649 space */
1650
1651 /* CASE 1: left and centered string overlap */
1652 /* there is a left string, need to merge left and center */
1653 if ((left_width != 0 && center_width != 0) &&
1654 (left_xpos + left_width + space_width > center_xpos)) {
1655 /* replace the former separator '\0' of left and
1656 center string with a space */
1657 *(--format_align->center) = ' ';
1658 /* calculate the new width and position of the merged string */
1659 left_width = left_width + space_width + center_width;
1660 left_xpos = 0;
1661 /* there is no centered string anymore */
1662 center_width = 0;
1663 }
1664 /* there is no left string, move center to left */
1665 if ((left_width == 0 && center_width != 0) &&
1666 (left_xpos + left_width > center_xpos)) {
1667 /* move the center string to the left string */
1668 format_align->left = format_align->center;
1669 /* calculate the new width and position of the string */
1670 left_width = center_width;
1671 left_xpos = 0;
1672 /* there is no centered string anymore */
1673 center_width = 0;
1674 }
1675
1676 /* CASE 2: centered and right string overlap */
1677 /* there is a right string, need to merge center and right */
1678 if ((center_width != 0 && right_width != 0) &&
1679 (center_xpos + center_width + space_width > right_xpos)) {
1680 /* replace the former separator '\0' of center and
1681 right string with a space */
1682 *(--format_align->right) = ' ';
1683 /* move the center string to the right after merge */
1684 format_align->right = format_align->center;
1685 /* calculate the new width and position of the merged string */
1686 right_width = center_width + space_width + right_width;
1687 right_xpos = (display->width - right_width);
1688 /* there is no centered string anymore */
1689 center_width = 0;
1690 }
1691 /* there is no right string, move center to right */
1692 if ((center_width != 0 && right_width == 0) &&
1693 (center_xpos + center_width > right_xpos)) {
1694 /* move the center string to the right string */
1695 format_align->right = format_align->center;
1696 /* calculate the new width and position of the string */
1697 right_width = center_width;
1698 right_xpos = (display->width - right_width);
1699 /* there is no centered string anymore */
1700 center_width = 0;
1701 }
1702
1703 /* CASE 3: left and right overlap
1704 There is no center string anymore, either there never
1705 was one or it has been merged in case 1 or 2 */
1706 /* there is a left string, need to merge left and right */
1707 if ((left_width != 0 && center_width == 0 && right_width != 0) &&
1708 (left_xpos + left_width + space_width > right_xpos)) {
1709 /* replace the former separator '\0' of left and
1710 right string with a space */
1711 *(--format_align->right) = ' ';
1712 /* calculate the new width and position of the string */
1713 left_width = left_width + space_width + right_width;
1714 left_xpos = 0;
1715 /* there is no right string anymore */
1716 right_width = 0;
1717 }
1718 /* there is no left string, move right to left */
1719 if ((left_width == 0 && center_width == 0 && right_width != 0) &&
1720 (left_xpos + left_width > right_xpos)) {
1721 /* move the right string to the left string */
1722 format_align->left = format_align->right;
1723 /* calculate the new width and position of the string */
1724 left_width = right_width;
1725 left_xpos = 0;
1726 /* there is no right string anymore */
1727 right_width = 0;
1728 }
1729
1730 ypos = (line * string_height) + display->getymargin();
1731
1732
1733 if (scroll && left_width > display->width)
1734 {
1735 display->puts_scroll(0, line,
1736 (unsigned char *)format_align->left);
1737 }
1738 else
1739 {
1740#ifdef HAVE_LCD_BITMAP
1741 /* clear the line first */
1742 display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
1743 display->fillrect(0, ypos, display->width, string_height);
1744 display->set_drawmode(DRMODE_SOLID);
1745#endif
1746
1747 /* Nasty hack: we output an empty scrolling string,
1748 which will reset the scroller for that line */
1749 display->puts_scroll(0, line, (unsigned char *)"");
1750
1751 /* print aligned strings */
1752 if (left_width != 0)
1753 {
1754 display->putsxy(left_xpos, ypos,
1755 (unsigned char *)format_align->left);
2583 } 1756 }
1757 if (center_width != 0)
1758 {
1759 display->putsxy(center_xpos, ypos,
1760 (unsigned char *)format_align->center);
1761 }
1762 if (right_width != 0)
1763 {
1764 display->putsxy(right_xpos, ypos,
1765 (unsigned char *)format_align->right);
1766 }
1767 }
1768}
2584 1769
2585 if (gui_wps_display()) 1770/* Refresh the WPS according to refresh_mode. */
2586 retcode = true; 1771bool gui_wps_refresh(struct gui_wps *gwps,
2587 else{ 1772 int ffwd_offset,
2588 gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL); 1773 unsigned char refresh_mode)
1774{
1775 struct wps_data *data = gwps->data;
1776 struct screen *display = gwps->display;
1777 struct wps_state *state = gwps->state;
1778
1779 if(!gwps || !data || !state || !display)
1780 return false;
1781
1782 int line, i;
1783 unsigned char flags;
1784 char linebuf[MAX_PATH];
1785
1786 struct align_pos align;
1787 align.left = NULL;
1788 align.center = NULL;
1789 align.right = NULL;
1790
1791 bool update_line, new_subline_refresh;
1792
1793#ifdef HAVE_LCD_BITMAP
1794 gui_wps_statusbar_draw(gwps, true);
1795
1796 /* to find out wether the peak meter is enabled we
1797 assume it wasn't until we find a line that contains
1798 the peak meter. We can't use peak_meter_enabled itself
1799 because that would mean to turn off the meter thread
1800 temporarily. (That shouldn't matter unless yield
1801 or sleep is called but who knows...)
1802 */
1803 bool enable_pm = false;
1804
1805 /* Set images to not to be displayed */
1806 for (i = 0; i < MAX_IMAGES; i++)
1807 {
1808 data->img[i].display = false;
1809 }
1810#endif
1811
1812 /* reset to first subline if refresh all flag is set */
1813 if (refresh_mode == WPS_REFRESH_ALL)
1814 {
1815 for (i = 0; i < data->num_lines; i++)
1816 {
1817 data->curr_subline[i] = SUBLINE_RESET;
2589 } 1818 }
1819 }
2590 1820
2591 if (gwps->state->id3) 1821#ifdef HAVE_LCD_CHARCELLS
2592 memcpy(gwps->state->current_track_path, gwps->state->id3->path, 1822 for (i = 0; i < 8; i++)
2593 sizeof(gwps->state->current_track_path)); 1823 {
1824 if (data->wps_progress_pat[i] == 0)
1825 data->wps_progress_pat[i] = display->get_locked_pattern();
2594 } 1826 }
1827#endif
2595 1828
2596 if (gwps->state->id3) 1829 if (!state->id3)
2597 { 1830 {
2598 if (cuesheet_is_enabled() && gwps->state->id3->cuesheet_type 1831 display->stop_scroll();
2599 && (gwps->state->id3->elapsed < curr_cue->curr_track->offset 1832 return false;
2600 || (curr_cue->curr_track_idx < curr_cue->track_count - 1 1833 }
2601 && gwps->state->id3->elapsed >= (curr_cue->curr_track+1)->offset))) 1834
1835 state->ff_rewind_count = ffwd_offset;
1836
1837 for (line = 0; line < data->num_lines; line++)
1838 {
1839 memset(linebuf, 0, sizeof(linebuf));
1840 update_line = false;
1841
1842 /* get current subline for the line */
1843 new_subline_refresh = get_curr_subline(data, line);
1844
1845 flags = data->line_type[line][data->curr_subline[line]];
1846
1847 if (refresh_mode == WPS_REFRESH_ALL || flags & refresh_mode
1848 || new_subline_refresh)
2602 { 1849 {
2603 /* We've changed tracks within the cuesheet : 1850 /* get_line tells us if we need to update the line */
2604 we need to update the ID3 info and refresh the WPS */ 1851 update_line = get_line(gwps, line, data->curr_subline[line],
1852 &align, linebuf, sizeof(linebuf));
1853 }
2605 1854
2606 cue_find_current_track(curr_cue, gwps->state->id3->elapsed); 1855#ifdef HAVE_LCD_BITMAP
2607 cue_spoof_id3(curr_cue, gwps->state->id3); 1856 /* progressbar */
1857 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1858 {
1859 /* the progressbar should be alone on its line */
1860 update_line = false;
1861 draw_progressbar(gwps, line);
1862 }
2608 1863
2609 gwps->display->stop_scroll(); 1864 /* peakmeter */
2610 if (gui_wps_display()) 1865 if (flags & refresh_mode & WPS_REFRESH_PEAK_METER)
2611 retcode = true; 1866 {
1867 /* the peakmeter should be alone on its line */
1868 update_line = false;
1869
1870 int h = font_get(FONT_UI)->height;
1871 int peak_meter_y = display->getymargin() + line * h;
1872
1873 /* The user might decide to have the peak meter in the last
1874 line so that it is only displayed if no status bar is
1875 visible. If so we neither want do draw nor enable the
1876 peak meter. */
1877 if (peak_meter_y + h <= display->height) {
1878 /* found a line with a peak meter -> remember that we must
1879 enable it later */
1880 enable_pm = true;
1881 peak_meter_screen(gwps->display, 0, peak_meter_y,
1882 MIN(h, display->height - peak_meter_y));
1883 }
1884 }
1885
1886#else /* HAVE_LCD_CHARCELL */
1887
1888 /* progressbar */
1889 if (flags & refresh_mode & WPS_REFRESH_PLAYER_PROGRESS)
1890 {
1891 if (data->full_line_progressbar)
1892 draw_player_fullbar(gwps, linebuf, sizeof(linebuf));
2612 else 1893 else
2613 gui_wps_refresh(gwps, 0, WPS_REFRESH_ALL); 1894 draw_player_progress(gwps);
1895 }
1896#endif
1897
1898 if (update_line)
1899 {
1900 /* calculate alignment and draw the strings */
1901 write_line(display, &align, line, flags & WPS_REFRESH_SCROLL);
2614 } 1902 }
2615 else
2616 gui_wps_refresh(gwps, 0, WPS_REFRESH_NON_STATIC);
2617 } 1903 }
2618 1904
2619 gui_wps_statusbar_draw(gwps, false); 1905#ifdef HAVE_LCD_BITMAP
1906 data->peak_meter_enabled = enable_pm;
1907 wps_display_images(gwps);
1908#endif
2620 1909
2621 return retcode; 1910 display->update();
2622}
2623 1911
1912#if CONFIG_BACKLIGHT
1913 if (global_settings.caption_backlight && state->id3)
1914 {
1915 /* turn on backlight n seconds before track ends, and turn it off n
1916 seconds into the new track. n == backlight_timeout, or 5s */
1917 int n = backlight_timeout_value[global_settings.backlight_timeout]
1918 * 1000;
2624 1919
2625void display_keylock_text(bool locked) 1920 if ( n < 1000 )
2626{ 1921 n = 5000; /* use 5s if backlight is always on or off */
2627 char* s;
2628 int i;
2629 FOR_NB_SCREENS(i)
2630 gui_wps[i].display->stop_scroll();
2631 1922
2632#ifdef HAVE_LCD_CHARCELLS 1923 if (((state->id3->elapsed < 1000) ||
2633 if(locked) 1924 ((state->id3->length - state->id3->elapsed) < (unsigned)n)) &&
2634 s = str(LANG_KEYLOCK_ON_PLAYER); 1925 (state->paused == false))
2635 else 1926 backlight_on();
2636 s = str(LANG_KEYLOCK_OFF_PLAYER); 1927 }
2637#else 1928#endif
2638 if(locked) 1929#ifdef HAVE_REMOTE_LCD
2639 s = str(LANG_KEYLOCK_ON_RECORDER); 1930 if (global_settings.remote_caption_backlight && state->id3)
2640 else 1931 {
2641 s = str(LANG_KEYLOCK_OFF_RECORDER); 1932 /* turn on remote backlight n seconds before track ends, and turn it
1933 off n seconds into the new track. n == remote_backlight_timeout,
1934 or 5s */
1935 int n = backlight_timeout_value[global_settings.remote_backlight_timeout]
1936 * 1000;
1937
1938 if ( n < 1000 )
1939 n = 5000; /* use 5s if backlight is always on or off */
1940
1941 if (((state->id3->elapsed < 1000) ||
1942 ((state->id3->length - state->id3->elapsed) < (unsigned)n)) &&
1943 (state->paused == false))
1944 remote_backlight_on();
1945 }
2642#endif 1946#endif
2643 gui_syncsplash(HZ, s);
2644}
2645 1947
1948 return true;
1949}