From efe0928fc659fd2ba72e43d1431b50820b049392 Mon Sep 17 00:00:00 2001 From: WerWolv Date: Mon, 10 Feb 2025 21:44:14 +0100 Subject: [PATCH] feat: Initial work rework current project system --- lib/libimhex/CMakeLists.txt | 3 + lib/libimhex/include/hex/project/project.hpp | 57 +++++++ .../include/hex/project/project_manager.hpp | 43 ++++++ lib/libimhex/source/project/project.cpp | 28 ++++ .../source/project/project_manager.cpp | 89 +++++++++++ plugins/builtin/CMakeLists.txt | 2 + .../include/content/views/view_bookmarks.hpp | 4 +- .../content/views/view_data_processor.hpp | 6 +- plugins/builtin/romfs/lang/en_US.json | 4 + .../content/sidebar/project_explorer.cpp | 145 ++++++++++++++++++ .../source/content/views/view_bookmarks.cpp | 70 +++++---- .../content/views/view_data_processor.cpp | 42 +++-- .../content/views/view_pattern_editor.cpp | 18 ++- plugins/builtin/source/plugin_builtin.cpp | 2 + 14 files changed, 464 insertions(+), 49 deletions(-) create mode 100644 lib/libimhex/include/hex/project/project.hpp create mode 100644 lib/libimhex/include/hex/project/project_manager.hpp create mode 100644 lib/libimhex/source/project/project.cpp create mode 100644 lib/libimhex/source/project/project_manager.cpp create mode 100644 plugins/builtin/source/content/sidebar/project_explorer.cpp diff --git a/lib/libimhex/CMakeLists.txt b/lib/libimhex/CMakeLists.txt index beb62210f..f911d8953 100644 --- a/lib/libimhex/CMakeLists.txt +++ b/lib/libimhex/CMakeLists.txt @@ -22,6 +22,9 @@ set(LIBIMHEX_SOURCES source/data_processor/link.cpp source/data_processor/node.cpp + source/project/project.cpp + source/project/project_manager.cpp + source/helpers/utils.cpp source/helpers/utils_linux.cpp source/helpers/fs.cpp diff --git a/lib/libimhex/include/hex/project/project.hpp b/lib/libimhex/include/hex/project/project.hpp new file mode 100644 index 000000000..d7fd17db8 --- /dev/null +++ b/lib/libimhex/include/hex/project/project.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace hex::proj { + + class Content { + public: + explicit Content(UnlocalizedString type, std::string name) + : m_type(std::move(type)), m_name(std::move(name)) { } + + void setData(std::string data) { m_data = std::move(data); } + const std::string& getData() const { return m_data; } + + const std::string& getName() const { return m_name; } + void setName(std::string name) { m_name = std::move(name); } + + const UnlocalizedString& getType() const { return m_type; } + + bool isOpen() const { return m_open; } + void setOpen(bool open) { m_open = open; } + + bool isEmpty() const { return m_data.empty(); } + + private: + UnlocalizedString m_type; + std::string m_name; + std::string m_data; + bool m_open = false; + }; + + class Project { + public: + explicit Project(std::string name) : m_name(std::move(name)) {} + Project(const Project &) = delete; + Project(Project &&) = delete; + ~Project(); + + Project &operator=(const Project &) = delete; + Project &operator=(Project &&) = delete; + + const std::string &getName() const { return m_name; } + + void addContent(UnlocalizedString type); + const std::list>& getContents() const { return m_contents; } + + private: + std::string m_name; + std::list> m_contents; + }; + +} diff --git a/lib/libimhex/include/hex/project/project_manager.hpp b/lib/libimhex/include/hex/project/project_manager.hpp new file mode 100644 index 000000000..3dd674cca --- /dev/null +++ b/lib/libimhex/include/hex/project/project_manager.hpp @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace hex::proj { + + class ProjectManager { + ProjectManager() = default; + + public: + static void createProject(std::string name); + static void loadProject(const std::filesystem::path &path); + static void removeProject(const Project &project); + + using LoadFunction = std::function; + using StoreFunction = std::function; + + struct ContentHandler { + UnlocalizedString type; + bool allowMultiple = false; + LoadFunction load; + StoreFunction store; + }; + + static void registerContentHandler(ContentHandler handler); + static const std::list& getContentHandlers(); + static const ContentHandler* getContentHandler(const UnlocalizedString &typeName); + + static const std::list> &getProjects(); + + static void storeContent(Content &content); + static void loadContent(Content &content); + + static Content* getLoadedContent(const UnlocalizedString &type); + }; + +} diff --git a/lib/libimhex/source/project/project.cpp b/lib/libimhex/source/project/project.cpp new file mode 100644 index 000000000..faebe1dee --- /dev/null +++ b/lib/libimhex/source/project/project.cpp @@ -0,0 +1,28 @@ +#include +#include + +namespace hex::proj { + + Project::~Project() { + for (auto &content : m_contents) { + ProjectManager::storeContent(*content); + } + } + + + void Project::addContent(UnlocalizedString type) { + const auto &content = m_contents.emplace_back( + std::make_unique( + type, + fmt::format("Unnamed {}", Lang(type)) + ) + ); + + const auto loadedContent = ProjectManager::getLoadedContent(type); + ProjectManager::storeContent(loadedContent == nullptr ? *content : *loadedContent); + + ProjectManager::loadContent(*content); + } + + +} diff --git a/lib/libimhex/source/project/project_manager.cpp b/lib/libimhex/source/project/project_manager.cpp new file mode 100644 index 000000000..f6c6af7bb --- /dev/null +++ b/lib/libimhex/source/project/project_manager.cpp @@ -0,0 +1,89 @@ +#include +#include + +namespace hex::proj { + + static AutoReset>> s_projects; + static AutoReset> s_contentHandlers; + + void ProjectManager::createProject(std::string name) { + s_projects->emplace_back(std::make_unique(std::move(name))); + } + + void ProjectManager::loadProject(const std::filesystem::path &path) { + std::ignore = path; + } + + void ProjectManager::removeProject(const Project &projectToClose) { + std::erase_if(*s_projects, [&](const std::unique_ptr &project) { + return project.get() == &projectToClose; + }); + } + + + const std::list> &ProjectManager::getProjects() { + return *s_projects; + } + + const std::list &ProjectManager::getContentHandlers() { + return *s_contentHandlers; + } + + void ProjectManager::registerContentHandler(ContentHandler handler) { + s_contentHandlers->emplace_back(std::move(handler)); + } + + const ProjectManager::ContentHandler* ProjectManager::getContentHandler(const UnlocalizedString &typeName) { + for (const auto &handler : getContentHandlers()) { + if (handler.type == typeName) { + return &handler; + } + } + + return nullptr; + } + + void ProjectManager::storeContent(Content &content) { + const auto *handler = getContentHandler(content.getType()); + if (handler != nullptr) { + handler->store(content); + } + } + + void ProjectManager::loadContent(Content &content) { + const auto *handler = getContentHandler(content.getType()); + + if (handler != nullptr) { + if (!handler->allowMultiple) { + for (const auto &project : getProjects()) { + for (const auto &projectContent : project->getContents()) { + if (projectContent->isOpen() && projectContent->getType() == content.getType()) { + storeContent(*projectContent); + projectContent->setOpen(false); + } + } + } + } + + if (handler->type == content.getType()) { + handler->load(content); + content.setOpen(true); + } + } + + } + + Content* ProjectManager::getLoadedContent(const UnlocalizedString& type) { + for (const auto &project : getProjects()) { + for (const auto &projectContent : project->getContents()) { + if (projectContent->isOpen() && projectContent->getType() == type) { + return projectContent.get(); + } + } + } + + return nullptr; + } + + +} \ No newline at end of file diff --git a/plugins/builtin/CMakeLists.txt b/plugins/builtin/CMakeLists.txt index f12797a46..ce7facc81 100644 --- a/plugins/builtin/CMakeLists.txt +++ b/plugins/builtin/CMakeLists.txt @@ -51,6 +51,8 @@ add_imhex_plugin( source/content/helpers/demangle.cpp + source/content/sidebar/project_explorer.cpp + source/content/data_processor_nodes/basic_nodes.cpp source/content/data_processor_nodes/control_nodes.cpp source/content/data_processor_nodes/decode_nodes.cpp diff --git a/plugins/builtin/include/content/views/view_bookmarks.hpp b/plugins/builtin/include/content/views/view_bookmarks.hpp index e1dc48926..d88757a28 100644 --- a/plugins/builtin/include/content/views/view_bookmarks.hpp +++ b/plugins/builtin/include/content/views/view_bookmarks.hpp @@ -32,8 +32,8 @@ namespace hex::plugin::builtin { private: std::string m_currFilter; - PerProvider> m_bookmarks; - PerProvider m_currBookmarkId; + std::list m_bookmarks; + u64 m_currBookmarkId; }; } \ No newline at end of file diff --git a/plugins/builtin/include/content/views/view_data_processor.hpp b/plugins/builtin/include/content/views/view_data_processor.hpp index b269b16fc..d8a938b88 100644 --- a/plugins/builtin/include/content/views/view_data_processor.hpp +++ b/plugins/builtin/include/content/views/view_data_processor.hpp @@ -56,7 +56,7 @@ namespace hex::plugin::builtin { void reloadCustomNodes(); void updateNodePositions(); - std::vector &getWorkspaceStack() { return *m_workspaceStack; } + std::vector &getWorkspaceStack() { return m_workspaceStack; } private: void drawContextMenus(ViewDataProcessor::Workspace &workspace); @@ -76,8 +76,8 @@ namespace hex::plugin::builtin { std::vector m_customNodes; - PerProvider m_mainWorkspace; - PerProvider> m_workspaceStack; + Workspace m_mainWorkspace; + std::vector m_workspaceStack; TaskHolder m_evaluationTask; }; diff --git a/plugins/builtin/romfs/lang/en_US.json b/plugins/builtin/romfs/lang/en_US.json index 820e9a0dc..3c4a849a4 100644 --- a/plugins/builtin/romfs/lang/en_US.json +++ b/plugins/builtin/romfs/lang/en_US.json @@ -399,6 +399,10 @@ "hex.builtin.popup.save_layout.title": "Save Layout", "hex.builtin.popup.save_layout.desc": "Enter a name under which to save the current layout.", "hex.builtin.popup.waiting_for_tasks.desc": "There are still tasks running in the background.\nImHex will close after they are finished.", + "hex.builtin.project.content_type.pattern": "Pattern Source Code", + "hex.builtin.project.content_type.bookmarks": "Bookmarks", + "hex.builtin.project.content_type.data_processor": "Data Processor Nodes", + "hex.builtin.project.free_items": "Free Items", "hex.builtin.provider.rename": "Rename", "hex.builtin.provider.rename.desc": "Enter a name for this provider.", "hex.builtin.provider.tooltip.show_more": "Hold SHIFT for more information", diff --git a/plugins/builtin/source/content/sidebar/project_explorer.cpp b/plugins/builtin/source/content/sidebar/project_explorer.cpp new file mode 100644 index 000000000..f7b211b4b --- /dev/null +++ b/plugins/builtin/source/content/sidebar/project_explorer.cpp @@ -0,0 +1,145 @@ +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +namespace hex::plugin::builtin { + + namespace { + + void drawContent(proj::Content &content) { + static proj::Content *rightClickedContent = nullptr; + static proj::Content *renamingContent = nullptr; + static std::string renameText; + + ImGui::TableNextColumn(); + + if (renamingContent != &content) { + ImGui::Selectable(content.getName().c_str(), content.isOpen(), ImGuiSelectableFlags_SpanAllColumns); + } else { + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2()); + if (ImGui::InputText("##ContentName", renameText)) { + renamingContent->setName(renameText); + } + ImGui::SetKeyboardFocusHere(-1); + ImGui::PopStyleVar(); + + if (ImGui::IsKeyPressed(ImGuiKey_Enter) || ImGui::IsKeyPressed(ImGuiKey_KeypadEnter)) { + renamingContent->setName(renameText); + renamingContent = nullptr; + } + if (ImGui::IsKeyPressed(ImGuiKey_Escape)) { + renamingContent = nullptr; + } + } + + if (ImGui::IsItemHovered()) { + if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left)) { + proj::ProjectManager::loadContent(content); + } + if (ImGui::IsMouseClicked(ImGuiMouseButton_Right)) { + rightClickedContent = &content; + ImGui::OpenPopup("ContentContextMenu"); + } + } else { + if (ImGui::IsMouseClicked(ImGuiMouseButton_Left) && renamingContent == &content) + renamingContent = nullptr; + } + + ImGui::TableNextColumn(); + ImGui::TextUnformatted(Lang(content.getType())); + + if (rightClickedContent == &content) { + if (ImGui::BeginPopup("ContentContextMenu")) { + if (ImGui::MenuItemEx("Open", ICON_VS_OPEN_PREVIEW)) { + proj::ProjectManager::loadContent(content); + } + if (ImGui::MenuItemEx("Rename", ICON_VS_DIFF_RENAMED)) { + renamingContent = &content; + renameText = content.getName(); + } + ImGui::EndPopup(); + } + } + } + + void drawProject(proj::Project &project) { + static proj::Project *rightClickedProject = nullptr; + + bool open = ImGui::TreeNodeEx(project.getName().c_str(), ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_DefaultOpen | ImGuiTreeNodeFlags_SpanAllColumns); + + if (ImGui::IsItemClicked(ImGuiMouseButton_Right)) { + rightClickedProject = &project; + ImGui::OpenPopup("ProjectContextMenu"); + } + if (rightClickedProject == &project) { + if (ImGui::BeginPopup("ProjectContextMenu")) { + if (ImGui::BeginMenuEx("Add", ICON_VS_FILE_ADD)) { + for (const auto &handler : proj::ProjectManager::getContentHandlers()) { + if (ImGui::MenuItem(Lang(handler.type))) { + rightClickedProject->addContent(handler.type); + } + } + ImGui::EndMenu(); + } + + if (ImGui::MenuItemEx("Close", ICON_VS_CLOSE)) { + TaskManager::doLater([project = rightClickedProject] { + proj::ProjectManager::removeProject(*project); + }); + } + ImGui::EndPopup(); + } + } + + if (open) { + for (const auto &content : project.getContents()) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + ImGui::PushID(content.get()); + + drawContent(*content); + + ImGui::PopID(); + } + ImGui::TreePop(); + } + } + + } + + void registerProjectExplorer() { + ContentRegistry::Interface::addSidebarItem(ICON_VS_PROJECT, [] { + if (ImGui::BeginTable("Projects", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY)) { + ImGui::TableSetupColumn("##Icon", ImGuiTableColumnFlags_WidthFixed, 20_scaled); + ImGui::TableSetupColumn("##Name", ImGuiTableColumnFlags_WidthStretch, 20_scaled); + ImGui::TableSetupColumn("##Type", ImGuiTableColumnFlags_WidthFixed, 100_scaled); + for (auto &project : proj::ProjectManager::getProjects()) { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::TableNextColumn(); + + ImGui::PushID(project.get()); + drawProject(*project); + ImGui::PopID(); + + ImGui::NewLine(); + } + ImGui::EndTable(); + } + }); + + proj::ProjectManager::createProject("Project 1"); + proj::ProjectManager::createProject("Project 2"); + proj::ProjectManager::createProject("Free Items"); + } + +} \ No newline at end of file diff --git a/plugins/builtin/source/content/views/view_bookmarks.cpp b/plugins/builtin/source/content/views/view_bookmarks.cpp index 0d22ce3ad..6f2c5327f 100644 --- a/plugins/builtin/source/content/views/view_bookmarks.cpp +++ b/plugins/builtin/source/content/views/view_bookmarks.cpp @@ -12,6 +12,7 @@ #include #include +#include #include @@ -45,16 +46,16 @@ namespace hex::plugin::builtin { bookmarkId }; - m_bookmarks->emplace_back(std::move(bookmark), true); + m_bookmarks.emplace_back(std::move(bookmark), true); ImHexApi::Provider::markDirty(); - EventBookmarkCreated::post(m_bookmarks->back().entry); + EventBookmarkCreated::post(m_bookmarks.back().entry); EventHighlightingChanged::post(); }); RequestRemoveBookmark::subscribe([this](u64 id) { - std::erase_if(m_bookmarks.get(), [id](const auto &bookmark) { + std::erase_if(m_bookmarks, [id](const auto &bookmark) { return bookmark.entry.id == id; }); }); @@ -65,7 +66,7 @@ namespace hex::plugin::builtin { // Check all bookmarks for potential overlaps with the current address std::optional color; - for (const auto &bookmark : *m_bookmarks) { + for (const auto &bookmark : m_bookmarks) { if (!bookmark.highlightVisible) continue; @@ -82,7 +83,7 @@ namespace hex::plugin::builtin { std::ignore = data; // Loop over all bookmarks - for (const auto &[bookmark, highlightVisible] : *m_bookmarks) { + for (const auto &[bookmark, highlightVisible] : m_bookmarks) { if (!highlightVisible) continue; @@ -158,7 +159,7 @@ namespace hex::plugin::builtin { return true; auto data = nlohmann::json::parse(fileContent.begin(), fileContent.end()); - m_bookmarks.get(provider).clear(); + m_bookmarks.clear(); return this->importBookmarks(provider, data); }, .store = [this](prv::Provider *provider, const std::fs::path &basePath, const Tar &tar) -> bool { @@ -170,11 +171,26 @@ namespace hex::plugin::builtin { return result; } }); + proj::ProjectManager::registerContentHandler({ + .type = "hex.builtin.project.content_type.bookmarks", + .load = [this](const proj::Content &content) { + m_bookmarks.clear(); + + const auto data = content.isEmpty() ? nlohmann::json() : nlohmann::json::parse(content.getData()); + this->importBookmarks(ImHexApi::Provider::get(), data); + EventHighlightingChanged::post(); + }, + .store = [this](proj::Content &content) { + nlohmann::json data; + this->exportBookmarks(ImHexApi::Provider::get(), data); + content.setData(data.dump(4)); + } + }); ContentRegistry::Reports::addReportProvider([this](prv::Provider *provider) -> std::string { std::string result; - const auto &bookmarks = m_bookmarks.get(provider); + const auto &bookmarks = m_bookmarks; if (bookmarks.empty()) return ""; @@ -252,7 +268,7 @@ namespace hex::plugin::builtin { void ViewBookmarks::drawDropTarget(std::list::iterator it, float height) { height = std::max(height, 1.0F); - if (it != m_bookmarks->begin()) { + if (it != m_bookmarks.begin()) { ImGui::SetCursorPosY(ImGui::GetCursorPosY() - height); } else { ImGui::SetCursorPosY(ImGui::GetCursorPosY() + height); @@ -261,7 +277,7 @@ namespace hex::plugin::builtin { ImGui::InvisibleButton("##DropTarget", ImVec2(ImGui::GetContentRegionAvail().x, height * 2.0F)); const auto dropTarget = ImRect(ImGui::GetItemRectMin(), ImVec2(ImGui::GetItemRectMax().x, ImGui::GetItemRectMin().y + 2_scaled)); - if (it == m_bookmarks->begin()) { + if (it == m_bookmarks.begin()) { ImGui::SetCursorPosY(ImGui::GetCursorPosY() - height); } @@ -274,13 +290,13 @@ namespace hex::plugin::builtin { u64 droppedBookmarkId = *static_cast(payload->Data); // Find the correct bookmark with that id - auto droppedIter = std::ranges::find_if(m_bookmarks->begin(), m_bookmarks->end(), [droppedBookmarkId](const auto &bookmarkItem) { + auto droppedIter = std::ranges::find_if(m_bookmarks.begin(), m_bookmarks.end(), [droppedBookmarkId](const auto &bookmarkItem) { return bookmarkItem.entry.id == droppedBookmarkId; }); // Swap the two bookmarks - if (droppedIter != m_bookmarks->end()) { - m_bookmarks->splice(it, m_bookmarks, droppedIter); + if (droppedIter != m_bookmarks.end()) { + m_bookmarks.splice(it, m_bookmarks, droppedIter); EventHighlightingChanged::post(); } @@ -298,18 +314,18 @@ namespace hex::plugin::builtin { ImGui::PopItemWidth(); if (ImGui::BeginChild("##bookmarks")) { - if (m_bookmarks->empty()) { + if (m_bookmarks.empty()) { ImGuiExt::TextOverlay("hex.builtin.view.bookmarks.no_bookmarks"_lang, ImGui::GetWindowPos() + ImGui::GetWindowSize() / 2, ImGui::GetWindowWidth() * 0.7); } - auto bookmarkToRemove = m_bookmarks->end(); + auto bookmarkToRemove = m_bookmarks.end(); const auto defaultItemSpacing = ImGui::GetStyle().ItemSpacing.y; ImGui::Dummy({ ImGui::GetContentRegionAvail().x, 0 }); - drawDropTarget(m_bookmarks->begin(), defaultItemSpacing); + drawDropTarget(m_bookmarks.begin(), defaultItemSpacing); // Draw all bookmarks - for (auto it = m_bookmarks->begin(); it != m_bookmarks->end(); ++it) { + for (auto it = m_bookmarks.begin(); it != m_bookmarks.end(); ++it) { auto &[bookmark, highlightVisible] = *it; auto &[region, name, comment, color, locked, bookmarkId] = bookmark; @@ -529,15 +545,15 @@ namespace hex::plugin::builtin { } // Remove the bookmark that was marked for removal - if (bookmarkToRemove != m_bookmarks->end()) { - m_bookmarks->erase(bookmarkToRemove); + if (bookmarkToRemove != m_bookmarks.end()) { + m_bookmarks.erase(bookmarkToRemove); EventHighlightingChanged::post(); } } ImGui::EndChild(); } - bool ViewBookmarks::importBookmarks(prv::Provider *provider, const nlohmann::json &json) { + bool ViewBookmarks::importBookmarks(prv::Provider *, const nlohmann::json &json) { if (!json.contains("bookmarks")) return false; @@ -549,30 +565,32 @@ namespace hex::plugin::builtin { if (!region.contains("address") || !region.contains("size")) continue; - m_bookmarks.get(provider).push_back({ + m_bookmarks.push_back({ { .region = { region["address"], region["size"] }, .name = bookmark["name"], .comment = bookmark["comment"], .color = bookmark["color"], .locked = bookmark["locked"], - .id = bookmark.contains("id") ? bookmark["id"].get() : m_currBookmarkId.get(provider), + .id = bookmark.contains("id") ? bookmark["id"].get() : m_currBookmarkId, }, bookmark.contains("highlightVisible") ? bookmark["highlightVisible"].get() : true, }); if (bookmark.contains("id")) - m_currBookmarkId.get(provider) = std::max(m_currBookmarkId.get(provider), bookmark["id"].get() + 1); + m_currBookmarkId = std::max(m_currBookmarkId, bookmark["id"].get() + 1); else - m_currBookmarkId.get(provider) += 1; + m_currBookmarkId += 1; } + EventHighlightingChanged::post(); + return true; } - bool ViewBookmarks::exportBookmarks(prv::Provider *provider, nlohmann::json &json) { + bool ViewBookmarks::exportBookmarks(prv::Provider *, nlohmann::json &json) { json["bookmarks"] = nlohmann::json::array(); size_t index = 0; - for (const auto &[bookmark, highlightVisible] : m_bookmarks.get(provider)) { + for (const auto &[bookmark, highlightVisible] : m_bookmarks) { json["bookmarks"][index] = { { "name", bookmark.name }, { "comment", bookmark.comment }, @@ -627,7 +645,7 @@ namespace hex::plugin::builtin { wolv::io::File(path, wolv::io::File::Mode::Create).writeString(json.dump(4)); }); }, [this]{ - return ImHexApi::Provider::isValid() && !m_bookmarks->empty(); + return ImHexApi::Provider::isValid() && !m_bookmarks.empty(); }); } diff --git a/plugins/builtin/source/content/views/view_data_processor.cpp b/plugins/builtin/source/content/views/view_data_processor.cpp index daba17cb9..667d5e1cb 100644 --- a/plugins/builtin/source/content/views/view_data_processor.cpp +++ b/plugins/builtin/source/content/views/view_data_processor.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -361,28 +362,39 @@ namespace hex::plugin::builtin { ProjectFile::registerPerProviderHandler({ .basePath = "data_processor.json", .required = false, - .load = [this](prv::Provider *provider, const std::fs::path &basePath, const Tar &tar) { + .load = [this](prv::Provider *, const std::fs::path &basePath, const Tar &tar) { std::string save = tar.readString(basePath); - ViewDataProcessor::loadNodes(m_mainWorkspace.get(provider), nlohmann::json::parse(save)); + this->loadNodes(m_mainWorkspace, nlohmann::json::parse(save)); m_updateNodePositions = true; return true; }, - .store = [this](prv::Provider *provider, const std::fs::path &basePath, const Tar &tar) { - tar.writeString(basePath, ViewDataProcessor::saveNodes(m_mainWorkspace.get(provider)).dump(4)); + .store = [this](prv::Provider *, const std::fs::path &basePath, const Tar &tar) { + tar.writeString(basePath, this->saveNodes(m_mainWorkspace).dump(4)); return true; } }); + proj::ProjectManager::registerContentHandler({ + .type = "hex.builtin.project.content_type.data_processor", + .load = [this](const proj::Content &content) { + const nlohmann::json data = content.isEmpty() ? nlohmann::json() : nlohmann::json::parse(content.getData()); + this->loadNodes(m_mainWorkspace, data); + m_updateNodePositions = true; + }, + .store = [this](proj::Content &content) { + content.setData(this->saveNodes(m_mainWorkspace).dump(4)); + } + }); - EventProviderOpened::subscribe(this, [this](auto *provider) { - m_mainWorkspace.get(provider) = { }; - m_workspaceStack.get(provider).push_back(&m_mainWorkspace.get(provider)); + EventProviderOpened::subscribe(this, [this](auto *) { + m_mainWorkspace = { }; + m_workspaceStack.push_back(&m_mainWorkspace); }); EventProviderChanged::subscribe(this, [this](const auto *, const auto *) { - for (auto *workspace : *m_workspaceStack) { + for (auto *workspace : m_workspaceStack) { for (auto &node : workspace->nodes) { node->setCurrentOverlay(nullptr); } @@ -399,7 +411,7 @@ namespace hex::plugin::builtin { [&](const std::fs::path &path) { wolv::io::File file(path, wolv::io::File::Mode::Read); if (file.isValid()) { - ViewDataProcessor::loadNodes(*m_mainWorkspace, nlohmann::json::parse(file.readString())); + ViewDataProcessor::loadNodes(m_mainWorkspace, nlohmann::json::parse(file.readString())); m_updateNodePositions = true; } }); @@ -411,17 +423,17 @@ namespace hex::plugin::builtin { [&, this](const std::fs::path &path) { wolv::io::File file(path, wolv::io::File::Mode::Create); if (file.isValid()) - file.writeString(ViewDataProcessor::saveNodes(*m_mainWorkspace).dump(4)); + file.writeString(ViewDataProcessor::saveNodes(m_mainWorkspace).dump(4)); }); }, [this]{ - return !m_workspaceStack->empty() && !m_workspaceStack->back()->nodes.empty() && ImHexApi::Provider::isValid(); + return !m_workspaceStack.empty() && !m_workspaceStack.back()->nodes.empty() && ImHexApi::Provider::isValid(); }); ContentRegistry::FileHandler::add({ ".hexnode" }, [this](const auto &path) { wolv::io::File file(path, wolv::io::File::Mode::Read); if (!file.isValid()) return false; - ViewDataProcessor::loadNodes(*m_mainWorkspace, file.readString()); + ViewDataProcessor::loadNodes(m_mainWorkspace, file.readString()); m_updateNodePositions = true; return true; @@ -884,7 +896,7 @@ namespace hex::plugin::builtin { } void ViewDataProcessor::drawContent() { - auto &workspace = *m_workspaceStack->back(); + auto &workspace = *m_workspaceStack.back(); ImGui::BeginDisabled(m_evaluationTask.isRunning()); @@ -962,7 +974,7 @@ namespace hex::plugin::builtin { ImGuiExt::TextFormattedCentered("{}", "hex.builtin.view.data_processor.help_text"_lang); // Draw a close button if there is more than one workspace on the stack - if (m_workspaceStack->size() > 1) { + if (m_workspaceStack.size() > 1) { ImGui::SetCursorPos(ImVec2(ImGui::GetContentRegionAvail().x - ImGui::GetTextLineHeightWithSpacing() * 1.5F, ImGui::GetTextLineHeightWithSpacing() * 0.2F)); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0F, 4.0F)); if (ImGuiExt::DimmedIconButton(ICON_VS_CLOSE, ImGuiExt::GetCustomColorVec4(ImGuiCustomCol_ToolbarRed))) { @@ -1079,7 +1091,7 @@ namespace hex::plugin::builtin { // Remove the top-most workspace from the stack if requested if (popWorkspace) { - m_workspaceStack->pop_back(); + m_workspaceStack.pop_back(); m_updateNodePositions = true; } } diff --git a/plugins/builtin/source/content/views/view_pattern_editor.cpp b/plugins/builtin/source/content/views/view_pattern_editor.cpp index d85a4c7f2..6be74be0e 100644 --- a/plugins/builtin/source/content/views/view_pattern_editor.cpp +++ b/plugins/builtin/source/content/views/view_pattern_editor.cpp @@ -36,6 +36,8 @@ #include #include +#include +#include #include namespace hex::plugin::builtin { @@ -333,9 +335,9 @@ namespace hex::plugin::builtin { const auto availableSize = g.CurrentWindow->Size; const auto windowPosition = ImGui::GetCursorScreenPos(); auto textEditorSize = availableSize; - textEditorSize.y *= 3.5 / 5.0; + textEditorSize.y *= 3.5F / 5.0F; textEditorSize.y -= ImGui::GetTextLineHeightWithSpacing(); - textEditorSize.y = std::clamp(textEditorSize.y + height,200.0F, availableSize.y-200.0F); + textEditorSize.y = std::clamp(textEditorSize.y + height, 200.0F, availableSize.y - 200.0F); if (g.NavWindow != nullptr) { std::string name = g.NavWindow->Name; @@ -1425,7 +1427,7 @@ namespace hex::plugin::builtin { const auto &currScope = evaluator->getScope(-m_debuggerScopeIndex); if (ImGui::BeginCombo("##scope", displayValue(currScope.parent, m_debuggerScopeIndex).c_str())) { for (size_t i = 0; i < evaluator->getScopeCount(); i++) { - auto &scope = evaluator->getScope(-i); + auto &scope = evaluator->getScope(-i32(i)); if (ImGui::Selectable(displayValue(scope.parent, i).c_str(), i == size_t(m_debuggerScopeIndex))) { m_debuggerScopeIndex = i; @@ -2299,6 +2301,16 @@ namespace hex::plugin::builtin { } }); + proj::ProjectManager::registerContentHandler({ + .type = "hex.builtin.project.content_type.pattern", + .load = [this](const proj::Content &content) { + m_textEditor.SetText(content.getData()); + }, + .store = [this](proj::Content &content) { + content.setData(wolv::util::trim(m_textEditor.GetText())); + } + }); + ShortcutManager::addShortcut(this, CTRLCMD + Keys::G + AllowWhileTyping, "hex.builtin.view.pattern_editor.shortcut.goto_line", [this] { m_openGotoLinePopUp = true; }); diff --git a/plugins/builtin/source/plugin_builtin.cpp b/plugins/builtin/source/plugin_builtin.cpp index 78fc4b8ac..485e1b84f 100644 --- a/plugins/builtin/source/plugin_builtin.cpp +++ b/plugins/builtin/source/plugin_builtin.cpp @@ -45,6 +45,7 @@ namespace hex::plugin::builtin { void registerReportGenerators(); void registerTutorials(); void registerDataInformationSections(); + void registerProjectExplorer(); void loadWorkspaces(); void addWindowDecoration(); @@ -140,6 +141,7 @@ IMHEX_PLUGIN_SETUP("Built-in", "WerWolv", "Default ImHex functionality") { registerReportGenerators(); registerTutorials(); registerDataInformationSections(); + registerProjectExplorer(); loadWorkspaces(); addWindowDecoration(); createWelcomeScreen();