1
0
mirror of synced 2024-11-28 09:30:51 +01:00

feat: Allow recents to also display other providers

This commit is contained in:
WerWolv 2022-08-14 10:07:45 +02:00
parent 440ba3823e
commit 85f0e04d0e
14 changed files with 150 additions and 78 deletions

View File

@ -337,12 +337,12 @@ namespace hex {
void add(bool addToList = true) {
auto typeName = T().getTypeName();
(void)EventManager::subscribe<RequestCreateProvider>([expectedName = typeName](const std::string &name, hex::prv::Provider **provider) {
(void)EventManager::subscribe<RequestCreateProvider>([expectedName = typeName](const std::string &name, bool skipLoadInterface, hex::prv::Provider **provider) {
if (name != expectedName) return;
auto newProvider = new T();
prv::Provider *newProvider = new T();
hex::ImHexApi::Provider::add(newProvider);
hex::ImHexApi::Provider::add(newProvider, skipLoadInterface);
if (provider != nullptr)
*provider = newProvider;

View File

@ -129,7 +129,7 @@ namespace hex {
EVENT_DEF(RequestOpenFile, std::fs::path);
EVENT_DEF(RequestChangeTheme, u32);
EVENT_DEF(RequestOpenPopup, std::string);
EVENT_DEF(RequestCreateProvider, std::string, hex::prv::Provider **);
EVENT_DEF(RequestCreateProvider, std::string, bool, hex::prv::Provider **);
EVENT_DEF(RequestShowInfoPopup, std::string);
EVENT_DEF(RequestShowErrorPopup, std::string);

View File

@ -137,7 +137,7 @@ namespace hex {
void resetDirty();
bool isDirty();
void add(prv::Provider *provider);
void add(prv::Provider *provider, bool skipLoadInterface = false);
template<std::derived_from<prv::Provider> T>
void add(auto &&...args) {
@ -146,7 +146,7 @@ namespace hex {
void remove(prv::Provider *provider, bool noQuestions = false);
prv::Provider* createProvider(const std::string &unlocalizedName);
prv::Provider* createProvider(const std::string &unlocalizedName, bool skipLoadInterface = false);
}

View File

@ -96,7 +96,8 @@ namespace hex::fs {
Resources,
Constants,
Encodings,
Logs
Logs,
Recent
};
std::optional<std::fs::path> getExecutablePath();

View File

@ -104,6 +104,9 @@ namespace hex::prv {
virtual std::pair<Region, bool> getRegionValidity(u64 address) const;
void skipLoadInterface() { this->m_skipLoadInterface = true; }
[[nodiscard]] bool shouldSkipLoadInterface() const { return this->m_skipLoadInterface; }
protected:
u32 m_currPage = 0;
u64 m_baseAddress = 0;
@ -115,6 +118,7 @@ namespace hex::prv {
u32 m_id;
bool m_dirty = false;
bool m_skipLoadInterface = false;
private:
static u32 s_idCounter;

View File

@ -268,10 +268,13 @@ namespace hex {
});
}
void add(prv::Provider *provider) {
void add(prv::Provider *provider, bool skipLoadInterface) {
if (Task::getRunningTaskCount() > 0)
return;
if (skipLoadInterface)
provider->skipLoadInterface();
s_providers.push_back(provider);
EventManager::post<EventProviderCreated>(provider);
@ -310,9 +313,9 @@ namespace hex {
delete provider;
}
prv::Provider* createProvider(const std::string &unlocalizedName) {
prv::Provider* createProvider(const std::string &unlocalizedName, bool skipLoadInterface) {
prv::Provider* result = nullptr;
EventManager::post<RequestCreateProvider>(unlocalizedName, &result);
EventManager::post<RequestCreateProvider>(unlocalizedName, skipLoadInterface, &result);
return result;
}

View File

@ -232,6 +232,9 @@ namespace hex::fs {
case ImHexPath::Yara:
result = appendPath(getDataPaths(), "yara");
break;
case ImHexPath::Recent:
result = appendPath(getConfigPaths(), "recent");
break;
}
if (!listNonExisting) {

View File

@ -251,6 +251,9 @@ namespace hex::prv {
}
nlohmann::json Provider::storeSettings(nlohmann::json settings) const {
settings["displayName"] = this->getName();
settings["type"] = this->getTypeName();
settings["baseAddress"] = this->m_baseAddress;
settings["currPage"] = this->m_currPage;
@ -263,7 +266,7 @@ namespace hex::prv {
}
std::pair<Region, bool> Provider::getRegionValidity(u64 address) const {
if (address > this->getActualSize())
if (address < this->getActualSize())
return { Region::Invalid(), false };
bool insideValidRegion = false;

View File

@ -59,18 +59,20 @@ namespace hex::init {
bool createDirectories() {
bool result = true;
using enum fs::ImHexPath;
constexpr std::array paths = {
fs::ImHexPath::Patterns,
fs::ImHexPath::PatternsInclude,
fs::ImHexPath::Magic,
fs::ImHexPath::Plugins,
fs::ImHexPath::Resources,
fs::ImHexPath::Config,
fs::ImHexPath::Constants,
fs::ImHexPath::Yara,
fs::ImHexPath::Encodings,
fs::ImHexPath::Python,
fs::ImHexPath::Logs
Patterns,
PatternsInclude,
Magic,
Plugins,
Resources,
Config,
Constants,
Yara,
Encodings,
Python,
Logs,
Recent
};
// Check if ImHex is installed in portable mode

View File

@ -27,8 +27,8 @@ namespace hex::plugin::builtin::prv {
[[nodiscard]] std::string getName() const override;
[[nodiscard]] std::vector<std::pair<std::string, std::string>> getDataInformation() const override { return { }; }
void loadSettings(const nlohmann::json &settings) override { hex::unused(settings); }
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override { return settings; }
void loadSettings(const nlohmann::json &settings) override;
[[nodiscard]] nlohmann::json storeSettings(nlohmann::json settings) const override;
[[nodiscard]] std::string getTypeName() const override {
return "hex.builtin.provider.intel_hex";

View File

@ -13,11 +13,16 @@
#include "provider_extra_data.hpp"
#include "content/providers/file_provider.hpp"
namespace hex::plugin::builtin {
static void openFile(const std::fs::path &path) {
ImHexApi::Provider::createProvider("hex.builtin.provider.file");
EventManager::post<EventFileLoaded>(path);
auto provider = ImHexApi::Provider::createProvider("hex.builtin.provider.file", true);
if (auto *fileProvider = dynamic_cast<prv::FileProvider*>(provider); fileProvider != nullptr) {
fileProvider->setPath(path);
(void)fileProvider->open();
}
}
void registerEventHandlers() {
@ -77,6 +82,9 @@ namespace hex::plugin::builtin {
});
EventManager::subscribe<EventProviderCreated>([](hex::prv::Provider *provider) {
if (provider->shouldSkipLoadInterface())
return;
if (provider->hasFilePicker()) {
if (!provider->handleFilePicker()) {
ImHexApi::Tasks::doLater([provider] { ImHexApi::Provider::remove(provider); });
@ -95,9 +103,6 @@ namespace hex::plugin::builtin {
View::showErrorPopup("hex.builtin.popup.error.open"_lang);
ImHexApi::Tasks::doLater([provider] { ImHexApi::Provider::remove(provider); });
}
if (!provider->isWritable())
View::showErrorPopup("hex.builtin.popup.error.read_only"_lang);
}
});

View File

@ -232,4 +232,16 @@ namespace hex::plugin::builtin::prv {
return { Region { closestInterval.start, (closestInterval.stop - closestInterval.start) + 1}, true };
}
void IntelHexProvider::loadSettings(const nlohmann::json &settings) {
Provider::loadSettings(settings);
this->m_sourceFilePath = settings["path"].get<std::string>();
}
nlohmann::json IntelHexProvider::storeSettings(nlohmann::json settings) const {
settings["path"] = this->m_sourceFilePath.string();
return Provider::storeSettings(settings);
}
}

View File

@ -35,8 +35,9 @@ namespace hex::plugin::builtin {
ImGui::Separator();
if (ImGui::Button("hex.builtin.common.open"_lang)) {
if (provider->open())
if (provider->open()) {
ImGui::CloseCurrentPopup();
}
else {
View::showErrorPopup("hex.builtin.view.provider_settings.load_error"_lang);
ImHexApi::Provider::remove(provider);

View File

@ -6,18 +6,17 @@
#include <hex/ui/view.hpp>
#include <hex/helpers/fs.hpp>
#include <hex/helpers/logger.hpp>
#include <hex/helpers/file.hpp>
#include <hex/api/project_file_manager.hpp>
#include <imgui.h>
#include <implot.h>
#include <imnodes.h>
#include <hex/ui/imgui_imhex_extensions.h>
#include <nlohmann/json.hpp>
#include <romfs/romfs.hpp>
#include <fonts/fontawesome_font.h>
#include <fonts/codicons_font.h>
#include <list>
@ -25,12 +24,74 @@
namespace hex::plugin::builtin {
static ImGui::Texture s_bannerTexture, s_backdropTexture;
static std::list<std::fs::path> s_recentFilePaths;
static std::fs::path s_safetyBackupPath;
static std::string s_tipOfTheDay;
struct RecentProvider {
std::string displayName;
std::string type;
std::fs::path filePath;
nlohmann::json data;
bool operator==(const RecentProvider &other) const {
return displayName == other.displayName && type == other.type && data == other.data;
}
};
static std::vector<RecentProvider> s_recentProviders;
static void updateRecentProviders() {
s_recentProviders.clear();
// Query all recent providers
std::vector<std::fs::path> recentFiles;
for (const auto &folder : fs::getDefaultPaths(fs::ImHexPath::Recent)) {
for (const auto &entry : std::fs::directory_iterator(folder)) {
if (entry.is_regular_file())
recentFiles.push_back(entry.path());
}
}
// Sort recent provider files by last modified time
std::sort(recentFiles.begin(), recentFiles.end(), [](const auto &a, const auto &b) {
return std::fs::last_write_time(a) > std::fs::last_write_time(b);
});
for (u32 i = 0; i < recentFiles.size() && s_recentProviders.size() < 5; i++) {
auto &path = recentFiles[i];
try {
auto jsonData = nlohmann::json::parse(fs::File(path, fs::File::Mode::Read).readString());
s_recentProviders.push_back(RecentProvider {
.displayName = jsonData["displayName"],
.type = jsonData["type"],
.filePath = path,
.data = jsonData
});
} catch (...) { }
}
// De-duplicate recent providers
s_recentProviders.erase(std::unique(s_recentProviders.begin(), s_recentProviders.end()), s_recentProviders.end());
}
static void loadRecentProvider(const RecentProvider &recentProvider) {
auto *provider = ImHexApi::Provider::createProvider(recentProvider.type, true);
if (provider != nullptr) {
provider->loadSettings(recentProvider.data);
if (!provider->open() || !provider->isAvailable()) {
View::showErrorPopup("hex.builtin.popup.error.open"_lang);
ImHexApi::Tasks::doLater([provider] { ImHexApi::Provider::remove(provider); });
return;
}
updateRecentProviders();
}
}
static void loadDefaultLayout() {
auto layouts = ContentRegistry::Interface::getLayouts();
if (!layouts.empty()) {
@ -153,9 +214,9 @@ namespace hex::plugin::builtin {
ImGui::UnderlinedText("hex.builtin.welcome.start.recent"_lang);
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + 5_scaled);
{
for (auto &path : s_recentFilePaths) {
if (ImGui::BulletHyperlink(std::fs::path(path).filename().string().c_str())) {
EventManager::post<RequestOpenFile>(path);
for (const auto &recentProvider : s_recentProviders) {
if (ImGui::BulletHyperlink(recentProvider.displayName.c_str())) {
loadRecentProvider(recentProvider);
break;
}
}
@ -314,6 +375,8 @@ namespace hex::plugin::builtin {
}
void createWelcomeScreen() {
updateRecentProviders();
(void)EventManager::subscribe<EventFrameBegin>(drawWelcomeScreen);
(void)EventManager::subscribe<EventFrameBegin>(drawNoViewsBackground);
@ -405,35 +468,16 @@ namespace hex::plugin::builtin {
}
});
(void)EventManager::subscribe<EventFileLoaded>([](const auto &path) {
s_recentFilePaths.push_front(path);
(void)EventManager::subscribe<EventProviderOpened>([](prv::Provider *provider) {
{
std::list<std::fs::path> uniques;
for (auto &file : s_recentFilePaths) {
auto recentPath = fs::getDefaultPaths(fs::ImHexPath::Recent).front();
auto fileName = hex::format("{:%y%m%d_%H%M%S}.json", fmt::gmtime(std::chrono::system_clock::now()));
fs::File recentFile(recentPath / fileName, fs::File::Mode::Create);
bool exists = false;
for (auto &unique : uniques) {
if (file == unique)
exists = true;
}
if (!exists && !file.empty())
uniques.push_back(file);
if (uniques.size() > 5)
break;
}
s_recentFilePaths = uniques;
recentFile.write(provider->storeSettings().dump(4));
}
{
std::vector<std::string> recentFilesVector;
for (const auto &recentPath : s_recentFilePaths)
recentFilesVector.push_back(recentPath.string());
ContentRegistry::Settings::write("hex.builtin.setting.imhex", "hex.builtin.setting.imhex.recent_files", recentFilesVector);
}
updateRecentProviders();
});
EventManager::subscribe<EventProviderCreated>([](auto) {
@ -442,23 +486,23 @@ namespace hex::plugin::builtin {
});
ContentRegistry::Interface::addMenuItem("hex.builtin.menu.file", 1075, [&] {
if (ImGui::BeginMenu("hex.builtin.menu.file.open_recent"_lang, !s_recentFilePaths.empty())) {
if (ImGui::BeginMenu("hex.builtin.menu.file.open_recent"_lang, !s_recentProviders.empty())) {
// Copy to avoid changing list while iteration
std::list<std::fs::path> recentFilePaths = s_recentFilePaths;
for (auto &path : recentFilePaths) {
auto filename = std::fs::path(path).filename().string();
if (ImGui::MenuItem(filename.c_str())) {
EventManager::post<RequestOpenFile>(path);
auto recentProviders = s_recentProviders;
for (auto &recentProvider : recentProviders) {
if (ImGui::MenuItem(recentProvider.displayName.c_str())) {
loadRecentProvider(recentProvider);
}
}
ImGui::Separator();
if (ImGui::MenuItem("hex.builtin.menu.file.clear_recent"_lang)) {
s_recentFilePaths.clear();
ContentRegistry::Settings::write(
"hex.builtin.setting.imhex",
"hex.builtin.setting.imhex.recent_files",
std::vector<std::string> {});
s_recentProviders.clear();
// Remove all recent files
for (const auto &recentPath : fs::getDefaultPaths(fs::ImHexPath::Recent))
for (const auto &entry : std::fs::directory_iterator(recentPath))
std::fs::remove(entry.path());
}
ImGui::EndMenu();
@ -474,12 +518,6 @@ namespace hex::plugin::builtin {
}
}
for (const auto &pathString : ContentRegistry::Settings::read("hex.builtin.setting.imhex", "hex.builtin.setting.imhex.recent_files")) {
std::fs::path path = std::u8string(pathString.begin(), pathString.end());
if (fs::exists(path))
s_recentFilePaths.emplace_back(path);
}
if (ImHexApi::System::getInitArguments().contains("tip-of-the-day")) {
s_tipOfTheDay = ImHexApi::System::getInitArguments()["tip-of-the-day"];