mirror of https://github.com/stella-emu/stella.git
added tracking of user favorites, recently played and most popular games
added virtual directories for selecting tracked games extended launcher context menu and shortcuts
This commit is contained in:
parent
083cf78797
commit
00609a3a7a
12
Changes.txt
12
Changes.txt
|
@ -14,12 +14,16 @@
|
||||||
|
|
||||||
6.6 to 6.?
|
6.6 to 6.?
|
||||||
|
|
||||||
|
* Reworked the file launcher:
|
||||||
|
- Added tracking of user favorites, recently played and most popular
|
||||||
|
games.
|
||||||
|
- Added virtual directories for selecting tracked games.
|
||||||
|
- Added icons for files and directories.
|
||||||
|
- Added option to show/hide file extensions.
|
||||||
|
- Extended context menu and shortcuts.
|
||||||
|
|
||||||
* Added option to toggle autofire mode.
|
* Added option to toggle autofire mode.
|
||||||
|
|
||||||
* Added icons to file lists.
|
|
||||||
|
|
||||||
* Added option to show/hide file extensions.
|
|
||||||
|
|
||||||
-Have fun!
|
-Have fun!
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -2273,6 +2273,11 @@
|
||||||
<td>Control + H</td>
|
<td>Control + H</td>
|
||||||
<td>Control + H</td>
|
<td>Control + H</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Toggle favorite</td>
|
||||||
|
<td>Control + F</td>
|
||||||
|
<td>Control + F</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>Reload ROM listing</td>
|
<td>Reload ROM listing</td>
|
||||||
<td>Control + R</td>
|
<td>Control + R</td>
|
||||||
|
|
|
@ -157,6 +157,11 @@ Settings::Settings()
|
||||||
setPermanent("launcherextensions", "false");
|
setPermanent("launcherextensions", "false");
|
||||||
setPermanent("romviewer", "1");
|
setPermanent("romviewer", "1");
|
||||||
setPermanent("lastrom", "");
|
setPermanent("lastrom", "");
|
||||||
|
setPermanent("favoriteroms", "");
|
||||||
|
setPermanent("recentroms", "");
|
||||||
|
setPermanent("maxrecentroms", "20");
|
||||||
|
setPermanent("popularroms", "");
|
||||||
|
setPermanent("altsorting", "false");
|
||||||
|
|
||||||
// UI-related options
|
// UI-related options
|
||||||
#ifdef DEBUGGER_SUPPORT
|
#ifdef DEBUGGER_SUPPORT
|
||||||
|
@ -594,6 +599,8 @@ void Settings::usage() const
|
||||||
<< " -launcherroms <1|0> Show only ROMs in the launcher (vs. all files)\n"
|
<< " -launcherroms <1|0> Show only ROMs in the launcher (vs. all files)\n"
|
||||||
<< " -launchersubdirs <0|1> Show files from subdirectories too\n"
|
<< " -launchersubdirs <0|1> Show files from subdirectories too\n"
|
||||||
<< " -launcherextensions <0|1> Display file extensions in launcher\n"
|
<< " -launcherextensions <0|1> Display file extensions in launcher\n"
|
||||||
|
<< " -altsorting <0|1> Alternative sorting in virtual folders\n"
|
||||||
|
<< " -maxrecentroms <number> Number of ROMs tracked in 'Recently played'\n"
|
||||||
<< " -romdir <dir> Set the path where the ROM launcher will start\n"
|
<< " -romdir <dir> Set the path where the ROM launcher will start\n"
|
||||||
<< " -followlauncher <0|1> Default ROM path follows launcher navigation\n"
|
<< " -followlauncher <0|1> Default ROM path follows launcher navigation\n"
|
||||||
<< " -userdir <dir> Set the path to save user files to\n"
|
<< " -userdir <dir> Set the path to save user files to\n"
|
||||||
|
|
|
@ -0,0 +1,264 @@
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SSSS tt lll lll
|
||||||
|
// SS SS tt ll ll
|
||||||
|
// SS tttttt eeee ll ll aaaa
|
||||||
|
// SSSS tt ee ee ll ll aa
|
||||||
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||||
|
// SS SS tt ee ll ll aa aa
|
||||||
|
// SSSS ttt eeeee llll llll aaaaa
|
||||||
|
//
|
||||||
|
// Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony
|
||||||
|
// and the Stella Team
|
||||||
|
//
|
||||||
|
// See the file "License.txt" for information on usage and redistribution of
|
||||||
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#include "FSNode.hxx"
|
||||||
|
#include "json_lib.hxx"
|
||||||
|
#include "Settings.hxx"
|
||||||
|
|
||||||
|
#include "FavoritesManager.hxx"
|
||||||
|
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
FavoritesManager::FavoritesManager(Settings& settings)
|
||||||
|
: mySettings(settings)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void FavoritesManager::load()
|
||||||
|
{
|
||||||
|
myMaxRecent = mySettings.getInt("maxrecentroms");
|
||||||
|
|
||||||
|
// User Favorites
|
||||||
|
myUserSet.clear();
|
||||||
|
const string& serializedUser = mySettings.getString("favoriteroms");
|
||||||
|
if(!serializedUser.empty())
|
||||||
|
{
|
||||||
|
const json& jUser = json::parse(serializedUser);
|
||||||
|
for(const auto& u : jUser)
|
||||||
|
{
|
||||||
|
const string& path = u.get<string>();
|
||||||
|
addUser(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recently Played
|
||||||
|
myRecentList.clear();
|
||||||
|
const string& serializedRecent = mySettings.getString("recentroms");
|
||||||
|
if(!serializedRecent.empty())
|
||||||
|
{
|
||||||
|
const json& jRecent = json::parse(serializedRecent);
|
||||||
|
for(const auto& r : jRecent)
|
||||||
|
{
|
||||||
|
const string& path = r.get<string>();
|
||||||
|
addRecent(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Most Popular
|
||||||
|
myPopularMap.clear();
|
||||||
|
const string& serializedPopular = mySettings.getString("popularroms");
|
||||||
|
if(!serializedPopular.empty())
|
||||||
|
{
|
||||||
|
const json& jPopular = json::parse(serializedPopular);
|
||||||
|
for(const auto& p : jPopular)
|
||||||
|
{
|
||||||
|
const string& path = p[0].get<string>();
|
||||||
|
const uInt32 count = p[1].get<uInt32>();
|
||||||
|
myPopularMap.emplace(path, count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void FavoritesManager::save()
|
||||||
|
{
|
||||||
|
// User Favorites
|
||||||
|
json jUser = json::array();
|
||||||
|
for(const auto& path : myUserSet)
|
||||||
|
jUser.push_back(path);
|
||||||
|
mySettings.setValue("favoriteroms", jUser.dump(2));
|
||||||
|
|
||||||
|
// Recently Played
|
||||||
|
json jRecent = json::array();
|
||||||
|
for(const auto& path : myRecentList)
|
||||||
|
jRecent.push_back(path);
|
||||||
|
mySettings.setValue("recentroms", jRecent.dump(2));
|
||||||
|
|
||||||
|
// Most Popular
|
||||||
|
json jPopular = json::array();
|
||||||
|
for(const auto& path : myPopularMap)
|
||||||
|
jPopular.push_back(path);
|
||||||
|
mySettings.setValue("popularroms", jPopular.dump(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void FavoritesManager::addUser(const string& path)
|
||||||
|
{
|
||||||
|
myUserSet.emplace(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void FavoritesManager::removeUser(const string& path)
|
||||||
|
{
|
||||||
|
myUserSet.erase(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool FavoritesManager::toggleUser(const string& path)
|
||||||
|
{
|
||||||
|
bool favorize = !existsUser(path);
|
||||||
|
|
||||||
|
if(favorize)
|
||||||
|
addUser(path);
|
||||||
|
else
|
||||||
|
removeUser(path);
|
||||||
|
|
||||||
|
return favorize;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool FavoritesManager::existsUser(const string& path) const
|
||||||
|
{
|
||||||
|
return myUserSet.find(path) != myUserSet.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
const FavoritesManager::UserList& FavoritesManager::userList() const
|
||||||
|
{
|
||||||
|
// Return newest to oldest
|
||||||
|
static UserList sortedList;
|
||||||
|
|
||||||
|
sortedList.clear();
|
||||||
|
sortedList.assign(myUserSet.begin(), myUserSet.end());
|
||||||
|
|
||||||
|
if(!mySettings.getBool("altsorting"))
|
||||||
|
std::sort(sortedList.begin(), sortedList.end(),
|
||||||
|
[](const string& a, const string& b)
|
||||||
|
{
|
||||||
|
// Sort without path
|
||||||
|
FilesystemNode aNode(a);
|
||||||
|
FilesystemNode bNode(b);
|
||||||
|
return BSPF::compareIgnoreCase(aNode.getName(), bNode.getName()) < 0;
|
||||||
|
});
|
||||||
|
return sortedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void FavoritesManager::update(const string& path)
|
||||||
|
{
|
||||||
|
addRecent(path);
|
||||||
|
incPopular(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void FavoritesManager::addRecent(const string& path)
|
||||||
|
{
|
||||||
|
auto it = std::find(myRecentList.begin(), myRecentList.end(), path);
|
||||||
|
|
||||||
|
// Always remove existing before adding at the end again
|
||||||
|
if(it != myRecentList.end())
|
||||||
|
myRecentList.erase(it);
|
||||||
|
myRecentList.emplace_back(path);
|
||||||
|
// Limit size
|
||||||
|
while(myRecentList.size() > myMaxRecent)
|
||||||
|
myRecentList.erase(myRecentList.begin());
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
const FavoritesManager::RecentList& FavoritesManager::recentList() const
|
||||||
|
{
|
||||||
|
static RecentList sortedList;
|
||||||
|
bool sortByName = mySettings.getBool("altsorting");
|
||||||
|
|
||||||
|
sortedList.clear();
|
||||||
|
if(sortByName)
|
||||||
|
{
|
||||||
|
sortedList.assign(myRecentList.begin(), myRecentList.end());
|
||||||
|
|
||||||
|
std::sort(sortedList.begin(), sortedList.end(),
|
||||||
|
[](const string& a, const string& b)
|
||||||
|
{
|
||||||
|
// Sort alphabetical, without path
|
||||||
|
FilesystemNode aNode(a);
|
||||||
|
FilesystemNode bNode(b);
|
||||||
|
return BSPF::compareIgnoreCase(aNode.getName(), bNode.getName()) < 0;
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// sort newest to oldest
|
||||||
|
sortedList.assign(myRecentList.rbegin(), myRecentList.rend());
|
||||||
|
|
||||||
|
return sortedList;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void FavoritesManager::incPopular(const string& path)
|
||||||
|
{
|
||||||
|
static constexpr uInt32 scale = 100;
|
||||||
|
static constexpr double factor = 0.7;
|
||||||
|
static constexpr uInt32 max_popular = scale;
|
||||||
|
static constexpr uInt32 min_popular = max_popular * factor;
|
||||||
|
|
||||||
|
auto increased = myPopularMap.find(path);
|
||||||
|
if(increased != myPopularMap.end())
|
||||||
|
increased->second += scale;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Limit number of entries and age data
|
||||||
|
if(myPopularMap.size() >= max_popular)
|
||||||
|
{
|
||||||
|
PopularList sortedList = sortedPopularList(); // sorted by frequency!
|
||||||
|
for(auto item = sortedList.cbegin(); item != sortedList.cend(); ++item)
|
||||||
|
{
|
||||||
|
auto entry = myPopularMap.find(item->first);
|
||||||
|
if(entry != myPopularMap.end())
|
||||||
|
{
|
||||||
|
//if(item - sortedList.cbegin() <= min_popular)
|
||||||
|
if(entry->second >= scale * (1.0 - factor))
|
||||||
|
entry->second *= factor; // age data
|
||||||
|
else
|
||||||
|
myPopularMap.erase(entry); // remove least popular
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
myPopularMap.emplace(path, scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
const FavoritesManager::PopularList& FavoritesManager::popularList() const
|
||||||
|
{
|
||||||
|
return sortedPopularList(mySettings.getBool("altsorting"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
const FavoritesManager::PopularList& FavoritesManager::sortedPopularList(bool sortByName) const
|
||||||
|
{
|
||||||
|
// Return most to least popular or sorted by name
|
||||||
|
static PopularList sortedList;
|
||||||
|
|
||||||
|
sortedList.clear();
|
||||||
|
sortedList.assign(myPopularMap.begin(), myPopularMap.end());
|
||||||
|
|
||||||
|
std::sort(sortedList.begin(), sortedList.end(),
|
||||||
|
[sortByName](const PopularType& a, const PopularType& b)
|
||||||
|
{
|
||||||
|
// 1. sort by most popular
|
||||||
|
if(!sortByName && a.second != b.second)
|
||||||
|
return a.second > b.second;
|
||||||
|
|
||||||
|
// 2. Sort alphabetical, without path
|
||||||
|
FilesystemNode aNode(a.first);
|
||||||
|
FilesystemNode bNode(b.first);
|
||||||
|
return BSPF::compareIgnoreCase(aNode.getName(), bNode.getName()) < 0;
|
||||||
|
});
|
||||||
|
return sortedList;
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SSSS tt lll lll
|
||||||
|
// SS SS tt ll ll
|
||||||
|
// SS tttttt eeee ll ll aaaa
|
||||||
|
// SSSS tt ee ee ll ll aa
|
||||||
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||||
|
// SS SS tt ee ll ll aa aa
|
||||||
|
// SSSS ttt eeeee llll llll aaaaa
|
||||||
|
//
|
||||||
|
// Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony
|
||||||
|
// and the Stella Team
|
||||||
|
//
|
||||||
|
// See the file "License.txt" for information on usage and redistribution of
|
||||||
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef FAVORITES_MANAGER_HXX
|
||||||
|
#define FAVORITES_MANAGER_HXX
|
||||||
|
|
||||||
|
class Settings;
|
||||||
|
|
||||||
|
#include "bspf.hxx"
|
||||||
|
#include <map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
/**
|
||||||
|
Manages user defined favorites, recently played ROMs and most popular ROMs.
|
||||||
|
|
||||||
|
@author Thomas Jentzsch
|
||||||
|
*/
|
||||||
|
|
||||||
|
class FavoritesManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using UserList = std::vector<string>;
|
||||||
|
using RecentList = std::vector<string>;
|
||||||
|
using PopularType = std::pair<string, uInt32>;
|
||||||
|
using PopularList = std::vector<PopularType>;
|
||||||
|
|
||||||
|
FavoritesManager::FavoritesManager(Settings& settings);
|
||||||
|
|
||||||
|
void load();
|
||||||
|
void save();
|
||||||
|
|
||||||
|
// User favorites
|
||||||
|
void addUser(const string& path);
|
||||||
|
void removeUser(const string& path);
|
||||||
|
bool toggleUser(const string& path);
|
||||||
|
bool existsUser(const string& path) const;
|
||||||
|
const UserList& userList() const;
|
||||||
|
|
||||||
|
void update(const string& path);
|
||||||
|
|
||||||
|
// Recently played
|
||||||
|
const RecentList& recentList() const;
|
||||||
|
|
||||||
|
// Most popular
|
||||||
|
const PopularList& popularList() const;
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
using PopularMap = std::map<string, uInt32>;
|
||||||
|
using UserSet = std::unordered_set<string>;
|
||||||
|
|
||||||
|
UserSet myUserSet;
|
||||||
|
RecentList myRecentList;
|
||||||
|
PopularMap myPopularMap;
|
||||||
|
uInt32 myMaxRecent{10};
|
||||||
|
|
||||||
|
Settings& mySettings;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addRecent(const string& path);
|
||||||
|
void incPopular(const string& path);
|
||||||
|
const PopularList& sortedPopularList(bool sortByName = false) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Following constructors and assignment operators not supported
|
||||||
|
FavoritesManager() = delete;
|
||||||
|
FavoritesManager(const FavoritesManager&) = delete;
|
||||||
|
FavoritesManager(FavoritesManager&&) = delete;
|
||||||
|
FavoritesManager& operator=(const FavoritesManager&) = delete;
|
||||||
|
FavoritesManager& operator=(FavoritesManager&&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -43,7 +43,7 @@ void FileListWidget::setDirectory(const FilesystemNode& node,
|
||||||
_node = node;
|
_node = node;
|
||||||
|
|
||||||
// We always want a directory listing
|
// We always want a directory listing
|
||||||
if(!_node.isDirectory() && _node.hasParent())
|
if(!isDirectory(_node) && _node.hasParent())
|
||||||
{
|
{
|
||||||
_selectedFile = _node.getName();
|
_selectedFile = _node.getName();
|
||||||
_node = _node.getParent();
|
_node = _node.getParent();
|
||||||
|
@ -83,6 +83,67 @@ void FileListWidget::setLocation(const FilesystemNode& node,
|
||||||
// Read in the data from the file system (start with an empty list)
|
// Read in the data from the file system (start with an empty list)
|
||||||
_fileList.clear();
|
_fileList.clear();
|
||||||
|
|
||||||
|
getChildren(isCancelled);
|
||||||
|
|
||||||
|
// Now fill the list widget with the names from the file list,
|
||||||
|
// even if cancelled
|
||||||
|
StringList list;
|
||||||
|
size_t orgLen = _node.getShortPath().length();
|
||||||
|
|
||||||
|
_dirList.clear();
|
||||||
|
_iconTypeList.clear();
|
||||||
|
|
||||||
|
for(const auto& file : _fileList)
|
||||||
|
{
|
||||||
|
const string& path = file.getShortPath();
|
||||||
|
const string& name = file.getName();
|
||||||
|
const string& displayName = _showFileExtensions ? name : file.getNameWithExt(EmptyString);
|
||||||
|
|
||||||
|
// display only relative path in tooltip
|
||||||
|
if(path.length() >= orgLen && !fullPathToolTip())
|
||||||
|
_dirList.push_back(path.substr(orgLen));
|
||||||
|
else
|
||||||
|
_dirList.push_back(path);
|
||||||
|
if(file.isDirectory())
|
||||||
|
{
|
||||||
|
if(BSPF::endsWithIgnoreCase(name, ".zip"))
|
||||||
|
{
|
||||||
|
list.push_back(displayName);
|
||||||
|
_iconTypeList.push_back(IconType::zip);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list.push_back(name);
|
||||||
|
if(name == "..")
|
||||||
|
_iconTypeList.push_back(IconType::updir);
|
||||||
|
else
|
||||||
|
_iconTypeList.push_back(IconType::directory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
list.push_back(displayName);
|
||||||
|
_iconTypeList.push_back(romIconType(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
extendLists(list);
|
||||||
|
|
||||||
|
setList(list);
|
||||||
|
setSelected(select);
|
||||||
|
ListWidget::recalc();
|
||||||
|
|
||||||
|
progress().close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool FileListWidget::isDirectory(const FilesystemNode& node) const
|
||||||
|
{
|
||||||
|
return node.isDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void FileListWidget::getChildren(const FilesystemNode::CancelCheck& isCancelled)
|
||||||
|
{
|
||||||
if(_includeSubDirs)
|
if(_includeSubDirs)
|
||||||
{
|
{
|
||||||
// Actually this could become HUGE
|
// Actually this could become HUGE
|
||||||
|
@ -94,53 +155,15 @@ void FileListWidget::setLocation(const FilesystemNode& node,
|
||||||
_fileList.reserve(0x200);
|
_fileList.reserve(0x200);
|
||||||
_node.getChildren(_fileList, _fsmode, _filter, false, true, isCancelled);
|
_node.getChildren(_fileList, _fsmode, _filter, false, true, isCancelled);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Now fill the list widget with the names from the file list,
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
// even if cancelled
|
FileListWidget::IconType FileListWidget::romIconType(const FilesystemNode& file) const
|
||||||
StringList l;
|
{
|
||||||
size_t orgLen = _node.getShortPath().length();
|
if(file.isFile() && Bankswitch::isValidRomName(file.getName()))
|
||||||
|
return IconType::rom;
|
||||||
_dirList.clear();
|
else
|
||||||
_iconList.clear();
|
return IconType::unknown;
|
||||||
for(const auto& file : _fileList)
|
|
||||||
{
|
|
||||||
const string& path = file.getShortPath();
|
|
||||||
const string& name = file.getName();
|
|
||||||
const string& displayName = _showFileExtensions ? name : file.getNameWithExt(EmptyString);
|
|
||||||
|
|
||||||
// display only relative path in tooltip
|
|
||||||
if(path.length() >= orgLen)
|
|
||||||
_dirList.push_back(path.substr(orgLen));
|
|
||||||
else
|
|
||||||
_dirList.push_back(path);
|
|
||||||
if(file.isDirectory())
|
|
||||||
{
|
|
||||||
if(BSPF::endsWithIgnoreCase(name, ".zip"))
|
|
||||||
{
|
|
||||||
l.push_back(displayName);
|
|
||||||
_iconList.push_back(IconType::zip);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
l.push_back(name);
|
|
||||||
_iconList.push_back(IconType::directory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
l.push_back(displayName);
|
|
||||||
if(file.isFile() && Bankswitch::isValidRomName(name))
|
|
||||||
_iconList.push_back(IconType::rom);
|
|
||||||
else
|
|
||||||
_iconList.push_back(IconType::unknown);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setList(l);
|
|
||||||
setSelected(select);
|
|
||||||
ListWidget::recalc();
|
|
||||||
|
|
||||||
progress().close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -160,12 +183,16 @@ void FileListWidget::selectParent()
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void FileListWidget::reload()
|
void FileListWidget::reload()
|
||||||
{
|
{
|
||||||
if(_node.exists())
|
if(isDirectory(_node))
|
||||||
{
|
{
|
||||||
if(_showFileExtensions || selected().isDirectory())
|
_selectedFile = selected().getName();
|
||||||
_selectedFile = selected().getName();
|
setLocation(_node, _selectedFile);
|
||||||
else
|
}
|
||||||
_selectedFile = selected().getNameWithExt(EmptyString);
|
else if(_node.exists())
|
||||||
|
{
|
||||||
|
_selectedFile = _showFileExtensions
|
||||||
|
? selected().getName()
|
||||||
|
: selected().getNameWithExt(EmptyString);
|
||||||
setLocation(_node, _selectedFile);
|
setLocation(_node, _selectedFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,7 +232,10 @@ bool FileListWidget::handleText(char text)
|
||||||
if(BSPF::startsWithIgnoreCase(i, _quickSelectStr))
|
if(BSPF::startsWithIgnoreCase(i, _quickSelectStr))
|
||||||
// Select directories when the first character is uppercase
|
// Select directories when the first character is uppercase
|
||||||
if((std::isupper(_quickSelectStr[0]) != 0) ==
|
if((std::isupper(_quickSelectStr[0]) != 0) ==
|
||||||
(_iconList[selectedItem] == IconType::directory))
|
(_iconTypeList[selectedItem] == IconType::directory
|
||||||
|
|| _iconTypeList[selectedItem] == IconType::favdir
|
||||||
|
|| _iconTypeList[selectedItem] == IconType::recentdir
|
||||||
|
|| _iconTypeList[selectedItem] == IconType::popdir))
|
||||||
break;
|
break;
|
||||||
selectedItem++;
|
selectedItem++;
|
||||||
}
|
}
|
||||||
|
@ -233,7 +263,7 @@ void FileListWidget::handleCommand(CommandSender* sender, int cmd, int data, int
|
||||||
case ListWidget::kActivatedCmd:
|
case ListWidget::kActivatedCmd:
|
||||||
case ListWidget::kDoubleClickedCmd:
|
case ListWidget::kDoubleClickedCmd:
|
||||||
_selected = data;
|
_selected = data;
|
||||||
if(selected().isDirectory())
|
if(isDirectory(selected())/* || !selected().exists()*/)
|
||||||
{
|
{
|
||||||
if(selected().getName() == "..")
|
if(selected().getName() == "..")
|
||||||
selectParent();
|
selectParent();
|
||||||
|
@ -269,6 +299,22 @@ void FileListWidget::handleCommand(CommandSender* sender, int cmd, int data, int
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
int FileListWidget::drawIcon(int i, int x, int y, ColorId color)
|
int FileListWidget::drawIcon(int i, int x, int y, ColorId color)
|
||||||
{
|
{
|
||||||
|
const bool smallIcon = iconWidth() < 24;
|
||||||
|
const Icon* icon = getIcon(i);
|
||||||
|
const int iconGap = smallIcon ? 2 : 3;
|
||||||
|
FBSurface& s = _boss->dialog().surface();
|
||||||
|
|
||||||
|
s.drawBitmap(icon->data(), x + 1 + iconGap,
|
||||||
|
y + (_lineHeight - static_cast<int>(icon->size())) / 2,
|
||||||
|
color, iconWidth() - iconGap * 2, static_cast<int>(icon->size()));
|
||||||
|
|
||||||
|
return iconWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
const FileListWidget::Icon* FileListWidget::getIcon(int i) const
|
||||||
|
{
|
||||||
|
#if 1
|
||||||
static const Icon unknown_small = {
|
static const Icon unknown_small = {
|
||||||
0b00111111'1100000,
|
0b00111111'1100000,
|
||||||
0b00100000'0110000,
|
0b00100000'0110000,
|
||||||
|
@ -342,8 +388,38 @@ int FileListWidget::drawIcon(int i, int x, int y, ColorId color)
|
||||||
0b10001111'1110001,
|
0b10001111'1110001,
|
||||||
0b10000000'0000001,
|
0b10000000'0000001,
|
||||||
0b11111111'1111111
|
0b11111111'1111111
|
||||||
|
|
||||||
};
|
};
|
||||||
|
static const Icon up_small = {
|
||||||
|
//0b00000100'0000000,
|
||||||
|
//0b00001110'0000000,
|
||||||
|
//0b00011111'0000000,
|
||||||
|
//0b00111111'1000000,
|
||||||
|
//0b01111111'1100000,
|
||||||
|
//0b11111111'1110000,
|
||||||
|
//0b00001110'0000000,
|
||||||
|
//0b00001110'0000000,
|
||||||
|
//0b00001110'0000000,
|
||||||
|
//0b00001111'0000000,
|
||||||
|
//0b00001111'1111111,
|
||||||
|
//0b00000111'1111111,
|
||||||
|
//0b00000011'1111111,
|
||||||
|
//0b00000000'0000000,
|
||||||
|
|
||||||
|
0b11111000'0000000,
|
||||||
|
0b11111100'0000000,
|
||||||
|
0b11111111'1111111,
|
||||||
|
0b10000000'0000001,
|
||||||
|
0b10000011'1000001,
|
||||||
|
0b10000111'1100001,
|
||||||
|
0b10001111'1110001,
|
||||||
|
0b10011111'1111001,
|
||||||
|
0b10000011'1000001,
|
||||||
|
0b10000011'1000001,
|
||||||
|
0b10000011'1000001,
|
||||||
|
0b10000011'1000001,
|
||||||
|
0b11111111'1111111
|
||||||
|
};
|
||||||
|
|
||||||
static const Icon unknown_large = {
|
static const Icon unknown_large = {
|
||||||
0b00111'11111111'11000000,
|
0b00111'11111111'11000000,
|
||||||
0b00111'11111111'11100000,
|
0b00111'11111111'11100000,
|
||||||
|
@ -420,52 +496,59 @@ int FileListWidget::drawIcon(int i, int x, int y, ColorId color)
|
||||||
0b111111'11111111'1111111,
|
0b111111'11111111'1111111,
|
||||||
0b111111'11111111'1111111,
|
0b111111'11111111'1111111,
|
||||||
0b110000'00000000'0000011,
|
0b110000'00000000'0000011,
|
||||||
0b110000'00000000'0000011,
|
0b110001'11111111'1100011,
|
||||||
0b110000'11111111'1000011,
|
0b110001'11111111'1100011,
|
||||||
0b110000'11111111'1000011,
|
0b110001'11111111'1100011,
|
||||||
0b110000'00000011'0000011,
|
0b110000'00000111'1000011,
|
||||||
0b110000'00000110'0000011,
|
0b110000'00001111'0000011,
|
||||||
0b110000'00001100'0000011,
|
0b110000'00011110'0000011,
|
||||||
0b110000'00011000'0000011,
|
0b110000'00111100'0000011,
|
||||||
0b110000'00110000'0000011,
|
0b110000'01111000'0000011,
|
||||||
0b110000'01100000'0000011,
|
0b110000'11110000'0000011,
|
||||||
0b110000'11111111'1000011,
|
0b110001'11111111'1100011,
|
||||||
0b110000'11111111'1000011,
|
0b110001'11111111'1100011,
|
||||||
0b110000'00000000'0000011,
|
0b110001'11111111'1100011,
|
||||||
0b110000'00000000'0000011,
|
0b110000'00000000'0000011,
|
||||||
0b111111'11111111'1111111,
|
0b111111'11111111'1111111,
|
||||||
0b111111'11111111'1111111
|
0b111111'11111111'1111111
|
||||||
};
|
};
|
||||||
|
static const Icon up_large = {
|
||||||
|
0b111111'10000000'0000000,
|
||||||
|
0b111111'11000000'0000000,
|
||||||
|
0b111111'11100000'0000000,
|
||||||
|
0b111111'11111111'1111111,
|
||||||
|
0b111111'11111111'1111111,
|
||||||
|
0b110000'00000000'0000011,
|
||||||
|
0b110000'00011100'0000011,
|
||||||
|
0b110000'00111110'0000011,
|
||||||
|
0b110000'01111111'0000011,
|
||||||
|
0b110000'11111111'1000011,
|
||||||
|
0b110001'11111111'1100011,
|
||||||
|
0b110011'11111111'1110011,
|
||||||
|
0b110011'11111111'1110011,
|
||||||
|
0b110000'00111110'0000011,
|
||||||
|
0b110000'00111110'0000011,
|
||||||
|
0b110000'00111110'0000011,
|
||||||
|
0b110000'00111110'0000011,
|
||||||
|
0b110000'00111110'0000011,
|
||||||
|
0b110000'00000000'0000011,
|
||||||
|
0b111111'11111111'1111111,
|
||||||
|
0b111111'11111111'1111111
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
static const Icon* small_icons[int(IconType::numTypes)] = {
|
||||||
|
&unknown_small, &rom_small, &directory_small, &zip_small, &up_small
|
||||||
|
};
|
||||||
|
static const Icon* large_icons[int(IconType::numTypes)] = {
|
||||||
|
&unknown_large, &rom_large, &directory_large, &zip_large, &up_large,
|
||||||
|
};
|
||||||
const bool smallIcon = iconWidth() < 24;
|
const bool smallIcon = iconWidth() < 24;
|
||||||
const int iconGap = smallIcon ? 2 : 3;
|
const int iconType = int(_iconTypeList[i]);
|
||||||
|
|
||||||
const Icon* icon{nullptr};
|
assert(iconType < 5);
|
||||||
switch(_iconList[i])
|
|
||||||
{
|
|
||||||
case IconType::rom:
|
|
||||||
icon = smallIcon ? &rom_small: &rom_large;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IconType::directory:
|
return smallIcon ? small_icons[iconType] : large_icons[iconType];
|
||||||
icon = smallIcon ? &directory_small : &directory_large;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case IconType::zip:
|
|
||||||
icon = smallIcon ? &zip_small : &zip_large;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
icon = smallIcon ? &unknown_small : &unknown_large;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
FBSurface& s = _boss->dialog().surface();
|
|
||||||
|
|
||||||
s.drawBitmap(icon->data(), x + 1 + iconGap,
|
|
||||||
y + (_lineHeight - static_cast<int>(icon->size())) / 2,
|
|
||||||
color, iconWidth() - iconGap * 2, static_cast<int>(icon->size()));
|
|
||||||
|
|
||||||
return iconWidth();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
|
|
@ -47,6 +47,7 @@ class FileListWidget : public StringListWidget
|
||||||
ItemChanged = 'FLic', // Entry in the list is changed (single-click, etc)
|
ItemChanged = 'FLic', // Entry in the list is changed (single-click, etc)
|
||||||
ItemActivated = 'FLac' // Entry in the list is activated (double-click, etc)
|
ItemActivated = 'FLac' // Entry in the list is activated (double-click, etc)
|
||||||
};
|
};
|
||||||
|
using IconTypeFilter = std::function<bool(const FilesystemNode& node)>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FileListWidget(GuiObject* boss, const GUI::Font& font,
|
FileListWidget(GuiObject* boss, const GUI::Font& font,
|
||||||
|
@ -99,16 +100,39 @@ class FileListWidget : public StringListWidget
|
||||||
ProgressDialog& progress();
|
ProgressDialog& progress();
|
||||||
void incProgress();
|
void incProgress();
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
enum class IconType {
|
enum class IconType {
|
||||||
unknown,
|
unknown,
|
||||||
rom,
|
rom,
|
||||||
directory,
|
directory,
|
||||||
zip
|
zip,
|
||||||
|
updir,
|
||||||
|
numTypes,
|
||||||
|
favorite = numTypes,
|
||||||
|
favdir,
|
||||||
|
recentdir,
|
||||||
|
popdir,
|
||||||
|
numLauncherTypes = popdir - numTypes + 1
|
||||||
};
|
};
|
||||||
using IconTypeList = std::vector<IconType>;
|
using IconTypeList = std::vector<IconType>;
|
||||||
using Icon = uIntArray;
|
using Icon = uIntArray;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual bool isDirectory(const FilesystemNode& node) const;
|
||||||
|
virtual void getChildren(const FilesystemNode::CancelCheck& isCancelled);
|
||||||
|
virtual void extendLists(StringList& list) { };
|
||||||
|
virtual IconType romIconType(const FilesystemNode& file) const;
|
||||||
|
virtual const Icon* getIcon(int i) const;
|
||||||
|
int iconWidth() const;
|
||||||
|
virtual bool fullPathToolTip() const { return false; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FilesystemNode _node;
|
||||||
|
FSList _fileList;
|
||||||
|
FilesystemNode::NameFilter _filter;
|
||||||
|
StringList _dirList;
|
||||||
|
IconTypeList _iconTypeList;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/** Very similar to setDirectory(), but also updates the history */
|
/** Very similar to setDirectory(), but also updates the history */
|
||||||
void setLocation(const FilesystemNode& node, const string& select);
|
void setLocation(const FilesystemNode& node, const string& select);
|
||||||
|
@ -116,19 +140,12 @@ class FileListWidget : public StringListWidget
|
||||||
bool handleText(char text) override;
|
bool handleText(char text) override;
|
||||||
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
|
void handleCommand(CommandSender* sender, int cmd, int data, int id) override;
|
||||||
int drawIcon(int i, int x, int y, ColorId color) override;
|
int drawIcon(int i, int x, int y, ColorId color) override;
|
||||||
int iconWidth() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FilesystemNode::ListMode _fsmode{FilesystemNode::ListMode::All};
|
FilesystemNode::ListMode _fsmode{FilesystemNode::ListMode::All};
|
||||||
FilesystemNode::NameFilter _filter;
|
|
||||||
FilesystemNode _node;
|
|
||||||
FSList _fileList;
|
|
||||||
bool _includeSubDirs{false};
|
bool _includeSubDirs{false};
|
||||||
bool _showFileExtensions{true};
|
bool _showFileExtensions{true};
|
||||||
|
|
||||||
StringList _dirList;
|
|
||||||
IconTypeList _iconList;
|
|
||||||
|
|
||||||
Common::FixedStack<string> _history;
|
Common::FixedStack<string> _history;
|
||||||
uInt32 _selected{0};
|
uInt32 _selected{0};
|
||||||
string _selectedFile;
|
string _selectedFile;
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "Dialog.hxx"
|
#include "Dialog.hxx"
|
||||||
#include "EditTextWidget.hxx"
|
#include "EditTextWidget.hxx"
|
||||||
#include "FileListWidget.hxx"
|
#include "FileListWidget.hxx"
|
||||||
|
#include "LauncherFileListWidget.hxx"
|
||||||
#include "FSNode.hxx"
|
#include "FSNode.hxx"
|
||||||
#include "MD5.hxx"
|
#include "MD5.hxx"
|
||||||
#include "OptionsDialog.hxx"
|
#include "OptionsDialog.hxx"
|
||||||
|
@ -211,9 +212,10 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
|
||||||
if(romWidth > 0) romWidth += HBORDER;
|
if(romWidth > 0) romWidth += HBORDER;
|
||||||
int listWidth = _w - (romWidth > 0 ? romWidth + fontWidth : 0) - HBORDER * 2;
|
int listWidth = _w - (romWidth > 0 ? romWidth + fontWidth : 0) - HBORDER * 2;
|
||||||
xpos = HBORDER; ypos += lineHeight + VGAP;
|
xpos = HBORDER; ypos += lineHeight + VGAP;
|
||||||
myList = new FileListWidget(this, _font, xpos, ypos, listWidth, listHeight);
|
myList = new LauncherFileListWidget(this, _font, xpos, ypos, listWidth, listHeight);
|
||||||
myList->setEditable(false);
|
myList->setEditable(false);
|
||||||
myList->setListMode(FilesystemNode::ListMode::All);
|
myList->setListMode(FilesystemNode::ListMode::All);
|
||||||
|
|
||||||
wid.push_back(myList);
|
wid.push_back(myList);
|
||||||
|
|
||||||
// Add ROM info area (if enabled)
|
// Add ROM info area (if enabled)
|
||||||
|
@ -301,10 +303,11 @@ LauncherDialog::LauncherDialog(OSystem& osystem, DialogContainer& parent,
|
||||||
myList->progress().setMessage(" Filtering files" + ELLIPSIS + " ");
|
myList->progress().setMessage(" Filtering files" + ELLIPSIS + " ");
|
||||||
|
|
||||||
// Do we show only ROMs or all files?
|
// Do we show only ROMs or all files?
|
||||||
bool onlyROMs = instance().settings().getBool("launcherroms");
|
myShowOnlyROMs = instance().settings().getBool("launcherroms");
|
||||||
showOnlyROMs(onlyROMs);
|
//showOnlyROMs(onlyROMs);
|
||||||
if(myAllFiles)
|
if(myAllFiles)
|
||||||
myAllFiles->setState(!onlyROMs);
|
myAllFiles->setState(!myShowOnlyROMs);
|
||||||
|
applyFiltering();
|
||||||
|
|
||||||
setHelpAnchor("ROMInfo");
|
setHelpAnchor("ROMInfo");
|
||||||
}
|
}
|
||||||
|
@ -348,11 +351,9 @@ const FilesystemNode& LauncherDialog::currentDir() const
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void LauncherDialog::reload()
|
void LauncherDialog::reload()
|
||||||
{
|
{
|
||||||
bool subDirs = instance().settings().getBool("launchersubdirs");
|
|
||||||
bool extensions = instance().settings().getBool("launcherextensions");
|
bool extensions = instance().settings().getBool("launcherextensions");
|
||||||
|
|
||||||
myMD5List.clear();
|
myMD5List.clear();
|
||||||
myList->setIncludeSubDirs(subDirs);
|
|
||||||
myList->setShowFileExtensions(extensions);
|
myList->setShowFileExtensions(extensions);
|
||||||
myList->reload();
|
myList->reload();
|
||||||
myPendingReload = false;
|
myPendingReload = false;
|
||||||
|
@ -372,34 +373,37 @@ void LauncherDialog::loadConfig()
|
||||||
{
|
{
|
||||||
// Should we use a temporary directory specified on the commandline, or the
|
// Should we use a temporary directory specified on the commandline, or the
|
||||||
// default one specified by the settings?
|
// default one specified by the settings?
|
||||||
const string& tmpromdir = instance().settings().getString("tmpromdir");
|
Settings& settings = instance().settings();
|
||||||
|
const string& tmpromdir = settings.getString("tmpromdir");
|
||||||
const string& romdir = tmpromdir != "" ? tmpromdir :
|
const string& romdir = tmpromdir != "" ? tmpromdir :
|
||||||
instance().settings().getString("romdir");
|
settings.getString("romdir");
|
||||||
const string& version = instance().settings().getString("stella.version");
|
const string& version = settings.getString("stella.version");
|
||||||
|
|
||||||
// Show "What's New" message when a new version of Stella is run for the first time
|
// Show "What's New" message when a new version of Stella is run for the first time
|
||||||
if(version != STELLA_VERSION)
|
if(version != STELLA_VERSION)
|
||||||
{
|
{
|
||||||
openWhatsNew();
|
openWhatsNew();
|
||||||
instance().settings().setValue("stella.version", STELLA_VERSION);
|
settings.setValue("stella.version", STELLA_VERSION);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool subDirs = instance().settings().getBool("launchersubdirs");
|
bool subDirs = settings.getBool("launchersubdirs");
|
||||||
bool extensions = instance().settings().getBool("launcherextensions");
|
bool extensions = settings.getBool("launcherextensions");
|
||||||
|
|
||||||
if (mySubDirs) mySubDirs->setState(subDirs);
|
if (mySubDirs) mySubDirs->setState(subDirs);
|
||||||
myList->setIncludeSubDirs(subDirs);
|
myList->setIncludeSubDirs(subDirs);
|
||||||
myList->setShowFileExtensions(extensions);
|
myList->setShowFileExtensions(extensions);
|
||||||
|
// Favorites
|
||||||
|
myList->loadFavorites();
|
||||||
|
|
||||||
// Assume that if the list is empty, this is the first time that loadConfig()
|
// Assume that if the list is empty, this is the first time that loadConfig()
|
||||||
// has been called (and we should reload the list)
|
// has been called (and we should reload the list)
|
||||||
if(myList->getList().empty())
|
if(myList->getList().empty())
|
||||||
{
|
{
|
||||||
FilesystemNode node(romdir == "" ? "~" : romdir);
|
FilesystemNode node(romdir == "" ? "~" : romdir);
|
||||||
if(!(node.exists() && node.isDirectory()))
|
if(!myList->isDirectory(node))
|
||||||
node = FilesystemNode("~");
|
node = FilesystemNode("~");
|
||||||
|
|
||||||
myList->setDirectory(node, instance().settings().getString("lastrom"));
|
myList->setDirectory(node, settings.getString("lastrom"));
|
||||||
updateUI();
|
updateUI();
|
||||||
}
|
}
|
||||||
Dialog::setFocus(getFocusList()[mySelectedItem]);
|
Dialog::setFocus(getFocusList()[mySelectedItem]);
|
||||||
|
@ -413,10 +417,15 @@ void LauncherDialog::loadConfig()
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void LauncherDialog::saveConfig()
|
void LauncherDialog::saveConfig()
|
||||||
{
|
{
|
||||||
if (mySubDirs)
|
Settings& settings = instance().settings();
|
||||||
instance().settings().setValue("launchersubdirs", mySubDirs->getState());
|
|
||||||
if(instance().settings().getBool("followlauncher"))
|
if(mySubDirs)
|
||||||
instance().settings().setValue("romdir", myList->currentDir().getShortPath());
|
settings.setValue("launchersubdirs", mySubDirs->getState());
|
||||||
|
if(settings.getBool("followlauncher"))
|
||||||
|
settings.setValue("romdir", myList->currentDir().getShortPath());
|
||||||
|
|
||||||
|
// Favorites
|
||||||
|
myList->saveFavorites();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -617,25 +626,36 @@ void LauncherDialog::loadRomInfo()
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void LauncherDialog::handleContextMenu()
|
void LauncherDialog::handleContextMenu()
|
||||||
{
|
{
|
||||||
const string& cmd = menu().getSelectedTag().toString();
|
const string& cmd = contextMenu().getSelectedTag().toString();
|
||||||
|
|
||||||
if(cmd == "override")
|
if(cmd == "favorite")
|
||||||
|
myList->toggleUserFavorite();
|
||||||
|
else if(cmd == "override")
|
||||||
openGlobalProps();
|
openGlobalProps();
|
||||||
|
else if(cmd == "extensions")
|
||||||
|
toggleExtensions();
|
||||||
|
else if(cmd == "sorting")
|
||||||
|
toggleSorting();
|
||||||
|
else if(cmd == "showall")
|
||||||
|
toggleShowAll();
|
||||||
|
else if(cmd == "subdirs")
|
||||||
|
toggleSubDirs();
|
||||||
else if(cmd == "reload")
|
else if(cmd == "reload")
|
||||||
reload();
|
reload();
|
||||||
else if(cmd == "highscores")
|
else if(cmd == "highscores")
|
||||||
openHighScores();
|
openHighScores();
|
||||||
|
else if(cmd == "options")
|
||||||
|
openSettings();
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
ContextMenu& LauncherDialog::menu()
|
ContextMenu& LauncherDialog::contextMenu()
|
||||||
{
|
{
|
||||||
if(myMenu == nullptr)
|
if(myContextMenu == nullptr)
|
||||||
// Create (empty) context menu for ROM list options
|
// Create (empty) context menu for ROM list options
|
||||||
myMenu = make_unique<ContextMenu>(this, _font, EmptyVarList);
|
myContextMenu = make_unique<ContextMenu>(this, _font, EmptyVarList);
|
||||||
|
|
||||||
|
return *myContextMenu;
|
||||||
return *myMenu;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
@ -658,8 +678,16 @@ void LauncherDialog::handleKeyDown(StellaKey key, StellaMod mod, bool repeated)
|
||||||
handled = true;
|
handled = true;
|
||||||
switch(key)
|
switch(key)
|
||||||
{
|
{
|
||||||
case KBDK_P:
|
case KBDK_A:
|
||||||
openGlobalProps();
|
toggleShowAll();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KBDK_D:
|
||||||
|
toggleSubDirs();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KBDK_F:
|
||||||
|
myList->toggleUserFavorite();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KBDK_H:
|
case KBDK_H:
|
||||||
|
@ -667,19 +695,25 @@ void LauncherDialog::handleKeyDown(StellaKey key, StellaMod mod, bool repeated)
|
||||||
openHighScores();
|
openHighScores();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case KBDK_O:
|
||||||
|
openSettings();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KBDK_P:
|
||||||
|
openGlobalProps();
|
||||||
|
break;
|
||||||
|
|
||||||
case KBDK_R:
|
case KBDK_R:
|
||||||
reload();
|
reload();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case KBDK_X:
|
case KBDK_S:
|
||||||
{
|
toggleSorting();
|
||||||
bool extensions = !instance().settings().getBool("launcherextensions");
|
break;
|
||||||
|
|
||||||
instance().settings().setValue("launcherextensions", extensions);
|
case KBDK_X:
|
||||||
myList->setShowFileExtensions(extensions);
|
toggleExtensions();
|
||||||
reload();
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
handled = false;
|
handled = false;
|
||||||
|
@ -736,7 +770,7 @@ void LauncherDialog::handleJoyUp(int stick, int button)
|
||||||
if (button == 1 && (e == Event::UIOK || e == Event::NoType) &&
|
if (button == 1 && (e == Event::UIOK || e == Event::NoType) &&
|
||||||
!currentNode().isDirectory() && Bankswitch::isValidRomName(currentNode()))
|
!currentNode().isDirectory() && Bankswitch::isValidRomName(currentNode()))
|
||||||
openGlobalProps();
|
openGlobalProps();
|
||||||
if (button == 3 && (e == Event::Event::UITabPrev || e == Event::NoType))
|
if (button == 3 && (e == Event::UITabPrev || e == Event::NoType))
|
||||||
openSettings();
|
openSettings();
|
||||||
else if (!myEventHandled)
|
else if (!myEventHandled)
|
||||||
Dialog::handleJoyUp(stick, button);
|
Dialog::handleJoyUp(stick, button);
|
||||||
|
@ -779,18 +813,7 @@ void LauncherDialog::handleMouseDown(int x, int y, MouseButton b, int clickCount
|
||||||
&& x + getAbsX() >= myList->getLeft() && x + getAbsX() <= myList->getRight()
|
&& x + getAbsX() >= myList->getLeft() && x + getAbsX() <= myList->getRight()
|
||||||
&& y + getAbsY() >= myList->getTop() && y + getAbsY() <= myList->getBottom())
|
&& y + getAbsY() >= myList->getTop() && y + getAbsY() <= myList->getBottom())
|
||||||
{
|
{
|
||||||
// Dynamically create context menu for ROM list options
|
openContextMenu(x, y);
|
||||||
VariantList items;
|
|
||||||
|
|
||||||
if(!currentNode().isDirectory() && Bankswitch::isValidRomName(currentNode()))
|
|
||||||
VarList::push_back(items, " Power-on options" + ELLIPSIS + " Ctrl+P", "override");
|
|
||||||
if(instance().highScores().enabled())
|
|
||||||
VarList::push_back(items, " High scores" + ELLIPSIS + " Ctrl+H", "highscores");
|
|
||||||
VarList::push_back(items, " Reload listing Ctrl+R ", "reload");
|
|
||||||
menu().addItems(items);
|
|
||||||
|
|
||||||
// Add menu at current x,y mouse location
|
|
||||||
menu().show(x + getAbsX(), y + getAbsY(), surface().dstRect());
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
Dialog::handleMouseDown(x, y, b, clickCount);
|
Dialog::handleMouseDown(x, y, b, clickCount);
|
||||||
|
@ -813,7 +836,7 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case kLoadROMCmd:
|
case kLoadROMCmd:
|
||||||
if(myList->selected().isDirectory())
|
if(myList->isDirectory(myList->selected()))
|
||||||
{
|
{
|
||||||
if(myList->selected().getName() == "..")
|
if(myList->selected().getName() == "..")
|
||||||
myList->selectParent();
|
myList->selectParent();
|
||||||
|
@ -823,6 +846,9 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
|
||||||
}
|
}
|
||||||
[[fallthrough]];
|
[[fallthrough]];
|
||||||
case FileListWidget::ItemActivated:
|
case FileListWidget::ItemActivated:
|
||||||
|
// Assumes that the ROM will be loaded successfully, has to be done
|
||||||
|
// before saving the config.
|
||||||
|
myList->updateFavorites();
|
||||||
saveConfig();
|
saveConfig();
|
||||||
loadRom();
|
loadRom();
|
||||||
break;
|
break;
|
||||||
|
@ -840,8 +866,8 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ListWidget::kLongButtonPressCmd:
|
case ListWidget::kLongButtonPressCmd:
|
||||||
if (!currentNode().isDirectory() && Bankswitch::isValidRomName(currentNode()))
|
if(!currentNode().isDirectory() && Bankswitch::isValidRomName(currentNode()))
|
||||||
openGlobalProps();
|
openContextMenu();
|
||||||
myEventHandled = true;
|
myEventHandled = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -878,7 +904,7 @@ void LauncherDialog::handleCommand(CommandSender* sender, int cmd,
|
||||||
{
|
{
|
||||||
FilesystemNode node(romDir);
|
FilesystemNode node(romDir);
|
||||||
|
|
||||||
if(!(node.exists() && node.isDirectory()))
|
if(!myList->isDirectory(node))
|
||||||
node = FilesystemNode("~");
|
node = FilesystemNode("~");
|
||||||
|
|
||||||
myList->setDirectory(node);
|
myList->setDirectory(node);
|
||||||
|
@ -930,6 +956,117 @@ void LauncherDialog::setDefaultDir()
|
||||||
instance().settings().setValue("romdir", myList->currentDir().getShortPath());
|
instance().settings().setValue("romdir", myList->currentDir().getShortPath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void LauncherDialog::toggleShowAll()
|
||||||
|
{
|
||||||
|
myShowOnlyROMs = !instance().settings().getBool("launcherroms");
|
||||||
|
|
||||||
|
instance().settings().setValue("launcherroms", myShowOnlyROMs);
|
||||||
|
if(myAllFiles)
|
||||||
|
myAllFiles->setState(!myShowOnlyROMs);
|
||||||
|
applyFiltering();
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void LauncherDialog::toggleSubDirs()
|
||||||
|
{
|
||||||
|
bool subdirs = !instance().settings().getBool("launchersubdirs");
|
||||||
|
|
||||||
|
instance().settings().setValue("launchersubdirs", subdirs);
|
||||||
|
if(mySubDirs)
|
||||||
|
mySubDirs->setState(subdirs);
|
||||||
|
myList->setIncludeSubDirs(subdirs);
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void LauncherDialog::toggleExtensions()
|
||||||
|
{
|
||||||
|
bool extensions = !instance().settings().getBool("launcherextensions");
|
||||||
|
|
||||||
|
instance().settings().setValue("launcherextensions", extensions);
|
||||||
|
myList->setShowFileExtensions(extensions);
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void LauncherDialog::toggleSorting()
|
||||||
|
{
|
||||||
|
if(myList->inVirtualDir())
|
||||||
|
{
|
||||||
|
// Toggle between normal and alternative sorting of virtual directories
|
||||||
|
bool altSorting = !instance().settings().getBool("altsorting");
|
||||||
|
|
||||||
|
instance().settings().setValue("altsorting", altSorting);
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LauncherDialog::addContextItem(VariantList& items, const string& label,
|
||||||
|
const string& shortcut, const string& key)
|
||||||
|
{
|
||||||
|
const string pad = " ";
|
||||||
|
|
||||||
|
if(myUseMinimalUI)
|
||||||
|
VarList::push_back(items, " " + label + " ", key);
|
||||||
|
else
|
||||||
|
VarList::push_back(items, " " + label + pad.substr(0, 24 - label.length())
|
||||||
|
+ shortcut + " ", key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void LauncherDialog::openContextMenu(int x, int y)
|
||||||
|
{
|
||||||
|
if(x < 0 || y < 0)
|
||||||
|
{
|
||||||
|
// Determine position from currently selected list item
|
||||||
|
x = myList->getLeft() + myList->getWidth() / 2;
|
||||||
|
y = myList->getTop() + (myList->getSelected() - myList->currentPos() + 1) * _font.getLineHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamically create context menu for ROM list options
|
||||||
|
VariantList items;
|
||||||
|
|
||||||
|
// TODO: remove subdirs and show all from GUI
|
||||||
|
|
||||||
|
if(!currentNode().isDirectory() && Bankswitch::isValidRomName(currentNode()))
|
||||||
|
{
|
||||||
|
addContextItem(items, myList->isUserFavorite(myList->selected().getPath())
|
||||||
|
? "Remove from favorites"
|
||||||
|
: "Add to favorites", "Ctrl+F", "favorite");
|
||||||
|
addContextItem(items, "Power-on options" + ELLIPSIS, "Ctrl+P", "override");
|
||||||
|
if(instance().highScores().enabled())
|
||||||
|
addContextItem(items, "High scores" + ELLIPSIS, "Ctrl+H", "highscores");
|
||||||
|
}
|
||||||
|
if(myUseMinimalUI)
|
||||||
|
addContextItem(items, "Options" + ELLIPSIS, "Ctrl+O", "options");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addContextItem(items, instance().settings().getBool("launcherextensions")
|
||||||
|
? "Disable file extensions"
|
||||||
|
: "Enable file extensions", "Ctrl+X", "extensions");
|
||||||
|
if(myList->inVirtualDir())
|
||||||
|
addContextItem(items, instance().settings().getBool("altsorting")
|
||||||
|
? "Normal sorting"
|
||||||
|
: "Alternative sorting", "Ctrl+S", "sorting");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addContextItem(items, instance().settings().getBool("launcherroms")
|
||||||
|
? "Show all files"
|
||||||
|
: "Show only ROMs", "Ctrl+A", "showall");
|
||||||
|
addContextItem(items, instance().settings().getBool("launchersubdirs")
|
||||||
|
? "Exclude subdirectories"
|
||||||
|
: "Include subdirectories", "Ctrl+D", "subdirs");
|
||||||
|
}
|
||||||
|
addContextItem(items, "Reload listing", "Ctrl+R", "reload");
|
||||||
|
}
|
||||||
|
contextMenu().addItems(items);
|
||||||
|
|
||||||
|
// Add menu at current x,y mouse location
|
||||||
|
contextMenu().show(x + getAbsX(), y + getAbsY(), surface().dstRect());
|
||||||
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
void LauncherDialog::openGlobalProps()
|
void LauncherDialog::openGlobalProps()
|
||||||
{
|
{
|
||||||
|
|
|
@ -25,9 +25,10 @@ class DialogContainer;
|
||||||
class OSystem;
|
class OSystem;
|
||||||
class Properties;
|
class Properties;
|
||||||
class EditTextWidget;
|
class EditTextWidget;
|
||||||
class FileListWidget;
|
class LauncherFileListWidget;
|
||||||
class RomInfoWidget;
|
class RomInfoWidget;
|
||||||
class StaticTextWidget;
|
class StaticTextWidget;
|
||||||
|
|
||||||
namespace Common {
|
namespace Common {
|
||||||
struct Size;
|
struct Size;
|
||||||
}
|
}
|
||||||
|
@ -36,10 +37,12 @@ namespace GUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
#include "Dialog.hxx"
|
#include "Dialog.hxx"
|
||||||
#include "FSNode.hxx"
|
#include "FSNode.hxx"
|
||||||
|
#include "Variant.hxx"
|
||||||
|
|
||||||
class LauncherDialog : public Dialog
|
class LauncherDialog : public Dialog
|
||||||
{
|
{
|
||||||
|
@ -50,6 +53,7 @@ class LauncherDialog : public Dialog
|
||||||
kRomDirChosenCmd = 'romc', // ROM dir chosen
|
kRomDirChosenCmd = 'romc', // ROM dir chosen
|
||||||
kExtChangedCmd = 'extc' // File extension display changed
|
kExtChangedCmd = 'extc' // File extension display changed
|
||||||
};
|
};
|
||||||
|
using FileList = std::unordered_set<string>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
LauncherDialog(OSystem& osystem, DialogContainer& parent,
|
LauncherDialog(OSystem& osystem, DialogContainer& parent,
|
||||||
|
@ -153,16 +157,23 @@ class LauncherDialog : public Dialog
|
||||||
void handleContextMenu();
|
void handleContextMenu();
|
||||||
void showOnlyROMs(bool state);
|
void showOnlyROMs(bool state);
|
||||||
void setDefaultDir();
|
void setDefaultDir();
|
||||||
|
void toggleShowAll();
|
||||||
|
void toggleSubDirs();
|
||||||
|
void toggleExtensions();
|
||||||
|
void toggleSorting();
|
||||||
|
void addContextItem(VariantList& items, const string& label,
|
||||||
|
const string& shortcut, const string& key);
|
||||||
|
void openContextMenu(int x = -1, int y = -1);
|
||||||
void openGlobalProps();
|
void openGlobalProps();
|
||||||
void openSettings();
|
void openSettings();
|
||||||
void openHighScores();
|
void openHighScores();
|
||||||
void openWhatsNew();
|
void openWhatsNew();
|
||||||
|
|
||||||
ContextMenu& menu();
|
ContextMenu& contextMenu();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unique_ptr<Dialog> myDialog;
|
unique_ptr<Dialog> myDialog;
|
||||||
unique_ptr<ContextMenu> myMenu;
|
unique_ptr<ContextMenu> myContextMenu;
|
||||||
|
|
||||||
// automatically sized font for ROM info viewer
|
// automatically sized font for ROM info viewer
|
||||||
unique_ptr<GUI::Font> myROMInfoFont;
|
unique_ptr<GUI::Font> myROMInfoFont;
|
||||||
|
@ -172,7 +183,7 @@ class LauncherDialog : public Dialog
|
||||||
CheckboxWidget* mySubDirs{nullptr};
|
CheckboxWidget* mySubDirs{nullptr};
|
||||||
StaticTextWidget* myRomCount{nullptr};
|
StaticTextWidget* myRomCount{nullptr};
|
||||||
|
|
||||||
FileListWidget* myList{nullptr};
|
LauncherFileListWidget* myList{nullptr};
|
||||||
|
|
||||||
StaticTextWidget* myDirLabel{nullptr};
|
StaticTextWidget* myDirLabel{nullptr};
|
||||||
EditTextWidget* myDir{nullptr};
|
EditTextWidget* myDir{nullptr};
|
||||||
|
@ -182,8 +193,8 @@ class LauncherDialog : public Dialog
|
||||||
ButtonWidget* myOptionsButton{nullptr};
|
ButtonWidget* myOptionsButton{nullptr};
|
||||||
ButtonWidget* myQuitButton{nullptr};
|
ButtonWidget* myQuitButton{nullptr};
|
||||||
|
|
||||||
// FIXME - NOT USED StaticTextWidget* myRomLink{nullptr};
|
|
||||||
RomInfoWidget* myRomInfoWidget{nullptr};
|
RomInfoWidget* myRomInfoWidget{nullptr};
|
||||||
|
|
||||||
std::unordered_map<string,string> myMD5List;
|
std::unordered_map<string,string> myMD5List;
|
||||||
|
|
||||||
int mySelectedItem{0};
|
int mySelectedItem{0};
|
||||||
|
|
|
@ -0,0 +1,445 @@
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SSSS tt lll lll
|
||||||
|
// SS SS tt ll ll
|
||||||
|
// SS tttttt eeee ll ll aaaa
|
||||||
|
// SSSS tt ee ee ll ll aa
|
||||||
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||||
|
// SS SS tt ee ll ll aa aa
|
||||||
|
// SSSS ttt eeeee llll llll aaaaa
|
||||||
|
//
|
||||||
|
// Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony
|
||||||
|
// and the Stella Team
|
||||||
|
//
|
||||||
|
// See the file "License.txt" for information on usage and redistribution of
|
||||||
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#include "Bankswitch.hxx"
|
||||||
|
#include "FavoritesManager.hxx"
|
||||||
|
#include "OSystem.hxx"
|
||||||
|
#include "ProgressDialog.hxx"
|
||||||
|
#include "Settings.hxx"
|
||||||
|
|
||||||
|
#include "LauncherFileListWidget.hxx"
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
LauncherFileListWidget::LauncherFileListWidget(GuiObject* boss, const GUI::Font& font,
|
||||||
|
int x, int y, int w, int h)
|
||||||
|
: FileListWidget(boss, font, x, y, w, h)
|
||||||
|
{
|
||||||
|
// This widget is special, in that it catches signals and redirects them
|
||||||
|
setTarget(this);
|
||||||
|
myFavorites = make_unique<FavoritesManager>(instance().settings());
|
||||||
|
myRomDir = instance().settings().getString("romdir");
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool LauncherFileListWidget::isDirectory(const FilesystemNode& node) const
|
||||||
|
{
|
||||||
|
bool isDir = node.isDirectory();
|
||||||
|
|
||||||
|
// Check for virtual directories
|
||||||
|
if(!isDir && !node.exists())
|
||||||
|
return node.getName() == user_name
|
||||||
|
|| node.getName() == recent_name
|
||||||
|
|| node.getName() == popular_name;
|
||||||
|
|
||||||
|
return isDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void LauncherFileListWidget::getChildren(const FilesystemNode::CancelCheck& isCancelled)
|
||||||
|
{
|
||||||
|
// TODO:
|
||||||
|
// + remove virtual folders in virtual folders
|
||||||
|
// + tooltips (incl. subdirs)
|
||||||
|
// + always add (after remove) recent ROMs
|
||||||
|
// + age recently played (e.g. reduce all regularly, WHEN? HOW MUCH?)
|
||||||
|
// + mark virtual dir when returning from it
|
||||||
|
// + "lastrom"
|
||||||
|
// + uppercase search
|
||||||
|
// + change sort order
|
||||||
|
// + move subdirs & all files into popup menu
|
||||||
|
// + no all files option in virtual folders
|
||||||
|
// + missing large icons
|
||||||
|
// + Settings.cxx doc
|
||||||
|
// + display only in ROM path folder
|
||||||
|
// - remove subdirs & all files from GUI
|
||||||
|
// - doc (settings, hotkeys, popup, launcher, virtual folders)
|
||||||
|
|
||||||
|
if(_node.exists() || !_node.hasParent())
|
||||||
|
{
|
||||||
|
myInVirtualDir = false;
|
||||||
|
FileListWidget::getChildren(isCancelled);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
myInVirtualDir = true;
|
||||||
|
FilesystemNode parent(_node.getParent());
|
||||||
|
parent.setName("..");
|
||||||
|
_fileList.emplace_back(parent);
|
||||||
|
|
||||||
|
const string& name = _node.getName();
|
||||||
|
if(name == user_name)
|
||||||
|
{
|
||||||
|
for(auto& item : myFavorites->userList())
|
||||||
|
{
|
||||||
|
FilesystemNode node(item);
|
||||||
|
if(_filter(node))
|
||||||
|
_fileList.emplace_back(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(name == popular_name)
|
||||||
|
{
|
||||||
|
for(auto& item : myFavorites->popularList())
|
||||||
|
{
|
||||||
|
FilesystemNode node(item.first);
|
||||||
|
if(_filter(node))
|
||||||
|
_fileList.emplace_back(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(name == recent_name)
|
||||||
|
{
|
||||||
|
for(auto& item : myFavorites->recentList())
|
||||||
|
{
|
||||||
|
FilesystemNode node(item);
|
||||||
|
if(_filter(node))
|
||||||
|
_fileList.emplace_back(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void LauncherFileListWidget::addFolder(StringList& list, int& offset, const string& name, IconType icon)
|
||||||
|
{
|
||||||
|
_fileList.insert(_fileList.begin() + offset,
|
||||||
|
FilesystemNode(_node.getPath() + name));
|
||||||
|
list.insert(list.begin() + offset, name);
|
||||||
|
_dirList.insert(_dirList.begin() + offset, "");
|
||||||
|
_iconTypeList.insert((_iconTypeList.begin() + offset), icon);
|
||||||
|
|
||||||
|
++offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void LauncherFileListWidget::extendLists(StringList& list)
|
||||||
|
{
|
||||||
|
// Only show virtual dirs in "romdir". Except if
|
||||||
|
// "romdir" is virtual or "romdir" is a ZIP
|
||||||
|
// Then show virtual dirs in parent dir of "romdir".
|
||||||
|
if(myRomDir == instance().settings().getString("romdir")
|
||||||
|
&& (myInVirtualDir || BSPF::endsWithIgnoreCase(_node.getPath(), ".zip")))
|
||||||
|
myRomDir = _node.getParent().getPath();
|
||||||
|
|
||||||
|
if(_node.getPath() == myRomDir)
|
||||||
|
{
|
||||||
|
// Add virtual directories behind ".."
|
||||||
|
int offset = _fileList.begin()->getName() == ".." ? 1 : 0;
|
||||||
|
|
||||||
|
if(myFavorites->userList().size())
|
||||||
|
addFolder(list, offset, user_name, IconType::favdir);
|
||||||
|
if(myFavorites->popularList().size())
|
||||||
|
addFolder(list, offset, popular_name, IconType::popdir);
|
||||||
|
if(myFavorites->recentList().size())
|
||||||
|
addFolder(list, offset, recent_name, IconType::recentdir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void LauncherFileListWidget::loadFavorites()
|
||||||
|
{
|
||||||
|
myFavorites->load();
|
||||||
|
|
||||||
|
for(const auto& path : myFavorites->userList())
|
||||||
|
userFavor(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void LauncherFileListWidget::saveFavorites()
|
||||||
|
{
|
||||||
|
myFavorites->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void LauncherFileListWidget::updateFavorites()
|
||||||
|
{
|
||||||
|
myFavorites->update(selected().getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
bool LauncherFileListWidget::isUserFavorite(const string& path) const
|
||||||
|
{
|
||||||
|
return myFavorites->existsUser(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void LauncherFileListWidget::toggleUserFavorite()
|
||||||
|
{
|
||||||
|
if(!selected().isDirectory() && Bankswitch::isValidRomName(selected()))
|
||||||
|
{
|
||||||
|
bool isUserFavorite = myFavorites->toggleUser(selected().getPath());
|
||||||
|
|
||||||
|
userFavor(selected().getPath(), isUserFavorite);
|
||||||
|
// Redraw file list
|
||||||
|
setDirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
void LauncherFileListWidget::userFavor(const string& path, bool isUserFavorite)
|
||||||
|
{
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
for(const auto& file : _fileList)
|
||||||
|
{
|
||||||
|
if(file.getPath() == path)
|
||||||
|
break;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
if(pos < _iconTypeList.size())
|
||||||
|
_iconTypeList[pos] = isUserFavorite ? IconType::favorite : IconType::rom;
|
||||||
|
}
|
||||||
|
|
||||||
|
FileListWidget::IconType LauncherFileListWidget::romIconType(const FilesystemNode& file) const
|
||||||
|
{
|
||||||
|
if(file.isFile() && Bankswitch::isValidRomName(file.getName()))
|
||||||
|
return isUserFavorite(file.getPath()) ? IconType::favorite : IconType::rom;
|
||||||
|
else
|
||||||
|
return IconType::unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
const FileListWidget::Icon* LauncherFileListWidget::getIcon(int i) const
|
||||||
|
{
|
||||||
|
static const Icon favorite_small = {
|
||||||
|
//0b0000001'11000000,
|
||||||
|
//0b0000011'01100000,
|
||||||
|
//0b0000010'00100000,
|
||||||
|
//0b0000110'00110000,
|
||||||
|
//0b0000100'00010000,
|
||||||
|
//0b1111100'00011111,
|
||||||
|
//0b1000000'00000001,
|
||||||
|
//0b1100000'00000011,
|
||||||
|
//0b0110000'00000110,
|
||||||
|
//0b0011000'00001100,
|
||||||
|
//0b0010000'00000100,
|
||||||
|
//0b0110000'10000110,
|
||||||
|
//0b0100011'01100010,
|
||||||
|
//0b0101110'00111010,
|
||||||
|
//0b0111000'00001110,
|
||||||
|
|
||||||
|
0b0000001'10000000,
|
||||||
|
0b0000011'11000000,
|
||||||
|
0b0000010'01000000,
|
||||||
|
0b0000110'01100000,
|
||||||
|
0b0111100'00111100,
|
||||||
|
0b1100000'00000110,
|
||||||
|
0b0110000'00001100,
|
||||||
|
0b0011000'00011000,
|
||||||
|
0b0001100'00110000,
|
||||||
|
0b0011000'00011000,
|
||||||
|
0b0010001'10001000,
|
||||||
|
0b0110111'11101100,
|
||||||
|
0b0111100'00111100,
|
||||||
|
0b0011000'00011000,
|
||||||
|
};
|
||||||
|
static const Icon favdir_small = {
|
||||||
|
//0b11111000'0000000,
|
||||||
|
//0b11111100'0000000,
|
||||||
|
//0b11111111'1111111,
|
||||||
|
//0b10000000'0000001,
|
||||||
|
//0b10000001'0000001,
|
||||||
|
//0b10000011'1000001,
|
||||||
|
//0b10001111'1110001,
|
||||||
|
//0b10000111'1100001,
|
||||||
|
//0b10000011'1000001,
|
||||||
|
//0b10000111'1100001,
|
||||||
|
//0b10001100'0110001,
|
||||||
|
//0b10000000'0000001,
|
||||||
|
//0b11111111'1111111
|
||||||
|
|
||||||
|
0b11111000'0000000,
|
||||||
|
0b11111100'0000000,
|
||||||
|
0b11111101'0111111,
|
||||||
|
0b10000011'1000001,
|
||||||
|
0b10000011'1000001,
|
||||||
|
0b10000111'1100001,
|
||||||
|
0b10111111'1111101,
|
||||||
|
0b10011111'1111001,
|
||||||
|
0b10001111'1110001,
|
||||||
|
0b10000111'1100001,
|
||||||
|
0b10001111'1110001,
|
||||||
|
0b10011100'0111001,
|
||||||
|
0b11011000'0011011
|
||||||
|
};
|
||||||
|
static const Icon recent_small = {
|
||||||
|
0b11111000'0000000,
|
||||||
|
0b11111100'0000000,
|
||||||
|
0b11111111'1111111,
|
||||||
|
0b10000011'1000001,
|
||||||
|
0b10001110'1110001,
|
||||||
|
0b10001110'1110001,
|
||||||
|
0b10011110'1111001,
|
||||||
|
0b10011110'0111001,
|
||||||
|
0b10011111'0011001,
|
||||||
|
0b10001111'1110001,
|
||||||
|
0b10001111'1110001,
|
||||||
|
0b10000011'1000001,
|
||||||
|
0b11111111'1111111
|
||||||
|
};
|
||||||
|
static const Icon popular_small = {
|
||||||
|
0b11111000'0000000,
|
||||||
|
0b11111100'0000000,
|
||||||
|
0b11111111'1111111,
|
||||||
|
0b10000000'0000001,
|
||||||
|
0b10001100'0110001,
|
||||||
|
0b10011110'1111001,
|
||||||
|
0b10011111'1111001,
|
||||||
|
0b10011111'1111001,
|
||||||
|
0b10001111'1110001,
|
||||||
|
0b10000111'1100001,
|
||||||
|
0b10000011'1000001,
|
||||||
|
0b10000001'0000001,
|
||||||
|
0b11111111'1111111
|
||||||
|
};
|
||||||
|
|
||||||
|
static const Icon favorite_large = {
|
||||||
|
//0b0000000'0001000'0000000,
|
||||||
|
//0b0000000'0011100'0000000,
|
||||||
|
//0b0000000'0011100'0000000,
|
||||||
|
//0b0000000'0110110'0000000,
|
||||||
|
//0b0000000'0110110'0000000,
|
||||||
|
//0b0000000'0110110'0000000,
|
||||||
|
//0b0000000'1100011'0000000,
|
||||||
|
//0b0111111'1000001'1111110,
|
||||||
|
//0b1111111'0000000'1111111,
|
||||||
|
//0b0110000'0000000'0000110,
|
||||||
|
//0b0011000'0000000'0001100,
|
||||||
|
//0b0001100'0000000'0011000,
|
||||||
|
//0b0000110'0000000'0110000,
|
||||||
|
//0b0000011'0000000'1100000,
|
||||||
|
//0b0000111'0000000'1110000,
|
||||||
|
//0b0000110'0001000'0110000,
|
||||||
|
//0b0001100'0011100'0011000,
|
||||||
|
//0b0001100'1110111'0011000,
|
||||||
|
//0b0011001'1000001'1001100,
|
||||||
|
//0b0011111'0000000'1111100,
|
||||||
|
//0b0001100'0000000'0011000
|
||||||
|
|
||||||
|
0b0000000'0001000'0000000,
|
||||||
|
0b0000000'0011100'0000000,
|
||||||
|
0b0000000'0011100'0000000,
|
||||||
|
0b0000000'0111110'0000000,
|
||||||
|
0b0000000'0111110'0000000,
|
||||||
|
0b0000000'0110110'0000000,
|
||||||
|
0b0000000'1100011'0000000,
|
||||||
|
0b0111111'1100011'1111110,
|
||||||
|
0b1111111'1000001'1111111,
|
||||||
|
0b0111000'0000000'0001110,
|
||||||
|
0b0011100'0000000'0011100,
|
||||||
|
0b0001110'0000000'0111000,
|
||||||
|
0b0000111'0000000'1110000,
|
||||||
|
0b0000011'1000001'1100000,
|
||||||
|
0b0000111'0000000'1110000,
|
||||||
|
0b0000111'0011100'1110000,
|
||||||
|
0b0001110'0111110'0111000,
|
||||||
|
0b0001100'1110111'0011000,
|
||||||
|
0b0011111'1000001'1111100,
|
||||||
|
0b0011111'0000000'1111100,
|
||||||
|
0b0001100'0000000'0011000
|
||||||
|
|
||||||
|
};
|
||||||
|
static const Icon favdir_large = {
|
||||||
|
0b111111'10000000'0000000,
|
||||||
|
0b111111'11000000'0000000,
|
||||||
|
0b111111'11100000'0000000,
|
||||||
|
0b111111'11111111'1111111,
|
||||||
|
0b111111'11111111'1111111,
|
||||||
|
0b110000'00001000'0000011,
|
||||||
|
0b110000'00011100'0000011,
|
||||||
|
0b110000'00011100'0000011,
|
||||||
|
0b110000'00111110'0000011,
|
||||||
|
0b110001'11111111'1100011,
|
||||||
|
0b110011'11111111'1110011,
|
||||||
|
0b110001'11111111'1100011,
|
||||||
|
0b110000'11111111'1000011,
|
||||||
|
0b110000'01111111'0000011,
|
||||||
|
0b110000'11111111'1000011,
|
||||||
|
0b110001'11110111'1100011,
|
||||||
|
0b110001'11100011'1100011,
|
||||||
|
0b110000'11000001'1000011,
|
||||||
|
0b110000'00000000'0000011,
|
||||||
|
0b111111'11111111'1111111,
|
||||||
|
0b111111'11111111'1111111
|
||||||
|
};
|
||||||
|
static const Icon recent_large = {
|
||||||
|
0b111111'10000000'0000000,
|
||||||
|
0b111111'11000000'0000000,
|
||||||
|
0b111111'11100000'0000000,
|
||||||
|
0b111111'11111111'1111111,
|
||||||
|
0b111111'11111111'1111111,
|
||||||
|
0b110000'00000000'0000011,
|
||||||
|
0b110000'00111110'0000011,
|
||||||
|
0b110000'11110111'1000011,
|
||||||
|
0b110001'11110111'1100011,
|
||||||
|
0b110001'11110111'1100011,
|
||||||
|
0b110011'11110111'1110011,
|
||||||
|
0b110011'11110111'1110011,
|
||||||
|
0b110011'11110011'1110011,
|
||||||
|
0b110011'11111001'1110011,
|
||||||
|
0b110001'11111100'1100011,
|
||||||
|
0b110001'11111111'1100011,
|
||||||
|
0b110000'11111111'1000011,
|
||||||
|
0b110000'00111110'0000011,
|
||||||
|
0b110000'00000000'0000011,
|
||||||
|
0b111111'11111111'1111111,
|
||||||
|
0b111111'11111111'1111111
|
||||||
|
};
|
||||||
|
static const Icon popular_large = {
|
||||||
|
0b111111'10000000'0000000,
|
||||||
|
0b111111'11000000'0000000,
|
||||||
|
0b111111'11100000'0000000,
|
||||||
|
0b111111'11111111'1111111,
|
||||||
|
0b111111'11111111'1111111,
|
||||||
|
0b110000'00000000'0000011,
|
||||||
|
0b110000'00000000'0000011,
|
||||||
|
0b110000'11100011'1000011,
|
||||||
|
0b110001'11110111'1100011,
|
||||||
|
0b110011'11111111'1110011,
|
||||||
|
0b110011'11111111'1110011,
|
||||||
|
0b110011'11111111'1110011,
|
||||||
|
0b110001'11111111'1100011,
|
||||||
|
0b110000'11111111'1000011,
|
||||||
|
0b110000'01111111'0000011,
|
||||||
|
0b110000'00111110'0000011,
|
||||||
|
0b110000'00011100'0000011,
|
||||||
|
0b110000'00001000'0000011,
|
||||||
|
0b110000'00000000'0000011,
|
||||||
|
0b111111'11111111'1111111,
|
||||||
|
0b111111'11111111'1111111
|
||||||
|
};
|
||||||
|
static const Icon* small_icons[int(IconType::numLauncherTypes)] = {
|
||||||
|
&favorite_small, &favdir_small, &recent_small, &popular_small
|
||||||
|
|
||||||
|
};
|
||||||
|
static const Icon* large_icons[int(IconType::numLauncherTypes)] = {
|
||||||
|
&favorite_large, &favdir_large, &recent_large, &popular_large
|
||||||
|
};
|
||||||
|
|
||||||
|
if(int(_iconTypeList[i]) < int(IconType::numTypes))
|
||||||
|
return FileListWidget::getIcon(i);
|
||||||
|
|
||||||
|
const bool smallIcon = iconWidth() < 24;
|
||||||
|
const int iconType = int(_iconTypeList[i]) - int(IconType::numTypes);
|
||||||
|
|
||||||
|
assert(iconType < int(IconType::numLauncherTypes));
|
||||||
|
|
||||||
|
return smallIcon ? small_icons[iconType] : large_icons[iconType];
|
||||||
|
}
|
||||||
|
|
||||||
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
|
const string LauncherFileListWidget::user_name = "Favorites";
|
||||||
|
const string LauncherFileListWidget::recent_name = "Recently Played";
|
||||||
|
const string LauncherFileListWidget::popular_name = "Most Popular";
|
|
@ -0,0 +1,70 @@
|
||||||
|
//============================================================================
|
||||||
|
//
|
||||||
|
// SSSS tt lll lll
|
||||||
|
// SS SS tt ll ll
|
||||||
|
// SS tttttt eeee ll ll aaaa
|
||||||
|
// SSSS tt ee ee ll ll aa
|
||||||
|
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
|
||||||
|
// SS SS tt ee ll ll aa aa
|
||||||
|
// SSSS ttt eeeee llll llll aaaaa
|
||||||
|
//
|
||||||
|
// Copyright (c) 1995-2021 by Bradford W. Mott, Stephen Anthony
|
||||||
|
// and the Stella Team
|
||||||
|
//
|
||||||
|
// See the file "License.txt" for information on usage and redistribution of
|
||||||
|
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
||||||
|
//============================================================================
|
||||||
|
|
||||||
|
#ifndef LAUNCHER_FILE_LIST_WIDGET_HXX
|
||||||
|
#define LAUNCHER_FILE_LIST_WIDGET_HXX
|
||||||
|
|
||||||
|
class FavoritesManager;
|
||||||
|
class FilesystemNode;
|
||||||
|
class ProgressDialog;
|
||||||
|
class Settings;
|
||||||
|
|
||||||
|
#include "FileListWidget.hxx"
|
||||||
|
|
||||||
|
/**
|
||||||
|
Specialization of the general FileListWidget which procides support for
|
||||||
|
user defined favorites, recently played ROMs and most popular ROMs.
|
||||||
|
|
||||||
|
@author Thomas Jentzsch
|
||||||
|
*/
|
||||||
|
|
||||||
|
class LauncherFileListWidget : public FileListWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LauncherFileListWidget(GuiObject* boss, const GUI::Font& font,
|
||||||
|
int x, int y, int w, int h);
|
||||||
|
~LauncherFileListWidget() override = default;
|
||||||
|
|
||||||
|
void loadFavorites();
|
||||||
|
void saveFavorites();
|
||||||
|
void updateFavorites();
|
||||||
|
bool isUserFavorite(const string& path) const;
|
||||||
|
void toggleUserFavorite();
|
||||||
|
|
||||||
|
bool isDirectory(const FilesystemNode& node) const override;
|
||||||
|
bool inVirtualDir() const { return myInVirtualDir; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static const string user_name;
|
||||||
|
static const string recent_name;
|
||||||
|
static const string popular_name;
|
||||||
|
|
||||||
|
unique_ptr<FavoritesManager> myFavorites;
|
||||||
|
bool myInVirtualDir{false};
|
||||||
|
string myRomDir;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void getChildren(const FilesystemNode::CancelCheck& isCancelled) override;
|
||||||
|
void userFavor(const string& path, bool enable = true);
|
||||||
|
void addFolder(StringList& list, int& offset, const string& name, IconType icon);
|
||||||
|
void extendLists(StringList& list) override;
|
||||||
|
IconType romIconType(const FilesystemNode& file) const override;
|
||||||
|
const Icon* getIcon(int i) const override;
|
||||||
|
bool fullPathToolTip() const override { return myInVirtualDir; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -18,7 +18,6 @@
|
||||||
#include "bspf.hxx"
|
#include "bspf.hxx"
|
||||||
#include "Dialog.hxx"
|
#include "Dialog.hxx"
|
||||||
#include "FBSurface.hxx"
|
#include "FBSurface.hxx"
|
||||||
#include "Settings.hxx"
|
|
||||||
#include "ScrollBarWidget.hxx"
|
#include "ScrollBarWidget.hxx"
|
||||||
#include "StringListWidget.hxx"
|
#include "StringListWidget.hxx"
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ MODULE_OBJS := \
|
||||||
src/gui/EditTextWidget.o \
|
src/gui/EditTextWidget.o \
|
||||||
src/gui/EmulationDialog.o \
|
src/gui/EmulationDialog.o \
|
||||||
src/gui/EventMappingWidget.o \
|
src/gui/EventMappingWidget.o \
|
||||||
|
src/gui/FavoritesManager.o \
|
||||||
src/gui/FileListWidget.o \
|
src/gui/FileListWidget.o \
|
||||||
src/gui/Font.o \
|
src/gui/Font.o \
|
||||||
src/gui/GameInfoDialog.o \
|
src/gui/GameInfoDialog.o \
|
||||||
|
@ -27,6 +28,7 @@ MODULE_OBJS := \
|
||||||
src/gui/InputTextDialog.o \
|
src/gui/InputTextDialog.o \
|
||||||
src/gui/JoystickDialog.o \
|
src/gui/JoystickDialog.o \
|
||||||
src/gui/LauncherDialog.o \
|
src/gui/LauncherDialog.o \
|
||||||
|
src/gui/LauncherFileListWidget.o \
|
||||||
src/gui/Launcher.o \
|
src/gui/Launcher.o \
|
||||||
src/gui/ListWidget.o \
|
src/gui/ListWidget.o \
|
||||||
src/gui/LoggerDialog.o \
|
src/gui/LoggerDialog.o \
|
||||||
|
|
|
@ -907,9 +907,11 @@
|
||||||
<ClCompile Include="..\gui\ColorWidget.cxx" />
|
<ClCompile Include="..\gui\ColorWidget.cxx" />
|
||||||
<ClCompile Include="..\gui\DeveloperDialog.cxx" />
|
<ClCompile Include="..\gui\DeveloperDialog.cxx" />
|
||||||
<ClCompile Include="..\gui\EmulationDialog.cxx" />
|
<ClCompile Include="..\gui\EmulationDialog.cxx" />
|
||||||
|
<ClCompile Include="..\gui\FavoritesManager.cxx" />
|
||||||
<ClCompile Include="..\gui\FileListWidget.cxx" />
|
<ClCompile Include="..\gui\FileListWidget.cxx" />
|
||||||
<ClCompile Include="..\gui\HighScoresDialog.cxx" />
|
<ClCompile Include="..\gui\HighScoresDialog.cxx" />
|
||||||
<ClCompile Include="..\gui\HighScoresMenu.cxx" />
|
<ClCompile Include="..\gui\HighScoresMenu.cxx" />
|
||||||
|
<ClCompile Include="..\gui\LauncherFileListWidget.cxx" />
|
||||||
<ClCompile Include="..\gui\PlusRomsMenu.cxx" />
|
<ClCompile Include="..\gui\PlusRomsMenu.cxx" />
|
||||||
<ClCompile Include="..\gui\JoystickDialog.cxx" />
|
<ClCompile Include="..\gui\JoystickDialog.cxx" />
|
||||||
<ClCompile Include="..\gui\LoggerDialog.cxx" />
|
<ClCompile Include="..\gui\LoggerDialog.cxx" />
|
||||||
|
@ -2111,9 +2113,11 @@
|
||||||
<ClInclude Include="..\gui\ConsoleMediumFont.hxx" />
|
<ClInclude Include="..\gui\ConsoleMediumFont.hxx" />
|
||||||
<ClInclude Include="..\gui\DeveloperDialog.hxx" />
|
<ClInclude Include="..\gui\DeveloperDialog.hxx" />
|
||||||
<ClInclude Include="..\gui\EmulationDialog.hxx" />
|
<ClInclude Include="..\gui\EmulationDialog.hxx" />
|
||||||
|
<ClInclude Include="..\gui\FavoritesManager.hxx" />
|
||||||
<ClInclude Include="..\gui\FileListWidget.hxx" />
|
<ClInclude Include="..\gui\FileListWidget.hxx" />
|
||||||
<ClInclude Include="..\gui\HighScoresDialog.hxx" />
|
<ClInclude Include="..\gui\HighScoresDialog.hxx" />
|
||||||
<ClInclude Include="..\gui\HighScoresMenu.hxx" />
|
<ClInclude Include="..\gui\HighScoresMenu.hxx" />
|
||||||
|
<ClInclude Include="..\gui\LauncherFileListWidget.hxx" />
|
||||||
<ClInclude Include="..\gui\PlusRomsMenu.hxx" />
|
<ClInclude Include="..\gui\PlusRomsMenu.hxx" />
|
||||||
<ClInclude Include="..\gui\JoystickDialog.hxx" />
|
<ClInclude Include="..\gui\JoystickDialog.hxx" />
|
||||||
<ClInclude Include="..\gui\LoggerDialog.hxx" />
|
<ClInclude Include="..\gui\LoggerDialog.hxx" />
|
||||||
|
|
|
@ -1119,6 +1119,12 @@
|
||||||
<ClCompile Include="..\common\DevSettingsHandler.cxx">
|
<ClCompile Include="..\common\DevSettingsHandler.cxx">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\gui\LauncherFileListWidget.cxx">
|
||||||
|
<Filter>Source Files\gui</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="..\gui\FavoritesManager.cxx">
|
||||||
|
<Filter>Source Files\gui</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="..\common\bspf.hxx">
|
<ClInclude Include="..\common\bspf.hxx">
|
||||||
|
@ -2300,6 +2306,12 @@
|
||||||
<ClInclude Include="..\common\DevSettingsHandler.hxx">
|
<ClInclude Include="..\common\DevSettingsHandler.hxx">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\gui\LauncherFileListWidget.hxx">
|
||||||
|
<Filter>Header Files\gui</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\gui\FavoritesManager.hxx">
|
||||||
|
<Filter>Header Files\gui</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="stella.ico">
|
<None Include="stella.ico">
|
||||||
|
|
Loading…
Reference in New Issue