diff options
Diffstat (limited to '')
-rw-r--r-- | svtools/source/misc/acceleratorexecute.cxx | 519 | ||||
-rw-r--r-- | svtools/source/misc/bindablecontrolhelper.cxx | 144 | ||||
-rw-r--r-- | svtools/source/misc/cliplistener.cxx | 85 | ||||
-rw-r--r-- | svtools/source/misc/dialogclosedlistener.cxx | 59 | ||||
-rw-r--r-- | svtools/source/misc/ehdl.cxx | 293 | ||||
-rw-r--r-- | svtools/source/misc/embedhlp.cxx | 1096 | ||||
-rw-r--r-- | svtools/source/misc/embedtransfer.cxx | 250 | ||||
-rw-r--r-- | svtools/source/misc/filechangedchecker.cxx | 115 | ||||
-rw-r--r-- | svtools/source/misc/imagemgr.cxx | 892 | ||||
-rw-r--r-- | svtools/source/misc/imageresourceaccess.cxx | 167 | ||||
-rw-r--r-- | svtools/source/misc/langhelp.cxx | 167 | ||||
-rw-r--r-- | svtools/source/misc/langtab.cxx | 347 | ||||
-rw-r--r-- | svtools/source/misc/openfiledroptargetlistener.cxx | 211 | ||||
-rw-r--r-- | svtools/source/misc/sampletext.cxx | 1671 | ||||
-rw-r--r-- | svtools/source/misc/stringtransfer.cxx | 98 | ||||
-rw-r--r-- | svtools/source/misc/svtresid.cxx | 26 | ||||
-rw-r--r-- | svtools/source/misc/templatefoldercache.cxx | 796 | ||||
-rw-r--r-- | svtools/source/misc/unitconv.cxx | 203 |
18 files changed, 7139 insertions, 0 deletions
diff --git a/svtools/source/misc/acceleratorexecute.cxx b/svtools/source/misc/acceleratorexecute.cxx new file mode 100644 index 0000000000..70e1bc6c6b --- /dev/null +++ b/svtools/source/misc/acceleratorexecute.cxx @@ -0,0 +1,519 @@ +/* -*- 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 <memory> +#include <svtools/acceleratorexecute.hxx> + +#include <com/sun/star/frame/ModuleManager.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp> +#include <com/sun/star/ui/XUIConfigurationManager.hpp> +#include <com/sun/star/ui/XUIConfigurationManager2.hpp> +#include <com/sun/star/ui/XModuleUIConfigurationManager2.hpp> +#include <com/sun/star/ui/theModuleUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/ui/XUIConfigurationManagerSupplier.hpp> +#include <com/sun/star/awt/KeyModifier.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <cppuhelper/implbase.hxx> + +#include <utility> +#include <vcl/evntpost.hxx> +#include <sal/log.hxx> +#include <vcl/lok.hxx> +#include <rtl/ref.hxx> + +#include <comphelper/lok.hxx> + +namespace svt +{ + +namespace { + +class AsyncAccelExec : public cppu::WeakImplHelper<css::lang::XEventListener> +{ + private: + css::uno::Reference<css::lang::XComponent> m_xFrame; + css::uno::Reference< css::frame::XDispatch > m_xDispatch; + css::util::URL m_aURL; + vcl::EventPoster m_aAsyncCallback; + public: + + /** creates a new instance of this class, which can be used + one times only! + + This instance can be forced to execute its internal set request + asynchronous. After that it deletes itself! + */ + static rtl::Reference<AsyncAccelExec> createOneShotInstance(const css::uno::Reference<css::lang::XComponent>& xFrame, + const css::uno::Reference<css::frame::XDispatch>& xDispatch, + const css::util::URL& rURL); + + void execAsync(); + private: + + virtual void SAL_CALL disposing(const css::lang::EventObject&) override + { + m_xFrame->removeEventListener(this); + m_xFrame.clear(); + m_xDispatch.clear(); + } + + /** @short allow creation of instances of this class + by using our factory only! + */ + AsyncAccelExec(css::uno::Reference<css::lang::XComponent> xFrame, + css::uno::Reference< css::frame::XDispatch > xDispatch, + css::util::URL aURL); + + DECL_LINK(impl_ts_asyncCallback, LinkParamNone*, void); +}; + +} + +AcceleratorExecute::AcceleratorExecute() +{ +} + +AcceleratorExecute::~AcceleratorExecute() +{ + // does nothing real +} + + +std::unique_ptr<AcceleratorExecute> AcceleratorExecute::createAcceleratorHelper() +{ + return std::unique_ptr<AcceleratorExecute>(new AcceleratorExecute); +} + + +void AcceleratorExecute::init(const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::frame::XFrame >& xEnv ) +{ + // SAFE -> ---------------------------------- + std::unique_lock aLock(m_aLock); + + // take over the uno service manager + m_xContext = rxContext; + + // specify our internal dispatch provider + // frame or desktop?! => document or global config. + bool bDesktopIsUsed = false; + m_xDispatcher.set(xEnv, css::uno::UNO_QUERY); + if (!m_xDispatcher.is()) + { + aLock.unlock(); + // <- SAFE ------------------------------ + + css::uno::Reference< css::frame::XDispatchProvider > xDispatcher(css::frame::Desktop::create(rxContext), css::uno::UNO_QUERY_THROW); + + // SAFE -> ------------------------------ + aLock.lock(); + + m_xDispatcher = xDispatcher; + bDesktopIsUsed = true; + } + + aLock.unlock(); + // <- SAFE ---------------------------------- + + // open all needed configuration objects + css::uno::Reference< css::ui::XAcceleratorConfiguration > xGlobalCfg; + css::uno::Reference< css::ui::XAcceleratorConfiguration > xModuleCfg; + css::uno::Reference< css::ui::XAcceleratorConfiguration > xDocCfg ; + + // global cfg + xGlobalCfg = css::ui::GlobalAcceleratorConfiguration::create(rxContext); + if (!bDesktopIsUsed) + { + // module cfg + xModuleCfg = AcceleratorExecute::st_openModuleConfig(rxContext, xEnv); + + // doc cfg + css::uno::Reference< css::frame::XController > xController; + css::uno::Reference< css::frame::XModel > xModel; + xController = xEnv->getController(); + if (xController.is()) + xModel = xController->getModel(); + if (xModel.is()) + xDocCfg = AcceleratorExecute::st_openDocConfig(xModel); + } + + // SAFE -> ------------------------------ + aLock.lock(); + + m_xGlobalCfg = xGlobalCfg; + m_xModuleCfg = xModuleCfg; + m_xDocCfg = xDocCfg ; + + aLock.unlock(); + // <- SAFE ---------------------------------- +} + + +bool AcceleratorExecute::execute(const vcl::KeyCode& aVCLKey) +{ + css::awt::KeyEvent aAWTKey = AcceleratorExecute::st_VCLKey2AWTKey(aVCLKey); + return execute(aAWTKey); +} + + +bool AcceleratorExecute::execute(const css::awt::KeyEvent& aAWTKey) +{ + OUString sCommand = impl_ts_findCommand(aAWTKey); + + // No Command found? Do nothing! User is not interested on any error handling .-) + // or for some reason m_xContext is NULL (which would crash impl_ts_getURLParser() + if (sCommand.isEmpty() || !m_xContext.is()) + { + return false; + } + + // SAFE -> ---------------------------------- + std::unique_lock aLock(m_aLock); + + css::uno::Reference< css::frame::XDispatchProvider > xProvider = m_xDispatcher; + + aLock.unlock(); + // <- SAFE ---------------------------------- + + // convert command in URL structure + css::uno::Reference< css::util::XURLTransformer > xParser = impl_ts_getURLParser(); + css::util::URL aURL; + aURL.Complete = sCommand; + xParser->parseStrict(aURL); + + // ask for dispatch object + css::uno::Reference< css::frame::XDispatch > xDispatch = xProvider->queryDispatch(aURL, OUString(), 0); + bool bRet = xDispatch.is(); + if ( bRet ) + { + // Note: Such instance can be used one times only and destroy itself afterwards .-) + css::uno::Reference<css::lang::XComponent> xFrame(xProvider, css::uno::UNO_QUERY); + if (vcl::lok::isUnipoll()) + { // tdf#130382 - all synchronous really. + try { + xDispatch->dispatch (aURL, css::uno::Sequence< css::beans::PropertyValue >()); + } + catch(const css::uno::Exception&ev) + { + SAL_INFO("svtools", "exception on key emission: " << ev.Message); + } + } + else + { + rtl::Reference<AsyncAccelExec> pExec = AsyncAccelExec::createOneShotInstance(xFrame, xDispatch, aURL); + pExec->execAsync(); + } + } + + return bRet; +} + + +css::awt::KeyEvent AcceleratorExecute::st_VCLKey2AWTKey(const vcl::KeyCode& aVCLKey) +{ + css::awt::KeyEvent aAWTKey; + aAWTKey.Modifiers = 0; + aAWTKey.KeyCode = static_cast<sal_Int16>(aVCLKey.GetCode()); + + if (aVCLKey.IsShift()) + aAWTKey.Modifiers |= css::awt::KeyModifier::SHIFT; + if (aVCLKey.IsMod1()) + aAWTKey.Modifiers |= css::awt::KeyModifier::MOD1; + if (aVCLKey.IsMod2()) + aAWTKey.Modifiers |= css::awt::KeyModifier::MOD2; + if (aVCLKey.IsMod3()) + aAWTKey.Modifiers |= css::awt::KeyModifier::MOD3; + return aAWTKey; +} + + +vcl::KeyCode AcceleratorExecute::st_AWTKey2VCLKey(const css::awt::KeyEvent& aAWTKey) +{ + bool bShift = ((aAWTKey.Modifiers & css::awt::KeyModifier::SHIFT) == css::awt::KeyModifier::SHIFT ); + bool bMod1 = ((aAWTKey.Modifiers & css::awt::KeyModifier::MOD1 ) == css::awt::KeyModifier::MOD1 ); + bool bMod2 = ((aAWTKey.Modifiers & css::awt::KeyModifier::MOD2 ) == css::awt::KeyModifier::MOD2 ); + bool bMod3 = ((aAWTKey.Modifiers & css::awt::KeyModifier::MOD3 ) == css::awt::KeyModifier::MOD3 ); + sal_uInt16 nKey = static_cast<sal_uInt16>(aAWTKey.KeyCode); + + return vcl::KeyCode(nKey, bShift, bMod1, bMod2, bMod3); +} + +OUString AcceleratorExecute::findCommand(const css::awt::KeyEvent& aKey) +{ + return impl_ts_findCommand(aKey); +} + +OUString AcceleratorExecute::impl_ts_findCommand(const css::awt::KeyEvent& aKey) +{ + // SAFE -> ---------------------------------- + std::unique_lock aLock(m_aLock); + + css::uno::Reference< css::ui::XAcceleratorConfiguration > xGlobalCfg = m_xGlobalCfg; + css::uno::Reference< css::ui::XAcceleratorConfiguration > xModuleCfg = m_xModuleCfg; + css::uno::Reference< css::ui::XAcceleratorConfiguration > xDocCfg = m_xDocCfg ; + + aLock.unlock(); + // <- SAFE ---------------------------------- + + OUString sCommand; + + try + { + if (xDocCfg.is()) + sCommand = xDocCfg->getCommandByKeyEvent(aKey); + if (!sCommand.isEmpty()) + return sCommand; + } + catch(const css::container::NoSuchElementException&) + {} + + try + { + if (xModuleCfg.is()) + sCommand = xModuleCfg->getCommandByKeyEvent(aKey); + if (!sCommand.isEmpty()) + return sCommand; + } + catch(const css::container::NoSuchElementException&) + {} + + try + { + if (xGlobalCfg.is()) + sCommand = xGlobalCfg->getCommandByKeyEvent(aKey); + if (!sCommand.isEmpty()) + return sCommand; + } + catch(const css::container::NoSuchElementException&) + {} + + // fall back to functional key codes + if( aKey.Modifiers == 0 ) + { + switch( aKey.KeyCode ) + { + case css::awt::Key::DELETE_TO_BEGIN_OF_LINE: + return ".uno:DelToStartOfLine"; + case css::awt::Key::DELETE_TO_END_OF_LINE: + return ".uno:DelToEndOfLine"; + case css::awt::Key::DELETE_TO_BEGIN_OF_PARAGRAPH: + return ".uno:DelToStartOfPara"; + case css::awt::Key::DELETE_TO_END_OF_PARAGRAPH: + return ".uno:DelToEndOfPara"; + case css::awt::Key::DELETE_WORD_BACKWARD: + return ".uno:DelToStartOfWord"; + case css::awt::Key::DELETE_WORD_FORWARD: + return ".uno:DelToEndOfWord"; + case css::awt::Key::INSERT_LINEBREAK: + return ".uno:InsertLinebreak"; + case css::awt::Key::INSERT_PARAGRAPH: + return ".uno:InsertPara"; + case css::awt::Key::MOVE_WORD_BACKWARD: + return ".uno:GoToPrevWord"; + case css::awt::Key::MOVE_WORD_FORWARD: + return ".uno:GoToNextWord"; + case css::awt::Key::MOVE_TO_BEGIN_OF_LINE: + return ".uno:GoToStartOfLine"; + case css::awt::Key::MOVE_TO_END_OF_LINE: + return ".uno:GoToEndOfLine"; + case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH: + return ".uno:GoToStartOfPara"; + case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH: + return ".uno:GoToEndOfPara"; + case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT: + return ".uno:GoToStartOfDoc"; + case css::awt::Key::MOVE_TO_END_OF_DOCUMENT: + return ".uno:GoToEndOfDoc"; + case css::awt::Key::SELECT_BACKWARD: + return ".uno:CharLeftSel"; + case css::awt::Key::SELECT_FORWARD: + return ".uno:CharRightSel"; + case css::awt::Key::SELECT_WORD_BACKWARD: + return ".uno:WordLeftSel"; + case css::awt::Key::SELECT_WORD_FORWARD: + return ".uno:WordRightSel"; + case css::awt::Key::SELECT_WORD: + return ".uno:SelectWord"; + case css::awt::Key::SELECT_LINE: + return OUString(); + case css::awt::Key::SELECT_PARAGRAPH: + return ".uno:SelectText"; + case css::awt::Key::SELECT_TO_BEGIN_OF_LINE: + return ".uno:StartOfLineSel"; + case css::awt::Key::SELECT_TO_END_OF_LINE: + return ".uno:EndOfLineSel"; + case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH: + return ".uno:StartOfParaSel"; + case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH: + return ".uno:EndOfParaSel"; + case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT: + return ".uno:StartOfDocumentSel"; + case css::awt::Key::SELECT_TO_END_OF_DOCUMENT: + return ".uno:EndOfDocumentSel"; + case css::awt::Key::SELECT_ALL: + return ".uno:SelectAll"; + default: + break; + } + } + + return OUString(); +} + + +css::uno::Reference< css::ui::XAcceleratorConfiguration > AcceleratorExecute::st_openModuleConfig(const css::uno::Reference< css::uno::XComponentContext >& rxContext, + const css::uno::Reference< css::frame::XFrame >& xFrame) +{ + css::uno::Reference< css::frame::XModuleManager2 > xModuleDetection( + css::frame::ModuleManager::create(rxContext)); + + OUString sModule; + try + { + sModule = xModuleDetection->identify(xFrame); + } + catch(const css::uno::RuntimeException&) + { throw; } + catch(const css::uno::Exception&) + { return css::uno::Reference< css::ui::XAcceleratorConfiguration >(); } + + css::uno::Reference< css::ui::XModuleUIConfigurationManagerSupplier > xUISupplier( + css::ui::theModuleUIConfigurationManagerSupplier::get(rxContext) ); + + css::uno::Reference< css::ui::XAcceleratorConfiguration > xAccCfg; + try + { + css::uno::Reference< css::ui::XUIConfigurationManager > xUIManager = xUISupplier->getUIConfigurationManager(sModule); + xAccCfg = xUIManager->getShortCutManager(); + } + catch(const css::container::NoSuchElementException&) + {} + return xAccCfg; +} + +css::uno::Reference<css::ui::XAcceleratorConfiguration> AcceleratorExecute::lok_createNewAcceleratorConfiguration(const css::uno::Reference< css::uno::XComponentContext >& rxContext, OUString sModule) +{ + css::uno::Reference< css::ui::XModuleUIConfigurationManagerSupplier > xUISupplier(css::ui::theModuleUIConfigurationManagerSupplier::get(rxContext)); + + try + { + css::uno::Reference<css::ui::XUIConfigurationManager> xUIManager = xUISupplier->getUIConfigurationManager(sModule); + + css::ui::XModuleUIConfigurationManager2* t = static_cast<css::ui::XModuleUIConfigurationManager2*>(xUIManager.get()); + + // Return new short cut manager in case current view's language is different from previous ones. + return t->createShortCutManager(); + } + catch(const css::container::NoSuchElementException&) + {} + + return css::uno::Reference<css::ui::XAcceleratorConfiguration>(); +} + +void AcceleratorExecute::lok_setModuleConfig(css::uno::Reference<css::ui::XAcceleratorConfiguration> acceleratorConfig) +{ + this->m_xModuleCfg = acceleratorConfig; +} + +css::uno::Reference< css::ui::XAcceleratorConfiguration > AcceleratorExecute::st_openDocConfig(const css::uno::Reference< css::frame::XModel >& xModel) +{ + css::uno::Reference< css::ui::XAcceleratorConfiguration > xAccCfg; + css::uno::Reference< css::ui::XUIConfigurationManagerSupplier > xUISupplier(xModel, css::uno::UNO_QUERY); + if (xUISupplier.is()) + { + css::uno::Reference< css::ui::XUIConfigurationManager > xUIManager = xUISupplier->getUIConfigurationManager(); + xAccCfg = xUIManager->getShortCutManager(); + } + return xAccCfg; +} + + +css::uno::Reference< css::util::XURLTransformer > AcceleratorExecute::impl_ts_getURLParser() +{ + // SAFE -> ---------------------------------- + std::unique_lock aLock(m_aLock); + + if (m_xURLParser.is()) + return m_xURLParser; + css::uno::Reference< css::uno::XComponentContext > xContext = m_xContext; + + aLock.unlock(); + // <- SAFE ---------------------------------- + + css::uno::Reference< css::util::XURLTransformer > xParser = css::util::URLTransformer::create( xContext ); + + // SAFE -> ---------------------------------- + aLock.lock(); + m_xURLParser = xParser; + aLock.unlock(); + // <- SAFE ---------------------------------- + + return xParser; +} + +AsyncAccelExec::AsyncAccelExec(css::uno::Reference<css::lang::XComponent> xFrame, + css::uno::Reference<css::frame::XDispatch> xDispatch, + css::util::URL aURL) + : m_xFrame(std::move(xFrame)) + , m_xDispatch(std::move(xDispatch)) + , m_aURL(std::move(aURL)) + , m_aAsyncCallback(LINK(this, AsyncAccelExec, impl_ts_asyncCallback)) +{ + acquire(); +} + +rtl::Reference<AsyncAccelExec> AsyncAccelExec::createOneShotInstance(const css::uno::Reference<css::lang::XComponent> &xFrame, + const css::uno::Reference< css::frame::XDispatch >& xDispatch, + const css::util::URL& rURL) +{ + rtl::Reference<AsyncAccelExec> pExec = new AsyncAccelExec(xFrame, xDispatch, rURL); + return pExec; +} + + +void AsyncAccelExec::execAsync() +{ + if (m_xFrame.is()) + m_xFrame->addEventListener(this); + m_aAsyncCallback.Post(); +} + +IMPL_LINK_NOARG(AsyncAccelExec, impl_ts_asyncCallback, LinkParamNone*, void) +{ + if (m_xDispatch.is()) + { + try + { + if (m_xFrame.is()) + m_xFrame->removeEventListener(this); + m_xDispatch->dispatch(m_aURL, css::uno::Sequence< css::beans::PropertyValue >()); + } + catch(const css::uno::Exception&) + { + } + } + release(); +} + +} // namespace svt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/bindablecontrolhelper.cxx b/svtools/source/misc/bindablecontrolhelper.cxx new file mode 100644 index 0000000000..ba54949756 --- /dev/null +++ b/svtools/source/misc/bindablecontrolhelper.cxx @@ -0,0 +1,144 @@ +/* -*- 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 <svtools/bindablecontrolhelper.hxx> +#include <com/sun/star/form/binding/XBindableValue.hpp> +#include <com/sun/star/form/binding/XValueBinding.hpp> +#include <com/sun/star/form/binding/XListEntrySink.hpp> +#include <com/sun/star/form/binding/XListEntrySource.hpp> +#include <com/sun/star/frame/XModel.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/table/CellRangeAddress.hpp> +#include <com/sun/star/sheet/XCellRangeReferrer.hpp> +#include <com/sun/star/sheet/XCellRangeAddressable.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/table/CellAddress.hpp> +#include <com/sun/star/beans/NamedValue.hpp> + + +namespace svt +{ + + +using namespace ::com::sun::star; + +static bool lcl_isNamedRange( const OUString& sAddress, const uno::Reference< frame::XModel >& xModel, css::table::CellRangeAddress& aAddress ) +{ + bool bRes = false; + uno::Reference< sheet::XCellRangeReferrer > xReferrer; + try + { + uno::Reference< beans::XPropertySet > xPropSet( xModel, uno::UNO_QUERY_THROW ); + uno::Reference< container::XNameAccess > xNamed( xPropSet->getPropertyValue( "NamedRanges" ), uno::UNO_QUERY_THROW ); + xReferrer.set ( xNamed->getByName( sAddress ), uno::UNO_QUERY ); + } + catch( uno::Exception& /*e*/ ) + { + // do nothing + } + if ( xReferrer.is() ) + { + uno::Reference< sheet::XCellRangeAddressable > xRangeAddressable( xReferrer->getReferredCells(), uno::UNO_QUERY ); + if ( xRangeAddressable.is() ) + { + aAddress = xRangeAddressable->getRangeAddress(); + bRes = true; + } + } + return bRes; +} + + +void +BindableControlHelper::ApplyListSourceAndBindableData( const css::uno::Reference< css::frame::XModel >& xModel, const css::uno::Reference< css::uno::XInterface >& rObj, const OUString& rsCtrlSource, const OUString& rsRowSource, sal_uInt16 nRefTab ) +{ +// XBindable etc. + uno::Reference< lang::XMultiServiceFactory > xFac; + if ( xModel.is() ) + xFac.set( xModel, uno::UNO_QUERY ); + uno::Reference< form::binding::XBindableValue > xBindable( rObj, uno::UNO_QUERY ); + if ( xFac.is() && rsCtrlSource.getLength() && xBindable.is() ) + { + + // OOo address structures + // RefCell - convert from XL + // pretend we converted the imported string address into the + // appropriate address structure + uno::Reference< beans::XPropertySet > xConvertor( xFac->createInstance( "com.sun.star.table.CellAddressConversion"), uno::UNO_QUERY ); + css::table::CellAddress aAddress; + if ( xConvertor.is() ) + { + // we need this service to properly convert XL notation also + // Should be easy to extend + xConvertor->setPropertyValue( "ReferenceSheet", uno::Any( nRefTab ) ); + xConvertor->setPropertyValue( "XLA1Representation", uno::Any( rsCtrlSource ) ); + xConvertor->getPropertyValue( "Address" ) >>= aAddress; + } + + beans::NamedValue aArg1; + aArg1.Name = "BoundCell"; + aArg1.Value <<= aAddress; + + uno::Sequence< uno::Any > aArgs{ uno::Any(aArg1) }; + uno::Reference< form::binding::XValueBinding > xBinding( xFac->createInstanceWithArguments( "com.sun.star.table.CellValueBinding", aArgs ), uno::UNO_QUERY ); + xBindable->setValueBinding( xBinding ); + } + else if ( xBindable.is() ) // reset it + xBindable->setValueBinding( uno::Reference< form::binding::XValueBinding >() ); + uno::Reference< form::binding::XListEntrySink > xListEntrySink( rObj, uno::UNO_QUERY ); + if ( xFac.is() && rsRowSource.getLength() && xListEntrySink.is() ) + { + + // OOo address structures + // RefCell - convert from XL + // pretend we converted the imported string address into the + // appropriate address structure + uno::Reference< beans::XPropertySet > xConvertor( xFac->createInstance( "com.sun.star.table.CellRangeAddressConversion"), uno::UNO_QUERY ); + css::table::CellRangeAddress aAddress; + if ( xConvertor.is() ) + { + if ( !lcl_isNamedRange( rsRowSource, xModel, aAddress ) ) + { + // we need this service to properly convert XL notation also + // Should be easy to extend + xConvertor->setPropertyValue( "ReferenceSheet", uno::Any( nRefTab ) ); + xConvertor->setPropertyValue( "XLA1Representation", uno::Any( rsRowSource ) ); + xConvertor->getPropertyValue( "Address" ) >>= aAddress; + } + } + + beans::NamedValue aArg1; + aArg1.Name = "CellRange"; + aArg1.Value <<= aAddress; + + uno::Sequence< uno::Any > aArgs{ uno::Any(aArg1) }; + uno::Reference< form::binding::XListEntrySource > xSource( xFac->createInstanceWithArguments( "com.sun.star.table.CellRangeListSource", aArgs ), uno::UNO_QUERY ); + xListEntrySink->setListEntrySource( xSource ); + } + else if ( xListEntrySink.is() ) // reset + xListEntrySink->setListEntrySource( uno::Reference< form::binding::XListEntrySource >() ); + +} + + +} // namespace svt + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/cliplistener.cxx b/svtools/source/misc/cliplistener.cxx new file mode 100644 index 0000000000..992d9efb93 --- /dev/null +++ b/svtools/source/misc/cliplistener.cxx @@ -0,0 +1,85 @@ +/* -*- 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/datatransfer/clipboard/XClipboardNotifier.hpp> + +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> + +#include <svtools/cliplistener.hxx> +#include <vcl/transfer.hxx> + +using namespace ::com::sun::star; + + +TransferableClipboardListener::TransferableClipboardListener( const Link<TransferableDataHelper*,void>& rCallback ) : + aLink( rCallback ) +{ +} + +TransferableClipboardListener::~TransferableClipboardListener() +{ +} + +void SAL_CALL TransferableClipboardListener::disposing( const lang::EventObject& ) +{ +} + +void SAL_CALL TransferableClipboardListener::changedContents( + const datatransfer::clipboard::ClipboardEvent& rEventObject ) +{ + if ( aLink.IsSet() ) + { + const SolarMutexGuard aGuard; + + TransferableDataHelper aDataHelper( rEventObject.Contents ); + aLink.Call( &aDataHelper ); + } +} + +void TransferableClipboardListener::AddRemoveListener( vcl::Window* pWin, bool bAdd ) +{ + try + { + if ( pWin ) + { + uno::Reference<datatransfer::clipboard::XClipboard> xClipboard = pWin->GetClipboard(); + uno::Reference<datatransfer::clipboard::XClipboardNotifier> xClpbrdNtfr( xClipboard, uno::UNO_QUERY ); + if( xClpbrdNtfr.is() ) + { + uno::Reference<datatransfer::clipboard::XClipboardListener> xClipEvtLstnr( this ); + if( bAdd ) + xClpbrdNtfr->addClipboardListener( xClipEvtLstnr ); + else + xClpbrdNtfr->removeClipboardListener( xClipEvtLstnr ); + } + } + } + catch( const css::uno::Exception& ) + { + } +} + +void TransferableClipboardListener::ClearCallbackLink() +{ + aLink = Link<TransferableDataHelper*,void>(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/dialogclosedlistener.cxx b/svtools/source/misc/dialogclosedlistener.cxx new file mode 100644 index 0000000000..c9e4c095e4 --- /dev/null +++ b/svtools/source/misc/dialogclosedlistener.cxx @@ -0,0 +1,59 @@ +/* -*- 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 <svtools/dialogclosedlistener.hxx> + + +namespace svt +{ + + + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::ui::dialogs; + + + //= DialogClosedListener + + + DialogClosedListener::DialogClosedListener() + { + } + + // XDialogClosedListener methods + void SAL_CALL DialogClosedListener::dialogClosed( const DialogClosedEvent& aEvent ) + { + if ( m_aDialogClosedLink.IsSet() ) + { + css::ui::dialogs::DialogClosedEvent aEvt( aEvent ); + m_aDialogClosedLink.Call( &aEvt ); + } + } + + // XEventListener methods + void SAL_CALL DialogClosedListener::disposing( const EventObject& ) + { + m_aDialogClosedLink = Link<css::ui::dialogs::DialogClosedEvent*,void>(); + } + + +} // namespace svt + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/ehdl.cxx b/svtools/source/misc/ehdl.cxx new file mode 100644 index 0000000000..59613089a5 --- /dev/null +++ b/svtools/source/misc/ehdl.cxx @@ -0,0 +1,293 @@ +/* -*- 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 <unotools/resmgr.hxx> +#include <utility> +#include <vcl/stdtext.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <sal/log.hxx> + +#include <svtools/ehdl.hxx> +#include <svtools/svtresid.hxx> +#include <svtools/sfxecode.hxx> +#include <memory> +#include <errtxt.hrc> + +static DialogMask aWndFunc( + weld::Window *pWin, // Parent of the dialog + DialogMask nFlags, + const OUString &rErr, // error text + const OUString &rAction) // action text + +/* [Description] + + Draw an errorbox on the screen. Depending on nFlags + Error/Info etc. boxes with the requested buttons are shown. + + Returnvalue is the button pressed + + */ + + +{ + SolarMutexGuard aGuard; + + // determine necessary WinBits from the flags + VclButtonsType eButtonsType = VclButtonsType::NONE; + bool bAddRetry = false; + if ( (nFlags & (DialogMask::ButtonsCancel | DialogMask::ButtonsRetry)) == (DialogMask::ButtonsCancel | DialogMask::ButtonsRetry)) + { + bAddRetry = true; + eButtonsType = VclButtonsType::Cancel; + } + else if ( (nFlags & DialogMask::ButtonsOk) == DialogMask::ButtonsOk ) + eButtonsType = VclButtonsType::Ok; + else if ( (nFlags & DialogMask::ButtonsYesNo) == DialogMask::ButtonsYesNo ) + eButtonsType = VclButtonsType::YesNo; + + OUString aErr("$(ACTION)$(ERROR)"); + OUString aAction(rAction); + if ( !aAction.isEmpty() ) + aAction += ":\n"; + aErr = aErr.replaceAll("$(ACTION)", aAction); + aErr = aErr.replaceAll("$(ERROR)", rErr); + + VclMessageType eMessageType; + switch (nFlags & DialogMask(0xf000)) + { + case DialogMask::MessageError: + eMessageType = VclMessageType::Error; + break; + + case DialogMask::MessageWarning: + eMessageType = VclMessageType::Warning; + break; + + case DialogMask::MessageInfo: + eMessageType = VclMessageType::Info; + break; + + default: + { + SAL_WARN( "svtools.misc", "no MessBox type"); + return DialogMask::ButtonsOk; + } + } + + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pWin, + eMessageType, eButtonsType, aErr)); + + if (bAddRetry) + xBox->add_button(GetStandardText(StandardButtonType::Retry), RET_RETRY); + + switch(nFlags & DialogMask(0x0f00)) + { + case DialogMask::ButtonDefaultsOk: + xBox->set_default_response(RET_OK); + break; + case DialogMask::ButtonDefaultsCancel: + xBox->set_default_response(RET_CANCEL); + break; + case DialogMask::ButtonDefaultsYes: + xBox->set_default_response(RET_YES); + break; + case DialogMask::ButtonDefaultsNo: + xBox->set_default_response(RET_NO); + break; + default: + break; + } + + DialogMask nRet = DialogMask::NONE; + switch (xBox->run()) + { + case RET_OK: + nRet = DialogMask::ButtonsOk; + break; + case RET_CANCEL: + nRet = DialogMask::ButtonsCancel; + break; + case RET_RETRY: + nRet = DialogMask::ButtonsRetry; + break; + case RET_YES: + nRet = DialogMask::ButtonsYes; + break; + case RET_NO: + nRet = DialogMask::ButtonsNo; + break; + default: + SAL_WARN( "svtools.misc", "Unknown MessBox return value" ); + break; + } + + return nRet; +} + +SfxErrorHandler::SfxErrorHandler(const ErrMsgCode* pIdPs, ErrCodeArea lStartP, ErrCodeArea lEndP, const std::locale& rLocale) + : lStart(lStartP), lEnd(lEndP), pIds(pIdPs), aResLocale(rLocale) +{ + ErrorRegistry::RegisterDisplay(&aWndFunc); +} + +SfxErrorHandler::~SfxErrorHandler() +{ +} + +bool SfxErrorHandler::CreateString(const ErrCodeMsg& nErr, OUString &rStr) const + +/* [Description] + + Assemble error string for the ErrorInfo pErr. + + */ + +{ + ErrCode nErrCode(sal_uInt32(nErr.GetCode()) & ERRCODE_ERROR_MASK); + if (nErr.GetCode().GetArea() < lStart || lEnd < nErr.GetCode().GetArea()) + return false; + if(GetErrorString(nErrCode, rStr)) + { + if(!nErr.GetArg1().isEmpty()) + rStr = rStr.replaceAll("$(ARG1)", nErr.GetArg1()); + if(!nErr.GetArg2().isEmpty()) + rStr = rStr.replaceAll("$(ARG2)", nErr.GetArg2()); + return true; + } + return false; +} + +void SfxErrorHandler::GetClassString(ErrCodeClass lClassId, OUString &rStr) + +/* [Description] + + Creates the string for the class of the error. Will always + be read from the resource of the Sfx. + + */ + +{ + for (const std::pair<TranslateId, ErrCodeClass>* pItem = RID_ERRHDL_CLASS; pItem->first; ++pItem) + { + if (pItem->second == lClassId) + { + rStr = SvtResId(pItem->first); + break; + } + } +} + +bool SfxErrorHandler::GetErrorString(ErrCode lErrId, OUString &rStr) const + +/* [Description] + + Creates the error string for the actual error + without its class + + */ + +{ + bool bRet = false; + rStr = "$(CLASS)$(ERROR)"; + + for (const ErrMsgCode* pItem = pIds; pItem->second; ++pItem) + { + if (pItem->second.StripWarning() == lErrId.StripWarning()) + { + rStr = rStr.replaceAll("$(ERROR)", Translate::get(pItem->first, aResLocale)); + bRet = true; + break; + } + } + + if( bRet ) + { + OUString aErrStr; + GetClassString(lErrId.GetClass(), aErrStr); + if(!aErrStr.isEmpty()) + aErrStr += ".\n"; + rStr = rStr.replaceAll("$(CLASS)",aErrStr); + } + + return bRet; +} + +SfxErrorContext::SfxErrorContext( + sal_uInt16 nCtxIdP, weld::Window *pWindow, const ErrMsgCode* pIdsP, const std::locale& rResLocaleP) +: ErrorContext(pWindow), nCtxId(nCtxIdP), pIds(pIdsP), aResLocale(rResLocaleP) +{ + if (!pIds) + pIds = RID_ERRCTX; +} + + +SfxErrorContext::SfxErrorContext( + sal_uInt16 nCtxIdP, OUString aArg1P, weld::Window *pWindow, + const ErrMsgCode* pIdsP, const std::locale& rResLocaleP) +: ErrorContext(pWindow), nCtxId(nCtxIdP), pIds(pIdsP), aResLocale(rResLocaleP), + aArg1(std::move(aArg1P)) +{ + if (!pIds) + pIds = RID_ERRCTX; +} + +bool SfxErrorContext::GetString(const ErrCodeMsg& nErr, OUString &rStr) + +/* [Description] + + Constructs the description of an error context + */ + +{ + bool bRet = false; + for (const ErrMsgCode* pItem = pIds; pItem->second; ++pItem) + { + if (sal_uInt32(pItem->second) == nCtxId) + { + rStr = Translate::get(pItem->first, aResLocale); + rStr = rStr.replaceAll("$(ARG1)", aArg1); + bRet = true; + break; + } + } + + SAL_WARN_IF(!bRet, "svtools.misc", "ErrorContext cannot find the resource"); + + if ( bRet ) + { + sal_uInt16 nId = nErr.IsWarning() ? ERRCTX_WARNING : ERRCTX_ERROR; + for (const ErrMsgCode* pItem = RID_ERRCTX; pItem->second; ++pItem) + { + if (sal_uInt32(pItem->second) == nId) + { + rStr = rStr.replaceAll("$(ERR)", Translate::get(pItem->first, aResLocale)); + break; + } + } + } + + // SfxInPlaceClient::DoVerb adds some extra info to report + if (bRet && nErr.GetCode() == ERRCODE_SO_GENERALERROR && !nErr.GetArg1().isEmpty()) + rStr += "\n" + nErr.GetArg1(); + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/embedhlp.cxx b/svtools/source/misc/embedhlp.cxx new file mode 100644 index 0000000000..d784b20a4a --- /dev/null +++ b/svtools/source/misc/embedhlp.cxx @@ -0,0 +1,1096 @@ +/* -*- 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 <libxml/xmlwriter.h> + +#include <svtools/embedhlp.hxx> +#include <vcl/graphicfilter.hxx> +#include <vcl/gdimtf.hxx> +#include <vcl/outdev.hxx> +#include <vcl/gfxlink.hxx> +#include <vcl/TypeSerializer.hxx> +#include <bitmaps.hlst> + +#include <sal/log.hxx> +#include <comphelper/fileformat.h> +#include <comphelper/embeddedobjectcontainer.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <unotools/streamwrap.hxx> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/XCoordinateSystem.hpp> +#include <com/sun/star/chart2/XCoordinateSystemContainer.hpp> +#include <com/sun/star/chart2/XDiagram.hpp> +#include <com/sun/star/chart2/XChartTypeContainer.hpp> +#include <com/sun/star/chart2/XChartType.hpp> +#include <tools/globname.hxx> +#include <comphelper/classids.hxx> +#include <com/sun/star/util/CloseVetoException.hpp> +#include <com/sun/star/util/XModifyListener.hpp> +#include <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XStateChangeListener.hpp> +#include <com/sun/star/embed/XLinkageSupport.hpp> +#include <com/sun/star/chart2/XDefaultSizeTransmitter.hpp> +#include <com/sun/star/qa/XDumper.hpp> +#include <embeddedobj/embeddedupdate.hxx> +#include <cppuhelper/implbase.hxx> +#include <vcl/svapp.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/debug.hxx> +#include <memory> + +using namespace com::sun::star; + +namespace svt { + +namespace { + +class EmbedEventListener_Impl : public ::cppu::WeakImplHelper < embed::XStateChangeListener, + document::XEventListener, + util::XModifyListener, + util::XCloseListener > +{ +public: + EmbeddedObjectRef* pObject; + sal_Int32 nState; + + explicit EmbedEventListener_Impl( EmbeddedObjectRef* p ) : + pObject(p) + , nState(-1) + {} + + static rtl::Reference<EmbedEventListener_Impl> Create( EmbeddedObjectRef* ); + + virtual void SAL_CALL changingState( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override; + virtual void SAL_CALL stateChanged( const lang::EventObject& aEvent, ::sal_Int32 nOldState, ::sal_Int32 nNewState ) override; + virtual void SAL_CALL queryClosing( const lang::EventObject& Source, sal_Bool GetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const lang::EventObject& Source ) override; + virtual void SAL_CALL notifyEvent( const document::EventObject& aEvent ) override; + virtual void SAL_CALL disposing( const lang::EventObject& aEvent ) override; + virtual void SAL_CALL modified( const css::lang::EventObject& aEvent ) override; +}; + +} + +rtl::Reference<EmbedEventListener_Impl> EmbedEventListener_Impl::Create( EmbeddedObjectRef* p ) +{ + rtl::Reference<EmbedEventListener_Impl> pRet(new EmbedEventListener_Impl( p )); + + if ( p->GetObject().is() ) + { + p->GetObject()->addStateChangeListener( pRet ); + + uno::Reference < util::XCloseable > xClose = p->GetObject(); + DBG_ASSERT( xClose.is(), "Object does not support XCloseable!" ); + if ( xClose.is() ) + xClose->addCloseListener( pRet ); + + uno::Reference < document::XEventBroadcaster > xBrd = p->GetObject(); + if ( xBrd.is() ) + xBrd->addEventListener( pRet ); + + pRet->nState = p->GetObject()->getCurrentState(); + if ( pRet->nState == embed::EmbedStates::RUNNING ) + { + uno::Reference < util::XModifiable > xMod( p->GetObject()->getComponent(), uno::UNO_QUERY ); + if ( xMod.is() ) + // listen for changes in running state (update replacements in case of changes) + xMod->addModifyListener( pRet ); + } + } + + return pRet; +} + +void SAL_CALL EmbedEventListener_Impl::changingState( const lang::EventObject&, + ::sal_Int32, + ::sal_Int32 ) +{ +} + +void SAL_CALL EmbedEventListener_Impl::stateChanged( const lang::EventObject&, + ::sal_Int32 nOldState, + ::sal_Int32 nNewState ) +{ + SolarMutexGuard aGuard; + nState = nNewState; + if ( !pObject ) + return; + + uno::Reference < util::XModifiable > xMod( pObject->GetObject()->getComponent(), uno::UNO_QUERY ); + if ( nNewState == embed::EmbedStates::RUNNING ) + { + bool bProtected = false; + if (pObject->GetIsProtectedHdl().IsSet()) + { + bProtected = pObject->GetIsProtectedHdl().Call(nullptr); + } + + // TODO/LATER: container must be set before! + // When is this event created? Who sets the new container when it changed? + if ((pObject->GetViewAspect() != embed::Aspects::MSOLE_ICON) + && nOldState != embed::EmbedStates::LOADED && !pObject->IsChart() && !bProtected) + // get new replacement after deactivation + pObject->UpdateReplacement(); + + if( pObject->IsChart() && nOldState == embed::EmbedStates::UI_ACTIVE ) + { + //create a new metafile replacement when leaving the edit mode + //for buggy documents where the old image looks different from the correct one + if( xMod.is() && !xMod->isModified() )//in case of modification a new replacement will be requested anyhow + pObject->UpdateReplacementOnDemand(); + } + + if ( xMod.is() && nOldState == embed::EmbedStates::LOADED ) + // listen for changes (update replacements in case of changes) + xMod->addModifyListener( this ); + } + else if ( nNewState == embed::EmbedStates::LOADED ) + { + // in loaded state we can't listen + if ( xMod.is() ) + xMod->removeModifyListener( this ); + } +} + +void SAL_CALL EmbedEventListener_Impl::modified( const lang::EventObject& ) +{ + SolarMutexGuard aGuard; + if ( !(pObject && pObject->GetViewAspect() != embed::Aspects::MSOLE_ICON) ) + return; + + if ( nState == embed::EmbedStates::RUNNING ) + { + // updates only necessary in non-active states + if( pObject->IsChart() ) + pObject->UpdateReplacementOnDemand(); + else + pObject->UpdateReplacement(); + } + else if ( nState == embed::EmbedStates::ACTIVE || + nState == embed::EmbedStates::UI_ACTIVE || + nState == embed::EmbedStates::INPLACE_ACTIVE ) + { + // in case the object is inplace or UI active the replacement image should be updated on demand + pObject->UpdateReplacementOnDemand(); + } +} + +void SAL_CALL EmbedEventListener_Impl::notifyEvent( const document::EventObject& aEvent ) +{ + SolarMutexGuard aGuard; + + if ( pObject && aEvent.EventName == "OnVisAreaChanged" && pObject->GetViewAspect() != embed::Aspects::MSOLE_ICON && !pObject->IsChart() ) + { + pObject->UpdateReplacement(); + } +} + +void SAL_CALL EmbedEventListener_Impl::queryClosing( const lang::EventObject& Source, sal_Bool ) +{ + // An embedded object can be shared between several objects (f.e. for undo purposes) + // the object will not be closed before the last "customer" is destroyed + // Now the EmbeddedObjectRef helper class works like a "lock" on the object + if ( pObject && pObject->IsLocked() && Source.Source == pObject->GetObject() ) + throw util::CloseVetoException(); +} + +void SAL_CALL EmbedEventListener_Impl::notifyClosing( const lang::EventObject& Source ) +{ + if ( pObject && Source.Source == pObject->GetObject() ) + { + pObject->Clear(); + pObject = nullptr; + } +} + +void SAL_CALL EmbedEventListener_Impl::disposing( const lang::EventObject& aEvent ) +{ + if ( pObject && aEvent.Source == pObject->GetObject() ) + { + pObject->Clear(); + pObject = nullptr; + } +} + +struct EmbeddedObjectRef_Impl +{ + uno::Reference <embed::XEmbeddedObject> mxObj; + + rtl::Reference<EmbedEventListener_Impl> mxListener; + OUString aPersistName; + OUString aMediaType; + comphelper::EmbeddedObjectContainer* pContainer; + std::optional<Graphic> oGraphic; + sal_Int64 nViewAspect; + bool bIsLocked:1; + bool bNeedUpdate:1; + bool bUpdating:1; + + // #i104867# + sal_uInt32 mnGraphicVersion; + awt::Size aDefaultSizeForChart_In_100TH_MM;//#i103460# charts do not necessarily have an own size within ODF files, in this case they need to use the size settings from the surrounding frame, which is made available with this member + + Link<LinkParamNone*, bool> m_aIsProtectedHdl; + + EmbeddedObjectRef_Impl() : + pContainer(nullptr), + nViewAspect(embed::Aspects::MSOLE_CONTENT), + bIsLocked(false), + bNeedUpdate(false), + bUpdating(false), + mnGraphicVersion(0), + aDefaultSizeForChart_In_100TH_MM(awt::Size(8000,7000)) + {} + + EmbeddedObjectRef_Impl( const EmbeddedObjectRef_Impl& r ) : + mxObj(r.mxObj), + aPersistName(r.aPersistName), + aMediaType(r.aMediaType), + pContainer(r.pContainer), + nViewAspect(r.nViewAspect), + bIsLocked(r.bIsLocked), + bNeedUpdate(r.bNeedUpdate), + bUpdating(r.bUpdating), + mnGraphicVersion(0), + aDefaultSizeForChart_In_100TH_MM(r.aDefaultSizeForChart_In_100TH_MM) + { + if (r.oGraphic && !r.bNeedUpdate) + oGraphic.emplace(*r.oGraphic); + } + + void dumpAsXml(xmlTextWriterPtr pWriter) const + { + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("EmbeddedObjectRef_Impl")); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mxObj")); + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("symbol"), + BAD_CAST(typeid(*mxObj).name())); + css::uno::Reference<css::qa::XDumper> pComponent( + mxObj->getComponent(), css::uno::UNO_QUERY); + if (pComponent.is()) + { + auto const s = pComponent->dump(""); + auto const s1 = OUStringToOString(s, RTL_TEXTENCODING_ISO_8859_1); //TODO + (void)xmlTextWriterWriteRawLen( + pWriter, reinterpret_cast<xmlChar const *>(s1.getStr()), s1.getLength()); + } + (void)xmlTextWriterEndElement(pWriter); + + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("pGraphic")); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", oGraphic ? &*oGraphic : nullptr); + if (oGraphic) + { + (void)xmlTextWriterWriteAttribute( + pWriter, BAD_CAST("is-none"), + BAD_CAST(OString::boolean(oGraphic->IsNone()).getStr())); + } + (void)xmlTextWriterEndElement(pWriter); + + (void)xmlTextWriterEndElement(pWriter); + } +}; + +const uno::Reference <embed::XEmbeddedObject>& EmbeddedObjectRef::operator->() const +{ + return mpImpl->mxObj; +} + +const uno::Reference <embed::XEmbeddedObject>& EmbeddedObjectRef::GetObject() const +{ + return mpImpl->mxObj; +} + +EmbeddedObjectRef::EmbeddedObjectRef() : mpImpl(new EmbeddedObjectRef_Impl) {} + +EmbeddedObjectRef::EmbeddedObjectRef( const uno::Reference < embed::XEmbeddedObject >& xObj, sal_Int64 nAspect ) : + mpImpl(new EmbeddedObjectRef_Impl) +{ + mpImpl->nViewAspect = nAspect; + mpImpl->mxObj = xObj; + mpImpl->mxListener = EmbedEventListener_Impl::Create( this ); +} + +EmbeddedObjectRef::EmbeddedObjectRef( const EmbeddedObjectRef& rObj ) : + mpImpl(new EmbeddedObjectRef_Impl(*rObj.mpImpl)) +{ + mpImpl->mxListener = EmbedEventListener_Impl::Create( this ); +} + +EmbeddedObjectRef::~EmbeddedObjectRef() +{ + Clear(); +} + +void EmbeddedObjectRef::Assign( const uno::Reference < embed::XEmbeddedObject >& xObj, sal_Int64 nAspect ) +{ + DBG_ASSERT(!mpImpl->mxObj.is(), "Never assign an already assigned object!"); + + Clear(); + mpImpl->nViewAspect = nAspect; + mpImpl->mxObj = xObj; + mpImpl->mxListener = EmbedEventListener_Impl::Create( this ); + + //#i103460# + if ( IsChart() ) + { + uno::Reference < chart2::XDefaultSizeTransmitter > xSizeTransmitter( xObj, uno::UNO_QUERY ); + DBG_ASSERT( xSizeTransmitter.is(), "Object does not support XDefaultSizeTransmitter -> will cause #i103460#!" ); + if( xSizeTransmitter.is() ) + xSizeTransmitter->setDefaultSize( mpImpl->aDefaultSizeForChart_In_100TH_MM ); + } +} + +void EmbeddedObjectRef::Clear() +{ + if (mpImpl->mxObj.is() && mpImpl->mxListener.is()) + { + mpImpl->mxObj->removeStateChangeListener(mpImpl->mxListener); + + mpImpl->mxObj->removeCloseListener( mpImpl->mxListener ); + mpImpl->mxObj->removeEventListener( mpImpl->mxListener ); + + if ( mpImpl->bIsLocked ) + { + try + { + mpImpl->mxObj->changeState(embed::EmbedStates::LOADED); + mpImpl->mxObj->close( true ); + } + catch (const util::CloseVetoException&) + { + // there's still someone who needs the object! + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("svtools.misc", "Error on switching of the object to loaded state and closing"); + } + } + } + + if (mpImpl->mxListener.is()) + { + mpImpl->mxListener->pObject = nullptr; + mpImpl->mxListener.clear(); + } + + mpImpl->mxObj = nullptr; + mpImpl->pContainer = nullptr; + mpImpl->bIsLocked = false; + mpImpl->bNeedUpdate = false; +} + +bool EmbeddedObjectRef::is() const +{ + return mpImpl->mxObj.is(); +} + +void EmbeddedObjectRef::AssignToContainer( comphelper::EmbeddedObjectContainer* pContainer, const OUString& rPersistName ) +{ + mpImpl->pContainer = pContainer; + mpImpl->aPersistName = rPersistName; + + if ( mpImpl->oGraphic && !mpImpl->bNeedUpdate && pContainer ) + SetGraphicToContainer( *mpImpl->oGraphic, *pContainer, mpImpl->aPersistName, OUString() ); +} + +comphelper::EmbeddedObjectContainer* EmbeddedObjectRef::GetContainer() const +{ + return mpImpl->pContainer; +} + +sal_Int64 EmbeddedObjectRef::GetViewAspect() const +{ + return mpImpl->nViewAspect; +} + +void EmbeddedObjectRef::SetViewAspect( sal_Int64 nAspect ) +{ + mpImpl->nViewAspect = nAspect; +} + +void EmbeddedObjectRef::Lock( bool bLock ) +{ + mpImpl->bIsLocked = bLock; +} + +bool EmbeddedObjectRef::IsLocked() const +{ + return mpImpl->bIsLocked; +} + +void EmbeddedObjectRef::SetIsProtectedHdl(const Link<LinkParamNone*, bool>& rProtectedHdl) +{ + mpImpl->m_aIsProtectedHdl = rProtectedHdl; +} + +const Link<LinkParamNone*, bool> & EmbeddedObjectRef::GetIsProtectedHdl() const +{ + return mpImpl->m_aIsProtectedHdl; +} + +void EmbeddedObjectRef::GetReplacement( bool bUpdate ) +{ + Graphic aOldGraphic; + + if ( bUpdate ) + { + if (mpImpl->oGraphic) + aOldGraphic = *mpImpl->oGraphic; + + mpImpl->oGraphic.reset(); + mpImpl->aMediaType.clear(); + mpImpl->oGraphic.emplace(); + mpImpl->mnGraphicVersion++; + } + else if ( !mpImpl->oGraphic ) + { + mpImpl->oGraphic.emplace(); + mpImpl->mnGraphicVersion++; + } + else + { + OSL_FAIL("No update, but replacement exists already!"); + return; + } + + std::unique_ptr<SvStream> pGraphicStream(GetGraphicStream( bUpdate )); + if (!pGraphicStream && aOldGraphic.IsNone()) + { + // We have no old graphic, tried to get an updated one, but that failed. Try to get an old + // graphic instead of having no graphic at all. + pGraphicStream = GetGraphicStream(false); + SAL_WARN("svtools.misc", + "EmbeddedObjectRef::GetReplacement: failed to get updated graphic stream"); + } + + if ( pGraphicStream ) + { + GraphicFilter& rGF = GraphicFilter::GetGraphicFilter(); + if( mpImpl->oGraphic ) + rGF.ImportGraphic( *mpImpl->oGraphic, u"", *pGraphicStream ); + mpImpl->mnGraphicVersion++; + } + + // note that UpdateReplacementOnDemand which resets mpImpl->oGraphic to null may have been called + // e.g. when exporting ooo58458-1.odt to doc + if (bUpdate && (!mpImpl->oGraphic || mpImpl->oGraphic->IsNone()) && !aOldGraphic.IsNone()) + { + // We used to have an old graphic, tried to update and the update + // failed. Go back to the old graphic instead of having no graphic at + // all. + mpImpl->oGraphic.emplace(aOldGraphic); + SAL_WARN("svtools.misc", "EmbeddedObjectRef::GetReplacement: failed to update graphic"); + } +} + +const Graphic* EmbeddedObjectRef::GetGraphic() const +{ + try + { + if ( mpImpl->bNeedUpdate ) + // bNeedUpdate will be set to false while retrieving new replacement + const_cast < EmbeddedObjectRef* >(this)->GetReplacement(true); + else if ( !mpImpl->oGraphic ) + const_cast < EmbeddedObjectRef* >(this)->GetReplacement(false); + } + catch( const uno::Exception& ) + { + DBG_UNHANDLED_EXCEPTION("svtools.misc", "Something went wrong on getting the graphic"); + } + + return mpImpl->oGraphic ? &*mpImpl->oGraphic : nullptr; +} + +Size EmbeddedObjectRef::GetSize( MapMode const * pTargetMapMode ) const +{ + MapMode aSourceMapMode( MapUnit::Map100thMM ); + Size aResult; + + if ( mpImpl->nViewAspect == embed::Aspects::MSOLE_ICON ) + { + const Graphic* pGraphic = GetGraphic(); + if ( pGraphic ) + { + aSourceMapMode = pGraphic->GetPrefMapMode(); + aResult = pGraphic->GetPrefSize(); + } + else + aResult = Size( 2500, 2500 ); + } + else + { + awt::Size aSize; + + if (mpImpl->mxObj.is()) + { + try + { + aSize = mpImpl->mxObj->getVisualAreaSize(mpImpl->nViewAspect); + } + catch(const embed::NoVisualAreaSizeException&) + { + SAL_WARN("svtools.misc", "EmbeddedObjectRef::GetSize: no visual area size"); + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("svtools.misc", "Something went wrong on getting of the size of the object"); + } + + try + { + aSourceMapMode = MapMode(VCLUnoHelper::UnoEmbed2VCLMapUnit(mpImpl->mxObj->getMapUnit(mpImpl->nViewAspect))); + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("svtools.misc", "Can not get the map mode"); + } + } + + if ( !aSize.Height && !aSize.Width ) + { + SAL_WARN("svtools.misc", "EmbeddedObjectRef::GetSize: empty size, defaulting to 5x5cm"); + aSize.Width = 5000; + aSize.Height = 5000; + } + + aResult = Size( aSize.Width, aSize.Height ); + } + + if ( pTargetMapMode ) + aResult = OutputDevice::LogicToLogic( aResult, aSourceMapMode, *pTargetMapMode ); + + return aResult; +} + +void EmbeddedObjectRef::SetGraphicStream( const uno::Reference< io::XInputStream >& xInGrStream, + const OUString& rMediaType ) +{ + mpImpl->oGraphic.emplace(); + mpImpl->aMediaType = rMediaType; + mpImpl->mnGraphicVersion++; + + std::unique_ptr<SvStream> pGraphicStream(::utl::UcbStreamHelper::CreateStream( xInGrStream )); + + if ( pGraphicStream ) + { + GraphicFilter& rGF = GraphicFilter::GetGraphicFilter(); + rGF.ImportGraphic( *mpImpl->oGraphic, u"", *pGraphicStream ); + mpImpl->mnGraphicVersion++; + + if ( mpImpl->pContainer ) + { + pGraphicStream->Seek( 0 ); + uno::Reference< io::XInputStream > xInSeekGrStream = new ::utl::OSeekableInputStreamWrapper( pGraphicStream.get() ); + + mpImpl->pContainer->InsertGraphicStream( xInSeekGrStream, mpImpl->aPersistName, rMediaType ); + } + } + + mpImpl->bNeedUpdate = false; + +} + +void EmbeddedObjectRef::SetGraphic( const Graphic& rGraphic, const OUString& rMediaType ) +{ + mpImpl->oGraphic.emplace( rGraphic ); + mpImpl->aMediaType = rMediaType; + mpImpl->mnGraphicVersion++; + + if ( mpImpl->pContainer ) + SetGraphicToContainer( rGraphic, *mpImpl->pContainer, mpImpl->aPersistName, rMediaType ); + + mpImpl->bNeedUpdate = false; +} + +std::unique_ptr<SvStream> EmbeddedObjectRef::GetGraphicStream( bool bUpdate ) const +{ + DBG_ASSERT( bUpdate || mpImpl->pContainer, "Can't retrieve current graphic!" ); + uno::Reference < io::XInputStream > xStream; + if ( mpImpl->pContainer && !bUpdate ) + { + SAL_INFO( "svtools.misc", "getting stream from container" ); + // try to get graphic stream from container storage + xStream = mpImpl->pContainer->GetGraphicStream(mpImpl->mxObj, &mpImpl->aMediaType); + if ( xStream.is() ) + { + const sal_Int32 nConstBufferSize = 32000; + std::unique_ptr<SvMemoryStream> pStream(new SvMemoryStream( 32000, 32000 )); + try + { + sal_Int32 nRead=0; + uno::Sequence < sal_Int8 > aSequence ( nConstBufferSize ); + do + { + nRead = xStream->readBytes ( aSequence, nConstBufferSize ); + pStream->WriteBytes(aSequence.getConstArray(), nRead); + } + while ( nRead == nConstBufferSize ); + pStream->Seek(0); + pStream->MakeReadOnly(); + return pStream; + } + catch (const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("svtools.misc", "discarding broken embedded object preview"); + xStream.clear(); + } + } + } + + if ( !xStream.is() ) + { + SAL_INFO( "svtools.misc", "getting stream from object" ); + bool bUpdateAllowed(true); + const comphelper::EmbeddedObjectContainer* pContainer = GetContainer(); + + if(pContainer) + { + uno::Reference<embed::XLinkageSupport> const xLinkage( + mpImpl->mxObj, uno::UNO_QUERY); + if (xLinkage.is() && xLinkage->isLink()) + { + bUpdateAllowed = pContainer->getUserAllowsLinkUpdate(); + + } + } + + if (bUpdateAllowed) + { + // update wanted or no stream in container storage available + xStream = GetGraphicReplacementStream(mpImpl->nViewAspect, mpImpl->mxObj, &mpImpl->aMediaType); + + if(xStream.is()) + { + if (mpImpl->pContainer) + { + bool bInsertGraphicStream = true; + uno::Reference<io::XSeekable> xSeekable(xStream, uno::UNO_QUERY); + std::optional<sal_Int64> oPosition; + if (xSeekable.is()) + { + oPosition = xSeekable->getPosition(); + } + if (bUpdate) + { + std::unique_ptr<SvStream> pResult = utl::UcbStreamHelper::CreateStream(xStream); + if (pResult) + { + GraphicFilter& rGF = GraphicFilter::GetGraphicFilter(); + Graphic aGraphic; + rGF.ImportGraphic(aGraphic, u"", *pResult); + if (aGraphic.IsNone()) + { + // The graphic is not something we can understand, don't overwrite a + // potentially working previous graphic. + SAL_WARN("svtools.misc", "EmbeddedObjectRef::GetGraphicStream: failed to parse xStream"); + bInsertGraphicStream = false; + } + } + } + if (xSeekable.is() && oPosition.has_value()) + { + xSeekable->seek(*oPosition); + } + if (bInsertGraphicStream) + { + mpImpl->pContainer->InsertGraphicStream(xStream,mpImpl->aPersistName,mpImpl->aMediaType); + } + } + + std::unique_ptr<SvStream> pResult = ::utl::UcbStreamHelper::CreateStream( xStream ); + if (pResult && bUpdate) + mpImpl->bNeedUpdate = false; + + return pResult; + } + } + } + + return nullptr; +} + +void EmbeddedObjectRef::DrawPaintReplacement( const tools::Rectangle &rRect, const OUString &rText, OutputDevice *pOut ) +{ + MapMode aMM( MapUnit::MapAppFont ); + Size aAppFontSz = pOut->LogicToLogic( Size( 0, 8 ), &aMM, nullptr ); + vcl::Font aFnt( "Helvetica", aAppFontSz ); + aFnt.SetTransparent( true ); + aFnt.SetColor( COL_LIGHTRED ); + aFnt.SetWeight( WEIGHT_BOLD ); + aFnt.SetFamily( FAMILY_SWISS ); + + pOut->Push(); + pOut->SetBackground(); + pOut->SetFont( aFnt ); + + Point aPt; + + // Now scale text such that it fits in the rectangle + // We start with the default size and decrease 1-AppFont + for( sal_uInt16 i = 8; i > 2; i-- ) + { + aPt.setX( (rRect.GetWidth() - pOut->GetTextWidth( rText )) / 2 ); + aPt.setY( (rRect.GetHeight() - pOut->GetTextHeight()) / 2 ); + + bool bTiny = false; + if( aPt.X() < 0 ) + { + bTiny = true; + aPt.setX( 0 ); + } + if( aPt.Y() < 0 ) + { + bTiny = true; + aPt.setY( 0 ); + } + if( bTiny ) + { + // decrease for small images + aFnt.SetFontSize( Size( 0, aAppFontSz.Height() * i / 8 ) ); + pOut->SetFont( aFnt ); + } + else + break; + } + + BitmapEx aBmp(BMP_PLUGIN); + tools::Long nHeight = rRect.GetHeight() - pOut->GetTextHeight(); + tools::Long nWidth = rRect.GetWidth(); + if(nHeight > 0 && nWidth > 0 && aBmp.GetSizePixel().Width() > 0) + { + aPt.setY( nHeight ); + Point aP = rRect.TopLeft(); + Size aBmpSize = aBmp.GetSizePixel(); + // fit bitmap in + if( nHeight * 10 / nWidth + > aBmpSize.Height() * 10 / aBmpSize.Width() ) + { + // adjust to the width + // keep proportions + tools::Long nH = nWidth * aBmpSize.Height() / aBmpSize.Width(); + // center + aP.AdjustY((nHeight - nH) / 2 ); + nHeight = nH; + } + else + { + // adjust to the height + // keep proportions + tools::Long nW = nHeight * aBmpSize.Width() / aBmpSize.Height(); + // center + aP.AdjustX((nWidth - nW) / 2 ); + nWidth = nW; + } + + pOut->DrawBitmapEx(aP, Size( nWidth, nHeight ), aBmp); + } + + pOut->IntersectClipRegion( rRect ); + aPt += rRect.TopLeft(); + pOut->DrawText( aPt, rText ); + pOut->Pop(); +} + +void EmbeddedObjectRef::DrawShading( const tools::Rectangle &rRect, OutputDevice *pOut ) +{ + GDIMetaFile * pMtf = pOut->GetConnectMetaFile(); + if( pMtf && pMtf->IsRecord() ) + return; + + pOut->Push(); + pOut->SetLineColor( COL_BLACK ); + + Size aPixSize = pOut->LogicToPixel( rRect.GetSize() ); + aPixSize.AdjustWidth( -1 ); + aPixSize.AdjustHeight( -1 ); + Point aPixViewPos = pOut->LogicToPixel( rRect.TopLeft() ); + sal_Int32 nMax = aPixSize.Width() + aPixSize.Height(); + for( sal_Int32 i = 5; i < nMax; i += 5 ) + { + Point a1( aPixViewPos ), a2( aPixViewPos ); + if( i > aPixSize.Width() ) + a1 += Point( aPixSize.Width(), i - aPixSize.Width() ); + else + a1 += Point( i, 0 ); + if( i > aPixSize.Height() ) + a2 += Point( i - aPixSize.Height(), aPixSize.Height() ); + else + a2 += Point( 0, i ); + + pOut->DrawLine( pOut->PixelToLogic( a1 ), pOut->PixelToLogic( a2 ) ); + } + + pOut->Pop(); + +} + +bool EmbeddedObjectRef::TryRunningState( const uno::Reference < embed::XEmbeddedObject >& xEmbObj ) +{ + if ( !xEmbObj.is() ) + return false; + + try + { + if ( xEmbObj->getCurrentState() == embed::EmbedStates::LOADED ) + xEmbObj->changeState( embed::EmbedStates::RUNNING ); + } + catch (const uno::Exception&) + { + return false; + } + + return true; +} + +void EmbeddedObjectRef::SetGraphicToContainer( const Graphic& rGraphic, + comphelper::EmbeddedObjectContainer& aContainer, + const OUString& aName, + const OUString& aMediaType ) +{ + SvMemoryStream aStream; + aStream.SetVersion( SOFFICE_FILEFORMAT_CURRENT ); + + auto pGfxLink = rGraphic.GetSharedGfxLink(); + if (pGfxLink && pGfxLink->IsNative()) + { + if (pGfxLink->ExportNative(aStream)) + { + aStream.Seek(0); + uno::Reference <io::XInputStream> xStream = new ::utl::OSeekableInputStreamWrapper(aStream); + aContainer.InsertGraphicStream(xStream, aName, aMediaType); + } + else + OSL_FAIL("Export of graphic is failed!"); + } + else + { + TypeSerializer aSerializer(aStream); + aSerializer.writeGraphic(rGraphic); + if (aStream.GetError() == ERRCODE_NONE) + { + aStream.Seek(0); + uno::Reference <io::XInputStream> xStream = new ::utl::OSeekableInputStreamWrapper(aStream); + aContainer.InsertGraphicStream(xStream, aName, aMediaType); + } + else + OSL_FAIL("Export of graphic is failed!"); + } +} + +uno::Reference< io::XInputStream > EmbeddedObjectRef::GetGraphicReplacementStream( + sal_Int64 nViewAspect, + const uno::Reference< embed::XEmbeddedObject >& xObj, + OUString* pMediaType ) + noexcept +{ + return ::comphelper::EmbeddedObjectContainer::GetGraphicReplacementStream(nViewAspect,xObj,pMediaType); +} + +bool EmbeddedObjectRef::IsChart(const css::uno::Reference < css::embed::XEmbeddedObject >& xObj) +{ + SvGlobalName aObjClsId(xObj->getClassID()); + return SvGlobalName(SO3_SCH_CLASSID_30) == aObjClsId + || SvGlobalName(SO3_SCH_CLASSID_40) == aObjClsId + || SvGlobalName(SO3_SCH_CLASSID_50) == aObjClsId + || SvGlobalName(SO3_SCH_CLASSID_60) == aObjClsId; +} + +void EmbeddedObjectRef::UpdateReplacement( bool bUpdateOle ) +{ + if (mpImpl->bUpdating) + { + SAL_WARN("svtools.misc", "UpdateReplacement called while UpdateReplacement already underway"); + return; + } + mpImpl->bUpdating = true; + UpdateOleObject( bUpdateOle ); + GetReplacement(true); + UpdateOleObject( false ); + mpImpl->bUpdating = false; +} + +void EmbeddedObjectRef::UpdateOleObject( bool bUpdateOle ) +{ + embed::EmbeddedUpdate* pObj = dynamic_cast<embed::EmbeddedUpdate*> (GetObject().get()); + if( pObj ) + pObj->SetOleState( bUpdateOle ); +} + + +void EmbeddedObjectRef::UpdateReplacementOnDemand() +{ + mpImpl->oGraphic.reset(); + mpImpl->bNeedUpdate = true; + mpImpl->mnGraphicVersion++; + + if( mpImpl->pContainer ) + { + //remove graphic from container thus a new up to date one is requested on save + mpImpl->pContainer->RemoveGraphicStream( mpImpl->aPersistName ); + } +} + +bool EmbeddedObjectRef::IsChart() const +{ + //todo maybe for 3.0: + //if the changes work good for chart + //we should apply them for all own ole objects + + //#i83708# #i81857# #i79578# request an ole replacement image only if really necessary + //as this call can be very expensive and does block the user interface as long at it takes + + if (!mpImpl->mxObj.is()) + return false; + + return EmbeddedObjectRef::IsChart(mpImpl->mxObj); +} + +// MT: Only used for getting accessible attributes, which are not localized +OUString EmbeddedObjectRef::GetChartType() +{ + OUString Style; + if ( mpImpl->mxObj.is() ) + { + if ( IsChart() ) + { + if ( svt::EmbeddedObjectRef::TryRunningState( mpImpl->mxObj ) ) + { + uno::Reference< chart2::XChartDocument > xChart( mpImpl->mxObj->getComponent(), uno::UNO_QUERY ); + if (xChart.is()) + { + uno::Reference< chart2::XDiagram > xDiagram( xChart->getFirstDiagram()); + if( ! xDiagram.is()) + return OUString(); + uno::Reference< chart2::XCoordinateSystemContainer > xCooSysCnt( xDiagram, uno::UNO_QUERY_THROW ); + const uno::Sequence< uno::Reference< chart2::XCoordinateSystem > > aCooSysSeq( xCooSysCnt->getCoordinateSystems()); + // IA2 CWS. Unused: int nCoordinateCount = aCooSysSeq.getLength(); + bool bGetChartType = false; + for( const auto& rCooSys : aCooSysSeq ) + { + uno::Reference< chart2::XChartTypeContainer > xCTCnt( rCooSys, uno::UNO_QUERY_THROW ); + const uno::Sequence< uno::Reference< chart2::XChartType > > aChartTypes( xCTCnt->getChartTypes()); + int nDimesionCount = rCooSys->getDimension(); + if( nDimesionCount == 3 ) + Style += "3D "; + else + Style += "2D "; + for( const auto& rChartType : aChartTypes ) + { + OUString strChartType = rChartType->getChartType(); + if (strChartType == "com.sun.star.chart2.AreaChartType") + { + Style += "Areas"; + bGetChartType = true; + } + else if (strChartType == "com.sun.star.chart2.BarChartType") + { + Style += "Bars"; + bGetChartType = true; + } + else if (strChartType == "com.sun.star.chart2.ColumnChartType") + { + uno::Reference< beans::XPropertySet > xProp( rCooSys, uno::UNO_QUERY ); + if( xProp.is()) + { + bool bCurrent = false; + if( xProp->getPropertyValue( "SwapXAndYAxis" ) >>= bCurrent ) + { + if (bCurrent) + Style += "Bars"; + else + Style += "Columns"; + bGetChartType = true; + } + } + } + else if (strChartType == "com.sun.star.chart2.LineChartType") + { + Style += "Lines"; + bGetChartType = true; + } + else if (strChartType == "com.sun.star.chart2.ScatterChartType") + { + Style += "XY Chart"; + bGetChartType = true; + } + else if (strChartType == "com.sun.star.chart2.PieChartType") + { + Style += "Pies"; + bGetChartType = true; + } + else if (strChartType == "com.sun.star.chart2.NetChartType") + { + Style += "Radar"; + bGetChartType = true; + } + else if (strChartType == "com.sun.star.chart2.CandleStickChartType") + { + Style += "Candle Stick Chart"; + bGetChartType = true; + } + if (bGetChartType) + return Style; + } + } + } + } + } + } + return Style; +} + +// #i104867# +sal_uInt32 EmbeddedObjectRef::getGraphicVersion() const +{ + return mpImpl->mnGraphicVersion; +} + +void EmbeddedObjectRef::SetDefaultSizeForChart( const Size& rSizeIn_100TH_MM ) +{ + //#i103460# charts do not necessarily have an own size within ODF files, + //for this case they need to use the size settings from the surrounding frame, + //which is made available with this method + + mpImpl->aDefaultSizeForChart_In_100TH_MM = awt::Size( rSizeIn_100TH_MM.getWidth(), rSizeIn_100TH_MM.getHeight() ); + + uno::Reference<chart2::XDefaultSizeTransmitter> xSizeTransmitter(mpImpl->mxObj, uno::UNO_QUERY); + DBG_ASSERT( xSizeTransmitter.is(), "Object does not support XDefaultSizeTransmitter -> will cause #i103460#!" ); + if( xSizeTransmitter.is() ) + xSizeTransmitter->setDefaultSize( mpImpl->aDefaultSizeForChart_In_100TH_MM ); +} + +void EmbeddedObjectRef::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("EmbeddedObjectRef")); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + + mpImpl->dumpAsXml(pWriter); + + (void)xmlTextWriterEndElement(pWriter); +} + +} // namespace svt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/embedtransfer.cxx b/svtools/source/misc/embedtransfer.cxx new file mode 100644 index 0000000000..2d453425b4 --- /dev/null +++ b/svtools/source/misc/embedtransfer.cxx @@ -0,0 +1,250 @@ +/* -*- 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/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XEmbedPersist.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <osl/diagnose.h> +#include <sot/exchange.hxx> +#include <svtools/embedtransfer.hxx> +#include <tools/mapunit.hxx> +#include <vcl/outdev.hxx> +#include <vcl/filter/SvmWriter.hxx> +#include <vcl/gdimtf.hxx> +#include <comphelper/fileformat.h> +#include <comphelper/propertysequence.hxx> +#include <comphelper/storagehelper.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <unotools/streamwrap.hxx> +#include <unotools/tempfile.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <svtools/embedhlp.hxx> + +using namespace ::com::sun::star; + +SvEmbedTransferHelper::SvEmbedTransferHelper( const uno::Reference< embed::XEmbeddedObject >& xObj, + const Graphic* pGraphic, + sal_Int64 nAspect ) +: m_xObj( xObj ) +, m_nAspect( nAspect ) +{ + if (pGraphic) + m_oGraphic.emplace(*pGraphic); + if( xObj.is() ) + { + TransferableObjectDescriptor aObjDesc; + + FillTransferableObjectDescriptor( aObjDesc, m_xObj, nullptr, m_nAspect ); + PrepareOLE( aObjDesc ); + } +} + + +SvEmbedTransferHelper::~SvEmbedTransferHelper() +{ +} + +void SvEmbedTransferHelper::SetParentShellID( const OUString& rShellID ) +{ + maParentShellID = rShellID; +} + + +void SvEmbedTransferHelper::AddSupportedFormats() +{ + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + AddFormat( SotClipboardFormatId::BITMAP ); +} + + +bool SvEmbedTransferHelper::GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) +{ + bool bRet = false; + + if( m_xObj.is() ) + { + try + { + SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor ); + if( HasFormat( nFormat ) ) + { + if( nFormat == SotClipboardFormatId::OBJECTDESCRIPTOR ) + { + TransferableObjectDescriptor aDesc; + FillTransferableObjectDescriptor( aDesc, m_xObj, m_oGraphic ? &*m_oGraphic : nullptr, m_nAspect ); + bRet = SetTransferableObjectDescriptor( aDesc ); + } + else if( nFormat == SotClipboardFormatId::EMBED_SOURCE ) + { + try + { + // TODO/LATER: Probably the graphic should be copied here as well + // currently it is handled by the applications + utl::TempFileFast aTmp; + uno::Reference < embed::XEmbedPersist > xPers( m_xObj, uno::UNO_QUERY ); + if ( xPers.is() ) + { + uno::Reference < embed::XStorage > xStg = comphelper::OStorageHelper::GetTemporaryStorage(); + OUString aName( "Dummy" ); + SvStream* pStream = nullptr; + bool bDeleteStream = false; + uno::Sequence < beans::PropertyValue > aEmpty; + uno::Sequence<beans::PropertyValue> aObjArgs( comphelper::InitPropertySequence({ + { "SourceShellID", uno::Any(maParentShellID) }, + { "DestinationShellID", uno::Any(rDestDoc) } + })); + xPers->storeToEntry(xStg, aName, aEmpty, aObjArgs); + if ( xStg->isStreamElement( aName ) ) + { + uno::Reference < io::XStream > xStm = xStg->cloneStreamElement( aName ); + pStream = utl::UcbStreamHelper::CreateStream( xStm ).release(); + bDeleteStream = true; + } + else + { + pStream = aTmp.GetStream( StreamMode::STD_READWRITE ); + uno::Reference < embed::XStorage > xStor = comphelper::OStorageHelper::GetStorageFromStream( new utl::OStreamWrapper( *pStream ) ); + xStg->openStorageElement( aName, embed::ElementModes::READ )->copyToStorage( xStor ); + } + + const sal_uInt32 nLen = pStream->TellEnd(); + css::uno::Sequence< sal_Int8 > aSeq( nLen ); + + pStream->Seek( STREAM_SEEK_TO_BEGIN ); + pStream->ReadBytes(aSeq.getArray(), nLen); + if ( bDeleteStream ) + delete pStream; + + bRet = aSeq.hasElements(); + if( bRet ) + { + SetAny( uno::Any(aSeq) ); + } + } + else + { + //TODO/LATER: how to handle objects without persistence?! + } + } + catch ( uno::Exception& ) + { + } + } + else if ( nFormat == SotClipboardFormatId::GDIMETAFILE && m_oGraphic ) + { + SvMemoryStream aMemStm( 65535, 65535 ); + aMemStm.SetVersion( SOFFICE_FILEFORMAT_CURRENT ); + + const GDIMetaFile& aMetaFile = m_oGraphic->GetGDIMetaFile(); + SvmWriter aWriter( aMemStm ); + aWriter.Write( aMetaFile ); + uno::Any aAny; + aAny <<= uno::Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), + aMemStm.TellEnd() ); + SetAny( aAny ); + bRet = true; + } + else if ( ( nFormat == SotClipboardFormatId::BITMAP || nFormat == SotClipboardFormatId::PNG ) && m_oGraphic ) + { + bRet = SetBitmapEx( m_oGraphic->GetBitmapEx(), rFlavor ); + } + else if ( m_xObj.is() && ::svt::EmbeddedObjectRef::TryRunningState( m_xObj ) ) + { + uno::Reference< datatransfer::XTransferable > xTransferable( m_xObj->getComponent(), uno::UNO_QUERY ); + if ( xTransferable.is() ) + { + uno::Any aAny = xTransferable->getTransferData( rFlavor ); + SetAny( aAny ); + bRet = true; + } + } + } + } + catch( uno::Exception& ) + { + // Error handling? + } + } + + return bRet; +} + + +void SvEmbedTransferHelper::ObjectReleased() +{ + m_xObj.clear(); +} + +void SvEmbedTransferHelper::FillTransferableObjectDescriptor( TransferableObjectDescriptor& rDesc, + const css::uno::Reference< css::embed::XEmbeddedObject >& xObj, + const Graphic* pGraphic, + sal_Int64 nAspect ) +{ + //TODO/LATER: need TypeName to fill it into the Descriptor (will be shown in listbox) + css::datatransfer::DataFlavor aFlavor; + SotExchange::GetFormatDataFlavor( SotClipboardFormatId::OBJECTDESCRIPTOR, aFlavor ); + + rDesc.maClassName = SvGlobalName( xObj->getClassID() ); + rDesc.maTypeName = aFlavor.HumanPresentableName; + + //TODO/LATER: the aspect size in the descriptor is wrong, unfortunately the stream + // representation of the descriptor allows only 4 bytes for the aspect + // so for internal transport something different should be found + rDesc.mnViewAspect = sal::static_int_cast<sal_uInt16>( nAspect ); + + Size aSize; + MapMode aMapMode( MapUnit::Map100thMM ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + { + if ( pGraphic ) + { + aMapMode = pGraphic->GetPrefMapMode(); + aSize = pGraphic->GetPrefSize(); + } + else + aSize = Size( 2500, 2500 ); + } + else + { + try + { + awt::Size aSz = xObj->getVisualAreaSize( rDesc.mnViewAspect ); + aSize = Size( aSz.Width, aSz.Height ); + } + catch( embed::NoVisualAreaSizeException& ) + { + OSL_FAIL( "Can not get visual area size!" ); + aSize = Size( 5000, 5000 ); + } + + // TODO/LEAN: getMapUnit can switch object to running state + aMapMode = MapMode( VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( rDesc.mnViewAspect ) ) ); + } + + rDesc.maSize = OutputDevice::LogicToLogic( aSize, aMapMode, MapMode( MapUnit::Map100thMM ) ); + rDesc.maDragStartPos = Point(); + rDesc.maDisplayName.clear(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/filechangedchecker.cxx b/svtools/source/misc/filechangedchecker.cxx new file mode 100644 index 0000000000..7ffc5cae83 --- /dev/null +++ b/svtools/source/misc/filechangedchecker.cxx @@ -0,0 +1,115 @@ +/* -*- 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 <sal/config.h> +#include <sal/log.hxx> +#include <osl/file.hxx> + +#include <svtools/filechangedchecker.hxx> +#include <utility> +#include <vcl/timer.hxx> + +FileChangedChecker::FileChangedChecker(OUString aFilename, + ::std::function<void ()> aCallback) + : mTimer("SVTools FileChangedChecker Timer") + , mFileName(std::move(aFilename)) + , mLastModTime() + , mpCallback(std::move(aCallback)) +{ + // Get the current last file modified Status + getCurrentModTime(mLastModTime); + + // associate the callback function for the Timer + mTimer.SetInvokeHandler(LINK(this, FileChangedChecker, TimerHandler)); + + // set timer interval + mTimer.SetTimeout(100); + + // start the timer + resetTimer(); +} + +FileChangedChecker::FileChangedChecker(OUString aFilename) + : mTimer("") + , mFileName(std::move(aFilename)) + , mLastModTime() + , mpCallback(nullptr) +{ + // Get the current last file modified Status + getCurrentModTime(mLastModTime); +} + +void FileChangedChecker::resetTimer() +{ + if (mpCallback == nullptr) + return; + + // Start the Idle if it's not active + if (!mTimer.IsActive()) + mTimer.Start(); + + // Set lowest Priority + mTimer.SetPriority(TaskPriority::LOWEST); +} + +bool FileChangedChecker::getCurrentModTime(TimeValue& o_rValue) const +{ + // Need a Directory item to fetch file status + osl::DirectoryItem aItem; + if (osl::FileBase::E_None != osl::DirectoryItem::get(mFileName, aItem)) + return false; + + // Retrieve the status - we are only interested in last File + // Modified time + osl::FileStatus aStatus( osl_FileStatus_Mask_ModifyTime ); + if (osl::FileBase::E_None != aItem.getFileStatus(aStatus)) + return false; + + o_rValue = aStatus.getModifyTime(); + return true; +} + +bool FileChangedChecker::hasFileChanged(bool bUpdate) +{ + // Get the current file Status + TimeValue newTime={0,0}; + if( !getCurrentModTime(newTime) ) + return true; // well. hard to answer correctly here ... + + // Check if the seconds time stamp has any difference + // If so, then our file has changed meanwhile + if( newTime.Seconds != mLastModTime.Seconds || + newTime.Nanosec != mLastModTime.Nanosec ) + { + // Since the file has changed, set the new status as the file status and + // return True + if(bUpdate) + mLastModTime = newTime ; + + return true; + } + else + return false; +} + +IMPL_LINK_NOARG(FileChangedChecker, TimerHandler, Timer *, void) +{ + // If the file has changed, then update the graphic in the doc + SAL_INFO("svtools", "Timeout Called"); + if(hasFileChanged()) + { + SAL_INFO("svtools", "File modified"); + mpCallback(); + } + + // Reset the Timer in any case + resetTimer(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/imagemgr.cxx b/svtools/source/misc/imagemgr.cxx new file mode 100644 index 0000000000..4c0d31ef3e --- /dev/null +++ b/svtools/source/misc/imagemgr.cxx @@ -0,0 +1,892 @@ +/* -*- 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 <svtools/imagemgr.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <tools/urlobj.hxx> +#include <tools/debug.hxx> +#include <vcl/image.hxx> +#include <sot/storage.hxx> +#include <comphelper/classids.hxx> +#include <unotools/ucbhelper.hxx> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/document/XTypeDetection.hpp> +#include <com/sun/star/ucb/CommandAbortedException.hpp> +#include <com/sun/star/ucb/ContentCreationException.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <ucbhelper/content.hxx> +#include <svtools/strings.hrc> +#include <svtools/svtresid.hxx> +#include <o3tl/string_view.hxx> +#include <bitmaps.hlst> +#include <strings.hxx> + +// globals ******************************************************************* + +#define NO_INDEX (-1) +#define CONTENT_HELPER ::utl::UCBContentHelper + +namespace { + +struct SvtExtensionResIdMapping_Impl +{ + const char* _pExt; + bool _bExt; + TranslateId pStrId; + SvImageId _nImgId; +}; + +} + +SvtExtensionResIdMapping_Impl const ExtensionMap_Impl[] = +{ + { "awk", true, STR_DESCRIPTION_SOURCEFILE, SvImageId::NONE }, + { "bas", true, STR_DESCRIPTION_SOURCEFILE, SvImageId::NONE }, + { "bat", true, STR_DESCRIPTION_BATCHFILE, SvImageId::NONE }, + { "bmk", false, STR_DESCRIPTION_BOOKMARKFILE, SvImageId::NONE }, + { "bmp", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::Bitmap }, + { "c", true, STR_DESCRIPTION_SOURCEFILE, SvImageId::NONE }, + { "cfg", false, STR_DESCRIPTION_CFGFILE, SvImageId::NONE }, + { "cmd", true, STR_DESCRIPTION_BATCHFILE, SvImageId::NONE }, + { "cob", true, STR_DESCRIPTION_SOURCEFILE, SvImageId::NONE }, + { "com", true, STR_DESCRIPTION_APPLICATION, SvImageId::NONE }, + { "cxx", true, STR_DESCRIPTION_SOURCEFILE, SvImageId::NONE }, + { "dbf", true, STR_DESCRIPTION_DATABASE_TABLE, SvImageId::Table }, + { "def", true, STR_DESCRIPTION_SOURCEFILE, SvImageId::NONE }, + { "dll", true, STR_DESCRIPTION_SYSFILE, SvImageId::NONE }, + { "doc", false, STR_DESCRIPTION_WORD_DOC, SvImageId::Writer }, + { "dot", false, STR_DESCRIPTION_WORD_DOC, SvImageId::WriterTemplate }, + { "docx", false, STR_DESCRIPTION_WORD_DOC, SvImageId::Writer }, + { "dxf", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::DXF }, + { "exe", true, STR_DESCRIPTION_APPLICATION, SvImageId::NONE }, + { "gif", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::GIF }, + { "h", true, STR_DESCRIPTION_SOURCEFILE, SvImageId::NONE }, + { "hlp", false, STR_DESCRIPTION_HELP_DOC, SvImageId::NONE }, + { "hrc", true, STR_DESCRIPTION_SOURCEFILE, SvImageId::NONE }, + { "htm", false, STR_DESCRIPTION_HTMLFILE, SvImageId::HTML }, + { "html", false, STR_DESCRIPTION_HTMLFILE, SvImageId::HTML }, + { "asp", false, STR_DESCRIPTION_HTMLFILE, SvImageId::HTML }, + { "hxx", true, STR_DESCRIPTION_SOURCEFILE, SvImageId::NONE }, + { "ini", false, STR_DESCRIPTION_CFGFILE, SvImageId::NONE }, + { "java", true, STR_DESCRIPTION_SOURCEFILE, SvImageId::NONE }, + { "jpeg", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::JPG }, + { "jpg", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::JPG }, + { "lha", true, STR_DESCRIPTION_ARCHIVFILE, SvImageId::NONE }, +#ifdef _WIN32 + { "lnk", false, {}, SvImageId::NONE }, +#endif + { "log", true, STR_DESCRIPTION_LOGFILE, SvImageId::NONE }, + { "lst", true, STR_DESCRIPTION_LOGFILE, SvImageId::NONE }, + { "met", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::MET }, + { "mml", false, STR_DESCRIPTION_MATHML_DOC, SvImageId::Math }, + { "mod", true, STR_DESCRIPTION_SOURCEFILE, SvImageId::NONE }, + { "odb", false, STR_DESCRIPTION_OO_DATABASE_DOC, SvImageId::OO_DatabaseDoc }, + { "odg", false, STR_DESCRIPTION_OO_DRAW_DOC, SvImageId::OO_DrawDoc }, + { "fodg", false, STR_DESCRIPTION_OO_DRAW_DOC, SvImageId::OO_DrawDoc }, + { "odf", false, STR_DESCRIPTION_OO_MATH_DOC, SvImageId::OO_MathDoc }, + { "odm", false, STR_DESCRIPTION_OO_GLOBAL_DOC, SvImageId::OO_GlobalDoc }, + { "odp", false, STR_DESCRIPTION_OO_IMPRESS_DOC, SvImageId::OO_ImpressDoc }, + { "fodp", false, STR_DESCRIPTION_OO_IMPRESS_DOC, SvImageId::OO_ImpressDoc }, + { "ods", false, STR_DESCRIPTION_OO_CALC_DOC, SvImageId::OO_CalcDoc }, + { "fods", false, STR_DESCRIPTION_OO_CALC_DOC, SvImageId::OO_CalcDoc }, + { "odt", false, STR_DESCRIPTION_OO_WRITER_DOC, SvImageId::OO_WriterDoc }, + { "fodt", false, STR_DESCRIPTION_OO_WRITER_DOC, SvImageId::OO_WriterDoc }, + { "otg", false, STR_DESCRIPTION_OO_DRAW_TEMPLATE, SvImageId::OO_DrawTemplate }, + { "otp", false, STR_DESCRIPTION_OO_IMPRESS_TEMPLATE, SvImageId::OO_ImpressTemplate }, + { "ots", false, STR_DESCRIPTION_OO_CALC_TEMPLATE, SvImageId::OO_CalcTemplate }, + { "ott", false, STR_DESCRIPTION_OO_WRITER_TEMPLATE, SvImageId::OO_WriterTemplate }, + { "pas", true, STR_DESCRIPTION_SOURCEFILE, SvImageId::NONE }, + { "pcd", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::PCD }, + { "pct", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::PCT }, + { "pict", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::PCT }, + { "pcx", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::PCX }, + { "pl", true, STR_DESCRIPTION_SOURCEFILE, SvImageId::NONE }, + { "png", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::PNG }, + { "rar", true, STR_DESCRIPTION_ARCHIVFILE, SvImageId::NONE }, + { "rtf", false, STR_DESCRIPTION_WORD_DOC, SvImageId::Writer }, + { "sbl", false, {}, SvImageId::NONE }, + { "sch", false, {}, SvImageId::NONE }, + { "sda", false, { nullptr, STR_DESCRIPTION_SDRAW_DOC}, SvImageId::Draw }, + { "sdb", false, STR_DESCRIPTION_SDATABASE_DOC, SvImageId::Database }, + { "sdc", false, { nullptr, STR_DESCRIPTION_SCALC_DOC}, SvImageId::Calc }, + { "sdd", false, { nullptr, STR_DESCRIPTION_SIMPRESS_DOC}, SvImageId::Impress }, + { "sdp", false, { nullptr, STR_DESCRIPTION_SIMPRESS_DOC}, SvImageId::NONE }, + { "sds", false, { nullptr, STR_DESCRIPTION_SCHART_DOC}, SvImageId::NONE }, + { "sdw", false, { nullptr, STR_DESCRIPTION_SWRITER_DOC}, SvImageId::Writer }, + { "sga", false, {}, SvImageId::NONE }, + { "sgl", false, STR_DESCRIPTION_GLOBALDOC, SvImageId::GlobalDoc }, + { "shtml", false, STR_DESCRIPTION_HTMLFILE, SvImageId::HTML }, + { "sim", false, STR_DESCRIPTION_SIMAGE_DOC, SvImageId::SIM }, + { "smf", false, { nullptr, STR_DESCRIPTION_SMATH_DOC}, SvImageId::Math }, + { "src", true, STR_DESCRIPTION_SOURCEFILE, SvImageId::NONE }, + { "svh", false, STR_DESCRIPTION_HELP_DOC, SvImageId::NONE }, + { "svm", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::SVM }, + { "stc", false, STR_DESCRIPTION_CALC_TEMPLATE, SvImageId::CalcTemplate }, + { "std", false, STR_DESCRIPTION_DRAW_TEMPLATE, SvImageId::DrawTemplate }, + { "sti", false, STR_DESCRIPTION_IMPRESS_TEMPLATE, SvImageId::ImpressTemplate }, + { "stw", false, STR_DESCRIPTION_WRITER_TEMPLATE, SvImageId::WriterTemplate }, + { "sxc", false, STR_DESCRIPTION_SXCALC_DOC, SvImageId::Calc }, + { "sxd", false, STR_DESCRIPTION_SXDRAW_DOC, SvImageId::Draw }, + { "sxg", false, STR_DESCRIPTION_SXGLOBAL_DOC, SvImageId::GlobalDoc }, + { "sxi", false, STR_DESCRIPTION_SXIMPRESS_DOC, SvImageId::Impress }, + { "sxm", false, STR_DESCRIPTION_SXMATH_DOC, SvImageId::Math }, + { "sxs", false, STR_DESCRIPTION_SXCHART_DOC, SvImageId::NONE }, + { "sxw", false, STR_DESCRIPTION_SXWRITER_DOC, SvImageId::Writer }, + { "sys", true, STR_DESCRIPTION_SYSFILE, SvImageId::NONE }, + { "tif", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::TIFF }, + { "tiff", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::TIFF }, + { "txt", false, STR_DESCRIPTION_TEXTFILE, SvImageId::TextFile }, + { "url", false, STR_DESCRIPTION_LINK, SvImageId::NONE }, + { "vor", false, STR_DESCRIPTION_SOFFICE_TEMPLATE_DOC, SvImageId::WriterTemplate }, + { "vxd", true, STR_DESCRIPTION_SYSFILE, SvImageId::NONE }, + { "webp", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::WEBP }, + { "wmf", true, STR_DESCRIPTION_GRAPHIC_DOC, SvImageId::WMF }, + { "xls", false, STR_DESCRIPTION_EXCEL_DOC, SvImageId::Calc }, + { "xlt", false, STR_DESCRIPTION_EXCEL_TEMPLATE_DOC, SvImageId::CalcTemplate }, + { "xlsx", false, STR_DESCRIPTION_EXCEL_DOC, SvImageId::Calc }, + { "uu", true, STR_DESCRIPTION_ARCHIVFILE, SvImageId::NONE }, + { "uue", true, STR_DESCRIPTION_ARCHIVFILE, SvImageId::NONE }, + { "z", true, STR_DESCRIPTION_ARCHIVFILE, SvImageId::NONE }, + { "zip", true, STR_DESCRIPTION_ARCHIVFILE, SvImageId::NONE }, + { "zoo", true, STR_DESCRIPTION_ARCHIVFILE, SvImageId::NONE }, + { "gz", true, STR_DESCRIPTION_ARCHIVFILE, SvImageId::NONE }, + { "ppt", false, STR_DESCRIPTION_POWERPOINT, SvImageId::Impress }, + { "pot", false, STR_DESCRIPTION_POWERPOINT_TEMPLATE, SvImageId::ImpressTemplate }, + { "pps", false, STR_DESCRIPTION_POWERPOINT_SHOW, SvImageId::Impress }, + { "pptx", false, STR_DESCRIPTION_POWERPOINT, SvImageId::Impress }, + { "oxt", false, STR_DESCRIPTION_EXTENSION, SvImageId::Extension }, + { nullptr, false, {}, SvImageId::NONE } +}; + +namespace { + +struct SvtFactory2ExtensionMapping_Impl +{ + const char* _pFactory; + const char* _pExtension; +}; + +} + +// mapping from "private:factory" url to extension + +SvtFactory2ExtensionMapping_Impl const Fac2ExtMap_Impl[] = +{ + { "swriter", "odt" }, + { "swriter/web", "html" }, + { "swriter/GlobalDocument", "odm" }, + { "scalc", "ods" }, + { "simpress", "odp" }, + { "sdraw", "odg" }, + { "smath", "odf" }, + { "sdatabase", "odb" }, + { nullptr, nullptr } +}; + + +static OUString GetImageExtensionByFactory_Impl( const OUString& rURL ) +{ + INetURLObject aObj( rURL ); + OUString aPath = aObj.GetURLPath( INetURLObject::DecodeMechanism::NONE ); + OUString aExtension; + + if ( !aPath.isEmpty() ) + { + sal_uInt16 nIndex = 0; + while ( Fac2ExtMap_Impl[ nIndex ]._pFactory ) + { + if ( aPath.equalsAscii( Fac2ExtMap_Impl[ nIndex ]._pFactory ) ) + { + // extension found + aExtension = OUString::createFromAscii(Fac2ExtMap_Impl[ nIndex ]._pExtension); + // and return it + return aExtension; + } + ++nIndex; + } + } + + // no extension found, so use the type detection (performance brake) + + try + { + // get the TypeDetection service to access all registered types + css::uno::Reference < css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + css::uno::Reference < css::document::XTypeDetection > xTypeDetector( + xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", xContext), + css::uno::UNO_QUERY ); + + OUString aInternalType = xTypeDetector->queryTypeByURL( rURL ); + css::uno::Reference < css::container::XNameAccess > xAccess( xTypeDetector, css::uno::UNO_QUERY ); + css::uno::Sequence < css::beans::PropertyValue > aTypeProps; + if ( !aInternalType.isEmpty() && xAccess->hasByName( aInternalType ) ) + { + xAccess->getByName( aInternalType ) >>= aTypeProps; + for ( const css::beans::PropertyValue& rProp : std::as_const(aTypeProps) ) + { + if (rProp.Name == "Extensions") + { + css::uno::Sequence < OUString > aExtensions; + if ( ( rProp.Value >>= aExtensions ) && aExtensions.hasElements() ) + { + const OUString* pExtensions = aExtensions.getConstArray(); + aExtension = pExtensions[0]; + break; + } + } + } + } + } + catch( const css::uno::RuntimeException& ) + { + throw; // don't hide it! + } + catch( const css::uno::Exception& ) + { + // type detection failed -> no extension + } + + return aExtension; +} + +static sal_Int32 GetIndexOfExtension_Impl( const OUString& rExtension ) +{ + sal_Int32 nRet = NO_INDEX; + if ( !rExtension.isEmpty() ) + { + sal_Int32 nIndex = 0; + OUString aExt = rExtension.toAsciiLowerCase(); + while ( ExtensionMap_Impl[ nIndex ]._pExt ) + { + if ( aExt.equalsAscii( ExtensionMap_Impl[ nIndex ]._pExt ) ) + { + nRet = nIndex; + break; + } + ++nIndex; + } + } + + return nRet; +} + +static SvImageId GetImageId_Impl( const OUString& rExtension ) +{ + SvImageId nImage = SvImageId::File; + sal_Int32 nIndex = GetIndexOfExtension_Impl( rExtension ); + if ( nIndex != NO_INDEX ) + { + nImage = ExtensionMap_Impl[ nIndex ]._nImgId; + if ( nImage == SvImageId::NONE ) + nImage = SvImageId::File; + } + + return nImage; +} + +static bool GetVolumeProperties_Impl( ::ucbhelper::Content& rContent, svtools::VolumeInfo& rVolumeInfo ) +{ + bool bRet = false; + + try + { + bRet = ( ( rContent.getPropertyValue( "IsVolume" ) >>= rVolumeInfo.m_bIsVolume ) && + ( rContent.getPropertyValue( "IsRemote" ) >>= rVolumeInfo.m_bIsRemote ) && + ( rContent.getPropertyValue( "IsRemoveable" ) >>= rVolumeInfo.m_bIsRemoveable ) && + ( rContent.getPropertyValue( "IsFloppy" ) >>= rVolumeInfo.m_bIsFloppy ) && + ( rContent.getPropertyValue( "IsCompactDisc" ) >>= rVolumeInfo.m_bIsCompactDisc ) ); + } + catch( const css::uno::RuntimeException& ) + { + throw; // don't hide it! + } + catch( const css::uno::Exception& ) + { + // type detection failed -> no extension + } + + return bRet; +} + +static SvImageId GetFolderImageId_Impl( const OUString& rURL ) +{ + SvImageId nRet = SvImageId::Folder; + try + { + ::svtools::VolumeInfo aVolumeInfo; + ::ucbhelper::Content aCnt( rURL, css::uno::Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() ); + if ( GetVolumeProperties_Impl( aCnt, aVolumeInfo ) ) + { + if ( aVolumeInfo.m_bIsRemote ) + nRet = SvImageId::NetworkDevice; + else if ( aVolumeInfo.m_bIsCompactDisc ) + nRet = SvImageId::CDRomDevice; + else if ( aVolumeInfo.m_bIsRemoveable ) + nRet = SvImageId::RemoveableDevice; + else if ( aVolumeInfo.m_bIsVolume ) + nRet = SvImageId::FixedDevice; + } + } + catch( const css::uno::RuntimeException& ) + { + throw; // don't hide it! + } + catch( const css::uno::Exception& ) + { + + } + return nRet; +} + +static bool isFolder( + OUString const & url, css::uno::Reference<css::ucb::XCommandEnvironment> const & env) +{ + try { + return ucbhelper::Content(url, env, comphelper::getProcessComponentContext()).isFolder(); + } catch (css::uno::RuntimeException &) { + throw; + } catch (css::ucb::CommandAbortedException &) { + assert(false); // this cannot happen + throw; + } catch (css::uno::Exception &) { + TOOLS_INFO_EXCEPTION("svtools.misc", "isFolder(" << url << ")"); + return false; + } +} + +static SvImageId GetImageId_Impl( + const INetURLObject& rObject, bool bDetectFolder, + css::uno::Reference<css::ucb::XCommandEnvironment> const & env ) +{ + OUString aExt, sURL = rObject.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + SvImageId nImage = SvImageId::File; + + if ( rObject.GetProtocol() == INetProtocol::PrivSoffice ) + { + std::u16string_view aURLPath = sURL.subView( strlen(URL_PREFIX_PRIV_SOFFICE) ); + std::u16string_view aType = o3tl::getToken(aURLPath, 0, '/' ); + if ( aType == u"factory" ) + { + // detect an image id for our "private:factory" urls + aExt = GetImageExtensionByFactory_Impl( sURL ); + if ( !aExt.isEmpty() ) + nImage = GetImageId_Impl( aExt ); + return nImage; + } + else if ( aType == u"image" ) + nImage = static_cast<SvImageId>(o3tl::toInt32(o3tl::getToken(aURLPath, 1, '/' ))); + } + else + { + aExt = rObject.getExtension(); + if ( aExt == "vor" ) + { + SvImageId nId = SvImageId::WriterTemplate; + try + { + tools::SvRef<SotStorage> aStorage = new SotStorage( sURL, StreamMode::STD_READ ); + if ( !aStorage->GetError() ) + { + SvGlobalName aGlobalName = aStorage->GetClassName(); + if ( aGlobalName == SvGlobalName(SO3_SC_CLASSID_50) || aGlobalName == SvGlobalName(SO3_SC_CLASSID_40) || aGlobalName == SvGlobalName(SO3_SC_CLASSID_30) ) + nId = SvImageId::CalcTemplate; + else if ( aGlobalName == SvGlobalName(SO3_SDRAW_CLASSID_50) ) + nId = SvImageId::DrawTemplate; + else if ( aGlobalName == SvGlobalName(SO3_SIMPRESS_CLASSID_50) || + aGlobalName == SvGlobalName(SO3_SIMPRESS_CLASSID_40) || aGlobalName == SvGlobalName(SO3_SIMPRESS_CLASSID_30) ) + nId = SvImageId::ImpressTemplate; + else if ( aGlobalName == SvGlobalName(SO3_SM_CLASSID_50) || aGlobalName == SvGlobalName(SO3_SM_CLASSID_40) || aGlobalName == SvGlobalName(SO3_SM_CLASSID_30) ) + nId = SvImageId::MathTemplate; + } + } + catch (const css::ucb::ContentCreationException&) + { + TOOLS_WARN_EXCEPTION("svtools.misc", "GetImageId_Impl"); + } + + return nId; + } + } + + if ( nImage == SvImageId::File && !sURL.isEmpty() ) + { + if ( bDetectFolder && isFolder( sURL, env ) ) + nImage = GetFolderImageId_Impl( sURL ); + else if ( !aExt.isEmpty() ) + nImage = GetImageId_Impl( aExt ); + } + return nImage; +} + +static TranslateId GetDescriptionId_Impl( const OUString& rExtension, bool& rbShowExt ) +{ + TranslateId pId; + sal_Int32 nIndex = GetIndexOfExtension_Impl( rExtension ); + if ( nIndex != NO_INDEX ) + { + pId = ExtensionMap_Impl[ nIndex ].pStrId; + rbShowExt = ExtensionMap_Impl[ nIndex ]._bExt; + } + + return pId; +} + +static OUString GetDescriptionByFactory_Impl( const OUString& rFactory ) +{ + TranslateId pResId; + if ( rFactory.startsWithIgnoreAsciiCase( "swriter" ) ) + pResId = STR_DESCRIPTION_FACTORY_WRITER; + else if ( rFactory.startsWithIgnoreAsciiCase( "scalc" ) ) + pResId = STR_DESCRIPTION_FACTORY_CALC; + else if ( rFactory.startsWithIgnoreAsciiCase( "simpress" ) ) + pResId = STR_DESCRIPTION_FACTORY_IMPRESS; + else if ( rFactory.startsWithIgnoreAsciiCase( "sdraw" ) ) + pResId = STR_DESCRIPTION_FACTORY_DRAW; + else if ( rFactory.startsWithIgnoreAsciiCase( "swriter/web" ) ) + pResId = STR_DESCRIPTION_FACTORY_WRITERWEB; + else if ( rFactory.startsWithIgnoreAsciiCase( "swriter/globaldocument" ) ) + pResId = STR_DESCRIPTION_FACTORY_GLOBALDOC; + else if ( rFactory.startsWithIgnoreAsciiCase( "smath" ) ) + pResId = STR_DESCRIPTION_FACTORY_MATH; + else if ( rFactory.startsWithIgnoreAsciiCase( "sdatabase" ) ) + pResId = STR_DESCRIPTION_FACTORY_DATABASE; + + if (pResId) + { + return SvtResId(pResId); + } + return OUString(); +} + +static TranslateId GetFolderDescriptionId_Impl( const OUString& rURL ) +{ + TranslateId pRet = STR_DESCRIPTION_FOLDER; + try + { + ::ucbhelper::Content aCnt( rURL, css::uno::Reference< css::ucb::XCommandEnvironment >(), comphelper::getProcessComponentContext() ); + svtools::VolumeInfo aVolumeInfo; + if ( GetVolumeProperties_Impl( aCnt, aVolumeInfo ) ) + { + if ( aVolumeInfo.m_bIsRemote ) + pRet = STR_DESCRIPTION_REMOTE_VOLUME; + else if ( aVolumeInfo.m_bIsFloppy ) + pRet = STR_DESCRIPTION_FLOPPY_VOLUME; + else if ( aVolumeInfo.m_bIsCompactDisc ) + pRet = STR_DESCRIPTION_CDROM_VOLUME; + else if ( aVolumeInfo.m_bIsRemoveable || aVolumeInfo.m_bIsVolume ) + pRet = STR_DESCRIPTION_LOCALE_VOLUME; + } + } + catch( const css::uno::RuntimeException& ) + { + throw; // don't hide it! + } + catch( const css::uno::Exception& ) + { + + } + return pRet; +} + +static OUString GetImageNameFromList_Impl( SvImageId nImageId, vcl::ImageType eImageType ) +{ + if (eImageType == vcl::ImageType::Size32) + { + switch (nImageId) + { + case SvImageId::HTML: + return BMP_HTML_32; + case SvImageId::OO_DatabaseDoc: + return BMP_OO_DATABASE_DOC_32; + case SvImageId::OO_DrawDoc: + return BMP_OO_DRAW_DOC_32; + case SvImageId::OO_MathDoc: + return BMP_OO_MATH_DOC_32; + case SvImageId::OO_GlobalDoc: + return BMP_OO_GLOBAL_DOC_32; + case SvImageId::OO_ImpressDoc: + return BMP_OO_IMPRESS_DOC_32; + case SvImageId::OO_CalcDoc: + return BMP_OO_CALC_DOC_32; + case SvImageId::OO_WriterDoc: + return BMP_OO_WRITER_DOC_32; + case SvImageId::OO_WriterTemplate: + return BMP_OO_WRITER_TEMPLATE_32; + default: break; + } + } + else if (eImageType == vcl::ImageType::Size26) + { + switch (nImageId) + { + case SvImageId::Impress: + return BMP_IMPRESS_LC; + case SvImageId::Bitmap: + return BMP_BITMAP_LC; + case SvImageId::Calc: + return BMP_CALC_LC; + case SvImageId::CalcTemplate: + return BMP_CALCTEMPLATE_LC; + case SvImageId::Database: + return BMP_DATABASE_LC; + case SvImageId::ImpressTemplate: + return BMP_IMPRESSTEMPLATE_LC; + case SvImageId::GIF: + return BMP_GIF_LC; + case SvImageId::HTML: + return BMP_HTML_LC; + case SvImageId::JPG: + return BMP_JPG_LC; + case SvImageId::Math: + return BMP_MATH_LC; + case SvImageId::MathTemplate: + return BMP_MATHTEMPLATE_LC; + case SvImageId::File: + return BMP_FILE_LC; + case SvImageId::PCD: + return BMP_PCD_LC; + case SvImageId::PCT: + return BMP_PCT_LC; + case SvImageId::PCX: + return BMP_PCX_LC; + case SvImageId::SIM: + return BMP_SIM_LC; + case SvImageId::TextFile: + return BMP_TEXTFILE_LC; + case SvImageId::TIFF: + return BMP_TIFF_LC; + case SvImageId::WEBP: + return BMP_WEBP_LC; + case SvImageId::WMF: + return BMP_WMF_LC; + case SvImageId::Writer: + return BMP_WRITER_LC; + case SvImageId::WriterTemplate: + return BMP_WRITERTEMPLATE_LC; + case SvImageId::FixedDevice: + return BMP_FIXEDDEV_LC; + case SvImageId::RemoveableDevice: + return BMP_REMOVABLEDEV_LC; + case SvImageId::CDRomDevice: + return BMP_CDROMDEV_LC; + case SvImageId::NetworkDevice: + return BMP_NETWORKDEV_LC; + case SvImageId::Table: + return BMP_TABLE_LC; + case SvImageId::Folder: + return BMP_FOLDER_LC; + case SvImageId::DXF: + return BMP_DXF_LC; + case SvImageId::MET: + return BMP_MET_LC; + case SvImageId::PNG: + return BMP_PNG_LC; + case SvImageId::SVM: + return BMP_SVM_LC; + case SvImageId::GlobalDoc: + return BMP_GLOBAL_DOC_LC; + case SvImageId::Draw: + return BMP_DRAW_LC; + case SvImageId::DrawTemplate: + return BMP_DRAWTEMPLATE_LC; + case SvImageId::OO_DatabaseDoc: + return BMP_OO_DATABASE_DOC_LC; + case SvImageId::OO_DrawDoc: + return BMP_OO_DRAW_DOC_LC; + case SvImageId::OO_MathDoc: + return BMP_OO_MATH_DOC_LC; + case SvImageId::OO_GlobalDoc: + return BMP_OO_GLOBAL_DOC_LC; + case SvImageId::OO_ImpressDoc: + return BMP_OO_IMPRESS_DOC_LC; + case SvImageId::OO_CalcDoc: + return BMP_OO_CALC_DOC_LC; + case SvImageId::OO_WriterDoc: + return BMP_OO_WRITER_DOC_LC; + case SvImageId::OO_DrawTemplate: + return BMP_OO_DRAW_TEMPLATE_LC; + case SvImageId::OO_ImpressTemplate: + return BMP_OO_IMPRESS_TEMPLATE_LC; + case SvImageId::OO_CalcTemplate: + return BMP_OO_CALC_TEMPLATE_LC; + case SvImageId::OO_WriterTemplate: + return BMP_OO_WRITER_TEMPLATE_LC; + case SvImageId::Extension: + return BMP_EXTENSION_LC; + default: break; + } + } + else + { + switch (nImageId) + { + case SvImageId::Impress: + return BMP_IMPRESS_SC; + case SvImageId::Bitmap: + return BMP_BITMAP_SC; + case SvImageId::Calc: + return BMP_CALC_SC; + case SvImageId::CalcTemplate: + return BMP_CALCTEMPLATE_SC; + case SvImageId::Database: + return BMP_DATABASE_SC; + case SvImageId::ImpressTemplate: + return BMP_IMPRESSTEMPLATE_SC; + case SvImageId::GIF: + return BMP_GIF_SC; + case SvImageId::HTML: + return BMP_HTML_SC; + case SvImageId::JPG: + return BMP_JPG_SC; + case SvImageId::Math: + return BMP_MATH_SC; + case SvImageId::MathTemplate: + return BMP_MATHTEMPLATE_SC; + case SvImageId::File: + return BMP_FILE_SC; + case SvImageId::PCD: + return BMP_PCD_SC; + case SvImageId::PCT: + return BMP_PCT_SC; + case SvImageId::PCX: + return BMP_PCX_SC; + case SvImageId::SIM: + return BMP_SIM_SC; + case SvImageId::TextFile: + return BMP_TEXTFILE_SC; + case SvImageId::TIFF: + return BMP_TIFF_SC; + case SvImageId::WEBP: + return BMP_WEBP_SC; + case SvImageId::WMF: + return BMP_WMF_SC; + case SvImageId::Writer: + return BMP_WRITER_SC; + case SvImageId::WriterTemplate: + return BMP_WRITERTEMPLATE_SC; + case SvImageId::FixedDevice: + return BMP_FIXEDDEV_SC; + case SvImageId::RemoveableDevice: + return BMP_REMOVABLEDEV_SC; + case SvImageId::CDRomDevice: + return BMP_CDROMDEV_SC; + case SvImageId::NetworkDevice: + return BMP_NETWORKDEV_SC; + case SvImageId::Table: + return BMP_TABLE_SC; + case SvImageId::Folder: + return RID_BMP_FOLDER; + case SvImageId::DXF: + return BMP_DXF_SC; + case SvImageId::MET: + return BMP_MET_SC; + case SvImageId::PNG: + return BMP_PNG_SC; + case SvImageId::SVM: + return BMP_SVM_SC; + case SvImageId::GlobalDoc: + return BMP_GLOBAL_DOC_SC; + case SvImageId::Draw: + return BMP_DRAW_SC; + case SvImageId::DrawTemplate: + return BMP_DRAWTEMPLATE_SC; + case SvImageId::OO_DatabaseDoc: + return BMP_OO_DATABASE_DOC_SC; + case SvImageId::OO_DrawDoc: + return BMP_OO_DRAW_DOC_SC; + case SvImageId::OO_MathDoc: + return BMP_OO_MATH_DOC_SC; + case SvImageId::OO_GlobalDoc: + return BMP_OO_GLOBAL_DOC_SC; + case SvImageId::OO_ImpressDoc: + return BMP_OO_IMPRESS_DOC_SC; + case SvImageId::OO_CalcDoc: + return BMP_OO_CALC_DOC_SC; + case SvImageId::OO_WriterDoc: + return BMP_OO_WRITER_DOC_SC; + case SvImageId::OO_DrawTemplate: + return BMP_OO_DRAW_TEMPLATE_SC; + case SvImageId::OO_ImpressTemplate: + return BMP_OO_IMPRESS_TEMPLATE_SC; + case SvImageId::OO_CalcTemplate: + return BMP_OO_CALC_TEMPLATE_SC; + case SvImageId::OO_WriterTemplate: + return BMP_OO_WRITER_TEMPLATE_SC; + case SvImageId::Extension: + return BMP_EXTENSION_SC; + default: break; + } + } + + return OUString(); +} + +static Image GetImageFromList_Impl( SvImageId nImageId, vcl::ImageType eImageType) +{ + OUString sImageName(GetImageNameFromList_Impl(nImageId, eImageType)); + if (!sImageName.isEmpty()) + return Image(StockImage::Yes, sImageName); + return Image(); +} + +OUString SvFileInformationManager::GetDescription_Impl( const INetURLObject& rObject, bool bDetectFolder ) +{ + OUString sExtension(rObject.getExtension()); + OUString sDescription, sURL( rObject.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + TranslateId pResId; + bool bShowExt = false, bOnlyFile = false; + bool bFolder = bDetectFolder && CONTENT_HELPER::IsFolder( sURL ); + if ( !bFolder ) + { + bool bDetected = false; + + if ( rObject.GetProtocol() == INetProtocol::PrivSoffice ) + { + OUString aURLPath = sURL.copy( strlen(URL_PREFIX_PRIV_SOFFICE) ); + std::u16string_view aType = o3tl::getToken(aURLPath, 0, '/' ); + if ( aType == u"factory" ) + { + sDescription = GetDescriptionByFactory_Impl( aURLPath.copy( aURLPath.indexOf( '/' ) + 1 ) ); + bDetected = true; + } + } + + if (!bDetected) + { + // search a description by extension + bool bExt = !sExtension.isEmpty(); + if ( bExt ) + { + sExtension = sExtension.toAsciiLowerCase(); + pResId = GetDescriptionId_Impl( sExtension, bShowExt ); + } + if (!pResId) + { + pResId = STR_DESCRIPTION_FILE; + bOnlyFile = bExt; + } + } + } + else + pResId = GetFolderDescriptionId_Impl( sURL ); + + if (pResId) + { + if ( bOnlyFile ) + { + bShowExt = false; + sExtension = sExtension.toAsciiUpperCase(); + sDescription = sExtension + "-"; + } + sDescription += SvtResId(pResId); + } + + DBG_ASSERT( !sDescription.isEmpty(), "file without description" ); + + if ( bShowExt ) + { + sDescription += " (" + sExtension + ")"; + } + + return sDescription; +} + +OUString SvFileInformationManager::GetImageId(const INetURLObject& rObject, bool bBig) +{ + SvImageId nImage = GetImageId_Impl( + rObject, true, utl::UCBContentHelper::getDefaultCommandEnvironment() ); + DBG_ASSERT( nImage != SvImageId::NONE, "invalid ImageId" ); + return GetImageNameFromList_Impl(nImage, bBig ? vcl::ImageType::Size26 : vcl::ImageType::Size16); +} + +Image SvFileInformationManager::GetImage( + const INetURLObject& rObject, bool bBig, + css::uno::Reference<css::ucb::XCommandEnvironment> const & env) +{ + SvImageId nImage = GetImageId_Impl( rObject, true, env ); + DBG_ASSERT( nImage != SvImageId::NONE, "invalid ImageId" ); + return GetImageFromList_Impl(nImage, bBig ? vcl::ImageType::Size26 : vcl::ImageType::Size16); +} + +OUString SvFileInformationManager::GetFileImageId(const INetURLObject& rObject) +{ + SvImageId nImage = GetImageId_Impl( + rObject, false, utl::UCBContentHelper::getDefaultCommandEnvironment() ); + DBG_ASSERT( nImage != SvImageId::NONE, "invalid ImageId" ); + return GetImageNameFromList_Impl(nImage, vcl::ImageType::Size16); +} + +Image SvFileInformationManager::GetImageNoDefault(const INetURLObject& rObject, vcl::ImageType eImageType) +{ + SvImageId nImage = GetImageId_Impl( + rObject, true, utl::UCBContentHelper::getDefaultCommandEnvironment()); + DBG_ASSERT( nImage != SvImageId::NONE, "invalid ImageId" ); + + if ( nImage == SvImageId::File ) + return Image(); + + return GetImageFromList_Impl(nImage, eImageType); +} + +OUString SvFileInformationManager::GetFolderImageId( const svtools::VolumeInfo& rInfo ) +{ + SvImageId nImage = SvImageId::Folder; + DBG_ASSERT( nImage != SvImageId::NONE, "invalid ImageId" ); + + if ( rInfo.m_bIsRemote ) + nImage = SvImageId::NetworkDevice; + else if ( rInfo.m_bIsCompactDisc ) + nImage = SvImageId::CDRomDevice; + else if ( rInfo.m_bIsRemoveable || rInfo.m_bIsFloppy ) + nImage = SvImageId::RemoveableDevice; + else if ( rInfo.m_bIsVolume ) + nImage = SvImageId::FixedDevice; + + return GetImageNameFromList_Impl(nImage, vcl::ImageType::Size16); +} + +OUString SvFileInformationManager::GetDescription( const INetURLObject& rObject ) +{ + return SvFileInformationManager::GetDescription_Impl( rObject, true ); +} + +OUString SvFileInformationManager::GetFileDescription( const INetURLObject& rObject ) +{ + return SvFileInformationManager::GetDescription_Impl( rObject, false ); +} + +OUString SvFileInformationManager::GetFolderDescription( const svtools::VolumeInfo& rInfo ) +{ + TranslateId pResId = STR_DESCRIPTION_FOLDER; + if ( rInfo.m_bIsRemote ) + pResId = STR_DESCRIPTION_REMOTE_VOLUME; + else if ( rInfo.m_bIsFloppy ) + pResId = STR_DESCRIPTION_FLOPPY_VOLUME; + else if ( rInfo.m_bIsCompactDisc ) + pResId = STR_DESCRIPTION_CDROM_VOLUME; + else if ( rInfo.m_bIsRemoveable || rInfo.m_bIsVolume ) + pResId = STR_DESCRIPTION_LOCALE_VOLUME; + + return SvtResId(pResId); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/imageresourceaccess.cxx b/svtools/source/misc/imageresourceaccess.cxx new file mode 100644 index 0000000000..7a44a4d90d --- /dev/null +++ b/svtools/source/misc/imageresourceaccess.cxx @@ -0,0 +1,167 @@ +/* -*- 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 <svtools/imageresourceaccess.hxx> + +#include <com/sun/star/io/NotConnectedException.hpp> +#include <com/sun/star/io/XSeekable.hpp> +#include <com/sun/star/graphic/GraphicProvider.hpp> +#include <com/sun/star/graphic/XGraphicProvider.hpp> +#include <com/sun/star/io/XStream.hpp> + +#include <comphelper/propertyvalue.hxx> +#include <o3tl/string_view.hxx> +#include <osl/diagnose.h> +#include <tools/stream.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <unotools/streamwrap.hxx> +#include <cppuhelper/implbase.hxx> +#include <utility> + +namespace svt::GraphicAccess +{ + +using namespace ::utl; +using namespace css; + +typedef ::cppu::WeakImplHelper<io::XStream, io::XSeekable> StreamSupplier_Base; + +namespace { + +class StreamSupplier : public StreamSupplier_Base +{ +private: + uno::Reference<io::XInputStream> m_xInput; + uno::Reference<io::XOutputStream> m_xOutput; + uno::Reference<io::XSeekable> m_xSeekable; + +public: + StreamSupplier(uno::Reference<io::XInputStream> xInput, uno::Reference<io::XOutputStream> xOutput); + +protected: + // XStream + virtual uno::Reference<io::XInputStream> SAL_CALL getInputStream() override; + virtual uno::Reference<io::XOutputStream> SAL_CALL getOutputStream() override; + + // XSeekable + virtual void SAL_CALL seek(sal_Int64 location) override; + virtual sal_Int64 SAL_CALL getPosition() override; + virtual sal_Int64 SAL_CALL getLength() override; +}; + +} + +StreamSupplier::StreamSupplier(uno::Reference<io::XInputStream> xInput, uno::Reference<io::XOutputStream> xOutput) + : m_xInput(std::move(xInput)) + , m_xOutput(std::move(xOutput)) +{ + m_xSeekable.set(m_xInput, uno::UNO_QUERY); + if (!m_xSeekable.is()) + m_xSeekable.set(m_xOutput, uno::UNO_QUERY); + OSL_ENSURE(m_xSeekable.is(), "StreamSupplier::StreamSupplier: at least one of both must be seekable!"); +} + +uno::Reference<io::XInputStream> SAL_CALL StreamSupplier::getInputStream() +{ + return m_xInput; +} + +uno::Reference<io::XOutputStream> SAL_CALL StreamSupplier::getOutputStream() +{ + return m_xOutput; +} + +void SAL_CALL StreamSupplier::seek(sal_Int64 nLocation) +{ + if (!m_xSeekable.is()) + throw io::NotConnectedException(); + m_xSeekable->seek(nLocation); +} + +sal_Int64 SAL_CALL StreamSupplier::getPosition() +{ + if (!m_xSeekable.is()) + throw io::NotConnectedException(); + return m_xSeekable->getPosition(); +} + +sal_Int64 SAL_CALL StreamSupplier::getLength() +{ + if (!m_xSeekable.is()) + throw io::NotConnectedException(); + + return m_xSeekable->getLength(); +} + +bool isSupportedURL(std::u16string_view rURL) +{ + return o3tl::starts_with(rURL, u"private:resource/") + || o3tl::starts_with(rURL, u"private:graphicrepository/") + || o3tl::starts_with(rURL, u"private:standardimage/") + || o3tl::starts_with(rURL, u"vnd.sun.star.extension://"); +} + +std::unique_ptr<SvStream> getImageStream(uno::Reference<uno::XComponentContext> const & rxContext, OUString const & rImageResourceURL) +{ + std::unique_ptr<SvMemoryStream> pMemBuffer; + + try + { + // get a GraphicProvider + uno::Reference<graphic::XGraphicProvider> xProvider = css::graphic::GraphicProvider::create(rxContext); + + // let it create a graphic from the given URL + uno::Sequence<beans::PropertyValue> aMediaProperties{ comphelper::makePropertyValue( + "URL", rImageResourceURL) }; + uno::Reference<graphic::XGraphic> xGraphic(xProvider->queryGraphic(aMediaProperties)); + + OSL_ENSURE(xGraphic.is(), "GraphicAccess::getImageStream: the provider did not give us a graphic object!"); + if (!xGraphic.is()) + return pMemBuffer; + + // copy the graphic to an in-memory buffer + pMemBuffer.reset(new SvMemoryStream); + uno::Reference<io::XStream> xBufferAccess = new StreamSupplier( + new OSeekableInputStreamWrapper(*pMemBuffer), + new OSeekableOutputStreamWrapper(*pMemBuffer)); + + aMediaProperties = { comphelper::makePropertyValue("OutputStream", xBufferAccess), + comphelper::makePropertyValue("MimeType", OUString("image/png")) }; + xProvider->storeGraphic(xGraphic, aMediaProperties); + + pMemBuffer->Seek(0); + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("svtools", "GraphicAccess::getImageStream"); + pMemBuffer.reset(); + } + + return pMemBuffer; +} + +uno::Reference<io::XInputStream> getImageXStream(uno::Reference<uno::XComponentContext> const & rxContext, OUString const & rImageResourceURL) +{ + return new OSeekableInputStreamWrapper(getImageStream(rxContext, rImageResourceURL).release(), true); // take ownership +} + +} // namespace svt::GraphicAccess + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/langhelp.cxx b/svtools/source/misc/langhelp.cxx new file mode 100644 index 0000000000..afc6830fde --- /dev/null +++ b/svtools/source/misc/langhelp.cxx @@ -0,0 +1,167 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <comphelper/sequence.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <i18nlangtag/mslangid.hxx> +#include <officecfg/Office/Common.hxx> +#include <officecfg/System.hxx> +#include <o3tl/string_view.hxx> +#include <org/freedesktop/PackageKit/SyncDbusSessionHelper.hpp> +#include <rtl/ustring.hxx> +#include <svtools/langhelp.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <vcl/idle.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <config_langs.h> +#include <config_vendor.h> + +void localizeWebserviceURI( OUString& rURI ) +{ + OUString aLang = Application::GetSettings().GetUILanguageTag().getLanguage(); + if ( aLang.equalsIgnoreAsciiCase("pt") + && Application::GetSettings().GetUILanguageTag().getCountry().equalsIgnoreAsciiCase("br") ) + { + aLang = "pt-br"; + } + if ( aLang.equalsIgnoreAsciiCase("zh") ) + { + if ( Application::GetSettings().GetUILanguageTag().getCountry().equalsIgnoreAsciiCase("cn") ) + aLang = "zh-cn"; + if ( Application::GetSettings().GetUILanguageTag().getCountry().equalsIgnoreAsciiCase("tw") ) + aLang = "zh-tw"; + } + + rURI += aLang; +} + +OUString getInstalledLocaleForLanguage(css::uno::Sequence<OUString> const & installed, OUString const & locale) +{ + if (locale.isEmpty()) + return OUString(); // do not attempt to resolve anything + + if (comphelper::findValue(installed, locale) != -1) + return locale; + + std::vector<OUString> fallbacks(LanguageTag(locale).getFallbackStrings(false)); + auto pf = std::find_if(fallbacks.begin(), fallbacks.end(), + [&installed](const OUString& rf) { return comphelper::findValue(installed, rf) != -1; }); + if (pf != fallbacks.end()) + return *pf; + return OUString(); +} + +static std::unique_ptr<Idle> xLangpackInstaller; + +namespace { + +class InstallLangpack : public Idle +{ + std::vector<OUString> m_aPackages; +public: + explicit InstallLangpack(std::vector<OUString>&& rPackages) + : Idle("install langpack") + , m_aPackages(std::move(rPackages)) + { + SetPriority(TaskPriority::LOWEST); + } + + virtual void Invoke() override + { + vcl::Window* pTopWindow = Application::GetActiveTopWindow(); + if (!pTopWindow) + pTopWindow = Application::GetFirstTopLevelWindow(); + if (!pTopWindow) + { + Start(); + return; + } + try + { + using namespace org::freedesktop::PackageKit; + css::uno::Reference<XSyncDbusSessionHelper> xSyncDbusSessionHelper(SyncDbusSessionHelper::create(comphelper::getProcessComponentContext())); + xSyncDbusSessionHelper->InstallPackageNames(comphelper::containerToSequence(m_aPackages), OUString()); + } + catch (const css::uno::Exception&) + { + TOOLS_INFO_EXCEPTION("svl", "trying to install a LibreOffice langpack"); + } + xLangpackInstaller.reset(); + } +}; + +} + +OUString getInstalledLocaleForSystemUILanguage(const css::uno::Sequence<OUString>& rLocaleElementNames, bool bRequestInstallIfMissing, const OUString& rPreferredLocale) +{ + OUString wantedLocale(rPreferredLocale); + if (wantedLocale.isEmpty()) + wantedLocale = officecfg::System::L10N::UILocale::get(); + + OUString locale = getInstalledLocaleForLanguage(rLocaleElementNames, wantedLocale); + if (bRequestInstallIfMissing && locale.isEmpty() && !wantedLocale.isEmpty() && !Application::IsHeadlessModeEnabled() && + officecfg::Office::Common::PackageKit::EnableLangpackInstallation::get()) + { + LanguageTag aWantedTag(wantedLocale); + if (aWantedTag.getLanguage() != "en") + { + // Get the list of langpacks that this build was configured to include + std::vector<OUString> aPackages; + static constexpr std::u16string_view sAvailableLocales(u"" WITH_LANG); + std::vector<OUString> aAvailable; + sal_Int32 nIndex = 0; + do + { + aAvailable.emplace_back(o3tl::getToken(sAvailableLocales, 0, ' ', nIndex)); + } + while (nIndex >= 0); + // See which one matches the desired ui locale + OUString install = getInstalledLocaleForLanguage(comphelper::containerToSequence(aAvailable), wantedLocale); + if (!install.isEmpty() && install != "en-US") + { + std::string_view sVendor(OOO_VENDOR); + if (sVendor == "Red Hat, Inc." || sVendor == "The Fedora Project") + { + // langpack is the typical Fedora/RHEL naming convention + LanguageType eType = aWantedTag.getLanguageType(); + if (MsLangId::isSimplifiedChinese(eType)) + aPackages.emplace_back("libreoffice-langpack-zh-Hans"); + else if (MsLangId::isTraditionalChinese(eType)) + aPackages.emplace_back("libreoffice-langpack-zh-Hant"); + else if (install == "pt") + aPackages.emplace_back("libreoffice-langpack-pt-PT"); + else + aPackages.emplace_back("libreoffice-langpack-" + install); + } + else if (sVendor == "The Document Foundation/Debian" || sVendor == "The Document Foundation, Debian and Ubuntu") + { + // l10n is the typical Debian/Ubuntu naming convention + aPackages.emplace_back("libreoffice-l10n-" + install); + } + } + if (!aPackages.empty()) + { + xLangpackInstaller.reset(new InstallLangpack(std::move(aPackages))); + xLangpackInstaller->Start(); + } + } + } + if (locale.isEmpty()) + locale = getInstalledLocaleForLanguage(rLocaleElementNames, "en-US"); + if (locale.isEmpty() && rLocaleElementNames.hasElements()) + locale = rLocaleElementNames[0]; + return locale; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/langtab.cxx b/svtools/source/misc/langtab.cxx new file mode 100644 index 0000000000..a3ed27a3bd --- /dev/null +++ b/svtools/source/misc/langtab.cxx @@ -0,0 +1,347 @@ +/* -*- 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 <sal/config.h> + +#include <string_view> + +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/i18n/DirectionProperty.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/Any.h> + +#include <i18nlangtag/lang.h> +#include <i18nlangtag/mslangid.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <i18nlangtag/languagetagicu.hxx> + +#include <i18nutil/unicode.hxx> + +#include <sal/log.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <svtools/svtresid.hxx> +#include <svtools/langtab.hxx> +#include <unotools/syslocale.hxx> +#include <unotools/charclass.hxx> +#include <tools/resary.hxx> +#include <officecfg/VCL.hxx> +#include <langtab.hrc> + +using namespace ::com::sun::star; + +namespace { + +class SvtLanguageTableImpl +{ +private: + std::vector<std::pair<OUString, LanguageType>> m_aStrings; + void AddItem(const OUString& rLanguage, const LanguageType eType) + { + m_aStrings.emplace_back(rLanguage, eType); + } + +public: + + SvtLanguageTableImpl(); + + bool HasType( const LanguageType eType ) const; + OUString GetString( const LanguageType eType ) const; + LanguageType GetType( std::u16string_view rStr ) const; + sal_uInt32 GetEntryCount() const; + LanguageType GetTypeAtIndex( sal_uInt32 nIndex ) const; + LanguageType GetValue(sal_uInt32 nIndex) const + { + return (nIndex < m_aStrings.size()) ? m_aStrings[nIndex].second : LANGUAGE_DONTKNOW; + } + sal_uInt32 FindIndex(LanguageType nValue) const + { + const size_t nItems = m_aStrings.size(); + for (size_t i = 0; i < nItems; ++i) + { + if (m_aStrings[i].second == nValue) + return i; + } + return RESARRAY_INDEX_NOTFOUND; + } + void AddEntry( const OUString& rString, const LanguageType eType); +}; + +SvtLanguageTableImpl& theLanguageTable() +{ + static SvtLanguageTableImpl aTable; + return aTable; +} +} + +OUString ApplyLreOrRleEmbedding( const OUString &rText ) +{ + const sal_Int32 nLen = rText.getLength(); + if (nLen == 0) + return OUString(); + + constexpr sal_Unicode cLRE_Embedding = 0x202A; // the start char of an LRE embedding + constexpr sal_Unicode cRLE_Embedding = 0x202B; // the start char of an RLE embedding + constexpr sal_Unicode cPopDirectionalFormat = 0x202C; // the unicode PDF (POP_DIRECTIONAL_FORMAT) char that terminates an LRE/RLE embedding + + // check if there are already embedding characters at the strings start + // if so change nothing + const sal_Unicode cChar = rText[0]; + if (cChar == cLRE_Embedding || cChar == cRLE_Embedding) + return rText; + + // since we only call the function getCharacterDirection + // it does not matter which locale the CharClass is for. + // Thus we can readily make use of SvtSysLocale::GetCharClass() + // which should come at no cost... + SvtSysLocale aSysLocale; + const CharClass &rCharClass = aSysLocale.GetCharClass(); + + // we should look for the first non-neutral LTR or RTL character + // and use that to determine the embedding of the whole text... + // Thus we can avoid to check every character of the text. + bool bFound = false; + bool bIsRtlText = false; + for (sal_Int32 i = 0; i < nLen && !bFound; ++i) + { + i18n::DirectionProperty nDirection = rCharClass.getCharacterDirection( rText, i ); + switch (nDirection) + { + case i18n::DirectionProperty_LEFT_TO_RIGHT : + case i18n::DirectionProperty_LEFT_TO_RIGHT_EMBEDDING : + case i18n::DirectionProperty_LEFT_TO_RIGHT_OVERRIDE : + case i18n::DirectionProperty_EUROPEAN_NUMBER : + case i18n::DirectionProperty_ARABIC_NUMBER : // yes! arabic numbers are written from left to right + { + bIsRtlText = false; + bFound = true; + break; + } + + case i18n::DirectionProperty_RIGHT_TO_LEFT : + case i18n::DirectionProperty_RIGHT_TO_LEFT_ARABIC : + case i18n::DirectionProperty_RIGHT_TO_LEFT_EMBEDDING : + case i18n::DirectionProperty_RIGHT_TO_LEFT_OVERRIDE : + { + bIsRtlText = true; + bFound = true; + break; + } + + default: + { + // nothing to be done, character is considered to be neutral we need to look further ... + } + } + } + + sal_Unicode cStart = cLRE_Embedding; // default is to use LRE embedding characters + if (bIsRtlText) + cStart = cRLE_Embedding; // then use RLE embedding + + // add embedding start and end chars to the text if the direction could be determined + OUString aRes( rText ); + if (bFound) + { + aRes = OUStringChar(cStart) + aRes + + OUStringChar(cPopDirectionalFormat); + } + + return aRes; +} + +static OUString lcl_getDescription( const LanguageTag& rTag ) +{ + OUString aStr( LanguageTagIcu::getDisplayName( rTag, Application::GetSettings().GetUILanguageTag())); + if (aStr.isEmpty() || aStr == rTag.getBcp47()) + { + // Place in curly brackets, so all on-the-fly tags without display name + // are grouped together at the top of a listbox (but behind the + // "[None]" entry), and not sprinkled all over, which alphabetically + // might make sense in an English UI only anyway. Also a visual + // indicator that it is a programmatical name, IMHO. + return OUString::Concat("{") + aStr + "}"; + } + else + { + // The ICU display name might be identical to a predefined name or even + // to another tag's ICU name; clarify that this is a generated name and + // append the language tag in curly brackets to distinguish. + return aStr + " {" + rTag.getBcp47() + "}"; + } +} + +SvtLanguageTableImpl::SvtLanguageTableImpl() +{ + for (size_t i = 0; i < SAL_N_ELEMENTS(STR_ARR_SVT_LANGUAGE_TABLE); ++i) + { + m_aStrings.emplace_back(SvtResId(STR_ARR_SVT_LANGUAGE_TABLE[i].first), STR_ARR_SVT_LANGUAGE_TABLE[i].second); + } + + auto xNA = officecfg::VCL::ExtraLanguages::get(); + const uno::Sequence <OUString> rElementNames = xNA->getElementNames(); + for (const OUString& rBcp47 : rElementNames) + { + OUString aName; + sal_Int32 nType = 0; + uno::Reference <container::XNameAccess> xNB; + xNA->getByName(rBcp47) >>= xNB; + bool bSuccess = (xNB->getByName("Name") >>= aName) && + (xNB->getByName("ScriptType") >>= nType); + if (bSuccess) + { + LanguageTag aLang(rBcp47); + LanguageType nLangType = aLang.getLanguageType(); + if (nType <= sal_Int32(LanguageTag::ScriptType::RTL) && nType > sal_Int32(LanguageTag::ScriptType::UNKNOWN)) + aLang.setScriptType(LanguageTag::ScriptType(nType)); + sal_uInt32 nPos = FindIndex(nLangType); + if (nPos == RESARRAY_INDEX_NOTFOUND) + AddEntry( (aName.isEmpty() ? lcl_getDescription(aLang) : aName), nLangType); + } + } +} + +bool SvtLanguageTableImpl::HasType( const LanguageType eType ) const +{ + LanguageType eLang = MsLangId::getReplacementForObsoleteLanguage( eType ); + sal_uInt32 nPos = FindIndex(eLang); + + return RESARRAY_INDEX_NOTFOUND != nPos && nPos < GetEntryCount(); +} + +bool SvtLanguageTable::HasLanguageType( const LanguageType eType ) +{ + return theLanguageTable().HasType( eType ); +} + +OUString SvtLanguageTableImpl::GetString( const LanguageType eType ) const +{ + const LanguageType nLang = MsLangId::getReplacementForObsoleteLanguage( eType); + const sal_uInt32 nPos = (eType == LANGUAGE_PROCESS_OR_USER_DEFAULT ? + FindIndex(LANGUAGE_SYSTEM) : FindIndex( nLang)); + + if ( RESARRAY_INDEX_NOTFOUND != nPos && nPos < GetEntryCount() ) + return m_aStrings[nPos].first; + + // Obtain from ICU, or a geeky but usable-in-a-pinch lang-tag. + OUString sLangTag( lcl_getDescription( LanguageTag(nLang))); + SAL_WARN("svtools.misc", "Language: 0x" + << std::hex << nLang + << " with unknown name, so returning generated: " + << sLangTag); + + // And add it to the table, so it is available in all subsequent language boxes. + const_cast<SvtLanguageTableImpl*>(this)->AddEntry( sLangTag, nLang); + + return sLangTag; +} + +OUString SvtLanguageTable::GetLanguageString( const LanguageType eType ) +{ + return theLanguageTable().GetString( eType ); +} + +LanguageType SvtLanguageTableImpl::GetType( std::u16string_view rStr ) const +{ + LanguageType eType = LANGUAGE_DONTKNOW; + sal_uInt32 nCount = GetEntryCount(); + + for ( sal_uInt32 i = 0; i < nCount; ++i ) + { + if (m_aStrings[i].first == rStr) + { + eType = GetValue(i); + break; + } + } + return eType; +} + +LanguageType SvtLanguageTable::GetLanguageType( std::u16string_view rStr ) +{ + return theLanguageTable().GetType( rStr ); +} + +sal_uInt32 SvtLanguageTableImpl::GetEntryCount() const +{ + return m_aStrings.size(); +} + +sal_uInt32 SvtLanguageTable::GetLanguageEntryCount() +{ + return theLanguageTable().GetEntryCount(); +} + + +LanguageType SvtLanguageTableImpl::GetTypeAtIndex( sal_uInt32 nIndex ) const +{ + LanguageType nType = LANGUAGE_DONTKNOW; + if (nIndex < GetEntryCount()) + nType = GetValue(nIndex); + return nType; +} + +LanguageType SvtLanguageTable::GetLanguageTypeAtIndex( sal_uInt32 nIndex ) +{ + return theLanguageTable().GetTypeAtIndex( nIndex); +} + +void SvtLanguageTableImpl::AddEntry( const OUString& rString, const LanguageType eType ) +{ + if (LanguageTag::isOnTheFlyID(eType) + && LanguageTag::getOnTheFlyScriptType(eType) == LanguageTag::ScriptType::UNKNOWN) + { + // Classify the script type to distribute the entry into the proper + // language list later. + LanguageTag aLanguageTag(eType); + const sal_Int16 nScriptClass = unicode::getScriptClassFromLanguageTag( aLanguageTag); + LanguageTag::ScriptType eScriptType; + switch (nScriptClass) + { + default: + eScriptType = LanguageTag::ScriptType::WESTERN; + assert(!"unexpected ScriptType"); + break; + case css::i18n::ScriptType::WEAK: + case css::i18n::ScriptType::LATIN: + eScriptType = LanguageTag::ScriptType::WESTERN; + break; + case css::i18n::ScriptType::ASIAN: + eScriptType = LanguageTag::ScriptType::CJK; + break; + case css::i18n::ScriptType::COMPLEX: + /* TODO: determine if it would be LanguageTag::ScriptType::RTL + * instead; could that be done by + * getScriptClassFromLanguageTag() as well by asking Unicode + * properties? */ + eScriptType = LanguageTag::ScriptType::CTL; + break; + } + aLanguageTag.setScriptType( eScriptType); + } + AddItem( rString, eType); +} + +void SvtLanguageTable::AddLanguageTag( const LanguageTag& rLanguageTag ) +{ + theLanguageTable().AddEntry( lcl_getDescription(rLanguageTag), rLanguageTag.getLanguageType()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/openfiledroptargetlistener.cxx b/svtools/source/misc/openfiledroptargetlistener.cxx new file mode 100644 index 0000000000..3c1552f0d9 --- /dev/null +++ b/svtools/source/misc/openfiledroptargetlistener.cxx @@ -0,0 +1,211 @@ +/* -*- 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 <svtools/openfiledroptargetlistener.hxx> + +#include <com/sun/star/datatransfer/dnd/DNDConstants.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> + +#include <utility> +#include <vcl/transfer.hxx> +#include <sot/filelist.hxx> + +#include <osl/file.hxx> +#include <vcl/svapp.hxx> + +OpenFileDropTargetListener::OpenFileDropTargetListener( css::uno::Reference< css::uno::XComponentContext > xContext, + const css::uno::Reference< css::frame::XFrame >& xFrame ) + : m_xContext (std::move( xContext )) + , m_xTargetFrame ( xFrame ) +{ +} + + +OpenFileDropTargetListener::~OpenFileDropTargetListener() +{ + m_xTargetFrame.clear(); + m_xContext.clear(); +} + + +void SAL_CALL OpenFileDropTargetListener::disposing( const css::lang::EventObject& ) +{ + m_xTargetFrame.clear(); + m_xContext.clear(); +} + + +void SAL_CALL OpenFileDropTargetListener::drop( const css::datatransfer::dnd::DropTargetDropEvent& dtde ) +{ + const sal_Int8 nAction = dtde.DropAction; + + try + { + if ( css::datatransfer::dnd::DNDConstants::ACTION_NONE != nAction ) + { + TransferableDataHelper aHelper( dtde.Transferable ); + bool bFormatFound = false; + FileList aFileList; + + // at first check filelist format + if ( aHelper.GetFileList( SotClipboardFormatId::FILE_LIST, aFileList ) ) + { + sal_uLong i, nCount = aFileList.Count(); + for ( i = 0; i < nCount; ++i ) + implts_OpenFile( aFileList.GetFile(i) ); + bFormatFound = true; + } + + // then, if necessary, the file format + OUString aFilePath; + if ( !bFormatFound && aHelper.GetString( SotClipboardFormatId::SIMPLE_FILE, aFilePath ) ) + implts_OpenFile( aFilePath ); + } + dtde.Context->dropComplete( css::datatransfer::dnd::DNDConstants::ACTION_NONE != nAction ); + } + catch( const css::uno::Exception& ) + { + } +} + + +void SAL_CALL OpenFileDropTargetListener::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent& dtdee ) +{ + try + { + implts_BeginDrag( dtdee.SupportedDataFlavors ); + } + catch( const css::uno::Exception& ) + { + } + + dragOver( dtdee ); +} + + +void SAL_CALL OpenFileDropTargetListener::dragExit( const css::datatransfer::dnd::DropTargetEvent& ) +{ + try + { + implts_EndDrag(); + } + catch( const css::uno::Exception& ) + { + } +} + + +void SAL_CALL OpenFileDropTargetListener::dragOver( const css::datatransfer::dnd::DropTargetDragEvent& dtde ) +{ + try + { + bool bAccept = ( implts_IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ) || + implts_IsDropFormatSupported( SotClipboardFormatId::FILE_LIST ) ); + + if ( !bAccept ) + dtde.Context->rejectDrag(); + else + dtde.Context->acceptDrag( css::datatransfer::dnd::DNDConstants::ACTION_COPY ); + } + catch( const css::uno::Exception& ) + { + } +} + + +void SAL_CALL OpenFileDropTargetListener::dropActionChanged( const css::datatransfer::dnd::DropTargetDragEvent& ) +{ +} + +void OpenFileDropTargetListener::implts_BeginDrag( const css::uno::Sequence< css::datatransfer::DataFlavor >& rSupportedDataFlavors ) +{ + /* SAFE { */ + SolarMutexGuard aGuard; + + m_aFormats.clear(); + TransferableDataHelper::FillDataFlavorExVector(rSupportedDataFlavors, m_aFormats); + /* } SAFE */ +} + +void OpenFileDropTargetListener::implts_EndDrag() +{ + /* SAFE { */ + SolarMutexGuard aGuard; + + m_aFormats.clear(); + /* } SAFE */ +} + +bool OpenFileDropTargetListener::implts_IsDropFormatSupported( SotClipboardFormatId nFormat ) +{ + /* SAFE { */ + SolarMutexGuard aGuard; + + for (auto const& format : m_aFormats) + { + if (nFormat == format.mnSotId) + { + return true; + } + } + /* } SAFE */ + + return false; +} + +void OpenFileDropTargetListener::implts_OpenFile( const OUString& rFilePath ) +{ + OUString aFileURL; + if ( osl::FileBase::getFileURLFromSystemPath( rFilePath, aFileURL ) != osl::FileBase::E_None ) + aFileURL = rFilePath; + + ::osl::FileStatus aStatus( osl_FileStatus_Mask_FileURL ); + ::osl::DirectoryItem aItem; + if( ::osl::FileBase::E_None == ::osl::DirectoryItem::get( aFileURL, aItem ) && + ::osl::FileBase::E_None == aItem.getFileStatus( aStatus ) ) + aFileURL = aStatus.getFileURL(); + + // open file + /* SAFE { */ + SolarMutexGuard aGuard; + + css::uno::Reference< css::frame::XFrame > xTargetFrame( m_xTargetFrame.get(), css::uno::UNO_QUERY ); + css::uno::Reference< css::util::XURLTransformer > xParser ( css::util::URLTransformer::create(m_xContext) ); + + if (xTargetFrame.is() && xParser.is()) + { + css::util::URL aURL; + aURL.Complete = aFileURL; + xParser->parseStrict(aURL); + + css::uno::Reference < css::frame::XDispatchProvider > xProvider( xTargetFrame, css::uno::UNO_QUERY ); + // Create a new task or recycle an existing one + css::uno::Reference< css::frame::XDispatch > xDispatcher = xProvider->queryDispatch( aURL, "_default", 0 ); + if ( xDispatcher.is() ) + xDispatcher->dispatch( aURL, css::uno::Sequence < css::beans::PropertyValue >() ); + } + /* } SAFE */ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/sampletext.cxx b/svtools/source/misc/sampletext.cxx new file mode 100644 index 0000000000..101d68362a --- /dev/null +++ b/svtools/source/misc/sampletext.cxx @@ -0,0 +1,1671 @@ +/* -*- 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 <svtools/sampletext.hxx> +#include <vcl/font.hxx> +#include <vcl/outdev.hxx> +#include <vcl/virdev.hxx> +#include <vcl/fontcharmap.hxx> +#include <i18nutil/unicode.hxx> +#include <sal/log.hxx> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <vector> +#include <map> + +// This should only be used when a commonly used font incorrectly declares its +// coverage. If you add a font here, please leave a note explaining the issue +// that caused it to be added +static UScriptCode lcl_getHardCodedScriptNameForFont (const OutputDevice &rDevice) +{ + const OUString &rName = rDevice.GetFont().GetFamilyName(); + + if (rName == "GB18030 Bitmap") + { + // As of OSX 10.9, the system font "GB18030 Bitmap" incorrectly declares + // that it only covers "Phoenician" when in fact it's a Chinese font. + return USCRIPT_HAN; + } + else if (rName == "BiauKai") + { + // "BiauKai" makes crazy claims to cover BUGINESE, SUNDANESE, etc + // but in fact it's a Traditional Chinese font. + return USCRIPT_TRADITIONAL_HAN; + } + else if (rName == "GungSeo" || rName == "PCMyungjo" || rName == "PilGi") + { + // These have no OS/2 tables, but we know they are Korean fonts. + return USCRIPT_KOREAN; + } + else if (rName == "Hei" || rName == "Kai") + { + // These have no OS/2 tables, but we know they are Chinese fonts. + return USCRIPT_HAN; + } + else if (rName.startsWith("Bangla ")) + { + // "Bangla Sangam MN" claims it supports MALAYALAM, but it doesn't + // "Bangla MN" claims just DEVANAGARI and not an additional BENGALI + return USCRIPT_BENGALI; + } + else if (rName.startsWith("Gurmukhi ")) + { + // "Gurmukhi MN" claims it supports TAMIL, but it doesn't + return USCRIPT_GURMUKHI; + } + else if (rName.startsWith("Kannada ")) + { + // "Kannada MN" claims it supports TAMIL, but it doesn't + return USCRIPT_KANNADA; + } + else if (rName.startsWith("Lao ")) + { + // "Lao Sangam MN" claims it supports TAMIL, but it doesn't + return USCRIPT_LAO; + } + else if (rName.startsWith("Malayalam ")) + { + // "Malayalam MN" claims it supports TAMIL, but it doesn't + return USCRIPT_MALAYALAM; + } + else if (rName.startsWith("Sinhala ")) + { + // "Sinhala MN" claims it supports CYRILLIC + return USCRIPT_SINHALA; + } + else if (rName.startsWith("Telugu ")) + { + // "Telugu MN" claims it supports TAMIL, but it doesn't + return USCRIPT_TELUGU; + } + else if (rName.startsWith("Myanmar ")) + { + return USCRIPT_MYANMAR; + } + else if (rName == "InaiMathi") + { + // "InaiMathi" claims it supports GOTHIC and CJK_UNIFIED_IDEOGRAPHS as well as + // TAMIL, but it doesn't + return USCRIPT_TAMIL; + } + else if (rName == "Hannotate TC" || rName == "HanziPen TC" || rName == "Heiti TC" || rName == "Weibei TC") + { + // These fonts claim support for ARMENIAN and a bunch of other stuff they don't support + return USCRIPT_TRADITIONAL_HAN; + } + else if (rName == "Hannotate SC" || rName == "HanziPen SC" || rName == "Heiti SC" || rName == "Weibei SC") + { + // These fonts claim support for ARMENIAN and a bunch of other stuff they don't support + return USCRIPT_SIMPLIFIED_HAN; + } + return USCRIPT_INVALID_CODE; +} + +bool isSymbolFont(const vcl::Font &rFont) +{ + return (rFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL) || + rFont.GetFamilyName().equalsIgnoreAsciiCase("Apple Color Emoji") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("cmsy10") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("cmex10") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("esint10") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("feta26") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("jsMath-cmsy10") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("jsMath-cmex10") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("msam10") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("msbm10") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("wasy10") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("Denemo") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("GlyphBasic1") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("GlyphBasic2") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("GlyphBasic3") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("GlyphBasic4") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("Letters Laughing") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("MusiQwik") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("MusiSync") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("stmary10") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("Symbol") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("Webdings") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("Wingdings") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("Wingdings 2") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("Wingdings 3") || + rFont.GetFamilyName().equalsIgnoreAsciiCase("Bookshelf Symbol 7") || + rFont.GetFamilyName().startsWith("STIXIntegrals") || + rFont.GetFamilyName().startsWith("STIXNonUnicode") || + rFont.GetFamilyName().startsWith("STIXSize") || + rFont.GetFamilyName().startsWith("STIXVariants") || + IsOpenSymbol(rFont.GetFamilyName()); +} + +bool canRenderNameOfSelectedFont(OutputDevice const &rDevice) +{ + const vcl::Font &rFont = rDevice.GetFont(); + return !isSymbolFont(rFont) && ( -1 == rDevice.HasGlyphs(rFont, rFont.GetFamilyName()) ); +} + +OUString makeShortRepresentativeSymbolTextForSelectedFont(OutputDevice const &rDevice) +{ + if (rDevice.GetFont().GetFamilyName() == "Symbol") + { + static constexpr OUString aImplAppleSymbolText = + u"\u03BC\u2202\u2211\u220F\u03C0\u222B\u03A9\u221A"_ustr; + bool bHasSampleTextGlyphs + = (-1 == rDevice.HasGlyphs(rDevice.GetFont(), aImplAppleSymbolText)); + //It's the Apple version + if (bHasSampleTextGlyphs) + return aImplAppleSymbolText; + static constexpr OUStringLiteral aImplAdobeSymbolText = + u"\uF06D\uF0B6\uF0E5\uF0D5\uF070\uF0F2\uF057\uF0D6"; + return aImplAdobeSymbolText; + } + + const bool bOpenSymbol = IsOpenSymbol(rDevice.GetFont().GetFamilyName()); + + if (!bOpenSymbol) + { + FontCharMapRef xFontCharMap; + bool bHasCharMap = rDevice.GetFontCharMap(xFontCharMap); + if( bHasCharMap ) + { + // use some sample characters available in the font + sal_Unicode aText[8]; + + // start just above the PUA used by most symbol fonts + sal_uInt32 cNewChar = 0xFF00; + + const int nMaxCount = SAL_N_ELEMENTS(aText) - 1; + int nSkip = xFontCharMap->GetCharCount() / nMaxCount; + if( nSkip > 10 ) + nSkip = 10; + else if( nSkip <= 0 ) + nSkip = 1; + for( int i = 0; i < nMaxCount; ++i ) + { + sal_uInt32 cOldChar = cNewChar; + for( int j = nSkip; --j >= 0; ) + cNewChar = xFontCharMap->GetPrevChar( cNewChar ); + if( cOldChar == cNewChar ) + break; + aText[ i ] = static_cast<sal_Unicode>(cNewChar); // TODO: support UCS4 samples + aText[ i+1 ] = 0; + } + + return OUString(aText); + } + } + + static const sal_Unicode aImplSymbolFontText[] = { + 0xF021,0xF032,0xF043,0xF054,0xF065,0xF076,0xF0B7,0xF0C8,0}; + static const sal_Unicode aImplStarSymbolText[] = { + 0x2702,0x2708,0x270D,0xE033,0x2211,0x2288,0}; + const sal_Unicode* pText = bOpenSymbol ? aImplStarSymbolText : aImplSymbolFontText; + OUString sSampleText(pText); + bool bHasSampleTextGlyphs = (-1 == rDevice.HasGlyphs(rDevice.GetFont(), sSampleText)); + return bHasSampleTextGlyphs ? sSampleText : OUString(); +} + +//These ones are typically for use in the font dropdown box beside the +//fontname, so say things roughly like "Script/Alphabet/Name-Of-Major-Language" + +//Here we don't always know the language of course, only the script that can be +//written with the font. Often that's one single language written in that +//script, or a handful of related languages where the name of the script is the +//same between languages, or the name in the major language is known by most +//readers of the minor languages, e.g. Yiddish is written with the HEBREW +//script as well, the vast majority of Yiddish readers will be able to read +//Hebrew as well. +OUString makeShortRepresentativeTextForScript(UScriptCode eScript) +{ + OUString sSampleText; + switch (eScript) + { + case USCRIPT_GREEK: + { + static constexpr OUStringLiteral aGrek = + u"\u0391\u03BB\u03C6\u03AC\u03B2\u03B7\u03C4\u03BF"; + sSampleText = aGrek; + break; + } + case USCRIPT_HEBREW: + { + static constexpr OUStringLiteral aHebr = + u"\u05D0\u05DC\u05E3\u05BE\u05D1\u05D9\u05EA " + "\u05E2\u05D1\u05E8\u05D9"; + sSampleText = aHebr; + break; + } + case USCRIPT_ARABIC: + { + static constexpr OUStringLiteral aArab = + u"\u0623\u0628\u062C\u062F\u064A\u0629 \u0639" + "\u0631\u0628\u064A\u0629"; + sSampleText = aArab; + break; + } + case USCRIPT_ARMENIAN: + { + static constexpr OUStringLiteral aArmenian = + u"\u0561\u0575\u0562\u0578\u0582\u0562\u0565" + "\u0576"; + sSampleText = aArmenian; + break; + } + case USCRIPT_DEVANAGARI: + { + static constexpr OUStringLiteral aDeva = + u"\u0926\u0947\u0935\u0928\u093E\u0917\u0930\u0940"; + sSampleText = aDeva; + break; + } + case USCRIPT_BENGALI: + { + static constexpr OUStringLiteral aBeng = + u"\u09AC\u09BE\u0982\u09B2\u09BE \u09B2\u09BF" + "\u09AA\u09BF"; + sSampleText = aBeng; + break; + } + case USCRIPT_GURMUKHI: + { + static constexpr OUStringLiteral aGuru = + u"\u0A17\u0A41\u0A30\u0A2E\u0A41\u0A16\u0A40"; + sSampleText = aGuru; + break; + } + case USCRIPT_GUJARATI: + { + static constexpr OUStringLiteral aGujr = + u"\u0A97\u0AC1\u0A9C\u0AB0\u0ABE\u0AA4\u0aC0 " + "\u0AB2\u0ABF\u0AAA\u0ABF"; + sSampleText = aGujr; + break; + } + case USCRIPT_ORIYA: + { + static constexpr OUStringLiteral aOrya = + u"\u0B09\u0B24\u0B4D\u0B15\u0B33 \u0B32\u0B3F" + "\u0B2A\u0B3F"; + sSampleText = aOrya; + break; + } + case USCRIPT_TAMIL: + { + static constexpr OUStringLiteral aTaml = + u"\u0B85\u0BB0\u0BBF\u0B9A\u0BCD\u0B9A\u0BC1\u0BB5" + "\u0B9F\u0BBF"; + sSampleText = aTaml; + break; + } + case USCRIPT_TELUGU: + { + static constexpr OUStringLiteral aTelu = + u"\u0C24\u0C46\u0C32\u0C41\u0C17\u0C41"; + sSampleText = aTelu; + break; + } + case USCRIPT_KANNADA: + { + static constexpr OUStringLiteral aKnda = + u"\u0C95\u0CA8\u0CCD\u0CA8\u0CA1 \u0CB2\u0CBF" + "\u0CAA\u0CBF"; + sSampleText = aKnda; + break; + } + case USCRIPT_MALAYALAM: + { + static constexpr OUStringLiteral aMlym = + u"\u0D2E\u0D32\u0D2F\u0D3E\u0D33\u0D32\u0D3F\u0D2A" + "\u0D3F"; + sSampleText = aMlym; + break; + } + case USCRIPT_THAI: + { + static constexpr OUStringLiteral aThai = + u"\u0E2D\u0E31\u0E01\u0E29\u0E23\u0E44\u0E17\u0E22"; + sSampleText = aThai; + break; + } + case USCRIPT_LAO: + { + static constexpr OUStringLiteral aLao = + u"\u0EAD\u0EB1\u0E81\u0EAA\u0EAD\u0E99\u0EA5\u0EB2" + "\u0EA7"; + sSampleText = aLao; + break; + } + case USCRIPT_GEORGIAN: + { + static constexpr OUStringLiteral aGeorgian = + u"\u10D3\u10D0\u10DB\u10EC\u10D4\u10E0\u10DA\u10DD" + "\u10D1\u10D0"; + sSampleText = aGeorgian; + break; + } + case USCRIPT_JAMO: + case USCRIPT_HANGUL: + case USCRIPT_KOREAN: + { + static constexpr OUStringLiteral aHang = + u"\uD55C\uAE00"; + sSampleText = aHang; + break; + } + case USCRIPT_TIBETAN: + { + static constexpr OUStringLiteral aTibt = + u"\u0F51\u0F56\u0F74\u0F0B\u0F45\u0F53\u0F0B"; + sSampleText = aTibt; + break; + } + case USCRIPT_SYRIAC: + { + static constexpr OUStringLiteral aSyri = + u"\u0723\u071B\u072A\u0722\u0713\u0720\u0710"; + sSampleText = aSyri; + break; + } + case USCRIPT_THAANA: + { + static constexpr OUStringLiteral aThaa = + u"\u078C\u07A7\u0782\u07A6"; + sSampleText = aThaa; + break; + } + case USCRIPT_SINHALA: + { + static constexpr OUStringLiteral aSinh = + u"\u0DC1\u0DD4\u0DAF\u0DCA\u0DB0 \u0DC3\u0DD2" + "\u0D82\u0DC4\u0DBD"; + sSampleText = aSinh; + break; + } + case USCRIPT_MYANMAR: + { + static constexpr OUStringLiteral aMymr = + u"\u1019\u103C\u1014\u103A\u1019\u102C\u1021\u1000" + "\u1039\u1001\u101B\u102C"; + sSampleText = aMymr; + break; + } + case USCRIPT_ETHIOPIC: + { + static constexpr OUStringLiteral aEthi = + u"\u130D\u12D5\u12DD"; + sSampleText = aEthi; + break; + } + case USCRIPT_CHEROKEE: + { + static constexpr OUStringLiteral aCher = + u"\u13D7\u13AA\u13EA\u13B6\u13D9\u13D7"; + sSampleText = aCher; + break; + } + case USCRIPT_KHMER: + { + static constexpr OUStringLiteral aKhmr = + u"\u17A2\u1780\u17D2\u1781\u179A\u1780\u17D2\u179A" + "\u1798\u1781\u17C1\u1798\u179A\u1797\u17B6\u179F" + "\u17B6"; + sSampleText = aKhmr; + break; + } + case USCRIPT_MONGOLIAN: + { + static constexpr OUStringLiteral aMongolian = + u"\u182A\u1822\u1834\u1822\u182D\u180C"; + sSampleText = aMongolian; + break; + } + case USCRIPT_TAGALOG: + { + static constexpr OUStringLiteral aTagalog = + u"\u170A\u170A\u170C\u1712"; + sSampleText = aTagalog; + break; + } + case USCRIPT_NEW_TAI_LUE: + { + static constexpr OUStringLiteral aTalu = + u"\u1991\u19BA\u199F\u19B9\u19C9"; + sSampleText = aTalu; + break; + } + case USCRIPT_TRADITIONAL_HAN: + { + static constexpr OUStringLiteral aHant = + u"\u7E41"; + sSampleText = aHant; + break; + } + case USCRIPT_SIMPLIFIED_HAN: + { + static constexpr OUStringLiteral aHans = + u"\u7B80"; + sSampleText = aHans; + break; + } + case USCRIPT_HAN: + { + static constexpr OUStringLiteral aSimplifiedAndTraditionalChinese = + u"\u7B80\u7E41"; + sSampleText = aSimplifiedAndTraditionalChinese; + break; + } + case USCRIPT_JAPANESE: + { + static constexpr OUStringLiteral aJpan = + u"\u65E5\u672C\u8A9E"; + sSampleText = aJpan; + break; + } + case USCRIPT_YI: + { + static constexpr OUStringLiteral aYiii = + u"\uA188\uA320\uA071\uA0B7"; + sSampleText = aYiii; + break; + } + case USCRIPT_PHAGS_PA: + { + static constexpr OUStringLiteral aPhag = + u"\uA84F\uA861\uA843 \uA863\uA861\uA859 " + u"\uA850\uA85C\uA85E"; + sSampleText = aPhag; + break; + } + case USCRIPT_TAI_LE: + { + static constexpr OUStringLiteral aTale = + u"\u1956\u196D\u1970\u1956\u196C\u1973\u1951\u1968" + "\u1952\u1970"; + sSampleText = aTale; + break; + } + case USCRIPT_LATIN: + sSampleText = "Lorem ipsum"; + break; + default: + break; + } + return sSampleText; +} + +static OUString makeRepresentativeTextForScript(UScriptCode eScript) +{ + OUString sSampleText; + switch (eScript) + { + case USCRIPT_TRADITIONAL_HAN: + case USCRIPT_SIMPLIFIED_HAN: + case USCRIPT_HAN: + { + //Three Character Classic + static constexpr OUStringLiteral aZh = + u"\u4EBA\u4E4B\u521D \u6027\u672C\u5584"; + sSampleText = aZh; + break; + } + case USCRIPT_JAPANESE: + { + //'Beautiful Japanese' + static constexpr OUStringLiteral aJa = + u"\u7F8E\u3057\u3044\u65E5\u672C\u8A9E"; + sSampleText = aJa; + break; + } + case USCRIPT_JAMO: + case USCRIPT_KOREAN: + case USCRIPT_HANGUL: + { + //The essential condition for... + static constexpr OUStringLiteral aKo = + u"\uD0A4\uC2A4\uC758 \uACE0\uC720\uC870" + "\uAC74\uC740"; + sSampleText = aKo; + break; + } + default: + break; + } + + if (sSampleText.isEmpty()) + sSampleText = makeShortRepresentativeTextForScript(eScript); + return sSampleText; +} + +OUString makeShortMinimalTextForScript(UScriptCode eScript) +{ + OUString sSampleText; + switch (eScript) + { + case USCRIPT_GREEK: + { + static constexpr OUStringLiteral aGrek = + u"\u0391\u0392"; + sSampleText = aGrek; + break; + } + case USCRIPT_HEBREW: + { + static constexpr OUStringLiteral aHebr = + u"\u05D0\u05D1"; + sSampleText = aHebr; + break; + } + default: + break; + } + return sSampleText; +} + +static OUString makeMinimalTextForScript(UScriptCode eScript) +{ + return makeShortMinimalTextForScript(eScript); +} + +//These ones are typically for use in the font preview window in format +//character + +//There we generally know the language. Though it's possible for the language to +//be "none". + +//Currently we fall back to makeShortRepresentativeTextForScript when we don't +//have suitable strings +static OUString makeRepresentativeTextForLanguage(LanguageType eLang) +{ + OUString sRet; + LanguageType pri = primary(eLang); + if( pri == primary(LANGUAGE_ARMENIAN) ) + sRet = makeRepresentativeTextForScript(USCRIPT_ARMENIAN); + else if( pri == primary(LANGUAGE_CHINESE) ) + sRet = makeRepresentativeTextForScript(USCRIPT_HAN); + else if( pri == primary(LANGUAGE_GREEK) ) + sRet = makeRepresentativeTextForScript(USCRIPT_GREEK); + else if( pri.anyOf( + primary(LANGUAGE_HEBREW), + primary(LANGUAGE_YIDDISH)) ) + sRet = makeRepresentativeTextForScript(USCRIPT_HEBREW); + else if( pri == primary(LANGUAGE_ARABIC_SAUDI_ARABIA) ) + sRet = makeRepresentativeTextForScript(USCRIPT_ARABIC); + else if( pri == primary(LANGUAGE_HINDI) ) + sRet = makeRepresentativeTextForScript(USCRIPT_DEVANAGARI); + else if( pri == primary(LANGUAGE_ASSAMESE) ) + { + static constexpr OUStringLiteral aAs = + u"\u0985\u09B8\u09AE\u09C0\u09AF\u09BC\u09BE" + " \u0986\u0996\u09F0"; + sRet = aAs; + } + else if( pri == primary(LANGUAGE_BENGALI) ) + sRet = makeRepresentativeTextForScript(USCRIPT_BENGALI); + else if( pri == primary(LANGUAGE_PUNJABI) ) + sRet = makeRepresentativeTextForScript(USCRIPT_GURMUKHI); + else if( pri == primary(LANGUAGE_GUJARATI) ) + sRet = makeRepresentativeTextForScript(USCRIPT_GUJARATI); + else if( pri == primary(LANGUAGE_ODIA) ) + sRet = makeRepresentativeTextForScript(USCRIPT_ORIYA); + else if( pri == primary(LANGUAGE_TAMIL) ) + sRet = makeRepresentativeTextForScript(USCRIPT_TAMIL); + else if( pri == primary(LANGUAGE_TELUGU) ) + sRet = makeRepresentativeTextForScript(USCRIPT_TELUGU); + else if( pri == primary(LANGUAGE_KANNADA) ) + sRet = makeRepresentativeTextForScript(USCRIPT_KANNADA); + else if( pri == primary(LANGUAGE_MALAYALAM) ) + sRet = makeRepresentativeTextForScript(USCRIPT_MALAYALAM); + else if( pri == primary(LANGUAGE_THAI) ) + sRet = makeRepresentativeTextForScript(USCRIPT_THAI); + else if( pri == primary(LANGUAGE_LAO) ) + sRet = makeRepresentativeTextForScript(USCRIPT_LAO); + else if( pri == primary(LANGUAGE_GEORGIAN) ) + sRet = makeRepresentativeTextForScript(USCRIPT_GEORGIAN); + else if( pri == primary(LANGUAGE_KOREAN) ) + sRet = makeRepresentativeTextForScript(USCRIPT_KOREAN); + else if( pri == primary(LANGUAGE_TIBETAN) ) + sRet = makeRepresentativeTextForScript(USCRIPT_TIBETAN); + else if( pri == primary(LANGUAGE_SYRIAC) ) + sRet = makeRepresentativeTextForScript(USCRIPT_SYRIAC); + else if( pri == primary(LANGUAGE_SINHALESE_SRI_LANKA) ) + sRet = makeRepresentativeTextForScript(USCRIPT_SINHALA); + else if( pri == primary(LANGUAGE_BURMESE) ) + sRet = makeRepresentativeTextForScript(USCRIPT_MYANMAR); + else if( pri == primary(LANGUAGE_AMHARIC_ETHIOPIA) ) + sRet = makeRepresentativeTextForScript(USCRIPT_ETHIOPIC); + else if( pri == primary(LANGUAGE_CHEROKEE_UNITED_STATES) ) + sRet = makeRepresentativeTextForScript(USCRIPT_CHEROKEE); + else if( pri == primary(LANGUAGE_KHMER) ) + sRet = makeRepresentativeTextForScript(USCRIPT_KHMER); + else if( pri == primary(LANGUAGE_MONGOLIAN_MONGOLIAN_LSO) ) + { + if (eLang.anyOf( + LANGUAGE_MONGOLIAN_MONGOLIAN_MONGOLIA, + LANGUAGE_MONGOLIAN_MONGOLIAN_CHINA, + LANGUAGE_MONGOLIAN_MONGOLIAN_LSO)) + sRet = makeRepresentativeTextForScript(USCRIPT_MONGOLIAN); + } + else if( pri == primary(LANGUAGE_JAPANESE) ) + sRet = makeRepresentativeTextForScript(USCRIPT_JAPANESE); + else if( pri == primary(LANGUAGE_YI) ) + sRet = makeRepresentativeTextForScript(USCRIPT_YI); + else if( pri == primary(LANGUAGE_GAELIC_IRELAND) ) + { + static constexpr OUStringLiteral aGa = + u"T\u00E9acs Samplach"; + sRet = aGa; + } + + return sRet; +} + +namespace +{ +#if OSL_DEBUG_LEVEL > 0 + void lcl_dump_unicode_coverage(const std::optional<std::bitset<vcl::UnicodeCoverage::MAX_UC_ENUM>> &roIn) + { + if (!roIn) + { + SAL_INFO("svtools", "<NOTHING>"); + return; + } + auto & rIn(*roIn); + if (rIn.none()) + { + SAL_INFO("svtools", "<NONE>"); + return; + } + if (rIn[vcl::UnicodeCoverage::BASIC_LATIN]) + SAL_INFO("svtools", "BASIC_LATIN"); + if (rIn[vcl::UnicodeCoverage::LATIN_1_SUPPLEMENT]) + SAL_INFO("svtools", "LATIN_1_SUPPLEMENT"); + if (rIn[vcl::UnicodeCoverage::LATIN_EXTENDED_A]) + SAL_INFO("svtools", "LATIN_EXTENDED_A"); + if (rIn[vcl::UnicodeCoverage::LATIN_EXTENDED_B]) + SAL_INFO("svtools", "LATIN_EXTENDED_B"); + if (rIn[vcl::UnicodeCoverage::IPA_EXTENSIONS]) + SAL_INFO("svtools", "IPA_EXTENSIONS"); + if (rIn[vcl::UnicodeCoverage::SPACING_MODIFIER_LETTERS]) + SAL_INFO("svtools", "SPACING_MODIFIER_LETTERS"); + if (rIn[vcl::UnicodeCoverage::COMBINING_DIACRITICAL_MARKS]) + SAL_INFO("svtools", "COMBINING_DIACRITICAL_MARKS"); + if (rIn[vcl::UnicodeCoverage::GREEK_AND_COPTIC]) + SAL_INFO("svtools", "GREEK_AND_COPTIC"); + if (rIn[vcl::UnicodeCoverage::COPTIC]) + SAL_INFO("svtools", "COPTIC"); + if (rIn[vcl::UnicodeCoverage::CYRILLIC]) + SAL_INFO("svtools", "CYRILLIC"); + if (rIn[vcl::UnicodeCoverage::ARMENIAN]) + SAL_INFO("svtools", "ARMENIAN"); + if (rIn[vcl::UnicodeCoverage::HEBREW]) + SAL_INFO("svtools", "HEBREW"); + if (rIn[vcl::UnicodeCoverage::VAI]) + SAL_INFO("svtools", "VAI"); + if (rIn[vcl::UnicodeCoverage::ARABIC]) + SAL_INFO("svtools", "ARABIC"); + if (rIn[vcl::UnicodeCoverage::NKO]) + SAL_INFO("svtools", "NKO"); + if (rIn[vcl::UnicodeCoverage::DEVANAGARI]) + SAL_INFO("svtools", "DEVANAGARI"); + if (rIn[vcl::UnicodeCoverage::BENGALI]) + SAL_INFO("svtools", "BENGALI"); + if (rIn[vcl::UnicodeCoverage::GURMUKHI]) + SAL_INFO("svtools", "GURMUKHI"); + if (rIn[vcl::UnicodeCoverage::GUJARATI]) + SAL_INFO("svtools", "GUJARATI"); + if (rIn[vcl::UnicodeCoverage::ODIA]) + SAL_INFO("svtools", "ODIA"); + if (rIn[vcl::UnicodeCoverage::TAMIL]) + SAL_INFO("svtools", "TAMIL"); + if (rIn[vcl::UnicodeCoverage::TELUGU]) + SAL_INFO("svtools", "TELUGU"); + if (rIn[vcl::UnicodeCoverage::KANNADA]) + SAL_INFO("svtools", "KANNADA"); + if (rIn[vcl::UnicodeCoverage::MALAYALAM]) + SAL_INFO("svtools", "MALAYALAM"); + if (rIn[vcl::UnicodeCoverage::THAI]) + SAL_INFO("svtools", "THAI"); + if (rIn[vcl::UnicodeCoverage::LAO]) + SAL_INFO("svtools", "LAO"); + if (rIn[vcl::UnicodeCoverage::GEORGIAN]) + SAL_INFO("svtools", "GEORGIAN"); + if (rIn[vcl::UnicodeCoverage::BALINESE]) + SAL_INFO("svtools", "BALINESE"); + if (rIn[vcl::UnicodeCoverage::HANGUL_JAMO]) + SAL_INFO("svtools", "HANGUL_JAMO"); + if (rIn[vcl::UnicodeCoverage::LATIN_EXTENDED_ADDITIONAL]) + SAL_INFO("svtools", "LATIN_EXTENDED_ADDITIONAL"); + if (rIn[vcl::UnicodeCoverage::GREEK_EXTENDED]) + SAL_INFO("svtools", "GREEK_EXTENDED"); + if (rIn[vcl::UnicodeCoverage::GENERAL_PUNCTUATION]) + SAL_INFO("svtools", "GENERAL_PUNCTUATION"); + if (rIn[vcl::UnicodeCoverage::SUPERSCRIPTS_AND_SUBSCRIPTS]) + SAL_INFO("svtools", "SUPERSCRIPTS_AND_SUBSCRIPTS"); + if (rIn[vcl::UnicodeCoverage::CURRENCY_SYMBOLS]) + SAL_INFO("svtools", "CURRENCY_SYMBOLS"); + if (rIn[vcl::UnicodeCoverage::COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS]) + SAL_INFO("svtools", "COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS"); + if (rIn[vcl::UnicodeCoverage::LETTERLIKE_SYMBOLS]) + SAL_INFO("svtools", "LETTERLIKE_SYMBOLS"); + if (rIn[vcl::UnicodeCoverage::NUMBER_FORMS]) + SAL_INFO("svtools", "NUMBER_FORMS"); + if (rIn[vcl::UnicodeCoverage::ARROWS]) + SAL_INFO("svtools", "ARROWS"); + if (rIn[vcl::UnicodeCoverage::MATHEMATICAL_OPERATORS]) + SAL_INFO("svtools", "MATHEMATICAL_OPERATORS"); + if (rIn[vcl::UnicodeCoverage::MISCELLANEOUS_TECHNICAL]) + SAL_INFO("svtools", "MISCELLANEOUS_TECHNICAL"); + if (rIn[vcl::UnicodeCoverage::CONTROL_PICTURES]) + SAL_INFO("svtools", "CONTROL_PICTURES"); + if (rIn[vcl::UnicodeCoverage::OPTICAL_CHARACTER_RECOGNITION]) + SAL_INFO("svtools", "OPTICAL_CHARACTER_RECOGNITION"); + if (rIn[vcl::UnicodeCoverage::ENCLOSED_ALPHANUMERICS]) + SAL_INFO("svtools", "ENCLOSED_ALPHANUMERICS"); + if (rIn[vcl::UnicodeCoverage::BOX_DRAWING]) + SAL_INFO("svtools", "BOX_DRAWING"); + if (rIn[vcl::UnicodeCoverage::BLOCK_ELEMENTS]) + SAL_INFO("svtools", "BLOCK_ELEMENTS"); + if (rIn[vcl::UnicodeCoverage::GEOMETRIC_SHAPES]) + SAL_INFO("svtools", "GEOMETRIC_SHAPES"); + if (rIn[vcl::UnicodeCoverage::MISCELLANEOUS_SYMBOLS]) + SAL_INFO("svtools", "MISCELLANEOUS_SYMBOLS"); + if (rIn[vcl::UnicodeCoverage::DINGBATS]) + SAL_INFO("svtools", "DINGBATS"); + if (rIn[vcl::UnicodeCoverage::CJK_SYMBOLS_AND_PUNCTUATION]) + SAL_INFO("svtools", "CJK_SYMBOLS_AND_PUNCTUATION"); + if (rIn[vcl::UnicodeCoverage::HIRAGANA]) + SAL_INFO("svtools", "HIRAGANA"); + if (rIn[vcl::UnicodeCoverage::KATAKANA]) + SAL_INFO("svtools", "KATAKANA"); + if (rIn[vcl::UnicodeCoverage::BOPOMOFO]) + SAL_INFO("svtools", "BOPOMOFO"); + if (rIn[vcl::UnicodeCoverage::HANGUL_COMPATIBILITY_JAMO]) + SAL_INFO("svtools", "HANGUL_COMPATIBILITY_JAMO"); + if (rIn[vcl::UnicodeCoverage::PHAGS_PA]) + SAL_INFO("svtools", "PHAGS_PA"); + if (rIn[vcl::UnicodeCoverage::ENCLOSED_CJK_LETTERS_AND_MONTHS]) + SAL_INFO("svtools", "ENCLOSED_CJK_LETTERS_AND_MONTHS"); + if (rIn[vcl::UnicodeCoverage::CJK_COMPATIBILITY]) + SAL_INFO("svtools", "CJK_COMPATIBILITY"); + if (rIn[vcl::UnicodeCoverage::HANGUL_SYLLABLES]) + SAL_INFO("svtools", "HANGUL_SYLLABLES"); + if (rIn[vcl::UnicodeCoverage::NONPLANE_0]) + SAL_INFO("svtools", "NONPLANE_0"); + if (rIn[vcl::UnicodeCoverage::PHOENICIAN]) + SAL_INFO("svtools", "PHOENICIAN"); + if (rIn[vcl::UnicodeCoverage::CJK_UNIFIED_IDEOGRAPHS]) + SAL_INFO("svtools", "CJK_UNIFIED_IDEOGRAPHS"); + if (rIn[vcl::UnicodeCoverage::PRIVATE_USE_AREA_PLANE_0]) + SAL_INFO("svtools", "PRIVATE_USE_AREA_PLANE_0"); + if (rIn[vcl::UnicodeCoverage::CJK_STROKES]) + SAL_INFO("svtools", "CJK_STROKES"); + if (rIn[vcl::UnicodeCoverage::ALPHABETIC_PRESENTATION_FORMS]) + SAL_INFO("svtools", "ALPHABETIC_PRESENTATION_FORMS"); + if (rIn[vcl::UnicodeCoverage::ARABIC_PRESENTATION_FORMS_A]) + SAL_INFO("svtools", "ARABIC_PRESENTATION_FORMS_A"); + if (rIn[vcl::UnicodeCoverage::COMBINING_HALF_MARKS]) + SAL_INFO("svtools", "COMBINING_HALF_MARKS"); + if (rIn[vcl::UnicodeCoverage::VERTICAL_FORMS]) + SAL_INFO("svtools", "VERTICAL_FORMS"); + if (rIn[vcl::UnicodeCoverage::SMALL_FORM_VARIANTS]) + SAL_INFO("svtools", "SMALL_FORM_VARIANTS"); + if (rIn[vcl::UnicodeCoverage::ARABIC_PRESENTATION_FORMS_B]) + SAL_INFO("svtools", "ARABIC_PRESENTATION_FORMS_B"); + if (rIn[vcl::UnicodeCoverage::HALFWIDTH_AND_FULLWIDTH_FORMS]) + SAL_INFO("svtools", "HALFWIDTH_AND_FULLWIDTH_FORMS"); + if (rIn[vcl::UnicodeCoverage::SPECIALS]) + SAL_INFO("svtools", "SPECIALS"); + if (rIn[vcl::UnicodeCoverage::TIBETAN]) + SAL_INFO("svtools", "TIBETAN"); + if (rIn[vcl::UnicodeCoverage::SYRIAC]) + SAL_INFO("svtools", "SYRIAC"); + if (rIn[vcl::UnicodeCoverage::THAANA]) + SAL_INFO("svtools", "THAANA"); + if (rIn[vcl::UnicodeCoverage::SINHALA]) + SAL_INFO("svtools", "SINHALA"); + if (rIn[vcl::UnicodeCoverage::MYANMAR]) + SAL_INFO("svtools", "MYANMAR"); + if (rIn[vcl::UnicodeCoverage::ETHIOPIC]) + SAL_INFO("svtools", "ETHIOPIC"); + if (rIn[vcl::UnicodeCoverage::CHEROKEE]) + SAL_INFO("svtools", "CHEROKEE"); + if (rIn[vcl::UnicodeCoverage::UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS]) + SAL_INFO("svtools", "UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS"); + if (rIn[vcl::UnicodeCoverage::OGHAM]) + SAL_INFO("svtools", "OGHAM"); + if (rIn[vcl::UnicodeCoverage::RUNIC]) + SAL_INFO("svtools", "RUNIC"); + if (rIn[vcl::UnicodeCoverage::KHMER]) + SAL_INFO("svtools", "KHMER"); + if (rIn[vcl::UnicodeCoverage::MONGOLIAN]) + SAL_INFO("svtools", "MONGOLIAN"); + if (rIn[vcl::UnicodeCoverage::BRAILLE_PATTERNS]) + SAL_INFO("svtools", "BRAILLE_PATTERNS"); + if (rIn[vcl::UnicodeCoverage::YI_SYLLABLES]) + SAL_INFO("svtools", "YI_SYLLABLES"); + if (rIn[vcl::UnicodeCoverage::TAGALOG]) + SAL_INFO("svtools", "TAGALOG"); + if (rIn[vcl::UnicodeCoverage::OLD_ITALIC]) + SAL_INFO("svtools", "OLD_ITALIC"); + if (rIn[vcl::UnicodeCoverage::GOTHIC]) + SAL_INFO("svtools", "GOTHIC"); + if (rIn[vcl::UnicodeCoverage::DESERET]) + SAL_INFO("svtools", "DESERET"); + if (rIn[vcl::UnicodeCoverage::BYZANTINE_MUSICAL_SYMBOLS]) + SAL_INFO("svtools", "BYZANTINE_MUSICAL_SYMBOLS"); + if (rIn[vcl::UnicodeCoverage::MATHEMATICAL_ALPHANUMERIC_SYMBOLS]) + SAL_INFO("svtools", "MATHEMATICAL_ALPHANUMERIC_SYMBOLS"); + if (rIn[vcl::UnicodeCoverage::PRIVATE_USE_PLANE_15]) + SAL_INFO("svtools", "PRIVATE_USE_PLANE_15"); + if (rIn[vcl::UnicodeCoverage::VARIATION_SELECTORS]) + SAL_INFO("svtools", "VARIATION_SELECTORS"); + if (rIn[vcl::UnicodeCoverage::TAGS]) + SAL_INFO("svtools", "TAGS"); + if (rIn[vcl::UnicodeCoverage::LIMBU]) + SAL_INFO("svtools", "LIMBU"); + if (rIn[vcl::UnicodeCoverage::TAI_LE]) + SAL_INFO("svtools", "TAI_LE"); + if (rIn[vcl::UnicodeCoverage::NEW_TAI_LUE]) + SAL_INFO("svtools", "NEW_TAI_LUE"); + if (rIn[vcl::UnicodeCoverage::BUGINESE]) + SAL_INFO("svtools", "BUGINESE"); + if (rIn[vcl::UnicodeCoverage::GLAGOLITIC]) + SAL_INFO("svtools", "GLAGOLITIC"); + if (rIn[vcl::UnicodeCoverage::TIFINAGH]) + SAL_INFO("svtools", "TIFINAGH"); + if (rIn[vcl::UnicodeCoverage::YIJING_HEXAGRAM_SYMBOLS]) + SAL_INFO("svtools", "YIJING_HEXAGRAM_SYMBOLS"); + if (rIn[vcl::UnicodeCoverage::SYLOTI_NAGRI]) + SAL_INFO("svtools", "SYLOTI_NAGRI"); + if (rIn[vcl::UnicodeCoverage::LINEAR_B_SYLLABARY]) + SAL_INFO("svtools", "LINEAR_B_SYLLABARY"); + if (rIn[vcl::UnicodeCoverage::ANCIENT_GREEK_NUMBERS]) + SAL_INFO("svtools", "ANCIENT_GREEK_NUMBERS"); + if (rIn[vcl::UnicodeCoverage::UGARITIC]) + SAL_INFO("svtools", "UGARITIC"); + if (rIn[vcl::UnicodeCoverage::OLD_PERSIAN]) + SAL_INFO("svtools", "OLD_PERSIAN"); + if (rIn[vcl::UnicodeCoverage::SHAVIAN]) + SAL_INFO("svtools", "SHAVIAN"); + if (rIn[vcl::UnicodeCoverage::OSMANYA]) + SAL_INFO("svtools", "OSMANYA"); + if (rIn[vcl::UnicodeCoverage::CYPRIOT_SYLLABARY]) + SAL_INFO("svtools", "CYPRIOT_SYLLABARY"); + if (rIn[vcl::UnicodeCoverage::KHAROSHTHI]) + SAL_INFO("svtools", "KHAROSHTHI"); + if (rIn[vcl::UnicodeCoverage::TAI_XUAN_JING_SYMBOLS]) + SAL_INFO("svtools", "TAI_XUAN_JING_SYMBOLS"); + if (rIn[vcl::UnicodeCoverage::CUNEIFORM]) + SAL_INFO("svtools", "CUNEIFORM"); + if (rIn[vcl::UnicodeCoverage::COUNTING_ROD_NUMERALS]) + SAL_INFO("svtools", "COUNTING_ROD_NUMERALS"); + if (rIn[vcl::UnicodeCoverage::SUNDANESE]) + SAL_INFO("svtools", "SUNDANESE"); + if (rIn[vcl::UnicodeCoverage::LEPCHA]) + SAL_INFO("svtools", "LEPCHA"); + if (rIn[vcl::UnicodeCoverage::OL_CHIKI]) + SAL_INFO("svtools", "OL_CHIKI"); + if (rIn[vcl::UnicodeCoverage::SAURASHTRA]) + SAL_INFO("svtools", "SAURASHTRA"); + if (rIn[vcl::UnicodeCoverage::KAYAH_LI]) + SAL_INFO("svtools", "KAYAH_LI"); + if (rIn[vcl::UnicodeCoverage::REJANG]) + SAL_INFO("svtools", "REJANG"); + if (rIn[vcl::UnicodeCoverage::CHAM]) + SAL_INFO("svtools", "CHAM"); + if (rIn[vcl::UnicodeCoverage::ANCIENT_SYMBOLS]) + SAL_INFO("svtools", "ANCIENT_SYMBOLS"); + if (rIn[vcl::UnicodeCoverage::PHAISTOS_DISC]) + SAL_INFO("svtools", "PHAISTOS_DISC"); + if (rIn[vcl::UnicodeCoverage::CARIAN]) + SAL_INFO("svtools", "CARIAN"); + if (rIn[vcl::UnicodeCoverage::DOMINO_TILES]) + SAL_INFO("svtools", "DOMINO_TILES"); + if (rIn[vcl::UnicodeCoverage::RESERVED1]) + SAL_INFO("svtools", "RESERVED1"); + if (rIn[vcl::UnicodeCoverage::RESERVED2]) + SAL_INFO("svtools", "RESERVED2"); + if (rIn[vcl::UnicodeCoverage::RESERVED3]) + SAL_INFO("svtools", "RESERVED3"); + if (rIn[vcl::UnicodeCoverage::RESERVED4]) + SAL_INFO("svtools", "RESERVED4"); + if (!(rIn[vcl::UnicodeCoverage::RESERVED5])) + return; + + SAL_INFO("svtools", "RESERVED5"); + } + + void lcl_dump_codepage_coverage(const std::optional<std::bitset<vcl::CodePageCoverage::MAX_CP_ENUM>> &roIn) + { + if (!roIn) + { + SAL_INFO("svtools", "<NOTHING>"); + return; + } + auto & rIn(*roIn); + if (rIn.none()) + { + SAL_INFO("svtools", "<NONE>"); + return; + } + if (rIn[vcl::CodePageCoverage::CP1252]) + SAL_INFO("svtools", "CP1252"); + if (rIn[vcl::CodePageCoverage::CP1250]) + SAL_INFO("svtools", "CP1250"); + if (rIn[vcl::CodePageCoverage::CP1251]) + SAL_INFO("svtools", "CP1251"); + if (rIn[vcl::CodePageCoverage::CP1253]) + SAL_INFO("svtools", "CP1253"); + if (rIn[vcl::CodePageCoverage::CP1254]) + SAL_INFO("svtools", "CP1254"); + if (rIn[vcl::CodePageCoverage::CP1255]) + SAL_INFO("svtools", "CP1255"); + if (rIn[vcl::CodePageCoverage::CP1256]) + SAL_INFO("svtools", "CP1256"); + if (rIn[vcl::CodePageCoverage::CP1257]) + SAL_INFO("svtools", "CP1257"); + if (rIn[vcl::CodePageCoverage::CP1258]) + SAL_INFO("svtools", "CP1258"); + if (rIn[vcl::CodePageCoverage::CP874]) + SAL_INFO("svtools", "CP874"); + if (rIn[vcl::CodePageCoverage::CP932]) + SAL_INFO("svtools", "CP932"); + if (rIn[vcl::CodePageCoverage::CP936]) + SAL_INFO("svtools", "CP936"); + if (rIn[vcl::CodePageCoverage::CP949]) + SAL_INFO("svtools", "CP949"); + if (rIn[vcl::CodePageCoverage::CP950]) + SAL_INFO("svtools", "CP950"); + if (rIn[vcl::CodePageCoverage::CP1361]) + SAL_INFO("svtools", "CP1361"); + if (rIn[vcl::CodePageCoverage::CP869]) + SAL_INFO("svtools", "CP869"); + if (rIn[vcl::CodePageCoverage::CP866]) + SAL_INFO("svtools", "CP866"); + if (rIn[vcl::CodePageCoverage::CP865]) + SAL_INFO("svtools", "CP865"); + if (rIn[vcl::CodePageCoverage::CP864]) + SAL_INFO("svtools", "CP864"); + if (rIn[vcl::CodePageCoverage::CP863]) + SAL_INFO("svtools", "CP863"); + if (rIn[vcl::CodePageCoverage::CP862]) + SAL_INFO("svtools", "CP862"); + if (rIn[vcl::CodePageCoverage::CP861]) + SAL_INFO("svtools", "CP861"); + if (rIn[vcl::CodePageCoverage::CP860]) + SAL_INFO("svtools", "CP860"); + if (rIn[vcl::CodePageCoverage::CP857]) + SAL_INFO("svtools", "CP857"); + if (rIn[vcl::CodePageCoverage::CP855]) + SAL_INFO("svtools", "CP855"); + if (rIn[vcl::CodePageCoverage::CP852]) + SAL_INFO("svtools", "CP852"); + if (rIn[vcl::CodePageCoverage::CP775]) + SAL_INFO("svtools", "CP775"); + if (rIn[vcl::CodePageCoverage::CP737]) + SAL_INFO("svtools", "CP737"); + if (rIn[vcl::CodePageCoverage::CP780]) + SAL_INFO("svtools", "CP780"); + if (rIn[vcl::CodePageCoverage::CP850]) + SAL_INFO("svtools", "CP850"); + if (!(rIn[vcl::CodePageCoverage::CP437])) + return; + + SAL_INFO("svtools", "CP437"); + } +#endif + + std::bitset<vcl::UnicodeCoverage::MAX_UC_ENUM> getMaskByScriptType(sal_Int16 nScriptType) + { + std::bitset<vcl::UnicodeCoverage::MAX_UC_ENUM> aMask; + aMask.set(); + + for (size_t i = 0; i < vcl::UnicodeCoverage::MAX_UC_ENUM; ++i) + { + using vcl::UnicodeCoverage::UnicodeCoverageEnum; + UScriptCode eScriptCode = otCoverageToScript(static_cast<UnicodeCoverageEnum>(i)); + if (unicode::getScriptClassFromUScriptCode(eScriptCode) == nScriptType) + aMask.set(i, false); + } + + return aMask; + } + + //false for all bits considered "Latin" by LibreOffice + std::bitset<vcl::UnicodeCoverage::MAX_UC_ENUM> const & getLatinMask() + { + static std::bitset<vcl::UnicodeCoverage::MAX_UC_ENUM> s_Mask(getMaskByScriptType(css::i18n::ScriptType::LATIN)); + return s_Mask; + } + + //false for all bits considered "Asian" by LibreOffice + std::bitset<vcl::UnicodeCoverage::MAX_UC_ENUM> const & getCJKMask() + { + static std::bitset<vcl::UnicodeCoverage::MAX_UC_ENUM> s_Mask(getMaskByScriptType(css::i18n::ScriptType::ASIAN)); + return s_Mask; + } + + //false for all bits considered "Complex" by LibreOffice + std::bitset<vcl::UnicodeCoverage::MAX_UC_ENUM> const & getCTLMask() + { + static std::bitset<vcl::UnicodeCoverage::MAX_UC_ENUM> s_Mask(getMaskByScriptType(css::i18n::ScriptType::COMPLEX)); + return s_Mask; + } + + //false for all bits considered "WEAK" by LibreOffice + std::bitset<vcl::UnicodeCoverage::MAX_UC_ENUM> const & getWeakMask() + { + static std::bitset<vcl::UnicodeCoverage::MAX_UC_ENUM> s_Mask(getMaskByScriptType(css::i18n::ScriptType::WEAK)); + return s_Mask; + } + + //Nearly every font supports some basic Latin + std::bitset<vcl::UnicodeCoverage::MAX_UC_ENUM> getCommonLatnSubsetMask() + { + std::bitset<vcl::UnicodeCoverage::MAX_UC_ENUM> aMask; + aMask.set(); + aMask.set(vcl::UnicodeCoverage::BASIC_LATIN, false); + aMask.set(vcl::UnicodeCoverage::LATIN_1_SUPPLEMENT, false); + aMask.set(vcl::UnicodeCoverage::LATIN_EXTENDED_A, false); + aMask.set(vcl::UnicodeCoverage::LATIN_EXTENDED_B, false); + aMask.set(vcl::UnicodeCoverage::LATIN_EXTENDED_ADDITIONAL, false); + return aMask; + } + + template<size_t N> + size_t find_first(std::bitset<N> const& rSet) + { + for (size_t i = 0; i < N; ++i) + { + if (rSet.test(i)) + return i; + } + assert(false); // see current usage + return N; + } + + UScriptCode getScript(const vcl::FontCapabilities &rFontCapabilities) + { + using vcl::UnicodeCoverage::UnicodeCoverageEnum; + + std::bitset<vcl::UnicodeCoverage::MAX_UC_ENUM> aMasked; + if (rFontCapabilities.oUnicodeRange) + { + aMasked = *rFontCapabilities.oUnicodeRange & getWeakMask(); + } + + if (aMasked.count() == 1) + return otCoverageToScript(static_cast<UnicodeCoverageEnum>(find_first(aMasked))); + + if (aMasked[vcl::UnicodeCoverage::ARABIC]) + { + aMasked.set(vcl::UnicodeCoverage::ARABIC_PRESENTATION_FORMS_A, false); + aMasked.set(vcl::UnicodeCoverage::ARABIC_PRESENTATION_FORMS_B, false); + aMasked.set(vcl::UnicodeCoverage::NKO, false); + //Probably strongly tuned for Arabic + if (aMasked.count() == 1) + return USCRIPT_ARABIC; + if (aMasked.count() == 2 && aMasked[vcl::UnicodeCoverage::SYRIAC]) + return USCRIPT_SYRIAC; + } + + if (aMasked[vcl::UnicodeCoverage::DEVANAGARI]) + { + aMasked.set(vcl::UnicodeCoverage::DEVANAGARI, false); + //Probably strongly tuned for a single Indic script + if (aMasked.count() == 1) + return otCoverageToScript(static_cast<UnicodeCoverageEnum>(find_first(aMasked))); + } + + aMasked.set(vcl::UnicodeCoverage::GREEK_EXTENDED, false); + aMasked.set(vcl::UnicodeCoverage::GREEK_AND_COPTIC, false); + // tdf#88484 + // Some fonts set the Arabic Presentation Forms-B bit because they + // support U+FEFF (Zero Width Space) which happens to be in that block + // but it isn’t an Arabic code point. By the time we reach here we + // decided this isn’t an Arabic font, so it should be safe. + aMasked.set(vcl::UnicodeCoverage::ARABIC_PRESENTATION_FORMS_B, false); + if (aMasked.count() == 1) + return otCoverageToScript(static_cast<UnicodeCoverageEnum>(find_first(aMasked))); + + if (aMasked[vcl::UnicodeCoverage::CYRILLIC]) + { + //Probably strongly tuned for Georgian + if (aMasked.count() == 2 && aMasked[vcl::UnicodeCoverage::GEORGIAN]) + return USCRIPT_GEORGIAN; + } + + aMasked &= getCJKMask(); + + aMasked.set(vcl::UnicodeCoverage::CYRILLIC, false); + aMasked.set(vcl::UnicodeCoverage::THAI, false); + aMasked.set(vcl::UnicodeCoverage::DESERET, false); + aMasked.set(vcl::UnicodeCoverage::PHAGS_PA, false); + + //So, possibly a CJK font + if (!aMasked.count() && rFontCapabilities.oCodePageRange) + { + std::bitset<vcl::CodePageCoverage::MAX_CP_ENUM> aCJKCodePageMask; + aCJKCodePageMask.set(vcl::CodePageCoverage::CP932); + aCJKCodePageMask.set(vcl::CodePageCoverage::CP936); + aCJKCodePageMask.set(vcl::CodePageCoverage::CP949); + aCJKCodePageMask.set(vcl::CodePageCoverage::CP950); + aCJKCodePageMask.set(vcl::CodePageCoverage::CP1361); + std::bitset<vcl::CodePageCoverage::MAX_CP_ENUM> aMaskedCodePage = + *rFontCapabilities.oCodePageRange & aCJKCodePageMask; + //fold Korean + if (aMaskedCodePage[vcl::CodePageCoverage::CP1361]) + { + aMaskedCodePage.set(vcl::CodePageCoverage::CP949); + aMaskedCodePage.set(vcl::CodePageCoverage::CP1361, false); + } + + if (aMaskedCodePage.count() == 1) + { + if (aMaskedCodePage[vcl::CodePageCoverage::CP932]) + return USCRIPT_JAPANESE; + if (aMaskedCodePage[vcl::CodePageCoverage::CP949]) + return USCRIPT_KOREAN; + if (aMaskedCodePage[vcl::CodePageCoverage::CP936]) + return USCRIPT_SIMPLIFIED_HAN; + if (aMaskedCodePage[vcl::CodePageCoverage::CP950]) + return USCRIPT_TRADITIONAL_HAN; + } + + if (aMaskedCodePage.count()) + return USCRIPT_HAN; + } + + return USCRIPT_COMMON; + } +} + +const std::map<UScriptCode, std::vector<OUString>> distCjkMap = +{ + { USCRIPT_KOREAN, { " KR", "Korean"} }, // Korean + { USCRIPT_JAPANESE, {" JP", "Japanese"} } , // Japanese + { USCRIPT_SIMPLIFIED_HAN, {" SC", " GB", "S Chinese"} }, // Simplified Chinese Family + { USCRIPT_TRADITIONAL_HAN, {" TC", " HC", " TW", " HK", " MO", "T Chinese"} }// Traditional Chinese Family +}; +namespace +{ + UScriptCode attemptToDisambiguateHan(UScriptCode eScript, OutputDevice const &rDevice) + { + //If we're a CJK font, see if we seem to be tuned for C, J or K + if (eScript == USCRIPT_HAN) + { + const vcl::Font &rFont = rDevice.GetFont(); + + bool bKore = false, bJpan = false, bHant = false, bHans = false; + + static constexpr OUStringLiteral sKorean = u"\u4E6D\u4E76\u596C"; + if (-1 == rDevice.HasGlyphs(rFont, sKorean)) + bKore = true; + + static constexpr OUStringLiteral sJapanese = u"\u5968\u67A0\u9D8F"; + if (-1 == rDevice.HasGlyphs(rFont, sJapanese)) + bJpan = true; + + static constexpr OUStringLiteral sTraditionalChinese = u"\u555F\u96DE"; + if (-1 == rDevice.HasGlyphs(rFont, sTraditionalChinese)) + bHant = true; + + static constexpr OUStringLiteral sSimplifiedChinese = u"\u4E61\u542F\u5956"; + if (-1 == rDevice.HasGlyphs(rFont, sSimplifiedChinese)) + bHans = true; + + if (bKore && !bJpan && !bHans && !bHant) { + eScript = USCRIPT_KOREAN; + return eScript; + } + else if (bJpan && !bKore && !bHans && !bHant) { + eScript = USCRIPT_JAPANESE; + return eScript; + } + else if (bHans && !bHant && !bKore && !bJpan) { + eScript = USCRIPT_SIMPLIFIED_HAN; + return eScript; + } + else if (bHant && !bHans && !bKore && !bJpan) { + eScript = USCRIPT_TRADITIONAL_HAN; + return eScript; + } + + // for the last time, Check the ISO code strings or font specific strings + const OUString &rName = rDevice.GetFont().GetFamilyName(); + std::map<UScriptCode, std::vector<OUString>>::const_iterator distCjkMapIt; + for (distCjkMapIt = distCjkMap.begin(); distCjkMapIt != distCjkMap.end(); ++distCjkMapIt) { + std::vector<OUString> cjkCodeList = distCjkMapIt->second; + std::vector<OUString>::const_iterator cjkPtr; + for (cjkPtr = cjkCodeList.begin(); cjkPtr != cjkCodeList.end(); ++cjkPtr) { + if (rName.indexOf(*cjkPtr) > 0) { + return distCjkMapIt->first; + } + } + } + //otherwise fall-through as USCRIPT_HAN and expect a combined Hant/Hans preview + } + return eScript; + } +} + +OUString makeShortRepresentativeTextForSelectedFont(OutputDevice const &rDevice) +{ + UScriptCode eScript = lcl_getHardCodedScriptNameForFont(rDevice); + if (eScript == USCRIPT_INVALID_CODE) + { + vcl::FontCapabilities aFontCapabilities; + if (!rDevice.GetFontCapabilities(aFontCapabilities)) + return OUString(); + +#if OSL_DEBUG_LEVEL > 0 + lcl_dump_unicode_coverage(aFontCapabilities.oUnicodeRange); + lcl_dump_codepage_coverage(aFontCapabilities.oCodePageRange); +#endif + + if (aFontCapabilities.oUnicodeRange) + *aFontCapabilities.oUnicodeRange &= getCommonLatnSubsetMask(); + + //If this font is probably tuned to display a single non-Latin + //script and the font name is itself in Latin, then show a small + //chunk of representative text for that script + eScript = getScript(aFontCapabilities); + if (eScript == USCRIPT_COMMON) + return OUString(); + + eScript = attemptToDisambiguateHan(eScript, rDevice); + } + + OUString sSampleText = makeShortRepresentativeTextForScript(eScript); + bool bHasSampleTextGlyphs = (-1 == rDevice.HasGlyphs(rDevice.GetFont(), sSampleText)); + return bHasSampleTextGlyphs ? sSampleText : OUString(); +} + +UScriptCode otCoverageToScript(vcl::UnicodeCoverage::UnicodeCoverageEnum eOTCoverage) +{ + UScriptCode eRet = USCRIPT_COMMON; + switch (eOTCoverage) + { + case vcl::UnicodeCoverage::BASIC_LATIN: + case vcl::UnicodeCoverage::LATIN_1_SUPPLEMENT: + case vcl::UnicodeCoverage::LATIN_EXTENDED_A: + case vcl::UnicodeCoverage::LATIN_EXTENDED_B: + eRet = USCRIPT_LATIN; + break; + case vcl::UnicodeCoverage::COMBINING_DIACRITICAL_MARKS: + eRet = USCRIPT_INHERITED; + break; + case vcl::UnicodeCoverage::GREEK_AND_COPTIC: + eRet = USCRIPT_GREEK; + break; + case vcl::UnicodeCoverage::COPTIC: + eRet = USCRIPT_COPTIC; + break; + case vcl::UnicodeCoverage::CYRILLIC: + eRet = USCRIPT_CYRILLIC; + break; + case vcl::UnicodeCoverage::ARMENIAN: + eRet = USCRIPT_ARMENIAN; + break; + case vcl::UnicodeCoverage::HEBREW: + eRet = USCRIPT_HEBREW; + break; + case vcl::UnicodeCoverage::VAI: + eRet = USCRIPT_VAI; + break; + case vcl::UnicodeCoverage::ARABIC: + eRet = USCRIPT_ARABIC; + break; + case vcl::UnicodeCoverage::NKO: + eRet = USCRIPT_NKO; + break; + case vcl::UnicodeCoverage::DEVANAGARI: + eRet = USCRIPT_DEVANAGARI; + break; + case vcl::UnicodeCoverage::BENGALI: + eRet = USCRIPT_BENGALI; + break; + case vcl::UnicodeCoverage::GURMUKHI: + eRet = USCRIPT_GURMUKHI; + break; + case vcl::UnicodeCoverage::GUJARATI: + eRet = USCRIPT_GUJARATI; + break; + case vcl::UnicodeCoverage::ODIA: + eRet = USCRIPT_ORIYA; + break; + case vcl::UnicodeCoverage::TAMIL: + eRet = USCRIPT_TAMIL; + break; + case vcl::UnicodeCoverage::TELUGU: + eRet = USCRIPT_TELUGU; + break; + case vcl::UnicodeCoverage::KANNADA: + eRet = USCRIPT_KANNADA; + break; + case vcl::UnicodeCoverage::MALAYALAM: + eRet = USCRIPT_MALAYALAM; + break; + case vcl::UnicodeCoverage::THAI: + eRet = USCRIPT_THAI; + break; + case vcl::UnicodeCoverage::LAO: + eRet = USCRIPT_LAO; + break; + case vcl::UnicodeCoverage::GEORGIAN: + eRet = USCRIPT_GEORGIAN; + break; + case vcl::UnicodeCoverage::BALINESE: + eRet = USCRIPT_BALINESE; + break; + case vcl::UnicodeCoverage::HANGUL_JAMO: + eRet = USCRIPT_HANGUL; + break; + case vcl::UnicodeCoverage::LATIN_EXTENDED_ADDITIONAL: + eRet = USCRIPT_LATIN; + break; + case vcl::UnicodeCoverage::GREEK_EXTENDED: + eRet = USCRIPT_GREEK; + break; + case vcl::UnicodeCoverage::CURRENCY_SYMBOLS: + eRet = USCRIPT_SYMBOLS; + break; + case vcl::UnicodeCoverage::COMBINING_DIACRITICAL_MARKS_FOR_SYMBOLS: + eRet = USCRIPT_INHERITED; + break; + case vcl::UnicodeCoverage::LETTERLIKE_SYMBOLS: + case vcl::UnicodeCoverage::NUMBER_FORMS: + case vcl::UnicodeCoverage::ARROWS: + eRet = USCRIPT_SYMBOLS; + break; + case vcl::UnicodeCoverage::MATHEMATICAL_OPERATORS: + eRet = USCRIPT_MATHEMATICAL_NOTATION; + break; + case vcl::UnicodeCoverage::MISCELLANEOUS_TECHNICAL: + case vcl::UnicodeCoverage::OPTICAL_CHARACTER_RECOGNITION: + case vcl::UnicodeCoverage::BOX_DRAWING: + case vcl::UnicodeCoverage::BLOCK_ELEMENTS: + case vcl::UnicodeCoverage::GEOMETRIC_SHAPES: + case vcl::UnicodeCoverage::MISCELLANEOUS_SYMBOLS: + case vcl::UnicodeCoverage::DINGBATS: + case vcl::UnicodeCoverage::CJK_SYMBOLS_AND_PUNCTUATION: + eRet = USCRIPT_SYMBOLS; + break; + case vcl::UnicodeCoverage::HIRAGANA: + eRet = USCRIPT_HIRAGANA; + break; + case vcl::UnicodeCoverage::KATAKANA: + eRet = USCRIPT_KATAKANA; + break; + case vcl::UnicodeCoverage::BOPOMOFO: + eRet = USCRIPT_BOPOMOFO; + break; + case vcl::UnicodeCoverage::HANGUL_COMPATIBILITY_JAMO: + eRet = USCRIPT_HANGUL; + break; + case vcl::UnicodeCoverage::PHAGS_PA: + eRet = USCRIPT_PHAGS_PA; + break; + case vcl::UnicodeCoverage::ENCLOSED_CJK_LETTERS_AND_MONTHS: + eRet = USCRIPT_HANGUL; + break; + case vcl::UnicodeCoverage::CJK_COMPATIBILITY: + eRet = USCRIPT_HAN; + break; + case vcl::UnicodeCoverage::HANGUL_SYLLABLES: + eRet = USCRIPT_HANGUL; + break; + case vcl::UnicodeCoverage::PHOENICIAN: + eRet = USCRIPT_PHOENICIAN; + break; + case vcl::UnicodeCoverage::CJK_UNIFIED_IDEOGRAPHS: + case vcl::UnicodeCoverage::CJK_STROKES: + eRet = USCRIPT_HAN; + break; + case vcl::UnicodeCoverage::ARABIC_PRESENTATION_FORMS_A: + eRet = USCRIPT_ARABIC; + break; + case vcl::UnicodeCoverage::COMBINING_HALF_MARKS: + eRet = USCRIPT_INHERITED; + break; + case vcl::UnicodeCoverage::ARABIC_PRESENTATION_FORMS_B: + eRet = USCRIPT_ARABIC; + break; + case vcl::UnicodeCoverage::TIBETAN: + eRet = USCRIPT_TIBETAN; + break; + case vcl::UnicodeCoverage::SYRIAC: + eRet = USCRIPT_SYRIAC; + break; + case vcl::UnicodeCoverage::THAANA: + eRet = USCRIPT_THAANA; + break; + case vcl::UnicodeCoverage::SINHALA: + eRet = USCRIPT_SINHALA; + break; + case vcl::UnicodeCoverage::MYANMAR: + eRet = USCRIPT_MYANMAR; + break; + case vcl::UnicodeCoverage::ETHIOPIC: + eRet = USCRIPT_ETHIOPIC; + break; + case vcl::UnicodeCoverage::CHEROKEE: + eRet = USCRIPT_CHEROKEE; + break; + case vcl::UnicodeCoverage::UNIFIED_CANADIAN_ABORIGINAL_SYLLABICS: + eRet = USCRIPT_CANADIAN_ABORIGINAL; + break; + case vcl::UnicodeCoverage::OGHAM: + eRet = USCRIPT_OGHAM; + break; + case vcl::UnicodeCoverage::RUNIC: + eRet = USCRIPT_RUNIC; + break; + case vcl::UnicodeCoverage::KHMER: + eRet = USCRIPT_KHMER; + break; + case vcl::UnicodeCoverage::MONGOLIAN: + eRet = USCRIPT_MONGOLIAN; + break; + case vcl::UnicodeCoverage::BRAILLE_PATTERNS: + eRet = USCRIPT_BRAILLE; + break; + case vcl::UnicodeCoverage::YI_SYLLABLES: + eRet = USCRIPT_YI; + break; + case vcl::UnicodeCoverage::TAGALOG: + eRet = USCRIPT_TAGALOG; + break; + case vcl::UnicodeCoverage::OLD_ITALIC: + eRet = USCRIPT_OLD_ITALIC; + break; + case vcl::UnicodeCoverage::GOTHIC: + eRet = USCRIPT_GOTHIC; + break; + case vcl::UnicodeCoverage::DESERET: + eRet = USCRIPT_DESERET; + break; + case vcl::UnicodeCoverage::BYZANTINE_MUSICAL_SYMBOLS: + case vcl::UnicodeCoverage::MATHEMATICAL_ALPHANUMERIC_SYMBOLS: + case vcl::UnicodeCoverage::PRIVATE_USE_PLANE_15: + eRet = USCRIPT_SYMBOLS; + break; + case vcl::UnicodeCoverage::VARIATION_SELECTORS: + eRet = USCRIPT_INHERITED; + break; + case vcl::UnicodeCoverage::TAGS: + eRet = USCRIPT_SYMBOLS; + break; + case vcl::UnicodeCoverage::LIMBU: + eRet = USCRIPT_LIMBU; + break; + case vcl::UnicodeCoverage::TAI_LE: + eRet = USCRIPT_TAI_LE; + break; + case vcl::UnicodeCoverage::NEW_TAI_LUE: + eRet = USCRIPT_NEW_TAI_LUE; + break; + case vcl::UnicodeCoverage::BUGINESE: + eRet = USCRIPT_BUGINESE; + break; + case vcl::UnicodeCoverage::GLAGOLITIC: + eRet = USCRIPT_GLAGOLITIC; + break; + case vcl::UnicodeCoverage::TIFINAGH: + eRet = USCRIPT_TIFINAGH; + break; + case vcl::UnicodeCoverage::YIJING_HEXAGRAM_SYMBOLS: + eRet = USCRIPT_SYMBOLS; + break; + case vcl::UnicodeCoverage::SYLOTI_NAGRI: + eRet = USCRIPT_SYLOTI_NAGRI; + break; + case vcl::UnicodeCoverage::LINEAR_B_SYLLABARY: + eRet = USCRIPT_LINEAR_B; + break; + case vcl::UnicodeCoverage::ANCIENT_GREEK_NUMBERS: + eRet = USCRIPT_GREEK; + break; + case vcl::UnicodeCoverage::UGARITIC: + eRet = USCRIPT_UGARITIC; + break; + case vcl::UnicodeCoverage::OLD_PERSIAN: + eRet = USCRIPT_OLD_PERSIAN; + break; + case vcl::UnicodeCoverage::SHAVIAN: + eRet = USCRIPT_SHAVIAN; + break; + case vcl::UnicodeCoverage::OSMANYA: + eRet = USCRIPT_OSMANYA; + break; + case vcl::UnicodeCoverage::CYPRIOT_SYLLABARY: + eRet = USCRIPT_CYPRIOT; + break; + case vcl::UnicodeCoverage::KHAROSHTHI: + eRet = USCRIPT_KHAROSHTHI; + break; + case vcl::UnicodeCoverage::CUNEIFORM: + eRet = USCRIPT_CUNEIFORM; + break; + case vcl::UnicodeCoverage::SUNDANESE: + eRet = USCRIPT_SUNDANESE; + break; + case vcl::UnicodeCoverage::LEPCHA: + eRet = USCRIPT_LEPCHA; + break; + case vcl::UnicodeCoverage::OL_CHIKI: + eRet = USCRIPT_OL_CHIKI; + break; + case vcl::UnicodeCoverage::SAURASHTRA: + eRet = USCRIPT_SAURASHTRA; + break; + case vcl::UnicodeCoverage::KAYAH_LI: + eRet = USCRIPT_KAYAH_LI; + break; + case vcl::UnicodeCoverage::REJANG: + eRet = USCRIPT_REJANG; + break; + case vcl::UnicodeCoverage::CHAM: + eRet = USCRIPT_CHAM; + break; + case vcl::UnicodeCoverage::CARIAN: + eRet = USCRIPT_CARIAN; + break; + case vcl::UnicodeCoverage::DOMINO_TILES: + case vcl::UnicodeCoverage::TAI_XUAN_JING_SYMBOLS: + case vcl::UnicodeCoverage::COUNTING_ROD_NUMERALS: + case vcl::UnicodeCoverage::ANCIENT_SYMBOLS: + case vcl::UnicodeCoverage::PHAISTOS_DISC: + eRet = USCRIPT_SYMBOLS; + break; + case vcl::UnicodeCoverage::IPA_EXTENSIONS: + case vcl::UnicodeCoverage::SPECIALS: + case vcl::UnicodeCoverage::HALFWIDTH_AND_FULLWIDTH_FORMS: + case vcl::UnicodeCoverage::VERTICAL_FORMS: + case vcl::UnicodeCoverage::SMALL_FORM_VARIANTS: + case vcl::UnicodeCoverage::ALPHABETIC_PRESENTATION_FORMS: + case vcl::UnicodeCoverage::PRIVATE_USE_AREA_PLANE_0: + case vcl::UnicodeCoverage::NONPLANE_0: + case vcl::UnicodeCoverage::ENCLOSED_ALPHANUMERICS: + case vcl::UnicodeCoverage::CONTROL_PICTURES: + case vcl::UnicodeCoverage::SUPERSCRIPTS_AND_SUBSCRIPTS: + case vcl::UnicodeCoverage::GENERAL_PUNCTUATION: + case vcl::UnicodeCoverage::SPACING_MODIFIER_LETTERS: + case vcl::UnicodeCoverage::RESERVED1: + case vcl::UnicodeCoverage::RESERVED2: + case vcl::UnicodeCoverage::RESERVED3: + case vcl::UnicodeCoverage::RESERVED4: + case vcl::UnicodeCoverage::RESERVED5: + case vcl::UnicodeCoverage::MAX_UC_ENUM: + break; + } + return eRet; +} + +OUString makeRepresentativeTextForFont(sal_Int16 nScriptType, const vcl::Font &rFont) +{ + OUString sRet(makeRepresentativeTextForLanguage(rFont.GetLanguage())); + + ScopedVclPtrInstance< VirtualDevice > aDevice; + if (sRet.isEmpty() || (-1 != aDevice->HasGlyphs(rFont, sRet))) + { + aDevice->SetFont(rFont); + vcl::FontCapabilities aFontCapabilities; + if (aDevice->GetFontCapabilities(aFontCapabilities)) + { +#if OSL_DEBUG_LEVEL > 0 + lcl_dump_unicode_coverage(aFontCapabilities.oUnicodeRange); +#endif + + if (aFontCapabilities.oUnicodeRange) + { + *aFontCapabilities.oUnicodeRange &= getWeakMask(); + + if (nScriptType != css::i18n::ScriptType::ASIAN) + { + *aFontCapabilities.oUnicodeRange &= getCJKMask(); + aFontCapabilities.oCodePageRange.reset(); + } + if (nScriptType != css::i18n::ScriptType::LATIN) + *aFontCapabilities.oUnicodeRange &= getLatinMask(); + if (nScriptType != css::i18n::ScriptType::COMPLEX) + *aFontCapabilities.oUnicodeRange &= getCTLMask(); + } + +#if OSL_DEBUG_LEVEL > 0 + SAL_INFO("svtools", "minimal"); + lcl_dump_unicode_coverage(aFontCapabilities.oUnicodeRange); + lcl_dump_codepage_coverage(aFontCapabilities.oCodePageRange); +#endif + + UScriptCode eScript = getScript(aFontCapabilities); + + if (nScriptType == css::i18n::ScriptType::ASIAN) + eScript = attemptToDisambiguateHan(eScript, *aDevice); + + sRet = makeRepresentativeTextForScript(eScript); + } + + if (sRet.isEmpty()) + { + if (nScriptType == css::i18n::ScriptType::COMPLEX) + { + sRet = makeRepresentativeTextForScript(USCRIPT_HEBREW); + if (-1 != aDevice->HasGlyphs(rFont, sRet)) + { + sRet = makeMinimalTextForScript(USCRIPT_HEBREW); + if (-1 != aDevice->HasGlyphs(rFont, sRet)) + sRet = makeRepresentativeTextForScript(USCRIPT_ARABIC); + } + } + else if (nScriptType == css::i18n::ScriptType::LATIN) + sRet = makeRepresentativeTextForScript(USCRIPT_LATIN); + } + } + + return sRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/stringtransfer.cxx b/svtools/source/misc/stringtransfer.cxx new file mode 100644 index 0000000000..37470128e5 --- /dev/null +++ b/svtools/source/misc/stringtransfer.cxx @@ -0,0 +1,98 @@ +/* -*- 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 <sal/config.h> + +#include <rtl/ref.hxx> +#include <sot/exchange.hxx> +#include <svtools/stringtransfer.hxx> +#include <utility> + + +namespace svt +{ + + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::datatransfer; + + + //= OStringTransferable + OStringTransferable::OStringTransferable(OUString aContent) + : TransferDataContainer() + , m_sContent(std::move(aContent)) + { + } + + void OStringTransferable::AddSupportedFormats() + { + AddFormat(SotClipboardFormatId::STRING); + } + + void OStringTransferable::SetData(const OUString& rContent) + { + m_sContent = rContent; + ClearFormats(); // invalidate m_aAny so new data will take effect + } + + bool OStringTransferable::GetData( const DataFlavor& _rFlavor, const OUString& /*rDestDoc*/ ) + { + SotClipboardFormatId nFormat = SotExchange::GetFormat( _rFlavor ); + if (SotClipboardFormatId::STRING == nFormat) + return SetString( m_sContent ); + + return false; + } + + //= OStringTransfer + void OStringTransfer::CopyString( const OUString& _rContent, vcl::Window* _pWindow ) + { + rtl::Reference<OStringTransferable> pTransferable = new OStringTransferable( _rContent ); + pTransferable->CopyToClipboard( _pWindow ); + } + + bool OStringTransfer::PasteString( OUString& _rContent, vcl::Window* _pWindow ) + { + TransferableDataHelper aClipboardData = TransferableDataHelper::CreateFromSystemClipboard( _pWindow ); + + // check for a string format + const DataFlavorExVector& rFormats = aClipboardData.GetDataFlavorExVector(); + for (auto const& format : rFormats) + { + if (SotClipboardFormatId::STRING == format.mnSotId) + { + OUString sContent; + bool bSuccess = aClipboardData.GetString( SotClipboardFormatId::STRING, sContent ); + _rContent = sContent; + return bSuccess; + } + } + + return false; + } + + void OStringTransfer::StartStringDrag( const OUString& _rContent, vcl::Window* _pWindow, sal_Int8 _nDragSourceActions ) + { + rtl::Reference<OStringTransferable> pTransferable = new OStringTransferable( _rContent ); + pTransferable->StartDrag(_pWindow, _nDragSourceActions); + } + +} // namespace svt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/svtresid.cxx b/svtools/source/misc/svtresid.cxx new file mode 100644 index 0000000000..5d886a01bf --- /dev/null +++ b/svtools/source/misc/svtresid.cxx @@ -0,0 +1,26 @@ +/* -*- 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 <svtools/svtresid.hxx> + +std::locale SvtResLocale() { return Translate::Create("svt"); } + +OUString SvtResId(TranslateId aId) { return Translate::get(aId, SvtResLocale()); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/templatefoldercache.cxx b/svtools/source/misc/templatefoldercache.cxx new file mode 100644 index 0000000000..d8e473350e --- /dev/null +++ b/svtools/source/misc/templatefoldercache.cxx @@ -0,0 +1,796 @@ +/* -*- 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 <sal/config.h> + +#include <osl/file.hxx> +#include <svtools/templatefoldercache.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/ucb/XDynamicResultSet.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/ucb/CommandAbortedException.hpp> +#include <com/sun/star/ucb/XContentAccess.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/util/theOfficeInstallationDirectories.hpp> +#include <ucbhelper/content.hxx> +#include <osl/diagnose.h> +#include <rtl/ref.hxx> +#include <salhelper/simplereferenceobject.hxx> +#include <tools/time.hxx> +#include <tools/urlobj.hxx> +#include <tools/debug.hxx> +#include <comphelper/diagnose_ex.hxx> +#include <unotools/pathoptions.hxx> + +#include <comphelper/processfactory.hxx> + +#include <mutex> +#include <utility> +#include <vector> +#include <algorithm> + + +namespace svt +{ + + + using namespace ::utl; + using namespace ::com::sun::star; + using namespace ::com::sun::star::sdbc; + using namespace ::com::sun::star::ucb; + using namespace ::com::sun::star::uno; + + + //= helpers + + + static SvStream& WriteDateTime( SvStream& _rStorage, const util::DateTime& _rDate ) + { + sal_uInt16 hundredthSeconds = static_cast< sal_uInt16 >( _rDate.NanoSeconds / tools::Time::nanoPerCenti ); + _rStorage.WriteUInt16( hundredthSeconds ); + + _rStorage.WriteUInt16( _rDate.Seconds ); + _rStorage.WriteUInt16( _rDate.Minutes ); + _rStorage.WriteUInt16( _rDate.Hours ); + _rStorage.WriteUInt16( _rDate.Day ); + _rStorage.WriteUInt16( _rDate.Month ); + _rStorage.WriteInt16( _rDate.Year ); + + return _rStorage; + } + + + static SvStream& operator >> ( SvStream& _rStorage, util::DateTime& _rDate ) + { + sal_uInt16 hundredthSeconds; + _rStorage.ReadUInt16( hundredthSeconds ); + _rDate.NanoSeconds = static_cast< sal_uInt32 >( hundredthSeconds ) * tools::Time::nanoPerCenti; + + _rStorage.ReadUInt16( _rDate.Seconds ); + _rStorage.ReadUInt16( _rDate.Minutes ); + _rStorage.ReadUInt16( _rDate.Hours ); + _rStorage.ReadUInt16( _rDate.Day ); + _rStorage.ReadUInt16( _rDate.Month ); + _rStorage.ReadInt16( _rDate.Year ); + + return _rStorage; + } + + //= TemplateContent + + namespace { + + struct TemplateContent; + + } + + typedef ::std::vector< ::rtl::Reference< TemplateContent > > TemplateFolderContent; + typedef TemplateFolderContent::const_iterator ConstFolderIterator; + typedef TemplateFolderContent::iterator FolderIterator; + + namespace { + + /** a struct describing one content in one of the template dirs (or at least it's relevant aspects) + */ + struct TemplateContent : public ::salhelper::SimpleReferenceObject + { + public: + + private: + INetURLObject m_aURL; + util::DateTime m_aLastModified; // date of last modification as reported by UCP + TemplateFolderContent m_aSubContents; // sorted (by name) list of the children + + private: + void implResetDate( ) + { + m_aLastModified.NanoSeconds = m_aLastModified.Seconds = m_aLastModified.Minutes = m_aLastModified.Hours = 0; + m_aLastModified.Day = m_aLastModified.Month = m_aLastModified.Year = 0; + } + + private: + virtual ~TemplateContent() override; + + public: + explicit TemplateContent( INetURLObject _aURL ); + + // attribute access + OUString getURL( ) const { return m_aURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ); } + void setModDate( const util::DateTime& _rDate ) { m_aLastModified = _rDate; } + const util::DateTime& getModDate( ) const { return m_aLastModified; } + + TemplateFolderContent& getSubContents() { return m_aSubContents; } + const TemplateFolderContent& getSubContents() const { return m_aSubContents; } + + ConstFolderIterator end() const { return m_aSubContents.end(); } + TemplateFolderContent::size_type + size() const { return m_aSubContents.size(); } + + void push_back( const ::rtl::Reference< TemplateContent >& _rxNewElement ) + { m_aSubContents.push_back( _rxNewElement ); } + }; + + } + + TemplateContent::TemplateContent( INetURLObject _aURL ) + :m_aURL(std::move( _aURL )) + { + DBG_ASSERT( INetProtocol::NotValid != m_aURL.GetProtocol(), "TemplateContent::TemplateContent: invalid URL!" ); + implResetDate(); + } + + + TemplateContent::~TemplateContent() + { + } + + + //= stl helpers + + namespace { + + /// compares two TemplateContent by URL + struct TemplateContentURLLess + { + bool operator() ( const ::rtl::Reference< TemplateContent >& _rxLHS, const ::rtl::Reference< TemplateContent >& _rxRHS ) const + { + return _rxLHS->getURL() < _rxRHS->getURL(); + } + }; + + + /// sorts the sib contents of a TemplateFolderContent + struct SubContentSort + { + void operator() ( TemplateFolderContent& _rFolder ) const + { + // sort the directory by name + ::std::sort( + _rFolder.begin(), + _rFolder.end(), + TemplateContentURLLess() + ); + + // sort the sub directories by name + ::std::for_each( + _rFolder.begin(), + _rFolder.end(), + *this + ); + } + + void operator() ( const ::rtl::Reference< TemplateContent >& _rxContent ) const + { + if ( _rxContent.is() && _rxContent->size() ) + { + operator()( _rxContent->getSubContents() ); + } + } + }; + + /** does a deep compare of two template contents + */ + struct TemplateContentEqual + { + + bool operator() (const ::rtl::Reference< TemplateContent >& _rLHS, const ::rtl::Reference< TemplateContent >& _rRHS ) + { + if ( !_rLHS.is() || !_rRHS.is() ) + { + OSL_FAIL( "TemplateContentEqual::operator(): invalid contents!" ); + return true; + // this is not strictly true, in case only one is invalid - but this is a heavy error anyway + } + + if ( _rLHS->getURL() != _rRHS->getURL() ) + return false; + + if ( _rLHS->getModDate() != _rRHS->getModDate() ) + return false; + + if ( _rLHS->getSubContents().size() != _rRHS->getSubContents().size() ) + return false; + + if ( !_rLHS->getSubContents().empty() ) + { // there are children + // -> compare them + ::std::pair< FolderIterator, FolderIterator > aFirstDifferent = ::std::mismatch( + _rLHS->getSubContents().begin(), + _rLHS->getSubContents().end(), + _rRHS->getSubContents().begin(), + *this + ); + if ( aFirstDifferent.first != _rLHS->getSubContents().end() ) + return false;// the sub contents differ + } + + return true; + } + }; + + + /// base class for functors which act on a SvStream + struct StorageHelper + { + protected: + SvStream& m_rStorage; + explicit StorageHelper( SvStream& _rStorage ) : m_rStorage( _rStorage ) { } + }; + + + struct StoreContentURL : public StorageHelper + { + uno::Reference< util::XOfficeInstallationDirectories > m_xOfficeInstDirs; + + StoreContentURL( SvStream& _rStorage, + uno::Reference< + util::XOfficeInstallationDirectories > xOfficeInstDirs ) + : StorageHelper( _rStorage ), m_xOfficeInstDirs(std::move( xOfficeInstDirs )) { } + + void operator() ( const ::rtl::Reference< TemplateContent >& _rxContent ) const + { + // use the base class operator with the local name of the content + OUString sURL = _rxContent->getURL(); + // #116281# Keep office installation relocatable. Never store + // any direct references to office installation directory. + sURL = m_xOfficeInstDirs->makeRelocatableURL( sURL ); + m_rStorage.WriteUniOrByteString( sURL, m_rStorage.GetStreamCharSet() ); + } + }; + + + /// functor which stores the complete content of a TemplateContent + struct StoreFolderContent : public StorageHelper + { + uno::Reference< util::XOfficeInstallationDirectories > m_xOfficeInstDirs; + + public: + StoreFolderContent( SvStream& _rStorage, + uno::Reference< + util::XOfficeInstallationDirectories > xOfficeInstDirs ) + : StorageHelper( _rStorage ), m_xOfficeInstDirs(std::move( xOfficeInstDirs )) { } + + + void operator() ( const TemplateContent& _rContent ) const + { + // store the info about this content + WriteDateTime( m_rStorage, _rContent.getModDate() ); + + // store the info about the children + // the number + m_rStorage.WriteInt32( _rContent.size() ); + // their URLs ( the local name is not enough, since URL might be not a hierarchical one, "expand:" for example ) + ::std::for_each( + _rContent.getSubContents().begin(), + _rContent.getSubContents().end(), + StoreContentURL( m_rStorage, m_xOfficeInstDirs ) + ); + // their content + ::std::for_each( + _rContent.getSubContents().begin(), + _rContent.getSubContents().end(), + *this + ); + } + + + void operator() ( const ::rtl::Reference< TemplateContent >& _rxContent ) const + { + if ( _rxContent.is() ) + { + operator()( *_rxContent ); + } + } + }; + + + /// functor which reads a complete TemplateContent instance + struct ReadFolderContent : public StorageHelper + { + uno::Reference< util::XOfficeInstallationDirectories > m_xOfficeInstDirs; + + ReadFolderContent( SvStream& _rStorage, + uno::Reference< + util::XOfficeInstallationDirectories > xOfficeInstDirs ) + : StorageHelper( _rStorage ), m_xOfficeInstDirs(std::move( xOfficeInstDirs )) { } + + + void operator() ( TemplateContent& _rContent ) const + { + // store the info about this content + util::DateTime aModDate; + m_rStorage >> aModDate; + _rContent.setModDate( aModDate ); + + // store the info about the children + // the number + sal_Int32 nChildren = 0; + m_rStorage.ReadInt32( nChildren ); + TemplateFolderContent& rChildren = _rContent.getSubContents(); + rChildren.resize( 0 ); + rChildren.reserve( nChildren ); + // initialize them with their (local) names + while ( nChildren-- ) + { + OUString sURL = m_rStorage.ReadUniOrByteString(m_rStorage.GetStreamCharSet()); + sURL = m_xOfficeInstDirs->makeAbsoluteURL( sURL ); + rChildren.push_back( new TemplateContent( INetURLObject( sURL ) ) ); + } + + // their content + ::std::for_each( + _rContent.getSubContents().begin(), + _rContent.getSubContents().end(), + *this + ); + } + + + void operator() ( const ::rtl::Reference< TemplateContent >& _rxContent ) const + { + if ( _rxContent.is() ) + { + operator()( *_rxContent ); + } + } + }; + + } + + //= TemplateFolderCacheImpl + + class TemplateFolderCacheImpl + { + private: + TemplateFolderContent m_aPreviousState; // the current state of the template dirs (as found on the HD) + TemplateFolderContent m_aCurrentState; // the previous state of the template dirs (as found in the cache file) + + std::mutex m_aMutex; + // will be lazy inited; never access directly; use getOfficeInstDirs(). + uno::Reference< util::XOfficeInstallationDirectories > m_xOfficeInstDirs; + + std::unique_ptr<SvStream> m_pCacheStream; + bool m_bNeedsUpdate : 1; + bool m_bKnowState : 1; + bool m_bValidCurrentState : 1; + bool m_bAutoStoreState : 1; + + public: + explicit TemplateFolderCacheImpl( bool _bAutoStoreState ); + ~TemplateFolderCacheImpl( ); + + bool needsUpdate(); + void storeState(); + + private: + bool openCacheStream( bool _bForRead ); + void closeCacheStream( ); + + /// read the state of the dirs from the cache file + bool readPreviousState(); + /// read the current state of the dirs + bool readCurrentState(); + + static OUString implParseSmart( const OUString& _rPath ); + + bool implReadFolder( const ::rtl::Reference< TemplateContent >& _rxRoot ); + + static sal_Int32 getMagicNumber(); + static void normalize( TemplateFolderContent& _rState ); + + // @return <TRUE/> if the states equal + static bool equalStates( const TemplateFolderContent& _rLHS, const TemplateFolderContent& _rRHS ); + + // late initialize m_xOfficeInstDirs + const uno::Reference< util::XOfficeInstallationDirectories >& getOfficeInstDirs(); + }; + + + TemplateFolderCacheImpl::TemplateFolderCacheImpl( bool _bAutoStoreState ) + :m_bNeedsUpdate ( true ) + ,m_bKnowState ( false ) + ,m_bValidCurrentState ( false ) + ,m_bAutoStoreState ( _bAutoStoreState ) + { + } + + + TemplateFolderCacheImpl::~TemplateFolderCacheImpl( ) + { + // store the current state if possible and required + if ( m_bValidCurrentState && m_bAutoStoreState ) + storeState(); + + closeCacheStream( ); + } + + + sal_Int32 TemplateFolderCacheImpl::getMagicNumber() + { + return (sal_Int8('T') << 12) + | (sal_Int8('D') << 8) + | (sal_Int8('S') << 4) + | (sal_Int8('C')); + } + + + void TemplateFolderCacheImpl::normalize( TemplateFolderContent& _rState ) + { + SubContentSort()( _rState ); + } + + + bool TemplateFolderCacheImpl::equalStates( const TemplateFolderContent& _rLHS, const TemplateFolderContent& _rRHS ) + { + if ( _rLHS.size() != _rRHS.size() ) + return false; + + // as both arrays are sorted (by definition - this is a precondition of this method) + // we can simply go from the front to the back and compare the single elements + + ::std::pair< ConstFolderIterator, ConstFolderIterator > aFirstDifferent = ::std::mismatch( + _rLHS.begin(), + _rLHS.end(), + _rRHS.begin(), + TemplateContentEqual() + ); + + return aFirstDifferent.first == _rLHS.end(); + } + + + void TemplateFolderCacheImpl::storeState() + { + if ( !m_bValidCurrentState ) + readCurrentState( ); + + if ( !(m_bValidCurrentState && openCacheStream( false )) ) + return; + + m_pCacheStream->WriteInt32( getMagicNumber() ); + + // store the template root folders + // the size + m_pCacheStream->WriteInt32( m_aCurrentState.size() ); + // the complete URLs + ::std::for_each( + m_aCurrentState.begin(), + m_aCurrentState.end(), + StoreContentURL( *m_pCacheStream, getOfficeInstDirs() ) + ); + + // the contents + ::std::for_each( + m_aCurrentState.begin(), + m_aCurrentState.end(), + StoreFolderContent( *m_pCacheStream, getOfficeInstDirs() ) + ); + } + + + OUString TemplateFolderCacheImpl::implParseSmart( const OUString& _rPath ) + { + INetURLObject aParser; + aParser.SetSmartProtocol( INetProtocol::File ); + aParser.SetURL( _rPath ); + if ( INetProtocol::NotValid == aParser.GetProtocol() ) + { + OUString sURL; + osl::FileBase::getFileURLFromSystemPath( _rPath, sURL ); + aParser.SetURL( sURL ); + } + return aParser.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ); + } + + + void TemplateFolderCacheImpl::closeCacheStream( ) + { + m_pCacheStream.reset(); + } + + + bool TemplateFolderCacheImpl::implReadFolder( const ::rtl::Reference< TemplateContent >& _rxRoot ) + { + try + { + // create a content for the current folder root + Reference< XResultSet > xResultSet; + Sequence< OUString > aContentProperties{ "Title", "DateModified", "DateCreated", + "IsFolder" }; + + // get the set of sub contents in the folder + try + { + Reference< XDynamicResultSet > xDynResultSet; + + ::ucbhelper::Content aTemplateRoot( _rxRoot->getURL(), Reference< XCommandEnvironment >(), comphelper::getProcessComponentContext() ); + xDynResultSet = aTemplateRoot.createDynamicCursor( aContentProperties ); + if ( xDynResultSet.is() ) + xResultSet = xDynResultSet->getStaticResultSet(); + } + catch( CommandAbortedException& ) + { + TOOLS_WARN_EXCEPTION( "svtools.misc", "" ); + return false; + } + catch( css::uno::Exception& ) + { + } + + // collect the infos about the sub contents + if ( xResultSet.is() ) + { + Reference< XRow > xRow( xResultSet, UNO_QUERY_THROW ); + Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY_THROW ); + + while ( xResultSet->next() ) + { + INetURLObject aSubContentURL( xContentAccess->queryContentIdentifierString() ); + + // a new content instance + ::rtl::Reference< TemplateContent > xChild = new TemplateContent( std::move(aSubContentURL) ); + + // the modified date + xChild->setModDate( xRow->getTimestamp( 2 ) ); // date modified + if ( xRow->wasNull() ) + xChild->setModDate( xRow->getTimestamp( 3 ) ); // fallback: date created + + // push back this content + _rxRoot->push_back( xChild ); + + // is it a folder? + if ( xRow->getBoolean( 4 ) && !xRow->wasNull() ) + { // yes -> step down + ConstFolderIterator aNextLevelRoot = _rxRoot->end(); + --aNextLevelRoot; + implReadFolder( *aNextLevelRoot ); + } + } + } + } + catch( const Exception& ) + { + TOOLS_WARN_EXCEPTION( "svtools", "TemplateFolderCacheImpl::implReadFolder" ); + return false; + } + return true; + } + + + bool TemplateFolderCacheImpl::readCurrentState() + { + // reset + m_bValidCurrentState = false; + TemplateFolderContent aTemplateFolderContent; + m_aCurrentState.swap( aTemplateFolderContent ); + + // the template directories from the config + const SvtPathOptions aPathOptions; + const OUString& aDirs = aPathOptions.GetTemplatePath(); + + // loop through all the root-level template folders + sal_Int32 nIndex = 0; + do + { + OUString sTemplatePath( aDirs.getToken(0, ';', nIndex) ); + sTemplatePath = aPathOptions.ExpandMacros( sTemplatePath ); + + // Make sure excess ".." path segments (from expanding bootstrap + // variables in paths) are normalized in the same way they are + // normalized for paths read from the .templdir.cache file (where + // paths have gone through makeRelocatable URL on writing out and + // then through makeAbsoluteURL when reading back in), as otherwise + // equalStates() in needsUpdate() could erroneously consider + // m_aCurrentState and m_aPreviousState as different: + sTemplatePath = getOfficeInstDirs()->makeAbsoluteURL( + getOfficeInstDirs()->makeRelocatableURL(sTemplatePath)); + + // create a new entry + m_aCurrentState.push_back( new TemplateContent( INetURLObject( sTemplatePath ) ) ); + TemplateFolderContent::iterator aCurrentRoot = m_aCurrentState.end(); + --aCurrentRoot; + + if ( !implReadFolder( *aCurrentRoot ) ) + return false; + } + while ( nIndex >= 0 ); + + // normalize the array (which basically means "sort it") + normalize( m_aCurrentState ); + + m_bValidCurrentState = true; + return m_bValidCurrentState; + } + + + bool TemplateFolderCacheImpl::readPreviousState() + { + DBG_ASSERT( m_pCacheStream, "TemplateFolderCacheImpl::readPreviousState: not to be called without stream!" ); + + // reset + TemplateFolderContent aTemplateFolderContent; + m_aPreviousState.swap( aTemplateFolderContent ); + + // check the magic number + sal_Int32 nMagic = 0; + m_pCacheStream->ReadInt32( nMagic ); + DBG_ASSERT( getMagicNumber() == nMagic, "TemplateFolderCacheImpl::readPreviousState: invalid cache file!" ); + if ( getMagicNumber() != nMagic ) + return false; + + // the root directories + // their number + sal_Int32 nRootDirectories = 0; + m_pCacheStream->ReadInt32( nRootDirectories ); + // init empty TemplateContents with the URLs + m_aPreviousState.reserve( nRootDirectories ); + while ( nRootDirectories-- ) + { + OUString sURL = m_pCacheStream->ReadUniOrByteString(m_pCacheStream->GetStreamCharSet()); + // #116281# Keep office installation relocatable. Never store + // any direct references to office installation directory. + sURL = getOfficeInstDirs()->makeAbsoluteURL( sURL ); + m_aPreviousState.push_back( + new TemplateContent( INetURLObject(sURL) ) ); + } + + // read the contents of the root folders + ::std::for_each( + m_aPreviousState.begin(), + m_aPreviousState.end(), + ReadFolderContent( *m_pCacheStream, getOfficeInstDirs() ) + ); + + DBG_ASSERT( !m_pCacheStream->GetErrorCode(), "TemplateFolderCacheImpl::readPreviousState: unknown error during reading the state cache!" ); + + // normalize the array (which basically means "sort it") + normalize( m_aPreviousState ); + + return true; + } + + + bool TemplateFolderCacheImpl::openCacheStream( bool _bForRead ) + { + // close any old stream instance + closeCacheStream( ); + + // get the storage directory + OUString sStorageURL = implParseSmart( SvtPathOptions().GetStoragePath() ); + INetURLObject aStorageURL( sStorageURL ); + if ( INetProtocol::NotValid == aStorageURL.GetProtocol() ) + { + OSL_FAIL( "TemplateFolderCacheImpl::openCacheStream: invalid storage path!" ); + return false; + } + + // append our name + aStorageURL.Append( u".templdir.cache" ); + + // open the stream + m_pCacheStream = UcbStreamHelper::CreateStream( aStorageURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ), + _bForRead ? StreamMode::READ | StreamMode::NOCREATE : StreamMode::WRITE | StreamMode::TRUNC ); + DBG_ASSERT( m_pCacheStream, "TemplateFolderCacheImpl::openCacheStream: could not open/create the cache stream!" ); + if ( m_pCacheStream && m_pCacheStream->GetErrorCode() ) + { + m_pCacheStream.reset(); + } + + if ( m_pCacheStream ) + m_pCacheStream->SetStreamCharSet( RTL_TEXTENCODING_UTF8 ); + + return nullptr != m_pCacheStream; + } + + + bool TemplateFolderCacheImpl::needsUpdate() + { + if ( m_bKnowState ) + return m_bNeedsUpdate; + + m_bNeedsUpdate = true; + m_bKnowState = true; + + if ( readCurrentState() ) + { + // open the stream which contains the cached state of the directories + if ( openCacheStream( true ) ) + { // opening the stream succeeded + if ( readPreviousState() ) + { + m_bNeedsUpdate = !equalStates( m_aPreviousState, m_aCurrentState ); + } + else + { + closeCacheStream(); + } + } + } + return m_bNeedsUpdate; + } + + + const uno::Reference< util::XOfficeInstallationDirectories >& + TemplateFolderCacheImpl::getOfficeInstDirs() + { + if ( !m_xOfficeInstDirs.is() ) + { + std::lock_guard aGuard( m_aMutex ); + if ( !m_xOfficeInstDirs.is() ) + { + uno::Reference< uno::XComponentContext > xCtx( + comphelper::getProcessComponentContext() ); + m_xOfficeInstDirs = util::theOfficeInstallationDirectories::get(xCtx); + } + } + return m_xOfficeInstDirs; + } + + + //= TemplateFolderCache + + + TemplateFolderCache::TemplateFolderCache( bool _bAutoStoreState ) + :m_pImpl( new TemplateFolderCacheImpl( _bAutoStoreState ) ) + { + } + + + TemplateFolderCache::~TemplateFolderCache( ) + { + } + + + bool TemplateFolderCache::needsUpdate() + { + return m_pImpl->needsUpdate(); + } + + + void TemplateFolderCache::storeState() + { + m_pImpl->storeState(); + } + + +} // namespace sfx2 + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/svtools/source/misc/unitconv.cxx b/svtools/source/misc/unitconv.cxx new file mode 100644 index 0000000000..cb4fdf6901 --- /dev/null +++ b/svtools/source/misc/unitconv.cxx @@ -0,0 +1,203 @@ +/* -*- 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 <o3tl/temporary.hxx> +#include <svtools/unitconv.hxx> +#include <tools/debug.hxx> +#include <tools/UnitConversion.hxx> +#include <vcl/outdev.hxx> +#include <vcl/weld.hxx> + +void SetFieldUnit(weld::MetricSpinButton& rField, FieldUnit eUnit, bool bAll) +{ + sal_Int64 nMin, nMax; + rField.get_range(nMin, nMax, FieldUnit::TWIP); + sal_Int64 nValue = rField.get_value(FieldUnit::TWIP); + nMin = rField.denormalize(nMin); + nMax = rField.denormalize(nMax); + nValue = rField.denormalize(nValue); + + if (!bAll) + { + switch (eUnit) + { + case FieldUnit::M: + case FieldUnit::KM: + eUnit = FieldUnit::CM; + break; + case FieldUnit::FOOT: + case FieldUnit::MILE: + eUnit = FieldUnit::INCH; + break; + default: //prevent warning + break; + } + } + + rField.set_unit(eUnit); + + if (FieldUnit::POINT == eUnit) + { + if (rField.get_digits() > 1) + rField.set_digits(1); + } + else + rField.set_digits(2); + + switch (eUnit) + { + // _CHAR and _LINE sets the step of "char" and "line" unit, they are same as FieldUnit::MM + case FieldUnit::CHAR: + case FieldUnit::LINE: + case FieldUnit::MM: + rField.set_increments(50, 500, eUnit); + break; + case FieldUnit::INCH: + rField.set_increments(2, 20, eUnit); + break; + default: + rField.set_increments(10, 100, eUnit); + break; + } + + if (!bAll) + { + nMin = rField.normalize(nMin); + nMax = rField.normalize(nMax); + rField.set_range(nMin, nMax, FieldUnit::TWIP); + } + + rField.set_value(rField.normalize(nValue), FieldUnit::TWIP); +} + +void SetMetricValue(weld::MetricSpinButton& rField, sal_Int64 nCoreValue, MapUnit eUnit) +{ + sal_Int64 nVal = OutputDevice::LogicToLogic(nCoreValue, eUnit, MapUnit::Map100thMM); + nVal = rField.normalize(nVal); + rField.set_value(nVal, FieldUnit::MM_100TH); +} + +sal_Int64 GetCoreValue(const weld::MetricSpinButton& rField, MapUnit eUnit) +{ + sal_Int64 nVal = rField.get_value(FieldUnit::MM_100TH); + // avoid rounding issues + const sal_Int64 nSizeMask = 0xffffffffff000000LL; + bool bRoundBefore = true; + if( nVal >= 0 ) + { + if( (nVal & nSizeMask) == 0 ) + bRoundBefore = false; + } + else + { + if( ((-nVal) & nSizeMask ) == 0 ) + bRoundBefore = false; + } + if( bRoundBefore ) + nVal = rField.denormalize( nVal ); + sal_Int64 nUnitVal = OutputDevice::LogicToLogic(nVal, MapUnit::Map100thMM, eUnit); + if (!bRoundBefore) + nUnitVal = rField.denormalize(nUnitVal); + return nUnitVal; +} + +tools::Long CalcToUnit( float nIn, MapUnit eUnit ) +{ + // nIn is in Points + + DBG_ASSERT( eUnit == MapUnit::MapTwip || + eUnit == MapUnit::Map100thMM || + eUnit == MapUnit::Map10thMM || + eUnit == MapUnit::MapMM || + eUnit == MapUnit::MapCM, "this unit is not implemented" ); + + if (const auto eTo = MapToO3tlLength(eUnit); eTo != o3tl::Length::invalid) + return std::round(o3tl::convert(nIn, o3tl::Length::pt, eTo)); + + return 0; +} + + +tools::Long ItemToControl( tools::Long nIn, MapUnit eItem, FieldUnit eCtrl ) +{ + const auto eFrom = MapToO3tlLength(eItem), eTo = FieldToO3tlLength(eCtrl); + if (eFrom != o3tl::Length::invalid && eTo != o3tl::Length::invalid) + return o3tl::convert(nIn, eFrom, eTo); + return 0; +} + + +tools::Long ControlToItem( tools::Long nIn, FieldUnit eCtrl, MapUnit eItem ) +{ + return ItemToControl( nIn, eItem, eCtrl ); +} + + +FieldUnit MapToFieldUnit( const MapUnit eUnit ) +{ + switch ( eUnit ) + { + case MapUnit::Map100thMM: + case MapUnit::Map10thMM: + case MapUnit::MapMM: + return FieldUnit::MM; + + case MapUnit::MapCM: + return FieldUnit::CM; + + case MapUnit::Map1000thInch: + case MapUnit::Map100thInch: + case MapUnit::Map10thInch: + case MapUnit::MapInch: + return FieldUnit::INCH; + + case MapUnit::MapPoint: + return FieldUnit::POINT; + + case MapUnit::MapTwip: + return FieldUnit::TWIP; + default: ;//prevent warning + } + return FieldUnit::NONE; +} + + +tools::Long CalcToPoint( tools::Long nIn, MapUnit eUnit, sal_uInt16 nFactor ) +{ + DBG_ASSERT( eUnit == MapUnit::MapTwip || + eUnit == MapUnit::Map100thMM || + eUnit == MapUnit::Map10thMM || + eUnit == MapUnit::MapMM || + eUnit == MapUnit::MapCM, "this unit is not implemented" ); + + if (const auto eFrom = MapToO3tlLength(eUnit); eFrom != o3tl::Length::invalid) + return o3tl::convert(nIn * static_cast<sal_Int64>(nFactor), eFrom, o3tl::Length::pt); + return 0; +} + + +tools::Long TransformMetric( tools::Long nVal, FieldUnit aOld, FieldUnit aNew ) +{ + const auto eFrom = FieldToO3tlLength(aOld), eTo = FieldToO3tlLength(aNew); + if (eFrom == o3tl::Length::invalid || eTo == o3tl::Length::invalid) + return nVal; + return o3tl::convert(nVal, eFrom, eTo, o3tl::temporary(bool())); // Just 0 when overflown +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |