mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-29 19:55:37 +00:00
Merge remote-tracking branch 'horstbaerbel/master'
Conflicts: src/components/GuiGameList.cpp
This commit is contained in:
commit
d616b4a202
|
@ -9,9 +9,9 @@ class FileData
|
|||
{
|
||||
public:
|
||||
virtual ~FileData() { };
|
||||
virtual bool isFolder() = 0;
|
||||
virtual std::string getName() = 0;
|
||||
virtual std::string getPath() = 0;
|
||||
virtual bool isFolder() const = 0;
|
||||
virtual const std::string & getName() const = 0;
|
||||
virtual const std::string & getPath() const = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,19 +1,29 @@
|
|||
#include "FolderData.h"
|
||||
#include "SystemData.h"
|
||||
#include "GameData.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
bool FolderData::isFolder() { return true; }
|
||||
std::string FolderData::getName() { return mName; }
|
||||
std::string FolderData::getPath() { return mPath; }
|
||||
|
||||
std::map<FolderData::ComparisonFunction*, std::string> FolderData::sortStateNameMap;
|
||||
|
||||
bool FolderData::isFolder() const { return true; }
|
||||
const std::string & FolderData::getName() const { return mName; }
|
||||
const std::string & FolderData::getPath() const { return mPath; }
|
||||
unsigned int FolderData::getFileCount() { return mFileVector.size(); }
|
||||
FileData* FolderData::getFile(unsigned int i) { return mFileVector.at(i); }
|
||||
|
||||
|
||||
FolderData::FolderData(SystemData* system, std::string path, std::string name)
|
||||
: mSystem(system), mPath(path), mName(name)
|
||||
{
|
||||
mSystem = system;
|
||||
mPath = path;
|
||||
mName = name;
|
||||
//first created folder data initializes the list
|
||||
if (sortStateNameMap.empty()) {
|
||||
sortStateNameMap[compareFileName] = "file name";
|
||||
sortStateNameMap[compareRating] = "rating";
|
||||
sortStateNameMap[compareUserRating] = "user rating";
|
||||
sortStateNameMap[compareTimesPlayed] = "times played";
|
||||
sortStateNameMap[compareLastPlayed] = "last time played";
|
||||
}
|
||||
}
|
||||
|
||||
FolderData::~FolderData()
|
||||
|
@ -31,8 +41,24 @@ void FolderData::pushFileData(FileData* file)
|
|||
mFileVector.push_back(file);
|
||||
}
|
||||
|
||||
//sort this folder and any subfolders
|
||||
void FolderData::sort(ComparisonFunction & comparisonFunction, bool ascending)
|
||||
{
|
||||
std::sort(mFileVector.begin(), mFileVector.end(), comparisonFunction);
|
||||
|
||||
for(unsigned int i = 0; i < mFileVector.size(); i++)
|
||||
{
|
||||
if(mFileVector.at(i)->isFolder())
|
||||
((FolderData*)mFileVector.at(i))->sort(comparisonFunction, ascending);
|
||||
}
|
||||
|
||||
if (!ascending) {
|
||||
std::reverse(mFileVector.begin(), mFileVector.end());
|
||||
}
|
||||
}
|
||||
|
||||
//returns if file1 should come before file2
|
||||
bool filesort(FileData* file1, FileData* file2)
|
||||
bool FolderData::compareFileName(const FileData* file1, const FileData* file2)
|
||||
{
|
||||
std::string name1 = file1->getName();
|
||||
std::string name2 = file2->getName();
|
||||
|
@ -43,29 +69,118 @@ bool filesort(FileData* file1, FileData* file2)
|
|||
{
|
||||
if(toupper(name1[i]) != toupper(name2[i]))
|
||||
{
|
||||
if(toupper(name1[i]) < toupper(name2[i]))
|
||||
{
|
||||
return true;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
return toupper(name1[i]) < toupper(name2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if(name1.length() < name2.length())
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
return name1.length() < name2.length();
|
||||
}
|
||||
|
||||
//sort this folder and any subfolders
|
||||
void FolderData::sort()
|
||||
bool FolderData::compareRating(const FileData* file1, const FileData* file2)
|
||||
{
|
||||
std::sort(mFileVector.begin(), mFileVector.end(), filesort);
|
||||
|
||||
for(unsigned int i = 0; i < mFileVector.size(); i++)
|
||||
{
|
||||
if(mFileVector.at(i)->isFolder())
|
||||
((FolderData*)mFileVector.at(i))->sort();
|
||||
//we need game data. try to cast
|
||||
const GameData * game1 = dynamic_cast<const GameData*>(file1);
|
||||
const GameData * game2 = dynamic_cast<const GameData*>(file2);
|
||||
if (game1 != nullptr && game2 != nullptr) {
|
||||
return game1->getRating() < game2->getRating();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FolderData::compareUserRating(const FileData* file1, const FileData* file2)
|
||||
{
|
||||
//we need game data. try to cast
|
||||
const GameData * game1 = dynamic_cast<const GameData*>(file1);
|
||||
const GameData * game2 = dynamic_cast<const GameData*>(file2);
|
||||
if (game1 != nullptr && game2 != nullptr) {
|
||||
return game1->getUserRating() < game2->getUserRating();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FolderData::compareTimesPlayed(const FileData* file1, const FileData* file2)
|
||||
{
|
||||
//we need game data. try to cast
|
||||
const GameData * game1 = dynamic_cast<const GameData*>(file1);
|
||||
const GameData * game2 = dynamic_cast<const GameData*>(file2);
|
||||
if (game1 != nullptr && game2 != nullptr) {
|
||||
return game1->getTimesPlayed() < game2->getTimesPlayed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FolderData::compareLastPlayed(const FileData* file1, const FileData* file2)
|
||||
{
|
||||
//we need game data. try to cast
|
||||
const GameData * game1 = dynamic_cast<const GameData*>(file1);
|
||||
const GameData * game2 = dynamic_cast<const GameData*>(file2);
|
||||
if (game1 != nullptr && game2 != nullptr) {
|
||||
return game1->getLastPlayed() < game2->getLastPlayed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string FolderData::getSortStateName(ComparisonFunction & comparisonFunction, bool ascending)
|
||||
{
|
||||
std::string temp = sortStateNameMap[comparisonFunction];
|
||||
if (ascending) {
|
||||
temp.append(" (ascending)");
|
||||
}
|
||||
else {
|
||||
temp.append(" (descending)");
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
FileData* FolderData::getFile(unsigned int i) const
|
||||
{
|
||||
return mFileVector.at(i);
|
||||
}
|
||||
|
||||
std::vector<FileData*> FolderData::getFiles(bool onlyFiles) const
|
||||
{
|
||||
std::vector<FileData*> temp;
|
||||
//now check if a child is a folder and get those children in turn
|
||||
std::vector<FileData*>::const_iterator fdit = mFileVector.cbegin();
|
||||
while(fdit != mFileVector.cend()) {
|
||||
//dynamically try to cast to FolderData type
|
||||
FolderData * folder = dynamic_cast<FolderData*>(*fdit);
|
||||
if (folder != nullptr) {
|
||||
//add this only when user wanted it
|
||||
if (!onlyFiles) {
|
||||
temp.push_back(*fdit);
|
||||
}
|
||||
}
|
||||
else {
|
||||
temp.push_back(*fdit);
|
||||
}
|
||||
++fdit;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
|
||||
std::vector<FileData*> FolderData::getFilesRecursive(bool onlyFiles) const
|
||||
{
|
||||
std::vector<FileData*> temp;
|
||||
//now check if a child is a folder and get those children in turn
|
||||
std::vector<FileData*>::const_iterator fdit = mFileVector.cbegin();
|
||||
while(fdit != mFileVector.cend()) {
|
||||
//dynamically try to cast to FolderData type
|
||||
FolderData * folder = dynamic_cast<FolderData*>(*fdit);
|
||||
if (folder != nullptr) {
|
||||
//add this onyl when user wanted it
|
||||
if (!onlyFiles) {
|
||||
temp.push_back(*fdit);
|
||||
}
|
||||
//worked. Is actual folder data. recurse
|
||||
std::vector<FileData*> children = folder->getFilesRecursive(onlyFiles);
|
||||
//insert children into return vector
|
||||
temp.insert(temp.end(), children.cbegin(), children.cend());
|
||||
}
|
||||
else {
|
||||
temp.push_back(*fdit);
|
||||
}
|
||||
++fdit;
|
||||
}
|
||||
return temp;
|
||||
}
|
|
@ -1,28 +1,54 @@
|
|||
#ifndef _FOLDER_H_
|
||||
#define _FOLDER_H_
|
||||
|
||||
#include "FileData.h"
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "FileData.h"
|
||||
|
||||
|
||||
class SystemData;
|
||||
|
||||
//This class lets us hold a vector of FileDatas within under a common name.
|
||||
class FolderData : public FileData
|
||||
{
|
||||
public:
|
||||
typedef bool ComparisonFunction(const FileData* a, const FileData* b);
|
||||
struct SortState
|
||||
{
|
||||
ComparisonFunction & comparisonFunction;
|
||||
bool ascending;
|
||||
std::string description;
|
||||
|
||||
SortState(ComparisonFunction & sortFunction, bool sortAscending, const std::string & sortDescription) : comparisonFunction(sortFunction), ascending(sortAscending), description(sortDescription) {}
|
||||
};
|
||||
|
||||
private:
|
||||
static std::map<ComparisonFunction*, std::string> sortStateNameMap;
|
||||
|
||||
public:
|
||||
FolderData(SystemData* system, std::string path, std::string name);
|
||||
~FolderData();
|
||||
|
||||
bool isFolder();
|
||||
std::string getName();
|
||||
std::string getPath();
|
||||
bool isFolder() const;
|
||||
const std::string & getName() const;
|
||||
const std::string & getPath() const;
|
||||
|
||||
unsigned int getFileCount();
|
||||
FileData* getFile(unsigned int i);
|
||||
FileData* getFile(unsigned int i) const;
|
||||
std::vector<FileData*> getFiles(bool onlyFiles = false) const;
|
||||
std::vector<FileData*> getFilesRecursive(bool onlyFiles = false) const;
|
||||
|
||||
void pushFileData(FileData* file);
|
||||
|
||||
void sort();
|
||||
void sort(ComparisonFunction & comparisonFunction = compareFileName, bool ascending = true);
|
||||
static bool compareFileName(const FileData* file1, const FileData* file2);
|
||||
static bool compareRating(const FileData* file1, const FileData* file2);
|
||||
static bool compareUserRating(const FileData* file1, const FileData* file2);
|
||||
static bool compareTimesPlayed(const FileData* file1, const FileData* file2);
|
||||
static bool compareLastPlayed(const FileData* file1, const FileData* file2);
|
||||
static std::string getSortStateName(ComparisonFunction & comparisonFunction = compareFileName, bool ascending = true);
|
||||
|
||||
private:
|
||||
SystemData* mSystem;
|
||||
std::string mPath;
|
||||
|
|
123
src/GameData.cpp
123
src/GameData.cpp
|
@ -2,23 +2,110 @@
|
|||
#include <boost/filesystem.hpp>
|
||||
#include <iostream>
|
||||
|
||||
bool GameData::isFolder() { return false; }
|
||||
std::string GameData::getName() { return mName; }
|
||||
std::string GameData::getPath() { return mPath; }
|
||||
std::string GameData::getDescription() { return mDescription; }
|
||||
std::string GameData::getImagePath() { return mImagePath; }
|
||||
|
||||
const std::string GameData::xmlTagGameList = "gameList";
|
||||
const std::string GameData::xmlTagGame = "game";
|
||||
const std::string GameData::xmlTagName = "name";
|
||||
const std::string GameData::xmlTagPath = "path";
|
||||
const std::string GameData::xmlTagDescription = "desc";
|
||||
const std::string GameData::xmlTagImagePath = "image";
|
||||
const std::string GameData::xmlTagRating = "rating";
|
||||
const std::string GameData::xmlTagUserRating = "userrating";
|
||||
const std::string GameData::xmlTagTimesPlayed = "timesplayed";
|
||||
const std::string GameData::xmlTagLastPlayed = "lastplayed";
|
||||
|
||||
|
||||
GameData::GameData(SystemData* system, std::string path, std::string name)
|
||||
: mSystem(system), mPath(path), mName(name), mRating(0.0f), mUserRating(0.0f), mTimesPlayed(0), mLastPlayed(0)
|
||||
{
|
||||
mSystem = system;
|
||||
mPath = path;
|
||||
mName = name;
|
||||
|
||||
mDescription = "";
|
||||
mImagePath = "";
|
||||
}
|
||||
|
||||
std::string GameData::getBashPath()
|
||||
bool GameData::isFolder() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string & GameData::getName() const
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
void GameData::setName(const std::string & name)
|
||||
{
|
||||
mName = name;
|
||||
}
|
||||
|
||||
const std::string & GameData::getPath() const
|
||||
{
|
||||
return mPath;
|
||||
}
|
||||
|
||||
void GameData::setPath(const std::string & path)
|
||||
{
|
||||
mPath = path;
|
||||
}
|
||||
|
||||
const std::string & GameData::getDescription() const
|
||||
{
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
void GameData::setDescription(const std::string & description)
|
||||
{
|
||||
mDescription = description;
|
||||
}
|
||||
|
||||
const std::string & GameData::getImagePath() const
|
||||
{
|
||||
return mImagePath;
|
||||
}
|
||||
|
||||
void GameData::setImagePath(const std::string & imagePath)
|
||||
{
|
||||
mImagePath = imagePath;
|
||||
}
|
||||
|
||||
float GameData::getRating() const
|
||||
{
|
||||
return mRating;
|
||||
}
|
||||
|
||||
void GameData::setRating(float rating)
|
||||
{
|
||||
mRating = rating;
|
||||
}
|
||||
|
||||
float GameData::getUserRating() const
|
||||
{
|
||||
return mUserRating;
|
||||
}
|
||||
|
||||
void GameData::setUserRating(float rating)
|
||||
{
|
||||
mUserRating = rating;
|
||||
}
|
||||
|
||||
size_t GameData::getTimesPlayed() const
|
||||
{
|
||||
return mTimesPlayed;
|
||||
}
|
||||
|
||||
void GameData::setTimesPlayed(size_t timesPlayed)
|
||||
{
|
||||
mTimesPlayed = timesPlayed;
|
||||
}
|
||||
|
||||
std::time_t GameData::getLastPlayed() const
|
||||
{
|
||||
return mLastPlayed;
|
||||
}
|
||||
|
||||
void GameData::setLastPlayed(std::time_t lastPlayed)
|
||||
{
|
||||
mLastPlayed = lastPlayed;
|
||||
}
|
||||
|
||||
std::string GameData::getBashPath() const
|
||||
{
|
||||
//a quick and dirty way to insert a backslash before most characters that would mess up a bash path
|
||||
std::string path = mPath;
|
||||
|
@ -44,18 +131,8 @@ std::string GameData::getBashPath()
|
|||
}
|
||||
|
||||
//returns the boost::filesystem stem of our path - e.g. for "/foo/bar.rom" returns "bar"
|
||||
std::string GameData::getBaseName()
|
||||
std::string GameData::getBaseName() const
|
||||
{
|
||||
boost::filesystem::path path(mPath);
|
||||
return path.stem().string();
|
||||
}
|
||||
|
||||
void GameData::set(std::string name, std::string description, std::string imagePath)
|
||||
{
|
||||
if(!name.empty())
|
||||
mName = name;
|
||||
if(!description.empty())
|
||||
mDescription = description;
|
||||
if(!imagePath.empty() && boost::filesystem::exists(imagePath))
|
||||
mImagePath = imagePath;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#define _GAMEDATA_H_
|
||||
|
||||
#include <string>
|
||||
#include <ctime>
|
||||
|
||||
#include "FileData.h"
|
||||
#include "SystemData.h"
|
||||
|
||||
|
@ -9,19 +11,49 @@
|
|||
class GameData : public FileData
|
||||
{
|
||||
public:
|
||||
//static tag names for reading/writing XML documents. This might fail in PUGIXML_WCHAR_MODE
|
||||
//TODO: The class should have member to read fromXML() and write toXML() probably...
|
||||
static const std::string xmlTagGameList;
|
||||
static const std::string xmlTagGame;
|
||||
static const std::string xmlTagName;
|
||||
static const std::string xmlTagPath;
|
||||
static const std::string xmlTagDescription;
|
||||
static const std::string xmlTagImagePath;
|
||||
static const std::string xmlTagRating;
|
||||
static const std::string xmlTagUserRating;
|
||||
static const std::string xmlTagTimesPlayed;
|
||||
static const std::string xmlTagLastPlayed;
|
||||
|
||||
GameData(SystemData* system, std::string path, std::string name);
|
||||
|
||||
void set(std::string name = "", std::string description = "", std::string imagePath = "");
|
||||
const std::string & getName() const;
|
||||
void setName(const std::string & name);
|
||||
|
||||
std::string getName();
|
||||
std::string getPath();
|
||||
std::string getBashPath();
|
||||
std::string getBaseName();
|
||||
const std::string & getPath() const;
|
||||
void setPath(const std::string & path);
|
||||
|
||||
std::string getDescription();
|
||||
std::string getImagePath();
|
||||
const std::string & getDescription() const;
|
||||
void setDescription(const std::string & description);
|
||||
|
||||
bool isFolder();
|
||||
const std::string & getImagePath() const;
|
||||
void setImagePath(const std::string & imagePath);
|
||||
|
||||
float getRating() const;
|
||||
void setRating(float rating);
|
||||
|
||||
float getUserRating() const;
|
||||
void setUserRating(float rating);
|
||||
|
||||
size_t getTimesPlayed() const;
|
||||
void setTimesPlayed(size_t timesPlayed);
|
||||
|
||||
std::time_t getLastPlayed() const;
|
||||
void setLastPlayed(std::time_t lastPlayed);
|
||||
|
||||
std::string getBashPath() const;
|
||||
std::string getBaseName() const;
|
||||
|
||||
bool isFolder() const;
|
||||
private:
|
||||
SystemData* mSystem;
|
||||
std::string mPath;
|
||||
|
@ -30,6 +62,10 @@ private:
|
|||
//extra data
|
||||
std::string mDescription;
|
||||
std::string mImagePath;
|
||||
float mRating;
|
||||
float mUserRating;
|
||||
size_t mTimesPlayed;
|
||||
std::time_t mLastPlayed;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -102,6 +102,19 @@ Vector2u GuiComponent::getSize()
|
|||
return mSize;
|
||||
}
|
||||
|
||||
void GuiComponent::setSize(Vector2u size)
|
||||
{
|
||||
mSize = size;
|
||||
onSizeChanged();
|
||||
}
|
||||
|
||||
void GuiComponent::setSize(unsigned int w, unsigned int h)
|
||||
{
|
||||
mSize.x = w;
|
||||
mSize.y = h;
|
||||
onSizeChanged();
|
||||
}
|
||||
|
||||
//Children stuff.
|
||||
void GuiComponent::addChild(GuiComponent* cmp)
|
||||
{
|
||||
|
@ -166,4 +179,4 @@ unsigned char GuiComponent::getOpacity()
|
|||
void GuiComponent::setOpacity(unsigned char opacity)
|
||||
{
|
||||
mOpacity = opacity;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,9 @@ public:
|
|||
virtual void onOffsetChanged() {};
|
||||
|
||||
Vector2u getSize();
|
||||
void setSize(Vector2u size);
|
||||
void setSize(unsigned int w, unsigned int h);
|
||||
virtual void onSizeChanged() {};
|
||||
|
||||
void setParent(GuiComponent* parent);
|
||||
GuiComponent* getParent();
|
||||
|
|
|
@ -423,6 +423,9 @@ void InputManager::loadDefaultConfig()
|
|||
|
||||
cfg->mapInput("mastervolup", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_PLUS, 1, true));
|
||||
cfg->mapInput("mastervoldown", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_MINUS, 1, true));
|
||||
|
||||
cfg->mapInput("sortordernext", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_F7, 1, true));
|
||||
cfg->mapInput("sortorderprevious", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_F8, 1, true));
|
||||
}
|
||||
|
||||
void InputManager::writeConfig()
|
||||
|
|
|
@ -33,6 +33,8 @@ void Settings::setDefaults()
|
|||
mBoolMap["WINDOWED"] = false;
|
||||
|
||||
mIntMap["DIMTIME"] = 30*1000;
|
||||
|
||||
mIntMap["GameListSortIndex"] = 0;
|
||||
}
|
||||
|
||||
template <typename K, typename V>
|
||||
|
|
|
@ -50,6 +50,10 @@ SystemData::SystemData(std::string name, std::string descName, std::string start
|
|||
|
||||
SystemData::~SystemData()
|
||||
{
|
||||
//save changed game data back to xml
|
||||
if(!Settings::getInstance()->getBool("IGNOREGAMELIST")) {
|
||||
updateGamelist(this);
|
||||
}
|
||||
delete mRootFolder;
|
||||
}
|
||||
|
||||
|
@ -90,6 +94,10 @@ void SystemData::launchGame(Window* window, GameData* game)
|
|||
window->init();
|
||||
VolumeControl::getInstance()->init();
|
||||
AudioManager::getInstance()->init();
|
||||
|
||||
//update number of times the game has been launched and the time
|
||||
game->setTimesPlayed(game->getTimesPlayed() + 1);
|
||||
game->setLastPlayed(std::time(nullptr));
|
||||
}
|
||||
|
||||
void SystemData::populateFolder(FolderData* folder)
|
||||
|
|
|
@ -117,19 +117,19 @@ void parseGamelist(SystemData* system)
|
|||
return;
|
||||
}
|
||||
|
||||
pugi::xml_node root = doc.child("gameList");
|
||||
pugi::xml_node root = doc.child(GameData::xmlTagGameList.c_str());
|
||||
if(!root)
|
||||
{
|
||||
LOG(LogError) << "Could not find <gameList> node in gamelist \"" << xmlpath << "\"!";
|
||||
LOG(LogError) << "Could not find <" << GameData::xmlTagGameList << "> node in gamelist \"" << xmlpath << "\"!";
|
||||
return;
|
||||
}
|
||||
|
||||
for(pugi::xml_node gameNode = root.child("game"); gameNode; gameNode = gameNode.next_sibling("game"))
|
||||
for(pugi::xml_node gameNode = root.child(GameData::xmlTagGame.c_str()); gameNode; gameNode = gameNode.next_sibling(GameData::xmlTagGame.c_str()))
|
||||
{
|
||||
pugi::xml_node pathNode = gameNode.child("path");
|
||||
pugi::xml_node pathNode = gameNode.child(GameData::xmlTagPath.c_str());
|
||||
if(!pathNode)
|
||||
{
|
||||
LOG(LogError) << "<game> node contains no <path> child!";
|
||||
LOG(LogError) << "<" << GameData::xmlTagGame << "> node contains no <" << GameData::xmlTagPath << "> child!";
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -152,13 +152,17 @@ void parseGamelist(SystemData* system)
|
|||
//actually gather the information in the XML doc, then pass it to the game's set method
|
||||
std::string newName, newDesc, newImage;
|
||||
|
||||
if(gameNode.child("name"))
|
||||
newName = gameNode.child("name").text().get();
|
||||
if(gameNode.child("desc"))
|
||||
newDesc = gameNode.child("desc").text().get();
|
||||
if(gameNode.child("image"))
|
||||
if(gameNode.child(GameData::xmlTagName.c_str()))
|
||||
{
|
||||
newImage = gameNode.child("image").text().get();
|
||||
game->setName(gameNode.child(GameData::xmlTagName.c_str()).text().get());
|
||||
}
|
||||
if(gameNode.child(GameData::xmlTagDescription.c_str()))
|
||||
{
|
||||
game->setDescription(gameNode.child(GameData::xmlTagDescription.c_str()).text().get());
|
||||
}
|
||||
if(gameNode.child(GameData::xmlTagImagePath.c_str()))
|
||||
{
|
||||
newImage = gameNode.child(GameData::xmlTagImagePath.c_str()).text().get();
|
||||
|
||||
//expand "."
|
||||
if(newImage[0] == '.')
|
||||
|
@ -168,15 +172,148 @@ void parseGamelist(SystemData* system)
|
|||
newImage.insert(0, pathname.parent_path().string() );
|
||||
}
|
||||
|
||||
//if the image doesn't exist, forget it
|
||||
if(!boost::filesystem::exists(newImage))
|
||||
newImage = "";
|
||||
//if the image exist, set it
|
||||
if(boost::filesystem::exists(newImage))
|
||||
{
|
||||
game->setImagePath(newImage);
|
||||
}
|
||||
}
|
||||
|
||||
game->set(newName, newDesc, newImage);
|
||||
|
||||
}else{
|
||||
//get rating and the times played from the XML doc
|
||||
if(gameNode.child(GameData::xmlTagRating.c_str()))
|
||||
{
|
||||
float rating;
|
||||
std::istringstream(gameNode.child(GameData::xmlTagRating.c_str()).text().get()) >> rating;
|
||||
game->setRating(rating);
|
||||
}
|
||||
if(gameNode.child(GameData::xmlTagUserRating.c_str()))
|
||||
{
|
||||
float userRating;
|
||||
std::istringstream(gameNode.child(GameData::xmlTagUserRating.c_str()).text().get()) >> userRating;
|
||||
game->setUserRating(userRating);
|
||||
}
|
||||
if(gameNode.child(GameData::xmlTagTimesPlayed.c_str()))
|
||||
{
|
||||
size_t timesPlayed;
|
||||
std::istringstream(gameNode.child(GameData::xmlTagTimesPlayed.c_str()).text().get()) >> timesPlayed;
|
||||
game->setTimesPlayed(timesPlayed);
|
||||
}
|
||||
if(gameNode.child(GameData::xmlTagLastPlayed.c_str()))
|
||||
{
|
||||
std::time_t lastPlayed;
|
||||
std::istringstream(gameNode.child(GameData::xmlTagLastPlayed.c_str()).text().get()) >> lastPlayed;
|
||||
game->setLastPlayed(lastPlayed);
|
||||
}
|
||||
}
|
||||
else{
|
||||
LOG(LogWarning) << "Game at \"" << path << "\" does not exist!";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addGameDataNode(pugi::xml_node & parent, const GameData * game)
|
||||
{
|
||||
//create game and add to parent node
|
||||
pugi::xml_node newGame = parent.append_child(GameData::xmlTagGame.c_str());
|
||||
//add values
|
||||
if (!game->getPath().empty()) {
|
||||
pugi::xml_node pathNode = newGame.append_child(GameData::xmlTagPath.c_str());
|
||||
pathNode.text().set(game->getPath().c_str());
|
||||
}
|
||||
if (!game->getName().empty()) {
|
||||
pugi::xml_node nameNode = newGame.append_child(GameData::xmlTagName.c_str());
|
||||
nameNode.text().set(game->getName().c_str());
|
||||
}
|
||||
if (!game->getDescription().empty()) {
|
||||
pugi::xml_node descriptionNode = newGame.append_child(GameData::xmlTagDescription.c_str());
|
||||
descriptionNode.text().set(game->getDescription().c_str());
|
||||
}
|
||||
if (!game->getImagePath().empty()) {
|
||||
pugi::xml_node imagePathNode = newGame.append_child(GameData::xmlTagImagePath.c_str());
|
||||
imagePathNode.text().set(game->getImagePath().c_str());
|
||||
}
|
||||
//all other values are added regardless of their value
|
||||
pugi::xml_node ratingNode = newGame.append_child(GameData::xmlTagRating.c_str());
|
||||
ratingNode.text().set(std::to_string((long double)game->getRating()).c_str());
|
||||
|
||||
pugi::xml_node userRatingNode = newGame.append_child(GameData::xmlTagUserRating.c_str());
|
||||
userRatingNode.text().set(std::to_string((long double)game->getUserRating()).c_str());
|
||||
|
||||
pugi::xml_node timesPlayedNode = newGame.append_child(GameData::xmlTagTimesPlayed.c_str());
|
||||
timesPlayedNode.text().set(std::to_string((unsigned long long)game->getTimesPlayed()).c_str());
|
||||
|
||||
pugi::xml_node lastPlayedNode = newGame.append_child(GameData::xmlTagLastPlayed.c_str());
|
||||
lastPlayedNode.text().set(std::to_string((unsigned long long)game->getLastPlayed()).c_str());
|
||||
}
|
||||
|
||||
void updateGamelist(SystemData* system)
|
||||
{
|
||||
//We do this by reading the XML again, adding changes and then writing it back,
|
||||
//because there might be information missing in our systemdata which would then miss in the new XML.
|
||||
//We have the complete information for every game though, so we can simply remove a game
|
||||
//we already have in the system from the XML, and then add it back from its GameData information...
|
||||
|
||||
std::string xmlpath = system->getGamelistPath();
|
||||
if(xmlpath.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOG(LogInfo) << "Parsing XML file \"" << xmlpath << "\" before writing...";
|
||||
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result result = doc.load_file(xmlpath.c_str());
|
||||
|
||||
if(!result) {
|
||||
LOG(LogError) << "Error parsing XML file \"" << xmlpath << "\"!\n " << result.description();
|
||||
return;
|
||||
}
|
||||
|
||||
pugi::xml_node root = doc.child(GameData::xmlTagGameList.c_str());
|
||||
if(!root) {
|
||||
LOG(LogError) << "Could not find <" << GameData::xmlTagGameList << "> node in gamelist \"" << xmlpath << "\"!";
|
||||
return;
|
||||
}
|
||||
|
||||
//now we have all the information from the XML. now iterate through all our games and add information from there
|
||||
FolderData * rootFolder = system->getRootFolder();
|
||||
if (rootFolder != nullptr) {
|
||||
//get only files, no folders
|
||||
std::vector<FileData*> files = rootFolder->getFilesRecursive(true);
|
||||
//iterate through all files, checking if they're already in the XML
|
||||
std::vector<FileData*>::const_iterator fit = files.cbegin();
|
||||
while(fit != files.cend()) {
|
||||
//try to cast to gamedata
|
||||
const GameData * game = dynamic_cast<const GameData*>(*fit);
|
||||
if (game != nullptr) {
|
||||
//worked. check if this games' path can be found somewhere in the XML
|
||||
for(pugi::xml_node gameNode = root.child(GameData::xmlTagGame.c_str()); gameNode; gameNode = gameNode.next_sibling(GameData::xmlTagGame.c_str())) {
|
||||
//get path from game node
|
||||
pugi::xml_node pathNode = gameNode.child(GameData::xmlTagPath.c_str());
|
||||
if(!pathNode)
|
||||
{
|
||||
LOG(LogError) << "<" << GameData::xmlTagGame << "> node contains no <" << GameData::xmlTagPath << "> child!";
|
||||
continue;
|
||||
}
|
||||
//check path
|
||||
if (pathNode.text().get() == game->getPath()) {
|
||||
//found the game. remove it. it will be added again later with updated values
|
||||
root.remove_child(gameNode);
|
||||
//break node search loop
|
||||
break;
|
||||
}
|
||||
}
|
||||
//either the game content was removed, because it needs to be updated,
|
||||
//or didn't exist in the first place, so just add it
|
||||
addGameDataNode(root, game);
|
||||
}
|
||||
++fit;
|
||||
}
|
||||
//now write the file
|
||||
if (!doc.save_file(xmlpath.c_str())) {
|
||||
LOG(LogError) << "Error saving XML file \"" << xmlpath << "\"!";
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOG(LogError) << "Found no root folder for system \"" << system->getName() << "\"!";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,4 +7,7 @@ class SystemData;
|
|||
//Loads gamelist.xml data into a SystemData.
|
||||
void parseGamelist(SystemData* system);
|
||||
|
||||
//Writes changes to SystemData back to a previously loaded gamelist.xml.
|
||||
void updateGamelist(SystemData* system);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,17 +7,15 @@ const std::string GuiFastSelect::LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|||
const int GuiFastSelect::SCROLLSPEED = 100;
|
||||
const int GuiFastSelect::SCROLLDELAY = 507;
|
||||
|
||||
GuiFastSelect::GuiFastSelect(Window* window, GuiGameList* parent, TextListComponent<FileData*>* list, char startLetter, GuiBoxData data,
|
||||
int textcolor, std::shared_ptr<Sound> & scrollsound, Font* font) : GuiComponent(window)
|
||||
GuiFastSelect::GuiFastSelect(Window* window, GuiGameList* parent, TextListComponent<FileData*>* list, char startLetter, ThemeComponent * theme)
|
||||
: GuiComponent(window), mParent(parent), mList(list), mTheme(theme)
|
||||
{
|
||||
mLetterID = LETTERS.find(toupper(startLetter));
|
||||
if(mLetterID == std::string::npos)
|
||||
mLetterID = 0;
|
||||
|
||||
mParent = parent;
|
||||
mList = list;
|
||||
mScrollSound = scrollsound;
|
||||
mFont = font;
|
||||
mScrollSound = mTheme->getSound("menuScroll");
|
||||
mTextColor = mTheme->getColor("fastSelect");
|
||||
|
||||
mScrolling = false;
|
||||
mScrollTimer = 0;
|
||||
|
@ -25,9 +23,7 @@ GuiFastSelect::GuiFastSelect(Window* window, GuiGameList* parent, TextListCompon
|
|||
|
||||
unsigned int sw = Renderer::getScreenWidth(), sh = Renderer::getScreenHeight();
|
||||
mBox = new GuiBox(window, (int)(sw * 0.2f), (int)(sh * 0.2f), (int)(sw * 0.6f), (int)(sh * 0.6f));
|
||||
mBox->setData(data);
|
||||
|
||||
mTextColor = textcolor;
|
||||
mBox->setData(mTheme->getBoxData());
|
||||
}
|
||||
|
||||
GuiFastSelect::~GuiFastSelect()
|
||||
|
@ -41,11 +37,14 @@ void GuiFastSelect::render()
|
|||
unsigned int sw = Renderer::getScreenWidth(), sh = Renderer::getScreenHeight();
|
||||
|
||||
if(!mBox->hasBackground())
|
||||
Renderer::drawRect((int)(sw * 0.2f), (int)(sh * 0.2f), (int)(sw * 0.6f), (int)(sh * 0.6f), 0x000FF0FF);
|
||||
Renderer::drawRect((int)(sw * 0.3f), (int)(sh * 0.3f), (int)(sw * 0.4f), (int)(sh * 0.4f), 0x000FF0AA);
|
||||
|
||||
mBox->render();
|
||||
|
||||
Renderer::drawCenteredText(LETTERS.substr(mLetterID, 1), 0, (int)(sh * 0.5f - (mFont->getHeight() * 0.5f)), mTextColor, mFont);
|
||||
Renderer::drawCenteredText(LETTERS.substr(mLetterID, 1), 0, (int)(sh * 0.5f - (mTheme->getFastSelectFont()->getHeight() * 0.5f)), mTextColor, mTheme->getFastSelectFont());
|
||||
Renderer::drawCenteredText("Sort order:", 0, (int)(sh * 0.6f - (mTheme->getDescriptionFont()->getHeight() * 0.5f)), mTextColor, mTheme->getDescriptionFont());
|
||||
std::string sortString = "<- " + mParent->getSortState().description + " ->";
|
||||
Renderer::drawCenteredText(sortString, 0, (int)(sh * 0.6f + (mTheme->getDescriptionFont()->getHeight() * 0.5f)), mTextColor, mTheme->getDescriptionFont());
|
||||
}
|
||||
|
||||
bool GuiFastSelect::input(InputConfig* config, Input input)
|
||||
|
@ -64,6 +63,19 @@ bool GuiFastSelect::input(InputConfig* config, Input input)
|
|||
return true;
|
||||
}
|
||||
|
||||
if(config->isMappedTo("left", input) && input.value != 0)
|
||||
{
|
||||
mParent->setPreviousSortIndex();
|
||||
mScrollSound->play();
|
||||
return true;
|
||||
}
|
||||
else if(config->isMappedTo("right", input) && input.value != 0)
|
||||
{
|
||||
mParent->setNextSortIndex();
|
||||
mScrollSound->play();
|
||||
return true;
|
||||
}
|
||||
|
||||
if((config->isMappedTo("up", input) || config->isMappedTo("down", input)) && input.value == 0)
|
||||
{
|
||||
mScrolling = false;
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#include "../SystemData.h"
|
||||
#include "../FolderData.h"
|
||||
#include "../Sound.h"
|
||||
#include "ThemeComponent.h"
|
||||
#include "TextListComponent.h"
|
||||
#include "GuiBox.h"
|
||||
|
||||
|
@ -13,8 +14,7 @@ class GuiGameList;
|
|||
class GuiFastSelect : public GuiComponent
|
||||
{
|
||||
public:
|
||||
GuiFastSelect(Window* window, GuiGameList* parent, TextListComponent<FileData*>* list, char startLetter, GuiBoxData data,
|
||||
int textcolor, std::shared_ptr<Sound> & scrollsound, Font* font);
|
||||
GuiFastSelect(Window* window, GuiGameList* parent, TextListComponent<FileData*>* list, char startLetter, ThemeComponent * theme);
|
||||
~GuiFastSelect();
|
||||
|
||||
bool input(InputConfig* config, Input input);
|
||||
|
@ -41,7 +41,7 @@ private:
|
|||
bool mScrolling;
|
||||
|
||||
std::shared_ptr<Sound> mScrollSound;
|
||||
Font* mFont;
|
||||
ThemeComponent * mTheme;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
#include "../Log.h"
|
||||
#include "../Settings.h"
|
||||
|
||||
|
||||
std::vector<FolderData::SortState> GuiGameList::sortStates;
|
||||
|
||||
|
||||
Vector2i GuiGameList::getImagePos()
|
||||
{
|
||||
return Vector2i((int)(Renderer::getScreenWidth() * mTheme->getFloat("gameImageOffsetX")), (int)(Renderer::getScreenHeight() * mTheme->getFloat("gameImageOffsetY")));
|
||||
|
@ -26,8 +30,23 @@ GuiGameList::GuiGameList(Window* window) : GuiComponent(window),
|
|||
mScreenshot(window),
|
||||
mDescription(window),
|
||||
mDescContainer(window),
|
||||
mTransitionImage(window, 0, 0, "", Renderer::getScreenWidth(), Renderer::getScreenHeight(), true)
|
||||
mTransitionImage(window, 0, 0, "", Renderer::getScreenWidth(), Renderer::getScreenHeight(), true),
|
||||
sortStateIndex(Settings::getInstance()->getInt("GameListSortIndex"))
|
||||
{
|
||||
//first object initializes the vector
|
||||
if (sortStates.empty()) {
|
||||
sortStates.push_back(FolderData::SortState(FolderData::compareFileName, true, "file name, ascending"));
|
||||
sortStates.push_back(FolderData::SortState(FolderData::compareFileName, false, "file name, descending"));
|
||||
sortStates.push_back(FolderData::SortState(FolderData::compareRating, true, "database rating, ascending"));
|
||||
sortStates.push_back(FolderData::SortState(FolderData::compareRating, false, "database rating, descending"));
|
||||
sortStates.push_back(FolderData::SortState(FolderData::compareUserRating, true, "your rating, ascending"));
|
||||
sortStates.push_back(FolderData::SortState(FolderData::compareUserRating, false, "your rating, descending"));
|
||||
sortStates.push_back(FolderData::SortState(FolderData::compareTimesPlayed, true, "played least often"));
|
||||
sortStates.push_back(FolderData::SortState(FolderData::compareTimesPlayed, false, "played most often"));
|
||||
sortStates.push_back(FolderData::SortState(FolderData::compareLastPlayed, true, "played least recently"));
|
||||
sortStates.push_back(FolderData::SortState(FolderData::compareLastPlayed, false, "played most recently"));
|
||||
}
|
||||
|
||||
mImageAnimation.addChild(&mScreenshot);
|
||||
mDescContainer.addChild(&mDescription);
|
||||
|
||||
|
@ -175,6 +194,16 @@ bool GuiGameList::input(InputConfig* config, Input input)
|
|||
}
|
||||
}
|
||||
|
||||
//change sort order
|
||||
if(config->isMappedTo("sortordernext", input) && input.value != 0) {
|
||||
setNextSortIndex();
|
||||
//std::cout << "Sort order is " << FolderData::getSortStateName(sortStates.at(sortStateIndex).comparisonFunction, sortStates.at(sortStateIndex).ascending) << std::endl;
|
||||
}
|
||||
else if(config->isMappedTo("sortorderprevious", input) && input.value != 0) {
|
||||
setPreviousSortIndex();
|
||||
//std::cout << "Sort order is " << FolderData::getSortStateName(sortStates.at(sortStateIndex).comparisonFunction, sortStates.at(sortStateIndex).ascending) << std::endl;
|
||||
}
|
||||
|
||||
//open the "start menu"
|
||||
if(config->isMappedTo("menu", input) && input.value != 0)
|
||||
{
|
||||
|
@ -185,7 +214,7 @@ bool GuiGameList::input(InputConfig* config, Input input)
|
|||
//open the fast select menu
|
||||
if(config->isMappedTo("select", input) && input.value != 0)
|
||||
{
|
||||
mWindow->pushGui(new GuiFastSelect(mWindow, this, &mList, mList.getSelectedObject()->getName()[0], mTheme->getBoxData(), mTheme->getColor("fastSelect"), mTheme->getSound("menuScroll"), mTheme->getFastSelectFont()));
|
||||
mWindow->pushGui(new GuiFastSelect(mWindow, this, &mList, mList.getSelectedObject()->getName()[0], mTheme));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -204,6 +233,52 @@ bool GuiGameList::input(InputConfig* config, Input input)
|
|||
return false;
|
||||
}
|
||||
|
||||
const FolderData::SortState & GuiGameList::getSortState() const
|
||||
{
|
||||
return sortStates.at(sortStateIndex);
|
||||
}
|
||||
|
||||
void GuiGameList::setSortIndex(size_t index)
|
||||
{
|
||||
//make the index valid
|
||||
if (index >= sortStates.size()) {
|
||||
index = 0;
|
||||
}
|
||||
if (index != sortStateIndex) {
|
||||
//get sort state from vector and sort list
|
||||
sortStateIndex = index;
|
||||
sort(sortStates.at(sortStateIndex).comparisonFunction, sortStates.at(sortStateIndex).ascending);
|
||||
}
|
||||
//save new index to settings
|
||||
Settings::getInstance()->setInt("GameListSortIndex", sortStateIndex);
|
||||
}
|
||||
|
||||
void GuiGameList::setNextSortIndex()
|
||||
{
|
||||
//make the index wrap around
|
||||
if ((sortStateIndex - 1) >= sortStates.size()) {
|
||||
setSortIndex(0);
|
||||
}
|
||||
setSortIndex(sortStateIndex + 1);
|
||||
}
|
||||
|
||||
void GuiGameList::setPreviousSortIndex()
|
||||
{
|
||||
//make the index wrap around
|
||||
if (((int)sortStateIndex - 1) < 0) {
|
||||
setSortIndex(sortStates.size() - 1);
|
||||
}
|
||||
setSortIndex(sortStateIndex - 1);
|
||||
}
|
||||
|
||||
void GuiGameList::sort(FolderData::ComparisonFunction & comparisonFunction, bool ascending)
|
||||
{
|
||||
//resort list and update it
|
||||
mFolder->sort(comparisonFunction, ascending);
|
||||
updateList();
|
||||
updateDetailData();
|
||||
}
|
||||
|
||||
void GuiGameList::updateList()
|
||||
{
|
||||
mList.clear();
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
//It has a TextListComponent child that handles the game list, a ThemeComponent that handles the theming system, and an ImageComponent for game images.
|
||||
class GuiGameList : public GuiComponent
|
||||
{
|
||||
static std::vector<FolderData::SortState> sortStates;
|
||||
size_t sortStateIndex;
|
||||
|
||||
public:
|
||||
GuiGameList(Window* window);
|
||||
virtual ~GuiGameList();
|
||||
|
@ -33,6 +36,12 @@ public:
|
|||
|
||||
void updateDetailData();
|
||||
|
||||
const FolderData::SortState & getSortState() const;
|
||||
void setSortIndex(size_t index);
|
||||
void setNextSortIndex();
|
||||
void setPreviousSortIndex();
|
||||
void sort(FolderData::ComparisonFunction & comparisonFunction = FolderData::compareFileName, bool ascending = true);
|
||||
|
||||
static GuiGameList* create(Window* window);
|
||||
|
||||
bool isDetailed() const;
|
||||
|
|
Loading…
Reference in a new issue