/* -*- 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: */