summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
Diffstat (limited to 'apps')
-rw-r--r--apps/action.c120
-rw-r--r--apps/action.h6
-rw-r--r--apps/bookmark.c1186
-rw-r--r--apps/bookmark.h8
-rw-r--r--apps/buffering.c1
-rw-r--r--apps/core_keymap.c134
-rw-r--r--apps/core_keymap.h7
-rw-r--r--apps/cuesheet.c34
-rw-r--r--apps/debug_menu.c97
-rw-r--r--apps/filetree.c101
-rw-r--r--apps/filetree.h3
-rw-r--r--apps/filetypes.c269
-rw-r--r--apps/filetypes.h4
-rw-r--r--apps/gui/bitmap/list-skinned.c3
-rw-r--r--apps/gui/bitmap/list.c13
-rw-r--r--apps/gui/color_picker.c26
-rw-r--r--apps/gui/folder_select.c8
-rw-r--r--apps/gui/icon.c16
-rw-r--r--apps/gui/line.c3
-rw-r--r--apps/gui/list.c171
-rw-r--r--apps/gui/list.h47
-rw-r--r--apps/gui/option_select.c17
-rw-r--r--apps/gui/quickscreen.c25
-rw-r--r--apps/gui/quickscreen.h7
-rw-r--r--apps/gui/skin_engine/skin_backdrops.c19
-rw-r--r--apps/gui/skin_engine/skin_display.c233
-rw-r--r--apps/gui/skin_engine/skin_display.h9
-rw-r--r--apps/gui/skin_engine/skin_engine.c61
-rw-r--r--apps/gui/skin_engine/skin_engine.h24
-rw-r--r--apps/gui/skin_engine/skin_parser.c73
-rw-r--r--apps/gui/skin_engine/skin_render.c44
-rw-r--r--apps/gui/skin_engine/skin_tokens.c51
-rw-r--r--apps/gui/skin_engine/skin_touchsupport.c11
-rw-r--r--apps/gui/skin_engine/wps_internals.h51
-rw-r--r--apps/gui/splash.c176
-rw-r--r--apps/gui/splash.h11
-rw-r--r--apps/gui/statusbar-skinned.c18
-rw-r--r--apps/gui/statusbar-skinned.h5
-rw-r--r--apps/gui/viewport.c40
-rw-r--r--apps/gui/viewport.h1
-rw-r--r--apps/gui/wps.c178
-rw-r--r--apps/gui/wps.h37
-rw-r--r--apps/gui/yesno.c5
-rw-r--r--apps/hosted/android/keyboard.c2
-rw-r--r--apps/hosted/android/notification.c1
-rw-r--r--apps/iap/iap-core.c2
-rw-r--r--apps/iap/iap-lingo4.c21
-rw-r--r--apps/keymaps/keymap-agptekrocker.c19
-rw-r--r--apps/keymaps/keymap-clip.c2
-rw-r--r--apps/keymaps/keymap-erosq.c1
-rw-r--r--apps/keymaps/keymap-fiiom3k.c3
-rw-r--r--apps/keymaps/keymap-fuzeplus.c3
-rw-r--r--apps/keymaps/keymap-ipod.c2
-rw-r--r--apps/keymaps/keymap-nwz.c3
-rw-r--r--apps/keymaps/keymap-nwza860.c3
-rw-r--r--apps/keymaps/keymap-zen.c3
-rw-r--r--apps/lang/english-us.lang54
-rw-r--r--apps/lang/english.lang22
-rw-r--r--apps/lang/italiano.lang196
-rw-r--r--apps/lang/nederlands.lang368
-rw-r--r--apps/lang/polski.lang20
-rw-r--r--apps/lang/srpski.lang256
-rw-r--r--apps/logfdisp.c15
-rw-r--r--apps/main.c12
-rw-r--r--apps/menu.c32
-rw-r--r--apps/menus/display_menu.c18
-rw-r--r--apps/menus/eq_menu.c40
-rw-r--r--apps/menus/main_menu.c6
-rw-r--r--apps/menus/playback_menu.c2
-rw-r--r--apps/menus/playlist_menu.c6
-rw-r--r--apps/menus/sound_menu.c18
-rw-r--r--apps/misc.c100
-rw-r--r--apps/misc.h6
-rw-r--r--apps/onplay.c91
-rw-r--r--apps/onplay.h3
-rw-r--r--apps/open_plugin.c36
-rw-r--r--apps/open_plugin.h5
-rw-r--r--apps/playback.c38
-rw-r--r--apps/playlist.c3813
-rw-r--r--apps/playlist.h35
-rw-r--r--apps/playlist_catalog.c241
-rw-r--r--apps/playlist_catalog.h4
-rw-r--r--apps/playlist_viewer.c125
-rw-r--r--apps/playlist_viewer.h3
-rw-r--r--apps/plugin.c39
-rw-r--r--apps/plugin.h44
-rw-r--r--apps/plugins/2048.c5
-rw-r--r--apps/plugins/SOURCES3
-rw-r--r--apps/plugins/SUBDIRS3
-rw-r--r--apps/plugins/bitmaps/native/SOURCES3
-rw-r--r--apps/plugins/brickmania.c23
-rw-r--r--apps/plugins/calendar.c2
-rw-r--r--apps/plugins/chessbox/chessbox_pgn.c4
-rw-r--r--apps/plugins/chopper.c8
-rw-r--r--apps/plugins/credits.c8
-rw-r--r--apps/plugins/demystify.c12
-rw-r--r--apps/plugins/doom/i_video.c3
-rw-r--r--apps/plugins/doom/rockdoom.c8
-rw-r--r--apps/plugins/fft/fft.c6
-rw-r--r--apps/plugins/fire.c6
-rw-r--r--apps/plugins/fireworks.c8
-rw-r--r--apps/plugins/imageviewer/imageviewer.c4
-rw-r--r--apps/plugins/imageviewer/jpeg/yuv2rgb.c2
-rw-r--r--apps/plugins/imageviewer/ppm/ppm_decoder.c2
-rw-r--r--apps/plugins/invadrox.c2
-rw-r--r--apps/plugins/keybox.c3
-rw-r--r--apps/plugins/keyremap.c5
-rw-r--r--apps/plugins/lamp.c15
-rw-r--r--apps/plugins/lib/bmp_smooth_scale.c2
-rw-r--r--apps/plugins/lib/helper.c48
-rw-r--r--apps/plugins/lib/helper.h20
-rw-r--r--apps/plugins/lib/osd.c8
-rw-r--r--apps/plugins/lib/pluginlib_bmp.c2
-rw-r--r--apps/plugins/lib/printcell_helper.c2
-rw-r--r--apps/plugins/lib/xlcd_scroll.c4
-rw-r--r--apps/plugins/lrcplayer.c4
-rw-r--r--apps/plugins/lua/rocklib_events.c4
-rw-r--r--apps/plugins/lua/rocklib_img.c2
-rw-r--r--apps/plugins/main_menu_config.c2
-rw-r--r--apps/plugins/maze.c5
-rw-r--r--apps/plugins/mazezam.c5
-rw-r--r--apps/plugins/mpegplayer/mpegplayer.c5
-rw-r--r--apps/plugins/open_plugins.c36
-rw-r--r--apps/plugins/oscilloscope.c6
-rwxr-xr-xapps/plugins/pacbox/pacbox.c4
-rw-r--r--apps/plugins/pictureflow/pictureflow.c94
-rw-r--r--apps/plugins/plasma.c8
-rw-r--r--apps/plugins/pong.c7
-rw-r--r--apps/plugins/properties.c221
-rw-r--r--apps/plugins/puzzles/rockbox.c15
-rw-r--r--apps/plugins/random_folder_advance_config.c3
-rw-r--r--apps/plugins/rb_info.c5
-rw-r--r--apps/plugins/resistor.c18
-rw-r--r--apps/plugins/rockblox.c11
-rw-r--r--apps/plugins/rockboy/menu.c6
-rw-r--r--apps/plugins/rockboy/rockboy.c5
-rw-r--r--apps/plugins/rocklife.c6
-rw-r--r--apps/plugins/sdl/main.c7
-rw-r--r--apps/plugins/shopper.c3
-rw-r--r--apps/plugins/shortcuts/shortcuts_view.c8
-rw-r--r--apps/plugins/spacerocks.c11
-rw-r--r--apps/plugins/speedread.c7
-rw-r--r--apps/plugins/starfield.c8
-rw-r--r--apps/plugins/superdom.c1
-rw-r--r--apps/plugins/test_disk.c6
-rw-r--r--apps/plugins/test_fps.c8
-rw-r--r--apps/plugins/test_gfx.c8
-rw-r--r--apps/plugins/test_grey.c8
-rw-r--r--apps/plugins/test_viewports.c4
-rw-r--r--apps/plugins/text_editor.c3
-rw-r--r--apps/plugins/wormlet.c7
-rw-r--r--apps/plugins/xobox.c7
-rw-r--r--apps/plugins/xworld/sys.c18
-rw-r--r--apps/radio/presets.c7
-rw-r--r--apps/radio/radio.c5
-rw-r--r--apps/radio/radio_skin.c16
-rw-r--r--apps/radio/radioart.c2
-rw-r--r--apps/rbcodec_helpers.c5
-rw-r--r--apps/recorder/albumart.c80
-rw-r--r--apps/recorder/albumart.h6
-rw-r--r--apps/recorder/keyboard.c4
-rw-r--r--apps/recorder/pcm_record.c7
-rw-r--r--apps/recorder/recording.c2
-rw-r--r--apps/recorder/resize.c2
-rw-r--r--apps/root_menu.c31
-rw-r--r--apps/screen_access.c2
-rw-r--r--apps/screen_access.h1
-rw-r--r--apps/screens.c26
-rw-r--r--apps/settings.c223
-rw-r--r--apps/settings.h7
-rw-r--r--apps/settings_list.c44
-rw-r--r--apps/settings_list.h10
-rw-r--r--apps/shortcuts.c18
-rw-r--r--apps/status.h2
-rw-r--r--apps/tagcache.c221
-rw-r--r--apps/tagcache.h63
-rw-r--r--apps/tagtree.c284
-rw-r--r--apps/tagtree.h4
-rw-r--r--apps/talk.c316
-rw-r--r--apps/talk.h7
-rw-r--r--apps/tree.c122
-rw-r--r--apps/tree.h19
182 files changed, 6120 insertions, 5289 deletions
diff --git a/apps/action.c b/apps/action.c
index 9ef10936f2..8c03ca4d65 100644
--- a/apps/action.c
+++ b/apps/action.c
@@ -34,6 +34,7 @@
#include "button.h"
#include "action.h"
#include "kernel.h"
+#include "core_alloc.h"
#include "splash.h"
#include "settings.h"
@@ -70,7 +71,7 @@ static action_last_t action_last =
.wait_for_release = false,
#ifndef DISABLE_ACTION_REMAP
- .core_keymap = NULL,
+ .key_remap = 0,
#endif
#ifdef HAVE_TOUCHSCREEN
@@ -601,9 +602,8 @@ static inline void action_code_lookup(action_last_t *last, action_cur_t *cur)
#endif
#ifndef DISABLE_ACTION_REMAP
- bool check_remap = (last->core_keymap != NULL);
/* attempt to look up the button in user supplied remap */
- if(check_remap && (context & CONTEXT_PLUGIN) == 0)
+ if(last->key_remap && (context & CONTEXT_PLUGIN) == 0)
{
#if 0 /*Disable the REMOTE context for remap for now (BUTTON_REMOTE != 0)*/
if ((cur->button & BUTTON_REMOTE) != 0)
@@ -611,7 +611,7 @@ static inline void action_code_lookup(action_last_t *last, action_cur_t *cur)
context |= CONTEXT_REMOTE;
}
#endif
- cur->items = last->core_keymap;
+ cur->items = core_get_data(last->key_remap);
i = 0;
action = ACTION_UNKNOWN;
/* check the lut at the beginning for the desired context */
@@ -1193,66 +1193,88 @@ int get_action(int context, int timeout)
int action_set_keymap(struct button_mapping* core_keymap, int count)
{
+#ifdef DISABLE_ACTION_REMAP
+ (void)core_keymap;
+ (void)count;
+ return -1;
+#else
+ if (count <= 0 || core_keymap == NULL)
+ return action_set_keymap_handle(0, 0);
+
+ size_t keyremap_buf_size = count * sizeof(struct button_mapping);
+ int handle = core_alloc("keyremap", keyremap_buf_size);
+ if (handle < 0)
+ return -6;
+ memcpy(core_get_data(handle), core_keymap, keyremap_buf_size);
+ return action_set_keymap_handle(handle, count);
+#endif
+}
+
+int action_set_keymap_handle(int handle, int count)
+{
#ifdef DISABLE_ACTION_REMAP
- count = -1;
+ (void)core_keymap;
+ (void)count;
+ return -1;
#else
- if (count > 0 && core_keymap != NULL) /* saf-tey checks :) */
+ /* free an existing remap */
+ action_last.key_remap = core_free(action_last.key_remap);
+
+ /* if clearing the remap, we're done */
+ if (count <= 0 || handle <= 0)
+ return 0;
+
+ /* validate the keymap */
+ struct button_mapping* core_keymap = core_get_data(handle);
+ struct button_mapping* entry = &core_keymap[count - 1];
+ if (entry->action_code != (int) CONTEXT_STOPSEARCHING ||
+ entry->button_code != BUTTON_NONE) /* check for sentinel at end*/
{
- int i = 0;
- struct button_mapping* entry = &core_keymap[count - 1];
- if (entry->action_code != (int) CONTEXT_STOPSEARCHING ||
- entry->button_code != BUTTON_NONE) /* check for sentinel at end*/
- {
- count = -1;
- }
+ /* missing sentinel entry */
+ return -1;
+ }
- while (count > 0 && /* check the lut at the beginning for invalid offsets */
- (entry = &core_keymap[i])->action_code != (int) CONTEXT_STOPSEARCHING)
- {
-
- if ((entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
- {
- int firstbtn = entry->button_code;
- int endpos = firstbtn + entry->pre_button_code;
- if (firstbtn > count || firstbtn < i || endpos > count)
- {
- /* offset out of bounds */
- count = -2;
- break;
- }
+ /* check the lut at the beginning for invalid offsets */
+ for (int i = 0; i < count; ++i)
+ {
+ entry = &core_keymap[i];
+ if (entry->action_code == (int)CONTEXT_STOPSEARCHING)
+ break;
- if (core_keymap[endpos].button_code != BUTTON_NONE)
- {
- /* stop sentinel is not at end of action lut*/
- count = -3;
- }
- }
- else /* something other than a context remap in the lut */
+ if ((entry->action_code & CONTEXT_REMAPPED) == CONTEXT_REMAPPED)
+ {
+ int firstbtn = entry->button_code;
+ int endpos = firstbtn + entry->pre_button_code;
+ if (firstbtn > count || firstbtn < i || endpos > count)
{
- count = -4;
- break;
+ /* offset out of bounds */
+ return -2;
}
- i++;
-
- if (i >= count) /* no sentinel in the lut */
+ if (core_keymap[endpos].button_code != BUTTON_NONE)
{
- count = -5;
- break;
+ /* stop sentinel is not at end of action lut */
+ return -3;
}
}
+ else
+ {
+ /* something other than a context remap in the lut */
+ return -4;
+ }
- if (count <= 0)
- core_keymap = NULL;
- }
- else
-#endif
- {
- core_keymap = NULL;
+ if (i+1 >= count)
+ {
+ /* no sentinel in the lut */
+ return -5;
+ }
}
- action_last.core_keymap = core_keymap;
+
+ /* success */
+ action_last.key_remap = handle;
return count;
+#endif
}
int get_custom_action(int context,int timeout,
diff --git a/apps/action.h b/apps/action.h
index 35f08a3dbd..444437edf8 100644
--- a/apps/action.h
+++ b/apps/action.h
@@ -419,7 +419,7 @@ typedef struct
bool wait_for_release;
#ifndef DISABLE_ACTION_REMAP
- struct button_mapping* core_keymap;
+ int key_remap;
#endif
#ifdef HAVE_TOUCHSCREEN
@@ -449,7 +449,9 @@ bool action_userabort(int timeout);
const struct button_mapping* get_context_mapping(int context);
/* load a key map to allow buttons for actions to be remapped see: core_keymap */
-int action_set_keymap(struct button_mapping* core_button_map, int count);
+int action_set_keymap(struct button_mapping* core_keymap, int count);
+/* load keymap in a handle: takes ownership of the handle on success */
+int action_set_keymap_handle(int handle, int count);
/* returns the status code variable from action.c for the button just pressed
If button != NULL it will be set to the actual button code */
diff --git a/apps/bookmark.c b/apps/bookmark.c
index 70dbd8075d..a3671a076f 100644
--- a/apps/bookmark.c
+++ b/apps/bookmark.c
@@ -43,6 +43,9 @@
#include "file.h"
#include "pathfuncs.h"
+/*#define LOGF_ENABLE*/
+#include "logf.h"
+
#define MAX_BOOKMARKS 10
#define MAX_BOOKMARK_SIZE 350
#define RECENT_BOOKMARK_FILE ROCKBOX_DIR "/most-recent.bmark"
@@ -66,241 +69,220 @@ struct bookmark_list
#define BM_SPEED 0x02
/* bookmark values */
-static struct {
+struct resume_info{
+ const struct mp3entry *id3;
int resume_index;
unsigned long resume_offset;
int resume_seed;
- long resume_time;
+ long resume_elapsed;
int repeat_mode;
bool shuffle;
/* optional values */
int pitch;
int speed;
-} bm;
-
-static bool add_bookmark(const char* bookmark_file_name, const char* bookmark,
- bool most_recent);
-static char* create_bookmark(void);
-static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id);
-static void say_bookmark(const char* bookmark,
- int bookmark_id, bool show_playlist_name);
-static bool play_bookmark(const char* bookmark);
-static bool generate_bookmark_file_name(const char *in);
-static bool parse_bookmark(const char *bookmark, const bool get_filenames, const bool strip_dir);
-static int buffer_bookmarks(struct bookmark_list* bookmarks, int first_line);
-static const char* get_bookmark_info(int list_index,
- void* data,
- char *buffer,
- size_t buffer_len);
-static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume, char** selected_bookmark);
-static bool write_bookmark(bool create_bookmark_file, const char *bookmark);
-static int get_bookmark_count(const char* bookmark_file_name);
+};
-#define TEMP_BUF_SIZE (MAX_PATH + 1)
+/* Temp buffer used for reading, create_bookmark and filename creation */
+#define TEMP_BUF_SIZE (MAX(MAX_BOOKMARK_SIZE, MAX_PATH + 1))
static char global_temp_buffer[TEMP_BUF_SIZE];
-/* File name created by generate_bookmark_file_name */
-static char global_bookmark_file_name[MAX_PATH];
-static char global_read_buffer[MAX_BOOKMARK_SIZE];
-/* Bookmark created by create_bookmark*/
-static char global_bookmark[MAX_BOOKMARK_SIZE];
-/* Filename from parsed bookmark (can be made local where needed) */
-static char global_filename[MAX_PATH];
-/* ----------------------------------------------------------------------- */
-/* This is an interface function from the context menu. */
-/* Returns true on successful bookmark creation. */
-/* ----------------------------------------------------------------------- */
-bool bookmark_create_menu(void)
+static const char* skip_tokens(const char* s, int ntokens)
{
- return write_bookmark(true, create_bookmark());
-}
-
-/* ----------------------------------------------------------------------- */
-/* This function acts as the load interface from the context menu. */
-/* This function determines the bookmark file name and then loads that file*/
-/* for the user. The user can then select or delete previous bookmarks. */
-/* This function returns BOOKMARK_SUCCESS on the selection of a track to */
-/* resume, BOOKMARK_FAIL if the menu is exited without a selection and */
-/* BOOKMARK_USB_CONNECTED if the menu is forced to exit due to a USB */
-/* connection. */
-/* ----------------------------------------------------------------------- */
-int bookmark_load_menu(void)
-{
- char* bookmark;
- int ret = BOOKMARK_FAIL;
-
- push_current_activity(ACTIVITY_BOOKMARKSLIST);
-
- char* name = playlist_get_name(NULL, global_temp_buffer,
- sizeof(global_temp_buffer));
- if (generate_bookmark_file_name(name))
+ for (int i = 0; i < ntokens; i++)
{
- ret = select_bookmark(global_bookmark_file_name, false, &bookmark);
- if (bookmark != NULL)
+ while (*s && *s != ';')
{
- ret = play_bookmark(bookmark) ? BOOKMARK_SUCCESS : BOOKMARK_FAIL;
+ s++;
+ }
+
+ if (*s)
+ {
+ s++;
}
}
+ return s;
+}
- pop_current_activity();
+static int int_token(const char **s)
+{
+ int ret = atoi(*s);
+ *s = skip_tokens(*s, 1);
return ret;
}
-/* ----------------------------------------------------------------------- */
-/* Gives the user a list of the Most Recent Bookmarks. This is an */
-/* interface function */
-/* Returns true on the successful selection of a recent bookmark. */
-/* ----------------------------------------------------------------------- */
-bool bookmark_mrb_load()
+static long long_token(const char **s)
{
- char* bookmark;
- bool ret = false;
-
- push_current_activity(ACTIVITY_BOOKMARKSLIST);
- select_bookmark(RECENT_BOOKMARK_FILE, false, &bookmark);
- if (bookmark != NULL)
- {
- ret = play_bookmark(bookmark);
- }
+ /* Should be atol, but we don't have it. */
+ return int_token(s);
+}
- pop_current_activity();
- return ret;
+/*-------------------------------------------------------------------------*/
+/* Get the name of the playlist and the name of the track from a bookmark. */
+/* Returns true iff both were extracted. */
+/*-------------------------------------------------------------------------*/
+static bool bookmark_get_playlist_and_track(const char *bookmark,
+ const char **pl_start,
+ const char **pl_end,
+ const char **track)
+{
+ logf("%s", __func__);
+ *pl_start = strchr(bookmark,'/');
+ if (!(*pl_start))
+ return false;
+ *pl_end = skip_tokens(*pl_start, 1) - 1;
+ *track = *pl_end + 1;
+ return true;
}
/* ----------------------------------------------------------------------- */
-/* This function handles an autobookmark creation. This is an interface */
-/* function. */
-/* Returns true on successful bookmark creation. */
+/* This function takes a bookmark and parses it. This function also */
+/* validates the bookmark. Valid filenamebuf indicates whether */
+/* the filename tokens are to be extracted. */
+/* Returns true on successful bookmark parse. */
/* ----------------------------------------------------------------------- */
-bool bookmark_autobookmark(bool prompt_ok)
+static bool parse_bookmark(char *filenamebuf,
+ size_t filenamebufsz,
+ const char *bookmark,
+ struct resume_info *resume_info,
+ const bool strip_dir)
{
- char* bookmark;
- bool update;
-
- if (!bookmark_is_bookmarkable_state())
- return false;
+ const char* s = bookmark;
+ const char* end;
- audio_pause(); /* first pause playback */
- update = (global_settings.autoupdatebookmark && bookmark_exists());
- bookmark = create_bookmark();
+#define GET_INT_TOKEN(var) var = int_token(&s)
+#define GET_LONG_TOKEN(var) var = long_token(&s)
+#define GET_BOOL_TOKEN(var) var = (int_token(&s) != 0)
- if (update)
- return write_bookmark(true, bookmark);
+ /* if new format bookmark, extract the optional content flags,
+ otherwise treat as an original format bookmark */
+ int opt_flags = 0;
+ int opt_pitch = 0;
+ int opt_speed = 0;
+ int old_format = ((strchr(s, '>') == s) ? 0 : 1);
+ if (old_format == 0) /* this is a new format bookmark */
+ {
+ s++;
+ GET_INT_TOKEN(opt_flags);
+ opt_pitch = (opt_flags & BM_PITCH) ? 1:0;
+ opt_speed = (opt_flags & BM_SPEED) ? 1:0;
+ }
- switch (global_settings.autocreatebookmark)
+ /* extract all original bookmark tokens */
+ if (resume_info)
{
- case BOOKMARK_YES:
- return write_bookmark(true, bookmark);
+ GET_INT_TOKEN(resume_info->resume_index);
+ GET_LONG_TOKEN(resume_info->resume_offset);
+ GET_INT_TOKEN(resume_info->resume_seed);
- case BOOKMARK_NO:
- return false;
+ s = skip_tokens(s, old_format); /* skip deprecated token */
- case BOOKMARK_RECENT_ONLY_YES:
- return write_bookmark(false, bookmark);
- }
- const char *lines[]={ID2P(LANG_AUTO_BOOKMARK_QUERY)};
- const struct text_message message={lines, 1};
+ GET_LONG_TOKEN(resume_info->resume_elapsed);
+ GET_INT_TOKEN(resume_info->repeat_mode);
+ GET_BOOL_TOKEN(resume_info->shuffle);
- if(prompt_ok && gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES)
+ /* extract all optional bookmark tokens */
+ if (opt_pitch != 0)
+ GET_INT_TOKEN(resume_info->pitch);
+ if (opt_speed != 0)
+ GET_INT_TOKEN(resume_info->speed);
+ }
+ else /* no resume info we just want the file name strings */
{
- if (global_settings.autocreatebookmark == BOOKMARK_RECENT_ONLY_ASK)
- return write_bookmark(false, bookmark);
- else
- return write_bookmark(true, bookmark);
+ #define DEFAULT_BM_TOKENS 6
+ int skipct = DEFAULT_BM_TOKENS + old_format + opt_pitch + opt_speed;
+ s = skip_tokens(s, skipct);
+ #undef DEFAULT_BM_TOKENS
}
- return false;
-}
-
-/* ----------------------------------------------------------------------- */
-/* This function takes the current current resume information and writes */
-/* that to the beginning of the bookmark file. */
-/* This file will contain N number of bookmarks in the following format: */
-/* resume_index*resume_offset*resume_seed*resume_first_index* */
-/* resume_file*milliseconds*MP3 Title* */
-/* Returns true on successful bookmark write. */
-/* Returns false if any part of the bookmarking process fails. It is */
-/* possible that a bookmark is successfully added to the most recent */
-/* bookmark list but fails to be added to the bookmark file or vice versa. */
-/* ------------------------------------------------------------------------*/
-static bool write_bookmark(bool create_bookmark_file, const char *bookmark)
-{
- bool ret=true;
- if (!bookmark)
+ if (*s == 0)
{
- ret = false; /* something didn't happen correctly, do nothing */
+ return false;
}
- else
- {
- if (global_settings.usemrb)
- ret = add_bookmark(RECENT_BOOKMARK_FILE, bookmark, true);
+ end = strchr(s, ';');
- /* writing the bookmark */
- if (create_bookmark_file)
+ /* extract file names */
+ if(filenamebuf)
+ {
+ size_t len = (end == NULL) ? strlen(s) : (size_t) (end - s);
+ len = MIN(TEMP_BUF_SIZE - 1, len);
+ strmemccpy(global_temp_buffer, s, len + 1);
+
+ if (end != NULL)
{
- char* name = playlist_get_name(NULL, global_temp_buffer,
- sizeof(global_temp_buffer));
- if (generate_bookmark_file_name(name))
- {
- ret = ret & add_bookmark(global_bookmark_file_name, bookmark, false);
- }
- else
+ end++;
+ if (strip_dir)
{
- ret = false; /* generating bookmark file failed */
+ s = strrchr(end, '/');
+ if (s)
+ {
+ end = s;
+ end++;
+ }
}
+ strmemccpy(filenamebuf, end, filenamebufsz);
}
- }
-
- splash(HZ, ret ? ID2P(LANG_BOOKMARK_CREATE_SUCCESS)
- : ID2P(LANG_BOOKMARK_CREATE_FAILURE));
+ }
- return ret;
+ return true;
}
-/* Get the name of the playlist and the name of the track from a bookmark. */
-/* Returns true iff both were extracted. */
-static bool get_playlist_and_track(const char *bookmark, char **pl_start,
- char **pl_end, char **track)
+/* ------------------------------------------------------------------------- */
+/* This function takes a filename and appends .tmp. This function also opens */
+/* the resulting file based on oflags, filename will be in buf on return */
+/* Returns file descriptor */
+/* --------------------------------------------------------------------------*/
+static int open_temp_bookmark(char *buf,
+ size_t bufsz,
+ int oflags,
+ const char* filename)
{
- *pl_start = strchr(bookmark,'/');
- if (!(*pl_start))
- return false;
- *pl_end = strrchr(bookmark,';');
- *track = *pl_end + 1;
- return true;
+ if(filename[0] == '/')
+ filename++;
+ /* Opening up a temp bookmark file */
+ int fd = open_pathfmt(buf, bufsz, oflags, "/%s.tmp", filename);
+#ifdef LOGF_ENABLE
+ if (oflags & O_PATH)
+ logf("tempfile path %s", buf);
+ else
+ logf("opening tempfile %s", buf);
+#endif
+ return fd;
}
/* ----------------------------------------------------------------------- */
/* This function adds a bookmark to a file. */
/* Returns true on successful bookmark add. */
/* ------------------------------------------------------------------------*/
-static bool add_bookmark(const char* bookmark_file_name, const char* bookmark,
+static bool add_bookmark(const char* bookmark_file_name,
+ const char* bookmark,
bool most_recent)
{
- int temp_bookmark_file = 0;
- int bookmark_file = 0;
- int bookmark_count = 0;
- char *pl_start = NULL, *bm_pl_start;
- char *pl_end = NULL, *bm_pl_end;
- int pl_len = 0, bm_pl_len;
- char *track = NULL, *bm_track;
- bool comp_playlist = false;
- bool comp_track = false;
- bool equal;
+ char fnamebuf[MAX_PATH];
+ int temp_bookmark_file = 0;
+ int bookmark_file = 0;
+ int bookmark_count = 0;
+ int pl_len = 0, bm_pl_len;
+ const char *pl_start = NULL, *bm_pl_start;
+ const char *pl_end = NULL, *bm_pl_end;
+ const char *track = NULL, *bm_track;
+ bool comp_playlist = false;
+ bool comp_track = false;
+ bool equal;
/* Opening up a temp bookmark file */
- snprintf(global_temp_buffer, sizeof(global_temp_buffer),
- "%s.tmp", bookmark_file_name);
- temp_bookmark_file = open(global_temp_buffer,
- O_WRONLY | O_CREAT | O_TRUNC, 0666);
- if (temp_bookmark_file < 0)
- return false; /* can't open the temp file */
+ temp_bookmark_file = open_temp_bookmark(fnamebuf,
+ sizeof(fnamebuf),
+ O_WRONLY | O_CREAT | O_TRUNC,
+ bookmark_file_name);
+
+ if (temp_bookmark_file < 0 || !bookmark)
+ return false; /* can't open the temp file or no bookmark */
if (most_recent && ((global_settings.usemrb == BOOKMARK_ONE_PER_PLAYLIST)
|| (global_settings.usemrb == BOOKMARK_ONE_PER_TRACK)))
{
- if (get_playlist_and_track(bookmark, &pl_start, &pl_end, &track))
+ if (bookmark_get_playlist_and_track(bookmark, &pl_start, &pl_end, &track))
{
comp_playlist = true;
pl_len = pl_end - pl_start;
@@ -308,35 +290,38 @@ static bool add_bookmark(const char* bookmark_file_name, const char* bookmark,
comp_track = true;
}
}
-
+ logf("adding bookmark to %s [%s]", fnamebuf, bookmark);
/* Writing the new bookmark to the begining of the temp file */
write(temp_bookmark_file, bookmark, strlen(bookmark));
write(temp_bookmark_file, "\n", 1);
bookmark_count++;
/* Reading in the previous bookmarks and writing them to the temp file */
+ logf("opening old bookmark %s", bookmark_file_name);
bookmark_file = open(bookmark_file_name, O_RDONLY);
if (bookmark_file >= 0)
{
- while (read_line(bookmark_file, global_read_buffer,
- sizeof(global_read_buffer)) > 0)
+ while (read_line(bookmark_file, global_temp_buffer,
+ sizeof(global_temp_buffer)) > 0)
{
/* The MRB has a max of MAX_BOOKMARKS in it */
/* This keeps it from getting too large */
if (most_recent && (bookmark_count >= MAX_BOOKMARKS))
break;
- if (!parse_bookmark(global_read_buffer, false, false))
+ if (!parse_bookmark(NULL, 0, global_temp_buffer, NULL, false))
break;
equal = false;
if (comp_playlist)
{
- if (get_playlist_and_track(global_read_buffer, &bm_pl_start,
- &bm_pl_end, &bm_track))
+ if (bookmark_get_playlist_and_track(global_temp_buffer,
+ &bm_pl_start, &bm_pl_end, &bm_track))
{
bm_pl_len = bm_pl_end - bm_pl_start;
- equal = (pl_len == bm_pl_len) && !strncmp(pl_start, bm_pl_start, pl_len);
+ equal = (pl_len == bm_pl_len) &&
+ !strncmp(pl_start, bm_pl_start, pl_len);
+
if (equal && comp_track)
equal = !strcmp(track, bm_track);
}
@@ -344,8 +329,9 @@ static bool add_bookmark(const char* bookmark_file_name, const char* bookmark,
if (!equal)
{
bookmark_count++;
- write(temp_bookmark_file, global_read_buffer,
- strlen(global_read_buffer));
+ /*logf("copying old bookmark [%s]", global_temp_buffer);*/
+ write(temp_bookmark_file, global_temp_buffer,
+ strlen(global_temp_buffer));
write(temp_bookmark_file, "\n", 1);
}
}
@@ -353,181 +339,239 @@ static bool add_bookmark(const char* bookmark_file_name, const char* bookmark,
}
close(temp_bookmark_file);
+ /* only retrieve the path*/
+ open_temp_bookmark(fnamebuf,
+ sizeof(fnamebuf),
+ O_PATH,
+ bookmark_file_name);
remove(bookmark_file_name);
- rename(global_temp_buffer, bookmark_file_name);
+ rename(fnamebuf, bookmark_file_name);
return true;
}
+/* ----------------------------------------------------------------------- */
+/* This function is used by multiple functions and is used to generate a */
+/* bookmark named based off of the input. */
+/* Changing this function could result in how the bookmarks are stored. */
+/* it would be here that the centralized/decentralized bookmark code */
+/* could be placed. */
+/* Returns true if the file name is generated, false if it was too long */
+/* ----------------------------------------------------------------------- */
+static bool generate_bookmark_file_name(char *filenamebuf,
+ size_t filenamebufsz,
+ const char *bmarknamein,
+ size_t bmarknamelen)
+{
+ /* if this is a root dir MP3, rename the bookmark file root_dir.bmark */
+ /* otherwise, name it based on the bmarknamein variable */
+ if (!strcmp("/", bmarknamein))
+ strmemccpy(filenamebuf, "/root_dir.bmark", filenamebufsz);
+ else
+ {
+ size_t buflen, len;
+ /* strmemccpy considers the NULL so bmarknamelen is one off */
+ buflen = MIN(filenamebufsz -1 , bmarknamelen);
+ if (buflen >= filenamebufsz)
+ return false;
+
+ strmemccpy(filenamebuf, bmarknamein, buflen + 1);
+
+ len = strlen(filenamebuf);
+
+#ifdef HAVE_MULTIVOLUME
+ /* The "root" of an extra volume need special handling too. */
+ const char *filename;
+ path_strip_volume(filenamebuf, &filename, true);
+ bool volume_root = *filename == '\0';
+#endif
+ if(filenamebuf[len-1] == '/') {
+ filenamebuf[len-1] = '\0';
+ }
+
+ const char *name = ".bmark";
+#ifdef HAVE_MULTIVOLUME
+ if (volume_root)
+ name = "/volume_dir.bmark";
+#endif
+ len = strlcat(filenamebuf, name, filenamebufsz);
+
+ if(len >= filenamebufsz)
+ return false;
+ }
+ logf ("generated name '%s' from '%.*s'",
+ filenamebuf, (int)bmarknamelen, bmarknamein);
+ return true;
+}
+
/* GCC 7 and up complain about the snprintf in create_bookmark() when
compiled with -D_FORTIFY_SOURCE or -Wformat-truncation
This is a false positive, so disable it here only */
-#if __GNUC__ >= 7
+/* SHOULD NO LONGER BE NEEDED --Bilgus 11-2022 */
+#if 0 /* __GNUC__ >= 7 */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-truncation"
#endif
/* ----------------------------------------------------------------------- */
/* This function takes the system resume data and formats it into a valid */
/* bookmark. */
-/* Returns not NULL on successful bookmark format. */
+/* playlist name and name len are passed back through the name/namelen */
+/* Return is not NULL on successful bookmark format. */
/* ----------------------------------------------------------------------- */
-static char* create_bookmark()
+static char* create_bookmark(char **name,
+ size_t *namelen,
+ struct resume_info *resume_info)
{
- int resume_index = 0;
- char *file;
+ const char *file;
+ char *buf = global_temp_buffer;
+ size_t bufsz = sizeof(global_temp_buffer);
- if (!bookmark_is_bookmarkable_state())
- return NULL; /* something didn't happen correctly, do nothing */
-
- /* grab the currently playing track */
- struct mp3entry *id3 = audio_current_track();
- if(!id3)
+ if(!resume_info->id3)
return NULL;
- /* Get some basic resume information */
- /* queue_resume and queue_resume_index are not used and can be ignored.*/
- playlist_get_resume_info(&resume_index);
-
- /* Get the currently playing file minus the path */
- /* This is used when displaying the available bookmarks */
- file = strrchr(id3->path,'/');
- if(NULL == file)
- return NULL;
-
- /* create the bookmark */
- playlist_get_name(NULL, global_temp_buffer, sizeof(global_temp_buffer));
- if (global_temp_buffer[strlen(global_temp_buffer) - 1] != '/')
- file = id3->path;
- else file++;
- snprintf(global_bookmark, sizeof(global_bookmark),
- /* new optional bookmark token descriptors should be inserted
- just before the "%s;%s" in this line... */
+ size_t bmarksz= snprintf(buf, bufsz,
+ /* new optional bookmark token descriptors should
+ be inserted just after ';"' in this line... */
#if defined(HAVE_PITCHCONTROL)
- ">%d;%d;%ld;%d;%ld;%d;%d;%ld;%ld;%s;%s",
+ ">%d;%d;%ld;%d;%ld;%d;%d;%ld;%ld;",
#else
- ">%d;%d;%ld;%d;%ld;%d;%d;%s;%s",
+ ">%d;%d;%ld;%d;%ld;%d;%d;",
#endif
- /* ... their flags should go here ... */
+ /* ... their flags should go here ... */
#if defined(HAVE_PITCHCONTROL)
- BM_PITCH | BM_SPEED,
+ BM_PITCH | BM_SPEED,
#else
- 0,
+ 0,
#endif
- resume_index,
- id3->offset,
- playlist_get_seed(NULL),
- id3->elapsed,
- global_settings.repeat_mode,
- global_settings.playlist_shuffle,
- /* ...and their values should go here */
+ resume_info->resume_index,
+ resume_info->id3->offset,
+ resume_info->resume_seed,
+ resume_info->id3->elapsed,
+ resume_info->repeat_mode,
+ resume_info->shuffle,
+ /* ...and their values should go here */
#if defined(HAVE_PITCHCONTROL)
- (long)sound_get_pitch(),
- (long)dsp_get_timestretch(),
+ (long)resume_info->pitch,
+ (long)resume_info->speed
#endif
- /* more mandatory tokens */
- global_temp_buffer,
- file);
+ ); /*sprintf*/
+/* mandatory tokens */
+ if (bmarksz >= bufsz) /* include NULL*/
+ return NULL;
+ buf += bmarksz;
+ bufsz -= bmarksz;
+
+ /* create the bookmark */
+ playlist_get_name(NULL, buf, bufsz);
+ bmarksz = strlen(buf);
+
+ if (bmarksz == 0 || (bmarksz + 1) >= bufsz) /* include the separator & NULL*/
+ return NULL;
+
+ *name = buf; /* return the playlist name through the *pointer */
+ *namelen = bmarksz; /* return the name length through the pointer */
+
+ /* Get the currently playing file minus the path */
+ /* This is used when displaying the available bookmarks */
+ file = strrchr(resume_info->id3->path,'/');
+ if(NULL == file)
+ return NULL;
+
+ if (buf[bmarksz - 1] != '/')
+ file = resume_info->id3->path;
+ else file++;
+
+ buf += bmarksz;
+ bufsz -= (bmarksz + 1);
+ buf[0] = ';';
+ buf[1] = '\0';
+
+ strlcat(buf, file, bufsz);
+ logf("%s [%s]", __func__, global_temp_buffer);
/* checking to see if the bookmark is valid */
- if (parse_bookmark(global_bookmark, false, false))
- return global_bookmark;
+ if (parse_bookmark(NULL, 0, global_temp_buffer, NULL, false))
+ return global_temp_buffer;
else
return NULL;
}
-#if __GNUC__ >= 7
+#if 0/* __GNUC__ >= 7*/
#pragma GCC diagnostic pop /* -Wformat-truncation */
#endif
/* ----------------------------------------------------------------------- */
-/* This function will determine if an autoload is necessary. This is an */
-/* interface function. */
-/* Returns true on bookmark load or bookmark selection. */
-/* ------------------------------------------------------------------------*/
-bool bookmark_autoload(const char* file)
+/* This function gets some basic resume information for the current song */
+/* from rockbox, */
+/* ----------------------------------------------------------------------- */
+static void get_track_resume_info(struct resume_info *resume_info)
{
- char* bookmark;
-
- if(global_settings.autoloadbookmark == BOOKMARK_NO)
- return false;
-
- /*Checking to see if a bookmark file exists.*/
- if(!generate_bookmark_file_name(file))
- {
- return false;
- }
-
- if(!file_exists(global_bookmark_file_name))
- return false;
-
- if(global_settings.autoloadbookmark == BOOKMARK_YES)
- {
- return bookmark_load(global_bookmark_file_name, true);
- }
- else
- {
- int ret = select_bookmark(global_bookmark_file_name, true, &bookmark);
-
- if (bookmark != NULL)
- {
- if (!play_bookmark(bookmark))
- {
- /* Selected bookmark not found. */
- splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME));
- }
-
- /* Act as if autoload was done even if it failed, since the
- * user did make an active selection.
- */
- return true;
- }
-
- return ret != BOOKMARK_SUCCESS;
- }
+ playlist_get_resume_info(&(resume_info->resume_index));
+ resume_info->resume_seed = playlist_get_seed(NULL);
+ resume_info->id3 = audio_current_track();
+ resume_info->repeat_mode = global_settings.repeat_mode;
+ resume_info->shuffle = global_settings.playlist_shuffle;
+#if defined(HAVE_PITCHCONTROL)
+ resume_info->pitch = sound_get_pitch();
+ resume_info->speed = dsp_get_timestretch();
+#endif
}
/* ----------------------------------------------------------------------- */
-/* This function loads the bookmark information into the resume memory. */
-/* This is an interface function. */
-/* Returns true on successful bookmark load. */
+/* This function takes the current current resume information and writes */
+/* that to the beginning of the bookmark file. */
+/* This file will contain N number of bookmarks in the following format: */
+/* resume_index*resume_offset*resume_seed*resume_first_index* */
+/* resume_file*milliseconds*MP3 Title* */
+/* Returns true on successful bookmark write. */
+/* Returns false if any part of the bookmarking process fails. It is */
+/* possible that a bookmark is successfully added to the most recent */
+/* bookmark list but fails to be added to the bookmark file or vice versa. */
/* ------------------------------------------------------------------------*/
-bool bookmark_load(const char* file, bool autoload)
+static bool write_bookmark(bool create_bookmark_file)
{
- int fd;
- char* bookmark = NULL;
+ logf("%s", __func__);
+ char bm_filename[MAX_PATH];
+ bool ret=true;
- if(autoload)
+ char *name = NULL;
+ size_t namelen = 0;
+ char* bm;
+ struct resume_info resume_info;
+
+ if (bookmark_is_bookmarkable_state())
{
- fd = open(file, O_RDONLY);
- if(fd >= 0)
+ get_track_resume_info(&resume_info);
+ /* writing the most recent bookmark */
+ if (global_settings.usemrb)
{
- if(read_line(fd, global_read_buffer, sizeof(global_read_buffer)) > 0)
- bookmark=global_read_buffer;
- close(fd);
+ /* since we use the same buffer bookmark needs created each time */
+ bm = create_bookmark(&name, &namelen, &resume_info);
+ ret = add_bookmark(RECENT_BOOKMARK_FILE, bm, true);
}
- }
- else
- {
- /* This is not an auto-load, so list the bookmarks */
- select_bookmark(file, false, &bookmark);
- }
- if (bookmark != NULL)
- {
- if (!play_bookmark(bookmark))
+ /* writing the directory bookmark */
+ if (create_bookmark_file)
{
- /* Selected bookmark not found. */
- if (!autoload)
+ bm = create_bookmark(&name, &namelen, &resume_info);
+ if (generate_bookmark_file_name(bm_filename,
+ sizeof(bm_filename), name, namelen))
{
- splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME));
+ ret &= add_bookmark(bm_filename, bm, false);
+ }
+ else
+ {
+ ret = false; /* generating bookmark file failed */
}
-
- return false;
}
}
+ else
+ ret = false;
- return true;
-}
+ splash(HZ, ret ? ID2P(LANG_BOOKMARK_CREATE_SUCCESS)
+ : ID2P(LANG_BOOKMARK_CREATE_FAILURE));
+ return ret;
+}
static int get_bookmark_count(const char* bookmark_file_name)
{
@@ -537,7 +581,7 @@ static int get_bookmark_count(const char* bookmark_file_name)
if(file < 0)
return -1;
- while(read_line(file, global_read_buffer, sizeof(global_read_buffer)) > 0)
+ while(read_line(file, global_temp_buffer, sizeof(global_temp_buffer)) > 0)
{
read_count++;
}
@@ -568,13 +612,13 @@ static int buffer_bookmarks(struct bookmark_list* bookmarks, int first_line)
bookmarks->count = 0;
bookmarks->reload = false;
- while(read_line(file, global_read_buffer, sizeof(global_read_buffer)) > 0)
+ while(read_line(file, global_temp_buffer, sizeof(global_temp_buffer)) > 0)
{
read_count++;
if (read_count >= first_line)
{
- dest -= strlen(global_read_buffer) + 1;
+ dest -= strlen(global_temp_buffer) + 1;
if (dest < ((char*) bookmarks) + sizeof(*bookmarks)
+ (sizeof(char*) * (bookmarks->count + 1)))
@@ -582,7 +626,7 @@ static int buffer_bookmarks(struct bookmark_list* bookmarks, int first_line)
break;
}
- strcpy(dest, global_read_buffer);
+ strcpy(dest, global_temp_buffer);
bookmarks->items[bookmarks->count] = dest;
bookmarks->count++;
}
@@ -597,6 +641,8 @@ static const char* get_bookmark_info(int list_index,
char *buffer,
size_t buffer_len)
{
+ char fnamebuf[MAX_PATH];
+ struct resume_info resume_info;
struct bookmark_list* bookmarks = (struct bookmark_list*) data;
int index = list_index / 2;
@@ -652,7 +698,8 @@ static const char* get_bookmark_info(int list_index,
}
}
- if (!parse_bookmark(bookmarks->items[index - bookmarks->start], true, true))
+ if (!parse_bookmark(fnamebuf, sizeof(fnamebuf),
+ bookmarks->items[index - bookmarks->start], &resume_info, true))
{
return list_index % 2 == 0 ? (char*) str(LANG_BOOKMARK_INVALID) : " ";
}
@@ -686,25 +733,73 @@ static const char* get_bookmark_info(int list_index,
}
else
{
- name = global_filename;
+ name = fnamebuf;
format = "%s";
}
- strrsplt(global_filename, '.');
- snprintf(buffer, buffer_len, format, name, global_filename);
+ strrsplt(fnamebuf, '.');
+ snprintf(buffer, buffer_len, format, name, fnamebuf);
return buffer;
}
else
{
char time_buf[32];
- format_time(time_buf, sizeof(time_buf), bm.resume_time);
- snprintf(buffer, buffer_len, "%s, %d%s", time_buf, bm.resume_index + 1,
- bm.shuffle ? (char*) str(LANG_BOOKMARK_SHUFFLE) : "");
+ format_time(time_buf, sizeof(time_buf), resume_info.resume_elapsed);
+ snprintf(buffer, buffer_len, "%s, %d%s", time_buf,
+ resume_info.resume_index + 1,
+ resume_info.shuffle ? (char*) str(LANG_BOOKMARK_SHUFFLE) : "");
return buffer;
}
}
+/* ----------------------------------------------------------------------- */
+/* This function parses a bookmark, says the voice UI part of it. */
+/* ------------------------------------------------------------------------*/
+static void say_bookmark(const char* bookmark,
+ int bookmark_id,
+ bool show_playlist_name)
+{
+ char fnamebuf[MAX_PATH];
+ struct resume_info resume_info;
+ if (!parse_bookmark(fnamebuf, sizeof(fnamebuf), bookmark, &resume_info, false))
+ {
+ talk_id(LANG_BOOKMARK_INVALID, false);
+ return;
+ }
+
+ talk_number(bookmark_id + 1, false);
+
+ bool is_dir = (global_temp_buffer[0]
+ && global_temp_buffer[strlen(global_temp_buffer)-1] == '/');
+
+ /* HWCODEC cannot enqueue voice file entries and .talk thumbnails
+ together, because there is no guarantee that the same mp3
+ parameters are used. */
+ if(show_playlist_name)
+ { /* It's useful to know which playlist this is */
+ if(is_dir)
+ talk_dir_or_spell(global_temp_buffer,
+ TALK_IDARRAY(VOICE_DIR), true);
+ else talk_file_or_spell(NULL, global_temp_buffer,
+ TALK_IDARRAY(LANG_PLAYLIST), true);
+ }
+
+ if(resume_info.shuffle)
+ talk_id(LANG_SHUFFLE, true);
+
+ talk_id(VOICE_BOOKMARK_SELECT_INDEX_TEXT, true);
+ talk_number(resume_info.resume_index + 1, true);
+ talk_id(LANG_TIME, true);
+ talk_value(resume_info.resume_elapsed / 1000, UNIT_TIME, true);
+
+ /* Track filename */
+ if(!is_dir)
+ global_temp_buffer[0] = 0;
+ talk_file_or_spell(global_temp_buffer, fnamebuf,
+ TALK_IDARRAY(VOICE_FILE), true);
+}
+
static int bookmark_list_voice_cb(int list_index, void* data)
{
struct bookmark_list* bookmarks = (struct bookmark_list*) data;
@@ -722,6 +817,57 @@ static int bookmark_list_voice_cb(int list_index, void* data)
}
/* ----------------------------------------------------------------------- */
+/* This function takes a location in a bookmark file and deletes that */
+/* bookmark. */
+/* Returns true on successful bookmark deletion. */
+/* ------------------------------------------------------------------------*/
+static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id)
+{
+ int temp_bookmark_file = 0;
+ int bookmark_file = 0;
+ int bookmark_count = 0;
+
+ /* Opening up a temp bookmark file */
+ temp_bookmark_file = open_temp_bookmark(global_temp_buffer,
+ sizeof(global_temp_buffer),
+ O_WRONLY | O_CREAT | O_TRUNC,
+ bookmark_file_name);
+
+ if (temp_bookmark_file < 0)
+ return false; /* can't open the temp file */
+
+ /* Reading in the previous bookmarks and writing them to the temp file */
+ bookmark_file = open(bookmark_file_name, O_RDONLY);
+ if (bookmark_file >= 0)
+ {
+ while (read_line(bookmark_file, global_temp_buffer,
+ sizeof(global_temp_buffer)) > 0)
+ {
+ if (bookmark_id != bookmark_count)
+ {
+ write(temp_bookmark_file, global_temp_buffer,
+ strlen(global_temp_buffer));
+ write(temp_bookmark_file, "\n", 1);
+ }
+ bookmark_count++;
+ }
+ close(bookmark_file);
+ }
+ close(temp_bookmark_file);
+
+ /* only retrieve the path*/
+ open_temp_bookmark(global_temp_buffer,
+ sizeof(global_temp_buffer),
+ O_PATH,
+ bookmark_file_name);
+
+ remove(bookmark_file_name);
+ rename(global_temp_buffer, bookmark_file_name);
+
+ return true;
+}
+
+/* ----------------------------------------------------------------------- */
/* This displays the bookmarks in a file and allows the user to */
/* select one to play. */
/* *selected_bookmark contains a non NULL value on successful bookmark */
@@ -730,7 +876,9 @@ static int bookmark_list_voice_cb(int list_index, void* data)
/* if no selection was made and BOOKMARK_USB_CONNECTED if the selection */
/* menu is forced to exit due to a USB connection. */
/* ------------------------------------------------------------------------*/
-static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume, char** selected_bookmark)
+static int select_bookmark(const char* bookmark_file_name,
+ bool show_dont_resume,
+ char** selected_bookmark)
{
struct bookmark_list* bookmarks;
struct gui_synclist list;
@@ -747,8 +895,11 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume
bookmarks->filename = bookmark_file_name;
bookmarks->start = 0;
bookmarks->show_playlist_name
- = strcmp(bookmark_file_name, RECENT_BOOKMARK_FILE) == 0;
- gui_synclist_init(&list, &get_bookmark_info, (void*) bookmarks, false, 2, NULL);
+ = (strcmp(bookmark_file_name, RECENT_BOOKMARK_FILE) == 0);
+
+ gui_synclist_init(&list, &get_bookmark_info,
+ (void*) bookmarks, false, 2, NULL);
+
if(global_settings.talk_menu)
gui_synclist_set_voice_callback(&list, bookmark_list_voice_cb);
gui_synclist_set_title(&list, str(LANG_BOOKMARK_SELECT_BOOKMARK),
@@ -793,8 +944,7 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume
refresh = false;
}
- list_do_action(CONTEXT_BOOKMARKSCREEN, HZ / 2,
- &list, &action, LIST_WRAP_UNLESS_HELD);
+ list_do_action(CONTEXT_BOOKMARKSCREEN, HZ / 2, &list, &action);
item = gui_synclist_get_sel_pos(&list) / 2;
if (bookmarks->show_dont_resume)
@@ -843,17 +993,7 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume
case ACTION_BMS_DELETE:
if (item >= 0)
{
- const char *lines[]={
- ID2P(LANG_REALLY_DELETE)
- };
- const char *yes_lines[]={
- ID2P(LANG_DELETING)
- };
-
- const struct text_message message={lines, 1};
- const struct text_message yes_message={yes_lines, 1};
-
- if(gui_syncyesno_run(&message, &yes_message, NULL)==YESNO_YES)
+ if (confirm_delete_yesno("") == YESNO_YES)
{
delete_bookmark(bookmark_file_name, item);
bookmarks->reload = true;
@@ -879,264 +1019,254 @@ static int select_bookmark(const char* bookmark_file_name, bool show_dont_resume
}
/* ----------------------------------------------------------------------- */
-/* This function takes a location in a bookmark file and deletes that */
-/* bookmark. */
-/* Returns true on successful bookmark deletion. */
+/* This function parses a bookmark and then plays it. */
+/* Returns true on successful bookmark play. */
/* ------------------------------------------------------------------------*/
-static bool delete_bookmark(const char* bookmark_file_name, int bookmark_id)
+static bool play_bookmark(const char* bookmark)
{
- int temp_bookmark_file = 0;
- int bookmark_file = 0;
- int bookmark_count = 0;
-
- /* Opening up a temp bookmark file */
- snprintf(global_temp_buffer, sizeof(global_temp_buffer),
- "%s.tmp", bookmark_file_name);
- temp_bookmark_file = open(global_temp_buffer,
- O_WRONLY | O_CREAT | O_TRUNC, 0666);
-
- if (temp_bookmark_file < 0)
- return false; /* can't open the temp file */
+ char fnamebuf[MAX_PATH];
+ struct resume_info resume_info;
+#if defined(HAVE_PITCHCONTROL)
+ /* preset pitch and speed to 100% in case bookmark doesn't have info */
+ resume_info.pitch = sound_get_pitch();
+ resume_info.speed = dsp_get_timestretch();
+#endif
- /* Reading in the previous bookmarks and writing them to the temp file */
- bookmark_file = open(bookmark_file_name, O_RDONLY);
- if (bookmark_file >= 0)
+ if (parse_bookmark(fnamebuf, sizeof(fnamebuf), bookmark, &resume_info, true))
{
- while (read_line(bookmark_file, global_read_buffer,
- sizeof(global_read_buffer)) > 0)
+ global_settings.repeat_mode = resume_info.repeat_mode;
+ global_settings.playlist_shuffle = resume_info.shuffle;
+#if defined(HAVE_PITCHCONTROL)
+ sound_set_pitch(resume_info.pitch);
+ dsp_set_timestretch(resume_info.speed);
+#endif
+ if (!warn_on_pl_erase())
+ return false;
+ bool success = bookmark_play(global_temp_buffer, resume_info.resume_index,
+ resume_info.resume_elapsed, resume_info.resume_offset,
+ resume_info.resume_seed, fnamebuf);
+ if (success) /* verify we loaded the correct track */
{
- if (bookmark_id != bookmark_count)
+ const struct mp3entry *id3 = audio_current_track();
+ if (id3)
{
- write(temp_bookmark_file, global_read_buffer,
- strlen(global_read_buffer));
- write(temp_bookmark_file, "\n", 1);
+ const char *path;
+ const char *track;
+ path_basename(id3->path, &path);
+ path_basename(fnamebuf, &track);
+ if (strcmp(path, track) == 0)
+ {
+ return true;
+ }
}
- bookmark_count++;
+ audio_stop();
}
- close(bookmark_file);
}
- close(temp_bookmark_file);
-
- remove(bookmark_file_name);
- rename(global_temp_buffer, bookmark_file_name);
- return true;
+ return false;
}
+/*-------------------------------------------------------------------------*/
+/* PUBLIC INTERFACE -------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+
+
/* ----------------------------------------------------------------------- */
-/* This function parses a bookmark, says the voice UI part of it. */
-/* ------------------------------------------------------------------------*/
-static void say_bookmark(const char* bookmark,
- int bookmark_id, bool show_playlist_name)
+/* This is an interface function from the context menu. */
+/* Returns true on successful bookmark creation. */
+/* ----------------------------------------------------------------------- */
+bool bookmark_create_menu(void)
{
- if (!parse_bookmark(bookmark, true, false))
- {
- talk_id(LANG_BOOKMARK_INVALID, false);
- return;
- }
-
- talk_number(bookmark_id + 1, false);
+ return write_bookmark(true);
+}
+/* ----------------------------------------------------------------------- */
+/* This function acts as the load interface from the context menu. */
+/* This function determines the bookmark file name and then loads that file*/
+/* for the user. The user can then select or delete previous bookmarks. */
+/* This function returns BOOKMARK_SUCCESS on the selection of a track to */
+/* resume, BOOKMARK_FAIL if the menu is exited without a selection and */
+/* BOOKMARK_USB_CONNECTED if the menu is forced to exit due to a USB */
+/* connection. */
+/* ----------------------------------------------------------------------- */
+int bookmark_load_menu(void)
+{
+ char bm_filename[MAX_PATH];
+ char* bookmark;
+ int ret = BOOKMARK_FAIL;
- bool is_dir = (global_temp_buffer[0]
- && global_temp_buffer[strlen(global_temp_buffer)-1] == '/');
+ push_current_activity(ACTIVITY_BOOKMARKSLIST);
- /* HWCODEC cannot enqueue voice file entries and .talk thumbnails
- together, because there is no guarantee that the same mp3
- parameters are used. */
- if(show_playlist_name)
- { /* It's useful to know which playlist this is */
- if(is_dir)
- talk_dir_or_spell(global_temp_buffer,
- TALK_IDARRAY(VOICE_DIR), true);
- else talk_file_or_spell(NULL, global_temp_buffer,
- TALK_IDARRAY(LANG_PLAYLIST), true);
+ char* name = playlist_get_name(NULL, global_temp_buffer,
+ sizeof(global_temp_buffer));
+ if (generate_bookmark_file_name(bm_filename, sizeof(bm_filename), name, -1))
+ {
+ ret = select_bookmark(bm_filename, false, &bookmark);
+ if (bookmark != NULL)
+ {
+ ret = play_bookmark(bookmark) ? BOOKMARK_SUCCESS : BOOKMARK_FAIL;
+ }
}
- if(bm.shuffle)
- talk_id(LANG_SHUFFLE, true);
-
- talk_id(VOICE_BOOKMARK_SELECT_INDEX_TEXT, true);
- talk_number(bm.resume_index + 1, true);
- talk_id(LANG_TIME, true);
- talk_value(bm.resume_time / 1000, UNIT_TIME, true);
-
- /* Track filename */
- if(!is_dir)
- global_temp_buffer[0] = 0;
- talk_file_or_spell(global_temp_buffer, global_filename,
- TALK_IDARRAY(VOICE_FILE), true);
+ pop_current_activity();
+ return ret;
}
/* ----------------------------------------------------------------------- */
-/* This function parses a bookmark and then plays it. */
-/* Returns true on successful bookmark play. */
-/* ------------------------------------------------------------------------*/
-static bool play_bookmark(const char* bookmark)
+/* Gives the user a list of the Most Recent Bookmarks. This is an */
+/* interface function */
+/* Returns true on the successful selection of a recent bookmark. */
+/* ----------------------------------------------------------------------- */
+bool bookmark_mrb_load()
{
-#if defined(HAVE_PITCHCONTROL)
- /* preset pitch and speed to 100% in case bookmark doesn't have info */
- bm.pitch = sound_get_pitch();
- bm.speed = dsp_get_timestretch();
-#endif
+ char* bookmark;
+ bool ret = false;
- if (parse_bookmark(bookmark, true, true))
+ push_current_activity(ACTIVITY_BOOKMARKSLIST);
+ select_bookmark(RECENT_BOOKMARK_FILE, false, &bookmark);
+ if (bookmark != NULL)
{
- global_settings.repeat_mode = bm.repeat_mode;
- global_settings.playlist_shuffle = bm.shuffle;
-#if defined(HAVE_PITCHCONTROL)
- sound_set_pitch(bm.pitch);
- dsp_set_timestretch(bm.speed);
-#endif
- if (!warn_on_pl_erase())
- return false;
- return bookmark_play(global_temp_buffer, bm.resume_index,
- bm.resume_time, bm.resume_offset, bm.resume_seed, global_filename);
+ ret = play_bookmark(bookmark);
}
- return false;
+ pop_current_activity();
+ return ret;
}
-static const char* skip_token(const char* s)
+/* ----------------------------------------------------------------------- */
+/* This function handles an autobookmark creation. This is an interface */
+/* function. */
+/* Returns true on successful bookmark creation. */
+/* ----------------------------------------------------------------------- */
+bool bookmark_autobookmark(bool prompt_ok)
{
- while (*s && *s != ';')
- {
- s++;
- }
+ logf("%s", __func__);
+ bool update;
+
+ if (!bookmark_is_bookmarkable_state())
+ return false;
- if (*s)
+ audio_pause(); /* first pause playback */
+ update = (global_settings.autoupdatebookmark && bookmark_exists());
+
+ if (update)
+ return write_bookmark(true);
+
+ switch (global_settings.autocreatebookmark)
{
- s++;
- }
+ case BOOKMARK_YES:
+ return write_bookmark(true);
- return s;
-}
+ case BOOKMARK_NO:
+ return false;
-static const char* int_token(const char* s, int* dest)
-{
- *dest = atoi(s);
- return skip_token(s);
-}
+ case BOOKMARK_RECENT_ONLY_YES:
+ return write_bookmark(false);
+ }
+ const char *lines[]={ID2P(LANG_AUTO_BOOKMARK_QUERY)};
+ const struct text_message message={lines, 1};
-static const char* long_token(const char* s, long* dest)
-{
- *dest = atoi(s); /* Should be atol, but we don't have it. */
- return skip_token(s);
+ if(prompt_ok && gui_syncyesno_run(&message, NULL, NULL)==YESNO_YES)
+ {
+ if (global_settings.autocreatebookmark == BOOKMARK_RECENT_ONLY_ASK)
+ return write_bookmark(false);
+ else
+ return write_bookmark(true);
+ }
+ return false;
}
/* ----------------------------------------------------------------------- */
-/* This function takes a bookmark and parses it. This function also */
-/* validates the bookmark. The parse_filenames flag indicates whether */
-/* the filename tokens are to be extracted. */
-/* Returns true on successful bookmark parse. */
-/* ----------------------------------------------------------------------- */
-static bool parse_bookmark(const char *bookmark, const bool parse_filenames, const bool strip_dir)
+/* This function will determine if an autoload is necessary. This is an */
+/* interface function. */
+/* Returns */
+/* BOOKMARK_DO_RESUME on bookmark load or bookmark selection. */
+/* BOOKMARK_DONT_RESUME if we're not going to resume */
+/* BOOKMARK_CANCEL if user canceled */
+/* ------------------------------------------------------------------------*/
+int bookmark_autoload(const char* file)
{
- const char* s = bookmark;
- const char* end;
+ logf("%s", __func__);
+ char bm_filename[MAX_PATH];
+ char* bookmark;
-#define GET_INT_TOKEN(var) s = int_token(s, &var)
-#define GET_LONG_TOKEN(var) s = long_token(s, &var)
-#define GET_BOOL_TOKEN(var) var = (atoi(s)!=0); s = skip_token(s)
+ if(global_settings.autoloadbookmark == BOOKMARK_NO)
+ return BOOKMARK_DONT_RESUME;
- /* if new format bookmark, extract the optional content flags,
- otherwise treat as an original format bookmark */
- int opt_flags = 0;
- bool new_format = (strchr(s, '>') == s);
- if (new_format)
+ /*Checking to see if a bookmark file exists.*/
+ if(!generate_bookmark_file_name(bm_filename, sizeof(bm_filename), file, -1))
{
- s++;
- GET_INT_TOKEN(opt_flags);
+ return BOOKMARK_DONT_RESUME;
}
- /* extract all original bookmark tokens */
- GET_INT_TOKEN(bm.resume_index);
- GET_LONG_TOKEN(bm.resume_offset);
- GET_INT_TOKEN(bm.resume_seed);
- if (!new_format) /* skip deprecated token */
- s = skip_token(s);
- GET_LONG_TOKEN(bm.resume_time);
- GET_INT_TOKEN(bm.repeat_mode);
- GET_BOOL_TOKEN(bm.shuffle);
-
- /* extract all optional bookmark tokens */
- if (opt_flags & BM_PITCH)
- GET_INT_TOKEN(bm.pitch);
- if (opt_flags & BM_SPEED)
- GET_INT_TOKEN(bm.speed);
+ if(!file_exists(bm_filename))
+ return BOOKMARK_DONT_RESUME;
- if (*s == 0)
+ if(global_settings.autoloadbookmark == BOOKMARK_YES)
{
- return false;
+ return (bookmark_load(bm_filename, true)
+ ? BOOKMARK_DO_RESUME : BOOKMARK_DONT_RESUME);
}
-
- end = strchr(s, ';');
-
- /* extract file names */
- if (parse_filenames)
+ else
{
- size_t len = (end == NULL) ? strlen(s) : (size_t) (end - s);
- len = MIN(TEMP_BUF_SIZE - 1, len);
- strlcpy(global_temp_buffer, s, len + 1);
+ int ret = select_bookmark(bm_filename, true, &bookmark);
- if (end != NULL)
+ if (bookmark != NULL)
{
- end++;
- if (strip_dir)
+ if (!play_bookmark(bookmark))
{
- s = strrchr(end, '/');
- if (s)
- {
- end = s;
- end++;
- }
+ /* Selected bookmark not found. */
+ splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME));
}
- strlcpy(global_filename, end, MAX_PATH);
+
+ /* Act as if autoload was done even if it failed, since the
+ * user did make an active selection.
+ */
+ return BOOKMARK_DO_RESUME;
}
- }
- return true;
+ return ret != (BOOKMARK_SUCCESS ? BOOKMARK_CANCEL : BOOKMARK_DONT_RESUME);
+ }
}
/* ----------------------------------------------------------------------- */
-/* This function is used by multiple functions and is used to generate a */
-/* bookmark named based off of the input. */
-/* Changing this function could result in how the bookmarks are stored. */
-/* it would be here that the centralized/decentralized bookmark code */
-/* could be placed. */
-/* Returns true if the file name is generated, false if it was too long */
-/* ----------------------------------------------------------------------- */
-static bool generate_bookmark_file_name(const char *in)
+/* This function loads the bookmark information into the resume memory. */
+/* This is an interface function. */
+/* Returns true on successful bookmark load. */
+/* ------------------------------------------------------------------------*/
+bool bookmark_load(const char* file, bool autoload)
{
- /* if this is a root dir MP3, rename the bookmark file root_dir.bmark */
- /* otherwise, name it based on the in variable */
- if (!strcmp("/", in))
- strcpy(global_bookmark_file_name, "/root_dir.bmark");
- else
- {
-#ifdef HAVE_MULTIVOLUME
- /* The "root" of an extra volume need special handling too. */
- const char *filename;
- path_strip_volume(in, &filename, true);
- bool volume_root = *filename == '\0';
-#endif
- size_t len = strlcpy(global_bookmark_file_name, in, MAX_PATH);
- if(len >= MAX_PATH)
- return false;
+ logf("%s", __func__);
+ int fd;
+ char* bookmark = NULL;
- if(global_bookmark_file_name[len-1] == '/') {
- global_bookmark_file_name[len-1] = '\0';
- len--;
+ if(autoload)
+ {
+ fd = open(file, O_RDONLY);
+ if(fd >= 0)
+ {
+ if(read_line(fd, global_temp_buffer, sizeof(global_temp_buffer)) > 0)
+ bookmark=global_temp_buffer;
+ close(fd);
}
+ }
+ else
+ {
+ /* This is not an auto-load, so list the bookmarks */
+ select_bookmark(file, false, &bookmark);
+ }
-#ifdef HAVE_MULTIVOLUME
- if (volume_root)
- len = strlcat(global_bookmark_file_name, "/volume_dir.bmark", MAX_PATH);
- else
-#endif
- len = strlcat(global_bookmark_file_name, ".bmark", MAX_PATH);
+ if (bookmark != NULL)
+ {
+ if (!play_bookmark(bookmark))
+ {
+ /* Selected bookmark not found. */
+ if (!autoload)
+ {
+ splash(HZ*2, ID2P(LANG_NOTHING_TO_RESUME));
+ }
- if(len >= MAX_PATH)
return false;
+ }
}
return true;
@@ -1148,13 +1278,14 @@ static bool generate_bookmark_file_name(const char *in)
/* ----------------------------------------------------------------------- */
bool bookmark_exists(void)
{
+ char bm_filename[MAX_PATH];
bool exist=false;
char* name = playlist_get_name(NULL, global_temp_buffer,
sizeof(global_temp_buffer));
- if (generate_bookmark_file_name(name))
+ if (generate_bookmark_file_name(bm_filename, sizeof(bm_filename), name, -1))
{
- exist = file_exists(global_bookmark_file_name);
+ exist = file_exists(bm_filename);
}
return exist;
}
@@ -1180,4 +1311,3 @@ bool bookmark_is_bookmarkable_state(void)
return true;
}
-
diff --git a/apps/bookmark.h b/apps/bookmark.h
index ff7b87c1bf..192e577ce6 100644
--- a/apps/bookmark.h
+++ b/apps/bookmark.h
@@ -29,11 +29,17 @@ enum {
BOOKMARK_USB_CONNECTED = 1
};
+enum {
+ BOOKMARK_CANCEL,
+ BOOKMARK_DONT_RESUME,
+ BOOKMARK_DO_RESUME
+};
+
int bookmark_load_menu(void);
bool bookmark_autobookmark(bool prompt_ok);
bool bookmark_create_menu(void);
bool bookmark_mrb_load(void);
-bool bookmark_autoload(const char* file);
+int bookmark_autoload(const char* file);
bool bookmark_load(const char* file, bool autoload);
bool bookmark_exists(void);
bool bookmark_is_bookmarkable_state(void);
diff --git a/apps/buffering.c b/apps/buffering.c
index 8661a42ab8..9743c9c319 100644
--- a/apps/buffering.c
+++ b/apps/buffering.c
@@ -20,7 +20,6 @@
****************************************************************************/
#include "config.h"
#include <string.h>
-#include "strlcpy.h"
#include "system.h"
#include "storage.h"
#include "thread.h"
diff --git a/apps/core_keymap.c b/apps/core_keymap.c
index dbe7ae0072..9d54fcffac 100644
--- a/apps/core_keymap.c
+++ b/apps/core_keymap.c
@@ -27,119 +27,67 @@
#include "logf.h"
#if !defined(__PCTOOL__) || defined(CHECKWPS)
-static int keymap_handle = -1;
-
-static int core_alloc_keymap(size_t bufsz)
+int core_set_keyremap(struct button_mapping* core_keymap, int count)
{
- keymap_handle = core_alloc_ex("key remap", bufsz, &buflib_ops_locked);
- return keymap_handle;
+ return action_set_keymap(core_keymap, count);
}
-static void core_free_keymap(void)
+static int open_key_remap(const char *filename, int *countp)
{
- action_set_keymap(NULL, -1);
- if (keymap_handle > 0) /* free old buffer */
+ int fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ size_t fsize = filesize(fd);
+ int count = fsize / sizeof(struct button_mapping);
+ if (count == 0 || (size_t)(count * sizeof(struct button_mapping)) != fsize)
{
- keymap_handle = core_free(keymap_handle);
+ logf("core_keyremap: bad filesize %d / %lu", count, (unsigned long)fsize);
+ goto error;
}
-}
-
-/* Allocates buffer from core and copies keymap into it */
-int core_set_keyremap(struct button_mapping* core_keymap, int count)
-{
- core_free_keymap();
- if (count > 0)
+ struct button_mapping header;
+ if(read(fd, &header, sizeof(header)) != (ssize_t)sizeof(header))
{
- size_t bufsize = count * sizeof(struct button_mapping);
- if (core_keymap != NULL && core_alloc_keymap(bufsize) > 0)
- {
- char *buf = core_get_data(keymap_handle);
- memcpy(buf, core_keymap, bufsize);
- count = action_set_keymap((struct button_mapping *) buf, count);
- }
- else
- count = -1;
+ logf("core_keyremap: read error");
+ goto error;
}
- return count;
-}
-
-int core_load_key_remap(const char *filename)
-{
- char *buf;
- int fd = -1;
- int count = 0;
- size_t fsize = 0;
- core_free_keymap();
- if (filename != NULL)
- count = open_key_remap(filename, &fd, &fsize);
- while (count > 0)
+ if (header.action_code != KEYREMAP_VERSION ||
+ header.button_code != KEYREMAP_HEADERID ||
+ header.pre_button_code != count)
{
- if (core_alloc_keymap(fsize) <= 0)
- {
- count = -30;
- logf("core_keymap: %d Failed to allocate buffer", count);
- break;
- }
- buf = core_get_data(keymap_handle);
- if (read(fd, buf, fsize) == (ssize_t) fsize)
- {
- count = action_set_keymap((struct button_mapping *) buf, count);
- }
- else
- {
- count = -40;
- logf("core_keymap: %d Failed to read", count);
- }
- break;
+ logf("core_keyremap: bad header %d", count);
+ goto error;
}
+
+ *countp = count - 1;
+ return fd;
+
+ error:
close(fd);
- return count;
+ return -1;
}
-int open_key_remap(const char *filename, int *fd, size_t *fsize)
+int core_load_key_remap(const char *filename)
{
- int count = 0;
+ int count = 0; /* gcc falsely believes this may be used uninitialized */
+ int fd = open_key_remap(filename, &count);
+ if (fd < 0)
+ return -1;
- while (filename && fd && fsize)
+ size_t bufsize = count * sizeof(struct button_mapping);
+ int handle = core_alloc("keyremap", bufsize);
+ if (handle > 0)
{
- *fsize = 0;
- *fd = open(filename, O_RDONLY);
- if (*fd)
- {
- *fsize = filesize(*fd);
-
- count = *fsize / sizeof(struct button_mapping);
-
- if (count * sizeof(struct button_mapping) != *fsize)
- {
- count = -10;
- logf("core_keymap: %d Size mismatch", count);
- break;
- }
+ core_pin(handle);
+ if (read(fd, core_get_data(handle), bufsize) == (ssize_t)bufsize)
+ count = action_set_keymap_handle(handle, count);
- if (count > 1)
- {
- struct button_mapping header = {0};
- read(*fd, &header, sizeof(struct button_mapping));
- if (KEYREMAP_VERSION == header.action_code &&
- KEYREMAP_HEADERID == header.button_code &&
- header.pre_button_code == count)
- {
- count--;
- *fsize -= sizeof(struct button_mapping);
- }
- else /* Header mismatch */
- {
- count = -20;
- logf("core_keymap: %d Header mismatch", count);
- break;
- }
- }
- }
- break;
+ core_unpin(handle);
}
+
+ close(fd);
return count;
}
diff --git a/apps/core_keymap.h b/apps/core_keymap.h
index dad9875364..2077daa685 100644
--- a/apps/core_keymap.h
+++ b/apps/core_keymap.h
@@ -34,13 +34,6 @@
/* Allocates core buffer, copies keymap to allow buttons for actions to be remapped*/
int core_set_keyremap(struct button_mapping* core_keymap, int count);
-/* open_key_remap(filename , *fd (you must close file_descriptor), *fsize)
- * checks/strips header and returns remaining count
- * fd is opened and set to first record
- * filesize contains the size of the remaining records
-*/
-int open_key_remap(const char *filename, int *fd, size_t *filesize);
-
/* load a remap file to allow buttons for actions to be remapped */
int core_load_key_remap(const char *filename);
diff --git a/apps/cuesheet.c b/apps/cuesheet.c
index 98040f9992..263fed154d 100644
--- a/apps/cuesheet.c
+++ b/apps/cuesheet.c
@@ -58,28 +58,28 @@ static bool search_for_cuesheet(const char *path, struct cuesheet_file *cue_file
slash_cuepath = &cuepath[slash - path];
dot = strrchr(slash_cuepath, '.');
if (dot)
- strlcpy(dot, ".cue", MAX_PATH - (dot-cuepath));
+ strmemccpy(dot, ".cue", MAX_PATH - (dot-cuepath));
if (!dot || !file_exists(cuepath))
{
strcpy(cuepath, CUE_DIR);
if (strlcat(cuepath, slash, MAX_PATH) >= MAX_PATH)
goto skip; /* overflow */
- char *dot = strrchr(cuepath, '.');
+ dot = strrchr(cuepath, '.');
strcpy(dot, ".cue");
if (!file_exists(cuepath))
{
skip:
if ((len+4) >= MAX_PATH)
return false;
- strlcpy(cuepath, path, MAX_PATH);
+ strmemccpy(cuepath, path, MAX_PATH);
strlcat(cuepath, ".cue", MAX_PATH);
if (!file_exists(cuepath))
return false;
}
}
- strlcpy(cue_file->path, cuepath, MAX_PATH);
+ strmemccpy(cue_file->path, cuepath, MAX_PATH);
return true;
}
@@ -91,7 +91,7 @@ bool look_for_cuesheet_file(struct mp3entry *track_id3, struct cuesheet_file *cu
cue_file->pos = track_id3->embedded_cuesheet.pos;
cue_file->size = track_id3->embedded_cuesheet.size;
cue_file->encoding = track_id3->embedded_cuesheet.encoding;
- strlcpy(cue_file->path, track_id3->path, MAX_PATH);
+ strmemccpy(cue_file->path, track_id3->path, MAX_PATH);
return true;
}
@@ -139,7 +139,7 @@ static unsigned long parse_cue_index(const char *line)
while (isdigit(*line))
{
value = 10 * value + (*line - '0');
- if (value > field_max[field]) /* Sanity check bail early */
+ if (field >= 0 && value > field_max[field]) /* Sanity check bail early */
return 0;
line++;
}
@@ -363,7 +363,7 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
}
else
{
- strlcpy(dest, string, count);
+ strmemccpy(dest, string, count);
}
}
}
@@ -386,7 +386,7 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
strcpy(cue->file, cue->path);
char *slash = strrchr(cue->file, '/');
if (!slash++) slash = cue->file;
- strlcpy(slash, line, MAX_PATH - (slash - cue->file));
+ strmemccpy(slash, line, MAX_PATH - (slash - cue->file));
}
/* If some songs don't have performer info, we copy the cuesheet performer */
@@ -394,10 +394,10 @@ bool parse_cuesheet(struct cuesheet_file *cue_file, struct cuesheet *cue)
for (i = 0; i < cue->track_count; i++)
{
if (*(cue->tracks[i].performer) == '\0')
- strlcpy(cue->tracks[i].performer, cue->performer, MAX_NAME*3);
+ strmemccpy(cue->tracks[i].performer, cue->performer, MAX_NAME*3);
if (*(cue->tracks[i].songwriter) == '\0')
- strlcpy(cue->tracks[i].songwriter, cue->songwriter, MAX_NAME*3);
+ strmemccpy(cue->tracks[i].songwriter, cue->songwriter, MAX_NAME*3);
}
return true;
@@ -441,7 +441,7 @@ static const char* list_get_name_cb(int selected_item,
struct cuesheet *cue = (struct cuesheet *)data;
if (selected_item & 1)
- strlcpy(buffer, cue->tracks[selected_item/2].title, buffer_len);
+ strmemccpy(buffer, cue->tracks[selected_item/2].title, buffer_len);
else
snprintf(buffer, buffer_len, "%02d. %s", selected_item/2+1,
cue->tracks[selected_item/2].performer);
@@ -481,7 +481,7 @@ void browse_cuesheet(struct cuesheet *cue)
{
gui_synclist_draw(&lists);
action = get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (gui_synclist_do_button(&lists, &action, LIST_WRAP_UNLESS_HELD))
+ if (gui_synclist_do_button(&lists, &action))
continue;
switch (action)
{
@@ -508,7 +508,7 @@ void browse_cuesheet(struct cuesheet *cue)
/* check that this cue is the same one that would be found by
a search from playback */
char file[MAX_PATH];
- strlcpy(file, cue->file, MAX_PATH);
+ strmemccpy(file, cue->file, MAX_PATH);
if (!strcmp(cue->path, file) || /* if embedded */
(search_for_cuesheet(file, &cue_file) &&
@@ -535,7 +535,7 @@ bool display_cuesheet_content(char* filename)
if (!cue || bufsize < sizeof(struct cuesheet))
return false;
- strlcpy(cue_file.path, filename, MAX_PATH);
+ strmemccpy(cue_file.path, filename, MAX_PATH);
cue_file.pos = 0;
cue_file.size = 0;
@@ -565,12 +565,12 @@ bool curr_cuesheet_skip(struct cuesheet *cue, int direction, unsigned long curr_
if (!(direction <= 0 && track == 0))
{
/* If skipping forward, skip to next cuesheet segment. If skipping
- backward before DEFAULT_SKIP_TRESH milliseconds have elapsed, skip
+ backward before DEFAULT_SKIP_THRESH milliseconds have elapsed, skip
to previous cuesheet segment. If skipping backward after
- DEFAULT_SKIP_TRESH seconds have elapsed, skip to the start of the
+ DEFAULT_SKIP_THRESH seconds have elapsed, skip to the start of the
current cuesheet segment */
if (direction == 1 ||
- ((curr_pos - cue->tracks[track].offset) < DEFAULT_SKIP_TRESH))
+ ((curr_pos - cue->tracks[track].offset) < DEFAULT_SKIP_THRESH))
{
track += direction;
}
diff --git a/apps/debug_menu.c b/apps/debug_menu.c
index f510597ad2..71c0395e6e 100644
--- a/apps/debug_menu.c
+++ b/apps/debug_menu.c
@@ -23,7 +23,7 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
-#include <string.h>
+#include <string-extra.h>
#include "lcd.h"
#include "lang.h"
#include "menu.h"
@@ -227,7 +227,6 @@ static bool dbg_os(void)
simplelist_info_init(&info, IF_COP("Core and ") "Stack usage:",
MAXTHREADS IF_COP( + NUM_CORES ), &xoffset);
- info.hide_selection = true;
info.scroll_all = false;
info.action_callback = dbg_threads_action_callback;
info.get_name = threads_getname;
@@ -341,7 +340,6 @@ static bool dbg_cpuinfo(void)
info.get_name = get_cpuinfo;
info.action_callback = cpuinfo_cb;
info.timeout = HZ;
- info.hide_selection = true;
info.scroll_all = true;
return simplelist_show_list(&info);
}
@@ -522,8 +520,7 @@ static int bf_action_cb(int action, struct gui_synclist* list)
/* for some reason simplelist doesn't allow adding items here if
* info.get_name is given, so use normal list api */
gui_synclist_set_nb_items(list, core_get_num_blocks());
- if (handle > 0)
- core_free(handle);
+ core_free(handle);
}
action = ACTION_REDRAW;
}
@@ -568,7 +565,6 @@ static bool dbg_partitions(void)
struct simplelist_info info;
simplelist_info_init(&info, "Partition Info", NUM_DRIVES * 4, NULL);
info.selection_size = 2;
- info.hide_selection = true;
info.scroll_all = true;
info.get_name = dbg_partitions_getname;
return simplelist_show_list(&info);
@@ -870,7 +866,7 @@ static int tsc2100debug_action_callback(int action, struct gui_synclist *lists)
if (action == ACTION_STD_OK)
{
*page = (*page+1)%3;
- snprintf(lists->title, 32, "tsc2100 registers - Page %d", *page);
+ snprintf((char*)lists->title, 32, "tsc2100 registers - Page %d", *page);
return ACTION_REDRAW;
}
return action;
@@ -1247,7 +1243,8 @@ static int disk_callback(int btn, struct gui_synclist *lists)
int *cardnum = (int*)lists->data;
unsigned char card_name[6];
unsigned char pbuf[32];
- char *title = lists->title;
+ /* Casting away const is safe; the buffer is defined as non-const. */
+ char *title = (char *)lists->title;
static const unsigned char i_vmin[] = { 0, 1, 5, 10, 25, 35, 60, 100 };
static const unsigned char i_vmax[] = { 1, 5, 10, 25, 35, 45, 80, 200 };
static const unsigned char * const kbit_units[] = { "kBit/s", "MBit/s", "GBit/s" };
@@ -1277,7 +1274,7 @@ static int disk_callback(int btn, struct gui_synclist *lists)
{
card_name[i] = card_extract_bits(card->cid, (103-8*i), 8);
}
- strlcpy(card_name, card_name, sizeof(card_name));
+ strmemccpy(card_name, card_name, sizeof(card_name));
simplelist_addline(
"%s Rev %d.%d", card_name,
(int) card_extract_bits(card->cid, 63, 4),
@@ -1630,7 +1627,8 @@ static int ata_smart_attr_to_string(
if (len >= name_sz) len = name_sz-1;
slen += len;
}
- snprintf(str+slen, size-slen, "%s", buf);
+
+ strmemccpy(str+slen, buf, size-slen);
}
return 1; /* ok */
@@ -1721,7 +1719,6 @@ static bool dbg_ata_smart(void)
struct simplelist_info info;
simplelist_info_init(&info, "S.M.A.R.T. Data [CONTEXT to dump]", 1, NULL);
info.action_callback = ata_smart_callback;
- info.hide_selection = true;
info.scroll_all = true;
return simplelist_show_list(&info);
}
@@ -1775,7 +1772,6 @@ static bool dbg_disk_info(void)
info.title = title;
#endif
info.action_callback = disk_callback;
- info.hide_selection = true;
info.scroll_all = true;
return simplelist_show_list(&info);
}
@@ -1844,7 +1840,6 @@ static bool dbg_dircache_info(void)
int syncbuild = 0;
simplelist_info_init(&info, "Dircache Info", 8, &syncbuild);
info.action_callback = dircache_callback;
- info.hide_selection = true;
info.scroll_all = true;
return simplelist_show_list(&info);
}
@@ -1902,7 +1897,6 @@ static bool dbg_tagcache_info(void)
struct simplelist_info info;
simplelist_info_init(&info, "Database Info", 8, NULL);
info.action_callback = database_callback;
- info.hide_selection = true;
info.scroll_all = true;
/* Don't do nonblock here, must give enough processing time
@@ -2175,7 +2169,6 @@ static bool dbg_fm_radio(void)
radio_hardware_present() ? "yes" : "no");
info.action_callback = radio_hardware_present()?radio_callback : NULL;
- info.hide_selection = true;
return simplelist_show_list(&info);
}
#endif /* CONFIG_TUNER */
@@ -2304,11 +2297,6 @@ static bool cpu_boost_log(void)
static bool cpu_boost_log_dump(void)
{
int fd;
-#if CONFIG_RTC
- struct tm *nowtm;
- char fname[MAX_PATH];
-#endif
-
int count = cpu_boost_log_getcount();
char *str = cpu_boost_log_getlog_first();
@@ -2319,11 +2307,12 @@ static bool cpu_boost_log_dump(void)
return false;
#if CONFIG_RTC
- nowtm = get_time();
- snprintf(fname, MAX_PATH, "%s/boostlog_%04d%02d%02d%02d%02d%02d.txt", ROCKBOX_DIR,
- nowtm->tm_year + 1900, nowtm->tm_mon + 1, nowtm->tm_mday,
- nowtm->tm_hour, nowtm->tm_min, nowtm->tm_sec);
- fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC);
+ char fname[MAX_PATH];
+ struct tm *nowtm = get_time();
+ fd = open_pathfmt(fname, sizeof(fname), O_CREAT|O_WRONLY|O_TRUNC,
+ "%s/boostlog_%04d%02d%02d%02d%02d%02d.txt", ROCKBOX_DIR,
+ nowtm->tm_year + 1900, nowtm->tm_mon + 1, nowtm->tm_mday,
+ nowtm->tm_hour, nowtm->tm_min, nowtm->tm_sec);
#else
fd = open(ROCKBOX_DIR "/boostlog.txt", O_CREAT|O_WRONLY|O_TRUNC, 0666);
#endif
@@ -2447,7 +2436,6 @@ static bool dbg_talk(void)
else
simplelist_info_init(&list, "Voice Information:", 2, &data);
list.scroll_all = true;
- list.hide_selection = true;
list.timeout = HZ;
list.get_name = dbg_talk_get_name;
@@ -2487,7 +2475,6 @@ static bool dbg_isp1583(void)
isp1583.scroll_all = true;
simplelist_info_init(&isp1583, "ISP1583", dbg_usb_num_items(), NULL);
isp1583.timeout = HZ/100;
- isp1583.hide_selection = true;
isp1583.get_name = dbg_usb_item;
isp1583.action_callback = isp1583_action_callback;
return simplelist_show_list(&isp1583);
@@ -2513,67 +2500,12 @@ static bool dbg_pic(void)
pic.scroll_all = true;
simplelist_info_init(&pic, "PIC", pic_dbg_num_items(), NULL);
pic.timeout = HZ/100;
- pic.hide_selection = true;
pic.get_name = pic_dbg_item;
pic.action_callback = pic_action_callback;
return simplelist_show_list(&pic);
}
#endif
-static bool dbg_skin_engine(void)
-{
- struct simplelist_info info;
- int i, total = 0;
-#if defined(HAVE_BACKDROP_IMAGE)
- int ref_count;
- char *path;
- size_t bytes;
- int path_prefix_len = strlen(ROCKBOX_DIR "/wps/");
-#endif
- simplelist_info_init(&info, "Skin engine usage", 0, NULL);
- simplelist_set_line_count(0);
- info.hide_selection = true;
- FOR_NB_SCREENS(j) {
-#if NB_SCREENS > 1
- simplelist_addline("%s display:",
- j == 0 ? "Main" : "Remote");
-#endif
- for (i = 0; i < skin_get_num_skins(); i++) {
- struct skin_stats *stats = skin_get_stats(i, j);
- if (stats->buflib_handles)
- {
- simplelist_addline("Skin ID: %d, %d allocations",
- i, stats->buflib_handles);
- simplelist_addline("\tskin: %d bytes",
- stats->tree_size);
- simplelist_addline("\tImages: %d bytes",
- stats->images_size);
- simplelist_addline("\tTotal: %d bytes",
- stats->tree_size + stats->images_size);
- total += stats->tree_size + stats->images_size;
- }
- }
- }
- simplelist_addline("Skin total usage: %d bytes", total);
-#if defined(HAVE_BACKDROP_IMAGE)
- simplelist_addline("Backdrop Images:");
- i = 0;
- while (skin_backdrop_get_debug(i++, &path, &ref_count, &bytes)) {
- if (ref_count > 0) {
-
- if (!strncasecmp(path, ROCKBOX_DIR "/wps/", path_prefix_len))
- path += path_prefix_len;
- simplelist_addline("%s", path);
- simplelist_addline("\tref_count: %d", ref_count);
- simplelist_addline("\tsize: %d", bytes);
- total += bytes;
- }
- }
- simplelist_addline("Total usage: %d bytes", total);
-#endif
- return simplelist_show_list(&info);
-}
-
#if defined(HAVE_BOOTDATA) && !defined(SIMULATOR)
static bool dbg_boot_data(void)
{
@@ -2611,7 +2543,6 @@ static bool dbg_boot_data(void)
boot_data.payload[i+1], boot_data.payload[i+2], boot_data.payload[i+3]);
}
- info.hide_selection = true;
return simplelist_show_list(&info);
}
#endif /* defined(HAVE_BOOTDATA) && !defined(SIMULATOR) */
diff --git a/apps/filetree.c b/apps/filetree.c
index 1944713d13..107163add5 100644
--- a/apps/filetree.c
+++ b/apps/filetree.c
@@ -51,7 +51,23 @@
#endif
#include "wps.h"
-static int compare_sort_dir; /* qsort key for sorting directories */
+static struct compare_data
+{
+ int sort_dir; /* qsort key for sorting directories */
+ int(*_compar)(const char*, const char*, size_t);
+} cmp_data;
+
+/* dummmy functions to allow compatibility with strncmp & strncasecmp */
+static int strnatcmp_n(const char *a, const char *b, size_t n)
+{
+ (void)n;
+ return strnatcmp(a, b);
+}
+static int strnatcasecmp_n(const char *a, const char *b, size_t n)
+{
+ (void)n;
+ return strnatcasecmp(a, b);
+}
int ft_build_playlist(struct tree_context* c, int start_index)
{
@@ -89,7 +105,8 @@ int ft_build_playlist(struct tree_context* c, int start_index)
* avoid allocating yet another path buffer on the stack (and save some
* code; the caller typically needs to create the full pathname anyway)...
*/
-bool ft_play_playlist(char* pathname, char* dirname, char* filename, bool skip_dyn_warning)
+bool ft_play_playlist(char* pathname, char* dirname,
+ char* filename, bool skip_warn_and_bookmarks)
{
if (global_settings.party_mode && audio_status())
{
@@ -97,22 +114,15 @@ bool ft_play_playlist(char* pathname, char* dirname, char* filename, bool skip_d
return false;
}
- if (bookmark_autoload(pathname))
+ if (!skip_warn_and_bookmarks)
{
- return false;
+ int res = bookmark_autoload(pathname);
+ if (res == BOOKMARK_CANCEL || res == BOOKMARK_DO_RESUME || !warn_on_pl_erase())
+ return false;
}
splash(0, ID2P(LANG_WAIT));
- /* about to create a new current playlist...
- * allow user to cancel the operation.
- * Do not show if skip_dyn_warning is true */
- if (!skip_dyn_warning)
- {
- if (!warn_on_pl_erase())
- return false;
- }
-
if (playlist_create(dirname, filename) != -1)
{
if (global_settings.playlist_shuffle)
@@ -198,7 +208,7 @@ static int compare(const void* p1, const void* p2)
if (e1->attr & ATTR_DIRECTORY && e2->attr & ATTR_DIRECTORY)
{ /* two directories */
- criteria = compare_sort_dir;
+ criteria = cmp_data.sort_dir;
#ifdef HAVE_MULTIVOLUME
if (e1->attr & ATTR_VOLUME || e2->attr & ATTR_VOLUME)
@@ -233,41 +243,23 @@ static int compare(const void* p1, const void* p2)
if (t1 != t2) /* if different */
return (t1 - t2) * (criteria == SORT_TYPE_REVERSED ? -1 : 1);
- /* else fall through to alphabetical sorting */
+ /* else alphabetical sorting */
+ return cmp_data._compar(e1->name, e2->name, MAX_PATH);
}
case SORT_DATE:
case SORT_DATE_REVERSED:
- /* Ignore SORT_TYPE */
- if (criteria == SORT_DATE || criteria == SORT_DATE_REVERSED)
- {
- if (e1->time_write != e2->time_write)
- return (e1->time_write - e2->time_write)
- * (criteria == SORT_DATE_REVERSED ? -1 : 1);
- /* else fall through to alphabetical sorting */
- }
-
+ {
+ if (e1->time_write != e2->time_write)
+ return (e1->time_write - e2->time_write)
+ * (criteria == SORT_DATE_REVERSED ? -1 : 1);
+ /* else fall through to alphabetical sorting */
+ }
case SORT_ALPHA:
case SORT_ALPHA_REVERSED:
{
- if (global_settings.sort_case)
- {
- if (global_settings.interpret_numbers == SORT_INTERPRET_AS_NUMBER)
- return strnatcmp(e1->name, e2->name)
- * (criteria == SORT_ALPHA_REVERSED ? -1 : 1);
- else
- return strncmp(e1->name, e2->name, MAX_PATH)
- * (criteria == SORT_ALPHA_REVERSED ? -1 : 1);
- }
- else
- {
- if (global_settings.interpret_numbers == SORT_INTERPRET_AS_NUMBER)
- return strnatcasecmp(e1->name, e2->name)
- * (criteria == SORT_ALPHA_REVERSED ? -1 : 1);
- else
- return strncasecmp(e1->name, e2->name, MAX_PATH)
- * (criteria == SORT_ALPHA_REVERSED ? -1 : 1);
- }
+ return cmp_data._compar(e1->name, e2->name, MAX_PATH) *
+ (criteria == SORT_ALPHA_REVERSED ? -1 : 1);
}
}
@@ -392,7 +384,22 @@ int ft_load(struct tree_context* c, const char* tempdir)
c->dirlength = files_in_dir;
closedir(dir);
- compare_sort_dir = c->sort_dir;
+ cmp_data.sort_dir = c->sort_dir;
+ if (global_settings.sort_case)
+ {
+ if (global_settings.interpret_numbers == SORT_INTERPRET_AS_NUMBER)
+ cmp_data._compar = strnatcmp_n;
+ else
+ cmp_data._compar = strncmp;
+ }
+ else
+ {
+ if (global_settings.interpret_numbers == SORT_INTERPRET_AS_NUMBER)
+ cmp_data._compar = strnatcasecmp_n;
+ else
+ cmp_data._compar = strncasecmp;
+ }
+
qsort(tree_get_entries(c), files_in_dir, sizeof(struct entry), compare);
/* If thumbnail talking is enabled, make an extra run to mark files with
@@ -488,7 +495,9 @@ int ft_enter(struct tree_context* c)
break;
case FILE_ATTR_AUDIO:
- if (bookmark_autoload(c->currdir))
+ {
+ int res = bookmark_autoload(c->currdir);
+ if (res == BOOKMARK_CANCEL || res == BOOKMARK_DO_RESUME)
break;
splash(0, ID2P(LANG_WAIT));
@@ -522,7 +531,7 @@ int ft_enter(struct tree_context* c)
play = true;
}
break;
-
+ }
#if CONFIG_TUNER
/* fmr preset file */
case FILE_ATTR_FMR:
@@ -674,7 +683,7 @@ int ft_enter(struct tree_context* c)
break;
}
- struct entry* file = tree_get_entry_at(c, c->selected_item);
+ file = tree_get_entry_at(c, c->selected_item);
if (!file)
{
splashf(HZ, str(LANG_READ_FAILED), str(LANG_UNKNOWN));
diff --git a/apps/filetree.h b/apps/filetree.h
index 178ba0e973..7931c3c454 100644
--- a/apps/filetree.h
+++ b/apps/filetree.h
@@ -26,6 +26,7 @@ int ft_load(struct tree_context* c, const char* tempdir);
int ft_enter(struct tree_context* c);
int ft_exit(struct tree_context* c);
int ft_build_playlist(struct tree_context* c, int start_index);
-bool ft_play_playlist(char* pathname, char* dirname, char* filename, bool skip_dyn_warning);
+bool ft_play_playlist(char* pathname, char* dirname,
+ char* filename, bool skip_warn_and_bookmarks);
#endif
diff --git a/apps/filetypes.c b/apps/filetypes.c
index 02d2af282e..ec9bd1a7ae 100644
--- a/apps/filetypes.c
+++ b/apps/filetypes.c
@@ -38,6 +38,7 @@
#include "splash.h"
#include "core_alloc.h"
#include "icons.h"
+/*#define LOGF_ENABLE*/
#include "logf.h"
/* max filetypes (plugins & icons stored here) */
@@ -47,103 +48,142 @@
/* a table for the known file types */
static const struct filetype inbuilt_filetypes[] = {
- { "mp3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mp2", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mpa", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mp1", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "ogg", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "oga", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "wma", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "wmv", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "asf", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "wav", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "flac",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "ac3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "a52", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mpc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "wv", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "m4a", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "m4b", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mp4", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mod", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mpga", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "shn", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "aif", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "aiff",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "spx" ,FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "opus",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "sid", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "adx", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "nsf", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "nsfe",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "spc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "ape", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mac", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "sap" ,FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "rm", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "ra", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "rmvb",FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "cmc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "cm3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "cmr", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "cms", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "dmc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "dlt", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mpt", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mpd", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "rmt", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "tmc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "tm8", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "tm2", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "oma", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "aa3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "at3", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "mmf", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "au", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "snd", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "vox", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "w64", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "tta", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "ay", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "vtx", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "gbs", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "hes", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "sgc", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "vgm", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "vgz", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "kss", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "aac", FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
- { "m3u", FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST },
- { "m3u8",FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST },
- { "cfg", FILE_ATTR_CFG, Icon_Config, VOICE_EXT_CFG },
- { "wps", FILE_ATTR_WPS, Icon_Wps, VOICE_EXT_WPS },
+ { "mp3", FILE_ATTR_AUDIO },
+ { "mp2", FILE_ATTR_AUDIO },
+ { "mpa", FILE_ATTR_AUDIO },
+ { "mp1", FILE_ATTR_AUDIO },
+ { "ogg", FILE_ATTR_AUDIO },
+ { "oga", FILE_ATTR_AUDIO },
+ { "wma", FILE_ATTR_AUDIO },
+ { "wmv", FILE_ATTR_AUDIO },
+ { "asf", FILE_ATTR_AUDIO },
+ { "wav", FILE_ATTR_AUDIO },
+ { "flac", FILE_ATTR_AUDIO },
+ { "ac3", FILE_ATTR_AUDIO },
+ { "a52", FILE_ATTR_AUDIO },
+ { "mpc", FILE_ATTR_AUDIO },
+ { "wv", FILE_ATTR_AUDIO },
+ { "m4a", FILE_ATTR_AUDIO },
+ { "m4b", FILE_ATTR_AUDIO },
+ { "mp4", FILE_ATTR_AUDIO },
+ { "mod", FILE_ATTR_AUDIO },
+ { "mpga", FILE_ATTR_AUDIO },
+ { "shn", FILE_ATTR_AUDIO },
+ { "aif", FILE_ATTR_AUDIO },
+ { "aiff", FILE_ATTR_AUDIO },
+ { "spx" , FILE_ATTR_AUDIO },
+ { "opus", FILE_ATTR_AUDIO },
+ { "sid", FILE_ATTR_AUDIO },
+ { "adx", FILE_ATTR_AUDIO },
+ { "nsf", FILE_ATTR_AUDIO },
+ { "nsfe", FILE_ATTR_AUDIO },
+ { "spc", FILE_ATTR_AUDIO },
+ { "ape", FILE_ATTR_AUDIO },
+ { "mac", FILE_ATTR_AUDIO },
+ { "sap" , FILE_ATTR_AUDIO },
+ { "rm", FILE_ATTR_AUDIO },
+ { "ra", FILE_ATTR_AUDIO },
+ { "rmvb", FILE_ATTR_AUDIO },
+ { "cmc", FILE_ATTR_AUDIO },
+ { "cm3", FILE_ATTR_AUDIO },
+ { "cmr", FILE_ATTR_AUDIO },
+ { "cms", FILE_ATTR_AUDIO },
+ { "dmc", FILE_ATTR_AUDIO },
+ { "dlt", FILE_ATTR_AUDIO },
+ { "mpt", FILE_ATTR_AUDIO },
+ { "mpd", FILE_ATTR_AUDIO },
+ { "rmt", FILE_ATTR_AUDIO },
+ { "tmc", FILE_ATTR_AUDIO },
+ { "tm8", FILE_ATTR_AUDIO },
+ { "tm2", FILE_ATTR_AUDIO },
+ { "oma", FILE_ATTR_AUDIO },
+ { "aa3", FILE_ATTR_AUDIO },
+ { "at3", FILE_ATTR_AUDIO },
+ { "mmf", FILE_ATTR_AUDIO },
+ { "au", FILE_ATTR_AUDIO },
+ { "snd", FILE_ATTR_AUDIO },
+ { "vox", FILE_ATTR_AUDIO },
+ { "w64", FILE_ATTR_AUDIO },
+ { "tta", FILE_ATTR_AUDIO },
+ { "ay", FILE_ATTR_AUDIO },
+ { "vtx", FILE_ATTR_AUDIO },
+ { "gbs", FILE_ATTR_AUDIO },
+ { "hes", FILE_ATTR_AUDIO },
+ { "sgc", FILE_ATTR_AUDIO },
+ { "vgm", FILE_ATTR_AUDIO },
+ { "vgz", FILE_ATTR_AUDIO },
+ { "kss", FILE_ATTR_AUDIO },
+ { "aac", FILE_ATTR_AUDIO },
+ { "m3u", FILE_ATTR_M3U },
+ { "m3u8", FILE_ATTR_M3U },
+ { "cfg", FILE_ATTR_CFG },
+ { "wps", FILE_ATTR_WPS },
#ifdef HAVE_REMOTE_LCD
- { "rwps",FILE_ATTR_RWPS, Icon_Wps, VOICE_EXT_RWPS },
+ { "rwps", FILE_ATTR_RWPS },
#endif
#if CONFIG_TUNER
- { "fmr", FILE_ATTR_FMR, Icon_Preset, LANG_FMR },
- { "fms", FILE_ATTR_FMS, Icon_Wps, VOICE_EXT_FMS },
+ { "fmr", FILE_ATTR_FMR },
+ { "fms", FILE_ATTR_FMS },
#endif
- { "lng", FILE_ATTR_LNG, Icon_Language, LANG_LANGUAGE },
- { "rock",FILE_ATTR_ROCK,Icon_Plugin, VOICE_EXT_ROCK },
- { "lua", FILE_ATTR_LUA, Icon_Plugin, VOICE_EXT_ROCK },
- { "opx", FILE_ATTR_OPX, Icon_Plugin, VOICE_EXT_ROCK },
- { "fnt", FILE_ATTR_FONT,Icon_Font, VOICE_EXT_FONT },
- { "kbd", FILE_ATTR_KBD, Icon_Keyboard, VOICE_EXT_KBD },
- { "bmark",FILE_ATTR_BMARK, Icon_Bookmark, VOICE_EXT_BMARK },
- { "cue", FILE_ATTR_CUE, Icon_Bookmark, VOICE_EXT_CUESHEET },
- { "sbs", FILE_ATTR_SBS, Icon_Wps, VOICE_EXT_SBS },
+ { "lng", FILE_ATTR_LNG },
+ { "rock", FILE_ATTR_ROCK },
+ { "lua", FILE_ATTR_LUA },
+ { "opx", FILE_ATTR_OPX },
+ { "fnt", FILE_ATTR_FONT },
+ { "kbd", FILE_ATTR_KBD },
+ { "bmark",FILE_ATTR_BMARK },
+ { "cue", FILE_ATTR_CUE },
+ { "sbs", FILE_ATTR_SBS },
#ifdef HAVE_REMOTE_LCD
- { "rsbs", FILE_ATTR_RSBS, Icon_Wps, VOICE_EXT_RSBS },
+ { "rsbs", FILE_ATTR_RSBS },
#if CONFIG_TUNER
- { "rfms", FILE_ATTR_RFMS, Icon_Wps, VOICE_EXT_RFMS },
+ { "rfms", FILE_ATTR_RFMS },
#endif
#endif
#ifdef BOOTFILE_EXT
- { BOOTFILE_EXT, FILE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ },
+ { BOOTFILE_EXT, FILE_ATTR_MOD },
#endif
#ifdef BOOTFILE_EXT2
- { BOOTFILE_EXT2, FILE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ },
+ { BOOTFILE_EXT2, FILE_ATTR_MOD },
+#endif
+};
+
+struct fileattr_icon_voice {
+ int tree_attr;
+ uint16_t icon;
+ uint16_t voiceclip;
+};
+
+/* a table for the known file types icons & voice clips */
+static const struct fileattr_icon_voice inbuilt_attr_icons_voices[] = {
+ { FILE_ATTR_AUDIO, Icon_Audio, VOICE_EXT_MPA },
+ { FILE_ATTR_M3U, Icon_Playlist, LANG_PLAYLIST },
+ { FILE_ATTR_CFG, Icon_Config, VOICE_EXT_CFG },
+ { FILE_ATTR_WPS, Icon_Wps, VOICE_EXT_WPS },
+#ifdef HAVE_REMOTE_LCD
+ {FILE_ATTR_RWPS, Icon_Wps, VOICE_EXT_RWPS },
+#endif
+#if CONFIG_TUNER
+ { FILE_ATTR_FMR, Icon_Preset, LANG_FMR },
+ { FILE_ATTR_FMS, Icon_Wps, VOICE_EXT_FMS },
+#endif
+ { FILE_ATTR_LNG, Icon_Language, LANG_LANGUAGE },
+ { FILE_ATTR_ROCK, Icon_Plugin, VOICE_EXT_ROCK },
+ { FILE_ATTR_LUA, Icon_Plugin, VOICE_EXT_ROCK },
+ { FILE_ATTR_OPX, Icon_Plugin, VOICE_EXT_ROCK },
+ { FILE_ATTR_FONT, Icon_Font, VOICE_EXT_FONT },
+ { FILE_ATTR_KBD, Icon_Keyboard, VOICE_EXT_KBD },
+ { FILE_ATTR_BMARK, Icon_Bookmark, VOICE_EXT_BMARK },
+ { FILE_ATTR_CUE, Icon_Bookmark, VOICE_EXT_CUESHEET },
+ { FILE_ATTR_SBS, Icon_Wps, VOICE_EXT_SBS },
+#ifdef HAVE_REMOTE_LCD
+ { FILE_ATTR_RSBS, Icon_Wps, VOICE_EXT_RSBS },
+#if CONFIG_TUNER
+ { FILE_ATTR_RFMS, Icon_Wps, VOICE_EXT_RFMS },
+#endif
+#endif
+#if defined(BOOTFILE_EXT) || defined(BOOTFILE_EXT2)
+ { FILE_ATTR_MOD, Icon_Firmware, VOICE_EXT_AJZ },
#endif
};
@@ -153,6 +193,28 @@ void tree_get_filetypes(const struct filetype** types, int* count)
*count = sizeof(inbuilt_filetypes) / sizeof(*inbuilt_filetypes);
}
+long tree_get_filetype_voiceclip(int attr)
+{
+ if (global_settings.talk_filetype)
+ {
+ size_t count = ARRAY_SIZE(inbuilt_attr_icons_voices);
+ /* try to find a voice ID for the extension, if known */
+ attr &= FILE_ATTR_MASK; /* file type */
+
+ for (size_t i = count - 1; i < count; i--)
+ {
+ if (attr == inbuilt_attr_icons_voices[i].tree_attr)
+ {
+ logf("%s found attr %d id %d", __func__, attr,
+ inbuilt_attr_icons_voices[i].voiceclip);
+ return inbuilt_attr_icons_voices[i].voiceclip;
+ }
+ }
+ }
+ logf("%s not found attr %d", __func__, attr);
+ return -1;
+}
+
#define ROCK_EXTENSION "rock"
struct file_type {
@@ -262,9 +324,9 @@ void read_color_theme_file(void) {
unknown_file.color = -1;
if (!global_settings.colors_file[0] || global_settings.colors_file[0] == '-')
return;
- snprintf(buffer, MAX_PATH, THEME_DIR "/%s.colours",
- global_settings.colors_file);
- fd = open(buffer, O_RDONLY);
+
+ fd = open_pathfmt(buffer, sizeof(buffer), O_RDONLY,
+ THEME_DIR "/%s.colours", global_settings.colors_file);
if (fd < 0)
return;
while (read_line(fd, buffer, MAX_PATH) > 0)
@@ -303,11 +365,11 @@ void read_viewer_theme_file(void)
custom_filetype_icons[i] = filetypes[i].icon;
}
- snprintf(buffer, MAX_PATH, "%s/%s.icons", ICON_DIR,
- global_settings.viewers_icon_file);
- fd = open(buffer, O_RDONLY);
+ fd = open_pathfmt(buffer, sizeof(buffer), O_RDONLY,
+ ICON_DIR "/%s.icons", global_settings.viewers_icon_file);
if (fd < 0)
return;
+
while (read_line(fd, buffer, MAX_PATH) > 0)
{
if (!settings_parseline(buffer, &ext, &icon))
@@ -399,15 +461,28 @@ static void rm_whitespaces(char* str)
static void read_builtin_types(void)
{
- int count = sizeof(inbuilt_filetypes)/sizeof(*inbuilt_filetypes), i;
- for(i=0; i<count && (filetype_count < MAX_FILETYPES); i++)
+ int tree_attr;
+ size_t count = ARRAY_SIZE(inbuilt_filetypes);
+ size_t icon_count = ARRAY_SIZE(inbuilt_attr_icons_voices);
+ for(size_t i = 0; (i < count) && (filetype_count < MAX_FILETYPES); i++)
{
filetypes[filetype_count].extension = inbuilt_filetypes[i].extension;
filetypes[filetype_count].plugin = NULL;
- filetypes[filetype_count].attr = inbuilt_filetypes[i].tree_attr>>8;
+
+ tree_attr = inbuilt_filetypes[i].tree_attr;
+ filetypes[filetype_count].attr = tree_attr>>8;
if (filetypes[filetype_count].attr > highest_attr)
highest_attr = filetypes[filetype_count].attr;
- filetypes[filetype_count].icon = inbuilt_filetypes[i].icon;
+
+ filetypes[filetype_count].icon = unknown_file.icon;
+ for (size_t j = icon_count - 1; j < icon_count; j--)
+ {
+ if (tree_attr == inbuilt_attr_icons_voices[j].tree_attr)
+ {
+ filetypes[filetype_count].icon = inbuilt_attr_icons_voices[j].icon;
+ break;
+ }
+ }
filetype_count++;
}
}
@@ -618,7 +693,7 @@ int filetype_list_viewers(const char* current_file)
int i = viewers[info.selection];
snprintf(plugin, MAX_PATH, "%s/%s." ROCK_EXTENSION,
PLUGIN_DIR, filetypes[i].plugin);
- plugin_load(plugin, current_file);
+ ret = plugin_load(plugin, current_file);
}
return ret;
}
diff --git a/apps/filetypes.h b/apps/filetypes.h
index efe9f3f5df..9013f81b02 100644
--- a/apps/filetypes.h
+++ b/apps/filetypes.h
@@ -53,10 +53,10 @@
struct filetype {
char* extension;
int tree_attr;
- enum themable_icons icon;
- int voiceclip;
};
+
void tree_get_filetypes(const struct filetype**, int*) INIT_ATTR;
+long tree_get_filetype_voiceclip(int attr) INIT_ATTR;
/* init the filetypes structs.
uses audio buffer for storage, so call early in init... */
diff --git a/apps/gui/bitmap/list-skinned.c b/apps/gui/bitmap/list-skinned.c
index a67ac8cb0a..bebff821f8 100644
--- a/apps/gui/bitmap/list-skinned.c
+++ b/apps/gui/bitmap/list-skinned.c
@@ -213,8 +213,7 @@ bool skinlist_draw(struct screen *display, struct gui_synclist *list)
if (list_start_item+cur_line+1 > list->nb_items)
break;
current_drawing_line = list_start_item+cur_line;
- is_selected = list->show_selection_marker &&
- list_start_item+cur_line == list->selected_item;
+ is_selected = list_start_item+cur_line == list->selected_item;
for (viewport = SKINOFFSETTOPTR(get_skin_buffer(wps.data), listcfg[screen]->data->tree);
viewport;
diff --git a/apps/gui/bitmap/list.c b/apps/gui/bitmap/list.c
index 53874a8dfa..1c9b73a5fe 100644
--- a/apps/gui/bitmap/list.c
+++ b/apps/gui/bitmap/list.c
@@ -103,7 +103,7 @@ static void _default_listdraw_fn(struct list_putlineinfo_t *list_info)
bool show_cursor = list_info->show_cursor;
bool have_icons = list_info->have_icons;
struct line_desc *linedes = list_info->linedes;
- char *dsp_text = list_info->dsp_text;
+ const char *dsp_text = list_info->dsp_text;
if (is_title)
{
@@ -196,10 +196,7 @@ void list_draw(struct screen *display, struct gui_synclist *list)
const int list_start_item = list->start_item[screen];
const bool scrollbar_in_left = (list->scrollbar == SCROLLBAR_LEFT);
const bool scrollbar_in_right = (list->scrollbar == SCROLLBAR_RIGHT);
-
- const bool show_cursor = list->show_selection_marker &&
- (list->cursor_style == SYNCLIST_CURSOR_NOSTYLE);
-
+ const bool show_cursor = (list->cursor_style == SYNCLIST_CURSOR_NOSTYLE);
const bool have_icons = list->callback_get_item_icon && list->show_icons;
struct viewport *parent = (list->parent[screen]);
@@ -329,7 +326,6 @@ void list_draw(struct screen *display, struct gui_synclist *list)
unsigned const char *s;
char entry_buffer[MAX_PATH];
unsigned char *entry_name;
- const int text_pos = 0; /* UNUSED */
int line = i - start;
int line_indent = 0;
int style = STYLE_DEFAULT;
@@ -355,7 +351,7 @@ void list_draw(struct screen *display, struct gui_synclist *list)
/* position the string at the correct offset place */
int item_width,h;
display->getstringsize(entry_name, &item_width, &h);
- item_offset = gui_list_get_item_offset(list, item_width, text_pos,
+ item_offset = gui_list_get_item_offset(list, item_width, indent + (list->show_icons ? icon_w : 0),
display, list_text_vp);
/* draw the selected line */
@@ -365,8 +361,7 @@ void list_draw(struct screen *display, struct gui_synclist *list)
!hide_selection &&
#endif
i >= list->selected_item
- && i < list->selected_item + list->selected_size
- && list->show_selection_marker)
+ && i < list->selected_item + list->selected_size)
{/* The selected item must be displayed scrolling */
#ifdef HAVE_LCD_COLOR
if (list->selection_color)
diff --git a/apps/gui/color_picker.c b/apps/gui/color_picker.c
index a32f1ee179..ef17c0a230 100644
--- a/apps/gui/color_picker.c
+++ b/apps/gui/color_picker.c
@@ -154,7 +154,6 @@ static void draw_screen(struct screen *display, char *title,
{
unsigned text_color = LCD_BLACK;
unsigned background_color = LCD_WHITE;
- char buf[32];
int i, char_height, line_height;
int max_label_width;
int text_x, text_top;
@@ -253,17 +252,16 @@ static void draw_screen(struct screen *display, char *title,
set_drawinfo(display, mode, fg, bg);
/* Draw label */
- buf[0] = str(LANG_COLOR_RGB_LABELS)[i];
- buf[1] = '\0';
vp.flags &= ~VP_FLAG_ALIGNMENT_MASK;
- display->putsxy(text_x, text_top, buf);
+ display->putsxyf(text_x, text_top, "%c", str(LANG_COLOR_RGB_LABELS)[i]);
/* Draw color value */
+ vp.flags |= VP_FLAG_ALIGN_RIGHT;
if (display->depth >= 24)
- snprintf(buf, 4, "%03d", rgb->rgb_val[i] & 0xFF);
+ display->putsxyf(text_x, text_top, "%03d", rgb->rgb_val[i] & 0xFF);
else
- snprintf(buf, 3, "%02d", rgb->rgb_val[i] & 0x3F);
- vp.flags |= VP_FLAG_ALIGN_RIGHT;
- display->putsxy(text_x, text_top, buf);
+ display->putsxyf(text_x, text_top, "%02d", rgb->rgb_val[i] & 0x3F);
+
+
/* Draw scrollbar */
gui_scrollbar_draw(display, /* screen */
@@ -280,9 +278,6 @@ static void draw_screen(struct screen *display, char *title,
text_top += line_height;
} /* end for */
- /* Format RGB: #rrggbb */
- snprintf(buf, sizeof(buf), str(LANG_COLOR_RGB_VALUE),
- rgb->red, rgb->green, rgb->blue);
vp.flags |= VP_FLAG_ALIGN_CENTER;
if (display->depth >= 16)
{
@@ -301,8 +296,9 @@ static void draw_screen(struct screen *display, char *title,
/* Draw RGB: #rrggbb in middle of swatch */
set_drawinfo(display, DRMODE_FG, get_black_or_white(rgb),
background_color);
-
- display->putsxy(0, top + (height - char_height) / 2, buf);
+ /* Format RGB: #rrggbb */
+ display->putsxyf(0, top + (height - char_height) / 2,
+ str(LANG_COLOR_RGB_VALUE), rgb->red, rgb->green, rgb->blue);
/* Draw border around the rect */
set_drawinfo(display, DRMODE_SOLID, text_color, background_color);
@@ -318,7 +314,9 @@ static void draw_screen(struct screen *display, char *title,
if (height >= char_height)
{
set_drawinfo(display, DRMODE_SOLID, text_color, background_color);
- display->putsxy(0, top + (height - char_height) / 2, buf);
+ /* Format RGB: #rrggbb */
+ display->putsxyf(0, top + (height - char_height) / 2,
+ str(LANG_COLOR_RGB_VALUE), rgb->red, rgb->green, rgb->blue);
}
}
diff --git a/apps/gui/folder_select.c b/apps/gui/folder_select.c
index cef95e179e..a76d77562b 100644
--- a/apps/gui/folder_select.c
+++ b/apps/gui/folder_select.c
@@ -185,7 +185,7 @@ static struct folder* load_folder(struct folder* parent, char *folder)
if (len >= sizeof(fullpath))
goto fail;
}
- strlcpy(&fullpath[len], folder, sizeof(fullpath) - len);
+ strmemccpy(&fullpath[len], folder, sizeof(fullpath) - len);
logf("load_folder: [%s]", fullpath);
dir = opendir(fullpath);
@@ -208,7 +208,7 @@ static struct folder* load_folder(struct folder* parent, char *folder)
if ((dn[0] == '.') && (dn[1] == '\0' || (dn[1] == '.' && dn[2] == '\0')))
continue;
/* copy entry name to end of buffer, save pointer */
- int len = strlen((char *)entry->d_name);
+ len = strlen((char *)entry->d_name);
char *name = folder_alloc_from_end(len+1); /*for NULL*/
if (name == NULL)
{
@@ -518,7 +518,7 @@ static int select_paths(struct folder* root, const char* filenames)
lastfnp = fnp;
if (len <= 0 || len + 1 >= buflen)
continue;
- strlcpy(buf, sstr, len + 1);
+ strmemccpy(buf, sstr, len + 1);
struct child *item = find_from_filename(buf, root);
if (item)
item->state = SELECTED;
@@ -563,7 +563,7 @@ static void save_folders_r(struct folder *root, char* dst, size_t maxlen, size_t
int dlen = strlen(dst);
if (dlen + len >= maxlen)
continue;
- strlcpy(&dst[dlen], buffer_front, maxlen - dlen);
+ strmemccpy(&dst[dlen], buffer_front, maxlen - dlen);
}
else
{
diff --git a/apps/gui/icon.c b/apps/gui/icon.c
index 9fe7090f4a..9deb1a0c65 100644
--- a/apps/gui/icon.c
+++ b/apps/gui/icon.c
@@ -32,6 +32,7 @@
#include "bmp.h"
#include "filetypes.h"
#include "language.h"
+#include "misc.h"
#include "bitmaps/default_icons.h"
#if defined(HAVE_REMOTE_LCD) && (NB_SCREENS > 1)
@@ -63,7 +64,6 @@ static struct iconset {
struct bitmap bmp;
bool loaded;
int handle;
- int handle_locked;
} iconsets[Iconset_Count][NB_SCREENS];
#define ICON_HEIGHT(screen) (!iconsets[Iconset_user][screen].loaded ? \
@@ -160,8 +160,6 @@ static int buflib_move_callback(int handle, void* current, void* new)
struct iconset *set = &iconsets[i][j];
if (set->bmp.data == current)
{
- if (set->handle_locked > 0)
- return BUFLIB_CB_CANNOT_MOVE;
set->bmp.data = new;
return BUFLIB_CB_OK;
}
@@ -184,10 +182,9 @@ static void load_icons(const char* filename, enum Iconset iconset,
ic->handle = 0;
if (filename[0] && filename[0] != '-')
{
- char path[MAX_PATH];
-
- snprintf(path, sizeof(path), ICON_DIR "/%s.bmp", filename);
- fd = open(path, O_RDONLY);
+ char fname[MAX_PATH];
+ fd = open_pathfmt(fname, sizeof(fname), O_RDONLY,
+ ICON_DIR "/%s.bmp", filename);
if (fd < 0)
return;
buf_size = read_bmp_fd(fd, &ic->bmp, 0,
@@ -201,11 +198,10 @@ static void load_icons(const char* filename, enum Iconset iconset,
goto finished;
}
lseek(fd, 0, SEEK_SET);
+ core_pin(ic->handle);
ic->bmp.data = core_get_data(ic->handle);
-
- ic->handle_locked = 1;
size_read = read_bmp_fd(fd, &ic->bmp, buf_size, bmpformat, NULL);
- ic->handle_locked = 0;
+ core_unpin(ic->handle);
if (size_read < 0)
{
diff --git a/apps/gui/line.c b/apps/gui/line.c
index d319ff3c51..7e84aa7b31 100644
--- a/apps/gui/line.c
+++ b/apps/gui/line.c
@@ -264,8 +264,7 @@ next:
else
{
/* any other character here is an erroneous format string */
- snprintf(tempbuf, sizeof(tempbuf), "<E:%c>", ch);
- display->putsxy(xpos, y, tempbuf);
+ display->putsxyf(xpos, y, "<E:%c>", ch);
/* Don't consider going forward, fix the caller */
return;
}
diff --git a/apps/gui/list.c b/apps/gui/list.c
index 29c80574c2..d15948e97a 100644
--- a/apps/gui/list.c
+++ b/apps/gui/list.c
@@ -47,12 +47,6 @@
*/
#define FRAMEDROP_TRIGGER 6
-static int offset_step = 16; /* pixels per screen scroll step */
-/* should lines scroll out of the screen */
-static bool offset_out_of_view = false;
-
-static void gui_list_select_at_offset(struct gui_synclist * gui_list,
- int offset);
void list_draw(struct screen *display, struct gui_synclist *list);
static long last_dirty_tick;
@@ -182,7 +176,6 @@ void gui_synclist_init(struct gui_synclist * gui_list,
list_init_viewports(gui_list);
FOR_NB_SCREENS(i)
list_init_item_height(gui_list, i);
- gui_list->limit_scroll = false;
gui_list->data = data;
gui_list->scroll_all = scroll_all;
gui_list->selected_size = selected_size;
@@ -191,7 +184,6 @@ void gui_synclist_init(struct gui_synclist * gui_list,
gui_list->scheduled_talk_tick = gui_list->last_talked_tick = 0;
gui_list->dirty_tick = current_tick;
- gui_list->show_selection_marker = true;
#ifdef HAVE_LCD_COLOR
gui_list->title_color = -1;
@@ -200,13 +192,6 @@ void gui_synclist_init(struct gui_synclist * gui_list,
#endif
}
-/* this toggles the selection bar or cursor */
-void gui_synclist_hide_selection_marker(struct gui_synclist * lists, bool hide)
-{
- lists->show_selection_marker = !hide;
-}
-
-
int gui_list_get_item_offset(struct gui_synclist * gui_list,
int item_width,
int text_pos,
@@ -215,7 +200,7 @@ int gui_list_get_item_offset(struct gui_synclist * gui_list,
{
int item_offset;
- if (offset_out_of_view)
+ if (global_settings.offset_out_of_view)
{
item_offset = gui_list->offset_position[display->screen_type];
}
@@ -269,11 +254,7 @@ static void gui_list_put_selection_on_screen(struct gui_synclist * gui_list,
const int scroll_limit_up = (nb_lines < gui_list->selected_size+2 ? 0:1);
const int scroll_limit_down = (scroll_limit_up+gui_list->selected_size);
- if (gui_list->show_selection_marker == false)
- {
- new_start_item = gui_list->selected_item;
- }
- else if (gui_list->selected_size >= nb_lines)
+ if (gui_list->selected_size >= nb_lines)
{
new_start_item = gui_list->selected_item;
}
@@ -398,7 +379,7 @@ void gui_synclist_select_item(struct gui_synclist * gui_list, int item_number)
}
static void gui_list_select_at_offset(struct gui_synclist * gui_list,
- int offset)
+ int offset, bool allow_wrap)
{
int new_selection;
if (gui_list->selected_size > 1)
@@ -410,41 +391,15 @@ static void gui_list_select_at_offset(struct gui_synclist * gui_list,
if (new_selection >= gui_list->nb_items)
{
- new_selection = gui_list->limit_scroll ?
- gui_list->nb_items - gui_list->selected_size : 0;
- edge_beep(gui_list, !gui_list->limit_scroll);
+ new_selection = allow_wrap ? 0 : gui_list->nb_items - gui_list->selected_size;
+ edge_beep(gui_list, allow_wrap);
}
else if (new_selection < 0)
{
- new_selection = gui_list->limit_scroll ?
- 0 : gui_list->nb_items - gui_list->selected_size;
- edge_beep(gui_list, !gui_list->limit_scroll);
+ new_selection = allow_wrap ? gui_list->nb_items - gui_list->selected_size : 0;
+ edge_beep(gui_list, allow_wrap);
}
- else if (gui_list->show_selection_marker == false)
- {
- FOR_NB_SCREENS(i)
- {
- int nb_lines = list_get_nb_lines(gui_list, i);
- if (offset > 0)
- {
- int screen_top = MAX(0, gui_list->nb_items - nb_lines);
- gui_list->start_item[i] = MIN(screen_top, gui_list->start_item[i] +
- gui_list->selected_size);
- gui_list->selected_item = gui_list->start_item[i];
- }
- else
- {
- gui_list->start_item[i] = MAX(0, gui_list->start_item[i] -
- gui_list->selected_size);
- gui_list->selected_item = gui_list->start_item[i] + nb_lines;
- }
-#ifdef HAVE_TOUCHSCREEN
- gui_list->y_pos = gui_list->start_item[SCREEN_MAIN] * gui_list->line_height[SCREEN_MAIN];
-#endif
- }
- return;
- }
gui_synclist_select_item(gui_list, new_selection);
}
@@ -475,22 +430,12 @@ void gui_synclist_del_item(struct gui_synclist * gui_list)
}
}
-void gui_list_screen_scroll_step(int ofs)
-{
- offset_step = ofs;
-}
-
-void gui_list_screen_scroll_out_of_view(bool enable)
-{
- offset_out_of_view = enable;
-}
-
/*
* Set the title and title icon of the list. Setting title to NULL disables
* both the title and icon. Use NOICON if there is no icon.
*/
void gui_synclist_set_title(struct gui_synclist * gui_list,
- char * title, enum themable_icons icon)
+ const char * title, enum themable_icons icon)
{
gui_list->title = title;
gui_list->title_icon = icon;
@@ -558,26 +503,25 @@ void gui_synclist_set_sel_color(struct gui_synclist * lists,
#endif
static void gui_synclist_select_next_page(struct gui_synclist * lists,
- enum screen_type screen)
+ enum screen_type screen,
+ bool allow_wrap)
{
int nb_lines = list_get_nb_lines(lists, screen);
if (lists->selected_size > 1)
nb_lines = MAX(1, nb_lines/lists->selected_size);
- gui_list_select_at_offset(lists, nb_lines);
+
+ gui_list_select_at_offset(lists, nb_lines, allow_wrap);
}
static void gui_synclist_select_previous_page(struct gui_synclist * lists,
- enum screen_type screen)
+ enum screen_type screen,
+ bool allow_wrap)
{
int nb_lines = list_get_nb_lines(lists, screen);
if (lists->selected_size > 1)
nb_lines = MAX(1, nb_lines/lists->selected_size);
- gui_list_select_at_offset(lists, -nb_lines);
-}
-void gui_synclist_limit_scroll(struct gui_synclist * lists, bool scroll)
-{
- lists->limit_scroll = scroll;
+ gui_list_select_at_offset(lists, -nb_lines, allow_wrap);
}
/*
@@ -592,7 +536,7 @@ static void gui_synclist_scroll_right(struct gui_synclist * lists)
/* FIXME: This is a fake right boundry limiter. there should be some
* callback function to find the longest item on the list in pixels,
* to stop the list from scrolling past that point */
- lists->offset_position[i] += offset_step;
+ lists->offset_position[i] += global_settings.screen_scroll_step;
if (lists->offset_position[i] > 1000)
lists->offset_position[i] = 1000;
}
@@ -606,7 +550,7 @@ static void gui_synclist_scroll_left(struct gui_synclist * lists)
{
FOR_NB_SCREENS(i)
{
- lists->offset_position[i] -= offset_step;
+ lists->offset_position[i] -= global_settings.screen_scroll_step;
if (lists->offset_position[i] < 0)
lists->offset_position[i] = 0;
}
@@ -616,16 +560,22 @@ bool gui_synclist_keyclick_callback(int action, void* data)
{
struct gui_synclist *lists = (struct gui_synclist *)data;
- /* block the beep if we are at the end of the list and we are not wrapping.
- * CAVEAT: mosts lists don't set limit_scroll untill it sees a repeat
- * press at the end of the list so this can cause an extra beep.
- */
- if (lists->limit_scroll == false)
- return true;
+ /* Block the beep if we're at the end of the list and we're not wrapping. */
if (lists->selected_item == 0)
- return (action != ACTION_STD_PREV && action != ACTION_STD_PREVREPEAT);
+ {
+ if (action == ACTION_STD_PREVREPEAT)
+ return false;
+ if (action == ACTION_STD_PREV && !lists->wraparound)
+ return false;
+ }
+
if (lists->selected_item == lists->nb_items - lists->selected_size)
- return (action != ACTION_STD_NEXT && action != ACTION_STD_NEXTREPEAT);
+ {
+ if (action == ACTION_STD_NEXTREPEAT)
+ return false;
+ if (action == ACTION_STD_NEXT && !lists->wraparound)
+ return false;
+ }
return action != ACTION_NONE;
}
@@ -650,8 +600,7 @@ static void _lists_uiviewport_update_callback(unsigned short id, void *data)
gui_synclist_draw(current_lists);
}
-bool gui_synclist_do_button(struct gui_synclist * lists,
- int *actionptr, enum list_wrap wrap)
+bool gui_synclist_do_button(struct gui_synclist * lists, int *actionptr)
{
int action = *actionptr;
static bool pgleft_allow_cancel = false;
@@ -697,23 +646,14 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
/* Disable the skin redraw callback */
current_lists = NULL;
- switch (wrap)
- {
- case LIST_WRAP_ON:
- gui_synclist_limit_scroll(lists, !(lists->wraparound));
- break;
- case LIST_WRAP_OFF:
- gui_synclist_limit_scroll(lists, true);
- break;
- case LIST_WRAP_UNLESS_HELD:
- if (action == ACTION_STD_PREVREPEAT ||
- action == ACTION_STD_NEXTREPEAT ||
- action == ACTION_LISTTREE_PGUP ||
- action == ACTION_LISTTREE_PGDOWN)
- gui_synclist_limit_scroll(lists, true);
- else gui_synclist_limit_scroll(lists, !(lists->wraparound));
- break;
- };
+
+ /* Prevent list wraparound by repeating actions */
+ bool allow_wrap = lists->wraparound;
+ if (action == ACTION_STD_PREVREPEAT ||
+ action == ACTION_STD_NEXTREPEAT ||
+ action == ACTION_LISTTREE_PGUP ||
+ action == ACTION_LISTTREE_PGDOWN)
+ allow_wrap = false;
switch (action)
{
@@ -733,7 +673,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
#endif
case ACTION_STD_PREV:
case ACTION_STD_PREVREPEAT:
- gui_list_select_at_offset(lists, -next_item_modifier);
+ gui_list_select_at_offset(lists, -next_item_modifier, allow_wrap);
#ifndef HAVE_WHEEL_ACCELERATION
if (button_queue_count() < FRAMEDROP_TRIGGER)
#endif
@@ -744,7 +684,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
case ACTION_STD_NEXT:
case ACTION_STD_NEXTREPEAT:
- gui_list_select_at_offset(lists, next_item_modifier);
+ gui_list_select_at_offset(lists, next_item_modifier, allow_wrap);
#ifndef HAVE_WHEEL_ACCELERATION
if (button_queue_count() < FRAMEDROP_TRIGGER)
#endif
@@ -795,7 +735,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
SCREEN_REMOTE :
#endif
SCREEN_MAIN;
- gui_synclist_select_previous_page(lists, screen);
+ gui_synclist_select_previous_page(lists, screen, allow_wrap);
gui_synclist_draw(lists);
yield();
*actionptr = ACTION_STD_NEXT;
@@ -810,7 +750,7 @@ bool gui_synclist_do_button(struct gui_synclist * lists,
SCREEN_REMOTE :
#endif
SCREEN_MAIN;
- gui_synclist_select_next_page(lists, screen);
+ gui_synclist_select_next_page(lists, screen, allow_wrap);
gui_synclist_draw(lists);
yield();
*actionptr = ACTION_STD_PREV;
@@ -849,8 +789,7 @@ int list_do_action_timeout(struct gui_synclist *lists, int timeout)
}
bool list_do_action(int context, int timeout,
- struct gui_synclist *lists, int *action,
- enum list_wrap wrap)
+ struct gui_synclist *lists, int *action)
/* Combines the get_action() (with possibly overridden timeout) and
gui_synclist_do_button() calls. Returns the list action from
do_button, and places the action from get_action in *action. */
@@ -858,14 +797,7 @@ bool list_do_action(int context, int timeout,
timeout = list_do_action_timeout(lists, timeout);
keyclick_set_callback(gui_synclist_keyclick_callback, lists);
*action = get_action(context, timeout);
- return gui_synclist_do_button(lists, action, wrap);
-}
-
-bool gui_synclist_item_is_onscreen(struct gui_synclist *lists,
- enum screen_type screen, int item)
-{
- int nb_lines = list_get_nb_lines(lists, screen);
- return (unsigned)(item - lists->start_item[screen]) < (unsigned) nb_lines;
+ return gui_synclist_do_button(lists, action);
}
/* Simple use list implementation */
@@ -925,7 +857,6 @@ bool simplelist_show_list(struct simplelist_info *info)
struct gui_synclist lists;
int action, old_line_count = simplelist_line_count;
list_get_name *getname;
- int wrap = global_settings.list_wraparound ? LIST_WRAP_UNLESS_HELD : LIST_WRAP_OFF;
if (info->get_name)
getname = info->get_name;
else
@@ -950,12 +881,6 @@ bool simplelist_show_list(struct simplelist_info *info)
gui_synclist_set_sel_color(&lists, info->selection_color);
#endif
- if (info->hide_selection)
- {
- gui_synclist_hide_selection_marker(&lists, true);
- wrap = LIST_WRAP_OFF;
- }
-
if (info->action_callback)
info->action_callback(ACTION_REDRAW, &lists);
@@ -974,8 +899,7 @@ bool simplelist_show_list(struct simplelist_info *info)
while(1)
{
- list_do_action(CONTEXT_LIST, info->timeout,
- &lists, &action, wrap);
+ list_do_action(CONTEXT_LIST, info->timeout, &lists, &action);
/* We must yield in this case or no other thread can run */
if (info->timeout == TIMEOUT_NOBLOCK)
@@ -1039,7 +963,6 @@ void simplelist_info_init(struct simplelist_info *info, char* title,
info->title = title;
info->count = count;
info->selection_size = 1;
- info->hide_selection = false;
info->scroll_all = false;
info->hide_theme = false;
info->speak_onshow = true;
diff --git a/apps/gui/list.h b/apps/gui/list.h
index 4dc83a1b27..15ee1df736 100644
--- a/apps/gui/list.h
+++ b/apps/gui/list.h
@@ -30,12 +30,6 @@
#define SCROLLBAR_WIDTH global_settings.scrollbar_width
-enum list_wrap {
- LIST_WRAP_ON = 0,
- LIST_WRAP_OFF,
- LIST_WRAP_UNLESS_HELD,
-};
-
enum synclist_cursor
{
SYNCLIST_CURSOR_NOSTYLE = 0,
@@ -96,7 +90,7 @@ struct list_putlineinfo_t {
struct viewport *vp;
struct line_desc *linedes;
struct gui_synclist * list;
- char *dsp_text;
+ const char *dsp_text;
bool is_selected;
bool is_title;
@@ -155,13 +149,9 @@ struct gui_synclist
bool talk_menu;
bool wraparound;
bool scroll_paginated;
- /* defines wether the list should stop when reaching the top/bottom
- * or should continue (by going to bottom/top) */
- bool limit_scroll;
/* whether the text of the whole items of the list have to be
* scrolled or only for the selected item */
bool scroll_all;
- bool show_selection_marker; /* set to true by default */
int nb_items;
int selected_item;
@@ -185,7 +175,7 @@ struct gui_synclist
/* The data that will be passed to the callback function YOU implement */
void * data;
/* The optional title, set to NULL for none */
- char * title;
+ const char * title;
/* Optional title icon */
enum themable_icons title_icon;
@@ -199,11 +189,7 @@ struct gui_synclist
extern void list_init(void);
-/* parse global setting to static int */
-extern void gui_list_screen_scroll_step(int ofs);
-/* parse global setting to static bool */
-extern void gui_list_screen_scroll_out_of_view(bool enable);
extern void gui_synclist_init_display_settings(struct gui_synclist * list);
extern void gui_synclist_init(
struct gui_synclist * lists,
@@ -232,13 +218,8 @@ extern void gui_synclist_select_item(struct gui_synclist * lists,
int item_number);
extern void gui_synclist_add_item(struct gui_synclist * lists);
extern void gui_synclist_del_item(struct gui_synclist * lists);
-extern void gui_synclist_limit_scroll(struct gui_synclist * lists, bool scroll);
-extern void gui_synclist_set_title(struct gui_synclist * lists, char * title,
+extern void gui_synclist_set_title(struct gui_synclist * lists, const char * title,
enum themable_icons icon);
-extern void gui_synclist_hide_selection_marker(struct gui_synclist *lists,
- bool hide);
-extern bool gui_synclist_item_is_onscreen(struct gui_synclist *lists,
- enum screen_type screen, int item);
extern bool gui_synclist_keyclick_callback(int action, void* data);
/*
@@ -246,20 +227,9 @@ extern bool gui_synclist_keyclick_callback(int action, void* data);
* returns true if the action was handled.
* NOTE: *action may be changed regardless of return value
*/
-extern bool gui_synclist_do_button(struct gui_synclist * lists,
- int *action,
- enum list_wrap);
+extern bool gui_synclist_do_button(struct gui_synclist * lists, int *action);
#if !defined(PLUGIN)
-struct listitem_viewport_cfg {
- struct wps_data *data;
- OFFSETTYPE(char *) label;
- int width;
- int height;
- int xmargin;
- int ymargin;
- bool tile;
- struct skin_viewport selected_item_vp;
-};
+struct listitem_viewport_cfg;
bool skinlist_get_item(struct screen *display, struct gui_synclist *list, int x, int y, int *item);
bool skinlist_draw(struct screen *display, struct gui_synclist *list);
@@ -291,8 +261,7 @@ extern int list_do_action_timeout(struct gui_synclist *lists, int timeout);
list_do_action_timeout) with the gui_synclist_do_button call, for
convenience. */
extern bool list_do_action(int context, int timeout,
- struct gui_synclist *lists, int *action,
- enum list_wrap wrap);
+ struct gui_synclist *lists, int *action);
/** Simplelist implementation.
@@ -301,10 +270,9 @@ extern bool list_do_action(int context, int timeout,
**/
struct simplelist_info {
- char *title; /* title to show on the list */
+ const char *title; /* title to show on the list */
int count; /* number of items in the list, each item is selection_size high */
int selection_size; /* list selection size, usually 1 */
- bool hide_selection;
bool scroll_all;
bool hide_theme;
bool speak_onshow; /* list speaks first item or 'empty list' */
@@ -350,7 +318,6 @@ void simplelist_addline(const char *fmt, ...);
/* setup the info struct. members not setup in this function need to be assigned manually
members set in this function:
info.selection_size = 1;
- info.hide_selection = false;
info.scroll_all = false;
info.hide_theme = false;
info.speak_onshow = true;
diff --git a/apps/gui/option_select.c b/apps/gui/option_select.c
index 7068fee510..4687367fba 100644
--- a/apps/gui/option_select.c
+++ b/apps/gui/option_select.c
@@ -68,8 +68,8 @@ const char *option_get_valuestring(const struct settings_list *setting,
if ((setting->flags & F_BOOL_SETTING) == F_BOOL_SETTING)
{
bool val = (bool)temp_var;
- strlcpy(buffer, str(val? setting->bool_setting->lang_yes :
- setting->bool_setting->lang_no), buf_len);
+ strmemccpy(buffer, str(val? setting->bool_setting->lang_yes :
+ setting->bool_setting->lang_no), buf_len);
}
#if 0 /* probably dont need this one */
else if ((setting->flags & F_FILENAME) == F_FILENAME)
@@ -121,7 +121,7 @@ const char *option_get_valuestring(const struct settings_list *setting,
const struct choice_setting *info = setting->choice_setting;
if (info->talks[(int)temp_var] < LANG_LAST_INDEX_IN_ARRAY)
{
- strlcpy(buffer, str(info->talks[(int)temp_var]), buf_len);
+ strmemccpy(buffer, str(info->talks[(int)temp_var]), buf_len);
}
else
{
@@ -133,7 +133,7 @@ const char *option_get_valuestring(const struct settings_list *setting,
{
int value = (int)temp_var;
char *val = P2STR(setting->choice_setting->desc[value]);
- strlcpy(buffer, val, buf_len);
+ strmemccpy(buffer, val, buf_len);
}
}
return str;
@@ -506,15 +506,18 @@ bool option_screen(const struct settings_list *setting,
gui_synclist_set_nb_items(&lists, nb_items);
gui_synclist_select_item(&lists, selected);
- gui_synclist_limit_scroll(&lists, true);
gui_synclist_draw(&lists);
/* talk the item */
gui_synclist_speak_item(&lists);
while (!done)
{
+ /* override user wraparound setting; used mainly by EQ settings.
+ * Not sure this is justified? */
+ if (!allow_wrap)
+ lists.wraparound = false;
+
if (list_do_action(CONTEXT_LIST, HZ, /* HZ so the status bar redraws */
- &lists, &action,
- allow_wrap? LIST_WRAP_UNLESS_HELD: LIST_WRAP_OFF))
+ &lists, &action))
{
/* setting changed */
selected = gui_synclist_get_sel_pos(&lists);
diff --git a/apps/gui/quickscreen.c b/apps/gui/quickscreen.c
index e403e13e6c..141eb1a16b 100644
--- a/apps/gui/quickscreen.c
+++ b/apps/gui/quickscreen.c
@@ -320,13 +320,13 @@ static int quickscreen_touchscreen_button(void)
}
#endif
-static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter, bool *usb)
+static int gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_enter, bool *usb)
{
int button;
struct viewport parent[NB_SCREENS];
struct viewport vps[NB_SCREENS][QUICKSCREEN_ITEM_COUNT];
struct viewport vp_icons[NB_SCREENS];
- bool changed = false;
+ int ret = QUICKSCREEN_OK;
/* To quit we need either :
* - a second press on the button that made us enter
* - an action taken while pressing the enter button,
@@ -367,7 +367,7 @@ static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_ente
}
if (gui_quickscreen_do_button(qs, button))
{
- changed = true;
+ ret |= QUICKSCREEN_CHANGED;
can_quit = true;
FOR_NB_SCREENS(i)
gui_quickscreen_draw(qs, &screens[i], &parent[i],
@@ -389,6 +389,11 @@ static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_ente
FOR_NB_SCREENS(i)
skin_update(CUSTOM_STATUSBAR, i, SKIN_REFRESH_NON_STATIC);
}
+ else if (button == ACTION_STD_CONTEXT)
+ {
+ ret |= QUICKSCREEN_GOTO_SHORTCUTS_MENU;
+ break;
+ }
if ((button == button_enter) && can_quit)
break;
@@ -405,7 +410,8 @@ static bool gui_syncquickscreen_run(struct gui_quickscreen * qs, int button_ente
}
pop_current_activity();
- return changed;
+
+ return ret;
}
static const struct settings_list *get_setting(int gs_value,
@@ -427,9 +433,6 @@ int quick_screen_quick(int button_enter)
#endif
bool usb = false;
- if (global_settings.shortcuts_replaces_qs)
- return do_shortcut_menu(NULL);
-
qs.items[QUICKSCREEN_TOP] =
get_setting(global_settings.qs_items[QUICKSCREEN_TOP], NULL);
qs.items[QUICKSCREEN_LEFT] =
@@ -442,7 +445,8 @@ int quick_screen_quick(int button_enter)
get_setting(global_settings.qs_items[QUICKSCREEN_BOTTOM], NULL);
qs.callback = NULL;
- if (gui_syncquickscreen_run(&qs, button_enter, &usb))
+ int ret = gui_syncquickscreen_run(&qs, button_enter, &usb);
+ if (ret & QUICKSCREEN_CHANGED)
{
settings_save();
/* make sure repeat/shuffle/any other nasty ones get updated */
@@ -465,7 +469,10 @@ int quick_screen_quick(int button_enter)
set_albumart_mode(global_settings.album_art);
#endif
}
- return (usb ? 1:0);
+ if (usb)
+ return QUICKSCREEN_IN_USB;
+ return ret & QUICKSCREEN_GOTO_SHORTCUTS_MENU ? QUICKSCREEN_GOTO_SHORTCUTS_MENU :
+ QUICKSCREEN_OK;
}
/* stuff to make the quickscreen configurable */
diff --git a/apps/gui/quickscreen.h b/apps/gui/quickscreen.h
index 015928ee8a..6082b5a883 100644
--- a/apps/gui/quickscreen.h
+++ b/apps/gui/quickscreen.h
@@ -36,6 +36,13 @@ enum quickscreen_item {
QUICKSCREEN_ITEM_COUNT,
};
+enum quickscreen_return {
+ QUICKSCREEN_OK = 0,
+ QUICKSCREEN_IN_USB = 0x1,
+ QUICKSCREEN_GOTO_SHORTCUTS_MENU = 0x2,
+ QUICKSCREEN_CHANGED = 0x4,
+};
+
struct gui_quickscreen
{
const struct settings_list *items[QUICKSCREEN_ITEM_COUNT];
diff --git a/apps/gui/skin_engine/skin_backdrops.c b/apps/gui/skin_engine/skin_backdrops.c
index 146dccb18a..8be40d1ce2 100644
--- a/apps/gui/skin_engine/skin_backdrops.c
+++ b/apps/gui/skin_engine/skin_backdrops.c
@@ -41,7 +41,6 @@ static struct skin_backdrop {
} backdrops[NB_BDROPS];
#define NB_BDROPS SKINNABLE_SCREENS_COUNT*NB_SCREENS
-static int handle_being_loaded;
static int current_lcd_backdrop[NB_SCREENS];
bool skin_backdrop_get_debug(int index, char **path, int *ref_count, size_t *size)
@@ -65,8 +64,8 @@ bool skin_backdrop_get_debug(int index, char **path, int *ref_count, size_t *siz
static int buflib_move_callback(int handle, void* current, void* new)
{
- if (handle == handle_being_loaded)
- return BUFLIB_CB_CANNOT_MOVE;
+ (void)handle;
+
for (int i=0; i<NB_BDROPS; i++)
{
if (backdrops[i].buffer == current)
@@ -96,7 +95,6 @@ bool skin_backdrop_init(void)
}
FOR_NB_SCREENS(i)
current_lcd_backdrop[i] = -1;
- handle_being_loaded = -1;
first_go = false;
}
return go_status;
@@ -139,7 +137,7 @@ int skin_backdrop_assign(char* backdrop, char *bmpdir,
}
if (free >= 0)
{
- strlcpy(backdrops[free].name, filename, MAX_PATH);
+ strmemccpy(backdrops[free].name, filename, MAX_PATH);
backdrops[free].buffer = NULL;
backdrops[free].screen = screen;
backdrops[free].ref_count = 1;
@@ -182,16 +180,16 @@ bool skin_backdrops_preload(void)
backdrops[i].buffer = core_get_data(backdrops[i].buflib_handle);
if (strcmp(filename, BACKDROP_BUFFERNAME))
{
- handle_being_loaded = backdrops[i].buflib_handle;
+ core_pin(backdrops[i].buflib_handle);
backdrops[i].loaded =
screens[screen].backdrop_load(filename, backdrops[i].buffer);
+ core_unpin(backdrops[i].buflib_handle);
if (!backdrops[i].loaded)
{
core_free(backdrops[i].buflib_handle);
backdrops[i].buflib_handle = -1;
retval = false;
}
- handle_being_loaded = -1;
}
else
backdrops[i].loaded = true;
@@ -267,8 +265,7 @@ void skin_backdrop_unload(int backdrop_id)
backdrops[backdrop_id].ref_count--;
if (backdrops[backdrop_id].ref_count <= 0)
{
- if (backdrops[backdrop_id].buflib_handle > 0)
- core_free(backdrops[backdrop_id].buflib_handle);
+ core_free(backdrops[backdrop_id].buflib_handle);
backdrops[backdrop_id].buffer = NULL;
backdrops[backdrop_id].buflib_handle = -1;
backdrops[backdrop_id].loaded = false;
@@ -296,12 +293,12 @@ void skin_backdrop_load_setting(void)
return;
}
bool loaded;
+ core_pin(backdrops[i].buflib_handle);
backdrops[i].buffer = core_get_data(backdrops[i].buflib_handle);
- handle_being_loaded = backdrops[i].buflib_handle;
loaded = screens[SCREEN_MAIN].backdrop_load(
global_settings.backdrop_file,
backdrops[i].buffer);
- handle_being_loaded = -1;
+ core_unpin(backdrops[i].buflib_handle);
backdrops[i].name[2] = loaded ? '.' : '\0';
backdrops[i].loaded = loaded;
return;
diff --git a/apps/gui/skin_engine/skin_display.c b/apps/gui/skin_engine/skin_display.c
index 4a3dcc2177..43337049fd 100644
--- a/apps/gui/skin_engine/skin_display.c
+++ b/apps/gui/skin_engine/skin_display.c
@@ -46,6 +46,7 @@
#include "tagcache.h"
#include "list.h"
#include "option_select.h"
+#include "buffering.h"
#include "peakmeter.h"
/* Image stuff */
@@ -64,7 +65,7 @@
#endif
#include "root_menu.h"
-
+#include "wps.h"
#include "wps_internals.h"
#include "skin_engine.h"
#include "statusbar-skinned.h"
@@ -82,7 +83,7 @@ void skin_update(enum skinnable_screens skin, enum screen_type screen,
struct gui_wps *gwps = skin_get_gwps(skin, screen);
/* This maybe shouldnt be here,
* This is also safe for skined screen which dont use the id3 */
- struct mp3entry *id3 = skin_get_global_state()->id3;
+ struct mp3entry *id3 = get_wps_state()->id3;
bool cuesheet_update = (id3 != NULL ? cuesheet_subtrack_changed(id3) : false);
if (cuesheet_update)
skin_request_full_update(skin);
@@ -157,11 +158,12 @@ void ab_draw_markers(struct screen * screen, int capacity,
#endif
-void draw_progressbar(struct gui_wps *gwps, int line, struct progressbar *pb)
+void draw_progressbar(struct gui_wps *gwps, struct skin_viewport* skin_viewport,
+ int line, struct progressbar *pb)
{
struct screen *display = gwps->display;
- struct viewport *vp = SKINOFFSETTOPTR(get_skin_buffer(gwps->data), pb->vp);
- struct wps_state *state = skin_get_global_state();
+ struct viewport *vp = &skin_viewport->vp;
+ struct wps_state *state = get_wps_state();
struct mp3entry *id3 = state->id3;
int x = pb->x, y = pb->y, width = pb->width, height = pb->height;
unsigned long length, end;
@@ -411,8 +413,7 @@ void wps_display_images(struct gui_wps *gwps, struct viewport* vp)
#ifdef HAVE_ALBUMART
/* now draw the AA */
struct skin_albumart *aa = SKINOFFSETTOPTR(get_skin_buffer(data), data->albumart);
- if (aa && SKINOFFSETTOPTR(get_skin_buffer(data), aa->vp) == vp
- && aa->draw_handle >= 0)
+ if (aa && aa->draw_handle >= 0)
{
draw_album_art(gwps, aa->draw_handle, false);
aa->draw_handle = -1;
@@ -499,78 +500,82 @@ void write_line(struct screen *display, struct align_pos *format_align,
/* CASE 1: left and centered string overlap */
/* there is a left string, need to merge left and center */
- if ((left_width != 0 && center_width != 0) &&
- (left_width + space_width > center_xpos)) {
- /* replace the former separator '\0' of left and
- center string with a space */
- *(--format_align->center) = ' ';
- /* calculate the new width and position of the merged string */
- left_width = left_width + space_width + center_width;
- /* there is no centered string anymore */
- center_width = 0;
- }
- /* there is no left string, move center to left */
- if ((left_width == 0 && center_width != 0) &&
- (left_width > center_xpos)) {
- /* move the center string to the left string */
- format_align->left = format_align->center;
- /* calculate the new width and position of the string */
- left_width = center_width;
- /* there is no centered string anymore */
- center_width = 0;
- }
+ if (center_width != 0)
+ {
+ if (left_width != 0 && left_width + space_width > center_xpos) {
+ /* replace the former separator '\0' of left and
+ center string with a space */
+ *(--format_align->center) = ' ';
+ /* calculate the new width and position of the merged string */
+ left_width = left_width + space_width + center_width;
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+ /* there is no left string, move center to left */
+ else if (left_width == 0 && center_xpos < 0) {
+ /* move the center string to the left string */
+ format_align->left = format_align->center;
+ /* calculate the new width and position of the string */
+ left_width = center_width;
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+ } /*(center_width != 0)*/
/* CASE 2: centered and right string overlap */
/* there is a right string, need to merge center and right */
- if ((center_width != 0 && right_width != 0) &&
- (center_xpos + center_width + space_width > right_xpos)) {
- /* replace the former separator '\0' of center and
- right string with a space */
- *(--format_align->right) = ' ';
- /* move the center string to the right after merge */
- format_align->right = format_align->center;
- /* calculate the new width and position of the merged string */
- right_width = center_width + space_width + right_width;
- right_xpos = (viewport_width - right_width);
- /* there is no centered string anymore */
- center_width = 0;
- }
- /* there is no right string, move center to right */
- if ((center_width != 0 && right_width == 0) &&
- (center_xpos + center_width > right_xpos)) {
- /* move the center string to the right string */
- format_align->right = format_align->center;
- /* calculate the new width and position of the string */
- right_width = center_width;
- right_xpos = (viewport_width - right_width);
- /* there is no centered string anymore */
- center_width = 0;
- }
+ if (center_width != 0)
+ {
+ int center_left_x = center_xpos + center_width;
+ if (right_width != 0 && center_left_x + space_width > right_xpos) {
+ /* replace the former separator '\0' of center and
+ right string with a space */
+ *(--format_align->right) = ' ';
+ /* move the center string to the right after merge */
+ format_align->right = format_align->center;
+ /* calculate the new width and position of the merged string */
+ right_width = center_width + space_width + right_width;
+ right_xpos = (viewport_width - right_width);
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+ /* there is no right string, move center to right */
+ else if (right_width == 0 && center_left_x > right_xpos) {
+ /* move the center string to the right string */
+ format_align->right = format_align->center;
+ /* calculate the new width and position of the string */
+ right_width = center_width;
+ right_xpos = (viewport_width - right_width);
+ /* there is no centered string anymore */
+ center_width = 0;
+ }
+ } /*(center_width != 0)*/
/* CASE 3: left and right overlap
There is no center string anymore, either there never
was one or it has been merged in case 1 or 2 */
/* there is a left string, need to merge left and right */
- if ((left_width != 0 && center_width == 0 && right_width != 0) &&
- (left_width + space_width > right_xpos)) {
- /* replace the former separator '\0' of left and
- right string with a space */
- *(--format_align->right) = ' ';
- /* calculate the new width and position of the string */
- left_width = left_width + space_width + right_width;
- /* there is no right string anymore */
- right_width = 0;
- }
- /* there is no left string, move right to left */
- if ((left_width == 0 && center_width == 0 && right_width != 0) &&
- (left_width > right_xpos)) {
- /* move the right string to the left string */
- format_align->left = format_align->right;
- /* calculate the new width and position of the string */
- left_width = right_width;
- /* there is no right string anymore */
- right_width = 0;
- }
+ if (center_width == 0 && right_width != 0)
+ {
+ if (left_width != 0 && left_width + space_width > right_xpos) {
+ /* replace the former separator '\0' of left and
+ right string with a space */
+ *(--format_align->right) = ' ';
+ /* calculate the new width and position of the string */
+ left_width = left_width + space_width + right_width;
+ /* there is no right string anymore */
+ right_width = 0;
+ }
+ /* there is no left string, move right to left */
+ else if (left_width == 0 && right_xpos < 0) {
+ /* move the right string to the left string */
+ format_align->left = format_align->right;
+ /* calculate the new width and position of the string */
+ left_width = right_width;
+ /* there is no right string anymore */
+ right_width = 0;
+ }
+ } /* (center_width == 0 && right_width != 0)*/
if (scroll && ((left_width > scroll_width) ||
(center_width > scroll_width) ||
@@ -601,9 +606,9 @@ void write_line(struct screen *display, struct align_pos *format_align,
char *center = format_align->center ?: "";
char *right = format_align->right ?: "";
- display->put_line(0, line, linedes, "$t$*s$t$*s$t", left,
- center_xpos - left_width, center,
- right_xpos - (center_xpos + center_width), right);
+ display->put_line(0, line, linedes, "$t$*s$t$*s$t", left_width == 0 ? "" : left ,
+ center_xpos - left_width, center_width == 0 ? "" : center,
+ right_xpos - center_xpos - center_width, right_width == 0 ? "" : right);
}
}
@@ -632,14 +637,86 @@ void draw_peakmeters(struct gui_wps *gwps, int line_number,
}
}
-bool skin_has_sbs(enum screen_type screen, struct wps_data *data)
+#ifdef HAVE_ALBUMART
+/* Draw the album art bitmap from the given handle ID onto the given WPS.
+ Call with clear = true to clear the bitmap instead of drawing it. */
+void draw_album_art(struct gui_wps *gwps, int handle_id, bool clear)
{
- (void)screen;
- (void)data;
+ if (!gwps || !gwps->data || !gwps->display || handle_id < 0)
+ return;
+
+ struct wps_data *data = gwps->data;
+ struct skin_albumart *aa = SKINOFFSETTOPTR(get_skin_buffer(data), data->albumart);
+
+ if (!aa)
+ return;
+
+ struct bitmap *bmp;
+ if (bufgetdata(handle_id, 0, (void *)&bmp) <= 0)
+ return;
+
+ short x = aa->x;
+ short y = aa->y;
+ short width = bmp->width;
+ short height = bmp->height;
+
+ if (aa->width > 0)
+ {
+ /* Crop if the bitmap is too wide */
+ width = MIN(bmp->width, aa->width);
+
+ /* Align */
+ if (aa->xalign & WPS_ALBUMART_ALIGN_RIGHT)
+ x += aa->width - width;
+ else if (aa->xalign & WPS_ALBUMART_ALIGN_CENTER)
+ x += (aa->width - width) / 2;
+ }
+
+ if (aa->height > 0)
+ {
+ /* Crop if the bitmap is too high */
+ height = MIN(bmp->height, aa->height);
+
+ /* Align */
+ if (aa->yalign & WPS_ALBUMART_ALIGN_BOTTOM)
+ y += aa->height - height;
+ else if (aa->yalign & WPS_ALBUMART_ALIGN_CENTER)
+ y += (aa->height - height) / 2;
+ }
+
+ if (!clear)
+ {
+ /* Draw the bitmap */
+ gwps->display->bitmap_part((fb_data*)bmp->data, 0, 0,
+ STRIDE(gwps->display->screen_type,
+ bmp->width, bmp->height),
+ x, y, width, height);
+#ifdef HAVE_LCD_INVERT
+ if (global_settings.invert) {
+ gwps->display->set_drawmode(DRMODE_COMPLEMENT);
+ gwps->display->fillrect(x, y, width, height);
+ gwps->display->set_drawmode(DRMODE_SOLID);
+ }
+#endif
+ }
+ else
+ {
+ /* Clear the bitmap */
+ gwps->display->set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+ gwps->display->fillrect(x, y, width, height);
+ gwps->display->set_drawmode(DRMODE_SOLID);
+ }
+}
+#endif
+
+bool skin_has_sbs(struct gui_wps *gwps)
+{
+ struct wps_data *data = gwps->data;
+
bool draw = false;
if (data->wps_sb_tag)
draw = data->show_sb_on_wps;
- else if (statusbar_position(screen) != STATUSBAR_OFF)
+ else if (statusbar_position(gwps->display->screen_type) != STATUSBAR_OFF)
draw = true;
return draw;
}
diff --git a/apps/gui/skin_engine/skin_display.h b/apps/gui/skin_engine/skin_display.h
index de1b0b20b5..56af12aa03 100644
--- a/apps/gui/skin_engine/skin_display.h
+++ b/apps/gui/skin_engine/skin_display.h
@@ -29,7 +29,8 @@
#define _SKIN_DISPLAY_H_
-void draw_progressbar(struct gui_wps *gwps, int line, struct progressbar *pb);
+void draw_progressbar(struct gui_wps *gwps, struct skin_viewport* skin_viewport,
+ int line, struct progressbar *pb);
void draw_playlist_viewer_list(struct gui_wps *gwps, struct playlistviewer *viewer);
/* clears the area where the image was shown */
void clear_image_pos(struct gui_wps *gwps, struct gui_img *img);
@@ -54,4 +55,10 @@ void write_line(struct screen *display, struct align_pos *format_align,
int line, bool scroll, struct line_desc *line_desc);
void draw_peakmeters(struct gui_wps *gwps, int line_number,
struct viewport *viewport);
+#ifdef HAVE_ALBUMART
+/* Draw the album art bitmap from the given handle ID onto the given Skin.
+ Call with clear = true to clear the bitmap instead of drawing it. */
+void draw_album_art(struct gui_wps *gwps, int handle_id, bool clear);
+#endif
+
#endif
diff --git a/apps/gui/skin_engine/skin_engine.c b/apps/gui/skin_engine/skin_engine.c
index 8ba76e5739..c007ed35d4 100644
--- a/apps/gui/skin_engine/skin_engine.c
+++ b/apps/gui/skin_engine/skin_engine.c
@@ -34,9 +34,11 @@
#if CONFIG_TUNER
#include "radio.h"
#endif
+#include "gui/list.h"
#include "skin_engine.h"
#include "skin_buffer.h"
#include "statusbar-skinned.h"
+#include "wps_internals.h"
#define FAILSAFENAME "rockbox_failsafe"
@@ -51,7 +53,6 @@ static bool skins_initialised = false;
static char* get_skin_filename(char *buf, size_t buf_size,
enum skinnable_screens skin, enum screen_type screen);
-struct wps_state wps_state = { .id3 = NULL };
static struct gui_skin_helper {
int (*preproccess)(enum screen_type screen, struct wps_data *data);
int (*postproccess)(enum screen_type screen, struct wps_data *data);
@@ -308,11 +309,6 @@ struct gui_wps *skin_get_gwps(enum skinnable_screens skin, enum screen_type scre
return &skins[skin][screen].gui_wps;
}
-struct wps_state *skin_get_global_state(void)
-{
- return &wps_state;
-}
-
/* This is called to find out if we the screen needs a full update.
* if true you MUST do a full update as the next call will return false */
bool skin_do_full_update(enum skinnable_screens skin,
@@ -334,3 +330,56 @@ void skin_request_full_update(enum skinnable_screens skin)
FOR_NB_SCREENS(i)
skins[skin][i].needs_full_update = true;
}
+
+bool dbg_skin_engine(void)
+{
+ struct simplelist_info info;
+ int i, total = 0;
+#if defined(HAVE_BACKDROP_IMAGE)
+ int ref_count;
+ char *path;
+ size_t bytes;
+ int path_prefix_len = strlen(ROCKBOX_DIR "/wps/");
+#endif
+ simplelist_info_init(&info, "Skin engine usage", 0, NULL);
+ simplelist_set_line_count(0);
+ FOR_NB_SCREENS(j) {
+#if NB_SCREENS > 1
+ simplelist_addline("%s display:",
+ j == 0 ? "Main" : "Remote");
+#endif
+ for (i = 0; i < skin_get_num_skins(); i++) {
+ struct skin_stats *stats = skin_get_stats(i, j);
+ if (stats->buflib_handles)
+ {
+ simplelist_addline("Skin ID: %d, %d allocations",
+ i, stats->buflib_handles);
+ simplelist_addline("\tskin: %d bytes",
+ stats->tree_size);
+ simplelist_addline("\tImages: %d bytes",
+ stats->images_size);
+ simplelist_addline("\tTotal: %d bytes",
+ stats->tree_size + stats->images_size);
+ total += stats->tree_size + stats->images_size;
+ }
+ }
+ }
+ simplelist_addline("Skin total usage: %d bytes", total);
+#if defined(HAVE_BACKDROP_IMAGE)
+ simplelist_addline("Backdrop Images:");
+ i = 0;
+ while (skin_backdrop_get_debug(i++, &path, &ref_count, &bytes)) {
+ if (ref_count > 0) {
+
+ if (!strncasecmp(path, ROCKBOX_DIR "/wps/", path_prefix_len))
+ path += path_prefix_len;
+ simplelist_addline("%s", path);
+ simplelist_addline("\tref_count: %d", ref_count);
+ simplelist_addline("\tsize: %d", bytes);
+ total += bytes;
+ }
+ }
+ simplelist_addline("Total usage: %d bytes", total);
+#endif
+ return simplelist_show_list(&info);
+}
diff --git a/apps/gui/skin_engine/skin_engine.h b/apps/gui/skin_engine/skin_engine.h
index e26ec34d1f..d04c873e84 100644
--- a/apps/gui/skin_engine/skin_engine.h
+++ b/apps/gui/skin_engine/skin_engine.h
@@ -26,8 +26,7 @@
#ifndef PLUGIN
#include "tag_table.h"
-
-#include "wps_internals.h" /* TODO: remove this line.. shoudlnt be needed */
+#include "screen_access.h"
enum skinnable_screens {
CUSTOM_STATUSBAR,
@@ -39,24 +38,20 @@ enum skinnable_screens {
SKINNABLE_SCREENS_COUNT
};
+struct skin_stats;
+struct skin_viewport;
+struct gui_wps;
+
#ifdef HAVE_TOUCHSCREEN
-int skin_get_touchaction(struct wps_data *data, int* edge_offset,
- struct touchregion **retregion);
-void skin_disarm_touchregions(struct wps_data *data);
+int skin_get_touchaction(struct gui_wps *gwps, int* edge_offset);
+void skin_disarm_touchregions(struct gui_wps *gwps);
#endif
/* Do a update_type update of the skinned screen */
void skin_update(enum skinnable_screens skin, enum screen_type screen,
unsigned int update_type);
-/*
- * setup up the skin-data from a format-buffer (isfile = false)
- * or from a skinfile (isfile = true)
- */
-bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
- const char *buf, bool isfile, struct skin_stats *stats);
-
-bool skin_has_sbs(enum screen_type screen, struct wps_data *data);
+bool skin_has_sbs(struct gui_wps *gwps);
/* load a backdrop into the skin buffer.
@@ -81,7 +76,6 @@ int skin_wait_for_action(enum skinnable_screens skin, int context, int timeout);
void skin_load(enum skinnable_screens skin, enum screen_type screen,
const char *buf, bool isfile);
struct gui_wps *skin_get_gwps(enum skinnable_screens skin, enum screen_type screen);
-struct wps_state *skin_get_global_state(void);
void gui_sync_skin_init(void);
void skin_unload_all(void);
@@ -89,5 +83,7 @@ void skin_unload_all(void);
bool skin_do_full_update(enum skinnable_screens skin, enum screen_type screen);
void skin_request_full_update(enum skinnable_screens skin);
+bool dbg_skin_engine(void);
+
#endif /* !PLUGIN */
#endif
diff --git a/apps/gui/skin_engine/skin_parser.c b/apps/gui/skin_engine/skin_parser.c
index d89ca8b534..3e91536411 100644
--- a/apps/gui/skin_engine/skin_parser.c
+++ b/apps/gui/skin_engine/skin_parser.c
@@ -427,9 +427,6 @@ static int parse_image_load(struct skin_element *element,
img->is_9_segment = false;
img->loaded = false;
- /* save current viewport */
- img->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp);
-
if (token->type == SKIN_TOKEN_IMAGE_DISPLAY)
token->value.data = PTRTOSKINOFFSET(skin_buffer, img);
@@ -497,7 +494,6 @@ static int parse_playlistview(struct skin_element *element,
struct playlistviewer *viewer = skin_buffer_alloc(sizeof(*viewer));
if (!viewer)
return WPS_ERROR_INVALID_PARAM;
- viewer->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp);
viewer->show_icons = true;
viewer->start_offset = get_param(element, 0)->data.number;
viewer->line = PTRTOSKINOFFSET(skin_buffer, get_param_code(element, 1));
@@ -615,7 +611,8 @@ static int parse_viewporttextstyle(struct skin_element *element,
if (element->params_count < 2 ||
!parse_color(curr_screen, get_param_text(element, 1), &colour))
return 1;
- line->style = STYLE_COLORED;
+ /* STYLE_COLORED is only a modifier and can't be used on its own */
+ line->style = STYLE_COLORED | STYLE_DEFAULT;
line->text_color = colour;
}
#ifdef HAVE_LCD_COLOR
@@ -714,8 +711,35 @@ static int parse_viewportcolour(struct skin_element *element,
return -1;
if (isdefault(param))
{
- colour->colour = get_viewport_default_colour(curr_screen,
- token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR);
+ unsigned int fg_color;
+ unsigned int bg_color;
+
+ switch (curr_screen)
+ {
+#if defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1
+ case SCREEN_REMOTE:
+ fg_color = LCD_REMOTE_DEFAULT_FG;
+ bg_color = LCD_REMOTE_DEFAULT_BG;
+ break;
+#endif
+ default:
+#if defined(HAVE_LCD_COLOR)
+ fg_color = global_settings.fg_color;
+ bg_color = global_settings.bg_color;
+#elif LCD_DEPTH > 1
+ fg_color = LCD_DEFAULT_FG;
+ bg_color = LCD_DEFAULT_BG;
+#else
+ fg_color = 0;
+ bg_color = 0;
+#endif
+ break;
+ }
+
+ if (token->type == SKIN_TOKEN_VIEWPORT_FGCOLOUR)
+ colour->colour = fg_color;
+ else
+ colour->colour = bg_color;
}
else
{
@@ -723,7 +747,6 @@ static int parse_viewportcolour(struct skin_element *element,
&colour->colour))
return -1;
}
- colour->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp);
token->value.data = PTRTOSKINOFFSET(skin_buffer, colour);
if (element->line == curr_viewport_element->line)
{
@@ -942,7 +965,6 @@ static int parse_progressbar_tag(struct skin_element* element,
if (!pb)
return WPS_ERROR_INVALID_PARAM;
- pb->vp = PTRTOSKINOFFSET(skin_buffer, vp);
pb->follow_lang_direction = follow_lang_direction > 0;
pb->nofill = false;
pb->noborder = false;
@@ -1169,7 +1191,6 @@ static int parse_progressbar_tag(struct skin_element* element,
img->display = -1;
img->using_preloaded_icons = false;
img->buflib_handle = -1;
- img->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp);
img->loaded = false;
struct skin_token_list *item = new_skin_token_list_item(NULL, img);
if (!item)
@@ -1296,7 +1317,6 @@ static int parse_albumart_load(struct skin_element* element,
if (!isdefault(param3) && param3->type == PERCENT)
aa->height = param3->data.number * curr_vp->vp.height / 1000;
- aa->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp);
aa->draw_handle = -1;
/* if we got here, we parsed everything ok .. ! */
@@ -1828,9 +1848,7 @@ void skin_data_free_buflib_allocs(struct wps_data *wps_data)
abort:
wps_data->font_ids = PTRTOSKINOFFSET(skin_buffer, NULL); /* Safe if skin_buffer is NULL */
wps_data->images = PTRTOSKINOFFSET(skin_buffer, NULL);
- if (wps_data->buflib_handle > 0)
- core_free(wps_data->buflib_handle);
- wps_data->buflib_handle = -1;
+ wps_data->buflib_handle = core_free(wps_data->buflib_handle);
#endif
}
@@ -1871,13 +1889,11 @@ static void skin_data_reset(struct wps_data *wps_data)
}
#ifndef __PCTOOL__
-static int currently_loading_handle = -1;
static int buflib_move_callback(int handle, void* current, void* new)
{
+ (void)handle;
(void)current;
(void)new;
- if (handle == currently_loading_handle)
- return BUFLIB_CB_CANNOT_MOVE;
/* Any active skins may be scrolling - which means using viewports which
* will be moved after this callback returns. This is a hammer to make that
* safe. TODO: use a screwdriver instead.
@@ -1891,14 +1907,6 @@ static int buflib_move_callback(int handle, void* current, void* new)
return BUFLIB_CB_OK;
}
static struct buflib_callbacks buflib_ops = {buflib_move_callback, NULL, NULL};
-static void lock_handle(int handle)
-{
- currently_loading_handle = handle;
-}
-static void unlock_handle(void)
-{
- currently_loading_handle = -1;
-}
#endif
static int load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char* bmpdir)
@@ -1945,12 +1953,12 @@ static int load_skin_bmp(struct wps_data *wps_data, struct bitmap *bitmap, char*
_stats->buflib_handles++;
_stats->images_size += buf_size;
lseek(fd, 0, SEEK_SET);
- lock_handle(handle);
+ core_pin(handle);
bitmap->data = core_get_data(handle);
int ret = read_bmp_fd(fd, bitmap, buf_size, format, NULL);
bitmap->data = NULL; /* do this to force a crash later if the
caller doesnt call core_get_data() */
- unlock_handle();
+ core_unpin(handle);
close(fd);
if (ret > 0)
{
@@ -1998,7 +2006,7 @@ static bool load_skin_bitmaps(struct wps_data *wps_data, char *bmpdir)
strcpy(path, img->bm.data);
handle = load_skin_bmp(wps_data, &img->bm, bmpdir);
img->buflib_handle = handle;
- img->loaded = img->buflib_handle >= 0;
+ img->loaded = img->buflib_handle > 0;
if (img->loaded)
{
@@ -2396,13 +2404,6 @@ static int skin_element_callback(struct skin_element* element, void* data)
break;
#endif
#ifdef HAVE_ALBUMART
- case SKIN_TOKEN_ALBUMART_DISPLAY:
- if (SKINOFFSETTOPTR(skin_buffer, wps_data->albumart))
- {
- struct skin_albumart *aa = SKINOFFSETTOPTR(skin_buffer, wps_data->albumart);
- aa->vp = PTRTOSKINOFFSET(skin_buffer, &curr_vp->vp);
- }
- break;
case SKIN_TOKEN_ALBUMART_LOAD:
function = parse_albumart_load;
break;
@@ -2563,7 +2564,7 @@ bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
{
/* get the bitmap dir */
char *dot = strrchr(buf, '.');
- strlcpy(bmpdir, buf, dot - buf + 1);
+ strmemccpy(bmpdir, buf, dot - buf + 1);
}
else
{
diff --git a/apps/gui/skin_engine/skin_render.c b/apps/gui/skin_engine/skin_render.c
index 1f777b6672..06f7d9798d 100644
--- a/apps/gui/skin_engine/skin_render.c
+++ b/apps/gui/skin_engine/skin_render.c
@@ -50,6 +50,7 @@
#include "root_menu.h"
#include "misc.h"
#include "list.h"
+#include "wps.h"
#define MAX_LINE 1024
@@ -66,7 +67,6 @@ struct skin_draw_info {
bool no_line_break;
bool line_scrolls;
bool force_redraw;
- bool viewport_change;
char *buf;
size_t buf_size;
@@ -74,6 +74,8 @@ struct skin_draw_info {
int offset; /* used by the playlist viewer */
};
+extern void sb_set_info_vp(enum screen_type screen, OFFSETTYPE(char*) label);
+
typedef bool (*skin_render_func)(struct skin_element* alternator, struct skin_draw_info *info);
bool skin_render_alternator(struct skin_element* alternator, struct skin_draw_info *info);
@@ -93,11 +95,11 @@ get_child(OFFSETTYPE(struct skin_element**) children, int child)
static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
- struct skin_element *element, struct skin_viewport* skin_vp)
+ struct skin_element *element)
{
struct wps_token *token = (struct wps_token *)SKINOFFSETTOPTR(skin_buffer, element->data);
if (!token) return false;
- struct viewport *vp = &skin_vp->vp;
+ struct skin_viewport *skin_vp = info->skin_vp;
struct wps_data *data = gwps->data;
bool do_refresh = (element->tag->flags & info->refresh_type) > 0;
@@ -108,9 +110,7 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
{
struct viewport_colour *col = SKINOFFSETTOPTR(skin_buffer, token->value.data);
if (!col) return false;
- struct viewport *vp = SKINOFFSETTOPTR(skin_buffer, col->vp);
- if (!vp) return false;
- vp->fg_pattern = col->colour;
+ skin_vp->vp.fg_pattern = col->colour;
skin_vp->fgbg_changed = true;
}
break;
@@ -118,9 +118,7 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
{
struct viewport_colour *col = SKINOFFSETTOPTR(skin_buffer, token->value.data);
if (!col) return false;
- struct viewport *vp = SKINOFFSETTOPTR(skin_buffer, col->vp);
- if (!vp) return false;
- vp->bg_pattern = col->colour;
+ skin_vp->vp.bg_pattern = col->colour;
skin_vp->fgbg_changed = true;
}
break;
@@ -196,7 +194,7 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
case SKIN_TOKEN_PEAKMETER:
data->peak_meter_enabled = true;
if (do_refresh)
- draw_peakmeters(gwps, info->line_number, vp);
+ draw_peakmeters(gwps, info->line_number, &skin_vp->vp);
break;
case SKIN_TOKEN_DRAWRECTANGLE:
if (do_refresh)
@@ -215,13 +213,13 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
#endif
{
#if LCD_DEPTH > 1
- unsigned backup = vp->fg_pattern;
- vp->fg_pattern = rect->start_colour;
+ unsigned backup = skin_vp->vp.fg_pattern;
+ skin_vp->vp.fg_pattern = rect->start_colour;
#endif
gwps->display->fillrect(rect->x, rect->y, rect->width,
rect->height);
#if LCD_DEPTH > 1
- vp->fg_pattern = backup;
+ skin_vp->vp.fg_pattern = backup;
#endif
}
}
@@ -239,7 +237,7 @@ static bool do_non_text_tags(struct gui_wps *gwps, struct skin_draw_info *info,
{
struct progressbar *bar = (struct progressbar*)SKINOFFSETTOPTR(skin_buffer, token->value.data);
if (do_refresh)
- draw_progressbar(gwps, info->line_number, bar);
+ draw_progressbar(gwps, info->skin_vp, info->line_number, bar);
}
break;
case SKIN_TOKEN_IMAGE_DISPLAY:
@@ -582,7 +580,7 @@ static bool skin_render_line(struct skin_element* line, struct skin_draw_info *i
{
break;
}
- if (!do_non_text_tags(info->gwps, info, child, info->skin_vp))
+ if (!do_non_text_tags(info->gwps, info, child))
{
static char tempbuf[128];
const char *valuestr = get_token_value(info->gwps, SKINOFFSETTOPTR(skin_buffer, child->data),
@@ -590,10 +588,6 @@ static bool skin_render_line(struct skin_element* line, struct skin_draw_info *i
sizeof(tempbuf), NULL);
if (valuestr)
{
-#if defined(ONDA_VX747) || defined(ONDA_VX747P)
- /* Doesn't redraw (in sim at least) */
- needs_update = true;
-#endif
#if CONFIG_RTC
if (child->tag->flags&SKIN_RTC_REFRESH)
needs_update = needs_update || info->refresh_type&SKIN_REFRESH_DYNAMIC;
@@ -606,10 +600,6 @@ static bool skin_render_line(struct skin_element* line, struct skin_draw_info *i
}
break;
case TEXT:
-#if defined(ONDA_VX747) || defined(ONDA_VX747P)
- /* Doesn't redraw (in sim at least) */
- needs_update = true;
-#endif
strlcat(info->cur_align_start, SKINOFFSETTOPTR(skin_buffer, child->data),
info->buf_size - (info->cur_align_start-info->buf));
needs_update = needs_update ||
@@ -943,7 +933,7 @@ void skin_render_playlistviewer(struct playlistviewer* viewer,
struct align_pos * align = &info.align;
bool needs_update;
int cur_pos, start_item, max;
- int nb_lines = viewport_get_nb_lines(SKINOFFSETTOPTR(skin_buffer, viewer->vp));
+ int nb_lines = viewport_get_nb_lines(&skin_viewport->vp);
#if CONFIG_TUNER
if (get_current_activity() == ACTIVITY_FM)
{
@@ -954,8 +944,8 @@ void skin_render_playlistviewer(struct playlistviewer* viewer,
else
#endif
{
- struct cuesheet *cue = skin_get_global_state()->id3 ?
- skin_get_global_state()->id3->cuesheet : NULL;
+ struct wps_state *state = get_wps_state();
+ struct cuesheet *cue = state->id3 ? state->id3->cuesheet : NULL;
cur_pos = playlist_get_display_index();
max = playlist_amount()+1;
if (cue)
@@ -989,7 +979,7 @@ void skin_render_playlistviewer(struct playlistviewer* viewer,
/* only update if the line needs to be, and there is something to write */
if (refresh_type && needs_update)
{
- struct viewport *vp = SKINOFFSETTOPTR(skin_buffer, viewer->vp);
+ struct viewport *vp = &skin_viewport->vp;
if (!info.force_redraw)
display->scroll_stop_viewport_rect(vp,
0, info.line_number*display->getcharheight(),
diff --git a/apps/gui/skin_engine/skin_tokens.c b/apps/gui/skin_engine/skin_tokens.c
index 27022b87d1..874eff3809 100644
--- a/apps/gui/skin_engine/skin_tokens.c
+++ b/apps/gui/skin_engine/skin_tokens.c
@@ -66,11 +66,10 @@
#include "fixedpoint.h"
#endif
#include "list.h"
+#include "wps.h"
#define NOINLINE __attribute__ ((noinline))
-extern struct wps_state wps_state;
-
static const char* get_codectype(const struct mp3entry* id3)
{
if (id3 && id3->codectype < AFMT_NUM_CODECS) {
@@ -115,7 +114,7 @@ char* get_dir(char* buf, int buf_size, const char* path, int level)
return NULL;
len = MIN(last_sep - sep, buf_size - 1);
- strlcpy(buf, sep + 1, len + 1);
+ strmemccpy(buf, sep + 1, len + 1);
return buf;
}
@@ -228,7 +227,7 @@ static const char* get_filename_token(struct wps_token *token, char* filename,
const char *get_id3_token(struct wps_token *token, struct mp3entry *id3,
char *filename, char *buf, int buf_size, int limit, int *intval)
{
- struct wps_state *state = &wps_state;
+ struct wps_state *state = get_wps_state();
if (id3)
{
unsigned long length = id3->length;
@@ -540,7 +539,7 @@ const char *get_radio_token(struct wps_token *token, int preset_offset,
static struct mp3entry* get_mp3entry_from_offset(int offset, char **filename)
{
struct mp3entry* pid3 = NULL;
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
struct cuesheet *cue = state->id3 ? state->id3->cuesheet : NULL;
const char *fname = NULL;
if (cue && cue->curr_track_idx + offset < cue->track_count)
@@ -678,7 +677,7 @@ const char *get_token_value(struct gui_wps *gwps,
return NULL;
struct wps_data *data = gwps->data;
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
struct mp3entry *id3; /* Think very carefully about using this.
maybe get_id3_token() is the better place? */
const char *out_text = NULL;
@@ -1040,37 +1039,19 @@ const char *get_token_value(struct gui_wps *gwps,
case SKIN_TOKEN_PLAYBACK_STATUS:
{
int status = current_playmode();
- /* music */
- int mode = 1; /* stop */
- if (status == STATUS_PLAY)
- mode = 2; /* play */
- if (state->is_fading ||
- (status == STATUS_PAUSE && !status_get_ffmode()))
- mode = 3; /* pause */
- else
- { /* ff / rwd */
- if (status_get_ffmode() == STATUS_FASTFORWARD)
- mode = 4;
- if (status_get_ffmode() == STATUS_FASTBACKWARD)
- mode = 5;
+ switch (status) {
+ case STATUS_STOP:
+ numeric_ret = 1;
+ break;
+ case STATUS_PLAY:
+ numeric_ret = 2;
+ break;
+ default:
+ numeric_ret = status + 1;
+ break;
}
-#ifdef HAVE_RECORDING
- /* recording */
- if (status == STATUS_RECORD)
- mode = 6;
- else if (status == STATUS_RECORD_PAUSE)
- mode = 7;
-#endif
-#if CONFIG_TUNER
- /* radio */
- if (status == STATUS_RADIO)
- mode = 8;
- else if (status == STATUS_RADIO_PAUSE)
- mode = 9;
-#endif
- numeric_ret = mode;
- snprintf(buf, buf_size, "%d", mode-1);
+ snprintf(buf, buf_size, "%d", numeric_ret-1);
numeric_buf = buf;
goto gtv_ret_numeric_tag_info;
}
diff --git a/apps/gui/skin_engine/skin_touchsupport.c b/apps/gui/skin_engine/skin_touchsupport.c
index 045bc809c8..77ab30d675 100644
--- a/apps/gui/skin_engine/skin_touchsupport.c
+++ b/apps/gui/skin_engine/skin_touchsupport.c
@@ -35,8 +35,9 @@
#include "dsp_misc.h"
/** Disarms all touchregions. */
-void skin_disarm_touchregions(struct wps_data *data)
+void skin_disarm_touchregions(struct gui_wps *gwps)
{
+ struct wps_data *data = gwps->data;
char* skin_buffer = get_skin_buffer(data);
struct skin_token_list *regions = SKINOFFSETTOPTR(skin_buffer, data->touchregions);
while (regions)
@@ -52,9 +53,9 @@ void skin_disarm_touchregions(struct wps_data *data)
* egde_offset is a percentage value for the position of the touch
* inside the bar for regions which arnt WPS_TOUCHREGION_ACTION type.
*/
-int skin_get_touchaction(struct wps_data *data, int* edge_offset,
- struct touchregion **retregion)
+int skin_get_touchaction(struct gui_wps *gwps, int* edge_offset)
{
+ struct wps_data *data = gwps->data;
int returncode = ACTION_NONE;
short x,y;
short vx, vy;
@@ -162,9 +163,7 @@ int skin_get_touchaction(struct wps_data *data, int* edge_offset,
/* On release, all regions are disarmed. */
if (released)
- skin_disarm_touchregions(data);
- if (retregion && temp)
- *retregion = temp;
+ skin_disarm_touchregions(gwps);
if (temp && temp->press_length == LONG_PRESS)
temp->armed = false;
diff --git a/apps/gui/skin_engine/wps_internals.h b/apps/gui/skin_engine/wps_internals.h
index bf368bc4f3..6a5d3c27f9 100644
--- a/apps/gui/skin_engine/wps_internals.h
+++ b/apps/gui/skin_engine/wps_internals.h
@@ -31,6 +31,8 @@
#include "core_alloc.h"
#endif
+struct wps_data;
+
struct skin_stats {
size_t buflib_handles;
size_t tree_size;
@@ -42,6 +44,13 @@ struct skin_stats *skin_get_stats(int number, int screen);
#define skin_clear_stats(stats) memset(stats, 0, sizeof(struct skin_stats))
bool skin_backdrop_get_debug(int index, char **path, int *ref_count, size_t *size);
+/*
+ * setup up the skin-data from a format-buffer (isfile = false)
+ * or from a skinfile (isfile = true)
+ */
+bool skin_data_load(enum screen_type screen, struct wps_data *wps_data,
+ const char *buf, bool isfile, struct skin_stats *stats);
+
/* Timeout unit expressed in HZ. In WPS, all timeouts are given in seconds
(possibly with a decimal fraction) but stored as integer values.
E.g. 2.5 is stored as 25. This means 25 tenth of a second, i.e. 25 units.
@@ -81,7 +90,6 @@ struct skin_token_list {
};
struct gui_img {
- OFFSETTYPE(struct viewport*) vp; /* The viewport to display this image in */
short int x; /* x-pos */
short int y; /* y-pos */
short int num_subimages; /* number of sub-images */
@@ -104,7 +112,6 @@ struct image_display {
struct progressbar {
enum skin_token_type type;
- OFFSETTYPE(struct viewport *) vp;
/* regular pb */
short x;
/* >=0: explicitly set in the tag -> y-coord within the viewport
@@ -189,7 +196,6 @@ struct skin_viewport {
#endif
};
struct viewport_colour {
- OFFSETTYPE(struct viewport *) vp;
unsigned colour;
};
@@ -236,7 +242,6 @@ struct touchregion_lastpress {
#endif
struct playlistviewer {
- OFFSETTYPE(struct viewport *) vp;
bool show_icons;
int start_offset;
OFFSETTYPE(struct skin_element *) line;
@@ -267,7 +272,6 @@ struct skin_albumart {
unsigned char yalign; /* WPS_ALBUMART_ALIGN_TOP, _CENTER, _BOTTOM */
unsigned char state; /* WPS_ALBUMART_NONE, _CHECK, _LOAD */
- OFFSETTYPE(struct viewport *) vp;
int draw_handle;
};
#endif
@@ -313,6 +317,17 @@ struct listitem {
short offset;
};
+struct listitem_viewport_cfg {
+ struct wps_data *data;
+ OFFSETTYPE(char *) label;
+ int width;
+ int height;
+ int xmargin;
+ int ymargin;
+ bool tile;
+ struct skin_viewport selected_item_vp;
+};
+
#ifdef HAVE_SKIN_VARIABLES
struct skin_var {
OFFSETTYPE(const char *) label;
@@ -372,7 +387,7 @@ struct wps_data
#ifndef __PCTOOL__
static inline char* get_skin_buffer(struct wps_data* data)
{
- if (data->buflib_handle >= 0)
+ if (data->buflib_handle > 0)
return core_get_data(data->buflib_handle);
return NULL;
}
@@ -382,30 +397,6 @@ static inline char* get_skin_buffer(struct wps_data* data)
/* wps_data end */
-/* wps_state
- holds the data which belongs to the current played track,
- the track which will be played afterwards, current path to the track
- and some status infos */
-struct wps_state
-{
- struct mp3entry* id3;
- struct mp3entry* nid3;
- int ff_rewind_count;
- bool ff_rewind;
- bool paused;
- bool is_fading;
-};
-
-/* change the ff/rew-status
- if ff_rew = true then we are in skipping mode
- else we are in normal mode */
-/* void wps_state_update_ff_rew(bool ff_rew); Currently unused */
-
-/* change the tag-information of the current played track
- and the following track */
-/* void wps_state_update_id3_nid3(struct mp3entry *id3, struct mp3entry *nid3); Currently unused */
-/* wps_state end*/
-
/* gui_wps
defines a wps with its data, state,
and the screen on which the wps-content should be drawn */
diff --git a/apps/gui/splash.c b/apps/gui/splash.c
index b85e4693aa..d0f1fbb67c 100644
--- a/apps/gui/splash.c
+++ b/apps/gui/splash.c
@@ -29,71 +29,84 @@
#include "talk.h"
#include "splash.h"
#include "viewport.h"
-#include "strtok_r.h"
+#include "strptokspn_r.h"
+#include "scrollbar.h"
+#include "font.h"
+
+static long progress_next_tick = 0;
#define MAXLINES (LCD_HEIGHT/6)
#define MAXBUFFER 512
#define RECT_SPACING 2
#define SPLASH_MEMORY_INTERVAL (HZ)
-static void splash_internal(struct screen * screen, const char *fmt, va_list ap)
+static bool splash_internal(struct screen * screen, const char *fmt, va_list ap,
+ struct viewport *vp, int addl_lines)
{
char splash_buf[MAXBUFFER];
- char *lines[MAXLINES];
-
- char *next;
- char *lastbreak = NULL;
- char *store = NULL;
+ struct splash_lines {
+ const char *str;
+ size_t len;
+ } lines[MAXLINES];
+ const char *next;
+ const char *lastbreak = NULL;
+ const char *store = NULL;
int line = 0;
int x = 0;
int y, i;
- int space_w, w, h;
- struct viewport vp;
+ int space_w, w, chr_h;
int width, height;
int maxw = 0;
+ int fontnum = vp->font;
- viewport_set_defaults(&vp, screen->screen_type);
- struct viewport *last_vp = screen->set_viewport(&vp);
-
- screen->getstringsize(" ", &space_w, &h);
- y = h;
+ char lastbrkchr;
+ size_t len, next_len;
+ const char matchstr[] = "\r\n\f\v\t ";
+ font_getstringsize(" ", &space_w, &chr_h, fontnum);
+ y = chr_h + (addl_lines * chr_h);
vsnprintf(splash_buf, sizeof(splash_buf), fmt, ap);
va_end(ap);
/* break splash string into display lines, doing proper word wrap */
-
- next = strtok_r(splash_buf, " ", &store);
+ next = strptokspn_r(splash_buf, matchstr, &next_len, &store);
if (!next)
- goto end; /* nothing to display */
+ return false; /* nothing to display */
- lines[0] = next;
+ lines[line].len = next_len;
+ lines[line].str = next;
while (true)
{
- screen->getstringsize(next, &w, NULL);
+ w = font_getstringnsize(next, next_len, NULL, NULL, fontnum);
if (lastbreak)
{
- if (x + (next - lastbreak) * space_w + w
- > vp.width - RECT_SPACING*2)
- { /* too wide, wrap */
+ len = next - lastbreak;
+ int next_w = len * space_w;
+ if (x + next_w + w > vp->width - RECT_SPACING*2 || lastbrkchr != ' ')
+ { /* too wide, or control character wrap */
if (x > maxw)
maxw = x;
- if ((y + h > vp.height) || (line >= (MAXLINES-1)))
+ if ((y + chr_h * 2 > vp->height) || (line >= (MAXLINES-1)))
break; /* screen full or out of lines */
x = 0;
- y += h;
- lines[++line] = next;
+ y += chr_h;
+ lines[++line].len = next_len;
+ lines[line].str = next;
}
else
{
/* restore & calculate spacing */
- *lastbreak = ' ';
- x += (next - lastbreak) * space_w;
+ lines[line].len += next_len + 1;
+ x += next_w;
}
}
x += w;
- lastbreak = next + strlen(next);
- next = strtok_r(NULL, " ", &store);
+
+ lastbreak = next + next_len;
+ lastbrkchr = *lastbreak;
+
+ next = strptokspn_r(NULL, matchstr, &next_len, &store);
+
if (!next)
{ /* no more words */
if (x > maxw)
@@ -111,53 +124,48 @@ static void splash_internal(struct screen * screen, const char *fmt, va_list ap)
width = maxw + 2*RECT_SPACING;
height = y + 2*RECT_SPACING;
- if (width > vp.width)
- width = vp.width;
- if (height > vp.height)
- height = vp.height;
+ if (width > vp->width)
+ width = vp->width;
+ if (height > vp->height)
+ height = vp->height;
- vp.x += (vp.width - width) / 2;
- vp.y += (vp.height - height) / 2;
- vp.width = width;
- vp.height = height;
+ vp->x += (vp->width - width) / 2;
+ vp->y += (vp->height - height) / 2;
+ vp->width = width;
+ vp->height = height;
- vp.flags |= VP_FLAG_ALIGN_CENTER;
+ vp->flags |= VP_FLAG_ALIGN_CENTER;
#if LCD_DEPTH > 1
if (screen->depth > 1)
{
- vp.drawmode = DRMODE_FG;
- /* can't do vp.fg_pattern here, since set_foreground does a bit more on
+ vp->drawmode = DRMODE_FG;
+ /* can't do vp->fg_pattern here, since set_foreground does a bit more on
* greyscale */
screen->set_foreground(SCREEN_COLOR_TO_NATIVE(screen, LCD_LIGHTGRAY));
}
else
#endif
- vp.drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID);
+ vp->drawmode = (DRMODE_SOLID|DRMODE_INVERSEVID);
screen->fill_viewport();
#if LCD_DEPTH > 1
if (screen->depth > 1)
- /* can't do vp.fg_pattern here, since set_foreground does a bit more on
+ /* can't do vp->fg_pattern here, since set_foreground does a bit more on
* greyscale */
screen->set_foreground(SCREEN_COLOR_TO_NATIVE(screen, LCD_BLACK));
else
#endif
- vp.drawmode = DRMODE_SOLID;
+ vp->drawmode = DRMODE_SOLID;
screen->draw_border_viewport();
- /* prepare putting the text */
- y = RECT_SPACING;
-
/* print the message to screen */
- for (i = 0; i <= line; i++, y+=h)
+ for(i = 0, y = RECT_SPACING; i <= line; i++, y+= chr_h)
{
- screen->putsxy(0, y, lines[i]);
+ screen->putsxyf(0, y, "%.*s", lines[i].len, lines[i].str);
}
- screen->update_viewport();
-end:
- screen->set_viewport(last_vp);
+ return true; /* needs update */
}
void splashf(int ticks, const char *fmt, ...)
@@ -169,9 +177,17 @@ void splashf(int ticks, const char *fmt, ...)
fmt = P2STR((unsigned char *)fmt);
FOR_NB_SCREENS(i)
{
+ struct screen * screen = &(screens[i]);
+ struct viewport vp;
+ viewport_set_defaults(&vp, screen->screen_type);
+ struct viewport *last_vp = screen->set_viewport(&vp);
+
va_start(ap, fmt);
- splash_internal(&(screens[i]), fmt, ap);
+ if (splash_internal(screen, fmt, ap, &vp, 0))
+ screen->update_viewport();
va_end(ap);
+
+ screen->set_viewport(last_vp);
}
if (ticks)
sleep(ticks);
@@ -189,3 +205,59 @@ void splash(int ticks, const char *str)
#endif
splashf(ticks, "%s", P2STR((const unsigned char*)str));
}
+
+/* set delay before progress meter is shown */
+void splash_progress_set_delay(long delay_ticks)
+{
+ progress_next_tick = current_tick + delay_ticks;
+}
+
+/* splash a progress meter */
+void splash_progress(int current, int total, const char *fmt, ...)
+{
+ va_list ap;
+ int vp_flag = VP_FLAG_VP_DIRTY;
+ /* progress update tick */
+ long now = current_tick;
+
+ if (current < total)
+ {
+ if(TIME_BEFORE(now, progress_next_tick))
+ return;
+ /* limit to 20fps */
+ progress_next_tick = now + HZ/20;
+ vp_flag = 0; /* don't mark vp dirty to prevent flashing */
+ }
+
+ /* If fmt is a lang ID then get the corresponding string (which
+ still might contain % place holders). */
+ fmt = P2STR((unsigned char *)fmt);
+ FOR_NB_SCREENS(i)
+ {
+ struct screen * screen = &(screens[i]);
+ struct viewport vp;
+ viewport_set_defaults(&vp, screen->screen_type);
+ struct viewport *last_vp = screen->set_viewport_ex(&vp, vp_flag);
+
+ va_start(ap, fmt);
+ if (splash_internal(screen, fmt, ap, &vp, 1))
+ {
+ int size = screen->getcharheight();
+ int x = RECT_SPACING;
+ int y = vp.height - size - RECT_SPACING;
+ int w = vp.width - RECT_SPACING * 2;
+ int h = size;
+#ifdef HAVE_LCD_COLOR
+ const int sb_flags = HORIZONTAL | FOREGROUND;
+#else
+ const int sb_flags = HORIZONTAL;
+#endif
+ gui_scrollbar_draw(screen, x, y, w, h, total, 0, current, sb_flags);
+
+ screen->update_viewport();
+ }
+ va_end(ap);
+
+ screen->set_viewport(last_vp);
+ }
+}
diff --git a/apps/gui/splash.h b/apps/gui/splash.h
index 76b4c16d0c..f7ff44e00b 100644
--- a/apps/gui/splash.h
+++ b/apps/gui/splash.h
@@ -39,4 +39,15 @@ extern void splashf(int ticks, const char *fmt, ...) ATTRIBUTE_PRINTF(2, 3);
* it will be voiced
*/
extern void splash(int ticks, const char *str);
+
+/* set a delay before displaying the progress meter the first time */
+extern void splash_progress_set_delay(long delay_ticks);
+/*
+ * Puts a splash message centered on all the screens with a progressbar
+ * - current : current progress increment
+ * - total : total increments
+ * - fmt : what to say *printf style
+ * updates limited internally to 20 fps - call repeatedly to update progress
+ */
+extern void splash_progress(int current, int total, const char *fmt, ...) ATTRIBUTE_PRINTF(3, 4);
#endif /* _GUI_ICON_H_ */
diff --git a/apps/gui/statusbar-skinned.c b/apps/gui/statusbar-skinned.c
index fd695bf6b6..8dd66641dd 100644
--- a/apps/gui/statusbar-skinned.c
+++ b/apps/gui/statusbar-skinned.c
@@ -27,7 +27,6 @@
#include "appevents.h"
#include "screens.h"
#include "screen_access.h"
-#include "strlcpy.h"
#include "skin_parser.h"
#include "skin_buffer.h"
#include "skin_engine/skin_engine.h"
@@ -49,11 +48,13 @@
static int update_delay = DEFAULT_UPDATE_DELAY;
static bool sbs_has_title[NB_SCREENS];
-static char* sbs_title[NB_SCREENS];
+static const char* sbs_title[NB_SCREENS];
static enum themable_icons sbs_icon[NB_SCREENS];
static bool sbs_loaded[NB_SCREENS] = { false };
-bool sb_set_title_text(char* title, enum themable_icons icon, enum screen_type screen)
+void sb_set_info_vp(enum screen_type screen, OFFSETTYPE(char*) label);
+
+bool sb_set_title_text(const char* title, enum themable_icons icon, enum screen_type screen)
{
sbs_title[screen] = title;
/* Icon_NOICON == -1 which the skin engine wants at position 1, so + 2 */
@@ -305,18 +306,17 @@ void sb_bypass_touchregions(bool enable)
int sb_touch_to_button(int context)
{
- struct touchregion *region;
static int last_context = -1;
int button, offset;
if (bypass_sb_touchregions)
return ACTION_TOUCHSCREEN;
-
+
+ struct gui_wps *gwps = skin_get_gwps(CUSTOM_STATUSBAR, SCREEN_MAIN);
if (last_context != context)
- skin_disarm_touchregions(skin_get_gwps(CUSTOM_STATUSBAR, SCREEN_MAIN)->data);
+ skin_disarm_touchregions(gwps);
last_context = context;
- button = skin_get_touchaction(skin_get_gwps(CUSTOM_STATUSBAR, SCREEN_MAIN)->data,
- &offset, &region);
-
+
+ button = skin_get_touchaction(gwps, &offset);
switch (button)
{
#ifdef HAVE_VOLUME_IN_LIST
diff --git a/apps/gui/statusbar-skinned.h b/apps/gui/statusbar-skinned.h
index 3ed36f1a84..e8fa14e676 100644
--- a/apps/gui/statusbar-skinned.h
+++ b/apps/gui/statusbar-skinned.h
@@ -30,16 +30,15 @@
#include "icon.h"
#include "skin_engine/skin_engine.h"
-void sb_skin_data_load(enum screen_type screen, const char *buf, bool isfile);
+struct wps_data;
char* sb_create_from_settings(enum screen_type screen);
void sb_skin_init(void) INIT_ATTR;
-void sb_set_info_vp(enum screen_type screen, OFFSETTYPE(char*) label);
struct viewport *sb_skin_get_info_vp(enum screen_type screen);
void sb_skin_update(enum screen_type screen, bool force);
void sb_skin_set_update_delay(int delay);
-bool sb_set_title_text(char* title, enum themable_icons icon, enum screen_type screen);
+bool sb_set_title_text(const char* title, enum themable_icons icon, enum screen_type screen);
void sb_skin_has_title(enum screen_type screen);
const char* sb_get_title(enum screen_type screen);
enum themable_icons sb_get_icon(enum screen_type screen);
diff --git a/apps/gui/viewport.c b/apps/gui/viewport.c
index 9f9cb186f5..e90426a132 100644
--- a/apps/gui/viewport.c
+++ b/apps/gui/viewport.c
@@ -233,7 +233,7 @@ static void viewportmanager_redraw(unsigned short id, void* data)
}
}
-void viewportmanager_init()
+void viewportmanager_init(void)
{
FOR_NB_SCREENS(i)
{
@@ -348,41 +348,3 @@ void viewport_set_defaults(struct viewport *vp,
#endif /* !__PCTOOL__ */
viewport_set_fullscreen(vp, screen);
}
-
-
-int get_viewport_default_colour(enum screen_type screen, bool fgcolour)
-{
- (void)screen; (void)fgcolour;
-#if (LCD_DEPTH > 1) || (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
- int colour;
- if (fgcolour)
- {
-#if (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
- if (screen == SCREEN_REMOTE)
- colour = REMOTE_FG_FALLBACK;
- else
-#endif
-#if defined(HAVE_LCD_COLOR)
- colour = global_settings.fg_color;
-#else
- colour = FG_FALLBACK;
-#endif
- }
- else
- {
-#if (defined(HAVE_REMOTE_LCD) && LCD_REMOTE_DEPTH > 1)
- if (screen == SCREEN_REMOTE)
- colour = REMOTE_BG_FALLBACK;
- else
-#endif
-#if defined(HAVE_LCD_COLOR)
- colour = global_settings.bg_color;
-#else
- colour = BG_FALLBACK;
-#endif
- }
- return colour;
-#else
- return 0;
-#endif /* LCD_DEPTH > 1 || LCD_REMOTE_DEPTH > 1 */
-}
diff --git a/apps/gui/viewport.h b/apps/gui/viewport.h
index 2810be2ac3..c57a58b232 100644
--- a/apps/gui/viewport.h
+++ b/apps/gui/viewport.h
@@ -43,7 +43,6 @@ void viewport_set_defaults(struct viewport *vp,
const enum screen_type screen);
void viewport_set_fullscreen(struct viewport *vp,
const enum screen_type screen);
-int get_viewport_default_colour(enum screen_type screen, bool fgcolour);
#ifndef __PCTOOL__
diff --git a/apps/gui/wps.c b/apps/gui/wps.c
index 7554892451..430746cc8d 100644
--- a/apps/gui/wps.c
+++ b/apps/gui/wps.c
@@ -53,6 +53,7 @@
#include "root_menu.h"
#include "backdrop.h"
#include "quickscreen.h"
+#include "shortcuts.h"
#include "pitchscreen.h"
#include "appevents.h"
#include "viewport.h"
@@ -61,6 +62,7 @@
#include "playlist_viewer.h"
#include "wps.h"
#include "statusbar-skinned.h"
+#include "skin_engine/wps_internals.h"
#define RESTORE_WPS_INSTANTLY 0l
#define RESTORE_WPS_NEXT_SECOND ((long)(HZ+current_tick))
@@ -69,6 +71,8 @@
/* 3% of 30min file == 54s step size */
#define MIN_FF_REWIND_STEP 500
+static struct wps_state wps_state;
+
/* initial setup of wps_data */
static void wps_state_init(void);
static void track_info_callback(unsigned short id, void *param);
@@ -114,7 +118,7 @@ static void update_non_static(void)
skin_update(WPS, i, SKIN_REFRESH_NON_STATIC);
}
-void pause_action(bool may_fade, bool updatewps)
+void pause_action(bool updatewps)
{
/* Do audio first, then update, unless skin were to use its local
status in which case, reverse it */
@@ -131,11 +135,9 @@ void pause_action(bool may_fade, bool updatewps)
- global_settings.pause_rewind * 1000;
audio_ff_rewind(newpos > 0 ? newpos : 0);
}
-
- (void)may_fade;
}
-void unpause_action(bool may_fade, bool updatewps)
+void unpause_action(bool updatewps)
{
/* Do audio first, then update, unless skin were to use its local
status in which case, reverse it */
@@ -143,8 +145,6 @@ void unpause_action(bool may_fade, bool updatewps)
if (updatewps)
update_non_static();
-
- (void)may_fade;
}
static bool update_onvol_change(enum screen_type screen)
@@ -156,11 +156,12 @@ static bool update_onvol_change(enum screen_type screen)
#ifdef HAVE_TOUCHSCREEN
-static int skintouch_to_wps(struct wps_data *data)
+static int skintouch_to_wps(void)
{
int offset = 0;
- struct touchregion *region;
- int button = skin_get_touchaction(data, &offset, &region);
+ struct wps_state *gstate = get_wps_state();
+ struct gui_wps *gwps = skin_get_gwps(WPS, SCREEN_MAIN);
+ int button = skin_get_touchaction(gwps, &offset);
switch (button)
{
case ACTION_STD_PREV:
@@ -182,9 +183,9 @@ static int skintouch_to_wps(struct wps_data *data)
return ACTION_WPS_HOTKEY;
#endif
case ACTION_TOUCH_SCROLLBAR:
- skin_get_global_state()->id3->elapsed = skin_get_global_state()->id3->length*offset/1000;
+ gstate->id3->elapsed = gstate->id3->length*offset/1000;
audio_pre_ff_rewind();
- audio_ff_rewind(skin_get_global_state()->id3->elapsed);
+ audio_ff_rewind(gstate->id3->elapsed);
return ACTION_TOUCHSCREEN;
case ACTION_TOUCH_VOLUME:
{
@@ -202,7 +203,7 @@ static int skintouch_to_wps(struct wps_data *data)
}
#endif /* HAVE_TOUCHSCREEN */
-bool ffwd_rew(int button)
+static bool ffwd_rew(int button, bool seek_from_end)
{
unsigned int step = 0; /* current ff/rewind step */
unsigned int max_step = 0; /* maximum ff/rewind step */
@@ -210,7 +211,10 @@ bool ffwd_rew(int button)
int direction = -1; /* forward=1 or backward=-1 */
bool exit = false;
bool usb = false;
+ bool ff_rewind = false;
const long ff_rw_accel = (global_settings.ff_rewind_accel + 3);
+ struct wps_state *gstate = get_wps_state();
+ struct mp3entry *old_id3 = gstate->id3;
if (button == ACTION_NONE)
{
@@ -219,27 +223,37 @@ bool ffwd_rew(int button)
}
while (!exit)
{
+ struct mp3entry *id3 = gstate->id3;
+ if (id3 != old_id3)
+ {
+ ff_rewind = false;
+ ff_rewind_count = 0;
+ old_id3 = id3;
+ }
+ if (id3 && seek_from_end)
+ id3->elapsed = id3->length;
+
switch ( button )
{
case ACTION_WPS_SEEKFWD:
direction = 1;
/* Fallthrough */
case ACTION_WPS_SEEKBACK:
- if (skin_get_global_state()->ff_rewind)
+ if (ff_rewind)
{
if (direction == 1)
{
/* fast forwarding, calc max step relative to end */
- max_step = (skin_get_global_state()->id3->length -
- (skin_get_global_state()->id3->elapsed +
+ max_step = (id3->length -
+ (id3->elapsed +
ff_rewind_count)) *
FF_REWIND_MAX_PERCENT / 100;
}
else
{
/* rewinding, calc max step relative to start */
- max_step = (skin_get_global_state()->id3->elapsed + ff_rewind_count) *
- FF_REWIND_MAX_PERCENT / 100;
+ max_step = (id3->elapsed + ff_rewind_count) *
+ FF_REWIND_MAX_PERCENT / 100;
}
max_step = MAX(max_step, MIN_FF_REWIND_STEP);
@@ -254,8 +268,7 @@ bool ffwd_rew(int button)
}
else
{
- if ( (audio_status() & AUDIO_STATUS_PLAY) &&
- skin_get_global_state()->id3 && skin_get_global_state()->id3->length )
+ if ((audio_status() & AUDIO_STATUS_PLAY) && id3 && id3->length )
{
audio_pre_ff_rewind();
if (direction > 0)
@@ -263,7 +276,7 @@ bool ffwd_rew(int button)
else
status_set_ffmode(STATUS_FASTBACKWARD);
- skin_get_global_state()->ff_rewind = true;
+ ff_rewind = true;
step = 1000 * global_settings.ff_rewind_min_step;
}
@@ -272,19 +285,17 @@ bool ffwd_rew(int button)
}
if (direction > 0) {
- if ((skin_get_global_state()->id3->elapsed + ff_rewind_count) >
- skin_get_global_state()->id3->length)
- ff_rewind_count = skin_get_global_state()->id3->length -
- skin_get_global_state()->id3->elapsed;
+ if ((id3->elapsed + ff_rewind_count) > id3->length)
+ ff_rewind_count = id3->length - id3->elapsed;
}
else {
- if ((int)(skin_get_global_state()->id3->elapsed + ff_rewind_count) < 0)
- ff_rewind_count = -skin_get_global_state()->id3->elapsed;
+ if ((int)(id3->elapsed + ff_rewind_count) < 0)
+ ff_rewind_count = -id3->elapsed;
}
/* set the wps state ff_rewind_count so the progess info
displays corectly */
- skin_get_global_state()->ff_rewind_count = ff_rewind_count;
+ gstate->ff_rewind_count = ff_rewind_count;
FOR_NB_SCREENS(i)
{
@@ -296,10 +307,10 @@ bool ffwd_rew(int button)
break;
case ACTION_WPS_STOPSEEK:
- skin_get_global_state()->id3->elapsed = skin_get_global_state()->id3->elapsed+ff_rewind_count;
- audio_ff_rewind(skin_get_global_state()->id3->elapsed);
- skin_get_global_state()->ff_rewind_count = 0;
- skin_get_global_state()->ff_rewind = false;
+ id3->elapsed = id3->elapsed + ff_rewind_count;
+ audio_ff_rewind(id3->elapsed);
+ gstate->ff_rewind_count = 0;
+ ff_rewind = false;
status_set_ffmode(0);
exit = true;
break;
@@ -317,10 +328,11 @@ bool ffwd_rew(int button)
button = get_action(CONTEXT_WPS|ALLOW_SOFTLOCK,TIMEOUT_BLOCK);
#ifdef HAVE_TOUCHSCREEN
if (button == ACTION_TOUCHSCREEN)
- button = skintouch_to_wps(skin_get_gwps(WPS, SCREEN_MAIN)->data);
+ button = skintouch_to_wps();
#endif
- if (button != ACTION_WPS_SEEKFWD &&
- button != ACTION_WPS_SEEKBACK)
+ if (button != ACTION_WPS_SEEKFWD
+ && button != ACTION_WPS_SEEKBACK
+ && button != 0)
button = ACTION_WPS_STOPSEEK;
}
}
@@ -330,7 +342,7 @@ bool ffwd_rew(int button)
#if defined(HAVE_BACKLIGHT) || defined(HAVE_REMOTE_LCD)
static void gwps_caption_backlight(struct wps_state *state)
{
- if (state && state->id3)
+ if (state->id3)
{
#ifdef HAVE_BACKLIGHT
if (global_settings.caption_backlight)
@@ -385,7 +397,7 @@ static void change_dir(int direction)
static void prev_track(unsigned long skip_thresh)
{
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
if (state->id3->elapsed < skip_thresh)
{
audio_prev();
@@ -406,7 +418,7 @@ static void prev_track(unsigned long skip_thresh)
static void next_track(void)
{
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
/* take care of if we're playing a cuesheet */
if (state->id3->cuesheet)
{
@@ -423,7 +435,7 @@ static void next_track(void)
static void play_hop(int direction)
{
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
struct cuesheet *cue = state->id3->cuesheet;
long step = global_settings.skip_length*1000;
long elapsed = state->id3->elapsed;
@@ -442,26 +454,26 @@ static void play_hop(int direction)
{
if (direction < 0)
{
- prev_track(DEFAULT_SKIP_TRESH);
+ prev_track(DEFAULT_SKIP_THRESH);
return;
}
- else if (remaining < DEFAULT_SKIP_TRESH*2)
+ else if (remaining < DEFAULT_SKIP_THRESH*2)
{
next_track();
return;
}
else
- elapsed += (remaining - DEFAULT_SKIP_TRESH*2);
+ elapsed += (remaining - DEFAULT_SKIP_THRESH*2);
}
else if (!global_settings.prevent_skip &&
(!step ||
(direction > 0 && step >= remaining) ||
- (direction < 0 && elapsed < DEFAULT_SKIP_TRESH)))
+ (direction < 0 && elapsed < DEFAULT_SKIP_THRESH)))
{ /* Do normal track skipping */
if (direction > 0)
next_track();
else if (direction < 0)
- prev_track(DEFAULT_SKIP_TRESH);
+ prev_track(DEFAULT_SKIP_THRESH);
return;
}
else if (direction == 1 && step >= remaining)
@@ -506,12 +518,12 @@ static void gwps_leave_wps(void)
{
FOR_NB_SCREENS(i)
{
- skin_get_gwps(WPS, i)->display->scroll_stop();
+ struct gui_wps *gwps = skin_get_gwps(WPS, i);
+ gwps->display->scroll_stop();
#ifdef HAVE_BACKDROP_IMAGE
skin_backdrop_show(sb_get_backdrop(i));
#endif
- viewportmanager_theme_undo(i, skin_has_sbs(i, skin_get_gwps(WPS, i)->data));
-
+ viewportmanager_theme_undo(i, skin_has_sbs(gwps));
}
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
@@ -536,7 +548,7 @@ static void gwps_enter_wps(void)
gwps = skin_get_gwps(WPS, i);
display = gwps->display;
display->scroll_stop();
- viewportmanager_theme_enable(i, skin_has_sbs(i, skin_get_gwps(WPS, i)->data), NULL);
+ viewportmanager_theme_enable(i, skin_has_sbs(gwps), NULL);
/* Update the values in the first (default) viewport - in case the user
has modified the statusbar or colour settings */
@@ -563,7 +575,7 @@ static void gwps_enter_wps(void)
}
#ifdef HAVE_TOUCHSCREEN
gwps = skin_get_gwps(WPS, SCREEN_MAIN);
- skin_disarm_touchregions(gwps->data);
+ skin_disarm_touchregions(gwps);
if (gwps->data->touchregions < 0)
touchscreen_set_mode(TOUCHSCREEN_BUTTON);
#endif
@@ -573,16 +585,16 @@ static void gwps_enter_wps(void)
void wps_do_playpause(bool updatewps)
{
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
if ( state->paused )
{
state->paused = false;
- unpause_action(true, updatewps);
+ unpause_action(updatewps);
}
else
{
state->paused = true;
- pause_action(true, updatewps);
+ pause_action(updatewps);
settings_save();
#if !defined(HAVE_SW_POWEROFF)
call_storage_idle_notifys(true); /* make sure resume info is saved */
@@ -610,7 +622,7 @@ long gui_wps_show(void)
bool update = false;
bool vol_changed = false;
long last_left = 0, last_right = 0;
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
#ifdef AB_REPEAT_ENABLE
ab_repeat_init();
@@ -644,7 +656,7 @@ long gui_wps_show(void)
exit = true;
#ifdef HAVE_TOUCHSCREEN
if (button == ACTION_TOUCHSCREEN)
- button = skintouch_to_wps(skin_get_gwps(WPS, SCREEN_MAIN)->data);
+ button = skintouch_to_wps();
#endif
/* The iPods/X5/M5 use a single button for the A-B mode markers,
defined as ACTION_WPSAB_SINGLE in their config files. */
@@ -733,7 +745,7 @@ long gui_wps_show(void)
}
}
else
- ffwd_rew(ACTION_WPS_SEEKFWD);
+ ffwd_rew(ACTION_WPS_SEEKFWD, false);
last_right = last_left = 0;
break;
/* fast rewind
@@ -752,9 +764,19 @@ long gui_wps_show(void)
{
change_dir(-1);
}
+ } else if (global_settings.rewind_across_tracks
+ && get_wps_state()->id3->elapsed < DEFAULT_SKIP_THRESH
+ && playlist_check(-1))
+ {
+ if (!audio_paused)
+ audio_pause();
+ audio_prev();
+ ffwd_rew(ACTION_WPS_SEEKBACK, true);
+ if (!audio_paused)
+ audio_resume();
}
else
- ffwd_rew(ACTION_WPS_SEEKBACK);
+ ffwd_rew(ACTION_WPS_SEEKBACK, false);
last_left = last_right = 0;
break;
@@ -839,15 +861,24 @@ long gui_wps_show(void)
case ACTION_WPS_QUICKSCREEN:
{
gwps_leave_wps();
- if (global_settings.shortcuts_replaces_qs)
+ bool enter_shortcuts_menu = global_settings.shortcuts_replaces_qs;
+ if (!enter_shortcuts_menu)
{
- global_status.last_screen = GO_TO_SHORTCUTMENU;
int ret = quick_screen_quick(button);
+ if (ret == QUICKSCREEN_IN_USB)
+ return GO_TO_ROOT;
+ else if (ret == QUICKSCREEN_GOTO_SHORTCUTS_MENU)
+ enter_shortcuts_menu = true;
+ else
+ restore = true;
+ }
+
+ if (enter_shortcuts_menu)
+ {
+ global_status.last_screen = GO_TO_SHORTCUTMENU;
+ int ret = do_shortcut_menu(NULL);
return (ret == GO_TO_PREVIOUS ? GO_TO_WPS : ret);
}
- else if (quick_screen_quick(button) > 0)
- return GO_TO_ROOT;
- restore = true;
}
break;
#endif /* HAVE_QUICKSCREEN */
@@ -917,7 +948,7 @@ long gui_wps_show(void)
break;
case ACTION_NONE: /* Timeout, do a partial update */
update = true;
- ffwd_rew(button); /* hopefully fix the ffw/rwd bug */
+ ffwd_rew(button, false); /* hopefully fix the ffw/rwd bug */
break;
#ifdef HAVE_RECORDING
case ACTION_WPS_REC:
@@ -1023,10 +1054,15 @@ long gui_wps_show(void)
return GO_TO_ROOT; /* unreachable - just to reduce compiler warnings */
}
+struct wps_state *get_wps_state(void)
+{
+ return &wps_state;
+}
+
/* this is called from the playback thread so NO DRAWING! */
static void track_info_callback(unsigned short id, void *param)
{
- struct wps_state *state = skin_get_global_state();
+ struct wps_state *state = get_wps_state();
if (id == PLAYBACK_EVENT_TRACK_CHANGE || id == PLAYBACK_EVENT_CUR_TRACK_READY)
{
@@ -1042,14 +1078,13 @@ static void track_info_callback(unsigned short id, void *param)
state->id3 = audio_current_track();
}
#endif
- skin_get_global_state()->nid3 = audio_next_track();
+ state->nid3 = audio_next_track();
skin_request_full_update(WPS);
}
static void wps_state_init(void)
{
- struct wps_state *state = skin_get_global_state();
- state->ff_rewind = false;
+ struct wps_state *state = get_wps_state();
state->paused = false;
if(audio_status() & AUDIO_STATUS_PLAY)
{
@@ -1073,16 +1108,3 @@ static void wps_state_init(void)
add_event(PLAYBACK_EVENT_TRACK_SKIP, track_info_callback);
#endif
}
-
-
-#ifdef IPOD_ACCESSORY_PROTOCOL
-bool is_wps_fading(void)
-{
- return skin_get_global_state()->is_fading;
-}
-
-int wps_get_ff_rewind_count(void)
-{
- return skin_get_global_state()->ff_rewind_count;
-}
-#endif
diff --git a/apps/gui/wps.h b/apps/gui/wps.h
index a463b0e9bb..001c112a4d 100644
--- a/apps/gui/wps.h
+++ b/apps/gui/wps.h
@@ -20,35 +20,30 @@
****************************************************************************/
#ifndef _WPS_H_
#define _WPS_H_
+
#include <stdbool.h>
-#include "config.h"
-#include "screen_access.h"
-
-long gui_wps_show(void);
-
-/* wrapper for the wps to load the skin (.wps/.rwps) files */
-void wps_data_load(enum screen_type, const char *, bool);
-void gui_sync_wps_init(void) INIT_ATTR;
+struct mp3entry;
-/* fade (if enabled) and pause the audio, optionally rewind a little */
-void pause_action(bool may_fade, bool updatewps);
-void unpause_action(bool may_fade, bool updatewps);
+/* Please don't add anything else to here... */
+struct wps_state
+{
+ struct mp3entry *id3;
+ struct mp3entry *nid3;
+ int ff_rewind_count;
+ bool paused;
+};
-/* fades the volume, e.g. on pause or stop */
-void fade(bool fade_in, bool updatewps);
+long gui_wps_show(void);
-bool ffwd_rew(int button);
+/* fade (if enabled) and pause the audio, optionally rewind a little */
+void pause_action(bool updatewps);
+void unpause_action(bool updatewps);
void wps_do_playpause(bool updatewps);
-#ifdef IPOD_ACCESSORY_PROTOCOL
-/* whether the wps is fading the volume due to pausing/stopping */
-bool is_wps_fading(void);
-/* return length of the current ff or rewin action, IAP needs this */
-int wps_get_ff_rewind_count(void);
-#endif /* IPOD_ACCESSORY_PROTOCOL */
+struct wps_state *get_wps_state(void);
/* in milliseconds */
-#define DEFAULT_SKIP_TRESH 3000l
+#define DEFAULT_SKIP_THRESH 3000l
#endif /* _WPS_H_ */
diff --git a/apps/gui/yesno.c b/apps/gui/yesno.c
index a79b8ae644..c763753cd7 100644
--- a/apps/gui/yesno.c
+++ b/apps/gui/yesno.c
@@ -253,14 +253,15 @@ enum yesno_res gui_syncyesno_run(const struct text_message * main_message,
if(result_displayed)
sleep(HZ);
+ exit:
+ remove_event_ex(GUI_EVENT_NEED_UI_UPDATE, gui_yesno_ui_update, &yn[0]);
+
FOR_NB_SCREENS(i)
{
screens[i].scroll_stop_viewport(yn[i].vp);
viewportmanager_theme_undo(i, true);
}
- exit:
- remove_event_ex(GUI_EVENT_NEED_UI_UPDATE, gui_yesno_ui_update, &yn[0]);
#ifdef HAVE_TOUCHSCREEN
touchscreen_set_mode(old_mode);
#endif
diff --git a/apps/hosted/android/keyboard.c b/apps/hosted/android/keyboard.c
index eda951a7c9..b74f67e782 100644
--- a/apps/hosted/android/keyboard.c
+++ b/apps/hosted/android/keyboard.c
@@ -100,7 +100,7 @@ int kbd_input(char* text, int buflen, unsigned short *kbd)
if (accepted)
{
utf8_string = e->GetStringUTFChars(env_ptr, new_string, 0);
- strlcpy(text, utf8_string, buflen);
+ strmemccpy(text, utf8_string, buflen);
e->ReleaseStringUTFChars(env_ptr, new_string, utf8_string);
e->DeleteGlobalRef(env_ptr, new_string);
}
diff --git a/apps/hosted/android/notification.c b/apps/hosted/android/notification.c
index 39c8b07737..a5b60c1013 100644
--- a/apps/hosted/android/notification.c
+++ b/apps/hosted/android/notification.c
@@ -29,6 +29,7 @@
#include "misc.h"
#include "thread.h"
#include "debug.h"
+#include "audio.h"
extern JNIEnv *env_ptr;
extern jclass RockboxService_class;
diff --git a/apps/iap/iap-core.c b/apps/iap/iap-core.c
index ae05806ae9..d2095b65bd 100644
--- a/apps/iap/iap-core.c
+++ b/apps/iap/iap-core.c
@@ -19,8 +19,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
-#include <string.h>
+#include "string-extra.h"
#include "panic.h"
#include "iap-core.h"
#include "iap-lingo.h"
diff --git a/apps/iap/iap-lingo4.c b/apps/iap/iap-lingo4.c
index 4ec5c462a1..eb629407f2 100644
--- a/apps/iap/iap-lingo4.c
+++ b/apps/iap/iap-lingo4.c
@@ -24,6 +24,7 @@
#include "filetree.h"
#include "wps.h"
#include "playback.h"
+#include "string-extra.h"
/*
* This macro is meant to be used inside an IAP mode message handler.
@@ -87,7 +88,7 @@ static void get_playlist_name(unsigned char *dest,
}
}
if (playlist_file != NULL) {
- strlcpy(dest, playlist_file->d_name, max_length);
+ strmemccpy(dest, playlist_file->d_name, max_length);
}
closedir(dp);
}
@@ -1218,7 +1219,7 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf)
{
memcpy(cur_dbrecord, buf + 3, 5);
- int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
+ int paused = !!(audio_status() & AUDIO_STATUS_PAUSE);
uint32_t index;
uint32_t trackcount;
index = get_u32(&cur_dbrecord[1]);
@@ -1430,7 +1431,6 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf)
unsigned int number_of_playlists = nbr_total_playlists();
uint32_t trackcount;
trackcount = playlist_amount();
- size_t len;
if ((buf[3] == 0x05) && ((start_index + read_count ) > trackcount))
{
@@ -1465,22 +1465,21 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf)
switch(buf[3])
{
case 0x05:
- len = strlcpy((char *)&data[7], id3.title,64);
+ strmemccpy((char *)&data[7], id3.title,64);
break;
case 0x02:
- len = strlcpy((char *)&data[7], id3.artist,64);
+ strmemccpy((char *)&data[7], id3.artist,64);
break;
case 0x03:
- len = strlcpy((char *)&data[7], id3.album,64);
+ strmemccpy((char *)&data[7], id3.album,64);
break;
case 0x04:
case 0x06:
- len = strlcpy((char *)&data[7], "Not Supported",14);
+ strmemccpy((char *)&data[7], "Not Supported",14);
break;
}
break;
}
- (void)len; /* Shut up, compiler */
put_u32(&data[3], start_index+counter);
iap_send_pkt(data, 7 + strlen(data+7) + 1);
yield();
@@ -2003,7 +2002,7 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf)
*
*/
{
- int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
+ int paused = !!(audio_status() & AUDIO_STATUS_PAUSE);
uint32_t index;
uint32_t trackcount;
index = get_u32(&buf[3]);
@@ -2821,7 +2820,7 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf)
*
*/
{
- int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
+ int paused = !!(audio_status() & AUDIO_STATUS_PAUSE);
long tracknum = get_u32(&buf[3]);
audio_pause();
@@ -2977,7 +2976,7 @@ void iap_handlepkt_mode4(const unsigned int len, const unsigned char *buf)
{
memcpy(cur_dbrecord, buf + 3, 5);
- int paused = (is_wps_fading() || (audio_status() & AUDIO_STATUS_PAUSE));
+ int paused = !!(audio_status() & AUDIO_STATUS_PAUSE);
unsigned int number_of_playlists = nbr_total_playlists();
uint32_t index;
uint32_t trackcount;
diff --git a/apps/keymaps/keymap-agptekrocker.c b/apps/keymaps/keymap-agptekrocker.c
index 1fb8465b0a..d016b3b323 100644
--- a/apps/keymaps/keymap-agptekrocker.c
+++ b/apps/keymaps/keymap-agptekrocker.c
@@ -139,15 +139,16 @@ static const struct button_mapping button_context_yesno[] = {
}; /* button_context_settings_yesno */
static const struct button_mapping button_context_quickscreen[] = {
- { ACTION_QS_TOP, BUTTON_UP|BUTTON_REL, BUTTON_NONE },
- { ACTION_QS_TOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REL, BUTTON_NONE },
- { ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE },
- { ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
- { ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE },
+ { ACTION_QS_TOP, BUTTON_UP|BUTTON_REL, BUTTON_NONE },
+ { ACTION_QS_TOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REL, BUTTON_NONE },
+ { ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REL, BUTTON_NONE },
+ { ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REL, BUTTON_NONE },
+ { ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
+ { ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT },
+ { ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
LAST_ITEM_IN_LIST
}; /* button_context_quickscreen */
diff --git a/apps/keymaps/keymap-clip.c b/apps/keymaps/keymap-clip.c
index 3c92f03a07..917f769890 100644
--- a/apps/keymaps/keymap-clip.c
+++ b/apps/keymaps/keymap-clip.c
@@ -179,7 +179,7 @@ static const struct button_mapping button_context_quickscreen[] = {
{ ACTION_NONE, BUTTON_LEFT, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_POWER|BUTTON_REL, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_HOME, BUTTON_NONE },
- { ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE },
+ { ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
{ ACTION_QS_TOP, BUTTON_UP|BUTTON_REL, BUTTON_NONE },
{ ACTION_QS_TOP, BUTTON_UP|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_QS_DOWN, BUTTON_DOWN|BUTTON_REL, BUTTON_NONE },
diff --git a/apps/keymaps/keymap-erosq.c b/apps/keymaps/keymap-erosq.c
index d486423a53..c69e4babdb 100644
--- a/apps/keymaps/keymap-erosq.c
+++ b/apps/keymaps/keymap-erosq.c
@@ -130,6 +130,7 @@ static const struct button_mapping button_context_quickscreen[] = {
{ ACTION_QS_DOWN, BUTTON_NEXT|BUTTON_REL, BUTTON_NONE },
{ ACTION_QS_DOWN, BUTTON_NEXT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE },
+ { ACTION_STD_CONTEXT,BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU },
LAST_ITEM_IN_LIST
}; /* button_context_quickscreen */
diff --git a/apps/keymaps/keymap-fiiom3k.c b/apps/keymaps/keymap-fiiom3k.c
index d0fbbb2e98..a9744b908b 100644
--- a/apps/keymaps/keymap-fiiom3k.c
+++ b/apps/keymaps/keymap-fiiom3k.c
@@ -175,7 +175,8 @@ static const struct button_mapping button_context_quickscreen[] = {
{ACTION_QS_VOLUP, BUTTON_VOL_UP|BUTTON_REPEAT, BUTTON_NONE},
{ACTION_QS_VOLDOWN, BUTTON_VOL_DOWN, BUTTON_NONE},
{ACTION_QS_VOLDOWN, BUTTON_VOL_DOWN|BUTTON_REPEAT, BUTTON_NONE},
- {ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE},
+ {ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT},
+ {ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT},
{ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE},
{ACTION_STD_CANCEL, BUTTON_MENU, BUTTON_NONE},
diff --git a/apps/keymaps/keymap-fuzeplus.c b/apps/keymaps/keymap-fuzeplus.c
index 6601316469..9ce5a79fc2 100644
--- a/apps/keymaps/keymap-fuzeplus.c
+++ b/apps/keymaps/keymap-fuzeplus.c
@@ -128,7 +128,8 @@ static const struct button_mapping button_context_keyboard[] = {
}; /* button_context_keyboard */
static const struct button_mapping button_context_quickscreen[] = {
- { ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE },
+ { ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT },
+ { ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
{ ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_PLAYPAUSE, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_BOTTOMRIGHT, BUTTON_NONE },
diff --git a/apps/keymaps/keymap-ipod.c b/apps/keymaps/keymap-ipod.c
index e3a17fffba..6d06bd0244 100644
--- a/apps/keymaps/keymap-ipod.c
+++ b/apps/keymaps/keymap-ipod.c
@@ -132,7 +132,7 @@ static const struct button_mapping button_context_quickscreen[] = {
{ ACTION_QS_LEFT, BUTTON_LEFT|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_QS_RIGHT, BUTTON_RIGHT, BUTTON_NONE },
{ ACTION_QS_RIGHT, BUTTON_RIGHT|BUTTON_REPEAT, BUTTON_NONE },
- { ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE },
+ { ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
{ ACTION_QS_VOLDOWN, BUTTON_SCROLL_BACK, BUTTON_NONE },
{ ACTION_QS_VOLDOWN, BUTTON_SCROLL_BACK|BUTTON_REPEAT, BUTTON_NONE },
{ ACTION_QS_VOLUP, BUTTON_SCROLL_FWD, BUTTON_NONE },
diff --git a/apps/keymaps/keymap-nwz.c b/apps/keymaps/keymap-nwz.c
index 028fbf1bfd..1fae8d3594 100644
--- a/apps/keymaps/keymap-nwz.c
+++ b/apps/keymaps/keymap-nwz.c
@@ -112,7 +112,8 @@ static const struct button_mapping button_context_keyboard[] = {
}; /* button_context_keyboard */
static const struct button_mapping button_context_quickscreen[] = {
- { ACTION_STD_CANCEL, BUTTON_PLAY, BUTTON_NONE },
+ { ACTION_STD_CONTEXT, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY },
+ { ACTION_STD_CANCEL, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY },
{ ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_POWER, BUTTON_NONE },
{ ACTION_QS_TOP, BUTTON_UP, BUTTON_NONE },
diff --git a/apps/keymaps/keymap-nwza860.c b/apps/keymaps/keymap-nwza860.c
index d455e23b62..899796552e 100644
--- a/apps/keymaps/keymap-nwza860.c
+++ b/apps/keymaps/keymap-nwza860.c
@@ -74,7 +74,8 @@ static const struct button_mapping button_context_keyboard[] = {
}; /* button_context_keyboard */
static const struct button_mapping button_context_quickscreen[] = {
- { ACTION_STD_CANCEL, BUTTON_PLAY, BUTTON_NONE },
+ { ACTION_STD_CONTEXT, BUTTON_PLAY|BUTTON_REPEAT, BUTTON_PLAY },
+ { ACTION_STD_CANCEL, BUTTON_PLAY|BUTTON_REL, BUTTON_PLAY },
{ ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE },
LAST_ITEM_IN_LIST
diff --git a/apps/keymaps/keymap-zen.c b/apps/keymaps/keymap-zen.c
index b0f24af459..04b65b3980 100644
--- a/apps/keymaps/keymap-zen.c
+++ b/apps/keymaps/keymap-zen.c
@@ -145,7 +145,8 @@ static const struct button_mapping button_context_keyboard[] = {
}; /* button_context_keyboard */
static const struct button_mapping button_context_quickscreen[] = {
- { ACTION_STD_CANCEL, BUTTON_SELECT, BUTTON_NONE },
+ { ACTION_STD_CONTEXT, BUTTON_SELECT|BUTTON_REPEAT, BUTTON_SELECT },
+ { ACTION_STD_CANCEL, BUTTON_SELECT|BUTTON_REL, BUTTON_SELECT },
{ ACTION_STD_CANCEL, BUTTON_BACK, BUTTON_NONE },
{ ACTION_STD_CANCEL, BUTTON_PLAYPAUSE, BUTTON_NONE },
{ ACTION_QS_TOP, BUTTON_UP, BUTTON_NONE },
diff --git a/apps/lang/english-us.lang b/apps/lang/english-us.lang
index bf5e9e93ce..4b35e2a82a 100644
--- a/apps/lang/english-us.lang
+++ b/apps/lang/english-us.lang
@@ -1694,12 +1694,6 @@
*: "Random"
</voice>
</phrase>
-### The 'desc' field for 'LANG_AUDIOSCROBBLER' differs from the english!
-### the previously used desc is commented below:
-### desc: "Last.fm Log" in the playback menu
-### The <source> section for 'LANG_AUDIOSCROBBLER:*' differs from the english!
-### the previously used one is commented below:
-### Last.fm Logger
<phrase>
id: LANG_AUDIOSCROBBLER
desc: "Last.fm Logger" in Plugin/apps/scrobbler
@@ -16099,7 +16093,6 @@
*: "Descending"
</voice>
</phrase>
-### This phrase is missing entirely, copying from english!
<phrase>
id: LANG_ALBUM_ART
desc: in Settings
@@ -16114,7 +16107,6 @@
*: "Album Art"
</voice>
</phrase>
-### This phrase is missing entirely, copying from english!
<phrase>
id: LANG_PREFER_EMBEDDED
desc: in Settings
@@ -16129,7 +16121,6 @@
*: "Prefer Embedded"
</voice>
</phrase>
-### This phrase is missing entirely, copying from english!
<phrase>
id: LANG_PREFER_IMAGE_FILE
desc: in Settings
@@ -16144,7 +16135,6 @@
*: "Prefer Image File"
</voice>
</phrase>
-### This phrase is missing entirely, copying from english!
<phrase>
id: LANG_FM_SYNC_RDS_TIME
desc: in radio screen and Settings
@@ -16162,7 +16152,6 @@
rds: "Sync RDS Time"
</voice>
</phrase>
-### This phrase is missing entirely, copying from english!
<phrase>
id: LANG_SORT_ALBUMS_BY
desc: in Settings
@@ -16177,7 +16166,6 @@
*: "Sort albums by"
</voice>
</phrase>
-### This phrase is missing entirely, copying from english!
<phrase>
id: LANG_ARTIST_PLUS_NAME
desc: in Settings
@@ -16192,7 +16180,6 @@
*: "Artist And Name"
</voice>
</phrase>
-### This phrase is missing entirely, copying from english!
<phrase>
id: LANG_ARTIST_PLUS_YEAR
desc: in Settings
@@ -16207,7 +16194,6 @@
*: "Artist And Year"
</voice>
</phrase>
-### This phrase is missing entirely, copying from english!
<phrase>
id: LANG_YEAR_SORT_ORDER
desc: in Settings
@@ -16222,7 +16208,6 @@
*: "Year sort order"
</voice>
</phrase>
-### This phrase is missing entirely, copying from english!
<phrase>
id: LANG_SHOW_YEAR_IN_ALBUM_TITLE
desc: in Settings
@@ -16237,7 +16222,6 @@
*: "Show year in album title"
</voice>
</phrase>
-### This phrase is missing entirely, copying from english!
<phrase>
id: LANG_WAIT_FOR_CACHE
desc: in Settings
@@ -16252,7 +16236,6 @@
*: "Cache needs to finish updating first!"
</voice>
</phrase>
-### This phrase is missing entirely, copying from english!
<phrase>
id: LANG_TRACK_INFO
desc: Track Info Title
@@ -16267,7 +16250,6 @@
*: "Track Info"
</voice>
</phrase>
-### This phrase is missing entirely, copying from english!
<phrase>
id: LANG_PLAY
desc: play selected file/directory, in playlist context menu
@@ -16282,7 +16264,6 @@
*: "Play"
</voice>
</phrase>
-### This phrase is missing entirely, copying from english!
<phrase>
id: LANG_PLAY_SHUFFLED
desc: play selected files in shuffled order, in playlist context menu
@@ -16297,7 +16278,6 @@
*: "Play Shuffled"
</voice>
</phrase>
-### This phrase is missing entirely, copying from english!
<phrase>
id: LANG_KEEP_CURRENT_TRACK_ON_REPLACE
desc: used in the playlist settings menu
@@ -16312,3 +16292,37 @@
*: "Keep Current Track When Replacing Playlist"
</voice>
</phrase>
+<phrase>
+ id: LANG_CLEAR_SETTINGS_ON_HOLD
+ desc: in the system sub menu
+ user: core
+ <source>
+ *: none
+ clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
+ </source>
+ <dest>
+ *: none
+ clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
+ </dest>
+ <voice>
+ *: none
+ clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_REWIND_ACROSS_TRACKS
+ desc: in playback settings menu
+ user: core
+ <source>
+ *: "Rewind Across Tracks"
+ </source>
+ <dest>
+ *: "Rewind Across Tracks"
+ </dest>
+ <voice>
+ *: "Rewind across tracks"
+ </voice>
+</phrase>
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index e8d646b258..29a2527bec 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -3645,22 +3645,22 @@
user: core
<source>
*: none
+ rtc: "ON = Set"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Set"
gogearsa9200,samsungyh*: "PLAY = Set"
iriverh100,iriverh120,iriverh300: "NAVI = Set"
mpiohd300: "ENTER = Set"
mrobe500: "HEART = Set"
- rtc: "ON = Set"
vibe500: "OK = Set"
</source>
<dest>
*: none
+ rtc: "ON = Set"
gigabeat*,iaudiom5,iaudiox5,ipod*,iriverh10,iriverh10_5gb,mrobe100,sansac200*,sansaclip*,sansaconnect,sansae200*,sansafuze*: "SELECT = Set"
gogearsa9200,samsungyh*: "PLAY = Set"
iriverh100,iriverh120,iriverh300: "NAVI = Set"
mpiohd300: "ENTER = Set"
mrobe500: "HEART = Set"
- rtc: "ON = Set"
vibe500: "OK = Set"
</dest>
<voice>
@@ -3674,6 +3674,7 @@
user: core
<source>
*: none
+ rtc: "OFF = Revert"
gigabeatfx,mrobe500: "POWER = Revert"
gigabeats,sansafuzeplus: "BACK = Revert"
gogearsa9200: "LEFT = Revert"
@@ -3682,12 +3683,12 @@
iriverh10,iriverh10_5gb,sansae200*,sansafuze*: "PREV = Revert"
iriverh100,iriverh120,iriverh300: "STOP = Revert"
mrobe100: "DISPLAY = Revert"
- rtc: "OFF = Revert"
samsungyh*: "REW = Revert"
vibe500: "CANCEL = Revert"
</source>
<dest>
*: none
+ rtc: "OFF = Revert"
gigabeatfx,mrobe500: "POWER = Revert"
gigabeats,sansafuzeplus: "BACK = Revert"
gogearsa9200: "LEFT = Revert"
@@ -3696,7 +3697,6 @@
iriverh10,iriverh10_5gb,sansae200*,sansafuze*: "PREV = Revert"
iriverh100,iriverh120,iriverh300: "STOP = Revert"
mrobe100: "DISPLAY = Revert"
- rtc: "OFF = Revert"
samsungyh*: "REW = Revert"
vibe500: "CANCEL = Revert"
</dest>
@@ -16388,3 +16388,17 @@
clear_settings_on_hold, iriverh10: "Clear settings when reset button is held during startup"
</voice>
</phrase>
+<phrase>
+ id: LANG_REWIND_ACROSS_TRACKS
+ desc: in playback settings menu
+ user: core
+ <source>
+ *: "Rewind Across Tracks"
+ </source>
+ <dest>
+ *: "Rewind Across Tracks"
+ </dest>
+ <voice>
+ *: "Rewind across tracks"
+ </voice>
+</phrase>
diff --git a/apps/lang/italiano.lang b/apps/lang/italiano.lang
index 0688ca7846..1dc3319e85 100644
--- a/apps/lang/italiano.lang
+++ b/apps/lang/italiano.lang
@@ -13069,16 +13069,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album]"
+ *: ""
</source>
<dest>
- *: "[Album]"
+ *: ""
</dest>
<voice>
- *: "Album"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -13198,16 +13198,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Artist]"
+ *: ""
</source>
<dest>
- *: "[Artista]"
+ *: ""
</dest>
<voice>
- *: "Artista"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -13257,16 +13257,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_TITLE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Title]"
+ *: ""
</source>
<dest>
- *: "[Titolo]"
+ *: ""
</dest>
<voice>
- *: "Titolo"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -13532,16 +13532,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_DURATION
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Duration]"
+ *: ""
</source>
<dest>
- *: "[Durata]"
+ *: ""
</dest>
<voice>
- *: "Durata"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15507,16 +15507,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_LIST_AND_PLAY_NEXT
- desc: in onplay menu. Replace current playlist with selected tracks
+ desc: deprecated
user: core
<source>
- *: "Clear List & Play Next"
+ *: ""
</source>
<dest>
- *: "Cancella Lista e Riproduci Successivo"
+ *: ""
</dest>
<voice>
- *: "Cancella Lista e Riproduci Successivo"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15637,16 +15637,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_LIST_AND_PLAY_SHUFFLED
- desc: in onplay menu. Replace current playlist with selected tracks in random order.
+ desc: deprecated
user: core
<source>
- *: "Clear List & Play Shuffled"
+ *: ""
</source>
<dest>
- *: "Cancella Lista e Riproduci in Ordine Casuale"
+ *: ""
</dest>
<voice>
- *: "Cancella Lista e Riproduci in Ordine Casuale"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15874,128 +15874,128 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUMARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album Artist]"
+ *: ""
</source>
<dest>
- *: "[Artista Album]"
+ *: ""
</dest>
<voice>
- *: "Artista Album"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_GENRE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Genre]"
+ *: ""
</source>
<dest>
- *: "[Genere]"
+ *: ""
</dest>
<voice>
- *: "Genere"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_COMMENT
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Comment]"
+ *: ""
</source>
<dest>
- *: "[Commento]"
+ *: ""
</dest>
<voice>
- *: "Commento"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_COMPOSER
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Composer]"
+ *: ""
</source>
<dest>
- *: "[Compositore]"
+ *: ""
</dest>
<voice>
- *: "Compositore"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_YEAR
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Year]"
+ *: ""
</source>
<dest>
- *: "[Anno]"
+ *: ""
</dest>
<voice>
- *: "Anno"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_TRACKNUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Tracknum]"
+ *: ""
</source>
<dest>
- *: "[Numtraccia]"
+ *: ""
</dest>
<voice>
- *: "Numero traccia"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_DISCNUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Discnum]"
+ *: ""
</source>
<dest>
- *: "[Numdisco]"
+ *: ""
</dest>
<voice>
- *: "Numero disco"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_FREQUENCY
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Frequency]"
+ *: ""
</source>
<dest>
- *: "[Frequenza]"
+ *: ""
</dest>
<voice>
- *: "Frequenza"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_BITRATE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Bitrate]"
+ *: ""
</source>
<dest>
- *: "[Bitrate]"
+ *: ""
</dest>
<voice>
- *: "Bitrate"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -16239,3 +16239,79 @@
*: "La cache deve prima completare l'aggiornamento!"
</voice>
</phrase>
+<phrase>
+ id: LANG_TRACK_INFO
+ desc: Track Info Title
+ user: core
+ <source>
+ *: "Track Info"
+ </source>
+ <dest>
+ *: "Info Traccia"
+ </dest>
+ <voice>
+ *: "Info Traccia"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY
+ desc: play selected file/directory, in playlist context menu
+ user: core
+ <source>
+ *: "Play"
+ </source>
+ <dest>
+ *: "Riproduci"
+ </dest>
+ <voice>
+ *: "Riproduci"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_SHUFFLED
+ desc: play selected files in shuffled order, in playlist context menu
+ user: core
+ <source>
+ *: "Play Shuffled"
+ </source>
+ <dest>
+ *: "Riproduci Casualmente"
+ </dest>
+ <voice>
+ *: "Riproduci Casualmente"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_KEEP_CURRENT_TRACK_ON_REPLACE
+ desc: used in the playlist settings menu
+ user: core
+ <source>
+ *: "Keep Current Track When Replacing Playlist"
+ </source>
+ <dest>
+ *: "Mantieni La Traccia Corrente Quando Sostituisci La Playlist"
+ </dest>
+ <voice>
+ *: "Mantieni La Traccia Corrente Quando Sostituisci La Playlist"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_SETTINGS_ON_HOLD
+ desc: in the system sub menu
+ user: core
+ <source>
+ *: none
+ clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
+ </source>
+ <dest>
+ *: none
+ clear_settings_on_hold,iriverh10: "Cancella le impostazioni quando si tiene premuto il pulsante reset durante l'avvio"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Cancella le impostazioni quando l'interruttore di blocco è attivo durante l'avvio"
+ </dest>
+ <voice>
+ *: none
+ clear_settings_on_hold,iriverh10: "Cancella le impostazioni quando si tiene premuto il pulsante reset durante l'avvio"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Cancella le impostazioni quando l'interruttore di blocco è attivo durante l'avvio"
+ </voice>
+</phrase>
diff --git a/apps/lang/nederlands.lang b/apps/lang/nederlands.lang
index 5fbd93df81..83139cc6de 100644
--- a/apps/lang/nederlands.lang
+++ b/apps/lang/nederlands.lang
@@ -4250,31 +4250,16 @@
</phrase>
<phrase>
id: LANG_ALARM_MOD_KEYS
- desc: Shown key functions in alarm menu (for the RTC alarm mod).
+ desc: deprecated
user: core
<source>
- *: none
- alarm: "PLAY=Set OFF=Cancel"
- gigabeats: "SELECT=Set POWER=Cancel"
- ipod*: "SELECT=Set MENU=Cancel"
- iriverh10,iriverh10_5gb: "SELECT=Set PREV=Cancel"
- mpiohd300: "ENTER=Set MENU=Cancel"
- sansafuzeplus: "SELECT=Set BACK=Cancel"
- vibe500: "OK=Set C=Cancel"
+ *: ""
</source>
<dest>
- *: none
- alarm: "PLAY=Instellen, OFF=Annuleren"
- gigabeats: "SELECT=Instellen, POWER=Annuleren"
- ipod*: "SELECT=Instellen, MENU=Annuleren"
- iriverh10,iriverh10_5gb: "SELECT=Instellen, PREV=Annuleren"
- mpiohd300: "ENTER=Instellen, MENU=Annuleren"
- sansafuzeplus: "SELECT=Instellen, BACK=Annuleren"
- vibe500: "OK=Instellen, C=Annuleren"
+ *: ""
</dest>
<voice>
- *: none
- alarm,ipod*: ""
+ *: ""
</voice>
</phrase>
<phrase>
@@ -7580,10 +7565,10 @@
</phrase>
<phrase>
id: LANG_AUDIOSCROBBLER
- desc: "Last.fm Log" in the playback menu
+ desc: "Last.fm Logger" in Plugin/apps/scrobbler
user: core
<source>
- *: "Last.fm Log"
+ *: "Last.fm Logger"
</source>
<dest>
*: "Last.fm-logboek"
@@ -14430,58 +14415,58 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Artist]"
+ *: ""
</source>
<dest>
- *: "[Artiest]"
+ *: ""
</dest>
<voice>
- *: "Artiest"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_TITLE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Title]"
+ *: ""
</source>
<dest>
- *: "[Titel]"
+ *: ""
</dest>
<voice>
- *: "Titel"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album]"
+ *: ""
</source>
<dest>
- *: "[Album]"
+ *: ""
</dest>
<voice>
- *: "Album"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_DURATION
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Duration]"
+ *: ""
</source>
<dest>
- *: "[Looptijd]"
+ *: ""
</dest>
<voice>
- *: "Looptijd"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -14683,7 +14668,7 @@
lowmem: none
</dest>
<voice>
- *: "CPU Boost"
+ *: "CPU-boost"
lowmem: none
</voice>
</phrase>
@@ -15744,16 +15729,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_LIST_AND_PLAY_NEXT
- desc: in onplay menu. Replace current playlist with selected tracks
+ desc: deprecated
user: core
<source>
- *: "Clear List & Play Next"
+ *: ""
</source>
<dest>
- *: "Lijst Wissen en Volgende Afspelen"
+ *: ""
</dest>
<voice>
- *: "Lijst Wissen en Volgende Afspelen"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15814,16 +15799,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_LIST_AND_PLAY_SHUFFLED
- desc: in onplay menu. Replace current playlist with selected tracks in random order.
+ desc: deprecated
user: core
<source>
- *: "Clear List & Play Shuffled"
+ *: ""
</source>
<dest>
- *: "Lijst Wissen en in Geschude Volgorde Afspelen"
+ *: ""
</dest>
<voice>
- *: "Lijst Wissen en in Geschude Volgorde Afspelen"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15898,128 +15883,128 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUMARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album Artist]"
+ *: ""
</source>
<dest>
- *: "[Album Artiest]"
+ *: ""
</dest>
<voice>
- *: "Album Artiest"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_GENRE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Genre]"
+ *: ""
</source>
<dest>
- *: "[Genre]"
+ *: ""
</dest>
<voice>
- *: "Genre"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_COMMENT
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Comment]"
+ *: ""
</source>
<dest>
- *: "[Commentaar]"
+ *: ""
</dest>
<voice>
- *: "Commentaar"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_COMPOSER
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Composer]"
+ *: ""
</source>
<dest>
- *: "[Componist]"
+ *: ""
</dest>
<voice>
- *: "Componist"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_YEAR
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Year]"
+ *: ""
</source>
<dest>
- *: "[Jaar]"
+ *: ""
</dest>
<voice>
- *: "Jaar"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_TRACKNUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Tracknum]"
+ *: ""
</source>
<dest>
- *: "[Tracknum]"
+ *: ""
</dest>
<voice>
- *: "Tracknummer"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_DISCNUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Discnum]"
+ *: ""
</source>
<dest>
- *: "[Schijfnum]"
+ *: ""
</dest>
<voice>
- *: "Schijfnummer"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_FREQUENCY
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Frequency]"
+ *: ""
</source>
<dest>
- *: "[Frequentie]"
+ *: ""
</dest>
<voice>
- *: "Frequentie"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_BITRATE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Bitrate]"
+ *: ""
</source>
<dest>
- *: "[Bitsnelheid]"
+ *: ""
</dest>
<voice>
- *: "Bitsnelheid"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -16120,3 +16105,222 @@
*: "Aflopend"
</voice>
</phrase>
+<phrase>
+ id: LANG_ALBUM_ART
+ desc: in Settings
+ user: core
+ <source>
+ *: "Album Art"
+ </source>
+ <dest>
+ *: "Albumhoezen"
+ </dest>
+ <voice>
+ *: "Albumhoezen"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_EMBEDDED
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Embedded"
+ </source>
+ <dest>
+ *: "Liever Ingesloten"
+ </dest>
+ <voice>
+ *: "Liever Ingesloten"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PREFER_IMAGE_FILE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Prefer Image File"
+ </source>
+ <dest>
+ *: "Liever Afbeeldingsbestand"
+ </dest>
+ <voice>
+ *: "Liever Afbeeldingsbestand"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_FM_SYNC_RDS_TIME
+ desc: in radio screen and Settings
+ user: core
+ <source>
+ *: none
+ rds: "Sync RDS Time"
+ </source>
+ <dest>
+ *: none
+ rds: "RDS-tijd Synchroniseren"
+ </dest>
+ <voice>
+ *: none
+ rds: "RDS Tijd Synchroniseren"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SORT_ALBUMS_BY
+ desc: in Settings
+ user: core
+ <source>
+ *: "Sort albums by"
+ </source>
+ <dest>
+ *: "Sorteer albums op"
+ </dest>
+ <voice>
+ *: "Sorteer albums op"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_NAME
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Name"
+ </source>
+ <dest>
+ *: "Artiest + Naam"
+ </dest>
+ <voice>
+ *: "Artiest En Naam"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_ARTIST_PLUS_YEAR
+ desc: in Settings
+ user: core
+ <source>
+ *: "Artist + Year"
+ </source>
+ <dest>
+ *: "Artiest + Jaar"
+ </dest>
+ <voice>
+ *: "Artiest En Jaar"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_YEAR_SORT_ORDER
+ desc: in Settings
+ user: core
+ <source>
+ *: "Year sort order"
+ </source>
+ <dest>
+ *: "Sorteervolgorde jaar"
+ </dest>
+ <voice>
+ *: "Sorteer volgorde jaar"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_SHOW_YEAR_IN_ALBUM_TITLE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Show year in album title"
+ </source>
+ <dest>
+ *: "Jaar in albumtitel weergeven"
+ </dest>
+ <voice>
+ *: "Jaar in albumtitel weergeven"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_WAIT_FOR_CACHE
+ desc: in Settings
+ user: core
+ <source>
+ *: "Cache needs to finish updating first!"
+ </source>
+ <dest>
+ *: "Cache moet eerst de update voltooien!"
+ </dest>
+ <voice>
+ *: "Cache moet eerst de update voltooien!"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_TRACK_INFO
+ desc: Track Info Title
+ user: core
+ <source>
+ *: "Track Info"
+ </source>
+ <dest>
+ *: "Nummer Info"
+ </dest>
+ <voice>
+ *: "Nummer Info"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY
+ desc: play selected file/directory, in playlist context menu
+ user: core
+ <source>
+ *: "Play"
+ </source>
+ <dest>
+ *: "Speel"
+ </dest>
+ <voice>
+ *: "Speel"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_SHUFFLED
+ desc: play selected files in shuffled order, in playlist context menu
+ user: core
+ <source>
+ *: "Play Shuffled"
+ </source>
+ <dest>
+ *: "Geschud Afspelen"
+ </dest>
+ <voice>
+ *: "Geschud Afspelen"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_KEEP_CURRENT_TRACK_ON_REPLACE
+ desc: used in the playlist settings menu
+ user: core
+ <source>
+ *: "Keep Current Track When Replacing Playlist"
+ </source>
+ <dest>
+ *: "Huidige Track Behouden bij het Vervangen van een Afspeellijst"
+ </dest>
+ <voice>
+ *: "Huidige Track Behouden bij het Vervangen van een Afspeellijst"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_SETTINGS_ON_HOLD
+ desc: in the system sub menu
+ user: core
+ <source>
+ *: none
+ clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
+ </source>
+ <dest>
+ *: none
+ clear_settings_on_hold,iriverh10: "Instellingen wissen wanneer de resetknop wordt ingedrukt tijdens het opstarten"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Instellingen wissen wanneer de houd-schakelaar aan staat tijdens het opstarten"
+ </dest>
+ <voice>
+ *: none
+ clear_settings_on_hold,iriverh10: "Instellingen wissen wanneer de resetknop wordt ingedrukt tijdens het opstarten"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Instellingen wissen wanneer de houd schakelaar aan staat tijdens het opstarten"
+ </voice>
+</phrase>
diff --git a/apps/lang/polski.lang b/apps/lang/polski.lang
index 4cef4ba59a..f9650475db 100644
--- a/apps/lang/polski.lang
+++ b/apps/lang/polski.lang
@@ -16301,3 +16301,23 @@
*: "Zachowaj bieżący utwór podczas zastępowania listy odtwarzania"
</voice>
</phrase>
+<phrase>
+ id: LANG_CLEAR_SETTINGS_ON_HOLD
+ desc: in the system sub menu
+ user: core
+ <source>
+ *: none
+ clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
+ </source>
+ <dest>
+ *: none
+ clear_settings_on_hold,iriverh10: "Wyczyść ustawienia, gdy przycisk resetowania będzie przytrzymany podczas uruchamiania"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Wyczyść ustawienia, gdy przełącznik blokady będzie włączony podczas uruchamiania"
+ </dest>
+ <voice>
+ *: none
+ clear_settings_on_hold,iriverh10: "Wyczyść ustawienia, gdy przycisk resetowania będzie przytrzymany podczas uruchamiania"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Wyczyść ustawienia, gdy przełącznik blokady będzie włączony podczas uruchamiania"
+ </voice>
+</phrase>
diff --git a/apps/lang/srpski.lang b/apps/lang/srpski.lang
index 8e64e3f938..dc7b3e3825 100644
--- a/apps/lang/srpski.lang
+++ b/apps/lang/srpski.lang
@@ -1473,10 +1473,10 @@
*: "Replaygain"
</source>
<dest>
- *: "Replaygain"
+ *: "Риплејгејн"
</dest>
<voice>
- *: "Replaygain"
+ *: "Риплејгејн"
</voice>
</phrase>
<phrase>
@@ -3400,13 +3400,13 @@
</source>
<dest>
*: none
- battery_types: "NiMH"
+ battery_types: "НиМХ"
xduoox3: "Старија (1500 mAh)"
</dest>
<voice>
*: none
battery_types: "Никл метал хидридна"
- xduoox3: "Старија (1500 милиампер часова)"
+ xduoox3: "Старија 1500 милиампер часова"
</voice>
</phrase>
<phrase>
@@ -4936,11 +4936,11 @@
</source>
<dest>
*: none
- recording: "MPEG Layer 3"
+ recording: "МПЕГ Слој 3"
</dest>
<voice>
*: none
- recording: "MPEG Layer 3"
+ recording: "МПЕГ Слој 3"
</voice>
</phrase>
<phrase>
@@ -4953,11 +4953,11 @@
</source>
<dest>
*: none
- recording: "PCM Wave"
+ recording: "ПЦМ Талас"
</dest>
<voice>
*: none
- recording: "PCM Wave"
+ recording: "ПЦМ Талас"
</voice>
</phrase>
<phrase>
@@ -6004,13 +6004,13 @@
</source>
<dest>
*: "Инт:"
- hibylinux: "mSD:"
- xduoox3: "mSD1:"
+ hibylinux: "мСД:"
+ xduoox3: "мСД1:"
</dest>
<voice>
*: "Интерни"
- hibylinux: "мајкро Ес Де"
- xduoox3: "мајкро Ес Де 1"
+ hibylinux: "микро Ес Де"
+ xduoox3: "микро Ес Де 1"
</voice>
</phrase>
<phrase>
@@ -6025,15 +6025,15 @@
</source>
<dest>
*: none
- multivolume: "HD1"
- sansac200*,sansaclipplus,sansae200*,sansafuze*: "mSD:"
- xduoox3: "mSD2:"
+ multivolume: "ХД1"
+ sansac200*,sansaclipplus,sansae200*,sansafuze*: "мСД:"
+ xduoox3: "мСД2:"
</dest>
<voice>
*: none
multivolume: "Ха Де 1"
- sansac200*,sansaclipplus,sansae200*,sansafuze*: "мајкро Ес Де"
- xduoox3: "мајкро Ес Де 2"
+ sansac200*,sansaclipplus,sansae200*,sansafuze*: "микро Ес Де"
+ xduoox3: "микро Ес Де 2"
</voice>
</phrase>
<phrase>
@@ -9581,7 +9581,7 @@
</dest>
<voice>
*: none
- recording: "величина"
+ recording: ""
</voice>
</phrase>
<phrase>
@@ -9674,7 +9674,7 @@
</dest>
<voice>
*: none
- recording: "време дељења"
+ recording: ""
</voice>
</phrase>
<phrase>
@@ -9705,7 +9705,7 @@
</dest>
<voice>
*: none
- recording: "клип"
+ recording: ""
</voice>
</phrase>
<phrase>
@@ -9735,11 +9735,11 @@
</source>
<dest>
*: none
- recording: "Фајл:"
+ recording: "Датотека:"
</dest>
<voice>
*: none
- recording: "фајл"
+ recording: ""
</voice>
</phrase>
<phrase>
@@ -9797,7 +9797,7 @@
</source>
<dest>
*: none
- agc: "AGC"
+ agc: "АРП"
</dest>
<voice>
*: none
@@ -10465,11 +10465,11 @@
</source>
<dest>
*: none
- pitchscreen: "Rate"
+ pitchscreen: "Рата"
</dest>
<voice>
*: none
- pitchscreen: "Рејт"
+ pitchscreen: "Рата"
</voice>
</phrase>
<phrase>
@@ -12192,10 +12192,10 @@
*: "Android Debug Bridge"
</source>
<dest>
- *: "Android Debug Bridge"
+ *: "Андроид Прегледни Мост"
</dest>
<voice>
- *: "Android Debug Bridge"
+ *: "Андроид Прегледни Мост"
</voice>
</phrase>
<phrase>
@@ -12248,10 +12248,10 @@
*: "Haas Surround"
</source>
<dest>
- *: "Haas Surround"
+ *: "Хаас Просторни Звук"
</dest>
<voice>
- *: "Haas Surround"
+ *: "Хаас Просторни Звук"
</voice>
</phrase>
<phrase>
@@ -13008,16 +13008,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album]"
+ *: ""
</source>
<dest>
- *: "[Албум]"
+ *: ""
</dest>
<voice>
- *: "Албум"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -13350,16 +13350,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_TITLE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Title]"
+ *: ""
</source>
<dest>
- *: "[Наслов]"
+ *: ""
</dest>
<voice>
- *: "Наслов"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -13765,16 +13765,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Artist]"
+ *: ""
</source>
<dest>
- *: "[Уметник]"
+ *: ""
</dest>
<voice>
- *: "Уметник"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -13869,16 +13869,16 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_DURATION
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Duration]"
+ *: ""
</source>
<dest>
- *: "[Трајање]"
+ *: ""
</dest>
<voice>
- *: "Трајање"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15732,16 +15732,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_LIST_AND_PLAY_NEXT
- desc: in onplay menu. Replace current playlist with selected tracks
+ desc: deprecated
user: core
<source>
- *: "Clear List & Play Next"
+ *: ""
</source>
<dest>
- *: "Очисти листу & репродукуј наредну"
+ *: ""
</dest>
<voice>
- *: "Очисти листу и репродукуј наредну"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15802,16 +15802,16 @@
</phrase>
<phrase>
id: LANG_CLEAR_LIST_AND_PLAY_SHUFFLED
- desc: in onplay menu. Replace current playlist with selected tracks in random order.
+ desc: deprecated
user: core
<source>
- *: "Clear List & Play Shuffled"
+ *: ""
</source>
<dest>
- *: "Очисти листу & репродукуј измешано"
+ *: ""
</dest>
<voice>
- *: "Очисти листу & репродукуј измешано"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -15872,128 +15872,128 @@
</phrase>
<phrase>
id: LANG_PROPERTIES_ALBUMARTIST
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Album Artist]"
+ *: ""
</source>
<dest>
- *: "[Уметник албума]"
+ *: ""
</dest>
<voice>
- *: "Уметник албума"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_GENRE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Genre]"
+ *: ""
</source>
<dest>
- *: "[Жанр]"
+ *: ""
</dest>
<voice>
- *: "Жанр"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_COMMENT
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Comment]"
+ *: ""
</source>
<dest>
- *: "[Коментар]"
+ *: ""
</dest>
<voice>
- *: "Коментар"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_COMPOSER
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Composer]"
+ *: ""
</source>
<dest>
- *: "[Композитор]"
+ *: ""
</dest>
<voice>
- *: "Композитор"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_YEAR
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Year]"
+ *: ""
</source>
<dest>
- *: "[Година]"
+ *: ""
</dest>
<voice>
- *: "Година"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_TRACKNUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Tracknum]"
+ *: ""
</source>
<dest>
- *: "[Брнумере]"
+ *: ""
</dest>
<voice>
- *: "Број нумере"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_DISCNUM
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Discnum]"
+ *: ""
</source>
<dest>
- *: "[Брдиска]"
+ *: ""
</dest>
<voice>
- *: "Број диска"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_FREQUENCY
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Frequency]"
+ *: ""
</source>
<dest>
- *: "[Фреквенција]"
+ *: ""
</dest>
<voice>
- *: "Фреквенција"
+ *: ""
</voice>
</phrase>
<phrase>
id: LANG_PROPERTIES_BITRATE
- desc: in properties plugin
+ desc: deprecated
user: core
<source>
- *: "[Bitrate]"
+ *: ""
</source>
<dest>
- *: "[Битски_проток]"
+ *: ""
</dest>
<voice>
- *: "Битски проток"
+ *: ""
</voice>
</phrase>
<phrase>
@@ -16018,7 +16018,7 @@
*: "dAY"
</source>
<dest>
- *: "dAY"
+ *: "dmY"
</dest>
<voice>
*: ""
@@ -16237,3 +16237,79 @@
*: "Најпре мора да се освежи кеш!"
</voice>
</phrase>
+<phrase>
+ id: LANG_TRACK_INFO
+ desc: Track Info Title
+ user: core
+ <source>
+ *: "Track Info"
+ </source>
+ <dest>
+ *: "Подаци о Песми"
+ </dest>
+ <voice>
+ *: "Подаци о Песми"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY
+ desc: play selected file/directory, in playlist context menu
+ user: core
+ <source>
+ *: "Play"
+ </source>
+ <dest>
+ *: "Покрени"
+ </dest>
+ <voice>
+ *: "Покрени"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_PLAY_SHUFFLED
+ desc: play selected files in shuffled order, in playlist context menu
+ user: core
+ <source>
+ *: "Play Shuffled"
+ </source>
+ <dest>
+ *: "Покрени Разбацано"
+ </dest>
+ <voice>
+ *: "Покрени Разбацано"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_KEEP_CURRENT_TRACK_ON_REPLACE
+ desc: used in the playlist settings menu
+ user: core
+ <source>
+ *: "Keep Current Track When Replacing Playlist"
+ </source>
+ <dest>
+ *: "Задржите Тренутну Нумеру Када Замењујете Плејлисту"
+ </dest>
+ <voice>
+ *: "Задржите Тренутну Нумеру Када Замењујете Плејлисту"
+ </voice>
+</phrase>
+<phrase>
+ id: LANG_CLEAR_SETTINGS_ON_HOLD
+ desc: in the system sub menu
+ user: core
+ <source>
+ *: none
+ clear_settings_on_hold,iriverh10: "Clear settings when reset button is held during startup"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Clear settings when hold switch is on during startup"
+ </source>
+ <dest>
+ *: none
+ clear_settings_on_hold,iriverh10: "Обришите подешавања када се дугме за ресетовање држи током покретања"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Обришите подешавања када је прекидач за чекање укључен током покретања"
+ </dest>
+ <voice>
+ *: none
+ clear_settings_on_hold,iriverh10: "Обришите подешавања када се дугме за ресетовање држи током покретања"
+ ipod4g,ipodcolor,ipodmini1g,ipodmini2g,ipodnano1g,ipodvideo: "Обришите подешавања када је прекидач за чекање укључен током покретања"
+ </voice>
+</phrase>
diff --git a/apps/logfdisp.c b/apps/logfdisp.c
index b139f30ac7..efbfa192f5 100644
--- a/apps/logfdisp.c
+++ b/apps/logfdisp.c
@@ -35,7 +35,9 @@
#include "logfdisp.h"
#include "action.h"
#include "splash.h"
-
+#if CONFIG_RTC
+#include "misc.h"
+#endif /*CONFIG_RTC*/
int compute_nb_lines(int w, struct font* font)
{
int i, nb_lines;
@@ -212,10 +214,6 @@ bool logfdisplay(void)
bool logfdump(void)
{
int fd;
-#if CONFIG_RTC
- struct tm *nowtm;
- char fname[MAX_PATH];
-#endif
splashf(HZ, "Log File Dumped");
@@ -227,11 +225,12 @@ bool logfdump(void)
logfenabled = false;
#if CONFIG_RTC
- nowtm = get_time();
- snprintf(fname, MAX_PATH, "%s/logf_%04d%02d%02d%02d%02d%02d.txt", ROCKBOX_DIR,
+ char fname[MAX_PATH];
+ struct tm *nowtm = get_time();
+ fd = open_pathfmt(fname, sizeof(fname), O_CREAT|O_WRONLY|O_TRUNC,
+ "%s/logf_%04d%02d%02d%02d%02d%02d.txt", ROCKBOX_DIR,
nowtm->tm_year + 1900, nowtm->tm_mon + 1, nowtm->tm_mday,
nowtm->tm_hour, nowtm->tm_min, nowtm->tm_sec);
- fd = open(fname, O_CREAT|O_WRONLY|O_TRUNC);
#else
fd = open(ROCKBOX_DIR "/logf.txt", O_CREAT|O_WRONLY|O_TRUNC, 0666);
#endif
diff --git a/apps/main.c b/apps/main.c
index 59932d6185..f86d91e42e 100644
--- a/apps/main.c
+++ b/apps/main.c
@@ -288,13 +288,15 @@ static void init_tagcache(void)
#endif
if (lang_is_rtl())
{
- splashf(0, "[%d/%d] %s", ret, tagcache_get_max_commit_step(),
- str(LANG_TAGCACHE_INIT));
+ splash_progress(ret, tagcache_get_max_commit_step(),
+ "[%d/%d] %s", ret, tagcache_get_max_commit_step(),
+ str(LANG_TAGCACHE_INIT));
}
else
{
- splashf(0, "%s [%d/%d]", str(LANG_TAGCACHE_INIT), ret,
- tagcache_get_max_commit_step());
+ splash_progress(ret, tagcache_get_max_commit_step(),
+ "%s [%d/%d]", str(LANG_TAGCACHE_INIT), ret,
+ tagcache_get_max_commit_step());
}
clear = true;
}
@@ -595,7 +597,7 @@ static void init(void)
CHART("<init_dircache(true)");
#ifdef HAVE_TAGCACHE
if (rc < 0)
- remove(TAGCACHE_STATEFILE);
+ tagcache_remove_statefile();
#endif /* HAVE_TAGCACHE */
#endif /* HAVE_DIRCACHE */
diff --git a/apps/menu.c b/apps/menu.c
index ab5578dede..eb3adcc037 100644
--- a/apps/menu.c
+++ b/apps/menu.c
@@ -260,7 +260,6 @@ static int init_menu_lists(const struct menu_item_ex *menu,
if(global_settings.talk_menu)
gui_synclist_set_voice_callback(lists, talk_menu_item);
gui_synclist_set_nb_items(lists,current_subitems_count);
- gui_synclist_limit_scroll(lists,true);
gui_synclist_select_item(lists, find_menu_selection(selected));
get_menu_callback(menu,&menu_callback);
@@ -334,10 +333,7 @@ void do_setting_screen(const struct settings_list *setting, const char * title,
if (setting->flags&F_PADTITLE)
{
int i = 0, len;
- if (setting->lang_id == -1)
- title = (char*)setting->cfg_vals;
- else
- title = P2STR((unsigned char*)title);
+ title = P2STR((unsigned char*)title);
len = strlen(title);
while (i < MAX_PATH-1)
{
@@ -451,19 +447,25 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
action = new_action;
}
- if (LIKELY(gui_synclist_do_button(&lists, &action, LIST_WRAP_UNLESS_HELD)))
+ if (LIKELY(gui_synclist_do_button(&lists, &action)))
continue;
#ifdef HAVE_QUICKSCREEN
else if (action == ACTION_STD_QUICKSCREEN)
{
- if (global_settings.shortcuts_replaces_qs)
+ if (global_settings.shortcuts_replaces_qs ||
+ quick_screen_quick(action) == QUICKSCREEN_GOTO_SHORTCUTS_MENU)
{
+ int last_screen = global_status.last_screen;
global_status.last_screen = GO_TO_SHORTCUTMENU;
- ret = quick_screen_quick(action);
- done = true;
+ int shortcut_ret = do_shortcut_menu(NULL);
+ if (shortcut_ret == GO_TO_PREVIOUS)
+ global_status.last_screen = last_screen;
+ else
+ {
+ ret = shortcut_ret;
+ done = true;
+ }
}
- else
- quick_screen_quick(action);
redraw_lists = true;
}
#endif
@@ -517,17 +519,17 @@ int do_menu(const struct menu_item_ex *start_menu, int *start_selected,
MENUITEM_STRINGLIST(notquickscreen_able_option,
ID2P(LANG_ONPLAY_MENU_TITLE), NULL,
ID2P(LANG_RESET_SETTING));
- const struct menu_item_ex *menu;
+ const struct menu_item_ex *context_menu;
const struct settings_list *setting =
find_setting(temp->variable, NULL);
#ifdef HAVE_QUICKSCREEN
if (is_setting_quickscreenable(setting))
- menu = &quickscreen_able_option;
+ context_menu = &quickscreen_able_option;
else
#endif
- menu = &notquickscreen_able_option;
+ context_menu = &notquickscreen_able_option;
- int msel = do_menu(menu, NULL, NULL, false);
+ int msel = do_menu(context_menu, NULL, NULL, false);
switch (msel)
{
diff --git a/apps/menus/display_menu.c b/apps/menus/display_menu.c
index 6ed0f34ab6..b228095bb3 100644
--- a/apps/menus/display_menu.c
+++ b/apps/menus/display_menu.c
@@ -332,22 +332,7 @@ MENUITEM_SETTING(list_accel_start_delay,
&global_settings.list_accel_start_delay, NULL);
MENUITEM_SETTING(list_accel_wait, &global_settings.list_accel_wait, NULL);
#endif /* HAVE_WHEEL_ACCELERATION */
-static int screenscroll_callback(int action,
- const struct menu_item_ex *this_item,
- struct gui_synclist *this_list)
-{
- (void)this_item;
- (void)this_list;
- switch (action)
- {
- case ACTION_EXIT_MENUITEM:
- gui_list_screen_scroll_out_of_view(global_settings.offset_out_of_view);
- break;
- }
- return action;
-}
-MENUITEM_SETTING(offset_out_of_view, &global_settings.offset_out_of_view,
- screenscroll_callback);
+MENUITEM_SETTING(offset_out_of_view, &global_settings.offset_out_of_view, NULL);
MENUITEM_SETTING(screen_scroll_step, &global_settings.screen_scroll_step, NULL);
MENUITEM_SETTING(scroll_paginated, &global_settings.scroll_paginated, NULL);
@@ -359,7 +344,6 @@ static int listwraparound_callback(int action,
switch (action)
{
case ACTION_EXIT_MENUITEM:
- gui_synclist_limit_scroll(this_list, !global_settings.list_wraparound);
gui_synclist_init_display_settings(this_list);
break;
}
diff --git a/apps/menus/eq_menu.c b/apps/menus/eq_menu.c
index 109c3c9ab7..c25d19e352 100644
--- a/apps/menus/eq_menu.c
+++ b/apps/menus/eq_menu.c
@@ -154,9 +154,9 @@ static int32_t get_dec_talkid(int value, int unit)
static const struct int_setting gain_int_setting = {
.option_callback = NULL,
.unit = UNIT_DB,
+ .step = EQ_GAIN_STEP,
.min = EQ_GAIN_MIN,
.max = EQ_GAIN_MAX,
- .step = EQ_GAIN_STEP,
.formatter = db_format,
.get_talk_id = get_dec_talkid,
};
@@ -164,9 +164,9 @@ static const struct int_setting gain_int_setting = {
static const struct int_setting q_int_setting = {
.option_callback = NULL,
.unit = UNIT_INT,
+ .step = EQ_Q_STEP,
.min = EQ_Q_MIN,
.max = EQ_Q_MAX,
- .step = EQ_Q_STEP,
.formatter = eq_q_format,
.get_talk_id = get_dec_talkid,
};
@@ -174,9 +174,9 @@ static const struct int_setting q_int_setting = {
static const struct int_setting cutoff_int_setting = {
.option_callback = NULL,
.unit = UNIT_HERTZ,
+ .step = EQ_CUTOFF_STEP,
.min = EQ_CUTOFF_MIN,
.max = EQ_CUTOFF_MAX,
- .step = EQ_CUTOFF_STEP,
.formatter = NULL,
.get_talk_id = NULL,
};
@@ -287,7 +287,7 @@ static char *advancedmenu_item_get_name(int selected_item, void *data, char *buf
buffer[0] = 0;
else {
buffer[0] = '\t';
- strlcpy(&buffer[1], str(lang), len - 1);
+ strmemccpy(&buffer[1], str(lang), len - 1);
}
return buffer;
@@ -480,17 +480,18 @@ static int draw_eq_slider(struct screen * screen, int x, int y,
/* Print out the band label */
if (band == 0) {
screen->putsxy(x1, y1, "LS: ");
- screen->getstringsize("LS:", &w, &h);
+ /*screen->getstringsize("LS:", &w, &h); UNUSED*/
} else if (band == EQ_NUM_BANDS - 1) {
screen->putsxy(x1, y1, "HS: ");
- screen->getstringsize("HS:", &w, &h);
+ /*screen->getstringsize("HS:", &w, &h); UNUSED*/
} else {
snprintf(buf, sizeof(buf), "PK%d:", band);
screen->putsxy(x1, y1, buf);
- screen->getstringsize(buf, &w, &h);
+ /*screen->getstringsize(buf, &w, &h); UNUSED*/
}
- screen->getstringsize("A", &w, &h);
+ w = screen->getstringsize("A", NULL, &h);
+
x1 += 5*w; /* 4 chars for label + 1 space = 5 */
/* Print out gain part of status line (left justify after label) */
@@ -503,7 +504,7 @@ static int draw_eq_slider(struct screen * screen, int x, int y,
abs_gain / EQ_USER_DIVISOR, abs_gain % EQ_USER_DIVISOR,
screen->lcdwidth >= 160 ? "dB" : "");
screen->putsxy(x1, y1, buf);
- screen->getstringsize(buf, &w, &h);
+ w = screen->getstringsize(buf, NULL, NULL);
x1 += w;
/* Print out Q part of status line (right justify) */
@@ -514,7 +515,7 @@ static int draw_eq_slider(struct screen * screen, int x, int y,
snprintf(buf, sizeof(buf), "%d.%d%s", q / EQ_USER_DIVISOR,
q % EQ_USER_DIVISOR, screen->lcdwidth >= 160 ? " Q" : "");
- screen->getstringsize(buf, &w, &h);
+ w = screen->getstringsize(buf, NULL, NULL);
x2 = x + width - w - 2;
screen->putsxy(x2, y1, buf);
@@ -526,7 +527,7 @@ static int draw_eq_slider(struct screen * screen, int x, int y,
snprintf(buf, sizeof(buf), "%5d%s", cutoff,
screen->lcdwidth >= 160 ? "Hz" : "");
- screen->getstringsize(buf, &w, &h);
+ w = screen->getstringsize(buf, NULL, NULL);
x1 = x1 + (x2 - x1 - w)/2;
screen->putsxy(x1, y1, buf);
@@ -589,8 +590,7 @@ int eq_menu_graphical(void)
int *setting;
int current_band, x, y, step, fast_step, min, max;
enum eq_slider_mode mode;
- char buf[24];
- int w, h, height, start_item, nb_eq_sliders[NB_SCREENS];
+ int h, height, start_item, nb_eq_sliders[NB_SCREENS];
FOR_NB_SCREENS(i)
viewportmanager_theme_enable(i, false, NULL);
@@ -601,7 +601,7 @@ int eq_menu_graphical(void)
screens[i].clear_display();
/* Figure out how many sliders can be drawn on the screen */
- screens[i].getstringsize("A", &w, &h);
+ h = screens[i].getcharheight();
/* Total height includes margins (1), text, slider, and line selector (1) */
height = 3 + h + 1 + SCROLLBAR_SIZE + 3;
@@ -637,10 +637,8 @@ int eq_menu_graphical(void)
min = EQ_GAIN_MIN;
max = EQ_GAIN_MAX;
- snprintf(buf, sizeof(buf), str(LANG_SYSFONT_EQUALIZER_EDIT_MODE),
+ screens[i].putsxyf(0, 0, str(LANG_SYSFONT_EQUALIZER_EDIT_MODE),
str(LANG_SYSFONT_GAIN), "(dB)");
-
- screens[i].putsxy(0, 0, buf);
} else if (mode == CUTOFF) {
/* cutoff */
setting = &global_settings.eq_band_settings[current_band].cutoff;
@@ -650,10 +648,8 @@ int eq_menu_graphical(void)
min = EQ_CUTOFF_MIN;
max = EQ_CUTOFF_MAX;
- snprintf(buf, sizeof(buf), str(LANG_SYSFONT_EQUALIZER_EDIT_MODE),
+ screens[i].putsxyf(0, 0, str(LANG_SYSFONT_EQUALIZER_EDIT_MODE),
str(LANG_SYSFONT_EQUALIZER_BAND_CUTOFF), "(Hz)");
-
- screens[i].putsxy(0, 0, buf);
} else {
/* Q */
setting = &global_settings.eq_band_settings[current_band].q;
@@ -663,10 +659,8 @@ int eq_menu_graphical(void)
min = EQ_Q_MIN;
max = EQ_Q_MAX;
- snprintf(buf, sizeof(buf), str(LANG_SYSFONT_EQUALIZER_EDIT_MODE),
+ screens[i].putsxyf(0, 0, str(LANG_SYSFONT_EQUALIZER_EDIT_MODE),
str(LANG_SYSFONT_EQUALIZER_BAND_Q), "");
-
- screens[i].putsxy(0, 0, buf);
}
/* Draw scrollbar if needed */
diff --git a/apps/menus/main_menu.c b/apps/menus/main_menu.c
index 321f2cdec4..03873faac9 100644
--- a/apps/menus/main_menu.c
+++ b/apps/menus/main_menu.c
@@ -441,8 +441,7 @@ static int info_action_callback(int action, struct gui_synclist *lists)
else if (action == ACTION_NONE)
{
static int last_redraw = 0;
- if (gui_synclist_item_is_onscreen(lists, 0, INFO_TIME)
- && TIME_AFTER(current_tick, last_redraw + HZ*5))
+ if (TIME_AFTER(current_tick, last_redraw + HZ*5))
{
last_redraw = current_tick;
return ACTION_REDRAW;
@@ -458,9 +457,6 @@ static int show_info(void)
struct simplelist_info info;
int count = INFO_COUNT + refresh_data(&data) - 1;
simplelist_info_init(&info, str(LANG_ROCKBOX_INFO), count, (void*)&data);
- info.hide_selection = !global_settings.talk_menu;
- if (info.hide_selection)
- info.scroll_all = true;
info.get_name = info_getname;
if(global_settings.talk_menu)
info.get_talk = info_speak_item;
diff --git a/apps/menus/playback_menu.c b/apps/menus/playback_menu.c
index 881a4b5a99..e4945be0b5 100644
--- a/apps/menus/playback_menu.c
+++ b/apps/menus/playback_menu.c
@@ -174,6 +174,7 @@ MAKE_MENU(unplug_menu, ID2P(LANG_HEADPHONE_UNPLUG), 0, Icon_NOICON,
MENUITEM_SETTING(skip_length, &global_settings.skip_length, NULL);
MENUITEM_SETTING(prevent_skip, &global_settings.prevent_skip, NULL);
+MENUITEM_SETTING(rewind_across_tracks, &global_settings.rewind_across_tracks, NULL);
MENUITEM_SETTING(resume_rewind, &global_settings.resume_rewind, NULL);
MENUITEM_SETTING(pause_rewind, &global_settings.pause_rewind, NULL);
#ifdef HAVE_PLAY_FREQ
@@ -226,6 +227,7 @@ MAKE_MENU(playback_settings,ID2P(LANG_PLAYBACK),0,
,&unplug_menu
#endif
,&skip_length, &prevent_skip
+ ,&rewind_across_tracks
,&resume_rewind
,&pause_rewind
diff --git a/apps/menus/playlist_menu.c b/apps/menus/playlist_menu.c
index e1e83d4311..89c93edc2e 100644
--- a/apps/menus/playlist_menu.c
+++ b/apps/menus/playlist_menu.c
@@ -61,8 +61,8 @@ int save_playlist_screen(struct playlist_info* playlist)
if (!dot && len <= 1)
{
- snprintf(temp, sizeof(temp), "%s%s",
- catalog_get_directory(), DEFAULT_DYNAMIC_PLAYLIST_NAME);
+ catalog_get_directory(temp, sizeof(temp));
+ strlcat(temp, DEFAULT_DYNAMIC_PLAYLIST_NAME, sizeof(temp));
}
dot = strrchr(temp, '.');
@@ -137,7 +137,7 @@ int save_playlist_screen(struct playlist_info* playlist)
static int playlist_view_(void)
{
- playlist_viewer_ex(NULL);
+ playlist_viewer_ex(NULL, NULL);
return 0;
}
MENUITEM_FUNCTION(create_playlist_item, 0, ID2P(LANG_CREATE_PLAYLIST),
diff --git a/apps/menus/sound_menu.c b/apps/menus/sound_menu.c
index 6d7ef270cb..d72e3c7fa7 100644
--- a/apps/menus/sound_menu.c
+++ b/apps/menus/sound_menu.c
@@ -54,13 +54,17 @@ static int volume_limit_callback(int action,
(void)this_list;
static struct int_setting volume_limit_int_setting;
- volume_limit_int_setting.option_callback = NULL;
- volume_limit_int_setting.unit = UNIT_DB;
- volume_limit_int_setting.min = sound_min(SOUND_VOLUME);
- volume_limit_int_setting.max = sound_max(SOUND_VOLUME);
- volume_limit_int_setting.step = sound_steps(SOUND_VOLUME);
- volume_limit_int_setting.formatter = vol_limit_format;
- volume_limit_int_setting.get_talk_id = NULL;
+
+ volume_limit_int_setting = (struct int_setting)
+ {
+ .option_callback = NULL,
+ .unit = UNIT_DB,
+ .step = sound_steps(SOUND_VOLUME),
+ .min = sound_min(SOUND_VOLUME),
+ .max = sound_max(SOUND_VOLUME),
+ .formatter = vol_limit_format,
+ .get_talk_id = NULL
+ };
struct settings_list setting;
setting.flags = F_BANFROMQS|F_INT_SETTING|F_T_INT|F_NO_WRAP;
diff --git a/apps/misc.c b/apps/misc.c
index 63aa3589b2..3e93cc9649 100644
--- a/apps/misc.c
+++ b/apps/misc.c
@@ -465,7 +465,7 @@ static void car_adapter_mode_processing(bool inserted)
if ((audio_status() & AUDIO_STATUS_PLAY) &&
!(audio_status() & AUDIO_STATUS_PAUSE))
{
- pause_action(true, true);
+ pause_action(true);
paused_on_unplugged = true;
}
else if (!waiting_to_resume_play)
@@ -513,14 +513,14 @@ static void hp_unplug_change(bool inserted)
if ((audio_stat & AUDIO_STATUS_PLAY) &&
headphone_caused_pause &&
global_settings.unplug_mode > 1 )
- unpause_action(true, true);
+ unpause_action(true);
headphone_caused_pause = false;
} else {
if ((audio_stat & AUDIO_STATUS_PLAY) &&
!(audio_stat & AUDIO_STATUS_PAUSE))
{
headphone_caused_pause = true;
- pause_action(false, false);
+ pause_action(false);
}
}
}
@@ -552,14 +552,14 @@ static void lo_unplug_change(bool inserted)
if ((audio_stat & AUDIO_STATUS_PLAY) &&
lineout_caused_pause &&
global_settings.unplug_mode > 1 )
- unpause_action(true, true);
+ unpause_action(true);
lineout_caused_pause = false;
} else {
if ((audio_stat & AUDIO_STATUS_PLAY) &&
!(audio_stat & AUDIO_STATUS_PAUSE))
{
lineout_caused_pause = true;
- pause_action(false, false);
+ pause_action(false);
}
}
}
@@ -629,7 +629,7 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame
return SYS_CHARGER_DISCONNECTED;
case SYS_CAR_ADAPTER_RESUME:
- unpause_action(true, true);
+ unpause_action(true);
return SYS_CAR_ADAPTER_RESUME;
#endif
#ifdef HAVE_HOTSWAP_STORAGE_AS_MAIN
@@ -710,9 +710,9 @@ long default_event_handler_ex(long event, void (*callback)(void *), void *parame
if (status & AUDIO_STATUS_PLAY)
{
if (status & AUDIO_STATUS_PAUSE)
- unpause_action(true, true);
+ unpause_action(true);
else
- pause_action(true, true);
+ pause_action(true);
}
else
if (playlist_resume() != -1)
@@ -748,25 +748,32 @@ long default_event_handler(long event)
int show_logo( void )
{
- char version[32];
- int font_h, font_w;
+ unsigned char version[32];
+ int font_h, ver_w;
snprintf(version, sizeof(version), "Ver. %s", rbversion);
+ ver_w = font_getstringsize(version, NULL, &font_h, FONT_SYSFIXED);
+
lcd_clear_display();
+
+ lcd_setfont(FONT_SYSFIXED);
+
#if defined(SANSA_CLIP) || defined(SANSA_CLIPV2) || defined(SANSA_CLIPPLUS)
/* display the logo in the blue area of the screen (bottom 48 pixels) */
- lcd_setfont(FONT_SYSFIXED);
- lcd_getstringsize((unsigned char *)"A", &font_w, &font_h);
- lcd_putsxy((LCD_WIDTH/2) - ((strlen(version)*font_w)/2),
- 0, (unsigned char *)version);
+ if (ver_w > LCD_WIDTH)
+ lcd_putsxy(0, 0, rbversion);
+ else
+ lcd_putsxy((LCD_WIDTH/2) - (ver_w/2), 0, version);
+
lcd_bmp(&bm_rockboxlogo, (LCD_WIDTH - BMPWIDTH_rockboxlogo) / 2, 16);
#else
lcd_bmp(&bm_rockboxlogo, (LCD_WIDTH - BMPWIDTH_rockboxlogo) / 2, 10);
- lcd_setfont(FONT_SYSFIXED);
- lcd_getstringsize((unsigned char *)"A", &font_w, &font_h);
- lcd_putsxy((LCD_WIDTH/2) - ((strlen(version)*font_w)/2),
- LCD_HEIGHT-font_h, (unsigned char *)version);
+
+ if (ver_w > LCD_WIDTH)
+ lcd_putsxy(0, LCD_HEIGHT-font_h, rbversion);
+ else
+ lcd_putsxy((LCD_WIDTH/2) - (ver_w/2), LCD_HEIGHT-font_h, version);
#endif
lcd_setfont(FONT_UI);
@@ -776,9 +783,13 @@ int show_logo( void )
lcd_remote_clear_display();
lcd_remote_bmp(&bm_remote_rockboxlogo, 0, 10);
lcd_remote_setfont(FONT_SYSFIXED);
- lcd_remote_getstringsize((unsigned char *)"A", &font_w, &font_h);
- lcd_remote_putsxy((LCD_REMOTE_WIDTH/2) - ((strlen(version)*font_w)/2),
- LCD_REMOTE_HEIGHT-font_h, (unsigned char *)version);
+
+ if (ver_w > LCD_REMOTE_WIDTH)
+ lcd_remote_putsxy(0, LCD_REMOTE_HEIGHT-font_h, rbversion);
+ else
+ lcd_remote_putsxy((LCD_REMOTE_WIDTH/2) - (ver_w/2),
+ LCD_REMOTE_HEIGHT-font_h, version);
+
lcd_remote_setfont(FONT_UI);
lcd_remote_update();
#endif
@@ -885,7 +896,7 @@ char *strip_extension(char* buffer, int buffer_size, const char *filename)
/* no match on filename beginning with '.' or beyond buffer_size */
if(dotpos > 1 && dotpos < buffer_size)
buffer_size = dotpos;
- strlcpy(buffer, filename, buffer_size);
+ strmemccpy(buffer, filename, buffer_size);
return buffer;
}
@@ -1122,7 +1133,6 @@ int read_line(int fd, char* buffer, int buffer_size)
return rdbufend >= 0 ? num_read : -1;
}
-
char* skip_whitespace(char* const str)
{
char *s = str;
@@ -1134,6 +1144,16 @@ char* skip_whitespace(char* const str)
}
#if !defined(CHECKWPS) && !defined(DBTOOL)
+
+int confirm_delete_yesno(const char *name)
+{
+ const char *lines[] = { ID2P(LANG_REALLY_DELETE), name };
+ const char *yes_lines[] = { ID2P(LANG_DELETING), name };
+ const struct text_message message = { lines, 2 };
+ const struct text_message yes_message = { yes_lines, 2 };
+ return gui_syncyesno_run(&message, &yes_message, NULL);
+}
+
/* time_split_units()
split time values depending on base unit
unit_idx: UNIT_HOUR, UNIT_MIN, UNIT_SEC, UNIT_MS
@@ -1324,12 +1344,12 @@ const char *format_time_auto(char *buffer, int buf_len, long value,
if (!supress_unit)
{
- strlcpy(buffer, unit_strings_core[units[max_idx]], buf_len);
+ strmemccpy(buffer, unit_strings_core[units[max_idx]], buf_len);
left_offset += strlcat(buffer, " ", buf_len);
strlcat(buffer, &timebuf[offsets[base_idx]], buf_len);
}
else
- strlcpy(buffer, &timebuf[offsets[base_idx]], buf_len);
+ strmemccpy(buffer, &timebuf[offsets[base_idx]], buf_len);
strlcat(buffer, sign, buf_len);
}
@@ -1373,20 +1393,16 @@ void format_time(char* buf, int buf_size, long t)
int split_string(char *str, const char split_char, char *vector[], const int vector_length)
{
int i;
- char *p = str;
-
- /* skip leading splitters */
- while(*p == split_char) p++;
+ char sep[2] = {split_char, '\0'};
+ char *e, *p = strtok_r(str, sep, &e);
- /* *p in the condition takes care of trailing splitters */
- for(i = 0; p && *p && i < vector_length; i++)
+ /* strtok takes care of leading & trailing splitters */
+ for(i = 0; i < vector_length; i++)
{
vector[i] = p;
- if ((p = strchr(p, split_char)))
- {
- *p++ = '\0';
- while(*p == split_char) p++; /* skip successive splitters */
- }
+ if (!p)
+ break;
+ p = strtok_r(NULL, sep, &e);
}
return i;
@@ -1411,6 +1427,18 @@ int string_option(const char *option, const char *const oplist[], bool ignore_ca
return -1;
}
+/* open but with a builtin printf for assembling the path */
+int open_pathfmt(char *buf, size_t size, int oflag, const char *pathfmt, ...)
+{
+ va_list ap;
+ va_start(ap, pathfmt);
+ vsnprintf(buf, size, pathfmt, ap);
+ va_end(ap);
+ if ((oflag & O_PATH) == O_PATH)
+ return -1;
+ return open(buf, oflag, 0666);
+}
+
/** Open a UTF-8 file and set file descriptor to first byte after BOM.
* If no BOM is present this behaves like open().
* If the file is opened for writing and O_TRUNC is set, write a BOM to
diff --git a/apps/misc.h b/apps/misc.h
index ed40de3e1f..af520a7fd1 100644
--- a/apps/misc.h
+++ b/apps/misc.h
@@ -122,6 +122,10 @@ extern int show_logo(void);
#define BOM_UTF_16_SIZE 2
int split_string(char *str, const char needle, char *vector[], int vector_length);
+#ifndef O_PATH
+#define O_PATH 0x2000
+#endif
+int open_pathfmt(char *buf, size_t size, int oflag, const char *pathfmt, ...);
int open_utf8(const char* pathname, int flags);
int string_option(const char *option, const char *const oplist[], bool ignore_case);
@@ -139,6 +143,8 @@ void setvol(void);
int hex_to_rgb(const char* hex, int* color);
#endif
+int confirm_delete_yesno(const char *name);
+
char* strrsplt(char* str, int c);
char* skip_whitespace(char* const str);
diff --git a/apps/onplay.c b/apps/onplay.c
index e44e81ee5d..f2dbf8b3bf 100644
--- a/apps/onplay.c
+++ b/apps/onplay.c
@@ -63,6 +63,7 @@
#include "viewport.h"
#include "pathfuncs.h"
#include "shortcuts.h"
+#include "misc.h"
static int context;
static const char *selected_file = NULL;
@@ -126,8 +127,8 @@ static bool clipboard_clip(struct clipboard *clip, const char *path,
unsigned int attr, unsigned int flags)
{
/* if it fits it clips */
- if (strlcpy(clip->path, path, sizeof (clip->path))
- < sizeof (clip->path)) {
+ if (strmemccpy(clip->path, path, sizeof (clip->path)) != NULL)
+ {
clip->attr = attr;
clip->flags = flags;
return true;
@@ -382,8 +383,9 @@ static bool playing_time(void)
although playback continues forward. */
for (i = 0; i < pti.nb_tracks; i++) {
/* Show a splash while we are loading. */
- splashf(0, str(LANG_LOADING_PERCENT),
- i*100/pti.nb_tracks, str(LANG_OFF_ABORT));
+ splash_progress(i, pti.nb_tracks,
+ "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT));
+
/* Voice equivalent */
if (TIME_AFTER(current_tick, talked_tick+5*HZ)) {
talked_tick = current_tick;
@@ -429,8 +431,7 @@ static bool playing_time(void)
gui_synclist_draw(&pt_lists);
gui_synclist_speak_item(&pt_lists);
while (true) {
- if (list_do_action(CONTEXT_LIST, HZ/2,
- &pt_lists, &key, LIST_WRAP_UNLESS_HELD) == 0
+ if (list_do_action(CONTEXT_LIST, HZ/2, &pt_lists, &key) == 0
&& key!=ACTION_NONE && key!=ACTION_UNKNOWN)
{
talk_force_shutup();
@@ -588,12 +589,11 @@ static int add_to_playlist(void* arg)
static bool view_playlist(void)
{
- bool was_playing = audio_status() & AUDIO_STATUS_PLAY;
bool result;
- result = playlist_viewer_ex(selected_file);
+ result = playlist_viewer_ex(selected_file, NULL);
- if (!was_playing && (audio_status() & AUDIO_STATUS_PLAY) &&
+ if (result == PLAYLIST_VIEWER_OK &&
onplay_result == ONPLAY_OK)
/* playlist was started from viewer */
onplay_result = ONPLAY_START_PLAY;
@@ -899,15 +899,6 @@ static int confirm_overwrite(void)
return gui_syncyesno_run(&message, NULL, NULL);
}
-static int confirm_delete(const char *file)
-{
- const char *lines[] = { ID2P(LANG_REALLY_DELETE), file };
- const char *yes_lines[] = { ID2P(LANG_DELETING), file };
- const struct text_message message = { lines, 2 };
- const struct text_message yes_message = { yes_lines, 2 };
- return gui_syncyesno_run(&message, &yes_message, NULL);
-}
-
static bool check_new_name(const char *basename)
{
/* at least prevent escapes out of the base directory from keyboard-
@@ -1002,7 +993,7 @@ static int delete_file_dir(void)
{
const char *to_delete=selected_file;
const int to_delete_attr=selected_file_attr;
- if (confirm_delete(to_delete) != YESNO_YES) {
+ if (confirm_delete_yesno(to_delete) != YESNO_YES) {
return 1;
}
@@ -1048,7 +1039,7 @@ static int rename_file(void)
size_t pathlen = oldbase - selection;
char *newbase = newname + pathlen;
- if (strlcpy(newname, selection, sizeof (newname)) >= sizeof (newname)) {
+ if (strmemccpy(newname, selection, sizeof (newname)) == NULL) {
/* Too long */
} else if (kbd_input(newbase, sizeof (newname) - pathlen, NULL) < 0) {
rc = OPRC_CANCELLED;
@@ -1559,6 +1550,8 @@ static bool onplay_load_plugin(void *param)
onplay_result = ONPLAY_RELOAD_DIR;
else if (ret == PLUGIN_GOTO_PLUGIN)
onplay_result = ONPLAY_PLUGIN;
+ else if (ret == PLUGIN_GOTO_WPS)
+ onplay_result = ONPLAY_START_PLAY;
return false;
}
@@ -1587,7 +1580,7 @@ MENUITEM_FUNCTION(add_to_faves_item, 0, ID2P(LANG_ADD_TO_FAVES),
#if LCD_DEPTH > 1
static bool set_backdrop(void)
{
- strlcpy(global_settings.backdrop_file, selected_file,
+ strmemccpy(global_settings.backdrop_file, selected_file,
sizeof(global_settings.backdrop_file));
settings_save();
skin_backdrop_load_setting();
@@ -1600,7 +1593,7 @@ MENUITEM_FUNCTION(set_backdrop_item, 0, ID2P(LANG_SET_AS_BACKDROP),
#ifdef HAVE_RECORDING
static bool set_recdir(void)
{
- strlcpy(global_settings.rec_directory, selected_file,
+ strmemccpy(global_settings.rec_directory, selected_file,
sizeof(global_settings.rec_directory));
settings_save();
return false;
@@ -1784,7 +1777,7 @@ static int onplaymenu_callback(int action,
#ifdef HAVE_HOTKEY
/* direct function calls, no need for menu callbacks */
-static bool delete_item(void)
+static bool hotkey_delete_item(void)
{
#ifdef HAVE_MULTIVOLUME
/* no delete for volumes */
@@ -1794,7 +1787,7 @@ static bool delete_item(void)
return delete_file_dir();
}
-static bool open_with(void)
+static bool hotkey_open_with(void)
{
/* only open files */
if (selected_file_attr & ATTR_DIRECTORY)
@@ -1806,30 +1799,37 @@ static bool open_with(void)
return list_viewers();
}
-static int playlist_insert_shuffled(void)
+static int hotkey_tree_pl_insert_shuffled(void)
{
if ((audio_status() & AUDIO_STATUS_PLAY) ||
(selected_file_attr & ATTR_DIRECTORY) ||
((selected_file_attr & FILE_ATTR_MASK) == FILE_ATTR_M3U))
{
add_to_playlist(&addtopl_insert_shuf);
- return ONPLAY_START_PLAY;
}
+ return ONPLAY_RELOAD_DIR;
+}
+
+static int hotkey_tree_run_plugin(void *param)
+{
+ if (filetype_load_plugin((const char*)param, selected_file) == PLUGIN_GOTO_WPS)
+ return ONPLAY_START_PLAY;
return ONPLAY_RELOAD_DIR;
}
-static void hotkey_run_plugin(void)
+static int hotkey_wps_run_plugin(void)
{
open_plugin_run(ID2P(LANG_HOTKEY_WPS));
+ return ONPLAY_OK;
}
struct hotkey_assignment {
int action; /* hotkey_action */
int lang_id; /* Language ID */
struct menu_func func; /* Function to run if this entry is selected */
- int return_code; /* What to return after the function is run */
-};
+ int return_code; /* What to return after the function is run. */
+}; /* (Pick ONPLAY_FUNC_RETURN to use function's return value) */
#define HOTKEY_FUNC(func, param) {{(void *)func}, param}
@@ -1849,30 +1849,30 @@ static struct hotkey_assignment hotkey_items[] = {
ONPLAY_RELOAD_DIR },
#endif
{ HOTKEY_OPEN_WITH, LANG_ONPLAY_OPEN_WITH,
- HOTKEY_FUNC(open_with, NULL),
+ HOTKEY_FUNC(hotkey_open_with, NULL),
ONPLAY_RELOAD_DIR },
{ HOTKEY_DELETE, LANG_DELETE,
- HOTKEY_FUNC(delete_item, NULL),
+ HOTKEY_FUNC(hotkey_delete_item, NULL),
ONPLAY_RELOAD_DIR },
{ HOTKEY_INSERT, LANG_INSERT,
HOTKEY_FUNC(add_to_playlist, (intptr_t*)&addtopl_insert),
ONPLAY_RELOAD_DIR },
{ HOTKEY_INSERT_SHUFFLED, LANG_INSERT_SHUFFLED,
- HOTKEY_FUNC(playlist_insert_shuffled, NULL),
- ONPLAY_RELOAD_DIR },
+ HOTKEY_FUNC(hotkey_tree_pl_insert_shuffled, NULL),
+ ONPLAY_FUNC_RETURN },
{ HOTKEY_PLUGIN, LANG_OPEN_PLUGIN,
- HOTKEY_FUNC(hotkey_run_plugin, NULL),
- ONPLAY_OK },
+ HOTKEY_FUNC(hotkey_wps_run_plugin, NULL),
+ ONPLAY_FUNC_RETURN },
{ HOTKEY_BOOKMARK, LANG_BOOKMARK_MENU_CREATE,
HOTKEY_FUNC(bookmark_create_menu, NULL),
ONPLAY_OK },
{ HOTKEY_PROPERTIES, LANG_PROPERTIES,
- HOTKEY_FUNC(onplay_load_plugin, (void *)"properties"),
- ONPLAY_RELOAD_DIR },
+ HOTKEY_FUNC(hotkey_tree_run_plugin, (void *)"properties"),
+ ONPLAY_FUNC_RETURN },
#ifdef HAVE_TAGCACHE
{ HOTKEY_PICTUREFLOW, LANG_ONPLAY_PICTUREFLOW,
- HOTKEY_FUNC(onplay_load_plugin, (void *)"pictureflow"),
- ONPLAY_RELOAD_DIR },
+ HOTKEY_FUNC(hotkey_tree_run_plugin, (void *)"pictureflow"),
+ ONPLAY_FUNC_RETURN },
#endif
};
@@ -1895,7 +1895,7 @@ static int execute_hotkey(bool is_wps)
int i = ARRAYLEN(hotkey_items);
struct hotkey_assignment *this_item;
const int action = (is_wps ? global_settings.hotkey_wps :
- global_settings.hotkey_tree);
+ global_settings.hotkey_tree);
/* search assignment struct for a match for the hotkey setting */
while (i--)
@@ -1913,12 +1913,11 @@ static int execute_hotkey(bool is_wps)
else
func_return = (*func.function)();
}
- /* return with the associated code */
const int return_code = this_item->return_code;
- /* ONPLAY_OK here means to use the function return code */
- if (return_code == ONPLAY_OK)
- return func_return;
- return return_code;
+
+ if (return_code == ONPLAY_FUNC_RETURN)
+ return func_return; /* Use value returned by function */
+ return return_code; /* or return the associated value */
}
}
@@ -1937,7 +1936,7 @@ int onplay(char* file, int attr, int from, bool hotkey)
selected_file = NULL;
else
{
- strlcpy(selected_file_path, file, MAX_PATH);
+ strmemccpy(selected_file_path, file, MAX_PATH);
selected_file = selected_file_path;
}
selected_file_attr = attr;
diff --git a/apps/onplay.h b/apps/onplay.h
index 3121c918ac..80e0371eaf 100644
--- a/apps/onplay.h
+++ b/apps/onplay.h
@@ -31,6 +31,9 @@ enum {
ONPLAY_START_PLAY,
ONPLAY_PLAYLIST,
ONPLAY_PLUGIN,
+#ifdef HAVE_HOTKEY
+ ONPLAY_FUNC_RETURN, /* for use in hotkey_assignment only */
+#endif
};
#ifdef HAVE_HOTKEY
diff --git a/apps/open_plugin.c b/apps/open_plugin.c
index fad528e215..45dd7cdd2c 100644
--- a/apps/open_plugin.c
+++ b/apps/open_plugin.c
@@ -28,7 +28,7 @@
#include "lang.h"
/* Define LOGF_ENABLE to enable logf output in this file */
-//#define LOGF_ENABLE
+/*#define LOGF_ENABLE*/
#include "logf.h"
#define ROCK_EXT "rock"
@@ -60,8 +60,12 @@ static inline void op_clear_entry(struct open_plugin_entry_t *entry)
static int op_entry_checksum(struct open_plugin_entry_t *entry)
{
- if (entry == NULL || entry->checksum != open_plugin_csum)
+ if (entry == NULL || entry->checksum != open_plugin_csum +
+ (entry->lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY))
+ {
+ logf("OP entry bad checksum");
return 0;
+ }
return 1;
}
@@ -81,6 +85,14 @@ static int op_find_entry(int fd, struct open_plugin_entry_t *entry,
if (entry->lang_id == lang_id || entry->hash == hash ||
(lang_id == OPEN_PLUGIN_LANG_IGNOREALL))/* return first entry found */
{
+#if (CONFIG_STORAGE & STORAGE_ATA)
+ /* may have invalid entries but we append the file so continue looking*/
+ if (op_entry_checksum(entry) <= 0)
+ {
+ ret = OPEN_PLUGIN_INVALID_ENTRY;
+ continue;
+ }
+#endif
ret = record;
/* NULL terminate fields NOTE -- all are actually +1 larger */
entry->name[OPEN_PLUGIN_NAMESZ] = '\0';
@@ -98,7 +110,12 @@ static int op_find_entry(int fd, struct open_plugin_entry_t *entry,
}
/* sanity check */
- if (ret > OPEN_PLUGIN_NOT_FOUND && op_entry_checksum(entry) <= 0)
+#if (CONFIG_STORAGE & STORAGE_ATA)
+ if (ret == OPEN_PLUGIN_INVALID_ENTRY ||
+#else
+ if(
+#endif
+ (ret > OPEN_PLUGIN_NOT_FOUND && op_entry_checksum(entry) <= 0))
{
splash(HZ * 2, "OpenPlugin Invalid entry");
ret = OPEN_PLUGIN_NOT_FOUND;
@@ -144,7 +161,7 @@ static int op_update_dat(struct open_plugin_entry_t *entry, bool clear)
hash_langid_csum[2] == open_plugin_csum)
{
logf("OP update *Entry Exists* hash: %x langid: %d",
- hash_langid_csum[0], (int32_t)hash_langid[1]);
+ hash_langid_csum[0], (int32_t)hash_langid_csum[1]);
lseek(fd, 0-hlc_sz, SEEK_CUR);/* back to the start of record */
break;
}
@@ -261,7 +278,8 @@ uint32_t open_plugin_add_path(const char *key, const char *plugin, const char *p
{
open_plugin_entry.hash = hash;
open_plugin_entry.lang_id = lang_id;
- open_plugin_entry.checksum = open_plugin_csum;
+ open_plugin_entry.checksum = open_plugin_csum +
+ (lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY);
/* name */
if (path_basename(plugin, (const char **)&pos) == 0)
pos = "\0";
@@ -270,11 +288,11 @@ uint32_t open_plugin_add_path(const char *key, const char *plugin, const char *p
if (len > ROCK_LEN && strcasecmp(&(pos[len-ROCK_LEN]), "." ROCK_EXT) == 0)
{
/* path */
- strlcpy(open_plugin_entry.path, plugin, OPEN_PLUGIN_BUFSZ);
+ strmemccpy(open_plugin_entry.path, plugin, OPEN_PLUGIN_BUFSZ);
if(!parameter)
parameter = "";
- strlcpy(open_plugin_entry.param, parameter, OPEN_PLUGIN_BUFSZ);
+ strmemccpy(open_plugin_entry.param, parameter, OPEN_PLUGIN_BUFSZ);
goto retnhash;
}
else if (len > OP_LEN && strcasecmp(&(pos[len-OP_LEN]), "." OP_EXT) == 0)
@@ -337,7 +355,7 @@ int open_plugin_get_entry(const char *key, struct open_plugin_entry_t *entry)
opret = op_get_entry(hash, lang_id, entry, OPEN_PLUGIN_DAT);
logf("OP entry hash: %x lang id: %d ret: %d key: %s", hash, lang_id, opret, skey);
- if (opret == OPEN_PLUGIN_NOT_FOUND && lang_id > OPEN_PLUGIN_LANG_INVALID)
+ if (opret == OPEN_PLUGIN_NOT_FOUND && lang_id > OPEN_PLUGIN_LANG_INVALID)
{ /* try rb defaults */
opret = op_get_entry(hash, lang_id, entry, OPEN_RBPLUGIN_DAT);
logf("OP rb_entry hash: %x lang id: %d ret: %d key: %s", hash, lang_id, opret, skey);
@@ -364,6 +382,8 @@ int open_plugin_run(const char *key)
if (param[0] == '\0')
param = NULL;
+ if (path[0] == '\0' && key)
+ path = P2STR((unsigned char *)key);
ret = plugin_load(path, param);
diff --git a/apps/open_plugin.h b/apps/open_plugin.h
index d16be2052c..62e3662849 100644
--- a/apps/open_plugin.h
+++ b/apps/open_plugin.h
@@ -40,8 +40,9 @@ enum {
OPEN_PLUGIN_LANG_INVALID = (-1),
OPEN_PLUGIN_LANG_IGNORE = (-2),
OPEN_PLUGIN_LANG_IGNOREALL = (-3),
- OPEN_PLUGIN_NOT_FOUND = (-1),
- OPEN_PLUGIN_NEEDS_FLUSHED = (-2),
+ OPEN_PLUGIN_INVALID_ENTRY = (-1),
+ OPEN_PLUGIN_NOT_FOUND = (-2),
+ OPEN_PLUGIN_NEEDS_FLUSHED = (-3),
};
struct open_plugin_entry_t
diff --git a/apps/playback.c b/apps/playback.c
index 7fdb302d71..7eaa149f2c 100644
--- a/apps/playback.c
+++ b/apps/playback.c
@@ -1483,7 +1483,7 @@ static bool audio_init_codec(struct track_info *track_infop,
enum { AUTORESUMABLE_UNKNOWN = 0, AUTORESUMABLE_TRUE, AUTORESUMABLE_FALSE };
static bool autoresumable(struct mp3entry *id3)
{
- char *endp, *path;
+ char *path;
size_t len;
bool is_resumable;
@@ -1501,13 +1501,7 @@ static bool autoresumable(struct mp3entry *id3)
if (*path == ':') /* Skip empty search patterns */
continue;
- /* FIXME: As soon as strcspn or strchrnul are made available in
- the core, the following can be made more efficient. */
- endp = strchr(path, ':');
- if (endp)
- len = endp - path;
- else
- len = strlen(path);
+ len = strcspn(path, ":");
/* Note: At this point, len is always > 0 */
@@ -2097,23 +2091,11 @@ static int audio_finish_load_track(struct track_info *infop)
/** Finally, load the audio **/
off_t file_offset = 0;
- if (track_id3->elapsed > track_id3->length)
- track_id3->elapsed = 0;
-
- if ((off_t)track_id3->offset >= buf_filesize(infop->audio_hid))
- track_id3->offset = 0;
-
- logf("%s: set offset for %s to %lu\n", __func__,
- track_id3->title, track_id3->offset);
-
/* Adjust for resume rewind so we know what to buffer - starting the codec
calls it again, so we don't save it (and they shouldn't accumulate) */
unsigned long elapsed, offset;
resume_rewind_adjust_progress(track_id3, &elapsed, &offset);
- logf("%s: Set resume for %s to %lu %lu", __func__,
- track_id3->title, elapsed, offset);
-
enum data_type audiotype = rbcodec_format_is_atomic(track_id3->codectype) ?
TYPE_ATOMIC_AUDIO : TYPE_PACKET_AUDIO;
@@ -2145,6 +2127,19 @@ static int audio_finish_load_track(struct track_info *infop)
if (hid >= 0)
{
infop->audio_hid = hid;
+
+ /*
+ * Fix up elapsed time and offset if past the end
+ */
+ if (track_id3->elapsed > track_id3->length)
+ track_id3->elapsed = 0;
+
+ if ((off_t)track_id3->offset >= buf_filesize(infop->audio_hid))
+ track_id3->offset = 0;
+
+ logf("%s: set resume for %s to %lu %lX", __func__,
+ track_id3->title, track_id3->elapsed, track_id3->offset);
+
if (infop->self_hid == cur_info.self_hid)
{
/* This is the current track to decode - should be started now */
@@ -3683,8 +3678,7 @@ void audio_hard_stop(void)
#ifdef PLAYBACK_VOICE
voice_stop();
#endif
- if (audiobuf_handle > 0)
- audiobuf_handle = core_free(audiobuf_handle);
+ audiobuf_handle = core_free(audiobuf_handle);
}
/* Resume playback if paused */
diff --git a/apps/playlist.c b/apps/playlist.c
index b1d5d5a4be..abfefa7e8d 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -29,7 +29,7 @@
directory, there will be no playlist file.
2. Control file : This file is automatically created when a playlist is
started and contains all the commands done to it.
-
+
The first non-comment line in a control file must begin with
"P:VERSION:DIR:FILE" where VERSION is the playlist control file version,
DIR is the directory where the playlist is located and FILE is the
@@ -68,8 +68,8 @@
*/
#include <stdio.h>
-#include <stdlib.h>
-#include <ctype.h>
+#include <stdlib.h>
+#include <ctype.h>
#include "string-extra.h"
#include "playlist.h"
#include "ata_idle_notify.h"
@@ -124,17 +124,18 @@
//NOTEF
#endif
-
-
-
+/* default load buffer size (should be at least 1 KiB) */
+#define PLAYLIST_LOAD_BUFLEN (32*1024)
#define PLAYLIST_CONTROL_FILE_VERSION 2
+#define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
+
/*
Each playlist index has a flag associated with it which identifies what
type of track it is. These flags are stored in the 4 high order bits of
the index.
-
+
NOTE: This limits the playlist file size to a max of 256M.
Bits 31-30:
@@ -169,56 +170,12 @@ struct directory_search_context {
static struct playlist_info current_playlist;
-static void empty_playlist(struct playlist_info* playlist, bool resume);
-static void new_playlist(struct playlist_info* playlist, const char *dir,
- const char *file);
-static void create_control(struct playlist_info* playlist);
-static int check_control(struct playlist_info* playlist);
-static int recreate_control(struct playlist_info* playlist);
-static void update_playlist_filename(struct playlist_info* playlist,
- const char *dir, const char *file);
-static int add_indices_to_playlist(struct playlist_info* playlist,
- char* buffer, size_t buflen);
-static int add_track_to_playlist(struct playlist_info* playlist,
- const char *filename, int position,
- bool queue, int seek_pos);
-static int directory_search_callback(char* filename, void* context);
-static int remove_track_from_playlist(struct playlist_info* playlist,
- int position, bool write);
-static int randomise_playlist(struct playlist_info* playlist,
- unsigned int seed, bool start_current,
- bool write);
-static int sort_playlist(struct playlist_info* playlist, bool start_current,
- bool write);
-static int get_next_index(const struct playlist_info* playlist, int steps,
- int repeat_mode);
-static void find_and_set_playlist_index(struct playlist_info* playlist,
- unsigned int seek);
-static int compare(const void* p1, const void* p2);
-static int get_filename(struct playlist_info* playlist, int index, int seek,
- bool control_file, char *buf, int buf_length);
-static int get_next_directory(char *dir);
-static int get_next_dir(char *dir, bool is_forward);
-static int get_previous_directory(char *dir);
-static int check_subdir_for_music(char *dir, const char *subdir, bool recurse);
-static ssize_t format_track_path(char *dest, char *src, int buf_length,
- const char *dir);
-static void display_playlist_count(int count, const unsigned char *fmt,
- bool final);
-static void display_buffer_full(void);
-static int flush_cached_control(struct playlist_info* playlist);
-static int update_control(struct playlist_info* playlist,
- enum playlist_command command, int i1, int i2,
- const char* s1, const char* s2, void* data);
-static void sync_control(struct playlist_info* playlist, bool force);
-static int rotate_index(const struct playlist_info* playlist, int index);
-
#ifdef HAVE_DIRCACHE
#define PLAYLIST_LOAD_POINTERS 1
static struct event_queue playlist_queue SHAREDBSS_ATTR;
static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)];
-static const char playlist_thread_name[] = "playlist cachectrl";
+static const char thread_playlist_name[] = "playlist cachectrl";
#endif
static struct mutex current_playlist_mutex SHAREDBSS_ATTR;
@@ -246,21 +203,21 @@ static void copy_filerefs(struct dircache_fileref *dcfto,
/* Check if the filename suggests M3U or M3U8 format. */
static bool is_m3u8(const char* filename)
{
- int len = strlen(filename);
+ char *dot = strrchr(filename, '.');
/* Default to M3U8 unless explicitly told otherwise. */
- return !(len > 4 && strcasecmp(&filename[len - 4], ".m3u") == 0);
+ return (!dot || strcasecmp(dot, ".m3u") != 0);
}
-/* Convert a filename in an M3U playlist to UTF-8.
+/* Convert a filename in an M3U playlist to UTF-8.
*
* buf - the filename to convert; can contain more than one line from the
* playlist.
* buf_len - amount of buf that is used.
* buf_max - total size of buf.
* temp - temporary conversion buffer, at least buf_max bytes.
- *
+ *
* Returns the length of the converted filename.
*/
static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
@@ -279,10 +236,10 @@ static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
{
i--;
}
-
+
buf_len = i;
dest = temp;
-
+
/* Convert char by char, so as to not overflow temp (iso_decode should
* preferably handle this). No more than 4 bytes should be generated for
* each input char.
@@ -291,31 +248,241 @@ static int convert_m3u(char* buf, int buf_len, int buf_max, char* temp)
{
dest = iso_decode(&buf[i], dest, -1, 1);
}
-
+
*dest = 0;
strcpy(buf, temp);
return dest - temp;
}
/*
+ * create control file for playlist
+ */
+static void create_control(struct playlist_info* playlist)
+{
+ playlist->control_fd = open(playlist->control_filename,
+ O_CREAT|O_RDWR|O_TRUNC, 0666);
+ if (playlist->control_fd < 0)
+ {
+ if (check_rockboxdir())
+ {
+ cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
+ splashf(HZ*2, (unsigned char *)"%s (%d)",
+ str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
+ playlist->control_fd);
+ }
+ playlist->control_created = false;
+ }
+ else
+ {
+ playlist->control_created = true;
+ }
+}
+
+/*
+ * Rotate indices such that first_index is index 0
+ */
+static int rotate_index(const struct playlist_info* playlist, int index)
+{
+ index -= playlist->first_index;
+ if (index < 0)
+ index += playlist->amount;
+
+ return index;
+}
+
+/*
+ * sync control file to disk
+ */
+static void sync_control(struct playlist_info* playlist, bool force)
+{
+#ifdef HAVE_DIRCACHE
+ if (playlist->started && force)
+#else
+ (void) force;
+
+ if (playlist->started)
+#endif
+ {
+ if (playlist->pending_control_sync)
+ {
+ mutex_lock(playlist->control_mutex);
+ fsync(playlist->control_fd);
+ playlist->pending_control_sync = false;
+ mutex_unlock(playlist->control_mutex);
+ }
+ }
+}
+
+/*
+ * Flush any cached control commands to disk. Called when playlist is being
+ * modified. Returns 0 on success and -1 on failure.
+ */
+static int flush_cached_control(struct playlist_info* playlist)
+{
+ int result = 0;
+ int i;
+
+ if (!playlist->num_cached)
+ return 0;
+
+ lseek(playlist->control_fd, 0, SEEK_END);
+
+ for (i=0; i<playlist->num_cached; i++)
+ {
+ struct playlist_control_cache* cache =
+ &(playlist->control_cache[i]);
+
+ switch (cache->command)
+ {
+ case PLAYLIST_COMMAND_PLAYLIST:
+ result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
+ cache->i1, cache->s1, cache->s2);
+ break;
+ case PLAYLIST_COMMAND_ADD:
+ case PLAYLIST_COMMAND_QUEUE:
+ result = fdprintf(playlist->control_fd, "%c:%d:%d:",
+ (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
+ cache->i1, cache->i2);
+ if (result > 0)
+ {
+ /* save the position in file where name is written */
+ int* seek_pos = (int *)cache->data;
+ *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
+ result = fdprintf(playlist->control_fd, "%s\n",
+ cache->s1);
+ }
+ break;
+ case PLAYLIST_COMMAND_DELETE:
+ result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
+ break;
+ case PLAYLIST_COMMAND_SHUFFLE:
+ result = fdprintf(playlist->control_fd, "S:%d:%d\n",
+ cache->i1, cache->i2);
+ break;
+ case PLAYLIST_COMMAND_UNSHUFFLE:
+ result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
+ break;
+ case PLAYLIST_COMMAND_RESET:
+ result = fdprintf(playlist->control_fd, "R\n");
+ break;
+ default:
+ break;
+ }
+
+ if (result <= 0)
+ break;
+ }
+
+ if (result > 0)
+ {
+ playlist->num_cached = 0;
+ playlist->pending_control_sync = true;
+
+ result = 0;
+ }
+ else
+ {
+ result = -1;
+ splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
+ }
+
+ return result;
+}
+
+/*
+ * Update control data with new command. Depending on the command, it may be
+ * cached or flushed to disk.
+ */
+static int update_control(struct playlist_info* playlist,
+ enum playlist_command command, int i1, int i2,
+ const char* s1, const char* s2, void* data)
+{
+ int result = 0;
+ struct playlist_control_cache* cache;
+ bool flush = false;
+
+ mutex_lock(playlist->control_mutex);
+
+ cache = &(playlist->control_cache[playlist->num_cached++]);
+
+ cache->command = command;
+ cache->i1 = i1;
+ cache->i2 = i2;
+ cache->s1 = s1;
+ cache->s2 = s2;
+ cache->data = data;
+
+ switch (command)
+ {
+ case PLAYLIST_COMMAND_PLAYLIST:
+ case PLAYLIST_COMMAND_ADD:
+ case PLAYLIST_COMMAND_QUEUE:
+#ifndef HAVE_DIRCACHE
+ case PLAYLIST_COMMAND_DELETE:
+ case PLAYLIST_COMMAND_RESET:
+#endif
+ flush = true;
+ break;
+ case PLAYLIST_COMMAND_SHUFFLE:
+ case PLAYLIST_COMMAND_UNSHUFFLE:
+ default:
+ /* only flush when needed */
+ break;
+ }
+
+ if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
+ result = flush_cached_control(playlist);
+
+ mutex_unlock(playlist->control_mutex);
+
+ return result;
+}
+
+/*
+ * store directory and name of playlist file
+ */
+static void update_playlist_filename(struct playlist_info* playlist,
+ const char *dir, const char *file)
+{
+ char *sep="";
+ int dirlen = strlen(dir);
+
+ playlist->utf8 = is_m3u8(file);
+
+ /* If the dir does not end in trailing slash, we use a separator.
+ Otherwise we don't. */
+ if(!dirlen || '/' != dir[dirlen-1])
+ {
+ sep="/";
+ dirlen++;
+ }
+
+ playlist->dirlen = dirlen;
+
+ snprintf(playlist->filename, sizeof(playlist->filename),
+ "%s%s%s", dir, sep, file);
+}
+
+/*
* remove any files and indices associated with the playlist
*/
static void empty_playlist(struct playlist_info* playlist, bool resume)
{
- playlist->filename[0] = '\0';
playlist->utf8 = true;
+ playlist->control_created = false;
+ playlist->in_ram = false;
- if(playlist->fd >= 0)
- /* If there is an already open playlist, close it. */
+ if(playlist->fd >= 0) /* If there is an already open playlist, close it. */
+ {
close(playlist->fd);
+ }
playlist->fd = -1;
if(playlist->control_fd >= 0)
close(playlist->control_fd);
playlist->control_fd = -1;
- playlist->control_created = false;
- playlist->in_ram = false;
+ playlist->num_inserted_tracks = 0;
if (playlist->buffer)
playlist->buffer[0] = 0;
@@ -326,14 +493,16 @@ static void empty_playlist(struct playlist_info* playlist, bool resume)
playlist->first_index = 0;
playlist->amount = 0;
playlist->last_insert_pos = -1;
- playlist->seed = 0;
- playlist->shuffle_modified = false;
+
playlist->deleted = false;
- playlist->num_inserted_tracks = 0;
playlist->started = false;
+ playlist->pending_control_sync = false;
+ playlist->shuffle_modified = false;
+ playlist->seed = 0;
playlist->num_cached = 0;
- playlist->pending_control_sync = false;
+
+ playlist->filename[0] = '\0';
if (!resume && playlist->current)
{
@@ -344,6 +513,73 @@ static void empty_playlist(struct playlist_info* playlist, bool resume)
}
/*
+ * Returns absolute path of track
+ *
+ * dest: output buffer
+ * src: the file name from the playlist
+ * dir: the absolute path to the directory where the playlist resides
+ *
+ * The type of path in "src" determines what will be written to "dest":
+ *
+ * 1. UNIX-style absolute paths (/foo/bar) remain unaltered
+ * 2. Windows-style absolute paths (C:/foo/bar) will be converted into an
+ * absolute path by replacing the drive letter with the volume that the
+ * *playlist* resides on, ie. the volume in "dir"
+ * 3. Relative paths are converted to absolute paths by prepending "dir".
+ * This also applies to Windows-style relative paths "C:foo/bar" where
+ * the drive letter is accepted but ignored.
+ */
+static ssize_t format_track_path(char *dest, char *src, int buf_length,
+ const char *dir)
+{
+ size_t len = 0;
+
+ /* Look for the end of the string */
+ while (1)
+ {
+ int c = src[len];
+ if (c == '\n' || c == '\r' || c == '\0')
+ break;
+ len++;
+ }
+
+ /* Now work back killing white space */
+ while (len > 0)
+ {
+ int c = src[len - 1];
+ if (c != '\t' && c != ' ')
+ break;
+ len--;
+ }
+
+ src[len] = '\0';
+
+ /* Replace backslashes with forward slashes */
+ path_correct_separators(src, src);
+
+ /* Handle Windows-style absolute paths */
+ if (path_strip_drive(src, (const char **)&src, true) >= 0 &&
+ src[-1] == PATH_SEPCH)
+ {
+ #ifdef HAVE_MULTIVOLUME
+ const char *p;
+ path_strip_last_volume(dir, &p, false);
+ dir = strmemdupa(dir, p - dir); /* empty if no volspec on dir */
+ #else
+ dir = ""; /* only volume is root */
+ #endif
+ }
+
+ len = path_append(dest, *dir ? dir : PATH_ROOTSTR, src, buf_length);
+ if (len >= (size_t)buf_length)
+ return -1; /* buffer too small */
+
+ path_remove_dot_segments (dest, dest);
+
+ return strlen (dest);
+}
+
+/*
* Initialize a new playlist for viewing/editing/playing. dir is the
* directory where the playlist is located and file is the filename.
*/
@@ -363,7 +599,7 @@ static void new_playlist(struct playlist_info* playlist, const char *dir,
else
dirused = ""; /* empty playlist */
}
-
+
update_playlist_filename(playlist, dirused, fileused);
if (playlist->control_fd >= 0)
@@ -375,30 +611,6 @@ static void new_playlist(struct playlist_info* playlist, const char *dir,
}
/*
- * create control file for playlist
- */
-static void create_control(struct playlist_info* playlist)
-{
- playlist->control_fd = open(playlist->control_filename,
- O_CREAT|O_RDWR|O_TRUNC, 0666);
- if (playlist->control_fd < 0)
- {
- if (check_rockboxdir())
- {
- cond_talk_ids_fq(LANG_PLAYLIST_CONTROL_ACCESS_ERROR);
- splashf(HZ*2, (unsigned char *)"%s (%d)",
- str(LANG_PLAYLIST_CONTROL_ACCESS_ERROR),
- playlist->control_fd);
- }
- playlist->control_created = false;
- }
- else
- {
- playlist->control_created = true;
- }
-}
-
-/*
* validate the control file. This may include creating/initializing it if
* necessary;
*/
@@ -429,6 +641,44 @@ static int check_control(struct playlist_info* playlist)
return 0;
}
+
+/*
+ * Display buffer full message
+ */
+static void display_buffer_full(void)
+{
+ splash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
+}
+
+/*
+ * Display splash message showing progress of playlist/directory insertion or
+ * save.
+ */
+static void display_playlist_count(int count, const unsigned char *fmt,
+ bool final)
+{
+ static long talked_tick = 0;
+ long id = P2ID(fmt);
+
+ if(id >= 0 && global_settings.talk_menu)
+ {
+ long next_tick = talked_tick + (HZ * 5);
+
+ if (final || talked_tick == 0)
+ next_tick = current_tick - 1;
+
+ if(count && TIME_AFTER(current_tick, next_tick))
+ {
+ talked_tick = current_tick;
+ talk_number(count, false);
+ talk_id(id, true);
+ }
+ }
+
+ splashf(0, P2STR(fmt), count, str(LANG_OFF_ABORT));
+}
+
+
/*
* recreate the control file based on current playlist entries
*/
@@ -532,36 +782,12 @@ static int recreate_control(struct playlist_info* playlist)
}
/*
- * store directory and name of playlist file
- */
-static void update_playlist_filename(struct playlist_info* playlist,
- const char *dir, const char *file)
-{
- char *sep="";
- int dirlen = strlen(dir);
-
- playlist->utf8 = is_m3u8(file);
-
- /* If the dir does not end in trailing slash, we use a separator.
- Otherwise we don't. */
- if(!dirlen || '/' != dir[dirlen-1])
- {
- sep="/";
- dirlen++;
- }
-
- playlist->dirlen = dirlen;
-
- snprintf(playlist->filename, sizeof(playlist->filename),
- "%s%s%s", dir, sep, file);
-}
-
-/*
* calculate track offsets within a playlist file
*/
static int add_indices_to_playlist(struct playlist_info* playlist,
char* buffer, size_t buflen)
{
+ int playlist_fd;
ssize_t nread;
unsigned int i = 0;
unsigned int count = 0;
@@ -572,20 +798,36 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
if (!buflen)
buffer = alloca((buflen = 64));
+ mutex_lock(playlist->control_mutex); /* read can yield! */
+
if(-1 == playlist->fd)
+ {
playlist->fd = open_utf8(playlist->filename, O_RDONLY);
+ if(playlist->fd >= 0 && lseek(playlist->fd, 0, SEEK_CUR) > 0)
+ playlist->utf8 = true; /* Override any earlier indication. */
+ }
+
if(playlist->fd < 0)
return -1; /* failure */
- if((i = lseek(playlist->fd, 0, SEEK_CUR)) > 0)
- playlist->utf8 = true; /* Override any earlier indication. */
+
+ i = lseek(playlist->fd, playlist->utf8 ? BOM_UTF_8_SIZE : 0, SEEK_SET);
+
+ playlist_fd = playlist->fd;
+ playlist->fd = -2; /* DEBUGGING Remove me! */
splash(0, ID2P(LANG_WAIT));
store_index = true;
+ /* invalidate playlist in case we yield */
+ int amount = playlist->amount;
+ playlist->amount = -1;
+ int max_playlist_size = playlist->max_playlist_size;
+ playlist->max_playlist_size = 0;
+
while(1)
{
- nread = read(playlist->fd, buffer, buflen);
+ nread = read(playlist_fd, buffer, buflen);
/* Terminate on EOF */
if(nread <= 0)
break;
@@ -605,18 +847,18 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
if(*p != '#')
{
- if ( playlist->amount >= playlist->max_playlist_size ) {
+ if ( amount >= max_playlist_size ) {
display_buffer_full();
result = -1;
goto exit;
}
/* Store a new entry */
- playlist->indices[ playlist->amount ] = i+count;
+ playlist->indices[ amount ] = i+count;
#ifdef HAVE_DIRCACHE
- copy_filerefs(&playlist->dcfrefs[playlist->amount], NULL, 1);
+ copy_filerefs(&playlist->dcfrefs[amount], NULL, 1);
#endif
- playlist->amount++;
+ amount++;
}
}
}
@@ -625,6 +867,14 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
}
exit:
+/* v DEBUGGING Remove me! */
+ if (playlist->fd != -2)
+ splashf(HZ, "Error playlist fd");
+ playlist->fd = playlist_fd;
+/* ^ DEBUGGING Remove me! */
+ playlist->amount = amount;
+ playlist->max_playlist_size = max_playlist_size;
+ mutex_unlock(playlist->control_mutex);
#ifdef HAVE_DIRCACHE
queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
#endif
@@ -632,6 +882,372 @@ exit:
return result;
}
+
+/*
+ * Checks if there are any music files in the dir or any of its
+ * subdirectories. May be called recursively.
+ */
+static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
+{
+ int result = -1;
+ size_t dirlen = strlen(dir);
+ int num_files = 0;
+ int i;
+ struct entry *files;
+ bool has_music = false;
+ bool has_subdir = false;
+ struct tree_context* tc = tree_get_context();
+
+ if (path_append(dir + dirlen, PA_SEP_HARD, subdir, MAX_PATH - dirlen) >=
+ MAX_PATH - dirlen)
+ {
+ return 0;
+ }
+
+ if (ft_load(tc, dir) < 0)
+ {
+ return -2;
+ }
+
+ tree_lock_cache(tc);
+ files = tree_get_entries(tc);
+ num_files = tc->filesindir;
+
+ for (i=0; i<num_files; i++)
+ {
+ if (files[i].attr & ATTR_DIRECTORY)
+ has_subdir = true;
+ else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
+ {
+ has_music = true;
+ break;
+ }
+ }
+
+ if (has_music)
+ {
+ tree_unlock_cache(tc);
+ return 0;
+ }
+
+ if (has_subdir && recurse)
+ {
+ for (i=0; i<num_files; i++)
+ {
+ if (action_userabort(TIMEOUT_NOBLOCK))
+ {
+ result = -2;
+ break;
+ }
+
+ if (files[i].attr & ATTR_DIRECTORY)
+ {
+ result = check_subdir_for_music(dir, files[i].name, true);
+ if (!result)
+ break;
+ }
+ }
+ }
+ tree_unlock_cache(tc);
+
+ if (result < 0)
+ {
+ if (dirlen)
+ {
+ dir[dirlen] = '\0';
+ }
+ else
+ {
+ strcpy(dir, PATH_ROOTSTR);
+ }
+
+ /* we now need to reload our current directory */
+ if(ft_load(tc, dir) < 0)
+ splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
+ }
+ return result;
+}
+
+/*
+ * search through all the directories (starting with the current) to find
+ * one that has tracks to play
+ */
+static int get_next_dir(char *dir, bool is_forward)
+{
+ struct playlist_info* playlist = &current_playlist;
+ int result = -1;
+ char *start_dir = NULL;
+ bool exit = false;
+ struct tree_context* tc = tree_get_context();
+ int saved_dirfilter = *(tc->dirfilter);
+ unsigned int base_len;
+
+ if (global_settings.constrain_next_folder)
+ {
+ /* constrain results to directories below user's start directory */
+ strcpy(dir, global_settings.start_directory);
+ base_len = strlen(dir);
+
+ /* strip any trailing slash from base directory */
+ if (base_len > 0 && dir[base_len - 1] == '/')
+ {
+ base_len--;
+ dir[base_len] = '\0';
+ }
+ }
+ else
+ {
+ /* start from root directory */
+ dir[0] = '\0';
+ base_len = 0;
+ }
+
+ /* process random folder advance */
+ if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
+ {
+ int fd = open(ROCKBOX_DIR "/folder_advance_list.dat", O_RDONLY);
+ if (fd >= 0)
+ {
+ int folder_count = 0;
+ ssize_t nread = read(fd,&folder_count,sizeof(int));
+ if ((nread == sizeof(int)) && folder_count)
+ {
+ char buffer[MAX_PATH];
+ /* give up looking for a directory after we've had four
+ times as many tries as there are directories. */
+ unsigned long allowed_tries = folder_count * 4;
+ int i;
+ srand(current_tick);
+ *(tc->dirfilter) = SHOW_MUSIC;
+ tc->sort_dir = global_settings.sort_dir;
+ while (!exit && allowed_tries--)
+ {
+ i = rand() % folder_count;
+ lseek(fd, sizeof(int) + (MAX_PATH * i), SEEK_SET);
+ read(fd, buffer, MAX_PATH);
+ /* is the current dir within our base dir and has music? */
+ if ((base_len == 0 || !strncmp(buffer, dir, base_len))
+ && check_subdir_for_music(buffer, "", false) == 0)
+ exit = true;
+ }
+ close(fd);
+ *(tc->dirfilter) = saved_dirfilter;
+ tc->sort_dir = global_settings.sort_dir;
+ reload_directory();
+ if (exit)
+ {
+ strcpy(dir,buffer);
+ return 0;
+ }
+ }
+ else
+ close(fd);
+ }
+ }
+
+ /* if the current file is within our base dir, use its dir instead */
+ if (base_len == 0 || !strncmp(playlist->filename, dir, base_len))
+ strmemccpy(dir, playlist->filename, playlist->dirlen);
+
+ /* use the tree browser dircache to load files */
+ *(tc->dirfilter) = SHOW_ALL;
+
+ /* set up sorting/direction */
+ tc->sort_dir = global_settings.sort_dir;
+ if (!is_forward)
+ {
+ static const char sortpairs[] =
+ {
+ [SORT_ALPHA] = SORT_ALPHA_REVERSED,
+ [SORT_DATE] = SORT_DATE_REVERSED,
+ [SORT_TYPE] = SORT_TYPE_REVERSED,
+ [SORT_ALPHA_REVERSED] = SORT_ALPHA,
+ [SORT_DATE_REVERSED] = SORT_DATE,
+ [SORT_TYPE_REVERSED] = SORT_TYPE,
+ };
+
+ if ((unsigned)tc->sort_dir < sizeof(sortpairs))
+ tc->sort_dir = sortpairs[tc->sort_dir];
+ }
+
+ while (!exit)
+ {
+ struct entry *files;
+ int num_files = 0;
+ int i;
+
+ if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
+ {
+ exit = true;
+ result = -1;
+ break;
+ }
+
+ tree_lock_cache(tc);
+ files = tree_get_entries(tc);
+ num_files = tc->filesindir;
+
+ for (i=0; i<num_files; i++)
+ {
+ /* user abort */
+ if (action_userabort(TIMEOUT_NOBLOCK))
+ {
+ result = -1;
+ exit = true;
+ break;
+ }
+
+ if (files[i].attr & ATTR_DIRECTORY)
+ {
+ if (!start_dir)
+ {
+ result = check_subdir_for_music(dir, files[i].name, true);
+ if (result != -1)
+ {
+ exit = true;
+ break;
+ }
+ }
+ else if (!strcmp(start_dir, files[i].name))
+ start_dir = NULL;
+ }
+ }
+ tree_unlock_cache(tc);
+
+ if (!exit)
+ {
+ /* we've already descended to the base dir with nothing found,
+ check whether that contains music */
+ if (strlen(dir) <= base_len)
+ {
+ result = check_subdir_for_music(dir, "", true);
+ if (result == -1)
+ /* there's no music files in the base directory,
+ treat as a fatal error */
+ result = -2;
+ break;
+ }
+ else
+ {
+ /* move down to parent directory. current directory name is
+ stored as the starting point for the search in parent */
+ start_dir = strrchr(dir, '/');
+ if (start_dir)
+ {
+ *start_dir = '\0';
+ start_dir++;
+ }
+ else
+ break;
+ }
+ }
+ }
+
+ /* restore dirfilter */
+ *(tc->dirfilter) = saved_dirfilter;
+ tc->sort_dir = global_settings.sort_dir;
+
+ return result;
+}
+
+static int get_next_directory(char *dir){
+ return get_next_dir(dir, true);
+}
+
+static int get_previous_directory(char *dir){
+ return get_next_dir(dir, false);
+}
+
+
+/*
+ * gets pathname for track at seek index
+ */
+static int get_filename(struct playlist_info* playlist, int index, int seek,
+ bool control_file, char *buf, int buf_length)
+{
+ int fd;
+ int max = -1;
+ char tmp_buf[MAX_PATH+1];
+ char dir_buf[MAX_PATH+1];
+ bool utf8 = playlist->utf8;
+
+ if (buf_length > MAX_PATH+1)
+ buf_length = MAX_PATH+1;
+
+#ifdef HAVE_DIRCACHE
+ if (playlist->dcfrefs)
+ {
+ max = dircache_get_fileref_path(&playlist->dcfrefs[index],
+ tmp_buf, sizeof(tmp_buf));
+ }
+#endif /* HAVE_DIRCACHE */
+
+ if (playlist->in_ram && !control_file && max < 0)
+ {
+ strmemccpy(tmp_buf, (char*)&playlist->buffer[seek], sizeof(tmp_buf));
+ }
+ else if (max < 0)
+ {
+ mutex_lock(playlist->control_mutex);
+
+ if (control_file)
+ {
+ fd = playlist->control_fd;
+ utf8 = true;
+ }
+ else
+ {
+ if(-1 == playlist->fd)
+ playlist->fd = open(playlist->filename, O_RDONLY);
+
+ fd = playlist->fd;
+ }
+
+ if(-1 != fd)
+ {
+
+ if (lseek(fd, seek, SEEK_SET) != seek)
+ max = -1;
+ else
+ {
+ max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
+
+ if (max > 0)
+ {
+ /* playlist file may end without a new line - terminate buffer */
+ tmp_buf[MIN(max, (int)sizeof(tmp_buf) - 1)] = '\0';
+
+ /* Use dir_buf as a temporary buffer. Note that dir_buf must
+ * be as large as tmp_buf.
+ */
+ if (!utf8)
+ max = convert_m3u(tmp_buf, max, sizeof(tmp_buf), dir_buf);
+ }
+ }
+ }
+
+ mutex_unlock(playlist->control_mutex);
+
+ if (max < 0)
+ {
+ if (usb_detect() == USB_INSERTED)
+ ; /* ignore error on usb plug */
+ else if (control_file)
+ splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ else
+ splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
+
+ return max;
+ }
+ }
+
+ strmemccpy(dir_buf, playlist->filename, playlist->dirlen);
+
+ return format_track_path(buf, tmp_buf, buf_length, dir_buf);
+
+ (void)index;
+}
+
/*
* Utility function to create a new playlist, fill it with the next or
* previous directory, shuffle it if needed, and start playback.
@@ -676,33 +1292,6 @@ static int create_and_play_dir(int direction, bool play_last)
}
/*
- * Removes all tracks, from the playlist, leaving the presently playing
- * track queued.
- */
-int playlist_remove_all_tracks(struct playlist_info *playlist)
-{
- int result;
-
- if (playlist == NULL)
- playlist = &current_playlist;
-
- while (playlist->index > 0)
- if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
- return result;
-
- while (playlist->amount > 1)
- if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
- return result;
-
- if (playlist->amount == 1) {
- playlist->indices[0] |= PLAYLIST_QUEUED;
- }
-
- return 0;
-}
-
-
-/*
* Add track to playlist at specified position. There are seven special
* positions that can be specified:
* PLAYLIST_PREPEND - Add track at beginning of playlist
@@ -778,16 +1367,16 @@ static int add_track_to_playlist(struct playlist_info* playlist,
int offset;
int n = playlist->amount -
rotate_index(playlist, playlist->index);
-
+
if (n > 0)
offset = rand() % n;
else
offset = 0;
-
+
position = playlist->index + offset + 1;
if (position >= playlist->amount)
position -= playlist->amount;
-
+
insert_position = position;
}
else
@@ -803,11 +1392,11 @@ static int add_track_to_playlist(struct playlist_info* playlist,
case PLAYLIST_REPLACE:
if (playlist_remove_all_tracks(playlist) < 0)
return -1;
-
+
playlist->last_insert_pos = position = insert_position = playlist->index + 1;
break;
}
-
+
if (queue)
flags |= PLAYLIST_QUEUED;
@@ -820,7 +1409,7 @@ static int add_track_to_playlist(struct playlist_info* playlist,
playlist->dcfrefs[i] = playlist->dcfrefs[i-1];
#endif
}
-
+
/* update stored indices if needed */
if (orig_position < 0)
@@ -856,7 +1445,7 @@ static int add_track_to_playlist(struct playlist_info* playlist,
playlist->amount++;
playlist->num_inserted_tracks++;
-
+
return insert_position;
}
@@ -893,7 +1482,7 @@ static int directory_search_callback(char* filename, void* context)
count_str = ID2P(LANG_PLAYLIST_INSERT_COUNT);
display_playlist_count(c->count, count_str, false);
-
+
if ((c->count) == PLAYLIST_DISPLAY_COUNT &&
(audio_status() & AUDIO_STATUS_PLAY) &&
c->playlist->started)
@@ -956,11 +1545,32 @@ static int remove_track_from_playlist(struct playlist_info* playlist,
sync_control(playlist, false);
}
-
+
return 0;
}
/*
+ * Search for the seek track and set appropriate indices. Used after shuffle
+ * to make sure the current index is still pointing to correct track.
+ */
+static void find_and_set_playlist_index(struct playlist_info* playlist,
+ unsigned int seek)
+{
+ int i;
+
+ /* Set the index to the current song */
+ for (i=0; i<playlist->amount; i++)
+ {
+ if (playlist->indices[i] == seek)
+ {
+ playlist->index = playlist->first_index = i;
+
+ break;
+ }
+ }
+}
+
+/*
* randomly rearrange the array of indices for the playlist. If start_current
* is true then update the index to the new index of the current playing track
*/
@@ -971,7 +1581,7 @@ static int randomise_playlist(struct playlist_info* playlist,
int count;
int candidate;
unsigned int current = playlist->indices[playlist->index];
-
+
/* seed 0 is used to identify sorted playlist for resume purposes */
if (seed == 0)
seed = 1;
@@ -1014,11 +1624,38 @@ static int randomise_playlist(struct playlist_info* playlist,
update_control(playlist, PLAYLIST_COMMAND_SHUFFLE, seed,
playlist->first_index, NULL, NULL, NULL);
}
-
+
return 0;
}
/*
+ * used to sort track indices. Sort order is as follows:
+ * 1. Prepended tracks (in prepend order)
+ * 2. Playlist/directory tracks (in playlist order)
+ * 3. Inserted/Appended tracks (in insert order)
+ */
+static int compare(const void* p1, const void* p2)
+{
+ unsigned long* e1 = (unsigned long*) p1;
+ unsigned long* e2 = (unsigned long*) p2;
+ unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
+ unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
+
+ if (flags1 == flags2)
+ return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
+ else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
+ flags2 == PLAYLIST_INSERT_TYPE_APPEND)
+ return -1;
+ else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
+ flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
+ return 1;
+ else if (flags1 && flags2)
+ return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
+ else
+ return *e1 - *e2;
+}
+
+/*
* Sort the array of indices for the playlist. If start_current is true then
* set the index to the new index of the current song.
* Also while going to unshuffled mode set the first_index to 0.
@@ -1054,7 +1691,7 @@ static int sort_playlist(struct playlist_info* playlist, bool start_current,
update_control(playlist, PLAYLIST_COMMAND_UNSHUFFLE,
playlist->first_index, -1, NULL, NULL, NULL);
}
-
+
return 0;
}
@@ -1066,7 +1703,7 @@ static int calculate_step_count(const struct playlist_info *playlist, int steps)
int i, count, direction;
int index;
int stepped_count = 0;
-
+
if (steps < 0)
{
direction = -1;
@@ -1104,28 +1741,6 @@ static int calculate_step_count(const struct playlist_info *playlist, int steps)
return steps;
}
-/* Marks the index of the track to be skipped that is "steps" away from
- * current playing track.
- */
-void playlist_skip_entry(struct playlist_info *playlist, int steps)
-{
- int index;
-
- if (playlist == NULL)
- playlist = &current_playlist;
-
- /* need to account for already skipped tracks */
- steps = calculate_step_count(playlist, steps);
-
- index = playlist->index + steps;
- if (index < 0)
- index += playlist->amount;
- else if (index >= playlist->amount)
- index -= playlist->amount;
-
- playlist->indices[index] |= PLAYLIST_SKIPPED;
-}
-
/*
* returns the index of the track that is "steps" away from current playing
* track.
@@ -1204,56 +1819,8 @@ static int get_next_index(const struct playlist_info* playlist, int steps,
/* No luck if the whole playlist was bad. */
if (playlist->indices[next_index] & PLAYLIST_SKIPPED)
return -1;
-
- return next_index;
-}
-
-/*
- * Search for the seek track and set appropriate indices. Used after shuffle
- * to make sure the current index is still pointing to correct track.
- */
-static void find_and_set_playlist_index(struct playlist_info* playlist,
- unsigned int seek)
-{
- int i;
-
- /* Set the index to the current song */
- for (i=0; i<playlist->amount; i++)
- {
- if (playlist->indices[i] == seek)
- {
- playlist->index = playlist->first_index = i;
- break;
- }
- }
-}
-
-/*
- * used to sort track indices. Sort order is as follows:
- * 1. Prepended tracks (in prepend order)
- * 2. Playlist/directory tracks (in playlist order)
- * 3. Inserted/Appended tracks (in insert order)
- */
-static int compare(const void* p1, const void* p2)
-{
- unsigned long* e1 = (unsigned long*) p1;
- unsigned long* e2 = (unsigned long*) p2;
- unsigned long flags1 = *e1 & PLAYLIST_INSERT_TYPE_MASK;
- unsigned long flags2 = *e2 & PLAYLIST_INSERT_TYPE_MASK;
-
- if (flags1 == flags2)
- return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
- else if (flags1 == PLAYLIST_INSERT_TYPE_PREPEND ||
- flags2 == PLAYLIST_INSERT_TYPE_APPEND)
- return -1;
- else if (flags1 == PLAYLIST_INSERT_TYPE_APPEND ||
- flags2 == PLAYLIST_INSERT_TYPE_PREPEND)
- return 1;
- else if (flags1 && flags2)
- return (*e1 & PLAYLIST_SEEK_MASK) - (*e2 & PLAYLIST_SEEK_MASK);
- else
- return *e1 - *e2;
+ return next_index;
}
#ifdef HAVE_DIRCACHE
@@ -1262,7 +1829,7 @@ static int compare(const void* p1, const void* p2)
* without affecting playlist load up performance. This thread also flushes
* any pending control commands when the disk spins up.
*/
-static void playlist_flush_callback(void)
+static void flush_playlist_callback(void)
{
struct playlist_info *playlist;
playlist = &current_playlist;
@@ -1278,7 +1845,7 @@ static void playlist_flush_callback(void)
}
}
-static void playlist_thread(void)
+static void thread_playlist(void)
{
struct queue_event ev;
bool dirty_pointers = false;
@@ -1315,12 +1882,12 @@ static void playlist_thread(void)
if (playlist->control_fd >= 0)
{
if (playlist->num_cached > 0)
- register_storage_idle_func(playlist_flush_callback);
+ register_storage_idle_func(flush_playlist_callback);
}
if (!playlist->dcfrefs || playlist->amount <= 0)
break ;
-
+
/* Check if previously loaded pointers are intact. */
if (!dirty_pointers)
break ;
@@ -1339,7 +1906,7 @@ static void playlist_thread(void)
/* Process only pointers that are superficially stale. */
if (dircache_search(DCS_FILEREF, &playlist->dcfrefs[index], NULL) > 0)
continue ;
-
+
control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
@@ -1362,10 +1929,10 @@ static void playlist_thread(void)
if (index == playlist->amount)
dirty_pointers = false;
-
+
break ;
}
-
+
case SYS_USB_CONNECTED:
usb_acknowledge(SYS_USB_CONNECTED_ACK);
usb_wait_for_disconnect(&playlist_queue);
@@ -1376,626 +1943,20 @@ static void playlist_thread(void)
#endif
/*
- * gets pathname for track at seek index
+ * Allocate a temporary buffer for loading playlists
*/
-static int get_filename(struct playlist_info* playlist, int index, int seek,
- bool control_file, char *buf, int buf_length)
+static int alloc_tempbuf(size_t* buflen)
{
- int fd;
- int max = -1;
- char tmp_buf[MAX_PATH+1];
- char dir_buf[MAX_PATH+1];
- bool utf8 = playlist->utf8;
-
- if (buf_length > MAX_PATH+1)
- buf_length = MAX_PATH+1;
-
-#ifdef HAVE_DIRCACHE
- if (playlist->dcfrefs)
+ /* request a reasonable size first */
+ int handle = core_alloc_ex(NULL, PLAYLIST_LOAD_BUFLEN, &buflib_ops_locked);
+ if (handle > 0)
{
- max = dircache_get_fileref_path(&playlist->dcfrefs[index],
- tmp_buf, sizeof(tmp_buf));
+ *buflen = PLAYLIST_LOAD_BUFLEN;
+ return handle;
}
-#endif /* HAVE_DIRCACHE */
-
- if (playlist->in_ram && !control_file && max < 0)
- {
- max = strlcpy(tmp_buf, (char*)&playlist->buffer[seek], sizeof(tmp_buf));
- }
- else if (max < 0)
- {
- mutex_lock(playlist->control_mutex);
-
- if (control_file)
- {
- fd = playlist->control_fd;
- utf8 = true;
- }
- else
- {
- if(-1 == playlist->fd)
- playlist->fd = open(playlist->filename, O_RDONLY);
-
- fd = playlist->fd;
- }
-
- if(-1 != fd)
- {
-
- if (lseek(fd, seek, SEEK_SET) != seek)
- max = -1;
- else
- {
- max = read(fd, tmp_buf, MIN((size_t) buf_length, sizeof(tmp_buf)));
-
- if (max > 0)
- {
- /* playlist file may end without a new line - terminate buffer */
- tmp_buf[MIN(max, (int)sizeof(tmp_buf) - 1)] = '\0';
- /* Use dir_buf as a temporary buffer. Note that dir_buf must
- * be as large as tmp_buf.
- */
- if (!utf8)
- max = convert_m3u(tmp_buf, max, sizeof(tmp_buf), dir_buf);
- }
- }
- }
-
- mutex_unlock(playlist->control_mutex);
-
- if (max < 0)
- {
- if (usb_detect() == USB_INSERTED)
- ; /* ignore error on usb plug */
- else if (control_file)
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- else
- splash(HZ*2, ID2P(LANG_PLAYLIST_ACCESS_ERROR));
-
- return max;
- }
- }
-
- strlcpy(dir_buf, playlist->filename, playlist->dirlen);
-
- return format_track_path(buf, tmp_buf, buf_length, dir_buf);
-
- (void)index;
-}
-
-static int get_next_directory(char *dir){
- return get_next_dir(dir, true);
-}
-
-static int get_previous_directory(char *dir){
- return get_next_dir(dir, false);
-}
-
-/*
- * search through all the directories (starting with the current) to find
- * one that has tracks to play
- */
-static int get_next_dir(char *dir, bool is_forward)
-{
- struct playlist_info* playlist = &current_playlist;
- int result = -1;
- char *start_dir = NULL;
- bool exit = false;
- struct tree_context* tc = tree_get_context();
- int saved_dirfilter = *(tc->dirfilter);
- unsigned int base_len;
-
- if (global_settings.constrain_next_folder)
- {
- /* constrain results to directories below user's start directory */
- strcpy(dir, global_settings.start_directory);
- base_len = strlen(dir);
-
- /* strip any trailing slash from base directory */
- if (base_len > 0 && dir[base_len - 1] == '/')
- {
- base_len--;
- dir[base_len] = '\0';
- }
- }
- else
- {
- /* start from root directory */
- dir[0] = '\0';
- base_len = 0;
- }
-
- /* process random folder advance */
- if (global_settings.next_folder == FOLDER_ADVANCE_RANDOM)
- {
- int fd = open(ROCKBOX_DIR "/folder_advance_list.dat", O_RDONLY);
- if (fd >= 0)
- {
- int folder_count = 0;
- ssize_t nread = read(fd,&folder_count,sizeof(int));
- if ((nread == sizeof(int)) && folder_count)
- {
- char buffer[MAX_PATH];
- /* give up looking for a directory after we've had four
- times as many tries as there are directories. */
- unsigned long allowed_tries = folder_count * 4;
- int i;
- srand(current_tick);
- *(tc->dirfilter) = SHOW_MUSIC;
- tc->sort_dir = global_settings.sort_dir;
- while (!exit && allowed_tries--)
- {
- i = rand() % folder_count;
- lseek(fd, sizeof(int) + (MAX_PATH * i), SEEK_SET);
- read(fd, buffer, MAX_PATH);
- /* is the current dir within our base dir and has music? */
- if ((base_len == 0 || !strncmp(buffer, dir, base_len))
- && check_subdir_for_music(buffer, "", false) == 0)
- exit = true;
- }
- close(fd);
- *(tc->dirfilter) = saved_dirfilter;
- tc->sort_dir = global_settings.sort_dir;
- reload_directory();
- if (exit)
- {
- strcpy(dir,buffer);
- return 0;
- }
- }
- else
- close(fd);
- }
- }
-
- /* if the current file is within our base dir, use its dir instead */
- if (base_len == 0 || !strncmp(playlist->filename, dir, base_len))
- strlcpy(dir, playlist->filename, playlist->dirlen);
-
- /* use the tree browser dircache to load files */
- *(tc->dirfilter) = SHOW_ALL;
-
- /* set up sorting/direction */
- tc->sort_dir = global_settings.sort_dir;
- if (!is_forward)
- {
- static const char sortpairs[] =
- {
- [SORT_ALPHA] = SORT_ALPHA_REVERSED,
- [SORT_DATE] = SORT_DATE_REVERSED,
- [SORT_TYPE] = SORT_TYPE_REVERSED,
- [SORT_ALPHA_REVERSED] = SORT_ALPHA,
- [SORT_DATE_REVERSED] = SORT_DATE,
- [SORT_TYPE_REVERSED] = SORT_TYPE,
- };
-
- if ((unsigned)tc->sort_dir < sizeof(sortpairs))
- tc->sort_dir = sortpairs[tc->sort_dir];
- }
-
- while (!exit)
- {
- struct entry *files;
- int num_files = 0;
- int i;
-
- if (ft_load(tc, (dir[0]=='\0')?"/":dir) < 0)
- {
- exit = true;
- result = -1;
- break;
- }
-
- tree_lock_cache(tc);
- files = tree_get_entries(tc);
- num_files = tc->filesindir;
-
- for (i=0; i<num_files; i++)
- {
- /* user abort */
- if (action_userabort(TIMEOUT_NOBLOCK))
- {
- result = -1;
- exit = true;
- break;
- }
-
- if (files[i].attr & ATTR_DIRECTORY)
- {
- if (!start_dir)
- {
- result = check_subdir_for_music(dir, files[i].name, true);
- if (result != -1)
- {
- exit = true;
- break;
- }
- }
- else if (!strcmp(start_dir, files[i].name))
- start_dir = NULL;
- }
- }
- tree_unlock_cache(tc);
-
- if (!exit)
- {
- /* we've already descended to the base dir with nothing found,
- check whether that contains music */
- if (strlen(dir) <= base_len)
- {
- result = check_subdir_for_music(dir, "", true);
- if (result == -1)
- /* there's no music files in the base directory,
- treat as a fatal error */
- result = -2;
- break;
- }
- else
- {
- /* move down to parent directory. current directory name is
- stored as the starting point for the search in parent */
- start_dir = strrchr(dir, '/');
- if (start_dir)
- {
- *start_dir = '\0';
- start_dir++;
- }
- else
- break;
- }
- }
- }
-
- /* restore dirfilter */
- *(tc->dirfilter) = saved_dirfilter;
- tc->sort_dir = global_settings.sort_dir;
-
- return result;
-}
-
-/*
- * Checks if there are any music files in the dir or any of its
- * subdirectories. May be called recursively.
- */
-static int check_subdir_for_music(char *dir, const char *subdir, bool recurse)
-{
- int result = -1;
- size_t dirlen = strlen(dir);
- int num_files = 0;
- int i;
- struct entry *files;
- bool has_music = false;
- bool has_subdir = false;
- struct tree_context* tc = tree_get_context();
-
- if (path_append(dir + dirlen, PA_SEP_HARD, subdir, MAX_PATH - dirlen) >=
- MAX_PATH - dirlen)
- {
- return 0;
- }
-
- if (ft_load(tc, dir) < 0)
- {
- return -2;
- }
-
- tree_lock_cache(tc);
- files = tree_get_entries(tc);
- num_files = tc->filesindir;
-
- for (i=0; i<num_files; i++)
- {
- if (files[i].attr & ATTR_DIRECTORY)
- has_subdir = true;
- else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
- {
- has_music = true;
- break;
- }
- }
-
- if (has_music)
- {
- tree_unlock_cache(tc);
- return 0;
- }
-
- if (has_subdir && recurse)
- {
- for (i=0; i<num_files; i++)
- {
- if (action_userabort(TIMEOUT_NOBLOCK))
- {
- result = -2;
- break;
- }
-
- if (files[i].attr & ATTR_DIRECTORY)
- {
- result = check_subdir_for_music(dir, files[i].name, true);
- if (!result)
- break;
- }
- }
- }
- tree_unlock_cache(tc);
-
- if (result < 0)
- {
- if (dirlen)
- {
- dir[dirlen] = '\0';
- }
- else
- {
- strcpy(dir, PATH_ROOTSTR);
- }
-
- /* we now need to reload our current directory */
- if(ft_load(tc, dir) < 0)
- splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
- }
- return result;
-}
-
-/*
- * Returns absolute path of track
- *
- * dest: output buffer
- * src: the file name from the playlist
- * dir: the absolute path to the directory where the playlist resides
- *
- * The type of path in "src" determines what will be written to "dest":
- *
- * 1. UNIX-style absolute paths (/foo/bar) remain unaltered
- * 2. Windows-style absolute paths (C:/foo/bar) will be converted into an
- * absolute path by replacing the drive letter with the volume that the
- * *playlist* resides on, ie. the volume in "dir"
- * 3. Relative paths are converted to absolute paths by prepending "dir".
- * This also applies to Windows-style relative paths "C:foo/bar" where
- * the drive letter is accepted but ignored.
- */
-static ssize_t format_track_path(char *dest, char *src, int buf_length,
- const char *dir)
-{
- size_t len = 0;
-
- /* Look for the end of the string */
- while (1)
- {
- int c = src[len];
- if (c == '\n' || c == '\r' || c == '\0')
- break;
- len++;
- }
-
- /* Now work back killing white space */
- while (len > 0)
- {
- int c = src[len - 1];
- if (c != '\t' && c != ' ')
- break;
- len--;
- }
-
- src[len] = '\0';
-
- /* Replace backslashes with forward slashes */
- path_correct_separators(src, src);
-
- /* Handle Windows-style absolute paths */
- if (path_strip_drive(src, (const char **)&src, true) >= 0 &&
- src[-1] == PATH_SEPCH)
- {
- #ifdef HAVE_MULTIVOLUME
- const char *p;
- path_strip_last_volume(dir, &p, false);
- dir = strmemdupa(dir, p - dir); /* empty if no volspec on dir */
- #else
- dir = ""; /* only volume is root */
- #endif
- }
-
- len = path_append(dest, *dir ? dir : PATH_ROOTSTR, src, buf_length);
- if (len >= (size_t)buf_length)
- return -1; /* buffer too small */
-
- path_remove_dot_segments (dest, dest);
-
- return strlen (dest);
-}
-
-/*
- * Display splash message showing progress of playlist/directory insertion or
- * save.
- */
-static void display_playlist_count(int count, const unsigned char *fmt,
- bool final)
-{
- static long talked_tick = 0;
- long id = P2ID(fmt);
- if(global_settings.talk_menu && id>=0)
- {
- if(final || (count && (talked_tick == 0
- || TIME_AFTER(current_tick, talked_tick+5*HZ))))
- {
- talked_tick = current_tick;
- talk_number(count, false);
- talk_id(id, true);
- }
- }
- fmt = P2STR(fmt);
-
- splashf(0, fmt, count, str(LANG_OFF_ABORT));
-}
-
-/*
- * Display buffer full message
- */
-static void display_buffer_full(void)
-{
- splash(HZ*2, ID2P(LANG_PLAYLIST_BUFFER_FULL));
-}
-
-/*
- * Flush any cached control commands to disk. Called when playlist is being
- * modified. Returns 0 on success and -1 on failure.
- */
-static int flush_cached_control(struct playlist_info* playlist)
-{
- int result = 0;
- int i;
-
- if (!playlist->num_cached)
- return 0;
-
- lseek(playlist->control_fd, 0, SEEK_END);
-
- for (i=0; i<playlist->num_cached; i++)
- {
- struct playlist_control_cache* cache =
- &(playlist->control_cache[i]);
-
- switch (cache->command)
- {
- case PLAYLIST_COMMAND_PLAYLIST:
- result = fdprintf(playlist->control_fd, "P:%d:%s:%s\n",
- cache->i1, cache->s1, cache->s2);
- break;
- case PLAYLIST_COMMAND_ADD:
- case PLAYLIST_COMMAND_QUEUE:
- result = fdprintf(playlist->control_fd, "%c:%d:%d:",
- (cache->command == PLAYLIST_COMMAND_ADD)?'A':'Q',
- cache->i1, cache->i2);
- if (result > 0)
- {
- /* save the position in file where name is written */
- int* seek_pos = (int *)cache->data;
- *seek_pos = lseek(playlist->control_fd, 0, SEEK_CUR);
- result = fdprintf(playlist->control_fd, "%s\n",
- cache->s1);
- }
- break;
- case PLAYLIST_COMMAND_DELETE:
- result = fdprintf(playlist->control_fd, "D:%d\n", cache->i1);
- break;
- case PLAYLIST_COMMAND_SHUFFLE:
- result = fdprintf(playlist->control_fd, "S:%d:%d\n",
- cache->i1, cache->i2);
- break;
- case PLAYLIST_COMMAND_UNSHUFFLE:
- result = fdprintf(playlist->control_fd, "U:%d\n", cache->i1);
- break;
- case PLAYLIST_COMMAND_RESET:
- result = fdprintf(playlist->control_fd, "R\n");
- break;
- default:
- break;
- }
-
- if (result <= 0)
- break;
- }
-
- if (result > 0)
- {
- playlist->num_cached = 0;
- playlist->pending_control_sync = true;
-
- result = 0;
- }
- else
- {
- result = -1;
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_UPDATE_ERROR));
- }
-
- return result;
-}
-
-/*
- * Update control data with new command. Depending on the command, it may be
- * cached or flushed to disk.
- */
-static int update_control(struct playlist_info* playlist,
- enum playlist_command command, int i1, int i2,
- const char* s1, const char* s2, void* data)
-{
- int result = 0;
- struct playlist_control_cache* cache;
- bool flush = false;
-
- mutex_lock(playlist->control_mutex);
-
- cache = &(playlist->control_cache[playlist->num_cached++]);
-
- cache->command = command;
- cache->i1 = i1;
- cache->i2 = i2;
- cache->s1 = s1;
- cache->s2 = s2;
- cache->data = data;
-
- switch (command)
- {
- case PLAYLIST_COMMAND_PLAYLIST:
- case PLAYLIST_COMMAND_ADD:
- case PLAYLIST_COMMAND_QUEUE:
-#ifndef HAVE_DIRCACHE
- case PLAYLIST_COMMAND_DELETE:
- case PLAYLIST_COMMAND_RESET:
-#endif
- flush = true;
- break;
- case PLAYLIST_COMMAND_SHUFFLE:
- case PLAYLIST_COMMAND_UNSHUFFLE:
- default:
- /* only flush when needed */
- break;
- }
-
- if (flush || playlist->num_cached == PLAYLIST_MAX_CACHE)
- result = flush_cached_control(playlist);
-
- mutex_unlock(playlist->control_mutex);
-
- return result;
-}
-
-/*
- * sync control file to disk
- */
-static void sync_control(struct playlist_info* playlist, bool force)
-{
-#ifdef HAVE_DIRCACHE
- if (playlist->started && force)
-#else
- (void) force;
-
- if (playlist->started)
-#endif
- {
- if (playlist->pending_control_sync)
- {
- mutex_lock(playlist->control_mutex);
- fsync(playlist->control_fd);
- playlist->pending_control_sync = false;
- mutex_unlock(playlist->control_mutex);
- }
- }
-}
-
-/*
- * Rotate indices such that first_index is index 0
- */
-static int rotate_index(const struct playlist_info* playlist, int index)
-{
- index -= playlist->first_index;
- if (index < 0)
- index += playlist->amount;
-
- return index;
+ /* otherwise, try being unreasonable */
+ return core_alloc_maximum(NULL, buflen, &buflib_ops_locked);
}
/*
@@ -2022,6 +1983,14 @@ static struct buflib_callbacks ops = {
.move_callback = move_callback,
.shrink_callback = NULL,
};
+
+/******************************************************************************/
+/******************************************************************************/
+/* ************************************************************************** */
+/* * PUBLIC INTERFACE FUNCTIONS * *********************************************/
+/* ************************************************************************** */
+/******************************************************************************/
+/******************************************************************************/
/*
* Initialize playlist entries at startup
*/
@@ -2034,7 +2003,7 @@ void playlist_init(void)
mutex_init(&created_playlist_mutex);
playlist->current = true;
- strlcpy(playlist->control_filename, PLAYLIST_CONTROL_FILE,
+ strmemccpy(playlist->control_filename, PLAYLIST_CONTROL_FILE,
sizeof(playlist->control_filename));
playlist->fd = -1;
playlist->control_fd = -1;
@@ -2057,8 +2026,8 @@ void playlist_init(void)
playlist->max_playlist_size * sizeof(*playlist->dcfrefs), &ops);
playlist->dcfrefs = core_get_data(handle);
copy_filerefs(playlist->dcfrefs, NULL, playlist->max_playlist_size);
- create_thread(playlist_thread, playlist_stack, sizeof(playlist_stack),
- 0, playlist_thread_name IF_PRIO(, PRIORITY_BACKGROUND)
+ create_thread(thread_playlist, playlist_stack, sizeof(playlist_stack),
+ 0, thread_playlist_name IF_PRIO(, PRIORITY_BACKGROUND)
IF_COP(, CPU));
queue_init(&playlist_queue, true);
@@ -2087,464 +2056,13 @@ void playlist_shutdown(void)
}
/*
- * Create new playlist
- */
-int playlist_create(const char *dir, const char *file)
-{
- struct playlist_info* playlist = &current_playlist;
-
- new_playlist(playlist, dir, file);
-
- if (file)
- {
- int handle;
- size_t buflen;
- /* use mp3 buffer for maximum load speed */
- handle = core_alloc_maximum("temp", &buflen, &buflib_ops_locked);
- if (handle > 0)
- {
- /* load the playlist file */
- add_indices_to_playlist(playlist, core_get_data(handle), buflen);
- core_free(handle);
- }
- else
- {
- /* should not happen -- happens if plugin takes audio buffer */
- splashf(HZ * 2, "%s(): OOM", __func__);
- return -1;
- }
- }
-
- return 0;
-}
-
-#define PLAYLIST_COMMAND_SIZE (MAX_PATH+12)
-
-/*
- * Restore the playlist state based on control file commands. Called to
- * resume playback after shutdown.
- */
-int playlist_resume(void)
-{
- struct playlist_info* playlist = &current_playlist;
- char *buffer;
- size_t buflen;
- int handle;
- int nread;
- int total_read = 0;
- int control_file_size = 0;
- bool first = true;
- bool sorted = true;
- int result = -1;
-
- splash(0, ID2P(LANG_WAIT));
- if (core_allocatable() < (1 << 10))
- talk_buffer_set_policy(TALK_BUFFER_LOOSE); /* back off voice buffer */
-
-#ifdef HAVE_DIRCACHE
- dircache_wait(); /* we need the dircache to use the files in the playlist */
-#endif
-
- /* use mp3 buffer for maximum load speed */
- handle = core_alloc_maximum("temp", &buflen, &buflib_ops_locked);
- if (handle < 0)
- {
- splashf(HZ * 2, "%s(): OOM", __func__);
- return -1;
- }
- buffer = core_get_data(handle);
-
- empty_playlist(playlist, true);
-
- playlist->control_fd = open(playlist->control_filename, O_RDWR);
- if (playlist->control_fd < 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- goto out;
- }
- playlist->control_created = true;
-
- control_file_size = filesize(playlist->control_fd);
- if (control_file_size <= 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- goto out;
- }
-
- /* read a small amount first to get the header */
- nread = read(playlist->control_fd, buffer,
- PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
- if(nread <= 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
- goto out;
- }
-
- playlist->started = true;
-
- while (1)
- {
- result = 0;
- int count;
- enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
- int last_newline = 0;
- int str_count = -1;
- bool newline = true;
- bool exit_loop = false;
- char *p = buffer;
- char *str1 = NULL;
- char *str2 = NULL;
- char *str3 = NULL;
- unsigned long last_tick = current_tick;
- bool useraborted = false;
-
- for(count=0; count<nread && !exit_loop && !useraborted; count++,p++)
- {
- /* So a splash while we are loading. */
- if (TIME_AFTER(current_tick, last_tick + HZ/4))
- {
- splashf(0, str(LANG_LOADING_PERCENT),
- (total_read+count)*100/control_file_size,
- str(LANG_OFF_ABORT));
- if (action_userabort(TIMEOUT_NOBLOCK))
- {
- useraborted = true;
- break;
- }
- last_tick = current_tick;
- }
-
- /* Are we on a new line? */
- if((*p == '\n') || (*p == '\r'))
- {
- *p = '\0';
-
- /* save last_newline in case we need to load more data */
- last_newline = count;
-
- switch (current_command)
- {
- case PLAYLIST_COMMAND_PLAYLIST:
- {
- /* str1=version str2=dir str3=file */
- int version;
-
- if (!str1)
- {
- result = -2;
- exit_loop = true;
- break;
- }
-
- if (!str2)
- str2 = "";
-
- if (!str3)
- str3 = "";
-
- version = atoi(str1);
-
- if (version != PLAYLIST_CONTROL_FILE_VERSION)
- {
- result = -3;
- goto out;
- }
-
- update_playlist_filename(playlist, str2, str3);
-
- if (str3[0] != '\0')
- {
- /* NOTE: add_indices_to_playlist() overwrites the
- audiobuf so we need to reload control file
- data */
- add_indices_to_playlist(playlist, buffer, buflen);
- }
- else if (str2[0] != '\0')
- {
- playlist->in_ram = true;
- resume_directory(str2);
- }
-
- /* load the rest of the data */
- first = false;
- exit_loop = true;
-
- break;
- }
- case PLAYLIST_COMMAND_ADD:
- case PLAYLIST_COMMAND_QUEUE:
- {
- /* str1=position str2=last_position str3=file */
- int position, last_position;
- bool queue;
-
- if (!str1 || !str2 || !str3)
- {
- result = -4;
- exit_loop = true;
- break;
- }
-
- position = atoi(str1);
- last_position = atoi(str2);
-
- queue = (current_command == PLAYLIST_COMMAND_ADD)?
- false:true;
-
- /* seek position is based on str3's position in
- buffer */
- if (add_track_to_playlist(playlist, str3, position,
- queue, total_read+(str3-buffer)) < 0)
- {
- result = -5;
- goto out;
- }
-
- playlist->last_insert_pos = last_position;
-
- break;
- }
- case PLAYLIST_COMMAND_DELETE:
- {
- /* str1=position */
- int position;
-
- if (!str1)
- {
- result = -6;
- exit_loop = true;
- break;
- }
-
- position = atoi(str1);
-
- if (remove_track_from_playlist(playlist, position,
- false) < 0)
- {
- result = -7;
- goto out;
- }
-
- break;
- }
- case PLAYLIST_COMMAND_SHUFFLE:
- {
- /* str1=seed str2=first_index */
- int seed;
-
- if (!str1 || !str2)
- {
- result = -8;
- exit_loop = true;
- break;
- }
-
- if (!sorted)
- {
- /* Always sort list before shuffling */
- sort_playlist(playlist, false, false);
- }
-
- seed = atoi(str1);
- playlist->first_index = atoi(str2);
-
- if (randomise_playlist(playlist, seed, false,
- false) < 0)
- {
- result = -9;
- goto out;
- }
- sorted = false;
- break;
- }
- case PLAYLIST_COMMAND_UNSHUFFLE:
- {
- /* str1=first_index */
- if (!str1)
- {
- result = -10;
- exit_loop = true;
- break;
- }
-
- playlist->first_index = atoi(str1);
-
- if (sort_playlist(playlist, false, false) < 0)
- {
- result = -11;
- goto out;
- }
-
- sorted = true;
- break;
- }
- case PLAYLIST_COMMAND_RESET:
- {
- playlist->last_insert_pos = -1;
- break;
- }
- case PLAYLIST_COMMAND_COMMENT:
- default:
- break;
- }
-
- newline = true;
-
- /* to ignore any extra newlines */
- current_command = PLAYLIST_COMMAND_COMMENT;
- }
- else if(newline)
- {
- newline = false;
-
- /* first non-comment line must always specify playlist */
- if (first && *p != 'P' && *p != '#')
- {
- result = -12;
- exit_loop = true;
- break;
- }
-
- switch (*p)
- {
- case 'P':
- /* playlist can only be specified once */
- if (!first)
- {
- result = -13;
- exit_loop = true;
- break;
- }
-
- current_command = PLAYLIST_COMMAND_PLAYLIST;
- break;
- case 'A':
- current_command = PLAYLIST_COMMAND_ADD;
- break;
- case 'Q':
- current_command = PLAYLIST_COMMAND_QUEUE;
- break;
- case 'D':
- current_command = PLAYLIST_COMMAND_DELETE;
- break;
- case 'S':
- current_command = PLAYLIST_COMMAND_SHUFFLE;
- break;
- case 'U':
- current_command = PLAYLIST_COMMAND_UNSHUFFLE;
- break;
- case 'R':
- current_command = PLAYLIST_COMMAND_RESET;
- break;
- case '#':
- current_command = PLAYLIST_COMMAND_COMMENT;
- break;
- default:
- result = -14;
- exit_loop = true;
- break;
- }
-
- str_count = -1;
- str1 = NULL;
- str2 = NULL;
- str3 = NULL;
- }
- else if(current_command != PLAYLIST_COMMAND_COMMENT)
- {
- /* all control file strings are separated with a colon.
- Replace the colon with 0 to get proper strings that can be
- used by commands above */
- if (*p == ':')
- {
- *p = '\0';
- str_count++;
-
- if ((count+1) < nread)
- {
- switch (str_count)
- {
- case 0:
- str1 = p+1;
- break;
- case 1:
- str2 = p+1;
- break;
- case 2:
- str3 = p+1;
- break;
- default:
- /* allow last string to contain colons */
- *p = ':';
- break;
- }
- }
- }
- }
- }
-
- if (result < 0)
- {
- splashf(HZ*2, "Err: %d, %s", result, str(LANG_PLAYLIST_CONTROL_INVALID));
- goto out;
- }
-
- if (useraborted)
- {
- splash(HZ*2, ID2P(LANG_CANCEL));
- result = -1;
- goto out;
- }
- if (!newline || (exit_loop && count<nread))
- {
- if ((total_read + count) >= control_file_size)
- {
- /* no newline at end of control file */
- splashf(HZ*2, "Err: EOF, %s", str(LANG_PLAYLIST_CONTROL_INVALID));
- result = -15;
- goto out;
- }
-
- /* We didn't end on a newline or we exited loop prematurely.
- Either way, re-read the remainder. */
- count = last_newline;
- lseek(playlist->control_fd, total_read+count, SEEK_SET);
- }
-
- total_read += count;
-
- if (first)
- /* still looking for header */
- nread = read(playlist->control_fd, buffer,
- PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
- else
- nread = read(playlist->control_fd, buffer, buflen);
-
- /* Terminate on EOF */
- if(nread <= 0)
- {
- break;
- }
- }
-
-#ifdef HAVE_DIRCACHE
- queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
-#endif
-
-out:
- talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
- core_free(handle);
- return result;
-}
-
-/*
* Add track to in_ram playlist. Used when playing directories.
*/
int playlist_add(const char *filename)
{
struct playlist_info* playlist = &current_playlist;
int len = strlen(filename);
-
+
if((len+1 > playlist->buffer_size - playlist->buffer_end_pos) ||
(playlist->amount >= playlist->max_playlist_size))
{
@@ -2558,7 +2076,7 @@ int playlist_add(const char *filename)
#endif
playlist->amount++;
-
+
strcpy((char*)&playlist->buffer[playlist->buffer_end_pos], filename);
playlist->buffer_end_pos += len;
playlist->buffer[playlist->buffer_end_pos++] = '\0';
@@ -2566,83 +2084,116 @@ int playlist_add(const char *filename)
return 0;
}
-/* shuffle newly created playlist using random seed. */
-int playlist_shuffle(int random_seed, int start_index)
+/* returns number of tracks in playlist (includes queued/inserted tracks) */
+int playlist_amount_ex(const struct playlist_info* playlist)
{
- struct playlist_info* playlist = &current_playlist;
-
- bool start_current = false;
-
- if (start_index >= 0 && global_settings.play_selected)
- {
- /* store the seek position before the shuffle */
- playlist->index = playlist->first_index = start_index;
- start_current = true;
- }
-
- randomise_playlist(playlist, random_seed, start_current, true);
+ if (!playlist)
+ playlist = &current_playlist;
- return playlist->index;
+ return playlist->amount;
}
-/* returns the crc32 of the filename of the track at the specified index */
-unsigned int playlist_get_filename_crc32(struct playlist_info *playlist,
- int index)
+/* returns number of tracks in current playlist */
+int playlist_amount(void)
{
- struct playlist_track_info track_info;
- if (playlist_get_track_info(playlist, index, &track_info) == -1)
- return -1;
- const char *basename;
-#ifdef HAVE_MULTIVOLUME
- /* remove the volume identifier it might change just use the relative part*/
- path_strip_volume(track_info.filename, &basename, false);
- if (basename == NULL)
-#endif
- basename = track_info.filename;
- NOTEF("%s: %s", __func__, basename);
- return crc_32(basename, strlen(basename), -1);
+ return playlist_amount_ex(NULL);
}
-/* resume a playlist track with the given crc_32 of the track name. */
-void playlist_resume_track(int start_index, unsigned int crc,
- unsigned long elapsed, unsigned long offset)
+/*
+ * Create a new playlist If playlist is not NULL then we're loading a
+ * playlist off disk for viewing/editing. The index_buffer is used to store
+ * playlist indices (required for and only used if !current playlist). The
+ * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
+ */
+int playlist_create_ex(struct playlist_info* playlist,
+ const char* dir, const char* file,
+ void* index_buffer, int index_buffer_size,
+ void* temp_buffer, int temp_buffer_size)
{
- int i;
- unsigned int tmp_crc;
- struct playlist_info* playlist = &current_playlist;
- tmp_crc = playlist_get_filename_crc32(playlist, start_index);
- if (tmp_crc == crc)
+ if (!playlist)
+ playlist = &current_playlist;
+ else
{
- playlist_start(start_index, elapsed, offset);
- return;
- }
+ /* Initialize playlist structure */
+ int r = rand() % 10;
+ playlist->current = false;
- for (i = 0 ; i < playlist->amount; i++)
- {
- tmp_crc = playlist_get_filename_crc32(playlist, i);
- if (tmp_crc == crc)
+ /* Use random name for control file */
+ snprintf(playlist->control_filename, sizeof(playlist->control_filename),
+ "%s.%d", PLAYLIST_CONTROL_FILE, r);
+ playlist->fd = -1;
+ playlist->control_fd = -1;
+
+ if (index_buffer)
{
- playlist_start(i, elapsed, offset);
- return;
+ int num_indices = index_buffer_size /
+ playlist_get_required_bufsz(playlist, false, 1);
+
+ if (num_indices > global_settings.max_files_in_playlist)
+ num_indices = global_settings.max_files_in_playlist;
+
+ playlist->max_playlist_size = num_indices;
+ playlist->indices = index_buffer;
+#ifdef HAVE_DIRCACHE
+ playlist->dcfrefs = (void *)&playlist->indices[num_indices];
+#endif
+ }
+ else
+ {
+ playlist->max_playlist_size = current_playlist.max_playlist_size;
+ playlist->indices = current_playlist.indices;
+#ifdef HAVE_DIRCACHE
+ playlist->dcfrefs = current_playlist.dcfrefs;
+#endif
}
+
+ playlist->buffer_size = 0;
+ playlist->buffer_handle = -1;
+ playlist->buffer = NULL;
+ playlist->control_mutex = &created_playlist_mutex;
}
- /* If we got here the file wasnt found, so start from the beginning */
- playlist_start(0, 0, 0);
+ new_playlist(playlist, dir, file);
+
+ if (file)
+ /* load the playlist file */
+ add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
+
+ return 0;
}
-/* start playing current playlist at specified index/offset */
-void playlist_start(int start_index, unsigned long elapsed,
- unsigned long offset)
+/*
+ * Create new playlist
+ */
+int playlist_create(const char *dir, const char *file)
{
struct playlist_info* playlist = &current_playlist;
- playlist->index = start_index;
+ new_playlist(playlist, dir, file);
- playlist->started = true;
- sync_control(playlist, false);
- audio_play(elapsed, offset);
- audio_resume();
+ if (file)
+ {
+ size_t buflen;
+ int handle = alloc_tempbuf(&buflen);
+ if (handle > 0)
+ {
+ /* align for faster load times */
+ void* buf = core_get_data(handle);
+ STORAGE_ALIGN_BUFFER(buf, buflen);
+ buflen = ALIGN_DOWN(buflen, 512); /* to avoid partial sector I/O */
+ /* load the playlist file */
+ add_indices_to_playlist(playlist, buf, buflen);
+ core_free(handle);
+ }
+ else
+ {
+ /* should not happen -- happens if plugin takes audio buffer */
+ splashf(HZ * 2, "%s(): OOM", __func__);
+ return -1;
+ }
+ }
+
+ return 0;
}
/* Returns false if 'steps' is out of bounds, else true */
@@ -2662,196 +2213,166 @@ bool playlist_check(int steps)
return (index >= 0);
}
-/* get trackname of track that is "steps" away from current playing track.
- NULL is used to identify end of playlist */
-const char* playlist_peek(int steps, char* buf, size_t buf_size)
+/*
+ * Close files and delete control file for non-current playlist.
+ */
+void playlist_close(struct playlist_info* playlist)
{
- struct playlist_info* playlist = &current_playlist;
- int seek;
- char *temp_ptr;
- int index;
- bool control_file;
+ if (!playlist)
+ return;
- index = get_next_index(playlist, steps, -1);
- if (index < 0)
- return NULL;
+ if (playlist->fd >= 0) {
+ close(playlist->fd);
+ playlist->fd = -1;
+ }
- /* Just testing - don't care about the file name */
- if (!buf || !buf_size)
- return "";
+ if (playlist->control_fd >= 0) {
+ close(playlist->control_fd);
+ playlist->control_fd = -1;
+ }
- control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
- seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
+ if (playlist->control_created) {
+ remove(playlist->control_filename);
+ playlist->control_created = false;
+ }
+}
- if (get_filename(playlist, index, seek, control_file, buf,
- buf_size) < 0)
- return NULL;
+/*
+ * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
+ * we want to delete the current playing track.
+ */
+int playlist_delete(struct playlist_info* playlist, int index)
+{
+ int result = 0;
- temp_ptr = buf;
+ if (!playlist)
+ playlist = &current_playlist;
- if (!playlist->in_ram || control_file)
+ if (check_control(playlist) < 0)
{
- /* remove bogus dirs from beginning of path
- (workaround for buggy playlist creation tools) */
- while (temp_ptr)
- {
- if (file_exists(temp_ptr))
- break;
+ splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ return -1;
+ }
- temp_ptr = strchr(temp_ptr+1, '/');
- }
+ if (index == PLAYLIST_DELETE_CURRENT)
+ index = playlist->index;
- if (!temp_ptr)
- {
- /* Even though this is an invalid file, we still need to pass a
- file name to the caller because NULL is used to indicate end
- of playlist */
- return buf;
- }
- }
+ result = remove_track_from_playlist(playlist, index, true);
- return temp_ptr;
+ if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
+ playlist->started)
+ audio_flush_and_reload_tracks();
+
+ return result;
}
/*
- * Update indices as track has changed
+ * Search specified directory for tracks and notify via callback. May be
+ * called recursively.
*/
-int playlist_next(int steps)
+int playlist_directory_tracksearch(const char* dirname, bool recurse,
+ int (*callback)(char*, void*),
+ void* context)
{
- struct playlist_info* playlist = &current_playlist;
- int index;
+ char buf[MAX_PATH+1];
+ int result = 0;
+ int num_files = 0;
+ int i;;
+ struct tree_context* tc = tree_get_context();
+ struct tree_cache* cache = &tc->cache;
+ int old_dirfilter = *(tc->dirfilter);
- if ( (steps > 0)
-#ifdef AB_REPEAT_ENABLE
- && (global_settings.repeat_mode != REPEAT_AB)
-#endif
- && (global_settings.repeat_mode != REPEAT_ONE) )
- {
- int i, j;
+ if (!callback)
+ return -1;
- /* We need to delete all the queued songs */
- for (i=0, j=steps; i<j; i++)
- {
- index = get_next_index(playlist, i, -1);
-
- if (index >= 0 && playlist->indices[index] & PLAYLIST_QUEUE_MASK)
- {
- remove_track_from_playlist(playlist, index, true);
- steps--; /* one less track */
- }
- }
+ /* use the tree browser dircache to load files */
+ *(tc->dirfilter) = SHOW_ALL;
+
+ if (ft_load(tc, dirname) < 0)
+ {
+ splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
+ *(tc->dirfilter) = old_dirfilter;
+ return -1;
}
- index = get_next_index(playlist, steps, -1);
+ num_files = tc->filesindir;
- if (index < 0)
+ /* we've overwritten the dircache so tree browser will need to be
+ reloaded */
+ reload_directory();
+
+ for (i=0; i<num_files; i++)
{
- /* end of playlist... or is it */
- if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
- playlist->amount > 1)
+ /* user abort */
+ if (action_userabort(TIMEOUT_NOBLOCK))
{
- /* Repeat shuffle mode. Re-shuffle playlist and resume play */
- playlist->first_index = 0;
- sort_playlist(playlist, false, false);
- randomise_playlist(playlist, current_tick, false, true);
-
- playlist->started = true;
- playlist->index = 0;
- index = 0;
+ result = -1;
+ break;
}
- else if (playlist->in_ram && global_settings.next_folder)
- {
- index = create_and_play_dir(steps, true);
- if (index >= 0)
+ struct entry *files = core_get_data(cache->entries_handle);
+ if (files[i].attr & ATTR_DIRECTORY)
+ {
+ if (recurse)
{
- playlist->index = index;
- }
- }
-
- return index;
- }
+ /* recursively add directories */
+ if (path_append(buf, dirname, files[i].name, sizeof(buf))
+ >= sizeof(buf))
+ {
+ continue;
+ }
- playlist->index = index;
+ result = playlist_directory_tracksearch(buf, recurse,
+ callback, context);
+ if (result < 0)
+ break;
- if (playlist->last_insert_pos >= 0 && steps > 0)
- {
- /* check to see if we've gone beyond the last inserted track */
- int cur = rotate_index(playlist, index);
- int last_pos = rotate_index(playlist, playlist->last_insert_pos);
+ /* we now need to reload our current directory */
+ if(ft_load(tc, dirname) < 0)
+ {
+ result = -1;
+ break;
+ }
- if (cur > last_pos)
+ num_files = tc->filesindir;
+ if (!num_files)
+ {
+ result = -1;
+ break;
+ }
+ }
+ else
+ continue;
+ }
+ else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
{
- /* reset last inserted track */
- playlist->last_insert_pos = -1;
-
- if (playlist->control_fd >= 0)
+ if (path_append(buf, dirname, files[i].name, sizeof(buf))
+ >= sizeof(buf))
{
- int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
- -1, -1, NULL, NULL, NULL);
-
- if (result < 0)
- return result;
+ continue;
+ }
- sync_control(playlist, false);
+ if (callback(buf, context) != 0)
+ {
+ result = -1;
+ break;
}
+
+ /* let the other threads work */
+ yield();
}
}
- return index;
-}
-
-/* try playing next or previous folder */
-bool playlist_next_dir(int direction)
-{
- /* not to mess up real playlists */
- if(!current_playlist.in_ram)
- return false;
+ /* restore dirfilter */
+ *(tc->dirfilter) = old_dirfilter;
- return create_and_play_dir(direction, false) >= 0;
+ return result;
}
-/* Get resume info for current playing song. If return value is -1 then
- settings shouldn't be saved. */
-int playlist_get_resume_info(int *resume_index)
-{
- struct playlist_info* playlist = &current_playlist;
-
- *resume_index = playlist->index;
-
- return 0;
-}
-/* Update resume info for current playing song. Returns -1 on error. */
-int playlist_update_resume_info(const struct mp3entry* id3)
+struct playlist_info *playlist_get_current(void)
{
- struct playlist_info* playlist = &current_playlist;
-
- if (id3)
- {
- if (global_status.resume_index != playlist->index ||
- global_status.resume_elapsed != id3->elapsed ||
- global_status.resume_offset != id3->offset)
- {
- unsigned int crc = playlist_get_filename_crc32(playlist,
- playlist->index);
- global_status.resume_index = playlist->index;
- global_status.resume_crc32 = crc;
- global_status.resume_elapsed = id3->elapsed;
- global_status.resume_offset = id3->offset;
- status_save();
- }
- }
- else
- {
- global_status.resume_index = -1;
- global_status.resume_crc32 = -1;
- global_status.resume_elapsed = -1;
- global_status.resume_offset = -1;
- status_save();
- }
-
- return 0;
+ return &current_playlist;
}
/* Returns index of current playing track for display purposes. This value
@@ -2867,211 +2388,127 @@ int playlist_get_display_index(void)
return (index+1);
}
-/* returns number of tracks in current playlist */
-int playlist_amount(void)
-{
- return playlist_amount_ex(NULL);
-}
-/* set playlist->last_shuffle_start to playlist->amount for
- PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
-void playlist_set_last_shuffled_start(void)
+/* returns the crc32 of the filename of the track at the specified index */
+unsigned int playlist_get_filename_crc32(struct playlist_info *playlist,
+ int index)
{
- struct playlist_info* playlist = &current_playlist;
- playlist->last_shuffled_start = playlist->amount;
+ struct playlist_track_info track_info;
+ if (playlist_get_track_info(playlist, index, &track_info) == -1)
+ return -1;
+ const char *basename;
+#ifdef HAVE_MULTIVOLUME
+ /* remove the volume identifier it might change just use the relative part*/
+ path_strip_volume(track_info.filename, &basename, false);
+ if (basename == NULL)
+#endif
+ basename = track_info.filename;
+ NOTEF("%s: %s", __func__, basename);
+ return crc_32(basename, strlen(basename), -1);
}
-/*
- * Create a new playlist If playlist is not NULL then we're loading a
- * playlist off disk for viewing/editing. The index_buffer is used to store
- * playlist indices (required for and only used if !current playlist). The
- * temp_buffer (if not NULL) is used as a scratchpad when loading indices.
- */
-int playlist_create_ex(struct playlist_info* playlist,
- const char* dir, const char* file,
- void* index_buffer, int index_buffer_size,
- void* temp_buffer, int temp_buffer_size)
+
+/* returns index of first track in playlist */
+int playlist_get_first_index(const struct playlist_info* playlist)
{
if (!playlist)
playlist = &current_playlist;
- else
- {
- /* Initialize playlist structure */
- int r = rand() % 10;
- playlist->current = false;
-
- /* Use random name for control file */
- snprintf(playlist->control_filename, sizeof(playlist->control_filename),
- "%s.%d", PLAYLIST_CONTROL_FILE, r);
- playlist->fd = -1;
- playlist->control_fd = -1;
- if (index_buffer)
- {
- int num_indices = index_buffer_size /
- playlist_get_required_bufsz(playlist, false, 1);
-
- if (num_indices > global_settings.max_files_in_playlist)
- num_indices = global_settings.max_files_in_playlist;
-
- playlist->max_playlist_size = num_indices;
- playlist->indices = index_buffer;
-#ifdef HAVE_DIRCACHE
- playlist->dcfrefs = (void *)&playlist->indices[num_indices];
-#endif
- }
- else
- {
- playlist->max_playlist_size = current_playlist.max_playlist_size;
- playlist->indices = current_playlist.indices;
-#ifdef HAVE_DIRCACHE
- playlist->dcfrefs = current_playlist.dcfrefs;
-#endif
- }
-
- playlist->buffer_size = 0;
- playlist->buffer_handle = -1;
- playlist->buffer = NULL;
- playlist->control_mutex = &created_playlist_mutex;
- }
-
- new_playlist(playlist, dir, file);
-
- if (file)
- /* load the playlist file */
- add_indices_to_playlist(playlist, temp_buffer, temp_buffer_size);
-
- return 0;
+ return playlist->first_index;
}
-/*
- * Set the specified playlist as the current.
- * NOTE: You will get undefined behaviour if something is already playing so
- * remember to stop before calling this. Also, this call will
- * effectively close your playlist, making it unusable.
- */
-int playlist_set_current(struct playlist_info* playlist)
+/* returns the playlist filename */
+char *playlist_get_name(const struct playlist_info* playlist, char *buf,
+ int buf_size)
{
- if (!playlist || (check_control(playlist) < 0))
- return -1;
+ if (!playlist)
+ playlist = &current_playlist;
- empty_playlist(&current_playlist, false);
+ strmemccpy(buf, playlist->filename, buf_size);
- strlcpy(current_playlist.filename, playlist->filename,
- sizeof(current_playlist.filename));
+ if (!buf[0])
+ return NULL;
- current_playlist.utf8 = playlist->utf8;
- current_playlist.fd = playlist->fd;
+ return buf;
+}
- close(playlist->control_fd);
- playlist->control_fd = -1;
- close(current_playlist.control_fd);
- current_playlist.control_fd = -1;
- remove(current_playlist.control_filename);
- current_playlist.control_created = false;
- if (rename(playlist->control_filename,
- current_playlist.control_filename) < 0)
- return -1;
- current_playlist.control_fd = open(current_playlist.control_filename,
- O_RDWR);
- if (current_playlist.control_fd < 0)
- return -1;
- current_playlist.control_created = true;
+/* return size of buffer needed for playlist to initialize num_indices entries */
+size_t playlist_get_required_bufsz(struct playlist_info* playlist,
+ bool include_namebuf,
+ int num_indices)
+{
+ size_t namebuf = 0;
- current_playlist.dirlen = playlist->dirlen;
+ if (!playlist)
+ playlist = &current_playlist;
- if (playlist->indices && playlist->indices != current_playlist.indices)
- {
- memcpy((void*)current_playlist.indices, (void*)playlist->indices,
- playlist->max_playlist_size*sizeof(*playlist->indices));
-#ifdef HAVE_DIRCACHE
- copy_filerefs(current_playlist.dcfrefs, playlist->dcfrefs,
- playlist->max_playlist_size);
-#endif
- }
-
- current_playlist.first_index = playlist->first_index;
- current_playlist.amount = playlist->amount;
- current_playlist.last_insert_pos = playlist->last_insert_pos;
- current_playlist.seed = playlist->seed;
- current_playlist.shuffle_modified = playlist->shuffle_modified;
- current_playlist.deleted = playlist->deleted;
- current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
-
- memcpy(current_playlist.control_cache, playlist->control_cache,
- sizeof(current_playlist.control_cache));
- current_playlist.num_cached = playlist->num_cached;
- current_playlist.pending_control_sync = playlist->pending_control_sync;
+ size_t unit_size = sizeof (*playlist->indices);
+ #ifdef HAVE_DIRCACHE
+ unit_size += sizeof (*playlist->dcfrefs);
+ #endif
+ if (include_namebuf)
+ namebuf = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
- return 0;
-}
-struct playlist_info *playlist_get_current(void)
-{
- return &current_playlist;
+ return (num_indices * unit_size) + namebuf;
}
-/*
- * Close files and delete control file for non-current playlist.
- */
-void playlist_close(struct playlist_info* playlist)
-{
- if (!playlist)
- return;
- if (playlist->fd >= 0) {
- close(playlist->fd);
- playlist->fd = -1;
- }
+/* Get resume info for current playing song. If return value is -1 then
+ settings shouldn't be saved. */
+int playlist_get_resume_info(int *resume_index)
+{
+ struct playlist_info* playlist = &current_playlist;
- if (playlist->control_fd >= 0) {
- close(playlist->control_fd);
- playlist->control_fd = -1;
- }
+ *resume_index = playlist->index;
- if (playlist->control_created) {
- remove(playlist->control_filename);
- playlist->control_created = false;
- }
+ return 0;
}
-void playlist_sync(struct playlist_info* playlist)
+/* returns shuffle seed of playlist */
+int playlist_get_seed(const struct playlist_info* playlist)
{
if (!playlist)
playlist = &current_playlist;
-
- sync_control(playlist, false);
- if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
- audio_flush_and_reload_tracks();
-#ifdef HAVE_DIRCACHE
- queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
-#endif
+ return playlist->seed;
}
-/*
- * Insert track into playlist at specified position (or one of the special
- * positions). Returns position where track was inserted or -1 if error.
- */
-int playlist_insert_track(struct playlist_info* playlist, const char *filename,
- int position, bool queue, bool sync)
+/* Fills info structure with information about track at specified index.
+ Returns 0 on success and -1 on failure */
+int playlist_get_track_info(struct playlist_info* playlist, int index,
+ struct playlist_track_info* info)
{
- int result;
-
+ int seek;
+ bool control_file;
+
if (!playlist)
playlist = &current_playlist;
- if (check_control(playlist) < 0)
- {
- splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ if (index < 0 || index >= playlist->amount)
return -1;
+
+ control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
+ seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
+
+ if (get_filename(playlist, index, seek, control_file, info->filename,
+ sizeof(info->filename)) < 0)
+ return -1;
+
+ info->attr = 0;
+
+ if (control_file)
+ {
+ if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
+ info->attr |= PLAYLIST_ATTR_QUEUED;
+ else
+ info->attr |= PLAYLIST_ATTR_INSERTED;
+
}
- result = add_track_to_playlist(playlist, filename, position, queue, -1);
+ if (playlist->indices[index] & PLAYLIST_SKIPPED)
+ info->attr |= PLAYLIST_ATTR_SKIPPED;
- /* Check if we want manually sync later. For example when adding
- * bunch of files from tagcache, syncing after every file wouldn't be
- * a good thing to do. */
- if (sync && result >= 0)
- playlist_sync(playlist);
+ info->index = index;
+ info->display_index = rotate_index(playlist, index) + 1;
- return result;
+ return 0;
}
/*
@@ -3113,7 +2550,7 @@ int playlist_insert_directory(struct playlist_info* playlist,
context.position = position;
context.queue = queue;
context.count = 0;
-
+
cpu_boost(true);
result = playlist_directory_tracksearch(dirname, recurse,
@@ -3196,11 +2633,11 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
/* user abort */
if (action_userabort(TIMEOUT_NOBLOCK))
break;
-
+
if (temp_buf[0] != '#' && temp_buf[0] != '\0')
{
int insert_pos;
-
+
if (!utf8)
{
/* Use trackname as a temporay buffer. Note that trackname must
@@ -3268,12 +2705,13 @@ int playlist_insert_playlist(struct playlist_info* playlist, const char *filenam
}
/*
- * Delete track at specified index. If index is PLAYLIST_DELETE_CURRENT then
- * we want to delete the current playing track.
+ * Insert track into playlist at specified position (or one of the special
+ * positions). Returns position where track was inserted or -1 if error.
*/
-int playlist_delete(struct playlist_info* playlist, int index)
+int playlist_insert_track(struct playlist_info* playlist, const char *filename,
+ int position, bool queue, bool sync)
{
- int result = 0;
+ int result;
if (!playlist)
playlist = &current_playlist;
@@ -3284,18 +2722,32 @@ int playlist_delete(struct playlist_info* playlist, int index)
return -1;
}
- if (index == PLAYLIST_DELETE_CURRENT)
- index = playlist->index;
+ result = add_track_to_playlist(playlist, filename, position, queue, -1);
- result = remove_track_from_playlist(playlist, index, true);
-
- if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
- playlist->started)
- audio_flush_and_reload_tracks();
+ /* Check if we want manually sync later. For example when adding
+ * bunch of files from tagcache, syncing after every file wouldn't be
+ * a good thing to do. */
+ if (sync && result >= 0)
+ playlist_sync(playlist);
return result;
}
+
+/* returns true if playlist has been modified */
+bool playlist_modified(const struct playlist_info* playlist)
+{
+ if (!playlist)
+ playlist = &current_playlist;
+
+ if (playlist->shuffle_modified ||
+ playlist->deleted ||
+ playlist->num_inserted_tracks > 0)
+ return true;
+
+ return false;
+}
+
/*
* Move track at index to new_index. Tracks between the two are shifted
* appropriately. Returns 0 on success and -1 on failure.
@@ -3428,182 +2880,669 @@ int playlist_move(struct playlist_info* playlist, int index, int new_index)
return result;
}
-/* shuffle currently playing playlist */
-int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
- bool start_current)
+/* returns full path of playlist (minus extension) */
+char *playlist_name(const struct playlist_info* playlist, char *buf,
+ int buf_size)
{
- int result;
+ char *sep;
if (!playlist)
playlist = &current_playlist;
- check_control(playlist);
+ strmemccpy(buf, playlist->filename+playlist->dirlen, buf_size);
- result = randomise_playlist(playlist, seed, start_current, true);
+ if (!buf[0])
+ return NULL;
- if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
- playlist->started)
- audio_flush_and_reload_tracks();
+ /* Remove extension */
+ sep = strrchr(buf, '.');
+ if (sep)
+ *sep = 0;
- return result;
+ return buf;
}
-/* sort currently playing playlist */
-int playlist_sort(struct playlist_info* playlist, bool start_current)
+/*
+ * Update indices as track has changed
+ */
+int playlist_next(int steps)
{
- int result;
+ struct playlist_info* playlist = &current_playlist;
+ int index;
- if (!playlist)
- playlist = &current_playlist;
+ if ( (steps > 0)
+#ifdef AB_REPEAT_ENABLE
+ && (global_settings.repeat_mode != REPEAT_AB)
+#endif
+ && (global_settings.repeat_mode != REPEAT_ONE) )
+ {
+ int i, j;
- check_control(playlist);
+ /* We need to delete all the queued songs */
+ for (i=0, j=steps; i<j; i++)
+ {
+ index = get_next_index(playlist, i, -1);
- result = sort_playlist(playlist, start_current, true);
+ if (index >= 0 && playlist->indices[index] & PLAYLIST_QUEUE_MASK)
+ {
+ remove_track_from_playlist(playlist, index, true);
+ steps--; /* one less track */
+ }
+ }
+ }
- if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
- playlist->started)
- audio_flush_and_reload_tracks();
+ index = get_next_index(playlist, steps, -1);
- return result;
-}
+ if (index < 0)
+ {
+ /* end of playlist... or is it */
+ if (global_settings.repeat_mode == REPEAT_SHUFFLE &&
+ playlist->amount > 1)
+ {
+ /* Repeat shuffle mode. Re-shuffle playlist and resume play */
+ playlist->first_index = 0;
+ sort_playlist(playlist, false, false);
+ randomise_playlist(playlist, current_tick, false, true);
-/* returns true if playlist has been modified */
-bool playlist_modified(const struct playlist_info* playlist)
-{
- if (!playlist)
- playlist = &current_playlist;
+ playlist->started = true;
+ playlist->index = 0;
+ index = 0;
+ }
+ else if (playlist->in_ram && global_settings.next_folder)
+ {
+ index = create_and_play_dir(steps, true);
- if (playlist->shuffle_modified ||
- playlist->deleted ||
- playlist->num_inserted_tracks > 0)
- return true;
+ if (index >= 0)
+ {
+ playlist->index = index;
+ }
+ }
- return false;
-}
+ return index;
+ }
-/* returns index of first track in playlist */
-int playlist_get_first_index(const struct playlist_info* playlist)
-{
- if (!playlist)
- playlist = &current_playlist;
+ playlist->index = index;
- return playlist->first_index;
-}
+ if (playlist->last_insert_pos >= 0 && steps > 0)
+ {
+ /* check to see if we've gone beyond the last inserted track */
+ int cur = rotate_index(playlist, index);
+ int last_pos = rotate_index(playlist, playlist->last_insert_pos);
-/* returns shuffle seed of playlist */
-int playlist_get_seed(const struct playlist_info* playlist)
-{
- if (!playlist)
- playlist = &current_playlist;
+ if (cur > last_pos)
+ {
+ /* reset last inserted track */
+ playlist->last_insert_pos = -1;
- return playlist->seed;
+ if (playlist->control_fd >= 0)
+ {
+ int result = update_control(playlist, PLAYLIST_COMMAND_RESET,
+ -1, -1, NULL, NULL, NULL);
+
+ if (result < 0)
+ return result;
+
+ sync_control(playlist, false);
+ }
+ }
+ }
+
+ return index;
}
-/* returns number of tracks in playlist (includes queued/inserted tracks) */
-int playlist_amount_ex(const struct playlist_info* playlist)
+/* try playing next or previous folder */
+bool playlist_next_dir(int direction)
{
- if (!playlist)
- playlist = &current_playlist;
+ /* not to mess up real playlists */
+ if(!current_playlist.in_ram)
+ return false;
- return playlist->amount;
+ return create_and_play_dir(direction, false) >= 0;
}
-/* returns full path of playlist (minus extension) */
-char *playlist_name(const struct playlist_info* playlist, char *buf,
- int buf_size)
+/* get trackname of track that is "steps" away from current playing track.
+ NULL is used to identify end of playlist */
+const char* playlist_peek(int steps, char* buf, size_t buf_size)
{
- char *sep;
+ struct playlist_info* playlist = &current_playlist;
+ int seek;
+ char *temp_ptr;
+ int index;
+ bool control_file;
- if (!playlist)
- playlist = &current_playlist;
+ index = get_next_index(playlist, steps, -1);
+ if (index < 0)
+ return NULL;
- strlcpy(buf, playlist->filename+playlist->dirlen, buf_size);
-
- if (!buf[0])
+ /* Just testing - don't care about the file name */
+ if (!buf || !buf_size)
+ return "";
+
+ control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
+ seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
+
+ if (get_filename(playlist, index, seek, control_file, buf,
+ buf_size) < 0)
return NULL;
- /* Remove extension */
- sep = strrchr(buf, '.');
- if (sep)
- *sep = 0;
+ temp_ptr = buf;
- return buf;
+ if (!playlist->in_ram || control_file)
+ {
+ /* remove bogus dirs from beginning of path
+ (workaround for buggy playlist creation tools) */
+ while (temp_ptr)
+ {
+ if (file_exists(temp_ptr))
+ break;
+
+ temp_ptr = strchr(temp_ptr+1, '/');
+ }
+
+ if (!temp_ptr)
+ {
+ /* Even though this is an invalid file, we still need to pass a
+ file name to the caller because NULL is used to indicate end
+ of playlist */
+ return buf;
+ }
+ }
+
+ return temp_ptr;
}
-/* returns the playlist filename */
-char *playlist_get_name(const struct playlist_info* playlist, char *buf,
- int buf_size)
+/* shuffle currently playing playlist */
+int playlist_randomise(struct playlist_info* playlist, unsigned int seed,
+ bool start_current)
{
+ int result;
+
if (!playlist)
playlist = &current_playlist;
- strlcpy(buf, playlist->filename, buf_size);
+ check_control(playlist);
- if (!buf[0])
- return NULL;
+ result = randomise_playlist(playlist, seed, start_current, true);
- return buf;
+ if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
+ playlist->started)
+ audio_flush_and_reload_tracks();
+
+ return result;
}
-/* return size of buffer needed for playlist to initialize num_indices entries */
-size_t playlist_get_required_bufsz(struct playlist_info* playlist,
- bool include_namebuf,
- int num_indices)
+/*
+ * Removes all tracks, from the playlist, leaving the presently playing
+ * track queued.
+ */
+int playlist_remove_all_tracks(struct playlist_info *playlist)
{
- size_t namebuf = 0;
+ int result;
- if (!playlist)
+ if (playlist == NULL)
playlist = &current_playlist;
- size_t unit_size = sizeof (*playlist->indices);
- #ifdef HAVE_DIRCACHE
- unit_size += sizeof (*playlist->dcfrefs);
- #endif
- if (include_namebuf)
- namebuf = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
+ while (playlist->index > 0)
+ if ((result = remove_track_from_playlist(playlist, 0, true)) < 0)
+ return result;
- return (num_indices * unit_size) + namebuf;
+ while (playlist->amount > 1)
+ if ((result = remove_track_from_playlist(playlist, 1, true)) < 0)
+ return result;
+
+ if (playlist->amount == 1) {
+ playlist->indices[0] |= PLAYLIST_QUEUED;
+ }
+
+ return 0;
}
-/* Fills info structure with information about track at specified index.
- Returns 0 on success and -1 on failure */
-int playlist_get_track_info(struct playlist_info* playlist, int index,
- struct playlist_track_info* info)
+/*
+ * Restore the playlist state based on control file commands. Called to
+ * resume playback after shutdown.
+ */
+int playlist_resume(void)
{
- int seek;
- bool control_file;
+ struct playlist_info* playlist = &current_playlist;
+ char *buffer;
+ size_t buflen;
+ int handle;
+ int nread;
+ int total_read = 0;
+ int control_file_size = 0;
+ bool first = true;
+ bool sorted = true;
+ int result = -1;
- if (!playlist)
- playlist = &current_playlist;
+ splash(0, ID2P(LANG_WAIT));
+ if (core_allocatable() < (1 << 10))
+ talk_buffer_set_policy(TALK_BUFFER_LOOSE); /* back off voice buffer */
- if (index < 0 || index >= playlist->amount)
+#ifdef HAVE_DIRCACHE
+ dircache_wait(); /* we need the dircache to use the files in the playlist */
+#endif
+
+ handle = alloc_tempbuf(&buflen);
+ if (handle < 0)
+ {
+ splashf(HZ * 2, "%s(): OOM", __func__);
return -1;
+ }
- control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
- seek = playlist->indices[index] & PLAYLIST_SEEK_MASK;
+ /* align buffer for faster load times */
+ buffer = core_get_data(handle);
+ STORAGE_ALIGN_BUFFER(buffer, buflen);
+ buflen = ALIGN_DOWN(buflen, 512); /* to avoid partial sector I/O */
- if (get_filename(playlist, index, seek, control_file, info->filename,
- sizeof(info->filename)) < 0)
- return -1;
+ playlist_shutdown(); /* flush any cached control commands to disk */
+ empty_playlist(playlist, true);
- info->attr = 0;
+ playlist->control_fd = open(playlist->control_filename, O_RDWR);
+ if (playlist->control_fd < 0)
+ {
+ splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ goto out;
+ }
+ playlist->control_created = true;
- if (control_file)
+ control_file_size = filesize(playlist->control_fd);
+ if (control_file_size <= 0)
{
- if (playlist->indices[index] & PLAYLIST_QUEUE_MASK)
- info->attr |= PLAYLIST_ATTR_QUEUED;
+ splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ goto out;
+ }
+
+ /* read a small amount first to get the header */
+ nread = read(playlist->control_fd, buffer,
+ PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
+ if(nread <= 0)
+ {
+ splash(HZ*2, ID2P(LANG_PLAYLIST_CONTROL_ACCESS_ERROR));
+ goto out;
+ }
+
+ playlist->started = true;
+
+ while (1)
+ {
+ result = 0;
+ int count;
+ enum playlist_command current_command = PLAYLIST_COMMAND_COMMENT;
+ int last_newline = 0;
+ int str_count = -1;
+ bool newline = true;
+ bool exit_loop = false;
+ char *p = buffer;
+ char *str1 = NULL;
+ char *str2 = NULL;
+ char *str3 = NULL;
+ unsigned long last_tick = current_tick;
+ splash_progress_set_delay(HZ / 2); /* wait 1/2 sec before progress */
+ bool useraborted = false;
+
+ for(count=0; count<nread && !exit_loop && !useraborted; count++,p++)
+ {
+ /* Show a splash while we are loading. */
+ splash_progress((total_read + count), control_file_size,
+ "%s (%s)", str(LANG_WAIT), str(LANG_OFF_ABORT));
+ if (TIME_AFTER(current_tick, last_tick + HZ/4))
+ {
+ if (action_userabort(TIMEOUT_NOBLOCK))
+ {
+ useraborted = true;
+ break;
+ }
+ last_tick = current_tick;
+ }
+ /* Are we on a new line? */
+ if((*p == '\n') || (*p == '\r'))
+ {
+ *p = '\0';
+
+ /* save last_newline in case we need to load more data */
+ last_newline = count;
+
+ switch (current_command)
+ {
+ case PLAYLIST_COMMAND_PLAYLIST:
+ {
+ /* str1=version str2=dir str3=file */
+ int version;
+
+ if (!str1)
+ {
+ result = -2;
+ exit_loop = true;
+ break;
+ }
+
+ if (!str2)
+ str2 = "";
+
+ if (!str3)
+ str3 = "";
+
+ version = atoi(str1);
+
+ if (version != PLAYLIST_CONTROL_FILE_VERSION)
+ {
+ result = -3;
+ goto out;
+ }
+
+ update_playlist_filename(playlist, str2, str3);
+
+ if (str3[0] != '\0')
+ {
+ /* NOTE: add_indices_to_playlist() overwrites the
+ audiobuf so we need to reload control file
+ data */
+ add_indices_to_playlist(playlist, buffer, buflen);
+ }
+ else if (str2[0] != '\0')
+ {
+ playlist->in_ram = true;
+ resume_directory(str2);
+ }
+
+ /* load the rest of the data */
+ first = false;
+ exit_loop = true;
+
+ break;
+ }
+ case PLAYLIST_COMMAND_ADD:
+ case PLAYLIST_COMMAND_QUEUE:
+ {
+ /* str1=position str2=last_position str3=file */
+ int position, last_position;
+ bool queue;
+
+ if (!str1 || !str2 || !str3)
+ {
+ result = -4;
+ exit_loop = true;
+ break;
+ }
+
+ position = atoi(str1);
+ last_position = atoi(str2);
+
+ queue = (current_command == PLAYLIST_COMMAND_ADD)?
+ false:true;
+
+ /* seek position is based on str3's position in
+ buffer */
+ if (add_track_to_playlist(playlist, str3, position,
+ queue, total_read+(str3-buffer)) < 0)
+ {
+ result = -5;
+ goto out;
+ }
+
+ playlist->last_insert_pos = last_position;
+
+ break;
+ }
+ case PLAYLIST_COMMAND_DELETE:
+ {
+ /* str1=position */
+ int position;
+
+ if (!str1)
+ {
+ result = -6;
+ exit_loop = true;
+ break;
+ }
+
+ position = atoi(str1);
+
+ if (remove_track_from_playlist(playlist, position,
+ false) < 0)
+ {
+ result = -7;
+ goto out;
+ }
+
+ break;
+ }
+ case PLAYLIST_COMMAND_SHUFFLE:
+ {
+ /* str1=seed str2=first_index */
+ int seed;
+
+ if (!str1 || !str2)
+ {
+ result = -8;
+ exit_loop = true;
+ break;
+ }
+
+ if (!sorted)
+ {
+ /* Always sort list before shuffling */
+ sort_playlist(playlist, false, false);
+ }
+
+ seed = atoi(str1);
+ playlist->first_index = atoi(str2);
+
+ if (randomise_playlist(playlist, seed, false,
+ false) < 0)
+ {
+ result = -9;
+ goto out;
+ }
+ sorted = false;
+ break;
+ }
+ case PLAYLIST_COMMAND_UNSHUFFLE:
+ {
+ /* str1=first_index */
+ if (!str1)
+ {
+ result = -10;
+ exit_loop = true;
+ break;
+ }
+
+ playlist->first_index = atoi(str1);
+
+ if (sort_playlist(playlist, false, false) < 0)
+ {
+ result = -11;
+ goto out;
+ }
+
+ sorted = true;
+ break;
+ }
+ case PLAYLIST_COMMAND_RESET:
+ {
+ playlist->last_insert_pos = -1;
+ break;
+ }
+ case PLAYLIST_COMMAND_COMMENT:
+ default:
+ break;
+ }
+
+ newline = true;
+
+ /* to ignore any extra newlines */
+ current_command = PLAYLIST_COMMAND_COMMENT;
+ }
+ else if(newline)
+ {
+ newline = false;
+
+ /* first non-comment line must always specify playlist */
+ if (first && *p != 'P' && *p != '#')
+ {
+ result = -12;
+ exit_loop = true;
+ break;
+ }
+
+ switch (*p)
+ {
+ case 'P':
+ /* playlist can only be specified once */
+ if (!first)
+ {
+ result = -13;
+ exit_loop = true;
+ break;
+ }
+
+ current_command = PLAYLIST_COMMAND_PLAYLIST;
+ break;
+ case 'A':
+ current_command = PLAYLIST_COMMAND_ADD;
+ break;
+ case 'Q':
+ current_command = PLAYLIST_COMMAND_QUEUE;
+ break;
+ case 'D':
+ current_command = PLAYLIST_COMMAND_DELETE;
+ break;
+ case 'S':
+ current_command = PLAYLIST_COMMAND_SHUFFLE;
+ break;
+ case 'U':
+ current_command = PLAYLIST_COMMAND_UNSHUFFLE;
+ break;
+ case 'R':
+ current_command = PLAYLIST_COMMAND_RESET;
+ break;
+ case '#':
+ current_command = PLAYLIST_COMMAND_COMMENT;
+ break;
+ default:
+ result = -14;
+ exit_loop = true;
+ break;
+ }
+
+ str_count = -1;
+ str1 = NULL;
+ str2 = NULL;
+ str3 = NULL;
+ }
+ else if(current_command != PLAYLIST_COMMAND_COMMENT)
+ {
+ /* all control file strings are separated with a colon.
+ Replace the colon with 0 to get proper strings that can be
+ used by commands above */
+ if (*p == ':')
+ {
+ *p = '\0';
+ str_count++;
+
+ if ((count+1) < nread)
+ {
+ switch (str_count)
+ {
+ case 0:
+ str1 = p+1;
+ break;
+ case 1:
+ str2 = p+1;
+ break;
+ case 2:
+ str3 = p+1;
+ break;
+ default:
+ /* allow last string to contain colons */
+ *p = ':';
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (result < 0)
+ {
+ splashf(HZ*2, "Err: %d, %s", result, str(LANG_PLAYLIST_CONTROL_INVALID));
+ goto out;
+ }
+
+ if (useraborted)
+ {
+ splash(HZ*2, ID2P(LANG_CANCEL));
+ result = -1;
+ goto out;
+ }
+ if (!newline || (exit_loop && count<nread))
+ {
+ if ((total_read + count) >= control_file_size)
+ {
+ /* no newline at end of control file */
+ splashf(HZ*2, "Err: EOF, %s", str(LANG_PLAYLIST_CONTROL_INVALID));
+ result = -15;
+ goto out;
+ }
+
+ /* We didn't end on a newline or we exited loop prematurely.
+ Either way, re-read the remainder. */
+ count = last_newline;
+ lseek(playlist->control_fd, total_read+count, SEEK_SET);
+ }
+
+ total_read += count;
+
+ if (first)
+ /* still looking for header */
+ nread = read(playlist->control_fd, buffer,
+ PLAYLIST_COMMAND_SIZE<buflen?PLAYLIST_COMMAND_SIZE:buflen);
else
- info->attr |= PLAYLIST_ATTR_INSERTED;
-
+ nread = read(playlist->control_fd, buffer, buflen);
+
+ /* Terminate on EOF */
+ if(nread <= 0)
+ {
+ break;
+ }
}
- if (playlist->indices[index] & PLAYLIST_SKIPPED)
- info->attr |= PLAYLIST_ATTR_SKIPPED;
-
- info->index = index;
- info->display_index = rotate_index(playlist, index) + 1;
+#ifdef HAVE_DIRCACHE
+ queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
+#endif
- return 0;
+out:
+ talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
+ core_free(handle);
+ return result;
+}
+
+/* resume a playlist track with the given crc_32 of the track name. */
+void playlist_resume_track(int start_index, unsigned int crc,
+ unsigned long elapsed, unsigned long offset)
+{
+ int i;
+ unsigned int tmp_crc;
+ struct playlist_info* playlist = &current_playlist;
+ tmp_crc = playlist_get_filename_crc32(playlist, start_index);
+ if (tmp_crc == crc)
+ {
+ playlist_start(start_index, elapsed, offset);
+ return;
+ }
+
+ for (i = 0 ; i < playlist->amount; i++)
+ {
+ tmp_crc = playlist_get_filename_crc32(playlist, i);
+ if (tmp_crc == crc)
+ {
+ playlist_start(i, elapsed, offset);
+ return;
+ }
+ }
+
+ /* If we got here the file wasnt found, so start from the beginning */
+ playlist_start(0, 0, 0);
}
/* save the current dynamic playlist to specified file. The
@@ -3791,104 +3730,194 @@ reset_old_buffer:
}
/*
- * Search specified directory for tracks and notify via callback. May be
- * called recursively.
+ * Set the specified playlist as the current.
+ * NOTE: You will get undefined behaviour if something is already playing so
+ * remember to stop before calling this. Also, this call will
+ * effectively close your playlist, making it unusable.
*/
-int playlist_directory_tracksearch(const char* dirname, bool recurse,
- int (*callback)(char*, void*),
- void* context)
+int playlist_set_current(struct playlist_info* playlist)
{
- char buf[MAX_PATH+1];
- int result = 0;
- int num_files = 0;
- int i;;
- struct tree_context* tc = tree_get_context();
- struct tree_cache* cache = &tc->cache;
- int old_dirfilter = *(tc->dirfilter);
+ if (!playlist || (check_control(playlist) < 0))
+ return -1;
- if (!callback)
+ empty_playlist(&current_playlist, false);
+
+ strmemccpy(current_playlist.filename, playlist->filename,
+ sizeof(current_playlist.filename));
+
+ current_playlist.utf8 = playlist->utf8;
+ current_playlist.fd = playlist->fd;
+
+ close(playlist->control_fd);
+ playlist->control_fd = -1;
+ close(current_playlist.control_fd);
+ current_playlist.control_fd = -1;
+ remove(current_playlist.control_filename);
+ current_playlist.control_created = false;
+ if (rename(playlist->control_filename,
+ current_playlist.control_filename) < 0)
+ return -1;
+ current_playlist.control_fd = open(current_playlist.control_filename,
+ O_RDWR);
+ if (current_playlist.control_fd < 0)
return -1;
+ current_playlist.control_created = true;
- /* use the tree browser dircache to load files */
- *(tc->dirfilter) = SHOW_ALL;
+ current_playlist.dirlen = playlist->dirlen;
- if (ft_load(tc, dirname) < 0)
+ if (playlist->indices && playlist->indices != current_playlist.indices)
{
- splash(HZ*2, ID2P(LANG_PLAYLIST_DIRECTORY_ACCESS_ERROR));
- *(tc->dirfilter) = old_dirfilter;
- return -1;
+ memcpy((void*)current_playlist.indices, (void*)playlist->indices,
+ playlist->max_playlist_size*sizeof(*playlist->indices));
+#ifdef HAVE_DIRCACHE
+ copy_filerefs(current_playlist.dcfrefs, playlist->dcfrefs,
+ playlist->max_playlist_size);
+#endif
}
- num_files = tc->filesindir;
+ current_playlist.first_index = playlist->first_index;
+ current_playlist.amount = playlist->amount;
+ current_playlist.last_insert_pos = playlist->last_insert_pos;
+ current_playlist.seed = playlist->seed;
+ current_playlist.shuffle_modified = playlist->shuffle_modified;
+ current_playlist.deleted = playlist->deleted;
+ current_playlist.num_inserted_tracks = playlist->num_inserted_tracks;
- /* we've overwritten the dircache so tree browser will need to be
- reloaded */
- reload_directory();
+ memcpy(current_playlist.control_cache, playlist->control_cache,
+ sizeof(current_playlist.control_cache));
+ current_playlist.num_cached = playlist->num_cached;
+ current_playlist.pending_control_sync = playlist->pending_control_sync;
- for (i=0; i<num_files; i++)
+ return 0;
+}
+
+/* set playlist->last_shuffle_start to playlist->amount for
+ PLAYLIST_INSERT_LAST_SHUFFLED command purposes*/
+void playlist_set_last_shuffled_start(void)
+{
+ struct playlist_info* playlist = &current_playlist;
+ playlist->last_shuffled_start = playlist->amount;
+}
+
+/* shuffle newly created playlist using random seed. */
+int playlist_shuffle(int random_seed, int start_index)
+{
+ struct playlist_info* playlist = &current_playlist;
+
+ bool start_current = false;
+
+ if (start_index >= 0 && global_settings.play_selected)
{
- /* user abort */
- if (action_userabort(TIMEOUT_NOBLOCK))
- {
- result = -1;
- break;
- }
+ /* store the seek position before the shuffle */
+ playlist->index = playlist->first_index = start_index;
+ start_current = true;
+ }
- struct entry *files = core_get_data(cache->entries_handle);
- if (files[i].attr & ATTR_DIRECTORY)
- {
- if (recurse)
- {
- /* recursively add directories */
- if (path_append(buf, dirname, files[i].name, sizeof(buf))
- >= sizeof(buf))
- {
- continue;
- }
+ randomise_playlist(playlist, random_seed, start_current, true);
- result = playlist_directory_tracksearch(buf, recurse,
- callback, context);
- if (result < 0)
- break;
+ return playlist->index;
+}
- /* we now need to reload our current directory */
- if(ft_load(tc, dirname) < 0)
- {
- result = -1;
- break;
- }
+/* Marks the index of the track to be skipped that is "steps" away from
+ * current playing track.
+ */
+void playlist_skip_entry(struct playlist_info *playlist, int steps)
+{
+ int index;
- num_files = tc->filesindir;
- if (!num_files)
- {
- result = -1;
- break;
- }
- }
- else
- continue;
- }
- else if ((files[i].attr & FILE_ATTR_MASK) == FILE_ATTR_AUDIO)
- {
- if (path_append(buf, dirname, files[i].name, sizeof(buf))
- >= sizeof(buf))
- {
- continue;
- }
+ if (playlist == NULL)
+ playlist = &current_playlist;
- if (callback(buf, context) != 0)
- {
- result = -1;
- break;
- }
+ /* need to account for already skipped tracks */
+ steps = calculate_step_count(playlist, steps);
- /* let the other threads work */
- yield();
- }
- }
+ index = playlist->index + steps;
+ if (index < 0)
+ index += playlist->amount;
+ else if (index >= playlist->amount)
+ index -= playlist->amount;
- /* restore dirfilter */
- *(tc->dirfilter) = old_dirfilter;
+ playlist->indices[index] |= PLAYLIST_SKIPPED;
+}
+
+/* sort currently playing playlist */
+int playlist_sort(struct playlist_info* playlist, bool start_current)
+{
+ int result;
+
+ if (!playlist)
+ playlist = &current_playlist;
+
+ check_control(playlist);
+
+ result = sort_playlist(playlist, start_current, true);
+
+ if (result != -1 && (audio_status() & AUDIO_STATUS_PLAY) &&
+ playlist->started)
+ audio_flush_and_reload_tracks();
return result;
}
+
+/* start playing current playlist at specified index/offset */
+void playlist_start(int start_index, unsigned long elapsed,
+ unsigned long offset)
+{
+ struct playlist_info* playlist = &current_playlist;
+
+ playlist->index = start_index;
+
+ playlist->started = true;
+ sync_control(playlist, false);
+ audio_play(elapsed, offset);
+ audio_resume();
+}
+
+void playlist_sync(struct playlist_info* playlist)
+{
+ if (!playlist)
+ playlist = &current_playlist;
+
+ sync_control(playlist, false);
+ if ((audio_status() & AUDIO_STATUS_PLAY) && playlist->started)
+ audio_flush_and_reload_tracks();
+
+#ifdef HAVE_DIRCACHE
+ queue_post(&playlist_queue, PLAYLIST_LOAD_POINTERS, 0);
+#endif
+}
+
+/* Update resume info for current playing song. Returns -1 on error. */
+int playlist_update_resume_info(const struct mp3entry* id3)
+{
+ struct playlist_info* playlist = &current_playlist;
+
+ if (id3)
+ {
+ if (global_status.resume_index != playlist->index ||
+ global_status.resume_elapsed != id3->elapsed ||
+ global_status.resume_offset != id3->offset)
+ {
+ unsigned int crc = playlist_get_filename_crc32(playlist,
+ playlist->index);
+ global_status.resume_index = playlist->index;
+ global_status.resume_crc32 = crc;
+ global_status.resume_elapsed = id3->elapsed;
+ global_status.resume_offset = id3->offset;
+ status_save();
+ }
+ }
+ else
+ {
+ global_status.resume_index = -1;
+ global_status.resume_crc32 = -1;
+ global_status.resume_elapsed = -1;
+ global_status.resume_offset = -1;
+ status_save();
+ }
+
+ return 0;
+}
+
+
+
diff --git a/apps/playlist.h b/apps/playlist.h
index 2eca7355e4..ab2afefddd 100644
--- a/apps/playlist.h
+++ b/apps/playlist.h
@@ -74,22 +74,16 @@ struct playlist_control_cache {
struct playlist_info
{
bool current; /* current playing playlist */
- char filename[MAX_PATH]; /* path name of m3u playlist on disk */
- char control_filename[MAX_PATH]; /* full path of control file */
bool utf8; /* playlist is in .m3u8 format */
+ bool control_created; /* has control file been created? */
+ bool in_ram; /* playlist stored in ram (dirplay) */
int fd; /* descriptor of the open playlist file */
int control_fd; /* descriptor of the open control file */
- bool control_created; /* has control file been created? */
- int dirlen; /* Length of the path to the playlist file */
- volatile unsigned long *indices; /* array of indices */
-#ifdef HAVE_DIRCACHE
- struct dircache_fileref *dcfrefs; /* Dircache entry shortcuts */
-#endif
int max_playlist_size; /* Max number of files in playlist. Mirror of
global_settings.max_files_in_playlist */
- bool in_ram; /* playlist stored in ram (dirplay) */
+ int num_inserted_tracks; /* number of tracks inserted */
+ volatile unsigned long *indices; /* array of indices */
int buffer_handle; /* handle to the below buffer (-1 if non-buflib) */
-
volatile char *buffer;/* buffer for in-ram playlists */
int buffer_size; /* size of buffer */
int buffer_end_pos; /* last position where buffer was written */
@@ -97,22 +91,25 @@ struct playlist_info
int first_index; /* index of first song in playlist */
int amount; /* number of tracks in the index */
int last_insert_pos; /* last position we inserted a track */
- int seed; /* shuffle seed */
- bool shuffle_modified; /* has playlist been shuffled with
- inserted tracks? */
bool deleted; /* have any tracks been deleted? */
- int num_inserted_tracks; /* number of tracks inserted */
bool started; /* has playlist been started? */
-
+ bool pending_control_sync; /* control file needs to be synced */
+ bool shuffle_modified; /* has playlist been shuffled with
+ inserted tracks? */
+ int last_shuffled_start; /* number of tracks when insert last
+ shuffled command start */
+ int seed; /* shuffle seed */
/* cache of playlist control commands waiting to be flushed to
to disk */
struct playlist_control_cache control_cache[PLAYLIST_MAX_CACHE];
int num_cached; /* number of cached entries */
- bool pending_control_sync; /* control file needs to be synced */
-
struct mutex *control_mutex; /* mutex for control file access */
- int last_shuffled_start; /* number of tracks when insert last
- shuffled command start */
+#ifdef HAVE_DIRCACHE
+ struct dircache_fileref *dcfrefs; /* Dircache entry shortcuts */
+#endif
+ int dirlen; /* Length of the path to the playlist file */
+ char filename[MAX_PATH]; /* path name of m3u playlist on disk */
+ char control_filename[MAX_PATH]; /* full path of control file */
};
struct playlist_track_info
diff --git a/apps/playlist_catalog.c b/apps/playlist_catalog.c
index ba12ff6d98..b160d6c3f4 100644
--- a/apps/playlist_catalog.c
+++ b/apps/playlist_catalog.c
@@ -46,6 +46,7 @@
#include "talk.h"
#include "playlist_viewer.h"
#include "bookmark.h"
+#include "root_menu.h"
/* Use for recursive directory search */
struct add_track_context {
@@ -53,73 +54,73 @@ struct add_track_context {
int count;
};
+enum catbrowse_status_flags{
+ CATBROWSE_NOTHING = 0,
+ CATBROWSE_CATVIEW,
+ CATBROWSE_PLAYLIST
+};
+
/* keep track of most recently used playlist */
static char most_recent_playlist[MAX_PATH];
+/* we need playlist_dir_length for easy removal of playlist dir prefix */
+static size_t playlist_dir_length;
+/* keep track of what browser(s) are current to prevent reentry */
+static int browser_status = CATBROWSE_NOTHING;
-/* directory where our playlists our stored */
-static char playlist_dir[MAX_PATH];
-static int playlist_dir_length;
-static bool playlist_dir_exists = false;
-
-/* Retrieve playlist directory from config file and verify it exists */
-static bool initialized = false;
-static int initialize_catalog(void)
+static size_t get_directory(char* dirbuf, size_t dirbuf_sz)
{
+ char *pl_dir = PLAYLIST_CATALOG_DEFAULT_DIR;
- if (!initialized)
+ /* directory config is of the format: "dir: /path/to/dir" */
+ if (global_settings.playlist_catalog_dir[0] != '\0')
{
- bool default_dir = true;
-
- /* directory config is of the format: "dir: /path/to/dir" */
- if (global_settings.playlist_catalog_dir[0] &&
- strcmp(global_settings.playlist_catalog_dir,
- PLAYLIST_CATALOG_DEFAULT_DIR))
- {
- strcpy(playlist_dir, global_settings.playlist_catalog_dir);
- default_dir = false;
- }
-
- /* fall back to default directory if no or invalid config */
- if (default_dir)
- {
- strcpy(playlist_dir, PLAYLIST_CATALOG_DEFAULT_DIR);
- if (!dir_exists(playlist_dir))
- mkdir(playlist_dir);
- }
+ pl_dir = global_settings.playlist_catalog_dir;
+ }
- playlist_dir_length = strlen(playlist_dir);
+ /* remove duplicate leading '/' */
+ if (pl_dir[0] == '/' && pl_dir[1] == '/')
+ {
+ pl_dir++;
+ }
- /* remove duplicate leading '/' */
- if (playlist_dir[0] == '/' && playlist_dir[1] == '/')
- {
- memmove(&playlist_dir[0], &playlist_dir[1], playlist_dir_length); /* gets the \0 too */
- playlist_dir_length--;
- }
+ return strlcpy(dirbuf, pl_dir, dirbuf_sz);
+}
- if (dir_exists(playlist_dir))
- {
- playlist_dir_exists = true;
- memset(most_recent_playlist, 0, sizeof(most_recent_playlist));
- initialized = true;
- }
+/* Retrieve playlist directory from config file and verify it exists
+ * attempts to create directory
+ * catalog dir is returned in dirbuf */
+static int initialize_catalog_buf(char* dirbuf, size_t dirbuf_sz)
+{
+ playlist_dir_length = get_directory(dirbuf, dirbuf_sz);
+ if (playlist_dir_length >= dirbuf_sz)
+ {
+ return -2;
}
- if (!playlist_dir_exists)
+ if (!dir_exists(dirbuf))
{
- if (mkdir(playlist_dir) < 0) {
- splashf(HZ*2, ID2P(LANG_CATALOG_NO_DIRECTORY), playlist_dir);
+ if (mkdir(dirbuf) < 0) {
+ splashf(HZ*2, ID2P(LANG_CATALOG_NO_DIRECTORY), dirbuf);
return -1;
}
else {
- playlist_dir_exists = true;
memset(most_recent_playlist, 0, sizeof(most_recent_playlist));
- initialized = true;
}
}
return 0;
}
+/* Retrieve playlist directory from config file and verify it exists
+ * attempts to create directory
+ * Don't inline as we want the stack to be freed ASAP */
+static NO_INLINE int initialize_catalog(void)
+{
+ char playlist_dir[MAX_PATH];
+
+ return initialize_catalog_buf(playlist_dir, sizeof(playlist_dir));
+}
+
void catalog_set_directory(const char* directory)
{
if (directory == NULL)
@@ -128,62 +129,123 @@ void catalog_set_directory(const char* directory)
}
else
{
- strlcpy(global_settings.playlist_catalog_dir,
- directory, sizeof(global_settings.playlist_catalog_dir));
+ strmemccpy(global_settings.playlist_catalog_dir,
+ directory, sizeof(global_settings.playlist_catalog_dir));
}
- initialized = false;
initialize_catalog();
}
-const char* catalog_get_directory(void)
+void catalog_get_directory(char* dirbuf, size_t dirbuf_sz)
{
- if (initialize_catalog() == -1)
- return "";
- return playlist_dir;
+ if (initialize_catalog_buf(dirbuf, dirbuf_sz) < 0)
+ {
+ dirbuf[0] = '\0';
+ return;
+ }
}
/* Display all playlists in catalog. Selected "playlist" is returned.
- If "view" mode is set then we're not adding anything into playlist. */
-static int display_playlists(char* playlist, bool view)
+ * If status is CATBROWSE_CATVIEW then we're not adding anything into playlist */
+static int display_playlists(char* playlist, enum catbrowse_status_flags status)
{
+ static bool reopen_last_playlist = false;
+ static int most_recent_selection = 0;
struct browse_context browse;
- char selected_playlist[MAX_PATH];
int result = -1;
+ char selected_playlist[MAX_PATH];
+ selected_playlist[0] = '\0';
+
+ browser_status |= status;
+ bool view = (status == CATBROWSE_CATVIEW);
browse_context_init(&browse, SHOW_M3U,
BROWSE_SELECTONLY|(view? 0: BROWSE_NO_CONTEXT_MENU),
str(LANG_CATALOG), NOICON,
- playlist_dir, most_recent_playlist);
+ selected_playlist,
+ playlist_dir_length + 1 + most_recent_playlist);
browse.buf = selected_playlist;
browse.bufsize = sizeof(selected_playlist);
restart:
+ /* set / restore the root directory for the browser */
+ catalog_get_directory(selected_playlist, sizeof(selected_playlist));
browse.flags &= ~BROWSE_SELECTED;
- rockbox_browse(&browse);
- if (browse.flags & BROWSE_SELECTED)
+ if (view && reopen_last_playlist)
{
- strlcpy(most_recent_playlist, selected_playlist+playlist_dir_length+1,
- sizeof(most_recent_playlist));
-
- if (view)
+ switch (playlist_viewer_ex(most_recent_playlist, &most_recent_selection))
{
-
- if (!bookmark_autoload(selected_playlist))
+ case PLAYLIST_VIEWER_OK:
{
- if (playlist_viewer_ex(selected_playlist) == PLAYLIST_VIEWER_CANCEL)
- goto restart;
+ result = 0;
+ break;
}
+ case PLAYLIST_VIEWER_CANCEL:
+ {
+ reopen_last_playlist = false;
+ goto restart;
+ }
+ case PLAYLIST_VIEWER_USB:
+ case PLAYLIST_VIEWER_MAINMENU:
+ /* Fall through */
+ default:
+ break;
+ }
+ }
+ else /* browse playlist dir */
+ {
+ int browse_ret = rockbox_browse(&browse);
+ if (browse_ret == GO_TO_WPS
+ || (view && browse_ret == GO_TO_PREVIOUS_MUSIC))
result = 0;
+ }
+
+ if (browse.flags & BROWSE_SELECTED) /* User picked a playlist */
+ {
+ if (strcmp(most_recent_playlist, selected_playlist)) /* isn't most recent one */
+ {
+ strmemccpy(most_recent_playlist, selected_playlist,
+ sizeof(most_recent_playlist));
+ most_recent_selection = 0;
+ reopen_last_playlist = false;
}
- else
+
+ if (view) /* display playlist contents or resume bookmark */
+ {
+
+ int res = bookmark_autoload(selected_playlist);
+ if (res == BOOKMARK_DO_RESUME)
+ result = 0;
+ else
+ {
+ switch (playlist_viewer_ex(selected_playlist, &most_recent_selection)) {
+ case PLAYLIST_VIEWER_OK:
+ {
+ reopen_last_playlist = true;
+ result = 0;
+ break;
+ }
+ case PLAYLIST_VIEWER_CANCEL:
+ {
+ goto restart;
+ }
+ case PLAYLIST_VIEWER_USB:
+ case PLAYLIST_VIEWER_MAINMENU:
+ /* Fall through */
+ default:
+ reopen_last_playlist = true;
+ break;
+ }
+ }
+ }
+ else /* we're just adding something to a playlist */
{
result = 0;
- strlcpy(playlist, selected_playlist, MAX_PATH);
+ strmemccpy(playlist, selected_playlist, MAX_PATH);
}
}
-
+ browser_status &= ~status;
return result;
}
@@ -192,7 +254,7 @@ restart:
static void display_insert_count(int count)
{
static long talked_tick = 0;
- if(global_settings.talk_menu && count &&
+ if(global_settings.talk_menu && count &&
(talked_tick == 0 || TIME_AFTER(current_tick, talked_tick+5*HZ)))
{
talked_tick = current_tick;
@@ -233,8 +295,10 @@ static int add_to_playlist(const char* playlist, bool new_playlist,
fd = open(playlist, O_CREAT|O_WRONLY|O_APPEND, 0666);
if(fd < 0)
+ {
+ splash(HZ*2, ID2P(LANG_FAILED));
return result;
-
+ }
/* In case we're in the playlist directory */
reload_directory();
@@ -310,32 +374,26 @@ exit:
close(fd);
return result;
}
-static bool in_cat_viewer = false;
+
bool catalog_view_playlists(void)
{
- bool retval = true;
- if (in_cat_viewer)
+ if ((browser_status & CATBROWSE_CATVIEW) == CATBROWSE_CATVIEW)
return false;
- if (initialize_catalog() == -1)
+ if (initialize_catalog() < 0)
return false;
- in_cat_viewer = true;
- retval = (display_playlists(NULL, true) != -1);
- in_cat_viewer = false;
- return retval;
+ return (display_playlists(NULL, CATBROWSE_CATVIEW) >= 0);
}
-static bool in_add_to_playlist = false;
bool catalog_add_to_a_playlist(const char* sel, int sel_attr,
bool new_playlist, char *m3u8name)
{
- int result;
char playlist[MAX_PATH + 7]; /* room for /.m3u8\0*/
- if (in_add_to_playlist)
+ if ((browser_status & CATBROWSE_PLAYLIST) == CATBROWSE_PLAYLIST)
return false;
- if (initialize_catalog() == -1)
+ if (initialize_catalog_buf(playlist, sizeof(playlist)) < 0)
return false;
if (new_playlist)
@@ -345,7 +403,7 @@ bool catalog_add_to_a_playlist(const char* sel, int sel_attr,
{
const char *name;
/* If sel is empty, root, or playlist directory we use 'all' */
- if (!sel || !strcmp(sel, "/") || !strcmp(sel, playlist_dir))
+ if (!sel || !strcmp(sel, "/") || !strcmp(sel, playlist))
{
sel = "/";
name = "/all";
@@ -353,12 +411,13 @@ bool catalog_add_to_a_playlist(const char* sel, int sel_attr,
else /*If sel is a folder, we prefill the text field with its name*/
name = strrchr(sel, '/');
- snprintf(playlist, MAX_PATH + 1, "%s/%s",
- playlist_dir,
- (name!=NULL && (sel_attr & ATTR_DIRECTORY))?name+1:"");
+ if (name == NULL || ((sel_attr & ATTR_DIRECTORY) != ATTR_DIRECTORY))
+ name = "/";
+
+ strlcat(playlist, name, sizeof(playlist));
}
else
- strlcpy(playlist, m3u8name, MAX_PATH);
+ strmemccpy(playlist, m3u8name, MAX_PATH);
if (kbd_input(playlist, MAX_PATH, NULL))
return false;
@@ -371,11 +430,7 @@ bool catalog_add_to_a_playlist(const char* sel, int sel_attr,
}
else
{
- in_add_to_playlist = true;
- result = display_playlists(playlist, false);
- in_add_to_playlist = false;
-
- if (result == -1)
+ if (display_playlists(playlist, CATBROWSE_PLAYLIST) < 0)
return false;
}
diff --git a/apps/playlist_catalog.h b/apps/playlist_catalog.h
index 2e317128b7..bb16e2dad9 100644
--- a/apps/playlist_catalog.h
+++ b/apps/playlist_catalog.h
@@ -22,14 +22,14 @@
#define _PLAYLIST_CATALOG_H_
/* Gets the configured playlist catalog dir */
-const char* catalog_get_directory(void);
+void catalog_get_directory(char* dirbuf, size_t dirbuf_sz);
/* Set the playlist catalog dir */
void catalog_set_directory(const char* directory);
/*
* View list of playlists in catalog.
- * ret : true if no error
+ * ret : true if item was selected
*/
bool catalog_view_playlists(void);
diff --git a/apps/playlist_viewer.c b/apps/playlist_viewer.c
index 7e50c42bdc..8f1c5d961d 100644
--- a/apps/playlist_viewer.c
+++ b/apps/playlist_viewer.c
@@ -76,6 +76,7 @@ enum direction
enum pv_onplay_result {
PV_ONPLAY_USB,
PV_ONPLAY_USB_CLOSED,
+ PV_ONPLAY_WPS_CLOSED,
PV_ONPLAY_CLOSED,
PV_ONPLAY_ITEM_REMOVED,
PV_ONPLAY_CHANGED,
@@ -101,8 +102,10 @@ struct playlist_buffer
/* Global playlist viewer settings */
struct playlist_viewer {
- struct playlist_info* playlist; /* playlist being viewed */
+ const char *title; /* Playlist Viewer list title */
+ struct playlist_info* playlist; /* Playlist being viewed */
int num_tracks; /* Number of tracks in playlist */
+ int *initial_selection; /* The initially selected track */
int current_playing_track; /* Index of current playing track */
int selected_track; /* The selected track, relative (first is 0) */
int moving_track; /* The track to move, relative (first is 0)
@@ -130,7 +133,8 @@ static struct playlist_entry * playlist_buffer_get_track(struct playlist_buffer
int index);
static bool playlist_viewer_init(struct playlist_viewer * viewer,
- const char* filename, bool reload);
+ const char* filename, bool reload,
+ int *most_recent_selection);
static void format_name(char* dest, const char* src);
static void format_line(const struct playlist_entry* track, char* str,
@@ -319,7 +323,8 @@ static struct playlist_entry * playlist_buffer_get_track(struct playlist_buffer
/* Initialize the playlist viewer. */
static bool playlist_viewer_init(struct playlist_viewer * viewer,
- const char* filename, bool reload)
+ const char* filename, bool reload,
+ int *most_recent_selection)
{
char* buffer;
size_t buffer_size;
@@ -348,7 +353,10 @@ static bool playlist_viewer_init(struct playlist_viewer * viewer,
return false;
if (!filename)
+ {
viewer->playlist = NULL;
+ viewer->title = (char *) str(LANG_PLAYLIST);
+ }
else
{
/* Viewing playlist on disk */
@@ -372,6 +380,7 @@ static bool playlist_viewer_init(struct playlist_viewer * viewer,
dir = "/";
file = filename+1;
}
+ viewer->title = file;
if (is_playing)
{
@@ -400,11 +409,12 @@ static bool playlist_viewer_init(struct playlist_viewer * viewer,
viewer->moving_track = -1;
viewer->moving_playlist_index = -1;
+ viewer->initial_selection = most_recent_selection;
if (!reload)
{
if (viewer->playlist)
- viewer->selected_track = 0;
+ viewer->selected_track = most_recent_selection ? *most_recent_selection : 0;
else
viewer->selected_track = playlist_get_display_index() - 1;
}
@@ -511,25 +521,47 @@ static enum pv_onplay_result show_track_info(const struct playlist_entry *curren
}
+#ifdef HAVE_HOTKEY
static enum pv_onplay_result open_with(const struct playlist_entry *current_track)
{
char selected_track[MAX_PATH];
close_playlist_viewer();
- snprintf(selected_track, sizeof(selected_track), "%s", current_track->name);
- return (filetype_list_viewers(selected_track) ==
- PLUGIN_USB_CONNECTED ? PV_ONPLAY_USB_CLOSED : PV_ONPLAY_CLOSED);
+ strmemccpy(selected_track, current_track->name, sizeof(selected_track));
+
+ int plugin_return = filetype_list_viewers(selected_track);
+
+ switch (plugin_return)
+ {
+ case PLUGIN_USB_CONNECTED:
+ return PV_ONPLAY_USB_CLOSED;
+ case PLUGIN_GOTO_WPS:
+ return PV_ONPLAY_WPS_CLOSED;
+ default:
+ return PV_ONPLAY_CLOSED;
+ }
}
+#endif /* HAVE_HOTKEY */
#ifdef HAVE_TAGCACHE
static enum pv_onplay_result open_pictureflow(const struct playlist_entry *current_track)
{
char selected_track[MAX_PATH];
close_playlist_viewer();
- snprintf(selected_track, sizeof(selected_track), "%s", current_track->name);
- return (filetype_load_plugin((void *)"pictureflow", selected_track) ==
- PLUGIN_USB_CONNECTED ? PV_ONPLAY_USB_CLOSED : PV_ONPLAY_CLOSED);
+ strmemccpy(selected_track, current_track->name, sizeof(selected_track));
+
+ int plugin_return = filetype_load_plugin((void *)"pictureflow", selected_track);
+
+ switch (plugin_return)
+ {
+ case PLUGIN_USB_CONNECTED:
+ return PV_ONPLAY_USB_CLOSED;
+ case PLUGIN_GOTO_WPS:
+ return PV_ONPLAY_WPS_CLOSED;
+ default:
+ return PV_ONPLAY_CLOSED;
+ }
}
#endif
@@ -640,7 +672,7 @@ static enum pv_onplay_result onplay_menu(int index)
/* View current playlist */
enum playlist_viewer_result playlist_viewer(void)
{
- return playlist_viewer_ex(NULL);
+ return playlist_viewer_ex(NULL, NULL);
}
static int get_track_num(struct playlist_viewer *local_viewer,
@@ -748,7 +780,7 @@ static void update_lists(struct gui_synclist * playlist_lists)
gui_synclist_set_icon_callback(playlist_lists,
global_settings.playlist_viewer_icons?
&playlist_callback_icons:NULL);
- gui_synclist_set_title(playlist_lists, str(LANG_PLAYLIST), Icon_Playlist);
+ gui_synclist_set_title(playlist_lists, viewer.title, Icon_Playlist);
gui_synclist_draw(playlist_lists);
gui_synclist_speak_item(playlist_lists);
}
@@ -785,7 +817,7 @@ static void prepare_lists(struct gui_synclist * playlist_lists)
global_settings.playlist_viewer_icons ?
&playlist_callback_icons : NULL);
gui_synclist_set_nb_items(playlist_lists, viewer.num_tracks);
- gui_synclist_set_title(playlist_lists, str(LANG_PLAYLIST), Icon_Playlist);
+ gui_synclist_set_title(playlist_lists, viewer.title, Icon_Playlist);
gui_synclist_select_item(playlist_lists, viewer.selected_track);
gui_synclist_draw(playlist_lists);
gui_synclist_speak_item(playlist_lists);
@@ -793,11 +825,11 @@ static void prepare_lists(struct gui_synclist * playlist_lists)
static bool open_playlist_viewer(const char* filename,
struct gui_synclist *playlist_lists,
- bool reload)
+ bool reload, int *most_recent_selection)
{
push_current_activity(ACTIVITY_PLAYLISTVIEWER);
- if (!playlist_viewer_init(&viewer, filename, reload))
+ if (!playlist_viewer_init(&viewer, filename, reload, most_recent_selection))
return false;
prepare_lists(playlist_lists);
@@ -807,15 +839,19 @@ static bool open_playlist_viewer(const char* filename,
/* Main viewer function. Filename identifies playlist to be viewed. If NULL,
view current playlist. */
-enum playlist_viewer_result playlist_viewer_ex(const char* filename)
+enum playlist_viewer_result playlist_viewer_ex(const char* filename,
+ int* most_recent_selection)
{
enum playlist_viewer_result ret = PLAYLIST_VIEWER_OK;
bool exit = false; /* exit viewer */
int button;
struct gui_synclist playlist_lists;
- if (!open_playlist_viewer(filename, &playlist_lists, false))
+ if (!open_playlist_viewer(filename, &playlist_lists, false, most_recent_selection))
+ {
+ ret = PLAYLIST_VIEWER_CANCEL;
goto exit;
+ }
while (!exit)
{
@@ -842,8 +878,7 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
}
/* Timeout so we can determine if play status has changed */
- bool res = list_do_action(CONTEXT_TREE, HZ/2,
- &playlist_lists, &button, LIST_WRAP_UNLESS_HELD);
+ bool res = list_do_action(CONTEXT_TREE, HZ/2, &playlist_lists, &button);
/* during moving, another redraw is going to be needed,
* since viewer.selected_track is updated too late (after the first draw)
* drawing the moving item needs it */
@@ -876,7 +911,8 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
else
{
exit = true;
- ret = PLAYLIST_VIEWER_CANCEL;
+ ret = button == ACTION_TREE_WPS ?
+ PLAYLIST_VIEWER_OK : PLAYLIST_VIEWER_CANCEL;
}
break;
}
@@ -930,8 +966,11 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
start_index = playlist_shuffle(current_tick, start_index);
playlist_start(start_index, 0, 0);
+ if (viewer.initial_selection)
+ *(viewer.initial_selection) = viewer.selected_track;
+
/* Our playlist is now the current list */
- if (!playlist_viewer_init(&viewer, NULL, true))
+ if (!playlist_viewer_init(&viewer, NULL, true, NULL))
goto exit;
exit = true;
}
@@ -951,13 +990,22 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
}
else if (pv_onplay_result == PV_ONPLAY_USB_CLOSED)
return PLAYLIST_VIEWER_USB;
+ else if (pv_onplay_result == PV_ONPLAY_WPS_CLOSED)
+ return PLAYLIST_VIEWER_OK;
else if (pv_onplay_result == PV_ONPLAY_CLOSED)
{
- if (!open_playlist_viewer(filename, &playlist_lists, true))
+ if (!open_playlist_viewer(filename, &playlist_lists, true, NULL))
+ {
+ ret = PLAYLIST_VIEWER_CANCEL;
goto exit;
+ }
break;
}
- exit = update_viewer_with_changes(&playlist_lists, pv_onplay_result);
+ if (update_viewer_with_changes(&playlist_lists, pv_onplay_result))
+ {
+ exit = true;
+ ret = PLAYLIST_VIEWER_CANCEL;
+ }
break;
}
case ACTION_STD_MENU:
@@ -970,8 +1018,8 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
quick_screen_quick(button);
update_playlist(true);
update_lists(&playlist_lists);
- break;
}
+ break;
#endif
#ifdef HAVE_HOTKEY
case ACTION_TREE_HOTKEY:
@@ -989,10 +1037,17 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
if (do_plugin != NULL)
{
- if (do_plugin(current_track) == PV_ONPLAY_USB_CLOSED)
+ int plugin_result = do_plugin(current_track);
+
+ if (plugin_result == PV_ONPLAY_USB_CLOSED)
return PLAYLIST_VIEWER_USB;
- else if (!open_playlist_viewer(filename, &playlist_lists, true))
+ else if (plugin_result == PV_ONPLAY_WPS_CLOSED)
+ return PLAYLIST_VIEWER_OK;
+ else if (!open_playlist_viewer(filename, &playlist_lists, true, NULL))
+ {
+ ret = PLAYLIST_VIEWER_CANCEL;
goto exit;
+ }
}
else if (global_settings.hotkey_tree == HOTKEY_PROPERTIES)
{
@@ -1004,10 +1059,16 @@ enum playlist_viewer_result playlist_viewer_ex(const char* filename)
update_lists(&playlist_lists);
}
else if (global_settings.hotkey_tree == HOTKEY_DELETE)
- exit = update_viewer_with_changes(&playlist_lists,
+ {
+ if (update_viewer_with_changes(&playlist_lists,
delete_track(current_track->index,
viewer.selected_track,
- (current_track->index == viewer.current_playing_track)));
+ (current_track->index == viewer.current_playing_track))))
+ {
+ ret = PLAYLIST_VIEWER_CANCEL;
+ exit = true;
+ }
+ }
else
onplay(current_track->name, FILE_ATTR_AUDIO, CONTEXT_STD, true);
break;
@@ -1034,6 +1095,9 @@ static void close_playlist_viewer(void)
pop_current_activity();
if (viewer.playlist)
{
+ if (viewer.initial_selection)
+ *(viewer.initial_selection) = viewer.selected_track;
+
if(dirty && yesno_pop(ID2P(LANG_SAVE_CHANGES)))
save_playlist_screen(viewer.playlist);
playlist_close(viewer.playlist);
@@ -1074,7 +1138,7 @@ bool search_playlist(void)
struct gui_synclist playlist_lists;
struct playlist_track_info track;
- if (!playlist_viewer_init(&viewer, 0, false))
+ if (!playlist_viewer_init(&viewer, 0, false, NULL))
return ret;
if (kbd_input(search_str, sizeof(search_str), NULL) < 0)
return ret;
@@ -1129,8 +1193,7 @@ bool search_playlist(void)
gui_synclist_speak_item(&playlist_lists);
while (!exit)
{
- if (list_do_action(CONTEXT_LIST, HZ/4,
- &playlist_lists, &button, LIST_WRAP_UNLESS_HELD))
+ if (list_do_action(CONTEXT_LIST, HZ/4, &playlist_lists, &button))
continue;
switch (button)
{
diff --git a/apps/playlist_viewer.h b/apps/playlist_viewer.h
index 0a54c1b2cd..2bb1336f4f 100644
--- a/apps/playlist_viewer.h
+++ b/apps/playlist_viewer.h
@@ -24,7 +24,8 @@
#define _PLAYLIST_VIEWER_H_
enum playlist_viewer_result playlist_viewer(void);
-enum playlist_viewer_result playlist_viewer_ex(const char* filename);
+enum playlist_viewer_result playlist_viewer_ex(const char* filename,
+ int* most_recent_selection);
bool search_playlist(void);
enum playlist_viewer_result {
diff --git a/apps/plugin.c b/apps/plugin.c
index eb76eb7753..128b52ea2d 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -341,7 +341,6 @@ static const struct plugin_api rockbox_api = {
gui_synclist_select_item,
gui_synclist_add_item,
gui_synclist_del_item,
- gui_synclist_limit_scroll,
gui_synclist_do_button,
gui_synclist_set_title,
gui_syncyesno_run,
@@ -357,6 +356,7 @@ static const struct plugin_api rockbox_api = {
action_get_touchscreen_press_in_vp,
#endif
action_userabort,
+ core_set_keyremap,
/* button */
button_get,
@@ -436,6 +436,8 @@ static const struct plugin_api rockbox_api = {
tree_get_entry_at,
set_current_file,
set_dirfilter,
+ onplay_show_playlist_menu,
+ browse_id3,
/* talking */
talk_id,
@@ -519,6 +521,7 @@ static const struct plugin_api rockbox_api = {
queue_wait,
queue_send,
queue_reply,
+ queue_remove_from_head,
#ifdef RB_PROFILE
profile_thread,
@@ -688,7 +691,9 @@ static const struct plugin_api rockbox_api = {
playlist_create,
playlist_insert_track,
playlist_insert_directory,
+ playlist_insert_playlist,
playlist_shuffle,
+ warn_on_pl_erase,
audio_play,
audio_stop,
audio_pause,
@@ -731,6 +736,7 @@ static const struct plugin_api rockbox_api = {
battery_level_safe,
battery_time,
battery_voltage,
+ battery_current,
#if CONFIG_CHARGING
charger_inserted,
# if CONFIG_CHARGING >= CHARGING_MONITOR
@@ -801,21 +807,24 @@ static const struct plugin_api rockbox_api = {
plugin_release_audio_buffer, /* defined in plugin.c */
plugin_tsr, /* defined in plugin.c */
plugin_get_current_filename,
- /* new stuff at the end, sort into place next time
- the API gets incompatible */
- warn_on_pl_erase,
- playlist_insert_playlist,
- battery_current,
- onplay_show_playlist_menu,
- queue_remove_from_head,
- core_set_keyremap,
plugin_reserve_buffer,
+
+ /* reboot and poweroff */
+ sys_poweroff,
+ sys_reboot,
+
+ /* pathfuncs */
#ifdef HAVE_MULTIVOLUME
path_strip_volume,
#endif
- sys_poweroff,
- sys_reboot,
- browse_id3,
+
+ /* new stuff at the end, sort into place next time
+ the API gets incompatible */
+#ifdef HAVE_TAGCACHE
+#ifdef HAVE_TC_RAMCACHE
+ tagcache_is_in_ram,
+#endif
+#endif
};
static int plugin_buffer_handle;
@@ -835,8 +844,7 @@ int plugin_load(const char* plugin, const void* parameter)
}
lc_close(current_plugin_handle);
current_plugin_handle = pfn_tsr_exit = NULL;
- if (plugin_buffer_handle > 0)
- plugin_buffer_handle = core_free(plugin_buffer_handle);
+ plugin_buffer_handle = core_free(plugin_buffer_handle);
}
splash(0, ID2P(LANG_WAIT));
@@ -917,8 +925,7 @@ int plugin_load(const char* plugin, const void* parameter)
{ /* close handle if plugin is no tsr one */
lc_close(current_plugin_handle);
current_plugin_handle = NULL;
- if (plugin_buffer_handle > 0)
- plugin_buffer_handle = core_free(plugin_buffer_handle);
+ plugin_buffer_handle = core_free(plugin_buffer_handle);
}
talk_buffer_set_policy(TALK_BUFFER_DEFAULT);
diff --git a/apps/plugin.h b/apps/plugin.h
index a487f64168..e2a821bb57 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -157,12 +157,12 @@ int plugin_open(const char *plugin, const char *parameter);
#define PLUGIN_MAGIC 0x526F634B /* RocK */
/* increase this every time the api struct changes */
-#define PLUGIN_API_VERSION 251
+#define PLUGIN_API_VERSION 256
/* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any
new function which are "waiting" at the end of the function table) */
-#define PLUGIN_MIN_API_VERSION 245
+#define PLUGIN_MIN_API_VERSION 255
/* 239 Marks the removal of ARCHOS HWCODEC and CHARCELL */
@@ -384,10 +384,8 @@ struct plugin_api {
int item_number);
void (*gui_synclist_add_item)(struct gui_synclist * lists);
void (*gui_synclist_del_item)(struct gui_synclist * lists);
- void (*gui_synclist_limit_scroll)(struct gui_synclist * lists, bool scroll);
- bool (*gui_synclist_do_button)(struct gui_synclist * lists,
- int *action, enum list_wrap wrap);
- void (*gui_synclist_set_title)(struct gui_synclist *lists, char* title,
+ bool (*gui_synclist_do_button)(struct gui_synclist * lists, int *action);
+ void (*gui_synclist_set_title)(struct gui_synclist *lists, const char* title,
enum themable_icons icon);
enum yesno_res (*gui_syncyesno_run)(const struct text_message * main_message,
const struct text_message * yes_message,
@@ -406,6 +404,7 @@ struct plugin_api {
int (*action_get_touchscreen_press_in_vp)(short *x1, short *y1, struct viewport *vp);
#endif
bool (*action_userabort)(int timeout);
+ int (*core_set_keyremap)(struct button_mapping* core_keymap, int count);
/* button */
long (*button_get)(bool block);
@@ -492,6 +491,10 @@ struct plugin_api {
void (*set_current_file)(const char* path);
void (*set_dirfilter)(int l_dirfilter);
+ void (*onplay_show_playlist_menu)(const char* path, void (*playlist_insert_cb));
+ bool (*browse_id3)(struct mp3entry *id3,
+ int playlist_display_index, int playlist_amount);
+
/* talking */
int (*talk_id)(int32_t id, bool enqueue);
int (*talk_file)(const char *root, const char *dir, const char *file,
@@ -588,6 +591,7 @@ struct plugin_api {
intptr_t (*queue_send)(struct event_queue *q, long id,
intptr_t data);
void (*queue_reply)(struct event_queue *q, intptr_t retval);
+ void (*queue_remove_from_head)(struct event_queue *q, long id);
#ifdef RB_PROFILE
void (*profile_thread)(void);
@@ -790,7 +794,10 @@ struct plugin_api {
int (*playlist_insert_directory)(struct playlist_info* playlist,
const char *dirname, int position, bool queue,
bool recurse);
+ int (*playlist_insert_playlist)(struct playlist_info* playlist,
+ const char *filename, int position, bool queue);
int (*playlist_shuffle)(int random_seed, int start_index);
+ bool (*warn_on_pl_erase)(void);
void (*audio_play)(unsigned long elapsed, unsigned long offset);
void (*audio_stop)(void);
void (*audio_pause)(void);
@@ -850,6 +857,7 @@ struct plugin_api {
bool (*battery_level_safe)(void);
int (*battery_time)(void);
int (*battery_voltage)(void);
+ int (*battery_current)(void);
#if CONFIG_CHARGING
bool (*charger_inserted)(void);
# if CONFIG_CHARGING >= CHARGING_MONITOR
@@ -928,23 +936,21 @@ struct plugin_api {
void (*plugin_release_audio_buffer)(void);
void (*plugin_tsr)(bool (*exit_callback)(bool reenter));
char* (*plugin_get_current_filename)(void);
- /* new stuff at the end, sort into place next time
- the API gets incompatible */
- bool (*warn_on_pl_erase)(void);
- int (*playlist_insert_playlist)(struct playlist_info* playlist,
- const char *filename, int position, bool queue);
- int (*battery_current)(void);
- void (*onplay_show_playlist_menu)(const char* path, void (*playlist_insert_cb));
- void (*queue_remove_from_head)(struct event_queue *q, long id);
- int (*core_set_keyremap)(struct button_mapping* core_keymap, int count);
size_t (*plugin_reserve_buffer)(size_t buffer_size);
+ /* reboot and poweroff */
+ void (*sys_poweroff)(void);
+ void (*sys_reboot)(void);
+ /* pathfuncs */
#ifdef HAVE_MULTIVOLUME
int (*path_strip_volume)(const char *name, const char **nameptr, bool greedy);
#endif
- void (*sys_poweroff)(void);
- void (*sys_reboot)(void);
- bool (*browse_id3)(struct mp3entry *id3,
- int playlist_display_index, int playlist_amount);
+ /* new stuff at the end, sort into place next time
+ the API gets incompatible */
+#ifdef HAVE_TAGCACHE
+#ifdef HAVE_TC_RAMCACHE
+ bool (*tagcache_is_in_ram)(void);
+#endif
+#endif
};
/* plugin header */
diff --git a/apps/plugins/2048.c b/apps/plugins/2048.c
index 2633753071..456f973aef 100644
--- a/apps/plugins/2048.c
+++ b/apps/plugins/2048.c
@@ -148,9 +148,7 @@ static inline int rand_range(int min, int max)
/* prepares for exit */
static void cleanup(void)
{
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
}
/* returns 2 or 4 */
@@ -700,9 +698,8 @@ static void init_game(bool newgame)
max_numeral_width = rb->font_get_width(rb->font_get(WHAT_FONT), '0');
#endif
-#ifdef HAVE_BACKLIGHT
backlight_ignore_timeout();
-#endif
+
draw();
}
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 942f9a5b20..85227d80d7 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -82,8 +82,7 @@ crypt_firmware.c
/* Overlays loaders */
-#if defined(HAVE_LCD_COLOR) && \
- (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE))
+#if defined(HAVE_LCD_COLOR) && (LCD_STRIDEFORMAT == HORIZONTAL_STRIDE)
#if (PLUGIN_BUFFER_SIZE > 0x14000) && (CONFIG_PLATFORM & (PLATFORM_NATIVE |PLATFORM_HOSTED)) && (defined(CPU_ARM) || defined(CPU_MIPS))
duke3d.c
quake.c
diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index 8479e4b3dd..4cb57edb1b 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -9,8 +9,7 @@ clock
#endif
/* color horizontal-stride LCDs */
-#if defined(HAVE_LCD_COLOR) && \
- (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE))
+#if defined(HAVE_LCD_COLOR) && (LCD_STRIDEFORMAT == HORIZONTAL_STRIDE)
xworld
/* for duke3d, wolf3d and quake */
diff --git a/apps/plugins/bitmaps/native/SOURCES b/apps/plugins/bitmaps/native/SOURCES
index f207f358b2..d0cc853f5b 100644
--- a/apps/plugins/bitmaps/native/SOURCES
+++ b/apps/plugins/bitmaps/native/SOURCES
@@ -548,8 +548,7 @@ pegbox_pieces.9x7x1.bmp
#endif
/* Puzzles */
-#if defined(HAVE_LCD_COLOR) && \
- (!defined(LCD_STRIDEFORMAT) || (LCD_STRIDEFORMAT != VERTICAL_STRIDE))
+#if defined(HAVE_LCD_COLOR) && (LCD_STRIDEFORMAT == HORIZONTAL_STRIDE)
puzzles_cursor.11x16x24.bmp
#endif
diff --git a/apps/plugins/brickmania.c b/apps/plugins/brickmania.c
index 4983d5a417..de65ce69e1 100644
--- a/apps/plugins/brickmania.c
+++ b/apps/plugins/brickmania.c
@@ -111,8 +111,7 @@
#elif CONFIG_KEYPAD == SANSA_C200_PAD || \
CONFIG_KEYPAD == SANSA_CLIP_PAD || \
-CONFIG_KEYPAD == SANSA_M200_PAD || \
-CONFIG_KEYPAD == SANSA_CONNECT_PAD
+CONFIG_KEYPAD == SANSA_M200_PAD
#define QUIT BUTTON_POWER
#define LEFT BUTTON_LEFT
#define RIGHT BUTTON_RIGHT
@@ -122,6 +121,18 @@ CONFIG_KEYPAD == SANSA_CONNECT_PAD
#define UP BUTTON_UP
#define DOWN BUTTON_DOWN
+#elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
+#define QUIT BUTTON_POWER
+#define LEFT BUTTON_LEFT
+#define RIGHT BUTTON_RIGHT
+#define SELECT BUTTON_SELECT
+#define UP BUTTON_UP
+#define DOWN BUTTON_DOWN
+
+#define SCROLL_FWD(x) ((x) & BUTTON_SCROLL_FWD)
+#define SCROLL_BACK(x) ((x) & BUTTON_SCROLL_BACK)
+
+
#elif CONFIG_KEYPAD == IRIVER_H10_PAD
#define QUIT BUTTON_POWER
#define LEFT BUTTON_LEFT
@@ -2522,10 +2533,10 @@ enum plugin_status plugin_start(const void* parameter)
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
/* now go ahead and have fun! */
rb->srand( *rb->current_tick );
brickmania_loadgame();
@@ -2554,9 +2565,9 @@ enum plugin_status plugin_start(const void* parameter)
configfile_save(CONFIG_FILE_NAME,config,1,0);
/* Restore user's original backlight setting */
rb->lcd_setfont(FONT_UI);
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return PLUGIN_OK;
}
diff --git a/apps/plugins/calendar.c b/apps/plugins/calendar.c
index 3299a81273..a70f47c34b 100644
--- a/apps/plugins/calendar.c
+++ b/apps/plugins/calendar.c
@@ -964,7 +964,7 @@ static bool view_events(int selected, struct shown *shown)
while (!exit)
{
button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
- rb->gui_synclist_do_button(&gui_memos, &button, LIST_WRAP_UNLESS_HELD);
+ rb->gui_synclist_do_button(&gui_memos, &button);
switch (button)
{
diff --git a/apps/plugins/chessbox/chessbox_pgn.c b/apps/plugins/chessbox/chessbox_pgn.c
index 0d9da441b1..bb35bec726 100644
--- a/apps/plugins/chessbox/chessbox_pgn.c
+++ b/apps/plugins/chessbox/chessbox_pgn.c
@@ -678,7 +678,6 @@ struct pgn_game_node* pgn_show_game_list(struct pgn_game_node* first_game){
if (rb->global_settings->talk_menu)
rb->gui_synclist_set_voice_callback(&games_list, speak_game_selection);
rb->gui_synclist_set_nb_items(&games_list, i);
- rb->gui_synclist_limit_scroll(&games_list, true);
rb->gui_synclist_select_item(&games_list, 0);
rb->gui_synclist_draw(&games_list);
@@ -687,9 +686,8 @@ struct pgn_game_node* pgn_show_game_list(struct pgn_game_node* first_game){
while (true) {
curr_selection = rb->gui_synclist_get_sel_pos(&games_list);
button = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&games_list,&button,LIST_WRAP_OFF)){
+ if (rb->gui_synclist_do_button(&games_list, &button))
continue;
- }
switch (button) {
case ACTION_STD_OK:
return get_game_info(curr_selection, first_game);
diff --git a/apps/plugins/chopper.c b/apps/plugins/chopper.c
index 392b840317..5c87e74e50 100644
--- a/apps/plugins/chopper.c
+++ b/apps/plugins/chopper.c
@@ -1083,10 +1083,10 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_set_foreground(LCD_WHITE);
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
rb->srand( *rb->current_tick );
@@ -1098,10 +1098,10 @@ enum plugin_status plugin_start(const void* parameter)
configfile_save(CFG_FILE, config, 1, 0);
rb->lcd_setfont(FONT_UI);
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return ret;
}
diff --git a/apps/plugins/credits.c b/apps/plugins/credits.c
index 9e43aab2a7..efdcf42df1 100644
--- a/apps/plugins/credits.c
+++ b/apps/plugins/credits.c
@@ -303,10 +303,10 @@ enum plugin_status plugin_start(const void* parameter)
{
(void)parameter;
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
#if LCD_DEPTH>=16
rb->lcd_set_foreground (LCD_WHITE);
@@ -318,10 +318,10 @@ enum plugin_status plugin_start(const void* parameter)
if(!rb->action_userabort(3*HZ))
roll_credits();
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return PLUGIN_OK;
}
diff --git a/apps/plugins/demystify.c b/apps/plugins/demystify.c
index 74537e94f8..1a1505ff96 100644
--- a/apps/plugins/demystify.c
+++ b/apps/plugins/demystify.c
@@ -262,12 +262,10 @@ static void polygons_draw(struct polygon_fifo * polygons, struct screen * displa
static void cleanup(void)
{
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#ifdef HAVE_REMOTE_LCD
remote_backlight_use_settings();
-#endif
-#endif
+
}
#ifdef HAVE_LCD_COLOR
@@ -438,12 +436,10 @@ enum plugin_status plugin_start(const void* parameter)
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#ifdef HAVE_REMOTE_LCD
remote_backlight_ignore_timeout();
-#endif
-#endif
+
ret = plugin_main();
return ret;
diff --git a/apps/plugins/doom/i_video.c b/apps/plugins/doom/i_video.c
index f5d07a8354..79f3212467 100644
--- a/apps/plugins/doom/i_video.c
+++ b/apps/plugins/doom/i_video.c
@@ -453,6 +453,9 @@ void I_ShutdownGraphics(void)
#define DOOMBUTTON_MAP BUTTON_BOTTOMRIGHT
#elif CONFIG_KEYPAD == SANSA_CONNECT_PAD
+#define DOOMBUTTON_SCROLLWHEEL
+#define DOOMBUTTON_SCROLLWHEEL_CC BUTTON_SCROLL_BACK
+#define DOOMBUTTON_SCROLLWHEEL_CW BUTTON_SCROLL_FWD
#define DOOMBUTTON_UP BUTTON_UP
#define DOOMBUTTON_DOWN BUTTON_DOWN
#define DOOMBUTTON_LEFT BUTTON_LEFT
diff --git a/apps/plugins/doom/rockdoom.c b/apps/plugins/doom/rockdoom.c
index 6594859c08..b68107f8aa 100644
--- a/apps/plugins/doom/rockdoom.c
+++ b/apps/plugins/doom/rockdoom.c
@@ -722,9 +722,9 @@ enum plugin_status plugin_start(const void* parameter)
systemvol= rb->global_settings->volume-rb->global_settings->volume%mod;
general_translucency = default_translucency; // phares
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
+
#ifdef RB_PROFILE
rb->profile_thread();
#endif
@@ -738,9 +738,9 @@ enum plugin_status plugin_start(const void* parameter)
#ifdef RB_PROFILE
rb->profstop();
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
M_SaveDefaults ();
I_Quit(); // Make SURE everything was closed out right
diff --git a/apps/plugins/fft/fft.c b/apps/plugins/fft/fft.c
index 35498227bf..f612a0ea35 100644
--- a/apps/plugins/fft/fft.c
+++ b/apps/plugins/fft/fft.c
@@ -1184,9 +1184,8 @@ static void fft_cleanup(void)
#ifndef HAVE_LCD_COLOR
grey_release();
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
/* save settings if changed */
if (rb->memcmp(&fft, &fft_disk, sizeof(fft)))
@@ -1237,9 +1236,8 @@ static bool fft_setup(void)
mylcd_clear_display();
myosd_lcd_update();
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->trigger_cpu_boost();
diff --git a/apps/plugins/fire.c b/apps/plugins/fire.c
index f3e6fb35e4..43d10bf920 100644
--- a/apps/plugins/fire.c
+++ b/apps/plugins/fire.c
@@ -279,10 +279,9 @@ static void cleanup(void *parameter)
#ifndef HAVE_LCD_COLOR
grey_release();
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
}
@@ -371,10 +370,9 @@ enum plugin_status plugin_start(const void* parameter)
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
rb->lcd_set_mode(LCD_MODE_PAL256);
diff --git a/apps/plugins/fireworks.c b/apps/plugins/fireworks.c
index b7dad0d8ba..febd093e3f 100644
--- a/apps/plugins/fireworks.c
+++ b/apps/plugins/fireworks.c
@@ -358,9 +358,9 @@ enum plugin_status plugin_start(const void* parameter)
/* set everything up.. no BL timeout, no backdrop,
white-text-on-black-background. */
-#ifdef HAVE_BACKLIGHT
+
backlight_ignore_timeout();
-#endif
+
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
rb->lcd_set_background(LCD_BLACK);
@@ -524,10 +524,10 @@ enum plugin_status plugin_start(const void* parameter)
break;
}
}
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
diff --git a/apps/plugins/imageviewer/imageviewer.c b/apps/plugins/imageviewer/imageviewer.c
index e30a98ef68..a8ef3efc97 100644
--- a/apps/plugins/imageviewer/imageviewer.c
+++ b/apps/plugins/imageviewer/imageviewer.c
@@ -1064,10 +1064,8 @@ enum plugin_status plugin_start(const void* parameter)
ARRAYLEN(config), IMGVIEW_SETTINGS_MINVERSION);
rb->memcpy(&old_settings, &settings, sizeof (settings));
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
@@ -1094,10 +1092,8 @@ enum plugin_status plugin_start(const void* parameter)
rb->storage_spindown(rb->global_settings->disk_spindown);
#endif
-#ifdef HAVE_BACKLIGHT
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
#ifdef USEGSLIB
grey_release(); /* deinitialize */
diff --git a/apps/plugins/imageviewer/jpeg/yuv2rgb.c b/apps/plugins/imageviewer/jpeg/yuv2rgb.c
index 61d7fd6487..3e7f08d8bc 100644
--- a/apps/plugins/imageviewer/jpeg/yuv2rgb.c
+++ b/apps/plugins/imageviewer/jpeg/yuv2rgb.c
@@ -238,7 +238,7 @@ static fb_data (* const pixel_funcs[COLOUR_NUM_MODES][DITHER_NUM_MODES])(void) =
};
/* These defines are used fornormal horizontal strides and vertical strides. */
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#define LCDADDR(x, y) (lcd_fb + LCD_HEIGHT*(x) + (y))
#define ROWENDOFFSET (width*LCD_HEIGHT)
#define ROWOFFSET (1)
diff --git a/apps/plugins/imageviewer/ppm/ppm_decoder.c b/apps/plugins/imageviewer/ppm/ppm_decoder.c
index 4a86be1a3a..ccb208b80b 100644
--- a/apps/plugins/imageviewer/ppm/ppm_decoder.c
+++ b/apps/plugins/imageviewer/ppm/ppm_decoder.c
@@ -177,7 +177,7 @@ static int read_ppm_row(int fd, struct ppm_info *ppm, int row)
int col;
int r, g, b;
#ifdef HAVE_LCD_COLOR
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
fb_data *dst = (fb_data *) ppm->buf + row;
const int stride = ppm->x;
#else
diff --git a/apps/plugins/invadrox.c b/apps/plugins/invadrox.c
index a164b95cf0..d130ab6108 100644
--- a/apps/plugins/invadrox.c
+++ b/apps/plugins/invadrox.c
@@ -785,7 +785,7 @@ static fb_data *lcd_fb;
/* No standard get_pixel function yet, use this hack instead */
#if (LCD_DEPTH >= 8)
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
static inline fb_data get_pixel(int x, int y)
{
return lcd_fb[x*LCD_HEIGHT+y];
diff --git a/apps/plugins/keybox.c b/apps/plugins/keybox.c
index f8c6800a4d..cb2e23a94a 100644
--- a/apps/plugins/keybox.c
+++ b/apps/plugins/keybox.c
@@ -567,7 +567,7 @@ static int keybox(void)
{
rb->gui_synclist_draw(&kb_list);
button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&kb_list, &button, LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&kb_list, &button))
continue;
switch (button)
@@ -659,7 +659,6 @@ enum plugin_status plugin_start(const void *parameter)
rb->gui_synclist_set_title(&kb_list, "Keybox", NOICON);
rb->gui_synclist_set_icon_callback(&kb_list, NULL);
rb->gui_synclist_set_nb_items(&kb_list, 0);
- rb->gui_synclist_limit_scroll(&kb_list, false);
rb->gui_synclist_select_item(&kb_list, 0);
init_ll();
diff --git a/apps/plugins/keyremap.c b/apps/plugins/keyremap.c
index a4ce1c48e6..4fd792646e 100644
--- a/apps/plugins/keyremap.c
+++ b/apps/plugins/keyremap.c
@@ -391,7 +391,7 @@ static struct button_mapping *keyremap_create_temp(int *entries)
size_t keymap_bytes = (entry_count) * sizeof(struct button_mapping);
*entries = entry_count;
logf("keyremap create temp entry count: %d", entry_count);
- logf("keyremap bytes: %ld, avail: %ld", keymap_bytes,
+ logf("keyremap bytes: %zu, avail: %zu", keymap_bytes,
(keyremap_buffer.end - keyremap_buffer.front));
if (keyremap_buffer.front + keymap_bytes < keyremap_buffer.end)
{
@@ -1950,7 +1950,6 @@ static void synclist_set(int id, int selected_item, int items, int sel_size)
rb->gui_synclist_set_icon_callback(&lists,NULL);
rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
rb->gui_synclist_set_nb_items(&lists,items);
- rb->gui_synclist_limit_scroll(&lists,true);
rb->gui_synclist_select_item(&lists, selected_item);
printcell_enable(&lists, false, false);
@@ -2055,7 +2054,7 @@ enum plugin_status plugin_start(const void* parameter)
redraw = true;
ret = menu_action_cb(&action, selected_item, &exit, &lists);
- if (rb->gui_synclist_do_button(&lists,&action,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&lists, &action))
continue;
selected_item = rb->gui_synclist_get_sel_pos(&lists);
diff --git a/apps/plugins/lamp.c b/apps/plugins/lamp.c
index 6c9ae6626d..42a2353b2a 100644
--- a/apps/plugins/lamp.c
+++ b/apps/plugins/lamp.c
@@ -103,9 +103,8 @@ enum plugin_status plugin_start(const void* parameter)
int current_brightness = MAX_BRIGHTNESS_SETTING;
backlight_brightness_set(MAX_BRIGHTNESS_SETTING);
#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
-#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
+
buttonlight_brightness_set(MAX_BRIGHTNESS_SETTING);
-#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
#ifdef HAVE_LCD_INVERT
#ifdef HAVE_NEGATIVE_LCD
@@ -116,9 +115,8 @@ enum plugin_status plugin_start(const void* parameter)
#endif /* HAVE_LCD_INVERT */
backlight_force_on();
-#ifdef HAVE_BUTTON_LIGHT
buttonlight_force_on();
-#endif /* HAVE_BUTTON_LIGHT */
+
rb->lcd_clear_display();
rb->lcd_update();
@@ -207,20 +205,17 @@ enum plugin_status plugin_start(const void* parameter)
/* restore */
backlight_use_settings();
-#ifdef HAVE_BUTTON_LIGHT
+
buttonlight_use_settings();
-#endif /* HAVE_BUTTON_LIGHT */
#ifdef HAVE_LCD_INVERT
rb->lcd_set_invert_display(rb->global_settings->invert);
#endif /* HAVE_LCD_INVERT */
-#ifdef HAVE_BACKLIGHT_BRIGHTNESS
+
backlight_brightness_use_setting();
-#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
-#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
+
buttonlight_brightness_use_setting();
-#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
#if LCD_DEPTH > 1
rb->lcd_set_background(bg_color);
diff --git a/apps/plugins/lib/bmp_smooth_scale.c b/apps/plugins/lib/bmp_smooth_scale.c
index c5f258cdbf..378ff96448 100644
--- a/apps/plugins/lib/bmp_smooth_scale.c
+++ b/apps/plugins/lib/bmp_smooth_scale.c
@@ -78,7 +78,7 @@ void smooth_resize_bitmap(struct bitmap *src_bmp, struct bitmap *dest_bmp)
fb_data *sptr, *dptr;
int x, y, end;
int val_y = 0, val_x;
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
const int sw = src_bmp->height;
const int sh = src_bmp->width;
const int dw = dest_bmp->height;
diff --git a/apps/plugins/lib/helper.c b/apps/plugins/lib/helper.c
index 018c1616c8..92d9ec905e 100644
--- a/apps/plugins/lib/helper.c
+++ b/apps/plugins/lib/helper.c
@@ -58,7 +58,12 @@ void backlight_use_settings(void)
backlight_timeout_plugged);
#endif /* CONFIG_CHARGING */
}
-#endif /* HAVE_BACKLIGHT */
+#else /* HAVE_BACKLIGHT */
+/* DUMMY FUNCTIONS */
+void backlight_force_on(void){}
+void backlight_ignore_timeout(void){}
+void backlight_use_settings(void){}
+#endif /* !HAVE_BACKLIGHT */
#ifdef HAVE_SW_POWEROFF
static bool original_sw_poweroff_state = true;
@@ -73,7 +78,11 @@ void sw_poweroff_restore(void)
{
rb->button_set_sw_poweroff_state(original_sw_poweroff_state);
}
-#endif
+#else /* HAVE_SW_POWEROFF */
+/* DUMMY FUNCTIONS */
+void sw_poweroff_disable(void){}
+void sw_poweroff_restore(void){}
+#endif /* !HAVE_SW_POWEROFF */
#ifdef HAVE_REMOTE_LCD
/* Force the backlight on */
@@ -106,7 +115,12 @@ void remote_backlight_use_settings(void)
remote_backlight_timeout_plugged);
#endif /* CONFIG_CHARGING */
}
-#endif /* HAVE_REMOTE_LCD */
+#else /* HAVE_REMOTE_LCD */
+/* DUMMY FUNCTIONS */
+void remote_backlight_force_on(void){}
+void remote_backlight_ignore_timeout(void){}
+void remote_backlight_use_settings(void){}
+#endif /* !HAVE_REMOTE_LCD */
#ifdef HAVE_BUTTON_LIGHT
/* Force the buttonlight on */
@@ -133,7 +147,13 @@ void buttonlight_use_settings(void)
{
rb->buttonlight_set_timeout(rb->global_settings->buttonlight_timeout);
}
-#endif /* HAVE_BUTTON_LIGHT */
+#else /* HAVE_BUTTON_LIGHT */
+/* DUMMY FUNCTIONS */
+void buttonlight_force_on(void){}
+void buttonlight_force_off(void){}
+void buttonlight_ignore_timeout(void){}
+void buttonlight_use_settings(void){}
+#endif /* !HAVE_BUTTON_LIGHT */
#ifdef HAVE_BACKLIGHT_BRIGHTNESS
void backlight_brightness_set(int brightness)
@@ -145,7 +165,15 @@ void backlight_brightness_use_setting(void)
{
rb->backlight_set_brightness(rb->global_settings->brightness);
}
-#endif /* HAVE_BACKLIGHT_BRIGHTNESS */
+#else /* HAVE_BACKLIGHT_BRIGHTNESS */
+/* DUMMY FUNCTIONS */
+void backlight_brightness_set(int brightness)
+{
+ (void)brightness;
+}
+void backlight_brightness_use_setting(void){}
+
+#endif /* !HAVE_BACKLIGHT_BRIGHTNESS */
#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
void buttonlight_brightness_set(int brightness)
@@ -157,4 +185,12 @@ void buttonlight_brightness_use_setting(void)
{
rb->buttonlight_set_brightness(rb->global_settings->buttonlight_brightness);
}
-#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
+#else /* HAVE_BUTTONLIGHT_BRIGHTNESS */
+/* DUMMY FUNCTIONS */
+void buttonlight_brightness_set(int brightness)
+{
+ (void)brightness;
+}
+
+void buttonlight_brightness_use_setting(void){}
+#endif /* !HAVE_BUTTONLIGHT_BRIGHTNESS */
diff --git a/apps/plugins/lib/helper.h b/apps/plugins/lib/helper.h
index 00ad8ac087..6aee4dc581 100644
--- a/apps/plugins/lib/helper.h
+++ b/apps/plugins/lib/helper.h
@@ -23,6 +23,16 @@
#include "plugin.h"
+#ifndef MAX_BRIGHTNESS_SETTING
+#define MAX_BRIGHTNESS_SETTING 0
+#endif
+#ifndef MIN_BRIGHTNESS_SETTING
+#define MIN_BRIGHTNESS_SETTING 0
+#endif
+#ifndef DEFAULT_BRIGHTNESS_SETTING
+#define DEFAULT_BRIGHTNESS_SETTING 0
+#endif
+
int talk_val(long n, int unit, bool enqueue);
/**
@@ -32,39 +42,29 @@ void backlight_force_on(void);
void backlight_ignore_timeout(void);
void backlight_use_settings(void);
-#ifdef HAVE_SW_POWEROFF
/**
* Disable and restore software poweroff (i.e. holding PLAY on iPods).
* Only call _restore() if _disable() was called earlier!
*/
void sw_poweroff_disable(void);
void sw_poweroff_restore(void);
-#endif
-#ifdef HAVE_REMOTE_LCD
void remote_backlight_force_on(void);
void remote_backlight_ignore_timeout(void);
void remote_backlight_use_settings(void);
-#endif
-#ifdef HAVE_BUTTON_LIGHT
void buttonlight_force_on(void);
void buttonlight_force_off(void);
void buttonlight_ignore_timeout(void);
void buttonlight_use_settings(void);
-#endif
/**
* Backlight brightness adjustment settings
*/
-#ifdef HAVE_BACKLIGHT_BRIGHTNESS
void backlight_brightness_set(int brightness);
void backlight_brightness_use_setting(void);
-#endif
-#ifdef HAVE_BUTTONLIGHT_BRIGHTNESS
void buttonlight_brightness_set(int brightness);
void buttonlight_brightness_use_setting(void);
-#endif /* HAVE_BUTTONLIGHT_BRIGHTNESS */
#endif /* _LIB_HELPER_H_ */
diff --git a/apps/plugins/lib/osd.c b/apps/plugins/lib/osd.c
index 7d6e10a410..99f77da7dc 100644
--- a/apps/plugins/lib/osd.c
+++ b/apps/plugins/lib/osd.c
@@ -106,10 +106,10 @@ static struct osd grey_osd;
# error Unknown 2-bit format; please define macros
# endif /* LCD_PIXELFORMAT */
#elif LCD_DEPTH == 16
-# if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+# if LCD_STRIDEFORMAT == VERTICAL_STRIDE
# define _OSD_HEIGHT2BYTES(h) ((h)*2)
# define _OSD_BYTES2HEIGHT(b) ((b)/2)
-# else /* !defined(LCD_STRIDEFORMAT) || LCD_STRIDEFORMAT != VERTICAL_STRIDE */
+# else /* LCD_STRIDEFORMAT != VERTICAL_STRIDE */
# define _OSD_WIDTH2BYTES(w) ((w)*2)
# define _OSD_BYTES2WIDTH(b) ((b)/2)
# endif /* end stride type selection */
@@ -160,7 +160,7 @@ static void * _osd_lcd_init_buffers(struct osd *osd, unsigned flags,
rb->viewport_set_fullscreen(&osd->vp, SCREEN_MAIN);
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
int colbytes = _OSD_HEIGHT2BYTES(LCD_HEIGHT);
int bytecols = *bufsize / colbytes;
int w = _OSD_BYTES2WIDTH(bytecols);
@@ -193,7 +193,7 @@ static void * _osd_lcd_init_buffers(struct osd *osd, unsigned flags,
w = _OSD_BYTES2WIDTH(_OSD_WIDTH2BYTES(w));
osd->lcd_bitmap_stride = _OSD_BYTES2HEIGHT(_OSD_HEIGHT2BYTES(LCD_HEIGHT));
osd->back_bitmap_stride = h;
-#else /* !defined(LCD_STRIDEFORMAT) || LCD_STRIDEFORMAT != VERTICAL_STRIDE */
+#else /* LCD_STRIDEFORMAT != VERTICAL_STRIDE */
int rowbytes = _OSD_WIDTH2BYTES(LCD_WIDTH);
int byterows = *bufsize / rowbytes;
int w = _OSD_BYTES2WIDTH(rowbytes);
diff --git a/apps/plugins/lib/pluginlib_bmp.c b/apps/plugins/lib/pluginlib_bmp.c
index f3edfbf425..82f84b05af 100644
--- a/apps/plugins/lib/pluginlib_bmp.c
+++ b/apps/plugins/lib/pluginlib_bmp.c
@@ -94,7 +94,7 @@ int save_bmp_file( char* filename, struct bitmap *bm )
*/
void simple_resize_bitmap(struct bitmap *src, struct bitmap *dst)
{
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
const int srcw = src->height;
const int srch = src->width;
const int dstw = dst->height;
diff --git a/apps/plugins/lib/printcell_helper.c b/apps/plugins/lib/printcell_helper.c
index 328f74e318..f34636585e 100644
--- a/apps/plugins/lib/printcell_helper.c
+++ b/apps/plugins/lib/printcell_helper.c
@@ -234,7 +234,7 @@ static void printcell_listdraw_fn(struct list_putlineinfo_t *list_info)
bool show_cursor = list_info->show_cursor;
bool have_icons = list_info->have_icons;
struct line_desc *linedes = list_info->linedes;
- char *dsp_text = list_info->dsp_text;
+ const char *dsp_text = list_info->dsp_text;
struct viewport *vp = list_info->vp;
int line = list_info->line;
diff --git a/apps/plugins/lib/xlcd_scroll.c b/apps/plugins/lib/xlcd_scroll.c
index 5ac4a366e8..906f4eaae1 100644
--- a/apps/plugins/lib/xlcd_scroll.c
+++ b/apps/plugins/lib/xlcd_scroll.c
@@ -30,7 +30,7 @@
static const unsigned short patterns[4] = {0xFFFF, 0xFF00, 0x00FF, 0x0000};
#endif
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
void xlcd_scroll_left(int count)
{
/*size_t dst_stride;*/
@@ -668,4 +668,4 @@ void xlcd_scroll_down(int count)
}
#endif /* LCD_PIXELFORMAT, LCD_DEPTH */
-#endif /* defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE */
+#endif /* LCD_STRIDEFORMAT == VERTICAL_STRIDE */
diff --git a/apps/plugins/lrcplayer.c b/apps/plugins/lrcplayer.c
index f42b96b5b3..32d001add9 100644
--- a/apps/plugins/lrcplayer.c
+++ b/apps/plugins/lrcplayer.c
@@ -2078,8 +2078,7 @@ static int timetag_editor(void)
while (!exit)
{
button = rb->get_action(CONTEXT_TREE, TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&gui_editor, &button,
- LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&gui_editor, &button))
continue;
switch (button)
@@ -2437,7 +2436,6 @@ static bool lrc_debug_menu(void)
{
struct simplelist_info info;
rb->simplelist_info_init(&info, "Debug Menu", 6, NULL);
- info.hide_selection = true;
info.scroll_all = true;
info.get_name = lrc_debug_data;
return rb->simplelist_show_list(&info);
diff --git a/apps/plugins/lua/rocklib_events.c b/apps/plugins/lua/rocklib_events.c
index 1c13a6758f..0cdec20213 100644
--- a/apps/plugins/lua/rocklib_events.c
+++ b/apps/plugins/lua/rocklib_events.c
@@ -253,7 +253,9 @@ static int lua_rev_callback(lua_State *L, struct cb_data *evt)
lua_pushlightuserdata(L, evt->data);
lua_status = lua_resume(L, 2); /* call the saved function */
- if (lua_status == LUA_YIELD) /* coroutine.yield() disallowed */
+ if (lua_status == LUA_SUCCESS)
+ lua_settop(L, 0); /* eat any value(s) returned */
+ else if (lua_status == LUA_YIELD) /* coroutine.yield() disallowed */
luaL_where(L, 1); /* push error string on stack */
return lua_status;
diff --git a/apps/plugins/lua/rocklib_img.c b/apps/plugins/lua/rocklib_img.c
index b0ca769ca4..68e5325ce0 100644
--- a/apps/plugins/lua/rocklib_img.c
+++ b/apps/plugins/lua/rocklib_img.c
@@ -380,7 +380,7 @@ static inline fb_data* rli_get_element(struct rocklua_image* img, int x, int y)
pixel_to_native(x, y, &x, &y);
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
/* column major address */
size_t data_address = (stride * (x - 1)) + (y - 1);
diff --git a/apps/plugins/main_menu_config.c b/apps/plugins/main_menu_config.c
index 9f651094b1..a5488ed2c0 100644
--- a/apps/plugins/main_menu_config.c
+++ b/apps/plugins/main_menu_config.c
@@ -188,7 +188,7 @@ enum plugin_status plugin_start(const void* parameter)
{
cur_sel = rb->gui_synclist_get_sel_pos(&list);
action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&list,&action,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&list, &action))
continue;
switch (action)
diff --git a/apps/plugins/maze.c b/apps/plugins/maze.c
index 20d5c82495..8c951ebdcc 100644
--- a/apps/plugins/maze.c
+++ b/apps/plugins/maze.c
@@ -498,10 +498,8 @@ enum plugin_status plugin_start(const void* parameter)
struct maze maze;
(void)parameter;
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
/* Seed the RNG */
rb->srand(*rb->current_tick);
@@ -591,8 +589,7 @@ enum plugin_status plugin_start(const void* parameter)
#endif
}
/* Turn on backlight timeout (revert to settings) */
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
+
return ((quit == 1) ? PLUGIN_OK : PLUGIN_USB_CONNECTED);
}
diff --git a/apps/plugins/mazezam.c b/apps/plugins/mazezam.c
index cd7b6e22a8..423b09288d 100644
--- a/apps/plugins/mazezam.c
+++ b/apps/plugins/mazezam.c
@@ -256,9 +256,7 @@ static void store_lcd_settings(void)
******************************************************************************/
static void restore_lcd_settings(void) {
/* Turn on backlight timeout (revert to settings) */
-#ifdef HAVE_BACKLIGHT
backlight_use_settings();
-#endif
/* Restore the old settings */
#if LCD_DEPTH > 1
@@ -272,10 +270,9 @@ static void restore_lcd_settings(void) {
* Adjust the LCD settings to suit MazezaM levels
******************************************************************************/
static void plugin_lcd_settings(void) {
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
/* Set the new settings */
#ifdef HAVE_LCD_COLOR
rb->lcd_set_background(MAZEZAM_BG_COLOR);
diff --git a/apps/plugins/mpegplayer/mpegplayer.c b/apps/plugins/mpegplayer/mpegplayer.c
index e66b4df146..654a348959 100644
--- a/apps/plugins/mpegplayer/mpegplayer.c
+++ b/apps/plugins/mpegplayer/mpegplayer.c
@@ -1215,10 +1215,9 @@ static void osd_lcd_enable_hook(unsigned short id, void* param)
static void osdbacklight_hw_on_video_mode(bool video_on)
{
if (video_on) {
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
rb->remove_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
#endif
@@ -1226,10 +1225,8 @@ static void osdbacklight_hw_on_video_mode(bool video_on)
#if defined(HAVE_LCD_ENABLE) || defined(HAVE_LCD_SLEEP)
rb->add_event(LCD_EVENT_ACTIVATION, osd_lcd_enable_hook);
#endif
-#ifdef HAVE_BACKLIGHT
/* Revert to user's backlight settings */
backlight_use_settings();
-#endif
}
}
diff --git a/apps/plugins/open_plugins.c b/apps/plugins/open_plugins.c
index 3a0c34d8d6..6deaf80f7d 100644
--- a/apps/plugins/open_plugins.c
+++ b/apps/plugins/open_plugins.c
@@ -87,6 +87,15 @@ static size_t pathbasename(const char *name, const char **nameptr)
*nameptr = q;
return r - q;
}
+static int op_entry_checksum(void)
+{
+ if (op_entry.checksum != open_plugin_csum +
+ (op_entry.lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY))
+ {
+ return 0;
+ }
+ return 1;
+}
static bool op_entry_read(int fd, int selected_item, off_t data_sz)
{
@@ -94,7 +103,7 @@ static bool op_entry_read(int fd, int selected_item, off_t data_sz)
op_entry.lang_id = -1;
return ((selected_item >= 0) &&
(rb->lseek(fd, selected_item * op_entry_sz, SEEK_SET) >= 0) &&
- (rb->read(fd, &op_entry, data_sz) == data_sz));
+ (rb->read(fd, &op_entry, data_sz) == data_sz) && op_entry_checksum() > 0);
}
static bool op_entry_read_name(int fd, int selected_item)
@@ -102,15 +111,6 @@ static bool op_entry_read_name(int fd, int selected_item)
return op_entry_read(fd, selected_item, op_name_sz);
}
-static int op_entry_checksum(void)
-{
- if (op_entry.checksum != open_plugin_csum)
- {
- return 0;
- }
- return 1;
-}
-
static int op_entry_read_opx(const char *path)
{
int ret = -1;
@@ -174,7 +174,8 @@ failure:
static void op_entry_set_checksum(void)
{
- op_entry.checksum = open_plugin_csum;
+ op_entry.checksum = open_plugin_csum +
+ (op_entry.lang_id <= OPEN_PLUGIN_LANG_INVALID ? 0 : LANG_LAST_INDEX_IN_ARRAY);
}
static void op_entry_set_name(void)
@@ -466,7 +467,7 @@ static void op_entry_remove_empty(void)
while (resave == false &&
rb->read(fd_dat, &op_entry, op_entry_sz) == op_entry_sz)
{
- if (op_entry.hash == 0)
+ if (op_entry.hash == 0 || !op_entry_checksum())
resave = true;
}
}
@@ -623,7 +624,6 @@ static void synclist_set(char* menu_id, int selection, int items, int sel_size)
rb->gui_synclist_set_icon_callback(&lists,NULL);
rb->gui_synclist_set_voice_callback(&lists, list_voice_cb);
rb->gui_synclist_set_nb_items(&lists,items);
- rb->gui_synclist_limit_scroll(&lists,true);
rb->gui_synclist_select_item(&lists, selection);
list_voice_cb(selection, menu_id);
}
@@ -682,7 +682,7 @@ static void edit_menu(int selection)
{
action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&lists,&action,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&lists, &action))
continue;
selected_item = rb->gui_synclist_get_sel_pos(&lists);
switch (action)
@@ -840,6 +840,12 @@ reopen_datfile:
}/* OP_EXT */
}
+ for (int i = items - 1; i > 0 && !exit; i--)
+ {
+ if (!op_entry_read(fd_dat, i, op_entry_sz))
+ items--;
+ }
+
if (items < 1 && !exit)
{
char* cur_filename = rb->plugin_get_current_filename();
@@ -865,7 +871,7 @@ reopen_datfile:
{
action = rb->get_action(CONTEXT_LIST,TIMEOUT_BLOCK);
- if (rb->gui_synclist_do_button(&lists,&action,LIST_WRAP_UNLESS_HELD))
+ if (rb->gui_synclist_do_button(&lists, &action))
continue;
selection = rb->gui_synclist_get_sel_pos(&lists);
switch (action)
diff --git a/apps/plugins/oscilloscope.c b/apps/plugins/oscilloscope.c
index ae84e14f7f..ef9e6b8a98 100644
--- a/apps/plugins/oscilloscope.c
+++ b/apps/plugins/oscilloscope.c
@@ -1939,10 +1939,9 @@ static void osc_cleanup(void)
rb->lcd_set_foreground(LCD_DEFAULT_FG);
rb->lcd_set_background(LCD_DEFAULT_BG);
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
/* save settings if changed */
if (rb->memcmp(&osc, &osc_disk, sizeof(osc)))
@@ -1975,10 +1974,9 @@ static void osc_setup(void)
mixer_sampr = rb->mixer_get_frequency();
#endif
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
graphmode_setup();
}
diff --git a/apps/plugins/pacbox/pacbox.c b/apps/plugins/pacbox/pacbox.c
index 71c9751cad..9434aed743 100755
--- a/apps/plugins/pacbox/pacbox.c
+++ b/apps/plugins/pacbox/pacbox.c
@@ -809,8 +809,8 @@ enum plugin_status plugin_start(const void* parameter)
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
#endif
-#ifdef HAVE_BACKLIGHT
+
backlight_use_settings();
-#endif
+
return PLUGIN_OK;
}
diff --git a/apps/plugins/pictureflow/pictureflow.c b/apps/plugins/pictureflow/pictureflow.c
index 2f075a7e61..7670ef01df 100644
--- a/apps/plugins/pictureflow/pictureflow.c
+++ b/apps/plugins/pictureflow/pictureflow.c
@@ -151,8 +151,11 @@ const struct button_mapping pf_context_buttons[] =
{PF_QUIT, BUTTON_POWER, BUTTON_NONE},
#elif (CONFIG_KEYPAD == IPOD_1G2G_PAD) \
|| (CONFIG_KEYPAD == IPOD_3G_PAD) \
- || (CONFIG_KEYPAD == IPOD_4G_PAD) \
- || (CONFIG_KEYPAD == MPIO_HD300_PAD)
+ || (CONFIG_KEYPAD == IPOD_4G_PAD)
+ {PF_QUIT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
+ {PF_SORTING_NEXT, BUTTON_SELECT|BUTTON_MENU, BUTTON_NONE},
+ {PF_SORTING_PREV, BUTTON_SELECT|BUTTON_PLAY, BUTTON_NONE},
+#elif CONFIG_KEYPAD == MPIO_HD300_PAD
{PF_QUIT, BUTTON_MENU|BUTTON_REPEAT, BUTTON_MENU},
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
{PF_QUIT, BUTTON_RC_REC, BUTTON_NONE},
@@ -689,7 +692,7 @@ static void config_set_defaults(struct pf_config_t *cfg)
cfg->resize = true;
cfg->cache_version = CACHE_REBUILD;
cfg->show_album_name = (LCD_HEIGHT > 100)
- ? ALBUM_NAME_TOP : ALBUM_NAME_BOTTOM;
+ ? ALBUM_AND_ARTIST_BOTTOM : ALBUM_NAME_BOTTOM;
cfg->sort_albums_by = SORT_BY_ARTIST_AND_NAME;
cfg->year_sort_order = ASCENDING;
cfg->show_year = false;
@@ -963,6 +966,9 @@ const struct custom_format format_transposed = {
static const struct button_mapping* get_context_map(int context)
{
+#ifdef HAVE_LOCKED_ACTIONS
+ context &= ~CONTEXT_LOCKED;
+#endif
return pf_contexts[context & ~CONTEXT_PLUGIN];
}
@@ -2318,6 +2324,8 @@ aa_success:
configfile_save(CONFIG_FILE, config, CONFIG_NUM_ITEMS,
CONFIG_VERSION);
free_all_slide_prio(0);
+ if (pf_state == pf_idle)
+ rb->queue_post(&thread_q, EV_WAKEUP, 0);
}
if(verbose)/* direct interaction with user */
@@ -3098,7 +3106,7 @@ static void render_slide(struct slide_data *slide, const int alpha)
const pix_t *ptr = &src[column * bmp->height];
-#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#define PIXELSTEP_Y 1
#define LCDADDR(x, y) (&buffer[BUFFER_HEIGHT*(x) + (y)])
#else
@@ -3227,6 +3235,12 @@ static bool sort_albums(int new_sorting, bool from_settings)
{
#ifdef USEGSLIB
grey_show(false);
+#if LCD_DEPTH > 1
+ rb->lcd_set_background(N_BRIGHT(0));
+ rb->lcd_set_foreground(N_BRIGHT(255));
+#endif
+ rb->lcd_clear_display();
+ rb->lcd_update();
#endif
rb->splash(HZ, sort_options[pf_cfg.sort_albums_by]);
#ifdef USEGSLIB
@@ -3479,16 +3493,29 @@ static void cleanup(void)
rb->cpu_boost(false);
#endif
end_pf_thread();
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
#ifdef USEGSLIB
grey_release();
#endif
}
+static void interrupt_cover_in_animation(void);
+static void adjust_album_display_for_setting(int old_val, int new_val)
+{
+ if (old_val == new_val)
+ return;
+
+ reset_track_list();
+ recalc_offsets();
+ reset_slides();
+
+ if (pf_state == pf_show_tracks)
+ interrupt_cover_in_animation();
+}
+
/**
Shows the settings menu
*/
@@ -3544,11 +3571,10 @@ static int settings_menu(void)
selection=rb->do_menu(&settings_menu,&selection, NULL, false);
switch(selection) {
case 0:
+ old_val = pf_cfg.show_album_name;
rb->set_option(rb->str(LANG_SHOW_ALBUM_TITLE),
&pf_cfg.show_album_name, INT, album_name_options, 5, NULL);
- reset_track_list();
- recalc_offsets();
- reset_slides();
+ adjust_album_display_for_setting(old_val, pf_cfg.show_album_name);
break;
case 1:
rb->set_bool(rb->str(LANG_SHOW_YEAR_IN_ALBUM_TITLE), &pf_cfg.show_year);
@@ -3570,38 +3596,40 @@ static int settings_menu(void)
pf_cfg.year_sort_order = old_val;
break;
case 4:
+ old_val = pf_cfg.show_fps;
rb->set_bool(rb->str(LANG_DISPLAY_FPS), &pf_cfg.show_fps);
- reset_track_list();
+ if (old_val != pf_cfg.show_fps)
+ reset_track_list();
break;
case 5:
+ old_val = pf_cfg.slide_spacing;
rb->set_int(rb->str(LANG_SPACING), "", 1,
&pf_cfg.slide_spacing,
NULL, 1, 0, 100, NULL );
- recalc_offsets();
- reset_slides();
+ adjust_album_display_for_setting(old_val, pf_cfg.slide_spacing);
break;
case 6:
+ old_val = pf_cfg.center_margin;
rb->set_int(rb->str(LANG_CENTRE_MARGIN), "", 1,
&pf_cfg.center_margin,
NULL, 1, 0, 80, NULL );
- recalc_offsets();
- reset_slides();
+ adjust_album_display_for_setting(old_val, pf_cfg.center_margin);
break;
case 7:
+ old_val = pf_cfg.num_slides;
rb->set_int(rb->str(LANG_NUMBER_OF_SLIDES), "", 1,
&pf_cfg.num_slides, NULL, 1, 1, MAX_SLIDES_COUNT, NULL );
- recalc_offsets();
- reset_slides();
+ adjust_album_display_for_setting(old_val, pf_cfg.num_slides);
break;
case 8:
+ old_val = pf_cfg.zoom;
rb->set_int(rb->str(LANG_ZOOM), "", 1, &pf_cfg.zoom,
NULL, 1, 10, 300, NULL );
- recalc_offsets();
- reset_slides();
+ adjust_album_display_for_setting(old_val, pf_cfg.zoom);
break;
case 9:
@@ -3892,7 +3920,10 @@ static void show_track_list(void)
{
mylcd_clear_display();
if ( center_slide.slide_index != pf_tracks.cur_idx ) {
- show_track_list_loading();
+#ifdef HAVE_TC_RAMCACHE
+ if (!rb->tagcache_is_in_ram())
+#endif
+ show_track_list_loading();
create_track_index(center_slide.slide_index);
if (pf_tracks.count == 0)
{
@@ -4016,7 +4047,10 @@ static bool track_list_ready(void)
{
if (pf_state != pf_show_tracks)
{
- rb->splash(0, ID2P(LANG_WAIT));
+#ifdef HAVE_TC_RAMCACHE
+ if (!rb->tagcache_is_in_ram())
+#endif
+ rb->splash(0, ID2P(LANG_WAIT));
create_track_index(center_slide.slide_index);
if (pf_tracks.count == 0)
{
@@ -4214,7 +4248,7 @@ static void set_initial_slide(const char* selected_file)
pf_cfg.last_album);
else
{
- struct mp3entry id3;
+ static struct mp3entry id3;
#if defined(HAVE_TC_RAMCACHE) && defined(HAVE_DIRCACHE)
if (rb->tagcache_fill_tags(&id3, selected_file))
set_current_slide(id3_get_index(&id3));
@@ -4268,14 +4302,11 @@ static int pictureflow_main(const char* selected_file)
config_set_defaults(&pf_cfg);
configfile_load(CONFIG_FILE, config, CONFIG_NUM_ITEMS, CONFIG_VERSION);
- if(pf_cfg.auto_wps == 0)
- draw_splashscreen(pf_idx.buf, pf_idx.buf_sz);
- if(pf_cfg.backlight_mode == 0) {
- /* Turn off backlight timeout */
+
#ifdef HAVE_BACKLIGHT
+ if(pf_cfg.backlight_mode == 0)
backlight_ignore_timeout();
#endif
- }
rb->mutex_init(&buf_ctx_mutex);
@@ -4451,7 +4482,8 @@ static int pictureflow_main(const char* selected_file)
rb->snprintf(fpstxt, sizeof(fpstxt), "%d %%", progress_pct);
}
- if (pf_cfg.show_album_name == ALBUM_NAME_TOP)
+ if (pf_cfg.show_album_name == ALBUM_NAME_TOP ||
+ pf_cfg.show_album_name == ALBUM_AND_ARTIST_TOP)
fpstxt_y = LCD_HEIGHT -
rb->screens[SCREEN_MAIN]->getcharheight();
else
@@ -4569,15 +4601,15 @@ static int pictureflow_main(const char* selected_file)
break;
#if PF_PLAYBACK_CAPABLE
case PF_CONTEXT:
- if (pf_cfg.auto_wps != 0 &&
- (pf_state == pf_idle || pf_state == pf_scrolling ||
- pf_state == pf_show_tracks || pf_state == pf_cover_out)) {
+ if (pf_state == pf_idle || pf_state == pf_scrolling ||
+ pf_state == pf_show_tracks || pf_state == pf_cover_out) {
if ( pf_state == pf_scrolling)
{
set_current_slide(target);
pf_state = pf_idle;
- } else if (pf_state == pf_cover_out)
+ }
+ else if (pf_state == pf_cover_out)
interrupt_cover_out_animation();
show_current_playlist_menu();
diff --git a/apps/plugins/plasma.c b/apps/plugins/plasma.c
index f944d3d775..8da53874e1 100644
--- a/apps/plugins/plasma.c
+++ b/apps/plugins/plasma.c
@@ -140,10 +140,10 @@ static void cleanup(void)
#ifndef HAVE_LCD_COLOR
grey_release();
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
rb->lcd_set_mode(LCD_MODE_RGB565);
#endif
@@ -321,10 +321,10 @@ enum plugin_status plugin_start(const void* parameter)
#if LCD_DEPTH > 1
rb->lcd_set_backdrop(NULL);
#endif
-#ifdef HAVE_BACKLIGHT
+
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
#if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
rb->lcd_set_mode(LCD_MODE_PAL256);
#endif
diff --git a/apps/plugins/pong.c b/apps/plugins/pong.c
index b49fec2459..17e6e6ed73 100644
--- a/apps/plugins/pong.c
+++ b/apps/plugins/pong.c
@@ -750,10 +750,9 @@ enum plugin_status plugin_start(const void* parameter)
this to avoid the compiler warning about it */
(void)parameter;
-#ifdef HAVE_BACKLIGHT
/* Turn off backlight timeout */
backlight_ignore_timeout();
-#endif
+
/* Clear screen */
rb->lcd_clear_display();
@@ -792,9 +791,9 @@ enum plugin_status plugin_start(const void* parameter)
rb->lcd_clear_display();
}
}
-#ifdef HAVE_BACKLIGHT
+
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-#endif
+
return (game == 0) ? PLUGIN_OK : PLUGIN_USB_CONNECTED;
}
diff --git a/apps/plugins/properties.c b/apps/plugins/properties.c
index c4378a0356..73ad841c0c 100644
--- a/apps/plugins/properties.c
+++ b/apps/plugins/properties.c
@@ -24,6 +24,14 @@
#define ARRAY_SIZE(x) (sizeof((x)) / sizeof((x)[0]))
#endif
+struct dir_stats {
+ char dirname[MAX_PATH];
+ int len;
+ unsigned int dir_count;
+ unsigned int file_count;
+ unsigned long long byte_count;
+};
+
enum props_types {
PROPS_FILE = 0,
PROPS_ID3,
@@ -34,18 +42,17 @@ static int props_type = PROPS_FILE;
static struct mp3entry id3;
-char str_filename[MAX_PATH];
-char str_dirname[MAX_PATH];
-char str_size[64];
-char str_dircount[64];
-char str_filecount[64];
-char str_date[64];
-char str_time[64];
+static char str_filename[MAX_PATH];
+static char str_dirname[MAX_PATH];
+static char str_size[64];
+static char str_dircount[64];
+static char str_filecount[64];
+static char str_date[64];
+static char str_time[64];
-unsigned nseconds;
-unsigned long nsize;
-int32_t size_unit;
-struct tm tm;
+static unsigned long nsize;
+static int32_t size_unit;
+static struct tm tm;
#define NUM_FILE_PROPERTIES 5
static const unsigned char* const props_file[] =
@@ -128,15 +135,7 @@ static bool file_properties(const char* selected_file)
return found;
}
-typedef struct {
- char dirname[MAX_PATH];
- int len;
- unsigned int dc;
- unsigned int fc;
- unsigned long long bc;
-} DPS;
-
-static bool _dir_properties(DPS *dps)
+static bool _dir_properties(struct dir_stats *stats)
{
/* recursively scan directories in search of files
and informs the user of the progress */
@@ -147,11 +146,11 @@ static bool _dir_properties(DPS *dps)
struct dirent* entry;
result = true;
- dirlen = rb->strlen(dps->dirname);
- dir = rb->opendir(dps->dirname);
+ dirlen = rb->strlen(stats->dirname);
+ dir = rb->opendir(stats->dirname);
if (!dir)
{
- rb->splashf(HZ*2, "%s", dps->dirname);
+ rb->splashf(HZ*2, "%s", stats->dirname);
return false; /* open error */
}
@@ -160,7 +159,7 @@ static bool _dir_properties(DPS *dps)
{
struct dirinfo info = rb->dir_get_info(dir, entry);
/* append name to current directory */
- rb->snprintf(dps->dirname+dirlen, dps->len-dirlen, "/%s",
+ rb->snprintf(stats->dirname+dirlen, stats->len-dirlen, "/%s",
entry->d_name);
if (info.attribute & ATTR_DIRECTORY)
@@ -169,29 +168,30 @@ static bool _dir_properties(DPS *dps)
!rb->strcmp((char *)entry->d_name, ".."))
continue; /* skip these */
- dps->dc++; /* new directory */
+ stats->dir_count++; /* new directory */
if (*rb->current_tick - lasttick > (HZ/8))
{
unsigned log;
lasttick = *rb->current_tick;
rb->lcd_clear_display();
rb->lcd_puts(0,0,"SCANNING...");
- rb->lcd_puts(0,1,dps->dirname);
- rb->lcd_putsf(0,2,"Directories: %d", dps->dc);
- rb->lcd_putsf(0,3,"Files: %d", dps->fc);
- log = human_size_log(dps->bc);
- rb->lcd_putsf(0,4,"Size: %lu %cB", (unsigned long)(dps->bc >> (10*log)),
+ rb->lcd_puts(0,1,stats->dirname);
+ rb->lcd_putsf(0,2,"Directories: %d", stats->dir_count);
+ rb->lcd_putsf(0,3,"Files: %d", stats->file_count);
+ log = human_size_log(stats->byte_count);
+ rb->lcd_putsf(0,4,"Size: %lu %s",
+ (unsigned long)(stats->byte_count >> (10*log)),
rb->str(units[log]));
rb->lcd_update();
}
/* recursion */
- result = _dir_properties(dps);
+ result = _dir_properties(stats);
}
else
{
- dps->fc++; /* new file */
- dps->bc += info.size;
+ stats->file_count++; /* new file */
+ stats->byte_count += info.size;
}
if(ACTION_STD_CANCEL == rb->get_action(CONTEXT_STD,TIMEOUT_NOBLOCK))
result = false;
@@ -201,17 +201,17 @@ static bool _dir_properties(DPS *dps)
return result;
}
-static bool dir_properties(const char* selected_file, DPS *dps)
+static bool dir_properties(const char* selected_file, struct dir_stats *stats)
{
unsigned log;
- rb->strlcpy(dps->dirname, selected_file, MAX_PATH);
+ rb->strlcpy(stats->dirname, selected_file, MAX_PATH);
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(true);
#endif
- if (!_dir_properties(dps))
+ if (!_dir_properties(stats))
{
#ifdef HAVE_ADJUSTABLE_CPU_FREQ
rb->cpu_boost(false);
@@ -224,10 +224,10 @@ static bool dir_properties(const char* selected_file, DPS *dps)
#endif
rb->strlcpy(str_dirname, selected_file, MAX_PATH);
- rb->snprintf(str_dircount, sizeof str_dircount, "%d", dps->dc);
- rb->snprintf(str_filecount, sizeof str_filecount, "%d", dps->fc);
- log = human_size_log(dps->bc);
- nsize = (long) (dps->bc >> (log*10));
+ rb->snprintf(str_dircount, sizeof str_dircount, "%d", stats->dir_count);
+ rb->snprintf(str_filecount, sizeof str_filecount, "%d", stats->file_count);
+ log = human_size_log(stats->byte_count);
+ nsize = (long) (stats->byte_count >> (log*10));
size_unit = units[log];
rb->snprintf(str_size, sizeof str_size, "%ld %s", nsize, rb->str(size_unit));
return true;
@@ -255,7 +255,7 @@ static const char * get_props(int selected_item, void* data,
static int speak_property_selection(int selected_item, void *data)
{
- DPS *dps = data;
+ struct dir_stats *stats = data;
int32_t id = P2ID((props_type == PROPS_DIR ? props_dir : props_file)[selected_item]);
rb->talk_id(id, false);
switch (id)
@@ -295,10 +295,10 @@ static int speak_property_selection(int selected_item, void *data)
rb->talk_time(&tm, true);
break;
case LANG_PROPERTIES_SUBDIRS:
- rb->talk_number(dps->dc, true);
+ rb->talk_number(stats->dir_count, true);
break;
case LANG_PROPERTIES_FILES:
- rb->talk_number(dps->fc, true);
+ rb->talk_number(stats->file_count, true);
break;
default:
rb->talk_spell(props_file[selected_item + 1], true);
@@ -307,34 +307,49 @@ static int speak_property_selection(int selected_item, void *data)
return 0;
}
-enum plugin_status plugin_start(const void* parameter)
+static int browse_file_or_dir(struct dir_stats *stats)
{
struct gui_synclist properties_lists;
int button;
- bool quit = false, usb = false;
- const char *file = parameter;
- if(!parameter || (file[0] != '/')) return PLUGIN_ERROR;
-
-#ifdef HAVE_TOUCHSCREEN
- rb->touchscreen_set_mode(rb->global_settings->touch_mode);
-#endif
- static DPS dps = {
- .len = MAX_PATH,
- .dc = 0,
- .fc = 0,
- .bc = 0,
- };
+ rb->gui_synclist_init(&properties_lists, &get_props, stats, false, 2, NULL);
+ rb->gui_synclist_set_title(&properties_lists,
+ rb->str(props_type == PROPS_DIR ?
+ LANG_PROPERTIES_DIRECTORY_PROPERTIES :
+ LANG_PROPERTIES_FILE_PROPERTIES),
+ NOICON);
+ rb->gui_synclist_set_icon_callback(&properties_lists, NULL);
+ if (rb->global_settings->talk_menu)
+ rb->gui_synclist_set_voice_callback(&properties_lists, speak_property_selection);
+ rb->gui_synclist_set_nb_items(&properties_lists,
+ 2 * (props_type == PROPS_FILE ? NUM_FILE_PROPERTIES :
+ NUM_DIR_PROPERTIES));
+ rb->gui_synclist_select_item(&properties_lists, 0);
+ rb->gui_synclist_draw(&properties_lists);
+ rb->gui_synclist_speak_item(&properties_lists);
+
+ while(true)
+ {
+ button = rb->get_action(CONTEXT_LIST, HZ);
+ /* HZ so the status bar redraws corectly */
+ if (rb->gui_synclist_do_button(&properties_lists,&button))
+ continue;
+ switch(button)
+ {
+ case ACTION_STD_CANCEL:
+ return false;
+ default:
+ if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
+ return true;
+ break;
+ }
+ }
+}
- /* determine if it's a file or a directory */
- bool found = false;
+static bool determine_file_or_dir(void)
+{
DIR* dir;
struct dirent* entry;
- char* ptr = rb->strrchr(file, '/') + 1;
- int dirlen = (ptr - file);
-
- rb->strlcpy(str_dirname, file, dirlen + 1);
- rb->snprintf(str_filename, sizeof str_filename, "%s", file+dirlen);
dir = rb->opendir(str_dirname);
if (dir)
@@ -345,15 +360,39 @@ enum plugin_status plugin_start(const void* parameter)
{
struct dirinfo info = rb->dir_get_info(dir, entry);
props_type = info.attribute & ATTR_DIRECTORY ? PROPS_DIR : PROPS_FILE;
- found = true;
- break;
+ rb->closedir(dir);
+ return true;
}
}
rb->closedir(dir);
}
- /* now we know if it's a file or a dir or maybe something failed */
+ return false;
+}
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ static struct dir_stats stats =
+ {
+ .len = MAX_PATH,
+ .dir_count = 0,
+ .file_count = 0,
+ .byte_count = 0,
+ };
+
+ const char *file = parameter;
+ if(!parameter || (file[0] != '/')) return PLUGIN_ERROR;
+
+#ifdef HAVE_TOUCHSCREEN
+ rb->touchscreen_set_mode(rb->global_settings->touch_mode);
+#endif
+
+ const char* file_name = rb->strrchr(file, '/') + 1;
+ int dirlen = (file_name - file);
+
+ rb->strlcpy(str_dirname, file, dirlen + 1);
+ rb->snprintf(str_filename, sizeof str_filename, "%s", file+dirlen);
- if(!found)
+ if(!determine_file_or_dir())
{
/* weird: we couldn't find the entry. This Should Never Happen (TM) */
rb->splashf(0, "File/Dir not found: %s", file);
@@ -362,7 +401,7 @@ enum plugin_status plugin_start(const void* parameter)
}
/* get the info depending on its_a_dir */
- if(!(props_type == PROPS_DIR ? dir_properties(file, &dps) : file_properties(file)))
+ if(!(props_type == PROPS_DIR ? dir_properties(file, &stats) : file_properties(file)))
{
/* something went wrong (to do: tell user what it was (nesting,...) */
rb->splash(0, ID2P(LANG_PROPERTIES_FAIL));
@@ -373,48 +412,8 @@ enum plugin_status plugin_start(const void* parameter)
FOR_NB_SCREENS(i)
rb->viewportmanager_theme_enable(i, true, NULL);
- if (props_type == PROPS_ID3)
- usb = rb->browse_id3(&id3, 0, 0);
- else
- {
- rb->gui_synclist_init(&properties_lists, &get_props, &dps, false, 2, NULL);
- rb->gui_synclist_set_title(&properties_lists,
- rb->str(props_type == PROPS_DIR ?
- LANG_PROPERTIES_DIRECTORY_PROPERTIES :
- LANG_PROPERTIES_FILE_PROPERTIES),
- NOICON);
- rb->gui_synclist_set_icon_callback(&properties_lists, NULL);
- if (rb->global_settings->talk_menu)
- rb->gui_synclist_set_voice_callback(&properties_lists, speak_property_selection);
- rb->gui_synclist_set_nb_items(&properties_lists,
- 2 * (props_type == PROPS_FILE ? NUM_FILE_PROPERTIES :
- NUM_DIR_PROPERTIES));
- rb->gui_synclist_limit_scroll(&properties_lists, true);
- rb->gui_synclist_select_item(&properties_lists, 0);
- rb->gui_synclist_draw(&properties_lists);
- rb->gui_synclist_speak_item(&properties_lists);
-
- while(!quit)
- {
- button = rb->get_action(CONTEXT_LIST, HZ);
- /* HZ so the status bar redraws corectly */
- if (rb->gui_synclist_do_button(&properties_lists,&button,LIST_WRAP_UNLESS_HELD))
- continue;
- switch(button)
- {
- case ACTION_STD_CANCEL:
- quit = true;
- break;
- default:
- if (rb->default_event_handler(button) == SYS_USB_CONNECTED)
- {
- quit = true;
- usb = true;
- }
- break;
- }
- }
- }
+ bool usb = props_type == PROPS_ID3 ? rb->browse_id3(&id3, 0, 0) :
+ browse_file_or_dir(&stats);
FOR_NB_SCREENS(i)
rb->viewportmanager_theme_undo(i, false);
diff --git a/apps/plugins/puzzles/rockbox.c b/apps/plugins/puzzles/rockbox.c
index 6e34adb1db..e175429075 100644
--- a/apps/plugins/puzzles/rockbox.c
+++ b/apps/plugins/puzzles/rockbox.c
@@ -1465,7 +1465,7 @@ static void rb_blitter_free(void *handle, blitter *bl)
static void rb_blitter_save(void *handle, blitter *bl, int x, int y)
{
/* no viewport offset */
-#if defined(LCD_STRIDEFORMAT) && (LCD_STRIDEFORMAT == VERTICAL_STRIDE)
+#if LCD_STRIDEFORMAT == VERTICAL_STRIDE
#error no vertical stride
#else
if(bl && bl->bmp.data)
@@ -2450,7 +2450,6 @@ static int list_choose(const char *list_str, const char *title, int sel)
rb->gui_synclist_init(&list, &config_choices_formatter, (void*)list_str, false, 1, NULL);
rb->gui_synclist_set_icon_callback(&list, NULL);
rb->gui_synclist_set_nb_items(&list, n);
- rb->gui_synclist_limit_scroll(&list, false);
rb->gui_synclist_select_item(&list, sel);
@@ -2459,7 +2458,7 @@ static int list_choose(const char *list_str, const char *title, int sel)
{
rb->gui_synclist_draw(&list);
int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
- if(rb->gui_synclist_do_button(&list, &button, LIST_WRAP_UNLESS_HELD))
+ if(rb->gui_synclist_do_button(&list, &button))
continue;
switch(button)
{
@@ -2664,7 +2663,6 @@ static bool config_menu(void)
rb->gui_synclist_init(&list, &config_formatter, config, false, 1, NULL);
rb->gui_synclist_set_icon_callback(&list, NULL);
rb->gui_synclist_set_nb_items(&list, n);
- rb->gui_synclist_limit_scroll(&list, false);
rb->gui_synclist_select_item(&list, 0);
@@ -2674,7 +2672,7 @@ static bool config_menu(void)
{
rb->gui_synclist_draw(&list);
int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
- if(rb->gui_synclist_do_button(&list, &button, LIST_WRAP_UNLESS_HELD))
+ if(rb->gui_synclist_do_button(&list, &button))
continue;
switch(button)
{
@@ -2750,7 +2748,6 @@ static int do_preset_menu(struct preset_menu *menu, char *title, int selected)
rb->gui_synclist_init(&list, &preset_formatter, menu, false, 1, NULL);
rb->gui_synclist_set_icon_callback(&list, NULL);
rb->gui_synclist_set_nb_items(&list, menu->n_entries);
- rb->gui_synclist_limit_scroll(&list, false);
rb->gui_synclist_select_item(&list, selected);
@@ -2760,7 +2757,7 @@ static int do_preset_menu(struct preset_menu *menu, char *title, int selected)
{
rb->gui_synclist_draw(&list);
int button = rb->get_action(CONTEXT_LIST, TIMEOUT_BLOCK);
- if(rb->gui_synclist_do_button(&list, &button, LIST_WRAP_UNLESS_HELD))
+ if(rb->gui_synclist_do_button(&list, &button))
continue;
switch(button)
{
@@ -3393,9 +3390,7 @@ static void shutdown_tlsf(void)
static void exit_handler(void)
{
-#ifdef HAVE_SW_POWEROFF
sw_poweroff_restore();
-#endif
unload_fonts();
shutdown_tlsf();
@@ -3655,9 +3650,7 @@ static void puzzles_main(void)
{
rb_atexit(exit_handler);
-#ifdef HAVE_SW_POWEROFF
sw_poweroff_disable();
-#endif
init_default_settings();
init_fonttab(