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 unsigned int step
= 0; /* current ff/rewind step */
149 unsigned int max_step
= 0; /* maximum ff/rewind step */
150 int ff_rewind_count
= 0; /* current ff/rewind count (in ticks) */
151 int direction
= -1; /* forward=1 or backward=-1 */
152 long accel_tick
= 0; /* next time at which to bump the step size */
157 if (button
== ACTION_NONE
)
159 status_set_ffmode(0);
166 case ACTION_WPS_SEEKFWD
:
168 case ACTION_WPS_SEEKBACK
:
169 if (wps_state
.ff_rewind
)
173 /* fast forwarding, calc max step relative to end */
174 max_step
= (wps_state
.id3
->length
-
175 (wps_state
.id3
->elapsed
+
177 FF_REWIND_MAX_PERCENT
/ 100;
181 /* rewinding, calc max step relative to start */
182 max_step
= (wps_state
.id3
->elapsed
+ ff_rewind_count
) *
183 FF_REWIND_MAX_PERCENT
/ 100;
186 max_step
= MAX(max_step
, MIN_FF_REWIND_STEP
);
191 ff_rewind_count
+= step
* direction
;
193 if (global_settings
.ff_rewind_accel
!= 0 &&
194 current_tick
>= accel_tick
)
197 accel_tick
= current_tick
+
198 global_settings
.ff_rewind_accel
*HZ
;
203 if ( (audio_status() & AUDIO_STATUS_PLAY
) &&
204 wps_state
.id3
&& wps_state
.id3
->length
)
206 if (!wps_state
.paused
)
207 #if (CONFIG_CODEC == SWCODEC)
208 audio_pre_ff_rewind();
212 #if CONFIG_KEYPAD == PLAYER_PAD
214 gui_wps
[i
].display
->stop_scroll();
217 status_set_ffmode(STATUS_FASTFORWARD
);
219 status_set_ffmode(STATUS_FASTBACKWARD
);
221 wps_state
.ff_rewind
= true;
223 step
= 1000 * global_settings
.ff_rewind_min_step
;
225 accel_tick
= current_tick
+
226 global_settings
.ff_rewind_accel
*HZ
;
233 if ((wps_state
.id3
->elapsed
+ ff_rewind_count
) >
234 wps_state
.id3
->length
)
235 ff_rewind_count
= wps_state
.id3
->length
-
236 wps_state
.id3
->elapsed
;
239 if ((int)(wps_state
.id3
->elapsed
+ ff_rewind_count
) < 0)
240 ff_rewind_count
= -wps_state
.id3
->elapsed
;
244 gui_wps_refresh(&gui_wps
[i
],
245 (wps_state
.wps_time_countup
== false)?
246 ff_rewind_count
:-ff_rewind_count
,
247 WPS_REFRESH_PLAYER_PROGRESS
|
248 WPS_REFRESH_DYNAMIC
);
252 case ACTION_WPS_STOPSEEK
:
253 wps_state
.id3
->elapsed
= wps_state
.id3
->elapsed
+ff_rewind_count
;
254 audio_ff_rewind(wps_state
.id3
->elapsed
);
256 wps_state
.ff_rewind
= false;
257 status_set_ffmode(0);
258 #if (CONFIG_CODEC != SWCODEC)
259 if (!wps_state
.paused
)
262 #ifdef HAVE_LCD_CHARCELLS
269 if(default_event_handler(button
) == SYS_USB_CONNECTED
) {
270 status_set_ffmode(0);
277 button
= get_action(CONTEXT_WPS
|ALLOW_SOFTLOCK
,TIMEOUT_BLOCK
);
282 bool gui_wps_display(void)
285 if (!wps_state
.id3
&& !(audio_status() & AUDIO_STATUS_PLAY
))
287 global_status
.resume_index
= -1;
288 #ifdef HAVE_LCD_BITMAP
289 gui_syncstatusbar_draw(&statusbars
, true);
291 gui_syncsplash(HZ
, ID2P(LANG_END_PLAYLIST
));
298 gui_wps
[i
].display
->clear_display();
299 if (!gui_wps
[i
].data
->wps_loaded
) {
300 if ( !gui_wps
[i
].data
->num_tokens
) {
301 /* set the default wps for the main-screen */
304 #ifdef HAVE_LCD_BITMAP
306 unload_wps_backdrop();
308 wps_data_load(gui_wps
[i
].data
,
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 wps_data_load(gui_wps
[i
].data
,
319 "%s%pp/%pe: %?it<%it|%fn> - %?ia<%ia|%d2> - %?id<%id|%d1>\n"
320 "%pc%?ps<*|/>%pt\n", false);
324 /* set the default wps for the remote-screen */
327 #if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
328 unload_remote_wps_backdrop();
330 wps_data_load(gui_wps
[i
].data
,
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"
345 gui_wps_refresh(&gui_wps
[i
], 0, WPS_REFRESH_ALL
);
350 bool update(struct gui_wps
*gwps
)
352 bool track_changed
= audio_has_changed_track();
353 bool retcode
= false;
355 gwps
->state
->nid3
= audio_next_track();
358 gwps
->display
->stop_scroll();
359 gwps
->state
->id3
= audio_current_track();
361 if (cuesheet_is_enabled() && gwps
->state
->id3
->cuesheet_type
362 && strcmp(gwps
->state
->id3
->path
, curr_cue
->audio_filename
))
364 /* the current cuesheet isn't the right one any more */
366 if (!strcmp(gwps
->state
->id3
->path
, temp_cue
->audio_filename
)) {
367 /* We have the new cuesheet in memory (temp_cue),
368 let's make it the current one ! */
369 memcpy(curr_cue
, temp_cue
, sizeof(struct cuesheet
));
372 /* We need to parse the new cuesheet */
374 char cuepath
[MAX_PATH
];
376 if (look_for_cuesheet_file(gwps
->state
->id3
->path
, cuepath
) &&
377 parse_cuesheet(cuepath
, curr_cue
))
379 gwps
->state
->id3
->cuesheet_type
= 1;
380 strcpy(curr_cue
->audio_filename
, gwps
->state
->id3
->path
);
384 cue_spoof_id3(curr_cue
, gwps
->state
->id3
);
387 if (gui_wps_display())
390 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
393 if (gwps
->state
->id3
)
395 strncpy(gwps
->state
->current_track_path
, gwps
->state
->id3
->path
,
396 sizeof(gwps
->state
->current_track_path
));
397 gwps
->state
->current_track_path
[sizeof(gwps
->state
->current_track_path
)-1] = '\0';
401 if (gwps
->state
->id3
)
403 if (cuesheet_is_enabled() && gwps
->state
->id3
->cuesheet_type
404 && (gwps
->state
->id3
->elapsed
< curr_cue
->curr_track
->offset
405 || (curr_cue
->curr_track_idx
< curr_cue
->track_count
- 1
406 && gwps
->state
->id3
->elapsed
>= (curr_cue
->curr_track
+1)->offset
)))
408 /* We've changed tracks within the cuesheet :
409 we need to update the ID3 info and refresh the WPS */
411 cue_find_current_track(curr_cue
, gwps
->state
->id3
->elapsed
);
412 cue_spoof_id3(curr_cue
, gwps
->state
->id3
);
414 gwps
->display
->stop_scroll();
415 if (gui_wps_display())
418 gui_wps_refresh(gwps
, 0, WPS_REFRESH_ALL
);
421 gui_wps_refresh(gwps
, 0, WPS_REFRESH_NON_STATIC
);
424 gui_wps_statusbar_draw(gwps
, false);
430 void display_keylock_text(bool locked
)
435 gui_wps
[i
].display
->stop_scroll();
438 s
= str(LANG_KEYLOCK_ON
);
440 s
= str(LANG_KEYLOCK_OFF
);
441 gui_syncsplash(HZ
, s
);
444 #ifdef HAVE_LCD_BITMAP
446 static void draw_progressbar(struct gui_wps
*gwps
, int line
)
448 struct wps_data
*data
= gwps
->data
;
449 struct screen
*display
= gwps
->display
;
450 struct wps_state
*state
= gwps
->state
;
451 int h
= font_get(FONT_UI
)->height
;
454 if (data
->progress_top
< 0)
455 sb_y
= line
*h
+ display
->getymargin() +
456 ((h
> data
->progress_height
+ 1)
457 ? (h
- data
->progress_height
) / 2 : 1);
459 sb_y
= data
->progress_top
;
461 if (!data
->progress_end
)
462 data
->progress_end
=display
->width
;
464 if (gwps
->data
->progressbar
.have_bitmap_pb
)
465 gui_bitmap_scrollbar_draw(display
, data
->progressbar
.bm
,
466 data
->progress_start
, sb_y
,
467 data
->progress_end
-data
->progress_start
,
468 data
->progressbar
.bm
.height
,
469 state
->id3
->length
? state
->id3
->length
: 1, 0,
470 state
->id3
->length
? state
->id3
->elapsed
471 + state
->ff_rewind_count
: 0,
474 gui_scrollbar_draw(display
, data
->progress_start
, sb_y
,
475 data
->progress_end
-data
->progress_start
,
476 data
->progress_height
,
477 state
->id3
->length
? state
->id3
->length
: 1, 0,
478 state
->id3
->length
? state
->id3
->elapsed
479 + state
->ff_rewind_count
: 0,
482 #ifdef AB_REPEAT_ENABLE
483 if ( ab_repeat_mode_enabled() && state
->id3
->length
!= 0 )
484 ab_draw_markers(display
, state
->id3
->length
,
485 data
->progress_start
, data
->progress_end
, sb_y
,
486 data
->progress_height
);
489 if ( cuesheet_is_enabled() && state
->id3
->cuesheet_type
)
490 cue_draw_markers(display
, state
->id3
->length
,
491 data
->progress_start
, data
->progress_end
,
492 sb_y
+1, data
->progress_height
-2);
495 /* clears the area where the image was shown */
496 static void clear_image_pos(struct gui_wps
*gwps
, int n
)
500 struct wps_data
*data
= gwps
->data
;
501 gwps
->display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
502 gwps
->display
->fillrect(data
->img
[n
].x
, data
->img
[n
].y
,
503 data
->img
[n
].bm
.width
, data
->img
[n
].bm
.height
);
504 gwps
->display
->set_drawmode(DRMODE_SOLID
);
507 static void wps_draw_image(struct gui_wps
*gwps
, int n
)
509 struct screen
*display
= gwps
->display
;
510 struct wps_data
*data
= gwps
->data
;
511 if(data
->img
[n
].always_display
)
512 display
->set_drawmode(DRMODE_FG
);
514 display
->set_drawmode(DRMODE_SOLID
);
517 if(data
->img
[n
].bm
.format
== FORMAT_MONO
) {
519 display
->mono_bitmap(data
->img
[n
].bm
.data
, data
->img
[n
].x
,
520 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
521 data
->img
[n
].bm
.height
);
524 display
->transparent_bitmap((fb_data
*)data
->img
[n
].bm
.data
,
526 data
->img
[n
].y
, data
->img
[n
].bm
.width
,
527 data
->img
[n
].bm
.height
);
532 static void wps_display_images(struct gui_wps
*gwps
)
534 if(!gwps
|| !gwps
->data
|| !gwps
->display
)
538 struct wps_data
*data
= gwps
->data
;
539 struct screen
*display
= gwps
->display
;
541 for (n
= 0; n
< MAX_IMAGES
; n
++)
543 if (data
->img
[n
].loaded
&&
544 (data
->img
[n
].display
|| data
->img
[n
].always_display
))
546 wps_draw_image(gwps
, n
);
549 display
->set_drawmode(DRMODE_SOLID
);
552 #else /* HAVE_LCD_CHARCELL */
554 static bool draw_player_progress(struct gui_wps
*gwps
)
556 struct wps_state
*state
= gwps
->state
;
557 struct screen
*display
= gwps
->display
;
558 unsigned char progress_pattern
[7];
565 if (state
->id3
->length
)
566 pos
= 36 * (state
->id3
->elapsed
+ state
->ff_rewind_count
)
567 / state
->id3
->length
;
569 for (i
= 0; i
< 7; i
++, pos
-= 5)
572 progress_pattern
[i
] = 0x1f;
574 progress_pattern
[i
] = 0x00;
576 progress_pattern
[i
] = 0x1f >> pos
;
579 display
->define_pattern(gwps
->data
->wps_progress_pat
[0], progress_pattern
);
583 static void draw_player_fullbar(struct gui_wps
*gwps
, char* buf
, int buf_size
)
585 static const unsigned char numbers
[10][4] = {
586 {0x0e, 0x0a, 0x0a, 0x0e}, /* 0 */
587 {0x04, 0x0c, 0x04, 0x04}, /* 1 */
588 {0x0e, 0x02, 0x04, 0x0e}, /* 2 */
589 {0x0e, 0x02, 0x06, 0x0e}, /* 3 */
590 {0x08, 0x0c, 0x0e, 0x04}, /* 4 */
591 {0x0e, 0x0c, 0x02, 0x0c}, /* 5 */
592 {0x0e, 0x08, 0x0e, 0x0e}, /* 6 */
593 {0x0e, 0x02, 0x04, 0x08}, /* 7 */
594 {0x0e, 0x0e, 0x0a, 0x0e}, /* 8 */
595 {0x0e, 0x0e, 0x02, 0x0e}, /* 9 */
598 struct wps_state
*state
= gwps
->state
;
599 struct screen
*display
= gwps
->display
;
600 struct wps_data
*data
= gwps
->data
;
601 unsigned char progress_pattern
[7];
610 if (!state
->id3
|| buf_size
< 34) /* worst case: 11x UTF-8 char + \0 */
613 time
= state
->id3
->elapsed
+ state
->ff_rewind_count
;
614 if (state
->id3
->length
)
615 pos
= 55 * time
/ state
->id3
->length
;
617 memset(timestr
, 0, sizeof(timestr
));
618 format_time(timestr
, sizeof(timestr
)-2, time
);
619 timestr
[strlen(timestr
)] = ':'; /* always safe */
621 for (i
= 0; i
< 11; i
++, pos
-= 5)
624 memset(progress_pattern
, 0, sizeof(progress_pattern
));
626 if ((digit
= timestr
[time_idx
]))
631 if (timestr
[time_idx
+ 1] == ':') /* ones, left aligned */
633 memcpy(progress_pattern
, numbers
[digit
], 4);
636 else /* tens, shifted right */
638 for (j
= 0; j
< 4; j
++)
639 progress_pattern
[j
] = numbers
[digit
][j
] >> 1;
641 if (time_idx
> 0) /* not the first group, add colon in front */
643 progress_pattern
[1] |= 0x10;
644 progress_pattern
[3] |= 0x10;
650 progress_pattern
[5] = progress_pattern
[6] = 0x1f;
653 if (pos
> 0 && pos
< 5)
656 progress_pattern
[5] = progress_pattern
[6] = (~0x1f >> pos
) & 0x1f;
659 if (softchar
&& pat_idx
< 8)
661 display
->define_pattern(data
->wps_progress_pat
[pat_idx
],
663 buf
= utf8encode(data
->wps_progress_pat
[pat_idx
], buf
);
667 buf
= utf8encode(' ', buf
);
669 buf
= utf8encode(0xe115, buf
); /* 2/7 _ */
674 #endif /* HAVE_LCD_CHARCELL */
676 /* Extract a part from a path.
678 * buf - buffer extract part to.
679 * buf_size - size of buffer.
680 * path - path to extract from.
681 * level - what to extract. 0 is file name, 1 is parent of file, 2 is
682 * parent of parent, etc.
684 * Returns buf if the desired level was found, NULL otherwise.
686 static char* get_dir(char* buf
, int buf_size
, const char* path
, int level
)
689 const char* last_sep
;
692 sep
= path
+ strlen(path
);
707 if (level
|| (last_sep
<= sep
))
710 len
= MIN(last_sep
- sep
, buf_size
- 1);
711 strncpy(buf
, sep
+ 1, len
);
716 /* Return the tag found at index i and write its value in buf.
717 The return value is buf if the tag had a value, or NULL if not.
719 intval is used with conditionals/enums: when this function is called,
720 intval should contain the number of options in the conditional/enum.
721 When this function returns, intval is -1 if the tag is non numeric or,
722 if the tag is numeric, intval is the enum case we want to go to.
723 When not treating a conditional/enum, intval should be NULL.
725 static char *get_token_value(struct gui_wps
*gwps
,
726 struct wps_token
*token
,
727 char *buf
, int buf_size
,
733 struct wps_data
*data
= gwps
->data
;
734 struct wps_state
*state
= gwps
->state
;
739 struct mp3entry
*id3
;
750 struct tm
* tm
= NULL
;
752 /* if the token is an RTC one, update the time
753 and do the necessary checks */
754 if (token
->type
>= WPS_TOKENS_RTC_BEGIN
755 && token
->type
<= WPS_TOKENS_RTC_END
)
773 case WPS_TOKEN_CHARACTER
:
774 return &(token
->value
.c
);
776 case WPS_TOKEN_STRING
:
777 return data
->strings
[token
->value
.i
];
779 case WPS_TOKEN_TRACK_TIME_ELAPSED
:
780 format_time(buf
, buf_size
,
781 id3
->elapsed
+ state
->ff_rewind_count
);
784 case WPS_TOKEN_TRACK_TIME_REMAINING
:
785 format_time(buf
, buf_size
,
786 id3
->length
- id3
->elapsed
-
787 state
->ff_rewind_count
);
790 case WPS_TOKEN_TRACK_LENGTH
:
791 format_time(buf
, buf_size
, id3
->length
);
794 case WPS_TOKEN_PLAYLIST_ENTRIES
:
795 snprintf(buf
, buf_size
, "%d", playlist_amount());
798 case WPS_TOKEN_PLAYLIST_NAME
:
799 return playlist_name(NULL
, buf
, buf_size
);
801 case WPS_TOKEN_PLAYLIST_POSITION
:
802 snprintf(buf
, buf_size
, "%d",
803 playlist_get_display_index());
806 case WPS_TOKEN_PLAYLIST_SHUFFLE
:
807 if ( global_settings
.playlist_shuffle
)
813 case WPS_TOKEN_VOLUME
:
814 snprintf(buf
, buf_size
, "%d", global_settings
.volume
);
817 if (global_settings
.volume
== sound_min(SOUND_VOLUME
))
821 else if (global_settings
.volume
== 0)
825 else if (global_settings
.volume
> 0)
831 *intval
= (limit
- 3) * (global_settings
.volume
832 - sound_min(SOUND_VOLUME
) - 1)
833 / (-1 - sound_min(SOUND_VOLUME
)) + 2;
838 case WPS_TOKEN_TRACK_ELAPSED_PERCENT
:
839 if (id3
->length
<= 0)
844 *intval
= limit
* (id3
->elapsed
+ state
->ff_rewind_count
)
847 snprintf(buf
, buf_size
, "%d",
848 100*(id3
->elapsed
+ state
->ff_rewind_count
) / id3
->length
);
851 case WPS_TOKEN_METADATA_ARTIST
:
854 case WPS_TOKEN_METADATA_COMPOSER
:
855 return id3
->composer
;
857 case WPS_TOKEN_METADATA_ALBUM
:
860 case WPS_TOKEN_METADATA_ALBUM_ARTIST
:
861 return id3
->albumartist
;
863 case WPS_TOKEN_METADATA_GROUPING
:
864 return id3
->grouping
;
866 case WPS_TOKEN_METADATA_GENRE
:
867 return id3
->genre_string
;
869 case WPS_TOKEN_METADATA_DISC_NUMBER
:
870 if (id3
->disc_string
)
871 return id3
->disc_string
;
873 snprintf(buf
, buf_size
, "%d", id3
->discnum
);
878 case WPS_TOKEN_METADATA_TRACK_NUMBER
:
879 if (id3
->track_string
)
880 return id3
->track_string
;
883 snprintf(buf
, buf_size
, "%d", id3
->tracknum
);
888 case WPS_TOKEN_METADATA_TRACK_TITLE
:
891 case WPS_TOKEN_METADATA_VERSION
:
892 switch (id3
->id3version
)
913 case WPS_TOKEN_METADATA_YEAR
:
914 if( id3
->year_string
)
915 return id3
->year_string
;
918 snprintf(buf
, buf_size
, "%d", id3
->year
);
923 case WPS_TOKEN_METADATA_COMMENT
:
927 case WPS_TOKEN_ALBUMART_DISPLAY
:
928 draw_album_art(gwps
, audio_current_aa_hid(), false);
931 case WPS_TOKEN_ALBUMART_FOUND
:
932 if (audio_current_aa_hid() >= 0) {
933 snprintf(buf
, buf_size
, "C");
939 case WPS_TOKEN_FILE_BITRATE
:
941 snprintf(buf
, buf_size
, "%d", id3
->bitrate
);
943 snprintf(buf
, buf_size
, "?");
946 case WPS_TOKEN_FILE_CODEC
:
949 if(id3
->codectype
== AFMT_UNKNOWN
)
950 *intval
= AFMT_NUM_CODECS
;
952 *intval
= id3
->codectype
;
954 return id3_get_codec(id3
);
956 case WPS_TOKEN_FILE_FREQUENCY
:
957 snprintf(buf
, buf_size
, "%ld", id3
->frequency
);
960 case WPS_TOKEN_FILE_FREQUENCY_KHZ
:
961 /* ignore remainders < 100, so 22050 Hz becomes just 22k */
962 if ((id3
->frequency
% 1000) < 100)
963 snprintf(buf
, buf_size
, "%ld", id3
->frequency
/ 1000);
965 snprintf(buf
, buf_size
, "%ld.%d",
966 id3
->frequency
/ 1000,
967 (id3
->frequency
% 1000) / 100);
970 case WPS_TOKEN_FILE_NAME
:
971 if (get_dir(buf
, buf_size
, id3
->path
, 0)) {
972 /* Remove extension */
973 char* sep
= strrchr(buf
, '.');
983 case WPS_TOKEN_FILE_NAME_WITH_EXTENSION
:
984 return get_dir(buf
, buf_size
, id3
->path
, 0);
986 case WPS_TOKEN_FILE_PATH
:
989 case WPS_TOKEN_FILE_SIZE
:
990 snprintf(buf
, buf_size
, "%ld", id3
->filesize
/ 1024);
993 case WPS_TOKEN_FILE_VBR
:
994 return id3
->vbr
? "(avg)" : NULL
;
996 case WPS_TOKEN_FILE_DIRECTORY
:
997 return get_dir(buf
, buf_size
, id3
->path
, token
->value
.i
);
999 case WPS_TOKEN_BATTERY_PERCENT
:
1001 int l
= battery_level();
1005 limit
= MAX(limit
, 2);
1007 /* First enum is used for "unknown level". */
1008 *intval
= (limit
- 1) * l
/ 100 + 2;
1015 snprintf(buf
, buf_size
, "%d", l
);
1022 case WPS_TOKEN_BATTERY_VOLTS
:
1024 unsigned int v
= battery_voltage();
1025 snprintf(buf
, buf_size
, "%d.%02d", v
/ 1000, (v
% 1000) / 10);
1029 case WPS_TOKEN_BATTERY_TIME
:
1031 int t
= battery_time();
1033 snprintf(buf
, buf_size
, "%dh %dm", t
/ 60, t
% 60);
1035 strncpy(buf
, "?h ?m", buf_size
);
1040 case WPS_TOKEN_BATTERY_CHARGER_CONNECTED
:
1042 if(charger_input_state
==CHARGER
)
1048 #if CONFIG_CHARGING >= CHARGING_MONITOR
1049 case WPS_TOKEN_BATTERY_CHARGING
:
1051 if (charge_state
== CHARGING
|| charge_state
== TOPOFF
) {
1058 case WPS_TOKEN_BATTERY_SLEEPTIME
:
1060 if (get_sleep_timer() == 0)
1064 format_time(buf
, buf_size
, get_sleep_timer() * 1000);
1069 case WPS_TOKEN_PLAYBACK_STATUS
:
1071 int status
= audio_status();
1073 if (status
== AUDIO_STATUS_PLAY
&& \
1074 !(status
& AUDIO_STATUS_PAUSE
))
1076 if (audio_status() & AUDIO_STATUS_PAUSE
&& \
1077 (! status_get_ffmode()))
1079 if (status_get_ffmode() == STATUS_FASTFORWARD
)
1081 if (status_get_ffmode() == STATUS_FASTBACKWARD
)
1088 snprintf(buf
, buf_size
, "%d", mode
);
1092 case WPS_TOKEN_REPEAT_MODE
:
1094 *intval
= global_settings
.repeat_mode
+ 1;
1095 snprintf(buf
, buf_size
, "%d", *intval
);
1097 case WPS_TOKEN_RTC_12HOUR_CFG
:
1099 *intval
= global_settings
.timeformat
+ 1;
1100 snprintf(buf
, buf_size
, "%d", *intval
);
1103 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1104 /* d: day of month (01..31) */
1105 snprintf(buf
, buf_size
, "%02d", tm
->tm_mday
);
1108 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1109 /* e: day of month, blank padded ( 1..31) */
1110 snprintf(buf
, buf_size
, "%2d", tm
->tm_mday
);
1113 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1114 /* H: hour (00..23) */
1115 snprintf(buf
, buf_size
, "%02d", tm
->tm_hour
);
1118 case WPS_TOKEN_RTC_HOUR_24
:
1119 /* k: hour ( 0..23) */
1120 snprintf(buf
, buf_size
, "%2d", tm
->tm_hour
);
1123 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1124 /* I: hour (01..12) */
1125 snprintf(buf
, buf_size
, "%02d",
1126 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1129 case WPS_TOKEN_RTC_HOUR_12
:
1130 /* l: hour ( 1..12) */
1131 snprintf(buf
, buf_size
, "%2d",
1132 (tm
->tm_hour
% 12 == 0) ? 12 : tm
->tm_hour
% 12);
1135 case WPS_TOKEN_RTC_MONTH
:
1136 /* m: month (01..12) */
1138 *intval
= tm
->tm_mon
+ 1;
1139 snprintf(buf
, buf_size
, "%02d", tm
->tm_mon
+ 1);
1142 case WPS_TOKEN_RTC_MINUTE
:
1143 /* M: minute (00..59) */
1144 snprintf(buf
, buf_size
, "%02d", tm
->tm_min
);
1147 case WPS_TOKEN_RTC_SECOND
:
1148 /* S: second (00..59) */
1149 snprintf(buf
, buf_size
, "%02d", tm
->tm_sec
);
1152 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1153 /* y: last two digits of year (00..99) */
1154 snprintf(buf
, buf_size
, "%02d", tm
->tm_year
% 100);
1157 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1158 /* Y: year (1970...) */
1159 snprintf(buf
, buf_size
, "%04d", tm
->tm_year
+ 1900);
1162 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1163 /* p: upper case AM or PM indicator */
1164 snprintf(buf
, buf_size
, (tm
->tm_hour
/12 == 0) ? "AM" : "PM");
1167 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1168 /* P: lower case am or pm indicator */
1169 snprintf(buf
, buf_size
, (tm
->tm_hour
/12 == 0) ? "am" : "pm");
1172 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1173 /* a: abbreviated weekday name (Sun..Sat) */
1174 snprintf(buf
, buf_size
, "%s",str(LANG_WEEKDAY_SUNDAY
+ tm
->tm_wday
));
1177 case WPS_TOKEN_RTC_MONTH_NAME
:
1178 /* b: abbreviated month name (Jan..Dec) */
1179 snprintf(buf
, buf_size
, "%s",str(LANG_MONTH_JANUARY
+ tm
->tm_mon
));
1182 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1183 /* u: day of week (1..7); 1 is Monday */
1185 *intval
= (tm
->tm_wday
== 0) ? 7 : tm
->tm_wday
;
1186 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
+ 1);
1189 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1190 /* w: day of week (0..6); 0 is Sunday */
1192 *intval
= tm
->tm_wday
+ 1;
1193 snprintf(buf
, buf_size
, "%1d", tm
->tm_wday
);
1196 case WPS_TOKEN_RTC_DAY_OF_MONTH
:
1197 case WPS_TOKEN_RTC_DAY_OF_MONTH_BLANK_PADDED
:
1198 case WPS_TOKEN_RTC_HOUR_24_ZERO_PADDED
:
1199 case WPS_TOKEN_RTC_HOUR_24
:
1200 case WPS_TOKEN_RTC_HOUR_12_ZERO_PADDED
:
1201 case WPS_TOKEN_RTC_HOUR_12
:
1202 case WPS_TOKEN_RTC_MONTH
:
1203 case WPS_TOKEN_RTC_MINUTE
:
1204 case WPS_TOKEN_RTC_SECOND
:
1205 case WPS_TOKEN_RTC_AM_PM_UPPER
:
1206 case WPS_TOKEN_RTC_AM_PM_LOWER
:
1207 case WPS_TOKEN_RTC_YEAR_2_DIGITS
:
1208 strncpy(buf
, "--", buf_size
);
1210 case WPS_TOKEN_RTC_YEAR_4_DIGITS
:
1211 strncpy(buf
, "----", buf_size
);
1213 case WPS_TOKEN_RTC_WEEKDAY_NAME
:
1214 case WPS_TOKEN_RTC_MONTH_NAME
:
1215 strncpy(buf
, "---", buf_size
);
1217 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_MON
:
1218 case WPS_TOKEN_RTC_DAY_OF_WEEK_START_SUN
:
1219 strncpy(buf
, "-", buf_size
);
1223 #ifdef HAVE_LCD_CHARCELLS
1224 case WPS_TOKEN_PROGRESSBAR
:
1226 char *end
= utf8encode(data
->wps_progress_pat
[0], buf
);
1231 case WPS_TOKEN_PLAYER_PROGRESSBAR
:
1234 /* we need 11 characters (full line) for
1236 snprintf(buf
, buf_size
, " ");
1240 /* Tell the user if we have an OldPlayer */
1241 snprintf(buf
, buf_size
, " <Old LCD> ");
1246 #ifdef HAVE_TAGCACHE
1247 case WPS_TOKEN_DATABASE_PLAYCOUNT
:
1249 *intval
= id3
->playcount
+ 1;
1251 snprintf(buf
, buf_size
, "%ld", id3
->playcount
);
1254 case WPS_TOKEN_DATABASE_RATING
:
1256 *intval
= id3
->rating
+ 1;
1258 snprintf(buf
, buf_size
, "%d", id3
->rating
);
1261 case WPS_TOKEN_DATABASE_AUTOSCORE
:
1263 *intval
= id3
->score
+ 1;
1265 snprintf(buf
, buf_size
, "%d", id3
->score
);
1269 #if (CONFIG_CODEC == SWCODEC)
1270 case WPS_TOKEN_CROSSFADE
:
1272 *intval
= global_settings
.crossfade
+ 1;
1273 snprintf(buf
, buf_size
, "%d", global_settings
.crossfade
);
1276 case WPS_TOKEN_REPLAYGAIN
:
1280 if (global_settings
.replaygain
== 0)
1285 get_replaygain_mode(id3
->track_gain_string
!= NULL
,
1286 id3
->album_gain_string
!= NULL
);
1288 val
= 6; /* no tag */
1292 if (global_settings
.replaygain_type
== REPLAYGAIN_SHUFFLE
)
1307 strncpy(buf
, id3
->track_gain_string
, buf_size
);
1311 strncpy(buf
, id3
->album_gain_string
, buf_size
);
1316 #endif /* (CONFIG_CODEC == SWCODEC) */
1318 #if (CONFIG_CODEC != MAS3507D)
1319 case WPS_TOKEN_SOUND_PITCH
:
1321 int val
= sound_get_pitch();
1322 snprintf(buf
, buf_size
, "%d.%d",
1323 val
/ 10, val
% 10);
1328 case WPS_TOKEN_MAIN_HOLD
:
1329 #ifdef HAS_BUTTON_HOLD
1332 if (is_keys_locked())
1333 #endif /*hold switch or softlock*/
1338 #ifdef HAS_REMOTE_BUTTON_HOLD
1339 case WPS_TOKEN_REMOTE_HOLD
:
1340 if (remote_button_hold())
1346 #if (CONFIG_LED == LED_VIRTUAL) || defined(HAVE_REMOTE_LCD)
1347 case WPS_TOKEN_VLED_HDD
:
1354 #ifdef HAVE_LCD_BITMAP
1355 case WPS_TOKEN_LEFTMARGIN
:
1356 gwps
->display
->setmargins(token
->value
.i
,
1357 gwps
->display
->getymargin());
1366 /* Return the index to the end token for the conditional token at index.
1367 The conditional token can be either a start token or a separator
1368 (i.e. option) token.
1370 static int find_conditional_end(struct wps_data
*data
, int index
)
1373 while (data
->tokens
[ret
].type
!= WPS_TOKEN_CONDITIONAL_END
)
1374 ret
= data
->tokens
[ret
].value
.i
;
1376 /* ret now is the index to the end token for the conditional. */
1380 /* Return the index of the appropriate case for the conditional
1381 that starts at cond_index.
1383 static bool evaluate_conditional(struct gui_wps
*gwps
, int *token_index
)
1388 struct wps_data
*data
= gwps
->data
;
1391 int cond_index
= *token_index
;
1392 char result
[128], *value
;
1393 unsigned char num_options
= data
->tokens
[cond_index
].value
.i
& 0xFF;
1394 unsigned char prev_val
= (data
->tokens
[cond_index
].value
.i
& 0xFF00) >> 8;
1396 /* treat ?xx<true> constructs as if they had 2 options. */
1397 if (num_options
< 2)
1400 int intval
= num_options
;
1401 /* get_token_value needs to know the number of options in the enum */
1402 value
= get_token_value(gwps
, &data
->tokens
[cond_index
+ 1],
1403 result
, sizeof(result
), &intval
);
1405 /* intval is now the number of the enum option we want to read,
1406 starting from 1. If intval is -1, we check if value is empty. */
1408 intval
= (value
&& *value
) ? 1 : num_options
;
1409 else if (intval
> num_options
|| intval
< 1)
1410 intval
= num_options
;
1412 data
->tokens
[cond_index
].value
.i
= (intval
<< 8) + num_options
;
1414 /* skip to the right enum case */
1415 int next
= cond_index
+ 2;
1416 for (i
= 1; i
< intval
; i
++)
1418 next
= data
->tokens
[next
].value
.i
;
1420 *token_index
= next
;
1422 if (prev_val
== intval
)
1424 /* Same conditional case as previously. Return without clearing the
1429 cond_end
= find_conditional_end(data
, cond_index
+ 2);
1430 for (i
= cond_index
+ 3; i
< cond_end
; i
++)
1432 #ifdef HAVE_LCD_BITMAP
1433 /* clear all pictures in the conditional and nested ones */
1434 if (data
->tokens
[i
].type
== WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
)
1435 clear_image_pos(gwps
, data
->tokens
[i
].value
.i
);
1437 #ifdef HAVE_ALBUMART
1438 if (data
->tokens
[i
].type
== WPS_TOKEN_ALBUMART_DISPLAY
)
1439 draw_album_art(gwps
, audio_current_aa_hid(), true);
1446 /* Read a (sub)line to the given alignment format buffer.
1447 linebuf is the buffer where the data is actually stored.
1448 align is the alignment format that'll be used to display the text.
1449 The return value indicates whether the line needs to be updated.
1451 static bool get_line(struct gui_wps
*gwps
,
1452 int line
, int subline
,
1453 struct align_pos
*align
,
1457 struct wps_data
*data
= gwps
->data
;
1460 char *buf
= linebuf
; /* will always point to the writing position */
1461 char *linebuf_end
= linebuf
+ linebuf_size
- 1;
1462 int i
, last_token_idx
;
1463 bool update
= false;
1465 /* alignment-related variables */
1467 char* cur_align_start
;
1468 cur_align_start
= buf
;
1469 cur_align
= WPS_ALIGN_LEFT
;
1471 align
->center
= NULL
;
1472 align
->right
= NULL
;
1474 #ifdef HAVE_LCD_BITMAP
1475 /* Reset margins - only bitmap targets modify them */
1476 gwps
->display
->setmargins(0, gwps
->display
->getymargin());
1479 /* Process all tokens of the desired subline */
1480 last_token_idx
= wps_last_token_index(data
, line
, subline
);
1481 for (i
= wps_first_token_index(data
, line
, subline
);
1482 i
<= last_token_idx
; i
++)
1484 switch(data
->tokens
[i
].type
)
1486 case WPS_TOKEN_CONDITIONAL
:
1487 /* place ourselves in the right conditional case */
1488 update
|= evaluate_conditional(gwps
, &i
);
1491 case WPS_TOKEN_CONDITIONAL_OPTION
:
1492 /* we've finished in the curent conditional case,
1493 skip to the end of the conditional structure */
1494 i
= find_conditional_end(data
, i
);
1497 #ifdef HAVE_LCD_BITMAP
1498 case WPS_TOKEN_IMAGE_PRELOAD_DISPLAY
:
1500 struct gui_img
*img
= data
->img
;
1501 int n
= data
->tokens
[i
].value
.i
;
1502 if (n
>= 0 && n
< MAX_IMAGES
&& img
[n
].loaded
)
1503 img
[n
].display
= true;
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
;
1547 /* get the value of the tag and copy it to the buffer */
1548 char *value
= get_token_value(gwps
, &data
->tokens
[i
],
1549 temp_buf
, sizeof(temp_buf
), NULL
);
1553 while (*value
&& (buf
< linebuf_end
))
1561 /* close the current alignment */
1564 case WPS_ALIGN_LEFT
:
1565 align
->left
= cur_align_start
;
1568 case WPS_ALIGN_CENTER
:
1569 align
->center
= cur_align_start
;
1572 case WPS_ALIGN_RIGHT
:
1573 align
->right
= cur_align_start
;
1580 static void get_subline_timeout(struct gui_wps
*gwps
, int line
, int subline
)
1582 struct wps_data
*data
= gwps
->data
;
1584 int subline_idx
= wps_subline_index(data
, line
, subline
);
1585 int last_token_idx
= wps_last_token_index(data
, line
, subline
);
1587 data
->sublines
[subline_idx
].time_mult
= DEFAULT_SUBLINE_TIME_MULTIPLIER
;
1589 for (i
= wps_first_token_index(data
, line
, subline
);
1590 i
<= last_token_idx
; i
++)
1592 switch(data
->tokens
[i
].type
)
1594 case WPS_TOKEN_CONDITIONAL
:
1595 /* place ourselves in the right conditional case */
1596 evaluate_conditional(gwps
, &i
);
1599 case WPS_TOKEN_CONDITIONAL_OPTION
:
1600 /* we've finished in the curent conditional case,
1601 skip to the end of the conditional structure */
1602 i
= find_conditional_end(data
, i
);
1605 case WPS_TOKEN_SUBLINE_TIMEOUT
:
1606 data
->sublines
[subline_idx
].time_mult
= data
->tokens
[i
].value
.i
;
1615 /* Calculates which subline should be displayed for the specified line
1616 Returns true iff the subline must be refreshed */
1617 static bool update_curr_subline(struct gui_wps
*gwps
, int line
)
1619 struct wps_data
*data
= gwps
->data
;
1621 int search
, search_start
, num_sublines
;
1623 bool new_subline_refresh
;
1624 bool only_one_subline
;
1626 num_sublines
= data
->lines
[line
].num_sublines
;
1627 reset_subline
= (data
->lines
[line
].curr_subline
== SUBLINE_RESET
);
1628 new_subline_refresh
= false;
1629 only_one_subline
= false;
1631 /* if time to advance to next sub-line */
1632 if (TIME_AFTER(current_tick
, data
->lines
[line
].subline_expire_time
- 1) ||
1635 /* search all sublines until the next subline with time > 0
1636 is found or we get back to the subline we started with */
1640 search_start
= data
->lines
[line
].curr_subline
;
1642 for (search
= 0; search
< num_sublines
; search
++)
1644 data
->lines
[line
].curr_subline
++;
1646 /* wrap around if beyond last defined subline or WPS_MAX_SUBLINES */
1647 if (data
->lines
[line
].curr_subline
== num_sublines
)
1649 if (data
->lines
[line
].curr_subline
== 1)
1650 only_one_subline
= true;
1651 data
->lines
[line
].curr_subline
= 0;
1654 /* if back where we started after search or
1655 only one subline is defined on the line */
1656 if (((search
> 0) &&
1657 (data
->lines
[line
].curr_subline
== search_start
)) ||
1660 /* no other subline with a time > 0 exists */
1661 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1663 data
->lines
[line
].subline_expire_time
) + 100 * HZ
;
1668 /* get initial time multiplier for this subline */
1669 get_subline_timeout(gwps
, line
, data
->lines
[line
].curr_subline
);
1671 int subline_idx
= wps_subline_index(data
, line
,
1672 data
->lines
[line
].curr_subline
);
1674 /* only use this subline if subline time > 0 */
1675 if (data
->sublines
[subline_idx
].time_mult
> 0)
1677 new_subline_refresh
= true;
1678 data
->lines
[line
].subline_expire_time
= (reset_subline
?
1679 current_tick
: data
->lines
[line
].subline_expire_time
) +
1680 BASE_SUBLINE_TIME
*data
->sublines
[subline_idx
].time_mult
;
1687 return new_subline_refresh
;
1690 /* Display a line appropriately according to its alignment format.
1691 format_align contains the text, separated between left, center and right.
1692 line is the index of the line on the screen.
1693 scroll indicates whether the line is a scrolling one or not.
1695 static void write_line(struct screen
*display
,
1696 struct align_pos
*format_align
,
1701 int left_width
= 0, left_xpos
;
1702 int center_width
= 0, center_xpos
;
1703 int right_width
= 0, right_xpos
;
1709 /* calculate different string sizes and positions */
1710 display
->getstringsize((unsigned char *)" ", &space_width
, &string_height
);
1711 if (format_align
->left
!= 0) {
1712 display
->getstringsize((unsigned char *)format_align
->left
,
1713 &left_width
, &string_height
);
1716 if (format_align
->right
!= 0) {
1717 display
->getstringsize((unsigned char *)format_align
->right
,
1718 &right_width
, &string_height
);
1721 if (format_align
->center
!= 0) {
1722 display
->getstringsize((unsigned char *)format_align
->center
,
1723 ¢er_width
, &string_height
);
1726 left_xpos
= display
->getxmargin();
1727 right_xpos
= (display
->width
- right_width
);
1728 center_xpos
= (display
->width
+ left_xpos
- center_width
) / 2;
1730 scroll_width
= display
->width
- left_xpos
;
1732 /* Checks for overlapping strings.
1733 If needed the overlapping strings will be merged, separated by a
1736 /* CASE 1: left and centered string overlap */
1737 /* there is a left string, need to merge left and center */
1738 if ((left_width
!= 0 && center_width
!= 0) &&
1739 (left_xpos
+ left_width
+ space_width
> center_xpos
)) {
1740 /* replace the former separator '\0' of left and
1741 center string with a space */
1742 *(--format_align
->center
) = ' ';
1743 /* calculate the new width and position of the merged string */
1744 left_width
= left_width
+ space_width
+ center_width
;
1745 /* there is no centered string anymore */
1748 /* there is no left string, move center to left */
1749 if ((left_width
== 0 && center_width
!= 0) &&
1750 (left_xpos
+ left_width
> center_xpos
)) {
1751 /* move the center string to the left string */
1752 format_align
->left
= format_align
->center
;
1753 /* calculate the new width and position of the string */
1754 left_width
= center_width
;
1755 /* there is no centered string anymore */
1759 /* CASE 2: centered and right string overlap */
1760 /* there is a right string, need to merge center and right */
1761 if ((center_width
!= 0 && right_width
!= 0) &&
1762 (center_xpos
+ center_width
+ space_width
> right_xpos
)) {
1763 /* replace the former separator '\0' of center and
1764 right string with a space */
1765 *(--format_align
->right
) = ' ';
1766 /* move the center string to the right after merge */
1767 format_align
->right
= format_align
->center
;
1768 /* calculate the new width and position of the merged string */
1769 right_width
= center_width
+ space_width
+ right_width
;
1770 right_xpos
= (display
->width
- right_width
);
1771 /* there is no centered string anymore */
1774 /* there is no right string, move center to right */
1775 if ((center_width
!= 0 && right_width
== 0) &&
1776 (center_xpos
+ center_width
> right_xpos
)) {
1777 /* move the center string to the right string */
1778 format_align
->right
= format_align
->center
;
1779 /* calculate the new width and position of the string */
1780 right_width
= center_width
;
1781 right_xpos
= (display
->width
- right_width
);
1782 /* there is no centered string anymore */
1786 /* CASE 3: left and right overlap
1787 There is no center string anymore, either there never
1788 was one or it has been merged in case 1 or 2 */
1789 /* there is a left string, need to merge left and right */
1790 if ((left_width
!= 0 && center_width
== 0 && right_width
!= 0) &&
1791 (left_xpos
+ left_width
+ space_width
> right_xpos
)) {
1792 /* replace the former separator '\0' of left and
1793 right string with a space */
1794 *(--format_align
->right
) = ' ';
1795 /* calculate the new width and position of the string */
1796 left_width
= left_width
+ space_width
+ right_width
;
1797 /* there is no right string anymore */
1800 /* there is no left string, move right to left */
1801 if ((left_width
== 0 && center_width
== 0 && right_width
!= 0) &&
1802 (left_width
> right_xpos
)) {
1803 /* move the right string to the left string */
1804 format_align
->left
= format_align
->right
;
1805 /* calculate the new width and position of the string */
1806 left_width
= right_width
;
1807 /* there is no right string anymore */
1811 ypos
= (line
* string_height
) + display
->getymargin();
1814 if (scroll
&& ((left_width
> scroll_width
) ||
1815 (center_width
> scroll_width
) ||
1816 (right_width
> scroll_width
)))
1818 display
->puts_scroll(0, line
,
1819 (unsigned char *)format_align
->left
);
1823 #ifdef HAVE_LCD_BITMAP
1824 /* clear the line first */
1825 display
->set_drawmode(DRMODE_SOLID
|DRMODE_INVERSEVID
);
1826 display
->fillrect(left_xpos
, ypos
, display
->width
, string_height
);
1827 display
->set_drawmode(DRMODE_SOLID
);
1830 /* Nasty hack: we output an empty scrolling string,
1831 which will reset the scroller for that line */
1832 display
->puts_scroll(0, line
, (unsigned char *)"");
1834 /* print aligned strings */
1835 if (left_width
!= 0)
1837 display
->putsxy(left_xpos
, ypos
,
1838 (unsigned char *)format_align
->left
);
1840 if (center_width
!= 0)
1842 display
->putsxy(center_xpos
, ypos
,
1843 (unsigned char *)format_align
->center
);
1845 if (right_width
!= 0)
1847 display
->putsxy(right_xpos
, ypos
,
1848 (unsigned char *)format_align
->right
);
1853 /* Refresh the WPS according to refresh_mode. */
1854 bool gui_wps_refresh(struct gui_wps
*gwps
,
1856 unsigned char refresh_mode
)
1858 struct wps_data
*data
= gwps
->data
;
1859 struct screen
*display
= gwps
->display
;
1860 struct wps_state
*state
= gwps
->state
;
1862 if(!gwps
|| !data
|| !state
|| !display
)
1865 int line
, i
, subline_idx
;
1866 unsigned char flags
;
1867 char linebuf
[MAX_PATH
];
1869 struct align_pos align
;
1871 align
.center
= NULL
;
1874 bool update_line
, new_subline_refresh
;
1876 #ifdef HAVE_LCD_BITMAP
1877 gui_wps_statusbar_draw(gwps
, true);
1879 /* to find out wether the peak meter is enabled we
1880 assume it wasn't until we find a line that contains
1881 the peak meter. We can't use peak_meter_enabled itself
1882 because that would mean to turn off the meter thread
1883 temporarily. (That shouldn't matter unless yield
1884 or sleep is called but who knows...)
1886 bool enable_pm
= false;
1888 /* Set images to not to be displayed */
1889 for (i
= 0; i
< MAX_IMAGES
; i
++)
1891 data
->img
[i
].display
= false;
1895 /* reset to first subline if refresh all flag is set */
1896 if (refresh_mode
== WPS_REFRESH_ALL
)
1898 for (i
= 0; i
< data
->num_lines
; i
++)
1900 data
->lines
[i
].curr_subline
= SUBLINE_RESET
;
1904 #ifdef HAVE_LCD_CHARCELLS
1905 for (i
= 0; i
< 8; i
++)
1907 if (data
->wps_progress_pat
[i
] == 0)
1908 data
->wps_progress_pat
[i
] = display
->get_locked_pattern();
1914 display
->stop_scroll();
1918 state
->ff_rewind_count
= ffwd_offset
;
1920 for (line
= 0; line
< data
->num_lines
; line
++)
1922 memset(linebuf
, 0, sizeof(linebuf
));
1923 update_line
= false;
1925 /* get current subline for the line */
1926 new_subline_refresh
= update_curr_subline(gwps
, line
);
1928 subline_idx
= wps_subline_index(data
, line
,
1929 data
->lines
[line
].curr_subline
);
1930 flags
= data
->sublines
[subline_idx
].line_type
;
1932 if (refresh_mode
== WPS_REFRESH_ALL
|| (flags
& refresh_mode
)
1933 || new_subline_refresh
)
1935 /* get_line tells us if we need to update the line */
1936 update_line
= get_line(gwps
, line
, data
->lines
[line
].curr_subline
,
1937 &align
, linebuf
, sizeof(linebuf
));
1940 #ifdef HAVE_LCD_BITMAP
1942 if (flags
& refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1944 /* the progressbar should be alone on its line */
1945 update_line
= false;
1946 draw_progressbar(gwps
, line
);
1950 if (flags
& refresh_mode
& WPS_REFRESH_PEAK_METER
)
1952 /* the peakmeter should be alone on its line */
1953 update_line
= false;
1955 int h
= font_get(FONT_UI
)->height
;
1956 int peak_meter_y
= display
->getymargin() + line
* h
;
1958 /* The user might decide to have the peak meter in the last
1959 line so that it is only displayed if no status bar is
1960 visible. If so we neither want do draw nor enable the
1962 if (peak_meter_y
+ h
<= display
->height
) {
1963 /* found a line with a peak meter -> remember that we must
1966 peak_meter_screen(gwps
->display
, 0, peak_meter_y
,
1967 MIN(h
, display
->height
- peak_meter_y
));
1971 #else /* HAVE_LCD_CHARCELL */
1974 if (flags
& refresh_mode
& WPS_REFRESH_PLAYER_PROGRESS
)
1976 if (data
->full_line_progressbar
)
1977 draw_player_fullbar(gwps
, linebuf
, sizeof(linebuf
));
1979 draw_player_progress(gwps
);
1985 if (flags
& WPS_REFRESH_SCROLL
)
1987 /* if the line is a scrolling one we don't want to update
1988 too often, so that it has the time to scroll */
1989 if ((refresh_mode
& WPS_REFRESH_SCROLL
) || new_subline_refresh
)
1990 write_line(display
, &align
, line
, true);
1993 write_line(display
, &align
, line
, false);
1997 #ifdef HAVE_LCD_BITMAP
1998 data
->peak_meter_enabled
= enable_pm
;
1999 wps_display_images(gwps
);
2004 #ifdef HAVE_BACKLIGHT
2005 if (global_settings
.caption_backlight
&& state
->id3
)
2007 /* turn on backlight n seconds before track ends, and turn it off n
2008 seconds into the new track. n == backlight_timeout, or 5s */
2009 int n
= global_settings
.backlight_timeout
* 1000;
2012 n
= 5000; /* use 5s if backlight is always on or off */
2014 if (((state
->id3
->elapsed
< 1000) ||
2015 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
)) &&
2016 (state
->paused
== false))
2020 #ifdef HAVE_REMOTE_LCD
2021 if (global_settings
.remote_caption_backlight
&& state
->id3
)
2023 /* turn on remote backlight n seconds before track ends, and turn it
2024 off n seconds into the new track. n == remote_backlight_timeout,
2026 int n
= global_settings
.remote_backlight_timeout
* 1000;
2029 n
= 5000; /* use 5s if backlight is always on or off */
2031 if (((state
->id3
->elapsed
< 1000) ||
2032 ((state
->id3
->length
- state
->id3
->elapsed
) < (unsigned)n
)) &&
2033 (state
->paused
== false))
2034 remote_backlight_on();