1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2007 Nicolas Pennequin
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 ****************************************************************************/
19 #include "gwps-common.h"
26 #include "rbunicode.h"
31 #include "powermgmt.h"
34 #ifdef HAVE_LCD_CHARCELLS
38 #include "mp3_playback.h"
39 #include "backlight.h"
43 #include "scrollbar.h"
46 #ifdef HAVE_LCD_BITMAP
47 #include "peakmeter.h"
57 #if (LCD_DEPTH > 1) || (defined(HAVE_LCD_REMOTE) && (LCD_REMOTE_DEPTH > 1))
61 #define FF_REWIND_MAX_PERCENT 3 /* cap ff/rewind step size at max % of file */
62 /* 3% of 30min file == 54s step size */
63 #define MIN_FF_REWIND_STEP 500
65 /* draws the statusbar on the given wps-screen */
66 #ifdef HAVE_LCD_BITMAP
67 void gui_wps_statusbar_draw(struct gui_wps
*wps
, bool force
)
69 bool draw
= global_settings
.statusbar
;
71 if (wps
->data
->wps_sb_tag
)
72 draw
= wps
->data
->show_sb_on_wps
;
75 gui_statusbar_draw(wps
->statusbar
, force
);
78 #define gui_wps_statusbar_draw(wps, force) \
79 gui_statusbar_draw((wps)->statusbar, (force))
82 /* fades the volume */
83 void fade(bool fade_in
)
85 int fp_global_vol
= global_settings
.volume
<< 8;
86 int fp_min_vol
= sound_min(SOUND_VOLUME
) << 8;
87 int fp_step
= (fp_global_vol
- fp_min_vol
) / 30;
91 int fp_volume
= fp_min_vol
;
93 /* zero out the sound */
94 sound_set_volume(fp_min_vol
>> 8);
96 sleep(HZ
/10); /* let audio thread run */
99 while (fp_volume
< fp_global_vol
- fp_step
) {
100 fp_volume
+= fp_step
;
101 sound_set_volume(fp_volume
>> 8);
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);
117 #if CONFIG_CODEC != SWCODEC
119 /* let audio thread run and wait for the mas to run out of data */
120 while (!mp3_pause_done())
125 /* reset volume to what it was before the fade */
126 sound_set_volume(global_settings
.volume
);
130 /* return true if screen restore is needed
131 return false otherwise
133 bool update_onvol_change(struct gui_wps
* gwps
)
135 gui_wps_statusbar_draw(gwps
, false);
136 gui_wps_refresh(gwps
, 0, WPS_REFRESH_NON_STATIC
);
138 #ifdef HAVE_LCD_CHARCELLS
139 gui_splash(gwps
->display
, 0, "Vol: %3d dB",
140 sound_val2phys(SOUND_VOLUME
, global_settings
.volume
));
146 bool ffwd_rew(int button
)
148 static const uint16_t ff_rew_steps
[] = {
149 1000, 2000, 3000, 4000,
150 5000, 6000, 8000, 10000,
151 15000, 20000, 25000, 30000,
155 unsigned int step
= 0; /* current ff/rewind step */
156 unsigned int max_step
= 0; /* maximum ff/rewind step */
157 int ff_rewind_count
= 0; /* current ff/rewind count (in ticks) */
158 int direction
= -1; /* forward=1 or backward=-1 */
159 long accel_tick
= 0; /* next time at which to bump the step size */
164 if (button
== ACTION_NONE
)
166 status_set_ffmode(0);
173 case ACTION_WPS_SEEKFWD
:
175 case ACTION_WPS_SEEKBACK
:
176 if (wps_state
.ff_rewind
)
180 /* fast forwarding, calc max step relative to end */
181 max_step
= (wps_state
.id3
->length
-
182 (wps_state
.id3
->elapsed
+
184 FF_REWIND_MAX_PERCENT
/ 100;
188 /* rewinding, calc max step relative to start */
189 max_step
= (wps_state
.id3
->elapsed
+ ff_rewind_count
) *
190 FF_REWIND_MAX_PERCENT
/ 100;
193 max_step
= MAX(max_step
, MIN_FF_REWIND_STEP
);
198 ff_rewind_count
+= step
* direction
;
200 if (global_settings
.ff_rewind_accel
!= 0 &&
201 current_tick
>= accel_tick
)
204 accel_tick
= current_tick
+
205 global_settings
.ff_rewind_accel
*HZ
;
210 if ( (audio_status() & AUDIO_STATUS_PLAY
) &&
211 wps_state
.id3
&& wps_state
.id3
->length
)
213 if (!wps_state
.paused
)
214 #if (CONFIG_CODEC == SWCODEC)
215 audio_pre_ff_rewind();
219 #if CONFIG_KEYPAD == PLAYER_PAD
221 gui_wps
[i
].display
->stop_scroll();
224 status_set_ffmode(STATUS_FASTFORWARD
);
226 status_set_ffmode(STATUS_FASTBACKWARD
);
228 wps_state
.ff_rewind
= true;
230 step
= ff_rew_steps
[global_settings
.ff_rewind_min_step
];
232 accel_tick
= current_tick
+
233 global_settings
.ff_rewind_accel
*HZ
;
240 if ((wps_state
.id3
->elapsed
+ ff_rewind_count
) >
241 wps_state
.id3
->length
)
242 ff_rewind_count
= wps_state
.id3
->length
-
243 wps_state
.id3
->elapsed
;
246 if ((int)(wps_state
.id3
->elapsed
+ ff_rewind_count
) < 0)
247 ff_rewind_count
= -wps_state
.id3
->elapsed
;
251 gui_wps_refresh(&gui_wps
[i
],
252 (wps_state
.wps_time_countup
== false)?
253 ff_rewind_count
:-ff_rewind_count
,
254 WPS_REFRESH_PLAYER_PROGRESS
|
255 WPS_REFRESH_DYNAMIC
);
259 case ACTION_WPS_STOPSEEK
:
260 wps_state
.id3
->elapsed
= wps_state
.id3
->elapsed
+ff_rewind_count
;
261 audio_ff_rewind(wps_state
.id3
->elapsed
);
263 wps_state
.ff_rewind
= false;
264 status_set_ffmode(0);
265 #if (CONFIG_CODEC != SWCODEC)
266 if (!wps_state
.paused
)
269 #ifdef HAVE_LCD_CHARCELLS
276 if(default_event_handler(button
) == SYS_USB_CONNECTED
) {
277 status_set_ffmode(0);
284 button
= get_action(CONTEXT_WPS
|ALLOW_SOFTLOCK
,TIMEOUT_BLOCK
);
289 bool gui_wps_display(void)
292 if (!wps_state
.id3
&& !(audio_status() & AUDIO_STATUS_PLAY
))
294 global_status
.resume_index
= -1;
295 #ifdef HAVE_LCD_BITMAP
296 gui_syncstatusbar_draw(&statusbars
, true);
298 gui_syncsplash(HZ
, ID2P(LANG_END_PLAYLIST
));
305 gui_wps
[i
].display
->clear_display();
306 if (!gui_wps
[i
].data
->wps_loaded
) {
307 if ( !gui_wps
[i
].data
->num_tokens
) {
308 /* set the default wps for the main-screen */
311 #ifdef HAVE_LCD_BITMAP
313 unload_wps_backdrop();
315 wps_data_load(gui_wps
[i
].data
,
316 "%s%?it<%?in<%in. |>%it|%fn>\n"
317 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
318 "%s%?id<%id|%?d1<%d1|(root)>> %?iy<(%iy)|>\n"
320 "%al%pc/%pt%ar[%pp:%pe]\n"
321 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
325 wps_data_load(gui_wps
[i
].data
,
326 "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
327 "%pc%?ps<*|/>%pt\n", false);
331 /* set the default wps for the remote-screen */
334 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
335 unload_remote_wps_backdrop();
337 wps_data_load(gui_wps
[i
].data
,
338 "%s%?ia<%ia|%?d2<%d2|(root)>>\n"
339 "%s%?it<%?in<%in. |>%it|%fn>\n"
340 "%al%pc/%pt%ar[%pp:%pe]\n"
341 "%fbkBit %?fv<avg|> %?iv<(id3v%iv)|(no id3)>\n"
352 gui_wps_refresh(&gui_wps
[i
], 0, WPS_REFRESH_ALL
);
357 bool update(struct gui_wps
*gwps
)
359 bool track_changed
= audio_has_changed_track();
360 bool retcode
= false;
362 gwps
->state
->nid3
= audio_next_track();
365 gwps
->display
->stop_scroll();
366 gwps
->state
->id3
= audio_current_track();
368 if (cuesheet_is_enabled() && gwps
->state
->id3
->cuesheet_type
369 && strcmp(gwps
->state
->id3
->path
, curr_cue
->audio_filename
))
371 /* the current cuesheet isn't the right one any more */
373 if (!strcmp(gwps
->state
->id3
->path
, temp_cue
->audio_filename
)) {
374 /* We have the new cuesheet in memory (temp_cue),
375 let's make it the current one ! */
376 memcpy(curr_cue
, temp_cue
, sizeof(struct cuesheet
));
379 /* We need to parse the new cuesheet */
381 char cuepath
[MAX_PATH
];
383 if (look_for_cuesheet_file(gwps
->state
->id3
->path
, cuepath
) &&
384 parse_cuesheet(cuepath
, curr_cue
))
386 gwps
->state
->id3
->cuesheet_type
= 1;
387 strcpy(curr_cue
->audio_filename
, gwps
->state
->id3
->path
);
391 cue_spoof_id3(curr_cue
, gwps
->state
->id3
);
394 if (gui_wps_display())
397 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
400 if (gwps
->state
->id3
)
402 strncpy(gwps
->state
->current_track_path
, gwps
->state
->id3
->path
,
403 sizeof(gwps
->state
->current_track_path
));
404 gwps
->state
->current_track_path
[sizeof(gwps
->state
->current_track_path
)-1] = '\0';
408 if (gwps
->state
->id3
)
410 if (cuesheet_is_enabled() && gwps
->state
->id3
->cuesheet_type
411 && (gwps
->state
->id3
->elapsed
< curr_cue
->curr_track
->offset
412 || (curr_cue
->curr_track_idx
< curr_cue
->track_count
- 1
413 && gwps
->state
->id3
->elapsed
>= (curr_cue
->curr_track
+1)->offset
)))
415 /* We've changed tracks within the cuesheet :
416 we need to update the ID3 info and refresh the WPS */
418 cue_find_current_track(curr_cue
, gwps
->state
->id3
->elapsed
);
419 cue_spoof_id3(curr_cue
, gwps
->state
->id3
);
421 gwps
->display
->stop_scroll();
422 if (gui_wps_display())
425 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
428 gui_wps_refresh(gwps
, 0, WPS_REFRESH_NON_STATIC
);
431 gui_wps_statusbar_draw(gwps
, false);
437 void display_keylock_text(bool locked
)
442 gui_wps
[i
].display
->stop_scroll();
445 s
= str(LANG_KEYLOCK_ON
);
447 s
= str(LANG_KEYLOCK_OFF
);
448 gui_syncsplash(HZ
, s
);
451 #ifdef HAVE_LCD_BITMAP
453 static void draw_progressbar(struct gui_wps
*gwps
, int line
)
455 struct wps_data
*data
= gwps
->data
;
456 struct screen
*display
= gwps
->display
;
457 struct wps_state
*state
= gwps
->state
;
458 int h
= font_get(FONT_UI
)->height
;
461 if (data
->progress_top
< 0)
462 sb_y
= line
*h
+ display
->getymargin() +
463 ((h
> data
->progress_height
+ 1)
464 ? (h
- data
->progress_height
) / 2 : 1);
466 sb_y
= data
->progress_top
;
468 if (!data
->progress_end
)
469 data
->progress_end
=display
->width
;
471 if (gwps
->data
->progressbar
.have_bitmap_pb
)
472 gui_bitmap_scrollbar_draw(display
, data
->progressbar
.bm
,
473 data
->progress_start
, sb_y
,
474 data
->progress_end
-data
->progress_start
,
475 data
->progressbar
.bm
.height
,
476 state
->id3
->length
? state
->id3
->length
: 1, 0,
477 state
->id3
->length
? state
->id3
->elapsed
478 + state
->ff_rewind_count
: 0,
481 gui_scrollbar_draw(display
, data
->progress_start
, sb_y
,
482 data
->progress_end
-data
->progress_start
,
483 data
->progress_height
,
484 state
->id3
->length
? state
->id3
->length
: 1, 0,
485 state
->id3
->length
? state
->id3
->elapsed
486 + state
->ff_rewind_count
: 0,
489 #ifdef AB_REPEAT_ENABLE
490 if ( ab_repeat_mode_enabled() && state
->id3
->length
!= 0 )
491 ab_draw_markers(display
, state
->id3
->length
,
492 data
->progress_start
, data
->progress_end
, sb_y
,
493 data
->progress_height
);
496 if ( cuesheet_is_enabled() && state
->id3
->cuesheet_type
)
497 cue_draw_markers(display
, state
->id3
->length
,
498 data
->progress_start
, data
->progress_end
,
499 sb_y
+1, data
->progress_height
-2);
502 /* clears the area where the image was shown */
503 static void clear_image_pos(struct gui_wps
*gwps
, int n
)
507 struct wps_data
*data
= gwps
->data
;
508 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
509 gwps
->display
->fillrect(data
->img
[n
].x
, data
->img
[n
].y
,
510 data
->img
[n
].bm
.width
, data
->img
[n
].bm
.height
);
511 gwps
->display
->set_drawmode(DRMODE_SOLID
);
514 static void wps_draw_image(struct gui_wps
*gwps
, int n
)
516 struct screen
*display
= gwps
->display
;
517 struct wps_data
*data
= gwps
->data
;
518 if(data
->img
[n
].always_display
)
519 display
->set_drawmode(DRMODE_FG
);
521 display
->set_drawmode(DRMODE_SOLID
);
524 if(data
->img
[n
].bm
.format
== FORMAT_MONO
) {
526 display
->mono_bitmap(data
->img
[n
].bm
.data
, data
->img
[n
].x
,
527 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
528 data
->img
[n
].bm
.height
);
531 display
->transparent_bitmap((fb_data
*)data
->img
[n
].bm
.data
,
533 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
534 data
->img
[n
].bm
.height
);
539 static void wps_display_images(struct gui_wps
*gwps
)
541 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
545 struct wps_data
*data
= gwps
->data
;
546 struct screen
*display
= gwps
->display
;
548 for (n
= 0; n
< MAX_IMAGES
; n
++)
550 if (data
->img
[n
].loaded
&&
551 (data
->img
[n
].display
|| data
->img
[n
].always_display
))
553 wps_draw_image(gwps
, n
);
556 display
->set_drawmode(DRMODE_SOLID
);
559 #else /* HAVE_LCD_CHARCELL */
561 static bool draw_player_progress(struct gui_wps
*gwps
)
563 struct wps_state
*state
= gwps
->state
;
564 struct screen
*display
= gwps
->display
;
565 unsigned char progress_pattern
[7];
572 if (state
->id3
->length
)
573 pos
= 36 * (state
->id3
->elapsed
+ state
->ff_rewind_count
)
574 / state
->id3
->length
;
576 for (i
= 0; i
< 7; i
++, pos
-= 5)
579 progress_pattern
[i
] = 0x1f;
581 progress_pattern
[i
] = 0x00;
583 progress_pattern
[i
] = 0x1f >> pos
;
586 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
590 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
592 static const unsigned char numbers
[10][4] = {
593 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
594 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
595 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
596 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
597 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
598 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
599 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
600 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
601 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
602 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
605 struct wps_state
*state
= gwps
->state
;
606 struct screen
*display
= gwps
->display
;
607 struct wps_data
*data
= gwps
->data
;
608 unsigned char progress_pattern
[7];
617 if (!state
->id3
|| buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
620 time
= state
->id3
->elapsed
+ state
->ff_rewind_count
;
621 if (state
->id3
->length
)
622 pos
= 55 * time
/ state
->id3
->length
;
624 memset(timestr
, 0, sizeof(timestr
));
625 format_time(timestr
, sizeof(timestr
)-2, time
);
626 timestr
[strlen(timestr
)] = ':'; /* always safe */
628 for (i
= 0; i
< 11; i
++, pos
-= 5)
631 memset(progress_pattern
, 0, sizeof(progress_pattern
));
633 if ((digit
= timestr
[time_idx
]))
638 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
640 memcpy(progress_pattern
, numbers
[digit
], 4);
643 else /* tens, shifted right */
645 for (j
= 0; j
< 4; j
++)
646 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
648 if (time_idx
> 0) /* not the first group, add colon in front */
650 progress_pattern
[1] |= 0x10;
651 progress_pattern
[3] |= 0x10;
657 progress_pattern
[5] = progress_pattern
[6] = 0x1f;
660 if (pos
> 0 && pos
< 5)
663 progress_pattern
[5] = progress_pattern
[6] = (~0x1f >> pos
) & 0x1f;
666 if (softchar
&& pat_idx
< 8)
668 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
670 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
674 buf
= utf8encode(' ', buf
);
676 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
681 #endif /* HAVE_LCD_CHARCELL */
683 /* Extract a part from a path.
685 * buf - buffer extract part to.
686 * buf_size - size of buffer.
687 * path - path to extract from.
688 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
689 * parent of parent, etc.
691 * Returns buf if the desired level was found, NULL otherwise.
693 static char* get_dir(char* buf
, int buf_size
, const char* path
, int level
)
696 const char* last_sep
;
699 sep
= path
+ strlen(path
);
714 if (level
|| (last_sep
<= sep
))
717 len
= MIN(last_sep
- sep
, buf_size
- 1);
718 strncpy(buf
, sep
+ 1, len
);
723 /* Return the tag found at index i and write its value in buf.
724 The return value is buf if the tag had a value, or NULL if not.
726 intval is used with conditionals/enums: when this function is called,
727 intval should contain the number of options in the conditional/enum.
728 When this function returns, intval is -1 if the tag is non numeric or,
729 if the tag is numeric, intval is the enum case we want to go to.
730 When not treating a conditional/enum, intval should be NULL.
732 static char *get_token_value(struct gui_wps
*gwps
,
733 struct wps_token
*token
,
734 char *buf
, int buf_size
,
740 struct wps_data
*data
= gwps
->data
;
741 struct wps_state
*state
= gwps
->state
;
746 struct mp3entry
*id3
;
757 struct tm
* tm
= NULL
;
759 /* if the token is an RTC one, update the time
760 and do the necessary checks */
761 if (token
->type
>= WPS_TOKENS_RTC_BEGIN
762 && token
->type
<= WPS_TOKENS_RTC_END
)
780 case WPS_TOKEN_CHARACTER
:
781 return &(token
->value
.c
);
783 case WPS_TOKEN_STRING
:
784 return data
->strings
[token
->value
.i
];
786 case WPS_TOKEN_TRACK_TIME_ELAPSED
:
787 format_time(buf
, buf_size
,
788 id3
->elapsed
+ state
->ff_rewind_count
);
791 case WPS_TOKEN_TRACK_TIME_REMAINING
:
792 format_time(buf
, buf_size
,
793 id3
->length
- id3
->elapsed
-
794 state
->ff_rewind_count
);
797 case WPS_TOKEN_TRACK_LENGTH
:
798 format_time(buf
, buf_size
, id3
->length
);
801 case WPS_TOKEN_PLAYLIST_ENTRIES
:
802 snprintf(buf
, buf_size
, "%d", playlist_amount());
805 case WPS_TOKEN_PLAYLIST_NAME
:
806 return playlist_name(NULL
, buf
, buf_size
);
808 case WPS_TOKEN_PLAYLIST_POSITION
:
809 snprintf(buf
, buf_size
, "%d",
810 playlist_get_display_index());
813 case WPS_TOKEN_PLAYLIST_SHUFFLE
:
814 if ( global_settings
.playlist_shuffle
)
820 case WPS_TOKEN_VOLUME
:
821 snprintf(buf
, buf_size
, "%d", global_settings
.volume
);
824 if (global_settings
.volume
== sound_min(SOUND_VOLUME
))
828 else if (global_settings
.volume
== 0)
832 else if (global_settings
.volume
> 0)
838 *intval
= (limit
- 3) * (global_settings
.volume
839 - sound_min(SOUND_VOLUME
) - 1)
840 / (-1 - sound_min(SOUND_VOLUME
)) + 2;
845 case WPS_TOKEN_TRACK_ELAPSED_PERCENT
:
846 if (id3
->length
<= 0)
851 *intval
= limit
* (id3
->elapsed
+ state
->ff_rewind_count
)
854 snprintf(buf
, buf_size
, "%d",
855 100*(id3
->elapsed
+ state
->ff_rewind_count
) / id3
->length
);
858 case WPS_TOKEN_METADATA_ARTIST
:
861 case WPS_TOKEN_METADATA_COMPOSER
:
862 return id3
->composer
;
864 case WPS_TOKEN_METADATA_ALBUM
:
867 case WPS_TOKEN_METADATA_ALBUM_ARTIST
:
868 return id3
->albumartist
;
870 case WPS_TOKEN_METADATA_GROUPING
:
871 return id3
->grouping
;
873 case WPS_TOKEN_METADATA_GENRE
:
874 return id3
->genre_string
;
876 case WPS_TOKEN_METADATA_DISC_NUMBER
:
877 if (id3
->disc_string
)
878 return id3
->disc_string
;
880 snprintf(buf
, buf_size
, "%d", id3
->discnum
);
885 case WPS_TOKEN_METADATA_TRACK_NUMBER
:
886 if (id3
->track_string
)
887 return id3
->track_string
;
890 snprintf(buf
, buf_size
, "%d", id3
->tracknum
);
895 case WPS_TOKEN_METADATA_TRACK_TITLE
:
898 case WPS_TOKEN_METADATA_VERSION
:
899 switch (id3
->id3version
)
920 case WPS_TOKEN_METADATA_YEAR
:
921 if( id3
->year_string
)
922 return id3
->year_string
;
925 snprintf(buf
, buf_size
, "%d", id3
->year
);
930 case WPS_TOKEN_METADATA_COMMENT
:
934 case WPS_TOKEN_ALBUMART_DISPLAY
:
935 draw_album_art(gwps
, audio_current_aa_hid(), false);
938 case WPS_TOKEN_ALBUMART_FOUND
:
939 if (audio_current_aa_hid() >= 0) {
940 snprintf(buf
, buf_size
, "C");
946 case WPS_TOKEN_FILE_BITRATE
:
948 snprintf(buf
, buf_size
, "%d", id3
->bitrate
);
950 snprintf(buf
, buf_size
, "?");
953 case WPS_TOKEN_FILE_CODEC
:
956 if(id3
->codectype
== AFMT_UNKNOWN
)
957 *intval
= AFMT_NUM_CODECS
;
959 *intval
= id3
->codectype
;
961 return id3_get_codec(id3
);
963 case WPS_TOKEN_FILE_FREQUENCY
:
964 snprintf(buf
, buf_size
, "%ld", id3
->frequency
);
967 case WPS_TOKEN_FILE_FREQUENCY_KHZ
:
968 /* ignore remainders < 100, so 22050 Hz becomes just 22k */
969 if ((id3
->frequency
% 1000) < 100)
970 snprintf(buf
, buf_size
, "%ld", id3
->frequency
/ 1000);
972 snprintf(buf
, buf_size
, "%ld.%d",
973 id3
->frequency
/ 1000,
974 (id3
->frequency
% 1000) / 100);
977 case WPS_TOKEN_FILE_NAME
:
978 if (get_dir(buf
, buf_size
, id3
->path
, 0)) {
979 /* Remove extension */
980 char* sep
= strrchr(buf
, '.');
990 case WPS_TOKEN_FILE_NAME_WITH_EXTENSION
:
991 return get_dir(buf
, buf_size
, id3
->path
, 0);
993 case WPS_TOKEN_FILE_PATH
:
996 case WPS_TOKEN_FILE_SIZE
:
997 snprintf(buf
, buf_size
, "%ld", id3
->filesize
/ 1024);
1000 case WPS_TOKEN_FILE_VBR
:
1001 return id3
->vbr
? "(avg)" : NULL
;
1003 case WPS_TOKEN_FILE_DIRECTORY
:
1004 return get_dir(buf
, buf_size
, id3
->path
, token
->value
.i
);
1006 case WPS_TOKEN_BATTERY_PERCENT
:
1008 int l
= battery_level();
1012 limit
= MAX(limit
, 2);
1014 /* First enum is used for "unknown level". */
1015 *intval
= (limit
- 1) * l
/ 100 + 2;
1022 snprintf(buf
, buf_size
, "%d", l
);
1029 case WPS_TOKEN_BATTERY_VOLTS
:
1031 unsigned int v
= battery_voltage();
1032 snprintf(buf
, buf_size
, "%d.%02d", v
/ 1000, (v
% 1000) / 10);
1036 case WPS_TOKEN_BATTERY_TIME
:
1038 int t
= battery_time();
1040 snprintf(buf
, buf_size
, "%dh %dm", t
/ 60, t
% 60);
1042 strncpy(buf
, "?h ?m", buf_size
);
1047 case WPS_TOKEN_BATTERY_CHARGER_CONNECTED
:
1049 if(charger_input_state
==CHARGER
)
1055 #if CONFIG_CHARGING >= CHARGING_MONITOR
1056 case WPS_TOKEN_BATTERY_CHARGING
:
1058 if (charge_state
== CHARGING
|| charge_state
== TOPOFF
) {
1065 case WPS_TOKEN_BATTERY_SLEEPTIME
:
1067 if (get_sleep_timer() == 0)
1071 format_time(buf
, buf_size
, get_sleep_timer() * 1000);
1076 case WPS_TOKEN_PLAYBACK_STATUS
:
1078 int status
= audio_status();
1080 if (status
== AUDIO_STATUS_PLAY
&& \
1081 !(status
& AUDIO_STATUS_PAUSE
))
1083 if (audio_status() & AUDIO_STATUS_PAUSE
&& \
1084 (! status_get_ffmode()))
1086 if (status_get_ffmode() == STATUS_FASTFORWARD
)
1088 if (status_get_ffmode() == STATUS_FASTBACKWARD
)
1095 snprintf(buf
, buf_size
, "%d", mode
);
1099 case WPS_TOKEN_REPEAT_MODE
:
1101 *intval
= global_settings
.repeat_mode
+ 1;
1102 snprintf(buf
, buf_size
, "%d", *intval
);
1106 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1107 /* d: day of month (01..31) */
1108 snprintf(buf
, buf_size
, "%02d", tm
->tm_mday
);
1111 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1112 /* e: day of month, blank padded ( 1..31) */
1113 snprintf(buf
, buf_size
, "%2d", tm
->tm_mday
);
1116 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1117 /* H: hour (00..23) */
1118 snprintf(buf
, buf_size
, "%02d", tm
->tm_hour
);
1121 case WPS_TOKEN_RTC_HOUR_24
:
1122 /* k: hour ( 0..23) */
1123 snprintf(buf
, buf_size
, "%2d", tm
->tm_hour
);
1126 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1127 /* I: hour (01..12) */
1128 snprintf(buf
, buf_size
, "%02d",
1129 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1132 case WPS_TOKEN_RTC_HOUR_12
:
1133 /* l: hour ( 1..12) */
1134 snprintf(buf
, buf_size
, "%2d",
1135 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1138 case WPS_TOKEN_RTC_MONTH
:
1139 /* m: month (01..12) */
1141 *intval
= tm
->tm_mon
+ 1;
1142 snprintf(buf
, buf_size
, "%02d", tm
->tm_mon
+ 1);
1145 case WPS_TOKEN_RTC_MINUTE
:
1146 /* M: minute (00..59) */
1147 snprintf(buf
, buf_size
, "%02d", tm
->tm_min
);
1150 case WPS_TOKEN_RTC_SECOND
:
1151 /* S: second (00..59) */
1152 snprintf(buf
, buf_size
, "%02d", tm
->tm_sec
);
1155 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1156 /* y: last two digits of year (00..99) */
1157 snprintf(buf
, buf_size
, "%02d", tm
->tm_year
% 100);
1160 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1161 /* Y: year (1970...) */
1162 snprintf(buf
, buf_size
, "%04d", tm
->tm_year
+ 1900);
1165 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1166 /* p: upper case AM or PM indicator */
1167 snprintf(buf
, buf_size
, (tm
->tm_hour
/12 == 0) ? "AM" : "PM");
1170 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1171 /* P: lower case am or pm indicator */
1172 snprintf(buf
, buf_size
, (tm
->tm_hour
/12 == 0) ? "am" : "pm");
1175 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1176 /* a: abbreviated weekday name (Sun..Sat) */
1177 snprintf(buf
, buf_size
, "%s",str(LANG_WEEKDAY_SUNDAY
+ tm
->tm_wday
));
1180 case WPS_TOKEN_RTC_MONTH_NAME
:
1181 /* b: abbreviated month name (Jan..Dec) */
1182 snprintf(buf
, buf_size
, "%s",str(LANG_MONTH_JANUARY
+ tm
->tm_mon
));
1185 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1186 /* u: day of week (1..7); 1 is Monday */
1188 *intval
= (tm
->tm_wday
== 0) ? 7 : tm
->tm_wday
;
1189 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
+ 1);
1192 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1193 /* w: day of week (0..6); 0 is Sunday */
1195 *intval
= tm
->tm_wday
+ 1;
1196 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
);
1199 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1200 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1201 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1202 case WPS_TOKEN_RTC_HOUR_24
:
1203 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1204 case WPS_TOKEN_RTC_HOUR_12
:
1205 case WPS_TOKEN_RTC_MONTH
:
1206 case WPS_TOKEN_RTC_MINUTE
:
1207 case WPS_TOKEN_RTC_SECOND
:
1208 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1209 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1210 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1211 strncpy(buf
, "--", buf_size
);
1213 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1214 strncpy(buf
, "----", buf_size
);
1216 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1217 case WPS_TOKEN_RTC_MONTH_NAME
:
1218 strncpy(buf
, "---", buf_size
);
1220 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1221 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1222 strncpy(buf
, "-", buf_size
);
1226 #ifdef HAVE_LCD_CHARCELLS
1227 case WPS_TOKEN_PROGRESSBAR
:
1229 char *end
= utf8encode(data
->wps_progress_pat
[0], buf
);
1234 case WPS_TOKEN_PLAYER_PROGRESSBAR
:
1237 /* we need 11 characters (full line) for
1239 snprintf(buf
, buf_size
, " ");
1243 /* Tell the user if we have an OldPlayer */
1244 snprintf(buf
, buf_size
, " <Old LCD> ");
1249 #ifdef HAVE_TAGCACHE
1250 case WPS_TOKEN_DATABASE_PLAYCOUNT
:
1252 *intval
= id3
->playcount
+ 1;
1254 snprintf(buf
, buf_size
, "%ld", id3
->playcount
);
1257 case WPS_TOKEN_DATABASE_RATING
:
1259 *intval
= id3
->rating
+ 1;
1261 snprintf(buf
, buf_size
, "%d", id3
->rating
);
1264 case WPS_TOKEN_DATABASE_AUTOSCORE
:
1266 *intval
= id3
->score
+ 1;
1268 snprintf(buf
, buf_size
, "%d", id3
->score
);
1272 #if (CONFIG_CODEC == SWCODEC)
1273 case WPS_TOKEN_CROSSFADE
:
1275 *intval
= global_settings
.crossfade
+ 1;
1276 snprintf(buf
, buf_size
, "%d", global_settings
.crossfade
);
1279 case WPS_TOKEN_REPLAYGAIN
:
1283 if (global_settings
.replaygain
== 0)
1288 get_replaygain_mode(id3
->track_gain_string
!= NULL
,
1289 id3
->album_gain_string
!= NULL
);
1291 val
= 6; /* no tag */
1295 if (global_settings
.replaygain_type
== REPLAYGAIN_SHUFFLE
)
1310 strncpy(buf
, id3
->track_gain_string
, buf_size
);
1314 strncpy(buf
, id3
->album_gain_string
, buf_size
);
1319 #endif /* (CONFIG_CODEC == SWCODEC) */
1321 #if (CONFIG_CODEC != MAS3507D)
1322 case WPS_TOKEN_SOUND_PITCH
:
1324 int val
= sound_get_pitch();
1325 snprintf(buf
, buf_size
, "%d.%d",
1326 val
/ 10, val
% 10);
1331 case WPS_TOKEN_MAIN_HOLD
:
1332 #ifdef HAS_BUTTON_HOLD
1335 if (is_keys_locked())
1336 #endif /*hold switch or softlock*/
1341 #ifdef HAS_REMOTE_BUTTON_HOLD
1342 case WPS_TOKEN_REMOTE_HOLD
:
1343 if (remote_button_hold())
1349 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
1350 case WPS_TOKEN_VLED_HDD
:
1357 #ifdef HAVE_LCD_BITMAP
1358 case WPS_TOKEN_LEFTMARGIN
:
1359 gwps
->display
->setmargins(token
->value
.i
,
1360 gwps
->display
->getymargin());
1369 /* Return the index to the end token for the conditional token at index.
1370 The conditional token can be either a start token or a separator
1371 (i.e. option) token.
1373 static int find_conditional_end(struct wps_data
*data
, int index
)
1376 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
1377 ret
= data
->tokens
[ret
].value
.i
;
1379 /* ret now is the index to the end token for the conditional. */
1383 /* Return the index of the appropriate case for the conditional
1384 that starts at cond_index.
1386 static int evaluate_conditional(struct gui_wps
*gwps
, int cond_index
)
1391 struct wps_data
*data
= gwps
->data
;
1394 char result
[128], *value
;
1395 int num_options
= data
->tokens
[cond_index
].value
.i
;
1397 /* treat ?xx<true> constructs as if they had 2 options. */
1398 if (num_options
< 2)
1401 int intval
= num_options
;
1402 /* get_token_value needs to know the number of options in the enum */
1403 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
1404 result
, sizeof(result
), &intval
);
1406 /* intval is now the number of the enum option we want to read,
1407 starting from 1. If intval is -1, we check if value is empty. */
1409 intval
= (value
&& *value
) ? 1 : num_options
;
1410 else if (intval
> num_options
|| intval
< 1)
1411 intval
= num_options
;
1413 /* skip to the right enum case */
1414 int next
= cond_index
+ 2;
1415 for (i
= 1; i
< intval
; i
++)
1417 next
= data
->tokens
[next
].value
.i
;
1421 #ifdef HAVE_LCD_BITMAP
1422 /* clear all pictures in the conditional */
1423 for (i
= 0; i
< MAX_IMAGES
; i
++)
1425 if (data
->img
[i
].cond_index
== cond_index
)
1426 clear_image_pos(gwps
, i
);
1430 #ifdef HAVE_ALBUMART
1431 if (data
->wps_uses_albumart
!= WPS_ALBUMART_NONE
&&
1432 data
->albumart_cond_index
== cond_index
)
1434 draw_album_art(gwps
, audio_current_aa_hid(), true);
1441 /* Read a (sub)line to the given alignment format buffer.
1442 linebuf is the buffer where the data is actually stored.
1443 align is the alignment format that'll be used to display the text.
1444 The return value indicates whether the line needs to be updated.
1446 static bool get_line(struct gui_wps
*gwps
,
1447 int line
, int subline
,
1448 struct align_pos
*align
,
1452 struct wps_data
*data
= gwps
->data
;
1455 char *buf
= linebuf
; /* will always point to the writing position */
1456 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
1457 int i
, last_token_idx
;
1458 bool update
= false;
1460 /* alignment-related variables */
1462 char* cur_align_start
;
1463 cur_align_start
= buf
;
1464 cur_align
= WPS_ALIGN_LEFT
;
1466 align
->center
= NULL
;
1467 align
->right
= NULL
;
1469 #ifdef HAVE_LCD_BITMAP
1470 /* Reset margins - only bitmap targets modify them */
1471 gwps
->display
->setmargins(0, gwps
->display
->getymargin());
1474 /* Process all tokens of the desired subline */
1475 last_token_idx
= wps_last_token_index(data
, line
, subline
);
1476 for (i
= wps_first_token_index(data
, line
, subline
);
1477 i
<= last_token_idx
; i
++)
1479 switch(data
->tokens
[i
].type
)
1481 case WPS_TOKEN_CONDITIONAL
:
1482 /* place ourselves in the right conditional case */
1483 i
= evaluate_conditional(gwps
, i
);
1487 case WPS_TOKEN_CONDITIONAL_OPTION
:
1488 /* we've finished in the curent conditional case,
1489 skip to the end of the conditional structure */
1490 i
= find_conditional_end(data
, i
);
1493 #ifdef HAVE_LCD_BITMAP
1494 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
1496 struct gui_img
*img
= data
->img
;
1497 int n
= data
->tokens
[i
].value
.i
;
1498 if (n
>= 0 && n
< MAX_IMAGES
&& img
[n
].loaded
)
1499 img
[n
].display
= true;
1504 case WPS_TOKEN_ALIGN_LEFT
:
1505 case WPS_TOKEN_ALIGN_CENTER
:
1506 case WPS_TOKEN_ALIGN_RIGHT
:
1507 /* remember where the current aligned text started */
1510 case WPS_ALIGN_LEFT
:
1511 align
->left
= cur_align_start
;
1514 case WPS_ALIGN_CENTER
:
1515 align
->center
= cur_align_start
;
1518 case WPS_ALIGN_RIGHT
:
1519 align
->right
= cur_align_start
;
1522 /* start a new alignment */
1523 switch (data
->tokens
[i
].type
)
1525 case WPS_TOKEN_ALIGN_LEFT
:
1526 cur_align
= WPS_ALIGN_LEFT
;
1528 case WPS_TOKEN_ALIGN_CENTER
:
1529 cur_align
= WPS_ALIGN_CENTER
;
1531 case WPS_TOKEN_ALIGN_RIGHT
:
1532 cur_align
= WPS_ALIGN_RIGHT
;
1538 cur_align_start
= buf
;
1543 /* get the value of the tag and copy it to the buffer */
1544 char *value
= get_token_value(gwps
, &data
->tokens
[i
],
1545 temp_buf
, sizeof(temp_buf
), NULL
);
1549 while (*value
&& (buf
< linebuf_end
))
1557 /* close the current alignment */
1560 case WPS_ALIGN_LEFT
:
1561 align
->left
= cur_align_start
;
1564 case WPS_ALIGN_CENTER
:
1565 align
->center
= cur_align_start
;
1568 case WPS_ALIGN_RIGHT
:
1569 align
->right
= cur_align_start
;
1576 static void get_subline_timeout(struct gui_wps
*gwps
, int line
, int subline
)
1578 struct wps_data
*data
= gwps
->data
;
1580 int subline_idx
= wps_subline_index(data
, line
, subline
);
1581 int last_token_idx
= wps_last_token_index(data
, line
, subline
);
1583 data
->sublines
[subline_idx
].time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
1585 for (i
= wps_first_token_index(data
, line
, subline
);
1586 i
<= last_token_idx
; i
++)
1588 switch(data
->tokens
[i
].type
)
1590 case WPS_TOKEN_CONDITIONAL
:
1591 /* place ourselves in the right conditional case */
1592 i
= evaluate_conditional(gwps
, i
);
1595 case WPS_TOKEN_CONDITIONAL_OPTION
:
1596 /* we've finished in the curent conditional case,
1597 skip to the end of the conditional structure */
1598 i
= find_conditional_end(data
, i
);
1601 case WPS_TOKEN_SUBLINE_TIMEOUT
:
1602 data
->sublines
[subline_idx
].time_mult
= data
->tokens
[i
].value
.i
;
1611 /* Calculates which subline should be displayed for the specified line
1612 Returns true iff the subline must be refreshed */
1613 static bool update_curr_subline(struct gui_wps
*gwps
, int line
)
1615 struct wps_data
*data
= gwps
->data
;
1617 int search
, search_start
, num_sublines
;
1619 bool new_subline_refresh
;
1620 bool only_one_subline
;
1622 num_sublines
= data
->lines
[line
].num_sublines
;
1623 reset_subline
= (data
->lines
[line
].curr_subline
== SUBLINE_RESET
);
1624 new_subline_refresh
= false;
1625 only_one_subline
= false;
1627 /* if time to advance to next sub-line */
1628 if (TIME_AFTER(current_tick
, data
->lines
[line
].subline_expire_time
- 1) ||
1631 /* search all sublines until the next subline with time > 0
1632 is found or we get back to the subline we started with */
1636 search_start
= data
->lines
[line
].curr_subline
;
1638 for (search
= 0; search
< num_sublines
; search
++)
1640 data
->lines
[line
].curr_subline
++;
1642 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
1643 if (data
->lines
[line
].curr_subline
== num_sublines
)
1645 if (data
->lines
[line
].curr_subline
== 1)
1646 only_one_subline
= true;
1647 data
->lines
[line
].curr_subline
= 0;
1650 /* if back where we started after search or
1651 only one subline is defined on the line */
1652 if (((search
> 0) &&
1653 (data
->lines
[line
].curr_subline
== search_start
)) ||
1656 /* no other subline with a time > 0 exists */
1657 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1659 data
->lines
[line
].subline_expire_time
) + 100 * HZ
;
1664 /* get initial time multiplier for this subline */
1665 get_subline_timeout(gwps
, line
, data
->lines
[line
].curr_subline
);
1667 int subline_idx
= wps_subline_index(data
, line
,
1668 data
->lines
[line
].curr_subline
);
1670 /* only use this subline if subline time > 0 */
1671 if (data
->sublines
[subline_idx
].time_mult
> 0)
1673 new_subline_refresh
= true;
1674 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1675 current_tick
: data
->lines
[line
].subline_expire_time
) +
1676 BASE_SUBLINE_TIME
*data
->sublines
[subline_idx
].time_mult
;
1683 return new_subline_refresh
;
1686 /* Display a line appropriately according to its alignment format.
1687 format_align contains the text, separated between left, center and right.
1688 line is the index of the line on the screen.
1689 scroll indicates whether the line is a scrolling one or not.
1691 static void write_line(struct screen
*display
,
1692 struct align_pos
*format_align
,
1697 int left_width
= 0, left_xpos
;
1698 int center_width
= 0, center_xpos
;
1699 int right_width
= 0, right_xpos
;
1705 /* calculate different string sizes and positions */
1706 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
1707 if (format_align
->left
!= 0) {
1708 display
->getstringsize((unsigned char *)format_align
->left
,
1709 &left_width
, &string_height
);
1712 if (format_align
->right
!= 0) {
1713 display
->getstringsize((unsigned char *)format_align
->right
,
1714 &right_width
, &string_height
);
1717 if (format_align
->center
!= 0) {
1718 display
->getstringsize((unsigned char *)format_align
->center
,
1719 ¢er_width
, &string_height
);
1722 left_xpos
= display
->getxmargin();
1723 right_xpos
= (display
->width
- right_width
);
1724 center_xpos
= (display
->width
+ left_xpos
- center_width
) / 2;
1726 scroll_width
= display
->width
- left_xpos
;
1728 /* Checks for overlapping strings.
1729 If needed the overlapping strings will be merged, separated by a
1732 /* CASE 1: left and centered string overlap */
1733 /* there is a left string, need to merge left and center */
1734 if ((left_width
!= 0 && center_width
!= 0) &&
1735 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
1736 /* replace the former separator '\0' of left and
1737 center string with a space */
1738 *(--format_align
->center
) = ' ';
1739 /* calculate the new width and position of the merged string */
1740 left_width
= left_width
+ space_width
+ center_width
;
1741 /* there is no centered string anymore */
1744 /* there is no left string, move center to left */
1745 if ((left_width
== 0 && center_width
!= 0) &&
1746 (left_xpos
+ left_width
> center_xpos
)) {
1747 /* move the center string to the left string */
1748 format_align
->left
= format_align
->center
;
1749 /* calculate the new width and position of the string */
1750 left_width
= center_width
;
1751 /* there is no centered string anymore */
1755 /* CASE 2: centered and right string overlap */
1756 /* there is a right string, need to merge center and right */
1757 if ((center_width
!= 0 && right_width
!= 0) &&
1758 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
1759 /* replace the former separator '\0' of center and
1760 right string with a space */
1761 *(--format_align
->right
) = ' ';
1762 /* move the center string to the right after merge */
1763 format_align
->right
= format_align
->center
;
1764 /* calculate the new width and position of the merged string */
1765 right_width
= center_width
+ space_width
+ right_width
;
1766 right_xpos
= (display
->width
- right_width
);
1767 /* there is no centered string anymore */
1770 /* there is no right string, move center to right */
1771 if ((center_width
!= 0 && right_width
== 0) &&
1772 (center_xpos
+ center_width
> right_xpos
)) {
1773 /* move the center string to the right string */
1774 format_align
->right
= format_align
->center
;
1775 /* calculate the new width and position of the string */
1776 right_width
= center_width
;
1777 right_xpos
= (display
->width
- right_width
);
1778 /* there is no centered string anymore */
1782 /* CASE 3: left and right overlap
1783 There is no center string anymore, either there never
1784 was one or it has been merged in case 1 or 2 */
1785 /* there is a left string, need to merge left and right */
1786 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
1787 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
1788 /* replace the former separator '\0' of left and
1789 right string with a space */
1790 *(--format_align
->right
) = ' ';
1791 /* calculate the new width and position of the string */
1792 left_width
= left_width
+ space_width
+ right_width
;
1793 /* there is no right string anymore */
1796 /* there is no left string, move right to left */
1797 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
1798 (left_width
> right_xpos
)) {
1799 /* move the right string to the left string */
1800 format_align
->left
= format_align
->right
;
1801 /* calculate the new width and position of the string */
1802 left_width
= right_width
;
1803 /* there is no right string anymore */
1807 ypos
= (line
* string_height
) + display
->getymargin();
1810 if (scroll
&& ((left_width
> scroll_width
) ||
1811 (center_width
> scroll_width
) ||
1812 (right_width
> scroll_width
)))
1814 display
->puts_scroll(0, line
,
1815 (unsigned char *)format_align
->left
);
1819 #ifdef HAVE_LCD_BITMAP
1820 /* clear the line first */
1821 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1822 display
->fillrect(left_xpos
, ypos
, display
->width
, string_height
);
1823 display
->set_drawmode(DRMODE_SOLID
);
1826 /* Nasty hack: we output an empty scrolling string,
1827 which will reset the scroller for that line */
1828 display
->puts_scroll(0, line
, (unsigned char *)"");
1830 /* print aligned strings */
1831 if (left_width
!= 0)
1833 display
->putsxy(left_xpos
, ypos
,
1834 (unsigned char *)format_align
->left
);
1836 if (center_width
!= 0)
1838 display
->putsxy(center_xpos
, ypos
,
1839 (unsigned char *)format_align
->center
);
1841 if (right_width
!= 0)
1843 display
->putsxy(right_xpos
, ypos
,
1844 (unsigned char *)format_align
->right
);
1849 /* Refresh the WPS according to refresh_mode. */
1850 bool gui_wps_refresh(struct gui_wps
*gwps
,
1852 unsigned char refresh_mode
)
1854 struct wps_data
*data
= gwps
->data
;
1855 struct screen
*display
= gwps
->display
;
1856 struct wps_state
*state
= gwps
->state
;
1858 if(!gwps
|| !data
|| !state
|| !display
)
1861 int line
, i
, subline_idx
;
1862 unsigned char flags
;
1863 char linebuf
[MAX_PATH
];
1865 struct align_pos align
;
1867 align
.center
= NULL
;
1870 bool update_line
, new_subline_refresh
;
1872 #ifdef HAVE_LCD_BITMAP
1873 gui_wps_statusbar_draw(gwps
, true);
1875 /* to find out wether the peak meter is enabled we
1876 assume it wasn't until we find a line that contains
1877 the peak meter. We can't use peak_meter_enabled itself
1878 because that would mean to turn off the meter thread
1879 temporarily. (That shouldn't matter unless yield
1880 or sleep is called but who knows...)
1882 bool enable_pm
= false;
1884 /* Set images to not to be displayed */
1885 for (i
= 0; i
< MAX_IMAGES
; i
++)
1887 data
->img
[i
].display
= false;
1891 /* reset to first subline if refresh all flag is set */
1892 if (refresh_mode
== WPS_REFRESH_ALL
)
1894 for (i
= 0; i
< data
->num_lines
; i
++)
1896 data
->lines
[i
].curr_subline
= SUBLINE_RESET
;
1900 #ifdef HAVE_LCD_CHARCELLS
1901 for (i
= 0; i
< 8; i
++)
1903 if (data
->wps_progress_pat
[i
] == 0)
1904 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1910 display
->stop_scroll();
1914 state
->ff_rewind_count
= ffwd_offset
;
1916 for (line
= 0; line
< data
->num_lines
; line
++)
1918 memset(linebuf
, 0, sizeof(linebuf
));
1919 update_line
= false;
1921 /* get current subline for the line */
1922 new_subline_refresh
= update_curr_subline(gwps
, line
);
1924 subline_idx
= wps_subline_index(data
, line
,
1925 data
->lines
[line
].curr_subline
);
1926 flags
= data
->sublines
[subline_idx
].line_type
;
1928 if (refresh_mode
== WPS_REFRESH_ALL
|| (flags
& refresh_mode
)
1929 || new_subline_refresh
)
1931 /* get_line tells us if we need to update the line */
1932 update_line
= get_line(gwps
, line
, data
->lines
[line
].curr_subline
,
1933 &align
, linebuf
, sizeof(linebuf
));
1936 #ifdef HAVE_LCD_BITMAP
1938 if (flags
& refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1940 /* the progressbar should be alone on its line */
1941 update_line
= false;
1942 draw_progressbar(gwps
, line
);
1946 if (flags
& refresh_mode
& WPS_REFRESH_PEAK_METER
)
1948 /* the peakmeter should be alone on its line */
1949 update_line
= false;
1951 int h
= font_get(FONT_UI
)->height
;
1952 int peak_meter_y
= display
->getymargin() + line
* h
;
1954 /* The user might decide to have the peak meter in the last
1955 line so that it is only displayed if no status bar is
1956 visible. If so we neither want do draw nor enable the
1958 if (peak_meter_y
+ h
<= display
->height
) {
1959 /* found a line with a peak meter -> remember that we must
1962 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
1963 MIN(h
, display
->height
- peak_meter_y
));
1967 #else /* HAVE_LCD_CHARCELL */
1970 if (flags
& refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1972 if (data
->full_line_progressbar
)
1973 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
1975 draw_player_progress(gwps
);
1981 if (flags
& WPS_REFRESH_SCROLL
)
1983 /* if the line is a scrolling one we don't want to update
1984 too often, so that it has the time to scroll */
1985 if ((refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
1986 write_line(display
, &align
, line
, true);
1989 write_line(display
, &align
, line
, false);
1993 #ifdef HAVE_LCD_BITMAP
1994 data
->peak_meter_enabled
= enable_pm
;
1995 wps_display_images(gwps
);
2000 #ifdef HAVE_BACKLIGHT
2001 if (global_settings
.caption_backlight
&& state
->id3
)
2003 /* turn on backlight n seconds before track ends, and turn it off n
2004 seconds into the new track. n == backlight_timeout, or 5s */
2005 int n
= backlight_timeout_value
[global_settings
.backlight_timeout
]
2009 n
= 5000; /* use 5s if backlight is always on or off */
2011 if (((state
->id3
->elapsed
< 1000) ||
2012 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
)) &&
2013 (state
->paused
== false))
2017 #ifdef HAVE_REMOTE_LCD
2018 if (global_settings
.remote_caption_backlight
&& state
->id3
)
2020 /* turn on remote backlight n seconds before track ends, and turn it
2021 off n seconds into the new track. n == remote_backlight_timeout,
2023 int n
= backlight_timeout_value
[global_settings
.remote_backlight_timeout
]
2027 n
= 5000; /* use 5s if backlight is always on or off */
2029 if (((state
->id3
->elapsed
< 1000) ||
2030 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
)) &&
2031 (state
->paused
== false))
2032 remote_backlight_on();