// SPDX-License-Identifier: LGPL-2.1-or-later /**************************************************************************** * * * Copyright (c) 2025 Kindred Systems * * * * This file is part of FreeCAD. * * * * FreeCAD is free software: you can redistribute it and/or modify it * * under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation, either version 2.1 of the * * License, or (at your option) any later version. * * * * FreeCAD is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with FreeCAD. If not, see * * . * * * ***************************************************************************/ #include "SDKRegistry.h" #include "IPanelProvider.h" #include #include #include #include #include namespace KCSDK { // -- Helpers: std ↔ Qt conversion (internal) -------------------------------- namespace { QString toQString(const std::string& s) { return QString::fromUtf8(s.data(), static_cast(s.size())); } std::string fromQString(const QString& s) { QByteArray utf8 = s.toUtf8(); return std::string(utf8.constData(), utf8.size()); } QStringList toQStringList(const std::vector& v) { QStringList out; out.reserve(static_cast(v.size())); for (const auto& s : v) { out.append(toQString(s)); } return out; } std::vector fromQStringList(const QStringList& list) { std::vector out; out.reserve(list.size()); for (const auto& s : list) { out.push_back(fromQString(s)); } return out; } } // anonymous namespace // -- Singleton -------------------------------------------------------------- SDKRegistry& SDKRegistry::instance() { static SDKRegistry reg; return reg; } SDKRegistry::SDKRegistry() { Base::Console().log("KCSDK: registry initialized (API v%d)\n", API_VERSION_MAJOR); } SDKRegistry::~SDKRegistry() = default; std::vector SDKRegistry::available() const { std::lock_guard lock(mutex_); std::vector result; result.reserve(panels_.size()); for (const auto& [id, _] : panels_) { result.push_back(id); } return result; } // -- Editing context API ---------------------------------------------------- void SDKRegistry::registerContext(const ContextDef& def) { Gui::ContextDefinition guiDef; guiDef.id = toQString(def.id); guiDef.labelTemplate = toQString(def.labelTemplate); guiDef.color = toQString(def.color); guiDef.toolbars = toQStringList(def.toolbars); guiDef.priority = def.priority; guiDef.match = def.match; Gui::EditingContextResolver::instance()->registerContext(guiDef); } void SDKRegistry::unregisterContext(const std::string& id) { Gui::EditingContextResolver::instance()->unregisterContext(toQString(id)); } void SDKRegistry::registerOverlay(const OverlayDef& def) { Gui::OverlayDefinition guiDef; guiDef.id = toQString(def.id); guiDef.toolbars = toQStringList(def.toolbars); guiDef.match = def.match; Gui::EditingContextResolver::instance()->registerOverlay(guiDef); } void SDKRegistry::unregisterOverlay(const std::string& id) { Gui::EditingContextResolver::instance()->unregisterOverlay(toQString(id)); } void SDKRegistry::injectCommands(const std::string& contextId, const std::string& toolbarName, const std::vector& commands) { Gui::EditingContextResolver::instance()->injectCommands( toQString(contextId), toQString(toolbarName), toQStringList(commands)); } ContextSnapshot SDKRegistry::currentContext() const { Gui::EditingContext ctx = Gui::EditingContextResolver::instance()->currentContext(); ContextSnapshot snap; snap.id = fromQString(ctx.id); snap.label = fromQString(ctx.label); snap.color = fromQString(ctx.color); snap.toolbars = fromQStringList(ctx.toolbars); snap.breadcrumb = fromQStringList(ctx.breadcrumb); snap.breadcrumbColors = fromQStringList(ctx.breadcrumbColors); return snap; } void SDKRegistry::refresh() { Gui::EditingContextResolver::instance()->refresh(); } // -- Panel provider API ----------------------------------------------------- void SDKRegistry::registerPanel(std::unique_ptr provider) { if (!provider) { return; } std::string id = provider->id(); std::lock_guard lock(mutex_); panels_[id] = std::move(provider); Base::Console().log("KCSDK: registered panel provider '%s'\n", id.c_str()); } void SDKRegistry::unregisterPanel(const std::string& id) { std::lock_guard lock(mutex_); auto it = panels_.find(id); if (it == panels_.end()) { return; } // Remove the dock widget if it was created. auto* dwm = Gui::DockWindowManager::instance(); if (dwm) { dwm->removeDockWindow(id.c_str()); } panels_.erase(it); Base::Console().log("KCSDK: unregistered panel provider '%s'\n", id.c_str()); } void SDKRegistry::createPanel(const std::string& id) { std::lock_guard lock(mutex_); auto it = panels_.find(id); if (it == panels_.end()) { Base::Console().warning("KCSDK: no panel provider '%s' registered\n", id.c_str()); return; } auto* dwm = Gui::DockWindowManager::instance(); if (!dwm) { return; } // Skip if already created. if (dwm->getDockWindow(id.c_str())) { return; } IPanelProvider* provider = it->second.get(); QWidget* widget = provider->create_widget(); if (!widget) { Base::Console().warning("KCSDK: panel '%s' create_widget() returned null\n", id.c_str()); return; } auto qtArea = static_cast(provider->preferred_area()); dwm->addDockWindow(id.c_str(), widget, qtArea); } void SDKRegistry::createAllPanels() { // Collect IDs under lock, then create outside to avoid recursive locking. std::vector ids; { std::lock_guard lock(mutex_); ids.reserve(panels_.size()); for (const auto& [id, _] : panels_) { ids.push_back(id); } } for (const auto& id : ids) { createPanel(id); } } std::vector SDKRegistry::registeredPanels() const { std::lock_guard lock(mutex_); std::vector result; result.reserve(panels_.size()); for (const auto& [id, _] : panels_) { result.push_back(id); } return result; } } // namespace KCSDK