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 bool cuesheet_update
= cuesheet_subtrack_changed(id3
);
359 gwps
->state
->do_full_update
= cuesheet_update
|| gwps
->state
->do_full_update
;
360 retval
= gui_wps_redraw(gwps
, 0,
361 gwps
->state
->do_full_update
?
362 WPS_REFRESH_ALL
: WPS_REFRESH_NON_STATIC
);
367 void display_keylock_text(bool locked
)
371 gui_wps
[i
].display
->stop_scroll();
373 splash(HZ
, locked
? ID2P(LANG_KEYLOCK_ON
) : ID2P(LANG_KEYLOCK_OFF
));
376 #ifdef HAVE_LCD_BITMAP
378 static void draw_progressbar(struct gui_wps
*gwps
,
379 struct wps_viewport
*wps_vp
)
381 struct screen
*display
= gwps
->display
;
382 struct wps_state
*state
= gwps
->state
;
383 struct progressbar
*pb
= wps_vp
->pb
;
388 int line_height
= font_get(wps_vp
->vp
.font
)->height
;
389 /* center the pb in the line, but only if the line is higher than the pb */
390 int center
= (line_height
-pb
->height
)/2;
391 /* if Y was not set calculate by font height,Y is -line_number-1 */
392 y
= (-y
-1)*line_height
+ (0 > center
? 0 : center
);
395 if (pb
->have_bitmap_pb
)
396 gui_bitmap_scrollbar_draw(display
, pb
->bm
,
397 pb
->x
, y
, pb
->width
, pb
->bm
.height
,
398 state
->id3
->length
? state
->id3
->length
: 1, 0,
399 state
->id3
->length
? state
->id3
->elapsed
400 + state
->ff_rewind_count
: 0,
403 gui_scrollbar_draw(display
, pb
->x
, y
, pb
->width
, pb
->height
,
404 state
->id3
->length
? state
->id3
->length
: 1, 0,
405 state
->id3
->length
? state
->id3
->elapsed
406 + state
->ff_rewind_count
: 0,
408 #ifdef AB_REPEAT_ENABLE
409 if ( ab_repeat_mode_enabled() && state
->id3
->length
!= 0 )
410 ab_draw_markers(display
, state
->id3
->length
,
411 pb
->x
, pb
->x
+ pb
->width
, y
, pb
->height
);
414 if (state
->id3
->cuesheet
)
415 cue_draw_markers(display
, state
->id3
->length
,
416 pb
->x
, pb
->x
+ pb
->width
, y
+1, pb
->height
-2);
419 /* clears the area where the image was shown */
420 static void clear_image_pos(struct gui_wps
*gwps
, int n
)
424 struct wps_data
*data
= gwps
->data
;
425 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
426 gwps
->display
->fillrect(data
->img
[n
].x
, data
->img
[n
].y
,
427 data
->img
[n
].bm
.width
, data
->img
[n
].subimage_height
);
428 gwps
->display
->set_drawmode(DRMODE_SOLID
);
431 static void wps_draw_image(struct gui_wps
*gwps
, int n
, int subimage
)
433 struct screen
*display
= gwps
->display
;
434 struct wps_data
*data
= gwps
->data
;
435 if(data
->img
[n
].always_display
)
436 display
->set_drawmode(DRMODE_FG
);
438 display
->set_drawmode(DRMODE_SOLID
);
441 if(data
->img
[n
].bm
.format
== FORMAT_MONO
) {
443 display
->mono_bitmap_part(data
->img
[n
].bm
.data
,
444 0, data
->img
[n
].subimage_height
* subimage
,
445 data
->img
[n
].bm
.width
, data
->img
[n
].x
,
446 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
447 data
->img
[n
].subimage_height
);
450 display
->transparent_bitmap_part((fb_data
*)data
->img
[n
].bm
.data
,
451 0, data
->img
[n
].subimage_height
* subimage
,
452 data
->img
[n
].bm
.width
, data
->img
[n
].x
,
453 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
454 data
->img
[n
].subimage_height
);
459 static void wps_display_images(struct gui_wps
*gwps
, struct viewport
* vp
)
461 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
465 struct wps_data
*data
= gwps
->data
;
466 struct screen
*display
= gwps
->display
;
468 for (n
= 0; n
< MAX_IMAGES
; n
++)
470 if (data
->img
[n
].loaded
)
472 if (data
->img
[n
].display
>= 0)
474 wps_draw_image(gwps
, n
, data
->img
[n
].display
);
475 } else if (data
->img
[n
].always_display
&& data
->img
[n
].vp
== vp
)
477 wps_draw_image(gwps
, n
, 0);
481 display
->set_drawmode(DRMODE_SOLID
);
484 #else /* HAVE_LCD_CHARCELL */
486 static bool draw_player_progress(struct gui_wps
*gwps
)
488 struct wps_state
*state
= gwps
->state
;
489 struct screen
*display
= gwps
->display
;
490 unsigned char progress_pattern
[7];
497 if (state
->id3
->length
)
498 pos
= 36 * (state
->id3
->elapsed
+ state
->ff_rewind_count
)
499 / state
->id3
->length
;
501 for (i
= 0; i
< 7; i
++, pos
-= 5)
504 progress_pattern
[i
] = 0x1fu
;
506 progress_pattern
[i
] = 0x00u
;
508 progress_pattern
[i
] = 0x1fu
>> pos
;
511 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
515 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
517 static const unsigned char numbers
[10][4] = {
518 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
519 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
520 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
521 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
522 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
523 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
524 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
525 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
526 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
527 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
530 struct wps_state
*state
= gwps
->state
;
531 struct screen
*display
= gwps
->display
;
532 struct wps_data
*data
= gwps
->data
;
533 unsigned char progress_pattern
[7];
542 if (!state
->id3
|| buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
545 time
= state
->id3
->elapsed
+ state
->ff_rewind_count
;
546 if (state
->id3
->length
)
547 pos
= 55 * time
/ state
->id3
->length
;
549 memset(timestr
, 0, sizeof(timestr
));
550 format_time(timestr
, sizeof(timestr
)-2, time
);
551 timestr
[strlen(timestr
)] = ':'; /* always safe */
553 for (i
= 0; i
< 11; i
++, pos
-= 5)
556 memset(progress_pattern
, 0, sizeof(progress_pattern
));
558 if ((digit
= timestr
[time_idx
]))
563 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
565 memcpy(progress_pattern
, numbers
[digit
], 4);
568 else /* tens, shifted right */
570 for (j
= 0; j
< 4; j
++)
571 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
573 if (time_idx
> 0) /* not the first group, add colon in front */
575 progress_pattern
[1] |= 0x10u
;
576 progress_pattern
[3] |= 0x10u
;
582 progress_pattern
[5] = progress_pattern
[6] = 0x1fu
;
585 if (pos
> 0 && pos
< 5)
588 progress_pattern
[5] = progress_pattern
[6] = (~0x1fu
>> pos
) & 0x1fu
;
591 if (softchar
&& pat_idx
< 8)
593 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
595 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
599 buf
= utf8encode(' ', buf
);
601 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
606 #endif /* HAVE_LCD_CHARCELL */
608 static char* get_codectype(const struct mp3entry
* id3
)
610 if (id3
->codectype
< AFMT_NUM_CODECS
) {
611 return (char*)audio_formats
[id3
->codectype
].label
;
617 /* Extract a part from a path.
619 * buf - buffer extract part to.
620 * buf_size - size of buffer.
621 * path - path to extract from.
622 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
623 * parent of parent, etc.
625 * Returns buf if the desired level was found, NULL otherwise.
627 static char* get_dir(char* buf
, int buf_size
, const char* path
, int level
)
630 const char* last_sep
;
633 sep
= path
+ strlen(path
);
648 if (level
|| (last_sep
<= sep
))
651 len
= MIN(last_sep
- sep
, buf_size
- 1);
652 strlcpy(buf
, sep
+ 1, len
+ 1);
656 /* Return the tag found at index i and write its value in buf.
657 The return value is buf if the tag had a value, or NULL if not.
659 intval is used with conditionals/enums: when this function is called,
660 intval should contain the number of options in the conditional/enum.
661 When this function returns, intval is -1 if the tag is non numeric or,
662 if the tag is numeric, *intval is the enum case we want to go to (between 1
663 and the original value of *intval, inclusive).
664 When not treating a conditional/enum, intval should be NULL.
666 static const char *get_token_value(struct gui_wps
*gwps
,
667 struct wps_token
*token
,
668 char *buf
, int buf_size
,
674 struct wps_data
*data
= gwps
->data
;
675 struct wps_state
*state
= gwps
->state
;
680 struct mp3entry
*id3
;
691 struct tm
* tm
= NULL
;
693 /* if the token is an RTC one, update the time
694 and do the necessary checks */
695 if (token
->type
>= WPS_TOKENS_RTC_BEGIN
696 && token
->type
<= WPS_TOKENS_RTC_END
)
714 case WPS_TOKEN_CHARACTER
:
715 return &(token
->value
.c
);
717 case WPS_TOKEN_STRING
:
718 return data
->strings
[token
->value
.i
];
720 case WPS_TOKEN_TRACK_TIME_ELAPSED
:
721 format_time(buf
, buf_size
,
722 id3
->elapsed
+ state
->ff_rewind_count
);
725 case WPS_TOKEN_TRACK_TIME_REMAINING
:
726 format_time(buf
, buf_size
,
727 id3
->length
- id3
->elapsed
-
728 state
->ff_rewind_count
);
731 case WPS_TOKEN_TRACK_LENGTH
:
732 format_time(buf
, buf_size
, id3
->length
);
735 case WPS_TOKEN_PLAYLIST_ENTRIES
:
736 snprintf(buf
, buf_size
, "%d", playlist_amount());
739 case WPS_TOKEN_PLAYLIST_NAME
:
740 return playlist_name(NULL
, buf
, buf_size
);
742 case WPS_TOKEN_PLAYLIST_POSITION
:
743 snprintf(buf
, buf_size
, "%d", playlist_get_display_index());
746 case WPS_TOKEN_PLAYLIST_SHUFFLE
:
747 if ( global_settings
.playlist_shuffle
)
753 case WPS_TOKEN_VOLUME
:
754 snprintf(buf
, buf_size
, "%d", global_settings
.volume
);
757 if (global_settings
.volume
== sound_min(SOUND_VOLUME
))
761 else if (global_settings
.volume
== 0)
765 else if (global_settings
.volume
> 0)
771 *intval
= (limit
- 3) * (global_settings
.volume
772 - sound_min(SOUND_VOLUME
) - 1)
773 / (-1 - sound_min(SOUND_VOLUME
)) + 2;
778 case WPS_TOKEN_TRACK_ELAPSED_PERCENT
:
779 if (id3
->length
<= 0)
784 *intval
= limit
* (id3
->elapsed
+ state
->ff_rewind_count
)
787 snprintf(buf
, buf_size
, "%d",
788 100*(id3
->elapsed
+ state
->ff_rewind_count
) / id3
->length
);
791 case WPS_TOKEN_METADATA_ARTIST
:
794 case WPS_TOKEN_METADATA_COMPOSER
:
795 return id3
->composer
;
797 case WPS_TOKEN_METADATA_ALBUM
:
800 case WPS_TOKEN_METADATA_ALBUM_ARTIST
:
801 return id3
->albumartist
;
803 case WPS_TOKEN_METADATA_GROUPING
:
804 return id3
->grouping
;
806 case WPS_TOKEN_METADATA_GENRE
:
807 return id3
->genre_string
;
809 case WPS_TOKEN_METADATA_DISC_NUMBER
:
810 if (id3
->disc_string
)
811 return id3
->disc_string
;
813 snprintf(buf
, buf_size
, "%d", id3
->discnum
);
818 case WPS_TOKEN_METADATA_TRACK_NUMBER
:
819 if (id3
->track_string
)
820 return id3
->track_string
;
823 snprintf(buf
, buf_size
, "%d", id3
->tracknum
);
828 case WPS_TOKEN_METADATA_TRACK_TITLE
:
831 case WPS_TOKEN_METADATA_VERSION
:
832 switch (id3
->id3version
)
853 case WPS_TOKEN_METADATA_YEAR
:
854 if( id3
->year_string
)
855 return id3
->year_string
;
858 snprintf(buf
, buf_size
, "%d", id3
->year
);
863 case WPS_TOKEN_METADATA_COMMENT
:
867 case WPS_TOKEN_ALBUMART_DISPLAY
:
868 draw_album_art(gwps
, audio_current_aa_hid(), false);
871 case WPS_TOKEN_ALBUMART_FOUND
:
872 if (audio_current_aa_hid() >= 0) {
878 case WPS_TOKEN_FILE_BITRATE
:
880 snprintf(buf
, buf_size
, "%d", id3
->bitrate
);
885 case WPS_TOKEN_FILE_CODEC
:
888 if(id3
->codectype
== AFMT_UNKNOWN
)
889 *intval
= AFMT_NUM_CODECS
;
891 *intval
= id3
->codectype
;
893 return get_codectype(id3
);
895 case WPS_TOKEN_FILE_FREQUENCY
:
896 snprintf(buf
, buf_size
, "%ld", id3
->frequency
);
899 case WPS_TOKEN_FILE_FREQUENCY_KHZ
:
900 /* ignore remainders < 100, so 22050 Hz becomes just 22k */
901 if ((id3
->frequency
% 1000) < 100)
902 snprintf(buf
, buf_size
, "%ld", id3
->frequency
/ 1000);
904 snprintf(buf
, buf_size
, "%ld.%d",
905 id3
->frequency
/ 1000,
906 (id3
->frequency
% 1000) / 100);
909 case WPS_TOKEN_FILE_NAME
:
910 if (get_dir(buf
, buf_size
, id3
->path
, 0)) {
911 /* Remove extension */
912 char* sep
= strrchr(buf
, '.');
922 case WPS_TOKEN_FILE_NAME_WITH_EXTENSION
:
923 return get_dir(buf
, buf_size
, id3
->path
, 0);
925 case WPS_TOKEN_FILE_PATH
:
928 case WPS_TOKEN_FILE_SIZE
:
929 snprintf(buf
, buf_size
, "%ld", id3
->filesize
/ 1024);
932 case WPS_TOKEN_FILE_VBR
:
933 return id3
->vbr
? "(avg)" : NULL
;
935 case WPS_TOKEN_FILE_DIRECTORY
:
936 return get_dir(buf
, buf_size
, id3
->path
, token
->value
.i
);
938 case WPS_TOKEN_BATTERY_PERCENT
:
940 int l
= battery_level();
944 limit
= MAX(limit
, 2);
946 /* First enum is used for "unknown level". */
947 *intval
= (limit
- 1) * l
/ 100 + 2;
954 snprintf(buf
, buf_size
, "%d", l
);
961 case WPS_TOKEN_BATTERY_VOLTS
:
963 unsigned int v
= battery_voltage();
964 snprintf(buf
, buf_size
, "%d.%02d", v
/ 1000, (v
% 1000) / 10);
968 case WPS_TOKEN_BATTERY_TIME
:
970 int t
= battery_time();
972 snprintf(buf
, buf_size
, "%dh %dm", t
/ 60, t
% 60);
979 case WPS_TOKEN_BATTERY_CHARGER_CONNECTED
:
981 if(charger_input_state
==CHARGER
)
987 #if CONFIG_CHARGING >= CHARGING_MONITOR
988 case WPS_TOKEN_BATTERY_CHARGING
:
990 if (charge_state
== CHARGING
|| charge_state
== TOPOFF
) {
997 case WPS_TOKEN_BATTERY_SLEEPTIME
:
999 if (get_sleep_timer() == 0)
1003 format_time(buf
, buf_size
, get_sleep_timer() * 1000);
1008 case WPS_TOKEN_PLAYBACK_STATUS
:
1010 int status
= audio_status();
1012 if (status
== AUDIO_STATUS_PLAY
)
1014 if (wps_fading_out
||
1015 (status
& AUDIO_STATUS_PAUSE
&& !status_get_ffmode()))
1017 if (status_get_ffmode() == STATUS_FASTFORWARD
)
1019 if (status_get_ffmode() == STATUS_FASTBACKWARD
)
1026 snprintf(buf
, buf_size
, "%d", mode
-1);
1030 case WPS_TOKEN_REPEAT_MODE
:
1032 *intval
= global_settings
.repeat_mode
+ 1;
1033 snprintf(buf
, buf_size
, "%d", global_settings
.repeat_mode
);
1036 case WPS_TOKEN_RTC_PRESENT
:
1044 case WPS_TOKEN_RTC_12HOUR_CFG
:
1046 *intval
= global_settings
.timeformat
+ 1;
1047 snprintf(buf
, buf_size
, "%d", global_settings
.timeformat
);
1050 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1051 /* d: day of month (01..31) */
1052 snprintf(buf
, buf_size
, "%02d", tm
->tm_mday
);
1055 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1056 /* e: day of month, blank padded ( 1..31) */
1057 snprintf(buf
, buf_size
, "%2d", tm
->tm_mday
);
1060 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1061 /* H: hour (00..23) */
1062 snprintf(buf
, buf_size
, "%02d", tm
->tm_hour
);
1065 case WPS_TOKEN_RTC_HOUR_24
:
1066 /* k: hour ( 0..23) */
1067 snprintf(buf
, buf_size
, "%2d", tm
->tm_hour
);
1070 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1071 /* I: hour (01..12) */
1072 snprintf(buf
, buf_size
, "%02d",
1073 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1076 case WPS_TOKEN_RTC_HOUR_12
:
1077 /* l: hour ( 1..12) */
1078 snprintf(buf
, buf_size
, "%2d",
1079 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1082 case WPS_TOKEN_RTC_MONTH
:
1083 /* m: month (01..12) */
1085 *intval
= tm
->tm_mon
+ 1;
1086 snprintf(buf
, buf_size
, "%02d", tm
->tm_mon
+ 1);
1089 case WPS_TOKEN_RTC_MINUTE
:
1090 /* M: minute (00..59) */
1091 snprintf(buf
, buf_size
, "%02d", tm
->tm_min
);
1094 case WPS_TOKEN_RTC_SECOND
:
1095 /* S: second (00..59) */
1096 snprintf(buf
, buf_size
, "%02d", tm
->tm_sec
);
1099 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1100 /* y: last two digits of year (00..99) */
1101 snprintf(buf
, buf_size
, "%02d", tm
->tm_year
% 100);
1104 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1105 /* Y: year (1970...) */
1106 snprintf(buf
, buf_size
, "%04d", tm
->tm_year
+ 1900);
1109 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1110 /* p: upper case AM or PM indicator */
1111 return tm
->tm_hour
/12 == 0 ? "AM" : "PM";
1113 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1114 /* P: lower case am or pm indicator */
1115 return tm
->tm_hour
/12 == 0 ? "am" : "pm";
1117 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1118 /* a: abbreviated weekday name (Sun..Sat) */
1119 return str(LANG_WEEKDAY_SUNDAY
+ tm
->tm_wday
);
1121 case WPS_TOKEN_RTC_MONTH_NAME
:
1122 /* b: abbreviated month name (Jan..Dec) */
1123 return str(LANG_MONTH_JANUARY
+ tm
->tm_mon
);
1125 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1126 /* u: day of week (1..7); 1 is Monday */
1128 *intval
= (tm
->tm_wday
== 0) ? 7 : tm
->tm_wday
;
1129 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
+ 1);
1132 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1133 /* w: day of week (0..6); 0 is Sunday */
1135 *intval
= tm
->tm_wday
+ 1;
1136 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
);
1139 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1140 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1141 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1142 case WPS_TOKEN_RTC_HOUR_24
:
1143 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1144 case WPS_TOKEN_RTC_HOUR_12
:
1145 case WPS_TOKEN_RTC_MONTH
:
1146 case WPS_TOKEN_RTC_MINUTE
:
1147 case WPS_TOKEN_RTC_SECOND
:
1148 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1149 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1150 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1152 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1154 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1155 case WPS_TOKEN_RTC_MONTH_NAME
:
1157 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1158 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1162 #ifdef HAVE_LCD_CHARCELLS
1163 case WPS_TOKEN_PROGRESSBAR
:
1165 char *end
= utf8encode(data
->wps_progress_pat
[0], buf
);
1170 case WPS_TOKEN_PLAYER_PROGRESSBAR
:
1173 /* we need 11 characters (full line) for
1175 strlcpy(buf
, " ", buf_size
);
1179 /* Tell the user if we have an OldPlayer */
1180 strlcpy(buf
, " <Old LCD> ", buf_size
);
1185 #ifdef HAVE_TAGCACHE
1186 case WPS_TOKEN_DATABASE_PLAYCOUNT
:
1188 *intval
= id3
->playcount
+ 1;
1190 snprintf(buf
, buf_size
, "%ld", id3
->playcount
);
1193 case WPS_TOKEN_DATABASE_RATING
:
1195 *intval
= id3
->rating
+ 1;
1197 snprintf(buf
, buf_size
, "%d", id3
->rating
);
1200 case WPS_TOKEN_DATABASE_AUTOSCORE
:
1202 *intval
= id3
->score
+ 1;
1204 snprintf(buf
, buf_size
, "%d", id3
->score
);
1208 #if (CONFIG_CODEC == SWCODEC)
1209 case WPS_TOKEN_CROSSFADE
:
1211 *intval
= global_settings
.crossfade
+ 1;
1212 snprintf(buf
, buf_size
, "%d", global_settings
.crossfade
);
1215 case WPS_TOKEN_REPLAYGAIN
:
1219 if (global_settings
.replaygain_type
== REPLAYGAIN_OFF
)
1224 get_replaygain_mode(id3
->track_gain_string
!= NULL
,
1225 id3
->album_gain_string
!= NULL
);
1227 val
= 6; /* no tag */
1231 if (global_settings
.replaygain_type
== REPLAYGAIN_SHUFFLE
)
1246 strlcpy(buf
, id3
->track_gain_string
, buf_size
);
1250 strlcpy(buf
, id3
->album_gain_string
, buf_size
);
1255 #endif /* (CONFIG_CODEC == SWCODEC) */
1257 #if (CONFIG_CODEC != MAS3507D)
1258 case WPS_TOKEN_SOUND_PITCH
:
1260 int val
= sound_get_pitch();
1261 snprintf(buf
, buf_size
, "%d.%d",
1262 val
/ 10, val
% 10);
1267 case WPS_TOKEN_MAIN_HOLD
:
1268 #ifdef HAS_BUTTON_HOLD
1271 if (is_keys_locked())
1272 #endif /*hold switch or softlock*/
1277 #ifdef HAS_REMOTE_BUTTON_HOLD
1278 case WPS_TOKEN_REMOTE_HOLD
:
1279 if (remote_button_hold())
1285 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
1286 case WPS_TOKEN_VLED_HDD
:
1292 case WPS_TOKEN_BUTTON_VOLUME
:
1293 if (data
->button_time_volume
&&
1294 TIME_BEFORE(current_tick
, data
->button_time_volume
+
1295 token
->value
.i
* TIMEOUT_UNIT
))
1298 case WPS_TOKEN_LASTTOUCH
:
1299 #ifdef HAVE_TOUCHSCREEN
1300 if (TIME_BEFORE(current_tick
, token
->value
.i
* TIMEOUT_UNIT
+
1301 touchscreen_last_touch()))
1306 case WPS_TOKEN_SETTING
:
1310 /* Handle contionals */
1311 const struct settings_list
*s
= settings
+token
->value
.i
;
1312 switch (s
->flags
&F_T_MASK
)
1317 /* %?St|name|<#000000|#000001|...|#FFFFFF> */
1318 /* shouldn't overflow since colors are stored
1320 * but this is pretty useless anyway */
1321 *intval
= *(int*)s
->setting
+ 1;
1322 else if (s
->cfg_vals
== NULL
)
1323 /* %?St|name|<1st choice|2nd choice|...> */
1324 *intval
= (*(int*)s
->setting
-s
->int_setting
->min
)
1325 /s
->int_setting
->step
+ 1;
1327 /* %?St|name|<1st choice|2nd choice|...> */
1328 /* Not sure about this one. cfg_name/vals are
1329 * indexed from 0 right? */
1330 *intval
= *(int*)s
->setting
+ 1;
1333 /* %?St|name|<if true|if false> */
1334 *intval
= *(bool*)s
->setting
?1:2;
1337 /* %?St|name|<if non empty string|if empty>
1338 * The string's emptyness discards the setting's
1339 * prefix and suffix */
1340 *intval
= ((char*)s
->setting
)[0]?1:2;
1343 /* This shouldn't happen ... but you never know */
1348 cfg_to_string(token
->value
.i
,buf
,buf_size
);
1357 /* Return the index to the end token for the conditional token at index.
1358 The conditional token can be either a start token or a separator
1359 (i.e. option) token.
1361 static int find_conditional_end(struct wps_data
*data
, int index
)
1364 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
1365 ret
= data
->tokens
[ret
].value
.i
;
1367 /* ret now is the index to the end token for the conditional. */
1371 /* Evaluate the conditional that is at *token_index and return whether a skip
1372 has ocurred. *token_index is updated with the new position.
1374 static bool evaluate_conditional(struct gui_wps
*gwps
, int *token_index
)
1379 struct wps_data
*data
= gwps
->data
;
1382 int cond_index
= *token_index
;
1385 unsigned char num_options
= data
->tokens
[cond_index
].value
.i
& 0xFF;
1386 unsigned char prev_val
= (data
->tokens
[cond_index
].value
.i
& 0xFF00) >> 8;
1388 /* treat ?xx<true> constructs as if they had 2 options. */
1389 if (num_options
< 2)
1392 int intval
= num_options
;
1393 /* get_token_value needs to know the number of options in the enum */
1394 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
1395 result
, sizeof(result
), &intval
);
1397 /* intval is now the number of the enum option we want to read,
1398 starting from 1. If intval is -1, we check if value is empty. */
1400 intval
= (value
&& *value
) ? 1 : num_options
;
1401 else if (intval
> num_options
|| intval
< 1)
1402 intval
= num_options
;
1404 data
->tokens
[cond_index
].value
.i
= (intval
<< 8) + num_options
;
1406 /* skip to the appropriate enum case */
1407 int next
= cond_index
+ 2;
1408 for (i
= 1; i
< intval
; i
++)
1410 next
= data
->tokens
[next
].value
.i
;
1412 *token_index
= next
;
1414 if (prev_val
== intval
)
1416 /* Same conditional case as previously. Return without clearing the
1421 cond_end
= find_conditional_end(data
, cond_index
+ 2);
1422 for (i
= cond_index
+ 3; i
< cond_end
; i
++)
1424 #ifdef HAVE_LCD_BITMAP
1425 /* clear all pictures in the conditional and nested ones */
1426 if (data
->tokens
[i
].type
== WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
)
1427 clear_image_pos(gwps
, data
->tokens
[i
].value
.i
& 0xFF);
1429 #ifdef HAVE_ALBUMART
1430 if (data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
1431 draw_album_art(gwps
, audio_current_aa_hid(), true);
1438 /* Read a (sub)line to the given alignment format buffer.
1439 linebuf is the buffer where the data is actually stored.
1440 align is the alignment format that'll be used to display the text.
1441 The return value indicates whether the line needs to be updated.
1443 static bool get_line(struct gui_wps
*gwps
,
1444 int line
, int subline
,
1445 struct align_pos
*align
,
1449 struct wps_data
*data
= gwps
->data
;
1452 char *buf
= linebuf
; /* will always point to the writing position */
1453 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
1454 int i
, last_token_idx
;
1455 bool update
= false;
1457 /* alignment-related variables */
1459 char* cur_align_start
;
1460 cur_align_start
= buf
;
1461 cur_align
= WPS_ALIGN_LEFT
;
1463 align
->center
= NULL
;
1464 align
->right
= NULL
;
1466 /* Process all tokens of the desired subline */
1467 last_token_idx
= wps_last_token_index(data
, line
, subline
);
1468 for (i
= wps_first_token_index(data
, line
, subline
);
1469 i
<= last_token_idx
; i
++)
1471 switch(data
->tokens
[i
].type
)
1473 case WPS_TOKEN_CONDITIONAL
:
1474 /* place ourselves in the right conditional case */
1475 update
|= evaluate_conditional(gwps
, &i
);
1478 case WPS_TOKEN_CONDITIONAL_OPTION
:
1479 /* we've finished in the curent conditional case,
1480 skip to the end of the conditional structure */
1481 i
= find_conditional_end(data
, i
);
1484 #ifdef HAVE_LCD_BITMAP
1485 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
1487 struct gui_img
*img
= data
->img
;
1488 int n
= data
->tokens
[i
].value
.i
& 0xFF;
1489 int subimage
= data
->tokens
[i
].value
.i
>> 8;
1491 if (n
>= 0 && n
< MAX_IMAGES
&& img
[n
].loaded
)
1492 img
[n
].display
= subimage
;
1497 case WPS_TOKEN_ALIGN_LEFT
:
1498 case WPS_TOKEN_ALIGN_CENTER
:
1499 case WPS_TOKEN_ALIGN_RIGHT
:
1500 /* remember where the current aligned text started */
1503 case WPS_ALIGN_LEFT
:
1504 align
->left
= cur_align_start
;
1507 case WPS_ALIGN_CENTER
:
1508 align
->center
= cur_align_start
;
1511 case WPS_ALIGN_RIGHT
:
1512 align
->right
= cur_align_start
;
1515 /* start a new alignment */
1516 switch (data
->tokens
[i
].type
)
1518 case WPS_TOKEN_ALIGN_LEFT
:
1519 cur_align
= WPS_ALIGN_LEFT
;
1521 case WPS_TOKEN_ALIGN_CENTER
:
1522 cur_align
= WPS_ALIGN_CENTER
;
1524 case WPS_TOKEN_ALIGN_RIGHT
:
1525 cur_align
= WPS_ALIGN_RIGHT
;
1531 cur_align_start
= buf
;
1533 case WPS_VIEWPORT_ENABLE
:
1535 char label
= data
->tokens
[i
].value
.i
;
1537 char temp
= VP_DRAW_HIDEABLE
;
1538 for(j
=0;j
<data
->num_viewports
;j
++)
1540 temp
= VP_DRAW_HIDEABLE
;
1541 if ((data
->viewports
[j
].hidden_flags
&VP_DRAW_HIDEABLE
) &&
1542 (data
->viewports
[j
].label
== label
))
1544 if (data
->viewports
[j
].hidden_flags
&VP_DRAW_WASHIDDEN
)
1545 temp
|= VP_DRAW_WASHIDDEN
;
1546 data
->viewports
[j
].hidden_flags
= temp
;
1553 /* get the value of the tag and copy it to the buffer */
1554 const char *value
= get_token_value(gwps
, &data
->tokens
[i
],
1555 temp_buf
, sizeof(temp_buf
), NULL
);
1559 while (*value
&& (buf
< linebuf_end
))
1567 /* close the current alignment */
1570 case WPS_ALIGN_LEFT
:
1571 align
->left
= cur_align_start
;
1574 case WPS_ALIGN_CENTER
:
1575 align
->center
= cur_align_start
;
1578 case WPS_ALIGN_RIGHT
:
1579 align
->right
= cur_align_start
;
1586 static void get_subline_timeout(struct gui_wps
*gwps
, int line
, int subline
)
1588 struct wps_data
*data
= gwps
->data
;
1590 int subline_idx
= wps_subline_index(data
, line
, subline
);
1591 int last_token_idx
= wps_last_token_index(data
, line
, subline
);
1593 data
->sublines
[subline_idx
].time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
1595 for (i
= wps_first_token_index(data
, line
, subline
);
1596 i
<= last_token_idx
; i
++)
1598 switch(data
->tokens
[i
].type
)
1600 case WPS_TOKEN_CONDITIONAL
:
1601 /* place ourselves in the right conditional case */
1602 evaluate_conditional(gwps
, &i
);
1605 case WPS_TOKEN_CONDITIONAL_OPTION
:
1606 /* we've finished in the curent conditional case,
1607 skip to the end of the conditional structure */
1608 i
= find_conditional_end(data
, i
);
1611 case WPS_TOKEN_SUBLINE_TIMEOUT
:
1612 data
->sublines
[subline_idx
].time_mult
= data
->tokens
[i
].value
.i
;
1621 /* Calculates which subline should be displayed for the specified line
1622 Returns true iff the subline must be refreshed */
1623 static bool update_curr_subline(struct gui_wps
*gwps
, int line
)
1625 struct wps_data
*data
= gwps
->data
;
1627 int search
, search_start
, num_sublines
;
1629 bool new_subline_refresh
;
1630 bool only_one_subline
;
1632 num_sublines
= data
->lines
[line
].num_sublines
;
1633 reset_subline
= (data
->lines
[line
].curr_subline
== SUBLINE_RESET
);
1634 new_subline_refresh
= false;
1635 only_one_subline
= false;
1637 /* if time to advance to next sub-line */
1638 if (TIME_AFTER(current_tick
, data
->lines
[line
].subline_expire_time
- 1) ||
1641 /* search all sublines until the next subline with time > 0
1642 is found or we get back to the subline we started with */
1646 search_start
= data
->lines
[line
].curr_subline
;
1648 for (search
= 0; search
< num_sublines
; search
++)
1650 data
->lines
[line
].curr_subline
++;
1652 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
1653 if (data
->lines
[line
].curr_subline
== num_sublines
)
1655 if (data
->lines
[line
].curr_subline
== 1)
1656 only_one_subline
= true;
1657 data
->lines
[line
].curr_subline
= 0;
1660 /* if back where we started after search or
1661 only one subline is defined on the line */
1662 if (((search
> 0) &&
1663 (data
->lines
[line
].curr_subline
== search_start
)) ||
1666 /* no other subline with a time > 0 exists */
1667 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1669 data
->lines
[line
].subline_expire_time
) + 100 * HZ
;
1674 /* get initial time multiplier for this subline */
1675 get_subline_timeout(gwps
, line
, data
->lines
[line
].curr_subline
);
1677 int subline_idx
= wps_subline_index(data
, line
,
1678 data
->lines
[line
].curr_subline
);
1680 /* only use this subline if subline time > 0 */
1681 if (data
->sublines
[subline_idx
].time_mult
> 0)
1683 new_subline_refresh
= true;
1684 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1685 current_tick
: data
->lines
[line
].subline_expire_time
) +
1686 TIMEOUT_UNIT
*data
->sublines
[subline_idx
].time_mult
;
1693 return new_subline_refresh
;
1696 /* Display a line appropriately according to its alignment format.
1697 format_align contains the text, separated between left, center and right.
1698 line is the index of the line on the screen.
1699 scroll indicates whether the line is a scrolling one or not.
1701 static void write_line(struct screen
*display
,
1702 struct align_pos
*format_align
,
1706 int left_width
= 0, left_xpos
;
1707 int center_width
= 0, center_xpos
;
1708 int right_width
= 0, right_xpos
;
1714 /* calculate different string sizes and positions */
1715 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
1716 if (format_align
->left
!= 0) {
1717 display
->getstringsize((unsigned char *)format_align
->left
,
1718 &left_width
, &string_height
);
1721 if (format_align
->right
!= 0) {
1722 display
->getstringsize((unsigned char *)format_align
->right
,
1723 &right_width
, &string_height
);
1726 if (format_align
->center
!= 0) {
1727 display
->getstringsize((unsigned char *)format_align
->center
,
1728 ¢er_width
, &string_height
);
1732 right_xpos
= (display
->getwidth() - right_width
);
1733 center_xpos
= (display
->getwidth() + left_xpos
- center_width
) / 2;
1735 scroll_width
= display
->getwidth() - left_xpos
;
1737 /* Checks for overlapping strings.
1738 If needed the overlapping strings will be merged, separated by a
1741 /* CASE 1: left and centered string overlap */
1742 /* there is a left string, need to merge left and center */
1743 if ((left_width
!= 0 && center_width
!= 0) &&
1744 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
1745 /* replace the former separator '\0' of left and
1746 center string with a space */
1747 *(--format_align
->center
) = ' ';
1748 /* calculate the new width and position of the merged string */
1749 left_width
= left_width
+ space_width
+ center_width
;
1750 /* there is no centered string anymore */
1753 /* there is no left string, move center to left */
1754 if ((left_width
== 0 && center_width
!= 0) &&
1755 (left_xpos
+ left_width
> center_xpos
)) {
1756 /* move the center string to the left string */
1757 format_align
->left
= format_align
->center
;
1758 /* calculate the new width and position of the string */
1759 left_width
= center_width
;
1760 /* there is no centered string anymore */
1764 /* CASE 2: centered and right string overlap */
1765 /* there is a right string, need to merge center and right */
1766 if ((center_width
!= 0 && right_width
!= 0) &&
1767 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
1768 /* replace the former separator '\0' of center and
1769 right string with a space */
1770 *(--format_align
->right
) = ' ';
1771 /* move the center string to the right after merge */
1772 format_align
->right
= format_align
->center
;
1773 /* calculate the new width and position of the merged string */
1774 right_width
= center_width
+ space_width
+ right_width
;
1775 right_xpos
= (display
->getwidth() - right_width
);
1776 /* there is no centered string anymore */
1779 /* there is no right string, move center to right */
1780 if ((center_width
!= 0 && right_width
== 0) &&
1781 (center_xpos
+ center_width
> right_xpos
)) {
1782 /* move the center string to the right string */
1783 format_align
->right
= format_align
->center
;
1784 /* calculate the new width and position of the string */
1785 right_width
= center_width
;
1786 right_xpos
= (display
->getwidth() - right_width
);
1787 /* there is no centered string anymore */
1791 /* CASE 3: left and right overlap
1792 There is no center string anymore, either there never
1793 was one or it has been merged in case 1 or 2 */
1794 /* there is a left string, need to merge left and right */
1795 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
1796 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
1797 /* replace the former separator '\0' of left and
1798 right string with a space */
1799 *(--format_align
->right
) = ' ';
1800 /* calculate the new width and position of the string */
1801 left_width
= left_width
+ space_width
+ right_width
;
1802 /* there is no right string anymore */
1805 /* there is no left string, move right to left */
1806 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
1807 (left_width
> right_xpos
)) {
1808 /* move the right string to the left string */
1809 format_align
->left
= format_align
->right
;
1810 /* calculate the new width and position of the string */
1811 left_width
= right_width
;
1812 /* there is no right string anymore */
1816 ypos
= (line
* string_height
);
1819 if (scroll
&& ((left_width
> scroll_width
) ||
1820 (center_width
> scroll_width
) ||
1821 (right_width
> scroll_width
)))
1823 display
->puts_scroll(0, line
,
1824 (unsigned char *)format_align
->left
);
1828 #ifdef HAVE_LCD_BITMAP
1829 /* clear the line first */
1830 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1831 display
->fillrect(left_xpos
, ypos
, display
->getwidth(), string_height
);
1832 display
->set_drawmode(DRMODE_SOLID
);
1835 /* Nasty hack: we output an empty scrolling string,
1836 which will reset the scroller for that line */
1837 display
->puts_scroll(0, line
, (unsigned char *)"");
1839 /* print aligned strings */
1840 if (left_width
!= 0)
1842 display
->putsxy(left_xpos
, ypos
,
1843 (unsigned char *)format_align
->left
);
1845 if (center_width
!= 0)
1847 display
->putsxy(center_xpos
, ypos
,
1848 (unsigned char *)format_align
->center
);
1850 if (right_width
!= 0)
1852 display
->putsxy(right_xpos
, ypos
,
1853 (unsigned char *)format_align
->right
);
1858 bool gui_wps_redraw(struct gui_wps
*gwps
,
1860 unsigned refresh_mode
)
1862 struct wps_data
*data
= gwps
->data
;
1863 struct screen
*display
= gwps
->display
;
1864 struct wps_state
*state
= gwps
->state
;
1866 if (!data
|| !state
|| !display
)
1869 struct mp3entry
*id3
= state
->id3
;
1874 int v
, line
, i
, subline_idx
;
1876 char linebuf
[MAX_PATH
];
1878 struct align_pos align
;
1880 align
.center
= NULL
;
1883 bool update_line
, new_subline_refresh
;
1885 #ifdef HAVE_LCD_BITMAP
1887 /* to find out wether the peak meter is enabled we
1888 assume it wasn't until we find a line that contains
1889 the peak meter. We can't use peak_meter_enabled itself
1890 because that would mean to turn off the meter thread
1891 temporarily. (That shouldn't matter unless yield
1892 or sleep is called but who knows...)
1894 bool enable_pm
= false;
1898 /* reset to first subline if refresh all flag is set */
1899 if (refresh_mode
== WPS_REFRESH_ALL
)
1901 display
->set_viewport(&data
->viewports
[0].vp
);
1902 display
->clear_viewport();
1904 for (i
= 0; i
<= data
->num_lines
; i
++)
1906 data
->lines
[i
].curr_subline
= SUBLINE_RESET
;
1910 #ifdef HAVE_LCD_CHARCELLS
1911 for (i
= 0; i
< 8; i
++)
1913 if (data
->wps_progress_pat
[i
] == 0)
1914 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1918 state
->ff_rewind_count
= ffwd_offset
;
1920 /* disable any viewports which are conditionally displayed */
1921 for (v
= 0; v
< data
->num_viewports
; v
++)
1923 if (data
->viewports
[v
].hidden_flags
&VP_DRAW_HIDEABLE
)
1925 if (data
->viewports
[v
].hidden_flags
&VP_DRAW_HIDDEN
)
1926 data
->viewports
[v
].hidden_flags
|= VP_DRAW_WASHIDDEN
;
1928 data
->viewports
[v
].hidden_flags
|= VP_DRAW_HIDDEN
;
1931 for (v
= 0; v
< data
->num_viewports
; v
++)
1933 struct wps_viewport
*wps_vp
= &(data
->viewports
[v
]);
1934 unsigned vp_refresh_mode
= refresh_mode
;
1935 display
->set_viewport(&wps_vp
->vp
);
1937 #ifdef HAVE_LCD_BITMAP
1938 /* Set images to not to be displayed */
1939 for (i
= 0; i
< MAX_IMAGES
; i
++)
1941 data
->img
[i
].display
= -1;
1944 /* dont redraw the viewport if its disabled */
1945 if ((wps_vp
->hidden_flags
&VP_DRAW_HIDDEN
))
1947 if (!(wps_vp
->hidden_flags
&VP_DRAW_WASHIDDEN
))
1948 display
->scroll_stop(&wps_vp
->vp
);
1949 wps_vp
->hidden_flags
|= VP_DRAW_WASHIDDEN
;
1952 else if (((wps_vp
->hidden_flags
&
1953 (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
))
1954 == (VP_DRAW_WASHIDDEN
|VP_DRAW_HIDEABLE
)))
1956 vp_refresh_mode
= WPS_REFRESH_ALL
;
1957 wps_vp
->hidden_flags
= VP_DRAW_HIDEABLE
;
1959 if (vp_refresh_mode
== WPS_REFRESH_ALL
)
1961 display
->clear_viewport();
1964 for (line
= wps_vp
->first_line
;
1965 line
<= wps_vp
->last_line
; line
++)
1967 memset(linebuf
, 0, sizeof(linebuf
));
1968 update_line
= false;
1970 /* get current subline for the line */
1971 new_subline_refresh
= update_curr_subline(gwps
, line
);
1973 subline_idx
= wps_subline_index(data
, line
,
1974 data
->lines
[line
].curr_subline
);
1975 flags
= data
->sublines
[subline_idx
].line_type
;
1977 if (vp_refresh_mode
== WPS_REFRESH_ALL
|| (flags
& vp_refresh_mode
)
1978 || new_subline_refresh
)
1980 /* get_line tells us if we need to update the line */
1981 update_line
= get_line(gwps
, line
, data
->lines
[line
].curr_subline
,
1982 &align
, linebuf
, sizeof(linebuf
));
1984 #ifdef HAVE_LCD_BITMAP
1986 if (flags
& vp_refresh_mode
& WPS_REFRESH_PEAK_METER
)
1988 /* the peakmeter should be alone on its line */
1989 update_line
= false;
1991 int h
= font_get(wps_vp
->vp
.font
)->height
;
1992 int peak_meter_y
= (line
- wps_vp
->first_line
)* h
;
1994 /* The user might decide to have the peak meter in the last
1995 line so that it is only displayed if no status bar is
1996 visible. If so we neither want do draw nor enable the
1998 if (peak_meter_y
+ h
<= display
->getheight()) {
1999 /* found a line with a peak meter -> remember that we must
2002 peak_meter_enabled
= true;
2003 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
2004 MIN(h
, display
->getheight() - peak_meter_y
));
2008 peak_meter_enabled
= false;
2012 #else /* HAVE_LCD_CHARCELL */
2015 if (flags
& vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
2017 if (data
->full_line_progressbar
)
2018 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
2020 draw_player_progress(gwps
);
2025 /* conditionals clear the line which means if the %Vd is put into the default
2026 viewport there will be a blank line.
2027 To get around this we dont allow any actual drawing to happen in the
2028 deault vp if other vp's are defined */
2029 ((data
->num_viewports
>1 && v
!=0) || data
->num_viewports
== 1))
2031 if (flags
& WPS_REFRESH_SCROLL
)
2033 /* if the line is a scrolling one we don't want to update
2034 too often, so that it has the time to scroll */
2035 if ((vp_refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
2036 write_line(display
, &align
, line
- wps_vp
->first_line
, true);
2039 write_line(display
, &align
, line
- wps_vp
->first_line
, false);
2043 #ifdef HAVE_LCD_BITMAP
2045 if (vp_refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
2049 draw_progressbar(gwps
, wps_vp
);
2052 /* Now display any images in this viewport */
2053 wps_display_images(gwps
, &wps_vp
->vp
);
2057 #ifdef HAVE_LCD_BITMAP
2058 data
->peak_meter_enabled
= enable_pm
;
2061 if (refresh_mode
& WPS_REFRESH_STATUSBAR
)
2063 gwps_draw_statusbars();
2065 /* Restore the default viewport */
2066 display
->set_viewport(NULL
);