2020-05-24 08:29:29 +00:00
|
|
|
//
|
|
|
|
// GuiGamelistOptions.cpp
|
|
|
|
//
|
|
|
|
// Gamelist options menu for the 'Jump to...' quick selector,
|
|
|
|
// game sorting, game filters, and metadata edit.
|
|
|
|
//
|
2020-05-26 16:34:33 +00:00
|
|
|
// The filter interface is covered by GuiGamelistFilter and the
|
|
|
|
// metadata edit interface is covered by GuiMetaDataEd.
|
|
|
|
//
|
2020-05-24 08:29:29 +00:00
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
#include "GuiGamelistOptions.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
|
|
|
|
#include "guis/GuiGamelistFilter.h"
|
|
|
|
#include "scrapers/Scraper.h"
|
2014-06-25 16:29:58 +00:00
|
|
|
#include "views/gamelist/IGameListView.h"
|
2017-11-18 22:23:56 +00:00
|
|
|
#include "views/UIModeController.h"
|
2014-06-25 16:29:58 +00:00
|
|
|
#include "views/ViewController.h"
|
2017-06-12 16:38:59 +00:00
|
|
|
#include "CollectionSystemManager.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "FileFilterIndex.h"
|
|
|
|
#include "FileSorts.h"
|
|
|
|
#include "GuiMetaDataEd.h"
|
|
|
|
#include "SystemData.h"
|
2020-05-15 16:21:24 +00:00
|
|
|
#include "Sound.h"
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
GuiGamelistOptions::GuiGamelistOptions(
|
|
|
|
Window* window,
|
|
|
|
SystemData* system)
|
|
|
|
: GuiComponent(window),
|
|
|
|
mSystem(system),
|
|
|
|
mMenu(window, "OPTIONS"),
|
|
|
|
fromPlaceholder(false),
|
|
|
|
mFiltersChanged(false)
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
|
|
|
addChild(&mMenu);
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
// Check that it's not a placeholder folder - if it is, only show "Filter Options".
|
2017-03-18 17:54:39 +00:00
|
|
|
FileData* file = getGamelist()->getCursor();
|
|
|
|
fromPlaceholder = file->isPlaceHolder();
|
2014-07-27 22:49:43 +00:00
|
|
|
ComponentListRow row;
|
2017-03-18 17:54:39 +00:00
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
// Read the applicable favorite sorting setting depending on whether the
|
|
|
|
// system is a custom collection or not.
|
|
|
|
if (CollectionSystemManager::get()->getIsCustomCollection(file->getSystem()))
|
|
|
|
mFavoritesSorting = Settings::getInstance()->getBool("FavFirstCustom");
|
|
|
|
else
|
|
|
|
mFavoritesSorting = Settings::getInstance()->getBool("FavoritesFirst");
|
|
|
|
|
2017-03-18 17:54:39 +00:00
|
|
|
if (!fromPlaceholder) {
|
2020-05-24 08:29:29 +00:00
|
|
|
// Jump to letter.
|
2017-11-03 11:11:11 +00:00
|
|
|
row.elements.clear();
|
2017-03-18 17:54:39 +00:00
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
// Define supported character range.
|
|
|
|
// This range includes all numbers, capital letters, and most reasonable symbols.
|
2017-11-03 11:11:11 +00:00
|
|
|
char startChar = '!';
|
|
|
|
char endChar = '_';
|
|
|
|
|
2018-04-25 05:07:25 +00:00
|
|
|
char curChar = (char)toupper(getGamelist()->getCursor()->getSortName()[0]);
|
2020-05-24 08:29:29 +00:00
|
|
|
if (curChar < startChar || curChar > endChar)
|
2017-11-03 11:11:11 +00:00
|
|
|
curChar = startChar;
|
|
|
|
|
|
|
|
mJumpToLetterList = std::make_shared<LetterList>(mWindow, "JUMP TO...", false);
|
2020-05-15 16:16:04 +00:00
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
if (mFavoritesSorting && system->getName() != "favorites" &&
|
|
|
|
system->getName() != "recent") {
|
|
|
|
// Check whether the first game in the list is a favorite, if it's
|
|
|
|
// not, then there are no favorites currently visible in this gamelist.
|
|
|
|
if (getGamelist()->getCursor()->getParent()->getChildrenListToDisplay()[0]->
|
|
|
|
getFavorite()) {
|
2020-05-15 16:16:04 +00:00
|
|
|
if (getGamelist()->getCursor()->getFavorite())
|
2020-05-17 17:30:29 +00:00
|
|
|
mJumpToLetterList->add(FAVORITE_CHAR, FAVORITE_CHAR, 1);
|
2020-05-15 16:16:04 +00:00
|
|
|
else
|
2020-05-17 17:30:29 +00:00
|
|
|
mJumpToLetterList->add(FAVORITE_CHAR, FAVORITE_CHAR, 0);
|
2020-05-15 16:16:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
for (char c = startChar; c <= endChar; c++) {
|
|
|
|
// Check if c is a valid first letter in the current list.
|
|
|
|
const std::vector<FileData*>& files =
|
|
|
|
getGamelist()->getCursor()->getParent()->getChildrenListToDisplay();
|
|
|
|
for (auto file : files) {
|
2018-04-25 05:07:25 +00:00
|
|
|
char candidate = (char)toupper(file->getSortName()[0]);
|
2020-05-24 08:29:29 +00:00
|
|
|
if (c == candidate) {
|
|
|
|
// If the game is a favorite, continue to the next entry in the list.
|
|
|
|
if (mFavoritesSorting && system->getName() != "favorites" &&
|
|
|
|
system->getName() != "recent" && file->getFavorite())
|
2020-05-15 16:16:04 +00:00
|
|
|
continue;
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
// If the currently selected game is a favorite, set the character
|
|
|
|
// as not selected so we don't get two current positions.
|
|
|
|
if (mFavoritesSorting && system->getName() != "favorites" &&
|
|
|
|
system->getName() != "recent" &&
|
|
|
|
getGamelist()->getCursor()->getFavorite())
|
|
|
|
mJumpToLetterList->add(std::string(1, c), std::string(1, c), 0);
|
|
|
|
else
|
|
|
|
mJumpToLetterList->add(std::string(1, c), std::string(1, c), c == curChar);
|
2017-11-03 11:11:11 +00:00
|
|
|
break;
|
2017-03-18 17:54:39 +00:00
|
|
|
}
|
2017-11-03 11:11:11 +00:00
|
|
|
}
|
2017-03-18 17:54:39 +00:00
|
|
|
}
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
row.addElement(std::make_shared<TextComponent>(
|
|
|
|
mWindow, "JUMP TO...", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
2017-11-03 11:11:11 +00:00
|
|
|
row.addElement(mJumpToLetterList, false);
|
|
|
|
row.input_handler = [&](InputConfig* config, Input input) {
|
2020-05-24 08:29:29 +00:00
|
|
|
if (config->isMappedTo("a", input) && input.value) {
|
2020-06-06 11:10:33 +00:00
|
|
|
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
2020-05-24 08:29:29 +00:00
|
|
|
if (mJumpToLetterList->getSelected() == FAVORITE_CHAR)
|
|
|
|
jumpToFirstRow();
|
|
|
|
else
|
|
|
|
jumpToLetter();
|
|
|
|
|
2017-11-03 11:11:11 +00:00
|
|
|
return true;
|
|
|
|
}
|
2020-05-24 08:29:29 +00:00
|
|
|
else if (mJumpToLetterList->input(config, input)) {
|
2017-11-03 11:11:11 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
2020-05-24 08:29:29 +00:00
|
|
|
if (system->getName() != "recent")
|
|
|
|
mMenu.addRow(row);
|
2017-11-03 11:11:11 +00:00
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
// Sort list by selected sort type (persistent throughout the program session).
|
2017-03-18 17:54:39 +00:00
|
|
|
mListSort = std::make_shared<SortList>(mWindow, "SORT GAMES BY", false);
|
2020-05-24 08:29:29 +00:00
|
|
|
FileData* root = mSystem->getRootFolder();
|
|
|
|
std::string sortType = root->getSortTypeString();
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) {
|
2017-03-18 17:54:39 +00:00
|
|
|
const FileData::SortType& sort = FileSorts::SortTypes.at(i);
|
2020-05-24 08:29:29 +00:00
|
|
|
if (sort.description == sortType)
|
|
|
|
mListSort->add(sort.description, &sort, 1);
|
|
|
|
else
|
|
|
|
mListSort->add(sort.description, &sort, 0);
|
2017-03-18 17:54:39 +00:00
|
|
|
}
|
2020-05-24 08:29:29 +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);
|
2017-03-18 17:54:39 +00:00
|
|
|
}
|
2020-05-24 08:29:29 +00:00
|
|
|
|
|
|
|
// Show filtered menu.
|
|
|
|
if (system->getName() != "recent" && !Settings::getInstance()->getBool("ForceDisableFilters")) {
|
2018-05-10 20:08:04 +00:00
|
|
|
row.elements.clear();
|
2020-05-24 08:29:29 +00:00
|
|
|
row.addElement(std::make_shared<TextComponent>(
|
|
|
|
mWindow, "FILTER GAMELIST", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
2018-05-10 20:08:04 +00:00
|
|
|
row.addElement(makeArrow(mWindow), false);
|
|
|
|
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openGamelistFilter, this));
|
2019-08-25 15:23:02 +00:00
|
|
|
mMenu.addRow(row);
|
2018-05-10 20:08:04 +00:00
|
|
|
}
|
2017-07-05 10:50:01 +00:00
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
std::map<std::string, CollectionSystemData> customCollections =
|
|
|
|
CollectionSystemManager::get()->getCustomCollectionSystems();
|
2017-09-08 14:49:47 +00:00
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
if (UIModeController::getInstance()->isUIModeFull() &&
|
|
|
|
((customCollections.find(system->getName()) != customCollections.cend() &&
|
|
|
|
CollectionSystemManager::get()->getEditingCollection() != system->getName()) ||
|
|
|
|
CollectionSystemManager::get()->getCustomCollectionsBundle()->getName() ==
|
|
|
|
system->getName())) {
|
2017-07-18 09:45:50 +00:00
|
|
|
row.elements.clear();
|
2020-05-24 08:29:29 +00:00
|
|
|
row.addElement(std::make_shared<TextComponent>(
|
|
|
|
mWindow, "ADD/REMOVE GAMES TO THIS GAME COLLECTION", Font::get(FONT_SIZE_MEDIUM),
|
|
|
|
0x777777FF), true);
|
2017-07-18 09:45:50 +00:00
|
|
|
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::startEditMode, this));
|
|
|
|
mMenu.addRow(row);
|
|
|
|
}
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
if (UIModeController::getInstance()->isUIModeFull() &&
|
|
|
|
CollectionSystemManager::get()->isEditing()) {
|
2017-07-18 09:45:50 +00:00
|
|
|
row.elements.clear();
|
2020-05-24 08:29:29 +00:00
|
|
|
row.addElement(std::make_shared<TextComponent>(
|
|
|
|
mWindow, "FINISH EDITING '" + Utils::String::toUpper(
|
|
|
|
CollectionSystemManager::get()->getEditingCollection()) +
|
|
|
|
"' COLLECTION",Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
2017-07-18 09:45:50 +00:00
|
|
|
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::exitEditMode, this));
|
|
|
|
mMenu.addRow(row);
|
|
|
|
}
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
if (UIModeController::getInstance()->isUIModeFull() && !fromPlaceholder &&
|
|
|
|
!(mSystem->isCollection() && file->getType() == FOLDER)) {
|
2017-07-18 09:45:50 +00:00
|
|
|
row.elements.clear();
|
2020-05-24 08:29:29 +00:00
|
|
|
row.addElement(std::make_shared<TextComponent>(mWindow,
|
|
|
|
"EDIT THIS GAME'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
2017-07-18 09:45:50 +00:00
|
|
|
row.addElement(makeArrow(mWindow), false);
|
|
|
|
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this));
|
|
|
|
mMenu.addRow(row);
|
|
|
|
}
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
// Center the menu.
|
2014-06-25 16:29:58 +00:00
|
|
|
setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
2020-05-24 08:29:29 +00:00
|
|
|
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, (mSize.y() -
|
|
|
|
mMenu.getSize().y()) / 2);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GuiGamelistOptions::~GuiGamelistOptions()
|
|
|
|
{
|
2017-03-18 17:54:39 +00:00
|
|
|
if (!fromPlaceholder) {
|
2017-07-18 09:45:50 +00:00
|
|
|
FileData* root = mSystem->getRootFolder();
|
2017-03-18 17:54:39 +00:00
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
// If a new sorting type was selected, then sort and update mSortTypeString for the system.
|
|
|
|
if ((*mListSort->getSelected()).description != root->getSortTypeString()) {
|
2020-06-06 11:10:33 +00:00
|
|
|
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND);
|
2020-05-24 08:29:29 +00:00
|
|
|
|
|
|
|
// This will also recursively sort children.
|
|
|
|
root->sort(*mListSort->getSelected(), mFavoritesSorting);
|
|
|
|
root->setSortTypeString((*mListSort->getSelected()).description);
|
|
|
|
|
|
|
|
// Select the first row of the gamelist.
|
|
|
|
FileData* firstRow = getGamelist()->getCursor()->getParent()->
|
|
|
|
getChildrenListToDisplay()[0];
|
|
|
|
getGamelist()->setCursor(firstRow);
|
|
|
|
|
|
|
|
// Notify that the root folder was sorted.
|
|
|
|
getGamelist()->onFileChanged(root, FILE_SORTED);
|
|
|
|
}
|
2017-05-18 10:16:57 +00:00
|
|
|
}
|
2020-05-24 08:29:29 +00:00
|
|
|
if (mFiltersChanged) {
|
|
|
|
// Only reload full view if we came from a placeholder as we need to
|
|
|
|
// re-display the remaining elements for whatever new game is selected.
|
2017-07-18 09:45:50 +00:00
|
|
|
ViewController::get()->reloadGameListView(mSystem);
|
2017-03-18 17:54:39 +00:00
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2017-03-18 17:54:39 +00:00
|
|
|
void GuiGamelistOptions::openGamelistFilter()
|
|
|
|
{
|
|
|
|
mFiltersChanged = true;
|
|
|
|
GuiGamelistFilter* ggf = new GuiGamelistFilter(mWindow, mSystem);
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
std::string editingSystem = mSystem->getName();
|
2020-05-24 08:29:29 +00:00
|
|
|
// Need to check if we're editing the collections bundle,
|
|
|
|
// as we will want to edit the selected collection within.
|
|
|
|
if (editingSystem == CollectionSystemManager::get()->getCustomCollectionsBundle()->getName()) {
|
2017-07-18 09:45:50 +00:00
|
|
|
FileData* file = getGamelist()->getCursor();
|
2020-05-24 08:29:29 +00:00
|
|
|
// Do we have the cursor on a specific collection?.
|
2017-07-18 09:45:50 +00:00
|
|
|
if (file->getType() == FOLDER)
|
|
|
|
editingSystem = file->getName();
|
|
|
|
else
|
2020-05-24 08:29:29 +00:00
|
|
|
// We are inside a specific collection. We want to edit that one.
|
2017-07-18 09:45:50 +00:00
|
|
|
editingSystem = file->getSystem()->getName();
|
|
|
|
}
|
|
|
|
CollectionSystemManager::get()->setEditMode(editingSystem);
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void GuiGamelistOptions::exitEditMode()
|
|
|
|
{
|
|
|
|
CollectionSystemManager::get()->exitEditMode();
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
void GuiGamelistOptions::openMetaDataEd()
|
|
|
|
{
|
2020-05-24 08:29:29 +00:00
|
|
|
// Open metadata editor.
|
|
|
|
// Get the FileData that holds the original metadata.
|
2017-06-12 16:38:59 +00:00
|
|
|
FileData* file = getGamelist()->getCursor()->getSourceFileData();
|
2014-06-25 16:29:58 +00:00
|
|
|
ScraperSearchParams p;
|
|
|
|
p.game = file;
|
|
|
|
p.system = file->getSystem();
|
2017-04-03 07:08:14 +00:00
|
|
|
|
|
|
|
std::function<void()> deleteBtnFunc;
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
if (file->getType() == FOLDER) {
|
2017-04-03 07:08:14 +00:00
|
|
|
deleteBtnFunc = NULL;
|
|
|
|
}
|
2020-05-24 08:29:29 +00:00
|
|
|
else {
|
2017-04-03 07:08:14 +00:00
|
|
|
deleteBtnFunc = [this, file] {
|
2017-06-12 16:38:59 +00:00
|
|
|
CollectionSystemManager::get()->deleteCollectionFiles(file);
|
|
|
|
ViewController::get()->getGameListView(file->getSystem()).get()->remove(file, true);
|
2017-04-03 07:08:14 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata, file->metadata.getMDD(), p,
|
|
|
|
Utils::FileSystem::getFileName(file->getPath()), std::bind(
|
|
|
|
&IGameListView::onFileChanged, ViewController::get()->getGameListView(
|
|
|
|
file->getSystem()).get(), file, FILE_METADATA_CHANGED), deleteBtnFunc));
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2014-07-27 22:49:43 +00:00
|
|
|
void GuiGamelistOptions::jumpToLetter()
|
|
|
|
{
|
2020-05-17 17:30:29 +00:00
|
|
|
char letter = mJumpToLetterList->getSelected()[0];
|
2014-07-27 22:49:43 +00:00
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
// Get first row of the gamelist.
|
|
|
|
const std::vector<FileData*>& files = getGamelist()->getCursor()->
|
|
|
|
getParent()->getChildrenListToDisplay();
|
2017-05-18 10:16:57 +00:00
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
for (unsigned int i = 0; i < files.size(); i++) {
|
|
|
|
if (mFavoritesSorting && mSystem->getName() != "favorites") {
|
|
|
|
if ((char)toupper(files.at(i)->getSortName()[0]) ==
|
|
|
|
letter && !files.at(i)->getFavorite()) {
|
|
|
|
getGamelist()->setCursor(files.at(i));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if ((char)toupper(files.at(i)->getSortName()[0]) == letter) {
|
|
|
|
getGamelist()->setCursor(files.at(i));
|
|
|
|
break;
|
2020-05-15 16:16:04 +00:00
|
|
|
}
|
|
|
|
}
|
2014-07-27 22:49:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
void GuiGamelistOptions::jumpToFirstRow()
|
2020-05-15 16:16:04 +00:00
|
|
|
{
|
2020-05-24 08:29:29 +00:00
|
|
|
// Get first row of the gamelist.
|
|
|
|
const std::vector<FileData*>& files = getGamelist()->getCursor()->
|
|
|
|
getParent()->getChildrenListToDisplay();
|
|
|
|
getGamelist()->setCursor(files.at(0));
|
2020-05-15 16:16:04 +00:00
|
|
|
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
bool GuiGamelistOptions::input(InputConfig* config, Input input)
|
|
|
|
{
|
2020-05-24 08:29:29 +00:00
|
|
|
if ((config->isMappedTo("b", input) ||
|
|
|
|
config->isMappedTo("select", input)) && input.value) {
|
2014-06-25 16:29:58 +00:00
|
|
|
delete this;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return mMenu.input(config, input);
|
|
|
|
}
|
|
|
|
|
2017-05-28 18:13:00 +00:00
|
|
|
HelpStyle GuiGamelistOptions::getHelpStyle()
|
|
|
|
{
|
|
|
|
HelpStyle style = HelpStyle();
|
|
|
|
style.applyTheme(mSystem->getTheme(), "system");
|
|
|
|
return style;
|
|
|
|
}
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
std::vector<HelpPrompt> GuiGamelistOptions::getHelpPrompts()
|
|
|
|
{
|
|
|
|
auto prompts = mMenu.getHelpPrompts();
|
|
|
|
prompts.push_back(HelpPrompt("b", "close"));
|
|
|
|
return prompts;
|
|
|
|
}
|
|
|
|
|
|
|
|
IGameListView* GuiGamelistOptions::getGamelist()
|
|
|
|
{
|
|
|
|
return ViewController::get()->getGameListView(mSystem).get();
|
|
|
|
}
|