diff options
226 files changed, 21527 insertions, 1254 deletions
diff --git a/Repository.mk b/Repository.mk index 0cb81f699ba4..3f5d69b0ddc4 100644 --- a/Repository.mk +++ b/Repository.mk @@ -97,6 +97,7 @@ $(eval $(call gb_Helper_register_executables_for_install,SDK,sdk, \ javamaker \ netmaker \ pythonmaker \ + $(if $(ENABLE_RUST_UNO),rustmaker) \ $(call gb_CondExeSp2bv,sp2bv) \ $(if $(filter ODK,$(BUILD_TYPE)),unoapploader) \ unoidl-read \ @@ -458,6 +459,7 @@ $(eval $(call gb_Helper_register_libraries_for_install,OOOLIBS,ooo, \ passwordcontainer \ pcr \ pdffilter \ + $(if $(ENABLE_RUST_UNO),rust_uno-cpp) \ $(call gb_Helper_optional,SCRIPTING,protocolhandler) \ sax \ sb \ @@ -777,6 +779,7 @@ $(eval $(call gb_Helper_register_libraries,EXTENSIONLIBS, \ active_native \ passive_native \ crashextension \ + rust_uno-example \ )) ifneq ($(ENABLE_JAVA),) diff --git a/RepositoryExternal.mk b/RepositoryExternal.mk index e5683f408009..432a8ff16b3f 100644 --- a/RepositoryExternal.mk +++ b/RepositoryExternal.mk @@ -659,13 +659,6 @@ endef gb_ExternalProject__use_boost_iostreams := -define gb_LinkTarget__use_boost_system -$(call gb_LinkTarget__use_boost_lib,$(1),$(BOOST_SYSTEM_LIB)) - -endef - -gb_ExternalProject__use_boost_system := - define gb_LinkTarget__use_boost_headers $(call gb_LinkTarget_set_include,$(1),\ $$(INCLUDE) \ @@ -718,15 +711,6 @@ define gb_ExternalProject__use_boost_iostreams $(call gb_ExternalProject_use_static_libraries,$(1),boost_iostreams) endef -define gb_LinkTarget__use_boost_system -$(call gb_LinkTarget__use_boost_lib,$(1),boost_system) - -endef - -define gb_ExternalProject__use_boost_system -$(call gb_ExternalProject_use_static_libraries,$(1),boost_system) -endef - define gb_LinkTarget__use_boost_headers $(call gb_LinkTarget_use_unpacked,$(1),boost) $(call gb_LinkTarget_set_include,$(1),\ diff --git a/RepositoryModule_host.mk b/RepositoryModule_host.mk index 8d23ee960f68..bb5add564c0c 100644 --- a/RepositoryModule_host.mk +++ b/RepositoryModule_host.mk @@ -126,6 +126,7 @@ $(eval $(call gb_Module_add_moduledirs,libreoffice,\ reportbuilder \ $(call gb_Helper_optional,DBCONNECTIVITY,reportdesign) \ ridljar \ + rust_uno \ sal \ salhelper \ sax \ diff --git a/android/source/res/values-ab/strings.xml b/android/source/res/values-ab/strings.xml index 4a4cb755f153..185c1c978b45 100644 --- a/android/source/res/values-ab/strings.xml +++ b/android/source/res/values-ab/strings.xml @@ -1,3 +1,119 @@ <?xml version="1.0" encoding="utf-8"?> <resources> - </resources> + <string name="app_name">LibreOffice Ахәаԥшыга</string> + <string name="app_version">Аверсиа: %1$s<br>Build ID: <a href=https://hub.libreoffice.org/git-core/%2$s>%2$s</a></string> + <string name="new_textdocument">Атексттә документ ҿыц</string> + <string name="title_recents">Ааигәатәи афаилқәа</string> + <string name="calc_optimal_length">Иоптималу аура</string> + <string name="action_keyboard">Аклавиатура аарԥштәуп</string> + <string name="message_saving_failed">Иамуӡеит адокумент аиқәырхара</string> + <string name="calc_show">Иаарԥштәуп</string> + <string name="pdf_export_finished">PDF аекспорт хыркәшоуп</string> + <string name="highlight_color">Алкаара аԥштәы</string> + <string name="select_insert_options">Иалышәх аҭаргылара апараметрқәа:</string> + <string name="calc_optimal_width">Иоптималу аҭбаара</string> + <string name="font_color">Ашрифт аԥштәы</string> + <string name="action_pwd_dialog_OK">ОК</string> + <string name="save_alert_dialog_title">Еиқәырхатәума адокумент аркра аԥхьа?</string> + <string name="action_copy">Икопиатәуп</string> + <string name="UNO_commands_string_type_hint">Атип</string> + <string name="pref_experimental_editing_summary">Иаҿактәуп аексперименталтә редакциазура арежим. Ара, иҟалар зылшо агхақәа рзы аҭакԥхықәра шәара ишәыдлоит.</string> + <string name="app_vendor">Ари аверсиа аԥҵара $VENDOR иабзоуроуп.</string> + <string name="action_delete_worksheet">Ианыхтәуп аусуратә бӷьыц</string> + <string name="action_presentation">Апрезентациа арбара</string> + <string name="compress_photo_title">Ишәҭахума афото аиҵацалара?</string> + <string name="action_add_worksheet">Аусуратә бӷьыц ацҵатәуп</string> + <string name="calc_alert_double_click_optimal_length">Аҵаҳәара: ахы ҩынтә ақәыӷәӷәара иқәнаргылоит иоптималу аҭбаара/аҳаракыра.</string> + <string name="about_license">Иаарԥштәуп алицензиа</string> + <string name="action_cut">Игәылԥҟатәуп</string> + <string name="app_name_settings">LibreOffice Ахәаԥшыга архиара</string> + <string name="app_credits">https://www.libreoffice.org</string> + <string name="app_description">LibreOffice ахәаԥшыга — ари апрограмма LibreOffice аҟны иаԥҵаз адокументқәа рыхәаԥшра иазкуп.</string> + <string name="readonly_file">Ари афаил аԥхьара мацара иазкуп.</string> + <string name="create_file">Иаԥҵатәуп афаил ҿыц</string> + <string name="new_presentation">Апрезентациа ҿыц</string> + <string name="new_spreadsheet">Алектронтә таблица ҿыц</string> + <string name="new_drawing">Асахьа ҿыц</string> + <string name="search_find_next">Иԥшаатәуп анаҩс</string> + <string name="title_browser">Афаилқәа зегьы</string> + <string name="pref_experimental_editing">Аексперименталтә режим</string> + <string name="pref_developer_mode">Аԥҵаҩы ирежим</string> + <string name="action_settings">Архиарақәа</string> + <string name="action_save">Еиқәырхатәуп</string> + <string name="action_save_as">Еиқәырхатәуп абас…</string> + <string name="action_fromat">Иаҿактәуп аформат</string> + <string name="action_search">Аԥшаара</string> + <string name="message_saving">Адокумент аиқәырхара…</string> + <string name="password">Ажәамаӡа</string> + <string name="action_undo">Иаҟәыхтәуп</string> + <string name="save_document">ЕИҚӘЫРХАТӘУП</string> + <string name="action_cancel">Аҟәыхра</string> + <string name="no_save_document">МАП</string> + <string name="action_add_slide">Аслаид ацҵатәуп</string> + <string name="slideshow_action_back">Шьҭахьҟа</string> + <string name="calc_insert_before">Иҭаргылатәуп</string> + <string name="calc_delete">Ианыхтәуп</string> + <string name="calc_hide">Иҵәахтәуп</string> + <string name="calc_optimal_height">Иоптималу аҳаракыра</string> + <string name="about_notice">Иаарԥштәуп ардырра</string> + <string name="about_privacy_policy">Аконфиденциалра аполитика</string> + <string name="select_file_to_open">Иалышәх иаарттәу афаил</string> + <string name="search_find_previous">Иԥшаатәуп аԥхьатәи</string> + <string name="pref_category_general">Азеиԥшқәа</string> + <string name="default_document_name">ахьӡ змам</string> + <string name="file_icon_desc">афаил адыргаҷ</string> + <string name="action_about">Апрограмма иазкны</string> + <string name="action_parts">Ахәҭақәа</string> + <string name="action_UNO_commands">Ишьҭтәуп UNO акоманда</string> + <string name="message_saved">Аиқәырхара нагӡоуп</string> + <string name="pref_developer_mode_summary">Иаҿашәк аԥҵаҩы ирежим, ара иауеит UNO акомандақәа апрограмма аҩныҵҟа рышьҭра. Ара, иҟалар зылшо агхақәа рзы аҭакԥхықәра шәара ишәыдлоит.</string> + <string name="action_redo">Шьҭахьҟа архынҳәра</string> + <string name="calc_adjust_length">Ишәырманшәал аура</string> + <string name="calc_adjust_height">Ишәырманшәал аҳаракыра</string> + <string name="calc_adjust_width">Ишәырманшәал аҭбаара</string> + <string name="calc_optimal_length_confirm">ОК</string> + <string name="alert_ok">ОК</string> + <string name="alert_cancel">Аҟәыхра</string> + <string name="unable_to_export_pdf">PDF ахь аекспорт ауам</string> + <string name="unable_to_save">Иамуӡеит афаил аиқәырхара</string> + <string name="error">Агха</string> + <string name="page">Адаҟьа</string> + <string name="sheet">Абӷьыц</string> + <string name="slide">Аслаид</string> + <string name="action_pwd_dialog_cancel">Аҟәыхра</string> + <string name="action_pwd_dialog_title">Иҭажәгал ажәамаӡа</string> + <string name="no_camera_found">Акамера ԥшаам</string> + <string name="compress_photo_max_quality">Имаксималу ахаҭабзиара</string> + <string name="compress_photo_no_compress">Еиҵацалатәӡам</string> + <string name="action_paste">Иҭаргылатәуп</string> + <string name="action_back">Шьҭахьҟа</string> + <string name="insert_table">Иҭаргылатәуп атаблица</string> + <string name="select_delete_options">Иалышәх аныхра апараметрқәа:</string> + <string name="action_delete_slide">Ианыхтәуп аслаид</string> + <string name="UNO_commands_string_hint">UNO акоманда</string> + <string name="UNO_commands_string_value_hint">Аҵакы</string> + <string name="action_exportToPDF">PDF ахь аекспорт</string> + <string name="action_print">Акьыԥхьра</string> + <string name="tabhost_character">Асимвол</string> + <string name="tabhost_paragraph">Абзац</string> + <string name="tabhost_insert">Иҭаргылатәуп</string> + <string name="tabhost_style">Астиль</string> + <string name="select_photo">Иалхтәуп афото</string> + <string name="select_photo_title">Иалхтәуп асахьа</string> + <string name="compress_photo_smallest_size">Иреиҵаӡоу ашәагаа</string> + <string name="compress_photo_medium_size">Абжьаратә шәагаа</string> + <string name="action_text_copied">Атекст аиҭныԥсахларатә буфер ахь акопиа</string> + <string name="action_rename_worksheet">Аусуратә бӷьыц ахьӡ ԥсахтәуп</string> + <string name="name_already_used">Ари ахьӡ ахархәара амоуп.</string> + <string name="part_name_changed">Ахәҭа ахьӡ ԥсахуп.</string> + <string name="part_deleted">Ахәҭа аныхуп.</string> + <string name="current_uno_command">Уажәтәи UNO акоманда</string> + <string name="enter_part_name">Иҭажәгал ахәҭа ахьӡ</string> + <string name="bmp_null">Bmp ашәагаа нульуп</string> + <string name="part">Ахәҭа</string> + <string name="take_photo">Афото ҟаҵатәуп</string> + <string name="calc_optimal_length_default_text">Иҭажәгал иацҵоу аура 1/100 мм ахәҭа ала</string> + <string name="UNO_commands_string_parent_value_hint">Излыҵыз аҵакы</string> + <string name="action_rename_slide">Аслаид ахьӡ ԥсахтәуп</string> + <string name="automatic">Автоматикала</string> +</resources> diff --git a/bin/find-can-be-private-symbols.functions.results b/bin/find-can-be-private-symbols.functions.results index fd2aebafc784..0646ae702adf 100644 --- a/bin/find-can-be-private-symbols.functions.results +++ b/bin/find-can-be-private-symbols.functions.results @@ -32,7 +32,6 @@ BasicManager::SetLibraryContainerInfo(LibraryContainerInfo const&) BasicManager::~BasicManager() BigInt::BigInt(unsigned int) BigInt::operator%=(BigInt const&) -BitmapAlphaClampFilter::execute(BitmapEx const&) const BitmapBasicMorphologyFilter::filter(Bitmap const&) const BitmapColorQuantizationFilter::execute(BitmapEx const&) const BitmapConvolutionMatrixFilter::execute(BitmapEx const&) const diff --git a/canvas/inc/pch/precompiled_vclcanvas.hxx b/canvas/inc/pch/precompiled_vclcanvas.hxx index d8a0e367643f..dee8538d638d 100644 --- a/canvas/inc/pch/precompiled_vclcanvas.hxx +++ b/canvas/inc/pch/precompiled_vclcanvas.hxx @@ -35,7 +35,6 @@ #include <vcl/BitmapReadAccess.hxx> #include <vcl/BitmapTools.hxx> #include <vcl/alpha.hxx> -#include <vcl/bitmap/BitmapAlphaClampFilter.hxx> #include <vcl/canvastools.hxx> #include <vcl/dibtools.hxx> #include <vcl/gradient.hxx> diff --git a/canvas/source/vcl/canvashelper.cxx b/canvas/source/vcl/canvashelper.cxx index ac182c7ae73a..909f25b979fc 100644 --- a/canvas/source/vcl/canvashelper.cxx +++ b/canvas/source/vcl/canvashelper.cxx @@ -40,7 +40,6 @@ #include <vcl/bitmap.hxx> #include <vcl/BitmapReadAccess.hxx> #include <vcl/canvastools.hxx> -#include <vcl/bitmap/BitmapAlphaClampFilter.hxx> #include <vcl/skia/SkiaHelper.hxx> #include <canvas/canvastools.hxx> diff --git a/codemaker/Executable_rustmaker.mk b/codemaker/Executable_rustmaker.mk new file mode 100644 index 000000000000..1fb006b23e83 --- /dev/null +++ b/codemaker/Executable_rustmaker.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_Executable_Executable,rustmaker)) + +$(eval $(call gb_Executable_use_libraries,rustmaker,\ + salhelper \ + sal \ + unoidl \ +)) + +$(eval $(call gb_Executable_use_static_libraries,rustmaker,\ + codemaker \ +)) + +$(eval $(call gb_Executable_add_exception_objects,rustmaker,\ + codemaker/source/rustmaker/rustmaker \ + codemaker/source/rustmaker/rustoptions \ + codemaker/source/rustmaker/rustproduce \ + codemaker/source/rustmaker/rustfile \ + codemaker/source/rustmaker/cpproduce \ + codemaker/source/rustmaker/unoproduce \ + codemaker/source/rustmaker/type_analyzer \ + codemaker/source/rustmaker/cpp_include_manager \ +)) + +# vim:set noet sw=4 ts=4: diff --git a/codemaker/Module_codemaker.mk b/codemaker/Module_codemaker.mk index 36f44f231195..495400366539 100644 --- a/codemaker/Module_codemaker.mk +++ b/codemaker/Module_codemaker.mk @@ -19,6 +19,7 @@ $(eval $(call gb_Module_add_targets,codemaker,\ Executable_cppumaker \ Executable_netmaker \ Executable_pythonmaker \ + $(if $(ENABLE_RUST_UNO),Executable_rustmaker) \ )) endif diff --git a/codemaker/source/rustmaker/cpp_include_manager.cxx b/codemaker/source/rustmaker/cpp_include_manager.cxx new file mode 100644 index 000000000000..bbf4b98bb777 --- /dev/null +++ b/codemaker/source/rustmaker/cpp_include_manager.cxx @@ -0,0 +1,174 @@ +/* -*- 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 "cpp_include_manager.hxx" +#include "rustfile.hxx" +#include <filesystem> +#include <algorithm> +#include <sal/log.hxx> + +CppIncludeManager::CppIncludeManager(const TypeAnalyzer::TypeInfo& typeInfo) + : m_typeInfo(typeInfo) +{ +} + +void CppIncludeManager::dumpIncludes(CppFile& file, std::string_view interfaceName) +{ + // Write file header comment + file.beginLine() + .append("// Auto-generated C++ bridge functions for ") + .append(interfaceName) + .endLine() + .beginLine() + .append("// Generated by rustmaker with dynamic includes") + .endLine() + .endLine(); + + // Write pragma once directive to prevent multiple inclusions (only for header files) + if (file.getExtension() == ".hxx") + { + file.beginLine().append("#pragma once").endLine().endLine(); + } + + // Write standard includes + writeStandardIncludes(file); + + // Write UNO-specific includes based on type analysis + writeUnoIncludes(file); + + // Write interface-specific includes + writeInterfaceIncludes(file, interfaceName); + + file.endLine(); +} + +void CppIncludeManager::dumpNamespaces(CppFile& file) +{ + // Generate specific namespace using declarations for only what we need + for (const auto& ns : m_typeInfo.namespaces) + { + file.beginLine().append("using namespace ").append(ns).append(";").endLine(); + } + file.endLine(); +} + +void CppIncludeManager::writeStandardIncludes(CppFile& file) +{ + // Always needed for UNO operations + if (m_typeInfo.needsUnoInterface) + { + file.beginLine().append("#include <com/sun/star/uno/XInterface.hpp>").endLine(); + file.beginLine().append("#include <com/sun/star/uno/Reference.hxx>").endLine(); + file.beginLine().append("#include <com/sun/star/uno/Exception.hpp>").endLine(); + file.beginLine().append("#include <sal/log.hxx>").endLine(); // For SAL_WARN macro + } + + // Basic type includes + if (m_typeInfo.needsSalTypes) + { + file.beginLine().append("#include <sal/types.h>").endLine(); + } + + if (m_typeInfo.needsRtlUstring) + { + file.beginLine().append("#include <rtl/ustring.hxx>").endLine(); + } +} + +void CppIncludeManager::writeUnoIncludes(CppFile& file) +{ + // Core UNO types - implemented: Any + if (m_typeInfo.needsAny) + { + file.beginLine().append("#include <com/sun/star/uno/Any.hxx>").endLine(); + file.beginLine().append("#include <uno/data.h>").endLine(); + file.beginLine().append("#include <cppu/unotype.hxx>").endLine(); + } + + if (m_typeInfo.needsSequence) + { + file.beginLine().append("#include <com/sun/star/uno/Sequence.hxx>").endLine(); + file.beginLine().append("#include <uno/sequence2.h>").endLine(); + file.beginLine().append("#include <typelib/typedescription.h>").endLine(); + } + + if (m_typeInfo.needsPropertyValue) + { + // TODO: implement PropertyValue handling + file.beginLine().append("// TODO: Add PropertyValue includes").endLine(); + file.beginLine().append("#include <com/sun/star/beans/PropertyValue.hpp>").endLine(); + } +} + +void CppIncludeManager::writeInterfaceIncludes(CppFile& file, std::string_view interfaceName) +{ + // Include the interface being processed + OString selfInclude = "<" + OString(interfaceName).replaceAll("."_ostr, "/"_ostr) + ".hpp>"; + file.beginLine().append("#include ").append(selfInclude).endLine(); + + // Include headers for all interface types found in the analysis + for (const auto& interfaceType : m_typeInfo.interfaceTypes) + { + // Don't include ourselves twice + if (interfaceType != interfaceName) + { + OString includeFile = "<" + interfaceType.replaceAll("."_ostr, "/"_ostr) + ".hpp>"; + file.beginLine().append("#include ").append(includeFile).endLine(); + } + } +} + +OString CppIncludeManager::calculateRelativeIncludePath(const CppFile& file, + const OString& targetType, + const std::string& extension) +{ + namespace fs = std::filesystem; + + // Get the current file's directory + fs::path currentFilePath = file.getPath(); + fs::path currentDir = currentFilePath.parent_path(); + + // Convert target type name to path (dots to slashes) + std::string targetTypeStr = std::string(targetType); + std::replace(targetTypeStr.begin(), targetTypeStr.end(), '.', '/'); + + // Find the base directory (cpp) + fs::path baseDir = currentDir; + while (baseDir.has_parent_path() && baseDir.filename() != "cpp") + { + baseDir = baseDir.parent_path(); + } + + // Create target file path + fs::path target = (baseDir / (targetTypeStr + extension)).lexically_normal(); + + // If they're in the same directory, just use the filename + if (target.parent_path() == currentDir) + { + std::string result = target.filename().string(); + return OString("\"" + result + "\""); + } + + // Otherwise calculate relative path from current directory to target file + fs::path rel; + try + { + rel = fs::relative(target, currentDir); + } + catch (const std::exception&) + { + // Fallback: use the path relative to base directory + rel = fs::relative(target, baseDir); + } + + std::string result = rel.generic_string(); + return OString("\"" + result + "\""); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/rustmaker/cpp_include_manager.hxx b/codemaker/source/rustmaker/cpp_include_manager.hxx new file mode 100644 index 000000000000..ea9a190b1680 --- /dev/null +++ b/codemaker/source/rustmaker/cpp_include_manager.hxx @@ -0,0 +1,86 @@ +/* -*- 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/. + */ + +#pragma once + +#include "type_analyzer.hxx" +#include <rtl/string.hxx> + +// Forward declaration +class CppFile; + +/** + * Manages dynamic generation of C++ includes and namespace declarations + * based on type analysis results. + */ +class CppIncludeManager +{ +public: + /** + * Constructor that takes type analysis results. + * + * @param typeInfo The type analysis results from TypeAnalyzer. + */ + explicit CppIncludeManager(const TypeAnalyzer::TypeInfo& typeInfo); + + /** + * Writes the necessary #include directives to the C++ file. + * + * @param file The CppFile to write to. + * @param interfaceName The name of the interface being processed (for self-include). + */ + void dumpIncludes(CppFile& file, std::string_view interfaceName); + + /** + * Writes the necessary using namespace declarations to the C++ file. + * + * @param file The CppFile to write to. + */ + void dumpNamespaces(CppFile& file); + +private: + const TypeAnalyzer::TypeInfo& m_typeInfo; + + /** + * Writes standard system includes that are always needed. + * + * @param file The CppFile to write to. + */ + void writeStandardIncludes(CppFile& file); + + /** + * Writes UNO-specific includes based on type analysis. + * + * @param file The CppFile to write to. + */ + void writeUnoIncludes(CppFile& file); + + /** + * Writes interface-specific includes. + * + * @param file The CppFile to write to. + * @param interfaceName The name of the interface being processed. + */ + void writeInterfaceIncludes(CppFile& file, std::string_view interfaceName); + + /** + * Calculates relative include paths like cppumaker does. + * This generates the proper relative path from the current file being generated + * to the target dependency file. + * + * @param file The current file being generated (provides context for relative path) + * @param targetType The UNO type name (e.g., "com.sun.star.beans.Property") + * @param extension The file extension to use (e.g., ".hxx", ".hpp") + * @return Properly formatted include statement with quotes + */ + static OString calculateRelativeIncludePath(const CppFile& file, const OString& targetType, + const std::string& extension); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/rustmaker/cpproduce.cxx b/codemaker/source/rustmaker/cpproduce.cxx new file mode 100644 index 000000000000..cf8af02c0950 --- /dev/null +++ b/codemaker/source/rustmaker/cpproduce.cxx @@ -0,0 +1,2739 @@ +/* -*- 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 "cpproduce.hxx" +#include <rtl/string.hxx> +#include "rustfile.hxx" +#include "type_analyzer.hxx" +#include "cpp_include_manager.hxx" +#include <codemaker/typemanager.hxx> +#include <codemaker/unotype.hxx> +#include <sal/log.hxx> +#include <sal/types.h> +#include <iostream> +#include <filesystem> +#include <string_view> +#include <algorithm> + +CppProducer::CppProducer(const OString& outputDir, bool verbose, bool dryRun, + const rtl::Reference<TypeManager>& typeManager) + : m_outputDir(outputDir) + , m_verbose(verbose) + , m_dryRun(dryRun) + , m_typeManager(typeManager) + , m_combinedSourceFile() + , m_combinedHeaderFile() +{ +} + +void CppProducer::initializeCombinedFile() +{ + if (m_dryRun) + return; + + // Create the combined source file (.cxx) + m_combinedSourceFile = std::make_unique<CppFile>(m_outputDir, "rust_uno_bindings", ".cxx"); + std::filesystem::path sourceFilePath = m_combinedSourceFile->getPath(); + if (std::filesystem::exists(sourceFilePath)) + { + std::filesystem::remove(sourceFilePath); + } + m_combinedSourceFile->openFile(); + + // Include the header file + m_combinedSourceFile->beginLine().append("#include \"rust_uno_bindings.hxx\"").endLine(); + m_combinedSourceFile->beginLine().append("").endLine(); + + // Create the combined header file (.hxx) + m_combinedHeaderFile = std::make_unique<CppFile>(m_outputDir, "rust_uno_bindings", ".hxx"); + std::filesystem::path headerFilePath = m_combinedHeaderFile->getPath(); + if (std::filesystem::exists(headerFilePath)) + { + std::filesystem::remove(headerFilePath); + } + m_combinedHeaderFile->openFile(); + + // Add header guard + m_combinedHeaderFile->beginLine().append("#pragma once").endLine(); + m_combinedHeaderFile->beginLine().append("").endLine(); + + // Add only sal/types.h which definitely exists + m_combinedHeaderFile->beginLine().append("#include <sal/types.h>").endLine(); + m_combinedHeaderFile->beginLine().append("").endLine(); +} + +void CppProducer::finalizeCombinedFile() +{ + if (m_combinedSourceFile) + { + m_combinedSourceFile->closeFile(); + m_combinedSourceFile.reset(); + } + + if (m_combinedHeaderFile) + { + m_combinedHeaderFile->closeFile(); + m_combinedHeaderFile.reset(); + } +} + +void CppProducer::produceEnum(std::string_view name, + const rtl::Reference<unoidl::EnumTypeEntity>& entity) +{ + if (m_verbose) + std::cout << "[cpp-opaque-enum] " << name << " -> enum bridge functions" << '\n'; + + generateEnumBridge(name, entity); +} + +void CppProducer::produceStruct(std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity) +{ + if (m_verbose) + std::cout << "[cpp-opaque-struct] " << name + << " -> constructor/destructor + getter/setter functions" << '\n'; + + generateStructWrapper(name, entity); + // generateStructBridge(name, entity); +} + +void CppProducer::produceInterface(std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity) +{ + if (m_verbose) + std::cout << "[cpp-opaque-interface] " << name + << " -> opaque handle + method wrapper functions" << '\n'; + + generateInterfaceHeader(name, entity); + generateInterfaceSource(name, entity); +} + +void CppProducer::produceService( + std::string_view name, const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& entity) +{ + if (m_verbose) + std::cout << "[cpp-opaque-service] " << name << " -> service creation bridge functions" + << '\n'; + + generateServiceHeader(name, entity); + generateServiceSource(name, entity); +} + +void CppProducer::generateEnumBridge(std::string_view name, + const rtl::Reference<unoidl::EnumTypeEntity>& entity) +{ + generateEnumHeader(name, entity); + generateEnumSource(name, entity); +} + +void CppProducer::generateStructWrapper(std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity) +{ + generateStructHeader(name, entity); + generateStructSource(name, entity); +} + +void CppProducer::generateStructBridge(std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>&) +{ + CppFile file(m_outputDir, name); + + if (m_dryRun) + return; + + file.openFile(); + + file.beginLine().append("extern \"C\"").endLine().beginBlock(); + + // Simple opaque struct bridge functions (like old FFI approach) + OString className = getInterfaceClassName(name); + OString functionPrefix = getCFunctionPrefix(name); + + file.beginLine() + .append("// Opaque struct creation") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT void* ") + .append(functionPrefix) + .append("_create()") + .endLine() + .beginBlock() + .beginLine() + .append("return new ") + .append(className) + .append("();") + .endLine() + .endBlock(); + + file.beginLine().append("").endLine(); + file.endBlock().append(" // extern \"C\""); + + file.closeFile(); +} + +void CppProducer::generateCBridgeIncludes(CppFile& file, std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity) +{ + // Use existing infrastructure for includes + TypeAnalyzer analyzer(m_typeManager); + TypeAnalyzer::TypeInfo typeInfo = analyzer.analyzeInterface(entity); + CppIncludeManager includeManager(typeInfo); + + includeManager.dumpIncludes(file, name); +} + +void CppProducer::generateCBridgeTypeDefinitions(CppFile& file, const OString& handleTypeName) +{ + // Opaque handle typedef for type safety + file.beginLine() + .append("// Opaque handle for type-safe C FFI") + .endLine() + .beginLine() + .append("typedef void* ") + .append(handleTypeName) + .append(";") + .endLine() + .beginLine() + .append("") + .endLine(); +} + +void CppProducer::generateCBridgeConstructors(CppFile& file, const OString& className, + const OString& functionPrefix, + const OString& handleTypeName) +{ + // Opaque constructor function - creates interface instance from existing Reference + file.beginLine() + .append("// Opaque constructor - creates interface wrapper from UNO Reference") + .endLine() + .beginLine() + .append(handleTypeName) + .append(" ") + .append(functionPrefix) + .append("_create_from_reference(void* xInterface)") + .endLine() + .beginBlock() + .beginLine() + .append("if (!xInterface) return nullptr;") + .endLine() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("// Cast void* back to Reference<") + .append(className) + .append(">") + .endLine() + .beginLine() + .append("Reference<") + .append(className) + .append(">* pRef = static_cast<Reference<") + .append(className) + .append(">*>(xInterface);") + .endLine() + .beginLine() + .append("if (!pRef->is()) return nullptr;") + .endLine() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("// Create wrapper with interface reference") + .endLine() + .beginLine() + .append("return new ") + .append(className) + .append("Wrapper(*pRef);") + .endLine() + .endBlock() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("// Opaque service creation - attempts to create UNO service") + .endLine() + .beginLine() + .append("// Note: This is a simplified approach - real service creation") + .endLine() + .beginLine() + .append("// would need ComponentContext and service name resolution") + .endLine() + .beginLine() + .append(handleTypeName) + .append(" ") + .append(functionPrefix) + .append("_create_service(void* xContext, const char* serviceName)") + .endLine() + .beginBlock() + .beginLine() + .append("if (!xContext || !serviceName) return nullptr;") + .endLine() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("try") + .endLine() + .beginBlock() + .beginLine() + .append("// Cast context") + .endLine() + .beginLine() + .append("Reference<XComponentContext>* pContext =") + .endLine() + .extraIndent() + .beginLine() + .append("static_cast<Reference<XComponentContext>*>(xContext);") + .endLine() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("// Create service") + .endLine() + .beginLine() + .append("OUString svcName = OUString::createFromAscii(serviceName);") + .endLine() + .beginLine() + .append("Reference<") + .append(className) + .append("> xService(") + .endLine() + .extraIndent() + .beginLine() + .append("pContext->getServiceManager()->createInstanceWithContext(") + .endLine() + .extraIndent() + .beginLine() + .append("svcName, *pContext), UNO_QUERY);") + .endLine() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("if (!xService.is()) return nullptr;") + .endLine() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("// Return wrapper") + .endLine() + .beginLine() + .append("return new ") + .append(className) + .append("Wrapper(xService);") + .endLine() + .endBlock() + .beginLine() + .append("catch (const Exception& ex)") + .endLine() + .beginBlock() + .beginLine() + .append("// TODO: Log exception details") + .endLine() + .beginLine() + .append("SAL_WARN(\"rustmaker\", \"UNO exception in interface method\");") + .endLine() + .beginLine() + .append("return nullptr;") + .endLine() + .endBlock() + .endBlock(); +} + +void CppProducer::generateCBridgeDestructor(CppFile& file, const OString& className, + const OString& functionPrefix, + const OString& handleTypeName) +{ + // Opaque destructor function - destroys interface instance + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("// Opaque destructor - destroys interface wrapper") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT void ") + .append(functionPrefix) + .append("_destroy(") + .append(handleTypeName) + .append(" handle)") + .endLine() + .beginBlock() + .beginLine() + .append("if (handle)") + .endLine() + .beginBlock() + .beginLine() + .append("delete static_cast<") + .append(className) + .append("Wrapper*>(handle);") + .endLine() + .endBlock(); +} + +void CppProducer::generateCBridgeValidation(CppFile& file, const OString& className, + const OString& functionPrefix, + const OString& handleTypeName) +{ + // Opaque validity check function + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("// Opaque validity check - tests if interface is valid") + .endLine() + .beginLine() + .append("bool ") + .append(functionPrefix) + .append("_is_valid(") + .append(handleTypeName) + .append(" handle)") + .endLine() + .beginBlock() + .beginLine() + .append("if (!handle) return false;") + .endLine() + .beginLine() + .append("return static_cast<") + .append(className) + .append("Wrapper*>(handle)->isValid();") + .endLine() + .endBlock(); +} + +void CppProducer::generateCBridgeMethods(CppFile& file, const OString& className, + const OString& functionPrefix, + const OString& handleTypeName, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity) +{ + // Opaque method bridge functions - each method gets unique extern "C" function + for (const auto& method : entity->getDirectMethods()) + { + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("// Opaque method bridge for ") + .append(method.name) + .endLine() + .beginLine() + .append(getMethodReturnType(method.returnType)) + .append(" ") + .append(functionPrefix) + .append("_") + .append(method.name) + .append("(") + .append(handleTypeName) + .append(" handle"); + + // Use proper types instead of void* for parameters + for (const auto& param : method.parameters) + { + file.append(", ").append(getCppTypeName(param.type)).append(" ").append(param.name); + } + + file.append(")") + .endLine() + .beginBlock() + .beginLine() + .append("if (!handle) return ") + .append(getMethodDefaultReturn(method.returnType)) + .append(";") + .endLine() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("auto* wrapper = static_cast<") + .append(className) + .append("Wrapper*>(handle);") + .endLine() + .beginLine() + .append("return wrapper->") + .append(method.name) + .append("("); + + // Pass through all opaque parameters + bool first = true; + for (const auto& param : method.parameters) + { + if (!first) + file.append(", "); + first = false; + file.append(param.name); + } + + file.append(");").endLine().endBlock(); + } +} + +void CppProducer::generateCBridge(std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity) +{ + CppFile file(m_outputDir, name); + + if (m_dryRun) + return; + + file.openFile(); + + // Generate file setup and includes + generateCBridgeIncludes(file, name, entity); + + // Generate type definitions + OString className = getInterfaceClassName(name); + OString functionPrefix = getCFunctionPrefix(name); + OString handleTypeName = functionPrefix + "Handle"; + + generateCBridgeTypeDefinitions(file, handleTypeName); + + file.beginLine().append("extern \"C\"").endLine().beginBlock(); + + // Generate constructor functions + generateCBridgeConstructors(file, className, functionPrefix, handleTypeName); + + // Generate destructor function + generateCBridgeDestructor(file, className, functionPrefix, handleTypeName); + + // Generate validation function + generateCBridgeValidation(file, className, functionPrefix, handleTypeName); + + // Generate method bridge functions + generateCBridgeMethods(file, className, functionPrefix, handleTypeName, entity); + + file.beginLine().append("").endLine(); + file.endBlock().append(" // extern \"C\""); + + file.closeFile(); +} + +void CppProducer::generateStructHeader(std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity) +{ + if (m_dryRun || !m_combinedHeaderFile) + return; + + // Use combined file instead of individual file + CppFile& file = *m_combinedHeaderFile; + file.openFileAppend(); + + // Generate includes following the pattern used in generateStructSource + file.beginLine().append("#include <sal/types.h>").endLine(); + file.beginLine().append("#include <com/sun/star/uno/Reference.hxx>").endLine(); + file.beginLine().append("#include <com/sun/star/uno/Any.hxx>").endLine(); + // Include the struct definition + std::string headerName(name); + std::replace(headerName.begin(), headerName.end(), '.', '/'); + file.beginLine().append("#include <").append(headerName).append(".hpp>").endLine(); + file.beginLine().append("").endLine(); + file.beginLine().append("using namespace com::sun::star::uno;").endLine(); + + file.beginLine().append("extern \"C\"").endLine().beginBlock(); + // Add struct-specific namespace + std::string cppNamespace(name); + size_t lastDot = cppNamespace.rfind('.'); + if (lastDot != std::string::npos) + { + cppNamespace = cppNamespace.substr(0, lastDot); + // Convert dots to double colons for C++ namespace syntax + size_t pos = 0; + while ((pos = cppNamespace.find('.', pos)) != std::string::npos) + { + cppNamespace.replace(pos, 1, "::"); + pos += 2; + } + } + + // Simple opaque struct bridge functions + OString functionPrefix = getCFunctionPrefix(name); + OString handleTypeName = functionPrefix + "Handle"; + file.beginLine() + .append("// Opaque handle for type-safe C FFI") + .endLine() + .beginLine() + .append("typedef void* ") + .append(handleTypeName) + .append(";") + .endLine() + .beginLine() + .append("") + .endLine(); + + file.beginLine() + .append("// Opaque struct creation") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT ") + .append(handleTypeName) + .append(" ") + .append(functionPrefix) + .append("_constructor();") + .endLine(); + + file.beginLine() + .append("// Opaque handle destruction") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT void ") + .append(functionPrefix) + .append("_destroy(") + .append(handleTypeName) + .append(" handle);") + .endLine(); + + file.beginLine() + .append("// Struct from_ptr type casting") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT ") + .append(handleTypeName) + .append(" ") + .append(functionPrefix) + .append("_from_ptr(void* ptr);") + .endLine(); + + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("// Getters/setters for direct struct members") + .endLine(); + + // Opaque member access functions - each member gets getter/setter + for (const auto& member : entity->getDirectMembers()) + { + file.beginLine() + .append("// Opaque getter for ") + .append(member.name) + .append(" (type: ") + .append(member.type) + .append(")") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT void* ") + .append(functionPrefix) + .append("_get_") + .append(member.name) + .append("(") + .append(handleTypeName) + .append(" handle);") + .endLine(); + + file.beginLine() + .append("// Opaque setter for ") + .append(member.name) + .append(" (type: ") + .append(member.type) + .append(")") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT void ") + .append(functionPrefix) + .append("_set_") + .append(member.name) + .append("(") + .append(handleTypeName) + .append(" handle, void* value);") + .endLine(); + } + file.endBlock().append("// extern \"C\"").endLine(); + file.closeFile(); +} + +// Unified include generation function to eliminate duplication +void CppProducer::generateCommonIncludes(CppFile& file, std::string_view name, bool needsLogging, + bool needsUnoTypes) +{ + // Always include sal/types.h + file.beginLine().append("#include <sal/types.h>").endLine(); + + // Include logging if needed + if (needsLogging) + { + file.beginLine().append("#include <sal/log.hxx>").endLine(); + } + + // Include UNO types if needed + if (needsUnoTypes) + { + file.beginLine().append("#include <com/sun/star/uno/Reference.hxx>").endLine(); + file.beginLine().append("#include <com/sun/star/uno/Any.hxx>").endLine(); + } + + // Include the type-specific header + std::string headerName(name); + std::replace(headerName.begin(), headerName.end(), '.', '/'); + file.beginLine().append("#include <").append(headerName).append(".hpp>").endLine(); + file.beginLine().append("").endLine(); +} + +void CppProducer::generateStructSourceIncludes(CppFile& file, std::string_view name) +{ + generateCommonIncludes(file, name, false, true); +} + +void CppProducer::generateEnumIncludes(CppFile& file, std::string_view name) +{ + generateCommonIncludes(file, name, false, false); +} + +void CppProducer::generateEnumSourceIncludes(CppFile& file, std::string_view name) +{ + generateCommonIncludes(file, name, true, false); +} + +void CppProducer::generateEnumHeader(std::string_view name, + const rtl::Reference<unoidl::EnumTypeEntity>&) +{ + if (m_dryRun || !m_combinedHeaderFile) + return; + + // Use combined file instead of individual file + CppFile& file = *m_combinedHeaderFile; + file.openFileAppend(); + + // Add separator comment for this enum + file.beginLine().append("").endLine(); + file.beginLine().append("// === ").append(name).append(" ===").endLine(); + + // Generate includes in combined file + generateEnumIncludes(file, name); + + // Generate extern "C" declarations + file.beginLine().append("extern \"C\"").endLine().beginBlock(); + + // Opaque enum conversion functions - treat enums as opaque values + OString enumName = convertUnoTypeToCpp(OUString::createFromAscii(std::string(name))); + OString functionPrefix = getCFunctionPrefix(name); // Use consistent double-underscore naming + file.beginLine() + .append(" SAL_DLLPUBLIC_EXPORT ") + .append(enumName) + .append(" ") + .append(functionPrefix) + .append("_from_i32(sal_Int32 value);") + .endLine(); + + file.beginLine() + .append(" SAL_DLLPUBLIC_EXPORT sal_Int32 ") + .append(functionPrefix) + .append("_to_i32(") + .append(enumName) + .append(" value);") + .endLine(); + + file.beginLine().append("}").endLine(); + + file.closeFile(); +} + +void CppProducer::generateEnumSource(std::string_view name, + const rtl::Reference<unoidl::EnumTypeEntity>&) +{ + if (m_dryRun || !m_combinedSourceFile) + return; + + // Use combined file instead of individual file + CppFile& file = *m_combinedSourceFile; + file.openFileAppend(); + + // Add separator comment for this enum + file.beginLine().append("").endLine(); + file.beginLine().append("// === ").append(name).append(" ===").endLine(); + + // Generate includes for source file + generateEnumSourceIncludes(file, name); + + // Generate extern "C" implementations + file.beginLine().append("extern \"C\"").endLine().beginBlock(); + + // Opaque enum conversion functions implementations + OString enumName = convertUnoTypeToCpp(OUString::createFromAscii(std::string(name))); + OString functionPrefix = getCFunctionPrefix(name); // Use consistent double-underscore naming + + // from_i32 implementation + file.beginLine() + .append("SAL_DLLPUBLIC_EXPORT ") + .append(enumName) + .append(" ") + .append(functionPrefix) + .append("_from_i32(sal_Int32 value)") + .endLine() + .beginBlock() + .beginLine() + .append("return static_cast<") + .append(enumName) + .append(">(value);") + .endLine() + .endBlock(); + + // to_i32 implementation + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT sal_Int32 ") + .append(functionPrefix) + .append("_to_i32(") + .append(enumName) + .append(" value)") + .endLine() + .beginBlock() + .beginLine() + .append("return static_cast<sal_Int32>(value);") + .endLine() + .endBlock(); + + file.beginLine().append("}").endLine(); + + file.closeFile(); +} + +// Unified namespace generation function to eliminate duplication +void CppProducer::generateSourceNamespaces(CppFile& file, std::string_view name) +{ + file.beginLine().append("using namespace com::sun::star::uno;").endLine(); + + // Add type-specific namespace + std::string cppNamespace(name); + size_t lastDot = cppNamespace.rfind('.'); + if (lastDot != std::string::npos) + { + cppNamespace = cppNamespace.substr(0, lastDot); + // Convert dots to double colons for C++ namespace syntax + size_t pos = 0; + while ((pos = cppNamespace.find('.', pos)) != std::string::npos) + { + cppNamespace.replace(pos, 1, "::"); + pos += 2; + } + file.beginLine().append("using namespace ").append(cppNamespace).append(";").endLine(); + } + file.beginLine().append("").endLine(); +} + +void CppProducer::generateStructSourceNamespaces(CppFile& file, std::string_view name) +{ + generateSourceNamespaces(file, name); +} + +void CppProducer::generateStructSourceBasicFunctions(CppFile& file, const OString& functionPrefix, + const OString& handleTypeName, + const std::string& className) +{ + // Generate struct creation function + file.beginLine() + .append("// Opaque struct creation") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT ") + .append(handleTypeName) + .append(" ") + .append(functionPrefix) + .append("_constructor()") + .endLine() + .beginBlock() + .beginLine() + .append("return new ") + .append(className) + .append("();") + .endLine() + .endBlock() + .beginLine() + .append("") + .endLine(); + + // Generate struct destroy function + file.beginLine() + .append("// Opaque handle destruction") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT void ") + .append(functionPrefix) + .append("_destroy(") + .append(handleTypeName) + .append(" handle)") + .endLine() + .beginBlock() + .beginLine() + .append("delete static_cast<") + .append(className) + .append("*>(handle);") + .endLine() + .endBlock() + .beginLine() + .append("") + .endLine(); + + // Generate from_ptr function for struct type casting + file.beginLine() + .append("// Struct from_ptr type casting") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT ") + .append(handleTypeName) + .append(" ") + .append(functionPrefix) + .append("_from_ptr(void* ptr)") + .endLine() + .beginBlock() + .beginLine() + .append("if (ptr == nullptr) return nullptr;") + .endLine() + .beginLine() + .append("// For structs, we assume the pointer is already the correct type") + .endLine() + .beginLine() + .append("// Create a copy of the struct") + .endLine() + .beginLine() + .append("") + .append(className) + .append("* source = static_cast<") + .append(className) + .append("*>(ptr);") + .endLine() + .beginLine() + .append("return new ") + .append(className) + .append("(*source);") + .endLine() + .endBlock() + .beginLine() + .append("") + .endLine(); +} + +void CppProducer::generateStructSourceBaseMembers( + CppFile& file, const OString& functionPrefix, const OString& handleTypeName, + const std::string& className, const rtl::Reference<unoidl::PlainStructTypeEntity>& entity) +{ + // Generate getters/setters for base type members (if struct has a base) + if (!entity->getDirectBase().isEmpty()) + { + file.beginLine() + .append("// Getters/setters for base type members (") + .append(entity->getDirectBase()) + .append(")") + .endLine(); + + // Get base type entity to access its members + rtl::Reference<unoidl::Entity> baseEntity; + codemaker::UnoType::Sort baseSort + = m_typeManager->getSort(entity->getDirectBase(), &baseEntity); + + if (baseSort == codemaker::UnoType::Sort::PlainStruct) + { + rtl::Reference<unoidl::PlainStructTypeEntity> baseStruct( + static_cast<unoidl::PlainStructTypeEntity*>(baseEntity.get())); + + for (const auto& baseMember : baseStruct->getDirectMembers()) + { + generateStructMemberAccessors(file, functionPrefix, handleTypeName, className, + baseMember); + } + } + } +} + +void CppProducer::generateStructSourceDirectMembers( + CppFile& file, const OString& functionPrefix, const OString& handleTypeName, + const std::string& className, const rtl::Reference<unoidl::PlainStructTypeEntity>& entity) +{ + // Generate getters/setters for direct struct members + file.beginLine().append("// Getters/setters for direct struct members").endLine(); + + for (const auto& member : entity->getDirectMembers()) + { + generateStructMemberAccessors(file, functionPrefix, handleTypeName, className, member); + } +} + +void CppProducer::generateStructMemberAccessors(CppFile& file, const OString& functionPrefix, + const OString& handleTypeName, + const std::string& className, + const unoidl::PlainStructTypeEntity::Member& member) +{ + // Generate getter implementation + file.beginLine() + .append("SAL_DLLPUBLIC_EXPORT void* ") + .append(functionPrefix) + .append("_get_") + .append(member.name) + .append("(") + .append(handleTypeName) + .append(" handle)") + .endLine() + .beginBlock() + .beginLine() + .append(className) + .append("* obj = static_cast<") + .append(className) + .append("*>(handle);") + .endLine() + .beginLine() + .append("return &(obj->") + .append(member.name) + .append(");") + .endLine() + .endBlock(); + + // Generate setter implementation + file.beginLine() + .append("SAL_DLLPUBLIC_EXPORT void ") + .append(functionPrefix) + .append("_set_") + .append(member.name) + .append("(") + .append(handleTypeName) + .append(" handle, void* value)") + .endLine() + .beginBlock() + .beginLine() + .append(className) + .append("* obj = static_cast<") + .append(className) + .append("*>(handle);") + .endLine() + .beginLine() + .append("obj->") + .append(member.name); + + // Check if this is a UNO interface type by checking the UNO type string + OUString unoType = member.type; + SAL_INFO("codemaker", "Struct member " << member.name << " UNO type: " << unoType); + + // UNO interface types start with "com.sun.star" and end with an interface name (typically starting with "X") + // Examples: "com.sun.star.uno.XInterface", "com.sun.star.text.XText" + bool isInterfaceType = (unoType.startsWith("com.sun.star.") && (unoType.lastIndexOf('.') != -1) + && (unoType.copy(unoType.lastIndexOf('.') + 1).startsWith("X"))); + + if (isInterfaceType) + { + SAL_INFO("codemaker", "Detected UNO interface type, using pointer assignment"); + // For interface types, assign the pointer directly without dereferencing + // The C++ struct expects Reference<Interface>, but we assign the raw interface pointer + file.append(" = reinterpret_cast<") + .append(convertUnoTypeToCpp(unoType)) + .append("*>(value);"); + } + else + { + SAL_INFO("codemaker", "Non-interface type, using dereferenced assignment"); + // For non-interface types, dereference the pointer as before + file.append(" = *reinterpret_cast<const ") + .append(convertUnoTypeToCpp(unoType)) + .append("*>(value);"); + } + file.endLine().endBlock().beginLine().append("").endLine(); +} + +void CppProducer::generateStructSource(std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity) +{ + if (m_dryRun || !m_combinedSourceFile) + return; + + // Use combined file instead of individual file + CppFile& file = *m_combinedSourceFile; + file.openFileAppend(); + + // Add separator comment for this struct + file.beginLine().append("").endLine(); + file.beginLine().append("// === ").append(name).append(" ===").endLine(); + + // Start isolated block for this struct to avoid namespace conflicts + file.beginLine().append("namespace"); + file.beginBlock(); + + // Generate includes + generateStructSourceIncludes(file, name); + + // Generate namespaces (now in isolated scope) + generateStructSourceNamespaces(file, name); + + OString functionPrefix = getCFunctionPrefix(name); + OString handleTypeName = functionPrefix + "Handle"; + // Use fully qualified C++ type name to avoid namespace ambiguity + OString className = convertUnoTypeToCpp(OUString::createFromAscii(std::string(name))); + + file.beginLine().append("extern \"C\"").endLine().beginBlock(); + + // Generate basic functions (creation/destruction) + generateStructSourceBasicFunctions(file, functionPrefix, handleTypeName, className.getStr()); + + // Generate accessors for base type members + generateStructSourceBaseMembers(file, functionPrefix, handleTypeName, className.getStr(), + entity); + + // Generate accessors for direct members + generateStructSourceDirectMembers(file, functionPrefix, handleTypeName, className.getStr(), + entity); + + file.endBlock().append(" // extern \"C\"").endLine(); + + // End isolated block for this struct + file.endBlock().append(" // End isolated block for ").append(name).endLine(); + + file.closeFile(); +} + +// Helper functions +std::string_view CppProducer::splitName(std::string_view name) +{ + size_t split = name.find_last_of(".::"); + if (split != std::string_view::npos) + return name.substr(split + 1); + else + return name; +} + +OString CppProducer::getInterfaceClassName(std::string_view unoName) +{ + return OString(splitName(unoName)); +} + +OString CppProducer::getCFunctionPrefix(std::string_view unoName) +{ + // Convert com.sun.star.lang.XMain to com__sun__star__lang__XMain for function names + OString temp(unoName); + return temp.replaceAll(".", "__"); +} + +OString CppProducer::getMethodReturnType(std::u16string_view returnType) const +{ + // Use the sophisticated type mapping from the old FFI producer + return getCppTypeName(returnType); +} + +OString CppProducer::getMethodDefaultReturn(std::u16string_view returnType) const +{ + // First resolve any typedefs to their underlying types + OUString resolvedType = resolveTypedef(returnType); + + // Handle void vs non-void return types properly + if (resolvedType == u"void") + return ""; // void methods don't return anything + + // Return appropriate default values for different types + if (resolvedType == u"double") + return "0.0"; + else if (resolvedType == u"float") + return "0.0f"; + else if (resolvedType == u"sal_Int32" || resolvedType == u"long" || resolvedType == u"sal_Int16" + || resolvedType == u"short" || resolvedType == u"sal_Int8" || resolvedType == u"byte" + || resolvedType == u"sal_uInt8" || resolvedType == u"sal_uInt16" + || resolvedType == u"sal_uInt32" || resolvedType == u"sal_Int64" + || resolvedType == u"sal_uInt64" || resolvedType == u"sal_Unicode" + || resolvedType == u"unsigned short" || resolvedType == u"unsigned long" + || resolvedType == u"unsigned hyper" || resolvedType == u"hyper") + return "0"; + else if (resolvedType == u"sal_Bool" || resolvedType == u"boolean") + return "false"; + else + { + // Check if it's an enum type + if (isUnoType(resolvedType) && isUnoEnum(resolvedType)) + { + // Return default enum value (first enum value, typically 0) + OString returnTypeStr = u2b(resolvedType); + size_t lastDot = returnTypeStr.lastIndexOf('.'); + std::string_view enumName = (lastDot != std::string_view::npos) + ? returnTypeStr.subView(lastDot + 1) + : returnTypeStr; + return "static_cast<"_ostr + enumName + "_>(0)"_ostr; + } + // Check if it's a struct type + else if (isUnoType(resolvedType) && isUnoStruct(resolvedType)) + { + // Return empty struct for struct types + return OString("{}"); // Empty struct literal + } + else + { + return "nullptr"; // For pointer/interface/any types + } + } +} + +OString CppProducer::convertUnoTypeToCpp(std::u16string_view unoType) const +{ + std::string result(unoType.begin(), unoType.end()); + + // Handle sequences: []type -> Sequence<type> + int sequenceDepth = 0; + while (result.starts_with("[]")) + { + result = result.substr(2); + sequenceDepth++; + } + + // Check if the inner type is an interface and wrap with Reference<> if needed for sequences + bool needsReferenceWrapper = false; + if (sequenceDepth > 0 && result.find(".") != std::string::npos) + { + // For UNO types with namespaces, check if it's an interface (before namespace conversion) + rtl::Reference<unoidl::Entity> entity; + OUString unoTypeName = OUString::createFromAscii(result); + codemaker::UnoType::Sort sort = m_typeManager->getSort(unoTypeName, &entity); + + if (sort == codemaker::UnoType::Sort::Interface) + { + needsReferenceWrapper = true; + } + } + + // Handle template arguments recursively (before namespace conversion) + result = convertTemplateArguments(result); + + // Convert dots to double colons for C++ namespace syntax + size_t pos = 0; + while ((pos = result.find('.', pos)) != std::string::npos) + { + result.replace(pos, 1, "::"); + pos += 2; + } + + // Handle primitive types (only for non-template contexts) + if (result.find('<') == std::string::npos) + { + if (result == "boolean") + result = "sal_Bool"; + else if (result == "byte") + result = "sal_Int8"; + else if (result == "short") + result = "sal_Int16"; + else if (result == "unsigned short") + result = "sal_uInt16"; + else if (result == "long") + result = "sal_Int32"; + else if (result == "unsigned long") + result = "sal_uInt32"; + else if (result == "hyper") + result = "sal_Int64"; + else if (result == "unsigned hyper") + result = "sal_uInt64"; + else if (result == "float") + result = "float"; + else if (result == "double") + result = "double"; + else if (result == "char") + result = "sal_Unicode"; + else if (result == "string") + result = "OUString"; + else if (result == "type") + result = "Type"; + else if (result == "any") + result = "Any"; + // else: keep as-is for UNO types like com::sun::star::... + } + + // Apply Reference<> wrapper if needed + if (needsReferenceWrapper) + { + result = "Reference<" + result + ">"; + } + + // Wrap with Sequence<> for each nesting level + for (int i = 0; i < sequenceDepth; i++) + { + result = "Sequence<" + result + ">"; + } + + return OString(result.c_str()); +} + +std::string CppProducer::convertTemplateArguments(const std::string& unoType) const +{ + std::string result = unoType; + + // Find template arguments within < > + size_t start = result.find('<'); + if (start == std::string::npos) + return result; // No template arguments + + size_t end = result.rfind('>'); + if (end == std::string::npos || end <= start) + return result; // Malformed template + + // Extract and process template arguments + std::string prefix = result.substr(0, start + 1); + std::string suffix = result.substr(end); + std::string args = result.substr(start + 1, end - start - 1); + + // Split arguments by comma (simple split for now) + std::string processedArgs = ""; + size_t argStart = 0; + size_t comma = 0; + + while (true) + { + comma = args.find(',', argStart); + std::string arg; + if (comma == std::string::npos) + { + arg = args.substr(argStart); + } + else + { + arg = args.substr(argStart, comma - argStart); + } + + // Trim whitespace + arg.erase(0, arg.find_first_not_of(" \t")); + arg.erase(arg.find_last_not_of(" \t") + 1); + + // Recursively process nested template arguments first + arg = convertTemplateArguments(arg); + + // Handle sequence types first: []type -> Sequence<type> + if (arg.starts_with("[]")) + { + std::string innerType = arg.substr(2); + arg = "Sequence<" + innerType + ">"; + } + + // Convert basic types + if (arg == "string") + arg = "OUString"; + else if (arg == "boolean") + arg = "sal_Bool"; + else if (arg == "byte") + arg = "sal_Int8"; + else if (arg == "short") + arg = "sal_Int16"; + else if (arg == "long") + arg = "sal_Int32"; + else if (arg == "hyper") + arg = "sal_Int64"; + else if (arg == "float") + arg = "float"; + else if (arg == "double") + arg = "double"; + else if (arg == "char") + arg = "sal_Unicode"; + else if (arg == "type") + arg = "Type"; + else if (arg == "any") + arg = "Any"; + // For complex types, convert dots to :: + else if (arg.find('.') != std::string::npos) + { + size_t pos = 0; + while ((pos = arg.find('.', pos)) != std::string::npos) + { + arg.replace(pos, 1, "::"); + pos += 2; + } + } + + if (!processedArgs.empty()) + processedArgs += ","; + processedArgs += arg; + + if (comma == std::string::npos) + break; + argStart = comma + 1; + } + + return prefix + processedArgs + suffix; +} + +void CppProducer::generateInterfaceHeader(std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity) +{ + if (m_dryRun || !m_combinedHeaderFile) + return; + + // Use combined file instead of individual file + CppFile& file = *m_combinedHeaderFile; + file.openFileAppend(); + + // Add separator comment for this interface + file.beginLine().append("").endLine(); + file.beginLine().append("// === ").append(name).append(" ===").endLine(); + + file.beginLine().append("extern \"C\"").endLine().beginBlock(); + + // Generate opaque handle typedef + OString functionPrefix = getCFunctionPrefix(name); + OString handleTypeName = functionPrefix + "Handle"; + + file.beginLine() + .append("// Opaque handle for type-safe C FFI") + .endLine() + .beginLine() + .append("typedef void* ") + .append(handleTypeName) + .append(";") + .endLine() + .beginLine() + .append("") + .endLine(); + + // Generate create/destroy functions + file.beginLine() + .append("// Interface lifecycle") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT ") + .append(handleTypeName) + .append(" ") + .append(functionPrefix) + .append("_constructor();") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT void ") + .append(functionPrefix) + .append("_destructor(") + .append(handleTypeName) + .append(" handle);") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT ") + .append(handleTypeName) + .append(" ") + .append(functionPrefix) + .append("_from_ptr(void* ptr);") + .endLine(); + + file.beginLine().append("").endLine(); + + // Generate method wrappers for all methods + file.beginLine().append("// Method wrappers").endLine(); + + // TODO: Add interface inheritance support in the future + // For now, only process direct methods from this interface + + // Process only this interface's direct methods (no inheritance) + for (const auto& method : entity->getDirectMethods()) + { + // Generate method signature + file.beginLine(); + + // Return type - use void* for all non-void returns (keep it simple) + OString returnType = getMethodReturnType(method.returnType); + if (returnType == "void") + { + file.append("SAL_DLLPUBLIC_EXPORT void "); + } + else + { + file.append("SAL_DLLPUBLIC_EXPORT void* "); + } + + // Function name: com__sun__star__lang__XMain__methodName + file.append(functionPrefix) + .append("_") + .append(method.name) + .append("(") + .append(handleTypeName) + .append(" handle"); + + // Add parameters + for (const auto& param : method.parameters) + { + file.append(", void* ").append(param.name); + } + + file.append(");"); + file.endLine(); + } + + file.endBlock().beginLine().append("// extern \"C\"").endLine(); + + file.closeFile(); +} + +void CppProducer::generateInterfaceSourceIncludes(CppFile& file, std::string_view name) +{ + generateCommonIncludes(file, name, true, true); +} + +void CppProducer::generateInterfaceSourceNamespaces(CppFile& file, std::string_view name) +{ + generateSourceNamespaces(file, name); +} + +void CppProducer::generateInterfaceSourceBasicFunctions(CppFile& file, + const OString& functionPrefix, + const OString& className, + const OString& handleTypeName) +{ + // Interface creation + file.beginLine() + .append("// Interface creation - creates empty Reference (to be populated by Rust)") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT ") + .append(handleTypeName) + .append(" ") + .append(functionPrefix) + .append("_constructor()") + .endLine() + .beginBlock() + .beginLine() + .append("//Debug: Interface constructor called for ") + .append(className) + .endLine() + .beginLine() + .append("SAL_WARN(\"rustmaker\", \"Debug: Creating interface ") + .append(className) + .append("\");") + .endLine() + .beginLine() + .append("// Create empty Reference - actual interface will be set from Rust side") + .endLine() + .beginLine() + .append("auto res = new Reference<") + .append(className) + .append(">();") + .endLine() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("if (!res)") + .endLine() + .beginBlock() + .beginLine() + .append("SAL_WARN(\"rust_uno_ffi\", \"Failed to allocate Reference<") + .append(className) + .append("> wrapper\");") + .endLine() + .beginLine() + .append("return nullptr;") + .endLine() + .endBlock() + .beginLine() + .append("") + .endLine() + .beginLine() + .append( + "// Note: Empty Reference is expected here - is() will be false until set from Rust") + .endLine() + .beginLine() + .append("SAL_INFO(\"rust_uno_ffi\", \"✅ Successfully created empty Reference<") + .append(className) + .append("> wrapper\");") + .endLine() + .beginLine() + .append("return res;") + .endLine() + .endBlock(); + + file.beginLine().append("").endLine(); + + // Interface destruction + file.beginLine() + .append("// Interface destruction") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT void ") + .append(functionPrefix) + .append("_destructor(") + .append(handleTypeName) + .append(" handle)") + .endLine() + .beginBlock() + .beginLine() + .append("delete static_cast<Reference<") + .append(className) + .append(">*>(handle);") + .endLine() + .endBlock(); + file.beginLine().append("").endLine(); + + // Interface casting from void* to typed Reference using UNO_QUERY + file.beginLine() + .append("// Interface casting - casts void* to typed Reference using UNO_QUERY") + .endLine() + .beginLine() + .append("SAL_DLLPUBLIC_EXPORT ") + .append(handleTypeName) + .append(" ") + .append(functionPrefix) + .append("_from_ptr(void* ptr)") + .endLine() + .beginBlock() + .beginLine() + .append("if (ptr == nullptr) return nullptr;") + .endLine() + .beginLine() + .append("try") + .endLine() + .beginBlock() + .beginLine() + .append("// Convert XInterface* to ") + .append(className) + .append(" via UNO_QUERY") + .endLine() + .beginLine() + .append("Reference<XInterface>* interfacePtr = static_cast<Reference<XInterface>*>(ptr);") + .endLine() + .beginLine() + .append("Reference<") + .append(className) + .append("> queryResult(interfacePtr->get(), UNO_QUERY);") + .endLine() + .beginLine() + .append("if (!queryResult.is()) return nullptr;") + .endLine() + .beginLine() + .append("Reference<") + .append(className) + .append(">* refPtr = new Reference<") + .append(className) + .append(">(queryResult);") + .endLine() + .beginLine() + .append("if (refPtr && refPtr->is())") + .endLine() + .beginBlock() + .beginLine() + .append("SAL_INFO(\"rust_uno_ffi\", \"Successfully created Reference<") + .append(className) + .append("> in from_ptr\");") + .endLine() + .beginLine() + .append("return refPtr;") + .endBlock() + .beginLine() + .append("SAL_WARN(\"rust_uno_ffi\", \"Reference is invalid in from_ptr for ") + .append(className) + .append("\");") + .endLine() + .beginLine() + .append("delete refPtr;") + .endLine() + .beginLine() + .append("return nullptr;") + .endLine() + .endBlock() + .beginLine() + .append("catch (const Exception& e)") + .endLine() + .beginBlock() + .beginLine() + .append("SAL_WARN(\"rustmaker\", \"UNO exception in ") + .append(functionPrefix) + .append("_from_ptr: \" << e.Message);") + .endLine() + .beginLine() + .append("return nullptr;") + .endLine() + .beginLine() + .append("}") + .endLine() + .endBlock(); + + file.beginLine().append("").endLine(); +} + +void CppProducer::generateInterfaceSourceMethodImplementations( + CppFile& file, const OString& functionPrefix, const OString& className, + const OString& handleTypeName, const rtl::Reference<unoidl::InterfaceTypeEntity>& entity) +{ + // Generate method implementations + file.beginLine().append("// Method implementations").endLine(); + + // TODO: Add interface inheritance support in the future + // For now, only process direct methods from this interface (no inheritance) + + // Process only this interface's direct methods + for (const auto& method : entity->getDirectMethods()) + { + generateSingleInterfaceMethod(file, functionPrefix, className, handleTypeName, method); + } +} + +void CppProducer::generateSingleInterfaceMethod(CppFile& file, const OString& functionPrefix, + const OString& className, + const OString& handleTypeName, + const unoidl::InterfaceTypeEntity::Method& method) +{ + file.beginLine(); + + // Use void* for all non-void returns (keep it simple) + OString returnType = getMethodReturnType(method.returnType); + if (returnType == "void") + { + file.append("SAL_DLLPUBLIC_EXPORT void "); + } + else + { + file.append("SAL_DLLPUBLIC_EXPORT void* "); + } + + file.append(functionPrefix) + .append("_") + .append(method.name) + .append("(") + .append(handleTypeName) + .append(" handle"); + + // Add parameters + for (const auto& param : method.parameters) + { + file.append(", void* ").append(param.name); + } + + file.append(")").endLine().beginBlock(); + + // Implementation body + file.beginLine() + .append("//Debug: Method ") + .append(method.name) + .append(" called") + .endLine() + .beginLine() + .append("SAL_WARN(\"rustmaker\", \"Debug: Method ") + .append(className) + .append("::") + .append(method.name) + .append(" called\");") + .endLine() + .beginLine() + .append("Reference<") + .append(className) + .append(">* ref = static_cast<Reference<") + .append(className) + .append(">*>(handle);") + .endLine() + .beginLine() + .append("//Debug: Checking reference validity") + .endLine() + .beginLine() + .append("if (!ref) { SAL_WARN(\"rustmaker\", \"Debug: ref is null\"); ") + .append(method.returnType == u"void" ? "return;" : "return nullptr;") + .append(" }") + .endLine() + .beginLine() + .append("if (!ref->is()) { SAL_WARN(\"rustmaker\", \"Debug: ref->is() is false\"); ") + .append(method.returnType == u"void" ? "return;" : "return nullptr;") + .append(" }") + .endLine(); + + // Generate try-catch block for UNO exceptions + file.beginLine().append("try").endLine().beginBlock(); + + // Generate actual UNO method call + generateActualMethodCall(file, method); + + // Note: Return value conversion is now handled inside generateActualMethodCall() + + // Exception handling + file.endBlock() + .beginLine() + .append("catch (const Exception& ex)") + .endLine() + .beginBlock() + .beginLine() + .append("SAL_WARN(\"rustmaker\", \"UNO exception in ") + .append(className) + .append("_") + .append(method.name) + .append(": \" << ex.Message);") + .endLine(); + + // Return appropriate error value based on return type + if (method.returnType == u"void") + { + file.beginLine().append("return; // void method - no return on exception").endLine(); + } + else + { + file.beginLine().append("return nullptr; // error return for non-void method").endLine(); + } + + file.endBlock().endBlock(); + + file.beginLine().append("").endLine(); +} + +void CppProducer::generateInterfaceSource(std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity) +{ + if (m_dryRun || !m_combinedSourceFile) + return; + + // Use combined file instead of individual file + CppFile& file = *m_combinedSourceFile; + file.openFileAppend(); + + // Add separator comment for this interface + file.beginLine().append("").endLine(); + file.beginLine().append("// === ").append(name).append(" ===").endLine(); + + // Generate includes + generateInterfaceSourceIncludes(file, name); + + // Add namespaces + generateInterfaceSourceNamespaces(file, name); + + file.beginLine().append("extern \"C\"").endLine().beginBlock(); + + // Generate implementation + OString functionPrefix = getCFunctionPrefix(name); + OString handleTypeName = functionPrefix + "Handle"; + // Use fully qualified C++ type name to avoid namespace ambiguity + OString className = convertUnoTypeToCpp(OUString::createFromAscii(std::string(name))); + + // Generate basic functions (creation/destruction) + generateInterfaceSourceBasicFunctions(file, functionPrefix, className, handleTypeName); + + // Generate method implementations + generateInterfaceSourceMethodImplementations(file, functionPrefix, className, handleTypeName, + entity); + + file.endBlock().append(" // extern \"C\""); + file.beginLine().append("").endLine(); + + file.closeFile(); +} + +void CppProducer::generateActualMethodCall(CppFile& file, + const unoidl::InterfaceTypeEntity::Method& method) +{ + // Generate actual UNO method call based on method signature + file.beginLine() + .append("//Debug: About to call UNO method ") + .append(method.name) + .endLine() + .beginLine() + .append("SAL_WARN(\"rustmaker\", \"Debug: Calling UNO method ") + .append(method.name) + .append(" with ") + .append(OString::number(static_cast<sal_Int32>(method.parameters.size()))) + .append(" parameters\");") + .endLine(); + + if (method.returnType != u"void") + { + // Method with return value + file.beginLine() + .append("// Call actual UNO method with return value") + .endLine() + .beginLine(); + + file.append("auto result = (*ref)->").append(method.name).append("("); + } + else + { + // Void method + file.beginLine().append("// Call actual UNO method (void return)").endLine().beginLine(); + + file.append("(*ref)->").append(method.name).append("("); + } + + // Add parameters with proper type conversion + bool first = true; + for (const auto& param : method.parameters) + { + if (!first) + file.append(", "); + first = false; + + // Convert void* parameters to proper UNO types based on parameter type + std::u16string_view paramType = param.type; + + // Determine if parameter is input-only (needs const) or output/inout (needs non-const reference) + bool isInputOnly + = (param.direction == unoidl::InterfaceTypeEntity::Method::Parameter::DIRECTION_IN); + + if (paramType == u"string") + { + if (isInputOnly) + { + // Input parameter: convert rtl_uString* to OUString object + file.append("OUString(static_cast<rtl_uString*>(").append(param.name).append("))"); + } + else + { + // Output parameter: needs OUString reference for binding + file.append("*reinterpret_cast<OUString*>(").append(param.name).append(")"); + } + } + else if (paramType == u"any") + { + if (isInputOnly) + file.append("*reinterpret_cast<const Any*>(").append(param.name).append(")"); + else + file.append("*reinterpret_cast<Any*>(").append(param.name).append(")"); + } + else if (paramType == u"boolean") + { + if (isInputOnly) + file.append("*reinterpret_cast<const sal_Bool*>(").append(param.name).append(")"); + else + file.append("*reinterpret_cast<sal_Bool*>(").append(param.name).append(")"); + } + else if (paramType == u"byte") + { + if (isInputOnly) + file.append("*reinterpret_cast<const sal_Int8*>(").append(param.name).append(")"); + else + file.append("*reinterpret_cast<sal_Int8*>(").append(param.name).append(")"); + } + else if (paramType == u"short") + { + if (isInputOnly) + file.append("*reinterpret_cast<const sal_Int16*>(").append(param.name).append(")"); + else + file.append("*reinterpret_cast<sal_Int16*>(").append(param.name).append(")"); + } + else if (paramType == u"long") + { + if (isInputOnly) + file.append("*reinterpret_cast<const sal_Int32*>(").append(param.name).append(")"); + else + file.append("*reinterpret_cast<sal_Int32*>(").append(param.name).append(")"); + } + else if (paramType == u"hyper") + { + if (isInputOnly) + file.append("*reinterpret_cast<const sal_Int64*>(").append(param.name).append(")"); + else + file.append("*reinterpret_cast<sal_Int64*>(").append(param.name).append(")"); + } + else if (paramType == u"float") + { + if (isInputOnly) + file.append("*reinterpret_cast<const float*>(").append(param.name).append(")"); + else + file.append("*reinterpret_cast<float*>(").append(param.name).append(")"); + } + else if (paramType == u"double") + { + if (isInputOnly) + file.append("*reinterpret_cast<const double*>(").append(param.name).append(")"); + else + file.append("*reinterpret_cast<double*>(").append(param.name).append(")"); + } + else if (paramType == u"type") + { + if (isInputOnly) + file.append("*reinterpret_cast<const Type*>(").append(param.name).append(")"); + else + file.append("*reinterpret_cast<Type*>(").append(param.name).append(")"); + } + else if (paramType.starts_with(u"[]")) + { + // Sequence type - convert with null pointer safety + OString cppTypeName = convertUnoTypeToCpp(paramType); + + if (isInputOnly) + { + // Input parameter - can use temporary empty sequence + file.append("(") + .append(param.name) + .append(" ? *reinterpret_cast<const ") + .append(cppTypeName) + .append("*>(") + .append(param.name) + .append(") : ") + .append(cppTypeName) + .append("())"); + } + else + { + // Output/InOut parameter - needs reference, must dereference non-null pointer + file.append("*reinterpret_cast<") + .append(cppTypeName) + .append("*>(") + .append(param.name) + .append(")"); + } + } + else + { + // For interfaces and other complex types + // Check if it's an interface type that needs Reference<> wrapper + rtl::Reference<unoidl::Entity> entity; + codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(paramType), &entity); + + if (sort == codemaker::UnoType::Sort::Interface) + { + // Interface parameters: void* from Rust points to Reference<ActualType>* + // We need to dereference and cast via UNO_QUERY to the expected interface type + OString cppTypeName = convertUnoTypeToCpp(paramType); + if (isInputOnly) + { + // For input interface parameters, we assume the Rust code passes Reference<SomeInterface>* + // We dereference it and cast via UNO_QUERY to the expected interface type + file.append("Reference<") + .append(cppTypeName) + .append(">(static_cast<Reference<XInterface>*>(") + .append(param.name) + .append(")->get(), UNO_QUERY)"); + } + else + file.append("*reinterpret_cast<Reference<") + .append(cppTypeName) + .append(">*>(") + .append(param.name) + .append(")"); + } + else + { + // For structs and other complex types, assume proper type cast + OString cppTypeName = convertUnoTypeToCpp(paramType); + if (isInputOnly) + { + file.append("(") + .append(param.name) + .append(" == nullptr ? ") + .append(cppTypeName) + .append("() : *reinterpret_cast<const ") + .append(cppTypeName) + .append("*>(") + .append(param.name) + .append("))"); + } + else + { + file.append("*reinterpret_cast<") + .append(cppTypeName) + .append("*>(") + .append(param.name) + .append(")"); + } + } + } + } + + file.append(");").endLine(); + + // Handle return value conversion for non-void methods + if (method.returnType != u"void") + { + file.beginLine().append("// Convert result to opaque void* return").endLine(); + + std::u16string_view returnType = method.returnType; + + if (returnType == u"string") + { + file.beginLine().append("return new OUString(result);").endLine(); + } + else if (returnType == u"any") + { + file.beginLine().append("return new Any(result);").endLine(); + } + else if (returnType == u"boolean") + { + file.beginLine().append("return new sal_Bool(result);").endLine(); + } + else if (returnType == u"byte") + { + file.beginLine().append("return new sal_Int8(result);").endLine(); + } + else if (returnType == u"short") + { + file.beginLine().append("return new sal_Int16(result);").endLine(); + } + else if (returnType == u"long") + { + file.beginLine().append("return new sal_Int32(result);").endLine(); + } + else if (returnType == u"hyper") + { + file.beginLine().append("return new sal_Int64(result);").endLine(); + } + else if (returnType == u"float") + { + file.beginLine().append("return new float(result);").endLine(); + } + else if (returnType == u"double") + { + file.beginLine().append("return new double(result);").endLine(); + } + else if (returnType == u"type") + { + file.beginLine().append("return new Type(result);").endLine(); + } + else if (returnType.starts_with(u"[]")) + { + // Sequence return type + file.beginLine() + .append("return new ") + .append(convertUnoTypeToCpp(returnType)) + .append("(result);") + .endLine(); + } + else + { + // For interfaces and other complex types, create appropriate wrapper + // Check if it's an interface type + rtl::Reference<unoidl::Entity> entity; + codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(returnType), &entity); + + if (sort == codemaker::UnoType::Sort::Interface) + { + // Add debug output for interface return values + file.beginLine() + .append("SAL_WARN(\"rustmaker\", \"Debug: UNO method ") + .append(method.name) + .append( + " completed, result.is() = \" << (result.is() ? \"true\" : \"false\"));") + .endLine() + .beginLine() + .append("SAL_INFO(\"rust_uno_ffi\", \"Creating new Reference<") + .append(convertUnoTypeToCpp(returnType)) + .append("> wrapper for method ") + .append(method.name) + .append("\");") + .endLine() + .beginLine() + .append("auto res = new Reference<") + .append(convertUnoTypeToCpp(returnType)) + .append(">(result);") + .endLine() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("if (!res)") + .endLine() + .beginBlock() + .beginLine() + .append("SAL_WARN(\"rust_uno_ffi\", \"Failed to create Reference wrapper for ") + .append(convertUnoTypeToCpp(returnType)) + .append(" in method ") + .append(method.name) + .append("\");") + .endLine() + .beginLine() + .append("return nullptr;") + .endLine() + .endBlock() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("if (!res->is())") + .endLine() + .beginBlock() + .beginLine() + .append("SAL_WARN(\"rust_uno_ffi\", \"Reference wrapper is invalid (is() = " + "false) for ") + .append(convertUnoTypeToCpp(returnType)) + .append(" in method ") + .append(method.name) + .append("\");") + .endLine() + .beginLine() + .append("delete res;") + .endLine() + .beginLine() + .append("return nullptr;") + .endLine() + .endBlock() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("SAL_INFO(\"rust_uno_ffi\", \"✅ Successfully created valid Reference " + "wrapper for ") + .append(convertUnoTypeToCpp(returnType)) + .append(" in method ") + .append(method.name) + .append("\");") + .endLine() + .beginLine() + .append("return res;") + .endLine(); + } + else + { + // For structs and other types + file.beginLine() + .append("return new ") + .append(convertUnoTypeToCpp(returnType)) + .append("(result);") + .endLine(); + } + } + } +} + +void CppProducer::generateReturnValueConversion(CppFile& file, + const unoidl::InterfaceTypeEntity::Method& method) +{ + file.beginLine().append("// Convert result to opaque void* return").endLine(); + + std::u16string_view returnType = method.returnType; + + if (returnType == u"string") + { + file.beginLine().append("return new OUString(result);").endLine(); + } + else if (returnType == u"any") + { + file.beginLine().append("return new Any(result);").endLine(); + } + else if (returnType == u"boolean") + { + file.beginLine().append("return new sal_Bool(result);").endLine(); + } + else if (returnType == u"byte") + { + file.beginLine().append("return new sal_Int8(result);").endLine(); + } + else if (returnType == u"short") + { + file.beginLine().append("return new sal_Int16(result);").endLine(); + } + else if (returnType == u"long") + { + file.beginLine().append("return new sal_Int32(result);").endLine(); + } + else if (returnType == u"hyper") + { + file.beginLine().append("return new sal_Int64(result);").endLine(); + } + else if (returnType == u"float") + { + file.beginLine().append("return new float(result);").endLine(); + } + else if (returnType == u"double") + { + file.beginLine().append("return new double(result);").endLine(); + } + else if (returnType == u"type") + { + file.beginLine().append("return new Type(result);").endLine(); + } + else if (returnType.starts_with(u"[]")) + { + // Sequence return type - return void* to new sequence + file.beginLine() + .append("return new ") + .append(convertUnoTypeToCpp(returnType)) + .append("(result);") + .endLine(); + } + else + { + // For interfaces - return Reference<> to avoid instantiating abstract types + rtl::Reference<unoidl::Entity> entity; + codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(returnType), &entity); + + if (sort == codemaker::UnoType::Sort::Interface) + { + // Interface return type - return new Reference<Interface>(result) + file.beginLine() + .append("SAL_INFO(\"rust_uno_ffi\", \"Creating Reference wrapper for return type ") + .append(convertUnoTypeToCpp(returnType)) + .append("\");") + .endLine() + .beginLine() + .append("auto res = new Reference<") + .append(convertUnoTypeToCpp(returnType)) + .append(">(result);") + .endLine() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("if (!res)") + .endLine() + .beginBlock() + .beginLine() + .append("SAL_WARN(\"rust_uno_ffi\", \"Failed to create Reference wrapper for ") + .append(convertUnoTypeToCpp(returnType)) + .append("\");") + .endLine() + .beginLine() + .append("return nullptr;") + .endLine() + .endBlock() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("if (!res->is())") + .endLine() + .beginBlock() + .beginLine() + .append( + "SAL_WARN(\"rust_uno_ffi\", \"Reference wrapper is invalid (is() = false) for ") + .append(convertUnoTypeToCpp(returnType)) + .append("\");") + .endLine() + .beginLine() + .append("delete res;") + .endLine() + .beginLine() + .append("return nullptr;") + .endLine() + .endBlock() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("SAL_INFO(\"rust_uno_ffi\", \"✅ Successfully created valid Reference " + "wrapper for ") + .append(convertUnoTypeToCpp(returnType)) + .append("\");") + .endLine() + .beginLine() + .append("return res;") + .endLine(); + } + else + { + // For structs, enums and other types - return void* to new object + file.beginLine() + .append("return new ") + .append(convertUnoTypeToCpp(returnType)) + .append("(result);") + .endLine(); + } + } +} + +// Type mapping functions (from old FFI producer) + +OUString CppProducer::resolveTypedef(std::u16string_view unoType) const +{ + // Recursively resolve typedefs to their underlying types + rtl::Reference<unoidl::Entity> entity; + rtl::Reference<unoidl::MapCursor> cursor; + codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(unoType), &entity, &cursor); + + if (sort == codemaker::UnoType::Sort::Typedef) + { + rtl::Reference<unoidl::TypedefEntity> typedefEntity( + static_cast<unoidl::TypedefEntity*>(entity.get())); + if (typedefEntity.is()) + { + // Recursively resolve in case of nested typedefs + OUString resolvedType = typedefEntity->getType(); + return resolveTypedef(resolvedType); + } + } + + // If not a typedef or resolution failed, return the original type + return OUString(unoType); +} + +OString CppProducer::mapUnoPrimitiveToSal(std::u16string_view unoType) const +{ + // Common primitive type mappings from UNO to SAL types + if (unoType == u"boolean") + return "sal_Bool"_ostr; + else if (unoType == u"byte") + return "sal_Int8"_ostr; + else if (unoType == u"short") + return "sal_Int16"_ostr; + else if (unoType == u"unsigned short") + return "sal_uInt16"_ostr; + else if (unoType == u"long") + return "sal_Int32"_ostr; + else if (unoType == u"unsigned long") + return "sal_uInt32"_ostr; + else if (unoType == u"hyper") + return "sal_Int64"_ostr; + else if (unoType == u"unsigned hyper") + return "sal_uInt64"_ostr; + else if (unoType == u"float") + return "float"_ostr; + else if (unoType == u"double") + return "double"_ostr; + else + return OString(); // Not a primitive type +} + +OString CppProducer::getCppTypeName(std::u16string_view unoType) const +{ + // First, resolve any typedefs to their underlying types + OUString resolvedType = resolveTypedef(unoType); + + // Map UNO types to C++ types for extern "C" functions + if (resolvedType == u"void") + return "void"_ostr; + else if (resolvedType == u"string") + return "rtl_uString*"_ostr; + else if (resolvedType == u"any" || resolvedType == u"com.sun.star.uno.Any") + return "uno_Any*"_ostr; + + // Try primitive type mapping + OString primitiveType = mapUnoPrimitiveToSal(resolvedType); + if (!primitiveType.isEmpty()) + return primitiveType; + + // Handle complex types + if (resolvedType.startsWith(u"[]")) + return "uno_Sequence*"_ostr; + else if (isUnoStruct(resolvedType)) + { + // For structs, return the FFI struct name with underscore (value type, not pointer) + OString type = u2b(resolvedType); + size_t lastDot = type.lastIndexOf('.'); + if (lastDot != std::string_view::npos) + return OString::Concat(type.subView(lastDot + 1)) + "_"; + else + return type + "_"; + } + else if (isUnoEnum(resolvedType)) + { + // For enums, return the enum name with underscore to match generated FFI enums + OString type = u2b(resolvedType); + size_t lastDot = type.lastIndexOf('.'); + if (lastDot != std::string_view::npos) + return OString::Concat(type.subView(lastDot + 1)) + "_"; + else + return type + "_"; + } + else if (isUnoConstantGroup(resolvedType)) + { + // For constant groups, return the FFI name with underscore + OString type = u2b(resolvedType); + size_t lastDot = type.lastIndexOf('.'); + if (lastDot != std::string_view::npos) + return OString::Concat(type.subView(lastDot + 1)) + "_"; + else + return type + "_"; + } + else if (isUnoType(resolvedType)) + { + // For interfaces and other types, return XInterface* pointer + return "XInterface*"_ostr; + } + else + return "void*"_ostr; // Default to void* for unknown types +} + +OString CppProducer::getRustFFITypeName(std::u16string_view unoType) const +{ + // Map UNO types to Rust FFI types + if (unoType == u"string") + return "*mut rtl_uString"_ostr; + else if (unoType == u"boolean") + return "u8"_ostr; // sal_Bool is typedef'd to u8 (unsigned char) + else if (unoType == u"byte") + return "i8"_ostr; + else if (unoType == u"short") + return "i16"_ostr; + else if (unoType == u"unsigned short") + return "u16"_ostr; + else if (unoType == u"long") + return "i32"_ostr; + else if (unoType == u"unsigned long") + return "u32"_ostr; + else if (unoType == u"hyper") + return "i64"_ostr; + else if (unoType == u"unsigned hyper") + return "u64"_ostr; + else if (unoType == u"float") + return "f32"_ostr; + else if (unoType == u"double") + return "f64"_ostr; + else if (unoType == u"void") + return "()"_ostr; + else if (unoType == u"any" || unoType == u"com.sun.star.uno.Any") + return "*mut uno_Any"_ostr; // UNO Any type + else if (isUnoStruct(unoType)) + { + // For structs, return just the struct name with underscore since we import it + OString type = u2b(unoType); + size_t lastDot = type.lastIndexOf('.'); + if (lastDot != std::string_view::npos) + return OString::Concat(type.subView(lastDot + 1)) + "_"; + else + return type + "_"; + } + else if (isUnoEnum(unoType)) + { + // For enums, return just the enum name since we import it + OString type = u2b(unoType); + size_t lastDot = type.lastIndexOf('.'); + if (lastDot != std::string_view::npos) + return OString(type.subView(lastDot + 1)); + else + return type; + } + else if (isUnoConstantGroup(unoType)) + { + // For constant groups, return the constant group name with underscore to match C++ side + OString type = u2b(unoType); + size_t lastDot = type.lastIndexOf('.'); + if (lastDot != std::string_view::npos) + return OString::Concat(type.subView(lastDot + 1)) + "_"; + else + return type + "_"; + } + else if (isUnoType(unoType)) + { + // For interfaces, return *mut XInterface pointer + return "*mut XInterface"_ostr; + } + else + return "*mut std::ffi::c_void"_ostr; // Default for unknown types +} + +OString CppProducer::convertBasicType(const OString& typeName) const +{ + OString result = typeName; + + // Convert UNO primitive types to C++ types + if (result == "long") + result = "sal_Int32"; + else if (result == "short") + result = "sal_Int16"; + else if (result == "byte") + result = "sal_Int8"; + else if (result == "boolean") + result = "sal_Bool"; + else if (result == "double") + result = "double"; + else if (result == "float") + result = "float"; + else if (result == "string") + result = "OUString"; + else if (result == "any") + result = "Any"; + else if (result.indexOf('.') != -1) + { + // For UNO types with namespace, convert dots to double colons + result = result.replaceAll(".", "::"); + } + + return result; +} + +// Type classification helper methods + +bool CppProducer::isUnoType(std::u16string_view typeName) const +{ + // Check if this is a valid UNO type using TypeManager + rtl::Reference<unoidl::Entity> entity; + rtl::Reference<unoidl::MapCursor> cursor; + + codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(typeName), &entity, &cursor); + + // Return true for any valid UNO type (not just interfaces/structs/enums) + return sort != codemaker::UnoType::Sort::Void && sort != codemaker::UnoType::Sort::Boolean + && sort != codemaker::UnoType::Sort::Byte && sort != codemaker::UnoType::Sort::Short + && sort != codemaker::UnoType::Sort::UnsignedShort + && sort != codemaker::UnoType::Sort::Long + && sort != codemaker::UnoType::Sort::UnsignedLong + && sort != codemaker::UnoType::Sort::Hyper + && sort != codemaker::UnoType::Sort::UnsignedHyper + && sort != codemaker::UnoType::Sort::Float && sort != codemaker::UnoType::Sort::Double + && sort != codemaker::UnoType::Sort::Char && sort != codemaker::UnoType::Sort::String + && sort != codemaker::UnoType::Sort::Type && sort != codemaker::UnoType::Sort::Any; +} + +bool CppProducer::isUnoStruct(std::u16string_view typeName) const +{ + // Check the type sort using TypeManager + rtl::Reference<unoidl::Entity> entity; + rtl::Reference<unoidl::MapCursor> cursor; + + codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(typeName), &entity, &cursor); + + // Return true if it's a plain struct or polymorphic struct template + return (sort == codemaker::UnoType::Sort::PlainStruct); +} + +bool CppProducer::isUnoEnum(std::u16string_view typeName) const +{ + rtl::Reference<unoidl::Entity> entity; + rtl::Reference<unoidl::MapCursor> cursor; + codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(typeName), &entity, &cursor); + return sort == codemaker::UnoType::Sort::Enum; +} + +bool CppProducer::isUnoPolymorphicStruct(std::u16string_view typeName) const +{ + rtl::Reference<unoidl::Entity> entity; + rtl::Reference<unoidl::MapCursor> cursor; + codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(typeName), &entity, &cursor); + return sort == codemaker::UnoType::Sort::PolymorphicStructTemplate; +} + +bool CppProducer::isUnoConstantGroup(std::u16string_view typeName) const +{ + rtl::Reference<unoidl::Entity> entity; + rtl::Reference<unoidl::MapCursor> cursor; + codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(typeName), &entity, &cursor); + return sort == codemaker::UnoType::Sort::ConstantGroup; +} + +bool CppProducer::isUnoInterface(std::u16string_view typeName) const +{ + rtl::Reference<unoidl::Entity> entity; + rtl::Reference<unoidl::MapCursor> cursor; + codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(typeName), &entity, &cursor); + return sort == codemaker::UnoType::Sort::Interface; +} + +OString CppProducer::convertUnoTypeToHandle(std::u16string_view unoType) const +{ + // Convert UNO type name to typedef handle name + // com.sun.star.lang.XMain -> com__sun__star__lang__XMainHandle + OString functionPrefix = getCFunctionPrefix(std::string(unoType.begin(), unoType.end())); + return functionPrefix + "Handle"; +} + +void CppProducer::generateServiceHeader( + std::string_view name, const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& entity) +{ + if (m_dryRun || !m_combinedHeaderFile) + return; + + CppFile& file = *m_combinedHeaderFile; + file.openFileAppend(); + + // Add pragma once for header guard + file.beginLine().append("#pragma once").endLine().endLine(); + + // Include the service's interface + OUString interfaceType = entity->getBase(); + std::string includePath(u2b(interfaceType)); + std::replace(includePath.begin(), includePath.end(), '.', '/'); + file.beginLine().append("#include <").append(includePath).append(".hpp>").endLine(); + file.beginLine().append("#include <com/sun/star/uno/XComponentContext.hpp>").endLine(); + file.beginLine().append("#include <com/sun/star/uno/Reference.hxx>").endLine(); + file.endLine(); + + // Generate extern "C" declarations for service creation + OString functionPrefix = getCFunctionPrefix(name); // Use consistent double-underscore naming + file.beginLine().append("extern \"C\"").endLine().beginBlock(); + file.beginLine().append(" // Service creation functions for ").append(name).endLine(); + file.beginLine() + .append(" SAL_DLLPUBLIC_EXPORT void* ") + .append(functionPrefix) + .append("_create(void* context_handle);") + .endLine(); + file.beginLine().append("}").endLine(); + + file.closeFile(); +} + +void CppProducer::generateServiceSource( + std::string_view name, const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& entity) +{ + if (m_dryRun || !m_combinedSourceFile) + return; + + CppFile& file = *m_combinedSourceFile; + file.openFileAppend(); + + // Include the header + std::string headerPath(name); + std::replace(headerPath.begin(), headerPath.end(), '.', '/'); + file.beginLine().append("#include <com/sun/star/lang/XMultiComponentFactory.hpp>").endLine(); + file.beginLine().append("#include <sal/log.hxx>").endLine(); + file.endLine(); + + // Services don't need namespace declarations - use fully qualified names instead + + // Generate service creation function + OString functionPrefix = getCFunctionPrefix(name); // Use consistent double-underscore naming + OUString interfaceType = entity->getBase(); + std::string interfaceCppType(u2b(interfaceType)); + std::replace(interfaceCppType.begin(), interfaceCppType.end(), '.', ':'); + // Fix single colons to double colons for C++ namespace syntax + size_t colonPos = 0; + while ((colonPos = interfaceCppType.find(":", colonPos)) != std::string::npos) + { + if (colonPos == 0 || interfaceCppType[colonPos - 1] != ':') + { + interfaceCppType.insert(colonPos, ":"); + colonPos += 2; + } + else + { + colonPos++; + } + } + if (!interfaceCppType.empty() && interfaceCppType.substr(0, 2) != "::") + { + interfaceCppType = "::" + interfaceCppType; + } + + file.beginLine().append("extern \"C\"").endLine().beginBlock(); + file.beginLine() + .append("SAL_DLLPUBLIC_EXPORT void* ") + .append(functionPrefix) + .append("_create(void* context_handle)") + .endLine() + .beginBlock() + .beginLine() + .append("try") + .endLine() + .beginBlock() + .beginLine() + .append("Reference<com::sun::star::uno::XComponentContext>* ctx = " + "static_cast<Reference<com::sun::star::uno::XComponentContext>*>(context_handle);") + .endLine() + .beginLine() + .append("if (!ctx || !ctx->is()) return nullptr;") + .endLine() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("Reference<com::sun::star::lang::XMultiComponentFactory> factory = " + "(*ctx)->getServiceManager();") + .endLine() + .beginLine() + .append("if (!factory.is()) return nullptr;") + .endLine() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("Reference<") + .append("com::sun::star::uno::XInterface") + .append("> service = Reference<") + .append("com::sun::star::uno::XInterface") + .append(">(") + .endLine() + .extraIndent() + .beginLine() + .append("factory->createInstanceWithContext(\"") + .append(name) + .append("\", *ctx), UNO_QUERY);") + .endLine() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("if (!service.is()) return nullptr;") + .endLine() + .beginLine() + .append("return new Reference<") + .append("com::sun::star::uno::XInterface") + .append(">(service);") + .endLine() + .endBlock() + .beginLine() + .append("catch (const Exception& ex)") + .endLine() + .beginBlock() + .beginLine() + .append("SAL_WARN(\"rustmaker\", \"Service creation failed for ") + .append(name) + .append(": \" << ex.Message);") + .endLine() + .beginLine() + .append("return nullptr;") + .endLine() + .endBlock() + .endBlock(); + file.beginLine().append("}").endLine(); + + file.closeFile(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/rustmaker/cpproduce.hxx b/codemaker/source/rustmaker/cpproduce.hxx new file mode 100644 index 000000000000..8c9aa50447da --- /dev/null +++ b/codemaker/source/rustmaker/cpproduce.hxx @@ -0,0 +1,225 @@ +/* -*- 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 <memory> +#include <string_view> +#include <unoidl/unoidl.hxx> +#include <codemaker/typemanager.hxx> +#include <rtl/string.hxx> + +// Forward declarations +class CppFile; + +/** + * CppProducer generates C++-side opaque pointer implementations. + * + * This is the C-side generator in the opaque pointer approach. + * It creates C++ wrapper classes and extern "C" bridge functions + * that the Rust-side can call through opaque pointers. + * + * Architecture: + * - C++ wrapper classes that hide UNO complexity + * - extern "C" bridge functions with unique names + * - Simple void* pointer casting for opaque compatibility + * - All UNO complexity isolated in C++ side + */ +class CppProducer +{ +public: + /** + * Constructor + * @param outputDir Directory for generated C++ files + * @param verbose Enable verbose output + * @param dryRun Don't actually write files + * @param typeManager UNO type manager for accessing type information + */ + CppProducer(const OString& outputDir, bool verbose, bool dryRun, + const rtl::Reference<TypeManager>& typeManager); + + /** + * Initialize the single combined output file + */ + void initializeCombinedFile(); + + /** + * Finalize and close the single combined output file + */ + void finalizeCombinedFile(); + + /** + * Generate C++ opaque enum bridge + * Creates conversion functions between enum and integers + */ + void produceEnum(std::string_view name, const rtl::Reference<unoidl::EnumTypeEntity>& entity); + + /** + * Generate C++ opaque struct bridge + * Creates wrapper class and field accessor functions + */ + void produceStruct(std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity); + + /** + * Generate C++ opaque interface bridge + * Creates wrapper class and method bridge functions + */ + void produceInterface(std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity); + + /** + * Generate C++ opaque service bridge + * Creates service creation bridge functions + */ + void produceService(std::string_view name, + const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& entity); + +private: + // Configuration + OString m_outputDir; + bool m_verbose; + bool m_dryRun; + rtl::Reference<TypeManager> m_typeManager; + + // Combined output files + std::unique_ptr<CppFile> m_combinedSourceFile; // .cxx file + std::unique_ptr<CppFile> m_combinedHeaderFile; // .hxx file + + // ======================================================================== + // CORE TYPE GENERATION METHODS + // ======================================================================== + void generateStructWrapper(std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity); + void generateEnumBridge(std::string_view name, + const rtl::Reference<unoidl::EnumTypeEntity>& entity); + void generateStructBridge(std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity); + void generateCBridge(std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity); + + void generateStructHeader(std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity); + void generateStructSource(std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity); + + void generateInterfaceHeader(std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity); + void generateInterfaceSource(std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity); + + void generateEnumHeader(std::string_view name, + const rtl::Reference<unoidl::EnumTypeEntity>& entity); + void generateEnumSource(std::string_view name, + const rtl::Reference<unoidl::EnumTypeEntity>& entity); + + void + generateServiceHeader(std::string_view name, + const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& entity); + void + generateServiceSource(std::string_view name, + const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& entity); + + // ======================================================================== + // FILE GENERATION UTILITIES + // ======================================================================== + // Unified include and namespace generation (eliminates duplication) + void generateCommonIncludes(CppFile& file, std::string_view name, bool needsLogging = false, + bool needsUnoTypes = false); + void generateSourceNamespaces(CppFile& file, std::string_view name); + + // Legacy functions (maintained for existing calls) + void generateStructSourceIncludes(CppFile& file, std::string_view name); + void generateStructSourceNamespaces(CppFile& file, std::string_view name); + void generateEnumIncludes(CppFile& file, std::string_view name); + void generateEnumSourceIncludes(CppFile& file, std::string_view name); + void generateInterfaceSourceIncludes(CppFile& file, std::string_view name); + void generateInterfaceSourceNamespaces(CppFile& file, std::string_view name); + + // ======================================================================== + // TYPE SYSTEM & CONVERSION UTILITIES + // ======================================================================== + // Type conversion and mapping + OString convertUnoTypeToCpp(std::u16string_view unoType) const; + std::string convertTemplateArguments(const std::string& unoType) const; + OString getCppTypeName(std::u16string_view unoType) const; + OString getRustFFITypeName(std::u16string_view unoType) const; + OString mapUnoPrimitiveToSal(std::u16string_view unoType) const; + OUString resolveTypedef(std::u16string_view unoType) const; + OString convertBasicType(const OString& typeName) const; + OString convertUnoTypeToHandle(std::u16string_view unoType) const; + + // Type classification + bool isUnoType(std::u16string_view typeName) const; + bool isUnoStruct(std::u16string_view typeName) const; + bool isUnoEnum(std::u16string_view typeName) const; + bool isUnoInterface(std::u16string_view typeName) const; + bool isUnoPolymorphicStruct(std::u16string_view typeName) const; + bool isUnoConstantGroup(std::u16string_view typeName) const; + + // Method return type handling + OString getMethodReturnType(std::u16string_view returnType) const; + OString getMethodDefaultReturn(std::u16string_view returnType) const; + + // ======================================================================== + // CODE GENERATION HELPERS + // ======================================================================== + // C Bridge generation helpers + void generateCBridgeIncludes(CppFile& file, std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity); + void generateCBridgeTypeDefinitions(CppFile& file, const OString& handleTypeName); + void generateCBridgeConstructors(CppFile& file, const OString& className, + const OString& functionPrefix, const OString& handleTypeName); + void generateCBridgeDestructor(CppFile& file, const OString& className, + const OString& functionPrefix, const OString& handleTypeName); + void generateCBridgeValidation(CppFile& file, const OString& className, + const OString& functionPrefix, const OString& handleTypeName); + void generateCBridgeMethods(CppFile& file, const OString& className, + const OString& functionPrefix, const OString& handleTypeName, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity); + + // Interface generation helpers + void generateInterfaceSourceBasicFunctions(CppFile& file, const OString& functionPrefix, + const OString& className, + const OString& handleTypeName); + void generateInterfaceSourceMethodImplementations( + CppFile& file, const OString& functionPrefix, const OString& className, + const OString& handleTypeName, const rtl::Reference<unoidl::InterfaceTypeEntity>& entity); + void generateSingleInterfaceMethod(CppFile& file, const OString& functionPrefix, + const OString& className, const OString& handleTypeName, + const unoidl::InterfaceTypeEntity::Method& method); + void generateActualMethodCall(CppFile& file, const unoidl::InterfaceTypeEntity::Method& method); + void generateReturnValueConversion(CppFile& file, + const unoidl::InterfaceTypeEntity::Method& method); + + // Struct generation helpers + void generateStructSourceBasicFunctions(CppFile& file, const OString& functionPrefix, + const OString& handleTypeName, + const std::string& className); + void + generateStructSourceBaseMembers(CppFile& file, const OString& functionPrefix, + const OString& handleTypeName, const std::string& className, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity); + void + generateStructSourceDirectMembers(CppFile& file, const OString& functionPrefix, + const OString& handleTypeName, const std::string& className, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity); + void generateStructMemberAccessors(CppFile& file, const OString& functionPrefix, + const OString& handleTypeName, const std::string& className, + const unoidl::PlainStructTypeEntity::Member& member); + + // ======================================================================== + // UTILITY FUNCTIONS + // ======================================================================== + static std::string_view splitName(std::string_view name); + static OString getInterfaceClassName(std::string_view unoName); + static OString getCFunctionPrefix(std::string_view unoName); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/rustmaker/rustfile.cxx b/codemaker/source/rustmaker/rustfile.cxx new file mode 100644 index 000000000000..0b2b863d87ed --- /dev/null +++ b/codemaker/source/rustmaker/rustfile.cxx @@ -0,0 +1,664 @@ +/* -*- 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/. + */ + +// Implementation of Rust file generation and module management + +#include "rustfile.hxx" +#include <algorithm> +#include <filesystem> +#include <iostream> +#include <vector> +#include <functional> +#include <codemaker/global.hxx> + +RustFile::RustFile(std::string_view directory, std::string_view typeName) + : m_filePath(createFilePath(directory, typeName)) + , m_indentLevel(0) +{ +} + +std::string RustFile::getPath() const { return m_filePath.string(); } + +void RustFile::openFile() +{ + std::filesystem::create_directories(m_filePath.parent_path()); + createModFiles(); + addFileToModDeclaration(); + m_fileStream.open(m_filePath, std::fstream::out | std::fstream::trunc); + m_indentLevel = 0; +} + +void RustFile::closeFile() { m_fileStream.close(); } + +void RustFile::createLibFile(const std::filesystem::path& outputDir) +{ + // Create generated/rustmaker directory first, then src directory inside it + std::filesystem::path generatedDir = outputDir / "generated"; + std::filesystem::path rustmakerDir = generatedDir / "rustmaker"; + std::filesystem::path srcDir = rustmakerDir / "src"; + std::filesystem::create_directories(srcDir); + + std::filesystem::path libFile = srcDir / "lib.rs"; + if (!std::filesystem::exists(libFile)) + { + std::ofstream libStream(libFile); + libStream << "//! Generated Rust bindings for LibreOffice UNO types\n"; + libStream << "//! \n"; + libStream << "//! This crate contains automatically generated Rust bindings\n"; + libStream << "//! for LibreOffice UNO (Universal Network Objects) types.\n\n"; + libStream.close(); + } +} + +void RustFile::createCargoFile(const std::filesystem::path& outputDir, const std::string& crateName) +{ + // Create Cargo.toml in the generated/rustmaker directory + std::filesystem::path generatedDir = outputDir / "generated"; + std::filesystem::path rustmakerDir = generatedDir / "rustmaker"; + std::filesystem::path cargoFile = rustmakerDir / "Cargo.toml"; + if (!std::filesystem::exists(cargoFile)) + { + std::ofstream cargoStream(cargoFile); + cargoStream << "[package]\n"; + cargoStream << "name = \"" << crateName << "\"\n"; + cargoStream << "version = \"0.1.0\"\n"; + cargoStream << "edition = \"2024\"\n"; + cargoStream << "\n"; + cargoStream << "[lib]\n"; + cargoStream << "name = \"rustmaker\"\n"; + // Support both dynamic and static linking + cargoStream << "crate-type = [\"cdylib\", \"rlib\"]\n"; + cargoStream << "description = \"Generated Rust bindings for LibreOffice UNO types\"\n\n"; + cargoStream << "[dependencies]\n"; + cargoStream << "# UNO runtime dependencies\n"; + cargoStream << "rust_uno = { path = \"../../../../rust_uno\" }\n"; + cargoStream.close(); + } +} + +void RustFile::createGeneratedModFile(const std::filesystem::path& outputDir) +{ + // Create generated directory and its mod.rs file + std::filesystem::path generatedDir = outputDir / "generated"; + std::filesystem::create_directories(generatedDir); + + std::filesystem::path modFile = generatedDir / "mod.rs"; + if (!std::filesystem::exists(modFile)) + { + std::ofstream modStream(modFile); + modStream << "//! Generated opaque bindings for LibreOffice UNO types\n"; + modStream << "//!\n"; + modStream << "//! This module contains both Rust and C++ opaque bindings\n"; + modStream << "//! auto-generated by rustmaker\n\n"; + modStream << "// Include the generated rustmaker bindings directly\n"; + modStream << "// The rustmaker directory contains a complete Rust project structure\n"; + modStream << "// We include its lib.rs as a module here\n"; + modStream << "#[path = \"rustmaker/src/lib.rs\"]\n"; + modStream << "pub mod rustmaker;\n\n"; + modStream << "// Re-export the UNO namespace for easier access\n"; + modStream << "pub use rustmaker::com;\n\n"; + modStream << "// Note: cpp_rustmaker directory contains C++ bridge files\n"; + modStream << "// These are compiled separately and linked via build.rs\n"; + modStream.close(); + } +} + +void RustFile::finalizeModFiles(const std::filesystem::path& outputDir) +{ + // Find all directories in the rustmaker source tree and update their mod.rs files + std::filesystem::path rustmakerSrc = outputDir / "generated" / "rustmaker" / "src"; + + if (!std::filesystem::exists(rustmakerSrc)) + { + return; + } + + // Start processing from the rustmaker src directory + processDirectoryRecursively(rustmakerSrc); +} + +RustFile& RustFile::beginBlock() +{ + beginLine(); + append("{"); + endLine(); + ++m_indentLevel; + return *this; +} + +RustFile& RustFile::endBlock() +{ + --m_indentLevel; + beginLine(); + append("}"); + endLine(); + return *this; +} + +// Text output methods +RustFile& RustFile::beginLine() +{ + for (int i = 0; i < m_indentLevel; i++) + { + m_fileStream << " "; + } + return *this; +} + +RustFile& RustFile::extraIndent() +{ + m_fileStream << " "; + return *this; +} + +RustFile& RustFile::append(std::string_view item) +{ + m_fileStream << item; + return *this; +} + +RustFile& RustFile::append(std::u16string_view item) +{ + m_fileStream << u2b(item); + return *this; +} + +RustFile& RustFile::endLine() +{ + m_fileStream << '\n'; + return *this; +} + +void RustFile::createModFiles() +{ + std::filesystem::path outputRoot = findOutputRoot(); + + // Create lib and cargo files only once at the proper root level + // outputRoot should be the rustmaker directory, so we pass its parent to the create functions + // which will then create the rustmaker directory and put files inside it + createGeneratedModFile( + outputRoot.parent_path().parent_path()); // Create generated/mod.rs at top level + createLibFile( + outputRoot.parent_path().parent_path()); // Create in the directory containing generated/ + createCargoFile(outputRoot.parent_path().parent_path(), "rustmaker"); + + // Build path hierarchy from file location up to project root + std::filesystem::path workingPath = m_filePath.parent_path(); + std::vector<std::filesystem::path> pathsToProcess; + + // Collect all directory paths from current file up to root + while (workingPath != outputRoot && workingPath.has_parent_path()) + { + pathsToProcess.push_back(workingPath); + workingPath = workingPath.parent_path(); + } + + // Process paths from root down to ensure proper mod.rs structure + std::reverse(pathsToProcess.begin(), pathsToProcess.end()); + for (const auto& path : pathsToProcess) + { + ensureModFileExists(path); + } +} + +// Complex path resolution - walks up directory tree to find project root +std::filesystem::path RustFile::findOutputRoot() +{ + std::filesystem::path currentPath = m_filePath.parent_path(); + + // Walk up until we find a directory structure indicating project root + while (currentPath.has_parent_path() && currentPath != currentPath.parent_path()) + { + if (currentPath.filename() == "src") + { + return currentPath.parent_path(); // Return rustmaker directory + } + + // Look for rustmaker directory as the project root + if (currentPath.filename() == "rustmaker") + { + return currentPath; + } + + // Look for existing src/ subdirectory as root indicator + std::filesystem::path srcDir = currentPath / "src"; + if (std::filesystem::exists(srcDir)) + { + return currentPath; + } + + currentPath = currentPath.parent_path(); + } + + // Fallback: find parent of src directory (which should be rustmaker) + std::filesystem::path result = m_filePath.parent_path(); + while (result.has_parent_path() && result.filename() != "src") + { + result = result.parent_path(); + } + + if (result.filename() == "src") + { + return result.parent_path(); // Return rustmaker directory + } + + return result; // Default case +} + +void RustFile::addFileToModDeclaration() +{ + std::filesystem::path parentDir = m_filePath.parent_path(); + std::filesystem::path modFile = parentDir / "mod.rs"; + std::string fileName = m_filePath.stem().string(); // Get filename without extension + + if (std::filesystem::exists(modFile)) + { + addModuleDeclaration(modFile, fileName); + } +} + +void RustFile::ensureModFileExists(const std::filesystem::path& dirPath) +{ + // Skip mod.rs creation for the src directory itself + if (dirPath.filename() == "src") + { + return; + } + + std::filesystem::path modFile = dirPath / "mod.rs"; + if (!std::filesystem::exists(modFile)) + { + // Create mod.rs file with proper structure + std::ofstream modStream(modFile); + modStream << "// Generated mod.rs file\n\n"; + modStream << "#[allow(non_snake_case)]\n"; + modStream.close(); + } + + // After ensuring the mod.rs exists, collect all subdirectories and modules + // to automatically add pub use ::* patterns + std::vector<std::string> childModules; + + // Scan directory for subdirectories (modules) and .rs files + if (std::filesystem::exists(dirPath)) + { + try + { + for (const auto& entry : std::filesystem::directory_iterator(dirPath)) + { + if (entry.is_directory()) + { + std::string dirName = entry.path().filename().string(); + childModules.push_back(dirName); + } + else if (entry.is_regular_file() && entry.path().extension() == ".rs") + { + std::string fileName = entry.path().stem().string(); + if (fileName != "mod") // Don't include mod.rs itself + { + childModules.push_back(fileName); + } + } + } + } + catch (const std::filesystem::filesystem_error&) + { + // Handle permission errors gracefully + } + } + + // Add module declarations and pub use statements for all child modules + for (const std::string& moduleName : childModules) + { + addModuleDeclaration(modFile, moduleName); + } + + // Recursively ensure parent modules are properly declared + if (dirPath.has_parent_path()) + { + std::filesystem::path parentDir = dirPath.parent_path(); + std::filesystem::path outputRoot = findOutputRoot(); + + if (parentDir >= outputRoot) + { + std::string moduleName = dirPath.filename().string(); + + if (parentDir.filename() == "src") + { + std::filesystem::path libFile = parentDir / "lib.rs"; + if (std::filesystem::exists(libFile)) + { + addModuleDeclaration(libFile, moduleName); + } + } + else if (parentDir == outputRoot) + { + std::filesystem::path libFile = parentDir / "src" / "lib.rs"; + if (std::filesystem::exists(libFile)) + { + addModuleDeclaration(libFile, moduleName); + } + } + else + { + ensureModFileExists(parentDir); + std::filesystem::path parentModFile = parentDir / "mod.rs"; + if (std::filesystem::exists(parentModFile)) + { + addModuleDeclaration(parentModFile, moduleName); + } + } + } + } +} + +void RustFile::addModuleDeclaration(const std::filesystem::path& modFile, + const std::string& moduleName) +{ + // Read existing content to check for duplicates and organize sections + std::ifstream inFile(modFile); + std::string line; + std::vector<std::string> pubModLines; + std::vector<std::string> otherLines; + bool foundModDeclaration = false; + + while (std::getline(inFile, line)) + { + if (line.find("pub mod " + moduleName + ";") != std::string::npos) + { + foundModDeclaration = true; + } + + if (line.find("pub mod ") == 0) + { + pubModLines.push_back(line); + } + else if (!(line.find("pub use ") == 0 && line.find("::*;") != std::string::npos)) + { + // Skip old pub use re-export lines, include everything else + otherLines.push_back(line); + } + } + inFile.close(); + + // If module declaration is already declared, return + if (foundModDeclaration) + { + return; + } + + // Add new declarations if not found + if (!foundModDeclaration) + { + pubModLines.push_back("pub mod " + moduleName + ";"); + } + // Re-export generation removed - causes unused import warnings + + // Rewrite the file with organized structure + std::ofstream outFile(modFile, std::ios::trunc); + + // Write other lines first (comments, etc.) + for (const auto& otherLine : otherLines) + { + // Skip re-export comment lines + if (otherLine.find("// Re-export all items from child modules") == std::string::npos) + { + outFile << otherLine << "\n"; + } + } + + // Write all pub mod declarations + for (const auto& modLine : pubModLines) + { + outFile << modLine << "\n"; + } + + // Re-export section removed - causes unused import warnings + + outFile.close(); +} + +// Converts UNO type name to file path (dots become path separators) +std::filesystem::path RustFile::createFilePath(std::string_view dir, std::string_view type) +{ + std::string subdir(type); + for (char& c : subdir) + if (c == '.') + c = '/'; + + std::filesystem::path path(dir); + path /= "rustmaker"; // Add rustmaker subdirectory for generated types + path /= "src"; + path /= subdir + ".rs"; + return path; +} + +// CppFile implementation +CppFile::CppFile(std::string_view directory, std::string_view typeName) + : m_filePath(createCppFilePath(directory, typeName)) + , m_indentLevel(0) +{ +} + +CppFile::CppFile(std::string_view directory, std::string_view typeName, std::string_view extension) + : m_filePath(createCppFilePath(directory, typeName, extension)) + , m_indentLevel(0) +{ +} + +std::string CppFile::getPathString() const { return m_filePath.string(); } +std::filesystem::path CppFile::getPath() const { return m_filePath; } +std::string CppFile::getExtension() const { return m_filePath.extension().string(); } + +void CppFile::openFile() +{ + std::filesystem::create_directories(m_filePath.parent_path()); + m_fileStream.open(m_filePath, std::fstream::out | std::fstream::trunc); + m_indentLevel = 0; +} + +void CppFile::openFileAppend() +{ + std::filesystem::create_directories(m_filePath.parent_path()); + m_fileStream.open(m_filePath, std::fstream::out | std::fstream::app); + m_indentLevel = 0; +} + +void CppFile::closeFile() { m_fileStream.close(); } + +CppFile& CppFile::beginBlock() +{ + beginLine(); + append("{"); + endLine(); + ++m_indentLevel; + return *this; +} + +CppFile& CppFile::endBlock() +{ + --m_indentLevel; + beginLine(); + append("}"); + endLine(); + return *this; +} + +CppFile& CppFile::beginLine() +{ + m_fileStream << std::string(4 * m_indentLevel, ' '); + return *this; +} + +CppFile& CppFile::extraIndent() +{ + m_fileStream << " "; + return *this; +} + +CppFile& CppFile::append(std::string_view item) +{ + m_fileStream << item; + return *this; +} + +CppFile& CppFile::append(std::u16string_view item) +{ + m_fileStream << u2b(item); + return *this; +} + +CppFile& CppFile::endLine() +{ + m_fileStream << std::endl; + return *this; +} + +CppFile& CppFile::writeIncludes(const std::vector<std::string>& includes) +{ + for (const auto& include : includes) + { + beginLine().append("#include ").append(include).endLine(); + } + endLine(); + return *this; +} + +CppFile& CppFile::writeNamespaceBegin(const std::vector<std::string>& namespaces) +{ + for (const auto& ns : namespaces) + { + beginLine().append("namespace ").append(ns).append(" {").endLine(); + } + endLine(); + return *this; +} + +CppFile& CppFile::writeNamespaceEnd(const std::vector<std::string>& namespaces) +{ + endLine(); + for (auto it = namespaces.rbegin(); it != namespaces.rend(); ++it) + { + beginLine().append("} // namespace ").append(*it).endLine(); + } + return *this; +} + +std::filesystem::path CppFile::createCppFilePath(std::string_view dir, std::string_view type, + std::string_view extension) +{ + std::filesystem::path outputDir(dir); + + // For the special case of "rust_uno_bindings", create it directly in the output directory + if (type == "rust_uno_bindings") + { + return outputDir / (std::string(type) + std::string(extension)); + } + + std::filesystem::path basePath = outputDir; + + std::string typeName(type); + + // Extract interface name (part after last dot) + size_t lastDot = typeName.find_last_of('.'); + std::string interfaceName + = (lastDot != std::string::npos) ? typeName.substr(lastDot + 1) : typeName; + + // Create namespace directory path from the full type name + if (lastDot != std::string::npos) + { + // Convert namespace dots to path separators: com.sun.star.frame -> com/sun/star/frame/ + std::string namespacePath = typeName.substr(0, lastDot); + + // Replace dots with path separators + std::replace(namespacePath.begin(), namespacePath.end(), '.', '/'); + + // Build full path: cpp_rustmaker/com/sun/star/frame/XComponentLoader.hxx + return basePath / namespacePath / (interfaceName + std::string(extension)); + } + else + { + // No namespace, put directly in cpp_rustmaker directory + return basePath / (interfaceName + std::string(extension)); + } +} + +void RustFile::processDirectoryRecursively(const std::filesystem::path& currentDir) +{ + if (!std::filesystem::exists(currentDir) || !std::filesystem::is_directory(currentDir)) + { + return; + } + + // Skip the root src directory + if (currentDir.filename() == "src" && currentDir.parent_path().filename() == "rustmaker") + { + // Process subdirectories but don't create mod.rs for src itself + for (const auto& entry : std::filesystem::directory_iterator(currentDir)) + { + if (entry.is_directory()) + { + processDirectoryRecursively(entry.path()); + } + } + return; + } + + std::vector<std::string> childModules; + + try + { + // Collect all subdirectories and .rs files + for (const auto& entry : std::filesystem::directory_iterator(currentDir)) + { + if (entry.is_directory()) + { + std::string dirName = entry.path().filename().string(); + childModules.push_back(dirName); + // Recursively process subdirectory + processDirectoryRecursively(entry.path()); + } + else if (entry.is_regular_file() && entry.path().extension() == ".rs") + { + std::string fileName = entry.path().stem().string(); + if (fileName != "mod" && fileName != "lib") // Don't include mod.rs or lib.rs + { + childModules.push_back(fileName); + } + } + } + } + catch (const std::filesystem::filesystem_error&) + { + // Handle permission errors gracefully + return; + } + + // Create/update mod.rs file if there are child modules + if (!childModules.empty()) + { + std::filesystem::path modFile = currentDir / "mod.rs"; + + // Create the mod.rs file with all declarations + std::ofstream modStream(modFile, std::ios::trunc); + modStream << "// Generated mod.rs file\n\n"; + modStream << "#[allow(non_snake_case)]\n"; + + for (const std::string& moduleName : childModules) + { + modStream << "pub mod " << moduleName << ";\n"; + } + + // Re-export section removed per request + + modStream.close(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/rustmaker/rustfile.hxx b/codemaker/source/rustmaker/rustfile.hxx new file mode 100644 index 000000000000..a5ecd307a902 --- /dev/null +++ b/codemaker/source/rustmaker/rustfile.hxx @@ -0,0 +1,112 @@ +/* -*- 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/. + */ + +#pragma once + +#include <filesystem> +#include <fstream> +#include <set> +#include <string> +#include <vector> +#include <algorithm> + +#include <codemaker/typemanager.hxx> + +/** + * Generates Rust source files from UNO type definitions + */ +class RustFile +{ +public: + RustFile(std::string_view directory, std::string_view typeName); + + std::string getPath() const; + void openFile(); + void closeFile(); + + // Static methods for project setup + static void createLibFile(const std::filesystem::path& outputDir); + static void createCargoFile(const std::filesystem::path& outputDir, + const std::string& crateName = "rustmaker"); + static void createGeneratedModFile(const std::filesystem::path& outputDir); + static void finalizeModFiles(const std::filesystem::path& outputDir); + + // Code generation helpers + RustFile& beginBlock(); + RustFile& endBlock(); + RustFile& beginLine(); + RustFile& extraIndent(); + RustFile& append(std::string_view item); + RustFile& append(std::u16string_view item); + RustFile& endLine(); + +private: + // Complex path resolution - walks up directory tree to find project root + std::filesystem::path findOutputRoot(); + + // Module management - handles mod.rs creation and declarations + void createModFiles(); + void addFileToModDeclaration(); + void ensureModFileExists(const std::filesystem::path& dirPath); + static void addModuleDeclaration(const std::filesystem::path& modFile, + const std::string& moduleName); + + // Converts UNO type name to file path (dots become path separators) + static std::filesystem::path createFilePath(std::string_view dir, std::string_view type); + +private: + std::filesystem::path m_filePath; + std::ofstream m_fileStream; + int m_indentLevel; + + // Helper function for recursive directory processing + static void processDirectoryRecursively(const std::filesystem::path& currentDir); +}; + +/** + * Generates C++ bridge files for UNO opaque functions + */ +class CppFile +{ +public: + CppFile(std::string_view directory, std::string_view typeName); + CppFile(std::string_view directory, std::string_view typeName, std::string_view extension); + + std::string getPathString() const; + std::filesystem::path getPath() const; + std::string getExtension() const; + void openFile(); + void openFileAppend(); // Open for appending (continue writing) + void closeFile(); + + // Code generation helpers + CppFile& beginBlock(); + CppFile& endBlock(); + CppFile& beginLine(); + CppFile& extraIndent(); + CppFile& append(std::string_view item); + CppFile& append(std::u16string_view item); + CppFile& endLine(); + + // C++ specific helpers + CppFile& writeIncludes(const std::vector<std::string>& includes); + CppFile& writeNamespaceBegin(const std::vector<std::string>& namespaces); + CppFile& writeNamespaceEnd(const std::vector<std::string>& namespaces); + +private: + static std::filesystem::path createCppFilePath(std::string_view dir, std::string_view type, + std::string_view extension = ".hxx"); + +private: + std::filesystem::path m_filePath; + std::ofstream m_fileStream; + int m_indentLevel; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/rustmaker/rustmaker.cxx b/codemaker/source/rustmaker/rustmaker.cxx new file mode 100644 index 000000000000..f17bef9e2442 --- /dev/null +++ b/codemaker/source/rustmaker/rustmaker.cxx @@ -0,0 +1,64 @@ +/* -*- 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 <iostream> + +#include <sal/main.h> +#include <unoidl/unoidl.hxx> + +#include "rustoptions.hxx" +#include "unoproduce.hxx" + +// coverity[tainted_data] - this is a build time tool +SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv) +{ + try + { + RustOptions options; + UnoProducer producer; + + // Parse command line arguments and generate code if successful + if (options.initOptions(argc, argv)) + { + producer.initProducer(options); + producer.produceAll(); + // Finalize opaque pointer generation + producer.finalizeGeneration(); + } + } + catch (const ::IllegalArgument& e) + { + std::cerr << "ERROR: Illegal option " << e.m_message << '\n'; + return EXIT_FAILURE; + } + catch (const ::CannotDumpException& e) + { + std::cerr << "ERROR: Could not dump as " << e.getMessage() << '\n'; + return EXIT_FAILURE; + } + catch (const unoidl::NoSuchFileException& e) + { + std::cerr << "ERROR: No such file " << e.getUri() << '\n'; + return EXIT_FAILURE; + } + catch (const unoidl::FileFormatException& e) + { + std::cerr << "ERROR: Bad format of " << e.getUri() << ", '" << e.getDetail() << "'\n"; + return EXIT_FAILURE; + } + catch (const std::exception& e) + { + std::cerr << "ERROR: " << e.what() << '\n'; + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/rustmaker/rustoptions.cxx b/codemaker/source/rustmaker/rustoptions.cxx new file mode 100644 index 000000000000..b92aba9c5565 --- /dev/null +++ b/codemaker/source/rustmaker/rustoptions.cxx @@ -0,0 +1,196 @@ +/* -*- 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 <iostream> + +#include "rustoptions.hxx" + +bool RustOptions::initOptions(int argc, char* argv[], bool) +{ + if (argc < 2) + { + std::cerr << prepareHelp(); + return false; + } + + for (int i = 1; i < argc; i++) + { + OString argument = argv[i]; + + if (argument == "-h"_ostr || argument == "--help"_ostr) + { + std::cout << prepareHelp(); + return false; + } + else if (argument == "-v"_ostr || argument == "--verbose"_ostr) + { + m_options["--verbose"_ostr] = ""_ostr; + } + else if (argument == "-n"_ostr || argument == "--dry-run"_ostr) + { + m_options["--dry-run"_ostr] = ""_ostr; + m_options["--verbose"_ostr] = "--dry-run"_ostr; // dry run implies verbose + } + else if (argument == "-T"_ostr || argument == "--types"_ostr) + { + if (i + 1 < argc) + { + if (m_options.count("--types"_ostr) == 0) + { + m_options["--types"_ostr] = argv[++i]; + } + else + { + // Allow multiple -T options by joining with semicolon + m_options["--types"_ostr] += ";"_ostr + argv[++i]; + } + } + else + { + throw IllegalArgument("-T/--types must be followed by type name or wildcard"_ostr); + } + } + else if (argument == "-X"_ostr || argument == "--extra-types"_ostr) + { + if (i + 1 < argc) + { + m_extra_input_files.emplace_back(argv[++i]); + } + else + { + throw IllegalArgument("-X/--extra-types must be followed by .rdb file"_ostr); + } + } + else if (argument == "-Ocpp"_ostr) + { + if (i + 1 < argc) + { + m_options["--cpp-output-dir"_ostr] = argv[++i]; + } + else + { + throw IllegalArgument("-Ocpp must be followed by directory"_ostr); + } + } + else if (argument == "-Orust"_ostr) + { + if (i + 1 < argc) + { + m_options["--rust-output-dir"_ostr] = argv[++i]; + } + else + { + throw IllegalArgument("-Orust must be followed by directory"_ostr); + } + } + else if (argument == "-O"_ostr || argument == "--output-dir"_ostr) + { + if (i + 1 < argc) + { + // Legacy support: set both cpp and rust output to same directory + m_options["--cpp-output-dir"_ostr] + = OString::Concat(argv[++i]) + "/generated/cpp_rustmaker"; + m_options["--rust-output-dir"_ostr] = OString::Concat(argv[i]) + "/generated"; + } + else + { + throw IllegalArgument("-O/--output-dir must be followed by directory"_ostr); + } + } + else + { + // Any non-option argument is treated as input .rdb file + m_inputFiles.emplace_back(argument); + } + } + + // Validate required arguments + if (m_inputFiles.empty()) + { + throw IllegalArgument("at least one .rdb file must be provided"_ostr); + } + + if (m_options.count("--cpp-output-dir"_ostr) == 0 + || m_options.count("--rust-output-dir"_ostr) == 0) + { + throw IllegalArgument( + "Both -Ocpp and -Orust must be provided (or use -O for legacy mode)"_ostr); + } + + return true; +} + +OString RustOptions::prepareHelp() +{ + return R"( + +About: + rustmaker is a tool for generating Rust files from a type library generated by the UNOIDL compiler unoidl-write. + The generated code files require the implemented Rust types from rust_uno. + +Usage: + rustmaker [-v|--verbose] [-n|--dry-run] + [-T|--types <type name or wildcard>] + [-X|--extra-types <.rdb file>] + -Ocpp <cpp output directory> -Orust <rust output directory> + <rdb file(s)> + + OR (legacy mode): + rustmaker [-v|--verbose] [-n|--dry-run] + [-T|--types <type name or wildcard>] + [-X|--extra-types <.rdb file>] + -O|--output-dir <output directory> + <rdb file(s)> + +Options: + -h, --help + Display this help message. + + -v, --verbose + Log the name of every file created and type generated to stdout. + + -n, --dry-run + Do not write generated files to disk. Implies --verbose. + + -T, --types <type name or wildcard> + Specify a type name or a wildcard pattern to generate code for. This option can be specified multiple times. If not specified, all types in the given .rdb files are generated. + + -X, --extra-types <.rdb file> + Use an .rdb file containing types to be taken into account without generating output for them. This option can be specified multiple times. + + -Ocpp <directory> + Specify the directory to write generated C++ files to. + + -Orust <directory> + Specify the directory to write generated Rust files to. + + -O, --output-dir <directory> (legacy mode) + Specify the directory to write generated files to. Creates subdirectories for C++ and Rust files. + +Examples: + rustmaker --verbose -T com.acme.XSomething \ + -X types.rdb -Ocpp acme/cpp -Orust acme/src acmetypes.rdb + + rustmaker --dry-run -T com.acme.* -X types.rdb \ + -X offapi.rdb -Ocpp acme/cpp -Orust acme/src acmetypes.rdb + + rustmaker -X types.rdb -Ocpp acme/cpp -Orust acme/src \ + acmetypes.rdb moretypes.rdb + + # Generate opaque pointer bindings for all UNO types + rustmaker -T com.sun.star.frame.XComponentLoader \ + -X offapi.rdb -Ocpp cpp/ -Orust src/generated/ offapi.rdb + + # Legacy mode (backward compatibility) + rustmaker --verbose -T com.acme.XSomething \ + -X types.rdb -O acme/ acmetypes.rdb +)"_ostr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/rustmaker/rustoptions.hxx b/codemaker/source/rustmaker/rustoptions.hxx new file mode 100644 index 000000000000..7300613ea8ad --- /dev/null +++ b/codemaker/source/rustmaker/rustoptions.hxx @@ -0,0 +1,27 @@ +/* -*- 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/. + */ + +#pragma once + +#include <rtl/string.hxx> +#include <codemaker/options.hxx> + +/** + * Command line options parser for rustmaker + */ +class RustOptions : public Options +{ +public: + RustOptions() { m_program = "rustmaker"_ostr; } + + bool initOptions(int argc, char* argv[], bool bCmdFile = false) override; + OString prepareHelp() override; +}; + +/* vim:set shiftwidth=2 softtabstop=4 expandtab: */ diff --git a/codemaker/source/rustmaker/rustproduce.cxx b/codemaker/source/rustmaker/rustproduce.cxx new file mode 100644 index 000000000000..100827f0936a --- /dev/null +++ b/codemaker/source/rustmaker/rustproduce.cxx @@ -0,0 +1,1234 @@ +/* -*- 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 "rustproduce.hxx" +#include "rustfile.hxx" +#include <codemaker/typemanager.hxx> +#include <sal/log.hxx> +#include <iostream> +#include <set> + +RustProducer::RustProducer(const OString& outputDir, bool verbose, bool dryRun, + const rtl::Reference<TypeManager>& typeManager) + : m_outputDir(outputDir) + , m_verbose(verbose) + , m_dryRun(dryRun) + , m_typeManager(typeManager) +{ +} + +void RustProducer::produceEnum(std::string_view name, + const rtl::Reference<unoidl::EnumTypeEntity>& entity) +{ + RustFile file(m_outputDir, name); + + if (m_verbose) + std::cout << "[rust-opaque-enum] " << name << " -> " << file.getPath() << '\n'; + if (m_dryRun) + return; + + file.openFile(); + + OString typeName(splitName(name)); // Simple name for Rust enum + OString externFunctionPrefix = getRustTypeName(name); // Full name for extern "C" functions + + file.beginLine() + .append("/// Opaque Rust enum wrapper for ") + .append(name) + .endLine() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("#[repr(C)]") + .endLine() + .beginLine() + .append("#[allow(non_camel_case_types)]") + .endLine() + .beginLine() + .append("#[derive(Debug, Clone, Copy, PartialEq, Eq)]") + .endLine() + .beginLine() + .append("pub enum ") + .append(typeName) + .endLine() + .beginBlock(); + + // Track used discriminant values to avoid duplicates + std::set<sal_Int32> usedValues; + + for (const auto& member : entity->getMembers()) + { + // Only include members with unique discriminant values + if (usedValues.find(member.value) == usedValues.end()) + { + usedValues.insert(member.value); + file.beginLine() + .append(member.name) + .append(" = ") + .append(OString::number(member.value)) + .append(",") + .endLine(); + } + } + + file.endBlock(); + + // Add conversion functions using extern "C" bridge + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("impl ") + .append(typeName) + .endLine() + .beginBlock() + .beginLine() + .append("pub fn from_i32(value: i32) -> Self {") + .endLine() + .extraIndent() + .beginLine() + .append("unsafe { ") + .append(externFunctionPrefix) + .append("_from_i32(value) }") + .endLine() + .beginLine() + .append("}") + .endLine() + .beginLine() + .append("") + .endLine() + .beginLine() + .append("pub fn to_i32(self) -> i32 {") + .endLine() + .extraIndent() + .beginLine() + .append("unsafe { ") + .append(externFunctionPrefix) + .append("_to_i32(self) }") + .endLine() + .beginLine() + .append("}") + .endLine() + .endBlock(); + + // Extern "C" declarations + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("unsafe extern \"C\" {") + .endLine() + .extraIndent() + .beginLine() + .append("fn ") + .append(externFunctionPrefix) + .append("_from_i32(value: i32) -> ") + .append(typeName) + .append(";") + .endLine() + .beginLine() + .append("fn ") + .append(externFunctionPrefix) + .append("_to_i32(value: ") + .append(typeName) + .append(") -> i32;") + .endLine() + .beginLine() + .append("}") + .endLine(); + + file.closeFile(); +} + +void RustProducer::produceStruct(std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity) +{ + RustFile file(m_outputDir, name); + + if (m_verbose) + std::cout << "[rust-opaque-struct] " << name << " -> " << file.getPath() << '\n'; + if (m_dryRun) + return; + + file.openFile(); + + OString typeName(splitName(name)); // Simple name for Rust struct + OString externFunctionPrefix = getRustTypeName(name); // Full name for extern "C" functions + + file.beginLine() + .append("/// Opaque Rust struct wrapper for ") + .append(name) + .endLine() + .beginLine() + .append("use std::ffi::c_void;") + .endLine() + .beginLine() + .append("#[allow(non_camel_case_types)]") + .endLine() + .beginLine() + .append("pub struct ") + .append(typeName) + .endLine() + .beginBlock() + .beginLine() + .append("ptr: *mut c_void,") + .endLine() + .endBlock(); + + // Implementation with opaque field accessors + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("impl ") + .append(typeName) + .endLine() + .beginBlock(); + + // Constructor + file.beginLine() + .append("pub fn new() -> Option<Self> {") + .endLine() + .extraIndent() + .beginLine() + .append("let ptr = unsafe { ") + .append(externFunctionPrefix) + .append("_constructor() };") + .endLine() + .beginLine() + .append("if ptr.is_null() {") + .endLine() + .extraIndent() + .beginLine() + .append("None") + .endLine() + .beginLine() + .append("} else {") + .endLine() + .extraIndent() + .beginLine() + .append("Some(Self { ptr })") + .endLine() + .beginLine() + .append("}") + .endLine() + .beginLine() + .append("}") + .endLine(); + + // from_ptr method for creating wrapper from existing pointer with C++ type casting + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("pub fn from_ptr(ptr: *mut c_void) -> Option<Self>") + .endLine() + .beginBlock() + .beginLine() + .append("let casted_ptr = unsafe { ") + .append(externFunctionPrefix) + .append("_from_ptr(ptr) };") + .endLine() + .beginLine() + .append("if casted_ptr.is_null()") + .endLine() + .beginBlock() + .beginLine() + .append("None") + .endLine() + .endBlock() + .beginLine() + .append("else") + .endLine() + .beginBlock() + .beginLine() + .append("Some(Self { ptr: casted_ptr })") + .endLine() + .endBlock() + .endBlock(); + + // Opaque field accessors + for (const auto& member : entity->getDirectMembers()) + { + OString memberName = u2b(member.name); // Use original case, not snake_case + + // Getter + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("pub fn get_") + .append(memberName) + .append("(&self) -> *mut c_void {") + .endLine() + .extraIndent() + .beginLine() + .append("unsafe { ") + .append(externFunctionPrefix) + .append("_get_") + .append(memberName) + .append("(self.ptr) }") + .endLine() + .beginLine() + .append("}") + .endLine(); + + // Setter + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("pub fn set_") + .append(memberName) + .append("(&mut self, value: *mut c_void) {") + .endLine() + .extraIndent() + .beginLine() + .append("unsafe { ") + .append(externFunctionPrefix) + .append("_set_") + .append(memberName) + .append("(self.ptr, value) }") + .endLine() + .beginLine() + .append("}") + .endLine(); + } + + file.endBlock(); + + // Drop implementation + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("impl Drop for ") + .append(typeName) + .endLine() + .beginBlock() + .beginLine() + .append("fn drop(&mut self) {") + .endLine() + .extraIndent() + .beginLine() + .append("if !self.ptr.is_null() {") + .endLine() + .extraIndent() + .beginLine() + .append("unsafe { ") + .append(externFunctionPrefix) + .append("_destroy(self.ptr) }") + .endLine() + .beginLine() + .append("}") + .endLine() + .beginLine() + .append("}") + .endLine() + .endBlock(); + + // Extern "C" declarations for opaque operations + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("unsafe extern \"C\" {") + .endLine() + .extraIndent() + .beginLine() + .append("fn ") + .append(externFunctionPrefix) + .append("_constructor() -> *mut c_void;") + .endLine() + .beginLine() + .append("fn ") + .append(externFunctionPrefix) + .append("_destroy(ptr: *mut c_void);") + .endLine() + .beginLine() + .append("fn ") + .append(externFunctionPrefix) + .append("_from_ptr(ptr: *mut c_void) -> *mut c_void;") + .endLine(); + + for (const auto& member : entity->getDirectMembers()) + { + OString memberName = u2b(member.name); // Use original case, not snake_case + file.beginLine() + .append("fn ") + .append(externFunctionPrefix) + .append("_get_") + .append(memberName) + .append("(ptr: *mut c_void) -> *mut c_void;") + .endLine() + .beginLine() + .append("fn ") + .append(externFunctionPrefix) + .append("_set_") + .append(memberName) + .append("(ptr: *mut c_void, value: *mut c_void);") + .endLine(); + } + + file.beginLine().append("}").endLine(); + + file.closeFile(); +} + +void RustProducer::produceInterface(std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity) +{ + RustFile file(m_outputDir, name); + + if (m_verbose) + std::cout << "[rust-opaque-interface] " << name << " -> " << file.getPath() << '\n'; + if (m_dryRun) + return; + + file.openFile(); + + OString typeName(splitName(name)); // Simple name for Rust interface + OString externFunctionPrefix = getRustTypeName(name); // Full name for extern "C" functions + + file.beginLine() + .append("/// Opaque Rust interface wrapper for ") + .append(name) + .endLine() + .beginLine() + .append("use std::ffi::c_void;") + .endLine() + .beginLine() + .append("#[allow(non_camel_case_types)]") + .endLine() + .beginLine() + .append("pub struct ") + .append(typeName) + .endLine() + .beginBlock() + .beginLine() + .append("ptr: *mut c_void,") + .endLine() + .endBlock(); + + // Implementation block + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("impl ") + .append(typeName) + .endLine() + .beginBlock(); + + // Constructor + file.beginLine() + .append("pub fn new() -> Option<Self> {") + .endLine() + .extraIndent() + .beginLine() + .append("let ptr = unsafe { ") + .append(externFunctionPrefix) + .append("_constructor() };") + .endLine() + .beginLine() + .append("if ptr.is_null() {") + .endLine() + .extraIndent() + .beginLine() + .append("None") + .endLine() + .beginLine() + .append("} else {") + .endLine() + .extraIndent() + .beginLine() + .append("Some(Self { ptr })") + .endLine() + .beginLine() + .append("}") + .endLine() + .beginLine() + .append("}") + .endLine(); + + // from_ptr method for creating wrapper from existing pointer with C++ type casting + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("pub fn from_ptr(ptr: *mut c_void) -> Option<Self>") + .endLine() + .beginBlock() + .beginLine() + .append("let casted_ptr = unsafe { ") + .append(externFunctionPrefix) + .append("_from_ptr(ptr) };") + .endLine() + .beginLine() + .append("if casted_ptr.is_null()") + .endLine() + .beginBlock() + .beginLine() + .append("None") + .endLine() + .endBlock() + .beginLine() + .append("else") + .endLine() + .beginBlock() + .beginLine() + .append("Some(Self { ptr: casted_ptr })") + .endLine() + .endBlock() + .endBlock(); + + // as_ptr method for getting the raw pointer + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("pub fn as_ptr(&self) -> *mut c_void") + .endLine() + .beginBlock() + .beginLine() + .append("self.ptr") + .endLine() + .endBlock(); + + // Validity check - only add if there's no conflicting isValid method + bool hasIsValidMethod = false; + for (const auto& method : entity->getDirectMethods()) + { + if (u2b(method.name).equalsIgnoreAsciiCase("isValid")) + { + hasIsValidMethod = true; + break; + } + } + + if (!hasIsValidMethod) + { + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("pub fn is_valid(&self) -> bool {") + .endLine() + .extraIndent() + .beginLine() + .append("unsafe { ") + .append(externFunctionPrefix) + .append("_is_valid(self.ptr) }") + .endLine() + .beginLine() + .append("}") + .endLine(); + } + + // Method wrappers - all methods return opaque pointers + for (const auto& method : entity->getDirectMethods()) + { + OString rustMethodName = getRustFunctionName(u2b(method.name)); + + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("pub fn ") + .append(rustMethodName) + .append("(&self"); + + // All parameters are opaque void pointers + for (const auto& param : method.parameters) + { + file.append(", ").append(param.name).append(": *mut c_void"); + } + + file.append(") -> ") + .append(getRustReturnType(method.returnType)) + .append(" {") + .endLine() + .extraIndent(); + + // Handle return value conversion based on return type + if (method.returnType == u"void") + { + // Void methods - just call the function with semicolon (no return) + file.beginLine() + .append("unsafe { ") + .append(externFunctionPrefix) + .append("_") + .append(method.name) + .append("(self.ptr"); + + for (const auto& param : method.parameters) + { + file.append(", ").append(param.name); + } + + file.append(") };").endLine(); + } + else + { + // Non-void methods - call function and convert result + file.beginLine() + .append("let ptr = unsafe { ") + .append(externFunctionPrefix) + .append("_") + .append(method.name) + .append("(self.ptr"); + + for (const auto& param : method.parameters) + { + file.append(", ").append(param.name); + } + + file.append(") };").endLine(); + + // Convert result based on return type + if (isUnoInterface(method.returnType) || isUnoStruct(method.returnType) + || isUnoEnum(method.returnType)) + { + // For interface/struct/enum returns - use service-style conversion + OString rustType = u2b(method.returnType); + if (rustType.indexOf('.') != -1) + { + sal_Int32 lastDot = rustType.lastIndexOf('.'); + if (lastDot != -1) + { + OString simpleName = rustType.copy(lastDot + 1); + OString modulePath = rustType.replaceAll("."_ostr, "::"_ostr); + + file.beginLine().append("if ptr.is_null() {").endLine(); + file.extraIndent().beginLine().append("None").endLine(); + file.beginLine().append("} else {").endLine(); + // Check if this is an enum type + if (isUnoEnum(method.returnType)) + { + // Enum types - cast from void* to i32 and use from_i32 + file.extraIndent() + .beginLine() + .append("Some(crate::generated::rustmaker::") + .append(modulePath) + .append("::") + .append(simpleName) + .append("::from_i32(unsafe { *(ptr as *const i32) }))") + .endLine(); + } + else + { + // Interface and struct types - use from_ptr + file.extraIndent() + .beginLine() + .append("crate::generated::rustmaker::") + .append(modulePath) + .append("::") + .append(simpleName) + .append("::from_ptr(ptr)") + .endLine(); + } + file.beginLine().append("}").endLine(); + } + } + } + else + { + // Convert raw pointer to wrapper type for other types + OString rustType = u2b(method.returnType); + + // Handle special types that need conversion + if (rustType == "string") + { + file.beginLine().append("if ptr.is_null() {").endLine(); + file.extraIndent().beginLine().append("None").endLine(); + file.beginLine().append("} else {").endLine(); + file.extraIndent() + .beginLine() + .append("Some(unsafe { crate::core::OUString::from_raw(ptr as *mut " + "crate::ffi::rtl_string::rtl_uString) })") + .endLine(); + file.beginLine().append("}").endLine(); + } + else if (rustType == "any" || rustType == "com.sun.star.uno.Any") + { + file.beginLine().append("if ptr.is_null() {").endLine(); + file.extraIndent().beginLine().append("None").endLine(); + file.beginLine().append("} else {").endLine(); + file.extraIndent() + .beginLine() + .append("Some(unsafe { crate::core::Any::from_raw(std::ptr::read(ptr as " + "*mut crate::ffi::uno_any::uno_Any)) })") + .endLine(); + file.beginLine().append("}").endLine(); + } + else + { + // Check for typedef resolution first + OString resolvedType = resolveTypedef(method.returnType); + if (resolvedType != rustType) + { + // It's a typedef - handle based on resolved type + if (resolvedType == "boolean") + { + file.beginLine().append("unsafe { *(ptr as *const u8) }").endLine(); + } + else if (resolvedType == "byte") + { + file.beginLine().append("unsafe { *(ptr as *const i8) }").endLine(); + } + else if (resolvedType == "short") + { + file.beginLine().append("unsafe { *(ptr as *const i16) }").endLine(); + } + else if (resolvedType == "unsigned short") + { + file.beginLine().append("unsafe { *(ptr as *const u16) }").endLine(); + } + else if (resolvedType == "long") + { + file.beginLine().append("unsafe { *(ptr as *const i32) }").endLine(); + } + else if (resolvedType == "unsigned long") + { + file.beginLine().append("unsafe { *(ptr as *const u32) }").endLine(); + } + else if (resolvedType == "hyper") + { + file.beginLine().append("unsafe { *(ptr as *const i64) }").endLine(); + } + else if (resolvedType == "unsigned hyper") + { + file.beginLine().append("unsafe { *(ptr as *const u64) }").endLine(); + } + else if (resolvedType == "float") + { + file.beginLine().append("unsafe { *(ptr as *const f32) }").endLine(); + } + else if (resolvedType == "double") + { + file.beginLine().append("unsafe { *(ptr as *const f64) }").endLine(); + } + else if (resolvedType == "char") + { + file.beginLine().append("unsafe { *(ptr as *const u16) }").endLine(); + } + else + { + // Typedef resolves to other types - return raw pointer + file.beginLine().append("ptr").endLine(); + } + } + else + { + // Not a typedef - check for primitive types that need dereferencing + if (rustType == "boolean") + { + file.beginLine().append("unsafe { *(ptr as *const u8) }").endLine(); + } + else if (rustType == "byte") + { + file.beginLine().append("unsafe { *(ptr as *const i8) }").endLine(); + } + else if (rustType == "short") + { + file.beginLine().append("unsafe { *(ptr as *const i16) }").endLine(); + } + else if (rustType == "unsigned short") + { + file.beginLine().append("unsafe { *(ptr as *const u16) }").endLine(); + } + else if (rustType == "long") + { + file.beginLine().append("unsafe { *(ptr as *const i32) }").endLine(); + } + else if (rustType == "unsigned long") + { + file.beginLine().append("unsafe { *(ptr as *const u32) }").endLine(); + } + else if (rustType == "hyper") + { + file.beginLine().append("unsafe { *(ptr as *const i64) }").endLine(); + } + else if (rustType == "unsigned hyper") + { + file.beginLine().append("unsafe { *(ptr as *const u64) }").endLine(); + } + else if (rustType == "float") + { + file.beginLine().append("unsafe { *(ptr as *const f32) }").endLine(); + } + else if (rustType == "double") + { + file.beginLine().append("unsafe { *(ptr as *const f64) }").endLine(); + } + else if (rustType == "char") + { + file.beginLine().append("unsafe { *(ptr as *const u16) }").endLine(); + } + else + { + // For other types (sequences, templates, etc), return the raw pointer + file.beginLine().append("ptr").endLine(); + } + } + } + } + } + + file.beginLine().append("}").endLine(); + } + + file.endBlock(); + + // Drop implementation for automatic cleanup + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("impl Drop for ") + .append(typeName) + .endLine() + .beginBlock() + .beginLine() + .append("fn drop(&mut self) {") + .endLine() + .extraIndent() + .beginLine() + .append("if !self.ptr.is_null() {") + .endLine() + .extraIndent() + .beginLine() + .append("unsafe { ") + .append(externFunctionPrefix) + .append("_destructor(self.ptr) }") + .endLine() + .beginLine() + .append("}") + .endLine() + .beginLine() + .append("}") + .endLine() + .endBlock(); + + // Thread safety markers + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("unsafe impl Send for ") + .append(typeName) + .append(" {}") + .endLine() + .beginLine() + .append("unsafe impl Sync for ") + .append(typeName) + .append(" {}") + .endLine(); + + // Extern "C" declarations - connects to C-side bridge + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("unsafe extern \"C\" {") + .endLine() + .extraIndent() + .beginLine() + .append("fn ") + .append(externFunctionPrefix) + .append("_constructor() -> *mut c_void;") + .endLine() + .beginLine() + .append("fn ") + .append(externFunctionPrefix) + .append("_destructor(ptr: *mut c_void);") + .endLine() + .beginLine() + .append("fn ") + .append(externFunctionPrefix) + .append("_from_ptr(ptr: *mut c_void) -> *mut c_void;") + .endLine(); + + // Only declare _is_valid if we don't have a conflicting isValid method + if (!hasIsValidMethod) + { + file.beginLine() + .append("fn ") + .append(externFunctionPrefix) + .append("_is_valid(ptr: *mut c_void) -> bool;") + .endLine(); + } + + for (const auto& method : entity->getDirectMethods()) + { + file.beginLine() + .append("fn ") + .append(externFunctionPrefix) + .append("_") + .append(method.name) + .append("(ptr: *mut c_void"); + + for (const auto& param : method.parameters) + { + file.append(", ").append(param.name).append(": *mut c_void"); + } + + file.append(") -> "); + + if (method.returnType == u"void") + { + file.append("()"); + } + else + { + file.append("*mut c_void"); + } + + file.append(";").endLine(); + } + + file.beginLine().append("}").endLine(); + + file.closeFile(); +} + +void RustProducer::produceService( + std::string_view name, const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& entity) +{ + RustFile file(m_outputDir, name); + + if (m_verbose) + std::cout << "[rust-opaque-service] " << name << " -> " << file.getPath() << '\n'; + if (m_dryRun) + return; + + file.openFile(); + + OString serviceName(splitName(name)); // Simple name for Rust service + OString externFunctionPrefix = getRustTypeName(name); // Full name for extern "C" functions + OUString interfaceType = entity->getBase(); + + // Generate proper module path for the interface + OString interfaceTypeStr = u2b(interfaceType); + std::string_view interfaceTypeName = splitName(interfaceTypeStr); + + // Convert interface type to full module path + OString path = interfaceTypeStr; + path = path.replaceAll("."_ostr, "::"_ostr); + OString interfaceModulePath + = "crate::generated::rustmaker::"_ostr + path + "::" + interfaceTypeName; + + file.beginLine() + .append("/// Opaque Rust service wrapper for ") + .append(name) + .endLine() + .beginLine() + .append("use std::ffi::c_void;") + .endLine() + .beginLine() + .append("#[allow(non_camel_case_types)]") + .endLine() + .append("") + .endLine() + .beginLine() + .append("pub struct ") + .append(serviceName) + .append(";") + .endLine(); + + // Implementation block + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("impl ") + .append(serviceName) + .endLine() + .beginBlock(); + + // Service creation method + file.beginLine() + .append("/// Create a new instance of ") + .append(name) + .endLine() + .beginLine() + .append("pub fn create(context: *mut c_void) -> Option<") + .append(interfaceModulePath) + .append("> ") + .endLine() + .beginBlock() + .beginLine() + .append("let ptr = unsafe { ") + .append(externFunctionPrefix) + .append("_create(context) };") + .endLine() + .beginLine() + .append("if ptr.is_null() ") + .endLine() + .beginBlock() + .beginLine() + .append("None") + .endLine() + .endBlock() + .beginLine() + .append(" else ") + .endLine() + .beginBlock() + .beginLine() + .append(interfaceModulePath) + .append("::from_ptr(ptr)") + .endLine() + .endBlock() + .endBlock(); + + file.endBlock(); + + // Extern "C" declarations + file.beginLine() + .append("") + .endLine() + .beginLine() + .append("unsafe extern \"C\" ") + .endLine() + .beginBlock() + .beginLine() + .append("fn ") + .append(externFunctionPrefix) + .append("_create(context: *mut c_void) -> *mut c_void;") + .endLine() + .endBlock(); + + file.closeFile(); +} + +// Helper functions +std::string_view RustProducer::splitName(std::string_view name) +{ + size_t split = name.find_last_of(".::"); + if (split != std::string_view::npos) + return name.substr(split + 1); + else + return name; +} + +OString RustProducer::getRustFunctionName(std::string_view methodName) +{ + // Keep original UNO method name (no snake_case conversion) + std::string result(methodName); + + // Handle Rust reserved keywords by prefixing with r# + if (result == "move" || result == "type" || result == "ref" || result == "mut" + || result == "impl" || result == "fn" || result == "let" || result == "const" + || result == "static" || result == "match" || result == "if" || result == "else" + || result == "for" || result == "while" || result == "loop" || result == "break" + || result == "continue" || result == "return" || result == "self" || result == "Self" + || result == "super" || result == "crate" || result == "mod" || result == "pub" + || result == "use" || result == "extern" || result == "struct" || result == "enum" + || result == "trait" || result == "async" || result == "await" || result == "where" + || result == "unsafe" || result == "dyn" || result == "true" || result == "false") + { + result = "r#" + result; + } + + return OString(result.c_str()); +} + +OString RustProducer::getRustTypeName(std::string_view unoName) +{ + // Convert com.sun.star.lang.XMain to com__sun__star__lang__XMain + OString result(unoName); + result = result.replaceAll("."_ostr, "__"_ostr); + return result; +} + +OString RustProducer::getRustWrapperTypeName(std::u16string_view unoType) const +{ + OString rustType = u2b(unoType); + + // Handle void returns + if (rustType == "void") + { + return "()"_ostr; + } + + // Handle primitive types + if (rustType == "boolean") + return "u8"; + if (rustType == "byte") + return "i8"; + if (rustType == "short") + return "i16"; + if (rustType == "unsigned short") + return "u16"; + if (rustType == "long") + return "i32"; + if (rustType == "unsigned long") + return "u32"; + if (rustType == "hyper") + return "i64"; + if (rustType == "unsigned hyper") + return "u64"; + if (rustType == "float") + return "f32"; + if (rustType == "double") + return "f64"; + if (rustType == "char") + return "u16"; + + // Handle special UNO types + if (rustType == "string") + return "Option<crate::core::OUString>"; + if (rustType == "any" || rustType == "com.sun.star.uno.Any") + return "Option<crate::core::Any>"; + + // Handle sequence types (arrays) - return raw pointer for now + if (rustType.startsWith("[]")) + { + return "*mut c_void"; + } + + // Handle template types like Pair<T,U> - return raw pointer for now to avoid parsing issues + if (rustType.indexOf('<') != -1 && rustType.indexOf('>') != -1) + { + return "*mut c_void"; + } + + // Handle generated UNO types (interface, struct, enum) + if (rustType.indexOf('.') != -1) + { + // Skip types that look malformed or have invalid characters + if (rustType.indexOf('[') != -1 || rustType.indexOf('<') != -1) + { + return "*mut c_void"; + } + + // First resolve typedefs + OString resolvedType = resolveTypedef(unoType); + if (resolvedType != rustType) + { + // It's a typedef - recursively get wrapper type for resolved type + return getRustWrapperTypeName(b2u(resolvedType)); + } + + // Not a typedef - convert dots to :: for module path + OString modulePath = rustType.replaceAll("."_ostr, "::"_ostr); + + // Extract simple type name + sal_Int32 lastDot = rustType.lastIndexOf('.'); + if (lastDot != -1) + { + OString simpleName = rustType.copy(lastDot + 1); + return "Option<crate::generated::rustmaker::" + modulePath + "::" + simpleName + ">"; + } + } + + // Default to raw pointer for unknown types + return "*mut c_void"; +} + +OString RustProducer::resolveTypedef(std::u16string_view unoType) const +{ + // Recursively resolve typedefs to underlying types + rtl::Reference<unoidl::Entity> entity; + codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(unoType), &entity); + + if (sort == codemaker::UnoType::Sort::Typedef) + { + rtl::Reference<unoidl::TypedefEntity> typedefEntity( + static_cast<unoidl::TypedefEntity*>(entity.get())); + if (typedefEntity.is()) + { + // Recursively resolve typedefs + return resolveTypedef(typedefEntity->getType()); + } + } + + // Return original type if not a typedef + return u2b(unoType); +} + +OString RustProducer::getRustReturnType(std::u16string_view unoType) const +{ + // Handle void returns + if (unoType == u"void") + { + return "()"; + } + + // For interfaces, structs, enums - return Option<TypeWrapper> like services + if (isUnoInterface(unoType) || isUnoStruct(unoType) || isUnoEnum(unoType)) + { + OString rustType = u2b(unoType); + if (rustType.indexOf('.') != -1) + { + // Convert dots to :: for module path + OString modulePath = rustType.replaceAll(".", "::"); + + // Extract simple type name + sal_Int32 lastDot = rustType.lastIndexOf('.'); + if (lastDot != -1) + { + OString simpleName = rustType.copy(lastDot + 1); + return "Option<crate::generated::rustmaker::" + modulePath + "::" + simpleName + + ">"; + } + } + } + + // For primitive and other types, use the existing logic + return getRustWrapperTypeName(unoType); +} + +OString RustProducer::getRustFFIReturnType(std::u16string_view unoType) const +{ + // Handle void returns + if (unoType == u"void") + { + return "()"; + } + + // For interfaces, structs, enums - return their opaque handle typedef + if (isUnoInterface(unoType) || isUnoStruct(unoType) || isUnoEnum(unoType)) + { + // Convert UNO type name to typedef handle name + // com.sun.star.lang.XMain -> com__sun__star__lang__XMainHandle + OString functionPrefix = getRustTypeName(std::string(unoType.begin(), unoType.end())); + return functionPrefix + "Handle"; + } + + // For other types (primitives, sequences, etc) - use void* + return "*mut c_void"; +} + +bool RustProducer::isUnoInterface(std::u16string_view typeName) const +{ + rtl::Reference<unoidl::Entity> entity; + rtl::Reference<unoidl::MapCursor> cursor; + codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(typeName), &entity, &cursor); + return sort == codemaker::UnoType::Sort::Interface; +} + +bool RustProducer::isUnoStruct(std::u16string_view typeName) const +{ + rtl::Reference<unoidl::Entity> entity; + rtl::Reference<unoidl::MapCursor> cursor; + codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(typeName), &entity, &cursor); + return sort == codemaker::UnoType::Sort::PlainStruct; +} + +bool RustProducer::isUnoEnum(std::u16string_view typeName) const +{ + rtl::Reference<unoidl::Entity> entity; + rtl::Reference<unoidl::MapCursor> cursor; + codemaker::UnoType::Sort sort = m_typeManager->getSort(OUString(typeName), &entity, &cursor); + return sort == codemaker::UnoType::Sort::Enum; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/rustmaker/rustproduce.hxx b/codemaker/source/rustmaker/rustproduce.hxx new file mode 100644 index 000000000000..3c2d0fb2cbfe --- /dev/null +++ b/codemaker/source/rustmaker/rustproduce.hxx @@ -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/. + */ + +#pragma once + +#include <string_view> +#include <unoidl/unoidl.hxx> +#include <codemaker/typemanager.hxx> +#include <rtl/string.hxx> + +/** + * RustProducer generates Rust-side opaque pointer wrappers. + * + * This is the Rust-side generator in the opaque pointer approach. + * It creates Rust structs that use void* pointers and call extern "C" + * bridge functions generated by the C-side (CppProducer). + * + * Architecture: + * - Rust structs with opaque void* pointers + * - Safe RAII wrappers with Drop trait + * - extern "C" function declarations matching C-side bridge + * - All parameters/returns are void* for maximum simplicity + */ +class RustProducer +{ +public: + /** + * Constructor + * @param outputDir Directory for generated Rust files + * @param verbose Enable verbose output + * @param dryRun Don't actually write files + * @param typeManager UNO type manager for accessing type information + */ + RustProducer(const OString& outputDir, bool verbose, bool dryRun, + const rtl::Reference<TypeManager>& typeManager); + + /** + * Generate Rust opaque enum wrapper + * Creates simple enum with conversion functions via extern "C" + */ + void produceEnum(std::string_view name, const rtl::Reference<unoidl::EnumTypeEntity>& entity); + + /** + * Generate Rust opaque struct wrapper + * Creates struct with void* pointer and field accessor methods + */ + void produceStruct(std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity); + + /** + * Generate Rust opaque interface wrapper + * Creates struct with void* pointer and method wrappers + */ + void produceInterface(std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity); + + /** + * Generate Rust opaque service wrapper + * Creates service creation wrapper methods + */ + void produceService(std::string_view name, + const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& entity); + +private: + // Configuration + OString m_outputDir; + bool m_verbose; + bool m_dryRun; + rtl::Reference<TypeManager> m_typeManager; + + // Helper functions + static std::string_view splitName(std::string_view name); + static OString getRustFunctionName(std::string_view methodName); + static OString getRustTypeName(std::string_view unoName); + OString getRustWrapperTypeName(std::u16string_view unoType) const; + OString getRustReturnType(std::u16string_view unoType) const; + OString getRustFFIReturnType(std::u16string_view unoType) const; + OString resolveTypedef(std::u16string_view unoType) const; + + // Type classification helpers + bool isUnoInterface(std::u16string_view typeName) const; + bool isUnoStruct(std::u16string_view typeName) const; + bool isUnoEnum(std::u16string_view typeName) const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/rustmaker/type_analyzer.cxx b/codemaker/source/rustmaker/type_analyzer.cxx new file mode 100644 index 000000000000..75eb4323e89e --- /dev/null +++ b/codemaker/source/rustmaker/type_analyzer.cxx @@ -0,0 +1,188 @@ +/* -*- 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 "type_analyzer.hxx" +#include <codemaker/unotype.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <unoidl/unoidl.hxx> +#include <vector> + +TypeAnalyzer::TypeAnalyzer(const rtl::Reference<TypeManager>& typeManager) + : m_typeManager(typeManager) +{ +} + +TypeAnalyzer::TypeInfo +TypeAnalyzer::analyzeInterface(const rtl::Reference<unoidl::InterfaceTypeEntity>& entity) +{ + TypeInfo typeInfo; + + // Always need XInterface for base type operations + typeInfo.needsUnoInterface = true; + typeInfo.namespaces.insert("com::sun::star::uno"_ostr); + + // Analyze all methods in the interface + for (const auto& method : entity->getDirectMethods()) + { + // Analyze return type + if (method.returnType != u"void") + { + analyzeType(typeInfo, method.returnType); + } + + // Analyze parameter types + for (const auto& param : method.parameters) + { + analyzeType(typeInfo, param.type); + } + } + + return typeInfo; +} + +void TypeAnalyzer::analyzeType(TypeInfo& typeInfo, std::u16string_view unoType) +{ + // Use cppumaker's approach: recursively insert dependencies + insertDependency(typeInfo, unoType); +} + +void TypeAnalyzer::insertDependency(TypeInfo& typeInfo, std::u16string_view unoType) +{ + // Decompose sequence/template types like cppumaker does + sal_Int32 sequenceDepth; + std::vector<OString> templateArgs; + OString baseType = codemaker::UnoType::decompose(u2b(unoType), &sequenceDepth, &templateArgs); + OUString baseTypeU = b2u(baseType); + + if (sequenceDepth != 0) + { + typeInfo.needsSequence = true; + } + + // Get the type sort to determine how to handle it + rtl::Reference<unoidl::Entity> entity; + rtl::Reference<unoidl::MapCursor> cursor; + codemaker::UnoType::Sort sort = m_typeManager->getSort(baseTypeU, &entity, &cursor); + + switch (sort) + { + case codemaker::UnoType::Sort::Void: + // No special handling needed + break; + case codemaker::UnoType::Sort::Boolean: + case codemaker::UnoType::Sort::Byte: + case codemaker::UnoType::Sort::Short: + case codemaker::UnoType::Sort::UnsignedShort: + case codemaker::UnoType::Sort::Long: + case codemaker::UnoType::Sort::UnsignedLong: + case codemaker::UnoType::Sort::Hyper: + case codemaker::UnoType::Sort::UnsignedHyper: + case codemaker::UnoType::Sort::Float: + case codemaker::UnoType::Sort::Double: + case codemaker::UnoType::Sort::Char: + typeInfo.needsSalTypes = true; + break; + case codemaker::UnoType::Sort::String: + typeInfo.needsString = true; + typeInfo.needsRtlUstring = true; + break; + case codemaker::UnoType::Sort::Type: + typeInfo.needsUnoInterface = true; // Type needs XInterface includes + break; + case codemaker::UnoType::Sort::Any: + typeInfo.needsAny = true; + break; + case codemaker::UnoType::Sort::PlainStruct: + { + // Add this struct to dependencies and recursively analyze its members + typeInfo.structTypes.insert(baseType); + + rtl::Reference<unoidl::PlainStructTypeEntity> structEntity( + static_cast<unoidl::PlainStructTypeEntity*>(entity.get())); + if (structEntity.is()) + { + // Recursively analyze base struct + if (!structEntity->getDirectBase().isEmpty()) + { + insertDependency(typeInfo, structEntity->getDirectBase()); + } + + // Recursively analyze member types + for (const auto& member : structEntity->getDirectMembers()) + { + insertDependency(typeInfo, member.type); + } + } + break; + } + case codemaker::UnoType::Sort::Enum: + typeInfo.enumTypes.insert(baseType); + typeInfo.needsEnum = true; + typeInfo.needsSalTypes = true; + break; + case codemaker::UnoType::Sort::Interface: + typeInfo.interfaceTypes.insert(baseType); + typeInfo.needsUnoInterface = true; + typeInfo.namespaces.insert("com::sun::star::uno"_ostr); + break; + case codemaker::UnoType::Sort::ConstantGroup: + typeInfo.constantGroupTypes.insert(baseType); + typeInfo.needsConstantGroup = true; + break; + case codemaker::UnoType::Sort::PolymorphicStructTemplate: + { + // Handle template arguments recursively + for (const OString& arg : templateArgs) + { + insertDependency(typeInfo, b2u(arg)); + } + // Note: Don't add polymorphic struct templates to structTypes + // since they don't have concrete header files to include + break; + } + default: + // For unknown types, ensure basic includes + typeInfo.needsUnoInterface = true; + break; + } + + // Extract namespace from UNO type name (for any valid UNO type) + if (sort != codemaker::UnoType::Sort::Void && sort != codemaker::UnoType::Sort::Boolean + && sort != codemaker::UnoType::Sort::Byte && sort != codemaker::UnoType::Sort::Short + && sort != codemaker::UnoType::Sort::UnsignedShort && sort != codemaker::UnoType::Sort::Long + && sort != codemaker::UnoType::Sort::UnsignedLong && sort != codemaker::UnoType::Sort::Hyper + && sort != codemaker::UnoType::Sort::UnsignedHyper + && sort != codemaker::UnoType::Sort::Float && sort != codemaker::UnoType::Sort::Double + && sort != codemaker::UnoType::Sort::Char && sort != codemaker::UnoType::Sort::String + && sort != codemaker::UnoType::Sort::Type && sort != codemaker::UnoType::Sort::Any + && baseType.indexOf('.') != sal_Int32(std::string::npos)) + { + typeInfo.namespaces.insert(extractNamespace(baseTypeU)); + } +} + +OString TypeAnalyzer::extractNamespace(std::u16string_view unoType) +{ + OString typeStr = u2b(unoType); + + // Convert dots to double colons for C++ namespace + OString cppNamespace = typeStr.replaceAll("."_ostr, "::"_ostr); + + // Remove the last component (the actual type name) to get just the namespace + sal_Int32 lastColon = cppNamespace.lastIndexOf("::"); + if (lastColon != -1) + { + return cppNamespace.copy(0, lastColon); + } + + return cppNamespace; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/rustmaker/type_analyzer.hxx b/codemaker/source/rustmaker/type_analyzer.hxx new file mode 100644 index 000000000000..e0b9f177e51c --- /dev/null +++ b/codemaker/source/rustmaker/type_analyzer.hxx @@ -0,0 +1,94 @@ +/* -*- 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/. + */ + +#pragma once + +#include <set> +#include <string> +#include <unoidl/unoidl.hxx> +#include <codemaker/typemanager.hxx> +#include <rtl/string.hxx> + +/** + * Analyzes UNO interface entities to identify type dependencies for dynamic include generation. + */ +class TypeAnalyzer +{ +public: + TypeAnalyzer(const rtl::Reference<TypeManager>& typeManager); + +private: + rtl::Reference<TypeManager> m_typeManager; + +public: + /** + * Container for type analysis results. + */ + struct TypeInfo + { + std::set<OString> interfaceTypes; // UNO interface types (e.g., "com.sun.star.text.XText") + std::set<OString> structTypes; // UNO struct types (e.g., "com.sun.star.beans.Property") + std::set<OString> enumTypes; // UNO enum types (e.g., "com.sun.star.uno.TypeClass") + std::set<OString> + constantGroupTypes; // UNO constant groups (e.g., "com.sun.star.awt.FontWeight") + std::set<OString> namespaces; // Required namespaces (e.g., "com::sun::star::text") + + // Core UNO types - implemented first: Any + bool needsAny = false; + + // Core UNO types - TODO: implement in future + bool needsString = false; // TODO: implement string type handling + bool needsSequence = false; // TODO: implement sequence type handling + bool needsPropertyValue = false; // TODO: implement PropertyValue handling + + // Basic types + bool needsSalTypes = false; // For sal_Int32, sal_Bool, etc. + bool needsRtlUstring = false; // For rtl_uString* + bool needsUnoInterface = false; // For XInterface base type + bool needsEnum = false; // For enum type handling + bool needsConstantGroup = false; // For constant group handling + }; + + /** + * Analyzes the given interface entity and returns type dependency information. + * + * @param entity The interface entity to analyze. + * @return TypeInfo containing all identified dependencies. + */ + TypeInfo analyzeInterface(const rtl::Reference<unoidl::InterfaceTypeEntity>& entity); + + /** + * Analyzes a single UNO type string and updates the TypeInfo accordingly. + * + * @param typeInfo The TypeInfo to update. + * @param unoType The UNO type string to analyze (e.g., "com.sun.star.uno.Any"). + */ + void analyzeType(TypeInfo& typeInfo, std::u16string_view unoType); + +private: + /** + * Recursively inserts type dependencies following cppumaker's pattern. + * This method handles decomposition of sequences and templates, and recursively + * analyzes struct members and base types. + * + * @param typeInfo The TypeInfo to update with dependencies. + * @param unoType The UNO type to analyze recursively. + */ + void insertDependency(TypeInfo& typeInfo, std::u16string_view unoType); + + /** + * Extracts namespace from a UNO type name. + * + * @param unoType The UNO type name (e.g., "com.sun.star.text.XText"). + * @return The corresponding C++ namespace (e.g., "com::sun::star::text"). + */ + static OString extractNamespace(std::u16string_view unoType); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/rustmaker/unoproduce.cxx b/codemaker/source/rustmaker/unoproduce.cxx new file mode 100644 index 000000000000..ec873466a012 --- /dev/null +++ b/codemaker/source/rustmaker/unoproduce.cxx @@ -0,0 +1,376 @@ +/* -*- 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 <iostream> +#include <set> +#include <memory> + +#include <o3tl/string_view.hxx> +#include <string_view> + +#include "unoproduce.hxx" +#include "rustproduce.hxx" +#include "cpproduce.hxx" +#include <rtl/strbuf.hxx> +#include <rtl/string.hxx> +#include <rtl/ustring.hxx> +#include "rustfile.hxx" +#include <unoidl/unoidl.hxx> +#include <codemaker/unotype.hxx> + +const std::unordered_set<std::string_view> UnoProducer::m_reservedKeywords + // Rust keywords that need special handling to avoid naming conflicts + = { "as", "break", "const", "continue", "crate", "else", "enum", "extern", + "false", "fn", "for", "if", "impl", "in", "let", "loop", + "match", "mod", "move", "mut", "pub", "ref", "return", "Result", + "self", "Self", "static", "struct", "super", "trait", "true", "type", + "unsafe", "use", "where", "while", "async", "await", "dyn", "try" }; + +const std::unordered_map<std::string_view, OString> UnoProducer::m_baseTypes + // Maps UNO basic types to their Rust equivalents + // TODO: need to edit the core types Implemented manauly + = { { "boolean", "bool"_ostr }, + { "char", "char"_ostr }, + { "byte", "i8"_ostr }, + { "short", "i16"_ostr }, + { "unsigned short", "u16"_ostr }, + { "long", "i32"_ostr }, + { "unsigned long", "u32"_ostr }, + { "hyper", "i64"_ostr }, + { "unsigned hyper", "u64"_ostr }, + { "float", "f32"_ostr }, + { "double", "f64"_ostr }, + { "string", "String"_ostr }, + { "void", "()"_ostr }, + { "type", "uno::type"_ostr }, // TODO: + { "any", "uno::any"_ostr }, // TODO: + // TODO: These exception types need proper implementation + { "com.sun.star.uno.Exception", "com::sun::star::uno::Exception"_ostr } }; +// Note: XInterface removed from baseTypes so it can be generated like other interfaces + +std::string_view UnoProducer::splitName(std::string_view name) +{ + size_t split = name.find_last_of(".::"); + if (split != std::string_view::npos) + return name.substr(split + 1); + else + return name; +} + +OString UnoProducer::handleName(std::string_view name, bool istype) +{ + // Convert UNO dotted names to Rust double-colon syntax + OString temp(name); + OString result = temp.replaceAll("."_ostr, "::"_ostr); + temp = result; + if (istype) + result = "crate::"_ostr + temp + "::" + splitName(name); + return result; +} + +OString UnoProducer::handleName(std::u16string_view name, bool istype) +{ + return handleName(u2b(name), istype); +} + +OString UnoProducer::getBaseUnoName(std::string_view name) +{ + // Extract base type name by removing array brackets and template parameters + size_t start = name.find_first_not_of("[]"); + if (start == std::string_view::npos) + start = 0; + + size_t end = name.find_first_of('<'); + if (end == std::string_view::npos) + end = name.size(); + + return OString(name.substr(start, end - start)); +} +OString UnoProducer::getBaseUnoName(std::u16string_view name) { return getBaseUnoName(u2b(name)); } + +OString UnoProducer::getSafeIdentifier(std::string_view name, bool istype = false) +{ + // Add underscore suffix to avoid Rust keyword conflicts + OString temp = handleName(name, istype); + return m_reservedKeywords.contains(temp) ? temp + "_"_ostr : temp; +} + +OString UnoProducer::getSafeIdentifier(std::u16string_view name, bool istype = false) +{ + return getSafeIdentifier(u2b(name), istype); +} + +void UnoProducer::separatedForeach(const auto& items, auto&& sepFunc, auto&& itemFunc) +{ + for (auto it = items.begin(); it != items.end(); ++it) + { + if (it != items.begin()) + sepFunc(); + itemFunc(*it); + } +} + +void UnoProducer::initProducer(const RustOptions& options) +{ + m_manager = rtl::Reference<TypeManager>(new TypeManager()); + + // Load type providers from input files (following old approach pattern) + for (const OString& file : options.getInputFiles()) + m_manager->loadProvider(convertToFileUrl(file), true); + for (const OString& file : options.getExtraInputFiles()) + m_manager->loadProvider(convertToFileUrl(file), false); + + // Get separate output directories for C++ and Rust files + OString cppOutputDir = options.getOption("--cpp-output-dir"_ostr); + m_rustOutputDir = options.getOption("--rust-output-dir"_ostr); + // Enable dry-run mode (don't actually write files) + m_dryRun = options.isValid("--dry-run"_ostr); + // Enable verbose output for debugging + m_verbose = options.isValid("--verbose"_ostr); + + // Initialize both producers with separate output directories + m_rustProducer + = std::make_unique<RustProducer>(m_rustOutputDir, m_verbose, m_dryRun, m_manager); + m_cppProducer = std::make_unique<CppProducer>(cppOutputDir, m_verbose, m_dryRun, m_manager); + + // Initialize combined C++ output file + m_cppProducer->initializeCombinedFile(); + + // Parse type selection criteria (following old approach exactly) + if (options.isValid("--types"_ostr)) + { + const OString& names(options.getOption("--types"_ostr)); + // Process semicolon-separated list of type names/patterns + for (size_t i = 0; i != std::string_view::npos;) + { + std::string_view name(o3tl::getToken(names, ';', i)); + if (name == "*") + // "*" means generate all types + m_startingTypes.insert(""_ostr); + else if (name.ends_with(".*")) + // "namespace.*" means generate all types in namespace + m_startingTypes.emplace(name.substr(0, name.size() - 2)); + else + // Specific type name + m_startingTypes.emplace(name); + } + } + else + { + // Default: generate all types + m_startingTypes.insert(""_ostr); + } +} + +void UnoProducer::produceAll() +{ + for (const OString& name : m_startingTypes) + produceType(name); +} + +void UnoProducer::finalizeGeneration() +{ + // Finalize combined C++ output file + if (m_cppProducer) + { + m_cppProducer->finalizeCombinedFile(); + } + + // Always finalize mod.rs files in opaque mode + if (!m_dryRun) + { + if (m_verbose) + std::cout << "Finalizing mod.rs files with complete module information...\n"; + + RustFile::finalizeModFiles(std::filesystem::path(m_rustOutputDir.getStr())); + + if (m_verbose) + std::cout << "Module finalization complete\n"; + } +} + +void UnoProducer::produceType(const OString& name) +{ + // Skip already processed types to avoid duplicates + if (m_typesProduced.contains(name)) + return; + + m_typesProduced.insert(name); + + // Skip built-in types that don't need code generation + if (m_baseTypes.contains(name)) + return; + + OUString uname(b2u(name)); + + rtl::Reference<unoidl::Entity> entity; + rtl::Reference<unoidl::MapCursor> cursor; + + // Only generate code for types from primary providers (not dependencies) + if (m_manager->foundAtPrimaryProvider(uname)) + { + // Dispatch to appropriate generator based on UNO type kind + switch (m_manager->getSort(uname, &entity, &cursor)) + { + case codemaker::UnoType::Sort::Module: + produceModule(name, cursor); + break; + + case codemaker::UnoType::Sort::Enum: + produceEnum(name, dynamic_cast<unoidl::EnumTypeEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::PlainStruct: + produceStruct(name, dynamic_cast<unoidl::PlainStructTypeEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::PolymorphicStructTemplate: + producePolyStruct( + name, dynamic_cast<unoidl::PolymorphicStructTypeTemplateEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::Exception: + produceException(name, dynamic_cast<unoidl::ExceptionTypeEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::Interface: + produceInterface(name, dynamic_cast<unoidl::InterfaceTypeEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::Typedef: + produceTypedef(name, dynamic_cast<unoidl::TypedefEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::ConstantGroup: + produceConstantGroup(name, + dynamic_cast<unoidl::ConstantGroupEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::SingleInterfaceBasedService: + produceService( + name, dynamic_cast<unoidl::SingleInterfaceBasedServiceEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::InterfaceBasedSingleton: + produceSingleton( + name, dynamic_cast<unoidl::InterfaceBasedSingletonEntity*>(entity.get())); + break; + + case codemaker::UnoType::Sort::AccumulationBasedService: + case codemaker::UnoType::Sort::ServiceBasedSingleton: + // old-style services and singletons not supported + break; + + default: + throw CannotDumpException(u"entity '"_ustr + uname + u"' has unexpected type"_ustr); + } + } + else + { + // type from --extra-types + switch (m_manager->getSort(uname, &entity, &cursor)) + { + case codemaker::UnoType::Sort::Typedef: + produceTypedef(name, dynamic_cast<unoidl::TypedefEntity*>(entity.get())); + break; + + default: + break; + } + } +} + +void UnoProducer::produceModule(std::string_view name, + const rtl::Reference<unoidl::MapCursor>& cursor) +{ + OUString moduleName; + while (cursor->getNext(&moduleName).is()) + { + OString memberName = name.empty() ? u2b(moduleName) : name + "."_ostr + u2b(moduleName); + produceType(memberName); + } +} +void UnoProducer::produceEnum(std::string_view name, + const rtl::Reference<unoidl::EnumTypeEntity>& entity) +{ + // Coordinate both producers for complete opaque enum generation + m_cppProducer->produceEnum(name, entity); // C++ side: bridge functions + m_rustProducer->produceEnum(name, entity); // Rust side: wrapper with extern declarations +} + +void UnoProducer::produceStruct(std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity) +{ + // Coordinate both producers for complete struct generation + m_cppProducer->produceStruct(name, entity); // C++ side: wrapper class + bridge functions + m_rustProducer->produceStruct(name, entity); // Rust side: wrapper with extern declarations +} + +void UnoProducer::producePolyStruct( + std::string_view name, const rtl::Reference<unoidl::PolymorphicStructTypeTemplateEntity>&) +{ + // TODO: Implement polymorphic struct support + if (m_verbose) + std::cout << "[poly-struct] " << name << " -> skipping polymorphic structs for now\n"; +} + +void UnoProducer::produceException(std::string_view name, + const rtl::Reference<unoidl::ExceptionTypeEntity>&) +{ + // TODO: Handle exceptions as special structs + if (m_verbose) + std::cout << "[exception] " << name << " -> treating as struct\n"; +} + +void UnoProducer::produceInterface(std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity) +{ + // Coordinate both producers for complete interface generation + m_cppProducer->produceInterface(name, entity); // C++ side: wrapper class + bridge functions + m_rustProducer->produceInterface(name, entity); // Rust side: wrapper with extern declarations +} + +void UnoProducer::produceTypedef(std::string_view name, + const rtl::Reference<unoidl::TypedefEntity>&) +{ + // TODO: Handle typedefs properly + if (m_verbose) + std::cout << "[typedef] " << name << " -> skipping typedef resolution\n"; +} + +void UnoProducer::produceConstantGroup(std::string_view name, + const rtl::Reference<unoidl::ConstantGroupEntity>&) +{ + // TODO: Handle constant groups + if (m_verbose) + std::cout << "[constant-group] " << name << " -> skipping constants for now\n"; +} + +void UnoProducer::produceService( + std::string_view name, const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& entity) +{ + // Generate service creation wrappers that access services through their interfaces + if (m_verbose) + std::cout << "[service] " << name << " -> generating service creation methods\n"; + + // Coordinate both producers for complete service generation + m_cppProducer->produceService(name, entity); // C++ side: service creation bridge functions + m_rustProducer->produceService(name, + entity); // Rust side: service wrapper with creation methods +} + +void UnoProducer::produceSingleton(std::string_view name, + const rtl::Reference<unoidl::InterfaceBasedSingletonEntity>&) +{ + // Singletons are accessed through their interfaces + if (m_verbose) + std::cout << "[singleton] " << name << " -> use underlying interface instead\n"; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/codemaker/source/rustmaker/unoproduce.hxx b/codemaker/source/rustmaker/unoproduce.hxx new file mode 100644 index 000000000000..61671c984643 --- /dev/null +++ b/codemaker/source/rustmaker/unoproduce.hxx @@ -0,0 +1,109 @@ +/* -*- 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/. + */ + +#pragma once + +#include <string_view> +#include <unordered_set> +#include <unordered_map> +#include <memory> + +#include <codemaker/typemanager.hxx> +#include <unoidl/unoidl.hxx> + +#include <rtl/string.hxx> +#include "rustoptions.hxx" +#include "rustfile.hxx" +#include "rustproduce.hxx" +#include "cpproduce.hxx" + +// Forward declarations +class CppFile; + +/** + * Main class responsible for generating Rust code from UNO type definitions. + * Processes UNO IDL types and produces corresponding Rust bindings. + */ +class UnoProducer +{ +public: + UnoProducer() + : m_manager(new TypeManager()) + { + } + +public: + // Initialize the producer with command line options + void initProducer(const RustOptions& options); + // Generate code for all requested types + void produceAll(); + // Finalize opaque pointer generation + void finalizeGeneration(); + + // Static lookup tables for Rust keyword conflicts and type mappings + static const std::unordered_set<std::string_view> m_reservedKeywords; + static const std::unordered_map<std::string_view, OString> m_baseTypes; + +private: + // Main UNO type processor - coordinates opaque generation for all types + void produceType(const OString& name); + void produceModule(std::string_view name, const rtl::Reference<unoidl::MapCursor>& cursor); + + // Generation for all UNO types - coordinates RustProducer + CppProducer + void produceEnum(std::string_view name, const rtl::Reference<unoidl::EnumTypeEntity>& entity); + void produceStruct(std::string_view name, + const rtl::Reference<unoidl::PlainStructTypeEntity>& entity); + void + producePolyStruct(std::string_view name, + const rtl::Reference<unoidl::PolymorphicStructTypeTemplateEntity>& entity); + void produceException(std::string_view name, + const rtl::Reference<unoidl::ExceptionTypeEntity>& entity); + void produceInterface(std::string_view name, + const rtl::Reference<unoidl::InterfaceTypeEntity>& entity); + void produceTypedef(std::string_view name, const rtl::Reference<unoidl::TypedefEntity>& entity); + void produceConstantGroup(std::string_view name, + const rtl::Reference<unoidl::ConstantGroupEntity>& entity); + void produceService(std::string_view name, + const rtl::Reference<unoidl::SingleInterfaceBasedServiceEntity>& entity); + void produceSingleton(std::string_view name, + const rtl::Reference<unoidl::InterfaceBasedSingletonEntity>& entity); + + // Type name conversion for Rust output + OString getRustName(std::string_view name, bool istype = false); + OString getRustName(std::u16string_view name, bool istype = false); + + // + static OString getBaseUnoName(std::string_view name); + OString getBaseUnoName(std::u16string_view name); + + // Core data members + rtl::Reference<TypeManager> m_manager; + + std::unordered_set<OString> m_startingTypes; // Types requested by user + std::unordered_set<OString> m_typesProduced; // Prevent duplicate generation + std::unordered_map<OString, OString> m_typedefs; + + // Runtime configuration + OString m_rustOutputDir; + bool m_verbose; + bool m_dryRun; + // Producer instances for coordinated opaque generation + std::unique_ptr<RustProducer> m_rustProducer; + std::unique_ptr<CppProducer> m_cppProducer; + + // Name processing utilities + static std::string_view splitName(std::string_view name); + static OString handleName(std::string_view name, bool istype); + static OString handleName(std::u16string_view name, bool istype); + static OString getSafeIdentifier(std::string_view name, bool istype); + static OString getSafeIdentifier(std::u16string_view name, bool istype); + static void separatedForeach(const auto& items, auto&& sepFunc, auto&& itemFunc); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/comphelper/source/misc/date.cxx b/comphelper/source/misc/date.cxx index b95f63f75c97..2625d32c1d2f 100644 --- a/comphelper/source/misc/date.cxx +++ b/comphelper/source/misc/date.cxx @@ -36,12 +36,14 @@ namespace comphelper::date /* XXX can that dbconversion cope with years > 9999 or negative years at all? * Database fields may be limited to positive 4 digits. */ -constexpr sal_Int32 MIN_DAYS = -11968265; // -32768-01-01 -constexpr sal_Int32 MAX_DAYS = 11967900; // 32767-12-31 - constexpr sal_Int16 kYearMax = SAL_MAX_INT16; constexpr sal_Int16 kYearMin = SAL_MIN_INT16; +constexpr sal_Int32 MIN_DAYS = convertDateToDays(1, 1, kYearMin); // -32768-01-01 +static_assert(MIN_DAYS == -11968265); +constexpr sal_Int32 MAX_DAYS = convertDateToDays(31, 12, kYearMax); // 32767-12-31 +static_assert(MAX_DAYS == 11967900); + constexpr sal_Int32 nNullDateDays = convertDateToDays(30, 12, 1899); static_assert(nNullDateDays == 693594); diff --git a/compilerplugins/clang/nullptr.cxx b/compilerplugins/clang/nullptr.cxx index 04fb5bf0f9fd..3189013fbc69 100644 --- a/compilerplugins/clang/nullptr.cxx +++ b/compilerplugins/clang/nullptr.cxx @@ -137,9 +137,11 @@ bool Nullptr::VisitImplicitCastExpr(CastExpr const * expr) { { break; // POSIX locale_t is left unspecified } - // Hack to handle libc++ and stdlibc++ `std::strong_ordering x; x < 0` etc.: + // Hack to handle libc++ and libstdc++ (libstdc++ < 16: __unspec; libstdc++ >= 16: + // __literal_zero) `std::strong_ordering x; x < 0` etc.: if (tc.MemberPointerOf().ClassOrStruct("_CmpUnspecifiedParam").StdNamespace() - || tc.Pointer().ClassOrStruct("__unspec").Namespace("__cmp_cat").StdNamespace()) + || tc.Pointer().ClassOrStruct("__unspec").Namespace("__cmp_cat").StdNamespace() + || tc.Pointer().ClassOrStruct("__literal_zero").Namespace("__cmp_cat").StdNamespace()) { break; } diff --git a/config_host.mk.in b/config_host.mk.in index 429a4610d9ee..48d2ed3d76dc 100644 --- a/config_host.mk.in +++ b/config_host.mk.in @@ -178,6 +178,7 @@ export ENABLE_DBUS=@ENABLE_DBUS@ export ENABLE_DCONF=@ENABLE_DCONF@ export ENABLE_DEBUG=@ENABLE_DEBUG@ ENABLE_DOTNET=@ENABLE_DOTNET@ +ENABLE_RUST_UNO=@ENABLE_RUST_UNO@ SYSTEM_DRAGONBOX=@SYSTEM_DRAGONBOX@ SYSTEM_FROZEN=@SYSTEM_FROZEN@ ENABLE_EMBINDTEST_UNO=@ENABLE_EMBINDTEST_UNO@ diff --git a/configure.ac b/configure.ac index 99ccaf54f748..4eeeefc3c19e 100644 --- a/configure.ac +++ b/configure.ac @@ -3022,6 +3022,10 @@ AC_ARG_WITH(dotnet, Requires .NET SDK 8 or higher. To disable building .NET components, use --without-dotnet or --with-dotnet=no.])) +AC_ARG_ENABLE(rust-uno, + AS_HELP_STRING([--enable-rust-uno], + [Enable experimental Rust UNO support.])) + dnl =================================================================== dnl Branding dnl =================================================================== @@ -5926,6 +5930,16 @@ AC_SUBST(ENABLE_DOTNET) AC_SUBST(DOTNET) AC_SUBST(DOTNET_ROOT) +AC_MSG_CHECKING([whether to enable Rust UNO support]) +if test "$enable_rust_uno" = "yes"; then + ENABLE_RUST_UNO=TRUE + AC_MSG_RESULT([yes]) +else + ENABLE_RUST_UNO= + AC_MSG_RESULT([no]) +fi +AC_SUBST(ENABLE_RUST_UNO) + DISABLE_GUI="" if test "$enable_gui" = "no"; then if test "$using_x11" != yes; then @@ -11587,18 +11601,9 @@ dnl =================================================================== dnl Orcus dnl =================================================================== libo_CHECK_SYSTEM_MODULE([orcus],[ORCUS],[liborcus-0.20 >= 0.20.2]) -if test "$with_system_orcus" != "yes"; then - if test "$SYSTEM_BOOST" = "TRUE"; then - dnl Link with Boost.System - dnl This seems to be necessary since boost 1.50 (1.48 does not need it, - dnl 1.49 is untested). The macro BOOST_THREAD_DONT_USE_SYSTEM mentioned - dnl in documentation has no effect. - AX_BOOST_SYSTEM - fi -fi + dnl FIXME by renaming SYSTEM_LIBORCUS to SYSTEM_ORCUS in the build system world SYSTEM_LIBORCUS=$SYSTEM_ORCUS -AC_SUBST([BOOST_SYSTEM_LIB]) AC_SUBST(SYSTEM_LIBORCUS) dnl =================================================================== @@ -11651,7 +11656,7 @@ else else # autodetect, prefer meson.py / standalone version that can more likely be run # by different python runtimes - AC_PATH_PROGS(MESON,[meson.py meson],,[$LODE_HOME/packages/meson-1.7.2:$PATH]) + AC_PATH_PROGS(MESON,[meson.py meson],,[$LODE_HOME/opt/bin:$PATH]) if test -z "$MESON"; then AC_MSG_WARN([meson not found, using internal copy]) BUILD_TYPE="$BUILD_TYPE MESON" @@ -11661,7 +11666,24 @@ else MESON="$formatted_path" AC_MSG_CHECKING([whether meson can be run with "$PYTHON_FOR_BUILD $MESON"]) if ($PYTHON_FOR_BUILD $MESON --version >/dev/null) ; then - AC_MSG_RESULT([yes]) + # Xcode 26 and higher need a newer version of meson so use + # the internal meson if the external meson's version is + # too old + if test $_os = Darwin -a $MACOSX_SDK_VERSION -ge 260000; then + _meson_minver=1.8.3 + _meson_minmajmin=`echo $_meson_minver | $AWK -F. '{ print \$1*10000+\$2*100+\$3 }'` + _meson_version=`$PYTHON_FOR_BUILD $MESON --version 2>/dev/null` + _meson_majmin=`echo $_meson_version | $AWK -F. '{ print \$1*10000+\$2*100+\$3 }'` + if test "$_meson_majmin" -lt "$_meson_minmajmin"; then + AC_MSG_WARN([meson "$_meson_version" is too old, using internal copy]) + BUILD_TYPE="$BUILD_TYPE MESON" + MESON='$(gb_UnpackedTarball_workdir)/meson/meson.py' + else + AC_MSG_RESULT([yes]) + fi + else + AC_MSG_RESULT([yes]) + fi else AC_MSG_RESULT([no]) AC_MSG_ERROR([meson incompatible with the specified python. Try using a different python runtime or a plain release of meson by adding PYTHON=/other/python.version and/or MESON=/path/to/meson.py to autogen.input]) diff --git a/cui/source/dialogs/about.cxx b/cui/source/dialogs/about.cxx index ca471ccf71f0..de52af0f8ce0 100644 --- a/cui/source/dialogs/about.cxx +++ b/cui/source/dialogs/about.cxx @@ -78,8 +78,7 @@ AboutDialog::AboutDialog(weld::Window *pParent) OUString sbuildId = GetBuildString(); if (IsStringValidGitHash(sbuildId)) { const tools::Long nMaxChar = 25; - m_pBuildLabel->set_uri("https://gerrit.libreoffice.org/gitweb?p=core.git;a=log;h=" - + sbuildId); + m_pBuildLabel->set_uri("https://git.libreoffice.org/core/history/" + sbuildId); m_pBuildLabel->set_label(sbuildId.getLength() > nMaxChar ? sbuildId.replaceAt( nMaxChar, sbuildId.getLength() - nMaxChar, u"...") : sbuildId); diff --git a/desktop/Executable_soffice_bin.mk b/desktop/Executable_soffice_bin.mk index 5838572d16fa..5987a9a4d456 100644 --- a/desktop/Executable_soffice_bin.mk +++ b/desktop/Executable_soffice_bin.mk @@ -19,6 +19,7 @@ $(eval $(call gb_Executable_add_defs,soffice_bin,\ )) $(eval $(call gb_Executable_use_libraries,soffice_bin,\ + $(if $(ENABLE_RUST_UNO),rust_uno-cpp) \ sal \ sofficeapp \ )) diff --git a/desktop/qa/data/formulabar.ods b/desktop/qa/data/formulabar.ods Binary files differnew file mode 100644 index 000000000000..ea099e24077c --- /dev/null +++ b/desktop/qa/data/formulabar.ods diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx b/desktop/qa/desktop_lib/test_desktop_lib.cxx index d997d9e8f673..02ad4a2da30b 100644 --- a/desktop/qa/desktop_lib/test_desktop_lib.cxx +++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx @@ -149,6 +149,7 @@ public: void testGetFilterTypes(); void testGetPartPageRectangles(); void testSearchCalc(); + void testPropertySettingOnFormulaBar(); void testSearchAllNotificationsCalc(); void testPaintTile(); void testSaveAs(); @@ -226,6 +227,7 @@ public: CPPUNIT_TEST(testGetFilterTypes); CPPUNIT_TEST(testGetPartPageRectangles); CPPUNIT_TEST(testSearchCalc); + CPPUNIT_TEST(testPropertySettingOnFormulaBar); CPPUNIT_TEST(testSearchAllNotificationsCalc); CPPUNIT_TEST(testPaintTile); CPPUNIT_TEST(testSaveAs); @@ -2259,6 +2261,7 @@ public: bool m_bEmptyTableSelection; bool m_bTilesInvalidated; bool m_bZeroCursor; + bool m_stateBold; tools::Rectangle m_aOwnCursor; boost::property_tree::ptree m_aCommentCallbackResult; boost::property_tree::ptree m_aColorPaletteCallbackResult; @@ -2269,7 +2272,8 @@ public: m_nTableSelectionCount(0), m_bEmptyTableSelection(false), m_bTilesInvalidated(false), - m_bZeroCursor(false) + m_bZeroCursor(false), + m_stateBold(false) { mnView = SfxLokHelper::getCurrentView(); mpDocument->m_pDocumentClass->registerCallback(pDocument, &ViewCallback::callback, this); @@ -2357,6 +2361,13 @@ public: m_aLastRedlineInfo = redlines[0]; } break; + case LOK_CALLBACK_STATE_CHANGED: + { + if (aPayload.startsWith(".uno:Bold=")) + { + m_stateBold = aPayload.copy(".uno:Bold="_ostr.getLength()).toBoolean(); + } + } } } }; @@ -3120,6 +3131,52 @@ void DesktopLOKTest::testCalcValidityDropdownInReadonlyMode() CPPUNIT_ASSERT_EQUAL(true, aView.m_JSONDialog.empty()); } +void DesktopLOKTest::testPropertySettingOnFormulaBar() +{ + LibLibreOffice_Impl aOffice; + LibLODocument_Impl* pDocument = loadDoc("formulabar.ods"); + Scheduler::ProcessEventsToIdle(); + + pDocument->m_pDocumentClass->initializeForRendering(pDocument, "{}"); + Scheduler::ProcessEventsToIdle(); + + ViewCallback aView(pDocument); + Scheduler::ProcessEventsToIdle(); + + // Go to A1. There are 2 words in the cell. + pDocument->pClass->postMouseEvent(pDocument, LOK_MOUSEEVENT_MOUSEBUTTONDOWN, 1000, 150, 1, 1, 0); + pDocument->pClass->postMouseEvent(pDocument, LOK_MOUSEEVENT_MOUSEBUTTONUP, 1000, 150, 1, 1, 0); + Scheduler::ProcessEventsToIdle(); + + // Set the focus to formulabar. + pDocument->pClass->sendDialogEvent(pDocument, 0, "{\"id\":\"sc_input_window\", \"cmd\": \"grab_focus\", \"data\": \"null\", \"type\": \"drawingarea\"}"); + Scheduler::ProcessEventsToIdle(); + + // Select the first word. + pDocument->pClass->sendDialogEvent(pDocument, 0, "{\"id\":\"sc_input_window\", \"cmd\": \"textselection\", \"data\": \"0;3;0;0\", \"type\": \"drawingarea\"}"); + Scheduler::ProcessEventsToIdle(); + + // Set bold property for the selected word. + pDocument->pClass->postUnoCommand(pDocument, ".uno:Bold", nullptr, false); + Scheduler::ProcessEventsToIdle(); + + CPPUNIT_ASSERT_EQUAL(true, aView.m_stateBold); + + // Select the second word. Without the fix, this selection removes the "bold" attribute. + pDocument->pClass->sendDialogEvent(pDocument, 0, "{\"id\":\"sc_input_window\", \"cmd\": \"textselection\", \"data\": \"4;9;0;0\", \"type\": \"drawingarea\"}"); + Scheduler::ProcessEventsToIdle(); + + // Select the first word again. + pDocument->pClass->sendDialogEvent(pDocument, 0, "{\"id\":\"sc_input_window\", \"cmd\": \"textselection\", \"data\": \"0;3;0;0\", \"type\": \"drawingarea\"}"); + Scheduler::ProcessEventsToIdle(); + + // Unset bold property for the selected word. + pDocument->pClass->postUnoCommand(pDocument, ".uno:Bold", nullptr, false); + Scheduler::ProcessEventsToIdle(); + + CPPUNIT_ASSERT_EQUAL(false, aView.m_stateBold); // This line doesn't pass without the fix in this commit. +} + void DesktopLOKTest::testRunMacro() { LibLibreOffice_Impl aOffice; diff --git a/download.lst b/download.lst index 480e930d9076..c30ccb1d6ed8 100644 --- a/download.lst +++ b/download.lst @@ -14,8 +14,8 @@ ARGON2_TARBALL := phc-winner-argon2-20190702.tar.gz # so that git cherry-pick # will not run into conflicts # please repack the tarball using external/boost/repack_tarball.sh -BOOST_SHA256SUM := 5c67a448c562f1606e38203ba7ed7e8d7453581b6b9ca324e96205eae0da5ff8 -BOOST_TARBALL := boost_1_88_0.tar.xz +BOOST_SHA256SUM := b3a976c659961f1d7fae9a8e58d6729a11f5473abaf2ef0e623f53777c86475d +BOOST_TARBALL := boost_1_89_0.tar.xz # three static lines # so that git cherry-pick # will not run into conflicts @@ -302,8 +302,8 @@ FREEHAND_TARBALL := libfreehand-0.1.2.tar.xz # three static lines # so that git cherry-pick # will not run into conflicts -FREETYPE_SHA256SUM := f8dfa8f15ef0576738dfb55b2e6e6b172fd5d09b6f03785a1df03239549f64d2 -FREETYPE_TARBALL := freetype-2.14.0.tar.xz +FREETYPE_SHA256SUM := 32427e8c471ac095853212a37aef816c60b42052d4d9e48230bab3bdf2936ccc +FREETYPE_TARBALL := freetype-2.14.1.tar.xz # three static lines # so that git cherry-pick # will not run into conflicts @@ -482,8 +482,8 @@ LXML_TARBALL := lxml-6.0.1.tar.gz # three static lines # so that git cherry-pick # will not run into conflicts -MARIADB_CONNECTOR_C_SHA256SUM := b593fdd3d5b8964a9feec2bf57a13e6cc8f178a4fe948e89f60ede9c53d621fe -MARIADB_CONNECTOR_C_TARBALL := mariadb-connector-c-3.3.15-src.tar.gz +MARIADB_CONNECTOR_C_SHA256SUM := a5abb7331508988f7287b481c1839bd929261ce38352cd0fde6c002c300e0c01 +MARIADB_CONNECTOR_C_TARBALL := mariadb-connector-c-3.3.17-src.tar.gz # three static lines # so that git cherry-pick # will not run into conflicts @@ -497,8 +497,8 @@ MDNSRESPONDER_TARBALL := mDNSResponder-878.200.35.tar.gz # three static lines # so that git cherry-pick # will not run into conflicts -MESON_SHA256SUM := 0a9b23311271519bd03dca12d7d8b0eab582c3a2c5da433d465b6e519dc88e2f -MESON_TARBALL := meson-1.8.0.tar.gz +MESON_SHA256SUM := f118aa910fc0a137cc2dd0122232dbf82153d9a12fb5b0f5bb64896f6a157abf +MESON_TARBALL := meson-1.8.3.tar.gz # three static lines # so that git cherry-pick # will not run into conflicts diff --git a/editeng/source/editeng/editview.cxx b/editeng/source/editeng/editview.cxx index ae94c4c9b3f0..0494d792fd33 100644 --- a/editeng/source/editeng/editview.cxx +++ b/editeng/source/editeng/editview.cxx @@ -1735,7 +1735,6 @@ OUString EditView::GetSurroundingText() const Selection EditView::GetSurroundingTextSelection() const { ESelection aSelection( GetSelection() ); - aSelection.Adjust(); if( HasSelection() ) { @@ -1745,7 +1744,13 @@ Selection EditView::GetSurroundingTextSelection() const // Stop reconversion if the selected text includes a line break. if ( aStr.indexOf( 0x0A ) == -1 ) - return Selection(0, aSelection.end.nIndex - aSelection.start.nIndex); + { + const tools::Long nLength = std::abs(aSelection.end.nIndex - aSelection.start.nIndex); + if (aSelection.start.nIndex < aSelection.end.nIndex) + return Selection(0, nLength); + else + return Selection(nLength, 0); + } else return Selection( 0, 0 ); } diff --git a/editeng/source/editeng/impedit3.cxx b/editeng/source/editeng/impedit3.cxx index f8a29522e5e2..e6b92171c974 100644 --- a/editeng/source/editeng/impedit3.cxx +++ b/editeng/source/editeng/impedit3.cxx @@ -569,6 +569,16 @@ void ImpEditEngine::populateRubyInfo(ParaPortion& rParaPortion, EditLine* pLine) for (sal_Int32 nP = pLine->GetStartPortion(); pNextRubyAttr && nP <= pLine->GetEndPortion(); ++nP) { + const auto* pRuby = static_cast<const SvxRubyItem*>(pNextRubyAttr->GetItem()); + if (pRuby->GetText().isEmpty()) + { + // Skip processing blank ruby spans + pNextRubyAttr + = rParaPortion.GetNode()->GetCharAttribs().FindNextAttrib(EE_CHAR_RUBY, nTextPos); + pTPRubyStart = nullptr; + continue; + } + SeekCursor(pNode, nTextPos, aTmpFont); TextPortion& rTP = rParaPortion.GetTextPortions()[nP]; @@ -596,7 +606,6 @@ void ImpEditEngine::populateRubyInfo(ParaPortion& rParaPortion, EditLine* pLine) auto pRubyInfo = std::make_unique<RubyPortionInfo>(); // Get ruby text width - const auto* pRuby = static_cast<const SvxRubyItem*>(pNextRubyAttr->GetItem()); // TODO: Style support is unimplemented. For now, use a hard-coded 50% scale aRubyStartFont.SetFontSize(aRubyStartFont.GetFontSize() / 2); diff --git a/emfio/inc/mtftools.hxx b/emfio/inc/mtftools.hxx index 91a7a461f84f..2230801ff288 100644 --- a/emfio/inc/mtftools.hxx +++ b/emfio/inc/mtftools.hxx @@ -204,6 +204,8 @@ namespace emfio enum PenStyle : sal_uInt32 { PS_COSMETIC = 0x00000000, + PS_GEOMETRIC = 0x00010000, + PS_SOLID = 0x00000000, PS_DASH = 0x00000001, PS_DOT = 0x00000002, @@ -223,9 +225,7 @@ namespace emfio PS_JOIN_ROUND = 0x00000000, PS_JOIN_BEVEL = 0x00001000, PS_JOIN_MITER = 0x00002000, - PS_JOIN_STYLE_MASK = 0x0000F000, - - PS_GEOMETRIC = 0x00010000 + PS_JOIN_STYLE_MASK = 0x0000F000 }; /* [MS-WMF] - v20210625 - pages 30, 82 */ diff --git a/emfio/qa/cppunit/emf/EmfImportTest.cxx b/emfio/qa/cppunit/emf/EmfImportTest.cxx index 37c42d4c2126..a59c95c1d8f9 100644 --- a/emfio/qa/cppunit/emf/EmfImportTest.cxx +++ b/emfio/qa/cppunit/emf/EmfImportTest.cxx @@ -87,19 +87,19 @@ CPPUNIT_TEST_FIXTURE(Test, testPolyPolygon) assertXPath(pDocument, aXPathPrefix + "mask/polypolygoncolor[2]/polypolygon", "path", u"m2574 13194v-12065h15303v12065z"); - assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke", 44); + assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke", 116); assertXPathContent(pDocument, aXPathPrefix + "mask/polygonstroke[1]/polygon", - u"2574,13194 2574,1129"); - assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[1]/line", "color", u"#000000"); - assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[1]/line", "width", u"0"); + u"2574,13194 2574,1129 17877,1129 17877,13194"); + assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[1]/line", "color", u"#ffffff"); + assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[1]/line", "width", u"35"); assertXPathContent(pDocument, aXPathPrefix + "mask/polygonstroke[2]/polygon", - u"2574,1129 2574,1129"); + u"2574,13194 2574,1129"); assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[2]/line", "color", u"#000000"); - assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[2]/line", "width", u"0"); + assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[2]/line", "width", u"35"); assertXPathContent(pDocument, aXPathPrefix + "mask/polygonstroke[10]/polygon", - u"8674,1129 8674,1129"); + u"8674,13194 8674,1129"); assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[10]/line", "color", u"#000000"); - assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[10]/line", "width", u"0"); + assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[10]/line", "width", u"35"); assertXPath(pDocument, aXPathPrefix + "mask/textsimpleportion", 28); assertXPath(pDocument, aXPathPrefix + "mask/textsimpleportion[6]", "width", u"459"); @@ -556,14 +556,13 @@ CPPUNIT_TEST_FIXTURE(Test, testEnglishMapMode) assertXPath(pDocument, aXPathPrefix + "mask/textsimpleportion[1]", "width", u"424"); assertXPath(pDocument, aXPathPrefix + "mask/textsimpleportion[1]", "height", u"424"); - assertXPath(pDocument, aXPathPrefix + "mask/polygonhairline", 3); + assertXPath(pDocument, aXPathPrefix + "mask/polygonhairline", 5); assertXPathContent(pDocument, aXPathPrefix + "mask/polygonhairline[1]/polygon", u"-1,-1 29699,-1 29699,21000 -1,21000"); assertXPathContent(pDocument, aXPathPrefix + "mask/polygonhairline[2]/polygon", u"1058,7937 1058,13230 4233,13230 4233,12171 2115,12171 2115,7937"); - assertXPathContent( - pDocument, aXPathPrefix + "mask/polygonhairline[3]/polygon", - u"12699,1058 16933,1058 16933,2118 15346,2118 15346,6349 14287,6349 14287,2118 12699,2118"); + assertXPathContent(pDocument, aXPathPrefix + "mask/polygonhairline[3]/polygon", + u"1058,7937 1058,13230 4233,13230 4233,12171 2115,12171 2115,7937"); } CPPUNIT_TEST_FIXTURE(Test, testRectangleWithModifyWorldTransform) @@ -910,7 +909,7 @@ CPPUNIT_TEST_FIXTURE(Test, testEmfPolydraw) assertXPathContent(pDocument, aXPathPrefix + "polygonstroke[1]/polygon", u"50,50 50,50 1000,1000 1000,1000 2000,2500 2000,1000"); assertXPath(pDocument, aXPathPrefix + "polygonstroke[1]/line", "color", u"#ff0000"); - assertXPath(pDocument, aXPathPrefix + "polygonstroke[1]/stroke", "dotDashArray", u"30 10 "); + assertXPath(pDocument, aXPathPrefix + "polygonstroke[1]/stroke", "dotDashArray", u"90 30 "); } CPPUNIT_TEST_FIXTURE(Test, testEmfPlusBrushPathGradientWithBlendColors) @@ -1588,29 +1587,23 @@ CPPUNIT_TEST_FIXTURE(Test, testCreatePen) assertXPath(pDocument, aXPathPrefix + "mask/polypolygon", "path", u"m0 0h31250v18192h-31250z"); - assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke", 3); + assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke", 758); assertXPathContent(pDocument, aXPathPrefix + "mask/polygonstroke[1]/polygon", - u"17898,5693 20172,5693"); - assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[1]/line", "color", u"#008000"); - assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[1]/line", "width", u"0"); + u"0,0 31225,0 31225,17742 0,17742"); + assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[1]/line", "color", u"#ffffff"); + assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[1]/line", "width", u"25"); assertXPathContent(pDocument, aXPathPrefix + "mask/polygonstroke[2]/polygon", - u"17898,6959 20172,6959"); - assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[2]/line", "color", u"#000080"); - assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[2]/line", "width", u"0"); + u"25,23 31200,23 31200,17719 25,17719"); + assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[2]/line", "color", u"#ffffff"); + assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[2]/line", "width", u"25"); assertXPathContent(pDocument, aXPathPrefix + "mask/polygonstroke[3]/polygon", - u"17898,7381 20172,7381"); + u"27875,16523 27875,1453"); assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[3]/line", "color", u"#ff0000"); - assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[3]/line", "width", u"0"); + assertXPath(pDocument, aXPathPrefix + "mask/polygonstroke[3]/line", "width", u"3"); - assertXPath(pDocument, aXPathPrefix + "mask/polygonhairline", 755); - assertXPath(pDocument, aXPathPrefix + "mask/polygonhairline[5]", "color", u"#ff0000"); - assertXPathContent(pDocument, aXPathPrefix + "mask/polygonhairline[5]/polygon", - u"27925,16078 27875,16078"); - assertXPath(pDocument, aXPathPrefix + "mask/polygonhairline[10]", "color", u"#ff0000"); - assertXPathContent(pDocument, aXPathPrefix + "mask/polygonhairline[10]/polygon", - u"27925,14180 27875,14180"); + assertXPath(pDocument, aXPathPrefix + "mask/polygonhairline", 0); assertXPath(pDocument, aXPathPrefix + "mask/textsimpleportion", 69); assertXPath(pDocument, aXPathPrefix + "mask/textsimpleportion[1]", "width", u"374"); diff --git a/emfio/source/reader/emfreader.cxx b/emfio/source/reader/emfreader.cxx index 9ecfe1a411d9..abd842e417da 100644 --- a/emfio/source/reader/emfreader.cxx +++ b/emfio/source/reader/emfreader.cxx @@ -1258,10 +1258,10 @@ namespace emfio SAL_INFO("emfio", "\t\tIndex: " << nIndex << " Style: 0x" << std::hex << nPenStyle << std::dec << " PenWidth: " << nPenWidth); - if ((nPenStyle & PS_STYLE_MASK) > PS_INSIDEFRAME) - nPenStyle = PS_COSMETIC; - if ((nPenStyle & PS_GEOMETRIC) == 0) - nPenWidth = 0; + // PS_COSMETIC width is always fixed at one logical unit + // and is not affected by any geometric transformations like scaling + if (nPenStyle == PS_COSMETIC) + nPenWidth = 1; CreateObjectIndexed(nIndex, std::make_unique<WinMtfLineStyle>(ReadColor(), nPenStyle, nPenWidth)); } } @@ -1284,10 +1284,10 @@ namespace emfio else { SAL_INFO("emfio", "\t\tStyle: 0x" << std::hex << nPenStyle << std::dec); - if ((nPenStyle & PS_STYLE_MASK) > PS_INSIDEFRAME) - nPenStyle = PS_COSMETIC; - if ((nPenStyle & PS_GEOMETRIC) == 0) - nWidth = 0; + // PS_COSMETIC width is always fixed at one logical unit + // and is not affected by any geometric transformations like scaling + if (nPenStyle == PS_COSMETIC) + nWidth = 1; SAL_INFO("emfio", "\t\tWidth: " << nWidth); CreateObjectIndexed(nIndex, std::make_unique<WinMtfLineStyle>(aColorRef, nPenStyle, nWidth)); } diff --git a/emfio/source/reader/mtftools.cxx b/emfio/source/reader/mtftools.cxx index f5467f88c923..b57c528b67f9 100644 --- a/emfio/source/reader/mtftools.cxx +++ b/emfio/source/reader/mtftools.cxx @@ -803,12 +803,12 @@ namespace emfio break; case StockObject::WHITE_PEN : { - maLineStyle = WinMtfLineStyle(COL_WHITE, PS_COSMETIC, 0); + maLineStyle = WinMtfLineStyle(COL_WHITE, PS_COSMETIC, 1); } break; case StockObject::BLACK_PEN : { - maLineStyle = WinMtfLineStyle(COL_BLACK, PS_COSMETIC, 0); + maLineStyle = WinMtfLineStyle(COL_BLACK, PS_COSMETIC, 1); } break; case StockObject::NULL_PEN : diff --git a/external/boost/Module_boost.mk b/external/boost/Module_boost.mk index 252bf1c9a3b3..f12e5dafa980 100644 --- a/external/boost/Module_boost.mk +++ b/external/boost/Module_boost.mk @@ -13,7 +13,6 @@ $(eval $(call gb_Module_add_targets,boost,\ StaticLibrary_boost_date_time \ StaticLibrary_boost_filesystem \ StaticLibrary_boost_locale \ - StaticLibrary_boost_system \ StaticLibrary_boost_iostreams \ UnpackedTarball_boost \ )) diff --git a/external/boost/StaticLibrary_boost_system.mk b/external/boost/StaticLibrary_boost_system.mk deleted file mode 100644 index e4b05d7b5cde..000000000000 --- a/external/boost/StaticLibrary_boost_system.mk +++ /dev/null @@ -1,29 +0,0 @@ -# -*- 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_StaticLibrary_StaticLibrary,boost_system)) - -$(eval $(call gb_StaticLibrary_use_unpacked,boost_system,boost)) - -$(eval $(call gb_StaticLibrary_set_warnings_disabled,boost_system)) - -# disable "auto link" "feature" on MSVC -$(eval $(call gb_StaticLibrary_add_defs,boost_system,\ - -DBOOST_ALL_NO_LIB \ -)) - -$(eval $(call gb_StaticLibrary_use_external,boost_system,boost_headers)) - -$(eval $(call gb_StaticLibrary_set_generated_cxx_suffix,boost_system,cpp)) - -$(eval $(call gb_StaticLibrary_add_generated_exception_objects,boost_system,\ - UnpackedTarball/boost/libs/system/src/error_code \ -)) - -# vim: set noet sw=4 ts=4: diff --git a/external/curl/0001-cookie-don-t-treat-the-leading-slash-as-trailing.patch b/external/curl/0001-cookie-don-t-treat-the-leading-slash-as-trailing.patch new file mode 100644 index 000000000000..45fba1f8af4e --- /dev/null +++ b/external/curl/0001-cookie-don-t-treat-the-leading-slash-as-trailing.patch @@ -0,0 +1,54 @@ +From c6ae07c6a541e0e96d0040afb62b45dd37711300 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Mon, 11 Aug 2025 20:23:05 +0200 +Subject: [PATCH] cookie: don't treat the leading slash as trailing + +If there is only a leading slash in the path, keep that. Also add an +assert to make sure the path is never blank. + +Reported-by: Google Big Sleep +Closes #18266 +--- + lib/cookie.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/lib/cookie.c b/lib/cookie.c +index 914a4aca1..b72dd99bc 100644 +--- a/lib/cookie.c ++++ b/lib/cookie.c +@@ -296,9 +296,9 @@ static char *sanitize_cookie_path(const char *cookie_path) + /* Let cookie-path be the default-path. */ + return strdup("/"); + +- /* remove trailing slash */ ++ /* remove trailing slash when path is non-empty */ + /* convert /hoge/ to /hoge */ +- if(len && cookie_path[len - 1] == '/') ++ if(len > 1 && cookie_path[len - 1] == '/') + len--; + + return Curl_memdup0(cookie_path, len); +@@ -965,7 +965,7 @@ replace_existing(struct Curl_easy *data, + clist->spath && co->spath && /* both have paths */ + clist->secure && !co->secure && !secure) { + size_t cllen; +- const char *sep; ++ const char *sep = NULL; + + /* + * A non-secure cookie may not overlay an existing secure cookie. +@@ -974,8 +974,9 @@ replace_existing(struct Curl_easy *data, + * "/loginhelper" is ok. + */ + +- sep = strchr(clist->spath + 1, '/'); +- ++ DEBUGASSERT(clist->spath[0]); ++ if(clist->spath[0]) ++ sep = strchr(clist->spath + 1, '/'); + if(sep) + cllen = sep - clist->spath; + else +-- +2.39.5 + diff --git a/external/curl/0001-ws-get-a-new-mask-for-each-new-outgoing-frame.patch b/external/curl/0001-ws-get-a-new-mask-for-each-new-outgoing-frame.patch new file mode 100644 index 000000000000..99f497d26726 --- /dev/null +++ b/external/curl/0001-ws-get-a-new-mask-for-each-new-outgoing-frame.patch @@ -0,0 +1,61 @@ +From 84db7a9eae8468c0445b15aa806fa7fa806fa0f2 Mon Sep 17 00:00:00 2001 +From: Daniel Stenberg <daniel@haxx.se> +Date: Mon, 8 Sep 2025 14:14:15 +0200 +Subject: [PATCH] ws: get a new mask for each new outgoing frame + +Reported-by: Calvin Ruocco +Closes #18496 +--- + lib/ws.c | 28 +++++++++++++--------------- + 1 file changed, 13 insertions(+), 15 deletions(-) + +diff --git a/lib/ws.c b/lib/ws.c +index e973409b6..3b6542816 100644 +--- a/lib/ws.c ++++ b/lib/ws.c +@@ -875,6 +875,19 @@ static CURLcode ws_enc_add_frame(struct Curl_easy *data, + enc->payload_remain = enc->payload_len = payload_len; + ws_enc_info(enc, data, "sending"); + ++ /* 4 bytes random */ ++ ++ CURLcode result = ++ Curl_rand(data, (unsigned char *)&enc->mask, sizeof(enc->mask)); ++ if(result) ++ return result; ++ ++#ifdef DEBUGBUILD ++ if(getenv("CURL_WS_FORCE_ZERO_MASK")) ++ /* force the bit mask to 0x00000000, effectively disabling masking */ ++ memset(&enc->mask, 0, sizeof(enc->mask)); ++#endif ++ + /* add 4 bytes mask */ + memcpy(&head[hlen], &enc->mask, 4); + hlen += 4; +@@ -1335,21 +1347,7 @@ CURLcode Curl_ws_accept(struct Curl_easy *data, + subprotocol not requested by the client), the client MUST Fail + the WebSocket Connection. */ + +- /* 4 bytes random */ +- +- result = Curl_rand(data, (unsigned char *)&ws->enc.mask, +- sizeof(ws->enc.mask)); +- if(result) +- return result; +- +-#ifdef DEBUGBUILD +- if(getenv("CURL_WS_FORCE_ZERO_MASK")) +- /* force the bit mask to 0x00000000, effectively disabling masking */ +- memset(ws->enc.mask, 0, sizeof(ws->enc.mask)); +-#endif +- +- infof(data, "[WS] Received 101, switch to WebSocket; mask %02x%02x%02x%02x", +- ws->enc.mask[0], ws->enc.mask[1], ws->enc.mask[2], ws->enc.mask[3]); ++ infof(data, "[WS] Received 101, switch to WebSocket"); + + /* Install our client writer that decodes WS frames payload */ + result = Curl_cwriter_create(&ws_dec_writer, data, &ws_cw_decode, +-- +2.39.5 + diff --git a/external/curl/UnpackedTarball_curl.mk b/external/curl/UnpackedTarball_curl.mk index 52861a50bed8..51bec9c4f659 100644 --- a/external/curl/UnpackedTarball_curl.mk +++ b/external/curl/UnpackedTarball_curl.mk @@ -28,6 +28,8 @@ $(eval $(call gb_UnpackedTarball_add_patches,curl,\ external/curl/zlib.patch.0 \ external/curl/configurable-z-option.patch.0 \ external/curl/0001-const-up-readonly-H2_NON_FIELD.patch.1 \ + external/curl/0001-cookie-don-t-treat-the-leading-slash-as-trailing.patch \ + external/curl/0001-ws-get-a-new-mask-for-each-new-outgoing-frame.patch \ )) ifeq ($(OS)-$(COM_IS_CLANG),WNT-TRUE) diff --git a/external/liborcus/ExternalProject_liborcus.mk b/external/liborcus/ExternalProject_liborcus.mk index a18026c13b6d..83fd8dea505e 100644 --- a/external/liborcus/ExternalProject_liborcus.mk +++ b/external/liborcus/ExternalProject_liborcus.mk @@ -15,7 +15,6 @@ $(eval $(call gb_ExternalProject_use_externals,liborcus, \ boost_headers \ boost_filesystem \ boost_iostreams \ - boost_system \ mdds_headers \ zlib \ )) @@ -41,7 +40,7 @@ endif ifneq ($(SYSTEM_BOOST),) liborcus_LIBS+=$(BOOST_SYSTEM_LIB) $(BOOST_IOSTREAMS_LIB) $(BOOST_FILESYSTEM_LIB) else -liborcus_LIBS+=-L$(gb_StaticLibrary_WORKDIR) -lboost_system -lboost_iostreams -lboost_filesystem +liborcus_LIBS+=-L$(gb_StaticLibrary_WORKDIR) -lboost_iostreams -lboost_filesystem endif ifeq ($(OS),ANDROID) liborcus_LIBS+=$(gb_STDLIBS) diff --git a/external/liborcus/Library_orcus-parser.mk b/external/liborcus/Library_orcus-parser.mk index 3680d57d41b4..5d8b5f08a74f 100644 --- a/external/liborcus/Library_orcus-parser.mk +++ b/external/liborcus/Library_orcus-parser.mk @@ -14,7 +14,6 @@ $(eval $(call gb_Library_use_unpacked,orcus-parser,liborcus)) $(eval $(call gb_Library_use_externals,orcus-parser,\ boost_headers \ boost_filesystem \ - boost_system \ mdds_headers \ zlib \ )) diff --git a/external/liborcus/Library_orcus.mk b/external/liborcus/Library_orcus.mk index 582dc6b6e474..f6243e4e0fd2 100644 --- a/external/liborcus/Library_orcus.mk +++ b/external/liborcus/Library_orcus.mk @@ -15,7 +15,6 @@ $(eval $(call gb_Library_use_externals,orcus,\ boost_headers \ boost_filesystem \ boost_iostreams \ - boost_system \ mdds_headers \ zlib \ )) diff --git a/external/mariadb-connector-c/UnpackedTarball_mariadb-connector-c.mk b/external/mariadb-connector-c/UnpackedTarball_mariadb-connector-c.mk index fb91af3e5a55..7c72bd807259 100644 --- a/external/mariadb-connector-c/UnpackedTarball_mariadb-connector-c.mk +++ b/external/mariadb-connector-c/UnpackedTarball_mariadb-connector-c.mk @@ -30,7 +30,6 @@ endif # $(OS),WNT $(eval $(call gb_UnpackedTarball_add_patches,mariadb-connector-c,\ external/mariadb-connector-c/clang-cl.patch.0 \ - external/mariadb-connector-c/c23.patch.0 \ external/mariadb-connector-c/0001-const-up-my_uca1400_collation_definitions.patch \ external/mariadb-connector-c/0001-const-up-mariadb_defaults-and-MADB_OS_CHARSET.patch \ )) diff --git a/external/mariadb-connector-c/c23.patch.0 b/external/mariadb-connector-c/c23.patch.0 deleted file mode 100644 index d19d6be4cdae..000000000000 --- a/external/mariadb-connector-c/c23.patch.0 +++ /dev/null @@ -1,11 +0,0 @@ ---- include/ma_global.h -+++ include/ma_global.h -@@ -683,7 +683,7 @@ - typedef int myf; /* Type of MyFlags in my_funcs */ - typedef char my_bool; /* Small bool */ - typedef unsigned long long my_ulonglong; --#if !defined(bool) && !defined(bool_defined) && (!defined(HAVE_BOOL) || !defined(__cplusplus)) -+#if !defined(bool) && !defined(bool_defined) && !(defined(__cplusplus) || __STDC_VERSION__ >= 202311L) - typedef char bool; /* Ordinary boolean values 0 1 */ - #endif - /* Macros for converting *constants* to the right type */ diff --git a/forms/source/component/FormComponent.cxx b/forms/source/component/FormComponent.cxx index 97ac1280bd91..0492afa4817f 100644 --- a/forms/source/component/FormComponent.cxx +++ b/forms/source/component/FormComponent.cxx @@ -53,7 +53,8 @@ #include <sal/log.hxx> #include <algorithm> - +#include <vcl/window.hxx> +#include <toolkit/helper/vclunohelper.hxx> namespace frm { using namespace ::com::sun::star::uno; @@ -270,6 +271,9 @@ void SAL_CALL OControl::createPeer(const Reference<XToolkit>& _rxToolkit, const { m_xControl->createPeer( _rxToolkit, _rxParent ); impl_resetStateGuard_nothrow(); + + VclPtr<vcl::Window> pVclPeer = VCLUnoHelper::GetWindow(getPeer()); + pVclPeer->SetFormControl(true); } } diff --git a/forms/source/xforms/convert.cxx b/forms/source/xforms/convert.cxx index 98fd3391f220..8cf61cd2ac2c 100644 --- a/forms/source/xforms/convert.cxx +++ b/forms/source/xforms/convert.cxx @@ -118,7 +118,7 @@ namespace css::util::Date lcl_toUNODate( std::u16string_view rString ) { - css::util::Date aDate( 1, 1, 1900 ); + css::util::Date aDate; bool bWellformed = ISO8601parseDate(rString, aDate); diff --git a/include/sfx2/lokhelper.hxx b/include/sfx2/lokhelper.hxx index b32f775db14b..bee8f3178ae7 100644 --- a/include/sfx2/lokhelper.hxx +++ b/include/sfx2/lokhelper.hxx @@ -187,6 +187,8 @@ public: static void notifyDocumentSizeChangedAllViews(vcl::ITiledRenderable* pDoc, bool bInvalidateAll = true); /// Emits a LOK_CALLBACK_DOCUMENT_SIZE_CHANGED for all views of the same document with the same part static void notifyPartSizeChangedAllViews(vcl::ITiledRenderable* pDoc, int nPart); + /// Emits a LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR + static void notifyCursorInvalidation(SfxViewShell const* pThisView, tools::Rectangle const * pRect, bool bControlEvent); /// Emits a LOK_CALLBACK_INVALIDATE_TILES, but tweaks it according to setOptionalFeatures() if needed. static void notifyInvalidation(SfxViewShell const* pThisView, int nPart, tools::Rectangle const *); /// Emits a LOK_CALLBACK_INVALIDATE_TILES, but tweaks it according to setOptionalFeatures() if needed diff --git a/include/sfx2/viewsh.hxx b/include/sfx2/viewsh.hxx index ee35c7195278..1748644dff75 100644 --- a/include/sfx2/viewsh.hxx +++ b/include/sfx2/viewsh.hxx @@ -434,6 +434,9 @@ public: /// ILibreOfficeKitNotifier. Emits a LOK_CALLBACK_INVALIDATE_TILES. virtual void notifyInvalidation(tools::Rectangle const *) const override; + /// ILibreOfficeKitNotifier. + virtual void notifyCursorInvalidation(tools::Rectangle const *, bool bControlEvent) const override; + /// See OutlinerViewShell::NotifyOtherViews(). void NotifyOtherViews(int nType, const OString& rKey, const OString& rPayload) override; /// See OutlinerViewShell::NotifyOtherView(). diff --git a/include/tools/date.hxx b/include/tools/date.hxx index f2e492d8f15e..db6c1a9e7029 100644 --- a/include/tools/date.hxx +++ b/include/tools/date.hxx @@ -134,6 +134,9 @@ public: */ void AddDays( sal_Int32 nAddDays ); + // Same as AddDays, but returns false on int overflow cases + [[nodiscard]] bool CheckedAddDays(sal_Int32 nAddDays); + /** Obtain the day of the week for the date. Internally normalizes a copy of values. diff --git a/include/vcl/IDialogRenderable.hxx b/include/vcl/IDialogRenderable.hxx index 79ed78765212..aaa17cd7991c 100644 --- a/include/vcl/IDialogRenderable.hxx +++ b/include/vcl/IDialogRenderable.hxx @@ -40,6 +40,9 @@ public: /// Emits a LOK_CALLBACK_INVALIDATE_TILES. virtual void notifyInvalidation(tools::Rectangle const *) const = 0; + /// Emits a LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR. + virtual void notifyCursorInvalidation(tools::Rectangle const *, bool bControlEvent) const = 0; + /// Debugging virtual OString dumpNotifyState() const = 0; }; diff --git a/include/vcl/accessibility/AccessibleBrowseBox.hxx b/include/vcl/accessibility/AccessibleBrowseBox.hxx index fd070e44e034..f1b0a63dbe60 100644 --- a/include/vcl/accessibility/AccessibleBrowseBox.hxx +++ b/include/vcl/accessibility/AccessibleBrowseBox.hxx @@ -33,7 +33,7 @@ class VCL_DLLPUBLIC AccessibleBrowseBox : public AccessibleBrowseBoxBase public: AccessibleBrowseBox( - const css::uno::Reference< css::accessibility::XAccessible >& _rxParent, + const rtl::Reference<comphelper::OAccessible>& rpParent, ::vcl::IAccessibleTableProvider& _rBrowseBox ); @@ -107,21 +107,11 @@ public: /** returns the accessible object for the row or the column header bar */ - css::uno::Reference< - css::accessibility::XAccessible > - getHeaderBar( AccessibleBrowseBoxObjType _eObjType ) - { - return implGetHeaderBar(_eObjType); - } + rtl::Reference<comphelper::OAccessible> getHeaderBar(AccessibleBrowseBoxObjType eObjType); /** returns the accessible object for the table representation */ - css::uno::Reference< - css::accessibility::XAccessible > - getTable( ) - { - return implGetTable(); - } + rtl::Reference<comphelper::OAccessible> getTable(); protected: // internal virtual methods @@ -132,26 +122,11 @@ protected: // internal helper methods - /** This method creates (once) and returns the accessible data table child. - @attention This method requires locked mutex's and a living object. - @return The XAccessible interface of the data table. */ - css::uno::Reference< - css::accessibility::XAccessible > implGetTable(); - - /** This method creates (once) and returns the specified header bar. - @attention This method requires locked mutex's and a living object. - @return The XAccessible interface of the header bar. */ - css::uno::Reference< - css::accessibility::XAccessible > - implGetHeaderBar( AccessibleBrowseBoxObjType eObjType ); - /** This method returns one of the children that are always present: Data table, row and column header bar or corner control. @attention This method requires locked mutex's and a living object. - @return The XAccessible interface of the specified child. */ - css::uno::Reference< - css::accessibility::XAccessible > - implGetFixedChild( sal_Int64 nChildIndex ); + @return The accessible object of the specified child. */ + rtl::Reference<comphelper::OAccessible> implGetFixedChild(sal_Int64 nChildIndex); /** This method creates and returns an accessible table. @return An AccessibleBrowseBoxTable. */ diff --git a/include/vcl/metaact.hxx b/include/vcl/metaact.hxx index 0ca9b1fe87ca..62655d770b49 100644 --- a/include/vcl/metaact.hxx +++ b/include/vcl/metaact.hxx @@ -104,7 +104,6 @@ private: Color maColor; public: - SAL_DLLPRIVATE MetaPixelAction(); MetaPixelAction(MetaPixelAction const &) = default; MetaPixelAction(MetaPixelAction &&) = default; MetaPixelAction & operator =(MetaPixelAction const &) = delete; // due to MetaAction @@ -130,7 +129,6 @@ private: Point maPt; public: - SAL_DLLPRIVATE MetaPointAction(); MetaPointAction(MetaPointAction const &) = default; MetaPointAction(MetaPointAction &&) = default; MetaPointAction & operator =(MetaPointAction const &) = delete; // due to MetaAction @@ -158,7 +156,6 @@ private: Point maEndPt; public: - SAL_DLLPRIVATE MetaLineAction(); MetaLineAction(MetaLineAction const &) = default; MetaLineAction(MetaLineAction &&) = default; MetaLineAction & operator =(MetaLineAction const &) = delete; // due to MetaAction @@ -188,7 +185,6 @@ private: tools::Rectangle maRect; public: - SAL_DLLPRIVATE MetaRectAction(); MetaRectAction(MetaRectAction const &) = default; MetaRectAction(MetaRectAction &&) = default; MetaRectAction & operator =(MetaRectAction const &) = delete; // due to MetaAction @@ -216,7 +212,6 @@ private: sal_uInt32 mnVertRound; public: - SAL_DLLPRIVATE MetaRoundRectAction(); MetaRoundRectAction(MetaRoundRectAction const &) = default; MetaRoundRectAction(MetaRoundRectAction &&) = default; MetaRoundRectAction & operator =(MetaRoundRectAction const &) = delete; // due to MetaAction @@ -245,7 +240,6 @@ private: tools::Rectangle maRect; public: - SAL_DLLPRIVATE MetaEllipseAction(); MetaEllipseAction(MetaEllipseAction const &) = default; MetaEllipseAction(MetaEllipseAction &&) = default; MetaEllipseAction & operator =(MetaEllipseAction const &) = delete; // due to MetaAction @@ -273,7 +267,6 @@ private: Point maEndPt; public: - SAL_DLLPRIVATE MetaArcAction(); MetaArcAction(MetaArcAction const &) = default; MetaArcAction(MetaArcAction &&) = default; MetaArcAction & operator =(MetaArcAction const &) = delete; // due to MetaAction @@ -304,7 +297,6 @@ private: Point maEndPt; public: - SAL_DLLPRIVATE MetaPieAction(); MetaPieAction(MetaPieAction const &) = default; MetaPieAction(MetaPieAction &&) = default; MetaPieAction & operator =(MetaPieAction const &) = delete; // due to MetaAction @@ -335,7 +327,6 @@ private: Point maEndPt; public: - SAL_DLLPRIVATE MetaChordAction(); MetaChordAction(MetaChordAction const &) = default; MetaChordAction(MetaChordAction &&) = default; MetaChordAction & operator =(MetaChordAction const &) = delete; // due to MetaAction @@ -365,7 +356,6 @@ private: tools::Polygon maPoly; public: - SAL_DLLPRIVATE MetaPolyLineAction(); MetaPolyLineAction(MetaPolyLineAction const &) = default; MetaPolyLineAction(MetaPolyLineAction &&) = default; MetaPolyLineAction & operator =(MetaPolyLineAction const &) = delete; // due to MetaAction @@ -393,7 +383,6 @@ private: tools::Polygon maPoly; public: - SAL_DLLPRIVATE MetaPolygonAction(); MetaPolygonAction(MetaPolygonAction const &) = default; MetaPolygonAction(MetaPolygonAction &&) = default; MetaPolygonAction & operator =(MetaPolygonAction const &) = delete; // due to MetaAction @@ -419,7 +408,6 @@ private: tools::PolyPolygon maPolyPoly; public: - SAL_DLLPRIVATE MetaPolyPolygonAction(); MetaPolyPolygonAction(MetaPolyPolygonAction const &) = default; MetaPolyPolygonAction(MetaPolyPolygonAction &&) = default; MetaPolyPolygonAction & operator =(MetaPolyPolygonAction const &) = delete; // due to MetaAction @@ -448,7 +436,6 @@ private: sal_Int32 mnLen; public: - MetaTextAction(); MetaTextAction(MetaTextAction const &) = default; MetaTextAction(MetaTextAction &&) = default; MetaTextAction & operator =(MetaTextAction const &) = delete; // due to MetaAction @@ -487,7 +474,6 @@ private: SAL_DLLPRIVATE virtual ~MetaTextArrayAction() override; public: - SAL_DLLPRIVATE MetaTextArrayAction(); SAL_DLLPRIVATE MetaTextArrayAction( const MetaTextArrayAction& rAction ); SAL_DLLPRIVATE MetaTextArrayAction( const Point& rStartPt, OUString aStr, KernArray rDXAry, @@ -531,7 +517,6 @@ private: sal_Int32 mnLen; public: - MetaStretchTextAction(); MetaStretchTextAction(MetaStretchTextAction const &) = default; MetaStretchTextAction(MetaStretchTextAction &&) = default; MetaStretchTextAction & operator =(MetaStretchTextAction const &) = delete; // due to MetaAction @@ -565,7 +550,6 @@ private: DrawTextFlags mnStyle; public: - MetaTextRectAction(); MetaTextRectAction(MetaTextRectAction const &) = default; MetaTextRectAction(MetaTextRectAction &&) = default; MetaTextRectAction & operator =(MetaTextRectAction const &) = delete; // due to MetaAction @@ -598,7 +582,6 @@ private: FontLineStyle meOverline; public: - MetaTextLineAction(); MetaTextLineAction(MetaTextLineAction const &) = default; MetaTextLineAction(MetaTextLineAction &&) = default; MetaTextLineAction & operator =(MetaTextLineAction const &) = delete; // due to MetaAction @@ -631,7 +614,6 @@ private: Point maPt; public: - SAL_DLLPRIVATE MetaBmpAction(); MetaBmpAction(MetaBmpAction const &) = default; MetaBmpAction(MetaBmpAction &&) = default; MetaBmpAction & operator =(MetaBmpAction const &) = delete; // due to MetaAction @@ -660,7 +642,6 @@ private: Size maSz; public: - SAL_DLLPRIVATE MetaBmpScaleAction(); MetaBmpScaleAction(MetaBmpScaleAction const &) = default; MetaBmpScaleAction(MetaBmpScaleAction &&) = default; MetaBmpScaleAction & operator =(MetaBmpScaleAction const &) = delete; // due to MetaAction @@ -693,7 +674,6 @@ private: Size maSrcSz; public: - SAL_DLLPRIVATE MetaBmpScalePartAction(); MetaBmpScalePartAction(MetaBmpScalePartAction const &) = default; MetaBmpScalePartAction(MetaBmpScalePartAction &&) = default; MetaBmpScalePartAction & operator =(MetaBmpScalePartAction const &) = delete; // due to MetaAction @@ -726,7 +706,6 @@ private: Point maPt; public: - SAL_DLLPRIVATE MetaBmpExAction(); MetaBmpExAction(MetaBmpExAction const &) = default; MetaBmpExAction(MetaBmpExAction &&) = default; MetaBmpExAction & operator =(MetaBmpExAction const &) = delete; // due to MetaAction @@ -756,7 +735,6 @@ private: Size maSz; public: - SAL_DLLPRIVATE MetaBmpExScaleAction(); MetaBmpExScaleAction(MetaBmpExScaleAction const &) = default; MetaBmpExScaleAction(MetaBmpExScaleAction &&) = default; MetaBmpExScaleAction & operator =(MetaBmpExScaleAction const &) = delete; // due to MetaAction @@ -790,7 +768,6 @@ private: Size maSrcSz; public: - SAL_DLLPRIVATE MetaBmpExScalePartAction(); MetaBmpExScalePartAction(MetaBmpExScalePartAction const &) = default; MetaBmpExScalePartAction(MetaBmpExScalePartAction &&) = default; MetaBmpExScalePartAction & operator =(MetaBmpExScalePartAction const &) = delete; // due to MetaAction @@ -825,7 +802,6 @@ private: Point maPt; public: - MetaMaskAction(); MetaMaskAction(MetaMaskAction const &) = default; MetaMaskAction(MetaMaskAction &&) = default; MetaMaskAction & operator =(MetaMaskAction const &) = delete; // due to MetaAction @@ -858,7 +834,6 @@ private: Size maSz; public: - MetaMaskScaleAction(); MetaMaskScaleAction(MetaMaskScaleAction const &) = default; MetaMaskScaleAction(MetaMaskScaleAction &&) = default; MetaMaskScaleAction & operator =(MetaMaskScaleAction const &) = delete; // due to MetaAction @@ -894,7 +869,6 @@ private: Size maSrcSz; public: - MetaMaskScalePartAction(); MetaMaskScalePartAction(MetaMaskScalePartAction const &) = default; MetaMaskScalePartAction(MetaMaskScalePartAction &&) = default; MetaMaskScalePartAction & operator =(MetaMaskScalePartAction const &) = delete; // due to MetaAction @@ -929,7 +903,6 @@ private: Gradient maGradient; public: - MetaGradientAction(); MetaGradientAction(MetaGradientAction const &) = default; MetaGradientAction(MetaGradientAction &&) = default; MetaGradientAction & operator =(MetaGradientAction const &) = delete; // due to MetaAction @@ -957,7 +930,6 @@ private: Gradient maGradient; public: - SAL_DLLPRIVATE MetaGradientExAction(); MetaGradientExAction(MetaGradientExAction const &) = default; MetaGradientExAction(MetaGradientExAction &&) = default; MetaGradientExAction & operator =(MetaGradientExAction const &) = delete; // due to MetaAction @@ -985,7 +957,6 @@ private: Hatch maHatch; public: - MetaHatchAction(); MetaHatchAction(MetaHatchAction const &) = default; MetaHatchAction(MetaHatchAction &&) = default; MetaHatchAction & operator =(MetaHatchAction const &) = delete; // due to MetaAction @@ -1013,7 +984,6 @@ private: Wallpaper maWallpaper; public: - SAL_DLLPRIVATE MetaWallpaperAction(); MetaWallpaperAction(MetaWallpaperAction const &) = default; MetaWallpaperAction(MetaWallpaperAction &&) = default; MetaWallpaperAction & operator =(MetaWallpaperAction const &) = delete; // due to MetaAction @@ -1042,7 +1012,6 @@ private: bool mbClip; public: - SAL_DLLPRIVATE MetaClipRegionAction(); MetaClipRegionAction(MetaClipRegionAction const &) = default; MetaClipRegionAction(MetaClipRegionAction &&) = default; MetaClipRegionAction & operator =(MetaClipRegionAction const &) = delete; // due to MetaAction @@ -1069,7 +1038,6 @@ private: tools::Rectangle maRect; public: - SAL_DLLPRIVATE MetaISectRectClipRegionAction(); MetaISectRectClipRegionAction(MetaISectRectClipRegionAction const &) = default; MetaISectRectClipRegionAction(MetaISectRectClipRegionAction &&) = default; MetaISectRectClipRegionAction & operator =(MetaISectRectClipRegionAction const &) = delete; // due to MetaAction @@ -1095,7 +1063,6 @@ private: vcl::Region maRegion; public: - SAL_DLLPRIVATE MetaISectRegionClipRegionAction(); MetaISectRegionClipRegionAction(MetaISectRegionClipRegionAction const &) = default; MetaISectRegionClipRegionAction(MetaISectRegionClipRegionAction &&) = default; MetaISectRegionClipRegionAction & operator =(MetaISectRegionClipRegionAction const &) = delete; // due to MetaAction @@ -1122,7 +1089,6 @@ private: tools::Long mnVertMove; public: - SAL_DLLPRIVATE MetaMoveClipRegionAction(); MetaMoveClipRegionAction(MetaMoveClipRegionAction const &) = default; MetaMoveClipRegionAction(MetaMoveClipRegionAction &&) = default; MetaMoveClipRegionAction & operator =(MetaMoveClipRegionAction const &) = delete; // due to MetaAction @@ -1149,7 +1115,6 @@ private: bool mbSet; public: - SAL_DLLPRIVATE MetaLineColorAction(); MetaLineColorAction(MetaLineColorAction const &) = default; MetaLineColorAction(MetaLineColorAction &&) = default; MetaLineColorAction & operator =(MetaLineColorAction const &) = delete; // due to MetaAction @@ -1174,7 +1139,6 @@ private: bool mbSet; public: - SAL_DLLPRIVATE MetaFillColorAction(); MetaFillColorAction(MetaFillColorAction const &) = default; MetaFillColorAction(MetaFillColorAction &&) = default; MetaFillColorAction & operator =(MetaFillColorAction const &) = delete; // due to MetaAction @@ -1199,7 +1163,6 @@ private: Color maColor; public: - SAL_DLLPRIVATE MetaTextColorAction(); MetaTextColorAction(MetaTextColorAction const &) = default; MetaTextColorAction(MetaTextColorAction &&) = default; MetaTextColorAction & operator =(MetaTextColorAction const &) = delete; // due to MetaAction @@ -1223,7 +1186,6 @@ private: bool mbSet; public: - SAL_DLLPRIVATE MetaTextFillColorAction(); MetaTextFillColorAction(MetaTextFillColorAction const &) = default; MetaTextFillColorAction(MetaTextFillColorAction &&) = default; MetaTextFillColorAction & operator =(MetaTextFillColorAction const &) = delete; // due to MetaAction @@ -1249,7 +1211,6 @@ private: bool mbSet; public: - SAL_DLLPRIVATE MetaTextLineColorAction(); MetaTextLineColorAction(MetaTextLineColorAction const &) = default; MetaTextLineColorAction(MetaTextLineColorAction &&) = default; MetaTextLineColorAction & operator =(MetaTextLineColorAction const &) = delete; // due to MetaAction @@ -1274,7 +1235,6 @@ private: bool mbSet; public: - SAL_DLLPRIVATE MetaOverlineColorAction(); MetaOverlineColorAction(MetaOverlineColorAction const &) = default; MetaOverlineColorAction(MetaOverlineColorAction &&) = default; MetaOverlineColorAction & operator =(MetaOverlineColorAction const &) = delete; // due to MetaAction @@ -1298,7 +1258,6 @@ private: TextAlign maAlign; public: - SAL_DLLPRIVATE MetaTextAlignAction(); MetaTextAlignAction(MetaTextAlignAction const &) = default; MetaTextAlignAction(MetaTextAlignAction &&) = default; MetaTextAlignAction & operator =(MetaTextAlignAction const &) = delete; // due to MetaAction @@ -1354,7 +1313,6 @@ private: void correctFontScale(tools::Long nNewFontScale) { maFont.SetAverageFontWidth(nNewFontScale); } public: - SAL_DLLPRIVATE MetaFontAction(); MetaFontAction(MetaFontAction const &) = default; MetaFontAction(MetaFontAction &&) = default; MetaFontAction & operator =(MetaFontAction const &) = delete; // due to MetaAction @@ -1380,7 +1338,6 @@ private: vcl::PushFlags mnFlags; public: - SAL_DLLPRIVATE MetaPushAction(); MetaPushAction(MetaPushAction const &) = default; MetaPushAction(MetaPushAction &&) = default; MetaPushAction & operator =(MetaPushAction const &) = delete; // due to MetaAction @@ -1419,7 +1376,6 @@ private: RasterOp meRasterOp; public: - SAL_DLLPRIVATE MetaRasterOpAction(); MetaRasterOpAction(MetaRasterOpAction const &) = default; MetaRasterOpAction(MetaRasterOpAction &&) = default; MetaRasterOpAction & operator =(MetaRasterOpAction const &) = delete; // due to MetaAction @@ -1443,7 +1399,6 @@ private: sal_uInt16 mnTransPercent; public: - MetaTransparentAction(); MetaTransparentAction(MetaTransparentAction const &) = default; MetaTransparentAction(MetaTransparentAction &&) = default; MetaTransparentAction & operator =(MetaTransparentAction const &) = delete; // due to MetaAction @@ -1478,7 +1433,6 @@ private: std::optional<basegfx::BColorStops> maSVGTransparencyColorStops; public: - MetaFloatTransparentAction(); MetaFloatTransparentAction(MetaFloatTransparentAction const &) = default; MetaFloatTransparentAction(MetaFloatTransparentAction &&) = default; MetaFloatTransparentAction & operator =(MetaFloatTransparentAction const &) = delete; // due to MetaAction @@ -1517,7 +1471,6 @@ private: Size maSize; public: - MetaEPSAction(); MetaEPSAction(MetaEPSAction const &) = default; MetaEPSAction(MetaEPSAction &&) = default; MetaEPSAction & operator =(MetaEPSAction const &) = delete; // due to MetaAction @@ -1548,7 +1501,6 @@ private: bool mbSet; public: - SAL_DLLPRIVATE MetaRefPointAction(); MetaRefPointAction(MetaRefPointAction const &) = default; MetaRefPointAction(MetaRefPointAction &&) = default; MetaRefPointAction & operator =(MetaRefPointAction const &) = delete; // due to MetaAction @@ -1581,7 +1533,6 @@ private: SAL_DLLPRIVATE virtual ~MetaCommentAction() override; public: - SAL_DLLPRIVATE explicit MetaCommentAction(); SAL_DLLPRIVATE explicit MetaCommentAction( const MetaCommentAction& rAct ); explicit MetaCommentAction( OString aComment, sal_Int32 nValue = 0, const sal_uInt8* pData = nullptr, sal_uInt32 nDataSize = 0 ); @@ -1604,7 +1555,6 @@ private: vcl::text::ComplexTextLayoutFlags mnLayoutMode; public: - SAL_DLLPRIVATE MetaLayoutModeAction(); MetaLayoutModeAction(MetaLayoutModeAction const &) = default; MetaLayoutModeAction(MetaLayoutModeAction &&) = default; MetaLayoutModeAction & operator =(MetaLayoutModeAction const &) = delete; // due to MetaAction @@ -1627,7 +1577,6 @@ private: LanguageType meTextLanguage; public: - SAL_DLLPRIVATE MetaTextLanguageAction(); MetaTextLanguageAction(MetaTextLanguageAction const &) = default; MetaTextLanguageAction(MetaTextLanguageAction &&) = default; MetaTextLanguageAction & operator =(MetaTextLanguageAction const &) = delete; // due to MetaAction diff --git a/include/vcl/settings.hxx b/include/vcl/settings.hxx index b50bcc67293a..5c57fe8d9eba 100644 --- a/include/vcl/settings.hxx +++ b/include/vcl/settings.hxx @@ -123,8 +123,6 @@ public: void SetButtonRepeat( sal_Int32 nRepeat ); sal_Int32 GetButtonRepeat() const; - SAL_DLLPRIVATE static sal_Int32 GetActionDelay(); - void SetMenuDelay( sal_Int32 nDelay ); sal_Int32 GetMenuDelay() const; diff --git a/include/vcl/svapp.hxx b/include/vcl/svapp.hxx index 058402d4b5e1..ef908f61931b 100644 --- a/include/vcl/svapp.hxx +++ b/include/vcl/svapp.hxx @@ -1325,7 +1325,8 @@ public: const OUString& rAction, const std::vector<vcl::LOKPayloadItem>& rPayload = std::vector<vcl::LOKPayloadItem>()) const override; virtual void libreOfficeKitViewCallback(int nType, const OString& pPayload) const override; - virtual void notifyInvalidation(tools::Rectangle const *) const override; + virtual void notifyInvalidation(tools::Rectangle const* pRect) const override; + virtual void notifyCursorInvalidation(tools::Rectangle const* pRect, bool bControlEvent) const override; virtual OString dumpNotifyState() const override; private: diff --git a/include/vcl/toolkit/MenuButton.hxx b/include/vcl/toolkit/MenuButton.hxx index 79f0edd9d71a..29b2ff53a556 100644 --- a/include/vcl/toolkit/MenuButton.hxx +++ b/include/vcl/toolkit/MenuButton.hxx @@ -47,8 +47,6 @@ private: Link<MenuButton*,void> maActivateHdl; Link<MenuButton*,void> maSelectHdl; - DECL_DLLPRIVATE_LINK( ImplMenuTimeoutHdl, Timer*, void ); - MenuButton( const MenuButton & ) = delete; MenuButton& operator=( const MenuButton & ) = delete; diff --git a/include/vcl/toolkit/button.hxx b/include/vcl/toolkit/button.hxx index e1f10eb67242..4d8e6fdc8571 100644 --- a/include/vcl/toolkit/button.hxx +++ b/include/vcl/toolkit/button.hxx @@ -57,7 +57,6 @@ public: SAL_DLLPRIVATE const tools::Rectangle& ImplGetFocusRect() const; SAL_DLLPRIVATE void ImplSetSymbolAlign( SymbolAlign eAlign ); /// The x-coordinate of the vertical separator line, use in MenuButton subclass only. - SAL_DLLPRIVATE tools::Long ImplGetSeparatorX() const; SAL_DLLPRIVATE void ImplSetSeparatorX( tools::Long nX ); protected: diff --git a/include/vcl/window.hxx b/include/vcl/window.hxx index 06b5a5478446..8369693dd05f 100644 --- a/include/vcl/window.hxx +++ b/include/vcl/window.hxx @@ -768,6 +768,8 @@ public: void SetType( WindowType eType ); WindowType GetType() const; std::string_view GetTypeName() const; + bool IsFormControl() const; + void SetFormControl(bool bFormControl); bool IsSystemWindow() const; SAL_DLLPRIVATE bool IsDockingWindow() const; bool IsDialog() const; @@ -1442,7 +1444,14 @@ public: void SimulateKeyPress( sal_uInt16 nKeyCode ) const; virtual OUString GetSurroundingText() const; + + /** + * Return the non-normalized Selection, i.e. calling Selection::Min() on + * the returned Selection returns the selection anchor and Selection::Max() + * returns the cursor position. + */ virtual Selection GetSurroundingTextSelection() const; + virtual bool DeleteSurroundingText(const Selection& rSelection); virtual FactoryFunction GetUITestFactory() const; diff --git a/m4/ax_boost_system.m4 b/m4/ax_boost_system.m4 deleted file mode 100644 index 323e2a676a8e..000000000000 --- a/m4/ax_boost_system.m4 +++ /dev/null @@ -1,121 +0,0 @@ -# =========================================================================== -# https://www.gnu.org/software/autoconf-archive/ax_boost_system.html -# =========================================================================== -# -# SYNOPSIS -# -# AX_BOOST_SYSTEM -# -# DESCRIPTION -# -# Test for System library from the Boost C++ libraries. The macro requires -# a preceding call to AX_BOOST_BASE. Further documentation is available at -# <http://randspringer.de/boost/index.html>. -# -# This macro calls: -# -# AC_SUBST(BOOST_SYSTEM_LIB) -# -# And sets: -# -# HAVE_BOOST_SYSTEM -# -# LICENSE -# -# Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de> -# Copyright (c) 2008 Michael Tindal -# Copyright (c) 2008 Daniel Casimiro <dan.casimiro@gmail.com> -# -# Copying and distribution of this file, with or without modification, are -# permitted in any medium without royalty provided the copyright notice -# and this notice are preserved. This file is offered as-is, without any -# warranty. - -#serial 20 - -AC_DEFUN([AX_BOOST_SYSTEM], -[ - AC_ARG_WITH([boost-system], - AS_HELP_STRING([--with-boost-system@<:@=special-lib@:>@], - [use the System library from boost - it is possible to specify a certain library for the linker - e.g. --with-boost-system=boost_system-gcc-mt ]), - [ - if test "$withval" = "no"; then - want_boost="no" - elif test "$withval" = "yes"; then - want_boost="yes" - ax_boost_user_system_lib="" - else - want_boost="yes" - ax_boost_user_system_lib="$withval" - fi - ], - [want_boost="yes"] - ) - - if test "x$want_boost" = "xyes"; then - AC_REQUIRE([AC_PROG_CC]) - AC_REQUIRE([AC_CANONICAL_BUILD]) - CPPFLAGS_SAVED="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" - export CPPFLAGS - - LDFLAGS_SAVED="$LDFLAGS" - LDFLAGS="$LDFLAGS $BOOST_LDFLAGS" - export LDFLAGS - - AC_CACHE_CHECK(whether the Boost::System library is available, - ax_cv_boost_system, - [AC_LANG_PUSH([C++]) - CXXFLAGS_SAVE=$CXXFLAGS - CXXFLAGS= - - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <boost/system/error_code.hpp>]], - [[boost::system::error_category *a = 0;]])], - ax_cv_boost_system=yes, ax_cv_boost_system=no) - CXXFLAGS=$CXXFLAGS_SAVE - AC_LANG_POP([C++]) - ]) - if test "x$ax_cv_boost_system" = "xyes"; then - AC_SUBST(BOOST_CPPFLAGS) - - AC_DEFINE(HAVE_BOOST_SYSTEM,,[define if the Boost::System library is available]) - BOOSTLIBDIR=`echo $BOOST_LDFLAGS | sed -e 's/@<:@^\/@:>@*//'` - - LDFLAGS_SAVE=$LDFLAGS - if test "x$ax_boost_user_system_lib" = "x"; then - for libextension in `ls -r $BOOSTLIBDIR/libboost_system* 2>/dev/null | sed 's,.*/lib,,' | sed 's,\..*,,'` ; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], - [link_system="no"]) - done - if test "x$link_system" != "xyes"; then - for libextension in `ls -r $BOOSTLIBDIR/boost_system* 2>/dev/null | sed 's,.*/,,' | sed -e 's,\..*,,'` ; do - ax_lib=${libextension} - AC_CHECK_LIB($ax_lib, exit, - [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], - [link_system="no"]) - done - fi - - else - for ax_lib in $ax_boost_user_system_lib boost_system-$ax_boost_user_system_lib; do - AC_CHECK_LIB($ax_lib, exit, - [BOOST_SYSTEM_LIB="-l$ax_lib"; AC_SUBST(BOOST_SYSTEM_LIB) link_system="yes"; break], - [link_system="no"]) - done - - fi - if test "x$ax_lib" = "x"; then - AC_MSG_ERROR(Could not find a version of the Boost::System library!) - fi - if test "x$link_system" = "xno"; then - AC_MSG_ERROR(Could not link against $ax_lib !) - fi - fi - - CPPFLAGS="$CPPFLAGS_SAVED" - LDFLAGS="$LDFLAGS_SAVED" - fi -]) diff --git a/odk/examples/DevelopersGuide/FirstSteps/FirstLoadComponent/csharp/Makefile b/odk/examples/DevelopersGuide/FirstSteps/FirstLoadComponent/csharp/Makefile index 2cd58d246ac7..3387e8672cb0 100644 --- a/odk/examples/DevelopersGuide/FirstSteps/FirstLoadComponent/csharp/Makefile +++ b/odk/examples/DevelopersGuide/FirstSteps/FirstLoadComponent/csharp/Makefile @@ -24,6 +24,7 @@ DOTNET_FLAGS = -c Release LO_NUPKG_ID = LibreOffice.Bindings LO_NUPKG_VERSION = 0.1.0 LO_NUPKG_DIR = $(abspath $(PRJ)/dotnet) +TARGET_FRAMEWORK = net$(shell dotnet --version | cut -d. -f1-2) # Targets .PHONY: ALL @@ -36,7 +37,7 @@ $(APP_PROJ_FILE) : $(ECHO) "<Project Sdk=\"Microsoft.NET.Sdk\">" > $@ $(ECHO) " <PropertyGroup>" >> $@ $(ECHO) " <AssemblyName>$(APP_NAME)</AssemblyName>" >> $@ - $(ECHO) " <TargetFramework>net8.0</TargetFramework>" >> $@ + $(ECHO) " <TargetFramework>$(TARGET_FRAMEWORK)</TargetFramework>" >> $@ $(ECHO) " <OutputType>exe</OutputType>" >> $@ $(ECHO) " <PublishSingleFile>true</PublishSingleFile>" >> $@ $(ECHO) " <SelfContained>false</SelfContained>" >> $@ diff --git a/odk/examples/DevelopersGuide/FirstSteps/FirstUnoContact/csharp/Makefile b/odk/examples/DevelopersGuide/FirstSteps/FirstUnoContact/csharp/Makefile index c3257605e853..c4797f4f5b3b 100644 --- a/odk/examples/DevelopersGuide/FirstSteps/FirstUnoContact/csharp/Makefile +++ b/odk/examples/DevelopersGuide/FirstSteps/FirstUnoContact/csharp/Makefile @@ -24,6 +24,7 @@ DOTNET_FLAGS = -c Release LO_NUPKG_ID = LibreOffice.Bindings LO_NUPKG_VERSION = 0.1.0 LO_NUPKG_DIR = $(abspath $(PRJ)/dotnet) +TARGET_FRAMEWORK = net$(shell dotnet --version | cut -d. -f1-2) # Targets .PHONY: ALL @@ -36,7 +37,7 @@ $(APP_PROJ_FILE) : $(ECHO) "<Project Sdk=\"Microsoft.NET.Sdk\">" > $@ $(ECHO) " <PropertyGroup>" >> $@ $(ECHO) " <AssemblyName>$(APP_NAME)</AssemblyName>" >> $@ - $(ECHO) " <TargetFramework>net8.0</TargetFramework>" >> $@ + $(ECHO) " <TargetFramework>$(TARGET_FRAMEWORK)</TargetFramework>" >> $@ $(ECHO) " <OutputType>exe</OutputType>" >> $@ $(ECHO) " <PublishSingleFile>true</PublishSingleFile>" >> $@ $(ECHO) " <SelfContained>false</SelfContained>" >> $@ diff --git a/odk/examples/DevelopersGuide/FirstSteps/HelloTextTableShape/csharp/Makefile b/odk/examples/DevelopersGuide/FirstSteps/HelloTextTableShape/csharp/Makefile index b30bfb7afcbd..bdc0fd054178 100644 --- a/odk/examples/DevelopersGuide/FirstSteps/HelloTextTableShape/csharp/Makefile +++ b/odk/examples/DevelopersGuide/FirstSteps/HelloTextTableShape/csharp/Makefile @@ -24,6 +24,7 @@ DOTNET_FLAGS = -c Release LO_NUPKG_ID = LibreOffice.Bindings LO_NUPKG_VERSION = 0.1.0 LO_NUPKG_DIR = $(abspath $(PRJ)/dotnet) +TARGET_FRAMEWORK = net$(shell dotnet --version | cut -d. -f1-2) # Targets .PHONY: ALL @@ -36,7 +37,7 @@ $(APP_PROJ_FILE) : $(ECHO) "<Project Sdk=\"Microsoft.NET.Sdk\">" > $@ $(ECHO) " <PropertyGroup>" >> $@ $(ECHO) " <AssemblyName>$(APP_NAME)</AssemblyName>" >> $@ - $(ECHO) " <TargetFramework>net8.0</TargetFramework>" >> $@ + $(ECHO) " <TargetFramework>$(TARGET_FRAMEWORK)</TargetFramework>" >> $@ $(ECHO) " <OutputType>exe</OutputType>" >> $@ $(ECHO) " <PublishSingleFile>true</PublishSingleFile>" >> $@ $(ECHO) " <SelfContained>false</SelfContained>" >> $@ diff --git a/odk/examples/DevelopersGuide/OfficeDev/PathSettings/csharp/Makefile b/odk/examples/DevelopersGuide/OfficeDev/PathSettings/csharp/Makefile index 5509df52153a..81d0d3d5e60b 100644 --- a/odk/examples/DevelopersGuide/OfficeDev/PathSettings/csharp/Makefile +++ b/odk/examples/DevelopersGuide/OfficeDev/PathSettings/csharp/Makefile @@ -24,6 +24,7 @@ DOTNET_FLAGS = -c Release LO_NUPKG_ID = LibreOffice.Bindings LO_NUPKG_VERSION = 0.1.0 LO_NUPKG_DIR = $(abspath $(PRJ)/dotnet) +TARGET_FRAMEWORK = net$(shell dotnet --version | cut -d. -f1-2) # Targets .PHONY: ALL @@ -36,7 +37,7 @@ $(APP_PROJ_FILE) : $(ECHO) "<Project Sdk=\"Microsoft.NET.Sdk\">" > $@ $(ECHO) " <PropertyGroup>" >> $@ $(ECHO) " <AssemblyName>$(APP_NAME)</AssemblyName>" >> $@ - $(ECHO) " <TargetFramework>net8.0</TargetFramework>" >> $@ + $(ECHO) " <TargetFramework>$(TARGET_FRAMEWORK)</TargetFramework>" >> $@ $(ECHO) " <OutputType>exe</OutputType>" >> $@ $(ECHO) " <PublishSingleFile>true</PublishSingleFile>" >> $@ $(ECHO) " <SelfContained>false</SelfContained>" >> $@ diff --git a/odk/examples/DevelopersGuide/OfficeDev/PathSubstitution/csharp/Makefile b/odk/examples/DevelopersGuide/OfficeDev/PathSubstitution/csharp/Makefile index 363c4ca92587..1477f7da5de9 100644 --- a/odk/examples/DevelopersGuide/OfficeDev/PathSubstitution/csharp/Makefile +++ b/odk/examples/DevelopersGuide/OfficeDev/PathSubstitution/csharp/Makefile @@ -24,6 +24,7 @@ DOTNET_FLAGS = -c Release LO_NUPKG_ID = LibreOffice.Bindings LO_NUPKG_VERSION = 0.1.0 LO_NUPKG_DIR = $(abspath $(PRJ)/dotnet) +TARGET_FRAMEWORK = net$(shell dotnet --version | cut -d. -f1-2) # Targets .PHONY: ALL @@ -36,7 +37,7 @@ $(APP_PROJ_FILE) : $(ECHO) "<Project Sdk=\"Microsoft.NET.Sdk\">" > $@ $(ECHO) " <PropertyGroup>" >> $@ $(ECHO) " <AssemblyName>$(APP_NAME)</AssemblyName>" >> $@ - $(ECHO) " <TargetFramework>net8.0</TargetFramework>" >> $@ + $(ECHO) " <TargetFramework>$(TARGET_FRAMEWORK)</TargetFramework>" >> $@ $(ECHO) " <OutputType>exe</OutputType>" >> $@ $(ECHO) " <PublishSingleFile>true</PublishSingleFile>" >> $@ $(ECHO) " <SelfContained>false</SelfContained>" >> $@ diff --git a/odk/examples/DevelopersGuide/OfficeDev/TerminationTest/csharp/Makefile b/odk/examples/DevelopersGuide/OfficeDev/TerminationTest/csharp/Makefile index 76d685fd3dcc..a4977c7709f7 100644 --- a/odk/examples/DevelopersGuide/OfficeDev/TerminationTest/csharp/Makefile +++ b/odk/examples/DevelopersGuide/OfficeDev/TerminationTest/csharp/Makefile @@ -24,6 +24,7 @@ DOTNET_FLAGS = -c Release LO_NUPKG_ID = LibreOffice.Bindings LO_NUPKG_VERSION = 0.1.0 LO_NUPKG_DIR = $(abspath $(PRJ)/dotnet) +TARGET_FRAMEWORK = net$(shell dotnet --version | cut -d. -f1-2) # Targets .PHONY: ALL @@ -36,7 +37,7 @@ $(APP_PROJ_FILE) : $(ECHO) "<Project Sdk=\"Microsoft.NET.Sdk\">" > $@ $(ECHO) " <PropertyGroup>" >> $@ $(ECHO) " <AssemblyName>$(APP_NAME)</AssemblyName>" >> $@ - $(ECHO) " <TargetFramework>net8.0</TargetFramework>" >> $@ + $(ECHO) " <TargetFramework>$(TARGET_FRAMEWORK)</TargetFramework>" >> $@ $(ECHO) " <OutputType>exe</OutputType>" >> $@ $(ECHO) " <PublishSingleFile>true</PublishSingleFile>" >> $@ $(ECHO) " <SelfContained>false</SelfContained>" >> $@ diff --git a/odk/examples/dotnet/WriterDemo/csharp/Makefile b/odk/examples/dotnet/WriterDemo/csharp/Makefile index 28992ec130d4..7bc34acf597b 100644 --- a/odk/examples/dotnet/WriterDemo/csharp/Makefile +++ b/odk/examples/dotnet/WriterDemo/csharp/Makefile @@ -24,6 +24,7 @@ DOTNET_FLAGS = -c Release LO_NUPKG_ID = LibreOffice.Bindings LO_NUPKG_VERSION = 0.1.0 LO_NUPKG_DIR = $(abspath $(PRJ)/dotnet) +TARGET_FRAMEWORK = net$(shell dotnet --version | cut -d. -f1-2) # Targets .PHONY: ALL @@ -36,7 +37,7 @@ $(APP_PROJ_FILE) : $(ECHO) "<Project Sdk=\"Microsoft.NET.Sdk\">" > $@ $(ECHO) " <PropertyGroup>" >> $@ $(ECHO) " <AssemblyName>$(APP_NAME)</AssemblyName>" >> $@ - $(ECHO) " <TargetFramework>net8.0</TargetFramework>" >> $@ + $(ECHO) " <TargetFramework>$(TARGET_FRAMEWORK)</TargetFramework>" >> $@ $(ECHO) " <OutputType>exe</OutputType>" >> $@ $(ECHO) " <PublishSingleFile>true</PublishSingleFile>" >> $@ $(ECHO) " <SelfContained>false</SelfContained>" >> $@ diff --git a/odk/examples/dotnet/WriterDemo/fsharp/Makefile b/odk/examples/dotnet/WriterDemo/fsharp/Makefile index d3aaad0c6499..83fc278d7f91 100644 --- a/odk/examples/dotnet/WriterDemo/fsharp/Makefile +++ b/odk/examples/dotnet/WriterDemo/fsharp/Makefile @@ -24,6 +24,7 @@ DOTNET_FLAGS = -c Release LO_NUPKG_ID = LibreOffice.Bindings LO_NUPKG_VERSION = 0.1.0 LO_NUPKG_DIR = $(abspath $(PRJ)/dotnet) +TARGET_FRAMEWORK = net$(shell dotnet --version | cut -d. -f1-2) # Targets .PHONY: ALL @@ -36,7 +37,7 @@ $(APP_PROJ_FILE) : $(ECHO) "<Project Sdk=\"Microsoft.NET.Sdk\">" > $@ $(ECHO) " <PropertyGroup>" >> $@ $(ECHO) " <AssemblyName>$(APP_NAME)</AssemblyName>" >> $@ - $(ECHO) " <TargetFramework>net8.0</TargetFramework>" >> $@ + $(ECHO) " <TargetFramework>$(TARGET_FRAMEWORK)</TargetFramework>" >> $@ $(ECHO) " <OutputType>exe</OutputType>" >> $@ $(ECHO) " <PublishSingleFile>true</PublishSingleFile>" >> $@ $(ECHO) " <SelfContained>false</SelfContained>" >> $@ diff --git a/odk/examples/dotnet/WriterDemo/vbasic/Makefile b/odk/examples/dotnet/WriterDemo/vbasic/Makefile index 01014c71a820..0bd37f6f219c 100644 --- a/odk/examples/dotnet/WriterDemo/vbasic/Makefile +++ b/odk/examples/dotnet/WriterDemo/vbasic/Makefile @@ -24,6 +24,7 @@ DOTNET_FLAGS = -c Release LO_NUPKG_ID = LibreOffice.Bindings LO_NUPKG_VERSION = 0.1.0 LO_NUPKG_DIR = $(abspath $(PRJ)/dotnet) +TARGET_FRAMEWORK = net$(shell dotnet --version | cut -d. -f1-2) # Targets .PHONY: ALL @@ -36,7 +37,7 @@ $(APP_PROJ_FILE) : $(ECHO) "<Project Sdk=\"Microsoft.NET.Sdk\">" > $@ $(ECHO) " <PropertyGroup>" >> $@ $(ECHO) " <AssemblyName>$(APP_NAME)</AssemblyName>" >> $@ - $(ECHO) " <TargetFramework>net8.0</TargetFramework>" >> $@ + $(ECHO) " <TargetFramework>$(TARGET_FRAMEWORK)</TargetFramework>" >> $@ $(ECHO) " <OutputType>exe</OutputType>" >> $@ $(ECHO) " <PublishSingleFile>true</PublishSingleFile>" >> $@ $(ECHO) " <SelfContained>false</SelfContained>" >> $@ diff --git a/rust_uno/.gitignore b/rust_uno/.gitignore new file mode 100644 index 000000000000..310213a05bc3 --- /dev/null +++ b/rust_uno/.gitignore @@ -0,0 +1,3 @@ +/target +Cargo.lock +generated/ diff --git a/rust_uno/Cargo.toml b/rust_uno/Cargo.toml new file mode 100644 index 000000000000..f54f9bc3c85e --- /dev/null +++ b/rust_uno/Cargo.toml @@ -0,0 +1,20 @@ +# -*- Mode: toml; tab-width: 4; indent-tabs-mode: nil; 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/. +# + +[package] +name = "rust_uno" +version = "0.1.0" +edition = "2024" +description = "Rust FFI binding for LibreOffice UNO API" + +[lib] +name = "rust_uno" +crate-type = ["cdylib"] + +# vim: set noet sw=4 ts=4: diff --git a/rust_uno/CustomTarget_cargo.mk b/rust_uno/CustomTarget_cargo.mk new file mode 100644 index 000000000000..d27ec0b0797a --- /dev/null +++ b/rust_uno/CustomTarget_cargo.mk @@ -0,0 +1,18 @@ +# -*- 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_CustomTarget_CustomTarget,rust_uno/cargo)) + +.PHONY: $(call gb_CustomTarget_get_target,rust_uno/cargo) +$(call gb_CustomTarget_get_target,rust_uno/cargo): \ + $(call gb_Library_get_target,rust_uno-cpp) \ + $(gb_CustomTarget_workdir)/rust_uno/rustmaker/cpp + cd $(SRCDIR)/rust_uno && cargo build $(if $(verbose),--verbose,) --release + +# vim: set noet sw=4 ts=4: diff --git a/rust_uno/CustomTarget_rustmaker.mk b/rust_uno/CustomTarget_rustmaker.mk new file mode 100644 index 000000000000..b519be3c627c --- /dev/null +++ b/rust_uno/CustomTarget_rustmaker.mk @@ -0,0 +1,31 @@ +# -*- 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_CustomTarget_CustomTarget,rust_uno/rustmaker)) + +$(call gb_CustomTarget_get_target,rust_uno/rustmaker): \ + $(gb_CustomTarget_workdir)/rust_uno/rustmaker/cpp + +$(gb_CustomTarget_workdir)/rust_uno/rustmaker/cpp: \ + $(call gb_Executable_get_target,rustmaker) \ + $(call gb_Executable_get_runtime_dependencies,rustmaker) \ + $(call gb_UnoApi_get_target,offapi) \ + $(call gb_UnoApi_get_target,udkapi) \ + $(gb_CustomTarget_workdir)/rust_uno/rustmaker/.dir + rm -fr $(SRCDIR)/rust_uno/src/generated + mkdir $(SRCDIR)/rust_uno/src/generated + rm -fr $(gb_CustomTarget_workdir)/rust_uno/rustmaker/cpp + mkdir $(gb_CustomTarget_workdir)/rust_uno/rustmaker/cpp + $(call gb_Helper_abbreviate_dirs, \ + $(call gb_Helper_execute,rustmaker $(if $(verbose),--verbose,) -Ocpp $(gb_CustomTarget_workdir)/rust_uno/rustmaker/cpp \ + $(if $(verbose),--verbose,) -Orust $(SRCDIR)/rust_uno/src/generated \ + $(call gb_UnoApi_get_target,offapi) $(call gb_UnoApi_get_target,udkapi))) + touch $@ + +# vim: set noet sw=4 ts=4: diff --git a/rust_uno/Extension_rust_uno-example.mk b/rust_uno/Extension_rust_uno-example.mk new file mode 100644 index 000000000000..fea1677e1966 --- /dev/null +++ b/rust_uno/Extension_rust_uno-example.mk @@ -0,0 +1,26 @@ +# -*- 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_Extension_Extension,rust_uno-example,rust_uno/example,nodeliver)) + +$(eval $(call gb_Extension_add_file,rust_uno-example,platform.components,$(call gb_Rdb_get_target,rust_uno-example))) + +$(eval $(call gb_Extension_add_files,rust_uno-example,, \ + $(SRCDIR)/rust_uno/example/Addons.xcu \ + $(SRCDIR)/rust_uno/example/ProtocolHandler.xcu \ + $(SRCDIR)/rust_uno/target/release/librust_uno.so \ +)) + +$(eval $(call gb_Extension_add_libraries,rust_uno-example, \ + rust_uno-example \ +)) + +$(SRCDIR)/rust_uno/target/release/librust_uno.so: $(call gb_CustomTarget_get_target,rust_uno/cargo) + +# vim: set noet sw=4 ts=4: diff --git a/rust_uno/Library_rust_uno-cpp.mk b/rust_uno/Library_rust_uno-cpp.mk new file mode 100644 index 000000000000..4a1875015b7f --- /dev/null +++ b/rust_uno/Library_rust_uno-cpp.mk @@ -0,0 +1,41 @@ +# -*- 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_Library_Library,rust_uno-cpp)) + +$(eval $(call gb_Library_use_libraries,rust_uno-cpp, \ + cppu \ + cppuhelper \ + sal \ +)) + +$(eval $(call gb_Library_use_sdk_api,rust_uno-cpp)) + +# Add the uno_bootstrap.cxx file for bootstrap functionality +$(eval $(call gb_Library_add_exception_objects,rust_uno-cpp, \ + rust_uno/uno_bootstrap \ +)) + +# Combined generated files instead of thousands of individual files +rust_uno_generated_cxx = \ + rust_uno_bindings + +define rust_uno_add_generated_cxx +$(gb_CustomTarget_workdir)/rust_uno/rustmaker/cpp/$(1).cxx: \ + $(call gb_CustomTarget_get_target,rust_uno/rustmaker) + +$(eval $(call gb_Library_add_generated_exception_objects,rust_uno-cpp, \ + CustomTarget/rust_uno/rustmaker/cpp/$(1) \ +)) + +endef + +$(foreach gencxx,$(rust_uno_generated_cxx),$(eval $(call rust_uno_add_generated_cxx,$(gencxx)))) + +# vim: set noet sw=4 ts=4: diff --git a/rust_uno/Library_rust_uno-example.mk b/rust_uno/Library_rust_uno-example.mk new file mode 100644 index 000000000000..bee03c23c3b6 --- /dev/null +++ b/rust_uno/Library_rust_uno-example.mk @@ -0,0 +1,37 @@ +# -*- 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_Library_Library,rust_uno-example)) + +$(eval $(call gb_Library_add_exception_objects,rust_uno-example, \ + rust_uno/example/example \ +)) + +$(eval $(call gb_Library_set_componentfile,rust_uno-example,rust_uno/example/example,rust_uno-example)) + +$(eval $(call gb_Library_set_external_code,rust_uno-example)) + +$(eval $(call gb_Library_use_externals,rust_uno-example, \ + boost_headers \ +)) + +$(eval $(call gb_Library_use_libraries,rust_uno-example, \ + cppu \ + cppuhelper \ + sal \ +)) + +$(eval $(call gb_Library_use_sdk_api,rust_uno-example)) + +$(call gb_Library_get_target,rust_uno-example): $(call gb_CustomTarget_get_target,rust_uno/cargo) +$(eval $(call gb_Library_add_libs,rust_uno-example,\ + $(SRCDIR)/rust_uno/target/release/librust_uno.so \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/rust_uno/Makefile b/rust_uno/Makefile new file mode 100644 index 000000000000..d5aa252aa8c3 --- /dev/null +++ b/rust_uno/Makefile @@ -0,0 +1,14 @@ +# -*- 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/. +# + +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/rust_uno/Module_rust_uno.mk b/rust_uno/Module_rust_uno.mk new file mode 100644 index 000000000000..ab6fa695a0cc --- /dev/null +++ b/rust_uno/Module_rust_uno.mk @@ -0,0 +1,22 @@ +# -*- 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_Module_Module,rust_uno)) + +ifeq ($(ENABLE_RUST_UNO),TRUE) +$(eval $(call gb_Module_add_targets,rust_uno, \ + CustomTarget_cargo \ + CustomTarget_rustmaker \ + Extension_rust_uno-example \ + Library_rust_uno-cpp \ + Library_rust_uno-example \ +)) +endif + +# vim: set noet sw=4 ts=4: diff --git a/rust_uno/build.rs b/rust_uno/build.rs new file mode 100644 index 000000000000..5d25a125b4fc --- /dev/null +++ b/rust_uno/build.rs @@ -0,0 +1,39 @@ +/* -*- Mode: rust; 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/. + */ + +//! Build script for rust_uno crate +//! +//! Links the generated C++ bridge library (librust_uno-cpp.so) to the Rust cdylib + +fn main() { + // Tell cargo to link against the rust_uno-cpp library + // This library contains the generated C++ bridge functions + println!("cargo:rustc-link-lib=rust_uno-cpplo"); + + // Add the LibreOffice instdir/program directory to the library search path + // This is where librust_uno-cpplo.so is located + if let Ok(instdir) = std::env::var("INSTDIR") { + println!("cargo:rustc-link-search=native={}/program", instdir); + } + + // Also try the workdir path where the library might be during build + if let Ok(workdir) = std::env::var("WORKDIR") { + println!( + "cargo:rustc-link-search=native={}/LinkTarget/Library", + workdir + ); + } + + // Fallback: try relative paths from the rust_uno directory + println!("cargo:rustc-link-search=native=../instdir/program"); + println!("cargo:rustc-link-search=native=../workdir/LinkTarget/Library"); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ + diff --git a/rust_uno/example/Addons.xcu b/rust_uno/example/Addons.xcu new file mode 100644 index 000000000000..9d906a2a4e9f --- /dev/null +++ b/rust_uno/example/Addons.xcu @@ -0,0 +1,43 @@ +<?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 . + --> + +<o:items xmlns:o="http://openoffice.org/2001/registry"> + <item o:path="/org.openoffice.Office.Addons"> + <node o:name="AddonUI"> + <node o:name="OfficeMenuBar"> + <node o:name="org.libreoffice.rust_uno.example" + o:op="replace"> + <prop o:name="Title" xml:lang="en-US"> + <value>Rust UNO</value> + </prop> + <node o:name="Submenu"> + <node o:name="1" o:op="replace"> + <prop o:name="URL"> + <value>vnd.org.libreoffice.rust_uno.example:</value> + </prop> + <prop o:name="Title" xml:lang="en-US"> + <value>Example</value> + </prop> + </node> + </node> + </node> + </node> + </node> + </item> +</o:items> diff --git a/rust_uno/example/META-INF/manifest.xml b/rust_uno/example/META-INF/manifest.xml new file mode 100644 index 000000000000..51af7893f844 --- /dev/null +++ b/rust_uno/example/META-INF/manifest.xml @@ -0,0 +1,28 @@ +<?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 . + --> + +<m:manifest xmlns:m="http://openoffice.org/2001/manifest"> + <m:file-entry m:media-type="application/vnd.sun.star.configuration-data" + m:full-path="Addons.xcu"/> + <m:file-entry m:media-type="application/vnd.sun.star.configuration-data" + m:full-path="ProtocolHandler.xcu"/> + <m:file-entry + m:media-type="application/vnd.sun.star.uno-components;platform=@PLATFORM@" + m:full-path="platform.components"/> +</m:manifest> diff --git a/rust_uno/example/ProtocolHandler.xcu b/rust_uno/example/ProtocolHandler.xcu new file mode 100644 index 000000000000..2d205f7f878f --- /dev/null +++ b/rust_uno/example/ProtocolHandler.xcu @@ -0,0 +1,29 @@ +<?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 . + --> + +<o:component-data xmlns:o="http://openoffice.org/2001/registry" + o:package="org.openoffice.Office" o:name="ProtocolHandler"> + <node o:name="HandlerSet"> + <node o:name="org.libreoffice.rust_uno.example" o:op="replace"> + <prop o:name="Protocols"> + <value>vnd.org.libreoffice.rust_uno.example:*</value> + </prop> + </node> + </node> +</o:component-data> diff --git a/rust_uno/example/description.xml b/rust_uno/example/description.xml new file mode 100644 index 000000000000..526680a84dbd --- /dev/null +++ b/rust_uno/example/description.xml @@ -0,0 +1,26 @@ +<?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 . + --> + +<d:description xmlns:d="http://openoffice.org/extensions/description/2006"> + <d:identifier value="org.libreoffice/rust_uno/example"/> + <d:version value="1"/> + <d:dependencies> + <d:OpenOffice.org-minimal-version d:name="OpenOffice.org 3.4" value="3.4"/> + </d:dependencies> +</d:description> diff --git a/rust_uno/example/example.component b/rust_uno/example/example.component new file mode 100644 index 000000000000..da779dac84c0 --- /dev/null +++ b/rust_uno/example/example.component @@ -0,0 +1,28 @@ +<?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" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="org.libreoffice.comp.rust_uno.example"> + <service name="org.libreoffice.rust_uno.example"/> + </implementation> + <implementation name="org.libreoffice.comp.rust_uno.example_singleton"> + <singleton name="org.libreoffice.rust_uno.example_singleton"/> + </implementation> +</component> diff --git a/rust_uno/example/example.cxx b/rust_uno/example/example.cxx new file mode 100644 index 000000000000..5fc0279281d5 --- /dev/null +++ b/rust_uno/example/example.cxx @@ -0,0 +1,243 @@ +/* -*- 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 <sal/config.h> + +#include <cassert> + +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/frame/DispatchDescriptor.hpp> +#include <com/sun/star/frame/Desktop.hpp> +#include <com/sun/star/frame/XDispatch.hpp> +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XStatusListener.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/DeploymentException.hpp> +#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 <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <com/sun/star/util/URL.hpp> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/implbase2.hxx> +#include <cppuhelper/implementationentry.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <rtl/ustring.hxx> +#include <sal/log.hxx> +#include <sal/types.h> +#include <uno/lbnames.h> + +#include "rust_uno_hook.hxx" + +namespace +{ +class Provider + : public cppu::WeakImplHelper2<css::lang::XServiceInfo, css::frame::XDispatchProvider> +{ +public: + Provider(const Provider&) = delete; + const Provider& operator=(const Provider&) = delete; + + static css::uno::Reference<css::uno::XInterface> + SAL_CALL static_create(css::uno::Reference<css::uno::XComponentContext> const& xContext) + { + return static_cast<cppu::OWeakObject*>(new Provider(xContext)); + } + + static rtl::OUString SAL_CALL static_getImplementationName(); + + static css::uno::Sequence<rtl::OUString> SAL_CALL static_getSupportedServiceNames(); + +private: + explicit Provider(css::uno::Reference<css::uno::XComponentContext> const& context) + : context_(context) + { + assert(context.is()); + } + + virtual ~Provider() {} + + virtual rtl::OUString SAL_CALL getImplementationName() override + { + return static_getImplementationName(); + } + + virtual sal_Bool SAL_CALL supportsService(rtl::OUString const& ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence<rtl::OUString> SAL_CALL getSupportedServiceNames() override + { + return static_getSupportedServiceNames(); + } + + virtual css::uno::Reference<css::frame::XDispatch> + SAL_CALL queryDispatch(css::util::URL const&, rtl::OUString const&, sal_Int32) override; + + virtual css::uno::Sequence<css::uno::Reference<css::frame::XDispatch>> SAL_CALL + queryDispatches(css::uno::Sequence<css::frame::DispatchDescriptor> const& Requests) override; + + css::uno::Reference<css::uno::XComponentContext> context_; +}; + +rtl::OUString Provider::static_getImplementationName() +{ + return rtl::OUString("org.libreoffice.comp.rust_uno.example"); +} + +css::uno::Sequence<rtl::OUString> Provider::static_getSupportedServiceNames() +{ + rtl::OUString name("org.libreoffice.rust_uno.example"); + return css::uno::Sequence<rtl::OUString>(&name, 1); +} + +css::uno::Reference<css::frame::XDispatch> Provider::queryDispatch(css::util::URL const&, + rtl::OUString const&, sal_Int32) +{ + css::uno::Reference<css::frame::XDispatch> dispatch; + if (!(context_->getValueByName("/singletons/org.libreoffice.rust_uno.example_singleton") + >>= dispatch) + || !dispatch.is()) + { + throw css::uno::DeploymentException("component context fails to supply singleton " + "org.libreoffice.rust_uno.example_singleton of type " + "com.sun.star.frame.XDispatch", + context_); + } + return dispatch; +} + +css::uno::Sequence<css::uno::Reference<css::frame::XDispatch>> +Provider::queryDispatches(css::uno::Sequence<css::frame::DispatchDescriptor> const& Requests) +{ + css::uno::Sequence<css::uno::Reference<css::frame::XDispatch>> s(Requests.getLength()); + for (sal_Int32 i = 0; i < s.getLength(); ++i) + { + s[i] + = queryDispatch(Requests[i].FeatureURL, Requests[i].FrameName, Requests[i].SearchFlags); + } + return s; +} + +class Dispatch : public cppu::WeakImplHelper2<css::lang::XServiceInfo, css::frame::XDispatch> +{ +public: + Dispatch(const Dispatch&) = delete; + const Dispatch& operator=(const Dispatch&) = delete; + + static css::uno::Reference<css::uno::XInterface> + SAL_CALL static_create(css::uno::Reference<css::uno::XComponentContext> const& xContext) + { + return static_cast<cppu::OWeakObject*>(new Dispatch(xContext)); + } + + static rtl::OUString SAL_CALL static_getImplementationName(); + + static css::uno::Sequence<rtl::OUString> SAL_CALL static_getSupportedServiceNames() + { + return css::uno::Sequence<rtl::OUString>(); + } + +private: + explicit Dispatch(css::uno::Reference<css::uno::XComponentContext> const& context) + : context_(context) + { + assert(context.is()); + } + + virtual ~Dispatch() {} + + virtual rtl::OUString SAL_CALL getImplementationName() override + { + return static_getImplementationName(); + } + + virtual sal_Bool SAL_CALL supportsService(rtl::OUString const& ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + virtual css::uno::Sequence<rtl::OUString> SAL_CALL getSupportedServiceNames() override + { + return static_getSupportedServiceNames(); + } + + virtual void SAL_CALL dispatch(css::util::URL const&, + css::uno::Sequence<css::beans::PropertyValue> const&) override; + + virtual void SAL_CALL addStatusListener(css::uno::Reference<css::frame::XStatusListener> const&, + css::util::URL const&) override + { + } + + virtual void SAL_CALL removeStatusListener( + css::uno::Reference<css::frame::XStatusListener> const&, css::util::URL const&) override + { + } + + css::uno::Reference<css::uno::XComponentContext> context_; +}; + +rtl::OUString Dispatch::static_getImplementationName() +{ + return rtl::OUString("org.libreoffice.comp.rust_uno.example_singleton"); +} + +void Dispatch::dispatch(css::util::URL const&, css::uno::Sequence<css::beans::PropertyValue> const&) +{ + // === RUST UNO BINDING TEST === + // Call our Rust UNO binding to test it works within LibreOffice + SAL_INFO("desktop.app", "Testing Rust UNO binding..."); + try + { + run_rust_uno_test(); + SAL_INFO("desktop.app", "Rust UNO binding test completed successfully!"); + } + catch (...) + { + SAL_WARN("desktop.app", "Rust UNO binding test failed!"); + } +} + +cppu::ImplementationEntry const services[] = { + { &Provider::static_create, &Provider::static_getImplementationName, + &Provider::static_getSupportedServiceNames, &cppu::createSingleComponentFactory, nullptr, 0 }, + { &Dispatch::static_create, &Dispatch::static_getImplementationName, + &Dispatch::static_getSupportedServiceNames, &cppu::createSingleComponentFactory, nullptr, 0 }, + { nullptr, nullptr, nullptr, nullptr, nullptr, 0 } +}; +} + +extern "C" SAL_DLLPUBLIC_EXPORT void* +component_getFactory(char const* pImplName, void* pServiceManager, void* pRegistryKey) +{ + return cppu::component_getFactoryHelper(pImplName, pServiceManager, pRegistryKey, services); +} + +extern "C" SAL_DLLPUBLIC_EXPORT void +component_getImplementationEnvironment(char const** ppEnvTypeName, uno_Environment**) +{ + *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/include/vcl/bitmap/BitmapAlphaClampFilter.hxx b/rust_uno/example/rust_uno_hook.hxx index 4a036263e0d9..8987ffeb8b4f 100644 --- a/include/vcl/bitmap/BitmapAlphaClampFilter.hxx +++ b/rust_uno/example/rust_uno_hook.hxx @@ -5,27 +5,13 @@ * 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 <vcl/bitmap/BitmapFilter.hxx> - -/** If the alpha is beyond a certain threshold, make it fully transparent - */ -class VCL_DLLPUBLIC BitmapAlphaClampFilter final : public BitmapFilter -{ -public: - BitmapAlphaClampFilter(sal_uInt8 cThreshold) - : mcThreshold(cThreshold) - { - } - - virtual Bitmap execute(Bitmap const& rBitmap) const override; - -private: - sal_uInt8 mcThreshold; -}; +extern "C" { +/// Function from the Rust UNO binding library that we'll call from LibreOffice +void run_rust_uno_test(); +} /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/core/any.rs b/rust_uno/src/core/any.rs new file mode 100644 index 000000000000..7e307239720b --- /dev/null +++ b/rust_uno/src/core/any.rs @@ -0,0 +1,257 @@ +/* -*- Mode: rust; 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/. + */ + +//! UNO Any Types and Functions +//! +//! This module provides the high-level safe Rust wrapper around LibreOffice's uno_Any. +//! It includes automatic memory management and safe conversions to/from Rust values. +//! +//! ## Key Components +//! - `Any` - Safe Rust wrapper around LibreOffice's uno_Any +//! - Value conversion functions with automatic type handling +//! - Memory management with automatic cleanup +//! +//! ## Features +//! - Safe construction from various Rust types +//! - Automatic memory management via RAII +//! - Safe value extraction with type checking +//! - Clone, equality, and display trait implementations +//! - Direct FFI interop with raw pointer access methods + +use crate::ffi::uno_any::*; +// Interface operations now use generated XInterface wrapper methods +// TODO: Replace with generated XInterface::from_ptr(ptr).acquire() pattern + +// === Safe UNO Any Wrapper === + +/// UNO Any wrapper - a safe Rust wrapper around LibreOffice's uno_Any +/// +/// Any provides a safe, memory-managed interface to LibreOffice's native +/// any type. It automatically handles reference counting and memory cleanup. +pub struct Any { + inner: uno_Any, +} + +impl Any { + /// Create a new empty UNO Any (void type) + /// + /// This creates an empty Any that contains no value (void type). + pub fn new() -> Self { + unsafe { + let mut any = Any { + inner: uno_Any { + pType: std::ptr::null_mut(), + pData: std::ptr::null_mut(), + pReserved: std::ptr::null_mut(), + }, + }; + + // Initialize as void using uno_any_construct + uno_any_construct( + &mut any.inner, + std::ptr::null_mut(), // pSource (null for void) + std::ptr::null_mut(), // pTypeDescr (null for void) + None, + ); + any + } + } + + /// Create Any from a boolean value + /// + /// This creates an Any containing a boolean value. + pub fn from_bool(value: bool) -> Self { + unsafe { + let mut any = Any { + inner: uno_Any { + pType: std::ptr::null_mut(), + pData: std::ptr::null_mut(), + pReserved: std::ptr::null_mut(), + }, + }; + + let bool_val = if value { 1u8 } else { 0u8 }; + + // Initialize with boolean data using uno_type_any_construct + uno_type_any_construct( + &mut any.inner, + &bool_val as *const u8 as *mut std::ffi::c_void, + get_boolean_type(), + None, + ); + any + } + } + + /// Create Any from a 32-bit integer + /// + /// This creates an Any containing a 32-bit signed integer. + pub fn from_i32(value: i32) -> Self { + unsafe { + let mut any = Any { + inner: uno_Any { + pType: std::ptr::null_mut(), + pData: std::ptr::null_mut(), + pReserved: std::ptr::null_mut(), + }, + }; + + // Initialize with integer data using uno_type_any_construct + uno_type_any_construct( + &mut any.inner, + &value as *const i32 as *mut std::ffi::c_void, + get_long_type(), + None, + ); + + any + } + } + + /// Create Any from raw uno_Any (takes ownership) + /// + /// # Safety + /// The caller must ensure that: + /// - The uno_Any is valid and properly initialized + /// - The uno_Any is not used elsewhere after this call + /// - Proper reference counting is maintained + pub unsafe fn from_raw(any: uno_Any) -> Self { + Any { inner: any } + } + + /// Convert to raw uno_Any (releases ownership) + /// + /// The caller becomes responsible for calling uno_any_destruct + pub fn into_raw(self) -> uno_Any { + unsafe { + let inner = std::ptr::read(&self.inner); + std::mem::forget(self); // Don't run destructor + inner + } + } + + /// Get the raw pointer for FFI calls (retains ownership) + pub fn as_ptr(&self) -> *const uno_Any { + &self.inner + } + + /// Get the mutable raw pointer for FFI calls (retains ownership) + pub fn as_mut_ptr(&mut self) -> *mut uno_Any { + &mut self.inner + } + + /// Check if the Any contains a value (not void) + pub fn has_value(&self) -> bool { + !self.inner.pType.is_null() + } + + /// Clear the Any (set to void) + pub fn clear(&mut self) { + unsafe { + uno_any_clear(&mut self.inner, None); + } + } +} + +impl Default for Any { + fn default() -> Self { + Self::new() + } +} + +/// Clone implementation: Create a copy of another Any +/// +/// This creates a new Any that contains the same value but is independently +/// managed (proper deep copy with reference counting). +impl Clone for Any { + fn clone(&self) -> Self { + let mut new_any = Any::new(); + + if self.has_value() { + unsafe { + // Clone by assigning from the existing any's data using type reference + uno_type_any_assign( + &mut new_any.inner, + self.inner.pData, + self.inner.pType, + None, + None, + ); + } + } + + new_any + } +} + +/// Comparison with other Any +impl PartialEq for Any { + fn eq(&self, other: &Self) -> bool { + // Handle cases where one or both are void + match (self.has_value(), other.has_value()) { + (false, false) => true, // Both void = equal + (false, true) | (true, false) => false, // One void, one not = not equal + (true, true) => { + // Both have values - basic pointer comparison for now + self.inner.pType == other.inner.pType && self.inner.pData == other.inner.pData + } + } + } +} + +impl Eq for Any {} + +/// From trait implementation for bool +impl From<bool> for Any { + fn from(value: bool) -> Self { + Self::from_bool(value) + } +} + +/// From trait implementation for i32 +impl From<i32> for Any { + fn from(value: i32) -> Self { + Self::from_i32(value) + } +} + +/// Display trait for printing +impl std::fmt::Display for Any { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if !self.has_value() { + write!(f, "Any(void)") + } else { + write!(f, "Any(<value>)") + } + } +} + +/// Debug trait for debugging +impl std::fmt::Debug for Any { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Any") + .field("has_value", &self.has_value()) + .field("pType", &self.inner.pType) + .field("pData", &self.inner.pData) + .finish() + } +} + +/// Destructor: Automatic cleanup when Any goes out of scope +impl Drop for Any { + fn drop(&mut self) { + if self.has_value() { + unsafe { + uno_any_destruct(&mut self.inner, None); + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/core/mod.rs b/rust_uno/src/core/mod.rs new file mode 100644 index 000000000000..0546fdf51917 --- /dev/null +++ b/rust_uno/src/core/mod.rs @@ -0,0 +1,37 @@ +/* -*- Mode: rust; 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/. + */ + +//! Core Module +//! +//! This module contains the high-level safe Rust wrappers for LibreOffice UNO types. +//! These provide memory-safe, idiomatic Rust interfaces to the underlying C APIs. + +pub mod any; +pub mod oustring; +pub mod sequence; +pub mod r#type; +pub mod uno_wrapper; + +// Re-export the main types for convenience +pub use crate::ffi::type_ffi::typelib_TypeClass; +pub use any::Any; +pub use oustring::OUString; +pub use sequence::Sequence; +pub use r#type::Type; +// UnoInterface replaced with generated XInterface from rustmaker +pub use uno_wrapper::{UnoError, UnoResult, defaultBootstrap_InitialComponentContext}; + +// Include unit tests +#[cfg(test)] +mod tests { + mod string_tests; + mod type_tests; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/core/oustring.rs b/rust_uno/src/core/oustring.rs new file mode 100644 index 000000000000..8be5188168e1 --- /dev/null +++ b/rust_uno/src/core/oustring.rs @@ -0,0 +1,256 @@ +/* -*- Mode: rust; 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/. + */ + +//! UNO String Types and Functions +//! +//! This module provides the high-level safe Rust wrapper around LibreOffice's rtl_uString. +//! It includes automatic memory management and safe conversions to/from Rust strings. +//! +//! ## Key Components +//! - `OUString` - Safe Rust wrapper around LibreOffice's rtl_uString +//! - String conversion functions with automatic encoding handling +//! - Memory management with automatic reference counting +//! +//! ## Features +//! - UTF-8, UTF-16, and ASCII string creation methods +//! - Automatic memory management via RAII +//! - Safe conversion to/from Rust strings +//! - Clone, equality, and display trait implementations +//! - Direct FFI interop with raw pointer access methods + +use std::ptr::NonNull; + +use crate::ffi::rtl_string::*; +use crate::ffi::sal_types::*; + +// === Safe UNO String Wrapper === + +/// UNO String wrapper - a safe Rust wrapper around LibreOffice's rtl_uString +/// +/// OUString provides a safe, memory-managed interface to LibreOffice's native +/// string type. It automatically handles reference counting and memory cleanup, +/// and provides conversions to/from standard Rust string types. +pub struct OUString { + inner: NonNull<rtl_uString>, +} + +impl OUString { + /// Create a new empty UNO string + /// + /// This creates an empty OUString with zero length. + pub fn new() -> Self { + unsafe { + let mut ptr: *mut rtl_uString = std::ptr::null_mut(); + rtl_uString_new(&mut ptr); + + // Convert to NonNull, panic if allocation failed + let inner = + NonNull::new(ptr).expect("RTL string allocation failed - system out of memory"); + + OUString { inner } + } + } + + /// Create a UNO string from UTF-8 text + /// + /// This uses LibreOffice's direct UTF-8 to UString conversion function, + /// which is more efficient than converting through UTF-16. Handles all + /// valid UTF-8 including Unicode characters and emojis. + pub fn from_utf8(text: &str) -> Self { + unsafe { + let mut ptr: *mut rtl_uString = std::ptr::null_mut(); + rtl_string2UString( + &mut ptr, + text.as_ptr() as *const std::os::raw::c_char, + text.len() as sal_Int32, + RTL_TEXTENCODING_UTF8, + OSTRING_TO_OUSTRING_CVTFLAGS, + ); + + let inner = NonNull::new(ptr).expect("RTL UTF-8 string creation failed"); + + OUString { inner } + } + } + + /// Create a UNO string from ASCII text + /// + /// This method uses LibreOffice's native ASCII conversion function for + /// actual ASCII text (7-bit characters only). More efficient than UTF-8 + /// conversion when you know the text is pure ASCII. + pub fn from_ascii(text: &str) -> Self { + unsafe { + let mut ptr: *mut rtl_uString = std::ptr::null_mut(); + // Ensure the string is null-terminated for the C function + let c_string = std::ffi::CString::new(text).expect("CString creation failed"); + rtl_uString_newFromAscii(&mut ptr, c_string.as_ptr()); + + let inner = NonNull::new(ptr).expect("RTL ASCII string creation failed"); + + OUString { inner } + } + } + + /// Create OUString from UTF-16 data + /// + /// Creates a UNO string directly from UTF-16 data. This is the most efficient + /// method when you already have UTF-16 data, since LibreOffice uses UTF-16 + /// internally (sal_Unicode is UTF-16 code units). + pub fn from_utf16(data: &[sal_Unicode]) -> Self { + unsafe { + let mut ptr: *mut rtl_uString = std::ptr::null_mut(); + rtl_uString_newFromStr_WithLength(&mut ptr, data.as_ptr(), data.len() as sal_Int32); + + let inner = NonNull::new(ptr).expect("RTL UTF-16 string creation failed"); + + OUString { inner } + } + } + + /// Create OUString from raw rtl_uString pointer (takes ownership) + /// + /// # Safety + /// The caller must ensure that: + /// - The pointer is valid and points to a properly initialized rtl_uString + /// - The pointer is not used elsewhere after this call + /// - The rtl_uString has the correct reference count + pub unsafe fn from_raw(str: *mut rtl_uString) -> Self { + let inner = NonNull::new(str).expect("Cannot create OUString from null pointer"); + + unsafe { + rtl_uString_acquire(inner.as_ptr()); + } + OUString { inner } + } + + /// Convert to raw rtl_uString pointer (releases ownership) + /// + /// The caller becomes responsible for calling rtl_uString_release + pub fn into_raw(self) -> *mut rtl_uString { + let ptr = self.inner.as_ptr(); + std::mem::forget(self); // Don't run destructor + ptr + } + + /// Get the raw pointer for FFI calls (retains ownership) + pub fn as_ptr(&self) -> *const rtl_uString { + self.inner.as_ptr() as *const _ + } + + /// Get the mutable raw pointer for FFI calls (retains ownership) + pub fn as_mut_ptr(&mut self) -> *mut rtl_uString { + self.inner.as_ptr() + } + + /// Get the length of the string + pub fn len(&self) -> usize { + unsafe { rtl_uString_getLength(self.inner.as_ptr()) as usize } + } + + /// Check if the string is empty + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl Default for OUString { + fn default() -> Self { + Self::new() + } +} + +/// Clone implementation: Create a copy of another OUString +/// +/// This creates a new OUString that shares the same content but is independently +/// managed (proper deep copy with reference counting). +impl Clone for OUString { + fn clone(&self) -> Self { + unsafe { + rtl_uString_acquire(self.inner.as_ptr()); + OUString { inner: self.inner } + } + } +} + +/// From trait implementation for &str (UTF-8) +/// +/// Convenient conversion from string slices to OUString using UTF-8 encoding. +impl<T: AsRef<str>> From<T> for OUString { + fn from(text: T) -> Self { + Self::from_utf8(text.as_ref()) + } +} + +/// Comparison with other OUString +impl PartialEq for OUString { + fn eq(&self, other: &Self) -> bool { + unsafe { + // Compare lengths first (fast check) + let self_len = rtl_uString_getLength(self.inner.as_ptr()); + let other_len = rtl_uString_getLength(other.inner.as_ptr()); + + if self_len != other_len { + return false; + } + + // If lengths are equal, compare UTF-16 data directly + if self_len == 0 { + return true; // Both empty strings + } + + let self_ptr = rtl_uString_getStr(self.inner.as_ptr()); + let other_ptr = rtl_uString_getStr(other.inner.as_ptr()); + + // Compare UTF-16 data byte by byte + let self_slice = std::slice::from_raw_parts(self_ptr, self_len as usize); + let other_slice = std::slice::from_raw_parts(other_ptr, other_len as usize); + + self_slice == other_slice + } + } +} + +impl Eq for OUString {} + +/// Display trait for printing +impl std::fmt::Display for OUString { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.is_empty() { + return Ok(()); + } + + unsafe { + let ptr = rtl_uString_getStr(self.inner.as_ptr()); + let len = rtl_uString_getLength(self.inner.as_ptr()) as usize; + + // Convert from UTF-16 to UTF-8 + let utf16_slice = std::slice::from_raw_parts(ptr, len); + let string = String::from_utf16_lossy(utf16_slice); + write!(f, "{string}") + } + } +} + +/// Debug trait for debugging - shows type and content for UNO string identification +impl std::fmt::Debug for OUString { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("OUString").field(&self.to_string()).finish() + } +} + +/// Destructor: Automatic cleanup when OUString goes out of scope +impl Drop for OUString { + fn drop(&mut self) { + unsafe { + rtl_uString_release(self.inner.as_ptr()); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/core/sequence.rs b/rust_uno/src/core/sequence.rs new file mode 100644 index 000000000000..f9d8a237b0a2 --- /dev/null +++ b/rust_uno/src/core/sequence.rs @@ -0,0 +1,245 @@ +/* -*- Mode: rust; 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/. + */ + +//! UNO Sequence Types and Functions +//! +//! This module provides the high-level safe Rust wrapper around LibreOffice's uno_Sequence. +//! It includes automatic memory management and safe conversions to/from Rust values. +//! +//! ## Key Components +//! - `Sequence` - Safe Rust wrapper around LibreOffice's uno_Sequence +//! - Value conversion functions with automatic type handling +//! - Memory management with automatic cleanup +//! +//! ## Features +//! - Safe construction from various Rust types +//! - Automatic memory management via RAII +//! - Safe value extraction with type checking +//! - Clone, equality, and display trait implementations +//! - Direct FFI interop with raw pointer access methods + +use crate::core::r#type::*; +use crate::ffi::type_ffi::*; +// Interface operations now use generated XInterface wrapper methods +// TODO: Replace with generated XInterface::from_ptr(ptr).acquire() pattern +use crate::ffi::uno_sequence::*; +use std::ptr::NonNull; + +// === Safe UNO Sequence Wrapper === + +/// UNO Sequence wrapper - a safe Rust wrapper around LibreOffice's uno_Sequence +/// +/// Sequence provides a safe, memory-managed interface to LibreOffice's native +/// Sequence type. It automatically handles reference counting and memory cleanup. +pub struct Sequence { + inner: NonNull<uno_Sequence>, +} + +impl Sequence { + /// Create a new empty Sequence + /// + /// Creates an empty sequence of Any elements, similar to the C++ default constructor. + /// The sequence will be empty (0 elements) and uses the Any type as the element type. + pub fn new() -> Self { + Self::with_capacity(0) + } + + pub fn with_capacity(capacity: i32) -> Self { + if capacity < 0 { + panic!("Sequence capacity cannot be negative: {}", capacity); + } + unsafe { + let mut ptr: *mut uno_Sequence = std::ptr::null_mut(); + + // Create an Any type for the sequence elements + let element_type = Type::new(); + + // Get the type description from the type reference + let mut type_desc = std::ptr::null_mut(); + typelib_typedescriptionreference_getDescription( + &mut type_desc, + element_type.get_typelib_type(), + ); + + // Construct empty sequence + let success = uno_type_sequence_construct( + &mut ptr, + type_desc, + std::ptr::null(), + capacity, + None, // TODO: Replace with generated XInterface acquire wrapper + ); + + // Check for construction success + if success == 0 { + panic!("Failed to construct UNO sequence"); + } + + Sequence { + inner: NonNull::new(ptr) + .expect("uno_type_sequence_construct returned null pointer"), + } + } + } + + /// Get the length of the sequence + /// + /// Returns the number of elements in this sequence. + /// Equivalent to C++ `getLength()` method. + pub fn get_length(&self) -> i32 { + unsafe { self.inner.as_ref().nElements } + } + + /// Check if the sequence has elements + /// + /// Returns true if the sequence contains at least one element. + /// Equivalent to C++ `hasElements()` method. + pub fn has_elements(&self) -> bool { + self.get_length() > 0 + } + + /// Get the length as usize for Rust iterator compatibility + /// + /// Returns the length as usize, which is commonly used in Rust. + pub fn len(&self) -> usize { + self.get_length() as usize + } + + /// Check if the sequence is empty + /// + /// Returns true if the sequence has no elements. + pub fn is_empty(&self) -> bool { + self.get_length() == 0 + } + + /// Get raw pointer to the underlying uno_Sequence + /// + /// This provides direct access to the underlying UNO sequence for FFI interop. + /// The returned pointer remains valid as long as this Sequence instance exists. + pub fn as_raw(&self) -> *mut uno_Sequence { + self.inner.as_ptr() + } + + /// Get the raw pointer for FFI calls (retains ownership) + pub fn as_ptr(&self) -> *const uno_Sequence { + self.inner.as_ptr() as *const _ + } + + /// Get the mutable raw pointer for FFI calls (retains ownership) + pub fn as_mut_ptr(&mut self) -> *mut uno_Sequence { + self.inner.as_ptr() + } +} + +impl Default for Sequence { + fn default() -> Self { + Self::new() + } +} + +/// Clone implementation: Create an independent copy of another Sequence +/// +/// This creates a new Sequence that references the same sequence data but is +/// independently managed. The clone operation increments the reference count +/// of the underlying sequence, ensuring proper memory management. +impl Clone for Sequence { + fn clone(&self) -> Self { + unsafe { + let ptr = self.inner.as_ptr(); + // Increment reference count + (*ptr).nRefCount += 1; + + Sequence { + inner: NonNull::new(ptr).expect("Cloning sequence with invalid pointer"), + } + } + } +} + +/// Equality comparison with other Sequence instances +/// +/// Two Sequence instances are considered equal if they have the same length +/// and contain the same elements in the same order. This uses pointer comparison +/// for efficiency when both sequences reference the same underlying data. +impl PartialEq for Sequence { + fn eq(&self, other: &Self) -> bool { + unsafe { + let self_ptr = self.inner.as_ptr(); + let other_ptr = other.inner.as_ptr(); + + // Fast path: same pointer + if self_ptr == other_ptr { + return true; + } + + let self_seq = self.inner.as_ref(); + let other_seq = other.inner.as_ref(); + + // Different lengths means not equal + if self_seq.nElements != other_seq.nElements { + return false; + } + + // For now, do basic comparison - in a full implementation, + // this would need to compare actual element values + self_seq.nElements == other_seq.nElements + } + } +} + +impl Eq for Sequence {} + +/// Debug trait for debugging - shows sequence length and type for UNO sequence identification +impl std::fmt::Debug for Sequence { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("Sequence") + .field(&format!("length={}", self.get_length())) + .finish() + } +} + +/// Display trait for printing sequences +/// +/// Provides a user-friendly string representation of the sequence. +impl std::fmt::Display for Sequence { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.is_empty() { + write!(f, "Sequence[]") + } else { + write!(f, "Sequence[{}]", self.get_length()) + } + } +} + +/// Destructor: Automatic cleanup when Sequence goes out of scope +impl Drop for Sequence { + fn drop(&mut self) { + unsafe { + let ptr = self.inner.as_ptr(); + let seq = &mut *ptr; + seq.nRefCount -= 1; + + // If reference count reaches zero, destroy the sequence + if seq.nRefCount == 0 { + // Get the element type for proper cleanup + let element_type = Type::new(); + let mut type_desc = std::ptr::null_mut(); + typelib_typedescriptionreference_getDescription( + &mut type_desc, + element_type.get_typelib_type(), + ); + + // Call UNO sequence destructor + uno_type_sequence_destroy(ptr, type_desc, None); // TODO: Replace with generated XInterface release wrapper + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/core/tests/string_tests.rs b/rust_uno/src/core/tests/string_tests.rs new file mode 100644 index 000000000000..b931dfa6a2ba --- /dev/null +++ b/rust_uno/src/core/tests/string_tests.rs @@ -0,0 +1,387 @@ +/* -*- Mode: rust; 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/. + */ + +//! Unit tests for OUString (Rust-side functionality) +//! +//! These tests verify OUString's Rust implementation including: +//! - Creation methods (new, from_utf8, from_ascii, from_utf16) +//! - Trait implementations (Clone, PartialEq, From, Display, Debug, Default) +//! - Instance methods (len, is_empty, as_ptr, etc.) +//! - Memory management and safety (RAII, reference counting) +//! - FFI interop (from_raw, into_raw) +//! +//! These tests do NOT require LibreOffice UNO components and test +//! only the Rust-side implementation and FFI safety. + +use crate::core::OUString; + +// === Creation Method Tests === + +#[test] +fn test_oustring_new() { + let s = OUString::new(); + assert_eq!(s.len(), 0); + assert!(s.is_empty()); + assert_eq!(s.to_string(), ""); +} + +#[test] +fn test_oustring_default() { + let s = OUString::default(); + assert_eq!(s.len(), 0); + assert!(s.is_empty()); + assert_eq!(s.to_string(), ""); +} + +#[test] +fn test_oustring_from_utf8() { + let s = OUString::from_utf8("Hello, 世界!"); + assert!(!s.is_empty()); + assert_eq!(s.to_string(), "Hello, 世界!"); + + // Test empty string + let empty = OUString::from_utf8(""); + assert!(empty.is_empty()); + assert_eq!(empty.len(), 0); + + // Test with Unicode characters + let unicode = OUString::from_utf8("Ñiño 🦀 café"); + assert_eq!(unicode.to_string(), "Ñiño 🦀 café"); +} + +#[test] +fn test_oustring_from_ascii() { + let s = OUString::from_ascii("Hello, World!"); + assert_eq!(s.to_string(), "Hello, World!"); + assert!(!s.is_empty()); + + // Test empty ASCII string + let empty = OUString::from_ascii(""); + assert!(empty.is_empty()); + assert_eq!(empty.len(), 0); +} + +// === Trait Implementation Tests === + +#[test] +fn test_oustring_clone() { + let original = OUString::from_utf8("Test string for cloning"); + let cloned = original.clone(); + + // They should be equal + assert_eq!(original, cloned); + assert_eq!(original.to_string(), cloned.to_string()); + assert_eq!(original.len(), cloned.len()); + + // Test cloning empty string + let empty = OUString::new(); + let empty_clone = empty.clone(); + assert_eq!(empty, empty_clone); + assert!(empty_clone.is_empty()); +} + +#[test] +fn test_oustring_partial_eq() { + let s1 = OUString::from_utf8("Hello"); + let s2 = OUString::from_utf8("Hello"); + let s3 = OUString::from_utf8("World"); + let empty1 = OUString::new(); + let empty2 = OUString::new(); + + // Equal strings + assert_eq!(s1, s2); + assert_eq!(empty1, empty2); + + // Different strings + assert_ne!(s1, s3); + assert_ne!(s1, empty1); + assert_ne!(empty1, s1); + + // Test with Unicode + let unicode1 = OUString::from_utf8("café"); + let unicode2 = OUString::from_utf8("café"); + let unicode3 = OUString::from_utf8("cafe"); // without accent + + assert_eq!(unicode1, unicode2); + assert_ne!(unicode1, unicode3); +} + +#[test] +fn test_oustring_from_str() { + let s = OUString::from("Hello from &str"); + assert_eq!(s.to_string(), "Hello from &str"); + + // Test with Unicode + let unicode: OUString = "Héllo wørld 🌍".into(); + assert_eq!(unicode.to_string(), "Héllo wørld 🌍"); +} + +#[test] +fn test_oustring_from_string() { + let rust_string = String::from("Hello from String"); + let s = OUString::from(rust_string); + assert_eq!(s.to_string(), "Hello from String"); + + // Test with owned Unicode String + let unicode_string = "Ñiño 🦀".to_string(); + let unicode_oustring = OUString::from(unicode_string); + assert_eq!(unicode_oustring.to_string(), "Ñiño 🦀"); +} + +#[test] +fn test_oustring_display() { + let s = OUString::from_utf8("Display test"); + assert_eq!(format!("{s}"), "Display test"); + + // Test empty string display + let empty = OUString::new(); + assert_eq!(format!("{empty}"), ""); + + // Test Unicode display + let unicode = OUString::from_utf8("Display 测试 🦀"); + assert_eq!(format!("{unicode}"), "Display 测试 🦀"); +} + +#[test] +fn test_oustring_debug() { + let s = OUString::from_utf8("Debug test"); + let debug_output = format!("{s:?}"); + assert!(debug_output.contains("OUString")); + assert!(debug_output.contains("Debug test")); + + // Test empty string debug + let empty = OUString::new(); + let empty_debug = format!("{empty:?}"); + assert!(empty_debug.contains("OUString")); +} + +// === Method Tests === + +#[test] +fn test_oustring_len() { + let empty = OUString::new(); + assert_eq!(empty.len(), 0); + + let ascii = OUString::from_utf8("Hello"); + assert_eq!(ascii.len(), 5); + + // Note: len() returns UTF-16 code units, not Unicode codepoints + let unicode = OUString::from_utf8("café"); // 'é' is one UTF-16 unit + assert_eq!(unicode.len(), 4); + + // Emoji takes 2 UTF-16 code units (surrogate pair) + let emoji = OUString::from_utf8("🦀"); + assert_eq!(emoji.len(), 2); +} + +#[test] +fn test_oustring_is_empty() { + let empty = OUString::new(); + assert!(empty.is_empty()); + + let non_empty = OUString::from_utf8("Not empty"); + assert!(!non_empty.is_empty()); + + let empty_from_str = OUString::from_utf8(""); + assert!(empty_from_str.is_empty()); +} + +// === Special Cases and Edge Cases === + +#[test] +fn test_oustring_special_characters() { + // Test various special Unicode characters + let special_chars = vec![ + "\0", // Null character + "\n", // Newline + "\r\n", // Windows line ending + "\t", // Tab + "\"", // Quote + "\\", // Backslash + "©", // Copyright symbol + "€", // Euro symbol + "™", // Trademark + "🦀", // Rust crab emoji + "👨💻", // Man technologist (complex emoji) + ]; + + for special in special_chars { + let s = OUString::from_utf8(special); + assert_eq!(s.to_string(), special); + } +} + +#[test] +fn test_oustring_long_strings() { + // Test with long strings to ensure proper memory management + let long_text = "A".repeat(10000); + let s = OUString::from_utf8(&long_text); + assert_eq!(s.len(), 10000); + assert_eq!(s.to_string(), long_text); + + // Test cloning long strings + let cloned = s.clone(); + assert_eq!(cloned, s); + assert_eq!(cloned.len(), 10000); +} + +#[test] +fn test_oustring_multilingual() { + // Test various languages and scripts + let multilingual = "English, 中文, العربية, Русский, ελληνικά, עברית"; + let s = OUString::from_utf8(multilingual); + assert_eq!(s.to_string(), multilingual); + assert!(!s.is_empty()); +} + +#[test] +fn test_oustring_consistency_across_constructors() { + let text = "Test consistency"; + + let from_utf8 = OUString::from_utf8(text); + let from_str: OUString = text.into(); + let from_string = OUString::from(text.to_string()); + + // All should produce equivalent strings + assert_eq!(from_utf8, from_str); + assert_eq!(from_utf8, from_string); + assert_eq!(from_str, from_string); + + // All should have same content + assert_eq!(from_utf8.to_string(), text); + assert_eq!(from_str.to_string(), text); + assert_eq!(from_string.to_string(), text); +} + +#[test] +fn test_oustring_memory_safety() { + // Test that dropping strings doesn't cause issues + { + let s1 = OUString::from_utf8("Temporary string 1"); + let s2 = s1.clone(); + let s3 = OUString::from_utf8("Temporary string 2"); + + assert_eq!(s1, s2); + assert_ne!(s1, s3); + + // Strings will be dropped here - this should not cause issues + } + + // Create new strings after the previous ones were dropped + let s4 = OUString::from_utf8("After drop test"); + assert_eq!(s4.to_string(), "After drop test"); +} + +#[test] +fn test_oustring_memory_release() { + // Simple memory release test - create and drop many strings + // If memory isn't released properly, this would cause memory leaks + + // Create many strings in a loop and let them drop + for i in 0..1000 { + let s = OUString::from_utf8(&format!("Test string number {i}")); + assert_eq!(s.to_string(), format!("Test string number {i}")); + // String automatically drops here + } + + // Create and explicitly drop strings + for _ in 0..1000 { + let s1 = OUString::from_utf8("Temporary string"); + let s2 = s1.clone(); // This should increase ref count + let s3 = s2.clone(); // This should increase ref count more + + // All should be equal + assert_eq!(s1, s2); + assert_eq!(s2, s3); + + // All will be dropped here - ref counts should decrease properly + } + + // If we reach here without crashes or memory issues, memory management is working +} + +#[test] +fn test_oustring_reference_counting_behavior() { + // Test that cloning works properly with reference counting + let original = OUString::from_utf8("Reference counted string"); + + // Create multiple references + let clone1 = original.clone(); + let clone2 = original.clone(); + let clone3 = clone1.clone(); + + // All should be equal + assert_eq!(original, clone1); + assert_eq!(original, clone2); + assert_eq!(original, clone3); + assert_eq!(clone1, clone2); + assert_eq!(clone1, clone3); + assert_eq!(clone2, clone3); + + // All have same content + assert_eq!(original.to_string(), "Reference counted string"); + assert_eq!(clone1.to_string(), "Reference counted string"); + assert_eq!(clone2.to_string(), "Reference counted string"); + assert_eq!(clone3.to_string(), "Reference counted string"); + + // When this function ends, all clones should be properly released +} + +#[test] +fn test_oustring_memory_from_raw_into_raw() { + // Test raw pointer ownership transfer (unique functionality) + for i in 0..100 { + let original = OUString::from_utf8(&format!("Raw pointer test {i}")); + let original_content = original.to_string(); + + // Transfer ownership to raw pointer + let raw_ptr = original.into_raw(); + + // Create new OUString from raw pointer (takes ownership back) + let restored = unsafe { OUString::from_raw(raw_ptr) }; + + assert_eq!(restored.to_string(), original_content); + } +} + +#[test] +fn test_oustring_memory_mixed_operations() { + // Test complex combinations of operations (not covered by existing tests) + for i in 0..50 { + // Create initial string + let s1 = OUString::from_utf8(&format!("Mixed test {i}")); + + // Convert through different types + let s2 = OUString::from(s1.to_string()); + let s3: OUString = s1.to_string().as_str().into(); + + // Clone operations + let c1 = s1.clone(); + let c2 = s2.clone(); + let c3 = s3.clone(); + + // Verify equality + assert_eq!(s1, s2); + assert_eq!(s2, s3); + assert_eq!(c1, c2); + assert_eq!(c2, c3); + + // Raw pointer operations + let raw1 = c1.into_raw(); + let raw2 = c2.into_raw(); + + let restored1 = unsafe { OUString::from_raw(raw1) }; + let restored2 = unsafe { OUString::from_raw(raw2) }; + + assert_eq!(restored1, restored2); + assert_eq!(restored1.to_string(), format!("Mixed test {i}")); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/core/tests/type_tests.rs b/rust_uno/src/core/tests/type_tests.rs new file mode 100644 index 000000000000..3248ac372175 --- /dev/null +++ b/rust_uno/src/core/tests/type_tests.rs @@ -0,0 +1,520 @@ +/* -*- Mode: rust; 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/. + */ + +//! Unit tests for Type (Rust-side functionality) +//! +//! These tests verify Type's Rust implementation including: +//! - Creation methods (new, new_with_name, from_typelib_ref, from_typelib_ref_no_acquire) +//! - Trait implementations (Clone, PartialEq, From, Default) +//! - Instance methods (get_type_class, get_type_name, equals, is_assignable_from) +//! - Memory management and safety (RAII, reference counting) +//! - FFI interop (get_typelib_type) +//! - Ergonomic API (generic string parameters) +//! +//! These tests do NOT require LibreOffice UNO components and test +//! only the Rust-side implementation and FFI safety. + +use crate::core::{OUString, Type, typelib_TypeClass}; + +// === Creation Method Tests === + +#[test] +fn test_type_new() { + let void_type = Type::new(); + assert_eq!( + void_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_VOID + ); + assert_eq!(void_type.get_type_name().to_string(), "void"); +} + +#[test] +fn test_type_default() { + let default_type = Type::default(); + assert_eq!( + default_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_VOID + ); + assert_eq!(default_type.get_type_name().to_string(), "void"); + + // Test that default equals new() + let new_type = Type::new(); + assert!(default_type.equals(&new_type)); +} + +#[test] +fn test_type_new_with_name_str() { + let void_type = Type::new_with_name(typelib_TypeClass::typelib_TypeClass_VOID, "void"); + assert_eq!( + void_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_VOID + ); + assert_eq!(void_type.get_type_name().to_string(), "void"); + + let int_type = Type::new_with_name(typelib_TypeClass::typelib_TypeClass_LONG, "sal_Int32"); + assert_eq!( + int_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_LONG + ); + assert_eq!(int_type.get_type_name().to_string(), "sal_Int32"); + + let interface_type = Type::new_with_name( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + "com.sun.star.lang.XComponent", + ); + assert_eq!( + interface_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_INTERFACE + ); + assert_eq!( + interface_type.get_type_name().to_string(), + "com.sun.star.lang.XComponent" + ); +} + +#[test] +fn test_type_new_with_name_string() { + let type_name = "com.sun.star.text.XText".to_string(); + let text_type = Type::new_with_name(typelib_TypeClass::typelib_TypeClass_INTERFACE, type_name); + assert_eq!( + text_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_INTERFACE + ); + assert_eq!( + text_type.get_type_name().to_string(), + "com.sun.star.text.XText" + ); +} + +#[test] +fn test_type_new_with_name_oustring() { + let oustring_name = OUString::from("com.sun.star.beans.XPropertySet"); + let prop_type = Type::new_with_name( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + oustring_name, + ); + assert_eq!( + prop_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_INTERFACE + ); + assert_eq!( + prop_type.get_type_name().to_string(), + "com.sun.star.beans.XPropertySet" + ); +} + +// === From Trait Tests === + +#[test] +fn test_type_from_typelib_typeclass() { + let string_type = Type::from(typelib_TypeClass::typelib_TypeClass_STRING); + assert_eq!( + string_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_STRING + ); + + let boolean_type = Type::from(typelib_TypeClass::typelib_TypeClass_BOOLEAN); + assert_eq!( + boolean_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_BOOLEAN + ); + + let double_type = Type::from(typelib_TypeClass::typelib_TypeClass_DOUBLE); + assert_eq!( + double_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_DOUBLE + ); +} + +#[test] +fn test_type_from_tuple_with_str() { + let primitive_types = vec![ + ("void", typelib_TypeClass::typelib_TypeClass_VOID), + ("boolean", typelib_TypeClass::typelib_TypeClass_BOOLEAN), + ("byte", typelib_TypeClass::typelib_TypeClass_BYTE), + ("short", typelib_TypeClass::typelib_TypeClass_SHORT), + ( + "unsigned short", + typelib_TypeClass::typelib_TypeClass_UNSIGNED_SHORT, + ), + ("long", typelib_TypeClass::typelib_TypeClass_LONG), + ( + "unsigned long", + typelib_TypeClass::typelib_TypeClass_UNSIGNED_LONG, + ), + ("hyper", typelib_TypeClass::typelib_TypeClass_HYPER), + ( + "unsigned hyper", + typelib_TypeClass::typelib_TypeClass_UNSIGNED_HYPER, + ), + ("float", typelib_TypeClass::typelib_TypeClass_FLOAT), + ("double", typelib_TypeClass::typelib_TypeClass_DOUBLE), + ("char", typelib_TypeClass::typelib_TypeClass_CHAR), + ("string", typelib_TypeClass::typelib_TypeClass_STRING), + ("type", typelib_TypeClass::typelib_TypeClass_TYPE), + ("any", typelib_TypeClass::typelib_TypeClass_ANY), + ]; + + for (type_name, expected_class) in primitive_types { + let type_obj = Type::from((expected_class, type_name)); + assert_eq!( + type_obj.get_type_class(), + expected_class, + "Failed for type: {}", + type_name + ); + assert_eq!(type_obj.get_type_name().to_string(), type_name); + } +} + +#[test] +fn test_type_from_tuple_with_string() { + let type_name = "com.sun.star.awt.XControl".to_string(); + let control_type = Type::from((typelib_TypeClass::typelib_TypeClass_INTERFACE, type_name)); + assert_eq!( + control_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_INTERFACE + ); + assert_eq!( + control_type.get_type_name().to_string(), + "com.sun.star.awt.XControl" + ); +} + +#[test] +fn test_type_from_tuple_with_oustring() { + let type_name = OUString::from("com.sun.star.beans.XPropertySet"); + let prop_type = Type::from((typelib_TypeClass::typelib_TypeClass_INTERFACE, type_name)); + assert_eq!( + prop_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_INTERFACE + ); + assert_eq!( + prop_type.get_type_name().to_string(), + "com.sun.star.beans.XPropertySet" + ); +} + +// === Trait Implementation Tests === + +#[test] +fn test_type_clone() { + let original = Type::new_with_name( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + "com.sun.star.lang.XComponent", + ); + let cloned = original.clone(); + + // They should be equal + assert!(original.equals(&cloned)); + assert_eq!(original.get_type_class(), cloned.get_type_class()); + assert_eq!( + original.get_type_name().to_string(), + cloned.get_type_name().to_string() + ); + + // Test cloning void type + let void_original = Type::new(); + let void_clone = void_original.clone(); + assert!(void_original.equals(&void_clone)); +} + +#[test] +fn test_type_partial_eq() { + let type1 = Type::new_with_name(typelib_TypeClass::typelib_TypeClass_LONG, "sal_Int32"); + let type2 = Type::new_with_name(typelib_TypeClass::typelib_TypeClass_LONG, "sal_Int32"); + let type3 = Type::new_with_name(typelib_TypeClass::typelib_TypeClass_SHORT, "sal_Int16"); + let void1 = Type::new(); + let void2 = Type::new(); + + // Equal types + assert_eq!(type1, type2); + assert_eq!(void1, void2); + + // Different types + assert_ne!(type1, type3); + assert_ne!(type1, void1); + + // Test with interface types + let interface1 = Type::from(( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + "com.sun.star.lang.XComponent", + )); + let interface2 = Type::from(( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + "com.sun.star.lang.XComponent", + )); + let interface3 = Type::from(( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + "com.sun.star.uno.XInterface", + )); + + assert_eq!(interface1, interface2); + assert_ne!(interface1, interface3); +} + +// === Method Tests === + +#[test] +fn test_type_get_type_class() { + let void_type = Type::new(); + assert_eq!( + void_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_VOID + ); + + let string_type = Type::from(typelib_TypeClass::typelib_TypeClass_STRING); + assert_eq!( + string_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_STRING + ); + + let interface_type = Type::from(( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + "com.sun.star.lang.XComponent", + )); + assert_eq!( + interface_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_INTERFACE + ); +} + +#[test] +fn test_type_get_type_name() { + let void_type = Type::new(); + assert_eq!(void_type.get_type_name().to_string(), "void"); + + let int_type = Type::new_with_name(typelib_TypeClass::typelib_TypeClass_LONG, "sal_Int32"); + assert_eq!(int_type.get_type_name().to_string(), "sal_Int32"); + + let interface_type = Type::from(( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + "com.sun.star.text.XTextDocument", + )); + assert_eq!( + interface_type.get_type_name().to_string(), + "com.sun.star.text.XTextDocument" + ); +} + +#[test] +fn test_type_equals() { + let type1 = Type::new_with_name(typelib_TypeClass::typelib_TypeClass_LONG, "sal_Int32"); + let type2 = Type::new_with_name(typelib_TypeClass::typelib_TypeClass_LONG, "sal_Int32"); + let type3 = Type::new_with_name(typelib_TypeClass::typelib_TypeClass_SHORT, "sal_Int16"); + + // Test equality + assert!(type1.equals(&type2)); + assert!(type2.equals(&type1)); // Symmetry + + // Test inequality + assert!(!type1.equals(&type3)); + assert!(!type3.equals(&type1)); // Symmetry + + // Test self-equality + assert!(type1.equals(&type1)); +} + +#[test] +fn test_type_is_assignable_from() { + // Create different types for testing assignment compatibility + let long_type = Type::new_with_name(typelib_TypeClass::typelib_TypeClass_LONG, "sal_Int32"); + let short_type = Type::new_with_name(typelib_TypeClass::typelib_TypeClass_SHORT, "sal_Int16"); + let byte_type = Type::new_with_name(typelib_TypeClass::typelib_TypeClass_BYTE, "sal_Int8"); + + // Test self-assignment (should always be true) + assert!(long_type.is_assignable_from(&long_type)); + assert!(short_type.is_assignable_from(&short_type)); + assert!(byte_type.is_assignable_from(&byte_type)); + + // These tests depend on UNO's specific assignment rules + // Note: The actual behavior depends on LibreOffice's typelib implementation + // We're just testing that the method calls work correctly + let _long_from_short = long_type.is_assignable_from(&short_type); + let _long_from_byte = long_type.is_assignable_from(&byte_type); + let _short_from_byte = short_type.is_assignable_from(&byte_type); +} + +#[test] +fn test_type_get_typelib_type() { + let type_obj = Type::new_with_name(typelib_TypeClass::typelib_TypeClass_STRING, "string"); + let raw_ptr = type_obj.get_typelib_type(); + + // Should return a non-null pointer + assert!(!raw_ptr.is_null()); + + // Test with void type + let void_type = Type::new(); + let void_ptr = void_type.get_typelib_type(); + assert!(!void_ptr.is_null()); +} + +// === Special Cases and Edge Cases === + +#[test] +fn test_type_primitive_type_classes() { + // Test creating types for primitive type classes that have static type references + let primitive_type_classes = vec![ + typelib_TypeClass::typelib_TypeClass_VOID, + typelib_TypeClass::typelib_TypeClass_CHAR, + typelib_TypeClass::typelib_TypeClass_BOOLEAN, + typelib_TypeClass::typelib_TypeClass_BYTE, + typelib_TypeClass::typelib_TypeClass_SHORT, + typelib_TypeClass::typelib_TypeClass_UNSIGNED_SHORT, + typelib_TypeClass::typelib_TypeClass_LONG, + typelib_TypeClass::typelib_TypeClass_UNSIGNED_LONG, + typelib_TypeClass::typelib_TypeClass_HYPER, + typelib_TypeClass::typelib_TypeClass_UNSIGNED_HYPER, + typelib_TypeClass::typelib_TypeClass_FLOAT, + typelib_TypeClass::typelib_TypeClass_DOUBLE, + typelib_TypeClass::typelib_TypeClass_STRING, + typelib_TypeClass::typelib_TypeClass_TYPE, + typelib_TypeClass::typelib_TypeClass_ANY, + ]; + + for type_class in primitive_type_classes { + let type_obj = Type::from(type_class); + assert_eq!(type_obj.get_type_class(), type_class); + } +} + +#[test] +fn test_type_complex_interface_names() { + let complex_names = vec![ + "com.sun.star.lang.XComponent", + "com.sun.star.uno.XInterface", + "com.sun.star.text.XTextDocument", + "com.sun.star.beans.XPropertySet", + "com.sun.star.awt.XControl", + "com.sun.star.frame.XController", + ]; + + for name in complex_names { + let type_obj = Type::from((typelib_TypeClass::typelib_TypeClass_INTERFACE, name)); + assert_eq!( + type_obj.get_type_class(), + typelib_TypeClass::typelib_TypeClass_INTERFACE + ); + assert_eq!(type_obj.get_type_name().to_string(), name); + } +} + +#[test] +fn test_type_consistency_across_creation_methods() { + // Test that different creation methods produce equivalent results for same type + + // String type via different methods + let string_from_class = Type::from(typelib_TypeClass::typelib_TypeClass_STRING); + let string_from_tuple = Type::from((typelib_TypeClass::typelib_TypeClass_STRING, "string")); + let string_from_new_with_name = + Type::new_with_name(typelib_TypeClass::typelib_TypeClass_STRING, "string"); + + assert!(string_from_class.equals(&string_from_tuple)); + assert!(string_from_tuple.equals(&string_from_new_with_name)); + assert!(string_from_class.equals(&string_from_new_with_name)); + + // Interface type via different methods + let interface_name = "com.sun.star.lang.XComponent"; + let interface_from_tuple = Type::from(( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + interface_name, + )); + let interface_from_new_with_name = Type::new_with_name( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + interface_name, + ); + + assert!(interface_from_tuple.equals(&interface_from_new_with_name)); +} + +// === Memory Management Tests === + +#[test] +fn test_type_memory_safety() { + // Test that Types can be created and dropped without issues + + { + let _type1 = Type::new(); + let _type2 = Type::from(typelib_TypeClass::typelib_TypeClass_STRING); + let _type3 = Type::new_with_name(typelib_TypeClass::typelib_TypeClass_LONG, "sal_Int32"); + } // Types should be properly cleaned up here + + // Create more types after the previous ones were dropped + let type4 = Type::from(( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + "com.sun.star.lang.XComponent", + )); + let type5 = type4.clone(); + + assert!(type4.equals(&type5)); +} + +#[test] +fn test_type_multiple_references() { + // Test that multiple references to the same logical type work correctly + let name = "com.sun.star.text.XTextDocument"; + + let type1 = Type::from((typelib_TypeClass::typelib_TypeClass_INTERFACE, name)); + let type2 = Type::from((typelib_TypeClass::typelib_TypeClass_INTERFACE, name)); + let type3 = type1.clone(); + + // All should be equal + assert!(type1.equals(&type2)); + assert!(type2.equals(&type3)); + assert!(type1.equals(&type3)); + + // All should have same properties + assert_eq!(type1.get_type_class(), type2.get_type_class()); + assert_eq!(type2.get_type_class(), type3.get_type_class()); + + assert_eq!( + type1.get_type_name().to_string(), + type2.get_type_name().to_string() + ); + assert_eq!( + type2.get_type_name().to_string(), + type3.get_type_name().to_string() + ); +} + +#[test] +fn test_type_mixed_operations() { + // Test mixing different operations + let original = Type::new_with_name( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + "com.sun.star.lang.XComponent", + ); + + // Clone and compare + let cloned = original.clone(); + assert!(original.equals(&cloned)); + + // Create equivalent via From trait + let from_tuple = Type::from(( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + "com.sun.star.lang.XComponent", + )); + assert!(original.equals(&from_tuple)); + + // Test assignment compatibility with itself + assert!(original.is_assignable_from(&original)); + assert!(cloned.is_assignable_from(&from_tuple)); + + // Check type information + assert_eq!( + original.get_type_class(), + typelib_TypeClass::typelib_TypeClass_INTERFACE + ); + assert_eq!( + original.get_type_name().to_string(), + "com.sun.star.lang.XComponent" + ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/core/type.rs b/rust_uno/src/core/type.rs new file mode 100644 index 000000000000..21363952ddcf --- /dev/null +++ b/rust_uno/src/core/type.rs @@ -0,0 +1,328 @@ +/* -*- Mode: rust; 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/. + */ + +//! UNO Type System and Type References +//! +//! This module provides the high-level safe Rust wrapper around LibreOffice's type system. +//! It includes safe type creation, automatic memory management, and type compatibility testing. +//! +//! ## Key Components +//! - `Type` - Safe Rust wrapper around LibreOffice's typelib_TypeDescriptionReference +//! +//! ## Features +//! - Safe type creation with automatic memory management via RAII +//! - Type equality and assignment compatibility testing +//! - Type class and name retrieval +//! - Clone, equality, and default trait implementations +//! - Direct FFI interop with raw pointer access methods +//! - Support for all UNO type classes (void, primitive types, interfaces, etc.) +//! - Safe From trait implementations for creating types from type classes and explicit type/name tuples + +use crate::core::oustring::*; +use crate::ffi::type_ffi::*; +use std::ptr; + +// === Safe Type Wrapper === + +/// Safe Rust wrapper for UNO Type +/// +/// This struct provides a safe, ergonomic interface to UNO's type system. +/// It handles memory management automatically and provides idiomatic Rust +/// methods for type operations and comparisons. +/// +/// # Examples +/// +/// ```rust +/// use rust_uno::{Type, typelib_TypeClass}; +/// +/// // Create a void type +/// let void_type = Type::new(); +/// +/// // Create a primitive type +/// let int_type = Type::from(typelib_TypeClass::typelib_TypeClass_LONG); +/// +/// // Create an interface type +/// let interface_type = Type::from(( +/// typelib_TypeClass::typelib_TypeClass_INTERFACE, +/// "com.sun.star.lang.XComponent" +/// )); +/// ``` +#[allow(non_snake_case)] // _pType follows FFI naming conventions +pub struct Type { + /// C typelib reference pointer + _pType: *mut typelib_TypeDescriptionReference, +} + +impl Type { + /// Create a new Type set to void + /// + /// Creates the default UNO type (void). This is equivalent to the default + /// constructor in C++ com::sun::star::uno::Type. + pub fn new() -> Self { + unsafe { + let void_type_ref = + typelib_static_type_getByTypeClass(typelib_TypeClass::typelib_TypeClass_VOID); + typelib_typedescriptionreference_acquire(*void_type_ref); + Type { + _pType: *void_type_ref, + } + } + } + + /// Create a Type from type class and name + /// + /// Creates a new Type from the specified type class and a string that will be + /// converted to OUString. Accepts any type that can be converted to OUString + /// (such as &str, String, etc.) through the Into trait. This is the most common + /// way to create custom types with specific names. + /// + /// # Arguments + /// * `type_class` - The UNO type class (VOID, STRING, INTERFACE, etc.) + /// * `type_name` - The fully qualified type name (convertible to OUString) + pub fn new_with_name<T: Into<OUString>>(type_class: typelib_TypeClass, type_name: T) -> Self { + let mut oustring_name = type_name.into(); + let mut type_ref: *mut typelib_TypeDescriptionReference = ptr::null_mut(); + + unsafe { + typelib_typedescriptionreference_new( + &mut type_ref, + type_class, + oustring_name.as_mut_ptr(), + ); + } + + Type { _pType: type_ref } + } + + /// Create Type from existing typelib reference (acquires reference) + /// + /// Takes ownership of an existing type reference by incrementing its reference count. + /// This is useful when interfacing with C code that provides type references. + /// + /// # Safety + /// The caller must ensure that `type_ref` points to a valid typelib_TypeDescriptionReference. + pub unsafe fn from_typelib_ref(type_ref: *mut typelib_TypeDescriptionReference) -> Self { + unsafe { + typelib_typedescriptionreference_acquire(type_ref); + } + Type { _pType: type_ref } + } + + /// Create Type from existing typelib reference (no acquire) + /// + /// Takes ownership of an existing type reference without incrementing reference count. + /// Use this when transferring ownership from C code that already has a reference. + /// This avoids unnecessary reference count manipulation when you know the ownership + /// is being transferred. + /// + /// # Safety + /// The caller must ensure that: + /// - `type_ref` points to a valid typelib_TypeDescriptionReference + /// - The reference count is properly managed (caller transfers ownership) + pub unsafe fn from_typelib_ref_no_acquire( + type_ref: *mut typelib_TypeDescriptionReference, + ) -> Self { + Type { _pType: type_ref } + } + + /// Get the type class of this type + /// + /// Returns the UNO type class (VOID, STRING, INTERFACE, etc.) that categorizes + /// this type within the UNO type system. This is used for type checking and + /// dispatch in UNO method calls. + pub fn get_type_class(&self) -> typelib_TypeClass { + unsafe { (*self._pType).eTypeClass } + } + + /// Get the fully qualified name of this type + /// + /// Returns an OUString containing the type name (e.g., "com.sun.star.lang.XComponent"). + /// For primitive types, returns the type name (e.g., "long", "string"). The returned + /// string is a copy, so it can be safely used without worrying about the lifetime + /// of the original Type instance. + pub fn get_type_name(&self) -> OUString { + unsafe { + let name_ptr = (*self._pType).pTypeName; + if name_ptr.is_null() { + return OUString::new(); + } + // Use OUString::from_raw which handles the acquisition internally + OUString::from_raw(name_ptr) + } + } + + /// Get the raw typelib reference pointer for FFI calls + /// + /// Returns the underlying pointer for FFI interoperability. The returned + /// pointer remains owned by this Type instance and should not be freed + /// by the caller. Useful when calling LibreOffice C APIs directly. + pub fn get_typelib_type(&self) -> *mut typelib_TypeDescriptionReference { + self._pType + } + + /// Convert Type into raw pointer (releases Rust ownership) + /// + /// Transfers ownership of the underlying type reference to the caller. + /// The caller becomes responsible for calling typelib_typedescriptionreference_release + /// when done with the pointer. The Type instance is consumed and cannot be used + /// after this call. + pub fn into_raw(self) -> *mut typelib_TypeDescriptionReference { + let ptr = self._pType; + std::mem::forget(self); // Don't run destructor since we're transferring ownership + ptr + } + + /// Test if values of this type can be assigned from values of the given type + /// + /// This includes widening conversions (e.g., long assignable from short) + /// as long as there is no data loss. The test follows UNO assignment rules + /// and is used in method dispatch to determine if argument types are compatible. + /// + /// # Arguments + /// * `other` - The source type to test assignment compatibility from + /// + /// # Returns + /// `true` if values of `other` type can be assigned to this type, `false` otherwise + pub fn is_assignable_from(&self, other: &Type) -> bool { + unsafe { typelib_typedescriptionreference_isAssignableFrom(self._pType, other._pType) != 0 } + } + + /// Test if this type is equal to another type + /// + /// Two types are equal if they have the same type class and fully qualified name. + /// This is used for type matching in UNO method dispatch and interface queries. + /// The comparison is efficient and uses LibreOffice's native type comparison. + /// + /// # Arguments + /// * `other` - The type to compare with + /// + /// # Returns + /// `true` if the types are equal, `false` otherwise + pub fn equals(&self, other: &Type) -> bool { + unsafe { typelib_typedescriptionreference_equals(self._pType, other._pType) != 0 } + } +} + +/// Default implementation: Creates a void type +/// +/// When a Type is created without explicit initialization, it defaults to +/// the void type, which is the UNO equivalent of "no type" or "empty type". +impl Default for Type { + fn default() -> Self { + Self::new() + } +} + +/// Clone implementation: Create an independent copy of another Type +/// +/// This creates a new Type that references the same type description but is +/// independently managed. The clone operation increments the reference count +/// of the underlying type description, ensuring proper memory management. +impl Clone for Type { + fn clone(&self) -> Self { + unsafe { + typelib_typedescriptionreference_acquire(self._pType); + } + Type { + _pType: self._pType, + } + } +} + +/// Equality comparison with other Type instances +/// +/// Two Type instances are considered equal if they refer to the same type +/// (same type class and name). This uses LibreOffice's native type comparison +/// which is efficient and handles all type categories correctly. +impl PartialEq for Type { + fn eq(&self, other: &Self) -> bool { + self.equals(other) + } +} + +impl Eq for Type {} + +/// Debug trait for debugging - shows type class and name for UNO type identification +/// +/// Displays the Type in a debug-friendly format showing both the type class +/// and the type name, which is useful for debugging UNO type-related issues. +impl std::fmt::Debug for Type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let type_class = self.get_type_class(); + let type_name = self.get_type_name().to_string(); + f.debug_struct("Type") + .field("type_class", &type_class) + .field("type_name", &type_name) + .finish() + } +} + +/// Destructor: Automatic cleanup when Type goes out of scope +/// +/// Automatically releases the reference to the underlying type description +/// when the Type instance is dropped. This ensures proper memory management +/// without manual intervention. +impl Drop for Type { + fn drop(&mut self) { + unsafe { + typelib_typedescriptionreference_release(self._pType); + } + } +} + +/// From trait implementation for typelib_TypeClass (primitive types) +/// +/// Creates a Type from a primitive type class. This uses LibreOffice's static +/// type system to get pre-defined type references for standard UNO types like +/// void, boolean, string, etc. This is the most efficient way to create +/// primitive types since they're statically allocated. +impl From<typelib_TypeClass> for Type { + fn from(type_class: typelib_TypeClass) -> Self { + unsafe { + let type_ref = typelib_static_type_getByTypeClass(type_class); + typelib_typedescriptionreference_acquire(*type_ref); + Type { _pType: *type_ref } + } + } +} + +/// From trait implementation for (typelib_TypeClass, &str) tuples +/// +/// Convenient conversion from type class and string slice to Type. The string +/// is automatically converted to OUString using UTF-8 encoding. This is useful +/// for creating complex types with explicit type names. +impl From<(typelib_TypeClass, &str)> for Type { + fn from((type_class, type_name): (typelib_TypeClass, &str)) -> Self { + Self::new_with_name(type_class, type_name) + } +} + +/// From trait implementation for (typelib_TypeClass, String) tuples +/// +/// Convenient conversion from type class and owned String to Type. The string +/// is automatically converted to OUString using UTF-8 encoding. This allows +/// using dynamically generated type names. +impl From<(typelib_TypeClass, String)> for Type { + fn from((type_class, type_name): (typelib_TypeClass, String)) -> Self { + Self::new_with_name(type_class, type_name) + } +} + +/// From trait implementation for (typelib_TypeClass, OUString) tuples +/// +/// Direct conversion from type class and OUString to Type. This is the most +/// efficient conversion when you already have an OUString, as it avoids +/// additional string conversions. +impl From<(typelib_TypeClass, OUString)> for Type { + fn from((type_class, type_name): (typelib_TypeClass, OUString)) -> Self { + Self::new_with_name(type_class, type_name) + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/core/uno_wrapper.rs b/rust_uno/src/core/uno_wrapper.rs new file mode 100644 index 000000000000..b9ad23bccd04 --- /dev/null +++ b/rust_uno/src/core/uno_wrapper.rs @@ -0,0 +1,59 @@ +/* -*- Mode: rust; 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/. + */ + +//! Safe Rust wrappers for UNO interfaces +//! +//! This module provides memory-safe, RAII-based wrappers around raw UNO interface pointers. +//! For specific UNO interface method calls, use the auto-generated FFI functions +//! in the `generated` module directly. + +use crate::ffi::uno_bridge; +use crate::generated::com::sun::star::uno::XComponentContext::XComponentContext; +use std::ffi::NulError; + +/// Error types for UNO operations +#[derive(Debug)] +pub enum UnoError { + /// Failed to create C string (contains null byte) + InvalidString(NulError), + /// UNO operation failed + OperationFailed, + /// Interface not supported + InterfaceNotSupported, + /// Null pointer encountered + NullPointer, + /// Bridge initialization failed + BridgeInitializationFailed, +} + +impl From<NulError> for UnoError { + fn from(err: NulError) -> Self { + UnoError::InvalidString(err) + } +} + +pub type UnoResult<T> = Result<T, UnoError>; + +/// Initialize the UNO bridge by directly calling LibreOffice bootstrap +/// Returns the initial component context +#[allow(non_snake_case)] +pub fn defaultBootstrap_InitialComponentContext() -> UnoResult<XComponentContext> { + unsafe { + let context_ptr = uno_bridge::defaultBootstrap_InitialComponentContext(); + if context_ptr.is_null() { + Err(UnoError::BridgeInitializationFailed) + } else { + // The bootstrap function returns Reference<XComponentContext>* directly + // Use from_ptr which now handles Reference<T>* correctly + XComponentContext::from_ptr(context_ptr).ok_or(UnoError::BridgeInitializationFailed) + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/examples/any_example.rs b/rust_uno/src/examples/any_example.rs new file mode 100644 index 000000000000..4a6b717a0e5a --- /dev/null +++ b/rust_uno/src/examples/any_example.rs @@ -0,0 +1,158 @@ +/* -*- Mode: rust; 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/. + */ + +//! Simple UNO Any Examples +//! +//! This example shows the basic usage of Any for UNO operations. +//! +//! ## Any Creation Methods: +//! - `Any::new()` - Creates an empty Any (void type) +//! - `Any::from_bool(value)` - Creates an Any containing a boolean +//! - `Any::from_i32(value)` - Creates an Any containing a 32-bit integer +//! - `Any::from(value)` - Uses From trait for supported types + +use crate::core::Any; + +/// Run the UNO Any example +/// +/// This function demonstrates the usage of Any for UNO operations, +/// including various creation methods, operations, and traits. +pub fn run_example() { + println!("=== UNO Any Examples ===\n"); + + // Create Any values using different methods + let empty = Any::new(); + let bool_any = Any::from_bool(true); + let int_any = Any::from_i32(42); + let false_any = Any::from_bool(false); + let zero_any = Any::from_i32(0); + + println!("Empty Any: {} (has value: {})", empty, empty.has_value()); + println!( + "Boolean Any (true): {} (has value: {})", + bool_any, + bool_any.has_value() + ); + println!( + "Integer Any (42): {} (has value: {})", + int_any, + int_any.has_value() + ); + println!( + "Boolean Any (false): {} (has value: {})", + false_any, + false_any.has_value() + ); + println!( + "Integer Any (0): {} (has value: {})", + zero_any, + zero_any.has_value() + ); + + // Test From trait implementations + println!("\n--- From Trait Examples ---"); + let from_bool: Any = true.into(); + let from_int: Any = 123i32.into(); + + println!( + "From bool (true): {} (has value: {})", + from_bool, + from_bool.has_value() + ); + println!( + "From i32 (123): {} (has value: {})", + from_int, + from_int.has_value() + ); + + // Any operations and traits + println!("\n--- Any Operations ---"); + let cloned = bool_any.clone(); + println!("Cloned == Original: {}", cloned == bool_any); + + // Trait examples + println!("\n--- Trait Examples ---"); + + // Default trait + let default_any = Any::default(); + println!( + "Default Any: '{}' (has value: {})", + default_any, + default_any.has_value() + ); + + // Debug trait + println!("Debug output: {bool_any:?}"); + + // PartialEq trait + let test1 = Any::from_bool(true); + let test2 = Any::from_bool(true); + let test3 = Any::from_bool(false); + let test4 = Any::new(); // void + let test5 = Any::new(); // void + + println!("true == true: {}", test1 == test2); + println!("true != false: {}", test1 != test3); + println!("void == void: {}", test4 == test5); + println!("true != void: {}", test1 != test4); + + // Test clear operation + println!("\n--- Clear Operation ---"); + let mut mutable_any = Any::from_i32(999); + println!( + "Before clear: {} (has value: {})", + mutable_any, + mutable_any.has_value() + ); + mutable_any.clear(); + println!( + "After clear: {} (has value: {})", + mutable_any, + mutable_any.has_value() + ); + + // Raw conversion examples + println!("\n--- Raw Conversion Examples ---"); + let original = Any::from_bool(true); + println!( + "Original: {} (has value: {})", + original, + original.has_value() + ); + + // Convert to raw and back (unsafe operations) + let raw_any = original.into_raw(); + let restored = unsafe { Any::from_raw(raw_any) }; + println!( + "Restored: {} (has value: {})", + restored, + restored.has_value() + ); + + // Test various integer values + println!("\n--- Integer Value Tests ---"); + let numbers = vec![-1, 0, 1, 42, 2024, i32::MAX, i32::MIN]; + for num in numbers { + let any = Any::from_i32(num); + println!("Any({num}): {} (has value: {})", any, any.has_value()); + } + + // Test memory safety with multiple clones + println!("\n--- Memory Safety Test ---"); + let original = Any::from_i32(12345); + let clones: Vec<Any> = (0..5).map(|_| original.clone()).collect(); + println!("Created {} clones of Any(12345)", clones.len()); + for (i, clone) in clones.iter().enumerate() { + println!("Clone {}: {} (has value: {})", i, clone, clone.has_value()); + } + + println!("\n✓ Any examples completed!"); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/examples/basic_example.rs b/rust_uno/src/examples/basic_example.rs new file mode 100644 index 000000000000..4190cf043605 --- /dev/null +++ b/rust_uno/src/examples/basic_example.rs @@ -0,0 +1,120 @@ +/* -*- Mode: rust; 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/. + */ + +//! Basic usage example of the Rust UNO Language Binding +//! +//! This example demonstrates how to use the Rust UNO bindings to interact +//! with LibreOffice UNO components, including the OUString wrapper for safe +//! string handling and cross-platform SAL types. + +// Import from the current crate since examples are now part of the library +use crate::core::OUString; + +/// Example function demonstrating OUString creation and operations +pub fn oustring_basic_example() { + println!("=== LibreOffice UNO Rust Binding - OUString Demonstration ==="); + + // Basic string creation + let empty = OUString::new(); + let hello = OUString::from("Hello UNO!"); + let unicode = OUString::from_utf8("Unicode: café 中文 🚀"); + + println!("Empty string: '{}' (len: {})", empty, empty.len()); + println!("Hello string: '{}' (len: {})", hello, hello.len()); + println!("Unicode string: '{}' (len: {})", unicode, unicode.len()); + + // String operations + let cloned = hello.clone(); + println!("Cloned == Original: {}", cloned == hello); + + // Display string conversion + println!("Display conversion: '{unicode}'"); + + println!("✓ OUString operations completed successfully!"); + println!("✓ Successfully demonstrated Rust UNO String Handling"); +} + +/// Example function demonstrating advanced OUString operations +/// +/// Shows various ways to create and manipulate OUString instances, +/// including UTF-8, ASCII, and Unicode handling. +pub fn advanced_string_example() { + println!("=== Advanced UNO String Types (OUString) Examples ==="); + + // Basic string creation using different methods + let empty_string = OUString::new(); + let hello_string = OUString::from("Hello, LibreOffice!"); + let unicode_string = OUString::from_utf8("Unicode: αβγ, 中文, 🚀"); + + println!("Empty: '{empty_string}' (length: {})", empty_string.len()); + println!("Hello: '{hello_string}' (length: {})", hello_string.len()); + println!( + "Unicode: '{unicode_string}' (length: {})", + unicode_string.len() + ); + + // String operations + let cloned = hello_string.clone(); + println!("Cloned == Original: {}", cloned == hello_string); + println!("Convert to String: '{unicode_string}'"); + + // More string examples + let ascii_only = OUString::from_ascii("ASCII Only!"); + println!( + "ASCII string: '{ascii_only}' (length: {})", + ascii_only.len() + ); + + // Comparison operations + let same_hello = OUString::from("Hello, LibreOffice!"); + println!("String equality: {}", hello_string == same_hello); + + println!("✓ Advanced string operations completed successfully!"); +} + +/// Run the basic UNO example +/// +/// This function demonstrates the complete basic usage of the Rust UNO bindings, +/// focusing on OUString operations and core library functionality. +pub fn run_example() { + println!("=== Rust UNO Language Binding - Basic Usage Example ===\n"); + + // Run the basic OUString demonstration + oustring_basic_example(); + + println!("\n=== Advanced String Examples ==="); + + // Run advanced string examples + advanced_string_example(); + + println!("\n=== Data Type Examples ==="); + + // Demonstrate various data type operations + println!("UNO Type System Examples:"); + + // Basic type demonstrations (without requiring UNO runtime) + println!(" Rust types compatible with UNO:"); + println!(" i8 (sal_Int8/byte): {}", i8::MIN); + println!(" i16 (sal_Int16/short): {}", i16::MIN); + println!(" i32 (sal_Int32/long): {}", i32::MIN); + println!(" i64 (sal_Int64/hyper): {}", i64::MIN); + println!(" u16 (sal_uInt16/unsigned short): {}", u16::MAX); + println!(" u32 (sal_uInt32/unsigned long): {}", u32::MAX); + println!(" u64 (sal_uInt64/unsigned hyper): {}", u64::MAX); + println!(" f32 (float): {}", std::f32::consts::PI); + println!(" f64 (double): {}", std::f64::consts::E); + println!(" bool (sal_Bool): true"); + println!(" u16 (sal_Unicode/char): {}", 'Ö' as u16); + + println!("\n✓ All UNO basic operations completed successfully!"); + println!("✓ Rust UNO Language Binding core functionality demonstrated!"); + println!("✓ OUString operations are fully functional!"); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/examples/load_writer.rs b/rust_uno/src/examples/load_writer.rs new file mode 100644 index 000000000000..575f64237005 --- /dev/null +++ b/rust_uno/src/examples/load_writer.rs @@ -0,0 +1,124 @@ +/* -*- Mode: rust; 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/. + */ + +use crate::core::uno_wrapper::{UnoError, defaultBootstrap_InitialComponentContext}; +use crate::core::OUString; +use crate::generated::rustmaker::com::sun::star::frame::Desktop::Desktop; +use crate::generated::rustmaker::com::sun::star::frame::XComponentLoader::XComponentLoader; +use crate::generated::rustmaker::com::sun::star::text::XTextDocument::XTextDocument; +use crate::generated::rustmaker::com::sun::star::text::XSimpleText::XSimpleText; +use std::ffi::c_void; + +/// LibreOffice Writer automation demo using generated Rust UNO bindings. +/// Creates a Writer document and inserts text using auto-generated interface wrappers. +pub fn run() { + println!("=== Rust UNO Bridge Test ==="); + println!("-> Running 'load_writer' example..."); + + match run_internal() { + Ok(()) => { + println!("=== Rust UNO Bridge Test Done ==="); + println!("<- 'load_writer' example completed successfully."); + }, + Err(e) => { + println!("=== Rust UNO Bridge Test Done ==="); + eprintln!(" ERROR: 'load_writer' example failed: {e:?}"); + }, + } +} + +fn run_internal() -> Result<(), UnoError> { + // Initialize UNO bridge + println!(" Initializing UNO bridge..."); + let context = defaultBootstrap_InitialComponentContext()?; + println!(" ✓ UNO bridge initialized successfully!"); + + // Create Desktop service + println!(" Creating Desktop service using generated wrapper..."); + let desktop = Desktop::create(context.as_ptr()).ok_or(UnoError::OperationFailed)?; + println!(" ✓ Desktop service created: {:p}", desktop.as_ptr()); + + // Get XComponentLoader interface + let component_loader = XComponentLoader::from_ptr(desktop.as_ptr()).ok_or(UnoError::InterfaceNotSupported)?; + println!(" ✓ XComponentLoader interface acquired: {:p}", component_loader.as_ptr()); + + // Load Writer document + println!(" Loading Writer document via XComponentLoader::loadComponentFromURL..."); + let url = OUString::from("private:factory/swriter"); + let target = OUString::from("_blank"); + let search_flags: i32 = 0; + let empty_args = std::ptr::null_mut(); + + let document_interface_ptr = component_loader.loadComponentFromURL( + url.as_ptr() as *mut c_void, + target.as_ptr() as *mut c_void, + &search_flags as *const i32 as *mut c_void, + empty_args, + ).ok_or(UnoError::OperationFailed)?; + + if document_interface_ptr.as_ptr().is_null() { + return Err(UnoError::OperationFailed); + } + + println!(" ✓ Writer document loaded successfully!"); + println!(" Document interface pointer: {:p}", document_interface_ptr.as_ptr()); + + // Cast to XTextDocument + println!(" Casting document to XTextDocument..."); + let text_document = XTextDocument::from_ptr(document_interface_ptr.as_ptr()).ok_or(UnoError::InterfaceNotSupported)?; + println!(" ✓ XTextDocument interface acquired: {:p}", text_document.as_ptr()); + + // Get text object + println!(" Getting text object via XTextDocument::getText..."); + let text = text_document.getText().ok_or(UnoError::OperationFailed)?; + println!(" ✓ Text object acquired: {:p}", text.as_ptr()); + + // Cast to XSimpleText + println!(" Casting XText to XSimpleText..."); + let simple_text = XSimpleText::from_ptr(text.as_ptr()).ok_or(UnoError::InterfaceNotSupported)?; + println!(" ✓ XSimpleText interface acquired: {:p}", simple_text.as_ptr()); + + // Create text cursor + println!(" Creating text cursor via XSimpleText::createTextCursor..."); + let cursor = simple_text.createTextCursor().ok_or(UnoError::OperationFailed)?; + println!(" ✓ Text cursor created: {:p}", cursor.as_ptr()); + + // Insert text + println!(" Inserting text via XSimpleText::insertString..."); + let hello_text = OUString::from( + "Hello from Generated Rust UNO Bindings!\n\n\ + This text was inserted using auto-generated service and interface wrappers:\n\n\ + - Desktop::create() - Generated service wrapper\n\ + - XComponentLoader::loadComponentFromURL() - Generated interface method\n\ + - XTextDocument::getText() - Generated interface method\n\ + - XSimpleText::createTextCursor() - Generated interface method\n\ + - XSimpleText::insertString() - Generated interface method\n\n\ + All types generated automatically from UNO IDL with type-safe opaque pointer architecture!\n\n\ + FFI Validation Features Demonstrated:\n\ + - Automatic null pointer checking in all generated methods\n\ + - Reference validity validation (is() checking) \n\ + - Memory cleanup on validation failure (delete + return nullptr)\n\ + - Detailed debug logging for troubleshooting\n\ + - Type-safe opaque pointer architecture prevents crashes\n\ + - Follows uno_bootstrap.cxx validation pattern exactly" + ); + let absorb: i32 = 0; + + simple_text.insertString( + cursor.as_ptr() as *mut c_void, + hello_text.as_ptr() as *mut c_void, + &absorb as *const i32 as *mut c_void, + ); + + println!(" ✓ Text inserted successfully using generated interfaces!"); + + Ok(()) +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
\ No newline at end of file diff --git a/rust_uno/src/examples/mod.rs b/rust_uno/src/examples/mod.rs new file mode 100644 index 000000000000..f28de5d7bbbe --- /dev/null +++ b/rust_uno/src/examples/mod.rs @@ -0,0 +1,41 @@ +/* -*- Mode: rust; 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/. + */ + +//! Example usage of the Rust UNO bridge + +pub mod any_example; +pub mod basic_example; +pub mod load_writer; +pub mod string_example; +pub mod type_example; +// pub mod autogen_test; // Temporarily disabled + +/// Run all examples +pub fn run_all() { + println!("=== Running all UNO examples ==="); + + println!("\n--- Load Writer example ---"); + load_writer::run(); + + println!("\n--- Basic example ---"); + basic_example::run_example(); + + println!("\n--- String example ---"); + string_example::run_example(); + + println!("\n--- Any example ---"); + any_example::run_example(); + + println!("\n--- Type example ---"); + type_example::run_example(); + + println!("\n=== All examples completed ==="); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/examples/string_example.rs b/rust_uno/src/examples/string_example.rs new file mode 100644 index 000000000000..ff73ee433d3f --- /dev/null +++ b/rust_uno/src/examples/string_example.rs @@ -0,0 +1,131 @@ +/* -*- Mode: rust; 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/. + */ + +//! Simple UNO String Examples +//! +//! This example shows the basic usage of OUString for UNO operations. +//! +//! ## String Creation Methods: +//! - `OUString::from(str)` - Uses UTF-8 via From trait (most common) +//! - `OUString::from_utf8(str)` - Explicit UTF-8 conversion (recommended) +//! - `OUString::from_ascii(str)` - For pure ASCII text only +//! - `OUString::from_utf16(data)` - When you already have UTF-16 data + +use crate::core::oustring::OUString; +use crate::ffi::rtl_string::rtl_uString; +use crate::ffi::sal_types::sal_Unicode; +use std::ptr::NonNull; + +/// Test what happens with allocation failure +/// NOTE: This is just for demonstration - real failure is hard to trigger +pub fn test_allocation_failure_behavior() { + println!("=== Testing Allocation Failure Behavior ==="); + + // Simulate what happens when NonNull::new gets a null pointer + let null_ptr: *mut rtl_uString = std::ptr::null_mut(); + + println!("Simulating allocation failure..."); + + // This will demonstrate the behavior: + match NonNull::new(null_ptr) { + Some(_) => println!("Got valid pointer"), + None => { + println!("Got null pointer - this would cause panic in OUString::new()"); + println!("In real code: .expect(\"RTL string allocation failed\") would panic here"); + println!( + "Program would terminate with: 'RTL string allocation failed - system out of memory'" + ); + return; + } + } + + println!("If you see this message, allocation succeeded"); +} + +/// Run the UNO string example +/// +/// This function demonstrates the usage of OUString for UNO operations, +/// including various creation methods, operations, and traits. +pub fn run_example() { + println!("=== UNO String Examples ==="); + + // Create strings using different methods + let empty = OUString::new(); + let hello = OUString::from("Hello UNO!"); // Uses from_utf8 via From trait (most common) + let unicode = OUString::from_utf8("Unicode: café 中文 🚀"); // Explicit UTF-8 (recommended) + let ascii_only = OUString::from_ascii("ASCII Only!"); // For pure ASCII text only + + // UTF-16 example (for when you already have UTF-16 data) + let utf16_data: Vec<sal_Unicode> = "UTF-16 example" + .encode_utf16() + .map(|c| c as sal_Unicode) + .collect(); + let utf16_string = OUString::from_utf16(&utf16_data); + + println!("Empty string: '{}' (len: {})", empty, empty.len()); + println!("Hello string: '{}' (len: {})", hello, hello.len()); + println!("UTF-8 string: '{}' (len: {})", unicode, unicode.len()); + println!("ASCII string: '{}' (len: {})", ascii_only, ascii_only.len()); + println!( + "UTF-16 string: '{}' (len: {})", + utf16_string, + utf16_string.len() + ); + + // String operations and traits + let cloned = hello.clone(); + println!("Cloned == Original: {}", cloned == hello); + + // Convert to Rust String (inherited from Display trait) + let rust_str = unicode.to_string(); + println!("As Rust String: '{rust_str}'"); + + // Trait examples + println!("\n--- Trait Examples ---"); + + // Default trait + let default_str = OUString::default(); + println!( + "Default string: '{}' (empty: {})", + default_str, + default_str.is_empty() + ); + + // Debug trait + println!("Debug output: {hello:?}"); + + // PartialEq trait + let test1 = OUString::from("test"); + let test2 = OUString::from("test"); + let test3 = OUString::from("different"); + println!("'{}' == '{}': {}", test1, test2, test1 == test2); + println!("'{}' != '{}': {}", test1, test2, test1 != test2); + println!("'{}' == '{}': {}", test1, test3, test1 == test3); + println!("'{}' != '{}': {}", test1, test3, test1 != test3); + + // Additional string examples + println!("\n--- Additional String Operations ---"); + + let special_chars = OUString::from_utf8("Special: αβγ, ñañá, العربية"); + println!("Special characters: '{special_chars}'"); + + let mixed = OUString::from("Mixed content: 123 + αβγ = 🎉"); + println!("Mixed content: '{mixed}' (len: {})", mixed.len()); + + // Test empty string operations + println!("Empty string is empty: {}", empty.is_empty()); + println!("Hello string is empty: {}", hello.is_empty()); + + println!("\n--- Testing Allocation Failure Behavior ---"); + test_allocation_failure_behavior(); + + println!("\n✓ OUString examples completed!"); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/examples/type_example.rs b/rust_uno/src/examples/type_example.rs new file mode 100644 index 000000000000..31889f8dd999 --- /dev/null +++ b/rust_uno/src/examples/type_example.rs @@ -0,0 +1,324 @@ +/* -*- Mode: rust; 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/. + */ + +//! UNO Type System Examples +//! +//! This example demonstrates the usage of the UNO Type system in Rust. +//! The Type class represents IDL meta types, holding type names and type classes. +//! +//! ## Type Creation Methods: +//! - `Type::new()` - Creates void type (default) +//! - `Type::from(typelib_TypeClass)` - Creates type from type class only +//! - `Type::from((typelib_TypeClass, name))` - Creates type with class and name +//! - `Type::new_with_name(class, name)` - Explicit creation with name +//! +//! ## Type Operations: +//! - `get_type_class()` - Get the UNO type class +//! - `get_type_name()` - Get the fully qualified type name +//! - `equals()` - Test type equality +//! - `is_assignable_from()` - Test assignment compatibility + +use crate::core::{OUString, Type}; +use crate::ffi::type_ffi::typelib_TypeClass; + +/// Run the UNO type system example +/// +/// This function demonstrates the usage of the UNO Type system in Rust, +/// including type creation, operations, traits, and UNO interface integration. +pub fn run_example() { + println!("=== UNO Type System Examples ===\n"); + + // === Basic Type Creation === + println!("--- Basic Type Creation ---"); + + // Default void type + let void_type = Type::new(); + let default_type = Type::default(); + println!("Void type: {void_type:?}"); + println!("Default type: {default_type:?}"); + println!("Void == Default: {}", void_type.equals(&default_type)); + + // === Primitive Types from Type Classes === + println!("\n--- Primitive Types from Type Classes ---"); + + let boolean_type = Type::from(typelib_TypeClass::typelib_TypeClass_BOOLEAN); + let byte_type = Type::from(typelib_TypeClass::typelib_TypeClass_BYTE); + let short_type = Type::from(typelib_TypeClass::typelib_TypeClass_SHORT); + let long_type = Type::from(typelib_TypeClass::typelib_TypeClass_LONG); + let float_type = Type::from(typelib_TypeClass::typelib_TypeClass_FLOAT); + let double_type = Type::from(typelib_TypeClass::typelib_TypeClass_DOUBLE); + let char_type = Type::from(typelib_TypeClass::typelib_TypeClass_CHAR); + let string_type = Type::from(typelib_TypeClass::typelib_TypeClass_STRING); + let type_type = Type::from(typelib_TypeClass::typelib_TypeClass_TYPE); + let any_type = Type::from(typelib_TypeClass::typelib_TypeClass_ANY); + + println!( + "Boolean: {} (class: {:?})", + boolean_type.get_type_name(), + boolean_type.get_type_class() + ); + println!( + "Byte: {} (class: {:?})", + byte_type.get_type_name(), + byte_type.get_type_class() + ); + println!( + "Short: {} (class: {:?})", + short_type.get_type_name(), + short_type.get_type_class() + ); + println!( + "Long: {} (class: {:?})", + long_type.get_type_name(), + long_type.get_type_class() + ); + println!( + "Float: {} (class: {:?})", + float_type.get_type_name(), + float_type.get_type_class() + ); + println!( + "Double: {} (class: {:?})", + double_type.get_type_name(), + double_type.get_type_class() + ); + println!( + "Char: {} (class: {:?})", + char_type.get_type_name(), + char_type.get_type_class() + ); + println!( + "String: {} (class: {:?})", + string_type.get_type_name(), + string_type.get_type_class() + ); + println!( + "Type: {} (class: {:?})", + type_type.get_type_name(), + type_type.get_type_class() + ); + println!( + "Any: {} (class: {:?})", + any_type.get_type_name(), + any_type.get_type_class() + ); + + // === Complex Types with Names === + println!("\n--- Complex Types with Names ---"); + + // Interface types using tuple syntax + let component_type = Type::from(( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + "com.sun.star.lang.XComponent", + )); + + let property_set_type = Type::from(( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + "com.sun.star.beans.XPropertySet", + )); + + // Service types + let text_service = Type::new_with_name( + typelib_TypeClass::typelib_TypeClass_SERVICE, + "com.sun.star.text.Text", + ); + + // Using String and OUString for type names + let string_name = "com.sun.star.awt.XControl".to_string(); + let control_type = Type::from((typelib_TypeClass::typelib_TypeClass_INTERFACE, string_name)); + + let oustring_name = OUString::from("com.sun.star.uno.XInterface"); + let interface_type = Type::new_with_name( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + oustring_name, + ); + + println!( + "Component: {} (class: {:?})", + component_type.get_type_name(), + component_type.get_type_class() + ); + println!( + "PropertySet: {} (class: {:?})", + property_set_type.get_type_name(), + property_set_type.get_type_class() + ); + println!( + "Text Service: {} (class: {:?})", + text_service.get_type_name(), + text_service.get_type_class() + ); + println!( + "Control: {} (class: {:?})", + control_type.get_type_name(), + control_type.get_type_class() + ); + println!( + "XInterface: {} (class: {:?})", + interface_type.get_type_name(), + interface_type.get_type_class() + ); + + // === Type Properties and Operations === + println!("\n--- Type Properties and Operations ---"); + + println!("Type class of long: {:?}", long_type.get_type_class()); + println!( + "Type name of component: '{}'", + component_type.get_type_name() + ); + + // Type equality testing + let long_type2 = Type::from(typelib_TypeClass::typelib_TypeClass_LONG); + let short_type2 = Type::from(typelib_TypeClass::typelib_TypeClass_SHORT); + + println!("long == long: {}", long_type.equals(&long_type2)); + println!("long == short: {}", long_type.equals(&short_type2)); + + // Assignment compatibility testing + println!( + "long assignable from short: {}", + long_type.is_assignable_from(&short_type) + ); + println!( + "short assignable from long: {}", + short_type.is_assignable_from(&long_type) + ); + println!( + "any assignable from long: {}", + any_type.is_assignable_from(&long_type) + ); + println!( + "long assignable from any: {}", + long_type.is_assignable_from(&any_type) + ); + + // === Trait Examples === + println!("\n--- Trait Examples ---"); + + // Clone trait + let original = Type::from(( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + "com.sun.star.lang.XServiceInfo", + )); + let cloned = original.clone(); + println!("Original: {}", original.get_type_name()); + println!("Cloned: {}", cloned.get_type_name()); + println!("Clone equals original: {}", original.equals(&cloned)); + + // PartialEq trait (using == operator) + let type1 = Type::from(typelib_TypeClass::typelib_TypeClass_STRING); + let type2 = Type::from(typelib_TypeClass::typelib_TypeClass_STRING); + let type3 = Type::from(typelib_TypeClass::typelib_TypeClass_LONG); + println!("string == string: {}", type1 == type2); + println!("string != long: {}", type1 != type3); + + // Debug trait + println!("Debug output: {long_type:?}"); + + // === Additional Type Examples === + println!("\n--- Additional Type Examples ---"); + + // Create types for common UNO interfaces + let service_manager_type = Type::from(( + typelib_TypeClass::typelib_TypeClass_SERVICE, + "com.sun.star.lang.XMultiServiceFactory", + )); + + let document_type = Type::from(( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + "com.sun.star.document.XDocumentPropertiesSupplier", + )); + + println!( + "Service Manager: {} (class: {:?})", + service_manager_type.get_type_name(), + service_manager_type.get_type_class() + ); + println!( + "Document: {} (class: {:?})", + document_type.get_type_name(), + document_type.get_type_class() + ); + + // Create expected sal_Int32 type for comparison + let sal_int32_type = Type::from((typelib_TypeClass::typelib_TypeClass_LONG, "sal_Int32")); + println!( + "sal_Int32 type: {} (class: {:?})", + sal_int32_type.get_type_name(), + sal_int32_type.get_type_class() + ); + + // === Advanced Type Operations === + println!("\n--- Advanced Type Operations ---"); + + // Demonstrate type compatibility chains + let unsigned_short_type = Type::from(typelib_TypeClass::typelib_TypeClass_UNSIGNED_SHORT); + let unsigned_long_type = Type::from(typelib_TypeClass::typelib_TypeClass_UNSIGNED_LONG); + let hyper_type = Type::from(typelib_TypeClass::typelib_TypeClass_HYPER); + + println!("Type compatibility chain:"); + println!( + " unsigned_long assignable from unsigned_short: {}", + unsigned_long_type.is_assignable_from(&unsigned_short_type) + ); + println!( + " hyper assignable from unsigned_short: {}", + hyper_type.is_assignable_from(&unsigned_short_type) + ); + println!( + " hyper assignable from long: {}", + hyper_type.is_assignable_from(&long_type) + ); + + // Interface hierarchy compatibility + let xinterface_type = Type::from(( + typelib_TypeClass::typelib_TypeClass_INTERFACE, + "com.sun.star.uno.XInterface", + )); + println!( + "XInterface assignable from XComponent: {}", + xinterface_type.is_assignable_from(&component_type) + ); + println!( + "XInterface type: {} (class: {:?})", + xinterface_type.get_type_name(), + xinterface_type.get_type_class() + ); + println!( + "XComponent type: {} (class: {:?})", + component_type.get_type_name(), + component_type.get_type_class() + ); + + // === Memory Management Demo === + println!("\n--- Memory Management Demo ---"); + + // Create multiple references to the same type + let type_a = Type::from(typelib_TypeClass::typelib_TypeClass_DOUBLE); + let type_b = type_a.clone(); + let type_c = Type::from(typelib_TypeClass::typelib_TypeClass_DOUBLE); + + println!("type_a: {}", type_a.get_type_name()); + println!("type_b (cloned): {}", type_b.get_type_name()); + println!("type_c (new): {}", type_c.get_type_name()); + println!( + "All three equal: {}", + type_a.equals(&type_b) && type_b.equals(&type_c) + ); + + // Types are automatically cleaned up when they go out of scope + // due to RAII (Drop trait implementation) + + println!("\n✓ UNO Type system examples completed!"); + println!("✓ All Type creation methods, operations, and traits demonstrated!"); + println!("✓ Type system functionality working correctly!"); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/ffi/mod.rs b/rust_uno/src/ffi/mod.rs new file mode 100644 index 000000000000..6f2518283076 --- /dev/null +++ b/rust_uno/src/ffi/mod.rs @@ -0,0 +1,27 @@ +/* -*- Mode: rust; 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/. + */ + +//! FFI modules for interacting with C/C++ code. + +// This module will contain the raw bindings generated by bindgen. +pub mod uno_any; +pub mod uno_bridge; +pub mod uno_sequence; + +pub mod rtl_string; +pub mod sal_types; +pub mod type_ffi; + +// Include unit tests +#[cfg(test)] +mod tests { + mod integration_tests; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/ffi/rtl_string.rs b/rust_uno/src/ffi/rtl_string.rs new file mode 100644 index 000000000000..bb1f0a9f0dd5 --- /dev/null +++ b/rust_uno/src/ffi/rtl_string.rs @@ -0,0 +1,87 @@ +/* -*- Mode: rust; 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/. + */ + +//! RTL String FFI Bindings +//! +//! This module contains the raw FFI bindings for LibreOffice's rtl_uString +//! functions and types. These are low-level C API bindings that should +//! typically be used through the high-level OUString wrapper. + +// Allow non-standard naming for FFI types that must match C API exactly +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +use crate::ffi::sal_types::*; + +// === RTL String Types and FFI Declarations === + +/// Interlocked count type for reference counting and atomic operations +pub type oslInterlockedCount = sal_Int32; + +/// Text encoding constants +pub type rtl_TextEncoding = sal_Int32; +pub const RTL_TEXTENCODING_UTF8: rtl_TextEncoding = 76; + +/// Conversion flags for string conversion +pub const OSTRING_TO_OUSTRING_CVTFLAGS: sal_uInt32 = 0x00000003; + +/// Internal structure for LibreOffice UNO strings +#[repr(C)] +pub struct rtl_uString { + /// reference count of string + pub refCount: oslInterlockedCount, + /// length of the string in UTF-16 code units + pub length: sal_Int32, + /// UTF-16 buffer array + pub buffer: [sal_Unicode; 1usize], +} + +unsafe extern "C" { + /// Create a new empty string + pub fn rtl_uString_new(newStr: *mut *mut rtl_uString); + + /// Create string from ASCII + pub fn rtl_uString_newFromAscii( + newStr: *mut *mut rtl_uString, + value: *const std::os::raw::c_char, + ); + + /// Create string from existing string + pub fn rtl_uString_newFromString(newStr: *mut *mut rtl_uString, value: *const rtl_uString); + + /// Create string from UTF-16 data with length + pub fn rtl_uString_newFromStr_WithLength( + newStr: *mut *mut rtl_uString, + value: *const sal_Unicode, + len: sal_Int32, + ); + + /// Create UNO string from byte string with encoding + pub fn rtl_string2UString( + newStr: *mut *mut rtl_uString, + str: *const std::os::raw::c_char, + len: sal_Int32, + encoding: rtl_TextEncoding, + convertFlags: sal_uInt32, + ); + + /// Release string reference + pub fn rtl_uString_release(str: *mut rtl_uString); + + /// Acquire string reference + pub fn rtl_uString_acquire(str: *mut rtl_uString); + + /// Get string length + pub fn rtl_uString_getLength(str: *const rtl_uString) -> sal_Int32; + + /// Get string data pointer + pub fn rtl_uString_getStr(str: *const rtl_uString) -> *const sal_Unicode; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/ffi/sal_types.rs b/rust_uno/src/ffi/sal_types.rs new file mode 100644 index 000000000000..784db4c1c51c --- /dev/null +++ b/rust_uno/src/ffi/sal_types.rs @@ -0,0 +1,50 @@ +/* -*- Mode: rust; 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/. + */ + +//! UNO Basic Types - Cross-Platform Implementation +//! +//! This module contains the fundamental SAL (System Abstraction Layer) types +//! that form the foundation of UNO's type system. These types use Rust native +//! types to ensure consistent behavior across all platforms. + +// Allow non-standard naming for FFI types that must match C API exactly +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +// === Fixed-size integer types === +// These types have guaranteed sizes across all platforms + +pub type sal_Bool = u8; // Always 8-bit boolean +pub type sal_Int8 = i8; // Always 8-bit signed +pub type sal_uInt8 = u8; // Always 8-bit unsigned +pub type sal_Int16 = i16; // Always 16-bit signed +pub type sal_uInt16 = u16; // Always 16-bit unsigned +pub type sal_Int32 = i32; // Always 32-bit signed +pub type sal_uInt32 = u32; // Always 32-bit unsigned +pub type sal_Int64 = i64; // Always 64-bit signed +pub type sal_uInt64 = u64; // Always 64-bit unsigned + +// === Platform-dependent types === +// These types adapt to the target platform's pointer width + +pub type sal_Size = usize; // Platform pointer width (32/64-bit) +pub type sal_sSize = isize; // Signed platform pointer width +pub type sal_PtrDiff = isize; // Pointer difference type +pub type sal_IntPtr = isize; // Integer type that can hold a pointer +pub type sal_uIntPtr = usize; // Unsigned integer type that can hold a pointer + +// === Special types === + +pub type sal_Unicode = u16; // UTF-16 code unit (always 16-bit) +pub type sal_Handle = *mut std::ffi::c_void; // Generic opaque handle + +pub const SAL_FALSE: sal_Bool = 0; +pub const SAL_TRUE: sal_Bool = 1; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/ffi/tests/integration_tests.rs b/rust_uno/src/ffi/tests/integration_tests.rs new file mode 100644 index 000000000000..4b89c229e052 --- /dev/null +++ b/rust_uno/src/ffi/tests/integration_tests.rs @@ -0,0 +1,828 @@ +/* -*- Mode: rust; 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/. + */ + +//! Integration tests for Rust UNO Language Binding +//! +//! These tests verify that the UNO binding works correctly with the +//! actual LibreOffice UNO components through FFI/C interop. +//! +//! **Note**: Rust-side unit tests (traits, constructors, etc.) are located +//! in separate test files like `string_tests.rs`. +//! +//! # Test Coverage +//! +//! ## Core FFI Functionality Tests +//! - `test_xtest_interface_creation()` - Basic interface instantiation +//! - `test_boolean_operations()` - Boolean data type FFI handling (both true/false) +//! - `test_integer_operations()` - Signed integer types FFI with wrong values +//! - `test_unsigned_integer_operations()` - Unsigned integer types FFI with wrong values +//! - `test_64bit_operations()` - 64-bit integer types FFI with wrong values +//! - `test_floating_point_operations()` - Float and double types FFI with wrong values +//! - `test_char_operations()` - Unicode character FFI handling with wrong values +//! - `test_string_operations()` - OUString FFI handling with UNO components +//! - `test_type_operations()` - UNO Type FFI interop with C++ components +//! - `test_rust_type_creation()` - Rust Type wrapper construction and basic operations +//! - `test_type_assignment_compatibility()` - Type assignment compatibility testing +//! - `test_type_cloning_and_memory()` - Type cloning and memory management validation +//! +//! ## Comprehensive UNO Component Validation Tests +//! - `test_wrong_values_comprehensive()` - Extensive wrong value testing for all data types +//! - `test_edge_cases_and_boundary_values()` - Boundary conditions and special values +//! - `test_consistency_across_multiple_calls()` - Stability and consistency verification +//! - `test_all_operations_comprehensive()` - Cross-functional integration testing +//! +//! ## Test Strategy +//! Each FFI function is tested with: +//! - Correct expected values (should return true) +//! - Multiple wrong values (should return false) +//! - Boundary values and edge cases +//! - Consistency across multiple calls +//! +//! This ensures robust validation of the UNO binding's FFI data type handling +//! and error detection capabilities with actual UNO components. + +use crate::ffi::rtl_string::rtl_uString; +use crate::ffi::sal_types::*; +use crate::ffi::type_ffi::typelib_TypeDescriptionReference; +use crate::core::{OUString, Type, typelib_TypeClass}; + +// === FFI Declarations for Test Functions === +#[allow(unused)] +unsafe extern "C" { + /// Print a greeting message from the UNO test component + pub fn xtest_hello(); + + /// Get a boolean value from UNO test component (always returns true) + pub fn xtest_getBoolean() -> sal_Bool; + + /// Test if a boolean value matches the expected UNO boolean + pub fn xtest_isBoolean(value: sal_Bool) -> sal_Bool; + + /// Get a byte value from UNO test component (always returns -12) + pub fn xtest_getByte() -> sal_Int8; + + /// Test if a byte value matches the expected UNO byte (-12) + pub fn xtest_isByte(value: sal_Int8) -> sal_Bool; + + /// Get a short value from UNO test component (always returns -1234) + pub fn xtest_getShort() -> sal_Int16; + + /// Test if a short value matches the expected UNO short (-1234) + pub fn xtest_isShort(value: sal_Int16) -> sal_Bool; + + /// Get a long value from UNO test component (always returns -123456) + pub fn xtest_getLong() -> sal_Int32; + + /// Test if a long value matches the expected UNO long (-123456) + pub fn xtest_isLong(value: sal_Int32) -> sal_Bool; + + /// Get an unsigned short value from UNO test component (always returns 54321) + pub fn xtest_getUnsignedShort() -> sal_uInt16; + + /// Test if an unsigned short value matches the expected UNO unsigned short (54321) + pub fn xtest_isUnsignedShort(value: sal_uInt16) -> sal_Bool; + + /// Get an unsigned long value from UNO test component (always returns 3456789012) + pub fn xtest_getUnsignedLong() -> sal_uInt32; + + /// Test if an unsigned long value matches the expected UNO unsigned long (3456789012) + pub fn xtest_isUnsignedLong(value: sal_uInt32) -> sal_Bool; + + /// Get a hyper (64-bit) value from UNO test component (always returns -123456789) + pub fn xtest_getHyper() -> sal_Int64; + + /// Test if a hyper value matches the expected UNO hyper (-123456789) + pub fn xtest_isHyper(value: sal_Int64) -> sal_Bool; + + /// Get an unsigned hyper (64-bit) value from UNO test component (always returns 9876543210) + pub fn xtest_getUnsignedHyper() -> sal_uInt64; + + /// Test if an unsigned hyper value matches the expected UNO unsigned hyper (9876543210) + pub fn xtest_isUnsignedHyper(value: sal_uInt64) -> sal_Bool; + + /// Get a float value from UNO test component (always returns -10.25) + pub fn xtest_getFloat() -> f32; + + /// Test if a float value matches the expected UNO float (-10.25) + pub fn xtest_isFloat(value: f32) -> sal_Bool; + + /// Get a double value from UNO test component (always returns 100.5) + pub fn xtest_getDouble() -> f64; + + /// Test if a double value matches the expected UNO double (100.5) + pub fn xtest_isDouble(value: f64) -> sal_Bool; + + /// Test if a character value matches the expected UNO character ('Ö') + pub fn xtest_isChar(value: sal_Unicode) -> sal_Bool; + + /// Get a character value from UNO test component (always returns 'Ö') + pub fn xtest_getChar() -> sal_Unicode; + + /// Get a string value from UNO test component - returns pointer to rtl_uString + pub fn xtest_getString() -> *mut rtl_uString; + + /// Test if a string value matches the expected string ("hä") - takes pointer to rtl_uString + pub fn xtest_isString(value: *mut rtl_uString) -> sal_Bool; + + /// Get a Type value from UNO test component - returns pointer to typelib_TypeDescriptionReference + pub fn xtest_getType() -> *mut typelib_TypeDescriptionReference; + + /// Test if a Type value matches the expected Type (cppu::UnoType<sal_Int32>) - takes pointer to typelib_TypeDescriptionReference + pub fn xtest_isType(value: *mut typelib_TypeDescriptionReference) -> sal_Bool; +} + +// === Test Interface Wrapper === + +/// Rust wrapper for the XTest UNO interface (TEST ONLY) +/// +/// This interface provides methods for testing basic UNO data types +/// and serves as a demonstration of the Rust UNO binding functionality. +/// +/// **Note**: This is a test interface, not part of the core UNO API. +pub struct XTest; + +#[allow(unused)] +impl XTest { + /// Create a new XTest interface instance + pub fn new() -> Self { + XTest + } + + /// Print a greeting message from the UNO component + pub fn hello(&self) { + unsafe { + xtest_hello(); + } + } + + /// Get a boolean value from UNO + pub fn get_boolean(&self) -> bool { + unsafe { + let result = xtest_getBoolean(); + result != 0 + } + } + + /// Test if a boolean value matches the expected UNO boolean + pub fn is_boolean(&self, value: bool) -> bool { + unsafe { + let sal_value = if value { 1 } else { 0 }; + let result = xtest_isBoolean(sal_value); + result != 0 + } + } + + /// Get a byte value from UNO + pub fn get_byte(&self) -> i8 { + unsafe { xtest_getByte() } + } + + /// Test if a byte value matches the expected UNO byte + pub fn is_byte(&self, value: i8) -> bool { + unsafe { + let result = xtest_isByte(value); + result != 0 + } + } + + /// Get a short value from UNO + pub fn get_short(&self) -> i16 { + unsafe { xtest_getShort() } + } + + /// Test if a short value matches the expected UNO short + pub fn is_short(&self, value: i16) -> bool { + unsafe { + let result = xtest_isShort(value); + result != 0 + } + } + + /// Get a long value from UNO + pub fn get_long(&self) -> i32 { + unsafe { xtest_getLong() } + } + + /// Test if a long value matches the expected UNO long + pub fn is_long(&self, value: i32) -> bool { + unsafe { + let result = xtest_isLong(value); + result != 0 + } + } + + /// Get an unsigned short value from UNO + pub fn get_unsigned_short(&self) -> u16 { + unsafe { xtest_getUnsignedShort() } + } + + /// Test if an unsigned short value matches the expected UNO unsigned short + pub fn is_unsigned_short(&self, value: u16) -> bool { + unsafe { + let result = xtest_isUnsignedShort(value); + result != 0 + } + } + + /// Get an unsigned long value from UNO + pub fn get_unsigned_long(&self) -> u32 { + unsafe { xtest_getUnsignedLong() } + } + + /// Test if an unsigned long value matches the expected UNO unsigned long + pub fn is_unsigned_long(&self, value: u32) -> bool { + unsafe { + let result = xtest_isUnsignedLong(value); + result != 0 + } + } + + /// Get a hyper (64-bit) value from UNO + pub fn get_hyper(&self) -> i64 { + unsafe { xtest_getHyper() } + } + + /// Test if a hyper value matches the expected UNO hyper + pub fn is_hyper(&self, value: i64) -> bool { + unsafe { + let result = xtest_isHyper(value); + result != 0 + } + } + + /// Get an unsigned hyper (64-bit) value from UNO + pub fn get_unsigned_hyper(&self) -> u64 { + unsafe { xtest_getUnsignedHyper() } + } + + /// Test if an unsigned hyper value matches the expected UNO unsigned hyper + pub fn is_unsigned_hyper(&self, value: u64) -> bool { + unsafe { + let result = xtest_isUnsignedHyper(value); + result != 0 + } + } + + /// Get a float value from UNO + pub fn get_float(&self) -> f32 { + unsafe { xtest_getFloat() } + } + + /// Test if a float value matches the expected UNO float + pub fn is_float(&self, value: f32) -> bool { + unsafe { + let result = xtest_isFloat(value); + result != 0 + } + } + + /// Get a double value from UNO + pub fn get_double(&self) -> f64 { + unsafe { xtest_getDouble() } + } + + /// Test if a double value matches the expected UNO double + pub fn is_double(&self, value: f64) -> bool { + unsafe { + let result = xtest_isDouble(value); + result != 0 + } + } + /// Get a char value from UNO + pub fn get_char(&self) -> sal_Unicode { + unsafe { xtest_getChar() } + } + + /// Test if a char value matches the expected UNO char + pub fn is_char(&self, value: sal_Unicode) -> bool { + unsafe { + let result = xtest_isChar(value); + result != 0 + } + } + + /// Get a string value from UNO + pub fn get_string(&self) -> OUString { + unsafe { + let result = xtest_getString(); + OUString::from_raw(result) + } + } + + /// Test if a string value matches the expected UNO string + pub fn is_string(&self, value: OUString) -> bool { + unsafe { + let result = xtest_isString(value.into_raw()); + result != 0 + } + } + + /// Get a Type value from UNO (returns cppu::UnoType<sal_Int32>) + pub fn get_type(&self) -> Type { + unsafe { Type::from_typelib_ref_no_acquire(xtest_getType()) } + } + + /// Test if a Type value matches the expected UNO Type + /// + /// Note: This test uses raw pointers for C++ Type interop. + /// For Rust Type instances, use direct comparison methods. + pub fn is_type(&self, cpp_type: Type) -> bool { + unsafe { + let result = xtest_isType(cpp_type.into_raw()); + result != 0 + } + } +} + +impl Default for XTest { + fn default() -> Self { + Self::new() + } +} + +// === Integration Tests === + +#[test] +fn test_xtest_interface_creation() { + let xtest = XTest::new(); + // Test that we can create the interface without panicking + xtest.hello(); // This should print a message +} + +#[test] +fn test_boolean_operations() { + let xtest = XTest::new(); + + // Test getBoolean + let result = xtest.get_boolean(); + assert!(result, "getBoolean should return true"); + + // Test isBoolean with true + let is_true = xtest.is_boolean(true); + assert!(is_true, "isBoolean(true) should return true"); + + // Test isBoolean with false + let is_false = xtest.is_boolean(false); + assert!(!is_false, "isBoolean(false) should return false"); +} + +#[test] +fn test_integer_operations() { + let xtest = XTest::new(); + + // Test byte operations + let byte_val = xtest.get_byte(); + assert_eq!(byte_val, -12, "getByte should return -12"); + + let is_byte = xtest.is_byte(-12); + assert!(is_byte, "isByte(-12) should return true"); + + let is_wrong_byte = xtest.is_byte(0); + assert!(!is_wrong_byte, "isByte(0) should return false"); + + // Test short operations + let short_val = xtest.get_short(); + assert_eq!(short_val, -1234, "getShort should return -1234"); + + let is_short = xtest.is_short(-1234); + assert!(is_short, "isShort(-1234) should return true"); + + let is_wrong_short = xtest.is_short(0); + assert!(!is_wrong_short, "isShort(0) should return false"); + + // Test long operations + let long_val = xtest.get_long(); + assert_eq!(long_val, -123456, "getLong should return -123456"); + + let is_long = xtest.is_long(-123456); + assert!(is_long, "isLong(-123456) should return true"); + + let is_wrong_long = xtest.is_long(0); + assert!(!is_wrong_long, "isLong(0) should return false"); +} + +#[test] +fn test_unsigned_integer_operations() { + let xtest = XTest::new(); + + // Test unsigned short operations + let ushort_val = xtest.get_unsigned_short(); + assert_eq!(ushort_val, 54321, "getUnsignedShort should return 54321"); + + let is_ushort = xtest.is_unsigned_short(54321); + assert!(is_ushort, "isUnsignedShort(54321) should return true"); + + let is_wrong_ushort = xtest.is_unsigned_short(0); + assert!(!is_wrong_ushort, "isUnsignedShort(0) should return false"); + + // Test unsigned long operations + let ulong_val = xtest.get_unsigned_long(); + assert_eq!( + ulong_val, 3456789012, + "getUnsignedLong should return 3456789012" + ); + + let is_ulong = xtest.is_unsigned_long(3456789012); + assert!(is_ulong, "isUnsignedLong(3456789012) should return true"); + + let is_wrong_ulong = xtest.is_unsigned_long(0); + assert!(!is_wrong_ulong, "isUnsignedLong(0) should return false"); +} + +#[test] +fn test_64bit_operations() { + let xtest = XTest::new(); + + // Test hyper operations + let hyper_val = xtest.get_hyper(); + assert_eq!(hyper_val, -123456789, "getHyper should return -123456789"); + + let is_hyper = xtest.is_hyper(-123456789); + assert!(is_hyper, "isHyper(-123456789) should return true"); + + let is_wrong_hyper = xtest.is_hyper(0); + assert!(!is_wrong_hyper, "isHyper(0) should return false"); + + // Test unsigned hyper operations + let uhyper_val = xtest.get_unsigned_hyper(); + assert_eq!( + uhyper_val, 9876543210, + "getUnsignedHyper should return 9876543210" + ); + + let is_uhyper = xtest.is_unsigned_hyper(9876543210); + assert!(is_uhyper, "isUnsignedHyper(9876543210) should return true"); + + let is_wrong_uhyper = xtest.is_unsigned_hyper(0); + assert!(!is_wrong_uhyper, "isUnsignedHyper(0) should return false"); +} + +#[test] +fn test_floating_point_operations() { + let xtest = XTest::new(); + + // Test float operations + let float_val = xtest.get_float(); + assert_eq!(float_val, -10.25, "getFloat should return -10.25"); + + let is_float = xtest.is_float(-10.25); + assert!(is_float, "isFloat(-10.25) should return true"); + + let is_wrong_float = xtest.is_float(0.0); + assert!(!is_wrong_float, "isFloat(0.0) should return false"); + + // Test double operations + let double_val = xtest.get_double(); + assert_eq!(double_val, 100.5, "getDouble should return 100.5"); + + let is_double = xtest.is_double(100.5); + assert!(is_double, "isDouble(100.5) should return true"); + + let is_wrong_double = xtest.is_double(0.0); + assert!(!is_wrong_double, "isDouble(0.0) should return false"); +} + +#[test] +fn test_char_operations() { + let xtest = XTest::new(); + + // Test char operations - 'Ö' is Unicode codepoint U+00D6 (214) + let char_val = xtest.get_char(); + assert_eq!( + char_val, 214u16, + "getChar should return 214 (Unicode for 'Ö')" + ); + + let is_char = xtest.is_char(214u16); + assert!(is_char, "isChar(214) should return true"); + + let is_wrong_char = xtest.is_char(65u16); // 'A' + assert!(!is_wrong_char, "isChar(65) should return false"); +} + +#[test] +fn test_string_operations() { + let xtest = XTest::new(); + + // Test string operations - expected value is "hä" + let string_val = xtest.get_string(); + assert_eq!(string_val.to_string(), "hä", "getString should return 'hä'"); + + // Test string validation with correct value + let test_string = OUString::from_utf8("hä"); + let is_string = xtest.is_string(test_string); + assert!(is_string, "isString('hä') should return true"); + + // Test string validation with wrong value + let wrong_string = OUString::from_utf8("wrong"); + let is_wrong_string = xtest.is_string(wrong_string); + assert!(!is_wrong_string, "isString('wrong') should return false"); +} + +#[test] +fn test_type_operations() { + let xtest = XTest::new(); + + // Test getting a Type value from C++ component + let cpp_type = xtest.get_type(); + // Test that we got a valid Type (non-void type in this case) + assert_ne!( + cpp_type.get_type_class(), + typelib_TypeClass::typelib_TypeClass_VOID, + "C++ Type should not be void type" + ); + println!("Got C++ Type: {:?}", cpp_type); + + // Test that the returned Type matches the expected Type (cppu::UnoType<sal_Int32>) + let is_correct = xtest.is_type(cpp_type); + assert!(is_correct, "C++ Type should match expected sal_Int32 type"); +} + +#[test] +fn test_wrong_values_comprehensive() { + let xtest = XTest::new(); + + // Test multiple wrong values for each function to ensure robust validation + + // Test multiple wrong byte values + let is_wrong_byte1 = xtest.is_byte(100); + assert!(!is_wrong_byte1, "isByte(100) should return false"); + + let is_wrong_byte2 = xtest.is_byte(-100); + assert!(!is_wrong_byte2, "isByte(-100) should return false"); + + // Test multiple wrong short values + let is_wrong_short1 = xtest.is_short(1000); + assert!(!is_wrong_short1, "isShort(1000) should return false"); + + let is_wrong_short2 = xtest.is_short(-5000); + assert!(!is_wrong_short2, "isShort(-5000) should return false"); + + // Test multiple wrong long values + let is_wrong_long1 = xtest.is_long(999999); + assert!(!is_wrong_long1, "isLong(999999) should return false"); + + let is_wrong_long2 = xtest.is_long(-999999); + assert!(!is_wrong_long2, "isLong(-999999) should return false"); + + // Test multiple wrong unsigned short values + let is_wrong_ushort1 = xtest.is_unsigned_short(12345); + assert!( + !is_wrong_ushort1, + "isUnsignedShort(12345) should return false" + ); + + let is_wrong_ushort2 = xtest.is_unsigned_short(65535); + assert!( + !is_wrong_ushort2, + "isUnsignedShort(65535) should return false" + ); + + // Test multiple wrong unsigned long values + let is_wrong_ulong1 = xtest.is_unsigned_long(1234567890); + assert!( + !is_wrong_ulong1, + "isUnsignedLong(1234567890) should return false" + ); + + let is_wrong_ulong2 = xtest.is_unsigned_long(4294967295); + assert!( + !is_wrong_ulong2, + "isUnsignedLong(4294967295) should return false" + ); + + // Test multiple wrong hyper values + let is_wrong_hyper1 = xtest.is_hyper(987654321); + assert!(!is_wrong_hyper1, "isHyper(987654321) should return false"); + + let is_wrong_hyper2 = xtest.is_hyper(-987654321); + assert!(!is_wrong_hyper2, "isHyper(-987654321) should return false"); + + // Test multiple wrong unsigned hyper values + let is_wrong_uhyper1 = xtest.is_unsigned_hyper(1234567890); + assert!( + !is_wrong_uhyper1, + "isUnsignedHyper(1234567890) should return false" + ); + + let is_wrong_uhyper2 = xtest.is_unsigned_hyper(18446744073709551615); + assert!( + !is_wrong_uhyper2, + "isUnsignedHyper(18446744073709551615) should return false" + ); + + // Test multiple wrong float values + let is_wrong_float1 = xtest.is_float(std::f32::consts::PI); + assert!(!is_wrong_float1, "isFloat(3.14159) should return false"); + + let is_wrong_float2 = xtest.is_float(-99.99); + assert!(!is_wrong_float2, "isFloat(-99.99) should return false"); + + // Test multiple wrong double values + let is_wrong_double1 = xtest.is_double(std::f64::consts::E); + assert!(!is_wrong_double1, "isDouble(2.71828) should return false"); + + let is_wrong_double2 = xtest.is_double(-200.75); + assert!(!is_wrong_double2, "isDouble(-200.75) should return false"); + + // Test multiple wrong char values + let is_wrong_char1 = xtest.is_char(72u16); // 'H' + assert!(!is_wrong_char1, "isChar(72) should return false"); + + let is_wrong_char2 = xtest.is_char(8364u16); // Euro symbol '€' + assert!(!is_wrong_char2, "isChar(8364) should return false"); +} + +#[test] +fn test_all_operations_comprehensive() { + let xtest = XTest::new(); + + // This test verifies that all methods work together + // and the component maintains state correctly + + // Test multiple calls to the same method + let bool1 = xtest.get_boolean(); + let bool2 = xtest.get_boolean(); + assert_eq!( + bool1, bool2, + "Multiple calls to getBoolean should return same value" + ); + + // Test that different data types work independently + let _byte_val = xtest.get_byte(); + let _short_val = xtest.get_short(); + let _long_val = xtest.get_long(); + let _float_val = xtest.get_float(); + let _double_val = xtest.get_double(); + + // Verify boolean still works after other operations + let bool3 = xtest.get_boolean(); + assert_eq!(bool1, bool3, "Boolean operations should be consistent"); +} + +#[test] +fn test_edge_cases_and_boundary_values() { + let xtest = XTest::new(); + + // Test edge cases for each data type to ensure robust handling + + // Test maximum and minimum values for signed integers + let is_max_byte = xtest.is_byte(i8::MAX); + assert!(!is_max_byte, "isByte(i8::MAX) should return false"); + + let is_min_byte = xtest.is_byte(i8::MIN); + assert!(!is_min_byte, "isByte(i8::MIN) should return false"); + + let is_max_short = xtest.is_short(i16::MAX); + assert!(!is_max_short, "isShort(i16::MAX) should return false"); + + let is_min_short = xtest.is_short(i16::MIN); + assert!(!is_min_short, "isShort(i16::MIN) should return false"); + + let is_max_long = xtest.is_long(i32::MAX); + assert!(!is_max_long, "isLong(i32::MAX) should return false"); + + let is_min_long = xtest.is_long(i32::MIN); + assert!(!is_min_long, "isLong(i32::MIN) should return false"); + + // Test maximum values for unsigned integers + let is_max_ushort = xtest.is_unsigned_short(u16::MAX); + assert!( + !is_max_ushort, + "isUnsignedShort(u16::MAX) should return false" + ); + + let is_max_ulong = xtest.is_unsigned_long(u32::MAX); + assert!( + !is_max_ulong, + "isUnsignedLong(u32::MAX) should return false" + ); + + // Test edge cases for 64-bit integers + let is_max_hyper = xtest.is_hyper(i64::MAX); + assert!(!is_max_hyper, "isHyper(i64::MAX) should return false"); + + let is_min_hyper = xtest.is_hyper(i64::MIN); + assert!(!is_min_hyper, "isHyper(i64::MIN) should return false"); + + let is_max_uhyper = xtest.is_unsigned_hyper(u64::MAX); + assert!( + !is_max_uhyper, + "isUnsignedHyper(u64::MAX) should return false" + ); + + // Test floating point edge cases + let is_infinity = xtest.is_float(f32::INFINITY); + assert!(!is_infinity, "isFloat(INFINITY) should return false"); + + let is_neg_infinity = xtest.is_float(f32::NEG_INFINITY); + assert!( + !is_neg_infinity, + "isFloat(NEG_INFINITY) should return false" + ); + + let is_double_infinity = xtest.is_double(f64::INFINITY); + assert!( + !is_double_infinity, + "isDouble(INFINITY) should return false" + ); + + let is_double_neg_infinity = xtest.is_double(f64::NEG_INFINITY); + assert!( + !is_double_neg_infinity, + "isDouble(NEG_INFINITY) should return false" + ); + + // Test special Unicode characters + let is_null_char = xtest.is_char(0u16); // Null character + assert!(!is_null_char, "isChar(0) should return false"); + + let is_high_unicode = xtest.is_char(0xFFFF); // Maximum Unicode BMP + assert!(!is_high_unicode, "isChar(0xFFFF) should return false"); + + // Test empty and special strings + let empty_string = OUString::from_utf8(""); + let is_empty_string = xtest.is_string(empty_string); + assert!(!is_empty_string, "isString(\"\") should return false"); + + let unicode_string = OUString::from_utf8("🦀"); // Rust crab emoji + let is_unicode_string = xtest.is_string(unicode_string); + assert!(!is_unicode_string, "isString(\"🦀\") should return false"); +} + +#[test] +fn test_consistency_across_multiple_calls() { + let xtest = XTest::new(); + + // Test that all getter functions return consistent values across multiple calls + + // Test consistency for all data types + for _ in 0..5 { + // Boolean consistency + let bool_val = xtest.get_boolean(); + assert!(bool_val, "getBoolean should always return true"); + + // Integer consistency + let byte_val = xtest.get_byte(); + assert_eq!(byte_val, -12, "getByte should always return -12"); + + let short_val = xtest.get_short(); + assert_eq!(short_val, -1234, "getShort should always return -1234"); + + let long_val = xtest.get_long(); + assert_eq!(long_val, -123456, "getLong should always return -123456"); + + // Unsigned integer consistency + let ushort_val = xtest.get_unsigned_short(); + assert_eq!( + ushort_val, 54321, + "getUnsignedShort should always return 54321" + ); + + let ulong_val = xtest.get_unsigned_long(); + assert_eq!( + ulong_val, 3456789012, + "getUnsignedLong should always return 3456789012" + ); + + // 64-bit consistency + let hyper_val = xtest.get_hyper(); + assert_eq!( + hyper_val, -123456789, + "getHyper should always return -123456789" + ); + + let uhyper_val = xtest.get_unsigned_hyper(); + assert_eq!( + uhyper_val, 9876543210, + "getUnsignedHyper should always return 9876543210" + ); + + // Floating point consistency + let float_val = xtest.get_float(); + assert_eq!(float_val, -10.25, "getFloat should always return -10.25"); + + let double_val = xtest.get_double(); + assert_eq!(double_val, 100.5, "getDouble should always return 100.5"); + + // Character consistency + let char_val = xtest.get_char(); + assert_eq!(char_val, 214u16, "getChar should always return 214"); + + // String consistency + let string_val = xtest.get_string(); + assert_eq!( + string_val.to_string(), + "hä", + "getString should always return 'hä'" + ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/ffi/type_ffi.rs b/rust_uno/src/ffi/type_ffi.rs new file mode 100644 index 000000000000..ad7aa75530f3 --- /dev/null +++ b/rust_uno/src/ffi/type_ffi.rs @@ -0,0 +1,173 @@ +/* -*- Mode: rust; 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/. + */ + +//! UNO Type System FFI +//! +//! This module contains FFI bindings for the UNO type system, including the +//! typelib_TypeClass enum and related structures for type introspection. + +// Allow non-standard naming for FFI types that must match C API exactly +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +use crate::ffi::rtl_string::rtl_uString; +use crate::ffi::sal_types::*; + +// === UNO Type System FFI Declarations === + +/// UNO type class enumeration - binary compatible with the IDL enum com.sun.star.uno.TypeClass +/// +/// This enumeration defines all possible type categories in the UNO type system. +/// Each type has a specific class that determines how it's handled in marshalling, +/// method dispatch, and memory management. The values must match exactly with +/// the corresponding C/C++ enum to ensure binary compatibility. +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum typelib_TypeClass { + typelib_TypeClass_VOID = 0, + typelib_TypeClass_CHAR = 1, + typelib_TypeClass_BOOLEAN = 2, + typelib_TypeClass_BYTE = 3, + typelib_TypeClass_SHORT = 4, + typelib_TypeClass_UNSIGNED_SHORT = 5, + typelib_TypeClass_LONG = 6, + typelib_TypeClass_UNSIGNED_LONG = 7, + typelib_TypeClass_HYPER = 8, + typelib_TypeClass_UNSIGNED_HYPER = 9, + typelib_TypeClass_FLOAT = 10, + typelib_TypeClass_DOUBLE = 11, + typelib_TypeClass_STRING = 12, + typelib_TypeClass_TYPE = 13, + typelib_TypeClass_ANY = 14, + typelib_TypeClass_ENUM = 15, + typelib_TypeClass_TYPEDEF = 16, + typelib_TypeClass_STRUCT = 17, + + #[deprecated = "UNOIDL does not have a union concept"] + typelib_TypeClass_UNION = 18, + typelib_TypeClass_EXCEPTION = 19, + typelib_TypeClass_SEQUENCE = 20, + + #[deprecated = "UNOIDL does not have an array concept"] + typelib_TypeClass_ARRAY = 21, + typelib_TypeClass_INTERFACE = 22, + typelib_TypeClass_SERVICE = 23, + typelib_TypeClass_MODULE = 24, + typelib_TypeClass_INTERFACE_METHOD = 25, + typelib_TypeClass_INTERFACE_ATTRIBUTE = 26, + typelib_TypeClass_UNKNOWN = 27, + typelib_TypeClass_PROPERTY = 28, + typelib_TypeClass_CONSTANT = 29, + typelib_TypeClass_CONSTANTS = 30, + typelib_TypeClass_SINGLETON = 31, + typelib_TypeClass_MAKE_FIXED_SIZE = 0x7fffffff, +} + +/// Weak reference to a type description - the core UNO type reference structure +/// +/// This structure holds a weak reference to a type description in the UNO type system. +/// It provides efficient type identification without requiring the full type description +/// to be loaded. Memory layout is designed for binary compatibility with LibreOffice's +/// C implementation. +#[repr(C)] +pub struct typelib_TypeDescriptionReference { + /// Reference count of type - managed automatically by acquire/release functions + pub nRefCount: sal_Int32, + /// Static reference count for types that persist until program termination + pub nStaticRefCount: sal_Int32, + /// Type class category (VOID, STRING, INTERFACE, etc.) + pub eTypeClass: typelib_TypeClass, + /// Fully qualified name of the type (e.g., "com.sun.star.lang.XComponent") + pub pTypeName: *mut rtl_uString, + /// Pointer to full type description (may be null if not loaded) + pub pType: *mut typelib_TypeDescription, + /// Internal unique identifier for runtime optimization + pub pUniqueIdentifier: *mut std::os::raw::c_void, + /// Reserved for future use + pub pReserved: *mut std::os::raw::c_void, +} + +/// Full type description with complete metadata +/// +/// Contains comprehensive information about a UNO type including size, alignment, +/// and member details. This is loaded on demand when detailed type information +/// is needed for marshalling, method dispatch, or introspection. +#[repr(C)] +pub struct typelib_TypeDescription { + /// Reference count - managed automatically by acquire/release functions + pub nRefCount: sal_Int32, + /// Static reference count for types that persist until program termination + pub nStaticRefCount: sal_Int32, + /// Type class category (VOID, STRING, INTERFACE, etc.) + pub eTypeClass: typelib_TypeClass, + /// Fully qualified name of the type + pub pTypeName: *mut rtl_uString, + /// Self-reference pointer for type identification + pub pSelf: *mut typelib_TypeDescription, + /// Internal unique identifier for runtime optimization + pub pUniqueIdentifier: *mut std::os::raw::c_void, + /// Reserved for future use + pub pReserved: *mut std::os::raw::c_void, + /// Flag indicating whether the description is complete and ready to use + pub bComplete: sal_Bool, + /// Size of the type in bytes + pub nSize: sal_Int32, + /// Memory alignment requirements for the type + pub nAlignment: sal_Int32, + /// Back-reference to the weak type reference + pub pWeakRef: *mut typelib_TypeDescriptionReference, + /// Flag indicating if type can be unloaded and reloaded on demand + pub bOnDemand: sal_Bool, +} + +unsafe extern "C" { + /// Increment reference count of type description reference + pub fn typelib_typedescriptionreference_acquire(pRef: *mut typelib_TypeDescriptionReference); + + /// Decrement reference count of type description reference + pub fn typelib_typedescriptionreference_release(pRef: *mut typelib_TypeDescriptionReference); + + /// Create new type description reference from type class and name + pub fn typelib_typedescriptionreference_new( + ppTDR: *mut *mut typelib_TypeDescriptionReference, + eTypeClass: typelib_TypeClass, + pTypeName: *mut rtl_uString, + ); + + /// Test equality of two type description references + pub fn typelib_typedescriptionreference_equals( + p1: *const typelib_TypeDescriptionReference, + p2: *const typelib_TypeDescriptionReference, + ) -> sal_Bool; + + /// Test type assignment compatibility + pub fn typelib_typedescriptionreference_isAssignableFrom( + pAssignable: *mut typelib_TypeDescriptionReference, + pFrom: *mut typelib_TypeDescriptionReference, + ) -> sal_Bool; + + /// Get full type description from type reference + pub fn typelib_typedescriptionreference_getDescription( + ppRet: *mut *mut typelib_TypeDescription, + pRef: *mut typelib_TypeDescriptionReference, + ); + + /// Get static type reference for primitive type classes + pub fn typelib_static_type_getByTypeClass( + eTypeClass: typelib_TypeClass, + ) -> *mut *mut typelib_TypeDescriptionReference; + + /// Assign type reference (with proper reference counting) + pub fn typelib_typedescriptionreference_assign( + ppDest: *mut *mut typelib_TypeDescriptionReference, + pSource: *mut typelib_TypeDescriptionReference, + ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/ffi/uno_any.rs b/rust_uno/src/ffi/uno_any.rs new file mode 100644 index 000000000000..b1e42df9b681 --- /dev/null +++ b/rust_uno/src/ffi/uno_any.rs @@ -0,0 +1,108 @@ +/* -*- Mode: rust; 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/. + */ + +//! FFI bindings for UNO Any type operations. +//! +//! This module contains direct bindings to LibreOffice's uno_Any +//! C functions for creating, manipulating, and destroying Any values. +//! +//! These are low-level FFI bindings. For safe Rust wrapper, use +//! the `Any` type from the `core` module instead. + +// Allow non-standard naming for FFI types that must match C API exactly +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +// Note: sal_types are included via typelib types +use crate::ffi::type_ffi::{ + typelib_TypeClass, typelib_TypeDescription, typelib_TypeDescriptionReference, +}; + +/// UNO Any type - represents a value of any UNO type +/// This matches the C structure _uno_Any from include/uno/any2.h +#[repr(C)] +pub struct uno_Any { + /// type of value + pub pType: *mut typelib_TypeDescriptionReference, + /// pointer to value; this may point to pReserved + pub pData: *mut std::ffi::c_void, + /// reserved space for storing value + pub pReserved: *mut std::ffi::c_void, +} + +/// Function types for acquire/release callbacks +pub type uno_AcquireFunc = unsafe extern "C" fn(*mut std::ffi::c_void); +pub type uno_ReleaseFunc = unsafe extern "C" fn(*mut std::ffi::c_void); + +unsafe extern "C" { + /// Assign an any with a given value + pub fn uno_any_assign( + pDest: *mut uno_Any, + pSource: *mut std::ffi::c_void, + pTypeDescr: *mut typelib_TypeDescription, + acquire: Option<uno_AcquireFunc>, + release: Option<uno_ReleaseFunc>, + ); + + /// Assign an any with a given value (using type reference) + pub fn uno_type_any_assign( + pDest: *mut uno_Any, + pSource: *mut std::ffi::c_void, + pType: *mut typelib_TypeDescriptionReference, + acquire: Option<uno_AcquireFunc>, + release: Option<uno_ReleaseFunc>, + ); + + /// Constructs an any with a given value + pub fn uno_any_construct( + pDest: *mut uno_Any, + pSource: *mut std::ffi::c_void, + pTypeDescr: *mut typelib_TypeDescription, + acquire: Option<uno_AcquireFunc>, + ); + + /// Constructs an any with a given value (using type reference) + pub fn uno_type_any_construct( + pDest: *mut uno_Any, + pSource: *mut std::ffi::c_void, + pType: *mut typelib_TypeDescriptionReference, + acquire: Option<uno_AcquireFunc>, + ); + + /// Destructs an any + pub fn uno_any_destruct(pValue: *mut uno_Any, release: Option<uno_ReleaseFunc>); + + /// Sets value to void (clears the any) + pub fn uno_any_clear(pValue: *mut uno_Any, release: Option<uno_ReleaseFunc>); + + // Get static type descriptors by type class (returns pointer to pointer) + pub fn typelib_static_type_getByTypeClass( + eTypeClass: typelib_TypeClass, + ) -> *mut *mut typelib_TypeDescriptionReference; + +} + +/// Get type descriptor for boolean values +pub fn get_boolean_type() -> *mut typelib_TypeDescriptionReference { + unsafe { + let ptr_ptr = + typelib_static_type_getByTypeClass(typelib_TypeClass::typelib_TypeClass_BOOLEAN); + *ptr_ptr + } +} + +/// Get type descriptor for 32-bit integer values +pub fn get_long_type() -> *mut typelib_TypeDescriptionReference { + unsafe { + let ptr_ptr = typelib_static_type_getByTypeClass(typelib_TypeClass::typelib_TypeClass_LONG); + *ptr_ptr + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/rust_uno/src/ffi/uno_bridge.rs b/rust_uno/src/ffi/uno_bridge.rs new file mode 100644 index 000000000000..e85a67d6e301 --- /dev/null +++ b/rust_uno/src/ffi/uno_bridge.rs @@ -0,0 +1,37 @@ +/* -*- Mode: rust; 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/. + */ + +//! FFI bindings for UNO bridge functionality. +//! +//! This module contains direct bindings to C++ functions +//! in the UNO bridge for core functionality like initialization +//! and interface management. +//! +//! For specific UNO interface method calls, use the auto-generated +//! FFI functions in the `generated` module instead. + +// c_char import removed - no longer needed + +// Basic UNO types +pub use crate::ffi::sal_types::*; + +/// Opaque pointer to a UNO XInterface +pub type XInterface = std::ffi::c_void; + +/// Function types for acquire/release callbacks +#[allow(non_camel_case_types)] +pub type uno_AcquireFunc = unsafe extern "C" fn(*mut std::ffi::c_void); +#[allow(non_camel_case_types)] +pub type uno_ReleaseFunc = unsafe extern "C" fn(*mut std::ffi::c_void); + +unsafe extern "C" { + /// Direct LibreOffice bootstrap function from cppuhelper + /// Returns Reference<XComponentContext> as opaque pointer + pub fn defaultBootstrap_InitialComponentContext() -> *mut XInterface; +} diff --git a/rust_uno/src/ffi/uno_sequence.rs b/rust_uno/src/ffi/uno_sequence.rs new file mode 100644 index 000000000000..912557bff6a4 --- /dev/null +++ b/rust_uno/src/ffi/uno_sequence.rs @@ -0,0 +1,54 @@ +/* -*- Mode: rust; 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/. + */ + +//! FFI bindings for UNO Any type operations. +//! +//! This module contains direct bindings to LibreOffice's uno_Sequence +//! C functions for creating, manipulating, and destroying Sequence values. +//! +//! These are low-level FFI bindings. For safe Rust wrapper, use +//! the `Sequence` type from the `core` module instead. + +// Allow non-standard naming for FFI types that must match C API exactly +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +use std::os::raw::c_void; + +use crate::ffi::sal_types::sal_Bool; +use crate::ffi::type_ffi::*; +use crate::ffi::uno_bridge::{uno_AcquireFunc, uno_ReleaseFunc}; + +/// UNO Sequence type - a dynamically sized array type. +/// This matches the C structure _uno_Sequence from include/uno/sequence.h +#[repr(C)] +pub struct uno_Sequence { + /// Reference count of sequence + pub nRefCount: i32, + /// element count + pub nElements: i32, + /// elements array + pub elements: [u8; 0], +} + +unsafe extern "C" { + pub fn uno_type_sequence_construct( + ppSequence: *mut *mut uno_Sequence, + pType: *const typelib_TypeDescription, + pElements: *const c_void, + nElements: i32, + acquire: Option<uno_AcquireFunc>, + ) -> sal_Bool; + + pub fn uno_type_sequence_destroy( + pSequence: *mut uno_Sequence, + pType: *const typelib_TypeDescription, + release: Option<uno_ReleaseFunc>, + ); +} diff --git a/rust_uno/src/lib.rs b/rust_uno/src/lib.rs new file mode 100644 index 000000000000..d716168d53c5 --- /dev/null +++ b/rust_uno/src/lib.rs @@ -0,0 +1,24 @@ +//! Generated Rust bindings for LibreOffice UNO types +//! +//! This crate contains automatically generated Rust bindings +//! for LibreOffice UNO (Universal Network Objects) types. + +// Core UNO functionality +pub mod core; +pub mod examples; +pub mod ffi; + +// Auto-generated FFI bindings +pub mod generated; + +/// Entry point function called by LibreOffice to test Rust UNO bindings +/// This function is called from desktop/source/app/app.cxx during LibreOffice startup +#[unsafe(no_mangle)] +pub extern "C" fn run_rust_uno_test() { + println!("=== Rust UNO Bridge Test with Auto-Generated FFI ==="); + + // Run the load_writer example to demonstrate service-style FFI functions + examples::load_writer::run(); + + println!("=== Rust UNO Bridge Test Done ==="); +} diff --git a/rust_uno/uno_bootstrap.cxx b/rust_uno/uno_bootstrap.cxx new file mode 100644 index 000000000000..ef75e81e85d7 --- /dev/null +++ b/rust_uno/uno_bootstrap.cxx @@ -0,0 +1,86 @@ +/* -*- 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 <cppuhelper/bootstrap.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/frame/XComponentLoader.hpp> +#include <com/sun/star/frame/XDesktop.hpp> +#include <sal/log.hxx> +#include <sal/types.h> +#include <rtl/ustring.hxx> + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::frame; + +extern "C" { + +/** + * C wrapper for cppu::defaultBootstrap_InitialComponentContext() + * + * This function provides a C interface to the LibreOffice UNO bootstrap + * functionality, allowing Rust code to initialize the UNO component context. + * + * @return XInterface* - Opaque pointer to XComponentContext (as XInterface) + * Returns nullptr on failure. + */ +SAL_DLLPUBLIC_EXPORT void* defaultBootstrap_InitialComponentContext() +{ + SAL_INFO("rust_uno", "Initializing UNO component context for Rust binding"); + + try + { + // Call the actual LibreOffice bootstrap function + SAL_INFO("rust_uno", "Calling cppu::defaultBootstrap_InitialComponentContext"); + Reference<XComponentContext> xContext = cppu::defaultBootstrap_InitialComponentContext(); + + if (!xContext.is()) + { + SAL_WARN("rust_uno", "LibreOffice bootstrap failed - component context is invalid"); + return nullptr; + } + + SAL_INFO("rust_uno", "Component context created successfully"); + + auto res = new Reference<XComponentContext>(xContext); + + if (!res->is()) + { + SAL_WARN("rust_uno", "Reference wrapper creation failed - context became invalid"); + delete res; + return nullptr; + } + + SAL_INFO("rust_uno", "Reference wrapper created successfully - returning context to Rust"); + // Return as opaque XComponentContext pointer for Rust + // Note: This creates a new Reference that transfers ownership to caller + return res; + } + catch (const Exception& e) + { + SAL_WARN("rust_uno", + "UNO exception during component context initialization: " << e.Message); + return nullptr; + } + catch (const std::exception& e) + { + SAL_WARN("rust_uno", + "Standard exception during component context initialization: " << e.what()); + return nullptr; + } + catch (...) + { + SAL_WARN("rust_uno", "Unknown exception during component context initialization"); + return nullptr; + } +} +} // extern "C" diff --git a/sc/inc/SheetView.hxx b/sc/inc/SheetView.hxx index abb0a3278ca7..cc29eb196423 100644 --- a/sc/inc/SheetView.hxx +++ b/sc/inc/SheetView.hxx @@ -10,6 +10,7 @@ #pragma once #include <vector> #include "SheetViewTypes.hxx" +#include "types.hxx" class ScTable; @@ -30,6 +31,7 @@ public: SheetView(ScTable* pTable); ScTable* getTablePointer() const; + SCTAB getTableNumber() const; /** A sheet view is valid if the pointer to the table is set */ bool isValid() const; @@ -49,6 +51,7 @@ public: /** Returns a sheet view for the ID. */ SheetView get(SheetViewID nID); + std::vector<SheetView> const& getSheetViews() { return maViews; } }; } diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx index 7a4a7a55861e..c7900b8b348d 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -114,7 +114,7 @@ class ExternalDataMapper; class Sparkline; class SparklineGroup; class SparklineList; - +class SheetViewManager; } class Fraction; @@ -2401,6 +2401,7 @@ public: /** Return the sheet view table for the ID */ SCTAB GetSheetViewNumber(SCTAB nTab, sc::SheetViewID nID); + std::shared_ptr<sc::SheetViewManager> GetSheetViewManager(SCTAB nTable); bool IsSheetView(SCTAB nTab) const; void SetSheetView(SCTAB nTab, bool bSheetView); diff --git a/sc/qa/unit/tiledrendering/SheetViewTest.cxx b/sc/qa/unit/tiledrendering/SheetViewTest.cxx index 351ca3d2123c..639dd967007d 100644 --- a/sc/qa/unit/tiledrendering/SheetViewTest.cxx +++ b/sc/qa/unit/tiledrendering/SheetViewTest.cxx @@ -86,6 +86,93 @@ CPPUNIT_TEST_FIXTURE(SheetViewTest, testSheetViewAutoFilter) CPPUNIT_ASSERT_EQUAL(u"7"_ustr, pTabView1->GetCurrentString(0, 4)); } +CPPUNIT_TEST_FIXTURE(SheetViewTest, testSyncValuesBetweenMainSheetAndSheetView) +{ + // Create two views, and leave the second one current. + ScModelObj* pModelObj = createDoc("empty.ods"); + pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>()); + ScDocument* pDocument = pModelObj->GetDocument(); + + // Setup 2 views + ScTestViewCallback aView1; + ScTabViewShell* pTabView1 = aView1.getTabViewShell(); + SfxLokHelper::createView(); + Scheduler::ProcessEventsToIdle(); + ScTestViewCallback aView2; + ScTabViewShell* pTabView2 = aView2.getTabViewShell(); + CPPUNIT_ASSERT(pTabView1 != pTabView2); + CPPUNIT_ASSERT(aView1.getViewID() != aView2.getViewID()); + + // Setup data + // String in A1, and a formula in A2 + ScAddress aA1(0, 0, 0); + ScAddress aA1SheetView(0, 0, 1); + + ScAddress aA2(0, 1, 0); + ScAddress aA2SheetView(0, 1, 1); + + typeCharsInCell(std::string("ABCD"), aA1.Col(), aA1.Row(), pTabView1, pModelObj); + typeCharsInCell(std::string("=UPPER(\"A\"&\"b\"&\"C\"&\"d\")"), aA2.Col(), aA2.Row(), pTabView1, + pModelObj); + + CPPUNIT_ASSERT_EQUAL(u"ABCD"_ustr, pDocument->GetString(aA1)); + CPPUNIT_ASSERT_EQUAL(u"ABCD"_ustr, pDocument->GetString(aA2)); + + // Check what the View1 and View2 sees + CPPUNIT_ASSERT_EQUAL(u"ABCD"_ustr, pTabView1->GetCurrentString(aA1.Col(), aA1.Row())); + CPPUNIT_ASSERT_EQUAL(u"ABCD"_ustr, pTabView2->GetCurrentString(aA1.Col(), aA1.Row())); + + CPPUNIT_ASSERT_EQUAL(u"ABCD"_ustr, pTabView1->GetCurrentString(aA2.Col(), aA2.Row())); + CPPUNIT_ASSERT_EQUAL(u"ABCD"_ustr, pTabView2->GetCurrentString(aA2.Col(), aA2.Row())); + + // Create a sheet view in View2 + SfxLokHelper::setView(aView2.getViewID()); + Scheduler::ProcessEventsToIdle(); + dispatchCommand(mxComponent, u".uno:NewSheetView"_ustr, {}); + + // Change content in View1 with default view -> default view ro sheet view sync + SfxLokHelper::setView(aView1.getViewID()); + typeCharsInCell(std::string("XYZ"), aA1.Col(), aA1.Row(), pTabView1, pModelObj); + typeCharsInCell(std::string("=UPPER(\"x\"&\"Y\"&\"z\""), aA2.Col(), aA2.Row(), pTabView1, + pModelObj); + + // Check the content is synced + CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pTabView1->GetCurrentString(aA1.Col(), aA1.Row())); + CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pTabView2->GetCurrentString(aA1.Col(), aA1.Row())); + + CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pTabView1->GetCurrentString(aA2.Col(), aA2.Row())); + CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pTabView2->GetCurrentString(aA2.Col(), aA2.Row())); + + // Check the content directly in sheets + CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pDocument->GetString(aA1)); + CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pDocument->GetString(aA1SheetView)); + + CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pDocument->GetString(aA2)); + CPPUNIT_ASSERT_EQUAL(u"XYZ"_ustr, pDocument->GetString(aA2SheetView)); + + // Change content in the View2 with the sheet view -> sheet view to default view sync + SfxLokHelper::setView(aView2.getViewID()); + Scheduler::ProcessEventsToIdle(); + + typeCharsInCell(std::string("ABC123"), aA1.Col(), aA1.Row(), pTabView2, pModelObj); + typeCharsInCell(std::string("=UPPER(\"aBc\"&\"123\""), aA2.Col(), aA2.Row(), pTabView2, + pModelObj); + + // Check the content is synced + CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pTabView1->GetCurrentString(aA1.Col(), aA1.Row())); + CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pTabView2->GetCurrentString(aA1.Col(), aA1.Row())); + + CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pTabView1->GetCurrentString(aA2.Col(), aA2.Row())); + CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pTabView2->GetCurrentString(aA2.Col(), aA2.Row())); + + // Check the content directly in sheets + CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pDocument->GetString(aA1)); + CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pDocument->GetString(aA1SheetView)); + + CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pDocument->GetString(aA2)); + CPPUNIT_ASSERT_EQUAL(u"ABC123"_ustr, pDocument->GetString(aA2SheetView)); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/qa/unit/tiledrendering/data/autofilter.ods b/sc/qa/unit/tiledrendering/data/autofilter.ods Binary files differnew file mode 100644 index 000000000000..a4ad405fdcc2 --- /dev/null +++ b/sc/qa/unit/tiledrendering/data/autofilter.ods diff --git a/sc/qa/unit/tiledrendering/data/pivotTableFilter.ods b/sc/qa/unit/tiledrendering/data/pivotTableFilter.ods Binary files differnew file mode 100644 index 000000000000..401b957338e2 --- /dev/null +++ b/sc/qa/unit/tiledrendering/data/pivotTableFilter.ods diff --git a/sc/qa/unit/tiledrendering/data/shape-textbox.ods b/sc/qa/unit/tiledrendering/data/shape-textbox.ods Binary files differnew file mode 100644 index 000000000000..0904f44f8455 --- /dev/null +++ b/sc/qa/unit/tiledrendering/data/shape-textbox.ods diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx b/sc/qa/unit/tiledrendering/tiledrendering.cxx index 90a68ae7d49a..df3035b55b77 100644 --- a/sc/qa/unit/tiledrendering/tiledrendering.cxx +++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx @@ -360,6 +360,34 @@ CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testMoveShapeHandle) } } +CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testMoveShapeHandleTextBox) +{ + ScModelObj* pModelObj = createDoc("shape-textbox.ods"); + ScTestViewCallback aView1; + pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONDOWN, /*x=*/ 1,/*y=*/ 1,/*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0); + pModelObj->postMouseEvent(LOK_MOUSEEVENT_MOUSEBUTTONUP, /*x=*/ 1, /*y=*/ 1, /*count=*/ 1, /*buttons=*/ 1, /*modifier=*/0); + Scheduler::ProcessEventsToIdle(); + + CPPUNIT_ASSERT(!aView1.m_ShapeSelection.isEmpty()); + { + sal_uInt32 id, x, y; + lcl_extractHandleParameters(aView1.m_ShapeSelection, id, x ,y); + sal_uInt32 oldX = x; + sal_uInt32 oldY = y; + uno::Sequence<beans::PropertyValue> aPropertyValues(comphelper::InitPropertySequence( + { + {"HandleNum", uno::Any(id)}, + {"NewPosX", uno::Any(x+1)}, + {"NewPosY", uno::Any(y+1)} + })); + dispatchCommand(mxComponent, u".uno:MoveShapeHandle"_ustr, aPropertyValues); + CPPUNIT_ASSERT(!aView1.m_ShapeSelection.isEmpty()); + lcl_extractHandleParameters(aView1.m_ShapeSelection, id, x ,y); + CPPUNIT_ASSERT_EQUAL(x-1, oldX); + CPPUNIT_ASSERT_EQUAL(y-1, oldY); + } +} + CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testColRowResize) { ScModelObj* pModelObj = createDoc("sort-range.ods"); @@ -3587,6 +3615,46 @@ CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testCursorVisibilityAfterPaste) CPPUNIT_ASSERT_EQUAL(true, aView.m_textCursorVisible); } +CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testAutoFilterPosition) +{ + ScModelObj* pModelObj = createDoc("autofilter.ods"); + ScTestViewCallback aView; + pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>()); + ScTabViewShell* pView = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current()); + + pView->SetCursor(0, 0); // Go to A1. + Scheduler::ProcessEventsToIdle(); + + // Use autofilter button shortcut (ALT + DOWNARROW) to avoid coordinate based click. + pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN | KEY_MOD2); + pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN | KEY_MOD2); + Scheduler::ProcessEventsToIdle(); + + // We should have the autofilter position callback. + auto it = aView.m_aStateChanges.find("AutoFilterInfo"); + CPPUNIT_ASSERT(it != aView.m_aStateChanges.end()); +} + +CPPUNIT_TEST_FIXTURE(ScTiledRenderingTest, testPivotFilterPosition) +{ + ScModelObj* pModelObj = createDoc("pivotTableFilter.ods"); + ScTestViewCallback aView; + pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>()); + ScTabViewShell* pView = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current()); + + pView->SetCursor(1, 0); // Go to B1. + Scheduler::ProcessEventsToIdle(); + + // Use filter button shortcut (ALT + DOWNARROW) to avoid coordinate based click. + pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::DOWN | KEY_MOD2); + pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::DOWN | KEY_MOD2); + Scheduler::ProcessEventsToIdle(); + + // We should have the autofilter position callback. + auto it = aView.m_aStateChanges.find("PivotTableFilterInfo"); + CPPUNIT_ASSERT(it != aView.m_aStateChanges.end()); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/qa/unit/ucalc_sort.cxx b/sc/qa/unit/ucalc_sort.cxx index 0c24b4b8063b..86e44d9f9699 100644 --- a/sc/qa/unit/ucalc_sort.cxx +++ b/sc/qa/unit/ucalc_sort.cxx @@ -30,6 +30,7 @@ #include <svx/svdocirc.hxx> #include <svx/svdpage.hxx> #include <rtl/math.hxx> +#include <unotools/syslocaleoptions.hxx> class TestSort : public ScUcalcTestBase { @@ -2222,6 +2223,66 @@ CPPUNIT_TEST_FIXTURE(TestSort, testQueryBinarySearch) m_pDoc->DeleteTab(0); } +CPPUNIT_TEST_FIXTURE(TestSort, testLanguageDependentNaturalSort) +{ + // Set the system locale to "en-US" for to have different decimal separator than "de-EN". + SvtSysLocaleOptions aOptions; + OUString sLocaleConfigString = aOptions.GetLanguageTag().getBcp47(); + aOptions.SetLocaleConfigString(u"en-US"_ustr); + aOptions.Commit(); + comphelper::ScopeGuard g([&aOptions, &sLocaleConfigString] { + aOptions.SetLocaleConfigString(sLocaleConfigString); + aOptions.Commit(); + }); + + // Generate test data + m_pDoc->InsertTab(0, u"NaturalSortTest"_ustr); + m_pDoc->SetString(ScAddress(0,0,0),u"Item"_ustr); // ScAddress(col, row, tab) + m_pDoc->SetString(ScAddress(0,1,0),u"K2,5"_ustr); + m_pDoc->SetString(ScAddress(0,2,0),u"K2,501"_ustr); + m_pDoc->SetString(ScAddress(0,3,0),u"K10"_ustr); + m_pDoc->SetString(ScAddress(0,4,0),u"K1,104"_ustr); + m_pDoc->SetString(ScAddress(0,5,0),u"K1,2"_ustr); + m_pDoc->SetString(ScAddress(0,6,0),u"K2,40"_ustr); + m_pDoc->SetAnonymousDBData( + 0, std::unique_ptr<ScDBData>(new ScDBData(STR_DB_LOCAL_NONAME, 0, 0, 0, 0, 6))); + + // Create sort parameters + ScSortParam aSortParam; + aSortParam.nCol1 = 0; + aSortParam.nCol2 = 0; + aSortParam.nRow1 = 0; + aSortParam.nRow2 = 6; + aSortParam.bHasHeader = true; + aSortParam.bNaturalSort = true; // needs to be adapted when mode 'integer' is implemented + aSortParam.bInplace = false; + aSortParam.nDestTab = 0; + aSortParam.nDestCol = 2; + aSortParam.nDestRow = 0; + aSortParam.aCollatorLocale = css::lang::Locale(u"de"_ustr, u"DE"_ustr, u""_ustr); + aSortParam.maKeyState[0].bDoSort = true; + aSortParam.maKeyState[0].nField = 0; + aSortParam.maKeyState[0].bAscending = true; + aSortParam.maKeyState[0].aColorSortMode = ScColorSortMode::None; + + // Actually sort + ScDBDocFunc aFunc(*m_xDocShell); + bool bSorted = aFunc.Sort(0, aSortParam, true, true, true); + CPPUNIT_ASSERT(bSorted); + + // Verify sort result. Without fix the comma was treated as ordinary character and thus the order + // had been Item | K1,2 | K1,104 | K2,5 | K2,40 | K2,501 | K10 + const std::array<OUString, 7> aExpected + = { u"Item"_ustr, u"K1,104"_ustr, u"K1,2"_ustr, u"K2,40"_ustr, + u"K2,5"_ustr, u"K2,501"_ustr, u"K10"_ustr }; + for (SCROW nRow = 0; nRow <= 6; nRow++) + { + CPPUNIT_ASSERT_EQUAL(aExpected[nRow], m_pDoc->GetString(ScAddress(2, nRow, 0))); + } + + m_pDoc->DeleteTab(0); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/core/data/SheetView.cxx b/sc/source/core/data/SheetView.cxx index 05ae7a72cc70..d96719eb13ad 100644 --- a/sc/source/core/data/SheetView.cxx +++ b/sc/source/core/data/SheetView.cxx @@ -8,6 +8,7 @@ */ #include <SheetView.hxx> +#include <table.hxx> namespace sc { @@ -18,6 +19,11 @@ SheetView::SheetView(ScTable* pTable) ScTable* SheetView::getTablePointer() const { return mpTable; } bool SheetView::isValid() const { return mpTable; } +SCTAB SheetView::getTableNumber() const +{ + assert(mpTable); + return mpTable->GetTab(); +} } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/core/data/document10.cxx b/sc/source/core/data/document10.cxx index 761c44612042..fc38a04ae378 100644 --- a/sc/source/core/data/document10.cxx +++ b/sc/source/core/data/document10.cxx @@ -1148,6 +1148,16 @@ sc::SheetViewID ScDocument::CreateNewSheetView(SCTAB nMainTable, SCTAB nSheetVie return -1; } +std::shared_ptr<sc::SheetViewManager> ScDocument::GetSheetViewManager(SCTAB nTable) +{ + if (ScTable* pTable = FetchTable(nTable)) + { + if (!pTable->IsSheetView()) + return pTable->GetSheetViewManager(); + } + return {}; +} + SCTAB ScDocument::GetSheetViewNumber(SCTAB nTab, sc::SheetViewID nID) { if (ScTable* pMainSheet = FetchTable(nTab)) diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx index 055617dfec14..a8eb314f935b 100644 --- a/sc/source/core/data/table3.cxx +++ b/sc/source/core/data/table3.cxx @@ -23,11 +23,13 @@ #include <svl/zforlist.hxx> #include <svl/zformat.hxx> #include <unotools/collatorwrapper.hxx> +#include <unotools/charclass.hxx> #include <stdlib.h> #include <com/sun/star/i18n/KParseTokens.hpp> #include <com/sun/star/i18n/KParseType.hpp> #include <sal/log.hxx> #include <osl/diagnose.h> +#include <i18nlangtag/languagetag.hxx> #include <refdata.hxx> #include <table.hxx> @@ -83,6 +85,9 @@ using namespace ::com::sun::star::i18n; @param sWhole Original string to be split into pieces + @param rLanguageTag + Contains the language related infos from the sort parameters + @param sPrefix Prefix string that consists of the part before the first number token. If no number was found, sPrefix is unchanged. @@ -98,14 +103,15 @@ using namespace ::com::sun::star::i18n; @return Returns TRUE if a numeral element is found in a given string, or FALSE if no numeral element is found. */ -static bool SplitString( const OUString &sWhole, - OUString &sPrefix, OUString &sSuffix, double &fNum ) +static bool SplitString(const OUString &sWhole, const LanguageTag& rLanguageTag, OUString &sPrefix, + OUString &sSuffix, double &fNum) { // Get prefix element, search for any digit and stop. sal_Int32 nPos = 0; + const CharClass aCharClass(rLanguageTag); while (nPos < sWhole.getLength()) { - const sal_uInt16 nType = ScGlobal::getCharClass().getCharacterType( sWhole, nPos); + const sal_uInt16 nType = aCharClass.getCharacterType( sWhole, nPos); if (nType & KCharacterType::DIGIT) break; sWhole.iterateCodePoints( &nPos ); @@ -116,10 +122,10 @@ static bool SplitString( const OUString &sWhole, return false; // Get numeral element - const OUString& sUser = ScGlobal::getLocaleData().getNumDecimalSep(); - ParseResult aPRNum = ScGlobal::getCharClass().parsePredefinedToken( - KParseType::ANY_NUMBER, sWhole, nPos, - KParseTokens::ANY_NUMBER, u""_ustr, KParseTokens::ANY_NUMBER, sUser ); + const OUString& sUser = LocaleDataWrapper::get(rLanguageTag)->getNumDecimalSep(); + ParseResult aPRNum = aCharClass.parsePredefinedToken(KParseType::ANY_NUMBER, sWhole, nPos, + KParseTokens::ANY_NUMBER, u""_ustr, + KParseTokens::ANY_NUMBER, sUser); if ( aPRNum.EndPos == nPos ) { @@ -146,6 +152,10 @@ static bool SplitString( const OUString &sWhole, @param sInput2 Input string 2 + @param rLanguageTag + Contains the language related infos from the sort parameters. They are needed + in method SplitString. + @param bCaseSens Boolean value for case sensitivity @@ -158,16 +168,17 @@ static bool SplitString( const OUString &sWhole, @return Returns 1 if sInput1 is greater, 0 if sInput1 == sInput2, and -1 if sInput2 is greater. */ -static short Compare( const OUString &sInput1, const OUString &sInput2, - const bool bCaseSens, const ScUserListData* pData, const CollatorWrapper *pCW ) +static short Compare(const OUString &sInput1, const OUString &sInput2, + const LanguageTag& rLanguageTag, const bool bCaseSens, + const ScUserListData* pData, const CollatorWrapper *pCW) { OUString sStr1( sInput1 ), sStr2( sInput2 ), sPre1, sSuf1, sPre2, sSuf2; do { double nNum1, nNum2; - bool bNumFound1 = SplitString( sStr1, sPre1, sSuf1, nNum1 ); - bool bNumFound2 = SplitString( sStr2, sPre2, sSuf2, nNum2 ); + bool bNumFound1 = SplitString( sStr1, rLanguageTag, sPre1, sSuf1, nNum1 ); + bool bNumFound2 = SplitString( sStr2, rLanguageTag, sPre2, sSuf2, nNum2 ); short nPreRes; // Prefix comparison result if ( pData ) @@ -1493,6 +1504,7 @@ short ScTable::CompareCell( bool bUserDef = aSortParam.bUserDef; // custom sort order bool bNaturalSort = aSortParam.bNaturalSort; // natural sort bool bCaseSens = aSortParam.bCaseSens; // case sensitivity + LanguageTag aSortLanguageTag(aSortParam.aCollatorLocale); ScUserList& rList = ScGlobal::GetUserList(); if (bUserDef && rList.size() > aSortParam.nUserIndex) @@ -1500,7 +1512,8 @@ short ScTable::CompareCell( const ScUserListData& rData = rList[aSortParam.nUserIndex]; if ( bNaturalSort ) - nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, &rData, pSortCollator ); + nRes = naturalsort::Compare(aStr1, aStr2, aSortLanguageTag, bCaseSens, + &rData, pSortCollator); else { if ( bCaseSens ) @@ -1513,7 +1526,8 @@ short ScTable::CompareCell( if (!bUserDef) { if ( bNaturalSort ) - nRes = naturalsort::Compare( aStr1, aStr2, bCaseSens, nullptr, pSortCollator ); + nRes = naturalsort::Compare(aStr1, aStr2, aSortLanguageTag, bCaseSens, + nullptr, pSortCollator ); else nRes = static_cast<short>( pSortCollator->compareString( aStr1, aStr2 ) ); } diff --git a/sc/source/core/tool/interpr2.cxx b/sc/source/core/tool/interpr2.cxx index 1744d3473951..8edd187df9e8 100644 --- a/sc/source/core/tool/interpr2.cxx +++ b/sc/source/core/tool/interpr2.cxx @@ -136,8 +136,13 @@ void ScInterpreter::ScGetMonth() void ScInterpreter::ScGetDay() { Date aDate = mrContext.NFGetNullDate(); - aDate.AddDays( GetFloor32()); - PushDouble(static_cast<double>(aDate.GetDay())); + if (aDate.CheckedAddDays(GetFloor32())) + PushDouble(static_cast<double>(aDate.GetDay())); + else + { + SetError(FormulaError::IllegalArgument); + PushDouble(HUGE_VAL); + } } void ScInterpreter::ScGetMin() diff --git a/sc/source/ui/app/inputwin.cxx b/sc/source/ui/app/inputwin.cxx index f4fdbe4fdfe8..868135c74809 100644 --- a/sc/source/ui/app/inputwin.cxx +++ b/sc/source/ui/app/inputwin.cxx @@ -1823,8 +1823,6 @@ bool ScTextWnd::Command( const CommandEvent& rCEvt ) return true; ScModule* mod = ScModule::get(); - // if we focus input after "Accept Formula" command, we need to notify to get it working - mod->InputChanged(m_xEditView.get()); // information about paragraph is in additional data // information about position in a paragraph in a Mouse Pos diff --git a/sc/source/ui/drawfunc/fuins1.cxx b/sc/source/ui/drawfunc/fuins1.cxx index 38e6684c0dec..d9a1d83568c3 100644 --- a/sc/source/ui/drawfunc/fuins1.cxx +++ b/sc/source/ui/drawfunc/fuins1.cxx @@ -37,8 +37,6 @@ #include <avmedia/mediawindow.hxx> #include <vcl/svapp.hxx> #include <vcl/weld.hxx> -#include <vcl/GraphicNativeTransform.hxx> -#include <vcl/GraphicNativeMetadata.hxx> #include <fuinsert.hxx> #include <tabvwsh.hxx> #include <drwlayer.hxx> @@ -114,16 +112,6 @@ static void lcl_InsertGraphic( const Graphic& rGraphic, ScAnchorType aAnchorType = SCA_CELL ) { Graphic& rGraphic1 = const_cast<Graphic &>(rGraphic); - GraphicNativeMetadata aMetadata; - if ( aMetadata.read(rGraphic1) ) - { - const Degree10 aRotation = aMetadata.getRotation(); - if (aRotation) - { - GraphicNativeTransform aTransform( rGraphic1 ); - aTransform.rotate( aRotation ); - } - } ScDrawView* pDrawView = rViewSh.GetScDrawView(); // #i123922# check if an existing object is selected; if yes, evtl. replace diff --git a/sc/source/ui/inc/colrowba.hxx b/sc/source/ui/inc/colrowba.hxx index 536a78dd1d71..57c6204e88dc 100644 --- a/sc/source/ui/inc/colrowba.hxx +++ b/sc/source/ui/inc/colrowba.hxx @@ -71,7 +71,6 @@ public: virtual sal_uInt16 GetEntrySize( SCCOLROW nEntryNo ) const override; virtual OUString GetEntryText( SCCOLROW nEntryNo ) const override; - virtual bool IsMirrored() const override; virtual SCCOLROW GetHiddenCount( SCCOLROW nEntryNo ) const override; virtual void SetEntrySize( SCCOLROW nPos, sal_uInt16 nNewSize ) override; diff --git a/sc/source/ui/inc/gridwin.hxx b/sc/source/ui/inc/gridwin.hxx index f12865d3f773..228d5088f527 100644 --- a/sc/source/ui/inc/gridwin.hxx +++ b/sc/source/ui/inc/gridwin.hxx @@ -234,6 +234,7 @@ class SAL_DLLPUBLIC_RTTI ScGridWindow : public vcl::DocWindow, public DropTarget bool DoPageFieldSelection( SCCOL nCol, SCROW nRow ); bool DoAutoFilterButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt ); + void SendAutofilterPopupPosition(SCCOL nCol, SCROW nRow); void DoPushPivotButton( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt, bool bButton, bool bPopup, bool bMultiField ); void DoPushPivotToggle( SCCOL nCol, SCROW nRow, const MouseEvent& rMEvt ); diff --git a/sc/source/ui/inc/hdrcont.hxx b/sc/source/ui/inc/hdrcont.hxx index 149db145e2be..86682f78f2e2 100644 --- a/sc/source/ui/inc/hdrcont.hxx +++ b/sc/source/ui/inc/hdrcont.hxx @@ -97,7 +97,6 @@ protected: virtual SCCOLROW GetHiddenCount( SCCOLROW nEntryNo ) const; virtual bool IsLayoutRTL() const; - virtual bool IsMirrored() const; virtual void SetEntrySize( SCCOLROW nPos, sal_uInt16 nNewWidth ) = 0; virtual void HideEntries( SCCOLROW nStart, SCCOLROW nEnd ) = 0; diff --git a/sc/source/ui/inc/viewfunc.hxx b/sc/source/ui/inc/viewfunc.hxx index f05e1981957a..55fe418435ed 100644 --- a/sc/source/ui/inc/viewfunc.hxx +++ b/sc/source/ui/inc/viewfunc.hxx @@ -78,7 +78,7 @@ public: ScViewFunc( vcl::Window* pParent, ScDocShell& rDocSh, ScTabViewShell* pViewShell ); ~ScViewFunc(); - SC_DLLPUBLIC const ScPatternAttr* GetSelectionPattern (); + SC_DLLPUBLIC const ScPatternAttr* GetSelectionPattern(); SC_DLLPUBLIC OUString GetCurrentString(SCCOL nCol, SCROW nRow); void GetSelectionFrame( diff --git a/sc/source/ui/view/colrowba.cxx b/sc/source/ui/view/colrowba.cxx index 9da02f3f4756..b6653cdb86e5 100644 --- a/sc/source/ui/view/colrowba.cxx +++ b/sc/source/ui/view/colrowba.cxx @@ -372,10 +372,4 @@ SCCOLROW ScRowBar::GetHiddenCount( SCCOLROW nEntryNo ) const // override only fo return rDoc.GetHiddenRowCount( nEntryNo, nTab ); } -bool ScRowBar::IsMirrored() const // override only for rows -{ - const ScViewData& rViewData = pTabView->GetViewData(); - return rViewData.GetDocument().IsLayoutRTL( rViewData.CurrentTabForData() ); -} - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/source/ui/view/gridwin.cxx b/sc/source/ui/view/gridwin.cxx index 85cd9b719062..b99dfbbe1cad 100644 --- a/sc/source/ui/view/gridwin.cxx +++ b/sc/source/ui/view/gridwin.cxx @@ -146,6 +146,7 @@ #include <sfx2/lokhelper.hxx> #include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <tools/json_writer.hxx> #include <vector> #include <boost/property_tree/json_parser.hpp> @@ -938,6 +939,22 @@ void collectUIInformation(const OUString& aRow, const OUString& aCol , const OUS } +void ScGridWindow::SendAutofilterPopupPosition(SCCOL nCol, SCROW nRow) { + ScTabViewShell* pViewShell = mrViewData.GetViewShell(); + if (pViewShell) + { + tools::JsonWriter writer; + writer.put("commandName", "AutoFilterInfo"); + { + const auto aState = writer.startNode("state"); + writer.put("column", nCol); + writer.put("row", nRow); + } + OString info = writer.finishAndGetAsOString(); + pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, info); + } +} + void ScGridWindow::LaunchAutoFilterMenu(SCCOL nCol, SCROW nRow) { SCTAB nTab = mrViewData.CurrentTabForData(); @@ -1009,6 +1026,7 @@ void ScGridWindow::LaunchAutoFilterMenu(SCCOL nCol, SCROW nRow) aPos.setY(aPos.getY() / fZoomY); nSizeX = nSizeX / fZoomX; nSizeY = nSizeY / fZoomY; + SendAutofilterPopupPosition(nCol, nRow); // Send the position of the autofilter popup. } tools::Rectangle aCellRect(bLOKActive ? aPos : OutputToScreenPixel(aPos), Size(nSizeX, nSizeY)); @@ -1514,6 +1532,7 @@ void ScGridWindow::LaunchDataSelectMenu(const SCCOL nCol, const SCROW nRow) // client (effective double scaling) causing wrong positioning/size. double fZoomX(mrViewData.GetZoomX()); double fZoomY(mrViewData.GetZoomY()); + assert(fZoomX != 0.0 && fZoomY != 0.0 && "cannot be zero"); aPos.setX(aPos.getX() / fZoomX); aPos.setY(aPos.getY() / fZoomY); nSizeX = nSizeX / fZoomX; diff --git a/sc/source/ui/view/gridwin2.cxx b/sc/source/ui/view/gridwin2.cxx index 519f55f57c28..5c07253e1de8 100644 --- a/sc/source/ui/view/gridwin2.cxx +++ b/sc/source/ui/view/gridwin2.cxx @@ -48,6 +48,9 @@ #include <memory> #include <vector> +#include <tools/json_writer.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> + using namespace css; using namespace css::sheet; @@ -549,6 +552,24 @@ void ScGridWindow::DPLaunchFieldPopupMenu(const Point& rScreenPosition, const Si DataPilotFieldOrientation nOrient; tools::Long nDimIndex = pDPObject->GetHeaderDim(rAddress, nOrient); + if (comphelper::LibreOfficeKit::isActive()) + { + // We send the cell position of the filter button to Online side. So the position of the popup can be adjusted near to the cell. + ScTabViewShell* pViewShell = mrViewData.GetViewShell(); + if (pViewShell) + { + tools::JsonWriter writer; + writer.put("commandName", "PivotTableFilterInfo"); + { + const auto aState = writer.startNode("state"); + writer.put("column", rAddress.Col()); + writer.put("row", rAddress.Row()); + } + OString info = writer.finishAndGetAsOString(); + pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, info); + } + } + DPLaunchFieldPopupMenu(rScreenPosition, rScreenSize, nDimIndex, pDPObject); } diff --git a/sc/source/ui/view/hdrcont.cxx b/sc/source/ui/view/hdrcont.cxx index 31585c2a8ccd..3aecfb9cc814 100644 --- a/sc/source/ui/view/hdrcont.cxx +++ b/sc/source/ui/view/hdrcont.cxx @@ -1073,11 +1073,6 @@ bool ScHeaderControl::IsLayoutRTL() const return false; } -bool ScHeaderControl::IsMirrored() const -{ - return false; -} - bool ScHeaderControl::IsDisabled() const { return false; diff --git a/sc/source/ui/view/viewfun3.cxx b/sc/source/ui/view/viewfun3.cxx index 312b9d537aa7..d0002823ef7f 100644 --- a/sc/source/ui/view/viewfun3.cxx +++ b/sc/source/ui/view/viewfun3.cxx @@ -2067,10 +2067,15 @@ void ScViewFunc::MakeNewSheetView() SCTAB nSheetViewTab = nTab + 1; if (rDoc.CopyTab(nTab, nSheetViewTab)) { - SetTabNo(nSheetViewTab); + // Add and register the created sheet view rDoc.SetSheetView(nSheetViewTab, true); sc::SheetViewID nSheetViewID = rDoc.CreateNewSheetView(nTab, nSheetViewTab); GetViewData().SetSheetViewID(nSheetViewID); + + // Update + GetViewData().SetTabNo(nSheetViewTab); // force add the sheet view tab + GetViewData().SetTabNo(nTab); // then change back to the current tab + PaintExtras(); // update Tab Control } } diff --git a/sc/source/ui/view/viewfunc.cxx b/sc/source/ui/view/viewfunc.cxx index b7df1d6092ef..6c8c43fc1cfd 100644 --- a/sc/source/ui/view/viewfunc.cxx +++ b/sc/source/ui/view/viewfunc.cxx @@ -77,6 +77,7 @@ #include <columnspanset.hxx> #include <stringutil.hxx> #include <SparklineList.hxx> +#include <SheetView.hxx> #include <memory> @@ -664,36 +665,12 @@ void ScViewFunc::EnterDataToCurrentCell(const OUString& rString, const EditTextO EnterData(nCol, nRow, nTab, rString, pData, bMatrixExpand); } -// input - undo OK -void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab, - const OUString& rString, - const EditTextObject* pData, - bool bMatrixExpand ) +namespace +{ +bool checkFormula(ScDocument& rDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, const OUString& rString) { - ScDocument& rDoc = GetViewData().GetDocument(); - ScMarkData aMark(GetViewData().GetMarkData()); - bool bRecord = rDoc.IsUndoEnabled(); - SCTAB i; - - ScDocShell& rDocSh = GetViewData().GetDocShell(); - ScDocFunc &rFunc = GetViewData().GetDocFunc(); - std::shared_ptr<ScDocShellModificator> xModificator = std::make_shared<ScDocShellModificator>(rDocSh); - - ScEditableTester aTester( rDoc, nCol,nRow, nCol,nRow, aMark ); - if (!aTester.IsEditable()) - { - ErrorMessage(aTester.GetMessageId()); - PaintArea(nCol, nRow, nCol, nRow); // possibly the edit-engine is still painted there - return; - } - - if ( bRecord ) - rFunc.EnterListAction( STR_UNDO_ENTERDATA ); - - bool bFormula = false; - // do not check formula if it is a text cell - sal_uInt32 format = rDoc.GetNumberFormat( nCol, nRow, nTab ); + sal_uInt32 format = rDoc.GetNumberFormat(nCol, nRow, nTab ); SvNumberFormatter* pFormatter = rDoc.GetFormatTable(); // a single '=' character is handled as string (needed for special filters) if ( pFormatter->GetType(format) != SvNumFormatType::TEXT && rString.getLength() > 1 ) @@ -701,7 +678,7 @@ void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab, if ( rString[0] == '=' ) { // handle as formula - bFormula = true; + return true; } else if ( rString[0] == '+' || rString[0] == '-' ) { @@ -721,65 +698,150 @@ void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab, double fNumber = 0; if ( !pFormatter->IsNumberFormat( aString, format, fNumber ) ) { - bFormula = true; + return true; } } } } + return false; +} - bool bNumFmtChanged = false; - if ( bFormula ) - { // formula, compile with autoCorrection - i = aMark.GetFirstSelected(); - auto xPosPtr = std::make_shared<ScAddress>(nCol, nRow, i); - auto xCompPtr = std::make_shared<ScCompiler>(rDoc, *xPosPtr, rDoc.GetGrammar(), true, false); - std::unique_ptr<EditTextObject> xTextObject(pData ? pData->Clone() : nullptr); +void applyFormulaToCell(ScViewFunc& rViewFunc, SCCOL nCol, SCROW nRow, SCTAB nTab, OUString const& rString, + const EditTextObject* pData, std::shared_ptr<ScDocShellModificator> xModificator, + ScMarkData const& rMark, bool bMatrixExpand, bool bRecord, bool& rbNumFmtChanged) +{ + ScDocument& rDoc = rViewFunc.GetViewData().GetDocument(); - //2do: enable/disable autoCorrection via calcoptions - xCompPtr->SetAutoCorrection( true ); - if ( rString[0] == '+' || rString[0] == '-' ) - { - xCompPtr->SetExtendedErrorDetection( ScCompiler::EXTENDED_ERROR_DETECTION_NAME_BREAK ); - } + // formula, compile with autoCorrection + auto xPosPtr = std::make_shared<ScAddress>(nCol, nRow, nTab); + auto xCompPtr = std::make_shared<ScCompiler>(rDoc, *xPosPtr, rDoc.GetGrammar(), true, false); + std::unique_ptr<EditTextObject> xTextObject(pData ? pData->Clone() : nullptr); + + //2do: enable/disable autoCorrection via calcoptions + xCompPtr->SetAutoCorrection( true ); + if (rString[0] == '+' || rString[0] == '-') + { + xCompPtr->SetExtendedErrorDetection( ScCompiler::EXTENDED_ERROR_DETECTION_NAME_BREAK ); + } - OUString aFormula( rString ); + OUString aFormula(rString); - FormulaProcessingContext context_instance{ - std::move(xPosPtr), std::move(xCompPtr), std::move(xModificator), nullptr, - nullptr, std::move(xTextObject), std::move(aMark), *this, - OUString(), aFormula, rString, nCol, - nRow, nTab, bMatrixExpand, bNumFmtChanged, - bRecord - }; + FormulaProcessingContext context_instance{ + std::move(xPosPtr), std::move(xCompPtr), xModificator, nullptr, + nullptr, std::move(xTextObject), rMark, rViewFunc, + OUString(), aFormula, rString, nCol, + nRow, nTab, bMatrixExpand, rbNumFmtChanged, + bRecord + }; - parseAndCorrectFormula(std::make_shared<FormulaProcessingContext>(context_instance)); + parseAndCorrectFormula(std::make_shared<FormulaProcessingContext>(context_instance)); +} + +void applyText(ScViewFunc& rViewFunc, SCCOL nCol, SCROW nRow, SCTAB nTab, OUString const& rString, bool& rbNumFmtChanged) +{ + ScViewData& rViewData = rViewFunc.GetViewData(); + ScDocument& rDoc = rViewData.GetDocument(); + ScDocShell& rDocSh = rViewData.GetDocShell(); + ScFieldEditEngine& rEngine = rDoc.GetEditEngine(); + ScDocFunc& rFunc = rViewData.GetDocFunc(); + + bool bNumFmtSet = false; + const ScAddress aAddress(nCol, nRow, nTab); + + // tdf#104902 - handle embedded newline + if (ScStringUtil::isMultiline(rString)) + { + rEngine.SetTextCurrentDefaults(rString); + rDoc.SetEditText(aAddress, rEngine.CreateTextObject()); + rDocSh.AdjustRowHeight(nRow, nRow, nTab); } else { - ScFieldEditEngine& rEngine = rDoc.GetEditEngine(); - for (const auto& rTab : aMark) + rFunc.SetNormalString(bNumFmtSet, aAddress, rString, false); + } + + if (bNumFmtSet) + { + /* FIXME: if set on any sheet results in changed only on + * sheet nTab for TestFormatArea() and DoAutoAttributes() */ + rbNumFmtChanged = true; + } +} + +} // end anonymous namespace + +// input - undo OK +void ScViewFunc::EnterData( SCCOL nCol, SCROW nRow, SCTAB nTab, + const OUString& rString, + const EditTextObject* pData, + bool bMatrixExpand ) +{ + ScDocument& rDoc = GetViewData().GetDocument(); + ScMarkData aMark(GetViewData().GetMarkData()); + bool bRecord = rDoc.IsUndoEnabled(); + + ScDocFunc &rFunc = GetViewData().GetDocFunc(); + std::shared_ptr<ScDocShellModificator> xModificator = std::make_shared<ScDocShellModificator>(GetViewData().GetDocShell()); + + ScEditableTester aTester( rDoc, nCol,nRow, nCol,nRow, aMark ); + if (!aTester.IsEditable()) + { + ErrorMessage(aTester.GetMessageId()); + PaintArea(nCol, nRow, nCol, nRow); // possibly the edit-engine is still painted there + return; + } + + if ( bRecord ) + rFunc.EnterListAction( STR_UNDO_ENTERDATA ); + + bool bFormula = checkFormula(rDoc, nCol, nRow, nTab, rString); + bool bNumFmtChanged = false; + + if (bFormula) + { + SCTAB nSelectedTab = aMark.GetFirstSelected(); + + applyFormulaToCell(*this, nCol, nRow, nTab, rString, pData, xModificator, aMark, bMatrixExpand, bRecord, bNumFmtChanged); + + if (!rDoc.IsSheetView(nSelectedTab)) { - bool bNumFmtSet = false; - const ScAddress aScAddress(nCol, nRow, rTab); + auto pManager = rDoc.GetSheetViewManager(nSelectedTab); - // tdf#104902 - handle embedded newline - if (ScStringUtil::isMultiline(rString)) + for (auto const& rSheetView : pManager->getSheetViews()) { - rEngine.SetTextCurrentDefaults(rString); - rDoc.SetEditText(aScAddress, rEngine.CreateTextObject()); - rDocSh.AdjustRowHeight(nRow, nRow, rTab); + if (!rSheetView.isValid()) + continue; + + SCTAB nSheetViewTab = rSheetView.getTableNumber(); + + ScMarkData aSheetViewMark(rDoc.GetSheetLimits()); + aSheetViewMark.SelectTable(nSheetViewTab, false); + ScRange aSheetViewRange(aMark.GetMarkArea()); + aSheetViewRange.aStart.SetTab(nSheetViewTab); + aSheetViewRange.aEnd.SetTab(nSheetViewTab); + aSheetViewMark.SetMarkArea(aSheetViewRange); + + applyFormulaToCell(*this, nCol, nRow, nSheetViewTab, rString, pData, xModificator, aSheetViewMark, bMatrixExpand, bRecord, bNumFmtChanged); } - else + } + } + else + { + for (const auto& rTab : aMark) + { + if (!rDoc.IsSheetView(rTab)) { - rFunc.SetNormalString(bNumFmtSet, aScAddress, rString, false); - } + auto pManager = rDoc.GetSheetViewManager(rTab); + for (auto const& rSheetView : pManager->getSheetViews()) + { + if (!rSheetView.isValid()) + continue; - if (bNumFmtSet) - { - /* FIXME: if set on any sheet results in changed only on - * sheet nTab for TestFormatArea() and DoAutoAttributes() */ - bNumFmtChanged = true; + SCTAB nSheetViewTab = rSheetView.getTableNumber(); + applyText(*this, nCol, nRow, nSheetViewTab, rString, bNumFmtChanged); + } } + applyText(*this, nCol, nRow, rTab, rString, bNumFmtChanged); } performAutoFormatAndUpdate(rString, aMark, nCol, nRow, nTab, bNumFmtChanged, bRecord, xModificator, *this); } diff --git a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng index 05bc58c52dc7..0721512cbf7b 100644 --- a/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng +++ b/schema/libreoffice/OpenDocument-v1.4+libreoffice-schema.rng @@ -4188,10 +4188,42 @@ xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1. <rng:define name="text-changed-region" combine="choice"> <rng:element name="text:changed-region"> <rng:ref name="text-changed-region-attr"/> - <rng:ref name="text-changed-region-content"/> - <rng:optional> - <rng:ref name="text-changed-region-content"/> - </rng:optional> + <rng:choice> + <rng:ref name="text-changed-region-content-insertion"/> + <rng:group> + <rng:ref name="text-changed-region-content-deletion"/> + <rng:optional> + <rng:ref name="text-changed-region-content-insertion"/> + </rng:optional> + </rng:group> + <rng:group> + <rng:ref name="text-changed-region-content-format-change"/> + <rng:optional> + <rng:choice> + <rng:ref name="text-changed-region-content-insertion"/> + <rng:ref name="text-changed-region-content-deletion"/> + </rng:choice> + </rng:optional> + </rng:group> + </rng:choice> + </rng:element> + </rng:define> + <rng:define name="text-changed-region-content-insertion"> + <rng:element name="text:insertion"> + <rng:ref name="office-change-info"/> + </rng:element> + </rng:define> + <rng:define name="text-changed-region-content-deletion"> + <rng:element name="text:deletion"> + <rng:ref name="office-change-info"/> + <rng:zeroOrMore> + <rng:ref name="text-content"/> + </rng:zeroOrMore> + </rng:element> + </rng:define> + <rng:define name="text-changed-region-content-format-change"> + <rng:element name="text:format-change"> + <rng:ref name="office-change-info"/> </rng:element> </rng:define> diff --git a/sd/qa/uitest/impress_tests2/tdf146019.py b/sd/qa/uitest/impress_tests2/tdf146019.py index 86b8ed48180f..dc568b5151c2 100644 --- a/sd/qa/uitest/impress_tests2/tdf146019.py +++ b/sd/qa/uitest/impress_tests2/tdf146019.py @@ -29,7 +29,7 @@ class tdf146019(UITestCase): # Check the shape is rotated, height > width shape = document.getDrawPages()[0][2] - self.assertEqual(8996, shape.getSize().Width) - self.assertEqual(11745, shape.getSize().Height) + self.assertEqual(8995, shape.getSize().Width) + self.assertEqual(11746, shape.getSize().Height) # vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sd/source/ui/func/fuinsert.cxx b/sd/source/ui/func/fuinsert.cxx index 041a4465172a..1b88fc4ff891 100644 --- a/sd/source/ui/func/fuinsert.cxx +++ b/sd/source/ui/func/fuinsert.cxx @@ -80,9 +80,6 @@ #include <vcl/errinf.hxx> #include <vcl/graphicfilter.hxx> -#include <vcl/GraphicNativeTransform.hxx> -#include <vcl/GraphicNativeMetadata.hxx> - #include <comphelper/lok.hxx> using namespace com::sun::star; @@ -156,16 +153,6 @@ void FuInsertGraphic::DoExecute( SfxRequest& rReq ) if( nError == ERRCODE_NONE ) { - GraphicNativeMetadata aMetadata; - if ( aMetadata.read(aGraphic) ) - { - const Degree10 aRotation = aMetadata.getRotation(); - if (aRotation) - { - GraphicNativeTransform aTransform( aGraphic ); - aTransform.rotate( aRotation ); - } - } if( dynamic_cast< DrawViewShell *>( &mrViewShell ) ) { sal_Int8 nAction = DND_ACTION_COPY; diff --git a/sd/source/ui/view/sdview3.cxx b/sd/source/ui/view/sdview3.cxx index d692c80b2397..6d89abe6b32b 100644 --- a/sd/source/ui/view/sdview3.cxx +++ b/sd/source/ui/view/sdview3.cxx @@ -722,12 +722,13 @@ bool View::InsertData( const TransferableDataHelper& rDataHelper, if (ShouldTry(SotClipboardFormatId::EMBED_SOURCE)) { - sd::slidesorter::SlideSorter& xSlideSorter - = ::sd::slidesorter::SlideSorterViewShell::GetSlideSorter( - mrDoc.GetDocSh()->GetViewShell()->GetViewShellBase()) - ->GetSlideSorter(); - if (xSlideSorter.GetController().GetClipboard().PasteSlidesFromSystemClipboard()) - return true; + if (::sd::slidesorter::SlideSorterViewShell* pSlideSorterViewShell = + ::sd::slidesorter::SlideSorterViewShell::GetSlideSorter( + mrDoc.GetDocSh()->GetViewShell()->GetViewShellBase())) + { + if (pSlideSorterViewShell->GetSlideSorter().GetController().GetClipboard().PasteSlidesFromSystemClipboard()) + return true; + } } if (ShouldTry(SotClipboardFormatId::DRAWING)) diff --git a/sfx2/source/doc/guisaveas.cxx b/sfx2/source/doc/guisaveas.cxx index 1d7241f42756..8f242373bb70 100644 --- a/sfx2/source/doc/guisaveas.cxx +++ b/sfx2/source/doc/guisaveas.cxx @@ -2095,7 +2095,8 @@ bool SfxStoringHelper::WarnUnacceptableFormat( const uno::Reference< frame::XMod } VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create(); - auto pDlg = pFact->CreateQueryDialog(pWin, SfxResId(STR_QUERY_ALIENFORMAT_TTITLE), sInfoText, sQuestion, true); + bool bShowAgain = !officecfg::Office::Common::Save::Document::WarnAlienFormat::isReadOnly(); + auto pDlg = pFact->CreateQueryDialog(pWin, SfxResId(STR_QUERY_ALIENFORMAT_TTITLE), sInfoText, sQuestion, bShowAgain); pDlg->SetYesLabel(SfxResId(STR_QUERY_ALIENFORMAT_YES).replaceAll("%FORMATNAME", aOldUIName)); // "Use %FORMATNAME Format" pDlg->SetNoLabel(SfxResId(STR_QUERY_ALIENFORMAT_NO).replaceAll("%DEFAULTEXTENSION", sExtension)); // "Use %DEFAULTEXTENSION _Format" diff --git a/sfx2/source/view/classificationhelper.cxx b/sfx2/source/view/classificationhelper.cxx index b74f2486d2a9..d35df6733a05 100644 --- a/sfx2/source/view/classificationhelper.cxx +++ b/sfx2/source/view/classificationhelper.cxx @@ -13,6 +13,10 @@ #include <algorithm> #include <iterator> +#include <frozen/bits/defines.h> +#include <frozen/bits/elsa_std.h> +#include <frozen/unordered_map.h> + #include <com/sun/star/beans/XPropertyContainer.hpp> #include <com/sun/star/beans/Property.hpp> #include <com/sun/star/beans/XPropertySet.hpp> @@ -761,12 +765,12 @@ sal_Int32 SfxClassificationHelper::GetImpactLevel() } else if (aScale == "FIPS-199") { - static std::map<OUString, sal_Int32> const aValues + static auto constexpr aValues = frozen::make_unordered_map<std::u16string_view, sal_Int32>( { - { "Low", 0 }, - { "Moderate", 1 }, - { "High", 2 } - }; + { u"Low", 0 }, + { u"Moderate", 1 }, + { u"High", 2 } + }); auto itValues = aValues.find(aLevel); if (itValues == aValues.end()) return nRet; diff --git a/sfx2/source/view/lokhelper.cxx b/sfx2/source/view/lokhelper.cxx index d27e0cbca0d9..495eefd13029 100644 --- a/sfx2/source/view/lokhelper.cxx +++ b/sfx2/source/view/lokhelper.cxx @@ -679,6 +679,16 @@ void SfxLokHelper::notifyWindow(const SfxViewShell* pThisView, pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_WINDOW, s); } +void SfxLokHelper::notifyCursorInvalidation(SfxViewShell const* pThisView, tools::Rectangle const* pRect, bool bControlEvent) +{ + int nViewId = SfxLokHelper::getView(*pThisView); + OString sPayload = OString::Concat("{ \"viewId\": \"") + OString::number(nViewId) + "\", \"rectangle\": \"" + pRect->toString(); + if (bControlEvent) + sPayload += "\", \"controlEvent\": true"; + sPayload += " }"; + pThisView->libreOfficeKitViewCallback(LOK_CALLBACK_INVALIDATE_VISIBLE_CURSOR, sPayload); +} + void SfxLokHelper::notifyInvalidation(SfxViewShell const* pThisView, tools::Rectangle const* pRect) { // -1 means all parts diff --git a/sfx2/source/view/viewsh.cxx b/sfx2/source/view/viewsh.cxx index 98d3ee3a237d..a8e1e6237579 100644 --- a/sfx2/source/view/viewsh.cxx +++ b/sfx2/source/view/viewsh.cxx @@ -3565,6 +3565,11 @@ void SfxViewShell::notifyInvalidation(tools::Rectangle const* pRect) const SfxLokHelper::notifyInvalidation(this, pRect); } +void SfxViewShell::notifyCursorInvalidation(tools::Rectangle const* pRect, bool bControlEvent) const +{ + SfxLokHelper::notifyCursorInvalidation(this, pRect, bControlEvent); +} + void SfxViewShell::NotifyOtherViews(int nType, const OString& rKey, const OString& rPayload) { SfxLokHelper::notifyOtherViews(this, nType, rKey, rPayload); diff --git a/solenv/gbuild/DotnetTest.mk b/solenv/gbuild/DotnetTest.mk index c51487cecf5c..635797cf782f 100644 --- a/solenv/gbuild/DotnetTest.mk +++ b/solenv/gbuild/DotnetTest.mk @@ -72,7 +72,7 @@ define gb_DotnetTest_DotnetTest gb_DotnetTest_$(1)_language := $(2) gb_DotnetTest_$(1)_project := $(gb_DotnetTest_workdir)/$(1)/$(1).$(2)proj -$$(gb_DotnetTest_$(1)_project) : DOTNET_PROPERTY_ELEMENTS := <TargetFramework>net8.0</TargetFramework> +$$(gb_DotnetTest_$(1)_project) : DOTNET_PROPERTY_ELEMENTS := <TargetFramework>net$$(shell "$$(DOTNET)" --version | cut -d. -f1-2)</TargetFramework> $$(gb_DotnetTest_$(1)_project) : DOTNET_PROPERTY_ELEMENTS += <IsPackable>false</IsPackable> $$(gb_DotnetTest_$(1)_project) : DOTNET_PROPERTY_ELEMENTS += <IsTestProject>true</IsTestProject> $$(gb_DotnetTest_$(1)_project) : DOTNET_PROPERTY_ELEMENTS += <AssemblyName>$(1)</AssemblyName> diff --git a/solenv/gbuild/platform/com_GCC_defs.mk b/solenv/gbuild/platform/com_GCC_defs.mk index 4e41aa48233f..31900e8b9551 100644 --- a/solenv/gbuild/platform/com_GCC_defs.mk +++ b/solenv/gbuild/platform/com_GCC_defs.mk @@ -46,7 +46,6 @@ gb_COMPILEROPTFLAGS += $(if $(ENABLE_HARDENING_FLAGS),$(HARDENING_OPT_CFLAGS)) gb_AFLAGS := $(AFLAGS) gb_COMPILERDEFS := \ - -DBOOST_SYSTEM_NO_DEPRECATED \ -DCPPU_ENV=$(CPPU_ENV) \ $(if $(filter EMSCRIPTEN,$(OS)),-U_FORTIFY_SOURCE) \ diff --git a/solenv/gbuild/platform/com_MSC_defs.mk b/solenv/gbuild/platform/com_MSC_defs.mk index e915036b54c8..12bac932532d 100644 --- a/solenv/gbuild/platform/com_MSC_defs.mk +++ b/solenv/gbuild/platform/com_MSC_defs.mk @@ -47,7 +47,6 @@ endif gb_COMPILERDEFS := \ -DBOOST_OPTIONAL_USE_OLD_DEFINITION_OF_NONE \ - -DBOOST_SYSTEM_NO_DEPRECATED \ -D_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING \ -D_SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING \ -D_SILENCE_CXX17_RESULT_OF_DEPRECATION_WARNING \ diff --git a/svtools/source/brwbox/brwbox3.cxx b/svtools/source/brwbox/brwbox3.cxx index b7f209ac8180..f72903e089c8 100644 --- a/svtools/source/brwbox/brwbox3.cxx +++ b/svtools/source/brwbox/brwbox3.cxx @@ -69,11 +69,9 @@ rtl::Reference<comphelper::OAccessible> BrowseBox::CreateAccessible() { if (!m_xAccessible) { - Reference<XAccessible> xAccParent = GetAccessibleParent(); - if( xAccParent.is() ) - { - m_xAccessible = new AccessibleBrowseBox(xAccParent, *this); - } + rtl::Reference<comphelper::OAccessible> pAccParent = GetAccessibleParent(); + if (pAccParent.is()) + m_xAccessible = new AccessibleBrowseBox(pAccParent, *this); } return m_xAccessible; diff --git a/sw/CppunitTest_sw_filter_md.mk b/sw/CppunitTest_sw_filter_md.mk index 533e6b9d6529..c16777849709 100644 --- a/sw/CppunitTest_sw_filter_md.mk +++ b/sw/CppunitTest_sw_filter_md.mk @@ -18,6 +18,7 @@ $(eval $(call gb_CppunitTest_add_exception_objects,sw_filter_md, \ $(eval $(call gb_CppunitTest_use_libraries,sw_filter_md, \ cppu \ cppuhelper \ + editeng \ sal \ subsequenttest \ sw \ diff --git a/sw/inc/init.hxx b/sw/inc/init.hxx index 5c6e358c0a01..11c9936b14b6 100644 --- a/sw/inc/init.hxx +++ b/sw/inc/init.hxx @@ -32,7 +32,7 @@ class ItemInfoPackage; void InitCore(); // bastyp/init.cxx void FinitCore(); -ItemInfoPackage& getItemInfoPackageSwAttributes(); +std::unique_ptr<ItemInfoPackage> createItemInfoPackageSwAttributes(); namespace sw { diff --git a/sw/inc/pagedesc.hxx b/sw/inc/pagedesc.hxx index c213b03bf34c..0113371b7de8 100644 --- a/sw/inc/pagedesc.hxx +++ b/sw/inc/pagedesc.hxx @@ -244,6 +244,9 @@ public: const SwFrameFormat &GetFirstMaster() const { return m_FirstMaster; } const SwFrameFormat &GetFirstLeft() const { return m_FirstLeft; } + /// Set format properties on all non-shared odd/even/first headers (or footers) + bool SetFormatAttrOnAll(const SfxItemSet& rSet, const bool bHeader); + /** Reset all attrs of the format but keep the ones a pagedesc cannot live without. */ inline void ResetAllMasterAttr(); diff --git a/sw/inc/swatrset.hxx b/sw/inc/swatrset.hxx index ead0d7dcb11c..8e3df5fcb145 100644 --- a/sw/inc/swatrset.hxx +++ b/sw/inc/swatrset.hxx @@ -27,6 +27,7 @@ class SwDoc; class OutputDevice; class IDocumentSettingAccess; +class ItemInfoPackage; class SvxPostureItem; class SvxWeightItem; class SvxShadowedItem; @@ -149,7 +150,7 @@ private: SwDoc& m_rDoc; public: - SwAttrPool( SwDoc& rDoc ); + SwAttrPool(ItemInfoPackage& rInfoPackage, SwDoc& rDoc); private: virtual ~SwAttrPool() override; public: diff --git a/sw/inc/swmodule.hxx b/sw/inc/swmodule.hxx index fbd29640a160..d860b75f62af 100644 --- a/sw/inc/swmodule.hxx +++ b/sw/inc/swmodule.hxx @@ -36,6 +36,7 @@ #include "fldupde.hxx" class Color; +class ItemInfoPackage; class SfxItemSet; class SfxRequest; class SfxErrorHandler; @@ -94,6 +95,7 @@ class SAL_DLLPUBLIC_RTTI SwModule final : public SfxModule, public SfxListener, std::unique_ptr<SwTableAutoFormatTable> m_xTableAutoFormatTable; + std::unique_ptr<ItemInfoPackage> m_xItemInfoPackageSwAttributes; // Current view is held here in order to avoid one's being forced // to work via GetActiveView. @@ -233,6 +235,11 @@ public: virtual std::unique_ptr<SfxTabPage> CreateTabPage( sal_uInt16 nId, weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet ) override; SW_DLLPUBLIC virtual SfxStyleFamilies CreateStyleFamilies() override; + void InitItemInfoPackageSwAttributes(); + void RemoveItemInfoPackageSwAttributes(); + + ItemInfoPackage& getItemInfoPackageSwAttributes(); + // Invalidates online spell-wrong-lists if necessary. static void CheckSpellChanges( bool bOnlineSpelling, bool bIsSpellWrongAgain, bool bIsSpellAllAgain, bool bSmartTags ); diff --git a/sw/inc/view.hxx b/sw/inc/view.hxx index 7380d225139c..843536a5026d 100644 --- a/sw/inc/view.hxx +++ b/sw/inc/view.hxx @@ -272,7 +272,7 @@ class SW_DLLPUBLIC SwView: public SfxViewShell int m_nMaxOutlineLevelShown = 10; - bool m_bIsHighlightCharDF = false; + bool m_bIsSpotlightCharDF = false; bool m_bIsSpotlightParaStyles = false; bool m_bIsSpotlightCharStyles = false; @@ -734,7 +734,7 @@ public: virtual void flushPendingLOKInvalidateTiles() override; virtual std::optional<OString> getLOKPayload(int nType, int nViewId) const override; - bool IsHighlightCharDF() const { return m_bIsHighlightCharDF; } + bool IsSpotlightCharDF() const { return m_bIsSpotlightCharDF; } bool IsSpotlightParaStyles() const { return m_bIsSpotlightParaStyles; } bool IsSpotlightCharStyles() const { return m_bIsSpotlightCharStyles; } diff --git a/sw/qa/extras/mailmerge/data/mm-single-date.fodt b/sw/qa/extras/mailmerge/data/mm-single-date.fodt new file mode 100644 index 000000000000..d8fcf2e27c1a --- /dev/null +++ b/sw/qa/extras/mailmerge/data/mm-single-date.fodt @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" office:version="1.4" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:settings> + <config:config-item-set config:name="ooo:configuration-settings"> + <config:config-item config:name="CurrentDatabaseDataSource" config:type="string">single-date</config:config-item> + <config:config-item config:name="CurrentDatabaseCommand" config:type="string">Sheet1</config:config-item> + <config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item> + </config:config-item-set> + </office:settings> + <office:automatic-styles> + <number:date-style style:name="ISO" number:language="en" number:country="US"> + <number:year number:style="long"/> + <number:text>-</number:text> + <number:month number:style="long"/> + <number:text>-</number:text> + <number:day number:style="long"/> + </number:date-style> + </office:automatic-styles> + <office:body> + <office:text> + <text:p><text:database-display text:table-name="Sheet1" text:table-type="table" text:column-name="date" style:data-style-name="ISO" text:database-name="single-date"><date></text:database-display></text:p> + </office:text> + </office:body> +</office:document>
\ No newline at end of file diff --git a/sw/qa/extras/mailmerge/data/single-date.ods b/sw/qa/extras/mailmerge/data/single-date.ods Binary files differnew file mode 100644 index 000000000000..6f3d36ddefa2 --- /dev/null +++ b/sw/qa/extras/mailmerge/data/single-date.ods diff --git a/sw/qa/extras/mailmerge/mailmerge2.cxx b/sw/qa/extras/mailmerge/mailmerge2.cxx index de3c8fb770c6..e1f21e2089ad 100644 --- a/sw/qa/extras/mailmerge/mailmerge2.cxx +++ b/sw/qa/extras/mailmerge/mailmerge2.cxx @@ -432,6 +432,20 @@ CPPUNIT_TEST_FIXTURE(MMTest2, testTdf156061) getXPath(pXmlDoc, "//txt[2]/infos/bounds", "height").toInt32()); } +// The document has a MM field referencing a date in the data source having a single record. +// The check ensures that the date arrives correctly to the merged document. +DECLARE_SHELL_MAILMERGE_TEST(testTdf168252, "mm-single-date.fodt", "single-date.ods", "Sheet1") +{ + executeMailMerge(); + CPPUNIT_ASSERT(mxSwTextDocument); + + // Without the fix, this would fail with + // - Expected: 2025-08-31 + // - Actual : 2025-09-02 + // because the DB filed code assumed obsolete null date. + CPPUNIT_ASSERT_EQUAL(u"2025-08-31"_ustr, mxSwTextDocument->getText()->getString()); +} + } // end of anonymous namespace namespace com::sun::star::table { diff --git a/sw/qa/filter/md/md.cxx b/sw/qa/filter/md/md.cxx index 2f62fa80703b..824b80d3874d 100644 --- a/sw/qa/filter/md/md.cxx +++ b/sw/qa/filter/md/md.cxx @@ -355,7 +355,7 @@ CPPUNIT_TEST_FIXTURE(Test, testExportingTable) SAL_NEWLINE_STRING "| A1 | B1 | C1 |" SAL_NEWLINE_STRING // Delimiter row consists of cells whose only content are hyphens (-). - "|-|-|-|" SAL_NEWLINE_STRING + "| - | - | - |" SAL_NEWLINE_STRING "| A2 | B2 | C2 |" SAL_NEWLINE_STRING "| A3 | B3 | C3 |" SAL_NEWLINE_STRING SAL_NEWLINE_STRING @@ -463,6 +463,48 @@ CPPUNIT_TEST_FIXTURE(Test, testCodeBlockMdExport) CPPUNIT_ASSERT_EQUAL(aExpected, aActual); } +CPPUNIT_TEST_FIXTURE(Test, testTableColumnAdjustMdExport) +{ + // Given a document that has a table with custom adjustments: + createSwDoc(); + SwDocShell* pDocShell = getSwDocShell(); + SwWrtShell* pWrtShell = pDocShell->GetWrtShell(); + pWrtShell->Insert(u"before"_ustr); + SwInsertTableOptions aInsertTableOptions(SwInsertTableFlags::DefaultBorder, + /*nRowsToRepeat=*/0); + pWrtShell->InsertTable(aInsertTableOptions, /*nRows=*/1, /*nCols=*/3); + pWrtShell->Insert(u"after"_ustr); + pWrtShell->SttPara(); + pWrtShell->MoveTable(GotoPrevTable, fnTableStart); + pWrtShell->Insert(u"A1"_ustr); + pWrtShell->GoNextCell(); + pWrtShell->Insert(u"B1"_ustr); + pWrtShell->SetAttrItem(SvxAdjustItem(SvxAdjust::Center, RES_PARATR_ADJUST)); + pWrtShell->GoNextCell(); + pWrtShell->Insert(u"C1"_ustr); + pWrtShell->SetAttrItem(SvxAdjustItem(SvxAdjust::Right, RES_PARATR_ADJUST)); + + // When saving that to markdown: + save(mpFilter); + + // Then make sure the table content is not lost: + std::string aActual = TempFileToString(); + std::string aExpected( + // clang-format off + "before" SAL_NEWLINE_STRING + SAL_NEWLINE_STRING + "| A1 | B1 | C1 |" SAL_NEWLINE_STRING + "| - | :-: | -: |" SAL_NEWLINE_STRING + SAL_NEWLINE_STRING + "after" SAL_NEWLINE_STRING + // clang-format on + ); + // Without the accompanying fix in place, this test would have failed with: + // - Actual : | - | - | - | + // i.e. the delimiter row's cell adjustments were lost. + CPPUNIT_ASSERT_EQUAL(aExpected, aActual); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sw/source/core/attr/hints.cxx b/sw/source/core/attr/hints.cxx index 8aaabaf26816..a585aa81453a 100644 --- a/sw/source/core/attr/hints.cxx +++ b/sw/source/core/attr/hints.cxx @@ -21,6 +21,7 @@ #include <hintids.hxx> #include <hints.hxx> #include <ndtxt.hxx> +#include <swmodule.hxx> #include <swtypes.hxx> #include <init.hxx> #include <svl/languageoptions.hxx> @@ -136,11 +137,11 @@ const SfxPoolItem* GetDfltAttr(sal_uInt16 nWhich) { #ifdef DBG_UTIL OSL_ASSERT(nWhich < POOLATTR_END && nWhich >= POOLATTR_BEGIN); - const SfxPoolItem* pRetval(getItemInfoPackageSwAttributes().getExistingItemInfo(nWhich - POOLATTR_BEGIN).getItem()); + const SfxPoolItem* pRetval(SwModule::get()->getItemInfoPackageSwAttributes().getExistingItemInfo(nWhich - POOLATTR_BEGIN).getItem()); OSL_ENSURE(pRetval, "GetDfltFormatAttr(): Dflt == 0"); return pRetval; #else - return getItemInfoPackageSwAttributes().getExistingItemInfo(nWhich - POOLATTR_BEGIN).getItem(); + return SwModule::get()->getItemInfoPackageSwAttributes().getExistingItemInfo(nWhich - POOLATTR_BEGIN).getItem(); #endif } diff --git a/sw/source/core/attr/swatrset.cxx b/sw/source/core/attr/swatrset.cxx index 76865e745aac..f7f49dcef96e 100644 --- a/sw/source/core/attr/swatrset.cxx +++ b/sw/source/core/attr/swatrset.cxx @@ -45,11 +45,11 @@ -SwAttrPool::SwAttrPool(SwDoc& rDoc) +SwAttrPool::SwAttrPool(ItemInfoPackage& rInfoPackage, SwDoc& rDoc) : SfxItemPool(u"SWG"_ustr) , m_rDoc(rDoc) { - registerItemInfoPackage(getItemInfoPackageSwAttributes()); + registerItemInfoPackage(rInfoPackage); // create SfxItemPool and EditEngine pool and add these in a chain. These // belong us and will be removed/destroyed in removeAndDeleteSecondaryPools() used from diff --git a/sw/source/core/bastyp/init.cxx b/sw/source/core/bastyp/init.cxx index ab482bfda115..21a5cc10c32f 100644 --- a/sw/source/core/bastyp/init.cxx +++ b/sw/source/core/bastyp/init.cxx @@ -271,7 +271,7 @@ SwTOXMark* createSwTOXMarkForItemInfoPackage() return new SwTOXMark(); } -ItemInfoPackage& getItemInfoPackageSwAttributes() +std::unique_ptr<ItemInfoPackage> createItemInfoPackageSwAttributes() { class ItemInfoPackageSwAttributes : public ItemInfoPackage { @@ -496,11 +496,7 @@ ItemInfoPackage& getItemInfoPackageSwAttributes() } }; - - static std::unique_ptr<ItemInfoPackageSwAttributes> g_aItemInfoPackageSwAttributes; - if (!g_aItemInfoPackageSwAttributes) - g_aItemInfoPackageSwAttributes.reset(new ItemInfoPackageSwAttributes); - return *g_aItemInfoPackageSwAttributes; + return std::make_unique<ItemInfoPackageSwAttributes>(); } std::vector<SvGlobalName> *pGlobalOLEExcludeList = nullptr; diff --git a/sw/source/core/doc/docdesc.cxx b/sw/source/core/doc/docdesc.cxx index 0aaa2d2a246d..5846812e924c 100644 --- a/sw/source/core/doc/docdesc.cxx +++ b/sw/source/core/doc/docdesc.cxx @@ -591,7 +591,7 @@ void SwDoc::ChgPageDesc( size_t i, const SwPageDesc &rChged ) CopyMasterFooter(rChged, pStashedFirstMasterFoot ? pStashedFirstMasterFoot->GetFooter() : rMasterFoot, rDesc, false, true); // Copy first master CopyMasterFooter(rChged, pStashedFirstLeftFoot ? pStashedFirstLeftFoot->GetFooter() : rMasterFoot, rDesc, true, true); // Copy first left - if (pStashedLeftFormat) + if (pStashedLeftFoot) rDesc.RemoveStashedFormat(false, true, false); if (pStashedFirstMasterFoot) diff --git a/sw/source/core/doc/docnew.cxx b/sw/source/core/doc/docnew.cxx index 0032bbff3f06..5b8120ca973d 100644 --- a/sw/source/core/doc/docnew.cxx +++ b/sw/source/core/doc/docnew.cxx @@ -99,6 +99,7 @@ #include <DocumentStylePoolManager.hxx> #include <DocumentExternalDataManager.hxx> #include <wrtsh.hxx> +#include <swmodule.hxx> #include <unocrsr.hxx> #include <fmthdft.hxx> #include <frameformats.hxx> @@ -206,7 +207,7 @@ static void lcl_DelFormatIndices( SwFormat const * pFormat ) */ SwDoc::SwDoc() : m_pNodes(new SwNodes(*this)), - mpAttrPool(new SwAttrPool(*this)), + mpAttrPool(new SwAttrPool(SwModule::get()->getItemInfoPackageSwAttributes(), *this)), maOLEModifiedIdle( "sw::SwDoc maOLEModifiedIdle" ), mpMarkManager(new ::sw::mark::MarkManager(*this)), m_pMetaFieldManager(new ::sw::MetaFieldManager()), diff --git a/sw/source/core/fields/dbfld.cxx b/sw/source/core/fields/dbfld.cxx index 700864f16818..573269e1ae01 100644 --- a/sw/source/core/fields/dbfld.cxx +++ b/sw/source/core/fields/dbfld.cxx @@ -264,7 +264,7 @@ bool SwDBField::FormatValue( SvNumberFormatter const * pDocFormatter, OUString c if( DataType::DATE == nColumnType || DataType::TIME == nColumnType || DataType::TIMESTAMP == nColumnType ) { - Date aStandard( 1, 1, 1900 ); + Date aStandard(30, 12, 1899); if( pDocFormatter->GetNullDate() != aStandard ) aNumber += (aStandard - pDocFormatter->GetNullDate()); } diff --git a/sw/source/core/layout/pagedesc.cxx b/sw/source/core/layout/pagedesc.cxx index a65f3332a792..780123d62452 100644 --- a/sw/source/core/layout/pagedesc.cxx +++ b/sw/source/core/layout/pagedesc.cxx @@ -28,6 +28,7 @@ #include <sal/log.hxx> #include <fmtclds.hxx> #include <fmtfsize.hxx> +#include <fmthdft.hxx> #include <pagefrm.hxx> #include <pagedesc.hxx> #include <swtable.hxx> @@ -423,6 +424,59 @@ void SwPageDesc::ChgFirstShare( bool bNew ) m_eUse &= UseOnPage::NoFirstShare; } +bool SwPageDesc::SetFormatAttrOnAll(const SfxItemSet& rSet, const bool bHeader) +{ + if( !rSet.Count() ) + return false; + + // Warning: no attempt is made here to limit rSet to properties that are "safe" + // to duplicate to all of the different headers/footers. + assert(!rSet.HasItem(RES_CNTNT) && "unexpected use of SwPageDesc::SetFormatAttrOnAll"); + + bool bRet = false; + if (bHeader) + { + auto pHF = const_cast<SwFrameFormat*>(GetMaster().GetHeader().GetHeaderFormat()); + bRet = pHF && pHF->SetFormatAttr(rSet); + if (bRet && !IsFirstShared()) + { + pHF = const_cast<SwFrameFormat*>(GetFirstMaster().GetHeader().GetHeaderFormat()); + pHF && pHF->SetFormatAttr(rSet); + } + if (bRet && !IsHeaderShared()) + { + pHF = const_cast<SwFrameFormat*>(GetLeft().GetHeader().GetHeaderFormat()); + pHF && pHF->SetFormatAttr(rSet); + if (!IsFirstShared()) + { + pHF = const_cast<SwFrameFormat*>(GetFirstLeft().GetHeader().GetHeaderFormat()); + pHF && pHF->SetFormatAttr(rSet); + } + } + } + else // footer + { + auto pHF = const_cast<SwFrameFormat*>(GetMaster().GetFooter().GetFooterFormat()); + bRet = pHF && pHF->SetFormatAttr(rSet); + if (bRet && !IsFirstShared()) + { + pHF = const_cast<SwFrameFormat*>(GetFirstMaster().GetFooter().GetFooterFormat()); + pHF && pHF->SetFormatAttr(rSet); + } + if (bRet && !IsFooterShared()) + { + pHF = const_cast<SwFrameFormat*>(GetLeft().GetFooter().GetFooterFormat()); + pHF && pHF->SetFormatAttr(rSet); + if (bRet && !IsFirstShared()) + { + pHF= const_cast<SwFrameFormat*>(GetFirstLeft().GetFooter().GetFooterFormat()); + pHF && pHF->SetFormatAttr(rSet); + } + } + } + return bRet; +} + void SwPageDesc::StashFrameFormat(const SwFrameFormat& rFormat, bool bHeader, bool bLeft, bool bFirst) { assert(rFormat.GetRegisteredIn()); diff --git a/sw/source/core/text/EnhancedPDFExportHelper.cxx b/sw/source/core/text/EnhancedPDFExportHelper.cxx index 193b33afa2ae..6591bec892cd 100644 --- a/sw/source/core/text/EnhancedPDFExportHelper.cxx +++ b/sw/source/core/text/EnhancedPDFExportHelper.cxx @@ -1449,10 +1449,13 @@ void SwTaggedPDFHelper::BeginBlockStructureElements() // open all parent sections, so that the SEs of sections // are nested in the same way as their SwSectionNodes std::vector<SwSection const*> parents; - for (SwSection const* pParent = pSection->GetParent(); - pParent != nullptr; pParent = pParent->GetParent()) + // iterate only *direct* parents - do not leave table cell! + for (SwSectionNode const* pSectionNode{pSection->GetFormat() + ->GetSectionNode()->StartOfSectionNode()->GetSectionNode()}; + pSectionNode != nullptr; + pSectionNode = pSectionNode->StartOfSectionNode()->GetSectionNode()) { - parents.push_back(pParent); + parents.push_back(&pSectionNode->GetSection()); } for (auto it = parents.rbegin(); it != parents.rend(); ++it) { diff --git a/sw/source/core/text/inftxt.cxx b/sw/source/core/text/inftxt.cxx index a9333e66b9b4..4f6eca343c12 100644 --- a/sw/source/core/text/inftxt.cxx +++ b/sw/source/core/text/inftxt.cxx @@ -1375,7 +1375,7 @@ void SwTextPaintInfo::DrawCSDFHighlighting(const SwLinePortion &rPor) const if (!pView) return; - if (!pView->IsSpotlightCharStyles() && !pView->IsHighlightCharDF()) + if (!pView->IsSpotlightCharStyles() && !pView->IsSpotlightCharDF()) return; SwRect aRect; @@ -1430,7 +1430,7 @@ void SwTextPaintInfo::DrawCSDFHighlighting(const SwLinePortion &rPor) const } } // not character style formatted - else if (pView->IsHighlightCharDF()) + else if (pView->IsSpotlightCharDF()) { const std::vector<OUString> aHiddenProperties{ UNO_NAME_RSID, UNO_NAME_PARA_IS_NUMBERING_RESTART, diff --git a/sw/source/filter/md/wrtmd.cxx b/sw/source/filter/md/wrtmd.cxx index 75400719e687..7430aa389447 100644 --- a/sw/source/filter/md/wrtmd.cxx +++ b/sw/source/filter/md/wrtmd.cxx @@ -678,7 +678,20 @@ void OutMarkdown_SwTextNode(SwMDWriter& rWrt, const SwTextNode& rNode, bool bFir rWrt.Strm().WriteUnicodeOrByteText(u"" SAL_NEWLINE_STRING); for (size_t nBox = 0; nBox < oCellInfo->nFirstRowBoxCount; ++nBox) { - rWrt.Strm().WriteUnicodeOrByteText(u"|-"); + std::u16string aDelimiter(u"| - "); + // Decorate with leading or trailing colon if the adjustment is not the default. + switch (oCellInfo->aFirstRowBoxAdjustments[nBox]) + { + case SvxAdjust::Center: + aDelimiter = u"| :-: "; + break; + case SvxAdjust::Right: + aDelimiter = u"| -: "; + break; + default: + break; + } + rWrt.Strm().WriteUnicodeOrByteText(aDelimiter); } rWrt.Strm().WriteUnicodeOrByteText(u"|"); } @@ -699,13 +712,30 @@ void OutMarkdown_SwTableNode(SwMDWriter& rWrt, const SwTableNode& rTableNode) for (size_t nLine = 0; nLine < rTable.GetTabLines().size(); ++nLine) { const SwTableLine* pLine = rTable.GetTabLines()[nLine]; + std::vector<SvxAdjust> aBoxAdjustments; for (size_t nBox = 0; nBox < pLine->GetTabBoxes().size(); ++nBox) { const SwTableBox* pBox = pLine->GetTabBoxes()[nBox]; const SwStartNode* pStart = pBox->GetSttNd(); - SwMDCellInfo& rStartInfo = aTableInfo.aCellInfos[pStart->GetIndex() + 1]; + SwNodeOffset nCellStartIndex = pStart->GetIndex() + 1; + SwMDCellInfo& rStartInfo = aTableInfo.aCellInfos[nCellStartIndex]; const SwEndNode* pEnd = pStart->EndOfSectionNode(); rStartInfo.bCellStart = true; + + if (nLine == 0) + { + // First row, save the alignment of the first text node, if the cell has one. + SwTextNode* pCellStartNode + = rWrt.m_pDoc->GetNodes()[nCellStartIndex]->GetTextNode(); + SvxAdjust eAdjust{}; + if (pCellStartNode) + { + const SwAttrSet& rCellStartSet = pCellStartNode->GetSwAttrSet(); + eAdjust = rCellStartSet.Get(RES_PARATR_ADJUST).GetAdjust(); + } + aBoxAdjustments.push_back(eAdjust); + } + if (nBox == 0) { rStartInfo.bRowStart = true; @@ -718,6 +748,7 @@ void OutMarkdown_SwTableNode(SwMDWriter& rWrt, const SwTableNode& rTableNode) { rEndInfo.bFirstRowEnd = true; rEndInfo.nFirstRowBoxCount = pLine->GetTabBoxes().size(); + rEndInfo.aFirstRowBoxAdjustments = aBoxAdjustments; } } } diff --git a/sw/source/filter/md/wrtmd.hxx b/sw/source/filter/md/wrtmd.hxx index 10960de60461..d3f6d15db58f 100644 --- a/sw/source/filter/md/wrtmd.hxx +++ b/sw/source/filter/md/wrtmd.hxx @@ -36,7 +36,10 @@ struct SwMDCellInfo bool bRowStart = false; bool bRowEnd = false; bool bFirstRowEnd = false; + + // These are only set in the bFirstRowEnd == true case. size_t nFirstRowBoxCount = 0; + std::vector<SvxAdjust> aFirstRowBoxAdjustments; }; /// Tracks information about one SwTableNode, the instance is alive while the write of the table is diff --git a/sw/source/ui/chrdlg/numpara.cxx b/sw/source/ui/chrdlg/numpara.cxx index 07044fcfbfb4..f11e3fb6edf0 100644 --- a/sw/source/ui/chrdlg/numpara.cxx +++ b/sw/source/ui/chrdlg/numpara.cxx @@ -29,7 +29,6 @@ #include <fmtline.hxx> #include <numpara.hxx> -#include <officecfg/Office/Common.hxx> #include <sfx2/dispatch.hxx> #include <sfx2/frame.hxx> #include <sfx2/viewsh.hxx> @@ -89,11 +88,6 @@ SwParagraphNumTabPage::SwParagraphNumTabPage(weld::Container* pPage, weld::Dialo m_xRestartParaCountCB->connect_toggled(LINK(this, SwParagraphNumTabPage, LineCountHdl_Impl)); m_xNumberStyleLB->connect_changed(LINK(this, SwParagraphNumTabPage, EditNumStyleSelectHdl_Impl)); m_xEditNumStyleBtn->connect_clicked(LINK(this, SwParagraphNumTabPage, EditNumStyleHdl_Impl)); - - if (officecfg::Office::Common::Misc::ExperimentalMode::get()) - m_xListLvBX->show(); - else - m_xListLvBX->hide(); } SwParagraphNumTabPage::~SwParagraphNumTabPage() diff --git a/sw/source/ui/dbui/dbinsdlg.cxx b/sw/source/ui/dbui/dbinsdlg.cxx index d12c56c8d287..ca29ed54341d 100644 --- a/sw/source/ui/dbui/dbinsdlg.cxx +++ b/sw/source/ui/dbui/dbinsdlg.cxx @@ -1085,7 +1085,7 @@ void SwInsertDBColAutoPilot::DataToDoc( const Sequence<Any>& rSelection, { if(rNumFormatr.GetType(aNumFormat.GetValue()) & SvNumFormatType::DATE) { - ::Date aStandard(1,1,1900); + ::Date aStandard(30, 12, 1899); if (rNumFormatr.GetNullDate() != aStandard) fVal += (aStandard - rNumFormatr.GetNullDate()); } @@ -1280,10 +1280,8 @@ void SwInsertDBColAutoPilot::DataToDoc( const Sequence<Any>& rSelection, DataType::TIMESTAMP == eDataType) { - ::Date aStandard(1,1,1900); - ::Date aCompare(aDBFormatData.aNullDate.Day , - aDBFormatData.aNullDate.Month, - aDBFormatData.aNullDate.Year); + ::Date aStandard(30, 12, 1899); + ::Date aCompare(aDBFormatData.aNullDate); if(aStandard != aCompare) nValue += (aStandard - aCompare); } @@ -1310,7 +1308,7 @@ void SwInsertDBColAutoPilot::DataToDoc( const Sequence<Any>& rSelection, const Color* pCol; if(rNumFormatr.GetType(pDBCol->nFormat) & SvNumFormatType::DATE) { - ::Date aStandard(1,1,1900); + ::Date aStandard(30, 12, 1899); if (rNumFormatr.GetNullDate() != aStandard) nValue += (aStandard - rNumFormatr.GetNullDate()); } diff --git a/sw/source/uibase/app/docst.cxx b/sw/source/uibase/app/docst.cxx index 989eeaa871ec..24ebdbbd5d03 100644 --- a/sw/source/uibase/app/docst.cxx +++ b/sw/source/uibase/app/docst.cxx @@ -657,7 +657,8 @@ IMPL_LINK_NOARG(ApplyStyle, ApplyHdl, LinkParamNone*, void) ::ConvertAttrGenToChar(aTmpSet, m_xTmp->GetItemSet()); } - m_xTmp->SetItemSet( aTmpSet, false ); + bool bParam1 = SfxStyleFamily::Page == m_nFamily; // bApplyToAllFormatFrames + m_xTmp->SetItemSet(aTmpSet, /*bBroadcast=*/ false, bParam1); if( SfxStyleFamily::Page == m_nFamily && SvtCTLOptions::IsCTLFontEnabled() ) { diff --git a/sw/source/uibase/app/docstyle.cxx b/sw/source/uibase/app/docstyle.cxx index d03083ac6dd3..73818d9ac2d5 100644 --- a/sw/source/uibase/app/docstyle.cxx +++ b/sw/source/uibase/app/docstyle.cxx @@ -1622,8 +1622,7 @@ void SwDocStyleSheet::MergeIndentAttrsOfListStyle( SfxItemSet& rSet ) } // handling of parameter <bResetIndentAttrsAtParagraphStyle> -void SwDocStyleSheet::SetItemSet( const SfxItemSet& rSet, const bool bBroadcast, - const bool bResetIndentAttrsAtParagraphStyle ) +void SwDocStyleSheet::SetItemSet(const SfxItemSet& rSet, const bool bBroadcast, const bool bParam1) { // if applicable determine format first if(!m_bPhysical) @@ -1715,6 +1714,7 @@ void SwDocStyleSheet::SetItemSet( const SfxItemSet& rSet, const bool bBroadcast, m_rDoc.DelTextFormatColl( m_pColl ); m_pColl = pCColl; } + const bool bResetIndentAttrsAtParagraphStyle = bParam1; if ( bResetIndentAttrsAtParagraphStyle && rSet.GetItemState( RES_PARATR_NUMRULE, false ) == SfxItemState::SET && rSet.GetItemState(RES_MARGIN_FIRSTLINE, false) != SfxItemState::SET && @@ -1892,7 +1892,8 @@ void SwDocStyleSheet::SetItemSet( const SfxItemSet& rSet, const bool bBroadcast, if( pNewDsc ) { - ::ItemSetToPageDesc( aSet, *pNewDsc ); + const bool bApplyToAllFormatFrames = bParam1; + ::ItemSetToPageDesc(aSet, *pNewDsc, bApplyToAllFormatFrames); m_rDoc.ChgPageDesc( nPgDscPos, *pNewDsc ); m_pDesc = &m_rDoc.GetPageDesc( nPgDscPos ); m_rDoc.PreDelPageDesc(pNewDsc.get()); // #i7983# diff --git a/sw/source/uibase/app/swdll.cxx b/sw/source/uibase/app/swdll.cxx index 765330c9c257..c85355300898 100644 --- a/sw/source/uibase/app/swdll.cxx +++ b/sw/source/uibase/app/swdll.cxx @@ -95,6 +95,7 @@ SwDLL::SwDLL() SfxObjectFactory* pWDocFact = &SwWebDocShell::Factory(); auto pUniqueModule = std::make_unique<SwModule>(pWDocFact, pDocFact, pGlobDocFact); + SwModule* pModule = pUniqueModule.get(); SfxApplication::SetModule(SfxToolsModule::Writer, std::move(pUniqueModule)); pWDocFact->SetDocumentServiceName(u"com.sun.star.text.WebDocument"_ustr); @@ -119,6 +120,8 @@ SwDLL::SwDLL() m_pFilters.reset(new sw::Filters); ::InitUI(); + pModule->InitItemInfoPackageSwAttributes(); + // register your view-factories here RegisterFactories(); @@ -146,8 +149,11 @@ SwDLL::~SwDLL() COVERITY_NOEXCEPT_FALSE m_pAutoCorrCfg->SetAutoCorrect(nullptr); // delete SwAutoCorrect before exit handlers } - ::FinitUI(); m_pFilters.reset(); + + SwModule::get()->RemoveItemInfoPackageSwAttributes(); + + ::FinitUI(); ::FinitCore(); // sign out object-Factory SdrObjFactory::RemoveMakeObjectHdl(LINK(&aSwObjectFactory, SwObjectFactory, MakeObject )); diff --git a/sw/source/uibase/app/swmodule.cxx b/sw/source/uibase/app/swmodule.cxx index 76986353f844..7d3cf07e2f47 100644 --- a/sw/source/uibase/app/swmodule.cxx +++ b/sw/source/uibase/app/swmodule.cxx @@ -72,6 +72,7 @@ #include <wlistsh.hxx> #include <wtabsh.hxx> #include <navipi.hxx> +#include <init.hxx> #include <inputwin.hxx> #include <usrpref.hxx> #include <uinums.hxx> @@ -337,6 +338,23 @@ void SwDLL::RegisterControls() SwJumpToSpecificPageControl::RegisterControl(SID_JUMP_TO_SPECIFIC_PAGE, pMod); } +void SwModule::InitItemInfoPackageSwAttributes() +{ + OSL_ENSURE(!m_xItemInfoPackageSwAttributes, "ItemInfoPackageSwAttributes already exists!"); + m_xItemInfoPackageSwAttributes = createItemInfoPackageSwAttributes(); +} + +ItemInfoPackage& SwModule::getItemInfoPackageSwAttributes() +{ + assert(m_xItemInfoPackageSwAttributes && "InitItemInfoPackageSwAttributes should have been called before this"); + return *m_xItemInfoPackageSwAttributes; +} + +void SwModule::RemoveItemInfoPackageSwAttributes() +{ + m_xItemInfoPackageSwAttributes.reset(); +} + SfxStyleFamilies SwModule::CreateStyleFamilies() { SfxStyleFamilies aStyleFamilies; diff --git a/sw/source/uibase/dochdl/swdtflvr.cxx b/sw/source/uibase/dochdl/swdtflvr.cxx index bbd0fe4ac382..610bc9d3b7f5 100644 --- a/sw/source/uibase/dochdl/swdtflvr.cxx +++ b/sw/source/uibase/dochdl/swdtflvr.cxx @@ -136,8 +136,6 @@ #include <vcl/uitest/eventdescription.hxx> #include <svx/GenericDropDownFieldDialog.hxx> -#include <vcl/GraphicNativeTransform.hxx> -#include <vcl/GraphicNativeMetadata.hxx> #include <vcl/TypeSerializer.hxx> #include <comphelper/lok.hxx> #include <sfx2/classificationhelper.hxx> @@ -427,20 +425,6 @@ namespace rDest.GetMetaFieldManager().copyDocumentProperties(rSrc); } - - void lclCheckAndPerformRotation(Graphic& aGraphic) - { - GraphicNativeMetadata aMetadata; - if ( !aMetadata.read(aGraphic) ) - return; - - Degree10 aRotation = aMetadata.getRotation(); - if (aRotation) - { - GraphicNativeTransform aTransform( aGraphic ); - aTransform.rotate( aRotation ); - } - } } sal_Bool SAL_CALL SwTransferable::isComplex() @@ -2631,9 +2615,6 @@ bool SwTransferable::PasteTargetURL( const TransferableDataHelper& rData, if( bRet ) { - //Check and Perform rotation if needed - lclCheckAndPerformRotation(aGraphic); - switch( nAction ) { case SwPasteSdr::Insert: @@ -3040,9 +3021,6 @@ bool SwTransferable::PasteGrf( const TransferableDataHelper& rData, SwWrtShell& if( bRet ) { - //Check and Perform rotation if needed - lclCheckAndPerformRotation(aGraphic); - OUString sURL; if( dynamic_cast< const SwWebDocShell *>( rSh.GetView().GetDocShell() ) != nullptr // #i123922# if link action is noted, also take URL diff --git a/sw/source/uibase/docvw/HeaderFooterWin.cxx b/sw/source/uibase/docvw/HeaderFooterWin.cxx index ebad3ae4c3a5..719e609d715c 100644 --- a/sw/source/uibase/docvw/HeaderFooterWin.cxx +++ b/sw/source/uibase/docvw/HeaderFooterWin.cxx @@ -488,8 +488,8 @@ void SwHeaderFooterWin::ExecuteCommand(std::u16string_view rIdent) } else if (rIdent == u"borderback") { - const SwPageDesc* pDesc = pPageFrame->GetPageDesc(); - const SwFrameFormat& rMaster = pDesc->GetMaster(); + SwPageDesc& rPageDesc = const_cast<SwPageDesc&>(*pPageFrame->GetPageDesc()); + SwFrameFormat& rMaster = rPageDesc.GetMaster(); SwFrameFormat* pHFFormat = const_cast< SwFrameFormat* >( rMaster.GetFooter().GetFooterFormat() ); if ( m_bIsHeader ) pHFFormat = const_cast< SwFrameFormat* >( rMaster.GetHeader().GetHeaderFormat() ); @@ -517,7 +517,9 @@ void SwHeaderFooterWin::ExecuteCommand(std::u16string_view rIdent) if (svx::ShowBorderBackgroundDlg( GetFrameWeld(), &aSet ) ) { - pHFFormat->SetFormatAttr( aSet ); + // Apply the modified format to all (first, even, odd) of the page style's FrameFormats + aSet.DisableItem(RES_CNTNT); // don't duplicate the content though... + rPageDesc.SetFormatAttrOnAll(aSet, m_bIsHeader); rView.GetDocShell()->SetModified(); } } diff --git a/sw/source/uibase/docvw/PageBreakWin.cxx b/sw/source/uibase/docvw/PageBreakWin.cxx index 4ef4d86a60c1..8fc7615444d1 100644 --- a/sw/source/uibase/docvw/PageBreakWin.cxx +++ b/sw/source/uibase/docvw/PageBreakWin.cxx @@ -305,20 +305,17 @@ void SwBreakDashedLine::execute(std::u16string_view rIdent) ? *static_cast<SwTextFrame*>(pCnt)->GetTextNodeFirst() : *static_cast<SwNoTextFrame*>(pCnt)->GetNode(); + rSh.Push(); + rSh.ClearMark(); + rSh.SetSelection(SwPaM(rNd)); + if ( pCnt->IsInTab() ) { - rSh.Push( ); - rSh.ClearMark(); - - rSh.SetSelection( SwPaM(rNd) ); - SfxStringItem aItem(m_pEditWin->GetView().GetPool().GetWhichIDFromSlotID(FN_FORMAT_TABLE_DLG), u"textflow"_ustr); m_pEditWin->GetView().GetViewFrame().GetDispatcher()->ExecuteList( FN_FORMAT_TABLE_DLG, SfxCallMode::SYNCHRON | SfxCallMode::RECORD, { &aItem }); - - rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); } else { @@ -330,6 +327,9 @@ void SwBreakDashedLine::execute(std::u16string_view rIdent) SfxCallMode::SYNCHRON | SfxCallMode::RECORD, { &aItem, &aPaMItem }); } + + rSh.Pop(SwCursorShell::PopMode::DeleteCurrent); + rSh.LockView( bOldLock ); m_pEditWin->GrabFocus( ); } diff --git a/sw/source/uibase/docvw/edtwin.cxx b/sw/source/uibase/docvw/edtwin.cxx index a1e688ae3f13..33ac354df7da 100644 --- a/sw/source/uibase/docvw/edtwin.cxx +++ b/sw/source/uibase/docvw/edtwin.cxx @@ -6939,14 +6939,18 @@ Selection SwEditWin::GetSurroundingTextSelection() const if (rSh.HasDrawView() && rSh.GetDrawView()->IsTextEdit()) return rSh.GetDrawView()->GetTextEditOutlinerView()->GetSurroundingTextSelection(); - Selection aSel(0, 0); if( rSh.HasSelection() ) { OUString sReturn; rSh.GetSelectedText( sReturn, ParaBreakType::ToOnlyCR ); - aSel = Selection( 0, sReturn.getLength() ); + const SwCursor* pCursor = rSh.GetCursor(); + if (pCursor && *pCursor->GetPoint() < *pCursor->GetMark()) + return Selection(sReturn.getLength(), 0); + else + return Selection(0, sReturn.getLength()); } - else if (rSh.GetCursor()->GetPoint()->GetNode().GetTextNode()) + + if (rSh.GetCursor()->GetPoint()->GetNode().GetTextNode()) { bool bUnLockView = !rSh.IsViewLocked(); rSh.LockView(true); @@ -6974,10 +6978,10 @@ Selection SwEditWin::GetSurroundingTextSelection() const if (bUnLockView) rSh.LockView(false); - aSel = Selection(sal_Int32(nPos - nStartPos), sal_Int32(nPos - nStartPos)); + return Selection(sal_Int32(nPos - nStartPos), sal_Int32(nPos - nStartPos)); } - return aSel; + return Selection(0, 0); } bool SwEditWin::DeleteSurroundingText(const Selection& rSelection) diff --git a/sw/source/uibase/docvw/edtwin2.cxx b/sw/source/uibase/docvw/edtwin2.cxx index d087cc2c666c..7da57f94f37c 100644 --- a/sw/source/uibase/docvw/edtwin2.cxx +++ b/sw/source/uibase/docvw/edtwin2.cxx @@ -105,7 +105,7 @@ bool PSCSDFPropsQuickHelp(const HelpEvent &rEvt, SwWrtShell& rSh) UIName sText; SwView& rView = rSh.GetView(); - if (rView.IsHighlightCharDF() || rView.IsSpotlightParaStyles() + if (rView.IsSpotlightCharDF() || rView.IsSpotlightParaStyles() || rView.IsSpotlightCharStyles()) { SwPosition aPos(rSh.GetDoc()->GetNodes()); @@ -150,7 +150,7 @@ bool PSCSDFPropsQuickHelp(const HelpEvent &rEvt, SwWrtShell& rSh) sText = SwStyleNameMapper::GetUIName(ProgName(sCharStyle), SwGetPoolIdFromName::ChrFmt); } - if (sText.isEmpty() && rView.IsHighlightCharDF()) + if (sText.isEmpty() && rView.IsSpotlightCharDF()) { // check if in direct formatting highlighted area const std::vector<OUString> aHiddenProperties{ UNO_NAME_RSID, diff --git a/sw/source/uibase/inc/uitool.hxx b/sw/source/uibase/inc/uitool.hxx index d2c7c3b051c0..4b616b95e1cd 100644 --- a/sw/source/uibase/inc/uitool.hxx +++ b/sw/source/uibase/inc/uitool.hxx @@ -69,7 +69,8 @@ SW_DLLPUBLIC void ConvertAttrGenToChar(SfxItemSet& rSet, const SfxItemSet& rOrig void ApplyCharBackground(Color const& rBackgroundColor, model::ComplexColor const& rComplexColor, SwWrtShell& rShell); // SfxItemSets <-> PageDesc -void ItemSetToPageDesc( const SfxItemSet& rSet, SwPageDesc& rPageDesc ); +void ItemSetToPageDesc(const SfxItemSet& rSet, SwPageDesc& rPageDesc, + bool bApplyToAllFormatFrames = false); void PageDescToItemSet( const SwPageDesc& rPageDesc, SfxItemSet& rSet); // fill tabs with default tabs diff --git a/sw/source/uibase/uiview/view0.cxx b/sw/source/uibase/uiview/view0.cxx index 0147e3679cba..e88730ce0ba7 100644 --- a/sw/source/uibase/uiview/view0.cxx +++ b/sw/source/uibase/uiview/view0.cxx @@ -360,7 +360,7 @@ void SwView::StateViewOptions(SfxItemSet &rSet) aBool.SetValue( pOpt->IsShowChangesInMargin() ); break; case SID_SPOTLIGHT_CHAR_DF: - aBool.SetValue(m_bIsHighlightCharDF); + aBool.SetValue(m_bIsSpotlightCharDF); break; case SID_SPOTLIGHT_PARASTYLES: aBool.SetValue(m_bIsSpotlightParaStyles); @@ -596,8 +596,8 @@ void SwView::ExecViewOptions(SfxRequest &rReq) case SID_SPOTLIGHT_CHAR_DF: if (STATE_TOGGLE == eState) - bFlag = !m_bIsHighlightCharDF; - m_bIsHighlightCharDF = bFlag; + bFlag = !m_bIsSpotlightCharDF; + m_bIsSpotlightCharDF = bFlag; break; case SID_SPOTLIGHT_PARASTYLES: diff --git a/sw/source/uibase/utlui/uitool.cxx b/sw/source/uibase/utlui/uitool.cxx index e7a79c92f929..5f98464af353 100644 --- a/sw/source/uibase/utlui/uitool.cxx +++ b/sw/source/uibase/utlui/uitool.cxx @@ -225,8 +225,20 @@ void ApplyCharBackground(Color const& rBackgroundColor, model::ComplexColor cons // Fill header footer -static void FillHdFt(SwFrameFormat* pFormat, const SfxItemSet& rSet) +static void FillHdFt(SfxPoolItem& rFormat, const SfxItemSet& rSet, SwPageDesc& rPageDesc, + bool bApplyToAllFormatFrames) { + auto pHeader = dynamic_cast<SwFormatHeader*>(&rFormat); + auto pFooter = dynamic_cast<SwFormatFooter*>(&rFormat); + assert(pHeader || pFooter); + + SwFrameFormat* pFormat; + if (pHeader) + pFormat = pHeader->GetHeaderFormat(); + else + pFormat = pFooter->GetFooterFormat(); + OSL_ENSURE(pFormat != nullptr, "no header or footer format"); + SwAttrSet aSet(pFormat->GetAttrSet()); aSet.Put(rSet); @@ -238,7 +250,14 @@ static void FillHdFt(SwFrameFormat* pFormat, const SfxItemSet& rSet) rSize.GetSize().Width(), rSize.GetSize().Height()); aSet.Put(aFrameSize); - pFormat->SetFormatAttr(aSet); + if (bApplyToAllFormatFrames) + { + // Apply the modified format to all (first, even, odd) of the page style's FrameFormats + aSet.DisableItem(RES_CNTNT); // don't duplicate the content though... + rPageDesc.SetFormatAttrOnAll(aSet, bool(pHeader)); + } + else + pFormat->SetFormatAttr(aSet); } /// Convert from UseOnPage to SvxPageUsage. @@ -273,7 +292,7 @@ static UseOnPage lcl_convertUseFromSvx(SvxPageUsage nUse) // PageDesc <-> convert into sets and back -void ItemSetToPageDesc( const SfxItemSet& rSet, SwPageDesc& rPageDesc ) +void ItemSetToPageDesc(const SfxItemSet& rSet, SwPageDesc& rPageDesc, bool bApplyToAllFormatFrames) { SwFrameFormat& rMaster = rPageDesc.GetMaster(); bool bFirstShare = false; @@ -364,10 +383,7 @@ void ItemSetToPageDesc( const SfxItemSet& rSet, SwPageDesc& rPageDesc ) // Pick out everything and adapt the header format SwFormatHeader aHeaderFormat(rMaster.GetHeader()); - SwFrameFormat *pHeaderFormat = aHeaderFormat.GetHeaderFormat(); - OSL_ENSURE(pHeaderFormat != nullptr, "no header format"); - - ::FillHdFt(pHeaderFormat, rHeaderSet); + ::FillHdFt(aHeaderFormat, rHeaderSet, rPageDesc, bApplyToAllFormatFrames); rPageDesc.ChgHeaderShare(rHeaderSet.Get(SID_ATTR_PAGE_SHARED).GetValue()); rPageDesc.ChgFirstShare(static_cast<const SfxBoolItem&>( @@ -400,10 +416,7 @@ void ItemSetToPageDesc( const SfxItemSet& rSet, SwPageDesc& rPageDesc ) // Pick out everything and adapt the footer format SwFormatFooter aFooterFormat(rMaster.GetFooter()); - SwFrameFormat *pFooterFormat = aFooterFormat.GetFooterFormat(); - OSL_ENSURE(pFooterFormat != nullptr, "no footer format"); - - ::FillHdFt(pFooterFormat, rFooterSet); + ::FillHdFt(aFooterFormat, rFooterSet, rPageDesc, bApplyToAllFormatFrames); rPageDesc.ChgFooterShare(rFooterSet.Get(SID_ATTR_PAGE_SHARED).GetValue()); if (!bFirstShare) diff --git a/toolkit/source/controls/table/cellvalueconversion.cxx b/toolkit/source/controls/table/cellvalueconversion.cxx index 735fb57f7e5d..da9a83346565 100644 --- a/toolkit/source/controls/table/cellvalueconversion.cxx +++ b/toolkit/source/controls/table/cellvalueconversion.cxx @@ -75,6 +75,27 @@ double lcl_convertTimeToDays(tools::Long const i_hours, tools::Long const i_minu //= StandardFormatNormalizer +class StandardFormatNormalizer +{ +public: + virtual ~StandardFormatNormalizer() = default; + + /** converts the given <code>Any</code> into a <code>double</code> value to be fed into a number formatter + */ + virtual double convertToDouble(css::uno::Any const& i_value) const = 0; + + /** returns the format key to be used for formatting values + */ + sal_Int32 getFormatKey() const { return m_nFormatKey; } + +protected: + StandardFormatNormalizer(css::uno::Reference<css::util::XNumberFormatter> const& i_formatter, + ::sal_Int32 const i_numberFormatType); + +private: + ::sal_Int32 m_nFormatKey; +}; + StandardFormatNormalizer::StandardFormatNormalizer(Reference<XNumberFormatter> const& i_formatter, ::sal_Int32 const i_numberFormatType) : m_nFormatKey(0) @@ -278,55 +299,49 @@ bool CellValueConversion::ensureNumberFormatter() return xNumberFormatter.is(); } -bool CellValueConversion::getValueNormalizer(Type const& i_valueType, - std::shared_ptr<StandardFormatNormalizer>& o_formatter) +const StandardFormatNormalizer* CellValueConversion::getValueNormalizer(Type const& i_valueType) { - auto pos = aNormalizers.find(i_valueType.getTypeName()); - if (pos == aNormalizers.end()) + auto [pos, inserted] = aNormalizers.try_emplace(i_valueType.getTypeName()); + if (inserted) { // never encountered this type before - o_formatter.reset(); - OUString const sTypeName(i_valueType.getTypeName()); TypeClass const eTypeClass = i_valueType.getTypeClass(); if (sTypeName == ::cppu::UnoType<DateTime>::get().getTypeName()) { - o_formatter = std::make_shared<DateTimeNormalization>(xNumberFormatter); + pos->second = std::make_unique<DateTimeNormalization>(xNumberFormatter); } else if (sTypeName == ::cppu::UnoType<css::util::Date>::get().getTypeName()) { - o_formatter = std::make_shared<DateNormalization>(xNumberFormatter); + pos->second = std::make_unique<DateNormalization>(xNumberFormatter); } else if (sTypeName == ::cppu::UnoType<css::util::Time>::get().getTypeName()) { - o_formatter = std::make_shared<TimeNormalization>(xNumberFormatter); + pos->second = std::make_unique<TimeNormalization>(xNumberFormatter); } else if (sTypeName == ::cppu::UnoType<sal_Bool>::get().getTypeName()) { - o_formatter = std::make_shared<BooleanNormalization>(xNumberFormatter); + pos->second = std::make_unique<BooleanNormalization>(xNumberFormatter); } else if (sTypeName == ::cppu::UnoType<double>::get().getTypeName() || sTypeName == ::cppu::UnoType<float>::get().getTypeName()) { - o_formatter = std::make_shared<DoubleNormalization>(xNumberFormatter); + pos->second = std::make_unique<DoubleNormalization>(xNumberFormatter); } else if ((eTypeClass == TypeClass_BYTE) || (eTypeClass == TypeClass_SHORT) || (eTypeClass == TypeClass_UNSIGNED_SHORT) || (eTypeClass == TypeClass_LONG) || (eTypeClass == TypeClass_UNSIGNED_LONG) || (eTypeClass == TypeClass_HYPER)) { - o_formatter = std::make_shared<IntegerNormalization>(xNumberFormatter); + pos->second = std::make_unique<IntegerNormalization>(xNumberFormatter); } else { SAL_WARN("svtools.table", "unsupported type '" << sTypeName << "'!"); } - aNormalizers[sTypeName] = o_formatter; } - else - o_formatter = pos->second; - return bool(o_formatter); + return pos->second.get(); } //= CellValueConversion @@ -350,8 +365,7 @@ OUString CellValueConversion::convertToString(const Any& i_value) { if (ensureNumberFormatter()) { - std::shared_ptr<StandardFormatNormalizer> pNormalizer; - if (getValueNormalizer(i_value.getValueType(), pNormalizer)) + if (auto* pNormalizer = getValueNormalizer(i_value.getValueType())) { try { diff --git a/toolkit/source/controls/table/cellvalueconversion.hxx b/toolkit/source/controls/table/cellvalueconversion.hxx index 2e05707e5bab..b50c1ebf098f 100644 --- a/toolkit/source/controls/table/cellvalueconversion.hxx +++ b/toolkit/source/controls/table/cellvalueconversion.hxx @@ -26,26 +26,7 @@ namespace svt { -class StandardFormatNormalizer -{ -public: - /** converts the given <code>Any</code> into a <code>double</code> value to be fed into a number formatter - */ - virtual double convertToDouble(css::uno::Any const& i_value) const = 0; - - /** returns the format key to be used for formatting values - */ - sal_Int32 getFormatKey() const { return m_nFormatKey; } - -protected: - StandardFormatNormalizer(css::uno::Reference<css::util::XNumberFormatter> const& i_formatter, - ::sal_Int32 const i_numberFormatType); - - virtual ~StandardFormatNormalizer() {} - -private: - ::sal_Int32 m_nFormatKey; -}; +class StandardFormatNormalizer; class CellValueConversion { @@ -57,14 +38,11 @@ public: private: bool ensureNumberFormatter(); - bool getValueNormalizer(css::uno::Type const& i_valueType, - std::shared_ptr<StandardFormatNormalizer>& o_formatter); - - typedef std::unordered_map<OUString, std::shared_ptr<StandardFormatNormalizer>> NormalizerCache; + const StandardFormatNormalizer* getValueNormalizer(css::uno::Type const& i_valueType); css::uno::Reference<css::util::XNumberFormatter> xNumberFormatter; bool bAttemptedFormatterCreation; - NormalizerCache aNormalizers; + std::unordered_map<OUString, std::unique_ptr<StandardFormatNormalizer>> aNormalizers; }; } // namespace svt diff --git a/tools/source/datetime/tdate.cxx b/tools/source/datetime/tdate.cxx index 92da1cd3d42a..0b2b25ffc882 100644 --- a/tools/source/datetime/tdate.cxx +++ b/tools/source/datetime/tdate.cxx @@ -72,11 +72,9 @@ sal_Int32 Date::GetAsNormalizedDays() const // This is a very common datum we often calculate from. if (mnDate == 18991230) // 1899-12-30 { -#ifndef NDEBUG - static sal_Int32 nDays = DateToDays( GetDay(), GetMonth(), GetYear()); - assert(nDays == 693594); -#endif - return 693594; + assert(GetDay() == 30 && GetMonth() == 12 && GetYear() == 1899); + constexpr sal_Int32 nullDays = comphelper::date::convertDateToDays(30, 12, 1899); + return nullDays; } // Not calling comphelper::date::convertDateToDaysNormalizing() here just // avoids a second check on null-date handling like above. @@ -378,6 +376,17 @@ void Date::AddDays( sal_Int32 nDays ) *this = lcl_DaysToDate( GetAsNormalizedDays() + nDays ); } +bool Date::CheckedAddDays( sal_Int32 nDays ) +{ + if (nDays == 0) + return true; + sal_Int32 nTotalDays; + if (o3tl::checked_add(GetAsNormalizedDays(), nDays, nTotalDays)) + return false; + *this = lcl_DaysToDate(nTotalDays); + return true; +} + Date& Date::operator ++() { *this = lcl_DaysToDate( GetAsNormalizedDays() + 1 ); diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index c70e84699562..d0ba4f73c12d 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -342,7 +342,6 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/gdi/mapmod \ vcl/source/gdi/metaact \ vcl/source/gdi/oldprintadaptor \ - vcl/source/gdi/pdfbuildin_fonts \ vcl/source/gdi/pdfextoutdevdata \ vcl/source/gdi/pdfwriter \ vcl/source/gdi/pdfwriter_impl2 \ @@ -392,7 +391,6 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/bitmap/bitmappaint \ vcl/source/bitmap/BitmapArithmeticBlendFilter \ vcl/source/bitmap/BitmapShadowFilter \ - vcl/source/bitmap/BitmapAlphaClampFilter \ vcl/source/bitmap/BitmapBasicMorphologyFilter \ vcl/source/bitmap/BitmapDarkenBlendFilter \ vcl/source/bitmap/BitmapLightenBlendFilter \ @@ -568,6 +566,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/pdf/ResourceDict \ vcl/source/pdf/Matrix3 \ vcl/source/pdf/XmpMetadata \ + vcl/source/pdf/pdfbuildin_fonts \ vcl/source/uitest/logger \ vcl/source/uitest/toolboxitemuiobject \ vcl/source/uitest/toolboxuiobject \ diff --git a/vcl/commonfuzzer.mk b/vcl/commonfuzzer.mk index 2d41dc27be24..a1df43ab435d 100644 --- a/vcl/commonfuzzer.mk +++ b/vcl/commonfuzzer.mk @@ -14,7 +14,6 @@ fuzzer_externals = \ orcus-parser \ boost_locale \ boost_filesystem \ - boost_system \ boost_iostreams \ curl \ dtoa \ diff --git a/vcl/inc/accessibility/accessibletablistbox.hxx b/vcl/inc/accessibility/accessibletablistbox.hxx index 59050a82540c..f1678f0c6ab6 100644 --- a/vcl/inc/accessibility/accessibletablistbox.hxx +++ b/vcl/inc/accessibility/accessibletablistbox.hxx @@ -30,10 +30,10 @@ private: public: /** ctor() - @param rxParent XAccessible interface of the parent object. + @param rpParent Accessible parent object. @param rBox The HeaderTabListBox control. */ AccessibleTabListBox( - const css::uno::Reference< css::accessibility::XAccessible >& rxParent, + const rtl::Reference<comphelper::OAccessible>& rpParent, SvHeaderTabListBox& rBox ); // XAccessibleContext ----------------------------------------------------- @@ -45,8 +45,7 @@ public: virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int64 nChildIndex ) override; - css::uno::Reference< css::accessibility::XAccessible > - getHeaderBar() + rtl::Reference<comphelper::OAccessible> getHeaderBar() { return AccessibleBrowseBox::getHeaderBar( AccessibleBrowseBoxObjType::ColumnHeaderBar ); } diff --git a/vcl/inc/graphic/GraphicFormatDetector.hxx b/vcl/inc/graphic/GraphicFormatDetector.hxx index 5940f3e8ea78..19c4e2f7e9b1 100644 --- a/vcl/inc/graphic/GraphicFormatDetector.hxx +++ b/vcl/inc/graphic/GraphicFormatDetector.hxx @@ -169,7 +169,7 @@ public: SAL_DLLPRIVATE bool checkPCD(); bool checkPSD(); bool checkEPS(); - SAL_DLLPRIVATE bool checkDXF(); + bool checkDXF(); SAL_DLLPRIVATE bool checkPCT(); SAL_DLLPRIVATE bool checkPBM(); SAL_DLLPRIVATE bool checkPGM(); diff --git a/vcl/inc/menutogglebutton.hxx b/vcl/inc/menutogglebutton.hxx index d15fbd474fec..ce8e14e9f7ee 100644 --- a/vcl/inc/menutogglebutton.hxx +++ b/vcl/inc/menutogglebutton.hxx @@ -26,9 +26,6 @@ class MenuToggleButton final : public MenuButton public: explicit MenuToggleButton(vcl::Window* pParent, WinBits nStyle); virtual ~MenuToggleButton() override; - - void SetActive(bool bSel); - bool GetActive() const; }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/qt5/QtInstancePopover.hxx b/vcl/inc/qt5/QtInstancePopover.hxx index db928aa4a600..8bcf1dfc865b 100644 --- a/vcl/inc/qt5/QtInstancePopover.hxx +++ b/vcl/inc/qt5/QtInstancePopover.hxx @@ -23,6 +23,8 @@ public: virtual void popdown() override; virtual void resize_to_request() override; + + virtual bool eventFilter(QObject* pObject, QEvent* pEvent) override; }; /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/inc/qt5/QtWidget.hxx b/vcl/inc/qt5/QtWidget.hxx index 5e5fa2dcf6a0..f2af232a1a8d 100644 --- a/vcl/inc/qt5/QtWidget.hxx +++ b/vcl/inc/qt5/QtWidget.hxx @@ -23,6 +23,7 @@ #include <QtWidgets/QGestureEvent> #include <QtWidgets/QWidget> #include <rtl/ustring.hxx> +#include <tools/solar.h> #include <com/sun/star/uno/Reference.hxx> #include <com/sun/star/accessibility/XAccessibleEditableText.hpp> @@ -45,6 +46,8 @@ class QtWidget : public QWidget void commitText(const QString& aText) const; void deleteReplacementText(int nReplacementStart, int nReplacementLength) const; + void retrieveSurrounding(sal_uLong& rPosition, sal_uLong& rAnchor, QString* pText, + QString* pSelection) const; bool handleEvent(QEvent* pEvent); // mouse events are always accepted void handleMouseButtonEvent(const QMouseEvent*) const; diff --git a/vcl/inc/salwtype.hxx b/vcl/inc/salwtype.hxx index 8a61dd73562c..27240fca0baf 100644 --- a/vcl/inc/salwtype.hxx +++ b/vcl/inc/salwtype.hxx @@ -210,6 +210,7 @@ struct SalSurroundingTextRequestEvent OUString maText; // Text sal_uLong mnStart; // The beginning index of selected range sal_uLong mnEnd; // The end index of selected range + sal_uLong mnCursorPos; // The cursor index (either mnStart or mnEnd) }; struct SalSurroundingTextSelectionChangeEvent diff --git a/vcl/inc/skia/gdiimpl.hxx b/vcl/inc/skia/gdiimpl.hxx index ee14d6e86121..8f94d6580c97 100644 --- a/vcl/inc/skia/gdiimpl.hxx +++ b/vcl/inc/skia/gdiimpl.hxx @@ -183,14 +183,11 @@ public: void dump(const char* file) const; // Default blend mode for SkPaint is SkBlendMode::kSrcOver - void drawBitmap(const SalTwoRect& rPosAry, const SkiaSalBitmap& bitmap, - SkBlendMode blendMode = SkBlendMode::kSrcOver); + void drawBitmap(const SalTwoRect& rPosAry, const SkiaSalBitmap& bitmap); - void drawImage(const SalTwoRect& rPosAry, const sk_sp<SkImage>& aImage, int srcScaling = 1, - SkBlendMode eBlendMode = SkBlendMode::kSrcOver); + void drawImage(const SalTwoRect& rPosAry, const sk_sp<SkImage>& aImage, int srcScaling = 1); - void drawShader(const SalTwoRect& rPosAry, const sk_sp<SkShader>& shader, - SkBlendMode blendMode = SkBlendMode::kSrcOver); + void drawShader(const SalTwoRect& rPosAry, const sk_sp<SkShader>& shader); void drawGenericLayout(const GenericSalLayout& layout, Color textColor, const SkFont& font, const SkFont& verticalFont); diff --git a/vcl/inc/window.h b/vcl/inc/window.h index 1c92cbc58c0e..1fd18186ac71 100644 --- a/vcl/inc/window.h +++ b/vcl/inc/window.h @@ -393,6 +393,7 @@ public: mbSecondary:1, mbNonHomogeneous:1, mbDoubleBufferingRequested:1; + bool mbIsFormControl : 1 = false; rtl::Reference< DNDListenerContainer > mxDNDListenerContainer; @@ -437,7 +438,4 @@ bool ImplLOKHandleMouseEvent( const VclPtr<vcl::Window>& xWindow, NotifyEventTyp void ImplHandleResize( vcl::Window* pWindow, tools::Long nNewWidth, tools::Long nNewHeight ); -VCL_DLLPUBLIC css::uno::Reference<css::accessibility::XAccessibleEditableText> -FindFocusedEditableText(css::uno::Reference<css::accessibility::XAccessibleContext> const&); - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/qa/cppunit/BitmapFilterTest.cxx b/vcl/qa/cppunit/BitmapFilterTest.cxx index 4c48d25fd69f..fb27ab59bd99 100644 --- a/vcl/qa/cppunit/BitmapFilterTest.cxx +++ b/vcl/qa/cppunit/BitmapFilterTest.cxx @@ -12,7 +12,6 @@ #include <tools/stream.hxx> #include <vcl/BitmapWriteAccess.hxx> -#include <vcl/bitmap/BitmapAlphaClampFilter.hxx> #include <vcl/bitmap/BitmapArithmeticBlendFilter.hxx> #include <vcl/bitmap/BitmapDarkenBlendFilter.hxx> #include <vcl/bitmap/BitmapLightenBlendFilter.hxx> @@ -41,7 +40,6 @@ public: { } - void testClampAlpha(); void testBlurCorrectness(); void testBasicMorphology(); void testPerformance(); @@ -54,7 +52,6 @@ public: void testArithmeticBlendFilter(); CPPUNIT_TEST_SUITE(BitmapFilterTest); - CPPUNIT_TEST(testClampAlpha); CPPUNIT_TEST(testBlurCorrectness); CPPUNIT_TEST(testBasicMorphology); CPPUNIT_TEST(testPerformance); @@ -92,21 +89,6 @@ private: } }; -void BitmapFilterTest::testClampAlpha() -{ - // Setup test bitmap - Size aSize(1, 1); - Bitmap aBitmap24Bit(aSize, vcl::PixelFormat::N24_BPP); - - { - BitmapScopedWriteAccess aWriteAccess(aBitmap24Bit); - aWriteAccess->Erase(COL_RED); - } - - BitmapFilter::Filter(aBitmap24Bit, BitmapAlphaClampFilter(0x7F)); - CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt8>(0xFF), aBitmap24Bit.GetPixelColor(0, 0).GetAlpha()); -} - void BitmapFilterTest::testBlurCorrectness() { // Setup test bitmap diff --git a/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx b/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx index aeed3e8bdfed..ad4bc9eed33f 100644 --- a/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx +++ b/vcl/qa/cppunit/GraphicFormatDetectorTest.cxx @@ -54,6 +54,8 @@ class GraphicFormatDetectorTest : public test::BootstrapFixtureBase void testDetectWEBP(); void testDetectEMF(); void testDetectEMZ(); + void testDetectDXF(); + void testDetectDXF2(); void testMatchArray(); void testCheckArrayForMatchingStrings(); @@ -80,6 +82,8 @@ class GraphicFormatDetectorTest : public test::BootstrapFixtureBase CPPUNIT_TEST(testDetectWEBP); CPPUNIT_TEST(testDetectEMF); CPPUNIT_TEST(testDetectEMZ); + CPPUNIT_TEST(testDetectDXF); + CPPUNIT_TEST(testDetectDXF2); CPPUNIT_TEST(testMatchArray); CPPUNIT_TEST(testCheckArrayForMatchingStrings); CPPUNIT_TEST_SUITE_END(); @@ -416,6 +420,36 @@ void GraphicFormatDetectorTest::testDetectEMZ() CPPUNIT_ASSERT_EQUAL(u"EMZ"_ustr, rFormatExtension); } +void GraphicFormatDetectorTest::testDetectDXF() +{ + SvFileStream aFileStream(getFullUrl(u"TypeDetectionExample.dxf"), StreamMode::READ); + vcl::GraphicFormatDetector aDetector(aFileStream, u"<DXF"_ustr); + + CPPUNIT_ASSERT(aDetector.detect()); + CPPUNIT_ASSERT(aDetector.checkDXF()); + + aFileStream.Seek(aDetector.mnStreamPosition); + + OUString rFormatExtension; + CPPUNIT_ASSERT(vcl::peekGraphicFormat(aFileStream, rFormatExtension, false)); + CPPUNIT_ASSERT_EQUAL(u"DXF"_ustr, rFormatExtension); +} + +void GraphicFormatDetectorTest::testDetectDXF2() +{ + SvFileStream aFileStream(getFullUrl(u"TypeDetectionExample2.dxf"), StreamMode::READ); + vcl::GraphicFormatDetector aDetector(aFileStream, u"<DXF"_ustr); + + CPPUNIT_ASSERT(aDetector.detect()); + CPPUNIT_ASSERT(aDetector.checkDXF()); + + aFileStream.Seek(aDetector.mnStreamPosition); + + OUString rFormatExtension; + CPPUNIT_ASSERT(vcl::peekGraphicFormat(aFileStream, rFormatExtension, false)); + CPPUNIT_ASSERT_EQUAL(u"DXF"_ustr, rFormatExtension); +} + void GraphicFormatDetectorTest::testMatchArray() { std::string aString("<?xml version=\"1.0\" standalone=\"no\"?>\n" diff --git a/vcl/qa/cppunit/data/TypeDetectionExample.dxf b/vcl/qa/cppunit/data/TypeDetectionExample.dxf new file mode 100644 index 000000000000..29eff8c4564a --- /dev/null +++ b/vcl/qa/cppunit/data/TypeDetectionExample.dxf @@ -0,0 +1,4184 @@ + 0 +SECTION + 2 +ENTITIES + 0 +LINE + 8 +0 + 10 +0.004042 + 20 +-0.091303 + 30 +0.014439 + 11 +0.007453 + 21 +-0.088746 + 31 +0.014536 + 0 +LINE + 8 +0 + 10 +-0.002689 + 20 +-0.096351 + 30 +0.014476 + 11 +0.004042 + 21 +-0.091303 + 31 +0.014439 + 0 +LINE + 8 +0 + 10 +-0.011156 + 20 +-0.102699 + 30 +0.014513 + 11 +-0.002689 + 21 +-0.096351 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +-0.021838 + 20 +-0.110707 + 30 +0.014551 + 11 +-0.011156 + 21 +-0.102699 + 31 +0.014513 + 0 +LINE + 8 +0 + 10 +-0.032129 + 20 +-0.118424 + 30 +0.014513 + 11 +-0.021838 + 21 +-0.110707 + 31 +0.014551 + 0 +LINE + 8 +0 + 10 +-0.042899 + 20 +-0.1265 + 30 +0.014551 + 11 +-0.032129 + 21 +-0.118424 + 31 +0.014513 + 0 +LINE + 8 +0 + 10 +-0.05391 + 20 +-0.134756 + 30 +0.014513 + 11 +-0.042899 + 21 +-0.1265 + 31 +0.014551 + 0 +LINE + 8 +0 + 10 +-0.064888 + 20 +-0.142988 + 30 +0.014476 + 11 +-0.05391 + 21 +-0.134756 + 31 +0.014513 + 0 +LINE + 8 +0 + 10 +-0.076438 + 20 +-0.151647 + 30 +0.014476 + 11 +-0.064888 + 21 +-0.142988 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +-0.085502 + 20 +-0.158444 + 30 +0.014551 + 11 +-0.076438 + 21 +-0.151647 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +-0.093041 + 20 +-0.164098 + 30 +0.014476 + 11 +-0.085502 + 21 +-0.158444 + 31 +0.014551 + 0 +LINE + 8 +0 + 10 +-0.096871 + 20 +-0.166969 + 30 +0.014573 + 11 +-0.093041 + 21 +-0.164098 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +-0.0877 + 20 +-0.158443 + 30 +-0.002693 + 11 +-0.088164 + 21 +-0.159741 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.090123 + 20 +-0.161099 + 30 +0.009188 + 11 +-0.088164 + 21 +-0.159741 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.0877 + 20 +-0.158443 + 30 +-0.002693 + 11 +-0.079942 + 21 +-0.16996 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.090123 + 20 +-0.161099 + 30 +0.009188 + 11 +-0.090789 + 21 +-0.162467 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.091168 + 20 +-0.160927 + 30 +-0.002693 + 11 +-0.090123 + 21 +-0.161099 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.090866 + 20 +-0.159382 + 30 +-0.002693 + 11 +-0.091168 + 21 +-0.160927 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.090123 + 20 +-0.161099 + 30 +0.009188 + 11 +-0.090866 + 21 +-0.159382 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.080451 + 20 +-0.171349 + 30 +0.009188 + 11 +-0.079942 + 21 +-0.16996 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.088164 + 20 +-0.159741 + 30 +0.009188 + 11 +-0.080451 + 21 +-0.171349 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.089492 + 20 +-0.158529 + 30 +-0.002693 + 11 +-0.088164 + 21 +-0.159741 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.0877 + 20 +-0.158443 + 30 +-0.002693 + 11 +-0.089492 + 21 +-0.158529 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.082569 + 20 +-0.172686 + 30 +0.009188 + 11 +-0.084111 + 21 +-0.17261 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.082854 + 20 +-0.17362 + 30 +-0.002693 + 11 +-0.082569 + 21 +-0.172686 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.081266 + 20 +-0.173604 + 30 +-0.002693 + 11 +-0.082854 + 21 +-0.17362 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.082569 + 20 +-0.172686 + 30 +0.009188 + 11 +-0.081266 + 21 +-0.173604 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.080451 + 20 +-0.171349 + 30 +0.009188 + 11 +-0.082569 + 21 +-0.172686 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.080184 + 20 +-0.173003 + 30 +-0.002693 + 11 +-0.080451 + 21 +-0.171349 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.079721 + 20 +-0.171706 + 30 +-0.002693 + 11 +-0.080184 + 21 +-0.173003 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.080451 + 20 +-0.171349 + 30 +0.009188 + 11 +-0.079721 + 21 +-0.171706 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.082569 + 20 +-0.172686 + 30 +0.009188 + 11 +-0.080451 + 21 +-0.171349 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.090123 + 20 +-0.161099 + 30 +0.009188 + 11 +-0.082569 + 21 +-0.172686 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.079721 + 20 +-0.171706 + 30 +-0.002693 + 11 +-0.079942 + 21 +-0.16996 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.080184 + 20 +-0.173003 + 30 +-0.002693 + 11 +-0.079721 + 21 +-0.171706 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.081266 + 20 +-0.173604 + 30 +-0.002693 + 11 +-0.080184 + 21 +-0.173003 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.082854 + 20 +-0.17362 + 30 +-0.002693 + 11 +-0.081266 + 21 +-0.173604 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.084111 + 20 +-0.17261 + 30 +-0.002693 + 11 +-0.082854 + 21 +-0.17362 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.090789 + 20 +-0.162467 + 30 +-0.002693 + 11 +-0.084111 + 21 +-0.17261 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.091168 + 20 +-0.160927 + 30 +-0.002693 + 11 +-0.090789 + 21 +-0.162467 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.090866 + 20 +-0.159382 + 30 +-0.002693 + 11 +-0.091168 + 21 +-0.160927 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.089492 + 20 +-0.158529 + 30 +-0.002693 + 11 +-0.090866 + 21 +-0.159382 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.0877 + 20 +-0.158443 + 30 +-0.002693 + 11 +-0.089492 + 21 +-0.158529 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +0.001362 + 20 +-0.091189 + 30 +-0.001109 + 11 +0.000899 + 21 +-0.092487 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.00106 + 20 +-0.093844 + 30 +0.009188 + 11 +0.000899 + 21 +-0.092487 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +0.001362 + 20 +-0.091189 + 30 +-0.001109 + 11 +0.00912 + 21 +-0.102707 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +-0.00106 + 20 +-0.093844 + 30 +0.009188 + 11 +-0.001727 + 21 +-0.095211 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +-0.002105 + 20 +-0.093672 + 30 +-0.001109 + 11 +-0.00106 + 21 +-0.093844 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.001803 + 20 +-0.092126 + 30 +-0.001109 + 11 +-0.002105 + 21 +-0.093672 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +-0.00106 + 20 +-0.093844 + 30 +0.009188 + 11 +-0.001803 + 21 +-0.092126 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.008612 + 20 +-0.104095 + 30 +0.009188 + 11 +0.00912 + 21 +-0.102707 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.000899 + 20 +-0.092487 + 30 +0.009188 + 11 +0.008612 + 21 +-0.104095 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.000428 + 20 +-0.091274 + 30 +-0.001109 + 11 +0.000899 + 21 +-0.092487 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +0.001362 + 20 +-0.091189 + 30 +-0.001109 + 11 +-0.000428 + 21 +-0.091274 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.006495 + 20 +-0.105431 + 30 +0.009188 + 11 +0.004952 + 21 +-0.105356 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.006208 + 20 +-0.106364 + 30 +-0.001109 + 11 +0.006495 + 21 +-0.105431 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +0.007796 + 20 +-0.106349 + 30 +-0.001109 + 11 +0.006208 + 21 +-0.106364 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.006495 + 20 +-0.105431 + 30 +0.009188 + 11 +0.007796 + 21 +-0.106349 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.008612 + 20 +-0.104095 + 30 +0.009188 + 11 +0.006495 + 21 +-0.105431 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +0.008879 + 20 +-0.105748 + 30 +-0.001109 + 11 +0.008612 + 21 +-0.104095 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +0.009342 + 20 +-0.10445 + 30 +-0.001109 + 11 +0.008879 + 21 +-0.105748 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.008612 + 20 +-0.104095 + 30 +0.009188 + 11 +0.009342 + 21 +-0.10445 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.006495 + 20 +-0.105431 + 30 +0.009188 + 11 +0.008612 + 21 +-0.104095 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.00106 + 20 +-0.093844 + 30 +0.009188 + 11 +0.006495 + 21 +-0.105431 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +0.009342 + 20 +-0.10445 + 30 +-0.001109 + 11 +0.00912 + 21 +-0.102707 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.008879 + 20 +-0.105748 + 30 +-0.001109 + 11 +0.009342 + 21 +-0.10445 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.007796 + 20 +-0.106349 + 30 +-0.001109 + 11 +0.008879 + 21 +-0.105748 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.006208 + 20 +-0.106364 + 30 +-0.001109 + 11 +0.007796 + 21 +-0.106349 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.004952 + 20 +-0.105356 + 30 +-0.001109 + 11 +0.006208 + 21 +-0.106364 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +-0.001727 + 20 +-0.095211 + 30 +-0.001109 + 11 +0.004952 + 21 +-0.105356 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +-0.002105 + 20 +-0.093672 + 30 +-0.001109 + 11 +-0.001727 + 21 +-0.095211 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +-0.001803 + 20 +-0.092126 + 30 +-0.001109 + 11 +-0.002105 + 21 +-0.093672 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +-0.000428 + 20 +-0.091274 + 30 +-0.001109 + 11 +-0.001803 + 21 +-0.092126 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.001362 + 20 +-0.091189 + 30 +-0.001109 + 11 +-0.000428 + 21 +-0.091274 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.011409 + 20 +-0.102736 + 30 +0.014391 + 11 +0.01482 + 21 +-0.100179 + 31 +0.014489 + 0 +LINE + 8 +0 + 10 +0.004678 + 20 +-0.107783 + 30 +0.01443 + 11 +0.011409 + 21 +-0.102736 + 31 +0.014391 + 0 +LINE + 8 +0 + 10 +-0.003789 + 20 +-0.114132 + 30 +0.014466 + 11 +0.004678 + 21 +-0.107783 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +-0.014469 + 20 +-0.12214 + 30 +0.014503 + 11 +-0.003789 + 21 +-0.114132 + 31 +0.014466 + 0 +LINE + 8 +0 + 10 +-0.024762 + 20 +-0.129857 + 30 +0.014466 + 11 +-0.014469 + 21 +-0.12214 + 31 +0.014503 + 0 +LINE + 8 +0 + 10 +-0.035532 + 20 +-0.137933 + 30 +0.014503 + 11 +-0.024762 + 21 +-0.129857 + 31 +0.014466 + 0 +LINE + 8 +0 + 10 +-0.046541 + 20 +-0.146189 + 30 +0.014466 + 11 +-0.035532 + 21 +-0.137933 + 31 +0.014503 + 0 +LINE + 8 +0 + 10 +-0.057521 + 20 +-0.154422 + 30 +0.01443 + 11 +-0.046541 + 21 +-0.146189 + 31 +0.014466 + 0 +LINE + 8 +0 + 10 +-0.069069 + 20 +-0.16308 + 30 +0.01443 + 11 +-0.057521 + 21 +-0.154422 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +-0.078135 + 20 +-0.169877 + 30 +0.014503 + 11 +-0.069069 + 21 +-0.16308 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +-0.085674 + 20 +-0.175531 + 30 +0.01443 + 11 +-0.078135 + 21 +-0.169877 + 31 +0.014503 + 0 +LINE + 8 +0 + 10 +-0.089503 + 20 +-0.178402 + 30 +0.014527 + 11 +-0.085674 + 21 +-0.175531 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +0.001416 + 20 +-0.110228 + 30 +0.025254 + 11 +0.000781 + 21 +-0.093749 + 31 +0.020553 + 0 +LINE + 8 +0 + 10 +-0.016452 + 20 +-0.106669 + 30 +0.031317 + 11 +0.001416 + 21 +-0.110228 + 31 +0.025254 + 0 +LINE + 8 +0 + 10 +-0.019645 + 20 +-0.126021 + 30 +0.03542 + 11 +-0.016452 + 21 +-0.106669 + 31 +0.031317 + 0 +LINE + 8 +0 + 10 +-0.037035 + 20 +-0.122103 + 30 +0.037298 + 11 +-0.019645 + 21 +-0.126021 + 31 +0.03542 + 0 +LINE + 8 +0 + 10 +-0.041186 + 20 +-0.142173 + 30 +0.037214 + 11 +-0.037035 + 21 +-0.122103 + 31 +0.037298 + 0 +LINE + 8 +0 + 10 +-0.059055 + 20 +-0.138615 + 30 +0.035504 + 11 +-0.041186 + 21 +-0.142173 + 31 +0.037214 + 0 +LINE + 8 +0 + 10 +-0.063205 + 20 +-0.158683 + 30 +0.031234 + 11 +-0.059055 + 21 +-0.138615 + 31 +0.035504 + 0 +LINE + 8 +0 + 10 +-0.081553 + 20 +-0.155484 + 30 +0.025337 + 11 +-0.063205 + 21 +-0.158683 + 31 +0.031234 + 0 +LINE + 8 +0 + 10 +-0.081875 + 20 +-0.172682 + 30 +0.02047 + 11 +-0.081553 + 21 +-0.155484 + 31 +0.025337 + 0 +LINE + 8 +0 + 10 +-0.089242 + 20 +-0.161249 + 30 +0.020516 + 11 +-0.081875 + 21 +-0.172682 + 31 +0.02047 + 0 +LINE + 8 +0 + 10 +-0.074186 + 20 +-0.166916 + 30 +0.025291 + 11 +-0.089242 + 21 +-0.161249 + 31 +0.020516 + 0 +LINE + 8 +0 + 10 +-0.070574 + 20 +-0.14725 + 30 +0.03128 + 11 +-0.074186 + 21 +-0.166916 + 31 +0.025291 + 0 +LINE + 8 +0 + 10 +-0.051688 + 20 +-0.150048 + 30 +0.035457 + 11 +-0.070574 + 21 +-0.14725 + 31 +0.03128 + 0 +LINE + 8 +0 + 10 +-0.048554 + 20 +-0.13074 + 30 +0.03726 + 11 +-0.051688 + 21 +-0.150048 + 31 +0.035457 + 0 +LINE + 8 +0 + 10 +-0.029668 + 20 +-0.133536 + 30 +0.037251 + 11 +-0.048554 + 21 +-0.13074 + 31 +0.03726 + 0 +LINE + 8 +0 + 10 +-0.027012 + 20 +-0.114588 + 30 +0.035466 + 11 +-0.029668 + 21 +-0.133536 + 31 +0.037251 + 0 +LINE + 8 +0 + 10 +-0.009085 + 20 +-0.118102 + 30 +0.031271 + 11 +-0.027012 + 21 +-0.114588 + 31 +0.035466 + 0 +LINE + 8 +0 + 10 +-0.005951 + 20 +-0.098795 + 30 +0.0253 + 11 +-0.009085 + 21 +-0.118102 + 31 +0.031271 + 0 +LINE + 8 +0 + 10 +0.008149 + 20 +-0.105182 + 30 +0.020507 + 11 +-0.005951 + 21 +-0.098795 + 31 +0.0253 + 0 +LINE + 8 +0 + 10 +0.000781 + 20 +-0.093749 + 30 +0.020553 + 11 +0.008149 + 21 +-0.105182 + 31 +0.020507 + 0 +LINE + 8 +0 + 10 +0.004042 + 20 +-0.091303 + 30 +0.014439 + 11 +0.003952 + 21 +-0.09137 + 31 +0.009355 + 0 +LINE + 8 +0 + 10 +-0.002689 + 20 +-0.096351 + 30 +0.014476 + 11 +-0.00266 + 21 +-0.096328 + 31 +0.009541 + 0 +LINE + 8 +0 + 10 +-0.011156 + 20 +-0.102699 + 30 +0.014513 + 11 +-0.011186 + 21 +-0.102721 + 31 +0.009355 + 0 +LINE + 8 +0 + 10 +-0.021838 + 20 +-0.110707 + 30 +0.014551 + 11 +-0.021867 + 21 +-0.11073 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.032129 + 20 +-0.118424 + 30 +0.014513 + 11 +-0.032129 + 21 +-0.118424 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.042899 + 20 +-0.1265 + 30 +0.014551 + 11 +-0.042929 + 21 +-0.126523 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.05391 + 20 +-0.134756 + 30 +0.014513 + 11 +-0.053969 + 21 +-0.134801 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.064888 + 20 +-0.142988 + 30 +0.014476 + 11 +-0.064919 + 21 +-0.14301 + 31 +0.009355 + 0 +LINE + 8 +0 + 10 +-0.076438 + 20 +-0.151647 + 30 +0.014476 + 11 +-0.076438 + 21 +-0.151647 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.085502 + 20 +-0.158444 + 30 +0.014551 + 11 +-0.085533 + 21 +-0.158467 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.093041 + 20 +-0.164098 + 30 +0.014476 + 11 +-0.093012 + 21 +-0.164076 + 31 +0.009541 + 0 +LINE + 8 +0 + 10 +-0.089242 + 20 +-0.161249 + 30 +0.020516 + 11 +-0.089242 + 21 +-0.161249 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.081553 + 20 +-0.155484 + 30 +0.025337 + 11 +-0.081553 + 21 +-0.155484 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.070574 + 20 +-0.14725 + 30 +0.03128 + 11 +-0.070603 + 21 +-0.147273 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.059055 + 20 +-0.138615 + 30 +0.035504 + 11 +-0.059115 + 21 +-0.138659 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.048554 + 20 +-0.13074 + 30 +0.03726 + 11 +-0.048554 + 21 +-0.13074 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.037035 + 20 +-0.122103 + 30 +0.037298 + 11 +-0.037095 + 21 +-0.122148 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.027012 + 20 +-0.114588 + 30 +0.035466 + 11 +-0.027043 + 21 +-0.114611 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.016452 + 20 +-0.106669 + 30 +0.031317 + 11 +-0.016482 + 21 +-0.106692 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.005951 + 20 +-0.098795 + 30 +0.0253 + 11 +-0.00592 + 21 +-0.098773 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +0.000781 + 20 +-0.093749 + 30 +0.020553 + 11 +0.000721 + 21 +-0.093794 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.093041 + 20 +-0.164098 + 30 +0.014476 + 11 +-0.096931 + 21 +-0.167014 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.089242 + 20 +-0.161249 + 30 +0.00943 + 11 +-0.093041 + 21 +-0.164098 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +-0.085502 + 20 +-0.158444 + 30 +0.014551 + 11 +-0.089242 + 21 +-0.161249 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.081553 + 20 +-0.155484 + 30 +0.009392 + 11 +-0.085502 + 21 +-0.158444 + 31 +0.014551 + 0 +LINE + 8 +0 + 10 +-0.076438 + 20 +-0.151647 + 30 +0.014476 + 11 +-0.081553 + 21 +-0.155484 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.070603 + 20 +-0.147273 + 30 +0.00943 + 11 +-0.076438 + 21 +-0.151647 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +-0.064888 + 20 +-0.142988 + 30 +0.014476 + 11 +-0.070603 + 21 +-0.147273 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.059115 + 20 +-0.138659 + 30 +0.009392 + 11 +-0.064888 + 21 +-0.142988 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +-0.05391 + 20 +-0.134756 + 30 +0.014513 + 11 +-0.059115 + 21 +-0.138659 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.048554 + 20 +-0.13074 + 30 +0.00943 + 11 +-0.05391 + 21 +-0.134756 + 31 +0.014513 + 0 +LINE + 8 +0 + 10 +-0.042899 + 20 +-0.1265 + 30 +0.014551 + 11 +-0.048554 + 21 +-0.13074 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.037095 + 20 +-0.122148 + 30 +0.009392 + 11 +-0.042899 + 21 +-0.1265 + 31 +0.014551 + 0 +LINE + 8 +0 + 10 +-0.032129 + 20 +-0.118424 + 30 +0.014513 + 11 +-0.037095 + 21 +-0.122148 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.027043 + 20 +-0.114611 + 30 +0.00943 + 11 +-0.032129 + 21 +-0.118424 + 31 +0.014513 + 0 +LINE + 8 +0 + 10 +-0.021838 + 20 +-0.110707 + 30 +0.014551 + 11 +-0.027043 + 21 +-0.114611 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.016482 + 20 +-0.106692 + 30 +0.009392 + 11 +-0.021838 + 21 +-0.110707 + 31 +0.014551 + 0 +LINE + 8 +0 + 10 +-0.011156 + 20 +-0.102699 + 30 +0.014513 + 11 +-0.016482 + 21 +-0.106692 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.00592 + 20 +-0.098773 + 30 +0.00943 + 11 +-0.011156 + 21 +-0.102699 + 31 +0.014513 + 0 +LINE + 8 +0 + 10 +-0.002689 + 20 +-0.096351 + 30 +0.014476 + 11 +-0.00592 + 21 +-0.098773 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +0.000721 + 20 +-0.093794 + 30 +0.009392 + 11 +-0.002689 + 21 +-0.096351 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +0.004042 + 20 +-0.091303 + 30 +0.014439 + 11 +0.000721 + 21 +-0.093794 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +0.007512 + 20 +-0.088701 + 30 +0.00943 + 11 +0.004042 + 21 +-0.091303 + 31 +0.014439 + 0 +LINE + 8 +0 + 10 +0.007453 + 20 +-0.088746 + 30 +0.014536 + 11 +0.007512 + 21 +-0.088701 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +0.003952 + 20 +-0.09137 + 30 +0.009355 + 11 +0.007512 + 21 +-0.088701 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +0.000721 + 20 +-0.093794 + 30 +0.009392 + 11 +0.003952 + 21 +-0.09137 + 31 +0.009355 + 0 +LINE + 8 +0 + 10 +-0.00266 + 20 +-0.096328 + 30 +0.009541 + 11 +0.000721 + 21 +-0.093794 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.00592 + 20 +-0.098773 + 30 +0.00943 + 11 +-0.00266 + 21 +-0.096328 + 31 +0.009541 + 0 +LINE + 8 +0 + 10 +-0.011186 + 20 +-0.102721 + 30 +0.009355 + 11 +-0.00592 + 21 +-0.098773 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.016482 + 20 +-0.106692 + 30 +0.009392 + 11 +-0.011186 + 21 +-0.102721 + 31 +0.009355 + 0 +LINE + 8 +0 + 10 +-0.021867 + 20 +-0.11073 + 30 +0.009392 + 11 +-0.016482 + 21 +-0.106692 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.027043 + 20 +-0.114611 + 30 +0.00943 + 11 +-0.021867 + 21 +-0.11073 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.032129 + 20 +-0.118424 + 30 +0.009392 + 11 +-0.027043 + 21 +-0.114611 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.037095 + 20 +-0.122148 + 30 +0.009392 + 11 +-0.032129 + 21 +-0.118424 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.042929 + 20 +-0.126523 + 30 +0.009392 + 11 +-0.037095 + 21 +-0.122148 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.048554 + 20 +-0.13074 + 30 +0.00943 + 11 +-0.042929 + 21 +-0.126523 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.053969 + 20 +-0.134801 + 30 +0.009392 + 11 +-0.048554 + 21 +-0.13074 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.059115 + 20 +-0.138659 + 30 +0.009392 + 11 +-0.053969 + 21 +-0.134801 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.064919 + 20 +-0.14301 + 30 +0.009355 + 11 +-0.059115 + 21 +-0.138659 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.070603 + 20 +-0.147273 + 30 +0.00943 + 11 +-0.064919 + 21 +-0.14301 + 31 +0.009355 + 0 +LINE + 8 +0 + 10 +-0.076438 + 20 +-0.151647 + 30 +0.00943 + 11 +-0.070603 + 21 +-0.147273 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.081553 + 20 +-0.155484 + 30 +0.009392 + 11 +-0.076438 + 21 +-0.151647 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.085533 + 20 +-0.158467 + 30 +0.009392 + 11 +-0.081553 + 21 +-0.155484 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.089242 + 20 +-0.161249 + 30 +0.00943 + 11 +-0.085533 + 21 +-0.158467 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.093012 + 20 +-0.164076 + 30 +0.009541 + 11 +-0.089242 + 21 +-0.161249 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.096931 + 20 +-0.167014 + 30 +0.009392 + 11 +-0.093012 + 21 +-0.164076 + 31 +0.009541 + 0 +LINE + 8 +0 + 10 +-0.096871 + 20 +-0.166969 + 30 +0.014573 + 11 +-0.096931 + 21 +-0.167014 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.089242 + 20 +-0.161249 + 30 +0.020516 + 11 +-0.096871 + 21 +-0.166969 + 31 +0.014573 + 0 +LINE + 8 +0 + 10 +-0.081553 + 20 +-0.155484 + 30 +0.025337 + 11 +-0.089242 + 21 +-0.161249 + 31 +0.020516 + 0 +LINE + 8 +0 + 10 +-0.070574 + 20 +-0.14725 + 30 +0.03128 + 11 +-0.081553 + 21 +-0.155484 + 31 +0.025337 + 0 +LINE + 8 +0 + 10 +-0.059055 + 20 +-0.138615 + 30 +0.035504 + 11 +-0.070574 + 21 +-0.14725 + 31 +0.03128 + 0 +LINE + 8 +0 + 10 +-0.048554 + 20 +-0.13074 + 30 +0.03726 + 11 +-0.059055 + 21 +-0.138615 + 31 +0.035504 + 0 +LINE + 8 +0 + 10 +-0.037035 + 20 +-0.122103 + 30 +0.037298 + 11 +-0.048554 + 21 +-0.13074 + 31 +0.03726 + 0 +LINE + 8 +0 + 10 +-0.027012 + 20 +-0.114588 + 30 +0.035466 + 11 +-0.037035 + 21 +-0.122103 + 31 +0.037298 + 0 +LINE + 8 +0 + 10 +-0.016452 + 20 +-0.106669 + 30 +0.031317 + 11 +-0.027012 + 21 +-0.114588 + 31 +0.035466 + 0 +LINE + 8 +0 + 10 +-0.005951 + 20 +-0.098795 + 30 +0.0253 + 11 +-0.016452 + 21 +-0.106669 + 31 +0.031317 + 0 +LINE + 8 +0 + 10 +0.000781 + 20 +-0.093749 + 30 +0.020553 + 11 +-0.005951 + 21 +-0.098795 + 31 +0.0253 + 0 +LINE + 8 +0 + 10 +0.007453 + 20 +-0.088746 + 30 +0.014536 + 11 +0.000781 + 21 +-0.093749 + 31 +0.020553 + 0 +LINE + 8 +0 + 10 +0.011409 + 20 +-0.102736 + 30 +0.014391 + 11 +0.011319 + 21 +-0.102803 + 31 +0.009309 + 0 +LINE + 8 +0 + 10 +0.004678 + 20 +-0.107783 + 30 +0.01443 + 11 +0.004709 + 21 +-0.107761 + 31 +0.009495 + 0 +LINE + 8 +0 + 10 +-0.003789 + 20 +-0.114132 + 30 +0.014466 + 11 +-0.003819 + 21 +-0.114154 + 31 +0.009309 + 0 +LINE + 8 +0 + 10 +-0.014469 + 20 +-0.12214 + 30 +0.014503 + 11 +-0.014499 + 21 +-0.122163 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.024762 + 20 +-0.129857 + 30 +0.014466 + 11 +-0.024762 + 21 +-0.129857 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.035532 + 20 +-0.137933 + 30 +0.014503 + 11 +-0.035561 + 21 +-0.137956 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.046541 + 20 +-0.146189 + 30 +0.014466 + 11 +-0.046602 + 21 +-0.146234 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.057521 + 20 +-0.154422 + 30 +0.01443 + 11 +-0.057552 + 21 +-0.154445 + 31 +0.009309 + 0 +LINE + 8 +0 + 10 +-0.069069 + 20 +-0.16308 + 30 +0.01443 + 11 +-0.069069 + 21 +-0.16308 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.078135 + 20 +-0.169877 + 30 +0.014503 + 11 +-0.078164 + 21 +-0.1699 + 31 +0.014573 + 0 +LINE + 8 +0 + 10 +-0.085674 + 20 +-0.175531 + 30 +0.01443 + 11 +-0.085644 + 21 +-0.175509 + 31 +0.009495 + 0 +LINE + 8 +0 + 10 +-0.081875 + 20 +-0.172682 + 30 +0.02047 + 11 +-0.081875 + 21 +-0.172682 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.074186 + 20 +-0.166916 + 30 +0.025291 + 11 +-0.074186 + 21 +-0.166916 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.063205 + 20 +-0.158683 + 30 +0.031234 + 11 +-0.063236 + 21 +-0.158706 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.051688 + 20 +-0.150048 + 30 +0.035457 + 11 +-0.051747 + 21 +-0.150092 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.041186 + 20 +-0.142173 + 30 +0.037214 + 11 +-0.041186 + 21 +-0.142173 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.029668 + 20 +-0.133536 + 30 +0.037251 + 11 +-0.029728 + 21 +-0.133581 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.019645 + 20 +-0.126021 + 30 +0.03542 + 11 +-0.019675 + 21 +-0.126044 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.009085 + 20 +-0.118102 + 30 +0.031271 + 11 +-0.009114 + 21 +-0.118125 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +0.001416 + 20 +-0.110228 + 30 +0.025254 + 11 +0.001447 + 21 +-0.110206 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +0.008149 + 20 +-0.105182 + 30 +0.020507 + 11 +0.008089 + 21 +-0.105227 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.085674 + 20 +-0.175531 + 30 +0.01443 + 11 +-0.089563 + 21 +-0.178447 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.081875 + 20 +-0.172682 + 30 +0.009383 + 11 +-0.085674 + 21 +-0.175531 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +-0.078135 + 20 +-0.169877 + 30 +0.014503 + 11 +-0.081875 + 21 +-0.172682 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.074186 + 20 +-0.166916 + 30 +0.009346 + 11 +-0.078135 + 21 +-0.169877 + 31 +0.014503 + 0 +LINE + 8 +0 + 10 +-0.069069 + 20 +-0.16308 + 30 +0.01443 + 11 +-0.074186 + 21 +-0.166916 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.063236 + 20 +-0.158706 + 30 +0.009383 + 11 +-0.069069 + 21 +-0.16308 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +-0.057521 + 20 +-0.154422 + 30 +0.01443 + 11 +-0.063236 + 21 +-0.158706 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.051747 + 20 +-0.150092 + 30 +0.009346 + 11 +-0.057521 + 21 +-0.154422 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +-0.046541 + 20 +-0.146189 + 30 +0.014466 + 11 +-0.051747 + 21 +-0.150092 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.041186 + 20 +-0.142173 + 30 +0.009383 + 11 +-0.046541 + 21 +-0.146189 + 31 +0.014466 + 0 +LINE + 8 +0 + 10 +-0.035532 + 20 +-0.137933 + 30 +0.014503 + 11 +-0.041186 + 21 +-0.142173 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.029728 + 20 +-0.133581 + 30 +0.009346 + 11 +-0.035532 + 21 +-0.137933 + 31 +0.014503 + 0 +LINE + 8 +0 + 10 +-0.024762 + 20 +-0.129857 + 30 +0.014466 + 11 +-0.029728 + 21 +-0.133581 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.019675 + 20 +-0.126044 + 30 +0.009383 + 11 +-0.024762 + 21 +-0.129857 + 31 +0.014466 + 0 +LINE + 8 +0 + 10 +-0.014469 + 20 +-0.12214 + 30 +0.014503 + 11 +-0.019675 + 21 +-0.126044 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.009114 + 20 +-0.118125 + 30 +0.009346 + 11 +-0.014469 + 21 +-0.12214 + 31 +0.014503 + 0 +LINE + 8 +0 + 10 +-0.003789 + 20 +-0.114132 + 30 +0.014466 + 11 +-0.009114 + 21 +-0.118125 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +0.001447 + 20 +-0.110206 + 30 +0.009383 + 11 +-0.003789 + 21 +-0.114132 + 31 +0.014466 + 0 +LINE + 8 +0 + 10 +0.004678 + 20 +-0.107783 + 30 +0.01443 + 11 +0.001447 + 21 +-0.110206 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +0.008089 + 20 +-0.105227 + 30 +0.009346 + 11 +0.004678 + 21 +-0.107783 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +0.011409 + 20 +-0.102736 + 30 +0.014391 + 11 +0.008089 + 21 +-0.105227 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +0.01488 + 20 +-0.100134 + 30 +0.009383 + 11 +0.011409 + 21 +-0.102736 + 31 +0.014391 + 0 +LINE + 8 +0 + 10 +0.01482 + 20 +-0.100179 + 30 +0.014489 + 11 +0.01488 + 21 +-0.100134 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +0.011319 + 20 +-0.102803 + 30 +0.009309 + 11 +0.01488 + 21 +-0.100134 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +0.008089 + 20 +-0.105227 + 30 +0.009346 + 11 +0.011319 + 21 +-0.102803 + 31 +0.009309 + 0 +LINE + 8 +0 + 10 +0.004709 + 20 +-0.107761 + 30 +0.009495 + 11 +0.008089 + 21 +-0.105227 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +0.001447 + 20 +-0.110206 + 30 +0.009383 + 11 +0.004709 + 21 +-0.107761 + 31 +0.009495 + 0 +LINE + 8 +0 + 10 +-0.003819 + 20 +-0.114154 + 30 +0.009309 + 11 +0.001447 + 21 +-0.110206 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.009114 + 20 +-0.118125 + 30 +0.009346 + 11 +-0.003819 + 21 +-0.114154 + 31 +0.009309 + 0 +LINE + 8 +0 + 10 +-0.014499 + 20 +-0.122163 + 30 +0.009346 + 11 +-0.009114 + 21 +-0.118125 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.019675 + 20 +-0.126044 + 30 +0.009383 + 11 +-0.014499 + 21 +-0.122163 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.024762 + 20 +-0.129857 + 30 +0.009346 + 11 +-0.019675 + 21 +-0.126044 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.029728 + 20 +-0.133581 + 30 +0.009346 + 11 +-0.024762 + 21 +-0.129857 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.035561 + 20 +-0.137956 + 30 +0.009346 + 11 +-0.029728 + 21 +-0.133581 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.041186 + 20 +-0.142173 + 30 +0.009383 + 11 +-0.035561 + 21 +-0.137956 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.046602 + 20 +-0.146234 + 30 +0.009346 + 11 +-0.041186 + 21 +-0.142173 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.051747 + 20 +-0.150092 + 30 +0.009346 + 11 +-0.046602 + 21 +-0.146234 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.057552 + 20 +-0.154445 + 30 +0.009309 + 11 +-0.051747 + 21 +-0.150092 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.063236 + 20 +-0.158706 + 30 +0.009383 + 11 +-0.057552 + 21 +-0.154445 + 31 +0.009309 + 0 +LINE + 8 +0 + 10 +-0.069069 + 20 +-0.16308 + 30 +0.009383 + 11 +-0.063236 + 21 +-0.158706 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.074186 + 20 +-0.166916 + 30 +0.009346 + 11 +-0.069069 + 21 +-0.16308 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.081875 + 20 +-0.172682 + 30 +0.009383 + 11 +-0.074186 + 21 +-0.166916 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.085644 + 20 +-0.175509 + 30 +0.009495 + 11 +-0.081875 + 21 +-0.172682 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.089563 + 20 +-0.178447 + 30 +0.009346 + 11 +-0.085644 + 21 +-0.175509 + 31 +0.009495 + 0 +LINE + 8 +0 + 10 +-0.089503 + 20 +-0.178402 + 30 +0.014527 + 11 +-0.089563 + 21 +-0.178447 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.081875 + 20 +-0.172682 + 30 +0.02047 + 11 +-0.089503 + 21 +-0.178402 + 31 +0.014527 + 0 +LINE + 8 +0 + 10 +-0.074186 + 20 +-0.166916 + 30 +0.025291 + 11 +-0.081875 + 21 +-0.172682 + 31 +0.02047 + 0 +LINE + 8 +0 + 10 +-0.063205 + 20 +-0.158683 + 30 +0.031234 + 11 +-0.074186 + 21 +-0.166916 + 31 +0.025291 + 0 +LINE + 8 +0 + 10 +-0.051688 + 20 +-0.150048 + 30 +0.035457 + 11 +-0.063205 + 21 +-0.158683 + 31 +0.031234 + 0 +LINE + 8 +0 + 10 +-0.041186 + 20 +-0.142173 + 30 +0.037214 + 11 +-0.051688 + 21 +-0.150048 + 31 +0.035457 + 0 +LINE + 8 +0 + 10 +-0.029668 + 20 +-0.133536 + 30 +0.037251 + 11 +-0.041186 + 21 +-0.142173 + 31 +0.037214 + 0 +LINE + 8 +0 + 10 +-0.019645 + 20 +-0.126021 + 30 +0.03542 + 11 +-0.029668 + 21 +-0.133536 + 31 +0.037251 + 0 +LINE + 8 +0 + 10 +-0.009085 + 20 +-0.118102 + 30 +0.031271 + 11 +-0.019645 + 21 +-0.126021 + 31 +0.03542 + 0 +LINE + 8 +0 + 10 +0.001416 + 20 +-0.110228 + 30 +0.025254 + 11 +-0.009085 + 21 +-0.118102 + 31 +0.031271 + 0 +LINE + 8 +0 + 10 +0.008149 + 20 +-0.105182 + 30 +0.020507 + 11 +0.001416 + 21 +-0.110228 + 31 +0.025254 + 0 +LINE + 8 +0 + 10 +0.01482 + 20 +-0.100179 + 30 +0.014489 + 11 +0.008149 + 21 +-0.105182 + 31 +0.020507 + 0 +ENDSEC + 0 +EOF diff --git a/vcl/qa/cppunit/data/TypeDetectionExample2.dxf b/vcl/qa/cppunit/data/TypeDetectionExample2.dxf new file mode 100644 index 000000000000..dd278f6de009 --- /dev/null +++ b/vcl/qa/cppunit/data/TypeDetectionExample2.dxf @@ -0,0 +1,4186 @@ +999 +DXF data, created by potrace 1.16, written by Peter Selinger 2001-2019 + 0 +SECTION + 2 +ENTITIES + 0 +LINE + 8 +0 + 10 +0.004042 + 20 +-0.091303 + 30 +0.014439 + 11 +0.007453 + 21 +-0.088746 + 31 +0.014536 + 0 +LINE + 8 +0 + 10 +-0.002689 + 20 +-0.096351 + 30 +0.014476 + 11 +0.004042 + 21 +-0.091303 + 31 +0.014439 + 0 +LINE + 8 +0 + 10 +-0.011156 + 20 +-0.102699 + 30 +0.014513 + 11 +-0.002689 + 21 +-0.096351 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +-0.021838 + 20 +-0.110707 + 30 +0.014551 + 11 +-0.011156 + 21 +-0.102699 + 31 +0.014513 + 0 +LINE + 8 +0 + 10 +-0.032129 + 20 +-0.118424 + 30 +0.014513 + 11 +-0.021838 + 21 +-0.110707 + 31 +0.014551 + 0 +LINE + 8 +0 + 10 +-0.042899 + 20 +-0.1265 + 30 +0.014551 + 11 +-0.032129 + 21 +-0.118424 + 31 +0.014513 + 0 +LINE + 8 +0 + 10 +-0.05391 + 20 +-0.134756 + 30 +0.014513 + 11 +-0.042899 + 21 +-0.1265 + 31 +0.014551 + 0 +LINE + 8 +0 + 10 +-0.064888 + 20 +-0.142988 + 30 +0.014476 + 11 +-0.05391 + 21 +-0.134756 + 31 +0.014513 + 0 +LINE + 8 +0 + 10 +-0.076438 + 20 +-0.151647 + 30 +0.014476 + 11 +-0.064888 + 21 +-0.142988 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +-0.085502 + 20 +-0.158444 + 30 +0.014551 + 11 +-0.076438 + 21 +-0.151647 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +-0.093041 + 20 +-0.164098 + 30 +0.014476 + 11 +-0.085502 + 21 +-0.158444 + 31 +0.014551 + 0 +LINE + 8 +0 + 10 +-0.096871 + 20 +-0.166969 + 30 +0.014573 + 11 +-0.093041 + 21 +-0.164098 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +-0.0877 + 20 +-0.158443 + 30 +-0.002693 + 11 +-0.088164 + 21 +-0.159741 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.090123 + 20 +-0.161099 + 30 +0.009188 + 11 +-0.088164 + 21 +-0.159741 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.0877 + 20 +-0.158443 + 30 +-0.002693 + 11 +-0.079942 + 21 +-0.16996 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.090123 + 20 +-0.161099 + 30 +0.009188 + 11 +-0.090789 + 21 +-0.162467 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.091168 + 20 +-0.160927 + 30 +-0.002693 + 11 +-0.090123 + 21 +-0.161099 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.090866 + 20 +-0.159382 + 30 +-0.002693 + 11 +-0.091168 + 21 +-0.160927 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.090123 + 20 +-0.161099 + 30 +0.009188 + 11 +-0.090866 + 21 +-0.159382 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.080451 + 20 +-0.171349 + 30 +0.009188 + 11 +-0.079942 + 21 +-0.16996 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.088164 + 20 +-0.159741 + 30 +0.009188 + 11 +-0.080451 + 21 +-0.171349 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.089492 + 20 +-0.158529 + 30 +-0.002693 + 11 +-0.088164 + 21 +-0.159741 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.0877 + 20 +-0.158443 + 30 +-0.002693 + 11 +-0.089492 + 21 +-0.158529 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.082569 + 20 +-0.172686 + 30 +0.009188 + 11 +-0.084111 + 21 +-0.17261 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.082854 + 20 +-0.17362 + 30 +-0.002693 + 11 +-0.082569 + 21 +-0.172686 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.081266 + 20 +-0.173604 + 30 +-0.002693 + 11 +-0.082854 + 21 +-0.17362 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.082569 + 20 +-0.172686 + 30 +0.009188 + 11 +-0.081266 + 21 +-0.173604 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.080451 + 20 +-0.171349 + 30 +0.009188 + 11 +-0.082569 + 21 +-0.172686 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.080184 + 20 +-0.173003 + 30 +-0.002693 + 11 +-0.080451 + 21 +-0.171349 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.079721 + 20 +-0.171706 + 30 +-0.002693 + 11 +-0.080184 + 21 +-0.173003 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.080451 + 20 +-0.171349 + 30 +0.009188 + 11 +-0.079721 + 21 +-0.171706 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.082569 + 20 +-0.172686 + 30 +0.009188 + 11 +-0.080451 + 21 +-0.171349 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.090123 + 20 +-0.161099 + 30 +0.009188 + 11 +-0.082569 + 21 +-0.172686 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.079721 + 20 +-0.171706 + 30 +-0.002693 + 11 +-0.079942 + 21 +-0.16996 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.080184 + 20 +-0.173003 + 30 +-0.002693 + 11 +-0.079721 + 21 +-0.171706 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.081266 + 20 +-0.173604 + 30 +-0.002693 + 11 +-0.080184 + 21 +-0.173003 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.082854 + 20 +-0.17362 + 30 +-0.002693 + 11 +-0.081266 + 21 +-0.173604 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.084111 + 20 +-0.17261 + 30 +-0.002693 + 11 +-0.082854 + 21 +-0.17362 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.090789 + 20 +-0.162467 + 30 +-0.002693 + 11 +-0.084111 + 21 +-0.17261 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.091168 + 20 +-0.160927 + 30 +-0.002693 + 11 +-0.090789 + 21 +-0.162467 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.090866 + 20 +-0.159382 + 30 +-0.002693 + 11 +-0.091168 + 21 +-0.160927 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.089492 + 20 +-0.158529 + 30 +-0.002693 + 11 +-0.090866 + 21 +-0.159382 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +-0.0877 + 20 +-0.158443 + 30 +-0.002693 + 11 +-0.089492 + 21 +-0.158529 + 31 +-0.002693 + 0 +LINE + 8 +0 + 10 +0.001362 + 20 +-0.091189 + 30 +-0.001109 + 11 +0.000899 + 21 +-0.092487 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.00106 + 20 +-0.093844 + 30 +0.009188 + 11 +0.000899 + 21 +-0.092487 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +0.001362 + 20 +-0.091189 + 30 +-0.001109 + 11 +0.00912 + 21 +-0.102707 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +-0.00106 + 20 +-0.093844 + 30 +0.009188 + 11 +-0.001727 + 21 +-0.095211 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +-0.002105 + 20 +-0.093672 + 30 +-0.001109 + 11 +-0.00106 + 21 +-0.093844 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.001803 + 20 +-0.092126 + 30 +-0.001109 + 11 +-0.002105 + 21 +-0.093672 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +-0.00106 + 20 +-0.093844 + 30 +0.009188 + 11 +-0.001803 + 21 +-0.092126 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.008612 + 20 +-0.104095 + 30 +0.009188 + 11 +0.00912 + 21 +-0.102707 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.000899 + 20 +-0.092487 + 30 +0.009188 + 11 +0.008612 + 21 +-0.104095 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.000428 + 20 +-0.091274 + 30 +-0.001109 + 11 +0.000899 + 21 +-0.092487 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +0.001362 + 20 +-0.091189 + 30 +-0.001109 + 11 +-0.000428 + 21 +-0.091274 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.006495 + 20 +-0.105431 + 30 +0.009188 + 11 +0.004952 + 21 +-0.105356 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.006208 + 20 +-0.106364 + 30 +-0.001109 + 11 +0.006495 + 21 +-0.105431 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +0.007796 + 20 +-0.106349 + 30 +-0.001109 + 11 +0.006208 + 21 +-0.106364 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.006495 + 20 +-0.105431 + 30 +0.009188 + 11 +0.007796 + 21 +-0.106349 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.008612 + 20 +-0.104095 + 30 +0.009188 + 11 +0.006495 + 21 +-0.105431 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +0.008879 + 20 +-0.105748 + 30 +-0.001109 + 11 +0.008612 + 21 +-0.104095 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +0.009342 + 20 +-0.10445 + 30 +-0.001109 + 11 +0.008879 + 21 +-0.105748 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.008612 + 20 +-0.104095 + 30 +0.009188 + 11 +0.009342 + 21 +-0.10445 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.006495 + 20 +-0.105431 + 30 +0.009188 + 11 +0.008612 + 21 +-0.104095 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +-0.00106 + 20 +-0.093844 + 30 +0.009188 + 11 +0.006495 + 21 +-0.105431 + 31 +0.009188 + 0 +LINE + 8 +0 + 10 +0.009342 + 20 +-0.10445 + 30 +-0.001109 + 11 +0.00912 + 21 +-0.102707 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.008879 + 20 +-0.105748 + 30 +-0.001109 + 11 +0.009342 + 21 +-0.10445 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.007796 + 20 +-0.106349 + 30 +-0.001109 + 11 +0.008879 + 21 +-0.105748 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.006208 + 20 +-0.106364 + 30 +-0.001109 + 11 +0.007796 + 21 +-0.106349 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.004952 + 20 +-0.105356 + 30 +-0.001109 + 11 +0.006208 + 21 +-0.106364 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +-0.001727 + 20 +-0.095211 + 30 +-0.001109 + 11 +0.004952 + 21 +-0.105356 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +-0.002105 + 20 +-0.093672 + 30 +-0.001109 + 11 +-0.001727 + 21 +-0.095211 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +-0.001803 + 20 +-0.092126 + 30 +-0.001109 + 11 +-0.002105 + 21 +-0.093672 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +-0.000428 + 20 +-0.091274 + 30 +-0.001109 + 11 +-0.001803 + 21 +-0.092126 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.001362 + 20 +-0.091189 + 30 +-0.001109 + 11 +-0.000428 + 21 +-0.091274 + 31 +-0.001109 + 0 +LINE + 8 +0 + 10 +0.011409 + 20 +-0.102736 + 30 +0.014391 + 11 +0.01482 + 21 +-0.100179 + 31 +0.014489 + 0 +LINE + 8 +0 + 10 +0.004678 + 20 +-0.107783 + 30 +0.01443 + 11 +0.011409 + 21 +-0.102736 + 31 +0.014391 + 0 +LINE + 8 +0 + 10 +-0.003789 + 20 +-0.114132 + 30 +0.014466 + 11 +0.004678 + 21 +-0.107783 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +-0.014469 + 20 +-0.12214 + 30 +0.014503 + 11 +-0.003789 + 21 +-0.114132 + 31 +0.014466 + 0 +LINE + 8 +0 + 10 +-0.024762 + 20 +-0.129857 + 30 +0.014466 + 11 +-0.014469 + 21 +-0.12214 + 31 +0.014503 + 0 +LINE + 8 +0 + 10 +-0.035532 + 20 +-0.137933 + 30 +0.014503 + 11 +-0.024762 + 21 +-0.129857 + 31 +0.014466 + 0 +LINE + 8 +0 + 10 +-0.046541 + 20 +-0.146189 + 30 +0.014466 + 11 +-0.035532 + 21 +-0.137933 + 31 +0.014503 + 0 +LINE + 8 +0 + 10 +-0.057521 + 20 +-0.154422 + 30 +0.01443 + 11 +-0.046541 + 21 +-0.146189 + 31 +0.014466 + 0 +LINE + 8 +0 + 10 +-0.069069 + 20 +-0.16308 + 30 +0.01443 + 11 +-0.057521 + 21 +-0.154422 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +-0.078135 + 20 +-0.169877 + 30 +0.014503 + 11 +-0.069069 + 21 +-0.16308 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +-0.085674 + 20 +-0.175531 + 30 +0.01443 + 11 +-0.078135 + 21 +-0.169877 + 31 +0.014503 + 0 +LINE + 8 +0 + 10 +-0.089503 + 20 +-0.178402 + 30 +0.014527 + 11 +-0.085674 + 21 +-0.175531 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +0.001416 + 20 +-0.110228 + 30 +0.025254 + 11 +0.000781 + 21 +-0.093749 + 31 +0.020553 + 0 +LINE + 8 +0 + 10 +-0.016452 + 20 +-0.106669 + 30 +0.031317 + 11 +0.001416 + 21 +-0.110228 + 31 +0.025254 + 0 +LINE + 8 +0 + 10 +-0.019645 + 20 +-0.126021 + 30 +0.03542 + 11 +-0.016452 + 21 +-0.106669 + 31 +0.031317 + 0 +LINE + 8 +0 + 10 +-0.037035 + 20 +-0.122103 + 30 +0.037298 + 11 +-0.019645 + 21 +-0.126021 + 31 +0.03542 + 0 +LINE + 8 +0 + 10 +-0.041186 + 20 +-0.142173 + 30 +0.037214 + 11 +-0.037035 + 21 +-0.122103 + 31 +0.037298 + 0 +LINE + 8 +0 + 10 +-0.059055 + 20 +-0.138615 + 30 +0.035504 + 11 +-0.041186 + 21 +-0.142173 + 31 +0.037214 + 0 +LINE + 8 +0 + 10 +-0.063205 + 20 +-0.158683 + 30 +0.031234 + 11 +-0.059055 + 21 +-0.138615 + 31 +0.035504 + 0 +LINE + 8 +0 + 10 +-0.081553 + 20 +-0.155484 + 30 +0.025337 + 11 +-0.063205 + 21 +-0.158683 + 31 +0.031234 + 0 +LINE + 8 +0 + 10 +-0.081875 + 20 +-0.172682 + 30 +0.02047 + 11 +-0.081553 + 21 +-0.155484 + 31 +0.025337 + 0 +LINE + 8 +0 + 10 +-0.089242 + 20 +-0.161249 + 30 +0.020516 + 11 +-0.081875 + 21 +-0.172682 + 31 +0.02047 + 0 +LINE + 8 +0 + 10 +-0.074186 + 20 +-0.166916 + 30 +0.025291 + 11 +-0.089242 + 21 +-0.161249 + 31 +0.020516 + 0 +LINE + 8 +0 + 10 +-0.070574 + 20 +-0.14725 + 30 +0.03128 + 11 +-0.074186 + 21 +-0.166916 + 31 +0.025291 + 0 +LINE + 8 +0 + 10 +-0.051688 + 20 +-0.150048 + 30 +0.035457 + 11 +-0.070574 + 21 +-0.14725 + 31 +0.03128 + 0 +LINE + 8 +0 + 10 +-0.048554 + 20 +-0.13074 + 30 +0.03726 + 11 +-0.051688 + 21 +-0.150048 + 31 +0.035457 + 0 +LINE + 8 +0 + 10 +-0.029668 + 20 +-0.133536 + 30 +0.037251 + 11 +-0.048554 + 21 +-0.13074 + 31 +0.03726 + 0 +LINE + 8 +0 + 10 +-0.027012 + 20 +-0.114588 + 30 +0.035466 + 11 +-0.029668 + 21 +-0.133536 + 31 +0.037251 + 0 +LINE + 8 +0 + 10 +-0.009085 + 20 +-0.118102 + 30 +0.031271 + 11 +-0.027012 + 21 +-0.114588 + 31 +0.035466 + 0 +LINE + 8 +0 + 10 +-0.005951 + 20 +-0.098795 + 30 +0.0253 + 11 +-0.009085 + 21 +-0.118102 + 31 +0.031271 + 0 +LINE + 8 +0 + 10 +0.008149 + 20 +-0.105182 + 30 +0.020507 + 11 +-0.005951 + 21 +-0.098795 + 31 +0.0253 + 0 +LINE + 8 +0 + 10 +0.000781 + 20 +-0.093749 + 30 +0.020553 + 11 +0.008149 + 21 +-0.105182 + 31 +0.020507 + 0 +LINE + 8 +0 + 10 +0.004042 + 20 +-0.091303 + 30 +0.014439 + 11 +0.003952 + 21 +-0.09137 + 31 +0.009355 + 0 +LINE + 8 +0 + 10 +-0.002689 + 20 +-0.096351 + 30 +0.014476 + 11 +-0.00266 + 21 +-0.096328 + 31 +0.009541 + 0 +LINE + 8 +0 + 10 +-0.011156 + 20 +-0.102699 + 30 +0.014513 + 11 +-0.011186 + 21 +-0.102721 + 31 +0.009355 + 0 +LINE + 8 +0 + 10 +-0.021838 + 20 +-0.110707 + 30 +0.014551 + 11 +-0.021867 + 21 +-0.11073 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.032129 + 20 +-0.118424 + 30 +0.014513 + 11 +-0.032129 + 21 +-0.118424 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.042899 + 20 +-0.1265 + 30 +0.014551 + 11 +-0.042929 + 21 +-0.126523 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.05391 + 20 +-0.134756 + 30 +0.014513 + 11 +-0.053969 + 21 +-0.134801 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.064888 + 20 +-0.142988 + 30 +0.014476 + 11 +-0.064919 + 21 +-0.14301 + 31 +0.009355 + 0 +LINE + 8 +0 + 10 +-0.076438 + 20 +-0.151647 + 30 +0.014476 + 11 +-0.076438 + 21 +-0.151647 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.085502 + 20 +-0.158444 + 30 +0.014551 + 11 +-0.085533 + 21 +-0.158467 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.093041 + 20 +-0.164098 + 30 +0.014476 + 11 +-0.093012 + 21 +-0.164076 + 31 +0.009541 + 0 +LINE + 8 +0 + 10 +-0.089242 + 20 +-0.161249 + 30 +0.020516 + 11 +-0.089242 + 21 +-0.161249 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.081553 + 20 +-0.155484 + 30 +0.025337 + 11 +-0.081553 + 21 +-0.155484 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.070574 + 20 +-0.14725 + 30 +0.03128 + 11 +-0.070603 + 21 +-0.147273 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.059055 + 20 +-0.138615 + 30 +0.035504 + 11 +-0.059115 + 21 +-0.138659 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.048554 + 20 +-0.13074 + 30 +0.03726 + 11 +-0.048554 + 21 +-0.13074 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.037035 + 20 +-0.122103 + 30 +0.037298 + 11 +-0.037095 + 21 +-0.122148 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.027012 + 20 +-0.114588 + 30 +0.035466 + 11 +-0.027043 + 21 +-0.114611 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.016452 + 20 +-0.106669 + 30 +0.031317 + 11 +-0.016482 + 21 +-0.106692 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.005951 + 20 +-0.098795 + 30 +0.0253 + 11 +-0.00592 + 21 +-0.098773 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +0.000781 + 20 +-0.093749 + 30 +0.020553 + 11 +0.000721 + 21 +-0.093794 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.093041 + 20 +-0.164098 + 30 +0.014476 + 11 +-0.096931 + 21 +-0.167014 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.089242 + 20 +-0.161249 + 30 +0.00943 + 11 +-0.093041 + 21 +-0.164098 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +-0.085502 + 20 +-0.158444 + 30 +0.014551 + 11 +-0.089242 + 21 +-0.161249 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.081553 + 20 +-0.155484 + 30 +0.009392 + 11 +-0.085502 + 21 +-0.158444 + 31 +0.014551 + 0 +LINE + 8 +0 + 10 +-0.076438 + 20 +-0.151647 + 30 +0.014476 + 11 +-0.081553 + 21 +-0.155484 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.070603 + 20 +-0.147273 + 30 +0.00943 + 11 +-0.076438 + 21 +-0.151647 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +-0.064888 + 20 +-0.142988 + 30 +0.014476 + 11 +-0.070603 + 21 +-0.147273 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.059115 + 20 +-0.138659 + 30 +0.009392 + 11 +-0.064888 + 21 +-0.142988 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +-0.05391 + 20 +-0.134756 + 30 +0.014513 + 11 +-0.059115 + 21 +-0.138659 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.048554 + 20 +-0.13074 + 30 +0.00943 + 11 +-0.05391 + 21 +-0.134756 + 31 +0.014513 + 0 +LINE + 8 +0 + 10 +-0.042899 + 20 +-0.1265 + 30 +0.014551 + 11 +-0.048554 + 21 +-0.13074 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.037095 + 20 +-0.122148 + 30 +0.009392 + 11 +-0.042899 + 21 +-0.1265 + 31 +0.014551 + 0 +LINE + 8 +0 + 10 +-0.032129 + 20 +-0.118424 + 30 +0.014513 + 11 +-0.037095 + 21 +-0.122148 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.027043 + 20 +-0.114611 + 30 +0.00943 + 11 +-0.032129 + 21 +-0.118424 + 31 +0.014513 + 0 +LINE + 8 +0 + 10 +-0.021838 + 20 +-0.110707 + 30 +0.014551 + 11 +-0.027043 + 21 +-0.114611 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.016482 + 20 +-0.106692 + 30 +0.009392 + 11 +-0.021838 + 21 +-0.110707 + 31 +0.014551 + 0 +LINE + 8 +0 + 10 +-0.011156 + 20 +-0.102699 + 30 +0.014513 + 11 +-0.016482 + 21 +-0.106692 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.00592 + 20 +-0.098773 + 30 +0.00943 + 11 +-0.011156 + 21 +-0.102699 + 31 +0.014513 + 0 +LINE + 8 +0 + 10 +-0.002689 + 20 +-0.096351 + 30 +0.014476 + 11 +-0.00592 + 21 +-0.098773 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +0.000721 + 20 +-0.093794 + 30 +0.009392 + 11 +-0.002689 + 21 +-0.096351 + 31 +0.014476 + 0 +LINE + 8 +0 + 10 +0.004042 + 20 +-0.091303 + 30 +0.014439 + 11 +0.000721 + 21 +-0.093794 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +0.007512 + 20 +-0.088701 + 30 +0.00943 + 11 +0.004042 + 21 +-0.091303 + 31 +0.014439 + 0 +LINE + 8 +0 + 10 +0.007453 + 20 +-0.088746 + 30 +0.014536 + 11 +0.007512 + 21 +-0.088701 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +0.003952 + 20 +-0.09137 + 30 +0.009355 + 11 +0.007512 + 21 +-0.088701 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +0.000721 + 20 +-0.093794 + 30 +0.009392 + 11 +0.003952 + 21 +-0.09137 + 31 +0.009355 + 0 +LINE + 8 +0 + 10 +-0.00266 + 20 +-0.096328 + 30 +0.009541 + 11 +0.000721 + 21 +-0.093794 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.00592 + 20 +-0.098773 + 30 +0.00943 + 11 +-0.00266 + 21 +-0.096328 + 31 +0.009541 + 0 +LINE + 8 +0 + 10 +-0.011186 + 20 +-0.102721 + 30 +0.009355 + 11 +-0.00592 + 21 +-0.098773 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.016482 + 20 +-0.106692 + 30 +0.009392 + 11 +-0.011186 + 21 +-0.102721 + 31 +0.009355 + 0 +LINE + 8 +0 + 10 +-0.021867 + 20 +-0.11073 + 30 +0.009392 + 11 +-0.016482 + 21 +-0.106692 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.027043 + 20 +-0.114611 + 30 +0.00943 + 11 +-0.021867 + 21 +-0.11073 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.032129 + 20 +-0.118424 + 30 +0.009392 + 11 +-0.027043 + 21 +-0.114611 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.037095 + 20 +-0.122148 + 30 +0.009392 + 11 +-0.032129 + 21 +-0.118424 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.042929 + 20 +-0.126523 + 30 +0.009392 + 11 +-0.037095 + 21 +-0.122148 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.048554 + 20 +-0.13074 + 30 +0.00943 + 11 +-0.042929 + 21 +-0.126523 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.053969 + 20 +-0.134801 + 30 +0.009392 + 11 +-0.048554 + 21 +-0.13074 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.059115 + 20 +-0.138659 + 30 +0.009392 + 11 +-0.053969 + 21 +-0.134801 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.064919 + 20 +-0.14301 + 30 +0.009355 + 11 +-0.059115 + 21 +-0.138659 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.070603 + 20 +-0.147273 + 30 +0.00943 + 11 +-0.064919 + 21 +-0.14301 + 31 +0.009355 + 0 +LINE + 8 +0 + 10 +-0.076438 + 20 +-0.151647 + 30 +0.00943 + 11 +-0.070603 + 21 +-0.147273 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.081553 + 20 +-0.155484 + 30 +0.009392 + 11 +-0.076438 + 21 +-0.151647 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.085533 + 20 +-0.158467 + 30 +0.009392 + 11 +-0.081553 + 21 +-0.155484 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.089242 + 20 +-0.161249 + 30 +0.00943 + 11 +-0.085533 + 21 +-0.158467 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.093012 + 20 +-0.164076 + 30 +0.009541 + 11 +-0.089242 + 21 +-0.161249 + 31 +0.00943 + 0 +LINE + 8 +0 + 10 +-0.096931 + 20 +-0.167014 + 30 +0.009392 + 11 +-0.093012 + 21 +-0.164076 + 31 +0.009541 + 0 +LINE + 8 +0 + 10 +-0.096871 + 20 +-0.166969 + 30 +0.014573 + 11 +-0.096931 + 21 +-0.167014 + 31 +0.009392 + 0 +LINE + 8 +0 + 10 +-0.089242 + 20 +-0.161249 + 30 +0.020516 + 11 +-0.096871 + 21 +-0.166969 + 31 +0.014573 + 0 +LINE + 8 +0 + 10 +-0.081553 + 20 +-0.155484 + 30 +0.025337 + 11 +-0.089242 + 21 +-0.161249 + 31 +0.020516 + 0 +LINE + 8 +0 + 10 +-0.070574 + 20 +-0.14725 + 30 +0.03128 + 11 +-0.081553 + 21 +-0.155484 + 31 +0.025337 + 0 +LINE + 8 +0 + 10 +-0.059055 + 20 +-0.138615 + 30 +0.035504 + 11 +-0.070574 + 21 +-0.14725 + 31 +0.03128 + 0 +LINE + 8 +0 + 10 +-0.048554 + 20 +-0.13074 + 30 +0.03726 + 11 +-0.059055 + 21 +-0.138615 + 31 +0.035504 + 0 +LINE + 8 +0 + 10 +-0.037035 + 20 +-0.122103 + 30 +0.037298 + 11 +-0.048554 + 21 +-0.13074 + 31 +0.03726 + 0 +LINE + 8 +0 + 10 +-0.027012 + 20 +-0.114588 + 30 +0.035466 + 11 +-0.037035 + 21 +-0.122103 + 31 +0.037298 + 0 +LINE + 8 +0 + 10 +-0.016452 + 20 +-0.106669 + 30 +0.031317 + 11 +-0.027012 + 21 +-0.114588 + 31 +0.035466 + 0 +LINE + 8 +0 + 10 +-0.005951 + 20 +-0.098795 + 30 +0.0253 + 11 +-0.016452 + 21 +-0.106669 + 31 +0.031317 + 0 +LINE + 8 +0 + 10 +0.000781 + 20 +-0.093749 + 30 +0.020553 + 11 +-0.005951 + 21 +-0.098795 + 31 +0.0253 + 0 +LINE + 8 +0 + 10 +0.007453 + 20 +-0.088746 + 30 +0.014536 + 11 +0.000781 + 21 +-0.093749 + 31 +0.020553 + 0 +LINE + 8 +0 + 10 +0.011409 + 20 +-0.102736 + 30 +0.014391 + 11 +0.011319 + 21 +-0.102803 + 31 +0.009309 + 0 +LINE + 8 +0 + 10 +0.004678 + 20 +-0.107783 + 30 +0.01443 + 11 +0.004709 + 21 +-0.107761 + 31 +0.009495 + 0 +LINE + 8 +0 + 10 +-0.003789 + 20 +-0.114132 + 30 +0.014466 + 11 +-0.003819 + 21 +-0.114154 + 31 +0.009309 + 0 +LINE + 8 +0 + 10 +-0.014469 + 20 +-0.12214 + 30 +0.014503 + 11 +-0.014499 + 21 +-0.122163 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.024762 + 20 +-0.129857 + 30 +0.014466 + 11 +-0.024762 + 21 +-0.129857 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.035532 + 20 +-0.137933 + 30 +0.014503 + 11 +-0.035561 + 21 +-0.137956 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.046541 + 20 +-0.146189 + 30 +0.014466 + 11 +-0.046602 + 21 +-0.146234 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.057521 + 20 +-0.154422 + 30 +0.01443 + 11 +-0.057552 + 21 +-0.154445 + 31 +0.009309 + 0 +LINE + 8 +0 + 10 +-0.069069 + 20 +-0.16308 + 30 +0.01443 + 11 +-0.069069 + 21 +-0.16308 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.078135 + 20 +-0.169877 + 30 +0.014503 + 11 +-0.078164 + 21 +-0.1699 + 31 +0.014573 + 0 +LINE + 8 +0 + 10 +-0.085674 + 20 +-0.175531 + 30 +0.01443 + 11 +-0.085644 + 21 +-0.175509 + 31 +0.009495 + 0 +LINE + 8 +0 + 10 +-0.081875 + 20 +-0.172682 + 30 +0.02047 + 11 +-0.081875 + 21 +-0.172682 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.074186 + 20 +-0.166916 + 30 +0.025291 + 11 +-0.074186 + 21 +-0.166916 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.063205 + 20 +-0.158683 + 30 +0.031234 + 11 +-0.063236 + 21 +-0.158706 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.051688 + 20 +-0.150048 + 30 +0.035457 + 11 +-0.051747 + 21 +-0.150092 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.041186 + 20 +-0.142173 + 30 +0.037214 + 11 +-0.041186 + 21 +-0.142173 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.029668 + 20 +-0.133536 + 30 +0.037251 + 11 +-0.029728 + 21 +-0.133581 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.019645 + 20 +-0.126021 + 30 +0.03542 + 11 +-0.019675 + 21 +-0.126044 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.009085 + 20 +-0.118102 + 30 +0.031271 + 11 +-0.009114 + 21 +-0.118125 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +0.001416 + 20 +-0.110228 + 30 +0.025254 + 11 +0.001447 + 21 +-0.110206 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +0.008149 + 20 +-0.105182 + 30 +0.020507 + 11 +0.008089 + 21 +-0.105227 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.085674 + 20 +-0.175531 + 30 +0.01443 + 11 +-0.089563 + 21 +-0.178447 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.081875 + 20 +-0.172682 + 30 +0.009383 + 11 +-0.085674 + 21 +-0.175531 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +-0.078135 + 20 +-0.169877 + 30 +0.014503 + 11 +-0.081875 + 21 +-0.172682 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.074186 + 20 +-0.166916 + 30 +0.009346 + 11 +-0.078135 + 21 +-0.169877 + 31 +0.014503 + 0 +LINE + 8 +0 + 10 +-0.069069 + 20 +-0.16308 + 30 +0.01443 + 11 +-0.074186 + 21 +-0.166916 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.063236 + 20 +-0.158706 + 30 +0.009383 + 11 +-0.069069 + 21 +-0.16308 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +-0.057521 + 20 +-0.154422 + 30 +0.01443 + 11 +-0.063236 + 21 +-0.158706 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.051747 + 20 +-0.150092 + 30 +0.009346 + 11 +-0.057521 + 21 +-0.154422 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +-0.046541 + 20 +-0.146189 + 30 +0.014466 + 11 +-0.051747 + 21 +-0.150092 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.041186 + 20 +-0.142173 + 30 +0.009383 + 11 +-0.046541 + 21 +-0.146189 + 31 +0.014466 + 0 +LINE + 8 +0 + 10 +-0.035532 + 20 +-0.137933 + 30 +0.014503 + 11 +-0.041186 + 21 +-0.142173 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.029728 + 20 +-0.133581 + 30 +0.009346 + 11 +-0.035532 + 21 +-0.137933 + 31 +0.014503 + 0 +LINE + 8 +0 + 10 +-0.024762 + 20 +-0.129857 + 30 +0.014466 + 11 +-0.029728 + 21 +-0.133581 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.019675 + 20 +-0.126044 + 30 +0.009383 + 11 +-0.024762 + 21 +-0.129857 + 31 +0.014466 + 0 +LINE + 8 +0 + 10 +-0.014469 + 20 +-0.12214 + 30 +0.014503 + 11 +-0.019675 + 21 +-0.126044 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.009114 + 20 +-0.118125 + 30 +0.009346 + 11 +-0.014469 + 21 +-0.12214 + 31 +0.014503 + 0 +LINE + 8 +0 + 10 +-0.003789 + 20 +-0.114132 + 30 +0.014466 + 11 +-0.009114 + 21 +-0.118125 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +0.001447 + 20 +-0.110206 + 30 +0.009383 + 11 +-0.003789 + 21 +-0.114132 + 31 +0.014466 + 0 +LINE + 8 +0 + 10 +0.004678 + 20 +-0.107783 + 30 +0.01443 + 11 +0.001447 + 21 +-0.110206 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +0.008089 + 20 +-0.105227 + 30 +0.009346 + 11 +0.004678 + 21 +-0.107783 + 31 +0.01443 + 0 +LINE + 8 +0 + 10 +0.011409 + 20 +-0.102736 + 30 +0.014391 + 11 +0.008089 + 21 +-0.105227 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +0.01488 + 20 +-0.100134 + 30 +0.009383 + 11 +0.011409 + 21 +-0.102736 + 31 +0.014391 + 0 +LINE + 8 +0 + 10 +0.01482 + 20 +-0.100179 + 30 +0.014489 + 11 +0.01488 + 21 +-0.100134 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +0.011319 + 20 +-0.102803 + 30 +0.009309 + 11 +0.01488 + 21 +-0.100134 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +0.008089 + 20 +-0.105227 + 30 +0.009346 + 11 +0.011319 + 21 +-0.102803 + 31 +0.009309 + 0 +LINE + 8 +0 + 10 +0.004709 + 20 +-0.107761 + 30 +0.009495 + 11 +0.008089 + 21 +-0.105227 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +0.001447 + 20 +-0.110206 + 30 +0.009383 + 11 +0.004709 + 21 +-0.107761 + 31 +0.009495 + 0 +LINE + 8 +0 + 10 +-0.003819 + 20 +-0.114154 + 30 +0.009309 + 11 +0.001447 + 21 +-0.110206 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.009114 + 20 +-0.118125 + 30 +0.009346 + 11 +-0.003819 + 21 +-0.114154 + 31 +0.009309 + 0 +LINE + 8 +0 + 10 +-0.014499 + 20 +-0.122163 + 30 +0.009346 + 11 +-0.009114 + 21 +-0.118125 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.019675 + 20 +-0.126044 + 30 +0.009383 + 11 +-0.014499 + 21 +-0.122163 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.024762 + 20 +-0.129857 + 30 +0.009346 + 11 +-0.019675 + 21 +-0.126044 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.029728 + 20 +-0.133581 + 30 +0.009346 + 11 +-0.024762 + 21 +-0.129857 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.035561 + 20 +-0.137956 + 30 +0.009346 + 11 +-0.029728 + 21 +-0.133581 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.041186 + 20 +-0.142173 + 30 +0.009383 + 11 +-0.035561 + 21 +-0.137956 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.046602 + 20 +-0.146234 + 30 +0.009346 + 11 +-0.041186 + 21 +-0.142173 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.051747 + 20 +-0.150092 + 30 +0.009346 + 11 +-0.046602 + 21 +-0.146234 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.057552 + 20 +-0.154445 + 30 +0.009309 + 11 +-0.051747 + 21 +-0.150092 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.063236 + 20 +-0.158706 + 30 +0.009383 + 11 +-0.057552 + 21 +-0.154445 + 31 +0.009309 + 0 +LINE + 8 +0 + 10 +-0.069069 + 20 +-0.16308 + 30 +0.009383 + 11 +-0.063236 + 21 +-0.158706 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.074186 + 20 +-0.166916 + 30 +0.009346 + 11 +-0.069069 + 21 +-0.16308 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.081875 + 20 +-0.172682 + 30 +0.009383 + 11 +-0.074186 + 21 +-0.166916 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.085644 + 20 +-0.175509 + 30 +0.009495 + 11 +-0.081875 + 21 +-0.172682 + 31 +0.009383 + 0 +LINE + 8 +0 + 10 +-0.089563 + 20 +-0.178447 + 30 +0.009346 + 11 +-0.085644 + 21 +-0.175509 + 31 +0.009495 + 0 +LINE + 8 +0 + 10 +-0.089503 + 20 +-0.178402 + 30 +0.014527 + 11 +-0.089563 + 21 +-0.178447 + 31 +0.009346 + 0 +LINE + 8 +0 + 10 +-0.081875 + 20 +-0.172682 + 30 +0.02047 + 11 +-0.089503 + 21 +-0.178402 + 31 +0.014527 + 0 +LINE + 8 +0 + 10 +-0.074186 + 20 +-0.166916 + 30 +0.025291 + 11 +-0.081875 + 21 +-0.172682 + 31 +0.02047 + 0 +LINE + 8 +0 + 10 +-0.063205 + 20 +-0.158683 + 30 +0.031234 + 11 +-0.074186 + 21 +-0.166916 + 31 +0.025291 + 0 +LINE + 8 +0 + 10 +-0.051688 + 20 +-0.150048 + 30 +0.035457 + 11 +-0.063205 + 21 +-0.158683 + 31 +0.031234 + 0 +LINE + 8 +0 + 10 +-0.041186 + 20 +-0.142173 + 30 +0.037214 + 11 +-0.051688 + 21 +-0.150048 + 31 +0.035457 + 0 +LINE + 8 +0 + 10 +-0.029668 + 20 +-0.133536 + 30 +0.037251 + 11 +-0.041186 + 21 +-0.142173 + 31 +0.037214 + 0 +LINE + 8 +0 + 10 +-0.019645 + 20 +-0.126021 + 30 +0.03542 + 11 +-0.029668 + 21 +-0.133536 + 31 +0.037251 + 0 +LINE + 8 +0 + 10 +-0.009085 + 20 +-0.118102 + 30 +0.031271 + 11 +-0.019645 + 21 +-0.126021 + 31 +0.03542 + 0 +LINE + 8 +0 + 10 +0.001416 + 20 +-0.110228 + 30 +0.025254 + 11 +-0.009085 + 21 +-0.118102 + 31 +0.031271 + 0 +LINE + 8 +0 + 10 +0.008149 + 20 +-0.105182 + 30 +0.020507 + 11 +0.001416 + 21 +-0.110228 + 31 +0.025254 + 0 +LINE + 8 +0 + 10 +0.01482 + 20 +-0.100179 + 30 +0.014489 + 11 +0.008149 + 21 +-0.105182 + 31 +0.020507 + 0 +ENDSEC + 0 +EOF diff --git a/vcl/qt5/QtInstancePopover.cxx b/vcl/qt5/QtInstancePopover.cxx index df88afcf1741..68638bdf8237 100644 --- a/vcl/qt5/QtInstancePopover.cxx +++ b/vcl/qt5/QtInstancePopover.cxx @@ -44,4 +44,22 @@ void QtInstancePopover::popdown() void QtInstancePopover::resize_to_request() { assert(false && "Not implemented yet"); } +bool QtInstancePopover::eventFilter(QObject* pObject, QEvent* pEvent) +{ + if (pObject == getQWidget() && pEvent->type() == QEvent::Close) + { + // signal that the popup was closed when control returns to the + // main loop (at which point the event to close the popup has + // actually been processed) + QMetaObject::invokeMethod(this, + [this] { + SolarMutexGuard g; + signal_closed(); + }, + Qt::QueuedConnection); + } + + return QtInstanceWidget::eventFilter(pObject, pEvent); +} + /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/qt5/QtWidget.cxx b/vcl/qt5/QtWidget.cxx index 3e3ec91d98d1..3bf463ef7a06 100644 --- a/vcl/qt5/QtWidget.cxx +++ b/vcl/qt5/QtWidget.cxx @@ -343,7 +343,7 @@ bool QtWidget::handleKeyEvent(QKeyEvent* pEvent) const return true; } - QGuiApplication::inputMethod()->update(Qt::ImCursorRectangle); + QGuiApplication::inputMethod()->update(Qt::ImQueryInput); if (nCode == 0 && pEvent->text().isEmpty()) { @@ -661,52 +661,25 @@ void QtWidget::inputMethodEvent(QInputMethodEvent* pEvent) pEvent->accept(); } -static bool lcl_retrieveSurrounding(sal_Int32& rPosition, sal_Int32& rAnchor, QString* pText, - QString* pSelection) +void QtWidget::retrieveSurrounding(sal_uLong& rPosition, sal_uLong& rAnchor, QString* pText, + QString* pSelection) const { SolarMutexGuard aGuard; - vcl::Window* pFocusWin = Application::GetFocusWindow(); - if (!pFocusWin) - return false; - uno::Reference<accessibility::XAccessibleEditableText> xText; - try - { - xText = FindFocusedEditableText(pFocusWin->GetAccessible()); - } - catch (const uno::Exception&) - { - TOOLS_WARN_EXCEPTION("vcl.qt", "Exception in getting input method surrounding text"); - } + SalSurroundingTextRequestEvent aEvt; + aEvt.mnStart = aEvt.mnEnd = aEvt.mnCursorPos = 0; + m_rFrame.CallCallback(SalEvent::SurroundingTextRequest, &aEvt); - if (xText.is()) - { - rPosition = xText->getCaretPosition(); - if (rPosition != -1) - { - if (pText) - *pText = toQString(xText->getText()); - - sal_Int32 nSelStart = xText->getSelectionStart(); - sal_Int32 nSelEnd = xText->getSelectionEnd(); - if (nSelStart == nSelEnd) - { - rAnchor = rPosition; - } - else - { - if (rPosition == nSelStart) - rAnchor = nSelEnd; - else - rAnchor = nSelStart; - if (pSelection) - *pSelection = toQString(xText->getSelectedText()); - } - return true; - } - } + rPosition = aEvt.mnCursorPos; + if (pText) + *pText = toQString(aEvt.maText); - return false; + if (rPosition == aEvt.mnStart) + rAnchor = aEvt.mnEnd; + else + rAnchor = aEvt.mnStart; + if (pSelection) + *pSelection = toQString(aEvt.maText).mid(aEvt.mnStart, aEvt.mnEnd - aEvt.mnStart); } QVariant QtWidget::inputMethodQuery(Qt::InputMethodQuery property) const @@ -716,17 +689,15 @@ QVariant QtWidget::inputMethodQuery(Qt::InputMethodQuery property) const case Qt::ImSurroundingText: { QString aText; - sal_Int32 nCursorPos, nAnchor; - if (lcl_retrieveSurrounding(nCursorPos, nAnchor, &aText, nullptr)) - return QVariant(aText); - return QVariant(); + sal_uLong nCursorPos, nAnchor; + retrieveSurrounding(nCursorPos, nAnchor, &aText, nullptr); + return QVariant(aText); } case Qt::ImCursorPosition: { - sal_Int32 nCursorPos, nAnchor; - if (lcl_retrieveSurrounding(nCursorPos, nAnchor, nullptr, nullptr)) - return QVariant(static_cast<int>(nCursorPos)); - return QVariant(); + sal_uLong nCursorPos, nAnchor; + retrieveSurrounding(nCursorPos, nAnchor, nullptr, nullptr); + return QVariant(static_cast<int>(nCursorPos)); } case Qt::ImCursorRectangle: { @@ -745,18 +716,16 @@ QVariant QtWidget::inputMethodQuery(Qt::InputMethodQuery property) const } case Qt::ImAnchorPosition: { - sal_Int32 nCursorPos, nAnchor; - if (lcl_retrieveSurrounding(nCursorPos, nAnchor, nullptr, nullptr)) - return QVariant(static_cast<int>(nAnchor)); - return QVariant(); + sal_uLong nCursorPos, nAnchor; + retrieveSurrounding(nCursorPos, nAnchor, nullptr, nullptr); + return QVariant(static_cast<int>(nAnchor)); } case Qt::ImCurrentSelection: { QString aSelection; - sal_Int32 nCursorPos, nAnchor; - if (lcl_retrieveSurrounding(nCursorPos, nAnchor, nullptr, &aSelection)) - return QVariant(aSelection); - return QVariant(); + sal_uLong nCursorPos, nAnchor; + retrieveSurrounding(nCursorPos, nAnchor, nullptr, &aSelection); + return QVariant(aSelection); } default: return QWidget::inputMethodQuery(property); diff --git a/vcl/skia/gdiimpl.cxx b/vcl/skia/gdiimpl.cxx index 629ab7552588..0872b0ac1c5c 100644 --- a/vcl/skia/gdiimpl.cxx +++ b/vcl/skia/gdiimpl.cxx @@ -1701,8 +1701,7 @@ void SkiaSalGraphicsImpl::drawAlphaBitmap(const SalTwoRect& rPosAry, const SalBi drawShader(rPosAry, rSkiaSourceBitmap.GetSkShader(makeSamplingOptions(rPosAry, mScaling))); } -void SkiaSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SkiaSalBitmap& bitmap, - SkBlendMode blendMode) +void SkiaSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SkiaSalBitmap& bitmap) { // Use mergeCacheBitmaps(), which may decide to cache the result, avoiding repeated // scaling. @@ -1720,15 +1719,15 @@ void SkiaSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SkiaSalBit } sk_sp<SkImage> image = mergeCacheBitmaps(bitmap, imageSize * mScaling); if (image) - drawImage(imagePosAry, image, mScaling, blendMode); + drawImage(imagePosAry, image, mScaling); else if (bitmap.PreferSkShader()) - drawShader(rPosAry, bitmap.GetSkShader(makeSamplingOptions(rPosAry, mScaling)), blendMode); + drawShader(rPosAry, bitmap.GetSkShader(makeSamplingOptions(rPosAry, mScaling))); else - drawImage(rPosAry, bitmap.GetSkImage(), 1, blendMode); + drawImage(rPosAry, bitmap.GetSkImage(), 1); } void SkiaSalGraphicsImpl::drawImage(const SalTwoRect& rPosAry, const sk_sp<SkImage>& aImage, - int srcScaling, SkBlendMode eBlendMode) + int srcScaling) { SkRect aSourceRect = SkRect::MakeXYWH(rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight); @@ -1738,11 +1737,11 @@ void SkiaSalGraphicsImpl::drawImage(const SalTwoRect& rPosAry, const sk_sp<SkIma rPosAry.mnDestWidth, rPosAry.mnDestHeight); SkPaint aPaint = makeBitmapPaint(); - aPaint.setBlendMode(eBlendMode); + aPaint.setBlendMode(SkBlendMode::kSrcOver); preDraw(); - SAL_INFO("vcl.skia.trace", - "drawimage(" << this << "): " << rPosAry << ":" << SkBlendMode_Name(eBlendMode)); + SAL_INFO("vcl.skia.trace", "drawimage(" << this << "): " << rPosAry << ":" + << SkBlendMode_Name(SkBlendMode::kSrcOver)); addUpdateRegion(aDestinationRect); getDrawCanvas()->drawImageRect(aImage, aSourceRect, aDestinationRect, makeSamplingOptions(rPosAry, mScaling, srcScaling), &aPaint, @@ -1753,8 +1752,7 @@ void SkiaSalGraphicsImpl::drawImage(const SalTwoRect& rPosAry, const sk_sp<SkIma // SkShader can be used to merge multiple bitmaps with appropriate blend modes (e.g. when // merging a bitmap with its alpha mask). -void SkiaSalGraphicsImpl::drawShader(const SalTwoRect& rPosAry, const sk_sp<SkShader>& shader, - SkBlendMode blendMode) +void SkiaSalGraphicsImpl::drawShader(const SalTwoRect& rPosAry, const sk_sp<SkShader>& shader) { preDraw(); SAL_INFO("vcl.skia.trace", "drawshader(" << this << "): " << rPosAry); @@ -1762,7 +1760,7 @@ void SkiaSalGraphicsImpl::drawShader(const SalTwoRect& rPosAry, const sk_sp<SkSh rPosAry.mnDestHeight); addUpdateRegion(destinationRect); SkPaint paint = makeBitmapPaint(); - paint.setBlendMode(blendMode); + paint.setBlendMode(SkBlendMode::kSrcOver); paint.setShader(shader); SkCanvas* canvas = getDrawCanvas(); // Scaling needs to be done explicitly using a matrix. diff --git a/vcl/source/accessibility/AccessibleBrowseBox.cxx b/vcl/source/accessibility/AccessibleBrowseBox.cxx index 8cc43e2678bc..934f82a6e804 100644 --- a/vcl/source/accessibility/AccessibleBrowseBox.cxx +++ b/vcl/source/accessibility/AccessibleBrowseBox.cxx @@ -35,9 +35,9 @@ using namespace ::com::sun::star::accessibility; // Ctor/Dtor/disposing AccessibleBrowseBox::AccessibleBrowseBox( - const css::uno::Reference<css::accessibility::XAccessible>& _rxParent, + const rtl::Reference<comphelper::OAccessible>& rpParent, ::vcl::IAccessibleTableProvider& _rBrowseBox) - : AccessibleBrowseBoxBase(_rxParent, _rBrowseBox, nullptr, + : AccessibleBrowseBoxBase(rpParent, _rBrowseBox, nullptr, AccessibleBrowseBoxObjType::BrowseBox) { m_xFocusWindow.set(mpBrowseBox->GetWindowInstance()->GetComponentInterface(), css::uno::UNO_QUERY); @@ -158,8 +158,7 @@ tools::Rectangle AccessibleBrowseBox::implGetBoundingBox() return mpBrowseBox->GetWindowExtentsRelative( *pParent ); } -// internal helper methods -css::uno::Reference< css::accessibility::XAccessible > AccessibleBrowseBox::implGetTable() +rtl::Reference<comphelper::OAccessible> AccessibleBrowseBox::getTable() { if( !mxTable.is() ) { @@ -169,8 +168,8 @@ css::uno::Reference< css::accessibility::XAccessible > AccessibleBrowseBox::impl return mxTable; } -css::uno::Reference< css::accessibility::XAccessible > -AccessibleBrowseBox::implGetHeaderBar(AccessibleBrowseBoxObjType eObjType) +rtl::Reference<comphelper::OAccessible> +AccessibleBrowseBox::getHeaderBar(AccessibleBrowseBoxObjType eObjType) { if( eObjType == AccessibleBrowseBoxObjType::RowHeaderBar ) { @@ -185,26 +184,23 @@ AccessibleBrowseBox::implGetHeaderBar(AccessibleBrowseBoxObjType eObjType) return mxColumnHeaderBar; } - return css::uno::Reference<css::accessibility::XAccessible>(); + return {}; } -css::uno::Reference< css::accessibility::XAccessible > -AccessibleBrowseBox::implGetFixedChild( sal_Int64 nChildIndex ) +rtl::Reference<comphelper::OAccessible> +AccessibleBrowseBox::implGetFixedChild(sal_Int64 nChildIndex) { - css::uno::Reference< css::accessibility::XAccessible > xRet; switch( nChildIndex ) { case vcl::BBINDEX_COLUMNHEADERBAR: - xRet = implGetHeaderBar( AccessibleBrowseBoxObjType::ColumnHeaderBar ); - break; + return getHeaderBar(AccessibleBrowseBoxObjType::ColumnHeaderBar); case vcl::BBINDEX_ROWHEADERBAR: - xRet = implGetHeaderBar( AccessibleBrowseBoxObjType::RowHeaderBar ); - break; + return getHeaderBar(AccessibleBrowseBoxObjType::RowHeaderBar); case vcl::BBINDEX_TABLE: - xRet = implGetTable(); - break; + return getTable(); } - return xRet; + + return {}; } rtl::Reference<AccessibleBrowseBoxTable> AccessibleBrowseBox::createAccessibleTable() diff --git a/vcl/source/accessibility/accessibletablistbox.cxx b/vcl/source/accessibility/accessibletablistbox.cxx index 281ec34a2145..9479bf5494a6 100644 --- a/vcl/source/accessibility/accessibletablistbox.cxx +++ b/vcl/source/accessibility/accessibletablistbox.cxx @@ -32,9 +32,9 @@ using namespace ::com::sun::star; // Ctor() and Dtor() -AccessibleTabListBox::AccessibleTabListBox(const Reference<XAccessible>& rxParent, +AccessibleTabListBox::AccessibleTabListBox(const rtl::Reference<comphelper::OAccessible>& rpParent, SvHeaderTabListBox& rBox) - : AccessibleBrowseBox(rxParent, rBox) + : AccessibleBrowseBox(rpParent, rBox) , m_pTabListBox(&rBox) { } @@ -72,7 +72,7 @@ AccessibleTabListBox::getAccessibleChild( sal_Int64 nChildIndex ) if (nChildIndex != 0) throw IndexOutOfBoundsException(); - return implGetTable(); + return getTable(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/app/scheduler.cxx b/vcl/source/app/scheduler.cxx index 64495994e6e5..73a9f1388044 100644 --- a/vcl/source/app/scheduler.cxx +++ b/vcl/source/app/scheduler.cxx @@ -135,7 +135,9 @@ void Scheduler::ImplDeInitScheduler() assert( nullptr == rSchedCtx.mpSchedulerStack || (!rSchedCtx.mpSchedulerStack->mpTask && !rSchedCtx.mpSchedulerStack->mpNext) ); - if (rSchedCtx.mpSalTimer) rSchedCtx.mpSalTimer->Stop(); + if (rSchedCtx.mpSalTimer) + rSchedCtx.mpSalTimer->Stop(); + delete rSchedCtx.mpSalTimer; rSchedCtx.mpSalTimer = nullptr; diff --git a/vcl/source/app/settings.cxx b/vcl/source/app/settings.cxx index b809187d3f3a..bb3f25fa8851 100644 --- a/vcl/source/app/settings.cxx +++ b/vcl/source/app/settings.cxx @@ -404,12 +404,6 @@ MouseSettings::GetButtonRepeat() const return mxData->mnButtonRepeat; } -sal_Int32 -MouseSettings::GetActionDelay() -{ - return 250; -} - void MouseSettings::SetMenuDelay( sal_Int32 nDelay ) { diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx index 124dc4289f29..d60d5fa59aea 100644 --- a/vcl/source/app/svapp.cxx +++ b/vcl/source/app/svapp.cxx @@ -341,6 +341,10 @@ void Application::notifyInvalidation(tools::Rectangle const* /*pRect*/) const { } +void Application::notifyCursorInvalidation(tools::Rectangle const* /*pRect*/, bool /*bControlEvent*/) const +{ +} + void Application::Execute() { ImplSVData* pSVData = ImplGetSVData(); diff --git a/vcl/source/bitmap/BitmapAlphaClampFilter.cxx b/vcl/source/bitmap/BitmapAlphaClampFilter.cxx deleted file mode 100644 index 68061d4e37d6..000000000000 --- a/vcl/source/bitmap/BitmapAlphaClampFilter.cxx +++ /dev/null @@ -1,43 +0,0 @@ -/* -*- 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 <vcl/bitmap/BitmapAlphaClampFilter.hxx> -#include <vcl/BitmapWriteAccess.hxx> - -Bitmap BitmapAlphaClampFilter::execute(Bitmap const& rBitmap) const -{ - if (!rBitmap.HasAlpha()) - return rBitmap; - - Bitmap aNewBitmap(rBitmap); - { - BitmapScopedWriteAccess pWriteAcc(aNewBitmap); - const Size aSize(aNewBitmap.GetSizePixel()); - - for (sal_Int32 nY = 0; nY < sal_Int32(aSize.Height()); ++nY) - { - Scanline pScan = pWriteAcc->GetScanline(nY); - - for (sal_Int32 nX = 0; nX < sal_Int32(aSize.Width()); ++nX) - { - BitmapColor aBitmapValue(pWriteAcc->GetPixelFromData(pScan, nX)); - if ((255 - aBitmapValue.GetAlpha()) > mcThreshold) - { - aBitmapValue.SetAlpha(0); - pWriteAcc->SetPixelOnData(pScan, nX, aBitmapValue); - } - } - } - } - - return aNewBitmap; -} - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/control/MenuButton.cxx b/vcl/source/control/MenuButton.cxx index d7b80ded0702..c1fdfd6b57b5 100644 --- a/vcl/source/control/MenuButton.cxx +++ b/vcl/source/control/MenuButton.cxx @@ -171,17 +171,6 @@ void MenuButton::dispose() PushButton::dispose(); } -IMPL_LINK_NOARG(MenuButton, ImplMenuTimeoutHdl, Timer *, void) -{ - // See if Button Tracking is still active, as it could've been cancelled earlier - if ( IsTracking() ) - { - if ( !(GetStyle() & WB_NOPOINTERFOCUS) ) - GrabFocus(); - ExecuteMenu(); - } -} - void MenuButton::MouseButtonDown( const MouseEvent& rMEvt ) { if ( PushButton::ImplHitTestPushButton( this, rMEvt.GetPosPixel() ) ) @@ -278,14 +267,4 @@ MenuToggleButton::~MenuToggleButton() disposeOnce(); } -void MenuToggleButton::SetActive( bool bSel ) -{ - mbIsActive = bSel; -} - -bool MenuToggleButton::GetActive() const -{ - return mbIsActive; -} - /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/vcl/source/control/button.cxx b/vcl/source/control/button.cxx index c7e1ddf708fa..14f65c850142 100644 --- a/vcl/source/control/button.cxx +++ b/vcl/source/control/button.cxx @@ -81,7 +81,6 @@ public: ImplCommonButtonData(); tools::Rectangle maFocusRect; - tools::Long mnSeparatorX; DrawButtonFlags mnButtonState; bool mbSmallSymbol; bool mbGeneratedTooltip; @@ -96,7 +95,7 @@ public: rtl::Reference<VclStatusListener<Button>> mpStatusListener; }; -ImplCommonButtonData::ImplCommonButtonData() : mnSeparatorX(0), mnButtonState(DrawButtonFlags::NONE), +ImplCommonButtonData::ImplCommonButtonData() : mnButtonState(DrawButtonFlags::NONE), mbSmallSymbol(false), mbGeneratedTooltip(false), meImageAlign(ImageAlign::Top), meSymbolAlign(SymbolAlign::LEFT) { } @@ -181,16 +180,6 @@ Image const & Button::GetCustomButtonImage() const return mpButtonData->maCustomContentImage; } -tools::Long Button::ImplGetSeparatorX() const -{ - return mpButtonData->mnSeparatorX; -} - -void Button::ImplSetSeparatorX( tools::Long nX ) -{ - mpButtonData->mnSeparatorX = nX; -} - DrawTextFlags Button::ImplGetTextStyle( WinBits nWinStyle, SystemTextColorFlags nSystemTextColorFlags ) const { const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); @@ -963,7 +952,6 @@ void PushButton::ImplDrawPushButtonContent(OutputDevice *pDev, SystemTextColorFl Point aEndPt( nSeparatorX, aSymbolRect.Bottom()-nDistance ); aDecoView.DrawSeparator( aStartPt, aEndPt ); } - ImplSetSeparatorX( nSeparatorX ); aDecoView.DrawSymbol( aSymbolRect, SymbolType::SPIN_DOWN, aColor, nStyle ); diff --git a/vcl/source/edit/vclmedit.cxx b/vcl/source/edit/vclmedit.cxx index 56232b32b58e..7475b7770d70 100644 --- a/vcl/source/edit/vclmedit.cxx +++ b/vcl/source/edit/vclmedit.cxx @@ -44,6 +44,8 @@ #include <strings.hrc> #include <svdata.hxx> +#include <comphelper/lok.hxx> + class ImpVclMEdit : public SfxListener { private: @@ -743,6 +745,9 @@ void TextWindow::KeyInput( const KeyEvent& rKEvent ) if ( !bDone ) Window::KeyInput( rKEvent ); + + if (comphelper::LibreOfficeKit::isActive()) + LogicInvalidate(nullptr); } void TextWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) @@ -852,6 +857,10 @@ void TextWindow::Command( const CommandEvent& rCEvt ) { mpExtTextView->Command( rCEvt ); } + + if (comphelper::LibreOfficeKit::isActive()) + LogicInvalidate(nullptr); + Window::Command( rCEvt ); } diff --git a/vcl/source/filter/graphicfilter.cxx b/vcl/source/filter/graphicfilter.cxx index d9a2802c25b7..49b385628de6 100644 --- a/vcl/source/filter/graphicfilter.cxx +++ b/vcl/source/filter/graphicfilter.cxx @@ -88,6 +88,8 @@ #include <o3tl/string_view.hxx> #include <o3tl/test_info.hxx> #include <vcl/TypeSerializer.hxx> +#include <vcl/GraphicNativeMetadata.hxx> +#include <vcl/GraphicNativeTransform.hxx> #include "FilterConfigCache.hxx" @@ -977,6 +979,18 @@ ErrCode GraphicFilter::readJPEG(SvStream & rStream, Graphic & rGraphic, GfxLinkT } } + // Get Orientation from EXIF data + GraphicNativeMetadata aMetadata; + if (aMetadata.read(rStream)) + { + Degree10 aRotation = aMetadata.getRotation(); + if (aRotation) + { + GraphicNativeTransform aTransform(rGraphic); + aTransform.rotate(aRotation); + } + } + return aReturnCode; } diff --git a/vcl/source/filter/png/PngImageReader.cxx b/vcl/source/filter/png/PngImageReader.cxx index 82d1827543bd..b0312c72610a 100644 --- a/vcl/source/filter/png/PngImageReader.cxx +++ b/vcl/source/filter/png/PngImageReader.cxx @@ -457,7 +457,7 @@ bool reader(SvStream& rStream, ImportOutput& rImportOutput, case PNG_COLOR_TYPE_RGB: aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N24_BPP); break; - case PNG_COLOR_TYPE_RGBA: + case PNG_COLOR_TYPE_RGB_ALPHA: aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N32_BPP); break; case PNG_COLOR_TYPE_GRAY: @@ -515,44 +515,26 @@ bool reader(SvStream& rStream, ImportOutput& rImportOutput, { size_t aRowSizeBytes = png_get_rowbytes(pPng, pInfo); - if (nNumberOfPasses == 1) + // Reuse the bitmap data as a buffer for reading the png data. The + // order is possibly incorrect from what it should finally be and + // in any case the result is not premultiplied. + for (int pass = 0; pass < nNumberOfPasses; pass++) { - // optimise the common case, where we can use a buffer of only a single row - std::vector<png_byte> aRow(aRowSizeBytes, 0); for (png_uint_32 y = 0; y < height; y++) { Scanline pScanline = pWriteAccess->GetScanline(y); - png_bytep pRow = aRow.data(); - png_read_row(pPng, pRow, nullptr); - for (size_t i = 0; i < aRowSizeBytes; i += 4) - { - Color aCol(ColorAlpha, pRow[i + 3], pRow[i + 0], pRow[i + 1], pRow[i + 2]); - pWriteAccess->SetPixelOnData(pScanline, i / 4, aCol); - } + png_read_row(pPng, pScanline, nullptr); } } - else + // Fix order and do premultiplication + for (png_uint_32 y = 0; y < height; y++) { - std::vector<std::vector<png_byte>> aRows(height); - for (auto& rRow : aRows) - rRow.resize(aRowSizeBytes, 0); - for (int pass = 0; pass < nNumberOfPasses; pass++) - { - for (png_uint_32 y = 0; y < height; y++) - { - png_bytep pRow = aRows[y].data(); - png_read_row(pPng, pRow, nullptr); - } - } - for (png_uint_32 y = 0; y < height; y++) + Scanline pScanline = pWriteAccess->GetScanline(y); + for (size_t i = 0; i < aRowSizeBytes; i += 4) { - Scanline pScanline = pWriteAccess->GetScanline(y); - png_bytep pRow = aRows[y].data(); - for (size_t i = 0; i < aRowSizeBytes; i += 4) - { - Color aCol(ColorAlpha, pRow[i + 3], pRow[i + 0], pRow[i + 1], pRow[i + 2]); - pWriteAccess->SetPixelOnData(pScanline, i / 4, aCol); - } + Color aCol(ColorAlpha, pScanline[i + 3], pScanline[i + 0], pScanline[i + 1], + pScanline[i + 2]); + pWriteAccess->SetPixelOnData(pScanline, i / 4, aCol); } } } diff --git a/vcl/source/filter/png/PngImageWriter.cxx b/vcl/source/filter/png/PngImageWriter.cxx index da0dde32f93a..b0eb6ec0fb0e 100644 --- a/vcl/source/filter/png/PngImageWriter.cxx +++ b/vcl/source/filter/png/PngImageWriter.cxx @@ -208,7 +208,7 @@ static bool pngWrite(SvStream& rStream, const Graphic& rGraphic, int nCompressio case ScanlineFormat::N32BitTcBgra: { assert(aBitmap.HasAlpha()); - colorType = PNG_COLOR_TYPE_RGBA; + colorType = PNG_COLOR_TYPE_RGB_ALPHA; bitDepth = 8; bCombineChannels = true; break; @@ -224,7 +224,7 @@ static bool pngWrite(SvStream& rStream, const Graphic& rGraphic, int nCompressio case ScanlineFormat::N32BitTcRgba: { assert(aBitmap.HasAlpha()); - colorType = PNG_COLOR_TYPE_RGBA; + colorType = PNG_COLOR_TYPE_RGB_ALPHA; bitDepth = 8; bCombineChannels = true; break; diff --git a/vcl/source/gdi/metaact.cxx b/vcl/source/gdi/metaact.cxx index 20406866e658..570083705f4b 100644 --- a/vcl/source/gdi/metaact.cxx +++ b/vcl/source/gdi/metaact.cxx @@ -113,10 +113,6 @@ void MetaAction::Scale( double, double ) { } -MetaPixelAction::MetaPixelAction() : - MetaAction(MetaActionType::PIXEL) -{} - MetaPixelAction::~MetaPixelAction() {} @@ -146,10 +142,6 @@ void MetaPixelAction::Scale( double fScaleX, double fScaleY ) ImplScalePoint( maPt, fScaleX, fScaleY ); } -MetaPointAction::MetaPointAction() : - MetaAction(MetaActionType::POINT) -{} - MetaPointAction::~MetaPointAction() {} @@ -178,10 +170,6 @@ void MetaPointAction::Scale( double fScaleX, double fScaleY ) ImplScalePoint( maPt, fScaleX, fScaleY ); } -MetaLineAction::MetaLineAction() : - MetaAction(MetaActionType::LINE) -{} - MetaLineAction::~MetaLineAction() {} @@ -225,10 +213,6 @@ void MetaLineAction::Scale( double fScaleX, double fScaleY ) ImplScaleLineInfo( maLineInfo, fScaleX, fScaleY ); } -MetaRectAction::MetaRectAction() : - MetaAction(MetaActionType::RECT) -{} - MetaRectAction::~MetaRectAction() {} @@ -284,12 +268,6 @@ void MetaRectAction::Scale( double fScaleX, double fScaleY ) ImplScaleRect( maRect, fScaleX, fScaleY ); } -MetaRoundRectAction::MetaRoundRectAction() : - MetaAction ( MetaActionType::ROUNDRECT ), - mnHorzRound ( 0 ), - mnVertRound ( 0 ) -{} - MetaRoundRectAction::~MetaRoundRectAction() {} @@ -323,10 +301,6 @@ void MetaRoundRectAction::Scale( double fScaleX, double fScaleY ) mnVertRound = basegfx::fround<sal_uInt32>(mnVertRound * fabs(fScaleY)); } -MetaEllipseAction::MetaEllipseAction() : - MetaAction(MetaActionType::ELLIPSE) -{} - MetaEllipseAction::~MetaEllipseAction() {} @@ -355,10 +329,6 @@ void MetaEllipseAction::Scale( double fScaleX, double fScaleY ) ImplScaleRect( maRect, fScaleX, fScaleY ); } -MetaArcAction::MetaArcAction() : - MetaAction(MetaActionType::ARC) -{} - MetaArcAction::~MetaArcAction() {} @@ -397,10 +367,6 @@ void MetaArcAction::Scale( double fScaleX, double fScaleY ) ImplScalePoint( maEndPt, fScaleX, fScaleY ); } -MetaPieAction::MetaPieAction() : - MetaAction(MetaActionType::PIE) -{} - MetaPieAction::~MetaPieAction() {} @@ -439,10 +405,6 @@ void MetaPieAction::Scale( double fScaleX, double fScaleY ) ImplScalePoint( maEndPt, fScaleX, fScaleY ); } -MetaChordAction::MetaChordAction() : - MetaAction(MetaActionType::CHORD) -{} - MetaChordAction::~MetaChordAction() {} @@ -478,10 +440,6 @@ void MetaChordAction::Scale( double fScaleX, double fScaleY ) ImplScalePoint( maEndPt, fScaleX, fScaleY ); } -MetaPolyLineAction::MetaPolyLineAction() : - MetaAction(MetaActionType::POLYLINE) -{} - MetaPolyLineAction::~MetaPolyLineAction() {} @@ -523,10 +481,6 @@ void MetaPolyLineAction::Scale( double fScaleX, double fScaleY ) ImplScaleLineInfo( maLineInfo, fScaleX, fScaleY ); } -MetaPolygonAction::MetaPolygonAction() : - MetaAction(MetaActionType::POLYGON) -{} - MetaPolygonAction::~MetaPolygonAction() {} @@ -555,10 +509,6 @@ void MetaPolygonAction::Scale( double fScaleX, double fScaleY ) ImplScalePoly( maPoly, fScaleX, fScaleY ); } -MetaPolyPolygonAction::MetaPolyPolygonAction() : - MetaAction(MetaActionType::POLYPOLYGON) -{} - MetaPolyPolygonAction::~MetaPolyPolygonAction() {} @@ -588,12 +538,6 @@ void MetaPolyPolygonAction::Scale( double fScaleX, double fScaleY ) ImplScalePoly( maPolyPoly[ i ], fScaleX, fScaleY ); } -MetaTextAction::MetaTextAction() : - MetaAction ( MetaActionType::TEXT ), - mnIndex ( 0 ), - mnLen ( 0 ) -{} - MetaTextAction::~MetaTextAction() {} @@ -629,12 +573,6 @@ void MetaTextAction::Scale( double fScaleX, double fScaleY ) ImplScalePoint( maPt, fScaleX, fScaleY ); } -MetaTextArrayAction::MetaTextArrayAction() : - MetaAction ( MetaActionType::TEXTARRAY ), - mnIndex ( 0 ), - mnLen ( 0 ) -{} - MetaTextArrayAction::MetaTextArrayAction(const MetaTextArrayAction& rAction) : MetaAction(MetaActionType::TEXTARRAY) , maStartPt(rAction.maStartPt) @@ -737,13 +675,6 @@ void MetaTextArrayAction::Scale( double fScaleX, double fScaleY ) } } -MetaStretchTextAction::MetaStretchTextAction() : - MetaAction ( MetaActionType::STRETCHTEXT ), - mnWidth ( 0 ), - mnIndex ( 0 ), - mnLen ( 0 ) -{} - MetaStretchTextAction::~MetaStretchTextAction() {} @@ -798,10 +729,6 @@ void MetaStretchTextAction::Scale( double fScaleX, double fScaleY ) ImplScalePoint( maPt, fScaleX, fScaleY ); mnWidth = basegfx::fround<sal_uInt32>(mnWidth * fabs(fScaleX)); } -MetaTextRectAction::MetaTextRectAction() : - MetaAction ( MetaActionType::TEXTRECT ), - mnStyle ( DrawTextFlags::NONE ) -{} MetaTextRectAction::~MetaTextRectAction() {} @@ -837,14 +764,6 @@ void MetaTextRectAction::Scale( double fScaleX, double fScaleY ) ImplScaleRect( maRect, fScaleX, fScaleY ); } -MetaTextLineAction::MetaTextLineAction() : - MetaAction ( MetaActionType::TEXTLINE ), - mnWidth ( 0 ), - meStrikeout ( STRIKEOUT_NONE ), - meUnderline ( LINESTYLE_NONE ), - meOverline ( LINESTYLE_NONE ) -{} - MetaTextLineAction::~MetaTextLineAction() {} @@ -889,10 +808,6 @@ void MetaTextLineAction::Scale( double fScaleX, double fScaleY ) mnWidth = basegfx::fround<tools::Long>(mnWidth * fabs(fScaleX)); } -MetaBmpAction::MetaBmpAction() : - MetaAction(MetaActionType::BMP) -{} - MetaBmpAction::~MetaBmpAction() {} @@ -925,10 +840,6 @@ void MetaBmpAction::Scale( double fScaleX, double fScaleY ) ImplScalePoint( maPt, fScaleX, fScaleY ); } -MetaBmpScaleAction::MetaBmpScaleAction() : - MetaAction(MetaActionType::BMPSCALE) -{} - MetaBmpScaleAction::~MetaBmpScaleAction() {} @@ -1012,10 +923,6 @@ void MetaBmpScaleAction::Scale( double fScaleX, double fScaleY ) maSz = aRectangle.GetSize(); } -MetaBmpScalePartAction::MetaBmpScalePartAction() : - MetaAction(MetaActionType::BMPSCALEPART) -{} - MetaBmpScalePartAction::~MetaBmpScalePartAction() {} @@ -1059,10 +966,6 @@ void MetaBmpScalePartAction::Scale( double fScaleX, double fScaleY ) maDstSz = aRectangle.GetSize(); } -MetaBmpExAction::MetaBmpExAction() : - MetaAction(MetaActionType::BMPEX) -{} - MetaBmpExAction::~MetaBmpExAction() {} @@ -1092,10 +995,6 @@ void MetaBmpExAction::Scale( double fScaleX, double fScaleY ) ImplScalePoint( maPt, fScaleX, fScaleY ); } -MetaBmpExScaleAction::MetaBmpExScaleAction() : - MetaAction(MetaActionType::BMPEXSCALE) -{} - MetaBmpExScaleAction::~MetaBmpExScaleAction() {} @@ -1135,10 +1034,6 @@ void MetaBmpExScaleAction::Scale( double fScaleX, double fScaleY ) maSz = aRectangle.GetSize(); } -MetaBmpExScalePartAction::MetaBmpExScalePartAction() : - MetaAction(MetaActionType::BMPEXSCALEPART) -{} - MetaBmpExScalePartAction::~MetaBmpExScalePartAction() {} @@ -1179,10 +1074,6 @@ void MetaBmpExScalePartAction::Scale( double fScaleX, double fScaleY ) maDstSz = aRectangle.GetSize(); } -MetaMaskAction::MetaMaskAction() : - MetaAction(MetaActionType::MASK) -{} - MetaMaskAction::~MetaMaskAction() {} @@ -1217,10 +1108,6 @@ void MetaMaskAction::Scale( double fScaleX, double fScaleY ) ImplScalePoint( maPt, fScaleX, fScaleY ); } -MetaMaskScaleAction::MetaMaskScaleAction() : - MetaAction(MetaActionType::MASKSCALE) -{} - MetaMaskScaleAction::~MetaMaskScaleAction() {} @@ -1259,10 +1146,6 @@ void MetaMaskScaleAction::Scale( double fScaleX, double fScaleY ) maSz = aRectangle.GetSize(); } -MetaMaskScalePartAction::MetaMaskScalePartAction() : - MetaAction(MetaActionType::MASKSCALEPART) -{} - MetaMaskScalePartAction::~MetaMaskScalePartAction() {} @@ -1305,10 +1188,6 @@ void MetaMaskScalePartAction::Scale( double fScaleX, double fScaleY ) maDstSz = aRectangle.GetSize(); } -MetaGradientAction::MetaGradientAction() : - MetaAction(MetaActionType::GRADIENT) -{} - MetaGradientAction::~MetaGradientAction() {} @@ -1338,10 +1217,6 @@ void MetaGradientAction::Scale( double fScaleX, double fScaleY ) ImplScaleRect( maRect, fScaleX, fScaleY ); } -MetaGradientExAction::MetaGradientExAction() : - MetaAction ( MetaActionType::GRADIENTEX ) -{} - MetaGradientExAction::MetaGradientExAction( tools::PolyPolygon aPolyPoly, Gradient aGradient ) : MetaAction ( MetaActionType::GRADIENTEX ), maPolyPoly (std::move( aPolyPoly )), @@ -1375,10 +1250,6 @@ void MetaGradientExAction::Scale( double fScaleX, double fScaleY ) ImplScalePoly( maPolyPoly[ i ], fScaleX, fScaleY ); } -MetaHatchAction::MetaHatchAction() : - MetaAction(MetaActionType::HATCH) -{} - MetaHatchAction::~MetaHatchAction() {} @@ -1414,10 +1285,6 @@ void MetaHatchAction::Scale( double fScaleX, double fScaleY ) ImplScalePoly( maPolyPoly[ i ], fScaleX, fScaleY ); } -MetaWallpaperAction::MetaWallpaperAction() : - MetaAction(MetaActionType::WALLPAPER) -{} - MetaWallpaperAction::~MetaWallpaperAction() {} @@ -1448,11 +1315,6 @@ void MetaWallpaperAction::Scale( double fScaleX, double fScaleY ) ImplScaleRect( maRect, fScaleX, fScaleY ); } -MetaClipRegionAction::MetaClipRegionAction() : - MetaAction ( MetaActionType::CLIPREGION ), - mbClip ( false ) -{} - MetaClipRegionAction::~MetaClipRegionAction() {} @@ -1485,10 +1347,6 @@ void MetaClipRegionAction::Scale( double fScaleX, double fScaleY ) maRegion.Scale( fScaleX, fScaleY ); } -MetaISectRectClipRegionAction::MetaISectRectClipRegionAction() : - MetaAction(MetaActionType::ISECTRECTCLIPREGION) -{} - MetaISectRectClipRegionAction::~MetaISectRectClipRegionAction() {} @@ -1517,10 +1375,6 @@ void MetaISectRectClipRegionAction::Scale( double fScaleX, double fScaleY ) ImplScaleRect( maRect, fScaleX, fScaleY ); } -MetaISectRegionClipRegionAction::MetaISectRegionClipRegionAction() : - MetaAction(MetaActionType::ISECTREGIONCLIPREGION) -{} - MetaISectRegionClipRegionAction::~MetaISectRegionClipRegionAction() {} @@ -1553,12 +1407,6 @@ void MetaISectRegionClipRegionAction::Scale( double fScaleX, double fScaleY ) maRegion.Scale( fScaleX, fScaleY ); } -MetaMoveClipRegionAction::MetaMoveClipRegionAction() : - MetaAction ( MetaActionType::MOVECLIPREGION ), - mnHorzMove ( 0 ), - mnVertMove ( 0 ) -{} - MetaMoveClipRegionAction::~MetaMoveClipRegionAction() {} @@ -1586,11 +1434,6 @@ void MetaMoveClipRegionAction::Scale( double fScaleX, double fScaleY ) mnVertMove = basegfx::fround<tools::Long>(mnVertMove * fScaleY); } -MetaLineColorAction::MetaLineColorAction() : - MetaAction ( MetaActionType::LINECOLOR ), - mbSet ( false ) -{} - MetaLineColorAction::~MetaLineColorAction() {} @@ -1613,11 +1456,6 @@ rtl::Reference<MetaAction> MetaLineColorAction::Clone() const return new MetaLineColorAction( *this ); } -MetaFillColorAction::MetaFillColorAction() : - MetaAction ( MetaActionType::FILLCOLOR ), - mbSet ( false ) -{} - MetaFillColorAction::~MetaFillColorAction() {} @@ -1640,10 +1478,6 @@ rtl::Reference<MetaAction> MetaFillColorAction::Clone() const return new MetaFillColorAction( *this ); } -MetaTextColorAction::MetaTextColorAction() : - MetaAction(MetaActionType::TEXTCOLOR) -{} - MetaTextColorAction::~MetaTextColorAction() {} @@ -1662,11 +1496,6 @@ rtl::Reference<MetaAction> MetaTextColorAction::Clone() const return new MetaTextColorAction( *this ); } -MetaTextFillColorAction::MetaTextFillColorAction() : - MetaAction ( MetaActionType::TEXTFILLCOLOR ), - mbSet ( false ) -{} - MetaTextFillColorAction::~MetaTextFillColorAction() {} @@ -1689,11 +1518,6 @@ rtl::Reference<MetaAction> MetaTextFillColorAction::Clone() const return new MetaTextFillColorAction( *this ); } -MetaTextLineColorAction::MetaTextLineColorAction() : - MetaAction ( MetaActionType::TEXTLINECOLOR ), - mbSet ( false ) -{} - MetaTextLineColorAction::~MetaTextLineColorAction() {} @@ -1716,11 +1540,6 @@ rtl::Reference<MetaAction> MetaTextLineColorAction::Clone() const return new MetaTextLineColorAction( *this ); } -MetaOverlineColorAction::MetaOverlineColorAction() : - MetaAction ( MetaActionType::OVERLINECOLOR ), - mbSet ( false ) -{} - MetaOverlineColorAction::~MetaOverlineColorAction() {} @@ -1743,11 +1562,6 @@ rtl::Reference<MetaAction> MetaOverlineColorAction::Clone() const return new MetaOverlineColorAction( *this ); } -MetaTextAlignAction::MetaTextAlignAction() : - MetaAction ( MetaActionType::TEXTALIGN ), - maAlign ( ALIGN_TOP ) -{} - MetaTextAlignAction::~MetaTextAlignAction() {} @@ -1796,10 +1610,6 @@ void MetaMapModeAction::Scale( double fScaleX, double fScaleY ) maMapMode.SetOrigin( aPoint ); } -MetaFontAction::MetaFontAction() : - MetaAction(MetaActionType::FONT) -{} - MetaFontAction::~MetaFontAction() {} @@ -1837,11 +1647,6 @@ void MetaFontAction::Scale( double fScaleX, double fScaleY ) maFont.SetFontSize( aSize ); } -MetaPushAction::MetaPushAction() : - MetaAction ( MetaActionType::PUSH ), - mnFlags ( vcl::PushFlags::NONE ) -{} - MetaPushAction::~MetaPushAction() {} @@ -1877,11 +1682,6 @@ rtl::Reference<MetaAction> MetaPopAction::Clone() const return new MetaPopAction( *this ); } -MetaRasterOpAction::MetaRasterOpAction() : - MetaAction ( MetaActionType::RASTEROP ), - meRasterOp ( RasterOp::OverPaint ) -{} - MetaRasterOpAction::~MetaRasterOpAction() {} @@ -1901,11 +1701,6 @@ rtl::Reference<MetaAction> MetaRasterOpAction::Clone() const return new MetaRasterOpAction( *this ); } -MetaTransparentAction::MetaTransparentAction() : - MetaAction ( MetaActionType::Transparent ), - mnTransPercent ( 0 ) -{} - MetaTransparentAction::~MetaTransparentAction() {} @@ -1936,10 +1731,6 @@ void MetaTransparentAction::Scale( double fScaleX, double fScaleY ) ImplScalePoly( maPolyPoly[ i ], fScaleX, fScaleY ); } -MetaFloatTransparentAction::MetaFloatTransparentAction() : - MetaAction(MetaActionType::FLOATTRANSPARENT) -{} - MetaFloatTransparentAction::~MetaFloatTransparentAction() {} @@ -1977,10 +1768,6 @@ void MetaFloatTransparentAction::Scale( double fScaleX, double fScaleY ) maSize = aRectangle.GetSize(); } -MetaEPSAction::MetaEPSAction() : - MetaAction(MetaActionType::EPS) -{} - MetaEPSAction::~MetaEPSAction() {} @@ -2016,11 +1803,6 @@ void MetaEPSAction::Scale( double fScaleX, double fScaleY ) maSize = aRectangle.GetSize(); } -MetaRefPointAction::MetaRefPointAction() : - MetaAction ( MetaActionType::REFPOINT ), - mbSet ( false ) -{} - MetaRefPointAction::~MetaRefPointAction() {} @@ -2043,13 +1825,6 @@ rtl::Reference<MetaAction> MetaRefPointAction::Clone() const return new MetaRefPointAction( *this ); } -MetaCommentAction::MetaCommentAction() : - MetaAction ( MetaActionType::COMMENT ), - mnValue ( 0 ) -{ - ImplInitDynamicData( nullptr, 0UL ); -} - MetaCommentAction::MetaCommentAction( const MetaCommentAction& rAct ) : MetaAction ( MetaActionType::COMMENT ), maComment ( rAct.maComment ), @@ -2215,11 +1990,6 @@ void MetaCommentAction::Scale( double fXScale, double fYScale ) } } -MetaLayoutModeAction::MetaLayoutModeAction() : - MetaAction ( MetaActionType::LAYOUTMODE ), - mnLayoutMode( vcl::text::ComplexTextLayoutFlags::Default ) -{} - MetaLayoutModeAction::~MetaLayoutModeAction() {} @@ -2238,11 +2008,6 @@ rtl::Reference<MetaAction> MetaLayoutModeAction::Clone() const return new MetaLayoutModeAction( *this ); } -MetaTextLanguageAction::MetaTextLanguageAction() : - MetaAction ( MetaActionType::TEXTLANGUAGE ), - meTextLanguage( LANGUAGE_DONTKNOW ) -{} - MetaTextLanguageAction::~MetaTextLanguageAction() {} diff --git a/vcl/source/gdi/pdfbuildin_fonts.cxx b/vcl/source/pdf/pdfbuildin_fonts.cxx index 823c429a5977..823c429a5977 100644 --- a/vcl/source/gdi/pdfbuildin_fonts.cxx +++ b/vcl/source/pdf/pdfbuildin_fonts.cxx diff --git a/vcl/source/treelist/svtabbx.cxx b/vcl/source/treelist/svtabbx.cxx index 8607d40a1cfb..ebed8e79ac8e 100644 --- a/vcl/source/treelist/svtabbx.cxx +++ b/vcl/source/treelist/svtabbx.cxx @@ -1100,10 +1100,10 @@ rtl::Reference<comphelper::OAccessible> SvHeaderTabListBox::CreateAccessible() if (m_xAccessible.is()) return m_xAccessible; - Reference< XAccessible > xAccParent = GetAccessibleParent(); - if ( xAccParent.is() ) + rtl::Reference<comphelper::OAccessible> pAccParent = GetAccessibleParent(); + if (pAccParent.is()) { - m_xAccessible = new AccessibleTabListBox(xAccParent, *this); + m_xAccessible = new AccessibleTabListBox(pAccParent, *this); return m_xAccessible; } return nullptr; diff --git a/vcl/source/window/accessibility.cxx b/vcl/source/window/accessibility.cxx index 52ab05b313d0..e65b4940c22f 100644 --- a/vcl/source/window/accessibility.cxx +++ b/vcl/source/window/accessibility.cxx @@ -679,44 +679,4 @@ bool Window::IsAccessibilityEventsSuppressed() } /* namespace vcl */ -uno::Reference<accessibility::XAccessibleEditableText> -FindFocusedEditableText(uno::Reference<accessibility::XAccessibleContext> const& xContext) -{ - if (!xContext.is()) - return uno::Reference<accessibility::XAccessibleEditableText>(); - - sal_Int64 nState = xContext->getAccessibleStateSet(); - if (nState & accessibility::AccessibleStateType::FOCUSED) - { - uno::Reference<accessibility::XAccessibleEditableText> xText(xContext, uno::UNO_QUERY); - if (xText.is()) - return xText; - if (nState & accessibility::AccessibleStateType::MANAGES_DESCENDANTS) - return uno::Reference<accessibility::XAccessibleEditableText>(); - } - - bool bSafeToIterate = true; - sal_Int64 nCount = xContext->getAccessibleChildCount(); - if (nCount < 0 || nCount > SAL_MAX_UINT16 /* slow enough for anyone */) - bSafeToIterate = false; - if (!bSafeToIterate) - return uno::Reference<accessibility::XAccessibleEditableText>(); - - for (sal_Int64 i = 0; i < xContext->getAccessibleChildCount(); ++i) - { - uno::Reference<accessibility::XAccessible> xChild = xContext->getAccessibleChild(i); - if (!xChild.is()) - continue; - uno::Reference<accessibility::XAccessibleContext> xChildContext - = xChild->getAccessibleContext(); - if (!xChildContext.is()) - continue; - uno::Reference<accessibility::XAccessibleEditableText> xText - = FindFocusedEditableText(xChildContext); - if (xText.is()) - return xText; - } - return uno::Reference<accessibility::XAccessibleEditableText>(); -} - /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/window/cursor.cxx b/vcl/source/window/cursor.cxx index 6c9d7577aa41..2437fc321348 100644 --- a/vcl/source/window/cursor.cxx +++ b/vcl/source/window/cursor.cxx @@ -241,7 +241,38 @@ void vcl::Cursor::ImplDoShow( bool bDrawDirect, bool bRestore ) } } -void vcl::Cursor::LOKNotify( vcl::Window* pWindow, const OUString& rAction ) +namespace +{ + +tools::Rectangle calcualteCursorRect(Point const& rPosition, Size const rSize, vcl::Window* pWindow, vcl::Window* pParent) +{ + Point aPositionPixel = pWindow->LogicToPixel(rPosition); + const tools::Long nX = pWindow->GetOutOffXPixel() + aPositionPixel.X() - pParent->GetOutOffXPixel(); + const tools::Long nY = pWindow->GetOutOffYPixel() + aPositionPixel.Y() - pParent->GetOutOffYPixel(); + + Size aSizePixel = pWindow->LogicToPixel(rSize); + if (!aSizePixel.Width()) + aSizePixel.setWidth( pWindow->GetSettings().GetStyleSettings().GetCursorSize() ); + + Point aPosition(nX, nY); + + if (pWindow->IsRTLEnabled() && pWindow->GetOutDev() && pParent->GetOutDev() + && !pWindow->GetOutDev()->ImplIsAntiparallel()) + pParent->GetOutDev()->ReMirror(aPosition); + + if (!pWindow->IsRTLEnabled() && pWindow->GetOutDev() && pParent->GetOutDev() + && pWindow->GetOutDev()->ImplIsAntiparallel()) + { + pWindow->GetOutDev()->ReMirror(aPosition); + pParent->GetOutDev()->ReMirror(aPosition); + } + + return tools::Rectangle(aPosition, aSizePixel); +} + +} // end anonymous namespace + +void vcl::Cursor::LOKNotify(vcl::Window* pWindow, const OUString& rAction) { VclPtr<vcl::Window> pParent = pWindow->GetParentWithLOKNotifier(); if (!pParent) @@ -251,39 +282,40 @@ void vcl::Cursor::LOKNotify( vcl::Window* pWindow, const OUString& rAction ) assert(mpData && "Require ImplCursorData"); assert(comphelper::LibreOfficeKit::isActive()); - if (comphelper::LibreOfficeKit::isDialogPainting()) - return; - const vcl::ILibreOfficeKitNotifier* pNotifier = pParent->GetLOKNotifier(); - std::vector<vcl::LOKPayloadItem> aItems; - if (rAction == "cursor_visible") - aItems.emplace_back("visible", mpData->mbCurVisible ? "true" : "false"); - else if (rAction == "cursor_invalidate") - { - const tools::Long nX = pWindow->GetOutOffXPixel() + pWindow->LogicToPixel(GetPos()).X() - pParent->GetOutOffXPixel(); - const tools::Long nY = pWindow->GetOutOffYPixel() + pWindow->LogicToPixel(GetPos()).Y() - pParent->GetOutOffYPixel(); - Size aSize = pWindow->LogicToPixel(GetSize()); - if (!aSize.Width()) - aSize.setWidth( pWindow->GetSettings().GetStyleSettings().GetCursorSize() ); - - Point aPos(nX, nY); - if (pWindow->IsRTLEnabled() && pWindow->GetOutDev() && pParent->GetOutDev() - && !pWindow->GetOutDev()->ImplIsAntiparallel()) - pParent->GetOutDev()->ReMirror(aPos); - - if (!pWindow->IsRTLEnabled() && pWindow->GetOutDev() && pParent->GetOutDev() - && pWindow->GetOutDev()->ImplIsAntiparallel()) + if (pWindow->IsFormControl() || (pWindow->GetParent() && pWindow->GetParent()->IsFormControl())) + { + if (rAction == "cursor_invalidate") { - pWindow->GetOutDev()->ReMirror(aPos); - pParent->GetOutDev()->ReMirror(aPos); + tools::Rectangle aRect; + if (pWindow->IsFormControl()) + aRect = calcualteCursorRect(GetPos(), GetSize(), pWindow, pWindow->GetParent()); + else + aRect = calcualteCursorRect(GetPos(), GetSize(), pWindow, pWindow->GetParent()->GetParent()); + + OutputDevice* pDevice = mpData->mpWindow->GetOutDev(); + const tools::Rectangle aRectTwip = pDevice->PixelToLogic(aRect, MapMode(MapUnit::MapTwip)); + pNotifier->notifyCursorInvalidation(&aRectTwip, true); } - - const tools::Rectangle aRect(aPos, aSize); - aItems.emplace_back("rectangle", aRect.toString()); } + else + { + if (comphelper::LibreOfficeKit::isDialogPainting()) + return; - pNotifier->notifyWindow(pParent->GetLOKWindowId(), rAction, aItems); + std::vector<vcl::LOKPayloadItem> aItems; + if (rAction == "cursor_visible") + { + aItems.emplace_back("visible", mpData->mbCurVisible ? "true" : "false"); + } + else if (rAction == "cursor_invalidate") + { + const tools::Rectangle aRect = calcualteCursorRect(GetPos(), GetSize(), pWindow, pParent); + aItems.emplace_back("rectangle", aRect.toString()); + } + pNotifier->notifyWindow(pParent->GetLOKWindowId(), rAction, aItems); + } } bool vcl::Cursor::ImplDoHide( bool bSuspend ) diff --git a/vcl/source/window/paint.cxx b/vcl/source/window/paint.cxx index 9ecd35cf39c3..e01a1cc0ddfd 100644 --- a/vcl/source/window/paint.cxx +++ b/vcl/source/window/paint.cxx @@ -1228,6 +1228,12 @@ void Window::PixelInvalidate(const tools::Rectangle* pRectangle) pNotifier->notifyWindow(GetLOKWindowId(), u"invalidate"_ustr, aPayload); } + else if (GetParent() && GetParent()->IsFormControl()) + { + const VclPtr<vcl::Window> pParent = GetParentWithLOKNotifier(); + if (pParent) + pParent->GetLOKNotifier()->notifyInvalidation(pRectangle); + } // Added for dialog items. Pass invalidation to the parent window. else if (VclPtr<vcl::Window> pParent = GetParentWithLOKNotifier()) { diff --git a/vcl/source/window/window.cxx b/vcl/source/window/window.cxx index e6fc3d032f62..f4a830888684 100644 --- a/vcl/source/window/window.cxx +++ b/vcl/source/window/window.cxx @@ -62,6 +62,7 @@ #include <dndeventdispatcher.hxx> #include <com/sun/star/accessibility/AccessibleRelation.hpp> +#include <com/sun/star/accessibility/AccessibleStateType.hpp> #include <com/sun/star/accessibility/XAccessible.hpp> #include <com/sun/star/accessibility/XAccessibleEditableText.hpp> #include <com/sun/star/awt/XVclWindowPeer.hpp> @@ -3801,6 +3802,46 @@ namespace { using namespace com::sun::star; + uno::Reference<accessibility::XAccessibleEditableText> + lcl_FindFocusedEditableText(uno::Reference<accessibility::XAccessibleContext> const& xContext) + { + if (!xContext.is()) + return uno::Reference<accessibility::XAccessibleEditableText>(); + + sal_Int64 nState = xContext->getAccessibleStateSet(); + if (nState & accessibility::AccessibleStateType::FOCUSED) + { + uno::Reference<accessibility::XAccessibleEditableText> xText(xContext, uno::UNO_QUERY); + if (xText.is()) + return xText; + if (nState & accessibility::AccessibleStateType::MANAGES_DESCENDANTS) + return uno::Reference<accessibility::XAccessibleEditableText>(); + } + + bool bSafeToIterate = true; + sal_Int64 nCount = xContext->getAccessibleChildCount(); + if (nCount < 0 || nCount > SAL_MAX_UINT16 /* slow enough for anyone */) + bSafeToIterate = false; + if (!bSafeToIterate) + return uno::Reference<accessibility::XAccessibleEditableText>(); + + for (sal_Int64 i = 0; i < xContext->getAccessibleChildCount(); ++i) + { + uno::Reference<accessibility::XAccessible> xChild = xContext->getAccessibleChild(i); + if (!xChild.is()) + continue; + uno::Reference<accessibility::XAccessibleContext> xChildContext + = xChild->getAccessibleContext(); + if (!xChildContext.is()) + continue; + uno::Reference<accessibility::XAccessibleEditableText> xText + = lcl_FindFocusedEditableText(xChildContext); + if (xText.is()) + return xText; + } + return uno::Reference<accessibility::XAccessibleEditableText>(); + } + uno::Reference<accessibility::XAccessibleEditableText> lcl_GetxText(vcl::Window *pFocusWin) { uno::Reference<accessibility::XAccessibleEditableText> xText; @@ -3808,7 +3849,7 @@ namespace { rtl::Reference<comphelper::OAccessible> pAccessible = pFocusWin->GetAccessible(); if (pAccessible.is()) - xText = FindFocusedEditableText(pAccessible); + xText = lcl_FindFocusedEditableText(pAccessible); } catch(const uno::Exception&) { diff --git a/vcl/source/window/window2.cxx b/vcl/source/window/window2.cxx index 7b1946360a47..93a81bb259a6 100644 --- a/vcl/source/window/window2.cxx +++ b/vcl/source/window/window2.cxx @@ -1045,6 +1045,17 @@ WindowType Window::GetType() const return WindowType::NONE; } +bool Window::IsFormControl() const +{ + return mpWindowImpl ? mpWindowImpl->mbIsFormControl : false; +} + +void Window::SetFormControl(bool bFormControl) +{ + if (mpWindowImpl) + mpWindowImpl->mbIsFormControl = bFormControl; +} + Dialog* Window::GetParentDialog() const { const vcl::Window *pWindow = this; diff --git a/vcl/source/window/winproc.cxx b/vcl/source/window/winproc.cxx index 801ef8499d9f..a75eb6ada250 100644 --- a/vcl/source/window/winproc.cxx +++ b/vcl/source/window/winproc.cxx @@ -2552,48 +2552,41 @@ static bool ImplHandleShowDialog( vcl::Window* pWindow, ShowDialogId nDialogId ) return ImplCallCommand( pWindow, CommandEventId::ShowDialog, &aCmdData ); } -static void ImplHandleSurroundingTextRequest( vcl::Window *pWindow, - OUString& rText, - Selection &rSelRange ) +static void ImplHandleSalSurroundingTextRequest( vcl::Window *pWindow, + SalSurroundingTextRequestEvent *pEvt ) { vcl::Window* pChild = ImplGetKeyInputWindow( pWindow ); - if ( !pChild ) { - rText.clear(); - rSelRange.setMin( 0 ); - rSelRange.setMax( 0 ); - } - else - { - rText = pChild->GetSurroundingText(); - Selection aSel = pChild->GetSurroundingTextSelection(); - rSelRange.setMin( aSel.Min() ); - rSelRange.setMax( aSel.Max() ); + pEvt->maText.clear(); + pEvt->mnStart = 0; + pEvt->mnEnd = 0; + return; } -} -static void ImplHandleSalSurroundingTextRequest( vcl::Window *pWindow, - SalSurroundingTextRequestEvent *pEvt ) -{ - Selection aSelRange; - ImplHandleSurroundingTextRequest( pWindow, pEvt->maText, aSelRange ); + pEvt->maText = pChild->GetSurroundingText(); + Selection aSelRange = pChild->GetSurroundingTextSelection(); - aSelRange.Normalize(); + sal_uLong nSelectionAnchorPos = 0; + sal_uLong nCursorPos = 0; if( aSelRange.Min() < 0 ) - pEvt->mnStart = 0; + nSelectionAnchorPos = 0; else if( aSelRange.Min() > pEvt->maText.getLength() ) - pEvt->mnStart = pEvt->maText.getLength(); + nSelectionAnchorPos = pEvt->maText.getLength(); else - pEvt->mnStart = aSelRange.Min(); + nSelectionAnchorPos = aSelRange.Min(); if( aSelRange.Max() < 0 ) - pEvt->mnStart = 0; + nCursorPos = 0; else if( aSelRange.Max() > pEvt->maText.getLength() ) - pEvt->mnEnd = pEvt->maText.getLength(); + nCursorPos = pEvt->maText.getLength(); else - pEvt->mnEnd = aSelRange.Max(); + nCursorPos = aSelRange.Max(); + + pEvt->mnCursorPos = nCursorPos; + pEvt->mnStart = std::min(nSelectionAnchorPos, nCursorPos); + pEvt->mnEnd = std::max(nSelectionAnchorPos, nCursorPos); } static void ImplHandleSalDeleteSurroundingTextRequest( vcl::Window *pWindow, diff --git a/vcl/unx/gtk3/fpicker/SalGtkPicker.cxx b/vcl/unx/gtk3/fpicker/SalGtkPicker.cxx index 5bbf94d7b74f..5fc439952722 100644 --- a/vcl/unx/gtk3/fpicker/SalGtkPicker.cxx +++ b/vcl/unx/gtk3/fpicker/SalGtkPicker.cxx @@ -20,8 +20,6 @@ #include <com/sun/star/frame/TerminationVetoException.hpp> #include <com/sun/star/lang/XMultiComponentFactory.hpp> #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp> -#include <com/sun/star/accessibility/XAccessibleContext.hpp> -#include <com/sun/star/accessibility/AccessibleRole.hpp> #include <osl/diagnose.h> #include <sal/log.hxx> #include <utility> |