From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- .../source/devtools/ObjectInspectorTreeHandler.cxx | 1384 ++++++++++++++++++++ 1 file changed, 1384 insertions(+) create mode 100644 sfx2/source/devtools/ObjectInspectorTreeHandler.cxx (limited to 'sfx2/source/devtools/ObjectInspectorTreeHandler.cxx') 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 + +#include +#include +#include +#include "DevToolsStrings.hrc" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include + +#include +#include + +using namespace css; + +namespace +{ +constexpr OUStringLiteral constTypeDescriptionManagerSingletonName + = u"/singletons/com.sun.star.reflection.theTypeDescriptionManager"; + +OUString enumValueToEnumName(uno::Any const& aValue, + uno::Reference const& xContext) +{ + sal_Int32 nIntValue = 0; + if (!cppu::enum2int(nIntValue, aValue)) + return OUString(); + + uno::Reference xManager; + xManager.set(xContext->getValueByName(constTypeDescriptionManagerSingletonName), + uno::UNO_QUERY); + + uno::Reference xTypeDescription; + xTypeDescription.set(xManager->getByHierarchicalName(aValue.getValueType().getTypeName()), + uno::UNO_QUERY); + + const uno::Sequence aValues = xTypeDescription->getEnumValues(); + sal_Int32 nValuesIndex = std::find(aValues.begin(), aValues.end(), nIntValue) - aValues.begin(); + uno::Sequence aNames = xTypeDescription->getEnumNames(); + return aNames[nValuesIndex]; +} + +OUString getInterfaceImplementationClass(uno::Reference const& xInterface) +{ + auto xServiceInfo = uno::Reference(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& 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(); + aRetStr = bBool ? SfxResId(STR_ANY_VALUE_TRUE) : SfxResId(STR_ANY_VALUE_FALSE); + break; + } + case uno::TypeClass_CHAR: + { + sal_Unicode aChar = aValue.get(); + aRetStr = OUString::number(aChar); + break; + } + case uno::TypeClass_STRING: + { + aRetStr = u"\"" + aValue.get() + u"\""; + break; + } + case uno::TypeClass_FLOAT: + { + auto aNumber = aValue.get(); + aRetStr = OUString::number(aNumber); + break; + } + case uno::TypeClass_DOUBLE: + { + auto aNumber = aValue.get(); + aRetStr = OUString::number(aNumber); + break; + } + case uno::TypeClass_BYTE: + { + auto aNumber = aValue.get(); + aRetStr = OUString::number(aNumber); + break; + } + case uno::TypeClass_SHORT: + { + auto aNumber = aValue.get(); + aRetStr = OUString::number(aNumber); + break; + } + case uno::TypeClass_LONG: + { + auto aNumber = aValue.get(); + aRetStr = OUString::number(aNumber); + break; + } + case uno::TypeClass_HYPER: + { + auto aNumber = aValue.get(); + aRetStr = OUString::number(aNumber); + break; + } + case uno::TypeClass_UNSIGNED_SHORT: + { + auto aNumber = aValue.get(); + aRetStr = OUString::number(aNumber); + break; + } + case uno::TypeClass_UNSIGNED_LONG: + { + auto aNumber = aValue.get(); + aRetStr = OUString::number(aNumber); + break; + } + case uno::TypeClass_UNSIGNED_HYPER: + { + auto aNumber = aValue.get(); + aRetStr = OUString::number(aNumber); + break; + } + case uno::TypeClass_TYPE: + { + auto aType = aValue.get(); + 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 const& xInterface, + const uno::Reference& xContext) +{ + uno::Reference xNamed(xInterface, uno::UNO_QUERY); + if (xNamed.is()) + return xNamed->getName(); + + auto xInvocationFactory = css::script::Invocation::create(xContext); + uno::Sequence aParameters = { uno::Any(xInterface) }; + auto xInvocationInterface = xInvocationFactory->createInstanceWithArguments(aParameters); + if (xInvocationInterface.is()) + { + uno::Reference 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(); + } + } + return OUString(); +} + +OUString convertAnyToString(const uno::Any& aValue, + const uno::Reference& 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 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& 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 +convertTypeToIdlClass(const uno::Type& rType, + const uno::Reference& 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& rTree, const weld::TreeIter* pParent) + = 0; + + // fill any additional column values for the current tree view node + virtual std::vector> getColumnValues() + { + return std::vector>(); + } +}; + +// appends the node to the root of the tree view +OUString lclAppendNode(const std::unique_ptr& pTree, + ObjectInspectorNodeInterface* pEntry) +{ + OUString sName = pEntry->getObjectName(); + OUString sId(weld::toId(pEntry)); + std::unique_ptr 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& pTree, + const weld::TreeIter* pParent, ObjectInspectorNodeInterface* pEntry) +{ + OUString sName = pEntry->getObjectName(); + OUString sId(weld::toId(pEntry)); + std::unique_ptr 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& /*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 mxMethod; + +public: + MethodNode(uno::Reference const& xMethod) + : mxMethod(xMethod) + { + } + + OUString getObjectName() override { return mxMethod->getName(); } + + static OUString simpleTypeName(uno::Reference 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> 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& /*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 mxClass; + + static bool isXInterface(uno::Reference const& xClass) + { + return xClass->getName() == "com.sun.star.uno.XInterface"; + } + +public: + ClassNode(uno::Reference 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& 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 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 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 xInterface(maAny, uno::UNO_QUERY); + return xInterface.is(); + } + case uno::TypeClass_SEQUENCE: + return true; + default: + break; + } + } + return false; + } + + std::vector> 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 const& xContext) + : BasicValueNode(rName, rAny, rInfo, xContext) + { + } + + void fillChildren(std::unique_ptr& 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 const& xContext) + : BasicValueNode(rName, rAny, rInfo, xContext) + { + } + + bool shouldShowExpander() override { return true; } + + void fillChildren(std::unique_ptr& pTree, + const weld::TreeIter* pParent) override; +}; + +/** Node represents a sequence */ +class SequenceNode : public BasicValueNode +{ + uno::Reference mxIdlArray; + +public: + SequenceNode(OUString const& rName, uno::Any const& rAny, OUString const& rInfo, + uno::Reference 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& 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> 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& pTree, + const weld::TreeIter* pParent) +{ + if (!maAny.hasValue()) + return; + + try + { + const auto xNameAccess = uno::Reference(maAny, uno::UNO_QUERY); + if (xNameAccess.is()) + { + const uno::Sequence 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(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(maAny, uno::UNO_QUERY); + if (xEnumAccess.is()) + { + uno::Reference 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 aParameters = { maAny }; + auto xInvocationInterface = xInvocationFactory->createInstanceWithArguments(aParameters); + if (!xInvocationInterface.is()) + return; + + uno::Reference xInvocation(xInvocationInterface, uno::UNO_QUERY); + if (!xInvocation.is()) + return; + + auto const& xInvocationAccess = xInvocation->getIntrospection(); + if (!xInvocationAccess.is()) + return; + + uno::Sequence 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 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& pTree, const weld::TreeIter* pParent) +{ + auto xReflection = reflection::theCoreReflection::get(mxContext); + uno::Reference 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(sID)) + return pNode; + + return nullptr; +} + +uno::Reference getSelectedXInterface(weld::TreeView const& rTreeView) +{ + uno::Reference xInterface; + + if (auto* pNode = getSelectedNode(rTreeView)) + { + if (auto* pBasicValueNode = dynamic_cast(pNode)) + { + uno::Any aAny = pBasicValueNode->getAny(); + xInterface.set(aAny, uno::UNO_QUERY); + } + } + + return xInterface; +} + +} // end anonymous namespace + +ObjectInspectorTreeHandler::ObjectInspectorTreeHandler( + std::unique_ptr& 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 aPropertiesWidths(4, nPropertiesDigitWidth * 30); + mpObjectInspectorWidgets->mpPropertiesTreeView->set_column_fixed_widths(aPropertiesWidths); + + auto nMethodsDigitWidth + = mpObjectInspectorWidgets->mpMethodsTreeView->get_approximate_digit_width(); + std::vector aMethodsWidths{ static_cast(nMethodsDigitWidth * 30), + static_cast(nMethodsDigitWidth * 15), + static_cast(nMethodsDigitWidth * 30), + static_cast(nMethodsDigitWidth * 50) }; + mpObjectInspectorWidgets->mpMethodsTreeView->set_column_fixed_widths(aMethodsWidths); + + mpObjectInspectorWidgets->mpPaned->set_position(160); +} + +void ObjectInspectorTreeHandler::setSortFunction(std::unique_ptr& 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& 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& pTreeView, + weld::TreeIter const& rParent) +{ + OUString sID = pTreeView->get_id(rParent); + if (sID.isEmpty()) + return; + + clearObjectInspectorChildren(pTreeView, rParent); + auto* pNode = weld::fromId(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(pNode)) + { + uno::Any aAny = pBasicValueNode->getAny(); + uno::Reference 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& 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 xBuilder(Application::CreateBuilder( + mpObjectInspectorWidgets->mpPropertiesTreeView.get(), "sfx/ui/devtoolsmenu.ui")); + std::unique_ptr 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 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 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& pTreeView, weld::TreeIter const& rParent) +{ + bool bChild = false; + do + { + bChild = pTreeView->iter_has_child(rParent); + if (bChild) + { + std::unique_ptr 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(sID); + delete pEntry; + pTreeView->remove(*pChild); + } + } + } while (bChild); +} + +/** Deletes all the node objects in a tree view */ +void ObjectInspectorTreeHandler::clearAll(std::unique_ptr& pTreeView) +{ + // destroy all ObjectInspectorNodes from the tree + pTreeView->all_foreach([&pTreeView](weld::TreeIter& rEntry) { + OUString sID = pTreeView->get_id(rEntry); + auto* pEntry = weld::fromId(sID); + delete pEntry; + return false; + }); + pTreeView->clear(); +} + +/** Append interfaces to the "interfaces" tree view */ +void ObjectInspectorTreeHandler::appendInterfaces(uno::Reference const& xInterface) +{ + if (!xInterface.is()) + return; + + uno::Reference 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 const& xInterface) +{ + if (!xInterface.is()) + return; + + auto xServiceInfo = uno::Reference(xInterface, uno::UNO_QUERY); + const uno::Sequence 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 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 const& xInterface) +{ + if (!xInterface.is()) + return; + + uno::Reference 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 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 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: */ -- cgit v1.2.3