diff --git a/DEVNOTES.md b/DEVNOTES.md index dc436a2ca..dfe997347 100644 --- a/DEVNOTES.md +++ b/DEVNOTES.md @@ -18,11 +18,12 @@ Some key points: * Comments should be proper sentences, starting with a capital letter and ending with a dot * Use K&R placements of braces, read the Linux Kernel coding style document for clarifications * Always use spaces between keywords and opening brackets, i.e. `if ()`, `for ()`, `while ()` etc. -* Use `std::string` instead of `char *` or `char []` unless there is a very specific reason not to +* Use `std::string` instead of `char *` or `char []` unless there is a very specific reason requiring the latter * If the arguments (and initializer list) for a function or class exceeds 4 items, arrange them vertically to make the code easier to read * Always declare one variable per line, never combine multiple declarations of the same type * Name local variables with the first word in small letters and the proceeding words starting with capital letters, e.g. myExampleVariable * Name member variables starting with a small 'm', e.g. mMyMemberVariable +* Don't pad variable declarations with spaces to make them align in columns, I'm sure it's well intended but it looks terrible * Use the same naming convention for functions as for local variables, e.g. someFunction() * Inline functions can be used but don't overdo it by using them for functions that won't be called very frequently * Never put more than one statement on a single line, except for lambda expressions diff --git a/NEWS.md b/NEWS.md index 53e668674..097ca8e68 100644 --- a/NEWS.md +++ b/NEWS.md @@ -31,4 +31,5 @@ v1.0.0 * Game images were sometimes scaled incorrectly * Non-transparent favorite icons were not rendered correctly * Restart and power-off menu entries not working (i.e. on a desktop OS) +* Toggling the screensaver didn't work as expected * Lots and lots of small bugs and inconsistencies fixed diff --git a/README.md b/README.md index 8d3725dfa..a4e046b28 100644 --- a/README.md +++ b/README.md @@ -3,26 +3,22 @@ EmulationStation Desktop Edition EmulationStation Desktop Edition is a cross-platform graphical front-end for emulators with controller and keyboard navigation. -This is a fork intended for use primarily on desktop computers where EmulationStation is not the primary interface for the computer. - -As such, this fork will not provide full control over emulator settings or emulator button mappings or provide system utility functions and similar. Instead it's assumed that the emulators and the overall environment has been properly configured upfront. +This is a fork intended for use primarily on desktop computers where EmulationStation is not the primary interface for the computer. As such, this software will not provide full control over emulator settings or emulator button mappings or provide system utility functions and similar. Instead it's assumed that the emulators and the overall environment has been properly configured upfront. The software comes preconfigured for use primarily with [RetroArch](https://www.retroarch.com), although this can certainly be changed as all emulator settings are fully configurable, even on a per-game basis. -** Help needed: ** +**Help needed:** Apart from code commits, help is especially needed for thorough testing of the software and for working on the RBSimple-DE theme. It's impossible for me to test every game system (RBSimple-DE has support for almost a 100 different systems!) so it would be especially useful to hear about any issues with starting games using the default es_systems.cfg configuration file and also if there are any issues regarding scraping for certain systems. -In general, a review of the es_systems.cfg file including the supported file extensions would be great: - -[es_systems.cfg_unix](resources/templates/es_systems.cfg_unix +In general, a review of the [es_systems.cfg](resources/templates/es_systems.cfg_unix) file including the supported file extensions would be great. As for RBSimple-DE there are quite some missing graphic files and other customizations for a number of game systems. \ Check out [MISSING.md](themes/rbsimple-DE/MISSING.md) for more details of what needs to be added. -Finally, if someone could make a proper port to the Macintosh Operating System, that would be great as then all of the three major desktop operating systems would be supported! There is some code present specifically for macOS but I've been unable to test it. +Finally, if someone could make a proper port to the Macintosh Operating System, that would be great as then all of the three major desktop operating systems would be supported. There is some code present specifically for macOS but I've been unable to test it. General information @@ -34,8 +30,6 @@ General information [DEVNOTES.md](DEVNOTES.md) is the place to go if you're interested in participating in the development of EmulationStation Desktop Edition. -Or just go ahead and browse the repository for additional information, or maybe more important, to see the actual source code :) - What it can do ============== diff --git a/es-app/src/FileFilterIndex.cpp b/es-app/src/FileFilterIndex.cpp index 4ddf6b830..eda408f57 100644 --- a/es-app/src/FileFilterIndex.cpp +++ b/es-app/src/FileFilterIndex.cpp @@ -1,3 +1,9 @@ +// +// FileFilterIndex.cpp +// +// Gamelist filters. +// + #include "FileFilterIndex.h" #include "utils/StringUtil.h" @@ -10,528 +16,499 @@ #define INCLUDE_UNKNOWN false; FileFilterIndex::FileFilterIndex() - : filterByFavorites(false), filterByGenre(false), filterByHidden(false), filterByKidGame(false), filterByPlayers(false), filterByPubDev(false), filterByRatings(false) + : filterByFavorites(false), + filterByGenre(false), + filterByHidden(false), + filterByKidGame(false), + filterByPlayers(false), + filterByPubDev(false), + filterByRatings(false) { - clearAllFilters(); - FilterDataDecl filterDecls[] = { - //type //allKeys //filteredBy //filteredKeys //primaryKey //hasSecondaryKey //secondaryKey //menuLabel - { FAVORITES_FILTER, &favoritesIndexAllKeys, &filterByFavorites, &favoritesIndexFilteredKeys,"favorite", false, "", "FAVORITES" }, - { GENRE_FILTER, &genreIndexAllKeys, &filterByGenre, &genreIndexFilteredKeys, "genre", true, "genre", "GENRE" }, - { PLAYER_FILTER, &playersIndexAllKeys, &filterByPlayers, &playersIndexFilteredKeys, "players", false, "", "PLAYERS" }, - { PUBDEV_FILTER, &pubDevIndexAllKeys, &filterByPubDev, &pubDevIndexFilteredKeys, "developer", true, "publisher", "PUBLISHER / DEVELOPER" }, - { RATINGS_FILTER, &ratingsIndexAllKeys, &filterByRatings, &ratingsIndexFilteredKeys, "rating", false, "", "RATING" }, - { KIDGAME_FILTER, &kidGameIndexAllKeys, &filterByKidGame, &kidGameIndexFilteredKeys, "kidgame", false, "", "KIDGAME" }, - { HIDDEN_FILTER, &hiddenIndexAllKeys, &filterByHidden, &hiddenIndexFilteredKeys, "hidden", false, "", "HIDDEN" } - }; + clearAllFilters(); - filterDataDecl = std::vector(filterDecls, filterDecls + sizeof(filterDecls) / sizeof(filterDecls[0])); + FilterDataDecl filterDecls[] = { + //type //allKeys //filteredBy //filteredKeys //primaryKey //hasSecondaryKey //secondaryKey //menuLabel + { FAVORITES_FILTER, &favoritesIndexAllKeys, &filterByFavorites, &favoritesIndexFilteredKeys,"favorite", false, "", "FAVORITES" }, + { GENRE_FILTER, &genreIndexAllKeys, &filterByGenre, &genreIndexFilteredKeys, "genre", true, "genre", "GENRE" }, + { PLAYER_FILTER, &playersIndexAllKeys, &filterByPlayers, &playersIndexFilteredKeys, "players", false, "", "PLAYERS" }, + { PUBDEV_FILTER, &pubDevIndexAllKeys, &filterByPubDev, &pubDevIndexFilteredKeys, "developer", true, "publisher", "PUBLISHER / DEVELOPER" }, + { RATINGS_FILTER, &ratingsIndexAllKeys, &filterByRatings, &ratingsIndexFilteredKeys, "rating", false, "", "RATING" }, + { KIDGAME_FILTER, &kidGameIndexAllKeys, &filterByKidGame, &kidGameIndexFilteredKeys, "kidgame", false, "", "KIDGAME" }, + { HIDDEN_FILTER, &hiddenIndexAllKeys, &filterByHidden, &hiddenIndexFilteredKeys, "hidden", false, "", "HIDDEN" } + }; + + filterDataDecl = std::vector(filterDecls, filterDecls + + sizeof(filterDecls) / sizeof(filterDecls[0])); } FileFilterIndex::~FileFilterIndex() { - resetIndex(); + resetIndex(); } std::vector& FileFilterIndex::getFilterDataDecls() { - return filterDataDecl; + return filterDataDecl; } void FileFilterIndex::importIndex(FileFilterIndex* indexToImport) { - struct IndexImportStructure - { - std::map* destinationIndex; - std::map* sourceIndex; - }; + struct IndexImportStructure { + std::map* destinationIndex; + std::map* sourceIndex; + }; - IndexImportStructure indexStructDecls[] = { - { &genreIndexAllKeys, &(indexToImport->genreIndexAllKeys) }, - { &playersIndexAllKeys, &(indexToImport->playersIndexAllKeys) }, - { &pubDevIndexAllKeys, &(indexToImport->pubDevIndexAllKeys) }, - { &ratingsIndexAllKeys, &(indexToImport->ratingsIndexAllKeys) }, - { &favoritesIndexAllKeys, &(indexToImport->favoritesIndexAllKeys) }, - { &hiddenIndexAllKeys, &(indexToImport->hiddenIndexAllKeys) }, - { &kidGameIndexAllKeys, &(indexToImport->kidGameIndexAllKeys) }, - }; + IndexImportStructure indexStructDecls[] = { + { &genreIndexAllKeys, &(indexToImport->genreIndexAllKeys) }, + { &playersIndexAllKeys, &(indexToImport->playersIndexAllKeys) }, + { &pubDevIndexAllKeys, &(indexToImport->pubDevIndexAllKeys) }, + { &ratingsIndexAllKeys, &(indexToImport->ratingsIndexAllKeys) }, + { &favoritesIndexAllKeys, &(indexToImport->favoritesIndexAllKeys) }, + { &hiddenIndexAllKeys, &(indexToImport->hiddenIndexAllKeys) }, + { &kidGameIndexAllKeys, &(indexToImport->kidGameIndexAllKeys) }, + }; - std::vector indexImportDecl = std::vector(indexStructDecls, indexStructDecls + sizeof(indexStructDecls) / sizeof(indexStructDecls[0])); + std::vector indexImportDecl = + std::vector(indexStructDecls, indexStructDecls + + sizeof(indexStructDecls) / sizeof(indexStructDecls[0])); - for (std::vector::const_iterator indexesIt = indexImportDecl.cbegin(); indexesIt != indexImportDecl.cend(); ++indexesIt ) - { - for (std::map::const_iterator sourceIt = (*indexesIt).sourceIndex->cbegin(); sourceIt != (*indexesIt).sourceIndex->cend(); ++sourceIt ) - { - if ((*indexesIt).destinationIndex->find((*sourceIt).first) == (*indexesIt).destinationIndex->cend()) - { - // entry doesn't exist - (*((*indexesIt).destinationIndex))[(*sourceIt).first] = (*sourceIt).second; - } - else - { - (*((*indexesIt).destinationIndex))[(*sourceIt).first] += (*sourceIt).second; - } - } - } + for (std::vector::const_iterator indexesIt = + indexImportDecl.cbegin(); indexesIt != indexImportDecl.cend(); ++indexesIt ) + { + for (std::map::const_iterator sourceIt = + (*indexesIt).sourceIndex->cbegin(); sourceIt != + (*indexesIt).sourceIndex->cend(); ++sourceIt ) { + if ((*indexesIt).destinationIndex->find((*sourceIt).first) == + (*indexesIt).destinationIndex->cend()) + // Entry doesn't exist. + (*((*indexesIt).destinationIndex))[(*sourceIt).first] = (*sourceIt).second; + else + (*((*indexesIt).destinationIndex))[(*sourceIt).first] += (*sourceIt).second; + } + } } + void FileFilterIndex::resetIndex() { - clearAllFilters(); - clearIndex(genreIndexAllKeys); - clearIndex(playersIndexAllKeys); - clearIndex(pubDevIndexAllKeys); - clearIndex(ratingsIndexAllKeys); - clearIndex(favoritesIndexAllKeys); - clearIndex(hiddenIndexAllKeys); - clearIndex(kidGameIndexAllKeys); + clearAllFilters(); + clearIndex(genreIndexAllKeys); + clearIndex(playersIndexAllKeys); + clearIndex(pubDevIndexAllKeys); + clearIndex(ratingsIndexAllKeys); + clearIndex(favoritesIndexAllKeys); + clearIndex(hiddenIndexAllKeys); + clearIndex(kidGameIndexAllKeys); } -std::string FileFilterIndex::getIndexableKey(FileData* game, FilterIndexType type, bool getSecondary) +std::string FileFilterIndex::getIndexableKey(FileData* game, + FilterIndexType type, bool getSecondary) { - std::string key = ""; - switch(type) - { - case GENRE_FILTER: - { - key = Utils::String::toUpper(game->metadata.get("genre")); - key = Utils::String::trim(key); - if (getSecondary && !key.empty()) { - std::istringstream f(key); - std::string newKey; - getline(f, newKey, '/'); - if (!newKey.empty() && newKey != key) - { - key = newKey; - } - else - { - key = std::string(); - } - } - break; - } - case PLAYER_FILTER: - { - if (getSecondary) - break; + std::string key = ""; + switch (type) { + case GENRE_FILTER: { + key = Utils::String::toUpper(game->metadata.get("genre")); + key = Utils::String::trim(key); + if (getSecondary && !key.empty()) { + std::istringstream f(key); + std::string newKey; + getline(f, newKey, '/'); + if (!newKey.empty() && newKey != key) + key = newKey; + else + key = std::string(); + } + break; + } + case PLAYER_FILTER: { + if (getSecondary) + break; - key = game->metadata.get("players"); - break; - } - case PUBDEV_FILTER: - { - key = Utils::String::toUpper(game->metadata.get("publisher")); - key = Utils::String::trim(key); + key = game->metadata.get("players"); + break; + } + case PUBDEV_FILTER: { + key = Utils::String::toUpper(game->metadata.get("publisher")); + key = Utils::String::trim(key); - if ((getSecondary && !key.empty()) || (!getSecondary && key.empty())) - key = Utils::String::toUpper(game->metadata.get("developer")); - else - key = Utils::String::toUpper(game->metadata.get("publisher")); - break; - } - case RATINGS_FILTER: - { - int ratingNumber = 0; - if (!getSecondary) - { - std::string ratingString = game->metadata.get("rating"); - if (!ratingString.empty()) { - try { - ratingNumber = (int)((std::stod(ratingString)*5)+0.5); - if (ratingNumber < 0) - ratingNumber = 0; + if ((getSecondary && !key.empty()) || (!getSecondary && key.empty())) + key = Utils::String::toUpper(game->metadata.get("developer")); + else + key = Utils::String::toUpper(game->metadata.get("publisher")); + break; + } + case RATINGS_FILTER: { + int ratingNumber = 0; + if (!getSecondary) { + std::string ratingString = game->metadata.get("rating"); + if (!ratingString.empty()) { + try { + ratingNumber = (int)((std::stod(ratingString)*5)+0.5); + if (ratingNumber < 0) + ratingNumber = 0; - key = std::to_string(ratingNumber) + " STARS"; - } - catch (int e) - { - LOG(LogError) << "Error parsing Rating (invalid value, exception nr.): " << ratingString << ", " << e; - } - } - } - break; - } - case FAVORITES_FILTER: - { - if (game->getType() != GAME) - return "FALSE"; - key = Utils::String::toUpper(game->metadata.get("favorite")); - break; - } - case HIDDEN_FILTER: - { - if (game->getType() != GAME) - return "FALSE"; - key = Utils::String::toUpper(game->metadata.get("hidden")); - break; - } - case KIDGAME_FILTER: - { - if (game->getType() != GAME) - return "FALSE"; - key = Utils::String::toUpper(game->metadata.get("kidgame")); - break; - } - } - key = Utils::String::trim(key); - if (key.empty() || (type == RATINGS_FILTER && key == "0 STARS")) { - key = UNKNOWN_LABEL; - } - return key; + key = std::to_string(ratingNumber) + " STARS"; + } + catch (int e) { + LOG(LogError) << "Error parsing Rating (invalid value, exception nr.): " << + ratingString << ", " << e; + } + } + } + break; + } + case FAVORITES_FILTER: { + if (game->getType() != GAME) + return "FALSE"; + key = Utils::String::toUpper(game->metadata.get("favorite")); + break; + } + case HIDDEN_FILTER: { + if (game->getType() != GAME) + return "FALSE"; + key = Utils::String::toUpper(game->metadata.get("hidden")); + break; + } + case KIDGAME_FILTER: { + if (game->getType() != GAME) + return "FALSE"; + key = Utils::String::toUpper(game->metadata.get("kidgame")); + break; + } + } + key = Utils::String::trim(key); + if (key.empty() || (type == RATINGS_FILTER && key == "0 STARS")) { + key = UNKNOWN_LABEL; + } + return key; } void FileFilterIndex::addToIndex(FileData* game) { - manageGenreEntryInIndex(game); - managePlayerEntryInIndex(game); - managePubDevEntryInIndex(game); - manageRatingsEntryInIndex(game); - manageFavoritesEntryInIndex(game); - manageHiddenEntryInIndex(game); - manageKidGameEntryInIndex(game); + manageGenreEntryInIndex(game); + managePlayerEntryInIndex(game); + managePubDevEntryInIndex(game); + manageRatingsEntryInIndex(game); + manageFavoritesEntryInIndex(game); + manageHiddenEntryInIndex(game); + manageKidGameEntryInIndex(game); } void FileFilterIndex::removeFromIndex(FileData* game) { - manageGenreEntryInIndex(game, true); - managePlayerEntryInIndex(game, true); - managePubDevEntryInIndex(game, true); - manageRatingsEntryInIndex(game, true); - manageFavoritesEntryInIndex(game, true); - manageHiddenEntryInIndex(game, true); - manageKidGameEntryInIndex(game, true); + manageGenreEntryInIndex(game, true); + managePlayerEntryInIndex(game, true); + managePubDevEntryInIndex(game, true); + manageRatingsEntryInIndex(game, true); + manageFavoritesEntryInIndex(game, true); + manageHiddenEntryInIndex(game, true); + manageKidGameEntryInIndex(game, true); } void FileFilterIndex::setFilter(FilterIndexType type, std::vector* values) { - // test if it exists before setting - if(type == NONE) - { - clearAllFilters(); - } - else - { - for (std::vector::const_iterator it = filterDataDecl.cbegin(); it != filterDataDecl.cend(); ++it ) { - if ((*it).type == type) - { - FilterDataDecl filterData = (*it); - *(filterData.filteredByRef) = values->size() > 0; - filterData.currentFilteredKeys->clear(); - for (std::vector::const_iterator vit = values->cbegin(); vit != values->cend(); ++vit ) { - // check if exists - if (filterData.allIndexKeys->find(*vit) != filterData.allIndexKeys->cend()) { - filterData.currentFilteredKeys->push_back(std::string(*vit)); - } - } - } - } - } - return; + // Test if it exists before setting. + if (type == NONE) { + clearAllFilters(); + } + else { + for (std::vector::const_iterator it = filterDataDecl.cbegin(); + it != filterDataDecl.cend(); ++it ) { + if ((*it).type == type) { + FilterDataDecl filterData = (*it); + *(filterData.filteredByRef) = values->size() > 0; + filterData.currentFilteredKeys->clear(); + for (std::vector::const_iterator vit = + values->cbegin(); vit != values->cend(); ++vit ) { + // Check if exists. + if (filterData.allIndexKeys->find(*vit) != filterData.allIndexKeys->cend()) { + filterData.currentFilteredKeys->push_back(std::string(*vit)); + } + } + } + } + } + return; } void FileFilterIndex::clearAllFilters() { - for (std::vector::const_iterator it = filterDataDecl.cbegin(); it != filterDataDecl.cend(); ++it ) - { - FilterDataDecl filterData = (*it); - *(filterData.filteredByRef) = false; - filterData.currentFilteredKeys->clear(); - } - return; + for (std::vector::const_iterator it = filterDataDecl.cbegin(); + it != filterDataDecl.cend(); ++it ) { + FilterDataDecl filterData = (*it); + *(filterData.filteredByRef) = false; + filterData.currentFilteredKeys->clear(); + } + return; } void FileFilterIndex::resetFilters() { - clearAllFilters(); - setUIModeFilters(); + clearAllFilters(); + setUIModeFilters(); } void FileFilterIndex::setUIModeFilters() { - if(!Settings::getInstance()->getBool("ForceDisableFilters")){ - if (UIModeController::getInstance()->isUIModeKiosk()) - { - filterByHidden = true; - std::vector val = { "FALSE" }; - setFilter(HIDDEN_FILTER, &val); - } - if (UIModeController::getInstance()->isUIModeKid()) - { - filterByKidGame = true; - std::vector val = { "TRUE" }; - setFilter(KIDGAME_FILTER, &val); - } - } + if (!Settings::getInstance()->getBool("ForceDisableFilters")){ + if (UIModeController::getInstance()->isUIModeKiosk()) { + filterByHidden = true; + std::vector val = { "FALSE" }; + setFilter(HIDDEN_FILTER, &val); + } + if (UIModeController::getInstance()->isUIModeKid()) { + filterByKidGame = true; + std::vector val = { "TRUE" }; + setFilter(KIDGAME_FILTER, &val); + } + } } void FileFilterIndex::debugPrintIndexes() { - LOG(LogInfo) << "Printing Indexes..."; - for (auto x: playersIndexAllKeys) { - LOG(LogInfo) << "Multiplayer Index: " << x.first << ": " << x.second; - } - for (auto x: genreIndexAllKeys) { - LOG(LogInfo) << "Genre Index: " << x.first << ": " << x.second; - } - for (auto x: ratingsIndexAllKeys) { - LOG(LogInfo) << "Ratings Index: " << x.first << ": " << x.second; - } - for (auto x: pubDevIndexAllKeys) { - LOG(LogInfo) << "PubDev Index: " << x.first << ": " << x.second; - } - for (auto x: favoritesIndexAllKeys) { - LOG(LogInfo) << "Favorites Index: " << x.first << ": " << x.second; - } - for (auto x : hiddenIndexAllKeys) { - LOG(LogInfo) << "Hidden Index: " << x.first << ": " << x.second; - } - for (auto x : kidGameIndexAllKeys) { - LOG(LogInfo) << "KidGames Index: " << x.first << ": " << x.second; - } + LOG(LogInfo) << "Printing Indexes..."; + for (auto x: playersIndexAllKeys) { + LOG(LogInfo) << "Multiplayer Index: " << x.first << ": " << x.second; + } + for (auto x: genreIndexAllKeys) { + LOG(LogInfo) << "Genre Index: " << x.first << ": " << x.second; + } + for (auto x: ratingsIndexAllKeys) { + LOG(LogInfo) << "Ratings Index: " << x.first << ": " << x.second; + } + for (auto x: pubDevIndexAllKeys) { + LOG(LogInfo) << "PubDev Index: " << x.first << ": " << x.second; + } + for (auto x: favoritesIndexAllKeys) { + LOG(LogInfo) << "Favorites Index: " << x.first << ": " << x.second; + } + for (auto x : hiddenIndexAllKeys) { + LOG(LogInfo) << "Hidden Index: " << x.first << ": " << x.second; + } + for (auto x : kidGameIndexAllKeys) { + LOG(LogInfo) << "KidGames Index: " << x.first << ": " << x.second; + } } bool FileFilterIndex::showFile(FileData* game) { - // this shouldn't happen, but just in case let's get it out of the way - if (!isFiltered()) - return true; + // This shouldn't happen, but just in case let's get it out of the way. + if (!isFiltered()) + return true; - // if folder, needs further inspection - i.e. see if folder contains at least one element - // that should be shown - if (game->getType() == FOLDER) { - std::vector children = game->getChildren(); - // iterate through all of the children, until there's a match + // If folder, needs further inspection - i.e. see if folder contains at least one element + // that should be shown. + if (game->getType() == FOLDER) { + std::vector children = game->getChildren(); + // Iterate through all of the children, until there's a match. + for (std::vector::const_iterator it = children.cbegin(); + it != children.cend(); ++it ) { + if (showFile(*it)) + return true; + } + return false; + } - for (std::vector::const_iterator it = children.cbegin(); it != children.cend(); ++it ) { - if (showFile(*it)) - { - return true; - } - } - return false; - } + bool keepGoing = false; - bool keepGoing = false; + for (std::vector::const_iterator it = filterDataDecl.cbegin(); + it != filterDataDecl.cend(); ++it ) { + FilterDataDecl filterData = (*it); + if (*(filterData.filteredByRef)) { + // Try to find a match. + std::string key = getIndexableKey(game, filterData.type, false); + keepGoing = isKeyBeingFilteredBy(key, filterData.type); - for (std::vector::const_iterator it = filterDataDecl.cbegin(); it != filterDataDecl.cend(); ++it ) { - FilterDataDecl filterData = (*it); - if(*(filterData.filteredByRef)) - { - // try to find a match - std::string key = getIndexableKey(game, filterData.type, false); - keepGoing = isKeyBeingFilteredBy(key, filterData.type); - - // if we didn't find a match, try for secondary keys - i.e. publisher and dev, or first genre - if (!keepGoing) - { - if (!filterData.hasSecondaryKey) - { - return false; - } - std::string secKey = getIndexableKey(game, filterData.type, true); - if (secKey != UNKNOWN_LABEL) - { - keepGoing = isKeyBeingFilteredBy(secKey, filterData.type); - } - } - // if still nothing, then it's not a match - if (!keepGoing) - return false; - - } - - } - - return keepGoing; + // If we didn't find a match, try for secondary keys - i.e. + // publisher and dev, or first genre. + if (!keepGoing) { + if (!filterData.hasSecondaryKey) + return false; + std::string secKey = getIndexableKey(game, filterData.type, true); + if (secKey != UNKNOWN_LABEL) + keepGoing = isKeyBeingFilteredBy(secKey, filterData.type); + } + // If still nothing, then it's not a match. + if (!keepGoing) + return false; + } + } + return keepGoing; } bool FileFilterIndex::isKeyBeingFilteredBy(std::string key, FilterIndexType type) { - const FilterIndexType filterTypes[7] = { FAVORITES_FILTER, GENRE_FILTER, PLAYER_FILTER, PUBDEV_FILTER, RATINGS_FILTER,HIDDEN_FILTER, KIDGAME_FILTER }; - std::vector filterKeysList[7] = { favoritesIndexFilteredKeys, genreIndexFilteredKeys, playersIndexFilteredKeys, pubDevIndexFilteredKeys, ratingsIndexFilteredKeys, hiddenIndexFilteredKeys, kidGameIndexFilteredKeys }; + const FilterIndexType filterTypes[7] = { FAVORITES_FILTER, GENRE_FILTER, + PLAYER_FILTER, PUBDEV_FILTER, RATINGS_FILTER,HIDDEN_FILTER, KIDGAME_FILTER }; + std::vector filterKeysList[7] = { favoritesIndexFilteredKeys, + genreIndexFilteredKeys, playersIndexFilteredKeys, pubDevIndexFilteredKeys, + ratingsIndexFilteredKeys, hiddenIndexFilteredKeys, kidGameIndexFilteredKeys }; - for (int i = 0; i < 7; i++) - { - if (filterTypes[i] == type) - { - for (std::vector::const_iterator it = filterKeysList[i].cbegin(); it != filterKeysList[i].cend(); ++it ) - { - if (key == (*it)) - { - return true; - } - } - return false; - } - } - - return false; + for (int i = 0; i < 7; i++) { + if (filterTypes[i] == type) { + for (std::vector::const_iterator it = filterKeysList[i].cbegin(); + it != filterKeysList[i].cend(); ++it ) { + if (key == (*it)) + return true; + } + return false; + } + } + return false; } void FileFilterIndex::manageGenreEntryInIndex(FileData* game, bool remove) { - std::string key = getIndexableKey(game, GENRE_FILTER, false); + std::string key = getIndexableKey(game, GENRE_FILTER, false); - // flag for including unknowns - bool includeUnknown = INCLUDE_UNKNOWN; + // Flag for including unknowns. + bool includeUnknown = INCLUDE_UNKNOWN; - // only add unknown in pubdev IF both dev and pub are empty - if (!includeUnknown && (key == UNKNOWN_LABEL || key == "BIOS")) { - // no valid genre info found - return; - } + // Only add unknown in pubdev IF both dev and pub are empty. + if (!includeUnknown && (key == UNKNOWN_LABEL || key == "BIOS")) + // No valid genre info found. + return; - manageIndexEntry(&genreIndexAllKeys, key, remove); + manageIndexEntry(&genreIndexAllKeys, key, remove); - key = getIndexableKey(game, GENRE_FILTER, true); - if (!includeUnknown && key == UNKNOWN_LABEL) - { - manageIndexEntry(&genreIndexAllKeys, key, remove); - } + key = getIndexableKey(game, GENRE_FILTER, true); + if (!includeUnknown && key == UNKNOWN_LABEL) + manageIndexEntry(&genreIndexAllKeys, key, remove); } void FileFilterIndex::managePlayerEntryInIndex(FileData* game, bool remove) { - // flag for including unknowns - bool includeUnknown = INCLUDE_UNKNOWN; - std::string key = getIndexableKey(game, PLAYER_FILTER, false); + // Flag for including unknowns. + bool includeUnknown = INCLUDE_UNKNOWN; + std::string key = getIndexableKey(game, PLAYER_FILTER, false); - // only add unknown in pubdev IF both dev and pub are empty - if (!includeUnknown && key == UNKNOWN_LABEL) { - // no valid player info found - return; - } + // Only add unknown in pubdev IF both dev and pub are empty. + if (!includeUnknown && key == UNKNOWN_LABEL) + // No valid player info found. + return; - manageIndexEntry(&playersIndexAllKeys, key, remove); + manageIndexEntry(&playersIndexAllKeys, key, remove); } void FileFilterIndex::managePubDevEntryInIndex(FileData* game, bool remove) { - std::string pub = getIndexableKey(game, PUBDEV_FILTER, false); - std::string dev = getIndexableKey(game, PUBDEV_FILTER, true); + std::string pub = getIndexableKey(game, PUBDEV_FILTER, false); + std::string dev = getIndexableKey(game, PUBDEV_FILTER, true); - // flag for including unknowns - bool includeUnknown = INCLUDE_UNKNOWN; - bool unknownPub = false; - bool unknownDev = false; + // Flag for including unknowns. + bool includeUnknown = INCLUDE_UNKNOWN; + bool unknownPub = false; + bool unknownDev = false; - if (pub == UNKNOWN_LABEL) { - unknownPub = true; - } - if (dev == UNKNOWN_LABEL) { - unknownDev = true; - } + if (pub == UNKNOWN_LABEL) + unknownPub = true; - if (!includeUnknown && unknownDev && unknownPub) { - // no valid rating info found - return; - } + if (dev == UNKNOWN_LABEL) + unknownDev = true; - if (unknownDev && unknownPub) { - // if no info at all - manageIndexEntry(&pubDevIndexAllKeys, pub, remove); - } - else - { - if (!unknownDev) { - // if no info at all - manageIndexEntry(&pubDevIndexAllKeys, dev, remove); - } - if (!unknownPub) { - // if no info at all - manageIndexEntry(&pubDevIndexAllKeys, pub, remove); - } - } + if (!includeUnknown && unknownDev && unknownPub) + // No valid rating info found. + return; + + if (unknownDev && unknownPub) { + // If no info at all. + manageIndexEntry(&pubDevIndexAllKeys, pub, remove); + } + else { + if (!unknownDev) { + // If no info at all. + manageIndexEntry(&pubDevIndexAllKeys, dev, remove); + } + if (!unknownPub) { + // If no info at all. + manageIndexEntry(&pubDevIndexAllKeys, pub, remove); + } + } } void FileFilterIndex::manageRatingsEntryInIndex(FileData* game, bool remove) { - std::string key = getIndexableKey(game, RATINGS_FILTER, false); + std::string key = getIndexableKey(game, RATINGS_FILTER, false); - // flag for including unknowns - bool includeUnknown = INCLUDE_UNKNOWN; + // Flag for including unknowns. + bool includeUnknown = INCLUDE_UNKNOWN; - if (!includeUnknown && key == UNKNOWN_LABEL) { - // no valid rating info found - return; - } + if (!includeUnknown && key == UNKNOWN_LABEL) + // No valid rating info found. + return; - manageIndexEntry(&ratingsIndexAllKeys, key, remove); + manageIndexEntry(&ratingsIndexAllKeys, key, remove); } void FileFilterIndex::manageFavoritesEntryInIndex(FileData* game, bool remove) { - // flag for including unknowns - bool includeUnknown = INCLUDE_UNKNOWN; - std::string key = getIndexableKey(game, FAVORITES_FILTER, false); - if (!includeUnknown && key == UNKNOWN_LABEL) { - // no valid favorites info found - return; - } + // Flag for including unknowns. + bool includeUnknown = INCLUDE_UNKNOWN; + std::string key = getIndexableKey(game, FAVORITES_FILTER, false); - manageIndexEntry(&favoritesIndexAllKeys, key, remove); + if (!includeUnknown && key == UNKNOWN_LABEL) + // No valid favorites info found. + return; + + manageIndexEntry(&favoritesIndexAllKeys, key, remove); } void FileFilterIndex::manageHiddenEntryInIndex(FileData* game, bool remove) { - // flag for including unknowns - bool includeUnknown = INCLUDE_UNKNOWN; - std::string key = getIndexableKey(game, HIDDEN_FILTER, false); - if (!includeUnknown && key == UNKNOWN_LABEL) { - // no valid hidden info found - return; - } + // Flag for including unknowns. + bool includeUnknown = INCLUDE_UNKNOWN; + std::string key = getIndexableKey(game, HIDDEN_FILTER, false); - manageIndexEntry(&hiddenIndexAllKeys, key, remove); + if (!includeUnknown && key == UNKNOWN_LABEL) + // No valid hidden info found. + return; + + manageIndexEntry(&hiddenIndexAllKeys, key, remove); } void FileFilterIndex::manageKidGameEntryInIndex(FileData* game, bool remove) { - // flag for including unknowns - bool includeUnknown = INCLUDE_UNKNOWN; - std::string key = getIndexableKey(game, KIDGAME_FILTER, false); - if (!includeUnknown && key == UNKNOWN_LABEL) { - // no valid kidgame info found - return; - } + // Flag for including unknowns. + bool includeUnknown = INCLUDE_UNKNOWN; + std::string key = getIndexableKey(game, KIDGAME_FILTER, false); - manageIndexEntry(&kidGameIndexAllKeys, key, remove); + if (!includeUnknown && key == UNKNOWN_LABEL) + // No valid kidgame info found. + return; + + manageIndexEntry(&kidGameIndexAllKeys, key, remove); } -void FileFilterIndex::manageIndexEntry(std::map* index, std::string key, bool remove) { - bool includeUnknown = INCLUDE_UNKNOWN; - if (!includeUnknown && key == UNKNOWN_LABEL) - return; - if (remove) { - // removing entry - if (index->find(key) == index->cend()) - { - // this shouldn't happen - LOG(LogInfo) << "Couldn't find entry in index! " << key; - } - else - { - (index->at(key))--; - if(index->at(key) <= 0) { - index->erase(key); - } - } - } - else - { - // adding entry - if (index->find(key) == index->cend()) - { - (*index)[key] = 1; - } - else - { - (index->at(key))++; - } - } +void FileFilterIndex::manageIndexEntry(std::map* index, + std::string key, bool remove) +{ + bool includeUnknown = INCLUDE_UNKNOWN; + if (!includeUnknown && key == UNKNOWN_LABEL) + return; + + if (remove) { + // Removing entry. + if (index->find(key) == index->cend()) { + // This shouldn't happen. + LOG(LogInfo) << "Couldn't find entry in index! " << key; + } + else { + (index->at(key))--; + if (index->at(key) <= 0) { + index->erase(key); + } + } + } + else { + // Adding entry. + if (index->find(key) == index->cend()) + (*index)[key] = 1; + else + (index->at(key))++; + } } void FileFilterIndex::clearIndex(std::map indexMap) { - indexMap.clear(); -} \ No newline at end of file + indexMap.clear(); +} diff --git a/es-app/src/FileFilterIndex.h b/es-app/src/FileFilterIndex.h index 93b9c8d9f..2c4eb3de3 100644 --- a/es-app/src/FileFilterIndex.h +++ b/es-app/src/FileFilterIndex.h @@ -1,3 +1,9 @@ +// +// FileFilterIndex.h +// +// Gamelist filters. +// + #pragma once #ifndef ES_APP_FILE_FILTER_INDEX_H #define ES_APP_FILE_FILTER_INDEX_H @@ -7,91 +13,90 @@ class FileData; -enum FilterIndexType -{ - NONE, - GENRE_FILTER, - PLAYER_FILTER, - PUBDEV_FILTER, - RATINGS_FILTER, - FAVORITES_FILTER, - HIDDEN_FILTER, - KIDGAME_FILTER +enum FilterIndexType { + NONE, + GENRE_FILTER, + PLAYER_FILTER, + PUBDEV_FILTER, + RATINGS_FILTER, + FAVORITES_FILTER, + HIDDEN_FILTER, + KIDGAME_FILTER }; -struct FilterDataDecl -{ - FilterIndexType type; // type of filter - std::map* allIndexKeys; // all possible filters for this type - bool* filteredByRef; // is it filtered by this type - std::vector* currentFilteredKeys; // current keys being filtered for - std::string primaryKey; // primary key in metadata - bool hasSecondaryKey; // has secondary key for comparison - std::string secondaryKey; // what's the secondary key - std::string menuLabel; // text to show in menu +struct FilterDataDecl { + FilterIndexType type; // Type of filter. + std::map* allIndexKeys; // All possible filters for this type. + bool* filteredByRef; // Is it filtered by this type? + std::vector* currentFilteredKeys; // Current keys being filtered for. + std::string primaryKey; // Primary key in metadata. + bool hasSecondaryKey; // Has secondary key for comparison. + std::string secondaryKey; // What's the secondary key. + std::string menuLabel; // Text to show in menu. }; class FileFilterIndex { public: - FileFilterIndex(); - ~FileFilterIndex(); - void addToIndex(FileData* game); - void removeFromIndex(FileData* game); - void setFilter(FilterIndexType type, std::vector* values); - void clearAllFilters(); - void debugPrintIndexes(); - bool showFile(FileData* game); - bool isFiltered() { return (filterByGenre || filterByPlayers || filterByPubDev || filterByRatings || filterByFavorites || filterByHidden || filterByKidGame); }; - bool isKeyBeingFilteredBy(std::string key, FilterIndexType type); - std::vector& getFilterDataDecls(); + FileFilterIndex(); + ~FileFilterIndex(); + void addToIndex(FileData* game); + void removeFromIndex(FileData* game); + void setFilter(FilterIndexType type, std::vector* values); + void clearAllFilters(); + void debugPrintIndexes(); + bool showFile(FileData* game); + bool isFiltered() { return (filterByGenre || filterByPlayers || filterByPubDev || + filterByRatings || filterByFavorites || filterByHidden || filterByKidGame); }; + bool isKeyBeingFilteredBy(std::string key, FilterIndexType type); + std::vector& getFilterDataDecls(); - void importIndex(FileFilterIndex* indexToImport); - void resetIndex(); - void resetFilters(); - void setUIModeFilters(); + void importIndex(FileFilterIndex* indexToImport); + void resetIndex(); + void resetFilters(); + void setUIModeFilters(); private: - std::vector filterDataDecl; - std::string getIndexableKey(FileData* game, FilterIndexType type, bool getSecondary); + std::vector filterDataDecl; + std::string getIndexableKey(FileData* game, FilterIndexType type, bool getSecondary); - void manageGenreEntryInIndex(FileData* game, bool remove = false); - void managePlayerEntryInIndex(FileData* game, bool remove = false); - void managePubDevEntryInIndex(FileData* game, bool remove = false); - void manageRatingsEntryInIndex(FileData* game, bool remove = false); - void manageFavoritesEntryInIndex(FileData* game, bool remove = false); - void manageHiddenEntryInIndex(FileData* game, bool remove = false); - void manageKidGameEntryInIndex(FileData* game, bool remove = false); + void manageGenreEntryInIndex(FileData* game, bool remove = false); + void managePlayerEntryInIndex(FileData* game, bool remove = false); + void managePubDevEntryInIndex(FileData* game, bool remove = false); + void manageRatingsEntryInIndex(FileData* game, bool remove = false); + void manageFavoritesEntryInIndex(FileData* game, bool remove = false); + void manageHiddenEntryInIndex(FileData* game, bool remove = false); + void manageKidGameEntryInIndex(FileData* game, bool remove = false); - void manageIndexEntry(std::map* index, std::string key, bool remove); + void manageIndexEntry(std::map* index, std::string key, bool remove); - void clearIndex(std::map indexMap); + void clearIndex(std::map indexMap); - bool filterByGenre; - bool filterByPlayers; - bool filterByPubDev; - bool filterByRatings; - bool filterByFavorites; - bool filterByHidden; - bool filterByKidGame; + bool filterByGenre; + bool filterByPlayers; + bool filterByPubDev; + bool filterByRatings; + bool filterByFavorites; + bool filterByHidden; + bool filterByKidGame; - std::map genreIndexAllKeys; - std::map playersIndexAllKeys; - std::map pubDevIndexAllKeys; - std::map ratingsIndexAllKeys; - std::map favoritesIndexAllKeys; - std::map hiddenIndexAllKeys; - std::map kidGameIndexAllKeys; + std::map genreIndexAllKeys; + std::map playersIndexAllKeys; + std::map pubDevIndexAllKeys; + std::map ratingsIndexAllKeys; + std::map favoritesIndexAllKeys; + std::map hiddenIndexAllKeys; + std::map kidGameIndexAllKeys; - std::vector genreIndexFilteredKeys; - std::vector playersIndexFilteredKeys; - std::vector pubDevIndexFilteredKeys; - std::vector ratingsIndexFilteredKeys; - std::vector favoritesIndexFilteredKeys; - std::vector hiddenIndexFilteredKeys; - std::vector kidGameIndexFilteredKeys; + std::vector genreIndexFilteredKeys; + std::vector playersIndexFilteredKeys; + std::vector pubDevIndexFilteredKeys; + std::vector ratingsIndexFilteredKeys; + std::vector favoritesIndexFilteredKeys; + std::vector hiddenIndexFilteredKeys; + std::vector kidGameIndexFilteredKeys; - FileData* mRootFolder; + FileData* mRootFolder; }; diff --git a/es-app/src/FileSorts.cpp b/es-app/src/FileSorts.cpp index 58b4ba90a..1922adaff 100644 --- a/es-app/src/FileSorts.cpp +++ b/es-app/src/FileSorts.cpp @@ -1,118 +1,125 @@ +// +// FileSorts.cpp +// +// Gamelist sorting functions. +// Actual sorting takes place in FileData. +// + #include "FileSorts.h" #include "utils/StringUtil.h" namespace FileSorts { - const FileData::SortType typesArr[] = { - FileData::SortType(&compareName, true, "filename, ascending"), - FileData::SortType(&compareName, false, "filename, descending"), + const FileData::SortType typesArr[] = { + FileData::SortType(&compareName, true, "filename, ascending"), + FileData::SortType(&compareName, false, "filename, descending"), - FileData::SortType(&compareRating, true, "rating, ascending"), - FileData::SortType(&compareRating, false, "rating, descending"), + FileData::SortType(&compareRating, true, "rating, ascending"), + FileData::SortType(&compareRating, false, "rating, descending"), - FileData::SortType(&compareTimesPlayed, true, "times played, ascending"), - FileData::SortType(&compareTimesPlayed, false, "times played, descending"), + FileData::SortType(&compareTimesPlayed, true, "times played, ascending"), + FileData::SortType(&compareTimesPlayed, false, "times played, descending"), - FileData::SortType(&compareLastPlayed, true, "last played, ascending"), - FileData::SortType(&compareLastPlayed, false, "last played, descending"), + FileData::SortType(&compareLastPlayed, true, "last played, ascending"), + FileData::SortType(&compareLastPlayed, false, "last played, descending"), - FileData::SortType(&compareNumPlayers, true, "number players, ascending"), - FileData::SortType(&compareNumPlayers, false, "number players, descending"), + FileData::SortType(&compareNumPlayers, true, "number players, ascending"), + FileData::SortType(&compareNumPlayers, false, "number players, descending"), - FileData::SortType(&compareReleaseDate, true, "release date, ascending"), - FileData::SortType(&compareReleaseDate, false, "release date, descending"), + FileData::SortType(&compareReleaseDate, true, "release date, ascending"), + FileData::SortType(&compareReleaseDate, false, "release date, descending"), - FileData::SortType(&compareGenre, true, "genre, ascending"), - FileData::SortType(&compareGenre, false, "genre, descending"), + FileData::SortType(&compareGenre, true, "genre, ascending"), + FileData::SortType(&compareGenre, false, "genre, descending"), - FileData::SortType(&compareDeveloper, true, "developer, ascending"), - FileData::SortType(&compareDeveloper, false, "developer, descending"), + FileData::SortType(&compareDeveloper, true, "developer, ascending"), + FileData::SortType(&compareDeveloper, false, "developer, descending"), - FileData::SortType(&comparePublisher, true, "publisher, ascending"), - FileData::SortType(&comparePublisher, false, "publisher, descending"), + FileData::SortType(&comparePublisher, true, "publisher, ascending"), + FileData::SortType(&comparePublisher, false, "publisher, descending"), - FileData::SortType(&compareSystem, true, "system, ascending"), - FileData::SortType(&compareSystem, false, "system, descending") - }; + FileData::SortType(&compareSystem, true, "system, ascending"), + FileData::SortType(&compareSystem, false, "system, descending") + }; - const std::vector SortTypes(typesArr, typesArr + sizeof(typesArr)/sizeof(typesArr[0])); + const std::vector SortTypes(typesArr, typesArr + + sizeof(typesArr)/sizeof(typesArr[0])); - //returns if file1 should come before file2 - bool compareName(const FileData* file1, const FileData* file2) - { - // we compare the actual metadata name, as collection files have the system appended which messes up the order - std::string name1 = Utils::String::toUpper(file1->metadata.get("sortname")); - std::string name2 = Utils::String::toUpper(file2->metadata.get("sortname")); - if(name1.empty()){ - name1 = Utils::String::toUpper(file1->metadata.get("name")); - } - if(name2.empty()){ - name2 = Utils::String::toUpper(file2->metadata.get("name")); - } - return name1.compare(name2) < 0; - } + // Returns if file1 should come before file2. + bool compareName(const FileData* file1, const FileData* file2) + { + // We compare the actual metadata name, as collection files have the system + // appended which messes up the order. + std::string name1 = Utils::String::toUpper(file1->metadata.get("sortname")); + std::string name2 = Utils::String::toUpper(file2->metadata.get("sortname")); + if(name1.empty()){ + name1 = Utils::String::toUpper(file1->metadata.get("name")); + } + if(name2.empty()){ + name2 = Utils::String::toUpper(file2->metadata.get("name")); + } + return name1.compare(name2) < 0; + } - bool compareRating(const FileData* file1, const FileData* file2) - { - return file1->metadata.getFloat("rating") < file2->metadata.getFloat("rating"); - } + bool compareRating(const FileData* file1, const FileData* file2) + { + return file1->metadata.getFloat("rating") < file2->metadata.getFloat("rating"); + } - bool compareTimesPlayed(const FileData* file1, const FileData* file2) - { - //only games have playcount metadata - if(file1->metadata.getType() == GAME_METADATA && file2->metadata.getType() == GAME_METADATA) - { - return (file1)->metadata.getInt("playcount") < (file2)->metadata.getInt("playcount"); - } + bool compareTimesPlayed(const FileData* file1, const FileData* file2) + { + //only games have playcount metadata + if(file1->metadata.getType() == GAME_METADATA && file2->metadata.getType() == GAME_METADATA) + return (file1)->metadata.getInt("playcount") < (file2)->metadata.getInt("playcount"); - return false; - } + return false; + } - bool compareLastPlayed(const FileData* file1, const FileData* file2) - { - // since it's stored as an ISO string (YYYYMMDDTHHMMSS), we can compare as a string - // as it's a lot faster than the time casts and then time comparisons - return (file1)->metadata.get("lastplayed") < (file2)->metadata.get("lastplayed"); - } + bool compareLastPlayed(const FileData* file1, const FileData* file2) + { + // Since it's stored as an ISO string (YYYYMMDDTHHMMSS), we can compare as a string + // which is a lot faster than the time casts and the time comparisons. + return (file1)->metadata.get("lastplayed") < (file2)->metadata.get("lastplayed"); + } - bool compareNumPlayers(const FileData* file1, const FileData* file2) - { - return (file1)->metadata.getInt("players") < (file2)->metadata.getInt("players"); - } + bool compareNumPlayers(const FileData* file1, const FileData* file2) + { + return (file1)->metadata.getInt("players") < (file2)->metadata.getInt("players"); + } - bool compareReleaseDate(const FileData* file1, const FileData* file2) - { - // since it's stored as an ISO string (YYYYMMDDTHHMMSS), we can compare as a string - // as it's a lot faster than the time casts and then time comparisons - return (file1)->metadata.get("releasedate") < (file2)->metadata.get("releasedate"); - } + bool compareReleaseDate(const FileData* file1, const FileData* file2) + { + // Since it's stored as an ISO string (YYYYMMDDTHHMMSS), we can compare as a string + // which is a lot faster than the time casts and the time comparisons. + return (file1)->metadata.get("releasedate") < (file2)->metadata.get("releasedate"); + } - bool compareGenre(const FileData* file1, const FileData* file2) - { - std::string genre1 = Utils::String::toUpper(file1->metadata.get("genre")); - std::string genre2 = Utils::String::toUpper(file2->metadata.get("genre")); - return genre1.compare(genre2) < 0; - } + bool compareGenre(const FileData* file1, const FileData* file2) + { + std::string genre1 = Utils::String::toUpper(file1->metadata.get("genre")); + std::string genre2 = Utils::String::toUpper(file2->metadata.get("genre")); + return genre1.compare(genre2) < 0; + } - bool compareDeveloper(const FileData* file1, const FileData* file2) - { - std::string developer1 = Utils::String::toUpper(file1->metadata.get("developer")); - std::string developer2 = Utils::String::toUpper(file2->metadata.get("developer")); - return developer1.compare(developer2) < 0; - } + bool compareDeveloper(const FileData* file1, const FileData* file2) + { + std::string developer1 = Utils::String::toUpper(file1->metadata.get("developer")); + std::string developer2 = Utils::String::toUpper(file2->metadata.get("developer")); + return developer1.compare(developer2) < 0; + } - bool comparePublisher(const FileData* file1, const FileData* file2) - { - std::string publisher1 = Utils::String::toUpper(file1->metadata.get("publisher")); - std::string publisher2 = Utils::String::toUpper(file2->metadata.get("publisher")); - return publisher1.compare(publisher2) < 0; - } + bool comparePublisher(const FileData* file1, const FileData* file2) + { + std::string publisher1 = Utils::String::toUpper(file1->metadata.get("publisher")); + std::string publisher2 = Utils::String::toUpper(file2->metadata.get("publisher")); + return publisher1.compare(publisher2) < 0; + } - bool compareSystem(const FileData* file1, const FileData* file2) - { - std::string system1 = Utils::String::toUpper(file1->getSystemName()); - std::string system2 = Utils::String::toUpper(file2->getSystemName()); - return system1.compare(system2) < 0; - } + bool compareSystem(const FileData* file1, const FileData* file2) + { + std::string system1 = Utils::String::toUpper(file1->getSystemName()); + std::string system2 = Utils::String::toUpper(file2->getSystemName()); + return system1.compare(system2) < 0; + } }; diff --git a/es-app/src/FileSorts.h b/es-app/src/FileSorts.h index d821ba0f9..aef046645 100644 --- a/es-app/src/FileSorts.h +++ b/es-app/src/FileSorts.h @@ -1,3 +1,10 @@ +// +// FileSorts.h +// +// Gamelist sorting functions. +// Actual sorting takes place in FileData. +// + #pragma once #ifndef ES_APP_FILE_SORTS_H #define ES_APP_FILE_SORTS_H @@ -7,18 +14,18 @@ namespace FileSorts { - bool compareName(const FileData* file1, const FileData* file2); - bool compareRating(const FileData* file1, const FileData* file2); - bool compareTimesPlayed(const FileData* file1, const FileData* fil2); - bool compareLastPlayed(const FileData* file1, const FileData* file2); - bool compareNumPlayers(const FileData* file1, const FileData* file2); - bool compareReleaseDate(const FileData* file1, const FileData* file2); - bool compareGenre(const FileData* file1, const FileData* file2); - bool compareDeveloper(const FileData* file1, const FileData* file2); - bool comparePublisher(const FileData* file1, const FileData* file2); - bool compareSystem(const FileData* file1, const FileData* file2); + bool compareName(const FileData* file1, const FileData* file2); + bool compareRating(const FileData* file1, const FileData* file2); + bool compareTimesPlayed(const FileData* file1, const FileData* fil2); + bool compareLastPlayed(const FileData* file1, const FileData* file2); + bool compareNumPlayers(const FileData* file1, const FileData* file2); + bool compareReleaseDate(const FileData* file1, const FileData* file2); + bool compareGenre(const FileData* file1, const FileData* file2); + bool compareDeveloper(const FileData* file1, const FileData* file2); + bool comparePublisher(const FileData* file1, const FileData* file2); + bool compareSystem(const FileData* file1, const FileData* file2); - extern const std::vector SortTypes; + extern const std::vector SortTypes; }; #endif // ES_APP_FILE_SORTS_H diff --git a/es-app/src/MetaData.cpp b/es-app/src/MetaData.cpp index 3080d0135..11eb7e217 100644 --- a/es-app/src/MetaData.cpp +++ b/es-app/src/MetaData.cpp @@ -88,7 +88,6 @@ MetaDataList MetaDataList::createFromXML(MetaDataListType type, mdl.set(iter->key, iter->defaultValue); } } - return mdl; } diff --git a/es-app/src/SystemData.h b/es-app/src/SystemData.h index ac0db06af..fc68b850c 100644 --- a/es-app/src/SystemData.h +++ b/es-app/src/SystemData.h @@ -71,8 +71,6 @@ public: // An example will be written if the file doesn't exist. static bool loadConfig(); static void writeExampleConfig(const std::string& path); - // If forWrite, will only return ~/.emulationstation/es_systems.cfg, - // never /etc/emulationstation/es_systems.cfg. static std::string getConfigPath(bool forWrite); static std::vector sSystemVector; diff --git a/es-app/src/guis/GuiCollectionSystemsOptions.cpp b/es-app/src/guis/GuiCollectionSystemsOptions.cpp index e93986843..1ec301477 100644 --- a/es-app/src/guis/GuiCollectionSystemsOptions.cpp +++ b/es-app/src/guis/GuiCollectionSystemsOptions.cpp @@ -1,3 +1,10 @@ +// +// GuiCollectionSystemsOptions.cpp +// +// User interface for the game collection settings. +// Submenu to the GuiMenu main menu. +// + #include "guis/GuiCollectionSystemsOptions.h" #include "components/OptionListComponent.h" @@ -10,228 +17,237 @@ #include "SystemData.h" #include "Window.h" -GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(Window* window) : GuiComponent(window), mMenu(window, "GAME COLLECTION SETTINGS") +GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(Window* window) + : GuiComponent(window), mMenu(window, "GAME COLLECTION SETTINGS") { - initializeMenu(); + initializeMenu(); } void GuiCollectionSystemsOptions::initializeMenu() { - addChild(&mMenu); + addChild(&mMenu); - // get collections + // Get collections. + addSystemsToMenu(); - addSystemsToMenu(); + // Add "Create New Custom Collection from Theme". + std::vector unusedFolders = + CollectionSystemManager::get()->getUnusedSystemsFromTheme(); + if (unusedFolders.size() > 0) { + addEntry("CREATE NEW CUSTOM COLLECTION FROM THEME", 0x777777FF, true, + [this, unusedFolders] { + auto s = new GuiSettings(mWindow, "SELECT THEME FOLDER"); + std::shared_ptr< OptionListComponent> + folderThemes = std::make_shared< OptionListComponent> + (mWindow, getHelpStyle(), "SELECT THEME FOLDER", true); - // add "Create New Custom Collection from Theme" + // Add custom systems. + for(auto it = unusedFolders.cbegin() ; it != unusedFolders.cend() ; it++ ) { + ComponentListRow row; + std::string name = *it; - std::vector unusedFolders = CollectionSystemManager::get()->getUnusedSystemsFromTheme(); - if (unusedFolders.size() > 0) - { - addEntry("CREATE NEW CUSTOM COLLECTION FROM THEME", 0x777777FF, true, - [this, unusedFolders] { - auto s = new GuiSettings(mWindow, "SELECT THEME FOLDER"); - std::shared_ptr< OptionListComponent> - folderThemes = std::make_shared< OptionListComponent> - (mWindow, getHelpStyle(), "SELECT THEME FOLDER", true); + std::function createCollectionCall = [name, this, s] { + createCollection(name); + }; + row.makeAcceptInputHandler(createCollectionCall); - // add Custom Systems - for(auto it = unusedFolders.cbegin() ; it != unusedFolders.cend() ; it++ ) - { - ComponentListRow row; - std::string name = *it; + auto themeFolder = std::make_shared(mWindow, + Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF); + row.addElement(themeFolder, true); + s->addRow(row); + } + mWindow->pushGui(s); + }); + } - std::function createCollectionCall = [name, this, s] { - createCollection(name); - }; - row.makeAcceptInputHandler(createCollectionCall); + ComponentListRow row; + row.addElement(std::make_shared(mWindow, + "CREATE NEW CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + auto bracket = std::make_shared(mWindow); + bracket->setImage(":/graphics/arrow.svg"); + bracket->setResize(Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight())); + row.addElement(bracket, false); + auto createCustomCollection = [this](const std::string& newVal) { + std::string name = newVal; + // We need to store the first GUI and remove it, as it'll + // be deleted by the actual GUI. + Window* window = mWindow; + GuiComponent* topGui = window->peekGui(); + window->removeGui(topGui); + createCollection(name); + }; + row.makeAcceptInputHandler([this, createCustomCollection] { + mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), + "New Collection Name", "", createCustomCollection, false, "SAVE")); + }); - auto themeFolder = std::make_shared(mWindow, Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF); - row.addElement(themeFolder, true); - s->addRow(row); - } - mWindow->pushGui(s); - }); - } + mMenu.addRow(row); - ComponentListRow row; - row.addElement(std::make_shared(mWindow, "CREATE NEW CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); - auto bracket = std::make_shared(mWindow); - bracket->setImage(":/graphics/arrow.svg"); - bracket->setResize(Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight())); - row.addElement(bracket, false); - auto createCustomCollection = [this](const std::string& newVal) { - std::string name = newVal; - // we need to store the first Gui and remove it, as it'll be deleted by the actual Gui - Window* window = mWindow; - GuiComponent* topGui = window->peekGui(); - window->removeGui(topGui); - createCollection(name); - }; - row.makeAcceptInputHandler([this, createCustomCollection] { - mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "New Collection Name", "", createCustomCollection, false, "SAVE")); - }); + sortFavFirstCustomSwitch = std::make_shared(mWindow); + sortFavFirstCustomSwitch->setState(Settings::getInstance()->getBool("FavFirstCustom")); + mMenu.addWithLabel("SORT FAVORITES ON TOP FOR CUSTOM COLLECTIONS", sortFavFirstCustomSwitch); - mMenu.addRow(row); + bundleCustomCollections = std::make_shared(mWindow); + bundleCustomCollections->setState(Settings::getInstance()-> + getBool("UseCustomCollectionsSystem")); + mMenu.addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", bundleCustomCollections); - sortFavFirstCustomSwitch = std::make_shared(mWindow); - sortFavFirstCustomSwitch->setState(Settings::getInstance()->getBool("FavFirstCustom")); - mMenu.addWithLabel("SORT FAVORITES ON TOP FOR CUSTOM COLLECTIONS", sortFavFirstCustomSwitch); + toggleSystemNameInCollections = std::make_shared(mWindow); + toggleSystemNameInCollections->setState(Settings::getInstance()-> + getBool("CollectionShowSystemInfo")); + mMenu.addWithLabel("SHOW SYSTEM NAMES IN COLLECTIONS", toggleSystemNameInCollections); - bundleCustomCollections = std::make_shared(mWindow); - bundleCustomCollections->setState(Settings::getInstance()->getBool("UseCustomCollectionsSystem")); - mMenu.addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", bundleCustomCollections); + if(CollectionSystemManager::get()->isEditing()) { + row.elements.clear(); + row.addElement(std::make_shared(mWindow, "FINISH EDITING '" + + Utils::String::toUpper(CollectionSystemManager::get()->getEditingCollection()) + + "' COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.makeAcceptInputHandler(std::bind(&GuiCollectionSystemsOptions::exitEditMode, this)); + mMenu.addRow(row); + } - toggleSystemNameInCollections = std::make_shared(mWindow); - toggleSystemNameInCollections->setState(Settings::getInstance()->getBool("CollectionShowSystemInfo")); - mMenu.addWithLabel("SHOW SYSTEM NAMES IN COLLECTIONS", toggleSystemNameInCollections); - - if(CollectionSystemManager::get()->isEditing()) - { - row.elements.clear(); - row.addElement(std::make_shared(mWindow, "FINISH EDITING '" + Utils::String::toUpper(CollectionSystemManager::get()->getEditingCollection()) + "' COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); - row.makeAcceptInputHandler(std::bind(&GuiCollectionSystemsOptions::exitEditMode, this)); - mMenu.addRow(row); - } - - mMenu.addButton("BACK", "back", std::bind(&GuiCollectionSystemsOptions::applySettings, this)); - - mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, Renderer::getScreenHeight() * 0.15f); + mMenu.addButton("BACK", "back", std::bind(&GuiCollectionSystemsOptions::applySettings, this)); + mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, + Renderer::getScreenHeight() * 0.15f); } -void GuiCollectionSystemsOptions::addEntry(const char* name, unsigned int color, bool add_arrow, const std::function& func) +void GuiCollectionSystemsOptions::addEntry(const char* name, unsigned int color, + bool add_arrow, const std::function& func) { - std::shared_ptr font = Font::get(FONT_SIZE_MEDIUM); + std::shared_ptr font = Font::get(FONT_SIZE_MEDIUM); - // populate the list - ComponentListRow row; - row.addElement(std::make_shared(mWindow, name, font, color), true); + // Populate the list. + ComponentListRow row; + row.addElement(std::make_shared(mWindow, name, font, color), true); - if(add_arrow) - { - std::shared_ptr bracket = makeArrow(mWindow); - row.addElement(bracket, false); - } + if(add_arrow) { + std::shared_ptr bracket = makeArrow(mWindow); + row.addElement(bracket, false); + } - row.makeAcceptInputHandler(func); - - mMenu.addRow(row); + row.makeAcceptInputHandler(func); + mMenu.addRow(row); } void GuiCollectionSystemsOptions::createCollection(std::string inName) { - std::string name = CollectionSystemManager::get()->getValidNewCollectionName(inName); - SystemData* newSys = CollectionSystemManager::get()->addNewCustomCollection(name); - customOptionList->add(name, name, true); - std::string outAuto = Utils::String::vectorToCommaString(autoOptionList->getSelectedObjects()); - std::string outCustom = Utils::String::vectorToCommaString(customOptionList->getSelectedObjects()); - updateSettings(outAuto, outCustom); - ViewController::get()->goToSystemView(newSys); + std::string name = CollectionSystemManager::get()->getValidNewCollectionName(inName); + SystemData* newSys = CollectionSystemManager::get()->addNewCustomCollection(name); + customOptionList->add(name, name, true); + std::string outAuto = Utils::String::vectorToCommaString( + autoOptionList->getSelectedObjects()); + std::string outCustom = Utils::String::vectorToCommaString( + customOptionList->getSelectedObjects()); + updateSettings(outAuto, outCustom); + ViewController::get()->goToSystemView(newSys); - Window* window = mWindow; - CollectionSystemManager::get()->setEditMode(name); - while(window->peekGui() && window->peekGui() != ViewController::get()) - delete window->peekGui(); - return; + Window* window = mWindow; + CollectionSystemManager::get()->setEditMode(name); + while(window->peekGui() && window->peekGui() != ViewController::get()) + delete window->peekGui(); + return; } void GuiCollectionSystemsOptions::exitEditMode() { - CollectionSystemManager::get()->exitEditMode(); - applySettings(); + CollectionSystemManager::get()->exitEditMode(); + applySettings(); } GuiCollectionSystemsOptions::~GuiCollectionSystemsOptions() { - } void GuiCollectionSystemsOptions::addSystemsToMenu() { + std::map autoSystems = + CollectionSystemManager::get()->getAutoCollectionSystems(); - std::map autoSystems = CollectionSystemManager::get()->getAutoCollectionSystems(); + autoOptionList = std::make_shared> + (mWindow, getHelpStyle(), "SELECT COLLECTIONS", true); - autoOptionList = std::make_shared< OptionListComponent >(mWindow, getHelpStyle(), "SELECT COLLECTIONS", true); + // Add automatic systems. + for(std::map::const_iterator it = autoSystems.cbegin(); + it != autoSystems.cend() ; it++ ) + autoOptionList->add(it->second.decl.longName, it->second.decl.name, it->second.isEnabled); + mMenu.addWithLabel("AUTOMATIC GAME COLLECTIONS", autoOptionList); - // add Auto Systems - for(std::map::const_iterator it = autoSystems.cbegin() ; it != autoSystems.cend() ; it++ ) - { - autoOptionList->add(it->second.decl.longName, it->second.decl.name, it->second.isEnabled); - } - mMenu.addWithLabel("AUTOMATIC GAME COLLECTIONS", autoOptionList); + std::map customSystems = + CollectionSystemManager::get()->getCustomCollectionSystems(); - std::map customSystems = CollectionSystemManager::get()->getCustomCollectionSystems(); + customOptionList = std::make_shared> + (mWindow, getHelpStyle(), "SELECT COLLECTIONS", true); - customOptionList = std::make_shared< OptionListComponent >(mWindow, getHelpStyle(), "SELECT COLLECTIONS", true); - - // add Custom Systems - for(std::map::const_iterator it = customSystems.cbegin() ; it != customSystems.cend() ; it++ ) - { - customOptionList->add(it->second.decl.longName, it->second.decl.name, it->second.isEnabled); - } - mMenu.addWithLabel("CUSTOM GAME COLLECTIONS", customOptionList); + // Add custom systems. + for(std::map::const_iterator it = customSystems.cbegin(); + it != customSystems.cend() ; it++ ) + customOptionList->add(it->second.decl.longName, it->second.decl.name, it->second.isEnabled); + mMenu.addWithLabel("CUSTOM GAME COLLECTIONS", customOptionList); } void GuiCollectionSystemsOptions::applySettings() { - std::string outAuto = Utils::String::vectorToCommaString(autoOptionList->getSelectedObjects()); - std::string prevAuto = Settings::getInstance()->getString("CollectionSystemsAuto"); - std::string outCustom = Utils::String::vectorToCommaString(customOptionList->getSelectedObjects()); - std::string prevCustom = Settings::getInstance()->getString("CollectionSystemsCustom"); - bool outSort = sortFavFirstCustomSwitch->getState(); - bool prevSort = Settings::getInstance()->getBool("FavFirstCustom"); - bool outBundle = bundleCustomCollections->getState(); - bool prevBundle = Settings::getInstance()->getBool("UseCustomCollectionsSystem"); - bool prevShow = Settings::getInstance()->getBool("CollectionShowSystemInfo"); - bool outShow = toggleSystemNameInCollections->getState(); - bool needUpdateSettings = prevAuto != outAuto || prevCustom != outCustom || outSort != prevSort || outBundle != prevBundle || prevShow != outShow ; - if (needUpdateSettings) - { - updateSettings(outAuto, outCustom); - } + std::string outAuto = Utils::String::vectorToCommaString( + autoOptionList->getSelectedObjects()); + std::string prevAuto = Settings::getInstance()->getString("CollectionSystemsAuto"); + std::string outCustom = Utils::String::vectorToCommaString( + customOptionList->getSelectedObjects()); + std::string prevCustom = Settings::getInstance()->getString("CollectionSystemsCustom"); + bool outSort = sortFavFirstCustomSwitch->getState(); + bool prevSort = Settings::getInstance()->getBool("FavFirstCustom"); + bool outBundle = bundleCustomCollections->getState(); + bool prevBundle = Settings::getInstance()->getBool("UseCustomCollectionsSystem"); + bool prevShow = Settings::getInstance()->getBool("CollectionShowSystemInfo"); + bool outShow = toggleSystemNameInCollections->getState(); + bool needUpdateSettings = prevAuto != outAuto || prevCustom != outCustom || outSort != + prevSort || outBundle != prevBundle || prevShow != outShow ; - delete this; + if (needUpdateSettings) + updateSettings(outAuto, outCustom); + + delete this; } -void GuiCollectionSystemsOptions::updateSettings(std::string newAutoSettings, std::string newCustomSettings) +void GuiCollectionSystemsOptions::updateSettings(std::string newAutoSettings, + std::string newCustomSettings) { - Settings::getInstance()->setString("CollectionSystemsAuto", newAutoSettings); - Settings::getInstance()->setString("CollectionSystemsCustom", newCustomSettings); - Settings::getInstance()->setBool("FavFirstCustom", sortFavFirstCustomSwitch->getState()); - Settings::getInstance()->setBool("UseCustomCollectionsSystem", bundleCustomCollections->getState()); - Settings::getInstance()->setBool("CollectionShowSystemInfo", toggleSystemNameInCollections->getState()); - Settings::getInstance()->saveFile(); - CollectionSystemManager::get()->loadEnabledListFromSettings(); - CollectionSystemManager::get()->updateSystemsList(); - ViewController::get()->goToStart(); - ViewController::get()->reloadAll(); + Settings::getInstance()->setString("CollectionSystemsAuto", newAutoSettings); + Settings::getInstance()->setString("CollectionSystemsCustom", newCustomSettings); + Settings::getInstance()->setBool("FavFirstCustom", sortFavFirstCustomSwitch->getState()); + Settings::getInstance()->setBool("UseCustomCollectionsSystem", + bundleCustomCollections->getState()); + Settings::getInstance()->setBool("CollectionShowSystemInfo", + toggleSystemNameInCollections->getState()); + Settings::getInstance()->saveFile(); + CollectionSystemManager::get()->loadEnabledListFromSettings(); + CollectionSystemManager::get()->updateSystemsList(); + ViewController::get()->goToStart(); + ViewController::get()->reloadAll(); } bool GuiCollectionSystemsOptions::input(InputConfig* config, Input input) { - bool consumed = GuiComponent::input(config, input); - if(consumed) - return true; + bool consumed = GuiComponent::input(config, input); - if(config->isMappedTo("b", input) && input.value != 0) - { - applySettings(); - } + if(consumed) + return true; + if(config->isMappedTo("b", input) && input.value != 0) + applySettings(); - return false; + return false; } std::vector GuiCollectionSystemsOptions::getHelpPrompts() { - std::vector prompts = mMenu.getHelpPrompts(); - prompts.push_back(HelpPrompt("a", "select")); - prompts.push_back(HelpPrompt("b", "back")); - return prompts; + std::vector prompts = mMenu.getHelpPrompts(); + prompts.push_back(HelpPrompt("a", "select")); + prompts.push_back(HelpPrompt("b", "back")); + return prompts; } HelpStyle GuiCollectionSystemsOptions::getHelpStyle() { - HelpStyle style = HelpStyle(); - style.applyTheme(ViewController::get()->getState().getSystem()->getTheme(), "system"); - return style; + HelpStyle style = HelpStyle(); + style.applyTheme(ViewController::get()->getState().getSystem()->getTheme(), "system"); + return style; } diff --git a/es-app/src/guis/GuiCollectionSystemsOptions.h b/es-app/src/guis/GuiCollectionSystemsOptions.h index c5848d51a..4ab9df6e4 100644 --- a/es-app/src/guis/GuiCollectionSystemsOptions.h +++ b/es-app/src/guis/GuiCollectionSystemsOptions.h @@ -1,3 +1,10 @@ +// +// GuiCollectionSystemsOptions.h +// +// User interface for the game collection settings. +// Submenu to the GuiMenu main menu. +// + #pragma once #ifndef ES_APP_GUIS_GUI_COLLECTION_SYSTEM_OPTIONS_H #define ES_APP_GUIS_GUI_COLLECTION_SYSTEM_OPTIONS_H @@ -12,28 +19,29 @@ class SystemData; class GuiCollectionSystemsOptions : public GuiComponent { public: - GuiCollectionSystemsOptions(Window* window); - ~GuiCollectionSystemsOptions(); - bool input(InputConfig* config, Input input) override; + GuiCollectionSystemsOptions(Window* window); + ~GuiCollectionSystemsOptions(); + bool input(InputConfig* config, Input input) override; - virtual std::vector getHelpPrompts() override; - HelpStyle getHelpStyle() override; + virtual std::vector getHelpPrompts() override; + HelpStyle getHelpStyle() override; private: - void initializeMenu(); - void applySettings(); - void addSystemsToMenu(); - void addEntry(const char* name, unsigned int color, bool add_arrow, const std::function& func); - void updateSettings(std::string newAutoSettings, std::string newCustomSettings); - void createCollection(std::string inName); - void exitEditMode(); - std::shared_ptr< OptionListComponent > autoOptionList; - std::shared_ptr< OptionListComponent > customOptionList; - std::shared_ptr bundleCustomCollections; - std::shared_ptr sortFavFirstCustomSwitch; - std::shared_ptr toggleSystemNameInCollections; - MenuComponent mMenu; - SystemData* mSystem; + void initializeMenu(); + void applySettings(); + void addSystemsToMenu(); + void addEntry(const char* name, unsigned int color, + bool add_arrow, const std::function& func); + void updateSettings(std::string newAutoSettings, std::string newCustomSettings); + void createCollection(std::string inName); + void exitEditMode(); + std::shared_ptr< OptionListComponent > autoOptionList; + std::shared_ptr< OptionListComponent > customOptionList; + std::shared_ptr bundleCustomCollections; + std::shared_ptr sortFavFirstCustomSwitch; + std::shared_ptr toggleSystemNameInCollections; + MenuComponent mMenu; + SystemData* mSystem; }; #endif // ES_APP_GUIS_GUI_COLLECTION_SYSTEM_OPTIONS_H diff --git a/es-app/src/guis/GuiGeneralScreensaverOptions.cpp b/es-app/src/guis/GuiGeneralScreensaverOptions.cpp index 8cf22639d..60af7e32d 100644 --- a/es-app/src/guis/GuiGeneralScreensaverOptions.cpp +++ b/es-app/src/guis/GuiGeneralScreensaverOptions.cpp @@ -1,3 +1,11 @@ +// +// GuiGeneralScreensaverOptions.cpp +// +// User interface for the screensaver options. +// Based on the GuiScreenSaverOptions template. +// Submenu to the GuiMenu main menu. +// + #include "guis/GuiGeneralScreensaverOptions.h" #include "components/OptionListComponent.h" @@ -8,58 +16,73 @@ #include "guis/GuiVideoScreensaverOptions.h" #include "Settings.h" -GuiGeneralScreensaverOptions::GuiGeneralScreensaverOptions(Window* window, const char* title) : GuiScreensaverOptions(window, title) +GuiGeneralScreensaverOptions::GuiGeneralScreensaverOptions(Window* window, const char* title) + : GuiScreensaverOptions(window, title) { - // screensaver time - auto screensaver_time = std::make_shared(mWindow, 0.f, 30.f, 1.f, "m"); - screensaver_time->setValue((float)(Settings::getInstance()->getInt("ScreenSaverTime") / (1000 * 60))); - addWithLabel("SCREENSAVER AFTER", screensaver_time); - addSaveFunc([screensaver_time] { - Settings::getInstance()->setInt("ScreenSaverTime", (int)Math::round(screensaver_time->getValue()) * (1000 * 60)); - PowerSaver::updateTimeouts(); - }); + // Screensaver time. + auto screensaver_time = std::make_shared(mWindow, 0.f, 30.f, 1.f, "m"); + screensaver_time->setValue((float)(Settings::getInstance()-> + getInt("ScreenSaverTime") / (1000 * 60))); + addWithLabel("SCREENSAVER AFTER", screensaver_time); + addSaveFunc([screensaver_time] { + Settings::getInstance()->setInt("ScreenSaverTime", + (int)Math::round(screensaver_time->getValue()) * (1000 * 60)); + PowerSaver::updateTimeouts(); + }); - // Allow ScreenSaver Controls - ScreenSaverControls - auto ss_controls = std::make_shared(mWindow); - ss_controls->setState(Settings::getInstance()->getBool("ScreenSaverControls")); - addWithLabel("SCREENSAVER CONTROLS", ss_controls); - addSaveFunc([ss_controls] { Settings::getInstance()->setBool("ScreenSaverControls", ss_controls->getState()); }); + // Allow ScreenSaver Controls - ScreenSaverControls. + auto ss_controls = std::make_shared(mWindow); + ss_controls->setState(Settings::getInstance()->getBool("ScreenSaverControls")); + addWithLabel("SCREENSAVER CONTROLS", ss_controls); + addSaveFunc([ss_controls] { Settings::getInstance()->setBool("ScreenSaverControls", + ss_controls->getState()); }); - // screensaver behavior - auto screensaver_behavior = std::make_shared< OptionListComponent >(mWindow, getHelpStyle(), "SCREENSAVER BEHAVIOR", false); - std::vector screensavers; - screensavers.push_back("dim"); - screensavers.push_back("black"); - screensavers.push_back("random video"); - screensavers.push_back("slideshow"); - for(auto it = screensavers.cbegin(); it != screensavers.cend(); it++) - screensaver_behavior->add(*it, *it, Settings::getInstance()->getString("ScreenSaverBehavior") == *it); - addWithLabel("SCREENSAVER BEHAVIOR", screensaver_behavior); - addSaveFunc([this, screensaver_behavior] { - if (Settings::getInstance()->getString("ScreenSaverBehavior") != "random video" && screensaver_behavior->getSelected() == "random video") { - // if before it wasn't risky but now there's a risk of problems, show warning - mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), - "The \"Random Video\" screensaver shows videos from your gamelist.\n\nIf you do not have videos, or if in several consecutive attempts the games it selects don't have videos it will default to black.\n\nMore options in the \"UI Settings\" > \"Video Screensaver\" menu.", - "OK", [] { return; })); - } - Settings::getInstance()->setString("ScreenSaverBehavior", screensaver_behavior->getSelected()); - PowerSaver::updateTimeouts(); - }); + // Screensaver behavior. + auto screensaver_behavior = std::make_shared> + (mWindow, getHelpStyle(), "SCREENSAVER BEHAVIOR", false); + std::vector screensavers; + screensavers.push_back("dim"); + screensavers.push_back("black"); + screensavers.push_back("random video"); + screensavers.push_back("slideshow"); + for(auto it = screensavers.cbegin(); it != screensavers.cend(); it++) + screensaver_behavior->add(*it, *it, Settings::getInstance()-> + getString("ScreenSaverBehavior") == *it); + addWithLabel("SCREENSAVER BEHAVIOR", screensaver_behavior); + addSaveFunc([this, screensaver_behavior] { + if (Settings::getInstance()->getString("ScreenSaverBehavior") != + "random video" && screensaver_behavior->getSelected() == "random video") { + // If before it wasn't risky but now there's a risk of problems, show warning. + mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(), + "THE \"RANDOM VIDEO\" SCREENSAVER SHOWS\nVIDEOS FROM YOUR GAMELISTS.\n\nIF YOU DO NOT " + "HAVE ANY VIDEOS, THE SCREENSAVER\nWILL DEFAULT TO \"BLACK\".\n\nSEE MORE " + "OPTIONS IN THE MENU \"UI SETTINGS\" >\n\"SCREENSAVER SETTINGS\" > " + "\"VIDEO SCREENSAVER SETTINGS\".", + "OK", [] { return; })); + } + Settings::getInstance()->setString("ScreenSaverBehavior", + screensaver_behavior->getSelected()); + PowerSaver::updateTimeouts(); + }); - ComponentListRow row; + ComponentListRow row; - // show filtered menu - row.elements.clear(); - row.addElement(std::make_shared(mWindow, "VIDEO SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); - row.addElement(makeArrow(mWindow), false); - row.makeAcceptInputHandler(std::bind(&GuiGeneralScreensaverOptions::openVideoScreensaverOptions, this)); - addRow(row); + // Show filtered menu. + row.elements.clear(); + row.addElement(std::make_shared(mWindow, + "VIDEO SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(makeArrow(mWindow), false); + row.makeAcceptInputHandler(std::bind( + &GuiGeneralScreensaverOptions::openVideoScreensaverOptions, this)); + addRow(row); - row.elements.clear(); - row.addElement(std::make_shared(mWindow, "SLIDESHOW SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); - row.addElement(makeArrow(mWindow), false); - row.makeAcceptInputHandler(std::bind(&GuiGeneralScreensaverOptions::openSlideshowScreensaverOptions, this)); - addRow(row); + row.elements.clear(); + row.addElement(std::make_shared(mWindow, + "SLIDESHOW SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); + row.addElement(makeArrow(mWindow), false); + row.makeAcceptInputHandler(std::bind( + &GuiGeneralScreensaverOptions::openSlideshowScreensaverOptions, this)); + addRow(row); } GuiGeneralScreensaverOptions::~GuiGeneralScreensaverOptions() @@ -67,10 +90,9 @@ GuiGeneralScreensaverOptions::~GuiGeneralScreensaverOptions() } void GuiGeneralScreensaverOptions::openVideoScreensaverOptions() { - mWindow->pushGui(new GuiVideoScreensaverOptions(mWindow, "VIDEO SCREENSAVER")); + mWindow->pushGui(new GuiVideoScreensaverOptions(mWindow, "VIDEO SCREENSAVER")); } void GuiGeneralScreensaverOptions::openSlideshowScreensaverOptions() { mWindow->pushGui(new GuiSlideshowScreensaverOptions(mWindow, "SLIDESHOW SCREENSAVER")); } - diff --git a/es-app/src/guis/GuiGeneralScreensaverOptions.h b/es-app/src/guis/GuiGeneralScreensaverOptions.h index 1992d9ad1..8d5192119 100644 --- a/es-app/src/guis/GuiGeneralScreensaverOptions.h +++ b/es-app/src/guis/GuiGeneralScreensaverOptions.h @@ -1,3 +1,11 @@ +// +// GuiGeneralScreensaverOptions.h +// +// User interface for the screensaver options. +// Based on the GuiScreenSaverOptions template. +// Submenu to the GuiMenu main menu. +// + #pragma once #ifndef ES_APP_GUIS_GUI_GENERAL_SCREENSAVER_OPTIONS_H #define ES_APP_GUIS_GUI_GENERAL_SCREENSAVER_OPTIONS_H @@ -7,12 +15,12 @@ class GuiGeneralScreensaverOptions : public GuiScreensaverOptions { public: - GuiGeneralScreensaverOptions(Window* window, const char* title); - virtual ~GuiGeneralScreensaverOptions(); + GuiGeneralScreensaverOptions(Window* window, const char* title); + virtual ~GuiGeneralScreensaverOptions(); private: - void openVideoScreensaverOptions(); - void openSlideshowScreensaverOptions(); + void openVideoScreensaverOptions(); + void openSlideshowScreensaverOptions(); }; #endif // ES_APP_GUIS_GUI_GENERAL_SCREENSAVER_OPTIONS_H diff --git a/es-app/src/guis/GuiInfoPopup.cpp b/es-app/src/guis/GuiInfoPopup.cpp index b0efc4477..e26f57517 100644 --- a/es-app/src/guis/GuiInfoPopup.cpp +++ b/es-app/src/guis/GuiInfoPopup.cpp @@ -1,3 +1,9 @@ +// +// GuiInfoPopup.cpp +// +// Popup window used for user notifications. +// + #include "guis/GuiInfoPopup.h" #include "components/ComponentGrid.h" @@ -5,112 +11,108 @@ #include "components/TextComponent.h" #include -GuiInfoPopup::GuiInfoPopup(Window* window, std::string message, int duration) : - GuiComponent(window), mMessage(message), mDuration(duration), running(true) +GuiInfoPopup::GuiInfoPopup( + Window* window, + std::string message, + int duration) + : GuiComponent(window), + mMessage(message), + mDuration(duration), + running(true) { - mFrame = new NinePatchComponent(window); - float maxWidth = Renderer::getScreenWidth() * 0.9f; - float maxHeight = Renderer::getScreenHeight() * 0.2f; + mFrame = new NinePatchComponent(window); + float maxWidth = Renderer::getScreenWidth() * 0.9f; + float maxHeight = Renderer::getScreenHeight() * 0.2f; - std::shared_ptr s = std::make_shared(mWindow, - "", - Font::get(FONT_SIZE_MINI), - 0x444444FF, - ALIGN_CENTER); + std::shared_ptr s = std::make_shared(mWindow, "", + Font::get(FONT_SIZE_MINI), 0x444444FF, ALIGN_CENTER); - // we do this to force the text container to resize and return an actual expected popup size - s->setSize(0,0); - s->setText(message); - mSize = s->getSize(); + // We do this to force the text container to resize and return the actual expected popup size. + s->setSize(0,0); + s->setText(message); + mSize = s->getSize(); - // confirm the size isn't larger than the screen width, otherwise cap it - if (mSize.x() > maxWidth) { - s->setSize(maxWidth, mSize[1]); - mSize[0] = maxWidth; - } - if (mSize.y() > maxHeight) { - s->setSize(mSize[0], maxHeight); - mSize[1] = maxHeight; - } + // Confirm that the size isn't larger than the screen width, otherwise cap it. + if (mSize.x() > maxWidth) { + s->setSize(maxWidth, mSize[1]); + mSize[0] = maxWidth; + } + if (mSize.y() > maxHeight) { + s->setSize(mSize[0], maxHeight); + mSize[1] = maxHeight; + } - // add a padding to the box - int paddingX = (int) (Renderer::getScreenWidth() * 0.03f); - int paddingY = (int) (Renderer::getScreenHeight() * 0.02f); - mSize[0] = mSize.x() + paddingX; - mSize[1] = mSize.y() + paddingY; + // Add a padding to the box. + int paddingX = (int) (Renderer::getScreenWidth() * 0.03f); + int paddingY = (int) (Renderer::getScreenHeight() * 0.02f); + mSize[0] = mSize.x() + paddingX; + mSize[1] = mSize.y() + paddingY; - float posX = Renderer::getScreenWidth()*0.5f - mSize.x()*0.5f; - float posY = Renderer::getScreenHeight() * 0.02f; + float posX = Renderer::getScreenWidth()*0.5f - mSize.x()*0.5f; + float posY = Renderer::getScreenHeight() * 0.02f; - setPosition(posX, posY, 0); + setPosition(posX, posY, 0); - mFrame->setImagePath(":/graphics/frame.png"); - mFrame->fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); - addChild(mFrame); + mFrame->setImagePath(":/graphics/frame.png"); + mFrame->fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); + addChild(mFrame); - // we only init the actual time when we first start to render - mStartTime = 0; + // We only initialize the actual time when we first start to render. + mStartTime = 0; - mGrid = new ComponentGrid(window, Vector2i(1, 3)); - mGrid->setSize(mSize); - mGrid->setEntry(s, Vector2i(0, 1), false, true); - addChild(mGrid); + mGrid = new ComponentGrid(window, Vector2i(1, 3)); + mGrid->setSize(mSize); + mGrid->setEntry(s, Vector2i(0, 1), false, true); + addChild(mGrid); } GuiInfoPopup::~GuiInfoPopup() { - } void GuiInfoPopup::render(const Transform4x4f& /*parentTrans*/) { - // we use identity as we want to render on a specific window position, not on the view - Transform4x4f trans = getTransform() * Transform4x4f::Identity(); - if(running && updateState()) - { - // if we're still supposed to be rendering it - Renderer::setMatrix(trans); - renderChildren(trans); - } + // We use Identity() as we want to render on a specific window position, not on the view. + Transform4x4f trans = getTransform() * Transform4x4f::Identity(); + if (running && updateState()) { + // If we're still supposed to be rendering it. + Renderer::setMatrix(trans); + renderChildren(trans); + } } bool GuiInfoPopup::updateState() { - int curTime = SDL_GetTicks(); + int curTime = SDL_GetTicks(); - // we only init the actual time when we first start to render - if(mStartTime == 0) - { - mStartTime = curTime; - } + // We only initialize the actual time when we first start to render. + if (mStartTime == 0) + mStartTime = curTime; - // compute fade in effect - if (curTime - mStartTime > mDuration) - { - // we're past the popup duration, no need to render - running = false; - return false; - } - else if (curTime < mStartTime) { - // if SDL reset - running = false; - return false; - } - else if (curTime - mStartTime <= 500) { - alpha = ((curTime - mStartTime)*255/500); - } - else if (curTime - mStartTime < mDuration - 500) - { - alpha = 255; - } - else - { - alpha = ((-(curTime - mStartTime - mDuration)*255)/500); - } - mGrid->setOpacity((unsigned char)alpha); + // Compute fade-in effect. + if (curTime - mStartTime > mDuration) { + // We're past the popup duration, no need to render. + running = false; + return false; + } + else if (curTime < mStartTime) { + // If SDL reset. + running = false; + return false; + } + else if (curTime - mStartTime <= 500) { + alpha = ((curTime - mStartTime)*255/500); + } + else if (curTime - mStartTime < mDuration - 500) { + alpha = 255; + } + else { + alpha = ((-(curTime - mStartTime - mDuration)*255)/500); + } + mGrid->setOpacity((unsigned char)alpha); - // apply fade in effect to popup frame - mFrame->setEdgeColor(0xFFFFFF00 | (unsigned char)(alpha)); - mFrame->setCenterColor(0xFFFFFF00 | (unsigned char)(alpha)); - return true; -} \ No newline at end of file + // Apply fade-in effect to popup frame. + mFrame->setEdgeColor(0xFFFFFF00 | (unsigned char)(alpha)); + mFrame->setCenterColor(0xFFFFFF00 | (unsigned char)(alpha)); + return true; +} diff --git a/es-app/src/guis/GuiInfoPopup.h b/es-app/src/guis/GuiInfoPopup.h index 8d4d3cfd1..fbf058ae8 100644 --- a/es-app/src/guis/GuiInfoPopup.h +++ b/es-app/src/guis/GuiInfoPopup.h @@ -1,3 +1,9 @@ +// +// GuiInfoPopup.h +// +// Popup window used for user notifications. +// + #pragma once #ifndef ES_APP_GUIS_GUI_INFO_POPUP_H #define ES_APP_GUIS_GUI_INFO_POPUP_H @@ -11,19 +17,20 @@ class NinePatchComponent; class GuiInfoPopup : public GuiComponent, public Window::InfoPopup { public: - GuiInfoPopup(Window* window, std::string message, int duration); - ~GuiInfoPopup(); - void render(const Transform4x4f& parentTrans) override; - inline void stop() { running = false; }; + GuiInfoPopup(Window* window, std::string message, int duration); + ~GuiInfoPopup(); + void render(const Transform4x4f& parentTrans) override; + inline void stop() { running = false; }; + private: - std::string mMessage; - int mDuration; - int alpha; - bool updateState(); - int mStartTime; - ComponentGrid* mGrid; - NinePatchComponent* mFrame; - bool running; + std::string mMessage; + int mDuration; + int alpha; + bool updateState(); + int mStartTime; + ComponentGrid* mGrid; + NinePatchComponent* mFrame; + bool running; }; #endif // ES_APP_GUIS_GUI_INFO_POPUP_H diff --git a/es-app/src/guis/GuiMenu.cpp b/es-app/src/guis/GuiMenu.cpp index 3640263e6..065316e7d 100644 --- a/es-app/src/guis/GuiMenu.cpp +++ b/es-app/src/guis/GuiMenu.cpp @@ -299,12 +299,12 @@ void GuiMenu::openUISettings() s->addSaveFunc([ UImodeSelection, window, this] { std::string selectedMode = UImodeSelection->getSelected(); if (selectedMode != "Full") { - std::string msg = "You are changing the UI to a restricted mode:\n" + - selectedMode + "\n"; - msg += "This will hide most menu-options to prevent changes to the system.\n"; - msg += "To unlock and return to the full UI, enter this code: \n"; + std::string msg = "YOU ARE CHANGING THE UI TO A RESTRICTED MODE:\n\"" + + Utils::String::toUpper(selectedMode) + "\"\n"; + msg += "THIS WILL HIDE MOST MENU OPTIONS TO PREVENT CHANGES TO THE SYSTEM.\n"; + msg += "TO UNLOCK AND RETURN TO THE FULL UI, ENTER THIS CODE: \n"; msg += "\"" + UIModeController::getInstance()->getFormattedPassKeyStr() + "\"\n\n"; - msg += "Do you want to proceed?"; + msg += "DO YOU WANT TO PROCEED?"; window->pushGui(new GuiMsgBox(window, this->getHelpStyle(), msg, "YES", [selectedMode] { LOG(LogDebug) << "Setting UI mode to " << selectedMode; diff --git a/es-app/src/guis/GuiScreensaverOptions.cpp b/es-app/src/guis/GuiScreensaverOptions.cpp index 1f6b1228e..56f52928c 100644 --- a/es-app/src/guis/GuiScreensaverOptions.cpp +++ b/es-app/src/guis/GuiScreensaverOptions.cpp @@ -1,3 +1,9 @@ +// +// GuiScreensaverOptions.cpp +// +// User interface template for the screensaver option GUIs. +// + #include "guis/GuiScreensaverOptions.h" #include "guis/GuiTextEditPopup.h" @@ -6,83 +12,87 @@ #include "SystemData.h" #include "Window.h" -GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const char* title) : GuiComponent(window), mMenu(window, title) +GuiScreensaverOptions::GuiScreensaverOptions(Window* window, const char* title) + : GuiComponent(window), mMenu(window, title) { - addChild(&mMenu); + addChild(&mMenu); + mMenu.addButton("BACK", "back", [this] { delete this; }); - mMenu.addButton("BACK", "back", [this] { delete this; }); - - setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); - mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, Renderer::getScreenHeight() * 0.15f); + setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); + mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, Renderer::getScreenHeight() * 0.15f); } GuiScreensaverOptions::~GuiScreensaverOptions() { - save(); + save(); } void GuiScreensaverOptions::save() { - if(!mSaveFuncs.size()) - return; + if(!mSaveFuncs.size()) + return; - for(auto it = mSaveFuncs.cbegin(); it != mSaveFuncs.cend(); it++) - (*it)(); + for(auto it = mSaveFuncs.cbegin(); it != mSaveFuncs.cend(); it++) + (*it)(); - Settings::getInstance()->saveFile(); + Settings::getInstance()->saveFile(); } bool GuiScreensaverOptions::input(InputConfig* config, Input input) { - if (GuiComponent::input(config, input)) - return true; + if (GuiComponent::input(config, input)) + return true; - if (config->isMappedTo("b", input) && - input.value != 0) { - delete this; - return true; - } + if (config->isMappedTo("b", input) && + input.value != 0) { + delete this; + return true; + } - return false; + return false; } HelpStyle GuiScreensaverOptions::getHelpStyle() { - HelpStyle style = HelpStyle(); - style.applyTheme(ViewController::get()->getState().getSystem()->getTheme(), "system"); - return style; + HelpStyle style = HelpStyle(); + style.applyTheme(ViewController::get()->getState().getSystem()->getTheme(), "system"); + return style; } std::vector GuiScreensaverOptions::getHelpPrompts() { - std::vector prompts = mMenu.getHelpPrompts(); - prompts.push_back(HelpPrompt("b", "back")); - return prompts; + std::vector prompts = mMenu.getHelpPrompts(); + prompts.push_back(HelpPrompt("b", "back")); + return prompts; } -void GuiScreensaverOptions::addEditableTextComponent(ComponentListRow row, const std::string label, std::shared_ptr ed, std::string value) +void GuiScreensaverOptions::addEditableTextComponent(ComponentListRow row, + const std::string label, std::shared_ptr ed, std::string value) { - row.elements.clear(); + row.elements.clear(); - auto lbl = std::make_shared(mWindow, Utils::String::toUpper(label), Font::get(FONT_SIZE_MEDIUM), 0x777777FF); - row.addElement(lbl, true); // label + auto lbl = std::make_shared(mWindow, Utils::String::toUpper(label), + Font::get(FONT_SIZE_MEDIUM), 0x777777FF); + row.addElement(lbl, true); // Label. - row.addElement(ed, true); + row.addElement(ed, true); - auto spacer = std::make_shared(mWindow); - spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0); - row.addElement(spacer, false); + auto spacer = std::make_shared(mWindow); + spacer->setSize(Renderer::getScreenWidth() * 0.005f, 0); + row.addElement(spacer, false); - auto bracket = std::make_shared(mWindow); - bracket->setImage(":/graphics/arrow.svg"); - bracket->setResize(Vector2f(0, lbl->getFont()->getLetterHeight())); - row.addElement(bracket, false); + auto bracket = std::make_shared(mWindow); + bracket->setImage(":/graphics/arrow.svg"); + bracket->setResize(Vector2f(0, lbl->getFont()->getLetterHeight())); + row.addElement(bracket, false); - auto updateVal = [ed](const std::string& newVal) { ed->setValue(newVal); }; // ok callback (apply new value to ed) - row.makeAcceptInputHandler([this, label, ed, updateVal] { - mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label, ed->getValue(), updateVal, false)); - }); - assert(ed); - addRow(row); - ed->setValue(value); + // OK callback (apply new value to ed). + auto updateVal = [ed](const std::string& newVal) { ed->setValue(newVal); }; + row.makeAcceptInputHandler([this, label, ed, updateVal] { + mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label, + ed->getValue(), updateVal, false)); + }); + assert(ed); + addRow(row); + ed->setValue(value); } diff --git a/es-app/src/guis/GuiScreensaverOptions.h b/es-app/src/guis/GuiScreensaverOptions.h index cabcff3c5..a3815b864 100644 --- a/es-app/src/guis/GuiScreensaverOptions.h +++ b/es-app/src/guis/GuiScreensaverOptions.h @@ -1,3 +1,9 @@ +// +// GuiScreensaverOptions.h +// +// User interface template for the screensaver option GUIs. +// + #pragma once #ifndef ES_APP_GUIS_GUI_SCREENSAVER_OPTIONS_H #define ES_APP_GUIS_GUI_SCREENSAVER_OPTIONS_H @@ -8,22 +14,24 @@ class GuiScreensaverOptions : public GuiComponent { public: - GuiScreensaverOptions(Window* window, const char* title); - virtual ~GuiScreensaverOptions(); // just calls save(); + GuiScreensaverOptions(Window* window, const char* title); + virtual ~GuiScreensaverOptions(); // Just calls save() - virtual void save(); - inline void addRow(const ComponentListRow& row) { mMenu.addRow(row); }; - inline void addWithLabel(const std::string& label, const std::shared_ptr& comp) { mMenu.addWithLabel(label, comp); }; - inline void addSaveFunc(const std::function& func) { mSaveFuncs.push_back(func); }; - void addEditableTextComponent(ComponentListRow row, const std::string label, std::shared_ptr ed, std::string value); + virtual void save(); + inline void addRow(const ComponentListRow& row) { mMenu.addRow(row); }; + inline void addWithLabel(const std::string& label, + const std::shared_ptr& comp) { mMenu.addWithLabel(label, comp); }; + inline void addSaveFunc(const std::function& func) { mSaveFuncs.push_back(func); }; + void addEditableTextComponent(ComponentListRow row, + const std::string label, std::shared_ptr ed, std::string value); - bool input(InputConfig* config, Input input) override; - std::vector getHelpPrompts() override; - HelpStyle getHelpStyle() override; + bool input(InputConfig* config, Input input) override; + std::vector getHelpPrompts() override; + HelpStyle getHelpStyle() override; protected: - MenuComponent mMenu; - std::vector< std::function > mSaveFuncs; + MenuComponent mMenu; + std::vector< std::function > mSaveFuncs; }; #endif // ES_APP_GUIS_GUI_SCREENSAVER_OPTIONS_H diff --git a/es-core/src/Scripting.cpp b/es-core/src/Scripting.cpp index 01f04ecb7..d230dde57 100644 --- a/es-core/src/Scripting.cpp +++ b/es-core/src/Scripting.cpp @@ -1,3 +1,15 @@ +// +// Scripting.cpp +// +// Executes custom scripts for various events in EmulationStation. +// By calling fireEvent() the scripts inside the directory corresponding to the +// argument 'eventName' will be executed with arg1 and arg2 as the script arguments. +// +// The scripts are searched for in $HOME/.emulationstation/scripts/. +// For example, if the event is called 'game-start', all scripts inside the directory +// $HOME/.emulationstation/scripts/game-start/ will be executed. +// + #include "Scripting.h" #include "Log.h" #include "Platform.h" @@ -12,25 +24,21 @@ namespace Scripting std::list scriptDirList; std::string test; - // check in exepath - test = Utils::FileSystem::getExePath() + "/scripts/" + eventName; - if(Utils::FileSystem::exists(test)) - scriptDirList.push_back(test); - - // check in homepath + // Check in homepath. test = Utils::FileSystem::getHomePath() + "/.emulationstation/scripts/" + eventName; if(Utils::FileSystem::exists(test)) scriptDirList.push_back(test); - for(std::list::const_iterator dirIt = scriptDirList.cbegin(); dirIt != scriptDirList.cend(); ++dirIt) { + for(std::list::const_iterator dirIt = scriptDirList.cbegin(); + dirIt != scriptDirList.cend(); ++dirIt) { std::list scripts = Utils::FileSystem::getDirContent(*dirIt); - for (std::list::const_iterator it = scripts.cbegin(); it != scripts.cend(); ++it) { - // append folder to path + for (std::list::const_iterator it = scripts.cbegin(); + it != scripts.cend(); ++it) { + // Append folder to path. std::string script = *it + " \"" + arg1 + "\" \"" + arg2 + "\""; LOG(LogDebug) << " executing: " << script; runSystemCommand(script); } } } - -} // Scripting:: +} diff --git a/es-core/src/Scripting.h b/es-core/src/Scripting.h index 3089a538c..3582f5081 100644 --- a/es-core/src/Scripting.h +++ b/es-core/src/Scripting.h @@ -1,3 +1,15 @@ +// +// Scripting.h +// +// Executes custom scripts for various events in EmulationStation. +// By calling fireEvent() the scripts inside the directory corresponding to the +// argument 'eventName' will be executed with arg1 and arg2 as the script arguments. +// +// The scripts are searched for in $HOME/.emulationstation/scripts/. +// For example, if the event is called 'game-start', all scripts inside the directory +// $HOME/.emulationstation/scripts/game-start/ will be executed. +// + #pragma once #ifndef ES_CORE_SCRIPTING_H #define ES_CORE_SCRIPTING_H @@ -6,7 +18,8 @@ namespace Scripting { - void fireEvent(const std::string& eventName, const std::string& arg1="", const std::string& arg2=""); -} // Scripting:: + void fireEvent(const std::string& eventName, + const std::string& arg1="", const std::string& arg2=""); +} #endif //ES_CORE_SCRIPTING_H diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index 2278ee329..e957cbc18 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -1,7 +1,9 @@ // // ThemeData.cpp // -// Theme handling. +// Finds available themes on the file system and loads these, +// including the parsing of individual theme components +// (includes, features, variables, views, elements). // #include "ThemeData.h" @@ -417,7 +419,6 @@ void ThemeData::parseView(const pugi::xml_node& root, ThemeView& view) } } - void ThemeData::parseElement(const pugi::xml_node& root, const std::map& typeMap, ThemeElement& element) { diff --git a/es-core/src/ThemeData.h b/es-core/src/ThemeData.h index 85b53fa6d..2fde3d553 100644 --- a/es-core/src/ThemeData.h +++ b/es-core/src/ThemeData.h @@ -1,7 +1,9 @@ // // ThemeData.h // -// Theme handling. +// Finds available themes on the file system and loads these, +// including the parsing of individual theme components +// (includes, features, variables, views, elements). // #pragma once @@ -148,7 +150,6 @@ private: }; public: - ThemeData(); // Throws ThemeException. diff --git a/es-core/src/utils/FileSystemUtil.cpp b/es-core/src/utils/FileSystemUtil.cpp index 821d5fb4a..6ebb0d0b3 100644 --- a/es-core/src/utils/FileSystemUtil.cpp +++ b/es-core/src/utils/FileSystemUtil.cpp @@ -1,3 +1,9 @@ +// +// FileSystemUtil.cpp +// +// Low-level filesystem functions. +// + #define _FILE_OFFSET_BITS 64 #include "utils/FileSystemUtil.h" @@ -5,8 +11,8 @@ #include #include -#if defined(_WIN32) -// because windows... +#ifdef WIN32 +// Because windows... #include #include #define getcwd _getcwd @@ -16,681 +22,626 @@ #define unlink _unlink #define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) #define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR) -#else // _WIN32 +#else #include #include -#endif // _WIN32 +#endif -// Try to ascertain the install prefix as defined when CMake was run. +// Try to get the install prefix as defined when CMake was run. // The installPrefix directory is the value set for CMAKE_INSTALL_DIRECTORY during build. // The datarootdir directory is the value set for CMAKE_INSTALL_DATAROOTDIR during build. // If not defined, the default prefix path '/usr/local' and the default datarootdir // directory 'share' will be used, i.e. combining to '/usr/local/share'. +#ifdef __unix__ #ifdef ES_INSTALL_PREFIX std::string installPrefix = ES_INSTALL_PREFIX; #else std::string installPrefix = "/usr/local"; #endif - #ifdef ES_DATAROOTDIR std::string dataRootDir = ES_DATAROOTDIR; #else std::string dataRootDir = "share"; #endif +#endif namespace Utils { - namespace FileSystem - { - static std::string homePath = ""; - static std::string exePath = ""; - -#if defined(_WIN32) - static std::string convertFromWideString(const std::wstring wstring) - { - int numBytes = WideCharToMultiByte(CP_UTF8, 0, wstring.c_str(), (int)wstring.length(), nullptr, 0, nullptr, nullptr); - std::string string; - - string.resize(numBytes); - WideCharToMultiByte(CP_UTF8, 0, wstring.c_str(), (int)wstring.length(), (char*)string.c_str(), numBytes, nullptr, nullptr); - - return std::string(string); - - } // convertFromWideString -#endif // _WIN32 - - stringList getDirContent(const std::string& _path, const bool _recursive) - { - std::string path = getGenericPath(_path); - stringList contentList; - - // only parse the directory, if it's a directory - if(isDirectory(path)) - { - -#if defined(_WIN32) - WIN32_FIND_DATAW findData; - std::string wildcard = path + "/*"; - HANDLE hFind = FindFirstFileW(std::wstring(wildcard.begin(), wildcard.end()).c_str(), &findData); - - if(hFind != INVALID_HANDLE_VALUE) - { - // loop over all files in the directory - do - { - std::string name = convertFromWideString(findData.cFileName); - - // ignore "." and ".." - if((name != ".") && (name != "..")) - { - std::string fullName(getGenericPath(path + "/" + name)); - contentList.push_back(fullName); - - if(_recursive && isDirectory(fullName)) - contentList.merge(getDirContent(fullName, true)); - } - } - while(FindNextFileW(hFind, &findData)); - - FindClose(hFind); - } -#else // _WIN32 - DIR* dir = opendir(path.c_str()); - - if(dir != NULL) - { - struct dirent* entry; - - // loop over all files in the directory - while((entry = readdir(dir)) != NULL) - { - std::string name(entry->d_name); - - // ignore "." and ".." - if((name != ".") && (name != "..")) - { - std::string fullName(getGenericPath(path + "/" + name)); - contentList.push_back(fullName); - - if(_recursive && isDirectory(fullName)) - contentList.merge(getDirContent(fullName, true)); - } - } - - closedir(dir); - } -#endif // _WIN32 - - } - - // sort the content list - contentList.sort(); - - // return the content list - return contentList; - - } // getDirContent - - stringList getPathList(const std::string& _path) - { - stringList pathList; - std::string path = getGenericPath(_path); - size_t start = 0; - size_t end = 0; - - // split at '/' - while((end = path.find("/", start)) != std::string::npos) - { - if(end != start) - pathList.push_back(std::string(path, start, end - start)); - - start = end + 1; - } - - // add last folder / file to pathList - if(start != path.size()) - pathList.push_back(std::string(path, start, path.size() - start)); - - // return the path list - return pathList; - - } // getPathList - - void setHomePath(const std::string& _path) - { - homePath = getGenericPath(_path); - - } // setHomePath - - std::string getHomePath() - { - // only construct the homepath once - if(homePath.length()) - return homePath; - - // check if "getExePath()/.emulationstation/es_systems.cfg" exists - if(Utils::FileSystem::exists(getExePath() + "/.emulationstation/es_systems.cfg")) - homePath = getExePath(); - - // check for HOME environment variable - if(!homePath.length()) - { - char* envHome = getenv("HOME"); - if(envHome) - homePath = getGenericPath(envHome); - } - -#if defined(_WIN32) - // on Windows we need to check HOMEDRIVE and HOMEPATH - if(!homePath.length()) - { - char* envHomeDrive = getenv("HOMEDRIVE"); - char* envHomePath = getenv("HOMEPATH"); - if(envHomeDrive && envHomePath) - homePath = getGenericPath(std::string(envHomeDrive) + "/" + envHomePath); - } -#endif // _WIN32 - - // no homepath found, fall back to current working directory - if(!homePath.length()) - homePath = getCWDPath(); - - // return constructed homepath - return homePath; - - } // getHomePath - - std::string getCWDPath() - { - char temp[512]; - - // return current working directory path - return (getcwd(temp, 512) ? getGenericPath(temp) : ""); - - } // getCWDPath - - void setExePath(const std::string& _path) - { - constexpr int path_max = 32767; -#if defined(_WIN32) - std::wstring result(path_max, 0); - if(GetModuleFileNameW(nullptr, &result[0], path_max) != 0) - exePath = convertFromWideString(result); -#else - std::string result(path_max, 0); - if(readlink("/proc/self/exe", &result[0], path_max) != -1) - exePath = result; -#endif - exePath = getCanonicalPath(exePath); - - // Fallback to argv[0] if everything else fails - if (exePath.empty()) - exePath = getCanonicalPath(_path); - if(isRegularFile(exePath)) - exePath = getParent(exePath); - - } // setExePath - - std::string getExePath() - { - // return constructed exepath - return exePath; - - } // getExePath - - std::string getInstallPrefixPath() - { - return installPrefix + "/" + dataRootDir; - } - - std::string getPreferredPath(const std::string& _path) - { - std::string path = _path; - size_t offset = std::string::npos; -#if defined(_WIN32) - // convert '/' to '\\' - while((offset = path.find('/')) != std::string::npos) - path.replace(offset, 1, "\\"); -#endif // _WIN32 - return path; - } - - std::string getGenericPath(const std::string& _path) - { - std::string path = _path; - size_t offset = std::string::npos; - - // remove "\\\\?\\" - if((path.find("\\\\?\\")) == 0) - path.erase(0, 4); - - // convert '\\' to '/' - while((offset = path.find('\\')) != std::string::npos) - path.replace(offset, 1 ,"/"); - - // remove double '/' - while((offset = path.find("//")) != std::string::npos) - path.erase(offset, 1); - - // remove trailing '/' when the path is more than a simple '/' - while(path.length() > 1 && ((offset = path.find_last_of('/')) == (path.length() - 1))) - path.erase(offset, 1); - - // return generic path - return path; - - } // getGenericPath - - std::string getEscapedPath(const std::string& _path) - { - std::string path = getGenericPath(_path); - -#if defined(_WIN32) - // windows escapes stuff by just putting everything in quotes - return '"' + getPreferredPath(path) + '"'; -#else // _WIN32 - // insert a backslash before most characters that would mess up a bash path - const char* invalidChars = "\\ '\"!$^&*(){}[]?;<>"; - const char* invalidChar = invalidChars; - - while(*invalidChar) - { - size_t start = 0; - size_t offset = 0; - - while((offset = path.find(*invalidChar, start)) != std::string::npos) - { - start = offset + 1; - - if((offset == 0) || (path[offset - 1] != '\\')) - { - path.insert(offset, 1, '\\'); - ++start; - } - } - - ++invalidChar; - } - - // return escaped path - return path; -#endif // _WIN32 - - } // getEscapedPath - - std::string getCanonicalPath(const std::string& _path) - { - // temporary hack for builtin resources - if((_path[0] == ':') && (_path[1] == '/')) - return _path; - - std::string path = exists(_path) ? getAbsolutePath(_path) : getGenericPath(_path); - - // cleanup path - bool scan = true; - while(scan) - { - stringList pathList = getPathList(path); - - path.clear(); - scan = false; - - for(stringList::const_iterator it = pathList.cbegin(); it != pathList.cend(); ++it) - { - // ignore empty - if((*it).empty()) - continue; - - // remove "/./" - if((*it) == ".") - continue; - - // resolve "/../" - if((*it) == "..") - { - path = getParent(path); - continue; - } - -#if defined(_WIN32) - // append folder to path - path += (path.size() == 0) ? (*it) : ("/" + (*it)); -#else // _WIN32 - // append folder to path - path += ("/" + (*it)); -#endif // _WIN32 - - // resolve symlink - if(isSymlink(path)) - { - std::string resolved = resolveSymlink(path); - - if(resolved.empty()) - return ""; - - if(isAbsolute(resolved)) - path = resolved; - else - path = getParent(path) + "/" + resolved; - - for(++it; it != pathList.cend(); ++it) - path += (path.size() == 0) ? (*it) : ("/" + (*it)); - - scan = true; - break; - } - } - } - - // return canonical path - return path; - - } // getCanonicalPath - - std::string getAbsolutePath(const std::string& _path, const std::string& _base) - { - std::string path = getGenericPath(_path); - std::string base = isAbsolute(_base) ? getGenericPath(_base) : getAbsolutePath(_base); - - // return absolute path - return isAbsolute(path) ? path : getGenericPath(base + "/" + path); - - } // getAbsolutePath - - std::string getParent(const std::string& _path) - { - std::string path = getGenericPath(_path); - size_t offset = std::string::npos; - - // find last '/' and erase it - if((offset = path.find_last_of('/')) != std::string::npos) - return path.erase(offset); - - // no parent found - return path; - - } // getParent - - std::string getFileName(const std::string& _path) - { - std::string path = getGenericPath(_path); - size_t offset = std::string::npos; - - // find last '/' and return the filename - if((offset = path.find_last_of('/')) != std::string::npos) - return ((path[offset + 1] == 0) ? "." : std::string(path, offset + 1)); - - // no '/' found, entire path is a filename - return path; - - } // getFileName - - std::string getStem(const std::string& _path) - { - std::string fileName = getFileName(_path); - size_t offset = std::string::npos; - - // empty fileName - if(fileName == ".") - return fileName; - - // find last '.' and erase the extension - if((offset = fileName.find_last_of('.')) != std::string::npos) - return fileName.erase(offset); - - // no '.' found, filename has no extension - return fileName; - - } // getStem - - std::string getExtension(const std::string& _path) - { - std::string fileName = getFileName(_path); - size_t offset = std::string::npos; - - // empty fileName - if(fileName == ".") - return fileName; - - // find last '.' and return the extension - if((offset = fileName.find_last_of('.')) != std::string::npos) - return std::string(fileName, offset); - - // no '.' found, filename has no extension - return "."; - - } // getExtension - - std::string resolveRelativePath(const std::string& _path, const std::string& _relativeTo, const bool _allowHome) - { - std::string path = getGenericPath(_path); - std::string relativeTo = isDirectory(_relativeTo) ? getGenericPath(_relativeTo) : getParent(_relativeTo); - - // nothing to resolve - if(!path.length()) - return path; - - // replace '.' with relativeTo - if((path[0] == '.') && (path[1] == '/')) - return (relativeTo + &(path[1])); - - // replace '~' with homePath - if(_allowHome && (path[0] == '~') && (path[1] == '/')) - return (getHomePath() + &(path[1])); - - // nothing to resolve - return path; - - } // resolveRelativePath - - std::string createRelativePath(const std::string& _path, const std::string& _relativeTo, const bool _allowHome) - { - bool contains = false; - std::string path = removeCommonPath(_path, _relativeTo, contains); - - // success - if(contains) - return ("./" + path); - - if(_allowHome) - { - path = removeCommonPath(_path, getHomePath(), contains); - - // success - if(contains) - return ("~/" + path); - } - - // nothing to resolve - return path; - - } // createRelativePath - - std::string removeCommonPath(const std::string& _path, const std::string& _common, bool& _contains) - { - std::string path = getGenericPath(_path); - std::string common = isDirectory(_common) ? getGenericPath(_common) : getParent(_common); - - // check if path contains common - if(path.find(common) == 0) - { - _contains = true; - return path.substr(common.length() + 1); - } - - // it didn't - _contains = false; - return path; - - } // removeCommonPath - - std::string resolveSymlink(const std::string& _path) - { - std::string path = getGenericPath(_path); - std::string resolved; - -#if defined(_WIN32) - HANDLE hFile = CreateFile(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); - - if(hFile != INVALID_HANDLE_VALUE) - { - resolved.resize(GetFinalPathNameByHandle(hFile, nullptr, 0, FILE_NAME_NORMALIZED) + 1); - if(GetFinalPathNameByHandle(hFile, (LPSTR)resolved.data(), (DWORD)resolved.size(), FILE_NAME_NORMALIZED) > 0) - { - resolved.resize(resolved.size() - 1); - resolved = getGenericPath(resolved); - } - CloseHandle(hFile); - } -#else // _WIN32 - struct stat info; - - // check if lstat succeeded - if(lstat(path.c_str(), &info) == 0) - { - resolved.resize(info.st_size); - if(readlink(path.c_str(), (char*)resolved.data(), resolved.size()) > 0) - resolved = getGenericPath(resolved); - } -#endif // _WIN32 - - // return resolved path - return resolved; - - } // resolveSymlink - - bool removeFile(const std::string& _path) - { - std::string path = getGenericPath(_path); - - // don't remove if it doesn't exists - if(!exists(path)) - return true; - - // try to remove file - return (unlink(path.c_str()) == 0); - - } // removeFile - - bool createDirectory(const std::string& _path) - { - std::string path = getGenericPath(_path); - - // don't create if it already exists - if(exists(path)) - return true; - - // try to create directory - if(mkdir(path.c_str(), 0755) == 0) - return true; - - // failed to create directory, try to create the parent - std::string parent = getParent(path); - - // only try to create parent if it's not identical to path - if(parent != path) - createDirectory(parent); - - // try to create directory again now that the parent should exist - return (mkdir(path.c_str(), 0755) == 0); - - } // createDirectory - - bool exists(const std::string& _path) - { - std::string path = getGenericPath(_path); - struct stat64 info; - - // check if stat64 succeeded - return (stat64(path.c_str(), &info) == 0); - - } // exists - - bool isAbsolute(const std::string& _path) - { - std::string path = getGenericPath(_path); - -#if defined(_WIN32) - return ((path.size() > 1) && (path[1] == ':')); -#else // _WIN32 - return ((path.size() > 0) && (path[0] == '/')); -#endif // _WIN32 - - } // isAbsolute - - bool isRegularFile(const std::string& _path) - { - std::string path = getGenericPath(_path); - struct stat64 info; - - // check if stat64 succeeded - if(stat64(path.c_str(), &info) != 0) - return false; - - // check for S_IFREG attribute - return (S_ISREG(info.st_mode)); - - } // isRegularFile - - bool isDirectory(const std::string& _path) - { - std::string path = getGenericPath(_path); - struct stat info; - - // check if stat succeeded - if(stat(path.c_str(), &info) != 0) - return false; - - // check for S_IFDIR attribute - return (S_ISDIR(info.st_mode)); - - } // isDirectory - - bool isSymlink(const std::string& _path) - { - std::string path = getGenericPath(_path); - -#if defined(_WIN32) - // check for symlink attribute - const DWORD Attributes = GetFileAttributes(path.c_str()); - if((Attributes != INVALID_FILE_ATTRIBUTES) && (Attributes & FILE_ATTRIBUTE_REPARSE_POINT)) - return true; -#else // _WIN32 - struct stat info; - - // check if lstat succeeded - if(lstat(path.c_str(), &info) != 0) - return false; - - // check for S_IFLNK attribute - return (S_ISLNK(info.st_mode)); -#endif // _WIN32 - - // not a symlink - return false; - - } // isSymlink - - bool isHidden(const std::string& _path) - { - std::string path = getGenericPath(_path); - -#if defined(_WIN32) - // check for hidden attribute - const DWORD Attributes = GetFileAttributes(path.c_str()); - if((Attributes != INVALID_FILE_ATTRIBUTES) && (Attributes & FILE_ATTRIBUTE_HIDDEN)) - return true; -#endif // _WIN32 - - // filenames starting with . are hidden in linux, we do this check for windows as well - if(getFileName(path)[0] == '.') - return true; - - // not hidden - return false; - - } // isHidden - - } // FileSystem:: - + namespace FileSystem + { + static std::string homePath = ""; + static std::string exePath = ""; + + #if defined(_WIN32) + static std::string convertFromWideString(const std::wstring wstring) + { + int numBytes = WideCharToMultiByte(CP_UTF8, 0, wstring.c_str(), + (int)wstring.length(), nullptr, 0, nullptr, nullptr); + std::string string; + + string.resize(numBytes); + WideCharToMultiByte(CP_UTF8, 0, wstring.c_str(), (int)wstring.length(), + (char*)string.c_str(), numBytes, nullptr, nullptr); + + return std::string(string); + } + #endif + + stringList getDirContent(const std::string& _path, const bool _recursive) + { + std::string path = getGenericPath(_path); + stringList contentList; + + // Only parse the directory, if it's a directory. + if (isDirectory(path)) { + + #if defined(_WIN32) + WIN32_FIND_DATAW findData; + std::string wildcard = path + "/*"; + HANDLE hFind = FindFirstFileW(std::wstring(wildcard.begin(), + wildcard.end()).c_str(), &findData); + + if (hFind != INVALID_HANDLE_VALUE) { + // Loop over all files in the directory. + do { + std::string name = convertFromWideString(findData.cFileName); + // Ignore "." and ".." + if ((name != ".") && (name != "..")) { + std::string fullName(getGenericPath(path + "/" + name)); + contentList.push_back(fullName); + + if (_recursive && isDirectory(fullName)) + contentList.merge(getDirContent(fullName, true)); + } + } + while (FindNextFileW(hFind, &findData)); + FindClose(hFind); + } + #else + DIR* dir = opendir(path.c_str()); + + if (dir != NULL) { + struct dirent* entry; + // Loop over all files in the directory. + while ((entry = readdir(dir)) != NULL) { + std::string name(entry->d_name); + + // Ignore "." and ".." + if ((name != ".") && (name != "..")) { + std::string fullName(getGenericPath(path + "/" + name)); + contentList.push_back(fullName); + + if (_recursive && isDirectory(fullName)) + contentList.merge(getDirContent(fullName, true)); + } + } + closedir(dir); + } + #endif + } + contentList.sort(); + return contentList; + } + + stringList getPathList(const std::string& _path) + { + stringList pathList; + std::string path = getGenericPath(_path); + size_t start = 0; + size_t end = 0; + + // Split at '/' + while ((end = path.find("/", start)) != std::string::npos) { + if (end != start) + pathList.push_back(std::string(path, start, end - start)); + start = end + 1; + } + // Add last folder / file to pathList. + if (start != path.size()) + pathList.push_back(std::string(path, start, path.size() - start)); + + return pathList; + } + + void setHomePath(const std::string& _path) + { + homePath = getGenericPath(_path); + } + + std::string getHomePath() + { + // Only construct the homepath once. + if (homePath.length()) + return homePath; + + // Check for HOME environment variable. + if (!homePath.length()) { + std::string envHome = getenv("HOME"); + if (envHome.length()) + homePath = getGenericPath(envHome); + } + + #if defined(_WIN32) + // On Windows we need to check HOMEDRIVE and HOMEPATH. + if (!homePath.length()) { + std::string envHomeDrive = getenv("HOMEDRIVE"); + std::string envHomePath = getenv("HOMEPATH"); + if (envHomeDrive.length() && envHomePath.length()) + homePath = getGenericPath(envHomeDrive + "/" + envHomePath); + } + #endif // _WIN32 + + // No homepath found, fall back to current working directory. + if (!homePath.length()) + homePath = getCWDPath(); + + return homePath; + } + + std::string getCWDPath() + { + char temp[512]; + + // Return current working directory. + return (getcwd(temp, 512) ? getGenericPath(temp) : ""); + } + + void setExePath(const std::string& _path) + { + constexpr int path_max = 32767; + #if defined(_WIN32) + std::wstring result(path_max, 0); + if (GetModuleFileNameW(nullptr, &result[0], path_max) != 0) + exePath = convertFromWideString(result); + #else + std::string result(path_max, 0); + if (readlink("/proc/self/exe", &result[0], path_max) != -1) + exePath = result; + #endif + exePath = getCanonicalPath(exePath); + + // Fallback to argv[0] if everything else fails. + if (exePath.empty()) + exePath = getCanonicalPath(_path); + if (isRegularFile(exePath)) + exePath = getParent(exePath); + } + + std::string getExePath() + { + return exePath; + } + + std::string getInstallPrefixPath() + { + // There seems to be a bug in CMake that when deleting the CMakeCache.txt + // file and running cmake, the ES_DATAROOTDIR is not populated, i.e. this fails: + // add_definitions(-DES_DATAROOTDIR="${CMAKE_INSTALL_DATAROOTDIR}") + // Re-running cmake a second time populates it correctly. But ES_INSTALL_PREFIX + // is always populated correctly on my machine which is very strange. + // Anyway, as an extra precaution, let's set datarootdir to 'share' if it's blank, + // as that's what most people would want anyway. When this bug has been fixed in + // CMake this code can be removed. + // Just in case, let's set installPrefix to '/usr/local' if blank as well as a + // fallback precaution as maybe some make environments won't handle this + // correctly either. + if (!installPrefix.length()) + installPrefix = "/usr/local"; + if (!dataRootDir.length()) + dataRootDir = "share"; + return installPrefix + "/" + dataRootDir; + } + + std::string getPreferredPath(const std::string& _path) + { + std::string path = _path; + size_t offset = std::string::npos; + #if defined(_WIN32) + // Convert '/' to '\\' + while ((offset = path.find('/')) != std::string::npos) + path.replace(offset, 1, "\\"); + #endif + return path; + } + + std::string getGenericPath(const std::string& _path) + { + std::string path = _path; + size_t offset = std::string::npos; + + // Remove "\\\\?\\" + if ((path.find("\\\\?\\")) == 0) + path.erase(0, 4); + + // Convert '\\' to '/' + while ((offset = path.find('\\')) != std::string::npos) + path.replace(offset, 1 ,"/"); + + // Remove double '/' + while ((offset = path.find("//")) != std::string::npos) + path.erase(offset, 1); + + // Remove trailing '/' when the path is more than a simple '/' + while (path.length() > 1 && ((offset = path.find_last_of('/')) == (path.length() - 1))) + path.erase(offset, 1); + + return path; + } + + std::string getEscapedPath(const std::string& _path) + { + std::string path = getGenericPath(_path); + + #if defined(_WIN32) + // Windows escapes stuff by just putting everything in quotes. + return '"' + getPreferredPath(path) + '"'; + #else + // Insert a backslash before most characters that would mess up a bash path. + const char* invalidChars = "\\ '\"!$^&*(){}[]?;<>"; + const char* invalidChar = invalidChars; + + while (*invalidChar) { + size_t start = 0; + size_t offset = 0; + + while ((offset = path.find(*invalidChar, start)) != std::string::npos) { + start = offset + 1; + + if ((offset == 0) || (path[offset - 1] != '\\')) { + path.insert(offset, 1, '\\'); + ++start; + } + } + ++invalidChar; + } + return path; + #endif + } + + std::string getCanonicalPath(const std::string& _path) + { + // Hack for builtin resources. + if ((_path[0] == ':') && (_path[1] == '/')) + return _path; + + std::string path = exists(_path) ? getAbsolutePath(_path) : getGenericPath(_path); + + // Cleanup path. + bool scan = true; + while (scan) { + stringList pathList = getPathList(path); + + path.clear(); + scan = false; + + for (stringList::const_iterator it = pathList.cbegin(); + it != pathList.cend(); ++it) { + // Ignore empty. + if ((*it).empty()) + continue; + + // Remove "/./" + if ((*it) == ".") + continue; + + // Resolve "/../" + if ((*it) == "..") { + path = getParent(path); + continue; + } + + #if defined(_WIN32) + // Append folder to path. + path += (path.size() == 0) ? (*it) : ("/" + (*it)); + #else + // Append folder to path. + path += ("/" + (*it)); + #endif + + // Resolve symlink. + if (isSymlink(path)) { + std::string resolved = resolveSymlink(path); + + if (resolved.empty()) + return ""; + + if (isAbsolute(resolved)) + path = resolved; + else + path = getParent(path) + "/" + resolved; + + for (++it; it != pathList.cend(); ++it) + path += (path.size() == 0) ? (*it) : ("/" + (*it)); + + scan = true; + break; + } + } + } + return path; + } + + std::string getAbsolutePath(const std::string& _path, const std::string& _base) + { + std::string path = getGenericPath(_path); + std::string base = isAbsolute(_base) ? getGenericPath(_base) : getAbsolutePath(_base); + + // Return absolute path. + return isAbsolute(path) ? path : getGenericPath(base + "/" + path); + + } + + std::string getParent(const std::string& _path) + { + std::string path = getGenericPath(_path); + size_t offset = std::string::npos; + + // Find last '/' and erase it. + if ((offset = path.find_last_of('/')) != std::string::npos) + return path.erase(offset); + + // No parent found. + return path; + } + + std::string getFileName(const std::string& _path) + { + std::string path = getGenericPath(_path); + size_t offset = std::string::npos; + + // Find last '/' and return the filename. + if ((offset = path.find_last_of('/')) != std::string::npos) + return ((path[offset + 1] == 0) ? "." : std::string(path, offset + 1)); + + // No '/' found, entire path is a filename. + return path; + } + + std::string getStem(const std::string& _path) + { + std::string fileName = getFileName(_path); + size_t offset = std::string::npos; + + // Empty fileName. + if (fileName == ".") + return fileName; + + // Find last '.' and erase the extension. + if ((offset = fileName.find_last_of('.')) != std::string::npos) + return fileName.erase(offset); + + // No '.' found, filename has no extension. + return fileName; + } + + std::string getExtension(const std::string& _path) + { + std::string fileName = getFileName(_path); + size_t offset = std::string::npos; + + // Empty fileName. + if (fileName == ".") + return fileName; + + // Find last '.' and return the extension. + if ((offset = fileName.find_last_of('.')) != std::string::npos) + return std::string(fileName, offset); + + // No '.' found, filename has no extension. + return "."; + } + + std::string resolveRelativePath(const std::string& _path, + const std::string& _relativeTo, const bool _allowHome) + { + std::string path = getGenericPath(_path); + std::string relativeTo = isDirectory(_relativeTo) ? + getGenericPath(_relativeTo) : getParent(_relativeTo); + + // Nothing to resolve. + if (!path.length()) + return path; + + // Replace '.' with relativeTo. + if ((path[0] == '.') && (path[1] == '/')) + return (relativeTo + &(path[1])); + + // Replace '~' with homePath. + if (_allowHome && (path[0] == '~') && (path[1] == '/')) + return (getHomePath() + &(path[1])); + + // Nothing to resolve. + return path; + } + + std::string createRelativePath(const std::string& _path, + const std::string& _relativeTo, const bool _allowHome) + { + bool contains = false; + std::string path = removeCommonPath(_path, _relativeTo, contains); + + if (contains) + return ("./" + path); + + if (_allowHome) { + path = removeCommonPath(_path, getHomePath(), contains); + + if (contains) + return ("~/" + path); + } + return path; + } + + std::string removeCommonPath(const std::string& _path, + const std::string& _common, bool& _contains) + { + std::string path = getGenericPath(_path); + std::string common = isDirectory(_common) ? + getGenericPath(_common) : getParent(_common); + + // Check if path contains common. + if (path.find(common) == 0) { + _contains = true; + return path.substr(common.length() + 1); + } + + // It didn't. + _contains = false; + return path; + } + + std::string resolveSymlink(const std::string& _path) + { + std::string path = getGenericPath(_path); + std::string resolved; + + #if defined(_WIN32) + HANDLE hFile = CreateFile(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ, + 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); + + if (hFile != INVALID_HANDLE_VALUE) { + resolved.resize(GetFinalPathNameByHandle(hFile, nullptr, 0, + FILE_NAME_NORMALIZED) + 1); + if (GetFinalPathNameByHandle(hFile, (LPSTR)resolved.data(), + (DWORD)resolved.size(), FILE_NAME_NORMALIZED) > 0) { + resolved.resize(resolved.size() - 1); + resolved = getGenericPath(resolved); + } + CloseHandle(hFile); + } + #else + struct stat info; + + // Check if lstat succeeded. + if (lstat(path.c_str(), &info) == 0) { + resolved.resize(info.st_size); + if (readlink(path.c_str(), (char*)resolved.data(), resolved.size()) > 0) + resolved = getGenericPath(resolved); + } + #endif + return resolved; + } + + bool removeFile(const std::string& _path) + { + std::string path = getGenericPath(_path); + + // Don't remove if it doesn't exists. + if (!exists(path)) + return true; + + // Try to remove file. + return (unlink(path.c_str()) == 0); + } + + bool createDirectory(const std::string& _path) + { + std::string path = getGenericPath(_path); + + // Don't create if it already exists. + if (exists(path)) + return true; + + // Try to create directory. + if (mkdir(path.c_str(), 0755) == 0) + return true; + + // Failed to create directory, try to create the parent. + std::string parent = getParent(path); + + // Only try to create parent if it's not identical to path. + if (parent != path) + createDirectory(parent); + + // Try to create directory again now that the parent should exist. + return (mkdir(path.c_str(), 0755) == 0); + } + + bool exists(const std::string& _path) + { + std::string path = getGenericPath(_path); + struct stat64 info; + + return (stat64(path.c_str(), &info) == 0); + } + + bool isAbsolute(const std::string& _path) + { + std::string path = getGenericPath(_path); + + #if defined(_WIN32) + return ((path.size() > 1) && (path[1] == ':')); + #else + return ((path.size() > 0) && (path[0] == '/')); + #endif + } + + bool isRegularFile(const std::string& _path) + { + std::string path = getGenericPath(_path); + struct stat64 info; + + if (stat64(path.c_str(), &info) != 0) + return false; + + // Check for S_IFREG attribute. + return (S_ISREG(info.st_mode)); + } + + bool isDirectory(const std::string& _path) + { + std::string path = getGenericPath(_path); + struct stat info; + + if (stat(path.c_str(), &info) != 0) + return false; + + // Check for S_IFDIR attribute. + return (S_ISDIR(info.st_mode)); + } + + bool isSymlink(const std::string& _path) + { + std::string path = getGenericPath(_path); + + #if defined(_WIN32) + // Check for symlink attribute. + const DWORD Attributes = GetFileAttributes(path.c_str()); + if ((Attributes != INVALID_FILE_ATTRIBUTES) && + (Attributes & FILE_ATTRIBUTE_REPARSE_POINT)) + return true; + #else + struct stat info; + + if (lstat(path.c_str(), &info) != 0) + return false; + + // Check for S_IFLNK attribute. + return (S_ISLNK(info.st_mode)); + #endif + + // Not a symlink. + return false; + } + + bool isHidden(const std::string& _path) + { + std::string path = getGenericPath(_path); + + #if defined(_WIN32) + // Check for hidden attribute. + const DWORD Attributes = GetFileAttributes(path.c_str()); + if ((Attributes != INVALID_FILE_ATTRIBUTES) && (Attributes & FILE_ATTRIBUTE_HIDDEN)) + return true; + #endif // _WIN32 + + // Filenames starting with . are hidden in Linux, but + // we do this check for windows as well. + if (getFileName(path)[0] == '.') + return true; + + return false; + } + + } // FileSystem:: } // Utils:: diff --git a/es-core/src/utils/FileSystemUtil.h b/es-core/src/utils/FileSystemUtil.h index 13e2bf959..1c6122e1b 100644 --- a/es-core/src/utils/FileSystemUtil.h +++ b/es-core/src/utils/FileSystemUtil.h @@ -1,3 +1,9 @@ +// +// FileSystemUtil.h +// +// Low-level filesystem functions. +// + #pragma once #ifndef ES_CORE_UTILS_FILE_SYSTEM_UTIL_H #define ES_CORE_UTILS_FILE_SYSTEM_UTIL_H @@ -7,42 +13,45 @@ namespace Utils { - namespace FileSystem - { - typedef std::list stringList; + namespace FileSystem + { + typedef std::list stringList; - stringList getDirContent (const std::string& _path, const bool _recursive = false); - stringList getPathList (const std::string& _path); - void setHomePath (const std::string& _path); - std::string getHomePath (); - std::string getCWDPath (); - void setExePath (const std::string& _path); - std::string getExePath (); - std::string getInstallPrefixPath (); - std::string getPreferredPath (const std::string& _path); - std::string getGenericPath (const std::string& _path); - std::string getEscapedPath (const std::string& _path); - std::string getCanonicalPath (const std::string& _path); - std::string getAbsolutePath (const std::string& _path, const std::string& _base = getCWDPath()); - std::string getParent (const std::string& _path); - std::string getFileName (const std::string& _path); - std::string getStem (const std::string& _path); - std::string getExtension (const std::string& _path); - std::string resolveRelativePath(const std::string& _path, const std::string& _relativeTo, const bool _allowHome); - std::string createRelativePath (const std::string& _path, const std::string& _relativeTo, const bool _allowHome); - std::string removeCommonPath (const std::string& _path, const std::string& _common, bool& _contains); - std::string resolveSymlink (const std::string& _path); - bool removeFile (const std::string& _path); - bool createDirectory (const std::string& _path); - bool exists (const std::string& _path); - bool isAbsolute (const std::string& _path); - bool isRegularFile (const std::string& _path); - bool isDirectory (const std::string& _path); - bool isSymlink (const std::string& _path); - bool isHidden (const std::string& _path); - - } // FileSystem:: - -} // Utils:: + stringList getDirContent(const std::string& _path, + const bool _recursive = false); + stringList getPathList(const std::string& _path); + void setHomePath(const std::string& _path); + std::string getHomePath(); + std::string getCWDPath(); + void setExePath(const std::string& _path); + std::string getExePath(); + std::string getInstallPrefixPath (); + std::string getPreferredPath(const std::string& _path); + std::string getGenericPath(const std::string& _path); + std::string getEscapedPath(const std::string& _path); + std::string getCanonicalPath(const std::string& _path); + std::string getAbsolutePath(const std::string& _path, + const std::string& _base = getCWDPath()); + std::string getParent(const std::string& _path); + std::string getFileName(const std::string& _path); + std::string getStem(const std::string& _path); + std::string getExtension(const std::string& _path); + std::string resolveRelativePath(const std::string& _path, + const std::string& _relativeTo, const bool _allowHome); + std::string createRelativePath(const std::string& _path, + const std::string& _relativeTo, const bool _allowHome); + std::string removeCommonPath(const std::string& _path, + const std::string& _common, bool& _contains); + std::string resolveSymlink(const std::string& _path); + bool removeFile(const std::string& _path); + bool createDirectory(const std::string& _path); + bool exists(const std::string& _path); + bool isAbsolute(const std::string& _path); + bool isRegularFile(const std::string& _path); + bool isDirectory(const std::string& _path); + bool isSymlink(const std::string& _path); + bool isHidden(const std::string& _path); + } +} #endif // ES_CORE_UTILS_FILE_SYSTEM_UTIL_H