Compare commits

...

9 commits

Author SHA1 Message Date
Rael Gugelmin Cunha
21733bd567
Merge 72892db014 into e8fb5a0869 2024-07-12 00:31:07 +09:00
pjft
e8fb5a0869
Merge pull request #875 from Gemba/fix_save_on_new_custom_collections
Fix persistence of new collections, either from theme or custom name
2024-07-01 10:14:37 +01:00
Gemba
3c41f15374 Fix persistence of new collections, either from theme or custom name 2024-07-01 08:18:21 +02:00
pjft
a9ee7e48c4
Merge pull request #878 from Gemba/fix_collection_metadataedit_navigation
Fixes segfault after metadata edit in collections and navigating back…
2024-06-24 08:40:52 +01:00
Gemba
6de6151b09 Fixes segfault after metadata edit in collections and navigating back to carousel 2024-06-23 17:20:34 +02:00
pjft
4a064a2130
Merge pull request #874 from Gemba/fix_lrlb_paging_cursor_misplaced_lastplayed
Fix cursor overrun in last played collection when LR/LB paging is used
2024-04-22 22:31:58 +01:00
Gemba
485b995196 Fix for cursor overrun in last played collection when LR/LB paging is active
and fix cursor placement when changing theme with a smaller/larger gamelist viewport.
2024-04-18 21:57:34 +02:00
Rael Gugelmin Cunha
72892db014 Properly parsing command params 2020-11-13 23:17:51 -03:00
Rael Gugelmin Cunha
d03b0e60c7 Use ShellExecute to run emulators on Win32 2020-11-13 15:07:55 -03:00
9 changed files with 169 additions and 101 deletions

View file

@ -1,5 +1,6 @@
#include "CollectionSystemManager.h"
#include "components/TextListComponent.h"
#include "guis/GuiInfoPopup.h"
#include "utils/FileSystemUtil.h"
#include "utils/StringUtil.h"
@ -14,6 +15,7 @@
#include "ThemeData.h"
#include <pugixml.hpp>
#include <fstream>
#include <cstring>
/* Handling the getting, initialization, deinitialization, saving and deletion of
* a CollectionSystemManager Instance */
@ -96,17 +98,29 @@ void CollectionSystemManager::deinit()
}
}
void CollectionSystemManager::saveCustomCollection(SystemData* sys)
bool CollectionSystemManager::saveCustomCollection(SystemData* sys)
{
std::string name = sys->getName();
std::unordered_map<std::string, FileData*> games = sys->getRootFolder()->getChildrenByFilename();
bool found = mCustomCollectionSystemsData.find(name) != mCustomCollectionSystemsData.cend();
if (found) {
if (!found)
{
LOG(LogError) << "Couldn't find collection to save! " << name;
return false;
}
CollectionSystemData sysData = mCustomCollectionSystemsData.at(name);
if (sysData.needsSave)
{
std::string absCollectionFn = getCustomCollectionConfigPath(name);
std::ofstream configFile;
configFile.open(getCustomCollectionConfigPath(name));
configFile.open(absCollectionFn);
if (!configFile.good())
{
auto const errNo = errno;
LOG(LogError) << "Failed to create file, collection not created: " << absCollectionFn << ": " << std::strerror(errNo) << " (" << errNo << ")";
return false;
}
for(std::unordered_map<std::string, FileData*>::const_iterator iter = games.cbegin(); iter != games.cend(); ++iter)
{
std::string path = iter->first;
@ -114,11 +128,7 @@ void CollectionSystemManager::saveCustomCollection(SystemData* sys)
}
configFile.close();
}
}
else
{
LOG(LogError) << "Couldn't find collection to save! " << name;
}
return true;
}
/* Methods to load all Collections into memory, and handle enabling the active ones */
@ -127,7 +137,7 @@ void CollectionSystemManager::loadCollectionSystems(bool async)
{
initAutoCollectionSystems();
CollectionSystemDecl decl = mCollectionSystemDeclsIndex[CUSTOM_COLL_ID];
mCustomCollectionsBundle = createNewCollectionEntry(decl.name, decl, false);
mCustomCollectionsBundle = createNewCollectionEntry(decl.name, decl, CollectionFlags::NONE);
// we will also load custom systems here
initCustomCollectionSystems();
if(Settings::getInstance()->getString("CollectionSystemsAuto") != "" || Settings::getInstance()->getString("CollectionSystemsCustom") != "")
@ -284,6 +294,8 @@ void CollectionSystemManager::updateCollectionSystem(FileData* file, CollectionS
{
trimCollectionCount(rootFolder, LAST_PLAYED_MAX, false);
ViewController::get()->onFileChanged(rootFolder, FILE_METADATA_CHANGED);
// Force re-calculation of cursor position
ViewController::get()->getGameListView(curSys)->setViewportTop(TextListComponent<FileData>::REFRESH_LIST_CURSOR_POS);
}
else
ViewController::get()->onFileChanged(rootFolder, FILE_SORTED);
@ -372,6 +384,7 @@ bool CollectionSystemManager::isThemeCustomCollectionCompatible(std::vector<std:
std::string CollectionSystemManager::getValidNewCollectionName(std::string inName, int index)
{
std::string name = inName;
const std::string infix = " (" + std::to_string(index) + ")";
if(index == 0)
{
@ -385,7 +398,7 @@ std::string CollectionSystemManager::getValidNewCollectionName(std::string inNam
}
else
{
name += " (" + std::to_string(index) + ")";
name += infix;
}
if(name == "")
@ -395,7 +408,7 @@ std::string CollectionSystemManager::getValidNewCollectionName(std::string inNam
if(name != inName)
{
LOG(LogInfo) << "Had to change name, from: " << inName << " to: " << name;
LOG(LogInfo) << "Name collision, had to change name from: " << inName << " to: " << name;
}
// get used systems in es_systems.cfg
@ -415,7 +428,7 @@ std::string CollectionSystemManager::getValidNewCollectionName(std::string inNam
if (*sysIt == name)
{
if(index > 0) {
name = name.substr(0, name.size()-4);
name = name.substr(0, name.size() - infix.size());
}
return getValidNewCollectionName(name, index+1);
}
@ -445,7 +458,7 @@ void CollectionSystemManager::setEditMode(std::string collectionName, bool quiet
mEditingCollectionSystemData = sysData;
if (!quiet) {
GuiInfoPopup* s = new GuiInfoPopup(mWindow, "Editing the '" + Utils::String::toUpper(collectionName) + "' Collection. Add/remove games with Y.", 10000);
GuiInfoPopup* s = new GuiInfoPopup(mWindow, "Editing the '" + Utils::String::toUpper(collectionName) + "' Collection. Add/remove games with Y.", 8000);
mWindow->setInfoPopup(s);
}
}
@ -453,14 +466,14 @@ void CollectionSystemManager::setEditMode(std::string collectionName, bool quiet
void CollectionSystemManager::exitEditMode(bool quiet)
{
if (!quiet) {
GuiInfoPopup* s = new GuiInfoPopup(mWindow, "Finished editing the '" + mEditingCollection + "' Collection.", 4000);
GuiInfoPopup* s = new GuiInfoPopup(mWindow, "Finished editing the '" + Utils::String::toUpper(mEditingCollection) + "' Collection.", 4000);
mWindow->setInfoPopup(s);
}
if (mIsEditingCustom) {
mIsEditingCustom = false;
mEditingCollection = "Favorites";
mEditingCollectionSystemData->system->onMetaDataSavePoint();
saveCustomCollection(mEditingCollectionSystemData->system);
}
}
@ -653,7 +666,7 @@ void CollectionSystemManager::initAutoCollectionSystems()
CollectionSystemDecl sysDecl = it->second;
if (!sysDecl.isCustom)
{
SystemData* newCol = createNewCollectionEntry(sysDecl.name, sysDecl);
SystemData* newCol = createNewCollectionEntry(sysDecl.name, sysDecl, CollectionFlags::HOLD_IN_MAP);
if (sysDecl.type == AUTO_RANDOM)
mRandomCollection = newCol;
}
@ -753,17 +766,20 @@ SystemData* CollectionSystemManager::getAllGamesCollection()
return allSysData->system;
}
SystemData* CollectionSystemManager::addNewCustomCollection(std::string name)
SystemData* CollectionSystemManager::addNewCustomCollection(std::string name, bool needsSave)
{
CollectionSystemDecl decl = mCollectionSystemDeclsIndex[CUSTOM_COLL_ID];
decl.themeFolder = name;
decl.name = name;
decl.longName = name;
return createNewCollectionEntry(name, decl);
CollectionFlags flags = CollectionFlags::HOLD_IN_MAP;
if (needsSave)
flags = flags | CollectionFlags::NEEDS_SAVE;
return createNewCollectionEntry(name, decl, flags);
}
// creates a new, empty Collection system, based on the name and declaration
SystemData* CollectionSystemManager::createNewCollectionEntry(std::string name, CollectionSystemDecl sysDecl, bool index)
SystemData* CollectionSystemManager::createNewCollectionEntry(std::string name, CollectionSystemDecl sysDecl, const CollectionFlags flags)
{
SystemData* newSys = new SystemData(name, sysDecl.longName, mCollectionEnvData, sysDecl.themeFolder, true);
@ -772,9 +788,9 @@ SystemData* CollectionSystemManager::createNewCollectionEntry(std::string name,
newCollectionData.decl = sysDecl;
newCollectionData.isEnabled = false;
newCollectionData.isPopulated = false;
newCollectionData.needsSave = false;
newCollectionData.needsSave = (flags & CollectionFlags::NEEDS_SAVE) == CollectionFlags::NEEDS_SAVE ? true : false;
if (index)
if ((flags & CollectionFlags::HOLD_IN_MAP) == CollectionFlags::HOLD_IN_MAP)
{
if (!sysDecl.isCustom)
{

View file

@ -30,6 +30,24 @@ enum CollectionSystemType
CUSTOM_COLLECTION
};
// Flags when loading or creating a collection
enum class CollectionFlags : uint8_t
{
NONE, // create only
HOLD_IN_MAP, // create and keep in mAutoCollectionSystemsData or mCustomCollectionSystemsData
NEEDS_SAVE // force save of newly added collection
};
constexpr CollectionFlags operator|(CollectionFlags a,CollectionFlags b)
{
return static_cast<CollectionFlags>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b));
}
constexpr CollectionFlags operator&(CollectionFlags a, CollectionFlags b)
{
return static_cast<CollectionFlags>(static_cast<uint8_t>(a) & static_cast<uint8_t>(b));
}
struct CollectionSystemDecl
{
CollectionSystemType type; // type of system
@ -58,7 +76,7 @@ public:
static CollectionSystemManager* get();
static void init(Window* window);
static void deinit();
void saveCustomCollection(SystemData* sys);
bool saveCustomCollection(SystemData* sys);
void loadCollectionSystems(bool async=false);
void loadEnabledListFromSettings();
@ -74,7 +92,7 @@ public:
inline SystemData* getCustomCollectionsBundle() { return mCustomCollectionsBundle; };
inline SystemData* getRandomCollection() { return mRandomCollection; };
std::vector<std::string> getUnusedSystemsFromTheme();
SystemData* addNewCustomCollection(std::string name);
SystemData* addNewCustomCollection(std::string name, bool needsSave = false);
bool isThemeGenericCollectionCompatible(bool genericCustomCollections);
bool isThemeCustomCollectionCompatible(std::vector<std::string> stringVector);
@ -107,7 +125,7 @@ private:
void initAutoCollectionSystems();
void initCustomCollectionSystems();
SystemData* createNewCollectionEntry(std::string name, CollectionSystemDecl sysDecl, bool index = true);
SystemData* createNewCollectionEntry(std::string name, CollectionSystemDecl sysDecl, const CollectionFlags flags);
void populateAutoCollection(CollectionSystemData* sysData);
void populateCustomCollection(CollectionSystemData* sysData);
void addRandomGames(SystemData* newSys, SystemData* sourceSystem, FileData* rootFolder, FileFilterIndex* index,

View file

@ -392,6 +392,11 @@ void TextListComponent<T>::onCursorChanged(const CursorState& state)
template <typename T>
void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties)
{
if(Settings::getInstance()->getBool("UseFullscreenPaging"))
{
mViewportTop = REFRESH_LIST_CURSOR_POS;
}
GuiComponent::applyTheme(theme, view, element, properties);
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "textlist");

View file

@ -1,7 +1,10 @@
#include <fstream>
#include "guis/GuiCollectionSystemsOptions.h"
#include "components/OptionListComponent.h"
#include "components/SwitchComponent.h"
#include "guis/GuiInfoPopup.h"
#include "guis/GuiRandomCollectionOptions.h"
#include "guis/GuiSettings.h"
#include "guis/GuiTextEditPopup.h"
@ -25,6 +28,16 @@ void GuiCollectionSystemsOptions::initializeMenu()
// manage random collection
addEntry("RANDOM GAME COLLECTION SETTINGS", 0x777777FF, true, [this] { openRandomCollectionSettings(); });
ComponentListRow row;
if(CollectionSystemManager::get()->isEditing())
{
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));
mMenu.addRow(row);
}
else
{
// add "Create New Custom Collection from Theme"
std::vector<std::string> unusedFolders = CollectionSystemManager::get()->getUnusedSystemsFromTheme();
if (unusedFolders.size() > 0)
@ -53,7 +66,6 @@ void GuiCollectionSystemsOptions::initializeMenu()
});
}
ComponentListRow row;
row.addElement(std::make_shared<TextComponent>(mWindow, "CREATE NEW CUSTOM COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
auto createCustomCollection = [this](const std::string& newVal) {
std::string name = newVal;
@ -66,8 +78,8 @@ void GuiCollectionSystemsOptions::initializeMenu()
row.makeAcceptInputHandler([this, createCustomCollection] {
mWindow->pushGui(new GuiTextEditPopup(mWindow, "New Collection Name", "", createCustomCollection, false));
});
mMenu.addRow(row);
}
bundleCustomCollections = std::make_shared<SwitchComponent>(mWindow);
bundleCustomCollections->setState(Settings::getInstance()->getBool("UseCustomCollectionsSystem"));
@ -103,14 +115,6 @@ void GuiCollectionSystemsOptions::initializeMenu()
mMenu.addWithLabel("ADD/REMOVE GAMES WHILE SCREENSAVER TO", defaultScreenSaverCollection);
if(CollectionSystemManager::get()->isEditing())
{
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.makeAcceptInputHandler(std::bind(&GuiCollectionSystemsOptions::exitEditMode, this));
mMenu.addRow(row);
}
mMenu.addButton("BACK", "back", std::bind(&GuiCollectionSystemsOptions::applySettings, this));
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2, Renderer::getScreenHeight() * 0.15f);
@ -137,8 +141,15 @@ void GuiCollectionSystemsOptions::addEntry(const char* name, unsigned int color,
void GuiCollectionSystemsOptions::createCollection(std::string inName)
{
std::string name = CollectionSystemManager::get()->getValidNewCollectionName(inName);
SystemData* newSys = CollectionSystemManager::get()->addNewCustomCollection(name);
CollectionSystemManager* collSysMgr = CollectionSystemManager::get();
std::string name = collSysMgr->getValidNewCollectionName(inName);
SystemData* newSys = collSysMgr->addNewCustomCollection(name, true);
if (!collSysMgr->saveCustomCollection(newSys)) {
GuiInfoPopup* s = new GuiInfoPopup(mWindow, "Failed creating '" + Utils::String::toUpper(name) + "' Collection. See log for details.", 8000);
mWindow->setInfoPopup(s);
return;
}
customOptionList->add(name, name, true);
std::string outAuto = Utils::String::vectorToDelimitedString(autoOptionList->getSelectedObjects(), ",");
std::string outCustom = Utils::String::vectorToDelimitedString(customOptionList->getSelectedObjects(), ",");
@ -146,10 +157,9 @@ void GuiCollectionSystemsOptions::createCollection(std::string inName)
ViewController::get()->goToSystemView(newSys);
Window* window = mWindow;
CollectionSystemManager::get()->setEditMode(name);
collSysMgr->setEditMode(name);
while(window->peekGui() && window->peekGui() != ViewController::get())
delete window->peekGui();
return;
}
void GuiCollectionSystemsOptions::openRandomCollectionSettings()

View file

@ -301,8 +301,7 @@ void GuiMenu::openUISettings()
{
Scripting::fireEvent("theme-changed", theme_set->getSelected(), oldTheme);
CollectionSystemManager::get()->updateSystemsList();
ViewController::get()->goToStart();
ViewController::get()->reloadAll(); // TODO - replace this with some sort of signal-based implementation
ViewController::get()->reloadAll(true); // TODO - replace this with some sort of signal-based implementation
}
});
}

View file

@ -555,7 +555,7 @@ void ViewController::reloadGameListView(IGameListView* view, bool reloadTheme)
}
void ViewController::reloadAll()
void ViewController::reloadAll(bool themeChanged)
{
// clear all gamelistviews
std::map<SystemData*, FileData*> cursorMap;
@ -567,7 +567,6 @@ void ViewController::reloadAll()
}
mGameListViews.clear();
// load themes, create gamelistviews and reset filters
for(auto it = cursorMap.cbegin(); it != cursorMap.cend(); it++)
{
@ -576,11 +575,14 @@ void ViewController::reloadAll()
getGameListView(it->first)->setCursor(it->second);
}
if(!themeChanged || !Settings::getInstance()->getBool("UseFullscreenPaging"))
{
// restore index of first list item on display
for(auto it = viewportTopMap.cbegin(); it != viewportTopMap.cend(); it++)
{
getGameListView(it->first)->setViewportTop(it->second);
}
}
// Rebuild SystemListView
mSystemListView.reset();

View file

@ -28,7 +28,7 @@ public:
// the current gamelist view (as it may change to be detailed).
void reloadGameListView(IGameListView* gamelist, bool reloadTheme = false);
inline void reloadGameListView(SystemData* system, bool reloadTheme = false) { reloadGameListView(getGameListView(system).get(), reloadTheme); }
void reloadAll(); // Reload everything with a theme. Used when the "ThemeSet" setting changes.
void reloadAll(bool themeChanged = false); // Reload everything with a theme. When the "ThemeSet" setting changes, themeChanged is true.
// Navigation.
void goToNextGameList();

View file

@ -66,9 +66,13 @@ void BasicGameListView::setCursor(FileData* cursor, bool refreshListCursorPos)
if (refreshListCursorPos)
setViewportTop(mList.REFRESH_LIST_CURSOR_POS);
if(!mList.setCursor(cursor) && (!cursor->isPlaceHolder()))
bool notInList = !mList.setCursor(cursor);
if(!refreshListCursorPos && notInList && !cursor->isPlaceHolder())
{
populateList(cursor->getParent()->getChildrenListToDisplay());
// this extra call is needed iff a system has games organized in folders
// and the cursor is focusing a game in a folder
if (cursor->getParent()->getType() == FOLDER)
mList.setCursor(cursor);
// update our cursor stack in case our cursor just got set to some folder we weren't in before

View file

@ -2,7 +2,9 @@
#include <SDL_events.h>
#ifdef WIN32
#include <codecvt>
#include <windows.h>
#include "Shlwapi.h"
#pragma comment(lib, "Shlwapi.lib")
#else
#include <unistd.h>
#endif
@ -31,12 +33,24 @@ int runRestartCommand()
int runSystemCommand(const std::string& cmd_utf8)
{
#ifdef WIN32
// on Windows we use _wsystem to support non-ASCII paths
// which requires converting from utf8 to a wstring
typedef std::codecvt_utf8<wchar_t> convert_type;
std::wstring_convert<convert_type, wchar_t> converter;
std::wstring wchar_str = converter.from_bytes(cmd_utf8);
return _wsystem(wchar_str.c_str());
std::string args = std::string(PathGetArgs(cmd_utf8.c_str()));
std::string program = cmd_utf8.substr(0, cmd_utf8.length() - args.length());
SHELLEXECUTEINFO ShExecInfo = {0};
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = NULL;
ShExecInfo.lpFile = program.c_str();
ShExecInfo.lpParameters = args.c_str();
ShExecInfo.lpDirectory = NULL;
ShExecInfo.nShow = SW_SHOW;
ShExecInfo.hInstApp = NULL;
ShellExecuteEx(&ShExecInfo);
WaitForSingleObject(ShExecInfo.hProcess, INFINITE);
CloseHandle(ShExecInfo.hProcess);
DWORD dwExitCode = 0;
GetExitCodeProcess(ShExecInfo.hProcess, &dwExitCode);
return dwExitCode;
#else
return system(cmd_utf8.c_str());
#endif