accessibility/source/standard/vclxaccessibletoolbox.cxx | 11 configure.ac | 14 distro-configs/LibreOfficeLinux.conf | 1 filter/qa/unit/svg.cxx | 3 include/svx/sdrmasterpagedescriptor.hxx | 3 include/svx/sdrpageuser.hxx | 3 include/svx/svdpage.hxx | 1 sal/osl/unx/file.cxx | 6 sc/inc/SolverSettings.hxx | 117 ++- sc/qa/uitest/data/tdf129701.ods |binary sc/qa/uitest/pasteSpecial/tdf129701-PasteUnformated.py | 70 + sc/qa/unit/data/ods/tdf158735.ods |binary sc/qa/unit/ucalc_solver.cxx | 29 sc/source/core/data/SolverSettings.cxx | 220 +++++ sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx | 12 sc/source/ui/docshell/impex.cxx | 19 sd/qa/unit/export-tests-ooxml4.cxx | 45 + sd/source/filter/eppt/pptx-epptooxml.cxx | 38 - sw/qa/extras/htmlexport/data/char_border_and_font_color.fodt | 14 sw/qa/extras/htmlexport/htmlexport.cxx | 21 sw/qa/uitest/writer_tests7/tdf150443.py | 4 sw/source/core/edit/edsect.cxx | 3 sw/source/filter/html/css1atr.cxx | 6 sw/source/filter/html/htmlatr.cxx | 409 ++++------- sw/source/uibase/uiview/view2.cxx | 13 vcl/source/window/accessibility.cxx | 2 vcl/source/window/paint.cxx | 2 vcl/unx/gtk3/salnativewidgets-gtk.cxx | 20 28 files changed, 802 insertions(+), 284 deletions(-)
New commits: commit 0f599907ae96699006dbc05643a4e19592b64b52 Author: Mike Kaganski <[email protected]> AuthorDate: Sat Mar 9 08:03:08 2024 +0100 Commit: Andras Timar <[email protected]> CommitDate: Tue Mar 12 15:28:19 2024 +0100 Enable CVE tests on Windows by default Since commit c16969b9bc73fdd77e763299d6aea7b614e203e2 (tdf#84553 Detect and warn of Windows Antivirus., 2020-02-08), it is checked that antivirus is disabled in $SRC_ROOT and $BUILDDIR. This reverts commit a6b2c618cb02168bba950652367f494a1021cf53 (disable cve tests by default on windows, 2014-10-01). Change-Id: I3816e97cfb4559f7647167ed291b75468b03dc4d Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164612 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> (cherry picked from commit 4c65ab909598766e5859a5f0ce4bf55b23c9551b) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164621 Reviewed-by: Xisco Fauli <[email protected]> diff --git a/configure.ac b/configure.ac index a16f1204325d..7ba76360416d 100644 --- a/configure.ac +++ b/configure.ac @@ -11102,20 +11102,6 @@ dnl =================================================================== dnl Check for disabling cve_tests dnl =================================================================== AC_MSG_CHECKING([whether to execute CVE tests]) -# If not explicitly enabled or disabled, default -if test -z "$enable_cve_tests"; then - case "$OS" in - WNT) - # Default cves off for Windows with its wild and wonderful - # variety of AV software kicking in and panicking - enable_cve_tests=no - ;; - *) - # otherwise yes - enable_cve_tests=yes - ;; - esac -fi if test "$enable_cve_tests" = "no"; then AC_MSG_RESULT([no]) DISABLE_CVE_TESTS=TRUE diff --git a/filter/qa/unit/svg.cxx b/filter/qa/unit/svg.cxx index bbf956045e5d..308fe75f8a86 100644 --- a/filter/qa/unit/svg.cxx +++ b/filter/qa/unit/svg.cxx @@ -46,7 +46,8 @@ void SvgFilterTest::registerNamespaces(xmlXPathContextPtr& pXmlXpathCtx) CPPUNIT_TEST_FIXTURE(SvgFilterTest, testPreserveJpg) { -#if !defined(MACOSX) +// On Windows, SVGFilter::filterWriterOrCalc can't get current frame to obtain selection +#if !defined(MACOSX) && !defined(_WIN32) // Load a document with a jpeg image in it. loadFromFile(u"preserve-jpg.odt"); commit 8865e57b2d4e81a4f5196456d67a406b85070492 Author: Julien Nabet <[email protected]> AuthorDate: Fri Mar 8 17:35:00 2024 +0100 Commit: Andras Timar <[email protected]> CommitDate: Tue Mar 12 15:28:19 2024 +0100 tdf#160095: fix crash when using ALT+RETURN twice Change-Id: Idc4c84039115c8d88418246a17e281c26fb49b70 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164592 Tested-by: Jenkins Reviewed-by: Julien Nabet <[email protected]> (cherry picked from commit 82e6236cd6ba6f45aa913a3be606e6b00f81fe07) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164614 Reviewed-by: Xisco Fauli <[email protected]> diff --git a/sw/source/core/edit/edsect.cxx b/sw/source/core/edit/edsect.cxx index daaa7296e81e..070a37a90d2f 100644 --- a/sw/source/core/edit/edsect.cxx +++ b/sw/source/core/edit/edsect.cxx @@ -316,6 +316,9 @@ static const SwNode* lcl_SpecialInsertNode(const SwPosition* pCurrentPos) // find the table/section which is close if( pTableNode == nullptr ) { + if( pSectionNode == nullptr ) + return nullptr; + pInnermostNode = pSectionNode; pSection = &static_cast<const SwSectionNode*>(pSectionNode)->GetSection(); } commit 89879bfd0e46a9e98b0008e13425aa463bc34b28 Author: Sarper Akdemir <[email protected]> AuthorDate: Wed Mar 6 13:48:14 2024 +0300 Commit: Andras Timar <[email protected]> CommitDate: Tue Mar 12 15:28:19 2024 +0100 tdf#159931: pptx export: export each used slide layout for a master attempts to fix the slideLayout reference related regression from Idb6b88ebe87a83818d8eb27a1fa087652a002c0c. To correctly export the all used slideLayout instances for a given master, iterate through sdr::PageUsers of that master and figure out all used layouts. Change-Id: I0f58befac1ba4d5ec01aeedbb5f611c83683dcf8 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164468 Tested-by: Jenkins Reviewed-by: Balazs Varga <[email protected]> Reviewed-by: Sarper Akdemir <[email protected]> (cherry picked from commit a35831becee3781daf8628c48944660d31d84d8b) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164495 diff --git a/include/svx/sdrmasterpagedescriptor.hxx b/include/svx/sdrmasterpagedescriptor.hxx index d7eac22a1b5c..9be1e663bcff 100644 --- a/include/svx/sdrmasterpagedescriptor.hxx +++ b/include/svx/sdrmasterpagedescriptor.hxx @@ -22,6 +22,7 @@ #include <svx/sdrpageuser.hxx> #include <svx/svdsob.hxx> +#include <svx/svxdllapi.h> #include <memory> class SdrPageProperties; @@ -30,7 +31,7 @@ namespace sdr::contact { class ViewContact; } namespace sdr { - class MasterPageDescriptor final : public sdr::PageUser + class SVXCORE_DLLPUBLIC MasterPageDescriptor final : public sdr::PageUser { private: SdrPage& maOwnerPage; diff --git a/include/svx/sdrpageuser.hxx b/include/svx/sdrpageuser.hxx index 8c31a2936c7e..5557b2729777 100644 --- a/include/svx/sdrpageuser.hxx +++ b/include/svx/sdrpageuser.hxx @@ -21,6 +21,7 @@ #define INCLUDED_SVX_SDRPAGEUSER_HXX #include <vector> +#include <svx/svxdllapi.h> class SdrPage; @@ -30,7 +31,7 @@ class SdrPage; namespace sdr { - class PageUser + class SVXCORE_DLLPUBLIC PageUser { public: // this method is called from the destructor of the referenced page. diff --git a/include/svx/svdpage.hxx b/include/svx/svdpage.hxx index c70a2f1f4a2d..cb1e7f9f0b20 100644 --- a/include/svx/svdpage.hxx +++ b/include/svx/svdpage.hxx @@ -405,6 +405,7 @@ private: public: void AddPageUser(sdr::PageUser& rNewUser); void RemovePageUser(sdr::PageUser& rOldUser); + const sdr::PageUserVector& GetPageUsers() const { return maPageUsers; }; // SdrModel access on SdrPage level SdrModel& getSdrModelFromSdrPage() const { return mrSdrModelFromSdrPage; } diff --git a/sd/qa/unit/export-tests-ooxml4.cxx b/sd/qa/unit/export-tests-ooxml4.cxx index c4c061c50d2d..06cc2805d64d 100644 --- a/sd/qa/unit/export-tests-ooxml4.cxx +++ b/sd/qa/unit/export-tests-ooxml4.cxx @@ -1095,6 +1095,51 @@ CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf157740_slideMasters) assertXPath(pXmlDocContent, "/p:sldMaster/p:sldLayoutIdLst/p:sldLayoutId"_ostr, 1); } +CPPUNIT_TEST_FIXTURE(SdOOXMLExportTest4, testTdf159931_slideLayouts) +{ + createSdImpressDoc("odp/repeatBitmapMode.odp"); + save("Impress Office Open XML"); + + xmlDocUniquePtr pXmlDocRels1 = parseExport("ppt/slides/_rels/slide1.xml.rels"); + xmlDocUniquePtr pXmlDocRels2 = parseExport("ppt/slides/_rels/slide2.xml.rels"); + + assertXPath( + pXmlDocRels1, + "(/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout'])"_ostr); + + // the relative target e.g. "../slideLayouts/slideLayout2.xml" + OUString sRelativeLayoutPath1 = getXPathContent( + pXmlDocRels1, + "(/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout'])/@Target"_ostr); + + assertXPath( + pXmlDocRels2, + "(/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout'])"_ostr); + + // the relative target e.g. "../slideLayouts/slideLayout1.xml" + OUString sRelativeLayoutPath2 = getXPathContent( + pXmlDocRels2, + "(/rels:Relationships/rels:Relationship[@Type='http://schemas.openxmlformats.org/officeDocument/2006/relationships/slideLayout'])/@Target"_ostr); + + uno::Reference<packages::zip::XZipFileAccess2> xNameAccess + = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), + maTempFile.GetURL()); + + // Check that the referenced slideLayout files exist + // Without the accompanying fix in place, this test would have failed with: + // equality assertion failed + // - Expected: 1 + // - Actual : 0 + // i.e. the referenced slideLayout file was missing on export. + OUString sSlideLayoutName1 = sRelativeLayoutPath1.getToken(2, '/'); + OUString sSlideLayoutName2 = sRelativeLayoutPath2.getToken(2, '/'); + + CPPUNIT_ASSERT_EQUAL(true, + bool(xNameAccess->hasByName("ppt/slideLayouts/" + sSlideLayoutName1))); + CPPUNIT_ASSERT_EQUAL(true, + bool(xNameAccess->hasByName("ppt/slideLayouts/" + sSlideLayoutName2))); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptx-epptooxml.cxx b/sd/source/filter/eppt/pptx-epptooxml.cxx index 2148653dcd20..96f135ecf585 100644 --- a/sd/source/filter/eppt/pptx-epptooxml.cxx +++ b/sd/source/filter/eppt/pptx-epptooxml.cxx @@ -63,6 +63,7 @@ #include "../ppt/pptanimations.hxx" #include <i18nlangtag/languagetag.hxx> +#include <svx/sdrmasterpagedescriptor.hxx> #include <svx/svdpage.hxx> #include <svx/unoapi.hxx> #include <svx/svdogrp.hxx> @@ -1515,23 +1516,48 @@ void PowerPointExport::ImplWriteSlideMaster(sal_uInt32 nPageNum, Reference< XPro // use master's id type as they have same range, mso does that as well pFS->startElementNS(XML_p, XML_sldLayoutIdLst); - sal_Int32 nLayout = 0; - OUString aSlideName; - css::uno::Reference< css::beans::XPropertySet >xPagePropSet; + auto getLayoutsUsedForMaster = [](SdrPage* pMaster) -> std::unordered_set<sal_Int32> + { + if (!pMaster) + return {}; + + std::unordered_set<sal_Int32> aUsedLayouts{}; + for (const auto* pPageUser : pMaster->GetPageUsers()) + { + const auto* pMasterPageDescriptor + = dynamic_cast<const sdr::MasterPageDescriptor*>(pPageUser); + + if (!pMasterPageDescriptor) + continue; + + AutoLayout eLayout + = static_cast<SdPage&>(pMasterPageDescriptor->GetOwnerPage()).GetAutoLayout(); + aUsedLayouts.insert(eLayout); + } + return aUsedLayouts; + }; + + std::unordered_set<sal_Int32> aLayouts = getLayoutsUsedForMaster(pMasterPage); + + css::uno::Reference< css::beans::XPropertySet > xPagePropSet; xPagePropSet.set(mXDrawPage, UNO_QUERY); if (xPagePropSet.is()) { uno::Any aAny; if (GetPropertyValue(aAny, xPagePropSet, "SlideLayout")) - aAny >>= nLayout; + aLayouts.insert(aAny.get<sal_Int32>()); } + OUString aSlideName; Reference< XNamed > xNamed(mXDrawPage, UNO_QUERY); if (xNamed.is()) aSlideName = xNamed->getName(); - ImplWritePPTXLayout(nLayout, nPageNum, aSlideName); - AddLayoutIdAndRelation(pFS, GetLayoutFileId(nLayout, nPageNum)); + for (auto nLayout : aLayouts) + { + ImplWritePPTXLayout(nLayout, nPageNum, aSlideName); + AddLayoutIdAndRelation(pFS, GetLayoutFileId(nLayout, nPageNum)); + } pFS->endElementNS(XML_p, XML_sldLayoutIdLst); commit 57ba760482f166f025756eb81036a60d1a3e81e2 Author: Stephan Bergmann <[email protected]> AuthorDate: Fri Mar 8 08:38:44 2024 +0100 Commit: Andras Timar <[email protected]> CommitDate: Tue Mar 12 15:28:19 2024 +0100 Blind fix for Linux 32-bit builds ...which, according to <https://lists.freedesktop.org/archives/libreoffice/2024-March/091666.html> "32 bit build failure (smb, narrowing)", started to fail with > /<<PKGBUILDDIR>>/sal/osl/unx/file.cxx: In function ‘void osl_file_adjustLockFlags(const rtl::OString&, int*, sal_uInt32*)’: > /<<PKGBUILDDIR>>/sal/osl/unx/file.cxx:71:26: error: narrowing conversion of ‘4283649346’ from ‘unsigned int’ to ‘int’ [-Wnarrowing] > 71 | #define CIFS_SUPER_MAGIC 0xFF534D42 > | ^~~~~~~~~~ > /<<PKGBUILDDIR>>/sal/osl/unx/file.cxx:795:14: note: in expansion of macro ‘CIFS_SUPER_MAGIC’ > 795 | case CIFS_SUPER_MAGIC: > | ^~~~~~~~~~~~~~~~ etc. My Fedora 39 "Linux man-pages 6.05" statfs(2) man page explains about the struct statfs f_type field of __fsword_t type: "The __fsword_t type used for various fields in the statfs structure definition is a glibc internal type, not intended for public use. This leaves the programmer in a bit of a conundrum when trying to copy or compare these fields to local variables in a program. Using unsigned int for such variables suffices on most systems." But the underlying __FSWORD_T_TYPE looks like it is actually defined as a signed type in /usr/include/bits/typesizes.h. Change-Id: Ida3ae84031c4e48b0d6e69d76b66b4e4facfa1ae Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164561 Tested-by: René Engelhard <[email protected]> Reviewed-by: René Engelhard <[email protected]> Tested-by: Jenkins Reviewed-by: Stephan Bergmann <[email protected]> (cherry picked from commit 0f5dfaebd61b9cabbe9762865563c2296ebb0112) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164504 diff --git a/sal/osl/unx/file.cxx b/sal/osl/unx/file.cxx index ada4f29578d9..912d61a50614 100644 --- a/sal/osl/unx/file.cxx +++ b/sal/osl/unx/file.cxx @@ -68,9 +68,9 @@ #ifdef LINUX #include <sys/vfs.h> // As documented by the kernel -#define SMB_SUPER_MAGIC 0x517B -#define CIFS_SUPER_MAGIC 0xFF534D42 -#define SMB2_SUPER_MAGIC 0xFE534D42 +#define SMB_SUPER_MAGIC static_cast<__fsword_t>(0x517B) +#define CIFS_SUPER_MAGIC static_cast<__fsword_t>(0xFF534D42) +#define SMB2_SUPER_MAGIC static_cast<__fsword_t>(0xFE534D42) #endif namespace { commit a1c94168c820788e5fad72ae7c6af2355d395fa8 Author: Michael Stahl <[email protected]> AuthorDate: Tue Mar 5 19:21:21 2024 +0100 Commit: Andras Timar <[email protected]> CommitDate: Tue Mar 12 15:28:19 2024 +0100 vcl,accessibility: try to fix a crash while disposing SvxFontNameBox_Impl This happened in a 6.3 based branch, no idea how to reproduce it. Clearly the SvxFontNameBox_Impl is being disposed and in its base class Window::dispose() when a call to SvxFontNameBox_Impl::CreateAccessible() happens, which seems very suspicious; try to prevent that. mergedlo.dll!ImplListBox::InsertEntry(long nPos, const rtl::OUString & rStr) Zeile 2225 unter d:\lo mergedlo.dll!ComboBox::InsertEntry(const rtl::OUString & rStr, long nPos) Zeile 886 unter d:\lo mergedlo.dll!FontNameBox::Fill(const FontList * pList) Zeile 447 unter d:\lo [Inlineframe] mergedlo.dll!SvxFontNameBox_Impl::Fill(const FontList * pList) Zeile 236 unter d:\lo mergedlo.dll!lcl_GetDocFontList(const FontList * * ppFontList, SvxFontNameBox_Impl * pBox) Zeile 1290 unter d:\lo mergedlo.dll!SvxFontNameBox_Impl::FillList() Zeile 1331 unter d:\lo mergedlo.dll!SvxFontNameBox_Impl::CreateAccessible() Zeile 3739 unter d:\lo mergedlo.dll!vcl::Window::GetAccessible(bool bCreate) Zeile 129 unter d:\lo acclo.dll!VCLXAccessibleToolBox::getAccessibleChild(long i) Zeile 733 unter d:\lo acclo.dll!VCLXAccessibleToolBox::GetItemWindowAccessible(const VclWindowEvent & rVclWindowEvent) Zeile 784 unter d:\lo acclo.dll!VCLXAccessibleToolBox::GetChildAccessible(const VclWindowEvent & rVclWindowEvent) Zeile 795 unter d:\lo mergedlo.dll!VCLXAccessibleComponent::ProcessWindowChildEvent(const VclWindowEvent & rVclWindowEvent) Zeile 165 unter d:\lo acclo.dll!VCLXAccessibleToolBox::ProcessWindowChildEvent(const VclWindowEvent & rVclWindowEvent) Zeile 657 unter d:\lo mergedlo.dll!VCLXAccessibleComponent::WindowChildEventListener(VclWindowEvent & rEvent) Zeile 129 unter d:\lo mergedlo.dll!VCLXAccessibleComponent::LinkStubWindowChildEventListener(void * instance, VclWindowEvent & data) Zeile 118 unter d:\lo [Inlineframe] mergedlo.dll!Link<VclWindowEvent &,void>::Call(VclWindowEvent &) Zeile 84 unter d:\lo mergedlo.dll!vcl::Window::CallEventListeners(VclEventId nEvent, void * pData) Zeile 280 unter d:\lo mergedlo.dll!vcl::Window::ImplResetReallyVisible() Zeile 735 unter d:\lo mergedlo.dll!vcl::Window::ImplResetReallyVisible() Zeile 747 unter d:\lo mergedlo.dll!vcl::Window::Show(bool bVisible, ShowFlags nFlags) Zeile 2198 unter d:\lo mergedlo.dll!vcl::Window::Show(bool bVisible, ShowFlags nFlags) Zeile 2176 unter d:\lo [Inlineframe] mergedlo.dll!vcl::Window::Hide() Zeile 925 unter d:\lo mergedlo.dll!vcl::Window::dispose() Zeile 402 unter d:\lo mergedlo.dll!Edit::dispose() Zeile 258 unter d:\lo mergedlo.dll!ComboBox::dispose() Zeile 132 unter d:\lo mergedlo.dll!SvxFontNameBox_Impl::dispose() Zeile 1322 unter d:\lo [Inlineframe] mergedlo.dll!VclPtr<SvxFontNameBox_Impl>::disposeAndClear() Zeile 206 unter d:\lo mergedlo.dll!SvxFontNameToolBoxControl::dispose() Zeile 3309 unter d:\lo mergedlo.dll!framework::ToolBarManager::RemoveControllers() Zeile 651 unter d:\lo mergedlo.dll!framework::ToolBarManager::dispose() Zeile 468 unter d:\lo mergedlo.dll!framework::ToolBarWrapper::dispose() Zeile 105 unter d:\lo mergedlo.dll!framework::ToolbarLayoutManager::destroyToolbars() Zeile 666 unter d:\lo mergedlo.dll!framework::ToolbarLayoutManager::reset() Zeile 364 unter d:\lo mergedlo.dll!framework::LayoutManager::implts_reset(bool bAttached) Zeile 458 unter d:\lo mergedlo.dll!framework::LayoutManager::frameAction(const com::sun::star::frame::FrameActionEvent & aEvent) Zeile 2757 unter d:\lo mergedlo.dll!`anonymous namespace'::XFrameImpl::implts_sendFrameActionEvent(const com::sun::star::frame::FrameAction & aAction) Zeile 2952 unter d:\lo mergedlo.dll!`anonymous namespace'::XFrameImpl::setComponent(const com::sun::star::uno::Reference<com::sun::star::awt::XWindow> & xComponentWindow, const com::sun::star::uno::Reference<com::sun::star::frame::XController> & xController) Zeile 1470 unter d:\lo mergedlo.dll!`anonymous namespace'::XFrameImpl::close(unsigned char bDeliverOwnership) Zeile 1701 unter d:\lo mergedlo.dll!framework::pattern::frame::closeIt(const com::sun::star::uno::Reference<com::sun::star::uno::XInterface> & xResource) Zeile 62 unter d:\lo [Inlineframe] mergedlo.dll!framework::CloseDispatcher::implts_closeFrame() Zeile 492 unter d:\lo Change-Id: Ie05cd05158df58021d0fb4a19e9e38cd35af2426 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164451 Tested-by: Jenkins Reviewed-by: Michael Weghorn <[email protected]> Reviewed-by: Michael Stahl <[email protected]> (cherry picked from commit 825dde03999a55d02e4d5bc88a4d5beacb65e67f) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164503 Reviewed-by: Noel Grandin <[email protected]> diff --git a/accessibility/source/standard/vclxaccessibletoolbox.cxx b/accessibility/source/standard/vclxaccessibletoolbox.cxx index acb1d2547133..c9f4546c1251 100644 --- a/accessibility/source/standard/vclxaccessibletoolbox.cxx +++ b/accessibility/source/standard/vclxaccessibletoolbox.cxx @@ -630,9 +630,14 @@ Reference< XAccessible > SAL_CALL VCLXAccessibleToolBox::getAccessibleChild( sal if ( pItemWindow ) { Reference< XAccessible> xParent = xChild; - rtl::Reference<OToolBoxWindowItem> xChild2( new OToolBoxWindowItem(0,::comphelper::getProcessComponentContext(),pItemWindow->GetAccessible(),xParent) ); - pItemWindow->SetAccessible(xChild2); - xChild->SetChild( xChild2 ); + auto const xInnerAcc(pItemWindow->GetAccessible()); + if (xInnerAcc) // else child is being disposed - avoid crashing + { + rtl::Reference<OToolBoxWindowItem> xChild2(new OToolBoxWindowItem(0, + ::comphelper::getProcessComponentContext(), xInnerAcc, xParent)); + pItemWindow->SetAccessible(xChild2); + xChild->SetChild( xChild2 ); + } } if ( nHighlightItemId > ToolBoxItemId(0) && nItemId == nHighlightItemId ) xChild->SetFocus( true ); diff --git a/vcl/source/window/accessibility.cxx b/vcl/source/window/accessibility.cxx index 3c6103ac31d9..295a5c3878fc 100644 --- a/vcl/source/window/accessibility.cxx +++ b/vcl/source/window/accessibility.cxx @@ -69,7 +69,7 @@ css::uno::Reference< css::accessibility::XAccessible > Window::GetAccessible( bo */ if ( !mpWindowImpl ) return css::uno::Reference< css::accessibility::XAccessible >(); - if ( !mpWindowImpl->mxAccessible.is() && bCreate ) + if (!mpWindowImpl->mxAccessible.is() && !mpWindowImpl->mbInDispose && bCreate) mpWindowImpl->mxAccessible = CreateAccessible(); return mpWindowImpl->mxAccessible; commit 108577b75d12091a6edba19fbe1f90479d4564e2 Author: Patrick Luby <[email protected]> AuthorDate: Thu Mar 7 17:22:35 2024 -0500 Commit: Andras Timar <[email protected]> CommitDate: Tue Mar 12 15:28:19 2024 +0100 tdf#159996 use transparent alpha mask for output device Since commit 81994cb2b8b32453a92bcb011830fcb884f22ff3, fixed text needs to be drawn to an output device with a transparent alpha mask. Change-Id: I2036e8c6c9133b1caceb24aeca40f04524c16b23 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164556 Tested-by: Jenkins Reviewed-by: Patrick Luby <[email protected]> (cherry picked from commit 73c14fcf97a1dcc9c95c98f9223449957b0b4963) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164499 Reviewed-by: Noel Grandin <[email protected]> diff --git a/vcl/source/window/paint.cxx b/vcl/source/window/paint.cxx index 7cb1c969a983..5996b818d4bb 100644 --- a/vcl/source/window/paint.cxx +++ b/vcl/source/window/paint.cxx @@ -1528,7 +1528,7 @@ void Window::ImplPaintToDevice( OutputDevice* i_pTargetOutDev, const Point& i_rP VclPtrInstance<VirtualDevice> pMaskedDevice(*i_pTargetOutDev, DeviceFormat::WITH_ALPHA); - pMaskedDevice->SetOutputSizePixel( GetOutputSizePixel() ); + pMaskedDevice->SetOutputSizePixel( GetOutputSizePixel(), true, true ); pMaskedDevice->EnableRTL( IsRTLEnabled() ); aMtf.WindStart(); aMtf.Play(*pMaskedDevice); commit 76df54e4b392757ab06ee5dde44ef39ae15c1b56 Author: Julien Nabet <[email protected]> AuthorDate: Fri Mar 1 12:14:52 2024 +0100 Commit: Andras Timar <[email protected]> CommitDate: Tue Mar 12 15:28:19 2024 +0100 tdf#159955: fix custom page number Change-Id: I1a56a4ba266dfb3c21e3b77263ecaa44b4e6dad5 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164210 Tested-by: Jenkins Reviewed-by: Julien Nabet <[email protected]> (cherry picked from commit d440dcfcaf688544a7a6d8bc478048159e5200f0) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164197 Reviewed-by: Xisco Fauli <[email protected]> diff --git a/sw/source/uibase/uiview/view2.cxx b/sw/source/uibase/uiview/view2.cxx index a9658fe92ab7..039dbdf9665e 100644 --- a/sw/source/uibase/uiview/view2.cxx +++ b/sw/source/uibase/uiview/view2.cxx @@ -288,14 +288,21 @@ OUString SwView::GetPageStr(sal_uInt16 nPhyNum, sal_uInt16 nVirtNum, const OUStr ? SwResId(STR_PAGE_COUNT_PRINTED) : (extra.isEmpty() ? SwResId(STR_PAGE_COUNT) : SwResId(STR_PAGE_COUNT_CUSTOM))); aStr = aStr.replaceFirst("%1", OUString::number(nPhyNum)); - aStr = aStr.replaceFirst("%2", OUString::number(nPageCount)); if (nPageCount != nPrintedPageCount) { + aStr = aStr.replaceFirst("%2", OUString::number(nPageCount)); aStr = aStr.replaceFirst("%3", OUString::number(nPrintedPhyNum)); aStr = aStr.replaceFirst("%4", OUString::number(nPrintedPageCount)); } - else - aStr = aStr.replaceFirst("%3", extra); + else { + if (extra.isEmpty()) + aStr = aStr.replaceFirst("%2", OUString::number(nPageCount)); + else + { + aStr = aStr.replaceFirst("%2", extra); + aStr = aStr.replaceFirst("%3", OUString::number(nPageCount)); + } + } return aStr; } commit 3b59c1ceec8828ba450bb7434809689d92a83e2f Author: Caolán McNamara <[email protected]> AuthorDate: Wed Mar 6 11:38:29 2024 +0000 Commit: Andras Timar <[email protected]> CommitDate: Tue Mar 12 15:28:19 2024 +0100 Breeze scrollbars clipped, allocated space not wide/tall enough https: //github.com/flathub/org.libreoffice.LibreOffice/issues/247 Change-Id: If04d4dd8b9608fc7c3a10bf36bad287cf183c2d2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164389 Tested-by: Jenkins Reviewed-by: Christian Lohmaier <[email protected]> diff --git a/vcl/unx/gtk3/salnativewidgets-gtk.cxx b/vcl/unx/gtk3/salnativewidgets-gtk.cxx index bfdeed423846..391baed22291 100644 --- a/vcl/unx/gtk3/salnativewidgets-gtk.cxx +++ b/vcl/unx/gtk3/salnativewidgets-gtk.cxx @@ -355,7 +355,6 @@ static GtkWidget* gCacheWindow; static GtkWidget* gDumbContainer; #if GTK_CHECK_VERSION(4, 0, 0) static GtkWidget* gVScrollbar; -static GtkWidget* gHScrollbar; static GtkWidget* gTextView; #else static GtkWidget* gComboBox; @@ -363,6 +362,7 @@ static GtkWidget* gListBox; static GtkWidget* gSpinBox; static GtkWidget* gTreeViewWidget; #endif +static GtkWidget* gHScrollbar; static GtkWidget* gEntryBox; namespace @@ -2625,10 +2625,11 @@ bool GtkSalGraphics::updateSettings(AllSettings& rSettings) // set scrollbar settings gint min_slider_length = 21; + GtkRequisition natural_horz_scroll_size; + gtk_widget_get_preferred_size(gHScrollbar, nullptr, &natural_horz_scroll_size); + #if GTK_CHECK_VERSION(4, 0, 0) - GtkRequisition natural_size; - gtk_widget_get_preferred_size(gHScrollbar, nullptr, &natural_size); - aStyleSet.SetScrollBarSize(natural_size.height); + aStyleSet.SetScrollBarSize(natural_horz_scroll_size.height); #else // Grab some button style attributes Size aSize; @@ -2646,6 +2647,10 @@ bool GtkSalGraphics::updateSettings(AllSettings& rSettings) if (has_forward || has_backward || has_forward2 || has_backward2) QuerySize(mpHScrollbarButtonStyle, aSize); + // Recent breeze (Mar 2024) has 17 vs 10, while Adwaita still reports 14 vs 14. + if (natural_horz_scroll_size.height > aSize.Height()) + aSize.setHeight(natural_horz_scroll_size.height); + aStyleSet.SetScrollBarSize(aSize.Height()); gtk_style_context_get(mpVScrollbarSliderStyle, gtk_style_context_get_state(mpVScrollbarSliderStyle), @@ -2932,15 +2937,16 @@ GtkSalGraphics::GtkSalGraphics( GtkSalFrame *pFrame, GtkWidget *pWindow ) mpToolButtonStyle = gtk_widget_get_style_context(GTK_WIDGET(pButton)); #endif + gHScrollbar = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, nullptr); + gtk_fixed_put(GTK_FIXED(gDumbContainer), gHScrollbar, 0, 0); + gtk_widget_show(gHScrollbar); + #if GTK_CHECK_VERSION(4, 0, 0) gVScrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, nullptr); gtk_fixed_put(GTK_FIXED(gDumbContainer), gVScrollbar, 0, 0); gtk_widget_show(gVScrollbar); mpVScrollbarStyle = gtk_widget_get_style_context(gVScrollbar); - gHScrollbar = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL, nullptr); - gtk_fixed_put(GTK_FIXED(gDumbContainer), gHScrollbar, 0, 0); - gtk_widget_show(gHScrollbar); mpHScrollbarStyle = gtk_widget_get_style_context(gHScrollbar); gTextView = gtk_text_view_new(); commit b16b7d0fdeab0b4191c6c75c9e886d1ccfeaf09d Author: Tomaž Vajngerl <[email protected]> AuthorDate: Thu Feb 29 23:51:26 2024 +0900 Commit: Andras Timar <[email protected]> CommitDate: Tue Mar 12 15:28:19 2024 +0100 tdf#83720 Pivot Table: Data field should always at last place In the pivot table dialog we should always put the "Data" field to the last place or the cell formats won't be shown correct in the pivot table. Change-Id: If4befe4fff1e6f04d9b709615a1955e3b5f4b4cc Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164161 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <[email protected]> (cherry picked from commit 78065e3798439dd790d1be5ac5c219477285c888) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164126 Reviewed-by: Christian Lohmaier <[email protected]> diff --git a/sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx b/sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx index 45af29a4f1a4..672de9559c4e 100644 --- a/sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx +++ b/sc/source/ui/dbgui/PivotLayoutTreeListBase.cxx @@ -67,6 +67,9 @@ void ScPivotLayoutTreeListBase::PushEntriesToPivotFieldVector(ScPivotFieldVector std::unique_ptr<weld::TreeIter> xEachEntry(mxControl->make_iterator()); if (!mxControl->get_iter_first(*xEachEntry)) return; + + std::optional<ScPivotField> oDataField; + do { ScItemValue* pItemValue = weld::fromId<ScItemValue*>(mxControl->get_id(*xEachEntry)); @@ -78,8 +81,15 @@ void ScPivotLayoutTreeListBase::PushEntriesToPivotFieldVector(ScPivotFieldVector aField.nFuncMask = rFunctionData.mnFuncMask; aField.mnDupCount = rFunctionData.mnDupCount; aField.maFieldRef = rFunctionData.maFieldRef; - rVector.push_back(aField); + + if (aField.nCol == PIVOT_DATA_FIELD) + oDataField = aField; + else + rVector.push_back(aField); } while (mxControl->iter_next(*xEachEntry)); + + if (oDataField) + rVector.push_back(*oDataField); } void ScPivotLayoutTreeListBase::InsertEntryForSourceTarget(weld::TreeView& /*pSource*/, int /*nTarget*/) commit ed5bb7356ded2af5ec75f99c96a8da94b2eded82 Author: Rafael Lima <[email protected]> AuthorDate: Mon Mar 4 19:01:40 2024 +0100 Commit: Andras Timar <[email protected]> CommitDate: Tue Mar 12 15:28:19 2024 +0100 tdf#158735 Save solver settings for DEPS and SCO as well When bug tdf#38948 was originally fixed, the solvers DEPS and SCO were not considered. This caused a regression, because setting engine options for these solvers made them never be saved, even in its own sheet. This patch fixes that by incorporating the engine options for DEPS and SCO. Change-Id: I93af712f91da2f7b1ac57ed74f6c2c2d7d411bba Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164376 Tested-by: Jenkins Reviewed-by: Tomaž Vajngerl <[email protected]> (cherry picked from commit 04d884cc99eb66679fb254129b54488bd40e5abf) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164385 Reviewed-by: Xisco Fauli <[email protected]> diff --git a/sc/inc/SolverSettings.hxx b/sc/inc/SolverSettings.hxx index ec1ef994a7b8..985e8d30f796 100644 --- a/sc/inc/SolverSettings.hxx +++ b/sc/inc/SolverSettings.hxx @@ -39,11 +39,34 @@ enum SolverParameter SP_LO_ENGINE, // Engine name used in LO SP_MS_ENGINE, // Engine ID used in MSO SP_INTEGER, // Assume all variables are integer (0: no, 1: yes) + // LpSolve, CoinMP and SwarmSolver SP_NON_NEGATIVE, // Assume non negativity (1: yes, 2: no) SP_EPSILON_LEVEL, // Epsilon level SP_LIMIT_BBDEPTH, // Branch and bound depth SP_TIMEOUT, // Time limit to return a solution - SP_ALGORITHM // Algorithm used by the SwarmSolver (1, 2 or 3) + SP_ALGORITHM, // Algorithm used by the SwarmSolver (1, 2 or 3) + // Engine options common for DEPS and SCO + SP_SWARM_SIZE, // Size of Swarm + SP_LEARNING_CYCLES, // Learning Cycles + SP_GUESS_VARIABLE_RANGE, // Variable Bounds Guessing + SP_VARIABLE_RANGE_THRESHOLD, // Variable Bounds Threshold (when guessing) + SP_ACR_COMPARATOR, // Use ACR Comparator (instead of BCH) + SP_RND_STARTING_POINT, // Use Random starting point + SP_STRONGER_PRNG, // Use a stronger random generator (slower) + SP_STAGNATION_LIMIT, // Stagnation Limit + SP_STAGNATION_TOLERANCE, // Stagnation Tolerance + SP_ENHANCED_STATUS, // Show enhanced solver status + // DEPS Options + SP_AGENT_SWITCH_RATE, // Agent Switch Rate (DE Probability) + SP_SCALING_MIN, // DE: Min Scaling Factor (0-1.2) + SP_SCALING_MAX, // DE: Max Scaling Factor (0-1.2) + SP_CROSSOVER_PROB, // DE: Crossover Probability (0-1) + SP_COGNITIVE_CONST, // Cognitive Constant + SP_SOCIAL_CONST, // Social Constant + SP_CONSTRICTION_COEFF, // PS: Constriction Coefficient + SP_MUTATION_PROB, // Mutation Probability (0-0.005) + // SCO Options + SP_LIBRARY_SIZE, // Size of library }; // Starts at 1 to maintain MS compatibility @@ -123,6 +146,28 @@ private: OUString m_sLimitBBDepth; OUString m_sTimeout; OUString m_sAlgorithm; + // DEPS and SCO + OUString m_sSwarmSize; + OUString m_sLearningCycles; + OUString m_sGuessVariableRange; + OUString m_sVariableRangeThreshold; + OUString m_sUseACRComparator; + OUString m_sUseRandomStartingPoint; + OUString m_sUseStrongerPRNG; + OUString m_sStagnationLimit; + OUString m_sTolerance; + OUString m_sEnhancedSolverStatus; + // DEPS only + OUString m_sAgentSwitchRate; + OUString m_sScalingFactorMin; + OUString m_sScalingFactorMax; + OUString m_sCrossoverProbability; + OUString m_sCognitiveConstant; + OUString m_sSocialConstant; + OUString m_sConstrictionCoeff; + OUString m_sMutationProbability; + OUString m_sLibrarySize; + css::uno::Sequence<css::beans::PropertyValue> m_aEngineOptions; std::vector<ModelConstraint> m_aConstraints; @@ -131,7 +176,9 @@ private: // Used to create or read a single solver parameter based on its named range bool ReadParamValue(SolverParameter eParam, OUString& rValue, bool bRemoveQuotes = false); + bool ReadDoubleParamValue(SolverParameter eParam, OUString& rValue); void WriteParamValue(SolverParameter eParam, OUString sValue, bool bQuoted = false); + void WriteDoubleParamValue(SolverParameter eParam, std::u16string_view sValue); // Creates or reads all constraints stored in named ranges void ReadConstraints(); @@ -149,19 +196,46 @@ private: // Maps solver parameters to named ranges std::map<SolverParameter, OUString> m_mNamedRanges - = { { SP_OBJ_CELL, "solver_opt" }, { SP_OBJ_TYPE, "solver_typ" }, - { SP_OBJ_VAL, "solver_val" }, { SP_VAR_CELLS, "solver_adj" }, - { SP_CONSTR_COUNT, "solver_num" }, { SP_LO_ENGINE, "solver_lo_eng" }, - { SP_MS_ENGINE, "solver_eng" }, { SP_INTEGER, "solver_int" }, - { SP_NON_NEGATIVE, "solver_neg" }, { SP_EPSILON_LEVEL, "solver_eps" }, - { SP_LIMIT_BBDEPTH, "solver_bbd" }, { SP_TIMEOUT, "solver_tim" }, - { SP_ALGORITHM, "solver_alg" } }; + = { { SP_OBJ_CELL, "solver_opt" }, + { SP_OBJ_TYPE, "solver_typ" }, + { SP_OBJ_VAL, "solver_val" }, + { SP_VAR_CELLS, "solver_adj" }, + { SP_CONSTR_COUNT, "solver_num" }, + { SP_LO_ENGINE, "solver_lo_eng" }, + { SP_MS_ENGINE, "solver_eng" }, + { SP_INTEGER, "solver_int" }, + { SP_NON_NEGATIVE, "solver_neg" }, + { SP_EPSILON_LEVEL, "solver_eps" }, + { SP_LIMIT_BBDEPTH, "solver_bbd" }, + { SP_TIMEOUT, "solver_tim" }, + { SP_ALGORITHM, "solver_alg" }, + { SP_SWARM_SIZE, "solver_ssz" }, + { SP_LEARNING_CYCLES, "solver_lcy" }, + { SP_GUESS_VARIABLE_RANGE, "solver_gvr" }, + { SP_VARIABLE_RANGE_THRESHOLD, "solver_vrt" }, + { SP_ACR_COMPARATOR, "solver_acr" }, + { SP_RND_STARTING_POINT, "solver_rsp" }, + { SP_STRONGER_PRNG, "solver_prng" }, + { SP_STAGNATION_LIMIT, "solver_slim" }, + { SP_STAGNATION_TOLERANCE, "solver_stol" }, + { SP_ENHANCED_STATUS, "solver_enst" }, + { SP_AGENT_SWITCH_RATE, "solver_asr" }, + { SP_SCALING_MIN, "solver_smin" }, + { SP_SCALING_MAX, "solver_smax" }, + { SP_CROSSOVER_PROB, "solver_crpb" }, + { SP_COGNITIVE_CONST, "solver_cog" }, + { SP_SOCIAL_CONST, "solver_soc" }, + { SP_CONSTRICTION_COEFF, "solver_ccoeff" }, + { SP_MUTATION_PROB, "solver_mtpb" }, + { SP_LIBRARY_SIZE, "solver_lbsz" } }; // Maps LO solver implementation names to MS engine codes std::map<OUString, OUString> SolverNamesToExcelEngines = { { "com.sun.star.comp.Calc.CoinMPSolver", "2" }, // Simplex LP { "com.sun.star.comp.Calc.LpsolveSolver", "2" }, // Simplex LP - { "com.sun.star.comp.Calc.SwarmSolver", "1" } // GRG Nonlinear + { "com.sun.star.comp.Calc.SwarmSolver", "1" }, // GRG Nonlinear + { "com.sun.star.comp.Calc.NLPSolver.DEPSSolverImpl", "3" }, // DEPS + { "com.sun.star.comp.Calc.NLPSolver.SCOSolverImpl", "3" } // SCO }; // Maps MS solver engine codes to LO solver implementation names @@ -180,7 +254,30 @@ private: { "EpsilonLevel", { SP_EPSILON_LEVEL, "solver_eps", "int" } }, { "LimitBBDepth", { SP_LIMIT_BBDEPTH, "solver_bbd", "bool" } }, { "Timeout", { SP_TIMEOUT, "solver_tim", "int" } }, - { "Algorithm", { SP_ALGORITHM, "solver_alg", "int" } } }; + { "Algorithm", { SP_ALGORITHM, "solver_alg", "int" } }, + // SCO and DEPS + { "AssumeNonNegative", { SP_NON_NEGATIVE, "solver_neg", "bool" } }, + { "SwarmSize", { SP_SWARM_SIZE, "solver_ssz", "int" } }, + { "LearningCycles", { SP_LEARNING_CYCLES, "solver_lcy", "int" } }, + { "GuessVariableRange", { SP_GUESS_VARIABLE_RANGE, "solver_gvr", "bool" } }, + { "VariableRangeThreshold", { SP_VARIABLE_RANGE_THRESHOLD, "solver_vrt", "double" } }, + { "UseACRComparator", { SP_ACR_COMPARATOR, "solver_acr", "bool" } }, + { "UseRandomStartingPoint", { SP_RND_STARTING_POINT, "solver_rsp", "bool" } }, + { "UseStrongerPRNG", { SP_STRONGER_PRNG, "solver_prng", "bool" } }, + { "StagnationLimit", { SP_STAGNATION_LIMIT, "solver_slim", "int" } }, + { "Tolerance", { SP_STAGNATION_TOLERANCE, "solver_stol", "double" } }, + { "EnhancedSolverStatus", { SP_ENHANCED_STATUS, "solver_enst", "bool" } }, + // DEPS only + { "AgentSwitchRate", { SP_AGENT_SWITCH_RATE, "solver_asr", "double" } }, + { "DEFactorMin", { SP_SCALING_MIN, "solver_smin", "double" } }, + { "DEFactorMax", { SP_SCALING_MAX, "solver_smax", "double" } }, + { "DECR", { SP_CROSSOVER_PROB, "solver_crpb", "double" } }, + { "PSC1", { SP_COGNITIVE_CONST, "solver_cog", "double" } }, + { "PSC2", { SP_SOCIAL_CONST, "solver_soc", "double" } }, + { "PSWeight", { SP_CONSTRICTION_COEFF, "solver_ccoeff", "double" } }, + { "PSCL", { SP_MUTATION_PROB, "solver_mtpb", "double" } }, + // SCO only + { "LibrarySize", { SP_LIBRARY_SIZE, "solver_lbsz", "int" } } }; // Stores the roots used for named ranges of constraint parts // Items here must be in the same order as in ConstraintPart enum diff --git a/sc/qa/unit/data/ods/tdf158735.ods b/sc/qa/unit/data/ods/tdf158735.ods new file mode 100644 index 000000000000..6003f29bf38a Binary files /dev/null and b/sc/qa/unit/data/ods/tdf158735.ods differ diff --git a/sc/qa/unit/ucalc_solver.cxx b/sc/qa/unit/ucalc_solver.cxx index 47770ec0c0e5..7834597e9c07 100644 --- a/sc/qa/unit/ucalc_solver.cxx +++ b/sc/qa/unit/ucalc_solver.cxx @@ -163,4 +163,33 @@ CPPUNIT_TEST_FIXTURE(SolverTest, tdf156815) CPPUNIT_ASSERT_EQUAL(OUString("$NewName.$B$2"), aConstraints[0].aRightStr); } +// Tests if settings for the DEPS and SCO solvers are kept in the file +CPPUNIT_TEST_FIXTURE(SolverTest, tdf158735) +{ + createScDoc("ods/tdf158735.ods"); + ScDocument* pDoc = getScDoc(); + + // Test the non-default values of the DEPS model + ScTable* pTable = pDoc->FetchTable(0); + std::shared_ptr<sc::SolverSettings> pSettings = pTable->GetSolverSettings(); + CPPUNIT_ASSERT(pSettings); + CPPUNIT_ASSERT_EQUAL(OUString("com.sun.star.comp.Calc.NLPSolver.DEPSSolverImpl"), + pSettings->GetParameter(SP_LO_ENGINE)); + CPPUNIT_ASSERT_EQUAL(OUString("0.45"), pSettings->GetParameter(SP_AGENT_SWITCH_RATE)); + CPPUNIT_ASSERT_EQUAL(OUString("0.85"), pSettings->GetParameter(SP_CROSSOVER_PROB)); + CPPUNIT_ASSERT_EQUAL(OUString("1500"), pSettings->GetParameter(SP_LEARNING_CYCLES)); + CPPUNIT_ASSERT_EQUAL(OUString("0"), pSettings->GetParameter(SP_ENHANCED_STATUS)); + + // Test the non-default values of the SCO model + pTable = pDoc->FetchTable(1); + pSettings = pTable->GetSolverSettings(); + CPPUNIT_ASSERT(pSettings); + CPPUNIT_ASSERT_EQUAL(OUString("com.sun.star.comp.Calc.NLPSolver.SCOSolverImpl"), + pSettings->GetParameter(SP_LO_ENGINE)); + CPPUNIT_ASSERT_EQUAL(OUString("180"), pSettings->GetParameter(SP_LIBRARY_SIZE)); + CPPUNIT_ASSERT_EQUAL(OUString("0.00055"), pSettings->GetParameter(SP_STAGNATION_TOLERANCE)); + CPPUNIT_ASSERT_EQUAL(OUString("1"), pSettings->GetParameter(SP_RND_STARTING_POINT)); + CPPUNIT_ASSERT_EQUAL(OUString("80"), pSettings->GetParameter(SP_STAGNATION_LIMIT)); +} + CPPUNIT_PLUGIN_IMPLEMENT(); diff --git a/sc/source/core/data/SolverSettings.cxx b/sc/source/core/data/SolverSettings.cxx index 60eb747f55f5..64badb8c2790 100644 --- a/sc/source/core/data/SolverSettings.cxx +++ b/sc/source/core/data/SolverSettings.cxx @@ -11,6 +11,7 @@ #include <global.hxx> #include <table.hxx> #include <docsh.hxx> +#include <rtl/math.hxx> #include <solverutil.hxx> #include <unotools/charclass.hxx> #include <SolverSettings.hxx> @@ -73,6 +74,28 @@ void SolverSettings::Initialize() ReadParamValue(SP_LIMIT_BBDEPTH, m_sLimitBBDepth); ReadParamValue(SP_TIMEOUT, m_sTimeout); ReadParamValue(SP_ALGORITHM, m_sAlgorithm); + // Engine options common for DEPS and SCO + ReadParamValue(SP_SWARM_SIZE, m_sSwarmSize); + ReadParamValue(SP_LEARNING_CYCLES, m_sLearningCycles); + ReadParamValue(SP_GUESS_VARIABLE_RANGE, m_sGuessVariableRange); + ReadDoubleParamValue(SP_VARIABLE_RANGE_THRESHOLD, m_sVariableRangeThreshold); + ReadParamValue(SP_ACR_COMPARATOR, m_sUseACRComparator); + ReadParamValue(SP_RND_STARTING_POINT, m_sUseRandomStartingPoint); + ReadParamValue(SP_STRONGER_PRNG, m_sUseStrongerPRNG); + ReadParamValue(SP_STAGNATION_LIMIT, m_sStagnationLimit); + ReadDoubleParamValue(SP_STAGNATION_TOLERANCE, m_sTolerance); + ReadParamValue(SP_ENHANCED_STATUS, m_sEnhancedSolverStatus); + // DEPS Options + ReadDoubleParamValue(SP_AGENT_SWITCH_RATE, m_sAgentSwitchRate); + ReadDoubleParamValue(SP_SCALING_MIN, m_sScalingFactorMin); + ReadDoubleParamValue(SP_SCALING_MAX, m_sScalingFactorMax); + ReadDoubleParamValue(SP_CROSSOVER_PROB, m_sCrossoverProbability); + ReadDoubleParamValue(SP_COGNITIVE_CONST, m_sCognitiveConstant); + ReadDoubleParamValue(SP_SOCIAL_CONST, m_sSocialConstant); + ReadDoubleParamValue(SP_CONSTRICTION_COEFF, m_sConstrictionCoeff); + ReadDoubleParamValue(SP_MUTATION_PROB, m_sMutationProbability); + // SCO Options + ReadParamValue(SP_LIBRARY_SIZE, m_sLibrarySize); } // Returns the current value of the parameter in the object as a string @@ -119,6 +142,63 @@ OUString SolverSettings::GetParameter(SolverParameter eParam) case SP_ALGORITHM: return m_sAlgorithm; break; + case SP_SWARM_SIZE: + return m_sSwarmSize; + break; + case SP_LEARNING_CYCLES: + return m_sLearningCycles; + break; + case SP_GUESS_VARIABLE_RANGE: + return m_sGuessVariableRange; + break; + case SP_VARIABLE_RANGE_THRESHOLD: + return m_sVariableRangeThreshold; + break; + case SP_ACR_COMPARATOR: + return m_sUseACRComparator; + break; + case SP_RND_STARTING_POINT: + return m_sUseRandomStartingPoint; + break; + case SP_STRONGER_PRNG: + return m_sUseStrongerPRNG; + break; + case SP_STAGNATION_LIMIT: + return m_sStagnationLimit; + break; + case SP_STAGNATION_TOLERANCE: + return m_sTolerance; + break; + case SP_ENHANCED_STATUS: + return m_sEnhancedSolverStatus; + break; + case SP_AGENT_SWITCH_RATE: + return m_sAgentSwitchRate; + break; + case SP_SCALING_MIN: + return m_sScalingFactorMin; + break; + case SP_SCALING_MAX: + return m_sScalingFactorMax; + break; + case SP_CROSSOVER_PROB: + return m_sCrossoverProbability; + break; + case SP_COGNITIVE_CONST: + return m_sCognitiveConstant; + break; + case SP_SOCIAL_CONST: + return m_sSocialConstant; + break; + case SP_CONSTRICTION_COEFF: + return m_sConstrictionCoeff; + break; + case SP_MUTATION_PROB: + return m_sMutationProbability; + break; + case SP_LIBRARY_SIZE: + return m_sLibrarySize; + break; default: return ""; } @@ -188,6 +268,75 @@ void SolverSettings::SetParameter(SolverParameter eParam, OUString sValue) m_sAlgorithm = sValue; } break; + case SP_SWARM_SIZE: + m_sSwarmSize = sValue; + break; + case SP_LEARNING_CYCLES: + m_sLearningCycles = sValue; + break; + case SP_GUESS_VARIABLE_RANGE: + m_sGuessVariableRange = sValue; + break; + case SP_VARIABLE_RANGE_THRESHOLD: + m_sVariableRangeThreshold = sValue; + break; + case SP_ACR_COMPARATOR: + { + if (sValue == "0" || sValue == "1") + m_sUseACRComparator = sValue; + } + break; + case SP_RND_STARTING_POINT: + { + if (sValue == "0" || sValue == "1") + m_sUseRandomStartingPoint = sValue; + } + break; + case SP_STRONGER_PRNG: + { + if (sValue == "0" || sValue == "1") + m_sUseStrongerPRNG = sValue; + } + break; + case SP_STAGNATION_LIMIT: + m_sStagnationLimit = sValue; + break; + case SP_STAGNATION_TOLERANCE: + m_sTolerance = sValue; + break; + case SP_ENHANCED_STATUS: + { + if (sValue == "0" || sValue == "1") + m_sEnhancedSolverStatus = sValue; + } + break; + case SP_AGENT_SWITCH_RATE: + m_sAgentSwitchRate = sValue; + break; + case SP_SCALING_MIN: + m_sScalingFactorMin = sValue; + break; + case SP_SCALING_MAX: + m_sScalingFactorMax = sValue; + break; + case SP_CROSSOVER_PROB: + m_sCrossoverProbability = sValue; + break; + case SP_COGNITIVE_CONST: + m_sCognitiveConstant = sValue; + break; + case SP_SOCIAL_CONST: + m_sSocialConstant = sValue; + break; + case SP_CONSTRICTION_COEFF: + m_sConstrictionCoeff = sValue; + break; + case SP_MUTATION_PROB: + m_sMutationProbability = sValue; + break; + case SP_LIBRARY_SIZE: + m_sLibrarySize = sValue; + break; default: break; } @@ -321,12 +470,35 @@ void SolverSettings::SaveSolverSettings() sal_Int32 nConstrCount = m_aConstraints.size(); WriteParamValue(SP_CONSTR_COUNT, OUString::number(nConstrCount)); + // Solver engine options WriteParamValue(SP_INTEGER, m_sInteger); WriteParamValue(SP_NON_NEGATIVE, m_sNonNegative); WriteParamValue(SP_EPSILON_LEVEL, m_sEpsilonLevel); WriteParamValue(SP_LIMIT_BBDEPTH, m_sLimitBBDepth); WriteParamValue(SP_TIMEOUT, m_sTimeout); WriteParamValue(SP_ALGORITHM, m_sAlgorithm); + // Engine options common for DEPS and SCO + WriteParamValue(SP_SWARM_SIZE, m_sSwarmSize); + WriteParamValue(SP_LEARNING_CYCLES, m_sLearningCycles); + WriteParamValue(SP_GUESS_VARIABLE_RANGE, m_sGuessVariableRange); + WriteDoubleParamValue(SP_VARIABLE_RANGE_THRESHOLD, m_sVariableRangeThreshold); + WriteParamValue(SP_ACR_COMPARATOR, m_sUseACRComparator); + WriteParamValue(SP_RND_STARTING_POINT, m_sUseRandomStartingPoint); + WriteParamValue(SP_STRONGER_PRNG, m_sUseStrongerPRNG); + WriteParamValue(SP_STAGNATION_LIMIT, m_sStagnationLimit); + WriteDoubleParamValue(SP_STAGNATION_TOLERANCE, m_sTolerance); + WriteParamValue(SP_ENHANCED_STATUS, m_sEnhancedSolverStatus); + // DEPS Options + WriteDoubleParamValue(SP_AGENT_SWITCH_RATE, m_sAgentSwitchRate); + WriteDoubleParamValue(SP_SCALING_MIN, m_sScalingFactorMin); + WriteDoubleParamValue(SP_SCALING_MAX, m_sScalingFactorMax); + WriteDoubleParamValue(SP_CROSSOVER_PROB, m_sCrossoverProbability); + WriteDoubleParamValue(SP_COGNITIVE_CONST, m_sCognitiveConstant); + WriteDoubleParamValue(SP_SOCIAL_CONST, m_sSocialConstant); + WriteDoubleParamValue(SP_CONSTRICTION_COEFF, m_sConstrictionCoeff); + WriteDoubleParamValue(SP_MUTATION_PROB, m_sMutationProbability); + // SCO Options + WriteParamValue(SP_LIBRARY_SIZE, m_sLibrarySize); if (m_pDocShell) m_pDocShell->SetDocumentModified(); @@ -354,6 +526,26 @@ bool SolverSettings::ReadParamValue(SolverParameter eParam, OUString& rValue, bo return false; } +// Reads a parameter value of type 'double' from the named range and into rValue +bool SolverSettings::ReadDoubleParamValue(SolverParameter eParam, OUString& rValue) +{ + const auto iter = m_mNamedRanges.find(eParam); + assert(iter != m_mNamedRanges.end()); + OUString sRange = iter->second; + ScRangeData* pRangeData + = m_pRangeName->findByUpperName(ScGlobal::getCharClass().uppercase(sRange)); + if (pRangeData) + { + OUString sLocalizedValue = pRangeData->GetSymbol(); + double fValue = rtl::math::stringToDouble(sLocalizedValue, + ScGlobal::getLocaleData().getNumDecimalSep()[0], + ScGlobal::getLocaleData().getNumThousandSep()[0]); + rValue = OUString::number(fValue); + return true; + } + return false; +} + /* Writes a parameter value to the file as a named range. * Argument bQuoted indicates whether the value should be enclosed with quotes or not (used * for string expressions that must be enclosed with quotes) @@ -375,6 +567,22 @@ void SolverSettings::WriteParamValue(SolverParameter eParam, OUString sValue, bo m_pRangeName->insert(pNewEntry); } +// Writes a parameter value of type 'double' to the file as a named range +// The argument 'sValue' uses dot as decimal separator and needs to be localized before +// being written to the file +void SolverSettings::WriteDoubleParamValue(SolverParameter eParam, std::u16string_view sValue) +{ + const auto iter = m_mNamedRanges.find(eParam); + assert(iter != m_mNamedRanges.end()); + OUString sRange = iter->second; + double fValue = rtl::math::stringToDouble(sValue, '.', ','); + OUString sLocalizedValue = rtl::math::doubleToUString( + fValue, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, + ScGlobal::getLocaleData().getNumDecimalSep()[0], true); + ScRangeData* pNewEntry = new ScRangeData(m_rDoc, sRange, sLocalizedValue); + m_pRangeName->insert(pNewEntry); +} + void SolverSettings::GetEngineOptions(css::uno::Sequence<css::beans::PropertyValue>& aOptions) { sal_Int32 nOptionsSize = aOptions.getLength(); @@ -398,6 +606,12 @@ void SolverSettings::GetEngineOptions(css::uno::Sequence<css::beans::PropertyVal pParamValues[i] = css::beans::PropertyValue(sLOParamName, -1, nValue, css::beans::PropertyState_DIRECT_VALUE); } + if (sParamType == "double") + { + css::uno::Any fValue(sParamValue.toDouble()); + pParamValues[i] = css::beans::PropertyValue(sLOParamName, -1, fValue, + css::beans::PropertyState_DIRECT_VALUE); + } if (sParamType == "bool") { // The parameter NonNegative is a special case for MS compatibility @@ -438,6 +652,12 @@ void SolverSettings::SetEngineOptions(css::uno::Sequence<css::beans::PropertyVal aProp.Value >>= nValue; SetParameter(eParamId, OUString::number(nValue)); } + if (sParamType == "double") + { + double fValue = 0; + aProp.Value >>= fValue; + SetParameter(eParamId, OUString::number(fValue)); + } if (sParamType == "bool") { bool bValue = false; commit 73f8cdb207eef3b53ce0c207f22daffb2318e041 Author: Michael Stahl <[email protected]> AuthorDate: Mon Mar 4 12:47:15 2024 +0100 Commit: Andras Timar <[email protected]> CommitDate: Tue Mar 12 15:28:18 2024 +0100 sw: UITest_writer_tests8 test_tdf150443 more tolerant This fails on Fedora 39, also in the libreoffice-24-2 branch, with: self.assertEqual(get_state_as_dict(xWriterEdit)["CurrentPage"], "4") AssertionError: '5' != '4' Presumably a font substitution changing metrics problem, as "NexusSansOT" is not found. It looks like the exact page doesn't matter for the test. Change-Id: I62eadb8321352ea47a0f8f83ab91fd50ff01f7e4 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164353 Tested-by: Jenkins Reviewed-by: Michael Stahl <[email protected]> (cherry picked from commit 0b21e2a404c114529376dc50764dc0286dafc745) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164364 Reviewed-by: Xisco Fauli <[email protected]> diff --git a/sw/qa/uitest/writer_tests7/tdf150443.py b/sw/qa/uitest/writer_tests7/tdf150443.py index fb39bd8a0375..91937551a4b3 100644 --- a/sw/qa/uitest/writer_tests7/tdf150443.py +++ b/sw/qa/uitest/writer_tests7/tdf150443.py @@ -26,7 +26,9 @@ class tdf150443(UITestCase): xsearch = xDialog.getChild("search") xsearch.executeAction("CLICK", tuple()) #first search xToolkit.processEventsToIdle() - self.assertEqual(get_state_as_dict(xWriterEdit)["CurrentPage"], "4") + page = get_state_as_dict(xWriterEdit)["CurrentPage"] + # page may depend on font subsitution, just check it moved + self.assertTrue(page == "4" or page == "5") # reject the tracked table row in Manage Changes dialog window with self.ui_test.execute_modeless_dialog_through_command(".uno:AcceptTrackedChanges", close_button="close") as xTrackDlg: commit 32e3ca5697c7cebee91539b2711d975fcc0d4738 Author: Mike Kaganski <[email protected]> AuthorDate: Mon Mar 4 12:20:13 2024 +0600 Commit: Andras Timar <[email protected]> CommitDate: Tue Mar 12 15:28:18 2024 +0100 tdf160017: make sure to emit the closing tags in correct order This reimplements how the starts and ends of attributes are stored in HTMLEndPosLst. Instead of a plain list, now it is a sorted map, with positions as keys, and a vector of HTMLStartEndPos* as values. In commit b94b1fe936ddc4a9b86fbeb9c9c6ab0fca52f0bc (CharBrd 9.1: HTML filters, 2013-09-08), the character borders attributes started to be set in a special order, in front of the position's other attributes, to allow merging them. But that created a problem of knowing in which order to close respective tags. The change here sorts the closing tags for the current node only when writing them. At this point, it is possible to consider the opening positions correctly. Change-Id: I466ffa1c0eb28874ded003035e0cf772e31585b3 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164325 Tested-by: Jenkins Reviewed-by: Mike Kaganski <[email protected]> Signed-off-by: Xisco Fauli <[email protected]> Reviewed-on: https://gerrit.libreoffice.org/c/core/+/164357 diff --git a/sw/qa/extras/htmlexport/data/char_border_and_font_color.fodt b/sw/qa/extras/htmlexport/data/char_border_and_font_color.fodt new file mode 100644 index 000000000000..bda2ec63133c --- /dev/null +++ b/sw/qa/extras/htmlexport/data/char_border_and_font_color.fodt @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible: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:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" office:version="1.3" office:mimetype="application/vnd.oasis.opendocument.text"> + <office:automatic-styles> + <style:style style:name="P1" style:family="paragraph"> + <style:text-properties fo:color="#000000" loext:border="none"/> + </style:style> + </office:automatic-styles> + <office:body> + <office:text> + <text:p text:style-name="P1">foo</text:p> + </office:text> + </office:body> +</office:document> \ No newline at end of file diff --git a/sw/qa/extras/htmlexport/htmlexport.cxx b/sw/qa/extras/htmlexport/htmlexport.cxx index 1bd883be111f..de2e9da4c678 100644 --- a/sw/qa/extras/htmlexport/htmlexport.cxx +++ b/sw/qa/extras/htmlexport/htmlexport.cxx @@ -3032,6 +3032,27 @@ CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_NoBrClearForImageWrap) 0); } +CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testReqIF_Tdf160017_spanClosingOrder) +{ + // Given a document with a paragraph having explicit font color and character border properties: + createSwDoc("char_border_and_font_color.fodt"); + // When exporting to reqif: + ExportToReqif(); + // Without the fix, this would fail, because there was an extra closing </reqif-xhtml:span> + WrapReqifFromTempFile(); +} + +CPPUNIT_TEST_FIXTURE(SwHtmlDomExportTest, testHTML_Tdf160017_spanClosingOrder) +{ + // Given a document with a paragraph having explicit font color and character border properties: + createSwDoc("char_border_and_font_color.fodt"); + // When exporting to HTML: + ExportToHTML(); + // Parse it as XML (strict!) + // Without the fix, this would fail, because span and font elements closed in wrong order + CPPUNIT_ASSERT(parseXml(maTempFile)); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sw/source/filter/html/css1atr.cxx b/sw/source/filter/html/css1atr.cxx index 30ba8d3c0a75..2587edfaf200 100644 --- a/sw/source/filter/html/css1atr.cxx +++ b/sw/source/filter/html/css1atr.cxx @@ -3301,14 +3301,16 @@ SwHTMLWriter& OutCSS1_SvxBox( SwHTMLWriter& rWrt, const SfxPoolItem& rHt ) if( rHt.Which() == RES_CHRATR_BOX ) { + constexpr std::string_view inline_block("inline-block"); if( rWrt.m_bTagOn ) { // Inline-block to make the line height changing correspond to the character border - rWrt.OutCSS1_PropertyAscii(sCSS1_P_display, "inline-block"); + rWrt.OutCSS1_PropertyAscii(sCSS1_P_display, inline_block); } else { - HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false ); + if (!IgnorePropertyForReqIF(rWrt.mbReqIF, sCSS1_P_display, inline_block)) + HTMLOutFuncs::Out_AsciiTag( rWrt.Strm(), Concat2View(rWrt.GetNamespace() + OOO_STRING_SVTOOLS_HTML_span), false ); return rWrt; } } diff --git a/sw/source/filter/html/htmlatr.cxx b/sw/source/filter/html/htmlatr.cxx index 9f67d1ee0304..c880082018f1 100644 --- a/sw/source/filter/html/htmlatr.cxx +++ b/sw/source/filter/html/htmlatr.cxx @@ -1058,7 +1058,7 @@ public: HTMLStartEndPos( const SfxPoolItem& rItem, sal_Int32 nStt, sal_Int32 nE ); - const SfxPoolItem* GetItem() const { return m_pItem.get(); } + const SfxPoolItem& GetItem() const { return *m_pItem; } void SetStart(sal_Int32 nStt) { m_nStart = nStt; } sal_Int32 GetStart() const { return m_nStart; } @@ -1075,7 +1075,7 @@ HTMLStartEndPos::HTMLStartEndPos(const SfxPoolItem& rItem, sal_Int32 nStt, sal_I , m_pItem(rItem.Clone()) {} -typedef std::vector<HTMLStartEndPos *> HTMLStartEndPositions; +typedef std::map<sal_Int32, std::vector<HTMLStartEndPos*>> HTMLStartEndPositions; namespace { @@ -1091,8 +1091,8 @@ enum HTMLOnOffState { HTML_NOT_SUPPORTED, // unsupported Attribute class HTMLEndPosLst { - HTMLStartEndPositions m_aStartLst; // list, sorted for start positions - HTMLStartEndPositions m_aEndLst; // list, sorted for end positions + HTMLStartEndPositions m_aStartLst; // list, each position's elements sorted by appearance order + HTMLStartEndPositions m_aEndLst; // list, no sort of elements in position std::deque<sal_Int32> m_aScriptChgLst; // positions where script changes // 0 is not contained in this list, // but the text length @@ -1110,8 +1110,7 @@ class HTMLEndPosLst // Insert/remove a SttEndPos in/from the Start and End lists. // The end position is known. - void InsertItem_( HTMLStartEndPos *pPos, HTMLStartEndPositions::size_type nEndPos ); - void RemoveItem_( HTMLStartEndPositions::size_type nEndPos ); + void InsertItem_(HTMLStartEndPos* pPos); // determine the 'type' of the attribute HTMLOnOffState GetHTMLItemState( const SfxPoolItem& rItem ); @@ -1125,8 +1124,7 @@ class HTMLEndPosLst sal_Int32 nEndPos ); // adapt the end of a split item - void FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd, - HTMLStartEndPositions::size_type nStartPos ); + void FixSplittedItem(HTMLStartEndPos* pPos, sal_Int32 nNewEnd); // insert an attribute in the lists and, if necessary, split it void InsertItem( const SfxPoolItem& rItem, sal_Int32 nStart, @@ -1144,6 +1142,8 @@ class HTMLEndPosLst const SwHTMLFormatInfo *GetFormatInfo( const SwFormat& rFormat, SwHTMLFormatInfos& rFormatInfos ); + void OutEndAttrs(SwHTMLWriter& rWrt, std::vector<HTMLStartEndPos*>& posItems); + public: HTMLEndPosLst( SwDoc *pDoc, SwDoc* pTemplate, std::optional<Color> xDfltColor, @@ -1169,36 +1169,46 @@ public: bool IsHTMLMode(sal_uLong nMode) const { return (m_nHTMLMode & nMode) != 0; } }; -} - -void HTMLEndPosLst::InsertItem_( HTMLStartEndPos *pPos, HTMLStartEndPositions::size_type nEndPos ) +struct SortEnds { - // Insert the attribute in the Start list behind all attributes that - // were started before, or at the same position. - sal_Int32 nStart = pPos->GetStart(); - HTMLStartEndPositions::size_type i {0}; + HTMLStartEndPositions& m_startList; + SortEnds(HTMLStartEndPositions& startList) : m_startList(startList) {} + bool operator()(const HTMLStartEndPos* p1, const HTMLStartEndPos* p2) + { + // if p1 start after p2, then it ends before + if (p1->GetStart() > p2->GetStart()) + return true; + if (p1->GetStart() < p2->GetStart()) + return false; + for (const auto p : m_startList[p1->GetStart()]) + { + if (p == p1) + return false; + if (p == p2) + return true; + } + assert(!"Neither p1 nor p2 found in their start list"); + return false; + } +}; - while (i < m_aStartLst.size() && m_aStartLst[i]->GetStart() <= nStart) - ++i; - m_aStartLst.insert(m_aStartLst.begin() + i, pPos); +#ifndef NDEBUG +bool IsEmpty(const HTMLStartEndPositions& l) +{ + return std::find_if(l.begin(), l.end(), [](auto& i) { return !i.second.empty(); }) == l.end(); +} +#endif - // the position in the End list was supplied - m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pPos); } -void HTMLEndPosLst::RemoveItem_( HTMLStartEndPositions::size_type nEndPos ) +void HTMLEndPosLst::InsertItem_(HTMLStartEndPos* pPos) { - HTMLStartEndPos* pPos = m_aEndLst[nEndPos]; + // Character border attribute must be the first which is written out because of border merge. + auto& posItems1 = m_aStartLst[pPos->GetStart()]; + auto it = pPos->GetItem().Which() == RES_CHRATR_BOX ? posItems1.begin() : posItems1.end(); + posItems1.insert(it, pPos); - // now, we are looking for it in the Start list - HTMLStartEndPositions::iterator it = std::find(m_aStartLst.begin(), m_aStartLst.end(), pPos); - OSL_ENSURE(it != m_aStartLst.end(), "Item not found in Start List!"); - if (it != m_aStartLst.end()) - m_aStartLst.erase(it); - - m_aEndLst.erase(m_aEndLst.begin() + nEndPos); - - delete pPos; + m_aEndLst[pPos->GetEnd()].push_back(pPos); } HTMLOnOffState HTMLEndPosLst::GetHTMLItemState( const SfxPoolItem& rItem ) @@ -1352,23 +1362,25 @@ HTMLOnOffState HTMLEndPosLst::GetHTMLItemState( const SfxPoolItem& rItem ) bool HTMLEndPosLst::ExistsOnTagItem( sal_uInt16 nWhich, sal_Int32 nPos ) { - for (auto pTest : m_aStartLst) + for (const auto& [startPos, items] : m_aStartLst) { - if( pTest->GetStart() > nPos ) + if (startPos > nPos) { // this attribute, and all attributes that follow, start later break; } - else if( pTest->GetEnd() > nPos ) + + for (const auto* pTest : items) { - // the attribute starts before, or at, the current position and - // ends after it - const SfxPoolItem *pItem = pTest->GetItem(); - if( pItem->Which() == nWhich && - HTML_ON_VALUE == GetHTMLItemState(*pItem) ) + if (pTest->GetEnd() > nPos) { - // an OnTag attribute was found - return true; + // the attribute starts before, or at, the current position and ends after it + const SfxPoolItem& rItem = pTest->GetItem(); + if (rItem.Which() == nWhich && HTML_ON_VALUE == GetHTMLItemState(rItem)) + { + // an OnTag attribute was found + return true; + } } } } @@ -1386,24 +1398,17 @@ bool HTMLEndPosLst::ExistsOffTagItem( sal_uInt16 nWhich, sal_Int32 nStartPos, return false; } - for (auto pTest : m_aStartLst) + for (const auto* pTest : m_aStartLst[nStartPos]) { - if( pTest->GetStart() > nStartPos ) - { - // this attribute, and all attributes that follow, start later - break; - } - else if( pTest->GetStart()==nStartPos && - pTest->GetEnd()==nEndPos ) + if (pTest->GetEnd() == nEndPos) { - // the attribute starts before or at the current position and - // ends after it - const SfxPoolItem *pItem = pTest->GetItem(); - sal_uInt16 nTstWhich = pItem->Which(); + // the attribute starts before or at the current position and ends after it + const SfxPoolItem& rItem = pTest->GetItem(); + sal_uInt16 nTstWhich = rItem.Which(); if( (nTstWhich == RES_CHRATR_CROSSEDOUT || nTstWhich == RES_CHRATR_UNDERLINE || nTstWhich == RES_CHRATR_BLINK) && - HTML_OFF_VALUE == GetHTMLItemState(*pItem) ) + HTML_OFF_VALUE == GetHTMLItemState(rItem) ) { // an OffTag attribute was found that is exported the same // way as the current item @@ -1415,55 +1420,51 @@ bool HTMLEndPosLst::ExistsOffTagItem( sal_uInt16 nWhich, sal_Int32 nStartPos, return false; } -void HTMLEndPosLst::FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd, - HTMLStartEndPositions::size_type nStartPos ) +void HTMLEndPosLst::FixSplittedItem(HTMLStartEndPos* pPos, sal_Int32 nNewEnd) { + // remove the item from the End list + std::erase(m_aEndLst[pPos->GetEnd()], pPos); // fix the end position accordingly pPos->SetEnd( nNewEnd ); - - // remove the item from the End list - HTMLStartEndPositions::iterator it = std::find(m_aEndLst.begin(), m_aEndLst.end(), pPos); - OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!"); - if (it != m_aEndLst.end()) - m_aEndLst.erase(it); - - // from now on, it is closed as the last one at the corresponding position - HTMLStartEndPositions::size_type nEndPos {0}; - while (nEndPos < m_aEndLst.size() && m_aEndLst[nEndPos]->GetEnd() <= nNewEnd) - ++nEndPos; - m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pPos); + // from now on, it is closed at the corresponding position + m_aEndLst[nNewEnd].push_back(pPos); // now, adjust the attributes that got started afterwards - for (HTMLStartEndPositions::size_type i = nStartPos + 1; i < m_aStartLst.size(); ++i) + const sal_Int32 nPos = pPos->GetStart(); + for (const auto& [startPos, items] : m_aStartLst) { - HTMLStartEndPos* pTest = m_aStartLst[i]; - sal_Int32 nTestEnd = pTest->GetEnd(); - if( pTest->GetStart() >= nNewEnd ) - { - // the Test attribute and all the following ones start, after the - // split attribute ends + if (startPos < nPos) + continue; + + if (startPos >= nNewEnd) break; + + auto it = items.begin(); + if (startPos == nPos) + { + it = std::find(items.begin(), items.end(), pPos); + if (it != items.end()) + ++it; } - else if( nTestEnd > nNewEnd ) + for (; it != items.end(); ++it) { + HTMLStartEndPos* pTest = *it; + const sal_Int32 nTestEnd = pTest->GetEnd(); + if (nTestEnd <= nNewEnd) + continue; + // the Test attribute starts before the split attribute // ends, and ends afterwards, i.e., it must be split, as well + // remove the attribute from the End list + std::erase(m_aEndLst[pTest->GetEnd()], pTest); // set the new end pTest->SetEnd( nNewEnd ); - - // remove the attribute from the End list - it = std::find(m_aEndLst.begin(), m_aEndLst.end(), pTest); - OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!"); - if (it != m_aEndLst.end()) - m_aEndLst.erase(it); - - // it now ends as the first attribute in the respective position. - // We already know this position in the End list. - m_aEndLst.insert(m_aEndLst.begin() + nEndPos, pTest); + // it now ends in the respective position. + m_aEndLst[nNewEnd].push_back(pTest); // insert the 'rest' of the attribute - InsertItem( *pTest->GetItem(), nNewEnd, nTestEnd ); + InsertItem( pTest->GetItem(), nNewEnd, nTestEnd ); } } } @@ -1471,36 +1472,38 @@ void HTMLEndPosLst::FixSplittedItem( HTMLStartEndPos *pPos, sal_Int32 nNewEnd, void HTMLEndPosLst::InsertItem( const SfxPoolItem& rItem, sal_Int32 nStart, sal_Int32 nEnd ) { - HTMLStartEndPositions::size_type i; - for (i = 0; i < m_aEndLst.size(); i++) + assert(nStart < nEnd); + + for (auto& [endPos, items] : m_aEndLst) { - HTMLStartEndPos* pTest = m_aEndLst[i]; - sal_Int32 nTestEnd = pTest->GetEnd(); - if( nTestEnd <= nStart ) + if (endPos <= nStart) { // the Test attribute ends, before the new one starts continue; } - else if( nTestEnd < nEnd ) + if (endPos >= nEnd) + { + // the Test attribute (and all that follow) ends, before the new + // one ends + break; + } + + std::sort(items.begin(), items.end(), SortEnds(m_aStartLst)); + + for (HTMLStartEndPos* pTest : items) { if( pTest->GetStart() < nStart ) { // the Test attribute ends, before the new one ends. Thus, the // new attribute must be split. - InsertItem_( new HTMLStartEndPos( rItem, nStart, nTestEnd ), i ); - nStart = nTestEnd; + InsertItem_(new HTMLStartEndPos(rItem, nStart, endPos)); + nStart = endPos; } } - else - { - // the Test attribute (and all that follow) ends, before the new - // one ends - break; - } } // one attribute must still be inserted - InsertItem_( new HTMLStartEndPos( rItem, nStart, nEnd ), i ); + InsertItem_(new HTMLStartEndPos(rItem, nStart, nEnd)); } void HTMLEndPosLst::SplitItem( const SfxPoolItem& rItem, sal_Int32 nStart, @@ -1511,59 +1514,47 @@ void HTMLEndPosLst::SplitItem( const SfxPoolItem& rItem, sal_Int32 nStart, // first, we must search for the old items by using the start list and // determine the new item range - for (HTMLStartEndPositions::size_type i = 0; i < m_aStartLst.size(); ++i) + for (auto& [nTestStart, items] : m_aStartLst) { - HTMLStartEndPos* pTest = m_aStartLst[i]; - sal_Int32 nTestStart = pTest->GetStart(); - sal_Int32 nTestEnd = pTest->GetEnd(); - if( nTestStart >= nEnd ) { // this attribute, and all that follow, start later break; } - else if( nTestEnd > nStart ) + + for (auto it = items.begin(); it != items.end();) { + HTMLStartEndPos* pTest = *it; + sal_Int32 nTestEnd = pTest->GetEnd(); + if (nTestEnd <= nStart) + continue; + // the Test attribute ends in the range that must be deleted - const SfxPoolItem *pItem = pTest->GetItem(); + const SfxPoolItem& rTestItem = pTest->GetItem(); // only the corresponding OnTag attributes have to be considered - if( pItem->Which() == nWhich && - HTML_ON_VALUE == GetHTMLItemState( *pItem ) ) + if (rTestItem.Which() == nWhich && HTML_ON_VALUE == GetHTMLItemState(rTestItem)) { - bool bDelete = true; + // if necessary, insert the second part of the split + // attribute + if (nTestEnd > nEnd) + InsertItem(pTest->GetItem(), nEnd, nTestEnd); - if( nTestStart < nStart ) - { - // the start of the new attribute corresponds to the new - // end of the attribute - FixSplittedItem( pTest, nStart, i ); - bDelete = false; - } - else + if (nTestStart >= nStart) { // the Test item only starts after the new end of the // attribute. Therefore, it can be completely erased. - m_aStartLst.erase(m_aStartLst.begin() + i); - i--; - - HTMLStartEndPositions::iterator it - = std::find(m_aEndLst.begin(), m_aEndLst.end(), pTest); - OSL_ENSURE(it != m_aEndLst.end(), "Item not found in End List!"); - if (it != m_aEndLst.end()) - m_aEndLst.erase(it); - } - - // if necessary, insert the second part of the split - // attribute - if( nTestEnd > nEnd ) - { - InsertItem( *pTest->GetItem(), nEnd, nTestEnd ); + it = items.erase(it); + std::erase(m_aEndLst[pTest->GetEnd()], pTest); + delete pTest; + continue; } - if( bDelete ) - delete pTest; + // the start of the new attribute corresponds to the new + // end of the attribute + FixSplittedItem(pTest, nStart); } + ++it; } } } @@ -1611,8 +1602,8 @@ HTMLEndPosLst::HTMLEndPosLst(SwDoc* pD, SwDoc* pTempl, std::optional<Color> xDfl HTMLEndPosLst::~HTMLEndPosLst() { - OSL_ENSURE(m_aStartLst.empty(), "Start List not empty in destructor"); - OSL_ENSURE(m_aEndLst.empty(), "End List not empty in destructor"); + assert(IsEmpty(m_aStartLst) && "Start List not empty in destructor"); + assert(IsEmpty(m_aEndLst) && "End List not empty in destructor"); } void HTMLEndPosLst::InsertNoScript( const SfxPoolItem& rItem, @@ -1900,53 +1891,25 @@ void HTMLEndPosLst::OutStartAttrs( SwHTMLWriter& rWrt, sal_Int32 nPos ) { rWrt.m_bTagOn = true; - // Character border attribute must be the first which is written out - // because of border merge. - HTMLStartEndPositions::size_type nCharBoxIndex = 0; - while (nCharBoxIndex < m_aStartLst.size() - && m_aStartLst[nCharBoxIndex]->GetItem()->Which() != RES_CHRATR_BOX) - { - ++nCharBoxIndex; - } - + auto it = m_aStartLst.find(nPos); + if (it == m_aStartLst.end()) + return; // the attributes of the start list are sorted in ascending order - for (HTMLStartEndPositions::size_type i = 0; i < m_aStartLst.size(); ++i) + for (HTMLStartEndPos* pPos : it->second) { - HTMLStartEndPos *pPos = nullptr; - if (nCharBoxIndex < m_aStartLst.size()) - { - if( i == 0 ) - pPos = m_aStartLst[nCharBoxIndex]; - else if( i == nCharBoxIndex ) - pPos = m_aStartLst[0]; - else - pPos = m_aStartLst[i]; - } - else - pPos = m_aStartLst[i]; - - sal_Int32 nStart = pPos->GetStart(); - if( nStart > nPos ) - { - // this attribute, and all that follow, will be opened later on - break; - } - else if( nStart == nPos ) + // output the attribute + sal_uInt16 nCSS1Script = rWrt.m_nCSS1Script; + sal_uInt16 nWhich = pPos->GetItem().Which(); + if( RES_TXTATR_CHARFMT == nWhich || + RES_TXTATR_INETFMT == nWhich || + RES_PARATR_DROP == nWhich ) { - // output the attribute - sal_uInt16 nCSS1Script = rWrt.m_nCSS1Script; - sal_uInt16 nWhich = pPos->GetItem()->Which(); - if( RES_TXTATR_CHARFMT == nWhich || - RES_TXTATR_INETFMT == nWhich || - RES_PARATR_DROP == nWhich ) - { - rWrt.m_nCSS1Script = GetScriptAtPos( nPos, nCSS1Script ); - } - HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - do we still need it? - Out( aHTMLAttrFnTab, *pPos->GetItem(), rWrt ); - rWrt.maStartedAttributes[pPos->GetItem()->Which()]++; - rWrt.m_nCSS1Script = nCSS1Script; + rWrt.m_nCSS1Script = GetScriptAtPos( nPos, nCSS1Script ); } + HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - do we still need it? + Out( aHTMLAttrFnTab, pPos->GetItem(), rWrt ); + rWrt.maStartedAttributes[pPos->GetItem().Which()]++; + rWrt.m_nCSS1Script = nCSS1Script; } } @@ -1954,59 +1917,55 @@ void HTMLEndPosLst::OutEndAttrs( SwHTMLWriter& rWrt, sal_Int32 nPos ) { rWrt.m_bTagOn = false; - // the attributes in the End list are sorted in ascending order - HTMLStartEndPositions::size_type i {0}; - while (i < m_aEndLst.size()) + if (nPos == SAL_MAX_INT32) + { + for (auto& element : m_aEndLst) + OutEndAttrs(rWrt, element.second); + } + else { - HTMLStartEndPos* pPos = m_aEndLst[i]; - sal_Int32 nEnd = pPos->GetEnd(); + auto it = m_aEndLst.find(nPos); + if (it != m_aEndLst.end()) + OutEndAttrs(rWrt, it->second); + } +} - if( SAL_MAX_INT32 == nPos || nEnd == nPos ) +void HTMLEndPosLst::OutEndAttrs(SwHTMLWriter& rWrt, std::vector<HTMLStartEndPos*>& posItems) +{ + std::sort(posItems.begin(), posItems.end(), SortEnds(m_aStartLst)); + for (auto it = posItems.begin(); it != posItems.end(); it = posItems.erase(it)) + { + HTMLStartEndPos* pPos = *it; + HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - do we still need it? + // Skip closing span if next character span has the same border (border merge) + bool bSkipOut = false; + if( pPos->GetItem().Which() == RES_CHRATR_BOX ) { - HTMLOutFuncs::FlushToAscii( rWrt.Strm() ); // was one time only - do we still need it? - // Skip closing span if next character span has the same border (border merge) - bool bSkipOut = false; - if( pPos->GetItem()->Which() == RES_CHRATR_BOX ) + auto& startPosItems = m_aStartLst[pPos->GetEnd()]; + for (auto it2 = startPosItems.begin(); it2 != startPosItems.end(); ++it2) { - HTMLStartEndPositions::iterator it - = std::find(m_aStartLst.begin(), m_aStartLst.end(), pPos); - OSL_ENSURE(it != m_aStartLst.end(), "Item not found in Start List!"); - if (it != m_aStartLst.end()) - ++it; - while (it != m_aStartLst.end()) + HTMLStartEndPos* pEndPos = *it2; + if( pEndPos->GetItem().Which() == RES_CHRATR_BOX && + static_cast<const SvxBoxItem&>(pEndPos->GetItem()) == + static_cast<const SvxBoxItem&>(pPos->GetItem()) ) { - HTMLStartEndPos *pEndPos = *it; - if( pEndPos->GetItem()->Which() == RES_CHRATR_BOX && - *static_cast<const SvxBoxItem*>(pEndPos->GetItem()) == - *static_cast<const SvxBoxItem*>(pPos->GetItem()) ) - { - pEndPos->SetStart(pPos->GetStart()); - bSkipOut = true; - break; - } - ++it; + startPosItems.erase(it2); + pEndPos->SetStart(pPos->GetStart()); + auto& oldStartPosItems = m_aStartLst[pEndPos->GetStart()]; + oldStartPosItems.insert(oldStartPosItems.begin(), pEndPos); + bSkipOut = true; + break; } } - if( !bSkipOut ) - { - Out( aHTMLAttrFnTab, *pPos->GetItem(), rWrt ); - rWrt.maStartedAttributes[pPos->GetItem()->Which()]--; - } - RemoveItem_( i ); - } - else if( nEnd > nPos ) - { - // this attribute, and all that follow, are closed later on - break; } - else + if( !bSkipOut ) { - // The attribute is closed before the current position. This - // is not allowed, but we can handle it anyway. - OSL_ENSURE( nEnd >= nPos, - "The attribute should've been closed a long time ago" ); - i++; + Out( aHTMLAttrFnTab, pPos->GetItem(), rWrt ); + rWrt.maStartedAttributes[pPos->GetItem().Which()]--; } + + std::erase(m_aStartLst[pPos->GetStart()], pPos); + delete pPos; } } commit 6018ba7cddd94be1ba932387dce9cf3a032001e3 Author: Laurent Balland <[email protected]> AuthorDate: Thu Dec 7 19:38:54 2023 +0100 Commit: Andras Timar <[email protected]> CommitDate: Tue Mar 12 15:28:18 2024 +0100 tdf#129701 Skip empty cell: treat last column When pasting data as text, with "Skip empty cells" not checked, empty cells in the last column of the source do not empty cells in destination, because there is nothing anymore to paste and while loop stops. This commit adds a flag to check if the last cell of each line is empty, and treat it if "Skip empty cells" is not checked. Add UITest to Copy data with empty cells; Paste Unformatted text, without "Skip empty cells"; Check data with empty cells in last column. Change-Id: Idec823fb1c27b803f49c6d13d6c757ef0f41e437 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160951 Tested-by: Jenkins Reviewed-by: Laurent Balland <[email protected]> (cherry picked from commit e66ffda7c7f1e78dbca082ff7549ded121dc7356) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163536 Reviewed-by: Christian Lohmaier <[email protected]> diff --git a/sc/qa/uitest/data/tdf129701.ods b/sc/qa/uitest/data/tdf129701.ods new file mode 100644 index 000000000000..94407fcd566e Binary files /dev/null and b/sc/qa/uitest/data/tdf129701.ods differ diff --git a/sc/qa/uitest/pasteSpecial/tdf129701-PasteUnformated.py b/sc/qa/uitest/pasteSpecial/tdf129701-PasteUnformated.py new file mode 100644 index 000000000000..596cd62f8bc1 --- /dev/null +++ b/sc/qa/uitest/pasteSpecial/tdf129701-PasteUnformated.py @@ -0,0 +1,70 @@ +# -*- tab-width: 4; indent-tabs-mode: nil; py-indent-offset: 4 -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +from uitest.framework import UITestCase +from libreoffice.uno.propertyvalue import mkPropertyValues +from uitest.uihelper.common import get_url_for_data_file +from uitest.uihelper.common import get_state_as_dict +from libreoffice.calc.document import get_cell_by_position + +class tdf129701(UITestCase): + + def test_tdf129701(self): + + with self.ui_test.load_file(get_url_for_data_file("tdf129701.ods")): + xCalcDoc = self.xUITest.getTopFocusWindow() + gridwin = xCalcDoc.getChild("grid_window") + gridwin.executeAction("SELECT", mkPropertyValues({"RANGE": "D21:F25"})) + self.xUITest.executeCommand(".uno:Copy") + + gridwin.executeAction("SELECT", mkPropertyValues({"CELL": "J4"})) + with self.ui_test.execute_dialog_through_command(".uno:PasteUnformatted", close_button="ok") as xDialog: + + xSkipEmtyCells = xDialog.getChild("skipemptycells") + xSeparatedBy = xDialog.getChild("toseparatedby") + xTab = xDialog.getChild("tab") + xMergeDelimiters = xDialog.getChild("mergedelimiters") + + xSeparatedBy.executeAction("CLICK", tuple()) + if get_state_as_dict(xTab)['Selected'] == 'false': + xTab.executeAction("CLICK", tuple()) + if get_state_as_dict(xMergeDelimiters)['Selected'] == 'true': + xMergeDelimiters.executeAction("CLICK", tuple()) + if get_state_as_dict(xSkipEmtyCells)['Selected'] == 'true': + xSkipEmtyCells.executeAction("CLICK", tuple()) + # Check wether Skip empty cells is unselected + self.assertEqual('false', get_state_as_dict(xSkipEmtyCells)['Selected']) + + document = self.ui_test.get_component() + # Without the fix in place, this test would have failed with + # non empty cells in column 11 + + self.assertEqual( "x1", get_cell_by_position(document, 0, 9, 3).getString()) + self.assertEqual( "" , get_cell_by_position(document, 0,10, 3).getString()) + self.assertEqual( "" , get_cell_by_position(document, 0,11, 3).getString()) + self.assertEqual("A16", get_cell_by_position(document, 0,12, 3).getString()) + self.assertEqual( "" , get_cell_by_position(document, 0, 9, 4).getString()) + self.assertEqual( "x2", get_cell_by_position(document, 0,10, 4).getString()) + self.assertEqual( "" , get_cell_by_position(document, 0,11, 4).getString()) + self.assertEqual("A17", get_cell_by_position(document, 0,12, 4).getString()) + self.assertEqual( "" , get_cell_by_position(document, 0, 9, 5).getString()) + self.assertEqual( "" , get_cell_by_position(document, 0,10, 5).getString()) + self.assertEqual( "x3", get_cell_by_position(document, 0,11, 5).getString()) + self.assertEqual("A18", get_cell_by_position(document, 0,12, 5).getString()) + self.assertEqual( "" , get_cell_by_position(document, 0, 9, 6).getString()) + self.assertEqual( "x4", get_cell_by_position(document, 0,10, 6).getString()) + self.assertEqual( "" , get_cell_by_position(document, 0,11, 6).getString()) + self.assertEqual("A19", get_cell_by_position(document, 0,12, 6).getString()) + self.assertEqual( "x5", get_cell_by_position(document, 0, 9, 7).getString()) + self.assertEqual( "x6", get_cell_by_position(document, 0,10, 7).getString()) + self.assertEqual( "x7", get_cell_by_position(document, 0,11, 7).getString()) + self.assertEqual("A20", get_cell_by_position(document, 0,12, 7).getString()) + + self.ui_test.close_doc() + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sc/source/ui/docshell/impex.cxx b/sc/source/ui/docshell/impex.cxx index 4a585657de1a..8e6315db9419 100644 --- a/sc/source/ui/docshell/impex.cxx +++ b/sc/source/ui/docshell/impex.cxx @@ -1662,6 +1662,7 @@ bool ScImportExport::ExtText2Doc( SvStream& rStrm ) ScDocumentImport aDocImport(rDoc); do { + SCCOL nLastCol = nEndCol; // tdf#129701 preserve value of nEndCol for( ;; ) { aLine = ReadCsvLine(rStrm, !bFixed, aSeps, cStr, cDetectSep); @@ -1745,11 +1746,14 @@ bool ScImportExport::ExtText2Doc( SvStream& rStrm ) SCCOL nSourceCol = 0; sal_uInt16 nInfoStart = 0; const sal_Unicode* p = aLine.getStr(); + // tdf#129701 if there is only one column, and user wants to treat empty cells, + // we need to detect *p = null + bool bIsLastColEmpty = !(*p) && !bSkipEmptyCells && !bDetermineRange; // Yes, the check is nCol<=rDoc.MaxCol()+1, +1 because it is only an // overflow if there is really data following to be put behind // the last column, which doesn't happen if info is // SC_COL_SKIP. - while (*p && nCol <= rDoc.MaxCol()+1) + while ( (*p || bIsLastColEmpty) && nCol <= rDoc.MaxCol()+1) { bool bIsQuoted = false; p = ScImportExport::ScanNextFieldFromString( p, aCell, @@ -1780,9 +1784,18 @@ bool ScImportExport::ExtText2Doc( SvStream& rStrm ) aTransliteration, aCalendar, pEnglishTransliteration.get(), pEnglishCalendar.get()); } - ++nCol; - } + if (bIsLastColEmpty) + { + bIsLastColEmpty = false; // toggle to stop + } + else + { + ++nCol; + // tdf#129701 detect if there is a last empty column when we need it + bIsLastColEmpty = !(*p) && !bSkipEmptyCells && !bDetermineRange && nCol == nLastCol; + } + } ++nSourceCol; } } commit c69758a45db34649e338067c24416cbc715cee8f Author: Michael Stahl <[email protected]> AuthorDate: Thu Feb 15 13:06:32 2024 +0100 Commit: Andras Timar <[email protected]> CommitDate: Tue Mar 12 15:28:18 2024 +0100 tdf#159502 remove --without-system-libxml from LibreOfficeLinux.conf configure.ac explicitly excludes libxml from --without-system-libs but this distro-config "helpfully" adds it explicitly, when the system already contains a usable libxml/libxslt anyway. Change-Id: I79d65d9ed9185277ffde8624efc152b290a6ffb3 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163438 Tested-by: Jenkins Reviewed-by: Michael Stahl <[email protected]> (cherry picked from commit 6f6364e572a0570fb097fec098314bffadc5b474) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/163409 Reviewed-by: Christian Lohmaier <[email protected]> diff --git a/distro-configs/LibreOfficeLinux.conf b/distro-configs/LibreOfficeLinux.conf index db759a8cb4e5..fba02d362d31 100644 --- a/distro-configs/LibreOfficeLinux.conf +++ b/distro-configs/LibreOfficeLinux.conf @@ -4,7 +4,6 @@ --without-system-poppler --without-system-openssl --without-system-libpng ---without-system-libxml --without-system-jpeg --without-system-jars --without-system-postgresql
