diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sfx2/source/bastyp/fltfnc.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sfx2/source/bastyp/fltfnc.cxx')
-rw-r--r-- | sfx2/source/bastyp/fltfnc.cxx | 1111 |
1 files changed, 1111 insertions, 0 deletions
diff --git a/sfx2/source/bastyp/fltfnc.cxx b/sfx2/source/bastyp/fltfnc.cxx new file mode 100644 index 000000000..56638fc1c --- /dev/null +++ b/sfx2/source/bastyp/fltfnc.cxx @@ -0,0 +1,1111 @@ +/* -*- 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 <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XEnumeration.hpp> +#include <com/sun/star/document/XTypeDetection.hpp> +#include <com/sun/star/container/XContainerQuery.hpp> +#include <com/sun/star/io/XInputStream.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include <comphelper/sequenceashashmap.hxx> + +#include <sot/exchange.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <rtl/ustring.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <svl/stritem.hxx> + +#include <comphelper/processfactory.hxx> + +#include <sal/types.h> +#include <com/sun/star/uno/Reference.hxx> +#include <unotools/moduleoptions.hxx> +#include <unotools/mediadescriptor.hxx> +#include <tools/urlobj.hxx> + +#include <unotools/syslocale.hxx> +#include <unotools/charclass.hxx> + +#include <sfx2/docfilt.hxx> +#include <sfx2/fcontnr.hxx> +#include <sfxtypes.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/strings.hrc> +#include <sfx2/sfxresid.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/sfxsids.hrc> +#include "fltlst.hxx" +#include <arrdecl.hxx> + +#include <vector> +#include <memory> + +#if defined(DBG_UTIL) +unsigned SfxStack::nLevel = 0; +#endif + +using namespace com::sun::star; + +static SfxFilterList_Impl* pFilterArr = nullptr; +static bool bFirstRead = true; + +static void CreateFilterArr() +{ + static SfxFilterList_Impl theSfxFilterArray; + pFilterArr = &theSfxFilterArray; + static SfxFilterListener theSfxFilterListener; +} + +static OUString ToUpper_Impl( const OUString &rStr ) +{ + return SvtSysLocale().GetCharClass().uppercase( rStr ); +} + +class SfxFilterContainer_Impl +{ +public: + OUString aName; + + explicit SfxFilterContainer_Impl( const OUString& rName ) + : aName( rName ) + { + } +}; + +std::shared_ptr<const SfxFilter> SfxFilterContainer::GetFilter4EA(const OUString& rEA, SfxFilterFlags nMust, SfxFilterFlags nDont) const +{ + SfxFilterMatcher aMatch(pImpl->aName); + return aMatch.GetFilter4EA(rEA, nMust, nDont); +} + +std::shared_ptr<const SfxFilter> SfxFilterContainer::GetFilter4Extension(const OUString& rExt, SfxFilterFlags nMust, SfxFilterFlags nDont) const +{ + SfxFilterMatcher aMatch(pImpl->aName); + return aMatch.GetFilter4Extension(rExt, nMust, nDont); +} + +std::shared_ptr<const SfxFilter> SfxFilterContainer::GetFilter4FilterName(const OUString& rName, SfxFilterFlags nMust, SfxFilterFlags nDont) const +{ + SfxFilterMatcher aMatch(pImpl->aName); + return aMatch.GetFilter4FilterName(rName, nMust, nDont); +} + +std::shared_ptr<const SfxFilter> SfxFilterContainer::GetAnyFilter( SfxFilterFlags nMust, SfxFilterFlags nDont ) const +{ + SfxFilterMatcher aMatch( pImpl->aName ); + return aMatch.GetAnyFilter( nMust, nDont ); +} + + +SfxFilterContainer::SfxFilterContainer( const OUString& rName ) + : pImpl( new SfxFilterContainer_Impl( rName ) ) +{ +} + + +SfxFilterContainer::~SfxFilterContainer() +{ +} + + +OUString const & SfxFilterContainer::GetName() const +{ + return pImpl->aName; +} + +std::shared_ptr<const SfxFilter> SfxFilterContainer::GetDefaultFilter_Impl( std::u16string_view rName ) +{ + // Try to find out the type of factory. + // Interpret given name as Service- and ShortName! + SvtModuleOptions aOpt; + SvtModuleOptions::EFactory eFactory = SvtModuleOptions::ClassifyFactoryByServiceName(rName); + if (eFactory == SvtModuleOptions::EFactory::UNKNOWN_FACTORY) + eFactory = SvtModuleOptions::ClassifyFactoryByShortName(rName); + + // could not classify factory by its service nor by its short name. + // Must be an unknown factory! => return NULL + if (eFactory == SvtModuleOptions::EFactory::UNKNOWN_FACTORY) + return nullptr; + + // For the following code we need some additional information. + OUString sServiceName = aOpt.GetFactoryName(eFactory); + OUString sDefaultFilter = aOpt.GetFactoryDefaultFilter(eFactory); + + // Try to get the default filter. Don't forget to verify it. + // May the set default filter does not exists any longer or + // does not fit the given factory. + const SfxFilterMatcher aMatcher; + std::shared_ptr<const SfxFilter> pFilter = aMatcher.GetFilter4FilterName(sDefaultFilter); + + if ( + pFilter && + !pFilter->GetServiceName().equalsIgnoreAsciiCase(sServiceName) + ) + { + pFilter = nullptr; + } + + // If at least no default filter could be located - use any filter of this + // factory. + if (!pFilter) + { + if ( bFirstRead ) + ReadFilters_Impl(); + + for (const std::shared_ptr<const SfxFilter>& pCheckFilter : *pFilterArr) + { + if ( pCheckFilter->GetServiceName().equalsIgnoreAsciiCase(sServiceName) ) + { + pFilter = pCheckFilter; + break; + } + } + } + + return pFilter; +} + + +// Impl-Data is shared between all FilterMatchers of the same factory +class SfxFilterMatcher_Impl +{ +public: + OUString aName; + mutable SfxFilterList_Impl* pList; // is created on demand + + void InitForIterating() const; + void Update() const; + explicit SfxFilterMatcher_Impl(const OUString &rName) + : aName(rName) + , pList(nullptr) + { + } + ~SfxFilterMatcher_Impl() + { + // SfxFilterMatcher_Impl::InitForIterating() will set pList to + // either the global filter array matcher pFilterArr, or to + // a new SfxFilterList_Impl. + if (pList != pFilterArr) + delete pList; + } +}; + +namespace +{ + std::vector<std::unique_ptr<SfxFilterMatcher_Impl> > aImplArr; + int nSfxFilterMatcherCount; + + SfxFilterMatcher_Impl & getSfxFilterMatcher_Impl(const OUString &rName) + { + OUString aName; + + if (!rName.isEmpty()) + aName = SfxObjectShell::GetServiceNameFromFactory(rName); + + // find the impl-Data of any comparable FilterMatcher that was created + // previously + for (std::unique_ptr<SfxFilterMatcher_Impl>& aImpl : aImplArr) + if (aImpl->aName == aName) + return *aImpl; + + // first Matcher created for this factory + aImplArr.push_back(std::make_unique<SfxFilterMatcher_Impl>(aName)); + return *aImplArr.back(); + } +} + +SfxFilterMatcher::SfxFilterMatcher( const OUString& rName ) + : m_rImpl( getSfxFilterMatcher_Impl(rName) ) +{ + ++nSfxFilterMatcherCount; +} + +SfxFilterMatcher::SfxFilterMatcher() + : m_rImpl( getSfxFilterMatcher_Impl(OUString()) ) +{ + // global FilterMatcher always uses global filter array (also created on + // demand) + ++nSfxFilterMatcherCount; +} + +SfxFilterMatcher::~SfxFilterMatcher() +{ + --nSfxFilterMatcherCount; + if (nSfxFilterMatcherCount == 0) + aImplArr.clear(); +} + +void SfxFilterMatcher_Impl::Update() const +{ + if ( pList ) + { + // this List was already used + pList->clear(); + for (const std::shared_ptr<const SfxFilter>& pFilter : *pFilterArr) + { + if ( pFilter->GetServiceName() == aName ) + pList->push_back( pFilter ); + } + } +} + +void SfxFilterMatcher_Impl::InitForIterating() const +{ + if ( pList ) + return; + + if ( bFirstRead ) + // global filter array has not been created yet + SfxFilterContainer::ReadFilters_Impl(); + + if ( !aName.isEmpty() ) + { + // matcher of factory: use only filters of that document type + pList = new SfxFilterList_Impl; + Update(); + } + else + { + // global matcher: use global filter array + pList = pFilterArr; + } +} + +std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetAnyFilter( SfxFilterFlags nMust, SfxFilterFlags nDont ) const +{ + m_rImpl.InitForIterating(); + for (const std::shared_ptr<const SfxFilter>& pFilter : *m_rImpl.pList) + { + SfxFilterFlags nFlags = pFilter->GetFilterFlags(); + if ( (nFlags & nMust) == nMust && !(nFlags & nDont ) ) + return pFilter; + } + + return nullptr; +} + + +ErrCode SfxFilterMatcher::GuessFilterIgnoringContent( + SfxMedium const & rMedium, + std::shared_ptr<const SfxFilter>& rpFilter ) const +{ + uno::Reference<document::XTypeDetection> xDetection( + comphelper::getProcessServiceFactory()->createInstance("com.sun.star.document.TypeDetection"), uno::UNO_QUERY); + + OUString sTypeName; + try + { + sTypeName = xDetection->queryTypeByURL( rMedium.GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + } + catch (uno::Exception&) + { + } + + rpFilter = nullptr; + if ( !sTypeName.isEmpty() ) + { + // make sure filter list is initialized + m_rImpl.InitForIterating(); + rpFilter = GetFilter4EA( sTypeName ); + } + + return rpFilter ? ERRCODE_NONE : ERRCODE_ABORT; +} + + +ErrCode SfxFilterMatcher::GuessFilter( SfxMedium& rMedium, std::shared_ptr<const SfxFilter>& rpFilter, SfxFilterFlags nMust, SfxFilterFlags nDont ) const +{ + return GuessFilterControlDefaultUI( rMedium, rpFilter, nMust, nDont ); +} + + +ErrCode SfxFilterMatcher::GuessFilterControlDefaultUI( SfxMedium& rMedium, std::shared_ptr<const SfxFilter>& rpFilter, SfxFilterFlags nMust, SfxFilterFlags nDont ) const +{ + std::shared_ptr<const SfxFilter> pOldFilter = rpFilter; + + // no detection service -> nothing to do ! + uno::Reference<document::XTypeDetection> xDetection( + comphelper::getProcessServiceFactory()->createInstance("com.sun.star.document.TypeDetection"), uno::UNO_QUERY); + + if (!xDetection.is()) + return ERRCODE_ABORT; + + try + { + // open the stream one times only ... + // Otherwise it will be tried more than once and show the same interaction more than once ... + + OUString sURL( rMedium.GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + uno::Reference< io::XInputStream > xInStream = rMedium.GetInputStream(); + OUString aFilterName; + OUString sTypeName; + + // stream exists => deep detection (with preselection ... if possible) + if (xInStream.is()) + { + utl::MediaDescriptor aDescriptor; + + aDescriptor[utl::MediaDescriptor::PROP_URL ] <<= sURL; + aDescriptor[utl::MediaDescriptor::PROP_INPUTSTREAM ] <<= xInStream; + aDescriptor[utl::MediaDescriptor::PROP_INTERACTIONHANDLER] <<= rMedium.GetInteractionHandler(); + SfxStringItem const * it = static_cast<SfxStringItem const *>( + rMedium.GetItemSet()->GetItem(SID_REFERER)); + if (it != nullptr) { + aDescriptor[utl::MediaDescriptor::PROP_REFERRER] + <<= it->GetValue(); + } + + if ( !m_rImpl.aName.isEmpty() ) + aDescriptor[utl::MediaDescriptor::PROP_DOCUMENTSERVICE] <<= m_rImpl.aName; + + if ( pOldFilter ) + { + aDescriptor[utl::MediaDescriptor::PROP_TYPENAME ] <<= pOldFilter->GetTypeName(); + aDescriptor[utl::MediaDescriptor::PROP_FILTERNAME] <<= pOldFilter->GetFilterName(); + } + + uno::Sequence< beans::PropertyValue > lDescriptor = aDescriptor.getAsConstPropertyValueList(); + sTypeName = xDetection->queryTypeByDescriptor(lDescriptor, true); // lDescriptor is used as In/Out param ... don't use aDescriptor.getAsConstPropertyValueList() directly! + + for (const auto& rProp : std::as_const(lDescriptor)) + { + if (rProp.Name == "FilterName") + // Type detection picked a preferred filter for this format. + aFilterName = rProp.Value.get<OUString>(); + } + } + // no stream exists => try flat detection without preselection as fallback + else + sTypeName = xDetection->queryTypeByURL(sURL); + + if (!sTypeName.isEmpty()) + { + std::shared_ptr<const SfxFilter> pNewFilter; + if (!aFilterName.isEmpty()) + // Type detection returned a suitable filter for this. Use it. + pNewFilter = SfxFilter::GetFilterByName(aFilterName); + + // fdo#78742 respect requested document service if set + if (!pNewFilter || (!m_rImpl.aName.isEmpty() + && m_rImpl.aName != pNewFilter->GetServiceName())) + { + // detect filter by given type + // In case of this matcher is bound to a particular document type: + // If there is no acceptable type for this document at all, the type detection has possibly returned something else. + // The DocumentService property is only a preselection, and all preselections are considered as optional! + // This "wrong" type will be sorted out now because we match only allowed filters to the detected type + uno::Sequence< beans::NamedValue > lQuery { { "Name", css::uno::Any(sTypeName) } }; + + pNewFilter = GetFilterForProps(lQuery, nMust, nDont); + } + + if (pNewFilter) + { + rpFilter = pNewFilter; + return ERRCODE_NONE; + } + } + } + catch (const uno::Exception&) + {} + + return ERRCODE_ABORT; +} + + +bool SfxFilterMatcher::IsFilterInstalled_Impl( const std::shared_ptr<const SfxFilter>& pFilter ) +{ + if ( pFilter->GetFilterFlags() & SfxFilterFlags::MUSTINSTALL ) + { + // Here could a re-installation be offered + OUString aText( SfxResId(STR_FILTER_NOT_INSTALLED) ); + aText = aText.replaceFirst( "$(FILTER)", pFilter->GetUIName() ); + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Question, VclButtonsType::YesNo, + aText)); + xQueryBox->set_default_response(RET_YES); + + short nRet = xQueryBox->run(); + if ( nRet == RET_YES ) + { +#ifdef DBG_UTIL + // Start Setup + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Info, VclButtonsType::Ok, + "Here should the Setup now be starting!")); + xInfoBox->run(); +#endif + // Installation must still give feedback if it worked or not, + // then the Filterflag be deleted + } + + return ( !(pFilter->GetFilterFlags() & SfxFilterFlags::MUSTINSTALL) ); + } + else if ( pFilter->GetFilterFlags() & SfxFilterFlags::CONSULTSERVICE ) + { + OUString aText( SfxResId(STR_FILTER_CONSULT_SERVICE) ); + aText = aText.replaceFirst( "$(FILTER)", pFilter->GetUIName() ); + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Info, VclButtonsType::Ok, + aText)); + xInfoBox->run(); + return false; + } + else + return true; +} + + +ErrCode SfxFilterMatcher::DetectFilter( SfxMedium& rMedium, std::shared_ptr<const SfxFilter>& rpFilter ) const +/* [Description] + + Here the Filter selection box is pulled up. Otherwise GuessFilter + */ + +{ + std::shared_ptr<const SfxFilter> pOldFilter = rMedium.GetFilter(); + if ( pOldFilter ) + { + if( !IsFilterInstalled_Impl( pOldFilter ) ) + pOldFilter = nullptr; + else + { + const SfxStringItem* pSalvageItem = SfxItemSet::GetItem<SfxStringItem>(rMedium.GetItemSet(), SID_DOC_SALVAGE, false); + if ( ( pOldFilter->GetFilterFlags() & SfxFilterFlags::PACKED ) && pSalvageItem ) + // Salvage is always done without packing + pOldFilter = nullptr; + } + } + + std::shared_ptr<const SfxFilter> pFilter = pOldFilter; + + bool bPreview = rMedium.IsPreview_Impl(); + const SfxStringItem* pReferer = SfxItemSet::GetItem<SfxStringItem>(rMedium.GetItemSet(), SID_REFERER, false); + if ( bPreview && rMedium.IsRemote() && ( !pReferer || !pReferer->GetValue().match("private:searchfolder:") ) ) + return ERRCODE_ABORT; + + ErrCode nErr = GuessFilter( rMedium, pFilter ); + if ( nErr == ERRCODE_ABORT ) + return nErr; + + if ( nErr == ERRCODE_IO_PENDING ) + { + rpFilter = pFilter; + return nErr; + } + + if ( !pFilter ) + { + std::shared_ptr<const SfxFilter> pInstallFilter; + + // Now test the filter which are not installed (ErrCode is irrelevant) + GuessFilter( rMedium, pInstallFilter, SfxFilterFlags::IMPORT, SfxFilterFlags::CONSULTSERVICE ); + if ( pInstallFilter ) + { + if ( IsFilterInstalled_Impl( pInstallFilter ) ) + // Maybe the filter was installed afterwards. + pFilter = pInstallFilter; + } + else + { + // Now test the filter, which first must be obtained by Star + // (ErrCode is irrelevant) + GuessFilter( rMedium, pInstallFilter, SfxFilterFlags::IMPORT, SfxFilterFlags::NONE ); + if ( pInstallFilter ) + IsFilterInstalled_Impl( pInstallFilter ); + } + } + + bool bHidden = bPreview; + const SfxStringItem* pFlags = SfxItemSet::GetItem<SfxStringItem>(rMedium.GetItemSet(), SID_OPTIONS, false); + if ( !bHidden && pFlags ) + { + OUString aFlags( pFlags->GetValue() ); + aFlags = aFlags.toAsciiUpperCase(); + if( -1 != aFlags.indexOf( 'H' ) ) + bHidden = true; + } + rpFilter = pFilter; + + if ( bHidden ) + nErr = pFilter ? ERRCODE_NONE : ERRCODE_ABORT; + return nErr; +} + +std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilterForProps( const css::uno::Sequence < beans::NamedValue >& aSeq, SfxFilterFlags nMust, SfxFilterFlags nDont ) const +{ + uno::Reference< lang::XMultiServiceFactory > xServiceManager = ::comphelper::getProcessServiceFactory(); + if( !xServiceManager ) + return nullptr; + + static constexpr OUStringLiteral sTypeDetection = u"com.sun.star.document.TypeDetection"; + uno::Reference< container::XContainerQuery > xTypeCFG( xServiceManager->createInstance( sTypeDetection ), uno::UNO_QUERY ); + if ( !xTypeCFG ) + return nullptr; + + // make query for all types matching the properties + uno::Reference < css::container::XEnumeration > xEnum = xTypeCFG->createSubSetEnumerationByProperties( aSeq ); + uno::Sequence<beans::PropertyValue> aProps; + while ( xEnum->hasMoreElements() ) + { + static constexpr OUStringLiteral sPreferredFilter = u"PreferredFilter"; + static constexpr OUStringLiteral sName = u"Name"; + + xEnum->nextElement() >>= aProps; + OUString aValue, aName; + for( const auto & rPropVal : aProps) + { + if (rPropVal.Name == sPreferredFilter) + rPropVal.Value >>= aValue; + else if (rPropVal.Name == sName) + rPropVal.Value >>= aName; + } + + // try to get the preferred filter (works without loading all filters!) + if ( !aValue.isEmpty() ) + { + std::shared_ptr<const SfxFilter> pFilter = SfxFilter::GetFilterByName( aValue ); + if ( !pFilter || (pFilter->GetFilterFlags() & nMust) != nMust || (pFilter->GetFilterFlags() & nDont ) ) + // check for filter flags + // pFilter == 0: if preferred filter is a Writer filter, but Writer module is not installed + continue; + + if ( !m_rImpl.aName.isEmpty() ) + { + // if this is not the global FilterMatcher: check if filter matches the document type + if ( pFilter->GetServiceName() != m_rImpl.aName ) + { + // preferred filter belongs to another document type; now we must search the filter + m_rImpl.InitForIterating(); + pFilter = GetFilter4EA( aName, nMust, nDont ); + if ( pFilter ) + return pFilter; + } + else + return pFilter; + } + else + return pFilter; + } + } + + return nullptr; +} + +std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4Mime( const OUString& rMediaType, SfxFilterFlags nMust, SfxFilterFlags nDont ) const +{ + if ( m_rImpl.pList ) + { + for (const std::shared_ptr<const SfxFilter>& pFilter : *m_rImpl.pList) + { + SfxFilterFlags nFlags = pFilter->GetFilterFlags(); + if ( (nFlags & nMust) == nMust && !(nFlags & nDont ) && pFilter->GetMimeType() == rMediaType ) + return pFilter; + } + + return nullptr; + } + + css::uno::Sequence < css::beans::NamedValue > aSeq { { "MediaType", css::uno::Any(rMediaType) } }; + return GetFilterForProps( aSeq, nMust, nDont ); +} + +std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4EA( const OUString& rType, SfxFilterFlags nMust, SfxFilterFlags nDont ) const +{ + if ( m_rImpl.pList ) + { + std::shared_ptr<const SfxFilter> pFirst; + for (const std::shared_ptr<const SfxFilter>& pFilter : *m_rImpl.pList) + { + SfxFilterFlags nFlags = pFilter->GetFilterFlags(); + if ( (nFlags & nMust) == nMust && !(nFlags & nDont ) && pFilter->GetTypeName() == rType ) + { + if (nFlags & SfxFilterFlags::PREFERED) + return pFilter; + if (!pFirst) + pFirst = pFilter; + } + } + if (pFirst) + return pFirst; + + return nullptr; + } + + css::uno::Sequence < css::beans::NamedValue > aSeq { { "Name", css::uno::Any(rType) } }; + return GetFilterForProps( aSeq, nMust, nDont ); +} + +std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4Extension( const OUString& rExt, SfxFilterFlags nMust, SfxFilterFlags nDont ) const +{ + if ( m_rImpl.pList ) + { + if (OUString sExt = ToUpper_Impl(rExt); !sExt.isEmpty()) + { + if (sExt[0] != '.') + sExt = "." + sExt; + + for (const std::shared_ptr<const SfxFilter>& pFilter : *m_rImpl.pList) + { + SfxFilterFlags nFlags = pFilter->GetFilterFlags(); + if ((nFlags & nMust) == nMust && !(nFlags & nDont)) + { + OUString sWildCard = ToUpper_Impl(pFilter->GetWildcard().getGlob()); + + WildCard aCheck(sWildCard, ';'); + if (aCheck.Matches(sExt)) + return pFilter; + } + } + } + + return nullptr; + } + + // Use extension without dot! + OUString sExt( rExt ); + if ( sExt.startsWith(".") ) + sExt = sExt.copy(1); + + css::uno::Sequence < css::beans::NamedValue > aSeq + { { "Extensions", css::uno::Any(uno::Sequence < OUString > { sExt } ) } }; + return GetFilterForProps( aSeq, nMust, nDont ); +} + +std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4ClipBoardId( SotClipboardFormatId nId, SfxFilterFlags nMust, SfxFilterFlags nDont ) const +{ + if (nId == SotClipboardFormatId::NONE) + return nullptr; + + css::uno::Sequence < css::beans::NamedValue > aSeq + { { "ClipboardFormat", css::uno::Any(SotExchange::GetFormatName( nId )) } }; + return GetFilterForProps( aSeq, nMust, nDont ); +} + +std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4UIName( std::u16string_view rName, SfxFilterFlags nMust, SfxFilterFlags nDont ) const +{ + m_rImpl.InitForIterating(); + std::shared_ptr<const SfxFilter> pFirstFilter; + for (const std::shared_ptr<const SfxFilter>& pFilter : *m_rImpl.pList) + { + SfxFilterFlags nFlags = pFilter->GetFilterFlags(); + if ( (nFlags & nMust) == nMust && + !(nFlags & nDont ) && pFilter->GetUIName() == rName ) + { + if ( pFilter->GetFilterFlags() & SfxFilterFlags::PREFERED ) + return pFilter; + else if ( !pFirstFilter ) + pFirstFilter = pFilter; + } + } + return pFirstFilter; +} + +std::shared_ptr<const SfxFilter> SfxFilterMatcher::GetFilter4FilterName( const OUString& rName, SfxFilterFlags nMust, SfxFilterFlags nDont ) const +{ + std::u16string_view aName( rName ); + sal_Int32 nIndex = rName.indexOf(": "); + if ( nIndex != -1 ) + { + SAL_WARN( "sfx.bastyp", "Old filter name used!"); + aName = rName.subView( nIndex + 2 ); + } + + if ( bFirstRead ) + { + uno::Reference< lang::XMultiServiceFactory > xServiceManager = ::comphelper::getProcessServiceFactory(); + uno::Reference< container::XNameAccess > xFilterCFG ; + uno::Reference< container::XNameAccess > xTypeCFG ; + if( xServiceManager.is() ) + { + static constexpr OUStringLiteral sFilterFactory = u"com.sun.star.document.FilterFactory"; + static constexpr OUStringLiteral sTypeDetection = u"com.sun.star.document.TypeDetection"; + xFilterCFG.set( xServiceManager->createInstance( sFilterFactory ), uno::UNO_QUERY ); + xTypeCFG.set( xServiceManager->createInstance( sTypeDetection ), uno::UNO_QUERY ); + } + + if( xFilterCFG.is() && xTypeCFG.is() ) + { + if ( !pFilterArr ) + CreateFilterArr(); + else + { + for (const std::shared_ptr<const SfxFilter>& pFilter : *pFilterArr) + { + SfxFilterFlags nFlags = pFilter->GetFilterFlags(); + if ((nFlags & nMust) == nMust && !(nFlags & nDont) && pFilter->GetFilterName().equalsIgnoreAsciiCase(aName)) + return pFilter; + } + } + + SfxFilterContainer::ReadSingleFilter_Impl( rName, xTypeCFG, xFilterCFG, false ); + } + } + + SfxFilterList_Impl* pList = m_rImpl.pList; + if ( !pList ) + pList = pFilterArr; + + for (const std::shared_ptr<const SfxFilter>& pFilter : *pList) + { + SfxFilterFlags nFlags = pFilter->GetFilterFlags(); + if ( (nFlags & nMust) == nMust && !(nFlags & nDont ) && pFilter->GetFilterName().equalsIgnoreAsciiCase(aName)) + return pFilter; + } + + return nullptr; +} + +IMPL_LINK( SfxFilterMatcher, MaybeFileHdl_Impl, OUString*, pString, bool ) +{ + std::shared_ptr<const SfxFilter> pFilter = GetFilter4Extension( *pString ); + return pFilter && + !pFilter->GetWildcard().Matches(u"") && + !pFilter->GetWildcard().Matches(u"*.*") && + !pFilter->GetWildcard().Matches(u"*"); +} + + +SfxFilterMatcherIter::SfxFilterMatcherIter( + const SfxFilterMatcher& rMatcher, + SfxFilterFlags nOrMaskP, SfxFilterFlags nAndMaskP ) + : nOrMask( nOrMaskP ), nAndMask( nAndMaskP ), + nCurrent(0), m_rMatch(rMatcher.m_rImpl) +{ + if( nOrMask == static_cast<SfxFilterFlags>(0xffff) ) //Due to faulty build on s + nOrMask = SfxFilterFlags::NONE; + m_rMatch.InitForIterating(); +} + + +std::shared_ptr<const SfxFilter> SfxFilterMatcherIter::Find_Impl() +{ + std::shared_ptr<const SfxFilter> pFilter; + while( nCurrent < m_rMatch.pList->size() ) + { + pFilter = (*m_rMatch.pList)[nCurrent++]; + SfxFilterFlags nFlags = pFilter->GetFilterFlags(); + if( ((nFlags & nOrMask) == nOrMask ) && !(nFlags & nAndMask ) ) + break; + pFilter = nullptr; + } + + return pFilter; +} + +std::shared_ptr<const SfxFilter> SfxFilterMatcherIter::First() +{ + nCurrent = 0; + return Find_Impl(); +} + + +std::shared_ptr<const SfxFilter> SfxFilterMatcherIter::Next() +{ + return Find_Impl(); +} + +/*--------------------------------------------------------------- + helper to build own formatted string from given stringlist by + using given separator + ---------------------------------------------------------------*/ +static OUString implc_convertStringlistToString( const uno::Sequence< OUString >& lList , + sal_Unicode cSeparator, + std::u16string_view sPrefix ) +{ + OUStringBuffer sString ( 1000 ) ; + sal_Int32 nCount = lList.getLength(); + sal_Int32 nItem = 0 ; + for( nItem=0; nItem<nCount; ++nItem ) + { + if( !sPrefix.empty() ) + { + sString.append( sPrefix ); + } + sString.append( lList[nItem] ); + if( nItem+1<nCount ) + { + sString.append( cSeparator ); + } + } + return sString.makeStringAndClear(); +} + + +void SfxFilterContainer::ReadSingleFilter_Impl( + const OUString& rName, + const uno::Reference< container::XNameAccess >& xTypeCFG, + const uno::Reference< container::XNameAccess >& xFilterCFG, + bool bUpdate + ) +{ + OUString sFilterName( rName ); + SfxFilterList_Impl& rList = *pFilterArr; + uno::Sequence< beans::PropertyValue > lFilterProperties; + uno::Any aResult; + try + { + aResult = xFilterCFG->getByName( sFilterName ); + } + catch( container::NoSuchElementException& ) + { + aResult = uno::Any(); + } + + if( !(aResult >>= lFilterProperties) ) + return; + + // collect information to add filter to container + // (attention: some information aren't available on filter directly ... you must search for corresponding type too!) + SfxFilterFlags nFlags = SfxFilterFlags::NONE; + SotClipboardFormatId nClipboardId = SotClipboardFormatId::NONE; + sal_Int32 nFormatVersion = 0 ; + OUString sMimeType ; + OUString sType ; + OUString sUIName ; + OUString sHumanName ; + OUString sDefaultTemplate ; + OUString sUserData ; + OUString sExtension ; + OUString sServiceName ; + bool bEnabled = true ; + + // first get directly available properties + for( const auto& rFilterProperty : std::as_const(lFilterProperties) ) + { + if ( rFilterProperty.Name == "FileFormatVersion" ) + { + rFilterProperty.Value >>= nFormatVersion; + } + else if ( rFilterProperty.Name == "TemplateName" ) + { + rFilterProperty.Value >>= sDefaultTemplate; + } + else if ( rFilterProperty.Name == "Flags" ) + { + sal_Int32 nTmp(0); + rFilterProperty.Value >>= nTmp; + assert((nTmp & ~o3tl::typed_flags<SfxFilterFlags>::mask) == 0); + nFlags = static_cast<SfxFilterFlags>(nTmp); + } + else if ( rFilterProperty.Name == "UIName" ) + { + rFilterProperty.Value >>= sUIName; + } + else if ( rFilterProperty.Name == "UserData" ) + { + uno::Sequence< OUString > lUserData; + rFilterProperty.Value >>= lUserData; + sUserData = implc_convertStringlistToString( lUserData, ',', u"" ); + } + else if ( rFilterProperty.Name == "DocumentService" ) + { + rFilterProperty.Value >>= sServiceName; + } + else if (rFilterProperty.Name == "ExportExtension") + { + // Extension preferred by the filter. This takes precedence + // over those that are given in the file format type. + rFilterProperty.Value >>= sExtension; + sExtension = "*." + sExtension; + } + else if ( rFilterProperty.Name == "Type" ) + { + rFilterProperty.Value >>= sType; + // Try to get filter .. but look for any exceptions! + // May be filter was deleted by another thread ... + try + { + aResult = xTypeCFG->getByName( sType ); + } + catch (const container::NoSuchElementException&) + { + aResult = uno::Any(); + } + + uno::Sequence< beans::PropertyValue > lTypeProperties; + if( aResult >>= lTypeProperties ) + { + // get indirect available properties then (types) + for( const auto& rTypeProperty : std::as_const(lTypeProperties) ) + { + if ( rTypeProperty.Name == "ClipboardFormat" ) + { + rTypeProperty.Value >>= sHumanName; + } + else if ( rTypeProperty.Name == "MediaType" ) + { + rTypeProperty.Value >>= sMimeType; + } + else if ( rTypeProperty.Name == "Extensions" ) + { + if (sExtension.isEmpty()) + { + uno::Sequence< OUString > lExtensions; + rTypeProperty.Value >>= lExtensions; + sExtension = implc_convertStringlistToString( lExtensions, ';', u"*." ); + } + } + } + } + } + else if ( rFilterProperty.Name == "Enabled" ) + { + rFilterProperty.Value >>= bEnabled; + } + + } + + if ( sServiceName.isEmpty() ) + return; + + // old formats are found ... using HumanPresentableName! + if( !sHumanName.isEmpty() ) + { + nClipboardId = SotExchange::RegisterFormatName( sHumanName ); + + // For external filters ignore clipboard IDs + if(nFlags & SfxFilterFlags::STARONEFILTER) + { + nClipboardId = SotClipboardFormatId::NONE; + } + } + // register SfxFilter + // first erase module name from old filter names! + // e.g: "scalc: DIF" => "DIF" + sal_Int32 nStartRealName = sFilterName.indexOf( ": " ); + if( nStartRealName != -1 ) + { + SAL_WARN( "sfx.bastyp", "Old format, not supported!"); + sFilterName = sFilterName.copy( nStartRealName+2 ); + } + + std::shared_ptr<const SfxFilter> pFilter = bUpdate ? SfxFilter::GetFilterByName( sFilterName ) : nullptr; + if (!pFilter) + { + pFilter = std::make_shared<SfxFilter>( sFilterName , + sExtension , + nFlags , + nClipboardId , + sType , + sMimeType , + sUserData , + sServiceName , + bEnabled ); + rList.push_back( pFilter ); + } + else + { + SfxFilter* pFilt = const_cast<SfxFilter*>(pFilter.get()); + pFilt->maFilterName = sFilterName; + pFilt->aWildCard = WildCard(sExtension, ';'); + pFilt->nFormatType = nFlags; + pFilt->lFormat = nClipboardId; + pFilt->aTypeName = sType; + pFilt->aMimeType = sMimeType; + pFilt->aUserData = sUserData; + pFilt->aServiceName = sServiceName; + pFilt->mbEnabled = bEnabled; + } + + SfxFilter* pFilt = const_cast<SfxFilter*>(pFilter.get()); + + // Don't forget to set right UIName! + // Otherwise internal name is used as fallback ... + pFilt->SetUIName( sUIName ); + pFilt->SetDefaultTemplate( sDefaultTemplate ); + if( nFormatVersion ) + { + pFilt->SetVersion( nFormatVersion ); + } +} + +void SfxFilterContainer::ReadFilters_Impl( bool bUpdate ) +{ + if ( !pFilterArr ) + CreateFilterArr(); + + bFirstRead = false; + SfxFilterList_Impl& rList = *pFilterArr; + + try + { + // get the FilterFactory service to access the registered filters ... and types! + uno::Reference< lang::XMultiServiceFactory > xServiceManager = ::comphelper::getProcessServiceFactory(); + uno::Reference< container::XNameAccess > xFilterCFG ; + uno::Reference< container::XNameAccess > xTypeCFG ; + if( xServiceManager.is() ) + { + xFilterCFG.set( xServiceManager->createInstance( "com.sun.star.document.FilterFactory" ), uno::UNO_QUERY ); + xTypeCFG.set( xServiceManager->createInstance( "com.sun.star.document.TypeDetection" ), uno::UNO_QUERY ); + } + + if( xFilterCFG.is() && xTypeCFG.is() ) + { + // select right query to get right set of filters for search module + const uno::Sequence< OUString > lFilterNames = xFilterCFG->getElementNames(); + if ( lFilterNames.hasElements() ) + { + // If list of filters already exist ... + // ReadExternalFilters must work in update mode. + // Best way seems to mark all filters NOT_INSTALLED + // and change it back for all valid filters afterwards. + if( !rList.empty() ) + { + bUpdate = true; + for (const std::shared_ptr<const SfxFilter>& pFilter : rList) + { + SfxFilter* pNonConstFilter = const_cast<SfxFilter*>(pFilter.get()); + pNonConstFilter->nFormatType |= SFX_FILTER_NOTINSTALLED; + } + } + + // get all properties of filters ... put it into the filter container + for( const OUString& sFilterName : lFilterNames ) + { + // Try to get filter .. but look for any exceptions! + // May be filter was deleted by another thread ... + ReadSingleFilter_Impl( sFilterName, xTypeCFG, xFilterCFG, bUpdate ); + } + } + } + } + catch(const uno::Exception&) + { + SAL_WARN( "sfx.bastyp", "SfxFilterContainer::ReadFilter()\nException detected. Possible not all filters could be cached." ); + } + + if ( bUpdate ) + { + // global filter array was modified, factory specific ones might need an + // update too + for (const auto& aImpl : aImplArr) + aImpl->Update(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |