Added favorites playlist support for menus and merged/sub collections.

Improved sorting algorithm for playlists. The order now follows the order of the complete lis
t.
Added favorites playlist display to Aeon Nox theme.
Added favorites common artwork.
Added reloadableMedia support for playlist display.
This commit is contained in:
Pieter Hulshoff 2016-09-16 09:45:22 +02:00
parent 596c636eda
commit 304154a4c4
13 changed files with 289 additions and 55 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

View File

@ -1970,4 +1970,46 @@
</reloadableVideo> </reloadableVideo>
<!----------------------------------------------------------------------------------------------------------------------------------->
<!-- Playlist -->
<!----------------------------------------------------------------------------------------------------------------------------------->
<reloadableImage type="playlist" mode="common" alpha="0" x="1100" y="430" xOrigin="center" yOrigin="top" maxHeight="40" layer="19">
<onEnter>
<set duration=".8">
<animate type="nop"/>
</set>
</onEnter>
<onExit>
<set duration=".25">
<animate type="alpha" to="0" algorithm="easeinquadratic"/>
</set>
</onExit>
<onMenuEnter menuIndex="0">
<set duration=".8">
<animate type="y" to="430" algorithm="easeinquadratic"/>
</set>
</onMenuEnter>
<onMenuEnter menuIndex="1">
<set duration=".25">
<animate type="y" from="-50" to="2" algorithm="easeinquadratic"/>
</set>
</onMenuEnter>
<onMenuExit>
<set duration=".25">
<animate type="alpha" to="0" algorithm="easeinquadratic"/>
</set>
</onMenuExit>
<onIdle>
<set duration="1">
<animate type="alpha" to="0.5" algorithm="easeinquadratic"/>
</set>
<set duration="1">
<animate type="alpha" to="1" algorithm="easeinquadratic"/>
</set>
</onIdle>
</reloadableImage>
</layout> </layout>

View File

@ -1970,4 +1970,46 @@
</reloadableVideo> </reloadableVideo>
<!----------------------------------------------------------------------------------------------------------------------------------->
<!-- Playlist -->
<!----------------------------------------------------------------------------------------------------------------------------------->
<reloadableImage type="playlist" mode="common" alpha="0" x="940" y="505" xOrigin="center" yOrigin="top" maxHeight="40" layer="19">
<onEnter>
<set duration=".8">
<animate type="nop"/>
</set>
</onEnter>
<onExit>
<set duration=".25">
<animate type="alpha" to="0" algorithm="easeinquadratic"/>
</set>
</onExit>
<onMenuEnter menuIndex="0">
<set duration=".8">
<animate type="y" to="505" algorithm="easeinquadratic"/>
</set>
</onMenuEnter>
<onMenuEnter menuIndex="1">
<set duration=".25">
<animate type="y" from="-50" to="2" algorithm="easeinquadratic"/>
</set>
</onMenuEnter>
<onMenuExit>
<set duration=".25">
<animate type="alpha" to="0" algorithm="easeinquadratic"/>
</set>
</onMenuExit>
<onIdle>
<set duration="1">
<animate type="alpha" to="0.5" algorithm="easeinquadratic"/>
</set>
<set duration="1">
<animate type="alpha" to="1" algorithm="easeinquadratic"/>
</set>
</onIdle>
</reloadableImage>
</layout> </layout>

View File

@ -1970,4 +1970,46 @@
</reloadableVideo> </reloadableVideo>
<!----------------------------------------------------------------------------------------------------------------------------------->
<!-- Playlist -->
<!----------------------------------------------------------------------------------------------------------------------------------->
<reloadableImage type="playlist" mode="common" alpha="0" x="1100" y="430" xOrigin="center" yOrigin="top" maxHeight="40" layer="19">
<onEnter>
<set duration=".8">
<animate type="nop"/>
</set>
</onEnter>
<onExit>
<set duration=".25">
<animate type="alpha" to="0" algorithm="easeinquadratic"/>
</set>
</onExit>
<onMenuEnter menuIndex="0">
<set duration=".8">
<animate type="y" to="430" algorithm="easeinquadratic"/>
</set>
</onMenuEnter>
<onMenuEnter menuIndex="1">
<set duration=".25">
<animate type="y" from="-50" to="2" algorithm="easeinquadratic"/>
</set>
</onMenuEnter>
<onMenuExit>
<set duration=".25">
<animate type="alpha" to="0" algorithm="easeinquadratic"/>
</set>
</onMenuExit>
<onIdle>
<set duration="1">
<animate type="alpha" to="0.5" algorithm="easeinquadratic"/>
</set>
<set duration="1">
<animate type="alpha" to="1" algorithm="easeinquadratic"/>
</set>
</onIdle>
</reloadableImage>
</layout> </layout>

View File

@ -51,16 +51,6 @@ CollectionInfo::CollectionInfo(std::string name,
CollectionInfo::~CollectionInfo() CollectionInfo::~CollectionInfo()
{ {
// remove items from the subcollections so their destructors do not
// delete the items since the parent collection will delete them.
std::vector<CollectionInfo *>::iterator subit;
for (subit = subcollections_.begin(); subit != subcollections_.end(); subit++)
{
CollectionInfo *info = *subit;
info->items.clear();
}
Playlists_T::iterator pit = playlists.begin(); Playlists_T::iterator pit = playlists.begin();
while(pit != playlists.end()) while(pit != playlists.end())
@ -128,9 +118,16 @@ bool CollectionInfo::Save()
filestream.open(file.c_str()); filestream.open(file.c_str());
std::vector<Item *> *saveitems = playlists["favorites"]; std::vector<Item *> *saveitems = playlists["favorites"];
for(std::vector<Item *>::iterator it = saveitems->begin(); it != saveitems->end(); it++) for(std::vector<Item *>::iterator it = saveitems->begin(); it != saveitems->end(); it++)
{
if ((*it)->collectionInfo->name == name)
{ {
filestream << (*it)->name << std::endl; filestream << (*it)->name << std::endl;
} }
else
{
filestream << "_" << (*it)->collectionInfo->name << ":" << (*it)->name << std::endl;
}
}
filestream.close(); filestream.close();
} }
@ -163,16 +160,9 @@ void CollectionInfo::extensionList(std::vector<std::string> &extensionlist)
void CollectionInfo::addSubcollection(CollectionInfo *newinfo) void CollectionInfo::addSubcollection(CollectionInfo *newinfo)
{ {
subcollections_.push_back(newinfo);
items.insert(items.begin(), newinfo->items.begin(), newinfo->items.end()); items.insert(items.begin(), newinfo->items.begin(), newinfo->items.end());
} }
bool CollectionInfo::hasSubcollections()
{
return (subcollections_.size() > 0);
}
bool CollectionInfo::itemIsLess(Item *lhs, Item *rhs) bool CollectionInfo::itemIsLess(Item *lhs, Item *rhs)
{ {
if(lhs->leaf && !rhs->leaf) return true; if(lhs->leaf && !rhs->leaf) return true;
@ -189,3 +179,28 @@ void CollectionInfo::sortItems()
std::sort(it->second->begin(), it->second->end(), itemIsLess); std::sort(it->second->begin(), it->second->end(), itemIsLess);
} }
} }
void CollectionInfo::sortFavoriteItems()
{
std::vector<Item *> *allItems = playlists["all"];
std::vector<Item *> favItems;
for(std::vector <Item *>::iterator itFav = playlists["favorites"]->begin(); itFav != playlists["favorites"]->end(); itFav++)
{
favItems.push_back((*itFav));
}
playlists["favorites"]->clear();
for(std::vector <Item *>::iterator itAll = allItems->begin(); itAll != allItems->end(); itAll++)
{
for(std::vector <Item *>::iterator itFav = favItems.begin(); itFav != favItems.end(); itFav++)
{
if ((*itAll) == (*itFav))
{
playlists["favorites"]->push_back((*itAll));
}
}
}
}

View File

@ -29,8 +29,8 @@ public:
std::string settingsPath() const; std::string settingsPath() const;
bool Save(); bool Save();
void sortItems(); void sortItems();
void sortFavoriteItems();
void addSubcollection(CollectionInfo *info); void addSubcollection(CollectionInfo *info);
bool hasSubcollections();
void extensionList(std::vector<std::string> &extensions); void extensionList(std::vector<std::string> &extensions);
std::string name; std::string name;
std::string listpath; std::string listpath;
@ -44,7 +44,6 @@ public:
bool menusort; bool menusort;
private: private:
std::vector<CollectionInfo *> subcollections_;
std::string metadataPath_; std::string metadataPath_;
std::string extensions_; std::string extensions_;
static bool itemIsLess(Item *lhs, Item *rhs); static bool itemIsLess(Item *lhs, Item *rhs);

View File

@ -232,12 +232,9 @@ bool CollectionInfoBuilder::ImportDirectory(CollectionInfo *info, std::string me
DIR *dp; DIR *dp;
struct dirent *dirp; struct dirent *dirp;
std::string path = info->listpath; std::string path = info->listpath;
std::map<std::string, Item *> allMap;
std::map<std::string, Item *> includeFilter; std::map<std::string, Item *> includeFilter;
std::map<std::string, Item *> favoritesFilter;
std::map<std::string, Item *> excludeFilter; std::map<std::string, Item *> excludeFilter;
std::string includeFile = Utils::combinePath(Configuration::absolutePath, "collections", info->name, "include.txt"); 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 excludeFile = Utils::combinePath(Configuration::absolutePath, "collections", info->name, "exclude.txt");
std::string launcher; std::string launcher;
@ -344,28 +341,52 @@ bool CollectionInfoBuilder::ImportDirectory(CollectionInfo *info, std::string me
excludeFilter.erase(it); excludeFilter.erase(it);
} }
for(std::vector<Item *>::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["all"] = &info->items;
return true;
}
void CollectionInfoBuilder::addFavorites(CollectionInfo *info)
{
std::map<std::string, Item *> favoritesFilter;
std::string favoritesFile = Utils::combinePath(Configuration::absolutePath, "collections", info->name, "playlists/favorites.txt");
ImportBasicList(info, favoritesFile, favoritesFilter);
info->playlists["favorites"] = new std::vector<Item *>(); info->playlists["favorites"] = new std::vector<Item *>();
// add the favorites list // add the favorites list
for(std::map<std::string, Item *>::iterator it = favoritesFilter.begin(); it != favoritesFilter.end(); it++) for(std::map<std::string, Item *>::iterator it = favoritesFilter.begin(); it != favoritesFilter.end(); it++)
{ {
std::map<std::string, Item *>::iterator itemit = allMap.find(it->first); std::string collectionName = info->name;
std::string itemName = it->first;
if(itemit != allMap.end()) if (itemName.at(0) == '_') // name consists of _<collectionName>:<itemName>
{ {
info->playlists["favorites"]->push_back(itemit->second); itemName.erase(0, 1); // Remove _
size_t position = itemName.find(":");
if (position != std::string::npos )
{
collectionName = itemName.substr(0, position);
itemName = itemName.erase(0, position+1);
} }
} }
for(std::vector<Item *>::iterator it = info->items.begin(); it != info->items.end(); it++)
{
if( (*it)->name == itemName && (*it)->collectionInfo->name == collectionName)
{
info->playlists["favorites"]->push_back((*it));
}
}
}
metaDB_.injectMetadata(info); return;
}
return true;
void CollectionInfoBuilder::injectMetadata(CollectionInfo *info)
{
metaDB_.injectMetadata(info);
return;
} }

View File

@ -31,6 +31,8 @@ public:
virtual ~CollectionInfoBuilder(); virtual ~CollectionInfoBuilder();
CollectionInfo *buildCollection(std::string collectionName); CollectionInfo *buildCollection(std::string collectionName);
CollectionInfo *buildCollection(std::string collectionName, std::string mergedCollectionName); CollectionInfo *buildCollection(std::string collectionName, std::string mergedCollectionName);
void addFavorites(CollectionInfo *info);
void injectMetadata(CollectionInfo *info);
static bool createCollectionDirectory(std::string collectionName); static bool createCollectionDirectory(std::string collectionName);
private: private:

View File

@ -276,6 +276,10 @@ void ReloadableMedia::reloadTexture()
{ {
basename = selectedItem->score; basename = selectedItem->score;
} }
else if(typeLC == "playlist")
{
basename = page.getPlaylistName();
}
Utils::replaceSlashesWithUnderscores(basename); Utils::replaceSlashesWithUnderscores(basename);

View File

@ -700,7 +700,6 @@ void Page::draw()
void Page::removePlaylist() void Page::removePlaylist()
{ {
if(!selectedItem_) return; if(!selectedItem_) return;
if(!selectedItem_->leaf) return;
MenuInfo_S &info = collections_.back(); MenuInfo_S &info = collections_.back();
CollectionInfo *collection = info.collection; CollectionInfo *collection = info.collection;
@ -711,14 +710,15 @@ void Page::removePlaylist()
if(it != items->end()) if(it != items->end())
{ {
items->erase(it); items->erase(it);
collection->sortFavoriteItems();
collection->saveRequest = true; collection->saveRequest = true;
} }
collection->Save();
} }
void Page::addPlaylist() void Page::addPlaylist()
{ {
if(!selectedItem_) return; if(!selectedItem_) return;
if(!selectedItem_->leaf) return;
MenuInfo_S &info = collections_.back(); MenuInfo_S &info = collections_.back();
CollectionInfo *collection = info.collection; CollectionInfo *collection = info.collection;
@ -727,9 +727,10 @@ void Page::addPlaylist()
if(playlist_->first != "favorites" && std::find(items->begin(), items->end(), selectedItem_) == items->end()) if(playlist_->first != "favorites" && std::find(items->begin(), items->end(), selectedItem_) == items->end())
{ {
items->push_back(selectedItem_); items->push_back(selectedItem_);
collection->sortItems(); collection->sortFavoriteItems();
collection->saveRequest = true; collection->saveRequest = true;
} }
collection->Save();
} }
std::string Page::getCollectionName() std::string Page::getCollectionName()

View File

@ -321,17 +321,21 @@ void RetroFE::run()
if(currentPage_) if(currentPage_)
{ {
std::string firstCollection = "Main"; std::string firstCollection = "Main";
bool menuSort = true;
config_.getProperty("firstCollection", firstCollection); config_.getProperty("firstCollection", firstCollection);
config_.getProperty("collections." + firstCollection + ".list.menuSort", menuSort);
config_.setProperty("currentCollection", firstCollection); config_.setProperty("currentCollection", firstCollection);
CollectionInfo *info = getCollection(firstCollection); CollectionInfo *info = getCollection(firstCollection);
MenuParser mp;
mp.buildMenuItems(info, menuSort);
currentPage_->pushCollection(info); currentPage_->pushCollection(info);
bool autoFavorites = true;
config_.getProperty("autoFavorites", autoFavorites);
if (autoFavorites)
{
currentPage_->selectPlaylist("favorites"); // Switch to favorites playlist
}
currentPage_->onNewItemSelected(); currentPage_->onNewItemSelected();
currentPage_->reallocateMenuSpritePoints(); currentPage_->reallocateMenuSpritePoints();
@ -344,6 +348,36 @@ void RetroFE::run()
} }
break; break;
case RETROFE_PLAYLIST_REQUEST:
currentPage_->highlightExit();
currentPage_->setScrolling(Page::ScrollDirectionIdle);
state = RETROFE_PLAYLIST_EXIT;
break;
case RETROFE_PLAYLIST_EXIT:
if (currentPage_->isIdle())
{
currentPage_->onNewItemSelected();
state = RETROFE_PLAYLIST_LOAD_ART;
}
break;
case RETROFE_PLAYLIST_LOAD_ART:
if (currentPage_->isIdle())
{
currentPage_->reallocateMenuSpritePoints();
currentPage_->highlightEnter();
state = RETROFE_PLAYLIST_ENTER;
}
break;
case RETROFE_PLAYLIST_ENTER:
if (currentPage_->isIdle())
{
state = RETROFE_IDLE;
}
break;
case RETROFE_HIGHLIGHT_REQUEST: case RETROFE_HIGHLIGHT_REQUEST:
currentPage_->highlightExit(); currentPage_->highlightExit();
currentPage_->setScrolling(Page::ScrollDirectionIdle); currentPage_->setScrolling(Page::ScrollDirectionIdle);
@ -395,6 +429,8 @@ void RetroFE::run()
case RETROFE_NEXT_PAGE_MENU_EXIT: case RETROFE_NEXT_PAGE_MENU_EXIT:
if(currentPage_->isIdle()) if(currentPage_->isIdle())
{ {
lastMenuOffsets_[currentPage_->getCollectionName()] = currentPage_->getScrollOffsetIndex();
lastMenuPlaylists_[currentPage_->getCollectionName()] = currentPage_->getPlaylistName();
// Load new layout if available // Load new layout if available
std::string layoutName; std::string layoutName;
config_.getProperty("layout", layoutName); config_.getProperty("layout", layoutName);
@ -408,14 +444,10 @@ void RetroFE::run()
currentPage_ = page; currentPage_ = page;
} }
bool menuSort = true;
config_.setProperty("currentCollection", nextPageName); config_.setProperty("currentCollection", nextPageName);
config_.getProperty("collections." + nextPageName + ".list.menuSort", menuSort);
CollectionInfo *info = getCollection(nextPageName); CollectionInfo *info = getCollection(nextPageName);
MenuParser mp;
mp.buildMenuItems(info, menuSort);
currentPage_->pushCollection(info); currentPage_->pushCollection(info);
bool rememberMenu = false; bool rememberMenu = false;
@ -502,6 +534,26 @@ void RetroFE::run()
currentPage_->popCollection(); currentPage_->popCollection();
} }
config_.setProperty("currentCollection", currentPage_->getCollectionName()); config_.setProperty("currentCollection", currentPage_->getCollectionName());
bool rememberMenu = false;
config_.getProperty("rememberMenu", rememberMenu);
bool autoFavorites = true;
config_.getProperty("autoFavorites", autoFavorites);
if (rememberMenu && lastMenuPlaylists_.find(currentPage_->getCollectionName()) != lastMenuPlaylists_.end())
{
currentPage_->selectPlaylist(lastMenuPlaylists_[currentPage_->getCollectionName()]); // Switch to last playlist
}
else if (autoFavorites)
{
currentPage_->selectPlaylist("favorites"); // Switch to favorites playlist
}
if(rememberMenu && lastMenuOffsets_.find(currentPage_->getCollectionName()) != lastMenuOffsets_.end())
{
currentPage_->setScrollOffsetIndex(lastMenuOffsets_[currentPage_->getCollectionName()]);
}
currentPage_->onNewItemSelected(); currentPage_->onNewItemSelected();
currentPage_->reallocateMenuSpritePoints(); currentPage_->reallocateMenuSpritePoints();
state = RETROFE_BACK_MENU_LOAD_ART; state = RETROFE_BACK_MENU_LOAD_ART;
@ -671,19 +723,17 @@ RetroFE::RETROFE_STATE RetroFE::processUserInput(Page *page)
if(input_.newKeyPressed(UserInput::KeyCodeNextPlaylist)) if(input_.newKeyPressed(UserInput::KeyCodeNextPlaylist))
{ {
page->nextPlaylist(); page->nextPlaylist();
page->reallocateMenuSpritePoints(); state = RETROFE_PLAYLIST_REQUEST;
state = RETROFE_HIGHLIGHT_REQUEST;
} }
if(input_.newKeyPressed(UserInput::KeyCodeRemovePlaylist)) if(input_.newKeyPressed(UserInput::KeyCodeRemovePlaylist))
{ {
page->removePlaylist(); page->removePlaylist();
page->onNewItemSelected(); state = RETROFE_PLAYLIST_REQUEST;
page->reallocateMenuSpritePoints();
} }
if(input_.newKeyPressed(UserInput::KeyCodeAddPlaylist)) if(input_.newKeyPressed(UserInput::KeyCodeAddPlaylist))
{ {
page->addPlaylist(); page->addPlaylist();
page->reallocateMenuSpritePoints(); state = RETROFE_PLAYLIST_REQUEST;
} }
if(input_.keystate(UserInput::KeyCodeRandom)) if(input_.keystate(UserInput::KeyCodeRandom))
{ {
@ -779,6 +829,8 @@ CollectionInfo *RetroFE::getCollection(std::string collectionName)
CollectionInfoBuilder cib(config_, *metadb_); CollectionInfoBuilder cib(config_, *metadb_);
CollectionInfo *collection = cib.buildCollection(collectionName); CollectionInfo *collection = cib.buildCollection(collectionName);
cib.injectMetadata(collection);
DIR *dp; DIR *dp;
struct dirent *dirp; struct dirent *dirp;
@ -803,11 +855,21 @@ CollectionInfo *RetroFE::getCollection(std::string collectionName)
CollectionInfo *subcollection = cib.buildCollection(basename, collectionName); CollectionInfo *subcollection = cib.buildCollection(basename, collectionName);
collection->addSubcollection(subcollection); collection->addSubcollection(subcollection);
cib.injectMetadata(subcollection);
} }
} }
} }
collection->sortItems(); collection->sortItems();
bool menuSort = true;
config_.getProperty("collections." + collectionName + ".list.menuSort", menuSort);
MenuParser mp;
mp.buildMenuItems(collection, menuSort);
cib.addFavorites(collection);
collection->sortFavoriteItems();
return collection; return collection;
} }

View File

@ -55,6 +55,10 @@ private:
RETROFE_LOAD_ART, RETROFE_LOAD_ART,
RETROFE_ENTER, RETROFE_ENTER,
RETROFE_SPLASH_EXIT, RETROFE_SPLASH_EXIT,
RETROFE_PLAYLIST_REQUEST,
RETROFE_PLAYLIST_EXIT,
RETROFE_PLAYLIST_LOAD_ART,
RETROFE_PLAYLIST_ENTER,
RETROFE_HIGHLIGHT_REQUEST, RETROFE_HIGHLIGHT_REQUEST,
RETROFE_HIGHLIGHT_EXIT, RETROFE_HIGHLIGHT_EXIT,
RETROFE_HIGHLIGHT_LOAD_ART, RETROFE_HIGHLIGHT_LOAD_ART,

View File

@ -20,7 +20,7 @@
std::string retrofe_version_major = "0"; std::string retrofe_version_major = "0";
std::string retrofe_version_minor = "7"; std::string retrofe_version_minor = "7";
std::string retrofe_version_build = "15"; std::string retrofe_version_build = "16";
std::string Version::getString() std::string Version::getString()