Merge remote-tracking branch 'horstbaerbel/master'

Conflicts:
	src/components/GuiGameList.cpp
This commit is contained in:
Aloshi 2013-07-02 22:24:43 -05:00
commit d616b4a202
16 changed files with 619 additions and 100 deletions

View file

@ -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

View file

@ -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;
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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

View file

@ -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;
}
}

View file

@ -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();

View file

@ -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()

View file

@ -33,6 +33,8 @@ void Settings::setDefaults()
mBoolMap["WINDOWED"] = false;
mIntMap["DIMTIME"] = 30*1000;
mIntMap["GameListSortIndex"] = 0;
}
template <typename K, typename V>

View file

@ -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)

View file

@ -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() << "\"!";
}
}

View file

@ -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

View file

@ -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;

View file

@ -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

View file

@ -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();

View file

@ -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;