From 29c1a0cb78d3dd3466042f15167c73320eb83031 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Sat, 28 Jan 2023 00:01:53 +0100 Subject: [PATCH] feat: Allow multiple yara files to be selected at once --- lib/libimhex/include/hex/api/event.hpp | 2 +- lib/libimhex/include/hex/helpers/fs.hpp | 2 +- lib/libimhex/include/hex/ui/view.hpp | 2 +- lib/libimhex/source/helpers/fs.cpp | 29 ++++++++++++----- lib/libimhex/source/ui/view.cpp | 6 ++-- plugins/builtin/source/content/ui_items.cpp | 31 ++++++++++++++----- .../source/content/views/view_hex_editor.cpp | 2 +- .../content/views/view_pattern_editor.cpp | 8 ++--- .../source/content/views/view_yara.cpp | 9 +++--- 9 files changed, 61 insertions(+), 30 deletions(-) diff --git a/lib/libimhex/include/hex/api/event.hpp b/lib/libimhex/include/hex/api/event.hpp index b1a0dc3d0..c842f2080 100644 --- a/lib/libimhex/include/hex/api/event.hpp +++ b/lib/libimhex/include/hex/api/event.hpp @@ -139,6 +139,6 @@ namespace hex { EVENT_DEF(RequestShowErrorPopup, std::string); EVENT_DEF(RequestShowFatalErrorPopup, std::string); EVENT_DEF(RequestShowYesNoQuestionPopup, std::string, std::function, std::function); - EVENT_DEF(RequestShowFileChooserPopup, std::vector, std::vector, std::function); + EVENT_DEF(RequestShowFileChooserPopup, std::vector, std::vector, std::function, bool); } \ No newline at end of file diff --git a/lib/libimhex/include/hex/helpers/fs.hpp b/lib/libimhex/include/hex/helpers/fs.hpp index ce3f3072a..19e866058 100644 --- a/lib/libimhex/include/hex/helpers/fs.hpp +++ b/lib/libimhex/include/hex/helpers/fs.hpp @@ -84,7 +84,7 @@ namespace hex::fs { }; void setFileBrowserErrorCallback(const std::function &callback); - bool openFileBrowser(DialogMode mode, const std::vector &validExtensions, const std::function &callback, const std::string &defaultPath = {}); + bool openFileBrowser(DialogMode mode, const std::vector &validExtensions, const std::function &callback, const std::string &defaultPath = {}, bool multiple = false); enum class ImHexPath : u32 { Patterns = 0, diff --git a/lib/libimhex/include/hex/ui/view.hpp b/lib/libimhex/include/hex/ui/view.hpp index 6d0664db3..be76b1f56 100644 --- a/lib/libimhex/include/hex/ui/view.hpp +++ b/lib/libimhex/include/hex/ui/view.hpp @@ -39,7 +39,7 @@ namespace hex { static void showFatalPopup(const std::string &message); static void showYesNoQuestionPopup(const std::string &message, const std::function &yesCallback, const std::function &noCallback); - static void showFileChooserPopup(const std::vector &paths, const std::vector &validExtensions, const std::function &callback); + static void showFileChooserPopup(const std::vector &paths, const std::vector &validExtensions, bool multiple, const std::function &callback); [[nodiscard]] virtual bool hasViewMenuItemEntry() const; [[nodiscard]] virtual ImVec2 getMinSize() const; diff --git a/lib/libimhex/source/helpers/fs.cpp b/lib/libimhex/source/helpers/fs.cpp index 90137e860..bde7e1df6 100644 --- a/lib/libimhex/source/helpers/fs.cpp +++ b/lib/libimhex/source/helpers/fs.cpp @@ -81,20 +81,24 @@ namespace hex::fs { s_fileBrowserErrorCallback = callback; } - bool openFileBrowser(DialogMode mode, const std::vector &validExtensions, const std::function &callback, const std::string &defaultPath) { + bool openFileBrowser(DialogMode mode, const std::vector &validExtensions, const std::function &callback, const std::string &defaultPath, bool multiple) { NFD::Init(); - nfdchar_t *outPath = nullptr; + NFD::UniquePathU8 outPath; + NFD::UniquePathSet outPaths; nfdresult_t result; switch (mode) { case DialogMode::Open: - result = NFD::OpenDialog(outPath, validExtensions.data(), validExtensions.size(), defaultPath.c_str()); + if (multiple) + result = NFD::OpenDialogMultiple(outPaths, validExtensions.data(), validExtensions.size(), defaultPath.empty() ? nullptr : defaultPath.c_str()); + else + result = NFD::OpenDialog(outPath, validExtensions.data(), validExtensions.size(), defaultPath.empty() ? nullptr : defaultPath.c_str()); break; case DialogMode::Save: - result = NFD::SaveDialog(outPath, validExtensions.data(), validExtensions.size(), defaultPath.c_str()); + result = NFD::SaveDialog(outPath, validExtensions.data(), validExtensions.size(), defaultPath.empty() ? nullptr : defaultPath.c_str()); break; case DialogMode::Folder: - result = NFD::PickFolder(outPath, defaultPath.c_str()); + result = NFD::PickFolder(outPath, defaultPath.empty() ? nullptr : defaultPath.c_str()); break; default: hex::unreachable(); @@ -102,10 +106,19 @@ namespace hex::fs { if (result == NFD_OKAY){ if(outPath != nullptr) { - callback(reinterpret_cast(outPath)); - NFD::FreePath(outPath); + callback(reinterpret_cast(outPath.get())); } - } else if (result==NFD_ERROR) { + if (outPaths != nullptr) { + nfdpathsetsize_t numPaths = 0; + if (NFD::PathSet::Count(outPaths, numPaths) == NFD_OKAY) { + for (size_t i = 0; i < numPaths; i++) { + NFD::UniquePathSetPath path; + if (NFD::PathSet::GetPath(outPaths, i, path) == NFD_OKAY) + callback(reinterpret_cast(path.get())); + } + } + } + } else if (result == NFD_ERROR) { if (s_fileBrowserErrorCallback != nullptr) s_fileBrowserErrorCallback(); } diff --git a/lib/libimhex/source/ui/view.cpp b/lib/libimhex/source/ui/view.cpp index 0e8aea25d..991cb5d05 100644 --- a/lib/libimhex/source/ui/view.cpp +++ b/lib/libimhex/source/ui/view.cpp @@ -34,13 +34,13 @@ namespace hex { } - void View::showFileChooserPopup(const std::vector &paths, const std::vector &validExtensions, const std::function &callback) { + void View::showFileChooserPopup(const std::vector &paths, const std::vector &validExtensions, bool multiple, const std::function &callback) { if (paths.empty()) { fs::openFileBrowser(fs::DialogMode::Open, validExtensions, [callback](const auto &path) { callback(path); - }); + }, {}, multiple); } else { - EventManager::post(paths, validExtensions, callback); + EventManager::post(paths, validExtensions, callback, multiple); } } diff --git a/plugins/builtin/source/content/ui_items.cpp b/plugins/builtin/source/content/ui_items.cpp index d4024a5bd..3d57e155f 100644 --- a/plugins/builtin/source/content/ui_items.cpp +++ b/plugins/builtin/source/content/ui_items.cpp @@ -16,10 +16,11 @@ namespace hex::plugin::builtin { static std::string s_popupMessage; static std::function s_yesCallback, s_noCallback; - static u32 s_selectableFileIndex; + static std::set s_selectableFileIndices; static std::vector s_selectableFiles; static std::function s_selectableFileOpenCallback; static std::vector s_selectableFilesValidExtensions; + static bool s_selectableFileMultiple; static void drawGlobalPopups() { @@ -128,8 +129,22 @@ namespace hex::plugin::builtin { u32 index = 0; for (auto &path : s_selectableFiles) { ImGui::PushID(index); - if (ImGui::Selectable(hex::toUTF8String(path.filename()).c_str(), index == s_selectableFileIndex)) - s_selectableFileIndex = index; + + bool selected = s_selectableFileIndices.contains(index); + if (ImGui::Selectable(hex::toUTF8String(path.filename()).c_str(), selected)) { + if (!s_selectableFileMultiple) { + s_selectableFileIndices.clear(); + s_selectableFileIndices.insert(index); + } else { + if (selected) { + s_selectableFileIndices.erase(index); + } else { + s_selectableFileIndices.insert(index); + } + } + + } + ImGui::PopID(); index++; @@ -139,7 +154,8 @@ namespace hex::plugin::builtin { } if (ImGui::Button("hex.builtin.common.open"_lang)) { - s_selectableFileOpenCallback(s_selectableFiles[s_selectableFileIndex]); + for (auto &index : s_selectableFileIndices) + s_selectableFileOpenCallback(s_selectableFiles[index]); ImGui::CloseCurrentPopup(); } @@ -149,7 +165,7 @@ namespace hex::plugin::builtin { fs::openFileBrowser(fs::DialogMode::Open, s_selectableFilesValidExtensions, [](const auto &path) { s_selectableFileOpenCallback(path); ImGui::CloseCurrentPopup(); - }); + }, {}, s_selectableFileMultiple); } ImGui::EndPopup(); @@ -195,11 +211,12 @@ namespace hex::plugin::builtin { TaskManager::doLater([] { ImGui::OpenPopup("hex.builtin.common.question"_lang); }); }); - EventManager::subscribe([](const std::vector &paths, const std::vector &validExtensions, const std::function &callback) { - s_selectableFileIndex = 0; + EventManager::subscribe([](const std::vector &paths, const std::vector &validExtensions, const std::function &callback, bool multiple) { + s_selectableFileIndices = { }; s_selectableFiles = paths; s_selectableFilesValidExtensions = validExtensions; s_selectableFileOpenCallback = callback; + s_selectableFileMultiple = multiple; TaskManager::doLater([] { ImGui::OpenPopup("hex.builtin.common.choose_file"_lang); }); }); diff --git a/plugins/builtin/source/content/views/view_hex_editor.cpp b/plugins/builtin/source/content/views/view_hex_editor.cpp index 3afbe4825..e36949aa1 100644 --- a/plugins/builtin/source/content/views/view_hex_editor.cpp +++ b/plugins/builtin/source/content/views/view_hex_editor.cpp @@ -944,7 +944,7 @@ namespace hex::plugin::builtin { } } - View::showFileChooserPopup(paths, { {"Thingy Table File", "tbl"} }, + View::showFileChooserPopup(paths, { {"Thingy Table File", "tbl"} }, false, [this](const auto &path) { this->m_hexEditor.setCustomEncoding(EncodingFile(EncodingFile::Type::Thingy, path)); }); diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index 06d89964b..bdc07bd23 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -867,10 +867,10 @@ namespace hex::plugin::builtin { } } - View::showFileChooserPopup(paths, { {"Pattern File", "hexpat"} }, - [this, provider](const std::fs::path &path) { - this->loadPatternFile(path, provider); - }); + View::showFileChooserPopup(paths, { { "Pattern File", "hexpat" } }, false, + [this, provider](const std::fs::path &path) { + this->loadPatternFile(path, provider); + }); } if (ImGui::MenuItem("hex.builtin.view.pattern_editor.menu.file.save_pattern"_lang, nullptr, false, providerValid)) { diff --git a/plugins/builtin/source/content/views/view_yara.cpp b/plugins/builtin/source/content/views/view_yara.cpp index e428f2521..0bf0c003d 100644 --- a/plugins/builtin/source/content/views/view_yara.cpp +++ b/plugins/builtin/source/content/views/view_yara.cpp @@ -20,7 +20,7 @@ namespace hex::plugin::builtin { ViewYara::ViewYara() : View("hex.builtin.view.yara.name") { yr_initialize(); - ContentRegistry::FileHandler::add({ ".yar" }, [](const auto &path) { + ContentRegistry::FileHandler::add({ ".yar", ".yara" }, [](const auto &path) { for (const auto &destPath : fs::getDefaultPaths(fs::ImHexPath::Yara)) { if (fs::copyFile(path, destPath / path.filename(), std::fs::copy_options::overwrite_existing)) { View::showInfoPopup("hex.builtin.view.yara.rule_added"_lang); @@ -64,9 +64,10 @@ namespace hex::plugin::builtin { } } - View::showFileChooserPopup(paths, { { "Yara File", "yara" }, { "Yara File", "yar" } }, [this](const auto &path) { - this->m_rules.push_back({ path.filename(), path }); - }); + View::showFileChooserPopup(paths, { { "Yara File", "yara" }, { "Yara File", "yar" } }, true, + [this](const auto &path) { + this->m_rules.push_back({ path.filename(), path }); + }); } ImGui::SameLine();