2020-09-15 20:57:54 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-05-24 08:29:29 +00:00
|
|
|
//
|
2020-09-15 20:57:54 +00:00
|
|
|
// EmulationStation Desktop Edition
|
2020-06-21 12:25:28 +00:00
|
|
|
// GuiGamelistOptions.cpp
|
2020-05-24 08:29:29 +00:00
|
|
|
//
|
2020-06-21 12:25:28 +00:00
|
|
|
// Gamelist options menu for the 'Jump to...' quick selector,
|
|
|
|
// game sorting, game filters, and metadata edit.
|
2020-05-24 08:29:29 +00:00
|
|
|
//
|
2020-06-21 12:25:28 +00:00
|
|
|
// The filter interface is covered by GuiGamelistFilter and the
|
|
|
|
// metadata edit interface is covered by GuiMetaDataEd.
|
2020-05-26 16:34:33 +00:00
|
|
|
//
|
2020-05-24 08:29:29 +00:00
|
|
|
|
2022-04-11 21:28:18 +00:00
|
|
|
#if defined(_WIN64)
|
|
|
|
// Why this is needed here is anyone's guess but without it the compilation fails.
|
|
|
|
#include <winsock2.h>
|
|
|
|
#endif
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
#include "GuiGamelistOptions.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
|
2020-12-23 17:06:30 +00:00
|
|
|
#include "CollectionSystemsManager.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "FileFilterIndex.h"
|
|
|
|
#include "FileSorts.h"
|
|
|
|
#include "GuiMetaDataEd.h"
|
2020-11-14 14:30:49 +00:00
|
|
|
#include "MameNames.h"
|
2020-05-15 16:21:24 +00:00
|
|
|
#include "Sound.h"
|
2020-09-15 20:57:54 +00:00
|
|
|
#include "SystemData.h"
|
2022-01-17 20:53:23 +00:00
|
|
|
#include "UIModeController.h"
|
2021-07-07 18:03:42 +00:00
|
|
|
#include "guis/GuiGamelistFilter.h"
|
|
|
|
#include "scrapers/Scraper.h"
|
|
|
|
#include "views/ViewController.h"
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
#include <SDL2/SDL.h>
|
|
|
|
|
2022-01-19 17:01:54 +00:00
|
|
|
GuiGamelistOptions::GuiGamelistOptions(SystemData* system)
|
|
|
|
: mMenu {"OPTIONS"}
|
2022-01-16 17:18:28 +00:00
|
|
|
, mSystem {system}
|
|
|
|
, mFiltersChanged {false}
|
|
|
|
, mCancelled {false}
|
|
|
|
, mIsCustomCollection {false}
|
|
|
|
, mIsCustomCollectionGroup {false}
|
2022-04-11 21:28:18 +00:00
|
|
|
, mLaunchFileOverride {false}
|
2022-01-16 17:18:28 +00:00
|
|
|
, mCustomCollectionSystem {nullptr}
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
addChild(&mMenu);
|
|
|
|
|
2022-01-16 17:18:28 +00:00
|
|
|
FileData* file {getGamelist()->getCursor()};
|
2021-01-02 17:30:39 +00:00
|
|
|
// Check if it's a placeholder, which would limit the menu entries presented.
|
|
|
|
file->isPlaceHolder();
|
|
|
|
mFromPlaceholder = file->isPlaceHolder();
|
2020-06-21 12:25:28 +00:00
|
|
|
ComponentListRow row;
|
|
|
|
|
2020-10-21 19:56:31 +00:00
|
|
|
// There is some special logic required for custom collections.
|
2021-07-07 18:03:42 +00:00
|
|
|
if (file->getSystem()->isCustomCollection() && file->getPath() != file->getSystem()->getName())
|
2021-01-02 17:30:39 +00:00
|
|
|
mIsCustomCollection = true;
|
2020-10-21 19:56:31 +00:00
|
|
|
else if (file->getSystem()->isCustomCollection() &&
|
2021-07-07 18:03:42 +00:00
|
|
|
file->getPath() == file->getSystem()->getName())
|
2021-01-02 17:30:39 +00:00
|
|
|
mIsCustomCollectionGroup = true;
|
|
|
|
|
|
|
|
if (mFromPlaceholder && file->getSystem()->isGroupedCustomCollection())
|
|
|
|
mCustomCollectionSystem = file->getSystem();
|
2020-10-21 19:56:31 +00:00
|
|
|
|
2020-07-28 17:44:17 +00:00
|
|
|
// Read the setting for whether folders are sorted on top of the gamelists.
|
|
|
|
// Also check if the gamelist only contains folders, as generated by the FileData sorting.
|
|
|
|
mFoldersOnTop = Settings::getInstance()->getBool("FoldersOnTop");
|
2020-08-06 20:11:55 +00:00
|
|
|
if (file->getType() != PLACEHOLDER)
|
|
|
|
mOnlyHasFolders = file->getParent()->getOnlyFoldersFlag();
|
2020-07-28 17:44:17 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// Read the applicable favorite sorting setting depending on whether the
|
|
|
|
// system is a custom collection or not.
|
2021-01-02 17:30:39 +00:00
|
|
|
if (mIsCustomCollection)
|
2020-06-21 12:25:28 +00:00
|
|
|
mFavoritesSorting = Settings::getInstance()->getBool("FavFirstCustom");
|
|
|
|
else
|
|
|
|
mFavoritesSorting = Settings::getInstance()->getBool("FavoritesFirst");
|
|
|
|
|
2021-01-02 17:30:39 +00:00
|
|
|
if (!mFromPlaceholder) {
|
2020-06-21 12:25:28 +00:00
|
|
|
// Jump to letter quick selector.
|
|
|
|
row.elements.clear();
|
|
|
|
|
|
|
|
// The letter index is generated in FileData during gamelist sorting.
|
2020-10-30 17:34:05 +00:00
|
|
|
mFirstLetterIndex = getGamelist()->getFirstLetterIndex();
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2020-07-28 17:44:17 +00:00
|
|
|
// Don't include the folder name starting characters if folders are sorted on top
|
|
|
|
// unless the list only contains folders.
|
|
|
|
if (!mOnlyHasFolders && mFoldersOnTop && file->getType() == FOLDER) {
|
2020-12-16 20:19:48 +00:00
|
|
|
mCurrentFirstCharacter = ViewController::FOLDER_CHAR;
|
2020-07-28 17:44:17 +00:00
|
|
|
}
|
|
|
|
else {
|
2020-09-20 10:36:51 +00:00
|
|
|
// Check if the currently selected game is a favorite.
|
|
|
|
bool isFavorite = false;
|
2021-07-07 18:03:42 +00:00
|
|
|
if (mFirstLetterIndex.size() == 1 &&
|
|
|
|
mFirstLetterIndex.front() == ViewController::FAVORITE_CHAR)
|
2020-09-20 10:36:51 +00:00
|
|
|
isFavorite = true;
|
2021-07-07 18:03:42 +00:00
|
|
|
else if (mFirstLetterIndex.size() > 1 &&
|
|
|
|
(mFirstLetterIndex.front() == ViewController::FAVORITE_CHAR ||
|
|
|
|
mFirstLetterIndex[1] == ViewController::FAVORITE_CHAR))
|
2020-09-20 10:36:51 +00:00
|
|
|
isFavorite = true;
|
|
|
|
|
2021-01-23 15:25:53 +00:00
|
|
|
// Get the first character of the game name (which could be a Unicode character).
|
|
|
|
if (mFavoritesSorting && file->getFavorite() && isFavorite)
|
2020-12-16 20:19:48 +00:00
|
|
|
mCurrentFirstCharacter = ViewController::FAVORITE_CHAR;
|
2021-01-23 15:25:53 +00:00
|
|
|
else
|
|
|
|
mCurrentFirstCharacter = Utils::String::getFirstCharacter(file->getSortName());
|
2020-07-28 17:44:17 +00:00
|
|
|
}
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2022-01-19 17:01:54 +00:00
|
|
|
mJumpToLetterList = std::make_shared<LetterList>(getHelpStyle(), "JUMP TO...", false);
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2021-10-08 17:16:23 +00:00
|
|
|
// Enable key repeat so that the left or right button can be held to cycle through
|
|
|
|
// the letters.
|
2021-10-08 19:11:38 +00:00
|
|
|
mJumpToLetterList->setKeyRepeat(true, 650, 200);
|
2021-10-08 17:16:23 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// Populate the quick selector.
|
2021-11-17 16:35:34 +00:00
|
|
|
for (unsigned int i = 0; i < mFirstLetterIndex.size(); ++i) {
|
2020-06-21 12:25:28 +00:00
|
|
|
mJumpToLetterList->add(mFirstLetterIndex[i], mFirstLetterIndex[i], 0);
|
|
|
|
if (mFirstLetterIndex[i] == mCurrentFirstCharacter)
|
|
|
|
mJumpToLetterList->selectEntry(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (system->getName() != "recent")
|
|
|
|
mMenu.addWithLabel("JUMP TO..", mJumpToLetterList);
|
|
|
|
|
2020-10-21 19:56:31 +00:00
|
|
|
// Add the sorting entry, unless this is the grouped custom collections list.
|
2021-01-02 17:30:39 +00:00
|
|
|
if (!mIsCustomCollectionGroup) {
|
2020-10-21 19:56:31 +00:00
|
|
|
// Sort list by selected sort type (persistent throughout the program session).
|
2022-01-19 17:01:54 +00:00
|
|
|
mListSort = std::make_shared<SortList>(getHelpStyle(), "SORT GAMES BY", false);
|
2020-10-21 19:56:31 +00:00
|
|
|
FileData* root;
|
2021-01-02 17:30:39 +00:00
|
|
|
if (mIsCustomCollection)
|
2020-10-21 19:56:31 +00:00
|
|
|
root = getGamelist()->getCursor()->getSystem()->getRootFolder();
|
2020-06-21 12:25:28 +00:00
|
|
|
else
|
2020-10-21 19:56:31 +00:00
|
|
|
root = mSystem->getRootFolder();
|
|
|
|
|
|
|
|
std::string sortType = root->getSortTypeString();
|
2021-01-09 12:44:18 +00:00
|
|
|
unsigned int numSortTypes = static_cast<unsigned int>(FileSorts::SortTypes.size());
|
2021-01-08 19:30:21 +00:00
|
|
|
// If it's not a collection, then hide the System sort options.
|
|
|
|
if (!root->getSystem()->isCollection())
|
|
|
|
numSortTypes -= 2;
|
2020-10-21 19:56:31 +00:00
|
|
|
|
2021-11-17 16:35:34 +00:00
|
|
|
for (unsigned int i = 0; i < numSortTypes; ++i) {
|
2020-10-21 19:56:31 +00:00
|
|
|
const FileData::SortType& sort = FileSorts::SortTypes.at(i);
|
|
|
|
if (sort.description == sortType)
|
2021-09-28 19:49:46 +00:00
|
|
|
mListSort->add(sort.description, &sort, true);
|
2020-10-21 19:56:31 +00:00
|
|
|
else
|
2021-09-28 19:49:46 +00:00
|
|
|
mListSort->add(sort.description, &sort, false);
|
2020-10-21 19:56:31 +00:00
|
|
|
}
|
2021-10-08 17:16:23 +00:00
|
|
|
|
|
|
|
// Enable key repeat so that the left or right button can be held to cycle through
|
|
|
|
// the sort options.
|
2021-10-08 19:11:38 +00:00
|
|
|
mListSort->setKeyRepeat(true, 650, 400);
|
2021-10-08 17:16:23 +00:00
|
|
|
|
2020-10-21 19:56:31 +00:00
|
|
|
// Don't show the sort type option if the gamelist type is recent/last played.
|
|
|
|
if (system->getName() != "recent")
|
|
|
|
mMenu.addWithLabel("SORT GAMES BY", mListSort);
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-05 12:37:33 +00:00
|
|
|
// Add the filters entry, unless this is the grouped custom collections system or if there
|
2021-04-09 17:16:27 +00:00
|
|
|
// are no games for the system.
|
2021-04-05 12:37:33 +00:00
|
|
|
if (!mIsCustomCollectionGroup && system->getRootFolder()->getChildren().size() > 0) {
|
2020-11-05 17:18:11 +00:00
|
|
|
if (system->getName() != "recent" && Settings::getInstance()->getBool("GamelistFilters")) {
|
2020-10-21 19:56:31 +00:00
|
|
|
row.elements.clear();
|
2022-01-19 17:01:54 +00:00
|
|
|
row.addElement(std::make_shared<TextComponent>("FILTER GAMELIST",
|
2021-07-07 18:03:42 +00:00
|
|
|
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
|
|
|
true);
|
2022-01-19 17:01:54 +00:00
|
|
|
row.addElement(makeArrow(), false);
|
2020-10-21 19:56:31 +00:00
|
|
|
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openGamelistFilter, this));
|
|
|
|
mMenu.addRow(row);
|
|
|
|
}
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
2021-04-09 17:16:27 +00:00
|
|
|
// Add a dummy entry when applicable as the menu looks quite ugly if it's just blank.
|
2022-01-04 20:21:26 +00:00
|
|
|
else if (!CollectionSystemsManager::getInstance()->isEditing() &&
|
2021-07-07 18:03:42 +00:00
|
|
|
mSystem->getRootFolder()->getChildren().size() == 0 && !mIsCustomCollectionGroup &&
|
|
|
|
!mIsCustomCollection) {
|
2021-04-09 17:16:27 +00:00
|
|
|
row.elements.clear();
|
2022-01-19 17:01:54 +00:00
|
|
|
row.addElement(std::make_shared<TextComponent>("THIS SYSTEM HAS NO GAMES",
|
2021-07-07 18:03:42 +00:00
|
|
|
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
|
|
|
true);
|
2021-04-09 17:16:27 +00:00
|
|
|
mMenu.addRow(row);
|
|
|
|
}
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2020-10-26 17:35:52 +00:00
|
|
|
std::string customSystem;
|
|
|
|
if (Settings::getInstance()->getBool("UseCustomCollectionsSystem"))
|
|
|
|
customSystem = file->getSystem()->getName();
|
|
|
|
else
|
|
|
|
customSystem = system->getName();
|
2020-06-21 12:25:28 +00:00
|
|
|
|
|
|
|
if (UIModeController::getInstance()->isUIModeFull() &&
|
2021-07-07 18:03:42 +00:00
|
|
|
(mIsCustomCollection || mIsCustomCollectionGroup)) {
|
2022-01-04 20:21:26 +00:00
|
|
|
if (CollectionSystemsManager::getInstance()->getEditingCollection() != customSystem) {
|
2020-10-26 17:35:52 +00:00
|
|
|
row.elements.clear();
|
2022-01-19 17:01:54 +00:00
|
|
|
row.addElement(std::make_shared<TextComponent>("ADD/REMOVE GAMES TO THIS COLLECTION",
|
2021-07-07 18:03:42 +00:00
|
|
|
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
|
|
|
true);
|
2020-10-26 17:35:52 +00:00
|
|
|
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::startEditMode, this));
|
|
|
|
mMenu.addRow(row);
|
|
|
|
}
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (UIModeController::getInstance()->isUIModeFull() &&
|
2022-01-04 20:21:26 +00:00
|
|
|
CollectionSystemsManager::getInstance()->isEditing()) {
|
2020-06-21 12:25:28 +00:00
|
|
|
row.elements.clear();
|
2022-01-04 20:21:26 +00:00
|
|
|
row.addElement(
|
|
|
|
std::make_shared<TextComponent>(
|
|
|
|
"FINISH EDITING '" +
|
|
|
|
Utils::String::toUpper(
|
|
|
|
CollectionSystemsManager::getInstance()->getEditingCollection()) +
|
|
|
|
"' COLLECTION",
|
|
|
|
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
|
|
|
true);
|
2020-06-21 12:25:28 +00:00
|
|
|
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::exitEditMode, this));
|
|
|
|
mMenu.addRow(row);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file->getType() == FOLDER) {
|
2021-01-02 17:30:39 +00:00
|
|
|
if (UIModeController::getInstance()->isUIModeFull() && !mFromPlaceholder &&
|
2021-07-07 18:03:42 +00:00
|
|
|
!(mSystem->isCollection() && file->getType() == FOLDER)) {
|
2020-06-21 12:25:28 +00:00
|
|
|
row.elements.clear();
|
2022-01-19 17:01:54 +00:00
|
|
|
row.addElement(std::make_shared<TextComponent>("EDIT THIS FOLDER'S METADATA",
|
2021-07-07 18:03:42 +00:00
|
|
|
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
|
|
|
true);
|
2022-01-19 17:01:54 +00:00
|
|
|
row.addElement(makeArrow(), false);
|
2020-06-21 12:25:28 +00:00
|
|
|
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this));
|
|
|
|
mMenu.addRow(row);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2021-01-02 17:30:39 +00:00
|
|
|
if (UIModeController::getInstance()->isUIModeFull() && !mFromPlaceholder &&
|
2021-07-07 18:03:42 +00:00
|
|
|
!(mSystem->isCollection() && file->getType() == FOLDER)) {
|
2020-06-21 12:25:28 +00:00
|
|
|
row.elements.clear();
|
2022-01-19 17:01:54 +00:00
|
|
|
row.addElement(std::make_shared<TextComponent>("EDIT THIS GAME'S METADATA",
|
2021-07-07 18:03:42 +00:00
|
|
|
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
|
|
|
true);
|
2022-01-19 17:01:54 +00:00
|
|
|
row.addElement(makeArrow(), false);
|
2020-06-21 12:25:28 +00:00
|
|
|
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this));
|
|
|
|
mMenu.addRow(row);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-11 21:28:18 +00:00
|
|
|
if (file->getType() == FOLDER && file->metadata.get("launchfile") != "") {
|
|
|
|
row.elements.clear();
|
|
|
|
row.addElement(std::make_shared<TextComponent>("ENTER FOLDER (OVERRIDE LAUNCH FILE)",
|
|
|
|
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
|
|
|
true);
|
|
|
|
row.makeAcceptInputHandler([this, file] {
|
|
|
|
mLaunchFileOverride = true;
|
|
|
|
getGamelist()->enterDirectory(file);
|
|
|
|
delete this;
|
|
|
|
});
|
|
|
|
mMenu.addRow(row);
|
|
|
|
}
|
|
|
|
|
2021-04-09 20:28:28 +00:00
|
|
|
// Buttons. The logic to apply or cancel settings are handled by the destructor.
|
|
|
|
if ((!mIsCustomCollectionGroup && system->getRootFolder()->getChildren().size() == 0) ||
|
2021-07-07 18:03:42 +00:00
|
|
|
system->getName() == "recent") {
|
|
|
|
mMenu.addButton("CLOSE", "close", [&] {
|
|
|
|
mCancelled = true;
|
|
|
|
delete this;
|
|
|
|
});
|
2021-04-09 20:28:28 +00:00
|
|
|
}
|
|
|
|
else {
|
2021-04-09 17:16:27 +00:00
|
|
|
mMenu.addButton("APPLY", "apply", [&] { delete this; });
|
2021-07-07 18:03:42 +00:00
|
|
|
mMenu.addButton("CANCEL", "cancel", [&] {
|
|
|
|
mCancelled = true;
|
|
|
|
delete this;
|
|
|
|
});
|
2021-04-09 20:28:28 +00:00
|
|
|
}
|
2020-06-21 12:25:28 +00:00
|
|
|
|
|
|
|
// Center the menu.
|
2022-02-11 22:38:23 +00:00
|
|
|
setSize(Renderer::getScreenWidth(), Renderer::getScreenHeight());
|
2021-08-16 16:25:01 +00:00
|
|
|
mMenu.setPosition((mSize.x - mMenu.getSize().x) / 2.0f, (mSize.y - mMenu.getSize().y) / 2.0f);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GuiGamelistOptions::~GuiGamelistOptions()
|
|
|
|
{
|
2020-09-15 20:57:54 +00:00
|
|
|
// This is required for the situation where scrolling started just before the menu
|
|
|
|
// was openened. Without this, the scrolling would run until manually stopped after
|
|
|
|
// the menu has been closed.
|
2022-01-04 20:49:22 +00:00
|
|
|
ViewController::getInstance()->stopScrolling();
|
2020-09-15 20:57:54 +00:00
|
|
|
|
2022-02-19 16:04:23 +00:00
|
|
|
ViewController::getInstance()->startViewVideos();
|
|
|
|
|
2021-01-02 17:30:39 +00:00
|
|
|
if (mFiltersChanged) {
|
|
|
|
if (!mCustomCollectionSystem) {
|
2022-01-15 12:38:09 +00:00
|
|
|
ViewController::getInstance()->reloadGamelistView(mSystem);
|
2021-01-02 17:30:39 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (!mFromPlaceholder) {
|
2022-01-15 12:38:09 +00:00
|
|
|
ViewController::getInstance()->reloadGamelistView(mSystem);
|
2021-01-02 17:30:39 +00:00
|
|
|
}
|
2021-07-07 18:03:42 +00:00
|
|
|
else if (!mCustomCollectionSystem->getRootFolder()
|
|
|
|
->getChildrenListToDisplay()
|
|
|
|
.empty()) {
|
2022-01-15 12:38:09 +00:00
|
|
|
ViewController::getInstance()->reloadGamelistView(mSystem);
|
2021-07-07 18:03:42 +00:00
|
|
|
getGamelist()->setCursor(
|
|
|
|
mCustomCollectionSystem->getRootFolder()->getChildrenListToDisplay().front());
|
2021-01-02 17:30:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
if (mCancelled)
|
|
|
|
return;
|
|
|
|
|
2021-01-02 17:30:39 +00:00
|
|
|
if (!mFromPlaceholder) {
|
2020-10-21 19:56:31 +00:00
|
|
|
FileData* root;
|
2021-07-07 18:03:42 +00:00
|
|
|
|
2021-01-02 17:30:39 +00:00
|
|
|
if (mIsCustomCollection)
|
2020-10-21 19:56:31 +00:00
|
|
|
root = getGamelist()->getCursor()->getSystem()->getRootFolder();
|
|
|
|
else
|
|
|
|
root = mSystem->getRootFolder();
|
2020-06-21 12:25:28 +00:00
|
|
|
|
|
|
|
// If a new sorting type was selected, then sort and update mSortTypeString for the system.
|
2021-01-02 17:30:39 +00:00
|
|
|
if (!mIsCustomCollectionGroup &&
|
2021-07-07 18:03:42 +00:00
|
|
|
(*mListSort->getSelected()).description != root->getSortTypeString()) {
|
2020-06-21 12:25:28 +00:00
|
|
|
// This will also recursively sort children.
|
|
|
|
root->sort(*mListSort->getSelected(), mFavoritesSorting);
|
|
|
|
root->setSortTypeString((*mListSort->getSelected()).description);
|
|
|
|
|
|
|
|
// Notify that the root folder was sorted (refresh).
|
2020-10-27 18:07:35 +00:00
|
|
|
getGamelist()->onFileChanged(root, false);
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Has the user changed the letter using the quick selector?
|
2020-09-20 10:17:38 +00:00
|
|
|
if (mCurrentFirstCharacter != mJumpToLetterList->getSelected()) {
|
2020-12-16 20:19:48 +00:00
|
|
|
if (mJumpToLetterList->getSelected() == ViewController::FAVORITE_CHAR ||
|
2021-07-07 18:03:42 +00:00
|
|
|
mJumpToLetterList->getSelected() == ViewController::FOLDER_CHAR)
|
2020-06-21 12:25:28 +00:00
|
|
|
jumpToFirstRow();
|
|
|
|
else
|
|
|
|
jumpToLetter();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-11 21:28:18 +00:00
|
|
|
if (mSystem->getRootFolder()->getChildren().size() != 0 && mSystem->getName() != "recent" &&
|
|
|
|
!mLaunchFileOverride)
|
2021-11-15 21:43:06 +00:00
|
|
|
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2017-03-18 17:54:39 +00:00
|
|
|
void GuiGamelistOptions::openGamelistFilter()
|
|
|
|
{
|
2020-10-25 17:55:01 +00:00
|
|
|
GuiGamelistFilter* ggf;
|
2021-03-18 19:22:49 +00:00
|
|
|
|
|
|
|
auto filtersChangedFunc = [this](bool filtersChanged) {
|
2021-03-19 18:14:23 +00:00
|
|
|
if (!mFiltersChanged)
|
|
|
|
mFiltersChanged = filtersChanged;
|
2021-03-18 19:22:49 +00:00
|
|
|
};
|
2020-10-25 17:55:01 +00:00
|
|
|
|
2021-01-02 17:30:39 +00:00
|
|
|
if (mIsCustomCollection)
|
2022-01-19 17:01:54 +00:00
|
|
|
ggf = new GuiGamelistFilter(getGamelist()->getCursor()->getSystem(), filtersChangedFunc);
|
2020-10-25 17:55:01 +00:00
|
|
|
else
|
2022-01-19 17:01:54 +00:00
|
|
|
ggf = new GuiGamelistFilter(mSystem, filtersChangedFunc);
|
2020-10-25 17:55:01 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
mWindow->pushGui(ggf);
|
2017-05-18 10:16:57 +00:00
|
|
|
}
|
2017-03-18 17:54:39 +00:00
|
|
|
|
2017-07-18 09:45:50 +00:00
|
|
|
void GuiGamelistOptions::startEditMode()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
std::string editingSystem = mSystem->getName();
|
|
|
|
// Need to check if we're editing the collections bundle,
|
|
|
|
// as we will want to edit the selected collection within.
|
2022-01-04 20:21:26 +00:00
|
|
|
if (editingSystem ==
|
|
|
|
CollectionSystemsManager::getInstance()->getCustomCollectionsBundle()->getName()) {
|
2020-06-21 12:25:28 +00:00
|
|
|
FileData* file = getGamelist()->getCursor();
|
|
|
|
// Do we have the cursor on a specific collection?.
|
|
|
|
if (file->getType() == FOLDER)
|
|
|
|
editingSystem = file->getName();
|
|
|
|
else
|
|
|
|
// We are inside a specific collection. We want to edit that one.
|
|
|
|
editingSystem = file->getSystem()->getName();
|
|
|
|
}
|
2022-01-04 20:21:26 +00:00
|
|
|
CollectionSystemsManager::getInstance()->setEditMode(editingSystem);
|
2020-10-28 16:49:29 +00:00
|
|
|
|
|
|
|
// Display the indication icons which show what games are part of the custom collection
|
|
|
|
// currently being edited. This is done cheaply using onFileChanged() which will trigger
|
|
|
|
// populateList().
|
2021-11-17 16:35:34 +00:00
|
|
|
for (auto it = SystemData::sSystemVector.begin(); it != SystemData::sSystemVector.end(); ++it) {
|
2022-01-15 12:38:09 +00:00
|
|
|
ViewController::getInstance()->getGamelistView((*it))->onFileChanged(
|
|
|
|
ViewController::getInstance()->getGamelistView((*it))->getCursor(), false);
|
2020-10-28 16:49:29 +00:00
|
|
|
}
|
|
|
|
|
2021-04-09 17:16:27 +00:00
|
|
|
if (mSystem->getRootFolder()->getChildren().size() == 0)
|
2021-11-15 21:43:06 +00:00
|
|
|
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
2020-06-21 12:25:28 +00:00
|
|
|
delete this;
|
2017-07-18 09:45:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void GuiGamelistOptions::exitEditMode()
|
|
|
|
{
|
2022-01-04 20:21:26 +00:00
|
|
|
CollectionSystemsManager::getInstance()->exitEditMode();
|
2021-04-09 17:16:27 +00:00
|
|
|
if (mSystem->getRootFolder()->getChildren().size() == 0)
|
2021-11-15 21:43:06 +00:00
|
|
|
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
|
2020-06-21 12:25:28 +00:00
|
|
|
delete this;
|
2017-07-18 09:45:50 +00:00
|
|
|
}
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
void GuiGamelistOptions::openMetaDataEd()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
// Open metadata editor.
|
|
|
|
// Get the FileData that holds the original metadata.
|
|
|
|
FileData* file = getGamelist()->getCursor()->getSourceFileData();
|
|
|
|
ScraperSearchParams p;
|
|
|
|
p.game = file;
|
|
|
|
p.system = file->getSystem();
|
|
|
|
|
2020-09-27 08:41:00 +00:00
|
|
|
std::function<void()> clearGameBtnFunc;
|
2020-07-30 18:05:57 +00:00
|
|
|
std::function<void()> deleteGameBtnFunc;
|
|
|
|
|
2020-09-27 08:41:00 +00:00
|
|
|
clearGameBtnFunc = [this, file] {
|
|
|
|
if (file->getType() == FOLDER) {
|
2021-07-07 18:03:42 +00:00
|
|
|
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the folder \""
|
|
|
|
<< file->getFullPath() << "\"";
|
2020-09-27 08:41:00 +00:00
|
|
|
}
|
2021-12-17 19:18:47 +00:00
|
|
|
else if (file->getType() == GAME && Utils::FileSystem::isDirectory(file->getFullPath())) {
|
|
|
|
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the "
|
|
|
|
"file-interpreted folder \""
|
|
|
|
<< file->getFullPath() << "\"";
|
|
|
|
}
|
2020-09-27 08:41:00 +00:00
|
|
|
else {
|
2021-07-07 18:03:42 +00:00
|
|
|
LOG(LogInfo) << "Deleting the media files and gamelist.xml entry for the file \""
|
|
|
|
<< file->getFullPath() << "\"";
|
2020-09-27 08:41:00 +00:00
|
|
|
}
|
2022-01-15 12:38:09 +00:00
|
|
|
ViewController::getInstance()->getGamelistView(file->getSystem()).get()->removeMedia(file);
|
2020-09-27 08:41:00 +00:00
|
|
|
|
|
|
|
// Manually reset all the metadata values, set the name to the actual file/folder name.
|
|
|
|
const std::vector<MetaDataDecl>& mdd = file->metadata.getMDD();
|
2021-11-17 16:35:34 +00:00
|
|
|
for (auto it = mdd.cbegin(); it != mdd.cend(); ++it) {
|
2020-09-27 08:41:00 +00:00
|
|
|
if (it->key == "name") {
|
2021-07-07 18:03:42 +00:00
|
|
|
if (file->isArcadeGame()) {
|
2020-11-14 14:30:49 +00:00
|
|
|
// If it's a MAME or Neo Geo game, expand the game name accordingly.
|
2021-11-17 20:32:40 +00:00
|
|
|
file->metadata.set(it->key,
|
|
|
|
MameNames::getInstance().getCleanName(file->getCleanName()));
|
2020-11-14 14:30:49 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
file->metadata.set(it->key, file->getDisplayName());
|
|
|
|
}
|
2020-09-27 08:41:00 +00:00
|
|
|
continue;
|
2020-07-30 18:05:57 +00:00
|
|
|
}
|
2020-09-27 08:41:00 +00:00
|
|
|
file->metadata.set(it->key, it->defaultValue);
|
|
|
|
}
|
2020-07-30 18:05:57 +00:00
|
|
|
|
2021-12-17 19:18:47 +00:00
|
|
|
// For the special case where a directory has a supported file extension and is therefore
|
|
|
|
// interpreted as a file, don't include the extension in the metadata name.
|
|
|
|
if (file->getType() == GAME && Utils::FileSystem::isDirectory(file->getFullPath()))
|
|
|
|
file->metadata.set("name", Utils::FileSystem::getStem(file->metadata.get("name")));
|
|
|
|
|
2020-12-31 18:58:51 +00:00
|
|
|
// Update all collections where the game is present.
|
|
|
|
if (file->getType() == GAME)
|
2022-01-04 20:21:26 +00:00
|
|
|
CollectionSystemsManager::getInstance()->refreshCollectionSystems(file, true);
|
2020-12-31 18:58:51 +00:00
|
|
|
|
2020-09-27 16:37:43 +00:00
|
|
|
file->getSystem()->sortSystem();
|
2021-03-21 15:41:24 +00:00
|
|
|
// This delay reduces the likelyhood that the SVG rasterizer which is running in a
|
|
|
|
// separate thread is not done until the cached background is invalidated. Without
|
|
|
|
// this delay there's a high chance that some theme elements are not rendered in
|
|
|
|
// time and thus not getting included in the regenerated cached background.
|
|
|
|
// This is just a hack though and a better mechanism is needed to handle this.
|
|
|
|
SDL_Delay(100);
|
2020-10-11 08:07:38 +00:00
|
|
|
mWindow->invalidateCachedBackground();
|
2020-09-27 16:37:43 +00:00
|
|
|
|
2020-09-27 08:41:00 +00:00
|
|
|
// Remove the folder entry from the gamelist.xml file.
|
2020-11-14 14:30:49 +00:00
|
|
|
file->setDeletionFlag(true);
|
2020-09-27 08:41:00 +00:00
|
|
|
file->getParent()->getSystem()->writeMetaData();
|
2020-11-14 14:30:49 +00:00
|
|
|
file->setDeletionFlag(false);
|
2020-09-27 08:41:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
deleteGameBtnFunc = [this, file] {
|
2021-07-07 18:03:42 +00:00
|
|
|
LOG(LogInfo) << "Deleting the game file \"" << file->getFullPath()
|
|
|
|
<< "\", all its media files and its gamelist.xml entry.";
|
2022-01-04 20:21:26 +00:00
|
|
|
CollectionSystemsManager::getInstance()->deleteCollectionFiles(file);
|
2022-01-15 12:38:09 +00:00
|
|
|
ViewController::getInstance()->getGamelistView(file->getSystem()).get()->removeMedia(file);
|
|
|
|
ViewController::getInstance()->getGamelistView(file->getSystem()).get()->remove(file, true);
|
2021-01-31 18:28:30 +00:00
|
|
|
mSystem->getRootFolder()->sort(*mListSort->getSelected(), mFavoritesSorting);
|
2022-01-15 12:38:09 +00:00
|
|
|
ViewController::getInstance()->reloadGamelistView(mSystem);
|
2021-01-31 18:28:30 +00:00
|
|
|
|
2020-10-11 08:07:38 +00:00
|
|
|
mWindow->invalidateCachedBackground();
|
2020-09-27 08:41:00 +00:00
|
|
|
};
|
2020-06-21 12:25:28 +00:00
|
|
|
|
|
|
|
if (file->getType() == FOLDER) {
|
2021-07-07 18:03:42 +00:00
|
|
|
mWindow->pushGui(new GuiMetaDataEd(
|
2022-01-19 17:01:54 +00:00
|
|
|
&file->metadata, file->metadata.getMDD(FOLDER_METADATA), p,
|
2022-01-18 19:42:50 +00:00
|
|
|
std::bind(&GamelistView::onFileChanged,
|
2022-01-15 12:38:09 +00:00
|
|
|
ViewController::getInstance()->getGamelistView(file->getSystem()).get(), file,
|
2022-01-04 20:49:22 +00:00
|
|
|
true),
|
2021-07-07 18:03:42 +00:00
|
|
|
clearGameBtnFunc, deleteGameBtnFunc));
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
else {
|
2021-07-07 18:03:42 +00:00
|
|
|
mWindow->pushGui(new GuiMetaDataEd(
|
2022-01-19 17:01:54 +00:00
|
|
|
&file->metadata, file->metadata.getMDD(GAME_METADATA), p,
|
2022-01-18 19:42:50 +00:00
|
|
|
std::bind(&GamelistView::onFileChanged,
|
2022-01-15 12:38:09 +00:00
|
|
|
ViewController::getInstance()->getGamelistView(file->getSystem()).get(), file,
|
2022-01-04 20:49:22 +00:00
|
|
|
true),
|
2021-07-07 18:03:42 +00:00
|
|
|
clearGameBtnFunc, deleteGameBtnFunc));
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2014-07-27 22:49:43 +00:00
|
|
|
void GuiGamelistOptions::jumpToLetter()
|
|
|
|
{
|
2022-01-19 17:01:54 +00:00
|
|
|
char letter {mJumpToLetterList->getSelected().front()};
|
2020-06-21 12:25:28 +00:00
|
|
|
|
|
|
|
// Get the gamelist.
|
2021-07-07 18:03:42 +00:00
|
|
|
const std::vector<FileData*>& files =
|
|
|
|
getGamelist()->getCursor()->getParent()->getChildrenListToDisplay();
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2021-11-17 16:35:34 +00:00
|
|
|
for (unsigned int i = 0; i < files.size(); ++i) {
|
2020-12-16 20:19:48 +00:00
|
|
|
if (mFavoritesSorting && (mFirstLetterIndex.front() == ViewController::FAVORITE_CHAR ||
|
2021-07-07 18:03:42 +00:00
|
|
|
mFirstLetterIndex.front() == ViewController::FOLDER_CHAR)) {
|
|
|
|
if (static_cast<char>(toupper(files.at(i)->getSortName().front())) == letter &&
|
|
|
|
!files.at(i)->getFavorite()) {
|
2020-09-20 18:25:32 +00:00
|
|
|
if (!mOnlyHasFolders && mFoldersOnTop && files.at(i)->getType() == FOLDER) {
|
2020-07-28 17:44:17 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
getGamelist()->setCursor(files.at(i));
|
|
|
|
break;
|
|
|
|
}
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2020-11-17 22:06:54 +00:00
|
|
|
if (static_cast<char>(toupper(files.at(i)->getSortName().front())) == letter) {
|
2020-07-28 17:44:17 +00:00
|
|
|
if (!mOnlyHasFolders && mFoldersOnTop && files.at(i)->getType() == FOLDER) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
getGamelist()->setCursor(files.at(i));
|
|
|
|
break;
|
|
|
|
}
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-07-27 22:49:43 +00:00
|
|
|
}
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
void GuiGamelistOptions::jumpToFirstRow()
|
2020-05-15 16:16:04 +00:00
|
|
|
{
|
2020-12-16 20:19:48 +00:00
|
|
|
if (mFoldersOnTop && mJumpToLetterList->getSelected() == ViewController::FAVORITE_CHAR) {
|
2020-07-28 17:44:17 +00:00
|
|
|
// Get the gamelist.
|
2021-07-07 18:03:42 +00:00
|
|
|
const std::vector<FileData*>& files =
|
|
|
|
getGamelist()->getCursor()->getParent()->getChildrenListToDisplay();
|
2020-09-20 18:25:32 +00:00
|
|
|
// Select the first game that is not a folder, unless it's a folder-only list in
|
|
|
|
// which case the first line overall is selected.
|
2021-11-17 16:35:34 +00:00
|
|
|
for (auto it = files.cbegin(); it != files.cend(); ++it) {
|
2020-09-20 18:25:32 +00:00
|
|
|
if (!mOnlyHasFolders && mFoldersOnTop && (*it)->getType() == FOLDER) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else {
|
2020-07-28 17:44:17 +00:00
|
|
|
getGamelist()->setCursor(*it);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Get first row of the gamelist.
|
|
|
|
getGamelist()->setCursor(getGamelist()->getFirstEntry());
|
|
|
|
}
|
2020-05-15 16:16:04 +00:00
|
|
|
}
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
bool GuiGamelistOptions::input(InputConfig* config, Input input)
|
|
|
|
{
|
2021-05-23 18:31:15 +00:00
|
|
|
if (input.value != 0 && config->isMappedTo("back", input))
|
2020-06-21 12:25:28 +00:00
|
|
|
mCancelled = true;
|
2020-06-09 18:03:31 +00:00
|
|
|
|
2021-07-07 18:03:42 +00:00
|
|
|
if (input.value != 0 && (config->isMappedTo("b", input) || config->isMappedTo("back", input))) {
|
2020-06-21 12:25:28 +00:00
|
|
|
delete this;
|
|
|
|
return true;
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
return mMenu.input(config, input);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<HelpPrompt> GuiGamelistOptions::getHelpPrompts()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
auto prompts = mMenu.getHelpPrompts();
|
2021-07-07 18:03:42 +00:00
|
|
|
if (mSystem->getRootFolder()->getChildren().size() > 0 || mIsCustomCollectionGroup ||
|
2022-01-04 20:21:26 +00:00
|
|
|
mIsCustomCollection || CollectionSystemsManager::getInstance()->isEditing())
|
2021-04-09 20:28:28 +00:00
|
|
|
prompts.push_back(HelpPrompt("a", "select"));
|
|
|
|
if (mSystem->getRootFolder()->getChildren().size() > 0 && mSystem->getName() != "recent") {
|
2021-04-09 17:16:27 +00:00
|
|
|
prompts.push_back(HelpPrompt("b", "close (apply)"));
|
2021-05-23 18:31:15 +00:00
|
|
|
prompts.push_back(HelpPrompt("back", "close (cancel)"));
|
2021-04-09 20:28:28 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
prompts.push_back(HelpPrompt("b", "close"));
|
2021-05-23 18:31:15 +00:00
|
|
|
prompts.push_back(HelpPrompt("back", "close"));
|
2021-04-09 20:28:28 +00:00
|
|
|
}
|
2020-06-21 12:25:28 +00:00
|
|
|
return prompts;
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2022-01-18 19:42:50 +00:00
|
|
|
GamelistView* GuiGamelistOptions::getGamelist()
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2022-01-15 12:38:09 +00:00
|
|
|
return ViewController::getInstance()->getGamelistView(mSystem).get();
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|