diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sfx2/source/devtools | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.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 '')
-rw-r--r-- | sfx2/source/devtools/DevToolsStrings.hrc | 73 | ||||
-rw-r--r-- | sfx2/source/devtools/DevelopmentToolChildWindow.cxx | 30 | ||||
-rw-r--r-- | sfx2/source/devtools/DevelopmentToolDockingWindow.cxx | 155 | ||||
-rw-r--r-- | sfx2/source/devtools/DocumentModelTreeHandler.cxx | 840 | ||||
-rw-r--r-- | sfx2/source/devtools/ObjectInspectorTreeHandler.cxx | 1384 | ||||
-rw-r--r-- | sfx2/source/devtools/SelectionChangeHandler.hxx | 74 |
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: */ |