diff --git a/Package/Environment/Common/controls.conf b/Package/Environment/Common/controls.conf index 299347c..1d0dbc4 100644 --- a/Package/Environment/Common/controls.conf +++ b/Package/Environment/Common/controls.conf @@ -6,6 +6,10 @@ pageUp = A pageDown = B letterUp = N letterDown = M +nextPlaylist = P +addPlaylist = I +removePlaylist = O +random = R select = Space back = Escape quit = Q diff --git a/Package/Environment/Common/layouts/Default 16x9/layout.xml b/Package/Environment/Common/layouts/Default 16x9/layout.xml index 87eb262..90732e1 100644 --- a/Package/Environment/Common/layouts/Default 16x9/layout.xml +++ b/Package/Environment/Common/layouts/Default 16x9/layout.xml @@ -12,7 +12,7 @@ - + @@ -367,4 +367,4 @@ - \ No newline at end of file + diff --git a/Package/Environment/Common/layouts/Mamed 16x9/layout.xml b/Package/Environment/Common/layouts/Mamed 16x9/layout.xml index db4770a..dd46163 100644 --- a/Package/Environment/Common/layouts/Mamed 16x9/layout.xml +++ b/Package/Environment/Common/layouts/Mamed 16x9/layout.xml @@ -76,7 +76,7 @@ - + diff --git a/Package/Environment/Common/layouts/Mamed 2015 16x9/layout.xml b/Package/Environment/Common/layouts/Mamed 2015 16x9/layout.xml index ccb4167..4ca289b 100644 --- a/Package/Environment/Common/layouts/Mamed 2015 16x9/layout.xml +++ b/Package/Environment/Common/layouts/Mamed 2015 16x9/layout.xml @@ -90,7 +90,7 @@ - + diff --git a/Package/Environment/Common/meta/hyperlist/Sega Genesis.xml b/Package/Environment/Common/meta/hyperlist/Sega Genesis.xml index 80e79e2..56c9281 100644 --- a/Package/Environment/Common/meta/hyperlist/Sega Genesis.xml +++ b/Package/Environment/Common/meta/hyperlist/Sega Genesis.xml @@ -1227,7 +1227,7 @@ Yes - Chi Chi\'s Pro Challenge Golf (USA) + Chi Chi's Pro Challenge Golf (USA) 9c3973a4 Coconuts Japan @@ -1337,7 +1337,7 @@ Yes - College Football\'s National Championship (USA) + College Football's National Championship (USA) 172c5dbb Bluesky Innovations @@ -1347,7 +1347,7 @@ Yes - College Football\'s National Championship II (USA) + College Football's National Championship II (USA) 65b64413 Bluesky Innovations @@ -1477,7 +1477,7 @@ Yes - Crystal\'s Pony Tale (USA) + Crystal's Pony Tale (USA) 6cf7a4df Artech Studios @@ -1547,7 +1547,7 @@ Yes - Dashin\' Desperadoes (USA) + Dashin' Desperadoes (USA) dcb76fb7 Data East @@ -1557,7 +1557,7 @@ Yes - David Crane\'s Amazing Tennis (USA) + David Crane's Amazing Tennis (USA) 9177088c Absolute Entertainment @@ -1567,7 +1567,7 @@ Yes - David Robinson\'s Supreme Court (USA, Europe) + David Robinson's Supreme Court (USA, Europe) 512b7599 Sega @@ -1677,7 +1677,7 @@ Yes - Dick Vitale\'s \'Awesome, Baby!\' College Hoops (USA) + Dick Vitale's 'Awesome, Baby!' College Hoops (USA) 1312cf22 Time Warner Interactive @@ -1687,7 +1687,7 @@ Yes - Dino Dini\'s Soccer (Europe) + Dino Dini's Soccer (Europe) 4608f53a Dini and Dini Productions @@ -1717,7 +1717,7 @@ Yes - Dinosaur\'s Tale, A (USA) + Dinosaur's Tale, A (USA) 70155b5b Malibu @@ -1817,7 +1817,7 @@ Yes - Dr. Robotnik\'s Mean Bean Machine (USA) + Dr. Robotnik's Mean Bean Machine (USA) c7ca517f Sega @@ -1837,7 +1837,7 @@ Yes - Dragon\'s Fury (USA, Europe) + Dragon's Fury (USA, Europe) 58037bc6 TechnoSoft @@ -1847,7 +1847,7 @@ Yes - Dragon\'s Revenge (USA, Europe) + Dragon's Revenge (USA, Europe) 841edbc0 Tengen @@ -2057,7 +2057,7 @@ Yes - Evander Holyfield\'s \'Real Deal\' Boxing (World) + Evander Holyfield's 'Real Deal' Boxing (World) 4fef37c8 ACME Interactive @@ -2407,7 +2407,7 @@ Yes - Fun \'N\' Games (USA) + Fun 'N' Games (USA) b5ae351d Leland Interactive Media @@ -2547,7 +2547,7 @@ Yes - George Foreman\'s KO Boxing (USA) + George Foreman's KO Boxing (USA) e1fdc787 Beam Software @@ -2567,7 +2567,7 @@ Yes - Ghouls \'n Ghosts (USA, Europe) (Rev A) + Ghouls 'n Ghosts (USA, Europe) (Rev A) 4f2561d5 AM8 @@ -2607,7 +2607,7 @@ Yes - Goofy\'s Hysterical History Tour (USA) + Goofy's Hysterical History Tour (USA) 4e1cc833 Imagineering @@ -2707,7 +2707,7 @@ Yes - Hard Drivin\' (World) + Hard Drivin' (World) 3225baaf Sterling Silver Software @@ -2727,7 +2727,7 @@ Yes - HardBall \'94 (USA, Europe) + HardBall '94 (USA, Europe) ea9c4878 MindSpan @@ -2737,7 +2737,7 @@ Yes - HardBall \'95 (USA) + HardBall '95 (USA) ed10bc9e MindSpan @@ -2977,7 +2977,7 @@ Yes - Izzy\'s Quest for the Olympic Rings (USA, Europe) + Izzy's Quest for the Olympic Rings (USA, Europe) 77b416e4 Alexandria @@ -2987,7 +2987,7 @@ Yes - Jack Nicklaus\' Power Challenge Golf (USA, Europe) + Jack Nicklaus' Power Challenge Golf (USA, Europe) 5545e909 Accolade @@ -3037,7 +3037,7 @@ Yes - James \'Buster\' Douglas Knockout Boxing (USA, Europe) + James 'Buster' Douglas Knockout Boxing (USA, Europe) 87bbcf2a Domark @@ -3097,7 +3097,7 @@ Yes - Jerry Glanville\'s Pigskin Footbrawl (USA) + Jerry Glanville's Pigskin Footbrawl (USA) e7f48d30 Developer Resources @@ -3117,7 +3117,7 @@ Yes - Jimmy White\'s Whirlwind Snooker (Europe) + Jimmy White's Whirlwind Snooker (Europe) 0aef5b1f Virgin Interactive @@ -3177,7 +3177,7 @@ Yes - John Madden Football \'92 (USA, Europe) + John Madden Football '92 (USA, Europe) 046e3945 Park Place Productions @@ -3187,7 +3187,7 @@ Yes - John Madden Football \'93 (USA, Europe) + John Madden Football '93 (USA, Europe) ca323b3e Park Place Productions @@ -3357,7 +3357,7 @@ Yes - King\'s Bounty - The Conqueror\'s Quest (USA, Europe) + King's Bounty - The Conqueror's Quest (USA, Europe) aa68a92e New World Computing @@ -3377,7 +3377,7 @@ Yes - Krusty\'s Super Fun House (USA, Europe) (v1.1) + Krusty's Super Fun House (USA, Europe) (v1.1) 56976261 Audiogenic @@ -3637,7 +3637,7 @@ Yes - Madden NFL \'94 (USA, Europe) + Madden NFL '94 (USA, Europe) d14b811b High Score Productions @@ -3727,7 +3727,7 @@ Yes - Mary Shelley\'s Frankenstein (USA) + Mary Shelley's Frankenstein (USA) 48993dc3 Bits Studios @@ -3767,7 +3767,7 @@ Yes - McDonald\'s Treasure Land Adventure (USA) + McDonald's Treasure Land Adventure (USA) 04ef4899 Treasure @@ -3897,7 +3897,7 @@ Yes - Michael Jackson\'s Moonwalker (World) (Rev A) + Michael Jackson's Moonwalker (World) (Rev A) 11ce1f9e Ultimate Productions @@ -3927,7 +3927,7 @@ Yes - Mickey\'s Ultimate Challenge (USA) + Mickey's Ultimate Challenge (USA) 30b512ee Designer Software @@ -4177,7 +4177,7 @@ Yes - NBA Action \'94 (USA) + NBA Action '94 (USA) 99c348ba Sega @@ -4187,7 +4187,7 @@ Yes - NBA Action \'95 Starring David Robinson (USA, Europe) + NBA Action '95 Starring David Robinson (USA, Europe) aa7006d6 Sega @@ -4277,7 +4277,7 @@ Yes - NBA Showdown \'94 (USA, Europe) + NBA Showdown '94 (USA, Europe) 160b7090 Electronic Arts @@ -4337,7 +4337,7 @@ Yes - NFL Football \'94 Starring Joe Montana (USA) + NFL Football '94 Starring Joe Montana (USA) 0d486ed5 Bluesky Innovations @@ -4367,7 +4367,7 @@ Yes - NFL Sports Talk Football \'93 Starring Joe Montana (USA, Europe) + NFL Sports Talk Football '93 Starring Joe Montana (USA, Europe) ce0b1fe1 Bluesky Innovations @@ -4377,7 +4377,7 @@ Yes - NFL \'95 (USA, Europe) + NFL '95 (USA, Europe) b58e4a81 Sega @@ -4427,7 +4427,7 @@ Yes - NHL All-Star Hockey \'95 (USA) + NHL All-Star Hockey '95 (USA) e6c0218b Sega @@ -4447,7 +4447,7 @@ Yes - NHL \'94 (USA, Europe) + NHL '94 (USA, Europe) 9438f5dd High Score Productions @@ -4467,7 +4467,7 @@ Yes - Nigel Mansell\'s World Championship Racing (USA) + Nigel Mansell's World Championship Racing (USA) 6bc57b2c Gremlin Interactive @@ -4487,7 +4487,7 @@ Yes - Nobunaga\'s Ambition (USA) + Nobunaga's Ambition (USA) b9bc07bc Koei @@ -4497,7 +4497,7 @@ Yes - Normy\'s Beach Babe-O-Rama (USA, Europe) + Normy's Beach Babe-O-Rama (USA, Europe) b56a8220 Realtime Associates @@ -5087,7 +5087,7 @@ Yes - R.B.I. Baseball \'93 (USA) + R.B.I. Baseball '93 (USA) beafce84 Tengen @@ -5097,7 +5097,7 @@ Yes - R.B.I. Baseball \'94 (USA, Europe) + R.B.I. Baseball '94 (USA, Europe) 4eb4d5e4 Tengen @@ -5107,7 +5107,7 @@ Yes - Race Drivin\' (USA) + Race Drivin' (USA) d737cf3d Polygames @@ -5187,7 +5187,7 @@ Yes - Ren & Stimpy Show Presents Stimpy\'s Invention, The (USA) + Ren & Stimpy Show Presents Stimpy's Invention, The (USA) d9503ba5 Blue Sky Software @@ -5217,7 +5217,7 @@ Yes - Richard Scarry\'s BusyTown (USA) + Richard Scarry's BusyTown (USA) 7bb60c3f Novotrade @@ -5337,7 +5337,7 @@ Yes - Rock n\' Roll Racing (USA) + Rock n' Roll Racing (USA) 6abab577 Blizzard Entertainment @@ -5597,7 +5597,7 @@ Yes - Shanghai II - Dragon\'s Eye (USA) + Shanghai II - Dragon's Eye (USA) ebe9e840 Activision @@ -5687,7 +5687,7 @@ Yes - Simpsons, The - Bart\'s Nightmare (USA, Europe) + Simpsons, The - Bart's Nightmare (USA, Europe) 24d7507c Sculptured Software @@ -5707,7 +5707,7 @@ Yes - Skitchin\' (USA, Europe) + Skitchin' (USA, Europe) f785f9d7 Electronic Arts @@ -5887,7 +5887,7 @@ Yes - Sorcerer\'s Kingdom (USA) (v1.1) + Sorcerer's Kingdom (USA) (v1.1) bb1fc9ce Treco @@ -5907,7 +5907,7 @@ Yes - Space Invaders \'91 (USA) + Space Invaders '91 (USA) bb83b528 Taito @@ -5977,7 +5977,7 @@ Yes - Spider-Man and X-Men - Arcade\'s Revenge (USA, Europe) + Spider-Man and X-Men - Arcade's Revenge (USA, Europe) 4a4414ea Software Creations @@ -6127,7 +6127,7 @@ Yes - Street Fighter II\' - Special Champion Edition (USA) + Street Fighter II' - Special Champion Edition (USA) 13fe08a1 Capcom @@ -6767,7 +6767,7 @@ Yes - Tiny Toon Adventures - Buster\'s Hidden Treasure (USA) + Tiny Toon Adventures - Buster's Hidden Treasure (USA) a26d3ae0 Konami @@ -6787,7 +6787,7 @@ Yes - TNN Outdoors Bass Tournament \'96 (USA) + TNN Outdoors Bass Tournament '96 (USA) 5c523c0b Imagitec Design @@ -6797,7 +6797,7 @@ Yes - Todd\'s Adventures in Slime World (USA) + Todd's Adventures in Slime World (USA) 652e8b7d Epyx @@ -7127,7 +7127,7 @@ Yes - Unnecessary Roughness \'95 (USA) + Unnecessary Roughness '95 (USA) 9920e7b7 Accolade @@ -7337,7 +7337,7 @@ Yes - Wayne\'s World (USA) + Wayne's World (USA) d2cf6ebe Gray Matter @@ -7407,7 +7407,7 @@ Yes - Williams Arcade\'s Greatest Hits (USA) + Williams Arcade's Greatest Hits (USA) d68e9c00 Digital Eclipse @@ -7457,7 +7457,7 @@ Yes - Wiz\'n\'Liz (USA) + Wiz'n'Liz (USA) df036b62 Raising Hell @@ -7587,7 +7587,7 @@ Yes - World Series Baseball \'95 (USA) + World Series Baseball '95 (USA) 25130077 Blue Sky Software @@ -7597,7 +7597,7 @@ Yes - World Series Baseball \'96 (USA) + World Series Baseball '96 (USA) 04ee8272 Blue Sky Software @@ -7787,7 +7787,7 @@ Yes - Zool - Ninja of the \'Nth\' Dimension (USA) + Zool - Ninja of the 'Nth' Dimension (USA) cb2939f1 Gremlin Interactive diff --git a/Package/Environment/Common/settings.conf b/Package/Environment/Common/settings.conf index 38c073f..c001c15 100644 --- a/Package/Environment/Common/settings.conf +++ b/Package/Environment/Common/settings.conf @@ -22,6 +22,9 @@ showSquareBrackets = yes # specify the name of the first collection to load on start firstCollection = Main +# specify whether RetroFE should switch to Favorites list if it exists. +autoFavorites = true + ####################################### # Video playback settings ####################################### diff --git a/RetroFE/Source/Collection/CollectionInfo.cpp b/RetroFE/Source/Collection/CollectionInfo.cpp index 45ff5e7..9071728 100644 --- a/RetroFE/Source/Collection/CollectionInfo.cpp +++ b/RetroFE/Source/Collection/CollectionInfo.cpp @@ -17,8 +17,22 @@ #include "Item.h" #include "../Database/Configuration.h" #include "../Utility/Utils.h" +#include "../Utility/Log.h" #include +#include #include +#include +#include +#include + +#ifdef _WIN32 +#include +#endif + +#ifdef __linux +#include +#include +#endif CollectionInfo::CollectionInfo(std::string name, std::string listPath, @@ -27,6 +41,7 @@ CollectionInfo::CollectionInfo(std::string name, std::string metadataPath) : name(name) , listpath(listPath) + , saveRequest(false) , metadataType(metadataType) , menusort(true) , metadataPath_(metadataPath) @@ -39,13 +54,25 @@ CollectionInfo::~CollectionInfo() // remove items from the subcollections so their destructors do not // delete the items since the parent collection will delete them. std::vector::iterator subit; - for (subit != subcollections_.begin(); subit != subcollections_.end(); subit++) + for (subit = subcollections_.begin(); subit != subcollections_.end(); subit++) { CollectionInfo *info = *subit; info->items.clear(); } + Playlists_T::iterator pit = playlists.begin(); + + while(pit != playlists.end()) + { + if(pit->second != &items) + { + delete pit->second; + } + playlists.erase(pit); + pit = playlists.begin(); + } + std::vector::iterator it = items.begin(); while(it != items.end()) { @@ -55,6 +82,68 @@ CollectionInfo::~CollectionInfo() } } +bool CollectionInfo::Save() +{ + bool retval = true; + if(saveRequest) + { + std::string dir = Utils::combinePath(Configuration::absolutePath, "collections", name, "playlists"); + std::string file = Utils::combinePath(Configuration::absolutePath, "collections", name, "playlists/favorites.txt"); + Logger::write(Logger::ZONE_INFO, "Collection", "Saving " + file); + + std::ofstream filestream; + try + { + // Create playlists directory if it does not exist yet. + struct stat info; + if ( stat( dir.c_str(), &info ) != 0 ) + { +#if defined(_WIN32) && !defined(__GNUC__) + if(!CreateDirectory(dir.c_str(), NULL)) + { + if(ERROR_ALREADY_EXISTS != GetLastError()) + { + Logger::write(Logger::ZONE_WARNING, "Collection", "Could not create directory " + dir); + return false; + } + } +#else +#if defined(__MINGW32__) + if(mkdir(dir.c_str()) == -1) +#else + if(mkdir(dir.c_str(), 0755) == -1) +#endif + { + Logger::write(Logger::ZONE_WARNING, "Collection", "Could not create directory " + dir); + return false; + } +#endif + } + else if ( !(info.st_mode & S_IFDIR) ) + { + Logger::write(Logger::ZONE_WARNING, "Collection", dir + " exists, but is not a directory."); + return false; + } + + filestream.open(file.c_str()); + std::vector *saveitems = playlists["favorites"]; + for(std::vector::iterator it = saveitems->begin(); it != saveitems->end(); it++) + { + filestream << (*it)->name << std::endl; + } + + filestream.close(); + } + catch(std::exception &) + { + Logger::write(Logger::ZONE_ERROR, "Collection", "Save failed: " + file); + retval = false; + } + } + + return retval; +} + std::string CollectionInfo::settingsPath() const { return Utils::combinePath(Configuration::absolutePath, "collections", name); @@ -95,5 +184,8 @@ bool CollectionInfo::itemIsLess(Item *lhs, Item *rhs) void CollectionInfo::sortItems() { - std::sort(items.begin(), items.end(), itemIsLess); + for(Playlists_T::iterator it = playlists.begin(); it != playlists.end(); it++) + { + std::sort(it->second->begin(), it->second->end(), itemIsLess); + } } diff --git a/RetroFE/Source/Collection/CollectionInfo.h b/RetroFE/Source/Collection/CollectionInfo.h index 04bc908..dd15d1a 100644 --- a/RetroFE/Source/Collection/CollectionInfo.h +++ b/RetroFE/Source/Collection/CollectionInfo.h @@ -17,6 +17,7 @@ #include #include +#include class Item; @@ -26,15 +27,21 @@ public: CollectionInfo(std::string name, std::string listPath, std::string extensions, std::string metadataType, std::string metadataPath); virtual ~CollectionInfo(); std::string settingsPath() const; + bool Save(); void sortItems(); void addSubcollection(CollectionInfo *info); bool hasSubcollections(); void extensionList(std::vector &extensions); std::string name; std::string listpath; + bool saveRequest; std::string metadataType; std::string launcher; std::vector items; + + typedef std::map *> Playlists_T; + Playlists_T playlists; + bool menusort; private: std::vector subcollections_; diff --git a/RetroFE/Source/Collection/CollectionInfoBuilder.cpp b/RetroFE/Source/Collection/CollectionInfoBuilder.cpp index e7925ec..2843f54 100644 --- a/RetroFE/Source/Collection/CollectionInfoBuilder.cpp +++ b/RetroFE/Source/Collection/CollectionInfoBuilder.cpp @@ -81,7 +81,7 @@ bool CollectionInfoBuilder::createCollectionDirectory(std::string name) #if defined(__MINGW32__) if(mkdir(it->c_str()) == -1) #else - if(mkdir(it->c_str(), 0744) == -1) + if(mkdir(it->c_str(), 0755) == -1) #endif { std::cout << "Could not create folder \"" << *it << "\":" << errno << std::endl; @@ -119,18 +119,23 @@ bool CollectionInfoBuilder::createCollectionDirectory(std::string name) settingsFile << "list.menuSort = yes" << std::endl; settingsFile << std::endl; settingsFile << "launcher = mame" << std::endl; - settingsFile << "metadata.type = MAME" << std::endl; + settingsFile << "#metadata.type = MAME" << std::endl; settingsFile << std::endl; - settingsFile << "#media.screenshot = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "screenshot") << std::endl; - settingsFile << "#media.screentitle = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "screentitle") << std::endl; - settingsFile << "#media.artwork_back = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "artwork_back") << std::endl; - settingsFile << "#media.artwork_front = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "artwork_front") << std::endl; - settingsFile << "#media.logo = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "logo") << std::endl; - settingsFile << "#media.medium_back = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "medium_back") << std::endl; - settingsFile << "#media.medium_front = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "medium_front") << std::endl; - settingsFile << "#media.screenshot = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "screenshot") << std::endl; - settingsFile << "#media.screentitle = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "screentitle") << std::endl; - settingsFile << "#media.video = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "video") << std::endl; + settingsFile << "#manufacturer = " << std::endl; + settingsFile << "#year = " << std::endl; + settingsFile << "#genre = " << std::endl; + settingsFile << std::endl; + settingsFile << "#media.screenshot = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "screenshot") << std::endl; + settingsFile << "#media.screentitle = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "screentitle") << std::endl; + settingsFile << "#media.artwork_back = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "artwork_back") << std::endl; + settingsFile << "#media.artwork_front = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "artwork_front") << std::endl; + settingsFile << "#media.logo = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "logo") << std::endl; + settingsFile << "#media.medium_back = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "medium_back") << std::endl; + settingsFile << "#media.medium_front = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "medium_front") << std::endl; + settingsFile << "#media.screenshot = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "screenshot") << std::endl; + settingsFile << "#media.screentitle = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "screentitle") << std::endl; + settingsFile << "#media.video = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "medium_artwork", "video") << std::endl; + settingsFile << "#media.system_artwork = " << Utils::combinePath("%BASE_MEDIA_PATH%", "%ITEM_COLLECTION_NAME%", "system_artwork") << std::endl; settingsFile.close(); filename = Utils::combinePath(collectionPath, "menu.txt"); @@ -230,9 +235,12 @@ bool CollectionInfoBuilder::ImportDirectory(CollectionInfo *info, std::string me DIR *dp; struct dirent *dirp; std::string path = info->listpath; + std::map allMap; std::map includeFilter; + std::map favoritesFilter; std::map excludeFilter; std::string includeFile = Utils::combinePath(Configuration::absolutePath, "collections", info->name, "include.txt"); + std::string favoritesFile = Utils::combinePath(Configuration::absolutePath, "collections", info->name, "playlists/favorites.txt"); std::string excludeFile = Utils::combinePath(Configuration::absolutePath, "collections", info->name, "exclude.txt"); std::string launcher; @@ -266,14 +274,12 @@ bool CollectionInfoBuilder::ImportDirectory(CollectionInfo *info, std::string me info->extensionList(extensions); - dp = opendir(path.c_str()); Logger::write(Logger::ZONE_INFO, "CollectionInfoBuilder", "Scanning directory \"" + path + "\""); if(dp == NULL) { Logger::write(Logger::ZONE_INFO, "CollectionInfoBuilder", "Could not read directory \"" + path + "\". Ignore if this is a menu."); - return false; } if(showMissing) @@ -287,7 +293,7 @@ bool CollectionInfoBuilder::ImportDirectory(CollectionInfo *info, std::string me } } - while((dirp = readdir(dp)) != NULL) + while(dp != NULL && (dirp = readdir(dp)) != NULL) { std::string file = dirp->d_name; @@ -322,7 +328,10 @@ bool CollectionInfoBuilder::ImportDirectory(CollectionInfo *info, std::string me } } - closedir(dp); + if(dp != NULL) + { + closedir(dp); + } while(includeFilter.size() > 0) { @@ -341,6 +350,27 @@ bool CollectionInfoBuilder::ImportDirectory(CollectionInfo *info, std::string me excludeFilter.erase(it); } + for(std::vector::iterator it = info->items.begin(); it != info->items.end(); it++) { + allMap[(*it)->fullTitle] = *it; + } + + + ImportBasicList(info, favoritesFile, favoritesFilter); + info->playlists["all"] = &info->items; + info->playlists["favorites"] = new std::vector(); + + // add the favorites list + for(std::map::iterator it = favoritesFilter.begin(); it != favoritesFilter.end(); it++) + { + std::map::iterator itemit = allMap.find(it->first); + + if(itemit != allMap.end()) + { + info->playlists["favorites"]->push_back(itemit->second); + } + } + + metaDB_.injectMetadata(info); return true; diff --git a/RetroFE/Source/Control/UserInput.cpp b/RetroFE/Source/Control/UserInput.cpp index 8e5559c..310c0c6 100644 --- a/RetroFE/Source/Control/UserInput.cpp +++ b/RetroFE/Source/Control/UserInput.cpp @@ -30,6 +30,7 @@ UserInput::UserInput(Configuration &c) for(unsigned int i = 0; i < KeyCodeMax; ++i) { keyHandlers_[i] = NULL; + currentKeyState_[i] = false; lastKeyState_[i] = false; } } @@ -66,13 +67,18 @@ bool UserInput::initialize() retVal = MapKey("down", KeyCodeRight) && retVal; } - retVal = MapKey("pageDown", KeyCodePageDown) && retVal; - retVal = MapKey("pageUp", KeyCodePageUp) && retVal; - MapKey("letterDown", KeyCodeLetterDown); - MapKey("letterUp", KeyCodeLetterUp); retVal = MapKey("select", KeyCodeSelect) && retVal; retVal = MapKey("back", KeyCodeBack) && retVal; retVal = MapKey("quit", KeyCodeQuit) && retVal; + retVal = MapKey("pageDown", KeyCodePageDown); + retVal = MapKey("pageUp", KeyCodePageUp); + + MapKey("letterDown", KeyCodeLetterDown, false); + MapKey("letterUp", KeyCodeLetterUp, false); + MapKey("nextPlaylist", KeyCodeNextPlaylist, false); + MapKey("addPlaylist", KeyCodeAddPlaylist, false); + MapKey("removePlaylist", KeyCodeRemovePlaylist, false); + MapKey("random", KeyCodeRandom, false); // these features will need to be implemented at a later time // retVal = MapKey("admin", KeyCodeAdminMode) && retVal; // retVal = MapKey("remove", KeyCodeHideItem) && retVal; @@ -87,6 +93,11 @@ bool UserInput::initialize() } bool UserInput::MapKey(std::string keyDescription, KeyCode_E key) +{ + return MapKey(keyDescription, key, true); +} + +bool UserInput::MapKey(std::string keyDescription, KeyCode_E key, bool required) { SDL_Scancode scanCode; std::string description; @@ -95,7 +106,8 @@ bool UserInput::MapKey(std::string keyDescription, KeyCode_E key) if(!config_.getProperty(configKey, description)) { - Logger::write(Logger::ZONE_ERROR, "Input", "Missing property " + configKey); + Logger::Zone zone = (required) ? Logger::ZONE_ERROR : Logger::ZONE_INFO; + Logger::write(zone, "Input", "Missing property " + configKey); return false; } @@ -223,6 +235,9 @@ void UserInput::resetStates() bool UserInput::update(SDL_Event &e) { bool updated = false; + + memcpy(lastKeyState_, currentKeyState_, sizeof(lastKeyState_)); + for(unsigned int i = 0; i < KeyCodeMax; ++i) { InputHandler *h = keyHandlers_[i]; @@ -230,7 +245,7 @@ bool UserInput::update(SDL_Event &e) { if(h->update(e)) updated = true; - lastKeyState_[i] = h->pressed(); + currentKeyState_[i] = h->pressed(); } } @@ -239,5 +254,11 @@ bool UserInput::update(SDL_Event &e) bool UserInput::keystate(KeyCode_E code) { - return lastKeyState_[code]; + return currentKeyState_[code]; } + +bool UserInput::newKeyPressed(KeyCode_E code) +{ + return currentKeyState_[code] && !lastKeyState_[code]; +} + diff --git a/RetroFE/Source/Control/UserInput.h b/RetroFE/Source/Control/UserInput.h index 610c995..92f9684 100644 --- a/RetroFE/Source/Control/UserInput.h +++ b/RetroFE/Source/Control/UserInput.h @@ -39,6 +39,10 @@ public: KeyCodePageUp, KeyCodeLetterDown, KeyCodeLetterUp, + KeyCodeNextPlaylist, + KeyCodeRandom, + KeyCodeAddPlaylist, + KeyCodeRemovePlaylist, KeyCodeAdminMode, KeyCodeHideItem, KeyCodeQuit, @@ -51,12 +55,14 @@ public: void resetStates(); bool update(SDL_Event &e); bool keystate(KeyCode_E); + bool newKeyPressed(KeyCode_E code); private: bool MapKey(std::string keyDescription, KeyCode_E key); + bool MapKey(std::string keyDescription, KeyCode_E key, bool required); Configuration &config_; std::vector joysticks_; InputHandler *keyHandlers_[KeyCodeMax]; bool lastKeyState_[KeyCodeMax]; - + bool currentKeyState_[KeyCodeMax]; }; diff --git a/RetroFE/Source/Database/Configuration.cpp b/RetroFE/Source/Database/Configuration.cpp index 3cc21ae..7d3e294 100644 --- a/RetroFE/Source/Database/Configuration.cpp +++ b/RetroFE/Source/Database/Configuration.cpp @@ -130,15 +130,14 @@ bool Configuration::parseLine(std::string collection, std::string keyPrefix, std key = trimEnds(key); + value = line.substr(position + delimiter.length(), line.length()); value = trimEnds(value); - // only overwrite the collection name if we know it. We could be parsing a launcher configuration if(collection != "") { value = Utils::replace(value, "%ITEM_COLLECTION_NAME%", collection); } - properties_.insert(PropertiesPair(key, value)); std::stringstream ss; @@ -305,11 +304,9 @@ std::string Configuration::convertToAbsolutePath(std::string prefix, std::string } // check to see if it is already an absolute path -#ifdef WIN32 - if((first != '\\') && (first != '/') && (second != ':')) -#else - if(first != Utils::pathSeparator) -#endif + if((first != Utils::pathSeparator) && + //(first != '.') && + (second != ':')) { path = Utils::combinePath(prefix, path); } @@ -338,6 +335,10 @@ void Configuration::getMediaPropertyAbsolutePath(std::string collectionName, std void Configuration::getMediaPropertyAbsolutePath(std::string collectionName, std::string mediaType, bool system, std::string &value) { std::string key = "collections." + collectionName + ".media." + mediaType; + if (system) + { + key = "collections." + collectionName + ".media.system_artwork"; + } // use user-overridden setting if it exists if(getPropertyAbsolutePath(key, value)) diff --git a/RetroFE/Source/Database/MetadataDatabase.cpp b/RetroFE/Source/Database/MetadataDatabase.cpp index 80a93b1..19453f1 100644 --- a/RetroFE/Source/Database/MetadataDatabase.cpp +++ b/RetroFE/Source/Database/MetadataDatabase.cpp @@ -417,7 +417,14 @@ bool MetadataDatabase::importMamelist(std::string filename, std::string collecti } sqlite3_exec(handle, "BEGIN IMMEDIATE TRANSACTION;", NULL, NULL, &error); - for (rapidxml::xml_node<> * game = rootNode->first_node("game"); game; game = game->next_sibling()) + std::string gameNodeName = "game"; + + // support new mame formats + if(rootNode->first_node(gameNodeName.c_str()) == NULL) { + gameNodeName = "machine"; + } + + for (rapidxml::xml_node<> * game = rootNode->first_node(gameNodeName.c_str()); game; game = game->next_sibling()) { rapidxml::xml_attribute<> *nameNode = game->first_attribute("name"); rapidxml::xml_attribute<> *cloneOfXml = game->first_attribute("cloneof"); diff --git a/RetroFE/Source/Execute/Launcher.cpp b/RetroFE/Source/Execute/Launcher.cpp index 8c307ba..53323f0 100644 --- a/RetroFE/Source/Execute/Launcher.cpp +++ b/RetroFE/Source/Execute/Launcher.cpp @@ -67,29 +67,28 @@ bool Launcher::run(std::string collection, Item *collectionItem) Logger::write(Logger::ZONE_ERROR, "Launcher", "No launcher arguments specified for launcher " + launcherName); return false; } - if(!findFile(selectedItemsPath, matchedExtension, selectedItemsDirectory, collectionItem->name, extensionstr)) - { - // FindFile() prints out diagnostic messages for us, no need to print anything here - return false; - } + + // It is ok to continue if the file could not be found. We could be launching a merged romset + findFile(selectedItemsPath, matchedExtension, selectedItemsDirectory, collectionItem->name, extensionstr); + args = replaceVariables(args, selectedItemsPath, collectionItem->name, - collectionItem->filename(), + Utils::getFileName(selectedItemsPath), selectedItemsDirectory, collection); executablePath = replaceVariables(executablePath, selectedItemsPath, collectionItem->name, - collectionItem->filename(), + Utils::getFileName(selectedItemsPath), selectedItemsDirectory, collection); currentDirectory = replaceVariables(currentDirectory, selectedItemsPath, collectionItem->name, - collectionItem->filename(), + Utils::getFileName(selectedItemsPath), selectedItemsDirectory, collection); @@ -329,7 +328,7 @@ bool Launcher::findFile(std::string &foundFilePath, std::string &foundFilename, << filenameWithoutExtension << "\" in folder \"" << directory; - Logger::write(Logger::ZONE_ERROR, "Launcher", ss.str()); + Logger::write(Logger::ZONE_WARNING, "Launcher", ss.str()); } diff --git a/RetroFE/Source/Graphics/Component/Component.cpp b/RetroFE/Source/Graphics/Component/Component.cpp index 31b03ce..3b81db2 100644 --- a/RetroFE/Source/Graphics/Component/Component.cpp +++ b/RetroFE/Source/Graphics/Component/Component.cpp @@ -1,460 +1,460 @@ -/* This file is part of RetroFE. - * - * RetroFE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * RetroFE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with RetroFE. If not, see . - */ -#include "Component.h" -#include "../Animate/Tween.h" -#include "../../Graphics/ViewInfo.h" -#include "../../Utility/Log.h" -#include "../../SDL.h" - -Component::Component() -{ - tweens_ = NULL; - selectedItem_ = NULL; - newItemSelectedSinceEnter = false; - backgroundTexture_ = NULL; - freeGraphicsMemory(); - -} - -Component::Component(const Component ©) -{ - tweens_ = NULL; - selectedItem_ = NULL; - newItemSelectedSinceEnter = false; - backgroundTexture_ = NULL; - freeGraphicsMemory(); - +/* This file is part of RetroFE. + * + * RetroFE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RetroFE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RetroFE. If not, see . + */ +#include "Component.h" +#include "../Animate/Tween.h" +#include "../../Graphics/ViewInfo.h" +#include "../../Utility/Log.h" +#include "../../SDL.h" + +Component::Component(Page &p) +: page(p) +{ + tweens_ = NULL; + newItemSelectedSinceEnter = false; + backgroundTexture_ = NULL; + freeGraphicsMemory(); + +} + +Component::Component(const Component ©) + : page(copy.page) +{ + tweens_ = NULL; + newItemSelectedSinceEnter = false; + backgroundTexture_ = NULL; + freeGraphicsMemory(); + if(copy.tweens_) { AnimationEvents *tweens = new AnimationEvents(*copy.tweens_); setTweens(tweens); - } - - -} - -Component::~Component() -{ - freeGraphicsMemory(); -} - -void Component::freeGraphicsMemory() -{ - currentAnimationState = HIDDEN; - enterRequested = false; - exitRequested = false; - menuEnterRequested = false; - menuEnterIndex = -1; - menuScrollRequested = false; - menuExitRequested = false; - menuExitIndex = -1; - - newItemSelected = false; - highlightExitComplete = false; - currentTweens_ = NULL; - currentTweenIndex_ = 0; - currentTweenComplete_ = false; - elapsedTweenTime_ = 0; - scrollActive = false; - - if(backgroundTexture_) - { - SDL_LockMutex(SDL::getMutex()); - SDL_DestroyTexture(backgroundTexture_); - SDL_UnlockMutex(SDL::getMutex()); - - backgroundTexture_ = NULL; - } -} -void Component::allocateGraphicsMemory() -{ - if(!backgroundTexture_) - { - // make a 4x4 pixel wide surface to be stretched during rendering, make it a white background so we can use - // color later - SDL_Surface *surface = SDL_CreateRGBSurface(0, 4, 4, 32, 0, 0, 0, 0); - SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, 255, 255, 255)); - - SDL_LockMutex(SDL::getMutex()); - backgroundTexture_ = SDL_CreateTextureFromSurface(SDL::getRenderer(), surface); - SDL_UnlockMutex(SDL::getMutex()); - - SDL_FreeSurface(surface); - SDL_SetTextureBlendMode(backgroundTexture_, SDL_BLENDMODE_BLEND); - } -} - -Item *Component::getSelectedItem() -{ - return selectedItem_; -} - -void Component::triggerEnterEvent() -{ - enterRequested = true; -} - -void Component::triggerExitEvent() -{ - exitRequested = true; -} - - - -void Component::triggerMenuEnterEvent(int menuIndex) -{ - menuEnterRequested = true; - menuEnterIndex = menuIndex; -} - -void Component::triggerMenuScrollEvent() -{ - menuScrollRequested = true; -} - - -void Component::triggerMenuExitEvent(int menuIndex) -{ - menuExitRequested = true; - menuExitIndex = menuIndex; -} -void Component::triggerHighlightEvent(Item *selectedItem) -{ - newItemSelected = true; - this->selectedItem_ = selectedItem; -} - - -bool Component::isIdle() -{ - return (currentAnimationState == IDLE); -} - -bool Component::isHidden() -{ - return (currentAnimationState == HIDDEN); -} -bool Component::isWaiting() -{ - return (currentAnimationState == HIGHLIGHT_WAIT); -} - -bool Component::isMenuScrolling() -{ - return (currentAnimationState == MENU_ENTER || currentAnimationState == MENU_SCROLL || currentAnimationState == MENU_EXIT || menuScrollRequested); -} - -void Component::setTweens(AnimationEvents *set) -{ - tweens_ = set; - forceIdle(); -} - -void Component::forceIdle() -{ - currentAnimationState = IDLE; - currentTweenIndex_ = 0; - currentTweenComplete_ = false; - elapsedTweenTime_ = 0; - currentTweens_ = NULL; -} - - -void Component::update(float dt) -{ - elapsedTweenTime_ += dt; - highlightExitComplete = false; - if(isHidden() || isWaiting() || (isIdle() && exitRequested)) - { - currentTweenComplete_ = true; - } - - if(currentTweenComplete_) - { - currentTweens_ = NULL; - - // There was no request to override our state path. Continue on as normal. - std::stringstream ss; - switch(currentAnimationState) - { - case MENU_ENTER: - currentTweens_ = NULL; - currentAnimationState = IDLE; - break; - - case MENU_SCROLL: - currentTweens_ = NULL; - currentAnimationState = IDLE; - break; - - case MENU_EXIT: - currentTweens_ = NULL; - currentAnimationState = IDLE; - break; - - - case ENTER: - currentTweens_ = tweens_->getAnimation("enter", menuEnterIndex); - currentAnimationState = HIGHLIGHT_ENTER; - break; - - case EXIT: - currentTweens_ = NULL; - currentAnimationState = HIDDEN; - break; - - case HIGHLIGHT_ENTER: - currentTweens_ = tweens_->getAnimation("idle", menuEnterIndex); - currentAnimationState = IDLE; - break; - - case IDLE: - // prevent us from automatically jumping to the exit tween upon enter - if(enterRequested) - { - enterRequested = false; - newItemSelected = false; - } - else if(menuExitRequested && (!menuEnterRequested || menuExitRequested <= menuEnterRequested)) - { - currentTweens_ = tweens_->getAnimation("menuExit", menuExitIndex); - currentAnimationState = MENU_EXIT; - menuExitRequested = false; - } - else if(menuEnterRequested && (!menuExitRequested || menuExitRequested > menuEnterRequested)) - { - currentTweens_ = tweens_->getAnimation("menuEnter", menuEnterIndex); - currentAnimationState = MENU_ENTER; - menuEnterRequested = false; - - } - else if(menuScrollRequested) - { - menuScrollRequested = false; - currentTweens_ = tweens_->getAnimation("menuScroll", menuEnterIndex); - currentAnimationState = MENU_SCROLL; - } - else if(scrollActive || newItemSelected || exitRequested) - { - currentTweens_ = tweens_->getAnimation("highlightExit", menuEnterIndex); - currentAnimationState = HIGHLIGHT_EXIT; - } - else - { - currentTweens_ = tweens_->getAnimation("idle", menuEnterIndex); - currentAnimationState = IDLE; - } - break; - - case HIGHLIGHT_EXIT: - - // intentionally break down - case HIGHLIGHT_WAIT: - - if(exitRequested && (currentAnimationState == HIGHLIGHT_WAIT)) - { - currentTweens_ = tweens_->getAnimation("highlightExit", menuEnterIndex); - currentAnimationState = HIGHLIGHT_EXIT; - - } - else if(exitRequested && (currentAnimationState == HIGHLIGHT_EXIT)) - { - - currentTweens_ = tweens_->getAnimation("exit", menuEnterIndex); - currentAnimationState = EXIT; - exitRequested = false; - } - else if(scrollActive) - { - currentTweens_ = NULL; - currentAnimationState = HIGHLIGHT_WAIT; - } - else if(newItemSelected) - { - currentTweens_ = tweens_->getAnimation("highlightEnter", menuEnterIndex); - currentAnimationState = HIGHLIGHT_ENTER; - highlightExitComplete = true; - newItemSelected = false; - } - else - { - currentTweens_ = NULL; - currentAnimationState = HIGHLIGHT_WAIT; - } - break; - - case HIDDEN: - if(enterRequested || exitRequested) - { - currentTweens_ = tweens_->getAnimation("enter", menuEnterIndex); - currentAnimationState = ENTER; - } - - else if(menuExitRequested && (!menuEnterRequested || menuExitRequested <= menuEnterRequested)) - { - currentTweens_ = tweens_->getAnimation("menuExit", menuExitIndex); - currentAnimationState = MENU_EXIT; - menuExitRequested = false; - } - else if(menuEnterRequested && (!menuExitRequested || menuExitRequested > menuEnterRequested)) - { - currentTweens_ = tweens_->getAnimation("menuEnter", menuEnterIndex); - currentAnimationState = MENU_ENTER; - menuEnterRequested = false; - - } - else if(menuScrollRequested) - { - menuScrollRequested = false; - currentTweens_ = tweens_->getAnimation("menuScroll", menuEnterIndex); - currentAnimationState = MENU_SCROLL; - } - else - { - currentTweens_ = NULL; - currentAnimationState = HIDDEN; - } - } - - currentTweenIndex_ = 0; - currentTweenComplete_ = false; - - elapsedTweenTime_ = 0; - } - - currentTweenComplete_ = animate(isIdle()); -} - -void Component::draw() -{ - - if(backgroundTexture_) - { - SDL_Rect rect; - rect.h = static_cast(baseViewInfo.ScaledHeight()); - rect.w = static_cast(baseViewInfo.ScaledWidth()); - rect.x = static_cast(baseViewInfo.XRelativeToOrigin()); - rect.y = static_cast(baseViewInfo.YRelativeToOrigin()); - - - SDL_SetTextureColorMod(backgroundTexture_, - static_cast(baseViewInfo.BackgroundRed*255), - static_cast(baseViewInfo.BackgroundGreen*255), - static_cast(baseViewInfo.BackgroundBlue*255)); - - SDL::renderCopy(backgroundTexture_, static_cast(baseViewInfo.BackgroundAlpha*255), NULL, &rect, baseViewInfo.Angle); - } -} - -bool Component::animate(bool loop) -{ - bool completeDone = false; - if(!currentTweens_ || currentTweenIndex_ >= currentTweens_->size()) - { - completeDone = true; - } - else if(currentTweens_) - { - bool currentDone = true; - TweenSet *tweens = currentTweens_->tweenSet(currentTweenIndex_); - - for(unsigned int i = 0; i < tweens->size(); i++) - { - Tween *tween = tweens->tweens()->at(i); - double elapsedTime = elapsedTweenTime_; - - //todo: too many levels of nesting - if(elapsedTime < tween->duration) - { - currentDone = false; - } - else - { - elapsedTime = static_cast(tween->duration); - } - - float value = tween->animate(elapsedTime); - - switch(tween->property) - { - case TWEEN_PROPERTY_X: - baseViewInfo.X = value; - break; - - case TWEEN_PROPERTY_Y: - baseViewInfo.Y = value; - break; - - case TWEEN_PROPERTY_HEIGHT: - baseViewInfo.Height = value; - break; - - case TWEEN_PROPERTY_WIDTH: - baseViewInfo.Width = value; - break; - - case TWEEN_PROPERTY_ANGLE: - baseViewInfo.Angle = value; - break; - - case TWEEN_PROPERTY_ALPHA: - baseViewInfo.Alpha = value; - break; - - case TWEEN_PROPERTY_X_ORIGIN: - baseViewInfo.XOrigin = value; - break; - - case TWEEN_PROPERTY_Y_ORIGIN: - baseViewInfo.YOrigin = value; - break; - - case TWEEN_PROPERTY_X_OFFSET: - baseViewInfo.XOffset = value; - break; - - case TWEEN_PROPERTY_Y_OFFSET: - baseViewInfo.YOffset = value; - break; - - case TWEEN_PROPERTY_FONT_SIZE: - baseViewInfo.FontSize = value; - break; - - case TWEEN_PROPERTY_BACKGROUND_ALPHA: - baseViewInfo.BackgroundAlpha = value; - break; - } - } - - if(currentDone) - { - currentTweenIndex_++; - elapsedTweenTime_ = 0; - } - } - - if(!currentTweens_ || currentTweenIndex_ >= currentTweens_->tweenSets()->size()) - { - if(loop) - { - currentTweenIndex_ = 0; - } - completeDone = true; - } - - return completeDone; -} + } + + +} + +Component::~Component() +{ + freeGraphicsMemory(); +} + +void Component::freeGraphicsMemory() +{ + currentAnimationState = HIDDEN; + enterRequested = false; + exitRequested = false; + menuEnterRequested = false; + menuEnterIndex = -1; + menuScrollRequested = false; + menuExitRequested = false; + menuExitIndex = -1; + + newItemSelected = false; + playlistChanged = false; + highlightExitComplete = false; + currentTweens_ = NULL; + currentTweenIndex_ = 0; + currentTweenComplete_ = false; + elapsedTweenTime_ = 0; + scrollActive = false; + + if(backgroundTexture_) + { + SDL_LockMutex(SDL::getMutex()); + SDL_DestroyTexture(backgroundTexture_); + SDL_UnlockMutex(SDL::getMutex()); + + backgroundTexture_ = NULL; + } +} +void Component::allocateGraphicsMemory() +{ + if(!backgroundTexture_) + { + // make a 4x4 pixel wide surface to be stretched during rendering, make it a white background so we can use + // color later + SDL_Surface *surface = SDL_CreateRGBSurface(0, 4, 4, 32, 0, 0, 0, 0); + SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, 255, 255, 255)); + + SDL_LockMutex(SDL::getMutex()); + backgroundTexture_ = SDL_CreateTextureFromSurface(SDL::getRenderer(), surface); + SDL_UnlockMutex(SDL::getMutex()); + + SDL_FreeSurface(surface); + SDL_SetTextureBlendMode(backgroundTexture_, SDL_BLENDMODE_BLEND); + } +} + +void Component::triggerEnterEvent() +{ + enterRequested = true; +} + +void Component::triggerExitEvent() +{ + exitRequested = true; +} + + + +void Component::triggerMenuEnterEvent(int menuIndex) +{ + menuEnterRequested = true; + menuEnterIndex = menuIndex; +} + +void Component::triggerMenuScrollEvent() +{ + menuScrollRequested = true; +} + + +void Component::triggerMenuExitEvent(int menuIndex) +{ + menuExitRequested = true; + menuExitIndex = menuIndex; +} +void Component::triggerHighlightEvent() +{ + newItemSelected = true; +} + +void Component::triggerPlaylistChangeEvent(std::string name) +{ + playlistChanged = true; + this->playlistName = name; +} + +bool Component::isIdle() +{ + return (currentAnimationState == IDLE); +} + +bool Component::isHidden() +{ + return (currentAnimationState == HIDDEN); +} +bool Component::isWaiting() +{ + return (currentAnimationState == HIGHLIGHT_WAIT); +} + +bool Component::isMenuScrolling() +{ + return (currentAnimationState == MENU_ENTER || currentAnimationState == MENU_SCROLL || currentAnimationState == MENU_EXIT || menuScrollRequested); +} + +void Component::setTweens(AnimationEvents *set) +{ + tweens_ = set; + forceIdle(); +} + +void Component::forceIdle() +{ + currentAnimationState = IDLE; + currentTweenIndex_ = 0; + currentTweenComplete_ = false; + elapsedTweenTime_ = 0; + currentTweens_ = NULL; +} + + +void Component::update(float dt) +{ + elapsedTweenTime_ += dt; + highlightExitComplete = false; + if(isHidden() || isWaiting() || (isIdle() && exitRequested)) + { + currentTweenComplete_ = true; + } + + if(currentTweenComplete_) + { + currentTweens_ = NULL; + + // There was no request to override our state path. Continue on as normal. + std::stringstream ss; + switch(currentAnimationState) + { + case MENU_ENTER: + currentTweens_ = NULL; + currentAnimationState = IDLE; + break; + + case MENU_SCROLL: + currentTweens_ = NULL; + currentAnimationState = IDLE; + break; + + case MENU_EXIT: + currentTweens_ = NULL; + currentAnimationState = IDLE; + break; + + + case ENTER: + currentTweens_ = tweens_->getAnimation("enter", menuEnterIndex); + currentAnimationState = HIGHLIGHT_ENTER; + break; + + case EXIT: + currentTweens_ = NULL; + currentAnimationState = HIDDEN; + break; + + case HIGHLIGHT_ENTER: + currentTweens_ = tweens_->getAnimation("idle", menuEnterIndex); + currentAnimationState = IDLE; + break; + + case IDLE: + // prevent us from automatically jumping to the exit tween upon enter + if(enterRequested) + { + enterRequested = false; + newItemSelected = false; + } + else if(menuExitRequested && (!menuEnterRequested || menuExitRequested <= menuEnterRequested)) + { + currentTweens_ = tweens_->getAnimation("menuExit", menuExitIndex); + currentAnimationState = MENU_EXIT; + menuExitRequested = false; + } + else if(menuEnterRequested && (!menuExitRequested || menuExitRequested > menuEnterRequested)) + { + currentTweens_ = tweens_->getAnimation("menuEnter", menuEnterIndex); + currentAnimationState = MENU_ENTER; + menuEnterRequested = false; + + } + else if(menuScrollRequested) + { + menuScrollRequested = false; + currentTweens_ = tweens_->getAnimation("menuScroll", menuEnterIndex); + currentAnimationState = MENU_SCROLL; + } + else if(scrollActive || newItemSelected || exitRequested) + { + currentTweens_ = tweens_->getAnimation("highlightExit", menuEnterIndex); + currentAnimationState = HIGHLIGHT_EXIT; + } + else + { + currentTweens_ = tweens_->getAnimation("idle", menuEnterIndex); + currentAnimationState = IDLE; + } + break; + + case HIGHLIGHT_EXIT: + + // intentionally break down + case HIGHLIGHT_WAIT: + + if(exitRequested && (currentAnimationState == HIGHLIGHT_WAIT)) + { + currentTweens_ = tweens_->getAnimation("highlightExit", menuEnterIndex); + currentAnimationState = HIGHLIGHT_EXIT; + + } + else if(exitRequested && (currentAnimationState == HIGHLIGHT_EXIT)) + { + + currentTweens_ = tweens_->getAnimation("exit", menuEnterIndex); + currentAnimationState = EXIT; + exitRequested = false; + } + else if(scrollActive) + { + currentTweens_ = NULL; + currentAnimationState = HIGHLIGHT_WAIT; + } + else if(newItemSelected) + { + currentTweens_ = tweens_->getAnimation("highlightEnter", menuEnterIndex); + currentAnimationState = HIGHLIGHT_ENTER; + highlightExitComplete = true; + newItemSelected = false; + } + else + { + currentTweens_ = NULL; + currentAnimationState = HIGHLIGHT_WAIT; + } + break; + + case HIDDEN: + if(enterRequested || exitRequested) + { + currentTweens_ = tweens_->getAnimation("enter", menuEnterIndex); + currentAnimationState = ENTER; + } + + else if(menuExitRequested && (!menuEnterRequested || menuExitRequested <= menuEnterRequested)) + { + currentTweens_ = tweens_->getAnimation("menuExit", menuExitIndex); + currentAnimationState = MENU_EXIT; + menuExitRequested = false; + } + else if(menuEnterRequested && (!menuExitRequested || menuExitRequested > menuEnterRequested)) + { + currentTweens_ = tweens_->getAnimation("menuEnter", menuEnterIndex); + currentAnimationState = MENU_ENTER; + menuEnterRequested = false; + + } + else if(menuScrollRequested) + { + menuScrollRequested = false; + currentTweens_ = tweens_->getAnimation("menuScroll", menuEnterIndex); + currentAnimationState = MENU_SCROLL; + } + else + { + currentTweens_ = NULL; + currentAnimationState = HIDDEN; + } + } + + currentTweenIndex_ = 0; + currentTweenComplete_ = false; + + elapsedTweenTime_ = 0; + } + + currentTweenComplete_ = animate(isIdle()); +} + +void Component::draw() +{ + + if(backgroundTexture_) + { + SDL_Rect rect; + rect.h = static_cast(baseViewInfo.ScaledHeight()); + rect.w = static_cast(baseViewInfo.ScaledWidth()); + rect.x = static_cast(baseViewInfo.XRelativeToOrigin()); + rect.y = static_cast(baseViewInfo.YRelativeToOrigin()); + + + SDL_SetTextureColorMod(backgroundTexture_, + static_cast(baseViewInfo.BackgroundRed*255), + static_cast(baseViewInfo.BackgroundGreen*255), + static_cast(baseViewInfo.BackgroundBlue*255)); + + SDL::renderCopy(backgroundTexture_, static_cast(baseViewInfo.BackgroundAlpha*255), NULL, &rect, baseViewInfo.Angle); + } +} + +bool Component::animate(bool loop) +{ + bool completeDone = false; + if(!currentTweens_ || currentTweenIndex_ >= currentTweens_->size()) + { + completeDone = true; + } + else if(currentTweens_) + { + bool currentDone = true; + TweenSet *tweens = currentTweens_->tweenSet(currentTweenIndex_); + + for(unsigned int i = 0; i < tweens->size(); i++) + { + Tween *tween = tweens->tweens()->at(i); + double elapsedTime = elapsedTweenTime_; + + //todo: too many levels of nesting + if(elapsedTime < tween->duration) + { + currentDone = false; + } + else + { + elapsedTime = static_cast(tween->duration); + } + + float value = tween->animate(elapsedTime); + + switch(tween->property) + { + case TWEEN_PROPERTY_X: + baseViewInfo.X = value; + break; + + case TWEEN_PROPERTY_Y: + baseViewInfo.Y = value; + break; + + case TWEEN_PROPERTY_HEIGHT: + baseViewInfo.Height = value; + break; + + case TWEEN_PROPERTY_WIDTH: + baseViewInfo.Width = value; + break; + + case TWEEN_PROPERTY_ANGLE: + baseViewInfo.Angle = value; + break; + + case TWEEN_PROPERTY_ALPHA: + baseViewInfo.Alpha = value; + break; + + case TWEEN_PROPERTY_X_ORIGIN: + baseViewInfo.XOrigin = value; + break; + + case TWEEN_PROPERTY_Y_ORIGIN: + baseViewInfo.YOrigin = value; + break; + + case TWEEN_PROPERTY_X_OFFSET: + baseViewInfo.XOffset = value; + break; + + case TWEEN_PROPERTY_Y_OFFSET: + baseViewInfo.YOffset = value; + break; + + case TWEEN_PROPERTY_FONT_SIZE: + baseViewInfo.FontSize = value; + break; + + case TWEEN_PROPERTY_BACKGROUND_ALPHA: + baseViewInfo.BackgroundAlpha = value; + break; + } + } + + if(currentDone) + { + currentTweenIndex_++; + elapsedTweenTime_ = 0; + } + } + + if(!currentTweens_ || currentTweenIndex_ >= currentTweens_->tweenSets()->size()) + { + if(loop) + { + currentTweenIndex_ = 0; + } + completeDone = true; + } + + return completeDone; +} diff --git a/RetroFE/Source/Graphics/Component/Component.h b/RetroFE/Source/Graphics/Component/Component.h index 78d20e9..495961e 100644 --- a/RetroFE/Source/Graphics/Component/Component.h +++ b/RetroFE/Source/Graphics/Component/Component.h @@ -19,15 +19,15 @@ #include "../../SDL.h" #include "../MenuNotifierInterface.h" +#include "../Page.h" #include "../ViewInfo.h" #include "../Animate/Tween.h" #include "../Animate/AnimationEvents.h" #include "../../Collection/Item.h" - class Component { public: - Component(); + Component(Page &p); Component(const Component ©); virtual ~Component(); virtual void freeGraphicsMemory(); @@ -39,7 +39,8 @@ public: void triggerMenuEnterEvent(int menuIndex = -1); void triggerMenuExitEvent(int menuIndex = -1); void triggerMenuScrollEvent(); - void triggerHighlightEvent(Item *selectedItem); + void triggerHighlightEvent(); + void triggerPlaylistChangeEvent(std::string name); bool isIdle(); bool isHidden(); bool isWaiting(); @@ -54,7 +55,8 @@ public: bool scrollActive; protected: - Item *getSelectedItem(); + Page &page; + enum AnimationState { IDLE, @@ -78,6 +80,8 @@ protected: bool menuExitRequested; int menuExitIndex; bool newItemSelected; + bool playlistChanged; + std::string playlistName; bool highlightExitComplete; bool newItemSelectedSinceEnter; private: @@ -87,7 +91,6 @@ private: AnimationEvents *tweens_; Animation *currentTweens_; - Item *selectedItem_; SDL_Texture *backgroundTexture_; unsigned int currentTweenIndex_; diff --git a/RetroFE/Source/Graphics/Component/Container.cpp b/RetroFE/Source/Graphics/Component/Container.cpp index df8982b..1b59d1e 100644 --- a/RetroFE/Source/Graphics/Component/Container.cpp +++ b/RetroFE/Source/Graphics/Component/Container.cpp @@ -18,7 +18,8 @@ #include "../ViewInfo.h" #include "../../SDL.h" -Container::Container() +Container::Container(Page &p) +: Component(p) { allocateGraphicsMemory(); } diff --git a/RetroFE/Source/Graphics/Component/Container.h b/RetroFE/Source/Graphics/Component/Container.h index 9c04a63..d98c0d4 100644 --- a/RetroFE/Source/Graphics/Component/Container.h +++ b/RetroFE/Source/Graphics/Component/Container.h @@ -22,7 +22,7 @@ class Container : public Component { public: - Container(); + Container(Page &p); virtual ~Container(); void freeGraphicsMemory(); void allocateGraphicsMemory(); diff --git a/RetroFE/Source/Graphics/Component/Image.cpp b/RetroFE/Source/Graphics/Component/Image.cpp index 347f7be..d13de5b 100644 --- a/RetroFE/Source/Graphics/Component/Image.cpp +++ b/RetroFE/Source/Graphics/Component/Image.cpp @@ -19,8 +19,9 @@ #include "../../Utility/Log.h" #include -Image::Image(std::string file, float scaleX, float scaleY) - : texture_(NULL) +Image::Image(std::string file, Page &p, float scaleX, float scaleY) + : Component(p) + , texture_(NULL) , file_(file) , scaleX_(scaleX) , scaleY_(scaleY) diff --git a/RetroFE/Source/Graphics/Component/Image.h b/RetroFE/Source/Graphics/Component/Image.h index 4798793..6dd5ff3 100644 --- a/RetroFE/Source/Graphics/Component/Image.h +++ b/RetroFE/Source/Graphics/Component/Image.h @@ -22,7 +22,7 @@ class Image : public Component { public: - Image(std::string file, float scaleX, float scaleY); + Image(std::string file, Page &p, float scaleX, float scaleY); virtual ~Image(); void freeGraphicsMemory(); void allocateGraphicsMemory(); diff --git a/RetroFE/Source/Graphics/Component/ImageBuilder.cpp b/RetroFE/Source/Graphics/Component/ImageBuilder.cpp index 964b1c7..8da2923 100644 --- a/RetroFE/Source/Graphics/Component/ImageBuilder.cpp +++ b/RetroFE/Source/Graphics/Component/ImageBuilder.cpp @@ -18,7 +18,7 @@ #include "../../Utility/Log.h" #include -Image * ImageBuilder::CreateImage(std::string path, std::string name, float scaleX, float scaleY) +Image * ImageBuilder::CreateImage(std::string path, Page &p, std::string name, float scaleX, float scaleY) { Image *image = NULL; std::vector extensions; @@ -35,7 +35,7 @@ Image * ImageBuilder::CreateImage(std::string path, std::string name, float scal if(Utils::findMatchingFile(prefix, extensions, file)) { - image = new Image(file, scaleX, scaleY); + image = new Image(file, p, scaleX, scaleY); } return image; diff --git a/RetroFE/Source/Graphics/Component/ImageBuilder.h b/RetroFE/Source/Graphics/Component/ImageBuilder.h index 7b77ba6..be3dc31 100644 --- a/RetroFE/Source/Graphics/Component/ImageBuilder.h +++ b/RetroFE/Source/Graphics/Component/ImageBuilder.h @@ -15,6 +15,7 @@ */ #pragma once +#include "../Page.h" #include "Image.h" #include "VideoComponent.h" #include "../../Video/VideoFactory.h" @@ -23,5 +24,5 @@ class ImageBuilder { public: - Image * CreateImage(std::string path, std::string name, float scaleX, float scaleY); + Image * CreateImage(std::string path, Page &p, std::string name, float scaleX, float scaleY); }; diff --git a/RetroFE/Source/Graphics/Component/ReloadableMedia.cpp b/RetroFE/Source/Graphics/Component/ReloadableMedia.cpp index fd855dc..e927835 100644 --- a/RetroFE/Source/Graphics/Component/ReloadableMedia.cpp +++ b/RetroFE/Source/Graphics/Component/ReloadableMedia.cpp @@ -28,8 +28,9 @@ #include #include -ReloadableMedia::ReloadableMedia(Configuration &config, bool systemMode, std::string type, bool isVideo, Font *font, float scaleX, float scaleY) - : config_(config) +ReloadableMedia::ReloadableMedia(Configuration &config, bool systemMode, std::string type, Page &p, int displayOffset, bool isVideo, Font *font, float scaleX, float scaleY) + : Component(p) + , config_(config) , systemMode_(systemMode) , loadedComponent_(NULL) , reloadRequested_(false) @@ -41,6 +42,8 @@ ReloadableMedia::ReloadableMedia(Configuration &config, bool systemMode, std::st , type_(type) , scaleX_(scaleX) , scaleY_(scaleY) + , displayOffset_(displayOffset) + { allocateGraphicsMemory(); } @@ -145,7 +148,7 @@ void ReloadableMedia::reloadTexture() loadedComponent_ = NULL; } - Item *selectedItem = getSelectedItem(); + Item *selectedItem = page.getSelectedItem(displayOffset_); if(!selectedItem) return; config_.getProperty("currentCollection", currentCollection_); @@ -252,7 +255,10 @@ void ReloadableMedia::reloadTexture() } else if(typeLC == "manufacturer") { + if ( selectedItem->leaf ) // item is a leaf basename = selectedItem->manufacturer; + else // item is a collection + (void)config_.getProperty("collections." + selectedItem->name + ".manufacturer", basename ); } else if(typeLC == "genre") { @@ -324,7 +330,7 @@ void ReloadableMedia::reloadTexture() // if image and artwork was not specified, fall back to displaying text if(!loadedComponent_ && textFallback_) { - loadedComponent_ = new Text(selectedItem->fullTitle, FfntInst_, scaleX_, scaleY_); + loadedComponent_ = new Text(selectedItem->fullTitle, page, FfntInst_, scaleX_, scaleY_); baseViewInfo.ImageWidth = loadedComponent_->baseViewInfo.ImageWidth; baseViewInfo.ImageHeight = loadedComponent_->baseViewInfo.ImageHeight; } @@ -343,11 +349,11 @@ Component *ReloadableMedia::findComponent(std::string collection, std::string ty if(type == "video") { - component = videoBuild.createVideo(imagePath, basename, scaleX_, scaleY_); + component = videoBuild.createVideo(imagePath, page, basename, scaleX_, scaleY_); } else { - component = imageBuild.CreateImage(imagePath, basename, scaleX_, scaleY_); + component = imageBuild.CreateImage(imagePath, page, basename, scaleX_, scaleY_); } return component; diff --git a/RetroFE/Source/Graphics/Component/ReloadableMedia.h b/RetroFE/Source/Graphics/Component/ReloadableMedia.h index 6cfdaf1..2f5683d 100644 --- a/RetroFE/Source/Graphics/Component/ReloadableMedia.h +++ b/RetroFE/Source/Graphics/Component/ReloadableMedia.h @@ -27,7 +27,7 @@ class Image; class ReloadableMedia : public Component { public: - ReloadableMedia(Configuration &config, bool systemMode, std::string type, bool isVideo, Font *font, float scaleX, float scaleY); + ReloadableMedia(Configuration &config, bool systemMode, std::string type, Page &page, int displayOffset, bool isVideo, Font *font, float scaleX, float scaleY); virtual ~ReloadableMedia(); void update(float dt); void draw(); @@ -54,4 +54,6 @@ private: float scaleX_; float scaleY_; std::string currentCollection_; + Page *page_; + int displayOffset_; }; diff --git a/RetroFE/Source/Graphics/Component/ReloadableText.cpp b/RetroFE/Source/Graphics/Component/ReloadableText.cpp index ceab954..36997d8 100644 --- a/RetroFE/Source/Graphics/Component/ReloadableText.cpp +++ b/RetroFE/Source/Graphics/Component/ReloadableText.cpp @@ -23,8 +23,10 @@ #include #include -ReloadableText::ReloadableText(std::string type, Font *font, std::string layoutKey, float scaleX, float scaleY) - : imageInst_(NULL) +ReloadableText::ReloadableText(std::string type, Page &page, Configuration &config, Font *font, std::string layoutKey, float scaleX, float scaleY) + : Component(page) + , config_(config) + , imageInst_(NULL) , layoutKey_(layoutKey) , reloadRequested_(false) , firstLoad_(true) @@ -59,7 +61,22 @@ ReloadableText::ReloadableText(std::string type, Font *font, std::string layoutK { type_ = TextTypeGenre; } - + else if(type == "playlist") + { + type_ = TextTypePlaylist; + } + else if(type == "collectionName") + { + type_ = TextTypeCollectionName; + } + else if(type == "collectionSize") + { + type_ = TextTypeCollectionSize; + } + else if(type == "collectionIndex") + { + type_ = TextTypeCollectionIndex; + } allocateGraphicsMemory(); } @@ -75,7 +92,8 @@ ReloadableText::~ReloadableText() void ReloadableText::update(float dt) { - if(newItemSelected) + if((type_ != TextTypePlaylist && newItemSelected) || + (type_ == TextTypePlaylist && playlistChanged)) { reloadRequested_ = true; } @@ -128,7 +146,7 @@ void ReloadableText::ReloadTexture() imageInst_ = NULL; } - Item *selectedItem = getSelectedItem(); + Item *selectedItem = page.getSelectedItem(); if (selectedItem != NULL) { @@ -143,22 +161,47 @@ void ReloadableText::ReloadTexture() ss << selectedItem->numberPlayers; break; case TextTypeYear: - ss << selectedItem->year; + if (selectedItem->leaf) // item is a leaf + text = selectedItem->year; + else // item is a collection + (void)config_.getProperty("collections." + selectedItem->name + ".year", text ); + ss << text; break; case TextTypeTitle: ss << selectedItem->title; break; case TextTypeManufacturer: - ss << selectedItem->manufacturer; + if (selectedItem->leaf) // item is a leaf + text = selectedItem->manufacturer; + else // item is a collection + (void)config_.getProperty("collections." + selectedItem->name + ".manufacturer", text ); + ss << text; break; case TextTypeGenre: - ss << selectedItem->genre; + if (selectedItem->leaf) // item is a leaf + text = selectedItem->genre; + else // item is a collection + (void)config_.getProperty("collections." + selectedItem->name + ".genre", text ); + ss << text; break; + case TextTypePlaylist: + ss << playlistName; + break; + case TextTypeCollectionName: + ss << page.getCollectionName(); + break; + case TextTypeCollectionSize: + ss << page.getCollectionSize(); + break; + case TextTypeCollectionIndex: + ss << (1+page.getSelectedIndex()); + break; + default: break; } - imageInst_ = new Text(ss.str(), fontInst_, scaleX_, scaleY_); + imageInst_ = new Text(ss.str(), page, fontInst_, scaleX_, scaleY_); } } diff --git a/RetroFE/Source/Graphics/Component/ReloadableText.h b/RetroFE/Source/Graphics/Component/ReloadableText.h index 161ccb9..5f059f3 100644 --- a/RetroFE/Source/Graphics/Component/ReloadableText.h +++ b/RetroFE/Source/Graphics/Component/ReloadableText.h @@ -17,6 +17,7 @@ #include "Component.h" #include "Text.h" #include "../Font.h" +#include "../Page.h" #include "../../Collection/Item.h" #include #include @@ -24,7 +25,7 @@ class ReloadableText : public Component { public: - ReloadableText(std::string type, Font *font, std::string layoutKey, float scaleX, float scaleY); + ReloadableText(std::string type, Page &page, Configuration &config, Font *font, std::string layoutKey, float scaleX, float scaleY); virtual ~ReloadableText(); void update(float dt); void draw(); @@ -43,10 +44,15 @@ private: TextTypeTitle, TextTypeManufacturer, TextTypeGenre, + TextTypePlaylist, + TextTypeCollectionName, + TextTypeCollectionSize, + TextTypeCollectionIndex }; void ReloadTexture(); + Configuration &config_; Text *imageInst_; TextType type_; std::string layoutKey_; diff --git a/RetroFE/Source/Graphics/Component/ScrollingList.cpp b/RetroFE/Source/Graphics/Component/ScrollingList.cpp index c42c291..ced3368 100644 --- a/RetroFE/Source/Graphics/Component/ScrollingList.cpp +++ b/RetroFE/Source/Graphics/Component/ScrollingList.cpp @@ -14,14 +14,13 @@ * along with RetroFE. If not, see . */ +#include "ScrollingList.h" #include "../Animate/Tween.h" #include "../Animate/TweenSet.h" #include "../Animate/Animation.h" #include "../Animate/AnimationEvents.h" #include "../Animate/TweenTypes.h" -#include "../ComponentItemBinding.h" #include "../Font.h" -#include "ScrollingList.h" #include "ImageBuilder.h" #include "VideoBuilder.h" #include "VideoComponent.h" @@ -37,23 +36,25 @@ #include #include #include - +#include //todo: remove coupling from configuration data (if possible) ScrollingList::ScrollingList(Configuration &c, + Page &p, float scaleX, float scaleY, Font *font, std::string layoutKey, std::string imageType) - : horizontalScroll(false) + : Component(p) + , horizontalScroll(false) , spriteList_(NULL) , scrollPoints_(NULL) , tweenPoints_(NULL) - , tweenEnterTime_(0) , focus_(false) - , firstSpriteIndex_(0) - , selectedSpriteListIndex_(0) + , itemIndex_(0) + , componentIndex_(0) + , selectedOffsetIndex_(0) , scrollStopRequested_(true) , notifyAllRequested_(false) , currentScrollDirection_(ScrollDirectionIdle) @@ -68,6 +69,7 @@ ScrollingList::ScrollingList(Configuration &c, , fontInst_(font) , layoutKey_(layoutKey) , imageType_(imageType) + , items_(NULL) { } @@ -75,10 +77,10 @@ ScrollingList::ScrollingList(const ScrollingList ©) : Component(copy) , horizontalScroll(copy.horizontalScroll) , spriteList_(NULL) - , tweenEnterTime_(0) , focus_(false) - , firstSpriteIndex_(0) - , selectedSpriteListIndex_(copy.selectedSpriteListIndex_) + , itemIndex_(0) + , componentIndex_(0) + , selectedOffsetIndex_(copy.selectedOffsetIndex_) , scrollStopRequested_(true) , notifyAllRequested_(false) , currentScrollDirection_(ScrollDirectionIdle) @@ -93,30 +95,13 @@ ScrollingList::ScrollingList(const ScrollingList ©) , fontInst_(copy.fontInst_) , layoutKey_(copy.layoutKey_) , imageType_(copy.imageType_) + , items_(NULL) { - scrollPoints_ = NULL; tweenPoints_ = NULL; - if(copy.scrollPoints_) - { - scrollPoints_ = new std::vector(); - for(unsigned int i = 0; i < copy.scrollPoints_->size(); ++i) - { - ViewInfo *v = new ViewInfo(*copy.scrollPoints_->at(i)); - scrollPoints_->push_back(v); - } - } + setPoints(copy.scrollPoints_, copy.tweenPoints_); - if(copy.tweenPoints_) - { - tweenPoints_ = new std::vector(); - for(unsigned int i = 0; i < copy.tweenPoints_->size(); ++i) - { - AnimationEvents *v = new AnimationEvents(*copy.tweenPoints_->at(i)); - tweenPoints_->push_back(v); - } - } } @@ -125,38 +110,32 @@ ScrollingList::~ScrollingList() destroyItems(); } -void ScrollingList::setItems(std::vector *spriteList) + +void ScrollingList::setItems(std::vector *items) { - notifyAllRequested_ = true; - spriteList_ = spriteList; - firstSpriteIndex_ = 0; + deallocateSpritePoints(); - if(!spriteList_) + items_ = items; + if(items_) { - return; + itemIndex_ = loopDecrement(0, selectedOffsetIndex_, items_->size()); } - unsigned int originalSize = spriteList_->size(); - - // loop the scroll points if there are not enough, the +2 represents the head and tail nodes (for when the item is allocated) - while(scrollPoints_ && scrollPoints_->size()+2 > spriteList_->size() && spriteList_->size() > 0) - { - for(unsigned int i = 0; i < originalSize; ++i) - { - Item *newItem = new Item(); - Item *originalItem = spriteList_->at(i)->item; - - *newItem = *originalItem; - ComponentItemBinding *newSprite = new ComponentItemBinding(newItem); - spriteList_->push_back(newSprite); - } - } - - for(unsigned int i = 0; scrollPoints_ && i < selectedSpriteListIndex_; ++i) - { - circularDecrement(firstSpriteIndex_, spriteList_); - } - + allocateSpritePoints(); + + notifyAllRequested_ = true; +} + +unsigned int ScrollingList::loopIncrement(unsigned int offset, unsigned int i, unsigned int size) +{ + if(size == 0) return 0; + return (offset + i) % size; +} + +unsigned int ScrollingList::loopDecrement(unsigned int offset, unsigned int i, unsigned int size) +{ + if(size == 0) return 0; + return ((offset % size) - (i % size) + size) % size; } @@ -172,294 +151,217 @@ void ScrollingList::setStartScrollTime(float value) void ScrollingList::deallocateSpritePoints() { - if(!spriteList_) + for(unsigned int i = 0; i < components_.size(); ++i) { - return; + deallocateTexture(i); } - unsigned int spriteIndex = firstSpriteIndex_; - - for(unsigned int i = 0; i < scrollPoints_->size() && spriteList_->size() > spriteIndex; ++i) - { - deallocateTexture(spriteList_->at(spriteIndex)); - circularIncrement(spriteIndex, spriteList_); - } + componentIndex_ = 0; } void ScrollingList::allocateSpritePoints() { - if(!scrollPoints_) - { - return; - } - if(!spriteList_) - { - return; - } - if(spriteList_->size() == 0) - { - return; - } - if(!tweenPoints_) - { - return; - } - - unsigned int spriteIndex = firstSpriteIndex_; + if(!items_ || items_->size() == 0) return; + if(!scrollPoints_) return; + if(components_.size() == 0) return; for(unsigned int i = 0; i < scrollPoints_->size(); ++i) { - allocateTexture(spriteList_->at(spriteIndex)); - Component *c = spriteList_->at(spriteIndex)->component; - ViewInfo *currentViewInfo = scrollPoints_->at(i); - unsigned int nextI = getNextTween(i, scrollPoints_); - ViewInfo *nextViewInfo = scrollPoints_->at(nextI); + componentIndex_ = 0; + unsigned int index = loopIncrement(itemIndex_, i, items_->size()); + Item *item = items_->at(index); - resetTweens(c, tweenPoints_->at(i), currentViewInfo, nextViewInfo, 0); + allocateTexture(i, item); + Component *c = components_.at(i); - circularIncrement(spriteIndex, spriteList_); + ViewInfo *current = scrollPoints_->at(i); + + unsigned int nextI = loopIncrement(i, 1, scrollPoints_->size()); + ViewInfo *next = scrollPoints_->at(nextI); + + resetTweens(c, tweenPoints_->at(i), current, next, 0); } } void ScrollingList::destroyItems() { - if(!spriteList_) - { - return; - } - std::vector::iterator it = spriteList_->begin(); - - while(it != spriteList_->end()) - { - if(*it != NULL) - { - deallocateTexture(*it); - - if((*it)->item) - { - delete (*it)->item; - } - - delete *it; - } - - - spriteList_->erase(it); - - it = spriteList_->begin(); - } - - delete spriteList_; - spriteList_ = NULL; + deallocateSpritePoints(); +//todo: who deletes the CollectionInfo? } void ScrollingList::setPoints(std::vector *scrollPoints, std::vector *tweenPoints) { + deallocateSpritePoints(); + scrollPoints_ = scrollPoints; tweenPoints_ = tweenPoints; + + // empty out the list as we will resize it + components_.clear(); + + int size = 0; + + if(scrollPoints) size = scrollPoints_->size(); + components_.resize(size); + + if(items_) + { + itemIndex_ = loopDecrement(0, selectedOffsetIndex_, items_->size()); + } + + allocateSpritePoints(); } unsigned int ScrollingList::getScrollOffsetIndex() { - return firstSpriteIndex_; + return itemIndex_; } void ScrollingList::setScrollOffsetIndex(unsigned int index) { - if(spriteList_ && index < spriteList_->size()) - { - deallocateSpritePoints(); - firstSpriteIndex_ = index; - allocateSpritePoints(); - } + itemIndex_ = index; } void ScrollingList::setSelectedIndex(int selectedIndex) { - selectedSpriteListIndex_ = selectedIndex; + selectedOffsetIndex_ = selectedIndex; +} - for(unsigned int i = 0; spriteList_ && scrollPoints_ && i < selectedSpriteListIndex_; ++i) - { - circularDecrement(firstSpriteIndex_, spriteList_); +Item *ScrollingList::getItemByOffset(int offset) +{ + if(!items_ || items_->size() == 0) return NULL; + unsigned int index = getSelectedIndex(); + if(offset > 0) { + index = loopIncrement(index, offset, items_->size()); } + else if(offset < 0) { + index = loopDecrement(index, offset*-1, items_->size()); + } + + return items_->at(index); } void ScrollingList::click(double nextScrollTime) { - if(!spriteList_) - { - return; - } - if(spriteList_->size() == 0) - { - return; - } - - unsigned int listSize = scrollPoints_->size(); - unsigned int end = circularIncrement(firstSpriteIndex_, listSize - 1, spriteList_); - unsigned int allocSpriteIndex = 0; - unsigned int deallocSpriteIndex = 0; - unsigned int allocPoint = 0; - if(currentScrollDirection_ == ScrollDirectionBack) { - deallocSpriteIndex = end; - circularDecrement(firstSpriteIndex_, spriteList_); - allocSpriteIndex = firstSpriteIndex_; - allocPoint = 0; + // get the previous item + itemIndex_ = loopDecrement(itemIndex_, 1, items_->size()); + Item *i = items_->at(itemIndex_); + componentIndex_ = loopDecrement(componentIndex_, 1, components_.size()); + + deallocateTexture(componentIndex_); + allocateTexture(componentIndex_, i); + + Component *c = components_.at(componentIndex_); + ViewInfo *v = scrollPoints_->at(0); + resetTweens(c, tweenPoints_->at(componentIndex_), v, v, 0); } else if(currentScrollDirection_ == ScrollDirectionForward) { - deallocSpriteIndex = firstSpriteIndex_; - circularIncrement(firstSpriteIndex_, spriteList_); - allocSpriteIndex = circularIncrement(firstSpriteIndex_, listSize - 1, spriteList_); - allocPoint = listSize - 1; + unsigned int itemIncrement = loopIncrement(itemIndex_, scrollPoints_->size(), items_->size()); + itemIndex_ = loopIncrement(itemIndex_, 1, items_->size()); + Item *i = items_->at(itemIncrement); + + deallocateTexture(componentIndex_); + allocateTexture(componentIndex_, i); + + Component *c = components_.at(componentIndex_); + ViewInfo *v = scrollPoints_->back(); + resetTweens(c, tweenPoints_->at(componentIndex_), v, v, 0); + + componentIndex_ = loopIncrement(componentIndex_, 1, components_.size()); } - else - { - return; - } - - deallocateTexture(spriteList_->at(deallocSpriteIndex)); - allocateTexture(spriteList_->at(allocSpriteIndex)); - - Component *c = spriteList_->at(allocSpriteIndex)->component; - ViewInfo *currentViewInfo = scrollPoints_->at(allocPoint); - unsigned int nextI = getNextTween(allocPoint, scrollPoints_); - ViewInfo *nextViewInfo = scrollPoints_->at(nextI); - - resetTweens(c, tweenPoints_->at(allocPoint), currentViewInfo, nextViewInfo, nextScrollTime); -} - -unsigned int ScrollingList::getNextTween(unsigned int currentIndex, std::vector *list) -{ - if(currentScrollDirection_ == ScrollDirectionForward) - { - circularDecrement(currentIndex, list); - } - else if(currentScrollDirection_ == ScrollDirectionBack) - { - circularIncrement(currentIndex, list); - } - - return currentIndex; } void ScrollingList::pageUp() { notifyAllRequested_ = true; + + if(components_.size() == 0) return; + deallocateSpritePoints(); - if(spriteList_ && scrollPoints_ && scrollPoints_->size() > 2) - { - scrollPeriod_ = 0; - unsigned int counts = scrollPoints_->size() - 2; - - for(unsigned int i = 0; i < counts; i++) - { - circularDecrement(firstSpriteIndex_, spriteList_); - } - } - + itemIndex_ = loopDecrement(itemIndex_, components_.size(), items_->size()); + allocateSpritePoints(); - -// CurrentScrollState = ScrollStatePageChange; } void ScrollingList::pageDown() { notifyAllRequested_ = true; + if(components_.size() == 0) return; + deallocateSpritePoints(); - if(spriteList_ && scrollPoints_ && scrollPoints_->size() > 2) - { - unsigned int counts = scrollPoints_->size() - 2; - - scrollPeriod_ = 0; - for(unsigned int i = 0; i < counts; i++) - { - circularIncrement(firstSpriteIndex_, spriteList_); - } - } + itemIndex_ = loopIncrement(itemIndex_, components_.size(), items_->size()); allocateSpritePoints(); } +void ScrollingList::random() +{ + if(!items_ || items_->size() == 0) return; + + notifyAllRequested_ = true; + + deallocateSpritePoints(); + itemIndex_ = rand() % items_->size(); + allocateSpritePoints(); +} void ScrollingList::letterUp() { - notifyAllRequested_ = true; - deallocateSpritePoints(); - - if(spriteList_ && scrollPoints_ && getSelectedCollectionItemSprite()) - { - unsigned int i = 0; - - // Select the previous item in the list in case we are at the top of all the items - // for the currently selected letter. - circularDecrement(firstSpriteIndex_, spriteList_); - std::string startname = getSelectedCollectionItemSprite()->item->lowercaseFullTitle(); - ++i; - - bool done = false; - - // traverse up through the list until we find the first item that starts with a different letter - while(!done && i < spriteList_->size()) - { - circularDecrement(firstSpriteIndex_, spriteList_); - std::string endname = getSelectedCollectionItemSprite()->item->lowercaseFullTitle(); - ++i; - - // check if we are changing characters from a-z, or changing from alpha character to non-alpha character - if(isalpha(startname[0]) ^ isalpha(endname[0])) - { - done = true; - } - else if(isalpha(startname[0]) && isalpha(endname[0]) && startname[0] != endname[0]) - { - done = true; - } - - if(done) - { - // our searching went too far, rewind to the first item in the list that matches the starting letter - circularIncrement(firstSpriteIndex_, spriteList_); - } - } - } - - allocateSpritePoints(); + letterChange(true); } void ScrollingList::letterDown() +{ + letterChange(false); +} + +void ScrollingList::letterChange(bool increment) { notifyAllRequested_ = true; deallocateSpritePoints(); - if(spriteList_ && scrollPoints_ && getSelectedCollectionItemSprite()) + std::string startname = items_->at((itemIndex_+selectedOffsetIndex_)%items_->size())->lowercaseFullTitle(); + + for(unsigned int i = 0; i < items_->size(); ++i) { + unsigned int index = 0; + if(increment) index = loopIncrement(itemIndex_, i, items_->size()); + else index = loopDecrement(itemIndex_, i, items_->size()); - std::string startname = getSelectedCollectionItemSprite()->item->lowercaseFullTitle(); - std::string endname = startname; + std::string endname = items_->at((index+selectedOffsetIndex_)%items_->size())->lowercaseFullTitle(); - unsigned int i = 0; - bool done = false; - while(!done && i < spriteList_->size()) + // check if we are changing characters from a-z, or changing from alpha character to non-alpha character + if((isalpha(startname[0]) ^ isalpha(endname[0])) || + (isalpha(startname[0]) && isalpha(endname[0]) && startname[0] != endname[0])) { - circularIncrement(firstSpriteIndex_, spriteList_); - endname = getSelectedCollectionItemSprite()->item->lowercaseFullTitle(); - ++i; - + itemIndex_ = index; + break; + } + } + + if (!increment) // For decrement, find the first game of the new letter + { + startname = items_->at((itemIndex_+selectedOffsetIndex_)%items_->size())->lowercaseFullTitle(); + + for(unsigned int i = 0; i < items_->size(); ++i) + { + unsigned int index = loopDecrement(itemIndex_, i, items_->size()); + + std::string endname = items_->at((index+selectedOffsetIndex_)%items_->size())->lowercaseFullTitle(); + // check if we are changing characters from a-z, or changing from alpha character to non-alpha character - if(isalpha(startname[0]) ^ isalpha(endname[0])) + if((isalpha(startname[0]) ^ isalpha(endname[0])) || + (isalpha(startname[0]) && isalpha(endname[0]) && startname[0] != endname[0])) { - done = true; - } - else if(isalpha(startname[0]) && isalpha(endname[0]) && startname[0] != endname[0]) - { - done = true; + itemIndex_ = loopIncrement(index,1,items_->size()); + break; } } } @@ -471,18 +373,12 @@ void ScrollingList::letterDown() void ScrollingList::freeGraphicsMemory() { Component::freeGraphicsMemory(); - tweenEnterTime_ = 0; currentScrollDirection_ = ScrollDirectionIdle; requestedScrollDirection_ = ScrollDirectionIdle; currentScrollState_ = ScrollStateIdle; scrollPeriod_ = 0; - - for(unsigned int i = 0; spriteList_ && i < spriteList_->size(); i++) - { - ComponentItemBinding *s = spriteList_->at(i); - - deallocateTexture(s); - } + + deallocateSpritePoints(); } void ScrollingList::triggerMenuEnterEvent() @@ -490,36 +386,10 @@ void ScrollingList::triggerMenuEnterEvent() focus_ = true; notifyAllRequested_ = true; - if(!scrollPoints_) + for(unsigned int i = 0; i < components_.size(); ++i) { - return; - } - if(!spriteList_) - { - return; - } - if(spriteList_->size() == 0 ) - { - return; - } - if(firstSpriteIndex_ >= spriteList_->size()) - { - return; - } - - unsigned int spriteIndex = firstSpriteIndex_; - - for(unsigned int i = 0; i < scrollPoints_->size(); ++i) - { - ComponentItemBinding *s = spriteList_->at(spriteIndex); - - Component *c = s->component; - if(c) - { - c->triggerMenuEnterEvent(); - } - - circularIncrement(spriteIndex, spriteList_); + Component *c = components_.at(i); + if(c) c->triggerMenuEnterEvent(); } } @@ -528,36 +398,10 @@ void ScrollingList::triggerMenuExitEvent() focus_ = false; notifyAllRequested_ = true; - if(!scrollPoints_) + for(unsigned int i = 0; i < components_.size(); ++i) { - return; - } - if(!spriteList_) - { - return; - } - if(spriteList_->size() == 0 ) - { - return; - } - if(firstSpriteIndex_ >= spriteList_->size()) - { - return; - } - - unsigned int spriteIndex = firstSpriteIndex_; - - for(unsigned int i = 0; i < scrollPoints_->size(); ++i) - { - ComponentItemBinding *s = spriteList_->at(spriteIndex); - - Component *c = s->component; - if(c) - { - c->triggerMenuExitEvent(); - } - - circularIncrement(spriteIndex, spriteList_); + Component *c = components_.at(i); + if(c) c->triggerMenuExitEvent(); } } @@ -565,31 +409,18 @@ void ScrollingList::update(float dt) { Component::update(dt); - if(!scrollPoints_) - { - return; - } - if(!spriteList_) - { - return; - } - if(spriteList_->size() == 0) - { - return; - } - bool readyToScroll = true; bool scrollChanged = false; bool scrollRequested = false; bool scrollStopped = false; - // validate all scroll points are done tweening to the next position - for(unsigned int i = 0; i < spriteList_->size(); i++) - { - ComponentItemBinding *s = spriteList_->at(i); - Component *c = s->component; + if(components_.size() == 0) return; + if(!items_ || items_->size() == 0) return; - if(c && c->isMenuScrolling()) + // validate all scroll points are done tweening to the next position + for(std::vector::iterator c = components_.begin(); c != components_.end(); c++) + { + if(*c && (*c)->isMenuScrolling()) { readyToScroll = false; break; @@ -618,24 +449,16 @@ void ScrollingList::update(float dt) { if(currentScrollState_ == ScrollStateStopping) { - click(0); currentScrollState_ = ScrollStateIdle; scrollStopped = true; - - // update the tweens now that we are done - unsigned int spriteIndex = firstSpriteIndex_; + click(0); for(unsigned int i = 0; i < tweenPoints_->size(); ++i) { - ComponentItemBinding *s = spriteList_->at(spriteIndex); + unsigned int cindex = loopIncrement(componentIndex_, i, components_.size()); + Component *c = components_.at(cindex); - Component *c = s->component; - if(c) - { - c->setTweens(tweenPoints_->at(i)); - } - - circularIncrement(spriteIndex, spriteList_); + if(c) c->setTweens(tweenPoints_->at(i)); } } @@ -654,28 +477,47 @@ void ScrollingList::update(float dt) } - unsigned int spriteIndex = firstSpriteIndex_; for(unsigned int i = 0; i < scrollPoints_->size(); i++) { - updateSprite(spriteIndex, i, (scrollRequested || scrollChanged), dt, scrollPeriod_); - circularIncrement(spriteIndex, spriteList_); + unsigned int cindex = loopIncrement(componentIndex_, i, components_.size()); + + Component *c = components_.at(cindex); + + if(c && (scrollRequested || scrollChanged)) + { + unsigned int nextI = 0; + if(currentScrollDirection_ == ScrollDirectionBack) + { + nextI = loopIncrement(i, 1, scrollPoints_->size()); + } + if(currentScrollDirection_ == ScrollDirectionForward) + { + nextI = loopDecrement(i, 1, scrollPoints_->size()); + } + + ViewInfo *currentvi = scrollPoints_->at(i); + ViewInfo *nextvi = scrollPoints_->at(nextI); + + resetTweens(c, tweenPoints_->at(i), currentvi, nextvi, scrollPeriod_); + c->triggerMenuScrollEvent(); + } + + if(c) c->update(dt); + } if(scrollStopped || (notifyAllRequested_ && focus_)) { - ComponentItemBinding *sprite = getPendingCollectionItemSprite(); Item *item = NULL; - - if(sprite) - { - item = sprite->item; - } + unsigned index = loopIncrement(itemIndex_, selectedOffsetIndex_, items_->size()); + item = items_->at(index); for(std::vector::iterator it = notificationComponents_.begin(); it != notificationComponents_.end(); it++) { MenuNotifierInterface *c = *it; + if(c && item) { c->onNewItemSelected(item); @@ -691,47 +533,25 @@ void ScrollingList::update(float dt) notifyAllRequested_ = false; } -void ScrollingList::updateSprite(unsigned int spriteIndex, unsigned int pointIndex, bool newScroll, float dt, double nextScrollTime) -{ - ComponentItemBinding *s = spriteList_->at(spriteIndex); - - Component *c = s->component; - //todo: remove me - if(c && newScroll) - { - ViewInfo *currentViewInfo = scrollPoints_->at(pointIndex); - unsigned int nextI = getNextTween(pointIndex, scrollPoints_); - ViewInfo *nextViewInfo = scrollPoints_->at(nextI); - - resetTweens(c, tweenPoints_->at(pointIndex), currentViewInfo, nextViewInfo, nextScrollTime); - c->triggerMenuScrollEvent(); - } - if(c) - { - c->update(dt); - } - - circularIncrement(spriteIndex, spriteList_); +unsigned int ScrollingList::getSelectedIndex() +{ + if(!items_) return 0; + return loopIncrement(itemIndex_, selectedOffsetIndex_, items_->size()); +} + +unsigned int ScrollingList::getSize() +{ + if(!items_) return 0; + + return items_->size(); } void ScrollingList::resetTweens(Component *c, AnimationEvents *sets, ViewInfo *currentViewInfo, ViewInfo *nextViewInfo, double scrollTime) { - if(!c) - { - return; - } - if(!sets) - { - return; - } - if(!currentViewInfo) - { - return; - } - if(!nextViewInfo) - { - return; - } + if(!c) return; + if(!sets) return; + if(!currentViewInfo) return; + if(!nextViewInfo) return; currentViewInfo->ImageHeight = c->baseViewInfo.ImageHeight; currentViewInfo->ImageWidth = c->baseViewInfo.ImageWidth; @@ -763,15 +583,11 @@ void ScrollingList::resetTweens(Component *c, AnimationEvents *sets, ViewInfo *c } -bool ScrollingList::allocateTexture(ComponentItemBinding *s) +bool ScrollingList::allocateTexture(unsigned int index, Item *item) { - if(!s || s->component != NULL) - { - return false; - } + if(index >= components_.size()) return false; - const Item *item = s->item; //todo: will create a runtime fault if not of the right type //todo: remove coupling from knowing the collection name @@ -785,71 +601,74 @@ bool ScrollingList::allocateTexture(ComponentItemBinding *s) // check collection path for art based on gamename config_.getMediaPropertyAbsolutePath(collectionName, imageType_, false, imagePath); - t = imageBuild.CreateImage(imagePath, item->name, scaleX_, scaleY_); + t = imageBuild.CreateImage(imagePath, page, item->name, scaleX_, scaleY_); // check sub-collection path for art based on gamename if(!t) { config_.getMediaPropertyAbsolutePath(item->collectionInfo->name, imageType_, false, imagePath); - t = imageBuild.CreateImage(imagePath, item->name, scaleX_, scaleY_); + t = imageBuild.CreateImage(imagePath, page, item->name, scaleX_, scaleY_); } // check collection path for art based on game name (full title) if(!t && item->title != item->fullTitle) { config_.getMediaPropertyAbsolutePath(collectionName, imageType_, false, imagePath); - t = imageBuild.CreateImage(imagePath, item->fullTitle, scaleX_, scaleY_); + t = imageBuild.CreateImage(imagePath, page, item->fullTitle, scaleX_, scaleY_); } // check sub-collection path for art based on game name (full title) if(!t && item->title != item->fullTitle) { config_.getMediaPropertyAbsolutePath(item->collectionInfo->name, imageType_, false, imagePath); - t = imageBuild.CreateImage(imagePath, item->fullTitle, scaleX_, scaleY_); + t = imageBuild.CreateImage(imagePath, page, item->fullTitle, scaleX_, scaleY_); } // check collection path for art based on parent game name if(!t && item->cloneof != "") { config_.getMediaPropertyAbsolutePath(collectionName, imageType_, false, imagePath); - t = imageBuild.CreateImage(imagePath, item->cloneof, scaleX_, scaleY_); + t = imageBuild.CreateImage(imagePath, page, item->cloneof, scaleX_, scaleY_); } // check sub-collection path for art based on parent game name if(!t && item->cloneof != "") { config_.getMediaPropertyAbsolutePath(item->collectionInfo->name, imageType_, false, imagePath); - t = imageBuild.CreateImage(imagePath, item->cloneof, scaleX_, scaleY_); + t = imageBuild.CreateImage(imagePath, page, item->cloneof, scaleX_, scaleY_); } // check collection path for art based on system name if(!t) { config_.getMediaPropertyAbsolutePath(item->name, imageType_, true, imagePath); - t = imageBuild.CreateImage(imagePath, imageType_, scaleX_, scaleY_); + t = imageBuild.CreateImage(imagePath, page, imageType_, scaleX_, scaleY_); } if (!t) { - t = new Text(item->title, fontInst_, scaleX_, scaleY_); + t = new Text(item->title, page, fontInst_, scaleX_, scaleY_); } if(t) { - s->component = t; + components_.at(index) = t; } return true; } -void ScrollingList::deallocateTexture(ComponentItemBinding *s) +void ScrollingList::deallocateTexture(unsigned int index) { - if(s && s->component != NULL) + if(components_.size() <= index) return; + + Component *s = components_.at(index); + + if(s) { - delete s->component; - //todo: memory leak here, need to destroy allocated tween points here and in page (cannot be destroyed by component) + delete s; + components_.at(index) = NULL; } - s->component = NULL; } void ScrollingList::draw() @@ -860,32 +679,13 @@ void ScrollingList::draw() void ScrollingList::draw(unsigned int layer) { - if(!scrollPoints_) - { - return; - } - if(!spriteList_) - { - return; - } - if(spriteList_->size() == 0) - { - return; - } + + if(components_.size() == 0) return; - unsigned int spriteIndex = firstSpriteIndex_; - - for(unsigned int i = 0; i < scrollPoints_->size(); i++) + for(unsigned int i = 0; i < components_.size(); ++i) { - ComponentItemBinding *s = spriteList_->at(spriteIndex); - Component *c = s->component; - ViewInfo *currentViewInfo = scrollPoints_->at(i); - - if(c && currentViewInfo && currentViewInfo->Layer == layer) - { - c->draw(); - } - circularIncrement(spriteIndex, spriteList_); + Component *c = components_.at(i); + if(c && c->baseViewInfo.Layer == layer) c->draw(); } } @@ -897,69 +697,6 @@ void ScrollingList::setScrollDirection(ScrollDirection direction) scrollStopRequested_ = (direction == ScrollDirectionIdle); } -void ScrollingList::removeSelectedItem() -{ - ComponentItemBinding *sprite = getSelectedCollectionItemSprite(); - if(sprite && spriteList_) - { - Item *item = sprite->item; - deallocateTexture(sprite); - int index = (firstSpriteIndex_ + selectedSpriteListIndex_) % spriteList_->size(); - - std::vector::iterator it = spriteList_->begin() + index; - - spriteList_->erase(it); - delete sprite; - - if(item) - { - delete item; - } - - if(selectedSpriteListIndex_ >= spriteList_->size()) - { - selectedSpriteListIndex_ = 0; - } - if(firstSpriteIndex_ >= spriteList_->size()) - { - firstSpriteIndex_ = 0; - } - } -} - - -std::vector *ScrollingList::getCollectionItemSprites() -{ - return spriteList_; -} - -ComponentItemBinding* ScrollingList::getSelectedCollectionItemSprite() -{ - ComponentItemBinding *item = NULL; - - if(spriteList_ && spriteList_->size() > 0) - { - int index = (firstSpriteIndex_ + selectedSpriteListIndex_) % spriteList_->size(); - - item = spriteList_->at(index); - } - - return item; -} - -ComponentItemBinding* ScrollingList::getPendingCollectionItemSprite() -{ - ComponentItemBinding *item = NULL; - unsigned int index = firstSpriteIndex_; - if(spriteList_ && spriteList_->size() > 0) - { - index = (index + selectedSpriteListIndex_) % spriteList_->size(); - - item = spriteList_->at(index); - } - - return item; -} void ScrollingList::addComponentForNotifications(MenuNotifierInterface *c) { @@ -979,108 +716,17 @@ void ScrollingList::removeComponentForNotifications(MenuNotifierInterface *c) } } - -ComponentItemBinding* ScrollingList::getPendingSelectedCollectionItemSprite() -{ - ComponentItemBinding *item = NULL; - - if(spriteList_) - { - unsigned int index = selectedSpriteListIndex_; - - if(currentScrollDirection_ == ScrollDirectionBack) - { - circularDecrement(index, spriteList_); - } - if(currentScrollDirection_ == ScrollDirectionForward) - { - circularIncrement(index, spriteList_); - } - - if(spriteList_ && spriteList_->size() > 0) - { - index = (index + selectedSpriteListIndex_) % spriteList_->size(); - - item = spriteList_->at(index); - } - } - - return item; -} - bool ScrollingList::isIdle() { - return (Component::isIdle() && currentScrollState_ == ScrollStateIdle); -} + if(!Component::isIdle() || currentScrollState_ != ScrollStateIdle) return false; -void ScrollingList::circularIncrement(unsigned int &index, std::vector* list) -{ - index++; - - if(index >= list->size()) + for(unsigned int i = 0; i < components_.size(); ++i) { - index = 0; - } -} - -void ScrollingList::circularDecrement(unsigned int &index, std::vector* list) -{ - if(index > 0) - { - index--; - } - else - { - if(list->size() > 0) - { - index = list->size() - 1; - } - else - { - index = 0; - } - } -} - -int ScrollingList::circularIncrement(unsigned int index, unsigned int offset, std::vector *list) -{ - unsigned int end = index + offset; - - while(end >= list->size() && list->size() > 0) - { - end -= list->size(); + Component *c = components_.at(i); + if(c && !c->isIdle()) return false; } - return end; + return true; } -void ScrollingList::circularIncrement(unsigned int &index, std::vector *list) -{ - index++; - - if(index >= list->size()) - { - index = 0; - } -} - -void ScrollingList::circularDecrement(unsigned int &index, std::vector *list) -{ - if(index > 0) - { - index--; - } - else - { - if(list && list->size() > 0) - { - index = list->size() - 1; - } - else - { - index = 0; - } - } -} - diff --git a/RetroFE/Source/Graphics/Component/ScrollingList.h b/RetroFE/Source/Graphics/Component/ScrollingList.h index 2a87fb9..4dafc51 100644 --- a/RetroFE/Source/Graphics/Component/ScrollingList.h +++ b/RetroFE/Source/Graphics/Component/ScrollingList.h @@ -1,136 +1,139 @@ -/* This file is part of RetroFE. - * - * RetroFE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * RetroFE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with RetroFE. If not, see . - */ -#pragma once - -#include -#include "Component.h" -#include "../Animate/Tween.h" -#include "../ComponentItemBinding.h" -#include "../MenuNotifierInterface.h" -#include "../ViewInfo.h" -#include "../../Database/Configuration.h" -#include - - -//todo: This scrolling implementation needs to be overhauled -// It needs to have a common interface to support different menu types -// (It was originally sandbox code that creeped into here) - -class Configuration; -class Font; - -class ScrollingList : public Component -{ -public: - enum ScrollDirection - { - ScrollDirectionBack, - ScrollDirectionForward, - ScrollDirectionIdle, - - }; - - ScrollingList(Configuration &c, - float scaleX, - float scaleY, - Font *font, - std::string layoutKey, - std::string imageType); - - ScrollingList(const ScrollingList ©); - virtual ~ScrollingList(); - void triggerMenuEnterEvent(); - void triggerMenuExitEvent(); - - bool allocateTexture(ComponentItemBinding *s); - void deallocateTexture(ComponentItemBinding *s); - void setItems(std::vector *spriteList); - void destroyItems(); - void setPoints(std::vector *scrollPoints, std::vector *tweenPoints); - void setScrollDirection(ScrollDirection direction); - void pageUp(); - void pageDown(); - void letterUp(); - void letterDown(); - bool isIdle(); +/* This file is part of RetroFE. + * + * RetroFE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RetroFE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RetroFE. If not, see . + */ +#pragma once + +#include +#include "Component.h" +#include "../Animate/Tween.h" +#include "../Page.h" +#include "../MenuNotifierInterface.h" +#include "../ViewInfo.h" +#include "../../Database/Configuration.h" +#include + + +//todo: This scrolling implementation needs to be overhauled +// It needs to have a common interface to support different menu types +// (It was originally sandbox code that creeped into here) + +class Configuration; +class Font; + +class ScrollingList : public Component +{ +public: + enum ScrollDirection + { + ScrollDirectionBack, + ScrollDirectionForward, + ScrollDirectionIdle, + + }; + + ScrollingList(Configuration &c, + Page &p, + float scaleX, + float scaleY, + Font *font, + std::string layoutKey, + std::string imageType); + + ScrollingList(const ScrollingList ©); + virtual ~ScrollingList(); + void triggerMenuEnterEvent(); + void triggerMenuExitEvent(); + + bool allocateTexture(unsigned int index, Item *i); + void deallocateTexture(unsigned int index); + void setItems(std::vector *items); + void destroyItems(); + void setPoints(std::vector *scrollPoints, std::vector *tweenPoints); + void setScrollDirection(ScrollDirection direction); + unsigned int getSelectedIndex(); + unsigned int getSize(); + void pageUp(); + void pageDown(); + void letterUp(); + void letterDown(); + void letterChange(bool increment); + void random(); + bool isIdle(); unsigned int getScrollOffsetIndex(); void setScrollOffsetIndex(unsigned int index); - void setSelectedIndex(int selectedIndex); - ComponentItemBinding *getSelectedCollectionItemSprite(); - ComponentItemBinding *getPendingCollectionItemSprite(); - ComponentItemBinding *getPendingSelectedCollectionItemSprite(); - void addComponentForNotifications(MenuNotifierInterface *c); - void removeComponentForNotifications(MenuNotifierInterface *c); - std::vector *getCollectionItemSprites(); - void removeSelectedItem(); - void freeGraphicsMemory(); - void update(float dt); - void draw(); - void draw(unsigned int layer); - void setScrollAcceleration(float value); - void setStartScrollTime(float value); - bool horizontalScroll; - -private: - void click(double nextScrollTime); - void deallocateSpritePoints(); - void allocateSpritePoints(); - void updateSprite(unsigned int spriteIndex, unsigned int pointIndex, bool newScroll, float dt, double nextScrollTime); - unsigned int getNextTween(unsigned int currentIndex, std::vector *list); - void resetTweens(Component *c, AnimationEvents *sets, ViewInfo *currentViewInfo, ViewInfo *nextViewInfo, double scrollTime); - - enum ScrollState - { - ScrollStateActive, - ScrollStatePageChange, - ScrollStateStopping, - ScrollStateIdle - }; - - std::vector *spriteList_; - std::vector *scrollPoints_; - std::vector *tweenPoints_; - std::vector notificationComponents_; - float tweenEnterTime_; - bool focus_; - - unsigned int firstSpriteIndex_; - unsigned int selectedSpriteListIndex_; - bool scrollStopRequested_; - bool notifyAllRequested_; - ScrollDirection currentScrollDirection_; - ScrollDirection requestedScrollDirection_; - ScrollState currentScrollState_; - float scrollAcceleration_; - float startScrollTime_; - float scrollPeriod_; - - int circularIncrement(unsigned int index, unsigned int offset, std::vector *list); - void circularIncrement(unsigned &index, std::vector *list); - void circularDecrement(unsigned &index, std::vector *list); - void circularIncrement(unsigned &index, std::vector *list); - void circularDecrement(unsigned &index, std::vector *list); - void updateOffset(float dt); - - std::string collection_; - Configuration &config_; - float scaleX_; - float scaleY_; - Font *fontInst_; - std::string layoutKey_; - std::string imageType_; -}; - + void setSelectedIndex(int selectedIndex); + Item *getItemByOffset(int offset); + void addComponentForNotifications(MenuNotifierInterface *c); + void removeComponentForNotifications(MenuNotifierInterface *c); + void freeGraphicsMemory(); + void update(float dt); + void draw(); + void draw(unsigned int layer); + void setScrollAcceleration(float value); + void setStartScrollTime(float value); + bool horizontalScroll; + void deallocateSpritePoints(); + void allocateSpritePoints(); + + +private: + void click(double nextScrollTime); + void resetTweens(Component *c, AnimationEvents *sets, ViewInfo *currentViewInfo, ViewInfo *nextViewInfo, double scrollTime); + unsigned int loopIncrement(unsigned int offset, unsigned int i, unsigned int size); + unsigned int loopDecrement(unsigned int offset, unsigned int i, unsigned int size); + + enum ScrollState + { + ScrollStateActive, + ScrollStatePageChange, + ScrollStateStopping, + ScrollStateIdle + }; + + std::vector *spriteList_; + std::vector *scrollPoints_; + std::vector *tweenPoints_; + std::vector notificationComponents_; + bool focus_; + + unsigned int itemIndex_; + unsigned int componentIndex_; + unsigned int selectedOffsetIndex_; + + bool scrollStopRequested_; + bool notifyAllRequested_; + + ScrollDirection currentScrollDirection_; + ScrollDirection requestedScrollDirection_; + ScrollState currentScrollState_; + + float scrollAcceleration_; + float startScrollTime_; + float scrollPeriod_; + + Configuration &config_; + float scaleX_; + float scaleY_; + Font *fontInst_; + std::string layoutKey_; + std::string imageType_; + + + std::vector *items_; + std::vector components_; + + +}; + diff --git a/RetroFE/Source/Graphics/Component/Text.cpp b/RetroFE/Source/Graphics/Component/Text.cpp index d703cf3..c065ae2 100644 --- a/RetroFE/Source/Graphics/Component/Text.cpp +++ b/RetroFE/Source/Graphics/Component/Text.cpp @@ -20,8 +20,9 @@ #include "../Font.h" #include -Text::Text(std::string text, Font *font, float scaleX, float scaleY) - : textData_(text) +Text::Text(std::string text, Page &p, Font *font, float scaleX, float scaleY) + : Component(p) + , textData_(text) , fontInst_(font) , scaleX_(scaleX) , scaleY_(scaleY) diff --git a/RetroFE/Source/Graphics/Component/Text.h b/RetroFE/Source/Graphics/Component/Text.h index 6f38a64..321cd44 100644 --- a/RetroFE/Source/Graphics/Component/Text.h +++ b/RetroFE/Source/Graphics/Component/Text.h @@ -16,7 +16,7 @@ #pragma once #include "Component.h" - +#include "../Page.h" #include #include @@ -26,7 +26,7 @@ class Text : public Component { public: //todo: should have a Font flass that references fontcache, pass that in as an argument - Text(std::string text, Font *font, float scaleX, float scaleY); + Text(std::string text, Page &p, Font *font, float scaleX, float scaleY); virtual ~Text(); void setText(std::string text); void allocateGraphicsMemory(); diff --git a/RetroFE/Source/Graphics/Component/VideoBuilder.cpp b/RetroFE/Source/Graphics/Component/VideoBuilder.cpp index fd3e950..30b615c 100644 --- a/RetroFE/Source/Graphics/Component/VideoBuilder.cpp +++ b/RetroFE/Source/Graphics/Component/VideoBuilder.cpp @@ -21,7 +21,7 @@ #include -VideoComponent * VideoBuilder::createVideo(std::string path, std::string name, float scaleX, float scaleY) +VideoComponent * VideoBuilder::createVideo(std::string path, Page &page, std::string name, float scaleX, float scaleY) { VideoComponent *component = NULL; std::vector extensions; @@ -40,7 +40,7 @@ VideoComponent * VideoBuilder::createVideo(std::string path, std::string name, f if(video) { - component = new VideoComponent(video, file, scaleX, scaleY); + component = new VideoComponent(video, page, file, scaleX, scaleY); } } diff --git a/RetroFE/Source/Graphics/Component/VideoBuilder.h b/RetroFE/Source/Graphics/Component/VideoBuilder.h index f944587..bd9954e 100644 --- a/RetroFE/Source/Graphics/Component/VideoBuilder.h +++ b/RetroFE/Source/Graphics/Component/VideoBuilder.h @@ -17,13 +17,14 @@ #include "Image.h" #include "VideoComponent.h" +#include "../Page.h" #include "../../Video/VideoFactory.h" //todo: this is more of a factory than a builder class VideoBuilder { public: - VideoComponent * createVideo(std::string path, std::string name, float scaleX, float scaleY); + VideoComponent * createVideo(std::string path, Page &page, std::string name, float scaleX, float scaleY); private: VideoFactory factory_; diff --git a/RetroFE/Source/Graphics/Component/VideoComponent.cpp b/RetroFE/Source/Graphics/Component/VideoComponent.cpp index 3b63bbd..672b6b4 100644 --- a/RetroFE/Source/Graphics/Component/VideoComponent.cpp +++ b/RetroFE/Source/Graphics/Component/VideoComponent.cpp @@ -20,8 +20,9 @@ #include "../../Utility/Log.h" #include "../../SDL.h" -VideoComponent::VideoComponent(IVideo *videoInst, std::string videoFile, float scaleX, float scaleY) - : videoFile_(videoFile) +VideoComponent::VideoComponent(IVideo *videoInst, Page &p, std::string videoFile, float scaleX, float scaleY) + : Component(p) + , videoFile_(videoFile) , videoInst_(videoInst) , scaleX_(scaleX) , scaleY_(scaleY) diff --git a/RetroFE/Source/Graphics/Component/VideoComponent.h b/RetroFE/Source/Graphics/Component/VideoComponent.h index 81fa93a..9477c7b 100644 --- a/RetroFE/Source/Graphics/Component/VideoComponent.h +++ b/RetroFE/Source/Graphics/Component/VideoComponent.h @@ -16,6 +16,7 @@ #pragma once #include "Component.h" #include "Image.h" +#include "../Page.h" #include "../../Collection/Item.h" #include "../../Video/IVideo.h" #include @@ -24,7 +25,7 @@ class VideoComponent : public Component { public: - VideoComponent(IVideo *videoInst, std::string videoFile, float scaleX, float scaleY); + VideoComponent(IVideo *videoInst, Page &p, std::string videoFile, float scaleX, float scaleY); virtual ~VideoComponent(); void update(float dt); void draw(); diff --git a/RetroFE/Source/Graphics/Font.cpp b/RetroFE/Source/Graphics/Font.cpp index b1e8354..3d1e70b 100644 --- a/RetroFE/Source/Graphics/Font.cpp +++ b/RetroFE/Source/Graphics/Font.cpp @@ -145,7 +145,7 @@ bool Font::initialize(std::string fontPath, int fontSize, SDL_Color color) SDL_LockMutex(SDL::getMutex()); texture = SDL_CreateTextureFromSurface(SDL::getRenderer(), atlasSurface); - SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_ADD); + SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND); SDL_FreeSurface(atlasSurface); SDL_UnlockMutex(SDL::getMutex()); diff --git a/RetroFE/Source/Graphics/Page.cpp b/RetroFE/Source/Graphics/Page.cpp index eb7392e..b889fff 100644 --- a/RetroFE/Source/Graphics/Page.cpp +++ b/RetroFE/Source/Graphics/Page.cpp @@ -23,17 +23,18 @@ #include "Component/ScrollingList.h" #include "../Sound/Sound.h" #include "ComponentItemBindingBuilder.h" +#include #include Page::Page(Configuration &config) : config_(config) , activeMenu_(NULL) , menuDepth_(0) - , items_(NULL) , scrollActive_(false) , selectedItem_(NULL) , textStatusComponent_(NULL) , selectedItemChanged_(false) + , playlistChanged_(false) , loadSoundChunk_(NULL) , unloadSoundChunk_(NULL) , highlightSoundChunk_(NULL) @@ -43,6 +44,11 @@ Page::Page(Configuration &config) } Page::~Page() +{ +} + + +void Page::DeInitialize() { MenuVector_T::iterator it = menus_.begin(); while(it != menus_.end()) @@ -89,6 +95,19 @@ Page::~Page() delete selectSoundChunk_; selectSoundChunk_ = NULL; } + CollectionVector_T::iterator itc = collections_.begin(); + + while(itc != collections_.end()) + { + itc->collection->Save(); + + if(itc->collection) + { + delete itc->collection; + } + collections_.erase(itc); + itc = collections_.begin(); + } } @@ -278,6 +297,12 @@ Item *Page::getSelectedItem() return selectedItem_; } +Item *Page::getSelectedItem(int offset) +{ + return activeMenu_->getItemByOffset(offset); +} + + void Page::removeSelectedItem() { /* @@ -315,25 +340,39 @@ float Page::getMinShowTime() return minShowTime_; } +void Page::playlistChange() +{ + if(activeMenu_) + { + activeMenu_->triggerPlaylistChangeEvent(playlist_->first); + } + + for(unsigned int i = 0; i < NUM_LAYERS; ++i) + { + for(std::vector::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it) + { + (*it)->triggerPlaylistChangeEvent(playlist_->first); + } + } +} + void Page::highlight() { Item *item = selectedItem_; - if(item) + if(!item) return; + if(activeMenu_) { - if(activeMenu_) - { - activeMenu_->triggerHighlightEvent(item); - activeMenu_->scrollActive = scrollActive_; - } + activeMenu_->triggerHighlightEvent(); + activeMenu_->scrollActive = scrollActive_; + } - for(unsigned int i = 0; i < NUM_LAYERS; ++i) + for(unsigned int i = 0; i < NUM_LAYERS; ++i) + { + for(std::vector::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it) { - for(std::vector::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it) - { - (*it)->triggerHighlightEvent(item); - (*it)->scrollActive = scrollActive_; - } + (*it)->triggerHighlightEvent(); + (*it)->scrollActive = scrollActive_; } } } @@ -392,6 +431,11 @@ void Page::pageScroll(ScrollDirection direction) } } +void Page::selectRandom() +{ + if(activeMenu_) activeMenu_->random(); +} + void Page::letterScroll(ScrollDirection direction) { if(activeMenu_) @@ -407,11 +451,19 @@ void Page::letterScroll(ScrollDirection direction) } } +unsigned int Page::getCollectionSize() +{ + return activeMenu_->getSize(); +} + +unsigned int Page::getSelectedIndex() +{ + return activeMenu_->getSelectedIndex(); +} + bool Page::pushCollection(CollectionInfo *collection) { - collections_.push_back(collection); - std::vector *sprites = ComponentItemBindingBuilder::buildCollectionItems(&collection->items); int menuExitIndex = -1; int menuEnterIndex = -1; @@ -426,19 +478,31 @@ bool Page::pushCollection(CollectionInfo *collection) menuExitIndex = menuDepth_ - 1; } + // grow the menu as needed if(menus_.size() >= menuDepth_ && activeMenu_) { - ScrollingList *newList = new ScrollingList(*activeMenu_); - newList->forceIdle(); - pushMenu(newList); + activeMenu_ = new ScrollingList(*activeMenu_); + activeMenu_->forceIdle(); + pushMenu(activeMenu_); } + activeMenu_ = menus_[menuDepth_]; activeMenu_->collectionName = collection->name; - activeMenu_->destroyItems(); - activeMenu_->setItems(sprites); + activeMenu_->setItems(&collection->items); activeMenu_->triggerMenuEnterEvent(); + // build the collection info instance + MenuInfo_S info; + info.collection = collection; + info.menu = activeMenu_; + info.playlist = collection->playlists.begin(); + info.queueDelete = false; + collections_.push_back(info); + + playlist_ = info.playlist; + playlistChanged_ = true; + if(menuDepth_ < menus_.size()) { menuEnterIndex = menuDepth_; @@ -469,22 +533,26 @@ bool Page::popCollection() { int menuExitIndex = -1; int menuEnterIndex = -1; - CollectionInfo *collection = NULL; - if(menuDepth_ <= 1) - { - return false; - } - if(collections_.size() <= 1) - { - return false; - } + if(!activeMenu_) return false; + if(menuDepth_ <= 1) return false; + if(collections_.size() <= 1) return false; + + // queue the collection for deletion + MenuInfo_S *info = &collections_.back(); + info->queueDelete = true; + deleteCollections_.push_back(*info); + + // get the next collection off of the stack collections_.pop_back(); - collection = collections_.back(); + info = &collections_.back(); + playlist_ = info->playlist; + playlistChanged_ = true; if(activeMenu_) { activeMenu_->triggerMenuExitEvent(); + } menuDepth_--; @@ -500,7 +568,7 @@ bool Page::popCollection() { for(std::vector::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it) { - (*it)->collectionName = collection->name; + (*it)->collectionName = info->collection->name; if(menuEnterIndex >= 0) { @@ -517,6 +585,54 @@ bool Page::popCollection() return true; } +void Page::nextPlaylist() +{ + MenuInfo_S &info = collections_.back(); + info.collection->Save(); + unsigned int numlists = info.collection->playlists.size(); + + for(unsigned int i = 0; i <= numlists; ++i) + { + playlist_++; + // wrap + if(playlist_ == info.collection->playlists.end()) playlist_ = info.collection->playlists.begin(); + + // find the first playlist + if(playlist_->second->size() != 0) break; + } + + activeMenu_->setItems(playlist_->second); + activeMenu_->triggerMenuEnterEvent(); + playlistChanged_ = true; +} + +void Page::favPlaylist() +{ + MenuInfo_S &info = collections_.back(); + info.collection->Save(); + unsigned int numlists = info.collection->playlists.size(); + + // Store current playlist + CollectionInfo::Playlists_T::iterator playlist_store = playlist_; + + for(unsigned int i = 0; i <= numlists; ++i) + { + playlist_++; + // wrap + if(playlist_ == info.collection->playlists.end()) playlist_ = info.collection->playlists.begin(); + + // find the first playlist + if(playlist_->second->size() != 0 && playlist_->first == "favorites") break; + } + + // Do not change playlist if favorites does not exist or if it's empty + if ( playlist_->second->size() == 0 || playlist_->first != "favorites") + playlist_ = playlist_store; + + activeMenu_->setItems(playlist_->second); + activeMenu_->triggerMenuEnterEvent(); + playlistChanged_ = true; +} void Page::update(float dt) { @@ -526,6 +642,12 @@ void Page::update(float dt) menu->update(dt); } + + if(playlistChanged_) + { + playlistChange(); + playlistChanged_ = false; + } if(selectedItemChanged_ && !scrollActive_) { @@ -544,7 +666,33 @@ void Page::update(float dt) { for(std::vector::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it) { - (*it)->update(dt); + if(*it) (*it)->update(dt); + } + } + + // many nodes still have handles on the collection info. We need to delete + // them once everything is done using them + std::list::iterator del = deleteCollections_.begin(); + + while(del != deleteCollections_.end()) + { + MenuInfo_S &info = *del; + if(info.queueDelete && info.menu && info.menu->isIdle()) + { + std::list::iterator next = del; + ++next; + + if(info.collection) + { + info.collection->Save(); + delete info.collection; + } + deleteCollections_.erase(del); + del = next; + } + else + { + ++del; } } } @@ -555,7 +703,7 @@ void Page::draw() { for(std::vector::iterator it = LayerComponents[i].begin(); it != LayerComponents[i].end(); ++it) { - (*it)->draw(); + if(*it && (*it)->baseViewInfo.Layer == i) (*it)->draw(); } for(MenuVector_T::iterator it = menus_.begin(); it != menus_.end(); it++) @@ -567,16 +715,58 @@ void Page::draw() } +void Page::removePlaylist() +{ + if(!selectedItem_) return; + if(!selectedItem_->leaf) return; + + MenuInfo_S &info = collections_.back(); + CollectionInfo *collection = info.collection; + + std::vector *items = collection->playlists["favorites"]; + std::vector::iterator it = std::find(items->begin(), items->end(), selectedItem_); + + if(it != items->end()) + { + items->erase(it); + collection->saveRequest = true; + + if(activeMenu_) + { + activeMenu_->deallocateSpritePoints(); + activeMenu_->allocateSpritePoints(); + } + } +} + +void Page::addPlaylist() +{ + if(!selectedItem_) return; + if(!selectedItem_->leaf) return; + + MenuInfo_S &info = collections_.back(); + CollectionInfo *collection = info.collection; + + std::vector *items = collection->playlists["favorites"]; + if(playlist_->first != "favorites" && std::find(items->begin(), items->end(), selectedItem_) == items->end()) + { + items->push_back(selectedItem_); + collection->saveRequest = true; + if(activeMenu_) + { + activeMenu_->deallocateSpritePoints(); + activeMenu_->allocateSpritePoints(); + } + } +} + std::string Page::getCollectionName() { - CollectionInfo *info = collections_.back(); + if(collections_.size() == 0) return ""; - if(info) - { - return info->name; - } + MenuInfo_S &info = collections_.back(); + return info.collection->name; - return ""; } void Page::freeGraphicsMemory() diff --git a/RetroFE/Source/Graphics/Page.h b/RetroFE/Source/Graphics/Page.h index 617fa76..a53e28c 100644 --- a/RetroFE/Source/Graphics/Page.h +++ b/RetroFE/Source/Graphics/Page.h @@ -16,13 +16,13 @@ #pragma once #include "MenuNotifierInterface.h" +#include "../Collection/CollectionInfo.h" #include #include #include #include -class CollectionInfo; class Component; class Configuration; class ScrollingList; @@ -43,9 +43,12 @@ public: Page(Configuration &c); virtual ~Page(); + void DeInitialize(); virtual void onNewItemSelected(Item *); bool pushCollection(CollectionInfo *collection); bool popCollection(); + void nextPlaylist(); + void favPlaylist(); void pushMenu(ScrollingList *s); bool isMenusFull(); void setLoadSound(Sound *chunk); @@ -55,6 +58,9 @@ public: bool addComponent(Component *c); void pageScroll(ScrollDirection direction); void letterScroll(ScrollDirection direction); + unsigned int getCollectionSize(); + unsigned int getSelectedIndex(); + void selectRandom(); void start(); void startComponents(); void stop(); @@ -62,6 +68,7 @@ public: bool isHorizontalScroll(); unsigned int getMenuDepth(); Item *getSelectedItem(); + Item *getSelectedItem(int offset); void removeSelectedItem(); void setScrollOffsetIndex(unsigned int i); unsigned int getScrollOffsetIndex(); @@ -78,33 +85,50 @@ public: std::string getCollectionName(); void setMinShowTime(float value); float getMinShowTime(); + void addPlaylist(); + void removePlaylist(); private: void highlight(); + void playlistChange(); std::string collectionName_; Configuration &config_; + + struct MenuInfo_S + { + CollectionInfo *collection; + ScrollingList *menu; + CollectionInfo::Playlists_T::iterator playlist; + bool queueDelete; + }; + typedef std::vector MenuVector_T; - typedef std::vector CollectionInfo_T; + typedef std::list CollectionVector_T; ScrollingList *activeMenu_; unsigned int menuDepth_; MenuVector_T menus_; - CollectionInfo_T collections_; + CollectionVector_T collections_; + CollectionVector_T deleteCollections_; - static const unsigned int NUM_LAYERS = 8; + static const unsigned int NUM_LAYERS = 20; std::vector LayerComponents[NUM_LAYERS]; - std::vector *items_; + std::list deleteMenuList_; + std::list deleteCollectionList_; + bool scrollActive_; Item *selectedItem_; Text *textStatusComponent_; bool selectedItemChanged_; + bool playlistChanged_; Sound *loadSoundChunk_; Sound *unloadSoundChunk_; Sound *highlightSoundChunk_; Sound *selectSoundChunk_; float minShowTime_; float elapsedTime_; + CollectionInfo::Playlists_T::iterator playlist_; }; diff --git a/RetroFE/Source/Graphics/PageBuilder.cpp b/RetroFE/Source/Graphics/PageBuilder.cpp index e912ccc..7e9e7b8 100644 --- a/RetroFE/Source/Graphics/PageBuilder.cpp +++ b/RetroFE/Source/Graphics/PageBuilder.cpp @@ -320,13 +320,13 @@ bool PageBuilder::buildComponents(xml_node<> *layout, Page *page) { for(xml_node<> *componentXml = layout->first_node("menu"); componentXml; componentXml = componentXml->next_sibling("menu")) { - ScrollingList *scrollingList = buildMenu(componentXml); + ScrollingList *scrollingList = buildMenu(componentXml,*page); page->pushMenu(scrollingList); } for(xml_node<> *componentXml = layout->first_node("container"); componentXml; componentXml = componentXml->next_sibling("container")) { - Container *c = new Container(); + Container *c = new Container(*page); buildViewInfo(componentXml, c->baseViewInfo); loadTweens(c, componentXml); page->addComponent(c); @@ -346,7 +346,7 @@ bool PageBuilder::buildComponents(xml_node<> *layout, Page *page) std::string imagePath; imagePath = Utils::combinePath(Configuration::convertToAbsolutePath(layoutPath, imagePath), std::string(src->value())); - Image *c = new Image(imagePath, scaleX_, scaleY_); + Image *c = new Image(imagePath, *page, scaleX_, scaleY_); buildViewInfo(componentXml, c->baseViewInfo); loadTweens(c, componentXml); page->addComponent(c); @@ -365,7 +365,7 @@ bool PageBuilder::buildComponents(xml_node<> *layout, Page *page) else { Font *font = addFont(componentXml, NULL); - Text *c = new Text(value->value(), font, scaleX_, scaleY_); + Text *c = new Text(value->value(), *page, font, scaleX_, scaleY_); buildViewInfo(componentXml, c->baseViewInfo); @@ -377,7 +377,7 @@ bool PageBuilder::buildComponents(xml_node<> *layout, Page *page) for(xml_node<> *componentXml = layout->first_node("statusText"); componentXml; componentXml = componentXml->next_sibling("statusText")) { Font *font = addFont(componentXml, NULL); - Text *c = new Text("", font, scaleX_, scaleY_); + Text *c = new Text("", *page, font, scaleX_, scaleY_); buildViewInfo(componentXml, c->baseViewInfo); @@ -403,8 +403,9 @@ void PageBuilder::loadReloadableImages(xml_node<> *layout, std::string tagName, std::string reloadableVideoPath; xml_attribute<> *type = componentXml->first_attribute("type"); xml_attribute<> *mode = componentXml->first_attribute("mode"); + xml_attribute<> *selectedOffsetXml = componentXml->first_attribute("selectedOffset"); bool systemMode = false; - + int selectedOffset = 0; if(tagName == "reloadableVideo") { type = componentXml->first_attribute("imageType"); @@ -430,6 +431,13 @@ void PageBuilder::loadReloadableImages(xml_node<> *layout, std::string tagName, } } + if(selectedOffsetXml) + { + std::stringstream ss; + ss << selectedOffsetXml->value(); + ss >> selectedOffset; + } + Component *c = NULL; @@ -438,13 +446,13 @@ void PageBuilder::loadReloadableImages(xml_node<> *layout, std::string tagName, if(type) { Font *font = addFont(componentXml, NULL); - c = new ReloadableText(type->value(), font, layoutKey, scaleX_, scaleY_); + c = new ReloadableText(type->value(), *page, config_, font, layoutKey, scaleX_, scaleY_); } } else { Font *font = addFont(componentXml, NULL); - c = new ReloadableMedia(config_, systemMode, type->value(), (tagName == "reloadableVideo"), font, scaleX_, scaleY_); + c = new ReloadableMedia(config_, systemMode, type->value(), *page, selectedOffset, (tagName == "reloadableVideo"), font, scaleX_, scaleY_); xml_attribute<> *textFallback = componentXml->first_attribute("textFallback"); if(textFallback && Utils::toLower(textFallback->value()) == "true") @@ -564,7 +572,7 @@ void PageBuilder::buildTweenSet(AnimationEvents *tweens, xml_node<> *componentXm } -ScrollingList * PageBuilder::buildMenu(xml_node<> *menuXml) +ScrollingList * PageBuilder::buildMenu(xml_node<> *menuXml, Page &page) { ScrollingList *menu = NULL; std::string menuType = "vertical"; @@ -596,7 +604,7 @@ ScrollingList * PageBuilder::buildMenu(xml_node<> *menuXml) // on default, text will be rendered to the menu. Preload it into cache. Font *font = addFont(itemDefaults, NULL); - menu = new ScrollingList(config_, scaleX_, scaleY_, font, layoutKey, imageType); + menu = new ScrollingList(config_, page, scaleX_, scaleY_, font, layoutKey, imageType); if(scrollTimeXml) { diff --git a/RetroFE/Source/Graphics/PageBuilder.h b/RetroFE/Source/Graphics/PageBuilder.h index a53602b..bea62ff 100644 --- a/RetroFE/Source/Graphics/PageBuilder.h +++ b/RetroFE/Source/Graphics/PageBuilder.h @@ -58,7 +58,7 @@ private: void loadTweens(Component *c, rapidxml::xml_node<> *componentXml); AnimationEvents *createTweenInstance(rapidxml::xml_node<> *componentXml); void buildTweenSet(AnimationEvents *tweens, rapidxml::xml_node<> *componentXml, std::string tagName, std::string tweenName); - ScrollingList * buildMenu(rapidxml::xml_node<> *menuXml); + ScrollingList * buildMenu(rapidxml::xml_node<> *menuXml, Page &p); void buildCustomMenu(ScrollingList *menu, rapidxml::xml_node<> *menuXml, rapidxml::xml_node<> *itemDefaults); void buildVerticalMenu(ScrollingList *menu, rapidxml::xml_node<> *menuXml, rapidxml::xml_node<> *itemDefaults); int parseMenuPosition(std::string strIndex); diff --git a/RetroFE/Source/Main.cpp b/RetroFE/Source/Main.cpp index d14bb6a..0915f38 100644 --- a/RetroFE/Source/Main.cpp +++ b/RetroFE/Source/Main.cpp @@ -24,12 +24,16 @@ #include #include #include +#include static bool ImportConfiguration(Configuration *c); static bool StartLogging(); int main(int argc, char **argv) { + // Initialize random seed + srand(static_cast(time(0))); + Configuration::initialize(); Configuration config; diff --git a/RetroFE/Source/RetroFE.cpp b/RetroFE/Source/RetroFE.cpp index e7a3111..ca26ed6 100644 --- a/RetroFE/Source/RetroFE.cpp +++ b/RetroFE/Source/RetroFE.cpp @@ -178,6 +178,7 @@ bool RetroFE::deInitialize() if(currentPage_) { + currentPage_->DeInitialize(); delete currentPage_; currentPage_ = NULL; } @@ -288,6 +289,7 @@ void RetroFE::run() } // delete the splash screen and use the standard menu + currentPage_->DeInitialize(); delete currentPage_; currentPage_ = loadPage(); @@ -389,7 +391,6 @@ void RetroFE::run() render(); } } - } @@ -447,7 +448,11 @@ RetroFE::RETROFE_STATE RetroFE::processUserInput(Page *page) if (!input_.keystate(UserInput::KeyCodePageUp) && !input_.keystate(UserInput::KeyCodePageDown) && !input_.keystate(UserInput::KeyCodeLetterUp) && - !input_.keystate(UserInput::KeyCodeLetterDown)) + !input_.keystate(UserInput::KeyCodeLetterDown) && + !input_.keystate(UserInput::KeyCodeNextPlaylist) && + !input_.keystate(UserInput::KeyCodeAddPlaylist) && + !input_.keystate(UserInput::KeyCodeRemovePlaylist) && + !input_.keystate(UserInput::KeyCodeRandom)) { keyLastTime_ = 0; keyDelayTime_= 0.3f; @@ -475,6 +480,22 @@ RetroFE::RETROFE_STATE RetroFE::processUserInput(Page *page) { page->letterScroll(Page::ScrollDirectionForward); } + if(input_.newKeyPressed(UserInput::KeyCodeNextPlaylist)) + { + page->nextPlaylist(); + } + if(input_.newKeyPressed(UserInput::KeyCodeRemovePlaylist)) + { + page->removePlaylist(); + } + if(input_.newKeyPressed(UserInput::KeyCodeAddPlaylist)) + { + page->addPlaylist(); + } + if(input_.keystate(UserInput::KeyCodeRandom)) + { + page->selectRandom(); + } } if (input_.keystate(UserInput::KeyCodeAdminMode)) @@ -508,6 +529,12 @@ RetroFE::RETROFE_STATE RetroFE::processUserInput(Page *page) page->setScrollOffsetIndex(lastMenuOffsets_[nextPageItem_->name]); } + bool autoFavorites = true; + config_.getProperty("autoFavorites", autoFavorites); + + if (autoFavorites) + page->favPlaylist(); // Switch to favorites if it exists + state = RETROFE_NEXT_PAGE_REQUEST; } } diff --git a/RetroFE/Source/Utility/Utils.cpp b/RetroFE/Source/Utility/Utils.cpp index e41cb42..ab3b6c6 100644 --- a/RetroFE/Source/Utility/Utils.cpp +++ b/RetroFE/Source/Utility/Utils.cpp @@ -194,11 +194,8 @@ void Utils::replaceSlashesWithUnderscores(std::string &content) std::string Utils::getDirectory(std::string filePath) { -#ifdef WIN32 - filePath = Utils::replace(filePath, "/", "\\"); -#endif - std::string directory = filePath; + const size_t last_slash_idx = filePath.rfind(pathSeparator); if (std::string::npos != last_slash_idx) { @@ -210,9 +207,6 @@ std::string Utils::getDirectory(std::string filePath) std::string Utils::getParentDirectory(std::string directory) { -#ifdef WIN32 - directory = Utils::replace(directory, "/", "\\"); -#endif size_t last_slash_idx = directory.find_last_of(pathSeparator); if(directory.length() - 1 == last_slash_idx) { @@ -231,9 +225,6 @@ std::string Utils::getParentDirectory(std::string directory) std::string Utils::getFileName(std::string filePath) { -#ifdef WIN32 - filePath = Utils::replace(filePath, "/", "\\"); -#endif std::string filename = filePath;