diff options
author | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2021-02-03 21:33:29 +0900 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2021-02-10 14:16:20 +0100 |
commit | 570df33b75298b7c7c3e7afd233d36d0cdf7e0a3 (patch) | |
tree | c4998b9d544a6eb925af1db1fb07731b2efcbeae /svx/source | |
parent | e71350e037dcd49f24c939395ab6436051382c83 (diff) |
devtools: handle object inspector with attached objects
This changes the object inspector tree view to create child nodes
on demand (when expanded) and attach specialized node objects to
the node, which are responsible to get the specific introspection
information for the current inspected UNO object.
Change-Id: Ib8431cc2a2c6d6d3ac7fd9db5bc61c9071d5e8f8
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/110462
Tested-by: Tomaž Vajngerl <quikee@gmail.com>
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Diffstat (limited to 'svx/source')
-rw-r--r-- | svx/source/devtools/DevelopmentToolDockingWindow.cxx | 471 |
1 files changed, 318 insertions, 153 deletions
diff --git a/svx/source/devtools/DevelopmentToolDockingWindow.cxx b/svx/source/devtools/DevelopmentToolDockingWindow.cxx index da3b8d5896b5..8c519e807125 100644 --- a/svx/source/devtools/DevelopmentToolDockingWindow.cxx +++ b/svx/source/devtools/DevelopmentToolDockingWindow.cxx @@ -100,105 +100,6 @@ private: SelectionChangeHandler& operator=(const SelectionChangeHandler&) = delete; }; -} // end anonymous namespace - -DevelopmentToolDockingWindow::DevelopmentToolDockingWindow(SfxBindings* pInputBindings, - SfxChildWindow* pChildWindow, - vcl::Window* pParent) - : SfxDockingWindow(pInputBindings, pChildWindow, pParent, "DevelopmentTool", - "svx/ui/developmenttool.ui") - , mpClassNameLabel(m_xBuilder->weld_label("class_name_value_id")) - , mpClassListBox(m_xBuilder->weld_tree_view("class_listbox_id")) - , mpLeftSideTreeView(m_xBuilder->weld_tree_view("leftside_treeview_id")) - , mpSelectionToggle(m_xBuilder->weld_toggle_button("selection_toggle")) - , maDocumentModelTreeHandler( - mpLeftSideTreeView, - pInputBindings->GetDispatcher()->GetFrame()->GetObjectShell()->GetBaseModel()) -{ - mpLeftSideTreeView->connect_changed(LINK(this, DevelopmentToolDockingWindow, LeftSideSelected)); - mpSelectionToggle->connect_toggled(LINK(this, DevelopmentToolDockingWindow, SelectionToggled)); - - auto* pViewFrame = pInputBindings->GetDispatcher()->GetFrame(); - - uno::Reference<frame::XController> xController = pViewFrame->GetFrame().GetController(); - - mxRoot = pInputBindings->GetDispatcher()->GetFrame()->GetObjectShell()->GetBaseModel(); - - introspect(mxRoot); - maDocumentModelTreeHandler.inspectDocument(); - mxSelectionListener.set(new SelectionChangeHandler(xController, this)); -} - -IMPL_LINK(DevelopmentToolDockingWindow, LeftSideSelected, weld::TreeView&, rView, void) -{ - if (mpSelectionToggle->get_state() == TRISTATE_TRUE) - return; - - OUString sID = rView.get_selected_id(); - auto xObject = DocumentModelTreeHandler::getObjectByID(sID); - if (xObject.is()) - introspect(xObject); -} - -IMPL_LINK_NOARG(DevelopmentToolDockingWindow, SelectionToggled, weld::ToggleButton&, void) -{ - updateSelection(); -} - -DevelopmentToolDockingWindow::~DevelopmentToolDockingWindow() { disposeOnce(); } - -void DevelopmentToolDockingWindow::dispose() -{ - auto* pSelectionChangeHandler - = dynamic_cast<SelectionChangeHandler*>(mxSelectionListener.get()); - if (pSelectionChangeHandler) - pSelectionChangeHandler->disconnect(); - - mxSelectionListener = uno::Reference<view::XSelectionChangeListener>(); - maDocumentModelTreeHandler.dispose(); - - mpClassNameLabel.reset(); - mpClassListBox.reset(); - mpSelectionToggle.reset(); - mpLeftSideTreeView.reset(); - - SfxDockingWindow::dispose(); -} - -void DevelopmentToolDockingWindow::updateSelection() -{ - TriState eTriState = mpSelectionToggle->get_state(); - if (eTriState == TRISTATE_TRUE) - { - introspect(mxCurrentSelection); - mpLeftSideTreeView->set_sensitive(false); - } - else - { - mpLeftSideTreeView->set_sensitive(true); - LeftSideSelected(*mpLeftSideTreeView); - } -} - -void DevelopmentToolDockingWindow::ToggleFloatingMode() -{ - SfxDockingWindow::ToggleFloatingMode(); - - if (GetFloatingWindow()) - GetFloatingWindow()->SetMinOutputSizePixel(Size(300, 300)); - - Invalidate(); -} - -void DevelopmentToolDockingWindow::selectionChanged( - uno::Reference<uno::XInterface> const& xInterface) -{ - mxCurrentSelection = xInterface; - updateSelection(); -} - -namespace -{ uno::Reference<reflection::XIdlClass> TypeToIdlClass(const uno::Type& rType, const uno::Reference<uno::XComponentContext>& xContext) { @@ -345,60 +246,133 @@ OUString AnyToString(const uno::Any& aValue, const uno::Reference<uno::XComponen } return aRetStr; } -} -void DevelopmentToolDockingWindow::introspect(uno::Reference<uno::XInterface> const& xInterface) +// Object inspector nodes + +class ObjectInspectorNode { - if (!xInterface.is()) - return; +public: + css::uno::Reference<css::uno::XInterface> mxObject; - uno::Reference<uno::XComponentContext> xContext = comphelper::getProcessComponentContext(); - if (!xContext.is()) - return; + ObjectInspectorNode() = default; - auto xServiceInfo = uno::Reference<lang::XServiceInfo>(xInterface, uno::UNO_QUERY); - OUString aImplementationName = xServiceInfo->getImplementationName(); + ObjectInspectorNode(css::uno::Reference<css::uno::XInterface> const& xObject) + : mxObject(xObject) + { + } - mpClassNameLabel->set_label(aImplementationName); + virtual ~ObjectInspectorNode() {} - mpClassListBox->freeze(); - mpClassListBox->clear(); + virtual OUString getObjectName() = 0; + + virtual void fillChildren(std::unique_ptr<weld::TreeView>& rTree, weld::TreeIter const& rParent) + = 0; +}; - std::unique_ptr<weld::TreeIter> pParent = mpClassListBox->make_iterator(); - OUString aServicesString("Services"); - mpClassListBox->insert(nullptr, -1, &aServicesString, nullptr, nullptr, nullptr, false, - pParent.get()); - mpClassListBox->set_text_emphasis(*pParent, true, 0); +OUString lclAppendNode(std::unique_ptr<weld::TreeView>& pTree, ObjectInspectorNode* pEntry, + bool bChildrenOnDemand = false) +{ + OUString sName = pEntry->getObjectName(); + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pEntry))); + std::unique_ptr<weld::TreeIter> pCurrent = pTree->make_iterator(); + pTree->insert(nullptr, -1, &sName, &sId, nullptr, nullptr, bChildrenOnDemand, pCurrent.get()); + pTree->set_text_emphasis(*pCurrent, true, 0); + return sId; +} - std::unique_ptr<weld::TreeIter> pResult = mpClassListBox->make_iterator(); - const uno::Sequence<OUString> aServiceNames(xServiceInfo->getSupportedServiceNames()); - for (auto const& aServiceName : aServiceNames) +OUString lclAppendNodeToParent(std::unique_ptr<weld::TreeView>& pTree, + weld::TreeIter const& rParent, ObjectInspectorNode* pEntry, + bool bChildrenOnDemand = false) +{ + OUString sName = pEntry->getObjectName(); + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pEntry))); + std::unique_ptr<weld::TreeIter> pCurrent = pTree->make_iterator(); + pTree->insert(&rParent, -1, &sName, &sId, nullptr, nullptr, bChildrenOnDemand, nullptr); + pTree->set_text_emphasis(*pCurrent, true, 0); + return sId; +} + +OUString lclAppendNodeWithIterToParent(std::unique_ptr<weld::TreeView>& pTree, + weld::TreeIter const& rParent, weld::TreeIter& rCurrent, + ObjectInspectorNode* pEntry, bool bChildrenOnDemand = false) +{ + OUString sName = pEntry->getObjectName(); + OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pEntry))); + pTree->insert(&rParent, -1, &sName, &sId, nullptr, nullptr, bChildrenOnDemand, &rCurrent); + pTree->set_text_emphasis(rCurrent, true, 0); + return sId; +} + +class ObjectInspectorNamedNode : public ObjectInspectorNode +{ +public: + OUString msName; + + ObjectInspectorNamedNode(OUString const& rName, + css::uno::Reference<css::uno::XInterface> const& xObject) + : ObjectInspectorNode(xObject) + , msName(rName) { - mpClassListBox->insert(pParent.get(), -1, &aServiceName, nullptr, nullptr, nullptr, false, - pResult.get()); - mpClassListBox->set_text_emphasis(*pResult, false, 0); } - uno::Reference<beans::XIntrospection> xIntrospection; - xIntrospection = beans::theIntrospection::get(xContext); + OUString getObjectName() override { return msName; } - uno::Reference<beans::XIntrospectionAccess> xIntrospectionAccess; - xIntrospectionAccess = xIntrospection->inspect(uno::makeAny(xInterface)); + void fillChildren(std::unique_ptr<weld::TreeView>& /*rTree*/, + weld::TreeIter const& /*rParent*/) override { - OUString aPropertiesString("Properties"); + } +}; - mpClassListBox->insert(nullptr, -1, &aPropertiesString, nullptr, nullptr, nullptr, false, - pParent.get()); - mpClassListBox->set_text_emphasis(*pParent, true, 0); +class ServicesNode : public ObjectInspectorNamedNode +{ +public: + ServicesNode(css::uno::Reference<css::uno::XInterface> const& xObject) + : ObjectInspectorNamedNode("Services", xObject) + { + } + + void fillChildren(std::unique_ptr<weld::TreeView>& pTree, + weld::TreeIter const& rParent) override + { + auto xServiceInfo = uno::Reference<lang::XServiceInfo>(mxObject, uno::UNO_QUERY); + const uno::Sequence<OUString> aServiceNames(xServiceInfo->getSupportedServiceNames()); + for (auto const& aServiceName : aServiceNames) + { + lclAppendNodeToParent(pTree, rParent, + new ObjectInspectorNamedNode(aServiceName, mxObject)); + } + } +}; - uno::Reference<beans::XPropertySet> xPropertySet(xInterface, uno::UNO_QUERY); +class PropertiesNode : public ObjectInspectorNamedNode +{ +public: + uno::Reference<uno::XComponentContext> mxContext; + + PropertiesNode(css::uno::Reference<css::uno::XInterface> const& xObject, + uno::Reference<uno::XComponentContext> const& xContext) + : ObjectInspectorNamedNode("Properties", xObject) + , mxContext(xContext) + { + } + + void fillChildren(std::unique_ptr<weld::TreeView>& pTree, + weld::TreeIter const& rParent) override + { + uno::Reference<beans::XIntrospection> xIntrospection + = beans::theIntrospection::get(mxContext); + auto xIntrospectionAccess = xIntrospection->inspect(uno::makeAny(mxObject)); const auto xProperties = xIntrospectionAccess->getProperties( beans::PropertyConcept::ALL - beans::PropertyConcept::DANGEROUS); + + uno::Reference<beans::XPropertySet> xPropertySet(mxObject, uno::UNO_QUERY); + for (auto const& xProperty : xProperties) { - mpClassListBox->insert(pParent.get(), -1, &xProperty.Name, nullptr, nullptr, nullptr, - false, pResult.get()); + std::unique_ptr<weld::TreeIter> pCurrent = pTree->make_iterator(); + lclAppendNodeWithIterToParent(pTree, rParent, *pCurrent, + new ObjectInspectorNamedNode(xProperty.Name, mxObject)); if (xPropertySet.is()) { @@ -406,50 +380,241 @@ void DevelopmentToolDockingWindow::introspect(uno::Reference<uno::XInterface> co try { uno::Any aAny = xPropertySet->getPropertyValue(xProperty.Name); - aValue = AnyToString(aAny, xContext); + aValue = AnyToString(aAny, mxContext); } catch (const beans::UnknownPropertyException&) { aValue = "UnknownPropertyException"; } - mpClassListBox->set_text(*pResult, aValue, 1); + pTree->set_text(*pCurrent, aValue, 1); } } } +}; + +class InterfacesNode : public ObjectInspectorNamedNode +{ +public: + InterfacesNode(css::uno::Reference<css::uno::XInterface> const& xObject) + : ObjectInspectorNamedNode("Interfaces", xObject) + { + } + void fillChildren(std::unique_ptr<weld::TreeView>& pTree, + weld::TreeIter const& rParent) override { - uno::Reference<lang::XTypeProvider> xTypeProvider(xInterface, uno::UNO_QUERY); + uno::Reference<lang::XTypeProvider> xTypeProvider(mxObject, uno::UNO_QUERY); if (xTypeProvider.is()) { - OUString aTypesString("Interfaces"); - mpClassListBox->insert(nullptr, -1, &aTypesString, nullptr, nullptr, nullptr, false, - pParent.get()); - mpClassListBox->set_text_emphasis(*pParent, true, 0); - const auto xSequenceTypes = xTypeProvider->getTypes(); for (auto const& xType : xSequenceTypes) { OUString aName = xType.getTypeName(); - mpClassListBox->insert(pParent.get(), -1, &aName, nullptr, nullptr, nullptr, false, - pResult.get()); + lclAppendNodeToParent(pTree, rParent, + new ObjectInspectorNamedNode(aName, mxObject)); } } } +}; + +class MethodsNode : public ObjectInspectorNamedNode +{ +public: + uno::Reference<uno::XComponentContext> mxContext; + + MethodsNode(css::uno::Reference<css::uno::XInterface> const& xObject, + uno::Reference<uno::XComponentContext> const& xContext) + : ObjectInspectorNamedNode("Methods", xObject) + , mxContext(xContext) + { + } + + void fillChildren(std::unique_ptr<weld::TreeView>& pTree, + weld::TreeIter const& rParent) override + { + uno::Reference<beans::XIntrospection> xIntrospection + = beans::theIntrospection::get(mxContext); + auto xIntrospectionAccess = xIntrospection->inspect(uno::makeAny(mxObject)); + + const auto xMethods = xIntrospectionAccess->getMethods(beans::MethodConcept::ALL); + for (auto const& xMethod : xMethods) + { + OUString aMethodName = xMethod->getName(); + + lclAppendNodeToParent(pTree, rParent, + new ObjectInspectorNamedNode(aMethodName, mxObject)); + } + } +}; + +} // end anonymous namespace + +DevelopmentToolDockingWindow::DevelopmentToolDockingWindow(SfxBindings* pInputBindings, + SfxChildWindow* pChildWindow, + vcl::Window* pParent) + : SfxDockingWindow(pInputBindings, pChildWindow, pParent, "DevelopmentTool", + "svx/ui/developmenttool.ui") + , mpClassNameLabel(m_xBuilder->weld_label("class_name_value_id")) + , mpClassListBox(m_xBuilder->weld_tree_view("class_listbox_id")) + , mpLeftSideTreeView(m_xBuilder->weld_tree_view("leftside_treeview_id")) + , mpSelectionToggle(m_xBuilder->weld_toggle_button("selection_toggle")) + , maDocumentModelTreeHandler( + mpLeftSideTreeView, + pInputBindings->GetDispatcher()->GetFrame()->GetObjectShell()->GetBaseModel()) +{ + mpLeftSideTreeView->connect_changed(LINK(this, DevelopmentToolDockingWindow, LeftSideSelected)); + mpSelectionToggle->connect_toggled(LINK(this, DevelopmentToolDockingWindow, SelectionToggled)); + mpClassListBox->connect_expanding( + LINK(this, DevelopmentToolDockingWindow, ObjectInspectorExpandingHandler)); + + auto* pViewFrame = pInputBindings->GetDispatcher()->GetFrame(); + + uno::Reference<frame::XController> xController = pViewFrame->GetFrame().GetController(); + + mxRoot = pInputBindings->GetDispatcher()->GetFrame()->GetObjectShell()->GetBaseModel(); + + introspect(mxRoot); + maDocumentModelTreeHandler.inspectDocument(); + mxSelectionListener.set(new SelectionChangeHandler(xController, this)); +} + +IMPL_LINK(DevelopmentToolDockingWindow, LeftSideSelected, weld::TreeView&, rView, void) +{ + if (mpSelectionToggle->get_state() == TRISTATE_TRUE) + return; + + OUString sID = rView.get_selected_id(); + auto xObject = DocumentModelTreeHandler::getObjectByID(sID); + if (xObject.is()) + introspect(xObject); +} + +IMPL_LINK_NOARG(DevelopmentToolDockingWindow, SelectionToggled, weld::ToggleButton&, void) +{ + updateSelection(); +} + +void DevelopmentToolDockingWindow::clearObjectInspectorChildren(weld::TreeIter const& rParent) +{ + bool bChild = false; + do + { + bChild = mpClassListBox->iter_has_child(rParent); + if (bChild) + { + std::unique_ptr<weld::TreeIter> pChild = mpClassListBox->make_iterator(&rParent); + bChild = mpClassListBox->iter_children(*pChild); + if (bChild) + { + clearObjectInspectorChildren(*pChild); + OUString sID = mpClassListBox->get_id(*pChild); + auto* pEntry = reinterpret_cast<ObjectInspectorNode*>(sID.toInt64()); + delete pEntry; + mpClassListBox->remove(*pChild); + } + } + } while (bChild); +} - OUString aMethodsString("Methods"); - mpClassListBox->insert(nullptr, -1, &aMethodsString, nullptr, nullptr, nullptr, false, - pParent.get()); - mpClassListBox->set_text_emphasis(*pParent, true, 0); +DevelopmentToolDockingWindow::~DevelopmentToolDockingWindow() { disposeOnce(); } + +void DevelopmentToolDockingWindow::dispose() +{ + auto* pSelectionChangeHandler + = dynamic_cast<SelectionChangeHandler*>(mxSelectionListener.get()); + if (pSelectionChangeHandler) + pSelectionChangeHandler->disconnect(); + + mxSelectionListener = uno::Reference<view::XSelectionChangeListener>(); + maDocumentModelTreeHandler.dispose(); + + // destroy all ObjectInspectorNodes from the tree + mpClassListBox->all_foreach([this](weld::TreeIter& rEntry) { + OUString sID = mpClassListBox->get_id(rEntry); + auto* pEntry = reinterpret_cast<ObjectInspectorNode*>(sID.toInt64()); + delete pEntry; + return false; + }); + + // dispose welded objects + mpClassNameLabel.reset(); + mpClassListBox.reset(); + mpSelectionToggle.reset(); + mpLeftSideTreeView.reset(); - const auto xMethods = xIntrospectionAccess->getMethods(beans::MethodConcept::ALL); - for (auto const& xMethod : xMethods) + SfxDockingWindow::dispose(); +} + +void DevelopmentToolDockingWindow::updateSelection() +{ + TriState eTriState = mpSelectionToggle->get_state(); + if (eTriState == TRISTATE_TRUE) + { + introspect(mxCurrentSelection); + mpLeftSideTreeView->set_sensitive(false); + } + else { - OUString aMethodName = xMethod->getName(); - mpClassListBox->insert(pParent.get(), -1, &aMethodName, nullptr, nullptr, nullptr, false, - pResult.get()); - mpClassListBox->set_text_emphasis(*pResult, false, 0); + mpLeftSideTreeView->set_sensitive(true); + LeftSideSelected(*mpLeftSideTreeView); } +} + +void DevelopmentToolDockingWindow::ToggleFloatingMode() +{ + SfxDockingWindow::ToggleFloatingMode(); + + if (GetFloatingWindow()) + GetFloatingWindow()->SetMinOutputSizePixel(Size(300, 300)); + + Invalidate(); +} + +IMPL_LINK(DevelopmentToolDockingWindow, ObjectInspectorExpandingHandler, weld::TreeIter const&, + rParent, bool) +{ + OUString sID = mpClassListBox->get_id(rParent); + if (sID.isEmpty()) + return true; + + clearObjectInspectorChildren(rParent); + auto* pNode = reinterpret_cast<ObjectInspectorNode*>(sID.toInt64()); + pNode->fillChildren(mpClassListBox, rParent); + + return true; +} + +void DevelopmentToolDockingWindow::selectionChanged( + uno::Reference<uno::XInterface> const& xInterface) +{ + mxCurrentSelection = xInterface; + updateSelection(); +} + +void DevelopmentToolDockingWindow::introspect(uno::Reference<uno::XInterface> const& xInterface) +{ + if (!xInterface.is()) + return; + + uno::Reference<uno::XComponentContext> xContext = comphelper::getProcessComponentContext(); + if (!xContext.is()) + return; + + // Set implementation name + auto xServiceInfo = uno::Reference<lang::XServiceInfo>(xInterface, uno::UNO_QUERY); + OUString aImplementationName = xServiceInfo->getImplementationName(); + mpClassNameLabel->set_label(aImplementationName); + + // fill object inspector + + mpClassListBox->freeze(); + mpClassListBox->clear(); + + lclAppendNode(mpClassListBox, new ServicesNode(xInterface), true); + lclAppendNode(mpClassListBox, new InterfacesNode(xInterface), true); + lclAppendNode(mpClassListBox, new PropertiesNode(xInterface, xContext), true); + lclAppendNode(mpClassListBox, new MethodsNode(xInterface, xContext), true); mpClassListBox->thaw(); } |