diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index e2525ec37e..e1d9357f7b 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -7048,6 +7048,38 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_EXPLORE_BY_SYSTEM_NAME, "By System Name" ) +MSG_HASH( + MENU_ENUM_LABEL_EXPLORE_RANGE_FILTER, + "Set Range Filter" + ) +MSG_HASH( + MENU_ENUM_LABEL_EXPLORE_VIEW, + "View" + ) +MSG_HASH( + MENU_ENUM_LABEL_EXPLORE_SAVE_VIEW, + "Save as View" + ) +MSG_HASH( + MENU_ENUM_LABEL_EXPLORE_DELETE_VIEW, + "Delete this View" + ) +MSG_HASH( + MENU_ENUM_LABEL_EXPLORE_NEW_VIEW, + "Enter name of new view" + ) +MSG_HASH( + MENU_ENUM_LABEL_EXPLORE_VIEW_EXISTS, + "View already exists with the same name" + ) +MSG_HASH( + MENU_ENUM_LABEL_EXPLORE_VIEW_SAVED, + "View was saved" + ) +MSG_HASH( + MENU_ENUM_LABEL_EXPLORE_VIEW_DELETED, + "View was deleted" + ) /* Playlist > Playlist Item */ diff --git a/libretro-common/formats/json/rjson.c b/libretro-common/formats/json/rjson.c index 696db7074f..f80c0ae482 100644 --- a/libretro-common/formats/json/rjson.c +++ b/libretro-common/formats/json/rjson.c @@ -1251,6 +1251,17 @@ char* rjsonwriter_get_memory_buffer(rjsonwriter_t *writer, int* len) return writer->buf; } +int rjsonwriter_count_memory_buffer(rjsonwriter_t *writer) +{ + return writer->buf_num; +} + +void rjsonwriter_erase_memory_buffer(rjsonwriter_t *writer, int keep_len) +{ + if (keep_len <= writer->buf_num) + writer->buf_num = (keep_len < 0 ? 0 : keep_len); +} + bool rjsonwriter_free(rjsonwriter_t *writer) { bool res; diff --git a/libretro-common/include/formats/rjson.h b/libretro-common/include/formats/rjson.h index 9b0cd68b1e..0261c716f6 100644 --- a/libretro-common/include/formats/rjson.h +++ b/libretro-common/include/formats/rjson.h @@ -195,6 +195,14 @@ rjsonwriter_t *rjsonwriter_open_user(rjsonwriter_io_t io, void *user_data); * Returned buffer is only valid until writer is modified or freed. */ char* rjsonwriter_get_memory_buffer(rjsonwriter_t *writer, int* len); +/* When opened with rjsonwriter_open_memory, will return current length */ +int rjsonwriter_count_memory_buffer(rjsonwriter_t *writer); + +/* When opened with rjsonwriter_open_memory, will clear the buffer. + The buffer will be partially erased if keep_len is > 0. + No memory is freed or re-allocated with this function. */ +void rjsonwriter_erase_memory_buffer(rjsonwriter_t *writer, int keep_len); + /* Free rjsonwriter handle and return result of final rjsonwriter_flush call */ bool rjsonwriter_free(rjsonwriter_t *writer); diff --git a/libretro-common/include/formats/rjson_helpers.h b/libretro-common/include/formats/rjson_helpers.h index 13e77dacb6..01f2589dd3 100644 --- a/libretro-common/include/formats/rjson_helpers.h +++ b/libretro-common/include/formats/rjson_helpers.h @@ -70,6 +70,8 @@ static INLINE void rjsonwriter_add_int(rjsonwriter_t *writer, int value) static INLINE void rjsonwriter_add_bool(rjsonwriter_t *writer, bool value) { rjsonwriter_raw(writer, (value ? "true" : "false"), (value ? 4 : 5)); } +static INLINE void rjsonwriter_add_null(rjsonwriter_t *writer) + { rjsonwriter_raw(writer, "null", 4); } RETRO_END_DECLS diff --git a/menu/cbs/menu_cbs_title.c b/menu/cbs/menu_cbs_title.c index 1401afdabb..636264ae30 100644 --- a/menu/cbs/menu_cbs_title.c +++ b/menu/cbs/menu_cbs_title.c @@ -1818,6 +1818,7 @@ int menu_cbs_init_bind_title(menu_file_list_cbs_t *cbs, BIND_ACTION_GET_TITLE(cbs, action_get_title_default); if (cbs->enum_idx != MENU_ENUM_LABEL_PLAYLIST_ENTRY && + cbs->enum_idx != MENU_ENUM_LABEL_EXPLORE_ITEM && menu_cbs_init_bind_title_compare_label(cbs, label) == 0) return 0; diff --git a/menu/drivers/ozone.c b/menu/drivers/ozone.c index 29fc386215..8494d1fbd7 100644 --- a/menu/drivers/ozone.c +++ b/menu/drivers/ozone.c @@ -455,7 +455,6 @@ struct ozone_handle size_t categories_selection_ptr; /* active tab id */ size_t categories_active_idx_old; size_t playlist_index; - size_t playlist_collection_offset; size_t selection; /* currently selected entry */ size_t selection_old; /* previously selected entry (for fancy animation) */ @@ -3619,12 +3618,25 @@ static void ozone_entries_update_thumbnail_bar( } } +static unsigned ozone_get_horizontal_selection_type(ozone_handle_t *ozone) +{ + if (ozone->categories_selection_ptr > ozone->system_tab_end) + { + size_t i = ozone->categories_selection_ptr - ozone->system_tab_end-1; + return ozone->horizontal_list.list[i].type; + } + return 0; +} + static bool ozone_is_playlist(ozone_handle_t *ozone, bool depth) { bool is_playlist; if (ozone->categories_selection_ptr > ozone->system_tab_end) - is_playlist = true; + { + unsigned type = ozone_get_horizontal_selection_type(ozone); + is_playlist = type == FILE_TYPE_PLAYLIST_COLLECTION; + } else { switch (ozone->tabs[ozone->categories_selection_ptr]) @@ -3687,7 +3699,8 @@ static void ozone_sidebar_update_collapse( goto end; /* Collapse it */ - if (ozone_collapse_sidebar || (is_playlist && !ozone->cursor_in_sidebar)) + if (ozone_collapse_sidebar || (!ozone->cursor_in_sidebar && (is_playlist || + ozone_get_horizontal_selection_type(ozone) == MENU_EXPLORE_TAB))) { if (allow_animation) { @@ -3838,15 +3851,12 @@ static void ozone_update_content_metadata(ozone_handle_t *ozone) (selection < list_size) && ozone->is_explore_list) { - playlist_index = menu_explore_get_entry_playlist_index(list->list[selection].type, &playlist, &entry); + playlist_index = menu_explore_get_entry_playlist_index(list->list[selection].type, &playlist, &entry, list, &selection, &list_size); /* Fill play time if applicable */ if (content_runtime_log || content_runtime_log_aggr) playlist_get_index(playlist, playlist_index, &entry); - /* Corrections */ - list_size--; - /* Remember playlist index for metadata */ ozone->playlist_index = playlist_index; } @@ -3872,7 +3882,7 @@ static void ozone_update_content_metadata(ozone_handle_t *ozone) { unsigned long _entry = (unsigned long)(playlist_index + 1); if (ozone->is_explore_list) - _entry = (unsigned long)(selection); + _entry = (unsigned long)(selection + 1); snprintf(ozone->selection_entry_enumeration, sizeof(ozone->selection_entry_enumeration), @@ -4651,6 +4661,13 @@ static void ozone_context_reset_horizontal_list(ozone_handle_t *ozone) else node->console_name = strdup(path); } + else if (string_ends_with_size(ozone->horizontal_list.list[i].label, ".lvw", + strlen(ozone->horizontal_list.list[i].label), STRLEN_CONST(".lvw"))) + { + /* For now use a default icon for views */ + node->console_name = strdup(path + strlen(msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_VIEW)) + 2); + node->icon = ozone->icons_textures[OZONE_ENTRIES_ICONS_TEXTURE_FILE]; + } } } @@ -5449,14 +5466,7 @@ border_iterate: } else if (ozone->depth == 3 && entry.type == FILE_TYPE_PLAYLIST_COLLECTION) { - size_t i_playlist = 0; - ozone_node_t *sidebar_node; - - if (i >= ozone->playlist_collection_offset) - i_playlist = i - ozone->playlist_collection_offset; - - sidebar_node = (ozone_node_t*) file_list_get_userdata_at_offset(&ozone->horizontal_list, i_playlist); - + ozone_node_t *sidebar_node = (ozone_node_t*) file_list_get_userdata_at_offset(&ozone->horizontal_list, selection_buf->list[i].entry_idx); if (sidebar_node && sidebar_node->icon) texture = sidebar_node->icon; } @@ -7096,48 +7106,17 @@ static void ozone_set_thumbnail_content(void *data, const char *s) /* Explore list */ if (string_is_empty(s)) { + /* Selected entry */ menu_entry_t entry; - size_t selection = menu_navigation_get_selection(); - MENU_ENTRY_INIT(entry); entry.label_enabled = false; entry.rich_label_enabled = false; entry.value_enabled = false; entry.sublabel_enabled = false; + menu_entry_get(&entry, 0, menu_navigation_get_selection(), NULL, true); - /* First entry */ - menu_entry_get(&entry, 0, 0, NULL, true); - if (string_is_empty(entry.path)) - return; - - /* No thumbnails for intermediate lists without playlist items */ - if (!string_is_equal(entry.path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_ADD_ADDITIONAL_FILTER)) && - !string_is_equal(entry.path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_SEARCH_NAME))) - { - gfx_thumbnail_set_content_playlist(ozone->thumbnail_path_data, NULL, 0); - ozone->want_thumbnail_bar = ozone->fullscreen_thumbnails_available = false; - return; - } - - /* Selected entry */ - menu_entry_get(&entry, 0, selection, NULL, true); - if (string_is_empty(entry.path)) - return; - - /* No thumbnails for header non-items */ - if (string_is_equal(entry.path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_ADD_ADDITIONAL_FILTER)) || - string_is_equal(entry.path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_SEARCH_NAME))) - { - gfx_thumbnail_set_content_playlist(ozone->thumbnail_path_data, NULL, 0); - ozone->want_thumbnail_bar = ozone->fullscreen_thumbnails_available = false; - return; - } - else - { - ozone->want_thumbnail_bar = ozone->fullscreen_thumbnails_available = - (menu_explore_set_entry_playlist_index(entry.type, ozone->thumbnail_path_data) >= 0 && - menu_explore_get_entry_icon(entry.type)); - } + ozone->want_thumbnail_bar = ozone->fullscreen_thumbnails_available = + (menu_explore_set_playlist_thumbnail(entry.type, ozone->thumbnail_path_data) >= 0); } } #endif @@ -10785,6 +10764,11 @@ static void ozone_populate_entries(void *data, ozone->num_search_terms_old = num_search_terms; } } + else if (ozone->is_explore_list) + { + /* when refreshing the explore view, also refresh the title */ + ozone_set_header(ozone); + } return; } @@ -10804,7 +10788,8 @@ static void ozone_populate_entries(void *data, ozone->depth = new_depth; ozone->is_db_manager_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_DATABASE_MANAGER_LIST)); ozone->is_explore_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_EXPLORE_LIST)) || - string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_TAB)); + string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_TAB)) || + ozone_get_horizontal_selection_type(ozone) == MENU_EXPLORE_TAB; ozone->is_file_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_FAVORITES)); ozone->is_quick_menu = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_RPL_ENTRY_ACTIONS)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_CONTENT_SETTINGS)) || @@ -10822,30 +10807,15 @@ static void ozone_populate_entries(void *data, ozone->skip_thumbnail_reset = true; } - /* Quick Menu under Explore list must also be Quick Menu */ +#if defined(HAVE_LIBRETRODB) if (ozone->is_explore_list) { + /* Quick Menu under Explore list must also be Quick Menu */ ozone->is_quick_menu |= menu_is_nonrunning_quick_menu() || menu_is_running_quick_menu(); - if (ozone->is_quick_menu) + if (!menu_explore_is_content_list()) ozone->is_explore_list = false; } - - /* Determine the first playlist item under "Load Content > Playlists" */ - ozone->playlist_collection_offset = 0; - if (settings->uints.menu_content_show_add_entry) - ozone->playlist_collection_offset++; - if (settings->uints.menu_content_show_contentless_cores) - ozone->playlist_collection_offset++; - if (settings->bools.menu_content_show_explore) - ozone->playlist_collection_offset++; - if (settings->bools.menu_content_show_favorites) - ozone->playlist_collection_offset++; - if (settings->bools.menu_content_show_images) - ozone->playlist_collection_offset++; - if (settings->bools.menu_content_show_music) - ozone->playlist_collection_offset++; - if (settings->bools.menu_content_show_video) - ozone->playlist_collection_offset++; +#endif if (animate) if (ozone->categories_selection_ptr == ozone->categories_active_idx_old) diff --git a/menu/drivers/rgui.c b/menu/drivers/rgui.c index 889a180a1f..790bdcda02 100644 --- a/menu/drivers/rgui.c +++ b/menu/drivers/rgui.c @@ -6736,47 +6736,19 @@ static void rgui_scan_selected_entry_thumbnail(rgui_t *rgui, bool force_load) { /* Get playlist index corresponding * to the selected entry */ - if (list && - (selection < list_size)) + if (list && selection < list_size) { + /* Selected entry */ menu_entry_t entry; - MENU_ENTRY_INIT(entry); entry.label_enabled = false; entry.rich_label_enabled = false; entry.value_enabled = false; entry.sublabel_enabled = false; - - /* First entry */ - menu_entry_get(&entry, 0, 0, NULL, true); - if (string_is_empty(entry.path)) - return; - - /* No thumbnails for intermediate lists without playlist items */ - if (!string_is_equal(entry.path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_ADD_ADDITIONAL_FILTER)) && - !string_is_equal(entry.path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_SEARCH_NAME))) - { - gfx_thumbnail_set_content_playlist(rgui->thumbnail_path_data, NULL, 0); - return; - } - - /* Selected entry */ menu_entry_get(&entry, 0, selection, NULL, true); - if (string_is_empty(entry.path)) - return; - /* No thumbnails for header non-items */ - if (string_is_equal(entry.path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_ADD_ADDITIONAL_FILTER)) || - string_is_equal(entry.path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_SEARCH_NAME))) - { - gfx_thumbnail_set_content_playlist(rgui->thumbnail_path_data, NULL, 0); - return; - } - else - { - rgui->playlist_index = - menu_explore_set_entry_playlist_index(entry.type, rgui->thumbnail_path_data); - } + rgui->playlist_index = + menu_explore_set_playlist_thumbnail(entry.type, rgui->thumbnail_path_data); } } #endif diff --git a/menu/drivers/xmb.c b/menu/drivers/xmb.c index da42a7bfd5..9f465b25d4 100644 --- a/menu/drivers/xmb.c +++ b/menu/drivers/xmb.c @@ -337,7 +337,6 @@ typedef struct xmb_handle size_t categories_selection_ptr_old; size_t selection_ptr_old; size_t fullscreen_thumbnail_selection; - size_t playlist_collection_offset; /* size of the current list */ size_t list_size; @@ -1290,6 +1289,16 @@ static unsigned xmb_get_system_tab(xmb_handle_t *xmb, unsigned i) return UINT_MAX; } +static unsigned xmb_get_horizontal_selection_type(xmb_handle_t *xmb) +{ + if (xmb->categories_selection_ptr > xmb->system_tab_end) + { + size_t i = xmb->categories_selection_ptr - xmb->system_tab_end-1; + return xmb->horizontal_list.list[i].type; + } + return 0; +} + static void xmb_refresh_thumbnail_image(void *data, unsigned i) { xmb_handle_t *xmb = (xmb_handle_t*)data; @@ -1406,48 +1415,17 @@ static void xmb_set_thumbnail_content(void *data, const char *s) /* Explore list */ if (string_is_empty(s)) { + /* Selected entry */ menu_entry_t entry; - size_t selection = menu_navigation_get_selection(); - MENU_ENTRY_INIT(entry); entry.label_enabled = false; entry.rich_label_enabled = false; entry.value_enabled = false; entry.sublabel_enabled = false; + menu_entry_get(&entry, 0, menu_navigation_get_selection(), NULL, true); - /* First entry */ - menu_entry_get(&entry, 0, 0, NULL, true); - if (string_is_empty(entry.path)) - return; - - /* No thumbnails for intermediate lists without playlist items */ - if (!string_is_equal(entry.path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_ADD_ADDITIONAL_FILTER)) && - !string_is_equal(entry.path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_SEARCH_NAME))) - { - gfx_thumbnail_set_content_playlist(xmb->thumbnail_path_data, NULL, 0); - xmb->fullscreen_thumbnails_available = false; - return; - } - - /* Selected entry */ - menu_entry_get(&entry, 0, selection, NULL, true); - if (string_is_empty(entry.path)) - return; - - /* No thumbnails for header non-items */ - if (string_is_equal(entry.path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_ADD_ADDITIONAL_FILTER)) || - string_is_equal(entry.path, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_SEARCH_NAME))) - { - gfx_thumbnail_set_content_playlist(xmb->thumbnail_path_data, NULL, 0); - xmb->fullscreen_thumbnails_available = false; - return; - } - else - { - xmb->fullscreen_thumbnails_available = - (menu_explore_set_entry_playlist_index(entry.type, xmb->thumbnail_path_data) >= 0 && - menu_explore_get_entry_icon(entry.type)); - } + xmb->fullscreen_thumbnails_available = + (menu_explore_set_playlist_thumbnail(entry.type, xmb->thumbnail_path_data) >= 0); } } #endif @@ -2423,6 +2401,12 @@ static void xmb_context_reset_horizontal_list( image_texture_free(&ti); } } + else if (string_ends_with_size(xmb->horizontal_list.list[i].label, ".lvw", + strlen(xmb->horizontal_list.list[i].label), STRLEN_CONST(".lvw"))) + { + /* For now use a default icon for views */ + node->icon = xmb->textures.list[XMB_TEXTURE_FILE]; + } } xmb_toggle_horizontal_list(xmb); @@ -2551,7 +2535,7 @@ static void xmb_populate_entries(void *data, const char *path, const char *label, unsigned k) { - unsigned xmb_system_tab; + unsigned xmb_system_tab, xmb_horizontal_type; xmb_handle_t *xmb = (xmb_handle_t*)data; settings_t *settings = config_get_ptr(); bool menu_dynamic_wallpaper_enable = @@ -2561,9 +2545,9 @@ static void xmb_populate_entries(void *data, unsigned depth = (unsigned)xmb_list_get_size(xmb, MENU_LIST_PLAIN); if (!xmb) return; + xmb_system_tab = xmb_get_system_tab(xmb, (unsigned)xmb->categories_selection_ptr); + xmb_horizontal_type = (xmb_system_tab == UINT_MAX ? xmb_get_horizontal_selection_type(xmb) : 0); /* Determine whether this is a playlist */ - xmb_system_tab = xmb_get_system_tab(xmb, - (unsigned)xmb->categories_selection_ptr); xmb->is_playlist = (depth == 1 && ((xmb_system_tab == XMB_SYSTEM_TAB_FAVORITES) || (xmb_system_tab == XMB_SYSTEM_TAB_HISTORY) @@ -2575,7 +2559,7 @@ static void xmb_populate_entries(void *data, || (xmb_system_tab == XMB_SYSTEM_TAB_VIDEO) #endif )) - || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HORIZONTAL_MENU)) + || (xmb_horizontal_type == FILE_TYPE_PLAYLIST_COLLECTION) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_PLAYLIST_LIST)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_FAVORITES_LIST)) || string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_IMAGES_LIST)) @@ -2614,34 +2598,20 @@ static void xmb_populate_entries(void *data, /* Explore list */ xmb->is_explore_list = string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_EXPLORE_LIST)) || - string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_TAB)); + string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_TAB)) || + xmb_horizontal_type == MENU_EXPLORE_TAB; - /* Quick Menu under Explore list must also be Quick Menu */ +#if defined(HAVE_LIBRETRODB) if (xmb->is_explore_list) { + /* Quick Menu under Explore list must also be Quick Menu */ xmb->is_quick_menu |= menu_is_nonrunning_quick_menu() || menu_is_running_quick_menu(); - if (xmb->is_quick_menu) - xmb->is_explore_list = false; - else + if (!menu_explore_is_content_list()) + xmb->is_explore_list = show_entry_idx = false; + else if (!xmb->is_quick_menu) xmb->skip_thumbnail_reset = true; } - - /* Determine the first playlist item under "Load Content > Playlists" */ - xmb->playlist_collection_offset = 0; - if (settings->uints.menu_content_show_add_entry) - xmb->playlist_collection_offset++; - if (settings->uints.menu_content_show_contentless_cores) - xmb->playlist_collection_offset++; - if (settings->bools.menu_content_show_explore) - xmb->playlist_collection_offset++; - if (settings->bools.menu_content_show_favorites) - xmb->playlist_collection_offset++; - if (settings->bools.menu_content_show_images) - xmb->playlist_collection_offset++; - if (settings->bools.menu_content_show_music) - xmb->playlist_collection_offset++; - if (settings->bools.menu_content_show_video) - xmb->playlist_collection_offset++; +#endif if (menu_driver_ctl(RARCH_MENU_CTL_IS_PREVENT_POPULATE, NULL)) { @@ -2662,7 +2632,7 @@ static void xmb_populate_entries(void *data, /* Determine whether to show entry index */ /* Update list size & entry index texts */ if ((xmb->entry_idx_enabled = show_entry_idx && - (xmb->is_playlist || (xmb->is_explore_list && xmb->depth > 1)))) + (xmb->is_playlist || xmb->is_explore_list))) { xmb->list_size = menu_entries_get_size(); snprintf(xmb->entry_index_str, sizeof(xmb->entry_index_str), @@ -3828,14 +3798,7 @@ static int xmb_draw_item( /* "Load Content" playlists */ if (xmb->depth == 3 && entry_type == FILE_TYPE_PLAYLIST_COLLECTION) { - size_t i_playlist = 0; - xmb_node_t *sidebar_node; - - if (i >= xmb->playlist_collection_offset) - i_playlist = i - xmb->playlist_collection_offset; - - sidebar_node = (xmb_node_t*) file_list_get_userdata_at_offset(&xmb->horizontal_list, i_playlist); - + xmb_node_t *sidebar_node = (xmb_node_t*) file_list_get_userdata_at_offset(&xmb->horizontal_list, list->list[i].entry_idx); if (sidebar_node && sidebar_node->icon) texture = sidebar_node->icon; } diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 18ef621872..e98c13f423 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -3352,6 +3352,13 @@ static int menu_displaylist_parse_horizontal_list( if (!item) return -1; + if (item->type == MENU_EXPLORE_TAB) + { + /* when opening a saved view the explore menu will handle the list */ + menu_displaylist_ctl(DISPLAYLIST_EXPLORE, info, settings); + return 0; + } + if (!string_is_empty(item->path)) { char lpl_basename[256]; @@ -4039,6 +4046,7 @@ static unsigned menu_displaylist_parse_playlists( size_t i, list_size; struct string_list str_list = {0}; unsigned count = 0; + unsigned content_count = 0; const char *path = info->path; bool show_hidden_files = settings->bools.show_hidden_files; @@ -4136,10 +4144,40 @@ static unsigned menu_displaylist_parse_playlists( show_hidden_files, true, false)) return count; + content_count = count; + dir_list_sort(&str_list, true); list_size = str_list.size; +#if defined(HAVE_LIBRETRODB) + if (settings->bools.menu_content_show_explore) + { + /* list any custom explore views above playlists */ + for (i = 0; i < list_size; i++) + { + char label[512]; + const char *path = str_list.elems[i].data; + const char *fname = path_basename(path); + const char *fext = path_get_extension(fname); + if (!string_is_equal_noncase(fext, "lvw")) + continue; + + snprintf(label, sizeof(label), "%s: %.*s", + msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_VIEW), + (int)(fext - 1 - fname), fname); + if (menu_entries_append(info->list, label, path, + MENU_ENUM_LABEL_GOTO_EXPLORE, + MENU_EXPLORE_TAB, 0, (count - content_count), NULL)) + { + menu_file_list_cbs_t *cbs = ((menu_file_list_cbs_t*)info->list->list[info->list->size-1].actiondata); + cbs->action_sublabel = NULL; + count++; + } + } + } +#endif + for (i = 0; i < list_size; i++) { const char *path = str_list.elems[i].data; @@ -4188,7 +4226,7 @@ static unsigned menu_displaylist_parse_playlists( if (menu_entries_append(info->list, path, "", MENU_ENUM_LABEL_PLAYLIST_COLLECTION_ENTRY, - file_type, 0, 0, NULL)) + file_type, 0, (count - content_count), NULL)) count++; } diff --git a/menu/menu_driver.h b/menu/menu_driver.h index 0be2838b18..3a20489b47 100644 --- a/menu/menu_driver.h +++ b/menu/menu_driver.h @@ -667,12 +667,12 @@ typedef struct explore_state explore_state_t; explore_state_t *menu_explore_build_list(const char *directory_playlist, const char *directory_database); uintptr_t menu_explore_get_entry_icon(unsigned type); -const char *menu_explore_get_entry_database(unsigned type); ssize_t menu_explore_get_entry_playlist_index(unsigned type, - playlist_t **playlist, - const struct playlist_entry **entry); -ssize_t menu_explore_set_entry_playlist_index(unsigned type, - gfx_thumbnail_path_data_t *thumbnail_path_data); + playlist_t **playlist, const struct playlist_entry **entry, + file_list_t *list, size_t *list_pos, size_t *list_size); +ssize_t menu_explore_set_playlist_thumbnail(unsigned type, + gfx_thumbnail_path_data_t *thumbnail_path_data); /* returns list index */ +bool menu_explore_is_content_list(); void menu_explore_context_init(void); void menu_explore_context_deinit(void); void menu_explore_free_state(explore_state_t *state); diff --git a/menu/menu_explore.c b/menu/menu_explore.c index 52733a30c2..83354ebdca 100644 --- a/menu/menu_explore.c +++ b/menu/menu_explore.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include "menu_driver.h" #include "menu_cbs.h" @@ -27,13 +29,10 @@ #include "../configuration.h" #include "../file_path_special.h" #include "../playlist.h" +#include "../verbosity.h" #include "../libretro-db/libretrodb.h" #include "../tasks/tasks_internal.h" -#define EX_ARENA_ALIGNMENT 8 -#define EX_ARENA_BLOCK_SIZE (64 * 1024) -#define EX_ARENA_ALIGN_UP(n, a) (((n) + (a) - 1) & ~((a) - 1)) - /* Explore */ enum { @@ -42,7 +41,6 @@ enum EXPLORE_BY_RELEASEYEAR, EXPLORE_BY_PLAYERCOUNT, EXPLORE_BY_GENRE, - EXPLORE_BY_ACHIEVEMENTS, EXPLORE_BY_CATEGORY, EXPLORE_BY_LANGUAGE, @@ -60,7 +58,6 @@ enum EXPLORE_BY_SETTING, EXPLORE_BY_VISUAL, EXPLORE_BY_VEHICULAR, - EXPLORE_BY_ORIGIN, EXPLORE_BY_REGION, EXPLORE_BY_FRANCHISE, @@ -68,11 +65,17 @@ enum EXPLORE_BY_SYSTEM, EXPLORE_CAT_COUNT, + EXPLORE_OP_EQUAL = 0, + EXPLORE_OP_MIN, + EXPLORE_OP_MAX, + EXPLORE_OP_RANGE, + EXPLORE_ICONS_OFF = 0, EXPLORE_ICONS_CONTENT = 1, EXPLORE_ICONS_SYSTEM_CATEGORY = 2, EXPLORE_TYPE_ADDITIONALFILTER = FILE_TYPE_RDB, /* database icon */ + EXPLORE_TYPE_VIEW = FILE_TYPE_PLAIN, /* file icon */ EXPLORE_TYPE_FILTERNULL = MENU_SETTINGS_LAST, EXPLORE_TYPE_SEARCH, EXPLORE_TYPE_SHOWALL, @@ -106,63 +109,77 @@ typedef struct struct explore_state { - ex_arena arena; /* ptr alignment */ + ex_arena arena; explore_string_t **by[EXPLORE_CAT_COUNT]; explore_entry_t *entries; playlist_t **playlists; uintptr_t *icons; const char *label_explore_item_str; - unsigned top_depth; - unsigned show_icons; char title[1024]; - char find_string[1024]; bool has_unknown[EXPLORE_CAT_COUNT]; - bool menu_initialised; + unsigned show_icons; + + unsigned view_levels; + char view_search[1024]; + uint8_t view_op[EXPLORE_CAT_COUNT]; + bool view_use_split[EXPLORE_CAT_COUNT]; + unsigned view_cats[EXPLORE_CAT_COUNT]; + explore_string_t* view_match[EXPLORE_CAT_COUNT]; + uint32_t view_idx_min[EXPLORE_CAT_COUNT]; + uint32_t view_idx_max[EXPLORE_CAT_COUNT]; }; static const struct { const char* rdbkey; enum msg_hash_enums name_enum, by_enum; - bool use_split, is_company, is_numeric; + bool use_split, is_company, is_numeric, is_boolean; } -explore_by_info[EXPLORE_CAT_COUNT] = +explore_by_info[EXPLORE_CAT_COUNT] = { - { "developer", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_DEVELOPER, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_DEVELOPER, true, true, false }, - { "publisher", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_PUBLISHER, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_PUBLISHER, true, true, false }, - { "releaseyear", MENU_ENUM_LABEL_VALUE_EXPLORE_CATEGORY_RELEASE_YEAR, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_RELEASE_YEAR, false, false, true }, - { "users", MENU_ENUM_LABEL_VALUE_EXPLORE_CATEGORY_PLAYER_COUNT, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_PLAYER_COUNT, false, false, true }, - { "genre", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_GENRE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_GENRE, true, false, false }, - - { "achievements", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ACHIEVEMENTS, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_ACHIEVEMENTS, false, false, true }, - { "category", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_CATEGORY, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_CATEGORY, true, false, false }, - { "language", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_LANGUAGE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_LANGUAGE, true, false, false }, - { "console_exclusive", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_CONSOLE_EXCLUSIVE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_CONSOLE_EXCLUSIVE, false, false, true }, - { "platform_exclusive", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_PLATFORM_EXCLUSIVE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_PLATFORM_EXCLUSIVE, false, false, true }, - { "rumble", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_RUMBLE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_RUMBLE, false, false, true }, - { "score", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_SCORE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_SCORE, true, false, false }, - { "media", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_MEDIA, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_MEDIA, true, false, false }, - { "controls", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_CONTROLS, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_CONTROLS, true, false, false }, - { "artstyle", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ARTSTYLE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_ARTSTYLE, true, false, false }, - { "gameplay", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_GAMEPLAY, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_GAMEPLAY, true, false, false }, - { "narrative", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_NARRATIVE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_NARRATIVE, true, false, false }, - { "pacing", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_PACING, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_PACING, true, false, false }, - { "perspective", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_PERSPECTIVE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_PERSPECTIVE, true, false, false }, - { "setting", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_SETTING, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_SETTING, true, false, false }, - { "visual", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_VISUAL, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_VISUAL, true, false, false }, - { "vehicular", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_VEHICULAR, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_VEHICULAR, true, false, false }, - - { "origin", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ORIGIN, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_ORIGIN, false, false, false }, - { "region", MENU_ENUM_LABEL_VALUE_EXPLORE_CATEGORY_REGION, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_REGION, false, false, false }, - { "franchise", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_FRANCHISE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_FRANCHISE, false, false, false }, - { "tags", MENU_ENUM_LABEL_VALUE_EXPLORE_CATEGORY_TAG, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_TAG, true, false, false }, - { "system", MENU_ENUM_LABEL_VALUE_CORE_INFO_SYSTEM_NAME, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_SYSTEM_NAME, false, false, false }, + { "developer", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_DEVELOPER, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_DEVELOPER, true, true, false, false }, + { "publisher", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_PUBLISHER, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_PUBLISHER, true, true, false, false }, + { "releaseyear", MENU_ENUM_LABEL_VALUE_EXPLORE_CATEGORY_RELEASE_YEAR, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_RELEASE_YEAR, false, false, true, false }, + { "users", MENU_ENUM_LABEL_VALUE_EXPLORE_CATEGORY_PLAYER_COUNT, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_PLAYER_COUNT, false, false, true, false }, + { "genre", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_GENRE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_GENRE, true, false, false, false }, + { "achievements", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ACHIEVEMENTS, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_ACHIEVEMENTS, false, false, false, true }, + { "category", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_CATEGORY, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_CATEGORY, true, false, false, false }, + { "language", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_LANGUAGE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_LANGUAGE, true, false, false, false }, + { "console_exclusive", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_CONSOLE_EXCLUSIVE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_CONSOLE_EXCLUSIVE, false, false, false, true }, + { "platform_exclusive", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_PLATFORM_EXCLUSIVE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_PLATFORM_EXCLUSIVE, false, false, false, true }, + { "rumble", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_RUMBLE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_RUMBLE, false, false, false, true }, + { "score", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_SCORE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_SCORE, true, false, false, false }, + { "media", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_MEDIA, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_MEDIA, true, false, false, false }, + { "controls", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_CONTROLS, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_CONTROLS, true, false, false, false }, + { "artstyle", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ARTSTYLE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_ARTSTYLE, true, false, false, false }, + { "gameplay", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_GAMEPLAY, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_GAMEPLAY, true, false, false, false }, + { "narrative", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_NARRATIVE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_NARRATIVE, true, false, false, false }, + { "pacing", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_PACING, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_PACING, true, false, false, false }, + { "perspective", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_PERSPECTIVE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_PERSPECTIVE, true, false, false, false }, + { "setting", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_SETTING, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_SETTING, true, false, false, false }, + { "visual", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_VISUAL, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_VISUAL, true, false, false, false }, + { "vehicular", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_VEHICULAR, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_VEHICULAR, true, false, false, false }, + { "origin", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ORIGIN, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_ORIGIN, false, false, false, false }, + { "region", MENU_ENUM_LABEL_VALUE_EXPLORE_CATEGORY_REGION, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_REGION, false, false, false, false }, + { "franchise", MENU_ENUM_LABEL_VALUE_RDB_ENTRY_FRANCHISE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_FRANCHISE, false, false, false, false }, + { "tags", MENU_ENUM_LABEL_VALUE_EXPLORE_CATEGORY_TAG, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_TAG, true, false, false, false }, + { "system", MENU_ENUM_LABEL_VALUE_CORE_INFO_SYSTEM_NAME, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_SYSTEM_NAME, false, false, false, false }, }; /* TODO/FIXME - static global */ static explore_state_t* explore_state; +#if defined(_MSC_VER) +#define EX_ALIGNOF(type) ((int)__alignof(type)) +#else +#define EX_ALIGNOF(type) ((int)__alignof__(type)) +#endif + +#define EX_ARENA_ALIGNMENT 8 +#define EX_ARENA_BLOCK_SIZE (64 * 1024) +#define EX_ARENA_ALIGN_UP(n, a) (((n) + (a) - 1) & ~((a) - 1)) + static void ex_arena_grow(ex_arena *arena, size_t min_size) { size_t size = EX_ARENA_ALIGN_UP( @@ -218,22 +235,28 @@ static uint32_t ex_hash32_nocase_filtered( return 1; } +static int explore_qsort_func_nums(const void *a_, const void *b_) +{ + const char *a = (*(const explore_string_t**)a_)->str; + const char *b = (*(const explore_string_t**)b_)->str; + return atoi(a) - atoi(b); +} + static int explore_qsort_func_strings(const void *a_, const void *b_) { - const explore_string_t **a = (const explore_string_t**)a_; - const explore_string_t **b = (const explore_string_t**)b_; - if ((*a)->str[0] != (*b)->str[0]) - return (unsigned char)(*a)->str[0] - (unsigned char)(*b)->str[0]; - return strcasecmp((*a)->str, (*b)->str); + const char *a = (*(const explore_string_t**)a_)->str; + const char *b = (*(const explore_string_t**)b_)->str; + int a0 = TOLOWER(a[0]), b0 = TOLOWER(b[0]); + return (a0 != b0 ? (a0 - b0) : strcasecmp(a, b)); } static int explore_qsort_func_entries(const void *a_, const void *b_) { - const explore_entry_t *a = (const explore_entry_t*)a_; - const explore_entry_t *b = (const explore_entry_t*)b_; - if (a->playlist_entry->label[0] != b->playlist_entry->label[0]) - return (unsigned char)a->playlist_entry->label[0] - (unsigned char)b->playlist_entry->label[0]; - return strcasecmp(a->playlist_entry->label, b->playlist_entry->label); + const char *a = ((const explore_entry_t*)a_)->playlist_entry->label; + const char *b = ((const explore_entry_t*)b_)->playlist_entry->label; + int a0 = TOLOWER(a[0]), b0 = TOLOWER(b[0]); + if (a0 != b0) return a0 - b0; + return strcasecmp(a, b); } static int explore_qsort_func_menulist(const void *a_, const void *b_) @@ -271,7 +294,7 @@ static int explore_check_company_suffix(const char* p, bool search_reverse) } static void explore_add_unique_string( - explore_state_t *explore, + explore_state_t *state, explore_string_t** maps[EXPLORE_CAT_COUNT], explore_entry_t *e, unsigned cat, const char *str, explore_string_t ***split_buf) @@ -281,7 +304,7 @@ static void explore_add_unique_string( const char *p_next; if (!str || !*str) { - explore->has_unknown[cat] = true; + state->has_unknown[cat] = true; return; } @@ -329,11 +352,11 @@ static void explore_add_unique_string( if (!entry) { entry = (explore_string_t*) - ex_arena_alloc(&explore->arena, + ex_arena_alloc(&state->arena, sizeof(explore_string_t) + len); memcpy(entry->str, str, len); entry->str[len] = '\0'; - RBUF_PUSH(explore->by[cat], entry); + RBUF_PUSH(state->by[cat], entry); RHMAP_SET(maps[cat], hash, entry); } @@ -429,11 +452,16 @@ explore_state_t *menu_explore_build_list(const char *directory_playlist, { unsigned i; char tmp[PATH_MAX_LENGTH]; + struct explore_source + { + const struct playlist_entry *source; + uint32_t entry_index, meta_count; + }; struct explore_rdb { libretrodb_t *handle; - const struct playlist_entry **playlist_crcs; - const struct playlist_entry **playlist_names; + struct explore_source *playlist_crcs; + struct explore_source *playlist_names; size_t count; char systemname[256]; } @@ -443,13 +471,12 @@ explore_state_t *menu_explore_build_list(const char *directory_playlist, explore_string_t **split_buf = NULL; libretro_vfs_implementation_dir *dir = NULL; - explore_state_t *explore = (explore_state_t*)calloc( - 1, sizeof(*explore)); + explore_state_t *state = (explore_state_t*)calloc(1, sizeof(*state)); - if (!explore) + if (!state) return NULL; - explore->label_explore_item_str = + state->label_explore_item_str = msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_ITEM); /* Index all playlists */ @@ -495,6 +522,7 @@ explore_state_t *menu_explore_build_list(const char *directory_playlist, { int rdb_num; uint32_t entry_crc32; + struct explore_source src = { NULL, (uint32_t)-1, 0 }; struct explore_rdb* rdb = NULL; const struct playlist_entry *entry = NULL; const char *db_name = fname; @@ -572,19 +600,20 @@ explore_state_t *menu_explore_build_list(const char *directory_playlist, rdb->count++; entry_crc32 = (uint32_t)strtoul( (entry->crc32 ? entry->crc32 : ""), NULL, 16); + src.source = entry; if (entry_crc32) { - RHMAP_SET(rdb->playlist_crcs, entry_crc32, entry); + RHMAP_SET(rdb->playlist_crcs, entry_crc32, src); } else { - RHMAP_SET_STR(rdb->playlist_names, entry->label, entry); + RHMAP_SET_STR(rdb->playlist_names, entry->label, src); } used_entries++; } if (used_entries) - RBUF_PUSH(explore->playlists, playlist); + RBUF_PUSH(state->playlists, playlist); else playlist_free(playlist); } @@ -605,15 +634,16 @@ explore_state_t *menu_explore_build_list(const char *directory_playlist, libretrodb_cursor_read_item(cur, &item) == 0)) { unsigned k, l, cat; - explore_entry_t e; - char *fields[EXPLORE_CAT_COUNT]; + explore_entry_t* e; + const char *fields[EXPLORE_CAT_COUNT]; char numeric_buf[EXPLORE_CAT_COUNT][16]; - const struct playlist_entry *entry = NULL; uint32_t crc32 = 0; + uint32_t meta_count = 0; char *name = NULL; #ifdef EXPLORE_SHOW_ORIGINAL_TITLE char *original_title = NULL; #endif + struct explore_source* src = NULL; if (item.type != RDT_MAP) continue; @@ -668,9 +698,10 @@ explore_state_t *menu_explore_build_list(const char *directory_playlist, if (!string_is_equal(key_str, explore_by_info[cat].rdbkey)) continue; + meta_count++; if (explore_by_info[cat].is_numeric) { - if (!val->val.int_) + if (val->type >= RDT_STRING) break; snprintf(numeric_buf[cat], sizeof(numeric_buf[cat]), @@ -678,6 +709,14 @@ explore_state_t *menu_explore_build_list(const char *directory_playlist, fields[cat] = numeric_buf[cat]; break; } + if (explore_by_info[cat].is_boolean) + { + if (val->type >= RDT_STRING) + break; + fields[cat] = msg_hash_to_str(val->val.int_ ? + MENU_ENUM_LABEL_VALUE_YES : MENU_ENUM_LABEL_VALUE_NO); + break; + } if (val->type != RDT_STRING) break; fields[cat] = val->val.string.buff; @@ -687,39 +726,50 @@ explore_state_t *menu_explore_build_list(const char *directory_playlist, if (crc32) { - entry = RHMAP_GET(rdb->playlist_crcs, crc32); + ptrdiff_t idx = RHMAP_IDX(rdb->playlist_crcs, crc32); + src = (idx != -1 ? &rdb->playlist_crcs[idx] : NULL); } - if (!entry && name) + if (!src && name) { - entry = RHMAP_GET_STR(rdb->playlist_names, name); + ptrdiff_t idx = RHMAP_IDX_STR(rdb->playlist_names, name); + src = (idx != -1 ? &rdb->playlist_names[idx] : NULL); } - if (!entry) + if (!src) + continue; + if (src->entry_index != (uint32_t)-1 && src->meta_count >= meta_count) continue; - e.playlist_entry = entry; + if (src->entry_index == (uint32_t)-1) + { + src->entry_index = RBUF_LEN(state->entries); + RBUF_RESIZE(state->entries, src->entry_index + 1); + } + e = &state->entries[src->entry_index]; + src->meta_count = meta_count; + e->playlist_entry = src->source; for (l = 0; l < EXPLORE_CAT_COUNT; l++) - e.by[l] = NULL; - e.split = NULL; + e->by[l] = NULL; + e->split = NULL; #ifdef EXPLORE_SHOW_ORIGINAL_TITLE - e.original_title = NULL; + e->original_title = NULL; #endif fields[EXPLORE_BY_SYSTEM] = rdb->systemname; for (cat = 0; cat != EXPLORE_CAT_COUNT; cat++) { - explore_add_unique_string(explore, - cat_maps, &e, cat, + explore_add_unique_string(state, + cat_maps, e, cat, fields[cat], &split_buf); } #ifdef EXPLORE_SHOW_ORIGINAL_TITLE if (original_title && *original_title) { - size_t len = strlen(original_title) + 1; - e.original_title = (char*) - ex_arena_alloc(&explore->arena, len); - memcpy(e.original_title, original_title, len); + size_t len = strlen(original_title) + 1; + e->original_title = (char*) + ex_arena_alloc(&state->arena, len); + memcpy(e->original_title, original_title, len); } #endif @@ -729,14 +779,12 @@ explore_state_t *menu_explore_build_list(const char *directory_playlist, RBUF_PUSH(split_buf, NULL); /* terminator */ len = RBUF_SIZEOF(split_buf); - e.split = (explore_string_t **) - ex_arena_alloc(&explore->arena, len); - memcpy(e.split, split_buf, len); + e->split = (explore_string_t **) + ex_arena_alloc(&state->arena, len); + memcpy(e->split, split_buf, len); RBUF_CLEAR(split_buf); } - RBUF_PUSH(explore->entries, e); - /* if all entries have found connections, we can leave early */ if (--rdb->count == 0) { @@ -759,38 +807,32 @@ explore_state_t *menu_explore_build_list(const char *directory_playlist, for (i = 0; i != EXPLORE_CAT_COUNT; i++) { uint32_t idx; - size_t len = RBUF_LEN(explore->by[i]); + size_t len = RBUF_LEN(state->by[i]); - if (explore->by[i]) - qsort(explore->by[i], len, sizeof(*explore->by[i]), - explore_qsort_func_strings); + if (state->by[i]) + qsort(state->by[i], len, sizeof(*state->by[i]), + (explore_by_info[i].is_numeric ? + explore_qsort_func_nums : explore_qsort_func_strings)); for (idx = 0; idx != len; idx++) - explore->by[i][idx]->idx = idx; + state->by[i][idx]->idx = idx; RHMAP_FREE(cat_maps[i]); } /* NULL is not a valid value as a first argument for qsort */ - if (explore->entries) - qsort(explore->entries, - RBUF_LEN(explore->entries), - sizeof(*explore->entries), explore_qsort_func_entries); - return explore; -} - -static int explore_action_get_title_default( - const char *path, const char *label, - unsigned menu_type, char *s, size_t len) -{ - strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_TAB), len); - return 0; + if (state->entries) + qsort(state->entries, + RBUF_LEN(state->entries), + sizeof(*state->entries), explore_qsort_func_entries); + return state; } static int explore_action_get_title( const char *path, const char *label, unsigned menu_type, char *s, size_t len) { - strlcpy(s, explore_state->title, len); + strlcpy(s, (explore_state ? explore_state->title : + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_TAB)), len); return 0; } @@ -830,27 +872,89 @@ static int explore_action_ok(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) { const char* explore_tab = msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_TAB); + if (type >= EXPLORE_TYPE_FIRSTITEM || type == EXPLORE_TYPE_FILTERNULL) + { + file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); + unsigned prev_type = menu_stack->list[menu_stack->size - 1].type; + unsigned cat = (prev_type - EXPLORE_TYPE_FIRSTCATEGORY); + if (cat < EXPLORE_CAT_COUNT) + { + explore_state_t *state = explore_state; + unsigned lvl, lvl_max = state->view_levels; + for (lvl = 0; lvl != lvl_max; lvl++) + if (state->view_cats[lvl] == cat) + break; + if (lvl == lvl_max) + { + /* new matching filter */ + state->view_op[lvl] = EXPLORE_OP_EQUAL; + state->view_use_split[lvl] = explore_by_info[cat].use_split; + state->view_cats[lvl] = cat; + state->view_match[lvl] = (type == EXPLORE_TYPE_FILTERNULL ? + NULL : state->by[cat][type-EXPLORE_TYPE_FIRSTITEM]); + state->view_levels++; + } + else + { + /* switch from match to filter range */ + explore_string_t* r1 = state->view_match[lvl]; + explore_string_t* r2 = state->by[cat][type-EXPLORE_TYPE_FIRSTITEM]; + state->view_op[lvl] = EXPLORE_OP_RANGE; + state->view_idx_min[lvl] = (r1->idx < r2->idx ? r1->idx : r2->idx); + state->view_idx_max[lvl] = (r1->idx < r2->idx ? r2->idx : r1->idx); + } + } + } filebrowser_clear_type(); return generic_action_ok_displaylist_push(explore_tab, NULL, explore_tab, type, idx, entry_idx, ACTION_OK_DL_PUSH_DEFAULT); } -static menu_file_list_cbs_t *explore_menu_entry( - file_list_t *list, - explore_state_t *state, - const char *path, unsigned type) +static int explore_cancel(const char *path, + const char *label, unsigned type, size_t idx) { - menu_file_list_cbs_t *cbs = NULL; - if (!state) - return NULL; - menu_entries_append(list, path, - state->label_explore_item_str, - MENU_ENUM_LABEL_EXPLORE_ITEM, type, 0, 0, NULL); - cbs = ((menu_file_list_cbs_t*)list->list[list->size-1].actiondata); + file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); + unsigned closed_type = menu_stack->list[menu_stack->size - 1].type; + if (closed_type >= EXPLORE_TYPE_FIRSTITEM || + closed_type == EXPLORE_TYPE_FILTERNULL) + { + /* popping one filter, check if popping filter range or match */ + unsigned prev_type = menu_stack->list[menu_stack->size - 2].type; + unsigned cat = (prev_type - EXPLORE_TYPE_FIRSTCATEGORY); + unsigned lvl, lvl_max = explore_state->view_levels; + for (lvl = 0; lvl != lvl_max; lvl++) + { + if (explore_state->view_cats[lvl] != cat) continue; + if (explore_state->view_op[lvl] == EXPLORE_OP_EQUAL) + explore_state->view_levels--; + else + { + explore_state->view_op[lvl] = EXPLORE_OP_EQUAL; + if (!explore_state->view_match[lvl]) + explore_state->view_match[lvl] = + explore_state->by[cat][explore_state->view_idx_min[lvl]]; + } + break; + } + } + else if (closed_type == EXPLORE_TYPE_SEARCH) + explore_state->view_search[0] = '\0'; + return action_cancel_pop_default(path, label, type, idx); +} + +void explore_menu_entry(file_list_t *list, explore_state_t *state, + const char *path, unsigned type, int (*action_ok)(const char *path, + const char *label, unsigned type, size_t idx, size_t entry_idx)) +{ + menu_file_list_cbs_t *cbs; + if (!menu_entries_append(list, path, state->label_explore_item_str, + MENU_ENUM_LABEL_EXPLORE_ITEM, type, 0, 0, NULL)) + return; + cbs = ((menu_file_list_cbs_t*)list->list[list->size-1].actiondata); if (!cbs) - return NULL; - cbs->action_ok = explore_action_ok; - return cbs; + return; + cbs->action_ok = action_ok; + cbs->action_cancel = explore_cancel; } static void explore_menu_add_spacer(file_list_t *list) @@ -864,8 +968,8 @@ static void explore_action_find_complete(void *userdata, const char *line) menu_input_dialog_end(); if (line && *line) { - strlcpy(explore_state->find_string, line, - sizeof(explore_state->find_string)); + strlcpy(explore_state->view_search, line, + sizeof(explore_state->view_search)); explore_action_ok(NULL, NULL, EXPLORE_TYPE_SEARCH, 0, 0); } } @@ -882,16 +986,328 @@ static int explore_action_ok_find(const char *path, const char *label, unsigned return 0; } -unsigned menu_displaylist_explore(file_list_t *list, - settings_t *settings) +static const char* explore_get_view_path() { - unsigned i, cat; - char tmp[512]; - unsigned depth, current_type, current_cat, previous_cat; - struct item_file *stack_top = NULL; - file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); + file_list_t *menu_stack = menu_entries_get_menu_stack_ptr(0); + struct item_file *cur = (struct item_file *)&menu_stack->list[menu_stack->size - 1]; - if (!explore_state) + /* check if we are opening a saved view from the horizontal/tabs menu */ + if (cur->type == MENU_SETTING_HORIZONTAL_MENU) + { + menu_ctx_list_t tabs, horizontal; + tabs.type = MENU_LIST_TABS; + if (menu_driver_list_get_selection(&tabs) && menu_driver_list_get_size(&tabs)) + { + horizontal.type = MENU_LIST_HORIZONTAL; + horizontal.idx = tabs.selection - (tabs.size + 1); + if (menu_driver_list_get_entry(&horizontal)) + { + /* label contains the path and path contains the label */ + return ((struct item_file*)horizontal.entry)->label; + } + } + } + + /* check if we are opening a saved view via Content > Playlists */ + if (cur->type == MENU_EXPLORE_TAB && cur->path && !string_is_equal(cur->path, + msg_hash_to_str(MENU_ENUM_LABEL_GOTO_EXPLORE))) + { + return cur->path; + } + + return NULL; +} + +static void explore_on_edit_views(enum msg_hash_enums msg) +{ + menu_ctx_environment_t menu_environ; + menu_environ.type = MENU_ENVIRON_NONE; + menu_environ.data = NULL; + menu_environ.type = MENU_ENVIRON_RESET_HORIZONTAL_LIST; + menu_driver_ctl(RARCH_MENU_CTL_ENVIRONMENT, &menu_environ); + + runloop_msg_queue_push(msg_hash_to_str(msg), + 1, 180, true, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_INFO); +} + +static int explore_action_ok_deleteview(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) +{ + filestream_delete(explore_get_view_path()); + explore_on_edit_views(MENU_ENUM_LABEL_EXPLORE_VIEW_DELETED); + + if (menu_entries_get_menu_stack_ptr(0)->size == 1) + { + /* if we're at the top of the menu we can't cancel so just refresh + what becomes selected after MENU_ENVIRON_RESET_HORIZONTAL_LIST. */ + bool refresh_nonblocking = false; + menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh_nonblocking); + } + + return explore_cancel(path, label, type, idx); +} + +static void explore_action_saveview_complete(void *userdata, const char *name) +{ + int count = 0, op; + char lvwpath[PATH_MAX_LENGTH]; + intfstream_t *file; + rjsonwriter_t* w; + explore_state_t *state = explore_state; + + menu_input_dialog_end(); + if (!name || !*name) return; + + fill_pathname_join_special(lvwpath, + config_get_ptr()->paths.directory_playlist, name, sizeof(lvwpath)); + strlcat(lvwpath, ".lvw", sizeof(lvwpath)); + + if (filestream_exists(lvwpath)) + { + runloop_msg_queue_push(msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_VIEW_EXISTS), + 1, 360, true, NULL, + MESSAGE_QUEUE_ICON_DEFAULT, MESSAGE_QUEUE_CATEGORY_ERROR); + return; + } + + file = intfstream_open_file(lvwpath, + RETRO_VFS_FILE_ACCESS_WRITE, RETRO_VFS_FILE_ACCESS_HINT_NONE); + if (!file) + { + RARCH_ERR("[explore view] Failed to write json file %s.\n", lvwpath); + return; + } + + w = rjsonwriter_open_stream(file); + + rjsonwriter_add_start_object(w); + + if (state->view_search[0]) + { + rjsonwriter_add_newline(w); + rjsonwriter_add_tabs(w, 1); + rjsonwriter_add_string(w, "filter_name"); + rjsonwriter_add_colon(w); + rjsonwriter_add_space(w); + rjsonwriter_add_string(w, state->view_search); + count++; + } + + for (op = EXPLORE_OP_EQUAL; op <= EXPLORE_OP_MAX; op++) + { + unsigned i, n; + for (i = n = 0; i != state->view_levels; i++) + { + uint8_t vop = state->view_op[i]; + unsigned vcat = state->view_cats[i]; + explore_string_t **by = state->by[vcat]; + if (vop != op && (vop != EXPLORE_OP_RANGE || op == EXPLORE_OP_EQUAL)) + continue; + if (n++ == 0) + { + if (count++) rjsonwriter_add_comma(w); + rjsonwriter_add_newline(w); + rjsonwriter_add_tabs(w, 1); + rjsonwriter_add_string(w, + (op == EXPLORE_OP_EQUAL ? "filter_equal" : + (op == EXPLORE_OP_MIN ? "filter_min" : + (op == EXPLORE_OP_MAX ? "filter_max" : "")))); + rjsonwriter_add_colon(w); + rjsonwriter_add_space(w); + rjsonwriter_add_start_object(w); + } + if (n > 1) rjsonwriter_add_comma(w); + rjsonwriter_add_newline(w); + rjsonwriter_add_tabs(w, 2); + rjsonwriter_add_string(w, explore_by_info[vcat].rdbkey); + rjsonwriter_add_colon(w); + rjsonwriter_add_space(w); + rjsonwriter_add_string(w, + ((op == EXPLORE_OP_EQUAL && !state->view_match[i]) ? "" : + (op == EXPLORE_OP_EQUAL ? state->view_match[i]->str : + (op == EXPLORE_OP_MIN ? by[state->view_idx_min[i]]->str : + (op == EXPLORE_OP_MAX ? by[state->view_idx_max[i]]->str : + ""))))); + } + if (n) + { + rjsonwriter_add_newline(w); + rjsonwriter_add_tabs(w, 1); + rjsonwriter_add_end_object(w); + } + } + rjsonwriter_add_newline(w); + rjsonwriter_add_end_object(w); + rjsonwriter_add_newline(w); + rjsonwriter_free(w); + intfstream_close(file); + free(file); + + explore_on_edit_views(MENU_ENUM_LABEL_EXPLORE_VIEW_SAVED); +} + +static int explore_action_ok_saveview(const char *path, const char *label, unsigned type, size_t idx, size_t entry_idx) +{ + menu_input_ctx_line_t line = {0}; + line.label = msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_NEW_VIEW); + line.cb = explore_action_saveview_complete; + menu_input_dialog_start(&line); + return 0; +} + +static void explore_load_view(explore_state_t *state, const char* path) +{ + intfstream_t *file; + rjson_t* json; + uint8_t op = ((uint8_t)-1); + unsigned cat; + enum rjson_type type; + + state->view_levels = 0; + state->view_search[0] = '\0'; + + file = intfstream_open_file(path, + RETRO_VFS_FILE_ACCESS_READ, RETRO_VFS_FILE_ACCESS_HINT_NONE); + + if (!file) + return; + + json = rjson_open_stream(file); + + /* Configure parser */ + rjson_set_options(json, + RJSON_OPTION_ALLOW_UTF8BOM|RJSON_OPTION_ALLOW_COMMENTS); + + while ((type = rjson_next(json)) != RJSON_DONE && type != RJSON_ERROR) + { + unsigned int depth = rjson_get_context_depth(json); + if (depth == 1 && type == RJSON_STRING) + { + const char* key = rjson_get_string(json, NULL); + if (string_is_equal(key, "filter_name") && + rjson_next(json) == RJSON_STRING) + strlcpy(state->view_search, + rjson_get_string(json, NULL), sizeof(state->view_search)); + else if (string_is_equal(key, "filter_equal") && + rjson_next(json) == RJSON_OBJECT) + op = EXPLORE_OP_EQUAL; + else if (string_is_equal(key, "filter_min") && + rjson_next(json) == RJSON_OBJECT) + op = EXPLORE_OP_MIN; + else if (string_is_equal(key, "filter_max") && + rjson_next(json) == RJSON_OBJECT) + op = EXPLORE_OP_MAX; + } + else if (depth == 2 && type == RJSON_STRING && op != ((uint8_t)-1)) + { + const char* key = rjson_get_string(json, NULL); + for (cat = 0; cat != EXPLORE_CAT_COUNT; cat++) + if (string_is_equal(key, explore_by_info[cat].rdbkey)) + break; + if (cat == EXPLORE_CAT_COUNT) + rjson_next(json); /* skip value */ + else + { + explore_string_t **entries = state->by[cat]; + const char* value = NULL; + unsigned lvl, lvl_max = state->view_levels; + uint8_t valid_op = ((uint8_t)-1); + + type = rjson_next(json); + if (type == RJSON_STRING || type == RJSON_NUMBER) + value = rjson_get_string(json, NULL); + if (value && !*value) + value = NULL; + + for (lvl = 0; lvl != lvl_max; lvl++) + if (state->view_cats[lvl] == cat) + break; + + if (!value && state->has_unknown[cat] && op == EXPLORE_OP_EQUAL) + { + state->view_match[lvl] = NULL; + valid_op = EXPLORE_OP_EQUAL; + } + else if (value && entries) + { + /* use existing qsort function for binary search */ + explore_string_t *evalue = (explore_string_t *) + (value - offsetof(explore_string_t, str)); + uint32_t i, ifrom, ito, imax = (uint32_t)RBUF_LEN(entries); + int cmp; + int (*compare_func)(const void *, const void *) = + (explore_by_info[cat].is_numeric + ? explore_qsort_func_nums + : explore_qsort_func_strings); + + /* binary search index where entry <= value */ + for (i = 0, ifrom = 0, ito = imax, cmp = 1; ito && cmp;) + { + size_t remain = ito % 2; + ito = ito / 2; + i = ifrom + ito; + cmp = compare_func(&evalue, &entries[i]); + ifrom = (cmp < 0 ? ifrom : (i + remain)); + } + + if (op == EXPLORE_OP_EQUAL && !cmp) + { + state->view_match[lvl] = entries[i]; + valid_op = EXPLORE_OP_EQUAL; + } + else if (op == EXPLORE_OP_MIN) + { + state->view_idx_min[lvl] = (cmp ? i + 1 : i); + valid_op = ((lvl != lvl_max && state->view_op[lvl] == EXPLORE_OP_MAX) + ? EXPLORE_OP_RANGE : EXPLORE_OP_MIN); + } + else if (op == EXPLORE_OP_MAX) + { + state->view_idx_max[lvl] = i; + valid_op = ((lvl != lvl_max && state->view_op[lvl] == EXPLORE_OP_MIN) + ? EXPLORE_OP_RANGE : EXPLORE_OP_MAX); + } + } + if (valid_op != ((uint8_t)-1)) + { + state->view_op [lvl] = valid_op; + state->view_cats [lvl] = cat; + state->view_use_split[lvl] = explore_by_info[cat].use_split; + if (lvl == lvl_max) state->view_levels++; + } + } + } + else if (depth == 1 && type == RJSON_OBJECT_END) + { + op = ((uint8_t)-1); + } + } + rjson_free(json); + intfstream_close(file); + free(file); +} + +unsigned menu_displaylist_explore(file_list_t *list, settings_t *settings) +{ + unsigned i; + char tmp[512]; + struct explore_state *state = explore_state; + struct file_list *menu_stack = menu_entries_get_menu_stack_ptr(0); + struct item_file *stack_top = menu_stack->list; + size_t depth = menu_stack->size; + unsigned current_type = (depth > 0 ? stack_top[depth - 1].type : 0); + unsigned previous_type = (depth > 1 ? stack_top[depth - 2].type : 0); + unsigned current_cat = current_type - EXPLORE_TYPE_FIRSTCATEGORY; + + if (depth > 1) + { + /* overwrite the menu title function with our custom one */ + /* depth 1 is never popped so we can only do this on sub menus */ + ((menu_file_list_cbs_t*)stack_top[depth - 1].actiondata) + ->action_get_title = explore_action_get_title; + } + + if (!state) { if (!menu_explore_init_in_progress(NULL)) task_push_menu_explore_init( @@ -904,268 +1320,273 @@ unsigned menu_displaylist_explore(file_list_t *list, MENU_ENUM_LABEL_EXPLORE_INITIALISING_LIST, FILE_TYPE_NONE, 0, 0, NULL); - if (menu_stack->size > 1) - { - struct item_file *stack = &menu_stack->list[menu_stack->size - 1]; - menu_file_list_cbs_t* cbs = ((menu_file_list_cbs_t*)stack->actiondata); - cbs->action_get_title = explore_action_get_title_default; - } - return (unsigned)list->size; } - if (!explore_state->menu_initialised) + /* check if we are opening a saved view */ + if (current_type == MENU_SETTING_HORIZONTAL_MENU || current_type == MENU_EXPLORE_TAB) { - explore_state->top_depth = (unsigned)menu_stack->size - 1; - explore_load_icons(explore_state); - explore_state->menu_initialised = true; - } - - if (menu_stack->size > 1) - { - struct item_file *stack = &menu_stack->list[menu_stack->size - 1]; - menu_file_list_cbs_t* cbs = ((menu_file_list_cbs_t*)stack->actiondata); - cbs->action_get_title = explore_action_get_title; - } - - stack_top = menu_stack->list + explore_state->top_depth; - depth = (unsigned)menu_stack->size - 1 - explore_state->top_depth; - current_type = stack_top[depth].type; - current_cat = current_type - EXPLORE_TYPE_FIRSTCATEGORY; - previous_cat = stack_top[depth ? depth - 1 : 0].type - EXPLORE_TYPE_FIRSTCATEGORY; - explore_state->show_icons = EXPLORE_ICONS_OFF; - - if (depth) - { - unsigned levels = 0; - bool clear_find_text = false; - ((menu_file_list_cbs_t*)stack_top[depth].actiondata)->action_get_title = - explore_action_get_title; - - clear_find_text = (current_type != EXPLORE_TYPE_SEARCH); - explore_state->title[0] = '\0'; - - for (i = 1; i < depth; i++) + const char* view_path = explore_get_view_path(); + if (view_path) { - unsigned by_selected_type; - unsigned by_category = (stack_top[i].type - - EXPLORE_TYPE_FIRSTCATEGORY); - - if (stack_top[i].type == EXPLORE_TYPE_SEARCH) - clear_find_text = false; - if (by_category >= EXPLORE_CAT_COUNT) - continue; - - by_selected_type = stack_top[i + 1].type; - explore_append_title(explore_state, "%s%s: %s", - (levels++ ? " / " : ""), - msg_hash_to_str(explore_by_info[by_category].name_enum), - (by_selected_type != EXPLORE_TYPE_FILTERNULL ? - explore_state->by[by_category][by_selected_type - EXPLORE_TYPE_FIRSTITEM]->str - : msg_hash_to_str(MENU_ENUM_LABEL_VALUE_UNKNOWN))); + explore_load_view(state, view_path); + current_type = EXPLORE_TYPE_VIEW; } - - if (clear_find_text) - explore_state->find_string[0] = '\0'; - - if (*explore_state->find_string) - explore_append_title(explore_state, "%s%s: '%s'", - (levels++ ? " / " : ""), - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_NAME), - explore_state->find_string); } + /* clear any filter remaining from showing a view on the horizontal menu */ + if (current_type == MENU_EXPLORE_TAB) + { + state->view_levels = 0; + state->view_search[0] = '\0'; + } + + /* clear title string */ + state->title[0] = '\0'; + + /* append filtered categories to title */ + for (i = 0; i != state->view_levels; i++) + { + unsigned cat = state->view_cats[i]; + explore_string_t **entries = state->by[cat]; + explore_string_t* match = state->view_match[i]; + explore_append_title(state, "%s%s: ", (i ? " / " : ""), + msg_hash_to_str(explore_by_info[cat].name_enum)); + switch (state->view_op[i]) + { + case EXPLORE_OP_EQUAL: + explore_append_title(state, "%s", (match ? match->str + : msg_hash_to_str(MENU_ENUM_LABEL_VALUE_UNKNOWN))); + break; + case EXPLORE_OP_MIN: + explore_append_title(state, "%s - %s", + entries[state->view_idx_min[i]]->str, + entries[RBUF_LEN(entries)-1]->str); + break; + case EXPLORE_OP_MAX: + explore_append_title(state, "%s - %s", + entries[0]->str, + entries[state->view_idx_max[i]]->str); + break; + case EXPLORE_OP_RANGE: + explore_append_title(state, "%s - %s", + entries[state->view_idx_min[i]]->str, + entries[state->view_idx_max[i]]->str); + break; + } + } + + /* append string search to title */ + if (*state->view_search) + explore_append_title(state, "%s%s: '%s'", + (state->view_levels ? " / " : ""), + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_NAME), + state->view_search); + + state->show_icons = (current_cat == EXPLORE_BY_SYSTEM + ? EXPLORE_ICONS_SYSTEM_CATEGORY : EXPLORE_ICONS_OFF); + if ( current_type == MENU_EXPLORE_TAB || current_type == EXPLORE_TYPE_ADDITIONALFILTER) { - /* Explore top or selecting an additional filter */ + /* Explore top or selecting an additional filter category */ + unsigned cat; bool is_top = (current_type == MENU_EXPLORE_TAB); if (is_top) - strlcpy(explore_state->title, + strlcpy(state->title, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_TAB), - sizeof(explore_state->title)); + sizeof(state->title)); else - explore_append_title(explore_state, " - %s", + explore_append_title(state, " / %s", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_ADDITIONAL_FILTER)); - if (is_top || !*explore_state->find_string) + if (!*state->view_search) { - menu_file_list_cbs_t *new_cbs = explore_menu_entry( - list, explore_state, + explore_menu_entry( + list, state, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_SEARCH_NAME), - EXPLORE_TYPE_SEARCH); - if (new_cbs) - new_cbs->action_ok = explore_action_ok_find; + EXPLORE_TYPE_SEARCH, explore_action_ok_find); explore_menu_add_spacer(list); } for (cat = 0; cat < EXPLORE_CAT_COUNT; cat++) { - explore_string_t **entries = explore_state->by[cat]; - size_t tmplen; - + explore_string_t **entries = state->by[cat]; if (!RBUF_LEN(entries)) continue; - for (i = 1; i < depth; i++) - if (stack_top[i].type == cat + EXPLORE_TYPE_FIRSTCATEGORY) - goto SKIP_EXPLORE_BY_CATEGORY; + /* don't list already filtered categories unless it can be filtered by range */ + for (i = 0; i != state->view_levels; i++) + if (state->view_cats[i] == cat) + break; - tmplen = strlcpy(tmp, - msg_hash_to_str(explore_by_info[cat].by_enum), sizeof(tmp)); - - if (is_top && tmplen < sizeof(tmp) - 5) + if (i == state->view_levels || (i == state->view_levels - 1 + && state->view_op[i] == EXPLORE_OP_EQUAL + && !explore_by_info[cat].is_boolean + && RBUF_LEN(state->by[cat]) > 1)) { - if (explore_by_info[cat].is_numeric) - snprintf(tmp + tmplen, sizeof(tmp) - tmplen, " (%s - %s)", - entries[0]->str, entries[RBUF_LEN(entries) - 1]->str); - else + size_t tmplen = strlcpy(tmp, + msg_hash_to_str(explore_by_info[cat].by_enum), sizeof(tmp)); + + if (is_top) + { + if (explore_by_info[cat].is_numeric) + snprintf(tmp + tmplen, sizeof(tmp) - tmplen, " (%s - %s)", + entries[0]->str, entries[RBUF_LEN(entries) - 1]->str); + else if (!explore_by_info[cat].is_boolean) + { + strlcat(tmp, " (", sizeof(tmp)); + snprintf(tmp + tmplen + 2, sizeof(tmp) - tmplen - 2, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_ITEMS_COUNT), + (unsigned)RBUF_LEN(entries)); + strlcat(tmp, ")", sizeof(tmp)); + } + } + else if (i != state->view_levels) { strlcat(tmp, " (", sizeof(tmp)); - snprintf(tmp + tmplen + 2, sizeof(tmp) - tmplen - 2, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_ITEMS_COUNT), - (unsigned)RBUF_LEN(entries)); + strlcat(tmp, msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_RANGE_FILTER), sizeof(tmp)); strlcat(tmp, ")", sizeof(tmp)); } + + explore_menu_entry(list, state, + tmp, cat + EXPLORE_TYPE_FIRSTCATEGORY, explore_action_ok); } - - explore_menu_entry(list, explore_state, - tmp, cat + EXPLORE_TYPE_FIRSTCATEGORY); - -SKIP_EXPLORE_BY_CATEGORY:; } if (is_top) { explore_menu_add_spacer(list); - explore_menu_entry(list, explore_state, + explore_menu_entry(list, state, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_SHOW_ALL), - EXPLORE_TYPE_SHOWALL); + EXPLORE_TYPE_SHOWALL, explore_action_ok); } } - else if ( - depth == 1 - && current_type != EXPLORE_TYPE_SEARCH - && current_type != EXPLORE_TYPE_SHOWALL) + else if ((!state->view_levels && !*state->view_search + && current_cat < EXPLORE_CAT_COUNT)) { - /* List all items in a selected explore by category */ - explore_string_t **entries = explore_state->by[current_cat]; + /* Unfiltered list of all items in a selected explore by category */ + explore_string_t **entries = state->by[current_cat]; size_t i_last = RBUF_LEN(entries) - 1; for (i = 0; i <= i_last; i++) - explore_menu_entry(list, explore_state, - entries[i]->str, EXPLORE_TYPE_FIRSTITEM + i); + explore_menu_entry(list, state, + entries[i]->str, EXPLORE_TYPE_FIRSTITEM + i, explore_action_ok); - if (explore_state->has_unknown[current_cat]) + if (state->has_unknown[current_cat]) { explore_menu_add_spacer(list); - explore_menu_entry(list, explore_state, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_UNKNOWN), EXPLORE_TYPE_FILTERNULL); + explore_menu_entry(list, state, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_UNKNOWN), + EXPLORE_TYPE_FILTERNULL, explore_action_ok); } - explore_append_title(explore_state, + explore_append_title(state, "%s", msg_hash_to_str(explore_by_info[current_cat].by_enum)); - - if (current_cat == EXPLORE_BY_SYSTEM) - explore_state->show_icons = EXPLORE_ICONS_SYSTEM_CATEGORY; } - else if ( - previous_cat < EXPLORE_CAT_COUNT - || current_type < EXPLORE_TYPE_FIRSTITEM) + else if (current_type < EXPLORE_TYPE_FIRSTITEM + || (previous_type >= EXPLORE_TYPE_FIRSTCATEGORY + && previous_type < EXPLORE_TYPE_FIRSTITEM)) { - bool use_split[10]; - unsigned cats[10]; - explore_string_t* filter[10]; - explore_entry_t *e = NULL; - explore_entry_t *e_end = NULL; + unsigned view_levels = state->view_levels; + char* view_search = state->view_search; + uint8_t* view_op = state->view_op; + bool* view_use_split = state->view_use_split; + unsigned* view_cats = state->view_cats; + explore_string_t** view_match = state->view_match; + uint32_t* view_idx_min = state->view_idx_min; + uint32_t* view_idx_max = state->view_idx_max; + explore_entry_t* entries = state->entries, *e, *eend; bool* map_filtered_category = NULL; - unsigned levels = 0; - bool use_find = ( - *explore_state->find_string != '\0'); - - bool is_show_all = (depth == 1 && !use_find); + bool has_search = !!*view_search; + bool is_show_all = (!view_levels && !has_search); bool is_filtered_category = (current_cat < EXPLORE_CAT_COUNT); bool filtered_category_have_unknown = false; + size_t first_list_entry; + + if (is_show_all) + { + explore_append_title(state, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_ALL)); + } - /* List filtered items in a selected explore by category */ if (is_filtered_category) { - explore_append_title(explore_state, " - %s", - msg_hash_to_str(explore_by_info[current_cat].by_enum)); - - if (current_cat == EXPLORE_BY_SYSTEM) - explore_state->show_icons = EXPLORE_ICONS_SYSTEM_CATEGORY; + /* List filtered items in a selected explore by category */ + if (!view_levels || view_cats[view_levels - 1] != current_cat) + { + explore_append_title(state, " / %s", + msg_hash_to_str(explore_by_info[current_cat].by_enum)); + } + else + { + /* List all items again when setting a range filter */ + explore_append_title(state, " (%s)", + msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_RANGE_FILTER)); + view_levels--; + } + } + else if (current_type == EXPLORE_TYPE_VIEW) + { + /* Show a saved view */ + state->show_icons = EXPLORE_ICONS_CONTENT; + explore_menu_entry(list, state, + msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_DELETE_VIEW), + EXPLORE_TYPE_VIEW, explore_action_ok_deleteview); + explore_menu_add_spacer(list); } else { /* Game list */ - if (is_show_all) - { - menu_file_list_cbs_t *new_cbs = NULL; - explore_append_title(explore_state, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_ALL)); - new_cbs = explore_menu_entry( - list, explore_state, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_SEARCH_NAME), - EXPLORE_TYPE_SEARCH); - if (new_cbs) - new_cbs->action_ok = explore_action_ok_find; - } - else - explore_menu_entry(list, explore_state, - msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_ADD_ADDITIONAL_FILTER), - EXPLORE_TYPE_ADDITIONALFILTER); + explore_menu_entry(list, state, + msg_hash_to_str(MENU_ENUM_LABEL_VALUE_EXPLORE_ADD_ADDITIONAL_FILTER), + EXPLORE_TYPE_ADDITIONALFILTER, explore_action_ok); + explore_menu_entry(list, state, + msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_SAVE_VIEW), + EXPLORE_TYPE_VIEW, explore_action_ok_saveview); explore_menu_add_spacer(list); - explore_state->show_icons = EXPLORE_ICONS_CONTENT; + state->show_icons = EXPLORE_ICONS_CONTENT; } - for (i = 1; i < depth; i++) + first_list_entry = list->size; + for (e = entries, eend = RBUF_END(entries); e != eend; e++) { - explore_string_t **entries = NULL; - unsigned by_selected_type = 0; - unsigned by_category = (stack_top[i].type - - EXPLORE_TYPE_FIRSTCATEGORY); - - if (by_category >= EXPLORE_CAT_COUNT) - continue; - - by_selected_type = stack_top[i + 1].type; - entries = explore_state->by[by_category]; - cats [levels] = by_category; - use_split[levels] = explore_by_info[by_category].use_split; - filter [levels] = - (by_selected_type == EXPLORE_TYPE_FILTERNULL - ? NULL - : entries[by_selected_type - EXPLORE_TYPE_FIRSTITEM]); - levels++; - } - - e = explore_state->entries; - e_end = RBUF_END(explore_state->entries); - - for (; e != e_end; e++) - { - unsigned lvl; - for (lvl = 0; lvl != levels; lvl++) + for (i = 0; i != view_levels; i++) { - if (filter[lvl] == e->by[cats[lvl]]) - continue; - if (use_split[lvl] && e->split) + explore_string_t* eby = e->by[view_cats[i]]; + switch (view_op[i]) { - explore_string_t** split = e->split; - do - { - if (*split == filter[lvl]) - break; - } while (*(++split)); - if (*split) - continue; + case EXPLORE_OP_EQUAL: + if (view_match[i] == eby) + continue; + if (view_use_split[i] && e->split) + { + explore_string_t** split = e->split; + do + { + if (*split == view_match[i]) + break; + } while (*(++split)); + if (*split) + continue; + } + goto SKIP_ENTRY; + case EXPLORE_OP_MIN: + if (eby && eby->idx >= view_idx_min[i]) + continue; + goto SKIP_ENTRY; + case EXPLORE_OP_MAX: + if (eby && eby->idx <= view_idx_max[i]) + continue; + goto SKIP_ENTRY; + case EXPLORE_OP_RANGE: + if (eby && eby->idx >= view_idx_min[i] + && eby->idx <= view_idx_max[i]) + continue; + goto SKIP_ENTRY; } - goto SKIP_ENTRY; } - if (use_find && - !strcasestr(e->playlist_entry->label, - explore_state->find_string)) + if (has_search && !strcasestr(e->playlist_entry->label, view_search)) goto SKIP_ENTRY; if (is_filtered_category) @@ -1179,38 +1600,34 @@ SKIP_EXPLORE_BY_CATEGORY:; if (RHMAP_HAS(map_filtered_category, str->idx + 1)) continue; RHMAP_SET(map_filtered_category, str->idx + 1, true); - explore_menu_entry(list, explore_state, - str->str, - EXPLORE_TYPE_FIRSTITEM + str->idx); + explore_menu_entry(list, state, str->str, + EXPLORE_TYPE_FIRSTITEM + str->idx, explore_action_ok); } #ifdef EXPLORE_SHOW_ORIGINAL_TITLE else if (e->original_title) - explore_menu_entry(list, - explore_state, e->original_title, - EXPLORE_TYPE_FIRSTITEM + (e - explore_state->entries)); + explore_menu_entry(list, state, e->original_title, + EXPLORE_TYPE_FIRSTITEM + (e - entries), explore_action_ok); #endif else - explore_menu_entry(list, - explore_state, e->playlist_entry->label, - EXPLORE_TYPE_FIRSTITEM + (e - explore_state->entries)); - + explore_menu_entry(list, state, e->playlist_entry->label, + EXPLORE_TYPE_FIRSTITEM + (e - entries), explore_action_ok); SKIP_ENTRY:; } if (is_filtered_category) qsort(list->list, list->size, sizeof(*list->list), explore_qsort_func_menulist); + explore_append_title(state, + " (%u)", (unsigned)(list->size - first_list_entry)); + if (is_filtered_category && filtered_category_have_unknown) { explore_menu_add_spacer(list); - explore_menu_entry(list, explore_state, + explore_menu_entry(list, state, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_UNKNOWN), - EXPLORE_TYPE_FILTERNULL); + EXPLORE_TYPE_FILTERNULL, explore_action_ok); } - explore_append_title(explore_state, - " (%u)", (unsigned) (list->size - (is_filtered_category ? 0 : 1))); - RHMAP_FREE(map_filtered_category); } else @@ -1218,20 +1635,18 @@ SKIP_ENTRY:; /* Content page of selected game */ int pl_idx; const struct playlist_entry *pl_entry = - explore_state->entries[current_type - EXPLORE_TYPE_FIRSTITEM].playlist_entry; + state->entries[current_type - EXPLORE_TYPE_FIRSTITEM].playlist_entry; menu_handle_t *menu = menu_state_get_ptr()->driver_data; - strlcpy(explore_state->title, - pl_entry->label, sizeof(explore_state->title)); + strlcpy(state->title, + pl_entry->label, sizeof(state->title)); - for (pl_idx = 0; pl_idx != RBUF_LEN(explore_state->playlists); pl_idx++) + for (pl_idx = 0; pl_idx != RBUF_LEN(state->playlists); pl_idx++) { menu_displaylist_info_t info; const struct playlist_entry* pl_first = NULL; playlist_t *pl = - explore_state->playlists[pl_idx]; - - menu_displaylist_info_init(&info); + state->playlists[pl_idx]; playlist_get_index(pl, 0, &pl_first); @@ -1241,6 +1656,7 @@ SKIP_ENTRY:; /* Fake all the state so the content screen * and information screen think we're viewing via playlist */ + menu_displaylist_info_init(&info); playlist_set_cached_external(pl); menu->rpl_entry_selection_ptr = (pl_entry - pl_first); strlcpy(menu->deferred_path, @@ -1277,83 +1693,69 @@ uintptr_t menu_explore_get_entry_icon(unsigned type) return 0; } -const char *menu_explore_get_entry_database(unsigned type) -{ - explore_entry_t* e = NULL; - unsigned i; - - if (!explore_state || type < EXPLORE_TYPE_FIRSTITEM) - return 0; - - i = (type - EXPLORE_TYPE_FIRSTITEM); - e = &explore_state->entries[i]; - - if (e < RBUF_END(explore_state->entries)) - return e->by[EXPLORE_BY_SYSTEM]->str; - - return NULL; -} - ssize_t menu_explore_get_entry_playlist_index(unsigned type, - playlist_t **playlist, - const struct playlist_entry **playlist_entry) + playlist_t **playlist, const struct playlist_entry **playlist_entry, + file_list_t *list, size_t *list_pos, size_t *list_size) { - explore_entry_t* e = NULL; - playlist_t* p = NULL; - unsigned i; + int pl_idx; + explore_entry_t* entry; - if (!explore_state || type < EXPLORE_TYPE_FIRSTITEM) - return 0; + if (!explore_state || type < EXPLORE_TYPE_FIRSTITEM + || explore_state->show_icons != EXPLORE_ICONS_CONTENT) + return -1; - i = (type - EXPLORE_TYPE_FIRSTITEM); - e = &explore_state->entries[i]; - p = explore_state->playlists[0]; + entry = &explore_state->entries[type - EXPLORE_TYPE_FIRSTITEM]; + if (entry >= RBUF_END(explore_state->entries) + || !entry->playlist_entry) + return -1; - if (e < RBUF_END(explore_state->entries)) + for (pl_idx = 0; pl_idx != RBUF_LEN(explore_state->playlists); pl_idx++) { - const struct playlist_entry *entry = NULL; - size_t pi = 0; - size_t j = 0; + const struct playlist_entry* pl_first = NULL; + playlist_t *pl = explore_state->playlists[pl_idx]; - playlist_get_index(p, 0, &entry); - while (!string_is_equal(e->playlist_entry->db_name, entry->db_name)) - { - p = explore_state->playlists[pi]; - playlist_get_index(p, 0, &entry); - pi++; - } + playlist_get_index(pl, 0, &pl_first); - for (j = 0; j < playlist_size(p); j++) - { - playlist_get_index(p, j, &entry); - if (string_is_equal(entry->label, e->playlist_entry->label)) - { - *playlist_entry = entry; - *playlist = p; + if ( entry->playlist_entry < pl_first || + entry->playlist_entry >= pl_first + playlist_size(pl)) + continue; - /* Playlist needs to get cached for on-demand thumbnails */ - playlist_set_cached_external(p); - return j; - } - } + if (playlist) *playlist = pl; + if (playlist_entry) *playlist_entry = entry->playlist_entry; + + /* correct numbers of list pos and list size */ + if (list && list_pos && list_size) + while (*list_size && list->list[list->size-*list_size].type < EXPLORE_TYPE_FIRSTITEM) + { (*list_size)--; (*list_pos)--; } + + /* Playlist needs to get cached for on-demand thumbnails */ + playlist_set_cached_external(pl); + return (ssize_t)(entry->playlist_entry - pl_first); } - return -1; } -ssize_t menu_explore_set_entry_playlist_index(unsigned type, +ssize_t menu_explore_set_playlist_thumbnail(unsigned type, gfx_thumbnail_path_data_t *thumbnail_path_data) { const char *db_name; - ssize_t playlist_index = -1; - playlist_t *playlist = NULL; - const struct playlist_entry *playlist_entry = NULL; + ssize_t playlist_index = -1; + playlist_t *playlist = NULL; + explore_entry_t* entry; - db_name = menu_explore_get_entry_database(type); + if (!explore_state || type < EXPLORE_TYPE_FIRSTITEM + || explore_state->show_icons != EXPLORE_ICONS_CONTENT) + return -1; + + entry = &explore_state->entries[type - EXPLORE_TYPE_FIRSTITEM]; + if (entry >= RBUF_END(explore_state->entries)) + return -1; + + db_name = entry->by[EXPLORE_BY_SYSTEM]->str; if (!string_is_empty(db_name)) - playlist_index = menu_explore_get_entry_playlist_index(type, &playlist, &playlist_entry); + playlist_index = menu_explore_get_entry_playlist_index(type, &playlist, NULL, NULL, NULL, NULL); - if (playlist_index >= 0 && playlist && playlist_entry) + if (playlist_index >= 0 && playlist) { gfx_thumbnail_set_system(thumbnail_path_data, db_name, playlist); gfx_thumbnail_set_content_playlist(thumbnail_path_data, @@ -1365,6 +1767,13 @@ ssize_t menu_explore_set_entry_playlist_index(unsigned type, return -1; } +bool menu_explore_is_content_list() +{ + if (explore_state) + return (explore_state->show_icons == EXPLORE_ICONS_CONTENT); + return explore_get_view_path() != NULL; +} + void menu_explore_context_init(void) { if (!explore_state) @@ -1419,5 +1828,8 @@ void menu_explore_set_state(explore_state_t *state) if (explore_state) menu_explore_free(); + /* needs to be done now on the main thread */ + explore_load_icons(state); + explore_state = state; } diff --git a/msg_hash.h b/msg_hash.h index 873769f310..1bf725c55f 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -2257,6 +2257,14 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_EXPLORE_BY_FRANCHISE, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_TAG, MENU_ENUM_LABEL_VALUE_EXPLORE_BY_SYSTEM_NAME, + MENU_ENUM_LABEL_EXPLORE_RANGE_FILTER, + MENU_ENUM_LABEL_EXPLORE_VIEW, + MENU_ENUM_LABEL_EXPLORE_SAVE_VIEW, + MENU_ENUM_LABEL_EXPLORE_DELETE_VIEW, + MENU_ENUM_LABEL_EXPLORE_NEW_VIEW, + MENU_ENUM_LABEL_EXPLORE_VIEW_EXISTS, + MENU_ENUM_LABEL_EXPLORE_VIEW_SAVED, + MENU_ENUM_LABEL_EXPLORE_VIEW_DELETED, /* Content information settings */ MENU_LABEL(CONTENT_INFO_LABEL), diff --git a/tasks/task_menu_explore.c b/tasks/task_menu_explore.c index c8eedffa36..5ca2bfcb7f 100644 --- a/tasks/task_menu_explore.c +++ b/tasks/task_menu_explore.c @@ -72,7 +72,7 @@ static void cb_task_menu_explore_init( void *user_data, const char *err) { menu_explore_init_handle_t *menu_explore = NULL; - const char *menu_label = NULL; + unsigned menu_type = 0; if (!task) return; @@ -88,18 +88,26 @@ static void cb_task_menu_explore_init( /* If the explore menu is currently displayed, * it must be refreshed */ - menu_entries_get_last_stack(NULL, &menu_label, NULL, NULL, NULL); + menu_entries_get_last_stack(NULL, NULL, &menu_type, NULL, NULL); - if (string_is_empty(menu_label)) - return; - - if (string_is_equal(menu_label, - msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_EXPLORE_LIST)) || - string_is_equal(menu_label, - msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_TAB))) + /* check if we are opening a saved view from the horizontal/tabs menu */ + if (menu_type == MENU_SETTING_HORIZONTAL_MENU) { - bool refresh = false; - menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh); + menu_ctx_list_t tabs, horizontal; + tabs.type = MENU_LIST_TABS; + if (menu_driver_list_get_selection(&tabs) && menu_driver_list_get_size(&tabs)) + { + horizontal.type = MENU_LIST_HORIZONTAL; + horizontal.idx = tabs.selection - (tabs.size + 1); + if (menu_driver_list_get_entry(&horizontal)) + menu_type = ((struct item_file*)horizontal.entry)->type; + } + } + + if (menu_type == MENU_EXPLORE_TAB) + { + bool refresh_nonblocking = false; + menu_entries_ctl(MENU_ENTRIES_CTL_SET_REFRESH, &refresh_nonblocking); menu_driver_ctl(RARCH_MENU_CTL_SET_PREVENT_POPULATE, NULL); } } diff --git a/tasks/task_pl_thumbnail_download.c b/tasks/task_pl_thumbnail_download.c index 56d0eb1598..9a62b1ec3f 100644 --- a/tasks/task_pl_thumbnail_download.c +++ b/tasks/task_pl_thumbnail_download.c @@ -581,21 +581,8 @@ static void cb_task_pl_entry_thumbnail_refresh_menu( else #endif { - char str[255]; - menu_entries_get_label(str, sizeof(str)); - - /* Explore menu selection index does not match playlist index even in System list. */ - if (string_is_equal(str, msg_hash_to_str(MENU_ENUM_LABEL_EXPLORE_TAB))) - { - if (!string_is_equal(pl_thumb->playlist_path, - playlist_get_conf_path(current_playlist))) - return; - } - else - if (((pl_thumb->list_index != menu_navigation_get_selection()) && - (pl_thumb->list_index != menu->rpl_entry_selection_ptr)) || - !string_is_equal(pl_thumb->playlist_path, - playlist_get_conf_path(current_playlist))) + if (!string_is_equal(pl_thumb->playlist_path, + playlist_get_conf_path(current_playlist))) return; } diff --git a/ui/drivers/qt/qt_playlist.cpp b/ui/drivers/qt/qt_playlist.cpp index 5c6b563f25..434467bec9 100644 --- a/ui/drivers/qt/qt_playlist.cpp +++ b/ui/drivers/qt/qt_playlist.cpp @@ -1213,6 +1213,11 @@ void MainWindow::reloadPlaylists() QString iconPath; QListWidgetItem *item = NULL; const QString &file = m_playlistFiles.at(i); + + /* don't show view files */ + if (file.endsWith(".lvw", Qt::CaseInsensitive)) + continue; + QString fileDisplayName = file; QString fileName = file; bool hasIcon = false;