compilerplugins/clang/test/writeonlyvars.cxx | 1 connectivity/source/drivers/dbase/DIndexIter.cxx | 3 include/o3tl/safeint.hxx | 36 +++++ lotuswordpro/source/filter/lwptablelayout.cxx | 2 offapi/com/sun/star/sheet/Spreadsheet.idl | 13 + oox/source/token/properties.txt | 1 sc/inc/document.hxx | 2 sc/inc/subtotalparam.hxx | 1 sc/inc/table.hxx | 4 sc/inc/unonames.hxx | 1 sc/inc/unowids.hxx | 3 sc/qa/uitest/data/tdf162262.ods |binary sc/qa/uitest/sort/subtotals.py | 109 ++++++++++++++++ sc/qa/unit/data/xlsx/subtotal-above.xlsx |binary sc/qa/unit/subsequent_export_test4.cxx | 12 + sc/source/core/data/document.cxx | 23 +++ sc/source/core/data/subtotalparam.cxx | 6 sc/source/core/data/table3.cxx | 92 +++++++++---- sc/source/filter/excel/excdoc.cxx | 3 sc/source/filter/excel/excrecds.cxx | 10 - sc/source/filter/inc/excrecds.hxx | 3 sc/source/filter/oox/worksheetsettings.cxx | 6 sc/source/ui/dbgui/tpsubt.cxx | 6 sc/source/ui/inc/tpsubt.hxx | 1 sc/source/ui/unoobj/cellsuno.cxx | 6 sc/source/ui/view/cellsh1.cxx | 6 sc/uiconfig/scalc/ui/subtotaloptionspage.ui | 152 +++++++++++++---------- sw/source/ui/index/cnttab.cxx | 2 28 files changed, 396 insertions(+), 108 deletions(-)
New commits: commit b5347e43505f132caa3f2dee0156b6c10df07b4c Author: Balazs Varga <balazs.varga.ext...@allotropia.de> AuthorDate: Thu Oct 24 14:17:46 2024 +0200 Commit: Thorsten Behrens <thorsten.behr...@allotropia.de> CommitDate: Mon Nov 4 23:57:03 2024 +0100 tdf#162262 sc add "Summary below data" option for Subtotal dialog With this option we can set where the summary rows should appear, above or below the datas when we create a new Subtotal area. The default option is "True" (which means the summary rows are below the datas) when we create a new subtotal area. Unless if we already have one subtotal area on the same sheet, in that case the sheet level property, "TotalsRowBelow" contains where should the summary rows be for the new Subtotal's. TODO: add new ODF xml attribute for Summary below Change-Id: Icf86c85041d75c24919cb528846d5bb2b517ca78 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/175556 Tested-by: Jenkins Reviewed-by: Balazs Varga <balazs.varga.ext...@allotropia.de> diff --git a/offapi/com/sun/star/sheet/Spreadsheet.idl b/offapi/com/sun/star/sheet/Spreadsheet.idl index d1a1dfcd0f18..605055c7b4ec 100644 --- a/offapi/com/sun/star/sheet/Spreadsheet.idl +++ b/offapi/com/sun/star/sheet/Spreadsheet.idl @@ -169,6 +169,19 @@ service Spreadsheet /** specifies all conditional formats of that sheet */ [optional, property] com::sun::star::sheet::XConditionalFormats ConditionalFormats; + + /** specifies whether summary rows appear below detail in an outline, + when applying an outline. + + <p> When true a summary row is inserted below the detailed data being + summarized and a new outline level is established on that row.</p> + + <p> When false a summary row is inserted above the detailed data being + summarized and a new outline level is established on that row.</p> + + @since LibreOffice 25.2 + */ + [optional, property] boolean TotalsRowBelow; }; diff --git a/oox/source/token/properties.txt b/oox/source/token/properties.txt index fd78234fbd0e..60dd62d7c6cc 100644 --- a/oox/source/token/properties.txt +++ b/oox/source/token/properties.txt @@ -608,6 +608,7 @@ TopBorderComplexColor TopBorderDistance TopMargin TotalsRow +TotalsRowBelow Transformation TransitionDirection TransitionDuration diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx index 15f1720142fb..f8102da7bb68 100644 --- a/sc/inc/document.hxx +++ b/sc/inc/document.hxx @@ -913,6 +913,8 @@ public: SC_DLLPUBLIC bool GetName( SCTAB nTab, OUString& rName ) const; SC_DLLPUBLIC bool GetCodeName( SCTAB nTab, OUString& rName ) const; SC_DLLPUBLIC bool SetCodeName( SCTAB nTab, const OUString& rName ); + SC_DLLPUBLIC bool GetTotalsRowBelow( SCTAB nTab ) const; + SC_DLLPUBLIC bool SetTotalsRowBelow( SCTAB nTab, bool bVal ); SC_DLLPUBLIC bool GetTable( const OUString& rName, SCTAB& rTab ) const; SC_DLLPUBLIC SCCOL MaxCol() const { return mxSheetLimits->mnMaxCol; } SC_DLLPUBLIC SCROW MaxRow() const { return mxSheetLimits->mnMaxRow; } diff --git a/sc/inc/subtotalparam.hxx b/sc/inc/subtotalparam.hxx index 8e36dad83987..3b379edb167d 100644 --- a/sc/inc/subtotalparam.hxx +++ b/sc/inc/subtotalparam.hxx @@ -24,6 +24,7 @@ struct SC_DLLPUBLIC ScSubTotalParam bool bPagebreak:1; ///< page break at change of group bool bCaseSens:1; bool bDoSort:1; ///< presort + bool bSummaryBelow:1; ///< Summary below or above (default: below) bool bAscending:1; ///< sort ascending bool bUserDef:1; ///< sort user defined bool bIncludePattern:1; ///< sort formats diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx index 7da55e4cb3ee..b70f77c95445 100644 --- a/sc/inc/table.hxx +++ b/sc/inc/table.hxx @@ -253,6 +253,7 @@ private: bool bActiveScenario:1; bool mbPageBreaksValid:1; bool mbForceBreaks:1; + bool mbTotalsRowBelow:1; /** this is touched from formula group threading context */ std::atomic<bool> bStreamValid; @@ -394,6 +395,9 @@ public: const OUString& GetCodeName() const { return aCodeName; } void SetCodeName( const OUString& rNewName ) { aCodeName = rNewName; } + bool GetTotalsRowBelow() const { return mbTotalsRowBelow; } + void SetTotalsRowBelow( bool bNewVal ) { mbTotalsRowBelow = bNewVal; } + const OUString& GetUpperName() const; const OUString& GetPageStyle() const { return aPageStyle; } diff --git a/sc/inc/unonames.hxx b/sc/inc/unonames.hxx index 13bb2600f471..1ae418d69102 100644 --- a/sc/inc/unonames.hxx +++ b/sc/inc/unonames.hxx @@ -191,6 +191,7 @@ inline constexpr OUString SC_UNONAME_TABLAYOUT = u"TableLayout"_ustr; inline constexpr OUString SC_UNONAME_AUTOPRINT = u"AutomaticPrintArea"_ustr; inline constexpr OUString SC_UNONAME_TABCOLOR = u"TabColor"_ustr; inline constexpr OUString SC_UNONAME_CONDFORMAT = u"ConditionalFormats"_ustr; +inline constexpr OUString SC_UNONAME_TOTALBELOW = u"TotalsRowBelow"_ustr; inline constexpr OUString SC_UNONAME_VISFLAG = u"VisibleFlag"_ustr; diff --git a/sc/inc/unowids.hxx b/sc/inc/unowids.hxx index cd1d6baf6ab8..0f6d33f655f1 100644 --- a/sc/inc/unowids.hxx +++ b/sc/inc/unowids.hxx @@ -74,7 +74,8 @@ #define SC_WID_UNO_FORMATID ( SC_WID_UNO_START + 45 ) #define SC_WID_UNO_FORMRT2 ( SC_WID_UNO_START + 46 ) #define SC_WID_UNO_CELLCONTENTTYPE ( SC_WID_UNO_START + 47 ) -#define SC_WID_UNO_END ( SC_WID_UNO_START + 47 ) +#define SC_WID_UNO_TOTALBELOW ( SC_WID_UNO_START + 48 ) +#define SC_WID_UNO_END ( SC_WID_UNO_START + 48 ) inline bool IsScUnoWid( sal_uInt16 nWid ) { diff --git a/sc/qa/uitest/data/tdf162262.ods b/sc/qa/uitest/data/tdf162262.ods new file mode 100644 index 000000000000..138348366298 Binary files /dev/null and b/sc/qa/uitest/data/tdf162262.ods differ diff --git a/sc/qa/uitest/sort/subtotals.py b/sc/qa/uitest/sort/subtotals.py index b824dcab98c8..6eddd1bc03b4 100644 --- a/sc/qa/uitest/sort/subtotals.py +++ b/sc/qa/uitest/sort/subtotals.py @@ -137,4 +137,113 @@ class Subtotals(UITestCase): self.assertEqual(get_cell_by_position(calc_doc, 0, 0, 1).getValue(), 1) self.assertEqual(get_cell_by_position(calc_doc, 0, 1, 1).getValue(), 2) + def test_tdf162262(self): + with self.ui_test.load_file(get_url_for_data_file("tdf162262.ods")) as calc_doc: + XcalcDoc = self.xUITest.getTopFocusWindow() + gridwin = XcalcDoc.getChild("grid_window") + # One group level + # Select cell range + gridwin.executeAction("SELECT", mkPropertyValues({"RANGE": "A1:C15"})) + # Select from the menu bar Data + # Select option subtotal + # Subtotal dialog displays + with self.ui_test.execute_dialog_through_command(".uno:DataSubTotals") as xDialog: + # Select group by: Day + xGroupBy = xDialog.getChild("group_by1") + select_by_text(xGroupBy, "Day") + # Select 'Calculate subtotals for' -> Value 1 and Value 2 + xCheckListMenu = xDialog.getChild("grid1") + xTreeList = xCheckListMenu.getChild("columns1") + xFirstEntry = xTreeList.getChild("1") + xFirstEntry.executeAction("CLICK", tuple()) + xFirstEntry = xTreeList.getChild("2") + xFirstEntry.executeAction("CLICK", tuple()) + + # Select tab options + xTabs = xDialog.getChild("tabcontrol") + select_pos(xTabs, "3") + # Unselect option Summary below -> false + xSummarybelow = xDialog.getChild("summarybelow") + xSummarybelow.executeAction("CLICK", tuple()) + # apply with OK + + self.assertEqual(get_cell_by_position(calc_doc, 0, 0, 1).getString(), "Grand Sum") + self.assertEqual(get_cell_by_position(calc_doc, 0, 1, 1).getValue(), 105) + self.assertEqual(get_cell_by_position(calc_doc, 0, 2, 1).getValue(), 119) + + self.assertEqual(get_cell_by_position(calc_doc, 0, 0, 2).getString(), "Friday Result") + self.assertEqual(get_cell_by_position(calc_doc, 0, 1, 2).getValue(), 19) + self.assertEqual(get_cell_by_position(calc_doc, 0, 2, 2).getValue(), 21) + + self.assertEqual(get_cell_by_position(calc_doc, 0, 0, 20).getString(), "Wednesday Result") + self.assertEqual(get_cell_by_position(calc_doc, 0, 1, 20).getValue(), 11) + self.assertEqual(get_cell_by_position(calc_doc, 0, 2, 20).getValue(), 13) + + def test_tdf162262_multi(self): + with self.ui_test.load_file(get_url_for_data_file("tdf162262.ods")) as calc_doc: + XcalcDoc = self.xUITest.getTopFocusWindow() + gridwin = XcalcDoc.getChild("grid_window") + # Multi group level + # Select cell range + gridwin.executeAction("SELECT", mkPropertyValues({"RANGE": "A1:C15"})) + # Select from the menu bar Data + # Select option subtotal + # Subtotal dialog displays + with self.ui_test.execute_dialog_through_command(".uno:DataSubTotals") as xDialog: + # Select group by 1: Day + xGroupBy = xDialog.getChild("group_by1") + select_by_text(xGroupBy, "Day") + # Select 'Calculate subtotals for' -> Value 1 + xCheckListMenu = xDialog.getChild("grid1") + xTreeList = xCheckListMenu.getChild("columns1") + # Select 1 column + xFirstEntry = xTreeList.getChild("1") + xFirstEntry.executeAction("CLICK", tuple()) + + # Select tab Group by 2 + xTabs = xDialog.getChild("tabcontrol") + select_pos(xTabs, "1") + + # Select group by 2: Day + xGroupBy = xDialog.getChild("group_by2") + select_by_text(xGroupBy, "Day") + # Select 'Calculate subtotals for' -> Value 2 + xCheckListMenu = xDialog.getChild("grid2") + xTreeList = xCheckListMenu.getChild("columns2") + # Select second column + xFirstEntry = xTreeList.getChild("2") + xFirstEntry.executeAction("CLICK", tuple()) + + # Select tab options + xTabs = xDialog.getChild("tabcontrol") + select_pos(xTabs, "3") + # Unselect option Summary below -> false + xSummarybelow = xDialog.getChild("summarybelow") + xSummarybelow.executeAction("CLICK", tuple()) + # apply with OK + + self.assertEqual(get_cell_by_position(calc_doc, 0, 0, 1).getString(), "Grand Sum") + self.assertEqual(get_cell_by_position(calc_doc, 0, 1, 1).getValue(), 0) + self.assertEqual(get_cell_by_position(calc_doc, 0, 2, 1).getValue(), 119) + + self.assertEqual(get_cell_by_position(calc_doc, 0, 0, 2).getString(), "Grand Sum") + self.assertEqual(get_cell_by_position(calc_doc, 0, 1, 2).getValue(), 105) + self.assertEqual(get_cell_by_position(calc_doc, 0, 2, 2).getValue(), 0) + + self.assertEqual(get_cell_by_position(calc_doc, 0, 0, 3).getString(), "Friday Sum") + self.assertEqual(get_cell_by_position(calc_doc, 0, 1, 3).getValue(), 0) + self.assertEqual(get_cell_by_position(calc_doc, 0, 2, 3).getValue(), 21) + + self.assertEqual(get_cell_by_position(calc_doc, 0, 0, 4).getString(), "Friday Sum") + self.assertEqual(get_cell_by_position(calc_doc, 0, 1, 4).getValue(), 19) + self.assertEqual(get_cell_by_position(calc_doc, 0, 2, 4).getValue(), 0) + + self.assertEqual(get_cell_by_position(calc_doc, 0, 0, 27).getString(), "Wednesday Sum") + self.assertEqual(get_cell_by_position(calc_doc, 0, 1, 27).getValue(), 0) + self.assertEqual(get_cell_by_position(calc_doc, 0, 2, 27).getValue(), 13) + + self.assertEqual(get_cell_by_position(calc_doc, 0, 0, 28).getString(), "Wednesday Sum") + self.assertEqual(get_cell_by_position(calc_doc, 0, 1, 28).getValue(), 11) + self.assertEqual(get_cell_by_position(calc_doc, 0, 2, 28).getValue(), 0) + # vim: set shiftwidth=4 softtabstop=4 expandtab: diff --git a/sc/qa/unit/data/xlsx/subtotal-above.xlsx b/sc/qa/unit/data/xlsx/subtotal-above.xlsx new file mode 100644 index 000000000000..b271dbab19be Binary files /dev/null and b/sc/qa/unit/data/xlsx/subtotal-above.xlsx differ diff --git a/sc/qa/unit/subsequent_export_test4.cxx b/sc/qa/unit/subsequent_export_test4.cxx index 8983756b6d41..1bbe843f4d39 100644 --- a/sc/qa/unit/subsequent_export_test4.cxx +++ b/sc/qa/unit/subsequent_export_test4.cxx @@ -380,6 +380,18 @@ CPPUNIT_TEST_FIXTURE(ScExportTest4, testTdf81470) assertXPath(pHeaders, "/x:headers/x:header[3]"_ostr, "userName"_ostr, u"Kohei Yoshida"_ustr); } +CPPUNIT_TEST_FIXTURE(ScExportTest4, testTdf162262) +{ + createScDoc("xlsx/subtotal-above.xlsx"); + + save(u"Calc Office Open XML"_ustr); + + xmlDocUniquePtr pSheet = parseExport(u"xl/worksheets/sheet1.xml"_ustr); + CPPUNIT_ASSERT(pSheet); + + assertXPath(pSheet, "/x:worksheet/x:sheetPr/x:outlinePr", "summaryBelow", u"0"_ustr); +} + CPPUNIT_TEST_FIXTURE(ScExportTest4, testTdf122331) { createScDoc("ods/tdf122331.ods"); diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx index 654aeb464736..7374e57c7dad 100644 --- a/sc/source/core/data/document.cxx +++ b/sc/source/core/data/document.cxx @@ -228,6 +228,25 @@ bool ScDocument::GetCodeName( SCTAB nTab, OUString& rName ) const return false; } +bool ScDocument::SetTotalsRowBelow( SCTAB nTab, bool bVal ) +{ + if (ScTable* pTable = FetchTable(nTab)) + { + pTable->SetTotalsRowBelow(bVal); + return true; + } + return false; +} + +bool ScDocument::GetTotalsRowBelow( SCTAB nTab ) const +{ + if (const ScTable* pTable = FetchTable(nTab)) + { + return pTable->GetTotalsRowBelow(); + } + return true; +} + bool ScDocument::GetTable( const OUString& rName, SCTAB& rTab ) const { static OUString aCacheName, aCacheUpperName; diff --git a/sc/source/core/data/subtotalparam.cxx b/sc/source/core/data/subtotalparam.cxx index e8f32954297c..6fd8e18c6b0e 100644 --- a/sc/source/core/data/subtotalparam.cxx +++ b/sc/source/core/data/subtotalparam.cxx @@ -26,7 +26,7 @@ ScSubTotalParam::ScSubTotalParam() ScSubTotalParam::ScSubTotalParam( const ScSubTotalParam& r ) : nCol1(r.nCol1),nRow1(r.nRow1),nCol2(r.nCol2),nRow2(r.nRow2),nUserIndex(r.nUserIndex), bRemoveOnly(r.bRemoveOnly),bReplace(r.bReplace),bPagebreak(r.bPagebreak),bCaseSens(r.bCaseSens), - bDoSort(r.bDoSort),bAscending(r.bAscending),bUserDef(r.bUserDef), + bDoSort(r.bDoSort), bSummaryBelow(r.bSummaryBelow), bAscending(r.bAscending), bUserDef(r.bUserDef), bIncludePattern(r.bIncludePattern) { for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++) @@ -59,7 +59,7 @@ void ScSubTotalParam::Clear() nRow1=nRow2 = 0; nUserIndex = 0; bPagebreak=bCaseSens=bUserDef=bIncludePattern=bRemoveOnly = false; - bAscending=bReplace=bDoSort = true; + bAscending=bReplace=bDoSort=bSummaryBelow = true; for (sal_uInt16 i=0; i<MAXSUBTOTAL; i++) { @@ -90,6 +90,7 @@ ScSubTotalParam& ScSubTotalParam::operator=( const ScSubTotalParam& r ) bPagebreak = r.bPagebreak; bCaseSens = r.bCaseSens; bDoSort = r.bDoSort; + bSummaryBelow = r.bSummaryBelow; bAscending = r.bAscending; bUserDef = r.bUserDef; nUserIndex = r.nUserIndex; @@ -135,6 +136,7 @@ bool ScSubTotalParam::operator==( const ScSubTotalParam& rOther ) const && (bReplace == rOther.bReplace) && (bPagebreak == rOther.bPagebreak) && (bDoSort == rOther.bDoSort) + && (bSummaryBelow == rOther.bSummaryBelow) && (bCaseSens == rOther.bCaseSens) && (bAscending == rOther.bAscending) && (bUserDef == rOther.bUserDef) diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx index d022ba2c4473..fed72506c2a3 100644 --- a/sc/source/core/data/table3.cxx +++ b/sc/source/core/data/table3.cxx @@ -2060,9 +2060,10 @@ bool ScTable::DoSubTotals( ScSubTotalParam& rParam ) if (nResCount > 0) // otherwise only sort { + SCROW nAboveRows = rParam.bSummaryBelow ? nStartRow : nStartRow + nLevel; for (sal_uInt16 i = 0; i <= aRowEntry.nGroupNo; ++i) { - aSubString = GetString( nGroupCol[i], nStartRow ); + aSubString = GetString( nGroupCol[i], nAboveRows ); if ( bIgnoreCase ) aCompString[i] = ScGlobal::getCharClass().uppercase( aSubString ); else @@ -2070,8 +2071,8 @@ bool ScTable::DoSubTotals( ScSubTotalParam& rParam ) } // aSubString stays on the last bool bBlockVis = false; // group visible? - aRowEntry.nSubStartRow = nStartRow; - for (SCROW nRow=nStartRow; nRow<=nEndRow+1 && bSpaceLeft; nRow++) + aRowEntry.nSubStartRow = nAboveRows; + for (SCROW nRow=nAboveRows; nRow<=nEndRow+1 && bSpaceLeft; nRow++) { bool bChanged; if (nRow>nEndRow) @@ -2099,9 +2100,21 @@ bool ScTable::DoSubTotals( ScSubTotalParam& rParam ) } if ( bChanged ) { - aRowEntry.nDestRow = nRow; - aRowEntry.nFuncStart = aRowEntry.nSubStartRow; - aRowEntry.nFuncEnd = nRow-1; + if (rParam.bSummaryBelow) + { + aRowEntry.nDestRow = nRow; + aRowEntry.nFuncStart = aRowEntry.nSubStartRow; + aRowEntry.nFuncEnd = nRow - 1; + } + else + { + aRowEntry.nDestRow = aRowEntry.nSubStartRow; + aRowEntry.nFuncStart = aRowEntry.nSubStartRow + 1; + if (nRow != nEndRow + 1) + aRowEntry.nFuncEnd = nRow - nLevel; + else + aRowEntry.nFuncEnd = nRow; + } bSpaceLeft = rDocument.InsertRow( 0, nTab, rDocument.MaxCol(), nTab, aRowEntry.nDestRow, 1 ); @@ -2157,17 +2170,27 @@ bool ScTable::DoSubTotals( ScSubTotalParam& rParam ) if (!aRowVector.empty()) { - // generate global total - SCROW nGlobalStartRow = aRowVector[0].nSubStartRow; - SCROW nGlobalStartFunc = aRowVector[0].nFuncStart; SCROW nGlobalEndRow = 0; SCROW nGlobalEndFunc = 0; - for (const auto& rRowEntry : aRowVector) + for (auto& rRowEntry : aRowVector) { + if (!rParam.bSummaryBelow) + { + // if we have Global summary above, we need to shift summary rows down + rRowEntry.nDestRow = rRowEntry.nDestRow + nLevelCount; + rRowEntry.nFuncEnd = rRowEntry.nFuncEnd + nLevelCount; + rRowEntry.nFuncStart = rRowEntry.nFuncStart + nLevelCount - rRowEntry.nGroupNo; + rRowEntry.nSubStartRow = rRowEntry.nSubStartRow + nLevelCount; + } + nGlobalEndRow = (nGlobalEndRow < rRowEntry.nDestRow) ? rRowEntry.nDestRow : nGlobalEndRow; nGlobalEndFunc = (nGlobalEndFunc < rRowEntry.nFuncEnd) ? rRowEntry.nFuncEnd : nGlobalEndRow; } + // generate global total + SCROW nGlobalStartRow = aRowVector[0].nSubStartRow; + SCROW nGlobalStartFunc = aRowVector[0].nFuncStart; + for (sal_uInt16 nLevel = 0; nLevel<nLevelCount; nLevel++) { const sal_uInt16 nGroupNo = nLevelCount - nLevel - 1; @@ -2179,18 +2202,30 @@ bool ScTable::DoSubTotals( ScSubTotalParam& rParam ) continue; } - // increment end row - nGlobalEndRow++; - - // add row entry for formula - aRowEntry.nGroupNo = nGroupNo; - aRowEntry.nSubStartRow = nGlobalStartRow; - aRowEntry.nFuncStart = nGlobalStartFunc; - aRowEntry.nDestRow = nGlobalEndRow; - aRowEntry.nFuncEnd = nGlobalEndFunc; - - // increment row - nGlobalEndFunc++; + if (rParam.bSummaryBelow) + { + // increment end row + nGlobalEndRow++; + + // add row entry for formula + aRowEntry.nGroupNo = nGroupNo; + aRowEntry.nSubStartRow = nGlobalStartRow; + aRowEntry.nFuncStart = nGlobalStartFunc; + aRowEntry.nDestRow = nGlobalEndRow; + aRowEntry.nFuncEnd = nGlobalEndFunc; + + // increment row + nGlobalEndFunc++; + } + else + { + // if we have Global summary we need to shift summary rows down + aRowEntry.nGroupNo = nGroupNo; + aRowEntry.nSubStartRow = nGlobalStartRow - nGroupNo - 1; + aRowEntry.nFuncStart = nGlobalStartFunc - nGroupNo - 1; + aRowEntry.nDestRow = nGlobalStartRow - nGroupNo - 1; + aRowEntry.nFuncEnd = nGlobalEndFunc; + } bSpaceLeft = rDocument.InsertRow(0, nTab, rDocument.MaxCol(), nTab, aRowEntry.nDestRow, 1); diff --git a/sc/source/filter/excel/excdoc.cxx b/sc/source/filter/excel/excdoc.cxx index 9b2a29ff62ca..32ff0f7a753e 100644 --- a/sc/source/filter/excel/excdoc.cxx +++ b/sc/source/filter/excel/excdoc.cxx @@ -584,8 +584,9 @@ void ExcTable::FillAsTableXml() XclExtLstRef xExtLst = new XclExtLst( GetRoot() ); bool bFitToPages = xPageSett->GetPageData().mbFitToPages; + bool bSummaryBelow = GetRoot().GetDoc().GetTotalsRowBelow(mnScTab); Color aTabColor = GetRoot().GetDoc().GetTabBgColor(mnScTab); - Add(new XclExpXmlSheetPr(bFitToPages, mnScTab, aTabColor, &GetFilterManager())); + Add(new XclExpXmlSheetPr(bFitToPages, mnScTab, aTabColor, bSummaryBelow, &GetFilterManager())); // GUTS (count & size of outline icons) aRecList.AppendRecord( mxCellTable->CreateRecord( EXC_ID_GUTS ) ); diff --git a/sc/source/filter/excel/excrecds.cxx b/sc/source/filter/excel/excrecds.cxx index 381836c10c12..afd38447d865 100644 --- a/sc/source/filter/excel/excrecds.cxx +++ b/sc/source/filter/excel/excrecds.cxx @@ -368,8 +368,8 @@ XclExpWsbool::XclExpWsbool( bool bFitToPages ) SetValue( GetValue() | EXC_WSBOOL_FITTOPAGE ); } -XclExpXmlSheetPr::XclExpXmlSheetPr( bool bFitToPages, SCTAB nScTab, const Color& rTabColor, XclExpFilterManager* pManager ) : - mnScTab(nScTab), mpManager(pManager), mbFitToPage(bFitToPages), maTabColor(rTabColor) {} +XclExpXmlSheetPr::XclExpXmlSheetPr( bool bFitToPages, SCTAB nScTab, const Color& rTabColor, bool bSummaryBelow, XclExpFilterManager* pManager ) : + mnScTab(nScTab), mpManager(pManager), mbFitToPage(bFitToPages), maTabColor(rTabColor), mbSummaryBelow(bSummaryBelow) {} void XclExpXmlSheetPr::SaveXml( XclExpXmlStream& rStrm ) { @@ -388,11 +388,13 @@ void XclExpXmlSheetPr::SaveXml( XclExpXmlStream& rStrm ) // Note : the order of child elements is significant. Don't change the order. - // OOXTODO: XML_outlinePr - if (maTabColor != COL_AUTO) rWorksheet->singleElement(XML_tabColor, XML_rgb, XclXmlUtils::ToOString(maTabColor)); + // OOXTODO: XML_outlinePr --> XML_applyStyles, XML_showOutlineSymbols, XML_summaryBelow, XML_summaryRight + if (!mbSummaryBelow) + rWorksheet->singleElement(XML_outlinePr, XML_summaryBelow, "0"); + rWorksheet->singleElement(XML_pageSetUpPr, // OOXTODO: XML_autoPageBreaks, XML_fitToPage, ToPsz(mbFitToPage)); diff --git a/sc/source/filter/inc/excrecds.hxx b/sc/source/filter/inc/excrecds.hxx index c7ab0aa96bd2..629ddfd18b5a 100644 --- a/sc/source/filter/inc/excrecds.hxx +++ b/sc/source/filter/inc/excrecds.hxx @@ -304,7 +304,7 @@ class XclExpXmlSheetPr : public XclExpRecordBase { public: explicit XclExpXmlSheetPr( - bool bFitToPages, SCTAB nScTab, const Color& rTabColor, XclExpFilterManager* pManager ); + bool bFitToPages, SCTAB nScTab, const Color& rTabColor, bool bSummaryBelow, XclExpFilterManager* pManager ); virtual void SaveXml( XclExpXmlStream& rStrm ) override; @@ -313,6 +313,7 @@ private: XclExpFilterManager* mpManager; bool mbFitToPage; Color maTabColor; + bool mbSummaryBelow; }; class XclExpFiltermode : public XclExpEmptyRecord diff --git a/sc/source/filter/oox/worksheetsettings.cxx b/sc/source/filter/oox/worksheetsettings.cxx index 988207aa9067..46d633044115 100644 --- a/sc/source/filter/oox/worksheetsettings.cxx +++ b/sc/source/filter/oox/worksheetsettings.cxx @@ -287,6 +287,12 @@ void WorksheetSettings::finalizeImport() ::Color nColor = maSheetSettings.maTabColor.getColor( getBaseFilter().getGraphicHelper() ); aPropSet.setProperty( PROP_TabColor, nColor ); } + + // Summary data below or above the contents + if ( !maSheetSettings.mbSummaryBelow ) + { + aPropSet.setProperty( PROP_TotalsRowBelow, false ); + } } } // namespace oox::xls diff --git a/sc/source/ui/dbgui/tpsubt.cxx b/sc/source/ui/dbgui/tpsubt.cxx index c657f482da34..e0fcf640f756 100644 --- a/sc/source/ui/dbgui/tpsubt.cxx +++ b/sc/source/ui/dbgui/tpsubt.cxx @@ -447,6 +447,7 @@ ScTpSubTotalOptions::ScTpSubTotalOptions(weld::Container* pPage, weld::DialogCon , m_xBtnPagebreak(m_xBuilder->weld_check_button(u"pagebreak"_ustr)) , m_xBtnCase(m_xBuilder->weld_check_button(u"case"_ustr)) , m_xBtnSort(m_xBuilder->weld_check_button(u"sort"_ustr)) + , m_xBtnSummary(m_xBuilder->weld_check_button(u"summarybelow"_ustr)) , m_xFlSort(m_xBuilder->weld_label(u"label2"_ustr)) , m_xBtnAscending(m_xBuilder->weld_radio_button(u"ascending"_ustr)) , m_xBtnDescending(m_xBuilder->weld_radio_button(u"descending"_ustr)) @@ -490,6 +491,7 @@ void ScTpSubTotalOptions::Reset( const SfxItemSet* /* rArgSet */ ) m_xBtnCase->set_active( rSubTotalData.bCaseSens ); m_xBtnFormats->set_active( rSubTotalData.bIncludePattern ); m_xBtnSort->set_active( rSubTotalData.bDoSort ); + m_xBtnSummary->set_active( rSubTotalData.bSummaryBelow ); m_xBtnAscending->set_active( rSubTotalData.bAscending ); m_xBtnDescending->set_active( !rSubTotalData.bAscending ); @@ -524,6 +526,10 @@ bool ScTpSubTotalOptions::FillItemSet( SfxItemSet* rArgSet ) theSubTotalData.bCaseSens = m_xBtnCase->get_active(); theSubTotalData.bIncludePattern = m_xBtnFormats->get_active(); theSubTotalData.bDoSort = m_xBtnSort->get_active(); + + theSubTotalData.bSummaryBelow = m_xBtnSummary->get_active(); + pDoc->SetTotalsRowBelow(pViewData->GetTabNo(), theSubTotalData.bSummaryBelow); + theSubTotalData.bAscending = m_xBtnAscending->get_active(); theSubTotalData.bUserDef = m_xBtnUserDef->get_active(); theSubTotalData.nUserIndex = (m_xBtnUserDef->get_active()) diff --git a/sc/source/ui/inc/tpsubt.hxx b/sc/source/ui/inc/tpsubt.hxx index ecfa2ec18522..42eed5e4046e 100644 --- a/sc/source/ui/inc/tpsubt.hxx +++ b/sc/source/ui/inc/tpsubt.hxx @@ -135,6 +135,7 @@ private: std::unique_ptr<weld::CheckButton> m_xBtnPagebreak; std::unique_ptr<weld::CheckButton> m_xBtnCase; std::unique_ptr<weld::CheckButton> m_xBtnSort; + std::unique_ptr<weld::CheckButton> m_xBtnSummary; std::unique_ptr<weld::Label> m_xFlSort; std::unique_ptr<weld::RadioButton> m_xBtnAscending; std::unique_ptr<weld::RadioButton> m_xBtnDescending; diff --git a/sc/source/ui/unoobj/cellsuno.cxx b/sc/source/ui/unoobj/cellsuno.cxx index 4defe47400b2..8dfe207ac010 100644 --- a/sc/source/ui/unoobj/cellsuno.cxx +++ b/sc/source/ui/unoobj/cellsuno.cxx @@ -784,6 +784,7 @@ static const SfxItemPropertySet* lcl_GetSheetPropertySet() { SC_UNONAME_TABCOLOR, SC_WID_UNO_TABCOLOR, cppu::UnoType<sal_Int32>::get(), 0, 0 }, { SC_UNO_CODENAME, SC_WID_UNO_CODENAME, cppu::UnoType<OUString>::get(), 0, 0}, { SC_UNO_NAMEDRANGES, SC_WID_UNO_NAMES, cppu::UnoType<sheet::XNamedRanges>::get(), 0, 0 }, + { SC_UNONAME_TOTALBELOW, SC_WID_UNO_TOTALBELOW, cppu::UnoType<bool>::get(), 0, 0 }, }; static SfxItemPropertySet aSheetPropertySet( aSheetPropertyMap_Impl ); return &aSheetPropertySet; @@ -8058,6 +8059,11 @@ void ScTableSheetObj::SetOnePropertyValue( const SfxItemPropertyMapEntry* pEntry // how to set the format correctly } } + else if (pEntry->nWID == SC_WID_UNO_TOTALBELOW) + { + bool bTotalsRowBelow = ScUnoHelpFunctions::GetBoolFromAny(aValue); + rDoc.SetTotalsRowBelow(nTab, bTotalsRowBelow); + } else ScCellRangeObj::SetOnePropertyValue(pEntry, aValue); // base class, no Item WID } diff --git a/sc/source/ui/view/cellsh1.cxx b/sc/source/ui/view/cellsh1.cxx index 50c5635a3b18..689e4c79f2cf 100644 --- a/sc/source/ui/view/cellsh1.cxx +++ b/sc/source/ui/view/cellsh1.cxx @@ -3567,6 +3567,12 @@ void ScCellShell::ExecuteSubtotals(SfxRequest& rReq) } pDBData->GetSubTotalParam( aSubTotalParam ); + + ScDocument& rDoc = GetViewData().GetDocument(); + SCTAB nTab = GetViewData().GetTabNo(); + if (!rDoc.GetTotalsRowBelow(nTab)) + aSubTotalParam.bSummaryBelow = false; + aSubTotalParam.bRemoveOnly = false; if (bAnonymous) { diff --git a/sc/uiconfig/scalc/ui/subtotaloptionspage.ui b/sc/uiconfig/scalc/ui/subtotaloptionspage.ui index d81fb987be61..0d1c54f510d0 100644 --- a/sc/uiconfig/scalc/ui/subtotaloptionspage.ui +++ b/sc/uiconfig/scalc/ui/subtotaloptionspage.ui @@ -1,39 +1,39 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.36.0 --> +<!-- Generated with glade 3.38.2 --> <interface domain="sc"> <requires lib="gtk+" version="3.20"/> <object class="GtkBox" id="SubTotalOptionsPage"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="border_width">6</property> + <property name="can-focus">False</property> + <property name="border-width">6</property> <property name="orientation">vertical</property> <property name="spacing">12</property> <child> <object class="GtkFrame" id="frame1"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <property name="hexpand">True</property> - <property name="label_xalign">0</property> - <property name="shadow_type">none</property> + <property name="label-xalign">0</property> + <property name="shadow-type">none</property> <child> - <!-- n-columns=1 n-rows=1 --> + <!-- n-columns=1 n-rows=4 --> <object class="GtkGrid" id="grid1"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="hexpand">True</property> - <property name="vexpand">True</property> - <property name="row_spacing">6</property> + <property name="can-focus">False</property> <property name="margin-start">12</property> <property name="margin-top">6</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="row-spacing">6</property> <child> <object class="GtkCheckButton" id="pagebreak"> <property name="label" translatable="yes" context="subtotaloptionspage|pagebreak">_Page break between groups</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> <property name="hexpand">True</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> <child internal-child="accessible"> <object class="AtkObject" id="pagebreak-atkobject"> <property name="AtkObject::accessible-description" translatable="yes" context="subtotaloptionspage|extended_tip|pagebreak">Inserts a new page after each group of subtotaled data.</property> @@ -41,19 +41,19 @@ </child> </object> <packing> - <property name="left_attach">0</property> - <property name="top_attach">0</property> + <property name="left-attach">0</property> + <property name="top-attach">0</property> </packing> </child> <child> <object class="GtkCheckButton" id="case"> <property name="label" translatable="yes" context="subtotaloptionspage|case">_Case sensitive</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> <property name="hexpand">True</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> <child internal-child="accessible"> <object class="AtkObject" id="case-atkobject"> <property name="AtkObject::accessible-description" translatable="yes" context="subtotaloptionspage|extended_tip|case">Recalculates subtotals when you change the case of a data label.</property> @@ -61,19 +61,19 @@ </child> </object> <packing> - <property name="left_attach">0</property> - <property name="top_attach">1</property> + <property name="left-attach">0</property> + <property name="top-attach">1</property> </packing> </child> <child> <object class="GtkCheckButton" id="sort"> <property name="label" translatable="yes" context="subtotaloptionspage|sort">Pre-_sort area according to groups</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> <property name="hexpand">True</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> <child internal-child="accessible"> <object class="AtkObject" id="sort-atkobject"> <property name="AtkObject::accessible-description" translatable="yes" context="subtotaloptionspage|extended_tip|sort">Sorts the area that you selected in the Group by box of the Group tabs according to the columns that you selected.</property> @@ -81,8 +81,28 @@ </child> </object> <packing> - <property name="left_attach">0</property> - <property name="top_attach">2</property> + <property name="left-attach">0</property> + <property name="top-attach">2</property> + </packing> + </child> + <child> + <object class="GtkCheckButton" id="summarybelow"> + <property name="label" translatable="yes" context="subtotaloptionspage|sort">_Summary below data</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> + <property name="hexpand">True</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> + <child internal-child="accessible"> + <object class="AtkObject" id="summarybelow-atkobject"> + <property name="AtkObject::accessible-description" translatable="yes" context="subtotaloptionspage|extended_tip|summarybelow">Decide if the subtotals below or above the data. Reposition subtotals when you change the summary below data options.</property> + </object> + </child> + </object> + <packing> + <property name="left-attach">0</property> + <property name="top-attach">3</property> </packing> </child> </object> @@ -90,7 +110,7 @@ <child type="label"> <object class="GtkLabel" id="label1"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <property name="label" translatable="yes" context="subtotaloptionspage|label1">Groups</property> <attributes> <attribute name="weight" value="bold"/> @@ -107,31 +127,31 @@ <child> <object class="GtkFrame" id="frame2"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <property name="hexpand">True</property> <property name="vexpand">True</property> - <property name="label_xalign">0</property> - <property name="shadow_type">none</property> + <property name="label-xalign">0</property> + <property name="shadow-type">none</property> <child> - <!-- n-columns=1 n-rows=1 --> + <!-- n-columns=1 n-rows=5 --> <object class="GtkGrid" id="grid2"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="hexpand">True</property> - <property name="vexpand">True</property> - <property name="row_spacing">6</property> + <property name="can-focus">False</property> <property name="margin-start">12</property> <property name="margin-top">6</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="row-spacing">6</property> <child> <object class="GtkRadioButton" id="ascending"> <property name="label" translatable="yes" context="subtotaloptionspage|ascending">_Ascending</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> <property name="hexpand">True</property> - <property name="use_underline">True</property> + <property name="use-underline">True</property> <property name="active">True</property> - <property name="draw_indicator">True</property> + <property name="draw-indicator">True</property> <child internal-child="accessible"> <object class="AtkObject" id="ascending-atkobject"> <property name="AtkObject::accessible-description" translatable="yes" context="subtotaloptionspage|extended_tip|ascending">Sorts beginning with the lowest value. You can define the sort rules on Data - Sort - Options.</property> @@ -139,19 +159,19 @@ </child> </object> <packing> - <property name="left_attach">0</property> - <property name="top_attach">0</property> + <property name="left-attach">0</property> + <property name="top-attach">0</property> </packing> </child> <child> <object class="GtkRadioButton" id="descending"> <property name="label" translatable="yes" context="subtotaloptionspage|descending">D_escending</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> <property name="hexpand">True</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> <property name="group">ascending</property> <child internal-child="accessible"> <object class="AtkObject" id="descending-atkobject"> @@ -160,19 +180,19 @@ </child> </object> <packing> - <property name="left_attach">0</property> - <property name="top_attach">1</property> + <property name="left-attach">0</property> + <property name="top-attach">1</property> </packing> </child> <child> <object class="GtkCheckButton" id="formats"> <property name="label" translatable="yes" context="subtotaloptionspage|formats">I_nclude formats</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> <property name="hexpand">True</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> <child internal-child="accessible"> <object class="AtkObject" id="formats-atkobject"> <property name="AtkObject::accessible-description" translatable="yes" context="subtotaloptionspage|extended_tip|formats">Considers formatting attributes when sorting.</property> @@ -180,40 +200,40 @@ </child> </object> <packing> - <property name="left_attach">0</property> - <property name="top_attach">2</property> + <property name="left-attach">0</property> + <property name="top-attach">2</property> </packing> </child> <child> <object class="GtkCheckButton" id="btnuserdef"> <property name="label" translatable="yes" context="subtotaloptionspage|btnuserdef">C_ustom sort order</property> <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> + <property name="can-focus">True</property> + <property name="receives-default">False</property> <property name="hexpand">True</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> + <property name="use-underline">True</property> + <property name="draw-indicator">True</property> <accessibility> <relation type="label-for" target="lbuserdef"/> </accessibility> </object> <packing> - <property name="left_attach">0</property> - <property name="top_attach">3</property> + <property name="left-attach">0</property> + <property name="top-attach">3</property> </packing> </child> <child> <object class="GtkComboBoxText" id="lbuserdef"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <property name="margin-start">12</property> <accessibility> <relation type="labelled-by" target="btnuserdef"/> </accessibility> </object> <packing> - <property name="left_attach">0</property> - <property name="top_attach">4</property> + <property name="left-attach">0</property> + <property name="top-attach">4</property> </packing> </child> </object> @@ -221,7 +241,7 @@ <child type="label"> <object class="GtkLabel" id="label2"> <property name="visible">True</property> - <property name="can_focus">False</property> + <property name="can-focus">False</property> <property name="label" translatable="yes" context="subtotaloptionspage|label2">Sort</property> <attributes> <attribute name="weight" value="bold"/> commit 64acf8d29d776a081e458b7600a0a382b24e66a0 Author: Caolán McNamara <caolan.mcnam...@collabora.com> AuthorDate: Mon Aug 26 09:59:59 2024 +0100 Commit: Thorsten Behrens <thorsten.behr...@allotropia.de> CommitDate: Mon Nov 4 23:10:36 2024 +0100 cid#e608033 silence bogus Overflowed array index read Change-Id: I37da01ba951cd2db0398b58627b00c0c72a2e57a Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172385 Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com> Tested-by: Jenkins diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx index 86c709f711c4..d022ba2c4473 100644 --- a/sc/source/core/data/table3.cxx +++ b/sc/source/core/data/table3.cxx @@ -2003,7 +2003,6 @@ bool ScTable::DoSubTotals( ScSubTotalParam& rParam ) SCROW nStartRow = rParam.nRow1 + 1; // Header SCCOL nEndCol = rParam.nCol2; SCROW nEndRow = rParam.nRow2; // will change - sal_uInt16 i; // Remove empty rows at the end // so that all exceeding (rDocument.MaxRow()) can be found by InsertRow (#35180#) @@ -2013,11 +2012,13 @@ bool ScTable::DoSubTotals( ScSubTotalParam& rParam ) sal_uInt16 nLevelCount = 0; // Number of levels bool bDoThis = true; - for (i=0; i<MAXSUBTOTAL && bDoThis; i++) + for (sal_uInt16 i = 0; i < MAXSUBTOTAL && bDoThis; ++i) + { if (rParam.bGroupActive[i]) - nLevelCount = i+1; + nLevelCount = o3tl::sanitizing_inc(i); else bDoThis = false; + } if (nLevelCount==0) // do nothing return true; @@ -2059,7 +2060,7 @@ bool ScTable::DoSubTotals( ScSubTotalParam& rParam ) if (nResCount > 0) // otherwise only sort { - for (i=0; i<=aRowEntry.nGroupNo; i++) + for (sal_uInt16 i = 0; i <= aRowEntry.nGroupNo; ++i) { aSubString = GetString( nGroupCol[i], nStartRow ); if ( bIgnoreCase ) @@ -2079,7 +2080,7 @@ bool ScTable::DoSubTotals( ScSubTotalParam& rParam ) { bChanged = false; OUString aString; - for (i=0; i<=aRowEntry.nGroupNo && !bChanged; i++) + for (sal_uInt16 i = 0; i <= aRowEntry.nGroupNo && !bChanged; ++i) { aString = GetString( nGroupCol[i], nRow ); if (bIgnoreCase) @@ -2139,7 +2140,7 @@ bool ScTable::DoSubTotals( ScSubTotalParam& rParam ) ++nRow; ++nEndRow; aRowEntry.nSubStartRow = nRow; - for (i=0; i<=aRowEntry.nGroupNo; i++) + for (sal_uInt16 i = 0; i <= aRowEntry.nGroupNo; ++i) { aSubString = GetString( nGroupCol[i], nRow ); if ( bIgnoreCase ) commit 11b0995c804258ef560858eb369e9f7e14a0b80e Author: Caolán McNamara <caolan.mcnam...@collabora.com> AuthorDate: Thu Aug 22 12:38:53 2024 +0100 Commit: Thorsten Behrens <thorsten.behr...@allotropia.de> CommitDate: Mon Nov 4 23:10:12 2024 +0100 cid#1608296 silence Overflowed integer argument and cid#1606815 Overflowed integer argument cid#1606617 Overflowed integer argument Change-Id: I4569190edd9b8d65e9b080a7ad0fac391f4a657e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/172348 Tested-by: Jenkins Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com> diff --git a/compilerplugins/clang/test/writeonlyvars.cxx b/compilerplugins/clang/test/writeonlyvars.cxx index 0fc141f62dd2..fc521fa7d417 100644 --- a/compilerplugins/clang/test/writeonlyvars.cxx +++ b/compilerplugins/clang/test/writeonlyvars.cxx @@ -24,6 +24,7 @@ #if defined LIBO_USE_SOURCE_LOCATION // expected-error@o3tl/runtimetooustring.hxx:* {{read s [loplugin:writeonlyvars]}} // expected-error@o3tl/runtimetooustring.hxx:* {{write s [loplugin:writeonlyvars]}} +// expected-error@o3tl/safeint.hxx:* {{read res [loplugin:writeonlyvars]}} #if !defined NDEBUG // expected-error@o3tl/runtimetooustring.hxx:* {{read ok [loplugin:writeonlyvars]}} #endif diff --git a/connectivity/source/drivers/dbase/DIndexIter.cxx b/connectivity/source/drivers/dbase/DIndexIter.cxx index 37e28a073f4c..5420d044d5c6 100644 --- a/connectivity/source/drivers/dbase/DIndexIter.cxx +++ b/connectivity/source/drivers/dbase/DIndexIter.cxx @@ -262,7 +262,8 @@ ONDXKey* OIndexIterator::GetNextKey() sal_uInt16 nPos = pParentPage->Search(pPage); if (nPos != pParentPage->Count() - 1) { // page found - pPage = (*pParentPage)[nPos + 1].GetChild(m_xIndex.get(), pParentPage); + pPage = (*pParentPage)[o3tl::sanitizing_inc(nPos)].GetChild(m_xIndex.get(), + pParentPage); break; } } diff --git a/include/o3tl/safeint.hxx b/include/o3tl/safeint.hxx index a32c6beea142..80f8b45c4042 100644 --- a/include/o3tl/safeint.hxx +++ b/include/o3tl/safeint.hxx @@ -231,6 +231,42 @@ template<typename T> [[nodiscard]] inline T sanitizing_min(T a, T b) return std::min(a, b); } +// To sanitize in/de-crementing value where the result is known by the caller to be guaranteed to fit in +// the source type range without over/under-flow +[[nodiscard]] inline unsigned short sanitizing_inc(unsigned short value) +{ + int res = value + 1; + assert(res <= std::numeric_limits<unsigned short>::max() && + "nValue was supposed to be incrementable without overflow"); + return static_cast<unsigned short>(res); +} + +[[nodiscard]] inline unsigned short sanitizing_dec(unsigned short value) +{ + int res = value - 1; + assert(res >= 0 && + "nValue was supposed to be decrementable without underflow"); + return static_cast<unsigned short>(res); +} + +[[nodiscard]] inline short sanitizing_inc(short value) +{ + int res = value + 1; + assert(res >= std::numeric_limits<short>::min() && + res <= std::numeric_limits<short>::max() && + "nValue was supposed to be incrementable without overflow"); + return static_cast<short>(res); +} + +[[nodiscard]] inline short sanitizing_dec(short value) +{ + int res = value - 1; + assert(res >= std::numeric_limits<short>::min() && + res <= std::numeric_limits<short>::max() && + "nValue was supposed to be decrementable without underflow"); + return static_cast<short>(res); +} + } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/lotuswordpro/source/filter/lwptablelayout.cxx b/lotuswordpro/source/filter/lwptablelayout.cxx index 90fb18c7ba67..b8c95f9d6eec 100644 --- a/lotuswordpro/source/filter/lwptablelayout.cxx +++ b/lotuswordpro/source/filter/lwptablelayout.cxx @@ -808,7 +808,7 @@ void LwpTableLayout::ParseTable() SAL_WARN("lwp", "truncating HeadingRow for fuzzing performance"); nEndHeadRow = nStartHeadRow + 128; } - nContentRow = ConvertHeadingRow(m_pXFTable,nStartHeadRow,nEndHeadRow+1); + nContentRow = ConvertHeadingRow(m_pXFTable, nStartHeadRow, o3tl::sanitizing_inc(nEndHeadRow)); } } diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx index aa8d7dd9bd3d..654aeb464736 100644 --- a/sc/source/core/data/document.cxx +++ b/sc/source/core/data/document.cxx @@ -5278,7 +5278,7 @@ void ScDocument::GetBorderLines( SCCOL nCol, SCROW nRow, SCTAB nTab, if ( nCol > 0 ) { - const SvxBorderLine* pOther = GetEffItem( nCol-1, nRow, nTab, ATTR_BORDER )->GetRight(); + const SvxBorderLine* pOther = GetEffItem( o3tl::sanitizing_dec(nCol), nRow, nTab, ATTR_BORDER )->GetRight(); if ( ScHasPriority( pOther, pLeftLine ) ) pLeftLine = pOther; } @@ -5290,7 +5290,7 @@ void ScDocument::GetBorderLines( SCCOL nCol, SCROW nRow, SCTAB nTab, } if ( nCol < MaxCol() ) { - const SvxBorderLine* pOther = GetEffItem( nCol+1, nRow, nTab, ATTR_BORDER )->GetLeft(); + const SvxBorderLine* pOther = GetEffItem( o3tl::sanitizing_inc(nCol), nRow, nTab, ATTR_BORDER )->GetLeft(); if ( ScHasPriority( pOther, pRightLine ) ) pRightLine = pOther; } diff --git a/sw/source/ui/index/cnttab.cxx b/sw/source/ui/index/cnttab.cxx index 535dc298dd5a..17b8a3728974 100644 --- a/sw/source/ui/index/cnttab.cxx +++ b/sw/source/ui/index/cnttab.cxx @@ -2809,7 +2809,7 @@ void SwTokenWindow::SetForm(SwForm& rForm, sal_uInt16 nL) if(m_nLevel < MAXLEVEL || rForm.GetTOXType() == TOX_AUTHORITIES) { // #i21237# - SwFormTokens aPattern = m_pForm->GetPattern(m_nLevel + 1); + SwFormTokens aPattern = m_pForm->GetPattern(o3tl::sanitizing_inc(m_nLevel)); bool bLastWasText = false; //assure alternating text - code - text SwTOXWidget* pSetActiveControl = nullptr;