Compare commits

...

5 commits

Author SHA1 Message Date
seroteunine
a1040cda5d plugins: add timer/countdown plugin
-New countdown timer plugin with pause, overtime support
-Add full name to credits and manual entry
-Make status strings translatable

Change-Id: I1437b2e5ac5ede292bdab8d36e58b81326ea2ba3
2026-04-16 21:35:52 -04:00
Christian Soffke
cc7418dd8b dsp: add option to swap left and right channels
Change-Id: Id4b518638436576cfb5e747548f10ece6e58eba0
2026-04-16 21:08:53 -04:00
Christian Soffke
5ac105c837 tagtree: Add menu item "Show in Files"
Quickly reveal a database item in the File Browser.
In case of entire tables, the first entry will be revealed.

Change-Id: I4191f27ea2ab7cacbe375719314d7eb23301fd07
2026-04-16 21:04:41 -04:00
Christian Soffke
e405858b9e wps: Replace menu items "Open With" & "Delete" with "Show in Files"
Limit these (less commonly used) "file handling" operations
to the File Browser while making it quicker to reveal the
file that is currently playing from the WPS.

"Open With" and "Delete" remain available as WPS shortcuts.

Change-Id: I91e582a45998160f07a3f7e16d475d770e8c5212
2026-04-16 21:04:27 -04:00
Christian Soffke
e85f120190 playlist_viewer: add character-based Now Playing indicator
Addresses issue where it became difficult to find the
currently playing track once you scrolled away from it,
either with icons disabled in the Playlist Viewer, or if
the theme's icon set used whitespace for the Music icon.

An example scenario would be when you're moving some track
and are trying to put it next to the currently playing one.

Change-Id: I073a7e55fb723eeff755e32a78f88dcc7db1c245
2026-04-16 21:04:06 -04:00
19 changed files with 563 additions and 22 deletions

View file

@ -829,6 +829,8 @@ long gui_wps_show(void)
if (retval == ONPLAY_MAINMENU
|| !audio_status())
return GO_TO_ROOT;
else if (retval == ONPLAY_REVEAL_FILE)
return GO_TO_FILEBROWSER;
else if (retval == ONPLAY_PLAYLIST)
return GO_TO_PLAYLIST_VIEWER;
else if (retval == ONPLAY_PLUGIN)

View file

@ -16984,3 +16984,73 @@
*: "U S B"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_SET
desc: countdown_timer plugin - header shown on the setup screen where the user enters the countdown duration
user: core
<source>
*: "SET TIMER"
</source>
<dest>
*: "SET TIMER"
</dest>
<voice>
*: "Set timer"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_RUNNING
desc: countdown_timer plugin - status label shown while the countdown is active
user: core
<source>
*: "RUNNING"
</source>
<dest>
*: "RUNNING"
</dest>
<voice>
*: "Running"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_PAUSED
desc: countdown_timer plugin - status label shown while the countdown is paused
user: core
<source>
*: "PAUSED"
</source>
<dest>
*: "PAUSED"
</dest>
<voice>
*: "Paused"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_OVERTIME
desc: countdown_timer plugin - status label shown when the countdown has passed zero and is counting up
user: core
<source>
*: "OVERTIME"
</source>
<dest>
*: "OVERTIME"
</dest>
<voice>
*: "Overtime"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_FINISHED
desc: countdown_timer plugin - status label shown at the moment the countdown expires
user: core
<source>
*: "FINISHED"
</source>
<dest>
*: "FINISHED"
</dest>
<voice>
*: "Finished"
</voice>
</phrase>

View file

@ -17057,3 +17057,115 @@
*: "U S B"
</voice>
</phrase>
<phrase>
id: LANG_FILE_NOT_FOUND
desc: When file does not exist
user: core
<source>
*: "File not found"
</source>
<dest>
*: "File not found"
</dest>
<voice>
*: "File not found"
</voice>
</phrase>
<phrase>
id: LANG_SHOW_IN_FILES
desc: Reveal item in File Browser
user: core
<source>
*: "Show in Files"
</source>
<dest>
*: "Show in Files"
</dest>
<voice>
*: "Show in Files"
</voice>
</phrase>
<phrase>
id: LANG_CHANNEL_SWAP
desc: in sound_settings
user: core
<source>
*: "Swap Left & Right"
</source>
<dest>
*: "Swap Left & Right"
</dest>
<voice>
*: "Swap Left & Right"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_SET
desc: countdown_timer plugin - header shown on the setup screen where the user enters the countdown duration
user: core
<source>
*: "SET TIMER"
</source>
<dest>
*: "SET TIMER"
</dest>
<voice>
*: "Set timer"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_RUNNING
desc: countdown_timer plugin - status label shown while the countdown is active
user: core
<source>
*: "RUNNING"
</source>
<dest>
*: "RUNNING"
</dest>
<voice>
*: "Running"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_PAUSED
desc: countdown_timer plugin - status label shown while the countdown is paused
user: core
<source>
*: "PAUSED"
</source>
<dest>
*: "PAUSED"
</dest>
<voice>
*: "Paused"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_OVERTIME
desc: countdown_timer plugin - status label shown when the countdown has passed zero and is counting up
user: core
<source>
*: "OVERTIME"
</source>
<dest>
*: "OVERTIME"
</dest>
<voice>
*: "Overtime"
</voice>
</phrase>
<phrase>
id: LANG_COUNTDOWN_TIMER_FINISHED
desc: countdown_timer plugin - status label shown at the moment the countdown expires
user: core
<source>
*: "FINISHED"
</source>
<dest>
*: "FINISHED"
</dest>
<voice>
*: "Finished"
</voice>
</phrase>

View file

@ -897,6 +897,26 @@ static bool onplay_load_plugin(void *param)
return false;
}
static int reveal(void)
{
#ifdef HAVE_TAGCACHE
if (!prepare_database_sel(NULL))
return 0;
#endif
if (!file_exists(selected_file.path))
{
splash(HZ*2, ID2P(LANG_FILE_NOT_FOUND));
return 0;
}
strmemccpy(global_status.browse_last_folder, selected_file.path,
sizeof global_status.browse_last_folder);
onplay_result = ONPLAY_REVEAL_FILE;
return 0;
}
MENUITEM_FUNCTION(reveal_item, 0, ID2P(LANG_SHOW_IN_FILES),
reveal, clipboard_callback, Icon_file_view_menu);
MENUITEM_FUNCTION(list_viewers_item, 0, ID2P(LANG_ONPLAY_OPEN_WITH),
list_viewers, clipboard_callback, Icon_NOICON);
MENUITEM_FUNCTION_W_PARAM(properties_item, 0, ID2P(LANG_PROPERTIES),
@ -1033,7 +1053,8 @@ static int clipboard_callback(int action,
if (selected_file.context == CONTEXT_ID3DB)
{
if (this_item == &track_info_item ||
this_item == &pictureflow_item)
this_item == &pictureflow_item ||
this_item == &reveal_item)
return action;
return ACTION_EXIT_MENUITEM;
}
@ -1088,7 +1109,9 @@ static int clipboard_callback(int action,
if (*tree_get_context()->dirfilter != SHOW_M3U)
return action;
}
else if (this_item == &delete_file_item)
else if (this_item == &delete_file_item ||
(this_item == &reveal_item &&
selected_file.context == CONTEXT_WPS))
return action;
#if LCD_DEPTH > 1
else if (this_item == &set_backdrop_item)
@ -1124,8 +1147,8 @@ MAKE_ONPLAYMENU( wps_onplay_menu, ID2P(LANG_ONPLAY_MENU_TITLE),
#endif
&bookmark_menu,
&plugin_item,
&browse_id3_item, &list_viewers_item,
&delete_file_item, &view_cue_item,
&browse_id3_item,
&reveal_item, &view_cue_item,
#ifdef HAVE_PITCHCONTROL
&pitch_menu,
#endif
@ -1162,6 +1185,7 @@ MAKE_ONPLAYMENU( tree_onplay_menu, ID2P(LANG_ONPLAY_MENU_TITLE),
&rename_file_item, &clipboard_cut_item, &clipboard_copy_item,
&clipboard_paste_item, &delete_file_item, &delete_dir_item,
&list_viewers_item, &create_dir_item, &properties_item, &track_info_item,
&reveal_item,
#ifdef HAVE_TAGCACHE
&pictureflow_item,
#endif

View file

@ -38,6 +38,7 @@ enum {
ONPLAY_MAINMENU = -1,
ONPLAY_OK = 0,
ONPLAY_RELOAD_DIR,
ONPLAY_REVEAL_FILE,
ONPLAY_START_PLAY,
ONPLAY_PLAYLIST,
ONPLAY_PLUGIN,

View file

@ -515,9 +515,15 @@ static void format_line(struct playlist_entry* track, char* str,
int len)
{
char *id3viewc = NULL;
char *skipped = "";
char *skipped, *prefix, *suffix;
skipped = prefix = suffix = "";
if (track->attr & PLAYLIST_ATTR_SKIPPED)
skipped = "(ERR) ";
if (track->index == viewer.current_playing_track)
{
prefix = "[";
suffix = "]";
}
if (!(track->attr & PLAYLIST_ATTR_RETRIEVE_ID3_ATTEMPTED) &&
(global_settings.playlist_viewer_track_display ==
PLAYLIST_VIEWER_ENTRY_SHOW_ID3_TITLE_AND_ALBUM ||
@ -591,9 +597,10 @@ static void format_line(struct playlist_entry* track, char* str,
format_name(name, track->name, sizeof(name));
if (global_settings.playlist_viewer_indices)
/* Display playlist index */
snprintf(str, len, "%d. %s%s", track->display_index, skipped, name);
snprintf(str, len, "%s%d. %s%s%s",
prefix, track->display_index, skipped, name, suffix);
else
snprintf(str, len, "%s%s", skipped, name);
snprintf(str, len, "%s%s%s%s", prefix, skipped, name, suffix);
}
else
{
@ -603,9 +610,10 @@ static void format_line(struct playlist_entry* track, char* str,
}
if (global_settings.playlist_viewer_indices)
/* Display playlist index */
snprintf(str, len, "%d. %s%s", track->display_index, skipped, id3viewc);
snprintf(str, len, "%s%d. %s%s%s",
prefix, track->display_index, skipped, id3viewc, suffix);
else
snprintf(str, len, "%s%s", skipped, id3viewc);
snprintf(str, len, "%s%s%s%s", prefix, skipped, id3viewc, suffix);
}
}

View file

@ -21,6 +21,7 @@ chopper,games
clix,games
clock,apps
codebuster,games
countdown_timer,apps
credits,viewers
cube,demos
cue_playlist,viewers

View file

@ -7,6 +7,7 @@ db_folder_select.c
tagcache/tagcache.c
#endif
chessclock.c
countdown_timer.c
credits.c
cube.c
cue_playlist.c

View file

@ -0,0 +1,242 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2026 Teun van Dalen
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "plugin.h"
#include "lib/pluginlib_actions.h"
#include "lib/pluginlib_exit.h"
const struct button_mapping *plugin_contexts[] = { pla_main_ctx };
#define MAX_MINUTES 99
#define MAX_SECONDS 59
typedef enum {
STATE_SETUP,
STATE_RUNNING,
STATE_PAUSED,
STATE_FINISHED,
} timer_state_t;
typedef struct {
int set_minutes;
int set_seconds;
int current_field;
long remaining_ticks;
long last_tick;
timer_state_t state;
} timer_ctx_t;
static inline int get_button(void)
{
return pluginlib_getaction(HZ / 10, plugin_contexts,
ARRAYLEN(plugin_contexts));
}
static void draw(const timer_ctx_t *ctx)
{
char time_str[12];
const char *status_str;
int disp_min, disp_sec;
int w, h, x, y;
rb->lcd_clear_display();
if (ctx->state == STATE_SETUP) {
disp_min = ctx->set_minutes;
disp_sec = ctx->set_seconds;
if (ctx->current_field == 0)
rb->snprintf(time_str, sizeof(time_str), "[%02d]:%02d",
disp_min, disp_sec);
else
rb->snprintf(time_str, sizeof(time_str), "%02d:[%02d]",
disp_min, disp_sec);
status_str = rb->str(LANG_COUNTDOWN_TIMER_SET);
} else if (ctx->state == STATE_FINISHED) {
rb->snprintf(time_str, sizeof(time_str), "00:00");
status_str = rb->str(LANG_COUNTDOWN_TIMER_FINISHED);
} else {
if (ctx->remaining_ticks >= 0) {
int remaining_secs = (ctx->remaining_ticks + HZ - 1) / HZ;
disp_min = remaining_secs / 60;
disp_sec = remaining_secs % 60;
rb->snprintf(time_str, sizeof(time_str), "%02d:%02d",
disp_min, disp_sec);
} else {
int over_secs = ((-ctx->remaining_ticks) + HZ - 1) / HZ;
disp_min = over_secs / 60;
disp_sec = over_secs % 60;
rb->snprintf(time_str, sizeof(time_str), "-%02d:%02d",
disp_min, disp_sec);
}
if (ctx->state == STATE_RUNNING && ctx->remaining_ticks < 0)
status_str = rb->str(LANG_COUNTDOWN_TIMER_OVERTIME);
else if (ctx->state == STATE_RUNNING)
status_str = rb->str(LANG_COUNTDOWN_TIMER_RUNNING);
else
status_str = rb->str(LANG_COUNTDOWN_TIMER_PAUSED);
}
/* Draw status string above center */
rb->lcd_getstringsize(status_str, &w, &h);
x = (LCD_WIDTH - w) / 2;
y = (LCD_HEIGHT / 2) - h - 2;
rb->lcd_putsxy(x, y, status_str);
/* Draw time string at center */
rb->lcd_getstringsize(time_str, &w, &h);
x = (LCD_WIDTH - w) / 2;
y = LCD_HEIGHT / 2;
rb->lcd_putsxy(x, y, time_str);
rb->lcd_update();
}
enum plugin_status plugin_start(const void *parameter)
{
timer_ctx_t ctx = {
.set_minutes = 10,
.set_seconds = 0,
.current_field = 0,
.remaining_ticks = 60 * HZ,
.last_tick = 0,
.state = STATE_SETUP,
};
bool overtime_alerted = false;
int button;
(void)parameter;
while (true) {
if (ctx.state == STATE_RUNNING) {
long now = *rb->current_tick;
ctx.remaining_ticks -= now - ctx.last_tick;
ctx.last_tick = now;
if (ctx.remaining_ticks < 0 && !overtime_alerted) {
overtime_alerted = true;
ctx.state = STATE_FINISHED;
draw(&ctx);
rb->audio_pause();
#ifdef HAVE_BACKLIGHT
rb->backlight_on();
#endif
rb->beep_play(1000, 200, 1000);
rb->sleep(HZ / 4);
rb->beep_play(1000, 200, 1000);
rb->sleep(HZ / 4);
rb->beep_play(1000, 400, 1000);
rb->sleep(HZ + HZ / 2);
rb->audio_resume();
ctx.state = STATE_RUNNING;
/* Reset last_tick so the beep/sleep time is not counted
* against remaining_ticks on the next iteration. */
ctx.last_tick = *rb->current_tick;
}
}
draw(&ctx);
button = get_button();
switch (ctx.state) {
case STATE_SETUP: {
bool time_changed = false;
switch (button) {
#ifdef HAVE_SCROLLWHEEL
case PLA_SCROLL_FWD:
case PLA_SCROLL_FWD_REPEAT:
#endif
case PLA_UP:
case PLA_UP_REPEAT:
if (ctx.current_field == 0)
ctx.set_minutes = (ctx.set_minutes + 1) % (MAX_MINUTES + 1);
else
ctx.set_seconds = (ctx.set_seconds + 1) % (MAX_SECONDS + 1);
time_changed = true;
break;
#ifdef HAVE_SCROLLWHEEL
case PLA_SCROLL_BACK:
case PLA_SCROLL_BACK_REPEAT:
#endif
case PLA_DOWN:
case PLA_DOWN_REPEAT:
if (ctx.current_field == 0)
ctx.set_minutes = (ctx.set_minutes + MAX_MINUTES) % (MAX_MINUTES + 1);
else
ctx.set_seconds = (ctx.set_seconds + MAX_SECONDS) % (MAX_SECONDS + 1);
time_changed = true;
break;
case PLA_LEFT:
case PLA_LEFT_REPEAT:
case PLA_RIGHT:
case PLA_RIGHT_REPEAT:
ctx.current_field = (ctx.current_field + 1) % 2;
break;
case PLA_SELECT:
if (ctx.set_minutes > 0 || ctx.set_seconds > 0) {
ctx.remaining_ticks = (ctx.set_minutes * 60 + ctx.set_seconds) * HZ;
ctx.last_tick = *rb->current_tick;
ctx.state = STATE_RUNNING;
}
break;
default:
exit_on_usb(button);
break;
}
if (time_changed)
ctx.remaining_ticks = (ctx.set_minutes * 60 + ctx.set_seconds) * HZ;
break;
}
case STATE_RUNNING:
switch (button) {
case PLA_SELECT:
ctx.state = STATE_PAUSED;
break;
default:
exit_on_usb(button);
break;
}
break;
case STATE_FINISHED:
exit_on_usb(button);
break;
case STATE_PAUSED:
switch (button) {
case PLA_SELECT:
ctx.last_tick = *rb->current_tick;
ctx.state = STATE_RUNNING;
break;
case PLA_UP:
case PLA_UP_REPEAT:
case PLA_CANCEL:
case PLA_EXIT:
return PLUGIN_OK;
default:
exit_on_usb(button);
break;
}
break;
}
}
}

View file

@ -1060,11 +1060,12 @@ const struct settings_list settings[] = {
/* 3-d enhancement effect */
CHOICE_SETTING(0, channel_config, LANG_CHANNEL_CONFIGURATION,
0,"channels",
"stereo,mono,custom,mono left,mono right,karaoke",
sound_set_channels, 6,
"stereo,mono,custom,mono left,mono right,karaoke,swap",
sound_set_channels, 7,
ID2P(LANG_CHANNEL_STEREO), ID2P(LANG_CHANNEL_MONO),
ID2P(LANG_CHANNEL_CUSTOM), ID2P(LANG_CHANNEL_LEFT),
ID2P(LANG_CHANNEL_RIGHT), ID2P(LANG_CHANNEL_KARAOKE)),
ID2P(LANG_CHANNEL_RIGHT), ID2P(LANG_CHANNEL_KARAOKE),
ID2P(LANG_CHANNEL_SWAP)),
SOUND_SETTING(0, stereo_width, LANG_STEREO_WIDTH,
"stereo_width", SOUND_STEREO_WIDTH),
#ifdef AUDIOHW_HAVE_DEPTH_3D

View file

@ -975,6 +975,10 @@ static int dirbrowse(void)
return exit_to_new_screen(GO_TO_ROOT);
break;
case ONPLAY_REVEAL_FILE:
return exit_to_new_screen(GO_TO_FILEBROWSER);
break;
case ONPLAY_OK:
restore = do_restore_display;
break;

View file

@ -760,6 +760,7 @@ Yegor Chernyshov
Javier Gutiérrez Gertrúdix
Sergey Puskov
Eivind Ødegård
Teun van Dalen
The libmad team
The wavpack team

View file

@ -674,6 +674,7 @@ enum AUDIOHW_CHANNEL_CONFIG
SOUND_CHAN_MONO_LEFT,
SOUND_CHAN_MONO_RIGHT,
SOUND_CHAN_KARAOKE,
SOUND_CHAN_SWAP,
SOUND_CHAN_NUM_MODES,
};

View file

@ -43,6 +43,8 @@ void channel_mode_proc_custom(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p);
void channel_mode_proc_karaoke(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p);
void channel_mode_proc_swap(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p);
static struct channel_mode_data
{
@ -131,6 +133,25 @@ void channel_mode_proc_karaoke(struct dsp_proc_entry *this,
}
#endif /* CPU */
void channel_mode_proc_swap(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p)
{
struct dsp_buffer *buf = *buf_p;
int32_t *sl = buf->p32[0];
int32_t *sr = buf->p32[1];
int count = buf->remcount;
do
{
int32_t l = *sl;
*sl++ = *sr;
*sr++ = l;
}
while (--count > 0);
(void)this;
}
void channel_mode_proc_mono_left(struct dsp_proc_entry *this,
struct dsp_buffer **buf_p)
{
@ -194,6 +215,7 @@ static void update_process_fn(struct dsp_proc_entry *this)
[SOUND_CHAN_MONO_LEFT] = channel_mode_proc_mono_left,
[SOUND_CHAN_MONO_RIGHT] = channel_mode_proc_mono_right,
[SOUND_CHAN_KARAOKE] = channel_mode_proc_karaoke,
[SOUND_CHAN_SWAP] = channel_mode_proc_swap,
};
this->process = fns[((struct channel_mode_data *)this->data)->mode];

View file

@ -178,6 +178,9 @@ change to customise your listening experience.
to make the singer sound centrally placed, this often (but not
always) has the effect of removing the voice track from a song. This
setting also very often has other undesirable effects on the sound.
\item[Swap Left \& Right.]
Plays the left channel in the right stereo channel, and the right
channel in the left stereo channel.
\end{description}
\section{Stereo Width}

View file

@ -0,0 +1,51 @@
% $Id$ %
\subsection{Countdown_timer}
A countdown timer. Set the desired duration, start the countdown, and the
\dap{} will alert you with a beep sequence when time is up. The timer
defaults to 10 minutes on launch.
\subsubsection{Setting the timer}
When the plugin starts, the display shows the time to count down from with
the active field highlighted in brackets (e.g.\ \texttt{[10]:00}).
\begin{btnmap}
\opt{scrollwheel}{\PluginScrollFwd{} / \PluginScrollBack{} or }
\PluginUp{} / \PluginDown
\opt{HAVEREMOTEKEYMAP}{& \PluginRCUp{} / \PluginRCDown}
& Increase / decrease the selected field \\
\PluginLeft{} / \PluginRight
\opt{HAVEREMOTEKEYMAP}{& \PluginRCLeft{} / \PluginRCRight}
& Switch between the minutes and seconds fields \\
\PluginSelect
\opt{HAVEREMOTEKEYMAP}{& \PluginRCSelect}
& Start the countdown \\
\end{btnmap}
Minutes can be set from 0 to 99 and seconds from 0 to 59. The timer cannot
be started if both fields are zero. Values wrap around when incremented or
decremented past their limits.
\subsubsection{Running the timer}
Once started, the remaining time counts down in \texttt{MM:SS} format.
\begin{btnmap}
\PluginSelect
\opt{HAVEREMOTEKEYMAP}{& \PluginRCSelect}
& Pause / resume the timer \\
\nopt{IPOD_4G_PAD,IPOD_3G_PAD}{\PluginCancel}
\opt{IPOD_4G_PAD,IPOD_3G_PAD}{\ButtonMenu}
\opt{HAVEREMOTEKEYMAP}{& \PluginRCCancel}
& Quit (only available while paused) \\
\end{btnmap}
When the countdown reaches zero the \dap{} pauses playback, turns on the
backlight, and plays three beeps. After the alert the timer continues
running into overtime, displaying the elapsed overtime as
\texttt{-MM:SS}. Playback resumes automatically after the beep sequence.
The timer can be quit by pausing it and pressing the quit button.

View file

@ -297,3 +297,5 @@ option from the \setting{Context Menu} (see \reference{ref:Contextmenu}).}
\input{plugins/stopwatch.tex}
\input{plugins/text_editor.tex}
\input{plugins/countdown_timer.tex}

View file

@ -143,7 +143,9 @@ each option pertains both to files and directories):
Deletes the currently selected file. This option applies only to files, and
not to directories. Rockbox will ask for confirmation before deleting a file.
Press \ActionYesNoAccept{}
to confirm deletion or any other key to cancel.
to confirm deletion or any other key to cancel. When deleting a currently
playing file, the part of the file that has already been buffered (i.e. read
into the \daps\ memory) will continue playing. This may even be the whole track.
\item [Delete Directory.]
Deletes the currently selected directory and all of the files and subdirectories
it may contain. Deleted directories cannot be recovered. Use this feature with

View file

@ -254,15 +254,8 @@ from the WPS, %
\setting{Context Menu}. Then select \setting{Show Track Info}. The same steps
work in the Playlist Viewer as well.}
\subsubsection{Open With...}
This \setting{Open With} function is the same as the \setting{Open With}
function in the file browser's \setting{Context Menu}.
\subsubsection{Delete}
Delete the currently playing file. The file will be deleted but the playback
of the file will not stop immediately. Instead, the part of the file that
has already been buffered (i.e. read into the \daps\ memory) will be played.
This may even be the whole track.
\subsubsection{Show in Files}
Reveal the currently playing track in the \setting{File Browser}.
\opt{pitchscreen}{
\subsubsection{\label{sec:pitchscreen}Pitch}