1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2002-2007 Björn Stenberg
11 * Copyright (C) 2007-2008 Nicolas Pennequin
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ****************************************************************************/
22 #include "gwps-common.h"
29 #include "settings_list.h"
30 #include "rbunicode.h"
35 #include "powermgmt.h"
38 #ifdef HAVE_LCD_CHARCELLS
42 #include "mp3_playback.h"
46 #include "scrollbar.h"
49 #ifdef HAVE_LCD_BITMAP
50 #include "peakmeter.h"
59 #if CONFIG_CODEC == SWCODEC
65 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
66 /* 3% of 30min file == 54s step size */
67 #define MIN_FF_REWIND_STEP 500
69 /* Timeout unit expressed in HZ. In WPS, all timeouts are given in seconds
70 (possibly with a decimal fraction) but stored as integer values.
71 E.g. 2.5 is stored as 25. This means 25 tenth of a second, i.e. 25 units.
73 #define TIMEOUT_UNIT (HZ/10) /* I.e. 0.1 sec */
74 #define DEFAULT_SUBLINE_TIME_MULTIPLIER 20 /* In TIMEOUT_UNIT's */
76 bool wps_fading_out
= false;
77 void fade(bool fade_in
, bool updatewps
)
79 int fp_global_vol
= global_settings
.volume
<< 8;
80 int fp_min_vol
= sound_min(SOUND_VOLUME
) << 8;
81 int fp_step
= (fp_global_vol
- fp_min_vol
) / 30;
83 wps_fading_out
= !fade_in
;
86 int fp_volume
= fp_min_vol
;
88 /* zero out the sound */
89 sound_set_volume(fp_min_vol
>> 8);
91 sleep(HZ
/10); /* let audio thread run */
94 while (fp_volume
< fp_global_vol
- fp_step
) {
96 sound_set_volume(fp_volume
>> 8);
100 gui_wps_redraw(&gui_wps
[i
], 0, WPS_REFRESH_NON_STATIC
);
104 sound_set_volume(global_settings
.volume
);
108 int fp_volume
= fp_global_vol
;
110 while (fp_volume
> fp_min_vol
+ fp_step
) {
111 fp_volume
-= fp_step
;
112 sound_set_volume(fp_volume
>> 8);
116 gui_wps_redraw(&gui_wps
[i
], 0, WPS_REFRESH_NON_STATIC
);
121 wps_fading_out
= false;
122 #if CONFIG_CODEC != SWCODEC
124 /* let audio thread run and wait for the mas to run out of data */
125 while (!mp3_pause_done())
130 /* reset volume to what it was before the fade */
131 sound_set_volume(global_settings
.volume
);
135 bool update_onvol_change(struct gui_wps
* gwps
)
137 gui_wps_redraw(gwps
, 0, WPS_REFRESH_NON_STATIC
);
139 #ifdef HAVE_LCD_CHARCELLS
140 splashf(0, "Vol: %3d dB",
141 sound_val2phys(SOUND_VOLUME
, global_settings
.volume
));
147 bool ffwd_rew(int button
)
149 unsigned int step
= 0; /* current ff/rewind step */
150 unsigned int max_step
= 0; /* maximum ff/rewind step */
151 int ff_rewind_count
= 0; /* current ff/rewind count (in ticks) */
152 int direction
= -1; /* forward=1 or backward=-1 */
156 const long ff_rw_accel
= (global_settings
.ff_rewind_accel
+ 3);
158 if (button
== ACTION_NONE
)
160 status_set_ffmode(0);
167 case ACTION_WPS_SEEKFWD
:
169 case ACTION_WPS_SEEKBACK
:
170 if (wps_state
.ff_rewind
)
174 /* fast forwarding, calc max step relative to end */
175 max_step
= (wps_state
.id3
->length
-
176 (wps_state
.id3
->elapsed
+
178 FF_REWIND_MAX_PERCENT
/ 100;
182 /* rewinding, calc max step relative to start */
183 max_step
= (wps_state
.id3
->elapsed
+ ff_rewind_count
) *
184 FF_REWIND_MAX_PERCENT
/ 100;
187 max_step
= MAX(max_step
, MIN_FF_REWIND_STEP
);
192 ff_rewind_count
+= step
* direction
;
194 /* smooth seeking by multiplying step by: 1 + (2 ^ -accel) */
195 step
+= step
>> ff_rw_accel
;
199 if ( (audio_status() & AUDIO_STATUS_PLAY
) &&
200 wps_state
.id3
&& wps_state
.id3
->length
)
202 if (!wps_state
.paused
)
203 #if (CONFIG_CODEC == SWCODEC)
204 audio_pre_ff_rewind();
208 #if CONFIG_KEYPAD == PLAYER_PAD
210 gui_wps
[i
].display
->stop_scroll();
213 status_set_ffmode(STATUS_FASTFORWARD
);
215 status_set_ffmode(STATUS_FASTBACKWARD
);
217 wps_state
.ff_rewind
= true;
219 step
= 1000 * global_settings
.ff_rewind_min_step
;
226 if ((wps_state
.id3
->elapsed
+ ff_rewind_count
) >
227 wps_state
.id3
->length
)
228 ff_rewind_count
= wps_state
.id3
->length
-
229 wps_state
.id3
->elapsed
;
232 if ((int)(wps_state
.id3
->elapsed
+ ff_rewind_count
) < 0)
233 ff_rewind_count
= -wps_state
.id3
->elapsed
;
237 gui_wps_redraw(&gui_wps
[i
],
238 (wps_state
.wps_time_countup
== false)?
239 ff_rewind_count
:-ff_rewind_count
,
240 WPS_REFRESH_PLAYER_PROGRESS
|
241 WPS_REFRESH_DYNAMIC
);
245 case ACTION_WPS_STOPSEEK
:
246 wps_state
.id3
->elapsed
= wps_state
.id3
->elapsed
+ff_rewind_count
;
247 audio_ff_rewind(wps_state
.id3
->elapsed
);
249 wps_state
.ff_rewind
= false;
250 status_set_ffmode(0);
251 #if (CONFIG_CODEC != SWCODEC)
252 if (!wps_state
.paused
)
255 #ifdef HAVE_LCD_CHARCELLS
257 gui_wps_redraw(&gui_wps
[i
],0, WPS_REFRESH_ALL
);
263 if(default_event_handler(button
) == SYS_USB_CONNECTED
) {
264 status_set_ffmode(0);
272 button
= get_action(CONTEXT_WPS
|ALLOW_SOFTLOCK
,TIMEOUT_BLOCK
);
273 #ifdef HAVE_TOUCHSCREEN
274 if (button
== ACTION_TOUCHSCREEN
)
275 button
= wps_get_touchaction(gui_wps
[SCREEN_MAIN
].data
);
282 bool gui_wps_display(struct gui_wps
*gwps
)
284 struct screen
*display
= gwps
->display
;
285 struct wps_data
*data
= gwps
->data
;
286 int screen
= display
->screen_type
;
288 /* Update the values in the first (default) viewport - in case the user
289 has modified the statusbar or colour settings */
291 if (display
->depth
> 1)
293 data
->viewports
[0].vp
.fg_pattern
= display
->get_foreground();
294 data
->viewports
[0].vp
.bg_pattern
= display
->get_background();
297 display
->clear_display();
298 if (!data
->wps_loaded
) {
299 if ( !data
->num_tokens
) {
300 /* set the default wps for the main-screen */
301 if(screen
== SCREEN_MAIN
)
304 unload_wps_backdrop();
308 #ifdef HAVE_LCD_BITMAP
309 "%s%?it<%?in<%in. |>%it|%fn>\n"
310 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
311 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
313 "%al%pc/%pt%ar[%pp:%pe]\n"
314 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
318 "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
319 "%pc%?ps<*|/>%pt\n", false);
322 #ifdef HAVE_REMOTE_LCD
323 /* set the default wps for the remote-screen */
324 else if(screen
== SCREEN_REMOTE
)
326 #if LCD_REMOTE_DEPTH > 1
327 unload_remote_wps_backdrop();
331 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
332 "%s%?it<%?in<%in. |>%it|%fn>\n"
333 "%al%pc/%pt%ar[%pp:%pe]\n"
334 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
342 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
343 if (screen
== SCREEN_REMOTE
)
344 show_remote_wps_backdrop();
345 else if (screen
== SCREEN_MAIN
)
351 return gui_wps_redraw(gwps
, 0, WPS_REFRESH_ALL
);
354 bool gui_wps_update(struct gui_wps
*gwps
)
356 struct mp3entry
*id3
= gwps
->state
->id3
;
358 if (cuesheet_is_enabled() && id3
->cuesheet_type
359 && (id3
->elapsed
< curr_cue
->curr_track
->offset
360 || (curr_cue
->curr_track_idx
< curr_cue
->track_count
- 1
361 && id3
->elapsed
>= (curr_cue
->curr_track
+1)->offset
)))
363 /* We've changed tracks within the cuesheet :
364 we need to update the ID3 info and refresh the WPS */
365 gwps
->state
->do_full_update
= true;
366 cue_find_current_track(curr_cue
, id3
->elapsed
);
367 cue_spoof_id3(curr_cue
, id3
);
370 retval
= gui_wps_redraw(gwps
, 0,
371 gwps
->state
->do_full_update
?
372 WPS_REFRESH_ALL
: WPS_REFRESH_NON_STATIC
);
377 void display_keylock_text(bool locked
)
381 gui_wps
[i
].display
->stop_scroll();
383 splash(HZ
, locked
? ID2P(LANG_KEYLOCK_ON
) : ID2P(LANG_KEYLOCK_OFF
));
386 #ifdef HAVE_LCD_BITMAP
388 static void draw_progressbar(struct gui_wps
*gwps
,
389 struct wps_viewport
*wps_vp
)
391 struct screen
*display
= gwps
->display
;
392 struct wps_state
*state
= gwps
->state
;
393 struct progressbar
*pb
= wps_vp
->pb
;
398 int line_height
= font_get(wps_vp
->vp
.font
)->height
;
399 /* center the pb in the line, but only if the line is higher than the pb */
400 int center
= (line_height
-pb
->height
)/2;
401 /* if Y was not set calculate by font height,Y is -line_number-1 */
402 y
= (-y
-1)*line_height
+ (0 > center
? 0 : center
);
405 if (pb
->have_bitmap_pb
)
406 gui_bitmap_scrollbar_draw(display
, pb
->bm
,
407 pb
->x
, y
, pb
->width
, pb
->bm
.height
,
408 state
->id3
->length
? state
->id3
->length
: 1, 0,
409 state
->id3
->length
? state
->id3
->elapsed
410 + state
->ff_rewind_count
: 0,
413 gui_scrollbar_draw(display
, pb
->x
, y
, pb
->width
, pb
->height
,
414 state
->id3
->length
? state
->id3
->length
: 1, 0,
415 state
->id3
->length
? state
->id3
->elapsed
416 + state
->ff_rewind_count
: 0,
418 #ifdef AB_REPEAT_ENABLE
419 if ( ab_repeat_mode_enabled() && state
->id3
->length
!= 0 )
420 ab_draw_markers(display
, state
->id3
->length
,
421 pb
->x
, pb
->x
+ pb
->width
, y
, pb
->height
);
424 if ( cuesheet_is_enabled() && state
->id3
->cuesheet_type
)
425 cue_draw_markers(display
, state
->id3
->length
,
426 pb
->x
, pb
->x
+ pb
->width
, y
+1, pb
->height
-2);
429 /* clears the area where the image was shown */
430 static void clear_image_pos(struct gui_wps
*gwps
, int n
)
434 struct wps_data
*data
= gwps
->data
;
435 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
436 gwps
->display
->fillrect(data
->img
[n
].x
, data
->img
[n
].y
,
437 data
->img
[n
].bm
.width
, data
->img
[n
].subimage_height
);
438 gwps
->display
->set_drawmode(DRMODE_SOLID
);
441 static void wps_draw_image(struct gui_wps
*gwps
, int n
, int subimage
)
443 struct screen
*display
= gwps
->display
;
444 struct wps_data
*data
= gwps
->data
;
445 if(data
->img
[n
].always_display
)
446 display
->set_drawmode(DRMODE_FG
);
448 display
->set_drawmode(DRMODE_SOLID
);
451 if(data
->img
[n
].bm
.format
== FORMAT_MONO
) {
453 display
->mono_bitmap_part(data
->img
[n
].bm
.data
,
454 0, data
->img
[n
].subimage_height
* subimage
,
455 data
->img
[n
].bm
.width
, data
->img
[n
].x
,
456 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
457 data
->img
[n
].subimage_height
);
460 display
->transparent_bitmap_part((fb_data
*)data
->img
[n
].bm
.data
,
461 0, data
->img
[n
].subimage_height
* subimage
,
462 data
->img
[n
].bm
.width
, data
->img
[n
].x
,
463 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
464 data
->img
[n
].subimage_height
);
469 static void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
471 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
475 struct wps_data
*data
= gwps
->data
;
476 struct screen
*display
= gwps
->display
;
478 for (n
= 0; n
< MAX_IMAGES
; n
++)
480 if (data
->img
[n
].loaded
)
482 if (data
->img
[n
].display
>= 0)
484 wps_draw_image(gwps
, n
, data
->img
[n
].display
);
485 } else if (data
->img
[n
].always_display
&& data
->img
[n
].vp
== vp
)
487 wps_draw_image(gwps
, n
, 0);
491 display
->set_drawmode(DRMODE_SOLID
);
494 #else /* HAVE_LCD_CHARCELL */
496 static bool draw_player_progress(struct gui_wps
*gwps
)
498 struct wps_state
*state
= gwps
->state
;
499 struct screen
*display
= gwps
->display
;
500 unsigned char progress_pattern
[7];
507 if (state
->id3
->length
)
508 pos
= 36 * (state
->id3
->elapsed
+ state
->ff_rewind_count
)
509 / state
->id3
->length
;
511 for (i
= 0; i
< 7; i
++, pos
-= 5)
514 progress_pattern
[i
] = 0x1fu
;
516 progress_pattern
[i
] = 0x00u
;
518 progress_pattern
[i
] = 0x1fu
>> pos
;
521 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
525 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
527 static const unsigned char numbers
[10][4] = {
528 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
529 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
530 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
531 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
532 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
533 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
534 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
535 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
536 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
537 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
540 struct wps_state
*state
= gwps
->state
;
541 struct screen
*display
= gwps
->display
;
542 struct wps_data
*data
= gwps
->data
;
543 unsigned char progress_pattern
[7];
552 if (!state
->id3
|| buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
555 time
= state
->id3
->elapsed
+ state
->ff_rewind_count
;
556 if (state
->id3
->length
)
557 pos
= 55 * time
/ state
->id3
->length
;
559 memset(timestr
, 0, sizeof(timestr
));
560 format_time(timestr
, sizeof(timestr
)-2, time
);
561 timestr
[strlen(timestr
)] = ':'; /* always safe */
563 for (i
= 0; i
< 11; i
++, pos
-= 5)
566 memset(progress_pattern
, 0, sizeof(progress_pattern
));
568 if ((digit
= timestr
[time_idx
]))
573 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
575 memcpy(progress_pattern
, numbers
[digit
], 4);
578 else /* tens, shifted right */
580 for (j
= 0; j
< 4; j
++)
581 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
583 if (time_idx
> 0) /* not the first group, add colon in front */
585 progress_pattern
[1] |= 0x10u
;
586 progress_pattern
[3] |= 0x10u
;
592 progress_pattern
[5] = progress_pattern
[6] = 0x1fu
;
595 if (pos
> 0 && pos
< 5)
598 progress_pattern
[5] = progress_pattern
[6] = (~0x1fu
>> pos
) & 0x1fu
;
601 if (softchar
&& pat_idx
< 8)
603 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
605 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
609 buf
= utf8encode(' ', buf
);
611 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
616 #endif /* HAVE_LCD_CHARCELL */
618 static char* get_codectype(const struct mp3entry
* id3
)
620 if (id3
->codectype
< AFMT_NUM_CODECS
) {
621 return (char*)audio_formats
[id3
->codectype
].label
;
627 /* Extract a part from a path.
629 * buf - buffer extract part to.
630 * buf_size - size of buffer.
631 * path - path to extract from.
632 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
633 * parent of parent, etc.
635 * Returns buf if the desired level was found, NULL otherwise.
637 static char* get_dir(char* buf
, int buf_size
, const char* path
, int level
)
640 const char* last_sep
;
643 sep
= path
+ strlen(path
);
658 if (level
|| (last_sep
<= sep
))
661 len
= MIN(last_sep
- sep
, buf_size
- 1);
662 strncpy(buf
, sep
+ 1, len
);
667 /* Return the tag found at index i and write its value in buf.
668 The return value is buf if the tag had a value, or NULL if not.
670 intval is used with conditionals/enums: when this function is called,
671 intval should contain the number of options in the conditional/enum.
672 When this function returns, intval is -1 if the tag is non numeric or,
673 if the tag is numeric, *intval is the enum case we want to go to (between 1
674 and the original value of *intval, inclusive).
675 When not treating a conditional/enum, intval should be NULL.
677 static const char *get_token_value(struct gui_wps
*gwps
,
678 struct wps_token
*token
,
679 char *buf
, int buf_size
,
685 struct wps_data
*data
= gwps
->data
;
686 struct wps_state
*state
= gwps
->state
;
691 struct mp3entry
*id3
;
702 struct tm
* tm
= NULL
;
704 /* if the token is an RTC one, update the time
705 and do the necessary checks */
706 if (token
->type
>= WPS_TOKENS_RTC_BEGIN
707 && token
->type
<= WPS_TOKENS_RTC_END
)
725 case WPS_TOKEN_CHARACTER
:
726 return &(token
->value
.c
);
728 case WPS_TOKEN_STRING
:
729 return data
->strings
[token
->value
.i
];
731 case WPS_TOKEN_TRACK_TIME_ELAPSED
:
732 format_time(buf
, buf_size
,
733 id3
->elapsed
+ state
->ff_rewind_count
);
736 case WPS_TOKEN_TRACK_TIME_REMAINING
:
737 format_time(buf
, buf_size
,
738 id3
->length
- id3
->elapsed
-
739 state
->ff_rewind_count
);
742 case WPS_TOKEN_TRACK_LENGTH
:
743 format_time(buf
, buf_size
, id3
->length
);
746 case WPS_TOKEN_PLAYLIST_ENTRIES
:
747 snprintf(buf
, buf_size
, "%d", playlist_amount());
750 case WPS_TOKEN_PLAYLIST_NAME
:
751 return playlist_name(NULL
, buf
, buf_size
);
753 case WPS_TOKEN_PLAYLIST_POSITION
:
754 snprintf(buf
, buf_size
, "%d", playlist_get_display_index());
757 case WPS_TOKEN_PLAYLIST_SHUFFLE
:
758 if ( global_settings
.playlist_shuffle
)
764 case WPS_TOKEN_VOLUME
:
765 snprintf(buf
, buf_size
, "%d", global_settings
.volume
);
768 if (global_settings
.volume
== sound_min(SOUND_VOLUME
))
772 else if (global_settings
.volume
== 0)
776 else if (global_settings
.volume
> 0)
782 *intval
= (limit
- 3) * (global_settings
.volume
783 - sound_min(SOUND_VOLUME
) - 1)
784 / (-1 - sound_min(SOUND_VOLUME
)) + 2;
789 case WPS_TOKEN_TRACK_ELAPSED_PERCENT
:
790 if (id3
->length
<= 0)
795 *intval
= limit
* (id3
->elapsed
+ state
->ff_rewind_count
)
798 snprintf(buf
, buf_size
, "%d",
799 100*(id3
->elapsed
+ state
->ff_rewind_count
) / id3
->length
);
802 case WPS_TOKEN_METADATA_ARTIST
:
805 case WPS_TOKEN_METADATA_COMPOSER
:
806 return id3
->composer
;
808 case WPS_TOKEN_METADATA_ALBUM
:
811 case WPS_TOKEN_METADATA_ALBUM_ARTIST
:
812 return id3
->albumartist
;
814 case WPS_TOKEN_METADATA_GROUPING
:
815 return id3
->grouping
;
817 case WPS_TOKEN_METADATA_GENRE
:
818 return id3
->genre_string
;
820 case WPS_TOKEN_METADATA_DISC_NUMBER
:
821 if (id3
->disc_string
)
822 return id3
->disc_string
;
824 snprintf(buf
, buf_size
, "%d", id3
->discnum
);
829 case WPS_TOKEN_METADATA_TRACK_NUMBER
:
830 if (id3
->track_string
)
831 return id3
->track_string
;
834 snprintf(buf
, buf_size
, "%d", id3
->tracknum
);
839 case WPS_TOKEN_METADATA_TRACK_TITLE
:
842 case WPS_TOKEN_METADATA_VERSION
:
843 switch (id3
->id3version
)
864 case WPS_TOKEN_METADATA_YEAR
:
865 if( id3
->year_string
)
866 return id3
->year_string
;
869 snprintf(buf
, buf_size
, "%d", id3
->year
);
874 case WPS_TOKEN_METADATA_COMMENT
:
878 case WPS_TOKEN_ALBUMART_DISPLAY
:
879 draw_album_art(gwps
, audio_current_aa_hid(), false);
882 case WPS_TOKEN_ALBUMART_FOUND
:
883 if (audio_current_aa_hid() >= 0) {
889 case WPS_TOKEN_FILE_BITRATE
:
891 snprintf(buf
, buf_size
, "%d", id3
->bitrate
);
896 case WPS_TOKEN_FILE_CODEC
:
899 if(id3
->codectype
== AFMT_UNKNOWN
)
900 *intval
= AFMT_NUM_CODECS
;
902 *intval
= id3
->codectype
;
904 return get_codectype(id3
);
906 case WPS_TOKEN_FILE_FREQUENCY
:
907 snprintf(buf
, buf_size
, "%ld", id3
->frequency
);
910 case WPS_TOKEN_FILE_FREQUENCY_KHZ
:
911 /* ignore remainders < 100, so 22050 Hz becomes just 22k */
912 if ((id3
->frequency
% 1000) < 100)
913 snprintf(buf
, buf_size
, "%ld", id3
->frequency
/ 1000);
915 snprintf(buf
, buf_size
, "%ld.%d",
916 id3
->frequency
/ 1000,
917 (id3
->frequency
% 1000) / 100);
920 case WPS_TOKEN_FILE_NAME
:
921 if (get_dir(buf
, buf_size
, id3
->path
, 0)) {
922 /* Remove extension */
923 char* sep
= strrchr(buf
, '.');
933 case WPS_TOKEN_FILE_NAME_WITH_EXTENSION
:
934 return get_dir(buf
, buf_size
, id3
->path
, 0);
936 case WPS_TOKEN_FILE_PATH
:
939 case WPS_TOKEN_FILE_SIZE
:
940 snprintf(buf
, buf_size
, "%ld", id3
->filesize
/ 1024);
943 case WPS_TOKEN_FILE_VBR
:
944 return id3
->vbr
? "(avg)" : NULL
;
946 case WPS_TOKEN_FILE_DIRECTORY
:
947 return get_dir(buf
, buf_size
, id3
->path
, token
->value
.i
);
949 case WPS_TOKEN_BATTERY_PERCENT
:
951 int l
= battery_level();
955 limit
= MAX(limit
, 2);
957 /* First enum is used for "unknown level". */
958 *intval
= (limit
- 1) * l
/ 100 + 2;
965 snprintf(buf
, buf_size
, "%d", l
);
972 case WPS_TOKEN_BATTERY_VOLTS
:
974 unsigned int v
= battery_voltage();
975 snprintf(buf
, buf_size
, "%d.%02d", v
/ 1000, (v
% 1000) / 10);
979 case WPS_TOKEN_BATTERY_TIME
:
981 int t
= battery_time();
983 snprintf(buf
, buf_size
, "%dh %dm", t
/ 60, t
% 60);
990 case WPS_TOKEN_BATTERY_CHARGER_CONNECTED
:
992 if(charger_input_state
==CHARGER
)
998 #if CONFIG_CHARGING >= CHARGING_MONITOR
999 case WPS_TOKEN_BATTERY_CHARGING
:
1001 if (charge_state
== CHARGING
|| charge_state
== TOPOFF
) {
1008 case WPS_TOKEN_BATTERY_SLEEPTIME
:
1010 if (get_sleep_timer() == 0)
1014 format_time(buf
, buf_size
, get_sleep_timer() * 1000);
1019 case WPS_TOKEN_PLAYBACK_STATUS
:
1021 int status
= audio_status();
1023 if (status
== AUDIO_STATUS_PLAY
)
1025 if (wps_fading_out
||
1026 (status
& AUDIO_STATUS_PAUSE
&& !status_get_ffmode()))
1028 if (status_get_ffmode() == STATUS_FASTFORWARD
)
1030 if (status_get_ffmode() == STATUS_FASTBACKWARD
)
1037 snprintf(buf
, buf_size
, "%d", mode
-1);
1041 case WPS_TOKEN_REPEAT_MODE
:
1043 *intval
= global_settings
.repeat_mode
+ 1;
1044 snprintf(buf
, buf_size
, "%d", global_settings
.repeat_mode
);
1047 case WPS_TOKEN_RTC_PRESENT
:
1055 case WPS_TOKEN_RTC_12HOUR_CFG
:
1057 *intval
= global_settings
.timeformat
+ 1;
1058 snprintf(buf
, buf_size
, "%d", global_settings
.timeformat
);
1061 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1062 /* d: day of month (01..31) */
1063 snprintf(buf
, buf_size
, "%02d", tm
->tm_mday
);
1066 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1067 /* e: day of month, blank padded ( 1..31) */
1068 snprintf(buf
, buf_size
, "%2d", tm
->tm_mday
);
1071 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1072 /* H: hour (00..23) */
1073 snprintf(buf
, buf_size
, "%02d", tm
->tm_hour
);
1076 case WPS_TOKEN_RTC_HOUR_24
:
1077 /* k: hour ( 0..23) */
1078 snprintf(buf
, buf_size
, "%2d", tm
->tm_hour
);
1081 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1082 /* I: hour (01..12) */
1083 snprintf(buf
, buf_size
, "%02d",
1084 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1087 case WPS_TOKEN_RTC_HOUR_12
:
1088 /* l: hour ( 1..12) */
1089 snprintf(buf
, buf_size
, "%2d",
1090 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1093 case WPS_TOKEN_RTC_MONTH
:
1094 /* m: month (01..12) */
1096 *intval
= tm
->tm_mon
+ 1;
1097 snprintf(buf
, buf_size
, "%02d", tm
->tm_mon
+ 1);
1100 case WPS_TOKEN_RTC_MINUTE
:
1101 /* M: minute (00..59) */
1102 snprintf(buf
, buf_size
, "%02d", tm
->tm_min
);
1105 case WPS_TOKEN_RTC_SECOND
:
1106 /* S: second (00..59) */
1107 snprintf(buf
, buf_size
, "%02d", tm
->tm_sec
);
1110 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1111 /* y: last two digits of year (00..99) */
1112 snprintf(buf
, buf_size
, "%02d", tm
->tm_year
% 100);
1115 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1116 /* Y: year (1970...) */
1117 snprintf(buf
, buf_size
, "%04d", tm
->tm_year
+ 1900);
1120 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1121 /* p: upper case AM or PM indicator */
1122 return tm
->tm_hour
/12 == 0 ? "AM" : "PM";
1124 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1125 /* P: lower case am or pm indicator */
1126 return tm
->tm_hour
/12 == 0 ? "am" : "pm";
1128 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1129 /* a: abbreviated weekday name (Sun..Sat) */
1130 return str(LANG_WEEKDAY_SUNDAY
+ tm
->tm_wday
);
1132 case WPS_TOKEN_RTC_MONTH_NAME
:
1133 /* b: abbreviated month name (Jan..Dec) */
1134 return str(LANG_MONTH_JANUARY
+ tm
->tm_mon
);
1136 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1137 /* u: day of week (1..7); 1 is Monday */
1139 *intval
= (tm
->tm_wday
== 0) ? 7 : tm
->tm_wday
;
1140 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
+ 1);
1143 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1144 /* w: day of week (0..6); 0 is Sunday */
1146 *intval
= tm
->tm_wday
+ 1;
1147 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
);
1150 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1151 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1152 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1153 case WPS_TOKEN_RTC_HOUR_24
:
1154 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1155 case WPS_TOKEN_RTC_HOUR_12
:
1156 case WPS_TOKEN_RTC_MONTH
:
1157 case WPS_TOKEN_RTC_MINUTE
:
1158 case WPS_TOKEN_RTC_SECOND
:
1159 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1160 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1161 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1163 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1165 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1166 case WPS_TOKEN_RTC_MONTH_NAME
:
1168 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1169 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1173 #ifdef HAVE_LCD_CHARCELLS
1174 case WPS_TOKEN_PROGRESSBAR
:
1176 char *end
= utf8encode(data
->wps_progress_pat
[0], buf
);
1181 case WPS_TOKEN_PLAYER_PROGRESSBAR
:
1184 /* we need 11 characters (full line) for
1186 strncpy(buf
, " ", buf_size
);
1190 /* Tell the user if we have an OldPlayer */
1191 strncpy(buf
, " <Old LCD> ", buf_size
);
1196 #ifdef HAVE_TAGCACHE
1197 case WPS_TOKEN_DATABASE_PLAYCOUNT
:
1199 *intval
= id3
->playcount
+ 1;
1201 snprintf(buf
, buf_size
, "%ld", id3
->playcount
);
1204 case WPS_TOKEN_DATABASE_RATING
:
1206 *intval
= id3
->rating
+ 1;
1208 snprintf(buf
, buf_size
, "%d", id3
->rating
);
1211 case WPS_TOKEN_DATABASE_AUTOSCORE
:
1213 *intval
= id3
->score
+ 1;
1215 snprintf(buf
, buf_size
, "%d", id3
->score
);
1219 #if (CONFIG_CODEC == SWCODEC)
1220 case WPS_TOKEN_CROSSFADE
:
1222 *intval
= global_settings
.crossfade
+ 1;
1223 snprintf(buf
, buf_size
, "%d", global_settings
.crossfade
);
1226 case WPS_TOKEN_REPLAYGAIN
:
1230 if (global_settings
.replaygain_type
== REPLAYGAIN_OFF
)
1235 get_replaygain_mode(id3
->track_gain_string
!= NULL
,
1236 id3
->album_gain_string
!= NULL
);
1238 val
= 6; /* no tag */
1242 if (global_settings
.replaygain_type
== REPLAYGAIN_SHUFFLE
)
1257 strncpy(buf
, id3
->track_gain_string
, buf_size
);
1261 strncpy(buf
, id3
->album_gain_string
, buf_size
);
1266 #endif /* (CONFIG_CODEC == SWCODEC) */
1268 #if (CONFIG_CODEC != MAS3507D)
1269 case WPS_TOKEN_SOUND_PITCH
:
1271 int val
= sound_get_pitch();
1272 snprintf(buf
, buf_size
, "%d.%d",
1273 val
/ 10, val
% 10);
1278 case WPS_TOKEN_MAIN_HOLD
:
1279 #ifdef HAS_BUTTON_HOLD
1282 if (is_keys_locked())
1283 #endif /*hold switch or softlock*/
1288 #ifdef HAS_REMOTE_BUTTON_HOLD
1289 case WPS_TOKEN_REMOTE_HOLD
:
1290 if (remote_button_hold())
1296 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
1297 case WPS_TOKEN_VLED_HDD
:
1303 case WPS_TOKEN_BUTTON_VOLUME
:
1304 if (data
->button_time_volume
&&
1305 TIME_BEFORE(current_tick
, data
->button_time_volume
+
1306 token
->value
.i
* TIMEOUT_UNIT
))
1309 case WPS_TOKEN_LASTTOUCH
:
1310 #ifdef HAVE_TOUCHSCREEN
1311 if (TIME_BEFORE(current_tick
, token
->value
.i
* TIMEOUT_UNIT
+
1312 touchscreen_last_touch()))
1317 case WPS_TOKEN_SETTING
:
1321 /* Handle contionals */
1322 const struct settings_list
*s
= settings
+token
->value
.i
;
1323 switch (s
->flags
&F_T_MASK
)
1328 /* %?St|name|<#000000|#000001|...|#FFFFFF> */
1329 /* shouldn't overflow since colors are stored
1331 * but this is pretty useless anyway */
1332 *intval
= *(int*)s
->setting
+ 1;
1333 else if (s
->cfg_vals
== NULL
)
1334 /* %?St|name|<1st choice|2nd choice|...> */
1335 *intval
= (*(int*)s
->setting
-s
->int_setting
->min
)
1336 /s
->int_setting
->step
+ 1;
1338 /* %?St|name|<1st choice|2nd choice|...> */
1339 /* Not sure about this one. cfg_name/vals are
1340 * indexed from 0 right? */
1341 *intval
= *(int*)s
->setting
+ 1;
1344 /* %?St|name|<if true|if false> */
1345 *intval
= *(bool*)s
->setting
?1:2;
1348 /* %?St|name|<if non empty string|if empty>
1349 * The string's emptyness discards the setting's
1350 * prefix and suffix */
1351 *intval
= ((char*)s
->setting
)[0]?1:2;
1354 /* This shouldn't happen ... but you never know */
1359 cfg_to_string(token
->value
.i
,buf
,buf_size
);
1368 /* Return the index to the end token for the conditional token at index.
1369 The conditional token can be either a start token or a separator
1370 (i.e. option) token.
1372 static int find_conditional_end(struct wps_data
*data
, int index
)
1375 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
1376 ret
= data
->tokens
[ret
].value
.i
;
1378 /* ret now is the index to the end token for the conditional. */
1382 /* Evaluate the conditional that is at *token_index and return whether a skip
1383 has ocurred. *token_index is updated with the new position.
1385 static bool evaluate_conditional(struct gui_wps
*gwps
, int *token_index
)
1390 struct wps_data
*data
= gwps
->data
;
1393 int cond_index
= *token_index
;
1396 unsigned char num_options
= data
->tokens
[cond_index
].value
.i
& 0xFF;
1397 unsigned char prev_val
= (data
->tokens
[cond_index
].value
.i
& 0xFF00) >> 8;
1399 /* treat ?xx<true> constructs as if they had 2 options. */
1400 if (num_options
< 2)
1403 int intval
= num_options
;
1404 /* get_token_value needs to know the number of options in the enum */
1405 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
1406 result
, sizeof(result
), &intval
);
1408 /* intval is now the number of the enum option we want to read,
1409 starting from 1. If intval is -1, we check if value is empty. */
1411 intval
= (value
&& *value
) ? 1 : num_options
;
1412 else if (intval
> num_options
|| intval
< 1)
1413 intval
= num_options
;
1415 data
->tokens
[cond_index
].value
.i
= (intval
<< 8) + num_options
;
1417 /* skip to the appropriate enum case */
1418 int next
= cond_index
+ 2;
1419 for (i
= 1; i
< intval
; i
++)
1421 next
= data
->tokens
[next
].value
.i
;
1423 *token_index
= next
;
1425 if (prev_val
== intval
)
1427 /* Same conditional case as previously. Return without clearing the
1432 cond_end
= find_conditional_end(data
, cond_index
+ 2);
1433 for (i
= cond_index
+ 3; i
< cond_end
; i
++)
1435 #ifdef HAVE_LCD_BITMAP
1436 /* clear all pictures in the conditional and nested ones */
1437 if (data
->tokens
[i
].type
== WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
)
1438 clear_image_pos(gwps
, data
->tokens
[i
].value
.i
& 0xFF);
1440 #ifdef HAVE_ALBUMART
1441 if (data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
1442 draw_album_art(gwps
, audio_current_aa_hid(), true);
1449 /* Read a (sub)line to the given alignment format buffer.
1450 linebuf is the buffer where the data is actually stored.
1451 align is the alignment format that'll be used to display the text.
1452 The return value indicates whether the line needs to be updated.
1454 static bool get_line(struct gui_wps
*gwps
,
1455 int line
, int subline
,
1456 struct align_pos
*align
,
1460 struct wps_data
*data
= gwps
->data
;
1463 char *buf
= linebuf
; /* will always point to the writing position */
1464 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
1465 int i
, last_token_idx
;
1466 bool update
= false;
1468 /* alignment-related variables */
1470 char* cur_align_start
;
1471 cur_align_start
= buf
;
1472 cur_align
= WPS_ALIGN_LEFT
;
1474 align
->center
= NULL
;
1475 align
->right
= NULL
;
1477 /* Process all tokens of the desired subline */
1478 last_token_idx
= wps_last_token_index(data
, line
, subline
);
1479 for (i
= wps_first_token_index(data
, line
, subline
);
1480 i
<= last_token_idx
; i
++)
1482 switch(data
->tokens
[i
].type
)
1484 case WPS_TOKEN_CONDITIONAL
:
1485 /* place ourselves in the right conditional case */
1486 update
|= evaluate_conditional(gwps
, &i
);
1489 case WPS_TOKEN_CONDITIONAL_OPTION
:
1490 /* we've finished in the curent conditional case,
1491 skip to the end of the conditional structure */
1492 i
= find_conditional_end(data
, i
);
1495 #ifdef HAVE_LCD_BITMAP
1496 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
1498 struct gui_img
*img
= data
->img
;
1499 int n
= data
->tokens
[i
].value
.i
& 0xFF;
1500 int subimage
= data
->tokens
[i
].value
.i
>> 8;
1502 if (n
>= 0 && n
< MAX_IMAGES
&& img
[n
].loaded
)
1503 img
[n
].display
= subimage
;
1508 case WPS_TOKEN_ALIGN_LEFT
:
1509 case WPS_TOKEN_ALIGN_CENTER
:
1510 case WPS_TOKEN_ALIGN_RIGHT
:
1511 /* remember where the current aligned text started */
1514 case WPS_ALIGN_LEFT
:
1515 align
->left
= cur_align_start
;
1518 case WPS_ALIGN_CENTER
:
1519 align
->center
= cur_align_start
;
1522 case WPS_ALIGN_RIGHT
:
1523 align
->right
= cur_align_start
;
1526 /* start a new alignment */
1527 switch (data
->tokens
[i
].type
)
1529 case WPS_TOKEN_ALIGN_LEFT
:
1530 cur_align
= WPS_ALIGN_LEFT
;
1532 case WPS_TOKEN_ALIGN_CENTER
:
1533 cur_align
= WPS_ALIGN_CENTER
;
1535 case WPS_TOKEN_ALIGN_RIGHT
:
1536 cur_align
= WPS_ALIGN_RIGHT
;
1542 cur_align_start
= buf
;
1544 case WPS_VIEWPORT_ENABLE
:
1546 char label
= data
->tokens
[i
].value
.i
;
1548 char temp
= VP_DRAW_HIDEABLE
;
1549 for(j
=0;j
<data
->num_viewports
;j
++)
1551 temp
= VP_DRAW_HIDEABLE
;
1552 if ((data
->viewports
[j
].hidden_flags
&VP_DRAW_HIDEABLE
) &&
1553 (data
->viewports
[j
].label
== label
))
1555 if (data
->viewports
[j
].hidden_flags
&VP_DRAW_WASHIDDEN
)
1556 temp
|= VP_DRAW_WASHIDDEN
;
1557 data
->viewports
[j
].hidden_flags
= temp
;
1564 /* get the value of the tag and copy it to the buffer */
1565 const char *value
= get_token_value(gwps
, &data
->tokens
[i
],
1566 temp_buf
, sizeof(temp_buf
), NULL
);
1570 while (*value
&& (buf
< linebuf_end
))
1578 /* close the current alignment */
1581 case WPS_ALIGN_LEFT
:
1582 align
->left
= cur_align_start
;
1585 case WPS_ALIGN_CENTER
:
1586 align
->center
= cur_align_start
;
1589 case WPS_ALIGN_RIGHT
:
1590 align
->right
= cur_align_start
;
1597 static void get_subline_timeout(struct gui_wps
*gwps
, int line
, int subline
)
1599 struct wps_data
*data
= gwps
->data
;
1601 int subline_idx
= wps_subline_index(data
, line
, subline
);
1602 int last_token_idx
= wps_last_token_index(data
, line
, subline
);
1604 data
->sublines
[subline_idx
].time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
1606 for (i
= wps_first_token_index(data
, line
, subline
);
1607 i
<= last_token_idx
; i
++)
1609 switch(data
->tokens
[i
].type
)
1611 case WPS_TOKEN_CONDITIONAL
:
1612 /* place ourselves in the right conditional case */
1613 evaluate_conditional(gwps
, &i
);
1616 case WPS_TOKEN_CONDITIONAL_OPTION
:
1617 /* we've finished in the curent conditional case,
1618 skip to the end of the conditional structure */
1619 i
= find_conditional_end(data
, i
);
1622 case WPS_TOKEN_SUBLINE_TIMEOUT
:
1623 data
->sublines
[subline_idx
].time_mult
= data
->tokens
[i
].value
.i
;
1632 /* Calculates which subline should be displayed for the specified line
1633 Returns true iff the subline must be refreshed */
1634 static bool update_curr_subline(struct gui_wps
*gwps
, int line
)
1636 struct wps_data
*data
= gwps
->data
;
1638 int search
, search_start
, num_sublines
;
1640 bool new_subline_refresh
;
1641 bool only_one_subline
;
1643 num_sublines
= data
->lines
[line
].num_sublines
;
1644 reset_subline
= (data
->lines
[line
].curr_subline
== SUBLINE_RESET
);
1645 new_subline_refresh
= false;
1646 only_one_subline
= false;
1648 /* if time to advance to next sub-line */
1649 if (TIME_AFTER(current_tick
, data
->lines
[line
].subline_expire_time
- 1) ||
1652 /* search all sublines until the next subline with time > 0
1653 is found or we get back to the subline we started with */
1657 search_start
= data
->lines
[line
].curr_subline
;
1659 for (search
= 0; search
< num_sublines
; search
++)
1661 data
->lines
[line
].curr_subline
++;
1663 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
1664 if (data
->lines
[line
].curr_subline
== num_sublines
)
1666 if (data
->lines
[line
].curr_subline
== 1)
1667 only_one_subline
= true;
1668 data
->lines
[line
].curr_subline
= 0;
1671 /* if back where we started after search or
1672 only one subline is defined on the line */
1673 if (((search
> 0) &&
1674 (data
->lines
[line
].curr_subline
== search_start
)) ||
1677 /* no other subline with a time > 0 exists */
1678 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1680 data
->lines
[line
].subline_expire_time
) + 100 * HZ
;
1685 /* get initial time multiplier for this subline */
1686 get_subline_timeout(gwps
, line
, data
->lines
[line
].curr_subline
);
1688 int subline_idx
= wps_subline_index(data
, line
,
1689 data
->lines
[line
].curr_subline
);
1691 /* only use this subline if subline time > 0 */
1692 if (data
->sublines
[subline_idx
].time_mult
> 0)
1694 new_subline_refresh
= true;
1695 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1696 current_tick
: data
->lines
[line
].subline_expire_time
) +
1697 TIMEOUT_UNIT
*data
->sublines
[subline_idx
].time_mult
;
1704 return new_subline_refresh
;
1707 /* Display a line appropriately according to its alignment format.
1708 format_align contains the text, separated between left, center and right.
1709 line is the index of the line on the screen.
1710 scroll indicates whether the line is a scrolling one or not.
1712 static void write_line(struct screen
*display
,
1713 struct align_pos
*format_align
,
1717 int left_width
= 0, left_xpos
;
1718 int center_width
= 0, center_xpos
;
1719 int right_width
= 0, right_xpos
;
1725 /* calculate different string sizes and positions */
1726 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
1727 if (format_align
->left
!= 0) {
1728 display
->getstringsize((unsigned char *)format_align
->left
,
1729 &left_width
, &string_height
);
1732 if (format_align
->right
!= 0) {
1733 display
->getstringsize((unsigned char *)format_align
->right
,
1734 &right_width
, &string_height
);
1737 if (format_align
->center
!= 0) {
1738 display
->getstringsize((unsigned char *)format_align
->center
,
1739 ¢er_width
, &string_height
);
1743 right_xpos
= (display
->getwidth() - right_width
);
1744 center_xpos
= (display
->getwidth() + left_xpos
- center_width
) / 2;
1746 scroll_width
= display
->getwidth() - left_xpos
;
1748 /* Checks for overlapping strings.
1749 If needed the overlapping strings will be merged, separated by a
1752 /* CASE 1: left and centered string overlap */
1753 /* there is a left string, need to merge left and center */
1754 if ((left_width
!= 0 && center_width
!= 0) &&
1755 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
1756 /* replace the former separator '\0' of left and
1757 center string with a space */
1758 *(--format_align
->center
) = ' ';
1759 /* calculate the new width and position of the merged string */
1760 left_width
= left_width
+ space_width
+ center_width
;
1761 /* there is no centered string anymore */
1764 /* there is no left string, move center to left */
1765 if ((left_width
== 0 && center_width
!= 0) &&
1766 (left_xpos
+ left_width
> center_xpos
)) {
1767 /* move the center string to the left string */
1768 format_align
->left
= format_align
->center
;
1769 /* calculate the new width and position of the string */
1770 left_width
= center_width
;
1771 /* there is no centered string anymore */
1775 /* CASE 2: centered and right string overlap */
1776 /* there is a right string, need to merge center and right */
1777 if ((center_width
!= 0 && right_width
!= 0) &&
1778 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
1779 /* replace the former separator '\0' of center and
1780 right string with a space */
1781 *(--format_align
->right
) = ' ';
1782 /* move the center string to the right after merge */
1783 format_align
->right
= format_align
->center
;
1784 /* calculate the new width and position of the merged string */
1785 right_width
= center_width
+ space_width
+ right_width
;
1786 right_xpos
= (display
->getwidth() - right_width
);
1787 /* there is no centered string anymore */
1790 /* there is no right string, move center to right */
1791 if ((center_width
!= 0 && right_width
== 0) &&
1792 (center_xpos
+ center_width
> right_xpos
)) {
1793 /* move the center string to the right string */
1794 format_align
->right
= format_align
->center
;
1795 /* calculate the new width and position of the string */
1796 right_width
= center_width
;
1797 right_xpos
= (display
->getwidth() - right_width
);
1798 /* there is no centered string anymore */
1802 /* CASE 3: left and right overlap
1803 There is no center string anymore, either there never
1804 was one or it has been merged in case 1 or 2 */
1805 /* there is a left string, need to merge left and right */
1806 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
1807 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
1808 /* replace the former separator '\0' of left and
1809 right string with a space */
1810 *(--format_align
->right
) = ' ';
1811 /* calculate the new width and position of the string */
1812 left_width
= left_width
+ space_width
+ right_width
;
1813 /* there is no right string anymore */
1816 /* there is no left string, move right to left */
1817 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
1818 (left_width
> right_xpos
)) {
1819 /* move the right string to the left string */
1820 format_align
->left
= format_align
->right
;
1821 /* calculate the new width and position of the string */
1822 left_width
= right_width
;
1823 /* there is no right string anymore */
1827 ypos
= (line
* string_height
);
1830 if (scroll
&& ((left_width
> scroll_width
) ||
1831 (center_width
> scroll_width
) ||
1832 (right_width
> scroll_width
)))
1834 display
->puts_scroll(0, line
,
1835 (unsigned char *)format_align
->left
);
1839 #ifdef HAVE_LCD_BITMAP
1840 /* clear the line first */
1841 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1842 display
->fillrect(left_xpos
, ypos
, display
->getwidth(), string_height
);
1843 display
->set_drawmode(DRMODE_SOLID
);
1846 /* Nasty hack: we output an empty scrolling string,
1847 which will reset the scroller for that line */
1848 display
->puts_scroll(0, line
, (unsigned char *)"");
1850 /* print aligned strings */
1851 if (left_width
!= 0)
1853 display
->putsxy(left_xpos
, ypos
,
1854 (unsigned char *)format_align
->left
);
1856 if (center_width
!= 0)
1858 display
->putsxy(center_xpos
, ypos
,
1859 (unsigned char *)format_align
->center
);
1861 if (right_width
!= 0)
1863 display
->putsxy(right_xpos
, ypos
,
1864 (unsigned char *)format_align
->right
);
1869 bool gui_wps_redraw(struct gui_wps
*gwps
,
1871 unsigned refresh_mode
)
1873 struct wps_data
*data
= gwps
->data
;
1874 struct screen
*display
= gwps
->display
;
1875 struct wps_state
*state
= gwps
->state
;
1877 if (!data
|| !state
|| !display
)
1880 struct mp3entry
*id3
= state
->id3
;
1885 int v
, line
, i
, subline_idx
;
1887 char linebuf
[MAX_PATH
];
1889 struct align_pos align
;
1891 align
.center
= NULL
;
1894 bool update_line
, new_subline_refresh
;
1896 #ifdef HAVE_LCD_BITMAP
1898 /* to find out wether the peak meter is enabled we
1899 assume it wasn't until we find a line that contains
1900 the peak meter. We can't use peak_meter_enabled itself
1901 because that would mean to turn off the meter thread
1902 temporarily. (That shouldn't matter unless yield
1903 or sleep is called but who knows...)
1905 bool enable_pm
= false;
1909 /* reset to first subline if refresh all flag is set */
1910 if (refresh_mode
== WPS_REFRESH_ALL
)
1912 display
->set_viewport(&data
->viewports
[0].vp
);
1913 display
->clear_viewport();
1915 for (i
= 0; i
<= data
->num_lines
; i
++)
1917 data
->lines
[i
].curr_subline
= SUBLINE_RESET
;
1921 #ifdef HAVE_LCD_CHARCELLS
1922 for (i
= 0; i
< 8; i
++)
1924 if (data
->wps_progress_pat
[i
] == 0)
1925 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1929 state
->ff_rewind_count
= ffwd_offset
;
1931 /* disable any viewports which are conditionally displayed */
1932 for (v
= 0; v
< data
->num_viewports
; v
++)
1934 if (data
->viewports
[v
].hidden_flags
&VP_DRAW_HIDEABLE
)
1936 if (data
->viewports
[v
].hidden_flags
&VP_DRAW_HIDDEN
)
1937 data
->viewports
[v
].hidden_flags
|= VP_DRAW_WASHIDDEN
;
1939 data
->viewports
[v
].hidden_flags
|= VP_DRAW_HIDDEN
;
1942 for (v
= 0; v
< data
->num_viewports
; v
++)
1944 struct wps_viewport
*wps_vp
= &(data
->viewports
[v
]);
1945 unsigned vp_refresh_mode
= refresh_mode
;
1946 display
->set_viewport(&wps_vp
->vp
);
1948 #ifdef HAVE_LCD_BITMAP
1949 /* Set images to not to be displayed */
1950 for (i
= 0; i
< MAX_IMAGES
; i
++)
1952 data
->img
[i
].display
= -1;
1955 /* dont redraw the viewport if its disabled */
1956 if ((wps_vp
->hidden_flags
&VP_DRAW_HIDDEN
))
1958 if (!(wps_vp
->hidden_flags
&VP_DRAW_WASHIDDEN
))
1959 display
->scroll_stop(&wps_vp
->vp
);
1960 wps_vp
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1963 else if (((wps_vp
->hidden_flags
&
1964 (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
))
1965 == (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
)))
1967 vp_refresh_mode
= WPS_REFRESH_ALL
;
1968 wps_vp
->hidden_flags
= VP_DRAW_HIDEABLE
;
1970 if (vp_refresh_mode
== WPS_REFRESH_ALL
)
1972 display
->clear_viewport();
1975 for (line
= wps_vp
->first_line
;
1976 line
<= wps_vp
->last_line
; line
++)
1978 memset(linebuf
, 0, sizeof(linebuf
));
1979 update_line
= false;
1981 /* get current subline for the line */
1982 new_subline_refresh
= update_curr_subline(gwps
, line
);
1984 subline_idx
= wps_subline_index(data
, line
,
1985 data
->lines
[line
].curr_subline
);
1986 flags
= data
->sublines
[subline_idx
].line_type
;
1988 if (vp_refresh_mode
== WPS_REFRESH_ALL
|| (flags
& vp_refresh_mode
)
1989 || new_subline_refresh
)
1991 /* get_line tells us if we need to update the line */
1992 update_line
= get_line(gwps
, line
, data
->lines
[line
].curr_subline
,
1993 &align
, linebuf
, sizeof(linebuf
));
1995 #ifdef HAVE_LCD_BITMAP
1997 if (flags
& vp_refresh_mode
& WPS_REFRESH_PEAK_METER
)
1999 /* the peakmeter should be alone on its line */
2000 update_line
= false;
2002 int h
= font_get(wps_vp
->vp
.font
)->height
;
2003 int peak_meter_y
= (line
- wps_vp
->first_line
)* h
;
2005 /* The user might decide to have the peak meter in the last
2006 line so that it is only displayed if no status bar is
2007 visible. If so we neither want do draw nor enable the
2009 if (peak_meter_y
+ h
<= display
->getheight()) {
2010 /* found a line with a peak meter -> remember that we must
2013 peak_meter_enabled
= true;
2014 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
2015 MIN(h
, display
->getheight() - peak_meter_y
));
2019 peak_meter_enabled
= false;
2023 #else /* HAVE_LCD_CHARCELL */
2026 if (flags
& vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
2028 if (data
->full_line_progressbar
)
2029 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
2031 draw_player_progress(gwps
);
2036 /* conditionals clear the line which means if the %Vd is put into the default
2037 viewport there will be a blank line.
2038 To get around this we dont allow any actual drawing to happen in the
2039 deault vp if other vp's are defined */
2040 ((data
->num_viewports
>1 && v
!=0) || data
->num_viewports
== 1))
2042 if (flags
& WPS_REFRESH_SCROLL
)
2044 /* if the line is a scrolling one we don't want to update
2045 too often, so that it has the time to scroll */
2046 if ((vp_refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
2047 write_line(display
, &align
, line
- wps_vp
->first_line
, true);
2050 write_line(display
, &align
, line
- wps_vp
->first_line
, false);
2054 #ifdef HAVE_LCD_BITMAP
2056 if (vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
2060 draw_progressbar(gwps
, wps_vp
);
2063 /* Now display any images in this viewport */
2064 wps_display_images(gwps
, &wps_vp
->vp
);
2068 #ifdef HAVE_LCD_BITMAP
2069 data
->peak_meter_enabled
= enable_pm
;
2072 if (refresh_mode
& WPS_REFRESH_STATUSBAR
)
2074 gwps_draw_statusbars();
2076 /* Restore the default viewport */
2077 display
->set_viewport(NULL
);