Code cleanup and code documentation

This commit is contained in:
Leon Styhre 2020-06-22 17:27:53 +02:00
parent ac91da6995
commit 672026632d
24 changed files with 1741 additions and 1688 deletions

View file

@ -18,11 +18,12 @@ Some key points:
* Comments should be proper sentences, starting with a capital letter and ending with a dot * 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 * 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. * 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 * 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 * 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 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 * 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() * 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 * 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 * Never put more than one statement on a single line, except for lambda expressions

View file

@ -31,4 +31,5 @@ v1.0.0
* Game images were sometimes scaled incorrectly * Game images were sometimes scaled incorrectly
* Non-transparent favorite icons were not rendered correctly * Non-transparent favorite icons were not rendered correctly
* Restart and power-off menu entries not working (i.e. on a desktop OS) * 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 * Lots and lots of small bugs and inconsistencies fixed

View file

@ -3,9 +3,7 @@ EmulationStation Desktop Edition
EmulationStation Desktop Edition is a cross-platform graphical front-end for emulators with controller and keyboard navigation. 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. 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.
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.
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. 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.
@ -15,14 +13,12 @@ Apart from code commits, help is especially needed for thorough testing of the s
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. 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: In general, a review of the [es_systems.cfg](resources/templates/es_systems.cfg_unix) file including the supported file extensions would be great.
[es_systems.cfg_unix](resources/templates/es_systems.cfg_unix
As for RBSimple-DE there are quite some missing graphic files and other customizations for a number of game systems. \ 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. 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 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. [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 What it can do
============== ==============

View file

@ -1,3 +1,9 @@
//
// FileFilterIndex.cpp
//
// Gamelist filters.
//
#include "FileFilterIndex.h" #include "FileFilterIndex.h"
#include "utils/StringUtil.h" #include "utils/StringUtil.h"
@ -10,9 +16,16 @@
#define INCLUDE_UNKNOWN false; #define INCLUDE_UNKNOWN false;
FileFilterIndex::FileFilterIndex() 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(); clearAllFilters();
FilterDataDecl filterDecls[] = { FilterDataDecl filterDecls[] = {
//type //allKeys //filteredBy //filteredKeys //primaryKey //hasSecondaryKey //secondaryKey //menuLabel //type //allKeys //filteredBy //filteredKeys //primaryKey //hasSecondaryKey //secondaryKey //menuLabel
{ FAVORITES_FILTER, &favoritesIndexAllKeys, &filterByFavorites, &favoritesIndexFilteredKeys,"favorite", false, "", "FAVORITES" }, { FAVORITES_FILTER, &favoritesIndexAllKeys, &filterByFavorites, &favoritesIndexFilteredKeys,"favorite", false, "", "FAVORITES" },
@ -24,7 +37,8 @@ FileFilterIndex::FileFilterIndex()
{ HIDDEN_FILTER, &hiddenIndexAllKeys, &filterByHidden, &hiddenIndexFilteredKeys, "hidden", false, "", "HIDDEN" } { HIDDEN_FILTER, &hiddenIndexAllKeys, &filterByHidden, &hiddenIndexFilteredKeys, "hidden", false, "", "HIDDEN" }
}; };
filterDataDecl = std::vector<FilterDataDecl>(filterDecls, filterDecls + sizeof(filterDecls) / sizeof(filterDecls[0])); filterDataDecl = std::vector<FilterDataDecl>(filterDecls, filterDecls +
sizeof(filterDecls) / sizeof(filterDecls[0]));
} }
FileFilterIndex::~FileFilterIndex() FileFilterIndex::~FileFilterIndex()
@ -39,8 +53,7 @@ std::vector<FilterDataDecl>& FileFilterIndex::getFilterDataDecls()
void FileFilterIndex::importIndex(FileFilterIndex* indexToImport) void FileFilterIndex::importIndex(FileFilterIndex* indexToImport)
{ {
struct IndexImportStructure struct IndexImportStructure {
{
std::map<std::string, int>* destinationIndex; std::map<std::string, int>* destinationIndex;
std::map<std::string, int>* sourceIndex; std::map<std::string, int>* sourceIndex;
}; };
@ -55,24 +68,26 @@ void FileFilterIndex::importIndex(FileFilterIndex* indexToImport)
{ &kidGameIndexAllKeys, &(indexToImport->kidGameIndexAllKeys) }, { &kidGameIndexAllKeys, &(indexToImport->kidGameIndexAllKeys) },
}; };
std::vector<IndexImportStructure> indexImportDecl = std::vector<IndexImportStructure>(indexStructDecls, indexStructDecls + sizeof(indexStructDecls) / sizeof(indexStructDecls[0])); std::vector<IndexImportStructure> indexImportDecl =
std::vector<IndexImportStructure>(indexStructDecls, indexStructDecls +
sizeof(indexStructDecls) / sizeof(indexStructDecls[0]));
for (std::vector<IndexImportStructure>::const_iterator indexesIt = indexImportDecl.cbegin(); indexesIt != indexImportDecl.cend(); ++indexesIt ) for (std::vector<IndexImportStructure>::const_iterator indexesIt =
indexImportDecl.cbegin(); indexesIt != indexImportDecl.cend(); ++indexesIt )
{ {
for (std::map<std::string, int>::const_iterator sourceIt = (*indexesIt).sourceIndex->cbegin(); sourceIt != (*indexesIt).sourceIndex->cend(); ++sourceIt ) for (std::map<std::string, int>::const_iterator sourceIt =
{ (*indexesIt).sourceIndex->cbegin(); sourceIt !=
if ((*indexesIt).destinationIndex->find((*sourceIt).first) == (*indexesIt).destinationIndex->cend()) (*indexesIt).sourceIndex->cend(); ++sourceIt ) {
{ if ((*indexesIt).destinationIndex->find((*sourceIt).first) ==
// entry doesn't exist (*indexesIt).destinationIndex->cend())
// Entry doesn't exist.
(*((*indexesIt).destinationIndex))[(*sourceIt).first] = (*sourceIt).second; (*((*indexesIt).destinationIndex))[(*sourceIt).first] = (*sourceIt).second;
}
else else
{
(*((*indexesIt).destinationIndex))[(*sourceIt).first] += (*sourceIt).second; (*((*indexesIt).destinationIndex))[(*sourceIt).first] += (*sourceIt).second;
} }
} }
} }
}
void FileFilterIndex::resetIndex() void FileFilterIndex::resetIndex()
{ {
clearAllFilters(); clearAllFilters();
@ -85,13 +100,12 @@ void FileFilterIndex::resetIndex()
clearIndex(kidGameIndexAllKeys); 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 = ""; std::string key = "";
switch(type) switch (type) {
{ case GENRE_FILTER: {
case GENRE_FILTER:
{
key = Utils::String::toUpper(game->metadata.get("genre")); key = Utils::String::toUpper(game->metadata.get("genre"));
key = Utils::String::trim(key); key = Utils::String::trim(key);
if (getSecondary && !key.empty()) { if (getSecondary && !key.empty()) {
@ -99,26 +113,20 @@ std::string FileFilterIndex::getIndexableKey(FileData* game, FilterIndexType typ
std::string newKey; std::string newKey;
getline(f, newKey, '/'); getline(f, newKey, '/');
if (!newKey.empty() && newKey != key) if (!newKey.empty() && newKey != key)
{
key = newKey; key = newKey;
}
else else
{
key = std::string(); key = std::string();
} }
}
break; break;
} }
case PLAYER_FILTER: case PLAYER_FILTER: {
{
if (getSecondary) if (getSecondary)
break; break;
key = game->metadata.get("players"); key = game->metadata.get("players");
break; break;
} }
case PUBDEV_FILTER: case PUBDEV_FILTER: {
{
key = Utils::String::toUpper(game->metadata.get("publisher")); key = Utils::String::toUpper(game->metadata.get("publisher"));
key = Utils::String::trim(key); key = Utils::String::trim(key);
@ -128,11 +136,9 @@ std::string FileFilterIndex::getIndexableKey(FileData* game, FilterIndexType typ
key = Utils::String::toUpper(game->metadata.get("publisher")); key = Utils::String::toUpper(game->metadata.get("publisher"));
break; break;
} }
case RATINGS_FILTER: case RATINGS_FILTER: {
{
int ratingNumber = 0; int ratingNumber = 0;
if (!getSecondary) if (!getSecondary) {
{
std::string ratingString = game->metadata.get("rating"); std::string ratingString = game->metadata.get("rating");
if (!ratingString.empty()) { if (!ratingString.empty()) {
try { try {
@ -142,30 +148,27 @@ std::string FileFilterIndex::getIndexableKey(FileData* game, FilterIndexType typ
key = std::to_string(ratingNumber) + " STARS"; key = std::to_string(ratingNumber) + " STARS";
} }
catch (int e) catch (int e) {
{ LOG(LogError) << "Error parsing Rating (invalid value, exception nr.): " <<
LOG(LogError) << "Error parsing Rating (invalid value, exception nr.): " << ratingString << ", " << e; ratingString << ", " << e;
} }
} }
} }
break; break;
} }
case FAVORITES_FILTER: case FAVORITES_FILTER: {
{
if (game->getType() != GAME) if (game->getType() != GAME)
return "FALSE"; return "FALSE";
key = Utils::String::toUpper(game->metadata.get("favorite")); key = Utils::String::toUpper(game->metadata.get("favorite"));
break; break;
} }
case HIDDEN_FILTER: case HIDDEN_FILTER: {
{
if (game->getType() != GAME) if (game->getType() != GAME)
return "FALSE"; return "FALSE";
key = Utils::String::toUpper(game->metadata.get("hidden")); key = Utils::String::toUpper(game->metadata.get("hidden"));
break; break;
} }
case KIDGAME_FILTER: case KIDGAME_FILTER: {
{
if (game->getType() != GAME) if (game->getType() != GAME)
return "FALSE"; return "FALSE";
key = Utils::String::toUpper(game->metadata.get("kidgame")); key = Utils::String::toUpper(game->metadata.get("kidgame"));
@ -203,21 +206,20 @@ void FileFilterIndex::removeFromIndex(FileData* game)
void FileFilterIndex::setFilter(FilterIndexType type, std::vector<std::string>* values) void FileFilterIndex::setFilter(FilterIndexType type, std::vector<std::string>* values)
{ {
// test if it exists before setting // Test if it exists before setting.
if(type == NONE) if (type == NONE) {
{
clearAllFilters(); clearAllFilters();
} }
else else {
{ for (std::vector<FilterDataDecl>::const_iterator it = filterDataDecl.cbegin();
for (std::vector<FilterDataDecl>::const_iterator it = filterDataDecl.cbegin(); it != filterDataDecl.cend(); ++it ) { it != filterDataDecl.cend(); ++it ) {
if ((*it).type == type) if ((*it).type == type) {
{
FilterDataDecl filterData = (*it); FilterDataDecl filterData = (*it);
*(filterData.filteredByRef) = values->size() > 0; *(filterData.filteredByRef) = values->size() > 0;
filterData.currentFilteredKeys->clear(); filterData.currentFilteredKeys->clear();
for (std::vector<std::string>::const_iterator vit = values->cbegin(); vit != values->cend(); ++vit ) { for (std::vector<std::string>::const_iterator vit =
// check if exists values->cbegin(); vit != values->cend(); ++vit ) {
// Check if exists.
if (filterData.allIndexKeys->find(*vit) != filterData.allIndexKeys->cend()) { if (filterData.allIndexKeys->find(*vit) != filterData.allIndexKeys->cend()) {
filterData.currentFilteredKeys->push_back(std::string(*vit)); filterData.currentFilteredKeys->push_back(std::string(*vit));
} }
@ -230,8 +232,8 @@ void FileFilterIndex::setFilter(FilterIndexType type, std::vector<std::string>*
void FileFilterIndex::clearAllFilters() void FileFilterIndex::clearAllFilters()
{ {
for (std::vector<FilterDataDecl>::const_iterator it = filterDataDecl.cbegin(); it != filterDataDecl.cend(); ++it ) for (std::vector<FilterDataDecl>::const_iterator it = filterDataDecl.cbegin();
{ it != filterDataDecl.cend(); ++it ) {
FilterDataDecl filterData = (*it); FilterDataDecl filterData = (*it);
*(filterData.filteredByRef) = false; *(filterData.filteredByRef) = false;
filterData.currentFilteredKeys->clear(); filterData.currentFilteredKeys->clear();
@ -248,14 +250,12 @@ void FileFilterIndex::resetFilters()
void FileFilterIndex::setUIModeFilters() void FileFilterIndex::setUIModeFilters()
{ {
if (!Settings::getInstance()->getBool("ForceDisableFilters")){ if (!Settings::getInstance()->getBool("ForceDisableFilters")){
if (UIModeController::getInstance()->isUIModeKiosk()) if (UIModeController::getInstance()->isUIModeKiosk()) {
{
filterByHidden = true; filterByHidden = true;
std::vector<std::string> val = { "FALSE" }; std::vector<std::string> val = { "FALSE" };
setFilter(HIDDEN_FILTER, &val); setFilter(HIDDEN_FILTER, &val);
} }
if (UIModeController::getInstance()->isUIModeKid()) if (UIModeController::getInstance()->isUIModeKid()) {
{
filterByKidGame = true; filterByKidGame = true;
std::vector<std::string> val = { "TRUE" }; std::vector<std::string> val = { "TRUE" };
setFilter(KIDGAME_FILTER, &val); setFilter(KIDGAME_FILTER, &val);
@ -291,79 +291,68 @@ void FileFilterIndex::debugPrintIndexes()
bool FileFilterIndex::showFile(FileData* game) bool FileFilterIndex::showFile(FileData* game)
{ {
// this shouldn't happen, but just in case let's get it out of the way // This shouldn't happen, but just in case let's get it out of the way.
if (!isFiltered()) if (!isFiltered())
return true; return true;
// if folder, needs further inspection - i.e. see if folder contains at least one element // If folder, needs further inspection - i.e. see if folder contains at least one element
// that should be shown // that should be shown.
if (game->getType() == FOLDER) { if (game->getType() == FOLDER) {
std::vector<FileData*> children = game->getChildren(); std::vector<FileData*> children = game->getChildren();
// iterate through all of the children, until there's a match // Iterate through all of the children, until there's a match.
for (std::vector<FileData*>::const_iterator it = children.cbegin();
for (std::vector<FileData*>::const_iterator it = children.cbegin(); it != children.cend(); ++it ) { it != children.cend(); ++it ) {
if (showFile(*it)) if (showFile(*it))
{
return true; return true;
} }
}
return false; return false;
} }
bool keepGoing = false; bool keepGoing = false;
for (std::vector<FilterDataDecl>::const_iterator it = filterDataDecl.cbegin(); it != filterDataDecl.cend(); ++it ) { for (std::vector<FilterDataDecl>::const_iterator it = filterDataDecl.cbegin();
it != filterDataDecl.cend(); ++it ) {
FilterDataDecl filterData = (*it); FilterDataDecl filterData = (*it);
if(*(filterData.filteredByRef)) if (*(filterData.filteredByRef)) {
{ // Try to find a match.
// try to find a match
std::string key = getIndexableKey(game, filterData.type, false); std::string key = getIndexableKey(game, filterData.type, false);
keepGoing = isKeyBeingFilteredBy(key, filterData.type); 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 we didn't find a match, try for secondary keys - i.e.
if (!keepGoing) // publisher and dev, or first genre.
{ if (!keepGoing) {
if (!filterData.hasSecondaryKey) if (!filterData.hasSecondaryKey)
{
return false; return false;
}
std::string secKey = getIndexableKey(game, filterData.type, true); std::string secKey = getIndexableKey(game, filterData.type, true);
if (secKey != UNKNOWN_LABEL) if (secKey != UNKNOWN_LABEL)
{
keepGoing = isKeyBeingFilteredBy(secKey, filterData.type); keepGoing = isKeyBeingFilteredBy(secKey, filterData.type);
} }
} // If still nothing, then it's not a match.
// if still nothing, then it's not a match
if (!keepGoing) if (!keepGoing)
return false; return false;
} }
} }
return keepGoing; return keepGoing;
} }
bool FileFilterIndex::isKeyBeingFilteredBy(std::string key, FilterIndexType type) 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 }; const FilterIndexType filterTypes[7] = { FAVORITES_FILTER, GENRE_FILTER,
std::vector<std::string> filterKeysList[7] = { favoritesIndexFilteredKeys, genreIndexFilteredKeys, playersIndexFilteredKeys, pubDevIndexFilteredKeys, ratingsIndexFilteredKeys, hiddenIndexFilteredKeys, kidGameIndexFilteredKeys }; PLAYER_FILTER, PUBDEV_FILTER, RATINGS_FILTER,HIDDEN_FILTER, KIDGAME_FILTER };
std::vector<std::string> filterKeysList[7] = { favoritesIndexFilteredKeys,
genreIndexFilteredKeys, playersIndexFilteredKeys, pubDevIndexFilteredKeys,
ratingsIndexFilteredKeys, hiddenIndexFilteredKeys, kidGameIndexFilteredKeys };
for (int i = 0; i < 7; i++) for (int i = 0; i < 7; i++) {
{ if (filterTypes[i] == type) {
if (filterTypes[i] == type) for (std::vector<std::string>::const_iterator it = filterKeysList[i].cbegin();
{ it != filterKeysList[i].cend(); ++it ) {
for (std::vector<std::string>::const_iterator it = filterKeysList[i].cbegin(); it != filterKeysList[i].cend(); ++it )
{
if (key == (*it)) if (key == (*it))
{
return true; return true;
} }
}
return false; return false;
} }
} }
return false; return false;
} }
@ -372,35 +361,31 @@ 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 // Flag for including unknowns.
bool includeUnknown = INCLUDE_UNKNOWN; bool includeUnknown = INCLUDE_UNKNOWN;
// only add unknown in pubdev IF both dev and pub are empty // Only add unknown in pubdev IF both dev and pub are empty.
if (!includeUnknown && (key == UNKNOWN_LABEL || key == "BIOS")) { if (!includeUnknown && (key == UNKNOWN_LABEL || key == "BIOS"))
// no valid genre info found // No valid genre info found.
return; return;
}
manageIndexEntry(&genreIndexAllKeys, key, remove); manageIndexEntry(&genreIndexAllKeys, key, remove);
key = getIndexableKey(game, GENRE_FILTER, true); key = getIndexableKey(game, GENRE_FILTER, true);
if (!includeUnknown && key == UNKNOWN_LABEL) if (!includeUnknown && key == UNKNOWN_LABEL)
{
manageIndexEntry(&genreIndexAllKeys, key, remove); manageIndexEntry(&genreIndexAllKeys, key, remove);
} }
}
void FileFilterIndex::managePlayerEntryInIndex(FileData* game, bool remove) void FileFilterIndex::managePlayerEntryInIndex(FileData* game, bool remove)
{ {
// flag for including unknowns // Flag for including unknowns.
bool includeUnknown = INCLUDE_UNKNOWN; bool includeUnknown = INCLUDE_UNKNOWN;
std::string key = getIndexableKey(game, PLAYER_FILTER, false); std::string key = getIndexableKey(game, PLAYER_FILTER, false);
// only add unknown in pubdev IF both dev and pub are empty // Only add unknown in pubdev IF both dev and pub are empty.
if (!includeUnknown && key == UNKNOWN_LABEL) { if (!includeUnknown && key == UNKNOWN_LABEL)
// no valid player info found // No valid player info found.
return; return;
}
manageIndexEntry(&playersIndexAllKeys, key, remove); manageIndexEntry(&playersIndexAllKeys, key, remove);
} }
@ -410,35 +395,32 @@ void FileFilterIndex::managePubDevEntryInIndex(FileData* game, bool remove)
std::string pub = getIndexableKey(game, PUBDEV_FILTER, false); std::string pub = getIndexableKey(game, PUBDEV_FILTER, false);
std::string dev = getIndexableKey(game, PUBDEV_FILTER, true); std::string dev = getIndexableKey(game, PUBDEV_FILTER, true);
// flag for including unknowns // Flag for including unknowns.
bool includeUnknown = INCLUDE_UNKNOWN; bool includeUnknown = INCLUDE_UNKNOWN;
bool unknownPub = false; bool unknownPub = false;
bool unknownDev = false; bool unknownDev = false;
if (pub == UNKNOWN_LABEL) { if (pub == UNKNOWN_LABEL)
unknownPub = true; unknownPub = true;
}
if (dev == UNKNOWN_LABEL) {
unknownDev = true;
}
if (!includeUnknown && unknownDev && unknownPub) { if (dev == UNKNOWN_LABEL)
// no valid rating info found unknownDev = true;
if (!includeUnknown && unknownDev && unknownPub)
// No valid rating info found.
return; return;
}
if (unknownDev && unknownPub) { if (unknownDev && unknownPub) {
// if no info at all // If no info at all.
manageIndexEntry(&pubDevIndexAllKeys, pub, remove); manageIndexEntry(&pubDevIndexAllKeys, pub, remove);
} }
else else {
{
if (!unknownDev) { if (!unknownDev) {
// if no info at all // If no info at all.
manageIndexEntry(&pubDevIndexAllKeys, dev, remove); manageIndexEntry(&pubDevIndexAllKeys, dev, remove);
} }
if (!unknownPub) { if (!unknownPub) {
// if no info at all // If no info at all.
manageIndexEntry(&pubDevIndexAllKeys, pub, remove); manageIndexEntry(&pubDevIndexAllKeys, pub, remove);
} }
} }
@ -448,88 +430,83 @@ 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 // Flag for including unknowns.
bool includeUnknown = INCLUDE_UNKNOWN; bool includeUnknown = INCLUDE_UNKNOWN;
if (!includeUnknown && key == UNKNOWN_LABEL) { if (!includeUnknown && key == UNKNOWN_LABEL)
// no valid rating info found // No valid rating info found.
return; return;
}
manageIndexEntry(&ratingsIndexAllKeys, key, remove); manageIndexEntry(&ratingsIndexAllKeys, key, remove);
} }
void FileFilterIndex::manageFavoritesEntryInIndex(FileData* game, bool remove) void FileFilterIndex::manageFavoritesEntryInIndex(FileData* game, bool remove)
{ {
// flag for including unknowns // Flag for including unknowns.
bool includeUnknown = INCLUDE_UNKNOWN; bool includeUnknown = INCLUDE_UNKNOWN;
std::string key = getIndexableKey(game, FAVORITES_FILTER, false); std::string key = getIndexableKey(game, FAVORITES_FILTER, false);
if (!includeUnknown && key == UNKNOWN_LABEL) {
// no valid favorites info found if (!includeUnknown && key == UNKNOWN_LABEL)
// No valid favorites info found.
return; return;
}
manageIndexEntry(&favoritesIndexAllKeys, key, remove); manageIndexEntry(&favoritesIndexAllKeys, key, remove);
} }
void FileFilterIndex::manageHiddenEntryInIndex(FileData* game, bool remove) void FileFilterIndex::manageHiddenEntryInIndex(FileData* game, bool remove)
{ {
// flag for including unknowns // Flag for including unknowns.
bool includeUnknown = INCLUDE_UNKNOWN; bool includeUnknown = INCLUDE_UNKNOWN;
std::string key = getIndexableKey(game, HIDDEN_FILTER, false); std::string key = getIndexableKey(game, HIDDEN_FILTER, false);
if (!includeUnknown && key == UNKNOWN_LABEL) {
// no valid hidden info found if (!includeUnknown && key == UNKNOWN_LABEL)
// No valid hidden info found.
return; return;
}
manageIndexEntry(&hiddenIndexAllKeys, key, remove); manageIndexEntry(&hiddenIndexAllKeys, key, remove);
} }
void FileFilterIndex::manageKidGameEntryInIndex(FileData* game, bool remove) void FileFilterIndex::manageKidGameEntryInIndex(FileData* game, bool remove)
{ {
// flag for including unknowns // Flag for including unknowns.
bool includeUnknown = INCLUDE_UNKNOWN; bool includeUnknown = INCLUDE_UNKNOWN;
std::string key = getIndexableKey(game, KIDGAME_FILTER, false); std::string key = getIndexableKey(game, KIDGAME_FILTER, false);
if (!includeUnknown && key == UNKNOWN_LABEL) {
// no valid kidgame info found if (!includeUnknown && key == UNKNOWN_LABEL)
// No valid kidgame info found.
return; return;
}
manageIndexEntry(&kidGameIndexAllKeys, key, remove); manageIndexEntry(&kidGameIndexAllKeys, key, remove);
} }
void FileFilterIndex::manageIndexEntry(std::map<std::string, int>* index, std::string key, bool remove) { void FileFilterIndex::manageIndexEntry(std::map<std::string, int>* index,
std::string key, bool remove)
{
bool includeUnknown = INCLUDE_UNKNOWN; bool includeUnknown = INCLUDE_UNKNOWN;
if (!includeUnknown && key == UNKNOWN_LABEL) if (!includeUnknown && key == UNKNOWN_LABEL)
return; return;
if (remove) { if (remove) {
// removing entry // Removing entry.
if (index->find(key) == index->cend()) if (index->find(key) == index->cend()) {
{ // This shouldn't happen.
// this shouldn't happen
LOG(LogInfo) << "Couldn't find entry in index! " << key; LOG(LogInfo) << "Couldn't find entry in index! " << key;
} }
else else {
{
(index->at(key))--; (index->at(key))--;
if (index->at(key) <= 0) { if (index->at(key) <= 0) {
index->erase(key); index->erase(key);
} }
} }
} }
else else {
{ // Adding entry.
// adding entry
if (index->find(key) == index->cend()) if (index->find(key) == index->cend())
{
(*index)[key] = 1; (*index)[key] = 1;
}
else else
{
(index->at(key))++; (index->at(key))++;
} }
} }
}
void FileFilterIndex::clearIndex(std::map<std::string, int> indexMap) void FileFilterIndex::clearIndex(std::map<std::string, int> indexMap)
{ {

View file

@ -1,3 +1,9 @@
//
// FileFilterIndex.h
//
// Gamelist filters.
//
#pragma once #pragma once
#ifndef ES_APP_FILE_FILTER_INDEX_H #ifndef ES_APP_FILE_FILTER_INDEX_H
#define ES_APP_FILE_FILTER_INDEX_H #define ES_APP_FILE_FILTER_INDEX_H
@ -7,8 +13,7 @@
class FileData; class FileData;
enum FilterIndexType enum FilterIndexType {
{
NONE, NONE,
GENRE_FILTER, GENRE_FILTER,
PLAYER_FILTER, PLAYER_FILTER,
@ -19,16 +24,15 @@ enum FilterIndexType
KIDGAME_FILTER KIDGAME_FILTER
}; };
struct FilterDataDecl struct FilterDataDecl {
{ FilterIndexType type; // Type of filter.
FilterIndexType type; // type of filter std::map<std::string, int>* allIndexKeys; // All possible filters for this type.
std::map<std::string, int>* allIndexKeys; // all possible filters for this type bool* filteredByRef; // Is it filtered by this type?
bool* filteredByRef; // is it filtered by this type std::vector<std::string>* currentFilteredKeys; // Current keys being filtered for.
std::vector<std::string>* currentFilteredKeys; // current keys being filtered for std::string primaryKey; // Primary key in metadata.
std::string primaryKey; // primary key in metadata bool hasSecondaryKey; // Has secondary key for comparison.
bool hasSecondaryKey; // has secondary key for comparison std::string secondaryKey; // What's the secondary key.
std::string secondaryKey; // what's the secondary key std::string menuLabel; // Text to show in menu.
std::string menuLabel; // text to show in menu
}; };
class FileFilterIndex class FileFilterIndex
@ -42,7 +46,8 @@ public:
void clearAllFilters(); void clearAllFilters();
void debugPrintIndexes(); void debugPrintIndexes();
bool showFile(FileData* game); bool showFile(FileData* game);
bool isFiltered() { return (filterByGenre || filterByPlayers || filterByPubDev || filterByRatings || filterByFavorites || filterByHidden || filterByKidGame); }; bool isFiltered() { return (filterByGenre || filterByPlayers || filterByPubDev ||
filterByRatings || filterByFavorites || filterByHidden || filterByKidGame); };
bool isKeyBeingFilteredBy(std::string key, FilterIndexType type); bool isKeyBeingFilteredBy(std::string key, FilterIndexType type);
std::vector<FilterDataDecl>& getFilterDataDecls(); std::vector<FilterDataDecl>& getFilterDataDecls();

View file

@ -1,3 +1,10 @@
//
// FileSorts.cpp
//
// Gamelist sorting functions.
// Actual sorting takes place in FileData.
//
#include "FileSorts.h" #include "FileSorts.h"
#include "utils/StringUtil.h" #include "utils/StringUtil.h"
@ -36,12 +43,14 @@ namespace FileSorts
FileData::SortType(&compareSystem, false, "system, descending") FileData::SortType(&compareSystem, false, "system, descending")
}; };
const std::vector<FileData::SortType> SortTypes(typesArr, typesArr + sizeof(typesArr)/sizeof(typesArr[0])); const std::vector<FileData::SortType> SortTypes(typesArr, typesArr +
sizeof(typesArr)/sizeof(typesArr[0]));
//returns if file1 should come before file2 // Returns if file1 should come before file2.
bool compareName(const FileData* file1, const FileData* 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 // 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 name1 = Utils::String::toUpper(file1->metadata.get("sortname"));
std::string name2 = Utils::String::toUpper(file2->metadata.get("sortname")); std::string name2 = Utils::String::toUpper(file2->metadata.get("sortname"));
if(name1.empty()){ if(name1.empty()){
@ -62,17 +71,15 @@ namespace FileSorts
{ {
//only games have playcount metadata //only games have playcount metadata
if(file1->metadata.getType() == GAME_METADATA && file2->metadata.getType() == GAME_METADATA) if(file1->metadata.getType() == GAME_METADATA && file2->metadata.getType() == GAME_METADATA)
{
return (file1)->metadata.getInt("playcount") < (file2)->metadata.getInt("playcount"); return (file1)->metadata.getInt("playcount") < (file2)->metadata.getInt("playcount");
}
return false; return false;
} }
bool compareLastPlayed(const FileData* file1, const FileData* file2) bool compareLastPlayed(const FileData* file1, const FileData* file2)
{ {
// since it's stored as an ISO string (YYYYMMDDTHHMMSS), we can compare as a string // 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 // which is a lot faster than the time casts and the time comparisons.
return (file1)->metadata.get("lastplayed") < (file2)->metadata.get("lastplayed"); return (file1)->metadata.get("lastplayed") < (file2)->metadata.get("lastplayed");
} }
@ -83,8 +90,8 @@ namespace FileSorts
bool compareReleaseDate(const FileData* file1, const FileData* file2) bool compareReleaseDate(const FileData* file1, const FileData* file2)
{ {
// since it's stored as an ISO string (YYYYMMDDTHHMMSS), we can compare as a string // 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 // which is a lot faster than the time casts and the time comparisons.
return (file1)->metadata.get("releasedate") < (file2)->metadata.get("releasedate"); return (file1)->metadata.get("releasedate") < (file2)->metadata.get("releasedate");
} }

View file

@ -1,3 +1,10 @@
//
// FileSorts.h
//
// Gamelist sorting functions.
// Actual sorting takes place in FileData.
//
#pragma once #pragma once
#ifndef ES_APP_FILE_SORTS_H #ifndef ES_APP_FILE_SORTS_H
#define ES_APP_FILE_SORTS_H #define ES_APP_FILE_SORTS_H

View file

@ -88,7 +88,6 @@ MetaDataList MetaDataList::createFromXML(MetaDataListType type,
mdl.set(iter->key, iter->defaultValue); mdl.set(iter->key, iter->defaultValue);
} }
} }
return mdl; return mdl;
} }

View file

@ -71,8 +71,6 @@ public:
// An example will be written if the file doesn't exist. // An example will be written if the file doesn't exist.
static bool loadConfig(); static bool loadConfig();
static void writeExampleConfig(const std::string& path); 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::string getConfigPath(bool forWrite);
static std::vector<SystemData*> sSystemVector; static std::vector<SystemData*> sSystemVector;

View file

@ -1,3 +1,10 @@
//
// GuiCollectionSystemsOptions.cpp
//
// User interface for the game collection settings.
// Submenu to the GuiMenu main menu.
//
#include "guis/GuiCollectionSystemsOptions.h" #include "guis/GuiCollectionSystemsOptions.h"
#include "components/OptionListComponent.h" #include "components/OptionListComponent.h"
@ -10,7 +17,8 @@
#include "SystemData.h" #include "SystemData.h"
#include "Window.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();
} }
@ -19,15 +27,13 @@ void GuiCollectionSystemsOptions::initializeMenu()
{ {
addChild(&mMenu); addChild(&mMenu);
// get collections // Get collections.
addSystemsToMenu(); addSystemsToMenu();
// add "Create New Custom Collection from Theme" // Add "Create New Custom Collection from Theme".
std::vector<std::string> unusedFolders =
std::vector<std::string> unusedFolders = CollectionSystemManager::get()->getUnusedSystemsFromTheme(); CollectionSystemManager::get()->getUnusedSystemsFromTheme();
if (unusedFolders.size() > 0) if (unusedFolders.size() > 0) {
{
addEntry("CREATE NEW CUSTOM COLLECTION FROM THEME", 0x777777FF, true, addEntry("CREATE NEW CUSTOM COLLECTION FROM THEME", 0x777777FF, true,
[this, unusedFolders] { [this, unusedFolders] {
auto s = new GuiSettings(mWindow, "SELECT THEME FOLDER"); auto s = new GuiSettings(mWindow, "SELECT THEME FOLDER");
@ -35,9 +41,8 @@ void GuiCollectionSystemsOptions::initializeMenu()
folderThemes = std::make_shared< OptionListComponent<std::string>> folderThemes = std::make_shared< OptionListComponent<std::string>>
(mWindow, getHelpStyle(), "SELECT THEME FOLDER", true); (mWindow, getHelpStyle(), "SELECT THEME FOLDER", true);
// add Custom Systems // Add custom systems.
for(auto it = unusedFolders.cbegin() ; it != unusedFolders.cend() ; it++ ) for(auto it = unusedFolders.cbegin() ; it != unusedFolders.cend() ; it++ ) {
{
ComponentListRow row; ComponentListRow row;
std::string name = *it; std::string name = *it;
@ -46,7 +51,8 @@ void GuiCollectionSystemsOptions::initializeMenu()
}; };
row.makeAcceptInputHandler(createCollectionCall); row.makeAcceptInputHandler(createCollectionCall);
auto themeFolder = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF); auto themeFolder = std::make_shared<TextComponent>(mWindow,
Utils::String::toUpper(name), Font::get(FONT_SIZE_SMALL), 0x777777FF);
row.addElement(themeFolder, true); row.addElement(themeFolder, true);
s->addRow(row); s->addRow(row);
} }
@ -55,21 +61,24 @@ void GuiCollectionSystemsOptions::initializeMenu()
} }
ComponentListRow row; ComponentListRow row;
row.addElement(std::make_shared<TextComponent>(mWindow, "CREATE NEW CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); row.addElement(std::make_shared<TextComponent>(mWindow,
"CREATE NEW CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
auto bracket = std::make_shared<ImageComponent>(mWindow); auto bracket = std::make_shared<ImageComponent>(mWindow);
bracket->setImage(":/graphics/arrow.svg"); bracket->setImage(":/graphics/arrow.svg");
bracket->setResize(Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight())); bracket->setResize(Vector2f(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()));
row.addElement(bracket, false); row.addElement(bracket, false);
auto createCustomCollection = [this](const std::string& newVal) { auto createCustomCollection = [this](const std::string& newVal) {
std::string name = newVal; std::string name = newVal;
// we need to store the first Gui and remove it, as it'll be deleted by the actual Gui // We need to store the first GUI and remove it, as it'll
// be deleted by the actual GUI.
Window* window = mWindow; Window* window = mWindow;
GuiComponent* topGui = window->peekGui(); GuiComponent* topGui = window->peekGui();
window->removeGui(topGui); window->removeGui(topGui);
createCollection(name); createCollection(name);
}; };
row.makeAcceptInputHandler([this, createCustomCollection] { row.makeAcceptInputHandler([this, createCustomCollection] {
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "New Collection Name", "", createCustomCollection, false, "SAVE")); mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(),
"New Collection Name", "", createCustomCollection, false, "SAVE"));
}); });
mMenu.addRow(row); mMenu.addRow(row);
@ -79,42 +88,44 @@ void GuiCollectionSystemsOptions::initializeMenu()
mMenu.addWithLabel("SORT FAVORITES ON TOP FOR CUSTOM COLLECTIONS", sortFavFirstCustomSwitch); mMenu.addWithLabel("SORT FAVORITES ON TOP FOR CUSTOM COLLECTIONS", sortFavFirstCustomSwitch);
bundleCustomCollections = std::make_shared<SwitchComponent>(mWindow); bundleCustomCollections = std::make_shared<SwitchComponent>(mWindow);
bundleCustomCollections->setState(Settings::getInstance()->getBool("UseCustomCollectionsSystem")); bundleCustomCollections->setState(Settings::getInstance()->
getBool("UseCustomCollectionsSystem"));
mMenu.addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", bundleCustomCollections); mMenu.addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", bundleCustomCollections);
toggleSystemNameInCollections = std::make_shared<SwitchComponent>(mWindow); toggleSystemNameInCollections = std::make_shared<SwitchComponent>(mWindow);
toggleSystemNameInCollections->setState(Settings::getInstance()->getBool("CollectionShowSystemInfo")); toggleSystemNameInCollections->setState(Settings::getInstance()->
getBool("CollectionShowSystemInfo"));
mMenu.addWithLabel("SHOW SYSTEM NAMES IN COLLECTIONS", toggleSystemNameInCollections); mMenu.addWithLabel("SHOW SYSTEM NAMES IN COLLECTIONS", toggleSystemNameInCollections);
if(CollectionSystemManager::get()->isEditing()) if(CollectionSystemManager::get()->isEditing()) {
{
row.elements.clear(); row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow, "FINISH EDITING '" + Utils::String::toUpper(CollectionSystemManager::get()->getEditingCollection()) + "' COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); row.addElement(std::make_shared<TextComponent>(mWindow, "FINISH EDITING '" +
Utils::String::toUpper(CollectionSystemManager::get()->getEditingCollection()) +
"' COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
row.makeAcceptInputHandler(std::bind(&GuiCollectionSystemsOptions::exitEditMode, this)); row.makeAcceptInputHandler(std::bind(&GuiCollectionSystemsOptions::exitEditMode, this));
mMenu.addRow(row); mMenu.addRow(row);
} }
mMenu.addButton("BACK", "back", std::bind(&GuiCollectionSystemsOptions::applySettings, this)); mMenu.addButton("BACK", "back", std::bind(&GuiCollectionSystemsOptions::applySettings, this));
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2,
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, Renderer::getScreenHeight() * 0.15f); Renderer::getScreenHeight() * 0.15f);
} }
void GuiCollectionSystemsOptions::addEntry(const char* name, unsigned int color, bool add_arrow, const std::function<void()>& func) void GuiCollectionSystemsOptions::addEntry(const char* name, unsigned int color,
bool add_arrow, const std::function<void()>& func)
{ {
std::shared_ptr<Font> font = Font::get(FONT_SIZE_MEDIUM); std::shared_ptr<Font> font = Font::get(FONT_SIZE_MEDIUM);
// populate the list // Populate the list.
ComponentListRow row; ComponentListRow row;
row.addElement(std::make_shared<TextComponent>(mWindow, name, font, color), true); row.addElement(std::make_shared<TextComponent>(mWindow, name, font, color), true);
if(add_arrow) if(add_arrow) {
{
std::shared_ptr<ImageComponent> bracket = makeArrow(mWindow); std::shared_ptr<ImageComponent> bracket = makeArrow(mWindow);
row.addElement(bracket, false); row.addElement(bracket, false);
} }
row.makeAcceptInputHandler(func); row.makeAcceptInputHandler(func);
mMenu.addRow(row); mMenu.addRow(row);
} }
@ -122,8 +133,10 @@ void GuiCollectionSystemsOptions::createCollection(std::string inName) {
std::string name = CollectionSystemManager::get()->getValidNewCollectionName(inName); std::string name = CollectionSystemManager::get()->getValidNewCollectionName(inName);
SystemData* newSys = CollectionSystemManager::get()->addNewCustomCollection(name); SystemData* newSys = CollectionSystemManager::get()->addNewCustomCollection(name);
customOptionList->add(name, name, true); customOptionList->add(name, name, true);
std::string outAuto = Utils::String::vectorToCommaString(autoOptionList->getSelectedObjects()); std::string outAuto = Utils::String::vectorToCommaString(
std::string outCustom = Utils::String::vectorToCommaString(customOptionList->getSelectedObjects()); autoOptionList->getSelectedObjects());
std::string outCustom = Utils::String::vectorToCommaString(
customOptionList->getSelectedObjects());
updateSettings(outAuto, outCustom); updateSettings(outAuto, outCustom);
ViewController::get()->goToSystemView(newSys); ViewController::get()->goToSystemView(newSys);
@ -142,40 +155,42 @@ void GuiCollectionSystemsOptions::exitEditMode()
GuiCollectionSystemsOptions::~GuiCollectionSystemsOptions() GuiCollectionSystemsOptions::~GuiCollectionSystemsOptions()
{ {
} }
void GuiCollectionSystemsOptions::addSystemsToMenu() void GuiCollectionSystemsOptions::addSystemsToMenu()
{ {
std::map<std::string, CollectionSystemData> autoSystems =
CollectionSystemManager::get()->getAutoCollectionSystems();
std::map<std::string, CollectionSystemData> autoSystems = CollectionSystemManager::get()->getAutoCollectionSystems(); autoOptionList = std::make_shared<OptionListComponent<std::string>>
(mWindow, getHelpStyle(), "SELECT COLLECTIONS", true);
autoOptionList = std::make_shared< OptionListComponent<std::string> >(mWindow, getHelpStyle(), "SELECT COLLECTIONS", true); // Add automatic systems.
for(std::map<std::string, CollectionSystemData>::const_iterator it = autoSystems.cbegin();
// add Auto Systems it != autoSystems.cend() ; it++ )
for(std::map<std::string, CollectionSystemData>::const_iterator it = autoSystems.cbegin() ; it != autoSystems.cend() ; it++ )
{
autoOptionList->add(it->second.decl.longName, it->second.decl.name, it->second.isEnabled); autoOptionList->add(it->second.decl.longName, it->second.decl.name, it->second.isEnabled);
}
mMenu.addWithLabel("AUTOMATIC GAME COLLECTIONS", autoOptionList); mMenu.addWithLabel("AUTOMATIC GAME COLLECTIONS", autoOptionList);
std::map<std::string, CollectionSystemData> customSystems = CollectionSystemManager::get()->getCustomCollectionSystems(); std::map<std::string, CollectionSystemData> customSystems =
CollectionSystemManager::get()->getCustomCollectionSystems();
customOptionList = std::make_shared< OptionListComponent<std::string> >(mWindow, getHelpStyle(), "SELECT COLLECTIONS", true); customOptionList = std::make_shared<OptionListComponent<std::string>>
(mWindow, getHelpStyle(), "SELECT COLLECTIONS", true);
// add Custom Systems // Add custom systems.
for(std::map<std::string, CollectionSystemData>::const_iterator it = customSystems.cbegin() ; it != customSystems.cend() ; it++ ) for(std::map<std::string, CollectionSystemData>::const_iterator it = customSystems.cbegin();
{ it != customSystems.cend() ; it++ )
customOptionList->add(it->second.decl.longName, it->second.decl.name, it->second.isEnabled); customOptionList->add(it->second.decl.longName, it->second.decl.name, it->second.isEnabled);
}
mMenu.addWithLabel("CUSTOM GAME COLLECTIONS", customOptionList); mMenu.addWithLabel("CUSTOM GAME COLLECTIONS", customOptionList);
} }
void GuiCollectionSystemsOptions::applySettings() void GuiCollectionSystemsOptions::applySettings()
{ {
std::string outAuto = Utils::String::vectorToCommaString(autoOptionList->getSelectedObjects()); std::string outAuto = Utils::String::vectorToCommaString(
autoOptionList->getSelectedObjects());
std::string prevAuto = Settings::getInstance()->getString("CollectionSystemsAuto"); std::string prevAuto = Settings::getInstance()->getString("CollectionSystemsAuto");
std::string outCustom = Utils::String::vectorToCommaString(customOptionList->getSelectedObjects()); std::string outCustom = Utils::String::vectorToCommaString(
customOptionList->getSelectedObjects());
std::string prevCustom = Settings::getInstance()->getString("CollectionSystemsCustom"); std::string prevCustom = Settings::getInstance()->getString("CollectionSystemsCustom");
bool outSort = sortFavFirstCustomSwitch->getState(); bool outSort = sortFavFirstCustomSwitch->getState();
bool prevSort = Settings::getInstance()->getBool("FavFirstCustom"); bool prevSort = Settings::getInstance()->getBool("FavFirstCustom");
@ -183,22 +198,25 @@ void GuiCollectionSystemsOptions::applySettings()
bool prevBundle = Settings::getInstance()->getBool("UseCustomCollectionsSystem"); bool prevBundle = Settings::getInstance()->getBool("UseCustomCollectionsSystem");
bool prevShow = Settings::getInstance()->getBool("CollectionShowSystemInfo"); bool prevShow = Settings::getInstance()->getBool("CollectionShowSystemInfo");
bool outShow = toggleSystemNameInCollections->getState(); bool outShow = toggleSystemNameInCollections->getState();
bool needUpdateSettings = prevAuto != outAuto || prevCustom != outCustom || outSort != prevSort || outBundle != prevBundle || prevShow != outShow ; bool needUpdateSettings = prevAuto != outAuto || prevCustom != outCustom || outSort !=
prevSort || outBundle != prevBundle || prevShow != outShow ;
if (needUpdateSettings) if (needUpdateSettings)
{
updateSettings(outAuto, outCustom); updateSettings(outAuto, outCustom);
}
delete this; 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("CollectionSystemsAuto", newAutoSettings);
Settings::getInstance()->setString("CollectionSystemsCustom", newCustomSettings); Settings::getInstance()->setString("CollectionSystemsCustom", newCustomSettings);
Settings::getInstance()->setBool("FavFirstCustom", sortFavFirstCustomSwitch->getState()); Settings::getInstance()->setBool("FavFirstCustom", sortFavFirstCustomSwitch->getState());
Settings::getInstance()->setBool("UseCustomCollectionsSystem", bundleCustomCollections->getState()); Settings::getInstance()->setBool("UseCustomCollectionsSystem",
Settings::getInstance()->setBool("CollectionShowSystemInfo", toggleSystemNameInCollections->getState()); bundleCustomCollections->getState());
Settings::getInstance()->setBool("CollectionShowSystemInfo",
toggleSystemNameInCollections->getState());
Settings::getInstance()->saveFile(); Settings::getInstance()->saveFile();
CollectionSystemManager::get()->loadEnabledListFromSettings(); CollectionSystemManager::get()->loadEnabledListFromSettings();
CollectionSystemManager::get()->updateSystemsList(); CollectionSystemManager::get()->updateSystemsList();
@ -209,14 +227,12 @@ void GuiCollectionSystemsOptions::updateSettings(std::string newAutoSettings, st
bool GuiCollectionSystemsOptions::input(InputConfig* config, Input input) bool GuiCollectionSystemsOptions::input(InputConfig* config, Input input)
{ {
bool consumed = GuiComponent::input(config, input); bool consumed = GuiComponent::input(config, input);
if(consumed) if(consumed)
return true; return true;
if(config->isMappedTo("b", input) && input.value != 0) if(config->isMappedTo("b", input) && input.value != 0)
{
applySettings(); applySettings();
}
return false; return false;
} }

View file

@ -1,3 +1,10 @@
//
// GuiCollectionSystemsOptions.h
//
// User interface for the game collection settings.
// Submenu to the GuiMenu main menu.
//
#pragma once #pragma once
#ifndef ES_APP_GUIS_GUI_COLLECTION_SYSTEM_OPTIONS_H #ifndef ES_APP_GUIS_GUI_COLLECTION_SYSTEM_OPTIONS_H
#define ES_APP_GUIS_GUI_COLLECTION_SYSTEM_OPTIONS_H #define ES_APP_GUIS_GUI_COLLECTION_SYSTEM_OPTIONS_H
@ -23,7 +30,8 @@ private:
void initializeMenu(); void initializeMenu();
void applySettings(); void applySettings();
void addSystemsToMenu(); void addSystemsToMenu();
void addEntry(const char* name, unsigned int color, bool add_arrow, const std::function<void()>& func); void addEntry(const char* name, unsigned int color,
bool add_arrow, const std::function<void()>& func);
void updateSettings(std::string newAutoSettings, std::string newCustomSettings); void updateSettings(std::string newAutoSettings, std::string newCustomSettings);
void createCollection(std::string inName); void createCollection(std::string inName);
void exitEditMode(); void exitEditMode();

View file

@ -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 "guis/GuiGeneralScreensaverOptions.h"
#include "components/OptionListComponent.h" #include "components/OptionListComponent.h"
@ -8,57 +16,72 @@
#include "guis/GuiVideoScreensaverOptions.h" #include "guis/GuiVideoScreensaverOptions.h"
#include "Settings.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 // Screensaver time.
auto screensaver_time = std::make_shared<SliderComponent>(mWindow, 0.f, 30.f, 1.f, "m"); auto screensaver_time = std::make_shared<SliderComponent>(mWindow, 0.f, 30.f, 1.f, "m");
screensaver_time->setValue((float)(Settings::getInstance()->getInt("ScreenSaverTime") / (1000 * 60))); screensaver_time->setValue((float)(Settings::getInstance()->
getInt("ScreenSaverTime") / (1000 * 60)));
addWithLabel("SCREENSAVER AFTER", screensaver_time); addWithLabel("SCREENSAVER AFTER", screensaver_time);
addSaveFunc([screensaver_time] { addSaveFunc([screensaver_time] {
Settings::getInstance()->setInt("ScreenSaverTime", (int)Math::round(screensaver_time->getValue()) * (1000 * 60)); Settings::getInstance()->setInt("ScreenSaverTime",
(int)Math::round(screensaver_time->getValue()) * (1000 * 60));
PowerSaver::updateTimeouts(); PowerSaver::updateTimeouts();
}); });
// Allow ScreenSaver Controls - ScreenSaverControls // Allow ScreenSaver Controls - ScreenSaverControls.
auto ss_controls = std::make_shared<SwitchComponent>(mWindow); auto ss_controls = std::make_shared<SwitchComponent>(mWindow);
ss_controls->setState(Settings::getInstance()->getBool("ScreenSaverControls")); ss_controls->setState(Settings::getInstance()->getBool("ScreenSaverControls"));
addWithLabel("SCREENSAVER CONTROLS", ss_controls); addWithLabel("SCREENSAVER CONTROLS", ss_controls);
addSaveFunc([ss_controls] { Settings::getInstance()->setBool("ScreenSaverControls", ss_controls->getState()); }); addSaveFunc([ss_controls] { Settings::getInstance()->setBool("ScreenSaverControls",
ss_controls->getState()); });
// screensaver behavior // Screensaver behavior.
auto screensaver_behavior = std::make_shared< OptionListComponent<std::string> >(mWindow, getHelpStyle(), "SCREENSAVER BEHAVIOR", false); auto screensaver_behavior = std::make_shared<OptionListComponent<std::string>>
(mWindow, getHelpStyle(), "SCREENSAVER BEHAVIOR", false);
std::vector<std::string> screensavers; std::vector<std::string> screensavers;
screensavers.push_back("dim"); screensavers.push_back("dim");
screensavers.push_back("black"); screensavers.push_back("black");
screensavers.push_back("random video"); screensavers.push_back("random video");
screensavers.push_back("slideshow"); screensavers.push_back("slideshow");
for(auto it = screensavers.cbegin(); it != screensavers.cend(); it++) for(auto it = screensavers.cbegin(); it != screensavers.cend(); it++)
screensaver_behavior->add(*it, *it, Settings::getInstance()->getString("ScreenSaverBehavior") == *it); screensaver_behavior->add(*it, *it, Settings::getInstance()->
getString("ScreenSaverBehavior") == *it);
addWithLabel("SCREENSAVER BEHAVIOR", screensaver_behavior); addWithLabel("SCREENSAVER BEHAVIOR", screensaver_behavior);
addSaveFunc([this, screensaver_behavior] { addSaveFunc([this, screensaver_behavior] {
if (Settings::getInstance()->getString("ScreenSaverBehavior") != "random video" && screensaver_behavior->getSelected() == "random video") { if (Settings::getInstance()->getString("ScreenSaverBehavior") !=
// if before it wasn't risky but now there's a risk of problems, show warning "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(), 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.", "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; })); "OK", [] { return; }));
} }
Settings::getInstance()->setString("ScreenSaverBehavior", screensaver_behavior->getSelected()); Settings::getInstance()->setString("ScreenSaverBehavior",
screensaver_behavior->getSelected());
PowerSaver::updateTimeouts(); PowerSaver::updateTimeouts();
}); });
ComponentListRow row; ComponentListRow row;
// show filtered menu // Show filtered menu.
row.elements.clear(); row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow, "VIDEO SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); row.addElement(std::make_shared<TextComponent>(mWindow,
"VIDEO SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
row.addElement(makeArrow(mWindow), false); row.addElement(makeArrow(mWindow), false);
row.makeAcceptInputHandler(std::bind(&GuiGeneralScreensaverOptions::openVideoScreensaverOptions, this)); row.makeAcceptInputHandler(std::bind(
&GuiGeneralScreensaverOptions::openVideoScreensaverOptions, this));
addRow(row); addRow(row);
row.elements.clear(); row.elements.clear();
row.addElement(std::make_shared<TextComponent>(mWindow, "SLIDESHOW SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true); row.addElement(std::make_shared<TextComponent>(mWindow,
"SLIDESHOW SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
row.addElement(makeArrow(mWindow), false); row.addElement(makeArrow(mWindow), false);
row.makeAcceptInputHandler(std::bind(&GuiGeneralScreensaverOptions::openSlideshowScreensaverOptions, this)); row.makeAcceptInputHandler(std::bind(
&GuiGeneralScreensaverOptions::openSlideshowScreensaverOptions, this));
addRow(row); addRow(row);
} }
@ -73,4 +96,3 @@ void GuiGeneralScreensaverOptions::openVideoScreensaverOptions() {
void GuiGeneralScreensaverOptions::openSlideshowScreensaverOptions() { void GuiGeneralScreensaverOptions::openSlideshowScreensaverOptions() {
mWindow->pushGui(new GuiSlideshowScreensaverOptions(mWindow, "SLIDESHOW SCREENSAVER")); mWindow->pushGui(new GuiSlideshowScreensaverOptions(mWindow, "SLIDESHOW SCREENSAVER"));
} }

View file

@ -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 #pragma once
#ifndef ES_APP_GUIS_GUI_GENERAL_SCREENSAVER_OPTIONS_H #ifndef ES_APP_GUIS_GUI_GENERAL_SCREENSAVER_OPTIONS_H
#define ES_APP_GUIS_GUI_GENERAL_SCREENSAVER_OPTIONS_H #define ES_APP_GUIS_GUI_GENERAL_SCREENSAVER_OPTIONS_H

View file

@ -1,3 +1,9 @@
//
// GuiInfoPopup.cpp
//
// Popup window used for user notifications.
//
#include "guis/GuiInfoPopup.h" #include "guis/GuiInfoPopup.h"
#include "components/ComponentGrid.h" #include "components/ComponentGrid.h"
@ -5,25 +11,28 @@
#include "components/TextComponent.h" #include "components/TextComponent.h"
#include <SDL_timer.h> #include <SDL_timer.h>
GuiInfoPopup::GuiInfoPopup(Window* window, std::string message, int duration) : GuiInfoPopup::GuiInfoPopup(
GuiComponent(window), mMessage(message), mDuration(duration), running(true) Window* window,
std::string message,
int duration)
: GuiComponent(window),
mMessage(message),
mDuration(duration),
running(true)
{ {
mFrame = new NinePatchComponent(window); mFrame = new NinePatchComponent(window);
float maxWidth = Renderer::getScreenWidth() * 0.9f; float maxWidth = Renderer::getScreenWidth() * 0.9f;
float maxHeight = Renderer::getScreenHeight() * 0.2f; float maxHeight = Renderer::getScreenHeight() * 0.2f;
std::shared_ptr<TextComponent> s = std::make_shared<TextComponent>(mWindow, std::shared_ptr<TextComponent> s = std::make_shared<TextComponent>(mWindow, "",
"", Font::get(FONT_SIZE_MINI), 0x444444FF, ALIGN_CENTER);
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 // We do this to force the text container to resize and return the actual expected popup size.
s->setSize(0,0); s->setSize(0,0);
s->setText(message); s->setText(message);
mSize = s->getSize(); mSize = s->getSize();
// confirm the size isn't larger than the screen width, otherwise cap it // Confirm that the size isn't larger than the screen width, otherwise cap it.
if (mSize.x() > maxWidth) { if (mSize.x() > maxWidth) {
s->setSize(maxWidth, mSize[1]); s->setSize(maxWidth, mSize[1]);
mSize[0] = maxWidth; mSize[0] = maxWidth;
@ -33,7 +42,7 @@ GuiInfoPopup::GuiInfoPopup(Window* window, std::string message, int duration) :
mSize[1] = maxHeight; mSize[1] = maxHeight;
} }
// add a padding to the box // Add a padding to the box.
int paddingX = (int) (Renderer::getScreenWidth() * 0.03f); int paddingX = (int) (Renderer::getScreenWidth() * 0.03f);
int paddingY = (int) (Renderer::getScreenHeight() * 0.02f); int paddingY = (int) (Renderer::getScreenHeight() * 0.02f);
mSize[0] = mSize.x() + paddingX; mSize[0] = mSize.x() + paddingX;
@ -48,7 +57,7 @@ GuiInfoPopup::GuiInfoPopup(Window* window, std::string message, int duration) :
mFrame->fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32)); mFrame->fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
addChild(mFrame); addChild(mFrame);
// we only init the actual time when we first start to render // We only initialize the actual time when we first start to render.
mStartTime = 0; mStartTime = 0;
mGrid = new ComponentGrid(window, Vector2i(1, 3)); mGrid = new ComponentGrid(window, Vector2i(1, 3));
@ -59,16 +68,14 @@ GuiInfoPopup::GuiInfoPopup(Window* window, std::string message, int duration) :
GuiInfoPopup::~GuiInfoPopup() GuiInfoPopup::~GuiInfoPopup()
{ {
} }
void GuiInfoPopup::render(const Transform4x4f& /*parentTrans*/) void GuiInfoPopup::render(const Transform4x4f& /*parentTrans*/)
{ {
// we use identity as we want to render on a specific window position, not on the view // We use Identity() as we want to render on a specific window position, not on the view.
Transform4x4f trans = getTransform() * Transform4x4f::Identity(); Transform4x4f trans = getTransform() * Transform4x4f::Identity();
if(running && updateState()) if (running && updateState()) {
{ // If we're still supposed to be rendering it.
// if we're still supposed to be rendering it
Renderer::setMatrix(trans); Renderer::setMatrix(trans);
renderChildren(trans); renderChildren(trans);
} }
@ -78,38 +85,33 @@ bool GuiInfoPopup::updateState()
{ {
int curTime = SDL_GetTicks(); int curTime = SDL_GetTicks();
// we only init the actual time when we first start to render // We only initialize the actual time when we first start to render.
if (mStartTime == 0) if (mStartTime == 0)
{
mStartTime = curTime; mStartTime = curTime;
}
// compute fade in effect // Compute fade-in effect.
if (curTime - mStartTime > mDuration) if (curTime - mStartTime > mDuration) {
{ // We're past the popup duration, no need to render.
// we're past the popup duration, no need to render
running = false; running = false;
return false; return false;
} }
else if (curTime < mStartTime) { else if (curTime < mStartTime) {
// if SDL reset // If SDL reset.
running = false; running = false;
return false; return false;
} }
else if (curTime - mStartTime <= 500) { else if (curTime - mStartTime <= 500) {
alpha = ((curTime - mStartTime)*255/500); alpha = ((curTime - mStartTime)*255/500);
} }
else if (curTime - mStartTime < mDuration - 500) else if (curTime - mStartTime < mDuration - 500) {
{
alpha = 255; alpha = 255;
} }
else else {
{
alpha = ((-(curTime - mStartTime - mDuration)*255)/500); alpha = ((-(curTime - mStartTime - mDuration)*255)/500);
} }
mGrid->setOpacity((unsigned char)alpha); mGrid->setOpacity((unsigned char)alpha);
// apply fade in effect to popup frame // Apply fade-in effect to popup frame.
mFrame->setEdgeColor(0xFFFFFF00 | (unsigned char)(alpha)); mFrame->setEdgeColor(0xFFFFFF00 | (unsigned char)(alpha));
mFrame->setCenterColor(0xFFFFFF00 | (unsigned char)(alpha)); mFrame->setCenterColor(0xFFFFFF00 | (unsigned char)(alpha));
return true; return true;

View file

@ -1,3 +1,9 @@
//
// GuiInfoPopup.h
//
// Popup window used for user notifications.
//
#pragma once #pragma once
#ifndef ES_APP_GUIS_GUI_INFO_POPUP_H #ifndef ES_APP_GUIS_GUI_INFO_POPUP_H
#define ES_APP_GUIS_GUI_INFO_POPUP_H #define ES_APP_GUIS_GUI_INFO_POPUP_H
@ -15,6 +21,7 @@ public:
~GuiInfoPopup(); ~GuiInfoPopup();
void render(const Transform4x4f& parentTrans) override; void render(const Transform4x4f& parentTrans) override;
inline void stop() { running = false; }; inline void stop() { running = false; };
private: private:
std::string mMessage; std::string mMessage;
int mDuration; int mDuration;

View file

@ -299,12 +299,12 @@ void GuiMenu::openUISettings()
s->addSaveFunc([ UImodeSelection, window, this] { s->addSaveFunc([ UImodeSelection, window, this] {
std::string selectedMode = UImodeSelection->getSelected(); std::string selectedMode = UImodeSelection->getSelected();
if (selectedMode != "Full") { if (selectedMode != "Full") {
std::string msg = "You are changing the UI to a restricted mode:\n" + std::string msg = "YOU ARE CHANGING THE UI TO A RESTRICTED MODE:\n\"" +
selectedMode + "\n"; Utils::String::toUpper(selectedMode) + "\"\n";
msg += "This will hide most menu-options to prevent changes to the system.\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 += "TO UNLOCK AND RETURN TO THE FULL UI, ENTER THIS CODE: \n";
msg += "\"" + UIModeController::getInstance()->getFormattedPassKeyStr() + "\"\n\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, window->pushGui(new GuiMsgBox(window, this->getHelpStyle(), msg,
"YES", [selectedMode] { "YES", [selectedMode] {
LOG(LogDebug) << "Setting UI mode to " << selectedMode; LOG(LogDebug) << "Setting UI mode to " << selectedMode;

View file

@ -1,3 +1,9 @@
//
// GuiScreensaverOptions.cpp
//
// User interface template for the screensaver option GUIs.
//
#include "guis/GuiScreensaverOptions.h" #include "guis/GuiScreensaverOptions.h"
#include "guis/GuiTextEditPopup.h" #include "guis/GuiTextEditPopup.h"
@ -6,10 +12,10 @@
#include "SystemData.h" #include "SystemData.h"
#include "Window.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()); setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
@ -60,12 +66,14 @@ std::vector<HelpPrompt> GuiScreensaverOptions::getHelpPrompts()
return prompts; return prompts;
} }
void GuiScreensaverOptions::addEditableTextComponent(ComponentListRow row, const std::string label, std::shared_ptr<GuiComponent> ed, std::string value) void GuiScreensaverOptions::addEditableTextComponent(ComponentListRow row,
const std::string label, std::shared_ptr<GuiComponent> ed, std::string value)
{ {
row.elements.clear(); row.elements.clear();
auto lbl = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(label), Font::get(FONT_SIZE_MEDIUM), 0x777777FF); auto lbl = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(label),
row.addElement(lbl, true); // label Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
row.addElement(lbl, true); // Label.
row.addElement(ed, true); row.addElement(ed, true);
@ -78,9 +86,11 @@ void GuiScreensaverOptions::addEditableTextComponent(ComponentListRow row, const
bracket->setResize(Vector2f(0, lbl->getFont()->getLetterHeight())); bracket->setResize(Vector2f(0, lbl->getFont()->getLetterHeight()));
row.addElement(bracket, false); row.addElement(bracket, false);
auto updateVal = [ed](const std::string& newVal) { ed->setValue(newVal); }; // ok callback (apply new value to ed) // OK callback (apply new value to ed).
auto updateVal = [ed](const std::string& newVal) { ed->setValue(newVal); };
row.makeAcceptInputHandler([this, label, ed, updateVal] { row.makeAcceptInputHandler([this, label, ed, updateVal] {
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label, ed->getValue(), updateVal, false)); mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label,
ed->getValue(), updateVal, false));
}); });
assert(ed); assert(ed);
addRow(row); addRow(row);

View file

@ -1,3 +1,9 @@
//
// GuiScreensaverOptions.h
//
// User interface template for the screensaver option GUIs.
//
#pragma once #pragma once
#ifndef ES_APP_GUIS_GUI_SCREENSAVER_OPTIONS_H #ifndef ES_APP_GUIS_GUI_SCREENSAVER_OPTIONS_H
#define ES_APP_GUIS_GUI_SCREENSAVER_OPTIONS_H #define ES_APP_GUIS_GUI_SCREENSAVER_OPTIONS_H
@ -9,13 +15,15 @@ class GuiScreensaverOptions : public GuiComponent
{ {
public: public:
GuiScreensaverOptions(Window* window, const char* title); GuiScreensaverOptions(Window* window, const char* title);
virtual ~GuiScreensaverOptions(); // just calls save(); virtual ~GuiScreensaverOptions(); // Just calls save()
virtual void save(); virtual void save();
inline void addRow(const ComponentListRow& row) { mMenu.addRow(row); }; inline void addRow(const ComponentListRow& row) { mMenu.addRow(row); };
inline void addWithLabel(const std::string& label, const std::shared_ptr<GuiComponent>& comp) { mMenu.addWithLabel(label, comp); }; inline void addWithLabel(const std::string& label,
const std::shared_ptr<GuiComponent>& comp) { mMenu.addWithLabel(label, comp); };
inline void addSaveFunc(const std::function<void()>& func) { mSaveFuncs.push_back(func); }; inline void addSaveFunc(const std::function<void()>& func) { mSaveFuncs.push_back(func); };
void addEditableTextComponent(ComponentListRow row, const std::string label, std::shared_ptr<GuiComponent> ed, std::string value); void addEditableTextComponent(ComponentListRow row,
const std::string label, std::shared_ptr<GuiComponent> ed, std::string value);
bool input(InputConfig* config, Input input) override; bool input(InputConfig* config, Input input) override;
std::vector<HelpPrompt> getHelpPrompts() override; std::vector<HelpPrompt> getHelpPrompts() override;

View file

@ -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/<eventName>.
// 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 "Scripting.h"
#include "Log.h" #include "Log.h"
#include "Platform.h" #include "Platform.h"
@ -12,25 +24,21 @@ namespace Scripting
std::list<std::string> scriptDirList; std::list<std::string> scriptDirList;
std::string test; std::string test;
// check in exepath // Check in homepath.
test = Utils::FileSystem::getExePath() + "/scripts/" + eventName;
if(Utils::FileSystem::exists(test))
scriptDirList.push_back(test);
// check in homepath
test = Utils::FileSystem::getHomePath() + "/.emulationstation/scripts/" + eventName; test = Utils::FileSystem::getHomePath() + "/.emulationstation/scripts/" + eventName;
if(Utils::FileSystem::exists(test)) if(Utils::FileSystem::exists(test))
scriptDirList.push_back(test); scriptDirList.push_back(test);
for(std::list<std::string>::const_iterator dirIt = scriptDirList.cbegin(); dirIt != scriptDirList.cend(); ++dirIt) { for(std::list<std::string>::const_iterator dirIt = scriptDirList.cbegin();
dirIt != scriptDirList.cend(); ++dirIt) {
std::list<std::string> scripts = Utils::FileSystem::getDirContent(*dirIt); std::list<std::string> scripts = Utils::FileSystem::getDirContent(*dirIt);
for (std::list<std::string>::const_iterator it = scripts.cbegin(); it != scripts.cend(); ++it) { for (std::list<std::string>::const_iterator it = scripts.cbegin();
// append folder to path it != scripts.cend(); ++it) {
// Append folder to path.
std::string script = *it + " \"" + arg1 + "\" \"" + arg2 + "\""; std::string script = *it + " \"" + arg1 + "\" \"" + arg2 + "\"";
LOG(LogDebug) << " executing: " << script; LOG(LogDebug) << " executing: " << script;
runSystemCommand(script); runSystemCommand(script);
} }
} }
} }
}
} // Scripting::

View file

@ -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/<eventName>.
// For example, if the event is called 'game-start', all scripts inside the directory
// $HOME/.emulationstation/scripts/game-start/ will be executed.
//
#pragma once #pragma once
#ifndef ES_CORE_SCRIPTING_H #ifndef ES_CORE_SCRIPTING_H
#define ES_CORE_SCRIPTING_H #define ES_CORE_SCRIPTING_H
@ -6,7 +18,8 @@
namespace Scripting namespace Scripting
{ {
void fireEvent(const std::string& eventName, const std::string& arg1="", const std::string& arg2=""); void fireEvent(const std::string& eventName,
} // Scripting:: const std::string& arg1="", const std::string& arg2="");
}
#endif //ES_CORE_SCRIPTING_H #endif //ES_CORE_SCRIPTING_H

View file

@ -1,7 +1,9 @@
// //
// ThemeData.cpp // 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" #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, void ThemeData::parseElement(const pugi::xml_node& root,
const std::map<std::string, ElementPropertyType>& typeMap, ThemeElement& element) const std::map<std::string, ElementPropertyType>& typeMap, ThemeElement& element)
{ {

View file

@ -1,7 +1,9 @@
// //
// ThemeData.h // 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 #pragma once
@ -148,7 +150,6 @@ private:
}; };
public: public:
ThemeData(); ThemeData();
// Throws ThemeException. // Throws ThemeException.

View file

@ -1,3 +1,9 @@
//
// FileSystemUtil.cpp
//
// Low-level filesystem functions.
//
#define _FILE_OFFSET_BITS 64 #define _FILE_OFFSET_BITS 64
#include "utils/FileSystemUtil.h" #include "utils/FileSystemUtil.h"
@ -5,8 +11,8 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <string.h> #include <string.h>
#if defined(_WIN32) #ifdef WIN32
// because windows... // Because windows...
#include <direct.h> #include <direct.h>
#include <Windows.h> #include <Windows.h>
#define getcwd _getcwd #define getcwd _getcwd
@ -16,27 +22,28 @@
#define unlink _unlink #define unlink _unlink
#define S_ISREG(x) (((x) & S_IFMT) == S_IFREG) #define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
#define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR) #define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
#else // _WIN32 #else
#include <dirent.h> #include <dirent.h>
#include <unistd.h> #include <unistd.h>
#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 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. // 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 // 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'. // directory 'share' will be used, i.e. combining to '/usr/local/share'.
#ifdef __unix__
#ifdef ES_INSTALL_PREFIX #ifdef ES_INSTALL_PREFIX
std::string installPrefix = ES_INSTALL_PREFIX; std::string installPrefix = ES_INSTALL_PREFIX;
#else #else
std::string installPrefix = "/usr/local"; std::string installPrefix = "/usr/local";
#endif #endif
#ifdef ES_DATAROOTDIR #ifdef ES_DATAROOTDIR
std::string dataRootDir = ES_DATAROOTDIR; std::string dataRootDir = ES_DATAROOTDIR;
#else #else
std::string dataRootDir = "share"; std::string dataRootDir = "share";
#endif #endif
#endif
namespace Utils namespace Utils
{ {
@ -48,41 +55,38 @@ namespace Utils
#if defined(_WIN32) #if defined(_WIN32)
static std::string convertFromWideString(const std::wstring wstring) static std::string convertFromWideString(const std::wstring wstring)
{ {
int numBytes = WideCharToMultiByte(CP_UTF8, 0, wstring.c_str(), (int)wstring.length(), nullptr, 0, nullptr, nullptr); int numBytes = WideCharToMultiByte(CP_UTF8, 0, wstring.c_str(),
(int)wstring.length(), nullptr, 0, nullptr, nullptr);
std::string string; std::string string;
string.resize(numBytes); string.resize(numBytes);
WideCharToMultiByte(CP_UTF8, 0, wstring.c_str(), (int)wstring.length(), (char*)string.c_str(), numBytes, nullptr, nullptr); WideCharToMultiByte(CP_UTF8, 0, wstring.c_str(), (int)wstring.length(),
(char*)string.c_str(), numBytes, nullptr, nullptr);
return std::string(string); return std::string(string);
}
} // convertFromWideString #endif
#endif // _WIN32
stringList getDirContent(const std::string& _path, const bool _recursive) stringList getDirContent(const std::string& _path, const bool _recursive)
{ {
std::string path = getGenericPath(_path); std::string path = getGenericPath(_path);
stringList contentList; stringList contentList;
// only parse the directory, if it's a directory // Only parse the directory, if it's a directory.
if(isDirectory(path)) if (isDirectory(path)) {
{
#if defined(_WIN32) #if defined(_WIN32)
WIN32_FIND_DATAW findData; WIN32_FIND_DATAW findData;
std::string wildcard = path + "/*"; std::string wildcard = path + "/*";
HANDLE hFind = FindFirstFileW(std::wstring(wildcard.begin(), wildcard.end()).c_str(), &findData); HANDLE hFind = FindFirstFileW(std::wstring(wildcard.begin(),
wildcard.end()).c_str(), &findData);
if(hFind != INVALID_HANDLE_VALUE) if (hFind != INVALID_HANDLE_VALUE) {
{ // Loop over all files in the directory.
// loop over all files in the directory do {
do
{
std::string name = convertFromWideString(findData.cFileName); std::string name = convertFromWideString(findData.cFileName);
// Ignore "." and ".."
// ignore "." and ".." if ((name != ".") && (name != "..")) {
if((name != ".") && (name != ".."))
{
std::string fullName(getGenericPath(path + "/" + name)); std::string fullName(getGenericPath(path + "/" + name));
contentList.push_back(fullName); contentList.push_back(fullName);
@ -91,24 +95,19 @@ namespace Utils
} }
} }
while (FindNextFileW(hFind, &findData)); while (FindNextFileW(hFind, &findData));
FindClose(hFind); FindClose(hFind);
} }
#else // _WIN32 #else
DIR* dir = opendir(path.c_str()); DIR* dir = opendir(path.c_str());
if(dir != NULL) if (dir != NULL) {
{
struct dirent* entry; struct dirent* entry;
// Loop over all files in the directory.
// loop over all files in the directory while ((entry = readdir(dir)) != NULL) {
while((entry = readdir(dir)) != NULL)
{
std::string name(entry->d_name); std::string name(entry->d_name);
// ignore "." and ".." // Ignore "." and ".."
if((name != ".") && (name != "..")) if ((name != ".") && (name != "..")) {
{
std::string fullName(getGenericPath(path + "/" + name)); std::string fullName(getGenericPath(path + "/" + name));
contentList.push_back(fullName); contentList.push_back(fullName);
@ -116,20 +115,13 @@ namespace Utils
contentList.merge(getDirContent(fullName, true)); contentList.merge(getDirContent(fullName, true));
} }
} }
closedir(dir); closedir(dir);
} }
#endif // _WIN32 #endif
} }
// sort the content list
contentList.sort(); contentList.sort();
// return the content list
return contentList; return contentList;
}
} // getDirContent
stringList getPathList(const std::string& _path) stringList getPathList(const std::string& _path)
{ {
@ -138,76 +130,61 @@ namespace Utils
size_t start = 0; size_t start = 0;
size_t end = 0; size_t end = 0;
// split at '/' // Split at '/'
while((end = path.find("/", start)) != std::string::npos) while ((end = path.find("/", start)) != std::string::npos) {
{
if (end != start) if (end != start)
pathList.push_back(std::string(path, start, end - start)); pathList.push_back(std::string(path, start, end - start));
start = end + 1; start = end + 1;
} }
// Add last folder / file to pathList.
// add last folder / file to pathList
if (start != path.size()) if (start != path.size())
pathList.push_back(std::string(path, start, path.size() - start)); pathList.push_back(std::string(path, start, path.size() - start));
// return the path list
return pathList; return pathList;
}
} // getPathList
void setHomePath(const std::string& _path) void setHomePath(const std::string& _path)
{ {
homePath = getGenericPath(_path); homePath = getGenericPath(_path);
}
} // setHomePath
std::string getHomePath() std::string getHomePath()
{ {
// only construct the homepath once // Only construct the homepath once.
if (homePath.length()) if (homePath.length())
return homePath; return homePath;
// check if "getExePath()/.emulationstation/es_systems.cfg" exists // Check for HOME environment variable.
if(Utils::FileSystem::exists(getExePath() + "/.emulationstation/es_systems.cfg")) if (!homePath.length()) {
homePath = getExePath(); std::string envHome = getenv("HOME");
if (envHome.length())
// check for HOME environment variable
if(!homePath.length())
{
char* envHome = getenv("HOME");
if(envHome)
homePath = getGenericPath(envHome); homePath = getGenericPath(envHome);
} }
#if defined(_WIN32) #if defined(_WIN32)
// on Windows we need to check HOMEDRIVE and HOMEPATH // On Windows we need to check HOMEDRIVE and HOMEPATH.
if(!homePath.length()) if (!homePath.length()) {
{ std::string envHomeDrive = getenv("HOMEDRIVE");
char* envHomeDrive = getenv("HOMEDRIVE"); std::string envHomePath = getenv("HOMEPATH");
char* envHomePath = getenv("HOMEPATH"); if (envHomeDrive.length() && envHomePath.length())
if(envHomeDrive && envHomePath) homePath = getGenericPath(envHomeDrive + "/" + envHomePath);
homePath = getGenericPath(std::string(envHomeDrive) + "/" + envHomePath);
} }
#endif // _WIN32 #endif // _WIN32
// no homepath found, fall back to current working directory // No homepath found, fall back to current working directory.
if (!homePath.length()) if (!homePath.length())
homePath = getCWDPath(); homePath = getCWDPath();
// return constructed homepath
return homePath; return homePath;
}
} // getHomePath
std::string getCWDPath() std::string getCWDPath()
{ {
char temp[512]; char temp[512];
// return current working directory path // Return current working directory.
return (getcwd(temp, 512) ? getGenericPath(temp) : ""); return (getcwd(temp, 512) ? getGenericPath(temp) : "");
}
} // getCWDPath
void setExePath(const std::string& _path) void setExePath(const std::string& _path)
{ {
@ -223,23 +200,35 @@ namespace Utils
#endif #endif
exePath = getCanonicalPath(exePath); exePath = getCanonicalPath(exePath);
// Fallback to argv[0] if everything else fails // Fallback to argv[0] if everything else fails.
if (exePath.empty()) if (exePath.empty())
exePath = getCanonicalPath(_path); exePath = getCanonicalPath(_path);
if (isRegularFile(exePath)) if (isRegularFile(exePath))
exePath = getParent(exePath); exePath = getParent(exePath);
}
} // setExePath
std::string getExePath() std::string getExePath()
{ {
// return constructed exepath
return exePath; return exePath;
}
} // getExePath
std::string getInstallPrefixPath() 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; return installPrefix + "/" + dataRootDir;
} }
@ -248,10 +237,10 @@ namespace Utils
std::string path = _path; std::string path = _path;
size_t offset = std::string::npos; size_t offset = std::string::npos;
#if defined(_WIN32) #if defined(_WIN32)
// convert '/' to '\\' // Convert '/' to '\\'
while ((offset = path.find('/')) != std::string::npos) while ((offset = path.find('/')) != std::string::npos)
path.replace(offset, 1, "\\"); path.replace(offset, 1, "\\");
#endif // _WIN32 #endif
return path; return path;
} }
@ -260,109 +249,97 @@ namespace Utils
std::string path = _path; std::string path = _path;
size_t offset = std::string::npos; size_t offset = std::string::npos;
// remove "\\\\?\\" // Remove "\\\\?\\"
if ((path.find("\\\\?\\")) == 0) if ((path.find("\\\\?\\")) == 0)
path.erase(0, 4); path.erase(0, 4);
// convert '\\' to '/' // Convert '\\' to '/'
while ((offset = path.find('\\')) != std::string::npos) while ((offset = path.find('\\')) != std::string::npos)
path.replace(offset, 1 ,"/"); path.replace(offset, 1 ,"/");
// remove double '/' // Remove double '/'
while ((offset = path.find("//")) != std::string::npos) while ((offset = path.find("//")) != std::string::npos)
path.erase(offset, 1); path.erase(offset, 1);
// remove trailing '/' when the path is more than a simple '/' // Remove trailing '/' when the path is more than a simple '/'
while (path.length() > 1 && ((offset = path.find_last_of('/')) == (path.length() - 1))) while (path.length() > 1 && ((offset = path.find_last_of('/')) == (path.length() - 1)))
path.erase(offset, 1); path.erase(offset, 1);
// return generic path
return path; return path;
}
} // getGenericPath
std::string getEscapedPath(const std::string& _path) std::string getEscapedPath(const std::string& _path)
{ {
std::string path = getGenericPath(_path); std::string path = getGenericPath(_path);
#if defined(_WIN32) #if defined(_WIN32)
// windows escapes stuff by just putting everything in quotes // Windows escapes stuff by just putting everything in quotes.
return '"' + getPreferredPath(path) + '"'; return '"' + getPreferredPath(path) + '"';
#else // _WIN32 #else
// insert a backslash before most characters that would mess up a bash path // Insert a backslash before most characters that would mess up a bash path.
const char* invalidChars = "\\ '\"!$^&*(){}[]?;<>"; const char* invalidChars = "\\ '\"!$^&*(){}[]?;<>";
const char* invalidChar = invalidChars; const char* invalidChar = invalidChars;
while(*invalidChar) while (*invalidChar) {
{
size_t start = 0; size_t start = 0;
size_t offset = 0; size_t offset = 0;
while((offset = path.find(*invalidChar, start)) != std::string::npos) while ((offset = path.find(*invalidChar, start)) != std::string::npos) {
{
start = offset + 1; start = offset + 1;
if((offset == 0) || (path[offset - 1] != '\\')) if ((offset == 0) || (path[offset - 1] != '\\')) {
{
path.insert(offset, 1, '\\'); path.insert(offset, 1, '\\');
++start; ++start;
} }
} }
++invalidChar; ++invalidChar;
} }
// return escaped path
return path; return path;
#endif // _WIN32 #endif
}
} // getEscapedPath
std::string getCanonicalPath(const std::string& _path) std::string getCanonicalPath(const std::string& _path)
{ {
// temporary hack for builtin resources // Hack for builtin resources.
if ((_path[0] == ':') && (_path[1] == '/')) if ((_path[0] == ':') && (_path[1] == '/'))
return _path; return _path;
std::string path = exists(_path) ? getAbsolutePath(_path) : getGenericPath(_path); std::string path = exists(_path) ? getAbsolutePath(_path) : getGenericPath(_path);
// cleanup path // Cleanup path.
bool scan = true; bool scan = true;
while(scan) while (scan) {
{
stringList pathList = getPathList(path); stringList pathList = getPathList(path);
path.clear(); path.clear();
scan = false; scan = false;
for(stringList::const_iterator it = pathList.cbegin(); it != pathList.cend(); ++it) for (stringList::const_iterator it = pathList.cbegin();
{ it != pathList.cend(); ++it) {
// ignore empty // Ignore empty.
if ((*it).empty()) if ((*it).empty())
continue; continue;
// remove "/./" // Remove "/./"
if ((*it) == ".") if ((*it) == ".")
continue; continue;
// resolve "/../" // Resolve "/../"
if((*it) == "..") if ((*it) == "..") {
{
path = getParent(path); path = getParent(path);
continue; continue;
} }
#if defined(_WIN32) #if defined(_WIN32)
// append folder to path // Append folder to path.
path += (path.size() == 0) ? (*it) : ("/" + (*it)); path += (path.size() == 0) ? (*it) : ("/" + (*it));
#else // _WIN32 #else
// append folder to path // Append folder to path.
path += ("/" + (*it)); path += ("/" + (*it));
#endif // _WIN32 #endif
// resolve symlink // Resolve symlink.
if(isSymlink(path)) if (isSymlink(path)) {
{
std::string resolved = resolveSymlink(path); std::string resolved = resolveSymlink(path);
if (resolved.empty()) if (resolved.empty())
@ -381,148 +358,137 @@ namespace Utils
} }
} }
} }
// return canonical path
return path; return path;
}
} // getCanonicalPath
std::string getAbsolutePath(const std::string& _path, const std::string& _base) std::string getAbsolutePath(const std::string& _path, const std::string& _base)
{ {
std::string path = getGenericPath(_path); std::string path = getGenericPath(_path);
std::string base = isAbsolute(_base) ? getGenericPath(_base) : getAbsolutePath(_base); std::string base = isAbsolute(_base) ? getGenericPath(_base) : getAbsolutePath(_base);
// return absolute path // Return absolute path.
return isAbsolute(path) ? path : getGenericPath(base + "/" + path); return isAbsolute(path) ? path : getGenericPath(base + "/" + path);
} // getAbsolutePath }
std::string getParent(const std::string& _path) std::string getParent(const std::string& _path)
{ {
std::string path = getGenericPath(_path); std::string path = getGenericPath(_path);
size_t offset = std::string::npos; size_t offset = std::string::npos;
// find last '/' and erase it // Find last '/' and erase it.
if ((offset = path.find_last_of('/')) != std::string::npos) if ((offset = path.find_last_of('/')) != std::string::npos)
return path.erase(offset); return path.erase(offset);
// no parent found // No parent found.
return path; return path;
}
} // getParent
std::string getFileName(const std::string& _path) std::string getFileName(const std::string& _path)
{ {
std::string path = getGenericPath(_path); std::string path = getGenericPath(_path);
size_t offset = std::string::npos; size_t offset = std::string::npos;
// find last '/' and return the filename // Find last '/' and return the filename.
if ((offset = path.find_last_of('/')) != std::string::npos) if ((offset = path.find_last_of('/')) != std::string::npos)
return ((path[offset + 1] == 0) ? "." : std::string(path, offset + 1)); return ((path[offset + 1] == 0) ? "." : std::string(path, offset + 1));
// no '/' found, entire path is a filename // No '/' found, entire path is a filename.
return path; return path;
}
} // getFileName
std::string getStem(const std::string& _path) std::string getStem(const std::string& _path)
{ {
std::string fileName = getFileName(_path); std::string fileName = getFileName(_path);
size_t offset = std::string::npos; size_t offset = std::string::npos;
// empty fileName // Empty fileName.
if (fileName == ".") if (fileName == ".")
return fileName; return fileName;
// find last '.' and erase the extension // Find last '.' and erase the extension.
if ((offset = fileName.find_last_of('.')) != std::string::npos) if ((offset = fileName.find_last_of('.')) != std::string::npos)
return fileName.erase(offset); return fileName.erase(offset);
// no '.' found, filename has no extension // No '.' found, filename has no extension.
return fileName; return fileName;
}
} // getStem
std::string getExtension(const std::string& _path) std::string getExtension(const std::string& _path)
{ {
std::string fileName = getFileName(_path); std::string fileName = getFileName(_path);
size_t offset = std::string::npos; size_t offset = std::string::npos;
// empty fileName // Empty fileName.
if (fileName == ".") if (fileName == ".")
return fileName; return fileName;
// find last '.' and return the extension // Find last '.' and return the extension.
if ((offset = fileName.find_last_of('.')) != std::string::npos) if ((offset = fileName.find_last_of('.')) != std::string::npos)
return std::string(fileName, offset); return std::string(fileName, offset);
// no '.' found, filename has no extension // No '.' found, filename has no extension.
return "."; return ".";
}
} // getExtension std::string resolveRelativePath(const std::string& _path,
const std::string& _relativeTo, const bool _allowHome)
std::string resolveRelativePath(const std::string& _path, const std::string& _relativeTo, const bool _allowHome)
{ {
std::string path = getGenericPath(_path); std::string path = getGenericPath(_path);
std::string relativeTo = isDirectory(_relativeTo) ? getGenericPath(_relativeTo) : getParent(_relativeTo); std::string relativeTo = isDirectory(_relativeTo) ?
getGenericPath(_relativeTo) : getParent(_relativeTo);
// nothing to resolve // Nothing to resolve.
if (!path.length()) if (!path.length())
return path; return path;
// replace '.' with relativeTo // Replace '.' with relativeTo.
if ((path[0] == '.') && (path[1] == '/')) if ((path[0] == '.') && (path[1] == '/'))
return (relativeTo + &(path[1])); return (relativeTo + &(path[1]));
// replace '~' with homePath // Replace '~' with homePath.
if (_allowHome && (path[0] == '~') && (path[1] == '/')) if (_allowHome && (path[0] == '~') && (path[1] == '/'))
return (getHomePath() + &(path[1])); return (getHomePath() + &(path[1]));
// nothing to resolve // Nothing to resolve.
return path; return path;
}
} // resolveRelativePath std::string createRelativePath(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)
{ {
bool contains = false; bool contains = false;
std::string path = removeCommonPath(_path, _relativeTo, contains); std::string path = removeCommonPath(_path, _relativeTo, contains);
// success
if (contains) if (contains)
return ("./" + path); return ("./" + path);
if(_allowHome) if (_allowHome) {
{
path = removeCommonPath(_path, getHomePath(), contains); path = removeCommonPath(_path, getHomePath(), contains);
// success
if (contains) if (contains)
return ("~/" + path); return ("~/" + path);
} }
// nothing to resolve
return path; return path;
}
} // createRelativePath std::string removeCommonPath(const std::string& _path,
const std::string& _common, bool& _contains)
std::string removeCommonPath(const std::string& _path, const std::string& _common, bool& _contains)
{ {
std::string path = getGenericPath(_path); std::string path = getGenericPath(_path);
std::string common = isDirectory(_common) ? getGenericPath(_common) : getParent(_common); std::string common = isDirectory(_common) ?
getGenericPath(_common) : getParent(_common);
// check if path contains common // Check if path contains common.
if(path.find(common) == 0) if (path.find(common) == 0) {
{
_contains = true; _contains = true;
return path.substr(common.length() + 1); return path.substr(common.length() + 1);
} }
// it didn't // It didn't.
_contains = false; _contains = false;
return path; return path;
}
} // removeCommonPath
std::string resolveSymlink(const std::string& _path) std::string resolveSymlink(const std::string& _path)
{ {
@ -530,81 +496,74 @@ namespace Utils
std::string resolved; std::string resolved;
#if defined(_WIN32) #if defined(_WIN32)
HANDLE hFile = CreateFile(path.c_str(), FILE_READ_ATTRIBUTES, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0); 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) if (hFile != INVALID_HANDLE_VALUE) {
{ resolved.resize(GetFinalPathNameByHandle(hFile, nullptr, 0,
resolved.resize(GetFinalPathNameByHandle(hFile, nullptr, 0, FILE_NAME_NORMALIZED) + 1); FILE_NAME_NORMALIZED) + 1);
if(GetFinalPathNameByHandle(hFile, (LPSTR)resolved.data(), (DWORD)resolved.size(), FILE_NAME_NORMALIZED) > 0) if (GetFinalPathNameByHandle(hFile, (LPSTR)resolved.data(),
{ (DWORD)resolved.size(), FILE_NAME_NORMALIZED) > 0) {
resolved.resize(resolved.size() - 1); resolved.resize(resolved.size() - 1);
resolved = getGenericPath(resolved); resolved = getGenericPath(resolved);
} }
CloseHandle(hFile); CloseHandle(hFile);
} }
#else // _WIN32 #else
struct stat info; struct stat info;
// check if lstat succeeded // Check if lstat succeeded.
if(lstat(path.c_str(), &info) == 0) if (lstat(path.c_str(), &info) == 0) {
{
resolved.resize(info.st_size); resolved.resize(info.st_size);
if (readlink(path.c_str(), (char*)resolved.data(), resolved.size()) > 0) if (readlink(path.c_str(), (char*)resolved.data(), resolved.size()) > 0)
resolved = getGenericPath(resolved); resolved = getGenericPath(resolved);
} }
#endif // _WIN32 #endif
// return resolved path
return resolved; return resolved;
}
} // resolveSymlink
bool removeFile(const std::string& _path) bool removeFile(const std::string& _path)
{ {
std::string path = getGenericPath(_path); std::string path = getGenericPath(_path);
// don't remove if it doesn't exists // Don't remove if it doesn't exists.
if (!exists(path)) if (!exists(path))
return true; return true;
// try to remove file // Try to remove file.
return (unlink(path.c_str()) == 0); return (unlink(path.c_str()) == 0);
}
} // removeFile
bool createDirectory(const std::string& _path) bool createDirectory(const std::string& _path)
{ {
std::string path = getGenericPath(_path); std::string path = getGenericPath(_path);
// don't create if it already exists // Don't create if it already exists.
if (exists(path)) if (exists(path))
return true; return true;
// try to create directory // Try to create directory.
if (mkdir(path.c_str(), 0755) == 0) if (mkdir(path.c_str(), 0755) == 0)
return true; return true;
// failed to create directory, try to create the parent // Failed to create directory, try to create the parent.
std::string parent = getParent(path); std::string parent = getParent(path);
// only try to create parent if it's not identical to path // Only try to create parent if it's not identical to path.
if (parent != path) if (parent != path)
createDirectory(parent); createDirectory(parent);
// try to create directory again now that the parent should exist // Try to create directory again now that the parent should exist.
return (mkdir(path.c_str(), 0755) == 0); return (mkdir(path.c_str(), 0755) == 0);
}
} // createDirectory
bool exists(const std::string& _path) bool exists(const std::string& _path)
{ {
std::string path = getGenericPath(_path); std::string path = getGenericPath(_path);
struct stat64 info; struct stat64 info;
// check if stat64 succeeded
return (stat64(path.c_str(), &info) == 0); return (stat64(path.c_str(), &info) == 0);
}
} // exists
bool isAbsolute(const std::string& _path) bool isAbsolute(const std::string& _path)
{ {
@ -612,85 +571,77 @@ namespace Utils
#if defined(_WIN32) #if defined(_WIN32)
return ((path.size() > 1) && (path[1] == ':')); return ((path.size() > 1) && (path[1] == ':'));
#else // _WIN32 #else
return ((path.size() > 0) && (path[0] == '/')); return ((path.size() > 0) && (path[0] == '/'));
#endif // _WIN32 #endif
}
} // isAbsolute
bool isRegularFile(const std::string& _path) bool isRegularFile(const std::string& _path)
{ {
std::string path = getGenericPath(_path); std::string path = getGenericPath(_path);
struct stat64 info; struct stat64 info;
// check if stat64 succeeded
if (stat64(path.c_str(), &info) != 0) if (stat64(path.c_str(), &info) != 0)
return false; return false;
// check for S_IFREG attribute // Check for S_IFREG attribute.
return (S_ISREG(info.st_mode)); return (S_ISREG(info.st_mode));
}
} // isRegularFile
bool isDirectory(const std::string& _path) bool isDirectory(const std::string& _path)
{ {
std::string path = getGenericPath(_path); std::string path = getGenericPath(_path);
struct stat info; struct stat info;
// check if stat succeeded
if (stat(path.c_str(), &info) != 0) if (stat(path.c_str(), &info) != 0)
return false; return false;
// check for S_IFDIR attribute // Check for S_IFDIR attribute.
return (S_ISDIR(info.st_mode)); return (S_ISDIR(info.st_mode));
}
} // isDirectory
bool isSymlink(const std::string& _path) bool isSymlink(const std::string& _path)
{ {
std::string path = getGenericPath(_path); std::string path = getGenericPath(_path);
#if defined(_WIN32) #if defined(_WIN32)
// check for symlink attribute // Check for symlink attribute.
const DWORD Attributes = GetFileAttributes(path.c_str()); const DWORD Attributes = GetFileAttributes(path.c_str());
if((Attributes != INVALID_FILE_ATTRIBUTES) && (Attributes & FILE_ATTRIBUTE_REPARSE_POINT)) if ((Attributes != INVALID_FILE_ATTRIBUTES) &&
(Attributes & FILE_ATTRIBUTE_REPARSE_POINT))
return true; return true;
#else // _WIN32 #else
struct stat info; struct stat info;
// check if lstat succeeded
if (lstat(path.c_str(), &info) != 0) if (lstat(path.c_str(), &info) != 0)
return false; return false;
// check for S_IFLNK attribute // Check for S_IFLNK attribute.
return (S_ISLNK(info.st_mode)); return (S_ISLNK(info.st_mode));
#endif // _WIN32 #endif
// not a symlink // Not a symlink.
return false; return false;
}
} // isSymlink
bool isHidden(const std::string& _path) bool isHidden(const std::string& _path)
{ {
std::string path = getGenericPath(_path); std::string path = getGenericPath(_path);
#if defined(_WIN32) #if defined(_WIN32)
// check for hidden attribute // Check for hidden attribute.
const DWORD Attributes = GetFileAttributes(path.c_str()); const DWORD Attributes = GetFileAttributes(path.c_str());
if ((Attributes != INVALID_FILE_ATTRIBUTES) && (Attributes & FILE_ATTRIBUTE_HIDDEN)) if ((Attributes != INVALID_FILE_ATTRIBUTES) && (Attributes & FILE_ATTRIBUTE_HIDDEN))
return true; return true;
#endif // _WIN32 #endif // _WIN32
// filenames starting with . are hidden in linux, we do this check for windows as well // Filenames starting with . are hidden in Linux, but
// we do this check for windows as well.
if (getFileName(path)[0] == '.') if (getFileName(path)[0] == '.')
return true; return true;
// not hidden
return false; return false;
}
} // isHidden
} // FileSystem:: } // FileSystem::
} // Utils:: } // Utils::

View file

@ -1,3 +1,9 @@
//
// FileSystemUtil.h
//
// Low-level filesystem functions.
//
#pragma once #pragma once
#ifndef ES_CORE_UTILS_FILE_SYSTEM_UTIL_H #ifndef ES_CORE_UTILS_FILE_SYSTEM_UTIL_H
#define ES_CORE_UTILS_FILE_SYSTEM_UTIL_H #define ES_CORE_UTILS_FILE_SYSTEM_UTIL_H
@ -11,7 +17,8 @@ namespace Utils
{ {
typedef std::list<std::string> stringList; typedef std::list<std::string> stringList;
stringList getDirContent (const std::string& _path, const bool _recursive = false); stringList getDirContent(const std::string& _path,
const bool _recursive = false);
stringList getPathList(const std::string& _path); stringList getPathList(const std::string& _path);
void setHomePath(const std::string& _path); void setHomePath(const std::string& _path);
std::string getHomePath(); std::string getHomePath();
@ -23,14 +30,18 @@ namespace Utils
std::string getGenericPath(const std::string& _path); std::string getGenericPath(const std::string& _path);
std::string getEscapedPath(const std::string& _path); std::string getEscapedPath(const std::string& _path);
std::string getCanonicalPath(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 getAbsolutePath(const std::string& _path,
const std::string& _base = getCWDPath());
std::string getParent(const std::string& _path); std::string getParent(const std::string& _path);
std::string getFileName(const std::string& _path); std::string getFileName(const std::string& _path);
std::string getStem(const std::string& _path); std::string getStem(const std::string& _path);
std::string getExtension(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 resolveRelativePath(const std::string& _path,
std::string createRelativePath (const std::string& _path, const std::string& _relativeTo, const bool _allowHome); const std::string& _relativeTo, const bool _allowHome);
std::string removeCommonPath (const std::string& _path, const std::string& _common, bool& _contains); 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); std::string resolveSymlink(const std::string& _path);
bool removeFile(const std::string& _path); bool removeFile(const std::string& _path);
bool createDirectory(const std::string& _path); bool createDirectory(const std::string& _path);
@ -40,9 +51,7 @@ namespace Utils
bool isDirectory(const std::string& _path); bool isDirectory(const std::string& _path);
bool isSymlink(const std::string& _path); bool isSymlink(const std::string& _path);
bool isHidden(const std::string& _path); bool isHidden(const std::string& _path);
}
} // FileSystem:: }
} // Utils::
#endif // ES_CORE_UTILS_FILE_SYSTEM_UTIL_H #endif // ES_CORE_UTILS_FILE_SYSTEM_UTIL_H