summaryrefslogtreecommitdiffstats
path: root/unotools
diff options
context:
space:
mode:
Diffstat (limited to 'unotools')
-rw-r--r--unotools/CppunitTest_unotools_configpaths.mk21
-rw-r--r--unotools/CppunitTest_unotools_fontcvt.mk30
-rw-r--r--unotools/CppunitTest_unotools_fontdefs.mk30
-rw-r--r--unotools/IwyuFilter_unotools.yaml43
-rw-r--r--unotools/JunitTest_unotools_complex.mk33
-rw-r--r--unotools/Library_utl.mk122
-rw-r--r--unotools/Makefile14
-rw-r--r--unotools/Module_unotools.mk25
-rw-r--r--unotools/PythonTest_unotools_python.mk16
-rw-r--r--unotools/README.md3
-rw-r--r--unotools/inc/pch/precompiled_utl.cxx12
-rw-r--r--unotools/inc/pch/precompiled_utl.hxx147
-rw-r--r--unotools/qa/python/test_tempfile.py149
-rw-r--r--unotools/qa/unit/configpaths.cxx115
-rw-r--r--unotools/qa/unit/testGetEnglishSearchName.cxx73
-rw-r--r--unotools/qa/unit/testRecodeString.cxx45
-rw-r--r--unotools/source/accessibility/accessiblerelationsethelper.cxx170
-rw-r--r--unotools/source/config/bootstrap.cxx720
-rw-r--r--unotools/source/config/cmdoptions.cxx337
-rw-r--r--unotools/source/config/compatibility.cxx415
-rw-r--r--unotools/source/config/configitem.cxx1190
-rw-r--r--unotools/source/config/configmgr.cxx193
-rw-r--r--unotools/source/config/confignode.cxx562
-rw-r--r--unotools/source/config/configpaths.cxx307
-rw-r--r--unotools/source/config/configvaluecontainer.cxx304
-rw-r--r--unotools/source/config/defaultoptions.cxx119
-rw-r--r--unotools/source/config/docinfohelper.cxx105
-rw-r--r--unotools/source/config/dynamicmenuoptions.cxx339
-rw-r--r--unotools/source/config/eventcfg.cxx380
-rw-r--r--unotools/source/config/fltrcfg.cxx670
-rw-r--r--unotools/source/config/fontcfg.cxx1076
-rw-r--r--unotools/source/config/historyoptions.cxx610
-rw-r--r--unotools/source/config/itemholder1.cxx150
-rw-r--r--unotools/source/config/itemholder1.hxx56
-rw-r--r--unotools/source/config/lingucfg.cxx1207
-rw-r--r--unotools/source/config/moduleoptions.cxx1117
-rw-r--r--unotools/source/config/options.cxx114
-rw-r--r--unotools/source/config/optionsdlg.cxx152
-rw-r--r--unotools/source/config/pathoptions.cxx737
-rw-r--r--unotools/source/config/saveopt.cxx94
-rw-r--r--unotools/source/config/searchopt.cxx612
-rw-r--r--unotools/source/config/securityoptions.cxx410
-rw-r--r--unotools/source/config/syslocaleoptions.cxx718
-rw-r--r--unotools/source/config/useroptions.cxx346
-rw-r--r--unotools/source/config/viewoptions.cxx450
-rw-r--r--unotools/source/i18n/calendarwrapper.cxx342
-rw-r--r--unotools/source/i18n/caserotate.cxx43
-rw-r--r--unotools/source/i18n/charclass.cxx452
-rw-r--r--unotools/source/i18n/collatorwrapper.cxx97
-rw-r--r--unotools/source/i18n/intlwrapper.cxx58
-rw-r--r--unotools/source/i18n/localedatawrapper.cxx1517
-rw-r--r--unotools/source/i18n/nativenumberwrapper.cxx110
-rw-r--r--unotools/source/i18n/resmgr.cxx321
-rw-r--r--unotools/source/i18n/textsearch.cxx357
-rw-r--r--unotools/source/i18n/transliterationwrapper.cxx231
-rw-r--r--unotools/source/misc/ServiceDocumenter.cxx89
-rw-r--r--unotools/source/misc/ServiceDocumenter.hxx57
-rw-r--r--unotools/source/misc/ZipPackageHelper.cxx173
-rw-r--r--unotools/source/misc/closeveto.cxx147
-rw-r--r--unotools/source/misc/datetime.cxx536
-rw-r--r--unotools/source/misc/defaultencoding.cxx33
-rw-r--r--unotools/source/misc/desktopterminationobserver.cxx188
-rw-r--r--unotools/source/misc/eventlisteneradapter.cxx155
-rw-r--r--unotools/source/misc/fontcvt.cxx1432
-rw-r--r--unotools/source/misc/fontdefs.cxx417
-rw-r--r--unotools/source/misc/mediadescriptor.cxx500
-rw-r--r--unotools/source/misc/sharedunocomponent.cxx199
-rw-r--r--unotools/source/misc/syslocale.cxx177
-rw-r--r--unotools/source/misc/wincodepage.cxx141
-rw-r--r--unotools/source/streaming/streamhelper.cxx123
-rw-r--r--unotools/source/streaming/streamwrap.cxx338
-rw-r--r--unotools/source/ucbhelper/XTempFile.hxx119
-rw-r--r--unotools/source/ucbhelper/localfilehelper.cxx92
-rw-r--r--unotools/source/ucbhelper/progresshandlerwrap.cxx88
-rw-r--r--unotools/source/ucbhelper/tempfile.cxx808
-rw-r--r--unotools/source/ucbhelper/ucbhelper.cxx413
-rw-r--r--unotools/source/ucbhelper/ucblockbytes.cxx1338
-rw-r--r--unotools/source/ucbhelper/ucblockbytes.hxx144
-rw-r--r--unotools/source/ucbhelper/ucbstreamhelper.cxx237
-rw-r--r--unotools/source/ucbhelper/xtempfile.cxx435
-rw-r--r--unotools/util/utl.component31
81 files changed, 26476 insertions, 0 deletions
diff --git a/unotools/CppunitTest_unotools_configpaths.mk b/unotools/CppunitTest_unotools_configpaths.mk
new file mode 100644
index 0000000000..d5f002b9bb
--- /dev/null
+++ b/unotools/CppunitTest_unotools_configpaths.mk
@@ -0,0 +1,21 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t; fill-column: 100 -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,unotools_configpaths))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,unotools_configpaths, \
+ unotools/qa/unit/configpaths \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,unotools_configpaths, \
+ sal \
+ utl \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/unotools/CppunitTest_unotools_fontcvt.mk b/unotools/CppunitTest_unotools_fontcvt.mk
new file mode 100644
index 0000000000..16236c16c5
--- /dev/null
+++ b/unotools/CppunitTest_unotools_fontcvt.mk
@@ -0,0 +1,30 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,unotools_fontcvt))
+
+$(eval $(call gb_CppunitTest_use_external,unotools_fontcvt,boost_headers))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,unotools_fontcvt))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,unotools_fontcvt, \
+ unotools/qa/unit/testRecodeString \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,unotools_fontcvt, \
+ comphelper \
+ cppu \
+ cppuhelper \
+ tl \
+ sal \
+ svt \
+ utl \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/unotools/CppunitTest_unotools_fontdefs.mk b/unotools/CppunitTest_unotools_fontdefs.mk
new file mode 100644
index 0000000000..5eada2084a
--- /dev/null
+++ b/unotools/CppunitTest_unotools_fontdefs.mk
@@ -0,0 +1,30 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_CppunitTest_CppunitTest,unotools_fontdefs))
+
+$(eval $(call gb_CppunitTest_use_external,unotools_fontdefs,boost_headers))
+
+$(eval $(call gb_CppunitTest_use_sdk_api,unotools_fontdefs))
+
+$(eval $(call gb_CppunitTest_add_exception_objects,unotools_fontdefs, \
+ unotools/qa/unit/testGetEnglishSearchName \
+))
+
+$(eval $(call gb_CppunitTest_use_libraries,unotools_fontdefs, \
+ comphelper \
+ cppu \
+ cppuhelper \
+ tl \
+ sal \
+ svt \
+ utl \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/unotools/IwyuFilter_unotools.yaml b/unotools/IwyuFilter_unotools.yaml
new file mode 100644
index 0000000000..0a3b6eeff0
--- /dev/null
+++ b/unotools/IwyuFilter_unotools.yaml
@@ -0,0 +1,43 @@
+---
+assumeFilename: unotools/source/i18n/resmgr.cxx
+excludelist:
+ unotools/source/config/itemholder1.hxx:
+ # Base class needs complete type
+ - com/sun/star/lang/XEventListener.hpp
+ unotools/source/ucbhelper/XTempFile.hxx:
+ # Base class needs complete type
+ - com/sun/star/io/XInputStream.hpp
+ - com/sun/star/io/XOutputStream.hpp
+ - com/sun/star/io/XTempFile.hpp
+ - com/sun/star/io/XTruncate.hpp
+ - com/sun/star/beans/XFastPropertySet.hpp
+ - com/sun/star/beans/XPropertyAccess.hpp
+ - com/sun/star/beans/XPropertySet.hpp
+ unotools/source/config/configpaths.cxx:
+ # Needed for linker visibility
+ - unotools/configpaths.hxx
+ unotools/source/config/lingucfg.cxx:
+ # Needed for direct member access
+ - com/sun/star/util/XChangesBatch.hpp
+ unotools/source/config/useroptions.cxx:
+ # Needed for direct member access
+ - com/sun/star/container/XNameAccess.hpp
+ unotools/source/i18n/resmgr.cxx:
+ # Boost wrappers, used only here
+ - boost/locale.hpp
+ - boost/locale/gnu_gettext.hpp
+ unotools/source/ucbhelper/tempfile.cxx:
+ # Needed for non-DBG_UTIL build
+ - unistd.h
+ unotools/source/ucbhelper/ucbhelper.cxx:
+ # Needed for namespaced function declarations
+ - unotools/ucbhelper.hxx
+ unotools/source/ucbhelper/ucblockbytes.cxx:
+ # Needed for direct member access
+ - com/sun/star/beans/PropertyValue.hpp
+ unotools/source/ucbhelper/ucbstreamhelper.cxx:
+ # Needed for direct member access
+ - com/sun/star/beans/PropertyValue.hpp
+ unotools/source/misc/datetime.cxx:
+ # Needed for rtl::math::round
+ - rtl/math.hxx
diff --git a/unotools/JunitTest_unotools_complex.mk b/unotools/JunitTest_unotools_complex.mk
new file mode 100644
index 0000000000..a673404933
--- /dev/null
+++ b/unotools/JunitTest_unotools_complex.mk
@@ -0,0 +1,33 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_JunitTest_JunitTest,unotools_complex))
+
+$(eval $(call gb_JunitTest_use_unoapi_jars,unotools_complex))
+
+$(eval $(call gb_JunitTest_use_jars,unotools_complex,\
+ test-tools \
+))
+
+$(eval $(call gb_JunitTest_set_defs,unotools_complex,\
+ $$(DEFS) \
+))
+
+$(eval $(call gb_JunitTest_add_classes,unotools_complex,\
+ complex.tempfile.TempFileUnitTest \
+))
+
+$(eval $(call gb_JunitTest_add_sourcefiles,unotools_complex,\
+ unotools/qa/complex/tempfile/TempFileUnitTest \
+ unotools/qa/complex/tempfile/Test01 \
+ unotools/qa/complex/tempfile/Test02 \
+ unotools/qa/complex/tempfile/TestHelper \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/unotools/Library_utl.mk b/unotools/Library_utl.mk
new file mode 100644
index 0000000000..14ef6e843a
--- /dev/null
+++ b/unotools/Library_utl.mk
@@ -0,0 +1,122 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+# declare a library
+# utl is the name of the library as it is found in Repository.mk
+$(eval $(call gb_Library_Library,utl))
+
+$(eval $(call gb_Library_use_externals,utl,\
+ boost_headers \
+ boost_locale \
+))
+
+$(eval $(call gb_Library_use_custom_headers,utl,\
+ officecfg/registry \
+))
+
+# in case UNO services are exported: declare location of component file
+$(eval $(call gb_Library_set_componentfile,utl,unotools/util/utl,services))
+
+$(eval $(call gb_Library_set_include,utl, \
+ $$(INCLUDE) \
+ -I$(SRCDIR)/unotools/inc \
+))
+
+# add any additional definitions to be set for compilation here
+# (e.g. -DLIB_DLLIMPLEMENTATION)
+$(eval $(call gb_Library_add_defs,utl,\
+ -DUNOTOOLS_DLLIMPLEMENTATION \
+))
+
+$(eval $(call gb_Library_set_precompiled_header,utl,unotools/inc/pch/precompiled_utl))
+
+$(eval $(call gb_Library_use_sdk_api,utl))
+
+# add libraries to be linked to utl; again these names need to be given as
+# specified in Repository.mk
+$(eval $(call gb_Library_use_libraries,utl,\
+ comphelper \
+ cppu \
+ cppuhelper \
+ i18nlangtag \
+ sal \
+ salhelper \
+ tl \
+ ucbhelper \
+))
+
+$(eval $(call gb_Library_add_exception_objects,utl,\
+ unotools/source/accessibility/accessiblerelationsethelper \
+))
+
+# add all source files that shall be compiled with exceptions enabled
+# the name is relative to $(SRCROOT) and must not contain an extension
+$(eval $(call gb_Library_add_exception_objects,utl,\
+ unotools/source/config/bootstrap \
+ unotools/source/config/cmdoptions \
+ unotools/source/config/compatibility \
+ unotools/source/config/configitem \
+ unotools/source/config/configmgr \
+ unotools/source/config/confignode \
+ unotools/source/config/configpaths \
+ unotools/source/config/configvaluecontainer \
+ unotools/source/config/defaultoptions \
+ unotools/source/config/docinfohelper \
+ unotools/source/config/dynamicmenuoptions \
+ unotools/source/config/eventcfg \
+ unotools/source/config/fltrcfg \
+ unotools/source/config/fontcfg \
+ unotools/source/config/historyoptions \
+ unotools/source/config/itemholder1 \
+ unotools/source/config/lingucfg \
+ unotools/source/config/moduleoptions \
+ unotools/source/config/options \
+ unotools/source/config/optionsdlg \
+ unotools/source/config/pathoptions \
+ unotools/source/config/saveopt \
+ unotools/source/config/searchopt \
+ unotools/source/config/securityoptions \
+ unotools/source/config/syslocaleoptions \
+ unotools/source/config/useroptions \
+ unotools/source/config/viewoptions \
+ unotools/source/i18n/calendarwrapper \
+ unotools/source/i18n/caserotate \
+ unotools/source/i18n/charclass \
+ unotools/source/i18n/collatorwrapper \
+ unotools/source/i18n/intlwrapper \
+ unotools/source/i18n/localedatawrapper \
+ unotools/source/i18n/nativenumberwrapper \
+ unotools/source/i18n/resmgr \
+ unotools/source/i18n/textsearch \
+ unotools/source/i18n/transliterationwrapper \
+ unotools/source/misc/closeveto \
+ unotools/source/misc/datetime \
+ unotools/source/misc/defaultencoding \
+ unotools/source/misc/desktopterminationobserver \
+ unotools/source/misc/eventlisteneradapter \
+ unotools/source/misc/fontcvt \
+ unotools/source/misc/fontdefs \
+ unotools/source/misc/mediadescriptor \
+ unotools/source/misc/sharedunocomponent \
+ unotools/source/misc/syslocale \
+ unotools/source/misc/wincodepage \
+ unotools/source/misc/ServiceDocumenter \
+ unotools/source/misc/ZipPackageHelper \
+ unotools/source/streaming/streamhelper \
+ unotools/source/streaming/streamwrap \
+ unotools/source/ucbhelper/localfilehelper \
+ unotools/source/ucbhelper/progresshandlerwrap \
+ unotools/source/ucbhelper/tempfile \
+ unotools/source/ucbhelper/ucbhelper \
+ unotools/source/ucbhelper/ucblockbytes \
+ unotools/source/ucbhelper/ucbstreamhelper \
+ unotools/source/ucbhelper/xtempfile \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/unotools/Makefile b/unotools/Makefile
new file mode 100644
index 0000000000..0997e62848
--- /dev/null
+++ b/unotools/Makefile
@@ -0,0 +1,14 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST))))
+
+include $(module_directory)/../solenv/gbuild/partial_build.mk
+
+# vim: set noet sw=4 ts=4:
diff --git a/unotools/Module_unotools.mk b/unotools/Module_unotools.mk
new file mode 100644
index 0000000000..5d8b786be5
--- /dev/null
+++ b/unotools/Module_unotools.mk
@@ -0,0 +1,25 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_Module_Module,unotools))
+
+$(eval $(call gb_Module_add_targets,unotools,\
+ Library_utl \
+))
+
+$(eval $(call gb_Module_add_subsequentcheck_targets,unotools,\
+ PythonTest_unotools_python \
+))
+$(eval $(call gb_Module_add_check_targets,unotools,\
+ CppunitTest_unotools_configpaths \
+ CppunitTest_unotools_fontdefs \
+ CppunitTest_unotools_fontcvt \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/unotools/PythonTest_unotools_python.mk b/unotools/PythonTest_unotools_python.mk
new file mode 100644
index 0000000000..5201cad4c2
--- /dev/null
+++ b/unotools/PythonTest_unotools_python.mk
@@ -0,0 +1,16 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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/.
+#
+
+$(eval $(call gb_PythonTest_PythonTest,unotools_python))
+
+$(eval $(call gb_PythonTest_add_modules,unotools_python,$(SRCDIR)/unotools/qa/python,\
+ test_tempfile \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/unotools/README.md b/unotools/README.md
new file mode 100644
index 0000000000..73ce9f0897
--- /dev/null
+++ b/unotools/README.md
@@ -0,0 +1,3 @@
+# UNO C++ Helpers
+
+Helpers for C++ use of UNO.
diff --git a/unotools/inc/pch/precompiled_utl.cxx b/unotools/inc/pch/precompiled_utl.cxx
new file mode 100644
index 0000000000..41a7f40d9a
--- /dev/null
+++ b/unotools/inc/pch/precompiled_utl.cxx
@@ -0,0 +1,12 @@
+/* -*- 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 "precompiled_utl.hxx"
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/inc/pch/precompiled_utl.hxx b/unotools/inc/pch/precompiled_utl.hxx
new file mode 100644
index 0000000000..1a46cbc1a9
--- /dev/null
+++ b/unotools/inc/pch/precompiled_utl.hxx
@@ -0,0 +1,147 @@
+/* -*- 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 has been autogenerated by update_pch.sh. It is possible to edit it
+ manually (such as when an include file has been moved/renamed/removed). All such
+ manual changes will be rewritten by the next run of update_pch.sh (which presumably
+ also fixes all possible problems, so it's usually better to use it).
+
+ Generated on 2021-03-08 13:22:27 using:
+ ./bin/update_pch unotools utl --cutoff=3 --exclude:system --exclude:module --include:local
+
+ If after updating build fails, use the following command to locate conflicting headers:
+ ./bin/update_pch_bisect ./unotools/inc/pch/precompiled_utl.hxx "make unotools.build" --find-conflicts
+*/
+
+#include <sal/config.h>
+#if PCH_LEVEL >= 1
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <memory>
+#include <optional>
+#include <ostream>
+#include <stddef.h>
+#include <string_view>
+#include <type_traits>
+#include <unordered_map>
+#include <utility>
+#include <vector>
+#endif // PCH_LEVEL >= 1
+#if PCH_LEVEL >= 2
+#include <osl/detail/file.h>
+#include <osl/diagnose.h>
+#include <osl/endian.h>
+#include <osl/file.hxx>
+#include <osl/mutex.hxx>
+#include <osl/nlsupport.h>
+#include <osl/process.h>
+#include <osl/thread.h>
+#include <osl/thread.hxx>
+#include <osl/time.h>
+#include <rtl/alloc.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/character.hxx>
+#include <rtl/instance.hxx>
+#include <rtl/locale.h>
+#include <rtl/math.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/string.h>
+#include <rtl/string.hxx>
+#include <rtl/tencinfo.h>
+#include <rtl/textenc.h>
+#include <rtl/uri.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.h>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <sal/macros.h>
+#include <sal/saldllapi.h>
+#include <sal/types.h>
+#include <sal/typesizes.h>
+#include <comphelper/errcode.hxx>
+#endif // PCH_LEVEL >= 2
+#if PCH_LEVEL >= 3
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/util/Date.hpp>
+#include <com/sun/star/util/Time.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <comphelper/comphelperdllapi.h>
+#include <comphelper/configurationhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <cppu/cppudllapi.h>
+#include <cppu/unotype.hxx>
+#include <cppuhelper/cppuhelperdllapi.h>
+#include <cppuhelper/implbase.hxx>
+#include <i18nlangtag/lang.h>
+#include <i18nlangtag/languagetag.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <i18nutil/transliteration.hxx>
+#include <o3tl/enumarray.hxx>
+#include <o3tl/safeint.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <o3tl/underlyingenumvalue.hxx>
+#include <salhelper/condition.hxx>
+#include <tools/date.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/lineend.hxx>
+#include <tools/long.hxx>
+#include <tools/ref.hxx>
+#include <tools/stream.hxx>
+#include <tools/time.hxx>
+#include <tools/toolsdllapi.h>
+#include <tools/urlobj.hxx>
+#include <ucbhelper/content.hxx>
+#include <ucbhelper/ucbhelperdllapi.h>
+#endif // PCH_LEVEL >= 3
+#if PCH_LEVEL >= 4
+#include <unotools/charclass.hxx>
+#include <unotools/configitem.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/configpaths.hxx>
+#include <unotools/fontdefs.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/options.hxx>
+#include <unotools/pathoptions.hxx>
+#include <unotools/securityoptions.hxx>
+#include <unotools/streamwrap.hxx>
+#include <unotools/syslocale.hxx>
+#include <unotools/syslocaleoptions.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <unotools/unotoolsdllapi.h>
+#endif // PCH_LEVEL >= 4
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/qa/python/test_tempfile.py b/unotools/qa/python/test_tempfile.py
new file mode 100644
index 0000000000..0d417dbbe9
--- /dev/null
+++ b/unotools/qa/python/test_tempfile.py
@@ -0,0 +1,149 @@
+#! /usr/bin/env python
+# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-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/.
+#
+
+import unittest
+
+import uno
+from org.libreoffice.unotest import UnoInProcess
+
+
+class TestTempFile(unittest.TestCase):
+ """Test temporary file object created from com.sun.star.io.TempFile"""
+
+ @classmethod
+ def setUpClass(cls):
+ cls._uno = UnoInProcess()
+ cls._uno.setUp()
+
+ @classmethod
+ def tearDownClass(cls):
+ cls._uno.tearDown()
+
+ def setUp(self):
+ self.file_data = uno.ByteSequence(b"some data")
+
+ service_manager = self._uno.getContext().getServiceManager()
+ if service_manager is None:
+ raise RuntimeError("Cannot create service factory!")
+
+ try:
+ self.file_access = service_manager.createInstance("com.sun.star.ucb.SimpleFileAccess")
+ if self.file_access is None:
+ raise RuntimeError("Cannot get simple access!")
+ except Exception as e:
+ raise RuntimeError(f"Cannot get simple file access! {e}")
+
+ self.temp_file = service_manager.createInstance("com.sun.star.io.TempFile")
+ has_xtempfile_if = bool([
+ 1 for type_info in self.temp_file.getTypes()
+ if type_info.typeName == "com.sun.star.io.XTempFile"
+ ])
+ if not has_xtempfile_if:
+ raise RuntimeError("Cannot get XTempFile interface.")
+
+ def close_temp_file(self) -> None:
+ stream = self.temp_file.getOutputStream()
+ if stream is None:
+ raise RuntimeError("Cannot get output stream")
+ stream.closeOutput()
+ stream = self.temp_file.getInputStream()
+ if stream is None:
+ raise RuntimeError("Cannot get input stream")
+ stream.closeInput()
+ print("Tempfile closed successfully.")
+
+ def read_bytes_with_stream(self) -> uno.ByteSequence:
+ input_stream = self.temp_file.getInputStream()
+ if input_stream is None:
+ raise RuntimeError("Cannot get input stream from tempfile.")
+ nbytes, read_data = input_stream.readBytes(None, len(self.file_data))
+ print("Read", nbytes, "bytes from tempfile successfully.")
+ return read_data
+
+ def read_directly_from_temp_file(self, file_url: str) -> uno.ByteSequence:
+ print("Attempting to read directly from", file_url)
+ input_stream = self.file_access.openFileRead(file_url)
+ if input_stream is None:
+ raise RuntimeError("Cannot create input stream from URL.")
+ nbytes, read_data = input_stream.readBytes(None, len(self.file_data))
+ print("Read", nbytes, "bytes directly from tempfile successfully.")
+ return read_data
+
+ def write_bytes_with_stream(self) -> None:
+ output_stream = self.temp_file.getOutputStream()
+ if output_stream is None:
+ raise RuntimeError("Cannot get output stream.")
+ output_stream.writeBytes(self.file_data)
+ output_stream.flush()
+ print("Write", len(self.file_data), "bytes to tempfile successfully.")
+
+ def get_temp_file_url(self) -> str:
+ uri = self.temp_file.Uri
+ if not uri:
+ raise RuntimeError("Temporary file not valid.")
+ return uri
+
+ def get_temp_file_name(self) -> str:
+ file_name = self.temp_file.ResourceName
+ if not file_name:
+ raise RuntimeError("Temporary file not valid.")
+ return file_name
+
+ def test_01(self):
+ file_uri = self.get_temp_file_url()
+ file_name = self.get_temp_file_name()
+ print("Tempfile URL:", file_uri)
+ print("Tempfile name:", file_name)
+ self.assertTrue(
+ file_uri.endswith(file_name.replace("\\", "/")),
+ "FILE NAME AND URL DO NOT MATCH.",
+ )
+
+ # write to the stream using the service.
+ self.write_bytes_with_stream()
+
+ # check the result by reading from the service.
+ self.temp_file.seek(0)
+ read_data = self.read_bytes_with_stream()
+ self.assertEqual(self.file_data, read_data, "Tempfile outputs false data!")
+
+ # check the result by reading from the file directly.
+ read_data = self.read_directly_from_temp_file(file_uri)
+ self.assertEqual(self.file_data, read_data, "Tempfile contains false data!")
+
+ # close the object(by closing input and output), check that the file
+ # was removed.
+ self.temp_file.RemoveFile = False
+ # After tempfile is closed, file name cannot be got from a TempFile object.
+ file_name = self.temp_file.ResourceName
+ self.close_temp_file()
+ self.assertTrue(
+ self.file_access.exists(file_name), "TempFile mistakenly removed.",
+ )
+
+ # Finally, cleanup this temp file.
+ self.file_access.kill(file_name)
+
+ def test_02(self):
+ self.write_bytes_with_stream()
+ file_url = self.get_temp_file_url()
+ # let the service not to remove the URL.
+ self.temp_file.RemoveFile = False
+ # close the tempfile by closing input and output.
+ self.close_temp_file()
+ # check that the file is still available.
+ read_data = self.read_directly_from_temp_file(file_url)
+ self.assertEqual(self.file_data, read_data, "Tempfile contains false data!")
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim: set shiftwidth=4 softtabstop=4 expandtab:
diff --git a/unotools/qa/unit/configpaths.cxx b/unotools/qa/unit/configpaths.cxx
new file mode 100644
index 0000000000..7d9907d9e3
--- /dev/null
+++ b/unotools/qa/unit/configpaths.cxx
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <cppunit/TestAssert.h>
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+#include <unotools/configpaths.hxx>
+
+namespace
+{
+class Test : public CppUnit::TestFixture
+{
+public:
+ void testSplitLastFromConfigurationPath()
+ {
+ {
+ OUString path, last;
+ CPPUNIT_ASSERT(!utl::splitLastFromConfigurationPath(u"", path, last));
+ CPPUNIT_ASSERT_EQUAL(OUString(""), path);
+ CPPUNIT_ASSERT_EQUAL(OUString(""), last);
+ }
+ {
+ // Already prior to 5edefc801fb48559c8064003f23d22d838710ee4 "use more string_view in
+ // unotools", and in discordance with the documentation, this returned true (but
+ // "@returns <FALSE/>, if the path was a one-level path or an invalid path"):
+ OUString path, last;
+ CPPUNIT_ASSERT(utl::splitLastFromConfigurationPath(u"/", path, last));
+ CPPUNIT_ASSERT_EQUAL(OUString(""), path);
+ CPPUNIT_ASSERT_EQUAL(OUString(""), last);
+ }
+ {
+ // Already prior to 5edefc801fb48559c8064003f23d22d838710ee4 "use more string_view in
+ // unotools", and in discordance with the documentation, this returned true (but
+ // "@returns <FALSE/>, if the path was a one-level path or an invalid path"):
+ OUString path, last;
+ CPPUNIT_ASSERT(utl::splitLastFromConfigurationPath(u"/foo", path, last));
+ CPPUNIT_ASSERT_EQUAL(OUString(""), path);
+ CPPUNIT_ASSERT_EQUAL(OUString("foo"), last);
+ }
+ {
+ // Already prior to 5edefc801fb48559c8064003f23d22d838710ee4 "use more string_view in
+ // unotools", and in discordance with the documentation, this returned true (but
+ // "@returns <FALSE/>, if the path was a one-level path or an invalid path"):
+ OUString path, last;
+ CPPUNIT_ASSERT(utl::splitLastFromConfigurationPath(u"/foo/", path, last));
+ CPPUNIT_ASSERT_EQUAL(OUString(""), path);
+ CPPUNIT_ASSERT_EQUAL(OUString("foo"), last);
+ }
+ {
+ OUString path, last;
+ CPPUNIT_ASSERT(utl::splitLastFromConfigurationPath(u"/foo/bar/baz", path, last));
+ CPPUNIT_ASSERT_EQUAL(OUString("/foo/bar"), path);
+ CPPUNIT_ASSERT_EQUAL(OUString("baz"), last);
+ }
+ {
+ // Trailing slash accepted for backwards compatibility (cf
+ // . "for backwards compatibility, ignore a final slash" comment in
+ // Data::resolvePathRepresentation, configmgr/source/data.cxx):
+ OUString path, last;
+ CPPUNIT_ASSERT(utl::splitLastFromConfigurationPath(u"/foo/bar/baz/", path, last));
+ CPPUNIT_ASSERT_EQUAL(OUString("/foo/bar"), path);
+ CPPUNIT_ASSERT_EQUAL(OUString("baz"), last);
+ }
+ {
+ OUString path, last;
+ CPPUNIT_ASSERT(utl::splitLastFromConfigurationPath(
+ u"/foo/bar/baz['abc']/baz['de&amp;f']", path, last));
+ CPPUNIT_ASSERT_EQUAL(OUString("/foo/bar/baz['abc']"), path);
+ CPPUNIT_ASSERT_EQUAL(OUString("de&f"), last);
+ }
+ {
+ OUString path, last;
+ CPPUNIT_ASSERT(!utl::splitLastFromConfigurationPath(u"foo", path, last));
+ CPPUNIT_ASSERT_EQUAL(OUString(""), path);
+ CPPUNIT_ASSERT_EQUAL(OUString("foo"), last);
+ }
+ {
+ // In accordance with the documentation, this sets last to "foo/" ("If
+ // <var>_sInPath</var> could not be parsed as a valid configuration path, this is set to
+ // <var>_sInPath</var>"):
+ OUString path, last;
+ CPPUNIT_ASSERT(!utl::splitLastFromConfigurationPath(u"foo/", path, last));
+ CPPUNIT_ASSERT_EQUAL(OUString(""), path);
+ CPPUNIT_ASSERT_EQUAL(OUString("foo/"), last);
+ }
+ {
+ // Some broken input missing a leading slash happens to be considered OK:
+ OUString path, last;
+ CPPUNIT_ASSERT(utl::splitLastFromConfigurationPath(u"foo/bar/baz", path, last));
+ CPPUNIT_ASSERT_EQUAL(OUString("foo/bar"), path);
+ CPPUNIT_ASSERT_EQUAL(OUString("baz"), last);
+ }
+ }
+
+ CPPUNIT_TEST_SUITE(Test);
+ CPPUNIT_TEST(testSplitLastFromConfigurationPath);
+ CPPUNIT_TEST_SUITE_END();
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(Test);
+}
+
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/unotools/qa/unit/testGetEnglishSearchName.cxx b/unotools/qa/unit/testGetEnglishSearchName.cxx
new file mode 100644
index 0000000000..f20f9c8d46
--- /dev/null
+++ b/unotools/qa/unit/testGetEnglishSearchName.cxx
@@ -0,0 +1,73 @@
+/* -*- 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 <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+#include <unotools/fontdefs.hxx>
+
+class Test: public CppUnit::TestFixture
+{
+public:
+ void testSingleElement();
+
+ CPPUNIT_TEST_SUITE(Test);
+ CPPUNIT_TEST(testSingleElement);
+
+ CPPUNIT_TEST_SUITE_END();
+};
+
+void Test::testSingleElement()
+{
+ // lowercase
+ OUString test1 = GetEnglishSearchFontName( u"SYMBOL" );
+ CPPUNIT_ASSERT_EQUAL( OUString("symbol"),test1);
+ //trailing whitespaces
+ test1 = GetEnglishSearchFontName( u"Symbol " );
+ CPPUNIT_ASSERT_EQUAL(OUString("symbol"),test1);
+ //no longer remove script suffixes
+ test1 = GetEnglishSearchFontName( u"Symbol(SIP)" );
+ CPPUNIT_ASSERT_EQUAL(OUString("symbol(sip)"),test1);
+ test1 = GetEnglishSearchFontName( u"CM Roman CE" );
+ CPPUNIT_ASSERT_EQUAL( OUString("cmromance"),test1);
+ //remove special characters; leave semicolon, numbers
+ test1 = GetEnglishSearchFontName( u"sy;mb?=ol129" );
+ CPPUNIT_ASSERT_EQUAL( OUString("sy;mbol129"),test1);
+
+ //transformation
+
+ //for Japanese fontname
+ // IPAMincho
+ static constexpr OUStringLiteral aIPAMincho=u"ipa\u660e\u671d";
+ OUString test_ja_JP1 = GetEnglishSearchFontName(aIPAMincho);
+ CPPUNIT_ASSERT_EQUAL( OUString("ipamincho"),test_ja_JP1);
+ // IPAGothic
+ static constexpr OUStringLiteral aIPAGothic=u"ipa\u30b4\u30b7\u30c3\u30af";
+ OUString test_ja_JP2 = GetEnglishSearchFontName(aIPAGothic);
+ CPPUNIT_ASSERT_EQUAL( OUString("ipagothic"),test_ja_JP2);
+ // HiraginoKakuGothic
+ static constexpr OUStringLiteral aHiraginoKakuGothic=u"\u30D2\u30E9\u30AE\u30CE\u89D2\u30B4\u30B7\u30C3\u30AF";
+ OUString test_ja_JP3 = GetEnglishSearchFontName(aHiraginoKakuGothic);
+ CPPUNIT_ASSERT_EQUAL( OUString("hiraginosans"),test_ja_JP3);
+ // HiraginoMincho Pro N
+ static constexpr OUStringLiteral aHiraginoMinchoProN=u"\u30D2\u30E9\u30AE\u30CE\u660E\u671Dpron";
+ OUString test_ja_JP4 = GetEnglishSearchFontName(aHiraginoMinchoProN);
+ CPPUNIT_ASSERT_EQUAL( OUString("hiraginominchopron"),test_ja_JP4);
+ // HiraginoMaruGothic Pro N
+ static constexpr OUStringLiteral aHiraginoMaruGothicProN
+ =u"\u30D2\u30E9\u30AE\u30CE\u4E38\u30B4pron";
+ OUString test_ja_JP5 = GetEnglishSearchFontName(aHiraginoMaruGothicProN);
+ CPPUNIT_ASSERT_EQUAL( OUString("hiraginomarugothicpron"),test_ja_JP5);
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(Test);
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/qa/unit/testRecodeString.cxx b/unotools/qa/unit/testRecodeString.cxx
new file mode 100644
index 0000000000..b24a4a27e0
--- /dev/null
+++ b/unotools/qa/unit/testRecodeString.cxx
@@ -0,0 +1,45 @@
+/* -*- 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 <config_options.h>
+
+#include <cppunit/TestFixture.h>
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/plugin/TestPlugIn.h>
+
+#include <unotools/fontcvt.hxx>
+#include <unotools/fontdefs.hxx>
+
+class Test : public CppUnit::TestFixture
+{
+public:
+ void testRecodeString();
+
+ CPPUNIT_TEST_SUITE(Test);
+ CPPUNIT_TEST(testRecodeString);
+
+ CPPUNIT_TEST_SUITE_END();
+};
+
+void Test::testRecodeString()
+{
+// note, the below won't work with mergelibs as the class is not visible to the linker
+#if !ENABLE_MERGELIBS
+ ConvertChar const* pConversion = ConvertChar::GetRecodeData(u"starbats", u"opensymbol");
+ OUString aStr(u"u"_ustr);
+ pConversion->RecodeString(aStr, 0, 1);
+ CPPUNIT_ASSERT_EQUAL(u""_ustr, aStr);
+#endif
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(Test);
+CPPUNIT_PLUGIN_IMPLEMENT();
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/accessibility/accessiblerelationsethelper.cxx b/unotools/source/accessibility/accessiblerelationsethelper.cxx
new file mode 100644
index 0000000000..323b12c535
--- /dev/null
+++ b/unotools/source/accessibility/accessiblerelationsethelper.cxx
@@ -0,0 +1,170 @@
+/* -*- 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 <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <o3tl/safeint.hxx>
+#include <unotools/accessiblerelationsethelper.hxx>
+#include <comphelper/sequence.hxx>
+
+using namespace ::utl;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+
+namespace
+{
+ AccessibleRelation lcl_getRelationByType( std::vector<AccessibleRelation>& raRelations, sal_Int16 aRelationType )
+ {
+ for (const auto& aRelation: raRelations)
+ {
+ if (aRelation.RelationType == aRelationType)
+ return aRelation;
+ }
+ return AccessibleRelation();
+ }
+}
+//===== internal ============================================================
+
+AccessibleRelationSetHelper::AccessibleRelationSetHelper ()
+{
+}
+
+AccessibleRelationSetHelper::AccessibleRelationSetHelper (const AccessibleRelationSetHelper& rHelper)
+ : cppu::WeakImplHelper<XAccessibleRelationSet>(rHelper),
+ maRelations(rHelper.maRelations)
+{
+}
+
+AccessibleRelationSetHelper::~AccessibleRelationSetHelper()
+{
+}
+
+//===== XAccessibleRelationSet ==============================================
+
+ /** Returns the number of relations in this relation set.
+
+ @return
+ Returns the number of relations or zero if there are none.
+ */
+sal_Int32 SAL_CALL
+ AccessibleRelationSetHelper::getRelationCount( )
+{
+ std::scoped_lock aGuard (maMutex);
+
+ return maRelations.size();
+}
+
+ /** Returns the relation of this relation set that is specified by
+ the given index.
+
+ @param nIndex
+ This index specifies the relatio to return.
+
+ @return
+ For a valid index, i.e. inside the range 0 to the number of
+ relations minus one, the returned value is the requested
+ relation. If the index is invalid then the returned relation
+ has the type INVALID.
+
+ */
+ AccessibleRelation SAL_CALL
+ AccessibleRelationSetHelper::getRelation( sal_Int32 nIndex )
+{
+ std::scoped_lock aGuard (maMutex);
+
+ if ((nIndex < 0) || (o3tl::make_unsigned(nIndex) >= maRelations.size()))
+ throw lang::IndexOutOfBoundsException();
+
+ return maRelations[nIndex];
+}
+
+ /** Tests whether the relation set contains a relation matching the
+ specified key.
+
+ @param aRelationType
+ The type of relation to look for in this set of relations. This
+ has to be one of the constants of
+ <type>AccessibleRelationType</type>.
+
+ @return
+ Returns <TRUE/> if there is a (at least one) relation of the
+ given type and <FALSE/> if there is no such relation in the set.
+ */
+sal_Bool SAL_CALL
+ AccessibleRelationSetHelper::containsRelation( sal_Int16 aRelationType )
+{
+ std::scoped_lock aGuard (maMutex);
+
+ AccessibleRelation defaultRelation; // default is INVALID
+ AccessibleRelation relationByType = lcl_getRelationByType(maRelations, aRelationType);
+ return relationByType.RelationType != defaultRelation.RelationType;
+}
+
+ /** Retrieve and return the relation with the given relation type.
+
+ @param aRelationType
+ The type of the relation to return. This has to be one of the
+ constants of <type>AccessibleRelationType</type>.
+
+ @return
+ If a relation with the given type could be found than (a copy
+ of) this relation is returned. Otherwise a relation with the
+ type INVALID is returned.
+ */
+AccessibleRelation SAL_CALL
+ AccessibleRelationSetHelper::getRelationByType( sal_Int16 aRelationType )
+{
+ std::scoped_lock aGuard (maMutex);
+
+ return lcl_getRelationByType(maRelations, aRelationType);
+}
+
+void AccessibleRelationSetHelper::AddRelation(const AccessibleRelation& rRelation)
+{
+ std::scoped_lock aGuard (maMutex);
+
+ for (auto& aRelation: maRelations)
+ {
+ if (aRelation.RelationType == rRelation.RelationType)
+ {
+ aRelation.TargetSet = comphelper::concatSequences(aRelation.TargetSet, rRelation.TargetSet);
+ return;
+ }
+ }
+ maRelations.push_back(rRelation);
+}
+
+//===== XTypeProvider =======================================================
+
+uno::Sequence< css::uno::Type> AccessibleRelationSetHelper::getTypes()
+{
+ static const uno::Sequence< css::uno::Type> aTypes {
+ cppu::UnoType<XAccessibleRelationSet>::get(),
+ cppu::UnoType<lang::XTypeProvider>::get()
+ };
+ return aTypes;
+}
+
+uno::Sequence<sal_Int8> SAL_CALL AccessibleRelationSetHelper::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/bootstrap.cxx b/unotools/source/config/bootstrap.cxx
new file mode 100644
index 0000000000..6aaac63f8f
--- /dev/null
+++ b/unotools/source/config/bootstrap.cxx
@@ -0,0 +1,720 @@
+/* -*- 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 <config_folders.h>
+
+#include <unotools/bootstrap.hxx>
+
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <osl/file.hxx>
+#include <osl/diagnose.h>
+
+#include <rtl/bootstrap.hxx>
+#include <osl/process.h>
+
+// #define this to true, if remembering defaults is not supported properly
+#define RTL_BOOTSTRAP_DEFAULTS_BROKEN true
+
+constexpr OUString BOOTSTRAP_ITEM_PRODUCT_KEY = u"ProductKey"_ustr;
+constexpr OUStringLiteral BOOTSTRAP_ITEM_VERSIONFILE = u"Location";
+constexpr OUStringLiteral BOOTSTRAP_ITEM_BUILDID = u"buildid";
+
+constexpr OUStringLiteral BOOTSTRAP_ITEM_BASEINSTALLATION = u"BRAND_BASE_DIR";
+constexpr OUStringLiteral BOOTSTRAP_ITEM_USERINSTALLATION = u"UserInstallation";
+
+constexpr OUString BOOTSTRAP_ITEM_USERDIR = u"UserDataDir"_ustr;
+
+constexpr OUStringLiteral BOOTSTRAP_DEFAULT_BASEINSTALL = u"$SYSBINDIR/..";
+
+constexpr OUString BOOTSTRAP_DIRNAME_USERDIR = u"user"_ustr;
+
+typedef char const * AsciiString;
+
+namespace utl
+{
+
+// Implementation class: Bootstrap::Impl
+
+static OUString makeImplName()
+{
+ OUString uri;
+ rtl::Bootstrap::get( "BRAND_BASE_DIR", uri);
+ return uri + "/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap");
+}
+
+class Bootstrap::Impl
+{
+ const OUString m_aImplName;
+public: // struct to cache the result of a path lookup
+ struct PathData
+ {
+ OUString path;
+ PathStatus status;
+
+ PathData()
+ : status(DATA_UNKNOWN)
+ {}
+ };
+public: // data members
+ // base install data
+ PathData aBaseInstall_;
+
+ // user install data
+ PathData aUserInstall_;
+
+ // INI files
+ PathData aBootstrapINI_;
+ PathData aVersionINI_;
+
+ // overall status
+ Status status_;
+
+public: // construction and initialization
+ Impl() : m_aImplName(makeImplName())
+ {
+ initialize();
+ }
+
+ void initialize();
+
+ // access helper
+ OUString getBootstrapValue(OUString const& _sName, OUString const& _sDefault) const;
+
+ const OUString& getImplName() const { return m_aImplName; }
+
+private: // implementation
+ bool initBaseInstallationData(rtl::Bootstrap const & _rData);
+ bool initUserInstallationData(rtl::Bootstrap const & _rData);
+};
+
+namespace
+{
+ Bootstrap::Impl& theImpl()
+ {
+ static Bootstrap::Impl SINGLETON;
+ return SINGLETON;
+ }
+}
+
+const Bootstrap::Impl& Bootstrap::data()
+{
+ return theImpl();
+}
+
+bool Bootstrap::getProcessWorkingDir(OUString &rUrl)
+{
+ rUrl.clear();
+ OUString s("$OOO_CWD");
+ rtl::Bootstrap::expandMacros(s);
+ if (s.isEmpty())
+ {
+ if (osl_getProcessWorkingDir(&rUrl.pData) == osl_Process_E_None)
+ return true;
+ }
+ else if (s[0] == '1')
+ {
+ rUrl = s.copy(1);
+ return true;
+ }
+ else if (s[0] == '2' &&
+ (osl::FileBase::getFileURLFromSystemPath(s.copy(1), rUrl) ==
+ osl::FileBase::E_None))
+ {
+ return true;
+ }
+ return false;
+}
+
+void Bootstrap::reloadData()
+{
+ theImpl().initialize();
+}
+
+// helper
+
+typedef Bootstrap::PathStatus PathStatus;
+
+sal_Unicode const cURLSeparator = '/';
+
+// path status utility function
+static PathStatus implCheckStatusOfURL(OUString const& _sURL, osl::DirectoryItem& aDirItem)
+{
+ using namespace osl;
+
+ PathStatus eStatus = Bootstrap::DATA_UNKNOWN;
+
+ if (!_sURL.isEmpty())
+ {
+ switch( DirectoryItem::get(_sURL, aDirItem) )
+ {
+ case DirectoryItem::E_None: // Success
+ eStatus = Bootstrap::PATH_EXISTS;
+ break;
+
+ case DirectoryItem::E_NOENT: // No such file or directory
+ eStatus = Bootstrap::PATH_VALID;
+ break;
+
+ case DirectoryItem::E_INVAL: // the format of the parameters was not valid
+ case DirectoryItem::E_NAMETOOLONG: // File name too long
+ case DirectoryItem::E_NOTDIR: // A component of the path prefix of path is not a directory
+ eStatus = Bootstrap::DATA_INVALID;
+ break;
+
+ // how to handle these ?
+ case DirectoryItem::E_LOOP: // Too many symbolic links encountered
+ case DirectoryItem::E_ACCES: // permission denied
+ // any other error - what to do ?
+ default:
+ eStatus = Bootstrap::DATA_UNKNOWN;
+ break;
+ }
+ }
+ else
+ {
+ eStatus = Bootstrap::DATA_MISSING;
+ }
+
+ return eStatus;
+}
+
+static bool implNormalizeURL(OUString & _sURL, osl::DirectoryItem& aDirItem)
+{
+ using namespace osl;
+
+ OSL_PRECOND(aDirItem.is(), "Opened DirItem required");
+
+ static const sal_uInt32 cosl_FileStatus_Mask = osl_FileStatus_Mask_FileURL;
+
+ FileStatus aFileStatus(cosl_FileStatus_Mask);
+
+ if (aDirItem.getFileStatus(aFileStatus) != DirectoryItem::E_None)
+ return false;
+
+ OUString aNormalizedURL = aFileStatus.getFileURL();
+
+ if (aNormalizedURL.isEmpty())
+ return false;
+
+ // #109863# sal/osl returns final slash for file URLs contradicting
+ // the URL/URI RFCs.
+ if ( !aNormalizedURL.endsWith(OUStringChar(cURLSeparator)) )
+ _sURL = aNormalizedURL;
+ else
+ _sURL = aNormalizedURL.copy( 0, aNormalizedURL.getLength()-1 );
+
+ return true;
+}
+
+static bool implEnsureAbsolute(OUString & _rsURL) // also strips embedded dots !!
+{
+ using osl::File;
+
+ OUString sBasePath;
+ OSL_VERIFY(Bootstrap::getProcessWorkingDir(sBasePath));
+
+ OUString sAbsolute;
+ if ( File::E_None == File::getAbsoluteFileURL(sBasePath, _rsURL, sAbsolute))
+ {
+ _rsURL = sAbsolute;
+ return true;
+ }
+ else
+ {
+ OSL_FAIL("Could not get absolute file URL for URL");
+ return false;
+ }
+}
+
+static bool implMakeAbsoluteURL(OUString & _rsPathOrURL)
+{
+ using namespace osl;
+
+ bool bURL;
+
+ OUString sOther;
+ // check if it already was normalized
+ if ( File::E_None == File::getSystemPathFromFileURL(_rsPathOrURL, sOther) )
+ {
+ bURL = true;
+ }
+
+ else if ( File::E_None == File::getFileURLFromSystemPath(_rsPathOrURL, sOther) )
+ {
+ _rsPathOrURL = sOther;
+ bURL = true;
+ }
+ else
+ bURL = false;
+
+ return bURL && implEnsureAbsolute(_rsPathOrURL);
+}
+
+static PathStatus dbgCheckStatusOfURL(OUString const& _sURL)
+{
+ using namespace osl;
+
+ DirectoryItem aDirItem;
+
+ return implCheckStatusOfURL(_sURL,aDirItem);
+}
+
+static PathStatus checkStatusAndNormalizeURL(OUString & _sURL)
+{
+ using namespace osl;
+
+ PathStatus eStatus = Bootstrap::DATA_UNKNOWN;
+
+ if (_sURL.isEmpty())
+ eStatus = Bootstrap::DATA_MISSING;
+
+ else if ( !implMakeAbsoluteURL(_sURL) )
+ eStatus = Bootstrap::DATA_INVALID;
+
+ else
+ {
+ DirectoryItem aDirItem;
+
+ eStatus = implCheckStatusOfURL(_sURL,aDirItem);
+
+ if (eStatus == Bootstrap::PATH_EXISTS && !implNormalizeURL(_sURL,aDirItem))
+ OSL_FAIL("Unexpected failure getting actual URL for existing object");
+ }
+ return eStatus;
+}
+
+// helpers to build and check a nested URL
+static PathStatus getDerivedPath(
+ OUString& _rURL,
+ OUString const& _aBaseURL, PathStatus _aBaseStatus,
+ std::u16string_view _sRelativeURL,
+ rtl::Bootstrap const & _rData, OUString const& _sBootstrapParameter
+ )
+{
+ OUString sDerivedURL;
+ OSL_PRECOND(!_rData.getFrom(_sBootstrapParameter,sDerivedURL),"Setting for derived path is already defined");
+ OSL_PRECOND(!_sRelativeURL.empty() && _sRelativeURL[0] != cURLSeparator,"Invalid Relative URL");
+
+ PathStatus aStatus = _aBaseStatus;
+
+ // do we have a base path ?
+ if (!_aBaseURL.isEmpty())
+ {
+ OSL_PRECOND(!_aBaseURL.endsWith(OUStringChar(cURLSeparator)), "Unexpected: base URL ends in slash");
+
+ sDerivedURL = _aBaseURL + OUStringChar(cURLSeparator) + _sRelativeURL;
+
+ // a derived (nested) URL can only exist or have a lesser status, if the parent exists
+ if (aStatus == Bootstrap::PATH_EXISTS)
+ aStatus = checkStatusAndNormalizeURL(sDerivedURL);
+
+ else // the relative appendix must be valid
+ OSL_ASSERT(aStatus != Bootstrap::PATH_VALID || dbgCheckStatusOfURL(sDerivedURL) == Bootstrap::PATH_VALID);
+
+ _rData.getFrom(_sBootstrapParameter, _rURL, sDerivedURL);
+
+ OSL_ENSURE(sDerivedURL == _rURL,"Could not set derived URL via Bootstrap default parameter");
+ SAL_WARN_IF( !(RTL_BOOTSTRAP_DEFAULTS_BROKEN || (_rData.getFrom(_sBootstrapParameter,sDerivedURL) && sDerivedURL==_rURL)),
+ "unotools.config",
+ "Use of default did not affect bootstrap value");
+ }
+ else
+ {
+ // clear the result
+ _rURL = _aBaseURL;
+
+ // if we have no data it can't be a valid path
+ OSL_ASSERT( aStatus > Bootstrap::PATH_VALID );
+ }
+
+ return aStatus;
+}
+
+static PathStatus getDerivedPath(
+ OUString& _rURL,
+ Bootstrap::Impl::PathData const& _aBaseData,
+ std::u16string_view _sRelativeURL,
+ rtl::Bootstrap const & _rData, OUString const& _sBootstrapParameter
+ )
+{
+ return getDerivedPath(_rURL,_aBaseData.path,_aBaseData.status,_sRelativeURL,_rData,_sBootstrapParameter);
+}
+
+static OUString getExecutableBaseName()
+{
+ OUString sExecutable;
+
+ if (osl_Process_E_None == osl_getExecutableFile(&sExecutable.pData))
+ {
+ // split the executable name
+ sal_Int32 nSepIndex = sExecutable.lastIndexOf(cURLSeparator);
+
+ sExecutable = sExecutable.copy(nSepIndex + 1);
+
+ // ... and get the basename (strip the extension)
+ sal_Unicode const cExtensionSep = '.';
+
+ sal_Int32 const nExtIndex = sExecutable.lastIndexOf(cExtensionSep);
+ sal_Int32 const nExtLength = sExecutable.getLength() - nExtIndex - 1;
+ if (0 < nExtIndex && nExtLength < 4)
+ sExecutable = sExecutable.copy(0,nExtIndex);
+ }
+ else
+ SAL_WARN("unotools.config", "Cannot get executable name: osl_getExecutableFile failed");
+
+ return sExecutable;
+}
+
+static Bootstrap::PathStatus updateStatus(Bootstrap::Impl::PathData & _rResult)
+{
+ _rResult.status = checkStatusAndNormalizeURL(_rResult.path);
+ return _rResult.status;
+}
+
+static Bootstrap::PathStatus implGetBootstrapFile(rtl::Bootstrap const & _rData, Bootstrap::Impl::PathData & _rBootstrapFile)
+{
+ _rData.getIniName(_rBootstrapFile.path);
+
+ return updateStatus(_rBootstrapFile);
+}
+
+static Bootstrap::PathStatus implGetVersionFile(rtl::Bootstrap const & _rData, Bootstrap::Impl::PathData & _rVersionFile)
+{
+ _rData.getFrom(BOOTSTRAP_ITEM_VERSIONFILE, _rVersionFile.path);
+
+ return updateStatus(_rVersionFile);
+}
+
+// Error reporting
+
+char const IS_MISSING[] = "is missing";
+char const IS_INVALID[] = "is corrupt";
+char const PERIOD[] = ". ";
+
+static void addFileError(OUStringBuffer& _rBuf, std::u16string_view _aPath, AsciiString _sWhat)
+{
+ std::u16string_view sSimpleFileName = _aPath.substr(1 +_aPath.rfind(cURLSeparator));
+
+ _rBuf.append("The configuration file");
+ _rBuf.append(OUString::Concat(" '") + sSimpleFileName + "' ");
+ _rBuf.appendAscii(_sWhat).append(PERIOD);
+}
+
+static void addMissingDirectoryError(OUStringBuffer& _rBuf, std::u16string_view _aPath)
+{
+ _rBuf.append(OUString::Concat("The configuration directory '") + _aPath + "' " +
+ IS_MISSING + PERIOD);
+}
+
+static void addUnexpectedError(OUStringBuffer& _rBuf, AsciiString _sExtraInfo = nullptr)
+{
+ if (nullptr == _sExtraInfo)
+ _sExtraInfo = "An internal failure occurred";
+
+ _rBuf.appendAscii(_sExtraInfo).append(PERIOD);
+}
+
+static Bootstrap::FailureCode describeError(OUStringBuffer& _rBuf, Bootstrap::Impl const& _rData)
+{
+ Bootstrap::FailureCode eErrCode = Bootstrap::INVALID_BOOTSTRAP_DATA;
+
+ _rBuf.append("The program cannot be started. ");
+
+ switch (_rData.aUserInstall_.status)
+ {
+ case Bootstrap::PATH_EXISTS:
+ switch (_rData.aBaseInstall_.status)
+ {
+ case Bootstrap::PATH_VALID:
+ addMissingDirectoryError(_rBuf, _rData.aBaseInstall_.path);
+ eErrCode = Bootstrap::MISSING_INSTALL_DIRECTORY;
+ break;
+
+ case Bootstrap::DATA_INVALID:
+ addUnexpectedError(_rBuf,"The installation path is invalid");
+ break;
+
+ case Bootstrap::DATA_MISSING:
+ addUnexpectedError(_rBuf,"The installation path is not available");
+ break;
+
+ case Bootstrap::PATH_EXISTS: // seems to be all fine (?)
+ addUnexpectedError(_rBuf,"");
+ break;
+
+ default: OSL_ASSERT(false);
+ addUnexpectedError(_rBuf);
+ break;
+ }
+ break;
+
+ case Bootstrap::PATH_VALID:
+ addMissingDirectoryError(_rBuf, _rData.aUserInstall_.path);
+ eErrCode = Bootstrap::MISSING_USER_DIRECTORY;
+ break;
+
+ // else fall through
+ case Bootstrap::DATA_INVALID:
+ if (_rData.aVersionINI_.status == Bootstrap::PATH_EXISTS)
+ {
+ addFileError(_rBuf, _rData.aVersionINI_.path, IS_INVALID);
+ eErrCode = Bootstrap::INVALID_VERSION_FILE_ENTRY;
+ break;
+ }
+ [[fallthrough]];
+
+ case Bootstrap::DATA_MISSING:
+ switch (_rData.aVersionINI_.status)
+ {
+ case Bootstrap::PATH_EXISTS:
+ addFileError(_rBuf, _rData.aVersionINI_.path, "does not support the current version");
+ eErrCode = Bootstrap::MISSING_VERSION_FILE_ENTRY;
+ break;
+
+ case Bootstrap::PATH_VALID:
+ addFileError(_rBuf, _rData.aVersionINI_.path, IS_MISSING);
+ eErrCode = Bootstrap::MISSING_VERSION_FILE;
+ break;
+
+ default:
+ switch (_rData.aBootstrapINI_.status)
+ {
+ case Bootstrap::PATH_EXISTS:
+ addFileError(_rBuf, _rData.aBootstrapINI_.path, IS_INVALID);
+
+ if (_rData.aVersionINI_.status == Bootstrap::DATA_MISSING)
+ eErrCode = Bootstrap::MISSING_BOOTSTRAP_FILE_ENTRY;
+ else
+ eErrCode = Bootstrap::INVALID_BOOTSTRAP_FILE_ENTRY;
+ break;
+
+ case Bootstrap::DATA_INVALID: OSL_ASSERT(false); [[fallthrough]];
+ case Bootstrap::PATH_VALID:
+ addFileError(_rBuf, _rData.aBootstrapINI_.path, IS_MISSING);
+ eErrCode = Bootstrap::MISSING_BOOTSTRAP_FILE;
+ break;
+
+ default:
+ addUnexpectedError(_rBuf);
+ break;
+ }
+ break;
+ }
+ break;
+
+ default: OSL_ASSERT(false);
+ addUnexpectedError(_rBuf);
+ break;
+ }
+
+ return eErrCode;
+}
+
+
+OUString Bootstrap::getProductKey()
+{
+ OUString const sDefaultProductKey = getExecutableBaseName();
+
+ return data().getBootstrapValue( BOOTSTRAP_ITEM_PRODUCT_KEY, sDefaultProductKey );
+}
+
+OUString Bootstrap::getProductKey(OUString const& _sDefault)
+{
+ return data().getBootstrapValue( BOOTSTRAP_ITEM_PRODUCT_KEY, _sDefault );
+}
+
+OUString Bootstrap::getBuildIdData(OUString const& _sDefault)
+{
+ // try to open version.ini (versionrc)
+ OUString uri;
+ rtl::Bootstrap::get( "BRAND_BASE_DIR", uri);
+ rtl::Bootstrap aData( uri + "/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("version") );
+ if ( aData.getHandle() == nullptr )
+ // version.ini (versionrc) doesn't exist
+ return _sDefault;
+
+ // read value
+ OUString sBuildId;
+ aData.getFrom(BOOTSTRAP_ITEM_BUILDID,sBuildId,_sDefault);
+ return sBuildId;
+}
+
+Bootstrap::PathStatus Bootstrap::locateBaseInstallation(OUString& _rURL)
+{
+ Impl::PathData const& aPathData = data().aBaseInstall_;
+
+ _rURL = aPathData.path;
+ return aPathData.status;
+}
+
+Bootstrap::PathStatus Bootstrap::locateUserInstallation(OUString& _rURL)
+{
+ Impl::PathData const& aPathData = data().aUserInstall_;
+
+ _rURL = aPathData.path;
+ return aPathData.status;
+}
+
+Bootstrap::PathStatus Bootstrap::locateUserData(OUString& _rURL)
+{
+ OUString const csUserDirItem(BOOTSTRAP_ITEM_USERDIR);
+
+ rtl::Bootstrap aData( data().getImplName() );
+
+ if ( aData.getFrom(csUserDirItem, _rURL) )
+ {
+ return checkStatusAndNormalizeURL(_rURL);
+ }
+ else
+ {
+ return getDerivedPath(_rURL, data().aUserInstall_ ,BOOTSTRAP_DIRNAME_USERDIR, aData, csUserDirItem);
+ }
+}
+
+Bootstrap::PathStatus Bootstrap::locateBootstrapFile(OUString& _rURL)
+{
+ Impl::PathData const& aPathData = data().aBootstrapINI_;
+
+ _rURL = aPathData.path;
+ return aPathData.status;
+}
+
+Bootstrap::PathStatus Bootstrap::locateVersionFile(OUString& _rURL)
+{
+ Impl::PathData const& aPathData = data().aVersionINI_;
+
+ _rURL = aPathData.path;
+ return aPathData.status;
+}
+
+Bootstrap::Status Bootstrap::checkBootstrapStatus(OUString& _rDiagnosticMessage, FailureCode& _rErrCode)
+{
+ Impl const& aData = data();
+
+ Status result = aData.status_;
+
+ // maybe do further checks here
+
+ OUStringBuffer sErrorBuffer;
+ if (result != DATA_OK)
+ _rErrCode = describeError(sErrorBuffer,aData);
+
+ else
+ _rErrCode = NO_FAILURE;
+
+ _rDiagnosticMessage = sErrorBuffer.makeStringAndClear();
+
+ return result;
+}
+
+
+bool Bootstrap::Impl::initBaseInstallationData(rtl::Bootstrap const & _rData)
+{
+ _rData.getFrom(BOOTSTRAP_ITEM_BASEINSTALLATION, aBaseInstall_.path, BOOTSTRAP_DEFAULT_BASEINSTALL);
+
+ bool bResult = (PATH_EXISTS == updateStatus(aBaseInstall_));
+
+ implGetBootstrapFile(_rData, aBootstrapINI_);
+
+ return bResult;
+}
+
+bool Bootstrap::Impl::initUserInstallationData(rtl::Bootstrap const & _rData)
+{
+ if (_rData.getFrom(BOOTSTRAP_ITEM_USERINSTALLATION, aUserInstall_.path))
+ {
+ updateStatus(aUserInstall_);
+ }
+ else
+ {
+ // should we do just this
+ aUserInstall_.status = DATA_MISSING;
+
+ // ... or this - look for a single-user user directory ?
+ OUString const csUserDirItem(BOOTSTRAP_ITEM_USERDIR);
+ OUString sDummy;
+ // look for $BASEINSTALLATION/user only if default UserDir setting is used
+ if (! _rData.getFrom(csUserDirItem, sDummy))
+ {
+ if ( PATH_EXISTS == getDerivedPath(sDummy, aBaseInstall_, BOOTSTRAP_DIRNAME_USERDIR, _rData, csUserDirItem) )
+ aUserInstall_ = aBaseInstall_;
+ }
+ }
+
+ bool bResult = (PATH_EXISTS == aUserInstall_.status);
+
+ implGetVersionFile(_rData, aVersionINI_);
+
+ return bResult;
+}
+
+void Bootstrap::Impl::initialize()
+{
+ rtl::Bootstrap aData( m_aImplName );
+
+ if (!initBaseInstallationData(aData))
+ {
+ status_ = INVALID_BASE_INSTALL;
+ }
+ else if (!initUserInstallationData(aData))
+ {
+ status_ = INVALID_USER_INSTALL;
+
+ if (aUserInstall_.status >= DATA_MISSING)
+ {
+ switch (aVersionINI_.status)
+ {
+ case PATH_EXISTS:
+ case PATH_VALID:
+ status_ = MISSING_USER_INSTALL;
+ break;
+
+ case DATA_INVALID:
+ case DATA_MISSING:
+ status_ = INVALID_BASE_INSTALL;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ else
+ {
+ status_ = DATA_OK;
+ }
+}
+
+OUString Bootstrap::Impl::getBootstrapValue(OUString const& _sName, OUString const& _sDefault) const
+{
+ rtl::Bootstrap aData( m_aImplName );
+
+ OUString sResult;
+ aData.getFrom(_sName,sResult,_sDefault);
+ return sResult;
+}
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/cmdoptions.cxx b/unotools/source/config/cmdoptions.cxx
new file mode 100644
index 0000000000..015637d644
--- /dev/null
+++ b/unotools/source/config/cmdoptions.cxx
@@ -0,0 +1,337 @@
+/* -*- 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 <sal/log.hxx>
+#include <unotools/cmdoptions.hxx>
+#include <unotools/configitem.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/frame/XFrame.hpp>
+#include <cppuhelper/weakref.hxx>
+
+#include "itemholder1.hxx"
+
+#include <algorithm>
+#include <unordered_map>
+
+using namespace ::utl;
+using namespace ::osl;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+constexpr OUStringLiteral ROOTNODE_CMDOPTIONS = u"Office.Commands/Execute";
+#define PATHDELIMITER "/"
+
+#define SETNODE_DISABLED "Disabled"
+
+#define PROPERTYNAME_CMD "Command"
+
+namespace {
+
+/*-****************************************************************************************************************
+ @descr support simple command option structures and operations on it
+****************************************************************************************************************-*/
+class SvtCmdOptions
+{
+ public:
+
+ // the only way to free memory!
+ void Clear()
+ {
+ m_aCommandHashMap.clear();
+ }
+
+ bool HasEntries() const
+ {
+ return ( !m_aCommandHashMap.empty() );
+ }
+
+ bool Lookup( const OUString& aCmd ) const
+ {
+ CommandHashMap::const_iterator pEntry = m_aCommandHashMap.find( aCmd );
+ return ( pEntry != m_aCommandHashMap.end() );
+ }
+
+ void AddCommand( const OUString& aCmd )
+ {
+ m_aCommandHashMap.emplace( aCmd, 0 );
+ }
+
+ private:
+ typedef std::unordered_map<OUString, sal_Int32>
+ CommandHashMap;
+
+ CommandHashMap m_aCommandHashMap;
+};
+
+std::mutex& GetOwnStaticMutex()
+{
+ static std::mutex theCommandOptionsMutex;
+ return theCommandOptionsMutex;
+}
+
+}
+
+typedef ::std::vector< css::uno::WeakReference< css::frame::XFrame > > SvtFrameVector;
+
+class SvtCommandOptions_Impl : public ConfigItem
+{
+ public:
+
+ SvtCommandOptions_Impl();
+ virtual ~SvtCommandOptions_Impl() override;
+
+ /*-****************************************************************************************************
+ @short called for notify of configmanager
+ @descr This method is called from the ConfigManager before the application ends or from the
+ PropertyChangeListener if the sub tree broadcasts changes. You must update your
+ internal values.
+
+ @seealso baseclass ConfigItem
+
+ @param "lPropertyNames" is the list of properties which should be updated.
+ *//*-*****************************************************************************************************/
+
+ virtual void Notify( const Sequence< OUString >& lPropertyNames ) override;
+
+ /*-****************************************************************************************************
+ @short base implementation of public interface for "SvtDynamicMenuOptions"!
+ @descr These class is used as static member of "SvtDynamicMenuOptions" ...
+ => The code exist only for one time and isn't duplicated for every instance!
+ *//*-*****************************************************************************************************/
+
+ bool HasEntriesDisabled() const;
+ bool LookupDisabled( const OUString& ) const;
+ void EstablishFrameCallback(const css::uno::Reference< css::frame::XFrame >& xFrame);
+
+ private:
+
+ virtual void ImplCommit() override;
+
+ /*-****************************************************************************************************
+ @short return list of key names of our configuration management which represent our module tree
+ @descr This method returns the current list of key names! We need it to get needed values from our
+ configuration management and support dynamical menu item lists!
+ @param "nDisabledCount", returns count of menu entries for "new"
+ @return A list of configuration key names is returned.
+ *//*-*****************************************************************************************************/
+
+ Sequence< OUString > impl_GetPropertyNames();
+
+ private:
+ SvtCmdOptions m_aDisabledCommands;
+ SvtFrameVector m_lFrames;
+};
+
+// constructor
+
+SvtCommandOptions_Impl::SvtCommandOptions_Impl()
+ // Init baseclasses first
+ : ConfigItem( ROOTNODE_CMDOPTIONS )
+ // Init member then...
+{
+ // Get names and values of all accessible menu entries and fill internal structures.
+ // See impl_GetPropertyNames() for further information.
+ Sequence< OUString > lNames = impl_GetPropertyNames ();
+ Sequence< Any > lValues = GetProperties ( lNames );
+
+ // Safe impossible cases.
+ // We need values from ALL configuration keys.
+ // Follow assignment use order of values in relation to our list of key names!
+ DBG_ASSERT( !(lNames.getLength()!=lValues.getLength()), "SvtCommandOptions_Impl::SvtCommandOptions_Impl()\nI miss some values of configuration keys!\n" );
+
+ // Copy values from list in right order to our internal member.
+ // Attention: List for names and values have an internal construction pattern!
+ sal_Int32 nItem = 0;
+ OUString sCmd;
+
+ // Get names/values for disabled commands.
+ for( nItem=0; nItem < lNames.getLength(); ++nItem )
+ {
+ // Currently only one value
+ lValues[nItem] >>= sCmd;
+ m_aDisabledCommands.AddCommand( sCmd );
+ }
+
+/*TODO: Not used in the moment! see Notify() ...
+ // Enable notification mechanism of our baseclass.
+ // We need it to get information about changes outside these class on our used configuration keys! */
+ Sequence<OUString> aNotifySeq { "Disabled" };
+ EnableNotification( aNotifySeq, true );
+}
+
+// destructor
+
+SvtCommandOptions_Impl::~SvtCommandOptions_Impl()
+{
+ assert(!IsModified()); // should have been committed
+}
+
+// public method
+
+void SvtCommandOptions_Impl::Notify( const Sequence< OUString >& )
+{
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+
+ Sequence< OUString > lNames = impl_GetPropertyNames ();
+ Sequence< Any > lValues = GetProperties ( lNames );
+
+ // Safe impossible cases.
+ // We need values from ALL configuration keys.
+ // Follow assignment use order of values in relation to our list of key names!
+ DBG_ASSERT( !(lNames.getLength()!=lValues.getLength()), "SvtCommandOptions_Impl::Notify()\nI miss some values of configuration keys!\n" );
+
+ // Copy values from list in right order to our internal member.
+ // Attention: List for names and values have an internal construction pattern!
+ sal_Int32 nItem = 0;
+ OUString sCmd;
+
+ m_aDisabledCommands.Clear();
+
+ // Get names/values for disabled commands.
+ for( nItem=0; nItem < lNames.getLength(); ++nItem )
+ {
+ // Currently only one value
+ lValues[nItem] >>= sCmd;
+ m_aDisabledCommands.AddCommand( sCmd );
+ }
+
+ // don't forget to update all existing frames and her might cached dispatch objects!
+ // But look for already killed frames. We hold weak references instead of hard ones ...
+ for (SvtFrameVector::iterator pIt = m_lFrames.begin(); pIt != m_lFrames.end(); )
+ {
+ css::uno::Reference< css::frame::XFrame > xFrame(pIt->get(), css::uno::UNO_QUERY);
+ if (xFrame.is())
+ {
+ aGuard.unlock(); // because we can call back into ourself
+ xFrame->contextChanged();
+ aGuard.lock();
+ ++pIt;
+ }
+ else
+ pIt = m_lFrames.erase(pIt);
+ }
+}
+
+// public method
+
+void SvtCommandOptions_Impl::ImplCommit()
+{
+ SAL_WARN("unotools.config","SvtCommandOptions_Impl::ImplCommit(): Not implemented yet!");
+}
+
+// public method
+
+bool SvtCommandOptions_Impl::HasEntriesDisabled() const
+{
+ return m_aDisabledCommands.HasEntries();
+}
+
+// public method
+
+bool SvtCommandOptions_Impl::LookupDisabled( const OUString& aCommand ) const
+{
+ return m_aDisabledCommands.Lookup( aCommand );
+}
+
+// public method
+
+void SvtCommandOptions_Impl::EstablishFrameCallback(const css::uno::Reference< css::frame::XFrame >& xFrame)
+{
+ // check if frame already exists inside list
+ // ignore double registrations
+ // every frame must be notified one times only!
+ css::uno::WeakReference< css::frame::XFrame > xWeak(xFrame);
+ SvtFrameVector::const_iterator pIt = ::std::find(m_lFrames.begin(), m_lFrames.end(), xWeak);
+ if (pIt == m_lFrames.end())
+ m_lFrames.push_back(xWeak);
+}
+
+// private method
+
+Sequence< OUString > SvtCommandOptions_Impl::impl_GetPropertyNames()
+{
+ // First get ALL names of current existing list items in configuration!
+ Sequence< OUString > lDisabledItems = GetNodeNames( SETNODE_DISABLED, utl::ConfigNameFormat::LocalPath );
+
+ // Expand all keys
+ for (OUString& rItem : asNonConstRange(lDisabledItems))
+ rItem = SETNODE_DISABLED PATHDELIMITER + rItem + PATHDELIMITER PROPERTYNAME_CMD;
+
+ // Return result.
+ return lDisabledItems;
+}
+
+namespace {
+
+std::weak_ptr<SvtCommandOptions_Impl> g_pCommandOptions;
+
+}
+
+SvtCommandOptions::SvtCommandOptions()
+{
+ // Global access, must be guarded (multithreading!).
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+
+ m_pImpl = g_pCommandOptions.lock();
+ if( !m_pImpl )
+ {
+ m_pImpl = std::make_shared<SvtCommandOptions_Impl>();
+ g_pCommandOptions = m_pImpl;
+ aGuard.unlock(); // because holdConfigItem will call this constructor
+ ItemHolder1::holdConfigItem(EItem::CmdOptions);
+ }
+}
+
+SvtCommandOptions::~SvtCommandOptions()
+{
+ // Global access, must be guarded (multithreading!)
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+
+ m_pImpl.reset();
+}
+
+// public method
+
+bool SvtCommandOptions::HasEntriesDisabled() const
+{
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+ return m_pImpl->HasEntriesDisabled();
+}
+
+// public method
+
+bool SvtCommandOptions::LookupDisabled( const OUString& aCommandURL ) const
+{
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+ return m_pImpl->LookupDisabled( aCommandURL );
+}
+
+// public method
+
+void SvtCommandOptions::EstablishFrameCallback(const css::uno::Reference< css::frame::XFrame >& xFrame)
+{
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+ m_pImpl->EstablishFrameCallback(xFrame);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/compatibility.cxx b/unotools/source/config/compatibility.cxx
new file mode 100644
index 0000000000..a5c8aec158
--- /dev/null
+++ b/unotools/source/config/compatibility.cxx
@@ -0,0 +1,415 @@
+/* -*- 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/compatibility.hxx>
+#include <unotools/configitem.hxx>
+#include <unotools/syslocale.hxx>
+#include <tools/debug.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/lang/Locale.hpp>
+#include <i18nlangtag/languagetag.hxx>
+
+#include "itemholder1.hxx"
+
+#include <algorithm>
+
+using namespace ::utl;
+using namespace ::osl;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+constexpr OUStringLiteral ROOTNODE_OPTIONS = u"Office.Compatibility";
+#define PATHDELIMITER "/"
+#define SETNODE_ALLFILEFORMATS "AllFileFormats"
+
+SvtCompatibilityEntry::SvtCompatibilityEntry()
+ : m_aPropertyValue( SvtCompatibilityEntry::getElementCount() )
+{
+ /* Should be in the start. Do not remove it. */
+ setValue<OUString>( Index::Name, OUString() );
+ setValue<OUString>( Index::Module, OUString() );
+
+ /* Editable list of default values. Sync it with the SvtCompatibilityEntry::Index enum class. */
+ setValue<bool>( Index::AddSpacing, false );
+ setValue<bool>( Index::AddSpacingAtPages, false );
+ setValue<bool>( Index::UseOurTabStops, false );
+ setValue<bool>( Index::NoExtLeading, false );
+ setValue<bool>( Index::UseLineSpacing, false );
+ setValue<bool>( Index::AddTableSpacing, false );
+ setValue<bool>( Index::UseObjectPositioning, false );
+ setValue<bool>( Index::UseOurTextWrapping, false );
+ setValue<bool>( Index::ConsiderWrappingStyle, false );
+ setValue<bool>( Index::ExpandWordSpace, true );
+ setValue<bool>( Index::ProtectForm, false );
+ setValue<bool>( Index::MsWordTrailingBlanks, false );
+ setValue<bool>( Index::SubtractFlysAnchoredAtFlys, false );
+ setValue<bool>( Index::EmptyDbFieldHidesPara, true );
+ setValue<bool>( Index::AddTableLineSpacing, false );
+ setValue<bool>( Index::UseVariableWidthNBSP, false );
+}
+
+OUString SvtCompatibilityEntry::getName( const Index rIdx )
+{
+ static const char* sPropertyName[] =
+ {
+ /* Should be in the start. Do not remove it. */
+ "Name",
+ "Module",
+
+ /* Editable list of compatibility option names. Sync it with the SvtCompatibilityEntry::Index enum class. */
+ "AddSpacing",
+ "AddSpacingAtPages",
+ "UseOurTabStopFormat",
+ "NoExternalLeading",
+ "UseLineSpacing",
+ "AddTableSpacing",
+ "UseObjectPositioning",
+ "UseOurTextWrapping",
+ "ConsiderWrappingStyle",
+ "ExpandWordSpace",
+ "ProtectForm",
+ "MsWordCompTrailingBlanks",
+ "SubtractFlysAnchoredAtFlys",
+ "EmptyDbFieldHidesPara",
+ "UseVariableWidthNBSP",
+
+ "AddTableLineSpacing" // Must be the last one
+ };
+
+ /* Size of sPropertyName array not equal size of the SvtCompatibilityEntry::Index enum class */
+ assert( std::size(sPropertyName) == SvtCompatibilityEntry::getElementCount() );
+
+ return OUString::createFromAscii( sPropertyName[ static_cast<int>(rIdx) ] );
+}
+
+/*-****************************************************************************************************************
+ @descr support simple menu structures and operations on it
+****************************************************************************************************************-*/
+
+/*-****************************************************************************************************
+ @short base implementation of public interface for "SvtCompatibilityOptions"!
+ @descr These class is used as static member of "SvtCompatibilityOptions" ...
+ => The code exist only for one time and isn't duplicated for every instance!
+*//*-*****************************************************************************************************/
+class SvtCompatibilityOptions_Impl : public ConfigItem
+{
+ public:
+ SvtCompatibilityOptions_Impl();
+ virtual ~SvtCompatibilityOptions_Impl() override;
+
+ void AppendItem( const SvtCompatibilityEntry& aItem );
+ void Clear();
+
+ void SetDefault( SvtCompatibilityEntry::Index rIdx, bool rValue );
+ bool GetDefault( SvtCompatibilityEntry::Index rIdx ) const;
+
+ const std::vector< SvtCompatibilityEntry > & GetOptions() const { return m_aOptions; }
+ bool GetOptionReadOnly( SvtCompatibilityEntry::Index rIdx ) const;
+ bool GetDefaultOptionReadOnly( SvtCompatibilityEntry::Index rIdx ) const;
+ bool HaveDefaultReadOnlyOption() const;
+
+ /*-****************************************************************************************************
+ @short called for notify of configmanager
+ @descr This method is called from the ConfigManager before the application ends or from the
+ PropertyChangeListener if the sub tree broadcasts changes. You must update your
+ internal values.
+
+ @seealso baseclass ConfigItem
+
+ @param "lPropertyNames" is the list of properties which should be updated.
+ *//*-*****************************************************************************************************/
+ virtual void Notify( const Sequence< OUString >& lPropertyNames ) override;
+
+ private:
+ virtual void ImplCommit() override;
+
+ /*-****************************************************************************************************
+ @short return list of key names of our configuration management which represent one module tree
+ @descr These methods return the current list of key names! We need it to get needed values from our
+ configuration management and support dynamical menu item lists!
+ @return A list of configuration key names is returned.
+ *//*-*****************************************************************************************************/
+ Sequence< OUString > impl_GetPropertyNames( Sequence< OUString >& rItems );
+
+ private:
+ std::vector< SvtCompatibilityEntry > m_aOptions;
+ SvtCompatibilityEntry m_aDefOptions;
+};
+
+SvtCompatibilityOptions_Impl::SvtCompatibilityOptions_Impl() : ConfigItem( ROOTNODE_OPTIONS )
+{
+ // Get names and values of all accessible menu entries and fill internal structures.
+ // See impl_GetPropertyNames() for further information.
+ Sequence< OUString > lNodes;
+ Sequence< OUString > lNames = impl_GetPropertyNames( lNodes );
+ Sequence< Any > lValues = GetProperties( lNames );
+ Sequence< sal_Bool > lReadOnly = GetReadOnlyStates( lNames );
+
+ // Safe impossible cases.
+ // We need values from ALL configuration keys.
+ // Follow assignment use order of values in relation to our list of key names!
+ DBG_ASSERT( !( lNames.getLength()!=lValues.getLength() ), "SvtCompatibilityOptions_Impl::SvtCompatibilityOptions_Impl()\nI miss some values of configuration keys!\n" );
+
+ // Get names/values for new menu.
+ // 4 subkeys for every item!
+ bool bDefaultFound = false;
+ sal_Int32 nDestStep = 0;
+ for ( const auto& rNode : std::as_const(lNodes) )
+ {
+ SvtCompatibilityEntry aItem;
+
+ aItem.setValue<OUString>( SvtCompatibilityEntry::Index::Name, rNode );
+
+ for ( int i = static_cast<int>(SvtCompatibilityEntry::Index::Module); i < static_cast<int>(SvtCompatibilityEntry::Index::INVALID); ++i )
+ {
+ aItem.setValue( SvtCompatibilityEntry::Index(i), lValues[ nDestStep ] );
+ aItem.setPropertyReadOnly( SvtCompatibilityEntry::Index(i), lReadOnly[ nDestStep ] );
+ nDestStep++;
+ }
+
+ m_aOptions.push_back( aItem );
+
+ if ( !bDefaultFound && aItem.getValue<OUString>( SvtCompatibilityEntry::Index::Name ) == SvtCompatibilityEntry::DEFAULT_ENTRY_NAME )
+ {
+ SvtSysLocale aSysLocale;
+ css::lang::Locale aLocale = aSysLocale.GetLanguageTag().getLocale();
+ if ( aLocale.Language == "zh" || aLocale.Language == "ja" || aLocale.Language == "ko" )
+ aItem.setValue<bool>( SvtCompatibilityEntry::Index::ExpandWordSpace, false );
+
+ m_aDefOptions = aItem;
+ bDefaultFound = true;
+ }
+ }
+}
+
+SvtCompatibilityOptions_Impl::~SvtCompatibilityOptions_Impl()
+{
+ assert( !IsModified() ); // should have been committed
+}
+
+void SvtCompatibilityOptions_Impl::AppendItem( const SvtCompatibilityEntry& aItem )
+{
+ m_aOptions.push_back( aItem );
+
+ // default item reset?
+ if ( aItem.getValue<OUString>( SvtCompatibilityEntry::Index::Name ) == SvtCompatibilityEntry::DEFAULT_ENTRY_NAME )
+ m_aDefOptions = aItem;
+
+ SetModified();
+}
+
+void SvtCompatibilityOptions_Impl::Clear()
+{
+ m_aOptions.clear();
+
+ SetModified();
+}
+
+void SvtCompatibilityOptions_Impl::SetDefault( SvtCompatibilityEntry::Index rIdx, bool rValue )
+{
+ /* Are not set Name and Module */
+ assert( rIdx != SvtCompatibilityEntry::Index::Name && rIdx != SvtCompatibilityEntry::Index::Module );
+
+ m_aDefOptions.setValue<bool>( rIdx, rValue );
+}
+
+bool SvtCompatibilityOptions_Impl::GetDefault( SvtCompatibilityEntry::Index rIdx ) const
+{
+ /* Are not set Name and Module */
+ assert( rIdx != SvtCompatibilityEntry::Index::Name && rIdx != SvtCompatibilityEntry::Index::Module );
+
+ return m_aDefOptions.getValue<bool>( rIdx );
+}
+
+void SvtCompatibilityOptions_Impl::Notify( const Sequence< OUString >& )
+{
+ SAL_WARN( "unotools.config", "SvtCompatibilityOptions_Impl::Notify() Not implemented yet! I don't know how I can handle a dynamical list of unknown properties ..." );
+}
+
+void SvtCompatibilityOptions_Impl::ImplCommit()
+{
+ // Write all properties!
+ // Delete complete set first.
+ ClearNodeSet( SETNODE_ALLFILEFORMATS );
+
+ Sequence< PropertyValue > lPropertyValues( SvtCompatibilityEntry::getElementCount() - 1 );
+ auto lPropertyValuesRange = asNonConstRange(lPropertyValues);
+ sal_uInt32 nNewCount = m_aOptions.size();
+ for ( sal_uInt32 nItem = 0; nItem < nNewCount; ++nItem )
+ {
+ SvtCompatibilityEntry aItem = m_aOptions[ nItem ];
+ OUString sNode = SETNODE_ALLFILEFORMATS PATHDELIMITER + aItem.getValue<OUString>( SvtCompatibilityEntry::Index::Name ) + PATHDELIMITER;
+
+ for ( int i = static_cast<int>(SvtCompatibilityEntry::Index::Module); i < static_cast<int>(SvtCompatibilityEntry::Index::INVALID); ++i )
+ {
+ lPropertyValuesRange[ i - 1 ].Name = sNode + SvtCompatibilityEntry::getName( SvtCompatibilityEntry::Index(i) );
+ lPropertyValuesRange[ i - 1 ].Value = aItem.getValue( SvtCompatibilityEntry::Index(i) );
+ }
+
+ SetSetProperties( SETNODE_ALLFILEFORMATS, lPropertyValues );
+ }
+}
+
+Sequence< OUString > SvtCompatibilityOptions_Impl::impl_GetPropertyNames( Sequence< OUString >& rItems )
+{
+ // First get ALL names of current existing list items in configuration!
+ rItems = GetNodeNames( SETNODE_ALLFILEFORMATS );
+
+ // expand list to result list ...
+ Sequence< OUString > lProperties( rItems.getLength() * ( SvtCompatibilityEntry::getElementCount() - 1 ) );
+ auto lPropertiesRange = asNonConstRange(lProperties);
+
+ sal_Int32 nDestStep = 0;
+ // Copy entries to destination and expand every item with 2 supported sub properties.
+ for ( const auto& rItem : std::as_const(rItems) )
+ {
+ OUString sFixPath = SETNODE_ALLFILEFORMATS PATHDELIMITER + rItem + PATHDELIMITER;
+ for ( int i = static_cast<int>(SvtCompatibilityEntry::Index::Module); i < static_cast<int>(SvtCompatibilityEntry::Index::INVALID); ++i )
+ {
+ lPropertiesRange[ nDestStep ] = sFixPath + SvtCompatibilityEntry::getName( SvtCompatibilityEntry::Index(i) );
+ nDestStep++;
+ }
+ }
+
+ // Return result.
+ return lProperties;
+}
+
+bool SvtCompatibilityOptions_Impl::GetOptionReadOnly(SvtCompatibilityEntry::Index rIdx) const
+{
+ /* Are not set Name and Module */
+ assert(rIdx != SvtCompatibilityEntry::Index::Name && rIdx != SvtCompatibilityEntry::Index::Module);
+
+ bool bReadOnly = false;
+
+ sal_uInt32 nNewCount = m_aOptions.size();
+ for (sal_uInt32 nItem = 0; nItem < nNewCount; ++nItem)
+ {
+ SvtCompatibilityEntry aItem = m_aOptions[nItem];
+ if (aItem.getValue<OUString>(SvtCompatibilityEntry::Index::Name) == SvtCompatibilityEntry::USER_ENTRY_NAME)
+ {
+ bReadOnly = aItem.getPropertyReadOnly(rIdx);
+ break;
+ }
+ }
+
+ return bReadOnly;
+}
+
+bool SvtCompatibilityOptions_Impl::GetDefaultOptionReadOnly(SvtCompatibilityEntry::Index rIdx) const
+{
+ /* Are not set Name and Module */
+ assert(rIdx != SvtCompatibilityEntry::Index::Name && rIdx != SvtCompatibilityEntry::Index::Module);
+
+ return m_aDefOptions.getPropertyReadOnly(rIdx);
+}
+
+bool SvtCompatibilityOptions_Impl::HaveDefaultReadOnlyOption() const
+{
+ return m_aDefOptions.haveReadOnlyProperty();
+}
+
+namespace
+{
+ std::weak_ptr<SvtCompatibilityOptions_Impl> theOptions;
+}
+
+SvtCompatibilityOptions::SvtCompatibilityOptions()
+{
+ // Global access, must be guarded (multithreading!).
+ MutexGuard aGuard( GetOwnStaticMutex() );
+
+ m_pImpl = theOptions.lock();
+ if ( !m_pImpl )
+ {
+ m_pImpl = std::make_shared<SvtCompatibilityOptions_Impl>();
+ theOptions = m_pImpl;
+ ItemHolder1::holdConfigItem( EItem::Compatibility );
+ }
+}
+
+SvtCompatibilityOptions::~SvtCompatibilityOptions()
+{
+ // Global access, must be guarded (multithreading!)
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ m_pImpl.reset();
+}
+
+void SvtCompatibilityOptions::AppendItem( const SvtCompatibilityEntry& aItem )
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ m_pImpl->AppendItem( aItem );
+}
+
+void SvtCompatibilityOptions::Clear()
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ m_pImpl->Clear();
+}
+
+void SvtCompatibilityOptions::SetDefault( SvtCompatibilityEntry::Index rIdx, bool rValue )
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ m_pImpl->SetDefault( rIdx, rValue );
+}
+
+bool SvtCompatibilityOptions::GetDefault( SvtCompatibilityEntry::Index rIdx ) const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetDefault( rIdx );
+}
+
+std::vector< SvtCompatibilityEntry > SvtCompatibilityOptions::GetList() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+
+ return m_pImpl->GetOptions();
+}
+
+bool SvtCompatibilityOptions::GetPropertyReadOnly( SvtCompatibilityEntry::Index rIdx ) const
+{
+ MutexGuard aGuard(GetOwnStaticMutex());
+
+ return m_pImpl->GetOptionReadOnly(rIdx);
+}
+
+bool SvtCompatibilityOptions::GetDefaultPropertyReadOnly( SvtCompatibilityEntry::Index rIdx ) const
+{
+ MutexGuard aGuard(GetOwnStaticMutex());
+
+ return m_pImpl->GetDefaultOptionReadOnly(rIdx);
+}
+
+bool SvtCompatibilityOptions::HaveDefaultReadOnlyProperty() const
+{
+ MutexGuard aGuard(GetOwnStaticMutex());
+
+ return m_pImpl->HaveDefaultReadOnlyOption();
+}
+
+Mutex& SvtCompatibilityOptions::GetOwnStaticMutex()
+{
+ static osl::Mutex aMutex;
+ return aMutex;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/configitem.cxx b/unotools/source/config/configitem.cxx
new file mode 100644
index 0000000000..d0b6a04a69
--- /dev/null
+++ b/unotools/source/config/configitem.cxx
@@ -0,0 +1,1190 @@
+/* -*- 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 <sal/log.hxx>
+#include <unotools/configitem.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/configpaths.hxx>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/util/XChangesListener.hpp>
+#include <com/sun/star/util/XChangesNotifier.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/configuration/XTemplateContainer.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <o3tl/deleter.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/sequence.hxx>
+#include <comphelper/solarmutex.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <utility>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::util;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::container;
+using namespace com::sun::star::configuration;
+
+/*
+ The ConfigChangeListener_Impl receives notifications from the configuration about changes that
+ have happened. It forwards this notification to the ConfigItem it knows a pParent by calling its
+ "CallNotify" method. As ConfigItems are most probably not thread safe, the SolarMutex is acquired
+ before doing so.
+*/
+
+namespace utl{
+ class ConfigChangeListener_Impl : public cppu::WeakImplHelper
+ <
+ css::util::XChangesListener
+ >
+ {
+ public:
+ ConfigItem* pParent;
+ const Sequence< OUString > aPropertyNames;
+ ConfigChangeListener_Impl(ConfigItem& rItem, const Sequence< OUString >& rNames);
+
+ //XChangesListener
+ virtual void SAL_CALL changesOccurred( const ChangesEvent& Event ) override;
+
+ //XEventListener
+ virtual void SAL_CALL disposing( const EventObject& Source ) override;
+ };
+}
+
+namespace {
+
+class ValueCounter_Impl
+{
+ sal_Int16& rCnt;
+public:
+ explicit ValueCounter_Impl(sal_Int16& rCounter)
+ : rCnt(rCounter)
+ {
+ rCnt++;
+ }
+ ~ValueCounter_Impl()
+ {
+ OSL_ENSURE(rCnt>0, "RefCount < 0 ??");
+ rCnt--;
+ }
+};
+
+}
+
+ConfigChangeListener_Impl::ConfigChangeListener_Impl(
+ ConfigItem& rItem, const Sequence< OUString >& rNames) :
+ pParent(&rItem),
+ aPropertyNames(rNames)
+{
+}
+
+void ConfigChangeListener_Impl::changesOccurred( const ChangesEvent& rEvent )
+{
+ Sequence<OUString> aChangedNames(rEvent.Changes.getLength());
+ OUString* pNames = aChangedNames.getArray();
+
+ sal_Int32 nNotify = 0;
+ for(const auto& rElementChange : rEvent.Changes)
+ {
+ OUString sTemp;
+ rElementChange.Accessor >>= sTemp;
+ //true if the path is completely correct or if it is longer
+ //i.e ...Print/Content/Graphic and .../Print
+ bool bFound = std::any_of(aPropertyNames.begin(), aPropertyNames.end(),
+ [&sTemp](const OUString& rCheckPropertyName) { return isPrefixOfConfigurationPath(sTemp, rCheckPropertyName); });
+ if(bFound)
+ pNames[nNotify++] = sTemp;
+ }
+ if( nNotify )
+ {
+ ::comphelper::SolarMutex *pMutex = ::comphelper::SolarMutex::get();
+ if ( pMutex )
+ {
+ osl::Guard<comphelper::SolarMutex> aMutexGuard( pMutex );
+ aChangedNames.realloc(nNotify);
+ pParent->CallNotify(aChangedNames);
+ }
+ }
+}
+
+void ConfigChangeListener_Impl::disposing( const EventObject& /*rSource*/ )
+{
+ pParent->RemoveChangesListener();
+}
+
+ConfigItem::ConfigItem(OUString aSubTree, ConfigItemMode nSetMode ) :
+ sSubTree(std::move(aSubTree)),
+ m_nMode(nSetMode),
+ m_bIsModified(false),
+ m_bEnableInternalNotification(false),
+ m_nInValueChange(0)
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return;
+
+ if (nSetMode & ConfigItemMode::ReleaseTree)
+ ConfigManager::getConfigManager().addConfigItem(*this);
+ else
+ m_xHierarchyAccess = ConfigManager::getConfigManager().addConfigItem(*this);
+}
+
+ConfigItem::~ConfigItem()
+{
+ suppress_fun_call_w_exception(RemoveChangesListener());
+ ConfigManager::getConfigManager().removeConfigItem(*this);
+}
+
+void ConfigItem::CallNotify( const css::uno::Sequence<OUString>& rPropertyNames )
+{
+ // the call is forwarded to the virtual Notify() method
+ // it is pure virtual, so all classes deriving from ConfigItem have to decide how they
+ // want to notify listeners
+ if(m_nInValueChange <= 0 || m_bEnableInternalNotification)
+ Notify(rPropertyNames);
+}
+
+void ConfigItem::impl_packLocalizedProperties( const Sequence< OUString >& lInNames ,
+ const Sequence< Any >& lInValues ,
+ Sequence< Any >& lOutValues )
+{
+ // This method should be called for special AllLocales ConfigItem-mode only!
+
+ sal_Int32 nSourceCounter; // used to step during input lists
+ sal_Int32 nSourceSize; // marks end of loop over input lists
+ sal_Int32 nDestinationCounter; // actual position in output lists
+ sal_Int32 nPropertyCounter; // counter of inner loop for Sequence< PropertyValue >
+ sal_Int32 nPropertiesSize; // marks end of inner loop
+ Sequence< OUString > lPropertyNames; // list of all locales for localized entry
+ Sequence< PropertyValue > lProperties; // localized values of a configuration entry packed for return
+ Reference< XInterface > xLocalizedNode; // if cfg entry is localized ... lInValues contains an XInterface!
+
+ // Optimise follow algorithm ... A LITTLE BIT :-)
+ // There exist two different possibilities:
+ // i ) There exist no localized entries ... => size of lOutValues will be the same like lInNames/lInValues!
+ // ii) There exist some (mostly one or two) localized entries ... => size of lOutValues will be the same like lInNames/lInValues!
+ // ... Why? If a localized value exist - the any is filled with an XInterface object (is a SetNode-service).
+ // We read all his child nodes and pack it into Sequence< PropertyValue >.
+ // The result list we pack into the return any. We never change size of lists!
+ nSourceSize = lInNames.getLength();
+ lOutValues.realloc( nSourceSize );
+ auto plOutValues = lOutValues.getArray();
+
+ // Algorithm:
+ // Copy all names and values from in to out lists.
+ // Look for special localized entries ... You can detect it as "XInterface" packed into an Any.
+ // Use this XInterface-object to read all localized values and pack it into Sequence< PropertValue >.
+ // Add this list to out lists then.
+
+ nDestinationCounter = 0;
+ for( nSourceCounter=0; nSourceCounter<nSourceSize; ++nSourceCounter )
+ {
+ // If item a special localized one ... convert and pack it ...
+ if( lInValues[nSourceCounter].getValueTypeName() == "com.sun.star.uno.XInterface" )
+ {
+ lInValues[nSourceCounter] >>= xLocalizedNode;
+ Reference< XNameContainer > xSetAccess( xLocalizedNode, UNO_QUERY );
+ if( xSetAccess.is() )
+ {
+ lPropertyNames = xSetAccess->getElementNames();
+ nPropertiesSize = lPropertyNames.getLength();
+ lProperties.realloc( nPropertiesSize );
+ auto plProperties = lProperties.getArray();
+
+ for( nPropertyCounter=0; nPropertyCounter<nPropertiesSize; ++nPropertyCounter )
+ {
+ plProperties[nPropertyCounter].Name = lPropertyNames[nPropertyCounter];
+ OUString sLocaleValue;
+ xSetAccess->getByName( lPropertyNames[nPropertyCounter] ) >>= sLocaleValue;
+ plProperties[nPropertyCounter].Value <<= sLocaleValue;
+ }
+
+ plOutValues[nDestinationCounter] <<= lProperties;
+ }
+ }
+ // ... or copy normal items to return lists directly.
+ else
+ {
+ plOutValues[nDestinationCounter] = lInValues[nSourceCounter];
+ }
+ ++nDestinationCounter;
+ }
+}
+
+void ConfigItem::impl_unpackLocalizedProperties( const Sequence< OUString >& lInNames ,
+ const Sequence< Any >& lInValues ,
+ Sequence< OUString >& lOutNames ,
+ Sequence< Any >& lOutValues)
+{
+ // This method should be called for special AllLocales ConfigItem-mode only!
+
+ sal_Int32 nSourceSize; // marks end of loop over input lists
+ sal_Int32 nDestinationCounter; // actual position in output lists
+ sal_Int32 nPropertiesSize; // marks end of inner loop
+ OUString sNodeName; // base name of node ( e.g. "UIName/" ) ... expand to locale ( e.g. "UIName/de" )
+ Sequence< PropertyValue > lProperties; // localized values of a configuration entry gotten from lInValues-Any
+
+ // Optimise follow algorithm ... A LITTLE BIT :-)
+ // There exist two different possibilities:
+ // i ) There exist no localized entries ... => size of lOutNames/lOutValues will be the same like lInNames/lInValues!
+ // ii) There exist some (mostly one or two) localized entries ... => size of lOutNames/lOutValues will be some bytes greater then lInNames/lInValues.
+ // => I think we should make it fast for i). ii) is a special case and mustn't be SOOOO... fast.
+ // We should reserve same space for output list like input ones first.
+ // Follow algorithm looks for these borders and change it for ii) only!
+ // It will be faster then a "realloc()" call in every loop ...
+ nSourceSize = lInNames.getLength();
+
+ lOutNames.realloc ( nSourceSize );
+ auto plOutNames = lOutNames.getArray();
+ lOutValues.realloc ( nSourceSize );
+ auto plOutValues = lOutValues.getArray();
+
+ // Algorithm:
+ // Copy all names and values from const to return lists.
+ // Look for special localized entries ... You can detect it as Sequence< PropertyValue > packed into an Any.
+ // Split it ... insert PropertyValue.Name to lOutNames and PropertyValue.Value to lOutValues.
+
+ nDestinationCounter = 0;
+ for( sal_Int32 nSourceCounter=0; nSourceCounter<nSourceSize; ++nSourceCounter )
+ {
+ // If item a special localized one ... split it and insert his parts to output lists ...
+ if( lInValues[nSourceCounter].getValueType() == cppu::UnoType<Sequence<PropertyValue>>::get() )
+ {
+ lInValues[nSourceCounter] >>= lProperties;
+ nPropertiesSize = lProperties.getLength();
+
+ sNodeName = lInNames[nSourceCounter] + "/";
+
+ if( (nDestinationCounter+nPropertiesSize) > lOutNames.getLength() )
+ {
+ lOutNames.realloc ( nDestinationCounter+nPropertiesSize );
+ plOutNames = lOutNames.getArray();
+ lOutValues.realloc ( nDestinationCounter+nPropertiesSize );
+ plOutValues = lOutValues.getArray();
+ }
+
+ for( const auto& rProperty : std::as_const(lProperties) )
+ {
+ plOutNames [nDestinationCounter] = sNodeName + rProperty.Name;
+ plOutValues[nDestinationCounter] = rProperty.Value;
+ ++nDestinationCounter;
+ }
+ }
+ // ... or copy normal items to return lists directly.
+ else
+ {
+ if( (nDestinationCounter+1) > lOutNames.getLength() )
+ {
+ lOutNames.realloc ( nDestinationCounter+1 );
+ plOutNames = lOutNames.getArray();
+ lOutValues.realloc ( nDestinationCounter+1 );
+ plOutValues = lOutValues.getArray();
+ }
+
+ plOutNames [nDestinationCounter] = lInNames [nSourceCounter];
+ plOutValues[nDestinationCounter] = lInValues[nSourceCounter];
+ ++nDestinationCounter;
+ }
+ }
+}
+
+Sequence< sal_Bool > ConfigItem::GetReadOnlyStates(const css::uno::Sequence< OUString >& rNames)
+{
+ sal_Int32 i;
+
+ // size of return list is fix!
+ // Every item must match to length of incoming name list.
+ sal_Int32 nCount = rNames.getLength();
+ Sequence< sal_Bool > lStates(nCount);
+ sal_Bool* plStates = lStates.getArray();
+
+ // We must be sure to return a valid information every time!
+ // Set default to non readonly... similar to the configuration handling of this property.
+ std::fill_n(plStates, lStates.getLength(), false);
+
+ // no access - no information...
+ Reference< XHierarchicalNameAccess > xHierarchyAccess = GetTree();
+ if (!xHierarchyAccess.is())
+ return lStates;
+
+ for (i=0; i<nCount; ++i)
+ {
+ try
+ {
+ OUString sName = rNames[i];
+ OUString sPath;
+ OUString sProperty;
+
+ (void)::utl::splitLastFromConfigurationPath(sName,sPath,sProperty);
+ if (sPath.isEmpty() && sProperty.isEmpty())
+ {
+ OSL_FAIL("ConfigItem::IsReadonly() split failed");
+ continue;
+ }
+
+ Reference< XInterface > xNode;
+ Reference< XPropertySet > xSet;
+ Reference< XPropertySetInfo > xInfo;
+ if (!sPath.isEmpty())
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(sPath);
+ if (!(aNode >>= xNode) || !xNode.is())
+ {
+ OSL_FAIL("ConfigItem::IsReadonly() no set available");
+ continue;
+ }
+ }
+ else
+ {
+ xNode = xHierarchyAccess;
+ }
+
+ xSet.set(xNode, UNO_QUERY);
+ if (xSet.is())
+ {
+ xInfo = xSet->getPropertySetInfo();
+ OSL_ENSURE(xInfo.is(), "ConfigItem::IsReadonly() getPropertySetInfo failed ...");
+ }
+ else
+ {
+ xInfo.set(xNode, UNO_QUERY);
+ OSL_ENSURE(xInfo.is(), "ConfigItem::IsReadonly() UNO_QUERY failed ...");
+ }
+
+ if (!xInfo.is())
+ {
+ OSL_FAIL("ConfigItem::IsReadonly() no prop info available");
+ continue;
+ }
+
+ Property aProp = xInfo->getPropertyByName(sProperty);
+ plStates[i] = (aProp.Attributes & PropertyAttribute::READONLY) == PropertyAttribute::READONLY;
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ return lStates;
+}
+
+Sequence< Any > ConfigItem::GetProperties(const Sequence< OUString >& rNames)
+{
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(xHierarchyAccess.is())
+ return GetProperties(xHierarchyAccess, rNames,
+ (m_nMode & ConfigItemMode::AllLocales ) == ConfigItemMode::AllLocales);
+ return Sequence< Any >(rNames.getLength());
+}
+
+Sequence< Any > ConfigItem::GetProperties(
+ css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
+ const Sequence< OUString >& rNames,
+ bool bAllLocales)
+{
+ Sequence< Any > aRet(rNames.getLength());
+ const OUString* pNames = rNames.getConstArray();
+ Any* pRet = aRet.getArray();
+ for(int i = 0; i < rNames.getLength(); i++)
+ {
+ try
+ {
+ pRet[i] = xHierarchyAccess->getByHierarchicalName(pNames[i]);
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION(
+ "unotools.config",
+ "ignoring XHierarchicalNameAccess " << pNames[i]);
+ }
+ }
+
+ // In special mode "ALL_LOCALES" we must convert localized values to Sequence< PropertyValue >.
+ if(bAllLocales)
+ {
+ Sequence< Any > lValues;
+ impl_packLocalizedProperties( rNames, aRet, lValues );
+ aRet = lValues;
+ }
+ return aRet;
+}
+
+bool ConfigItem::PutProperties( const Sequence< OUString >& rNames,
+ const Sequence< Any>& rValues)
+{
+ ValueCounter_Impl aCounter(m_nInValueChange);
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ Reference<XNameReplace> xTopNodeReplace(xHierarchyAccess, UNO_QUERY);
+ bool bRet = xHierarchyAccess.is() && xTopNodeReplace.is();
+ if(bRet)
+ {
+ Sequence< OUString > lNames;
+ Sequence< Any > lValues;
+ const OUString* pNames = nullptr;
+ const Any* pValues = nullptr;
+ sal_Int32 nNameCount;
+ if(( m_nMode & ConfigItemMode::AllLocales ) == ConfigItemMode::AllLocales )
+ {
+ // If ConfigItem works in "ALL_LOCALES"-mode ... we must support a Sequence< PropertyValue >
+ // as value of a localized configuration entry!
+ // How we can do that?
+ // We must split all PropertyValues to "Sequence< OUString >" AND "Sequence< Any >"!
+ impl_unpackLocalizedProperties( rNames, rValues, lNames, lValues );
+ pNames = lNames.getConstArray ();
+ pValues = lValues.getConstArray ();
+ nNameCount = lNames.getLength ();
+ }
+ else
+ {
+ // This is the normal mode ...
+ // Use given input lists directly.
+ pNames = rNames.getConstArray ();
+ pValues = rValues.getConstArray ();
+ nNameCount = rNames.getLength ();
+ }
+ for(int i = 0; i < nNameCount; i++)
+ {
+ try
+ {
+ OUString sNode, sProperty;
+ if (splitLastFromConfigurationPath(pNames[i],sNode, sProperty))
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(sNode);
+
+ Reference<XNameAccess> xNodeAcc;
+ aNode >>= xNodeAcc;
+ Reference<XNameReplace> xNodeReplace(xNodeAcc, UNO_QUERY);
+ Reference<XNameContainer> xNodeCont (xNodeAcc, UNO_QUERY);
+
+ bool bExist = (xNodeAcc.is() && xNodeAcc->hasByName(sProperty));
+ if (bExist && xNodeReplace.is())
+ xNodeReplace->replaceByName(sProperty, pValues[i]);
+ else
+ if (!bExist && xNodeCont.is())
+ xNodeCont->insertByName(sProperty, pValues[i]);
+ else
+ bRet = false;
+ }
+ else //direct value
+ {
+ xTopNodeReplace->replaceByName(sProperty, pValues[i]);
+ }
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from PutProperties");
+ }
+ }
+ try
+ {
+ Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
+ xBatch->commitChanges();
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
+ }
+ }
+
+ return bRet;
+}
+
+bool ConfigItem::PutProperties(
+ css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
+ const Sequence< OUString >& rNames,
+ const Sequence< Any>& rValues,
+ bool bAllLocales)
+{
+ Reference<XNameReplace> xTopNodeReplace(xHierarchyAccess, UNO_QUERY);
+ bool bRet = xTopNodeReplace.is();
+ if(bRet)
+ {
+ Sequence< OUString > lNames;
+ Sequence< Any > lValues;
+ const OUString* pNames = nullptr;
+ const Any* pValues = nullptr;
+ sal_Int32 nNameCount;
+ if(bAllLocales)
+ {
+ // If ConfigItem works in "ALL_LOCALES"-mode ... we must support a Sequence< PropertyValue >
+ // as value of a localized configuration entry!
+ // How we can do that?
+ // We must split all PropertyValues to "Sequence< OUString >" AND "Sequence< Any >"!
+ impl_unpackLocalizedProperties( rNames, rValues, lNames, lValues );
+ pNames = lNames.getConstArray ();
+ pValues = lValues.getConstArray ();
+ nNameCount = lNames.getLength ();
+ }
+ else
+ {
+ // This is the normal mode ...
+ // Use given input lists directly.
+ pNames = rNames.getConstArray ();
+ pValues = rValues.getConstArray ();
+ nNameCount = rNames.getLength ();
+ }
+ for(int i = 0; i < nNameCount; i++)
+ {
+ try
+ {
+ OUString sNode, sProperty;
+ if (splitLastFromConfigurationPath(pNames[i],sNode, sProperty))
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(sNode);
+
+ Reference<XNameAccess> xNodeAcc;
+ aNode >>= xNodeAcc;
+ Reference<XNameReplace> xNodeReplace(xNodeAcc, UNO_QUERY);
+ Reference<XNameContainer> xNodeCont (xNodeAcc, UNO_QUERY);
+
+ bool bExist = (xNodeAcc.is() && xNodeAcc->hasByName(sProperty));
+ if (bExist && xNodeReplace.is())
+ xNodeReplace->replaceByName(sProperty, pValues[i]);
+ else
+ if (!bExist && xNodeCont.is())
+ xNodeCont->insertByName(sProperty, pValues[i]);
+ else
+ bRet = false;
+ }
+ else //direct value
+ {
+ xTopNodeReplace->replaceByName(sProperty, pValues[i]);
+ }
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from PutProperties");
+ }
+ }
+ try
+ {
+ Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
+ xBatch->commitChanges();
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
+ }
+ }
+
+ return bRet;
+}
+
+void ConfigItem::DisableNotification()
+{
+ OSL_ENSURE( xChangeLstnr.is(), "ConfigItem::DisableNotification: notifications not enabled currently!" );
+ RemoveChangesListener();
+}
+
+bool ConfigItem::EnableNotification(const Sequence< OUString >& rNames,
+ bool bEnableInternalNotification )
+{
+ OSL_ENSURE(!(m_nMode & ConfigItemMode::ReleaseTree), "notification in ConfigItemMode::ReleaseTree mode not possible");
+ m_bEnableInternalNotification = bEnableInternalNotification;
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ Reference<XChangesNotifier> xChgNot(xHierarchyAccess, UNO_QUERY);
+ if(!xChgNot.is())
+ return false;
+
+ OSL_ENSURE(!xChangeLstnr.is(), "EnableNotification already called");
+ if(xChangeLstnr.is())
+ xChgNot->removeChangesListener( xChangeLstnr );
+ bool bRet = true;
+
+ try
+ {
+ xChangeLstnr = new ConfigChangeListener_Impl(*this, rNames);
+ xChgNot->addChangesListener( xChangeLstnr );
+ }
+ catch (const RuntimeException&)
+ {
+ bRet = false;
+ }
+ return bRet;
+}
+
+void ConfigItem::RemoveChangesListener()
+{
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(!xHierarchyAccess.is())
+ return;
+
+ Reference<XChangesNotifier> xChgNot(xHierarchyAccess, UNO_QUERY);
+ if(xChgNot.is() && xChangeLstnr.is())
+ {
+ try
+ {
+ xChgNot->removeChangesListener( xChangeLstnr );
+ xChangeLstnr = nullptr;
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+}
+
+static void lcl_normalizeLocalNames(Sequence< OUString >& _rNames, ConfigNameFormat _eFormat, Reference<XInterface> const& _xParentNode)
+{
+ switch (_eFormat)
+ {
+ case ConfigNameFormat::LocalNode:
+ // unaltered - this is our input format
+ break;
+
+ case ConfigNameFormat::LocalPath:
+ {
+ Reference<XTemplateContainer> xTypeContainer(_xParentNode, UNO_QUERY);
+ if (xTypeContainer.is())
+ {
+ OUString sTypeName = xTypeContainer->getElementTemplateName();
+ sTypeName = sTypeName.copy(sTypeName.lastIndexOf('/')+1);
+
+ std::transform(std::cbegin(_rNames), std::cend(_rNames), _rNames.getArray(),
+ [&sTypeName](const OUString& rName) -> OUString { return wrapConfigurationElementName(rName,sTypeName); });
+ }
+ else
+ {
+ Reference<XServiceInfo> xSVI(_xParentNode, UNO_QUERY);
+ if (xSVI.is() && xSVI->supportsService("com.sun.star.configuration.SetAccess"))
+ {
+ std::transform(std::cbegin(_rNames), std::cend(_rNames), _rNames.getArray(),
+ [](const OUString& rName) -> OUString { return wrapConfigurationElementName(rName); });
+ }
+ }
+ }
+ break;
+
+ }
+}
+
+Sequence< OUString > ConfigItem::GetNodeNames(const OUString& rNode)
+{
+ ConfigNameFormat const eDefaultFormat = ConfigNameFormat::LocalNode; // CONFIG_NAME_DEFAULT;
+
+ return GetNodeNames(rNode, eDefaultFormat);
+}
+
+Sequence< OUString > ConfigItem::GetNodeNames(const OUString& rNode, ConfigNameFormat eFormat)
+{
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(xHierarchyAccess.is())
+ return GetNodeNames(xHierarchyAccess, rNode, eFormat);
+ return Sequence< OUString >();
+}
+
+Sequence< OUString > ConfigItem::GetNodeNames(
+ css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
+ const OUString& rNode,
+ ConfigNameFormat eFormat)
+{
+ Sequence< OUString > aRet;
+ try
+ {
+ Reference<XNameAccess> xCont;
+ if(!rNode.isEmpty())
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
+ aNode >>= xCont;
+ }
+ else
+ xCont.set(xHierarchyAccess, UNO_QUERY);
+ if(xCont.is())
+ {
+ aRet = xCont->getElementNames();
+ lcl_normalizeLocalNames(aRet,eFormat,xCont);
+ }
+
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from GetNodeNames");
+ }
+ return aRet;
+}
+
+bool ConfigItem::ClearNodeSet(const OUString& rNode)
+{
+ ValueCounter_Impl aCounter(m_nInValueChange);
+ bool bRet = false;
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(xHierarchyAccess.is())
+ bRet = ClearNodeSet(xHierarchyAccess, rNode);
+ return bRet;
+}
+
+bool ConfigItem::ClearNodeSet(
+ css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
+ const OUString& rNode)
+{
+ bool bRet = false;
+ try
+ {
+ Reference<XNameContainer> xCont;
+ if(!rNode.isEmpty())
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
+ aNode >>= xCont;
+ }
+ else
+ xCont.set(xHierarchyAccess, UNO_QUERY);
+ if(!xCont.is())
+ return false;
+ const Sequence< OUString > aNames = xCont->getElementNames();
+ Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
+ for(const OUString& rName : aNames)
+ {
+ try
+ {
+ xCont->removeByName(rName);
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from removeByName");
+ }
+ }
+ xBatch->commitChanges();
+ bRet = true;
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from ClearNodeSet");
+ }
+ return bRet;
+}
+
+bool ConfigItem::ClearNodeElements(const OUString& rNode, Sequence< OUString > const & rElements)
+{
+ ValueCounter_Impl aCounter(m_nInValueChange);
+ bool bRet = false;
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(xHierarchyAccess.is())
+ {
+ try
+ {
+ Reference<XNameContainer> xCont;
+ if(!rNode.isEmpty())
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
+ aNode >>= xCont;
+ }
+ else
+ xCont.set(xHierarchyAccess, UNO_QUERY);
+ if(!xCont.is())
+ return false;
+ try
+ {
+ for(const OUString& rElement : rElements)
+ {
+ xCont->removeByName(rElement);
+ }
+ Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
+ xBatch->commitChanges();
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges()");
+ }
+ bRet = true;
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from GetNodeNames()");
+ }
+ }
+ return bRet;
+}
+
+static OUString lcl_extractSetPropertyName( const OUString& rInPath, std::u16string_view rPrefix )
+{
+ OUString const sSubPath = dropPrefixFromConfigurationPath( rInPath, rPrefix);
+ return extractFirstFromConfigurationPath( sSubPath );
+}
+
+static
+Sequence< OUString > lcl_extractSetPropertyNames( const Sequence< PropertyValue >& rValues, std::u16string_view rPrefix )
+{
+ Sequence< OUString > aSubNodeNames(rValues.getLength());
+ OUString* pSubNodeNames = aSubNodeNames.getArray();
+
+ OUString sLastSubNode;
+ sal_Int32 nSubIndex = 0;
+
+ for(const PropertyValue& rProperty : rValues)
+ {
+ OUString const sSubPath = dropPrefixFromConfigurationPath( rProperty.Name, rPrefix);
+ OUString const sSubNode = extractFirstFromConfigurationPath( sSubPath );
+
+ if(sLastSubNode != sSubNode)
+ {
+ pSubNodeNames[nSubIndex++] = sSubNode;
+ }
+
+ sLastSubNode = sSubNode;
+ }
+ aSubNodeNames.realloc(nSubIndex);
+
+ return aSubNodeNames;
+}
+
+// Add or change properties
+bool ConfigItem::SetSetProperties(
+ const OUString& rNode, const Sequence< PropertyValue >& rValues)
+{
+ ValueCounter_Impl aCounter(m_nInValueChange);
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(!xHierarchyAccess.is())
+ return true;
+ return SetSetProperties(xHierarchyAccess, rNode, rValues);
+}
+
+// Add or change properties
+bool ConfigItem::SetSetProperties(
+ css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
+ const OUString& rNode, const Sequence< PropertyValue >& rValues)
+{
+ bool bRet = true;
+ Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
+ try
+ {
+ Reference<XNameContainer> xCont;
+ if(!rNode.isEmpty())
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
+ aNode >>= xCont;
+ }
+ else
+ xCont.set(xHierarchyAccess, UNO_QUERY);
+ if(!xCont.is())
+ return false;
+
+ Reference<XSingleServiceFactory> xFac(xCont, UNO_QUERY);
+
+ if(xFac.is())
+ {
+ const Sequence< OUString > aSubNodeNames = lcl_extractSetPropertyNames(rValues, rNode);
+
+ for(const auto& rSubNodeName : aSubNodeNames)
+ {
+ if(!xCont->hasByName(rSubNodeName))
+ {
+ Reference<XInterface> xInst = xFac->createInstance();
+ Any aVal; aVal <<= xInst;
+ xCont->insertByName(rSubNodeName, aVal);
+ }
+ //set values
+ }
+ try
+ {
+ xBatch->commitChanges();
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges()");
+ }
+
+ const PropertyValue* pProperties = rValues.getConstArray();
+
+ Sequence< OUString > aSetNames(rValues.getLength());
+ OUString* pSetNames = aSetNames.getArray();
+
+ Sequence< Any> aSetValues(rValues.getLength());
+ Any* pSetValues = aSetValues.getArray();
+
+ bool bEmptyNode = rNode.isEmpty();
+ for(sal_Int32 k = 0; k < rValues.getLength(); k++)
+ {
+ pSetNames[k] = pProperties[k].Name.copy( bEmptyNode ? 1 : 0);
+ pSetValues[k] = pProperties[k].Value;
+ }
+ bRet = PutProperties(xHierarchyAccess, aSetNames, aSetValues, /*bAllLocales*/false);
+ }
+ else
+ {
+ //if no factory is available then the node contains basic data elements
+ for(const PropertyValue& rValue : rValues)
+ {
+ try
+ {
+ OUString sSubNode = lcl_extractSetPropertyName( rValue.Name, rNode );
+
+ if(xCont->hasByName(sSubNode))
+ xCont->replaceByName(sSubNode, rValue.Value);
+ else
+ xCont->insertByName(sSubNode, rValue.Value);
+
+ OSL_ENSURE( xHierarchyAccess->hasByHierarchicalName(rValue.Name),
+ "Invalid config path" );
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from insert/replaceByName()");
+ }
+ }
+ xBatch->commitChanges();
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from SetSetProperties");
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool ConfigItem::ReplaceSetProperties(
+ const OUString& rNode, const Sequence< PropertyValue >& rValues)
+{
+ ValueCounter_Impl aCounter(m_nInValueChange);
+ bool bRet = true;
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(xHierarchyAccess.is())
+ bRet = ReplaceSetProperties(xHierarchyAccess, rNode, rValues,
+ ( m_nMode & ConfigItemMode::AllLocales ) == ConfigItemMode::AllLocales);
+ return bRet;
+}
+
+bool ConfigItem::ReplaceSetProperties(
+ css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
+ const OUString& rNode,
+ const Sequence< PropertyValue >& rValues,
+ bool bAllLocales)
+{
+ bool bRet = true;
+ Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
+ try
+ {
+ Reference<XNameContainer> xCont;
+ if(!rNode.isEmpty())
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
+ aNode >>= xCont;
+ }
+ else
+ xCont.set(xHierarchyAccess, UNO_QUERY);
+ if(!xCont.is())
+ return false;
+
+ // JB: Change: now the same name handling for sets of simple values
+ const Sequence< OUString > aSubNodeNames = lcl_extractSetPropertyNames(rValues, rNode);
+
+ Reference<XSingleServiceFactory> xFac(xCont, UNO_QUERY);
+ const bool isSimpleValueSet = !xFac.is();
+
+ //remove unknown members first
+ {
+ const Sequence<OUString> aContainerSubNodes = xCont->getElementNames();
+
+ for(const OUString& rContainerSubNode : aContainerSubNodes)
+ {
+ bool bFound = comphelper::findValue(aSubNodeNames, rContainerSubNode) != -1;
+ if(!bFound)
+ try
+ {
+ xCont->removeByName(rContainerSubNode);
+ }
+ catch (const Exception&)
+ {
+ if (isSimpleValueSet)
+ {
+ try
+ {
+ // #i37322#: fallback action: replace with <void/>
+ xCont->replaceByName(rContainerSubNode, Any());
+ // fallback successful: continue looping
+ continue;
+ }
+ catch (Exception &)
+ {} // propagate original exception, if fallback fails
+ }
+ throw;
+ }
+ }
+ try { xBatch->commitChanges(); }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
+ }
+ }
+
+ if(xFac.is()) // !isSimpleValueSet
+ {
+ for(const OUString& rSubNodeName : aSubNodeNames)
+ {
+ if(!xCont->hasByName(rSubNodeName))
+ {
+ //create if not available
+ Reference<XInterface> xInst = xFac->createInstance();
+ Any aVal; aVal <<= xInst;
+ xCont->insertByName(rSubNodeName, aVal);
+ }
+ }
+ try { xBatch->commitChanges(); }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
+ }
+
+ const PropertyValue* pProperties = rValues.getConstArray();
+
+ Sequence< OUString > aSetNames(rValues.getLength());
+ OUString* pSetNames = aSetNames.getArray();
+
+ Sequence< Any> aSetValues(rValues.getLength());
+ Any* pSetValues = aSetValues.getArray();
+
+ bool bEmptyNode = rNode.isEmpty();
+ for(sal_Int32 k = 0; k < rValues.getLength(); k++)
+ {
+ pSetNames[k] = pProperties[k].Name.copy( bEmptyNode ? 1 : 0);
+ pSetValues[k] = pProperties[k].Value;
+ }
+ bRet = PutProperties(xHierarchyAccess, aSetNames, aSetValues, bAllLocales);
+ }
+ else
+ {
+ //if no factory is available then the node contains basic data elements
+ for(const PropertyValue& rValue : rValues)
+ {
+ try
+ {
+ OUString sSubNode = lcl_extractSetPropertyName( rValue.Name, rNode );
+
+ if(xCont->hasByName(sSubNode))
+ xCont->replaceByName(sSubNode, rValue.Value);
+ else
+ xCont->insertByName(sSubNode, rValue.Value);
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from insert/replaceByName");
+ }
+ }
+ xBatch->commitChanges();
+ }
+ }
+ catch (const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from ReplaceSetProperties");
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool ConfigItem::AddNode(const OUString& rNode, const OUString& rNewNode)
+{
+ ValueCounter_Impl aCounter(m_nInValueChange);
+ bool bRet = true;
+ Reference<XHierarchicalNameAccess> xHierarchyAccess = GetTree();
+ if(xHierarchyAccess.is())
+ {
+ Reference<XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
+ try
+ {
+ Reference<XNameContainer> xCont;
+ if(!rNode.isEmpty())
+ {
+ Any aNode = xHierarchyAccess->getByHierarchicalName(rNode);
+ aNode >>= xCont;
+ }
+ else
+ xCont.set(xHierarchyAccess, UNO_QUERY);
+ if(!xCont.is())
+ return false;
+
+ Reference<XSingleServiceFactory> xFac(xCont, UNO_QUERY);
+
+ if(xFac.is())
+ {
+ if(!xCont->hasByName(rNewNode))
+ {
+ Reference<XInterface> xInst = xFac->createInstance();
+ Any aVal; aVal <<= xInst;
+ xCont->insertByName(rNewNode, aVal);
+ }
+ try
+ {
+ xBatch->commitChanges();
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from commitChanges");
+ }
+ }
+ else
+ {
+ //if no factory is available then the node contains basic data elements
+ try
+ {
+ if(!xCont->hasByName(rNewNode))
+ xCont->insertByName(rNewNode, Any());
+ }
+ catch (css::uno::Exception &)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "Exception from AddNode");
+ }
+ }
+ xBatch->commitChanges();
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools.config");
+ bRet = false;
+ }
+ }
+ return bRet;
+}
+
+
+void ConfigItem::SetModified()
+{
+ m_bIsModified = true;
+}
+
+void ConfigItem::ClearModified()
+{
+ m_bIsModified = false;
+}
+
+Reference< XHierarchicalNameAccess> ConfigItem::GetTree()
+{
+ Reference< XHierarchicalNameAccess> xRet;
+ if (utl::ConfigManager::IsFuzzing())
+ return xRet;
+ if(!m_xHierarchyAccess.is())
+ xRet = ConfigManager::acquireTree(*this);
+ else
+ xRet = m_xHierarchyAccess;
+ return xRet;
+}
+
+void ConfigItem::Commit()
+{
+ ImplCommit();
+ ClearModified();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/configmgr.cxx b/unotools/source/config/configmgr.cxx
new file mode 100644
index 0000000000..e19c7c694a
--- /dev/null
+++ b/unotools/source/config/configmgr.cxx
@@ -0,0 +1,193 @@
+/* -*- 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 <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <officecfg/Setup.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <unotools/configitem.hxx>
+#include <unotools/configmgr.hxx>
+#include <comphelper/processfactory.hxx>
+
+namespace {
+
+class RegisterConfigItemHelper {
+public:
+ RegisterConfigItemHelper(
+ utl::ConfigManager & manager, utl::ConfigItem & item):
+ manager_(manager), item_(&item)
+ {
+ manager.registerConfigItem(item_);
+ }
+
+ ~RegisterConfigItemHelper() {
+ if (item_ != nullptr) {
+ manager_.removeConfigItem(*item_);
+ }
+ }
+
+ void keep() { item_ = nullptr; }
+
+private:
+ utl::ConfigManager & manager_;
+ utl::ConfigItem * item_;
+
+ RegisterConfigItemHelper(const RegisterConfigItemHelper&) = delete;
+ RegisterConfigItemHelper& operator=(const RegisterConfigItemHelper&) = delete;
+};
+
+css::uno::Reference< css::lang::XMultiServiceFactory >
+getConfigurationProvider() {
+ return css::configuration::theDefaultProvider::get( comphelper::getProcessComponentContext() );
+}
+
+}
+
+OUString utl::ConfigManager::getAboutBoxProductVersion() {
+ return officecfg::Setup::Product::ooSetupVersionAboutBox::get();
+}
+
+OUString utl::ConfigManager::getAboutBoxProductVersionSuffix() {
+ return officecfg::Setup::Product::ooSetupVersionAboutBoxSuffix::get();
+}
+
+OUString utl::ConfigManager::getDefaultCurrency() {
+ return officecfg::Setup::L10N::ooSetupCurrency::get();
+}
+
+OUString utl::ConfigManager::getUILocale() {
+ return officecfg::Setup::L10N::ooLocale::get();
+}
+
+OUString utl::ConfigManager::getWorkLocale() {
+ return officecfg::Setup::L10N::ooSetupSystemLocale::get();
+}
+
+OUString utl::ConfigManager::getProductExtension() {
+ return officecfg::Setup::Product::ooSetupExtension::get();
+}
+
+OUString utl::ConfigManager::getProductName() {
+ return officecfg::Setup::Product::ooName::get();
+}
+
+OUString utl::ConfigManager::getProductVersion() {
+ return officecfg::Setup::Product::ooSetupVersion::get();
+}
+
+OUString utl::ConfigManager::getVendor() {
+ return officecfg::Setup::Product::ooVendor::get();
+}
+
+void utl::ConfigManager::storeConfigItems() {
+ getConfigManager().doStoreConfigItems();
+}
+
+utl::ConfigManager & utl::ConfigManager::getConfigManager() {
+ static utl::ConfigManager theConfigManager;
+ return theConfigManager;
+}
+
+css::uno::Reference< css::container::XHierarchicalNameAccess >
+utl::ConfigManager::acquireTree(utl::ConfigItem const & item) {
+ css::uno::Sequence< css::uno::Any > args{ css::uno::Any(css::beans::NamedValue(
+ "nodepath",
+ css::uno::Any("/org.openoffice." + item.GetSubTreeName()))) };
+ if (item.GetMode() & ConfigItemMode::AllLocales) {
+ args.realloc(2);
+ args.getArray()[1] <<= css::beans::NamedValue("locale", css::uno::Any(OUString("*")));
+ }
+ return css::uno::Reference< css::container::XHierarchicalNameAccess >(
+ getConfigurationProvider()->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationUpdateAccess",
+ args),
+ css::uno::UNO_QUERY_THROW);
+}
+
+css::uno::Reference< css::container::XHierarchicalNameAccess >
+utl::ConfigManager::acquireTree(std::u16string_view rSubTreeName) {
+ css::uno::Sequence< css::uno::Any > args{ css::uno::Any(css::beans::NamedValue(
+ "nodepath",
+ css::uno::Any(OUString::Concat(u"/org.openoffice.") + rSubTreeName))) };
+ return css::uno::Reference< css::container::XHierarchicalNameAccess >(
+ getConfigurationProvider()->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationUpdateAccess",
+ args),
+ css::uno::UNO_QUERY_THROW);
+}
+
+utl::ConfigManager::ConfigManager() {}
+
+utl::ConfigManager::~ConfigManager() {
+ SAL_WARN_IF(!items_.empty(), "unotools.config", "ConfigManager not empty");
+}
+
+css::uno::Reference< css::container::XHierarchicalNameAccess >
+utl::ConfigManager::addConfigItem(utl::ConfigItem & item) {
+ RegisterConfigItemHelper reg(*this, item);
+ css::uno::Reference< css::container::XHierarchicalNameAccess > tree(
+ acquireTree(item));
+ reg.keep();
+ return tree;
+}
+
+void utl::ConfigManager::removeConfigItem(utl::ConfigItem & item) {
+ std::erase(items_, &item);
+}
+
+void utl::ConfigManager::registerConfigItem(utl::ConfigItem * item) {
+ assert(item != nullptr);
+ items_.push_back(item);
+}
+
+void utl::ConfigManager::doStoreConfigItems() {
+ for (auto const& item : items_)
+ {
+ if (item->IsModified()) {
+ item->Commit();
+ item->ClearModified();
+ }
+ }
+}
+
+static bool bIsFuzzing = false;
+
+#if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
+bool utl::ConfigManager::IsFuzzing()
+{
+ return bIsFuzzing;
+}
+#endif
+
+void utl::ConfigManager::EnableFuzzing()
+{
+ bIsFuzzing = true;
+ LanguageTag::disable_lt_tag_parse();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/confignode.cxx b/unotools/source/config/confignode.cxx
new file mode 100644
index 0000000000..93b8953207
--- /dev/null
+++ b/unotools/source/config/confignode.cxx
@@ -0,0 +1,562 @@
+/* -*- 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/confignode.hxx>
+#include <unotools/configpaths.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <com/sun/star/util/XStringEscape.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <comphelper/namedvaluecollection.hxx>
+
+namespace utl
+{
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::util;
+ using namespace ::com::sun::star::beans;
+ using namespace ::com::sun::star::container;
+ using namespace ::com::sun::star::configuration;
+
+ //= OConfigurationNode
+
+ OConfigurationNode::OConfigurationNode(const Reference< XInterface >& _rxNode )
+ :m_bEscapeNames(false)
+ {
+ OSL_ENSURE(_rxNode.is(), "OConfigurationNode::OConfigurationNode: invalid node interface!");
+ if (_rxNode.is())
+ {
+ // collect all interfaces necessary
+ m_xHierarchyAccess.set(_rxNode, UNO_QUERY);
+ m_xDirectAccess.set(_rxNode, UNO_QUERY);
+
+ // reset _all_ interfaces if _one_ of them is not supported
+ if (!m_xHierarchyAccess.is() || !m_xDirectAccess.is())
+ {
+ m_xHierarchyAccess = nullptr;
+ m_xDirectAccess = nullptr;
+ }
+
+ // now for the non-critical interfaces
+ m_xReplaceAccess.set(_rxNode, UNO_QUERY);
+ m_xContainerAccess.set(_rxNode, UNO_QUERY);
+ }
+
+ Reference< XComponent > xConfigNodeComp(m_xDirectAccess, UNO_QUERY);
+ if (xConfigNodeComp.is())
+ startComponentListening(xConfigNodeComp);
+
+ if (isValid())
+ m_bEscapeNames = isSetNode() && Reference< XStringEscape >::query(m_xDirectAccess).is();
+ }
+
+ OConfigurationNode::OConfigurationNode(const OConfigurationNode& _rSource)
+ : OEventListenerAdapter()
+ , m_xHierarchyAccess(_rSource.m_xHierarchyAccess)
+ , m_xDirectAccess(_rSource.m_xDirectAccess)
+ , m_xReplaceAccess(_rSource.m_xReplaceAccess)
+ , m_xContainerAccess(_rSource.m_xContainerAccess)
+ , m_bEscapeNames(_rSource.m_bEscapeNames)
+ {
+ Reference< XComponent > xConfigNodeComp(m_xDirectAccess, UNO_QUERY);
+ if (xConfigNodeComp.is())
+ startComponentListening(xConfigNodeComp);
+ }
+
+ OConfigurationNode::OConfigurationNode(OConfigurationNode&& _rSource)
+ : OEventListenerAdapter()
+ , m_xHierarchyAccess(std::move(_rSource.m_xHierarchyAccess))
+ , m_xDirectAccess(std::move(_rSource.m_xDirectAccess))
+ , m_xReplaceAccess(std::move(_rSource.m_xReplaceAccess))
+ , m_xContainerAccess(std::move(_rSource.m_xContainerAccess))
+ , m_bEscapeNames(std::move(_rSource.m_bEscapeNames))
+ {
+ Reference< XComponent > xConfigNodeComp(m_xDirectAccess, UNO_QUERY);
+ if (xConfigNodeComp.is())
+ startComponentListening(xConfigNodeComp);
+ }
+
+ OConfigurationNode& OConfigurationNode::operator=(const OConfigurationNode& _rSource)
+ {
+ stopAllComponentListening();
+
+ m_xHierarchyAccess = _rSource.m_xHierarchyAccess;
+ m_xDirectAccess = _rSource.m_xDirectAccess;
+ m_xContainerAccess = _rSource.m_xContainerAccess;
+ m_xReplaceAccess = _rSource.m_xReplaceAccess;
+ m_bEscapeNames = _rSource.m_bEscapeNames;
+
+ Reference< XComponent > xConfigNodeComp(m_xDirectAccess, UNO_QUERY);
+ if (xConfigNodeComp.is())
+ startComponentListening(xConfigNodeComp);
+
+ return *this;
+ }
+
+ OConfigurationNode& OConfigurationNode::operator=(OConfigurationNode&& _rSource)
+ {
+ stopAllComponentListening();
+
+ m_xHierarchyAccess = std::move(_rSource.m_xHierarchyAccess);
+ m_xDirectAccess = std::move(_rSource.m_xDirectAccess);
+ m_xContainerAccess = std::move(_rSource.m_xContainerAccess);
+ m_xReplaceAccess = std::move(_rSource.m_xReplaceAccess);
+ m_bEscapeNames = std::move(_rSource.m_bEscapeNames);
+
+ Reference< XComponent > xConfigNodeComp(m_xDirectAccess, UNO_QUERY);
+ if (xConfigNodeComp.is())
+ startComponentListening(xConfigNodeComp);
+
+ return *this;
+ }
+
+ void OConfigurationNode::_disposing( const EventObject& _rSource )
+ {
+ Reference< XComponent > xDisposingSource(_rSource.Source, UNO_QUERY);
+ Reference< XComponent > xConfigNodeComp(m_xDirectAccess, UNO_QUERY);
+ if (xDisposingSource.get() == xConfigNodeComp.get())
+ clear();
+ }
+
+ OUString OConfigurationNode::getLocalName() const
+ {
+ OUString sLocalName;
+ try
+ {
+ Reference< XNamed > xNamed( m_xDirectAccess, UNO_QUERY_THROW );
+ sLocalName = xNamed->getName();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools");
+ }
+ return sLocalName;
+ }
+
+ OUString OConfigurationNode::normalizeName(const OUString& _rName, NAMEORIGIN _eOrigin) const
+ {
+ OUString sName(_rName);
+ if (m_bEscapeNames)
+ {
+ Reference< XStringEscape > xEscaper(m_xDirectAccess, UNO_QUERY);
+ if (xEscaper.is() && !sName.isEmpty())
+ {
+ try
+ {
+ if (NO_CALLER == _eOrigin)
+ sName = xEscaper->escapeString(sName);
+ else
+ sName = xEscaper->unescapeString(sName);
+ }
+ catch(Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools");
+ }
+ }
+ }
+ return sName;
+ }
+
+ Sequence< OUString > OConfigurationNode::getNodeNames() const noexcept
+ {
+ OSL_ENSURE(m_xDirectAccess.is(), "OConfigurationNode::getNodeNames: object is invalid!");
+ Sequence< OUString > aReturn;
+ if (m_xDirectAccess.is())
+ {
+ try
+ {
+ aReturn = m_xDirectAccess->getElementNames();
+ // normalize the names
+ std::transform(std::cbegin(aReturn), std::cend(aReturn), aReturn.getArray(),
+ [this](const OUString& rName) -> OUString { return normalizeName(rName, NO_CONFIGURATION); });
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools", "OConfigurationNode::getNodeNames");
+ }
+ }
+
+ return aReturn;
+ }
+
+ bool OConfigurationNode::removeNode(const OUString& _rName) const noexcept
+ {
+ OSL_ENSURE(m_xContainerAccess.is(), "OConfigurationNode::removeNode: object is invalid!");
+ if (m_xContainerAccess.is())
+ {
+ try
+ {
+ OUString sName = normalizeName(_rName, NO_CALLER);
+ m_xContainerAccess->removeByName(sName);
+ return true;
+ }
+ catch (NoSuchElementException&)
+ {
+ SAL_WARN( "unotools", "OConfigurationNode::removeNode: there is no element named: " << _rName );
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools", "OConfigurationNode::removeNode");
+ }
+ }
+ return false;
+ }
+
+ OConfigurationNode OConfigurationNode::insertNode(const OUString& _rName,const Reference< XInterface >& _xNode) const noexcept
+ {
+ if(_xNode.is())
+ {
+ try
+ {
+ OUString sName = normalizeName(_rName, NO_CALLER);
+ m_xContainerAccess->insertByName(sName, Any(_xNode));
+ // if we're here, all was ok ...
+ return OConfigurationNode( _xNode );
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools");
+ }
+
+ // dispose the child if it has already been created, but could not be inserted
+ Reference< XComponent > xChildComp(_xNode, UNO_QUERY);
+ if (xChildComp.is())
+ try { xChildComp->dispose(); } catch(Exception&) { }
+ }
+
+ return OConfigurationNode();
+ }
+
+ OConfigurationNode OConfigurationNode::createNode(const OUString& _rName) const noexcept
+ {
+ Reference< XSingleServiceFactory > xChildFactory(m_xContainerAccess, UNO_QUERY);
+ OSL_ENSURE(xChildFactory.is(), "OConfigurationNode::createNode: object is invalid or read-only!");
+
+ if (xChildFactory.is()) // implies m_xContainerAccess.is()
+ {
+ Reference< XInterface > xNewChild;
+ try
+ {
+ xNewChild = xChildFactory->createInstance();
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools");
+ }
+ return insertNode(_rName,xNewChild);
+ }
+
+ return OConfigurationNode();
+ }
+
+ OConfigurationNode OConfigurationNode::openNode(const OUString& _rPath) const noexcept
+ {
+ OSL_ENSURE(m_xDirectAccess.is(), "OConfigurationNode::openNode: object is invalid!");
+ OSL_ENSURE(m_xHierarchyAccess.is(), "OConfigurationNode::openNode: object is invalid!");
+ try
+ {
+ OUString sNormalized = normalizeName(_rPath, NO_CALLER);
+
+ Reference< XInterface > xNode;
+ if (m_xDirectAccess.is() && m_xDirectAccess->hasByName(sNormalized))
+ {
+ xNode.set(
+ m_xDirectAccess->getByName(sNormalized), css::uno::UNO_QUERY);
+ if (!xNode.is())
+ OSL_FAIL("OConfigurationNode::openNode: could not open the node!");
+ }
+ else if (m_xHierarchyAccess.is())
+ {
+ xNode.set(
+ m_xHierarchyAccess->getByHierarchicalName(_rPath),
+ css::uno::UNO_QUERY);
+ if (!xNode.is())
+ OSL_FAIL("OConfigurationNode::openNode: could not open the node!");
+ }
+ if (xNode.is())
+ return OConfigurationNode( xNode );
+ }
+ catch(const NoSuchElementException&)
+ {
+ SAL_WARN( "unotools", "OConfigurationNode::openNode: there is no element named " << _rPath );
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools", "OConfigurationNode::openNode: caught an exception while retrieving the node!");
+ }
+ return OConfigurationNode();
+ }
+
+ bool OConfigurationNode::isSetNode() const
+ {
+ bool bIsSet = false;
+ Reference< XServiceInfo > xSI(m_xHierarchyAccess, UNO_QUERY);
+ if (xSI.is())
+ {
+ try { bIsSet = xSI->supportsService("com.sun.star.configuration.SetAccess"); }
+ catch(Exception&) { }
+ }
+ return bIsSet;
+ }
+
+ bool OConfigurationNode::hasByHierarchicalName( const OUString& _rName ) const noexcept
+ {
+ OSL_ENSURE( m_xHierarchyAccess.is(), "OConfigurationNode::hasByHierarchicalName: no hierarchy access!" );
+ try
+ {
+ if ( m_xHierarchyAccess.is() )
+ {
+ OUString sName = normalizeName( _rName, NO_CALLER );
+ return m_xHierarchyAccess->hasByHierarchicalName( sName );
+ }
+ }
+ catch(Exception&)
+ {
+ }
+ return false;
+ }
+
+ bool OConfigurationNode::hasByName(const OUString& _rName) const noexcept
+ {
+ OSL_ENSURE(m_xDirectAccess.is(), "OConfigurationNode::hasByName: object is invalid!");
+ try
+ {
+ OUString sName = normalizeName(_rName, NO_CALLER);
+ if (m_xDirectAccess.is())
+ return m_xDirectAccess->hasByName(sName);
+ }
+ catch(Exception&)
+ {
+ }
+ return false;
+ }
+
+ bool OConfigurationNode::setNodeValue(const OUString& _rPath, const Any& _rValue) const noexcept
+ {
+ bool bResult = false;
+
+ OSL_ENSURE(m_xReplaceAccess.is(), "OConfigurationNode::setNodeValue: object is invalid!");
+ if (m_xReplaceAccess.is())
+ {
+ try
+ {
+ // check if _rPath is a level-1 path
+ OUString sNormalizedName = normalizeName(_rPath, NO_CALLER);
+ if (m_xReplaceAccess->hasByName(sNormalizedName))
+ {
+ m_xReplaceAccess->replaceByName(sNormalizedName, _rValue);
+ bResult = true;
+ }
+
+ // check if the name refers to an indirect descendant
+ else if (m_xHierarchyAccess.is() && m_xHierarchyAccess->hasByHierarchicalName(_rPath))
+ {
+ OSL_ASSERT(!_rPath.isEmpty());
+
+ OUString sParentPath, sLocalName;
+
+ if ( splitLastFromConfigurationPath(_rPath, sParentPath, sLocalName) )
+ {
+ OConfigurationNode aParentAccess = openNode(sParentPath);
+ if (aParentAccess.isValid())
+ bResult = aParentAccess.setNodeValue(sLocalName, _rValue);
+ }
+ else
+ {
+ m_xReplaceAccess->replaceByName(sLocalName, _rValue);
+ bResult = true;
+ }
+ }
+
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools", "OConfigurationNode::setNodeValue: could not replace the value");
+ }
+
+ }
+ return bResult;
+ }
+
+ Any OConfigurationNode::getNodeValue(const OUString& _rPath) const noexcept
+ {
+ OSL_ENSURE(m_xDirectAccess.is(), "OConfigurationNode::hasByName: object is invalid!");
+ OSL_ENSURE(m_xHierarchyAccess.is(), "OConfigurationNode::hasByName: object is invalid!");
+ Any aReturn;
+ try
+ {
+ OUString sNormalizedPath = normalizeName(_rPath, NO_CALLER);
+ if (m_xDirectAccess.is() && m_xDirectAccess->hasByName(sNormalizedPath) )
+ {
+ aReturn = m_xDirectAccess->getByName(sNormalizedPath);
+ }
+ else if (m_xHierarchyAccess.is())
+ {
+ aReturn = m_xHierarchyAccess->getByHierarchicalName(_rPath);
+ }
+ }
+ catch(const NoSuchElementException&)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools");
+ }
+ return aReturn;
+ }
+
+ void OConfigurationNode::clear() noexcept
+ {
+ m_xHierarchyAccess.clear();
+ m_xDirectAccess.clear();
+ m_xReplaceAccess.clear();
+ m_xContainerAccess.clear();
+ }
+
+ //= helper
+
+ namespace
+ {
+
+ Reference< XMultiServiceFactory > lcl_getConfigProvider( const Reference<XComponentContext> & i_rContext )
+ {
+ try
+ {
+ Reference< XMultiServiceFactory > xProvider = theDefaultProvider::get( i_rContext );
+ return xProvider;
+ }
+ catch ( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools");
+ }
+ return nullptr;
+ }
+
+ Reference< XInterface > lcl_createConfigurationRoot( const Reference< XMultiServiceFactory >& i_rxConfigProvider,
+ const OUString& i_rNodePath, const bool i_bUpdatable, const sal_Int32 i_nDepth )
+ {
+ ENSURE_OR_RETURN( i_rxConfigProvider.is(), "invalid provider", nullptr );
+ try
+ {
+ ::comphelper::NamedValueCollection aArgs;
+ aArgs.put( "nodepath", i_rNodePath );
+ aArgs.put( "depth", i_nDepth );
+
+ OUString sAccessService( i_bUpdatable ?
+ OUString( "com.sun.star.configuration.ConfigurationUpdateAccess" ) :
+ OUString( "com.sun.star.configuration.ConfigurationAccess" ));
+
+ Reference< XInterface > xRoot(
+ i_rxConfigProvider->createInstanceWithArguments( sAccessService, aArgs.getWrappedPropertyValues() ),
+ UNO_SET_THROW
+ );
+ return xRoot;
+ }
+ catch ( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools");
+ }
+ return nullptr;
+ }
+ }
+
+ OConfigurationTreeRoot::OConfigurationTreeRoot( const Reference< XInterface >& _rxRootNode )
+ :OConfigurationNode( _rxRootNode )
+ ,m_xCommitter( _rxRootNode, UNO_QUERY )
+ {
+ }
+
+ OConfigurationTreeRoot::OConfigurationTreeRoot( const Reference<XComponentContext> & i_rContext, const OUString& i_rNodePath, const bool i_bUpdatable )
+ :OConfigurationNode( lcl_createConfigurationRoot( lcl_getConfigProvider( i_rContext ),
+ i_rNodePath, i_bUpdatable, -1 ) )
+ ,m_xCommitter()
+ {
+ if ( i_bUpdatable )
+ {
+ m_xCommitter.set( getUNONode(), UNO_QUERY );
+ OSL_ENSURE( m_xCommitter.is(), "OConfigurationTreeRoot::OConfigurationTreeRoot: could not create an updatable node!" );
+ }
+ }
+
+ void OConfigurationTreeRoot::clear() noexcept
+ {
+ OConfigurationNode::clear();
+ m_xCommitter.clear();
+ }
+
+ bool OConfigurationTreeRoot::commit() const noexcept
+ {
+ OSL_ENSURE(isValid(), "OConfigurationTreeRoot::commit: object is invalid!");
+ if (!isValid())
+ return false;
+ OSL_ENSURE(m_xCommitter.is(), "OConfigurationTreeRoot::commit: I'm a readonly node!");
+ if (!m_xCommitter.is())
+ return false;
+
+ try
+ {
+ m_xCommitter->commitChanges();
+ return true;
+ }
+ catch(const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools");
+ }
+ return false;
+ }
+
+ OConfigurationTreeRoot OConfigurationTreeRoot::createWithProvider(const Reference< XMultiServiceFactory >& _rxConfProvider, const OUString& _rPath, sal_Int32 _nDepth, CREATION_MODE _eMode)
+ {
+ Reference< XInterface > xRoot( lcl_createConfigurationRoot(
+ _rxConfProvider, _rPath, _eMode != CM_READONLY, _nDepth ) );
+ if ( xRoot.is() )
+ return OConfigurationTreeRoot( xRoot );
+ return OConfigurationTreeRoot();
+ }
+
+ OConfigurationTreeRoot OConfigurationTreeRoot::createWithComponentContext( const Reference< XComponentContext >& _rxContext, const OUString& _rPath, sal_Int32 _nDepth, CREATION_MODE _eMode )
+ {
+ return createWithProvider( lcl_getConfigProvider( _rxContext ), _rPath, _nDepth, _eMode );
+ }
+
+ OConfigurationTreeRoot OConfigurationTreeRoot::tryCreateWithComponentContext( const Reference< XComponentContext >& rxContext,
+ const OUString& _rPath, sal_Int32 _nDepth , CREATION_MODE _eMode )
+ {
+ OSL_ENSURE( rxContext.is(), "OConfigurationTreeRoot::tryCreateWithComponentContext: invalid XComponentContext!" );
+ try
+ {
+ Reference< XMultiServiceFactory > xConfigFactory = theDefaultProvider::get( rxContext );
+ return createWithProvider( xConfigFactory, _rPath, _nDepth, _eMode );
+ }
+ catch(const Exception&)
+ {
+ // silence this, 'cause the contract of this method states "no assertions"
+ }
+ return OConfigurationTreeRoot();
+ }
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/configpaths.cxx b/unotools/source/config/configpaths.cxx
new file mode 100644
index 0000000000..4c626d96b4
--- /dev/null
+++ b/unotools/source/config/configpaths.cxx
@@ -0,0 +1,307 @@
+/* -*- 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 <cassert>
+#include <string_view>
+
+#include <unotools/configpaths.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+namespace utl
+{
+
+static
+void lcl_resolveCharEntities(OUString & aLocalString)
+{
+ sal_Int32 nEscapePos=aLocalString.indexOf('&');
+ if (nEscapePos < 0) return;
+
+ OUStringBuffer aResult;
+ sal_Int32 nStart = 0;
+
+ do
+ {
+ sal_Unicode ch = 0;
+ if (aLocalString.match("&amp;",nEscapePos))
+ ch = '&';
+
+ else if (aLocalString.match("&apos;",nEscapePos))
+ ch = '\'';
+
+ else if (aLocalString.match("&quot;",nEscapePos))
+ ch = '"';
+
+ OSL_ENSURE(ch,"Configuration path contains '&' that is not part of a valid character escape");
+ if (ch)
+ {
+ aResult.append(aLocalString.subView(nStart,nEscapePos-nStart) + OUStringChar(ch));
+
+ sal_Int32 nEscapeEnd=aLocalString.indexOf(';',nEscapePos);
+ nStart = nEscapeEnd+1;
+ nEscapePos=aLocalString.indexOf('&',nStart);
+ }
+ else
+ {
+ nEscapePos=aLocalString.indexOf('&',nEscapePos+1);
+ }
+ }
+ while ( nEscapePos > 0);
+
+ aResult.append(aLocalString.subView(nStart));
+
+ aLocalString = aResult.makeStringAndClear();
+}
+
+bool splitLastFromConfigurationPath(std::u16string_view _sInPath,
+ OUString& _rsOutPath,
+ OUString& _rsLocalName)
+{
+ size_t nStart,nEnd;
+
+ size_t nPos = _sInPath.size();
+
+ // for backwards compatibility, strip trailing slash
+ if (nPos > 1 && _sInPath[ nPos - 1 ] == '/')
+ {
+ --nPos;
+ }
+
+ // check for set element ['xxx'] or ["yyy"]
+ bool decode;
+ if (nPos > 0 && _sInPath[ nPos - 1 ] == ']')
+ {
+ decode = true;
+ if (nPos < 3) { // expect at least chQuote + chQuote + ']' at _sInPath[nPos-3..nPos-1]
+ goto invalid;
+ }
+ nPos -= 2;
+ sal_Unicode chQuote = _sInPath[nPos];
+
+ if (chQuote == '\'' || chQuote == '\"')
+ {
+ nEnd = nPos;
+ nPos = _sInPath.rfind(chQuote,nEnd - 1);
+ if (nPos == std::u16string_view::npos) {
+ goto invalid;
+ }
+ nStart = nPos + 1;
+ }
+ else
+ {
+ goto invalid;
+ }
+
+ OSL_ENSURE(nPos > 0 && _sInPath[nPos - 1] == '[', "Invalid config path: unmatched quotes or brackets");
+ if (nPos > 1 && _sInPath[nPos - 1] == '[')
+ // expect at least '/' + '[' at _sInPath[nPos-2..nPos-1]
+ {
+ nPos = _sInPath.rfind('/',nPos - 2);
+ if (nPos == std::u16string_view::npos) {
+ goto invalid;
+ }
+ }
+ else
+ {
+ goto invalid;
+ }
+
+ }
+ else
+ {
+ decode = false;
+ nEnd = nPos;
+ if (nEnd == 0) {
+ goto invalid;
+ }
+ nPos = _sInPath.rfind('/',nEnd - 1);
+ if (nPos == std::u16string_view::npos) {
+ goto invalid;
+ }
+ nStart = nPos + 1;
+ }
+ assert( nPos != std::u16string_view::npos &&
+ nPos < nStart &&
+ nStart <= nEnd &&
+ nEnd <= _sInPath.size() );
+
+ assert(_sInPath[nPos] == '/');
+ OSL_ENSURE(nPos != 0 , "Invalid config child path: immediate child of root");
+
+ _rsLocalName = _sInPath.substr(nStart, nEnd-nStart);
+ _rsOutPath = (nPos > 0) ? OUString(_sInPath.substr(0,nPos)) : OUString();
+ if (decode) {
+ lcl_resolveCharEntities(_rsLocalName);
+ }
+
+ return true;
+
+invalid:
+ _rsOutPath.clear();
+ _rsLocalName = _sInPath;
+ return false;
+}
+
+OUString extractFirstFromConfigurationPath(OUString const& _sInPath, OUString* _sOutPath)
+{
+ sal_Int32 nSep = _sInPath.indexOf('/');
+ sal_Int32 nBracket = _sInPath.indexOf('[');
+
+ sal_Int32 nStart = nBracket + 1;
+ sal_Int32 nEnd = nSep;
+
+ if (0 <= nBracket) // found a bracket-quoted relative path
+ {
+ if (nSep < 0 || nBracket < nSep) // and the separator comes after it
+ {
+ sal_Unicode chQuote = _sInPath[nStart];
+ if (chQuote == '\'' || chQuote == '\"')
+ {
+ ++nStart;
+ nEnd = _sInPath.indexOf(chQuote, nStart+1);
+ nBracket = nEnd+1;
+ }
+ else
+ {
+ nEnd = _sInPath.indexOf(']',nStart);
+ nBracket = nEnd;
+ }
+ OSL_ENSURE(nEnd > nStart && _sInPath[nBracket] == ']', "Invalid config path: improper mismatch of quote or bracket");
+ OSL_ENSURE((nBracket+1 == _sInPath.getLength() && nSep == -1) || (_sInPath[nBracket+1] == '/' && nSep == nBracket+1), "Invalid config path: brackets not followed by slash");
+ }
+ else // ... but our initial element name is in simple form
+ nStart = 0;
+ }
+
+ OUString sResult = (nEnd >= 0) ? _sInPath.copy(nStart, nEnd-nStart) : _sInPath;
+ lcl_resolveCharEntities(sResult);
+
+ if (_sOutPath != nullptr)
+ {
+ *_sOutPath = (nSep >= 0) ? _sInPath.copy(nSep + 1) : OUString();
+ }
+
+ return sResult;
+}
+
+// find the position after the prefix in the nested path
+static sal_Int32 lcl_findPrefixEnd(std::u16string_view _sNestedPath, std::u16string_view _sPrefixPath)
+{
+ // TODO: currently handles only exact prefix matches
+ size_t nPrefixLength = _sPrefixPath.size();
+
+ OSL_ENSURE(nPrefixLength == 0 || _sPrefixPath[nPrefixLength-1] != '/',
+ "Cannot handle slash-terminated prefix paths");
+
+ bool bIsPrefix;
+ if (_sNestedPath.size() > nPrefixLength)
+ {
+ bIsPrefix = _sNestedPath[nPrefixLength] == '/' &&
+ o3tl::starts_with(_sNestedPath, _sPrefixPath);
+ ++nPrefixLength;
+ }
+ else if (_sNestedPath.size() == nPrefixLength)
+ {
+ bIsPrefix = _sNestedPath == _sPrefixPath;
+ }
+ else
+ {
+ bIsPrefix = false;
+ }
+
+ return bIsPrefix ? nPrefixLength : 0;
+}
+
+bool isPrefixOfConfigurationPath(std::u16string_view _sNestedPath,
+ std::u16string_view _sPrefixPath)
+{
+ return _sPrefixPath.empty() || lcl_findPrefixEnd(_sNestedPath,_sPrefixPath) != 0;
+}
+
+OUString dropPrefixFromConfigurationPath(OUString const& _sNestedPath,
+ std::u16string_view _sPrefixPath)
+{
+ if ( sal_Int32 nPrefixEnd = lcl_findPrefixEnd(_sNestedPath,_sPrefixPath) )
+ {
+ return _sNestedPath.copy(nPrefixEnd);
+ }
+ else
+ {
+ OSL_ENSURE(_sPrefixPath.empty(), "Path does not start with expected prefix");
+
+ return _sNestedPath;
+ }
+}
+
+static
+OUString lcl_wrapName(std::u16string_view _sContent, const OUString& _sType)
+{
+ const sal_Unicode * const pBeginContent = _sContent.data();
+ const sal_Unicode * const pEndContent = pBeginContent + _sContent.size();
+
+ OSL_PRECOND(!_sType.isEmpty(), "Unexpected config type name: empty");
+ OSL_PRECOND(pBeginContent <= pEndContent, "Invalid config name: empty");
+
+ if (pBeginContent == pEndContent)
+ return _sType;
+
+ OUStringBuffer aNormalized(_sType.getLength() + _sContent.size() + 4); // reserve approximate size initially
+
+ // prefix: type, opening bracket and quote
+ aNormalized.append( _sType + "['" );
+
+ // content: copy over each char and handle escaping
+ for(const sal_Unicode* pCur = pBeginContent; pCur != pEndContent; ++pCur)
+ {
+ // append (escape if needed)
+ switch(*pCur)
+ {
+ case u'&' : aNormalized.append( "&amp;" ); break;
+ case u'\'': aNormalized.append( "&apos;" ); break;
+ case u'\"': aNormalized.append( "&quot;" ); break;
+
+ default: aNormalized.append( *pCur );
+ }
+ }
+
+ // suffix: closing quote and bracket
+ aNormalized.append( "']" );
+
+ return aNormalized.makeStringAndClear();
+}
+
+OUString wrapConfigurationElementName(std::u16string_view _sElementName)
+{
+ return lcl_wrapName(_sElementName, "*" );
+}
+
+OUString wrapConfigurationElementName(std::u16string_view _sElementName,
+ OUString const& _sTypeName)
+{
+ // todo: check that _sTypeName is valid
+ return lcl_wrapName(_sElementName, _sTypeName);
+}
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/configvaluecontainer.cxx b/unotools/source/config/configvaluecontainer.cxx
new file mode 100644
index 0000000000..6852674a95
--- /dev/null
+++ b/unotools/source/config/configvaluecontainer.cxx
@@ -0,0 +1,304 @@
+/* -*- 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 <sal/log.hxx>
+#include <unotools/configvaluecontainer.hxx>
+#include <unotools/confignode.hxx>
+#include <uno/data.h>
+#include <algorithm>
+#include <utility>
+#include <vector>
+
+namespace utl
+{
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+
+ //= NodeValueAccessor
+
+ namespace {
+
+ enum class LocationType
+ {
+ SimplyObjectInstance,
+ Unbound
+ };
+
+ }
+
+ struct NodeValueAccessor
+ {
+ private:
+ OUString sRelativePath; // the relative path of the node
+ LocationType eLocationType; // the type of location where the value is stored
+ void* pLocation; // the pointer to the location
+ Type aDataType; // the type object pointed to by pLocation
+
+ public:
+ explicit NodeValueAccessor( OUString _aNodePath );
+
+ void bind( void* _pLocation, const Type& _rType );
+
+ bool isBound( ) const { return ( LocationType::Unbound != eLocationType ) && ( nullptr != pLocation ); }
+ const OUString& getPath( ) const { return sRelativePath; }
+ LocationType getLocType( ) const { return eLocationType; }
+ void* getLocation( ) const { return pLocation; }
+ const Type& getDataType( ) const { return aDataType; }
+
+ bool operator == ( const NodeValueAccessor& rhs ) const;
+ };
+
+ NodeValueAccessor::NodeValueAccessor( OUString _aNodePath )
+ :sRelativePath(std::move( _aNodePath ))
+ ,eLocationType( LocationType::Unbound )
+ ,pLocation( nullptr )
+ {
+ }
+
+ bool NodeValueAccessor::operator == ( const NodeValueAccessor& rhs ) const
+ {
+ return ( sRelativePath == rhs.sRelativePath )
+ && ( eLocationType == rhs.eLocationType )
+ && ( pLocation == rhs.pLocation );
+ }
+
+ void NodeValueAccessor::bind( void* _pLocation, const Type& _rType )
+ {
+ SAL_WARN_IF(isBound(), "unotools.config", "NodeValueAccessor::bind: already bound!");
+
+ eLocationType = LocationType::SimplyObjectInstance;
+ pLocation = _pLocation;
+ aDataType = _rType;
+ }
+
+ static
+ void lcl_copyData( const NodeValueAccessor& _rAccessor, const Any& _rData, std::mutex& _rMutex )
+ {
+ std::unique_lock aGuard( _rMutex );
+
+ SAL_WARN_IF(!_rAccessor.isBound(), "unotools.config", "::utl::lcl_copyData: invalid accessor!");
+ switch ( _rAccessor.getLocType() )
+ {
+ case LocationType::SimplyObjectInstance:
+ {
+ if ( _rData.hasValue() )
+ {
+ // assign the value
+ bool bSuccess = uno_type_assignData(
+ _rAccessor.getLocation(), _rAccessor.getDataType().getTypeLibType(),
+ const_cast< void* >( _rData.getValue() ), _rData.getValueType().getTypeLibType(),
+ cpp_queryInterface, cpp_acquire, cpp_release
+ );
+ SAL_WARN_IF(!bSuccess, "unotools.config",
+ "::utl::lcl_copyData( Accessor, Any ): could not assign the data (node path: \"" << _rAccessor.getPath() << "\"");
+ }
+ else {
+ SAL_INFO("unotools.config", "::utl::lcl_copyData: NULL value lost!");
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ static
+ void lcl_copyData( Any& _rData, const NodeValueAccessor& _rAccessor, std::mutex& _rMutex )
+ {
+ std::unique_lock aGuard( _rMutex );
+
+ SAL_WARN_IF(!_rAccessor.isBound(), "unotools.config", "::utl::lcl_copyData: invalid accessor!" );
+ switch ( _rAccessor.getLocType() )
+ {
+ case LocationType::SimplyObjectInstance:
+ // a simple setValue...
+ _rData.setValue( _rAccessor.getLocation(), _rAccessor.getDataType() );
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ //= functors on NodeValueAccessor instances
+
+ namespace {
+
+ /// base class for functors synchronizing between exchange locations and config sub nodes
+ struct SubNodeAccess
+ {
+ protected:
+ const OConfigurationNode& m_rRootNode;
+ std::mutex& m_rMutex;
+
+ public:
+ SubNodeAccess( const OConfigurationNode& _rRootNode, std::mutex& _rMutex )
+ :m_rRootNode( _rRootNode )
+ ,m_rMutex( _rMutex )
+ {
+ }
+ };
+
+ struct UpdateFromConfig : public SubNodeAccess
+ {
+ public:
+ UpdateFromConfig( const OConfigurationNode& _rRootNode, std::mutex& _rMutex ) : SubNodeAccess( _rRootNode, _rMutex ) { }
+
+ void operator() ( NodeValueAccessor const & _rAccessor )
+ {
+ ::utl::lcl_copyData( _rAccessor, m_rRootNode.getNodeValue( _rAccessor.getPath( ) ), m_rMutex );
+ }
+ };
+
+ struct UpdateToConfig : public SubNodeAccess
+ {
+ public:
+ UpdateToConfig( const OConfigurationNode& _rRootNode, std::mutex& _rMutex ) : SubNodeAccess( _rRootNode, _rMutex ) { }
+
+ void operator() ( NodeValueAccessor const & _rAccessor )
+ {
+ Any aNewValue;
+ lcl_copyData( aNewValue, _rAccessor, m_rMutex );
+ m_rRootNode.setNodeValue( _rAccessor.getPath( ), aNewValue );
+ }
+ };
+
+ }
+
+ //= OConfigurationValueContainerImpl
+
+ struct OConfigurationValueContainerImpl
+ {
+ Reference< XComponentContext > xORB; // the service factory
+ std::mutex& rMutex; // the mutex for accessing the data containers
+ OConfigurationTreeRoot aConfigRoot; // the configuration node we're accessing
+
+ std::vector<NodeValueAccessor> aAccessors; // the accessors to the node values
+
+ OConfigurationValueContainerImpl( const Reference< XComponentContext >& _rxORB, std::mutex& _rMutex )
+ :xORB( _rxORB )
+ ,rMutex( _rMutex )
+ {
+ }
+ };
+
+ //= OConfigurationValueContainer
+
+ OConfigurationValueContainer::OConfigurationValueContainer(
+ const Reference< XComponentContext >& _rxORB, std::mutex& _rAccessSafety,
+ const char* _pConfigLocation, const sal_Int32 _nLevels )
+ :m_pImpl( new OConfigurationValueContainerImpl( _rxORB, _rAccessSafety ) )
+ {
+ implConstruct( OUString::createFromAscii( _pConfigLocation ), _nLevels );
+ }
+
+ OConfigurationValueContainer::~OConfigurationValueContainer()
+ {
+ }
+
+ void OConfigurationValueContainer::implConstruct( const OUString& _rConfigLocation,
+ const sal_Int32 _nLevels )
+ {
+ SAL_WARN_IF(m_pImpl->aConfigRoot.isValid(), "unotools.config", "OConfigurationValueContainer::implConstruct: already initialized!");
+
+ // create the configuration node we're about to work with
+ m_pImpl->aConfigRoot = OConfigurationTreeRoot::createWithComponentContext(
+ m_pImpl->xORB,
+ _rConfigLocation,
+ _nLevels
+ );
+ SAL_WARN_IF(!m_pImpl->aConfigRoot.isValid(), "unotools.config",
+ "Could not access the configuration node located at " << _rConfigLocation);
+ }
+
+ void OConfigurationValueContainer::registerExchangeLocation( const char* _pRelativePath,
+ void* _pContainer, const Type& _rValueType )
+ {
+ // checks...
+ SAL_WARN_IF(!_pContainer, "unotools.config",
+ "OConfigurationValueContainer::registerExchangeLocation: invalid container location!");
+ SAL_WARN_IF(!( (TypeClass_CHAR == _rValueType.getTypeClass( ) )
+ || ( TypeClass_BOOLEAN == _rValueType.getTypeClass( ) )
+ || ( TypeClass_BYTE == _rValueType.getTypeClass( ) )
+ || ( TypeClass_SHORT == _rValueType.getTypeClass( ) )
+ || ( TypeClass_LONG == _rValueType.getTypeClass( ) )
+ || ( TypeClass_DOUBLE == _rValueType.getTypeClass( ) )
+ || ( TypeClass_STRING == _rValueType.getTypeClass( ) )
+ || ( TypeClass_SEQUENCE == _rValueType.getTypeClass( ) )),
+ "unotools.config",
+ "OConfigurationValueContainer::registerExchangeLocation: invalid type!" );
+
+ // build an accessor for this container
+ NodeValueAccessor aNewAccessor( OUString::createFromAscii( _pRelativePath ) );
+ aNewAccessor.bind( _pContainer, _rValueType );
+
+ // insert it into our structure
+ implRegisterExchangeLocation( aNewAccessor );
+ }
+
+ void OConfigurationValueContainer::read( )
+ {
+ std::for_each(
+ m_pImpl->aAccessors.begin(),
+ m_pImpl->aAccessors.end(),
+ UpdateFromConfig( m_pImpl->aConfigRoot, m_pImpl->rMutex )
+ );
+ }
+
+ void OConfigurationValueContainer::commit()
+ {
+ // write the current values in the exchange locations
+ std::for_each(
+ m_pImpl->aAccessors.begin(),
+ m_pImpl->aAccessors.end(),
+ UpdateToConfig( m_pImpl->aConfigRoot, m_pImpl->rMutex )
+ );
+
+ // commit the changes done
+ m_pImpl->aConfigRoot.commit( );
+ }
+
+ void OConfigurationValueContainer::implRegisterExchangeLocation( const NodeValueAccessor& _rAccessor )
+ {
+ // some checks
+ SAL_WARN_IF(m_pImpl->aConfigRoot.isValid() && !m_pImpl->aConfigRoot.hasByHierarchicalName(_rAccessor.getPath()),
+ "unotools.config",
+ "OConfigurationValueContainer::implRegisterExchangeLocation: invalid relative path!" );
+
+ // another check (should be the first container for this node)
+ SAL_WARN_IF(!(m_pImpl->aAccessors.end() == ::std::find(
+ m_pImpl->aAccessors.begin(),
+ m_pImpl->aAccessors.end(),
+ _rAccessor)),
+ "unotools.config",
+ "OConfigurationValueContainer::implRegisterExchangeLocation: already registered a container for this subnode!" );
+
+ // remember the accessor
+ m_pImpl->aAccessors.push_back( _rAccessor );
+
+ // and initially fill the value
+ lcl_copyData( _rAccessor, m_pImpl->aConfigRoot.getNodeValue( _rAccessor.getPath() ), m_pImpl->rMutex );
+ }
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/defaultoptions.cxx b/unotools/source/config/defaultoptions.cxx
new file mode 100644
index 0000000000..127cc68580
--- /dev/null
+++ b/unotools/source/config/defaultoptions.cxx
@@ -0,0 +1,119 @@
+/* -*- 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 <unotools/defaultoptions.hxx>
+#include <unotools/pathoptions.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <officecfg/Office/Common.hxx>
+
+namespace SvtDefaultOptions
+{
+
+OUString GetDefaultPath( SvtPathOptions::Paths nId )
+{
+ SvtPathOptions aPathOpt;
+ auto seqToPath = [&aPathOpt] (const css::uno::Sequence<OUString> & rSeq)
+ {
+ // single paths
+ sal_Int32 nCount = rSeq.getLength();
+ OUStringBuffer aFullPathBuf(nCount * 40);
+ for ( sal_Int32 nPosition = 0; nPosition < nCount; ++nPosition )
+ {
+ aFullPathBuf.append(aPathOpt.SubstituteVariable( rSeq[ nPosition ] ));
+ if ( nPosition < nCount-1 )
+ aFullPathBuf.append(";");
+ }
+ return aFullPathBuf.makeStringAndClear();
+ };
+
+ OUString aRet;
+ switch (nId)
+ {
+ case SvtPathOptions::Paths::AddIn:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::Addin::get()); break;
+ case SvtPathOptions::Paths::AutoCorrect:
+ aRet = seqToPath(officecfg::Office::Common::Path::Default::AutoCorrect::get()); break;
+ case SvtPathOptions::Paths::AutoText:
+ aRet = seqToPath(officecfg::Office::Common::Path::Default::AutoText::get()); break;
+ case SvtPathOptions::Paths::Backup:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::Backup::get()); break;
+ case SvtPathOptions::Paths::Basic:
+ aRet = seqToPath(officecfg::Office::Common::Path::Default::Basic::get()); break;
+ case SvtPathOptions::Paths::Bitmap:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::Bitmap::get()); break;
+ case SvtPathOptions::Paths::Config:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::Config::get()); break;
+ case SvtPathOptions::Paths::Dictionary:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::Dictionary::get()); break;
+ case SvtPathOptions::Paths::Favorites:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::Favorite::get()); break;
+ case SvtPathOptions::Paths::Filter:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::Filter::get()); break;
+ case SvtPathOptions::Paths::Gallery:
+ aRet = seqToPath(officecfg::Office::Common::Path::Default::Gallery::get()); break;
+ case SvtPathOptions::Paths::Graphic:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::Graphic::get()); break;
+ case SvtPathOptions::Paths::Help:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::Help::get()); break;
+ case SvtPathOptions::Paths::Linguistic:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::Linguistic::get()); break;
+ case SvtPathOptions::Paths::Module:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::Module::get()); break;
+ case SvtPathOptions::Paths::Palette:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::Palette::get()); break;
+ case SvtPathOptions::Paths::Plugin:
+ aRet = seqToPath(officecfg::Office::Common::Path::Default::Plugin::get()); break;
+ case SvtPathOptions::Paths::Temp:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::Temp::get()); break;
+ case SvtPathOptions::Paths::Template:
+ aRet = seqToPath(officecfg::Office::Common::Path::Default::Template::get()); break;
+ case SvtPathOptions::Paths::UserConfig:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::UserConfig::get()); break;
+ case SvtPathOptions::Paths::Work:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::Work::get()); break;
+ case SvtPathOptions::Paths::Classification:
+ aRet = aPathOpt.SubstituteVariable(officecfg::Office::Common::Path::Default::Classification::get()); break;
+ default:
+ assert(false);
+ }
+
+
+ if ( nId == SvtPathOptions::Paths::AddIn ||
+ nId == SvtPathOptions::Paths::Filter ||
+ nId == SvtPathOptions::Paths::Help ||
+ nId == SvtPathOptions::Paths::Module ||
+ nId == SvtPathOptions::Paths::Plugin )
+ {
+ OUString aTmp;
+ osl::FileBase::getFileURLFromSystemPath( aRet, aTmp );
+ aRet = aTmp;
+ }
+
+ return aRet;
+}
+
+} // namespace
+
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/docinfohelper.cxx b/unotools/source/config/docinfohelper.cxx
new file mode 100644
index 0000000000..6674649eae
--- /dev/null
+++ b/unotools/source/config/docinfohelper.cxx
@@ -0,0 +1,105 @@
+/* -*- 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 <rtl/ustrbuf.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/bootstrap.hxx>
+#include <unotools/docinfohelper.hxx>
+#include <rtl/bootstrap.hxx>
+#include <officecfg/Office/Common.hxx>
+
+using namespace ::com::sun::star;
+
+namespace utl
+{
+
+OUString DocInfoHelper::GetGeneratorString()
+{
+ static const OUString sGenerator = []()
+ {
+ OUString aResultOverride = officecfg::Office::Common::Save::Document::GeneratorOverride::get();
+ if( !aResultOverride.isEmpty())
+ return aResultOverride;
+
+ OUStringBuffer aResult(128);
+
+ // First product: branded name + version
+ // version is <product_versions>_<product_extension>$<platform>
+
+ // plain product name
+ OUString aValue( utl::ConfigManager::getProductName() );
+ if ( !aValue.isEmpty() )
+ {
+ aResult.append( aValue.replace( ' ', '_' ) + "/" );
+
+ aValue = utl::ConfigManager::getProductVersion();
+ if ( !aValue.isEmpty() )
+ {
+ aResult.append( aValue.replace( ' ', '_' ) );
+
+ aValue = utl::ConfigManager::getProductExtension();
+ if ( !aValue.isEmpty() )
+ {
+ aResult.append( aValue.replace( ' ', '_' ) );
+ }
+ }
+
+ OUString os( "$_OS" );
+ OUString arch( "$_ARCH" );
+ ::rtl::Bootstrap::expandMacros(os);
+ ::rtl::Bootstrap::expandMacros(arch);
+ aResult.append( "$" + os + "_" + arch + " " );
+ }
+
+ // second product: LibreOffice_project/<build_information>
+ // build_information has '(' and '[' encoded as '$', ')' and ']' ignored
+ // and ':' replaced by '-'
+ {
+ aResult.append( "LibreOffice_project/" );
+ OUString aBuildId( Bootstrap::getBuildIdData( OUString() ) );
+ for( sal_Int32 i=0; i < aBuildId.getLength(); i++ )
+ {
+ sal_Unicode c = aBuildId[i];
+ switch( c )
+ {
+ case '(':
+ case '[':
+ aResult.append( '$' );
+ break;
+ case ')':
+ case ']':
+ break;
+ case ':':
+ aResult.append( '-' );
+ break;
+ default:
+ aResult.append( c );
+ break;
+ }
+ }
+ }
+
+ return aResult.makeStringAndClear();
+ }();
+ return sGenerator;
+}
+
+} // end of namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/dynamicmenuoptions.cxx b/unotools/source/config/dynamicmenuoptions.cxx
new file mode 100644
index 0000000000..05b9c17073
--- /dev/null
+++ b/unotools/source/config/dynamicmenuoptions.cxx
@@ -0,0 +1,339 @@
+/* -*- 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 <o3tl/string_view.hxx>
+#include <unotools/dynamicmenuoptions.hxx>
+#include <tools/debug.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/configitem.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <vector>
+#include <algorithm>
+#include <string_view>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+constexpr OUStringLiteral DYNAMICMENU_PROPERTYNAME_URL = u"URL";
+constexpr OUStringLiteral DYNAMICMENU_PROPERTYNAME_TITLE = u"Title";
+constexpr OUStringLiteral DYNAMICMENU_PROPERTYNAME_IMAGEIDENTIFIER = u"ImageIdentifier";
+constexpr OUStringLiteral DYNAMICMENU_PROPERTYNAME_TARGETNAME = u"TargetName";
+
+constexpr OUString PATHDELIMITER = u"/"_ustr;
+
+constexpr OUString SETNODE_NEWMENU = u"New"_ustr;
+constexpr OUString SETNODE_WIZARDMENU = u"Wizard"_ustr;
+
+#define PROPERTYNAME_URL DYNAMICMENU_PROPERTYNAME_URL
+#define PROPERTYNAME_TITLE DYNAMICMENU_PROPERTYNAME_TITLE
+#define PROPERTYNAME_IMAGEIDENTIFIER DYNAMICMENU_PROPERTYNAME_IMAGEIDENTIFIER
+#define PROPERTYNAME_TARGETNAME DYNAMICMENU_PROPERTYNAME_TARGETNAME
+
+#define PROPERTYCOUNT 4
+
+constexpr std::u16string_view PATHPREFIX_SETUP = u"m";
+
+namespace
+{
+/*-****************************************************************************************************************
+ @descr support simple menu structures and operations on it
+****************************************************************************************************************-*/
+struct SvtDynMenu
+{
+ // append setup written menu entry
+ // Don't touch name of entry. It was defined by setup and must be the same every time!
+ // Look for double menu entries here too... may be some separator items are superfluous...
+ void AppendSetupEntry( const SvtDynMenuEntry& rEntry )
+ {
+ if( lSetupEntries.empty() || lSetupEntries.rbegin()->sURL != rEntry.sURL )
+ lSetupEntries.push_back( rEntry );
+ }
+
+ // convert internal list to external format
+ // for using it on right menus really
+ // Notice: We build a property list with 4 entries and set it on result list then.
+ // Separator entries will be packed in another way then normal entries! We define
+ // special string "sSeparator" to perform too ...
+ std::vector< SvtDynMenuEntry > GetList() const
+ {
+ sal_Int32 nSetupCount = static_cast<sal_Int32>(lSetupEntries.size());
+ sal_Int32 nUserCount = static_cast<sal_Int32>(lUserEntries.size());
+ sal_Int32 nStep = 0;
+ std::vector< SvtDynMenuEntry > lResult ( nSetupCount+nUserCount );
+ OUString sSeparator ( "private:separator" );
+
+ for( const auto& pList : {&lSetupEntries, &lUserEntries} )
+ {
+ for( const auto& rItem : *pList )
+ {
+ SvtDynMenuEntry entry;
+ if( rItem.sURL == sSeparator )
+ {
+ entry.sURL = sSeparator;
+ }
+ else
+ {
+ entry = rItem;
+ }
+ lResult[nStep] = entry;
+ ++nStep;
+ }
+ }
+ return lResult;
+ }
+
+private:
+ std::vector< SvtDynMenuEntry > lSetupEntries;
+ std::vector< SvtDynMenuEntry > lUserEntries;
+};
+
+}
+
+namespace SvtDynamicMenuOptions
+{
+
+static Sequence< OUString > lcl_GetPropertyNames(
+ css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
+ sal_uInt32& nNewCount, sal_uInt32& nWizardCount );
+
+std::vector< SvtDynMenuEntry > GetMenu( EDynamicMenuType eMenu )
+{
+ SvtDynMenu aNewMenu;
+ SvtDynMenu aWizardMenu;
+
+ Reference<css::container::XHierarchicalNameAccess> xHierarchyAccess = utl::ConfigManager::acquireTree(u"Office.Common/Menus/");
+
+ // Get names and values of all accessible menu entries and fill internal structures.
+ // See impl_GetPropertyNames() for further information.
+ sal_uInt32 nNewCount = 0;
+ sal_uInt32 nWizardCount = 0;
+ Sequence< OUString > lNames = lcl_GetPropertyNames ( xHierarchyAccess, nNewCount ,
+ nWizardCount );
+ Sequence< Any > lValues = utl::ConfigItem::GetProperties( xHierarchyAccess, lNames, /*bAllLocales*/false );
+
+ // Safe impossible cases.
+ // We need values from ALL configuration keys.
+ // Follow assignment use order of values in relation to our list of key names!
+ DBG_ASSERT( !(lNames.getLength()!=lValues.getLength()), "SvtDynamicMenuOptions_Impl::SvtDynamicMenuOptions_Impl()\nI miss some values of configuration keys!\n" );
+
+ // Copy values from list in right order to our internal member.
+ // Attention: List for names and values have an internal construction pattern!
+
+ // first "New" menu ...
+ // Name Value
+ // /New/1/URL "private:factory/swriter"
+ // /New/1/Title "New Writer Document"
+ // /New/1/ImageIdentifier "icon_writer"
+ // /New/1/TargetName "_blank"
+
+ // /New/2/URL "private:factory/scalc"
+ // /New/2/Title "New Calc Document"
+ // /New/2/ImageIdentifier "icon_calc"
+ // /New/2/TargetName "_blank"
+
+ // second "Wizard" menu ...
+ // /Wizard/1/URL "file://b"
+ // /Wizard/1/Title "PaintSomething"
+ // /Wizard/1/ImageIdentifier "icon_?"
+ // /Wizard/1/TargetName "_self"
+
+ // ... and so on ...
+
+ sal_uInt32 nItem = 0;
+ sal_uInt32 nPosition = 0;
+
+ // Get names/values for new menu.
+ // 4 subkeys for every item!
+ for( nItem=0; nItem<nNewCount; ++nItem )
+ {
+ SvtDynMenuEntry aItem;
+ lValues[nPosition] >>= aItem.sURL;
+ ++nPosition;
+ lValues[nPosition] >>= aItem.sTitle;
+ ++nPosition;
+ lValues[nPosition] >>= aItem.sImageIdentifier;
+ ++nPosition;
+ lValues[nPosition] >>= aItem.sTargetName;
+ ++nPosition;
+ aNewMenu.AppendSetupEntry( aItem );
+ }
+
+ // Attention: Don't reset nPosition here!
+
+ // Get names/values for wizard menu.
+ // 4 subkeys for every item!
+ for( nItem=0; nItem<nWizardCount; ++nItem )
+ {
+ SvtDynMenuEntry aItem;
+ lValues[nPosition] >>= aItem.sURL;
+ ++nPosition;
+ lValues[nPosition] >>= aItem.sTitle;
+ ++nPosition;
+ lValues[nPosition] >>= aItem.sImageIdentifier;
+ ++nPosition;
+ lValues[nPosition] >>= aItem.sTargetName;
+ ++nPosition;
+ aWizardMenu.AppendSetupEntry( aItem );
+ }
+
+ std::vector< SvtDynMenuEntry > lReturn;
+ switch( eMenu )
+ {
+ case EDynamicMenuType::NewMenu :
+ lReturn = aNewMenu.GetList();
+ break;
+
+ case EDynamicMenuType::WizardMenu :
+ lReturn = aWizardMenu.GetList();
+ break;
+ }
+ return lReturn;
+}
+
+static void lcl_SortAndExpandPropertyNames( const Sequence< OUString >& lSource,
+ Sequence< OUString >& lDestination, std::u16string_view sSetNode );
+
+/*-****************************************************************************************************
+ @short return list of key names of our configuration management which represent our module tree
+ @descr This method returns the current list of key names! We need it to get needed values from our
+ configuration management and support dynamical menu item lists!
+ @param "nNewCount" , returns count of menu entries for "new"
+ @param "nWizardCount" , returns count of menu entries for "wizard"
+ @return A list of configuration key names is returned.
+*//*-*****************************************************************************************************/
+static Sequence< OUString > lcl_GetPropertyNames(
+ css::uno::Reference<css::container::XHierarchicalNameAccess> const & xHierarchyAccess,
+ sal_uInt32& nNewCount, sal_uInt32& nWizardCount )
+{
+ // First get ALL names of current existing list items in configuration!
+ Sequence< OUString > lNewItems = utl::ConfigItem::GetNodeNames( xHierarchyAccess, SETNODE_NEWMENU, utl::ConfigNameFormat::LocalPath );
+ Sequence< OUString > lWizardItems = utl::ConfigItem::GetNodeNames( xHierarchyAccess, SETNODE_WIZARDMENU, utl::ConfigNameFormat::LocalPath );
+
+ // Get information about list counts ...
+ nNewCount = lNewItems.getLength();
+ nWizardCount = lWizardItems.getLength();
+
+ // Sort and expand all three list to result list ...
+ Sequence< OUString > lProperties;
+ lcl_SortAndExpandPropertyNames( lNewItems , lProperties, SETNODE_NEWMENU );
+ lcl_SortAndExpandPropertyNames( lWizardItems , lProperties, SETNODE_WIZARDMENU );
+
+ // Return result.
+ return lProperties;
+}
+
+/*-****************************************************************************************************
+ @short sort given source list and expand it for all well known properties to destination
+ @descr We must support sets of entries with count inside the name .. but some of them could be missing!
+ e.g. s1-s2-s3-s0-u1-s6-u5-u7
+ Then we must sort it by name and expand it to the follow one:
+ sSetNode/s0/URL
+ sSetNode/s0/Title
+ sSetNode/s0/...
+ sSetNode/s1/URL
+ sSetNode/s1/Title
+ sSetNode/s1/...
+ ...
+ sSetNode/s6/URL
+ sSetNode/s6/Title
+ sSetNode/s6/...
+ sSetNode/u1/URL
+ sSetNode/u1/Title
+ sSetNode/u1/...
+ ...
+ sSetNode/u7/URL
+ sSetNode/u7/Title
+ sSetNode/u7/...
+ Rules: We start with all setup written entries names "sx" and x=[0..n].
+ Then we handle all "ux" items. Inside these blocks we sort it ascending by number.
+
+ @attention We add these expanded list to the end of given "lDestination" list!
+ So we must start on "lDestination.getLength()".
+ Reallocation of memory of destination list is done by us!
+
+ @seealso method impl_GetPropertyNames()
+
+ @param "lSource" , original list (e.g. [m1-m2-m3-m6-m0] )
+ @param "lDestination" , destination of operation
+ @param "sSetNode" , name of configuration set to build complete path
+ @return A list of configuration key names is returned.
+*//*-*****************************************************************************************************/
+
+static void lcl_SortAndExpandPropertyNames( const Sequence< OUString >& lSource ,
+ Sequence< OUString >& lDestination ,
+ std::u16string_view sSetNode )
+{
+ struct CountWithPrefixSort
+ {
+ bool operator() ( std::u16string_view s1, std::u16string_view s2 ) const
+ {
+ // Get order numbers from entry name without prefix.
+ // e.g. "m10" => 10
+ // "m5" => 5
+ sal_Int32 n1 = o3tl::toInt32(s1.substr( 1 ));
+ sal_Int32 n2 = o3tl::toInt32(s2.substr( 1 ));
+ // MUST be in [0,1] ... because it's a difference between
+ // insert-positions of given entries in sorted list!
+ return( n1<n2 );
+ }
+ };
+ struct SelectByPrefix
+ {
+ bool operator() ( std::u16string_view s ) const
+ {
+ // Prefer setup written entries by check first letter of given string. It must be a "s".
+ return o3tl::starts_with( s, PATHPREFIX_SETUP );
+ }
+ };
+
+ std::vector< OUString > lTemp;
+ sal_Int32 nSourceCount = lSource.getLength();
+ sal_Int32 nDestinationStep = lDestination.getLength(); // start on end of current list ...!
+
+ lDestination.realloc( (nSourceCount*PROPERTYCOUNT)+nDestinationStep ); // get enough memory for copy operations after nDestination ...
+ auto plDestination = lDestination.getArray();
+
+ // Copy all items to temp. vector to use fast sort operations :-)
+ lTemp.insert( lTemp.end(), lSource.begin(), lSource.end() );
+
+ // Sort all entries by number ...
+ std::stable_sort( lTemp.begin(), lTemp.end(), CountWithPrefixSort() );
+ // and split into setup & user written entries!
+ std::stable_partition( lTemp.begin(), lTemp.end(), SelectByPrefix() );
+
+ // Copy sorted entries to destination and expand every item with
+ // 4 supported sub properties.
+ for( const auto& rItem : lTemp )
+ {
+ OUString sFixPath(OUString::Concat(sSetNode) + PATHDELIMITER + rItem + PATHDELIMITER);
+ plDestination[nDestinationStep++] = sFixPath + PROPERTYNAME_URL;
+ plDestination[nDestinationStep++] = sFixPath + PROPERTYNAME_TITLE;
+ plDestination[nDestinationStep++] = sFixPath + PROPERTYNAME_IMAGEIDENTIFIER;
+ plDestination[nDestinationStep++] = sFixPath + PROPERTYNAME_TARGETNAME;
+ }
+}
+
+
+} // namespace SvtDynamicMenuOptions
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/eventcfg.cxx b/unotools/source/config/eventcfg.cxx
new file mode 100644
index 0000000000..62f854c003
--- /dev/null
+++ b/unotools/source/config/eventcfg.cxx
@@ -0,0 +1,380 @@
+/* -*- 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 <comphelper/propertyvalue.hxx>
+#include <unotools/eventcfg.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/configitem.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <o3tl/enumarray.hxx>
+#include <o3tl/enumrange.hxx>
+#include <rtl/ref.hxx>
+#include <sal/log.hxx>
+
+#include "itemholder1.hxx"
+
+#include <algorithm>
+#include <unordered_map>
+
+using namespace ::utl;
+using namespace ::osl;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star;
+
+#define PATHDELIMITER "/"
+#define SETNODE_BINDINGS "Bindings"
+#define PROPERTYNAME_BINDINGURL "BindingURL"
+
+static o3tl::enumarray<GlobalEventId, const char*> pEventAsciiNames =
+{
+"OnStartApp",
+"OnCloseApp",
+"OnCreate",
+"OnNew",
+"OnLoadFinished",
+"OnLoad",
+"OnPrepareUnload",
+"OnUnload",
+"OnSave",
+"OnSaveDone",
+"OnSaveFailed",
+"OnSaveAs",
+"OnSaveAsDone",
+"OnSaveAsFailed",
+"OnCopyTo",
+"OnCopyToDone",
+"OnCopyToFailed",
+"OnFocus",
+"OnUnfocus",
+"OnPrint",
+"OnViewCreated",
+"OnPrepareViewClosing",
+"OnViewClosed",
+"OnModifyChanged",
+"OnTitleChanged",
+"OnVisAreaChanged",
+"OnModeChanged",
+"OnStorageChanged"
+};
+
+typedef std::unordered_map< OUString, OUString > EventBindingHash;
+typedef o3tl::enumarray< GlobalEventId, OUString > SupportedEventsVector;
+
+static std::mutex& GetOwnStaticMutex()
+{
+ static std::mutex INSTANCE;
+ return INSTANCE;
+}
+
+class GlobalEventConfig_Impl : public utl::ConfigItem
+{
+private:
+ EventBindingHash m_eventBindingHash;
+ SupportedEventsVector m_supportedEvents;
+
+ void initBindingInfo();
+
+ virtual void ImplCommit() override;
+
+public:
+ GlobalEventConfig_Impl( );
+ virtual ~GlobalEventConfig_Impl( ) override;
+
+ void Notify( const css::uno::Sequence<OUString>& aPropertyNames) override;
+
+ /// @throws css::lang::IllegalArgumentException
+ /// @throws css::container::NoSuchElementException
+ /// @throws css::lang::WrappedTargetException
+ /// @throws css::uno::RuntimeException
+ void replaceByName( const OUString& aName, const css::uno::Any& aElement );
+ /// @throws css::container::NoSuchElementException
+ /// @throws css::lang::WrappedTargetException
+ /// @throws css::uno::RuntimeException
+ css::uno::Sequence < css::beans::PropertyValue > getByName( const OUString& aName );
+ /// @throws css::uno::RuntimeException
+ css::uno::Sequence< OUString > getElementNames( );
+ /// @throws css::uno::RuntimeException
+ bool hasByName( const OUString& aName );
+ /// @throws css::uno::RuntimeException
+ static css::uno::Type const & getElementType( );
+ /// @throws css::uno::RuntimeException
+ bool hasElements() const;
+ OUString const & GetEventName( GlobalEventId nID ) const;
+};
+
+
+GlobalEventConfig_Impl::GlobalEventConfig_Impl()
+ : ConfigItem( "Office.Events/ApplicationEvents", ConfigItemMode::NONE )
+{
+ // the supported event names
+ for (const GlobalEventId id : o3tl::enumrange<GlobalEventId>())
+ m_supportedEvents[id] = OUString::createFromAscii( pEventAsciiNames[id] );
+
+ initBindingInfo();
+
+/*TODO: Not used in the moment! see Notify() ...
+ // Enable notification mechanism of our baseclass.
+ // We need it to get information about changes outside these class on our used configuration keys! */
+ Sequence<OUString> aNotifySeq { "Events" };
+ EnableNotification( aNotifySeq, true );
+}
+
+// destructor
+
+GlobalEventConfig_Impl::~GlobalEventConfig_Impl()
+{
+ assert(!IsModified()); // should have been committed
+}
+
+OUString const & GlobalEventConfig_Impl::GetEventName( GlobalEventId nIndex ) const
+{
+ return m_supportedEvents[nIndex];
+}
+
+// public method
+
+void GlobalEventConfig_Impl::Notify( const Sequence< OUString >& )
+{
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+
+ initBindingInfo();
+}
+
+// public method
+
+void GlobalEventConfig_Impl::ImplCommit()
+{
+ //DF need to check it this is correct??
+ SAL_INFO("unotools", "In GlobalEventConfig_Impl::ImplCommit");
+ // clear the existing nodes
+ ClearNodeSet( SETNODE_BINDINGS );
+ OUString sNode;
+ //step through the list of events
+ for(const auto& rEntry : m_eventBindingHash)
+ {
+ //no point in writing out empty bindings!
+ if(rEntry.second.isEmpty() )
+ continue;
+ sNode = SETNODE_BINDINGS PATHDELIMITER "BindingType['" +
+ rEntry.first +
+ "']" PATHDELIMITER PROPERTYNAME_BINDINGURL;
+ SAL_INFO("unotools", "writing binding for: " << sNode);
+ //write the data to the registry
+ SetSetProperties(SETNODE_BINDINGS,{ comphelper::makePropertyValue(sNode, rEntry.second) });
+ }
+}
+
+// private method
+
+void GlobalEventConfig_Impl::initBindingInfo()
+{
+ // Get ALL names of current existing list items in configuration!
+ const Sequence< OUString > lEventNames = GetNodeNames( SETNODE_BINDINGS, utl::ConfigNameFormat::LocalPath );
+
+ OUString aSetNode = SETNODE_BINDINGS PATHDELIMITER;
+ OUString aCommandKey = PATHDELIMITER PROPERTYNAME_BINDINGURL;
+
+ // Expand all keys
+ Sequence< OUString > lMacros(1);
+ auto plMacros = lMacros.getArray();
+ for (const auto& rEventName : lEventNames )
+ {
+ plMacros[0] = aSetNode + rEventName + aCommandKey;
+ SAL_INFO("unotools", "reading binding for: " << lMacros[0]);
+ Sequence< Any > lValues = GetProperties( lMacros );
+ if( lValues.hasElements() )
+ {
+ OUString sMacroURL;
+ lValues[0] >>= sMacroURL;
+ sal_Int32 startIndex = rEventName.indexOf('\'');
+ sal_Int32 endIndex = rEventName.lastIndexOf('\'');
+ if( startIndex >=0 && endIndex > 0 )
+ {
+ startIndex++;
+ OUString eventName = rEventName.copy(startIndex,endIndex-startIndex);
+ m_eventBindingHash[ eventName ] = sMacroURL;
+ }
+ }
+ }
+}
+
+void GlobalEventConfig_Impl::replaceByName( const OUString& aName, const Any& aElement )
+{
+ Sequence< beans::PropertyValue > props;
+ //DF should we prepopulate the hash with a list of valid event Names?
+ if( !( aElement >>= props ) )
+ {
+ throw lang::IllegalArgumentException( OUString(),
+ Reference< XInterface > (), 2);
+ }
+ OUString macroURL;
+ for( const auto& rProp : std::as_const(props) )
+ {
+ if ( rProp.Name == "Script" )
+ rProp.Value >>= macroURL;
+ }
+ m_eventBindingHash[ aName ] = macroURL;
+ SetModified();
+}
+
+css::uno::Sequence < css::beans::PropertyValue > GlobalEventConfig_Impl::getByName( const OUString& aName )
+{
+ static constexpr OUStringLiteral sEventType = u"EventType";
+ static constexpr OUString sScript = u"Script"_ustr;
+ Sequence< beans::PropertyValue > props(2);
+ auto pProps = props.getArray();
+ pProps[0].Name = sEventType;
+ pProps[0].Value <<= sScript;
+ pProps[1].Name = sScript;
+ EventBindingHash::const_iterator it = m_eventBindingHash.find( aName );
+ if( it != m_eventBindingHash.end() )
+ {
+ pProps[1].Value <<= it->second;
+ }
+ else
+ {
+ // not yet accessed - is it a supported name?
+ SupportedEventsVector::iterator pos = ::std::find(
+ m_supportedEvents.begin(), m_supportedEvents.end(), aName );
+ if ( pos == m_supportedEvents.end() )
+ throw container::NoSuchElementException( aName );
+
+ pProps[1].Value <<= OUString();
+ }
+ return props;
+}
+
+Sequence< OUString > GlobalEventConfig_Impl::getElementNames( )
+{
+ return uno::Sequence< OUString >(m_supportedEvents.data(), SupportedEventsVector::size());
+}
+
+bool GlobalEventConfig_Impl::hasByName( const OUString& aName )
+{
+ if ( m_eventBindingHash.find( aName ) != m_eventBindingHash.end() )
+ return true;
+
+ // never accessed before - is it supported in general?
+ SupportedEventsVector::iterator pos = ::std::find(
+ m_supportedEvents.begin(), m_supportedEvents.end(), aName );
+ return pos != m_supportedEvents.end();
+}
+
+Type const & GlobalEventConfig_Impl::getElementType( )
+{
+ //DF definitely not sure about this??
+ return cppu::UnoType<Sequence<beans::PropertyValue>>::get();
+}
+
+bool GlobalEventConfig_Impl::hasElements() const
+{
+ return !m_eventBindingHash.empty();
+}
+
+// and now the wrapper
+
+//initialize static member
+GlobalEventConfig_Impl* GlobalEventConfig::m_pImpl = nullptr;
+sal_Int32 GlobalEventConfig::m_nRefCount = 0;
+
+GlobalEventConfig::GlobalEventConfig()
+{
+ // Global access, must be guarded (multithreading!).
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+ // Increase our refcount ...
+ ++m_nRefCount;
+ // ... and initialize our data container only if it not already exist!
+ if( m_pImpl == nullptr )
+ {
+ m_pImpl = new GlobalEventConfig_Impl;
+ aGuard.unlock();
+ ItemHolder1::holdConfigItem(EItem::EventConfig);
+ }
+}
+
+GlobalEventConfig::~GlobalEventConfig()
+{
+ // Global access, must be guarded (multithreading!)
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+ // Decrease our refcount.
+ --m_nRefCount;
+ // If last instance was deleted ...
+ // we must destroy our static data container!
+ if( m_nRefCount <= 0 )
+ {
+ delete m_pImpl;
+ m_pImpl = nullptr;
+ }
+}
+
+Reference< container::XNameReplace > SAL_CALL GlobalEventConfig::getEvents()
+{
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+ Reference< container::XNameReplace > ret(this);
+ return ret;
+}
+
+void SAL_CALL GlobalEventConfig::replaceByName( const OUString& aName, const Any& aElement )
+{
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+ m_pImpl->replaceByName( aName, aElement );
+}
+Any SAL_CALL GlobalEventConfig::getByName( const OUString& aName )
+{
+ return Any(getByName2(aName));
+}
+css::uno::Sequence < css::beans::PropertyValue > GlobalEventConfig::getByName2( const OUString& aName )
+{
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+ return m_pImpl->getByName( aName );
+}
+Sequence< OUString > SAL_CALL GlobalEventConfig::getElementNames( )
+{
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+ return m_pImpl->getElementNames( );
+}
+sal_Bool SAL_CALL GlobalEventConfig::hasByName( const OUString& aName )
+{
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+ return m_pImpl->hasByName( aName );
+}
+Type SAL_CALL GlobalEventConfig::getElementType( )
+{
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+ return GlobalEventConfig_Impl::getElementType( );
+}
+sal_Bool SAL_CALL GlobalEventConfig::hasElements( )
+{
+ std::unique_lock aGuard( GetOwnStaticMutex() );
+ return m_pImpl->hasElements( );
+}
+
+OUString GlobalEventConfig::GetEventName( GlobalEventId nIndex )
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return OUString();
+ static rtl::Reference<GlobalEventConfig> createImpl(new GlobalEventConfig);
+ return GlobalEventConfig::m_pImpl->GetEventName( nIndex );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/fltrcfg.cxx b/unotools/source/config/fltrcfg.cxx
new file mode 100644
index 0000000000..21a540e404
--- /dev/null
+++ b/unotools/source/config/fltrcfg.cxx
@@ -0,0 +1,670 @@
+/* -*- 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/any.hxx>
+#include <o3tl/typed_flags_set.hxx>
+#include <unotools/fltrcfg.hxx>
+#include <tools/debug.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/uno/Sequence.hxx>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+
+namespace {
+
+enum class ConfigFlags {
+ NONE = 0x0000000,
+ WordCode = 0x0000001,
+ WordStorage = 0x0000002,
+ ExcelCode = 0x0000004,
+ ExcelStorage = 0x0000008,
+ PowerPointCode = 0x0000010,
+ PowerPointStorage = 0x0000020,
+ MathLoad = 0x0000100,
+ MathSave = 0x0000200,
+ WriterLoad = 0x0000400,
+ WriterSave = 0x0000800,
+ CalcLoad = 0x0001000,
+ CalcSave = 0x0002000,
+ ImpressLoad = 0x0004000,
+ ImpressSave = 0x0008000,
+ ExcelExecTbl = 0x0010000,
+ EnablePowerPointPreview = 0x0020000,
+ EnableExcelPreview = 0x0040000,
+ EnableWordPreview = 0x0080000,
+ UseEnhancedFields = 0x0100000,
+ WordWbctbl = 0x0200000,
+ SmartArtShapeLoad = 0x0400000,
+ CharBackgroundToHighlighting = 0x8000000,
+ CreateMSOLockFiles = 0x2000000,
+ VisioLoad = 0x4000000,
+};
+
+}
+
+namespace o3tl {
+ template<> struct typed_flags<ConfigFlags> : is_typed_flags<ConfigFlags, 0xe7fff3f> {};
+}
+
+namespace {
+
+class SvtAppFilterOptions_Impl : public utl::ConfigItem
+{
+private:
+ bool bLoadVBA;
+ bool bSaveVBA;
+
+protected:
+ virtual void ImplCommit() override;
+
+public:
+ explicit SvtAppFilterOptions_Impl(const OUString& rRoot) :
+ utl::ConfigItem(rRoot),
+ bLoadVBA(false),
+ bSaveVBA(false) {}
+ virtual ~SvtAppFilterOptions_Impl() override;
+ virtual void Notify( const css::uno::Sequence<OUString>& aPropertyNames) override;
+ void Load();
+
+ bool IsLoad() const {return bLoadVBA;}
+ void SetLoad(bool bSet)
+ {
+ if(bSet != bLoadVBA)
+ SetModified();
+ bLoadVBA = bSet;
+ }
+ bool IsSave() const {return bSaveVBA;}
+ void SetSave(bool bSet)
+ {
+ if(bSet != bSaveVBA)
+ SetModified();
+ bSaveVBA = bSet;
+ }
+};
+
+}
+
+SvtAppFilterOptions_Impl::~SvtAppFilterOptions_Impl()
+{
+ assert(!IsModified()); // should have been committed
+}
+
+void SvtAppFilterOptions_Impl::ImplCommit()
+{
+ PutProperties(
+ {"Load", "Save"}, {css::uno::Any(bLoadVBA), css::uno::Any(bSaveVBA)});
+}
+
+void SvtAppFilterOptions_Impl::Notify( const Sequence< OUString >& )
+{
+ // no listeners supported yet
+}
+
+void SvtAppFilterOptions_Impl::Load()
+{
+ Sequence<Any> aValues = GetProperties({ "Load", "Save" });
+ const Any* pValues = aValues.getConstArray();
+
+ if(pValues[0].hasValue())
+ bLoadVBA = *o3tl::doAccess<bool>(pValues[0]);
+ if(pValues[1].hasValue())
+ bSaveVBA = *o3tl::doAccess<bool>(pValues[1]);
+}
+
+namespace {
+
+class SvtWriterFilterOptions_Impl : public SvtAppFilterOptions_Impl
+{
+private:
+ bool bLoadExecutable;
+
+ virtual void ImplCommit() override;
+
+public:
+ explicit SvtWriterFilterOptions_Impl(const OUString& rRoot) :
+ SvtAppFilterOptions_Impl(rRoot),
+ bLoadExecutable(false)
+ {}
+ void Load();
+
+ bool IsLoadExecutable() const {return bLoadExecutable;}
+ void SetLoadExecutable(bool bSet)
+ {
+ if(bSet != bLoadExecutable)
+ SetModified();
+ bLoadExecutable = bSet;
+ }
+};
+
+}
+
+void SvtWriterFilterOptions_Impl::ImplCommit()
+{
+ SvtAppFilterOptions_Impl::ImplCommit();
+
+ PutProperties({ "Executable" }, { Any(bLoadExecutable) });
+}
+
+void SvtWriterFilterOptions_Impl::Load()
+{
+ SvtAppFilterOptions_Impl::Load();
+
+ Sequence<OUString> aNames { "Executable" };
+
+ Sequence<Any> aValues = GetProperties(aNames);
+ const Any* pValues = aValues.getConstArray();
+ if(pValues[0].hasValue())
+ bLoadExecutable = *o3tl::doAccess<bool>(pValues[0]);
+}
+
+namespace {
+
+class SvtCalcFilterOptions_Impl : public SvtAppFilterOptions_Impl
+{
+private:
+ bool bLoadExecutable;
+
+ virtual void ImplCommit() override;
+
+public:
+ explicit SvtCalcFilterOptions_Impl(const OUString& rRoot) :
+ SvtAppFilterOptions_Impl(rRoot),
+ bLoadExecutable(false)
+ {}
+ void Load();
+
+ bool IsLoadExecutable() const {return bLoadExecutable;}
+ void SetLoadExecutable(bool bSet)
+ {
+ if(bSet != bLoadExecutable)
+ SetModified();
+ bLoadExecutable = bSet;
+ }
+};
+
+}
+
+void SvtCalcFilterOptions_Impl::ImplCommit()
+{
+ SvtAppFilterOptions_Impl::ImplCommit();
+
+ PutProperties({ "Executable" }, { Any(bLoadExecutable) });
+}
+
+void SvtCalcFilterOptions_Impl::Load()
+{
+ SvtAppFilterOptions_Impl::Load();
+
+ Sequence<OUString> aNames { "Executable" };
+
+ Sequence<Any> aValues = GetProperties(aNames);
+ const Any* pValues = aValues.getConstArray();
+ if(pValues[0].hasValue())
+ bLoadExecutable = *o3tl::doAccess<bool>(pValues[0]);
+}
+
+struct SvtFilterOptions_Impl
+{
+ ConfigFlags nFlags;
+ SvtWriterFilterOptions_Impl aWriterCfg;
+ SvtCalcFilterOptions_Impl aCalcCfg;
+ SvtAppFilterOptions_Impl aImpressCfg;
+
+ SvtFilterOptions_Impl() :
+ aWriterCfg("Office.Writer/Filter/Import/VBA"),
+ aCalcCfg("Office.Calc/Filter/Import/VBA"),
+ aImpressCfg("Office.Impress/Filter/Import/VBA")
+ {
+ nFlags = ConfigFlags::WordCode |
+ ConfigFlags::WordStorage |
+ ConfigFlags::ExcelCode |
+ ConfigFlags::ExcelStorage |
+ ConfigFlags::PowerPointCode |
+ ConfigFlags::PowerPointStorage |
+ ConfigFlags::MathLoad |
+ ConfigFlags::MathSave |
+ ConfigFlags::WriterLoad |
+ ConfigFlags::WriterSave |
+ ConfigFlags::CalcLoad |
+ ConfigFlags::CalcSave |
+ ConfigFlags::ImpressLoad |
+ ConfigFlags::ImpressSave |
+ ConfigFlags::UseEnhancedFields |
+ ConfigFlags::SmartArtShapeLoad |
+ ConfigFlags::CharBackgroundToHighlighting|
+ ConfigFlags::CreateMSOLockFiles;
+ Load();
+ }
+
+ void SetFlag( ConfigFlags nFlag, bool bSet );
+ bool IsFlag( ConfigFlags nFlag ) const;
+ void Load()
+ {
+ aWriterCfg.Load();
+ aCalcCfg.Load();
+ aImpressCfg.Load();
+ }
+};
+
+void SvtFilterOptions_Impl::SetFlag( ConfigFlags nFlag, bool bSet )
+{
+ switch(nFlag)
+ {
+ case ConfigFlags::WordCode: aWriterCfg.SetLoad(bSet);break;
+ case ConfigFlags::WordStorage: aWriterCfg.SetSave(bSet);break;
+ case ConfigFlags::WordWbctbl: aWriterCfg.SetLoadExecutable(bSet);break;
+ case ConfigFlags::ExcelCode: aCalcCfg.SetLoad(bSet);break;
+ case ConfigFlags::ExcelStorage: aCalcCfg.SetSave(bSet);break;
+ case ConfigFlags::ExcelExecTbl: aCalcCfg.SetLoadExecutable(bSet);break;
+ case ConfigFlags::PowerPointCode: aImpressCfg.SetLoad(bSet);break;
+ case ConfigFlags::PowerPointStorage: aImpressCfg.SetSave(bSet);break;
+ default:
+ if( bSet )
+ nFlags |= nFlag;
+ else
+ nFlags &= ~nFlag;
+ }
+}
+
+bool SvtFilterOptions_Impl::IsFlag( ConfigFlags nFlag ) const
+{
+ bool bRet;
+ switch(nFlag)
+ {
+ case ConfigFlags::WordCode : bRet = aWriterCfg.IsLoad();break;
+ case ConfigFlags::WordStorage : bRet = aWriterCfg.IsSave();break;
+ case ConfigFlags::WordWbctbl : bRet = aWriterCfg.IsLoadExecutable();break;
+ case ConfigFlags::ExcelCode : bRet = aCalcCfg.IsLoad();break;
+ case ConfigFlags::ExcelStorage : bRet = aCalcCfg.IsSave();break;
+ case ConfigFlags::ExcelExecTbl : bRet = aCalcCfg.IsLoadExecutable();break;
+ case ConfigFlags::PowerPointCode : bRet = aImpressCfg.IsLoad();break;
+ case ConfigFlags::PowerPointStorage : bRet = aImpressCfg.IsSave();break;
+ default:
+ bRet = bool(nFlags & nFlag );
+ }
+ return bRet;
+}
+
+namespace {
+
+const Sequence<OUString>& GetPropertyNames()
+{
+ static Sequence<OUString> const aNames
+ {
+ "Import/MathTypeToMath", // 0
+ "Import/WinWordToWriter", // 1
+ "Import/PowerPointToImpress", // 2
+ "Import/ExcelToCalc", // 3
+ "Export/MathToMathType", // 4
+ "Export/WriterToWinWord", // 5
+ "Export/ImpressToPowerPoint", // 6
+ "Export/CalcToExcel", // 7
+ "Export/EnablePowerPointPreview", // 8
+ "Export/EnableExcelPreview", // 9
+ "Export/EnableWordPreview", // 10
+ "Import/ImportWWFieldsAsEnhancedFields", // 11
+ "Import/SmartArtToShapes", // 12
+ "Export/CharBackgroundToHighlighting", // 13
+ "Import/CreateMSOLockFiles", // 14
+ "Import/VisioToDraw" // 15
+ };
+ return aNames;
+}
+
+}
+
+SvtFilterOptions::SvtFilterOptions() :
+ ConfigItem( "Office.Common/Filter/Microsoft" ),
+ pImpl(new SvtFilterOptions_Impl)
+{
+ EnableNotification(GetPropertyNames());
+ Load();
+}
+
+SvtFilterOptions::~SvtFilterOptions()
+{
+}
+
+static ConfigFlags lcl_GetFlag(sal_Int32 nProp)
+{
+ ConfigFlags nFlag = ConfigFlags::NONE;
+ switch(nProp)
+ {
+ case 0: nFlag = ConfigFlags::MathLoad; break;
+ case 1: nFlag = ConfigFlags::WriterLoad; break;
+ case 2: nFlag = ConfigFlags::ImpressLoad; break;
+ case 3: nFlag = ConfigFlags::CalcLoad; break;
+ case 4: nFlag = ConfigFlags::MathSave; break;
+ case 5: nFlag = ConfigFlags::WriterSave; break;
+ case 6: nFlag = ConfigFlags::ImpressSave; break;
+ case 7: nFlag = ConfigFlags::CalcSave; break;
+ case 8: nFlag = ConfigFlags::EnablePowerPointPreview; break;
+ case 9: nFlag = ConfigFlags::EnableExcelPreview; break;
+ case 10: nFlag = ConfigFlags::EnableWordPreview; break;
+ case 11: nFlag = ConfigFlags::UseEnhancedFields; break;
+ case 12: nFlag = ConfigFlags::SmartArtShapeLoad; break;
+ case 13: nFlag = ConfigFlags::CharBackgroundToHighlighting; break;
+ case 14: nFlag = ConfigFlags::CreateMSOLockFiles; break;
+ case 15:
+ nFlag = ConfigFlags::VisioLoad;
+ break;
+
+ default: OSL_FAIL("illegal value");
+ }
+ return nFlag;
+}
+
+void SvtFilterOptions::Notify( const Sequence<OUString>& )
+{
+ Load();
+}
+
+void SvtFilterOptions::ImplCommit()
+{
+ const Sequence<OUString>& aNames = GetPropertyNames();
+ Sequence<Any> aValues(aNames.getLength());
+ Any* pValues = aValues.getArray();
+
+ for(int nProp = 0; nProp < aNames.getLength(); nProp++)
+ {
+ ConfigFlags nFlag = lcl_GetFlag(nProp);
+ pValues[nProp] <<= pImpl->IsFlag(nFlag);
+
+ }
+ PutProperties(aNames, aValues);
+}
+
+void SvtFilterOptions::Load()
+{
+ pImpl->Load();
+ const Sequence<OUString>& rNames = GetPropertyNames();
+ Sequence<Any> aValues = GetProperties(rNames);
+ const Any* pValues = aValues.getConstArray();
+ DBG_ASSERT(aValues.getLength() == rNames.getLength(), "GetProperties failed");
+ if(aValues.getLength() == rNames.getLength())
+ {
+ for(int nProp = 0; nProp < rNames.getLength(); nProp++)
+ {
+ if(pValues[nProp].hasValue())
+ {
+ bool bVal = *o3tl::doAccess<bool>(pValues[nProp]);
+ ConfigFlags nFlag = lcl_GetFlag(nProp);
+ pImpl->SetFlag( nFlag, bVal);
+ }
+ }
+ }
+}
+
+void SvtFilterOptions::SetLoadWordBasicCode( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::WordCode, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsLoadWordBasicCode() const
+{
+ return pImpl->IsFlag( ConfigFlags::WordCode );
+}
+
+void SvtFilterOptions::SetLoadWordBasicExecutable( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::WordWbctbl, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsLoadWordBasicExecutable() const
+{
+ return pImpl->IsFlag( ConfigFlags::WordWbctbl );
+}
+
+void SvtFilterOptions::SetLoadWordBasicStorage( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::WordStorage, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsLoadWordBasicStorage() const
+{
+ return pImpl->IsFlag( ConfigFlags::WordStorage );
+}
+
+void SvtFilterOptions::SetLoadExcelBasicCode( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::ExcelCode, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsLoadExcelBasicCode() const
+{
+ return pImpl->IsFlag( ConfigFlags::ExcelCode );
+}
+
+void SvtFilterOptions::SetLoadExcelBasicExecutable( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::ExcelExecTbl, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsLoadExcelBasicExecutable() const
+{
+ return pImpl->IsFlag( ConfigFlags::ExcelExecTbl );
+}
+
+void SvtFilterOptions::SetLoadExcelBasicStorage( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::ExcelStorage, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsLoadExcelBasicStorage() const
+{
+ return pImpl->IsFlag( ConfigFlags::ExcelStorage );
+}
+
+void SvtFilterOptions::SetLoadPPointBasicCode( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::PowerPointCode, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsLoadPPointBasicCode() const
+{
+ return pImpl->IsFlag( ConfigFlags::PowerPointCode );
+}
+
+void SvtFilterOptions::SetLoadPPointBasicStorage( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::PowerPointStorage, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsLoadPPointBasicStorage() const
+{
+ return pImpl->IsFlag( ConfigFlags::PowerPointStorage );
+}
+
+bool SvtFilterOptions::IsMathType2Math() const
+{
+ return pImpl->IsFlag( ConfigFlags::MathLoad );
+}
+
+void SvtFilterOptions::SetMathType2Math( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::MathLoad, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsMath2MathType() const
+{
+ return pImpl->IsFlag( ConfigFlags::MathSave );
+}
+
+void SvtFilterOptions::SetMath2MathType( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::MathSave, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsWinWord2Writer() const
+{
+ return pImpl->IsFlag( ConfigFlags::WriterLoad );
+}
+
+void SvtFilterOptions::SetWinWord2Writer( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::WriterLoad, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsWriter2WinWord() const
+{
+ return pImpl->IsFlag( ConfigFlags::WriterSave );
+}
+
+void SvtFilterOptions::SetWriter2WinWord( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::WriterSave, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsUseEnhancedFields() const
+{
+ return pImpl->IsFlag( ConfigFlags::UseEnhancedFields );
+}
+
+bool SvtFilterOptions::IsExcel2Calc() const
+{
+ return pImpl->IsFlag( ConfigFlags::CalcLoad );
+}
+
+void SvtFilterOptions::SetExcel2Calc( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::CalcLoad, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsCalc2Excel() const
+{
+ return pImpl->IsFlag( ConfigFlags::CalcSave );
+}
+
+void SvtFilterOptions::SetCalc2Excel( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::CalcSave, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsPowerPoint2Impress() const
+{
+ return pImpl->IsFlag( ConfigFlags::ImpressLoad );
+}
+
+void SvtFilterOptions::SetPowerPoint2Impress( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::ImpressLoad, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsImpress2PowerPoint() const
+{
+ return pImpl->IsFlag( ConfigFlags::ImpressSave );
+}
+
+void SvtFilterOptions::SetImpress2PowerPoint( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::ImpressSave, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsSmartArt2Shape() const
+{
+ return pImpl->IsFlag( ConfigFlags::SmartArtShapeLoad );
+}
+
+void SvtFilterOptions::SetSmartArt2Shape( bool bFlag )
+{
+ pImpl->SetFlag( ConfigFlags::SmartArtShapeLoad, bFlag );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsVisio2Draw() const { return pImpl->IsFlag(ConfigFlags::VisioLoad); }
+
+void SvtFilterOptions::SetVisio2Draw(bool bFlag)
+{
+ pImpl->SetFlag(ConfigFlags::VisioLoad, bFlag);
+ SetModified();
+}
+
+SvtFilterOptions& SvtFilterOptions::Get()
+{
+ static SvtFilterOptions aOptions;
+ return aOptions;
+}
+
+bool SvtFilterOptions::IsEnablePPTPreview() const
+{
+ return pImpl->IsFlag( ConfigFlags::EnablePowerPointPreview );
+}
+
+bool SvtFilterOptions::IsEnableCalcPreview() const
+{
+ return pImpl->IsFlag( ConfigFlags::EnableExcelPreview );
+}
+
+bool SvtFilterOptions::IsEnableWordPreview() const
+{
+ return pImpl->IsFlag( ConfigFlags::EnableWordPreview );
+}
+
+bool SvtFilterOptions::IsCharBackground2Highlighting() const
+{
+ return pImpl->IsFlag( ConfigFlags::CharBackgroundToHighlighting );
+}
+
+bool SvtFilterOptions::IsCharBackground2Shading() const
+{
+ return !pImpl->IsFlag( ConfigFlags::CharBackgroundToHighlighting );
+}
+
+void SvtFilterOptions::SetCharBackground2Highlighting()
+{
+ pImpl->SetFlag( ConfigFlags::CharBackgroundToHighlighting, true );
+ SetModified();
+}
+
+void SvtFilterOptions::SetCharBackground2Shading()
+{
+ pImpl->SetFlag( ConfigFlags::CharBackgroundToHighlighting, false );
+ SetModified();
+}
+
+bool SvtFilterOptions::IsMSOLockFileCreationIsEnabled() const
+{
+ return pImpl->IsFlag( ConfigFlags::CreateMSOLockFiles );
+}
+
+void SvtFilterOptions::EnableMSOLockFileCreation(bool bEnable)
+{
+ pImpl->SetFlag( ConfigFlags::CreateMSOLockFiles, bEnable );
+ SetModified();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/fontcfg.cxx b/unotools/source/config/fontcfg.cxx
new file mode 100644
index 0000000000..26ceda82d5
--- /dev/null
+++ b/unotools/source/config/fontcfg.cxx
@@ -0,0 +1,1076 @@
+/* -*- 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 <i18nlangtag/mslangid.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <o3tl/any.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/fontcfg.hxx>
+#include <unotools/fontdefs.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <comphelper/propertysequence.hxx>
+#include <unotools/syslocale.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/diagnose.h>
+#include <sal/log.hxx>
+
+#include <string.h>
+#include <algorithm>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::container;
+using namespace com::sun::star::configuration;
+
+/*
+ * DefaultFontConfiguration
+ */
+
+static const char* getKeyType( DefaultFontType nKeyType )
+{
+ switch( nKeyType )
+ {
+ case DefaultFontType::CJK_DISPLAY: return "CJK_DISPLAY";
+ case DefaultFontType::CJK_HEADING: return "CJK_HEADING";
+ case DefaultFontType::CJK_PRESENTATION: return "CJK_PRESENTATION";
+ case DefaultFontType::CJK_SPREADSHEET: return "CJK_SPREADSHEET";
+ case DefaultFontType::CJK_TEXT: return "CJK_TEXT";
+ case DefaultFontType::CTL_DISPLAY: return "CTL_DISPLAY";
+ case DefaultFontType::CTL_HEADING: return "CTL_HEADING";
+ case DefaultFontType::CTL_PRESENTATION: return "CTL_PRESENTATION";
+ case DefaultFontType::CTL_SPREADSHEET: return "CTL_SPREADSHEET";
+ case DefaultFontType::CTL_TEXT: return "CTL_TEXT";
+ case DefaultFontType::FIXED: return "FIXED";
+ case DefaultFontType::LATIN_DISPLAY: return "LATIN_DISPLAY";
+ case DefaultFontType::LATIN_FIXED: return "LATIN_FIXED";
+ case DefaultFontType::LATIN_HEADING: return "LATIN_HEADING";
+ case DefaultFontType::LATIN_PRESENTATION: return "LATIN_PRESENTATION";
+ case DefaultFontType::LATIN_SPREADSHEET: return "LATIN_SPREADSHEET";
+ case DefaultFontType::LATIN_TEXT: return "LATIN_TEXT";
+ case DefaultFontType::SANS: return "SANS";
+ case DefaultFontType::SANS_UNICODE: return "SANS_UNICODE";
+ case DefaultFontType::SERIF: return "SERIF";
+ case DefaultFontType::SYMBOL: return "SYMBOL";
+ case DefaultFontType::UI_FIXED: return "UI_FIXED";
+ case DefaultFontType::UI_SANS: return "UI_SANS";
+ default:
+ OSL_FAIL( "unmatched type" );
+ return "";
+ }
+}
+
+DefaultFontConfiguration& DefaultFontConfiguration::get()
+{
+ static DefaultFontConfiguration theDefaultFontConfiguration;
+ return theDefaultFontConfiguration;
+}
+
+DefaultFontConfiguration::DefaultFontConfiguration()
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return;
+ // create configuration hierarchical access name
+ try
+ {
+ // get service provider
+ m_xConfigProvider = theDefaultProvider::get(comphelper::getProcessComponentContext());
+ Sequence<Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", Any(OUString( "/org.openoffice.VCL/DefaultFonts" ))}
+ }));
+ m_xConfigAccess =
+ Reference< XNameAccess >(
+ m_xConfigProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess",
+ aArgs ),
+ UNO_QUERY );
+ if( m_xConfigAccess.is() )
+ {
+ const Sequence< OUString > aLocales = m_xConfigAccess->getElementNames();
+ // fill config hash with empty interfaces
+ for( const OUString& rLocaleString : aLocales )
+ {
+ // Feed through LanguageTag for casing.
+ OUString aLoc( LanguageTag( rLocaleString, true).getBcp47( false));
+ m_aConfig[ aLoc ] = LocaleAccess();
+ m_aConfig[ aLoc ].aConfigLocaleString = rLocaleString;
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ // configuration is awry
+ m_xConfigProvider.clear();
+ m_xConfigAccess.clear();
+ }
+ SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider.is()
+ << ", config access: " << m_xConfigAccess.is());
+}
+
+DefaultFontConfiguration::~DefaultFontConfiguration()
+{
+ // release all nodes
+ m_aConfig.clear();
+ // release top node
+ m_xConfigAccess.clear();
+ // release config provider
+ m_xConfigProvider.clear();
+}
+
+OUString DefaultFontConfiguration::tryLocale( const OUString& rBcp47, const OUString& rType ) const
+{
+ OUString aRet;
+
+ std::unordered_map< OUString, LocaleAccess >::const_iterator it = m_aConfig.find( rBcp47 );
+ if( it != m_aConfig.end() )
+ {
+ if( !it->second.xAccess.is() )
+ {
+ try
+ {
+ Reference< XNameAccess > xNode;
+ if ( m_xConfigAccess->hasByName( it->second.aConfigLocaleString ) )
+ {
+ Any aAny = m_xConfigAccess->getByName( it->second.aConfigLocaleString );
+ if( aAny >>= xNode )
+ it->second.xAccess = xNode;
+ }
+ }
+ catch (const NoSuchElementException&)
+ {
+ }
+ catch (const WrappedTargetException&)
+ {
+ }
+ }
+ if( it->second.xAccess.is() )
+ {
+ try
+ {
+ if ( it->second.xAccess->hasByName( rType ) )
+ {
+ Any aAny = it->second.xAccess->getByName( rType );
+ aAny >>= aRet;
+ }
+ }
+ catch (const NoSuchElementException&)
+ {
+ }
+ catch (const WrappedTargetException&)
+ {
+ }
+ }
+ }
+
+ return aRet;
+}
+
+OUString DefaultFontConfiguration::getDefaultFont( const LanguageTag& rLanguageTag, DefaultFontType nType ) const
+{
+ OUString aType = OUString::createFromAscii( getKeyType( nType ) );
+ // Try the simple cases first without constructing fallbacks.
+ OUString aRet = tryLocale( rLanguageTag.getBcp47(), aType );
+ if (aRet.isEmpty())
+ {
+ if (rLanguageTag.isIsoLocale())
+ {
+ if (!rLanguageTag.getCountry().isEmpty())
+ {
+ aRet = tryLocale( rLanguageTag.getLanguage(), aType );
+ }
+ }
+ else
+ {
+ ::std::vector< OUString > aFallbacks( rLanguageTag.getFallbackStrings( false));
+ for (const auto& rFallback : aFallbacks)
+ {
+ aRet = tryLocale( rFallback, aType );
+ if (!aRet.isEmpty())
+ break;
+ }
+ }
+ }
+ if( aRet.isEmpty() )
+ {
+ aRet = tryLocale( "en", aType );
+ }
+ return aRet;
+}
+
+OUString DefaultFontConfiguration::getUserInterfaceFont( const LanguageTag& rLanguageTag ) const
+{
+ LanguageTag aLanguageTag( rLanguageTag);
+ if( aLanguageTag.isSystemLocale() )
+ aLanguageTag = SvtSysLocale().GetUILanguageTag();
+
+ OUString aUIFont = getDefaultFont( aLanguageTag, DefaultFontType::UI_SANS );
+
+ if( !aUIFont.isEmpty() )
+ return aUIFont;
+
+ // fallback mechanism (either no configuration or no entry in configuration
+
+ static constexpr OUStringLiteral FALLBACKFONT_UI_SANS = u"Andale Sans UI;Albany;Albany AMT;Tahoma;Arial Unicode MS;Arial;Nimbus Sans L;Bitstream Vera Sans;gnu-unifont;Interface User;Geneva;WarpSans;Dialog;Swiss;Lucida;Helvetica;Charcoal;Chicago;MS Sans Serif;Helv;Times;Times New Roman;Interface System";
+ static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_LATIN2 = u"Andale Sans UI;Albany;Albany AMT;Tahoma;Arial Unicode MS;Arial;Nimbus Sans L;Luxi Sans;Bitstream Vera Sans;Interface User;Geneva;WarpSans;Dialog;Swiss;Lucida;Helvetica;Charcoal;Chicago;MS Sans Serif;Helv;Times;Times New Roman;Interface System";
+ static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_ARABIC = u"Tahoma;Traditional Arabic;Simplified Arabic;Lucidasans;Lucida Sans;Supplement;Andale Sans UI;clearlyU;Interface User;Arial Unicode MS;Lucida Sans Unicode;WarpSans;Geneva;MS Sans Serif;Helv;Dialog;Albany;Lucida;Helvetica;Charcoal;Chicago;Arial;Helmet;Interface System;Sans Serif";
+ static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_THAI = u"OONaksit;Tahoma;Lucidasans;Arial Unicode MS";
+ static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_KOREAN = u"Noto Sans KR;Noto Sans CJK KR;Noto Serif KR;Noto Serif CJK KR;Source Han Sans KR;NanumGothic;NanumBarunGothic;NanumBarunGothic YetHangul;KoPubWorld Dotum;Malgun Gothic;Apple SD Gothic Neo;Dotum;DotumChe;Gulim;GulimChe;Batang;BatangChe;Apple Gothic;UnDotum;Baekmuk Gulim;Arial Unicode MS;Lucida Sans Unicode;gnu-unifont;Andale Sans UI";
+ static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_JAPANESE = u"Noto Sans CJK JP;Noto Sans JP;Source Han Sans;Source Han Sans JP;Yu Gothic UI;Yu Gothic;YuGothic;Hiragino Sans;Hiragino Kaku Gothic ProN;Hiragino Kaku Gothic Pro;Hiragino Kaku Gothic StdN;Meiryo UI;Meiryo;IPAexGothic;IPAPGothic;IPAGothic;MS UI Gothic;MS PGothic;MS Gothic;Osaka;Unifont;gnu-unifont;Arial Unicode MS;Interface System";
+ static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_CHINSIM = u"Andale Sans UI;Arial Unicode MS;ZYSong18030;AR PL SungtiL GB;AR PL KaitiM GB;SimSun;Lucida Sans Unicode;Fangsong;Hei;Song;Kai;Ming;gnu-unifont;Interface User;";
+ static constexpr OUStringLiteral FALLBACKFONT_UI_SANS_CHINTRD = u"Andale Sans UI;Arial Unicode MS;AR PL Mingti2L Big5;AR PL KaitiM Big5;Kai;PMingLiU;MingLiU;Ming;Lucida Sans Unicode;gnu-unifont;Interface User;";
+
+ const OUString aLanguage( aLanguageTag.getLanguage());
+
+ // optimize font list for some locales, as long as Andale Sans UI does not support them
+ if( aLanguage == "ar" || aLanguage == "he" || aLanguage == "iw" )
+ {
+ return FALLBACKFONT_UI_SANS_ARABIC;
+ }
+ else if ( aLanguage == "th" )
+ {
+ return FALLBACKFONT_UI_SANS_THAI;
+ }
+ else if ( aLanguage == "ko" )
+ {
+ return FALLBACKFONT_UI_SANS_KOREAN;
+ }
+ else if ( aLanguage == "ja" )
+ {
+ return FALLBACKFONT_UI_SANS_JAPANESE;
+ }
+ else if( aLanguage == "cs" ||
+ aLanguage == "hu" ||
+ aLanguage == "pl" ||
+ aLanguage == "ro" ||
+ aLanguage == "rm" ||
+ aLanguage == "hr" ||
+ aLanguage == "sk" ||
+ aLanguage == "sl" ||
+ aLanguage == "sb")
+ {
+ return FALLBACKFONT_UI_SANS_LATIN2;
+ }
+ else
+ {
+ const Locale& aLocale( aLanguageTag.getLocale());
+ if (MsLangId::isTraditionalChinese(aLocale))
+ return FALLBACKFONT_UI_SANS_CHINTRD;
+ else if (MsLangId::isSimplifiedChinese(aLocale))
+ return FALLBACKFONT_UI_SANS_CHINSIM;
+ }
+
+ return FALLBACKFONT_UI_SANS;
+}
+
+/*
+ * FontSubstConfigItem::get
+ */
+
+FontSubstConfiguration& FontSubstConfiguration::get()
+{
+ static FontSubstConfiguration theFontSubstConfiguration;
+ return theFontSubstConfiguration;
+}
+
+/*
+ * FontSubstConfigItem::FontSubstConfigItem
+ */
+
+FontSubstConfiguration::FontSubstConfiguration() :
+ maSubstHash( 300 ),
+ maLanguageTag("en")
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return;
+ try
+ {
+ // get service provider
+ Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() );
+ // create configuration hierarchical access name
+ m_xConfigProvider = theDefaultProvider::get( xContext );
+ Sequence<Any> aArgs(comphelper::InitAnyPropertySequence(
+ {
+ {"nodepath", Any(OUString( "/org.openoffice.VCL/FontSubstitutions" ))}
+ }));
+ m_xConfigAccess =
+ Reference< XNameAccess >(
+ m_xConfigProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess",
+ aArgs ),
+ UNO_QUERY );
+ if( m_xConfigAccess.is() )
+ {
+ const Sequence< OUString > aLocales = m_xConfigAccess->getElementNames();
+ // fill config hash with empty interfaces
+ for( const OUString& rLocaleString : aLocales )
+ {
+ // Feed through LanguageTag for casing.
+ OUString aLoc( LanguageTag( rLocaleString, true).getBcp47( false));
+ m_aSubst[ aLoc ] = LocaleSubst();
+ m_aSubst[ aLoc ].aConfigLocaleString = rLocaleString;
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ // configuration is awry
+ m_xConfigProvider.clear();
+ m_xConfigAccess.clear();
+ }
+ SAL_INFO("unotools.config", "config provider: " << m_xConfigProvider.is()
+ << ", config access: " << m_xConfigAccess.is());
+
+ if( maLanguageTag.isSystemLocale() )
+ maLanguageTag = SvtSysLocale().GetUILanguageTag();
+}
+
+/*
+ * FontSubstConfigItem::~FontSubstConfigItem
+ */
+
+FontSubstConfiguration::~FontSubstConfiguration()
+{
+ // release config access
+ m_xConfigAccess.clear();
+ // release config provider
+ m_xConfigProvider.clear();
+}
+
+/*
+ * FontSubstConfigItem::getMapName
+ */
+
+const char* const aImplKillLeadingList[] =
+{
+ "microsoft",
+ "monotype",
+ "linotype",
+ "baekmuk",
+ "adobe",
+ "nimbus",
+ "zycjk",
+ "itc",
+ "sun",
+ "amt",
+ "ms",
+ "mt",
+ "cg",
+ "hg",
+ "fz",
+ "ipa",
+ "sazanami",
+ "kochi",
+ nullptr
+};
+
+const char* const aImplKillTrailingList[] =
+{
+ "microsoft",
+ "monotype",
+ "linotype",
+ "adobe",
+ "nimbus",
+ "itc",
+ "sun",
+ "amt",
+ "ms",
+ "mt",
+ "clm",
+ // Scripts, for compatibility with older versions
+ "we",
+ "cyr",
+ "tur",
+ "wt",
+ "greek",
+ "wl",
+ // CJK extensions
+ "gb",
+ "big5",
+ "pro",
+ "z01",
+ "z02",
+ "z03",
+ "z13",
+ "b01",
+ "w3x12",
+ // Old Printer Fontnames
+ "5cpi",
+ "6cpi",
+ "7cpi",
+ "8cpi",
+ "9cpi",
+ "10cpi",
+ "11cpi",
+ "12cpi",
+ "13cpi",
+ "14cpi",
+ "15cpi",
+ "16cpi",
+ "18cpi",
+ "24cpi",
+ "scale",
+ "pc",
+ nullptr
+};
+
+const char* const aImplKillTrailingWithExceptionsList[] =
+{
+ "ce", "monospace", "oldface", nullptr,
+ "ps", "caps", nullptr,
+ nullptr
+};
+
+namespace {
+
+struct ImplFontAttrWeightSearchData
+{
+ const char* mpStr;
+ FontWeight meWeight;
+};
+
+}
+
+ImplFontAttrWeightSearchData const aImplWeightAttrSearchList[] =
+{
+// the attribute names are ordered by "first match wins"
+// e.g. "semilight" should wins over "semi"
+{ "extrablack", WEIGHT_BLACK },
+{ "ultrablack", WEIGHT_BLACK },
+{ "ultrabold", WEIGHT_ULTRABOLD },
+{ "semibold", WEIGHT_SEMIBOLD },
+{ "semilight", WEIGHT_SEMILIGHT },
+{ "semi", WEIGHT_SEMIBOLD },
+{ "demi", WEIGHT_SEMIBOLD },
+{ "black", WEIGHT_BLACK },
+{ "bold", WEIGHT_BOLD },
+{ "heavy", WEIGHT_BLACK },
+{ "ultralight", WEIGHT_ULTRALIGHT },
+{ "light", WEIGHT_LIGHT },
+{ "medium", WEIGHT_MEDIUM },
+{ nullptr, WEIGHT_DONTKNOW },
+};
+
+namespace {
+
+struct ImplFontAttrWidthSearchData
+{
+ const char* mpStr;
+ FontWidth meWidth;
+};
+
+}
+
+ImplFontAttrWidthSearchData const aImplWidthAttrSearchList[] =
+{
+{ "narrow", WIDTH_CONDENSED },
+{ "semicondensed", WIDTH_SEMI_CONDENSED },
+{ "ultracondensed", WIDTH_ULTRA_CONDENSED },
+{ "semiexpanded", WIDTH_SEMI_EXPANDED },
+{ "ultraexpanded", WIDTH_ULTRA_EXPANDED },
+{ "expanded", WIDTH_EXPANDED },
+{ "wide", WIDTH_ULTRA_EXPANDED },
+{ "condensed", WIDTH_CONDENSED },
+{ "cond", WIDTH_CONDENSED },
+{ "cn", WIDTH_CONDENSED },
+{ nullptr, WIDTH_DONTKNOW },
+};
+
+namespace {
+
+struct ImplFontAttrTypeSearchData
+{
+ const char* mpStr;
+ ImplFontAttrs mnType;
+};
+
+}
+
+ImplFontAttrTypeSearchData const aImplTypeAttrSearchList[] =
+{
+{ "monotype", ImplFontAttrs::None },
+{ "linotype", ImplFontAttrs::None },
+{ "titling", ImplFontAttrs::Titling },
+{ "captitals", ImplFontAttrs::Capitals },
+{ "captital", ImplFontAttrs::Capitals },
+{ "caps", ImplFontAttrs::Capitals },
+{ "italic", ImplFontAttrs::Italic },
+{ "oblique", ImplFontAttrs::Italic },
+{ "rounded", ImplFontAttrs::Rounded },
+{ "outline", ImplFontAttrs::Outline },
+{ "shadow", ImplFontAttrs::Shadow },
+{ "handwriting", ImplFontAttrs::Handwriting | ImplFontAttrs::Script },
+{ "hand", ImplFontAttrs::Handwriting | ImplFontAttrs::Script },
+{ "signet", ImplFontAttrs::Handwriting | ImplFontAttrs::Script },
+{ "script", ImplFontAttrs::BrushScript | ImplFontAttrs::Script },
+{ "calligraphy", ImplFontAttrs::Chancery | ImplFontAttrs::Script },
+{ "chancery", ImplFontAttrs::Chancery | ImplFontAttrs::Script },
+{ "corsiva", ImplFontAttrs::Chancery | ImplFontAttrs::Script },
+{ "gothic", ImplFontAttrs::SansSerif | ImplFontAttrs::Gothic },
+{ "schoolbook", ImplFontAttrs::Serif | ImplFontAttrs::Schoolbook },
+{ "schlbk", ImplFontAttrs::Serif | ImplFontAttrs::Schoolbook },
+{ "typewriter", ImplFontAttrs::Typewriter | ImplFontAttrs::Fixed },
+{ "lineprinter", ImplFontAttrs::Typewriter | ImplFontAttrs::Fixed },
+{ "monospaced", ImplFontAttrs::Fixed },
+{ "monospace", ImplFontAttrs::Fixed },
+{ "mono", ImplFontAttrs::Fixed },
+{ "fixed", ImplFontAttrs::Fixed },
+{ "sansserif", ImplFontAttrs::SansSerif },
+{ "sans", ImplFontAttrs::SansSerif },
+{ "swiss", ImplFontAttrs::SansSerif },
+{ "serif", ImplFontAttrs::Serif },
+{ "bright", ImplFontAttrs::Serif },
+{ "symbols", ImplFontAttrs::Symbol },
+{ "symbol", ImplFontAttrs::Symbol },
+{ "dingbats", ImplFontAttrs::Symbol },
+{ "dings", ImplFontAttrs::Symbol },
+{ "ding", ImplFontAttrs::Symbol },
+{ "bats", ImplFontAttrs::Symbol },
+{ "math", ImplFontAttrs::Symbol },
+{ "oldstyle", ImplFontAttrs::OtherStyle },
+{ "oldface", ImplFontAttrs::OtherStyle },
+{ "old", ImplFontAttrs::OtherStyle },
+{ "new", ImplFontAttrs::None },
+{ "modern", ImplFontAttrs::None },
+{ "lucida", ImplFontAttrs::None },
+{ "regular", ImplFontAttrs::None },
+{ "extended", ImplFontAttrs::None },
+{ "extra", ImplFontAttrs::OtherStyle },
+{ "ext", ImplFontAttrs::None },
+{ "scalable", ImplFontAttrs::None },
+{ "scale", ImplFontAttrs::None },
+{ "nimbus", ImplFontAttrs::None },
+{ "adobe", ImplFontAttrs::None },
+{ "itc", ImplFontAttrs::None },
+{ "amt", ImplFontAttrs::None },
+{ "mt", ImplFontAttrs::None },
+{ "ms", ImplFontAttrs::None },
+{ "cpi", ImplFontAttrs::None },
+{ "no", ImplFontAttrs::None },
+{ nullptr, ImplFontAttrs::None },
+};
+
+static bool ImplKillLeading( OUString& rName, const char* const* ppStr )
+{
+ for(; *ppStr; ++ppStr )
+ {
+ const char* pStr = *ppStr;
+ const sal_Unicode* pNameStr = rName.getStr();
+ while ( (*pNameStr == static_cast<sal_Unicode>(static_cast<unsigned char>(*pStr))) && *pStr )
+ {
+ pNameStr++;
+ pStr++;
+ }
+ if ( !*pStr )
+ {
+ sal_Int32 nLen = static_cast<sal_Int32>(pNameStr - rName.getStr());
+ rName = rName.copy(nLen);
+ return true;
+ }
+ }
+
+ // special case for Baekmuk
+ // TODO: allow non-ASCII KillLeading list
+ const sal_Unicode* pNameStr = rName.getStr();
+ if( (pNameStr[0]==0xBC31) && (pNameStr[1]==0xBC35) )
+ {
+ sal_Int32 nLen = (pNameStr[2]==0x0020) ? 3 : 2;
+ rName = rName.copy(nLen);
+ return true;
+ }
+
+ return false;
+}
+
+static sal_Int32 ImplIsTrailing( std::u16string_view rName, const char* pStr )
+{
+ size_t nStrLen = strlen( pStr );
+ if( nStrLen >= rName.size() )
+ return 0;
+
+ const sal_Unicode* pEndName = rName.data() + rName.size();
+ const sal_Unicode* pNameStr = pEndName - nStrLen;
+ do if( *(pNameStr++) != *(pStr++) )
+ return 0;
+ while( *pStr );
+
+ return nStrLen;
+}
+
+static bool ImplKillTrailing( OUString& rName, const char* const* ppStr )
+{
+ for(; *ppStr; ++ppStr )
+ {
+ sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
+ if( nTrailLen )
+ {
+ rName = rName.copy(0, rName.getLength() - nTrailLen );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool ImplKillTrailingWithExceptions( OUString& rName, const char* const* ppStr )
+{
+ for(; *ppStr; ++ppStr )
+ {
+ sal_Int32 nTrailLen = ImplIsTrailing( rName, *ppStr );
+ if( nTrailLen )
+ {
+ // check string match against string exceptions
+ while( *++ppStr )
+ if( ImplIsTrailing( rName, *ppStr ) )
+ return false;
+
+ rName = rName.copy(0, rName.getLength() - nTrailLen );
+ return true;
+ }
+ else
+ {
+ // skip exception strings
+ while( *++ppStr ) {}
+ }
+ }
+
+ return false;
+}
+
+static bool ImplFindAndErase( OUString& rName, const char* pStr )
+{
+ sal_Int32 nLen = static_cast<sal_Int32>(strlen(pStr));
+ sal_Int32 nPos = rName.indexOfAsciiL(pStr, nLen );
+ if ( nPos < 0 )
+ return false;
+
+ OUStringBuffer sBuff(rName);
+ sBuff.remove(nPos, nLen);
+ rName = sBuff.makeStringAndClear();
+ return true;
+}
+
+void FontSubstConfiguration::getMapName( const OUString& rOrgName, OUString& rShortName,
+ OUString& rFamilyName, FontWeight& rWeight,
+ FontWidth& rWidth, ImplFontAttrs& rType )
+{
+ rShortName = rOrgName;
+
+ // TODO: get rid of the crazy O(N*strlen) searches below
+ // they should be possible in O(strlen)
+
+ // Kill leading vendor names and other unimportant data
+ ImplKillLeading( rShortName, aImplKillLeadingList );
+
+ // Kill trailing vendor names and other unimportant data
+ ImplKillTrailing( rShortName, aImplKillTrailingList );
+ ImplKillTrailingWithExceptions( rShortName, aImplKillTrailingWithExceptionsList );
+
+ rFamilyName = rShortName;
+
+ // Kill attributes from the name and update the data
+ // Weight
+ const ImplFontAttrWeightSearchData* pWeightList = aImplWeightAttrSearchList;
+ while ( pWeightList->mpStr )
+ {
+ if ( ImplFindAndErase( rFamilyName, pWeightList->mpStr ) )
+ {
+ if ( (rWeight == WEIGHT_DONTKNOW) || (rWeight == WEIGHT_NORMAL) )
+ rWeight = pWeightList->meWeight;
+ break;
+ }
+ pWeightList++;
+ }
+
+ // Width
+ const ImplFontAttrWidthSearchData* pWidthList = aImplWidthAttrSearchList;
+ while ( pWidthList->mpStr )
+ {
+ if ( ImplFindAndErase( rFamilyName, pWidthList->mpStr ) )
+ {
+ if ( (rWidth == WIDTH_DONTKNOW) || (rWidth == WIDTH_NORMAL) )
+ rWidth = pWidthList->meWidth;
+ break;
+ }
+ pWidthList++;
+ }
+
+ // Type
+ rType = ImplFontAttrs::None;
+ const ImplFontAttrTypeSearchData* pTypeList = aImplTypeAttrSearchList;
+ while ( pTypeList->mpStr )
+ {
+ if ( ImplFindAndErase( rFamilyName, pTypeList->mpStr ) )
+ rType |= pTypeList->mnType;
+ pTypeList++;
+ }
+
+ // Remove numbers
+ // TODO: also remove localized and fullwidth digits
+ sal_Int32 i = 0;
+ OUStringBuffer sBuff(rFamilyName);
+ while ( i < sBuff.getLength() )
+ {
+ sal_Unicode c = sBuff[ i ];
+ if ( (c >= 0x0030) && (c <= 0x0039) )
+ sBuff.remove(i, 1);
+ else
+ i++;
+ }
+}
+
+namespace {
+
+struct StrictStringSort
+{
+ bool operator()( const FontNameAttr& rLeft, const FontNameAttr& rRight )
+ { return rLeft.Name.compareTo( rRight.Name ) < 0; }
+};
+
+}
+
+// The entries in this table must match the bits in the ImplFontAttrs enum.
+
+const char* const pAttribNames[] =
+{
+ "default",
+ "standard",
+ "normal",
+ "symbol",
+ "fixed",
+ "sansserif",
+ "serif",
+ "decorative",
+ "special",
+ "italic",
+ "title",
+ "capitals",
+ "cjk",
+ "cjk_jp",
+ "cjk_sc",
+ "cjk_tc",
+ "cjk_kr",
+ "ctl",
+ "nonelatin",
+ "full",
+ "outline",
+ "shadow",
+ "rounded",
+ "typewriter",
+ "script",
+ "handwriting",
+ "chancery",
+ "comic",
+ "brushscript",
+ "gothic",
+ "schoolbook",
+ "other"
+};
+
+namespace {
+
+struct enum_convert
+{
+ const char* pName;
+ int nEnum;
+};
+
+}
+
+const enum_convert pWeightNames[] =
+{
+ { "normal", WEIGHT_NORMAL },
+ { "medium", WEIGHT_MEDIUM },
+ { "bold", WEIGHT_BOLD },
+ { "black", WEIGHT_BLACK },
+ { "semibold", WEIGHT_SEMIBOLD },
+ { "light", WEIGHT_LIGHT },
+ { "semilight", WEIGHT_SEMILIGHT },
+ { "ultrabold", WEIGHT_ULTRABOLD },
+ { "semi", WEIGHT_SEMIBOLD },
+ { "demi", WEIGHT_SEMIBOLD },
+ { "heavy", WEIGHT_BLACK },
+ { "unknown", WEIGHT_DONTKNOW },
+ { "thin", WEIGHT_THIN },
+ { "ultralight", WEIGHT_ULTRALIGHT }
+};
+
+const enum_convert pWidthNames[] =
+{
+ { "normal", WIDTH_NORMAL },
+ { "condensed", WIDTH_CONDENSED },
+ { "expanded", WIDTH_EXPANDED },
+ { "unknown", WIDTH_DONTKNOW },
+ { "ultracondensed", WIDTH_ULTRA_CONDENSED },
+ { "extracondensed", WIDTH_EXTRA_CONDENSED },
+ { "semicondensed", WIDTH_SEMI_CONDENSED },
+ { "semiexpanded", WIDTH_SEMI_EXPANDED },
+ { "extraexpanded", WIDTH_EXTRA_EXPANDED },
+ { "ultraexpanded", WIDTH_ULTRA_EXPANDED }
+};
+
+void FontSubstConfiguration::fillSubstVector( const css::uno::Reference< XNameAccess >& rFont,
+ const OUString& rType,
+ std::vector< OUString >& rSubstVector ) const
+{
+ try
+ {
+ Any aAny = rFont->getByName( rType );
+ if( auto pLine = o3tl::tryAccess<OUString>(aAny) )
+ {
+ sal_Int32 nLength = pLine->getLength();
+ if( nLength )
+ {
+ const sal_Unicode* pStr = pLine->getStr();
+ sal_Int32 nTokens = 0;
+ // count tokens
+ while( nLength-- )
+ {
+ if( *pStr++ == ';' )
+ nTokens++;
+ }
+ rSubstVector.clear();
+ // optimize performance, heap fragmentation
+ rSubstVector.reserve( nTokens );
+ sal_Int32 nIndex = 0;
+ while( nIndex != -1 )
+ {
+ OUString aSubst( pLine->getToken( 0, ';', nIndex ) );
+ if( !aSubst.isEmpty() )
+ {
+ auto itPair = maSubstHash.insert( aSubst );
+ if (!itPair.second)
+ aSubst = *itPair.first;
+ rSubstVector.push_back( aSubst );
+ }
+ }
+ }
+ }
+ }
+ catch (const NoSuchElementException&)
+ {
+ }
+ catch (const WrappedTargetException&)
+ {
+ }
+}
+
+FontWeight FontSubstConfiguration::getSubstWeight( const css::uno::Reference< XNameAccess >& rFont,
+ const OUString& rType ) const
+{
+ int weight = -1;
+ try
+ {
+ Any aAny = rFont->getByName( rType );
+ if( auto pLine = o3tl::tryAccess<OUString>(aAny) )
+ {
+ if( !pLine->isEmpty() )
+ {
+ for( weight=std::size(pWeightNames)-1; weight >= 0; weight-- )
+ if( pLine->equalsIgnoreAsciiCaseAscii( pWeightNames[weight].pName ) )
+ break;
+ }
+ SAL_WARN_IF(weight < 0, "unotools.config", "Error: invalid weight " << *pLine);
+ }
+ }
+ catch (const NoSuchElementException&)
+ {
+ }
+ catch (const WrappedTargetException&)
+ {
+ }
+ return static_cast<FontWeight>( weight >= 0 ? pWeightNames[weight].nEnum : WEIGHT_DONTKNOW );
+}
+
+FontWidth FontSubstConfiguration::getSubstWidth( const css::uno::Reference< XNameAccess >& rFont,
+ const OUString& rType ) const
+{
+ int width = -1;
+ try
+ {
+ Any aAny = rFont->getByName( rType );
+ if( auto pLine = o3tl::tryAccess<OUString>(aAny) )
+ {
+ if( !pLine->isEmpty() )
+ {
+ for( width=std::size(pWidthNames)-1; width >= 0; width-- )
+ if( pLine->equalsIgnoreAsciiCaseAscii( pWidthNames[width].pName ) )
+ break;
+ }
+ SAL_WARN_IF( width < 0, "unotools.config", "Error: invalid width " << *pLine);
+ }
+ }
+ catch (const NoSuchElementException&)
+ {
+ }
+ catch (const WrappedTargetException&)
+ {
+ }
+ return static_cast<FontWidth>( width >= 0 ? pWidthNames[width].nEnum : WIDTH_DONTKNOW );
+}
+
+ImplFontAttrs FontSubstConfiguration::getSubstType( const css::uno::Reference< XNameAccess >& rFont,
+ const OUString& rType ) const
+{
+ sal_uInt32 type = 0;
+ try
+ {
+ Any aAny = rFont->getByName( rType );
+ auto pLine = o3tl::tryAccess<OUString>(aAny);
+ if( !pLine )
+ return ImplFontAttrs::None;
+ if( pLine->isEmpty() )
+ return ImplFontAttrs::None;
+ sal_Int32 nIndex = 0;
+ while( nIndex != -1 )
+ {
+ OUString aToken( pLine->getToken( 0, ',', nIndex ) );
+ for( int k = 0; k < 32; k++ )
+ if( aToken.equalsIgnoreAsciiCaseAscii( pAttribNames[k] ) )
+ {
+ type |= sal_uInt32(1) << k;
+ break;
+ }
+ }
+ assert(((type & ~o3tl::typed_flags<ImplFontAttrs>::mask) == 0) && "invalid font attributes");
+ }
+ catch (const NoSuchElementException&)
+ {
+ }
+ catch (const WrappedTargetException&)
+ {
+ }
+
+ return static_cast<ImplFontAttrs>(type);
+}
+
+void FontSubstConfiguration::readLocaleSubst( const OUString& rBcp47 ) const
+{
+ std::unordered_map< OUString, LocaleSubst >::const_iterator it = m_aSubst.find( rBcp47 );
+ if( it == m_aSubst.end() )
+ return;
+
+ if( it->second.bConfigRead )
+ return;
+
+ it->second.bConfigRead = true;
+ Reference< XNameAccess > xNode;
+ try
+ {
+ Any aAny = m_xConfigAccess->getByName( it->second.aConfigLocaleString );
+ aAny >>= xNode;
+ }
+ catch (const NoSuchElementException&)
+ {
+ }
+ catch (const WrappedTargetException&)
+ {
+ }
+ if( !xNode.is() )
+ return;
+
+ const Sequence< OUString > aFonts = xNode->getElementNames();
+ int nFonts = aFonts.getLength();
+ // improve performance, heap fragmentation
+ it->second.aSubstAttributes.reserve( nFonts );
+
+ // strings for subst retrieval, construct only once
+ static constexpr OUStringLiteral aSubstFontsStr ( u"SubstFonts" );
+ static constexpr OUStringLiteral aSubstFontsMSStr( u"SubstFontsMS" );
+ static constexpr OUStringLiteral aSubstWeightStr ( u"FontWeight" );
+ static constexpr OUStringLiteral aSubstWidthStr ( u"FontWidth" );
+ static constexpr OUStringLiteral aSubstTypeStr ( u"FontType" );
+ for( const OUString& rFontName : aFonts )
+ {
+ Reference< XNameAccess > xFont;
+ try
+ {
+ Any aAny = xNode->getByName( rFontName );
+ aAny >>= xFont;
+ }
+ catch (const NoSuchElementException&)
+ {
+ }
+ catch (const WrappedTargetException&)
+ {
+ }
+ if( ! xFont.is() )
+ {
+ SAL_WARN("unotools.config", "did not get font attributes for " << rFontName);
+ continue;
+ }
+
+ FontNameAttr aAttr;
+ // read subst attributes from config
+ aAttr.Name = rFontName;
+ fillSubstVector( xFont, aSubstFontsStr, aAttr.Substitutions );
+ fillSubstVector( xFont, aSubstFontsMSStr, aAttr.MSSubstitutions );
+ aAttr.Weight = getSubstWeight( xFont, aSubstWeightStr );
+ aAttr.Width = getSubstWidth( xFont, aSubstWidthStr );
+ aAttr.Type = getSubstType( xFont, aSubstTypeStr );
+
+ // finally insert this entry
+ it->second.aSubstAttributes.push_back( aAttr );
+ }
+ std::sort( it->second.aSubstAttributes.begin(), it->second.aSubstAttributes.end(), StrictStringSort() );
+}
+
+const FontNameAttr* FontSubstConfiguration::getSubstInfo( const OUString& rFontName ) const
+{
+ if( rFontName.isEmpty() )
+ return nullptr;
+
+ // search if a (language dep.) replacement table for the given font exists
+ // fallback is english
+ OUString aSearchFont( rFontName.toAsciiLowerCase() );
+ FontNameAttr aSearchAttr;
+ aSearchAttr.Name = aSearchFont;
+
+ ::std::vector< OUString > aFallbacks( maLanguageTag.getFallbackStrings( true));
+ if (maLanguageTag.getLanguage() != "en")
+ aFallbacks.emplace_back("en");
+
+ for (const auto& rFallback : aFallbacks)
+ {
+ std::unordered_map< OUString, LocaleSubst >::const_iterator lang = m_aSubst.find( rFallback );
+ if( lang != m_aSubst.end() )
+ {
+ if( ! lang->second.bConfigRead )
+ readLocaleSubst( rFallback );
+ // try to find an exact match
+ // because the list is sorted this will also find fontnames of the form searchfontname*
+ std::vector< FontNameAttr >::const_iterator it = ::std::lower_bound( lang->second.aSubstAttributes.begin(), lang->second.aSubstAttributes.end(), aSearchAttr, StrictStringSort() );
+ if( it != lang->second.aSubstAttributes.end())
+ {
+ const FontNameAttr& rFoundAttr = *it;
+ // a search for "abcblack" may match with an entry for "abc"
+ // the reverse is not a good idea (e.g. #i112731# alba->albani)
+ if( rFoundAttr.Name.getLength() <= aSearchFont.getLength() )
+ if( aSearchFont.startsWith( rFoundAttr.Name))
+ return &rFoundAttr;
+ }
+ }
+ }
+ return nullptr;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/historyoptions.cxx b/unotools/source/config/historyoptions.cxx
new file mode 100644
index 0000000000..3d8d8f933b
--- /dev/null
+++ b/unotools/source/config/historyoptions.cxx
@@ -0,0 +1,610 @@
+/* -*- 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/historyoptions.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+#include <comphelper/configurationhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <optional>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+
+namespace {
+ constexpr OUString s_sItemList = u"ItemList"_ustr;
+ constexpr OUString s_sOrderList = u"OrderList"_ustr;
+ constexpr OUString s_sHistoryItemRef = u"HistoryItemRef"_ustr;
+ constexpr OUString s_sFilter = u"Filter"_ustr;
+ constexpr OUString s_sTitle = u"Title"_ustr;
+ constexpr OUString s_sThumbnail = u"Thumbnail"_ustr;
+ constexpr OUString s_sReadOnly = u"ReadOnly"_ustr;
+ constexpr OUString s_sPinned = u"Pinned"_ustr;
+}
+
+static uno::Reference<container::XNameAccess> GetConfig();
+static uno::Reference<container::XNameAccess> GetCommonXCU();
+static uno::Reference<container::XNameAccess> GetListAccess(
+ uno::Reference<container::XNameAccess> const & xCfg,
+ EHistoryType eHistory);
+static void TruncateList(
+ const uno::Reference<container::XNameAccess>& xCfg,
+ const uno::Reference<container::XNameAccess>& xList,
+ sal_uInt32 nSize);
+
+static void PrependItem(const uno::Reference<container::XNameAccess>& xCfg,
+ uno::Reference<container::XNameContainer>& xList, std::u16string_view sURL);
+static void MoveItemToUnpinned(const uno::Reference<container::XNameAccess>& xCfg,
+ uno::Reference<container::XNameContainer>& xOrderList,
+ uno::Reference<container::XNameContainer>& xItemList,
+ std::u16string_view sURL);
+
+static sal_uInt32 GetCapacity(const uno::Reference<container::XNameAccess>& xCommonXCU, EHistoryType eHistory);
+
+namespace SvtHistoryOptions
+{
+
+void Clear(EHistoryType eHistory, const bool bClearPinnedItems)
+{
+ try
+ {
+ uno::Reference<container::XNameAccess> xCfg = GetConfig();
+ uno::Reference<container::XNameAccess> xListAccess(GetListAccess(xCfg, eHistory));
+
+ // Retrieve order and item lists using name access to check properties of individual items
+ uno::Reference<container::XNameAccess> xItemList;
+ uno::Reference<container::XNameAccess> xOrderList;
+ xListAccess->getByName(s_sItemList) >>= xItemList;
+ xListAccess->getByName(s_sOrderList) >>= xOrderList;
+
+ // Retrieve order and item lists using name container to delete individual items
+ uno::Reference<container::XNameContainer> xOrderNode;
+ uno::Reference<container::XNameContainer> xItemNode;
+ xListAccess->getByName(s_sItemList) >>= xItemNode;
+ xListAccess->getByName(s_sOrderList) >>= xOrderNode;
+
+ const sal_Int32 nLength = xOrderList->getElementNames().getLength();
+ for (sal_Int32 nItem = 0; nItem < nLength; ++nItem)
+ {
+ // Retrieve single item from the order list
+ OUString sUrl;
+ uno::Reference<beans::XPropertySet> xSet;
+ xOrderList->getByName(OUString::number(nItem)) >>= xSet;
+ xSet->getPropertyValue(s_sHistoryItemRef) >>= sUrl;
+
+ // tdf#155698 - check if pinned items should be cleared
+ bool bIsItemPinned = false;
+ if (!bClearPinnedItems)
+ {
+ xItemList->getByName(sUrl) >>= xSet;
+ if (xSet->getPropertySetInfo()->hasPropertyByName(s_sPinned))
+ xSet->getPropertyValue(s_sPinned) >>= bIsItemPinned;
+ }
+
+ if (!bIsItemPinned)
+ {
+ xItemNode->removeByName(sUrl);
+ xOrderNode->removeByName(OUString::number(nItem));
+ }
+ }
+
+ ::comphelper::ConfigurationHelper::flush(xCfg);
+ }
+ catch(const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools.config");
+ }
+}
+
+std::vector< HistoryItem > GetList( EHistoryType eHistory )
+{
+ std::vector< HistoryItem > aRet;
+ try
+ {
+ uno::Reference<container::XNameAccess> xCfg = GetConfig();
+ uno::Reference<container::XNameAccess> xCommonXCU = GetCommonXCU();
+ uno::Reference<container::XNameAccess> xListAccess(GetListAccess(xCfg, eHistory));
+
+ TruncateList(xCfg, xListAccess, GetCapacity(xCommonXCU, eHistory));
+
+ uno::Reference<container::XNameAccess> xItemList;
+ uno::Reference<container::XNameAccess> xOrderList;
+ xListAccess->getByName(s_sItemList) >>= xItemList;
+ xListAccess->getByName(s_sOrderList) >>= xOrderList;
+
+ const sal_Int32 nLength = xOrderList->getElementNames().getLength();
+ aRet.reserve(nLength);
+
+ for (sal_Int32 nItem = 0; nItem < nLength; ++nItem)
+ {
+ try
+ {
+ OUString sUrl;
+ uno::Reference<beans::XPropertySet> xSet;
+ xOrderList->getByName(OUString::number(nItem)) >>= xSet;
+ xSet->getPropertyValue(s_sHistoryItemRef) >>= sUrl;
+
+ xItemList->getByName(sUrl) >>= xSet;
+ HistoryItem aItem;
+ aItem.sURL = sUrl;
+ xSet->getPropertyValue(s_sFilter) >>= aItem.sFilter;
+ xSet->getPropertyValue(s_sTitle) >>= aItem.sTitle;
+ xSet->getPropertyValue(s_sThumbnail) >>= aItem.sThumbnail;
+ xSet->getPropertyValue(s_sReadOnly) >>= aItem.isReadOnly;
+ if (xSet->getPropertySetInfo()->hasPropertyByName(s_sPinned))
+ xSet->getPropertyValue(s_sPinned) >>= aItem.isPinned;
+
+ aRet.push_back(aItem);
+ }
+ catch(const uno::Exception&)
+ {
+ // <https://bugs.libreoffice.org/show_bug.cgi?id=46074>
+ // "FILEOPEN: No Recent Documents..." discusses a problem
+ // with corrupted /org.openoffice.Office/Histories/Histories
+ // configuration items; to work around that problem, simply
+ // ignore such corrupted individual items here, so that at
+ // least newly added items are successfully reported back
+ // from this function:
+ DBG_UNHANDLED_EXCEPTION("unotools.config");
+ }
+ }
+ }
+ catch(const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools.config");
+ }
+ return aRet;
+}
+
+void AppendItem(EHistoryType eHistory, const OUString& sURL, const OUString& sFilter,
+ const OUString& sTitle, const std::optional<OUString>& sThumbnail,
+ ::std::optional<bool> const oIsReadOnly)
+{
+ try
+ {
+ uno::Reference<container::XNameAccess> xCfg = GetConfig();
+ uno::Reference<container::XNameAccess> xCommonXCU = GetCommonXCU();
+ uno::Reference<container::XNameAccess> xListAccess(GetListAccess(xCfg, eHistory));
+
+ TruncateList(xCfg, xListAccess, GetCapacity(xCommonXCU, eHistory));
+
+ sal_Int32 nMaxSize = GetCapacity(xCommonXCU, eHistory);
+ if (nMaxSize == 0)
+ return;
+
+ uno::Reference<container::XNameContainer> xItemList;
+ uno::Reference<container::XNameContainer> xOrderList;
+ xListAccess->getByName(s_sItemList) >>= xItemList;
+ xListAccess->getByName(s_sOrderList) >>= xOrderList;
+ sal_Int32 nLength = xOrderList->getElementNames().getLength();
+
+ // The item to be appended already exists
+ if (xItemList->hasByName(sURL))
+ {
+ uno::Reference<beans::XPropertySet> xSet;
+ xItemList->getByName(sURL) >>= xSet;
+ if (sThumbnail)
+ {
+ // update the thumbnail
+ xSet->setPropertyValue(s_sThumbnail, uno::Any(*sThumbnail));
+ }
+ if (oIsReadOnly)
+ {
+ xSet->setPropertyValue(s_sReadOnly, uno::Any(*oIsReadOnly));
+ }
+
+ // tdf#38742 - check the current pinned state of the item and move it accordingly
+ bool bIsItemPinned = false;
+ if (xSet->getPropertySetInfo()->hasPropertyByName(s_sPinned))
+ xSet->getPropertyValue(s_sPinned) >>= bIsItemPinned;
+ if (bIsItemPinned)
+ PrependItem(xCfg, xOrderList, sURL);
+ else
+ MoveItemToUnpinned(xCfg, xOrderList, xItemList, sURL);
+ }
+ else // The item to be appended does not exist yet
+ {
+ uno::Reference<beans::XPropertySet> xSet;
+ uno::Reference<lang::XSingleServiceFactory> xFac;
+ uno::Reference<uno::XInterface> xInst;
+ uno::Reference<beans::XPropertySet> xPrevSet;
+ uno::Reference<beans::XPropertySet> xNextSet;
+
+ // Append new item to OrderList.
+ if ( nLength == nMaxSize )
+ {
+ OUString sRemove;
+ xOrderList->getByName(OUString::number(nLength-1)) >>= xSet;
+ xSet->getPropertyValue(s_sHistoryItemRef) >>= sRemove;
+ try
+ {
+ xItemList->removeByName(sRemove);
+ }
+ catch (container::NoSuchElementException &)
+ {
+ // <https://bugs.libreoffice.org/show_bug.cgi?id=46074>
+ // "FILEOPEN: No Recent Documents..." discusses a problem
+ // with corrupted /org.openoffice.Office/Histories/Histories
+ // configuration items; to work around that problem, simply
+ // ignore such corrupted individual items here, so that at
+ // least newly added items are successfully added:
+ if (!sRemove.isEmpty())
+ {
+ throw;
+ }
+ }
+ }
+ if (nLength != nMaxSize)
+ {
+ xFac.set(xOrderList, uno::UNO_QUERY);
+ xInst = xFac->createInstance();
+ OUString sPush = OUString::number(nLength++);
+ xOrderList->insertByName(sPush, uno::Any(xInst));
+ }
+ for (sal_Int32 j=nLength-1; j>0; --j)
+ {
+ xOrderList->getByName( OUString::number(j) ) >>= xPrevSet;
+ xOrderList->getByName( OUString::number(j-1) ) >>= xNextSet;
+ OUString sTemp;
+ xNextSet->getPropertyValue(s_sHistoryItemRef) >>= sTemp;
+ xPrevSet->setPropertyValue(s_sHistoryItemRef, uno::Any(sTemp));
+ }
+ xOrderList->getByName( OUString::number(0) ) >>= xSet;
+ xSet->setPropertyValue(s_sHistoryItemRef, uno::Any(sURL));
+
+ // Append the item to ItemList.
+ xFac.set(xItemList, uno::UNO_QUERY);
+ xInst = xFac->createInstance();
+ xItemList->insertByName(sURL, uno::Any(xInst));
+
+ xSet.set(xInst, uno::UNO_QUERY);
+ xSet->setPropertyValue(s_sFilter, uno::Any(sFilter));
+ xSet->setPropertyValue(s_sTitle, uno::Any(sTitle));
+ xSet->setPropertyValue(s_sThumbnail, uno::Any(sThumbnail.value_or(OUString())));
+ if (oIsReadOnly)
+ {
+ xSet->setPropertyValue(s_sReadOnly, uno::Any(*oIsReadOnly));
+ }
+
+ // tdf#38742 - check the current pinned state of the item and move it accordingly
+ bool bIsItemPinned = false;
+ if (xSet->getPropertySetInfo()->hasPropertyByName(s_sPinned))
+ xSet->getPropertyValue(s_sPinned) >>= bIsItemPinned;
+ if (bIsItemPinned)
+ PrependItem(xCfg, xOrderList, sURL);
+ else
+ MoveItemToUnpinned(xCfg, xOrderList, xItemList, sURL);
+
+ ::comphelper::ConfigurationHelper::flush(xCfg);
+ }
+ }
+ catch(const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools.config");
+ }
+}
+
+void DeleteItem(EHistoryType eHistory, const OUString& sURL, const bool bClearPinned)
+{
+ try
+ {
+ uno::Reference<container::XNameAccess> xCfg = GetConfig();
+ uno::Reference<container::XNameAccess> xListAccess(GetListAccess(xCfg, eHistory));
+
+ uno::Reference<container::XNameContainer> xItemList;
+ uno::Reference<container::XNameContainer> xOrderList;
+ xListAccess->getByName(s_sItemList) >>= xItemList;
+ xListAccess->getByName(s_sOrderList) >>= xOrderList;
+ sal_Int32 nLength = xOrderList->getElementNames().getLength();
+
+ // if it does not exist, nothing to do
+ if (!xItemList->hasByName(sURL))
+ return;
+
+ // it's the last one and pinned items can be cleared, just clear the lists
+ if (nLength == 1 && bClearPinned)
+ {
+ Clear(eHistory);
+ return;
+ }
+
+ // find it in the OrderList
+ sal_Int32 nFromWhere = 0;
+ bool bIsItemPinned = false;
+ for (; nFromWhere < nLength - 1; ++nFromWhere)
+ {
+ uno::Reference<beans::XPropertySet> xSet;
+ OUString aItem;
+ xOrderList->getByName(OUString::number(nFromWhere)) >>= xSet;
+ xSet->getPropertyValue(s_sHistoryItemRef) >>= aItem;
+ // tdf#155698 - check if pinned item should be deleted
+ if (!bClearPinned && xSet->getPropertySetInfo()->hasPropertyByName(s_sPinned))
+ xSet->getPropertyValue(s_sPinned) >>= bIsItemPinned;
+
+ if (aItem == sURL)
+ break;
+ }
+
+ // tdf#155698 - check if pinned item should be deleted
+ if (!bIsItemPinned)
+ {
+ // and shift the rest of the items in OrderList accordingly
+ for (sal_Int32 i = nFromWhere; i < nLength - 1; ++i)
+ {
+ uno::Reference<beans::XPropertySet> xPrevSet;
+ uno::Reference<beans::XPropertySet> xNextSet;
+ xOrderList->getByName(OUString::number(i)) >>= xPrevSet;
+ xOrderList->getByName(OUString::number(i + 1)) >>= xNextSet;
+
+ OUString sTemp;
+ xNextSet->getPropertyValue(s_sHistoryItemRef) >>= sTemp;
+ xPrevSet->setPropertyValue(s_sHistoryItemRef, uno::Any(sTemp));
+ }
+ xOrderList->removeByName(OUString::number(nLength - 1));
+
+ // and finally remove it from the ItemList
+ xItemList->removeByName(sURL);
+
+ ::comphelper::ConfigurationHelper::flush(xCfg);
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools.config");
+ }
+}
+
+void TogglePinItem(EHistoryType eHistory, const OUString& sURL)
+{
+ try
+ {
+ uno::Reference<container::XNameAccess> xCfg = GetConfig();
+ uno::Reference<container::XNameAccess> xListAccess(GetListAccess(xCfg, eHistory));
+
+ uno::Reference<container::XNameContainer> xItemList;
+ xListAccess->getByName(s_sItemList) >>= xItemList;
+
+ // Check if item exists
+ if (xItemList->hasByName(sURL))
+ {
+ // Toggle pinned option
+ uno::Reference<beans::XPropertySet> xSet;
+ xItemList->getByName(sURL) >>= xSet;
+ bool bIsItemPinned = false;
+ if (xSet->getPropertySetInfo()->hasPropertyByName(s_sPinned))
+ xSet->getPropertyValue(s_sPinned) >>= bIsItemPinned;
+ xSet->setPropertyValue(s_sPinned, uno::Any(!bIsItemPinned));
+
+ uno::Reference<container::XNameContainer> xOrderList;
+ xListAccess->getByName(s_sOrderList) >>= xOrderList;
+
+ // Shift item to the beginning of the document list if is not pinned now
+ if (bIsItemPinned)
+ MoveItemToUnpinned(xCfg, xOrderList, xItemList, sURL);
+ else
+ PrependItem(xCfg, xOrderList, sURL);
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools.config");
+ }
+}
+
+} // namespace
+
+
+static uno::Reference<container::XNameAccess> GetConfig()
+{
+ return uno::Reference<container::XNameAccess>(
+ ::comphelper::ConfigurationHelper::openConfig(
+ ::comphelper::getProcessComponentContext(),
+ "org.openoffice.Office.Histories/Histories",
+ ::comphelper::EConfigurationModes::Standard),
+ uno::UNO_QUERY_THROW);
+}
+
+static uno::Reference<container::XNameAccess> GetCommonXCU()
+{
+ return uno::Reference<container::XNameAccess>(
+ ::comphelper::ConfigurationHelper::openConfig(
+ ::comphelper::getProcessComponentContext(),
+ "org.openoffice.Office.Common/History",
+ ::comphelper::EConfigurationModes::Standard),
+ uno::UNO_QUERY_THROW);
+}
+
+static uno::Reference<container::XNameAccess> GetListAccess(
+ const uno::Reference<container::XNameAccess>& xCfg,
+ EHistoryType eHistory)
+{
+ uno::Reference<container::XNameAccess> xListAccess;
+ switch (eHistory)
+ {
+ case EHistoryType::PickList:
+ xCfg->getByName("PickList") >>= xListAccess;
+ break;
+
+ case EHistoryType::HelpBookmarks:
+ xCfg->getByName("HelpBookmarks") >>= xListAccess;
+ break;
+ }
+ return xListAccess;
+}
+
+static void TruncateList(
+ const uno::Reference<container::XNameAccess>& xCfg,
+ const uno::Reference<container::XNameAccess>& xList,
+ sal_uInt32 nSize)
+{
+ uno::Reference<container::XNameContainer> xItemList;
+ uno::Reference<container::XNameContainer> xOrderList;
+ xList->getByName(s_sOrderList) >>= xOrderList;
+ xList->getByName(s_sItemList) >>= xItemList;
+
+ const sal_uInt32 nLength = xOrderList->getElementNames().getLength();
+ if (nSize >= nLength)
+ return;
+
+ for (sal_uInt32 i=nLength-1; i>=nSize; --i)
+ {
+ uno::Reference<beans::XPropertySet> xSet;
+ OUString sTmp;
+ const OUString sRemove = OUString::number(i);
+ xOrderList->getByName(sRemove) >>= xSet;
+ xSet->getPropertyValue(s_sHistoryItemRef) >>= sTmp;
+ xItemList->removeByName(sTmp);
+ xOrderList->removeByName(sRemove);
+ }
+
+ ::comphelper::ConfigurationHelper::flush(xCfg);
+}
+
+static void PrependItem(const uno::Reference<container::XNameAccess>& xCfg,
+ uno::Reference<container::XNameContainer>& xList, std::u16string_view sURL)
+{
+ uno::Reference<beans::XPropertySet> xSet;
+ const sal_Int32 nLength = xList->getElementNames().getLength();
+ for (sal_Int32 i = 0; i < nLength; i++)
+ {
+ OUString aItem;
+ xList->getByName(OUString::number(i)) >>= xSet;
+ xSet->getPropertyValue(s_sHistoryItemRef) >>= aItem;
+
+ if (aItem == sURL)
+ {
+ for (sal_Int32 j = i - 1; j >= 0; --j)
+ {
+ uno::Reference<beans::XPropertySet> xPrevSet;
+ uno::Reference<beans::XPropertySet> xNextSet;
+ xList->getByName(OUString::number(j + 1)) >>= xPrevSet;
+ xList->getByName(OUString::number(j)) >>= xNextSet;
+
+ OUString sTemp;
+ xNextSet->getPropertyValue(s_sHistoryItemRef) >>= sTemp;
+ xPrevSet->setPropertyValue(s_sHistoryItemRef, uno::Any(sTemp));
+ }
+ xList->getByName(OUString::number(0)) >>= xSet;
+ xSet->setPropertyValue(s_sHistoryItemRef, uno::Any(aItem));
+ ::comphelper::ConfigurationHelper::flush(xCfg);
+ return;
+ }
+ }
+}
+
+static void MoveItemToUnpinned(const uno::Reference<container::XNameAccess>& xCfg,
+ uno::Reference<container::XNameContainer>& xOrderList,
+ uno::Reference<container::XNameContainer>& xItemList,
+ std::u16string_view sURL)
+{
+ uno::Reference<beans::XPropertySet> xSet;
+ const sal_Int32 nLength = xOrderList->getElementNames().getLength();
+ // Search for item in the ordered list list
+ for (sal_Int32 i = 0; i < nLength; i++)
+ {
+ OUString aItem;
+ xOrderList->getByName(OUString::number(i)) >>= xSet;
+ xSet->getPropertyValue(s_sHistoryItemRef) >>= aItem;
+
+ if (aItem == sURL)
+ {
+ // Move item to the unpinned document section to the right if it was previously pinned
+ for (sal_Int32 j = i + 1; j < nLength; j++)
+ {
+ uno::Reference<beans::XPropertySet> xNextSet;
+ xOrderList->getByName(OUString::number(j)) >>= xNextSet;
+
+ OUString aNextItem;
+ xNextSet->getPropertyValue(s_sHistoryItemRef) >>= aNextItem;
+
+ uno::Reference<beans::XPropertySet> xNextItemSet;
+ xItemList->getByName(aNextItem) >>= xNextItemSet;
+ bool bIsItemPinned = false;
+ if (xNextItemSet->getPropertySetInfo()->hasPropertyByName(s_sPinned))
+ xNextItemSet->getPropertyValue(s_sPinned) >>= bIsItemPinned;
+ if (bIsItemPinned)
+ {
+ xOrderList->getByName(OUString::number(j - 1)) >>= xSet;
+ xSet->getPropertyValue(s_sHistoryItemRef) >>= aItem;
+ xSet->setPropertyValue(s_sHistoryItemRef, uno::Any(aNextItem));
+ xNextSet->setPropertyValue(s_sHistoryItemRef, uno::Any(aItem));
+ }
+ else
+ break;
+ }
+
+ // Move item to the unpinned document section to the left if it was previously unpinned
+ for (sal_Int32 j = i - 1; j >= 0; --j)
+ {
+ uno::Reference<beans::XPropertySet> xPrevSet;
+ xOrderList->getByName(OUString::number(j)) >>= xPrevSet;
+
+ OUString aPrevItem;
+ xPrevSet->getPropertyValue(s_sHistoryItemRef) >>= aPrevItem;
+
+ uno::Reference<beans::XPropertySet> xPrevItemSet;
+ xItemList->getByName(aPrevItem) >>= xPrevItemSet;
+ bool bIsItemPinned = false;
+ if (xPrevItemSet->getPropertySetInfo()->hasPropertyByName(s_sPinned))
+ xPrevItemSet->getPropertyValue(s_sPinned) >>= bIsItemPinned;
+ if (!bIsItemPinned)
+ {
+ xOrderList->getByName(OUString::number(j + 1)) >>= xSet;
+ xSet->getPropertyValue(s_sHistoryItemRef) >>= aItem;
+ xSet->setPropertyValue(s_sHistoryItemRef, uno::Any(aPrevItem));
+ xPrevSet->setPropertyValue(s_sHistoryItemRef, uno::Any(aItem));
+ }
+ else
+ break;
+ }
+
+ ::comphelper::ConfigurationHelper::flush(xCfg);
+ return;
+ }
+ }
+}
+
+static sal_uInt32 GetCapacity(const uno::Reference<container::XNameAccess>& xCommonXCU, EHistoryType eHistory)
+{
+ uno::Reference<beans::XPropertySet> xListAccess(xCommonXCU, uno::UNO_QUERY_THROW);
+
+ sal_uInt32 nSize = 0;
+
+ switch (eHistory)
+ {
+ case EHistoryType::PickList:
+ xListAccess->getPropertyValue("PickListSize") >>= nSize;
+ break;
+
+ case EHistoryType::HelpBookmarks:
+ xListAccess->getPropertyValue("HelpBookmarkSize") >>= nSize;
+ break;
+ }
+
+ return nSize;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/itemholder1.cxx b/unotools/source/config/itemholder1.cxx
new file mode 100644
index 0000000000..a8fa0dbd5c
--- /dev/null
+++ b/unotools/source/config/itemholder1.cxx
@@ -0,0 +1,150 @@
+/* -*- 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 "itemholder1.hxx"
+
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+
+#include <unotools/useroptions.hxx>
+#include <unotools/cmdoptions.hxx>
+#include <unotools/compatibility.hxx>
+#include <unotools/lingucfg.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <unotools/pathoptions.hxx>
+#include <unotools/options.hxx>
+#include <unotools/syslocaleoptions.hxx>
+#include <rtl/ref.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+
+ItemHolder1::ItemHolder1()
+{
+ try
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ css::uno::Reference< css::lang::XComponent > xCfg(
+ css::configuration::theDefaultProvider::get( xContext ),
+ css::uno::UNO_QUERY_THROW );
+ xCfg->addEventListener(static_cast< css::lang::XEventListener* >(this));
+ }
+#ifdef DBG_UTIL
+ catch(const css::uno::Exception&)
+ {
+ static bool bMessage = true;
+ if(bMessage)
+ {
+ bMessage = false;
+ TOOLS_WARN_EXCEPTION( "unotools", "CreateInstance with arguments");
+ }
+ }
+#else
+ catch(css::uno::Exception&){}
+#endif
+}
+
+ItemHolder1::~ItemHolder1()
+{
+ impl_releaseAllItems();
+}
+
+void ItemHolder1::holdConfigItem(EItem eItem)
+{
+ static rtl::Reference<ItemHolder1> pHolder = new ItemHolder1();
+ pHolder->impl_addItem(eItem);
+}
+
+void SAL_CALL ItemHolder1::disposing(const css::lang::EventObject&)
+{
+ css::uno::Reference< css::uno::XInterface > xSelfHold(static_cast< css::lang::XEventListener* >(this), css::uno::UNO_QUERY);
+ impl_releaseAllItems();
+}
+
+void ItemHolder1::impl_addItem(EItem eItem)
+{
+ std::scoped_lock aLock(m_aLock);
+
+ for ( auto const & rInfo : m_lItems )
+ {
+ if (rInfo.eItem == eItem)
+ return;
+ }
+
+ TItemInfo aNewItem;
+ aNewItem.eItem = eItem;
+ impl_newItem(aNewItem);
+ if (aNewItem.pItem)
+ m_lItems.emplace_back(std::move(aNewItem));
+}
+
+void ItemHolder1::impl_releaseAllItems()
+{
+ std::vector< TItemInfo > items;
+ {
+ std::scoped_lock aLock(m_aLock);
+ items.swap(m_lItems);
+ }
+
+ // items will be freed when the block exits
+}
+
+void ItemHolder1::impl_newItem(TItemInfo& rItem)
+{
+ switch(rItem.eItem)
+ {
+ case EItem::CmdOptions :
+ rItem.pItem.reset( new SvtCommandOptions() );
+ break;
+
+ case EItem::Compatibility :
+ rItem.pItem.reset( new SvtCompatibilityOptions() );
+ break;
+
+ case EItem::EventConfig :
+ //rItem.pItem.reset( new GlobalEventConfig() );
+ break;
+
+ case EItem::LinguConfig :
+ rItem.pItem.reset( new SvtLinguConfig() );
+ break;
+
+ case EItem::ModuleOptions :
+ rItem.pItem.reset( new SvtModuleOptions() );
+ break;
+
+ case EItem::PathOptions :
+ rItem.pItem.reset( new SvtPathOptions() );
+ break;
+
+ case EItem::UserOptions :
+ rItem.pItem.reset( new SvtUserOptions() );
+ break;
+
+ case EItem::SysLocaleOptions :
+ rItem.pItem.reset( new SvtSysLocaleOptions() );
+ break;
+
+ default:
+ OSL_FAIL( "unknown item type" );
+ break;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/itemholder1.hxx b/unotools/source/config/itemholder1.hxx
new file mode 100644
index 0000000000..b8a6504fd3
--- /dev/null
+++ b/unotools/source/config/itemholder1.hxx
@@ -0,0 +1,56 @@
+/* -*- 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 .
+ */
+
+#pragma once
+
+#include <unotools/itemholderbase.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/lang/XEventListener.hpp>
+#include <mutex>
+
+class ItemHolder1 : public ::cppu::WeakImplHelper< css::lang::XEventListener >
+{
+
+ // member
+ private:
+
+ std::mutex m_aLock;
+ std::vector<TItemInfo> m_lItems;
+
+ // c++ interface
+ public:
+
+ ItemHolder1();
+ virtual ~ItemHolder1() override;
+ static void holdConfigItem(EItem eItem);
+
+ // uno interface
+ public:
+
+ virtual void SAL_CALL disposing(const css::lang::EventObject& aEvent) override;
+
+ // helper
+ private:
+
+ void impl_addItem(EItem eItem);
+ void impl_releaseAllItems();
+ static void impl_newItem(TItemInfo& rItem);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/lingucfg.cxx b/unotools/source/config/lingucfg.cxx
new file mode 100644
index 0000000000..730cab027f
--- /dev/null
+++ b/unotools/source/config/lingucfg.cxx
@@ -0,0 +1,1207 @@
+/* -*- 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 <com/sun/star/lang/Locale.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <sal/log.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <tools/debug.hxx>
+#include <unotools/configitem.hxx>
+#include <unotools/lingucfg.hxx>
+#include <unotools/linguprops.hxx>
+#include <comphelper/getexpandeduri.hxx>
+#include <comphelper/processfactory.hxx>
+#include <o3tl/string_view.hxx>
+#include <mutex>
+
+#include "itemholder1.hxx"
+
+using namespace com::sun::star;
+
+constexpr OUStringLiteral FILE_PROTOCOL = u"file:///";
+
+namespace
+{
+ std::mutex& theSvtLinguConfigItemMutex()
+ {
+ static std::mutex SINGLETON;
+ return SINGLETON;
+ }
+}
+
+static bool lcl_SetLocale( LanguageType &rLanguage, const uno::Any &rVal )
+{
+ bool bSucc = false;
+
+ lang::Locale aNew;
+ if (rVal >>= aNew) // conversion successful?
+ {
+ LanguageType nNew = LanguageTag::convertToLanguageType( aNew, false);
+ if (nNew != rLanguage)
+ {
+ rLanguage = nNew;
+ bSucc = true;
+ }
+ }
+ return bSucc;
+}
+
+static OUString lcl_LanguageToCfgLocaleStr( LanguageType nLanguage )
+{
+ OUString aRes;
+ if (LANGUAGE_SYSTEM != nLanguage)
+ aRes = LanguageTag::convertToBcp47( nLanguage );
+ return aRes;
+}
+
+static LanguageType lcl_CfgAnyToLanguage( const uno::Any &rVal )
+{
+ OUString aTmp;
+ rVal >>= aTmp;
+ return (aTmp.isEmpty()) ? LANGUAGE_SYSTEM : LanguageTag::convertToLanguageTypeWithFallback( aTmp );
+}
+
+SvtLinguOptions::SvtLinguOptions()
+ : bROActiveDics(false)
+ , bROActiveConvDics(false)
+ , nHyphMinLeading(2)
+ , nHyphMinTrailing(2)
+ , nHyphMinWordLength(0)
+ , bROHyphMinLeading(false)
+ , bROHyphMinTrailing(false)
+ , bROHyphMinWordLength(false)
+ , nDefaultLanguage(LANGUAGE_NONE)
+ , nDefaultLanguage_CJK(LANGUAGE_NONE)
+ , nDefaultLanguage_CTL(LANGUAGE_NONE)
+ , bRODefaultLanguage(false)
+ , bRODefaultLanguage_CJK(false)
+ , bRODefaultLanguage_CTL(false)
+ , bIsSpellSpecial(true)
+ , bIsSpellAuto(false)
+ , bIsSpellReverse(false)
+ , bROIsSpellSpecial(false)
+ , bROIsSpellAuto(false)
+ , bROIsSpellReverse(false)
+ , bIsHyphSpecial(true)
+ , bIsHyphAuto(false)
+ , bROIsHyphSpecial(false)
+ , bROIsHyphAuto(false)
+ , bIsUseDictionaryList(true)
+ , bIsIgnoreControlCharacters(true)
+ , bROIsUseDictionaryList(false)
+ , bROIsIgnoreControlCharacters(false)
+ , bIsSpellWithDigits(false)
+ , bIsSpellUpperCase(false)
+ , bIsSpellClosedCompound(true)
+ , bIsSpellHyphenatedCompound(true)
+ , bROIsSpellWithDigits(false)
+ , bROIsSpellUpperCase(false)
+ , bROIsSpellClosedCompound(false)
+ , bROIsSpellHyphenatedCompound(false)
+ , bIsIgnorePostPositionalWord(true)
+ , bIsAutoCloseDialog(false)
+ , bIsShowEntriesRecentlyUsedFirst(false)
+ , bIsAutoReplaceUniqueEntries(false)
+ , bIsDirectionToSimplified(true)
+ , bIsUseCharacterVariants(false)
+ , bIsTranslateCommonTerms(false)
+ , bIsReverseMapping(false)
+ , bROIsIgnorePostPositionalWord(false)
+ , bROIsAutoCloseDialog(false)
+ , bROIsShowEntriesRecentlyUsedFirst(false)
+ , bROIsAutoReplaceUniqueEntries(false)
+ , bROIsDirectionToSimplified(false)
+ , bROIsUseCharacterVariants(false)
+ , bROIsTranslateCommonTerms(false)
+ , bROIsReverseMapping(false)
+ , nDataFilesChangedCheckValue(0)
+ , bRODataFilesChangedCheckValue(false)
+ , bIsGrammarAuto(false)
+ , bIsGrammarInteractive(false)
+ , bROIsGrammarAuto(false)
+ , bROIsGrammarInteractive(false)
+{
+}
+
+class SvtLinguConfigItem : public utl::ConfigItem
+{
+ SvtLinguOptions aOpt;
+
+ static bool GetHdlByName( sal_Int32 &rnHdl, std::u16string_view rPropertyName, bool bFullPropName = false );
+ static uno::Sequence< OUString > GetPropertyNames();
+ void LoadOptions( const uno::Sequence< OUString > &rProperyNames );
+ bool SaveOptions( const uno::Sequence< OUString > &rProperyNames );
+
+ SvtLinguConfigItem(const SvtLinguConfigItem&) = delete;
+ SvtLinguConfigItem& operator=(const SvtLinguConfigItem&) = delete;
+ virtual void ImplCommit() override;
+
+public:
+ SvtLinguConfigItem();
+
+ // utl::ConfigItem
+ virtual void Notify( const css::uno::Sequence< OUString > &rPropertyNames ) override;
+
+ // make some protected functions of utl::ConfigItem public
+ using utl::ConfigItem::GetNodeNames;
+ using utl::ConfigItem::GetProperties;
+ //using utl::ConfigItem::PutProperties;
+ //using utl::ConfigItem::SetSetProperties;
+ using utl::ConfigItem::ReplaceSetProperties;
+ //using utl::ConfigItem::GetReadOnlyStates;
+
+ css::uno::Any
+ GetProperty( std::u16string_view rPropertyName ) const;
+ css::uno::Any
+ GetProperty( sal_Int32 nPropertyHandle ) const;
+
+ bool SetProperty( std::u16string_view rPropertyName,
+ const css::uno::Any &rValue );
+ bool SetProperty( sal_Int32 nPropertyHandle,
+ const css::uno::Any &rValue );
+
+ void GetOptions( SvtLinguOptions& ) const;
+
+ bool IsReadOnly( std::u16string_view rPropertyName ) const;
+ bool IsReadOnly( sal_Int32 nPropertyHandle ) const;
+};
+
+SvtLinguConfigItem::SvtLinguConfigItem() :
+ utl::ConfigItem( "Office.Linguistic" )
+{
+ const uno::Sequence< OUString > &rPropertyNames = GetPropertyNames();
+ LoadOptions( rPropertyNames );
+ ClearModified();
+
+ // request notify events when properties change
+ EnableNotification( rPropertyNames );
+}
+
+void SvtLinguConfigItem::Notify( const uno::Sequence< OUString > &rPropertyNames )
+{
+ {
+ std::unique_lock aGuard(theSvtLinguConfigItemMutex());
+ LoadOptions( rPropertyNames );
+ }
+ NotifyListeners(ConfigurationHints::NONE);
+}
+
+void SvtLinguConfigItem::ImplCommit()
+{
+ SaveOptions( GetPropertyNames() );
+}
+
+namespace {
+
+struct NamesToHdl
+{
+ const char *pFullPropName; // full qualified name as used in configuration
+ OUString aPropName; // property name only (atom) of above
+ sal_Int32 nHdl; // numeric handle representing the property
+};
+
+}
+
+NamesToHdl const aNamesToHdl[] =
+{
+{/* 0 */ "General/DefaultLocale", UPN_DEFAULT_LOCALE, UPH_DEFAULT_LOCALE},
+{/* 1 */ "General/DictionaryList/ActiveDictionaries", UPN_ACTIVE_DICTIONARIES, UPH_ACTIVE_DICTIONARIES},
+{/* 2 */ "General/DictionaryList/IsUseDictionaryList", UPN_IS_USE_DICTIONARY_LIST, UPH_IS_USE_DICTIONARY_LIST},
+{/* 3 */ "General/IsIgnoreControlCharacters", UPN_IS_IGNORE_CONTROL_CHARACTERS, UPH_IS_IGNORE_CONTROL_CHARACTERS},
+{/* 5 */ "General/DefaultLocale_CJK", UPN_DEFAULT_LOCALE_CJK, UPH_DEFAULT_LOCALE_CJK},
+{/* 6 */ "General/DefaultLocale_CTL", UPN_DEFAULT_LOCALE_CTL, UPH_DEFAULT_LOCALE_CTL},
+
+{/* 7 */ "SpellChecking/IsSpellUpperCase", UPN_IS_SPELL_UPPER_CASE, UPH_IS_SPELL_UPPER_CASE},
+{/* 8 */ "SpellChecking/IsSpellWithDigits", UPN_IS_SPELL_WITH_DIGITS, UPH_IS_SPELL_WITH_DIGITS},
+{/* 9 */ "SpellChecking/IsSpellAuto", UPN_IS_SPELL_AUTO, UPH_IS_SPELL_AUTO},
+{/* 10 */ "SpellChecking/IsSpellSpecial", UPN_IS_SPELL_SPECIAL, UPH_IS_SPELL_SPECIAL},
+{/* 11 */ "SpellChecking/IsSpellClosedCompound", UPN_IS_SPELL_CLOSED_COMPOUND, UPH_IS_SPELL_CLOSED_COMPOUND},
+{/* 12 */ "SpellChecking/IsSpellHyphenatedCompound", UPN_IS_SPELL_HYPHENATED_COMPOUND, UPH_IS_SPELL_HYPHENATED_COMPOUND},
+{/* 13 */ "SpellChecking/IsReverseDirection", UPN_IS_WRAP_REVERSE, UPH_IS_WRAP_REVERSE},
+
+{/* 14 */ "Hyphenation/MinLeading", UPN_HYPH_MIN_LEADING, UPH_HYPH_MIN_LEADING},
+{/* 15 */ "Hyphenation/MinTrailing", UPN_HYPH_MIN_TRAILING, UPH_HYPH_MIN_TRAILING},
+{/* 16 */ "Hyphenation/MinWordLength", UPN_HYPH_MIN_WORD_LENGTH, UPH_HYPH_MIN_WORD_LENGTH},
+{/* 17*/ "Hyphenation/IsHyphSpecial", UPN_IS_HYPH_SPECIAL, UPH_IS_HYPH_SPECIAL},
+{/* 18 */ "Hyphenation/IsHyphAuto", UPN_IS_HYPH_AUTO, UPH_IS_HYPH_AUTO},
+
+{/* 19 */ "TextConversion/ActiveConversionDictionaries", UPN_ACTIVE_CONVERSION_DICTIONARIES, UPH_ACTIVE_CONVERSION_DICTIONARIES},
+{/* 20 */ "TextConversion/IsIgnorePostPositionalWord", UPN_IS_IGNORE_POST_POSITIONAL_WORD, UPH_IS_IGNORE_POST_POSITIONAL_WORD},
+{/* 21 */ "TextConversion/IsAutoCloseDialog", UPN_IS_AUTO_CLOSE_DIALOG, UPH_IS_AUTO_CLOSE_DIALOG},
+{/* 22 */ "TextConversion/IsShowEntriesRecentlyUsedFirst", UPN_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST, UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST},
+{/* 23 */ "TextConversion/IsAutoReplaceUniqueEntries", UPN_IS_AUTO_REPLACE_UNIQUE_ENTRIES, UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES},
+{/* 24 */ "TextConversion/IsDirectionToSimplified", UPN_IS_DIRECTION_TO_SIMPLIFIED, UPH_IS_DIRECTION_TO_SIMPLIFIED},
+{/* 25 */ "TextConversion/IsUseCharacterVariants", UPN_IS_USE_CHARACTER_VARIANTS, UPH_IS_USE_CHARACTER_VARIANTS},
+{/* 26 */ "TextConversion/IsTranslateCommonTerms", UPN_IS_TRANSLATE_COMMON_TERMS, UPH_IS_TRANSLATE_COMMON_TERMS},
+{/* 27 */ "TextConversion/IsReverseMapping", UPN_IS_REVERSE_MAPPING, UPH_IS_REVERSE_MAPPING},
+
+{/* 28 */ "ServiceManager/DataFilesChangedCheckValue", UPN_DATA_FILES_CHANGED_CHECK_VALUE, UPH_DATA_FILES_CHANGED_CHECK_VALUE},
+
+{/* 29 */ "GrammarChecking/IsAutoCheck", UPN_IS_GRAMMAR_AUTO, UPH_IS_GRAMMAR_AUTO},
+{/* 30 */ "GrammarChecking/IsInteractiveCheck", UPN_IS_GRAMMAR_INTERACTIVE, UPH_IS_GRAMMAR_INTERACTIVE},
+
+ /* similar to entry 0 (thus no own configuration entry) but with different property name and type */
+{ nullptr, UPN_DEFAULT_LANGUAGE, UPH_DEFAULT_LANGUAGE},
+
+{ nullptr, "", -1}
+};
+
+uno::Sequence< OUString > SvtLinguConfigItem::GetPropertyNames()
+{
+ uno::Sequence< OUString > aNames;
+ aNames.realloc(std::size(aNamesToHdl));
+ OUString *pNames = aNames.getArray();
+ sal_Int32 nIdx = 0;
+ for (auto const & nameToHdl: aNamesToHdl)
+ {
+ const char *pFullPropName = nameToHdl.pFullPropName;
+ if (pFullPropName)
+ pNames[ nIdx++ ] = OUString::createFromAscii( pFullPropName );
+ }
+ aNames.realloc( nIdx );
+
+ return aNames;
+}
+
+bool SvtLinguConfigItem::GetHdlByName(
+ sal_Int32 &rnHdl,
+ std::u16string_view rPropertyName,
+ bool bFullPropName )
+{
+ NamesToHdl const *pEntry = &aNamesToHdl[0];
+
+ if (bFullPropName)
+ {
+ while (pEntry && pEntry->pFullPropName != nullptr)
+ {
+ if (o3tl::equalsAscii(rPropertyName, pEntry->pFullPropName ))
+ {
+ rnHdl = pEntry->nHdl;
+ break;
+ }
+ ++pEntry;
+ }
+ return pEntry && pEntry->pFullPropName != nullptr;
+ }
+ else
+ {
+ while (pEntry && pEntry->pFullPropName != nullptr)
+ {
+ if (rPropertyName == pEntry->aPropName )
+ {
+ rnHdl = pEntry->nHdl;
+ break;
+ }
+ ++pEntry;
+ }
+ return pEntry && pEntry->pFullPropName != nullptr;
+ }
+}
+
+uno::Any SvtLinguConfigItem::GetProperty( std::u16string_view rPropertyName ) const
+{
+ sal_Int32 nHdl;
+ return GetHdlByName( nHdl, rPropertyName ) ? GetProperty( nHdl ) : uno::Any();
+}
+
+uno::Any SvtLinguConfigItem::GetProperty( sal_Int32 nPropertyHandle ) const
+{
+ std::unique_lock aGuard(theSvtLinguConfigItemMutex());
+
+ uno::Any aRes;
+
+ const sal_Int16 *pnVal = nullptr;
+ const LanguageType *plVal = nullptr;
+ const bool *pbVal = nullptr;
+ const sal_Int32 *pnInt32Val = nullptr;
+
+ const SvtLinguOptions &rOpt = const_cast< SvtLinguConfigItem * >(this)->aOpt;
+ switch (nPropertyHandle)
+ {
+ case UPH_IS_USE_DICTIONARY_LIST : pbVal = &rOpt.bIsUseDictionaryList; break;
+ case UPH_IS_IGNORE_CONTROL_CHARACTERS : pbVal = &rOpt.bIsIgnoreControlCharacters; break;
+ case UPH_IS_HYPH_AUTO : pbVal = &rOpt.bIsHyphAuto; break;
+ case UPH_IS_HYPH_SPECIAL : pbVal = &rOpt.bIsHyphSpecial; break;
+ case UPH_IS_SPELL_AUTO : pbVal = &rOpt.bIsSpellAuto; break;
+ case UPH_IS_SPELL_SPECIAL : pbVal = &rOpt.bIsSpellSpecial; break;
+ case UPH_IS_WRAP_REVERSE : pbVal = &rOpt.bIsSpellReverse; break;
+ case UPH_DEFAULT_LANGUAGE : plVal = &rOpt.nDefaultLanguage; break;
+ case UPH_IS_SPELL_CLOSED_COMPOUND: pbVal = &rOpt.bIsSpellClosedCompound; break;
+ case UPH_IS_SPELL_HYPHENATED_COMPOUND: pbVal = &rOpt.bIsSpellHyphenatedCompound; break;
+ case UPH_IS_SPELL_WITH_DIGITS : pbVal = &rOpt.bIsSpellWithDigits; break;
+ case UPH_IS_SPELL_UPPER_CASE : pbVal = &rOpt.bIsSpellUpperCase; break;
+ case UPH_HYPH_MIN_LEADING : pnVal = &rOpt.nHyphMinLeading; break;
+ case UPH_HYPH_MIN_TRAILING : pnVal = &rOpt.nHyphMinTrailing; break;
+ case UPH_HYPH_MIN_WORD_LENGTH : pnVal = &rOpt.nHyphMinWordLength; break;
+ case UPH_ACTIVE_DICTIONARIES :
+ {
+ aRes <<= rOpt.aActiveDics;
+ break;
+ }
+ case UPH_ACTIVE_CONVERSION_DICTIONARIES :
+ {
+ aRes <<= rOpt.aActiveConvDics;
+ break;
+ }
+ case UPH_DEFAULT_LOCALE :
+ {
+ aRes <<= LanguageTag::convertToLocale( rOpt.nDefaultLanguage, false);
+ break;
+ }
+ case UPH_DEFAULT_LOCALE_CJK :
+ {
+ aRes <<= LanguageTag::convertToLocale( rOpt.nDefaultLanguage_CJK, false);
+ break;
+ }
+ case UPH_DEFAULT_LOCALE_CTL :
+ {
+ aRes <<= LanguageTag::convertToLocale( rOpt.nDefaultLanguage_CTL, false);
+ break;
+ }
+ case UPH_IS_IGNORE_POST_POSITIONAL_WORD : pbVal = &rOpt.bIsIgnorePostPositionalWord; break;
+ case UPH_IS_AUTO_CLOSE_DIALOG : pbVal = &rOpt.bIsAutoCloseDialog; break;
+ case UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST : pbVal = &rOpt.bIsShowEntriesRecentlyUsedFirst; break;
+ case UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES : pbVal = &rOpt.bIsAutoReplaceUniqueEntries; break;
+
+ case UPH_IS_DIRECTION_TO_SIMPLIFIED: pbVal = &rOpt.bIsDirectionToSimplified; break;
+ case UPH_IS_USE_CHARACTER_VARIANTS : pbVal = &rOpt.bIsUseCharacterVariants; break;
+ case UPH_IS_TRANSLATE_COMMON_TERMS : pbVal = &rOpt.bIsTranslateCommonTerms; break;
+ case UPH_IS_REVERSE_MAPPING : pbVal = &rOpt.bIsReverseMapping; break;
+
+ case UPH_DATA_FILES_CHANGED_CHECK_VALUE : pnInt32Val = &rOpt.nDataFilesChangedCheckValue; break;
+ case UPH_IS_GRAMMAR_AUTO: pbVal = &rOpt.bIsGrammarAuto; break;
+ case UPH_IS_GRAMMAR_INTERACTIVE: pbVal = &rOpt.bIsGrammarInteractive; break;
+ default :
+ SAL_WARN( "unotools.config", "unexpected property handle" );
+ }
+
+ if (pbVal)
+ aRes <<= *pbVal;
+ else if (pnVal)
+ aRes <<= *pnVal;
+ else if (plVal)
+ aRes <<= static_cast<sal_Int16>(static_cast<sal_uInt16>(*plVal));
+ else if (pnInt32Val)
+ aRes <<= *pnInt32Val;
+
+ return aRes;
+}
+
+bool SvtLinguConfigItem::SetProperty( std::u16string_view rPropertyName, const uno::Any &rValue )
+{
+ bool bSucc = false;
+ sal_Int32 nHdl;
+ if (GetHdlByName( nHdl, rPropertyName ))
+ bSucc = SetProperty( nHdl, rValue );
+ return bSucc;
+}
+
+bool SvtLinguConfigItem::SetProperty( sal_Int32 nPropertyHandle, const uno::Any &rValue )
+{
+ std::unique_lock aGuard(theSvtLinguConfigItemMutex());
+
+ bool bSucc = false;
+ if (!rValue.hasValue())
+ return bSucc;
+
+ bool bMod = false;
+
+ sal_Int16 *pnVal = nullptr;
+ LanguageType *plVal = nullptr;
+ bool *pbVal = nullptr;
+ sal_Int32 *pnInt32Val = nullptr;
+
+ SvtLinguOptions &rOpt = aOpt;
+ switch (nPropertyHandle)
+ {
+ case UPH_IS_USE_DICTIONARY_LIST : pbVal = &rOpt.bIsUseDictionaryList; break;
+ case UPH_IS_IGNORE_CONTROL_CHARACTERS : pbVal = &rOpt.bIsIgnoreControlCharacters; break;
+ case UPH_IS_HYPH_AUTO : pbVal = &rOpt.bIsHyphAuto; break;
+ case UPH_IS_HYPH_SPECIAL : pbVal = &rOpt.bIsHyphSpecial; break;
+ case UPH_IS_SPELL_AUTO : pbVal = &rOpt.bIsSpellAuto; break;
+ case UPH_IS_SPELL_SPECIAL : pbVal = &rOpt.bIsSpellSpecial; break;
+ case UPH_IS_WRAP_REVERSE : pbVal = &rOpt.bIsSpellReverse; break;
+ case UPH_DEFAULT_LANGUAGE : plVal = &rOpt.nDefaultLanguage; break;
+ case UPH_IS_SPELL_CLOSED_COMPOUND: pbVal = &rOpt.bIsSpellClosedCompound; break;
+ case UPH_IS_SPELL_HYPHENATED_COMPOUND: pbVal = &rOpt.bIsSpellHyphenatedCompound; break;
+ case UPH_IS_SPELL_WITH_DIGITS : pbVal = &rOpt.bIsSpellWithDigits; break;
+ case UPH_IS_SPELL_UPPER_CASE : pbVal = &rOpt.bIsSpellUpperCase; break;
+ case UPH_HYPH_MIN_LEADING : pnVal = &rOpt.nHyphMinLeading; break;
+ case UPH_HYPH_MIN_TRAILING : pnVal = &rOpt.nHyphMinTrailing; break;
+ case UPH_HYPH_MIN_WORD_LENGTH : pnVal = &rOpt.nHyphMinWordLength; break;
+ case UPH_ACTIVE_DICTIONARIES :
+ {
+ rValue >>= rOpt.aActiveDics;
+ bMod = true;
+ break;
+ }
+ case UPH_ACTIVE_CONVERSION_DICTIONARIES :
+ {
+ rValue >>= rOpt.aActiveConvDics;
+ bMod = true;
+ break;
+ }
+ case UPH_DEFAULT_LOCALE :
+ {
+ bSucc = lcl_SetLocale( rOpt.nDefaultLanguage, rValue );
+ bMod = bSucc;
+ break;
+ }
+ case UPH_DEFAULT_LOCALE_CJK :
+ {
+ bSucc = lcl_SetLocale( rOpt.nDefaultLanguage_CJK, rValue );
+ bMod = bSucc;
+ break;
+ }
+ case UPH_DEFAULT_LOCALE_CTL :
+ {
+ bSucc = lcl_SetLocale( rOpt.nDefaultLanguage_CTL, rValue );
+ bMod = bSucc;
+ break;
+ }
+ case UPH_IS_IGNORE_POST_POSITIONAL_WORD : pbVal = &rOpt.bIsIgnorePostPositionalWord; break;
+ case UPH_IS_AUTO_CLOSE_DIALOG : pbVal = &rOpt.bIsAutoCloseDialog; break;
+ case UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST : pbVal = &rOpt.bIsShowEntriesRecentlyUsedFirst; break;
+ case UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES : pbVal = &rOpt.bIsAutoReplaceUniqueEntries; break;
+
+ case UPH_IS_DIRECTION_TO_SIMPLIFIED : pbVal = &rOpt.bIsDirectionToSimplified; break;
+ case UPH_IS_USE_CHARACTER_VARIANTS : pbVal = &rOpt.bIsUseCharacterVariants; break;
+ case UPH_IS_TRANSLATE_COMMON_TERMS : pbVal = &rOpt.bIsTranslateCommonTerms; break;
+ case UPH_IS_REVERSE_MAPPING : pbVal = &rOpt.bIsReverseMapping; break;
+
+ case UPH_DATA_FILES_CHANGED_CHECK_VALUE : pnInt32Val = &rOpt.nDataFilesChangedCheckValue; break;
+ case UPH_IS_GRAMMAR_AUTO: pbVal = &rOpt.bIsGrammarAuto; break;
+ case UPH_IS_GRAMMAR_INTERACTIVE: pbVal = &rOpt.bIsGrammarInteractive; break;
+ default :
+ SAL_WARN( "unotools.config", "unexpected property handle" );
+ }
+
+ if (pbVal)
+ {
+ bool bNew = bool();
+ if (rValue >>= bNew)
+ {
+ if (bNew != *pbVal)
+ {
+ *pbVal = bNew;
+ bMod = true;
+ }
+ bSucc = true;
+ }
+ }
+ else if (pnVal)
+ {
+ sal_Int16 nNew = sal_Int16();
+ if (rValue >>= nNew)
+ {
+ if (nNew != *pnVal)
+ {
+ *pnVal = nNew;
+ bMod = true;
+ }
+ bSucc = true;
+ }
+ }
+ else if (plVal)
+ {
+ sal_Int16 nNew = sal_Int16();
+ if (rValue >>= nNew)
+ {
+ if (nNew != static_cast<sal_uInt16>(*plVal))
+ {
+ *plVal = LanguageType(static_cast<sal_uInt16>(nNew));
+ bMod = true;
+ }
+ bSucc = true;
+ }
+ }
+ else if (pnInt32Val)
+ {
+ sal_Int32 nNew = sal_Int32();
+ if (rValue >>= nNew)
+ {
+ if (nNew != *pnInt32Val)
+ {
+ *pnInt32Val = nNew;
+ bMod = true;
+ }
+ bSucc = true;
+ }
+ }
+
+ if (bMod)
+ SetModified();
+
+ NotifyListeners(ConfigurationHints::NONE);
+ return bSucc;
+}
+
+void SvtLinguConfigItem::GetOptions(SvtLinguOptions &rOptions) const
+{
+ std::unique_lock aGuard(theSvtLinguConfigItemMutex());
+ rOptions = aOpt;
+}
+
+void SvtLinguConfigItem::LoadOptions( const uno::Sequence< OUString > &rProperyNames )
+{
+ bool bRes = false;
+
+ const OUString *pProperyNames = rProperyNames.getConstArray();
+ sal_Int32 nProps = rProperyNames.getLength();
+
+ const uno::Sequence< uno::Any > aValues = GetProperties( rProperyNames );
+ const uno::Sequence< sal_Bool > aROStates = GetReadOnlyStates( rProperyNames );
+
+ if (nProps && aValues.getLength() == nProps && aROStates.getLength() == nProps)
+ {
+ SvtLinguOptions &rOpt = aOpt;
+
+ const uno::Any *pValue = aValues.getConstArray();
+ const sal_Bool *pROStates = aROStates.getConstArray();
+ for (sal_Int32 i = 0; i < nProps; ++i)
+ {
+ const uno::Any &rVal = pValue[i];
+ sal_Int32 nPropertyHandle(0);
+ GetHdlByName( nPropertyHandle, pProperyNames[i], true );
+ switch ( nPropertyHandle )
+ {
+ case UPH_DEFAULT_LOCALE :
+ { rOpt.bRODefaultLanguage = pROStates[i]; rOpt.nDefaultLanguage = lcl_CfgAnyToLanguage( rVal ); } break;
+ case UPH_ACTIVE_DICTIONARIES :
+ { rOpt.bROActiveDics = pROStates[i]; rVal >>= rOpt.aActiveDics; } break;
+ case UPH_IS_USE_DICTIONARY_LIST :
+ { rOpt.bROIsUseDictionaryList = pROStates[i]; rVal >>= rOpt.bIsUseDictionaryList; } break;
+ case UPH_IS_IGNORE_CONTROL_CHARACTERS :
+ { rOpt.bROIsIgnoreControlCharacters = pROStates[i]; rVal >>= rOpt.bIsIgnoreControlCharacters; } break;
+ case UPH_DEFAULT_LOCALE_CJK :
+ { rOpt.bRODefaultLanguage_CJK = pROStates[i]; rOpt.nDefaultLanguage_CJK = lcl_CfgAnyToLanguage( rVal ); } break;
+ case UPH_DEFAULT_LOCALE_CTL :
+ { rOpt.bRODefaultLanguage_CTL = pROStates[i]; rOpt.nDefaultLanguage_CTL = lcl_CfgAnyToLanguage( rVal ); } break;
+
+ case UPH_IS_SPELL_UPPER_CASE :
+ { rOpt.bROIsSpellUpperCase = pROStates[i]; rVal >>= rOpt.bIsSpellUpperCase; } break;
+ case UPH_IS_SPELL_WITH_DIGITS :
+ { rOpt.bROIsSpellWithDigits = pROStates[i]; rVal >>= rOpt.bIsSpellWithDigits; } break;
+ case UPH_IS_SPELL_CLOSED_COMPOUND :
+ { rOpt.bROIsSpellClosedCompound = pROStates[i]; rVal >>= rOpt.bIsSpellClosedCompound; } break;
+ case UPH_IS_SPELL_HYPHENATED_COMPOUND :
+ { rOpt.bROIsSpellHyphenatedCompound = pROStates[i]; rVal >>= rOpt.bIsSpellHyphenatedCompound; } break;
+
+ case UPH_IS_SPELL_AUTO :
+ { rOpt.bROIsSpellAuto = pROStates[i]; rVal >>= rOpt.bIsSpellAuto; } break;
+ case UPH_IS_SPELL_SPECIAL :
+ { rOpt.bROIsSpellSpecial = pROStates[i]; rVal >>= rOpt.bIsSpellSpecial; } break;
+ case UPH_IS_WRAP_REVERSE :
+ { rOpt.bROIsSpellReverse = pROStates[i]; rVal >>= rOpt.bIsSpellReverse; } break;
+
+ case UPH_HYPH_MIN_LEADING :
+ { rOpt.bROHyphMinLeading = pROStates[i]; rVal >>= rOpt.nHyphMinLeading; } break;
+ case UPH_HYPH_MIN_TRAILING :
+ { rOpt.bROHyphMinTrailing = pROStates[i]; rVal >>= rOpt.nHyphMinTrailing; } break;
+ case UPH_HYPH_MIN_WORD_LENGTH :
+ { rOpt.bROHyphMinWordLength = pROStates[i]; rVal >>= rOpt.nHyphMinWordLength; } break;
+ case UPH_IS_HYPH_SPECIAL :
+ { rOpt.bROIsHyphSpecial = pROStates[i]; rVal >>= rOpt.bIsHyphSpecial; } break;
+ case UPH_IS_HYPH_AUTO :
+ { rOpt.bROIsHyphAuto = pROStates[i]; rVal >>= rOpt.bIsHyphAuto; } break;
+
+ case UPH_ACTIVE_CONVERSION_DICTIONARIES : { rOpt.bROActiveConvDics = pROStates[i]; rVal >>= rOpt.aActiveConvDics; } break;
+
+ case UPH_IS_IGNORE_POST_POSITIONAL_WORD :
+ { rOpt.bROIsIgnorePostPositionalWord = pROStates[i]; rVal >>= rOpt.bIsIgnorePostPositionalWord; } break;
+ case UPH_IS_AUTO_CLOSE_DIALOG :
+ { rOpt.bROIsAutoCloseDialog = pROStates[i]; rVal >>= rOpt.bIsAutoCloseDialog; } break;
+ case UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST :
+ { rOpt.bROIsShowEntriesRecentlyUsedFirst = pROStates[i]; rVal >>= rOpt.bIsShowEntriesRecentlyUsedFirst; } break;
+ case UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES :
+ { rOpt.bROIsAutoReplaceUniqueEntries = pROStates[i]; rVal >>= rOpt.bIsAutoReplaceUniqueEntries; } break;
+
+ case UPH_IS_DIRECTION_TO_SIMPLIFIED :
+ {
+ rOpt.bROIsDirectionToSimplified = pROStates[i];
+ if( ! (rVal >>= rOpt.bIsDirectionToSimplified) )
+ {
+ //default is locale dependent:
+ if (MsLangId::isTraditionalChinese(rOpt.nDefaultLanguage_CJK))
+ {
+ rOpt.bIsDirectionToSimplified = false;
+ }
+ else
+ {
+ rOpt.bIsDirectionToSimplified = true;
+ }
+ }
+ } break;
+ case UPH_IS_USE_CHARACTER_VARIANTS :
+ { rOpt.bROIsUseCharacterVariants = pROStates[i]; rVal >>= rOpt.bIsUseCharacterVariants; } break;
+ case UPH_IS_TRANSLATE_COMMON_TERMS :
+ { rOpt.bROIsTranslateCommonTerms = pROStates[i]; rVal >>= rOpt.bIsTranslateCommonTerms; } break;
+ case UPH_IS_REVERSE_MAPPING :
+ { rOpt.bROIsReverseMapping = pROStates[i]; rVal >>= rOpt.bIsReverseMapping; } break;
+
+ case UPH_DATA_FILES_CHANGED_CHECK_VALUE :
+ { rOpt.bRODataFilesChangedCheckValue = pROStates[i]; rVal >>= rOpt.nDataFilesChangedCheckValue; } break;
+
+ case UPH_IS_GRAMMAR_AUTO:
+ { rOpt.bROIsGrammarAuto = pROStates[i]; rVal >>= rOpt.bIsGrammarAuto; }
+ break;
+ case UPH_IS_GRAMMAR_INTERACTIVE:
+ { rOpt.bROIsGrammarInteractive = pROStates[i]; rVal >>= rOpt.bIsGrammarInteractive; }
+ break;
+
+ default:
+ SAL_WARN( "unotools.config", "unexpected case" );
+ }
+ }
+
+ bRes = true;
+ }
+ DBG_ASSERT( bRes, "LoadOptions failed" );
+}
+
+bool SvtLinguConfigItem::SaveOptions( const uno::Sequence< OUString > &rProperyNames )
+{
+ if (!IsModified())
+ return true;
+
+ std::unique_lock aGuard(theSvtLinguConfigItemMutex());
+
+ bool bRet = false;
+
+ sal_Int32 nProps = rProperyNames.getLength();
+ uno::Sequence< uno::Any > aValues( nProps );
+ uno::Any *pValue = aValues.getArray();
+
+ if (nProps && aValues.getLength() == nProps)
+ {
+ const SvtLinguOptions &rOpt = aOpt;
+
+ OUString aTmp( lcl_LanguageToCfgLocaleStr( rOpt.nDefaultLanguage ) );
+ *pValue++ <<= aTmp; // 0
+ *pValue++ <<= rOpt.aActiveDics; // 1
+ *pValue++ <<= rOpt.bIsUseDictionaryList; // 2
+ *pValue++ <<= rOpt.bIsIgnoreControlCharacters; // 3
+ aTmp = lcl_LanguageToCfgLocaleStr( rOpt.nDefaultLanguage_CJK );
+ *pValue++ <<= aTmp; // 5
+ aTmp = lcl_LanguageToCfgLocaleStr( rOpt.nDefaultLanguage_CTL );
+ *pValue++ <<= aTmp; // 6
+
+ *pValue++ <<= rOpt.bIsSpellUpperCase; // 7
+ *pValue++ <<= rOpt.bIsSpellWithDigits; // 8
+ *pValue++ <<= rOpt.bIsSpellAuto; // 9
+ *pValue++ <<= rOpt.bIsSpellSpecial; // 10
+ *pValue++ <<= rOpt.bIsSpellClosedCompound; // 11
+ *pValue++ <<= rOpt.bIsSpellHyphenatedCompound; // 12
+ *pValue++ <<= rOpt.bIsSpellReverse; // 13
+
+ *pValue++ <<= rOpt.nHyphMinLeading; // 14
+ *pValue++ <<= rOpt.nHyphMinTrailing; // 15
+ *pValue++ <<= rOpt.nHyphMinWordLength; // 16
+ *pValue++ <<= rOpt.bIsHyphSpecial; // 17
+ *pValue++ <<= rOpt.bIsHyphAuto; // 18
+
+ *pValue++ <<= rOpt.aActiveConvDics; // 19
+
+ *pValue++ <<= rOpt.bIsIgnorePostPositionalWord; // 20
+ *pValue++ <<= rOpt.bIsAutoCloseDialog; // 21
+ *pValue++ <<= rOpt.bIsShowEntriesRecentlyUsedFirst; // 22
+ *pValue++ <<= rOpt.bIsAutoReplaceUniqueEntries; // 23
+
+ *pValue++ <<= rOpt.bIsDirectionToSimplified; // 24
+ *pValue++ <<= rOpt.bIsUseCharacterVariants; // 25
+ *pValue++ <<= rOpt.bIsTranslateCommonTerms; // 26
+ *pValue++ <<= rOpt.bIsReverseMapping; // 27
+
+ *pValue++ <<= rOpt.nDataFilesChangedCheckValue; // 28
+ *pValue++ <<= rOpt.bIsGrammarAuto; // 29
+ *pValue++ <<= rOpt.bIsGrammarInteractive; // 30
+
+ bRet |= PutProperties( rProperyNames, aValues );
+ }
+
+ if (bRet)
+ ClearModified();
+
+ return bRet;
+}
+
+bool SvtLinguConfigItem::IsReadOnly( std::u16string_view rPropertyName ) const
+{
+ bool bReadOnly = false;
+ sal_Int32 nHdl;
+ if (GetHdlByName( nHdl, rPropertyName ))
+ bReadOnly = IsReadOnly( nHdl );
+ return bReadOnly;
+}
+
+bool SvtLinguConfigItem::IsReadOnly( sal_Int32 nPropertyHandle ) const
+{
+ std::unique_lock aGuard(theSvtLinguConfigItemMutex());
+
+ bool bReadOnly = false;
+
+ const SvtLinguOptions &rOpt = const_cast< SvtLinguConfigItem * >(this)->aOpt;
+ switch(nPropertyHandle)
+ {
+ case UPH_IS_USE_DICTIONARY_LIST : bReadOnly = rOpt.bROIsUseDictionaryList; break;
+ case UPH_IS_IGNORE_CONTROL_CHARACTERS : bReadOnly = rOpt.bROIsIgnoreControlCharacters; break;
+ case UPH_IS_HYPH_AUTO : bReadOnly = rOpt.bROIsHyphAuto; break;
+ case UPH_IS_HYPH_SPECIAL : bReadOnly = rOpt.bROIsHyphSpecial; break;
+ case UPH_IS_SPELL_AUTO : bReadOnly = rOpt.bROIsSpellAuto; break;
+ case UPH_IS_SPELL_SPECIAL : bReadOnly = rOpt.bROIsSpellSpecial; break;
+ case UPH_IS_WRAP_REVERSE : bReadOnly = rOpt.bROIsSpellReverse; break;
+ case UPH_DEFAULT_LANGUAGE : bReadOnly = rOpt.bRODefaultLanguage; break;
+ case UPH_IS_SPELL_CLOSED_COMPOUND : bReadOnly = rOpt.bROIsSpellClosedCompound; break;
+ case UPH_IS_SPELL_HYPHENATED_COMPOUND : bReadOnly = rOpt.bROIsSpellHyphenatedCompound; break;
+ case UPH_IS_SPELL_WITH_DIGITS : bReadOnly = rOpt.bROIsSpellWithDigits; break;
+ case UPH_IS_SPELL_UPPER_CASE : bReadOnly = rOpt.bROIsSpellUpperCase; break;
+ case UPH_HYPH_MIN_LEADING : bReadOnly = rOpt.bROHyphMinLeading; break;
+ case UPH_HYPH_MIN_TRAILING : bReadOnly = rOpt.bROHyphMinTrailing; break;
+ case UPH_HYPH_MIN_WORD_LENGTH : bReadOnly = rOpt.bROHyphMinWordLength; break;
+ case UPH_ACTIVE_DICTIONARIES : bReadOnly = rOpt.bROActiveDics; break;
+ case UPH_ACTIVE_CONVERSION_DICTIONARIES : bReadOnly = rOpt.bROActiveConvDics; break;
+ case UPH_DEFAULT_LOCALE : bReadOnly = rOpt.bRODefaultLanguage; break;
+ case UPH_DEFAULT_LOCALE_CJK : bReadOnly = rOpt.bRODefaultLanguage_CJK; break;
+ case UPH_DEFAULT_LOCALE_CTL : bReadOnly = rOpt.bRODefaultLanguage_CTL; break;
+ case UPH_IS_IGNORE_POST_POSITIONAL_WORD : bReadOnly = rOpt.bROIsIgnorePostPositionalWord; break;
+ case UPH_IS_AUTO_CLOSE_DIALOG : bReadOnly = rOpt.bROIsAutoCloseDialog; break;
+ case UPH_IS_SHOW_ENTRIES_RECENTLY_USED_FIRST : bReadOnly = rOpt.bROIsShowEntriesRecentlyUsedFirst; break;
+ case UPH_IS_AUTO_REPLACE_UNIQUE_ENTRIES : bReadOnly = rOpt.bROIsAutoReplaceUniqueEntries; break;
+ case UPH_IS_DIRECTION_TO_SIMPLIFIED : bReadOnly = rOpt.bROIsDirectionToSimplified; break;
+ case UPH_IS_USE_CHARACTER_VARIANTS : bReadOnly = rOpt.bROIsUseCharacterVariants; break;
+ case UPH_IS_TRANSLATE_COMMON_TERMS : bReadOnly = rOpt.bROIsTranslateCommonTerms; break;
+ case UPH_IS_REVERSE_MAPPING : bReadOnly = rOpt.bROIsReverseMapping; break;
+ case UPH_DATA_FILES_CHANGED_CHECK_VALUE : bReadOnly = rOpt.bRODataFilesChangedCheckValue; break;
+ case UPH_IS_GRAMMAR_AUTO: bReadOnly = rOpt.bROIsGrammarAuto; break;
+ case UPH_IS_GRAMMAR_INTERACTIVE: bReadOnly = rOpt.bROIsGrammarInteractive; break;
+ default :
+ SAL_WARN( "unotools.config", "unexpected property handle" );
+ }
+ return bReadOnly;
+}
+
+static SvtLinguConfigItem *pCfgItem = nullptr;
+static sal_Int32 nCfgItemRefCount = 0;
+
+constexpr OUString aG_Dictionaries = u"Dictionaries"_ustr;
+
+SvtLinguConfig::SvtLinguConfig()
+{
+ // Global access, must be guarded (multithreading)
+ std::unique_lock aGuard(theSvtLinguConfigItemMutex());
+ ++nCfgItemRefCount;
+}
+
+SvtLinguConfig::~SvtLinguConfig()
+{
+ if (pCfgItem && pCfgItem->IsModified())
+ pCfgItem->Commit();
+
+ std::unique_lock aGuard(theSvtLinguConfigItemMutex());
+
+ if (--nCfgItemRefCount <= 0)
+ {
+ delete pCfgItem;
+ pCfgItem = nullptr;
+ }
+}
+
+SvtLinguConfigItem & SvtLinguConfig::GetConfigItem()
+{
+ // Global access, must be guarded (multithreading)
+ std::unique_lock aGuard(theSvtLinguConfigItemMutex());
+ if (!pCfgItem)
+ {
+ pCfgItem = new SvtLinguConfigItem;
+ aGuard.unlock();
+ ItemHolder1::holdConfigItem(EItem::LinguConfig);
+ }
+ return *pCfgItem;
+}
+
+uno::Sequence< OUString > SvtLinguConfig::GetNodeNames( const OUString &rNode ) const
+{
+ return GetConfigItem().GetNodeNames( rNode );
+}
+
+uno::Sequence< uno::Any > SvtLinguConfig::GetProperties( const uno::Sequence< OUString > &rNames ) const
+{
+ return GetConfigItem().GetProperties(rNames);
+}
+
+bool SvtLinguConfig::ReplaceSetProperties(
+ const OUString &rNode, const uno::Sequence< beans::PropertyValue >& rValues )
+{
+ return GetConfigItem().ReplaceSetProperties( rNode, rValues );
+}
+
+uno::Any SvtLinguConfig::GetProperty( std::u16string_view rPropertyName ) const
+{
+ return GetConfigItem().GetProperty( rPropertyName );
+}
+
+uno::Any SvtLinguConfig::GetProperty( sal_Int32 nPropertyHandle ) const
+{
+ return GetConfigItem().GetProperty( nPropertyHandle );
+}
+
+bool SvtLinguConfig::SetProperty( std::u16string_view rPropertyName, const uno::Any &rValue )
+{
+ return GetConfigItem().SetProperty( rPropertyName, rValue );
+}
+
+bool SvtLinguConfig::SetProperty( sal_Int32 nPropertyHandle, const uno::Any &rValue )
+{
+ return GetConfigItem().SetProperty( nPropertyHandle, rValue );
+}
+
+void SvtLinguConfig::GetOptions( SvtLinguOptions &rOptions ) const
+{
+ GetConfigItem().GetOptions(rOptions);
+}
+
+bool SvtLinguConfig::IsReadOnly( std::u16string_view rPropertyName ) const
+{
+ return GetConfigItem().IsReadOnly( rPropertyName );
+}
+
+bool SvtLinguConfig::GetElementNamesFor(
+ const OUString &rNodeName,
+ uno::Sequence< OUString > &rElementNames ) const
+{
+ bool bSuccess = false;
+ try
+ {
+ uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName("ServiceManager"), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName( rNodeName ), uno::UNO_QUERY_THROW );
+ rElementNames = xNA->getElementNames();
+ bSuccess = true;
+ }
+ catch (uno::Exception &)
+ {
+ }
+ return bSuccess;
+}
+
+bool SvtLinguConfig::GetSupportedDictionaryFormatsFor(
+ const OUString &rSetName,
+ const OUString &rSetEntry,
+ uno::Sequence< OUString > &rFormatList ) const
+{
+ if (rSetName.isEmpty() || rSetEntry.isEmpty())
+ return false;
+ bool bSuccess = false;
+ try
+ {
+ uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName("ServiceManager"), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName( rSetName ), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName( rSetEntry ), uno::UNO_QUERY_THROW );
+ if (xNA->getByName( "SupportedDictionaryFormats" ) >>= rFormatList)
+ bSuccess = true;
+ DBG_ASSERT( rFormatList.hasElements(), "supported dictionary format list is empty" );
+ }
+ catch (uno::Exception &)
+ {
+ }
+ return bSuccess;
+}
+
+bool SvtLinguConfig::GetLocaleListFor( const OUString &rSetName, const OUString &rSetEntry, css::uno::Sequence< OUString > &rLocaleList ) const
+{
+ if (rSetName.isEmpty() || rSetEntry.isEmpty())
+ return false;
+ bool bSuccess = false;
+ try
+ {
+ uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName("ServiceManager"), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName( rSetName ), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName( rSetEntry ), uno::UNO_QUERY_THROW );
+ if (xNA->getByName( "Locales" ) >>= rLocaleList)
+ bSuccess = true;
+ DBG_ASSERT( rLocaleList.hasElements(), "Locale list is empty" );
+ }
+ catch (uno::Exception &)
+ {
+ }
+ return bSuccess;
+}
+
+static bool lcl_GetFileUrlFromOrigin(
+ OUString /*out*/ &rFileUrl,
+ const OUString &rOrigin )
+{
+ OUString aURL(
+ comphelper::getExpandedUri(
+ comphelper::getProcessComponentContext(), rOrigin));
+ if (aURL.startsWith( FILE_PROTOCOL ))
+ {
+ rFileUrl = aURL;
+ return true;
+ }
+ else
+ {
+ SAL_WARN(
+ "unotools.config", "not a file URL, <" << aURL << ">" );
+ return false;
+ }
+}
+
+bool SvtLinguConfig::GetDictionaryEntry(
+ const OUString &rNodeName,
+ SvtLinguConfigDictionaryEntry &rDicEntry ) const
+{
+ if (rNodeName.isEmpty())
+ return false;
+ bool bSuccess = false;
+ try
+ {
+ uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName("ServiceManager"), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName( aG_Dictionaries ), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName( rNodeName ), uno::UNO_QUERY_THROW );
+
+ // read group data...
+ uno::Sequence< OUString > aLocations;
+ OUString aFormatName;
+ uno::Sequence< OUString > aLocaleNames;
+ bSuccess = (xNA->getByName( "Locations" ) >>= aLocations) &&
+ (xNA->getByName( "Format" ) >>= aFormatName) &&
+ (xNA->getByName( "Locales" ) >>= aLocaleNames);
+ DBG_ASSERT( aLocations.hasElements(), "Dictionary locations not set" );
+ DBG_ASSERT( !aFormatName.isEmpty(), "Dictionary format name not set" );
+ DBG_ASSERT( aLocaleNames.hasElements(), "No locales set for the dictionary" );
+
+ // if successful continue
+ if (bSuccess)
+ {
+ // get file URL's for the locations
+ for (OUString& rLocation : asNonConstRange(aLocations))
+ {
+ if (!lcl_GetFileUrlFromOrigin( rLocation, rLocation ))
+ bSuccess = false;
+ }
+
+ // if everything was fine return the result
+ if (bSuccess)
+ {
+ rDicEntry.aLocations = aLocations;
+ rDicEntry.aFormatName = aFormatName;
+ rDicEntry.aLocaleNames = aLocaleNames;
+ }
+ }
+ }
+ catch (uno::Exception &)
+ {
+ }
+ return bSuccess;
+}
+
+uno::Sequence< OUString > SvtLinguConfig::GetDisabledDictionaries() const
+{
+ uno::Sequence< OUString > aResult;
+ try
+ {
+ uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName("ServiceManager"), uno::UNO_QUERY_THROW );
+ xNA->getByName( "DisabledDictionaries" ) >>= aResult;
+ }
+ catch (uno::Exception &)
+ {
+ }
+ return aResult;
+}
+
+std::vector< SvtLinguConfigDictionaryEntry > SvtLinguConfig::GetActiveDictionariesByFormat(
+ std::u16string_view rFormatName ) const
+{
+ std::vector< SvtLinguConfigDictionaryEntry > aRes;
+ if (rFormatName.empty())
+ return aRes;
+
+ try
+ {
+ uno::Sequence< OUString > aElementNames;
+ GetElementNamesFor( aG_Dictionaries, aElementNames );
+
+ const uno::Sequence< OUString > aDisabledDics( GetDisabledDictionaries() );
+
+ SvtLinguConfigDictionaryEntry aDicEntry;
+ for (const OUString& rElementName : std::as_const(aElementNames))
+ {
+ // does dictionary match the format we are looking for?
+ if (GetDictionaryEntry( rElementName, aDicEntry ) &&
+ aDicEntry.aFormatName == rFormatName)
+ {
+ // check if it is active or not
+ bool bDicIsActive = std::none_of(aDisabledDics.begin(), aDisabledDics.end(),
+ [&rElementName](const OUString& rDic) { return rDic == rElementName; });
+
+ if (bDicIsActive)
+ {
+ DBG_ASSERT( !aDicEntry.aFormatName.isEmpty(),
+ "FormatName not set" );
+ DBG_ASSERT( aDicEntry.aLocations.hasElements(),
+ "Locations not set" );
+ DBG_ASSERT( aDicEntry.aLocaleNames.hasElements(),
+ "Locales not set" );
+ aRes.push_back( aDicEntry );
+ }
+ }
+ }
+ }
+ catch (uno::Exception &)
+ {
+ }
+
+ return aRes;
+}
+
+uno::Reference< util::XChangesBatch > const & SvtLinguConfig::GetMainUpdateAccess() const
+{
+ if (!m_xMainUpdateAccess.is())
+ {
+ try
+ {
+ // get configuration provider
+ uno::Reference< uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+ uno::Reference< lang::XMultiServiceFactory > xConfigurationProvider =
+ configuration::theDefaultProvider::get( xContext );
+
+ // get configuration update access
+ beans::PropertyValue aValue;
+ aValue.Name = "nodepath";
+ aValue.Value <<= OUString("org.openoffice.Office.Linguistic");
+ uno::Sequence< uno::Any > aProps{ uno::Any(aValue) };
+ m_xMainUpdateAccess.set(
+ xConfigurationProvider->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationUpdateAccess", aProps),
+ uno::UNO_QUERY_THROW );
+ }
+ catch (uno::Exception &)
+ {
+ }
+ }
+
+ return m_xMainUpdateAccess;
+}
+
+OUString SvtLinguConfig::GetVendorImageUrl_Impl(
+ const OUString &rServiceImplName,
+ const OUString &rImageName ) const
+{
+ OUString aRes;
+ try
+ {
+ uno::Reference< container::XNameAccess > xImagesNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
+ xImagesNA.set( xImagesNA->getByName("Images"), uno::UNO_QUERY_THROW );
+
+ uno::Reference< container::XNameAccess > xNA( xImagesNA->getByName("ServiceNameEntries"), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName( rServiceImplName ), uno::UNO_QUERY_THROW );
+ uno::Any aAny(xNA->getByName("VendorImagesNode"));
+ OUString aVendorImagesNode;
+ if (aAny >>= aVendorImagesNode)
+ {
+ xNA = xImagesNA;
+ xNA.set( xNA->getByName("VendorImages"), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName( aVendorImagesNode ), uno::UNO_QUERY_THROW );
+ aAny = xNA->getByName( rImageName );
+ OUString aTmp;
+ if (aAny >>= aTmp)
+ {
+ if (lcl_GetFileUrlFromOrigin( aTmp, aTmp ))
+ aRes = aTmp;
+ }
+ }
+ }
+ catch (uno::Exception &)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools");
+ }
+ return aRes;
+}
+
+OUString SvtLinguConfig::GetSpellAndGrammarContextSuggestionImage(
+ const OUString &rServiceImplName
+) const
+{
+ OUString aRes;
+ if (!rServiceImplName.isEmpty())
+ {
+ aRes = GetVendorImageUrl_Impl( rServiceImplName, "SpellAndGrammarContextMenuSuggestionImage" );
+ }
+ return aRes;
+}
+
+OUString SvtLinguConfig::GetSpellAndGrammarContextDictionaryImage(
+ const OUString &rServiceImplName
+) const
+{
+ OUString aRes;
+ if (!rServiceImplName.isEmpty())
+ {
+ aRes = GetVendorImageUrl_Impl( rServiceImplName, "SpellAndGrammarContextMenuDictionaryImage" );
+ }
+ return aRes;
+}
+
+OUString SvtLinguConfig::GetSynonymsContextImage(
+ const OUString &rServiceImplName
+) const
+{
+ OUString aRes;
+ if (!rServiceImplName.isEmpty())
+ {
+ OUString aPath( GetVendorImageUrl_Impl( rServiceImplName, "SynonymsContextMenuImage" ) );
+ aRes = aPath;
+ }
+ return aRes;
+}
+
+bool SvtLinguConfig::HasGrammarChecker() const
+{
+ bool bRes = false;
+
+ try
+ {
+ uno::Reference< container::XNameAccess > xNA( GetMainUpdateAccess(), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName("ServiceManager"), uno::UNO_QUERY_THROW );
+ xNA.set( xNA->getByName("GrammarCheckerList"), uno::UNO_QUERY_THROW );
+
+ uno::Sequence< OUString > aElementNames( xNA->getElementNames() );
+ bRes = aElementNames.hasElements();
+ }
+ catch (const uno::Exception&)
+ {
+ }
+
+ return bRes;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/moduleoptions.cxx b/unotools/source/config/moduleoptions.cxx
new file mode 100644
index 0000000000..5918b749e6
--- /dev/null
+++ b/unotools/source/config/moduleoptions.cxx
@@ -0,0 +1,1117 @@
+/* -*- 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 <unotools/moduleoptions.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <unotools/configitem.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/enumarray.hxx>
+#include <o3tl/string_view.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/frame/XModel.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/document/XTypeDetection.hpp>
+#include <com/sun/star/util/PathSubstitution.hpp>
+#include <com/sun/star/util/XStringSubstitution.hpp>
+
+#include "itemholder1.hxx"
+
+/*-************************************************************************************************************
+ @descr These values are used to define necessary keys from our configuration management to support
+ all functionality of these implementation.
+ It's a fast way to make changes if some keys change its name or location!
+
+ Property handle are necessary to specify right position in return list of configuration
+ for asked values. We ask it with a list of properties to get its values. The returned list
+ has the same order like our given name list!
+ e.g.:
+ NAMELIST[ PROPERTYHANDLE_xxx ] => VALUELIST[ PROPERTYHANDLE_xxx ]
+*//*-*************************************************************************************************************/
+constexpr OUStringLiteral ROOTNODE_FACTORIES = u"Setup/Office/Factories";
+#define PATHSEPARATOR "/"
+
+// Attention: The property "ooSetupFactoryEmptyDocumentURL" is read from configuration but not used! There is
+// special code that uses hard coded strings to return them.
+#define PROPERTYNAME_SHORTNAME "ooSetupFactoryShortName"
+#define PROPERTYNAME_TEMPLATEFILE "ooSetupFactoryTemplateFile"
+#define PROPERTYNAME_WINDOWATTRIBUTES "ooSetupFactoryWindowAttributes"
+#define PROPERTYNAME_EMPTYDOCUMENTURL "ooSetupFactoryEmptyDocumentURL"
+#define PROPERTYNAME_DEFAULTFILTER "ooSetupFactoryDefaultFilter"
+#define PROPERTYNAME_ICON "ooSetupFactoryIcon"
+
+#define PROPERTYHANDLE_SHORTNAME 0
+#define PROPERTYHANDLE_TEMPLATEFILE 1
+#define PROPERTYHANDLE_WINDOWATTRIBUTES 2
+#define PROPERTYHANDLE_EMPTYDOCUMENTURL 3
+#define PROPERTYHANDLE_DEFAULTFILTER 4
+#define PROPERTYHANDLE_ICON 5
+
+#define PROPERTYCOUNT 6
+
+constexpr OUString FACTORYNAME_WRITER = u"com.sun.star.text.TextDocument"_ustr;
+constexpr OUString FACTORYNAME_WRITERWEB = u"com.sun.star.text.WebDocument"_ustr;
+constexpr OUString FACTORYNAME_WRITERGLOBAL = u"com.sun.star.text.GlobalDocument"_ustr;
+constexpr OUString FACTORYNAME_CALC = u"com.sun.star.sheet.SpreadsheetDocument"_ustr;
+constexpr OUString FACTORYNAME_DRAW = u"com.sun.star.drawing.DrawingDocument"_ustr;
+constexpr OUString FACTORYNAME_IMPRESS = u"com.sun.star.presentation.PresentationDocument"_ustr;
+constexpr OUString FACTORYNAME_MATH = u"com.sun.star.formula.FormulaProperties"_ustr;
+constexpr OUString FACTORYNAME_CHART = u"com.sun.star.chart2.ChartDocument"_ustr;
+constexpr OUString FACTORYNAME_DATABASE = u"com.sun.star.sdb.OfficeDatabaseDocument"_ustr;
+constexpr OUString FACTORYNAME_STARTMODULE = u"com.sun.star.frame.StartModule"_ustr;
+constexpr OUString FACTORYNAME_BASIC = u"com.sun.star.script.BasicIDE"_ustr;
+
+#define FACTORYCOUNT 11
+
+namespace {
+
+/*-************************************************************************************************************
+ @descr This struct hold information about one factory. We declare a complete array which can hold infos
+ for all well known factories. Values of enum "EFactory" (see header!) are directly used as index!
+ So we can support a fast access on this information.
+*//*-*************************************************************************************************************/
+struct FactoryInfo
+{
+ public:
+
+ // initialize empty struct
+ FactoryInfo()
+ {
+ free();
+ }
+
+ // easy way to reset struct member!
+ void free()
+ {
+ bInstalled = false;
+ sFactory.clear();
+ sTemplateFile.clear();
+ sDefaultFilter.clear();
+ nIcon = 0;
+ bChangedTemplateFile = false;
+ bChangedDefaultFilter = false;
+ bDefaultFilterReadonly = false;
+ }
+
+ // returns list of properties, which has changed only!
+ // We use given value of sNodeBase to build full qualified paths ...
+ // Last sign of it must be "/". because we use it directly, without any additional things!
+ css::uno::Sequence< css::beans::PropertyValue > getChangedProperties( std::u16string_view sNodeBase )
+ {
+ // a) reserve memory for max. count of changed properties
+ // b) add names and values of changed ones only and count it
+ // c) resize return list by using count
+ css::uno::Sequence< css::beans::PropertyValue > lProperties ( 4 );
+ auto plProperties = lProperties.getArray();
+ sal_Int8 nRealyChanged = 0;
+
+ if( bChangedTemplateFile )
+ {
+ plProperties[nRealyChanged].Name
+ = OUString::Concat(sNodeBase) + PROPERTYNAME_TEMPLATEFILE;
+
+ if ( !sTemplateFile.isEmpty() )
+ {
+ plProperties[nRealyChanged].Value
+ <<= getStringSubstitution()
+ ->reSubstituteVariables( sTemplateFile );
+ }
+ else
+ {
+ plProperties[nRealyChanged].Value <<= sTemplateFile;
+ }
+
+ ++nRealyChanged;
+ }
+ if( bChangedDefaultFilter )
+ {
+ plProperties[nRealyChanged].Name
+ = OUString::Concat(sNodeBase) + PROPERTYNAME_DEFAULTFILTER;
+ plProperties[nRealyChanged].Value <<= sDefaultFilter;
+ ++nRealyChanged;
+ }
+
+ // Don't forget to reset changed flags! Otherwise we save it again and again and ...
+ bChangedTemplateFile = false;
+ bChangedDefaultFilter = false;
+
+ lProperties.realloc( nRealyChanged );
+ return lProperties;
+ }
+
+ // We must support setting AND marking of changed values.
+ // That's why we can't make our member public. We must use get/set/init methods
+ // to control access on it!
+ bool getInstalled () const { return bInstalled; };
+ const OUString& getFactory () const { return sFactory; };
+ const OUString& getTemplateFile () const { return sTemplateFile; };
+ const OUString& getDefaultFilter () const { return sDefaultFilter; };
+ bool isDefaultFilterReadonly() const { return bDefaultFilterReadonly; }
+ sal_Int32 getIcon () const { return nIcon; };
+
+ // If you call set-methods - we check for changes of values and mark it.
+ // But if you wish to set it without that... you must initialize it!
+ void initInstalled () { bInstalled = true; }
+ void initFactory ( const OUString& sNewFactory ) { sFactory = sNewFactory; }
+ void initDefaultFilter ( const OUString& sNewDefaultFilter ) { sDefaultFilter = sNewDefaultFilter; }
+ void setDefaultFilterReadonly( const bool bVal){bDefaultFilterReadonly = bVal;}
+ void initIcon ( sal_Int32 nNewIcon ) { nIcon = nNewIcon; }
+
+ void initTemplateFile( const OUString& sNewTemplateFile )
+ {
+ if ( !sNewTemplateFile.isEmpty() )
+ {
+ sTemplateFile= getStringSubstitution()->substituteVariables( sNewTemplateFile, false );
+ }
+ else
+ {
+ sTemplateFile = sNewTemplateFile;
+ }
+ }
+
+ void setTemplateFile( const OUString& sNewTemplateFile )
+ {
+ if( sTemplateFile != sNewTemplateFile )
+ {
+ sTemplateFile = sNewTemplateFile;
+ bChangedTemplateFile = true;
+ }
+ };
+
+ void setDefaultFilter( const OUString& sNewDefaultFilter )
+ {
+ if( sDefaultFilter != sNewDefaultFilter )
+ {
+ sDefaultFilter = sNewDefaultFilter;
+ bChangedDefaultFilter = true;
+ }
+ };
+
+ private:
+ css::uno::Reference< css::util::XStringSubstitution > const & getStringSubstitution()
+ {
+ if ( !xSubstVars.is() )
+ {
+ xSubstVars.set( css::util::PathSubstitution::create(::comphelper::getProcessComponentContext()) );
+ }
+ return xSubstVars;
+ }
+
+ bool bInstalled;
+ OUString sFactory;
+ OUString sTemplateFile;
+ OUString sDefaultFilter;
+ sal_Int32 nIcon;
+
+ bool bChangedTemplateFile :1;
+ bool bChangedDefaultFilter :1;
+ bool bDefaultFilterReadonly :1;
+
+ css::uno::Reference< css::util::XStringSubstitution > xSubstVars;
+};
+
+}
+
+class SvtModuleOptions_Impl : public ::utl::ConfigItem
+{
+
+ // public methods
+
+ public:
+
+ // constructor / destructor
+
+ SvtModuleOptions_Impl();
+ virtual ~SvtModuleOptions_Impl() override;
+
+ // override methods of baseclass
+
+ virtual void Notify( const css::uno::Sequence< OUString >& lPropertyNames ) override;
+
+ // public interface
+
+ bool IsModuleInstalled ( SvtModuleOptions::EModule eModule ) const;
+ css::uno::Sequence < OUString > GetAllServiceNames();
+ OUString const & GetFactoryName ( SvtModuleOptions::EFactory eFactory ) const;
+ OUString const & GetFactoryStandardTemplate( SvtModuleOptions::EFactory eFactory ) const;
+ static OUString GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory eFactory );
+ OUString const & GetFactoryDefaultFilter ( SvtModuleOptions::EFactory eFactory ) const;
+ bool IsDefaultFilterReadonly( SvtModuleOptions::EFactory eFactory ) const;
+ sal_Int32 GetFactoryIcon ( SvtModuleOptions::EFactory eFactory ) const;
+ static bool ClassifyFactoryByName ( std::u16string_view sName ,
+ SvtModuleOptions::EFactory& eFactory );
+ void SetFactoryStandardTemplate( SvtModuleOptions::EFactory eFactory ,
+ const OUString& sTemplate );
+ void SetFactoryDefaultFilter ( SvtModuleOptions::EFactory eFactory ,
+ const OUString& sFilter );
+ void MakeReadonlyStatesAvailable();
+
+ // private methods
+
+ private:
+ static css::uno::Sequence< OUString > impl_ExpandSetNames ( const css::uno::Sequence< OUString >& lSetNames );
+ void impl_Read ( const css::uno::Sequence< OUString >& lSetNames );
+
+ virtual void ImplCommit() override;
+
+ // private member
+
+ private:
+ o3tl::enumarray<SvtModuleOptions::EFactory, FactoryInfo> m_lFactories;
+ bool m_bReadOnlyStatesWellKnown;
+};
+
+/*-************************************************************************************************************
+ @short default ctor
+ @descr We open our configuration here and read all necessary values from it.
+ These values are cached till everyone call Commit(). Then we write changed ones back to cfg.
+
+ @seealso baseclass ConfigItem
+ @seealso method impl_Read()
+ @threadsafe no
+*//*-*************************************************************************************************************/
+SvtModuleOptions_Impl::SvtModuleOptions_Impl()
+ : ::utl::ConfigItem( ROOTNODE_FACTORIES )
+ , m_bReadOnlyStatesWellKnown( false )
+{
+ // First initialize list of factory infos! Otherwise we couldn't guarantee right working of these class.
+ for( auto & rFactory : m_lFactories )
+ rFactory.free();
+
+ // Get name list of all existing set node names in configuration to read her properties in impl_Read().
+ // These list is a list of long names of our factories.
+ const css::uno::Sequence< OUString > lFactories = GetNodeNames( OUString() );
+ impl_Read( lFactories );
+
+ // Enable notification for changes by using configuration directly.
+ // So we can update our internal values immediately.
+ EnableNotification( lFactories );
+}
+
+SvtModuleOptions_Impl::~SvtModuleOptions_Impl()
+{
+ assert(!IsModified()); // should have been committed
+}
+
+/*-************************************************************************************************************
+ @short called for notify of configmanager
+ @descr This method is called from the ConfigManager before application ends or from the
+ PropertyChangeListener if the sub tree broadcasts changes. You must update our
+ internal values.
+
+ @attention We are registered for pure set node names only. So we can use our internal method "impl_Read()" to
+ update our info list. Because - this method expand given name list to full qualified property list
+ and use it to read the values. These values are filled into our internal member list m_lFactories
+ at right position.
+
+ @seealso method impl_Read()
+
+ @param "lNames" is the list of set node entries which should be updated.
+ @threadsafe no
+*//*-*************************************************************************************************************/
+void SvtModuleOptions_Impl::Notify( const css::uno::Sequence< OUString >& )
+{
+ OSL_FAIL( "SvtModuleOptions_Impl::Notify() Not implemented yet!" );
+}
+
+/*-****************************************************************************************************
+ @short write changes to configuration
+ @descr This method writes the changed values into the sub tree
+ and should always called in our destructor to guarantee consistency of config data.
+
+ @attention We clear complete set in configuration first and write it completely new! So we don't must
+ distinguish between existing, added or removed elements. Our internal cached values
+ are the only and right ones.
+
+ @seealso baseclass ConfigItem
+ @threadsafe no
+*//*-*****************************************************************************************************/
+void SvtModuleOptions_Impl::ImplCommit()
+{
+ // Reserve memory for ALL possible factory properties!
+ // Step over all factories and get her really changed values only.
+ // Build list of these ones and use it for commit.
+ css::uno::Sequence< css::beans::PropertyValue > lCommitProperties( FACTORYCOUNT*PROPERTYCOUNT );
+ sal_Int32 nRealCount = 0;
+ OUString sBasePath;
+ for( FactoryInfo & rInfo : m_lFactories )
+ {
+ // These path is used to build full qualified property names...
+ // See pInfo->getChangedProperties() for further information
+ sBasePath = PATHSEPARATOR + rInfo.getFactory() + PATHSEPARATOR;
+
+ const css::uno::Sequence< css::beans::PropertyValue > lChangedProperties = rInfo.getChangedProperties ( sBasePath );
+ std::copy(lChangedProperties.begin(), lChangedProperties.end(), std::next(lCommitProperties.getArray(), nRealCount));
+ nRealCount += lChangedProperties.getLength();
+ }
+ // Resize commit list to real size.
+ // If nothing to do - suppress calling of configuration...
+ // It could be too expensive :-)
+ if( nRealCount > 0 )
+ {
+ lCommitProperties.realloc( nRealCount );
+ SetSetProperties( OUString(), lCommitProperties );
+ }
+}
+
+/*-****************************************************************************************************
+ @short access method to get internal values
+ @descr These methods implement easy access to our internal values.
+ You give us right enum value to specify which module interest you ... we return right information.
+
+ @attention Some people use any value as enum ... but we support in header specified values only!
+ We use it directly as index in our internal list. If enum value isn't right - we crash with an
+ "index out of range"!!! Please use me right - otherwise there is no guarantee.
+ @param "eModule" , index in list - specify module
+ @return Queried information.
+
+ @onerror We return default values. (mostly "not installed"!)
+ @threadsafe no
+*//*-*****************************************************************************************************/
+bool SvtModuleOptions_Impl::IsModuleInstalled( SvtModuleOptions::EModule eModule ) const
+{
+ switch( eModule )
+ {
+ case SvtModuleOptions::EModule::WRITER:
+ return m_lFactories[SvtModuleOptions::EFactory::WRITER].getInstalled();
+ case SvtModuleOptions::EModule::WEB:
+ return m_lFactories[SvtModuleOptions::EFactory::WRITERWEB].getInstalled();
+ case SvtModuleOptions::EModule::GLOBAL:
+ return m_lFactories[SvtModuleOptions::EFactory::WRITERGLOBAL].getInstalled();
+ case SvtModuleOptions::EModule::CALC:
+ return m_lFactories[SvtModuleOptions::EFactory::CALC].getInstalled();
+ case SvtModuleOptions::EModule::DRAW:
+ return m_lFactories[SvtModuleOptions::EFactory::DRAW].getInstalled();
+ case SvtModuleOptions::EModule::IMPRESS:
+ return m_lFactories[SvtModuleOptions::EFactory::IMPRESS].getInstalled();
+ case SvtModuleOptions::EModule::MATH:
+ return m_lFactories[SvtModuleOptions::EFactory::MATH].getInstalled();
+ case SvtModuleOptions::EModule::CHART:
+ return m_lFactories[SvtModuleOptions::EFactory::CHART].getInstalled();
+ case SvtModuleOptions::EModule::STARTMODULE:
+ return m_lFactories[SvtModuleOptions::EFactory::STARTMODULE].getInstalled();
+ case SvtModuleOptions::EModule::BASIC:
+ return true; // Couldn't be deselected by setup yet!
+ case SvtModuleOptions::EModule::DATABASE:
+ return m_lFactories[SvtModuleOptions::EFactory::DATABASE].getInstalled();
+ }
+
+ return false;
+}
+
+css::uno::Sequence < OUString > SvtModuleOptions_Impl::GetAllServiceNames()
+{
+ std::vector<OUString> aVec;
+
+ for( const auto & rFactory : m_lFactories )
+ if( rFactory.getInstalled() )
+ aVec.push_back( rFactory.getFactory() );
+
+ return comphelper::containerToSequence(aVec);
+}
+
+OUString const & SvtModuleOptions_Impl::GetFactoryName( SvtModuleOptions::EFactory eFactory ) const
+{
+ return m_lFactories[eFactory].getFactory();
+}
+
+OUString SvtModuleOptions::GetFactoryShortName(SvtModuleOptions::EFactory eFactory)
+{
+ // Attention: Hard configured yet ... because it's not fine to make changes possible by xml file yet.
+ // But it's good to plan further possibilities!
+
+ //return m_lFactories[eFactory].sShortName;
+
+ OUString sShortName;
+ switch( eFactory )
+ {
+ case SvtModuleOptions::EFactory::WRITER : sShortName = "swriter";
+ break;
+ case SvtModuleOptions::EFactory::WRITERWEB: sShortName = "swriter/web";
+ break;
+ case SvtModuleOptions::EFactory::WRITERGLOBAL: sShortName = "swriter/GlobalDocument";
+ break;
+ case SvtModuleOptions::EFactory::CALC : sShortName = "scalc";
+ break;
+ case SvtModuleOptions::EFactory::DRAW : sShortName = "sdraw";
+ break;
+ case SvtModuleOptions::EFactory::IMPRESS : sShortName = "simpress";
+ break;
+ case SvtModuleOptions::EFactory::MATH : sShortName = "smath";
+ break;
+ case SvtModuleOptions::EFactory::CHART : sShortName = "schart";
+ break;
+ case SvtModuleOptions::EFactory::BASIC : sShortName = "sbasic";
+ break;
+ case SvtModuleOptions::EFactory::DATABASE : sShortName = "sdatabase";
+ break;
+ case SvtModuleOptions::EFactory::STARTMODULE : sShortName = "startmodule";
+ break;
+ default:
+ OSL_FAIL( "unknown factory" );
+ break;
+ }
+
+ return sShortName;
+}
+
+OUString const & SvtModuleOptions_Impl::GetFactoryStandardTemplate( SvtModuleOptions::EFactory eFactory ) const
+{
+ return m_lFactories[eFactory].getTemplateFile();
+}
+
+OUString SvtModuleOptions_Impl::GetFactoryEmptyDocumentURL( SvtModuleOptions::EFactory eFactory )
+{
+ // Attention: Hard configured yet ... because it's not fine to make changes possible by xml file yet.
+ // But it's good to plan further possibilities!
+
+ //return m_lFactories[eFactory].getEmptyDocumentURL();
+
+ OUString sURL;
+ switch( eFactory )
+ {
+ case SvtModuleOptions::EFactory::WRITER : sURL = "private:factory/swriter";
+ break;
+ case SvtModuleOptions::EFactory::WRITERWEB : sURL = "private:factory/swriter/web";
+ break;
+ case SvtModuleOptions::EFactory::WRITERGLOBAL : sURL = "private:factory/swriter/GlobalDocument";
+ break;
+ case SvtModuleOptions::EFactory::CALC : sURL = "private:factory/scalc";
+ break;
+ case SvtModuleOptions::EFactory::DRAW : sURL = "private:factory/sdraw";
+ break;
+ case SvtModuleOptions::EFactory::IMPRESS : sURL = "private:factory/simpress?slot=6686";
+ break;
+ case SvtModuleOptions::EFactory::MATH : sURL = "private:factory/smath";
+ break;
+ case SvtModuleOptions::EFactory::CHART : sURL = "private:factory/schart";
+ break;
+ case SvtModuleOptions::EFactory::BASIC : sURL = "private:factory/sbasic";
+ break;
+ case SvtModuleOptions::EFactory::DATABASE : sURL = "private:factory/sdatabase?Interactive";
+ break;
+ default:
+ OSL_FAIL( "unknown factory" );
+ break;
+ }
+ return sURL;
+}
+
+OUString const & SvtModuleOptions_Impl::GetFactoryDefaultFilter( SvtModuleOptions::EFactory eFactory ) const
+{
+ return m_lFactories[eFactory].getDefaultFilter();
+}
+
+bool SvtModuleOptions_Impl::IsDefaultFilterReadonly( SvtModuleOptions::EFactory eFactory ) const
+{
+ return m_lFactories[eFactory].isDefaultFilterReadonly();
+}
+
+sal_Int32 SvtModuleOptions_Impl::GetFactoryIcon( SvtModuleOptions::EFactory eFactory ) const
+{
+ return m_lFactories[eFactory].getIcon();
+}
+
+void SvtModuleOptions_Impl::SetFactoryStandardTemplate( SvtModuleOptions::EFactory eFactory ,
+ const OUString& sTemplate )
+{
+ m_lFactories[eFactory].setTemplateFile( sTemplate );
+ SetModified();
+}
+
+void SvtModuleOptions_Impl::SetFactoryDefaultFilter( SvtModuleOptions::EFactory eFactory,
+ const OUString& sFilter )
+{
+ m_lFactories[eFactory].setDefaultFilter( sFilter );
+ SetModified();
+}
+
+/*-************************************************************************************************************
+ @short return list of key names of our configuration management which represent our module tree
+ @descr You give use a list of current existing set node names .. and we expand it for all
+ well known properties which are necessary for this implementation.
+ These full expanded list should be used to get values of this properties.
+
+ @seealso ctor
+ @return List of all relative addressed properties of given set entry names.
+
+ @onerror List will be empty.
+ @threadsafe no
+*//*-*************************************************************************************************************/
+css::uno::Sequence< OUString > SvtModuleOptions_Impl::impl_ExpandSetNames( const css::uno::Sequence< OUString >& lSetNames )
+{
+ sal_Int32 nCount = lSetNames.getLength();
+ css::uno::Sequence< OUString > lPropNames ( nCount*PROPERTYCOUNT );
+ OUString* pPropNames = lPropNames.getArray();
+ sal_Int32 nPropStart = 0;
+
+ for( const auto& rSetName : lSetNames )
+ {
+ pPropNames[nPropStart+PROPERTYHANDLE_SHORTNAME ] = rSetName + PATHSEPARATOR PROPERTYNAME_SHORTNAME;
+ pPropNames[nPropStart+PROPERTYHANDLE_TEMPLATEFILE ] = rSetName + PATHSEPARATOR PROPERTYNAME_TEMPLATEFILE;
+ pPropNames[nPropStart+PROPERTYHANDLE_WINDOWATTRIBUTES] = rSetName + PATHSEPARATOR PROPERTYNAME_WINDOWATTRIBUTES;
+ pPropNames[nPropStart+PROPERTYHANDLE_EMPTYDOCUMENTURL] = rSetName + PATHSEPARATOR PROPERTYNAME_EMPTYDOCUMENTURL;
+ pPropNames[nPropStart+PROPERTYHANDLE_DEFAULTFILTER ] = rSetName + PATHSEPARATOR PROPERTYNAME_DEFAULTFILTER;
+ pPropNames[nPropStart+PROPERTYHANDLE_ICON ] = rSetName + PATHSEPARATOR PROPERTYNAME_ICON;
+ nPropStart += PROPERTYCOUNT;
+ }
+
+ return lPropNames;
+}
+
+/*-************************************************************************************************************
+ @short helper to classify given factory by name
+ @descr Every factory has its own long and short name. So we can match right enum value for internal using.
+
+ @attention We change in/out parameter "eFactory" in every case! But you should use it only, if return value is sal_True!
+ Algorithm: Set out-parameter to probably value ... and check the longname.
+ If it matches with these factory - break operation and return true AND right set parameter.
+ Otherwise try next one and so on. If no factory was found return false. Out parameter eFactory
+ is set to last tried value but shouldn't be used! Because our return value is false!
+ @param "sLongName" , long name of factory, which should be classified
+ @return "eFactory" , right enum value, which match given long name
+ and true for successfully classification, false otherwise
+
+ @onerror We return false.
+ @threadsafe no
+*//*-*************************************************************************************************************/
+bool SvtModuleOptions_Impl::ClassifyFactoryByName( std::u16string_view sName, SvtModuleOptions::EFactory& eFactory )
+{
+ bool bState;
+
+ eFactory = SvtModuleOptions::EFactory::WRITER;
+ bState = ( sName == FACTORYNAME_WRITER );
+
+ if( !bState )
+ {
+ eFactory = SvtModuleOptions::EFactory::WRITERWEB;
+ bState = ( sName == FACTORYNAME_WRITERWEB );
+ }
+ // no else!
+ if( !bState )
+ {
+ eFactory = SvtModuleOptions::EFactory::WRITERGLOBAL;
+ bState = ( sName == FACTORYNAME_WRITERGLOBAL );
+ }
+ // no else!
+ if( !bState )
+ {
+ eFactory = SvtModuleOptions::EFactory::CALC;
+ bState = ( sName == FACTORYNAME_CALC );
+ }
+ // no else!
+ if( !bState )
+ {
+ eFactory = SvtModuleOptions::EFactory::DRAW;
+ bState = ( sName == FACTORYNAME_DRAW );
+ }
+ // no else!
+ if( !bState )
+ {
+ eFactory = SvtModuleOptions::EFactory::IMPRESS;
+ bState = ( sName == FACTORYNAME_IMPRESS );
+ }
+ // no else!
+ if( !bState )
+ {
+ eFactory = SvtModuleOptions::EFactory::MATH;
+ bState = ( sName == FACTORYNAME_MATH );
+ }
+ // no else!
+ if( !bState )
+ {
+ eFactory = SvtModuleOptions::EFactory::CHART;
+ bState = ( sName == FACTORYNAME_CHART );
+ }
+ // no else!
+ if( !bState )
+ {
+ eFactory = SvtModuleOptions::EFactory::DATABASE;
+ bState = ( sName == FACTORYNAME_DATABASE );
+ }
+ // no else!
+ if( !bState )
+ {
+ eFactory = SvtModuleOptions::EFactory::STARTMODULE;
+ bState = ( sName == FACTORYNAME_STARTMODULE);
+ }
+ // no else!
+ if( !bState )
+ {
+ eFactory = SvtModuleOptions::EFactory::BASIC;
+ bState = ( sName == FACTORYNAME_BASIC);
+ }
+
+ return bState;
+}
+
+/*-************************************************************************************************************
+ @short read factory configuration
+ @descr Give us a list of pure factory names (long names!) which can be used as
+ direct set node names... and we read her property values and fill internal list.
+ These method can be used by initial reading at ctor and later updating by "Notify()".
+
+ @seealso ctor
+ @seealso method Notify()
+
+ @param "lFactories" is the list of set node entries which should be read.
+ @onerror We do nothing.
+ @threadsafe no
+*//*-*************************************************************************************************************/
+void SvtModuleOptions_Impl::impl_Read( const css::uno::Sequence< OUString >& lFactories )
+{
+ // Expand every set node name in lFactories to full qualified paths to its properties
+ // and get right values from configuration.
+ const css::uno::Sequence< OUString > lProperties = impl_ExpandSetNames( lFactories );
+ const css::uno::Sequence< css::uno::Any > lValues = GetProperties( lProperties );
+
+ // Safe impossible cases.
+ // We need values from ALL configuration keys.
+ // Follow assignment use order of values in relation to our list of key names!
+ OSL_ENSURE( !(lProperties.getLength()!=lValues.getLength()), "SvtModuleOptions_Impl::impl_Read()\nI miss some values of configuration keys!" );
+
+ // Algorithm: We step over all given factory names and classify it. These enum value can be used as direct index
+ // in our member list m_lFactories! VAriable nPropertyStart marks start position of every factory
+ // and her properties in expanded property/value list. The defines PROPERTHANDLE_xxx are used as offset values
+ // added to nPropertyStart. So we can address every property relative in these lists.
+ // If we found any valid values ... we reset all existing information for corresponding m_lFactories-entry and
+ // use a pointer to these struct in memory directly to set new values.
+ // But we set it only, if bInstalled is true. Otherwise all other values of a factory can be undeclared .. They
+ // shouldn't be used then.
+ // Attention: If a propertyset of a factory will be ignored we must step to next start position of next factory infos!
+ // see "nPropertyStart += PROPERTYCOUNT" ...
+
+ sal_Int32 nPropertyStart = 0;
+ FactoryInfo* pInfo = nullptr;
+ SvtModuleOptions::EFactory eFactory;
+
+ for( const OUString& sFactoryName : lFactories )
+ {
+ if( ClassifyFactoryByName( sFactoryName, eFactory ) )
+ {
+ OUString sTemp;
+ sal_Int32 nTemp = 0;
+
+ pInfo = &(m_lFactories[eFactory]);
+ pInfo->free();
+
+ pInfo->initInstalled();
+ pInfo->initFactory ( sFactoryName );
+
+ if (lValues[nPropertyStart+PROPERTYHANDLE_TEMPLATEFILE] >>= sTemp)
+ pInfo->initTemplateFile( sTemp );
+ if (lValues[nPropertyStart+PROPERTYHANDLE_DEFAULTFILTER ] >>= sTemp)
+ pInfo->initDefaultFilter( sTemp );
+ if (lValues[nPropertyStart+PROPERTYHANDLE_ICON] >>= nTemp)
+ pInfo->initIcon( nTemp );
+ }
+ nPropertyStart += PROPERTYCOUNT;
+ }
+}
+
+void SvtModuleOptions_Impl::MakeReadonlyStatesAvailable()
+{
+ if (m_bReadOnlyStatesWellKnown)
+ return;
+
+ css::uno::Sequence< OUString > lFactories = GetNodeNames(OUString());
+ for (OUString& rFactory : asNonConstRange(lFactories))
+ rFactory += PATHSEPARATOR PROPERTYNAME_DEFAULTFILTER;
+
+ css::uno::Sequence< sal_Bool > lReadonlyStates = GetReadOnlyStates(lFactories);
+ sal_Int32 c = lFactories.getLength();
+ for (sal_Int32 i=0; i<c; ++i)
+ {
+ const OUString& rFactoryName = std::as_const(lFactories)[i];
+ SvtModuleOptions::EFactory eFactory;
+
+ if (!ClassifyFactoryByName(rFactoryName, eFactory))
+ continue;
+
+ FactoryInfo& rInfo = m_lFactories[eFactory];
+ rInfo.setDefaultFilterReadonly(lReadonlyStates[i]);
+ }
+
+ m_bReadOnlyStatesWellKnown = true;
+}
+
+namespace {
+ //global
+ std::weak_ptr<SvtModuleOptions_Impl> g_pModuleOptions;
+
+std::mutex& impl_GetOwnStaticMutex()
+{
+ static std::mutex s_Mutex;
+ return s_Mutex;
+}
+}
+
+/*-************************************************************************************************************
+ @short standard constructor and destructor
+ @descr This will initialize an instance with default values. We initialize/deinitialize our static data
+ container and create a static mutex, which is used for threadsafe code in further time of this object.
+
+ @seealso method impl_GetOwnStaticMutex()
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+SvtModuleOptions::SvtModuleOptions()
+{
+ // no need to take the mutex yet, shared_ptr/weak_ptr are thread-safe
+ m_pImpl = g_pModuleOptions.lock();
+ if( m_pImpl )
+ return;
+
+ // take the mutex, so we don't accidentally create more than one
+ std::unique_lock aGuard( impl_GetOwnStaticMutex() );
+
+ m_pImpl = g_pModuleOptions.lock();
+ if( !m_pImpl )
+ {
+ m_pImpl = std::make_shared<SvtModuleOptions_Impl>();
+ g_pModuleOptions = m_pImpl;
+ aGuard.unlock(); // because holdConfigItem will call this constructor
+ ItemHolder1::holdConfigItem(EItem::ModuleOptions);
+ }
+}
+
+SvtModuleOptions::~SvtModuleOptions()
+{
+ m_pImpl.reset();
+}
+
+/*-************************************************************************************************************
+ @short access to configuration data
+ @descr This methods allow read/write access to configuration values.
+ They are threadsafe. All calls are forwarded to impl-data-container. See there for further information!
+
+ @seealso method impl_GetOwnStaticMutex()
+ @threadsafe yes
+*//*-*************************************************************************************************************/
+bool SvtModuleOptions::IsModuleInstalled( EModule eModule ) const
+{
+ // doesn't need mutex, never modified
+ return m_pImpl->IsModuleInstalled( eModule );
+}
+
+const OUString & SvtModuleOptions::GetFactoryName( EFactory eFactory ) const
+{
+ // doesn't need mutex, never modified
+ return m_pImpl->GetFactoryName( eFactory );
+}
+
+OUString SvtModuleOptions::GetFactoryStandardTemplate( EFactory eFactory ) const
+{
+ std::unique_lock aGuard( impl_GetOwnStaticMutex() );
+ return m_pImpl->GetFactoryStandardTemplate( eFactory );
+}
+
+OUString SvtModuleOptions::GetFactoryEmptyDocumentURL( EFactory eFactory ) const
+{
+ std::unique_lock aGuard( impl_GetOwnStaticMutex() );
+ return SvtModuleOptions_Impl::GetFactoryEmptyDocumentURL( eFactory );
+}
+
+OUString SvtModuleOptions::GetFactoryDefaultFilter( EFactory eFactory ) const
+{
+ std::unique_lock aGuard( impl_GetOwnStaticMutex() );
+ return m_pImpl->GetFactoryDefaultFilter( eFactory );
+}
+
+bool SvtModuleOptions::IsDefaultFilterReadonly( EFactory eFactory ) const
+{
+ std::unique_lock aGuard( impl_GetOwnStaticMutex() );
+ m_pImpl->MakeReadonlyStatesAvailable();
+ return m_pImpl->IsDefaultFilterReadonly( eFactory );
+}
+
+sal_Int32 SvtModuleOptions::GetFactoryIcon( EFactory eFactory ) const
+{
+ std::unique_lock aGuard( impl_GetOwnStaticMutex() );
+ return m_pImpl->GetFactoryIcon( eFactory );
+}
+
+bool SvtModuleOptions::ClassifyFactoryByName( std::u16string_view sName ,
+ EFactory& eFactory )
+{
+ // We don't need any mutex here ... because we don't use any member here!
+ return SvtModuleOptions_Impl::ClassifyFactoryByName( sName, eFactory );
+}
+
+void SvtModuleOptions::SetFactoryStandardTemplate( EFactory eFactory ,
+ const OUString& sTemplate )
+{
+ std::unique_lock aGuard( impl_GetOwnStaticMutex() );
+ m_pImpl->SetFactoryStandardTemplate( eFactory, sTemplate );
+}
+
+void SvtModuleOptions::SetFactoryDefaultFilter( EFactory eFactory,
+ const OUString& sFilter )
+{
+ std::unique_lock aGuard( impl_GetOwnStaticMutex() );
+ m_pImpl->SetFactoryDefaultFilter( eFactory, sFilter );
+}
+
+bool SvtModuleOptions::IsMath() const
+{
+ // doesn't need mutex, never modified
+ return m_pImpl->IsModuleInstalled( EModule::MATH );
+}
+
+bool SvtModuleOptions::IsChart() const
+{
+ // doesn't need mutex, never modified
+ return m_pImpl->IsModuleInstalled( EModule::CHART );
+}
+
+bool SvtModuleOptions::IsCalc() const
+{
+ // doesn't need mutex, never modified
+ return m_pImpl->IsModuleInstalled( EModule::CALC );
+}
+
+bool SvtModuleOptions::IsDraw() const
+{
+ // doesn't need mutex, never modified
+ return m_pImpl->IsModuleInstalled( EModule::DRAW );
+}
+
+bool SvtModuleOptions::IsWriter() const
+{
+ std::unique_lock aGuard( impl_GetOwnStaticMutex() );
+ return m_pImpl->IsModuleInstalled( EModule::WRITER );
+}
+
+bool SvtModuleOptions::IsImpress() const
+{
+ // doesn't need mutex, never modified
+ return m_pImpl->IsModuleInstalled( EModule::IMPRESS );
+}
+
+bool SvtModuleOptions::IsDataBase() const
+{
+ // doesn't need mutex, never modified
+ return m_pImpl->IsModuleInstalled( EModule::DATABASE );
+}
+
+OUString SvtModuleOptions::GetModuleName( EModule eModule ) const
+{
+ switch( eModule )
+ {
+ case SvtModuleOptions::EModule::WRITER : { return "Writer"; }
+ case SvtModuleOptions::EModule::WEB : { return "Web"; }
+ case SvtModuleOptions::EModule::GLOBAL : { return "Global"; }
+ case SvtModuleOptions::EModule::CALC : { return "Calc"; }
+ case SvtModuleOptions::EModule::DRAW : { return "Draw"; }
+ case SvtModuleOptions::EModule::IMPRESS : { return "Impress"; }
+ case SvtModuleOptions::EModule::MATH : { return "Math"; }
+ case SvtModuleOptions::EModule::CHART : { return "Chart"; }
+ case SvtModuleOptions::EModule::BASIC : { return "Basic"; }
+ case SvtModuleOptions::EModule::DATABASE : { return "Database"; }
+ default:
+ OSL_FAIL( "unknown module" );
+ break;
+ }
+
+ return OUString();
+}
+
+SvtModuleOptions::EFactory SvtModuleOptions::ClassifyFactoryByShortName(std::u16string_view sName)
+{
+ if ( sName == u"swriter" )
+ return EFactory::WRITER;
+ if (o3tl::equalsIgnoreAsciiCase(sName, u"swriter/Web")) // sometimes they are registered for swriter/web :-(
+ return EFactory::WRITERWEB;
+ if (o3tl::equalsIgnoreAsciiCase(sName, u"swriter/GlobalDocument")) // sometimes they are registered for swriter/globaldocument :-(
+ return EFactory::WRITERGLOBAL;
+ if ( sName == u"scalc" )
+ return EFactory::CALC;
+ if ( sName == u"sdraw" )
+ return EFactory::DRAW;
+ if ( sName == u"simpress" )
+ return EFactory::IMPRESS;
+ if ( sName == u"schart" )
+ return EFactory::CHART;
+ if ( sName == u"smath" )
+ return EFactory::MATH;
+ if ( sName == u"sbasic" )
+ return EFactory::BASIC;
+ if ( sName == u"sdatabase" )
+ return EFactory::DATABASE;
+
+ return EFactory::UNKNOWN_FACTORY;
+}
+
+SvtModuleOptions::EFactory SvtModuleOptions::ClassifyFactoryByServiceName(std::u16string_view sName)
+{
+ if (sName == FACTORYNAME_WRITERGLOBAL)
+ return EFactory::WRITERGLOBAL;
+ if (sName == FACTORYNAME_WRITERWEB)
+ return EFactory::WRITERWEB;
+ if (sName == FACTORYNAME_WRITER)
+ return EFactory::WRITER;
+ if (sName == FACTORYNAME_CALC)
+ return EFactory::CALC;
+ if (sName == FACTORYNAME_DRAW)
+ return EFactory::DRAW;
+ if (sName == FACTORYNAME_IMPRESS)
+ return EFactory::IMPRESS;
+ if (sName == FACTORYNAME_MATH)
+ return EFactory::MATH;
+ if (sName == FACTORYNAME_CHART)
+ return EFactory::CHART;
+ if (sName == FACTORYNAME_DATABASE)
+ return EFactory::DATABASE;
+ if (sName == FACTORYNAME_STARTMODULE)
+ return EFactory::STARTMODULE;
+ if (sName == FACTORYNAME_BASIC)
+ return EFactory::BASIC;
+
+ return EFactory::UNKNOWN_FACTORY;
+}
+
+SvtModuleOptions::EFactory SvtModuleOptions::ClassifyFactoryByURL(const OUString& sURL ,
+ const css::uno::Sequence< css::beans::PropertyValue >& lMediaDescriptor)
+{
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+
+ css::uno::Reference< css::container::XNameAccess > xFilterCfg;
+ css::uno::Reference< css::container::XNameAccess > xTypeCfg;
+ try
+ {
+ xFilterCfg.set(
+ xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.FilterFactory", xContext), css::uno::UNO_QUERY);
+ xTypeCfg.set(
+ xContext->getServiceManager()->createInstanceWithContext("com.sun.star.document.TypeDetection", xContext), css::uno::UNO_QUERY);
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { return EFactory::UNKNOWN_FACTORY; }
+
+ ::comphelper::SequenceAsHashMap stlDesc(lMediaDescriptor);
+
+ // is there already a filter inside the descriptor?
+ OUString sFilterName = stlDesc.getUnpackedValueOrDefault("FilterName", OUString());
+ if (!sFilterName.isEmpty())
+ {
+ try
+ {
+ ::comphelper::SequenceAsHashMap stlFilterProps (xFilterCfg->getByName(sFilterName));
+ OUString sDocumentService = stlFilterProps.getUnpackedValueOrDefault("DocumentService", OUString());
+ SvtModuleOptions::EFactory eApp = SvtModuleOptions::ClassifyFactoryByServiceName(sDocumentService);
+
+ if (eApp != EFactory::UNKNOWN_FACTORY)
+ return eApp;
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { /* do nothing here ... may the following code can help!*/ }
+ }
+
+ // is there already a type inside the descriptor?
+ OUString sTypeName = stlDesc.getUnpackedValueOrDefault("TypeName", OUString());
+ if (sTypeName.isEmpty())
+ {
+ // no :-(
+ // start flat detection of URL
+ css::uno::Reference< css::document::XTypeDetection > xDetect(xTypeCfg, css::uno::UNO_QUERY);
+ sTypeName = xDetect->queryTypeByURL(sURL);
+ }
+
+ if (sTypeName.isEmpty())
+ return EFactory::UNKNOWN_FACTORY;
+
+ // yes - there is a type info
+ // Try to find the preferred filter.
+ try
+ {
+ ::comphelper::SequenceAsHashMap stlTypeProps (xTypeCfg->getByName(sTypeName));
+ OUString sPreferredFilter = stlTypeProps.getUnpackedValueOrDefault("PreferredFilter", OUString());
+ ::comphelper::SequenceAsHashMap stlFilterProps (xFilterCfg->getByName(sPreferredFilter));
+ OUString sDocumentService = stlFilterProps.getUnpackedValueOrDefault("DocumentService", OUString());
+ SvtModuleOptions::EFactory eApp = SvtModuleOptions::ClassifyFactoryByServiceName(sDocumentService);
+
+ if (eApp != EFactory::UNKNOWN_FACTORY)
+ return eApp;
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { /* do nothing here ... may the following code can help!*/ }
+
+ // no filter/no type/no detection result => no fun :-)
+ return EFactory::UNKNOWN_FACTORY;
+}
+
+SvtModuleOptions::EFactory SvtModuleOptions::ClassifyFactoryByModel(const css::uno::Reference< css::frame::XModel >& xModel)
+{
+ css::uno::Reference< css::lang::XServiceInfo > xInfo(xModel, css::uno::UNO_QUERY);
+ if (!xInfo.is())
+ return EFactory::UNKNOWN_FACTORY;
+
+ const css::uno::Sequence< OUString > lServices = xInfo->getSupportedServiceNames();
+
+ for (const OUString& rService : lServices)
+ {
+ SvtModuleOptions::EFactory eApp = SvtModuleOptions::ClassifyFactoryByServiceName(rService);
+ if (eApp != EFactory::UNKNOWN_FACTORY)
+ return eApp;
+ }
+
+ return EFactory::UNKNOWN_FACTORY;
+}
+
+css::uno::Sequence < OUString > SvtModuleOptions::GetAllServiceNames()
+{
+ std::unique_lock aGuard( impl_GetOwnStaticMutex() );
+ return m_pImpl->GetAllServiceNames();
+}
+
+OUString SvtModuleOptions::GetDefaultModuleName() const
+{
+ OUString aModule;
+ if (m_pImpl->IsModuleInstalled(SvtModuleOptions::EModule::WRITER))
+ aModule = GetFactoryShortName(SvtModuleOptions::EFactory::WRITER);
+ else if (m_pImpl->IsModuleInstalled(SvtModuleOptions::EModule::CALC))
+ aModule = GetFactoryShortName(SvtModuleOptions::EFactory::CALC);
+ else if (m_pImpl->IsModuleInstalled(SvtModuleOptions::EModule::IMPRESS))
+ aModule = GetFactoryShortName(SvtModuleOptions::EFactory::IMPRESS);
+ else if (m_pImpl->IsModuleInstalled(SvtModuleOptions::EModule::DATABASE))
+ aModule = GetFactoryShortName(SvtModuleOptions::EFactory::DATABASE);
+ else if (m_pImpl->IsModuleInstalled(SvtModuleOptions::EModule::DRAW))
+ aModule = GetFactoryShortName(SvtModuleOptions::EFactory::DRAW);
+ else if (m_pImpl->IsModuleInstalled(SvtModuleOptions::EModule::WEB))
+ aModule = GetFactoryShortName(SvtModuleOptions::EFactory::WRITERWEB);
+ else if (m_pImpl->IsModuleInstalled(SvtModuleOptions::EModule::GLOBAL))
+ aModule = GetFactoryShortName(SvtModuleOptions::EFactory::WRITERGLOBAL);
+ else if (m_pImpl->IsModuleInstalled(SvtModuleOptions::EModule::MATH))
+ aModule = GetFactoryShortName(SvtModuleOptions::EFactory::MATH);
+ return aModule;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/options.cxx b/unotools/source/config/options.cxx
new file mode 100644
index 0000000000..4da44fa274
--- /dev/null
+++ b/unotools/source/config/options.cxx
@@ -0,0 +1,114 @@
+/* -*- 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 <unotools/options.hxx>
+
+#include <algorithm>
+
+using utl::detail::Options;
+using utl::ConfigurationBroadcaster;
+
+utl::ConfigurationListener::~ConfigurationListener() {}
+
+ConfigurationBroadcaster::ConfigurationBroadcaster()
+: m_nBroadcastBlocked( 0 )
+, m_nBlockedHint( ConfigurationHints::NONE )
+{
+}
+
+ConfigurationBroadcaster::ConfigurationBroadcaster(ConfigurationBroadcaster const & rSource)
+: mpList( rSource.mpList ? new IMPL_ConfigurationListenerList(*rSource.mpList) : nullptr )
+, m_nBroadcastBlocked( rSource.m_nBroadcastBlocked )
+, m_nBlockedHint( rSource.m_nBlockedHint )
+{
+}
+
+ConfigurationBroadcaster::~ConfigurationBroadcaster()
+{
+}
+
+ConfigurationBroadcaster & ConfigurationBroadcaster::operator =(
+ ConfigurationBroadcaster const & other)
+{
+ if (&other != this) {
+ mpList.reset(
+ other.mpList == nullptr ? nullptr : new IMPL_ConfigurationListenerList(*other.mpList));
+ m_nBroadcastBlocked = other.m_nBroadcastBlocked;
+ m_nBlockedHint = other.m_nBlockedHint;
+ }
+ return *this;
+}
+
+void ConfigurationBroadcaster::AddListener( utl::ConfigurationListener* pListener )
+{
+ if ( !mpList )
+ mpList.reset(new IMPL_ConfigurationListenerList);
+ mpList->push_back( pListener );
+}
+
+void ConfigurationBroadcaster::RemoveListener( utl::ConfigurationListener const * pListener )
+{
+ if ( mpList ) {
+ auto it = std::find(mpList->begin(), mpList->end(), pListener);
+ if ( it != mpList->end() )
+ mpList->erase( it );
+ }
+}
+
+void ConfigurationBroadcaster::NotifyListeners( ConfigurationHints nHint )
+{
+ if ( m_nBroadcastBlocked )
+ m_nBlockedHint |= nHint;
+ else
+ {
+ nHint |= m_nBlockedHint;
+ m_nBlockedHint = ConfigurationHints::NONE;
+ if ( mpList ) {
+ for ( size_t n = 0; n < mpList->size(); n++ )
+ (*mpList)[ n ]->ConfigurationChanged( this, nHint );
+ }
+ }
+}
+
+void ConfigurationBroadcaster::BlockBroadcasts( bool bBlock )
+{
+ if ( bBlock )
+ ++m_nBroadcastBlocked;
+ else if ( m_nBroadcastBlocked )
+ {
+ if ( --m_nBroadcastBlocked == 0 )
+ NotifyListeners( ConfigurationHints::NONE );
+ }
+}
+
+Options::Options()
+{
+}
+
+Options::~Options()
+{
+}
+
+void Options::ConfigurationChanged( ConfigurationBroadcaster*, ConfigurationHints nHint )
+{
+ NotifyListeners( nHint );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/optionsdlg.cxx b/unotools/source/config/optionsdlg.cxx
new file mode 100644
index 0000000000..c88bd09510
--- /dev/null
+++ b/unotools/source/config/optionsdlg.cxx
@@ -0,0 +1,152 @@
+/* -*- 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/optionsdlg.hxx>
+#include <unotools/configitem.hxx>
+#include <unotools/configmgr.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <cassert>
+
+using namespace com::sun::star::beans;
+using namespace com::sun::star::uno;
+
+constexpr OUString ROOT_NODE = u"OptionsDialogGroups"_ustr;
+constexpr OUString PAGES_NODE = u"Pages"_ustr;
+constexpr OUString OPTIONS_NODE = u"Options"_ustr;
+
+namespace {
+ enum NodeType{ NT_Group, NT_Page, NT_Option };
+}
+constexpr OUString g_sPathDelimiter = u"/"_ustr;
+static void ReadNode(
+ const Reference<css::container::XHierarchicalNameAccess>& xHierarchyAccess,
+ SvtOptionsDialogOptions::OptionNodeList & aOptionNodeList,
+ std::u16string_view _rNode, NodeType _eType );
+
+
+SvtOptionsDialogOptions::SvtOptionsDialogOptions()
+{
+ Reference<css::container::XHierarchicalNameAccess> xHierarchyAccess = utl::ConfigManager::acquireTree(u"Office.OptionsDialog");
+ const Sequence< OUString > aNodeSeq = utl::ConfigItem::GetNodeNames( xHierarchyAccess, ROOT_NODE, utl::ConfigNameFormat::LocalPath);
+ OUString sNode( ROOT_NODE + g_sPathDelimiter );
+ for ( const auto& rNode : aNodeSeq )
+ {
+ OUString sSubNode( sNode + rNode );
+ ReadNode( xHierarchyAccess, m_aOptionNodeList, sSubNode, NT_Group );
+ }
+}
+
+
+static void ReadNode(
+ const Reference<css::container::XHierarchicalNameAccess>& xHierarchyAccess,
+ SvtOptionsDialogOptions::OptionNodeList & aOptionNodeList,
+ std::u16string_view _rNode, NodeType _eType )
+{
+ OUString sNode( _rNode + g_sPathDelimiter );
+ OUString sSet;
+ sal_Int32 nLen = 0;
+ switch ( _eType )
+ {
+ case NT_Group :
+ {
+ sSet = PAGES_NODE;
+ nLen = 2;
+ break;
+ }
+
+ case NT_Page :
+ {
+ sSet = OPTIONS_NODE;
+ nLen = 2;
+ break;
+ }
+
+ case NT_Option :
+ {
+ nLen = 1;
+ break;
+ }
+ }
+
+ assert(nLen > 0);
+
+ Sequence< OUString > lResult( nLen );
+ auto plResult = lResult.getArray();
+ plResult[0] = sNode + "Hide";
+ if ( _eType != NT_Option )
+ plResult[1] = sNode + sSet;
+
+ Sequence< Any > aValues = utl::ConfigItem::GetProperties( xHierarchyAccess, lResult, /*bAllLocales*/false );
+ bool bHide = false;
+ if ( aValues[0] >>= bHide )
+ aOptionNodeList.emplace( sNode, bHide );
+
+ if ( _eType != NT_Option )
+ {
+ OUString sNodes( sNode + sSet );
+ const Sequence< OUString > aNodes = utl::ConfigItem::GetNodeNames( xHierarchyAccess, sNodes, utl::ConfigNameFormat::LocalPath );
+ for ( const auto& rNode : aNodes )
+ {
+ OUString sSubNodeName( sNodes + g_sPathDelimiter + rNode );
+ ReadNode( xHierarchyAccess, aOptionNodeList, sSubNodeName, _eType == NT_Group ? NT_Page : NT_Option );
+ }
+ }
+}
+
+static OUString getGroupPath( std::u16string_view _rGroup )
+{
+ return OUString( OUString::Concat(ROOT_NODE) + "/" + _rGroup + "/" );
+}
+static OUString getPagePath( std::u16string_view _rPage )
+{
+ return OUString( OUString::Concat(PAGES_NODE) + "/" + _rPage + "/" );
+}
+static OUString getOptionPath( std::u16string_view _rOption )
+{
+ return OUString( OUString::Concat(OPTIONS_NODE) + "/" + _rOption + "/" );
+}
+
+bool SvtOptionsDialogOptions::IsHidden( const OUString& _rPath ) const
+{
+ bool bRet = false;
+ OptionNodeList::const_iterator pIter = m_aOptionNodeList.find( _rPath );
+ if ( pIter != m_aOptionNodeList.end() )
+ bRet = pIter->second;
+ return bRet;
+}
+
+bool SvtOptionsDialogOptions::IsGroupHidden( std::u16string_view _rGroup ) const
+{
+ return IsHidden( getGroupPath( _rGroup ) );
+}
+
+bool SvtOptionsDialogOptions::IsPageHidden( std::u16string_view _rPage, std::u16string_view _rGroup ) const
+{
+ return IsHidden( getGroupPath( _rGroup ) + getPagePath( _rPage ) );
+}
+
+bool SvtOptionsDialogOptions::IsOptionHidden(
+ std::u16string_view _rOption, std::u16string_view _rPage, std::u16string_view _rGroup ) const
+{
+ return IsHidden( getGroupPath( _rGroup ) + getPagePath( _rPage ) + getOptionPath( _rOption ) );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/pathoptions.cxx b/unotools/source/config/pathoptions.cxx
new file mode 100644
index 0000000000..24c1fb323d
--- /dev/null
+++ b/unotools/source/config/pathoptions.cxx
@@ -0,0 +1,737 @@
+/* -*- 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 <sal/log.hxx>
+#include <unotools/pathoptions.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/urlobj.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <osl/mutex.hxx>
+#include <osl/file.hxx>
+
+#include <unotools/ucbhelper.hxx>
+#include <comphelper/getexpandeduri.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/util/thePathSettings.hpp>
+#include <com/sun/star/util/PathSubstitution.hpp>
+#include <com/sun/star/util/XStringSubstitution.hpp>
+#include <com/sun/star/util/theMacroExpander.hpp>
+#include <o3tl/enumarray.hxx>
+#include <o3tl/string_view.hxx>
+
+#include "itemholder1.hxx"
+
+#include <set>
+#include <unordered_map>
+
+using namespace osl;
+using namespace utl;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::util;
+using namespace com::sun::star::lang;
+
+#define SEARCHPATH_DELIMITER ';'
+#define SIGN_STARTVARIABLE "$("
+#define SIGN_ENDVARIABLE ")"
+
+// Supported variables by the old SvtPathOptions implementation
+#define SUBSTITUTE_INSTPATH "$(instpath)"
+#define SUBSTITUTE_PROGPATH "$(progpath)"
+#define SUBSTITUTE_USERPATH "$(userpath)"
+#define SUBSTITUTE_PATH "$(path)"
+
+#define STRPOS_NOTFOUND -1
+
+typedef std::unordered_map<OUString, sal_Int32> NameToHandleMap;
+
+typedef std::set<OUString> VarNameSet;
+
+// class SvtPathOptions_Impl ---------------------------------------------
+class SvtPathOptions_Impl
+{
+ private:
+ // Local variables to return const references
+ o3tl::enumarray< SvtPathOptions::Paths, OUString > m_aPathArray;
+ Reference< XFastPropertySet > m_xPathSettings;
+ Reference< XStringSubstitution > m_xSubstVariables;
+ Reference< XMacroExpander > m_xMacroExpander;
+ mutable std::unordered_map<SvtPathOptions::Paths, sal_Int32>
+ m_aMapEnumToPropHandle;
+ VarNameSet m_aSystemPathVarNames;
+
+ OUString m_aEmptyString;
+ mutable std::mutex m_aMutex;
+
+ public:
+ SvtPathOptions_Impl();
+
+ // get the paths, not const because of using a mutex
+ const OUString& GetPath( SvtPathOptions::Paths );
+ const OUString& GetAddinPath() { return GetPath( SvtPathOptions::Paths::AddIn ); }
+ const OUString& GetAutoCorrectPath() { return GetPath( SvtPathOptions::Paths::AutoCorrect ); }
+ const OUString& GetAutoTextPath() { return GetPath( SvtPathOptions::Paths::AutoText ); }
+ const OUString& GetBackupPath() { return GetPath( SvtPathOptions::Paths::Backup ); }
+ const OUString& GetBasicPath() { return GetPath( SvtPathOptions::Paths::Basic ); }
+ const OUString& GetBitmapPath() { return GetPath( SvtPathOptions::Paths::Bitmap ); }
+ const OUString& GetConfigPath() { return GetPath( SvtPathOptions::Paths::Config ); }
+ const OUString& GetDictionaryPath() { return GetPath( SvtPathOptions::Paths::Dictionary ); }
+ const OUString& GetFavoritesPath() { return GetPath( SvtPathOptions::Paths::Favorites ); }
+ const OUString& GetFilterPath() { return GetPath( SvtPathOptions::Paths::Filter ); }
+ const OUString& GetGalleryPath() { return GetPath( SvtPathOptions::Paths::Gallery ); }
+ const OUString& GetGraphicPath() { return GetPath( SvtPathOptions::Paths::Graphic ); }
+ const OUString& GetHelpPath() { return GetPath( SvtPathOptions::Paths::Help ); }
+ const OUString& GetLinguisticPath() { return GetPath( SvtPathOptions::Paths::Linguistic ); }
+ const OUString& GetModulePath() { return GetPath( SvtPathOptions::Paths::Module ); }
+ const OUString& GetPalettePath() { return GetPath( SvtPathOptions::Paths::Palette ); }
+ const OUString& GetIconsetPath() { return GetPath( SvtPathOptions::Paths::IconSet); }
+ const OUString& GetPluginPath() { return GetPath( SvtPathOptions::Paths::Plugin ); }
+ const OUString& GetStoragePath() { return GetPath( SvtPathOptions::Paths::Storage ); }
+ const OUString& GetTempPath() { return GetPath( SvtPathOptions::Paths::Temp ); }
+ const OUString& GetTemplatePath() { return GetPath( SvtPathOptions::Paths::Template ); }
+ const OUString& GetUserConfigPath() { return GetPath( SvtPathOptions::Paths::UserConfig ); }
+ const OUString& GetWorkPath() { return GetPath( SvtPathOptions::Paths::Work ); }
+ const OUString& GetUIConfigPath() { return GetPath( SvtPathOptions::Paths::UIConfig ); }
+ const OUString& GetFingerprintPath() { return GetPath( SvtPathOptions::Paths::Fingerprint ); }
+ const OUString& GetNumbertextPath() { return GetPath( SvtPathOptions::Paths::NumberText ); }
+ const OUString& GetClassificationPath() { return GetPath( SvtPathOptions::Paths::Classification ); }
+
+ // set the paths
+ void SetPath( SvtPathOptions::Paths, const OUString& rNewPath );
+ void SetAutoTextPath( const OUString& rPath ) { SetPath( SvtPathOptions::Paths::AutoText, rPath ); }
+ void SetBasicPath( const OUString& rPath ) { SetPath( SvtPathOptions::Paths::Basic, rPath ); }
+ void SetTempPath( const OUString& rPath ) { SetPath( SvtPathOptions::Paths::Temp, rPath ); }
+
+ OUString SubstVar( const OUString& rVar ) const;
+ OUString ExpandMacros( const OUString& rPath ) const;
+ OUString UsePathVariables( const OUString& rPath ) const;
+};
+
+// global ----------------------------------------------------------------
+
+static std::weak_ptr<SvtPathOptions_Impl> g_pOptions;
+
+namespace {
+
+// functions -------------------------------------------------------------
+struct PropertyStruct
+{
+ const char* pPropName; // The ascii name of the Office path
+ SvtPathOptions::Paths ePath; // The enum value used by SvtPathOptions
+};
+
+struct VarNameAttribute
+{
+ const char* pVarName; // The name of the path variable
+};
+
+}
+
+const PropertyStruct aPropNames[] =
+{
+ { "Addin", SvtPathOptions::Paths::AddIn },
+ { "AutoCorrect", SvtPathOptions::Paths::AutoCorrect },
+ { "AutoText", SvtPathOptions::Paths::AutoText },
+ { "Backup", SvtPathOptions::Paths::Backup },
+ { "Basic", SvtPathOptions::Paths::Basic },
+ { "Bitmap", SvtPathOptions::Paths::Bitmap },
+ { "Config", SvtPathOptions::Paths::Config },
+ { "Dictionary", SvtPathOptions::Paths::Dictionary },
+ { "Favorite", SvtPathOptions::Paths::Favorites },
+ { "Filter", SvtPathOptions::Paths::Filter },
+ { "Gallery", SvtPathOptions::Paths::Gallery },
+ { "Graphic", SvtPathOptions::Paths::Graphic },
+ { "Help", SvtPathOptions::Paths::Help },
+ { "Iconset", SvtPathOptions::Paths::IconSet },
+ { "Linguistic", SvtPathOptions::Paths::Linguistic },
+ { "Module", SvtPathOptions::Paths::Module },
+ { "Palette", SvtPathOptions::Paths::Palette },
+ { "Plugin", SvtPathOptions::Paths::Plugin },
+ { "Storage", SvtPathOptions::Paths::Storage },
+ { "Temp", SvtPathOptions::Paths::Temp },
+ { "Template", SvtPathOptions::Paths::Template },
+ { "UserConfig", SvtPathOptions::Paths::UserConfig },
+ { "Work", SvtPathOptions::Paths::Work },
+ { "UIConfig", SvtPathOptions::Paths::UIConfig },
+ { "Fingerprint", SvtPathOptions::Paths::Fingerprint },
+ { "Numbertext", SvtPathOptions::Paths::NumberText },
+ { "Classification", SvtPathOptions::Paths::Classification }
+};
+
+const VarNameAttribute aVarNameAttribute[] =
+{
+ { SUBSTITUTE_INSTPATH }, // $(instpath)
+ { SUBSTITUTE_PROGPATH }, // $(progpath)
+ { SUBSTITUTE_USERPATH }, // $(userpath)
+ { SUBSTITUTE_PATH }, // $(path)
+};
+
+// class SvtPathOptions_Impl ---------------------------------------------
+
+const OUString& SvtPathOptions_Impl::GetPath( SvtPathOptions::Paths ePath )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ try
+ {
+ OUString aPathValue;
+ sal_Int32 nHandle = m_aMapEnumToPropHandle[ePath];
+
+ // Substitution is done by the service itself using the substitution service
+ Any a = m_xPathSettings->getFastPropertyValue( nHandle );
+ a >>= aPathValue;
+ if( ePath == SvtPathOptions::Paths::AddIn ||
+ ePath == SvtPathOptions::Paths::Filter ||
+ ePath == SvtPathOptions::Paths::Help ||
+ ePath == SvtPathOptions::Paths::Module ||
+ ePath == SvtPathOptions::Paths::Plugin ||
+ ePath == SvtPathOptions::Paths::Storage
+ )
+ {
+ // These office paths have to be converted to system pates
+ OUString aResult;
+ osl::FileBase::getSystemPathFromFileURL( aPathValue, aResult );
+ aPathValue = aResult;
+ }
+ else if (ePath == SvtPathOptions::Paths::Palette ||
+ ePath == SvtPathOptions::Paths::IconSet)
+ {
+ auto ctx = comphelper::getProcessComponentContext();
+ OUStringBuffer buf(aPathValue.getLength()*2);
+ for (sal_Int32 i = 0;;)
+ {
+ buf.append(
+ comphelper::getExpandedUri(
+ ctx, aPathValue.getToken(0, ';', i)));
+ if (i == -1) {
+ break;
+ }
+ buf.append(';');
+ }
+ aPathValue = buf.makeStringAndClear();
+ }
+
+ m_aPathArray[ ePath ] = aPathValue;
+ return m_aPathArray[ ePath ];
+ }
+ catch (UnknownPropertyException &)
+ {
+ }
+
+ return m_aEmptyString;
+}
+
+void SvtPathOptions_Impl::SetPath( SvtPathOptions::Paths ePath, const OUString& rNewPath )
+{
+ std::unique_lock aGuard( m_aMutex );
+
+ OUString aResult;
+ OUString aNewValue;
+ Any a;
+
+ switch ( ePath )
+ {
+ case SvtPathOptions::Paths::AddIn:
+ case SvtPathOptions::Paths::Filter:
+ case SvtPathOptions::Paths::Help:
+ case SvtPathOptions::Paths::Module:
+ case SvtPathOptions::Paths::Plugin:
+ case SvtPathOptions::Paths::Storage:
+ {
+ // These office paths have to be convert back to UCB-URL's
+ osl::FileBase::getFileURLFromSystemPath( rNewPath, aResult );
+ aNewValue = aResult;
+ }
+ break;
+
+ default:
+ aNewValue = rNewPath;
+ }
+
+ // Resubstitution is done by the service itself using the substitution service
+ a <<= aNewValue;
+ try
+ {
+ m_xPathSettings->setFastPropertyValue( m_aMapEnumToPropHandle[ePath], a );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.config", "SetPath");
+ }
+}
+
+OUString SvtPathOptions_Impl::ExpandMacros( const OUString& rPath ) const
+{
+ OUString sExpanded( rPath );
+
+ const INetURLObject aParser( rPath );
+ if ( aParser.GetProtocol() == INetProtocol::VndSunStarExpand )
+ sExpanded = m_xMacroExpander->expandMacros( aParser.GetURLPath( INetURLObject::DecodeMechanism::WithCharset ) );
+
+ return sExpanded;
+}
+
+OUString SvtPathOptions_Impl::UsePathVariables( const OUString& rPath ) const
+{
+ return m_xSubstVariables->reSubstituteVariables( rPath );
+}
+
+OUString SvtPathOptions_Impl::SubstVar( const OUString& rVar ) const
+{
+ // Don't work at parameter-string directly. Copy it.
+ OUString aWorkText = rVar;
+
+ // Convert the returned path to system path!
+ bool bConvertLocal = false;
+
+ // Search for first occurrence of "$(...".
+ sal_Int32 nPosition = aWorkText.indexOf( SIGN_STARTVARIABLE ); // = first position of "$(" in string
+ sal_Int32 nLength = 0; // = count of letters from "$(" to ")" in string
+
+ // Have we found any variable like "$(...)"?
+ if ( nPosition != STRPOS_NOTFOUND )
+ {
+ // Yes; Get length of found variable.
+ // If no ")" was found - nLength is set to 0 by default! see before.
+ sal_Int32 nEndPosition = aWorkText.indexOf( SIGN_ENDVARIABLE, nPosition );
+ if ( nEndPosition != STRPOS_NOTFOUND )
+ nLength = nEndPosition - nPosition + 1;
+ }
+
+ // Is there another path variable?
+ while ( ( nPosition != STRPOS_NOTFOUND ) && ( nLength > 0 ) )
+ {
+ // YES; Get the next variable for replace.
+ OUString aSubString = aWorkText.copy( nPosition, nLength );
+ aSubString = aSubString.toAsciiLowerCase();
+
+ // Look for special variable that needs a system path.
+ VarNameSet::const_iterator pIter = m_aSystemPathVarNames.find( aSubString );
+ if ( pIter != m_aSystemPathVarNames.end() )
+ bConvertLocal = true;
+
+ nPosition += nLength;
+
+ // We must control index in string before call something at OUString!
+ // The OUString-implementation don't do it for us :-( but the result is not defined otherwise.
+ if ( nPosition + 1 > aWorkText.getLength() )
+ {
+ // Position is out of range. Break loop!
+ nPosition = STRPOS_NOTFOUND;
+ nLength = 0;
+ }
+ else
+ {
+ // Else; Position is valid. Search for next variable.
+ nPosition = aWorkText.indexOf( SIGN_STARTVARIABLE, nPosition );
+ // Have we found any variable like "$(...)"?
+ if ( nPosition != STRPOS_NOTFOUND )
+ {
+ // Yes; Get length of found variable. If no ")" was found - nLength must set to 0!
+ nLength = 0;
+ sal_Int32 nEndPosition = aWorkText.indexOf( SIGN_ENDVARIABLE, nPosition );
+ if ( nEndPosition != STRPOS_NOTFOUND )
+ nLength = nEndPosition - nPosition + 1;
+ }
+ }
+ }
+
+ aWorkText = m_xSubstVariables->substituteVariables( rVar, false );
+
+ if ( bConvertLocal )
+ {
+ // Convert the URL to a system path for special path variables
+ OUString aReturn;
+ osl::FileBase::getSystemPathFromFileURL( aWorkText, aReturn );
+ return aReturn;
+ }
+
+ return aWorkText;
+}
+
+SvtPathOptions_Impl::SvtPathOptions_Impl()
+{
+ Reference< XComponentContext > xContext = comphelper::getProcessComponentContext();
+
+ // Create necessary services
+ Reference< XPathSettings > xPathSettings = thePathSettings::get(xContext);
+ m_xPathSettings.set( xPathSettings, UNO_QUERY_THROW );
+ m_xSubstVariables.set( PathSubstitution::create(xContext) );
+ m_xMacroExpander = theMacroExpander::get(xContext);
+
+ // Create temporary hash map to have a mapping between property names and property handles
+ Reference< XPropertySetInfo > xPropSetInfo = xPathSettings->getPropertySetInfo();
+ const Sequence< Property > aPathPropSeq = xPropSetInfo->getProperties();
+
+ NameToHandleMap aTempHashMap;
+ for ( const css::beans::Property& aProperty : aPathPropSeq )
+ {
+ aTempHashMap.emplace(aProperty.Name, aProperty.Handle);
+ }
+
+ // Create mapping between internal enum (SvtPathOptions::Paths) and property handle
+ for ( auto const & p : aPropNames )
+ {
+ NameToHandleMap::const_iterator pIter =
+ aTempHashMap.find( OUString::createFromAscii( p.pPropName ));
+
+ if ( pIter != aTempHashMap.end() )
+ {
+ sal_Int32 nHandle = pIter->second;
+ SvtPathOptions::Paths nEnum = p.ePath;
+ m_aMapEnumToPropHandle.emplace( nEnum, nHandle );
+ }
+ }
+
+ // Create hash map for path variables that need a system path as a return value!
+ for ( auto const & i : aVarNameAttribute )
+ {
+ m_aSystemPathVarNames.insert( OUString::createFromAscii( i.pVarName ) );
+ }
+}
+
+// class SvtPathOptions --------------------------------------------------
+
+namespace
+{
+ std::mutex& lclMutex()
+ {
+ static std::mutex SINGLETON;
+ return SINGLETON;
+ }
+}
+
+SvtPathOptions::SvtPathOptions()
+{
+ // Global access, must be guarded (multithreading)
+ std::unique_lock aGuard( lclMutex() );
+ pImpl = g_pOptions.lock();
+ if ( !pImpl )
+ {
+ pImpl = std::make_shared<SvtPathOptions_Impl>();
+ g_pOptions = pImpl;
+ aGuard.unlock(); // because holdConfigItem will call this constructor
+ ItemHolder1::holdConfigItem(EItem::PathOptions);
+ }
+}
+
+SvtPathOptions::~SvtPathOptions()
+{
+ // Global access, must be guarded (multithreading)
+ std::unique_lock aGuard( lclMutex() );
+
+ pImpl.reset();
+}
+
+const OUString& SvtPathOptions::GetAddinPath() const
+{
+ return pImpl->GetAddinPath();
+}
+
+const OUString& SvtPathOptions::GetAutoCorrectPath() const
+{
+ return pImpl->GetAutoCorrectPath();
+}
+
+const OUString& SvtPathOptions::GetAutoTextPath() const
+{
+ return pImpl->GetAutoTextPath();
+}
+
+const OUString& SvtPathOptions::GetBackupPath() const
+{
+ return pImpl->GetBackupPath();
+}
+
+const OUString& SvtPathOptions::GetBasicPath() const
+{
+ return pImpl->GetBasicPath();
+}
+
+const OUString& SvtPathOptions::GetBitmapPath() const
+{
+ return pImpl->GetBitmapPath();
+}
+
+const OUString& SvtPathOptions::GetConfigPath() const
+{
+ return pImpl->GetConfigPath();
+}
+
+const OUString& SvtPathOptions::GetDictionaryPath() const
+{
+ return pImpl->GetDictionaryPath();
+}
+
+const OUString& SvtPathOptions::GetFavoritesPath() const
+{
+ return pImpl->GetFavoritesPath();
+}
+
+const OUString& SvtPathOptions::GetFilterPath() const
+{
+ return pImpl->GetFilterPath();
+}
+
+const OUString& SvtPathOptions::GetGalleryPath() const
+{
+ return pImpl->GetGalleryPath();
+}
+
+const OUString& SvtPathOptions::GetGraphicPath() const
+{
+ return pImpl->GetGraphicPath();
+}
+
+const OUString& SvtPathOptions::GetHelpPath() const
+{
+ return pImpl->GetHelpPath();
+}
+
+const OUString& SvtPathOptions::GetLinguisticPath() const
+{
+ return pImpl->GetLinguisticPath();
+}
+
+const OUString& SvtPathOptions::GetFingerprintPath() const
+{
+ return pImpl->GetFingerprintPath();
+}
+
+const OUString& SvtPathOptions::GetNumbertextPath() const
+{
+ return pImpl->GetNumbertextPath();
+}
+
+const OUString& SvtPathOptions::GetModulePath() const
+{
+ return pImpl->GetModulePath();
+}
+
+const OUString& SvtPathOptions::GetPalettePath() const
+{
+ return pImpl->GetPalettePath();
+}
+
+const OUString& SvtPathOptions::GetIconsetPath() const
+{
+ return pImpl->GetIconsetPath();
+}
+
+const OUString& SvtPathOptions::GetPluginPath() const
+{
+ return pImpl->GetPluginPath();
+}
+
+const OUString& SvtPathOptions::GetStoragePath() const
+{
+ return pImpl->GetStoragePath();
+}
+
+const OUString& SvtPathOptions::GetTempPath() const
+{
+ return pImpl->GetTempPath();
+}
+
+const OUString& SvtPathOptions::GetTemplatePath() const
+{
+ return pImpl->GetTemplatePath();
+}
+
+const OUString& SvtPathOptions::GetUserConfigPath() const
+{
+ return pImpl->GetUserConfigPath();
+}
+
+const OUString& SvtPathOptions::GetWorkPath() const
+{
+ return pImpl->GetWorkPath();
+}
+
+const OUString& SvtPathOptions::GetClassificationPath() const
+{
+ return pImpl->GetClassificationPath();
+}
+
+void SvtPathOptions::SetAutoTextPath( const OUString& rPath )
+{
+ pImpl->SetAutoTextPath( rPath );
+}
+
+void SvtPathOptions::SetBasicPath( const OUString& rPath )
+{
+ pImpl->SetBasicPath( rPath );
+}
+
+void SvtPathOptions::SetTempPath( const OUString& rPath )
+{
+ pImpl->SetTempPath( rPath );
+}
+
+OUString SvtPathOptions::SubstituteVariable( const OUString& rVar ) const
+{
+ return pImpl->SubstVar( rVar );
+}
+
+OUString SvtPathOptions::ExpandMacros( const OUString& rPath ) const
+{
+ return pImpl->ExpandMacros( rPath );
+}
+
+OUString SvtPathOptions::UseVariable( const OUString& rPath ) const
+{
+ return pImpl->UsePathVariables( rPath );
+}
+
+bool SvtPathOptions::SearchFile( OUString& rIniFile, SvtPathOptions::Paths ePath )
+{
+ // check parameter: empty inifile name?
+ if ( rIniFile.isEmpty() )
+ {
+ SAL_WARN( "unotools.config", "SvtPathOptions::SearchFile(): invalid parameter" );
+ return false;
+ }
+
+ OUString aIniFile = pImpl->SubstVar( rIniFile );
+ bool bRet = false;
+
+ switch ( ePath )
+ {
+ case SvtPathOptions::Paths::UserConfig:
+ {
+ // path is a URL
+ bRet = true;
+ INetURLObject aObj( GetUserConfigPath() );
+
+ sal_Int32 nIniIndex = 0;
+ do
+ {
+ std::u16string_view aToken = o3tl::getToken(aIniFile, 0, '/', nIniIndex );
+ aObj.insertName(aToken);
+ }
+ while ( nIniIndex >= 0 );
+
+ if ( !::utl::UCBContentHelper::Exists( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ) )
+ {
+ aObj.SetSmartURL( GetConfigPath() );
+ aObj.insertName( aIniFile );
+ bRet = ::utl::UCBContentHelper::Exists( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ }
+
+ if ( bRet )
+ rIniFile = aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+
+ break;
+ }
+
+ default:
+ {
+ OUString aPath;
+ switch ( ePath )
+ {
+ case SvtPathOptions::Paths::AddIn: aPath = GetAddinPath(); break;
+ case SvtPathOptions::Paths::AutoCorrect: aPath = GetAutoCorrectPath(); break;
+ case SvtPathOptions::Paths::AutoText: aPath = GetAutoTextPath(); break;
+ case SvtPathOptions::Paths::Backup: aPath = GetBackupPath(); break;
+ case SvtPathOptions::Paths::Basic: aPath = GetBasicPath(); break;
+ case SvtPathOptions::Paths::Bitmap: aPath = GetBitmapPath(); break;
+ case SvtPathOptions::Paths::Config: aPath = GetConfigPath(); break;
+ case SvtPathOptions::Paths::Dictionary: aPath = GetDictionaryPath(); break;
+ case SvtPathOptions::Paths::Favorites: aPath = GetFavoritesPath(); break;
+ case SvtPathOptions::Paths::Filter: aPath = GetFilterPath(); break;
+ case SvtPathOptions::Paths::Gallery: aPath = GetGalleryPath(); break;
+ case SvtPathOptions::Paths::Graphic: aPath = GetGraphicPath(); break;
+ case SvtPathOptions::Paths::Help: aPath = GetHelpPath(); break;
+ case SvtPathOptions::Paths::Linguistic: aPath = GetLinguisticPath(); break;
+ case SvtPathOptions::Paths::Module: aPath = GetModulePath(); break;
+ case SvtPathOptions::Paths::Palette: aPath = GetPalettePath(); break;
+ case SvtPathOptions::Paths::IconSet: aPath = GetIconsetPath(); break;
+ case SvtPathOptions::Paths::Plugin: aPath = GetPluginPath(); break;
+ case SvtPathOptions::Paths::Storage: aPath = GetStoragePath(); break;
+ case SvtPathOptions::Paths::Temp: aPath = GetTempPath(); break;
+ case SvtPathOptions::Paths::Template: aPath = GetTemplatePath(); break;
+ case SvtPathOptions::Paths::Work: aPath = GetWorkPath(); break;
+ case SvtPathOptions::Paths::UIConfig: aPath = pImpl->GetUIConfigPath(); break;
+ case SvtPathOptions::Paths::Fingerprint: aPath = GetFingerprintPath(); break;
+ case SvtPathOptions::Paths::NumberText: aPath = GetNumbertextPath(); break;
+ case SvtPathOptions::Paths::Classification: aPath = GetClassificationPath(); break;
+ // coverity[dead_error_begin] - following conditions exist to avoid compiler warning
+ case SvtPathOptions::Paths::UserConfig:
+ case SvtPathOptions::Paths::LAST:
+ break;
+ }
+
+ sal_Int32 nPathIndex = 0;
+ do
+ {
+ bool bIsURL = true;
+ OUString aPathToken( aPath.getToken( 0, SEARCHPATH_DELIMITER, nPathIndex ) );
+ INetURLObject aObj( aPathToken );
+ if ( aObj.HasError() )
+ {
+ bIsURL = false;
+ OUString aURL;
+ if ( osl::FileBase::getFileURLFromSystemPath( aPathToken, aURL )
+ == osl::FileBase::E_None )
+ aObj.SetURL( aURL );
+ }
+ if ( aObj.GetProtocol() == INetProtocol::VndSunStarExpand )
+ {
+ Reference< XMacroExpander > xMacroExpander = theMacroExpander::get( ::comphelper::getProcessComponentContext() );
+ const OUString sExpandedPath = xMacroExpander->expandMacros( aObj.GetURLPath( INetURLObject::DecodeMechanism::WithCharset ) );
+ aObj.SetURL( sExpandedPath );
+ }
+
+ sal_Int32 nIniIndex = 0;
+ do
+ {
+ std::u16string_view aToken = o3tl::getToken(aIniFile, 0, '/', nIniIndex );
+ aObj.insertName(aToken);
+ }
+ while ( nIniIndex >= 0 );
+
+ bRet = ::utl::UCBContentHelper::Exists( aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+
+ if ( bRet )
+ {
+ if ( !bIsURL )
+ {
+ OUString sTmp;
+ osl::FileBase::getSystemPathFromFileURL(
+ aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE ), sTmp );
+ rIniFile = sTmp;
+ }
+ else
+ rIniFile = aObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
+ break;
+ }
+ }
+ while ( nPathIndex >= 0 );
+ }
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/saveopt.cxx b/unotools/source/config/saveopt.cxx
new file mode 100644
index 0000000000..b91ba86396
--- /dev/null
+++ b/unotools/source/config/saveopt.cxx
@@ -0,0 +1,94 @@
+/* -*- 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 <sal/log.hxx>
+#include <unotools/saveopt.hxx>
+#include <unotools/configmgr.hxx>
+
+#include <officecfg/Office/Common.hxx>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+
+void SetODFDefaultVersion( SvtSaveOptions::ODFDefaultVersion eVersion, const std::shared_ptr<comphelper::ConfigurationChanges>& xChanges )
+{
+ sal_Int16 nTmp = (eVersion == SvtSaveOptions::ODFVER_LATEST) ? sal_Int16( 3 ) : sal_Int16( eVersion );
+ officecfg::Office::Common::Save::ODF::DefaultVersion::set(nTmp, xChanges);
+}
+
+void SetODFDefaultVersion( SvtSaveOptions::ODFDefaultVersion eVersion )
+{
+ auto xChanges = comphelper::ConfigurationChanges::create();
+ SetODFDefaultVersion(eVersion, xChanges);
+ xChanges->commit();
+}
+
+SvtSaveOptions::ODFDefaultVersion GetODFDefaultVersion()
+{
+ SvtSaveOptions::ODFDefaultVersion nRet;
+ sal_Int16 nTmp = officecfg::Office::Common::Save::ODF::DefaultVersion::get();
+ if( nTmp == 3 )
+ nRet = SvtSaveOptions::ODFVER_LATEST;
+ else
+ nRet = SvtSaveOptions::ODFDefaultVersion( nTmp );
+ SAL_WARN_IF(nRet == SvtSaveOptions::ODFVER_UNKNOWN, "unotools.config", "DefaultVersion is ODFVER_UNKNOWN?");
+ return (nRet == SvtSaveOptions::ODFVER_UNKNOWN) ? SvtSaveOptions::ODFVER_LATEST : nRet;
+}
+
+SvtSaveOptions::ODFSaneDefaultVersion GetODFSaneDefaultVersion()
+{
+ SvtSaveOptions::ODFDefaultVersion nRet;
+ sal_Int16 nTmp = officecfg::Office::Common::Save::ODF::DefaultVersion::get();
+ if( nTmp == 3 )
+ nRet = SvtSaveOptions::ODFVER_LATEST;
+ else
+ nRet = SvtSaveOptions::ODFDefaultVersion( nTmp );
+
+ return GetODFSaneDefaultVersion(nRet);
+}
+
+SvtSaveOptions::ODFSaneDefaultVersion GetODFSaneDefaultVersion(SvtSaveOptions::ODFDefaultVersion eODFDefaultVersion)
+{
+ switch (eODFDefaultVersion)
+ {
+ default:
+ assert(!"map new ODFDefaultVersion to ODFSaneDefaultVersion");
+ break;
+ case SvtSaveOptions::ODFVER_UNKNOWN:
+ case SvtSaveOptions::ODFVER_LATEST:
+ return SvtSaveOptions::ODFSVER_LATEST_EXTENDED;
+ case SvtSaveOptions::ODFVER_010:
+ return SvtSaveOptions::ODFSVER_010;
+ case SvtSaveOptions::ODFVER_011:
+ return SvtSaveOptions::ODFSVER_011;
+ case SvtSaveOptions::ODFVER_012:
+ return SvtSaveOptions::ODFSVER_012;
+ case SvtSaveOptions::ODFVER_012_EXT_COMPAT:
+ return SvtSaveOptions::ODFSVER_012_EXT_COMPAT;
+ case SvtSaveOptions::ODFVER_012_EXTENDED:
+ return SvtSaveOptions::ODFSVER_012_EXTENDED;
+ case SvtSaveOptions::ODFVER_013:
+ return SvtSaveOptions::ODFSVER_013;
+ }
+ return SvtSaveOptions::ODFSVER_LATEST_EXTENDED;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/searchopt.cxx b/unotools/source/config/searchopt.cxx
new file mode 100644
index 0000000000..4237fd7281
--- /dev/null
+++ b/unotools/source/config/searchopt.cxx
@@ -0,0 +1,612 @@
+/* -*- 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 <unotools/searchopt.hxx>
+#include <tools/debug.hxx>
+#include <unotools/configitem.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/uno/Any.h>
+#include <osl/diagnose.h>
+#include <i18nutil/transliteration.hxx>
+
+using namespace utl;
+using namespace com::sun::star::uno;
+
+#define MAX_FLAGS_OFFSET 29
+
+class SvtSearchOptions_Impl : public ConfigItem
+{
+ sal_Int32 nFlags;
+ bool bModified;
+
+ SvtSearchOptions_Impl(const SvtSearchOptions_Impl&) = delete;
+ SvtSearchOptions_Impl& operator=(const SvtSearchOptions_Impl&) = delete;
+
+ // ConfigItem
+ virtual void ImplCommit() override;
+
+protected:
+ bool IsModified() const { return bModified; }
+ using ConfigItem::SetModified;
+ void SetModified( bool bVal );
+ void Load();
+ bool Save();
+
+ static Sequence< OUString > GetPropertyNames();
+
+public:
+ SvtSearchOptions_Impl();
+ virtual ~SvtSearchOptions_Impl() override;
+
+ virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override;
+
+ bool GetFlag( sal_uInt16 nOffset ) const;
+ void SetFlag( sal_uInt16 nOffset, bool bVal );
+ void SetSearchAlgorithm( sal_uInt16 nOffset, bool bVal );
+};
+
+SvtSearchOptions_Impl::SvtSearchOptions_Impl() :
+ ConfigItem( "Office.Common/SearchOptions" ),
+ nFlags(0x0003FFFF) // set all options values to 'true'
+
+{
+ Load();
+ SetModified( false );
+}
+
+SvtSearchOptions_Impl::~SvtSearchOptions_Impl()
+{
+ assert(!IsModified()); // should have been committed
+}
+
+void SvtSearchOptions_Impl::ImplCommit()
+{
+ if (IsModified())
+ Save();
+}
+
+void SvtSearchOptions_Impl::Notify( const Sequence< OUString >& )
+{
+}
+
+bool SvtSearchOptions_Impl::GetFlag( sal_uInt16 nOffset ) const
+{
+ DBG_ASSERT( nOffset <= MAX_FLAGS_OFFSET, "offset out of range");
+ return ((nFlags >> nOffset) & 0x01) != 0;
+}
+
+void SvtSearchOptions_Impl::SetFlag( sal_uInt16 nOffset, bool bVal )
+{
+ DBG_ASSERT( nOffset <= MAX_FLAGS_OFFSET, "offset out of range");
+ sal_Int32 nOldFlags = nFlags;
+ sal_Int32 nMask = (sal_Int32(1)) << nOffset;
+ if (bVal)
+ nFlags |= nMask;
+ else
+ nFlags &= ~nMask;
+ if (nFlags != nOldFlags)
+ SetModified( true );
+}
+
+void SvtSearchOptions_Impl::SetModified( bool bVal )
+{
+ bModified = bVal;
+ if (bModified)
+ {
+ ConfigItem::SetModified();
+ }
+}
+
+Sequence< OUString > SvtSearchOptions_Impl::GetPropertyNames()
+{
+ static const char* aPropNames[ MAX_FLAGS_OFFSET + 1 ] =
+ {
+ "IsWholeWordsOnly", // 0
+ "IsBackwards", // 1
+ "IsUseRegularExpression", // 2
+ //"IsCurrentSelectionOnly", // interactively set or not...
+ "IsSearchForStyles", // 3
+ "IsSimilaritySearch", // 4
+ "IsUseAsianOptions", // 5
+ "IsMatchCase", // 6
+ "Japanese/IsMatchFullHalfWidthForms", // 7
+ "Japanese/IsMatchHiraganaKatakana", // 8
+ "Japanese/IsMatchContractions", // 9
+ "Japanese/IsMatchMinusDashCho-on", // 10
+ "Japanese/IsMatchRepeatCharMarks", // 11
+ "Japanese/IsMatchVariantFormKanji", // 12
+ "Japanese/IsMatchOldKanaForms", // 13
+ "Japanese/IsMatch_DiZi_DuZu", // 14
+ "Japanese/IsMatch_BaVa_HaFa", // 15
+ "Japanese/IsMatch_TsiThiChi_DhiZi", // 16
+ "Japanese/IsMatch_HyuIyu_ByuVyu", // 17
+ "Japanese/IsMatch_SeShe_ZeJe", // 18
+ "Japanese/IsMatch_IaIya", // 19
+ "Japanese/IsMatch_KiKu", // 20
+ "Japanese/IsIgnorePunctuation", // 21
+ "Japanese/IsIgnoreWhitespace", // 22
+ "Japanese/IsIgnoreProlongedSoundMark", // 23
+ "Japanese/IsIgnoreMiddleDot", // 24
+ "IsNotes", // 25
+ "IsIgnoreDiacritics_CTL", // 26
+ "IsIgnoreKashida_CTL", // 27
+ "IsSearchFormatted", // 28
+ "IsUseWildcard" // 29
+ };
+
+ Sequence< OUString > aNames(std::size(aPropNames));
+ OUString* pNames = aNames.getArray();
+ for (std::size_t i = 0; i < std::size(aPropNames); ++i)
+ pNames[i] = OUString::createFromAscii( aPropNames[i] );
+
+ return aNames;
+}
+
+void SvtSearchOptions_Impl::SetSearchAlgorithm( sal_uInt16 nOffset, bool bVal )
+{
+ if (bVal)
+ {
+ // Search algorithms are mutually exclusive.
+ if (nOffset != 2 && GetFlag(2))
+ SetFlag( 2, false );
+ if (nOffset != 4 && GetFlag(4))
+ SetFlag( 4, false );
+ if (nOffset != 29 && GetFlag(29))
+ SetFlag( 29, false );
+ }
+ SetFlag( nOffset, bVal );
+}
+
+void SvtSearchOptions_Impl::Load()
+{
+ bool bSucc = false;
+
+ Sequence< OUString > aNames = GetPropertyNames();
+ sal_Int32 nProps = aNames.getLength();
+
+ const Sequence< Any > aValues = GetProperties( aNames );
+ DBG_ASSERT( aValues.getLength() == aNames.getLength(),
+ "GetProperties failed" );
+ //EnableNotification( aNames );
+
+ if (nProps && aValues.getLength() == nProps)
+ {
+ bSucc = true;
+
+ const Any* pValues = aValues.getConstArray();
+ for (sal_Int32 i = 0; i < nProps; ++i)
+ {
+ const Any &rVal = pValues[i];
+ DBG_ASSERT( rVal.hasValue(), "property value missing" );
+ if (rVal.hasValue())
+ {
+ bool bVal = bool();
+ if (rVal >>= bVal)
+ {
+ if (i <= MAX_FLAGS_OFFSET)
+ {
+ // use index in sequence as flag index
+ SetFlag( i, bVal );
+ }
+ else {
+ OSL_FAIL( "unexpected index" );
+ }
+ }
+ else
+ {
+ OSL_FAIL( "unexpected type" );
+ bSucc = false;
+ }
+ }
+ else
+ {
+ OSL_FAIL( "value missing" );
+ bSucc = false;
+ }
+ }
+ }
+ DBG_ASSERT( bSucc, "LoadConfig failed" );
+}
+
+bool SvtSearchOptions_Impl::Save()
+{
+ bool bSucc = false;
+
+ const Sequence< OUString > aNames = GetPropertyNames();
+ sal_Int32 nProps = aNames.getLength();
+
+ Sequence< Any > aValues( nProps );
+ Any *pValue = aValues.getArray();
+
+ DBG_ASSERT( nProps == MAX_FLAGS_OFFSET + 1,
+ "unexpected size of index" );
+ if (nProps == MAX_FLAGS_OFFSET + 1)
+ {
+ for (sal_Int32 i = 0; i < nProps; ++i)
+ pValue[i] <<= GetFlag(i);
+ bSucc |= PutProperties( aNames, aValues );
+ }
+
+ if (bSucc)
+ SetModified( false );
+
+ return bSucc;
+}
+
+SvtSearchOptions::SvtSearchOptions()
+ : pImpl( new SvtSearchOptions_Impl )
+{
+}
+
+SvtSearchOptions::~SvtSearchOptions()
+{
+}
+
+void SvtSearchOptions::Commit()
+{
+ pImpl->Commit();
+}
+
+TransliterationFlags SvtSearchOptions::GetTransliterationFlags() const
+{
+ TransliterationFlags nRes = TransliterationFlags::NONE;
+
+ if (!IsMatchCase()) // 'IsMatchCase' means act case sensitive
+ nRes |= TransliterationFlags::IGNORE_CASE;
+ if ( IsMatchFullHalfWidthForms())
+ nRes |= TransliterationFlags::IGNORE_WIDTH;
+ if ( IsMatchHiraganaKatakana())
+ nRes |= TransliterationFlags::IGNORE_KANA;
+ if ( IsMatchContractions())
+ nRes |= TransliterationFlags::ignoreSize_ja_JP;
+ if ( IsMatchMinusDashChoon())
+ nRes |= TransliterationFlags::ignoreMinusSign_ja_JP;
+ if ( IsMatchRepeatCharMarks())
+ nRes |= TransliterationFlags::ignoreIterationMark_ja_JP;
+ if ( IsMatchVariantFormKanji())
+ nRes |= TransliterationFlags::ignoreTraditionalKanji_ja_JP;
+ if ( IsMatchOldKanaForms())
+ nRes |= TransliterationFlags::ignoreTraditionalKana_ja_JP;
+ if ( IsMatchDiziDuzu())
+ nRes |= TransliterationFlags::ignoreZiZu_ja_JP;
+ if ( IsMatchBavaHafa())
+ nRes |= TransliterationFlags::ignoreBaFa_ja_JP;
+ if ( IsMatchTsithichiDhizi())
+ nRes |= TransliterationFlags::ignoreTiJi_ja_JP;
+ if ( IsMatchHyuiyuByuvyu())
+ nRes |= TransliterationFlags::ignoreHyuByu_ja_JP;
+ if ( IsMatchSesheZeje())
+ nRes |= TransliterationFlags::ignoreSeZe_ja_JP;
+ if ( IsMatchIaiya())
+ nRes |= TransliterationFlags::ignoreIandEfollowedByYa_ja_JP;
+ if ( IsMatchKiku())
+ nRes |= TransliterationFlags::ignoreKiKuFollowedBySa_ja_JP;
+ if ( IsIgnorePunctuation())
+ nRes |= TransliterationFlags::ignoreSeparator_ja_JP;
+ if ( IsIgnoreWhitespace())
+ nRes |= TransliterationFlags::ignoreSpace_ja_JP;
+ if ( IsIgnoreProlongedSoundMark())
+ nRes |= TransliterationFlags::ignoreProlongedSoundMark_ja_JP;
+ if ( IsIgnoreMiddleDot())
+ nRes |= TransliterationFlags::ignoreMiddleDot_ja_JP;
+ if ( IsIgnoreDiacritics_CTL())
+ nRes |= TransliterationFlags::IGNORE_DIACRITICS_CTL;
+ if ( IsIgnoreKashida_CTL())
+ nRes |= TransliterationFlags::IGNORE_KASHIDA_CTL;
+ return nRes;
+}
+
+bool SvtSearchOptions::IsWholeWordsOnly() const
+{
+ return pImpl->GetFlag( 0 );
+}
+
+void SvtSearchOptions::SetWholeWordsOnly( bool bVal )
+{
+ pImpl->SetFlag( 0, bVal );
+}
+
+bool SvtSearchOptions::IsBackwards() const
+{
+ return pImpl->GetFlag( 1 );
+}
+
+void SvtSearchOptions::SetBackwards( bool bVal )
+{
+ pImpl->SetFlag( 1, bVal );
+}
+
+bool SvtSearchOptions::IsUseRegularExpression() const
+{
+ return pImpl->GetFlag( 2 );
+}
+
+void SvtSearchOptions::SetUseRegularExpression( bool bVal )
+{
+ pImpl->SetSearchAlgorithm( 2, bVal );
+}
+
+void SvtSearchOptions::SetSearchForStyles( bool bVal )
+{
+ pImpl->SetFlag( 3, bVal );
+}
+
+bool SvtSearchOptions::IsSimilaritySearch() const
+{
+ return pImpl->GetFlag( 4 );
+}
+
+void SvtSearchOptions::SetSimilaritySearch( bool bVal )
+{
+ pImpl->SetSearchAlgorithm( 4, bVal );
+}
+
+bool SvtSearchOptions::IsUseAsianOptions() const
+{
+ return pImpl->GetFlag( 5 );
+}
+
+void SvtSearchOptions::SetUseAsianOptions( bool bVal )
+{
+ pImpl->SetFlag( 5, bVal );
+}
+
+bool SvtSearchOptions::IsMatchCase() const
+{
+ return pImpl->GetFlag( 6 );
+}
+
+void SvtSearchOptions::SetMatchCase( bool bVal )
+{
+ pImpl->SetFlag( 6, bVal );
+}
+
+bool SvtSearchOptions::IsMatchFullHalfWidthForms() const
+{
+ return pImpl->GetFlag( 7 );
+}
+
+void SvtSearchOptions::SetMatchFullHalfWidthForms( bool bVal )
+{
+ pImpl->SetFlag( 7, bVal );
+}
+
+bool SvtSearchOptions::IsMatchHiraganaKatakana() const
+{
+ return pImpl->GetFlag( 8 );
+}
+
+void SvtSearchOptions::SetMatchHiraganaKatakana( bool bVal )
+{
+ pImpl->SetFlag( 8, bVal );
+}
+
+bool SvtSearchOptions::IsMatchContractions() const
+{
+ return pImpl->GetFlag( 9 );
+}
+
+void SvtSearchOptions::SetMatchContractions( bool bVal )
+{
+ pImpl->SetFlag( 9, bVal );
+}
+
+bool SvtSearchOptions::IsMatchMinusDashChoon() const
+{
+ return pImpl->GetFlag( 10 );
+}
+
+void SvtSearchOptions::SetMatchMinusDashChoon( bool bVal )
+{
+ pImpl->SetFlag( 10, bVal );
+}
+
+bool SvtSearchOptions::IsMatchRepeatCharMarks() const
+{
+ return pImpl->GetFlag( 11 );
+}
+
+void SvtSearchOptions::SetMatchRepeatCharMarks( bool bVal )
+{
+ pImpl->SetFlag( 11, bVal );
+}
+
+bool SvtSearchOptions::IsMatchVariantFormKanji() const
+{
+ return pImpl->GetFlag( 12 );
+}
+
+void SvtSearchOptions::SetMatchVariantFormKanji( bool bVal )
+{
+ pImpl->SetFlag( 12, bVal );
+}
+
+bool SvtSearchOptions::IsMatchOldKanaForms() const
+{
+ return pImpl->GetFlag( 13 );
+}
+
+void SvtSearchOptions::SetMatchOldKanaForms( bool bVal )
+{
+ pImpl->SetFlag( 13, bVal );
+}
+
+bool SvtSearchOptions::IsMatchDiziDuzu() const
+{
+ return pImpl->GetFlag( 14 );
+}
+
+void SvtSearchOptions::SetMatchDiziDuzu( bool bVal )
+{
+ pImpl->SetFlag( 14, bVal );
+}
+
+bool SvtSearchOptions::IsMatchBavaHafa() const
+{
+ return pImpl->GetFlag( 15 );
+}
+
+void SvtSearchOptions::SetMatchBavaHafa( bool bVal )
+{
+ pImpl->SetFlag( 15, bVal );
+}
+
+bool SvtSearchOptions::IsMatchTsithichiDhizi() const
+{
+ return pImpl->GetFlag( 16 );
+}
+
+void SvtSearchOptions::SetMatchTsithichiDhizi( bool bVal )
+{
+ pImpl->SetFlag( 16, bVal );
+}
+
+bool SvtSearchOptions::IsMatchHyuiyuByuvyu() const
+{
+ return pImpl->GetFlag( 17 );
+}
+
+void SvtSearchOptions::SetMatchHyuiyuByuvyu( bool bVal )
+{
+ pImpl->SetFlag( 17, bVal );
+}
+
+bool SvtSearchOptions::IsMatchSesheZeje() const
+{
+ return pImpl->GetFlag( 18 );
+}
+
+void SvtSearchOptions::SetMatchSesheZeje( bool bVal )
+{
+ pImpl->SetFlag( 18, bVal );
+}
+
+bool SvtSearchOptions::IsMatchIaiya() const
+{
+ return pImpl->GetFlag( 19 );
+}
+
+void SvtSearchOptions::SetMatchIaiya( bool bVal )
+{
+ pImpl->SetFlag( 19, bVal );
+}
+
+bool SvtSearchOptions::IsMatchKiku() const
+{
+ return pImpl->GetFlag( 20 );
+}
+
+void SvtSearchOptions::SetMatchKiku( bool bVal )
+{
+ pImpl->SetFlag( 20, bVal );
+}
+
+bool SvtSearchOptions::IsIgnorePunctuation() const
+{
+ return pImpl->GetFlag( 21 );
+}
+
+void SvtSearchOptions::SetIgnorePunctuation( bool bVal )
+{
+ pImpl->SetFlag( 21, bVal );
+}
+
+bool SvtSearchOptions::IsIgnoreWhitespace() const
+{
+ return pImpl->GetFlag( 22 );
+}
+
+void SvtSearchOptions::SetIgnoreWhitespace( bool bVal )
+{
+ pImpl->SetFlag( 22, bVal );
+}
+
+bool SvtSearchOptions::IsIgnoreProlongedSoundMark() const
+{
+ return pImpl->GetFlag( 23 );
+}
+
+void SvtSearchOptions::SetIgnoreProlongedSoundMark( bool bVal )
+{
+ pImpl->SetFlag( 23, bVal );
+}
+
+bool SvtSearchOptions::IsIgnoreMiddleDot() const
+{
+ return pImpl->GetFlag( 24 );
+}
+
+void SvtSearchOptions::SetIgnoreMiddleDot( bool bVal )
+{
+ pImpl->SetFlag( 24, bVal );
+}
+
+bool SvtSearchOptions::IsNotes() const
+{
+ return pImpl->GetFlag( 25 );
+}
+
+void SvtSearchOptions::SetNotes( bool bVal )
+{
+ pImpl->SetFlag( 25, bVal );
+}
+
+bool SvtSearchOptions::IsIgnoreDiacritics_CTL() const
+{
+ return pImpl->GetFlag( 26 );
+}
+
+void SvtSearchOptions::SetIgnoreDiacritics_CTL( bool bVal )
+{
+ pImpl->SetFlag( 26, bVal );
+}
+
+bool SvtSearchOptions::IsIgnoreKashida_CTL() const
+{
+ return pImpl->GetFlag( 27 );
+}
+
+void SvtSearchOptions::SetIgnoreKashida_CTL( bool bVal )
+{
+ pImpl->SetFlag( 27, bVal );
+}
+
+bool SvtSearchOptions::IsSearchFormatted() const
+{
+ return pImpl->GetFlag( 28 );
+}
+
+void SvtSearchOptions::SetSearchFormatted( bool bVal )
+{
+ pImpl->SetFlag( 28, bVal );
+}
+
+bool SvtSearchOptions::IsUseWildcard() const
+{
+ return pImpl->GetFlag( 29 );
+}
+
+void SvtSearchOptions::SetUseWildcard( bool bVal )
+{
+ pImpl->SetSearchAlgorithm( 29, bVal );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/securityoptions.cxx b/unotools/source/config/securityoptions.cxx
new file mode 100644
index 0000000000..1c2070ab95
--- /dev/null
+++ b/unotools/source/config/securityoptions.cxx
@@ -0,0 +1,410 @@
+/* -*- 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/securityoptions.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/configitem.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <tools/urlobj.hxx>
+
+#include <unotools/pathoptions.hxx>
+#include <officecfg/Office/Common.hxx>
+
+// namespaces
+
+using namespace ::com::sun::star::uno;
+
+constexpr OUString PROPERTYNAME_MACRO_TRUSTEDAUTHORS = u"TrustedAuthors"_ustr;
+constexpr OUString PROPERTYNAME_TRUSTEDAUTHOR_SUBJECTNAME = u"SubjectName"_ustr;
+constexpr OUString PROPERTYNAME_TRUSTEDAUTHOR_SERIALNUMBER = u"SerialNumber"_ustr;
+constexpr OUString PROPERTYNAME_TRUSTEDAUTHOR_RAWDATA = u"RawData"_ustr;
+
+
+namespace SvtSecurityOptions
+{
+
+bool IsReadOnly( EOption eOption )
+{
+ bool bReadonly;
+ switch(eOption)
+ {
+ case SvtSecurityOptions::EOption::SecureUrls :
+ bReadonly = officecfg::Office::Common::Security::Scripting::SecureURL::isReadOnly();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnSaveOrSend:
+ bReadonly = officecfg::Office::Common::Security::Scripting::WarnSaveOrSendDoc::isReadOnly();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnSigning:
+ bReadonly = officecfg::Office::Common::Security::Scripting::WarnSignDoc::isReadOnly();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnPrint:
+ bReadonly = officecfg::Office::Common::Security::Scripting::WarnPrintDoc::isReadOnly();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnCreatePdf:
+ bReadonly = officecfg::Office::Common::Security::Scripting::WarnCreatePDF::isReadOnly();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo:
+ bReadonly = officecfg::Office::Common::Security::Scripting::RemovePersonalInfoOnSaving::isReadOnly();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnKeepRedlineInfo:
+ bReadonly = officecfg::Office::Common::Security::Scripting::KeepRedlineInfoOnSaving::isReadOnly();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnKeepDocUserInfo:
+ bReadonly = officecfg::Office::Common::Security::Scripting::KeepDocUserInfoOnSaving::isReadOnly();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnKeepNoteAuthorDateInfo:
+ bReadonly = officecfg::Office::Common::Security::Scripting::KeepNoteAuthorDateInfoOnSaving::isReadOnly();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnKeepDocVersionInfo:
+ bReadonly = officecfg::Office::Common::Security::Scripting::KeepDocVersionInfoOnSaving::isReadOnly();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnRecommendPassword:
+ bReadonly = officecfg::Office::Common::Security::Scripting::RecommendPasswordProtection::isReadOnly();
+ break;
+ case SvtSecurityOptions::EOption::MacroSecLevel:
+ bReadonly = officecfg::Office::Common::Security::Scripting::MacroSecurityLevel::isReadOnly();
+ break;
+ case SvtSecurityOptions::EOption::MacroTrustedAuthors:
+ // the officecfg does not expose isReadOnly for a ConfigurationSet, so we have to code this ourself
+ bReadonly =
+ comphelper::detail::ConfigurationWrapper::get().isReadOnly(u"/org.openoffice.Office.Common/Security/Scripting/TrustedAuthors"_ustr);
+ break;
+ case SvtSecurityOptions::EOption::CtrlClickHyperlink:
+ bReadonly = officecfg::Office::Common::Security::Scripting::HyperlinksWithCtrlClick::isReadOnly();
+ break;
+ case SvtSecurityOptions::EOption::BlockUntrustedRefererLinks:
+ bReadonly = officecfg::Office::Common::Security::Scripting::BlockUntrustedRefererLinks::isReadOnly();
+ break;
+
+ default:
+ assert(false);
+ bReadonly = true;
+ }
+
+ return bReadonly;
+}
+
+std::vector< OUString > GetSecureURLs()
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return {};
+ std::vector<OUString> aRet = comphelper::sequenceToContainer<std::vector<OUString>>(
+ officecfg::Office::Common::Security::Scripting::SecureURL::get());
+ SvtPathOptions aOpt;
+ std::transform(aRet.begin(), aRet.end(), aRet.begin(),
+ [&aOpt](const OUString& rUrl) -> OUString { return aOpt.SubstituteVariable( rUrl ); });
+ return aRet;
+}
+
+void SetSecureURLs( std::vector< OUString >&& urlList )
+{
+// DBG_ASSERT(!officecfg::SecureURL::isReadOnly(), "SvtSecurityOptions_Impl::SetSecureURLs()\nYou tried to write on a readonly value!\n");
+// if (officecfg::SecureURL::isReadOnly())
+// return;
+ std::vector< OUString > lURLs( std::move(urlList) );
+ SvtPathOptions aOpt;
+ std::transform(lURLs.begin(), lURLs.end(), lURLs.begin(),
+ [&aOpt](const OUString& rUrl) -> OUString { return aOpt.UseVariable( rUrl ); });
+ std::shared_ptr<comphelper::ConfigurationChanges> xChanges = comphelper::ConfigurationChanges::create();
+ officecfg::Office::Common::Security::Scripting::SecureURL::set(comphelper::containerToSequence(lURLs), xChanges);
+ xChanges->commit();
+}
+
+bool isSecureMacroUri(
+ OUString const & uri, OUString const & referer)
+{
+ switch (INetURLObject(uri).GetProtocol()) {
+ case INetProtocol::Macro:
+ if (uri.startsWithIgnoreAsciiCase("macro:///")) {
+ // Denotes an App-BASIC macro (see SfxMacroLoader::loadMacro), which
+ // is considered safe:
+ return true;
+ }
+ [[fallthrough]];
+ case INetProtocol::Slot:
+ return referer.equalsIgnoreAsciiCase("private:user")
+ || isTrustedLocationUri(referer);
+ default:
+ return true;
+ }
+}
+
+bool isUntrustedReferer(OUString const & referer)
+{
+ return IsOptionSet(EOption::BlockUntrustedRefererLinks)
+ && !(referer.isEmpty() || referer.startsWithIgnoreAsciiCase("private:")
+ || isTrustedLocationUri(referer));
+}
+
+bool isTrustedLocationUri(OUString const & uri)
+{
+ for (const auto & url : GetSecureURLs())
+ {
+ if (utl::UCBContentHelper::IsSubPath(url, uri))
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool isTrustedLocationUriForUpdatingLinks(OUString const & uri)
+{
+ return GetMacroSecurityLevel() == 0 || uri.isEmpty()
+ || uri.startsWithIgnoreAsciiCase("private:")
+ || isTrustedLocationUri(uri);
+}
+
+sal_Int32 GetMacroSecurityLevel()
+{
+ return utl::ConfigManager::IsFuzzing() ? 3 : officecfg::Office::Common::Security::Scripting::MacroSecurityLevel::get();
+}
+
+void SetMacroSecurityLevel( sal_Int32 _nLevel )
+{
+ if (utl::ConfigManager::IsFuzzing() || officecfg::Office::Common::Security::Scripting::MacroSecurityLevel::isReadOnly())
+ return;
+
+ if( _nLevel > 3 || _nLevel < 0 )
+ _nLevel = 3;
+
+ std::shared_ptr<comphelper::ConfigurationChanges> xChanges = comphelper::ConfigurationChanges::create();
+ officecfg::Office::Common::Security::Scripting::MacroSecurityLevel::set(_nLevel, xChanges);
+ xChanges->commit();
+}
+
+bool IsMacroDisabled()
+{
+ return utl::ConfigManager::IsFuzzing() || officecfg::Office::Common::Security::Scripting::DisableMacrosExecution::get();
+}
+
+std::vector< SvtSecurityOptions::Certificate > GetTrustedAuthors()
+{
+ Reference<css::container::XHierarchicalNameAccess> xHierarchyAccess = utl::ConfigManager::acquireTree(u"Office.Common/Security/Scripting");
+ const Sequence< OUString > lAuthors = utl::ConfigItem::GetNodeNames( xHierarchyAccess, PROPERTYNAME_MACRO_TRUSTEDAUTHORS, utl::ConfigNameFormat::LocalPath );
+ sal_Int32 c1 = lAuthors.getLength();
+ if( !c1 )
+ return {};
+
+ sal_Int32 c2 = c1 * 3; // 3 Properties inside Struct TrustedAuthor
+ Sequence< OUString > lAllAuthors( c2 );
+ auto plAllAuthors = lAllAuthors.getArray();
+ sal_Int32 i2 = 0;
+ OUString aSep( "/" );
+ for( const auto& rAuthor : lAuthors )
+ {
+ plAllAuthors[ i2 ] = PROPERTYNAME_MACRO_TRUSTEDAUTHORS + aSep + rAuthor + aSep + PROPERTYNAME_TRUSTEDAUTHOR_SUBJECTNAME;
+ ++i2;
+ plAllAuthors[ i2 ] = PROPERTYNAME_MACRO_TRUSTEDAUTHORS + aSep + rAuthor + aSep + PROPERTYNAME_TRUSTEDAUTHOR_SERIALNUMBER;
+ ++i2;
+ plAllAuthors[ i2 ] = PROPERTYNAME_MACRO_TRUSTEDAUTHORS + aSep + rAuthor + aSep + PROPERTYNAME_TRUSTEDAUTHOR_RAWDATA;
+ ++i2;
+ }
+
+ Sequence< Any > lValues = utl::ConfigItem::GetProperties( xHierarchyAccess, lAllAuthors, /*bAllLocales*/false );
+ if( lValues.getLength() != c2 )
+ return {};
+
+ std::vector< SvtSecurityOptions::Certificate > aTrustedAuthors;
+ SvtSecurityOptions::Certificate aCert;
+ i2 = 0;
+ for( sal_Int32 i1 = 0; i1 < c1; ++i1 )
+ {
+ lValues[ i2 ] >>= aCert.SubjectName;
+ ++i2;
+ lValues[ i2 ] >>= aCert.SerialNumber;
+ ++i2;
+ lValues[ i2 ] >>= aCert.RawData;
+ ++i2;
+ // Filter out TrustedAuthor entries with empty RawData, which
+ // would cause an unexpected std::bad_alloc in
+ // SecurityEnvironment_NssImpl::createCertificateFromAscii and
+ // have been observed in the wild (fdo#55019):
+ if( !aCert.RawData.isEmpty() )
+ {
+ aTrustedAuthors.push_back( aCert );
+ }
+ }
+ return aTrustedAuthors;
+}
+
+void SetTrustedAuthors( const std::vector< Certificate >& rAuthors )
+{
+// DBG_ASSERT(!m_bROTrustedAuthors, "SvtSecurityOptions_Impl::SetTrustedAuthors()\nYou tried to write on a readonly value!\n");
+// if( m_bROTrustedAuthors )
+// return;
+
+ Reference<css::container::XHierarchicalNameAccess> xHierarchyAccess = utl::ConfigManager::acquireTree(u"Office.Common/Security/Scripting");
+
+ // first, clear existing entries
+ {
+ Reference<css::container::XNameContainer> xCont;
+ xHierarchyAccess->getByHierarchicalName(PROPERTYNAME_MACRO_TRUSTEDAUTHORS) >>= xCont;
+ const Sequence< OUString > aNames = xCont->getElementNames();
+ Reference<css::util::XChangesBatch> xBatch(xHierarchyAccess, UNO_QUERY);
+ for (const OUString& rName : aNames)
+ xCont->removeByName(rName);
+ xBatch->commitChanges();
+ }
+
+ sal_Int32 nCnt = rAuthors.size();
+ for( sal_Int32 i = 0; i < nCnt; ++i )
+ {
+ OUString aPrefix(
+ PROPERTYNAME_MACRO_TRUSTEDAUTHORS + "/a"
+ + OUString::number(i) + "/");
+ Sequence< css::beans::PropertyValue > lPropertyValues{
+ comphelper::makePropertyValue(aPrefix + PROPERTYNAME_TRUSTEDAUTHOR_SUBJECTNAME,
+ rAuthors[i].SubjectName),
+ comphelper::makePropertyValue(aPrefix + PROPERTYNAME_TRUSTEDAUTHOR_SERIALNUMBER,
+ rAuthors[i].SerialNumber),
+ comphelper::makePropertyValue(aPrefix + PROPERTYNAME_TRUSTEDAUTHOR_RAWDATA,
+ rAuthors[i].RawData)
+ };
+
+ utl::ConfigItem::SetSetProperties( xHierarchyAccess, PROPERTYNAME_MACRO_TRUSTEDAUTHORS, lPropertyValues );
+ }
+}
+
+bool IsOptionSet( EOption eOption )
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return false;
+ bool bSet = false;
+ switch(eOption)
+ {
+ case SvtSecurityOptions::EOption::DocWarnSaveOrSend:
+ bSet = officecfg::Office::Common::Security::Scripting::WarnSaveOrSendDoc::get();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnSigning:
+ bSet = officecfg::Office::Common::Security::Scripting::WarnSignDoc::get();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnPrint:
+ bSet = officecfg::Office::Common::Security::Scripting::WarnPrintDoc::get();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnCreatePdf:
+ bSet = officecfg::Office::Common::Security::Scripting::WarnCreatePDF::get();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo:
+ bSet = officecfg::Office::Common::Security::Scripting::RemovePersonalInfoOnSaving::get();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnKeepRedlineInfo:
+ bSet = officecfg::Office::Common::Security::Scripting::KeepRedlineInfoOnSaving::get();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnKeepDocUserInfo:
+ bSet = officecfg::Office::Common::Security::Scripting::KeepDocUserInfoOnSaving::get();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnKeepNoteAuthorDateInfo:
+ bSet = officecfg::Office::Common::Security::Scripting::KeepNoteAuthorDateInfoOnSaving::get();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnKeepDocVersionInfo:
+ bSet = officecfg::Office::Common::Security::Scripting::KeepDocVersionInfoOnSaving::get();
+ break;
+ case SvtSecurityOptions::EOption::DocWarnRecommendPassword:
+ bSet = officecfg::Office::Common::Security::Scripting::RecommendPasswordProtection::get();
+ break;
+ case SvtSecurityOptions::EOption::CtrlClickHyperlink:
+ bSet = officecfg::Office::Common::Security::Scripting::HyperlinksWithCtrlClick::get();
+ break;
+ case SvtSecurityOptions::EOption::BlockUntrustedRefererLinks:
+ bSet = officecfg::Office::Common::Security::Scripting::BlockUntrustedRefererLinks::get();
+ break;
+
+ default:
+ assert(false);
+ }
+
+ return bSet;
+}
+
+void SetOption( EOption eOption, bool bValue )
+{
+ std::shared_ptr<comphelper::ConfigurationChanges> xChanges = comphelper::ConfigurationChanges::create();
+ switch(eOption)
+ {
+ case SvtSecurityOptions::EOption::DocWarnSaveOrSend:
+ officecfg::Office::Common::Security::Scripting::WarnSaveOrSendDoc::set(bValue, xChanges);
+ break;
+ case SvtSecurityOptions::EOption::DocWarnSigning:
+ officecfg::Office::Common::Security::Scripting::WarnSignDoc::set(bValue, xChanges);
+ break;
+ case SvtSecurityOptions::EOption::DocWarnPrint:
+ officecfg::Office::Common::Security::Scripting::WarnPrintDoc::set(bValue, xChanges);
+ break;
+ case SvtSecurityOptions::EOption::DocWarnCreatePdf:
+ officecfg::Office::Common::Security::Scripting::WarnCreatePDF::set(bValue, xChanges);
+ break;
+ case SvtSecurityOptions::EOption::DocWarnRemovePersonalInfo:
+ officecfg::Office::Common::Security::Scripting::RemovePersonalInfoOnSaving::set(bValue, xChanges);
+ break;
+ case SvtSecurityOptions::EOption::DocWarnKeepRedlineInfo:
+ officecfg::Office::Common::Security::Scripting::KeepRedlineInfoOnSaving::set(bValue, xChanges);
+ break;
+ case SvtSecurityOptions::EOption::DocWarnKeepDocUserInfo:
+ officecfg::Office::Common::Security::Scripting::KeepDocUserInfoOnSaving::set(bValue, xChanges);
+ break;
+ case SvtSecurityOptions::EOption::DocWarnKeepNoteAuthorDateInfo:
+ officecfg::Office::Common::Security::Scripting::KeepNoteAuthorDateInfoOnSaving::set(bValue, xChanges);
+ break;
+ case SvtSecurityOptions::EOption::DocWarnKeepDocVersionInfo:
+ officecfg::Office::Common::Security::Scripting::KeepDocVersionInfoOnSaving::set(bValue, xChanges);
+ break;
+ case SvtSecurityOptions::EOption::DocWarnRecommendPassword:
+ officecfg::Office::Common::Security::Scripting::RecommendPasswordProtection::set(bValue, xChanges);
+ break;
+ case SvtSecurityOptions::EOption::CtrlClickHyperlink:
+ officecfg::Office::Common::Security::Scripting::HyperlinksWithCtrlClick::set(bValue, xChanges);
+ break;
+ case SvtSecurityOptions::EOption::BlockUntrustedRefererLinks:
+ officecfg::Office::Common::Security::Scripting::BlockUntrustedRefererLinks::set(bValue, xChanges);
+ break;
+
+ default:
+ assert(false);
+ }
+ xChanges->commit();
+}
+
+} // namespace SvtSecurityOptions
+
+
+// map personal info strings to 1, 2, ... to remove personal info
+size_t SvtSecurityMapPersonalInfo::GetInfoID( const OUString sPersonalInfo )
+{
+ auto aIter = aInfoIDs.find( sPersonalInfo );
+ if ( aIter == aInfoIDs.end() )
+ {
+ size_t nNewID = aInfoIDs.size() + 1;
+ aInfoIDs[sPersonalInfo] = nNewID;
+ return nNewID;
+ }
+
+ return aIter->second;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/syslocaleoptions.cxx b/unotools/source/config/syslocaleoptions.cxx
new file mode 100644
index 0000000000..70177c5c05
--- /dev/null
+++ b/unotools/source/config/syslocaleoptions.cxx
@@ -0,0 +1,718 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <sal/log.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <tools/debug.hxx>
+#include <tools/link.hxx>
+#include <unotools/syslocaleoptions.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/configitem.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+#include "itemholder1.hxx"
+
+#define CFG_READONLY_DEFAULT false
+
+using namespace osl;
+using namespace utl;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+
+namespace
+{
+ std::weak_ptr<SvtSysLocaleOptions_Impl> g_pSysLocaleOptions;
+ Link<LinkParamNone*,void> g_CurrencyChangeLink;
+
+Mutex& GetMutex()
+{
+ // #i77768# Due to a static reference in the toolkit lib
+ // we need a mutex that lives longer than the svl library.
+ // Otherwise the dtor would use a destructed mutex!!
+ static Mutex* persistentMutex(new Mutex);
+
+ return *persistentMutex;
+}
+
+}
+
+class SvtSysLocaleOptions_Impl : public utl::ConfigItem
+{
+ LanguageTag m_aRealLocale;
+ LanguageTag m_aRealUILocale;
+ OUString m_aLocaleString; // en-US or de-DE or empty for SYSTEM
+ OUString m_aUILocaleString; // en-US or de-DE or empty for SYSTEM
+ OUString m_aCurrencyString; // USD-en-US or EUR-de-DE
+ OUString m_aDatePatternsString; // "Y-M-D;M-D"
+ bool m_bDecimalSeparator; //use decimal separator same as locale
+ bool m_bIgnoreLanguageChange; //OS language change doesn't affect LO document language
+
+ bool m_bROLocale;
+ bool m_bROUILocale;
+ bool m_bROCurrency;
+ bool m_bRODatePatterns;
+ bool m_bRODecimalSeparator;
+ bool m_bROIgnoreLanguageChange;
+
+ static Sequence<OUString> GetPropertyNames();
+ void MakeRealLocale();
+ void MakeRealUILocale();
+
+ virtual void ImplCommit() override;
+
+public:
+ SvtSysLocaleOptions_Impl();
+ virtual ~SvtSysLocaleOptions_Impl() override;
+
+ virtual void Notify( const css::uno::Sequence< OUString >& aPropertyNames ) override;
+
+ const OUString& GetLocaleString() const
+ { return m_aLocaleString; }
+ void SetLocaleString( const OUString& rStr );
+
+ void SetUILocaleString( const OUString& rStr );
+
+ const OUString& GetCurrencyString() const
+ { return m_aCurrencyString; }
+ void SetCurrencyString( const OUString& rStr );
+
+ const OUString& GetDatePatternsString() const
+ { return m_aDatePatternsString; }
+ void SetDatePatternsString( const OUString& rStr );
+
+ bool IsDecimalSeparatorAsLocale() const { return m_bDecimalSeparator;}
+ void SetDecimalSeparatorAsLocale( bool bSet);
+
+ bool IsIgnoreLanguageChange() const { return m_bIgnoreLanguageChange;}
+ void SetIgnoreLanguageChange( bool bSet);
+
+ bool IsReadOnly( SvtSysLocaleOptions::EOption eOption ) const;
+ const LanguageTag& GetRealLocale() const { return m_aRealLocale; }
+ const LanguageTag& GetRealUILocale() const { return m_aRealUILocale; }
+};
+
+constexpr OUStringLiteral ROOTNODE_SYSLOCALE = u"Setup/L10N";
+
+constexpr OUString PROPERTYNAME_LOCALE = u"ooSetupSystemLocale"_ustr;
+constexpr OUString PROPERTYNAME_UILOCALE = u"ooLocale"_ustr;
+constexpr OUString PROPERTYNAME_CURRENCY = u"ooSetupCurrency"_ustr;
+constexpr OUString PROPERTYNAME_DECIMALSEPARATOR = u"DecimalSeparatorAsLocale"_ustr;
+constexpr OUString PROPERTYNAME_DATEPATTERNS = u"DateAcceptancePatterns"_ustr;
+constexpr OUString PROPERTYNAME_IGNORELANGCHANGE = u"IgnoreLanguageChange"_ustr;
+
+#define PROPERTYHANDLE_LOCALE 0
+#define PROPERTYHANDLE_UILOCALE 1
+#define PROPERTYHANDLE_CURRENCY 2
+#define PROPERTYHANDLE_DECIMALSEPARATOR 3
+#define PROPERTYHANDLE_DATEPATTERNS 4
+#define PROPERTYHANDLE_IGNORELANGCHANGE 5
+
+Sequence< OUString > SvtSysLocaleOptions_Impl::GetPropertyNames()
+{
+ return Sequence< OUString >
+ {
+ PROPERTYNAME_LOCALE,
+ PROPERTYNAME_UILOCALE,
+ PROPERTYNAME_CURRENCY,
+ PROPERTYNAME_DECIMALSEPARATOR,
+ PROPERTYNAME_DATEPATTERNS,
+ PROPERTYNAME_IGNORELANGCHANGE
+ };
+}
+
+SvtSysLocaleOptions_Impl::SvtSysLocaleOptions_Impl()
+ : ConfigItem( ROOTNODE_SYSLOCALE )
+ , m_aRealLocale( LANGUAGE_SYSTEM)
+ , m_aRealUILocale( LANGUAGE_SYSTEM)
+ , m_bDecimalSeparator( true )
+ , m_bIgnoreLanguageChange( false)
+ , m_bROLocale(CFG_READONLY_DEFAULT)
+ , m_bROUILocale(CFG_READONLY_DEFAULT)
+ , m_bROCurrency(CFG_READONLY_DEFAULT)
+ , m_bRODatePatterns(CFG_READONLY_DEFAULT)
+ , m_bRODecimalSeparator(false)
+ , m_bROIgnoreLanguageChange(false)
+
+{
+ const Sequence< OUString > aNames = GetPropertyNames();
+ Sequence< Any > aValues = GetProperties( aNames );
+ Sequence< sal_Bool > aROStates = GetReadOnlyStates( aNames );
+ const Any* pValues = aValues.getConstArray();
+ const sal_Bool* pROStates = aROStates.getConstArray();
+ DBG_ASSERT( aValues.getLength() == aNames.getLength(), "GetProperties failed" );
+ DBG_ASSERT( aROStates.getLength() == aNames.getLength(), "GetReadOnlyStates failed" );
+ if ( aValues.getLength() == aNames.getLength() && aROStates.getLength() == aNames.getLength() )
+ {
+ for ( sal_Int32 nProp = 0; nProp < aNames.getLength(); nProp++ )
+ {
+ if ( pValues[nProp].hasValue() )
+ {
+ switch ( nProp )
+ {
+ case PROPERTYHANDLE_LOCALE :
+ {
+ OUString aStr;
+ if ( pValues[nProp] >>= aStr )
+ m_aLocaleString = aStr;
+ else
+ {
+ SAL_WARN( "unotools.config", "Wrong property type!" );
+ }
+ m_bROLocale = pROStates[nProp];
+ }
+ break;
+ case PROPERTYHANDLE_UILOCALE :
+ {
+ OUString aStr;
+ if ( pValues[nProp] >>= aStr )
+ m_aUILocaleString = aStr;
+ else
+ {
+ SAL_WARN( "unotools.config", "Wrong property type!" );
+ }
+ m_bROUILocale = pROStates[nProp];
+ }
+ break;
+ case PROPERTYHANDLE_CURRENCY :
+ {
+ OUString aStr;
+ if ( pValues[nProp] >>= aStr )
+ m_aCurrencyString = aStr;
+ else
+ {
+ SAL_WARN( "unotools.config", "Wrong property type!" );
+ }
+ m_bROCurrency = pROStates[nProp];
+ }
+ break;
+ case PROPERTYHANDLE_DECIMALSEPARATOR:
+ {
+ bool bValue = false;
+ if ( pValues[nProp] >>= bValue )
+ m_bDecimalSeparator = bValue;
+ else
+ {
+ SAL_WARN( "unotools.config", "Wrong property type!" );
+ }
+ m_bRODecimalSeparator = pROStates[nProp];
+ }
+ break;
+ case PROPERTYHANDLE_DATEPATTERNS :
+ {
+ OUString aStr;
+ if ( pValues[nProp] >>= aStr )
+ m_aDatePatternsString = aStr;
+ else
+ {
+ SAL_WARN( "unotools.config", "Wrong property type!" );
+ }
+ m_bRODatePatterns = pROStates[nProp];
+ }
+ break;
+ case PROPERTYHANDLE_IGNORELANGCHANGE :
+ {
+ bool bValue = false;
+ if ( pValues[nProp] >>= bValue )
+ m_bIgnoreLanguageChange = bValue;
+ else
+ {
+ SAL_WARN( "unotools.config", "Wrong property type!" );
+ }
+ m_bROIgnoreLanguageChange = pROStates[nProp];
+ }
+ break;
+ default:
+ SAL_WARN( "unotools.config", "Wrong property type!" );
+ }
+ }
+ }
+ }
+ EnableNotification( aNames );
+
+ MakeRealLocale();
+ MakeRealUILocale();
+}
+
+SvtSysLocaleOptions_Impl::~SvtSysLocaleOptions_Impl()
+{
+ assert(!IsModified()); // should have been committed
+}
+
+void SvtSysLocaleOptions_Impl::MakeRealLocale()
+{
+ if (m_aLocaleString.isEmpty())
+ {
+ LanguageType nLang = MsLangId::getConfiguredSystemLanguage();
+ m_aRealLocale.reset( nLang).makeFallback();
+ }
+ else
+ {
+ m_aRealLocale.reset( m_aLocaleString).makeFallback();
+ }
+}
+
+void SvtSysLocaleOptions_Impl::MakeRealUILocale()
+{
+ if (m_aUILocaleString.isEmpty())
+ {
+ LanguageType nLang = MsLangId::getConfiguredSystemUILanguage();
+ m_aRealUILocale.reset( nLang).makeFallback();
+ }
+ else
+ {
+ m_aRealUILocale.reset( m_aUILocaleString).makeFallback();
+ }
+}
+
+bool SvtSysLocaleOptions_Impl::IsReadOnly( SvtSysLocaleOptions::EOption eOption ) const
+{
+ bool bReadOnly = CFG_READONLY_DEFAULT;
+ switch(eOption)
+ {
+ case SvtSysLocaleOptions::EOption::Locale :
+ {
+ bReadOnly = m_bROLocale;
+ break;
+ }
+ case SvtSysLocaleOptions::EOption::Currency :
+ {
+ bReadOnly = m_bROCurrency;
+ break;
+ }
+ case SvtSysLocaleOptions::EOption::DatePatterns :
+ {
+ bReadOnly = m_bRODatePatterns;
+ break;
+ }
+ case SvtSysLocaleOptions::EOption::DecimalSeparator:
+ {
+ bReadOnly = m_bRODecimalSeparator;
+ break;
+ }
+ case SvtSysLocaleOptions::EOption::IgnoreLanguageChange:
+ {
+ bReadOnly = m_bROIgnoreLanguageChange;
+ break;
+ }
+ }
+ return bReadOnly;
+}
+
+void SvtSysLocaleOptions_Impl::ImplCommit()
+{
+ const Sequence< OUString > aOrgNames = GetPropertyNames();
+ sal_Int32 nOrgCount = aOrgNames.getLength();
+
+ Sequence< OUString > aNames( nOrgCount );
+ Sequence< Any > aValues( nOrgCount );
+
+ OUString* pNames = aNames.getArray();
+ Any* pValues = aValues.getArray();
+ sal_Int32 nRealCount = 0;
+
+ for ( sal_Int32 nProp = 0; nProp < nOrgCount; nProp++ )
+ {
+ switch ( nProp )
+ {
+ case PROPERTYHANDLE_LOCALE :
+ {
+ if (!m_bROLocale)
+ {
+ pNames[nRealCount] = aOrgNames[nProp];
+ pValues[nRealCount] <<= m_aLocaleString;
+ ++nRealCount;
+ }
+ }
+ break;
+ case PROPERTYHANDLE_UILOCALE :
+ {
+ if (!m_bROUILocale)
+ {
+ pNames[nRealCount] = aOrgNames[nProp];
+ pValues[nRealCount] <<= m_aUILocaleString;
+ ++nRealCount;
+ }
+ }
+ break;
+ case PROPERTYHANDLE_CURRENCY :
+ {
+ if (!m_bROCurrency)
+ {
+ pNames[nRealCount] = aOrgNames[nProp];
+ pValues[nRealCount] <<= m_aCurrencyString;
+ ++nRealCount;
+ }
+ }
+ break;
+ case PROPERTYHANDLE_DECIMALSEPARATOR:
+ if( !m_bRODecimalSeparator )
+ {
+ pNames[nRealCount] = aOrgNames[nProp];
+ pValues[nRealCount] <<= m_bDecimalSeparator;
+ ++nRealCount;
+ }
+ break;
+ case PROPERTYHANDLE_DATEPATTERNS :
+ if (!m_bRODatePatterns)
+ {
+ pNames[nRealCount] = aOrgNames[nProp];
+ pValues[nRealCount] <<= m_aDatePatternsString;
+ ++nRealCount;
+ }
+ break;
+ case PROPERTYHANDLE_IGNORELANGCHANGE :
+ if (!m_bROIgnoreLanguageChange)
+ {
+ pNames[nRealCount] = aOrgNames[nProp];
+ pValues[nRealCount] <<= m_bIgnoreLanguageChange;
+ ++nRealCount;
+ }
+ break;
+ default:
+ SAL_WARN( "unotools.config", "invalid index to save a path" );
+ }
+ }
+ aNames.realloc(nRealCount);
+ aValues.realloc(nRealCount);
+ PutProperties( aNames, aValues );
+}
+
+void SvtSysLocaleOptions_Impl::SetLocaleString( const OUString& rStr )
+{
+ ConfigurationHints nHint = ConfigurationHints::Locale;
+ {
+ MutexGuard aGuard( GetMutex() );
+ if (m_bROLocale || rStr == m_aLocaleString )
+ {
+ return;
+ }
+ m_aLocaleString = rStr;
+ MakeRealLocale();
+ LanguageTag::setConfiguredSystemLanguage( m_aRealLocale.getLanguageType() );
+ SetModified();
+ if ( m_aCurrencyString.isEmpty() )
+ nHint |= ConfigurationHints::Currency;
+ }
+ NotifyListeners( nHint );
+}
+
+void SvtSysLocaleOptions_Impl::SetUILocaleString( const OUString& rStr )
+{
+ {
+ MutexGuard aGuard( GetMutex() );
+ if (m_bROUILocale || rStr == m_aUILocaleString )
+ {
+ return;
+ }
+ m_aUILocaleString = rStr;
+
+ // as we can't switch UILocale at runtime, we only store changes in the configuration
+ MakeRealUILocale();
+ SetModified();
+ }
+ NotifyListeners( ConfigurationHints::UiLocale );
+}
+
+void SvtSysLocaleOptions_Impl::SetCurrencyString( const OUString& rStr )
+{
+ {
+ MutexGuard aGuard( GetMutex() );
+ if (m_bROCurrency || rStr == m_aCurrencyString )
+ {
+ return;
+ }
+ m_aCurrencyString = rStr;
+ SetModified();
+ }
+ NotifyListeners( ConfigurationHints::Currency );
+}
+
+void SvtSysLocaleOptions_Impl::SetDatePatternsString( const OUString& rStr )
+{
+ {
+ MutexGuard aGuard( GetMutex() );
+ if (m_bRODatePatterns || rStr == m_aDatePatternsString )
+ {
+ return;
+ }
+ m_aDatePatternsString = rStr;
+ SetModified();
+ }
+ NotifyListeners( ConfigurationHints::DatePatterns );
+}
+
+void SvtSysLocaleOptions_Impl::SetDecimalSeparatorAsLocale( bool bSet)
+{
+ {
+ MutexGuard aGuard( GetMutex() );
+ if(bSet == m_bDecimalSeparator)
+ {
+ return;
+ }
+ m_bDecimalSeparator = bSet;
+ SetModified();
+ }
+ NotifyListeners( ConfigurationHints::DecSep );
+}
+
+void SvtSysLocaleOptions_Impl::SetIgnoreLanguageChange( bool bSet)
+{
+ {
+ MutexGuard aGuard( GetMutex() );
+ if(bSet == m_bIgnoreLanguageChange)
+ {
+ return;
+ }
+ m_bIgnoreLanguageChange = bSet;
+ SetModified();
+ }
+ NotifyListeners( ConfigurationHints::IgnoreLang );
+}
+
+void SvtSysLocaleOptions_Impl::Notify( const Sequence< OUString >& seqPropertyNames )
+{
+ ConfigurationHints nHint = ConfigurationHints::NONE;
+ Sequence< Any > seqValues = GetProperties( seqPropertyNames );
+ Sequence< sal_Bool > seqROStates = GetReadOnlyStates( seqPropertyNames );
+ sal_Int32 nCount = seqPropertyNames.getLength();
+ for( sal_Int32 nProp = 0; nProp < nCount; ++nProp )
+ {
+ if( seqPropertyNames[nProp] == PROPERTYNAME_LOCALE )
+ {
+ DBG_ASSERT( seqValues[nProp].getValueTypeClass() == TypeClass_STRING, "Locale property type" );
+ seqValues[nProp] >>= m_aLocaleString;
+ m_bROLocale = seqROStates[nProp];
+ nHint |= ConfigurationHints::Locale;
+ if ( m_aCurrencyString.isEmpty() )
+ nHint |= ConfigurationHints::Currency;
+ MakeRealLocale();
+ }
+ if( seqPropertyNames[nProp] == PROPERTYNAME_UILOCALE )
+ {
+ DBG_ASSERT( seqValues[nProp].getValueTypeClass() == TypeClass_STRING, "Locale property type" );
+ seqValues[nProp] >>= m_aUILocaleString;
+ m_bROUILocale = seqROStates[nProp];
+ nHint |= ConfigurationHints::UiLocale;
+ MakeRealUILocale();
+ }
+ else if( seqPropertyNames[nProp] == PROPERTYNAME_CURRENCY )
+ {
+ DBG_ASSERT( seqValues[nProp].getValueTypeClass() == TypeClass_STRING, "Currency property type" );
+ seqValues[nProp] >>= m_aCurrencyString;
+ m_bROCurrency = seqROStates[nProp];
+ nHint |= ConfigurationHints::Currency;
+ }
+ else if( seqPropertyNames[nProp] == PROPERTYNAME_DECIMALSEPARATOR )
+ {
+ seqValues[nProp] >>= m_bDecimalSeparator;
+ m_bRODecimalSeparator = seqROStates[nProp];
+ }
+ else if( seqPropertyNames[nProp] == PROPERTYNAME_IGNORELANGCHANGE )
+ {
+ seqValues[nProp] >>= m_bIgnoreLanguageChange;
+ m_bROIgnoreLanguageChange = seqROStates[nProp];
+ }
+ else if( seqPropertyNames[nProp] == PROPERTYNAME_DATEPATTERNS )
+ {
+ DBG_ASSERT( seqValues[nProp].getValueTypeClass() == TypeClass_STRING, "DatePatterns property type" );
+ seqValues[nProp] >>= m_aDatePatternsString;
+ m_bRODatePatterns = seqROStates[nProp];
+ nHint |= ConfigurationHints::DatePatterns;
+ }
+ }
+ if ( nHint != ConfigurationHints::NONE )
+ NotifyListeners( nHint );
+}
+
+SvtSysLocaleOptions::SvtSysLocaleOptions()
+{
+ MutexGuard aGuard( GetMutex() );
+ pImpl = g_pSysLocaleOptions.lock();
+ if ( !pImpl )
+ {
+ pImpl = std::make_shared<SvtSysLocaleOptions_Impl>();
+ g_pSysLocaleOptions = pImpl;
+ if (!utl::ConfigManager::IsFuzzing())
+ ItemHolder1::holdConfigItem(EItem::SysLocaleOptions);
+ }
+ pImpl->AddListener(this);
+}
+
+SvtSysLocaleOptions::~SvtSysLocaleOptions()
+{
+ MutexGuard aGuard( GetMutex() );
+ pImpl->RemoveListener(this);
+ pImpl.reset();
+}
+
+bool SvtSysLocaleOptions::IsModified() const
+{
+ MutexGuard aGuard( GetMutex() );
+ return pImpl->IsModified();
+}
+
+void SvtSysLocaleOptions::Commit()
+{
+ MutexGuard aGuard( GetMutex() );
+ pImpl->Commit();
+}
+
+void SvtSysLocaleOptions::BlockBroadcasts( bool bBlock )
+{
+ MutexGuard aGuard( GetMutex() );
+ pImpl->BlockBroadcasts( bBlock );
+}
+
+void SvtSysLocaleOptions::SetLocaleConfigString( const OUString& rStr )
+{
+ pImpl->SetLocaleString( rStr );
+}
+
+void SvtSysLocaleOptions::SetUILocaleConfigString( const OUString& rStr )
+{
+ pImpl->SetUILocaleString( rStr );
+}
+
+const OUString& SvtSysLocaleOptions::GetCurrencyConfigString() const
+{
+ MutexGuard aGuard( GetMutex() );
+ return pImpl->GetCurrencyString();
+}
+
+void SvtSysLocaleOptions::SetCurrencyConfigString( const OUString& rStr )
+{
+ pImpl->SetCurrencyString( rStr );
+}
+
+const OUString& SvtSysLocaleOptions::GetDatePatternsConfigString() const
+{
+ MutexGuard aGuard( GetMutex() );
+ return pImpl->GetDatePatternsString();
+}
+
+void SvtSysLocaleOptions::SetDatePatternsConfigString( const OUString& rStr )
+{
+ pImpl->SetDatePatternsString( rStr );
+}
+
+bool SvtSysLocaleOptions::IsDecimalSeparatorAsLocale() const
+{
+ MutexGuard aGuard( GetMutex() );
+ return pImpl->IsDecimalSeparatorAsLocale();
+}
+
+void SvtSysLocaleOptions::SetDecimalSeparatorAsLocale( bool bSet)
+{
+ pImpl->SetDecimalSeparatorAsLocale(bSet);
+}
+
+bool SvtSysLocaleOptions::IsIgnoreLanguageChange() const
+{
+ MutexGuard aGuard( GetMutex() );
+ return pImpl->IsIgnoreLanguageChange();
+}
+
+void SvtSysLocaleOptions::SetIgnoreLanguageChange( bool bSet)
+{
+ pImpl->SetIgnoreLanguageChange(bSet);
+}
+
+bool SvtSysLocaleOptions::IsReadOnly( EOption eOption ) const
+{
+ MutexGuard aGuard( GetMutex() );
+ return pImpl->IsReadOnly( eOption );
+}
+
+// static
+void SvtSysLocaleOptions::GetCurrencyAbbrevAndLanguage( OUString& rAbbrev,
+ LanguageType& eLang,
+ const OUString& rConfigString )
+{
+ sal_Int32 nDelim = rConfigString.indexOf( '-' );
+ if ( nDelim >= 0 )
+ {
+ rAbbrev = rConfigString.copy( 0, nDelim );
+ OUString aIsoStr( rConfigString.copy( nDelim+1 ) );
+ eLang = LanguageTag::convertToLanguageTypeWithFallback( aIsoStr );
+ }
+ else
+ {
+ rAbbrev = rConfigString;
+ eLang = (rAbbrev.isEmpty() ? LANGUAGE_SYSTEM : LANGUAGE_NONE);
+ }
+}
+
+// static
+OUString SvtSysLocaleOptions::CreateCurrencyConfigString(
+ const OUString& rAbbrev, LanguageType eLang )
+{
+ OUString aIsoStr( LanguageTag::convertToBcp47( eLang ) );
+ if ( !aIsoStr.isEmpty() )
+ {
+ return rAbbrev + "-" + aIsoStr;
+ }
+ else
+ return rAbbrev;
+}
+
+// static
+void SvtSysLocaleOptions::SetCurrencyChangeLink( const Link<LinkParamNone*,void>& rLink )
+{
+ MutexGuard aGuard( GetMutex() );
+ DBG_ASSERT( !g_CurrencyChangeLink.IsSet(), "SvtSysLocaleOptions::SetCurrencyChangeLink: already set" );
+ g_CurrencyChangeLink = rLink;
+}
+
+// static
+const Link<LinkParamNone*,void>& SvtSysLocaleOptions::GetCurrencyChangeLink()
+{
+ MutexGuard aGuard( GetMutex() );
+ return g_CurrencyChangeLink;
+}
+
+void SvtSysLocaleOptions::ConfigurationChanged( utl::ConfigurationBroadcaster* p, ConfigurationHints nHint )
+{
+ if ( nHint & ConfigurationHints::Currency )
+ {
+ const Link<LinkParamNone*,void>& rLink = GetCurrencyChangeLink();
+ rLink.Call( nullptr );
+ }
+
+ ::utl::detail::Options::ConfigurationChanged( p, nHint );
+}
+
+LanguageTag SvtSysLocaleOptions::GetLanguageTag() const
+{
+ MutexGuard aGuard( GetMutex() );
+ return LanguageTag( pImpl->GetLocaleString() );
+}
+
+const LanguageTag & SvtSysLocaleOptions::GetRealLanguageTag() const
+{
+ return pImpl->GetRealLocale();
+}
+
+const LanguageTag & SvtSysLocaleOptions::GetRealUILanguageTag() const
+{
+ return pImpl->GetRealUILocale();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/useroptions.cxx b/unotools/source/config/useroptions.cxx
new file mode 100644
index 0000000000..1e181e8813
--- /dev/null
+++ b/unotools/source/config/useroptions.cxx
@@ -0,0 +1,346 @@
+/* -*- 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 <unotools/useroptions.hxx>
+#include <unotools/syslocale.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include "itemholder1.hxx"
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/beans/Property.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/util/XChangesListener.hpp>
+#include <com/sun/star/util/XChangesNotifier.hpp>
+#include <com/sun/star/util/ChangesEvent.hpp>
+#include <comphelper/configurationhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <i18nlangtag/mslangid.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <o3tl/enumarray.hxx>
+#include <o3tl/string_view.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace utl;
+using namespace com::sun::star;
+
+// vOptionNames[] -- names of the user option entries
+// The order must correspond to the enum class UserOptToken in useroptions.hxx.
+static o3tl::enumarray<UserOptToken, char const *> vOptionNames = {
+ "l", // UserOptToken::City
+ "o", // UserOptToken::Company
+ "c", // UserOptToken::Country
+ "mail", // UserOptToken::Email
+ "facsimiletelephonenumber", // UserOptToken::Fax
+ "givenname", // UserOptToken::FirstName
+ "sn", // UserOptToken::LastName
+ "position", // UserOptToken::Position
+ "st", // UserOptToken::State
+ "street", // UserOptToken::Street
+ "homephone", // UserOptToken::TelephoneHome
+ "telephonenumber", // UserOptToken::TelephoneWork
+ "title", // UserOptToken::Title
+ "initials", // UserOptToken::ID
+ "postalcode", // UserOptToken::Zip
+ "fathersname", // UserOptToken::FathersName
+ "apartment", // UserOptToken::Apartment
+ "signingkey", // UserOptToken::SigningKey
+ "encryptionkey", // UserOptToken::EncryptionKey
+ "encrypttoself" // UserOptToken::EncryptToSelf
+};
+
+std::weak_ptr<SvtUserOptions::Impl> SvtUserOptions::xSharedImpl;
+
+class SvtUserOptions::ChangeListener : public cppu::WeakImplHelper<util::XChangesListener>
+{
+public:
+ explicit ChangeListener (Impl& rParent): m_rParent(rParent) { }
+
+ // XChangesListener
+ virtual void SAL_CALL changesOccurred (util::ChangesEvent const& Event) override;
+ // XEventListener
+ virtual void SAL_CALL disposing (lang::EventObject const& Source) override;
+
+private:
+ Impl& m_rParent;
+};
+
+class SvtUserOptions::Impl : public utl::ConfigurationBroadcaster
+{
+public:
+ Impl ();
+
+ OUString GetFullName () const;
+
+ bool IsTokenReadonly (UserOptToken nToken) const;
+ OUString GetToken (UserOptToken nToken) const;
+ void SetToken (UserOptToken nToken, OUString const& rNewToken);
+ bool GetBoolValue (UserOptToken nToken) const;
+ void SetBoolValue (UserOptToken nToken, bool bNewValue);
+ void Notify ();
+
+private:
+ uno::Reference<util::XChangesListener> m_xChangeListener;
+ uno::Reference<container::XNameAccess> m_xCfg;
+ uno::Reference<beans::XPropertySet> m_xData;
+
+ template < typename ValueType >
+ ValueType GetValue_Impl( UserOptToken nToken ) const;
+ template < typename ValueType >
+ void SetValue_Impl( UserOptToken nToken, ValueType const& rNewValue );
+};
+
+void SvtUserOptions::ChangeListener::changesOccurred (util::ChangesEvent const& rEvent)
+{
+ if (rEvent.Changes.hasElements())
+ m_rParent.Notify();
+}
+
+void SvtUserOptions::ChangeListener::disposing (lang::EventObject const& rSource)
+{
+ try
+ {
+ uno::Reference<util::XChangesNotifier> xChgNot(rSource.Source, uno::UNO_QUERY_THROW);
+ xChgNot->removeChangesListener(this);
+ }
+ catch (uno::Exception&)
+ {
+ }
+}
+
+SvtUserOptions::Impl::Impl() :
+ m_xChangeListener( new ChangeListener(*this) )
+{
+ try
+ {
+ m_xCfg.set(
+ comphelper::ConfigurationHelper::openConfig(
+ comphelper::getProcessComponentContext(),
+ "org.openoffice.UserProfile/Data",
+ comphelper::EConfigurationModes::Standard
+ ),
+ uno::UNO_QUERY
+ );
+
+ m_xData.set(m_xCfg, uno::UNO_QUERY);
+ uno::Reference<util::XChangesNotifier> xChgNot(m_xCfg, uno::UNO_QUERY);
+ try
+ {
+ xChgNot->addChangesListener(m_xChangeListener);
+ }
+ catch (uno::RuntimeException&)
+ {
+ }
+ }
+ catch (uno::Exception const&)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools.config");
+ m_xCfg.clear();
+ }
+}
+
+template < typename ValueType >
+ValueType SvtUserOptions::Impl::GetValue_Impl (UserOptToken nToken) const
+{
+ ValueType sToken = ValueType();
+ try
+ {
+ if (m_xData.is())
+ m_xData->getPropertyValue(OUString::createFromAscii(vOptionNames[nToken])) >>= sToken;
+ }
+ catch (uno::Exception const&)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools.config");
+ }
+ return sToken;
+}
+
+template < typename ValueType >
+void SvtUserOptions::Impl::SetValue_Impl (UserOptToken nToken, ValueType const& sToken)
+{
+ try
+ {
+ if (m_xData.is())
+ m_xData->setPropertyValue(OUString::createFromAscii(vOptionNames[nToken]), uno::Any(sToken));
+ comphelper::ConfigurationHelper::flush(m_xCfg);
+ }
+ catch (uno::Exception const&)
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools.config");
+ }
+}
+
+OUString SvtUserOptions::Impl::GetToken (UserOptToken nToken) const
+{
+ return GetValue_Impl<OUString>( nToken );
+}
+
+void SvtUserOptions::Impl::SetToken (UserOptToken nToken, OUString const& sToken)
+{
+ SetValue_Impl<OUString>( nToken, sToken );
+}
+
+bool SvtUserOptions::Impl::GetBoolValue (UserOptToken nToken) const
+{
+ return GetValue_Impl<bool>( nToken );
+}
+
+void SvtUserOptions::Impl::SetBoolValue (UserOptToken nToken, bool bNewValue)
+{
+ SetValue_Impl<bool>( nToken, bNewValue );
+}
+
+OUString SvtUserOptions::Impl::GetFullName () const
+{
+ OUString sFullName;
+ LanguageType const eLang = SvtSysLocale().GetUILanguageTag().getLanguageType();
+ if (eLang == LANGUAGE_RUSSIAN)
+ {
+ sFullName = GetToken(UserOptToken::FirstName).trim();
+ if (!sFullName.isEmpty())
+ sFullName += " ";
+ sFullName += o3tl::trim(GetToken(UserOptToken::FathersName));
+ if (!sFullName.isEmpty())
+ sFullName += " ";
+ sFullName += o3tl::trim(GetToken(UserOptToken::LastName));
+ }
+ else
+ {
+ if (MsLangId::isFamilyNameFirst(eLang))
+ {
+ sFullName = GetToken(UserOptToken::LastName).trim();
+ if (!sFullName.isEmpty())
+ sFullName += " ";
+ sFullName += o3tl::trim(GetToken(UserOptToken::FirstName));
+ }
+ else
+ {
+ sFullName = GetToken(UserOptToken::FirstName).trim();
+ if (!sFullName.isEmpty())
+ sFullName += " ";
+ sFullName += o3tl::trim(GetToken(UserOptToken::LastName));
+ }
+ }
+ sFullName = sFullName.trim();
+
+ return sFullName;
+}
+
+void SvtUserOptions::Impl::Notify ()
+{
+ NotifyListeners(ConfigurationHints::NONE);
+}
+
+bool SvtUserOptions::Impl::IsTokenReadonly (UserOptToken nToken) const
+{
+ uno::Reference<beans::XPropertySet> xData(m_xCfg, uno::UNO_QUERY);
+ uno::Reference<beans::XPropertySetInfo> xInfo = xData->getPropertySetInfo();
+ beans::Property aProp = xInfo->getPropertyByName(OUString::createFromAscii(vOptionNames[nToken]));
+ return ((aProp.Attributes & beans::PropertyAttribute::READONLY) ==
+ beans::PropertyAttribute::READONLY);
+}
+
+static std::recursive_mutex& GetInitMutex()
+{
+ static std::recursive_mutex gMutex;
+ return gMutex;
+}
+
+
+SvtUserOptions::SvtUserOptions ()
+{
+ // Global access, must be guarded (multithreading)
+ std::unique_lock aGuard(GetInitMutex());
+
+ xImpl = xSharedImpl.lock();
+ if (!xImpl)
+ {
+ xImpl = std::make_shared<Impl>();
+ xSharedImpl = xImpl;
+ aGuard.unlock(); // because holdConfigItem will call this constructor
+ ItemHolder1::holdConfigItem(EItem::UserOptions);
+ }
+ xImpl->AddListener(this);
+}
+
+SvtUserOptions::~SvtUserOptions()
+{
+ // Global access, must be guarded (multithreading)
+ std::unique_lock aGuard( GetInitMutex() );
+ xImpl->RemoveListener(this);
+}
+
+OUString SvtUserOptions::GetCompany () const { return GetToken(UserOptToken::Company); }
+OUString SvtUserOptions::GetFirstName () const { return GetToken(UserOptToken::FirstName); }
+OUString SvtUserOptions::GetLastName () const { return GetToken(UserOptToken::LastName); }
+OUString SvtUserOptions::GetID () const { return GetToken(UserOptToken::ID); }
+OUString SvtUserOptions::GetStreet () const { return GetToken(UserOptToken::Street); }
+OUString SvtUserOptions::GetCity () const { return GetToken(UserOptToken::City); }
+OUString SvtUserOptions::GetState () const { return GetToken(UserOptToken::State); }
+OUString SvtUserOptions::GetZip () const { return GetToken(UserOptToken::Zip); }
+OUString SvtUserOptions::GetCountry () const { return GetToken(UserOptToken::Country); }
+OUString SvtUserOptions::GetPosition () const { return GetToken(UserOptToken::Position); }
+OUString SvtUserOptions::GetTitle () const { return GetToken(UserOptToken::Title); }
+OUString SvtUserOptions::GetTelephoneHome () const { return GetToken(UserOptToken::TelephoneHome); }
+OUString SvtUserOptions::GetTelephoneWork () const { return GetToken(UserOptToken::TelephoneWork); }
+OUString SvtUserOptions::GetFax () const { return GetToken(UserOptToken::Fax); }
+OUString SvtUserOptions::GetEmail () const { return GetToken(UserOptToken::Email); }
+OUString SvtUserOptions::GetSigningKey () const { return GetToken(UserOptToken::SigningKey); }
+OUString SvtUserOptions::GetEncryptionKey () const { return GetToken(UserOptToken::EncryptionKey); }
+
+bool SvtUserOptions::IsTokenReadonly (UserOptToken nToken) const
+{
+ std::unique_lock aGuard(GetInitMutex());
+ return xImpl->IsTokenReadonly(nToken);
+}
+
+OUString SvtUserOptions::GetToken (UserOptToken nToken) const
+{
+ std::unique_lock aGuard(GetInitMutex());
+ return xImpl->GetToken(nToken);
+}
+
+void SvtUserOptions::SetToken (UserOptToken nToken, OUString const& rNewToken)
+{
+ std::unique_lock aGuard(GetInitMutex());
+ xImpl->SetToken(nToken, rNewToken);
+}
+
+void SvtUserOptions::SetBoolValue (UserOptToken nToken, bool bNewValue)
+{
+ std::unique_lock aGuard(GetInitMutex());
+ xImpl->SetBoolValue(nToken, bNewValue);
+}
+
+bool SvtUserOptions::GetEncryptToSelf() const
+{
+ std::unique_lock aGuard(GetInitMutex());
+ return xImpl->GetBoolValue(UserOptToken::EncryptToSelf);
+}
+
+OUString SvtUserOptions::GetFullName () const
+{
+ std::unique_lock aGuard(GetInitMutex());
+ return xImpl->GetFullName();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/config/viewoptions.cxx b/unotools/source/config/viewoptions.cxx
new file mode 100644
index 0000000000..382af97b2c
--- /dev/null
+++ b/unotools/source/config/viewoptions.cxx
@@ -0,0 +1,450 @@
+/* -*- 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/viewoptions.hxx>
+#include <com/sun/star/uno/Any.hxx>
+
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <unotools/configmgr.hxx>
+#include <comphelper/configurationhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <utility>
+#include <comphelper/diagnose_ex.hxx>
+
+constexpr OUStringLiteral PACKAGE_VIEWS = u"org.openoffice.Office.Views";
+constexpr OUString PROPERTY_WINDOWSTATE = u"WindowState"_ustr;
+constexpr OUString PROPERTY_PAGEID = u"PageID"_ustr;
+constexpr OUString PROPERTY_VISIBLE = u"Visible"_ustr;
+constexpr OUString PROPERTY_USERDATA = u"UserData"_ustr;
+
+
+SvtViewOptions::SvtViewOptions( EViewType eType, OUString sViewName )
+ : m_eViewType ( eType )
+ , m_sViewName (std::move( sViewName ))
+{
+ (void)m_eViewType; // so the release build does not complain, since we only use it in assert
+ // we must know, which view type we must support
+ switch( eType )
+ {
+ case EViewType::Dialog: m_sListName = "Dialogs"; break;
+ case EViewType::TabDialog: m_sListName = "TabDialogs"; break;
+ case EViewType::TabPage: m_sListName = "TabPages"; break;
+ case EViewType::Window: m_sListName = "Windows"; break;
+ default: assert(false);
+ }
+ if (utl::ConfigManager::IsFuzzing())
+ return;
+
+ try
+ {
+ m_xRoot.set( ::comphelper::ConfigurationHelper::openConfig(
+ ::comphelper::getProcessComponentContext(),
+ PACKAGE_VIEWS,
+ ::comphelper::EConfigurationModes::Standard),
+ css::uno::UNO_QUERY);
+ if (m_xRoot.is())
+ m_xRoot->getByName(m_sListName) >>= m_xSet;
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools", "Unexpected exception");
+ m_xRoot.clear();
+ m_xSet.clear();
+ }
+}
+
+// public method
+
+/*-************************************************************************************************************
+ @short checks for already existing entries
+ @descr If user don't know, if an entry already exist - he can get this information by calling this method.
+
+ @seealso member m_aList
+
+ @param "sName", name of entry to check exist state
+ @return true , if item exist
+ false, otherwise
+*//*-*************************************************************************************************************/
+bool SvtViewOptions::Exists() const
+{
+ bool bExists = false;
+
+ try
+ {
+ if (m_xSet.is())
+ bExists = m_xSet->hasByName(m_sViewName);
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools", "Unexpected exception");
+ bExists = false;
+ }
+
+ return bExists;
+}
+
+// public method
+
+/*-************************************************************************************************************
+ @short delete entry
+ @descr Use it to delete set entry by given name.
+
+ @seealso member m_aList
+
+ @param "sName", name of entry to delete it
+*//*-*************************************************************************************************************/
+void SvtViewOptions::Delete()
+{
+ try
+ {
+ css::uno::Reference< css::container::XNameContainer > xSet(m_xSet, css::uno::UNO_QUERY_THROW);
+ xSet->removeByName(m_sViewName);
+ ::comphelper::ConfigurationHelper::flush(m_xRoot);
+ }
+ catch(const css::container::NoSuchElementException&)
+ { }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools", "Unexpected exception");
+ }
+}
+
+// public method
+
+/*-************************************************************************************************************
+ @short read/write access to cache view items and her properties
+ @descr Follow methods support read/write access to all cache view items.
+
+ @seealso member m_sList
+*//*-*************************************************************************************************************/
+OUString SvtViewOptions::GetWindowState() const
+{
+ OUString sWindowState;
+ try
+ {
+ css::uno::Reference< css::beans::XPropertySet > xNode(
+ impl_getSetNode(m_sViewName, false),
+ css::uno::UNO_QUERY);
+ if (xNode.is())
+ xNode->getPropertyValue(PROPERTY_WINDOWSTATE) >>= sWindowState;
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools", "Unexpected exception");
+ sWindowState.clear();
+ }
+
+ return sWindowState;
+}
+
+
+// public method
+
+/*-************************************************************************************************************
+ @short ctor
+ @descr We use it to open right configuration file and let configuration objects fill her caches.
+ Then we read all existing entries from right list and cached it inside our object too.
+ Normally we should enable notifications for changes on these values too ... but these feature
+ isn't full implemented in the moment.
+
+ @seealso baseclass ::utl::ConfigItem
+ @seealso method Notify()
+*//*-*************************************************************************************************************/
+void SvtViewOptions::SetWindowState( const OUString& sState )
+{
+ try
+ {
+ css::uno::Reference< css::beans::XPropertySet > xNode(
+ impl_getSetNode(m_sViewName, true),
+ css::uno::UNO_QUERY_THROW);
+ xNode->setPropertyValue(PROPERTY_WINDOWSTATE, css::uno::Any(sState));
+ ::comphelper::ConfigurationHelper::flush(m_xRoot);
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools", "Unexpected exception");
+ }
+}
+
+// public method
+
+OUString SvtViewOptions::GetPageID() const
+{
+ // Safe impossible cases.
+ // These call isn't allowed for dialogs, tab-pages or windows!
+ assert( m_eViewType == EViewType::TabDialog && "SvtViewOptions::GetPageID()\nCall not allowed for Dialogs, TabPages or Windows! I do nothing!" );
+
+ OUString sID;
+ try
+ {
+ css::uno::Reference< css::beans::XPropertySet > xNode(
+ impl_getSetNode(m_sViewName, false),
+ css::uno::UNO_QUERY);
+ if (xNode.is())
+ xNode->getPropertyValue(PROPERTY_PAGEID) >>= sID;
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools", "Unexpected exception");
+ }
+
+ return sID;
+}
+
+
+// public method
+
+void SvtViewOptions::SetPageID(const OUString& rID)
+{
+ // Safe impossible cases.
+ // These call isn't allowed for dialogs, tab-pages or windows!
+ assert( m_eViewType == EViewType::TabDialog && "SvtViewOptions::SetPageID()\nCall not allowed for Dialogs, TabPages or Windows! I do nothing!" );
+
+ try
+ {
+ css::uno::Reference< css::beans::XPropertySet > xNode(
+ impl_getSetNode(m_sViewName, true),
+ css::uno::UNO_QUERY_THROW);
+ xNode->setPropertyValue(PROPERTY_PAGEID, css::uno::Any(rID));
+ ::comphelper::ConfigurationHelper::flush(m_xRoot);
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools", "Unexpected exception");
+ }
+}
+
+
+// public method
+
+bool SvtViewOptions::IsVisible() const
+{
+ // Safe impossible cases.
+ // These call isn't allowed for dialogs, tab-dialogs or tab-pages!
+ assert( m_eViewType == EViewType::Window && "SvtViewOptions::IsVisible()\nCall not allowed for Dialogs, TabDialogs or TabPages! I do nothing!" );
+
+ return GetVisible() == STATE_TRUE;
+}
+
+SvtViewOptions::State SvtViewOptions::GetVisible() const
+{
+ State eState = STATE_NONE;
+ try
+ {
+ css::uno::Reference< css::beans::XPropertySet > xNode(
+ impl_getSetNode(m_sViewName, false),
+ css::uno::UNO_QUERY);
+ if (xNode.is())
+ {
+ bool bVisible = false;
+ if (xNode->getPropertyValue(PROPERTY_VISIBLE) >>= bVisible)
+ {
+ eState = bVisible ? STATE_TRUE : STATE_FALSE;
+ }
+ }
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools", "Unexpected exception");
+ }
+ return eState;
+}
+
+// public method
+
+void SvtViewOptions::SetVisible( bool bVisible )
+{
+ // Safe impossible cases.
+ // These call isn't allowed for dialogs, tab-dialogs or tab-pages!
+ assert(m_eViewType == EViewType::Window && "SvtViewOptions::SetVisible()\nCall not allowed for Dialogs, TabDialogs or TabPages! I do nothing!" );
+
+ try
+ {
+ css::uno::Reference< css::beans::XPropertySet > xNode(
+ impl_getSetNode(m_sViewName, true),
+ css::uno::UNO_QUERY_THROW);
+ xNode->setPropertyValue(PROPERTY_VISIBLE, css::uno::Any(bVisible));
+ ::comphelper::ConfigurationHelper::flush(m_xRoot);
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools", "Unexpected exception");
+ }
+}
+
+// public method
+
+bool SvtViewOptions::HasVisible() const
+{
+ // Safe impossible cases.
+ // These call isn't allowed for dialogs, tab-dialogs or tab-pages!
+ assert( m_eViewType == EViewType::Window && "SvtViewOptions::IsVisible()\nCall not allowed for Dialogs, TabDialogs or TabPages! I do nothing!" );
+
+ return GetVisible() != STATE_NONE;
+}
+
+css::uno::Sequence< css::beans::NamedValue > SvtViewOptions::GetUserData() const
+{
+ try
+ {
+ css::uno::Reference< css::container::XNameAccess > xNode(
+ impl_getSetNode(m_sViewName, false),
+ css::uno::UNO_QUERY); // no _THROW ! because we don't create missing items here. So we have to live with zero references .-)
+ css::uno::Reference< css::container::XNameAccess > xUserData;
+ if (xNode.is())
+ xNode->getByName(PROPERTY_USERDATA) >>= xUserData;
+ if (xUserData.is())
+ {
+ const css::uno::Sequence<OUString> lNames = xUserData->getElementNames();
+ sal_Int32 c = lNames.getLength();
+ css::uno::Sequence< css::beans::NamedValue > lUserData(c);
+
+ std::transform(lNames.begin(), lNames.end(), lUserData.getArray(),
+ [&xUserData](const OUString& rName) -> css::beans::NamedValue {
+ return { rName, xUserData->getByName(rName) }; });
+
+ return lUserData;
+ }
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools", "Unexpected exception");
+ }
+
+ return css::uno::Sequence< css::beans::NamedValue >();
+}
+
+void SvtViewOptions::SetUserData( const css::uno::Sequence< css::beans::NamedValue >& lData )
+{
+ try
+ {
+ css::uno::Reference< css::container::XNameAccess > xNode(
+ impl_getSetNode(m_sViewName, true),
+ css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::container::XNameContainer > xUserData;
+ xNode->getByName(PROPERTY_USERDATA) >>= xUserData;
+ if (xUserData.is())
+ {
+ for (const css::beans::NamedValue& rData : lData)
+ {
+ if (xUserData->hasByName(rData.Name))
+ xUserData->replaceByName(rData.Name, rData.Value);
+ else
+ xUserData->insertByName(rData.Name, rData.Value);
+ }
+ }
+ ::comphelper::ConfigurationHelper::flush(m_xRoot);
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools", "Unexpected exception");
+ }
+}
+
+css::uno::Any SvtViewOptions::GetUserItem( const OUString& sItemName ) const
+{
+ css::uno::Any aItem;
+ try
+ {
+ css::uno::Reference< css::container::XNameAccess > xNode(
+ impl_getSetNode(m_sViewName, false),
+ css::uno::UNO_QUERY);
+ css::uno::Reference< css::container::XNameAccess > xUserData;
+ if (xNode.is())
+ xNode->getByName(PROPERTY_USERDATA) >>= xUserData;
+ if (xUserData.is())
+ aItem = xUserData->getByName(sItemName);
+ }
+ catch(const css::container::NoSuchElementException&)
+ { aItem.clear(); }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools", "Unexpected exception");
+ aItem.clear();
+ }
+
+ return aItem;
+}
+
+void SvtViewOptions::SetUserItem( const OUString& sItemName ,
+ const css::uno::Any& aValue )
+{
+ try
+ {
+ css::uno::Reference< css::container::XNameAccess > xNode(
+ impl_getSetNode(m_sViewName, true),
+ css::uno::UNO_QUERY_THROW);
+ css::uno::Reference< css::container::XNameContainer > xUserData;
+ xNode->getByName(PROPERTY_USERDATA) >>= xUserData;
+ if (xUserData.is())
+ {
+ if (xUserData->hasByName(sItemName))
+ xUserData->replaceByName(sItemName, aValue);
+ else
+ xUserData->insertByName(sItemName, aValue);
+ }
+ ::comphelper::ConfigurationHelper::flush(m_xRoot);
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools", "Unexpected exception");
+ }
+}
+
+
+
+/*-************************************************************************************************************
+ @short create new set node with default values on disk
+ @descr To create a new UserData item - the super node of these property must already exist!
+ You can call this method to create these new entry with default values and change UserData then.
+
+ @seealso method impl_writeDirectProp()
+
+ @param "sNode", name of new entry
+*//*-*************************************************************************************************************/
+css::uno::Reference< css::uno::XInterface > SvtViewOptions::impl_getSetNode( const OUString& sNode ,
+ bool bCreateIfMissing) const
+{
+ css::uno::Reference< css::uno::XInterface > xNode;
+
+ try
+ {
+ if (bCreateIfMissing)
+ xNode = ::comphelper::ConfigurationHelper::makeSureSetNodeExists(m_xRoot, m_sListName, sNode);
+ else
+ {
+ if (m_xSet.is() && m_xSet->hasByName(sNode) )
+ m_xSet->getByName(sNode) >>= xNode;
+ }
+ }
+ catch(const css::container::NoSuchElementException&)
+ { xNode.clear(); }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools", "Unexpected exception");
+ xNode.clear();
+ }
+
+ return xNode;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/calendarwrapper.cxx b/unotools/source/i18n/calendarwrapper.cxx
new file mode 100644
index 0000000000..51e975bbe9
--- /dev/null
+++ b/unotools/source/i18n/calendarwrapper.cxx
@@ -0,0 +1,342 @@
+/* -*- 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/log.hxx>
+#include <unotools/calendarwrapper.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <com/sun/star/i18n/LocaleCalendar2.hpp>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::uno;
+
+CalendarWrapper::CalendarWrapper(
+ const Reference< uno::XComponentContext > & rxContext
+ )
+ :
+ aEpochStart( Date( 1, 1, 1970 ) )
+{
+ xC = LocaleCalendar2::create(rxContext);
+}
+
+CalendarWrapper::~CalendarWrapper()
+{
+}
+
+void CalendarWrapper::loadDefaultCalendar( const css::lang::Locale& rLocale, bool bTimeZoneUTC )
+{
+ try
+ {
+ if ( xC.is() )
+ xC->loadDefaultCalendarTZ( rLocale, (bTimeZoneUTC ? "UTC" : OUString()));
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "loadDefaultCalendar" );
+ }
+}
+
+void CalendarWrapper::loadCalendar( const OUString& rUniqueID, const css::lang::Locale& rLocale, bool bTimeZoneUTC )
+{
+ try
+ {
+ if ( xC.is() )
+ xC->loadCalendarTZ( rUniqueID, rLocale, (bTimeZoneUTC ? "UTC" : OUString()));
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "loadCalendar: "
+ << rUniqueID << " Locale: " << LanguageTag::convertToBcp47(rLocale) );
+ }
+}
+
+css::uno::Sequence< OUString > CalendarWrapper::getAllCalendars( const css::lang::Locale& rLocale ) const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getAllCalendars( rLocale );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllCalendars" );
+ }
+
+ return {};
+}
+
+OUString CalendarWrapper::getUniqueID() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getUniqueID();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getUniqueID" );
+ }
+ return OUString();
+}
+
+void CalendarWrapper::setDateTime( double fTimeInDays )
+{
+ try
+ {
+ if ( xC.is() )
+ xC->setDateTime( fTimeInDays );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "setDateTime" );
+ }
+}
+
+double CalendarWrapper::getDateTime() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getDateTime();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getDateTime" );
+ }
+ return 0.0;
+}
+
+void CalendarWrapper::setLocalDateTime( double fTimeInDays )
+{
+ try
+ {
+ if ( xC.is() )
+ {
+ xC->setLocalDateTime( fTimeInDays );
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "setLocalDateTime" );
+ }
+}
+
+double CalendarWrapper::getLocalDateTime() const
+{
+ try
+ {
+ if ( xC.is() )
+ {
+ return xC->getLocalDateTime();
+ }
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getLocalDateTime" );
+ }
+ return 0.0;
+}
+
+void CalendarWrapper::setValue( sal_Int16 nFieldIndex, sal_Int16 nValue )
+{
+ try
+ {
+ if ( xC.is() )
+ xC->setValue( nFieldIndex, nValue );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "setValue" );
+ }
+}
+
+bool CalendarWrapper::isValid() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->isValid();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "isValid" );
+ }
+ return false;
+}
+
+sal_Int16 CalendarWrapper::getValue( sal_Int16 nFieldIndex ) const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getValue( nFieldIndex );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getValue" );
+ }
+ return 0;
+}
+
+sal_Int16 CalendarWrapper::getFirstDayOfWeek() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getFirstDayOfWeek();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getFirstDayOfWeek" );
+ }
+ return 0;
+}
+
+sal_Int16 CalendarWrapper::getNumberOfMonthsInYear() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getNumberOfMonthsInYear();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getNumberOfMonthsInYear" );
+ }
+ return 0;
+}
+
+sal_Int16 CalendarWrapper::getNumberOfDaysInWeek() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getNumberOfDaysInWeek();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getNumberOfDaysInWeek" );
+ }
+ return 0;
+}
+
+css::uno::Sequence< css::i18n::CalendarItem2 > CalendarWrapper::getMonths() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getMonths2();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getMonths" );
+ }
+ return {};
+}
+
+css::uno::Sequence< css::i18n::CalendarItem2 > CalendarWrapper::getDays() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getDays2();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getDays" );
+ }
+ return {};
+}
+
+OUString CalendarWrapper::getDisplayName( sal_Int16 nCalendarDisplayIndex, sal_Int16 nIdx, sal_Int16 nNameType ) const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getDisplayName( nCalendarDisplayIndex, nIdx, nNameType );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getDisplayName" );
+ }
+ return OUString();
+}
+
+// --- XExtendedCalendar -----------------------------------------------------
+
+OUString CalendarWrapper::getDisplayString( sal_Int32 nCalendarDisplayCode, sal_Int16 nNativeNumberMode ) const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getDisplayString( nCalendarDisplayCode, nNativeNumberMode );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getDisplayString" );
+ }
+ return OUString();
+}
+
+// --- XCalendar3 ------------------------------------------------------------
+
+css::i18n::Calendar2 CalendarWrapper::getLoadedCalendar() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getLoadedCalendar2();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getLoadedCalendar2" );
+ }
+ return css::i18n::Calendar2();
+}
+
+css::uno::Sequence< css::i18n::CalendarItem2 > CalendarWrapper::getGenitiveMonths() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getGenitiveMonths2();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getGenitiveMonths" );
+ }
+ return {};
+}
+
+css::uno::Sequence< css::i18n::CalendarItem2 > CalendarWrapper::getPartitiveMonths() const
+{
+ try
+ {
+ if ( xC.is() )
+ return xC->getPartitiveMonths2();
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getPartitiveMonths" );
+ }
+ return {};
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/caserotate.cxx b/unotools/source/i18n/caserotate.cxx
new file mode 100644
index 0000000000..a242b855ec
--- /dev/null
+++ b/unotools/source/i18n/caserotate.cxx
@@ -0,0 +1,43 @@
+/* -*- 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 <unotools/caserotate.hxx>
+#include <i18nutil/transliteration.hxx>
+
+//TODO Use XCharacterClassification::getStringType to determine the current
+//(possibly mixed) case type and rotate to the next one
+
+TransliterationFlags RotateTransliteration::getNextMode()
+{
+ TransliterationFlags nMode = TransliterationFlags::NONE;
+
+ switch (nF3ShiftCounter)
+ {
+ case 0:
+ nMode = TransliterationFlags::TITLE_CASE;
+ break;
+ case 1: //tdf#116315
+ nMode = TransliterationFlags::SENTENCE_CASE;
+ break;
+ case 2:
+ nMode = TransliterationFlags::LOWERCASE_UPPERCASE;
+ break;
+ default:
+ case 3:
+ nMode = TransliterationFlags::UPPERCASE_LOWERCASE;
+ nF3ShiftCounter = -1;
+ break;
+ }
+
+ nF3ShiftCounter++;
+
+ return nMode;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/charclass.cxx b/unotools/source/i18n/charclass.cxx
new file mode 100644
index 0000000000..423f9530f2
--- /dev/null
+++ b/unotools/source/i18n/charclass.cxx
@@ -0,0 +1,452 @@
+/* -*- 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 <comphelper/processfactory.hxx>
+#include <unotools/charclass.hxx>
+#include <rtl/character.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <com/sun/star/i18n/CharacterClassification.hpp>
+#include <utility>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::uno;
+
+CharClass::CharClass(
+ const Reference< uno::XComponentContext > & rxContext,
+ LanguageTag aLanguageTag
+ )
+ : maLanguageTag(std::move( aLanguageTag))
+{
+ xCC = CharacterClassification::create( rxContext );
+}
+
+CharClass::CharClass( LanguageTag aLanguageTag )
+ : maLanguageTag(std::move( aLanguageTag))
+{
+ xCC = CharacterClassification::create( comphelper::getProcessComponentContext() );
+}
+
+CharClass::~CharClass()
+{
+}
+
+const LanguageTag& CharClass::getLanguageTag() const
+{
+ return maLanguageTag;
+}
+
+const css::lang::Locale& CharClass::getMyLocale() const
+{
+ return maLanguageTag.getLocale();
+}
+
+// static
+bool CharClass::isAsciiNumeric( std::u16string_view rStr )
+{
+ if ( rStr.empty() )
+ return false;
+ const sal_Unicode* p = rStr.data();
+ const sal_Unicode* const pStop = p + rStr.size();
+
+ do
+ {
+ if ( !rtl::isAsciiDigit( *p ) )
+ return false;
+ }
+ while ( ++p < pStop );
+
+ return true;
+}
+
+// static
+bool CharClass::isAsciiAlpha( std::u16string_view rStr )
+{
+ if ( rStr.empty() )
+ return false;
+ const sal_Unicode* p = rStr.data();
+ const sal_Unicode* const pStop = p + rStr.size();
+
+ do
+ {
+ if ( !rtl::isAsciiAlpha( *p ) )
+ return false;
+ }
+ while ( ++p < pStop );
+
+ return true;
+}
+
+bool CharClass::isAlpha( const OUString& rStr, sal_Int32 nPos ) const
+{
+ sal_Unicode c = rStr[nPos];
+ if ( c < 128 )
+ return rtl::isAsciiAlpha( c );
+
+ try
+ {
+ return (xCC->getCharacterType( rStr, nPos, getMyLocale() ) &
+ nCharClassAlphaType) != 0;
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return false;
+}
+
+bool CharClass::isLetter( const OUString& rStr, sal_Int32 nPos ) const
+{
+ sal_Unicode c = rStr[nPos];
+ if ( c < 128 )
+ return rtl::isAsciiAlpha( c );
+
+ try
+ {
+ return (xCC->getCharacterType( rStr, nPos, getMyLocale() ) &
+ nCharClassLetterType) != 0;
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return false;
+}
+
+bool CharClass::isLetter( const OUString& rStr ) const
+{
+ if (rStr.isEmpty())
+ return false;
+
+ try
+ {
+ sal_Int32 nPos = 0;
+ while (nPos < rStr.getLength())
+ {
+ if (!isLetter( rStr, nPos))
+ return false;
+ rStr.iterateCodePoints( &nPos);
+ }
+ return true;
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return false;
+}
+
+bool CharClass::isDigit( const OUString& rStr, sal_Int32 nPos ) const
+{
+ sal_Unicode c = rStr[ nPos ];
+ if ( c < 128 )
+ return rtl::isAsciiDigit( c );
+
+ try
+ {
+ return (xCC->getCharacterType( rStr, nPos, getMyLocale() ) &
+ KCharacterType::DIGIT) != 0;
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return false;
+}
+
+bool CharClass::isNumeric( const OUString& rStr ) const
+{
+ if (rStr.isEmpty())
+ return false;
+
+ try
+ {
+ sal_Int32 nPos = 0;
+ while (nPos < rStr.getLength())
+ {
+ if (!isDigit( rStr, nPos))
+ return false;
+ rStr.iterateCodePoints( &nPos);
+ }
+ return true;
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return false;
+}
+
+bool CharClass::isAlphaNumeric( const OUString& rStr, sal_Int32 nPos ) const
+{
+ sal_Unicode c = rStr[nPos];
+ if ( c < 128 )
+ return rtl::isAsciiAlphanumeric( c );
+
+ try
+ {
+ return (xCC->getCharacterType( rStr, nPos, getMyLocale() ) &
+ (nCharClassAlphaType | nCharClassNumericType)) != 0;
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return false;
+}
+
+bool CharClass::isLetterNumeric( const OUString& rStr, sal_Int32 nPos ) const
+{
+ sal_Unicode c = rStr[nPos];
+ if ( c < 128 )
+ return rtl::isAsciiAlphanumeric( c );
+
+ try
+ {
+ return (xCC->getCharacterType( rStr, nPos, getMyLocale() ) &
+ (nCharClassLetterType | nCharClassNumericType)) != 0;
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return false;
+}
+
+bool CharClass::isLetterNumeric( const OUString& rStr ) const
+{
+ if (rStr.isEmpty())
+ return false;
+
+ try
+ {
+ sal_Int32 nPos = 0;
+ while (nPos < rStr.getLength())
+ {
+ if (!isLetterNumeric( rStr, nPos))
+ return false;
+ rStr.iterateCodePoints( &nPos);
+ }
+ return true;
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return false;
+}
+
+bool CharClass::isBase( const OUString& rStr, sal_Int32 nPos ) const
+{
+ sal_Unicode c = rStr[nPos];
+ if ( c < 128 )
+ return rtl::isAsciiAlphanumeric( c );
+
+ try
+ {
+ return (xCC->getCharacterType( rStr, nPos, getMyLocale() ) & nCharClassBaseType ) != 0;
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return false;
+}
+
+bool CharClass::isUpper( const OUString& rStr, sal_Int32 nPos ) const
+{
+ sal_Unicode c = rStr[nPos];
+ if ( c < 128 )
+ return rtl::isAsciiUpperCase(c);
+
+ try
+ {
+ return (xCC->getCharacterType( rStr, nPos, getMyLocale()) &
+ KCharacterType::UPPER) != 0;
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return false;
+}
+
+bool CharClass::isUpper( const OUString& rStr, sal_Int32 nPos, sal_Int32 nCount ) const
+{
+ if (rStr.isEmpty())
+ return false;
+
+ assert(nPos >= 0 && nPos < rStr.getLength() && nCount > 0);
+ if (nPos < 0 || nPos >= rStr.getLength() || nCount == 0)
+ return false;
+
+ try
+ {
+ const sal_Int32 nLen = std::min( nPos + nCount, rStr.getLength());
+ while (nPos < nLen)
+ {
+ if (!isUpper( rStr, nPos))
+ return false;
+ rStr.iterateCodePoints( &nPos);
+ }
+ return true;
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return false;
+}
+
+OUString CharClass::titlecase(const OUString& rStr, sal_Int32 nPos, sal_Int32 nCount) const
+{
+ try
+ {
+ return xCC->toTitle( rStr, nPos, nCount, getMyLocale() );
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return rStr.copy( nPos, nCount );
+}
+
+OUString CharClass::uppercase( const OUString& rStr, sal_Int32 nPos, sal_Int32 nCount ) const
+{
+ try
+ {
+ return xCC->toUpper( rStr, nPos, nCount, getMyLocale() );
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return rStr.copy( nPos, nCount );
+}
+
+OUString CharClass::lowercase( const OUString& rStr, sal_Int32 nPos, sal_Int32 nCount ) const
+{
+ try
+ {
+ return xCC->toLower( rStr, nPos, nCount, getMyLocale() );
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return rStr.copy( nPos, nCount );
+}
+
+sal_Int16 CharClass::getType( const OUString& rStr, sal_Int32 nPos ) const
+{
+ try
+ {
+ return xCC->getType( rStr, nPos );
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return 0;
+}
+
+css::i18n::DirectionProperty CharClass::getCharacterDirection( const OUString& rStr, sal_Int32 nPos ) const
+{
+ try
+ {
+ return static_cast<css::i18n::DirectionProperty>(xCC->getCharacterDirection( rStr, nPos ));
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return css::i18n::DirectionProperty_LEFT_TO_RIGHT;
+}
+
+css::i18n::UnicodeScript CharClass::getScript( const OUString& rStr, sal_Int32 nPos ) const
+{
+ try
+ {
+ return static_cast<css::i18n::UnicodeScript>(xCC->getScript( rStr, nPos ));
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return UnicodeScript_kBasicLatin;
+}
+
+sal_Int32 CharClass::getCharacterType( const OUString& rStr, sal_Int32 nPos ) const
+{
+ try
+ {
+ return xCC->getCharacterType( rStr, nPos, getMyLocale() );
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return 0;
+}
+
+css::i18n::ParseResult CharClass::parseAnyToken(
+ const OUString& rStr,
+ sal_Int32 nPos,
+ sal_Int32 nStartCharFlags,
+ const OUString& userDefinedCharactersStart,
+ sal_Int32 nContCharFlags,
+ const OUString& userDefinedCharactersCont ) const
+{
+ try
+ {
+ return xCC->parseAnyToken( rStr, nPos, getMyLocale(),
+ nStartCharFlags, userDefinedCharactersStart,
+ nContCharFlags, userDefinedCharactersCont );
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "parseAnyToken" );
+ }
+ return ParseResult();
+}
+
+css::i18n::ParseResult CharClass::parsePredefinedToken(
+ sal_Int32 nTokenType,
+ const OUString& rStr,
+ sal_Int32 nPos,
+ sal_Int32 nStartCharFlags,
+ const OUString& userDefinedCharactersStart,
+ sal_Int32 nContCharFlags,
+ const OUString& userDefinedCharactersCont ) const
+{
+ try
+ {
+ return xCC->parsePredefinedToken( nTokenType, rStr, nPos, getMyLocale(),
+ nStartCharFlags, userDefinedCharactersStart,
+ nContCharFlags, userDefinedCharactersCont );
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "parsePredefinedToken" );
+ }
+ return ParseResult();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/collatorwrapper.cxx b/unotools/source/i18n/collatorwrapper.cxx
new file mode 100644
index 0000000000..4da1398e06
--- /dev/null
+++ b/unotools/source/i18n/collatorwrapper.cxx
@@ -0,0 +1,97 @@
+/* -*- 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 <sal/log.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <com/sun/star/i18n/Collator.hpp>
+
+using namespace ::com::sun::star;
+
+CollatorWrapper::CollatorWrapper ( const uno::Reference< uno::XComponentContext > &rxContext )
+{
+ mxInternationalCollator = i18n::Collator::create( rxContext );
+}
+
+sal_Int32
+CollatorWrapper::compareString (const OUString& s1, const OUString& s2) const
+{
+ try
+ {
+ if (mxInternationalCollator.is())
+ return mxInternationalCollator->compareString (s1, s2);
+ }
+ catch (const uno::RuntimeException&)
+ {
+ SAL_WARN( "unotools.i18n","CollatorWrapper: compareString failed");
+ }
+
+ return 0;
+}
+
+uno::Sequence< OUString >
+CollatorWrapper::listCollatorAlgorithms (const lang::Locale& rLocale) const
+{
+ try
+ {
+ if (mxInternationalCollator.is())
+ return mxInternationalCollator->listCollatorAlgorithms (rLocale);
+ }
+ catch (const uno::RuntimeException&)
+ {
+ SAL_WARN( "unotools.i18n","CollatorWrapper: listCollatorAlgorithms failed");
+ }
+
+ return uno::Sequence< OUString > ();
+}
+
+sal_Int32
+CollatorWrapper::loadDefaultCollator (const lang::Locale& rLocale, sal_Int32 nOptions)
+{
+ try
+ {
+ if (mxInternationalCollator.is())
+ return mxInternationalCollator->loadDefaultCollator (rLocale, nOptions);
+ }
+ catch (const uno::RuntimeException&)
+ {
+ SAL_WARN( "unotools.i18n","CollatorWrapper: loadDefaultCollator failed");
+ }
+
+ return 0;
+}
+
+void
+CollatorWrapper::loadCollatorAlgorithm (const OUString& rAlgorithm,
+ const lang::Locale& rLocale, sal_Int32 nOptions)
+{
+ try
+ {
+ if (mxInternationalCollator.is())
+ mxInternationalCollator->loadCollatorAlgorithm (
+ rAlgorithm, rLocale, nOptions);
+ }
+ catch (const uno::RuntimeException&)
+ {
+ SAL_WARN( "unotools.i18n","CollatorWrapper: loadCollatorAlgorithm failed");
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/intlwrapper.cxx b/unotools/source/i18n/intlwrapper.cxx
new file mode 100644
index 0000000000..19157dab4a
--- /dev/null
+++ b/unotools/source/i18n/intlwrapper.cxx
@@ -0,0 +1,58 @@
+/* -*- 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/intlwrapper.hxx>
+#include <unotools/collatorwrapper.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <com/sun/star/i18n/CollatorOptions.hpp>
+#include <comphelper/processfactory.hxx>
+#include <utility>
+
+IntlWrapper::IntlWrapper( LanguageTag aLanguageTag )
+ :
+ maLanguageTag(std::move( aLanguageTag )),
+ m_xContext( comphelper::getProcessComponentContext() )
+{
+}
+
+IntlWrapper::~IntlWrapper()
+{
+}
+
+void IntlWrapper::ImplNewLocaleData() const
+{
+ const_cast<IntlWrapper*>(this)->pLocaleData.reset( new LocaleDataWrapper( m_xContext, maLanguageTag ) );
+}
+
+void IntlWrapper::ImplNewCollator( bool bCaseSensitive ) const
+{
+ if ( bCaseSensitive )
+ {
+ const_cast<IntlWrapper*>(this)->moCaseCollator.emplace(m_xContext);
+ const_cast<IntlWrapper*>(this)->moCaseCollator->loadDefaultCollator( maLanguageTag.getLocale(), 0 );
+ }
+ else
+ {
+ const_cast<IntlWrapper*>(this)->moCollator.emplace(m_xContext);
+ const_cast<IntlWrapper*>(this)->moCollator->loadDefaultCollator( maLanguageTag.getLocale(),
+ css::i18n::CollatorOptions::CollatorOptions_IGNORE_CASE );
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/localedatawrapper.cxx b/unotools/source/i18n/localedatawrapper.cxx
new file mode 100644
index 0000000000..87299810ab
--- /dev/null
+++ b/unotools/source/i18n/localedatawrapper.cxx
@@ -0,0 +1,1517 @@
+/* -*- 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 <limits>
+#include <stdio.h>
+#include <string>
+
+#include <sal/log.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/digitgroupingiterator.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <tools/debug.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <o3tl/safeint.hxx>
+
+#include <com/sun/star/i18n/KNumberFormatUsage.hpp>
+#include <com/sun/star/i18n/KNumberFormatType.hpp>
+#include <com/sun/star/i18n/LocaleData2.hpp>
+#include <com/sun/star/i18n/NumberFormatIndex.hpp>
+#include <com/sun/star/i18n/NumberFormatMapper.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/math.hxx>
+#include <tools/date.hxx>
+#include <tools/time.hxx>
+#include <tools/duration.hxx>
+#include <o3tl/string_view.hxx>
+#include <utility>
+
+const sal_uInt16 nCurrFormatDefault = 0;
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::uno;
+
+namespace
+{
+ uno::Sequence< lang::Locale > gInstalledLocales;
+ std::vector< LanguageType > gInstalledLanguageTypes;
+}
+
+sal_uInt8 LocaleDataWrapper::nLocaleDataChecking = 0;
+
+LocaleDataWrapper::LocaleDataWrapper(
+ const Reference< uno::XComponentContext > & rxContext,
+ LanguageTag aLanguageTag
+ )
+ :
+ m_xContext( rxContext ),
+ xLD( LocaleData2::create(rxContext) ),
+ maLanguageTag(std::move( aLanguageTag ))
+{
+ loadData();
+ loadDateAcceptancePatterns({});
+}
+
+LocaleDataWrapper::LocaleDataWrapper(
+ LanguageTag aLanguageTag,
+ const std::vector<OUString> & rOverrideDateAcceptancePatterns
+ )
+ :
+ m_xContext( comphelper::getProcessComponentContext() ),
+ xLD( LocaleData2::create(m_xContext) ),
+ maLanguageTag(std::move( aLanguageTag ))
+{
+ loadData();
+ loadDateAcceptancePatterns(rOverrideDateAcceptancePatterns);
+}
+
+LocaleDataWrapper::~LocaleDataWrapper()
+{
+}
+
+const LanguageTag& LocaleDataWrapper::getLanguageTag() const
+{
+ return maLanguageTag;
+}
+
+const css::lang::Locale& LocaleDataWrapper::getMyLocale() const
+{
+ return maLanguageTag.getLocale();
+}
+
+void LocaleDataWrapper::loadData()
+{
+ const css::lang::Locale& rMyLocale = maLanguageTag.getLocale();
+
+ {
+ const Sequence< Currency2 > aCurrSeq = getAllCurrencies();
+ if ( !aCurrSeq.hasElements() )
+ {
+ if (areChecksEnabled())
+ outputCheckMessage("LocaleDataWrapper::getCurrSymbolsImpl: no currency at all, using ShellsAndPebbles");
+ aCurrSymbol = "ShellsAndPebbles";
+ aCurrBankSymbol = aCurrSymbol;
+ nCurrPositiveFormat = nCurrNegativeFormat = nCurrFormatDefault;
+ nCurrDigits = 2;
+ }
+ else
+ {
+ auto pCurr = std::find_if(aCurrSeq.begin(), aCurrSeq.end(),
+ [](const Currency2& rCurr) { return rCurr.Default; });
+ if ( pCurr == aCurrSeq.end() )
+ {
+ if (areChecksEnabled())
+ {
+ outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getCurrSymbolsImpl: no default currency" ) );
+ }
+ pCurr = aCurrSeq.begin();
+ }
+ aCurrSymbol = pCurr->Symbol;
+ aCurrBankSymbol = pCurr->BankSymbol;
+ nCurrDigits = pCurr->DecimalPlaces;
+ }
+ }
+
+ loadCurrencyFormats();
+
+ {
+ xDefaultCalendar.reset();
+ xSecondaryCalendar.reset();
+ const Sequence< Calendar2 > xCals = getAllCalendars();
+ if (xCals.getLength() > 1)
+ {
+ auto pCal = std::find_if(xCals.begin(), xCals.end(),
+ [](const Calendar2& rCal) { return !rCal.Default; });
+ if (pCal != xCals.end())
+ xSecondaryCalendar = std::make_shared<Calendar2>( *pCal);
+ }
+ auto pCal = xCals.begin();
+ if (xCals.getLength() > 1)
+ {
+ pCal = std::find_if(xCals.begin(), xCals.end(),
+ [](const Calendar2& rCal) { return rCal.Default; });
+ if (pCal == xCals.end())
+ pCal = xCals.begin();
+ }
+ xDefaultCalendar = std::make_shared<Calendar2>( *pCal);
+ }
+
+ loadDateOrders();
+
+ try
+ {
+ aDateAcceptancePatterns = xLD->getDateAcceptancePatterns( rMyLocale );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getDateAcceptancePatterns" );
+ aDateAcceptancePatterns = {};
+ }
+
+
+ loadDigitGrouping();
+
+ try
+ {
+ aReservedWords = comphelper::sequenceToContainer<std::vector<OUString>>(xLD->getReservedWord( rMyLocale ));
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getReservedWord" );
+ }
+
+ try
+ {
+ aLocaleDataItem = xLD->getLocaleItem2( rMyLocale );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getLocaleItem" );
+ static const css::i18n::LocaleDataItem2 aEmptyItem;
+ aLocaleDataItem = aEmptyItem;
+ }
+
+ aLocaleItem[LocaleItem::DATE_SEPARATOR] = aLocaleDataItem.dateSeparator;
+ aLocaleItem[LocaleItem::THOUSAND_SEPARATOR] = aLocaleDataItem.thousandSeparator;
+ aLocaleItem[LocaleItem::DECIMAL_SEPARATOR] = aLocaleDataItem.decimalSeparator;
+ aLocaleItem[LocaleItem::TIME_SEPARATOR] = aLocaleDataItem.timeSeparator;
+ aLocaleItem[LocaleItem::TIME_100SEC_SEPARATOR] = aLocaleDataItem.time100SecSeparator;
+ aLocaleItem[LocaleItem::LIST_SEPARATOR] = aLocaleDataItem.listSeparator;
+ aLocaleItem[LocaleItem::SINGLE_QUOTATION_START] = aLocaleDataItem.quotationStart;
+ aLocaleItem[LocaleItem::SINGLE_QUOTATION_END] = aLocaleDataItem.quotationEnd;
+ aLocaleItem[LocaleItem::DOUBLE_QUOTATION_START] = aLocaleDataItem.doubleQuotationStart;
+ aLocaleItem[LocaleItem::DOUBLE_QUOTATION_END] = aLocaleDataItem.doubleQuotationEnd;
+ aLocaleItem[LocaleItem::MEASUREMENT_SYSTEM] = aLocaleDataItem.measurementSystem;
+ aLocaleItem[LocaleItem::TIME_AM] = aLocaleDataItem.timeAM;
+ aLocaleItem[LocaleItem::TIME_PM] = aLocaleDataItem.timePM;
+ aLocaleItem[LocaleItem::LONG_DATE_DAY_OF_WEEK_SEPARATOR] = aLocaleDataItem.LongDateDayOfWeekSeparator;
+ aLocaleItem[LocaleItem::LONG_DATE_DAY_SEPARATOR] = aLocaleDataItem.LongDateDaySeparator;
+ aLocaleItem[LocaleItem::LONG_DATE_MONTH_SEPARATOR] = aLocaleDataItem.LongDateMonthSeparator;
+ aLocaleItem[LocaleItem::LONG_DATE_YEAR_SEPARATOR] = aLocaleDataItem.LongDateYearSeparator;
+ aLocaleItem[LocaleItem::DECIMAL_SEPARATOR_ALTERNATIVE] = aLocaleDataItem.decimalSeparatorAlternative;
+}
+
+/* FIXME-BCP47: locale data should provide a language tag instead that could be
+ * passed on. */
+css::i18n::LanguageCountryInfo LocaleDataWrapper::getLanguageCountryInfo() const
+{
+ try
+ {
+ return xLD->getLanguageCountryInfo( getMyLocale() );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getLanguageCountryInfo" );
+ }
+ return css::i18n::LanguageCountryInfo();
+}
+
+const css::i18n::LocaleDataItem2& LocaleDataWrapper::getLocaleItem() const
+{
+ return aLocaleDataItem;
+}
+
+css::uno::Sequence< css::i18n::Currency2 > LocaleDataWrapper::getAllCurrencies() const
+{
+ try
+ {
+ return xLD->getAllCurrencies2( getMyLocale() );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllCurrencies" );
+ }
+ return {};
+}
+
+css::uno::Sequence< css::i18n::FormatElement > LocaleDataWrapper::getAllFormats() const
+{
+ try
+ {
+ return xLD->getAllFormats( getMyLocale() );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllFormats" );
+ }
+ return {};
+}
+
+css::i18n::ForbiddenCharacters LocaleDataWrapper::getForbiddenCharacters() const
+{
+ try
+ {
+ return xLD->getForbiddenCharacters( getMyLocale() );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getForbiddenCharacters" );
+ }
+ return css::i18n::ForbiddenCharacters();
+}
+
+const css::uno::Sequence< css::lang::Locale > & LocaleDataWrapper::getAllInstalledLocaleNames() const
+{
+ uno::Sequence< lang::Locale > &rInstalledLocales = gInstalledLocales;
+
+ if ( rInstalledLocales.hasElements() )
+ return rInstalledLocales;
+
+ try
+ {
+ rInstalledLocales = xLD->getAllInstalledLocaleNames();
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllInstalledLocaleNames" );
+ }
+ return rInstalledLocales;
+}
+
+// --- Impl and helpers ----------------------------------------------------
+
+// static
+const css::uno::Sequence< css::lang::Locale >& LocaleDataWrapper::getInstalledLocaleNames()
+{
+ const uno::Sequence< lang::Locale > &rInstalledLocales = gInstalledLocales;
+
+ if ( !rInstalledLocales.hasElements() )
+ {
+ LocaleDataWrapper aLDW( ::comphelper::getProcessComponentContext(), LanguageTag( LANGUAGE_SYSTEM) );
+ aLDW.getAllInstalledLocaleNames();
+ }
+ return rInstalledLocales;
+}
+
+// static
+const std::vector< LanguageType >& LocaleDataWrapper::getInstalledLanguageTypes()
+{
+ std::vector< LanguageType > &rInstalledLanguageTypes = gInstalledLanguageTypes;
+
+ if ( !rInstalledLanguageTypes.empty() )
+ return rInstalledLanguageTypes;
+
+ const css::uno::Sequence< css::lang::Locale > xLoc = getInstalledLocaleNames();
+ sal_Int32 nCount = xLoc.getLength();
+ std::vector< LanguageType > xLang;
+ xLang.reserve(nCount);
+ for ( const auto& rLoc : xLoc )
+ {
+ LanguageTag aLanguageTag( rLoc );
+ OUString aDebugLocale;
+ if (areChecksEnabled())
+ {
+ aDebugLocale = aLanguageTag.getBcp47( false);
+ }
+
+ LanguageType eLang = aLanguageTag.getLanguageType( false);
+ if (areChecksEnabled() && eLang == LANGUAGE_DONTKNOW)
+ {
+ OUString aMsg = "ConvertIsoNamesToLanguage: unknown MS-LCID for locale\n" +
+ aDebugLocale;
+ outputCheckMessage(aMsg);
+ }
+
+ if ( eLang == LANGUAGE_NORWEGIAN) // no_NO, not Bokmal (nb_NO), not Nynorsk (nn_NO)
+ eLang = LANGUAGE_DONTKNOW; // don't offer "Unknown" language
+ if ( eLang != LANGUAGE_DONTKNOW )
+ {
+ LanguageTag aBackLanguageTag( eLang);
+ if ( aLanguageTag != aBackLanguageTag )
+ {
+ // In checks, exclude known problems because no MS-LCID defined
+ // and default for Language found.
+ if ( areChecksEnabled()
+ && aDebugLocale != "ar-SD" // Sudan/ar
+ && aDebugLocale != "en-CB" // Caribbean is not a country
+// && aDebugLocale != "en-BG" // ?!? Bulgaria/en
+// && aDebugLocale != "es-BR" // ?!? Brazil/es
+ )
+ {
+ outputCheckMessage(Concat2View(
+ "ConvertIsoNamesToLanguage/ConvertLanguageToIsoNames: ambiguous locale (MS-LCID?)\n"
+ + aDebugLocale
+ + " -> 0x"
+ + OUString::number(static_cast<sal_Int32>(static_cast<sal_uInt16>(eLang)), 16)
+ + " -> "
+ + aBackLanguageTag.getBcp47() ));
+ }
+ eLang = LANGUAGE_DONTKNOW;
+ }
+ }
+ if ( eLang != LANGUAGE_DONTKNOW )
+ xLang.push_back(eLang);
+ }
+ rInstalledLanguageTypes = xLang;
+
+ return rInstalledLanguageTypes;
+}
+
+const OUString& LocaleDataWrapper::getOneLocaleItem( sal_Int16 nItem ) const
+{
+ if ( nItem >= LocaleItem::COUNT2 )
+ {
+ SAL_WARN( "unotools.i18n", "getOneLocaleItem: bounds" );
+ return aLocaleItem[0];
+ }
+ return aLocaleItem[nItem];
+}
+
+const OUString& LocaleDataWrapper::getOneReservedWord( sal_Int16 nWord ) const
+{
+ if ( nWord < 0 || o3tl::make_unsigned(nWord) >= aReservedWords.size() )
+ {
+ SAL_WARN( "unotools.i18n", "getOneReservedWord: bounds" );
+ static const OUString EMPTY;
+ return EMPTY;
+ }
+ return aReservedWords[nWord];
+}
+
+MeasurementSystem LocaleDataWrapper::mapMeasurementStringToEnum( std::u16string_view rMS ) const
+{
+//! TODO: could be cached too
+ if ( o3tl::equalsIgnoreAsciiCase( rMS, u"metric" ) )
+ return MeasurementSystem::Metric;
+//! TODO: other measurement systems? => extend enum MeasurementSystem
+ return MeasurementSystem::US;
+}
+
+bool LocaleDataWrapper::doesSecondaryCalendarUseEC( std::u16string_view rName ) const
+{
+ if (rName.empty())
+ return false;
+
+ // Check language tag first to avoid loading all calendars of this locale.
+ LanguageTag aLoaded( getLoadedLanguageTag());
+ const OUString& aBcp47( aLoaded.getBcp47());
+ // So far determine only by locale, we know for a few.
+ /* TODO: check date format codes? or add to locale data? */
+ if ( aBcp47 != "ja-JP" &&
+ aBcp47 != "lo-LA" &&
+ aBcp47 != "zh-TW")
+ return false;
+
+ if (!xSecondaryCalendar)
+ return false;
+ if (!xSecondaryCalendar->Name.equalsIgnoreAsciiCase( rName))
+ return false;
+
+ return true;
+}
+
+const std::shared_ptr< css::i18n::Calendar2 >& LocaleDataWrapper::getDefaultCalendar() const
+{
+ return xDefaultCalendar;
+}
+
+css::uno::Sequence< css::i18n::CalendarItem2 > const & LocaleDataWrapper::getDefaultCalendarDays() const
+{
+ return getDefaultCalendar()->Days;
+}
+
+css::uno::Sequence< css::i18n::CalendarItem2 > const & LocaleDataWrapper::getDefaultCalendarMonths() const
+{
+ return getDefaultCalendar()->Months;
+}
+
+// --- currencies -----------------------------------------------------
+
+const OUString& LocaleDataWrapper::getCurrSymbol() const
+{
+ return aCurrSymbol;
+}
+
+const OUString& LocaleDataWrapper::getCurrBankSymbol() const
+{
+ return aCurrBankSymbol;
+}
+
+sal_uInt16 LocaleDataWrapper::getCurrPositiveFormat() const
+{
+ return nCurrPositiveFormat;
+}
+
+sal_uInt16 LocaleDataWrapper::getCurrNegativeFormat() const
+{
+ return nCurrNegativeFormat;
+}
+
+sal_uInt16 LocaleDataWrapper::getCurrDigits() const
+{
+ return nCurrDigits;
+}
+
+void LocaleDataWrapper::scanCurrFormatImpl( std::u16string_view rCode,
+ sal_Int32 nStart, sal_Int32& nSign, sal_Int32& nPar,
+ sal_Int32& nNum, sal_Int32& nBlank, sal_Int32& nSym ) const
+{
+ nSign = nPar = nNum = nBlank = nSym = -1;
+ const sal_Unicode* const pStr = rCode.data();
+ const sal_Unicode* const pStop = pStr + rCode.size();
+ const sal_Unicode* p = pStr + nStart;
+ int nInSection = 0;
+ bool bQuote = false;
+ while ( p < pStop )
+ {
+ if ( bQuote )
+ {
+ if ( *p == '"' && *(p-1) != '\\' )
+ bQuote = false;
+ }
+ else
+ {
+ switch ( *p )
+ {
+ case '"' :
+ if ( pStr == p || *(p-1) != '\\' )
+ bQuote = true;
+ break;
+ case '-' :
+ if (!nInSection && nSign == -1)
+ nSign = p - pStr;
+ break;
+ case '(' :
+ if (!nInSection && nPar == -1)
+ nPar = p - pStr;
+ break;
+ case '0' :
+ case '#' :
+ if (!nInSection && nNum == -1)
+ nNum = p - pStr;
+ break;
+ case '[' :
+ nInSection++;
+ break;
+ case ']' :
+ if ( nInSection )
+ {
+ nInSection--;
+ if (!nInSection && nBlank == -1
+ && nSym != -1 && p < pStop-1 && *(p+1) == ' ' )
+ nBlank = p - pStr + 1;
+ }
+ break;
+ case '$' :
+ if (nSym == -1 && nInSection && *(p-1) == '[')
+ {
+ nSym = p - pStr + 1;
+ if (nNum != -1 && *(p-2) == ' ')
+ nBlank = p - pStr - 2;
+ }
+ break;
+ case ';' :
+ if ( !nInSection )
+ p = pStop;
+ break;
+ default:
+ if (!nInSection && nSym == -1 && o3tl::starts_with(rCode.substr(static_cast<sal_Int32>(p - pStr)), aCurrSymbol))
+ { // currency symbol not surrounded by [$...]
+ nSym = p - pStr;
+ if (nBlank == -1 && pStr < p && *(p-1) == ' ')
+ nBlank = p - pStr - 1;
+ p += aCurrSymbol.getLength() - 1;
+ if (nBlank == -1 && p < pStop-2 && *(p+2) == ' ')
+ nBlank = p - pStr + 2;
+ }
+ }
+ }
+ p++;
+ }
+}
+
+void LocaleDataWrapper::loadCurrencyFormats()
+{
+ css::uno::Reference< css::i18n::XNumberFormatCode > xNFC = i18n::NumberFormatMapper::create( m_xContext );
+ uno::Sequence< NumberFormatCode > aFormatSeq = xNFC->getAllFormatCode( KNumberFormatUsage::CURRENCY, maLanguageTag.getLocale() );
+ sal_Int32 nCnt = aFormatSeq.getLength();
+ if ( !nCnt )
+ { // bad luck
+ if (areChecksEnabled())
+ {
+ outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getCurrFormatsImpl: no currency formats" ) );
+ }
+ nCurrPositiveFormat = nCurrNegativeFormat = nCurrFormatDefault;
+ return;
+ }
+ // find a negative code (medium preferred) and a default (medium preferred) (not necessarily the same)
+ NumberFormatCode const * const pFormatArr = aFormatSeq.getArray();
+ sal_Int32 nElem, nDef, nNeg, nMedium;
+ nDef = nNeg = nMedium = -1;
+ for ( nElem = 0; nElem < nCnt; nElem++ )
+ {
+ if ( pFormatArr[nElem].Type == KNumberFormatType::MEDIUM )
+ {
+ if ( pFormatArr[nElem].Default )
+ {
+ nDef = nElem;
+ nMedium = nElem;
+ if ( pFormatArr[nElem].Code.indexOf( ';' ) >= 0 )
+ nNeg = nElem;
+ }
+ else
+ {
+ if ( (nNeg == -1 || nMedium == -1) && pFormatArr[nElem].Code.indexOf( ';' ) >= 0 )
+ nNeg = nElem;
+ if ( nMedium == -1 )
+ nMedium = nElem;
+ }
+ }
+ else
+ {
+ if ( nDef == -1 && pFormatArr[nElem].Default )
+ nDef = nElem;
+ if ( nNeg == -1 && pFormatArr[nElem].Code.indexOf( ';' ) >= 0 )
+ nNeg = nElem;
+ }
+ }
+
+ sal_Int32 nSign, nPar, nNum, nBlank, nSym;
+
+ // positive format
+ nElem = (nDef >= 0 ? nDef : (nNeg >= 0 ? nNeg : 0));
+ scanCurrFormatImpl( pFormatArr[nElem].Code, 0, nSign, nPar, nNum, nBlank, nSym );
+ if (areChecksEnabled() && (nNum == -1 || nSym == -1))
+ {
+ outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getCurrFormatsImpl: CurrPositiveFormat?" ) );
+ }
+ if (nBlank == -1)
+ {
+ if ( nSym < nNum )
+ nCurrPositiveFormat = 0; // $1
+ else
+ nCurrPositiveFormat = 1; // 1$
+ }
+ else
+ {
+ if ( nSym < nNum )
+ nCurrPositiveFormat = 2; // $ 1
+ else
+ nCurrPositiveFormat = 3; // 1 $
+ }
+
+ // negative format
+ if ( nNeg < 0 )
+ nCurrNegativeFormat = nCurrFormatDefault;
+ else
+ {
+ const OUString& rCode = pFormatArr[nNeg].Code;
+ sal_Int32 nDelim = rCode.indexOf(';');
+ scanCurrFormatImpl( rCode, nDelim+1, nSign, nPar, nNum, nBlank, nSym );
+ if (areChecksEnabled() && (nNum == -1 || nSym == -1 || (nPar == -1 && nSign == -1)))
+ {
+ outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getCurrFormatsImpl: CurrNegativeFormat?" ) );
+ }
+ // NOTE: one of nPar or nSign are allowed to be -1
+ if (nBlank == -1)
+ {
+ if ( nSym < nNum )
+ {
+ if ( -1 < nPar && nPar < nSym )
+ nCurrNegativeFormat = 0; // ($1)
+ else if ( -1 < nSign && nSign < nSym )
+ nCurrNegativeFormat = 1; // -$1
+ else if ( nNum < nSign )
+ nCurrNegativeFormat = 3; // $1-
+ else
+ nCurrNegativeFormat = 2; // $-1
+ }
+ else
+ {
+ if ( -1 < nPar && nPar < nNum )
+ nCurrNegativeFormat = 4; // (1$)
+ else if ( -1 < nSign && nSign < nNum )
+ nCurrNegativeFormat = 5; // -1$
+ else if ( nSym < nSign )
+ nCurrNegativeFormat = 7; // 1$-
+ else
+ nCurrNegativeFormat = 6; // 1-$
+ }
+ }
+ else
+ {
+ if ( nSym < nNum )
+ {
+ if ( -1 < nPar && nPar < nSym )
+ nCurrNegativeFormat = 14; // ($ 1)
+ else if ( -1 < nSign && nSign < nSym )
+ nCurrNegativeFormat = 9; // -$ 1
+ else if ( nNum < nSign )
+ nCurrNegativeFormat = 12; // $ 1-
+ else
+ nCurrNegativeFormat = 11; // $ -1
+ }
+ else
+ {
+ if ( -1 < nPar && nPar < nNum )
+ nCurrNegativeFormat = 15; // (1 $)
+ else if ( -1 < nSign && nSign < nNum )
+ nCurrNegativeFormat = 8; // -1 $
+ else if ( nSym < nSign )
+ nCurrNegativeFormat = 10; // 1 $-
+ else
+ nCurrNegativeFormat = 13; // 1- $
+ }
+ }
+ }
+}
+
+// --- date -----------------------------------------------------------
+
+DateOrder LocaleDataWrapper::getDateOrder() const
+{
+ return nDateOrder;
+}
+
+LongDateOrder LocaleDataWrapper::getLongDateOrder() const
+{
+ return nLongDateOrder;
+}
+
+LongDateOrder LocaleDataWrapper::scanDateOrderImpl( std::u16string_view rCode ) const
+{
+ // Only some european versions were translated, the ones with different
+ // keyword combinations are:
+ // English DMY, German TMJ, Spanish DMA, French JMA, Italian GMA,
+ // Dutch DMJ, Finnish PKV
+
+ // default is English keywords for every other language
+ size_t nDay = rCode.find('D');
+ size_t nMonth = rCode.find('M');
+ size_t nYear = rCode.find('Y');
+ if (nDay == std::u16string_view::npos || nMonth == std::u16string_view::npos || nYear == std::u16string_view::npos)
+ { // This algorithm assumes that all three parts (DMY) are present
+ if (nMonth == std::u16string_view::npos)
+ { // only Finnish has something else than 'M' for month
+ nMonth = rCode.find('K');
+ if (nMonth != std::u16string_view::npos)
+ {
+ nDay = rCode.find('P');
+ nYear = rCode.find('V');
+ }
+ }
+ else if (nDay == std::u16string_view::npos)
+ { // We have a month 'M' if we reach this branch.
+ // Possible languages containing 'M' but no 'D':
+ // German, French, Italian
+ nDay = rCode.find('T'); // German
+ if (nDay != std::u16string_view::npos)
+ nYear = rCode.find('J');
+ else
+ {
+ nYear = rCode.find('A'); // French, Italian
+ if (nYear != std::u16string_view::npos)
+ {
+ nDay = rCode.find('J'); // French
+ if (nDay == std::u16string_view::npos)
+ nDay = rCode.find('G'); // Italian
+ }
+ }
+ }
+ else
+ { // We have a month 'M' and a day 'D'.
+ // Possible languages containing 'D' and 'M' but not 'Y':
+ // Spanish, Dutch
+ nYear = rCode.find('A'); // Spanish
+ if (nYear == std::u16string_view::npos)
+ nYear = rCode.find('J'); // Dutch
+ }
+ if (nDay == std::u16string_view::npos || nMonth == std::u16string_view::npos || nYear == std::u16string_view::npos)
+ {
+ if (areChecksEnabled())
+ {
+ outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::scanDateOrder: not all DMY present" ) );
+ }
+ if (nDay == std::u16string_view::npos)
+ nDay = rCode.size();
+ if (nMonth == std::u16string_view::npos)
+ nMonth = rCode.size();
+ if (nYear == std::u16string_view::npos)
+ nYear = rCode.size();
+ }
+ }
+ // compare with <= because each position may equal rCode.getLength()
+ if ( nDay <= nMonth && nMonth <= nYear )
+ return LongDateOrder::DMY; // also if every position equals rCode.getLength()
+ else if ( nMonth <= nDay && nDay <= nYear )
+ return LongDateOrder::MDY;
+ else if ( nYear <= nMonth && nMonth <= nDay )
+ return LongDateOrder::YMD;
+ else if ( nYear <= nDay && nDay <= nMonth )
+ return LongDateOrder::YDM;
+ else
+ {
+ if (areChecksEnabled())
+ {
+ outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::scanDateOrder: no magic applicable" ) );
+ }
+ return LongDateOrder::DMY;
+ }
+}
+
+static DateOrder getDateOrderFromLongDateOrder( LongDateOrder eLong )
+{
+ switch (eLong)
+ {
+ case LongDateOrder::YMD:
+ return DateOrder::YMD;
+ break;
+ case LongDateOrder::DMY:
+ return DateOrder::DMY;
+ break;
+ case LongDateOrder::MDY:
+ return DateOrder::MDY;
+ break;
+ case LongDateOrder::YDM:
+ default:
+ assert(!"unhandled LongDateOrder to DateOrder");
+ return DateOrder::DMY;
+ }
+}
+
+void LocaleDataWrapper::loadDateOrders()
+{
+ css::uno::Reference< css::i18n::XNumberFormatCode > xNFC = i18n::NumberFormatMapper::create( m_xContext );
+ uno::Sequence< NumberFormatCode > aFormatSeq = xNFC->getAllFormatCode( KNumberFormatUsage::DATE, maLanguageTag.getLocale() );
+ sal_Int32 nCnt = aFormatSeq.getLength();
+ if ( !nCnt )
+ { // bad luck
+ if (areChecksEnabled())
+ {
+ outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getDateOrdersImpl: no date formats" ) );
+ }
+ nDateOrder = DateOrder::DMY;
+ nLongDateOrder = LongDateOrder::DMY;
+ return;
+ }
+ // find the edit (21), a default (medium preferred),
+ // a medium (default preferred), and a long (default preferred)
+ NumberFormatCode const * const pFormatArr = aFormatSeq.getArray();
+ sal_Int32 nEdit, nDef, nMedium, nLong;
+ nEdit = nDef = nMedium = nLong = -1;
+ for ( sal_Int32 nElem = 0; nElem < nCnt; nElem++ )
+ {
+ if ( nEdit == -1 && pFormatArr[nElem].Index == NumberFormatIndex::DATE_SYS_DDMMYYYY )
+ nEdit = nElem;
+ if ( nDef == -1 && pFormatArr[nElem].Default )
+ nDef = nElem;
+ switch ( pFormatArr[nElem].Type )
+ {
+ case KNumberFormatType::MEDIUM :
+ {
+ if ( pFormatArr[nElem].Default )
+ {
+ nDef = nElem;
+ nMedium = nElem;
+ }
+ else if ( nMedium == -1 )
+ nMedium = nElem;
+ }
+ break;
+ case KNumberFormatType::LONG :
+ {
+ if ( pFormatArr[nElem].Default )
+ nLong = nElem;
+ else if ( nLong == -1 )
+ nLong = nElem;
+ }
+ break;
+ }
+ }
+ if ( nEdit == -1 )
+ {
+ if (areChecksEnabled())
+ {
+ outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getDateOrdersImpl: no edit" ) );
+ }
+ if ( nDef == -1 )
+ {
+ if (areChecksEnabled())
+ {
+ outputCheckMessage( appendLocaleInfo( u"LocaleDataWrapper::getDateOrdersImpl: no default" ) );
+ }
+ if ( nMedium != -1 )
+ nDef = nMedium;
+ else if ( nLong != -1 )
+ nDef = nLong;
+ else
+ nDef = 0;
+ }
+ nEdit = nDef;
+ }
+ LongDateOrder nDO = scanDateOrderImpl( pFormatArr[nEdit].Code );
+ if ( pFormatArr[nEdit].Type == KNumberFormatType::LONG )
+ { // normally this is not the case
+ nLongDateOrder = nDO;
+ nDateOrder = getDateOrderFromLongDateOrder(nDO);
+ }
+ else
+ {
+ // YDM should not occur in a short/medium date (i.e. no locale has
+ // that) and is nowhere handled.
+ nDateOrder = getDateOrderFromLongDateOrder(nDO);
+ if ( nLong == -1 )
+ nLongDateOrder = nDO;
+ else
+ nLongDateOrder = scanDateOrderImpl( pFormatArr[nLong].Code );
+ }
+}
+
+// --- digit grouping -------------------------------------------------
+
+void LocaleDataWrapper::loadDigitGrouping()
+{
+ /* TODO: This is a very simplified grouping setup that only serves its
+ * current purpose for Indian locales. A free-form flexible one would
+ * obtain grouping from locale data where it could be specified using, for
+ * example, codes like #,### and #,##,### that would generate the integer
+ * sequence. Needed additional API and a locale data element.
+ */
+
+ if (aGrouping.hasElements() && aGrouping[0])
+ return;
+
+ i18n::LanguageCountryInfo aLCInfo( getLanguageCountryInfo());
+ if (aLCInfo.Country.equalsIgnoreAsciiCase("IN") || // India
+ aLCInfo.Country.equalsIgnoreAsciiCase("BT") ) // Bhutan
+ {
+ aGrouping = { 3, 2, 0 };
+ }
+ else
+ {
+ aGrouping = { 3, 0, 0 };
+ }
+}
+
+const css::uno::Sequence< sal_Int32 >& LocaleDataWrapper::getDigitGrouping() const
+{
+ return aGrouping;
+}
+
+// --- simple number formatting helpers -------------------------------
+
+// The ImplAdd... methods are taken from class International and modified to
+// suit the needs.
+
+static void ImplAddUNum( OUStringBuffer& rBuf, sal_uInt64 nNumber )
+{
+ // fill temp buffer with digits
+ sal_Unicode aTempBuf[64];
+ sal_Unicode* pTempBuf = aTempBuf;
+ do
+ {
+ *pTempBuf = static_cast<sal_Unicode>(nNumber % 10) + '0';
+ pTempBuf++;
+ nNumber /= 10;
+ }
+ while ( nNumber );
+
+ // copy temp buffer to buffer passed
+ do
+ {
+ pTempBuf--;
+ rBuf.append(*pTempBuf);
+ }
+ while ( pTempBuf != aTempBuf );
+}
+
+static void ImplAddUNum( OUStringBuffer& rBuf, sal_uInt64 nNumber, int nMinLen )
+{
+ // fill temp buffer with digits
+ sal_Unicode aTempBuf[64];
+ sal_Unicode* pTempBuf = aTempBuf;
+ do
+ {
+ *pTempBuf = static_cast<sal_Unicode>(nNumber % 10) + '0';
+ pTempBuf++;
+ nNumber /= 10;
+ nMinLen--;
+ }
+ while ( nNumber );
+
+ // fill with zeros up to the minimal length
+ while ( nMinLen > 0 )
+ {
+ rBuf.append('0');
+ nMinLen--;
+ }
+
+ // copy temp buffer to real buffer
+ do
+ {
+ pTempBuf--;
+ rBuf.append(*pTempBuf);
+ }
+ while ( pTempBuf != aTempBuf );
+}
+
+static void ImplAddNum( OUStringBuffer& rBuf, sal_Int64 nNumber, int nMinLen )
+{
+ if (nNumber < 0)
+ {
+ rBuf.append('-');
+ nNumber = -nNumber;
+ }
+ return ImplAddUNum( rBuf, nNumber, nMinLen);
+}
+
+static void ImplAdd2UNum( OUStringBuffer& rBuf, sal_uInt16 nNumber )
+{
+ DBG_ASSERT( nNumber < 100, "ImplAdd2UNum() - Number >= 100" );
+
+ if ( nNumber < 10 )
+ {
+ rBuf.append('0');
+ rBuf.append(static_cast<char>(nNumber + '0'));
+ }
+ else
+ {
+ sal_uInt16 nTemp = nNumber % 10;
+ nNumber /= 10;
+ rBuf.append(static_cast<char>(nNumber + '0'));
+ rBuf.append(static_cast<char>(nTemp + '0'));
+ }
+}
+
+static void ImplAdd9UNum( OUStringBuffer& rBuf, sal_uInt32 nNumber )
+{
+ DBG_ASSERT( nNumber < 1000000000, "ImplAdd9UNum() - Number >= 1000000000" );
+
+ std::ostringstream ostr;
+ ostr.fill('0');
+ ostr.width(9);
+ ostr << nNumber;
+ std::string aStr = ostr.str();
+ rBuf.appendAscii(aStr.c_str(), aStr.size());
+}
+
+void LocaleDataWrapper::ImplAddFormatNum( OUStringBuffer& rBuf,
+ sal_Int64 nNumber, sal_uInt16 nDecimals, bool bUseThousandSep,
+ bool bTrailingZeros ) const
+{
+ OUStringBuffer aNumBuf(64);
+ sal_uInt16 nNumLen;
+
+ // negative number
+ sal_uInt64 abs;
+ if ( nNumber < 0 )
+ {
+ // Avoid overflow, map -2^63 -> 2^63 explicitly:
+ abs = nNumber == std::numeric_limits<sal_Int64>::min()
+ ? static_cast<sal_uInt64>(std::numeric_limits<sal_Int64>::min()) : nNumber * -1;
+ rBuf.append('-');
+ }
+ else
+ {
+ abs = nNumber;
+ }
+
+ // convert number
+ ImplAddUNum( aNumBuf, abs );
+ nNumLen = static_cast<sal_uInt16>(aNumBuf.getLength());
+
+ if ( nNumLen <= nDecimals )
+ {
+ // strip .0 in decimals?
+ if ( !nNumber && !bTrailingZeros )
+ {
+ rBuf.append('0');
+ }
+ else
+ {
+ // LeadingZero, insert 0
+ if ( isNumLeadingZero() )
+ {
+ rBuf.append('0');
+ }
+
+ // append decimal separator
+ rBuf.append( aLocaleDataItem.decimalSeparator );
+
+ // fill with zeros
+ sal_uInt16 i = 0;
+ while ( i < (nDecimals-nNumLen) )
+ {
+ rBuf.append('0');
+ i++;
+ }
+
+ // append decimals
+ rBuf.append(aNumBuf);
+ }
+ }
+ else
+ {
+ const OUString& rThoSep = aLocaleDataItem.thousandSeparator;
+
+ // copy number to buffer (excluding decimals)
+ sal_uInt16 nNumLen2 = nNumLen-nDecimals;
+ uno::Sequence< sal_Bool > aGroupPos;
+ if (bUseThousandSep)
+ aGroupPos = utl::DigitGroupingIterator::createForwardSequence(
+ nNumLen2, getDigitGrouping());
+ sal_uInt16 i = 0;
+ for (; i < nNumLen2; ++i )
+ {
+ rBuf.append(aNumBuf[i]);
+
+ // add thousand separator?
+ if ( bUseThousandSep && aGroupPos[i] )
+ rBuf.append( rThoSep );
+ }
+
+ // append decimals
+ if ( nDecimals )
+ {
+ rBuf.append( aLocaleDataItem.decimalSeparator );
+
+ bool bNullEnd = true;
+ while ( i < nNumLen )
+ {
+ if ( aNumBuf[i] != '0' )
+ bNullEnd = false;
+
+ rBuf.append(aNumBuf[i]);
+ i++;
+ }
+
+ // strip .0 in decimals?
+ if ( bNullEnd && !bTrailingZeros )
+ rBuf.setLength( rBuf.getLength() - (nDecimals + 1) );
+ }
+ }
+}
+
+// --- simple date and time formatting --------------------------------
+
+OUString LocaleDataWrapper::getDate( const Date& rDate ) const
+{
+//!TODO: leading zeros et al
+ OUStringBuffer aBuf(128);
+ sal_uInt16 nDay = rDate.GetDay();
+ sal_uInt16 nMonth = rDate.GetMonth();
+ sal_Int16 nYear = rDate.GetYear();
+ sal_uInt16 nYearLen;
+
+ if ( (true) /* IsDateCentury() */ )
+ nYearLen = 4;
+ else
+ {
+ nYearLen = 2;
+ nYear %= 100;
+ }
+
+ switch ( getDateOrder() )
+ {
+ case DateOrder::DMY :
+ ImplAdd2UNum( aBuf, nDay );
+ aBuf.append( aLocaleDataItem.dateSeparator );
+ ImplAdd2UNum( aBuf, nMonth );
+ aBuf.append( aLocaleDataItem.dateSeparator );
+ ImplAddNum( aBuf, nYear, nYearLen );
+ break;
+ case DateOrder::MDY :
+ ImplAdd2UNum( aBuf, nMonth );
+ aBuf.append( aLocaleDataItem.dateSeparator );
+ ImplAdd2UNum( aBuf, nDay );
+ aBuf.append( aLocaleDataItem.dateSeparator );
+ ImplAddNum( aBuf, nYear, nYearLen );
+ break;
+ default:
+ ImplAddNum( aBuf, nYear, nYearLen );
+ aBuf.append( aLocaleDataItem.dateSeparator );
+ ImplAdd2UNum( aBuf, nMonth );
+ aBuf.append( aLocaleDataItem.dateSeparator );
+ ImplAdd2UNum( aBuf, nDay );
+ }
+
+ return aBuf.makeStringAndClear();
+}
+
+OUString LocaleDataWrapper::getTime( const tools::Time& rTime, bool bSec, bool b100Sec ) const
+{
+//!TODO: leading zeros et al
+ OUStringBuffer aBuf(128);
+ sal_uInt16 nHour = rTime.GetHour();
+
+ nHour %= 24;
+
+ ImplAdd2UNum( aBuf, nHour );
+ aBuf.append( aLocaleDataItem.timeSeparator );
+ ImplAdd2UNum( aBuf, rTime.GetMin() );
+ if ( bSec )
+ {
+ aBuf.append( aLocaleDataItem.timeSeparator );
+ ImplAdd2UNum( aBuf, rTime.GetSec() );
+
+ if ( b100Sec )
+ {
+ aBuf.append( aLocaleDataItem.time100SecSeparator );
+ ImplAdd9UNum( aBuf, rTime.GetNanoSec() );
+ }
+ }
+
+ return aBuf.makeStringAndClear();
+}
+
+OUString LocaleDataWrapper::getDuration( const tools::Duration& rDuration, bool bSec, bool b100Sec ) const
+{
+ OUStringBuffer aBuf(128);
+
+ if ( rDuration.IsNegative() )
+ aBuf.append(' ');
+
+ sal_Int64 nHours = static_cast<sal_Int64>(rDuration.GetDays()) * 24 +
+ (rDuration.IsNegative() ?
+ -static_cast<sal_Int64>(rDuration.GetTime().GetHour()) :
+ rDuration.GetTime().GetHour());
+ if ( (true) /* IsTimeLeadingZero() */ )
+ ImplAddNum( aBuf, nHours, 2 );
+ else
+ ImplAddNum( aBuf, nHours, 1 );
+ aBuf.append( aLocaleDataItem.timeSeparator );
+ ImplAdd2UNum( aBuf, rDuration.GetTime().GetMin() );
+ if ( bSec )
+ {
+ aBuf.append( aLocaleDataItem.timeSeparator );
+ ImplAdd2UNum( aBuf, rDuration.GetTime().GetSec() );
+
+ if ( b100Sec )
+ {
+ aBuf.append( aLocaleDataItem.time100SecSeparator );
+ ImplAdd9UNum( aBuf, rDuration.GetTime().GetNanoSec() );
+ }
+ }
+
+ return aBuf.makeStringAndClear();
+}
+
+// --- simple number formatting ---------------------------------------
+
+static size_t ImplGetNumberStringLengthGuess( const css::i18n::LocaleDataItem2& rLocaleDataItem, sal_uInt16 nDecimals )
+{
+ // approximately 3.2 bits per digit
+ const size_t nDig = ((sizeof(sal_Int64) * 8) / 3) + 1;
+ // digits, separators (pessimized for insane "every digit may be grouped"), leading zero, sign
+ size_t nGuess = ((nDecimals < nDig) ?
+ (((nDig - nDecimals) * rLocaleDataItem.thousandSeparator.getLength()) + nDig) :
+ nDecimals) + rLocaleDataItem.decimalSeparator.getLength() + 3;
+ return nGuess;
+}
+
+OUString LocaleDataWrapper::getNum( sal_Int64 nNumber, sal_uInt16 nDecimals,
+ bool bUseThousandSep, bool bTrailingZeros ) const
+{
+ // check if digits and separators will fit into fixed buffer or allocate
+ size_t nGuess = ImplGetNumberStringLengthGuess( aLocaleDataItem, nDecimals );
+ OUStringBuffer aBuf(int(nGuess + 16));
+
+ ImplAddFormatNum( aBuf, nNumber, nDecimals,
+ bUseThousandSep, bTrailingZeros );
+
+ return aBuf.makeStringAndClear();
+}
+
+OUString LocaleDataWrapper::getCurr( sal_Int64 nNumber, sal_uInt16 nDecimals,
+ std::u16string_view rCurrencySymbol, bool bUseThousandSep ) const
+{
+ sal_Unicode cZeroChar = getCurrZeroChar();
+
+ // check if digits and separators will fit into fixed buffer or allocate
+ size_t nGuess = ImplGetNumberStringLengthGuess( aLocaleDataItem, nDecimals );
+ OUStringBuffer aNumBuf(sal_Int32(nGuess + 16));
+
+ bool bNeg;
+ if ( nNumber < 0 )
+ {
+ bNeg = true;
+ nNumber *= -1;
+ }
+ else
+ bNeg = false;
+
+ // convert number
+ ImplAddFormatNum( aNumBuf, nNumber, nDecimals,
+ bUseThousandSep, true );
+ const sal_Int32 nNumLen = aNumBuf.getLength();
+
+ // replace zeros with zero character
+ if ( (cZeroChar != '0') && nDecimals /* && IsNumTrailingZeros() */ )
+ {
+ sal_uInt16 i;
+ bool bZero = true;
+
+ sal_uInt16 nNumBufIndex = nNumLen-nDecimals;
+ i = 0;
+ do
+ {
+ if ( aNumBuf[nNumBufIndex] != '0' )
+ {
+ bZero = false;
+ break;
+ }
+
+ nNumBufIndex++;
+ i++;
+ }
+ while ( i < nDecimals );
+
+ if ( bZero )
+ {
+ nNumBufIndex = nNumLen-nDecimals;
+ i = 0;
+ do
+ {
+ aNumBuf[nNumBufIndex] = cZeroChar;
+ nNumBufIndex++;
+ i++;
+ }
+ while ( i < nDecimals );
+ }
+ }
+
+ OUString aCur;
+ if ( !bNeg )
+ {
+ switch( getCurrPositiveFormat() )
+ {
+ case 0:
+ aCur = rCurrencySymbol + aNumBuf;
+ break;
+ case 1:
+ aCur = aNumBuf + rCurrencySymbol;
+ break;
+ case 2:
+ aCur = OUString::Concat(rCurrencySymbol) + " " + aNumBuf;
+ break;
+ case 3:
+ aCur = aNumBuf + " " + rCurrencySymbol;
+ break;
+ }
+ }
+ else
+ {
+ switch( getCurrNegativeFormat() )
+ {
+ case 0:
+ aCur = OUString::Concat("(") + rCurrencySymbol + aNumBuf + ")";
+ break;
+ case 1:
+ aCur = OUString::Concat("-") + rCurrencySymbol + aNumBuf;
+ break;
+ case 2:
+ aCur = OUString::Concat(rCurrencySymbol) + "-" + aNumBuf;
+ break;
+ case 3:
+ aCur = rCurrencySymbol + aNumBuf + "-";
+ break;
+ case 4:
+ aCur = "(" + aNumBuf + rCurrencySymbol + ")";
+ break;
+ case 5:
+ aCur = "-" + aNumBuf + rCurrencySymbol;
+ break;
+ case 6:
+ aCur = aNumBuf + "-" + rCurrencySymbol;
+ break;
+ case 7:
+ aCur = aNumBuf + rCurrencySymbol + "-";
+ break;
+ case 8:
+ aCur = "-" + aNumBuf + " " + rCurrencySymbol;
+ break;
+ case 9:
+ aCur = OUString::Concat("-") + rCurrencySymbol + " " + aNumBuf;
+ break;
+ case 10:
+ aCur = aNumBuf + " " + rCurrencySymbol + "-";
+ break;
+ case 11:
+ aCur = OUString::Concat(rCurrencySymbol) + " -" + aNumBuf;
+ break;
+ case 12:
+ aCur = OUString::Concat(rCurrencySymbol) + " " + aNumBuf + "-";
+ break;
+ case 13:
+ aCur = aNumBuf + "- " + rCurrencySymbol;
+ break;
+ case 14:
+ aCur = OUString::Concat("(") + rCurrencySymbol + " " + aNumBuf + ")";
+ break;
+ case 15:
+ aCur = "(" + aNumBuf + " " + rCurrencySymbol + ")";
+ break;
+ }
+ }
+
+ return aCur;
+}
+
+// --- number parsing -------------------------------------------------
+
+double LocaleDataWrapper::stringToDouble( std::u16string_view aString, bool bUseGroupSep,
+ rtl_math_ConversionStatus* pStatus, sal_Int32* pParseEnd ) const
+{
+ const sal_Unicode* pParseEndChar;
+ double fValue = stringToDouble(aString.data(), aString.data() + aString.size(), bUseGroupSep, pStatus, &pParseEndChar);
+ if (pParseEnd)
+ *pParseEnd = pParseEndChar - aString.data();
+ return fValue;
+}
+
+double LocaleDataWrapper::stringToDouble( const sal_Unicode* pBegin, const sal_Unicode* pEnd, bool bUseGroupSep,
+ rtl_math_ConversionStatus* pStatus, const sal_Unicode** ppParseEnd ) const
+{
+ const sal_Unicode cGroupSep = (bUseGroupSep ? aLocaleDataItem.thousandSeparator[0] : 0);
+ rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
+ const sal_Unicode* pParseEnd = nullptr;
+ double fValue = rtl_math_uStringToDouble( pBegin, pEnd, aLocaleDataItem.decimalSeparator[0], cGroupSep, &eStatus, &pParseEnd);
+ bool bTryAlt = (pParseEnd < pEnd && !aLocaleDataItem.decimalSeparatorAlternative.isEmpty() &&
+ *pParseEnd == aLocaleDataItem.decimalSeparatorAlternative.toChar());
+ // Try re-parsing with alternative if that was the reason to stop.
+ if (bTryAlt)
+ fValue = rtl_math_uStringToDouble( pBegin, pEnd, aLocaleDataItem.decimalSeparatorAlternative.toChar(), cGroupSep, &eStatus, &pParseEnd);
+ if (pStatus)
+ *pStatus = eStatus;
+ if (ppParseEnd)
+ *ppParseEnd = pParseEnd;
+ return fValue;
+}
+
+// --- mixed ----------------------------------------------------------
+
+LanguageTag LocaleDataWrapper::getLoadedLanguageTag() const
+{
+ LanguageCountryInfo aLCInfo = getLanguageCountryInfo();
+ return LanguageTag( lang::Locale( aLCInfo.Language, aLCInfo.Country, aLCInfo.Variant ));
+}
+
+OUString LocaleDataWrapper::appendLocaleInfo(std::u16string_view rDebugMsg) const
+{
+ LanguageTag aLoaded = getLoadedLanguageTag();
+ return OUString::Concat(rDebugMsg) + "\n" + maLanguageTag.getBcp47() + " requested\n"
+ + aLoaded.getBcp47() + " loaded";
+}
+
+// static
+void LocaleDataWrapper::outputCheckMessage( std::u16string_view rMsg )
+{
+ outputCheckMessage(OUStringToOString(rMsg, RTL_TEXTENCODING_UTF8).getStr());
+}
+
+// static
+void LocaleDataWrapper::outputCheckMessage( const char* pStr )
+{
+ fprintf( stderr, "\n%s\n", pStr);
+ fflush( stderr);
+ SAL_WARN("unotools.i18n", pStr);
+}
+
+// static
+void LocaleDataWrapper::evaluateLocaleDataChecking()
+{
+ // Using the rtl_Instance template here wouldn't solve all threaded write
+ // accesses, since we want to assign the result to the static member
+ // variable and would need to dereference the pointer returned and assign
+ // the value unguarded. This is the same pattern manually coded.
+ sal_uInt8 nCheck = nLocaleDataChecking;
+ if (!nCheck)
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex());
+ nCheck = nLocaleDataChecking;
+ if (!nCheck)
+ {
+#ifdef DBG_UTIL
+ nCheck = 1;
+#else
+ const char* pEnv = getenv( "OOO_ENABLE_LOCALE_DATA_CHECKS");
+ if (pEnv && (pEnv[0] == 'Y' || pEnv[0] == 'y' || pEnv[0] == '1'))
+ nCheck = 1;
+ else
+ nCheck = 2;
+#endif
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ nLocaleDataChecking = nCheck;
+ }
+ }
+ else {
+ OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER();
+ }
+}
+
+// --- XLocaleData3 ----------------------------------------------------------
+
+css::uno::Sequence< css::i18n::Calendar2 > LocaleDataWrapper::getAllCalendars() const
+{
+ try
+ {
+ return xLD->getAllCalendars2( getMyLocale() );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "getAllCalendars" );
+ }
+ return {};
+}
+
+// --- XLocaleData4 ----------------------------------------------------------
+
+const css::uno::Sequence< OUString > & LocaleDataWrapper::getDateAcceptancePatterns() const
+{
+ return aDateAcceptancePatterns;
+}
+
+// --- Override layer --------------------------------------------------------
+
+void LocaleDataWrapper::loadDateAcceptancePatterns(
+ const std::vector<OUString> & rPatterns )
+{
+ if (!aDateAcceptancePatterns.hasElements() || rPatterns.empty())
+ {
+ try
+ {
+ aDateAcceptancePatterns = xLD->getDateAcceptancePatterns( maLanguageTag.getLocale() );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "setDateAcceptancePatterns" );
+ }
+ if (rPatterns.empty())
+ return; // just a reset
+ if (!aDateAcceptancePatterns.hasElements())
+ {
+ aDateAcceptancePatterns = comphelper::containerToSequence(rPatterns);
+ return;
+ }
+ }
+
+ // Earlier versions checked for presence of the full date pattern with
+ // aDateAcceptancePatterns[0] == rPatterns[0] and prepended that if not.
+ // This lead to confusion if the patterns were intentionally specified
+ // without, giving entirely a different DMY order, see tdf#150288.
+ // Not checking this and accepting the given patterns as is may result in
+ // the user shooting themself in the foot, but we can't have both.
+ aDateAcceptancePatterns = comphelper::containerToSequence(rPatterns);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/nativenumberwrapper.cxx b/unotools/source/i18n/nativenumberwrapper.cxx
new file mode 100644
index 0000000000..882005ebf7
--- /dev/null
+++ b/unotools/source/i18n/nativenumberwrapper.cxx
@@ -0,0 +1,110 @@
+/* -*- 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 <unotools/nativenumberwrapper.hxx>
+#include <com/sun/star/i18n/NativeNumberSupplier2.hpp>
+#include <comphelper/diagnose_ex.hxx>
+
+using namespace ::com::sun::star;
+
+NativeNumberWrapper::NativeNumberWrapper(
+ const uno::Reference< uno::XComponentContext > & rxContext
+ )
+{
+ xNNS = i18n::NativeNumberSupplier2::create(rxContext);
+}
+
+NativeNumberWrapper::~NativeNumberWrapper()
+{
+}
+
+OUString
+NativeNumberWrapper::getNativeNumberString(
+ const OUString& rNumberString,
+ const css::lang::Locale& rLocale,
+ sal_Int16 nNativeNumberMode) const
+{
+ try
+ {
+ if ( xNNS.is() )
+ return xNNS->getNativeNumberString(rNumberString, rLocale, nNativeNumberMode);
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return OUString();
+}
+
+OUString
+NativeNumberWrapper::getNativeNumberStringParams(
+ const OUString& rNumberString,
+ const css::lang::Locale& rLocale,
+ sal_Int16 nNativeNumberMode,
+ const OUString& rNativeNumberParams) const
+{
+ try
+ {
+ if ( xNNS.is() )
+ return xNNS->getNativeNumberStringParams(rNumberString, rLocale, nNativeNumberMode,
+ rNativeNumberParams);
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return OUString();
+}
+
+i18n::NativeNumberXmlAttributes
+NativeNumberWrapper::convertToXmlAttributes(
+ const css::lang::Locale& rLocale,
+ sal_Int16 nNativeNumberMode ) const
+{
+ try
+ {
+ if ( xNNS.is() )
+ return xNNS->convertToXmlAttributes( rLocale, nNativeNumberMode );
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return i18n::NativeNumberXmlAttributes();
+}
+
+sal_Int16
+NativeNumberWrapper::convertFromXmlAttributes(
+ const i18n::NativeNumberXmlAttributes& rAttr ) const
+{
+ try
+ {
+ if ( xNNS.is() )
+ return xNNS->convertFromXmlAttributes( rAttr );
+ }
+ catch ( const uno::Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/resmgr.cxx b/unotools/source/i18n/resmgr.cxx
new file mode 100644
index 0000000000..2cf013d161
--- /dev/null
+++ b/unotools/source/i18n/resmgr.cxx
@@ -0,0 +1,321 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <boost/version.hpp>
+#if BOOST_VERSION < 106700
+// Needed when #include <boost/locale.hpp> below includes Boost 1.65.1
+// workdir/UnpackedTarball/boost/boost/locale/format.hpp using "std::auto_ptr<data> d;", but must
+// come very early here in case <memory> is already (indirectly) included earlier:
+#include <config_libcxx.h>
+#if HAVE_LIBCPP
+#define _LIBCPP_ENABLE_CXX17_REMOVED_AUTO_PTR
+#elif defined _MSC_VER
+#define _HAS_AUTO_PTR_ETC 1
+#endif
+#endif
+
+#include <sal/config.h>
+
+#include <cassert>
+
+#include <string.h>
+#include <stdio.h>
+#if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID && !defined EMSCRIPTEN
+# include <libintl.h>
+#endif
+
+#include <comphelper/lok.hxx>
+#include <unotools/resmgr.hxx>
+#include <osl/thread.h>
+#include <osl/file.hxx>
+#include <rtl/crc.h>
+#include <rtl/bootstrap.hxx>
+#include <i18nlangtag/languagetag.hxx>
+
+#include <boost/locale.hpp>
+#include <boost/locale/gnu_gettext.hpp>
+
+#include <unordered_map>
+
+#ifdef ANDROID
+#include <osl/detail/android-bootstrap.h>
+#endif
+
+#ifdef EMSCRIPTEN
+#include <osl/detail/emscripten-bootstrap.h>
+#endif
+
+#if defined(_WIN32) && defined(DBG_UTIL)
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <prewin.h>
+#include <crtdbg.h>
+#include <postwin.h>
+#endif
+
+namespace
+{
+ OUString createFromUtf8(const char* data, size_t size)
+ {
+ OUString aTarget;
+ bool bSuccess = rtl_convertStringToUString(&aTarget.pData,
+ data,
+ size,
+ RTL_TEXTENCODING_UTF8,
+ RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR|RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR|RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR);
+ (void) bSuccess;
+ assert(bSuccess);
+ return aTarget;
+ }
+
+ OString genKeyId(const OString& rGenerator)
+ {
+ sal_uInt32 nCRC = rtl_crc32(0, rGenerator.getStr(), rGenerator.getLength());
+ // Use simple ASCII characters, exclude I, l, 1 and O, 0 to avoid confusing IDs
+ static const char sSymbols[] =
+ "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
+ char sKeyId[6];
+ for (short nKeyInd = 0; nKeyInd < 5; ++nKeyInd)
+ {
+ sKeyId[nKeyInd] = sSymbols[(nCRC & 63) % strlen(sSymbols)];
+ nCRC >>= 6;
+ }
+ sKeyId[5] = '\0';
+ return sKeyId;
+ }
+}
+
+#if defined(_WIN32) && defined(DBG_UTIL)
+static int IgnoringCrtReportHook(int reportType, wchar_t *message, int * /* returnValue */)
+{
+ OUString sType;
+ if (reportType == _CRT_WARN)
+ sType = "WARN";
+ else if (reportType == _CRT_ERROR)
+ sType = "ERROR";
+ else if (reportType == _CRT_ASSERT)
+ sType = "ASSERT";
+ else
+ sType = "?(" + OUString::number(reportType) + ")";
+
+ SAL_WARN("unotools.i18n", "CRT Report Hook: " << sType << ": " << OUString(o3tl::toU(message)));
+
+ return TRUE;
+}
+#endif
+
+
+namespace Translate
+{
+ std::locale Create(std::string_view aPrefixName, const LanguageTag& rLocale)
+ {
+ static std::unordered_map<OString, std::locale> aCache;
+ OString sIdentifier = rLocale.getGlibcLocaleString(u".UTF-8").toUtf8();
+ OString sUnique = sIdentifier + aPrefixName;
+ auto aFind = aCache.find(sUnique);
+ if (aFind != aCache.end())
+ return aFind->second;
+ boost::locale::generator gen;
+#if BOOST_VERSION < 108100
+ gen.characters(boost::locale::char_facet);
+ gen.categories(boost::locale::message_facet | boost::locale::information_facet);
+#else
+ gen.characters(boost::locale::char_facet_t::char_f);
+ gen.categories(boost::locale::category_t::message | boost::locale::category_t::information);
+#endif
+#if defined(ANDROID) || defined(EMSCRIPTEN)
+ OString sPath(OString(lo_get_app_data_dir()) + "/program/resource");
+#else
+ OUString uri("$BRAND_BASE_DIR/$BRAND_SHARE_RESOURCE_SUBDIR/");
+ rtl::Bootstrap::expandMacros(uri);
+ OUString path;
+ osl::File::getSystemPathFromFileURL(uri, path);
+#if defined _WIN32
+ // add_messages_path is documented to treat path string in the *created* locale's encoding
+ // on Windows; creating an UTF-8 encoding, we're lucky to have Unicode path support here.
+ constexpr rtl_TextEncoding eEncoding = RTL_TEXTENCODING_UTF8;
+#else
+ const rtl_TextEncoding eEncoding = osl_getThreadTextEncoding();
+#endif
+ OString sPath(OUStringToOString(path, eEncoding));
+#endif
+ gen.add_messages_path(std::string(sPath));
+#if defined UNX && !defined MACOSX && !defined IOS && !defined ANDROID && !defined EMSCRIPTEN
+ // allow gettext to find these .mo files e.g. so gtk dialogs can use them
+ bindtextdomain(aPrefixName.data(), sPath.getStr());
+ // tdf#131069 gtk, and anything sane, always wants utf-8 strings as output
+ bind_textdomain_codeset(aPrefixName.data(), "UTF-8");
+#endif
+ gen.add_messages_domain(aPrefixName.data());
+
+#if defined(_WIN32) && defined(DBG_UTIL)
+ // With a newer C++ debug runtime (in an --enable-dbgutil build), passing an invalid locale
+ // name causes an attempt to display an error dialog. Which does not even show up, at least
+ // for me, but instead the process (gengal, at least) just hangs. Which is far from ideal.
+
+ // Passing a POSIX-style locale name to the std::locale constructor on Windows is a bit odd,
+ // but apparently in the normal C++ runtime it "just" causes an exception to be thrown, that
+ // boost catches (see the loadable(std::string name) in boost's
+ // libs\locale\src\std\std_backend.cpp), and then instead uses the Windows style locale name
+ // it knows how to construct. (Why does it even try the POSIX style name I can't
+ // understand.)
+
+ // Actually it isn't just the locale name part "en_US" of a locale like "en_US.UTF-8" that
+ // is problematic, but also the encoding part, "UTF-8". The Microsoft C/C++ library does not
+ // support UTF-8 locales. The error message that our own report hook catches says:
+ // "f:\dd\vctools\crt\crtw32\stdcpp\xmbtowc.c(89) : Assertion failed: ploc->_Mbcurmax == 1
+ // || ploc->_Mbcurmax == 2". Clearly in a UTF-8 locale (perhaps one that boost internally
+ // constructs?) the maximum bytes per character will be more than 2.
+
+ // With a debug C++ runtime, we need to avoid the error dialog, and just ignore the error.
+
+ struct CrtSetReportHook
+ {
+ int mnCrtSetReportHookSucceeded;
+
+ CrtSetReportHook()
+ {
+ mnCrtSetReportHookSucceeded = _CrtSetReportHookW2(_CRT_RPTHOOK_INSTALL, IgnoringCrtReportHook);
+ }
+
+ ~CrtSetReportHook()
+ {
+ if (mnCrtSetReportHookSucceeded >= 0)
+ _CrtSetReportHookW2(_CRT_RPTHOOK_REMOVE, IgnoringCrtReportHook);
+ }
+ } aHook;
+
+#endif
+
+ std::locale aRet(gen(std::string(sIdentifier)));
+
+ aCache[sUnique] = aRet;
+ return aRet;
+ }
+
+ OUString get(TranslateId sContextAndId, const std::locale &loc)
+ {
+ assert(!strchr(sContextAndId.getId(), '\004') && "should be using nget, not get");
+
+ //if it's a key id locale, generate it here
+ if (std::use_facet<boost::locale::info>(loc).language() == "qtz")
+ {
+ OString sKeyId(genKeyId(OString::Concat(sContextAndId.mpContext) + "|" + std::string_view(sContextAndId.getId())));
+ return OUString::fromUtf8(sKeyId) + u"\u2016" + createFromUtf8(sContextAndId.getId(), strlen(sContextAndId.getId()));
+ }
+
+ //otherwise translate it
+ const std::string ret = boost::locale::pgettext(sContextAndId.mpContext, sContextAndId.getId(), loc);
+ OUString result(ExpandVariables(createFromUtf8(ret.data(), ret.size())));
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // If it is de-CH, change sharp s to double s.
+ if (std::use_facet<boost::locale::info>(loc).country() == "CH" &&
+ std::use_facet<boost::locale::info>(loc).language() == "de")
+ result = result.replaceAll(OUString::fromUtf8("\xC3\x9F"), "ss");
+ }
+ return result;
+ }
+
+ OUString nget(TranslateNId aContextSingularPlural, int n, const std::locale &loc)
+ {
+ //if it's a key id locale, generate it here
+ if (std::use_facet<boost::locale::info>(loc).language() == "qtz")
+ {
+ OString sKeyId(genKeyId(OString::Concat(aContextSingularPlural.mpContext) + "|" + aContextSingularPlural.mpSingular));
+ const char* pForm = n == 0 ? aContextSingularPlural.mpSingular : aContextSingularPlural.mpPlural;
+ return OUString::fromUtf8(sKeyId) + u"\u2016" + createFromUtf8(pForm, strlen(pForm));
+ }
+
+ //otherwise translate it
+ const std::string ret = boost::locale::npgettext(aContextSingularPlural.mpContext, aContextSingularPlural.mpSingular, aContextSingularPlural.mpPlural, n, loc);
+ OUString result(ExpandVariables(createFromUtf8(ret.data(), ret.size())));
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ if (std::use_facet<boost::locale::info>(loc).country() == "CH" &&
+ std::use_facet<boost::locale::info>(loc).language() == "de")
+ result = result.replaceAll(OUString::fromUtf8("\xC3\x9F"), "ss");
+ }
+ return result;
+ }
+
+ static ResHookProc pImplResHookProc = nullptr;
+
+ OUString ExpandVariables(const OUString& rString)
+ {
+ if (pImplResHookProc)
+ return pImplResHookProc(rString);
+ return rString;
+ }
+
+ void SetReadStringHook( ResHookProc pProc )
+ {
+ pImplResHookProc = pProc;
+ }
+
+ ResHookProc GetReadStringHook()
+ {
+ return pImplResHookProc;
+ }
+}
+
+bool TranslateId::operator==(const TranslateId& other) const
+{
+ if (mpContext == nullptr || other.mpContext == nullptr)
+ {
+ if (mpContext != other.mpContext)
+ return false;
+ }
+ else if (strcmp(mpContext, other.mpContext) != 0)
+ return false;
+
+ if (mpId == nullptr || other.mpId == nullptr)
+ {
+ return mpId == other.mpId;
+ }
+ return strcmp(getId(),other.getId()) == 0;
+}
+
+bool TranslateNId::operator==(const TranslateNId& other) const
+{
+ if (mpContext == nullptr || other.mpContext == nullptr)
+ {
+ if (mpContext != other.mpContext)
+ return false;
+ }
+ else if (strcmp(mpContext, other.mpContext) != 0)
+ return false;
+
+ if (mpSingular == nullptr || other.mpSingular == nullptr)
+ {
+ if (mpSingular != other.mpSingular)
+ return false;
+ }
+ else if (strcmp(mpSingular, other.mpSingular) != 0)
+ return false;
+
+ if (mpPlural == nullptr || other.mpPlural == nullptr)
+ {
+ return mpPlural == other.mpPlural;
+ }
+ return strcmp(mpPlural,other.mpPlural) == 0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/textsearch.cxx b/unotools/source/i18n/textsearch.cxx
new file mode 100644
index 0000000000..f61aeaf1a2
--- /dev/null
+++ b/unotools/source/i18n/textsearch.cxx
@@ -0,0 +1,357 @@
+/* -*- 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 <cstdlib>
+#include <string_view>
+
+#include <i18nlangtag/languagetag.hxx>
+#include <i18nutil/searchopt.hxx>
+#include <i18nutil/transliteration.hxx>
+#include <com/sun/star/util/TextSearch2.hpp>
+#include <com/sun/star/util/SearchAlgorithms2.hpp>
+#include <com/sun/star/util/SearchFlags.hpp>
+#include <unotools/charclass.hxx>
+#include <comphelper/processfactory.hxx>
+#include <unotools/textsearch.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <mutex>
+
+using namespace ::com::sun::star::util;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+
+namespace utl
+{
+
+SearchParam::SearchParam( const OUString &rText,
+ SearchType eType,
+ bool bCaseSensitive,
+ sal_uInt32 cWildEscChar,
+ bool bWildMatchSel )
+{
+ sSrchStr = rText;
+ m_eSrchType = eType;
+
+ m_cWildEscChar = cWildEscChar;
+
+ m_bCaseSense = bCaseSensitive;
+ m_bWildMatchSel = bWildMatchSel;
+}
+
+SearchParam::SearchParam( const SearchParam& rParam )
+{
+ sSrchStr = rParam.sSrchStr;
+ m_eSrchType = rParam.m_eSrchType;
+
+ m_cWildEscChar = rParam.m_cWildEscChar;
+
+ m_bCaseSense = rParam.m_bCaseSense;
+ m_bWildMatchSel = rParam.m_bWildMatchSel;
+}
+
+SearchParam::~SearchParam() {}
+
+static bool lcl_Equals( const i18nutil::SearchOptions2& rSO1, const i18nutil::SearchOptions2& rSO2 )
+{
+ return
+ rSO1.AlgorithmType2 == rSO2.AlgorithmType2 &&
+ rSO1.WildcardEscapeCharacter == rSO2.WildcardEscapeCharacter &&
+ rSO1.searchFlag == rSO2.searchFlag &&
+ rSO1.searchString == rSO2.searchString &&
+ rSO1.replaceString == rSO2.replaceString &&
+ rSO1.changedChars == rSO2.changedChars &&
+ rSO1.deletedChars == rSO2.deletedChars &&
+ rSO1.insertedChars == rSO2.insertedChars &&
+ rSO1.Locale.Language == rSO2.Locale.Language &&
+ rSO1.Locale.Country == rSO2.Locale.Country &&
+ rSO1.Locale.Variant == rSO2.Locale.Variant &&
+ rSO1.transliterateFlags == rSO2.transliterateFlags;
+}
+
+namespace
+{
+ struct CachedTextSearch
+ {
+ std::mutex mutex;
+ i18nutil::SearchOptions2 Options;
+ css::uno::Reference< css::util::XTextSearch2 > xTextSearch;
+ };
+}
+
+Reference<XTextSearch2> TextSearch::getXTextSearch( const i18nutil::SearchOptions2& rPara )
+{
+ static CachedTextSearch theCachedTextSearch;
+
+ std::scoped_lock aGuard(theCachedTextSearch.mutex);
+
+ if ( lcl_Equals(theCachedTextSearch.Options, rPara) )
+ return theCachedTextSearch.xTextSearch;
+
+ Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ theCachedTextSearch.xTextSearch.set( ::TextSearch2::create(xContext) );
+ theCachedTextSearch.xTextSearch->setOptions2( rPara.toUnoSearchOptions2() );
+ theCachedTextSearch.Options = rPara;
+
+ return theCachedTextSearch.xTextSearch;
+}
+
+TextSearch::TextSearch(const SearchParam & rParam, LanguageType eLang )
+{
+ if( LANGUAGE_NONE == eLang )
+ eLang = LANGUAGE_SYSTEM;
+ css::lang::Locale aLocale( LanguageTag::convertToLocale( eLang ) );
+
+ Init( rParam, aLocale);
+}
+
+TextSearch::TextSearch(const SearchParam & rParam, const CharClass& rCClass )
+{
+ Init( rParam, rCClass.getLanguageTag().getLocale() );
+}
+
+TextSearch::TextSearch( const i18nutil::SearchOptions2& rPara )
+{
+ xTextSearch = getXTextSearch( rPara );
+}
+
+void TextSearch::Init( const SearchParam & rParam,
+ const css::lang::Locale& rLocale )
+{
+ // convert SearchParam to the UNO SearchOptions2
+ i18nutil::SearchOptions2 aSOpt;
+
+ switch( rParam.GetSrchType() )
+ {
+ case SearchParam::SearchType::Wildcard:
+ aSOpt.AlgorithmType2 = SearchAlgorithms2::WILDCARD;
+ aSOpt.WildcardEscapeCharacter = rParam.GetWildEscChar();
+ if (rParam.IsWildMatchSel())
+ aSOpt.searchFlag |= SearchFlags::WILD_MATCH_SELECTION;
+ break;
+
+ case SearchParam::SearchType::Regexp:
+ aSOpt.AlgorithmType2 = SearchAlgorithms2::REGEXP;
+ break;
+
+ case SearchParam::SearchType::Normal:
+ aSOpt.AlgorithmType2 = SearchAlgorithms2::ABSOLUTE;
+ break;
+
+ default:
+ for (;;) std::abort();
+ }
+ aSOpt.searchString = rParam.GetSrchStr();
+ aSOpt.replaceString = "";
+ aSOpt.Locale = rLocale;
+ aSOpt.transliterateFlags = TransliterationFlags::NONE;
+ if( !rParam.IsCaseSensitive() )
+ {
+ aSOpt.searchFlag |= SearchFlags::ALL_IGNORE_CASE;
+ aSOpt.transliterateFlags |= TransliterationFlags::IGNORE_CASE;
+ }
+
+ xTextSearch = getXTextSearch( aSOpt );
+}
+
+void TextSearch::SetLocale( const i18nutil::SearchOptions2& rOptions,
+ const css::lang::Locale& rLocale )
+{
+ i18nutil::SearchOptions2 aSOpt( rOptions );
+ aSOpt.Locale = rLocale;
+
+ xTextSearch = getXTextSearch( aSOpt );
+}
+
+TextSearch::~TextSearch()
+{
+}
+
+/*
+ * General search methods. These methods will call the respective
+ * methods, such as ordinary string searching or regular expression
+ * matching, using the method pointer.
+ */
+bool TextSearch::SearchForward( const OUString &rStr,
+ sal_Int32* pStart, sal_Int32* pEnd,
+ css::util::SearchResult* pRes)
+{
+ bool bRet = false;
+ try
+ {
+ if( xTextSearch.is() )
+ {
+ SearchResult aRet( xTextSearch->searchForward( rStr, *pStart, *pEnd ));
+ if( aRet.subRegExpressions > 0 )
+ {
+ bRet = true;
+ // the XTextsearch returns in startOffset the higher position
+ // and the endposition is always exclusive.
+ // The caller of this function will have in startPos the
+ // lower pos. and end
+ *pStart = aRet.startOffset[ 0 ];
+ *pEnd = aRet.endOffset[ 0 ];
+ if( pRes )
+ *pRes = aRet;
+ }
+ }
+ }
+ catch ( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return bRet;
+}
+
+bool TextSearch::searchForward( const OUString &rStr )
+{
+ sal_Int32 pStart = 0;
+ sal_Int32 pEnd = rStr.getLength();
+
+ bool bResult = SearchForward(rStr, &pStart, &pEnd);
+
+ return bResult;
+}
+
+bool TextSearch::SearchBackward( const OUString & rStr, sal_Int32* pStart,
+ sal_Int32* pEnd, SearchResult* pRes )
+{
+ bool bRet = false;
+ try
+ {
+ if( xTextSearch.is() )
+ {
+ SearchResult aRet( xTextSearch->searchBackward( rStr, *pStart, *pEnd ));
+ if( aRet.subRegExpressions )
+ {
+ bRet = true;
+ // the XTextsearch returns in startOffset the higher position
+ // and the endposition is always exclusive.
+ // The caller of this function will have in startPos the
+ // lower pos. and end
+ *pEnd = aRet.startOffset[ 0 ];
+ *pStart = aRet.endOffset[ 0 ];
+ if( pRes )
+ *pRes = aRet;
+ }
+ }
+ }
+ catch ( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ return bRet;
+}
+
+void TextSearch::ReplaceBackReferences( OUString& rReplaceStr, std::u16string_view rStr, const SearchResult& rResult ) const
+{
+ if( rResult.subRegExpressions <= 0 )
+ return;
+
+ sal_Unicode sFndChar;
+ sal_Int32 i;
+ OUStringBuffer sBuff(rReplaceStr.getLength()*4);
+ for(i = 0; i < rReplaceStr.getLength(); i++)
+ {
+ if( rReplaceStr[i] == '&')
+ {
+ sal_Int32 nStart = rResult.startOffset[0];
+ sal_Int32 nLength = rResult.endOffset[0] - rResult.startOffset[0];
+ sBuff.append(rStr.substr(nStart, nLength));
+ }
+ else if((i < rReplaceStr.getLength() - 1) && rReplaceStr[i] == '$')
+ {
+ sFndChar = rReplaceStr[ i + 1 ];
+ switch(sFndChar)
+ { // placeholder for a backward reference?
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ {
+ int j = sFndChar - '0'; // index
+ if(j < rResult.subRegExpressions)
+ {
+ sal_Int32 nSttReg = rResult.startOffset[j];
+ sal_Int32 nRegLen = rResult.endOffset[j];
+ if (nSttReg < 0 || nRegLen < 0) // A "not found" optional capture
+ {
+ nSttReg = nRegLen = 0; // Copy empty string
+ }
+ else if (nRegLen >= nSttReg)
+ {
+ nRegLen = nRegLen - nSttReg;
+ }
+ else
+ {
+ nRegLen = nSttReg - nRegLen;
+ nSttReg = rResult.endOffset[j];
+ }
+ // Copy reference from found string
+ sBuff.append(rStr.substr(nSttReg, nRegLen));
+ }
+ i += 1;
+ }
+ break;
+ default:
+ sBuff.append(OUStringChar(rReplaceStr[i]) + OUStringChar(rReplaceStr[i+1]));
+ i += 1;
+ break;
+ }
+ }
+ else if((i < rReplaceStr.getLength() - 1) && rReplaceStr[i] == '\\')
+ {
+ sFndChar = rReplaceStr[ i+1 ];
+ switch(sFndChar)
+ {
+ case '\\':
+ case '&':
+ case '$':
+ sBuff.append(sFndChar);
+ i+=1;
+ break;
+ case 't':
+ sBuff.append('\t');
+ i += 1;
+ break;
+ default:
+ sBuff.append(OUStringChar(rReplaceStr[i]) + OUStringChar(rReplaceStr[i+1]));
+ i += 1;
+ break;
+ }
+ }
+ else
+ {
+ sBuff.append(rReplaceStr[i]);
+ }
+ }
+ rReplaceStr = sBuff.makeStringAndClear();
+}
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/i18n/transliterationwrapper.cxx b/unotools/source/i18n/transliterationwrapper.cxx
new file mode 100644
index 0000000000..a8e4baedf9
--- /dev/null
+++ b/unotools/source/i18n/transliterationwrapper.cxx
@@ -0,0 +1,231 @@
+/* -*- 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 <unotools/transliterationwrapper.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <i18nutil/transliteration.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <com/sun/star/i18n/Transliteration.hpp>
+
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::uno;
+using namespace ::utl;
+
+TransliterationWrapper::TransliterationWrapper(
+ const Reference< XComponentContext > & rxContext,
+ TransliterationFlags nTyp )
+ : xTrans( Transliteration::create(rxContext) ),
+ aLanguageTag( LANGUAGE_SYSTEM ), nType( nTyp ), bFirstCall( true )
+{
+}
+
+TransliterationWrapper::~TransliterationWrapper()
+{
+}
+
+OUString TransliterationWrapper::transliterate(const OUString& rStr, LanguageType nLang,
+ sal_Int32 nStart, sal_Int32 nLen,
+ Sequence <sal_Int32>* pOffset )
+{
+ OUString sRet;
+ if( xTrans.is() )
+ {
+ try
+ {
+ loadModuleIfNeeded( nLang );
+
+ if ( pOffset )
+ sRet = xTrans->transliterate( rStr, nStart, nLen, *pOffset );
+ else
+ sRet = xTrans->transliterateString2String( rStr, nStart, nLen);
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ }
+ return sRet;
+}
+
+OUString TransliterationWrapper::transliterate( const OUString& rStr,
+ sal_Int32 nStart, sal_Int32 nLen ) const
+{
+ OUString sRet( rStr );
+ if( xTrans.is() )
+ {
+ try
+ {
+ sRet = xTrans->transliterateString2String( rStr, nStart, nLen);
+ }
+ catch( Exception& )
+ {
+ TOOLS_WARN_EXCEPTION("unotools.i18n", "" );
+ }
+ }
+ return sRet;
+}
+
+bool TransliterationWrapper::needLanguageForTheMode() const
+{
+ return TransliterationFlags::UPPERCASE_LOWERCASE == nType ||
+ TransliterationFlags::LOWERCASE_UPPERCASE == nType ||
+ TransliterationFlags::IGNORE_CASE == nType ||
+ TransliterationFlags::SENTENCE_CASE == nType ||
+ TransliterationFlags::TITLE_CASE == nType ||
+ TransliterationFlags::TOGGLE_CASE == nType;
+}
+
+void TransliterationWrapper::setLanguageLocaleImpl( LanguageType nLang )
+{
+ if( LANGUAGE_NONE == nLang )
+ nLang = LANGUAGE_SYSTEM;
+ aLanguageTag.reset( nLang);
+}
+
+void TransliterationWrapper::loadModuleIfNeeded( LanguageType nLang )
+{
+ bool bLoad = bFirstCall;
+ bFirstCall = false;
+
+ if( nType == TransliterationFlags::SENTENCE_CASE )
+ {
+ if( bLoad )
+ loadModuleByImplName("SENTENCE_CASE", nLang);
+ }
+ else if( nType == TransliterationFlags::TITLE_CASE )
+ {
+ if( bLoad )
+ loadModuleByImplName("TITLE_CASE", nLang);
+ }
+ else if( nType == TransliterationFlags::TOGGLE_CASE )
+ {
+ if( bLoad )
+ loadModuleByImplName("TOGGLE_CASE", nLang);
+ }
+ else
+ {
+ if( aLanguageTag.getLanguageType() != nLang )
+ {
+ setLanguageLocaleImpl( nLang );
+ if( !bLoad )
+ bLoad = needLanguageForTheMode();
+ }
+ if( bLoad )
+ loadModuleImpl();
+ }
+}
+
+void TransliterationWrapper::loadModuleImpl() const
+{
+ if ( bFirstCall )
+ const_cast<TransliterationWrapper*>(this)->setLanguageLocaleImpl( LANGUAGE_SYSTEM );
+
+ try
+ {
+ if ( xTrans.is() )
+ xTrans->loadModule( static_cast<TransliterationModules>(nType), aLanguageTag.getLocale() );
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "loadModuleImpl" );
+ }
+
+ bFirstCall = false;
+}
+
+void TransliterationWrapper::loadModuleByImplName(const OUString& rModuleName,
+ LanguageType nLang )
+{
+ try
+ {
+ setLanguageLocaleImpl( nLang );
+ css::lang::Locale aLocale( aLanguageTag.getLocale());
+ // Reset LanguageTag, so the next call to loadModuleIfNeeded() forces
+ // new settings.
+ aLanguageTag.reset( LANGUAGE_DONTKNOW);
+ if ( xTrans.is() )
+ xTrans->loadModuleByImplName( rModuleName, aLocale );
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "loadModuleByImplName" );
+ }
+
+ bFirstCall = false;
+}
+
+bool TransliterationWrapper::equals(
+ const OUString& rStr1, sal_Int32 nPos1, sal_Int32 nCount1, sal_Int32& nMatch1,
+ const OUString& rStr2, sal_Int32 nPos2, sal_Int32 nCount2, sal_Int32& nMatch2 ) const
+{
+ try
+ {
+ if( bFirstCall )
+ loadModuleImpl();
+ if ( xTrans.is() )
+ return xTrans->equals( rStr1, nPos1, nCount1, nMatch1, rStr2, nPos2, nCount2, nMatch2 );
+ }
+ catch ( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "equals" );
+ }
+ return false;
+}
+
+sal_Int32 TransliterationWrapper::compareString( const OUString& rStr1, const OUString& rStr2 ) const
+{
+ try
+ {
+ if( bFirstCall )
+ loadModuleImpl();
+ if ( xTrans.is() )
+ return xTrans->compareString( rStr1, rStr2 );
+ }
+ catch (const Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "unotools.i18n", "compareString" );
+ }
+ return 0;
+}
+
+// --- helpers --------------------------------------------------------
+
+bool TransliterationWrapper::isEqual( const OUString& rStr1, const OUString& rStr2 ) const
+{
+ sal_Int32 nMatch1(0), nMatch2(0);
+ bool bMatch = equals(
+ rStr1, 0, rStr1.getLength(), nMatch1,
+ rStr2, 0, rStr2.getLength(), nMatch2 );
+ return bMatch;
+}
+
+bool TransliterationWrapper::isMatch( const OUString& rStr1, const OUString& rStr2 ) const
+{
+ sal_Int32 nMatch1(0), nMatch2(0);
+ equals(
+ rStr1, 0, rStr1.getLength(), nMatch1,
+ rStr2, 0, rStr2.getLength(), nMatch2 );
+ return (nMatch1 <= nMatch2) && (nMatch1 == rStr1.getLength());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/misc/ServiceDocumenter.cxx b/unotools/source/misc/ServiceDocumenter.cxx
new file mode 100644
index 0000000000..0bf1ce9fa5
--- /dev/null
+++ b/unotools/source/misc/ServiceDocumenter.cxx
@@ -0,0 +1,89 @@
+/* -*- 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 "ServiceDocumenter.hxx"
+#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
+#include <com/sun/star/system/XSystemShellExecute.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <cppuhelper/supportsservice.hxx>
+
+using namespace com::sun::star;
+using uno::Reference;
+using lang::XServiceInfo;
+using lang::XTypeProvider;
+
+void unotools::misc::ServiceDocumenter::showCoreDocs(const Reference<XServiceInfo>& xService)
+{
+ if(!xService.is())
+ return;
+ auto xMSF(m_xContext->getServiceManager());
+ Reference<system::XSystemShellExecute> xShell(xMSF->createInstanceWithContext("com.sun.star.system.SystemShellExecute", m_xContext), uno::UNO_QUERY);
+ xShell->execute(
+ m_sCoreBaseUrl + xService->getImplementationName() + ".html", "",
+ css::system::SystemShellExecuteFlags::URIS_ONLY);
+}
+
+void unotools::misc::ServiceDocumenter::showInterfaceDocs(const Reference<XTypeProvider>& xTypeProvider)
+{
+ if(!xTypeProvider.is())
+ return;
+ auto xMSF(m_xContext->getServiceManager());
+ Reference<system::XSystemShellExecute> xShell(xMSF->createInstanceWithContext("com.sun.star.system.SystemShellExecute", m_xContext), uno::UNO_QUERY);
+ const css::uno::Sequence<css::uno::Type> aTypes = xTypeProvider->getTypes();
+ for(const auto& aType : aTypes)
+ {
+ auto sUrl = aType.getTypeName();
+ sal_Int32 nIdx = 0;
+ while(nIdx != -1)
+ sUrl = sUrl.replaceFirst(".", "_1_1", &nIdx);
+ xShell->execute(
+ m_sServiceBaseUrl + "/interface" + sUrl + ".html", "",
+ css::system::SystemShellExecuteFlags::URIS_ONLY);
+ }
+}
+
+void unotools::misc::ServiceDocumenter::showServiceDocs(const Reference<XServiceInfo>& xService)
+{
+ if(!xService.is())
+ return;
+ auto xMSF(m_xContext->getServiceManager());
+ Reference<system::XSystemShellExecute> xShell(xMSF->createInstanceWithContext("com.sun.star.system.SystemShellExecute", m_xContext), uno::UNO_QUERY);
+ const css::uno::Sequence<OUString> aServiceNames = xService->getSupportedServiceNames();
+ for(const auto& sService : aServiceNames)
+ {
+ auto sUrl = sService;
+ sal_Int32 nIdx = 0;
+ while(nIdx != -1)
+ sUrl = sUrl.replaceFirst(".", "_1_1", &nIdx);
+ xShell->execute(
+ m_sServiceBaseUrl + "/service" + sUrl + ".html", "",
+ css::system::SystemShellExecuteFlags::URIS_ONLY);
+ }
+}
+
+// XServiceInfo
+sal_Bool unotools::misc::ServiceDocumenter::supportsService(const OUString& sServiceName)
+{
+ return cppu::supportsService(this, sServiceName);
+}
+OUString unotools::misc::ServiceDocumenter::getImplementationName()
+{
+ return "com.sun.star.comp.unotools.misc.ServiceDocumenter";
+}
+css::uno::Sequence< OUString > unotools::misc::ServiceDocumenter::getSupportedServiceNames()
+{
+ return { "com.sun.star.script.ServiceDocumenter" };
+}
+
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+unotools_ServiceDocument_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new unotools::misc::ServiceDocumenter(context));
+}
diff --git a/unotools/source/misc/ServiceDocumenter.hxx b/unotools/source/misc/ServiceDocumenter.hxx
new file mode 100644
index 0000000000..0c94b5ac7e
--- /dev/null
+++ b/unotools/source/misc/ServiceDocumenter.hxx
@@ -0,0 +1,57 @@
+/* -*- 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/.
+ */
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/script/XServiceDocumenter.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <utility>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+namespace unotools::misc {
+
+class ServiceDocumenter : public ::cppu::WeakImplHelper<
+ css::script::XServiceDocumenter, css::lang::XServiceInfo>
+{
+ public:
+ ServiceDocumenter(css::uno::Reference< css::uno::XComponentContext> xContext)
+ : m_xContext(std::move(xContext))
+ , m_sCoreBaseUrl("http://example.com")
+ , m_sServiceBaseUrl("https://api.libreoffice.org/docs/idl/ref")
+ {};
+
+ // XServiceInfo
+ virtual sal_Bool SAL_CALL supportsService(const OUString& sServiceName) override;
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XServiceDocumenter
+ virtual OUString SAL_CALL getCoreBaseUrl() override
+ { return m_sCoreBaseUrl; };
+ virtual void SAL_CALL setCoreBaseUrl( const OUString& sCoreBaseUrl ) override
+ { m_sCoreBaseUrl = sCoreBaseUrl; };
+ virtual OUString SAL_CALL getServiceBaseUrl() override
+ { return m_sServiceBaseUrl; };
+ virtual void SAL_CALL setServiceBaseUrl( const OUString& sServiceBaseUrl ) override
+ { m_sServiceBaseUrl = sServiceBaseUrl; };
+ virtual void SAL_CALL showServiceDocs( const ::css::uno::Reference< ::css::lang::XServiceInfo >& xService) override;
+ virtual void SAL_CALL showInterfaceDocs( const ::css::uno::Reference< ::css::lang::XTypeProvider >& xTypeProvider ) override;
+ virtual void SAL_CALL showCoreDocs( const ::css::uno::Reference< ::css::lang::XServiceInfo >& xService) override;
+ protected:
+ virtual ~ServiceDocumenter() override
+ {};
+ private:
+ css::uno::Reference< css::uno::XComponentContext> m_xContext;
+ OUString m_sCoreBaseUrl;
+ OUString m_sServiceBaseUrl;
+};
+
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/misc/ZipPackageHelper.cxx b/unotools/source/misc/ZipPackageHelper.cxx
new file mode 100644
index 0000000000..8bbd751318
--- /dev/null
+++ b/unotools/source/misc/ZipPackageHelper.cxx
@@ -0,0 +1,173 @@
+/* -*- 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/io/XActiveDataSink.hpp>
+#include <com/sun/star/beans/NamedValue.hpp>
+#include <com/sun/star/container/XNamed.hpp>
+#include <com/sun/star/container/XChild.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+
+#include <unotools/ZipPackageHelper.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <osl/file.hxx>
+#include <unotools/streamwrap.hxx>
+#include <tools/stream.hxx>
+#include <tools/urlobj.hxx>
+
+#include <rtl/uri.hxx>
+
+namespace com::sun::star::io { class XInputStream; }
+
+using namespace utl;
+using namespace osl;
+using namespace comphelper;
+using namespace com::sun::star;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::util;
+using namespace com::sun::star::container;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::io;
+
+using ::rtl::Uri;
+
+ZipPackageHelper::ZipPackageHelper(
+ const Reference< XComponentContext >& rxContext,
+ const OUString& sPackageURL)
+: mxContext( rxContext )
+{
+ // create the package zip file
+ Sequence< Any > aArguments{
+ Any(sPackageURL),
+ // let ZipPackage be used
+ Any(beans::NamedValue("StorageFormat", Any(ZIP_STORAGE_FORMAT_STRING)))
+ };
+
+ Reference< XHierarchicalNameAccess > xHNameAccess(
+ mxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.packages.comp.ZipPackage",
+ aArguments, mxContext ), UNO_QUERY);
+ mxHNameAccess = xHNameAccess;
+
+ if( !mxHNameAccess.is() )
+ return;
+
+ Reference<XSingleServiceFactory> xFactory(mxHNameAccess, UNO_QUERY);
+ mxFactory = xFactory;
+
+ // get root zip folder
+ mxHNameAccess->getByHierarchicalName( "/" ) >>= mxRootFolder;
+}
+
+static OUString encodeZipUri( const OUString& rURI )
+{
+ return Uri::encode( rURI, rtl_UriCharClassUric, rtl_UriEncodeCheckEscapes, RTL_TEXTENCODING_UTF8 );
+}
+
+Reference< XInterface >& ZipPackageHelper::getRootFolder()
+{
+ return mxRootFolder;
+}
+
+Reference< XInterface > ZipPackageHelper::addFolder( Reference< XInterface > const & xRootFolder,
+ const OUString& rName )
+{
+ if ( rName == ".." || rName == "." )
+ throw lang::IllegalArgumentException();
+
+ Reference< XInterface > xFolder( mxFactory->createInstanceWithArguments({ Any(true) } ));
+ Reference< XNamed > xNamed( xFolder, UNO_QUERY );
+ Reference< XChild > xChild( xFolder, UNO_QUERY );
+
+ if( xNamed.is() && xChild.is() )
+ {
+ OUString aName( encodeZipUri( rName ) );
+ xNamed->setName( aName );
+ xChild->setParent( xRootFolder );
+ }
+
+ return xFolder;
+}
+
+void ZipPackageHelper::addFolderWithContent( Reference< XInterface > const & xRootFolder, const OUString& rDirURL )
+{
+ if (rDirURL.isEmpty())
+ return;
+
+ osl::Directory aDirectory(rDirURL);
+
+ if (aDirectory.open() != osl::FileBase::E_None)
+ return;
+
+ osl::DirectoryItem aDirectoryItem;
+
+ while (osl::FileBase::E_None == aDirectory.getNextItem(aDirectoryItem))
+ {
+ osl::FileStatus aFileStatus(osl_FileStatus_Mask_Type | osl_FileStatus_Mask_FileURL | osl_FileStatus_Mask_FileName);
+
+ if (osl::FileBase::E_None == aDirectoryItem.getFileStatus(aFileStatus))
+ {
+ if (aFileStatus.isDirectory())
+ {
+ const OUString aFileName(aFileStatus.getFileName());
+
+ if (!aFileName.isEmpty())
+ {
+ Reference<XInterface> folder(addFolder(xRootFolder, aFileName));
+ addFolderWithContent(folder, aFileStatus.getFileURL());
+ }
+ }
+ else if (aFileStatus.isRegular())
+ {
+ addFile(xRootFolder, aFileStatus.getFileURL());
+ }
+ }
+ }
+}
+
+void ZipPackageHelper::addFile( css::uno::Reference< css::uno::XInterface > const & xRootFolder,
+ const OUString& rSourceFileURL )
+{
+ INetURLObject aURL( rSourceFileURL );
+ OUString aName( aURL.getName() );
+
+ SvFileStream* pStream = new SvFileStream(rSourceFileURL, StreamMode::READ );
+ Reference< XInputStream > xInput( new utl::OSeekableInputStreamWrapper( pStream, true ) );
+ Reference< XActiveDataSink > xSink( mxFactory->createInstance(), UNO_QUERY );
+ assert(xSink); // this should never fail
+ if( !xSink.is() )
+ return;
+
+ Reference< XNameContainer > xNameContainer(xRootFolder, UNO_QUERY );
+ xNameContainer->insertByName(encodeZipUri( aName ), Any(xSink));
+ xSink->setInputStream( xInput );
+}
+
+void ZipPackageHelper::savePackage()
+{
+ Reference< XChangesBatch > xBatch( mxHNameAccess, UNO_QUERY );
+ if( xBatch.is() )
+ xBatch->commitChanges();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/misc/closeveto.cxx b/unotools/source/misc/closeveto.cxx
new file mode 100644
index 0000000000..cb5ee7face
--- /dev/null
+++ b/unotools/source/misc/closeveto.cxx
@@ -0,0 +1,147 @@
+/* -*- 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/closeveto.hxx>
+
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+namespace utl
+{
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::util::XCloseable;
+ using ::com::sun::star::util::XCloseListener;
+ using ::com::sun::star::util::CloseVetoException;
+ using ::com::sun::star::lang::EventObject;
+
+ //= CloseListener_Impl
+
+ typedef ::cppu::WeakImplHelper < XCloseListener
+ > CloseListener_Base;
+
+ namespace {
+
+ class CloseListener_Impl : public CloseListener_Base
+ {
+ public:
+ explicit CloseListener_Impl(bool const bHasOwnership)
+ : m_bHasOwnership(bHasOwnership)
+ {
+ }
+
+ // XCloseListener
+ virtual void SAL_CALL queryClosing( const EventObject& Source, sal_Bool GetsOwnership ) override;
+ virtual void SAL_CALL notifyClosing( const EventObject& Source ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const EventObject& Source) override;
+
+ bool hasOwnership() const { return m_bHasOwnership; }
+
+ protected:
+ virtual ~CloseListener_Impl() override
+ {
+ }
+
+ private:
+ bool m_bHasOwnership;
+ };
+
+ }
+
+ void SAL_CALL CloseListener_Impl::queryClosing( const EventObject&, sal_Bool i_deliverOwnership )
+ {
+ if ( !m_bHasOwnership )
+ m_bHasOwnership = i_deliverOwnership;
+
+ throw CloseVetoException();
+ }
+
+ void SAL_CALL CloseListener_Impl::notifyClosing( const EventObject& ) {}
+
+ void SAL_CALL CloseListener_Impl::disposing( const EventObject& ) {}
+
+ //= CloseVeto_Data
+
+ struct CloseVeto_Data
+ {
+ Reference< XCloseable > xCloseable;
+ ::rtl::Reference< CloseListener_Impl > pListener;
+ };
+
+ //= operations
+
+ namespace
+ {
+
+ void lcl_init( CloseVeto_Data& i_data, const Reference< XInterface >& i_closeable,
+ bool const hasOwnership)
+ {
+ i_data.xCloseable.set( i_closeable, UNO_QUERY );
+ ENSURE_OR_RETURN_VOID( i_data.xCloseable.is(), "CloseVeto: the component is not closeable!" );
+
+ i_data.pListener = new CloseListener_Impl(hasOwnership);
+ i_data.xCloseable->addCloseListener( i_data.pListener );
+ }
+
+ void lcl_deinit( CloseVeto_Data const & i_data )
+ {
+ if ( !i_data.xCloseable.is() )
+ return;
+
+ i_data.xCloseable->removeCloseListener( i_data.pListener );
+ if ( i_data.pListener->hasOwnership() )
+ {
+ try
+ {
+ i_data.xCloseable->close( true );
+ }
+ catch( const CloseVetoException& ) { }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("unotools");
+ }
+ }
+ }
+ }
+
+ //= CloseVeto
+ CloseVeto::CloseVeto(const Reference< XInterface >& i_closeable,
+ bool const hasOwnership)
+ : m_xData(new CloseVeto_Data)
+ {
+ lcl_init(*m_xData, i_closeable, hasOwnership);
+ }
+
+ CloseVeto::~CloseVeto()
+ {
+ lcl_deinit(*m_xData);
+ }
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/misc/datetime.cxx b/unotools/source/misc/datetime.cxx
new file mode 100644
index 0000000000..e2cc1f6d11
--- /dev/null
+++ b/unotools/source/misc/datetime.cxx
@@ -0,0 +1,536 @@
+/* -*- 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/datetime.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/syslocale.hxx>
+#include <tools/date.hxx>
+#include <tools/time.hxx>
+#include <tools/datetime.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/math.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/string.hxx>
+#include <o3tl/string_view.hxx>
+#include <cstddef>
+#include <sstream>
+
+namespace
+{
+ bool checkAllNumber(std::u16string_view rString)
+ {
+ sal_Int32 nPos = 0;
+ sal_Int32 nLen = rString.size();
+
+ // skip white space
+ while( nPos < nLen && ' ' == rString[nPos] )
+ nPos++;
+
+ if( nPos < nLen && '-' == rString[nPos] )
+ nPos++;
+
+ // get number
+ while( nPos < nLen &&
+ '0' <= rString[nPos] &&
+ '9' >= rString[nPos] )
+ {
+ nPos++;
+ }
+
+ return nPos == nLen;
+ }
+
+ /** convert string to number with optional min and max values */
+ bool convertNumber32(sal_Int32& rValue,
+ std::u16string_view rString,
+ sal_Int32 /*nMin*/ = -1, sal_Int32 /*nMax*/ = -1)
+ {
+ if (!checkAllNumber(rString))
+ {
+ rValue = 0;
+ return false;
+ }
+
+ rValue = o3tl::toInt32(rString);
+ return true;
+ }
+
+ bool convertNumber64(sal_Int64& rValue,
+ std::u16string_view rString,
+ sal_Int64 /*nMin*/ = -1, sal_Int64 /*nMax*/ = -1)
+ {
+ if (!checkAllNumber(rString))
+ {
+ rValue = 0;
+ return false;
+ }
+
+ rValue = o3tl::toInt64(rString);
+ return true;
+ }
+
+ // although the standard calls for fixed-length (zero-padded) tokens
+ // (in their integer part), we are here liberal and allow shorter tokens
+ // (when there are separators, else it is ambiguous).
+ // Note that:
+ // the token separator is OPTIONAL
+ // empty string is a valid token! (to recognise hh or hhmm or hh:mm formats)
+ // returns: success / failure
+ // in case of failure, no reference argument is changed
+ // arguments:
+ // i_str: string to extract token from
+ // index: index in i_str where to start tokenizing
+ // after return, start of *next* token (if any)
+ // if this was the last token, then the value is UNDEFINED
+ // o_strInt: output; integer part of token
+ // o_bFraction: output; was there a fractional part?
+ // o_strFrac: output; fractional part of token
+ bool impl_getISO8601TimeToken(std::u16string_view i_str, std::size_t &nPos, OUString &resInt, bool &bFraction, OUString &resFrac)
+ {
+ bFraction = false;
+ // all tokens are of length 2
+ const std::size_t nEndPos = nPos + 2;
+ const sal_Unicode c0 = '0';
+ const sal_Unicode c9 = '9';
+ const sal_Unicode sep = ':';
+ for (;nPos < nEndPos && nPos < i_str.size(); ++nPos)
+ {
+ const sal_Unicode c = i_str[nPos];
+ if (c == sep)
+ return true;
+ if (c < c0 || c > c9)
+ return false;
+ resInt += OUStringChar(c);
+ }
+ if (nPos == 0)
+ return false;
+ if (nPos == i_str.size() || i_str[nPos] == sep)
+ return true;
+ if (i_str[nPos] == ',' || i_str[nPos] == '.')
+ {
+ bFraction = true;
+ ++nPos;
+ for (; nPos < i_str.size(); ++nPos)
+ {
+ const sal_Unicode c = i_str[nPos];
+ if (c == 'Z' || c == '+' || c == '-')
+ {
+ --nPos; // we don't want to skip the tz separator
+ return true;
+ }
+ if (c == sep)
+ // fractional part allowed only in *last* token
+ return false;
+ if (c < c0 || c > c9)
+ return false;
+ resFrac += OUStringChar(c);
+ }
+ OSL_ENSURE(nPos == i_str.size(), "impl_getISO8601TimeToken internal error; expected to be at end of string");
+ return true;
+ }
+ if (i_str[nPos] == 'Z' || i_str[nPos] == '+' || i_str[nPos] == '-')
+ {
+ --nPos; // we don't want to skip the tz separator
+ return true;
+ }
+ else
+ return false;
+ }
+ bool getISO8601TimeToken(std::u16string_view i_str, std::size_t &io_index, OUString &o_strInt, bool &o_bFraction, OUString &o_strFrac)
+ {
+ OUString resInt;
+ OUString resFrac;
+ bool bFraction = false;
+ std::size_t index = io_index;
+ if(!impl_getISO8601TimeToken(i_str, index, resInt, bFraction, resFrac))
+ return false;
+ else
+ {
+ io_index = index+1;
+ o_strInt = resInt;
+ o_strFrac = resFrac;
+ o_bFraction = bFraction;
+ return true;
+ }
+ }
+ bool getISO8601TimeZoneToken(std::u16string_view i_str, std::size_t &io_index, OUString &o_strInt)
+ {
+ const sal_Unicode c0 = '0';
+ const sal_Unicode c9 = '9';
+ const sal_Unicode sep = ':';
+ if (i_str[io_index] == 'Z') // UTC timezone indicator
+ {
+ ++io_index;
+ o_strInt = "Z";
+ return true;
+ }
+ else if (i_str[io_index] == '+' || i_str[io_index] == '-') // other timezones indicator
+ {
+ ++io_index;
+ o_strInt.clear();
+ for (; io_index < i_str.size(); ++io_index)
+ {
+ const sal_Unicode c = i_str[io_index];
+ if ((c < c0 || c > c9) && c != sep)
+ return false;
+ o_strInt += OUStringChar(c);
+ }
+ return true;
+ }
+ else
+ return false;
+ }
+}
+
+namespace utl
+{
+const LocaleDataWrapper& GetLocaleData()
+{
+ static SvtSysLocale ourSysLocale;
+ return ourSysLocale.GetLocaleData();
+}
+
+DateTime GetDateTime(const css::util::DateTime& _rDT) { return DateTime(_rDT); }
+
+OUString GetDateTimeString(const css::util::DateTime& _rDT)
+{
+ // String with date and time information (#i20172#)
+ DateTime aDT(GetDateTime(_rDT));
+ const LocaleDataWrapper& rLoDa = GetLocaleData();
+
+ return rLoDa.getDate(aDT) + " " + rLoDa.getTime(aDT);
+}
+
+OUString GetDateTimeString(sal_Int32 _nDate, sal_Int32 _nTime)
+{
+ const LocaleDataWrapper& rLoDa = GetLocaleData();
+
+ Date aDate(_nDate);
+ tools::Time aTime(_nTime * tools::Time::nanoPerCenti);
+ return rLoDa.getDate(aDate) + ", " + rLoDa.getTime(aTime);
+}
+
+OUString GetDateString(const css::util::DateTime& _rDT)
+{
+ return GetLocaleData().getDate(GetDateTime(_rDT));
+}
+
+void typeConvert(const Date& _rDate, css::util::Date& _rOut)
+{
+ _rOut.Day = _rDate.GetDay();
+ _rOut.Month = _rDate.GetMonth();
+ _rOut.Year = _rDate.GetYear();
+}
+
+void typeConvert(const css::util::Date& _rDate, Date& _rOut)
+{
+ _rOut = Date(_rDate.Day, _rDate.Month, _rDate.Year);
+}
+
+void typeConvert(const DateTime& _rDateTime, css::util::DateTime& _rOut)
+{
+ _rOut.Year = _rDateTime.GetYear();
+ _rOut.Month = _rDateTime.GetMonth();
+ _rOut.Day = _rDateTime.GetDay();
+ _rOut.Hours = _rDateTime.GetHour();
+ _rOut.Minutes = _rDateTime.GetMin();
+ _rOut.Seconds = _rDateTime.GetSec();
+ _rOut.NanoSeconds = _rDateTime.GetNanoSec();
+}
+
+void typeConvert(const css::util::DateTime& _rDateTime, DateTime& _rOut)
+{
+ Date aDate(_rDateTime.Day, _rDateTime.Month, _rDateTime.Year);
+ tools::Time aTime(_rDateTime.Hours, _rDateTime.Minutes, _rDateTime.Seconds, _rDateTime.NanoSeconds);
+ _rOut = DateTime(aDate, aTime);
+}
+
+OUString toISO8601(const css::util::DateTime& rDateTime)
+{
+ OUStringBuffer rBuffer(32);
+ rBuffer.append(OUString::number(static_cast<sal_Int32>(rDateTime.Year)) + "-");
+ if( rDateTime.Month < 10 )
+ rBuffer.append('0');
+ rBuffer.append(OUString::number(static_cast<sal_Int32>(rDateTime.Month)) + "-");
+ if( rDateTime.Day < 10 )
+ rBuffer.append('0');
+ rBuffer.append(static_cast<sal_Int32>(rDateTime.Day));
+
+ if( rDateTime.NanoSeconds != 0 ||
+ rDateTime.Seconds != 0 ||
+ rDateTime.Minutes != 0 ||
+ rDateTime.Hours != 0 )
+ {
+ rBuffer.append('T');
+ if( rDateTime.Hours < 10 )
+ rBuffer.append('0');
+ rBuffer.append(OUString::number(static_cast<sal_Int32>(rDateTime.Hours)) + ":");
+ if( rDateTime.Minutes < 10 )
+ rBuffer.append('0');
+ rBuffer.append(OUString::number(static_cast<sal_Int32>(rDateTime.Minutes)) + ":");
+ if( rDateTime.Seconds < 10 )
+ rBuffer.append('0');
+ rBuffer.append(static_cast<sal_Int32>(rDateTime.Seconds));
+ if ( rDateTime.NanoSeconds > 0)
+ {
+ OSL_ENSURE(rDateTime.NanoSeconds < 1000000000,"NanoSeconds cannot be more than 999 999 999");
+ rBuffer.append(',');
+ std::ostringstream ostr;
+ ostr.fill('0');
+ ostr.width(9);
+ ostr << rDateTime.NanoSeconds;
+ rBuffer.appendAscii(ostr.str().c_str());
+ }
+ }
+ return rBuffer.makeStringAndClear();
+}
+
+/** convert ISO8601 DateTime String to util::DateTime */
+bool ISO8601parseDateTime(std::u16string_view rString, css::util::DateTime& rDateTime)
+{
+ bool bSuccess = true;
+
+ std::u16string_view aDateStr, aTimeStr;
+ css::util::Date aDate;
+ css::util::Time aTime;
+ size_t nPos = rString.find( 'T' );
+ if ( nPos != std::u16string_view::npos )
+ {
+ aDateStr = rString.substr( 0, nPos );
+ aTimeStr = rString.substr( nPos + 1 );
+ }
+ else
+ aDateStr = rString; // no separator: only date part
+
+ bSuccess = ISO8601parseDate(aDateStr, aDate);
+
+ if ( bSuccess && !aTimeStr.empty() ) // time is optional
+ {
+ bSuccess = ISO8601parseTime(aTimeStr, aTime);
+ }
+
+ if (bSuccess)
+ {
+ rDateTime = css::util::DateTime(aTime.NanoSeconds, aTime.Seconds, aTime.Minutes, aTime.Hours,
+ aDate.Day, aDate.Month, aDate.Year, false);
+ }
+
+ return bSuccess;
+}
+
+/** convert ISO8601 Date String to util::Date */
+// TODO: supports only calendar dates YYYY-MM-DD
+// MISSING: calendar dates YYYYMMDD YYYY-MM
+// year, week date, ordinal date
+bool ISO8601parseDate(std::u16string_view aDateStr, css::util::Date& rDate)
+{
+ const sal_Int32 nDateTokens {comphelper::string::getTokenCount(aDateStr, '-')};
+
+ if (nDateTokens<1 || nDateTokens>3)
+ return false;
+
+ sal_Int32 nYear = 1899;
+ sal_Int32 nMonth = 12;
+ sal_Int32 nDay = 30;
+
+ sal_Int32 nIdx {0};
+ auto strCurrentToken = o3tl::getToken(aDateStr, 0, '-', nIdx );
+ if ( !convertNumber32( nYear, strCurrentToken, 0, 9999 ) )
+ return false;
+ if ( nDateTokens >= 2 )
+ {
+ strCurrentToken = o3tl::getToken(aDateStr, 0, '-', nIdx );
+ if (strCurrentToken.size() > 2)
+ return false;
+ if ( !convertNumber32( nMonth, strCurrentToken, 0, 12 ) )
+ return false;
+ }
+ if ( nDateTokens >= 3 )
+ {
+ strCurrentToken = o3tl::getToken(aDateStr, 0, '-', nIdx );
+ if (strCurrentToken.size() > 2)
+ return false;
+ if ( !convertNumber32( nDay, strCurrentToken, 0, 31 ) )
+ return false;
+ }
+
+ rDate.Year = static_cast<sal_uInt16>(nYear);
+ rDate.Month = static_cast<sal_uInt16>(nMonth);
+ rDate.Day = static_cast<sal_uInt16>(nDay);
+
+ return true;
+}
+
+/** convert ISO8601 Time String to util::Time */
+bool ISO8601parseTime(std::u16string_view aTimeStr, css::util::Time& rTime)
+{
+ sal_Int32 nHour = 0;
+ sal_Int32 nMin = 0;
+ sal_Int32 nSec = 0;
+ sal_Int32 nNanoSec = 0;
+
+ std::size_t n = 0;
+ OUString tokInt;
+ OUString tokFrac;
+ OUString tokTz;
+ bool bFrac = false;
+ // hours
+ bool bSuccess = getISO8601TimeToken(aTimeStr, n, tokInt, bFrac, tokFrac);
+ if (!bSuccess)
+ return false;
+
+ if ( bFrac && n < aTimeStr.size())
+ {
+ // is it junk or the timezone?
+ bSuccess = getISO8601TimeZoneToken(aTimeStr, n, tokTz);
+ if (!bSuccess)
+ return false;
+ }
+ bSuccess = convertNumber32( nHour, tokInt, 0, 23 );
+ if (!bSuccess)
+ return false;
+
+ if (bFrac)
+ {
+ sal_Int64 fracNumerator;
+ bSuccess = convertNumber64(fracNumerator, tokFrac);
+ if ( bSuccess )
+ {
+ double frac = static_cast<double>(fracNumerator) / pow(static_cast<double>(10), static_cast<double>(tokFrac.getLength()));
+ // minutes
+ OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac hours (of hours) not between 0 and 1");
+ frac *= 60;
+ nMin = floor(frac);
+ frac -= nMin;
+ // seconds
+ OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac minutes (of hours) not between 0 and 1");
+ frac *= 60;
+ nSec = floor(frac);
+ frac -= nSec;
+ // nanoseconds
+ OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac seconds (of hours) not between 0 and 1");
+ frac *= 1000000000;
+ nNanoSec = ::rtl::math::round(frac);
+ }
+ goto end;
+ }
+ if(n >= aTimeStr.size())
+ goto end;
+
+ // minutes
+ bSuccess = getISO8601TimeToken(aTimeStr, n, tokInt, bFrac, tokFrac);
+ if (!bSuccess)
+ return false;
+ if ( bFrac && n < aTimeStr.size())
+ {
+ // is it junk or the timezone?
+ bSuccess = getISO8601TimeZoneToken(aTimeStr, n, tokTz);
+ if (!bSuccess)
+ return false;
+ }
+ bSuccess = convertNumber32( nMin, tokInt, 0, 59 );
+ if (!bSuccess)
+ return false;
+ if (bFrac)
+ {
+ sal_Int64 fracNumerator;
+ bSuccess = convertNumber64(fracNumerator, tokFrac);
+ if ( bSuccess )
+ {
+ double frac = static_cast<double>(fracNumerator) / pow(static_cast<double>(10), static_cast<double>(tokFrac.getLength()));
+ // seconds
+ OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac minutes (of minutes) not between 0 and 1");
+ frac *= 60;
+ nSec = floor(frac);
+ frac -= nSec;
+ // nanoseconds
+ OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac seconds (of minutes) not between 0 and 1");
+ frac *= 1000000000;
+ nNanoSec = ::rtl::math::round(frac);
+ }
+ goto end;
+ }
+ if(n >= aTimeStr.size())
+ goto end;
+
+ // seconds
+ bSuccess = getISO8601TimeToken(aTimeStr, n, tokInt, bFrac, tokFrac);
+ if (!bSuccess)
+ return false;
+ if (n < aTimeStr.size())
+ {
+ // is it junk or the timezone?
+ bSuccess = getISO8601TimeZoneToken(aTimeStr, n, tokTz);
+ if (!bSuccess)
+ return false;
+ }
+ // max 60 for leap seconds
+ bSuccess = convertNumber32( nSec, tokInt, 0, 60 );
+ if (!bSuccess)
+ return false;
+ if (bFrac)
+ {
+ sal_Int64 fracNumerator;
+ bSuccess = convertNumber64(fracNumerator, tokFrac);
+ if ( bSuccess )
+ {
+ double frac = static_cast<double>(fracNumerator) / pow(static_cast<double>(10), static_cast<double>(tokFrac.getLength()));
+ // nanoseconds
+ OSL_ENSURE(frac < 1 && frac >= 0, "ISO8601parse internal error frac seconds (of seconds) not between 0 and 1");
+ frac *= 1000000000;
+ nNanoSec = ::rtl::math::round(frac);
+ }
+ goto end;
+ }
+
+ end:
+ if (bSuccess)
+ {
+ // normalise time
+ const int secondsOverflow = (nSec == 60) ? 61 : 60;
+ if (nNanoSec == 1000000000)
+ {
+ nNanoSec = 0;
+ ++nSec;
+ }
+ if(nSec == secondsOverflow)
+ {
+ nSec = 0;
+ ++nMin;
+ }
+ if(nMin == 60)
+ {
+ nMin = 0;
+ ++nHour;
+ }
+ if(!tokTz.isEmpty())
+ rTime.IsUTC = (tokTz == "Z");
+
+ rTime.Hours = static_cast<sal_uInt16>(nHour);
+ rTime.Minutes = static_cast<sal_uInt16>(nMin);
+ rTime.Seconds = static_cast<sal_uInt16>(nSec);
+ rTime.NanoSeconds = nNanoSec;
+ }
+
+ return bSuccess;
+}
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/misc/defaultencoding.cxx b/unotools/source/misc/defaultencoding.cxx
new file mode 100644
index 0000000000..552f3b8c40
--- /dev/null
+++ b/unotools/source/misc/defaultencoding.cxx
@@ -0,0 +1,33 @@
+/* -*- 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 <unotools/configmgr.hxx>
+#include <unotools/defaultencoding.hxx>
+#include <officecfg/Office/Linguistic.hxx>
+#include <officecfg/Setup.hxx>
+#include <officecfg/System.hxx>
+
+OUString utl_getLocaleForGlobalDefaultEncoding()
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return "en-US";
+ // First try document default language
+ OUString result(officecfg::Office::Linguistic::General::DefaultLocale::get());
+ // Fallback to LO locale
+ if (result.isEmpty())
+ result = officecfg::Setup::L10N::ooSetupSystemLocale::get();
+ // Fallback to system locale
+ if (result.isEmpty())
+ result = officecfg::System::L10N::Locale::get();
+ return result;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/misc/desktopterminationobserver.cxx b/unotools/source/misc/desktopterminationobserver.cxx
new file mode 100644
index 0000000000..2eeb44a92d
--- /dev/null
+++ b/unotools/source/misc/desktopterminationobserver.cxx
@@ -0,0 +1,188 @@
+/* -*- 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/desktopterminationobserver.hxx>
+
+#include <com/sun/star/frame/TerminationVetoException.hpp>
+#include <com/sun/star/frame/XTerminateListener.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/processfactory.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+
+#include <vector>
+
+namespace utl
+{
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::frame;
+
+ namespace
+ {
+
+ typedef ::std::vector< ITerminationListener* > Listeners;
+
+ struct ListenerAdminData
+ {
+ Listeners aListeners;
+ bool bAlreadyTerminated;
+ bool bCreatedAdapter;
+
+ ListenerAdminData() : bAlreadyTerminated( false ), bCreatedAdapter( false ) { }
+ };
+
+ ListenerAdminData& getListenerAdminData()
+ {
+ static ListenerAdminData s_aData;
+ return s_aData;
+ }
+
+ //= OObserverImpl
+
+ class OObserverImpl : public ::cppu::WeakImplHelper< XTerminateListener >
+ {
+ public:
+ static void ensureObservation();
+
+ private:
+ OObserverImpl();
+ virtual ~OObserverImpl() override;
+
+ // XTerminateListener
+ virtual void SAL_CALL queryTermination( const EventObject& Event ) override;
+ virtual void SAL_CALL notifyTermination( const EventObject& Event ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+ };
+
+ OObserverImpl::OObserverImpl()
+ {
+ }
+
+ OObserverImpl::~OObserverImpl()
+ {
+ }
+
+ void OObserverImpl::ensureObservation()
+ {
+ {
+ if ( getListenerAdminData().bCreatedAdapter )
+ return;
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ if ( getListenerAdminData().bCreatedAdapter )
+ return;
+
+ getListenerAdminData().bCreatedAdapter = true;
+ }
+
+ try
+ {
+ Reference< XDesktop2 > xDesktop = Desktop::create( ::comphelper::getProcessComponentContext() );
+ xDesktop->addTerminateListener( new OObserverImpl );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools", "OObserverImpl::ensureObservation" );
+ }
+ }
+
+ void SAL_CALL OObserverImpl::queryTermination( const EventObject& /*Event*/ )
+ {
+ Listeners aToNotify;
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ aToNotify = getListenerAdminData().aListeners;
+ }
+
+ for (auto const& listener : aToNotify)
+ {
+ if ( !listener->queryTermination() )
+ throw TerminationVetoException();
+ }
+ }
+
+ void SAL_CALL OObserverImpl::notifyTermination( const EventObject& /*Event*/ )
+ {
+ // get the listeners
+ Listeners aToNotify;
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ OSL_ENSURE( !getListenerAdminData().bAlreadyTerminated, "OObserverImpl::notifyTermination: terminated twice?" );
+ aToNotify = getListenerAdminData().aListeners;
+ getListenerAdminData().bAlreadyTerminated = true;
+ }
+
+ // notify the listeners
+ for (auto const& listener : aToNotify)
+ {
+ listener->notifyTermination();
+ }
+
+ // clear the listener container
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ getListenerAdminData().aListeners.clear();
+ }
+ }
+
+ void SAL_CALL OObserverImpl::disposing( const EventObject& /*Event*/ )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ OSL_ENSURE( getListenerAdminData().bAlreadyTerminated, "OObserverImpl::disposing: disposing without terminated?" );
+#endif
+ // not interested in
+ }
+ }
+
+ //= DesktopTerminationObserver
+
+ void DesktopTerminationObserver::registerTerminationListener( ITerminationListener* _pListener )
+ {
+ if ( !_pListener )
+ return;
+
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ if ( getListenerAdminData().bAlreadyTerminated )
+ {
+ _pListener->notifyTermination();
+ return;
+ }
+
+ getListenerAdminData().aListeners.push_back( _pListener );
+ }
+
+ OObserverImpl::ensureObservation();
+ }
+
+ void DesktopTerminationObserver::revokeTerminationListener( ITerminationListener const * _pListener )
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ Listeners& rListeners = getListenerAdminData().aListeners;
+ std::erase(rListeners, _pListener);
+ }
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/misc/eventlisteneradapter.cxx b/unotools/source/misc/eventlisteneradapter.cxx
new file mode 100644
index 0000000000..b899db5c07
--- /dev/null
+++ b/unotools/source/misc/eventlisteneradapter.cxx
@@ -0,0 +1,155 @@
+/* -*- 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 <vector>
+
+#include <com/sun/star/lang/XComponent.hpp>
+#include <unotools/eventlisteneradapter.hxx>
+#include <osl/diagnose.h>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+
+namespace utl
+{
+
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+
+ //= OEventListenerImpl
+
+ class OEventListenerImpl : public ::cppu::WeakImplHelper< XEventListener >
+ {
+ protected:
+ OEventListenerAdapter* m_pAdapter;
+ Reference< XEventListener > m_xKeepMeAlive;
+ // imagine an implementation of XComponent which holds it's listeners with a weak reference ...
+ // would be very bad if we don't hold ourself
+ Reference< XComponent > m_xComponent;
+
+ public:
+ OEventListenerImpl( OEventListenerAdapter* _pAdapter, const Reference< XComponent >& _rxComp );
+
+ void dispose();
+ const Reference< XComponent >& getComponent() const { return m_xComponent; }
+
+ protected:
+ virtual void SAL_CALL disposing( const EventObject& _rSource ) override;
+ };
+
+ OEventListenerImpl::OEventListenerImpl( OEventListenerAdapter* _pAdapter, const Reference< XComponent >& _rxComp )
+ :m_pAdapter(_pAdapter)
+ {
+ OSL_ENSURE(m_pAdapter, "OEventListenerImpl::OEventListenerImpl: invalid adapter!");
+ // no checks of _rxComp !!
+ // (OEventListenerAdapter is responsible for this)
+
+ // just in case addEventListener throws an exception ... don't initialize m_xKeepMeAlive before this
+ // is done
+ Reference< XEventListener > xMeMyselfAndI = this;
+ _rxComp->addEventListener(xMeMyselfAndI);
+
+ m_xComponent = _rxComp;
+ m_xKeepMeAlive = xMeMyselfAndI;
+ }
+
+ void OEventListenerImpl::dispose()
+ {
+ if (m_xComponent.is())
+ {
+ if (m_xKeepMeAlive.is())
+ m_xComponent->removeEventListener(m_xKeepMeAlive);
+ m_xComponent.clear();
+ m_xKeepMeAlive.clear();
+ }
+ }
+
+ void SAL_CALL OEventListenerImpl::disposing( const EventObject& _rSource )
+ {
+ Reference< XEventListener > xDeleteUponLeaving = m_xKeepMeAlive;
+ m_xKeepMeAlive.clear();
+
+ m_pAdapter->_disposing(_rSource);
+ }
+
+ //= OEventListenerAdapterImpl
+
+ struct OEventListenerAdapterImpl
+ {
+ public:
+ std::vector< rtl::Reference<OEventListenerImpl> > aListeners;
+ };
+
+ //= OEventListenerAdapter
+
+ OEventListenerAdapter::OEventListenerAdapter()
+ :m_pImpl(new OEventListenerAdapterImpl)
+ {
+ }
+
+ OEventListenerAdapter::~OEventListenerAdapter()
+ {
+ stopAllComponentListening( );
+ }
+
+ void OEventListenerAdapter::stopComponentListening( const css::uno::Reference< css::lang::XComponent >& _rxComp )
+ {
+ if ( m_pImpl->aListeners.empty() )
+ return;
+
+ auto it = m_pImpl->aListeners.begin();
+ do
+ {
+ rtl::Reference<OEventListenerImpl>& pListenerImpl = *it;
+ if ((pListenerImpl->getComponent().get() == _rxComp.get()) || (pListenerImpl->getComponent() == _rxComp))
+ {
+ pListenerImpl->dispose();
+ it = m_pImpl->aListeners.erase( it );
+ }
+ else
+ ++it;
+ }
+ while ( it != m_pImpl->aListeners.end() );
+ }
+
+ void OEventListenerAdapter::stopAllComponentListening( )
+ {
+ for ( const auto & i : m_pImpl->aListeners )
+ {
+ i->dispose();
+ }
+ m_pImpl->aListeners.clear();
+ }
+
+ void OEventListenerAdapter::startComponentListening( const Reference< XComponent >& _rxComp )
+ {
+ if (!_rxComp.is())
+ {
+ OSL_FAIL("OEventListenerAdapter::startComponentListening: invalid component!");
+ return;
+ }
+
+ rtl::Reference<OEventListenerImpl> pListenerImpl = new OEventListenerImpl(this, _rxComp);
+ m_pImpl->aListeners.emplace_back(pListenerImpl);
+ }
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/misc/fontcvt.cxx b/unotools/source/misc/fontcvt.cxx
new file mode 100644
index 0000000000..5abda6fdf9
--- /dev/null
+++ b/unotools/source/misc/fontcvt.cxx
@@ -0,0 +1,1432 @@
+/* -*- 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/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <unotools/fontcvt.hxx>
+#include <unotools/fontdefs.hxx>
+
+#include <cstddef>
+#include <map>
+#include <vector>
+
+//These conversion tables were designed for StarSymbol. OpenSymbol
+//originally didn't have the same code points as StarSymbol, and
+//then gained some extra code points, but there are still potentially
+//various holes in OpenSymbol which were filled by StarSymbol, i.e.
+//destination mapping points which are empty in OpenSymbol
+
+const sal_Unicode aStarBatsTab[224] =
+{
+ // F020
+ 0x0020, 0x263a, 0x25cf, 0x274d,
+ 0x25a0, 0x25a1, 0xE000, 0x2751,
+ 0x2752, 0xE001, 0xE002, 0xE003,
+ 0x2756, 0xE004, 0xE005, 0x27a2,
+ // F030
+ 0xE006, 0x2794, 0x2713, 0x2612,
+ 0x2611, 0x27b2, 0x261b, 0x270d,
+ 0x270e, 0xE007, 0x2714, 0xE008,
+ 0xE009, 0xE00A, 0x274f, 0x2750,
+ // F040
+ 0xE00B, 0xE00C, 0xE00D, 0xE00E,
+ 0x2722, 0x2723, 0x2724, 0x2725,
+ 0x2733, 0x2734, 0x2735, 0x2736,
+ 0x2737, 0x2738, 0x2739, 0x2717,
+ // F050
+ 0x2718, 0x2719, 0x271a, 0x271b,
+ 0x271c, 0x272b, 0x272c, 0x272d,
+ 0x272e, 0x272f, 0x2730, 0,
+ 0xE00F, 0x278a, 0x278b, 0x278c,
+ // F060
+ 0x278d, 0x278e, 0x278f, 0x2790,
+ 0x2791, 0x2792, 0x2793, 0xE010,
+ 0x2780, 0x2781, 0x2782, 0x2783,
+ 0x2784, 0x2785, 0x2786, 0x2787,
+ // F070
+ 0x2788, 0x2789, 0xE011, 0xE012,
+ 0x260e, 0xE013, 0xE014, 0xE015,
+ 0xE016, 0xE017, 0xE018, 0xE019,
+ 0xE01A, 0x261e, 0xE01B, 0,
+ // F080
+ 0x20ac, 0, 0x201a, 0x0192,
+ 0x201e, 0x2026, 0x2020, 0x2021,
+ 0xE01c, 0x2030, 0x0160, 0x2039,
+ 0x0152, 0, 0x017d, 0,
+ // F090
+ 0, 0x2018, 0x2019, 0x201c,
+ 0x201d, 0x2022, 0x2013, 0x2014,
+ 0xE01d, 0x2122, 0x0161, 0x203a,
+ 0x0153, 0, 0x017e, 0x0178,
+ // F0A0
+ 0, 0x21e7, 0x21e8, 0x21e9,
+ 0x21e6, 0xE01e, 0xE01f, 0x00a7,
+ 0xE020, 0xE021, 0xE022, 0x00ab,
+ 0xE023, 0x2639, 0xE024, 0xE025,
+ // F0B0
+ 0xE026, 0xE027, 0xE028, 0x21e5,
+ 0x21e4, 0x2192, 0x2193, 0x2190,
+ 0x2191, 0xE029, 0xE02a, 0x00bb,
+ 0xE02b, 0xE02c, 0xE02d, 0xE02e,
+ // F0C0
+ 0xE02f, 0xE030, 0xE031, 0xE032,
+ 0x25be, 0x25b4, 0x25bf, 0x25b5,
+ 0xE033, 0xE034, 0xE035, 0x2702,
+ 0x2708, 0x2721, 0x273f, 0x2744,
+ // F0D0
+ 0x25d7, 0x2759, 0xE036, 0xE037,
+ 0x2762, 0x2663, 0x2665, 0x2660,
+ 0x2194, 0x2195, 0x2798, 0x279a,
+ 0x27b8, 0, 0x00b6, 0,
+ // F0E0
+ 0x00a2, 0x00a4, 0x00a5, 0xE038,
+ 0x20a1, 0x20a2, 0x20a3, 0x20a4,
+ 0x20a9, 0x20ab, 0x20a8, 0xE039,
+ 0, 0, 0, 0,
+ // F0F0
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0xE03a
+};
+
+const sal_Unicode aStarMathTab[224] =
+{
+ // F020
+ 0x0020, 0x0021, 0x0022, 0x0023,
+ 0xE080, 0x0025, 0x0026, 0x221e,
+ 0x0028, 0x0029, 0x002a, 0x002b,
+ 0x002c, 0x002d, 0x002e, 0x002f,
+ // F030
+ 0x2224, 0x21d2, 0x21d0, 0x21d4,
+ 0xE081, 0xE082, 0x00b0, 0,
+ 0, 0, 0x003a, 0x003b,
+ 0x003c, 0x003d, 0x003e, 0x00bf,
+ // F040
+ 0x2260, 0xE083, 0x2212, 0x2217,
+ 0x00d7, 0x22c5, 0x00f7, 0x00b1,
+ 0x2213, 0x2295, 0x2296, 0x2297,
+ 0x2298, 0x2299, 0x222a, 0x2229,
+ // F050
+ 0xE084, 0xE085, 0x2264, 0x2265,
+ 0xE086, 0xE087, 0x226a, 0x226b,
+ 0x007e, 0x2243, 0x2248, 0x225d,
+ 0x2261, 0x221d, 0x2202, 0x2282,
+ // F060
+ 0x2283, 0x2286, 0x2287, 0x2284,
+ 0x2285, 0x2288, 0x2289, 0x2208,
+ 0x2209, 0xE089, 0x2203, 0x220d,
+ 0x2135, 0x2111, 0x211c, 0x2118,
+ // F070
+ 0xE08a, 0x2221, 0x2222, 0x2223,
+ 0x2225, 0x22a5, 0xE08b, 0x22ef,
+ 0x22ee, 0x22f0, 0x22f1, 0x22b6,
+ 0x22b7, 0xE08c, 0x2216, 0x00ac,
+ // F080
+ 0x222b, 0x222c, 0x222d, 0x222e,
+ 0x222f, 0x2230, 0x221a, 0xE08d,
+ 0xE08e, 0xE08f, 0x2210, 0x220f,
+ 0x2211, 0x2207, 0x2200, 0xE090,
+ // F090
+ 0xE091, 0xE092, 0xE093, 0xE094,
+ 0xE095, 0xE096, 0xE097, 0xE098,
+ 0x02d9, 0x00a8, 0xE09b, 0x02da,
+ 0x2227, 0x2228, 0x220b, 0x2205,
+ // F0A0
+ 0x007b, 0x007d, 0xe09e, 0xe09f,
+ 0x2329, 0x232a, 0x005b, 0x005d,
+ 0xE0a0, 0x22be, 0xE0a1, 0x2259,
+ 0x2254, 0x2255, 0x21b3, 0x2197,
+ // F0B0
+ 0x2198, 0x2245, 0x301a, 0x301b,
+ 0x2373, 0xE0a5, 0xE0a6, 0x22a4,
+ 0x2112, 0x2130, 0x2131, 0,
+ 0xE0a7, 0xE0a8, 0xE0a9, 0xE0aa,
+ // F0C0
+ 0x2308, 0x230a, 0x2309, 0x230b,
+ 0, 0xe0ab, 0xe0ac, 0xe0ad,
+ 0xe0ae, 0xe0af, 0xe0b0, 0xe0b1,
+ 0xe0b2, 0xe0b3, 0xe0b4, 0xe0b5,
+ // F0D0
+ 0xe0b6, 0xe0b7, 0xe0b8, 0xe0b9,
+ 0xe0ba, 0xe0bb, 0xe0bc, 0xe0bd,
+ 0xe0be, 0xe0bf, 0xe0c0, 0xe0c1,
+ 0xe0c2, 0xe0c3, 0xe0c4, 0xe0c5,
+ // F0E0
+ 0xe0c6, 0xe0c7, 0xe0c8, 0xe0c9,
+ 0xe0ca, 0xe0cb, 0xe0cc, 0xe0cd,
+ 0xe0ce, 0xe0cf, 0xe0d0, 0xe0d1,
+ 0x03f1, 0xe0d3, 0xe0d4, 0xe0d5,
+ // F0F0
+ 0x2113, 0xe0d6, 0x2107, 0x2127,
+ 0x210a, 0xe0d9, 0x210f, 0x019b,
+ 0xe0db, 0xe0dc, 0xe0dd, 0x2115,
+ 0x2124, 0x211a, 0x211d, 0x2102
+};
+
+const sal_Unicode aWingDingsTab[224] =
+{
+ // F020
+ 0x0020, 0xe400, 0xe401, 0xe402,
+ 0xe403, 0xe404, 0xe405, 0xe406,
+ 0xe407, 0xe408, 0xe409, 0xe40a,
+ 0xe40b, 0xe40c, 0xe40d, 0xe40e,
+ // F030
+ 0xe40f, 0xe410, 0xe411, 0xe412,
+ 0xe413, 0xe414, 0xe415, 0xe416,
+ 0xe417, 0xe418, 0xe419, 0xe41a,
+ 0xe41b, 0xe41c, 0x2707, 0xe41d,
+ // F040
+ 0xe41e, 0xe41f, 0xe420, 0xe421,
+ 0x261c, 0xe423, 0x261d, 0x261f,
+ 0x261f, 0xe424, 0xe425, 0xe426,
+ 0xe427, 0xe428, 0xe429, 0xe42a,
+ // F050
+ 0xe42b, 0xe42c, 0x263c, 0xe42d,
+ 0xe42e, 0xe42f, 0xe430, 0xe431,
+ 0xe432, 0xe433, 0x262a, 0x262f,
+ 0x0950, 0xe434, 0x2648, 0x2649,
+ // F060
+ 0x264a, 0x264b, 0x264c, 0x264d,
+ 0x264e, 0x264f, 0x2650, 0x2651,
+ 0x2652, 0x2653, 0xe435, 0xe436,
+ 0xe437, 0xe438, 0xe439, 0xe43a,
+ // F070
+ 0xe43b, 0xe43c, 0xe43d, 0xe43e,
+ 0xe43f, 0xe440, 0xe441, 0xe442,
+ 0xe443, 0x2353, 0x2318, 0xe444,
+ 0xe445, 0xe446, 0xe447, 0,
+ // F080
+ 0xe448, 0xe449, 0xe44a, 0xe44b,
+ 0xe44c, 0xe44d, 0xe44e, 0xe44f,
+ 0xe450, 0xe451, 0xe452, 0xe453,
+ 0xe454, 0xe455, 0xe456, 0xe457,
+ // F090
+ 0xe458, 0xe459, 0xe45a, 0xe45b,
+ 0xe45c, 0xe45d, 0xe45e, 0xe45f,
+ 0xe460, 0xe461, 0xe462, 0xe463,
+ 0xe464, 0xe465, 0xe466, 0xe467,
+ // F0a0
+ 0xe468, 0xe469, 0xe46a, 0xe46b,
+ 0xe46c, 0xe46d, 0xe46e, 0xe46f,
+ 0xe470, 0xe471, 0xe472, 0xe473,
+ 0xe474, 0xe475, 0xe476, 0xe477,
+ // F0b0
+ 0xe478, 0xe479, 0xe47a, 0xe47b,
+ 0xe47c, 0xe47d, 0xe47e, 0xe47f,
+ 0xe480, 0xe481, 0xe482, 0xe483,
+ 0xe484, 0xe485, 0xe486, 0xe487,
+ // F0c0
+ 0xe488, 0xe489, 0xe48a, 0xe48b,
+ 0xe48c, 0xe48d, 0xe48e, 0xe48f,
+ 0xe490, 0xe491, 0xe492, 0xe493,
+ 0xe494, 0xe495, 0xe496, 0xe497,
+ // F0d0
+ 0xe498, 0xe499, 0xe49a, 0xe49b,
+ 0xe49c, 0x232b, 0x2326, 0xe49d,
+ 0xe49e, 0xe49f, 0xe4a0, 0xe4a1,
+ 0xe4a2, 0xe4a3, 0xe4a4, 0xe4a5,
+ // F0e0
+ 0xe4a6, 0xe4a7, 0xe4a8, 0xe4a9,
+ 0xe4aa, 0xe4ab, 0xe4ac, 0xe4ad,
+ 0xe4ae, 0xe4af, 0xe4b0, 0xe4b1,
+ 0xe4b2, 0xe4b3, 0xe4b4, 0xe4b5,
+ // F0f0
+ 0xe4b6, 0xe4b7, 0xe4b8, 0xe4b9,
+ 0xe4ba, 0xe4bb, 0xe4bc, 0xe4bd,
+ 0xe4be, 0xe4bf, 0xe4c0, 0xe4c1,
+ 0xe4c2, 0xe4c3, 0xe4c4, 0xe4c5
+};
+
+const sal_Unicode aWingDings2Tab[224] =
+{
+ // F020
+ 0x0020, 0xe500, 0xe501, 0xe502,
+ 0xe503, 0xe504, 0xe505, 0xe506,
+ 0xe507, 0xe508, 0xe509, 0xe50a,
+ 0xe50b, 0xe50c, 0xe50d, 0xe50e,
+ // F030
+ 0xe50f, 0xe510, 0xe511, 0xe512,
+ 0xe513, 0xe514, 0xe515, 0xe516,
+ 0xe517, 0xe518, 0xe519, 0xe51a,
+ 0xe51b, 0xe51c, 0xe51d, 0xe51e,
+ // F040
+ 0xe51f, 0xe520, 0xe521, 0xe522,
+ 0xe523, 0xe524, 0xe525, 0xe526,
+ 0xe527, 0xe528, 0xe529, 0xe52a,
+ 0xe52b, 0xe52c, 0xe52d, 0xe52e,
+ // F050
+ 0xe52f, 0xe530, 0xe531, 0xe532,
+ 0xe533, 0xe534, 0xe535, 0xe536,
+ 0xe537, 0x203D, 0x203D, 0x203D,
+ 0xe53b, 0xe53c, 0xe53d, 0xe53e,
+ // F060
+ 0xe53f, 0xe540, 0xe541, 0xe542,
+ 0xe543, 0xe544, 0xe545, 0xe546,
+ 0xe547, 0x24EA, 0x2460, 0x2461,
+ 0x2462, 0x2463, 0x2464, 0x2465,
+ // F070
+ 0x2466, 0x2467, 0x2468, 0x2469,
+ 0xE453, 0x278A, 0x278B, 0x278C,
+ 0x278D, 0x278E, 0x278F, 0x2790,
+ 0x2791, 0x2792, 0x2793, 0,
+ // F080
+ 0x2609, 0x25cb, 0x263d, 0x263e,
+ 0xe55d, 0xe55e, 0xe55f, 0xe560,
+ 0xe561, 0xe562, 0xe563, 0xe564,
+ 0xe565, 0xe566, 0xe567, 0xe568,
+ // F090
+ 0xe569, 0xe56a, 0xe56b, 0xe56c,
+ 0xe56d, 0xe56e, 0xe56f, 0xe570,
+ 0xe571, 0xe572, 0xe573, 0xe574,
+ 0xe575, 0, 0, 0xe578,
+ // F0a0
+ 0xe579, 0xe57a, 0xe57b, 0xe57c,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0xe584,
+ 0xe585, 0, 0xe586, 0,
+ // F0b0
+ 0, 0, 0, 0,
+ 0xe58d, 0xe58e, 0xe58f, 0xe590,
+ 0, 0, 0xe593, 0xe594,
+ 0, 0, 0, 0xe587,
+ // F0c0
+ 0xe599, 0xe59a, 0xe59b, 0xe59c,
+ 0xe59d, 0xe59e, 0xe59f, 0xe5a0,
+ 0xe5a1, 0xe5a2, 0xe5a3, 0xe5a4,
+ 0xe5a5, 0xe5a6, 0xe5a7, 0xe5a8,
+ // F0d0
+ 0xe5a9, 0xe5aa, 0xe5ab, 0xe5ac,
+ 0xe5ad, 0xe5ae, 0xe5af, 0xe5b0,
+ 0xe5b1, 0xe5b2, 0xe5b3, 0xe5b4,
+ 0xe5b5, 0xe5b6, 0xe5b7, 0xe5b8,
+ // F0e0
+ 0xe5b9, 0xe5ba, 0xe5bb, 0xe5bc,
+ 0xe5bd, 0xe5be, 0xe5bf, 0xe5c0,
+ 0xe5c1, 0xe5c2, 0xe5c3, 0xe5c4,
+ 0xe5c5, 0xe5c6, 0xe5c7, 0xe5c8,
+ // F0f0
+ 0xe5c9, 0, 0xe5cb, 0xe477,
+ 0xe5cd, 0xe5ce, 0xe5cf, 0xe5d0,
+ 0x203b, 0x2042, 0, 0,
+ 0, 0, 0, 0
+};
+
+const sal_Unicode aWingDings3Tab[224] =
+{
+ // F020
+ 0x0020, 0xe600, 0xe601, 0xe602,
+ 0xe603, 0x2196, 0xe604, 0x2199,
+ 0xe605, 0xe606, 0xe607, 0xe608,
+ 0xe609, 0xe60a, 0xe60b, 0x21de,
+ // F030
+ 0x21df, 0xe60c, 0xe60d, 0xe60e,
+ 0x21e2, 0x21e1, 0x21e3, 0x21af,
+ 0x21b5, 0xe60f, 0xe610, 0xe611,
+ 0xe612, 0xe613, 0xe614, 0xe615,
+ // F040
+ 0xe616, 0xe617, 0xe618, 0xe619,
+ 0x21c4, 0x21c5, 0xe61a, 0xe61b,
+ 0x21c7, 0x21c9, 0x21c8, 0x21ca,
+ 0x21b6, 0x21b7, 0xe61c, 0xe61d,
+ // F050
+ 0x21bb, 0x21ba, 0xe61e, 0x2324,
+ 0x2303, 0x2325, 0x2334, 0xe61f,
+ 0x21ea, 0xe620, 0xe621, 0xe622,
+ 0xe623, 0xe624, 0xe625, 0xe626,
+ // F060
+ 0xe627, 0xe628, 0xe629, 0xe62a,
+ 0xe62b, 0xe62c, 0xe62d, 0xe62e,
+ 0xe62f, 0xe630, 0xe631, 0xe632,
+ 0xe633, 0xe634, 0xe635, 0xe636,
+ // F070
+ 0xe637, 0xe638, 0x25b3, 0x25bd,
+ 0x25c0, 0x25b6, 0x25c1, 0x25b7,
+ 0x25e3, 0xe639, 0x25e4, 0x25e5,
+ 0x25c2, 0x25b8, 0xe63a, 0,
+ // F080
+ 0xe63b, 0xe63c, 0xe63d, 0xe63e,
+ 0xe63f, 0xe640, 0xe641, 0xe642,
+ 0xe643, 0xe644, 0xe645, 0xe646,
+ 0xe647, 0xe648, 0xe649, 0xe64a,
+ // F090
+ 0xe64b, 0xe64c, 0xe64d, 0xe64e,
+ 0xe64f, 0xe650, 0xe651, 0xe652,
+ 0xe653, 0xe654, 0xe655, 0xe656,
+ 0xe657, 0xe658, 0xe659, 0xe65a,
+ // F0a0
+ 0xe65b, 0xe65c, 0xe65d, 0xe65e,
+ 0xe65f, 0xe660, 0xe661, 0xe662,
+ 0xe663, 0xe664, 0xe665, 0xe666,
+ 0xe667, 0xe668, 0xe669, 0xe66a,
+ // F0b0
+ 0xe66b, 0xe66c, 0xe66d, 0xe66e,
+ 0xe66f, 0xe670, 0xe671, 0xe672,
+ 0xe673, 0xe674, 0xe675, 0xe676,
+ 0xe677, 0xe678, 0xe679, 0xe67a,
+ // F0c0
+ 0xe67b, 0xe67c, 0xe67d, 0xe67e,
+ 0xe67f, 0xe680, 0xe681, 0xe682,
+ 0xe683, 0xe684, 0xe685, 0xe686,
+ 0xe687, 0xe688, 0xe689, 0xe68a,
+ // F0d0
+ 0xe68b, 0xe68c, 0xe68d, 0xe68e,
+ 0xe68f, 0xe690, 0xe691, 0xe692,
+ 0xe693, 0xe694, 0xe695, 0xe696,
+ 0xe697, 0xe698, 0xe699, 0xe69a,
+ // F0e0
+ 0xe69b, 0xe69c, 0xe69d, 0xe69e,
+ 0xe69f, 0xe6a0, 0xe6a1, 0xe6a2,
+ 0xe6a3, 0xe6a4, 0xe6a5, 0xe6a6,
+ 0xe6a7, 0xe6a8, 0xe6a9, 0xe6aa,
+ // F0f0
+ 0xe6ab, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0
+};
+
+const sal_Unicode aWebDingsTab[224] =
+{
+ // F020
+ 0x0020, 0xe300, 0xe301, 0xe302,
+ 0xe303, 0xe304, 0xe305, 0xe306,
+ 0xe307, 0xe308, 0xe309, 0xe30a,
+ 0xe30b, 0xe30c, 0xe30d, 0xe30e,
+ // F030
+ 0xe30f, 0xe310, 0xe311, 0xe312,
+ 0xe313, 0xe314, 0xe315, 0xe316,
+ 0xe317, 0xe318, 0xe319, 0xe31a,
+ 0xe31b, 0xe31c, 0xe31d, 0xe31e,
+ // F040
+ 0xe31f, 0xe320, 0xe321, 0xe322,
+ 0xe323, 0xe324, 0xe325, 0xe326,
+ 0xe327, 0xe328, 0xe329, 0xe32a,
+ 0xe32b, 0xe32c, 0xe32d, 0xe32e,
+ // F050
+ 0xe32f, 0xe330, 0xe331, 0xe332,
+ 0xe333, 0xe334, 0xe335, 0xe336,
+ 0xe337, 0xe338, 0xe339, 0xe33a,
+ 0xe33b, 0xe33c, 0xe33d, 0xe33e,
+ // F060
+ 0xe33f, 0xe340, 0xe341, 0xe342,
+ 0xe343, 0xe344, 0xe345, 0xe346,
+ 0xe347, 0xe348, 0xe349, 0xe34a,
+ 0xe34b, 0xe34c, 0xe34d, 0xe34e,
+ // F070
+ 0xe34f, 0xe350, 0xe351, 0xe352,
+ 0xe353, 0xe354, 0xe355, 0xe356,
+ 0xe357, 0xe358, 0xe359, 0xe35a,
+ 0xe35b, 0xe35c, 0xe35d, 0,
+ // F080
+ 0xe35e, 0xe35f, 0xe360, 0xe361,
+ 0xe362, 0xe363, 0xe364, 0xe365,
+ 0xe366, 0xe367, 0xe368, 0xe369,
+ 0xe36a, 0xe36b, 0xe36c, 0xe36d,
+ // F090
+ 0xe36e, 0xe36f, 0xe370, 0xe371,
+ 0xe372, 0xe373, 0xe374, 0xe375,
+ 0xe376, 0xe377, 0xe378, 0xe379,
+ 0xe37a, 0xe37b, 0xe37c, 0xe37d,
+ // F0a0
+ 0xe37e, 0xe37f, 0xe380, 0xe381,
+ 0xe382, 0xe383, 0xe384, 0xe385,
+ 0xe386, 0xe387, 0xe388, 0xe389,
+ 0xe38a, 0xe38b, 0xe38c, 0xe38d,
+ // F0b0
+ 0xe38e, 0xe38f, 0xe390, 0xe391,
+ 0xe392, 0xe393, 0xe394, 0xe395,
+ 0xe396, 0xe397, 0xe398, 0xe399,
+ 0xe39a, 0xe39b, 0xe39c, 0xe39d,
+ // F0c0
+ 0xe39e, 0xe39f, 0xe3a0, 0xe3a1,
+ 0xe3a2, 0xe3a3, 0xe3a4, 0xe3a5,
+ 0xe3a6, 0xe3a7, 0xe3a8, 0xe3a9,
+ 0xe3aa, 0xe3ab, 0xe3ac, 0xe3ad,
+ // F0d0
+ 0xe3ae, 0xe3af, 0xe3b0, 0xe3b1,
+ 0xe3b2, 0xe3b3, 0xe3b4, 0xe3b5,
+ 0xe3b6, 0xe3b7, 0xe3b8, 0xe3b9,
+ 0xe3ba, 0xe3bb, 0xe3bc, 0xe3bd,
+ // F0e0
+ 0xe3be, 0xe3bf, 0xe3c0, 0xe3c1,
+ 0xe3c2, 0xe3c3, 0xe3c4, 0xe3c5,
+ 0xe3c6, 0xe3c7, 0xe3c8, 0xe3c9,
+ 0xe3ca, 0xe3cb, 0xe3cd, 0xe3ce,
+ // F0f0
+ 0xe3cf, 0xe3d0, 0xe3d1, 0xe3d2,
+ 0xe3d3, 0xe3d4, 0xe3d5, 0xe3d6,
+ 0xe3d7, 0xe3d8, 0xe3d9, 0xe3da,
+ 0xe3db, 0xe3dc, 0xe3dd, 0xe3de
+};
+
+// See http://www.iana.org/assignments/character-sets/character-sets.xml
+// See ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/ADOBE/symbol.txt
+
+const sal_Unicode aAdobeSymbolTab[224] =
+{
+//TODO:
+ // F020
+ 0x0020, 0xe100, 0xe101, 0xe102,
+ 0xe103, 0xe104, 0xe16a, 0xe105,
+ 0xe106, 0xe107, 0xe108, 0xe109,
+ 0xe10a, 0xe10b, 0xe10c, 0xe10d,
+ // F030
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0xe10e, 0xe10f,
+ 0xe110, 0xe111, 0xe112, 0x003f,
+ // F040
+ 0xe113, 0x0391, 0x0392, 0x03a7,
+ 0x0394, 0x0395, 0x03a6, 0x0393,
+ 0x0397, 0x0399, 0x03d1, 0x039a,
+ 0x039b, 0x039c, 0x039d, 0x039f,
+ // F050
+ 0x03a0, 0x0398, 0x03a1, 0x03a3,
+ 0x03a4, 0x03a5, 0x03c2, 0x03a9,
+ 0x039e, 0x03a8, 0x0396, 0xe114,
+ 0x2234, 0xe115, 0xe116, 0x005f,
+ // F060
+ 0x00af, 0x03b1, 0x03b2, 0x03c7,
+ 0x03b4, 0x03b5, 0x03d5, 0x03b3,
+ 0x03b7, 0x03b9, 0x03c6, 0x03ba,
+ 0x03bb, 0x03bc, 0x03bd, 0x03bf,
+ // F070
+ 0x03c0, 0x03b8, 0x03c1, 0x03c3,
+ 0x03c4, 0x03c5, 0x03d6, 0x03c9,
+ 0x03be, 0x03c8, 0x03b6, 0xe117,
+ 0x007c, 0xe118, 0xe119, 0,
+ // F080
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ // F090
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ // F0a0
+ 0xe11a, 0x03d2, 0x2032, 0xe11b,
+ 0x2215, 0xe11c, 0xe11d, 0xe11e,
+ 0x2666, 0xe11f, 0xe120, 0xe121,
+ 0xe122, 0xe123, 0xe124, 0xe125,
+ // F0b0
+ 0xe126, 0xe127, 0x2033, 0xe128,
+ 0xe129, 0xe12a, 0xe12b, 0xe12c,
+ 0xe12d, 0xe12e, 0xe12f, 0xe130,
+ 0xe131, 0x2502, 0x2500, 0x21b2,
+ // F0c0
+ 0xe132, 0xe133, 0xe134, 0xe135,
+ 0xe136, 0xe137, 0xe138, 0xe139,
+ 0xe13a, 0xe13b, 0xe13c, 0xe13d,
+ 0xe13e, 0xe13f, 0xe140, 0xe141,
+ // F0d0
+ 0x2220, 0xe142, 0x00ae, 0x00a9,
+ 0xe143, 0xe144, 0xe145, 0xe146,
+ 0xe147, 0xe148, 0xe149, 0xe14a,
+ 0xe14b, 0x21d1, 0xe14c, 0x21d3,
+ // F0e0
+ 0x25ca, 0xe14d, 0xe14e, 0xe14f,
+ 0xe150, 0xe151, 0xf8eb, 0xf8ec,
+ 0xf8ed, 0xf8ee, 0xf8ef, 0xf8f0,
+ 0xf8f1, 0xf8f2, 0xf8f3, 0xf8f4,
+ // F0f0
+ 0, 0x232a, 0x222b, 0x2320,
+ 0xf8f5, 0x2321, 0xf8f6, 0xf8f7,
+ 0xf8f8, 0xf8f9, 0xf8fa, 0xf8fb,
+ 0xf8fc, 0xf8fd, 0xf8fe, 0,
+};
+
+const sal_Unicode aMonotypeSortsTab[224] =
+{
+ // F020
+ 0x0020, 0x2701, 0xe200, 0x2703,
+ 0x2704, 0xe201, 0x2706, 0xe202,
+ 0xe203, 0xe203, 0xe204, 0xe205,
+ 0x270c, 0xe206, 0xe207, 0xe208,
+ // F030
+ 0x2710, 0x2711, 0x2712, 0xe209,
+ 0xe20a, 0x2715, 0x2716, 0xe20b,
+ 0xe20c, 0xe20d, 0xe20e, 0xe20f,
+ 0xe210, 0x271d, 0x271e, 0x271f,
+ // F040
+ 0x2720, 0xe211, 0xe212, 0xe213,
+ 0xe214, 0xe215, 0x2726, 0x2727,
+ 0x2605, 0x2729, 0x272a, 0xe216,
+ 0xe217, 0xe218, 0xe219, 0xe21a,
+ // F050
+ 0xe21b, 0xe21c, 0x2732, 0xe21d,
+ 0xe21e, 0xe21f, 0xe220, 0xe221,
+ 0xe222, 0xe223, 0x273a, 0x273b,
+ 0x273c, 0x273d, 0x273e, 0xe224,
+ // F060
+ 0x2740, 0x2741, 0x2742, 0x2743,
+ 0xe225, 0x2745, 0x2746, 0x2747,
+ 0x2748, 0x2749, 0x274a, 0x274b,
+ 0xe226, 0xe227, 0xe228, 0xe229,
+ // F070
+ 0xe22a, 0xe22b, 0xe22c, 0x25b2,
+ 0x25bc, 0xe22d, 0xe22e, 0xe22f,
+ 0x2758, 0xe230, 0x275a, 0x275b,
+ 0x275c, 0x275d, 0x275e, 0,
+ // F080
+ 0xe231, 0xe232, 0xe233, 0xe234,
+ 0xe235, 0xe236, 0xe237, 0xe238,
+ 0xe239, 0xe23a, 0xe23b, 0xe23c,
+ 0xe23d, 0xe23e, 0, 0,
+ // F090
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ // F0a0
+ 0, 0xe23f, 0xe240, 0x2763,
+ 0x2764, 0x2765, 0x2766, 0x2767,
+ 0xe241, 0xe242, 0xe243, 0xe244,
+ 0x2460, 0x2461, 0x2462, 0x2463,
+ // F0b0
+ 0x2464, 0x2465, 0x2466, 0x2467,
+ 0x2468, 0x2469, 0x2776, 0x2777,
+ 0x2778, 0x2779, 0x277a, 0x277b,
+ 0x277c, 0x277d, 0x277e, 0x277f,
+ // F0c0
+ 0xe245, 0xe246, 0xe247, 0xe248,
+ 0xe249, 0xe24a, 0xe24b, 0xe24c,
+ 0xe24d, 0xe24e, 0xe24f, 0xe250,
+ 0xe251, 0xe252, 0xe253, 0xe254,
+ // F0d0
+ 0xe255, 0xe256, 0xe257, 0xe258,
+ 0xe259, 0xe25a, 0xe25b, 0xe25c,
+ 0xe25d, 0x2799, 0xe25e, 0x279b,
+ 0x279c, 0x279d, 0x279e, 0x279f,
+ // F0e0
+ 0x27a0, 0x27a1, 0xe25f, 0x27a3,
+ 0x27a4, 0x27a5, 0x27a6, 0x27a7,
+ 0x27a8, 0x27a9, 0x27aa, 0x27ab,
+ 0x27ac, 0x27ad, 0x27ae, 0x27af,
+ // F0f0
+ 0, 0x27b1, 0xe260, 0x27b3,
+ 0x27b4, 0x27b5, 0x27b6, 0x27b7,
+ 0xe261, 0x27b9, 0x27ba, 0x27bb,
+ 0x27bc, 0x27bd, 0x27be, 0,
+};
+
+const sal_Unicode aMTExtraTab[224] =
+{
+ // F020
+ 0x0020, 0, 0, 0x0300,
+ 0x0302, 0x0303, 0x0307, 0,
+ 0x2323, 0x2322, 0, 0,
+ 0, 0, 0, 0,
+ // F030
+ 0, 0xEC00, 0xEC01, 0xEC02,
+ 0xEC03, 0xEC0B, 0xEC04, 0xEC05,
+ 0xEC06, 0, 0x223C, 0x2243,
+ 0x22B2, 0x226A, 0x22B3, 0x226B,
+ // F040
+ 0x225C, 0x2259, 0x2250, 0x2210,
+ 0x019B, 0xEC0E, 0xEC0F, 0xEC10,
+ 0xEC11, 0x2229, 0x2127, 0x2026,
+ 0x22EF, 0x22EE, 0x22F0, 0x22F1,
+ // F050
+ 0x2225, 0x2235, 0x2221, 0x2222,
+ 0, 0x222A, 0x25B3, 0x25A1,
+ 0x25AD, 0x25B1, 0x2197, 0x2199,
+ 0xEB05, 0x2198, 0x2196, 0xEB06,
+ // F060
+ 0x2035, 0x21A6, 0x2195, 0x21D5,
+ 0x25CB, 0x2299, 0x227B, 0xE98F,
+ 0x210F, 0xEE00, 0xEE01, 0,
+ 0x2113, 0x2213, 0xFFFD, 0x2218,
+ // F070
+ 0x227A, 0xEB1A, 0x20D7, 0x20D6,
+ 0x20E1, 0xEB00, 0x20D1, 0x20D0,
+ 0xEB19, 0, 0, 0xFE38,
+ 0xEC0C, 0xFE37, 0xEC0D, 0,
+ // F080
+ 0x21C4, 0xEB01, 0xEB02, 0x21CC,
+ 0xEB03, 0xEB04, 0x21C0, 0x21BD,
+ 0xF8E7, 0, 0, 0,
+ 0, 0, 0, 0,
+ // F090
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ // F0a0
+ 0, 0x211D, 0x2124, 0x2102,
+ 0x211A, 0x2115, 0, 0x301A,
+ 0x301B, 0xEC22, 0xEC23, 0xEC24,
+ 0xEC25, 0xEC26, 0xEC27, 0,
+ // F0b0
+ 0xEE04, 0xEE05, 0xEE06, 0,
+ 0, 0xEE07, 0xEE08, 0xEE09,
+ 0, 0, 0xEE0A, 0xEE0B,
+ 0xEE0C, 0, 0, 0,
+ // F0c0
+ 0xEE0D, 0xEE0E, 0xEE0F, 0xEE10,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ // F0d0
+ 0, 0xEE11, 0xEE12, 0xEE13,
+ 0, 0xEE15, 0xEE16, 0,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0,
+ // F0e0
+ 0, 0, 0, 0,
+ 0, 0, 0, 0xF8FF,
+ 0x0160, 0x00DD, 0x00DE, 0x00D0,
+ 0x0161, 0x00FD, 0x00FE, 0x00F0,
+ // F0f0
+ 0xFB01, 0xFB02, 0x0131, 0x00B9,
+ 0x00B2, 0x00B3, 0x00BD, 0x00BC,
+ 0x00BE, 0x2044, 0x00A6, 0x02DD,
+ 0x02D8, 0x02C7, 0x02DA, 0x02DB,
+};
+
+const sal_Unicode aAdobeSymbolToAppleSymbolTab[224] =
+{
+ // F020
+ 0x0020, 0x0021, 0x2200, 0x0023,
+ 0x2203, 0x0025, 0x0026, 0x220D,
+ 0x0028, 0x0029, 0x2217, 0x002B,
+ 0x002C, 0x2212, 0x002E, 0x002F,
+ // F030
+ 0x0030, 0x0031, 0x0032, 0x0033,
+ 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003A, 0x003B,
+ 0x003C, 0x003D, 0x003E, 0x003F,
+ // F040
+ 0x2245, 0x0391, 0x0392, 0x03A7,
+ 0x0394, 0x0395, 0x03A6, 0x0393,
+ 0x0397, 0x0399, 0x03D1, 0x039A,
+ 0x039B, 0x039C, 0x039D, 0x039F,
+ // F050
+ 0x03A0, 0x0398, 0x03A1, 0x03A3,
+ 0x03A4, 0x03A5, 0x03C2, 0x03A9,
+ 0x039E, 0x03A8, 0x0396, 0x005B,
+ 0x2234, 0x005D, 0x22A5, 0x005F,
+ // F060
+ 0xF8E5, 0x03B1, 0x03B2, 0x03C7,
+ 0x03B4, 0x03B5, 0x03C6, 0x03B3,
+ 0x03B7, 0x03B9, 0x03D5, 0x03BA,
+ 0x03BB, 0x03BC, 0x03BD, 0x03BF,
+ // F070
+ 0x03C0, 0x03B8, 0x03C1, 0x03C3,
+ 0x03C4, 0x03C5, 0x03D6, 0x03C9,
+ 0x03BE, 0x03C8, 0x03B6, 0x007B,
+ 0x007C, 0x007D, 0x223C, 0x007F,
+ // F080
+ 0x0080, 0x0081, 0x0082, 0x0083,
+ 0x0084, 0x0085, 0x0086, 0x0087,
+ 0x0088, 0x0089, 0x008A, 0x008B,
+ 0x008C, 0x008D, 0x008E, 0x008F,
+ // F090
+ 0x0090, 0x0091, 0x0092, 0x0093,
+ 0x0094, 0x0095, 0x0096, 0x0097,
+ 0x0098, 0x0099, 0x009A, 0x009B,
+ 0x009C, 0x009D, 0x009E, 0x009F,
+ // F0a0
+ 0x20AC, 0x03D2, 0x2032, 0x2264,
+ 0x2044, 0x221E, 0x0192, 0x2663,
+ 0x2666, 0x2665, 0x2660, 0x2194,
+ 0x2190, 0x2191, 0x2192, 0x2193,
+ // F0b0
+ 0x00B0, 0x00B1, 0x2033, 0x2065,
+ 0x00D7, 0x221D, 0x2202, 0x2022,
+ 0x00F7, 0x2260, 0x2261, 0x2248,
+ 0x2026, 0x23D0, 0x23AF, 0x21B5,
+ // F0c0
+ 0x2135, 0x2111, 0x211C, 0x2118,
+ 0x2297, 0x2295, 0x2205, 0x2229,
+ 0x222A, 0x2283, 0x2287, 0x2284,
+ 0x2282, 0x2286, 0x2208, 0x2209,
+ // F0d0
+ 0x2220, 0x2207, 0x00AE, 0x00A9,
+ 0x2122, 0x220F, 0x221A, 0x22C5,
+ 0x00AC, 0x2227, 0x2228, 0x21D4,
+ 0x21D0, 0x21D1, 0x21D2, 0x21D3,
+ // F0e0
+ 0x25CA, 0x3008, 0x00AE, 0x00A9,
+ 0x2122, 0x2211, 0x239B, 0x239C,
+ 0x239D, 0x23A1, 0x23A2, 0x23A3,
+ 0x23A7, 0x23A8, 0x23A9, 0x23AA,
+ // F0f0
+ 0xF8FF, 0x3009, 0x222B, 0x2320,
+ 0x23AE, 0x2321, 0x239E, 0x239F,
+ 0x23A0, 0x23A4, 0x23A5, 0x23A6,
+ 0x23AB, 0x23AC, 0x23AD, 0x00FF
+};
+
+static sal_Unicode ImplStarSymbolToStarBats( sal_Unicode c )
+{
+ switch ( c )
+ {
+ case 0x00A2: c = 0xF0E0; break;
+ case 0x00A4: c = 0xF0E1; break;
+ case 0x00A5: c = 0xF0E2; break;
+ case 0x00A7: c = 0xF0A7; break;
+ case 0x00AB: c = 0xF0AB; break;
+ case 0x00B6: c = 0xF0DE; break;
+ case 0x00BB: c = 0xF0BB; break;
+
+ case 0x0152: c = 0xF08C; break;
+ case 0x0153: c = 0xF09C; break;
+ case 0x0160: c = 0xF08A; break;
+ case 0x0161: c = 0xF09A; break;
+ case 0x0178: c = 0xF09F; break;
+ case 0x017D: c = 0xF08E; break;
+ case 0x017E: c = 0xF09E; break;
+
+ case 0x0192: c = 0xF083; break;
+ case 0x02C6: c = 0xF088; break;
+ case 0x02DC: c = 0xF098; break;
+ case 0x2013: c = 0xF096; break;
+ case 0x2014: c = 0xF097; break;
+ case 0x2018: c = 0xF091; break;
+
+ case 0x2019: c = 0xF092; break;
+ case 0x201A: c = 0xF082; break;
+ case 0x201C: c = 0xF093; break;
+ case 0x201D: c = 0xF094; break;
+ case 0x201E: c = 0xF084; break;
+ case 0x2020: c = 0xF086; break;
+
+ case 0x2021: c = 0xF087; break;
+ case 0x2022: c = 0xF095; break;
+ case 0x2026: c = 0xF085; break;
+ case 0x2030: c = 0xF089; break;
+ case 0x2039: c = 0xF08B; break;
+ case 0x203A: c = 0xF09B; break;
+
+ case 0x20A1: c = 0xF0E4; break;
+ case 0x20A2: c = 0xF0E5; break;
+ case 0x20A3: c = 0xF0E6; break;
+ case 0x20A4: c = 0xF0E7; break;
+ case 0x20A8: c = 0xF0EA; break;
+ case 0x20A9: c = 0xF0E8; break;
+ case 0x20AB: c = 0xF0E9; break;
+ case 0x20AC: c = 0xF080; break;
+
+ case 0x2122: c = 0xF099; break;
+ case 0x2190: c = 0xF0B7; break;
+ case 0x2191: c = 0xF0B8; break;
+ case 0x2192: c = 0xF0B5; break;
+ case 0x2193: c = 0xF0B6; break;
+ case 0x2194: c = 0xF0D8; break;
+ case 0x2195: c = 0xF0D9; break;
+
+ case 0x21E4: c = 0xF0B4; break;
+ case 0x21E5: c = 0xF0B3; break;
+ case 0x21E6: c = 0xF0A4; break;
+ case 0x21E7: c = 0xF0B2; break;
+ case 0x21E8: c = 0xF0AF; break;
+ case 0x21E9: c = 0xF0A3; break;
+
+ case 0x25A0: c = 0xF024; break;
+ case 0x25A1: c = 0xF025; break;
+ case 0x25B4: c = 0xF0C5; break;
+ case 0x25B5: c = 0xF0C7; break;
+ case 0x25BE: c = 0xF0C4; break;
+ case 0x25BF: c = 0xF0C6; break;
+ case 0x25C6: c = 0xF043; break;
+ case 0x25CF: c = 0xF022; break;
+ case 0x25D7: c = 0xF0D0; break;
+
+ case 0x260E: c = 0xF074; break;
+ case 0x2611: c = 0xF034; break;
+ case 0x2612: c = 0xF033; break;
+ case 0x261B: c = 0xF036; break;
+ case 0x261E: c = 0xF07D; break;
+ case 0x2639: c = 0xF0AD; break;
+ case 0x263A: c = 0xF021; break;
+
+ case 0x2702: c = 0xF0CB; break;
+ case 0x2708: c = 0xF0CC; break;
+ case 0x270D: c = 0xF07E; break;
+ case 0x270E: c = 0xF038; break;
+
+ case 0x2713: c = 0xF039; break;
+ case 0x2714: c = 0xF03A; break;
+ case 0x2717: c = 0xF04F; break;
+ case 0x2718: c = 0xF050; break;
+ case 0x2719: c = 0xF051; break;
+ case 0x271A: c = 0xF052; break;
+ case 0x271B: c = 0xF053; break;
+ case 0x271C: c = 0xF054; break;
+
+ case 0x2721: c = 0xF0CD; break;
+ case 0x2722: c = 0xF044; break;
+ case 0x2723: c = 0xF045; break;
+ case 0x2724: c = 0xF046; break;
+ case 0x2725: c = 0xF047; break;
+ case 0x272B: c = 0xF055; break;
+ case 0x272C: c = 0xF056; break;
+ case 0x272D: c = 0xF057; break;
+ case 0x272E: c = 0xF058; break;
+ case 0x272F: c = 0xF059; break;
+
+ case 0x2730: c = 0xF05A; break;
+ case 0x2733: c = 0xF048; break;
+ case 0x2734: c = 0xF049; break;
+ case 0x2735: c = 0xF04A; break;
+ case 0x2736: c = 0xF04B; break;
+ case 0x2737: c = 0xF04C; break;
+ case 0x2738: c = 0xF04D; break;
+ case 0x2739: c = 0xF04E; break;
+ case 0x273F: c = 0xF0CE; break;
+
+ case 0x2744: c = 0xF0CF; break;
+ case 0x274D: c = 0xF023; break;
+ case 0x274F: c = 0xF03E; break;
+ case 0x2750: c = 0xF03F; break;
+ case 0x2751: c = 0xF027; break;
+ case 0x2752: c = 0xF028; break;
+ case 0x2756: c = 0xF02C; break;
+ case 0x2759: c = 0xF0D1; break;
+ case 0x2762: c = 0xF0D4; break;
+
+ case 0x2780: c = 0xF068; break;
+ case 0x2781: c = 0xF069; break;
+ case 0x2782: c = 0xF06A; break;
+ case 0x2783: c = 0xF06B; break;
+ case 0x2784: c = 0xF06C; break;
+ case 0x2785: c = 0xF06D; break;
+ case 0x2786: c = 0xF06E; break;
+ case 0x2787: c = 0xF06F; break;
+ case 0x2788: c = 0xF070; break;
+ case 0x2789: c = 0xF071; break;
+ case 0x278A: c = 0xF05D; break;
+ case 0x278B: c = 0xF05E; break;
+ case 0x278C: c = 0xF05F; break;
+ case 0x278D: c = 0xF060; break;
+ case 0x278E: c = 0xF061; break;
+ case 0x278F: c = 0xF062; break;
+
+ case 0x2790: c = 0xF063; break;
+ case 0x2791: c = 0xF064; break;
+ case 0x2792: c = 0xF065; break;
+ case 0x2793: c = 0xF066; break;
+ case 0x2794: c = 0xF031; break;
+ case 0x2798: c = 0xF0DA; break;
+ case 0x279A: c = 0xF0DB; break;
+
+ case 0x27A2: c = 0xF02F; break;
+ case 0x27B2: c = 0xF035; break;
+ case 0x27B8: c = 0xF0DC; break;
+
+ case 0xE000: c = 0xF000+38; break;
+ case 0xE001: c = 0xF000+41; break;
+ case 0xE002: c = 0xF000+42; break;
+ case 0xE003: c = 0xF000+43; break;
+ case 0xE004: c = 0xF000+45; break;
+ case 0xE005: c = 0xF000+46; break;
+ case 0xE006: c = 0xF000+48; break;
+ case 0xE007: c = 0xF000+57; break;
+
+ case 0xE008: c = 0xF000+59; break;
+ case 0xE009: c = 0xF000+60; break;
+ case 0xE00a: c = 0xF000+61; break;
+ case 0xE00b: c = 0xF000+64; break;
+ case 0xE00c: c = 0xF000+65; break;
+ case 0xE00d: c = 0xF000+66; break;
+ case 0xE00e: c = 0xF000+67; break;
+ case 0xE00f: c = 0xF000+92; break;
+
+ case 0xE010: c = 0xF000+103; break;
+ case 0xE011: c = 0xF000+114; break;
+ case 0xE012: c = 0xF000+115; break;
+ case 0xE013: c = 0xF000+117; break;
+ case 0xE014: c = 0xF000+118; break;
+ case 0xE015: c = 0xF000+119; break;
+ case 0xE016: c = 0xF000+120; break;
+ case 0xE017: c = 0xF000+121; break;
+
+ case 0xE018: c = 0xF000+122; break;
+ case 0xE019: c = 0xF000+123; break;
+ case 0xE01a: c = 0xF000+124; break;
+ case 0xE01b: c = 0xF000+126; break;
+ case 0xE01c: c = 0xF000+136; break;
+ case 0xE01d: c = 0xF000+155; break;
+ case 0xE01e: c = 0xF000+165; break;
+ case 0xE01f: c = 0xF000+166; break;
+
+ case 0xE020: c = 0xF000+168; break;
+ case 0xE021: c = 0xF000+169; break;
+ case 0xE022: c = 0xF000+170; break;
+ case 0xE023: c = 0xF000+172; break;
+ case 0xE024: c = 0xF000+174; break;
+ case 0xE025: c = 0xF000+175; break;
+ case 0xE026: c = 0xF000+176; break;
+ case 0xE027: c = 0xF000+177; break;
+
+ case 0xE028: c = 0xF000+178; break;
+ case 0xE029: c = 0xF000+185; break;
+ case 0xE02a: c = 0xF000+186; break;
+ case 0xE02b: c = 0xF000+188; break;
+ case 0xE02c: c = 0xF000+189; break;
+ case 0xE02d: c = 0xF000+190; break;
+ case 0xE02e: c = 0xF000+191; break;
+ case 0xE02f: c = 0xF000+192; break;
+
+ case 0xE030: c = 0xF000+193; break;
+ case 0xE031: c = 0xF000+194; break;
+ case 0xE032: c = 0xF000+195; break;
+ case 0xE033: c = 0xF000+200; break;
+ case 0xE034: c = 0xF000+201; break;
+ case 0xE035: c = 0xF000+202; break;
+ case 0xE036: c = 0xF000+210; break;
+ case 0xE037: c = 0xF000+211; break;
+
+ case 0xE038: c = 0xF000+227; break;
+ case 0xE039: c = 0xF000+235; break;
+ case 0xE03a: c = 0xF000+255; break;
+
+ default: c = 0; break;
+ }
+
+ return c;
+}
+
+namespace {
+
+enum SymbolFont
+{
+ Symbol=1, Wingdings=2, MonotypeSorts=4, Webdings=8, Wingdings2=16,
+ Wingdings3=32, MTExtra=64, TimesNewRoman=128
+};
+
+}
+
+const char * const aSymbolNames[] =
+{
+ "Symbol", "Wingdings", "Monotype Sorts", "Webdings", "Wingdings 2",
+ "Wingdings 3", "MT Extra", "Times New Roman"
+};
+
+namespace {
+
+struct SymbolEntry
+{
+ sal_uInt8 cIndex;
+ enum SymbolFont eFont;
+};
+
+class StarSymbolToMSMultiFontImpl : public StarSymbolToMSMultiFont
+{
+private:
+ ::std::multimap<sal_Unicode, SymbolEntry> maMagicMap;
+public:
+ explicit StarSymbolToMSMultiFontImpl();
+ OUString ConvertChar(sal_Unicode &rChar) override;
+};
+
+struct ExtraTable { sal_Unicode cStar; sal_uInt8 cMS;};
+
+}
+
+ExtraTable const aWingDingsExtraTab[] =
+{
+ {0x25cf, 0x6C}, {0x2714, 0xFC}, {0x2717, 0xFB}, {0x2794, 0xE8},
+ {0x27a2, 0xD8}, {0xe000, 0x6F}, {0xe001, 0x73}, {0xe002, 0x74},
+ {0xe003, 0x75}, {0xe004, 0x77}, {0xe005, 0xA6}, {0xe006, 0xE0},
+ {0xe007, 0xFC}, {0xe008, 0x6C}, {0xe009, 0x6D}, {0xe00a, 0x6E},
+ {0xe00b, 0x72}, {0xe00c, 0x75}, {0xe00d, 0x76}, {0xe00e, 0x74},
+ {0xe00f, 0x8B}, {0xe010, 0x80}, {0xe011, 0x2B}, {0xe012, 0x3A},
+ {0xe013, 0x5D}, {0xe014, 0x29}, {0xe015, 0x3A}, {0xe016, 0x3C},
+ {0xe017, 0x38}, {0xe018, 0x3A}, {0xe019, 0x2A}, {0xe01a, 0x2B},
+ {0xe01b, 0x3F}, {0xe01c, 0x9F}, {0xe01d, 0x80}, {0xe01e, 0x8B},
+ {0xe023, 0x4A}, {0xe025, 0xF0}, {0xe026, 0xF2}, {0xe027, 0xEF},
+ {0xe028, 0xF1}, {0xe029, 0x52}, {0xe02a, 0x29}, {0xe02b, 0xE0},
+ {0xe02c, 0xE2}, {0xe02d, 0xDF}, {0xe02e, 0xE1}, {0xe02f, 0xAC},
+ {0xe030, 0xAD}, {0xe031, 0xAE}, {0xe032, 0x7C}, {0xe033, 0x43},
+ {0xe034, 0x4D}, {0xe0aa, 0x71}, {0xe422, 0x44}
+};
+
+ExtraTable const aSymbolExtraTab2[] =
+{
+ {0x0020, 0x20}, {0x00A0, 0x20}, {0x0021, 0x21}, {0x2200, 0x22},
+ {0x0023, 0x23}, {0x2203, 0x24}, {0x0025, 0x25}, {0x0026, 0x26},
+ {0x220B, 0x27}, {0x0028, 0x28}, {0x0029, 0x29}, {0x2217, 0x2A},
+ {0x002B, 0x2B}, {0x002C, 0x2C}, {0x2212, 0x2D}, {0x002E, 0x2E},
+ {0x002F, 0x2F}, {0x003A, 0x3A}, {0x003B, 0x3B}, {0x003C, 0x3C},
+ {0x003D, 0x3D}, {0x003E, 0x3E}, {0x2245, 0x40}, {0x2206, 0x44},
+ {0x2126, 0x57}, {0x005B, 0x5B}, {0x005D, 0x5D}, {0x22A5, 0x5E},
+ {0x03C6, 0x66}, {0x03D5, 0x6A}, {0x00B5, 0x6D}, {0x007B, 0x7B},
+ {0x007C, 0x7C}, {0x007D, 0x7D}, {0x223C, 0x7E}, {0x20AC, 0xA0},
+ {0x2032, 0xA2}, {0x2264, 0xA3}, {0x2044, 0xA4}, {0x221E, 0xA5},
+ {0x0192, 0xA6}, {0x2663, 0xA7}, {0x2665, 0xA9}, {0x2660, 0xAA},
+ {0x2194, 0xAB}, {0x2190, 0xAC}, {0x2191, 0xAD}, {0x2192, 0xAE},
+ {0x2193, 0xAF}, {0x00B0, 0xB0}, {0x00B1, 0xB1}, {0x2265, 0xB3},
+ {0x00D7, 0xB4}, {0x221D, 0xB5}, {0x2202, 0xB6}, {0x2022, 0xB7},
+ {0x00F7, 0xB8}, {0x2260, 0xB9}, {0x2261, 0xBA}, {0x2248, 0xBB},
+ {0x2026, 0xBC}, {0x21B5, 0xBF}, {0x2135, 0xC0}, {0x2111, 0xC1},
+ {0x211C, 0xC2}, {0x2118, 0xC3}, {0x2297, 0xC4}, {0x2295, 0xC5},
+ {0x2205, 0xC6}, {0x2229, 0xC7}, {0x222A, 0xC8}, {0x2283, 0xC9},
+ {0x2287, 0xCA}, {0x2284, 0xCB}, {0x2282, 0xCC}, {0x2286, 0xCD},
+ {0x2208, 0xCE}, {0x2209, 0xCF}, {0x2207, 0xD1}, {0x220F, 0xD5},
+ {0x221A, 0xD6}, {0x22C5, 0xD7}, {0x00AC, 0xD8}, {0x2227, 0xD9},
+ {0x2228, 0xDA}, {0x21D4, 0xDB}, {0x21D0, 0xDC}, {0x21D2, 0xDE},
+ {0x2329, 0xE1}, {0x2211, 0xE5}, {0x232A, 0xF1}, {0x222B, 0xF2},
+ {0x2320, 0xF3}, {0x2321, 0xF5}, {0x2013, 0x2D}
+};
+
+ExtraTable const aSymbolExtraTab[] =
+{
+ {0xe021, 0xD3}, {0xe024, 0xD2}, {0xe035, 0x20}, {0xe036, 0x28},
+ {0xe037, 0x29}, {0xe039, 0x20}, {0xe083, 0x2B}, {0xe084, 0x3C},
+ {0xe085, 0x3E}, {0xe086, 0xA3}, {0xe087, 0xB3}, {0xe089, 0xCE},
+ {0xe08a, 0xA6}, {0xe08c, 0xAE}, {0xe08d, 0xD6}, {0xe08e, 0xD6},
+ {0xe08f, 0xD6}, {0xe094, 0xA2}, {0xe09e, 0x28}, {0xe09f, 0x29},
+ {0xe0a0, 0xD0}, {0xe0a6, 0xA2}, {0xe0a7, 0x7C}, {0xe0a8, 0x2F},
+ {0xe0ab, 0x7C}, {0xe0ac, 0x47}, {0xe0ad, 0x44}, {0xe0ae, 0x51},
+ {0xe0af, 0x4C}, {0xe0b0, 0x58}, {0xe0b1, 0x50}, {0xe0b2, 0x53},
+ {0xe0b3, 0x55}, {0xe0b4, 0x46}, {0xe0b5, 0x59}, {0xe0b6, 0x57},
+ {0xe0b7, 0x61}, {0xe0b8, 0x62}, {0xe0b9, 0x67}, {0xe0ba, 0x64},
+ {0xe0bb, 0x65}, {0xe0bc, 0x7A}, {0xe0bd, 0x68}, {0xe0be, 0x71},
+ {0xe0bf, 0x69}, {0xe0c0, 0x6B}, {0xe0c1, 0x6C}, {0xe0c2, 0x6D},
+ {0xe0c3, 0x6E}, {0xe0c4, 0x78}, {0xe0c5, 0x6F}, {0xe0c6, 0x70},
+ {0xe0c7, 0x72}, {0xe0c8, 0x73}, {0xe0c9, 0x74}, {0xe0ca, 0x75},
+ {0xe0cb, 0x66}, {0xe0cc, 0x63}, {0xe0cd, 0x79}, {0xe0ce, 0x77},
+ {0xe0cf, 0x65}, {0xe0d0, 0x4A}, {0xe0d1, 0x76}, {0xe0d3, 0x56},
+ {0xe0d4, 0x6A}, {0xe0d5, 0xB6}, {0xe0d6, 0x69}, {0xe0db, 0xAC},
+ {0xe0dc, 0xAD}, {0xe0dd, 0xAF}
+};
+
+ExtraTable const aTNRExtraTab[] =
+{
+ {0xe021, 0xA9},
+ {0xe022, 0x40},
+ {0xe024, 0xAE},
+ {0xe035, 0x20},
+ {0xe036, '('},
+ {0xe037, ')'},
+ {0xe039, 0x20},
+ {0xe03a, 0x80},
+ {0xe080, 0x89},
+ {0xe083, '+'},
+ {0xe084, '<'},
+ {0xe085, '>'},
+ {0xe0a9, '\\'}
+};
+
+StarSymbolToMSMultiFontImpl::StarSymbolToMSMultiFontImpl()
+{
+ struct ConvertTable
+ {
+ enum SymbolFont meFont;
+ const sal_Unicode* pTab;
+ };
+
+ //In order of preference
+ static const ConvertTable aConservativeTable[] =
+ {
+ {Symbol, aAdobeSymbolTab},
+ {Wingdings, aWingDingsTab},
+ {MonotypeSorts, aMonotypeSortsTab},
+ {Webdings, aWebDingsTab},
+ {Wingdings2, aWingDings2Tab},
+ {Wingdings3, aWingDings3Tab},
+ {MTExtra, aMTExtraTab}
+ };
+
+ struct ExtendedConvertTable
+ {
+ enum SymbolFont meFont;
+ const ExtraTable *mpTable;
+ size_t mnSize;
+ ExtendedConvertTable(SymbolFont eFont, const ExtraTable *pTable,
+ size_t nSize)
+ : meFont(eFont), mpTable(pTable), mnSize(nSize) {}
+ };
+
+ //Reverse map from a given starsymbol char to exact matches in ms symbol
+ //fonts.
+ for (auto const & r: aConservativeTable)
+ {
+ SymbolEntry aEntry;
+ aEntry.eFont = r.meFont;
+ for (aEntry.cIndex = 0xFF; aEntry.cIndex >= 0x20; --aEntry.cIndex)
+ {
+ if (sal_Unicode cChar = r.pTab[aEntry.cIndex-0x20])
+ maMagicMap.emplace(cChar, aEntry);
+ }
+ }
+
+ //In order of preference
+ static const ExtendedConvertTable aAggressiveTable[] =
+ {
+ ExtendedConvertTable(Symbol, aSymbolExtraTab2,
+ sizeof(aSymbolExtraTab2)),
+ ExtendedConvertTable(Symbol, aSymbolExtraTab,
+ sizeof(aSymbolExtraTab)),
+ ExtendedConvertTable(Wingdings, aWingDingsExtraTab,
+ sizeof(aWingDingsExtraTab)),
+ ExtendedConvertTable(TimesNewRoman, aTNRExtraTab,
+ sizeof(aTNRExtraTab))
+ };
+
+ //Allow extra conversions that are not perfect, but "good enough"
+ for (auto const & r: aAggressiveTable)
+ {
+ SymbolEntry aEntry;
+ aEntry.eFont = r.meFont;
+ for (int j = r.mnSize / sizeof(r.mpTable[0]) - 1; j >=0; --j)
+ {
+ aEntry.cIndex = r.mpTable[j].cMS;
+ maMagicMap.emplace(r.mpTable[j].cStar, aEntry);
+ }
+ }
+}
+
+static const char *SymbolFontToString(int nResult)
+{
+ const char * const *ppName = aSymbolNames;
+ int nI = Symbol;
+ while (nI <= nResult)
+ {
+ if (!(nI & nResult))
+ nI = nI << 1;
+ else
+ break;
+ ppName++;
+ }
+ return *ppName;
+}
+
+OUString StarSymbolToMSMultiFontImpl::ConvertChar(sal_Unicode &rChar)
+{
+ OUString sRet;
+
+ ::std::multimap<sal_Unicode, SymbolEntry>::const_iterator aResult =
+ maMagicMap.find(rChar);
+
+ if (aResult != maMagicMap.end())
+ {
+ const SymbolEntry &rEntry = (*aResult).second;
+ const char* pc = SymbolFontToString(rEntry.eFont);
+ sRet = OUString(pc, strlen(pc), RTL_TEXTENCODING_ASCII_US);
+ rChar = rEntry.cIndex;
+ }
+
+ return sRet;
+}
+
+StarSymbolToMSMultiFont *CreateStarSymbolToMSMultiFont()
+{
+ return new StarSymbolToMSMultiFontImpl;
+}
+
+sal_Unicode ConvertChar::RecodeChar( sal_Unicode cChar ) const
+{
+ sal_Unicode cRetVal = 0;
+ if( mpCvtFunc )
+ {
+ // use a conversion function for recoding
+ cRetVal = mpCvtFunc( cChar );
+ }
+ else
+ {
+ // use a conversion table for recoding
+ sal_Unicode cIndex = cChar;
+ // allow symbol aliasing
+ if( cIndex & 0xFF00 )
+ cIndex -= 0xF000;
+ // recode the symbol
+ if( cIndex>=0x0020 && cIndex<=0x00FF )
+ {
+ cRetVal = mpCvtTab[ cIndex - 0x0020 ];
+
+ if (!cRetVal && mpSubsFontName)
+ {
+ if ( IsOpenSymbol( OUString::createFromAscii(mpSubsFontName) ) )
+ {
+ cRetVal = 0xE12C;
+ SAL_WARN( "unotools.misc", "Forcing a bullet substitution from 0x" <<
+ OString::number(cChar, 16) << " to 0x" <<
+ OString::number(cRetVal, 16));
+ }
+ }
+ }
+ }
+
+ return cRetVal ? cRetVal : cChar;
+}
+
+// recode the string assuming the character codes are symbol codes
+// from a traditional symbol font (i.e. U+F020..U+F0FF)
+void ConvertChar::RecodeString( OUString& rStr, sal_Int32 nIndex, sal_Int32 nLen ) const
+{
+ sal_Int32 nLastIndex = nIndex + nLen;
+ OUStringBuffer aTmpStr(rStr);
+
+ if( nLastIndex > aTmpStr.getLength() )
+ nLastIndex = aTmpStr.getLength();
+
+ for(; nIndex < nLastIndex; ++nIndex )
+ {
+ sal_Unicode cOrig = rStr[ nIndex ];
+ // only recode symbols and their U+00xx aliases
+ if( ((cOrig < 0x0020) || (cOrig > 0x00FF))
+ && ((cOrig < 0xF020) || (cOrig > 0xF0FF)) )
+ continue;
+
+ // recode a symbol
+ sal_Unicode cNew = RecodeChar( cOrig );
+ if( cOrig != cNew )
+ aTmpStr[ nIndex ] = cNew;
+ }
+ rStr = aTmpStr.makeStringAndClear();
+}
+
+namespace {
+
+struct RecodeTable { const char* pOrgName; ConvertChar aCvt;};
+
+}
+
+const RecodeTable aStarSymbolRecodeTable[] =
+{
+ // the first two entries must be StarMath and StarBats; do not reorder!
+ // reason: see CreateFontToSubsFontConverter method
+ {"starbats", {aStarBatsTab, "StarSymbol", nullptr}},
+ {"starmath", {aStarMathTab, "StarSymbol", nullptr}},
+
+ {"symbol", {aAdobeSymbolTab, "StarSymbol", nullptr}},
+ {"standardsymbols", {aAdobeSymbolTab, "StarSymbol", nullptr}},
+ {"standardsymbolsl",{aAdobeSymbolTab, "StarSymbol", nullptr}},
+
+ {"monotypesorts", {aMonotypeSortsTab, "StarSymbol", nullptr}},
+// {"monotypesorts2", {aMonotypeSorts2Tab, "StarSymbol", NULL}}
+ {"zapfdingbats", {aMonotypeSortsTab, "StarSymbol", nullptr}}, //ZapfDingbats=MonotypeSorts-X?
+ {"itczapfdingbats", {aMonotypeSortsTab, "StarSymbol", nullptr}},
+ {"dingbats", {aMonotypeSortsTab, "StarSymbol", nullptr}},
+
+ {"webdings", {aWebDingsTab, "StarSymbol", nullptr}},
+ {"wingdings", {aWingDingsTab, "StarSymbol", nullptr}},
+ {"wingdings2", {aWingDings2Tab, "StarSymbol", nullptr}},
+ {"wingdings3", {aWingDings3Tab, "StarSymbol", nullptr}},
+ {"mtextra", {aMTExtraTab, "StarSymbol", nullptr}}
+};
+
+const RecodeTable aAppleSymbolRecodeTable[] = {
+ {"symbol", {aAdobeSymbolToAppleSymbolTab, "AppleSymbol", nullptr}}
+};
+
+static ConvertChar aImplStarSymbolCvt = { nullptr, "StarBats", ImplStarSymbolToStarBats };
+
+const ConvertChar* ConvertChar::GetRecodeData( std::u16string_view rOrgFontName, std::u16string_view rMapFontName )
+{
+ const ConvertChar* pCvt = nullptr;
+
+ // clean up and lowercase font name
+ OUString aOrgName( GetEnglishSearchFontName( rOrgFontName ) );
+ OUString aMapName( GetEnglishSearchFontName( rMapFontName ) );
+
+ if( aMapName == "starsymbol"
+ || aMapName == "opensymbol" )
+ {
+ for (auto const & r: aStarSymbolRecodeTable)
+ {
+ if( aOrgName.equalsAscii( r.pOrgName ) )
+ {
+ pCvt = &r.aCvt;
+ break;
+ }
+ }
+ }
+ //It's plausible that it's better to implement this
+ //as an additional encoding alongside the existing
+ //adobe-symbol to unicode conversion in rtl instead
+ else if( aMapName == "applesymbol" )
+ {
+ for (auto const & r: aAppleSymbolRecodeTable)
+ {
+ if( aOrgName.equalsAscii( r.pOrgName ) )
+ {
+ pCvt = &r.aCvt;
+ break;
+ }
+ }
+ }
+ else if( aMapName == "starbats" )
+ {
+ if( aOrgName == "starsymbol" )
+ pCvt = &aImplStarSymbolCvt;
+ else if( aOrgName == "opensymbol" )
+ pCvt = &aImplStarSymbolCvt;
+ }
+
+ return pCvt;
+}
+
+FontToSubsFontConverter CreateFontToSubsFontConverter( std::u16string_view rOrgName, FontToSubsFontFlags nFlags )
+{
+ const ConvertChar* pCvt = nullptr;
+
+ OUString aName = GetEnglishSearchFontName( rOrgName );
+
+ if ( nFlags == FontToSubsFontFlags::IMPORT )
+ {
+ const std::size_t nEntries = 2; // only StarMath+StarBats
+ for( std::size_t i = 0; i < nEntries; ++i )
+ {
+ const RecodeTable& r = aStarSymbolRecodeTable[i];
+ if( aName.equalsAscii( r.pOrgName ) )
+ {
+ pCvt = &r.aCvt;
+ break;
+ }
+ }
+ }
+ else
+ {
+ // TODO: only StarMath+StarBats
+ if( aName == "starsymbol" )
+ pCvt = &aImplStarSymbolCvt;
+ else if( aName == "opensymbol" )
+ pCvt = &aImplStarSymbolCvt;
+ }
+
+ return const_cast<ConvertChar *>(pCvt);
+}
+
+sal_Unicode ConvertFontToSubsFontChar(
+ FontToSubsFontConverter hConverter, sal_Unicode cChar )
+{
+ if ( hConverter )
+ return static_cast<ConvertChar*>(hConverter)->RecodeChar( cChar );
+ else
+ return cChar;
+}
+
+OUString GetFontToSubsFontName( FontToSubsFontConverter hConverter )
+{
+ if ( !hConverter )
+ return OUString();
+
+ const char* pName = static_cast<ConvertChar*>(hConverter)->mpSubsFontName;
+ return OUString::createFromAscii( pName );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/misc/fontdefs.cxx b/unotools/source/misc/fontdefs.cxx
new file mode 100644
index 0000000000..43b9e05543
--- /dev/null
+++ b/unotools/source/misc/fontdefs.cxx
@@ -0,0 +1,417 @@
+/* -*- 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/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <unotools/fontdefs.hxx>
+#include <unotools/fontcfg.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <string_view>
+#include <unordered_map>
+
+OUString StripScriptFromName(const OUString& _aName)
+{
+ // I worry that someone will have a font which *does* have
+ // e.g. "Greek" legitimately at the end of its name :-(
+ const char*const suffixes[] = { " baltic",
+ " ce",
+ " cyr",
+ " greek",
+ " tur",
+ " (arabic)",
+ " (hebrew)",
+ " (thai)",
+ " (vietnamese)"
+ };
+
+ OUString aName = _aName;
+ // These can be crazily piled up, e.g. Times New Roman CYR Greek
+ bool bFinished = false;
+ while (!bFinished)
+ {
+ bFinished = true;
+ for (const char* suffix : suffixes)
+ {
+ size_t nLen = strlen(suffix);
+ if (aName.endsWithIgnoreAsciiCaseAsciiL(suffix, nLen))
+ {
+ bFinished = false;
+ aName = aName.copy(0, aName.getLength() - nLen);
+ }
+ }
+ }
+ return aName;
+}
+
+//return true if the character is stripped from the string
+static bool toOnlyLowerAsciiOrStrip(sal_Unicode c, OUStringBuffer &rName, sal_Int32 nIndex, sal_Int32& rLen)
+{
+ // not lowercase Ascii
+ if (c < 'a' || c > 'z')
+ {
+ // To Lowercase-Ascii
+ if ( (c >= 'A') && (c <= 'Z') )
+ {
+ c += 'a' - 'A';
+ rName[nIndex] = c;
+ }
+ else if( ((c < '0') || (c > '9')) && (c != ';') && (c != '(') && (c != ')') ) // not 0-9, semicolon, or brackets
+ {
+ // Remove white spaces and special characters
+ rName.remove(nIndex, 1);
+ rLen--;
+ return true;
+ }
+ }
+ return false;
+}
+
+OUString GetEnglishSearchFontName(std::u16string_view rInName)
+{
+ OUStringBuffer rName(rInName);
+ bool bNeedTranslation = false;
+ sal_Int32 nLen = rName.getLength();
+
+ // Remove trailing whitespaces
+ sal_Int32 i = nLen;
+ while ( i && (rName[ i-1 ] < 32) )
+ i--;
+ if ( i != nLen )
+ rName.truncate(i);
+
+ nLen = rName.getLength();
+
+ // remove all whitespaces and converts to lower case ASCII
+ // TODO: better transliteration to ASCII e.g. all digits
+ i = 0;
+ while ( i < nLen )
+ {
+ sal_Unicode c = rName[ i ];
+ if ( c > 127 )
+ {
+ // Translate to Lowercase-ASCII
+ // FullWidth-ASCII to half ASCII
+ if ( (c >= 0xFF00) && (c <= 0xFF5E) )
+ {
+ c -= 0xFF00-0x0020;
+ rName[ i ] = c;
+ if (toOnlyLowerAsciiOrStrip(c, rName, i, nLen))
+ continue;
+ }
+ else
+ {
+ // Only Fontnames with None-Ascii-Characters must be translated
+ bNeedTranslation = true;
+ }
+ }
+ else if (toOnlyLowerAsciiOrStrip(c, rName, i, nLen))
+ continue;
+
+ i++;
+ }
+ OUString rNameStr = rName.makeStringAndClear();
+ // translate normalized localized name to its normalized English ASCII name
+ if( bNeedTranslation )
+ {
+ typedef std::unordered_map<OUString, OUString> FontNameDictionary;
+ static FontNameDictionary const aDictionary = {
+ {u"\uBC14\uD0D5"_ustr, "batang"},
+ {u"\uBC14\uD0D5\uCCB4"_ustr, "batangche"},
+ {u"\uAD81\uC11C"_ustr, "gungshu"},
+ {u"\uAD81\uC11C\uCCB4"_ustr, "gungshuche"},
+ {u"\uAD74\uB9BC"_ustr, "gulim"},
+ {u"\uAD74\uB9BC\uCCB4"_ustr, "gulimche"},
+ {u"\uB3CB\uC6C0"_ustr, "dotum"},
+ {u"\uB3CB\uC6C0\uCCB4"_ustr, "dotumche"},
+ {u"\u5B8B\u4F53"_ustr, "simsun"},
+ {u"\u65B0\u5B8B\u4F53"_ustr, "nsimsun"},
+ {u"\u9ED1\u4F53"_ustr, "simhei"},
+ {u"\u6977\u4F53"_ustr, "simkai"},
+ {u"\u4E2D\u6613\u5B8B\u4F53"_ustr, "zycjksun"},
+ {u"\u4E2D\u6613\u9ED1\u4F53"_ustr, "zycjkhei"},
+ {u"\u4E2D\u6613\u6977\u4F53"_ustr, "zycjkkai"},
+ {u"\u65B9\u6B63\u9ED1\u4F53"_ustr, "fzhei"},
+ {u"\u65B9\u6B63\u6977\u4F53"_ustr, "fzkai"},
+ {u"\u65B9\u6B63\u5B8B\u4E00"_ustr, "fzsong"},
+ {u"\u65B9\u6B63\u4E66\u5B8B"_ustr, "fzshusong"},
+ {u"\u65B9\u6B63\u4EFF\u5B8B"_ustr, "fzfangsong"},
+ // Attention: this fonts includes the wrong encoding vector - so we double the names with correct and wrong encoding
+ // First one is the GB-Encoding (we think the correct one), second is the big5 encoded name
+ {u"m\u7B80\u9ED1"_ustr, "mhei"},
+ {u"m\u6F60\u7AAA"_ustr, "mhei"},
+ {u"m\u7B80\u6977\u566C"_ustr, "mkai"},
+ {u"m\u6F60\u7FF1\u628E"_ustr, "mkai"},
+ {u"m\u7B80\u5B8B"_ustr, "msong"},
+ {u"m\u6F60\u51BC"_ustr, "msong"},
+ {u"m\u7B80\u592B\u5B8B"_ustr, "cfangsong"},
+ {u"m\u6F60\u6E98\u51BC"_ustr, "cfangsong"},
+ {u"\u7D30\u660E\u9AD4"_ustr, "mingliu"},
+ {u"\u65B0\u7D30\u660E\u9AD4"_ustr, "pmingliu"},
+ {u"\u6865"_ustr, "hei"},
+ {u"\u6B61"_ustr, "kai"},
+ {u"\u6D69\u6E67"_ustr, "ming"},
+ {u"ms\u30B4\u30B7\u30C3\u30AF"_ustr, "msgothic"},
+ {u"msp\u30B4\u30B7\u30C3\u30AF"_ustr, "mspgothic"},
+ {u"ms\u660E\u671D"_ustr, "msmincho"},
+ {u"msp\u660E\u671D"_ustr, "mspmincho"},
+ {u"\u5FAE\u8EDF\u6B63\u9ED1\u9AD4"_ustr, "microsoftjhenghei"},
+ {u"\u5FAE\u8F6F\u96C5\u9ED1"_ustr, "microsoftyahei"},
+ {u"\u30e1\u30a4\u30ea\u30aa"_ustr, "meiryo"},
+ {u"hg\u660E\u671Dl"_ustr, "hgminchol"},
+ {u"hg\u30B4\u30B7\u30C3\u30AFb"_ustr, "hggothicb"},
+ {u"hgp\u660E\u671Dl"_ustr, "hgpminchol"},
+ {u"hgp\u30B4\u30B7\u30C3\u30AFb"_ustr, "hgpgothicb"},
+ {u"hg\u660E\u671Dlsun"_ustr, "hgmincholsun"},
+ {u"hg\u30B4\u30B7\u30C3\u30AFbsun"_ustr, "hggothicbsun"},
+ {u"hgp\u660E\u671Dlsun"_ustr, "hgpmincholsun"},
+ {u"hgp\u30B4\u30B7\u30C3\u30AFbsun"_ustr, "hgpgothicbsun"},
+ {u"hg\u5E73\u6210\u660E\u671D\u4F53"_ustr, "hgheiseimin"},
+ {u"hg\u5E73\u6210\u660E\u671D\u4F53w3x12"_ustr, "hgheiseimin"},
+ {u"ipa\u660E\u671D"_ustr, "ipamincho"},
+ {u"ipap\u660E\u671D"_ustr, "ipapmincho"},
+ {u"ipa\u30B4\u30B7\u30C3\u30AF"_ustr, "ipagothic"},
+ {u"ipap\u30B4\u30B7\u30C3\u30AF"_ustr, "ipapgothic"},
+ {u"ipaui\u30B4\u30B7\u30C3\u30AF"_ustr, "ipauigothic"},
+ {u"takao\u660E\u671D"_ustr, "takaomincho"},
+ {u"takaop\u660E\u671D"_ustr, "takaopmincho"},
+ {u"takao\u30B4\u30B7\u30C3\u30AF"_ustr, "takaogothic"},
+ {u"takaop\u30B4\u30B7\u30C3\u30AF"_ustr, "takaopgothic"},
+ {u"\u3055\u3056\u306A\u307F\u660E\u671D"_ustr, "sazanamimincho"},
+ {u"\u3055\u3056\u306A\u307F\u30B4\u30B7\u30C3\u30AF"_ustr, "sazanamigothic"},
+ {u"\u6771\u98A8\u660E\u671D"_ustr, "kochimincho"},
+ {u"\u6771\u98A8\u30B4\u30B7\u30C3\u30AF"_ustr, "kochigothic"},
+ {u"\uC36C\uB3CB\uC6C0"_ustr, "sundotum"},
+ {u"\uC36C\uAD74\uB9BC"_ustr, "sungulim"},
+ {u"\uC36C\uBC14\uD0D5"_ustr, "sunbatang"},
+ {u"\uBC31\uBB35\uB3CB\uC6C0"_ustr, "baekmukdotum"},
+ {u"\uBC31\uBB35\uAD74\uB9BC"_ustr, "baekmukgulim"},
+ {u"\uBC31\uBB35\uBC14\uD0D5"_ustr, "baekmukbatang"},
+ {u"\u65B9\u6B63\u9ED1\u4F53"_ustr, "fzheiti"},
+ {u"\u65B9\u6B63\u9ED1\u9AD4"_ustr, "fzheiti"},
+ {u"\u65B9\u6B63\u6977\u4F53"_ustr, "fzkaiti"},
+ {u"\u65B9\u6B63\u6977\u9AD4"_ustr, "fzkaitib"},
+ {u"\u65B9\u6B63\u660E\u9AD4"_ustr, "fzmingtib"},
+ {u"\u65B9\u6B63\u5B8B\u4F53"_ustr, "fzsongti"},
+ {u"hy\uACAC\uBA85\uC870"_ustr, "hymyeongjoextra"},
+ {u"hy\uC2E0\uBA85\uC870"_ustr, "hysinmyeongjomedium"},
+ {u"hy\uC911\uACE0\uB515"_ustr, "hygothicmedium"},
+ {u"hy\uADF8\uB798\uD53Dm"_ustr, "hygraphicmedium"},
+ {u"hy\uADF8\uB798\uD53D"_ustr, "hygraphic"},
+ {u"\uC0C8\uAD74\uB9BC"_ustr, "newgulim"},
+ {u"\uC36C\uAD81\uC11C"_ustr, "sungungseo"},
+ {u"hy\uAD81\uC11Cb"_ustr, "hygungsobold"},
+ {u"hy\uAD81\uC11C"_ustr, "hygungso"},
+ {u"\uC36C\uD5E4\uB4DC\uB77C\uC778"_ustr, "sunheadline"},
+ {u"hy\uD5E4\uB4DC\uB77C\uC778m"_ustr, "hyheadlinemedium"},
+ {u"hy\uD5E4\uB4DC\uB77C\uC778"_ustr, "hyheadline"},
+ {u"\uD734\uBA3C\uC61B\uCCB4"_ustr, "yetr"},
+ {u"hy\uACAC\uACE0\uB515"_ustr, "hygothicextra"},
+ {u"\uC36C\uBAA9\uD310"_ustr, "sunmokpan"},
+ {u"\uC36C\uC5FD\uC11C"_ustr, "sunyeopseo"},
+ {u"\uC36C\uBC31\uC1A1"_ustr, "sunbaeksong"},
+ {u"hy\uC5FD\uC11Cl"_ustr, "hypostlight"},
+ {u"hy\uC5FD\uC11C"_ustr, "hypost"},
+ {u"\uD734\uBA3C\uB9E4\uC9C1\uCCB4"_ustr, "magicr"},
+ {u"\uC36C\uD06C\uB9AC\uC2A4\uD0C8"_ustr, "suncrystal"},
+ {u"\uC36C\uC0D8\uBB3C"_ustr, "sunsaemmul"},
+ {u"hy\uC595\uC740\uC0D8\uBB3Cm"_ustr, "hyshortsamulmedium"},
+ {u"hy\uC595\uC740\uC0D8\uBB3C"_ustr, "hyshortsamul"},
+ {u"\uD55C\uCEF4\uBC14\uD0D5"_ustr, "haansoftbatang"},
+ {u"\uD55C\uCEF4\uB3CB\uC6C0"_ustr, "haansoftdotum"},
+ {u"\uD55C\uC591\uD574\uC11C"_ustr, "hyhaeseo"},
+ {u"md\uC194\uCCB4"_ustr, "mdsol"},
+ {u"md\uAC1C\uC131\uCCB4"_ustr, "mdgaesung"},
+ {u"md\uC544\uD2B8\uCCB4"_ustr, "mdart"},
+ {u"md\uC544\uB871\uCCB4"_ustr, "mdalong"},
+ {u"md\uC774\uC19D\uCCB4"_ustr, "mdeasop"},
+ {u"hg\uFF7A\uFF9E\uFF7C\uFF6F\uFF78e"_ustr, "hggothice"},
+ {u"hgp\uFF7A\uFF9E\uFF7C\uFF6F\uFF78e"_ustr, "hgpgothice"},
+ {u"hgs\uFF7A\uFF9E\uFF7C\uFF6F\uFF78e"_ustr, "hgsgothice"},
+ {u"hg\uFF7A\uFF9E\uFF7C\uFF6F\uFF78m"_ustr, "hggothicm"},
+ {u"hgp\uFF7A\uFF9E\uFF7C\uFF6F\uFF78m"_ustr, "hgpgothicm"},
+ {u"hgs\uFF7A\uFF9E\uFF7C\uFF6F\uFF78m"_ustr, "hgsgothicm"},
+ {u"hg\u884C\u66F8\u4F53"_ustr, "hggyoshotai"},
+ {u"hgp\u884C\u66F8\u4F53"_ustr, "hgpgyoshotai"},
+ {u"hgs\u884C\u66F8\u4F53"_ustr, "hgsgyoshotai"},
+ {u"hg\u6559\u79D1\u66F8\u4F53"_ustr, "hgkyokashotai"},
+ {u"hgp\u6559\u79D1\u66F8\u4F53"_ustr, "hgpkyokashotai"},
+ {u"hgs\u6559\u79D1\u66F8\u4F53"_ustr, "hgskyokashotai"},
+ {u"hg\u660E\u671Db"_ustr, "hgminchob"},
+ {u"hgp\u660E\u671Db"_ustr, "hgpminchob"},
+ {u"hgs\u660E\u671Db"_ustr, "hgsminchob"},
+ {u"hg\u660E\u671De"_ustr, "hgminchoe"},
+ {u"hgp\u660E\u671De"_ustr, "hgpminchoe"},
+ {u"hgs\u660E\u671De"_ustr, "hgsminchoe"},
+ {u"hg\u5275\u82F1\u89D2\uFF8E\uFF9F\uFF6F\uFF8C\uFF9F\u4F53"_ustr, "hgsoeikakupoptai"},
+ {u"hgp\u5275\u82F1\u89D2\uFF8E\uFF9F\uFF6F\uFF8C\uFF9F\u4F53"_ustr, "hgpsoeikakupopta"},
+ {u"hgs\u5275\u82F1\u89D2\uFF8E\uFF9F\uFF6F\uFF8C\uFF9F\u4F53"_ustr, "hgssoeikakupopta"},
+ {u"hg\u5275\u82F1\uFF8C\uFF9F\uFF9A\uFF7E\uFF9E\uFF9D\uFF7Deb"_ustr,
+ "hgsoeipresenceeb"},
+ {u"hgp\u5275\u82F1\uFF8C\uFF9F\uFF9A\uFF7E\uFF9E\uFF9D\uFF7Deb"_ustr,
+ "hgpsoeipresenceeb"},
+ {u"hgs\u5275\u82F1\uFF8C\uFF9F\uFF9A\uFF7E\uFF9E\uFF9D\uFF7Deb"_ustr,
+ "hgssoeipresenceeb"},
+ {u"hg\u5275\u82F1\u89D2\uFF7A\uFF9E\uFF7C\uFF6F\uFF78ub"_ustr, "hgsoeikakugothicub"},
+ {u"hgp\u5275\u82F1\u89D2\uFF7A\uFF9E\uFF7C\uFF6F\uFF78ub"_ustr, "hgpsoeikakugothicub"},
+ {u"hgs\u5275\u82F1\u89D2\uFF7A\uFF9E\uFF7C\uFF6F\uFF78ub"_ustr, "hgssoeikakugothicub"},
+ {u"hg\u6B63\u6977\u66F8\u4F53-pro"_ustr, "hgseikaishotaipro"},
+ {u"hg\u4E38\uFF7A\uFF9E\uFF7C\uFF6F\uFF78-pro"_ustr, "hgmarugothicmpro"},
+ {u"\u30D2\u30E9\u30AE\u30CE\u660E\u671Dpro"_ustr, "hiraginominchopro"},
+ {u"\u30D2\u30E9\u30AE\u30CE\u660E\u671Dpron"_ustr, "hiraginominchopron"},
+ {u"\u30D2\u30E9\u30AE\u30CE\u89D2\u30B4\u30B7\u30C3\u30AF"_ustr, "hiraginosans"},
+ {u"\u30D2\u30E9\u30AE\u30CE\u89D2\u30B4pro"_ustr, "hiraginokakugothicpro"},
+ {u"\u30D2\u30E9\u30AE\u30CE\u89D2\u30B4pron"_ustr, "hiraginokakugothicpron"},
+ {u"\u30D2\u30E9\u30AE\u30CE\u4E38\u30B4pro"_ustr, "hiraginomarugothicpro"},
+ {u"\u30D2\u30E9\u30AE\u30CE\u4E38\u30B4pron"_ustr, "hiraginomarugothicpron"},
+ {u"\u6E38\u30B4\u30B7\u30C3\u30AF"_ustr, "yugothic"},
+ {u"\u6E38\u30B4\u30B7\u30C3\u30AF\u4F53"_ustr, "yugothictai"},
+ {u"\u6E38\u660E\u671D"_ustr, "yumincho"},
+ {u"\u6E38\u660E\u671D\u4F53"_ustr, "yuminchotai"},
+ {u"\u6E90\u30CE\u89D2\u30B4\u30B7\u30C3\u30AF"_ustr, "sourcehansans"},
+ {u"\u6E90\u30CE\u89D2\u30B4\u30B7\u30C3\u30AFjp"_ustr, "sourcehansansjp"},
+ {u"\u6E90\u30CE\u89D2\u30B4\u30B7\u30C3\u30AFhw"_ustr, "sourcehansanshw"},
+ {u"\u6E90\u30CE\u660E\u671D"_ustr, "sourcehanserif"},
+ {u"\u6E90\u30CE\u660E\u671Djp"_ustr, "sourcehanserifjp"},
+ {u"ipamj\u660E\u671D"_ustr, "ipamjmincho"},
+ {u"ipaex\u30B4\u30B7\u30C3\u30AF"_ustr, "ipaexgothic"},
+ {u"ipaex\u660E\u671D"_ustr, "ipaexmimcho"}};
+
+ FontNameDictionary::const_iterator it = aDictionary.find( rNameStr );
+ if( it != aDictionary.end() )
+ rNameStr = it->second;
+ }
+
+ return rNameStr;
+}
+
+std::u16string_view GetNextFontToken( std::u16string_view rTokenStr, sal_Int32& rIndex )
+{
+ // check for valid start index
+ size_t nStringLen = rTokenStr.size();
+ if( o3tl::make_unsigned(rIndex) >= nStringLen )
+ {
+ rIndex = -1;
+ return {};
+ }
+
+ // find the next token delimiter and return the token substring
+ const sal_Unicode* pStr = rTokenStr.data() + rIndex;
+ const sal_Unicode* pEnd = rTokenStr.data() + nStringLen;
+ for(; pStr < pEnd; ++pStr )
+ if( (*pStr == ';') || (*pStr == ',') )
+ break;
+
+ sal_Int32 nTokenStart = rIndex;
+ sal_Int32 nTokenLen;
+ if( pStr < pEnd )
+ {
+ rIndex = sal::static_int_cast<sal_Int32>(pStr - rTokenStr.data());
+ nTokenLen = rIndex - nTokenStart;
+ ++rIndex; // skip over token separator
+ }
+ else
+ {
+ // no token delimiter found => handle last token
+ rIndex = -1;
+
+ // optimize if the token string consists of just one token
+ if( !nTokenStart )
+ {
+ return rTokenStr;
+ }
+ else
+ {
+ nTokenLen = nStringLen - nTokenStart;
+ }
+ }
+
+ return rTokenStr.substr( nTokenStart, nTokenLen );
+}
+
+static bool ImplIsFontToken( std::u16string_view rName, std::u16string_view rToken )
+{
+ sal_Int32 nIndex = 0;
+ do
+ {
+ std::u16string_view aTempName = GetNextFontToken( rName, nIndex );
+ if ( rToken == aTempName )
+ return true;
+ }
+ while ( nIndex != -1 );
+
+ return false;
+}
+
+static void ImplAppendFontToken( OUString& rName, std::u16string_view rNewToken )
+{
+ if ( !rName.isEmpty() )
+ {
+ rName += ";";
+ }
+ rName += rNewToken;
+}
+
+void AddTokenFontName( OUString& rName, std::u16string_view rNewToken )
+{
+ if ( !ImplIsFontToken( rName, rNewToken ) )
+ ImplAppendFontToken( rName, rNewToken );
+}
+
+OUString GetSubsFontName( std::u16string_view rName, SubsFontFlags nFlags )
+{
+ OUString aName;
+
+ sal_Int32 nIndex = 0;
+ OUString aOrgName = GetEnglishSearchFontName(
+ GetNextFontToken( rName, nIndex ) );
+
+ // #93662# do not try to replace StarSymbol with MS only font
+ if( nFlags == (SubsFontFlags::MS|SubsFontFlags::ONLYONE)
+ && ( aOrgName == "starsymbol"
+ || aOrgName == "opensymbol" ) )
+ return aName;
+
+ if (nFlags & SubsFontFlags::MS)
+ {
+ const utl::FontNameAttr* pAttr = utl::FontSubstConfiguration::get().getSubstInfo( aOrgName );
+ if (pAttr)
+ for( const auto& rSubstitution : pAttr->MSSubstitutions )
+ if( ! ImplIsFontToken( rName, rSubstitution ) )
+ {
+ ImplAppendFontToken( aName, rSubstitution );
+ if( nFlags & SubsFontFlags::ONLYONE )
+ {
+ break;
+ }
+ }
+ }
+
+ return aName;
+}
+
+bool IsOpenSymbol(std::u16string_view rFontName)
+{
+ sal_Int32 nIndex = 0;
+ std::u16string_view sFamilyNm(GetNextFontToken(rFontName, nIndex));
+ return (o3tl::equalsIgnoreAsciiCase(sFamilyNm, "starsymbol") ||
+ o3tl::equalsIgnoreAsciiCase(sFamilyNm, "opensymbol"));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/misc/mediadescriptor.cxx b/unotools/source/misc/mediadescriptor.cxx
new file mode 100644
index 0000000000..f797728f7b
--- /dev/null
+++ b/unotools/source/misc/mediadescriptor.cxx
@@ -0,0 +1,500 @@
+/* -*- 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 <comphelper/docpasswordhelper.hxx>
+#include <sal/log.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <unotools/securityoptions.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <comphelper/stillreadwriteinteraction.hxx>
+
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/io/XStream.hpp>
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <com/sun/star/uri/UriReferenceFactory.hpp>
+#include <com/sun/star/uri/XUriReference.hpp>
+#include <com/sun/star/ucb/PostCommandArgument2.hpp>
+#include <officecfg/Office/Common.hxx>
+#include <ucbhelper/content.hxx>
+#include <ucbhelper/commandenvironment.hxx>
+#include <ucbhelper/activedatasink.hxx>
+#include <comphelper/processfactory.hxx>
+#include <tools/urlobj.hxx>
+#include <osl/diagnose.h>
+#include <comphelper/diagnose_ex.hxx>
+
+namespace utl {
+
+namespace {
+
+OUString removeFragment(OUString const & uri) {
+ css::uno::Reference< css::uri::XUriReference > ref(
+ css::uri::UriReferenceFactory::create(
+ comphelper::getProcessComponentContext())->
+ parse(uri));
+ if (ref.is()) {
+ ref->clearFragment();
+ return ref->getUriReference();
+ } else {
+ SAL_WARN("unotools.misc", "cannot parse <" << uri << ">");
+ return uri;
+ }
+}
+
+}
+
+MediaDescriptor::MediaDescriptor()
+{
+}
+
+MediaDescriptor::MediaDescriptor(const css::uno::Sequence< css::beans::PropertyValue >& lSource)
+ : SequenceAsHashMap(lSource)
+{
+}
+
+bool MediaDescriptor::isStreamReadOnly() const
+{
+ bool bReadOnly = false;
+
+ // check for explicit readonly state
+ const_iterator pIt = find(MediaDescriptor::PROP_READONLY);
+ if (pIt != end())
+ {
+ pIt->second >>= bReadOnly;
+ return bReadOnly;
+ }
+
+ // streams based on post data are readonly by definition
+ pIt = find(MediaDescriptor::PROP_POSTDATA);
+ if (pIt != end())
+ return true;
+
+ // A XStream capsulate XInputStream and XOutputStream ...
+ // If it exists - the file must be open in read/write mode!
+ pIt = find(MediaDescriptor::PROP_STREAM);
+ if (pIt != end())
+ return false;
+
+ // Only file system content provider is able to provide XStream
+ // so for this content impossibility to create XStream triggers
+ // switch to readonly mode.
+ try
+ {
+ css::uno::Reference< css::ucb::XContent > xContent = getUnpackedValueOrDefault(MediaDescriptor::PROP_UCBCONTENT, css::uno::Reference< css::ucb::XContent >());
+ if (xContent.is())
+ {
+ css::uno::Reference< css::ucb::XContentIdentifier > xId = xContent->getIdentifier();
+ OUString aScheme;
+ if (xId.is())
+ aScheme = xId->getContentProviderScheme();
+
+ if (aScheme.equalsIgnoreAsciiCase("file"))
+ bReadOnly = true;
+ else
+ {
+ ::ucbhelper::Content aContent(xContent,
+ utl::UCBContentHelper::getDefaultCommandEnvironment(),
+ comphelper::getProcessComponentContext());
+ aContent.getPropertyValue("IsReadOnly") >>= bReadOnly;
+ }
+ }
+ }
+ catch(const css::uno::RuntimeException& )
+ { throw; }
+ catch(const css::uno::Exception&)
+ {}
+
+ return bReadOnly;
+}
+
+css::uno::Any MediaDescriptor::getComponentDataEntry( const OUString& rName ) const
+{
+ comphelper::SequenceAsHashMap::const_iterator aPropertyIter = find( PROP_COMPONENTDATA );
+ if( aPropertyIter != end() )
+ return comphelper::NamedValueCollection( aPropertyIter->second ).get( rName );
+ return css::uno::Any();
+}
+
+void MediaDescriptor::setComponentDataEntry( const OUString& rName, const css::uno::Any& rValue )
+{
+ if( rValue.hasValue() )
+ {
+ // get or create the 'ComponentData' property entry
+ css::uno::Any& rCompDataAny = operator[]( PROP_COMPONENTDATA );
+ // insert the value (retain sequence type, create NamedValue elements by default)
+ bool bHasNamedValues = !rCompDataAny.hasValue() || rCompDataAny.has< css::uno::Sequence< css::beans::NamedValue > >();
+ bool bHasPropValues = rCompDataAny.has< css::uno::Sequence< css::beans::PropertyValue > >();
+ OSL_ENSURE( bHasNamedValues || bHasPropValues, "MediaDescriptor::setComponentDataEntry - incompatible 'ComponentData' property in media descriptor" );
+ if( bHasNamedValues || bHasPropValues )
+ {
+ // insert or overwrite the passed value
+ comphelper::SequenceAsHashMap aCompDataMap( rCompDataAny );
+ aCompDataMap[ rName ] = rValue;
+ // write back the sequence (restore sequence with correct element type)
+ rCompDataAny = aCompDataMap.getAsConstAny( bHasPropValues );
+ }
+ }
+ else
+ {
+ // if an empty Any is passed, clear the entry
+ clearComponentDataEntry( rName );
+ }
+}
+
+void MediaDescriptor::clearComponentDataEntry( const OUString& rName )
+{
+ comphelper::SequenceAsHashMap::iterator aPropertyIter = find( PROP_COMPONENTDATA );
+ if( aPropertyIter == end() )
+ return;
+
+ css::uno::Any& rCompDataAny = aPropertyIter->second;
+ bool bHasNamedValues = rCompDataAny.has< css::uno::Sequence< css::beans::NamedValue > >();
+ bool bHasPropValues = rCompDataAny.has< css::uno::Sequence< css::beans::PropertyValue > >();
+ OSL_ENSURE( bHasNamedValues || bHasPropValues, "MediaDescriptor::clearComponentDataEntry - incompatible 'ComponentData' property in media descriptor" );
+ if( bHasNamedValues || bHasPropValues )
+ {
+ // remove the value with the passed name
+ comphelper::SequenceAsHashMap aCompDataMap( rCompDataAny );
+ aCompDataMap.erase( rName );
+ // write back the sequence, or remove it completely if it is empty
+ if( aCompDataMap.empty() )
+ erase( aPropertyIter );
+ else
+ rCompDataAny = aCompDataMap.getAsConstAny( bHasPropValues );
+ }
+}
+
+css::uno::Sequence< css::beans::NamedValue > MediaDescriptor::requestAndVerifyDocPassword(
+ comphelper::IDocPasswordVerifier& rVerifier,
+ comphelper::DocPasswordRequestType eRequestType,
+ const ::std::vector< OUString >* pDefaultPasswords )
+{
+ css::uno::Sequence< css::beans::NamedValue > aMediaEncData = getUnpackedValueOrDefault(
+ PROP_ENCRYPTIONDATA, css::uno::Sequence< css::beans::NamedValue >() );
+ OUString aMediaPassword = getUnpackedValueOrDefault(
+ PROP_PASSWORD, OUString() );
+ css::uno::Reference< css::task::XInteractionHandler > xInteractHandler = getUnpackedValueOrDefault(
+ PROP_INTERACTIONHANDLER, css::uno::Reference< css::task::XInteractionHandler >() );
+ OUString aDocumentName = getUnpackedValueOrDefault(
+ PROP_URL, OUString() );
+
+ bool bIsDefaultPassword = false;
+ css::uno::Sequence< css::beans::NamedValue > aEncryptionData = comphelper::DocPasswordHelper::requestAndVerifyDocPassword(
+ rVerifier, aMediaEncData, aMediaPassword, xInteractHandler, aDocumentName, eRequestType, pDefaultPasswords, &bIsDefaultPassword );
+
+ erase( PROP_PASSWORD );
+ erase( PROP_ENCRYPTIONDATA );
+
+ // insert encryption info into media descriptor
+ // TODO
+ if( aEncryptionData.hasElements() )
+ (*this)[ PROP_ENCRYPTIONDATA ] <<= aEncryptionData;
+
+ return aEncryptionData;
+}
+
+bool MediaDescriptor::addInputStream()
+{
+ return impl_addInputStream( true );
+}
+
+/*-----------------------------------------------*/
+bool MediaDescriptor::addInputStreamOwnLock()
+{
+ const bool bLock = !utl::ConfigManager::IsFuzzing()
+ && officecfg::Office::Common::Misc::UseDocumentSystemFileLocking::get();
+ return impl_addInputStream(bLock);
+}
+
+/*-----------------------------------------------*/
+bool MediaDescriptor::impl_addInputStream( bool bLockFile )
+{
+ // check for an already existing stream item first
+ const_iterator pIt = find(MediaDescriptor::PROP_INPUTSTREAM);
+ if (pIt != end())
+ return true;
+
+ try
+ {
+ // No stream available - create a new one
+ // a) data comes as PostData ...
+ pIt = find(MediaDescriptor::PROP_POSTDATA);
+ if (pIt != end())
+ {
+ const css::uno::Any& rPostData = pIt->second;
+ css::uno::Reference< css::io::XInputStream > xPostData;
+ rPostData >>= xPostData;
+
+ return impl_openStreamWithPostData( xPostData );
+ }
+
+ // b) ... or we must get it from the given URL
+ OUString sURL = getUnpackedValueOrDefault(MediaDescriptor::PROP_URL, OUString());
+ if (sURL.isEmpty())
+ throw css::uno::Exception("Found no URL.",
+ css::uno::Reference< css::uno::XInterface >());
+
+ return impl_openStreamWithURL( removeFragment(sURL), bLockFile );
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.misc", "invalid MediaDescriptor detected");
+ return false;
+ }
+}
+
+bool MediaDescriptor::impl_openStreamWithPostData( const css::uno::Reference< css::io::XInputStream >& _rxPostData )
+{
+ if ( !_rxPostData.is() )
+ throw css::lang::IllegalArgumentException("Found invalid PostData.",
+ css::uno::Reference< css::uno::XInterface >(), 1);
+
+ // PostData can't be used in read/write mode!
+ (*this)[MediaDescriptor::PROP_READONLY] <<= true;
+
+ // prepare the environment
+ css::uno::Reference< css::task::XInteractionHandler > xInteraction = getUnpackedValueOrDefault(
+ MediaDescriptor::PROP_INTERACTIONHANDLER,
+ css::uno::Reference< css::task::XInteractionHandler >());
+ css::uno::Reference< css::ucb::XProgressHandler > xProgress;
+ rtl::Reference<::ucbhelper::CommandEnvironment> xCommandEnv = new ::ucbhelper::CommandEnvironment(xInteraction, xProgress);
+
+ // media type
+ OUString sMediaType = getUnpackedValueOrDefault(MediaDescriptor::PROP_MEDIATYPE, OUString());
+ if (sMediaType.isEmpty())
+ {
+ sMediaType = "application/x-www-form-urlencoded";
+ (*this)[MediaDescriptor::PROP_MEDIATYPE] <<= sMediaType;
+ }
+
+ // url
+ OUString sURL( getUnpackedValueOrDefault( PROP_URL, OUString() ) );
+
+ css::uno::Reference< css::io::XInputStream > xResultStream;
+ try
+ {
+ // seek PostData stream to the beginning
+ css::uno::Reference< css::io::XSeekable > xSeek( _rxPostData, css::uno::UNO_QUERY );
+ if ( xSeek.is() )
+ xSeek->seek( 0 );
+
+ // a content for the URL
+ ::ucbhelper::Content aContent( sURL, xCommandEnv, comphelper::getProcessComponentContext() );
+
+ // use post command
+ css::ucb::PostCommandArgument2 aPostArgument;
+ aPostArgument.Source = _rxPostData;
+ css::uno::Reference< css::io::XActiveDataSink > xSink( new ucbhelper::ActiveDataSink );
+ aPostArgument.Sink = xSink;
+ aPostArgument.MediaType = sMediaType;
+ aPostArgument.Referer = getUnpackedValueOrDefault( PROP_REFERRER, OUString() );
+
+ aContent.executeCommand( "post", css::uno::Any( aPostArgument ) );
+
+ // get result
+ xResultStream = xSink->getInputStream();
+ }
+ catch( const css::uno::Exception& )
+ {
+ }
+
+ // success?
+ if ( !xResultStream.is() )
+ {
+ OSL_FAIL( "no valid reply to the HTTP-Post" );
+ return false;
+ }
+
+ (*this)[MediaDescriptor::PROP_INPUTSTREAM] <<= xResultStream;
+ return true;
+}
+
+/*-----------------------------------------------*/
+bool MediaDescriptor::impl_openStreamWithURL( const OUString& sURL, bool bLockFile )
+{
+ if (sURL.matchIgnoreAsciiCase(".component:"))
+ return false; // No UCB content for .component URLs
+
+ OUString referer(getUnpackedValueOrDefault(PROP_REFERRER, OUString()));
+ if (SvtSecurityOptions::isUntrustedReferer(referer)) {
+ return false;
+ }
+
+ // prepare the environment
+ css::uno::Reference< css::task::XInteractionHandler > xOrgInteraction = getUnpackedValueOrDefault(
+ MediaDescriptor::PROP_INTERACTIONHANDLER,
+ css::uno::Reference< css::task::XInteractionHandler >());
+
+ css::uno::Reference< css::task::XInteractionHandler > xAuthenticationInteraction = getUnpackedValueOrDefault(
+ MediaDescriptor::PROP_AUTHENTICATIONHANDLER,
+ css::uno::Reference< css::task::XInteractionHandler >());
+
+ rtl::Reference<comphelper::StillReadWriteInteraction> xInteraction = new comphelper::StillReadWriteInteraction(xOrgInteraction,xAuthenticationInteraction);
+
+ css::uno::Reference< css::ucb::XProgressHandler > xProgress;
+ rtl::Reference<::ucbhelper::CommandEnvironment> xCommandEnv = new ::ucbhelper::CommandEnvironment(xInteraction, xProgress);
+
+ // try to create the content
+ // no content -> no stream => return immediately with FALSE
+ ::ucbhelper::Content aContent;
+ css::uno::Reference< css::ucb::XContent > xContent;
+ try
+ {
+ aContent = ::ucbhelper::Content(sURL, xCommandEnv, comphelper::getProcessComponentContext());
+ xContent = aContent.get();
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::ucb::ContentCreationException&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.misc", "url: '" << sURL << "'");
+ return false; // TODO error handling
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("unotools.misc", "url: '" << sURL << "'");
+ return false; // TODO error handling
+ }
+
+ // try to open the file in read/write mode
+ // (if it's allowed to do so).
+ // But handle errors in a "hidden mode". Because
+ // we try it readonly later - if read/write is not an option.
+ css::uno::Reference< css::io::XStream > xStream;
+ css::uno::Reference< css::io::XInputStream > xInputStream;
+
+ bool bReadOnly = false;
+ bool bModeRequestedExplicitly = false;
+ const_iterator pIt = find(MediaDescriptor::PROP_READONLY);
+ if (pIt != end())
+ {
+ pIt->second >>= bReadOnly;
+ bModeRequestedExplicitly = true;
+ }
+
+ if ( !bReadOnly && bLockFile )
+ {
+ try
+ {
+ // TODO: use "special" still interaction to suppress error messages
+ xStream = aContent.openWriteableStream();
+ if (xStream.is())
+ xInputStream = xStream->getInputStream();
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ {
+ css::uno::Any ex( cppu::getCaughtException() );
+ // ignore exception, if reason was problem reasoned on
+ // open it in WRITABLE mode! Then we try it READONLY
+ // later a second time.
+ // All other errors must be handled as real error an
+ // break this method.
+ if (!xInteraction->wasWriteError() || bModeRequestedExplicitly)
+ {
+ SAL_WARN("unotools.misc","url: '" << sURL << "' " << exceptionToString(ex));
+ // If the protocol is webdav, then we need to treat the stream as readonly, even if the
+ // operation was requested as read/write explicitly (the WebDAV UCB implementation is monodirectional
+ // read or write not both at the same time).
+ if ( !INetURLObject( sURL ).isAnyKnownWebDAVScheme() )
+ return false;
+ }
+ xStream.clear();
+ xInputStream.clear();
+ }
+ }
+
+ // If opening of the stream in read/write mode was not allowed
+ // or failed by an error - we must try it in readonly mode.
+ if (!xInputStream.is())
+ {
+ OUString aScheme;
+
+ try
+ {
+ css::uno::Reference< css::ucb::XContentIdentifier > xContId(
+ aContent.get().is() ? aContent.get()->getIdentifier() : nullptr );
+
+ if ( xContId.is() )
+ aScheme = xContId->getContentProviderScheme();
+
+ // Only file system content provider is able to provide XStream
+ // so for this content impossibility to create XStream triggers
+ // switch to readonly mode in case of opening with locking on
+ if( bLockFile && aScheme.equalsIgnoreAsciiCase("file") )
+ bReadOnly = true;
+ else
+ {
+ bool bRequestReadOnly = bReadOnly;
+ aContent.getPropertyValue("IsReadOnly") >>= bReadOnly;
+ if ( bReadOnly && !bRequestReadOnly && bModeRequestedExplicitly )
+ return false; // the document is explicitly requested with WRITABLE mode
+ }
+ }
+ catch(const css::uno::RuntimeException&)
+ { throw; }
+ catch(const css::uno::Exception&)
+ { /* no error handling if IsReadOnly property does not exist for UCP */ }
+
+ if ( bReadOnly )
+ (*this)[MediaDescriptor::PROP_READONLY] <<= bReadOnly;
+
+ xInteraction->resetInterceptions();
+ xInteraction->resetErrorStates();
+ try
+ {
+ // all the contents except file-URLs should be opened as usual
+ if ( bLockFile || !aScheme.equalsIgnoreAsciiCase("file") )
+ xInputStream = aContent.openStream();
+ else
+ xInputStream = aContent.openStreamNoLock();
+ }
+ catch(const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch(const css::uno::Exception&)
+ {
+ TOOLS_INFO_EXCEPTION("unotools.misc","url: '" << sURL << "'");
+ return false;
+ }
+ }
+
+ // add streams to the descriptor
+ if (xContent.is())
+ (*this)[MediaDescriptor::PROP_UCBCONTENT] <<= xContent;
+ if (xStream.is())
+ (*this)[MediaDescriptor::PROP_STREAM] <<= xStream;
+ if (xInputStream.is())
+ (*this)[MediaDescriptor::PROP_INPUTSTREAM] <<= xInputStream;
+
+ // At least we need an input stream. The r/w stream is optional ...
+ return xInputStream.is();
+}
+
+} // namespace comphelper
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/misc/sharedunocomponent.cxx b/unotools/source/misc/sharedunocomponent.cxx
new file mode 100644
index 0000000000..1b724a2b94
--- /dev/null
+++ b/unotools/source/misc/sharedunocomponent.cxx
@@ -0,0 +1,199 @@
+/* -*- 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/diagnose.h>
+
+#include <unotools/sharedunocomponent.hxx>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/util/CloseVetoException.hpp>
+#include <com/sun/star/util/XCloseable.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <tools/debug.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+namespace utl
+{
+
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::util::XCloseable;
+ using ::com::sun::star::util::XCloseListener;
+ using ::com::sun::star::util::CloseVetoException;
+
+ //= DisposableComponent
+
+ DisposableComponent::DisposableComponent( const Reference< XInterface >& _rxComponent )
+ :m_xComponent( _rxComponent, UNO_QUERY )
+ {
+ DBG_ASSERT( m_xComponent.is() || !_rxComponent.is(), "DisposableComponent::DisposableComponent: should be an XComponent!" );
+ }
+
+ DisposableComponent::~DisposableComponent()
+ {
+ if ( m_xComponent.is() )
+ {
+ try
+ {
+ m_xComponent->dispose();
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools", "DisposableComponent::~DisposableComponent" );
+ }
+ m_xComponent.clear();
+ }
+ }
+
+ typedef ::cppu::WeakImplHelper < XCloseListener
+ > CloseableComponentImpl_Base;
+ class CloseableComponentImpl : public CloseableComponentImpl_Base
+ {
+ private:
+ Reference< XCloseable > m_xCloseable;
+
+ CloseableComponentImpl(const CloseableComponentImpl&) = delete;
+ CloseableComponentImpl& operator=(const CloseableComponentImpl&) = delete;
+
+ public:
+ explicit CloseableComponentImpl( const Reference< XInterface >& _rxComponent );
+
+ /** closes the component
+
+ @nofail
+ */
+ void nf_closeComponent();
+
+ protected:
+ virtual ~CloseableComponentImpl() override;
+
+ // XCloseListener overridables
+ virtual void SAL_CALL queryClosing( const EventObject& Source, sal_Bool GetsOwnership ) override;
+ virtual void SAL_CALL notifyClosing( const EventObject& Source ) override;
+
+ // XEventListener overridables
+ virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override;
+
+ private:
+ /** starts or stops being a CloseListener at the component
+
+ Only to be called upon construction of the instance, or when the component
+ is to be closed.
+
+ @nofail
+ */
+ void impl_nf_switchListening( bool _bListen );
+ };
+
+ CloseableComponentImpl::CloseableComponentImpl( const Reference< XInterface >& _rxComponent )
+ :m_xCloseable( _rxComponent, UNO_QUERY )
+ {
+ DBG_ASSERT( m_xCloseable.is() || !_rxComponent.is(), "CloseableComponentImpl::CloseableComponentImpl: component is not an XCloseable!" );
+ impl_nf_switchListening( true );
+ }
+
+ CloseableComponentImpl::~CloseableComponentImpl()
+ {
+ nf_closeComponent();
+ }
+
+ void CloseableComponentImpl::nf_closeComponent()
+ {
+ if ( !m_xCloseable.is() )
+ // nothing to do
+ return;
+
+ // stop listening
+ impl_nf_switchListening( false );
+
+ // close
+ try
+ {
+ m_xCloseable->close( true );
+ }
+ catch( const CloseVetoException& ) { /* fine */ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools", "CloseableComponentImpl::nf_closeComponent: caught an unexpected exception!" );
+ }
+
+ // reset
+ m_xCloseable.clear();
+ }
+
+ void CloseableComponentImpl::impl_nf_switchListening( bool _bListen )
+ {
+ if ( !m_xCloseable.is() )
+ return;
+
+ try
+ {
+ if ( _bListen )
+ m_xCloseable->addCloseListener( this );
+ else
+ m_xCloseable->removeCloseListener( this );
+ }
+ catch( const Exception& )
+ {
+ TOOLS_WARN_EXCEPTION( "unotools", "CloseableComponentImpl::impl_nf_switchListening" );
+ }
+ }
+
+ void SAL_CALL CloseableComponentImpl::queryClosing( const EventObject& Source, sal_Bool /*GetsOwnership*/ )
+ {
+ // as long as we live, somebody wants to keep the object alive. So, veto the
+ // closing
+ DBG_ASSERT( Source.Source == m_xCloseable, "CloseableComponentImpl::queryClosing: where did this come from?" );
+ throw CloseVetoException();
+ }
+
+ void SAL_CALL CloseableComponentImpl::notifyClosing( const EventObject& Source )
+ {
+ DBG_ASSERT( Source.Source == m_xCloseable, "CloseableComponentImpl::notifyClosing: where did this come from?" );
+
+ // this should be unreachable: As long as we're a CloseListener, we veto the closing. If we're going
+ // to close the component ourself, then we revoke ourself as listener *before* the close call. So,
+ // if this here fires, something went definitely wrong.
+ OSL_FAIL( "CloseableComponentImpl::notifyClosing: unreachable!" );
+ }
+
+ void SAL_CALL CloseableComponentImpl::disposing( const EventObject& Source )
+ {
+ DBG_ASSERT( Source.Source == m_xCloseable, "CloseableComponentImpl::disposing: where did this come from?" );
+ OSL_FAIL( "CloseableComponentImpl::disposing: unreachable!" );
+ // same reasoning for this assertion as in ->notifyClosing
+ }
+
+ CloseableComponent::CloseableComponent( const Reference< XInterface >& _rxComponent )
+ :m_pImpl( new CloseableComponentImpl( _rxComponent ) )
+ {
+ }
+
+ CloseableComponent::~CloseableComponent()
+ {
+ // close the component, deliver ownership to anybody who wants to veto the close
+ m_pImpl->nf_closeComponent();
+ }
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/misc/syslocale.cxx b/unotools/source/misc/syslocale.cxx
new file mode 100644
index 0000000000..31b448e658
--- /dev/null
+++ b/unotools/source/misc/syslocale.cxx
@@ -0,0 +1,177 @@
+/* -*- 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 <sal/log.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/syslocale.hxx>
+#include <unotools/syslocaleoptions.hxx>
+#include <comphelper/lok.hxx>
+#include <rtl/tencinfo.h>
+#include <rtl/locale.h>
+#include <osl/thread.h>
+#include <osl/nlsupport.h>
+
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <vector>
+
+using namespace osl;
+using namespace com::sun::star;
+
+namespace {
+
+std::weak_ptr<SvtSysLocale_Impl> g_pSysLocale;
+
+// static
+std::mutex& GetMutex()
+{
+ // #i77768# Due to a static reference in the toolkit lib
+ // we need a mutex that lives longer than the svl library.
+ // Otherwise the dtor would use a destructed mutex!!
+ static std::mutex* persistentMutex(new std::mutex);
+
+ return *persistentMutex;
+}
+
+
+}
+
+class SvtSysLocale_Impl : public utl::ConfigurationListener
+{
+public:
+ SvtSysLocaleOptions aSysLocaleOptions;
+ std::optional<LocaleDataWrapper> moLocaleData;
+ std::optional<CharClass> moCharClass;
+
+ SvtSysLocale_Impl();
+ virtual ~SvtSysLocale_Impl() override;
+
+ CharClass& GetCharClass();
+ virtual void ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints ) override;
+
+private:
+ std::vector<OUString> getDateAcceptancePatternsConfig() const;
+};
+
+SvtSysLocale_Impl::SvtSysLocale_Impl()
+{
+ moLocaleData.emplace(
+ aSysLocaleOptions.GetRealLanguageTag(),
+ getDateAcceptancePatternsConfig() );
+
+ // listen for further changes
+ aSysLocaleOptions.AddListener( this );
+}
+
+SvtSysLocale_Impl::~SvtSysLocale_Impl()
+{
+ aSysLocaleOptions.RemoveListener( this );
+}
+
+CharClass& SvtSysLocale_Impl::GetCharClass()
+{
+ if ( !moCharClass )
+ moCharClass.emplace( aSysLocaleOptions.GetRealLanguageTag() );
+ return *moCharClass;
+}
+
+void SvtSysLocale_Impl::ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints nHint )
+{
+ if ( !(nHint & ConfigurationHints::Locale) &&
+ !(nHint & ConfigurationHints::DatePatterns) )
+ return;
+
+ std::unique_lock aGuard( GetMutex() );
+
+ const LanguageTag& rLanguageTag = aSysLocaleOptions.GetRealLanguageTag();
+ if ( nHint & ConfigurationHints::Locale )
+ {
+ moCharClass.emplace( rLanguageTag );
+ }
+ moLocaleData.emplace(rLanguageTag, getDateAcceptancePatternsConfig());
+}
+
+std::vector<OUString> SvtSysLocale_Impl::getDateAcceptancePatternsConfig() const
+{
+ OUString aStr( aSysLocaleOptions.GetDatePatternsConfigString());
+ if (aStr.isEmpty())
+ return {}; // reset
+ ::std::vector< OUString > aVec;
+ for (sal_Int32 nIndex = 0; nIndex >= 0; /*nop*/)
+ {
+ OUString aTok( aStr.getToken( 0, ';', nIndex));
+ if (!aTok.isEmpty())
+ aVec.push_back( aTok);
+ }
+ return aVec;
+}
+
+SvtSysLocale::SvtSysLocale()
+{
+ std::unique_lock aGuard( GetMutex() );
+ pImpl = g_pSysLocale.lock();
+ if ( !pImpl )
+ {
+ pImpl = std::make_shared<SvtSysLocale_Impl>();
+ g_pSysLocale = pImpl;
+ }
+}
+
+SvtSysLocale::~SvtSysLocale()
+{
+ std::unique_lock aGuard( GetMutex() );
+ pImpl.reset();
+}
+
+const LocaleDataWrapper& SvtSysLocale::GetLocaleData() const
+{
+ return *(pImpl->moLocaleData);
+}
+
+const CharClass& SvtSysLocale::GetCharClass() const
+{
+ return pImpl->GetCharClass();
+}
+
+SvtSysLocaleOptions& SvtSysLocale::GetOptions() const
+{
+ return pImpl->aSysLocaleOptions;
+}
+
+const LanguageTag& SvtSysLocale::GetLanguageTag() const
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return comphelper::LibreOfficeKit::getLocale();
+
+ return pImpl->aSysLocaleOptions.GetRealLanguageTag();
+}
+
+const LanguageTag& SvtSysLocale::GetUILanguageTag() const
+{
+ if (comphelper::LibreOfficeKit::isActive())
+ return comphelper::LibreOfficeKit::getLanguageTag();
+
+ return pImpl->aSysLocaleOptions.GetRealUILanguageTag();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/misc/wincodepage.cxx b/unotools/source/misc/wincodepage.cxx
new file mode 100644
index 0000000000..79353e47ce
--- /dev/null
+++ b/unotools/source/misc/wincodepage.cxx
@@ -0,0 +1,141 @@
+/* -*- 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 <unotools/wincodepage.hxx>
+#include <rtl/textenc.h>
+
+namespace{
+
+struct LangEncodingDef
+{
+ const std::u16string_view msLangStr;
+ rtl_TextEncoding meTextEncoding;
+};
+
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd317756
+rtl_TextEncoding impl_getWinTextEncodingFromLangStrANSI(const OUString& sLanguage)
+{
+ static constexpr LangEncodingDef aLanguageTab[] =
+ {
+ { u"en", RTL_TEXTENCODING_MS_1252 }, // Most used -> first in list
+ { u"th", RTL_TEXTENCODING_MS_874 },
+ { u"ja", RTL_TEXTENCODING_MS_932 },
+ { u"zh-cn", RTL_TEXTENCODING_MS_936 }, // Chinese (simplified) - must go before "zh"
+ { u"ko", RTL_TEXTENCODING_MS_949 },
+ { u"zh", RTL_TEXTENCODING_MS_950 }, // Chinese (traditional)
+ { u"bs", RTL_TEXTENCODING_MS_1250 },
+ { u"cs", RTL_TEXTENCODING_MS_1250 },
+ { u"hr", RTL_TEXTENCODING_MS_1250 },
+ { u"hu", RTL_TEXTENCODING_MS_1250 },
+ { u"pl", RTL_TEXTENCODING_MS_1250 },
+ { u"ro", RTL_TEXTENCODING_MS_1250 },
+ { u"sk", RTL_TEXTENCODING_MS_1250 },
+ { u"sl", RTL_TEXTENCODING_MS_1250 },
+// { "sr", RTL_TEXTENCODING_MS_1250 },
+ { u"sq", RTL_TEXTENCODING_MS_1250 },
+ { u"be", RTL_TEXTENCODING_MS_1251 },
+ { u"bg", RTL_TEXTENCODING_MS_1251 },
+ { u"mk", RTL_TEXTENCODING_MS_1251 },
+ { u"ru", RTL_TEXTENCODING_MS_1251 },
+ { u"sr", RTL_TEXTENCODING_MS_1251 },
+ { u"uk", RTL_TEXTENCODING_MS_1251 },
+ { u"es", RTL_TEXTENCODING_MS_1252 },
+ { u"el", RTL_TEXTENCODING_MS_1253 },
+ { u"tr", RTL_TEXTENCODING_MS_1254 },
+ { u"he", RTL_TEXTENCODING_MS_1255 },
+ { u"ar", RTL_TEXTENCODING_MS_1256 },
+ { u"et", RTL_TEXTENCODING_MS_1257 },
+ { u"lt", RTL_TEXTENCODING_MS_1257 },
+ { u"lv", RTL_TEXTENCODING_MS_1257 },
+ { u"vi", RTL_TEXTENCODING_MS_1258 },
+ };
+
+ for (auto& def : aLanguageTab)
+ {
+ if (sLanguage.startsWithIgnoreAsciiCase(def.msLangStr))
+ return def.meTextEncoding;
+ }
+
+ return RTL_TEXTENCODING_MS_1252;
+}
+
+/* ----------------------------------------------------------------------- */
+
+// See https://msdn.microsoft.com/en-us/library/windows/desktop/dd317756
+// See http://shapelib.maptools.org/codepage.html
+rtl_TextEncoding impl_getWinTextEncodingFromLangStrOEM(const OUString& sLanguage)
+{
+ static constexpr LangEncodingDef aLanguageTab[] =
+ {
+ { u"de", RTL_TEXTENCODING_IBM_437 }, // OEM United States
+ { u"en-us", RTL_TEXTENCODING_IBM_437 }, // OEM United States
+ { u"fi", RTL_TEXTENCODING_IBM_437 }, // OEM United States
+ { u"fr-ca", RTL_TEXTENCODING_IBM_863 }, // OEM French Canadian; French Canadian (DOS)
+ { u"fr", RTL_TEXTENCODING_IBM_437 }, // OEM United States
+ { u"it", RTL_TEXTENCODING_IBM_437 }, // OEM United States
+ { u"nl", RTL_TEXTENCODING_IBM_437 }, // OEM United States
+ { u"sv", RTL_TEXTENCODING_IBM_437 }, // OEM United States
+ { u"el", RTL_TEXTENCODING_IBM_737 }, // OEM Greek (formerly 437G); Greek (DOS)
+ { u"et", RTL_TEXTENCODING_IBM_775 }, // OEM Baltic; Baltic (DOS)
+ { u"lt", RTL_TEXTENCODING_IBM_775 }, // OEM Baltic; Baltic (DOS)
+ { u"lv", RTL_TEXTENCODING_IBM_775 }, // OEM Baltic; Baltic (DOS)
+ { u"en", RTL_TEXTENCODING_IBM_850 }, // OEM Multilingual Latin 1; Western European (DOS)
+ { u"bs", RTL_TEXTENCODING_IBM_852 }, // OEM Latin 2; Central European (DOS)
+ { u"cs", RTL_TEXTENCODING_IBM_852 }, // OEM Latin 2; Central European (DOS)
+ { u"hr", RTL_TEXTENCODING_IBM_852 }, // OEM Latin 2; Central European (DOS)
+ { u"hu", RTL_TEXTENCODING_IBM_852 }, // OEM Latin 2; Central European (DOS)
+ { u"pl", RTL_TEXTENCODING_IBM_852 }, // OEM Latin 2; Central European (DOS)
+ { u"ro", RTL_TEXTENCODING_IBM_852 }, // OEM Latin 2; Central European (DOS)
+ { u"sk", RTL_TEXTENCODING_IBM_852 }, // OEM Latin 2; Central European (DOS)
+ { u"sl", RTL_TEXTENCODING_IBM_852 }, // OEM Latin 2; Central European (DOS)
+// { "sr", RTL_TEXTENCODING_IBM_852 }, // OEM Latin 2; Central European (DOS)
+ { u"bg", RTL_TEXTENCODING_IBM_855 }, // OEM Cyrillic (primarily Russian)
+ { u"mk", RTL_TEXTENCODING_IBM_855 }, // OEM Cyrillic (primarily Russian)
+ { u"sr", RTL_TEXTENCODING_IBM_855 }, // OEM Cyrillic (primarily Russian)
+ { u"tr", RTL_TEXTENCODING_IBM_857 }, // OEM Turkish; Turkish (DOS)
+ { u"pt", RTL_TEXTENCODING_IBM_860 }, // OEM Portuguese; Portuguese (DOS)
+ { u"is", RTL_TEXTENCODING_IBM_861 }, // OEM Icelandic; Icelandic (DOS)
+ { u"he", RTL_TEXTENCODING_IBM_862 }, // OEM Hebrew; Hebrew (DOS)
+ { u"ar", RTL_TEXTENCODING_IBM_864 }, // OEM Arabic; Arabic (864)
+ { u"da", RTL_TEXTENCODING_IBM_865 }, // OEM Nordic; Nordic (DOS)
+ { u"nn", RTL_TEXTENCODING_IBM_865 }, // OEM Nordic; Nordic (DOS)
+ { u"be", RTL_TEXTENCODING_IBM_866 }, // OEM Russian; Cyrillic (DOS)
+ { u"ru", RTL_TEXTENCODING_IBM_866 }, // OEM Russian; Cyrillic (DOS)
+ { u"uk", RTL_TEXTENCODING_IBM_866 }, // OEM Russian; Cyrillic (DOS)
+ { u"th", RTL_TEXTENCODING_MS_874 }, // ANSI/OEM Thai (ISO 8859-11); Thai (Windows)
+ { u"ja", RTL_TEXTENCODING_MS_932 }, // ANSI/OEM Japanese; Japanese (Shift-JIS)
+ { u"zh-cn", RTL_TEXTENCODING_MS_936 }, // ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)
+ { u"ko", RTL_TEXTENCODING_MS_949 }, // ANSI/OEM Korean (Unified Hangul Code)
+ { u"zh", RTL_TEXTENCODING_MS_950 }, // ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)
+ { u"vi", RTL_TEXTENCODING_MS_1258 }, // ANSI/OEM Vietnamese; Vietnamese (Windows)
+ };
+
+ for (auto& def : aLanguageTab)
+ {
+ if (sLanguage.startsWithIgnoreAsciiCase(def.msLangStr))
+ return def.meTextEncoding;
+ }
+
+ return RTL_TEXTENCODING_IBM_850;
+}
+
+} // namespace
+
+rtl_TextEncoding utl_getWinTextEncodingFromLangStr(const OUString& sLanguage, bool bOEM)
+{
+ return bOEM ?
+ impl_getWinTextEncodingFromLangStrOEM(sLanguage) :
+ impl_getWinTextEncodingFromLangStrANSI(sLanguage);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/streaming/streamhelper.cxx b/unotools/source/streaming/streamhelper.cxx
new file mode 100644
index 0000000000..12f6ad7afd
--- /dev/null
+++ b/unotools/source/streaming/streamhelper.cxx
@@ -0,0 +1,123 @@
+/* -*- 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 <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <o3tl/safeint.hxx>
+#include <unotools/streamhelper.hxx>
+
+namespace utl
+{
+
+sal_Int32 SAL_CALL OInputStreamHelper::readBytes(css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead)
+{
+ if (!m_xLockBytes.is())
+ throw css::io::NotConnectedException(OUString(), getXWeak());
+
+ if (nBytesToRead < 0)
+ throw css::io::BufferSizeExceededException(OUString(), getXWeak());
+
+ std::scoped_lock aGuard( m_aMutex );
+ if (aData.getLength() < nBytesToRead)
+ aData.realloc(nBytesToRead);
+
+ std::size_t nRead(0);
+ ErrCode nError = m_xLockBytes->ReadAt(m_nActPos, static_cast<void*>(aData.getArray()), nBytesToRead, &nRead);
+ m_nActPos += nRead;
+
+ if (nError != ERRCODE_NONE)
+ throw css::io::IOException(OUString(), getXWeak());
+
+ // adjust sequence if data read is lower than the desired data
+ if (nRead < o3tl::make_unsigned(aData.getLength()))
+ aData.realloc( nRead );
+
+ return nRead;
+}
+
+void SAL_CALL OInputStreamHelper::seek( sal_Int64 location )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ m_nActPos = location;
+}
+
+sal_Int64 SAL_CALL OInputStreamHelper::getPosition( )
+{
+ return m_nActPos;
+}
+
+sal_Int64 SAL_CALL OInputStreamHelper::getLength( )
+{
+ if (!m_xLockBytes.is())
+ return 0;
+
+ std::scoped_lock aGuard( m_aMutex );
+ SvLockBytesStat aStat;
+ m_xLockBytes->Stat( &aStat );
+ return aStat.nSize;
+}
+
+sal_Int32 SAL_CALL OInputStreamHelper::readSomeBytes(css::uno::Sequence< sal_Int8 >& aData,
+ sal_Int32 nMaxBytesToRead)
+{
+ // read all data desired
+ return readBytes(aData, nMaxBytesToRead);
+}
+
+void SAL_CALL OInputStreamHelper::skipBytes(sal_Int32 nBytesToSkip)
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if (!m_xLockBytes.is())
+ throw css::io::NotConnectedException(OUString(), getXWeak());
+
+ if (nBytesToSkip < 0)
+ throw css::io::BufferSizeExceededException(OUString(), getXWeak());
+
+ m_nActPos += nBytesToSkip;
+}
+
+sal_Int32 SAL_CALL OInputStreamHelper::available()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if (!m_xLockBytes.is())
+ throw css::io::NotConnectedException(OUString(), getXWeak());
+
+ return m_nAvailable;
+}
+
+void SAL_CALL OInputStreamHelper::closeInput()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if (!m_xLockBytes.is())
+ throw css::io::NotConnectedException(OUString(), getXWeak());
+
+ m_xLockBytes = nullptr;
+}
+
+void SAL_CALL OInputStreamHelper::acquire() SAL_NOEXCEPT
+{
+ cppu::WeakImplHelper<css::io::XInputStream, css::io::XSeekable>::acquire();
+}
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/streaming/streamwrap.cxx b/unotools/source/streaming/streamwrap.cxx
new file mode 100644
index 0000000000..beb909f36b
--- /dev/null
+++ b/unotools/source/streaming/streamwrap.cxx
@@ -0,0 +1,338 @@
+/* -*- 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 <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <o3tl/safeint.hxx>
+#include <unotools/streamwrap.hxx>
+#include <tools/stream.hxx>
+
+namespace utl
+{
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::lang;
+
+OInputStreamWrapper::OInputStreamWrapper( SvStream& _rStream )
+ :m_pSvStream(&_rStream)
+ ,m_bSvStreamOwner(false)
+{
+}
+
+OInputStreamWrapper::OInputStreamWrapper( SvStream* pStream, bool bOwner )
+ :m_pSvStream( pStream )
+ ,m_bSvStreamOwner( bOwner )
+{
+}
+
+OInputStreamWrapper::OInputStreamWrapper( std::unique_ptr<SvStream> pStream )
+ :m_pSvStream( pStream.release() )
+ ,m_bSvStreamOwner( true )
+{
+}
+
+OInputStreamWrapper::~OInputStreamWrapper()
+{
+ if( m_bSvStreamOwner )
+ delete m_pSvStream;
+}
+
+sal_Int32 SAL_CALL OInputStreamWrapper::readBytes(css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead)
+{
+ checkConnected();
+
+ if (nBytesToRead < 0)
+ throw css::io::BufferSizeExceededException(OUString(), getXWeak());
+
+ std::scoped_lock aGuard( m_aMutex );
+
+ if (aData.getLength() < nBytesToRead)
+ aData.realloc(nBytesToRead);
+
+ sal_uInt32 nRead = m_pSvStream->ReadBytes(static_cast<void*>(aData.getArray()), nBytesToRead);
+ checkError();
+
+ // If read characters < MaxLength, adjust css::uno::Sequence
+ if (nRead < o3tl::make_unsigned(aData.getLength()))
+ aData.realloc( nRead );
+
+ return nRead;
+}
+
+sal_Int32 OInputStreamWrapper::readSomeBytes(sal_Int8* pData, sal_Int32 nBytesToRead)
+{
+ checkConnected();
+
+ if (nBytesToRead < 0)
+ throw css::io::BufferSizeExceededException(OUString(), getXWeak());
+
+ std::scoped_lock aGuard( m_aMutex );
+
+ sal_uInt32 nRead = m_pSvStream->ReadBytes(static_cast<void*>(pData), nBytesToRead);
+ checkError();
+
+ return nRead;
+}
+
+sal_Int32 SAL_CALL OInputStreamWrapper::readSomeBytes(css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead)
+{
+ checkError();
+
+ if (nMaxBytesToRead < 0)
+ throw css::io::BufferSizeExceededException(OUString(), getXWeak());
+
+ if (m_pSvStream->eof())
+ {
+ aData.realloc(0);
+ return 0;
+ }
+ else
+ return readBytes(aData, nMaxBytesToRead);
+}
+
+void SAL_CALL OInputStreamWrapper::skipBytes(sal_Int32 nBytesToSkip)
+{
+ std::scoped_lock aGuard( m_aMutex );
+ checkError();
+
+ m_pSvStream->SeekRel(nBytesToSkip);
+ checkError();
+}
+
+sal_Int32 SAL_CALL OInputStreamWrapper::available()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ checkConnected();
+
+ sal_Int64 nAvailable = m_pSvStream->remainingSize();
+ checkError();
+
+ return std::min<sal_Int64>(SAL_MAX_INT32, nAvailable);
+}
+
+void SAL_CALL OInputStreamWrapper::closeInput()
+{
+ std::scoped_lock aGuard( m_aMutex );
+ if (m_pSvStream)
+ {
+ if (m_bSvStreamOwner)
+ delete m_pSvStream;
+
+ m_pSvStream = nullptr;
+ }
+}
+
+void OInputStreamWrapper::checkConnected() const
+{
+ if (!m_pSvStream)
+ throw css::io::NotConnectedException(OUString(), const_cast<OInputStreamWrapper*>(this)->getXWeak());
+}
+
+void OInputStreamWrapper::checkError() const
+{
+ checkConnected();
+
+ auto const e = m_pSvStream->SvStream::GetError();
+ if (e != ERRCODE_NONE)
+ // TODO: really evaluate the error
+ throw css::io::NotConnectedException("utl::OInputStreamWrapper error " + e.toString(), const_cast<OInputStreamWrapper*>(this)->getXWeak());
+}
+
+//= OSeekableInputStreamWrapper
+
+OSeekableInputStreamWrapper::~OSeekableInputStreamWrapper() = default;
+
+OSeekableInputStreamWrapper::OSeekableInputStreamWrapper(SvStream& _rStream)
+{
+ SetStream( &_rStream, false );
+}
+
+OSeekableInputStreamWrapper::OSeekableInputStreamWrapper(SvStream* _pStream, bool _bOwner)
+{
+ SetStream( _pStream, _bOwner );
+}
+
+void SAL_CALL OSeekableInputStreamWrapper::seek( sal_Int64 _nLocation )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ checkConnected();
+
+ m_pSvStream->Seek(static_cast<sal_uInt64>(_nLocation));
+ checkError();
+}
+
+sal_Int64 SAL_CALL OSeekableInputStreamWrapper::getPosition( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ checkConnected();
+
+ sal_uInt64 nPos = m_pSvStream->Tell();
+ checkError();
+ return static_cast<sal_Int64>(nPos);
+}
+
+sal_Int64 SAL_CALL OSeekableInputStreamWrapper::getLength( )
+{
+ std::scoped_lock aGuard( m_aMutex );
+ checkConnected();
+
+ checkError();
+
+ sal_Int64 nEndPos = m_pSvStream->TellEnd();
+
+ return nEndPos;
+}
+
+//= OOutputStreamWrapper
+
+OOutputStreamWrapper::OOutputStreamWrapper(SvStream& _rStream):
+ rStream(_rStream)
+{}
+
+OOutputStreamWrapper::~OOutputStreamWrapper() {}
+
+void SAL_CALL OOutputStreamWrapper::writeBytes(const css::uno::Sequence< sal_Int8 >& aData)
+{
+ sal_uInt32 nWritten = rStream.WriteBytes(aData.getConstArray(), aData.getLength());
+ ErrCode err = rStream.GetError();
+ if ( (ERRCODE_NONE != err)
+ || (nWritten != static_cast<sal_uInt32>(aData.getLength()))
+ )
+ {
+ throw css::io::BufferSizeExceededException(OUString(), getXWeak());
+ }
+}
+
+void SAL_CALL OOutputStreamWrapper::flush()
+{
+ rStream.FlushBuffer();
+ checkError();
+}
+
+void SAL_CALL OOutputStreamWrapper::closeOutput()
+{
+}
+
+void OOutputStreamWrapper::checkError() const
+{
+ if (rStream.GetError() != ERRCODE_NONE)
+ // TODO: really evaluate the error
+ throw css::io::NotConnectedException(OUString(), const_cast<OOutputStreamWrapper*>(this)->getXWeak());
+}
+
+//= OSeekableOutputStreamWrapper
+
+OSeekableOutputStreamWrapper::OSeekableOutputStreamWrapper(SvStream& _rStream)
+ :OOutputStreamWrapper(_rStream)
+{
+}
+
+OSeekableOutputStreamWrapper::~OSeekableOutputStreamWrapper() {}
+
+Any SAL_CALL OSeekableOutputStreamWrapper::queryInterface( const Type& _rType )
+{
+ Any aReturn = OOutputStreamWrapper::queryInterface(_rType);
+ if (!aReturn.hasValue())
+ aReturn = OSeekableOutputStreamWrapper_Base::queryInterface(_rType);
+ return aReturn;
+}
+
+void SAL_CALL OSeekableOutputStreamWrapper::seek( sal_Int64 _nLocation )
+{
+ rStream.Seek(static_cast<sal_uInt32>(_nLocation));
+ checkError();
+}
+
+sal_Int64 SAL_CALL OSeekableOutputStreamWrapper::getPosition( )
+{
+ sal_uInt64 nPos = rStream.Tell();
+ checkError();
+ return static_cast<sal_Int64>(nPos);
+}
+
+sal_Int64 SAL_CALL OSeekableOutputStreamWrapper::getLength( )
+{
+ checkError();
+
+ sal_Int64 nEndPos = rStream.TellEnd();
+
+ return nEndPos;
+}
+
+OStreamWrapper::~OStreamWrapper() = default;
+
+OStreamWrapper::OStreamWrapper(SvStream& _rStream)
+{
+ SetStream( &_rStream, false );
+}
+
+OStreamWrapper::OStreamWrapper(std::unique_ptr<SvStream> pStream)
+{
+ SetStream( pStream.release(), true );
+}
+
+OStreamWrapper::OStreamWrapper(SvStream* pStream, bool bOwner)
+{
+ SetStream( pStream, bOwner );
+}
+
+css::uno::Reference< css::io::XInputStream > SAL_CALL OStreamWrapper::getInputStream( )
+{
+ return this;
+}
+
+css::uno::Reference< css::io::XOutputStream > SAL_CALL OStreamWrapper::getOutputStream( )
+{
+ return this;
+}
+
+void SAL_CALL OStreamWrapper::writeBytes(const css::uno::Sequence< sal_Int8 >& aData)
+{
+ sal_uInt32 nWritten = m_pSvStream->WriteBytes(aData.getConstArray(), aData.getLength());
+ ErrCode err = m_pSvStream->GetError();
+ if ( (ERRCODE_NONE != err)
+ || (nWritten != static_cast<sal_uInt32>(aData.getLength()))
+ )
+ {
+ throw css::io::BufferSizeExceededException(OUString(), getXWeak());
+ }
+}
+
+void SAL_CALL OStreamWrapper::flush()
+{
+ m_pSvStream->FlushBuffer();
+ if (m_pSvStream->GetError() != ERRCODE_NONE)
+ throw css::io::NotConnectedException(OUString(), getXWeak());
+}
+
+void SAL_CALL OStreamWrapper::closeOutput()
+{
+}
+
+void SAL_CALL OStreamWrapper::truncate()
+{
+ m_pSvStream->SetStreamSize(0);
+}
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/XTempFile.hxx b/unotools/source/ucbhelper/XTempFile.hxx
new file mode 100644
index 0000000000..2b0ec33cb4
--- /dev/null
+++ b/unotools/source/ucbhelper/XTempFile.hxx
@@ -0,0 +1,119 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <optional>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/io/XTempFile.hpp>
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/XPropertyAccess.hpp>
+#include <com/sun/star/beans/XFastPropertySet.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <mutex>
+#include <unotools/tempfile.hxx>
+
+namespace com::sun::star::uno { class XComponentContext; }
+
+class SvStream;
+namespace utl { class TempFile; }
+
+
+typedef ::cppu::WeakImplHelper< css::io::XTempFile
+ , css::io::XInputStream
+ , css::io::XOutputStream
+ , css::io::XTruncate
+ , css::beans::XPropertySet
+ , css::beans::XFastPropertySet
+ , css::beans::XPropertyAccess
+ , css::lang::XServiceInfo> OTempFileBase;
+
+class OTempFileService : public OTempFileBase
+{
+ std::optional<utl::TempFileNamed> mpTempFile;
+ std::mutex maMutex;
+ SvStream* mpStream;
+ bool mbRemoveFile;
+ bool mbInClosed;
+ bool mbOutClosed;
+
+protected:
+ void checkError () const;
+ void checkConnected ();
+
+public:
+ explicit OTempFileService (css::uno::Reference< css::uno::XComponentContext > const & context);
+
+ //Methods
+ // XTypeProvider
+ virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override;
+
+ // XServiceInfo
+ virtual sal_Bool SAL_CALL supportsService(const OUString& sServiceName) override;
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XTempFile
+ virtual sal_Bool SAL_CALL getRemoveFile() override;
+ virtual void SAL_CALL setRemoveFile( sal_Bool _removefile ) override;
+ virtual OUString SAL_CALL getUri() override;
+ virtual OUString SAL_CALL getResourceName() override;
+
+ // XInputStream
+ virtual ::sal_Int32 SAL_CALL readBytes( css::uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nBytesToRead ) override;
+ virtual ::sal_Int32 SAL_CALL readSomeBytes( css::uno::Sequence< ::sal_Int8 >& aData, ::sal_Int32 nMaxBytesToRead ) override;
+ virtual void SAL_CALL skipBytes( ::sal_Int32 nBytesToSkip ) override;
+ virtual ::sal_Int32 SAL_CALL available( ) override;
+ virtual void SAL_CALL closeInput( ) override;
+ // XOutputStream
+ virtual void SAL_CALL writeBytes( const css::uno::Sequence< ::sal_Int8 >& aData ) override;
+ virtual void SAL_CALL flush( ) override;
+ virtual void SAL_CALL closeOutput( ) 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;
+ // XStream
+ virtual css::uno::Reference< css::io::XInputStream > SAL_CALL getInputStream( ) override;
+ virtual css::uno::Reference< css::io::XOutputStream > SAL_CALL getOutputStream( ) override;
+ // XTruncate
+ virtual void SAL_CALL truncate() override;
+
+ // XPropertySet
+ virtual ::css::uno::Reference< ::css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override;
+ virtual void SAL_CALL setPropertyValue( const ::rtl::OUString& aPropertyName, const ::css::uno::Any& aValue ) override;
+ virtual ::css::uno::Any SAL_CALL getPropertyValue( const ::rtl::OUString& PropertyName ) override;
+ virtual void SAL_CALL addPropertyChangeListener( const ::rtl::OUString& aPropertyName, const ::css::uno::Reference< ::css::beans::XPropertyChangeListener >& xListener ) override;
+ virtual void SAL_CALL removePropertyChangeListener( const ::rtl::OUString& aPropertyName, const ::css::uno::Reference< ::css::beans::XPropertyChangeListener >& aListener ) override;
+ virtual void SAL_CALL addVetoableChangeListener( const ::rtl::OUString& PropertyName, const ::css::uno::Reference< ::css::beans::XVetoableChangeListener >& aListener ) override;
+ virtual void SAL_CALL removeVetoableChangeListener( const ::rtl::OUString& PropertyName, const ::css::uno::Reference< ::css::beans::XVetoableChangeListener >& aListener ) override;
+ // XFastPropertySet
+ virtual void SAL_CALL setFastPropertyValue( ::sal_Int32 nHandle, const ::css::uno::Any& aValue ) override;
+ virtual ::css::uno::Any SAL_CALL getFastPropertyValue( ::sal_Int32 nHandle ) override;
+ // XPropertyAccess
+ virtual ::css::uno::Sequence< ::css::beans::PropertyValue > SAL_CALL getPropertyValues() override;
+ virtual void SAL_CALL setPropertyValues( const ::css::uno::Sequence< ::css::beans::PropertyValue >& aProps ) override;
+
+
+ virtual ~OTempFileService () override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/localfilehelper.cxx b/unotools/source/ucbhelper/localfilehelper.cxx
new file mode 100644
index 0000000000..bdabd5f0ac
--- /dev/null
+++ b/unotools/source/ucbhelper/localfilehelper.cxx
@@ -0,0 +1,92 @@
+/* -*- 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/sdbc/XResultSet.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <comphelper/DirectoryHelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <sal/log.hxx>
+#include <unotools/localfilehelper.hxx>
+#include <rtl/ustring.hxx>
+#include <ucbhelper/content.hxx>
+#include <vector>
+
+using namespace ::osl;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::ucb;
+
+namespace utl
+{
+
+css::uno::Sequence < OUString > LocalFileHelper::GetFolderContents( const OUString& rFolder, bool bFolder )
+{
+ std::vector< OUString > vFiles;
+ try
+ {
+ ::ucbhelper::Content aCnt(
+ rFolder, Reference< XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ Reference< css::sdbc::XResultSet > xResultSet;
+ css::uno::Sequence< OUString > aProps { "Url" };
+
+ try
+ {
+ ::ucbhelper::ResultSetInclude eInclude = bFolder ? ::ucbhelper::INCLUDE_FOLDERS_AND_DOCUMENTS : ::ucbhelper::INCLUDE_DOCUMENTS_ONLY;
+ xResultSet = aCnt.createCursor( aProps, eInclude );
+ }
+ catch( css::ucb::CommandAbortedException& )
+ {
+ }
+ catch( Exception& )
+ {
+ }
+
+ if ( xResultSet.is() )
+ {
+ Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
+ try
+ {
+ while ( xResultSet->next() )
+ vFiles.push_back( xContentAccess->queryContentIdentifierString() );
+ }
+ catch( css::ucb::CommandAbortedException& )
+ {
+ }
+ catch( Exception& )
+ {
+ }
+ }
+ }
+ catch( Exception& )
+ {
+ }
+
+ return comphelper::containerToSequence(vFiles);
+}
+
+void removeTree(OUString const & url) {
+ const bool bError = comphelper::DirectoryHelper::deleteDirRecursively(url);
+ SAL_WARN_IF(bError, "desktop.app", "error removing directory " << url);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/progresshandlerwrap.cxx b/unotools/source/ucbhelper/progresshandlerwrap.cxx
new file mode 100644
index 0000000000..29c1067ec7
--- /dev/null
+++ b/unotools/source/ucbhelper/progresshandlerwrap.cxx
@@ -0,0 +1,88 @@
+/* -*- 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/progresshandlerwrap.hxx>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <utility>
+
+namespace utl
+{
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::ucb;
+
+ProgressHandlerWrap::ProgressHandlerWrap( css::uno::Reference< css::task::XStatusIndicator > xSI )
+: m_xStatusIndicator(std::move( xSI ))
+{
+}
+
+static bool getStatusFromAny_Impl( const Any& aAny, OUString& aText, sal_Int32& nNum )
+{
+ bool bNumIsSet = false;
+
+ Sequence< Any > aSetList;
+ if( aAny >>= aSetList )
+ for( const auto& rSet : std::as_const(aSetList) )
+ {
+ if( !bNumIsSet && ( rSet >>= nNum ) )
+ bNumIsSet = true;
+ else
+ aText.isEmpty() && ( rSet >>= aText );
+ }
+
+ return bNumIsSet;
+}
+
+void SAL_CALL ProgressHandlerWrap::push( const Any& Status )
+{
+ if( !m_xStatusIndicator.is() )
+ return;
+
+ OUString aText;
+ sal_Int32 nRange;
+
+ if( getStatusFromAny_Impl( Status, aText, nRange ) )
+ m_xStatusIndicator->start( aText, nRange );
+}
+
+void SAL_CALL ProgressHandlerWrap::update( const Any& Status )
+{
+ if( !m_xStatusIndicator.is() )
+ return;
+
+ OUString aText;
+ sal_Int32 nValue;
+
+ if( getStatusFromAny_Impl( Status, aText, nValue ) )
+ {
+ if( !aText.isEmpty() ) m_xStatusIndicator->setText( aText );
+ m_xStatusIndicator->setValue( nValue );
+ }
+}
+
+void SAL_CALL ProgressHandlerWrap::pop()
+{
+ if( m_xStatusIndicator.is() )
+ m_xStatusIndicator->end();
+}
+
+} // namespace utl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/tempfile.cxx b/unotools/source/ucbhelper/tempfile.cxx
new file mode 100644
index 0000000000..992ff8814d
--- /dev/null
+++ b/unotools/source/ucbhelper/tempfile.cxx
@@ -0,0 +1,808 @@
+/* -*- 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 <cassert>
+#include <utility>
+
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <unotools/tempfile.hxx>
+#include <rtl/ustring.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/mutex.hxx>
+#include <osl/detail/file.h>
+#include <osl/file.hxx>
+#include <tools/time.hxx>
+#include <tools/debug.hxx>
+#include <tools/Guid.hxx>
+#include <comphelper/DirectoryHelper.hxx>
+
+#ifdef UNX
+#include <unistd.h>
+#elif defined( _WIN32 )
+#include <process.h>
+#endif
+
+using namespace osl;
+
+namespace
+{
+ OUString gTempNameBase_Impl;
+}
+
+namespace utl
+{
+
+static OUString getParentName( std::u16string_view aFileName )
+{
+ size_t lastIndex = aFileName.rfind( '/' );
+ OUString aParent;
+
+ if (lastIndex != std::u16string_view::npos)
+ {
+ aParent = aFileName.substr(0, lastIndex);
+
+ if (aParent.endsWith(":") && aParent.getLength() == 6)
+ aParent += "/";
+
+ if (aParent.equalsIgnoreAsciiCase("file://"))
+ aParent = "file:///";
+ }
+
+ return aParent;
+}
+
+static bool ensuredir( const OUString& rUnqPath )
+{
+ OUString aPath;
+ if ( rUnqPath.isEmpty() )
+ return false;
+
+ // remove trailing slash
+ if ( rUnqPath.endsWith("/") )
+ aPath = rUnqPath.copy( 0, rUnqPath.getLength() - 1 );
+ else
+ aPath = rUnqPath;
+
+ // HACK: create directory on a mount point with nobrowse option
+ // returns ENOSYS in any case !!
+ osl::Directory aDirectory( aPath );
+ osl::FileBase::RC nError = aDirectory.open();
+ aDirectory.close();
+ if( nError == osl::File::E_None )
+ return true;
+
+ // try to create the directory
+ nError = osl::Directory::create( aPath );
+ bool bSuccess = ( nError == osl::File::E_None || nError == osl::FileBase::E_EXIST );
+ if( !bSuccess )
+ {
+ // perhaps parent(s) don't exist
+ OUString aParentDir = getParentName( aPath );
+ if ( aParentDir != aPath )
+ {
+ bSuccess = ensuredir( getParentName( aPath ) );
+
+ // After parent directory structure exists try it one's more
+ if ( bSuccess )
+ {
+ // Parent directory exists, retry creation of directory
+ nError = osl::Directory::create( aPath );
+ bSuccess =( nError == osl::File::E_None || nError == osl::FileBase::E_EXIST );
+ }
+ }
+ }
+
+ return bSuccess;
+}
+
+static OUString ConstructTempDir_Impl( const OUString* pParent, bool bCreateParentDirs )
+{
+ OUString aName;
+
+ // Ignore pParent on iOS. We don't want to create any temp files
+ // in the same directory where the document being edited is.
+#ifndef IOS
+ if ( pParent && !pParent->isEmpty() )
+ {
+ // test for valid filename
+ OUString aRet;
+ if ((osl::FileBase::getSystemPathFromFileURL(*pParent, aRet)
+ == osl::FileBase::E_None)
+ && (osl::FileBase::getFileURLFromSystemPath(aRet, aRet)
+ == osl::FileBase::E_None))
+ {
+ ::osl::DirectoryItem aItem;
+ sal_Int32 i = aRet.getLength();
+ if ( aRet[i-1] == '/' )
+ i--;
+
+ if ( DirectoryItem::get( aRet.copy(0, i), aItem ) == FileBase::E_None || bCreateParentDirs )
+ aName = aRet;
+ }
+ }
+#else
+ (void) pParent;
+ (void) bCreateParentDirs;
+#endif
+
+ if ( aName.isEmpty() )
+ {
+ if (gTempNameBase_Impl.isEmpty())
+ {
+ OUString ustrTempDirURL;
+ ::osl::FileBase::RC rc = ::osl::File::getTempDirURL(
+ ustrTempDirURL );
+ if (rc == ::osl::FileBase::E_None)
+ gTempNameBase_Impl = ustrTempDirURL;
+ ensuredir( aName );
+ }
+ // if no parent or invalid parent : use default directory
+ DBG_ASSERT( !gTempNameBase_Impl.isEmpty(), "No TempDir!" );
+ aName = gTempNameBase_Impl;
+ }
+
+ // Make sure that directory ends with a separator
+ if( !aName.isEmpty() && !aName.endsWith("/") )
+ aName += "/";
+
+ return aName;
+}
+
+namespace {
+
+class Tokens {
+public:
+ virtual bool next(OUString *) = 0;
+
+protected:
+ virtual ~Tokens() {} // avoid warnings
+};
+
+class SequentialTokens: public Tokens {
+public:
+ explicit SequentialTokens(bool showZero): m_value(0), m_show(showZero) {}
+
+ bool next(OUString * token) override {
+ assert(token != nullptr);
+ if (m_value == SAL_MAX_UINT32) {
+ return false;
+ }
+ *token = m_show ? OUString::number(m_value) : OUString();
+ ++m_value;
+ m_show = true;
+ return true;
+ }
+
+private:
+ sal_uInt32 m_value;
+ bool m_show;
+};
+
+class UniqueTokens: public Tokens {
+public:
+ UniqueTokens(): m_count(0) {}
+
+ bool next(OUString * token) override {
+ assert(token != nullptr);
+ // Because of the shared globalValue, no single instance of UniqueTokens
+ // is guaranteed to exhaustively test all 36^6 possible values, but stop
+ // after that many attempts anyway:
+ sal_uInt32 radix = 36;
+ sal_uInt32 max = radix * radix * radix * radix * radix * radix;
+ // 36^6 == 2'176'782'336 < SAL_MAX_UINT32 == 4'294'967'295
+ if (m_count == max) {
+ return false;
+ }
+ sal_uInt32 v;
+ {
+ osl::MutexGuard g(osl::Mutex::getGlobalMutex());
+ globalValue
+ = ((globalValue == SAL_MAX_UINT32
+ ? tools::Time::GetSystemTicks() : globalValue + 1)
+ % max);
+ v = globalValue;
+ }
+ *token = OUString::number(v, radix);
+ ++m_count;
+ return true;
+ }
+
+private:
+ static sal_uInt32 globalValue;
+
+ sal_uInt32 m_count;
+};
+
+}
+
+sal_uInt32 UniqueTokens::globalValue = SAL_MAX_UINT32;
+
+namespace
+{
+ class TempDirCreatedObserver : public DirectoryCreationObserver
+ {
+ public:
+ virtual void DirectoryCreated(const OUString& aDirectoryUrl) override
+ {
+ File::setAttributes( aDirectoryUrl, osl_File_Attribute_OwnRead |
+ osl_File_Attribute_OwnWrite | osl_File_Attribute_OwnExe );
+ };
+ };
+};
+
+static OUString lcl_createName(
+ std::u16string_view rLeadingChars, Tokens & tokens, std::u16string_view pExtension,
+ const OUString* pParent, bool bDirectory, bool bKeep, bool bLock,
+ bool bCreateParentDirs )
+{
+ OUString aName = ConstructTempDir_Impl( pParent, bCreateParentDirs );
+ if ( bCreateParentDirs )
+ {
+ size_t nOffset = rLeadingChars.rfind(u"/");
+ OUString aDirName;
+ if (std::u16string_view::npos != nOffset)
+ aDirName = aName + rLeadingChars.substr( 0, nOffset );
+ else
+ aDirName = aName;
+ TempDirCreatedObserver observer;
+ FileBase::RC err = Directory::createPath( aDirName, &observer );
+ if ( err != FileBase::E_None && err != FileBase::E_EXIST )
+ return OUString();
+ }
+ aName += rLeadingChars;
+
+ OUString token;
+ while (tokens.next(&token))
+ {
+ OUString aTmp( aName + token );
+ if ( !pExtension.empty() )
+ aTmp += pExtension;
+ else
+ aTmp += ".tmp";
+ if ( bDirectory )
+ {
+ FileBase::RC err = Directory::create(
+ aTmp,
+ (osl_File_OpenFlag_Read | osl_File_OpenFlag_Write
+ | osl_File_OpenFlag_Private));
+ if ( err == FileBase::E_None )
+ {
+ // !bKeep: only for creating a name, not a file or directory
+ if ( bKeep || Directory::remove( aTmp ) == FileBase::E_None )
+ return aTmp;
+ else
+ return OUString();
+ }
+ else if ( err != FileBase::E_EXIST )
+ // if f.e. name contains invalid chars stop trying to create dirs
+ return OUString();
+ }
+ else
+ {
+ DBG_ASSERT( bKeep, "Too expensive, use directory for creating name!" );
+ File aFile( aTmp );
+ FileBase::RC err = aFile.open(
+ osl_File_OpenFlag_Create | osl_File_OpenFlag_Private
+ | (bLock ? 0 : osl_File_OpenFlag_NoLock));
+ if ( err == FileBase::E_None || (bLock && err == FileBase::E_NOLCK) )
+ {
+ aFile.close();
+ return aTmp;
+ }
+ else if ( err != FileBase::E_EXIST )
+ {
+ // if f.e. name contains invalid chars stop trying to create dirs
+ // but if there is a folder with such name proceed further
+
+ DirectoryItem aTmpItem;
+ FileStatus aTmpStatus( osl_FileStatus_Mask_Type );
+ if ( DirectoryItem::get( aTmp, aTmpItem ) != FileBase::E_None
+ || aTmpItem.getFileStatus( aTmpStatus ) != FileBase::E_None
+ || aTmpStatus.getFileType() != FileStatus::Directory )
+ return OUString();
+ }
+ }
+ }
+ return OUString();
+}
+
+static OUString CreateTempName_Impl( const OUString* pParent, bool bKeep, bool bDir = true )
+{
+ OUString aEyeCatcher = "lu";
+#ifdef UNX
+#ifdef DBG_UTIL
+ const char* eye = getenv("LO_TESTNAME");
+ if(eye)
+ {
+ aEyeCatcher = OUString(eye, strlen(eye), RTL_TEXTENCODING_ASCII_US);
+ }
+#else
+ static const pid_t pid = getpid();
+ static const OUString aPidString = OUString::number(pid);
+ aEyeCatcher += aPidString;
+#endif
+#elif defined(_WIN32)
+ static const int pid = _getpid();
+ static const OUString aPidString = OUString::number(pid);
+ aEyeCatcher += aPidString;
+#endif
+ UniqueTokens t;
+ return lcl_createName( aEyeCatcher, t, u"", pParent, bDir, bKeep,
+ false, false);
+}
+
+static OUString CreateTempNameFast()
+{
+ OUString aEyeCatcher = "lu";
+#ifdef UNX
+#ifdef DBG_UTIL
+ const char* eye = getenv("LO_TESTNAME");
+ if(eye)
+ {
+ aEyeCatcher = OUString(eye, strlen(eye), RTL_TEXTENCODING_ASCII_US);
+ }
+#else
+ static const pid_t pid = getpid();
+ static const OUString aPidString = OUString::number(pid);
+ aEyeCatcher += aPidString;
+#endif
+#elif defined(_WIN32)
+ static const int pid = _getpid();
+ static const OUString aPidString = OUString::number(pid);
+ aEyeCatcher += aPidString;
+#endif
+
+ OUString aName = ConstructTempDir_Impl( /*pParent*/nullptr, /*bCreateParentDirs*/false ) + aEyeCatcher;
+
+ tools::Guid aGuid(tools::Guid::Generate);
+
+ return aName + aGuid.getOUString() + ".tmp" ;
+}
+
+OUString CreateTempName()
+{
+ OUString aName(CreateTempName_Impl( nullptr, false ));
+
+ // convert to file URL
+ OUString aTmp;
+ if ( !aName.isEmpty() )
+ FileBase::getSystemPathFromFileURL( aName, aTmp );
+ return aTmp;
+}
+
+TempFileFast::TempFileFast( )
+{
+}
+
+TempFileFast::TempFileFast(TempFileFast && other) noexcept :
+ mxStream(std::move(other.mxStream))
+{
+}
+
+TempFileFast::~TempFileFast()
+{
+ CloseStream();
+}
+
+SvStream* TempFileFast::GetStream( StreamMode eMode )
+{
+ if (!mxStream)
+ {
+ OUString aName = CreateTempNameFast();
+#ifdef _WIN32
+ mxStream.reset(new SvFileStream(aName, eMode | StreamMode::TEMPORARY | StreamMode::DELETE_ON_CLOSE));
+#else
+ mxStream.reset(new SvFileStream(aName, eMode | StreamMode::TEMPORARY));
+#endif
+ }
+ return mxStream.get();
+}
+
+void TempFileFast::CloseStream()
+{
+ if (mxStream)
+ {
+#if !defined _WIN32
+ OUString aName = mxStream->GetFileName();
+#endif
+ mxStream.reset();
+#ifdef _WIN32
+ // On Windows, the file is opened with FILE_FLAG_DELETE_ON_CLOSE, so it will delete as soon as the handle closes.
+ // On other platforms, we need to explicitly delete it.
+#else
+ if (!aName.isEmpty() && (osl::FileBase::getFileURLFromSystemPath(aName, aName) == osl::FileBase::E_None))
+ File::remove(aName);
+#endif
+ }
+}
+
+OUString CreateTempURL( const OUString* pParent, bool bDirectory )
+{
+ return CreateTempName_Impl( pParent, true, bDirectory );
+}
+
+OUString CreateTempURL( std::u16string_view rLeadingChars, bool _bStartWithZero,
+ std::u16string_view pExtension, const OUString* pParent,
+ bool bCreateParentDirs )
+{
+ SequentialTokens t(_bStartWithZero);
+ return lcl_createName( rLeadingChars, t, pExtension, pParent, false,
+ true, true, bCreateParentDirs );
+}
+
+TempFileNamed::TempFileNamed( const OUString* pParent, bool bDirectory )
+ : bIsDirectory( bDirectory )
+ , bKillingFileEnabled( false )
+{
+ aName = CreateTempName_Impl( pParent, true, bDirectory );
+}
+
+TempFileNamed::TempFileNamed( std::u16string_view rLeadingChars, bool _bStartWithZero,
+ std::u16string_view pExtension, const OUString* pParent,
+ bool bCreateParentDirs )
+ : bIsDirectory( false )
+ , bKillingFileEnabled( false )
+{
+ SequentialTokens t(_bStartWithZero);
+ aName = lcl_createName( rLeadingChars, t, pExtension, pParent, false,
+ true, true, bCreateParentDirs );
+}
+
+TempFileNamed::TempFileNamed(TempFileNamed && other) noexcept :
+ aName(std::move(other.aName)), pStream(std::move(other.pStream)), bIsDirectory(other.bIsDirectory),
+ bKillingFileEnabled(other.bKillingFileEnabled)
+{
+ other.bKillingFileEnabled = false;
+}
+
+TempFileNamed::~TempFileNamed()
+{
+ if ( !bKillingFileEnabled )
+ return;
+
+ pStream.reset();
+ if ( bIsDirectory )
+ {
+ comphelper::DirectoryHelper::deleteDirRecursively(aName);
+ }
+ else
+ {
+ File::remove( aName );
+ }
+}
+
+bool TempFileNamed::IsValid() const
+{
+ return !aName.isEmpty();
+}
+
+OUString TempFileNamed::GetFileName() const
+{
+ OUString aTmp;
+ FileBase::getSystemPathFromFileURL(aName, aTmp);
+ return aTmp;
+}
+
+OUString const & TempFileNamed::GetURL() const
+{
+ // if you request the URL, then you presumably want to access this via UCB,
+ // and UCB will want to open the file via a separate file handle, which means
+ // we have to make this file data actually hit disk. We do this here (and not
+ // elsewhere) to make the other (normal) paths fast. Flushing to disk
+ // really slows temp files down.
+ if (pStream)
+ pStream->Flush();
+ return aName;
+}
+
+SvStream* TempFileNamed::GetStream( StreamMode eMode )
+{
+ if (!pStream)
+ {
+ if (!aName.isEmpty())
+ pStream.reset(new SvFileStream(aName, eMode | StreamMode::TEMPORARY));
+ else
+ pStream.reset(new SvMemoryStream);
+ }
+
+ return pStream.get();
+}
+
+void TempFileNamed::CloseStream()
+{
+ pStream.reset();
+}
+
+OUString SetTempNameBaseDirectory( const OUString &rBaseName )
+{
+ if( rBaseName.isEmpty() )
+ return OUString();
+
+ OUString aUnqPath( rBaseName );
+
+ // remove trailing slash
+ if ( rBaseName.endsWith("/") )
+ aUnqPath = rBaseName.copy( 0, rBaseName.getLength() - 1 );
+
+ // try to create the directory
+ bool bRet = false;
+ osl::FileBase::RC err = osl::Directory::create( aUnqPath );
+ if ( err != FileBase::E_None && err != FileBase::E_EXIST )
+ // perhaps parent(s) don't exist
+ bRet = ensuredir( aUnqPath );
+ else
+ bRet = true;
+
+ // failure to create base directory means returning an empty string
+ OUString aTmp;
+ if ( bRet )
+ {
+ // append own internal directory
+ OUString &rTempNameBase_Impl = gTempNameBase_Impl;
+ rTempNameBase_Impl = rBaseName + "/";
+
+ TempFileNamed aBase( {}, true );
+ if ( aBase.IsValid() )
+ // use it in case of success
+ rTempNameBase_Impl = aBase.aName;
+
+ // return system path of used directory
+ FileBase::getSystemPathFromFileURL( rTempNameBase_Impl, aTmp );
+ }
+
+ return aTmp;
+}
+
+OUString GetTempNameBaseDirectory()
+{
+ return ConstructTempDir_Impl(nullptr, false);
+}
+
+
+TempFileFastService::TempFileFastService()
+: mbInClosed( false )
+, mbOutClosed( false )
+{
+ mpTempFile.emplace();
+ mpStream = mpTempFile->GetStream(StreamMode::READWRITE);
+}
+
+TempFileFastService::~TempFileFastService ()
+{
+}
+
+// XInputStream
+
+sal_Int32 SAL_CALL TempFileFastService::readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbInClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ checkConnected();
+ if (nBytesToRead < 0)
+ throw css::io::BufferSizeExceededException( OUString(), getXWeak());
+
+ if (aData.getLength() < nBytesToRead)
+ aData.realloc(nBytesToRead);
+
+ sal_uInt32 nRead = mpStream->ReadBytes(static_cast<void*>(aData.getArray()), nBytesToRead);
+ checkError();
+
+ if (nRead < o3tl::make_unsigned(aData.getLength()))
+ aData.realloc( nRead );
+
+ return nRead;
+}
+
+sal_Int32 SAL_CALL TempFileFastService::readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ {
+ std::unique_lock aGuard( maMutex );
+ if ( mbInClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ checkConnected();
+ checkError();
+
+ if (nMaxBytesToRead < 0)
+ throw css::io::BufferSizeExceededException( OUString(), getXWeak() );
+
+ if (mpStream->eof())
+ {
+ aData.realloc(0);
+ return 0;
+ }
+ }
+ return readBytes(aData, nMaxBytesToRead);
+}
+
+void SAL_CALL TempFileFastService::skipBytes( sal_Int32 nBytesToSkip )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbInClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ checkConnected();
+ checkError();
+ mpStream->SeekRel(nBytesToSkip);
+ checkError();
+}
+
+sal_Int32 SAL_CALL TempFileFastService::available()
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbInClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ checkConnected();
+
+ sal_Int64 nAvailable = mpStream->remainingSize();
+ checkError();
+
+ return std::min<sal_Int64>(SAL_MAX_INT32, nAvailable);
+}
+
+void SAL_CALL TempFileFastService::closeInput()
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbInClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ mbInClosed = true;
+
+ if ( mbOutClosed )
+ {
+ // stream will be deleted by TempFile implementation
+ mpStream = nullptr;
+ mpTempFile.reset();
+ }
+}
+
+// XOutputStream
+
+void SAL_CALL TempFileFastService::writeBytes( const css::uno::Sequence< sal_Int8 >& aData )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbOutClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ checkConnected();
+ sal_uInt32 nWritten = mpStream->WriteBytes(aData.getConstArray(), aData.getLength());
+ checkError();
+ if ( nWritten != static_cast<sal_uInt32>(aData.getLength()))
+ throw css::io::BufferSizeExceededException( OUString(), getXWeak() );
+}
+
+void SAL_CALL TempFileFastService::flush()
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbOutClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ checkConnected();
+ mpStream->Flush();
+ checkError();
+}
+
+void SAL_CALL TempFileFastService::closeOutput()
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbOutClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ mbOutClosed = true;
+ if (mpStream)
+ {
+ // so that if you then open the InputStream, you can read the content
+ mpStream->FlushBuffer();
+ mpStream->Seek(0);
+ }
+
+ if ( mbInClosed )
+ {
+ // stream will be deleted by TempFile implementation
+ mpStream = nullptr;
+ mpTempFile.reset();
+ }
+}
+
+void TempFileFastService::checkError() const
+{
+ if (!mpStream || mpStream->SvStream::GetError () != ERRCODE_NONE )
+ throw css::io::NotConnectedException ( OUString(), const_cast < TempFileFastService * > (this)->getXWeak() );
+}
+
+void TempFileFastService::checkConnected()
+{
+ if (!mpStream)
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+}
+
+// XSeekable
+
+void SAL_CALL TempFileFastService::seek( sal_Int64 nLocation )
+{
+ std::unique_lock aGuard( maMutex );
+ checkConnected();
+ checkError();
+ if ( nLocation < 0 )
+ throw css::lang::IllegalArgumentException();
+
+ sal_Int64 nNewLoc = mpStream->Seek(static_cast<sal_uInt32>(nLocation) );
+ if ( nNewLoc != nLocation )
+ throw css::lang::IllegalArgumentException();
+ checkError();
+}
+
+sal_Int64 SAL_CALL TempFileFastService::getPosition()
+{
+ std::unique_lock aGuard( maMutex );
+ checkConnected();
+
+ sal_uInt64 nPos = mpStream->Tell();
+ checkError();
+ return static_cast<sal_Int64>(nPos);
+}
+
+sal_Int64 SAL_CALL TempFileFastService::getLength()
+{
+ std::unique_lock aGuard( maMutex );
+ checkConnected();
+
+ checkError();
+
+ sal_Int64 nEndPos = mpStream->TellEnd();
+
+ return nEndPos;
+}
+
+// XStream
+
+css::uno::Reference< css::io::XInputStream > SAL_CALL TempFileFastService::getInputStream()
+{
+ return this;
+}
+
+css::uno::Reference< css::io::XOutputStream > SAL_CALL TempFileFastService::getOutputStream()
+{
+ return this;
+}
+
+// XTruncate
+
+void SAL_CALL TempFileFastService::truncate()
+{
+ std::unique_lock aGuard( maMutex );
+ checkConnected();
+ // SetStreamSize() call does not change the position
+ mpStream->Seek( 0 );
+ mpStream->SetStreamSize( 0 );
+ checkError();
+}
+
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/ucbhelper.cxx b/unotools/source/ucbhelper/ucbhelper.cxx
new file mode 100644
index 0000000000..aaa9d214bb
--- /dev/null
+++ b/unotools/source/ucbhelper/ucbhelper.cxx
@@ -0,0 +1,413 @@
+/* -*- 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 <cassert>
+#include <vector>
+
+#include <com/sun/star/sdbc/XResultSet.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/ContentInfo.hpp>
+#include <com/sun/star/ucb/ContentInfoAttribute.hpp>
+#include <com/sun/star/ucb/IOErrorCode.hpp>
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+#include <com/sun/star/ucb/NameClashException.hpp>
+#include <com/sun/star/ucb/UniversalContentBroker.hpp>
+#include <com/sun/star/ucb/XCommandEnvironment.hpp>
+#include <com/sun/star/ucb/XContentAccess.hpp>
+#include <com/sun/star/ucb/XUniversalContentBroker.hpp>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/simplefileaccessinteraction.hxx>
+#include <osl/file.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <tools/datetime.hxx>
+#include <tools/urlobj.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <ucbhelper/commandenvironment.hxx>
+#include <ucbhelper/content.hxx>
+#include <unotools/ucbhelper.hxx>
+
+namespace com::sun::star::ucb { class XProgressHandler; }
+namespace com::sun::star::uno { class XComponentContext; }
+namespace com::sun::star::util { struct DateTime; }
+
+namespace {
+
+OUString canonic(OUString const & url) {
+ INetURLObject o(url);
+ SAL_WARN_IF(o.HasError(), "unotools.ucbhelper", "Invalid URL \"" << url << '"');
+ return o.GetMainURL(INetURLObject::DecodeMechanism::NONE);
+}
+
+ucbhelper::Content content(OUString const & url) {
+ return ucbhelper::Content(
+ canonic(url),
+ utl::UCBContentHelper::getDefaultCommandEnvironment(),
+ comphelper::getProcessComponentContext());
+}
+
+ucbhelper::Content content(INetURLObject const & url) {
+ return ucbhelper::Content(
+ url.GetMainURL(INetURLObject::DecodeMechanism::NONE),
+ utl::UCBContentHelper::getDefaultCommandEnvironment(),
+ comphelper::getProcessComponentContext());
+}
+
+std::vector<OUString> getContents(OUString const & url) {
+ try {
+ std::vector<OUString> cs;
+ ucbhelper::Content c(content(url));
+ css::uno::Sequence<OUString> args { "Title" };
+ css::uno::Reference<css::sdbc::XResultSet> res( c.createCursor(args), css::uno::UNO_SET_THROW);
+ css::uno::Reference<css::ucb::XContentAccess> acc( res, css::uno::UNO_QUERY_THROW);
+ while (res->next()) {
+ cs.push_back(acc->queryContentIdentifierString());
+ }
+ return cs;
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "getContents(" << url << ")");
+ return std::vector<OUString>();
+ }
+}
+
+OUString getCasePreservingUrl(const INetURLObject& url) {
+ return
+ content(url).executeCommand(
+ "getCasePreservingURL",
+ css::uno::Any()).
+ get<OUString>();
+}
+
+DateTime convert(css::util::DateTime const & dt) {
+ return DateTime(dt);
+}
+
+}
+
+css::uno::Reference< css::ucb::XCommandEnvironment > utl::UCBContentHelper::getDefaultCommandEnvironment()
+{
+ css::uno::Reference< css::task::XInteractionHandler > xIH(
+ css::task::InteractionHandler::createWithParent(
+ comphelper::getProcessComponentContext(), nullptr ) );
+
+ css::uno::Reference< css::ucb::XProgressHandler > xProgress;
+ rtl::Reference<ucbhelper::CommandEnvironment> pCommandEnv =
+ new ::ucbhelper::CommandEnvironment(
+ new comphelper::SimpleFileAccessInteraction( xIH ), xProgress );
+
+ return pCommandEnv;
+}
+
+bool utl::UCBContentHelper::IsDocument(OUString const & url) {
+ try {
+ return content(url).isDocument();
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::IsDocument(" << url << ")");
+ return false;
+ }
+}
+
+css::uno::Any utl::UCBContentHelper::GetProperty(
+ OUString const & url, OUString const & property)
+{
+ try {
+ return content(url).getPropertyValue(property);
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::GetProperty(" << url << ", " << property << ")");
+ return css::uno::Any();
+ }
+}
+
+bool utl::UCBContentHelper::IsFolder(OUString const & url) {
+ try {
+ return content(url).isFolder();
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::IsFolder(" << url << ")");
+ return false;
+ }
+}
+
+bool utl::UCBContentHelper::GetTitle(
+ OUString const & url, OUString * title)
+{
+ assert(title != nullptr);
+ try {
+ return content(url).getPropertyValue("Title") >>= *title;
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::GetTitle(" << url << ")");
+ return false;
+ }
+}
+
+bool utl::UCBContentHelper::Kill(OUString const & url) {
+ try {
+ content(url).executeCommand(
+ "delete",
+ css::uno::Any(true));
+ return true;
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION("unotools.ucbhelper", "UCBContentHelper::Kill(" << url << ")");
+ return false;
+ }
+}
+
+bool utl::UCBContentHelper::MakeFolder(
+ ucbhelper::Content & parent, OUString const & title,
+ ucbhelper::Content & result)
+{
+ bool exists = false;
+ try {
+ const css::uno::Sequence<css::ucb::ContentInfo> info(
+ parent.queryCreatableContentsInfo());
+ for (const auto& rInfo : info) {
+ // Simply look for the first KIND_FOLDER:
+ if ((rInfo.Attributes
+ & css::ucb::ContentInfoAttribute::KIND_FOLDER)
+ != 0)
+ {
+ // Make sure the only required bootstrap property is "Title":
+ if ( rInfo.Properties.getLength() != 1 || rInfo.Properties[0].Name != "Title" )
+ {
+ continue;
+ }
+ if (parent.insertNewContent(rInfo.Type, { "Title" }, { css::uno::Any(title) }, result))
+ {
+ return true;
+ }
+ }
+ }
+ } catch (css::ucb::InteractiveIOException const & e) {
+ if (e.Code == css::ucb::IOErrorCode_ALREADY_EXISTING) {
+ exists = true;
+ } else {
+ TOOLS_INFO_EXCEPTION(
+ "unotools.ucbhelper",
+ "UCBContentHelper::MakeFolder(" << title << ")");
+ }
+ } catch (css::ucb::NameClashException const &) {
+ exists = true;
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION(
+ "unotools.ucbhelper",
+ "UCBContentHelper::MakeFolder(" << title << ") ");
+ }
+ if (exists) {
+ INetURLObject o(parent.getURL());
+ o.Append(title);
+ result = content(o);
+ return true;
+ } else {
+ return false;
+ }
+}
+
+bool utl::UCBContentHelper::IsYounger(
+ OUString const & younger, OUString const & older)
+{
+ try {
+ return
+ convert(
+ content(younger).getPropertyValue(
+ "DateModified").
+ get<css::util::DateTime>())
+ > convert(
+ content(older).getPropertyValue(
+ "DateModified").
+ get<css::util::DateTime>());
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION(
+ "unotools.ucbhelper",
+ "UCBContentHelper::IsYounger(" << younger << ", " << older << ")");
+ return false;
+ }
+}
+
+bool utl::UCBContentHelper::Exists(OUString const & url) {
+ OUString pathname;
+ if (osl::FileBase::getSystemPathFromFileURL(url, pathname)
+ == osl::FileBase::E_None)
+ {
+ // Try to create a directory entry for the given URL:
+ OUString url2;
+ if (osl::FileBase::getFileURLFromSystemPath(pathname, url2)
+ == osl::FileBase::E_None)
+ {
+ // #106526 osl_getDirectoryItem is an existence check, no further
+ // osl_getFileStatus call necessary:
+ osl::DirectoryItem item;
+ return osl::DirectoryItem::get(url2, item) == osl::FileBase::E_None;
+ } else {
+ return false;
+ }
+ } else {
+ // Divide URL into folder and name part:
+ INetURLObject o(url);
+ OUString name(
+ o.getName(
+ INetURLObject::LAST_SEGMENT, true,
+ INetURLObject::DecodeMechanism::WithCharset));
+ o.removeSegment();
+ o.removeFinalSlash();
+ std::vector<OUString> cs(
+ getContents(o.GetMainURL(INetURLObject::DecodeMechanism::NONE)));
+ return std::any_of(cs.begin(), cs.end(),
+ [&name](const OUString& rItem) {
+ return INetURLObject(rItem).
+ getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset).
+ equalsIgnoreAsciiCase(name); });
+ }
+}
+
+bool utl::UCBContentHelper::IsSubPath(
+ OUString const & parent, OUString const & child)
+{
+ // The comparison is done in the following way:
+ // - First, compare case sensitively
+ // - If names are different, try a fallback comparing case insensitively
+ // - If the last comparison succeeded, get case preserving normalized names
+ // for the files and compare them
+ // (The second step is required because retrieving the normalized names
+ // might be very expensive in some cases.)
+ INetURLObject candidate(child);
+ INetURLObject folder(parent);
+ if (candidate.GetProtocol() != folder.GetProtocol()) {
+ return false;
+ }
+ INetURLObject candidateLower(child.toAsciiLowerCase());
+ INetURLObject folderLower(parent.toAsciiLowerCase());
+ try {
+ INetURLObject tmp;
+ do {
+ if (candidate == folder
+ || (candidate.GetProtocol() == INetProtocol::File
+ && candidateLower == folderLower
+ && (getCasePreservingUrl(candidate)
+ == getCasePreservingUrl(folder))))
+ {
+ return true;
+ }
+ tmp = candidate;
+ } while (candidate.removeSegment() && candidateLower.removeSegment()
+ && candidate != tmp);
+ // INetURLObject::removeSegment sometimes returns true without
+ // modifying the URL, e.g., in case of "file:///"
+ } catch (css::uno::RuntimeException const &) {
+ throw;
+ } catch (css::ucb::CommandAbortedException const &) {
+ assert(false && "this cannot happen");
+ throw;
+ } catch (css::uno::Exception const &) {
+ TOOLS_INFO_EXCEPTION(
+ "unotools.ucbhelper",
+ "UCBContentHelper::IsSubPath(" << parent << ", " << child << ")");
+ }
+ return false;
+}
+
+bool utl::UCBContentHelper::EqualURLs(
+ OUString const & url1, OUString const & url2)
+{
+ if (url1.isEmpty() || url2.isEmpty()) {
+ return false;
+ }
+ css::uno::Reference< css::ucb::XUniversalContentBroker > ucb(
+ css::ucb::UniversalContentBroker::create(
+ comphelper::getProcessComponentContext()));
+ return
+ ucb->compareContentIds(
+ ucb->createContentIdentifier(canonic(url1)),
+ ucb->createContentIdentifier(canonic(url2)))
+ == 0;
+}
+
+bool utl::UCBContentHelper::ensureFolder(
+ const css::uno::Reference< css::uno::XComponentContext >& xCtx,
+ const css::uno::Reference< css::ucb::XCommandEnvironment >& xEnv,
+ std::u16string_view rFolder, ucbhelper::Content & result) noexcept
+{
+ try
+ {
+ INetURLObject aURL( rFolder );
+ OUString aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset );
+ aURL.removeSegment();
+ ::ucbhelper::Content aParent;
+
+ if ( ::ucbhelper::Content::create( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ),
+ xEnv, xCtx, aParent ) )
+ {
+ return ::utl::UCBContentHelper::MakeFolder(aParent, aTitle, result);
+ }
+ }
+ catch (...)
+ {
+ }
+
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/ucblockbytes.cxx b/unotools/source/ucbhelper/ucblockbytes.cxx
new file mode 100644
index 0000000000..182b1f674e
--- /dev/null
+++ b/unotools/source/ucbhelper/ucblockbytes.cxx
@@ -0,0 +1,1338 @@
+/* -*- 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 "ucblockbytes.hxx"
+
+#include <sal/log.hxx>
+#include <comphelper/processfactory.hxx>
+#include <salhelper/condition.hxx>
+#include <osl/thread.hxx>
+#include <osl/diagnose.h>
+#include <tools/urlobj.hxx>
+#include <tools/solar.h>
+#include <ucbhelper/interactionrequest.hxx>
+#include <com/sun/star/lang/XUnoTunnel.hpp>
+#include <com/sun/star/task/XInteractionAbort.hpp>
+#include <com/sun/star/ucb/InteractiveNetworkConnectException.hpp>
+#include <com/sun/star/ucb/CommandFailedException.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/UnsupportedDataSinkException.hpp>
+#include <com/sun/star/ucb/InteractiveIOException.hpp>
+#include <com/sun/star/ucb/XContentIdentifier.hpp>
+#include <com/sun/star/ucb/XContent.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XActiveDataStreamer.hpp>
+#include <com/sun/star/io/TempFile.hpp>
+#include <com/sun/star/ucb/XCommandProcessor.hpp>
+#include <com/sun/star/task/XInteractionHandler.hpp>
+#include <com/sun/star/ucb/OpenCommandArgument2.hpp>
+#include <com/sun/star/ucb/PostCommandArgument2.hpp>
+#include <com/sun/star/ucb/OpenMode.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertiesChangeNotifier.hpp>
+#include <com/sun/star/beans/XPropertiesChangeListener.hpp>
+#include <com/sun/star/io/XActiveDataSink.hpp>
+#include <com/sun/star/io/XActiveDataControl.hpp>
+#include <com/sun/star/io/XSeekable.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <tools/debug.hxx>
+#include <com/sun/star/io/XTruncate.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+
+#include <comphelper/bytereader.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <ucbhelper/content.hxx>
+#include <unotools/tempfile.hxx>
+#include <mutex>
+#include <utility>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+namespace utl
+{
+
+namespace {
+
+/**
+ Helper class for getting a XInputStream when opening a content
+ */
+class UcbDataSink_Impl : public ::cppu::WeakImplHelper< XActiveDataControl, XActiveDataSink >
+{
+ UcbLockBytesRef m_xLockBytes;
+
+public:
+ explicit UcbDataSink_Impl( UcbLockBytes* pLockBytes )
+ : m_xLockBytes( pLockBytes )
+ {}
+
+ // XActiveDataControl.
+ virtual void SAL_CALL addListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
+ virtual void SAL_CALL removeListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
+ virtual void SAL_CALL start() override {}
+ virtual void SAL_CALL terminate() override
+ { m_xLockBytes->terminate(); }
+
+ // XActiveDataSink.
+ virtual void SAL_CALL setInputStream ( const Reference<XInputStream> &rxInputStream) override
+ { m_xLockBytes->setInputStream(rxInputStream); }
+ virtual Reference<XInputStream> SAL_CALL getInputStream() override
+ { return m_xLockBytes->getInputStream(); }
+};
+
+/**
+ Helper class for getting a XStream when opening a content
+ */
+class UcbStreamer_Impl : public ::cppu::WeakImplHelper< XActiveDataStreamer, XActiveDataControl >
+{
+ Reference < XStream > m_xStream;
+ UcbLockBytesRef m_xLockBytes;
+
+public:
+ explicit UcbStreamer_Impl( UcbLockBytes* pLockBytes )
+ : m_xLockBytes( pLockBytes )
+ {}
+
+ // XActiveDataControl.
+ virtual void SAL_CALL addListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
+ virtual void SAL_CALL removeListener ( const Reference<XStreamListener> &/*rxListener*/) override {}
+ virtual void SAL_CALL start() override {}
+ virtual void SAL_CALL terminate() override
+ { m_xLockBytes->terminate(); }
+
+ // XActiveDataStreamer
+ virtual void SAL_CALL setStream( const Reference< XStream >& aStream ) override
+ { m_xStream = aStream; m_xLockBytes->setStream( aStream ); }
+ virtual Reference< XStream > SAL_CALL getStream() override
+ { return m_xStream; }
+};
+
+/**
+ Helper class for managing interactions and progress when executing UCB commands
+ */
+class UcbTaskEnvironment : public ::cppu::WeakImplHelper< XCommandEnvironment >
+{
+ Reference< XInteractionHandler > m_xInteractionHandler;
+ Reference< XProgressHandler > m_xProgressHandler;
+
+public:
+ UcbTaskEnvironment( const Reference< XInteractionHandler>& rxInteractionHandler,
+ const Reference< XProgressHandler>& rxProgressHandler )
+ : m_xInteractionHandler( rxInteractionHandler )
+ , m_xProgressHandler( rxProgressHandler )
+ {}
+
+ virtual Reference<XInteractionHandler> SAL_CALL getInteractionHandler() override
+ { return m_xInteractionHandler; }
+
+ virtual Reference<XProgressHandler> SAL_CALL getProgressHandler() override
+ { return m_xProgressHandler; }
+};
+
+/**
+ Helper class for property change notifies when executing UCB commands
+*/
+class UcbPropertiesChangeListener_Impl : public ::cppu::WeakImplHelper< XPropertiesChangeListener >
+{
+public:
+ UcbLockBytesRef m_xLockBytes;
+
+ explicit UcbPropertiesChangeListener_Impl( UcbLockBytesRef xRef )
+ : m_xLockBytes(std::move( xRef ))
+ {}
+
+ virtual void SAL_CALL disposing ( const EventObject &/*rEvent*/) override {}
+ virtual void SAL_CALL propertiesChange ( const Sequence<PropertyChangeEvent> &rEvent) override;
+};
+
+}
+
+void SAL_CALL UcbPropertiesChangeListener_Impl::propertiesChange ( const Sequence<PropertyChangeEvent> &rEvent)
+{
+ for (const auto& rPropChangeEvent : rEvent)
+ {
+ if (rPropChangeEvent.PropertyName == "DocumentHeader")
+ {
+ m_xLockBytes->SetStreamValid();
+ }
+ }
+}
+
+namespace {
+
+class Moderator
+ : public osl::Thread
+{
+ // usage restriction:
+ // It might be possible, that the call to the interactionhandler and/or
+ // progresshandler is done asynchronously, while the 'execute' simply
+ // returns. This would imply that these class must be refcounted!!!
+
+public:
+ /// @throws ContentCreationException
+ /// @throws RuntimeException
+ Moderator(
+ Reference < XContent > const & xContent,
+ Reference < XInteractionHandler > const & xInteract,
+ Command aArg
+ );
+
+ enum class ResultType {
+ NORESULT,
+
+ INTERACTIONREQUEST, // reply expected
+
+ INPUTSTREAM,
+ STREAM,
+
+ RESULT,
+ TIMEDOUT,
+ COMMANDABORTED,
+ COMMANDFAILED,
+ INTERACTIVEIO,
+ UNSUPPORTED,
+ GENERAL
+ };
+
+ class ConditionRes
+ : public salhelper::Condition
+ {
+ public:
+ ConditionRes(osl::Mutex& aMutex,Moderator& aModerator)
+ : salhelper::Condition(aMutex),
+ m_aModerator(aModerator)
+ {
+ }
+
+ protected:
+ bool applies() const override {
+ return m_aModerator.m_aResultType != ResultType::NORESULT;
+ }
+
+ private:
+ Moderator& m_aModerator;
+ };
+
+ struct Result {
+ ResultType type;
+ Any result;
+ IOErrorCode ioErrorCode;
+ };
+
+ Result getResult(const sal_uInt32 milliSec);
+
+ enum ReplyType {
+ NOREPLY,
+ EXIT,
+ REQUESTHANDLED
+ };
+
+ class ConditionRep
+ : public salhelper::Condition
+ {
+ public:
+ ConditionRep(osl::Mutex& aMutex,Moderator& aModerator)
+ : salhelper::Condition(aMutex),
+ m_aModerator(aModerator)
+ {
+ }
+
+ protected:
+ bool applies() const override {
+ return m_aModerator.m_aReplyType != NOREPLY;
+ }
+
+ private:
+ Moderator& m_aModerator;
+ };
+
+ void setReply(ReplyType);
+
+ void handle( const Reference<XInteractionRequest >& Request );
+
+ void setStream(const Reference< XStream >& aStream);
+ void setInputStream(const Reference<XInputStream> &rxInputStream);
+
+protected:
+ virtual void SAL_CALL run() override;
+ virtual void SAL_CALL onTerminated() override;
+
+private:
+ osl::Mutex m_aMutex;
+
+ friend class ConditionRes;
+
+ ConditionRes m_aRes;
+ ResultType m_aResultType;
+ IOErrorCode m_nIOErrorCode;
+ Any m_aResult;
+
+ friend class ConditionRep;
+
+ ConditionRep m_aRep;
+ ReplyType m_aReplyType;
+
+ Command m_aArg;
+ ::ucbhelper::Content m_aContent;
+};
+
+class ModeratorsActiveDataStreamer
+ : public ::cppu::WeakImplHelper<XActiveDataStreamer>
+{
+public:
+
+ explicit ModeratorsActiveDataStreamer(Moderator &theModerator);
+
+ // XActiveDataStreamer
+ virtual void SAL_CALL
+ setStream(
+ const Reference< XStream >& aStream
+ ) override;
+
+ virtual Reference<XStream> SAL_CALL getStream () override
+ {
+ std::scoped_lock aGuard(m_aMutex);
+ return m_xStream;
+ }
+
+private:
+ Moderator& m_aModerator;
+
+ std::mutex m_aMutex;
+ Reference<XStream> m_xStream;
+};
+
+class ModeratorsActiveDataSink
+ : public ::cppu::WeakImplHelper<XActiveDataSink>
+{
+public:
+
+ explicit ModeratorsActiveDataSink(Moderator &theModerator);
+
+ // XActiveDataSink.
+ virtual void SAL_CALL
+ setInputStream (
+ const Reference<XInputStream> &rxInputStream
+ ) override;
+
+ virtual Reference<XInputStream> SAL_CALL getInputStream() override
+ {
+ std::scoped_lock aGuard(m_aMutex);
+ return m_xStream;
+ }
+
+private:
+ Moderator& m_aModerator;
+ std::mutex m_aMutex;
+ Reference<XInputStream> m_xStream;
+};
+
+}
+
+ModeratorsActiveDataSink::ModeratorsActiveDataSink(Moderator &theModerator)
+ : m_aModerator(theModerator)
+{
+}
+
+// XActiveDataSink.
+void SAL_CALL
+ModeratorsActiveDataSink::setInputStream (
+ const Reference<XInputStream> &rxInputStream
+)
+{
+ m_aModerator.setInputStream(rxInputStream);
+ std::scoped_lock aGuard(m_aMutex);
+ m_xStream = rxInputStream;
+}
+
+ModeratorsActiveDataStreamer::ModeratorsActiveDataStreamer(
+ Moderator &theModerator
+)
+ : m_aModerator(theModerator)
+{
+}
+
+// XActiveDataStreamer.
+void SAL_CALL
+ModeratorsActiveDataStreamer::setStream (
+ const Reference<XStream> &rxStream
+)
+{
+ m_aModerator.setStream(rxStream);
+ std::scoped_lock aGuard(m_aMutex);
+ m_xStream = rxStream;
+}
+
+namespace {
+
+class ModeratorsInteractionHandler
+ : public ::cppu::WeakImplHelper<XInteractionHandler>
+{
+public:
+
+ explicit ModeratorsInteractionHandler(Moderator &theModerator);
+
+ virtual void SAL_CALL
+ handle( const Reference<XInteractionRequest >& Request ) override;
+
+private:
+
+ Moderator& m_aModerator;
+};
+
+}
+
+ModeratorsInteractionHandler::ModeratorsInteractionHandler(
+ Moderator &aModerator)
+ : m_aModerator(aModerator)
+{
+}
+
+void SAL_CALL
+ModeratorsInteractionHandler::handle(
+ const Reference<XInteractionRequest >& Request
+)
+{
+ // wakes up the mainthread
+ m_aModerator.handle(Request);
+}
+
+Moderator::Moderator(
+ Reference < XContent > const & xContent,
+ Reference < XInteractionHandler > const & xInteract,
+ Command aArg
+)
+ : m_aRes(m_aMutex,*this),
+ m_aResultType(ResultType::NORESULT),
+ m_nIOErrorCode(IOErrorCode_ABORT),
+ m_aRep(m_aMutex,*this),
+ m_aReplyType(NOREPLY),
+ m_aArg(std::move(aArg)),
+ m_aContent(
+ xContent,
+ new UcbTaskEnvironment(
+ xInteract.is() ? new ModeratorsInteractionHandler(*this) : nullptr,
+ nullptr),
+ comphelper::getProcessComponentContext())
+{
+ // now exchange the whole data sink stuff
+ // with a thread safe version
+
+ Reference<XInterface> *pxSink = nullptr;
+
+ PostCommandArgument2 aPostArg;
+ OpenCommandArgument2 aOpenArg;
+
+ int dec(2);
+ if(m_aArg.Argument >>= aPostArg) {
+ pxSink = &aPostArg.Sink;
+ dec = 0;
+ }
+ else if(m_aArg.Argument >>= aOpenArg) {
+ pxSink = &aOpenArg.Sink;
+ dec = 1;
+ }
+
+ if(dec ==2)
+ throw ContentCreationException();
+
+ Reference < XActiveDataSink > xActiveSink(*pxSink,UNO_QUERY);
+ if(xActiveSink.is())
+ pxSink->set(getXWeak(new ModeratorsActiveDataSink(*this)));
+
+ Reference<XActiveDataStreamer> xStreamer( *pxSink, UNO_QUERY );
+ if ( xStreamer.is() )
+ pxSink->set(getXWeak(new ModeratorsActiveDataStreamer(*this)));
+
+ if(dec == 0)
+ m_aArg.Argument <<= aPostArg;
+ else if(dec == 1)
+ m_aArg.Argument <<= aOpenArg;
+}
+
+Moderator::Result Moderator::getResult(const sal_uInt32 milliSec)
+{
+ Result ret;
+ try {
+ salhelper::ConditionWaiter aWaiter(m_aRes,milliSec);
+ ret.type = m_aResultType;
+ ret.result = m_aResult;
+ ret.ioErrorCode = m_nIOErrorCode;
+
+ // reset
+ m_aResultType = ResultType::NORESULT;
+ }
+ catch (const salhelper::ConditionWaiter::timedout&)
+ {
+ ret.type = ResultType::TIMEDOUT;
+ }
+
+ return ret;
+}
+
+void Moderator::setReply(ReplyType aReplyType )
+{
+ salhelper::ConditionModifier aMod(m_aRep);
+ m_aReplyType = aReplyType;
+}
+
+void Moderator::handle( const Reference<XInteractionRequest >& Request )
+{
+ ReplyType aReplyType;
+
+ do {
+ {
+ salhelper::ConditionModifier aMod(m_aRes);
+ m_aResultType = ResultType::INTERACTIONREQUEST;
+ m_aResult <<= Request;
+ }
+
+ {
+ salhelper::ConditionWaiter aWait(m_aRep);
+ aReplyType = m_aReplyType;
+
+ // reset
+ m_aReplyType = NOREPLY;
+ }
+
+ if(aReplyType == EXIT) {
+ const Sequence<Reference<XInteractionContinuation> > aSeq(
+ Request->getContinuations());
+ for(const auto& rContinuation : aSeq) {
+ Reference<XInteractionAbort> aRef(rContinuation,UNO_QUERY);
+ if(aRef.is()) {
+ aRef->select();
+ }
+ }
+
+ // resignal the exit condition
+ setReply(EXIT);
+ break;
+ }
+ } while(aReplyType != REQUESTHANDLED);
+}
+
+void Moderator::setStream(const Reference< XStream >& aStream)
+{
+ {
+ salhelper::ConditionModifier aMod(m_aRes);
+ m_aResultType = ResultType::STREAM;
+ m_aResult <<= aStream;
+ }
+ ReplyType aReplyType;
+ {
+ salhelper::ConditionWaiter aWait(m_aRep);
+ aReplyType = m_aReplyType;
+ m_aReplyType = NOREPLY;
+ }
+ if(aReplyType == EXIT)
+ setReply(EXIT);
+}
+
+void Moderator::setInputStream(const Reference<XInputStream> &rxInputStream)
+{
+ {
+ salhelper::ConditionModifier aMod(m_aRes);
+ m_aResultType = ResultType::INPUTSTREAM;
+ m_aResult <<= rxInputStream;
+ }
+ ReplyType aReplyType;
+ {
+ salhelper::ConditionWaiter aWait(m_aRep);
+ aReplyType = m_aReplyType;
+ m_aReplyType = NOREPLY;
+ }
+ if(aReplyType == EXIT)
+ setReply(EXIT);
+}
+
+void SAL_CALL Moderator::run()
+{
+ osl_setThreadName("utl::Moderator");
+
+ ResultType aResultType;
+ Any aResult;
+ IOErrorCode nIOErrorCode = IOErrorCode_ABORT;
+
+ try
+ {
+ aResult = m_aContent.executeCommand(m_aArg.Name,m_aArg.Argument);
+ aResultType = ResultType::RESULT;
+ }
+ catch (const CommandAbortedException&)
+ {
+ aResultType = ResultType::COMMANDABORTED;
+ }
+ catch (const CommandFailedException&)
+ {
+ aResultType = ResultType::COMMANDFAILED;
+ }
+ catch (const InteractiveIOException& r)
+ {
+ nIOErrorCode = r.Code;
+ aResultType = ResultType::INTERACTIVEIO;
+ }
+ catch (const UnsupportedDataSinkException &)
+ {
+ aResultType = ResultType::UNSUPPORTED;
+ }
+ catch (const Exception&)
+ {
+ aResultType = ResultType::GENERAL;
+ }
+
+ {
+ salhelper::ConditionModifier aMod(m_aRes);
+ m_aResultType = aResultType;
+ m_aResult = aResult;
+ m_nIOErrorCode = nIOErrorCode;
+ }
+}
+
+void SAL_CALL Moderator::onTerminated()
+{
+ {
+ salhelper::ConditionWaiter aWaiter(m_aRep);
+ }
+ delete this;
+}
+
+/**
+ Function for opening UCB contents synchronously,
+ but with handled timeout;
+*/
+static bool UCBOpenContentSync_(
+ const UcbLockBytesRef& xLockBytes,
+ const Reference < XContent >& xContent,
+ const Command& rArg,
+ const Reference < XInterface >& xSink,
+ const Reference < XInteractionHandler >& xInteract );
+
+static bool UCBOpenContentSync(
+ const UcbLockBytesRef& xLockBytes,
+ Reference < XContent > const & xContent,
+ const Command& rArg,
+ const Reference < XInterface >& xSink,
+ Reference < XInteractionHandler > const & xInteract )
+{
+ // http protocol must be handled in a special way:
+ // during the opening process the input stream may change
+ // only the last inputstream after notifying the document
+ // headers is valid
+
+ Reference<XContentIdentifier> xContId(
+ xContent.is() ? xContent->getIdentifier() : nullptr );
+
+ OUString aScheme;
+ if(xContId.is())
+ aScheme = xContId->getContentProviderScheme();
+
+ // now determine whether we use a timeout or not;
+ if( ! aScheme.equalsIgnoreAsciiCase("http") &&
+ ! aScheme.equalsIgnoreAsciiCase("https") &&
+ ! aScheme.equalsIgnoreAsciiCase("vnd.sun.star.webdav") &&
+ ! aScheme.equalsIgnoreAsciiCase("vnd.sun.star.webdavs") &&
+ ! aScheme.equalsIgnoreAsciiCase("ftp"))
+ return UCBOpenContentSync_(
+ xLockBytes,xContent,rArg,xSink,xInteract);
+
+ if ( !aScheme.equalsIgnoreAsciiCase( "http" ) &&
+ !aScheme.equalsIgnoreAsciiCase( "https" ) )
+ xLockBytes->SetStreamValid();
+
+ Reference< XPropertiesChangeListener > xListener;
+ Reference< XPropertiesChangeNotifier > xProps(xContent,UNO_QUERY);
+ if(xProps.is()) {
+ xListener =
+ new UcbPropertiesChangeListener_Impl(xLockBytes);
+ xProps->addPropertiesChangeListener(
+ Sequence< OUString >(),
+ xListener);
+ }
+
+ bool bException(false);
+ bool bAborted(false);
+ bool bResultAchieved(false);
+
+ Moderator* pMod = nullptr;
+ try
+ {
+ pMod = new Moderator(xContent,xInteract,rArg);
+ pMod->create();
+ //TODO: a protocol is missing how to join with the launched thread before exit(3), to
+ // ensure the thread is no longer relying on any infrastructure while that
+ // infrastructure is being shut down in atexit handlers
+ }
+ catch (const ContentCreationException&)
+ {
+ bResultAchieved = bException = true;
+ xLockBytes->SetError( ERRCODE_IO_GENERAL );
+ }
+
+ sal_uInt32 nTimeout(5000); // initially 5000 milliSec
+ while(!bResultAchieved) {
+
+ // try to get the result for with timeout
+ Moderator::Result res = pMod->getResult(nTimeout);
+
+ switch(res.type) {
+ case Moderator::ResultType::STREAM:
+ {
+ Reference<XStream> result;
+ if(res.result >>= result) {
+ Reference < XActiveDataStreamer > xStreamer(
+ xSink, UNO_QUERY
+ );
+
+ if(xStreamer.is())
+ xStreamer->setStream(result);
+ }
+ pMod->setReply(Moderator::REQUESTHANDLED);
+ break;
+ }
+ case Moderator::ResultType::INPUTSTREAM:
+ {
+ Reference<XInputStream> result;
+ res.result >>= result;
+ Reference < XActiveDataSink > xActiveSink(
+ xSink, UNO_QUERY
+ );
+
+ if(xActiveSink.is())
+ xActiveSink->setInputStream(result);
+ pMod->setReply(Moderator::REQUESTHANDLED);
+ break;
+ }
+ case Moderator::ResultType::TIMEDOUT:
+ {
+ Reference<XInteractionRetry> xRet;
+ if(xInteract.is()) {
+ INetURLObject aURL(
+ xContId.is() ?
+ xContId->getContentIdentifier() :
+ OUString() );
+ InteractiveNetworkConnectException aExcep(
+ "server not responding after five seconds", {},
+ InteractionClassification_ERROR, aURL.GetHost());
+ Any request;
+ request <<= aExcep;
+ rtl::Reference<ucbhelper::InteractionRequest> xIR =
+ new ucbhelper::InteractionRequest(request);
+ rtl::Reference<ucbhelper::InteractionRetry> retryP =
+ new ucbhelper::InteractionRetry(xIR.get());
+ rtl::Reference<ucbhelper::InteractionAbort> abortP =
+ new ucbhelper::InteractionAbort(xIR.get());
+ Sequence<Reference<XInteractionContinuation> > aSeq { retryP, abortP };
+
+ xIR->setContinuations(aSeq);
+ xInteract->handle(xIR);
+ rtl::Reference< ucbhelper::InteractionContinuation > ref
+ = xIR->getSelection();
+ if(ref.is()) {
+ Reference<XInterface> xInt(ref);
+ xRet.set(xInt,UNO_QUERY);
+ }
+ }
+
+ if(!xRet.is()) {
+ bAborted = true;
+ xLockBytes->SetError(ERRCODE_ABORT);
+ }
+
+ break;
+ }
+ case Moderator::ResultType::INTERACTIONREQUEST:
+ {
+ Reference<XInteractionRequest> Request;
+ res.result >>= Request;
+ xInteract->handle(Request);
+ pMod->setReply(Moderator::REQUESTHANDLED);
+ break;
+ }
+ case Moderator::ResultType::RESULT:
+ {
+ bResultAchieved = true;
+ break;
+ }
+ case Moderator::ResultType::COMMANDABORTED:
+ {
+ bAborted = true;
+ xLockBytes->SetError( ERRCODE_ABORT );
+ break;
+ }
+ case Moderator::ResultType::COMMANDFAILED:
+ {
+ bAborted = true;
+ xLockBytes->SetError( ERRCODE_ABORT );
+ break;
+ }
+ case Moderator::ResultType::INTERACTIVEIO:
+ {
+ bException = true;
+ if ( res.ioErrorCode == IOErrorCode_ACCESS_DENIED ||
+ res.ioErrorCode == IOErrorCode_LOCKING_VIOLATION )
+ xLockBytes->SetError( ERRCODE_IO_ACCESSDENIED );
+ else if ( res.ioErrorCode == IOErrorCode_NOT_EXISTING )
+ xLockBytes->SetError( ERRCODE_IO_NOTEXISTS );
+ else if ( res.ioErrorCode == IOErrorCode_CANT_READ )
+ xLockBytes->SetError( ERRCODE_IO_CANTREAD );
+ else
+ xLockBytes->SetError( ERRCODE_IO_GENERAL );
+ break;
+ }
+ case Moderator::ResultType::UNSUPPORTED:
+ {
+ bException = true;
+ xLockBytes->SetError( ERRCODE_IO_NOTSUPPORTED );
+ break;
+ }
+ default:
+ {
+ bException = true;
+ xLockBytes->SetError( ERRCODE_IO_GENERAL );
+ break;
+ }
+ }
+
+ bResultAchieved |= bException;
+ bResultAchieved |= bAborted;
+ if(nTimeout == 5000) nTimeout *= 2;
+ }
+
+ if(pMod) pMod->setReply(Moderator::EXIT);
+
+ if ( bAborted || bException )
+ {
+ Reference < XActiveDataSink > xActiveSink( xSink, UNO_QUERY );
+ if ( xActiveSink.is() )
+ xActiveSink->setInputStream( Reference < XInputStream >() );
+
+ Reference < XActiveDataStreamer > xStreamer( xSink, UNO_QUERY );
+ if ( xStreamer.is() )
+ xStreamer->setStream( Reference < XStream >() );
+ }
+
+ Reference < XActiveDataControl > xControl( xSink, UNO_QUERY );
+ if ( xControl.is() )
+ xControl->terminate();
+
+ if ( xProps.is() )
+ xProps->removePropertiesChangeListener(
+ Sequence< OUString >(),
+ xListener );
+
+ return ( bAborted || bException );
+}
+
+/**
+ Function for opening UCB contents synchronously
+ */
+static bool UCBOpenContentSync_(
+ const UcbLockBytesRef& xLockBytes,
+ const Reference < XContent >& xContent,
+ const Command& rArg,
+ const Reference < XInterface >& xSink,
+ const Reference < XInteractionHandler >& xInteract )
+{
+ ::ucbhelper::Content aContent(
+ xContent, new UcbTaskEnvironment( xInteract, nullptr ),
+ comphelper::getProcessComponentContext() );
+ Reference < XContentIdentifier > xIdent = xContent->getIdentifier();
+ OUString aScheme = xIdent->getContentProviderScheme();
+
+ // http protocol must be handled in a special way: during the opening process the input stream may change
+ // only the last inputstream after notifying the document headers is valid
+ if ( !aScheme.equalsIgnoreAsciiCase("http") )
+ xLockBytes->SetStreamValid();
+
+ Reference< XPropertiesChangeListener > xListener = new UcbPropertiesChangeListener_Impl( xLockBytes );
+ Reference< XPropertiesChangeNotifier > xProps ( xContent, UNO_QUERY );
+ if ( xProps.is() )
+ xProps->addPropertiesChangeListener( Sequence< OUString >(), xListener );
+
+ bool bException = false;
+ bool bAborted = false;
+
+ try
+ {
+ aContent.executeCommand( rArg.Name, rArg.Argument );
+ }
+ catch (const CommandAbortedException&)
+ {
+ bAborted = true;
+ xLockBytes->SetError( ERRCODE_ABORT );
+ }
+ catch (const CommandFailedException&)
+ {
+ bAborted = true;
+ xLockBytes->SetError( ERRCODE_ABORT );
+ }
+ catch (const InteractiveIOException& r)
+ {
+ bException = true;
+ if ( r.Code == IOErrorCode_ACCESS_DENIED || r.Code == IOErrorCode_LOCKING_VIOLATION )
+ xLockBytes->SetError( ERRCODE_IO_ACCESSDENIED );
+ else if ( r.Code == IOErrorCode_NOT_EXISTING )
+ xLockBytes->SetError( ERRCODE_IO_NOTEXISTS );
+ else if ( r.Code == IOErrorCode_CANT_READ )
+ xLockBytes->SetError( ERRCODE_IO_CANTREAD );
+ else
+ xLockBytes->SetError( ERRCODE_IO_GENERAL );
+ }
+ catch (const UnsupportedDataSinkException&)
+ {
+ bException = true;
+ xLockBytes->SetError( ERRCODE_IO_NOTSUPPORTED );
+ }
+ catch (const Exception&)
+ {
+ bException = true;
+ xLockBytes->SetError( ERRCODE_IO_GENERAL );
+ }
+
+ if ( bAborted || bException )
+ {
+ Reference < XActiveDataSink > xActiveSink( xSink, UNO_QUERY );
+ if ( xActiveSink.is() )
+ xActiveSink->setInputStream( Reference < XInputStream >() );
+
+ Reference < XActiveDataStreamer > xStreamer( xSink, UNO_QUERY );
+ if ( xStreamer.is() )
+ xStreamer->setStream( Reference < XStream >() );
+ }
+
+ Reference < XActiveDataControl > xControl( xSink, UNO_QUERY );
+ if ( xControl.is() )
+ xControl->terminate();
+
+ if ( xProps.is() )
+ xProps->removePropertiesChangeListener( Sequence< OUString >(), xListener );
+
+ return ( bAborted || bException );
+}
+
+UcbLockBytes::UcbLockBytes()
+ : m_nError( ERRCODE_NONE )
+ , m_bTerminated (false)
+ , m_bDontClose( false )
+ , m_bStreamValid (false)
+{
+ SetSynchronMode();
+}
+
+UcbLockBytes::~UcbLockBytes()
+{
+ if ( !m_bDontClose )
+ {
+ if ( m_xInputStream.is() )
+ {
+ try
+ {
+ m_xInputStream->closeInput();
+ }
+ catch (const RuntimeException&)
+ {
+ }
+ catch (const IOException&)
+ {
+ }
+ }
+ }
+
+ if ( m_xInputStream.is() || !m_xOutputStream.is() )
+ return;
+
+ try
+ {
+ m_xOutputStream->closeOutput();
+ }
+ catch (const RuntimeException&)
+ {
+ }
+ catch (const IOException&)
+ {
+ }
+}
+
+Reference < XInputStream > UcbLockBytes::getInputStream()
+{
+ std::unique_lock aGuard( m_aMutex );
+ m_bDontClose = true;
+ return m_xInputStream;
+}
+
+void UcbLockBytes::setStream( const Reference<XStream>& aStream )
+{
+ std::unique_lock aGuard( m_aMutex );
+ if ( aStream.is() )
+ {
+ m_xOutputStream = aStream->getOutputStream();
+ setInputStreamImpl( aGuard, aStream->getInputStream(), false );
+ m_xSeekable.set( aStream, UNO_QUERY );
+ }
+ else
+ {
+ m_xOutputStream.clear();
+ setInputStreamImpl( aGuard, Reference < XInputStream >() );
+ }
+}
+
+bool UcbLockBytes::setInputStream( const Reference<XInputStream> &rxInputStream, bool bSetXSeekable )
+{
+ std::unique_lock aGuard( m_aMutex );
+ return setInputStreamImpl(aGuard, rxInputStream, bSetXSeekable);
+}
+
+bool UcbLockBytes::setInputStreamImpl( std::unique_lock<std::mutex>& /*rGuard*/, const Reference<XInputStream> &rxInputStream, bool bSetXSeekable )
+{
+ bool bRet = false;
+
+ try
+ {
+ if ( !m_bDontClose && m_xInputStream.is() )
+ m_xInputStream->closeInput();
+
+ m_xInputStream = rxInputStream;
+
+ if( bSetXSeekable )
+ {
+ m_xSeekable.set( rxInputStream, UNO_QUERY );
+ if( !m_xSeekable.is() && rxInputStream.is() )
+ {
+ Reference < XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ rtl::Reference< utl::TempFileFastService > rxTempOut( new utl::TempFileFastService );
+
+ ::comphelper::OStorageHelper::CopyInputToOutput( rxInputStream, rxTempOut );
+ m_xInputStream.set( rxTempOut );
+ m_xSeekable.set( rxTempOut );
+ }
+ }
+
+ bRet = m_xInputStream.is();
+ }
+ catch (const Exception&)
+ {
+ }
+
+ if ( m_bStreamValid && m_xInputStream.is() )
+ m_aInitialized.set();
+
+ return bRet;
+}
+
+void UcbLockBytes::SetStreamValid()
+{
+ m_bStreamValid = true;
+ if ( m_xInputStream.is() )
+ m_aInitialized.set();
+}
+
+void UcbLockBytes::terminate()
+{
+ m_bTerminated = true;
+ m_aInitialized.set();
+ m_aTerminated.set();
+
+ if ( GetError() == ERRCODE_NONE && !m_xInputStream.is() )
+ {
+ OSL_FAIL("No InputStream, but no error set!" );
+ SetError( ERRCODE_IO_NOTEXISTS );
+ }
+}
+
+ErrCode UcbLockBytes::ReadAt(sal_uInt64 const nPos,
+ void *pBuffer, std::size_t nCount, std::size_t *pRead) const
+{
+ if ( IsSynchronMode() )
+ {
+ UcbLockBytes* pThis = const_cast < UcbLockBytes* >( this );
+ pThis->m_aInitialized.wait();
+ }
+
+ Reference <XInputStream> xStream = getInputStream();
+ if ( !xStream.is() )
+ {
+ if ( m_bTerminated )
+ return ERRCODE_IO_CANTREAD;
+ else
+ return ERRCODE_IO_PENDING;
+ }
+
+ if ( pRead )
+ *pRead = 0;
+
+ Reference <XSeekable> xSeekable = getSeekable();
+ if ( !xSeekable.is() )
+ return ERRCODE_IO_CANTREAD;
+
+ try
+ {
+ xSeekable->seek( nPos );
+ }
+ catch (const IOException&)
+ {
+ return ERRCODE_IO_CANTSEEK;
+ }
+ catch (const css::lang::IllegalArgumentException&)
+ {
+ return ERRCODE_IO_CANTSEEK;
+ }
+
+ sal_Int32 nSize;
+
+ if(nCount > 0x7FFFFFFF)
+ {
+ nCount = 0x7FFFFFFF;
+ }
+ try
+ {
+ if ( !m_bTerminated && !IsSynchronMode() )
+ {
+ sal_uInt64 nLen = xSeekable->getLength();
+ if ( nPos + nCount > nLen )
+ return ERRCODE_IO_PENDING;
+ }
+
+ comphelper::ByteReader* pByteReader = dynamic_cast< comphelper::ByteReader* >(xStream.get());
+ if (pByteReader)
+ {
+ nSize = pByteReader->readSomeBytes( static_cast<sal_Int8*>(pBuffer), sal_Int32(nCount) );
+ }
+ else
+ {
+ Sequence<sal_Int8> aData;
+ nSize = xStream->readBytes( aData, sal_Int32(nCount) );
+ memcpy (pBuffer, aData.getConstArray(), nSize);
+ }
+ }
+ catch (const IOException&)
+ {
+ return ERRCODE_IO_CANTREAD;
+ }
+
+ if (pRead)
+ *pRead = static_cast<std::size_t>(nSize);
+
+ return ERRCODE_NONE;
+}
+
+ErrCode UcbLockBytes::WriteAt(sal_uInt64 const nPos, const void *pBuffer,
+ std::size_t nCount, std::size_t *pWritten)
+{
+ if ( pWritten )
+ *pWritten = 0;
+
+ DBG_ASSERT( IsSynchronMode(), "Writing is only possible in SynchronMode!" );
+ DBG_ASSERT( m_aInitialized.check(), "Writing bevor stream is ready!" );
+
+ Reference <XSeekable> xSeekable = getSeekable();
+ Reference <XOutputStream> xOutputStream = getOutputStream();
+ if ( !xOutputStream.is() || !xSeekable.is() )
+ return ERRCODE_IO_CANTWRITE;
+
+ try
+ {
+ xSeekable->seek( nPos );
+ }
+ catch (const IOException&)
+ {
+ return ERRCODE_IO_CANTSEEK;
+ }
+
+ sal_Int8 const * pData = static_cast<sal_Int8 const *>(pBuffer);
+ Sequence<sal_Int8> aData( pData, nCount );
+ try
+ {
+ xOutputStream->writeBytes( aData );
+ if ( pWritten )
+ *pWritten = nCount;
+ }
+ catch (const Exception&)
+ {
+ return ERRCODE_IO_CANTWRITE;
+ }
+
+ return ERRCODE_NONE;
+}
+
+ErrCode UcbLockBytes::Flush() const
+{
+ Reference <XOutputStream > xOutputStream = getOutputStream();
+ if ( !xOutputStream.is() )
+ return ERRCODE_IO_CANTWRITE;
+
+ try
+ {
+ xOutputStream->flush();
+ }
+ catch (const Exception&)
+ {
+ return ERRCODE_IO_CANTWRITE;
+ }
+
+ return ERRCODE_NONE;
+}
+
+ErrCode UcbLockBytes::SetSize (sal_uInt64 const nNewSize)
+{
+ SvLockBytesStat aStat;
+ Stat( &aStat );
+ std::size_t nSize = aStat.nSize;
+
+ if ( nSize > nNewSize )
+ {
+ Reference < XTruncate > xTrunc( getOutputStream(), UNO_QUERY );
+ if ( xTrunc.is() )
+ {
+ xTrunc->truncate();
+ nSize = 0;
+ }
+ else {
+ SAL_INFO("unotools.ucbhelper", "Not truncable!");
+ }
+ }
+
+ if ( nSize < nNewSize )
+ {
+ std::size_t nDiff = nNewSize-nSize, nCount=0;
+ std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[ nDiff ]);
+ memset(pBuffer.get(), 0, nDiff); // initialize for enhanced security
+ WriteAt( nSize, pBuffer.get(), nDiff, &nCount );
+ if ( nCount != nDiff )
+ return ERRCODE_IO_CANTWRITE;
+ }
+
+ return ERRCODE_NONE;
+}
+
+ErrCode UcbLockBytes::Stat( SvLockBytesStat *pStat ) const
+{
+ if ( IsSynchronMode() )
+ {
+ UcbLockBytes* pThis = const_cast < UcbLockBytes* >( this );
+ pThis->m_aInitialized.wait();
+ }
+
+ if (!pStat)
+ return ERRCODE_IO_INVALIDPARAMETER;
+
+ Reference <XInputStream> xStream = getInputStream();
+ Reference <XSeekable> xSeekable = getSeekable();
+
+ if ( !xStream.is() )
+ {
+ if ( m_bTerminated )
+ return ERRCODE_IO_INVALIDACCESS;
+ else
+ return ERRCODE_IO_PENDING;
+ }
+ else if( !xSeekable.is() )
+ return ERRCODE_IO_CANTTELL;
+
+ try
+ {
+ pStat->nSize = sal_uLong(xSeekable->getLength());
+ }
+ catch (const IOException&)
+ {
+ return ERRCODE_IO_CANTTELL;
+ }
+
+ return ERRCODE_NONE;
+}
+
+UcbLockBytesRef UcbLockBytes::CreateInputLockBytes( const Reference< XInputStream >& xInputStream )
+{
+ if( !xInputStream.is() )
+ return nullptr;
+
+ UcbLockBytesRef xLockBytes = new UcbLockBytes;
+ xLockBytes->setDontClose();
+ xLockBytes->setInputStream( xInputStream );
+ xLockBytes->terminate();
+ return xLockBytes;
+}
+
+UcbLockBytesRef UcbLockBytes::CreateLockBytes( const Reference< XStream >& xStream )
+{
+ if( !xStream.is() )
+ return nullptr;
+
+ UcbLockBytesRef xLockBytes = new UcbLockBytes;
+ xLockBytes->setDontClose();
+ xLockBytes->setStream( xStream );
+ xLockBytes->terminate();
+ return xLockBytes;
+}
+
+UcbLockBytesRef UcbLockBytes::CreateLockBytes( const Reference < XContent >& xContent, const Sequence < PropertyValue >& rProps,
+ StreamMode eOpenMode, const Reference < XInteractionHandler >& xInteractionHandler )
+{
+ if( !xContent.is() )
+ return nullptr;
+
+ UcbLockBytesRef xLockBytes = new UcbLockBytes;
+ xLockBytes->SetSynchronMode();
+ Reference< XActiveDataControl > xSink;
+ if ( eOpenMode & StreamMode::WRITE )
+ xSink = new UcbStreamer_Impl(xLockBytes.get());
+ else
+ xSink = new UcbDataSink_Impl(xLockBytes.get());
+
+ if ( rProps.hasElements() )
+ {
+ Reference < XCommandProcessor > xProcessor( xContent, UNO_QUERY );
+ Command aCommand;
+ aCommand.Name = "setPropertyValues";
+ aCommand.Handle = -1; /* unknown */
+ aCommand.Argument <<= rProps;
+ xProcessor->execute( aCommand, 0, Reference < XCommandEnvironment >() );
+ }
+
+ OpenCommandArgument2 aArgument;
+ aArgument.Sink = xSink;
+ aArgument.Mode = OpenMode::DOCUMENT;
+
+ Command aCommand;
+ aCommand.Name = "open";
+ aCommand.Argument <<= aArgument;
+
+ bool bError = UCBOpenContentSync( xLockBytes,
+ xContent,
+ aCommand,
+ xSink,
+ xInteractionHandler );
+
+ if ( xLockBytes->GetError() == ERRCODE_NONE && ( bError || !xLockBytes->getInputStream().is() ) )
+ {
+ OSL_FAIL("No InputStream, but no error set!" );
+ xLockBytes->SetError( ERRCODE_IO_GENERAL );
+ }
+
+ return xLockBytes;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/ucblockbytes.hxx b/unotools/source/ucbhelper/ucblockbytes.hxx
new file mode 100644
index 0000000000..d866015b25
--- /dev/null
+++ b/unotools/source/ucbhelper/ucblockbytes.hxx
@@ -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 .
+ */
+#pragma once
+
+#include <com/sun/star/uno/Reference.hxx>
+
+#include <osl/conditn.hxx>
+#include <tools/stream.hxx>
+#include <comphelper/errcode.hxx>
+#include <mutex>
+
+namespace com
+{
+ namespace sun
+ {
+ namespace star
+ {
+ namespace task
+ {
+ class XInteractionHandler;
+ }
+ namespace io
+ {
+ class XStream;
+ class XInputStream;
+ class XOutputStream;
+ class XSeekable;
+ }
+ namespace ucb
+ {
+ class XContent;
+ }
+ namespace beans
+ {
+ struct PropertyValue;
+ }
+ }
+ }
+}
+
+namespace utl
+{
+class UcbLockBytes;
+typedef tools::SvRef<UcbLockBytes> UcbLockBytesRef;
+
+class UcbLockBytes : public SvLockBytes
+{
+ osl::Condition m_aInitialized;
+ osl::Condition m_aTerminated;
+ std::mutex m_aMutex;
+
+ css::uno::Reference < css::io::XInputStream > m_xInputStream;
+ css::uno::Reference < css::io::XOutputStream > m_xOutputStream;
+ css::uno::Reference < css::io::XSeekable > m_xSeekable;
+
+ ErrCode m_nError;
+
+ bool m_bTerminated;
+ bool m_bDontClose;
+ bool m_bStreamValid;
+
+ UcbLockBytes();
+protected:
+ virtual ~UcbLockBytes() override;
+
+public:
+ // properties: Referer, PostMimeType
+ static UcbLockBytesRef CreateLockBytes( const css::uno::Reference < css::ucb::XContent >& xContent,
+ const css::uno::Sequence < css::beans::PropertyValue >& rProps,
+ StreamMode eMode,
+ const css::uno::Reference < css::task::XInteractionHandler >& xInter );
+
+ static UcbLockBytesRef CreateInputLockBytes( const css::uno::Reference < css::io::XInputStream >& xContent );
+ static UcbLockBytesRef CreateLockBytes( const css::uno::Reference < css::io::XStream >& xContent );
+
+ // SvLockBytes
+ virtual ErrCode ReadAt(sal_uInt64 nPos, void *pBuffer, std::size_t nCount, std::size_t *pRead) const override;
+ virtual ErrCode WriteAt(sal_uInt64, const void*, std::size_t, std::size_t *pWritten) override;
+ virtual ErrCode Flush() const override;
+ virtual ErrCode SetSize(sal_uInt64) override;
+ virtual ErrCode Stat ( SvLockBytesStat *pStat ) const override;
+
+ void SetError( ErrCode nError )
+ { m_nError = nError; }
+
+ ErrCode const & GetError() const
+ { return m_nError; }
+
+ // calling this method delegates the responsibility to call closeinput to the caller!
+ css::uno::Reference < css::io::XInputStream > getInputStream();
+
+ bool setInputStream( const css::uno::Reference < css::io::XInputStream > &rxInputStream,
+ bool bSetXSeekable = true );
+ void setStream( const css::uno::Reference < css::io::XStream > &rxStream );
+ void terminate();
+
+ css::uno::Reference < css::io::XInputStream > getInputStream() const
+ {
+ std::unique_lock aGuard( const_cast< UcbLockBytes* >(this)->m_aMutex );
+ return m_xInputStream;
+ }
+
+ css::uno::Reference < css::io::XOutputStream > getOutputStream() const
+ {
+ std::unique_lock aGuard( const_cast< UcbLockBytes* >(this)->m_aMutex );
+ return m_xOutputStream;
+ }
+
+ css::uno::Reference < css::io::XSeekable > getSeekable() const
+ {
+ std::unique_lock aGuard( const_cast< UcbLockBytes* >(this)->m_aMutex );
+ return m_xSeekable;
+ }
+
+ void setDontClose()
+ { m_bDontClose = true; }
+
+ void SetStreamValid();
+
+private:
+ bool setInputStreamImpl( std::unique_lock<std::mutex>& rGuard,
+ const css::uno::Reference < css::io::XInputStream > &rxInputStream,
+ bool bSetXSeekable = true );
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/ucbstreamhelper.cxx b/unotools/source/ucbhelper/ucbstreamhelper.cxx
new file mode 100644
index 0000000000..e3437a864f
--- /dev/null
+++ b/unotools/source/ucbhelper/ucbstreamhelper.cxx
@@ -0,0 +1,237 @@
+/* -*- 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 <rtl/ustring.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/task/InteractionHandler.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/ucb/CommandAbortedException.hpp>
+#include <com/sun/star/ucb/InsertCommandArgument.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <comphelper/simplefileaccessinteraction.hxx>
+#include <ucbhelper/content.hxx>
+#include <unotools/streamwrap.hxx>
+#include "ucblockbytes.hxx"
+
+namespace com::sun::star::ucb { class XCommandEnvironment; }
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::ucb;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+
+namespace utl
+{
+
+static std::unique_ptr<SvStream> lcl_CreateStream( const OUString& rFileName, StreamMode eOpenMode,
+ const Reference < XInteractionHandler >& xInteractionHandler,
+ bool bEnsureFileExists )
+{
+ std::unique_ptr<SvStream> pStream;
+ UcbLockBytesRef xLockBytes;
+ if ( eOpenMode & StreamMode::WRITE )
+ {
+ bool bTruncate = bool( eOpenMode & StreamMode::TRUNC );
+ if ( bTruncate )
+ {
+ try
+ {
+ // truncate is implemented with deleting the original file
+ ::ucbhelper::Content aCnt(
+ rFileName, Reference < XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ aCnt.executeCommand( "delete", css::uno::Any( true ) );
+ }
+
+ catch ( const CommandAbortedException& )
+ {
+ // couldn't truncate/delete
+ }
+ catch ( const ContentCreationException& )
+ {
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+
+ if ( bEnsureFileExists || bTruncate )
+ {
+ try
+ {
+ // make sure that the desired file exists before trying to open
+ SvMemoryStream aStream(0,0);
+ rtl::Reference<::utl::OInputStreamWrapper> xInput = new ::utl::OInputStreamWrapper( aStream );
+
+ ::ucbhelper::Content aContent(
+ rFileName, Reference < XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ InsertCommandArgument aInsertArg;
+ aInsertArg.Data = xInput;
+
+ aInsertArg.ReplaceExisting = false;
+ Any aCmdArg;
+ aCmdArg <<= aInsertArg;
+ aContent.executeCommand( "insert", aCmdArg );
+ }
+
+ // it is NOT an error when the stream already exists and no truncation was desired
+ catch ( const CommandAbortedException& )
+ {
+ // currently never an error is detected !
+ }
+ catch ( const ContentCreationException& )
+ {
+ }
+ catch ( const Exception& )
+ {
+ }
+ }
+ }
+
+ try
+ {
+ // create LockBytes using UCB
+ ::ucbhelper::Content aContent(
+ rFileName, Reference < XCommandEnvironment >(),
+ comphelper::getProcessComponentContext() );
+ xLockBytes = UcbLockBytes::CreateLockBytes( aContent.get(), Sequence < PropertyValue >(),
+ eOpenMode, xInteractionHandler );
+ if ( xLockBytes.is() )
+ {
+ pStream.reset( new SvStream( xLockBytes.get() ) );
+ pStream->SetBufferSize( 4096 );
+ pStream->SetError( xLockBytes->GetError() );
+ }
+ }
+ catch ( const CommandAbortedException& )
+ {
+ }
+ catch ( const ContentCreationException& )
+ {
+ }
+ catch ( const Exception& )
+ {
+ }
+
+ return pStream;
+}
+
+std::unique_ptr<SvStream> UcbStreamHelper::CreateStream(const OUString& rFileName, StreamMode eOpenMode, css::uno::Reference<css::awt::XWindow> xParentWin)
+{
+ // related tdf#99312
+ // create a specialized interaction handler to manages Web certificates and Web credentials when needed
+ Reference< XInteractionHandler > xIH(
+ css::task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), xParentWin));
+ Reference<XInteractionHandler> xIHScoped(new comphelper::SimpleFileAccessInteraction(xIH));
+
+ return lcl_CreateStream( rFileName, eOpenMode, xIHScoped, true /* bEnsureFileExists */ );
+}
+
+std::unique_ptr<SvStream> UcbStreamHelper::CreateStream(const OUString& rFileName, StreamMode eOpenMode,
+ bool bFileExists, css::uno::Reference<css::awt::XWindow> xParentWin)
+{
+ // related tdf#99312
+ // create a specialized interaction handler to manages Web certificates and Web credentials when needed
+ Reference< XInteractionHandler > xIH(
+ css::task::InteractionHandler::createWithParent(comphelper::getProcessComponentContext(), xParentWin));
+ Reference<XInteractionHandler> xIHScoped(new comphelper::SimpleFileAccessInteraction(xIH));
+ return lcl_CreateStream( rFileName, eOpenMode, xIHScoped,!bFileExists );
+}
+
+
+std::unique_ptr<SvStream> UcbStreamHelper::CreateStream( const Reference < XInputStream >& xStream )
+{
+ std::unique_ptr<SvStream> pStream;
+ UcbLockBytesRef xLockBytes = UcbLockBytes::CreateInputLockBytes( xStream );
+ if ( xLockBytes.is() )
+ {
+ pStream.reset( new SvStream( xLockBytes.get() ) );
+ pStream->SetBufferSize( 4096 );
+ pStream->SetError( xLockBytes->GetError() );
+ }
+
+ return pStream;
+}
+
+std::unique_ptr<SvStream> UcbStreamHelper::CreateStream( const Reference < XStream >& xStream )
+{
+ std::unique_ptr<SvStream> pStream;
+ if ( xStream->getOutputStream().is() )
+ {
+ UcbLockBytesRef xLockBytes = UcbLockBytes::CreateLockBytes( xStream );
+ if ( xLockBytes.is() )
+ {
+ pStream.reset( new SvStream( xLockBytes.get() ) );
+ pStream->SetBufferSize( 4096 );
+ pStream->SetError( xLockBytes->GetError() );
+ }
+ }
+ else
+ return CreateStream( xStream->getInputStream() );
+
+ return pStream;
+}
+
+std::unique_ptr<SvStream> UcbStreamHelper::CreateStream( const Reference < XInputStream >& xStream, bool bCloseStream )
+{
+ std::unique_ptr<SvStream> pStream;
+ UcbLockBytesRef xLockBytes = UcbLockBytes::CreateInputLockBytes( xStream );
+ if ( xLockBytes.is() )
+ {
+ if ( !bCloseStream )
+ xLockBytes->setDontClose();
+
+ pStream.reset( new SvStream( xLockBytes.get() ) );
+ pStream->SetBufferSize( 4096 );
+ pStream->SetError( xLockBytes->GetError() );
+ }
+
+ return pStream;
+};
+
+std::unique_ptr<SvStream> UcbStreamHelper::CreateStream( const Reference < XStream >& xStream, bool bCloseStream )
+{
+ std::unique_ptr<SvStream> pStream;
+ if ( xStream->getOutputStream().is() )
+ {
+ UcbLockBytesRef xLockBytes = UcbLockBytes::CreateLockBytes( xStream );
+ if ( xLockBytes.is() )
+ {
+ if ( !bCloseStream )
+ xLockBytes->setDontClose();
+
+ pStream.reset( new SvStream( xLockBytes.get() ) );
+ pStream->SetBufferSize( 4096 );
+ pStream->SetError( xLockBytes->GetError() );
+ }
+ }
+ else
+ return CreateStream( xStream->getInputStream(), bCloseStream );
+
+ return pStream;
+};
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/source/ucbhelper/xtempfile.cxx b/unotools/source/ucbhelper/xtempfile.cxx
new file mode 100644
index 0000000000..5e0cf5cbdb
--- /dev/null
+++ b/unotools/source/ucbhelper/xtempfile.cxx
@@ -0,0 +1,435 @@
+/* -*- 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 "XTempFile.hxx"
+#include <com/sun/star/io/BufferSizeExceededException.hpp>
+#include <com/sun/star/io/NotConnectedException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <cppuhelper/typeprovider.hxx>
+#include <o3tl/safeint.hxx>
+#include <unotools/tempfile.hxx>
+#include <cppuhelper/propshlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+OTempFileService::OTempFileService(css::uno::Reference< css::uno::XComponentContext > const &)
+: mpStream( nullptr )
+, mbRemoveFile( true )
+, mbInClosed( false )
+, mbOutClosed( false )
+{
+ mpTempFile.emplace();
+ mpTempFile->EnableKillingFile();
+}
+
+OTempFileService::~OTempFileService ()
+{
+}
+
+// XTypeProvider
+
+css::uno::Sequence< css::uno::Type > SAL_CALL OTempFileService::getTypes( )
+{
+ static ::cppu::OTypeCollection ourTypeCollection(
+ cppu::UnoType<css::beans::XPropertySet>::get()
+ ,OTempFileBase::getTypes() );
+
+ return ourTypeCollection.getTypes();
+};
+
+// XTempFile
+
+sal_Bool SAL_CALL OTempFileService::getRemoveFile()
+{
+ std::unique_lock aGuard( maMutex );
+
+ if ( !mpTempFile )
+ {
+ // the stream is already disconnected
+ throw css::uno::RuntimeException("Not connected to a file.");
+ }
+
+ return mbRemoveFile;
+};
+void SAL_CALL OTempFileService::setRemoveFile( sal_Bool _removefile )
+{
+ std::unique_lock aGuard( maMutex );
+
+ if ( !mpTempFile )
+ {
+ // the stream is already disconnected
+ throw css::uno::RuntimeException("Not connected to a file.");
+ }
+
+ mbRemoveFile = _removefile;
+ mpTempFile->EnableKillingFile( mbRemoveFile );
+};
+OUString SAL_CALL OTempFileService::getUri()
+{
+ std::unique_lock aGuard( maMutex );
+
+ if ( !mpTempFile )
+ {
+ throw css::uno::RuntimeException("Not connected to a file.");
+ }
+
+ return mpTempFile->GetURL();
+
+};
+OUString SAL_CALL OTempFileService::getResourceName()
+{
+ std::unique_lock aGuard( maMutex );
+
+ if ( !mpTempFile )
+ {
+ throw css::uno::RuntimeException("Not connected to a file.");
+ }
+
+ return mpTempFile->GetFileName();
+};
+
+// XInputStream
+
+sal_Int32 SAL_CALL OTempFileService::readBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nBytesToRead )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbInClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ checkConnected();
+ if (nBytesToRead < 0)
+ throw css::io::BufferSizeExceededException( OUString(), getXWeak());
+
+ if (aData.getLength() < nBytesToRead)
+ aData.realloc(nBytesToRead);
+
+ sal_uInt32 nRead = mpStream->ReadBytes(static_cast<void*>(aData.getArray()), nBytesToRead);
+ checkError();
+
+ if (nRead < o3tl::make_unsigned(aData.getLength()))
+ aData.realloc( nRead );
+
+ return nRead;
+}
+sal_Int32 SAL_CALL OTempFileService::readSomeBytes( css::uno::Sequence< sal_Int8 >& aData, sal_Int32 nMaxBytesToRead )
+{
+ {
+ std::unique_lock aGuard( maMutex );
+ if ( mbInClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ checkConnected();
+ checkError();
+
+ if (nMaxBytesToRead < 0)
+ throw css::io::BufferSizeExceededException( OUString(), getXWeak() );
+
+ if (mpStream->eof())
+ {
+ aData.realloc(0);
+ return 0;
+ }
+ }
+ return readBytes(aData, nMaxBytesToRead);
+}
+void SAL_CALL OTempFileService::skipBytes( sal_Int32 nBytesToSkip )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbInClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ checkConnected();
+ checkError();
+ mpStream->SeekRel(nBytesToSkip);
+ checkError();
+}
+sal_Int32 SAL_CALL OTempFileService::available( )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbInClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ checkConnected();
+
+ sal_Int64 nAvailable = mpStream->remainingSize();
+ checkError();
+
+ return std::min<sal_Int64>(SAL_MAX_INT32, nAvailable);
+}
+void SAL_CALL OTempFileService::closeInput( )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbInClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ mbInClosed = true;
+
+ if ( mbOutClosed )
+ {
+ // stream will be deleted by TempFile implementation
+ mpStream = nullptr;
+ mpTempFile.reset();
+ }
+}
+
+// XOutputStream
+
+void SAL_CALL OTempFileService::writeBytes( const css::uno::Sequence< sal_Int8 >& aData )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbOutClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ checkConnected();
+ sal_uInt32 nWritten = mpStream->WriteBytes(aData.getConstArray(), aData.getLength());
+ checkError();
+ if ( nWritten != static_cast<sal_uInt32>(aData.getLength()))
+ throw css::io::BufferSizeExceededException( OUString(), getXWeak() );
+}
+void SAL_CALL OTempFileService::flush( )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbOutClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ checkConnected();
+ mpStream->Flush();
+ checkError();
+}
+void SAL_CALL OTempFileService::closeOutput( )
+{
+ std::unique_lock aGuard( maMutex );
+ if ( mbOutClosed )
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+
+ mbOutClosed = true;
+ if (mpStream)
+ {
+ // so that if you then open the InputStream, you can read the content
+ mpStream->FlushBuffer();
+ mpStream->Seek(0);
+ }
+
+ if ( mbInClosed )
+ {
+ // stream will be deleted by TempFile implementation
+ mpStream = nullptr;
+ mpTempFile.reset();
+ }
+}
+
+void OTempFileService::checkError () const
+{
+ if (!mpStream || mpStream->SvStream::GetError () != ERRCODE_NONE )
+ throw css::io::NotConnectedException ( OUString(), const_cast < OTempFileService * > (this)->getXWeak() );
+}
+void OTempFileService::checkConnected ()
+{
+ if (!mpStream && mpTempFile)
+ {
+ // Ideally we should open this SHARE_DENYALL, but the JunitTest_unotools_complex test wants to open
+ // this file directly and read from it.
+ mpStream = mpTempFile->GetStream(StreamMode::READ | StreamMode::WRITE
+ | StreamMode::SHARE_DENYWRITE);
+ }
+
+ if (!mpStream)
+ throw css::io::NotConnectedException ( OUString(), getXWeak() );
+}
+
+// XSeekable
+
+void SAL_CALL OTempFileService::seek( sal_Int64 nLocation )
+{
+ std::unique_lock aGuard( maMutex );
+ checkConnected();
+ checkError();
+ sal_Int64 nEndPos = mpStream->TellEnd();
+ if ( nLocation < 0 || nLocation > nEndPos )
+ throw css::lang::IllegalArgumentException();
+
+ mpStream->Seek(static_cast<sal_uInt32>(nLocation) );
+ checkError();
+}
+sal_Int64 SAL_CALL OTempFileService::getPosition( )
+{
+ std::unique_lock aGuard( maMutex );
+ checkConnected();
+
+ sal_uInt64 nPos = mpStream->Tell();
+ checkError();
+ return static_cast<sal_Int64>(nPos);
+}
+sal_Int64 SAL_CALL OTempFileService::getLength( )
+{
+ std::unique_lock aGuard( maMutex );
+ checkConnected();
+
+ checkError();
+
+ sal_Int64 nEndPos = mpStream->TellEnd();
+
+ return nEndPos;
+}
+
+// XStream
+
+css::uno::Reference< css::io::XInputStream > SAL_CALL OTempFileService::getInputStream()
+{
+ return this;
+}
+
+css::uno::Reference< css::io::XOutputStream > SAL_CALL OTempFileService::getOutputStream()
+{
+ return this;
+}
+
+// XTruncate
+
+void SAL_CALL OTempFileService::truncate()
+{
+ std::unique_lock aGuard( maMutex );
+ checkConnected();
+ // SetStreamSize() call does not change the position
+ mpStream->Seek( 0 );
+ mpStream->SetStreamSize( 0 );
+ checkError();
+}
+
+#define PROPERTY_HANDLE_URI 1
+#define PROPERTY_HANDLE_REMOVE_FILE 2
+#define PROPERTY_HANDLE_RESOURCE_NAME 3
+
+// XPropertySet
+::css::uno::Reference< ::css::beans::XPropertySetInfo > OTempFileService::getPropertySetInfo()
+{
+ // Create a table that map names to index values.
+ // attention: properties need to be sorted by name!
+ static cppu::OPropertyArrayHelper ourPropertyInfo(
+ {
+ css::beans::Property( "Uri", PROPERTY_HANDLE_URI, cppu::UnoType<OUString>::get(),
+ css::beans::PropertyAttribute::READONLY ),
+ css::beans::Property( "RemoveFile", PROPERTY_HANDLE_REMOVE_FILE, cppu::UnoType<bool>::get(),
+ 0 ),
+ css::beans::Property( "ResourceName", PROPERTY_HANDLE_RESOURCE_NAME, cppu::UnoType<OUString>::get(),
+ css::beans::PropertyAttribute::READONLY )
+ },
+ true );
+ static css::uno::Reference< css::beans::XPropertySetInfo > xInfo(
+ ::cppu::OPropertySetHelper::createPropertySetInfo( ourPropertyInfo ) );
+ return xInfo;
+}
+void OTempFileService::setPropertyValue( const ::rtl::OUString& aPropertyName, const ::css::uno::Any& aValue )
+{
+ if ( aPropertyName == "RemoveFile" )
+ setRemoveFile( aValue.get<bool>() );
+ else
+ {
+ assert(false);
+ throw css::beans::UnknownPropertyException(aPropertyName);
+ }
+}
+::css::uno::Any OTempFileService::getPropertyValue( const ::rtl::OUString& aPropertyName )
+{
+ if ( aPropertyName == "RemoveFile" )
+ return css::uno::Any(getRemoveFile());
+ else if ( aPropertyName == "ResourceName" )
+ return css::uno::Any(getResourceName());
+ else if ( aPropertyName == "Uri" )
+ return css::uno::Any(getUri());
+ else
+ {
+ assert(false);
+ throw css::beans::UnknownPropertyException(aPropertyName);
+ }
+}
+void OTempFileService::addPropertyChangeListener( const ::rtl::OUString& /*aPropertyName*/, const ::css::uno::Reference< ::css::beans::XPropertyChangeListener >& /*xListener*/ )
+{
+ assert(false);
+}
+void OTempFileService::removePropertyChangeListener( const ::rtl::OUString& /*aPropertyName*/, const ::css::uno::Reference< ::css::beans::XPropertyChangeListener >& /*xListener*/ )
+{
+ assert(false);
+}
+void OTempFileService::addVetoableChangeListener( const ::rtl::OUString& /*aPropertyName*/, const ::css::uno::Reference< ::css::beans::XVetoableChangeListener >& /*xListener*/ )
+{
+ assert(false);
+}
+void OTempFileService::removeVetoableChangeListener( const ::rtl::OUString& /*aPropertyName*/, const ::css::uno::Reference< ::css::beans::XVetoableChangeListener >& /*xListener*/ )
+{
+ assert(false);
+}
+// XFastPropertySet
+void OTempFileService::setFastPropertyValue( ::sal_Int32 nHandle, const ::css::uno::Any& aValue )
+{
+ switch (nHandle)
+ {
+ case PROPERTY_HANDLE_REMOVE_FILE: setRemoveFile( aValue.get<bool>() ); return;
+ }
+ assert(false);
+ throw css::beans::UnknownPropertyException(OUString::number(nHandle));
+}
+::css::uno::Any OTempFileService::getFastPropertyValue( ::sal_Int32 nHandle )
+{
+ switch (nHandle)
+ {
+ case PROPERTY_HANDLE_REMOVE_FILE: return css::uno::Any(getRemoveFile());
+ case PROPERTY_HANDLE_RESOURCE_NAME: return css::uno::Any(getResourceName());
+ case PROPERTY_HANDLE_URI: return css::uno::Any(getUri());
+ }
+ assert(false);
+ throw css::beans::UnknownPropertyException(OUString::number(nHandle));
+}
+// XPropertyAccess
+::css::uno::Sequence< ::css::beans::PropertyValue > OTempFileService::getPropertyValues()
+{
+ return {
+ css::beans::PropertyValue("Uri", PROPERTY_HANDLE_URI, css::uno::Any(getUri()), css::beans::PropertyState_DEFAULT_VALUE),
+ css::beans::PropertyValue("RemoveFile", PROPERTY_HANDLE_REMOVE_FILE, css::uno::Any(getRemoveFile()), css::beans::PropertyState_DEFAULT_VALUE),
+ css::beans::PropertyValue("ResourceName", PROPERTY_HANDLE_RESOURCE_NAME, css::uno::Any(getResourceName()), css::beans::PropertyState_DEFAULT_VALUE)
+ };
+}
+void OTempFileService::setPropertyValues( const ::css::uno::Sequence< ::css::beans::PropertyValue >& aProps )
+{
+ for ( auto const & rPropVal : aProps )
+ setPropertyValue( rPropVal.Name, rPropVal.Value );
+}
+
+// XServiceInfo
+sal_Bool OTempFileService::supportsService(const OUString& sServiceName)
+{
+ return cppu::supportsService(this, sServiceName);
+}
+OUString OTempFileService::getImplementationName()
+{
+ return "com.sun.star.io.comp.TempFile";
+}
+css::uno::Sequence< OUString > OTempFileService::getSupportedServiceNames()
+{
+ return { "com.sun.star.io.TempFile" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+unotools_OTempFileService_get_implementation(
+ css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new OTempFileService(context));
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/unotools/util/utl.component b/unotools/util/utl.component
new file mode 100644
index 0000000000..cd31d1c3e3
--- /dev/null
+++ b/unotools/util/utl.component
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.io.comp.TempFile"
+ constructor="unotools_OTempFileService_get_implementation">
+ <service name="com.sun.star.io.TempFile"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.unotools.misc.ServiceDocumenter"
+ constructor="unotools_ServiceDocument_get_implementation"
+ single-instance="true">
+ <singleton name="com.sun.star.util.theServiceDocumenter"/>
+ </implementation>
+</component>