/* -*- 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/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace ::com::sun::star; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::script; using namespace ::com::sun::star::frame; using namespace ::com::sun::star::document; SfxStylesInfo_Impl::SfxStylesInfo_Impl() {} void SfxStylesInfo_Impl::init(const OUString& rModuleName, const css::uno::Reference< css::frame::XModel >& xModel) { m_aModuleName = rModuleName; m_xDoc = xModel; } const char CMDURL_STYLEPROT_ONLY[] = ".uno:StyleApply?"; const char CMDURL_SPART_ONLY [] = "Style:string="; const char CMDURL_FPART_ONLY [] = "FamilyName:string="; constexpr OUString STYLEPROP_UINAME = u"DisplayName"_ustr; constexpr OUString MACRO_SELECTOR_CONFIGNAME = u"MacroSelectorDialog"_ustr; constexpr OUString LAST_RUN_MACRO_INFO = u"LastRunMacro"_ustr; OUString SfxStylesInfo_Impl::generateCommand( std::u16string_view sFamily, std::u16string_view sStyle) { return OUString::Concat(".uno:StyleApply?Style:string=") + sStyle + "&FamilyName:string=" + sFamily; } bool SfxStylesInfo_Impl::parseStyleCommand(SfxStyleInfo_Impl& aStyle) { static const sal_Int32 LEN_STYLEPROT = strlen(CMDURL_STYLEPROT_ONLY); static const sal_Int32 LEN_SPART = strlen(CMDURL_SPART_ONLY); static const sal_Int32 LEN_FPART = strlen(CMDURL_FPART_ONLY); if (!aStyle.sCommand.startsWith(CMDURL_STYLEPROT_ONLY)) return false; aStyle.sFamily.clear(); aStyle.sStyle.clear(); sal_Int32 nCmdLen = aStyle.sCommand.getLength(); OUString sCmdArgs = aStyle.sCommand.copy(LEN_STYLEPROT, nCmdLen-LEN_STYLEPROT); sal_Int32 i = sCmdArgs.indexOf('&'); if (i<0) return false; OUString sArg = sCmdArgs.copy(0, i); if (sArg.startsWith(CMDURL_SPART_ONLY)) aStyle.sStyle = sArg.copy(LEN_SPART); else if (sArg.startsWith(CMDURL_FPART_ONLY)) aStyle.sFamily = sArg.copy(LEN_FPART); sArg = sCmdArgs.copy(i+1, sCmdArgs.getLength()-i-1); if (sArg.startsWith(CMDURL_SPART_ONLY)) aStyle.sStyle = sArg.copy(LEN_SPART); else if (sArg.startsWith(CMDURL_FPART_ONLY)) aStyle.sFamily = sArg.copy(LEN_FPART); return !(aStyle.sFamily.isEmpty() || aStyle.sStyle.isEmpty()); } void SfxStylesInfo_Impl::getLabel4Style(SfxStyleInfo_Impl& aStyle) { try { css::uno::Reference< css::style::XStyleFamiliesSupplier > xModel(m_xDoc, css::uno::UNO_QUERY); css::uno::Reference< css::container::XNameAccess > xFamilies; if (xModel.is()) xFamilies = xModel->getStyleFamilies(); css::uno::Reference< css::container::XNameAccess > xStyleSet; if (xFamilies.is()) xFamilies->getByName(aStyle.sFamily) >>= xStyleSet; css::uno::Reference< css::beans::XPropertySet > xStyle; if (xStyleSet.is()) xStyleSet->getByName(aStyle.sStyle) >>= xStyle; aStyle.sLabel.clear(); if (xStyle.is()) xStyle->getPropertyValue(STYLEPROP_UINAME) >>= aStyle.sLabel; } catch(const css::uno::RuntimeException&) { throw; } catch(const css::uno::Exception&) { aStyle.sLabel.clear(); } if (aStyle.sLabel.isEmpty()) { aStyle.sLabel = aStyle.sCommand; } } std::vector< SfxStyleInfo_Impl > SfxStylesInfo_Impl::getStyleFamilies() const { // It's an optional interface! css::uno::Reference< css::style::XStyleFamiliesSupplier > xModel(m_xDoc, css::uno::UNO_QUERY); if (!xModel.is()) return std::vector< SfxStyleInfo_Impl >(); css::uno::Reference< css::container::XNameAccess > xCont = xModel->getStyleFamilies(); const css::uno::Sequence< OUString > lFamilyNames = xCont->getElementNames(); std::vector< SfxStyleInfo_Impl > lFamilies; for (const auto& aFamily : lFamilyNames) { if ((aFamily == "CellStyles" && m_aModuleName != "com.sun.star.sheet.SpreadsheetDocument") || aFamily == "cell" || aFamily == "table" || aFamily == "Default") continue; SfxStyleInfo_Impl aFamilyInfo; aFamilyInfo.sFamily = aFamily; try { css::uno::Reference< css::beans::XPropertySet > xFamilyInfo; xCont->getByName(aFamilyInfo.sFamily) >>= xFamilyInfo; if (!xFamilyInfo.is()) { // TODO_AS currently there is no support for an UIName property .. use internal family name instead aFamilyInfo.sLabel = aFamilyInfo.sFamily; } else xFamilyInfo->getPropertyValue(STYLEPROP_UINAME) >>= aFamilyInfo.sLabel; } catch(const css::uno::RuntimeException&) { throw; } catch(const css::uno::Exception&) { return std::vector< SfxStyleInfo_Impl >(); } lFamilies.push_back(aFamilyInfo); } return lFamilies; } std::vector< SfxStyleInfo_Impl > SfxStylesInfo_Impl::getStyles(const OUString& sFamily) { css::uno::Sequence< OUString > lStyleNames; css::uno::Reference< css::style::XStyleFamiliesSupplier > xModel(m_xDoc, css::uno::UNO_QUERY_THROW); css::uno::Reference< css::container::XNameAccess > xFamilies = xModel->getStyleFamilies(); css::uno::Reference< css::container::XNameAccess > xStyleSet; try { xFamilies->getByName(sFamily) >>= xStyleSet; lStyleNames = xStyleSet->getElementNames(); } catch(const css::uno::RuntimeException&) { throw; } catch(const css::uno::Exception&) { return std::vector< SfxStyleInfo_Impl >(); } std::vector< SfxStyleInfo_Impl > lStyles; sal_Int32 c = lStyleNames.getLength(); sal_Int32 i = 0; for (i=0; i xStyle; xStyleSet->getByName(aStyleInfo.sStyle) >>= xStyle; if (!xStyle.is()) continue; xStyle->getPropertyValue(u"DisplayName"_ustr) >>= aStyleInfo.sLabel; } catch(const css::uno::RuntimeException&) { throw; } catch(const css::uno::Exception&) { continue; } lStyles.push_back(aStyleInfo); } return lStyles; } OUString CuiConfigFunctionListBox::GetCommandHelpText() { SfxGroupInfo_Impl *pData = weld::fromId(get_selected_id()); if (pData) { if ( pData->nKind == SfxCfgKind::FUNCTION_SLOT ) { return Application::GetHelp()->GetHelpText(pData->sCommand); } else if ( pData->nKind == SfxCfgKind::FUNCTION_SCRIPT ) { return pData->sHelpText; } } return OUString(); } OUString CuiConfigFunctionListBox::GetCurCommand() const { SfxGroupInfo_Impl *pData = weld::fromId(get_selected_id()); if (!pData) return OUString(); return pData->sCommand; } OUString CuiConfigFunctionListBox::GetCurLabel() const { SfxGroupInfo_Impl *pData = weld::fromId(get_selected_id()); if (!pData) return OUString(); if (!pData->sLabel.isEmpty()) return pData->sLabel; return pData->sCommand; } CuiConfigFunctionListBox::CuiConfigFunctionListBox(std::unique_ptr xTreeView) : m_xTreeView(std::move(xTreeView)) , m_xScratchIter(m_xTreeView->make_iterator()) { m_xTreeView->make_sorted(); m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 35, m_xTreeView->get_height_rows(9)); m_xTreeView->connect_query_tooltip(LINK(this, CuiConfigFunctionListBox, QueryTooltip)); } CuiConfigFunctionListBox::~CuiConfigFunctionListBox() { ClearAll(); } IMPL_LINK(CuiConfigFunctionListBox, QueryTooltip, const weld::TreeIter&, rIter, OUString) { SfxGroupInfo_Impl *pData = weld::fromId(m_xTreeView->get_id(rIter)); if (!pData) return OUString(); OUString aLabel = CuiResId(RID_CUISTR_COMMANDLABEL) + ": "; OUString aName = CuiResId(RID_CUISTR_COMMANDNAME) + ": "; OUString aTip = CuiResId(RID_CUISTR_COMMANDTIP) + ": "; return aLabel + pData->sLabel + "\n" + aName + pData->sCommand+ "\n" + aTip + pData->sTooltip; } void CuiConfigFunctionListBox::ClearAll() /* Description Deletes all entries in the FunctionListBox, all UserData and all possibly existing MacroInfo. */ { sal_uInt16 nCount = aArr.size(); for ( sal_uInt16 i=0; inKind == SfxCfgKind::FUNCTION_SCRIPT ) { OUString* pScriptURI = static_cast(pData->pObject); delete pScriptURI; } if ( pData->nKind == SfxCfgKind::GROUP_SCRIPTCONTAINER ) { XInterface* xi = static_cast(pData->pObject); if (xi != nullptr) { xi->release(); } } } aArr.clear(); m_xTreeView->clear(); } OUString CuiConfigFunctionListBox::GetSelectedScriptURI() const { SfxGroupInfo_Impl *pData = weld::fromId(get_selected_id()); if (pData && pData->nKind == SfxCfgKind::FUNCTION_SCRIPT) return *static_cast(pData->pObject); return OUString(); } struct SvxConfigGroupBoxResource_Impl { OUString m_sMyMacros; OUString m_sProdMacros; OUString m_sDlgMacros; OUString m_aStrGroupStyles; OUString m_aStrGroupSidebarDecks; SvxConfigGroupBoxResource_Impl(); }; SvxConfigGroupBoxResource_Impl::SvxConfigGroupBoxResource_Impl() : m_sMyMacros(CuiResId(RID_CUISTR_MYMACROS)), m_sProdMacros(CuiResId(RID_CUISTR_PRODMACROS)), m_sDlgMacros(CuiResId(RID_CUISTR_PRODMACROS)), m_aStrGroupStyles(CuiResId(RID_CUISTR_GROUP_STYLES)), m_aStrGroupSidebarDecks(CuiResId(RID_CUISTR_GROUP_SIDEBARDECKS)) { } void CuiConfigGroupListBox::SetStylesInfo(SfxStylesInfo_Impl* pStyles) { m_pStylesInfo = pStyles; } namespace { /** examines a component whether it supports XEmbeddedScripts, or provides access to such a component by implementing XScriptInvocationContext. @return the model which supports the embedded scripts, or if it cannot find such a model */ Reference< XModel > lcl_getDocumentWithScripts_throw( const Reference< XInterface >& _rxComponent ) { Reference< XEmbeddedScripts > xScripts( _rxComponent, UNO_QUERY ); if ( !xScripts.is() ) { Reference< XScriptInvocationContext > xContext( _rxComponent, UNO_QUERY ); if ( xContext.is() ) xScripts = xContext->getScriptContainer(); } return Reference< XModel >( xScripts, UNO_QUERY ); } Reference< XModel > lcl_getScriptableDocument_nothrow( const Reference< XFrame >& _rxFrame ) { Reference< XModel > xDocument; // examine our associated frame try { OSL_ENSURE( _rxFrame.is(), "lcl_getScriptableDocument_nothrow: you need to pass a frame to this dialog/tab page!" ); if ( _rxFrame.is() ) { // first try the model in the frame Reference< XController > xController( _rxFrame->getController(), UNO_SET_THROW ); xDocument = lcl_getDocumentWithScripts_throw( xController->getModel() ); if ( !xDocument.is() ) { // if there is no suitable document in the frame, try the controller xDocument = lcl_getDocumentWithScripts_throw( _rxFrame->getController() ); } } } catch( const Exception& ) { } return xDocument; } } CuiConfigGroupListBox::CuiConfigGroupListBox(std::unique_ptr xTreeView) : xImp(new SvxConfigGroupBoxResource_Impl()) , m_pFunctionListBox(nullptr) , m_pStylesInfo(nullptr) , m_xTreeView(std::move(xTreeView)) , m_xScratchIter(m_xTreeView->make_iterator()) { m_xTreeView->connect_expanding(LINK(this, CuiConfigGroupListBox, ExpandingHdl)); m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 35, m_xTreeView->get_height_rows(9)); } CuiConfigGroupListBox::~CuiConfigGroupListBox() { ClearAll(); } void CuiConfigGroupListBox::ClearAll() { sal_uInt16 nCount = aArr.size(); for ( sal_uInt16 i=0; inKind == SfxCfgKind::GROUP_STYLES && pData->pObject) { SfxStyleInfo_Impl* pStyle = static_cast(pData->pObject); delete pStyle; } else if (pData->nKind == SfxCfgKind::FUNCTION_SCRIPT && pData->pObject ) { OUString* pScriptURI = static_cast(pData->pObject); delete pScriptURI; } else if (pData->nKind == SfxCfgKind::GROUP_SCRIPTCONTAINER) { XInterface* xi = static_cast(pData->pObject); if (xi != nullptr) { xi->release(); } } } aArr.clear(); m_xTreeView->clear(); } sal_Int32 CuiConfigGroupListBox::InitModule() { try { // return the number of added groups css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider(m_xFrame, css::uno::UNO_QUERY_THROW); css::uno::Sequence< sal_Int16 > lGroups = xProvider->getSupportedCommandGroups(); sal_Int32 c1 = lGroups.getLength(); sal_Int32 i1 = 0; sal_Int32 nAddedGroups = 0; for (i1=0; i1getByName(sGroupID) >>= sGroupName; if (sGroupName.isEmpty()) continue; } catch(const css::container::NoSuchElementException&) { continue; } aArr.push_back( std::make_unique( SfxCfgKind::GROUP_FUNCTION, nGroupID ) ); m_xTreeView->append(weld::toId(aArr.back().get()), sGroupName); nAddedGroups++; } return nAddedGroups; } catch(const css::uno::RuntimeException&) { throw; } catch(const css::uno::Exception&) {} return 0; } void CuiConfigGroupListBox::FillScriptList(const css::uno::Reference< css::script::browse::XBrowseNode >& xRootNode, const weld::TreeIter* pParentEntry) { try { if ( xRootNode->hasChildNodes() ) { // tdf#120362: Don't ask to enable disabled Java when filling script list css::uno::ContextLayer layer(comphelper::NoEnableJavaInteractionContext()); const Sequence< Reference< browse::XBrowseNode > > children = xRootNode->getChildNodes(); bool bIsRootNode = false; OUString user(u"user"_ustr); OUString share(u"share"_ustr); if ( xRootNode->getName() == "Root" ) { bIsRootNode = true; } //To mimic current starbasic behaviour we //need to make sure that only the current document //is displayed in the config tree. Tests below //set the bDisplay flag to FALSE if the current //node is a first level child of the Root and is NOT //either the current document, user or share OUString currentDocTitle; Reference< XModel > xDocument( lcl_getScriptableDocument_nothrow( m_xFrame ) ); if ( xDocument.is() ) { currentDocTitle = ::comphelper::DocumentInfo::getDocumentTitle( xDocument ); } for ( Reference< browse::XBrowseNode > const & theChild : children ) { if (!theChild.is()) continue; bool bDisplay = true; OUString uiName = theChild->getName(); if ( bIsRootNode ) { if ( ! (uiName == user || uiName == share || uiName == currentDocTitle ) ) { bDisplay=false; } else { if ( uiName == user ) { uiName = xImp->m_sMyMacros; } else if ( uiName == share ) { uiName = xImp->m_sProdMacros; } } } if (theChild->getType() != browse::BrowseNodeTypes::SCRIPT && bDisplay ) { // We call acquire on the XBrowseNode so that it does not // get autodestructed and become invalid when accessed later. theChild->acquire(); bool bChildOnDemand = false; if ( theChild->hasChildNodes() ) { const Sequence< Reference< browse::XBrowseNode > > grandchildren = theChild->getChildNodes(); for ( const auto& rxNode : grandchildren ) { if (!rxNode.is()) continue; if ( rxNode->getType() == browse::BrowseNodeTypes::CONTAINER ) { bChildOnDemand = true; break; } } } OUString aImage = GetImage(theChild, m_xContext, bIsRootNode); aArr.push_back( std::make_unique(SfxCfgKind::GROUP_SCRIPTCONTAINER, 0, static_cast( theChild.get()))); OUString sId(weld::toId(aArr.back().get())); m_xTreeView->insert(pParentEntry, -1, &uiName, &sId, nullptr, nullptr, bChildOnDemand, m_xScratchIter.get()); m_xTreeView->set_image(*m_xScratchIter, aImage); } } } } catch (RuntimeException&) { // do nothing, the entry will not be displayed in the UI } } void CuiConfigGroupListBox::FillFunctionsList(const css::uno::Sequence& xCommands) { m_pFunctionListBox->freeze(); for (const auto & rInfo : xCommands) { auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rInfo.Command, m_sModuleLongName); OUString sUIName = MapCommand2UIName(rInfo.Command); aArr.push_back( std::make_unique( SfxCfgKind::FUNCTION_SLOT, 0 ) ); SfxGroupInfo_Impl* pGrpInfo = aArr.back().get(); pGrpInfo->sCommand = rInfo.Command; pGrpInfo->sLabel = sUIName; pGrpInfo->sTooltip = vcl::CommandInfoProvider::GetTooltipForCommand(rInfo.Command, aProperties, m_xFrame); m_pFunctionListBox->append(weld::toId(pGrpInfo), sUIName); } m_pFunctionListBox->thaw(); } void CuiConfigGroupListBox::Init(const css::uno::Reference< css::uno::XComponentContext >& xContext, const css::uno::Reference< css::frame::XFrame >& xFrame, const OUString& sModuleLongName, bool bEventMode) { m_xTreeView->freeze(); ClearAll(); // Remove all old entries from treelist box m_xContext = xContext; m_xFrame = xFrame; sal_Int32 nAddedGroups = 0; if( bEventMode ) { m_sModuleLongName = sModuleLongName; m_xGlobalCategoryInfo = css::ui::theUICategoryDescription::get( m_xContext ); m_xModuleCategoryInfo.set(m_xGlobalCategoryInfo->getByName(m_sModuleLongName), css::uno::UNO_QUERY_THROW); m_xUICmdDescription = css::frame::theUICommandDescription::get( m_xContext ); nAddedGroups = InitModule(); } SAL_INFO("cui.customize", "** ** About to initialise SF Scripts"); // Add Scripting Framework entries Reference< browse::XBrowseNode > rootNode; try { Reference< browse::XBrowseNodeFactory > xFac = browse::theBrowseNodeFactory::get( m_xContext ); rootNode.set( xFac->createView( browse::BrowseNodeFactoryViewTypes::MACROSELECTOR ) ); } catch( const Exception& ) { TOOLS_WARN_EXCEPTION("cui.customize", "Caught some exception whilst retrieving browse nodes from factory"); // TODO exception handling } m_xTreeView->thaw(); m_xTreeView->make_sorted(); m_xTreeView->make_unsorted(); m_xTreeView->freeze(); // add All Commands to the top if ( bEventMode && nAddedGroups ) { aArr.insert(aArr.begin(), std::make_unique(SfxCfgKind::GROUP_ALLFUNCTIONS, 0)); OUString sId(weld::toId(aArr.front().get())); OUString s(CuiResId(RID_CUISTR_ALLFUNCTIONS)); m_xTreeView->insert(nullptr, 0, &s, &sId, nullptr, nullptr, false, nullptr); } // add application macros to the end if ( rootNode.is() ) { if ( bEventMode ) { //We call acquire on the XBrowseNode so that it does not //get autodestructed and become invalid when accessed later. rootNode->acquire(); aArr.push_back( std::make_unique( SfxCfgKind::GROUP_SCRIPTCONTAINER, 0, static_cast(rootNode.get()))); OUString aTitle(xImp->m_sDlgMacros); OUString sId(weld::toId(aArr.back().get())); m_xTreeView->insert(nullptr, -1, &aTitle, &sId, nullptr, nullptr, true, nullptr); } else { //We are only showing scripts not slot APIs so skip //Root node and show location nodes FillScriptList(rootNode, nullptr); } } // add styles and sidebar decks to the end if ( bEventMode ) { aArr.push_back( std::make_unique( SfxCfgKind::GROUP_STYLES, 0, nullptr ) ); // TODO last parameter should contain user data OUString sStyle(xImp->m_aStrGroupStyles); OUString sId(weld::toId(aArr.back().get())); m_xTreeView->insert(nullptr, -1, &sStyle, &sId, nullptr, nullptr, true, nullptr); aArr.push_back( std::make_unique(SfxCfgKind::GROUP_SIDEBARDECKS, 0)); OUString sSidebarDecks(xImp->m_aStrGroupSidebarDecks); sId = weld::toId(aArr.back().get()); m_xTreeView->insert(nullptr, -1, &sSidebarDecks, &sId, nullptr, nullptr, false, nullptr); } m_xTreeView->thaw(); m_xTreeView->scroll_to_row(0); m_xTreeView->select(0); } OUString CuiConfigGroupListBox::GetImage( const Reference< browse::XBrowseNode >& node, Reference< XComponentContext > const & xCtx, bool bIsRootNode) { OUString aImage; if ( bIsRootNode ) { if (node->getName() == "user" || node->getName() == "share" ) { aImage = RID_CUIBMP_HARDDISK; } else { OUString factoryURL; OUString nodeName = node->getName(); Reference xDocumentModel = getDocumentModel(xCtx, nodeName ); if ( xDocumentModel.is() ) { Reference< frame::XModuleManager2 > xModuleManager( frame::ModuleManager::create(xCtx) ); // get the long name of the document: OUString appModule( xModuleManager->identify( xDocumentModel ) ); Sequence moduleDescr; Any aAny = xModuleManager->getByName(appModule); if( !( aAny >>= moduleDescr ) ) { throw RuntimeException(u"SFTreeListBox::Init: failed to get PropertyValue"_ustr); } for ( sal_Int32 pos = moduleDescr.getLength(); pos--; ) { if (moduleDescr[pos].Name == "ooSetupFactoryEmptyDocumentURL") { moduleDescr[pos].Value >>= factoryURL; SAL_INFO("cui.customize", "factory url for doc images is " << factoryURL); break; } } } if( !factoryURL.isEmpty() ) { aImage = SvFileInformationManager::GetFileImageId(INetURLObject(factoryURL)); } else { aImage = RID_CUIBMP_DOC; } } } else { if( node->getType() == browse::BrowseNodeTypes::SCRIPT ) aImage = RID_CUIBMP_MACRO; else aImage = RID_CUIBMP_LIB; } return aImage; } Reference< XInterface > CuiConfigGroupListBox::getDocumentModel( Reference< XComponentContext > const & xCtx, std::u16string_view docName ) { Reference< XInterface > xModel; Reference< frame::XDesktop2 > desktop = frame::Desktop::create( xCtx ); Reference< container::XEnumerationAccess > componentsAccess = desktop->getComponents(); Reference< container::XEnumeration > components = componentsAccess->createEnumeration(); while (components->hasMoreElements()) { Reference< frame::XModel > model( components->nextElement(), UNO_QUERY ); if ( model.is() ) { OUString sTdocUrl = ::comphelper::DocumentInfo::getDocumentTitle( model ); if( sTdocUrl == docName ) { xModel = model; break; } } } return xModel; } OUString CuiConfigGroupListBox::MapCommand2UIName(const OUString& sCommand) { OUString sUIName; try { css::uno::Reference< css::container::XNameAccess > xModuleConf; m_xUICmdDescription->getByName(m_sModuleLongName) >>= xModuleConf; if (xModuleConf.is()) { ::comphelper::SequenceAsHashMap lProps(xModuleConf->getByName(sCommand)); sUIName = lProps.getUnpackedValueOrDefault(u"Name"_ustr, OUString()); } } catch(const css::uno::RuntimeException&) { throw; } catch(css::uno::Exception&) { sUIName.clear(); } // fallback for missing UINames !? if (sUIName.isEmpty()) { sUIName = sCommand; } return sUIName; } void CuiConfigGroupListBox::GroupSelected() /* Description A function group or a basic module has been selected. All functions/macros are displayed in the functionlistbox. */ { std::unique_ptr xIter(m_xTreeView->make_iterator()); if (!m_xTreeView->get_selected(xIter.get())) return; SfxGroupInfo_Impl *pInfo = weld::fromId(m_xTreeView->get_id(*xIter)); m_pFunctionListBox->freeze(); m_pFunctionListBox->ClearAll(); switch ( pInfo->nKind ) { case SfxCfgKind::GROUP_ALLFUNCTIONS: { css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider( m_xFrame, UNO_QUERY ); bool bValidIter = m_xTreeView->get_iter_first(*xIter); while (bValidIter) { SfxGroupInfo_Impl *pCurrentInfo = weld::fromId(m_xTreeView->get_id(*xIter)); if (pCurrentInfo->nKind == SfxCfgKind::GROUP_FUNCTION) { css::uno::Sequence< css::frame::DispatchInformation > lCommands; try { lCommands = xProvider->getConfigurableDispatchInformation( pCurrentInfo->nUniqueID ); FillFunctionsList( lCommands ); } catch ( container::NoSuchElementException& ) { } } bValidIter = m_xTreeView->iter_next(*xIter); } break; } case SfxCfgKind::GROUP_FUNCTION : { sal_uInt16 nGroup = pInfo->nUniqueID; css::uno::Reference< css::frame::XDispatchInformationProvider > xProvider (m_xFrame, css::uno::UNO_QUERY_THROW); css::uno::Sequence< css::frame::DispatchInformation > lCommands = xProvider->getConfigurableDispatchInformation(nGroup); FillFunctionsList( lCommands ); break; } case SfxCfgKind::GROUP_SCRIPTCONTAINER: { Reference< browse::XBrowseNode > rootNode( static_cast< browse::XBrowseNode* >( pInfo->pObject ) ) ; try { if ( rootNode->hasChildNodes() ) { const Sequence< Reference< browse::XBrowseNode > > children = rootNode->getChildNodes(); for ( const Reference< browse::XBrowseNode >& childNode : children ) { if (!childNode.is()) continue; if (childNode->getType() == browse::BrowseNodeTypes::SCRIPT) { OUString uri, description; Reference < beans::XPropertySet >xPropSet( childNode, UNO_QUERY ); if (!xPropSet.is()) { continue; } Any value = xPropSet->getPropertyValue(u"URI"_ustr); value >>= uri; try { value = xPropSet->getPropertyValue(u"Description"_ustr); value >>= description; } catch (Exception &) { // do nothing, the description will be empty } OUString* pScriptURI = new OUString( uri ); OUString aImage = GetImage(childNode, Reference< XComponentContext >(), false); m_pFunctionListBox->aArr.push_back( std::make_unique( SfxCfgKind::FUNCTION_SCRIPT, 0, pScriptURI )); m_pFunctionListBox->aArr.back()->sCommand = uri; m_pFunctionListBox->aArr.back()->sLabel = childNode->getName(); m_pFunctionListBox->aArr.back()->sHelpText = description; OUString sId(weld::toId(m_pFunctionListBox->aArr.back().get())); m_pFunctionListBox->append(sId, childNode->getName(), aImage); } } } } catch (RuntimeException&) { // do nothing, the entry will not be displayed in the UI } break; } case SfxCfgKind::GROUP_STYLES : { SfxStyleInfo_Impl* pFamily = static_cast(pInfo->pObject); if (pFamily) { const std::vector< SfxStyleInfo_Impl > lStyles = m_pStylesInfo->getStyles(pFamily->sFamily); for (auto const& lStyle : lStyles) { SfxStyleInfo_Impl* pStyle = new SfxStyleInfo_Impl(lStyle); m_pFunctionListBox->aArr.push_back(std::make_unique(SfxCfgKind::GROUP_STYLES, 0, pStyle)); m_pFunctionListBox->aArr.back()->sCommand = pStyle->sCommand; m_pFunctionListBox->aArr.back()->sLabel = pStyle->sLabel; OUString sId(weld::toId(m_pFunctionListBox->aArr.back().get())); m_pFunctionListBox->append(sId, pStyle->sLabel); } } break; } case SfxCfgKind::GROUP_SIDEBARDECKS: { sfx2::sidebar::ResourceManager aResourceManager; sfx2::sidebar::Context aContext(m_sModuleLongName, OUString()); sfx2::sidebar::ResourceManager::DeckContextDescriptorContainer aDecks; aResourceManager.GetMatchingDecks(aDecks, aContext, false, m_xFrame->getController()); for (auto const& rDeck : aDecks) { const OUString sCommand = ".uno:SidebarDeck." + rDeck.msId; m_pFunctionListBox->aArr.push_back(std::make_unique( SfxCfgKind::GROUP_SIDEBARDECKS, 0, nullptr)); m_pFunctionListBox->aArr.back()->sCommand = sCommand; m_pFunctionListBox->aArr.back()->sLabel = rDeck.msId; m_pFunctionListBox->aArr.back()->sTooltip = vcl::CommandInfoProvider::GetCommandShortcut(sCommand, m_xFrame); m_pFunctionListBox->append(weld::toId(m_pFunctionListBox->aArr.back().get()), rDeck.msId); } break; } default: // Do nothing, the list box will stay empty SAL_INFO( "cui.customize", "Ignoring unexpected SfxCfgKind: " << static_cast(pInfo->nKind) ); break; } m_pFunctionListBox->thaw(); if (m_pFunctionListBox->n_children()) m_pFunctionListBox->select(0); } /* Description A basic or a library is opened. */ IMPL_LINK(CuiConfigGroupListBox, ExpandingHdl, const weld::TreeIter&, rIter, bool) { SfxGroupInfo_Impl *pInfo = weld::fromId(m_xTreeView->get_id(rIter)); switch ( pInfo->nKind ) { case SfxCfgKind::GROUP_SCRIPTCONTAINER: { if (!m_xTreeView->iter_has_child(rIter)) { Reference< browse::XBrowseNode > rootNode( static_cast< browse::XBrowseNode* >( pInfo->pObject ) ) ; FillScriptList(rootNode, &rIter); } break; } case SfxCfgKind::GROUP_STYLES: { if (!m_xTreeView->iter_has_child(rIter)) { const std::vector lStyleFamilies = m_pStylesInfo->getStyleFamilies(); for (auto const& lStyleFamily : lStyleFamilies) { SfxStyleInfo_Impl* pFamily = new SfxStyleInfo_Impl(lStyleFamily); aArr.push_back( std::make_unique( SfxCfgKind::GROUP_STYLES, 0, pFamily )); OUString sId(weld::toId(aArr.back().get())); m_xTreeView->insert(&rIter, -1, &pFamily->sLabel, &sId, nullptr, nullptr, false, nullptr); } } break; } default: OSL_FAIL( "Wrong group type!" ); break; } return true; } #if HAVE_FEATURE_SCRIPTING void CuiConfigGroupListBox::SelectMacro( const SfxMacroInfoItem *pItem ) { const std::u16string_view aLocation = pItem->GetLocation(); const std::u16string_view aLib = pItem->GetLib(); const std::u16string_view aModule = pItem->GetModule(); const std::u16string_view aMethod = pItem->GetMethod(); std::unique_ptr xIter = m_xTreeView->make_iterator(); if (!m_xTreeView->get_iter_first(*xIter)) return; do { OUString aEntryBas = m_xTreeView->get_text(*xIter); if (aEntryBas == xImp->m_sDlgMacros) { m_xTreeView->expand_row(*xIter); std::unique_ptr xLocationIter = m_xTreeView->make_iterator(xIter.get()); if (m_xTreeView->iter_children(*xLocationIter)) { do { if (aLocation != m_xTreeView->get_text(*xLocationIter)) continue; m_xTreeView->expand_row(*xLocationIter); std::unique_ptr xLibIter = m_xTreeView->make_iterator(xLocationIter.get()); if (m_xTreeView->iter_children(*xLibIter)) { do { OUString aEntryLib = m_xTreeView->get_text(*xLibIter); if (aEntryLib == aLib) { if (aModule.empty()) { m_xTreeView->scroll_to_row(*xLibIter); m_xTreeView->select(*xLibIter); GroupSelected(); weld::TreeView& rFunctionListBoxTreeView = m_pFunctionListBox->get_widget(); std::unique_ptr xFunctionListBoxIter = rFunctionListBoxTreeView.make_iterator(); if (!rFunctionListBoxTreeView.get_iter_first( *xFunctionListBoxIter)) return; do { OUString aEntryMethod = rFunctionListBoxTreeView.get_text( *xFunctionListBoxIter); if (aEntryMethod == aMethod) { rFunctionListBoxTreeView.scroll_to_row( *xFunctionListBoxIter); rFunctionListBoxTreeView.select(*xFunctionListBoxIter); return; } } while ( rFunctionListBoxTreeView.iter_next(*xFunctionListBoxIter)); return; } m_xTreeView->expand_row(*xLibIter); std::unique_ptr xModIter = m_xTreeView->make_iterator(xLibIter.get()); if (m_xTreeView->iter_children(*xModIter)) { do { OUString aEntryMod = m_xTreeView->get_text(*xModIter); if ( aEntryMod == aModule ) { m_xTreeView->expand_row(*xModIter); m_xTreeView->scroll_to_row(*xModIter); m_xTreeView->select(*xModIter); GroupSelected(); for (int i = 0, nCount = m_pFunctionListBox->n_children(); i < nCount; ++i) { OUString aEntryMethod = m_pFunctionListBox->get_text(i); if (aEntryMethod == aMethod) { m_pFunctionListBox->select(i); m_pFunctionListBox->scroll_to_row(i); return; } } m_xTreeView->collapse_row(*xModIter); } } while (m_xTreeView->iter_next_sibling(*xModIter)); } m_xTreeView->collapse_row(*xLibIter); } } while (m_xTreeView->iter_next_sibling(*xLibIter)); } m_xTreeView->collapse_row(*xLocationIter); } while (m_xTreeView->iter_next_sibling(*xLocationIter)); } // If the macro can't be located, preselect the "Application Macros" category: m_xTreeView->scroll_to_row(*xIter); m_xTreeView->select(*xIter); return; } } while (m_xTreeView->iter_next_sibling(*xIter)); } #endif /* * Implementation of SvxScriptSelectorDialog * * This dialog is used for selecting Slot API commands * and Scripting Framework Scripts. */ SvxScriptSelectorDialog::SvxScriptSelectorDialog( weld::Window* pParent, const css::uno::Reference< css::frame::XFrame >& xFrame) : GenericDialogController(pParent, u"cui/ui/macroselectordialog.ui"_ustr, u"MacroSelectorDialog"_ustr) , m_xDialogDescription(m_xBuilder->weld_label(u"helpmacro"_ustr)) , m_xCategories(new CuiConfigGroupListBox(m_xBuilder->weld_tree_view(u"categories"_ustr))) , m_xCommands(new CuiConfigFunctionListBox(m_xBuilder->weld_tree_view(u"commands"_ustr))) , m_xLibraryFT(m_xBuilder->weld_label(u"libraryft"_ustr)) , m_xMacronameFT(m_xBuilder->weld_label(u"macronameft"_ustr)) , m_xOKButton(m_xBuilder->weld_button(u"ok"_ustr)) , m_xCancelButton(m_xBuilder->weld_button(u"cancel"_ustr)) , m_xDescriptionText(m_xBuilder->weld_text_view(u"description"_ustr)) , m_xDescriptionFrame(m_xBuilder->weld_frame(u"descriptionframe"_ustr)) { m_xCancelButton->show(); m_xDialogDescription->show(); m_xOKButton->show(); m_xLibraryFT->set_visible(true); m_xMacronameFT->set_visible(true); const OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame)); m_xCategories->SetFunctionListBox(m_xCommands.get()); m_xCategories->Init(comphelper::getProcessComponentContext(), xFrame, aModuleName, /*bShowSlots*/false); m_xCategories->connect_changed( LINK( this, SvxScriptSelectorDialog, SelectHdl ) ); m_xCommands->connect_changed( LINK( this, SvxScriptSelectorDialog, SelectHdl ) ); m_xCommands->connect_row_activated( LINK( this, SvxScriptSelectorDialog, FunctionDoubleClickHdl ) ); m_xCommands->connect_popup_menu( LINK( this, SvxScriptSelectorDialog, ContextMenuHdl ) ); m_xOKButton->connect_clicked( LINK( this, SvxScriptSelectorDialog, ClickHdl ) ); m_xCancelButton->connect_clicked( LINK( this, SvxScriptSelectorDialog, ClickHdl ) ); m_sDefaultDesc = m_xDescriptionText->get_text(); // Support style commands uno::Reference xController; uno::Reference xModel; if (xFrame.is()) xController = xFrame->getController(); if (xController.is()) xModel = xController->getModel(); m_aStylesInfo.init(aModuleName, xModel); m_xCategories->SetStylesInfo(&m_aStylesInfo); // The following call is a workaround to make scroll_to_row work as expected in kf5/x11 m_xDialog->resize_to_request(); LoadLastUsedMacro(); UpdateUI(); if (comphelper::LibreOfficeKit::isActive()) m_xDescriptionFrame->hide(); } SvxScriptSelectorDialog::~SvxScriptSelectorDialog() { } IMPL_LINK(SvxScriptSelectorDialog, SelectHdl, weld::TreeView&, rCtrl, void) { if (&rCtrl == &m_xCategories->get_widget()) { m_xCategories->GroupSelected(); } UpdateUI(); } IMPL_LINK_NOARG(SvxScriptSelectorDialog, FunctionDoubleClickHdl, weld::TreeView&, bool) { if (m_xOKButton->get_sensitive()) ClickHdl(*m_xOKButton); return true; } IMPL_LINK(SvxScriptSelectorDialog, ContextMenuHdl, const CommandEvent&, rCEvt, bool) { weld::TreeView& xTreeView = m_xCommands->get_widget(); if (rCEvt.GetCommand() != CommandEventId::ContextMenu || !xTreeView.n_children()) return false; std::unique_ptr xBuilder(Application::CreateBuilder(&xTreeView, u"modules/BasicIDE/ui/sortmenu.ui"_ustr)); std::unique_ptr xPopup(xBuilder->weld_menu(u"sortmenu"_ustr)); std::unique_ptr xDropMenu(xBuilder->weld_menu(u"sortsubmenu"_ustr)); xDropMenu->set_active(u"alphabetically"_ustr, xTreeView.get_sort_order()); xDropMenu->set_active(u"properorder"_ustr, !xTreeView.get_sort_order()); OUString sCommand(xPopup->popup_at_rect(&xTreeView, tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1)))); if (sCommand == "alphabetically") { xTreeView.make_sorted(); } else if (sCommand == "properorder") { xTreeView.make_unsorted(); m_xCategories->GroupSelected(); } else if (!sCommand.isEmpty()) { SAL_WARN("cui.customize", "Unknown context menu action: " << sCommand ); } return true; } // Check if command is selected and enable the OK button accordingly // Grab the help text for this id if available and update the description field void SvxScriptSelectorDialog::UpdateUI() { OUString url = GetScriptURL(); if ( !url.isEmpty() ) { OUString sMessage = m_xCommands->GetCommandHelpText(); m_xDescriptionText->set_text(sMessage.isEmpty() ? m_sDefaultDesc : sMessage); m_xOKButton->set_sensitive(true); } else { m_xDescriptionText->set_text(m_sDefaultDesc); m_xOKButton->set_sensitive(false); } } IMPL_LINK(SvxScriptSelectorDialog, ClickHdl, weld::Button&, rButton, void) { if (&rButton == m_xCancelButton.get()) { m_xDialog->response(RET_CANCEL); } else if (&rButton == m_xOKButton.get()) { SaveLastUsedMacro(); m_xDialog->response(RET_OK); } } void SvxScriptSelectorDialog::SetRunLabel() { m_xOKButton->set_label(CuiResId(RID_CUISTR_SELECTOR_RUN)); } OUString SvxScriptSelectorDialog::GetScriptURL() const { OUString result; std::unique_ptr xIter = m_xCommands->make_iterator(); if (m_xCommands->get_selected(xIter.get())) { SfxGroupInfo_Impl *pData = weld::fromId(m_xCommands->get_id(*xIter)); if ( ( pData->nKind == SfxCfgKind::FUNCTION_SLOT ) || ( pData->nKind == SfxCfgKind::FUNCTION_SCRIPT ) || ( pData->nKind == SfxCfgKind::GROUP_STYLES ) ) { result = pData->sCommand; } } return result; } void SvxScriptSelectorDialog::SaveLastUsedMacro() { // Gets the current selection in the dialog as a series of selected entries OUString sMacroInfo; sMacroInfo = m_xCommands->get_selected_text(); weld::TreeView& xCategories = m_xCategories->get_widget(); std::unique_ptr xIter = xCategories.make_iterator(); if (!xCategories.get_selected(xIter.get())) return; do { sMacroInfo = xCategories.get_text(*xIter) + "|" + sMacroInfo; } while (xCategories.iter_parent(*xIter)); SvtViewOptions( EViewType::Dialog, MACRO_SELECTOR_CONFIGNAME ).SetUserItem( LAST_RUN_MACRO_INFO, Any(sMacroInfo)); } void SvxScriptSelectorDialog::LoadLastUsedMacro() { SvtViewOptions aDlgOpt( EViewType::Dialog, MACRO_SELECTOR_CONFIGNAME ); if (!aDlgOpt.Exists()) return; OUString sMacroInfo; aDlgOpt.GetUserItem(LAST_RUN_MACRO_INFO) >>= sMacroInfo; if (sMacroInfo.isEmpty()) return; // Counts how many entries exist in the macro info string sal_Int16 nInfoParts = 0; sal_Int16 nLastIndex = sMacroInfo.indexOf('|'); if (nLastIndex > -1) { nInfoParts = 1; while ( nLastIndex != -1 ) { nInfoParts++; nLastIndex = sMacroInfo.indexOf('|', nLastIndex + 1); } } weld::TreeView& xCategories = m_xCategories->get_widget(); std::unique_ptr xIter = xCategories.make_iterator(); if (!xCategories.get_iter_first(*xIter)) return; // Expand the nodes in the category tree OUString sNodeToExpand; bool bIsIterValid; sal_Int16 nOpenedNodes = 0; for (sal_Int16 i=0; iGroupSelected(); // Select the macro in the command tree weld::TreeView& xCommands = m_xCommands->get_widget(); xIter = xCommands.make_iterator(); if (!xCommands.get_iter_first(*xIter)) return; OUString sMacroName = sMacroInfo.getToken(nInfoParts - 1, '|'); bIsIterValid = true; while (bIsIterValid && xCommands.get_text(*xIter) != sMacroName) bIsIterValid = xCommands.iter_next_sibling(*xIter); if (bIsIterValid) { xCommands.scroll_to_row(*xIter); xCommands.select(*xIter); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */