summaryrefslogtreecommitdiffstats
path: root/sfx2/source/devtools
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sfx2/source/devtools
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sfx2/source/devtools')
-rw-r--r--sfx2/source/devtools/DevToolsStrings.hrc73
-rw-r--r--sfx2/source/devtools/DevelopmentToolChildWindow.cxx30
-rw-r--r--sfx2/source/devtools/DevelopmentToolDockingWindow.cxx155
-rw-r--r--sfx2/source/devtools/DocumentModelTreeHandler.cxx840
-rw-r--r--sfx2/source/devtools/ObjectInspectorTreeHandler.cxx1384
-rw-r--r--sfx2/source/devtools/SelectionChangeHandler.hxx74
6 files changed, 2556 insertions, 0 deletions
diff --git a/sfx2/source/devtools/DevToolsStrings.hrc b/sfx2/source/devtools/DevToolsStrings.hrc
new file mode 100644
index 000000000..4c3f3e9ce
--- /dev/null
+++ b/sfx2/source/devtools/DevToolsStrings.hrc
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#define NC_(Context, String) TranslateId(Context, reinterpret_cast<char const *>(u8##String))
+
+#define STR_TEXT_PORTION NC_("STR_TEXT_PORTION", "Text Portion %1")
+#define STR_PARAGRAPH NC_("STR_PARAGRAPH", "Paragraph %1")
+#define STR_SHAPE NC_("STR_SHAPE", "Shape %1")
+#define STR_PAGE NC_("STR_PAGE", "Page %1")
+#define STR_SLIDE NC_("STR_SLIDE", "Slide %1")
+#define STR_MASTER_SLIDE NC_("STR_MASTER_SLIDE", "Master Slide %1")
+#define STR_SHEET NC_("STR_SHEET", "Sheet %1")
+
+#define STR_SHAPES_ENTRY NC_("STR_SHAPES_NODE", "Shapes")
+#define STR_CHARTS_ENTRY NC_("STR_CHARTS_ENTRY", "Charts")
+#define STR_PIVOT_TABLES_ENTRY NC_("STR_PIVOT_TABLES_ENTRY", "Pivot Tables")
+#define STR_DOCUMENT_ENTRY NC_("STR_DOCUMENT_ENTRY", "Document")
+#define STR_SHEETS_ENTRY NC_("STR_SHEETS_ENTRY", "Sheets")
+#define STR_STYLES_ENTRY NC_("STR_STYLES_ENTRY", "Styles")
+#define STR_SLIDES_ENTRY NC_("STR_SLIDES_ENTRY", "Slides")
+#define STR_MASTER_SLIDES_ENTRY NC_("STR_MASTER_SLIDES_ENTRY", "Master Slides")
+#define STR_PAGES_ENTRY NC_("STR_PAGES_ENTRY", "Pages")
+#define STR_PARAGRAPHS_ENTRY NC_("STR_PARAGRAPHS_ENTRY", "Paragraphs")
+#define STR_TABLES_ENTRY NC_("STR_TABLES_ENTRY", "Tables")
+#define STR_FRAMES_ENTRY NC_("STR_FRAMES_ENTRY", "Frames")
+#define STR_GRAPHIC_OBJECTS_ENTRY NC_("STR_GRAPHIC_OBJECTS_ENTRY", "Graphic Objects")
+#define STR_EMBEDDED_OBJECTS_ENTRY NC_("STR_EMBEDDED_OBJECTS_ENTRY", "Embedded Objects")
+
+#define STR_ANY_VALUE_TRUE NC_("STR_ANY_VALUE_TRUE", "True")
+#define STR_ANY_VALUE_FALSE NC_("STR_ANY_VALUE_FALSE", "False")
+#define STR_ANY_VALUE_NULL NC_("STR_ANY_VALUE_NULL", "Null")
+#define STR_CLASS_UNKNOWN NC_("STR_CLASS_UNKNOWN", "Unknown")
+
+#define STR_METHOD_TYPE_OBJECT NC_("STR_METHOD_TYPE_OBJECT", "object")
+#define STR_METHOD_TYPE_STRUCT NC_("STR_METHOD_TYPE_STRUCT", "struct")
+#define STR_METHOD_TYPE_ENUM NC_("STR_METHOD_TYPE_ENUM", "enum")
+#define STR_METHOD_TYPE_SEQUENCE NC_("STR_METHOD_TYPE_SEQUENCE", "sequence")
+
+#define STR_PROPERTY_TYPE_IS_NAMED_CONTAINER NC_("STR_PROPERTY_TYPE_IS_NAMED_CONTAINER", "name container")
+#define STR_PROPERTY_TYPE_IS_INDEX_CONTAINER NC_("STR_PROPERTY_TYPE_IS_INDEX_CONTAINER", "index container")
+#define STR_PROPERTY_TYPE_IS_ENUMERATION NC_("STR_PROPERTY_TYPE_IS_ENUMERATION", "enumeration")
+
+#define STR_PARMETER_MODE_IN NC_("STR_PARMETER_MODE_IN", "[in]")
+#define STR_PARMETER_MODE_OUT NC_("STR_PARMETER_MODE_OUT", "[out]")
+#define STR_PARMETER_MODE_IN_AND_OUT NC_("STR_PARMETER_MODE_IN_AND_OUT", "[in&out]")
+
+#define STR_PROPERTY_ATTRIBUTE_IS_ATTRIBUTE NC_("STR_PROPERTY_ATTRIBUTE_IS_ATTRIBUTE", "attribute")
+#define STR_PROPERTY_ATTRIBUTE_GET NC_("STR_PROPERTY_ATTRIBUTE_GET", "get")
+#define STR_PROPERTY_ATTRIBUTE_SET NC_("STR_PROPERTY_ATTRIBUTE_SET", "set")
+#define STR_PROPERTY_ATTRIBUTE_MAYBEVOID NC_("STR_PROPERTY_ATTRIBUTE_MAYBEVOID", "may be void")
+#define STR_PROPERTY_ATTRIBUTE_READONLY NC_("STR_PROPERTY_ATTRIBUTE_READONLY", "read-only")
+#define STR_PROPERTY_ATTRIBUTE_WRITEONLY NC_("STR_PROPERTY_ATTRIBUTE_WRITEONLY", "write-only")
+#define STR_PROPERTY_ATTRIBUTE_REMOVABLE NC_("STR_PROPERTY_ATTRIBUTE_REMOVABLE", "removeable")
+#define STR_PROPERTY_ATTRIBUTE_BOUND NC_("STR_PROPERTY_ATTRIBUTE_BOUND", "bound")
+#define STR_PROPERTY_ATTRIBUTE_CONSTRAINED NC_("STR_PROPERTY_ATTRIBUTE_CONSTRAINED", "constrained")
+#define STR_PROPERTY_ATTRIBUTE_TRANSIENT NC_("STR_PROPERTY_ATTRIBUTE_TRANSIENT", "transient")
+#define STR_PROPERTY_ATTRIBUTE_MAYBEAMBIGUOUS NC_("STR_PROPERTY_ATTRIBUTE_MAYBEAMBIGUOUS", "may be ambiguous")
+#define STR_PROPERTY_ATTRIBUTE_MAYBEDEFAULT NC_("STR_PROPERTY_ATTRIBUTE_MAYBEDEFAULT", "may be default")
+
+#define STR_PROPERTY_VALUE_SEQUENCE NC_("STR_PROPERTY_VALUE_SEQUENCE", "<Sequence [%1]>")
+#define STR_PROPERTY_VALUE_OBJECT NC_("STR_PROPERTY_VALUE_OBJECT", "<Object@%1>")
+#define STR_PROPERTY_VALUE_STRUCT NC_("STR_PROPERTY_VALUE_STRUCT", "<Struct>")
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/devtools/DevelopmentToolChildWindow.cxx b/sfx2/source/devtools/DevelopmentToolChildWindow.cxx
new file mode 100644
index 000000000..6b160bfa0
--- /dev/null
+++ b/sfx2/source/devtools/DevelopmentToolChildWindow.cxx
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <sfx2/devtools/DevelopmentToolChildWindow.hxx>
+#include <sfx2/devtools/DevelopmentToolDockingWindow.hxx>
+#include <sfx2/sfxsids.hrc>
+
+SFX_IMPL_DOCKINGWINDOW_WITHID(DevelopmentToolChildWindow, SID_DEVELOPMENT_TOOLS_DOCKING_WINDOW);
+
+DevelopmentToolChildWindow::DevelopmentToolChildWindow(vcl::Window* pParentWindow, sal_uInt16 nId,
+ SfxBindings* pBindings,
+ SfxChildWinInfo* pInfo)
+ : SfxChildWindow(pParentWindow, nId)
+{
+ VclPtr<DevelopmentToolDockingWindow> pWin
+ = VclPtr<DevelopmentToolDockingWindow>::Create(pBindings, this, pParentWindow);
+ SetWindow(pWin);
+ SetAlignment(SfxChildAlignment::BOTTOM);
+ pWin->SetSizePixel(Size(0, 290));
+ pWin->Initialize(pInfo);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/devtools/DevelopmentToolDockingWindow.cxx b/sfx2/source/devtools/DevelopmentToolDockingWindow.cxx
new file mode 100644
index 000000000..817647ca9
--- /dev/null
+++ b/sfx2/source/devtools/DevelopmentToolDockingWindow.cxx
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <memory>
+
+#include <sfx2/devtools/DevelopmentToolDockingWindow.hxx>
+
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <sfx2/dispatch.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewfrm.hxx>
+
+#include "SelectionChangeHandler.hxx"
+
+using namespace css;
+
+DevelopmentToolDockingWindow::DevelopmentToolDockingWindow(SfxBindings* pInputBindings,
+ SfxChildWindow* pChildWindow,
+ vcl::Window* pParent)
+ : SfxDockingWindow(pInputBindings, pChildWindow, pParent, "DevelopmentTool",
+ "sfx/ui/developmenttool.ui")
+ , mpObjectInspectorWidgets(new ObjectInspectorWidgets(m_xBuilder))
+ , mpDocumentModelTreeView(m_xBuilder->weld_tree_view("leftside_treeview_id"))
+ , mpDomToolbar(m_xBuilder->weld_toolbar("dom_toolbar"))
+ , maDocumentModelTreeHandler(
+ mpDocumentModelTreeView,
+ pInputBindings->GetDispatcher()->GetFrame()->GetObjectShell()->GetBaseModel())
+ , maObjectInspectorTreeHandler(mpObjectInspectorWidgets)
+{
+ mpDocumentModelTreeView->connect_changed(
+ LINK(this, DevelopmentToolDockingWindow, DocumentModelTreeViewSelectionHandler));
+ mpDomToolbar->connect_clicked(
+ LINK(this, DevelopmentToolDockingWindow, DomToolbarButtonClicked));
+
+ auto* pViewFrame = pInputBindings->GetDispatcher()->GetFrame();
+
+ uno::Reference<frame::XController> xController = pViewFrame->GetFrame().GetController();
+
+ mxRoot = pInputBindings->GetDispatcher()->GetFrame()->GetObjectShell()->GetBaseModel();
+
+ maDocumentModelTreeHandler.inspectDocument();
+ mxSelectionListener.set(new SelectionChangeHandler(xController, this));
+ mxSelectionSupplier.set(xController, css::uno::UNO_QUERY);
+
+ maObjectInspectorTreeHandler.introspect(mxRoot);
+}
+
+IMPL_LINK(DevelopmentToolDockingWindow, DocumentModelTreeViewSelectionHandler, weld::TreeView&,
+ rView, void)
+{
+ if (mpDomToolbar->get_item_active("dom_current_selection_toggle"))
+ return;
+
+ OUString sID = rView.get_selected_id();
+ auto xObject = DocumentModelTreeHandler::getObjectByID(sID);
+ if (xObject.is())
+ maObjectInspectorTreeHandler.introspect(xObject);
+}
+
+IMPL_LINK(DevelopmentToolDockingWindow, DomToolbarButtonClicked, const OString&, rSelectionId, void)
+{
+ if (rSelectionId == "dom_refresh_button")
+ {
+ maDocumentModelTreeHandler.inspectDocument();
+ }
+ else if (rSelectionId == "dom_current_selection_toggle")
+ {
+ updateSelection();
+ }
+}
+
+DevelopmentToolDockingWindow::~DevelopmentToolDockingWindow() { disposeOnce(); }
+
+void DevelopmentToolDockingWindow::dispose()
+{
+ // Stop and remove the listener
+ auto* pSelectionChangeHandler
+ = dynamic_cast<SelectionChangeHandler*>(mxSelectionListener.get());
+ if (pSelectionChangeHandler)
+ pSelectionChangeHandler->stopListening();
+
+ mxSelectionListener = uno::Reference<view::XSelectionChangeListener>();
+
+ // dispose DOM and object inspector handlers
+ maDocumentModelTreeHandler.dispose();
+ maObjectInspectorTreeHandler.dispose();
+
+ // dispose welded objects
+ mpObjectInspectorWidgets.reset();
+ mpDomToolbar.reset();
+ mpDocumentModelTreeView.reset();
+
+ SfxDockingWindow::dispose();
+}
+
+void DevelopmentToolDockingWindow::updateSelection()
+{
+ bool bActive = mpDomToolbar->get_item_active("dom_current_selection_toggle");
+ if (bActive)
+ {
+ maObjectInspectorTreeHandler.introspect(mxCurrentSelection);
+ maDocumentModelTreeHandler.selectObject(mxCurrentSelection);
+ }
+ else
+ {
+ mpDocumentModelTreeView->set_sensitive(true);
+ }
+}
+
+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();
+}
+
+void DevelopmentToolDockingWindow::changeToCurrentSelection()
+{
+ if (mxSelectionSupplier.is())
+ {
+ css::uno::Any aAny = mxSelectionSupplier->getSelection();
+ if (aAny.hasValue())
+ {
+ auto xInterface = aAny.get<css::uno::Reference<css::uno::XInterface>>();
+ if (xInterface.is())
+ {
+ maObjectInspectorTreeHandler.introspect(xInterface);
+ mpDomToolbar->set_item_active("dom_current_selection_toggle", true);
+ return;
+ }
+ }
+ }
+ mpDomToolbar->set_item_active("dom_current_selection_toggle", false);
+ maObjectInspectorTreeHandler.introspect(mxRoot);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/devtools/DocumentModelTreeHandler.cxx b/sfx2/source/devtools/DocumentModelTreeHandler.cxx
new file mode 100644
index 000000000..c5358c02b
--- /dev/null
+++ b/sfx2/source/devtools/DocumentModelTreeHandler.cxx
@@ -0,0 +1,840 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <memory>
+
+#include <sfx2/devtools/DocumentModelTreeHandler.hxx>
+
+#include <sfx2/sfxresid.hxx>
+#include "DevToolsStrings.hrc"
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/drawing/XDrawPages.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/drawing/XDrawPageSupplier.hpp>
+#include <com/sun/star/drawing/XMasterPagesSupplier.hpp>
+#include <com/sun/star/sheet/XSpreadsheetDocument.hpp>
+#include <com/sun/star/sheet/XSpreadsheet.hpp>
+#include <com/sun/star/sheet/XDataPilotTablesSupplier.hpp>
+#include <com/sun/star/sheet/XDataPilotTables.hpp>
+#include <com/sun/star/table/XTableChartsSupplier.hpp>
+#include <com/sun/star/table/XTableCharts.hpp>
+#include <com/sun/star/text/XTextDocument.hpp>
+#include <com/sun/star/text/XTextTablesSupplier.hpp>
+#include <com/sun/star/text/XTextFramesSupplier.hpp>
+#include <com/sun/star/text/XTextGraphicObjectsSupplier.hpp>
+#include <com/sun/star/text/XTextEmbeddedObjectsSupplier.hpp>
+#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
+
+using namespace css;
+
+namespace
+{
+// returns a name of the object, if available
+OUString lclGetNamed(uno::Reference<uno::XInterface> const& xObject)
+{
+ uno::Reference<container::XNamed> xNamed(xObject, uno::UNO_QUERY);
+ if (!xNamed.is())
+ return OUString();
+ return xNamed->getName();
+}
+
+/** DocumentModelTreeEntry is an object "attached" to a tree node.
+ *
+ * It represents an object that is "attached" to the tree view an is
+ * responsible to provide the UNO object associated with the current
+ * node and on demand create and fill the children of the said node.
+ */
+class DocumentModelTreeEntry
+{
+protected:
+ OUString maString;
+ css::uno::Reference<css::uno::XInterface> mxObject;
+
+public:
+ DocumentModelTreeEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : maString(rString)
+ , mxObject(xObject)
+ {
+ }
+
+ virtual ~DocumentModelTreeEntry() {}
+
+ /// the node string shown in the tree view
+ OUString& getString() { return maString; }
+
+ /// should show the expander for the tree view node
+ virtual bool shouldShowExpander() { return false; }
+
+ /// The main UNO object for this entry
+ virtual css::uno::Reference<css::uno::XInterface> getMainObject() { return mxObject; }
+
+ /// Create and fill the children to the parent tree view node.
+ virtual void fill(std::unique_ptr<weld::TreeView>& /*pDocumentModelTree*/,
+ weld::TreeIter const& /*rParent*/)
+ {
+ }
+};
+
+// append an entry to a input TreeView to a parent
+void lclAppendToParentEntry(const std::unique_ptr<weld::TreeView>& rTree,
+ weld::TreeIter const& rParent, DocumentModelTreeEntry* pEntry)
+{
+ OUString sId(weld::toId(pEntry));
+ OUString const& rString = pEntry->getString();
+ rTree->insert(&rParent, -1, &rString, &sId, nullptr, nullptr, pEntry->shouldShowExpander(),
+ nullptr);
+}
+
+// append a root entry to a input TreeView
+OUString lclAppend(const std::unique_ptr<weld::TreeView>& rTree, DocumentModelTreeEntry* pEntry)
+{
+ OUString sId(weld::toId(pEntry));
+ OUString const& rString = pEntry->getString();
+ rTree->insert(nullptr, -1, &rString, &sId, nullptr, nullptr, pEntry->shouldShowExpander(),
+ nullptr);
+ return sId;
+}
+
+/** Entry that represents a object, which implements a XNameAccess */
+class NameAccessTreeEntry : public DocumentModelTreeEntry
+{
+protected:
+ NameAccessTreeEntry(OUString const& rString, uno::Reference<uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<container::XNameAccess> xNameAccess(getMainObject(), uno::UNO_QUERY);
+ return xNameAccess.is() && xNameAccess->getElementNames().getLength() > 0;
+ }
+
+ /// A generic fill when the UNO object implements XNameAccess interface
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<container::XNameAccess> xNameAccess(getMainObject(), uno::UNO_QUERY);
+ xNameAccess.set(getMainObject(), uno::UNO_QUERY);
+ if (!xNameAccess.is())
+ return;
+
+ const uno::Sequence<OUString> aNames = xNameAccess->getElementNames();
+ for (auto const& rName : aNames)
+ {
+ uno::Reference<uno::XInterface> xObject(xNameAccess->getByName(rName), uno::UNO_QUERY);
+ auto pEntry = std::make_unique<DocumentModelTreeEntry>(rName, xObject);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pEntry.release());
+ }
+ }
+};
+
+/** Entry that represents the document root object */
+class DocumentRootEntry : public DocumentModelTreeEntry
+{
+public:
+ DocumentRootEntry(OUString const& rString, uno::Reference<uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ bool shouldShowExpander() override { return false; }
+};
+
+/** Represents a paragraph object (XParagraph) */
+class ParagraphEntry : public DocumentModelTreeEntry
+{
+public:
+ ParagraphEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<container::XEnumerationAccess> xEnumAccess(getMainObject(), uno::UNO_QUERY);
+ if (!xEnumAccess.is())
+ return false;
+ auto xTextPortions = xEnumAccess->createEnumeration();
+ if (!xTextPortions.is())
+ return false;
+ return xTextPortions->hasMoreElements();
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<container::XEnumerationAccess> xEnumAccess(getMainObject(), uno::UNO_QUERY);
+ if (!xEnumAccess.is())
+ return;
+
+ uno::Reference<container::XEnumeration> xTextPortions = xEnumAccess->createEnumeration();
+ if (!xTextPortions.is())
+ return;
+
+ for (sal_Int32 i = 0; xTextPortions->hasMoreElements(); i++)
+ {
+ uno::Reference<text::XTextRange> const xTextPortion(xTextPortions->nextElement(),
+ uno::UNO_QUERY);
+ OUString aString = lclGetNamed(xTextPortion);
+ if (aString.isEmpty())
+ {
+ OUString aNumber = OUString::number(i + 1);
+ aString = SfxResId(STR_TEXT_PORTION).replaceFirst("%1", aNumber);
+ }
+
+ auto pEntry = std::make_unique<DocumentModelTreeEntry>(aString, xTextPortion);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pEntry.release());
+ }
+ }
+};
+
+/** Represents a list of paragraphs */
+class ParagraphsEntry : public DocumentModelTreeEntry
+{
+public:
+ ParagraphsEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<text::XTextDocument> xDocument(mxObject, uno::UNO_QUERY);
+ if (!xDocument.is())
+ return mxObject;
+
+ return xDocument->getText()->getText();
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<container::XEnumerationAccess> xEnumAccess(getMainObject(), uno::UNO_QUERY);
+ if (!xEnumAccess.is())
+ return false;
+ auto xParagraphEnum = xEnumAccess->createEnumeration();
+ if (!xParagraphEnum.is())
+ return false;
+ return xParagraphEnum->hasMoreElements();
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<container::XEnumerationAccess> xEnumAccess(getMainObject(), uno::UNO_QUERY);
+ if (!xEnumAccess.is())
+ return;
+
+ uno::Reference<container::XEnumeration> xParagraphEnum = xEnumAccess->createEnumeration();
+
+ if (!xParagraphEnum.is())
+ return;
+
+ for (sal_Int32 i = 0; xParagraphEnum->hasMoreElements(); i++)
+ {
+ uno::Reference<text::XTextContent> const xParagraph(xParagraphEnum->nextElement(),
+ uno::UNO_QUERY);
+ OUString aString = lclGetNamed(xParagraph);
+ if (aString.isEmpty())
+ {
+ aString = SfxResId(STR_PARAGRAPH).replaceFirst("%1", OUString::number(i + 1));
+ }
+
+ auto pEntry = std::make_unique<ParagraphEntry>(aString, xParagraph);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pEntry.release());
+ }
+ }
+};
+
+/** Represents a list of shapes */
+class ShapesEntry : public DocumentModelTreeEntry
+{
+public:
+ ShapesEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<drawing::XDrawPageSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getDrawPage();
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<container::XIndexAccess> xShapes(getMainObject(), uno::UNO_QUERY);
+ return xShapes.is() && xShapes->getCount() > 0;
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<container::XIndexAccess> xShapes(getMainObject(), uno::UNO_QUERY);
+ if (!xShapes.is())
+ return;
+ for (sal_Int32 nIndexShapes = 0; nIndexShapes < xShapes->getCount(); ++nIndexShapes)
+ {
+ uno::Reference<uno::XInterface> xShape(xShapes->getByIndex(nIndexShapes),
+ uno::UNO_QUERY);
+ OUString aShapeName = lclGetNamed(xShape);
+ if (aShapeName.isEmpty())
+ {
+ aShapeName
+ = SfxResId(STR_SHAPE).replaceFirst("%1", OUString::number(nIndexShapes + 1));
+ }
+
+ auto pEntry = std::make_unique<DocumentModelTreeEntry>(aShapeName, xShape);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pEntry.release());
+ }
+ }
+};
+
+/** Represents a list of tables */
+class TablesEntry : public NameAccessTreeEntry
+{
+public:
+ TablesEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : NameAccessTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<text::XTextTablesSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getTextTables();
+ }
+};
+
+/** Represents a list of frames */
+class FramesEntry : public NameAccessTreeEntry
+{
+public:
+ FramesEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : NameAccessTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<text::XTextFramesSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getTextFrames();
+ }
+};
+
+/** Represents a list of writer graphic objects */
+class WriterGraphicObjectsEntry : public NameAccessTreeEntry
+{
+public:
+ WriterGraphicObjectsEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : NameAccessTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<text::XTextGraphicObjectsSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getGraphicObjects();
+ }
+};
+
+/** Represents a list of writer embedded (OLE) objects */
+class EmbeddedObjectsEntry : public NameAccessTreeEntry
+{
+public:
+ EmbeddedObjectsEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : NameAccessTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<text::XTextEmbeddedObjectsSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getEmbeddedObjects();
+ }
+};
+
+/** Represents a style family, which contains a list of styles */
+class StylesFamilyEntry : public NameAccessTreeEntry
+{
+public:
+ StylesFamilyEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : NameAccessTreeEntry(rString, xObject)
+ {
+ }
+};
+
+/** Represents a list of style families */
+class StylesFamiliesEntry : public DocumentModelTreeEntry
+{
+public:
+ StylesFamiliesEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<style::XStyleFamiliesSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getStyleFamilies();
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<container::XNameAccess> xStyleFamilies(getMainObject(), uno::UNO_QUERY);
+ return xStyleFamilies.is() && xStyleFamilies->getElementNames().getLength() > 0;
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<container::XNameAccess> xStyleFamilies(getMainObject(), uno::UNO_QUERY);
+ if (!xStyleFamilies.is())
+ return;
+
+ const uno::Sequence<OUString> aNames = xStyleFamilies->getElementNames();
+ for (auto const& rFamilyName : aNames)
+ {
+ uno::Reference<uno::XInterface> xStyleFamily(xStyleFamilies->getByName(rFamilyName),
+ uno::UNO_QUERY);
+
+ auto pStylesFamilyEntry
+ = std::make_unique<StylesFamilyEntry>(rFamilyName, xStyleFamily);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pStylesFamilyEntry.release());
+ }
+ }
+};
+
+/** Represents a list of pages */
+class PagesEntry : public DocumentModelTreeEntry
+{
+public:
+ PagesEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<drawing::XDrawPagesSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getDrawPages();
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<drawing::XDrawPages> xDrawPages(getMainObject(), uno::UNO_QUERY);
+ return xDrawPages.is() && xDrawPages->getCount() > 0;
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<drawing::XDrawPages> xDrawPages(getMainObject(), uno::UNO_QUERY);
+ for (sal_Int32 i = 0; i < xDrawPages->getCount(); ++i)
+ {
+ uno::Reference<drawing::XDrawPage> xPage(xDrawPages->getByIndex(i), uno::UNO_QUERY);
+ if (!xPage.is())
+ continue;
+
+ OUString aPageString = lclGetNamed(xPage);
+ if (aPageString.isEmpty())
+ aPageString = SfxResId(STR_PAGE).replaceFirst("%1", OUString::number(i + 1));
+
+ auto pShapesEntry = std::make_unique<ShapesEntry>(aPageString, xPage);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pShapesEntry.release());
+ }
+ }
+};
+
+/** Represents a list of (Impress) slides */
+class SlidesEntry : public DocumentModelTreeEntry
+{
+public:
+ SlidesEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<drawing::XDrawPagesSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getDrawPages();
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<drawing::XDrawPages> xDrawPages(getMainObject(), uno::UNO_QUERY);
+ return xDrawPages.is() && xDrawPages->getCount() > 0;
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<drawing::XDrawPages> xDrawPages(getMainObject(), uno::UNO_QUERY);
+ for (sal_Int32 i = 0; i < xDrawPages->getCount(); ++i)
+ {
+ uno::Reference<drawing::XDrawPage> xPage(xDrawPages->getByIndex(i), uno::UNO_QUERY);
+ if (!xPage.is())
+ continue;
+
+ OUString aPageString = lclGetNamed(xPage);
+ if (aPageString.isEmpty())
+ aPageString = SfxResId(STR_SLIDE).replaceFirst("%1", OUString::number(i + 1));
+
+ auto pShapesEntry = std::make_unique<ShapesEntry>(aPageString, xPage);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pShapesEntry.release());
+ }
+ }
+};
+
+/** Represents a list of (Impress) master slides */
+class MasterSlidesEntry : public DocumentModelTreeEntry
+{
+public:
+ MasterSlidesEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<drawing::XMasterPagesSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getMasterPages();
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<drawing::XDrawPages> xDrawPages(getMainObject(), uno::UNO_QUERY);
+ return xDrawPages.is() && xDrawPages->getCount() > 0;
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<drawing::XDrawPages> xDrawPages(getMainObject(), uno::UNO_QUERY);
+ for (sal_Int32 i = 0; i < xDrawPages->getCount(); ++i)
+ {
+ uno::Reference<drawing::XDrawPage> xPage(xDrawPages->getByIndex(i), uno::UNO_QUERY);
+ if (!xPage.is())
+ continue;
+
+ OUString aPageString = lclGetNamed(xPage);
+ if (aPageString.isEmpty())
+ {
+ aPageString
+ = SfxResId(STR_MASTER_SLIDE).replaceFirst("%1", OUString::number(i + 1));
+ }
+
+ auto pShapesEntry = std::make_unique<ShapesEntry>(aPageString, xPage);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pShapesEntry.release());
+ }
+ }
+};
+
+/** Represents a list of charts */
+class ChartsEntry : public NameAccessTreeEntry
+{
+public:
+ ChartsEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : NameAccessTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<table::XTableChartsSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getCharts();
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<table::XTableCharts> xCharts(getMainObject(), uno::UNO_QUERY);
+ if (!xCharts.is())
+ return;
+ NameAccessTreeEntry::fill(pDocumentModelTree, rParent);
+ }
+};
+
+/** Represents a list of pivot tables */
+class PivotTablesEntry : public NameAccessTreeEntry
+{
+public:
+ PivotTablesEntry(OUString const& rString,
+ css::uno::Reference<css::uno::XInterface> const& xObject)
+ : NameAccessTreeEntry(rString, xObject)
+ {
+ }
+
+ bool shouldShowExpander() override { return true; }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<sheet::XDataPilotTablesSupplier> xSupplier(mxObject, uno::UNO_QUERY);
+ if (!xSupplier.is())
+ return mxObject;
+ return xSupplier->getDataPilotTables();
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<sheet::XDataPilotTables> xPivotTables(getMainObject(), uno::UNO_QUERY);
+ if (!xPivotTables.is())
+ return;
+ NameAccessTreeEntry::fill(pDocumentModelTree, rParent);
+ }
+};
+
+/** Represents a (Calc) sheet */
+class SheetEntry : public DocumentModelTreeEntry
+{
+public:
+ SheetEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ bool shouldShowExpander() override { return true; }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ auto pShapesEntry
+ = std::make_unique<ShapesEntry>(SfxResId(STR_SHAPES_ENTRY), getMainObject());
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pShapesEntry.release());
+
+ auto pChartsEntry
+ = std::make_unique<ChartsEntry>(SfxResId(STR_CHARTS_ENTRY), getMainObject());
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pChartsEntry.release());
+
+ auto pPivotTablesEntry
+ = std::make_unique<PivotTablesEntry>(SfxResId(STR_PIVOT_TABLES_ENTRY), getMainObject());
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pPivotTablesEntry.release());
+ }
+};
+
+/** Represents a list of (Calc) sheet */
+class SheetsEntry : public DocumentModelTreeEntry
+{
+public:
+ SheetsEntry(OUString const& rString, css::uno::Reference<css::uno::XInterface> const& xObject)
+ : DocumentModelTreeEntry(rString, xObject)
+ {
+ }
+
+ css::uno::Reference<css::uno::XInterface> getMainObject() override
+ {
+ uno::Reference<sheet::XSpreadsheetDocument> xSheetDocument(mxObject, uno::UNO_QUERY);
+ if (!xSheetDocument.is())
+ return mxObject;
+ return xSheetDocument->getSheets();
+ }
+
+ bool shouldShowExpander() override
+ {
+ uno::Reference<container::XIndexAccess> xIndexAccess(getMainObject(), uno::UNO_QUERY);
+ return xIndexAccess.is() && xIndexAccess->getCount() > 0;
+ }
+
+ void fill(std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ weld::TreeIter const& rParent) override
+ {
+ uno::Reference<container::XIndexAccess> xIndexAccesss(getMainObject(), uno::UNO_QUERY);
+ if (!xIndexAccesss.is())
+ return;
+
+ for (sal_Int32 i = 0; i < xIndexAccesss->getCount(); ++i)
+ {
+ uno::Reference<sheet::XSpreadsheet> xSheet(xIndexAccesss->getByIndex(i),
+ uno::UNO_QUERY);
+ OUString aString = lclGetNamed(xSheet);
+ if (aString.isEmpty())
+ aString = SfxResId(STR_SHEET).replaceFirst("%1", OUString::number(i + 1));
+ auto pEntry = std::make_unique<SheetEntry>(aString, xSheet);
+ lclAppendToParentEntry(pDocumentModelTree, rParent, pEntry.release());
+ }
+ }
+};
+
+} // end anonymous namespace
+
+DocumentModelTreeHandler::DocumentModelTreeHandler(
+ std::unique_ptr<weld::TreeView>& pDocumentModelTree,
+ css::uno::Reference<css::uno::XInterface> const& xDocument)
+ : mpDocumentModelTree(pDocumentModelTree)
+ , mxDocument(xDocument)
+{
+ mpDocumentModelTree->connect_expanding(LINK(this, DocumentModelTreeHandler, ExpandingHandler));
+}
+
+uno::Reference<uno::XInterface> DocumentModelTreeHandler::getObjectByID(OUString const& rID)
+{
+ uno::Reference<uno::XInterface> xObject;
+ if (rID.isEmpty())
+ return xObject;
+ auto* pEntry = weld::fromId<DocumentModelTreeEntry*>(rID);
+ return pEntry->getMainObject();
+}
+
+void DocumentModelTreeHandler::clearAll()
+{
+ // destroy all DocumentModelTreeEntries from the tree
+ mpDocumentModelTree->all_foreach([this](weld::TreeIter& rEntry) {
+ OUString sID = mpDocumentModelTree->get_id(rEntry);
+ auto* pEntry = weld::fromId<DocumentModelTreeEntry*>(sID);
+ delete pEntry;
+ return false;
+ });
+ mpDocumentModelTree->clear();
+}
+
+void DocumentModelTreeHandler::clearChildren(weld::TreeIter const& rParent)
+{
+ bool bChild = false;
+ do
+ {
+ bChild = mpDocumentModelTree->iter_has_child(rParent);
+ if (bChild)
+ {
+ std::unique_ptr<weld::TreeIter> pChild = mpDocumentModelTree->make_iterator(&rParent);
+ bChild = mpDocumentModelTree->iter_children(*pChild);
+ if (bChild)
+ {
+ clearChildren(*pChild);
+ OUString sID = mpDocumentModelTree->get_id(*pChild);
+ auto* pEntry = weld::fromId<DocumentModelTreeEntry*>(sID);
+ delete pEntry;
+ mpDocumentModelTree->remove(*pChild);
+ }
+ }
+ } while (bChild);
+}
+
+void DocumentModelTreeHandler::dispose()
+{
+ mpDocumentModelTree->all_foreach([this](weld::TreeIter& rEntry) {
+ OUString sID = mpDocumentModelTree->get_id(rEntry);
+ auto* pEntry = weld::fromId<DocumentModelTreeEntry*>(sID);
+ delete pEntry;
+ return false;
+ });
+}
+
+IMPL_LINK(DocumentModelTreeHandler, ExpandingHandler, weld::TreeIter const&, rParent, bool)
+{
+ OUString sID = mpDocumentModelTree->get_id(rParent);
+ if (sID.isEmpty())
+ return true;
+
+ clearChildren(rParent);
+ auto* pEntry = weld::fromId<DocumentModelTreeEntry*>(sID);
+ pEntry->fill(mpDocumentModelTree, rParent);
+
+ return true;
+}
+
+void DocumentModelTreeHandler::selectObject(
+ css::uno::Reference<css::uno::XInterface> const& xInterface)
+{
+ mpDocumentModelTree->unselect_all();
+
+ mpDocumentModelTree->all_foreach([this, xInterface](weld::TreeIter& rEntry) {
+ OUString sID = mpDocumentModelTree->get_id(rEntry);
+ auto* pEntry = weld::fromId<DocumentModelTreeEntry*>(sID);
+ if (xInterface == pEntry->getMainObject())
+ {
+ mpDocumentModelTree->select(rEntry);
+ return true;
+ }
+ return false;
+ });
+}
+
+void DocumentModelTreeHandler::inspectDocument()
+{
+ clearAll();
+
+ uno::Reference<lang::XServiceInfo> xDocumentServiceInfo(mxDocument, uno::UNO_QUERY_THROW);
+
+ lclAppend(mpDocumentModelTree, new DocumentRootEntry(SfxResId(STR_DOCUMENT_ENTRY), mxDocument));
+
+ if (xDocumentServiceInfo->supportsService("com.sun.star.sheet.SpreadsheetDocument"))
+ {
+ lclAppend(mpDocumentModelTree, new SheetsEntry(SfxResId(STR_SHEETS_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree,
+ new StylesFamiliesEntry(SfxResId(STR_STYLES_ENTRY), mxDocument));
+ }
+ else if (xDocumentServiceInfo->supportsService(
+ "com.sun.star.presentation.PresentationDocument"))
+ {
+ lclAppend(mpDocumentModelTree, new SlidesEntry(SfxResId(STR_SLIDES_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree,
+ new MasterSlidesEntry(SfxResId(STR_MASTER_SLIDES_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree,
+ new StylesFamiliesEntry(SfxResId(STR_STYLES_ENTRY), mxDocument));
+ }
+ else if (xDocumentServiceInfo->supportsService("com.sun.star.drawing.DrawingDocument"))
+ {
+ lclAppend(mpDocumentModelTree, new PagesEntry(SfxResId(STR_PAGES_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree,
+ new StylesFamiliesEntry(SfxResId(STR_STYLES_ENTRY), mxDocument));
+ }
+ else if (xDocumentServiceInfo->supportsService("com.sun.star.text.TextDocument")
+ || xDocumentServiceInfo->supportsService("com.sun.star.text.WebDocument"))
+ {
+ lclAppend(mpDocumentModelTree,
+ new ParagraphsEntry(SfxResId(STR_PARAGRAPHS_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree, new ShapesEntry(SfxResId(STR_SHAPES_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree, new TablesEntry(SfxResId(STR_TABLES_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree, new FramesEntry(SfxResId(STR_FRAMES_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree,
+ new WriterGraphicObjectsEntry(SfxResId(STR_GRAPHIC_OBJECTS_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree,
+ new EmbeddedObjectsEntry(SfxResId(STR_EMBEDDED_OBJECTS_ENTRY), mxDocument));
+ lclAppend(mpDocumentModelTree,
+ new StylesFamiliesEntry(SfxResId(STR_STYLES_ENTRY), mxDocument));
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/devtools/ObjectInspectorTreeHandler.cxx b/sfx2/source/devtools/ObjectInspectorTreeHandler.cxx
new file mode 100644
index 000000000..18c4206e0
--- /dev/null
+++ b/sfx2/source/devtools/ObjectInspectorTreeHandler.cxx
@@ -0,0 +1,1384 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <memory>
+
+#include <sfx2/devtools/ObjectInspectorTreeHandler.hxx>
+#include <sfx2/sfxresid.hxx>
+#include <vcl/svapp.hxx>
+#include "DevToolsStrings.hrc"
+
+#include <com/sun/star/beans/theIntrospection.hpp>
+#include <com/sun/star/beans/XIntrospection.hpp>
+#include <com/sun/star/beans/XIntrospectionAccess.hpp>
+#include <com/sun/star/beans/PropertyConcept.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/MethodConcept.hpp>
+
+#include <com/sun/star/reflection/theCoreReflection.hpp>
+#include <com/sun/star/reflection/XIdlReflection.hpp>
+#include <com/sun/star/reflection/XIdlMethod.hpp>
+#include <com/sun/star/reflection/XIdlArray.hpp>
+#include <com/sun/star/reflection/XEnumTypeDescription.hpp>
+
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+
+#include <com/sun/star/script/Invocation.hpp>
+#include <com/sun/star/script/XInvocation2.hpp>
+#include <com/sun/star/script/MemberType.hpp>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XTypeProvider.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/extract.hxx>
+
+#include <vcl/settings.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+using namespace css;
+
+namespace
+{
+constexpr OUStringLiteral constTypeDescriptionManagerSingletonName
+ = u"/singletons/com.sun.star.reflection.theTypeDescriptionManager";
+
+OUString enumValueToEnumName(uno::Any const& aValue,
+ uno::Reference<uno::XComponentContext> const& xContext)
+{
+ sal_Int32 nIntValue = 0;
+ if (!cppu::enum2int(nIntValue, aValue))
+ return OUString();
+
+ uno::Reference<container::XHierarchicalNameAccess> xManager;
+ xManager.set(xContext->getValueByName(constTypeDescriptionManagerSingletonName),
+ uno::UNO_QUERY);
+
+ uno::Reference<reflection::XEnumTypeDescription> xTypeDescription;
+ xTypeDescription.set(xManager->getByHierarchicalName(aValue.getValueType().getTypeName()),
+ uno::UNO_QUERY);
+
+ const uno::Sequence<sal_Int32> aValues = xTypeDescription->getEnumValues();
+ sal_Int32 nValuesIndex = std::find(aValues.begin(), aValues.end(), nIntValue) - aValues.begin();
+ uno::Sequence<OUString> aNames = xTypeDescription->getEnumNames();
+ return aNames[nValuesIndex];
+}
+
+OUString getInterfaceImplementationClass(uno::Reference<uno::XInterface> const& xInterface)
+{
+ auto xServiceInfo = uno::Reference<lang::XServiceInfo>(xInterface, uno::UNO_QUERY);
+ if (xServiceInfo.is())
+ return xServiceInfo->getImplementationName();
+ return OUString();
+}
+
+/** converts basic any value to a string */
+OUString convertBasicValueToString(const uno::Any& aValue,
+ const uno::Reference<uno::XComponentContext>& xContext)
+{
+ OUString aRetStr;
+
+ // return early if we don't have any value
+ if (!aValue.hasValue())
+ return SfxResId(STR_ANY_VALUE_NULL);
+
+ uno::TypeClass eType = aValue.getValueTypeClass();
+
+ switch (eType)
+ {
+ case uno::TypeClass_BOOLEAN:
+ {
+ bool bBool = aValue.get<bool>();
+ aRetStr = bBool ? SfxResId(STR_ANY_VALUE_TRUE) : SfxResId(STR_ANY_VALUE_FALSE);
+ break;
+ }
+ case uno::TypeClass_CHAR:
+ {
+ sal_Unicode aChar = aValue.get<sal_Unicode>();
+ aRetStr = OUString::number(aChar);
+ break;
+ }
+ case uno::TypeClass_STRING:
+ {
+ aRetStr = u"\"" + aValue.get<OUString>() + u"\"";
+ break;
+ }
+ case uno::TypeClass_FLOAT:
+ {
+ auto aNumber = aValue.get<float>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_DOUBLE:
+ {
+ auto aNumber = aValue.get<double>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_BYTE:
+ {
+ auto aNumber = aValue.get<sal_Int8>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_SHORT:
+ {
+ auto aNumber = aValue.get<sal_Int16>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_LONG:
+ {
+ auto aNumber = aValue.get<sal_Int32>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_HYPER:
+ {
+ auto aNumber = aValue.get<sal_Int64>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_UNSIGNED_SHORT:
+ {
+ auto aNumber = aValue.get<sal_uInt16>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_UNSIGNED_LONG:
+ {
+ auto aNumber = aValue.get<sal_uInt32>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_UNSIGNED_HYPER:
+ {
+ auto aNumber = aValue.get<sal_uInt64>();
+ aRetStr = OUString::number(aNumber);
+ break;
+ }
+ case uno::TypeClass_TYPE:
+ {
+ auto aType = aValue.get<uno::Type>();
+ aRetStr = aType.getTypeName();
+ break;
+ }
+ case uno::TypeClass_ENUM:
+ {
+ aRetStr = enumValueToEnumName(aValue, xContext);
+ break;
+ }
+
+ default:
+ break;
+ }
+ return aRetStr;
+}
+
+// returns a name of the object, if available
+OUString getInterfaceName(uno::Reference<uno::XInterface> const& xInterface,
+ const uno::Reference<uno::XComponentContext>& xContext)
+{
+ uno::Reference<container::XNamed> xNamed(xInterface, uno::UNO_QUERY);
+ if (xNamed.is())
+ return xNamed->getName();
+
+ auto xInvocationFactory = css::script::Invocation::create(xContext);
+ uno::Sequence<uno::Any> aParameters = { uno::Any(xInterface) };
+ auto xInvocationInterface = xInvocationFactory->createInstanceWithArguments(aParameters);
+ if (xInvocationInterface.is())
+ {
+ uno::Reference<script::XInvocation2> xInvocation(xInvocationInterface, uno::UNO_QUERY);
+ if (xInvocation.is() && xInvocation->hasProperty("Name"))
+ {
+ uno::Any aAny = xInvocation->getValue("Name");
+ if (aAny.hasValue() && aAny.getValueTypeClass() == uno::TypeClass_STRING)
+ return aAny.get<OUString>();
+ }
+ }
+ return OUString();
+}
+
+OUString convertAnyToString(const uno::Any& aValue,
+ const uno::Reference<uno::XComponentContext>& xContext)
+{
+ // return early if we don't have any value
+ if (!aValue.hasValue())
+ return SfxResId(STR_ANY_VALUE_NULL);
+
+ OUString aRetStr;
+
+ uno::TypeClass eType = aValue.getValueTypeClass();
+
+ switch (eType)
+ {
+ case uno::TypeClass_INTERFACE:
+ {
+ uno::Reference<uno::XInterface> xInterface(aValue, uno::UNO_QUERY);
+ if (!xInterface.is())
+ aRetStr = SfxResId(STR_ANY_VALUE_NULL);
+ else
+ {
+ OUString aImplementationClass = getInterfaceImplementationClass(xInterface);
+ if (aImplementationClass.isEmpty())
+ aImplementationClass = SfxResId(STR_CLASS_UNKNOWN);
+ aRetStr
+ = SfxResId(STR_PROPERTY_VALUE_OBJECT).replaceFirst("%1", aImplementationClass);
+
+ OUString aString = getInterfaceName(xInterface, xContext);
+ if (!aString.isEmpty())
+ aRetStr += " {" + aString + "}";
+ }
+ break;
+ }
+ case uno::TypeClass_STRUCT:
+ {
+ aRetStr = SfxResId(STR_PROPERTY_VALUE_STRUCT);
+ break;
+ }
+ default:
+ {
+ aRetStr = convertBasicValueToString(aValue, xContext);
+ break;
+ }
+ }
+ return aRetStr;
+}
+
+OUString convertAnyToShortenedString(const uno::Any& aValue,
+ const uno::Reference<uno::XComponentContext>& xContext)
+{
+ // return early if we don't have any value
+ if (!aValue.hasValue())
+ return SfxResId(STR_ANY_VALUE_NULL);
+
+ OUString aRetStr;
+
+ uno::TypeClass eType = aValue.getValueTypeClass();
+
+ constexpr const sal_Int32 constMaxStringLength = 60;
+
+ switch (eType)
+ {
+ case uno::TypeClass_INTERFACE:
+ {
+ aRetStr = convertAnyToString(aValue, xContext);
+
+ if (aRetStr.getLength() > constMaxStringLength + 3)
+ aRetStr = OUString::Concat(aRetStr.subView(0, constMaxStringLength)) + u"...";
+ break;
+ }
+ case uno::TypeClass_STRING:
+ {
+ OUString aString = convertAnyToString(aValue, xContext);
+ if (aString.getLength() > constMaxStringLength + 4)
+ aString = OUString::Concat(aString.subView(0, constMaxStringLength)) + u"\"...";
+ aRetStr = aString.replaceAll("\n", " ");
+ break;
+ }
+ default:
+ {
+ aRetStr = convertAnyToString(aValue, xContext);
+ break;
+ }
+ }
+ return aRetStr;
+}
+
+/** converts an any's type to a string (in a short form) */
+OUString getAnyType(const uno::Any& aValue)
+{
+ OUString aTypeName = aValue.getValueType().getTypeName();
+ return aTypeName.replaceAll("com.sun.star", "css");
+}
+
+/** converts a Type to a XIdlClass */
+uno::Reference<reflection::XIdlClass>
+convertTypeToIdlClass(const uno::Type& rType,
+ const uno::Reference<uno::XComponentContext>& xContext)
+{
+ auto xReflection = reflection::theCoreReflection::get(xContext);
+ return xReflection->forName(rType.getTypeName());
+}
+
+// Object inspector nodes
+
+/** Object inspector node's main interface
+ *
+ * The interface for the "attached" object to a tree view nodes that
+ * are added to the tree views of the object inspector part. The node
+ * can return the main value of the node (object name) and if present
+ * also the values for additional columns. It signals if a tree needs
+ * an expander and fills the children of the tree is any exists.
+ *
+ */
+class ObjectInspectorNodeInterface
+{
+public:
+ ObjectInspectorNodeInterface() = default;
+
+ virtual ~ObjectInspectorNodeInterface() {}
+
+ // main value (object name) of the tree view node
+ virtual OUString getObjectName() = 0;
+
+ // should show the expander for the tree view node
+ virtual bool shouldShowExpander() { return false; }
+
+ // fill the children for the current tree view node
+ virtual void fillChildren(std::unique_ptr<weld::TreeView>& rTree, const weld::TreeIter* pParent)
+ = 0;
+
+ // fill any additional column values for the current tree view node
+ virtual std::vector<std::pair<sal_Int32, OUString>> getColumnValues()
+ {
+ return std::vector<std::pair<sal_Int32, OUString>>();
+ }
+};
+
+// appends the node to the root of the tree view
+OUString lclAppendNode(const std::unique_ptr<weld::TreeView>& pTree,
+ ObjectInspectorNodeInterface* pEntry)
+{
+ OUString sName = pEntry->getObjectName();
+ OUString sId(weld::toId(pEntry));
+ std::unique_ptr<weld::TreeIter> pCurrent = pTree->make_iterator();
+ pTree->insert(nullptr, -1, &sName, &sId, nullptr, nullptr, pEntry->shouldShowExpander(),
+ pCurrent.get());
+ pTree->set_text_emphasis(*pCurrent, true, 0);
+
+ for (auto const& rPair : pEntry->getColumnValues())
+ {
+ pTree->set_text(*pCurrent, rPair.second, rPair.first);
+ }
+
+ return sId;
+}
+
+// appends the node to the parent
+OUString lclAppendNodeToParent(const std::unique_ptr<weld::TreeView>& pTree,
+ const weld::TreeIter* pParent, ObjectInspectorNodeInterface* pEntry)
+{
+ OUString sName = pEntry->getObjectName();
+ OUString sId(weld::toId(pEntry));
+ std::unique_ptr<weld::TreeIter> pCurrent = pTree->make_iterator();
+ pTree->insert(pParent, -1, &sName, &sId, nullptr, nullptr, pEntry->shouldShowExpander(),
+ pCurrent.get());
+ pTree->set_text_emphasis(*pCurrent, true, 0);
+
+ for (auto const& rPair : pEntry->getColumnValues())
+ {
+ pTree->set_text(*pCurrent, rPair.second, rPair.first);
+ }
+
+ return sId;
+}
+
+/** Node that represent just a simple string with no children or columns */
+class SimpleStringNode : public ObjectInspectorNodeInterface
+{
+protected:
+ OUString msName;
+
+public:
+ SimpleStringNode(OUString const& rName)
+ : msName(rName)
+ {
+ }
+
+ void fillChildren(std::unique_ptr<weld::TreeView>& /*rTree*/,
+ const weld::TreeIter* /*pParent*/) override
+ {
+ }
+
+ OUString getObjectName() override { return msName; }
+};
+
+/** Node represents a method of an object */
+class MethodNode : public ObjectInspectorNodeInterface
+{
+private:
+ uno::Reference<reflection::XIdlMethod> mxMethod;
+
+public:
+ MethodNode(uno::Reference<reflection::XIdlMethod> const& xMethod)
+ : mxMethod(xMethod)
+ {
+ }
+
+ OUString getObjectName() override { return mxMethod->getName(); }
+
+ static OUString simpleTypeName(uno::Reference<reflection::XIdlClass> const& xClass)
+ {
+ switch (xClass->getTypeClass())
+ {
+ case uno::TypeClass_INTERFACE:
+ return SfxResId(STR_METHOD_TYPE_OBJECT);
+ case uno::TypeClass_STRUCT:
+ return SfxResId(STR_METHOD_TYPE_STRUCT);
+ case uno::TypeClass_ENUM:
+ return SfxResId(STR_METHOD_TYPE_ENUM);
+ case uno::TypeClass_SEQUENCE:
+ return SfxResId(STR_METHOD_TYPE_SEQUENCE);
+ default:
+ break;
+ }
+ return xClass->getName();
+ }
+
+ std::vector<std::pair<sal_Int32, OUString>> getColumnValues() override
+ {
+ OUString aOutString;
+ auto xClass = mxMethod->getReturnType();
+ aOutString = simpleTypeName(xClass);
+
+ OUString aInString;
+ const auto aParameters = mxMethod->getParameterInfos();
+ bool bFirst = true;
+ for (auto const& rParameterInfo : aParameters)
+ {
+ if (!bFirst)
+ aInString += ", ";
+ else
+ bFirst = false;
+
+ switch (rParameterInfo.aMode)
+ {
+ case reflection::ParamMode_IN:
+ aInString += SfxResId(STR_PARMETER_MODE_IN) + " ";
+ break;
+ case reflection::ParamMode_OUT:
+ aInString += SfxResId(STR_PARMETER_MODE_OUT) + " ";
+ break;
+ case reflection::ParamMode_INOUT:
+ aInString += SfxResId(STR_PARMETER_MODE_IN_AND_OUT) + " ";
+ break;
+ default:
+ break;
+ }
+
+ aInString += rParameterInfo.aName + " : " + simpleTypeName(rParameterInfo.aType);
+ }
+
+ OUString aImplementationClass = mxMethod->getDeclaringClass()->getName();
+
+ return {
+ { 1, aOutString },
+ { 2, aInString },
+ { 3, aImplementationClass },
+ };
+ }
+
+ void fillChildren(std::unique_ptr<weld::TreeView>& /*rTree*/,
+ const weld::TreeIter* /*pParent*/) override
+ {
+ }
+};
+
+/** Node represents a class (XIdlClass) of an object.
+ *
+ * Children are superclasses of the current class. XInterface superclass
+ * is ignored.
+ *
+ */
+class ClassNode : public ObjectInspectorNodeInterface
+{
+private:
+ uno::Reference<reflection::XIdlClass> mxClass;
+
+ static bool isXInterface(uno::Reference<reflection::XIdlClass> const& xClass)
+ {
+ return xClass->getName() == "com.sun.star.uno.XInterface";
+ }
+
+public:
+ ClassNode(uno::Reference<reflection::XIdlClass> const& xClass)
+ : mxClass(xClass)
+ {
+ }
+
+ bool shouldShowExpander() override
+ {
+ auto const& xSuperClasses = mxClass->getSuperclasses();
+ return xSuperClasses.getLength() > 2
+ || (xSuperClasses.getLength() == 1 && !isXInterface(xSuperClasses[0]));
+ }
+
+ OUString getObjectName() override { return mxClass->getName(); }
+
+ // Fill superclasses
+ void fillChildren(std::unique_ptr<weld::TreeView>& rTree,
+ const weld::TreeIter* pParent) override
+ {
+ auto const& xSuperClasses = mxClass->getSuperclasses();
+ for (auto const& xSuper : xSuperClasses)
+ {
+ if (!isXInterface(xSuper))
+ lclAppendNodeToParent(rTree, pParent, new ClassNode(xSuper));
+ }
+ }
+};
+
+/** Node represents a basic value, that can be any object, sequence, struct */
+class BasicValueNode : public SimpleStringNode
+{
+protected:
+ uno::Any maAny;
+ OUString mrInfo;
+ uno::Reference<uno::XComponentContext> mxContext;
+
+ ObjectInspectorNodeInterface*
+ createNodeObjectForAny(OUString const& rName, const uno::Any& rAny, OUString const& mrInfo);
+
+public:
+ BasicValueNode(OUString const& rName, uno::Any const& rAny, OUString const& rInfo,
+ uno::Reference<uno::XComponentContext> const& xContext)
+ : SimpleStringNode(rName)
+ , maAny(rAny)
+ , mrInfo(rInfo)
+ , mxContext(xContext)
+ {
+ }
+
+ const uno::Any& getAny() const { return maAny; }
+
+ bool shouldShowExpander() override
+ {
+ if (maAny.hasValue())
+ {
+ switch (maAny.getValueType().getTypeClass())
+ {
+ case uno::TypeClass_INTERFACE:
+ {
+ uno::Reference<uno::XInterface> xInterface(maAny, uno::UNO_QUERY);
+ return xInterface.is();
+ }
+ case uno::TypeClass_SEQUENCE:
+ return true;
+ default:
+ break;
+ }
+ }
+ return false;
+ }
+
+ std::vector<std::pair<sal_Int32, OUString>> getColumnValues() override
+ {
+ OUString aValue = convertAnyToShortenedString(maAny, mxContext);
+ OUString aType = getAnyType(maAny);
+
+ return { { 1, aValue }, { 2, aType }, { 3, mrInfo } };
+ }
+};
+
+/** Node represents a property */
+class GenericPropertiesNode : public BasicValueNode
+{
+public:
+ GenericPropertiesNode(OUString const& rName, uno::Any const& rAny, OUString const& rInfo,
+ uno::Reference<uno::XComponentContext> const& xContext)
+ : BasicValueNode(rName, rAny, rInfo, xContext)
+ {
+ }
+
+ void fillChildren(std::unique_ptr<weld::TreeView>& pTree,
+ const weld::TreeIter* pParent) override;
+};
+
+/** Node represents a struct */
+class StructNode : public BasicValueNode
+{
+public:
+ StructNode(OUString const& rName, uno::Any const& rAny, OUString const& rInfo,
+ uno::Reference<uno::XComponentContext> const& xContext)
+ : BasicValueNode(rName, rAny, rInfo, xContext)
+ {
+ }
+
+ bool shouldShowExpander() override { return true; }
+
+ void fillChildren(std::unique_ptr<weld::TreeView>& pTree,
+ const weld::TreeIter* pParent) override;
+};
+
+/** Node represents a sequence */
+class SequenceNode : public BasicValueNode
+{
+ uno::Reference<reflection::XIdlArray> mxIdlArray;
+
+public:
+ SequenceNode(OUString const& rName, uno::Any const& rAny, OUString const& rInfo,
+ uno::Reference<uno::XComponentContext> const& xContext)
+ : BasicValueNode(rName, rAny, rInfo, xContext)
+ {
+ auto xClass = convertTypeToIdlClass(maAny.getValueType(), mxContext);
+ mxIdlArray = xClass->getArray();
+ }
+
+ bool shouldShowExpander() override
+ {
+ // Show expander only if the sequence has elements
+ int nLength = mxIdlArray->getLen(maAny);
+ return nLength > 0;
+ }
+
+ void fillChildren(std::unique_ptr<weld::TreeView>& pTree,
+ const weld::TreeIter* pParent) override
+ {
+ int nLength = mxIdlArray->getLen(maAny);
+
+ for (int i = 0; i < nLength; i++)
+ {
+ uno::Any aArrayValue = mxIdlArray->get(maAny, i);
+
+ auto* pObjectInspectorNode
+ = createNodeObjectForAny(OUString::number(i), aArrayValue, "");
+ if (pObjectInspectorNode)
+ lclAppendNodeToParent(pTree, pParent, pObjectInspectorNode);
+ }
+ }
+
+ std::vector<std::pair<sal_Int32, OUString>> getColumnValues() override
+ {
+ int nLength = mxIdlArray->getLen(maAny);
+
+ OUString aType = getAnyType(maAny).replaceAll(u"[]", u"");
+ aType += u"[" + OUString::number(nLength) + u"]";
+
+ OUString aValue
+ = SfxResId(STR_PROPERTY_VALUE_SEQUENCE).replaceFirst("%1", OUString::number(nLength));
+
+ return {
+ { 1, aValue },
+ { 2, aType },
+ };
+ }
+};
+
+void GenericPropertiesNode::fillChildren(std::unique_ptr<weld::TreeView>& pTree,
+ const weld::TreeIter* pParent)
+{
+ if (!maAny.hasValue())
+ return;
+
+ try
+ {
+ const auto xNameAccess = uno::Reference<container::XNameAccess>(maAny, uno::UNO_QUERY);
+ if (xNameAccess.is())
+ {
+ const uno::Sequence<OUString> aNames = xNameAccess->getElementNames();
+ for (OUString const& rName : aNames)
+ {
+ uno::Any aAny = xNameAccess->getByName(rName);
+ auto* pObjectInspectorNode = createNodeObjectForAny(
+ u"@" + rName, aAny, SfxResId(STR_PROPERTY_TYPE_IS_NAMED_CONTAINER));
+ lclAppendNodeToParent(pTree, pParent, pObjectInspectorNode);
+ }
+ }
+ }
+ catch (...)
+ {
+ }
+
+ try
+ {
+ const auto xIndexAccess = uno::Reference<container::XIndexAccess>(maAny, uno::UNO_QUERY);
+ if (xIndexAccess.is())
+ {
+ for (sal_Int32 nIndex = 0; nIndex < xIndexAccess->getCount(); ++nIndex)
+ {
+ uno::Any aAny = xIndexAccess->getByIndex(nIndex);
+ auto* pObjectInspectorNode
+ = createNodeObjectForAny(u"@" + OUString::number(nIndex), aAny,
+ SfxResId(STR_PROPERTY_TYPE_IS_INDEX_CONTAINER));
+ lclAppendNodeToParent(pTree, pParent, pObjectInspectorNode);
+ }
+ }
+ }
+ catch (...)
+ {
+ }
+
+ try
+ {
+ const auto xEnumAccess
+ = uno::Reference<container::XEnumerationAccess>(maAny, uno::UNO_QUERY);
+ if (xEnumAccess.is())
+ {
+ uno::Reference<container::XEnumeration> xEnumeration = xEnumAccess->createEnumeration();
+ if (xEnumeration.is())
+ {
+ for (sal_Int32 nIndex = 0; xEnumeration->hasMoreElements(); nIndex++)
+ {
+ uno::Any aAny = xEnumeration->nextElement();
+ auto* pObjectInspectorNode
+ = createNodeObjectForAny(u"@" + OUString::number(nIndex), aAny,
+ SfxResId(STR_PROPERTY_TYPE_IS_ENUMERATION));
+ lclAppendNodeToParent(pTree, pParent, pObjectInspectorNode);
+ }
+ }
+ }
+ }
+ catch (...)
+ {
+ }
+
+ auto xInvocationFactory = css::script::Invocation::create(mxContext);
+ uno::Sequence<uno::Any> aParameters = { maAny };
+ auto xInvocationInterface = xInvocationFactory->createInstanceWithArguments(aParameters);
+ if (!xInvocationInterface.is())
+ return;
+
+ uno::Reference<script::XInvocation2> xInvocation(xInvocationInterface, uno::UNO_QUERY);
+ if (!xInvocation.is())
+ return;
+
+ auto const& xInvocationAccess = xInvocation->getIntrospection();
+ if (!xInvocationAccess.is())
+ return;
+
+ uno::Sequence<script::InvocationInfo> aInvocationInfoSequence;
+ try
+ {
+ aInvocationInfoSequence = xInvocation->getInfo();
+ }
+ catch (...)
+ {
+ }
+
+ for (auto const& aInvocationInfo : std::as_const(aInvocationInfoSequence))
+ {
+ if (aInvocationInfo.eMemberType == script::MemberType_PROPERTY)
+ {
+ uno::Any aCurrentAny;
+ auto const& aPropertyName = aInvocationInfo.aName;
+
+ bool bIsAttribute = false;
+ bool bIsGetSetMethod = false;
+ bool bMethodGet = false;
+ bool bMethodSet = false;
+ bool bMethodIs = false;
+ try
+ {
+ aCurrentAny = xInvocation->getValue(aPropertyName);
+ bIsAttribute = xInvocationAccess->hasProperty(aPropertyName,
+ beans::PropertyConcept::ATTRIBUTES);
+ bIsGetSetMethod = xInvocationAccess->hasProperty(aPropertyName,
+ beans::PropertyConcept::METHODS);
+ if (bIsGetSetMethod)
+ {
+ bMethodGet = xInvocationAccess->hasMethod(u"get" + aPropertyName,
+ beans::MethodConcept::PROPERTY);
+ bMethodSet = xInvocationAccess->hasMethod(u"set" + aPropertyName,
+ beans::MethodConcept::PROPERTY);
+ bMethodIs = xInvocationAccess->hasMethod(u"is" + aPropertyName,
+ beans::MethodConcept::PROPERTY);
+ }
+ }
+ catch (...)
+ {
+ }
+
+ std::vector<OUString> aInfoCollection;
+ if (bIsAttribute)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_IS_ATTRIBUTE));
+ if (bIsGetSetMethod)
+ {
+ bool bHasGet = false;
+ OUString aString;
+ if (bMethodGet || bMethodIs)
+ {
+ aString += SfxResId(STR_PROPERTY_ATTRIBUTE_GET);
+ bHasGet = true;
+ }
+ if (bMethodSet)
+ {
+ if (bHasGet)
+ aString += u"+";
+ aString += SfxResId(STR_PROPERTY_ATTRIBUTE_SET);
+ }
+ aInfoCollection.push_back(aString);
+ if (bMethodSet && !bHasGet)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_WRITEONLY));
+ }
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::MAYBEVOID)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_MAYBEVOID));
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::READONLY)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_READONLY));
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::REMOVABLE)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_REMOVABLE));
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::BOUND)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_BOUND));
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::CONSTRAINED)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_CONSTRAINED));
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::TRANSIENT)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_TRANSIENT));
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::MAYBEAMBIGUOUS)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_MAYBEAMBIGUOUS));
+ if (aInvocationInfo.PropertyAttribute & beans::PropertyAttribute::MAYBEDEFAULT)
+ aInfoCollection.push_back(SfxResId(STR_PROPERTY_ATTRIBUTE_MAYBEDEFAULT));
+
+ bool bSet = false;
+ OUString aInfoString;
+ for (auto const& rString : aInfoCollection)
+ {
+ if (bSet)
+ aInfoString += ", ";
+ else
+ bSet = true;
+
+ aInfoString += rString;
+ }
+
+ auto* pObjectInspectorNode
+ = createNodeObjectForAny(aPropertyName, aCurrentAny, aInfoString);
+ if (pObjectInspectorNode)
+ lclAppendNodeToParent(pTree, pParent, pObjectInspectorNode);
+ }
+ }
+}
+
+void StructNode::fillChildren(std::unique_ptr<weld::TreeView>& pTree, const weld::TreeIter* pParent)
+{
+ auto xReflection = reflection::theCoreReflection::get(mxContext);
+ uno::Reference<reflection::XIdlClass> xClass
+ = xReflection->forName(maAny.getValueType().getTypeName());
+
+ const auto xFields = xClass->getFields();
+
+ for (auto const& xField : xFields)
+ {
+ OUString aFieldName = xField->getName();
+ uno::Any aFieldValue = xField->get(maAny);
+
+ auto* pObjectInspectorNode = createNodeObjectForAny(aFieldName, aFieldValue, "");
+ if (pObjectInspectorNode)
+ {
+ lclAppendNodeToParent(pTree, pParent, pObjectInspectorNode);
+ }
+ }
+}
+
+ObjectInspectorNodeInterface* BasicValueNode::createNodeObjectForAny(OUString const& rName,
+ const uno::Any& rAny,
+ OUString const& rInfo)
+{
+ switch (rAny.getValueType().getTypeClass())
+ {
+ case uno::TypeClass_INTERFACE:
+ return new GenericPropertiesNode(rName, rAny, rInfo, mxContext);
+
+ case uno::TypeClass_SEQUENCE:
+ return new SequenceNode(rName, rAny, rInfo, mxContext);
+
+ case uno::TypeClass_STRUCT:
+ return new StructNode(rName, rAny, rInfo, mxContext);
+
+ default:
+ break;
+ }
+
+ return new BasicValueNode(rName, rAny, rInfo, mxContext);
+}
+
+} // end anonymous namespace
+
+// Object inspector tree view helper functions
+namespace
+{
+ObjectInspectorNodeInterface* getSelectedNode(weld::TreeView const& rTreeView)
+{
+ OUString sID = rTreeView.get_selected_id();
+ if (sID.isEmpty())
+ return nullptr;
+
+ if (auto* pNode = weld::fromId<ObjectInspectorNodeInterface*>(sID))
+ return pNode;
+
+ return nullptr;
+}
+
+uno::Reference<uno::XInterface> getSelectedXInterface(weld::TreeView const& rTreeView)
+{
+ uno::Reference<uno::XInterface> xInterface;
+
+ if (auto* pNode = getSelectedNode(rTreeView))
+ {
+ if (auto* pBasicValueNode = dynamic_cast<BasicValueNode*>(pNode))
+ {
+ uno::Any aAny = pBasicValueNode->getAny();
+ xInterface.set(aAny, uno::UNO_QUERY);
+ }
+ }
+
+ return xInterface;
+}
+
+} // end anonymous namespace
+
+ObjectInspectorTreeHandler::ObjectInspectorTreeHandler(
+ std::unique_ptr<ObjectInspectorWidgets>& pObjectInspectorWidgets)
+ : mpObjectInspectorWidgets(pObjectInspectorWidgets)
+ , mxContext(comphelper::getProcessComponentContext())
+ , mxSorter(mxContext, Application::GetSettings().GetLanguageTag().getLocale())
+{
+ mpObjectInspectorWidgets->mpInterfacesTreeView->connect_expanding(
+ LINK(this, ObjectInspectorTreeHandler, ExpandingHandlerInterfaces));
+ mpObjectInspectorWidgets->mpServicesTreeView->connect_expanding(
+ LINK(this, ObjectInspectorTreeHandler, ExpandingHandlerServices));
+ mpObjectInspectorWidgets->mpPropertiesTreeView->connect_expanding(
+ LINK(this, ObjectInspectorTreeHandler, ExpandingHandlerProperties));
+ mpObjectInspectorWidgets->mpMethodsTreeView->connect_expanding(
+ LINK(this, ObjectInspectorTreeHandler, ExpandingHandlerMethods));
+
+ mpObjectInspectorWidgets->mpPropertiesTreeView->connect_popup_menu(
+ LINK(this, ObjectInspectorTreeHandler, PopupMenuHandler));
+
+ mpObjectInspectorWidgets->mpInterfacesTreeView->connect_changed(
+ LINK(this, ObjectInspectorTreeHandler, SelectionChanged));
+ mpObjectInspectorWidgets->mpServicesTreeView->connect_changed(
+ LINK(this, ObjectInspectorTreeHandler, SelectionChanged));
+ mpObjectInspectorWidgets->mpPropertiesTreeView->connect_changed(
+ LINK(this, ObjectInspectorTreeHandler, SelectionChanged));
+ mpObjectInspectorWidgets->mpMethodsTreeView->connect_changed(
+ LINK(this, ObjectInspectorTreeHandler, SelectionChanged));
+
+ mpObjectInspectorWidgets->mpInterfacesTreeView->make_sorted();
+ mpObjectInspectorWidgets->mpServicesTreeView->make_sorted();
+ mpObjectInspectorWidgets->mpPropertiesTreeView->make_sorted();
+ mpObjectInspectorWidgets->mpMethodsTreeView->make_sorted();
+
+ setSortFunction(mpObjectInspectorWidgets->mpInterfacesTreeView);
+ setSortFunction(mpObjectInspectorWidgets->mpServicesTreeView);
+ setSortFunction(mpObjectInspectorWidgets->mpPropertiesTreeView);
+ setSortFunction(mpObjectInspectorWidgets->mpMethodsTreeView);
+
+ mpObjectInspectorWidgets->mpInterfacesTreeView->connect_column_clicked(
+ LINK(this, ObjectInspectorTreeHandler, HeaderBarClick));
+ mpObjectInspectorWidgets->mpServicesTreeView->connect_column_clicked(
+ LINK(this, ObjectInspectorTreeHandler, HeaderBarClick));
+ mpObjectInspectorWidgets->mpPropertiesTreeView->connect_column_clicked(
+ LINK(this, ObjectInspectorTreeHandler, HeaderBarClick));
+ mpObjectInspectorWidgets->mpMethodsTreeView->connect_column_clicked(
+ LINK(this, ObjectInspectorTreeHandler, HeaderBarClick));
+
+ mpObjectInspectorWidgets->mpToolbar->connect_clicked(
+ LINK(this, ObjectInspectorTreeHandler, ToolbarButtonClicked));
+ mpObjectInspectorWidgets->mpToolbar->set_item_sensitive("inspect", false);
+ mpObjectInspectorWidgets->mpToolbar->set_item_sensitive("back", false);
+
+ mpObjectInspectorWidgets->mpNotebook->connect_leave_page(
+ LINK(this, ObjectInspectorTreeHandler, NotebookLeavePage));
+ mpObjectInspectorWidgets->mpNotebook->connect_enter_page(
+ LINK(this, ObjectInspectorTreeHandler, NotebookEnterPage));
+
+ auto nPropertiesDigitWidth
+ = mpObjectInspectorWidgets->mpPropertiesTreeView->get_approximate_digit_width();
+ std::vector<int> aPropertiesWidths(4, nPropertiesDigitWidth * 30);
+ mpObjectInspectorWidgets->mpPropertiesTreeView->set_column_fixed_widths(aPropertiesWidths);
+
+ auto nMethodsDigitWidth
+ = mpObjectInspectorWidgets->mpMethodsTreeView->get_approximate_digit_width();
+ std::vector<int> aMethodsWidths{ static_cast<int>(nMethodsDigitWidth * 30),
+ static_cast<int>(nMethodsDigitWidth * 15),
+ static_cast<int>(nMethodsDigitWidth * 30),
+ static_cast<int>(nMethodsDigitWidth * 50) };
+ mpObjectInspectorWidgets->mpMethodsTreeView->set_column_fixed_widths(aMethodsWidths);
+
+ mpObjectInspectorWidgets->mpPaned->set_position(160);
+}
+
+void ObjectInspectorTreeHandler::setSortFunction(std::unique_ptr<weld::TreeView>& pTreeView)
+{
+ pTreeView->set_sort_func(
+ [this, &pTreeView](const weld::TreeIter& rLeft, const weld::TreeIter& rRight) {
+ return compare(pTreeView, rLeft, rRight);
+ });
+}
+
+sal_Int32 ObjectInspectorTreeHandler::compare(std::unique_ptr<weld::TreeView>& pTreeView,
+ const weld::TreeIter& rLeft,
+ const weld::TreeIter& rRight)
+{
+ int nSortColumn = pTreeView->get_sort_column();
+
+ OUString sLeft = pTreeView->get_text(rLeft, nSortColumn);
+ OUString sRight = pTreeView->get_text(rRight, nSortColumn);
+ sal_Int32 nCompare = mxSorter.compare(sLeft, sRight);
+ return nCompare;
+}
+
+void ObjectInspectorTreeHandler::handleExpanding(std::unique_ptr<weld::TreeView>& pTreeView,
+ weld::TreeIter const& rParent)
+{
+ OUString sID = pTreeView->get_id(rParent);
+ if (sID.isEmpty())
+ return;
+
+ clearObjectInspectorChildren(pTreeView, rParent);
+ auto* pNode = weld::fromId<ObjectInspectorNodeInterface*>(sID);
+ pNode->fillChildren(pTreeView, &rParent);
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, ExpandingHandlerInterfaces, weld::TreeIter const&, rParent,
+ bool)
+{
+ handleExpanding(mpObjectInspectorWidgets->mpInterfacesTreeView, rParent);
+ return true;
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, ExpandingHandlerServices, weld::TreeIter const&, rParent,
+ bool)
+{
+ handleExpanding(mpObjectInspectorWidgets->mpServicesTreeView, rParent);
+ return true;
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, ExpandingHandlerProperties, weld::TreeIter const&, rParent,
+ bool)
+{
+ handleExpanding(mpObjectInspectorWidgets->mpPropertiesTreeView, rParent);
+ return true;
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, ExpandingHandlerMethods, weld::TreeIter const&, rParent, bool)
+{
+ handleExpanding(mpObjectInspectorWidgets->mpMethodsTreeView, rParent);
+ return true;
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, SelectionChanged, weld::TreeView&, rTreeView, void)
+{
+ bool bHaveNodeWithObject = false;
+ mpObjectInspectorWidgets->mpTextView->set_text("");
+ if (mpObjectInspectorWidgets->mpPropertiesTreeView.get() == &rTreeView)
+ {
+ auto* pNode = getSelectedNode(rTreeView);
+ if (auto* pBasicValueNode = dynamic_cast<BasicValueNode*>(pNode))
+ {
+ uno::Any aAny = pBasicValueNode->getAny();
+ uno::Reference<uno::XInterface> xInterface(aAny, uno::UNO_QUERY);
+ bHaveNodeWithObject = xInterface.is();
+ mpObjectInspectorWidgets->mpTextView->set_text(convertAnyToString(aAny, mxContext));
+ }
+ }
+
+ mpObjectInspectorWidgets->mpToolbar->set_item_sensitive("inspect", bHaveNodeWithObject);
+}
+
+static void updateOrder(const std::unique_ptr<weld::TreeView>& pTreeView, sal_Int32 nColumn)
+{
+ pTreeView->set_sort_column(nColumn);
+
+ bool bSortAtoZ = pTreeView->get_sort_order();
+ pTreeView->set_sort_order(!bSortAtoZ);
+ pTreeView->set_sort_indicator(!bSortAtoZ ? TRISTATE_TRUE : TRISTATE_FALSE, nColumn);
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, HeaderBarClick, int, nColumn, void)
+{
+ auto rPageId = mpObjectInspectorWidgets->mpNotebook->get_current_page_ident();
+
+ if (rPageId == "object_inspector_interfaces_tab")
+ updateOrder(mpObjectInspectorWidgets->mpInterfacesTreeView, nColumn);
+ else if (rPageId == "object_inspector_services_tab")
+ updateOrder(mpObjectInspectorWidgets->mpServicesTreeView, nColumn);
+ else if (rPageId == "object_inspector_properties_tab")
+ updateOrder(mpObjectInspectorWidgets->mpPropertiesTreeView, nColumn);
+ else if (rPageId == "object_inspector_methods_tab")
+ updateOrder(mpObjectInspectorWidgets->mpMethodsTreeView, nColumn);
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, PopupMenuHandler, const CommandEvent&, rCommandEvent, bool)
+{
+ if (rCommandEvent.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ auto xInterface = getSelectedXInterface(*mpObjectInspectorWidgets->mpPropertiesTreeView);
+ if (xInterface.is())
+ {
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(
+ mpObjectInspectorWidgets->mpPropertiesTreeView.get(), "sfx/ui/devtoolsmenu.ui"));
+ std::unique_ptr<weld::Menu> xMenu(xBuilder->weld_menu("inspect_menu"));
+
+ OString sCommand(
+ xMenu->popup_at_rect(mpObjectInspectorWidgets->mpPropertiesTreeView.get(),
+ tools::Rectangle(rCommandEvent.GetMousePosPixel(), Size(1, 1))));
+
+ if (sCommand == "inspect")
+ {
+ addToStack(uno::Any(xInterface));
+ inspectObject(xInterface);
+ }
+ }
+ return true;
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, ToolbarButtonClicked, const OString&, rSelectionId, void)
+{
+ if (rSelectionId == "inspect")
+ {
+ auto xInterface = getSelectedXInterface(*mpObjectInspectorWidgets->mpPropertiesTreeView);
+ if (xInterface.is())
+ {
+ addToStack(uno::Any(xInterface));
+ inspectObject(xInterface);
+ }
+ }
+ else if (rSelectionId == "back")
+ {
+ uno::Any aAny = popFromStack();
+ if (aAny.hasValue())
+ {
+ uno::Reference<uno::XInterface> xInterface(aAny, uno::UNO_QUERY);
+ inspectObject(xInterface);
+ }
+ }
+ else if (rSelectionId == "refresh")
+ {
+ auto rPageId = mpObjectInspectorWidgets->mpNotebook->get_current_page_ident();
+ NotebookEnterPage(rPageId);
+ }
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, NotebookEnterPage, const OString&, rPageId, void)
+{
+ uno::Any aAny = maInspectionStack.back();
+ if (!aAny.hasValue())
+ return;
+
+ uno::Reference<uno::XInterface> xInterface(aAny, uno::UNO_QUERY);
+ if (rPageId == "object_inspector_interfaces_tab")
+ {
+ mpObjectInspectorWidgets->mpInterfacesTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpInterfacesTreeView);
+ appendInterfaces(xInterface);
+ mpObjectInspectorWidgets->mpInterfacesTreeView->thaw();
+ }
+ else if (rPageId == "object_inspector_services_tab")
+ {
+ mpObjectInspectorWidgets->mpServicesTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpServicesTreeView);
+ appendServices(xInterface);
+ mpObjectInspectorWidgets->mpServicesTreeView->thaw();
+ }
+ else if (rPageId == "object_inspector_properties_tab")
+ {
+ mpObjectInspectorWidgets->mpPropertiesTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpPropertiesTreeView);
+ appendProperties(xInterface);
+ mpObjectInspectorWidgets->mpPropertiesTreeView->thaw();
+ }
+ else if (rPageId == "object_inspector_methods_tab")
+ {
+ mpObjectInspectorWidgets->mpMethodsTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpMethodsTreeView);
+ appendMethods(xInterface);
+ mpObjectInspectorWidgets->mpMethodsTreeView->thaw();
+ }
+}
+
+IMPL_LINK(ObjectInspectorTreeHandler, NotebookLeavePage, const OString&, rPageId, bool)
+{
+ if (rPageId == "object_inspector_interfaces_tab")
+ {
+ mpObjectInspectorWidgets->mpInterfacesTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpInterfacesTreeView);
+ mpObjectInspectorWidgets->mpInterfacesTreeView->thaw();
+ }
+ else if (rPageId == "object_inspector_services_tab")
+ {
+ mpObjectInspectorWidgets->mpServicesTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpServicesTreeView);
+ mpObjectInspectorWidgets->mpServicesTreeView->thaw();
+ }
+ else if (rPageId == "object_inspector_properties_tab")
+ {
+ mpObjectInspectorWidgets->mpPropertiesTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpPropertiesTreeView);
+ mpObjectInspectorWidgets->mpPropertiesTreeView->thaw();
+ }
+ else if (rPageId == "object_inspector_methods_tab")
+ {
+ mpObjectInspectorWidgets->mpMethodsTreeView->freeze();
+ clearAll(mpObjectInspectorWidgets->mpMethodsTreeView);
+ mpObjectInspectorWidgets->mpMethodsTreeView->thaw();
+ }
+ return true;
+}
+
+void ObjectInspectorTreeHandler::clearObjectInspectorChildren(
+ std::unique_ptr<weld::TreeView>& pTreeView, weld::TreeIter const& rParent)
+{
+ bool bChild = false;
+ do
+ {
+ bChild = pTreeView->iter_has_child(rParent);
+ if (bChild)
+ {
+ std::unique_ptr<weld::TreeIter> pChild = pTreeView->make_iterator(&rParent);
+ bChild = pTreeView->iter_children(*pChild);
+ if (bChild)
+ {
+ clearObjectInspectorChildren(pTreeView, *pChild);
+ OUString sID = pTreeView->get_id(*pChild);
+ auto* pEntry = weld::fromId<ObjectInspectorNodeInterface*>(sID);
+ delete pEntry;
+ pTreeView->remove(*pChild);
+ }
+ }
+ } while (bChild);
+}
+
+/** Deletes all the node objects in a tree view */
+void ObjectInspectorTreeHandler::clearAll(std::unique_ptr<weld::TreeView>& pTreeView)
+{
+ // destroy all ObjectInspectorNodes from the tree
+ pTreeView->all_foreach([&pTreeView](weld::TreeIter& rEntry) {
+ OUString sID = pTreeView->get_id(rEntry);
+ auto* pEntry = weld::fromId<ObjectInspectorNodeInterface*>(sID);
+ delete pEntry;
+ return false;
+ });
+ pTreeView->clear();
+}
+
+/** Append interfaces to the "interfaces" tree view */
+void ObjectInspectorTreeHandler::appendInterfaces(uno::Reference<uno::XInterface> const& xInterface)
+{
+ if (!xInterface.is())
+ return;
+
+ uno::Reference<lang::XTypeProvider> xTypeProvider(xInterface, uno::UNO_QUERY);
+ if (xTypeProvider.is())
+ {
+ const auto xSequenceTypes = xTypeProvider->getTypes();
+ for (auto const& xType : xSequenceTypes)
+ {
+ auto xClass = convertTypeToIdlClass(xType, mxContext);
+ lclAppendNode(mpObjectInspectorWidgets->mpInterfacesTreeView, new ClassNode(xClass));
+ }
+ }
+}
+
+/** Append services to the "services" tree view */
+void ObjectInspectorTreeHandler::appendServices(uno::Reference<uno::XInterface> const& xInterface)
+{
+ if (!xInterface.is())
+ return;
+
+ auto xServiceInfo = uno::Reference<lang::XServiceInfo>(xInterface, uno::UNO_QUERY);
+ const uno::Sequence<OUString> aServiceNames(xServiceInfo->getSupportedServiceNames());
+ for (auto const& aServiceName : aServiceNames)
+ {
+ lclAppendNode(mpObjectInspectorWidgets->mpServicesTreeView,
+ new SimpleStringNode(aServiceName));
+ }
+}
+
+/** Append properties to the "properties" tree view */
+void ObjectInspectorTreeHandler::appendProperties(uno::Reference<uno::XInterface> const& xInterface)
+{
+ if (!xInterface.is())
+ return;
+ GenericPropertiesNode aNode("", uno::Any(xInterface), "", mxContext);
+ aNode.fillChildren(mpObjectInspectorWidgets->mpPropertiesTreeView, nullptr);
+}
+
+/** Append methods to the "methods" tree view */
+void ObjectInspectorTreeHandler::appendMethods(uno::Reference<uno::XInterface> const& xInterface)
+{
+ if (!xInterface.is())
+ return;
+
+ uno::Reference<beans::XIntrospection> xIntrospection = beans::theIntrospection::get(mxContext);
+ auto xIntrospectionAccess = xIntrospection->inspect(uno::Any(xInterface));
+
+ const auto xMethods = xIntrospectionAccess->getMethods(beans::MethodConcept::ALL);
+ for (auto const& xMethod : xMethods)
+ {
+ lclAppendNode(mpObjectInspectorWidgets->mpMethodsTreeView, new MethodNode(xMethod));
+ }
+}
+
+// Update the back button state depending if there are objects in the stack
+void ObjectInspectorTreeHandler::updateBackButtonState()
+{
+ mpObjectInspectorWidgets->mpToolbar->set_item_sensitive("back", maInspectionStack.size() > 1);
+}
+
+// Clears all the objects from the stack
+void ObjectInspectorTreeHandler::clearStack()
+{
+ maInspectionStack.clear();
+ updateBackButtonState();
+}
+
+// Adds an object to the stack
+void ObjectInspectorTreeHandler::addToStack(css::uno::Any const& rAny)
+{
+ maInspectionStack.push_back(rAny);
+ updateBackButtonState();
+}
+
+// Removes an object from the back of the stack and return it
+css::uno::Any ObjectInspectorTreeHandler::popFromStack()
+{
+ maInspectionStack.pop_back();
+ uno::Any aAny = maInspectionStack.back();
+ updateBackButtonState();
+ return aAny;
+}
+
+// Inspect the input object in the object inspector
+void ObjectInspectorTreeHandler::inspectObject(uno::Reference<uno::XInterface> const& xInterface)
+{
+ if (!xInterface.is())
+ return;
+
+ // Set implementation name
+ OUString aImplementationName = getInterfaceImplementationClass(xInterface);
+ mpObjectInspectorWidgets->mpClassNameLabel->set_label(aImplementationName);
+ sal_Int32 nStrLen = aImplementationName.getLength();
+ sal_Int32 nDigitWidth
+ = mpObjectInspectorWidgets->mpClassNameLabel->get_approximate_digit_width();
+
+ //get_about_digit_width() returns an approximate value. To always see the full class name (nStrLen+2)
+ mpObjectInspectorWidgets->mpClassNameLabel->set_size_request((nStrLen + 2) * nDigitWidth, -1);
+
+ // Fire entering the current opened page manually
+ auto rPageId = mpObjectInspectorWidgets->mpNotebook->get_current_page_ident();
+ NotebookEnterPage(rPageId);
+}
+
+// Inspect the input object in the object inspector.
+// Make the input object the root of the stack (clear all other
+// objects from the stack).
+void ObjectInspectorTreeHandler::introspect(uno::Reference<uno::XInterface> const& xInterface)
+{
+ clearStack();
+ addToStack(uno::Any(xInterface));
+ inspectObject(xInterface);
+}
+
+void ObjectInspectorTreeHandler::dispose()
+{
+ // We need to clear all the nodes
+ clearAll(mpObjectInspectorWidgets->mpInterfacesTreeView);
+ clearAll(mpObjectInspectorWidgets->mpServicesTreeView);
+ clearAll(mpObjectInspectorWidgets->mpPropertiesTreeView);
+ clearAll(mpObjectInspectorWidgets->mpMethodsTreeView);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sfx2/source/devtools/SelectionChangeHandler.hxx b/sfx2/source/devtools/SelectionChangeHandler.hxx
new file mode 100644
index 000000000..15a2b3596
--- /dev/null
+++ b/sfx2/source/devtools/SelectionChangeHandler.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#pragma once
+
+#include <sfx2/devtools/DevelopmentToolDockingWindow.hxx>
+
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <comphelper/compbase.hxx>
+
+typedef comphelper::WeakComponentImplHelper<css::view::XSelectionChangeListener>
+ SelectionChangeHandlerInterfaceBase;
+
+/** Selection change handler to listen to document selection changes.
+ *
+ * Listens to the changes and notifies the docking window with a new
+ * selected object, when a change happens.
+ */
+class SelectionChangeHandler final : public SelectionChangeHandlerInterfaceBase
+{
+private:
+ css::uno::Reference<css::frame::XController> mxController;
+ VclPtr<DevelopmentToolDockingWindow> mpDockingWindow;
+
+public:
+ SelectionChangeHandler(const css::uno::Reference<css::frame::XController>& rxController,
+ DevelopmentToolDockingWindow* pDockingWindow)
+ : mxController(rxController)
+ , mpDockingWindow(pDockingWindow)
+ {
+ css::uno::Reference<css::view::XSelectionSupplier> xSupplier(mxController,
+ css::uno::UNO_QUERY);
+ xSupplier->addSelectionChangeListener(this);
+ }
+
+ ~SelectionChangeHandler() { mpDockingWindow.disposeAndClear(); }
+
+ virtual void SAL_CALL selectionChanged(const css::lang::EventObject& /*rEvent*/) override
+ {
+ css::uno::Reference<css::view::XSelectionSupplier> xSupplier(mxController,
+ css::uno::UNO_QUERY);
+ if (xSupplier.is())
+ {
+ css::uno::Any aAny = xSupplier->getSelection();
+ auto xInterface = aAny.get<css::uno::Reference<css::uno::XInterface>>();
+ mpDockingWindow->selectionChanged(xInterface);
+ }
+ }
+
+ void stopListening()
+ {
+ css::uno::Reference<css::view::XSelectionSupplier> xSupplier(mxController,
+ css::uno::UNO_QUERY);
+ xSupplier->removeSelectionChangeListener(this);
+ }
+
+ virtual void SAL_CALL disposing(const css::lang::EventObject& /*rEvent*/) override {}
+ using comphelper::WeakComponentImplHelperBase::disposing;
+
+private:
+ SelectionChangeHandler(const SelectionChangeHandler&) = delete;
+ SelectionChangeHandler& operator=(const SelectionChangeHandler&) = delete;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */