diff options
Diffstat (limited to 'sc')
25 files changed, 714 insertions, 125 deletions
diff --git a/sc/CppunitTest_sc_parallelism.mk b/sc/CppunitTest_sc_parallelism.mk new file mode 100644 index 0000000000..f7f3cc9fa7 --- /dev/null +++ b/sc/CppunitTest_sc_parallelism.mk @@ -0,0 +1,81 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +#************************************************************************* +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# +#************************************************************************* + +$(eval $(call gb_CppunitTest_CppunitTest,sc_parallelism)) + +$(eval $(call gb_CppunitTest_use_common_precompiled_header,sc_parallelism)) + +$(eval $(call gb_CppunitTest_add_exception_objects,sc_parallelism, \ + sc/qa/unit/parallelism \ +)) + +$(eval $(call gb_CppunitTest_use_externals,sc_parallelism, \ + boost_headers \ + mdds_headers \ + libxml2 \ + $(call gb_Helper_optional,OPENCL, \ + clew) \ +)) + +$(eval $(call gb_CppunitTest_use_libraries,sc_parallelism, \ + basegfx \ + comphelper \ + cppu \ + cppuhelper \ + i18nlangtag \ + sal \ + sax \ + sc \ + scqahelper \ + sfx \ + subsequenttest \ + svl \ + svx \ + svxcore \ + test \ + tl \ + unotest \ + utl \ + vcl \ +)) + +$(eval $(call gb_CppunitTest_set_include,sc_parallelism,\ + -I$(SRCDIR)/sc/source/ui/inc \ + -I$(SRCDIR)/sc/inc \ + $$(INCLUDE) \ +)) + +$(eval $(call gb_CppunitTest_use_api,sc_parallelism,\ + offapi \ + udkapi \ +)) + +$(eval $(call gb_CppunitTest_use_custom_headers,sc_parallelism,\ + officecfg/registry \ +)) + +$(eval $(call gb_CppunitTest_use_sdk_api,sc_parallelism)) + +$(eval $(call gb_CppunitTest_use_ure,sc_parallelism)) + +$(eval $(call gb_CppunitTest_use_vcl,sc_parallelism)) + +$(eval $(call gb_CppunitTest_use_rdb,sc_parallelism,services)) + +$(eval $(call gb_CppunitTest_use_components,sc_parallelism)) + +$(eval $(call gb_CppunitTest_use_configuration,sc_parallelism)) + +$(eval $(call gb_CppunitTest_add_arguments,sc_parallelism, \ + -env:arg-env=$(gb_Helper_LIBRARY_PATH_VAR)"$$$${$(gb_Helper_LIBRARY_PATH_VAR)+=$$$$$(gb_Helper_LIBRARY_PATH_VAR)}" \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/sc/Module_sc.mk b/sc/Module_sc.mk index a01c9115f3..3281184d82 100644 --- a/sc/Module_sc.mk +++ b/sc/Module_sc.mk @@ -62,6 +62,7 @@ $(eval $(call gb_Module_add_check_targets,sc,\ CppunitTest_sc_core \ CppunitTest_sc_dataprovider \ CppunitTest_sc_cache_test \ + CppunitTest_sc_parallelism \ CppunitTest_sc_shapetest \ )) endif diff --git a/sc/qa/extras/scpdfexport.cxx b/sc/qa/extras/scpdfexport.cxx index bcca563ec9..78084b30b3 100644 --- a/sc/qa/extras/scpdfexport.cxx +++ b/sc/qa/extras/scpdfexport.cxx @@ -58,6 +58,7 @@ private: // unit tests public: + void testMediaShapeScreen_Tdf159094(); void testExportRange_Tdf120161(); void testExportFitToPage_Tdf103516(); void testUnoCommands_Tdf120161(); @@ -69,6 +70,7 @@ public: void testForcepoint97(); CPPUNIT_TEST_SUITE(ScPDFExportTest); + CPPUNIT_TEST(testMediaShapeScreen_Tdf159094); CPPUNIT_TEST(testExportRange_Tdf120161); CPPUNIT_TEST(testExportFitToPage_Tdf103516); CPPUNIT_TEST(testUnoCommands_Tdf120161); @@ -199,6 +201,18 @@ void ScPDFExportTest::setFont(ScFieldEditEngine& rEE, sal_Int32 nStart, sal_Int3 rEE.QuickSetAttribs(aItemSet, aSel); } +void ScPDFExportTest::testMediaShapeScreen_Tdf159094() +{ + loadFromFile(u"tdf159094.ods"); + uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY); + + // A1:B8 + ScRange aRange(0, 0, 0, 1, 7, 0); + + // Without the fix, this test would crash on export media file to pdf + exportToPDF(xModel, aRange); +} + // Selection was not taken into account during export into PDF void ScPDFExportTest::testExportRange_Tdf120161() { diff --git a/sc/qa/extras/testdocuments/tdf159094.ods b/sc/qa/extras/testdocuments/tdf159094.ods Binary files differnew file mode 100644 index 0000000000..c267b21521 --- /dev/null +++ b/sc/qa/extras/testdocuments/tdf159094.ods diff --git a/sc/qa/uitest/pasteSpecial/tdf160765.py b/sc/qa/uitest/pasteSpecial/tdf160765.py new file mode 100755 index 0000000000..0b56be4c2d --- /dev/null +++ b/sc/qa/uitest/pasteSpecial/tdf160765.py @@ -0,0 +1,75 @@ +# -*- 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.calc.document import get_cell_by_position +from libreoffice.uno.propertyvalue import mkPropertyValues +from uitest.uihelper.calc import enter_text_to_cell +from libreoffice.calc.paste_special import reset_default_values + +class tdf160765(UITestCase): + def test_tdf160765_undo_paste_comment(self): + with self.ui_test.create_doc_in_start_center("calc") as document: + xGridWin = self.xUITest.getTopFocusWindow().getChild("grid_window") + + # Write text to cell A1 and B1 + enter_text_to_cell(xGridWin, "A1", "A1 sample text") + enter_text_to_cell(xGridWin, "B1", "B1 sample text") + + # Insert a comment in cell B1 + xArgs = mkPropertyValues({"Text": "Comment 1"}) + self.xUITest.executeCommandWithParameters(".uno:InsertAnnotation", xArgs) + + # Insert a comment in cell A2 + xGridWin.executeAction("SELECT", mkPropertyValues({"CELL":"A2"})) + xArgs = mkPropertyValues({"Text": "Comment 2"}) + self.xUITest.executeCommandWithParameters(".uno:InsertAnnotation", xArgs) + + # Copy cell A2 to clipboard + xGridWin.executeAction("SELECT", mkPropertyValues({"CELL": "A2"})) + self.xUITest.executeCommand(".uno:Copy") + + # Set cursor to cells and paste data using special options (check only comments) + targetCells = ["A1", "B1"] + for index, targetCell in enumerate(targetCells): + xGridWin.executeAction("SELECT", mkPropertyValues({"CELL": targetCell})) + with self.ui_test.execute_dialog_through_command(".uno:PasteSpecial") as xPasteSpecialDlg: + reset_default_values(self, xPasteSpecialDlg) + xDateTimeChkBox = xPasteSpecialDlg.getChild("datetime") + xDateTimeChkBox.executeAction("CLICK", tuple()) + xTextChkBox = xPasteSpecialDlg.getChild("text") + xTextChkBox.executeAction("CLICK", tuple()) + xNumbersChkBox = xPasteSpecialDlg.getChild("numbers") + xNumbersChkBox.executeAction("CLICK", tuple()) + xCommentsChkBox = xPasteSpecialDlg.getChild("comments") + xCommentsChkBox.executeAction("CLICK", tuple()) + + # Undo both inserted comments + self.xUITest.executeCommand(".uno:Undo") + # Without the fix in place, this test would have failed with + # AssertionError: 'Comment 1' != '' + # i.e., the cell does not contain any comment + self.assertEqual("Comment 1", get_cell_by_position(document, 0, 1, 0).Annotation.String) + self.xUITest.executeCommand(".uno:Undo") + self.assertEqual("", get_cell_by_position(document, 0, 0, 0).Annotation.String) + + # Redo both inserted comments + self.xUITest.executeCommand(".uno:Redo") + # Without the fix in place, this test would have failed with + # AssertionError: 'Comment 2' != '' + # i.e., the cell does not contain the restored comment + self.assertEqual("Comment 2", get_cell_by_position(document, 0, 0, 0).Annotation.String) + self.xUITest.executeCommand(".uno:Redo") + # Without the fix in place, this test would have failed with + # AssertionError: 'Comment 2' != '' + # i.e., the cell does not contain the restored comment + self.assertEqual("Comment 2", get_cell_by_position(document, 0, 0, 0).Annotation.String) + +# vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sc/qa/unit/data/ods/tdf160368.ods b/sc/qa/unit/data/ods/tdf160368.ods Binary files differnew file mode 100644 index 0000000000..f9da81d278 --- /dev/null +++ b/sc/qa/unit/data/ods/tdf160368.ods diff --git a/sc/qa/unit/data/ods/tdf160369_groupshape.ods b/sc/qa/unit/data/ods/tdf160369_groupshape.ods Binary files differnew file mode 100644 index 0000000000..8c26fe8ce5 --- /dev/null +++ b/sc/qa/unit/data/ods/tdf160369_groupshape.ods diff --git a/sc/qa/unit/parallelism.cxx b/sc/qa/unit/parallelism.cxx new file mode 100644 index 0000000000..0ced71c442 --- /dev/null +++ b/sc/qa/unit/parallelism.cxx @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> + +#include "helper/qahelper.hxx" + +#include <document.hxx> +#include <formulagroup.hxx> + +#include <officecfg/Office/Calc.hxx> + +using namespace sc; + +// test-suite suitable for loading documents to test parallelism in +// with access only to exported symbols + +class ScParallelismTest : public ScModelTestBase +{ +public: + ScParallelismTest() + : ScModelTestBase("sc/qa/unit/data") + { + } + + virtual void setUp() override; + virtual void tearDown() override; + +private: + bool getThreadingFlag() const; + void setThreadingFlag(bool bSet); + + bool m_bThreadingFlagCfg; +}; + +bool ScParallelismTest::getThreadingFlag() const +{ + return officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups:: + get(); +} + +void ScParallelismTest::setThreadingFlag(bool bSet) +{ + std::shared_ptr<comphelper::ConfigurationChanges> xBatch( + comphelper::ConfigurationChanges::create()); + officecfg::Office::Calc::Formula::Calculation::UseThreadedCalculationForFormulaGroups::set( + bSet, xBatch); + xBatch->commit(); +} + +void ScParallelismTest::setUp() +{ + ScModelTestBase::setUp(); + + sc::FormulaGroupInterpreter::disableOpenCL_UnitTestsOnly(); + + m_bThreadingFlagCfg = getThreadingFlag(); + if (!m_bThreadingFlagCfg) + setThreadingFlag(true); +} + +void ScParallelismTest::tearDown() +{ + // Restore threading flag + if (!m_bThreadingFlagCfg) + setThreadingFlag(false); + + ScModelTestBase::tearDown(); +} + +// Dependency range in this document was detected as I9:I9 instead of expected I9:I367 +CPPUNIT_TEST_FIXTURE(ScParallelismTest, testTdf160368) +{ + createScDoc("ods/tdf160368.ods"); + ScDocShell* pDocSh = getScDocShell(); + // without fix: ScFormulaCell::MaybeInterpret(): Assertion `!rDocument.IsThreadedGroupCalcInProgress()' failed. + pDocSh->DoHardRecalc(); +} + +CPPUNIT_PLUGIN_IMPLEMENT(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sc/qa/unit/scshapetest.cxx b/sc/qa/unit/scshapetest.cxx index 5e48270053..c5b4b098c8 100644 --- a/sc/qa/unit/scshapetest.cxx +++ b/sc/qa/unit/scshapetest.cxx @@ -1231,6 +1231,74 @@ CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf160003_copy_page_anchored) CPPUNIT_ASSERT_EQUAL(size_t(1), pPage->GetObjCount()); } +CPPUNIT_TEST_FIXTURE(ScShapeTest, testTdf160369_groupshape) +{ + // The document contains a group spanning range C5:F12. It is currently anchored to page to + // make sure its position does not change. When the group was anchored 'To Cell' and rows or + // columns were hidden before the group, saving changed the anchor position and anchor + // offset. This happened both with using 'resize with cell' and not. + createScDoc("ods/tdf160369_groupshape.ods"); + + // Get document and group object + ScDocument* pDoc = getScDoc(); + SdrObject* pObj = lcl_getSdrObjectWithAssert(*pDoc, 0); + + // Anchor group 'To Cell (resize with cell)' to prepare the test. + ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *pDoc, 0 /*SCTAB*/, true /*bResizeWithCell*/); + + // Hide rows 3 and 4 (UI number), which are before the group + // Hide column D, which is inside the group + pDoc->SetRowHidden(2, 3, 0, true); + pDoc->SetDrawPageSize(0); // trigger recalcpos, otherwise shapes are not changed + pDoc->SetColHidden(3, 3, 0, true); + pDoc->SetDrawPageSize(0); + + // Get geometry of the group + ScDrawObjData* pObjData = ScDrawLayer::GetObjData(pObj); + ScAddress aOrigStart = (*pObjData).maStart; + ScAddress aOrigEnd = (*pObjData).maEnd; + tools::Rectangle aOrigRect = pObj->GetSnapRect(); + + // Save document but do not reload. Saving alone had already caused the error. + save("calc8"); + + // Get geometry of the group again + ScDrawObjData* pAfterObjData = ScDrawLayer::GetObjData(pObj); + ScAddress aAfterStart = (*pAfterObjData).maStart; + ScAddress aAfterEnd = (*pAfterObjData).maEnd; + tools::Rectangle aAfterRect = pObj->GetSnapRect(); + + // verify Orig equals After + CPPUNIT_ASSERT_EQUAL(aOrigStart, aAfterStart); + CPPUNIT_ASSERT_EQUAL(aOrigEnd, aAfterEnd); + CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aOrigRect, aAfterRect, 1); + + // The same but with saveAndReload. + createScDoc("ods/tdf160369_groupshape.ods"); + pDoc = getScDoc(); + pObj = lcl_getSdrObjectWithAssert(*pDoc, 0); + ScDrawLayer::SetCellAnchoredFromPosition(*pObj, *pDoc, 0 /*SCTAB*/, true /*bResizeWithCell*/); + pDoc->SetRowHidden(2, 3, 0, true); + pDoc->SetDrawPageSize(0); // trigger recalcpos, otherwise shapes are not changed + pDoc->SetColHidden(3, 3, 0, true); + pDoc->SetDrawPageSize(0); + + saveAndReload("calc8"); + + // Verify geometry is same as before save + pDoc = getScDoc(); + pObj = lcl_getSdrObjectWithAssert(*pDoc, 0); + pAfterObjData = ScDrawLayer::GetObjData(pObj); + aAfterStart = (*pAfterObjData).maStart; + aAfterEnd = (*pAfterObjData).maEnd; + aAfterRect = pObj->GetSnapRect(); + + // verify Orig equals After + CPPUNIT_ASSERT_EQUAL(aOrigStart, aAfterStart); + CPPUNIT_ASSERT_EQUAL(aOrigEnd, aAfterEnd); + CPPUNIT_ASSERT_RECTANGLE_EQUAL_WITH_TOLERANCE(aOrigRect, aAfterRect, 1); +} + CPPUNIT_PLUGIN_IMPLEMENT(); /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sc/qa/unit/ucalc_parallelism.cxx b/sc/qa/unit/ucalc_parallelism.cxx index 2ee55708db..8996fb5fdc 100644 --- a/sc/qa/unit/ucalc_parallelism.cxx +++ b/sc/qa/unit/ucalc_parallelism.cxx @@ -21,6 +21,9 @@ using namespace css; using namespace css::uno; +// test-suite suitable for creating documents to test parallelism in +// with access to internal unexported symbols + class ScParallelismTest : public ScUcalcTestBase { public: diff --git a/sc/source/core/data/documen9.cxx b/sc/source/core/data/documen9.cxx index a9f04943c0..ee72637fa9 100644 --- a/sc/source/core/data/documen9.cxx +++ b/sc/source/core/data/documen9.cxx @@ -440,13 +440,8 @@ bool ScDocument::IsPrintEmpty( SCCOL nStartCol, SCROW nStartRow, // keep vertical part of aMMRect, only update horizontal position aMMRect = *pLastMM; - tools::Long nLeft = 0; - SCCOL i; - for (i=0; i<nStartCol; i++) - nLeft += GetColWidth(i,nTab); - tools::Long nRight = nLeft; - for (i=nStartCol; i<=nEndCol; i++) - nRight += GetColWidth(i,nTab); + tools::Long nLeft = GetColWidth(0, nStartCol-1, nTab); + tools::Long nRight = nLeft + GetColWidth(nStartCol,nEndCol, nTab); aMMRect.SetLeft(o3tl::convert(nLeft, o3tl::Length::twip, o3tl::Length::mm100)); aMMRect.SetRight(o3tl::convert(nRight, o3tl::Length::twip, o3tl::Length::mm100)); diff --git a/sc/source/core/data/drwlayer.cxx b/sc/source/core/data/drwlayer.cxx index d5f2cc09b9..d03f3572dc 100644 --- a/sc/source/core/data/drwlayer.cxx +++ b/sc/source/core/data/drwlayer.cxx @@ -1055,6 +1055,10 @@ void ScDrawLayer::InitializeCellAnchoredObj(SdrObject* pObj, ScDrawObjData& rDat UpdateCellAnchorFromPositionEnd(*pObj, rNoRotatedAnchor, *pDoc, nTab1, true /*bUseLogicRect*/); } + else if (pObj->GetObjIdentifier() == SdrObjKind::Group) + { + // nothing to do. + } else { // In case there are hidden rows or cols, versions 7.0 and earlier have written width and diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx index f278492b09..93d712d106 100644 --- a/sc/source/core/data/formulacell.cxx +++ b/sc/source/core/data/formulacell.cxx @@ -4607,10 +4607,17 @@ struct ScDependantsCalculator continue; } + SCROW nFirstRefStartRow = bIsRef1RowRel ? aAbs.aStart.Row() + mnStartOffset : aAbs.aStart.Row(); + SCROW nLastRefEndRow = bIsRef2RowRel ? aAbs.aEnd.Row() + mnEndOffset : aAbs.aEnd.Row(); + + SCROW nFirstRefEndRow = bIsRef1RowRel ? aAbs.aStart.Row() + mnEndOffset : aAbs.aStart.Row(); + SCROW nLastRefStartRow = bIsRef2RowRel ? aAbs.aEnd.Row() + mnStartOffset : aAbs.aEnd.Row(); + // The first row that will be referenced through the doubleref. - SCROW nFirstRefRow = bIsRef1RowRel ? aAbs.aStart.Row() + mnStartOffset : aAbs.aStart.Row(); + SCROW nFirstRefRow = std::min(nFirstRefStartRow, nLastRefStartRow); // The last row that will be referenced through the doubleref. - SCROW nLastRefRow = bIsRef2RowRel ? aAbs.aEnd.Row() + mnEndOffset : aAbs.aEnd.Row(); + SCROW nLastRefRow = std::max(nLastRefEndRow, nFirstRefEndRow); + // Number of rows to be evaluated from nFirstRefRow. SCROW nArrayLength = nLastRefRow - nFirstRefRow + 1; assert(nArrayLength > 0); diff --git a/sc/source/core/data/table1.cxx b/sc/source/core/data/table1.cxx index 93d023f962..4df5d92ec5 100644 --- a/sc/source/core/data/table1.cxx +++ b/sc/source/core/data/table1.cxx @@ -2091,17 +2091,19 @@ void ScTable::ExtendPrintArea( OutputDevice* pDev, else { // These columns are visible. Check for empty columns. - for (SCCOL j = i; j <= nLastCol; ++j) + SCCOL nEmptyCount = 0; + SCCOL j = i; + for (; j <= nLastCol; ++j) { if ( j >= aCol.size() ) - { - aSkipCols.setTrue( j, rDocument.MaxCol() ); break; - } - if (aCol[j].GetCellCount() == 0) - // empty - aSkipCols.setTrue(j,j); + if (aCol[j].GetCellCount() == 0) // empty + nEmptyCount++; } + if (nEmptyCount) + aSkipCols.setTrue(i,i+nEmptyCount); + if ( j >= aCol.size() ) + aSkipCols.setTrue( j, rDocument.MaxCol() ); } i = nLastCol; } diff --git a/sc/source/filter/html/htmlpars.cxx b/sc/source/filter/html/htmlpars.cxx index c9d53d93be..c6507bd54e 100644 --- a/sc/source/filter/html/htmlpars.cxx +++ b/sc/source/filter/html/htmlpars.cxx @@ -301,7 +301,7 @@ ScHTMLLayoutParser::ScHTMLLayoutParser( aPageSize( aPageSizeP ), aBaseURL(std::move( _aBaseURL )), xLockedList( new ScRangeList ), - pLocalColOffset( new ScHTMLColOffset ), + xLocalColOffset( new ScHTMLColOffset ), nFirstTableCell(0), nTableLevel(0), nTable(0), @@ -317,20 +317,15 @@ ScHTMLLayoutParser::ScHTMLLayoutParser( bInCell( false ), bInTitle( false ) { - MakeColNoRef( pLocalColOffset, 0, 0, 0, 0 ); + MakeColNoRef( xLocalColOffset.get(), 0, 0, 0, 0 ); MakeColNoRef( &maColOffset, 0, 0, 0, 0 ); } ScHTMLLayoutParser::~ScHTMLLayoutParser() { - while ( !aTableStack.empty() ) - { - ScHTMLTableStackEntry * pS = aTableStack.top().get(); - if ( pS->pLocalColOffset != pLocalColOffset ) - delete pS->pLocalColOffset; + while (!aTableStack.empty()) aTableStack.pop(); - } - delete pLocalColOffset; + xLocalColOffset.reset(); if ( pTables ) { for( const auto& rEntry : *pTables) @@ -676,8 +671,8 @@ void ScHTMLLayoutParser::Adjust() SkipLocked(pE.get(), false); if ( pE->nCol != nColBeforeSkip ) { - SCCOL nCount = static_cast<SCCOL>(maColOffset.size()); - if ( nCount <= pE->nCol ) + size_t nCount = maColOffset.size(); + if ( nCount <= o3tl::make_unsigned(pE->nCol) ) { pE->nOffset = static_cast<sal_uInt16>(maColOffset[nCount-1]); MakeCol( &maColOffset, pE->nOffset, pE->nWidth, nOffsetTolerance, nOffsetTolerance ); @@ -713,9 +708,9 @@ sal_uInt16 ScHTMLLayoutParser::GetWidth( const ScEEParseEntry* pE ) return pE->nWidth; sal_Int32 nTmp = std::min( static_cast<sal_Int32>( pE->nCol - nColCntStart + pE->nColOverlap), - static_cast<sal_Int32>( pLocalColOffset->size() - 1)); + static_cast<sal_Int32>( xLocalColOffset->size() - 1)); SCCOL nPos = (nTmp < 0 ? 0 : static_cast<SCCOL>(nTmp)); - sal_uInt16 nOff2 = static_cast<sal_uInt16>((*pLocalColOffset)[nPos]); + sal_uInt16 nOff2 = static_cast<sal_uInt16>((*xLocalColOffset)[nPos]); if ( pE->nOffset < nOff2 ) return nOff2 - pE->nOffset; return 0; @@ -723,28 +718,27 @@ sal_uInt16 ScHTMLLayoutParser::GetWidth( const ScEEParseEntry* pE ) void ScHTMLLayoutParser::SetWidths() { - SCCOL nCol; if ( !nTableWidth ) nTableWidth = static_cast<sal_uInt16>(aPageSize.Width()); SCCOL nColsPerRow = nMaxCol - nColCntStart; if ( nColsPerRow <= 0 ) nColsPerRow = 1; - if ( pLocalColOffset->size() <= 2 ) + if ( xLocalColOffset->size() <= 2 ) { // Only PageSize, there was no width setting sal_uInt16 nWidth = nTableWidth / static_cast<sal_uInt16>(nColsPerRow); sal_uInt16 nOff = nColOffsetStart; - pLocalColOffset->clear(); - for ( nCol = 0; nCol <= nColsPerRow; ++nCol, nOff = nOff + nWidth ) + xLocalColOffset->clear(); + for (int nCol = 0; nCol <= nColsPerRow; ++nCol, nOff = nOff + nWidth) { - MakeColNoRef( pLocalColOffset, nOff, 0, 0, 0 ); + MakeColNoRef( xLocalColOffset.get(), nOff, 0, 0, 0 ); } - nTableWidth = static_cast<sal_uInt16>(pLocalColOffset->back() - pLocalColOffset->front()); + nTableWidth = static_cast<sal_uInt16>(xLocalColOffset->back() - xLocalColOffset->front()); for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i ) { auto& pE = maList[ i ]; if ( pE->nTab == nTable ) { - pE->nOffset = static_cast<sal_uInt16>((*pLocalColOffset)[pE->nCol - nColCntStart]); + pE->nOffset = static_cast<sal_uInt16>((*xLocalColOffset)[pE->nCol - nColCntStart]); pE->nWidth = 0; // to be recalculated later } } @@ -764,7 +758,7 @@ void ScHTMLLayoutParser::SetWidths() auto& pE = maList[ i ]; if ( pE->nTab == nTable && pE->nWidth ) { - nCol = pE->nCol - nColCntStart; + SCCOL nCol = pE->nCol - nColCntStart; if ( nCol < nColsPerRow ) { if ( pE->nColOverlap == 1 ) @@ -801,7 +795,7 @@ void ScHTMLLayoutParser::SetWidths() } sal_uInt16 nWidths = 0; sal_uInt16 nUnknown = 0; - for ( nCol = 0; nCol < nColsPerRow; nCol++ ) + for (SCCOL nCol = 0; nCol < nColsPerRow; nCol++) { if ( pWidths[nCol] ) nWidths = nWidths + pWidths[nCol]; @@ -813,51 +807,59 @@ void ScHTMLLayoutParser::SetWidths() sal_uInt16 nW = ((nWidths < nTableWidth) ? ((nTableWidth - nWidths) / nUnknown) : (nTableWidth / nUnknown)); - for ( nCol = 0; nCol < nColsPerRow; nCol++ ) + for (SCCOL nCol = 0; nCol < nColsPerRow; nCol++) { if ( !pWidths[nCol] ) pWidths[nCol] = nW; } } - for ( nCol = 1; nCol <= nColsPerRow; nCol++ ) + for (SCCOL nCol = 1; nCol <= nColsPerRow; nCol++) { pOffsets[nCol] = pOffsets[nCol-1] + pWidths[nCol-1]; } - pLocalColOffset->clear(); - for ( nCol = 0; nCol <= nColsPerRow; nCol++ ) + xLocalColOffset->clear(); + for (SCCOL nCol = 0; nCol <= nColsPerRow; nCol++) { - MakeColNoRef( pLocalColOffset, pOffsets[nCol], 0, 0, 0 ); + MakeColNoRef( xLocalColOffset.get(), pOffsets[nCol], 0, 0, 0 ); } nTableWidth = pOffsets[nColsPerRow] - pOffsets[0]; for ( size_t i = nFirstTableCell, nListSize = maList.size(); i < nListSize; ++i ) { auto& pE = maList[ i ]; - if ( pE->nTab == nTable ) + if (pE->nTab != nTable) + continue; + SCCOL nCol = pE->nCol - nColCntStart; + OSL_ENSURE( nCol < nColsPerRow, "ScHTMLLayoutParser::SetWidths: column overflow" ); + if (nCol >= nColsPerRow) + continue; + if (nCol < 0) { - nCol = pE->nCol - nColCntStart; - OSL_ENSURE( nCol < nColsPerRow, "ScHTMLLayoutParser::SetWidths: column overflow" ); - if ( nCol < nColsPerRow ) - { - pE->nOffset = pOffsets[nCol]; - nCol = nCol + pE->nColOverlap; - if ( nCol > nColsPerRow ) - nCol = nColsPerRow; - pE->nWidth = pOffsets[nCol] - pE->nOffset; - } + SAL_WARN("sc", "negative offset: " << nCol); + continue; } + pE->nOffset = pOffsets[nCol]; + nCol = nCol + pE->nColOverlap; + if ( nCol > nColsPerRow ) + nCol = nColsPerRow; + if (nCol < 0) + { + SAL_WARN("sc", "negative offset: " << nCol); + continue; + } + pE->nWidth = pOffsets[nCol] - pE->nOffset; } } } - if ( !pLocalColOffset->empty() ) + if ( !xLocalColOffset->empty() ) { - sal_uInt16 nMax = static_cast<sal_uInt16>(pLocalColOffset->back()); + sal_uInt16 nMax = static_cast<sal_uInt16>(xLocalColOffset->back()); if ( aPageSize.Width() < nMax ) aPageSize.setWidth( nMax ); if (nTableLevel == 0) { // Local table is very outer table, create missing offsets. - for (auto it = pLocalColOffset->begin(); it != pLocalColOffset->end(); ++it) + for (auto it = xLocalColOffset->begin(); it != xLocalColOffset->end(); ++it) { // Only exact offsets, do not use MakeColNoRef(). maColOffset.insert(*it); @@ -891,15 +893,15 @@ void ScHTMLLayoutParser::Colonize( ScEEParseEntry* pE ) if ( nCol < pE->nCol ) { // Replaced nCol = pE->nCol - nColCntStart; - SCCOL nCount = static_cast<SCCOL>(pLocalColOffset->size()); + SCCOL nCount = static_cast<SCCOL>(xLocalColOffset->size()); if ( nCol < nCount ) - nColOffset = static_cast<sal_uInt16>((*pLocalColOffset)[nCol]); + nColOffset = static_cast<sal_uInt16>((*xLocalColOffset)[nCol]); else - nColOffset = static_cast<sal_uInt16>((*pLocalColOffset)[nCount - 1]); + nColOffset = static_cast<sal_uInt16>((*xLocalColOffset)[nCount - 1]); } pE->nOffset = nColOffset; sal_uInt16 nWidth = GetWidth( pE ); - MakeCol( pLocalColOffset, pE->nOffset, nWidth, nOffsetTolerance, nOffsetTolerance ); + MakeCol( xLocalColOffset.get(), pE->nOffset, nWidth, nOffsetTolerance, nOffsetTolerance ); if ( pE->nWidth ) pE->nWidth = nWidth; nColOffset = pE->nOffset + nWidth; @@ -913,7 +915,8 @@ void ScHTMLLayoutParser::CloseEntry( const HtmlImportInfo* pInfo ) if ( bTabInTabCell ) { // From the stack in TableOff bTabInTabCell = false; - NewActEntry(maList.back().get()); // New free flying mxActEntry + SAL_WARN_IF(maList.empty(), "sc", "unexpected close entry without open"); + NewActEntry(maList.empty() ? nullptr : maList.back().get()); // New free flying mxActEntry return ; } if (mxActEntry->nTab == 0) @@ -1035,12 +1038,20 @@ void ScHTMLLayoutParser::TableDataOn( HtmlImportInfo* pInfo ) { case HtmlOptionId::COLSPAN: { - mxActEntry->nColOverlap = static_cast<SCCOL>(rOption.GetString().toInt32()); + sal_Int32 nColOverlap = rOption.GetString().toInt32(); + if (nColOverlap >= 0 && nColOverlap <= SCCOL_MAX) + mxActEntry->nColOverlap = static_cast<SCCOL>(nColOverlap); + else + SAL_WARN("sc", "ScHTMLLayoutParser::TableDataOn ignoring colspan: " << nColOverlap); } break; case HtmlOptionId::ROWSPAN: { - mxActEntry->nRowOverlap = static_cast<SCROW>(rOption.GetString().toInt32()); + sal_Int32 nRowOverlap = rOption.GetString().toInt32(); + if (nRowOverlap >= 0) + mxActEntry->nRowOverlap = static_cast<SCROW>(nRowOverlap); + else + SAL_WARN("sc", "ScHTMLLayoutParser::TableDataOn ignoring rowspan: " << nRowOverlap); } break; case HtmlOptionId::ALIGN: @@ -1143,7 +1154,7 @@ void ScHTMLLayoutParser::TableOn( HtmlImportInfo* pInfo ) sal_uInt16 nTmpColOffset = nColOffset; // Will be changed in Colonize() Colonize(mxActEntry.get()); aTableStack.push( std::make_unique<ScHTMLTableStackEntry>( - mxActEntry, xLockedList, pLocalColOffset, nFirstTableCell, + mxActEntry, xLockedList, xLocalColOffset, nFirstTableCell, nRowCnt, nColCntStart, nMaxCol, nTable, nTableWidth, nColOffset, nColOffsetStart, bFirstRow ) ); @@ -1199,7 +1210,7 @@ void ScHTMLLayoutParser::TableOn( HtmlImportInfo* pInfo ) NextRow( pInfo ); } aTableStack.push( std::make_unique<ScHTMLTableStackEntry>( - mxActEntry, xLockedList, pLocalColOffset, nFirstTableCell, + mxActEntry, xLockedList, xLocalColOffset, nFirstTableCell, nRowCnt, nColCntStart, nMaxCol, nTable, nTableWidth, nColOffset, nColOffsetStart, bFirstRow ) ); @@ -1232,8 +1243,8 @@ void ScHTMLLayoutParser::TableOn( HtmlImportInfo* pInfo ) bFirstRow = true; nFirstTableCell = maList.size(); - pLocalColOffset = new ScHTMLColOffset; - MakeColNoRef( pLocalColOffset, nColOffsetStart, 0, 0, 0 ); + xLocalColOffset.reset(new ScHTMLColOffset); + MakeColNoRef( xLocalColOffset.get(), nColOffsetStart, 0, 0, 0 ); } void ScHTMLLayoutParser::TableOff( const HtmlImportInfo* pInfo ) @@ -1349,7 +1360,7 @@ void ScHTMLLayoutParser::TableOff( const HtmlImportInfo* pInfo ) { sal_uInt16 nOldOffset = pE->nOffset + pE->nWidth; sal_uInt16 nNewOffset = pE->nOffset + nTableWidth; - ModifyOffset( pS->pLocalColOffset, nOldOffset, nNewOffset, nOffsetTolerance ); + ModifyOffset( pS->xLocalColOffset.get(), nOldOffset, nNewOffset, nOffsetTolerance ); sal_uInt16 nTmp = nNewOffset - pE->nOffset - pE->nWidth; pE->nWidth = nNewOffset - pE->nOffset; pS->nTableWidth = pS->nTableWidth + nTmp; @@ -1368,7 +1379,7 @@ void ScHTMLLayoutParser::TableOff( const HtmlImportInfo* pInfo ) nColOffsetStart = pS->nColOffsetStart; bFirstRow = pS->bFirstRow; xLockedList = pS->xLockedList; - pLocalColOffset = pS->pLocalColOffset; + xLocalColOffset = pS->xLocalColOffset; // mxActEntry is kept around if a table is started in the same row // (anything's possible in HTML); will be deleted by CloseEntry mxActEntry = pE; @@ -1384,8 +1395,7 @@ void ScHTMLLayoutParser::TableOff( const HtmlImportInfo* pInfo ) if ( !aTableStack.empty() ) { ScHTMLTableStackEntry* pS = aTableStack.top().get(); - delete pLocalColOffset; - pLocalColOffset = pS->pLocalColOffset; + xLocalColOffset = std::move(pS->xLocalColOffset); aTableStack.pop(); } } @@ -1491,7 +1501,7 @@ void ScHTMLLayoutParser::ColOn( HtmlImportInfo* pInfo ) if( rOption.GetToken() == HtmlOptionId::WIDTH ) { sal_uInt16 nVal = GetWidthPixel( rOption ); - MakeCol( pLocalColOffset, nColOffset, nVal, 0, 0 ); + MakeCol( xLocalColOffset.get(), nColOffset, nVal, 0, 0 ); nColOffset = nColOffset + nVal; } } @@ -1595,13 +1605,14 @@ void ScHTMLLayoutParser::ProcToken( HtmlImportInfo* pInfo ) switch ( pInfo->nToken ) { case HtmlTokenId::META: + if (ScDocShell* pDocSh = mpDoc->GetDocumentShell()) { HTMLParser* pParser = static_cast<HTMLParser*>(pInfo->pParser); uno::Reference<document::XDocumentPropertiesSupplier> xDPS( - static_cast<cppu::OWeakObject*>(mpDoc->GetDocumentShell()->GetModel()), uno::UNO_QUERY_THROW); + static_cast<cppu::OWeakObject*>(pDocSh->GetModel()), uno::UNO_QUERY_THROW); pParser->ParseMetaOptions( xDPS->getDocumentProperties(), - mpDoc->GetDocumentShell()->GetHeaderAttributes() ); + pDocSh->GetHeaderAttributes() ); } break; case HtmlTokenId::TITLE_ON: @@ -1612,12 +1623,13 @@ void ScHTMLLayoutParser::ProcToken( HtmlImportInfo* pInfo ) break; case HtmlTokenId::TITLE_OFF: { - if ( bInTitle && !aString.isEmpty() ) + ScDocShell* pDocSh = mpDoc->GetDocumentShell(); + if ( bInTitle && !aString.isEmpty() && pDocSh ) { // Remove blanks from line breaks aString = aString.trim(); uno::Reference<document::XDocumentPropertiesSupplier> xDPS( - static_cast<cppu::OWeakObject*>(mpDoc->GetDocumentShell()->GetModel()), + static_cast<cppu::OWeakObject*>(pDocSh->GetModel()), uno::UNO_QUERY_THROW); xDPS->getDocumentProperties()->setTitle(aString); } diff --git a/sc/source/filter/inc/htmlpars.hxx b/sc/source/filter/inc/htmlpars.hxx index 1ac9aa0002..7043c91761 100644 --- a/sc/source/filter/inc/htmlpars.hxx +++ b/sc/source/filter/inc/htmlpars.hxx @@ -102,7 +102,7 @@ struct ScHTMLTableStackEntry { ScRangeListRef xLockedList; std::shared_ptr<ScEEParseEntry> xCellEntry; - ScHTMLColOffset* pLocalColOffset; + std::shared_ptr<ScHTMLColOffset> xLocalColOffset; sal_uLong nFirstTableCell; SCROW nRowCnt; SCCOL nColCntStart; @@ -113,14 +113,14 @@ struct ScHTMLTableStackEntry sal_uInt16 nColOffsetStart; bool bFirstRow; ScHTMLTableStackEntry( std::shared_ptr<ScEEParseEntry> xE, - ScRangeListRef xL, ScHTMLColOffset* pTO, + ScRangeListRef xL, std::shared_ptr<ScHTMLColOffset> xTO, sal_uLong nFTC, SCROW nRow, SCCOL nStart, SCCOL nMax, sal_uInt16 nTab, sal_uInt16 nTW, sal_uInt16 nCO, sal_uInt16 nCOS, bool bFR ) : xLockedList(std::move( xL )), xCellEntry(std::move(xE)), - pLocalColOffset( pTO ), + xLocalColOffset( std::move(xTO) ), nFirstTableCell( nFTC ), nRowCnt( nRow ), nColCntStart( nStart ), nMaxCol( nMax ), @@ -162,7 +162,7 @@ private: ScRangeListRef xLockedList; // per table std::unique_ptr<OuterMap> pTables; ScHTMLColOffset maColOffset; - ScHTMLColOffset* pLocalColOffset; // per table + std::shared_ptr<ScHTMLColOffset> xLocalColOffset; // per table sal_uLong nFirstTableCell; // per table short nTableLevel; sal_uInt16 nTable; diff --git a/sc/source/filter/xml/xmlexprt.cxx b/sc/source/filter/xml/xmlexprt.cxx index 9da5da65b7..74c9d093d5 100644 --- a/sc/source/filter/xml/xmlexprt.cxx +++ b/sc/source/filter/xml/xmlexprt.cxx @@ -3530,10 +3530,12 @@ void ScXMLExport::WriteShapes(const ScMyCell& rMyCell) // rectangle from the anchor as if all column/rows are shown. Then we move and resize // (in case of "resize with cell") the object to meet this snap rectangle. We need to // manipulate the object itself, because the used methods in xmloff do not evaluate the - // ObjData. This manipulation is only done temporarily for export. Thus we stash the geometry - // and restore it when export is done and we use NbcFoo methods. - bool bNeedsRestore = false; - std::unique_ptr<SdrObjGeoData> pGeoData = pObj->GetGeoData(); + // ObjData. We remember the transformations and restore them later. + Point aMoveBy(0, 0); + bool bNeedsRestorePosition = false; + Fraction aScaleWidth(1, 1); + Fraction aScaleHeight(1, 1); + bool bNeedsRestoreSize = false; // Determine top point of fictive snap rectangle ('Full' rectangle). SCTAB aTab(aSnapStartAddress.Tab()); @@ -3554,8 +3556,8 @@ void ScXMLExport::WriteShapes(const ScMyCell& rMyCell) Point aActualTopPoint = bNegativePage ? aOrigSnapRect.TopRight() : aOrigSnapRect.TopLeft(); if (aFullTopPoint != aActualTopPoint) { - bNeedsRestore = true; - Point aMoveBy = aFullTopPoint - aActualTopPoint; + bNeedsRestorePosition = true; + aMoveBy = aFullTopPoint - aActualTopPoint; pObj->NbcMove(Size(aMoveBy.X(), aMoveBy.Y())); } @@ -3567,6 +3569,7 @@ void ScXMLExport::WriteShapes(const ScMyCell& rMyCell) // Object is anchored "To cell (resize with cell)". Compare size of actual snap rectangle // and fictive full one. Resize object accordingly. tools::Rectangle aActualSnapRect(pObj->GetSnapRect()); + Point aSnapEndOffset(pObjData->maEndOffset); aCol = aSnapEndAddress.Col(); aRow = aSnapEndAddress.Row(); @@ -3583,12 +3586,13 @@ void ScXMLExport::WriteShapes(const ScMyCell& rMyCell) if (aFullSnapRect != aActualSnapRect) { - bNeedsRestore = true; - Fraction aScaleWidth(aFullSnapRect.getOpenWidth(), aActualSnapRect.getOpenWidth()); + bNeedsRestoreSize = true; + aScaleWidth + = Fraction(aFullSnapRect.getOpenWidth(), aActualSnapRect.getOpenWidth()); if (!aScaleWidth.IsValid()) aScaleWidth = Fraction(1, 1); - Fraction aScaleHeight(aFullSnapRect.getOpenHeight(), - aActualSnapRect.getOpenHeight()); + aScaleHeight + = Fraction(aFullSnapRect.getOpenHeight(), aActualSnapRect.getOpenHeight()); if (!aScaleHeight.IsValid()) aScaleHeight = Fraction(1, 1); pObj->NbcResize(aFullTopPoint, aScaleWidth, aScaleHeight); @@ -3636,9 +3640,20 @@ void ScXMLExport::WriteShapes(const ScMyCell& rMyCell) ExportShape(rShape.xShape, &aPoint); - // Restore object geometry - if (bNeedsRestore && pGeoData) - pObj->SetGeoData(*pGeoData); + if (bNeedsRestoreSize) + { + Fraction aScaleWidthInvers(aScaleWidth.GetDenominator(), aScaleWidth.GetNumerator()); + if (!aScaleWidthInvers.IsValid()) + aScaleWidthInvers = Fraction(1, 1); + Fraction aScaleHeightInvers(aScaleHeight.GetDenominator(), aScaleHeight.GetNumerator()); + if (!aScaleHeightInvers.IsValid()) + aScaleHeightInvers = Fraction(1, 1); + pObj->NbcResize(aFullTopPoint, aScaleWidthInvers, aScaleHeightInvers); + } + if (bNeedsRestorePosition) + { + pObj->NbcMove(Size(-aMoveBy.X(), -aMoveBy.Y())); + } } } diff --git a/sc/source/ui/docshell/impex.cxx b/sc/source/ui/docshell/impex.cxx index 8e6315db94..a61ee53d25 100644 --- a/sc/source/ui/docshell/impex.cxx +++ b/sc/source/ui/docshell/impex.cxx @@ -1662,7 +1662,7 @@ bool ScImportExport::ExtText2Doc( SvStream& rStrm ) ScDocumentImport aDocImport(rDoc); do { - SCCOL nLastCol = nEndCol; // tdf#129701 preserve value of nEndCol + const SCCOL nLastCol = nEndCol; // tdf#129701 preserve value of nEndCol for( ;; ) { aLine = ReadCsvLine(rStrm, !bFixed, aSeps, cStr, cDetectSep); @@ -1784,15 +1784,15 @@ 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; + bIsLastColEmpty = (nCol == nLastCol) && !(*p) && !bSkipEmptyCells && !bDetermineRange; } } diff --git a/sc/source/ui/inc/tabview.hxx b/sc/source/ui/inc/tabview.hxx index 66bbc010ea..e13488e302 100644 --- a/sc/source/ui/inc/tabview.hxx +++ b/sc/source/ui/inc/tabview.hxx @@ -241,7 +241,7 @@ private: void UpdateVarZoom(); static void SetScrollBar( ScrollAdaptor& rScroll, tools::Long nRangeMax, tools::Long nVisible, tools::Long nPos, bool bLayoutRTL ); - static tools::Long GetScrollBarPos( const ScrollAdaptor& rScroll ); + static tools::Long GetScrollBarPos( const ScrollAdaptor& rScroll, bool bLayoutRTL ); void GetAreaMoveEndPosition(SCCOL nMovX, SCROW nMovY, ScFollowMode eMode, SCCOL& rAreaX, SCROW& rAreaY, ScFollowMode& rMode, diff --git a/sc/source/ui/miscdlgs/solveroptions.cxx b/sc/source/ui/miscdlgs/solveroptions.cxx index 3d5b2b47c1..81f5c8b7b4 100644 --- a/sc/source/ui/miscdlgs/solveroptions.cxx +++ b/sc/source/ui/miscdlgs/solveroptions.cxx @@ -69,7 +69,7 @@ ScSolverOptionsDialog::ScSolverOptionsDialog(weld::Window* pParent, , m_xBtnEdit(m_xBuilder->weld_button("edit")) { m_xLbSettings->set_size_request(m_xLbSettings->get_approximate_digit_width() * 32, - m_xLbSettings->get_height_rows(6)); + m_xLbSettings->get_height_rows(12)); m_xLbSettings->enable_toggle_buttons(weld::ColumnToggleType::Check); diff --git a/sc/source/ui/unoobj/docuno.cxx b/sc/source/ui/unoobj/docuno.cxx index c906f39336..6d94542095 100644 --- a/sc/source/ui/unoobj/docuno.cxx +++ b/sc/source/ui/unoobj/docuno.cxx @@ -27,6 +27,9 @@ #include <editeng/editview.hxx> #include <editeng/memberids.h> #include <editeng/outliner.hxx> +#include <editeng/lrspitem.hxx> +#include <editeng/ulspitem.hxx> +#include <editeng/sizeitem.hxx> #include <o3tl/any.hxx> #include <o3tl/safeint.hxx> #include <svx/fmview.hxx> @@ -131,6 +134,7 @@ #include <table.hxx> #include <appoptio.hxx> #include <formulaopt.hxx> +#include <stlpool.hxx> #include <strings.hrc> @@ -2059,7 +2063,7 @@ uno::Sequence<beans::PropertyValue> SAL_CALL ScModelObj::getRenderer( sal_Int32 bWasCellRange = pPrintFunc->GetLastSourceRange( aCellRange ); Size aTwips = pPrintFunc->GetPageSize(); - if (!m_pPrintState) + if (!m_pPrintState || nRenderer == nTabStart) { m_pPrintState.reset(new ScPrintState()); pPrintFunc->GetPrintState(*m_pPrintState, true); @@ -2105,6 +2109,174 @@ uno::Sequence<beans::PropertyValue> SAL_CALL ScModelObj::getRenderer( sal_Int32 return aSequence; } +static void lcl_SetMediaScreen(const uno::Reference<drawing::XShape>& xMediaShape, + const OutputDevice* pDev, tools::Rectangle& aRect, + sal_Int32 nPageNumb) +{ + OUString sMediaURL; + uno::Reference<beans::XPropertySet> xPropSet(xMediaShape, uno::UNO_QUERY); + xPropSet->getPropertyValue("MediaURL") >>= sMediaURL; + if (sMediaURL.isEmpty()) + return; + vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(pDev->GetExtOutDevData()); + if (!pPDF) + return; + + OUString sTitle; + xPropSet->getPropertyValue("Title") >>= sTitle; + OUString sDescription; + xPropSet->getPropertyValue("Description") >>= sDescription; + OUString const altText(sTitle.isEmpty() ? sDescription + : sDescription.isEmpty() + ? sTitle + : OUString::Concat(sTitle) + OUString::Concat("\n") + + OUString::Concat(sDescription)); + + OUString const mimeType(xPropSet->getPropertyValue("MediaMimeType").get<OUString>()); + SdrObject* pSdrObj(SdrObject::getSdrObjectFromXShape(xMediaShape)); + sal_Int32 nScreenId = pPDF->CreateScreen(aRect, altText, mimeType, nPageNumb, pSdrObj); + if (sMediaURL.startsWith("vnd.sun.star.Package:")) + { + // Embedded media + OUString aTempFileURL; + xPropSet->getPropertyValue("PrivateTempFileURL") >>= aTempFileURL; + pPDF->SetScreenStream(nScreenId, aTempFileURL); + } + else // Linked media + pPDF->SetScreenURL(nScreenId, sMediaURL); +} + +static void lcl_PDFExportMediaShapeScreen(const OutputDevice* pDev, const ScPrintState& rState, + ScDocument& rDoc, SCTAB nTab, tools::Long nStartPage, + bool bSinglePageSheets) +{ + ScDrawLayer* pDrawLayer = rDoc.GetDrawLayer(); + vcl::PDFExtOutDevData* pPDF = dynamic_cast<vcl::PDFExtOutDevData*>(pDev->GetExtOutDevData()); + if (pPDF && pPDF->GetIsExportTaggedPDF() && pDrawLayer) + { + + if (!bSinglePageSheets) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + OSL_ENSURE(pPage, "Page ?"); + if (pPage) + { + ScStyleSheetPool* pStylePool = rDoc.GetStyleSheetPool(); + SfxStyleSheetBase* pStyleSheet = pStylePool->Find(rDoc.GetPageStyle(nTab), SfxStyleFamily::Page); + SfxItemSet* pItemSet = &pStyleSheet->GetItemSet(); + + tools::Long nLeftMargin(pItemSet->Get(ATTR_LRSPACE).GetLeft()); + nLeftMargin = o3tl::convert(nLeftMargin, o3tl::Length::twip, o3tl::Length::mm100); + + tools::Long nTopMargin(pItemSet->Get(ATTR_ULSPACE).GetUpper()); + nTopMargin = o3tl::convert(nTopMargin, o3tl::Length::twip, o3tl::Length::mm100); + + tools::Long nHeader = 0; + const SvxSetItem* pHeaderSetItem = &pItemSet->Get(ATTR_PAGE_HEADERSET); + bool bHasHdr = pHeaderSetItem->GetItemSet().Get(ATTR_PAGE_ON).GetValue(); + if (bHasHdr) + { + const SfxItemSet* pHeaderSet = &pHeaderSetItem->GetItemSet(); + tools::Long nHdrHeight = pHeaderSet->Get(ATTR_PAGE_SIZE).GetSize().Height(); + nHeader = o3tl::convert(nHdrHeight, o3tl::Length::twip, o3tl::Length::mm100); + } + + bool bTopDown = pItemSet->Get(ATTR_PAGE_TOPDOWN).GetValue(); + + SdrObjListIter aIter(pPage, SdrIterMode::DeepWithGroups); + SdrObject* pObj = aIter.Next(); + while (pObj && pObj->IsVisible()) + { + uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY); + if (xShape->getShapeType() == "com.sun.star.drawing.MediaShape") + { + SCCOL nX1, nX2; + SCROW nY1, nY2; + sal_Int32 nPageNumb = nStartPage; + if (bTopDown) // top-bottom page order + { + nX1 = 0; + for (size_t i = 0; i < rState.nPagesX; ++i) + { + nX2 = (*rState.xPageEndX)[i]; + for (size_t j = 0; j < rState.nPagesY; ++j) + { + auto& rPageRow = (*rState.xPageRows)[j]; + nY1 = rPageRow.GetStartRow(); + nY2 = rPageRow.GetEndRow(); + + tools::Rectangle aPageRect(rDoc.GetMMRect(nX1, nY1, nX2, nY2, nTab)); + tools::Rectangle aTmpRect(aPageRect.GetIntersection(pObj->GetCurrentBoundRect())); + if (!aTmpRect.IsEmpty()) + { + tools::Long nPosX(aTmpRect.getX() - aPageRect.getX() + nLeftMargin); + tools::Long nPosY(aTmpRect.getY() - aPageRect.getY() + nHeader + nTopMargin); + tools::Rectangle aRect(Point(nPosX, nPosY), aTmpRect.GetSize()); + lcl_SetMediaScreen(xShape, pDev, aRect, nPageNumb); + } + ++nPageNumb; + } + nX1 = nX2 + 1; + } + } + else // left to right page order + { + for (size_t i = 0; i < rState.nPagesY; ++i) + { + auto& rPageRow = (*rState.xPageRows)[i]; + nY1 = rPageRow.GetStartRow(); + nY2 = rPageRow.GetEndRow(); + nX1 = 0; + for (size_t j = 0; j < rState.nPagesX; ++j) + { + nX2 = (*rState.xPageEndX)[j]; + + tools::Rectangle aPageRect(rDoc.GetMMRect(nX1, nY1, nX2, nY2, nTab)); + tools::Rectangle aTmpRect(aPageRect.GetIntersection(pObj->GetCurrentBoundRect())); + if (!aTmpRect.IsEmpty()) + { + tools::Long nPosX(aTmpRect.getX() - aPageRect.getX() + nLeftMargin); + tools::Long nPosY(aTmpRect.getY() - aPageRect.getY() + nHeader + nTopMargin); + tools::Rectangle aRect(Point(nPosX, nPosY), aTmpRect.GetSize()); + lcl_SetMediaScreen(xShape, pDev, aRect, nPageNumb); + } + ++nPageNumb; + nX1 = nX2 + 1; + } + } + } + } + pObj = aIter.Next(); + } + } + } + else // export whole sheet + { + SCTAB nTabCount = rDoc.GetTableCount(); + for (SCTAB i = 0; i < nTabCount; ++i) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(i)); + OSL_ENSURE(pPage, "Page ?"); + if (pPage) + { + SdrObjListIter aIter(pPage, SdrIterMode::DeepWithGroups); + SdrObject* pObj = aIter.Next(); + while (pObj && pObj->IsVisible()) + { + uno::Reference<drawing::XShape> xShape(pObj->getUnoShape(), uno::UNO_QUERY); + if (xShape->getShapeType() == "com.sun.star.drawing.MediaShape") + { + tools::Rectangle aRect(pObj->GetCurrentBoundRect()); + lcl_SetMediaScreen(xShape, pDev, aRect, i); + } + pObj = aIter.Next(); + } + } + } + } + } +} + void SAL_CALL ScModelObj::render( sal_Int32 nSelRenderer, const uno::Any& aSelection, const uno::Sequence<beans::PropertyValue>& rOptions ) { @@ -2151,6 +2323,17 @@ void SAL_CALL ScModelObj::render( sal_Int32 nSelRenderer, const uno::Any& aSelec ScDocument& rDoc = pDocShell->GetDocument(); + SCTAB nTab; + if (!maValidPages.empty()) + nTab = pPrintFuncCache->GetTabForPage(maValidPages.at(nRenderer) - 1); + else + nTab = pPrintFuncCache->GetTabForPage(nRenderer); + + tools::Long nTabStart = pPrintFuncCache->GetTabStart(nTab); + + if (nRenderer == nTabStart) + lcl_PDFExportMediaShapeScreen(pDev, *m_pPrintState, rDoc, nTab, nTabStart, bSinglePageSheets); + ScRange aRange; const ScRange* pSelRange = nullptr; if ( bSinglePageSheets ) @@ -2225,12 +2408,6 @@ void SAL_CALL ScModelObj::render( sal_Int32 nSelRenderer, const uno::Any& aSelec } } aDrawViewKeeper; - SCTAB nTab; - if ( !maValidPages.empty() ) - nTab = pPrintFuncCache->GetTabForPage( maValidPages.at( nRenderer )-1 ); - else - nTab = pPrintFuncCache->GetTabForPage( nRenderer ); - ScDrawLayer* pModel = rDoc.GetDrawLayer(); if( pModel ) @@ -2285,7 +2462,6 @@ void SAL_CALL ScModelObj::render( sal_Int32 nSelRenderer, const uno::Any& aSelec aPage.Select( nRenderer+1 ); tools::Long nDisplayStart = pPrintFuncCache->GetDisplayStart( nTab ); - tools::Long nTabStart = pPrintFuncCache->GetTabStart( nTab ); vcl::PDFExtOutDevData* pPDFData = dynamic_cast< vcl::PDFExtOutDevData* >(pDev->GetExtOutDevData() ); if ( nRenderer == nTabStart ) diff --git a/sc/source/ui/view/output2.cxx b/sc/source/ui/view/output2.cxx index d419981d8e..877675c0ac 100644 --- a/sc/source/ui/view/output2.cxx +++ b/sc/source/ui/view/output2.cxx @@ -1877,8 +1877,7 @@ void ScOutputData::LayoutStrings(bool bPixelToLogic) // Cells to the left are marked directly, cells to the // right are handled by the flag for nX2 SCCOL nMarkX = ( nCellX <= nX2 ) ? nCellX : nX2; - RowInfo* pMarkRowInfo = ( nCellY == nY ) ? pThisRowInfo : &pRowInfo[0]; - pMarkRowInfo->basicCellInfo(nMarkX).bEditEngine = true; + pThisRowInfo->basicCellInfo(nMarkX).bEditEngine = true; bDoCell = false; // don't draw here } if ( bDoCell ) @@ -4429,14 +4428,17 @@ void ScOutputData::DrawEdit(bool bPixelToLogic) SCROW nCellY = nY; bool bDoCell = false; + // if merged cell contains hidden row or column or both + const ScMergeFlagAttr* pMergeFlag = mpDoc->GetAttr(nX, nY, nTab, ATTR_MERGE_FLAG); + bool bOverlapped = (pMergeFlag->IsHorOverlapped() || pMergeFlag->IsVerOverlapped()); + tools::Long nPosY = nRowPosY; - if ( nArrY == 0 ) + if (bOverlapped) { - nPosY = nScrY; - nY = pRowInfo[1].nRowNo; + nY = pRowInfo[nArrY].nRowNo; SCCOL nOverX; // start of the merged cells SCROW nOverY; - if (GetMergeOrigin( nX,nY, 1, nOverX,nOverY, true )) + if (GetMergeOrigin( nX,nY, nArrY, nOverX,nOverY, true )) { nCellX = nOverX; nCellY = nOverY; diff --git a/sc/source/ui/view/tabview.cxx b/sc/source/ui/view/tabview.cxx index 9eff50195e..a69ab1447f 100644 --- a/sc/source/ui/view/tabview.cxx +++ b/sc/source/ui/view/tabview.cxx @@ -1091,7 +1091,7 @@ void ScTabView::ScrollHdl(ScrollAdaptor* pScroll) nViewPos = aViewData.GetPosY( (pScroll == aVScrollTop.get()) ? SC_SPLIT_TOP : SC_SPLIT_BOTTOM ); - bool bLayoutRTL = aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() ); + bool bLayoutRTL = bHoriz && aViewData.GetDocument().IsLayoutRTL( aViewData.GetTabNo() ); ScrollType eType = pScroll->GetScrollType(); if ( eType == ScrollType::Drag ) @@ -1129,7 +1129,7 @@ void ScTabView::ScrollHdl(ScrollAdaptor* pScroll) nScrollMin = aViewData.GetFixPosX(); if ( aViewData.GetVSplitMode()==SC_SPLIT_FIX && pScroll == aVScrollBottom.get() ) nScrollMin = aViewData.GetFixPosY(); - tools::Long nScrollPos = GetScrollBarPos( *pScroll ) + nScrollMin; + tools::Long nScrollPos = GetScrollBarPos( *pScroll, bLayoutRTL ) + nScrollMin; OUString aHelpStr; tools::Rectangle aRect; @@ -1162,6 +1162,22 @@ void ScTabView::ScrollHdl(ScrollAdaptor* pScroll) else bDragging = false; + if ( bHoriz && bLayoutRTL ) + { + // change scroll type so visible/previous cells calculation below remains the same + switch ( eType ) + { + case ScrollType::LineUp: eType = ScrollType::LineDown; break; + case ScrollType::LineDown: eType = ScrollType::LineUp; break; + case ScrollType::PageUp: eType = ScrollType::PageDown; break; + case ScrollType::PageDown: eType = ScrollType::PageUp; break; + default: + { + // added to avoid warnings + } + } + } + tools::Long nDelta(0); switch ( eType ) { @@ -1194,7 +1210,7 @@ void ScTabView::ScrollHdl(ScrollAdaptor* pScroll) if ( aViewData.GetVSplitMode()==SC_SPLIT_FIX && pScroll == aVScrollBottom.get() ) nScrollMin = aViewData.GetFixPosY(); - tools::Long nScrollPos = GetScrollBarPos( *pScroll ) + nScrollMin; + tools::Long nScrollPos = GetScrollBarPos( *pScroll, bLayoutRTL ) + nScrollMin; nDelta = nScrollPos - nViewPos; // tdf#152406 Disable anti-jitter code for scroll wheel events diff --git a/sc/source/ui/view/tabview4.cxx b/sc/source/ui/view/tabview4.cxx index a7de6bdf67..5c19b6cf07 100644 --- a/sc/source/ui/view/tabview4.cxx +++ b/sc/source/ui/view/tabview4.cxx @@ -352,16 +352,45 @@ void ScTabView::SetScrollBar( ScrollAdaptor& rScroll, tools::Long nRangeMax, too if ( nVisible == 0 ) nVisible = 1; // #i59893# don't use visible size 0 - rScroll.SetRange( Range( 0, nRangeMax ) ); - rScroll.SetVisibleSize( nVisible ); - rScroll.SetThumbPos( nPos ); + // RTL layout uses a negative range to simulate a mirrored scroll bar. + // SetScrollBar/GetScrollBarPos hide this so outside of these functions normal cell + // addresses can be used. + if ( bLayoutRTL ) + { + rScroll.SetRange( Range( -nRangeMax, 0 ) ); + rScroll.SetVisibleSize( nVisible ); + rScroll.SetThumbPos( -nPos - nVisible ); + } + else + { + rScroll.SetRange( Range( 0, nRangeMax ) ); + rScroll.SetVisibleSize( nVisible ); + rScroll.SetThumbPos( nPos ); + } - rScroll.EnableRTL( bLayoutRTL ); + // Related: tdf#93352 always disable RTL for scrollbars + // Enabling RTL causes the following bugs when clicking or + // dragging the mouse in scrollbars in Calc's RTL UI: + // - Click or drag events get mirrored so you must click or + // drag in unexpected locations to move the scrollbar thumb + // in the desired direction + // - Repeatedly dragging the scrollbar thumb leftward can only + // move no highter than the R, S, or T columns + rScroll.EnableRTL( false ); + + // Related: tdf#93352 swap arrows if layout is RTL + // We cannot use EnableRTL(true) to signal that the arrows + // should be swapped (see comment above) so explicitly enable + // or disable arrow swapping. + rScroll.SetSwapArrows( bLayoutRTL ); } -tools::Long ScTabView::GetScrollBarPos( const ScrollAdaptor& rScroll ) +tools::Long ScTabView::GetScrollBarPos( const ScrollAdaptor& rScroll, bool bLayoutRTL ) { - return rScroll.GetThumbPos(); + if ( bLayoutRTL ) + return -rScroll.GetThumbPos() - rScroll.GetVisibleSize(); + else + return rScroll.GetThumbPos(); } // UpdateScrollBars - set visible area and scroll width of scroll bars @@ -425,7 +454,7 @@ void ScTabView::UpdateScrollBars( HeaderType eHeaderType ) nVisYB = aViewData.VisibleCellsY( SC_SPLIT_BOTTOM ); tools::Long nMaxYB = lcl_GetScrollRange( nUsedY, aViewData.GetPosY(SC_SPLIT_BOTTOM), nVisYB, rDoc.MaxRow(), nStartY ); - SetScrollBar( *aVScrollBottom, nMaxYB, nVisYB, aViewData.GetPosY( SC_SPLIT_BOTTOM ) - nStartY, bLayoutRTL ); + SetScrollBar( *aVScrollBottom, nMaxYB, nVisYB, aViewData.GetPosY( SC_SPLIT_BOTTOM ) - nStartY, false ); if (bRight) { @@ -438,7 +467,7 @@ void ScTabView::UpdateScrollBars( HeaderType eHeaderType ) { nVisYT = aViewData.VisibleCellsY( SC_SPLIT_TOP ); tools::Long nMaxYT = lcl_GetScrollRange( nUsedY, aViewData.GetPosY(SC_SPLIT_TOP), nVisYT, rDoc.MaxRow(), 0 ); - SetScrollBar( *aVScrollTop, nMaxYT, nVisYT, aViewData.GetPosY( SC_SPLIT_TOP ), bLayoutRTL ); + SetScrollBar( *aVScrollTop, nMaxYT, nVisYT, aViewData.GetPosY( SC_SPLIT_TOP ), false ); } // test the range diff --git a/sc/source/ui/view/viewfun3.cxx b/sc/source/ui/view/viewfun3.cxx index 7a6403237b..e52357f808 100644 --- a/sc/source/ui/view/viewfun3.cxx +++ b/sc/source/ui/view/viewfun3.cxx @@ -885,7 +885,8 @@ bool ScViewFunc::PasteFromClip( InsertDeleteFlags nFlags, ScDocument* pClipDoc, // undo: save all or no content InsertDeleteFlags nContFlags = InsertDeleteFlags::NONE; - if (nFlags & InsertDeleteFlags::CONTENTS) + // tdf#160765 - save content for undo when pasting notes, even if no content was changed + if (nFlags & (InsertDeleteFlags::CONTENTS | InsertDeleteFlags::ADDNOTES)) nContFlags |= InsertDeleteFlags::CONTENTS; if (nFlags & InsertDeleteFlags::ATTRIB) nContFlags |= InsertDeleteFlags::ATTRIB; |