/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source eCode Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace com::sun::star; using ::editeng::SvxBorderLine; namespace { void collectUIInformation(std::map&& aParameters, const OUString& rAction) { EventDescription aDescription; aDescription.aID = "grid_window"; aDescription.aAction = rAction; aDescription.aParameters = std::move(aParameters); aDescription.aParent = "MainWindow"; aDescription.aKeyWord = "ScGridWinUIObject"; UITestLogger::getInstance().logEvent(aDescription); } } using ::std::vector; using ::std::unique_ptr; bool ScViewFunc::AdjustBlockHeight( bool bPaint, ScMarkData* pMarkData ) { ScDocShell* pDocSh = GetViewData().GetDocShell(); if (!pMarkData) pMarkData = &GetViewData().GetMarkData(); ScDocument& rDoc = pDocSh->GetDocument(); std::vector aMarkedRows = pMarkData->GetMarkedRowSpans(); if (aMarkedRows.empty()) { SCROW nCurRow = GetViewData().GetCurY(); aMarkedRows.emplace_back(nCurRow, nCurRow); } if (comphelper::LibreOfficeKit::isActive()) { SCCOLROW nStart = aMarkedRows[0].mnStart; OnLOKSetWidthOrHeight(nStart, /*width: */ false); } double nPPTX = GetViewData().GetPPTX(); double nPPTY = GetViewData().GetPPTY(); Fraction aZoomX = GetViewData().GetZoomX(); Fraction aZoomY = GetViewData().GetZoomY(); ScSizeDeviceProvider aProv(pDocSh); if (aProv.IsPrinter()) { nPPTX = aProv.GetPPTX(); nPPTY = aProv.GetPPTY(); aZoomX = aZoomY = Fraction( 1, 1 ); } sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice()); bool bAnyChanged = false; for (const SCTAB& nTab : *pMarkData) { bool bChanged = false; SCROW nPaintY = 0; for (const auto& rRow : aMarkedRows) { SCROW nStartNo = rRow.mnStart; SCROW nEndNo = rRow.mnEnd; ScAddress aTopLeft(0, nStartNo, nTab); rDoc.UpdateScriptTypes(aTopLeft, rDoc.GetSheetLimits().GetMaxColCount(), nEndNo-nStartNo+1); if (rDoc.SetOptimalHeight(aCxt, nStartNo, nEndNo, nTab, true)) { if (!bChanged) nPaintY = nStartNo; bAnyChanged = bChanged = true; } } // tdf#76183: recalculate objects' positions if (bChanged) rDoc.SetDrawPageSize(nTab); if ( bPaint && bChanged ) pDocSh->PostPaint( 0, nPaintY, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid | PaintPartFlags::Left ); } if ( bPaint && bAnyChanged ) pDocSh->UpdateOle(GetViewData()); if (comphelper::LibreOfficeKit::isActive()) { SCTAB nTab = GetViewData().GetTabNo(); ScTabViewShell::notifyAllViewsSheetGeomInvalidation( GetViewData().GetViewShell(), false /* bColumns */, true /* bRows */, true /* bSizes*/, false /* bHidden */, false /* bFiltered */, false /* bGroups */, nTab); ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, nTab); } return bAnyChanged; } bool ScViewFunc::AdjustRowHeight( SCROW nStartRow, SCROW nEndRow, bool bApi ) { if (comphelper::LibreOfficeKit::isActive()) { OnLOKSetWidthOrHeight(nStartRow, /*width: */ false); } ScDocShell* pDocSh = GetViewData().GetDocShell(); ScDocument& rDoc = pDocSh->GetDocument(); SCTAB nTab = GetViewData().GetTabNo(); double nPPTX = GetViewData().GetPPTX(); double nPPTY = GetViewData().GetPPTY(); Fraction aZoomX = GetViewData().GetZoomX(); Fraction aZoomY = GetViewData().GetZoomY(); sal_uInt16 nOldPixel = 0; if (nStartRow == nEndRow) nOldPixel = static_cast(rDoc.GetRowHeight(nStartRow,nTab) * nPPTY); ScSizeDeviceProvider aProv(pDocSh); if (aProv.IsPrinter()) { nPPTX = aProv.GetPPTX(); nPPTY = aProv.GetPPTY(); aZoomX = aZoomY = Fraction( 1, 1 ); } sc::RowHeightContext aCxt(rDoc.MaxRow(), nPPTX, nPPTY, aZoomX, aZoomY, aProv.GetDevice()); bool bChanged = rDoc.SetOptimalHeight(aCxt, nStartRow, nEndRow, nTab, bApi); // tdf#76183: recalculate objects' positions if (bChanged) rDoc.SetDrawPageSize(nTab); if (bChanged && ( nStartRow == nEndRow )) { sal_uInt16 nNewPixel = static_cast(rDoc.GetRowHeight(nStartRow,nTab) * nPPTY); if ( nNewPixel == nOldPixel ) bChanged = false; } if ( bChanged ) pDocSh->PostPaint( 0, nStartRow, nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid | PaintPartFlags::Left ); if (comphelper::LibreOfficeKit::isActive()) { ScTabViewShell::notifyAllViewsSheetGeomInvalidation( GetViewData().GetViewShell(), false /* bColumns */, true /* bRows */, true /* bSizes*/, false /* bHidden */, false /* bFiltered */, false /* bGroups */, nTab); ScTabViewShell::notifyAllViewsHeaderInvalidation(GetViewData().GetViewShell(), ROW_HEADER, GetViewData().GetTabNo()); } return bChanged; } namespace { enum ScAutoSum { ScAutoSumNone = 0, ScAutoSumData, ScAutoSumSum, ScAutoSumAverage, ScAutoSumMax, ScAutoSumMin, ScAutoSumCount, ScAutoSumCountA, ScAutoSumProduct, ScAutoSumStDev, ScAutoSumStDevP, ScAutoSumVar, ScAutoSumVarP, ScAutoSumEnd }; } static ScAutoSum lcl_IsAutoSumData( ScDocument& rDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, ScDirection eDir, SCCOLROW& nExtend ) { ScRefCellValue aCell(rDoc, ScAddress(nCol, nRow, nTab)); if (aCell.hasNumeric()) { if (aCell.getType() == CELLTYPE_FORMULA) { ScAutoSum val = ScAutoSumNone; ScTokenArray* pCode = aCell.getFormula()->GetCode(); if ( pCode ) { switch( pCode->GetOuterFuncOpCode() ) { case ocSum : val = ScAutoSumSum; break; case ocAverage : val = ScAutoSumAverage; break; case ocMax : val = ScAutoSumMax; break; case ocMin : val = ScAutoSumMin; break; case ocCount : val = ScAutoSumCount; break; case ocCount2 : val = ScAutoSumCountA; break; case ocProduct : val = ScAutoSumProduct; break; case ocStDev : val = ScAutoSumStDev; break; case ocStDevP : val = ScAutoSumStDevP; break; case ocVar : val = ScAutoSumVar; break; case ocVarP : val = ScAutoSumVarP; break; default : break; } if ( pCode->GetAdjacentExtendOfOuterFuncRefs( nExtend, ScAddress( nCol, nRow, nTab ), eDir ) ) return val; } } return ScAutoSumData; } return ScAutoSumNone; } #define SC_AUTOSUM_MAXCOUNT 20 static ScAutoSum lcl_SeekAutoSumData( ScDocument& rDoc, SCCOL& nCol, SCROW& nRow, SCTAB nTab, ScDirection eDir, SCCOLROW& nExtend ) { sal_uInt16 nCount = 0; while (nCount < SC_AUTOSUM_MAXCOUNT) { if ( eDir == DIR_TOP ) { if (nRow > 0) --nRow; else return ScAutoSumNone; } else { if (nCol > 0) --nCol; else return ScAutoSumNone; } ScAutoSum eSum; if ( (eSum = lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, eDir, nExtend )) != ScAutoSumNone ) return eSum; ++nCount; } return ScAutoSumNone; } #undef SC_AUTOSUM_MAXCOUNT static bool lcl_FindNextSumEntryInColumn( ScDocument& rDoc, SCCOL nCol, SCROW& nRow, SCTAB nTab, SCCOLROW& nExtend, SCROW nMinRow ) { const SCROW nTmp = nRow; ScAutoSum eSkip = ScAutoSumNone; for (;;) { eSkip = lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_TOP, nExtend ); if (eSkip != ScAutoSumData || nRow <= nMinRow ) break; --nRow; } return eSkip >= ScAutoSumSum && nRow < nTmp; } static bool lcl_FindNextSumEntryInRow( ScDocument& rDoc, SCCOL& nCol, SCROW nRow, SCTAB nTab, SCCOLROW& nExtend, SCCOL nMinCol ) { const SCCOL nTmp = nCol; ScAutoSum eSkip = ScAutoSumNone; for (;;) { eSkip = lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_LEFT, nExtend ); if (eSkip != ScAutoSumData || nCol <= nMinCol ) break; --nCol; } return eSkip >= ScAutoSumSum && nCol < nTmp; } static ScAutoSum lcl_GetAutoSumForColumnRange( ScDocument& rDoc, ScRangeList& rRangeList, const ScRange& rRange ) { const ScAddress aStart = rRange.aStart; const ScAddress aEnd = rRange.aEnd; if ( aStart.Col() != aEnd.Col() ) { return ScAutoSumNone; } const SCTAB nTab = aEnd.Tab(); const SCCOL nCol = aEnd.Col(); SCROW nEndRow = aEnd.Row(); SCROW nStartRow = nEndRow; SCCOLROW nExtend = 0; ScAutoSum eSum = lcl_IsAutoSumData( rDoc, nCol, nEndRow, nTab, DIR_TOP, nExtend /*out*/ ); if ( eSum >= ScAutoSumSum ) { bool bContinue = false; do { rRangeList.push_back( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab ) ); nEndRow = static_cast< SCROW >( nExtend ); bContinue = lcl_FindNextSumEntryInColumn( rDoc, nCol, nEndRow /*inout*/, nTab, nExtend /*out*/, aStart.Row() ); if ( bContinue ) { nStartRow = nEndRow; } } while ( bContinue ); } else { while ( nStartRow > aStart.Row() ) { eSum = lcl_IsAutoSumData( rDoc, nCol, nStartRow-1, nTab, DIR_TOP, nExtend /*out*/ ); if (eSum >= ScAutoSumSum ) break; --nStartRow; } rRangeList.push_back( ScRange( nCol, nStartRow, nTab, nCol, nEndRow, nTab ) ); if (eSum == ScAutoSumNone) eSum = ScAutoSumData; } return eSum; } static ScAutoSum lcl_GetAutoSumForRowRange( ScDocument& rDoc, ScRangeList& rRangeList, const ScRange& rRange ) { const ScAddress aStart = rRange.aStart; const ScAddress aEnd = rRange.aEnd; if ( aStart.Row() != aEnd.Row() ) { return ScAutoSumNone; } const SCTAB nTab = aEnd.Tab(); const SCROW nRow = aEnd.Row(); SCCOL nEndCol = aEnd.Col(); SCCOL nStartCol = nEndCol; SCCOLROW nExtend = 0; ScAutoSum eSum = lcl_IsAutoSumData( rDoc, nEndCol, nRow, nTab, DIR_LEFT, nExtend /*out*/ ); if ( eSum >= ScAutoSumSum ) { bool bContinue = false; do { rRangeList.push_back( ScRange( nStartCol, nRow, nTab, nEndCol, nRow, nTab ) ); nEndCol = static_cast< SCCOL >( nExtend ); bContinue = lcl_FindNextSumEntryInRow( rDoc, nEndCol /*inout*/, nRow, nTab, nExtend /*out*/, aStart.Col() ); if ( bContinue ) { nStartCol = nEndCol; } } while ( bContinue ); } else { while ( nStartCol > aStart.Col() ) { eSum = lcl_IsAutoSumData( rDoc, nStartCol-1, nRow, nTab, DIR_LEFT, nExtend /*out*/ ); if (eSum >= ScAutoSumSum ) break; --nStartCol; } rRangeList.push_back( ScRange( nStartCol, nRow, nTab, nEndCol, nRow, nTab ) ); if (eSum == ScAutoSumNone) eSum = ScAutoSumData; } return eSum; } static sal_Int8 GetSubTotal( const OpCode eCode ) { sal_Int8 val; switch ( eCode ) { case ocSum : val = 9; break; case ocAverage : val = 1; break; case ocMax : val = 4; break; case ocMin : val = 5; break; case ocCount : val = 2; break; case ocCount2 : val = 3; break; case ocProduct : val = 6; break; case ocStDev : val = 7; break; case ocStDevP : val = 8; break; case ocVar : val = 10; break; case ocVarP : val = 11; break; default : val = 9; } return val; } bool ScViewFunc::GetAutoSumArea( ScRangeList& rRangeList ) { ScDocument& rDoc = GetViewData().GetDocument(); SCTAB nTab = GetViewData().GetTabNo(); SCCOL nCol = GetViewData().GetCurX(); SCROW nRow = GetViewData().GetCurY(); SCCOL nStartCol = nCol; SCROW nStartRow = nRow; SCCOL nEndCol = nCol; SCROW nEndRow = nRow; SCCOL nSeekCol = nCol; SCROW nSeekRow = nRow; SCCOLROW nExtend; // will become valid via reference for ScAutoSumSum bool bCol = false; bool bRow = false; ScAutoSum eSum; if ( nRow != 0 && ((eSum = lcl_IsAutoSumData( rDoc, nCol, nRow-1, nTab, DIR_TOP, nExtend /*out*/ )) == ScAutoSumData ) && ((eSum = lcl_IsAutoSumData( rDoc, nCol, nRow-1, nTab, DIR_LEFT, nExtend /*out*/ )) == ScAutoSumData ) ) { bRow = true; nSeekRow = nRow - 1; } else if ( nCol != 0 && (eSum = lcl_IsAutoSumData( rDoc, nCol-1, nRow, nTab, DIR_LEFT, nExtend /*out*/ )) == ScAutoSumData ) { bCol = true; nSeekCol = nCol - 1; } else if ( (eSum = lcl_SeekAutoSumData( rDoc, nCol, nSeekRow, nTab, DIR_TOP, nExtend /*out*/ )) != ScAutoSumNone ) bRow = true; else if (( eSum = lcl_SeekAutoSumData( rDoc, nSeekCol, nRow, nTab, DIR_LEFT, nExtend /*out*/ )) != ScAutoSumNone ) bCol = true; if ( bCol || bRow ) { if ( bRow ) { nStartRow = nSeekRow; // nSeekRow might be adjusted via reference if ( eSum >= ScAutoSumSum && eSum < ScAutoSumEnd ) nEndRow = nStartRow; // only sum sums else nEndRow = nRow - 1; // maybe extend data area at bottom } else { nStartCol = nSeekCol; // nSeekCol might be adjusted via reference if ( eSum >= ScAutoSumSum ) nEndCol = nStartCol; // only sum sums else nEndCol = nCol - 1; // maybe extend data area to the right } bool bContinue = false; do { if ( eSum == ScAutoSumData ) { if ( bRow ) { while ( nStartRow != 0 && lcl_IsAutoSumData( rDoc, nCol, nStartRow-1, nTab, DIR_TOP, nExtend /*out*/ ) == eSum ) --nStartRow; } else { while ( nStartCol != 0 && lcl_IsAutoSumData( rDoc, nStartCol-1, nRow, nTab, DIR_LEFT, nExtend /*out*/ ) == eSum ) --nStartCol; } } rRangeList.push_back( ScRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab ) ); if ( eSum >= ScAutoSumSum ) { if ( bRow ) { nEndRow = static_cast< SCROW >( nExtend ); bContinue = lcl_FindNextSumEntryInColumn( rDoc, nCol, nEndRow /*inout*/, nTab, nExtend /*out*/, 0 ); if ( bContinue ) { nStartRow = nEndRow; } } else { nEndCol = static_cast< SCCOL >( nExtend ); bContinue = lcl_FindNextSumEntryInRow( rDoc, nEndCol /*inout*/, nRow, nTab, nExtend /*out*/, 0 ); if ( bContinue ) { nStartCol = nEndCol; } } } } while ( bContinue ); return true; } return false; } void ScViewFunc::EnterAutoSum(const ScRangeList& rRangeList, bool bSubTotal, const ScAddress& rAddr, const OpCode eCode) { OUString aFormula = GetAutoSumFormula( rRangeList, bSubTotal, rAddr , eCode); EnterBlock( aFormula, nullptr ); } bool ScViewFunc::AutoSum( const ScRange& rRange, bool bSubTotal, bool bSetCursor, bool bContinue , const OpCode eCode) { ScDocument& rDoc = GetViewData().GetDocument(); const SCTAB nTab = rRange.aStart.Tab(); SCCOL nStartCol = rRange.aStart.Col(); SCROW nStartRow = rRange.aStart.Row(); const SCCOL nEndCol = rRange.aEnd.Col(); const SCROW nEndRow = rRange.aEnd.Row(); SCCOLROW nExtend = 0; // out parameter for lcl_IsAutoSumData // ignore rows at the top of the given range which don't contain autosum data bool bRowData = false; for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow ) { for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol ) { if ( lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_TOP, nExtend ) != ScAutoSumNone ) { bRowData = true; break; } } if ( bRowData ) { nStartRow = nRow; break; } } if ( !bRowData ) { return false; } // ignore columns at the left of the given range which don't contain autosum data bool bColData = false; for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol ) { for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow ) { if ( lcl_IsAutoSumData( rDoc, nCol, nRow, nTab, DIR_LEFT, nExtend ) != ScAutoSumNone ) { bColData = true; break; } } if ( bColData ) { nStartCol = nCol; break; } } if ( !bColData ) { return false; } const bool bEndRowEmpty = rDoc.IsBlockEmpty( nStartCol, nEndRow, nEndCol, nEndRow, nTab ); const bool bEndColEmpty = rDoc.IsBlockEmpty( nEndCol, nStartRow, nEndCol, nEndRow, nTab ); bool bRow = ( nStartRow != nEndRow ) && ( bEndRowEmpty || !bEndColEmpty ); bool bCol = ( nStartCol != nEndCol ) && ( bEndColEmpty || nStartRow == nEndRow ); // find an empty row for entering the result SCROW nInsRow = nEndRow; if ( bRow && !bEndRowEmpty ) { if ( nInsRow < rDoc.MaxRow() ) { ++nInsRow; while ( !rDoc.IsBlockEmpty( nStartCol, nInsRow, nEndCol, nInsRow, nTab ) ) { if ( nInsRow < rDoc.MaxRow() ) { ++nInsRow; } else { bRow = false; break; } } } else { bRow = false; } } // find an empty column for entering the result SCCOL nInsCol = nEndCol; if ( bCol && !bEndColEmpty ) { if ( nInsCol < rDoc.MaxCol() ) { ++nInsCol; while ( !rDoc.IsBlockEmpty( nInsCol, nStartRow, nInsCol, nEndRow, nTab ) ) { if ( nInsCol < rDoc.MaxCol() ) { ++nInsCol; } else { bCol = false; break; } } } else { bCol = false; } } if ( !bRow && !bCol ) { return false; } SCCOL nMarkEndCol = nEndCol; SCROW nMarkEndRow = nEndRow; ScAutoSum eSum = ScAutoSumNone; SCROW nColSums = 0; SCCOL nRowSums = 0; SCROW nColSumsStartRow = 0; SCCOL nRowSumsStartCol = 0; if ( bRow ) { // calculate the row sums for all columns of the given range SCROW nSumEndRow = nEndRow; if ( bEndRowEmpty ) { // the last row of the given range is empty; // don't take into account for calculating the autosum --nSumEndRow; } else { // increase mark range ++nMarkEndRow; } for ( SCCOL nCol = nStartCol; nCol <= nEndCol; ++nCol ) { if ( !rDoc.IsBlockEmpty( nCol, nStartRow, nCol, nSumEndRow, nTab ) ) { ScRangeList aRangeList; // Include the originally selected start row. const ScRange aRange( nCol, rRange.aStart.Row(), nTab, nCol, nSumEndRow, nTab ); if ( (eSum = lcl_GetAutoSumForColumnRange( rDoc, aRangeList, aRange )) != ScAutoSumNone ) { if (++nRowSums == 1) nRowSumsStartCol = aRangeList[0].aStart.Col(); const OUString aFormula = GetAutoSumFormula( aRangeList, bSubTotal, ScAddress(nCol, nInsRow, nTab), eCode); EnterData( nCol, nInsRow, nTab, aFormula ); } } } } if ( bCol ) { // calculate the column sums for all rows of the given range SCCOL nSumEndCol = nEndCol; if ( bEndColEmpty ) { // the last column of the given range is empty; // don't take into account for calculating the autosum --nSumEndCol; } else { // increase mark range ++nMarkEndCol; } for ( SCROW nRow = nStartRow; nRow <= nEndRow; ++nRow ) { if ( !rDoc.IsBlockEmpty( nStartCol, nRow, nSumEndCol, nRow, nTab ) ) { ScRangeList aRangeList; // Include the originally selected start column. const ScRange aRange( rRange.aStart.Col(), nRow, nTab, nSumEndCol, nRow, nTab ); if ( (eSum = lcl_GetAutoSumForRowRange( rDoc, aRangeList, aRange )) != ScAutoSumNone ) { if (++nColSums == 1) nColSumsStartRow = aRangeList[0].aStart.Row(); const OUString aFormula = GetAutoSumFormula( aRangeList, bSubTotal, ScAddress(nInsCol, nRow, nTab), eCode ); EnterData( nInsCol, nRow, nTab, aFormula ); } } } } // Set new mark range and cursor position. // For sum of sums (and data until sum) mark the actual resulting range if // there is only one, or the data range if more than one. Otherwise use the // original selection. All extended by end column/row where the sum is put. const ScRange aMarkRange( (eSum >= ScAutoSumSum ? (nRowSums == 1 ? nRowSumsStartCol : nStartCol) : rRange.aStart.Col()), (eSum >= ScAutoSumSum ? (nColSums == 1 ? nColSumsStartRow : nStartRow) : rRange.aStart.Row()), nTab, nMarkEndCol, nMarkEndRow, nTab ); MarkRange( aMarkRange, false, bContinue ); if ( bSetCursor ) { SetCursor( nMarkEndCol, nMarkEndRow ); } return true; } OUString ScViewFunc::GetAutoSumFormula( const ScRangeList& rRangeList, bool bSubTotal, const ScAddress& rAddr , const OpCode eCode) { ScViewData& rViewData = GetViewData(); ScDocument& rDoc = rViewData.GetDocument(); ScTokenArray aArray(rDoc); aArray.AddOpCode(bSubTotal ? ocSubTotal : eCode); aArray.AddOpCode(ocOpen); if (bSubTotal) { aArray.AddDouble( GetSubTotal( eCode ) ); aArray.AddOpCode(ocSep); } if(!rRangeList.empty()) { ScRangeList aRangeList = rRangeList; size_t ListSize = aRangeList.size(); for ( size_t i = 0; i < ListSize; ++i ) { const ScRange & r = aRangeList[i]; if (i != 0) aArray.AddOpCode(ocSep); ScComplexRefData aRef; aRef.InitRangeRel(rDoc, r, rAddr); aArray.AddDoubleReference(aRef); } } aArray.AddOpCode(ocClose); ScCompiler aComp(rDoc, rAddr, aArray, rDoc.GetGrammar()); OUStringBuffer aBuf; aComp.CreateStringFromTokenArray(aBuf); aBuf.insert(0, "="); return aBuf.makeStringAndClear(); } void ScViewFunc::EnterBlock( const OUString& rString, const EditTextObject* pData ) { // test for multi selection SCCOL nCol = GetViewData().GetCurX(); SCROW nRow = GetViewData().GetCurY(); SCTAB nTab = GetViewData().GetTabNo(); ScMarkData& rMark = GetViewData().GetMarkData(); if ( rMark.IsMultiMarked() ) { rMark.MarkToSimple(); if ( rMark.IsMultiMarked() ) { // "Insert into multi selection not possible" ErrorMessage(STR_MSSG_PASTEFROMCLIP_0); // insert into single cell if ( pData ) EnterData(nCol, nRow, nTab, *pData); else EnterData( nCol, nRow, nTab, rString ); return; } } if (GetViewData().SelectionForbidsCellFill()) { PaintArea(nCol, nRow, nCol, nRow); // possibly the edit-engine is still painted there return; } ScDocument& rDoc = GetViewData().GetDocument(); OUString aNewStr = rString; if ( pData ) { const ScPatternAttr* pOldPattern = rDoc.GetPattern( nCol, nRow, nTab ); ScTabEditEngine aEngine( *pOldPattern, rDoc.GetEnginePool(), &rDoc ); aEngine.SetTextCurrentDefaults(*pData); ScEditAttrTester aTester( &aEngine ); if (!aTester.NeedsObject()) { aNewStr = aEngine.GetText(); pData = nullptr; } } // Insert via PasteFromClip weld::WaitObject aWait(GetViewData().GetDialogParent()); ScAddress aPos( nCol, nRow, nTab ); ScDocumentUniquePtr pInsDoc(new ScDocument( SCDOCMODE_CLIP )); pInsDoc->ResetClip( &rDoc, nTab ); if (aNewStr[0] == '=') // Formula ? { // SetString not possible, because in Clipboard-Documents nothing will be compiled! pInsDoc->SetFormulaCell(aPos, new ScFormulaCell(rDoc, aPos, aNewStr)); } else if ( pData ) { // A copy of pData will be stored. pInsDoc->SetEditText(aPos, *pData, rDoc.GetEditPool()); } else pInsDoc->SetString( nCol, nRow, nTab, aNewStr ); pInsDoc->SetClipArea( ScRange(aPos) ); // insert Block, with Undo etc. if ( !PasteFromClip( InsertDeleteFlags::CONTENTS, pInsDoc.get(), ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::ATTRIB ) ) return; const SfxUInt32Item* pItem = pInsDoc->GetAttr( nCol, nRow, nTab, ATTR_VALUE_FORMAT ); if ( pItem ) { // set number format if incompatible // MarkData was already MarkToSimple'ed in PasteFromClip const ScRange& aRange = rMark.GetMarkArea(); ScPatternAttr aPattern( rDoc.GetPool() ); aPattern.GetItemSet().Put( *pItem ); SvNumFormatType nNewType = rDoc.GetFormatTable()->GetType( pItem->GetValue() ); rDoc.ApplyPatternIfNumberformatIncompatible( aRange, rMark, aPattern, nNewType ); } } // manual page break void ScViewFunc::InsertPageBreak( bool bColumn, bool bRecord, const ScAddress* pPos, bool bSetModified ) { SCTAB nTab = GetViewData().GetTabNo(); ScAddress aCursor; if (pPos) aCursor = *pPos; else aCursor = ScAddress( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab ); bool bSuccess = GetViewData().GetDocShell()->GetDocFunc(). InsertPageBreak( bColumn, aCursor, bRecord, bSetModified ); if ( bSuccess && bSetModified ) UpdatePageBreakData( true ); // for PageBreak-Mode } void ScViewFunc::DeletePageBreak( bool bColumn, bool bRecord, const ScAddress* pPos, bool bSetModified ) { SCTAB nTab = GetViewData().GetTabNo(); ScAddress aCursor; if (pPos) aCursor = *pPos; else aCursor = ScAddress( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab ); bool bSuccess = GetViewData().GetDocShell()->GetDocFunc(). RemovePageBreak( bColumn, aCursor, bRecord, bSetModified ); if ( bSuccess && bSetModified ) UpdatePageBreakData( true ); // for PageBreak-Mode } void ScViewFunc::RemoveManualBreaks() { ScDocShell* pDocSh = GetViewData().GetDocShell(); ScDocument& rDoc = pDocSh->GetDocument(); SCTAB nTab = GetViewData().GetTabNo(); bool bUndo(rDoc.IsUndoEnabled()); if (bUndo) { ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO )); pUndoDoc->InitUndo( rDoc, nTab, nTab, true, true ); rDoc.CopyToDocument( 0,0,nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, InsertDeleteFlags::NONE, false, *pUndoDoc ); pDocSh->GetUndoManager()->AddUndoAction( std::make_unique( pDocSh, nTab, std::move(pUndoDoc) ) ); } rDoc.RemoveManualBreaks(nTab); rDoc.UpdatePageBreaks(nTab); UpdatePageBreakData( true ); pDocSh->SetDocumentModified(); pDocSh->PostPaint( 0,0,nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, PaintPartFlags::Grid ); } void ScViewFunc::SetPrintZoom(sal_uInt16 nScale) { ScDocShell* pDocSh = GetViewData().GetDocShell(); SCTAB nTab = GetViewData().GetTabNo(); pDocSh->SetPrintZoom( nTab, nScale, 0/*nPages*/ ); } void ScViewFunc::AdjustPrintZoom() { ScRange aRange; if ( GetViewData().GetSimpleArea( aRange ) != SC_MARK_SIMPLE ) aRange = GetViewData().GetMarkData().GetMultiMarkArea(); GetViewData().GetDocShell()->AdjustPrintZoom( aRange ); } void ScViewFunc::SetPrintRanges( bool bEntireSheet, const OUString* pPrint, const OUString* pRepCol, const OUString* pRepRow, bool bAddPrint ) { // on all selected tables ScDocShell* pDocSh = GetViewData().GetDocShell(); ScDocument& rDoc = pDocSh->GetDocument(); ScMarkData& rMark = GetViewData().GetMarkData(); bool bUndo (rDoc.IsUndoEnabled()); std::unique_ptr pOldRanges = rDoc.CreatePrintRangeSaver(); ScAddress::Details aDetails(rDoc.GetAddressConvention(), 0, 0); for (const SCTAB& nTab : rMark) { ScRange aRange( 0,0,nTab ); // print ranges if( !bAddPrint ) { rDoc.ClearPrintRanges( nTab ); rDoc.ClearPrintNamedRanges(nTab); } if( bEntireSheet ) { rDoc.SetPrintEntireSheet( nTab ); } else if ( pPrint ) { if ( !pPrint->isEmpty() ) { const sal_Unicode sep = ScCompiler::GetNativeSymbolChar(ocSep); sal_Int32 nPos = 0; do { const OUString aToken = pPrint->getToken(0, sep, nPos); if ( aRange.ParseAny( aToken, rDoc, aDetails ) & ScRefFlags::VALID ) rDoc.AddPrintRange( nTab, aRange ); } while (nPos >= 0); } } else // NULL = use selection (print range is always set), use empty string to delete all ranges { if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE ) { rDoc.AddPrintRange( nTab, aRange ); } else if ( rMark.IsMultiMarked() ) { rMark.MarkToMulti(); ScRangeListRef pList( new ScRangeList ); rMark.FillRangeListWithMarks( pList.get(), false ); for (size_t i = 0, n = pList->size(); i < n; ++i) { const ScRange & rR = (*pList)[i]; rDoc.AddPrintRange(nTab, rR); } } } // repeat columns if ( pRepCol ) { if ( pRepCol->isEmpty() ) rDoc.SetRepeatColRange( nTab, std::nullopt ); else if ( aRange.ParseAny( *pRepCol, rDoc, aDetails ) & ScRefFlags::VALID ) rDoc.SetRepeatColRange( nTab, std::move(aRange) ); } // repeat rows if ( pRepRow ) { if ( pRepRow->isEmpty() ) rDoc.SetRepeatRowRange( nTab, std::nullopt ); else if ( aRange.ParseAny( *pRepRow, rDoc, aDetails ) & ScRefFlags::VALID ) rDoc.SetRepeatRowRange( nTab, std::move(aRange) ); } } // undo (for all tables) if (bUndo) { SCTAB nCurTab = GetViewData().GetTabNo(); std::unique_ptr pNewRanges = rDoc.CreatePrintRangeSaver(); if (comphelper::LibreOfficeKit::isActive()) { tools::JsonWriter aJsonWriter; pNewRanges->GetPrintRangesInfo(aJsonWriter); SfxViewShell* pViewShell = GetViewData().GetViewShell(); const OString message = aJsonWriter.finishAndGetAsOString(); pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_PRINT_RANGES, message); } pDocSh->GetUndoManager()->AddUndoAction( std::make_unique( pDocSh, nCurTab, std::move(pOldRanges), std::move(pNewRanges) ) ); } else pOldRanges.reset(); // update page breaks for (const auto& rTab : rMark) ScPrintFunc( pDocSh, pDocSh->GetPrinter(), rTab ).UpdatePages(); SfxBindings& rBindings = GetViewData().GetBindings(); rBindings.Invalidate( SID_DELETE_PRINTAREA ); pDocSh->SetDocumentModified(); } // Merge cells bool ScViewFunc::TestMergeCells() // pre-test (for menu) { // simple test: true if there's a selection but no multi selection and not filtered const ScMarkData& rMark = GetViewData().GetMarkData(); if ( rMark.IsMarked() || rMark.IsMultiMarked() ) { ScRange aRange; bool bMergeable = ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE ); bMergeable = bMergeable && ( aRange.aStart.Col() != aRange.aEnd.Col() || aRange.aStart.Row() != aRange.aEnd.Row() ); return bMergeable; } else return false; } void ScViewFunc::MergeCells( bool bApi, bool bDoContents, bool bCenter, const sal_uInt16 nSlot ) { // Editable- and Being-Nested- test must be at the beginning (in DocFunc too), // so that the Contents-QueryBox won't appear ScEditableTester aTester( this ); if (!aTester.IsEditable()) { ErrorMessage(aTester.GetMessageId()); return; } ScMarkData& rMark = GetViewData().GetMarkData(); rMark.MarkToSimple(); if (!rMark.IsMarked()) { ErrorMessage(STR_NOMULTISELECT); return; } ScDocShell* pDocSh = GetViewData().GetDocShell(); ScDocument& rDoc = pDocSh->GetDocument(); const ScRange& aMarkRange = rMark.GetMarkArea(); SCCOL nStartCol = aMarkRange.aStart.Col(); SCROW nStartRow = aMarkRange.aStart.Row(); SCTAB nStartTab = aMarkRange.aStart.Tab(); SCCOL nEndCol = aMarkRange.aEnd.Col(); SCROW nEndRow = aMarkRange.aEnd.Row(); SCTAB nEndTab = aMarkRange.aEnd.Tab(); if ( nStartCol == nEndCol && nStartRow == nEndRow ) { // nothing to do return; } if ( rDoc.HasAttrib( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab, HasAttrFlags::Merged | HasAttrFlags::Overlapped ) ) { // "Don't nest merging !" ErrorMessage(STR_MSSG_MERGECELLS_0); return; } // Check for the contents of all selected tables. bool bAskDialog = false; ScCellMergeOption aMergeOption(nStartCol, nStartRow, nEndCol, nEndRow, bCenter); for (const SCTAB& i : rMark) { aMergeOption.maTabs.insert(i); sc::MultiDataCellState aState = rDoc.HasMultipleDataCells(aMergeOption.getSingleRange(i)); switch (aState.meState) { case sc::MultiDataCellState::HasMultipleCells: { // this range contains multiple data cells. bAskDialog = true; break; } case sc::MultiDataCellState::HasOneCell: { // this range contains only one data cell. if (nStartCol != aState.mnCol1 || nStartRow != aState.mnRow1) bDoContents = true; // move the value to the top-left. break; } default: ; } } bool bEmptyMergedCells = officecfg::Office::Calc::Compatibility::MergeCells::EmptyMergedCells::get(); auto doMerge = [this, pDocSh, aMergeOption, bApi, nStartCol, nStartRow, aMarkRange] (bool bNowDoContents, bool bNowEmptyMergedCells) { if (pDocSh->GetDocFunc().MergeCells(aMergeOption, bNowDoContents, true/*bRecord*/, bApi, bNowEmptyMergedCells)) { SetCursor( nStartCol, nStartRow ); // DoneBlockMode( sal_False); Unmark(); pDocSh->UpdateOle(GetViewData()); UpdateInputLine(); OUString aStartAddress = aMarkRange.aStart.GetColRowString(); OUString aEndAddress = aMarkRange.aEnd.GetColRowString(); collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "MERGE_CELLS"); } }; if (bAskDialog) { bool bShowDialog = officecfg::Office::Calc::Compatibility::MergeCells::ShowDialog::get(); if (!bApi && bShowDialog) { auto pBox = std::make_shared(GetViewData().GetDialogParent()); SfxViewShell* pViewShell = GetViewData().GetViewShell(); weld::DialogController::runAsync(pBox, [=](sal_Int32 nRetVal) { if (nRetVal == RET_OK) { bool bRealDoContents = bDoContents; bool bRealEmptyMergedCells = bEmptyMergedCells; switch (pBox->GetMergeCellsOption()) { case MoveContentHiddenCells: bRealDoContents = true; break; case KeepContentHiddenCells: bRealEmptyMergedCells = false; break; case EmptyContentHiddenCells: bRealEmptyMergedCells = true; break; default: assert(!"Unknown option for merge cells."); break; } doMerge(bRealDoContents, bRealEmptyMergedCells); if (nSlot != 0) { SfxRequest aReq(pViewShell->GetViewFrame(), nSlot); if (!bApi && bRealDoContents) aReq.AppendItem(SfxBoolItem(nSlot, bDoContents)); SfxBindings& rBindings = pViewShell->GetViewFrame().GetBindings(); rBindings.Invalidate(nSlot); aReq.Done(); } } // else cancelled }); } } else doMerge(bDoContents, bEmptyMergedCells); } bool ScViewFunc::TestRemoveMerge() { bool bMerged = false; ScRange aRange; if (GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE) { ScDocument& rDoc = GetViewData().GetDocument(); if ( rDoc.HasAttrib( aRange, HasAttrFlags::Merged ) ) bMerged = true; } return bMerged; } static bool lcl_extendMergeRange(ScCellMergeOption& rOption, const ScRange& rRange) { bool bExtended = false; if (rOption.mnStartCol > rRange.aStart.Col()) { rOption.mnStartCol = rRange.aStart.Col(); bExtended = true; } if (rOption.mnStartRow > rRange.aStart.Row()) { rOption.mnStartRow = rRange.aStart.Row(); bExtended = true; } if (rOption.mnEndCol < rRange.aEnd.Col()) { rOption.mnEndCol = rRange.aEnd.Col(); bExtended = true; } if (rOption.mnEndRow < rRange.aEnd.Row()) { rOption.mnEndRow = rRange.aEnd.Row(); bExtended = true; } return bExtended; } bool ScViewFunc::RemoveMerge() { ScRange aRange; ScEditableTester aTester( this ); if (!aTester.IsEditable()) { ErrorMessage(aTester.GetMessageId()); return false; } else if (GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE) { ScDocument& rDoc = GetViewData().GetDocument(); ScRange aExtended( aRange ); rDoc.ExtendMerge( aExtended ); ScDocShell* pDocSh = GetViewData().GetDocShell(); const ScMarkData& rMark = GetViewData().GetMarkData(); ScCellMergeOption aOption(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row()); bool bExtended = false; do { bExtended = false; for (const SCTAB& i : rMark) { aOption.maTabs.insert(i); aExtended.aStart.SetTab(i); aExtended.aEnd.SetTab(i); rDoc.ExtendMerge(aExtended); rDoc.ExtendOverlapped(aExtended); // Expand the current range to be inclusive of all merged // areas on all sheets. bExtended = lcl_extendMergeRange(aOption, aExtended); } } while (bExtended); bool bOk = pDocSh->GetDocFunc().UnmergeCells(aOption, true/*bRecord*/, nullptr); aExtended = aOption.getFirstSingleRange(); MarkRange( aExtended ); if (bOk) pDocSh->UpdateOle(GetViewData()); } OUString aCellLocation = aRange.aStart.GetColRowString(); collectUIInformation({{"CELL", aCellLocation}}, "UNMERGE_CELL"); return true; //! bOk ?? } void ScViewFunc::FillSimple( FillDir eDir ) { ScRange aRange; if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE) { ScDocShell* pDocSh = GetViewData().GetDocShell(); const ScMarkData& rMark = GetViewData().GetMarkData(); bool bSuccess = pDocSh->GetDocFunc().FillSimple( aRange, &rMark, eDir, false ); if (bSuccess) { pDocSh->UpdateOle(GetViewData()); UpdateScrollBars(); auto& rDoc = pDocSh->GetDocument(); bool bDoAutoSpell = rDoc.GetDocOptions().IsAutoSpell(); if ( bDoAutoSpell ) { // Copy AutoSpellData from above(left/right/below) if no selection. switch (eDir) { case FILL_TO_BOTTOM: if (aRange.aStart.Row() > 0 && aRange.aStart.Row() == aRange.aEnd.Row()) aRange.aStart.IncRow(-1); break; case FILL_TO_TOP: if (aRange.aEnd.Row() < rDoc.MaxRow() && aRange.aStart.Row() == aRange.aEnd.Row()) aRange.aEnd.IncRow(1); break; case FILL_TO_RIGHT: if (aRange.aStart.Col() > 0 && aRange.aStart.Col() == aRange.aEnd.Col()) aRange.aStart.IncCol(-1); break; case FILL_TO_LEFT: if (aRange.aEnd.Col() < rDoc.MaxCol() && aRange.aStart.Col() == aRange.aEnd.Col()) aRange.aEnd.IncCol(1); break; } CopyAutoSpellData(eDir, aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(), ::std::numeric_limits::max()); } // Invalidate cell slots and update input line with new content. CellContentChanged(); } } else ErrorMessage(STR_NOMULTISELECT); } void ScViewFunc::FillSeries( FillDir eDir, FillCmd eCmd, FillDateCmd eDateCmd, double fStart, double fStep, double fMax ) { ScRange aRange; if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE) { ScDocShell* pDocSh = GetViewData().GetDocShell(); const ScMarkData& rMark = GetViewData().GetMarkData(); bool bSuccess = pDocSh->GetDocFunc(). FillSeries( aRange, &rMark, eDir, eCmd, eDateCmd, fStart, fStep, fMax, false ); if (bSuccess) { pDocSh->UpdateOle(GetViewData()); UpdateScrollBars(); HelperNotifyChanges::NotifyIfChangesListeners(*pDocSh, aRange); } } else ErrorMessage(STR_NOMULTISELECT); } void ScViewFunc::FillAuto( FillDir eDir, SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount ) { SCTAB nTab = GetViewData().GetTabNo(); ScRange aRange( nStartCol,nStartRow,nTab, nEndCol,nEndRow,nTab ); ScRange aSourceRange( aRange ); ScDocShell* pDocSh = GetViewData().GetDocShell(); const ScMarkData& rMark = GetViewData().GetMarkData(); bool bSuccess = pDocSh->GetDocFunc(). FillAuto( aRange, &rMark, eDir, nCount, false ); if (!bSuccess) return; MarkRange( aRange, false ); // aRange was modified in FillAuto pDocSh->UpdateOle(GetViewData()); UpdateScrollBars(); bool bDoAutoSpell = pDocSh->GetDocument().GetDocOptions().IsAutoSpell(); if ( bDoAutoSpell ) CopyAutoSpellData(eDir, nStartCol, nStartRow, nEndCol, nEndRow, nCount); ScModelObj* pModelObj = pDocSh->GetModel(); ScRangeList aChangeRanges; ScRange aChangeRange( aRange ); switch (eDir) { case FILL_TO_BOTTOM: aChangeRange.aStart.SetRow( aSourceRange.aEnd.Row() + 1 ); break; case FILL_TO_TOP: aChangeRange.aEnd.SetRow( aSourceRange.aStart.Row() - 1 ); break; case FILL_TO_RIGHT: aChangeRange.aStart.SetCol( aSourceRange.aEnd.Col() + 1 ); break; case FILL_TO_LEFT: aChangeRange.aEnd.SetCol( aSourceRange.aStart.Col() - 1 ); break; default: break; } aChangeRanges.push_back( aChangeRange ); if (HelperNotifyChanges::getMustPropagateChangesModel(pModelObj)) HelperNotifyChanges::Notify(*pModelObj, aChangeRanges); else if (pModelObj) HelperNotifyChanges::Notify(*pModelObj, aChangeRanges, "data-area-invalidate"); } void ScViewFunc::CopyAutoSpellData( FillDir eDir, SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, sal_uLong nCount ) { const ScDocument* pDoc = &GetViewData().GetDocument(); SCTAB nTab = GetViewData().GetTabNo(); CellType eCellType; ScGridWindow* pWin = GetActiveWin(); if ( pWin->InsideVisibleRange(nStartCol, nStartRow) && pWin->InsideVisibleRange(nEndCol, nEndRow) ) { if ( nCount == ::std::numeric_limits::max() ) { switch( eDir ) { case FILL_TO_BOTTOM: for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr ) { eCellType = pDoc->GetCellType(nColItr, nStartRow, nTab); // We need this optimization only for EditTextObject source cells if (eCellType != CELLTYPE_EDIT) continue; const std::vector* pRanges = pWin->GetAutoSpellData(nColItr, nStartRow); if ( !pRanges ) continue; for ( SCROW nRowItr = nStartRow + 1; nRowItr <= nEndRow; ++nRowItr ) pWin->SetAutoSpellData(nColItr, nRowItr, pRanges); } break; case FILL_TO_TOP: for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr ) { eCellType = pDoc->GetCellType(nColItr, nEndRow, nTab); // We need this optimization only for EditTextObject source cells if (eCellType != CELLTYPE_EDIT) continue; const std::vector* pRanges = pWin->GetAutoSpellData(nColItr, nEndRow); if ( !pRanges ) continue; for ( SCROW nRowItr = nEndRow - 1; nRowItr >= nStartRow; --nRowItr ) pWin->SetAutoSpellData(nColItr, nRowItr, pRanges); } break; case FILL_TO_RIGHT: for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr ) { eCellType = pDoc->GetCellType(nStartCol, nRowItr, nTab); // We need this optimization only for EditTextObject source cells if (eCellType != CELLTYPE_EDIT) continue; const std::vector* pRanges = pWin->GetAutoSpellData(nStartCol, nRowItr); if ( !pRanges ) continue; for ( SCCOL nColItr = nStartCol + 1; nColItr <= nEndCol; ++nColItr ) pWin->SetAutoSpellData(nColItr, nRowItr, pRanges); } break; case FILL_TO_LEFT: for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr ) { eCellType = pDoc->GetCellType(nEndCol, nRowItr, nTab); // We need this optimization only for EditTextObject source cells if (eCellType != CELLTYPE_EDIT) continue; const std::vector* pRanges = pWin->GetAutoSpellData(nEndCol, nRowItr); if ( !pRanges ) continue; for ( SCCOL nColItr = nEndCol - 1; nColItr >= nStartCol; --nColItr ) pWin->SetAutoSpellData(nColItr, nRowItr, pRanges); } break; } return; } typedef const std::vector* MisspellRangesType; SCROW nRowRepeatSize = nEndRow - nStartRow + 1; SCCOL nColRepeatSize = nEndCol - nStartCol + 1; SCROW nTillRow = 0; SCCOL nTillCol = 0; std::vector> aSourceSpellRanges(nRowRepeatSize, std::vector(nColRepeatSize, nullptr)); for ( SCROW nRowIdx = 0; nRowIdx < nRowRepeatSize; ++nRowIdx ) { for ( SCCOL nColIdx = 0; nColIdx < nColRepeatSize; ++nColIdx ) { eCellType = pDoc->GetCellType(nStartCol + nColIdx, nStartRow + nRowIdx, nTab); // We need this optimization only for EditTextObject source cells if (eCellType != CELLTYPE_EDIT) continue; aSourceSpellRanges[nRowIdx][nColIdx] = pWin->GetAutoSpellData( nStartCol + nColIdx, nStartRow + nRowIdx ); } } switch( eDir ) { case FILL_TO_BOTTOM: nTillRow = nEndRow + nCount; for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr ) { for ( SCROW nRowItr = nEndRow + 1; nRowItr <= nTillRow; ++nRowItr ) { size_t nSourceRowIdx = ( nRowItr - nEndRow - 1 ) % nRowRepeatSize; MisspellRangesType pRanges = aSourceSpellRanges[nSourceRowIdx][nColItr - nStartCol]; if ( !pRanges ) continue; pWin->SetAutoSpellData(nColItr, nRowItr, pRanges); } } break; case FILL_TO_TOP: nTillRow = nStartRow - nCount; for ( SCCOL nColItr = nStartCol; nColItr <= nEndCol; ++nColItr ) { for ( SCROW nRowItr = nStartRow - 1; nRowItr >= nTillRow; --nRowItr ) { size_t nSourceRowIdx = nRowRepeatSize - 1 - ( ( nStartRow - 1 - nRowItr ) % nRowRepeatSize ); MisspellRangesType pRanges = aSourceSpellRanges[nSourceRowIdx][nColItr - nStartCol]; if ( !pRanges ) continue; pWin->SetAutoSpellData(nColItr, nRowItr, pRanges); } } break; case FILL_TO_RIGHT: nTillCol = nEndCol + nCount; for ( SCCOL nColItr = nEndCol + 1; nColItr <= nTillCol; ++nColItr ) { size_t nSourceColIdx = ( nColItr - nEndCol - 1 ) % nColRepeatSize; for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr ) { MisspellRangesType pRanges = aSourceSpellRanges[nRowItr - nStartRow][nSourceColIdx]; if ( !pRanges ) continue; pWin->SetAutoSpellData(nColItr, nRowItr, pRanges); } } break; case FILL_TO_LEFT: nTillCol = nStartCol - nCount; for ( SCCOL nColItr = nStartCol - 1; nColItr >= nTillCol; --nColItr ) { size_t nSourceColIdx = nColRepeatSize - 1 - ( ( nStartCol - 1 - nColItr ) % nColRepeatSize ); for ( SCROW nRowItr = nStartRow; nRowItr <= nEndRow; ++nRowItr ) { MisspellRangesType pRanges = aSourceSpellRanges[nRowItr - nStartRow][nSourceColIdx]; if ( !pRanges ) continue; pWin->SetAutoSpellData(nColItr, nRowItr, pRanges); } } break; } } else pWin->ResetAutoSpellForContentChange(); } void ScViewFunc::FillTab( InsertDeleteFlags nFlags, ScPasteFunc nFunction, bool bSkipEmpty, bool bAsLink ) { //! allow source sheet to be protected ScEditableTester aTester( this ); if (!aTester.IsEditable()) { ErrorMessage(aTester.GetMessageId()); return; } ScDocShell* pDocSh = GetViewData().GetDocShell(); ScDocument& rDoc = pDocSh->GetDocument(); ScMarkData& rMark = GetViewData().GetMarkData(); SCTAB nTab = GetViewData().GetTabNo(); bool bUndo(rDoc.IsUndoEnabled()); ScRange aMarkRange; rMark.MarkToSimple(); bool bMulti = rMark.IsMultiMarked(); if (bMulti) aMarkRange = rMark.GetMultiMarkArea(); else if (rMark.IsMarked()) aMarkRange = rMark.GetMarkArea(); else aMarkRange = ScRange( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab ); ScDocumentUniquePtr pUndoDoc; if (bUndo) { pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); pUndoDoc->InitUndo( rDoc, nTab, nTab ); for (const SCTAB& i : rMark) if (i != nTab ) { pUndoDoc->AddUndoTab( i, i ); aMarkRange.aStart.SetTab( i ); aMarkRange.aEnd.SetTab( i ); rDoc.CopyToDocument( aMarkRange, InsertDeleteFlags::ALL, bMulti, *pUndoDoc ); } } if (bMulti) rDoc.FillTabMarked( nTab, rMark, nFlags, nFunction, bSkipEmpty, bAsLink ); else { aMarkRange.aStart.SetTab( nTab ); aMarkRange.aEnd.SetTab( nTab ); rDoc.FillTab( aMarkRange, rMark, nFlags, nFunction, bSkipEmpty, bAsLink ); } if (bUndo) { //! for ChangeTrack not until the end pDocSh->GetUndoManager()->AddUndoAction( std::make_unique( pDocSh, rMark, aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), nTab, aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), nTab, std::move(pUndoDoc), bMulti, nTab, nFlags, nFunction, bSkipEmpty, bAsLink ) ); } pDocSh->PostPaintGridAll(); pDocSh->PostDataChanged(); } /** Downward fill of selected cell(s) by double-clicking cross-hair cursor Either, extends a current selection if non-empty cells exist immediately below the selection, overwriting cells below the selection up to the minimum row of already filled cells. Or, extends a current selection down to the last non-empty cell of an adjacent column when the lower-right corner of the selection is double-clicked. It uses a left-adjoining non-empty column as a guide if such is available, otherwise a right-adjoining non-empty column is used. @return No return value @see #i12313# */ void ScViewFunc::FillCrossDblClick() { ScRange aRange; GetViewData().GetSimpleArea( aRange ); aRange.PutInOrder(); SCTAB nTab = GetViewData().GetCurPos().Tab(); SCCOL nStartX = aRange.aStart.Col(); SCROW nStartY = aRange.aStart.Row(); SCCOL nEndX = aRange.aEnd.Col(); SCROW nEndY = aRange.aEnd.Row(); ScDocument& rDoc = GetViewData().GetDocument(); if (nEndY >= rDoc.MaxRow()) // Nothing to fill. return; // Make sure the selection is not empty if ( rDoc.IsBlockEmpty( nStartX, nStartY, nEndX, nEndY, nTab ) ) return; // If there is data in all columns immediately below the selection then // switch to overwriting fill. SCROW nOverWriteEndRow = rDoc.MaxRow(); for (SCCOL nCol = nStartX; nCol <= nEndX; ++nCol) { if (rDoc.HasData( nCol, nEndY + 1, nTab)) { // Determine the shortest data column to end the fill. SCROW nY = nEndY + 1; // FindAreaPos() returns the start row of the next data block if // the current row is the last row of a data block and an empty // cell follows. Somewhat unexpected behaviour... // So check beforehand if there is one non-empty cell following. if (rDoc.HasData( nCol, nY + 1, nTab)) { rDoc.FindAreaPos( nCol, nY, nTab, SC_MOVE_DOWN); if (nOverWriteEndRow > nY) nOverWriteEndRow = nY; } else { nOverWriteEndRow = nY; } } else { nOverWriteEndRow = 0; break; // for } } if (nOverWriteEndRow > nEndY) { FillAuto( FILL_TO_BOTTOM, nStartX, nStartY, nEndX, nEndY, nOverWriteEndRow - nEndY); return; } // Non-overwriting fill follows. const bool bDataLeft = (nStartX > 0); if (!bDataLeft && nEndX >= rDoc.MaxCol()) // Absolutely no data left or right of selection. return; // Check that there is // 1) data immediately left (preferred) or right of start (row) of selection // 2) data there below // 3) no data immediately below selection SCCOL nMovX = (bDataLeft ? nStartX - 1 : nEndX + 1); SCROW nMovY = nStartY; bool bDataFound = (rDoc.HasData( nMovX, nStartY, nTab) && rDoc.HasData( nMovX, nStartY + 1, nTab)); if (!bDataFound && bDataLeft && nEndX < rDoc.MaxCol()) { nMovX = nEndX + 1; // check right bDataFound = (rDoc.HasData( nMovX, nStartY, nTab) && rDoc.HasData( nMovX, nStartY + 1, nTab)); } if (!(bDataFound && rDoc.IsEmptyData( nStartX, nEndY + 1, nEndX, nEndY + 1, nTab ))) return; // Get end of data left or right. rDoc.FindAreaPos( nMovX, nMovY, nTab, SC_MOVE_DOWN); // Find minimum end row of below empty area and data right. for (SCCOL nX = nStartX; nX <= nEndX; ++nX) { SCROW nY = nEndY + 1; // Get next row with data in this column. rDoc.FindAreaPos( nX, nY, nTab, SC_MOVE_DOWN); if (nMovY == rDoc.MaxRow() && nY == rDoc.MaxRow()) { // FindAreaPos() returns MAXROW also if there is no data at all // from the start, so check if that contains data if the nearby // (left or right) data ends there and increment if no data // here, pretending the next data would be thereafter so nMovY // will not be decremented. if (!rDoc.HasData( nX, nY, nTab)) ++nY; } if (nMovY > nY - 1) nMovY = nY - 1; } if (nMovY > nEndY) { FillAuto( FILL_TO_BOTTOM, nStartX, nStartY, nEndX, nEndY, nMovY - nEndY); } } void ScViewFunc::ConvertFormulaToValue() { ScRange aRange; GetViewData().GetSimpleArea(aRange); aRange.PutInOrder(); ScDocShell* pDocSh = GetViewData().GetDocShell(); pDocSh->GetDocFunc().ConvertFormulaToValue(aRange, true); // tdf#131326 - invalidate cell slots and update input line with new content CellContentChanged(); pDocSh->PostPaint(aRange, PaintPartFlags::Grid); } void ScViewFunc::TransliterateText( TransliterationFlags nType ) { ScMarkData aFuncMark = GetViewData().GetMarkData(); if ( !aFuncMark.IsMarked() && !aFuncMark.IsMultiMarked() ) { // no selection -> use cursor position ScAddress aCursor( GetViewData().GetCurX(), GetViewData().GetCurY(), GetViewData().GetTabNo() ); aFuncMark.SetMarkArea( ScRange( aCursor ) ); } bool bSuccess = GetViewData().GetDocShell()->GetDocFunc(). TransliterateText( aFuncMark, nType, false ); if (bSuccess) { GetViewData().GetViewShell()->UpdateInputHandler(); } } // AutoFormat ScAutoFormatData* ScViewFunc::CreateAutoFormatData() { ScAutoFormatData* pData = nullptr; SCCOL nStartCol; SCROW nStartRow; SCTAB nStartTab; SCCOL nEndCol; SCROW nEndRow; SCTAB nEndTab; if (GetViewData().GetSimpleArea(nStartCol,nStartRow,nStartTab,nEndCol,nEndRow,nEndTab) == SC_MARK_SIMPLE) { if ( nEndCol-nStartCol >= 3 && nEndRow-nStartRow >= 3 ) { ScDocument& rDoc = GetViewData().GetDocument(); pData = new ScAutoFormatData; rDoc.GetAutoFormatData( nStartTab, nStartCol,nStartRow,nEndCol,nEndRow, *pData ); } } return pData; } void ScViewFunc::AutoFormat( sal_uInt16 nFormatNo ) { ScRange aRange; if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE) { ScDocShell* pDocSh = GetViewData().GetDocShell(); ScMarkData& rMark = GetViewData().GetMarkData(); bool bSuccess = pDocSh->GetDocFunc().AutoFormat( aRange, &rMark, nFormatNo, false ); if (bSuccess) pDocSh->UpdateOle(GetViewData()); } else ErrorMessage(STR_NOMULTISELECT); } // Search & Replace bool ScViewFunc::SearchAndReplace( const SvxSearchItem* pSearchItem, bool bAddUndo, bool bIsApi ) { SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Empty); ScDocShell* pDocSh = GetViewData().GetDocShell(); ScDocument& rDoc = pDocSh->GetDocument(); ScMarkData& rMark = GetViewData().GetMarkData(); if (bAddUndo && !rDoc.IsUndoEnabled()) bAddUndo = false; if ( !rMark.IsMarked() && !rMark.IsMultiMarked() && (pSearchItem->HasStartPoint()) ) { // No selection -> but we have a start point (top left corner of the // current view), start searching from there, not from the current // cursor position. SCCOL nPosX; SCROW nPosY; int nPixelX = pSearchItem->GetStartPointX() * GetViewData().GetPPTX(); int nPixelY = pSearchItem->GetStartPointY() * GetViewData().GetPPTY(); GetViewData().GetPosFromPixel(nPixelX, nPixelY, GetViewData().GetActivePart(), nPosX, nPosY); AlignToCursor( nPosX, nPosY, SC_FOLLOW_JUMP ); SetCursor( nPosX, nPosY, true ); } SCCOL nCol, nOldCol; SCROW nRow, nOldRow; SCTAB nTab, nOldTab; nCol = nOldCol = GetViewData().GetCurX(); nRow = nOldRow = GetViewData().GetCurY(); nTab = nOldTab = GetViewData().GetTabNo(); SvxSearchCmd nCommand = pSearchItem->GetCommand(); bool bAllTables = pSearchItem->IsAllTables(); std::set aOldSelectedTables; SCTAB nLastTab = rDoc.GetTableCount() - 1; SCTAB nStartTab, nEndTab; if ( bAllTables ) { nStartTab = 0; nEndTab = nLastTab; std::set aTmp(rMark.begin(), rMark.end()); aOldSelectedTables.swap(aTmp); } else { //! at least one is always selected nStartTab = rMark.GetFirstSelected(); nEndTab = rMark.GetLastSelected(); } if ( nCommand == SvxSearchCmd::FIND || nCommand == SvxSearchCmd::FIND_ALL) bAddUndo = false; //! account for bAttrib during Undo !!! ScDocumentUniquePtr pUndoDoc; std::unique_ptr pUndoMark; OUString aUndoStr; if (bAddUndo) { pUndoMark.reset(new ScMarkData(rMark)); // Mark is being modified if ( nCommand == SvxSearchCmd::REPLACE_ALL ) { pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO)); pUndoDoc->InitUndo( rDoc, nStartTab, nEndTab ); } } if ( bAllTables ) { //! select all, after pUndoMark has been created for ( SCTAB j = nStartTab; j <= nEndTab; j++ ) { rMark.SelectTable( j, true ); } } DoneBlockMode(true); // don't delete mark InitOwnBlockMode( ScRange( nCol, nRow, nStartTab, nCol, nRow, nEndTab)); // If search starts at the beginning don't ask again whether it shall start at the beginning bool bFirst = true; if ( nCol == 0 && nRow == 0 && nTab == nStartTab && !pSearchItem->GetBackward() ) bFirst = false; bool bFound = false; while (true) { GetFrameWin()->EnterWait(); ScRangeList aMatchedRanges; bool bMatchedRangesWereClamped = false; if (rDoc.SearchAndReplace(*pSearchItem, nCol, nRow, nTab, rMark, aMatchedRanges, aUndoStr, pUndoDoc.get(), bMatchedRangesWereClamped)) { bFound = true; if (bAddUndo) { GetViewData().GetDocShell()->GetUndoManager()->AddUndoAction( std::make_unique( GetViewData().GetDocShell(), *pUndoMark, nCol, nRow, nTab, aUndoStr, std::move(pUndoDoc), pSearchItem ) ); } if (nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL) { SfxViewFrame* pViewFrm = SfxViewFrame::Current(); bool bShow = GetViewData().GetViewShell()->GetViewData().GetOptions().GetOption( VOPT_SUMMARY ); if (bShow && pViewFrm && !comphelper::LibreOfficeKit::isActive()) { pViewFrm->ShowChildWindow(sc::SearchResultsDlgWrapper::GetChildWindowId()); SfxChildWindow* pWnd = pViewFrm->GetChildWindow(sc::SearchResultsDlgWrapper::GetChildWindowId()); if (pWnd) { sc::SearchResultsDlg* pDlg = static_cast(pWnd->GetController().get()); if (pDlg) { const bool bCellNotes = (pSearchItem->GetCellType() == SvxSearchCellType::NOTE); // ScCellIterator iterates over cells with content, // for empty cells iterate over match positions. const bool bEmptyCells = (!bCellNotes && ((nCommand == SvxSearchCmd::FIND_ALL && ScDocument::IsEmptyCellSearch(*pSearchItem)) || (nCommand == SvxSearchCmd::REPLACE_ALL && pSearchItem->GetReplaceString().isEmpty()))); pDlg->FillResults(rDoc, aMatchedRanges, bCellNotes, bEmptyCells, bMatchedRangesWereClamped); } } } rMark.ResetMark(); for (size_t i = 0, n = aMatchedRanges.size(); i < n; ++i) { const ScRange& r = aMatchedRanges[i]; if (r.aStart.Tab() == nTab) rMark.SetMultiMarkArea(r); } } break; // break 'while (TRUE)' } else if ( bFirst && (nCommand == SvxSearchCmd::FIND || nCommand == SvxSearchCmd::REPLACE) ) { bFirst = false; GetFrameWin()->LeaveWait(); if (!bIsApi) { if ( nStartTab == nEndTab ) SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::EndSheet); else SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::End); rDoc.GetSearchAndReplaceStart( *pSearchItem, nCol, nRow ); if (pSearchItem->GetBackward()) nTab = nEndTab; else nTab = nStartTab; } else { break; // break 'while (TRUE)' } } else // nothing found { if ( nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL ) { pDocSh->PostPaintGridAll(); // Mark } GetFrameWin()->LeaveWait(); if (!bIsApi) { GetViewData().GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, pSearchItem->GetSearchString().toUtf8()); SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound); } break; // break 'while (TRUE)' } } // of while true if (!aOldSelectedTables.empty()) { // restore originally selected table for (SCTAB i = 0; i <= nEndTab; ++i) rMark.SelectTable(i, false); for (const auto& rTab : aOldSelectedTables) rMark.SelectTable(rTab, true); if ( bFound ) { // if a table is selected as a "match" it remains selected. rMark.SelectTable( nTab, true ); // It's a swap if only one table was selected before //! otherwise now one table more might be selected if ( aOldSelectedTables.size() == 1 && nTab != nOldTab ) rMark.SelectTable( nOldTab, false ); } } // Avoid LOK selection notifications before we have all the results. GetViewData().GetViewShell()->setTiledSearching(true); MarkDataChanged(); GetViewData().GetViewShell()->setTiledSearching(false); if ( bFound ) { if ( nTab != GetViewData().GetTabNo() ) SetTabNo( nTab ); // if nothing is marked, DoneBlockMode, then marking can start // directly from this place via Shift-Cursor if (!rMark.IsMarked() && !rMark.IsMultiMarked()) DoneBlockMode(true); AlignToCursor( nCol, nRow, SC_FOLLOW_JUMP ); SetCursor( nCol, nRow, true ); if (comphelper::LibreOfficeKit::isActive()) { Point aCurPos = GetViewData().GetScrPos(nCol, nRow, GetViewData().GetActivePart()); // just update the cell selection ScGridWindow* pGridWindow = GetViewData().GetActiveWin(); // Don't move cell selection handles for find-all: selection of all but the first result would be lost. if (pGridWindow && nCommand == SvxSearchCmd::FIND) { // move the cell selection handles pGridWindow->SetCellSelectionPixel(LOK_SETTEXTSELECTION_RESET, aCurPos.X(), aCurPos.Y()); pGridWindow->SetCellSelectionPixel(LOK_SETTEXTSELECTION_START, aCurPos.X(), aCurPos.Y()); pGridWindow->SetCellSelectionPixel(LOK_SETTEXTSELECTION_END, aCurPos.X(), aCurPos.Y()); } if (pGridWindow) { std::vector aLogicRects; pGridWindow->GetCellSelection(aLogicRects); boost::property_tree::ptree aTree; aTree.put("searchString", pSearchItem->GetSearchString().toUtf8().getStr()); aTree.put("highlightAll", nCommand == SvxSearchCmd::FIND_ALL); boost::property_tree::ptree aSelections; for (const tools::Rectangle& rLogicRect : aLogicRects) { boost::property_tree::ptree aSelection; aSelection.put("part", OString::number(nTab).getStr()); aSelection.put("rectangles", rLogicRect.toString().getStr()); aSelections.push_back(std::make_pair("", aSelection)); } aTree.add_child("searchResultSelection", aSelections); std::stringstream aStream; boost::property_tree::write_json(aStream, aTree); OString aPayload( aStream.str() ); SfxViewShell* pViewShell = GetViewData().GetViewShell(); pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload); // Trigger LOK_CALLBACK_TEXT_SELECTION now. MarkDataChanged(); } } if ( nCommand == SvxSearchCmd::REPLACE || nCommand == SvxSearchCmd::REPLACE_ALL ) { if ( nCommand == SvxSearchCmd::REPLACE ) { pDocSh->PostPaint( nCol,nRow,nTab, nCol,nRow,nTab, PaintPartFlags::Grid ); // jump to next cell if we replaced everything in the cell // where the cursor was positioned (but avoid switching tabs) if ( nCol == nOldCol && nRow == nOldRow && nTab == nOldTab ) { SvxSearchItem aSearchItem = ScGlobal::GetSearchItem(); aSearchItem.SetCommand(SvxSearchCmd::FIND); aSearchItem.SetWhich(SID_SEARCH_ITEM); ScRangeList aMatchedRanges; bool bMatchedRangesWereClamped; ScTable::UpdateSearchItemAddressForReplace( aSearchItem, nCol, nRow ); if ( rDoc.SearchAndReplace( aSearchItem, nCol, nRow, nTab, rMark, aMatchedRanges, aUndoStr, nullptr, bMatchedRangesWereClamped ) && ( nTab == nOldTab ) && ( nCol != nOldCol || nRow != nOldRow ) ) { AlignToCursor(nCol, nRow, SC_FOLLOW_JUMP); SetCursor( nCol, nRow, true ); } } } else pDocSh->PostPaintGridAll(); pDocSh->SetDocumentModified(); } else if ( nCommand == SvxSearchCmd::FIND_ALL ) pDocSh->PostPaintGridAll(); // mark GetFrameWin()->LeaveWait(); } return bFound; } // Goal Seek void ScViewFunc::Solve( const ScSolveParam& rParam ) { ScDocument& rDoc = GetViewData().GetDocument(); SCCOL nDestCol = rParam.aRefVariableCell.Col(); SCROW nDestRow = rParam.aRefVariableCell.Row(); SCTAB nDestTab = rParam.aRefVariableCell.Tab(); ScEditableTester aTester( rDoc, nDestTab, nDestCol,nDestRow, nDestCol,nDestRow ); if (!aTester.IsEditable()) { ErrorMessage(aTester.GetMessageId()); return; } OUString aTargetValStr; if ( rParam.pStrTargetVal ) aTargetValStr = *rParam.pStrTargetVal; OUString aMsgStr; OUString aResStr; double nSolveResult; GetFrameWin()->EnterWait(); bool bExact = rDoc.Solver( rParam.aRefFormulaCell.Col(), rParam.aRefFormulaCell.Row(), rParam.aRefFormulaCell.Tab(), nDestCol, nDestRow, nDestTab, aTargetValStr, nSolveResult ); GetFrameWin()->LeaveWait(); SvNumberFormatter* pFormatter = rDoc.GetFormatTable(); sal_uLong nFormat = 0; const ScPatternAttr* pPattern = rDoc.GetPattern( nDestCol, nDestRow, nDestTab ); if ( pPattern ) nFormat = pPattern->GetNumberFormat( pFormatter ); const Color* p; pFormatter->GetOutputString( nSolveResult, nFormat, aResStr, &p ); if ( bExact ) { aMsgStr += ScResId( STR_MSSG_SOLVE_0 ) + aResStr + ScResId( STR_MSSG_SOLVE_1 ); } else { aMsgStr = ScResId( STR_MSSG_SOLVE_2 ) + ScResId( STR_MSSG_SOLVE_3 ) + aResStr + ScResId( STR_MSSG_SOLVE_4 ); } std::unique_ptr xBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(), VclMessageType::Question, VclButtonsType::YesNo, aMsgStr)); xBox->set_title(ScResId(STR_MSSG_DOSUBTOTALS_0)); xBox->set_default_response(RET_NO); if (xBox->run() == RET_YES) EnterValue( nDestCol, nDestRow, nDestTab, nSolveResult ); GetViewData().GetViewShell()->UpdateInputHandler( true ); } // multi operation void ScViewFunc::TabOp( const ScTabOpParam& rParam, bool bRecord ) { ScRange aRange; if (GetViewData().GetSimpleArea(aRange) == SC_MARK_SIMPLE) { ScDocShell* pDocSh = GetViewData().GetDocShell(); ScMarkData& rMark = GetViewData().GetMarkData(); pDocSh->GetDocFunc().TabOp( aRange, &rMark, rParam, bRecord, false ); } else ErrorMessage(STR_NOMULTISELECT); } void ScViewFunc::MakeScenario( const OUString& rName, const OUString& rComment, const Color& rColor, ScScenarioFlags nFlags ) { ScDocShell* pDocSh = GetViewData().GetDocShell(); ScMarkData& rMark = GetViewData().GetMarkData(); SCTAB nTab = GetViewData().GetTabNo(); SCTAB nNewTab = pDocSh->MakeScenario( nTab, rName, rComment, rColor, nFlags, rMark ); if (nFlags & ScScenarioFlags::CopyAll) SetTabNo( nNewTab, true ); // ScScenarioFlags::CopyAll -> visible else { SfxBindings& rBindings = GetViewData().GetBindings(); rBindings.Invalidate( SID_STATUS_DOCPOS ); // Statusbar rBindings.Invalidate( SID_ROWCOL_SELCOUNT ); // Statusbar rBindings.Invalidate( SID_TABLES_COUNT ); rBindings.Invalidate( SID_SELECT_SCENARIO ); rBindings.Invalidate( FID_TABLE_SHOW ); } } void ScViewFunc::ExtendScenario() { ScEditableTester aTester( this ); if (!aTester.IsEditable()) { ErrorMessage(aTester.GetMessageId()); return; } // Undo: apply attributes ScDocument& rDoc = GetViewData().GetDocument(); ScPatternAttr aPattern( rDoc.GetPool() ); aPattern.GetItemSet().Put( ScMergeFlagAttr( ScMF::Scenario ) ); aPattern.GetItemSet().Put( ScProtectionAttr( true ) ); ApplySelectionPattern(aPattern); } void ScViewFunc::UseScenario( const OUString& rName ) { ScDocShell* pDocSh = GetViewData().GetDocShell(); SCTAB nTab = GetViewData().GetTabNo(); DoneBlockMode(); InitOwnBlockMode( ScRange( GetViewData().GetCurX(), GetViewData().GetCurY(), nTab)); pDocSh->UseScenario( nTab, rName ); } // Insert table bool ScViewFunc::InsertTable( const OUString& rName, SCTAB nTab, bool bRecord ) { // Order Table/Name is inverted for DocFunc bool bSuccess = GetViewData().GetDocShell()->GetDocFunc(). InsertTable( nTab, rName, bRecord, false ); if (bSuccess) SetTabNo( nTab, true ); return bSuccess; } // Insert tables void ScViewFunc::InsertTables(std::vector& aNames, SCTAB nTab, SCTAB nCount, bool bRecord ) { ScDocShell* pDocSh = GetViewData().GetDocShell(); ScDocument& rDoc = pDocSh->GetDocument(); if (bRecord && !rDoc.IsUndoEnabled()) bRecord = false; weld::WaitObject aWait(GetViewData().GetDialogParent()); if (bRecord) { rDoc.BeginDrawUndo(); // InsertTab creates a SdrUndoNewPage } bool bFlag=false; if(aNames.empty()) { rDoc.CreateValidTabNames(aNames, nCount); } if (rDoc.InsertTabs(nTab, aNames)) { pDocSh->Broadcast( ScTablesHint( SC_TABS_INSERTED, nTab, nCount ) ); bFlag = true; } if (!bFlag) return; if (bRecord) pDocSh->GetUndoManager()->AddUndoAction( std::make_unique( pDocSh, nTab, std::move(aNames))); // Update views SetTabNo( nTab, true ); pDocSh->PostPaintExtras(); pDocSh->SetDocumentModified(); SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); } bool ScViewFunc::AppendTable( const OUString& rName, bool bRecord ) { ScDocShell* pDocSh = GetViewData().GetDocShell(); ScDocument& rDoc = pDocSh->GetDocument(); if (bRecord && !rDoc.IsUndoEnabled()) bRecord = false; weld::WaitObject aWait(GetViewData().GetDialogParent()); if (bRecord) rDoc.BeginDrawUndo(); // InsertTab creates a SdrUndoNewPage if (rDoc.InsertTab( SC_TAB_APPEND, rName )) { SCTAB nTab = rDoc.GetTableCount()-1; if (bRecord) pDocSh->GetUndoManager()->AddUndoAction( std::make_unique( pDocSh, nTab, true, rName)); GetViewData().InsertTab( nTab ); SetTabNo( nTab, true ); pDocSh->PostPaintExtras(); pDocSh->SetDocumentModified(); SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); return true; } else { return false; } } void ScViewFunc::DeleteTable( SCTAB nTab, bool bRecord ) { ScDocShell* pDocSh = GetViewData().GetDocShell(); ScDocument& rDoc = pDocSh->GetDocument(); bool bSuccess = pDocSh->GetDocFunc().DeleteTable( nTab, bRecord ); if (bSuccess) { SCTAB nNewTab = nTab; if ( nNewTab >= rDoc.GetTableCount() ) --nNewTab; SetTabNo( nNewTab, true ); } } //only use this method for undo for now, all sheets must be connected //this method doesn't support undo for now, merge it when it with the other method later void ScViewFunc::DeleteTables( const SCTAB nTab, SCTAB nSheets ) { ScDocShell* pDocSh = GetViewData().GetDocShell(); ScDocument& rDoc = pDocSh->GetDocument(); bool bVbaEnabled = rDoc.IsInVBAMode(); SCTAB nNewTab = nTab; weld::WaitObject aWait(GetViewData().GetDialogParent()); while ( nNewTab > 0 && !rDoc.IsVisible( nNewTab ) ) --nNewTab; if (!rDoc.DeleteTabs(nTab, nSheets)) return; if( bVbaEnabled ) { for (SCTAB aTab = 0; aTab < nSheets; ++aTab) { OUString sCodeName; bool bHasCodeName = rDoc.GetCodeName( nTab + aTab, sCodeName ); if ( bHasCodeName ) VBA_DeleteModule( *pDocSh, sCodeName ); } } pDocSh->Broadcast( ScTablesHint( SC_TABS_DELETED, nTab, nSheets ) ); if ( nNewTab >= rDoc.GetTableCount() ) nNewTab = rDoc.GetTableCount() - 1; SetTabNo( nNewTab, true ); pDocSh->PostPaintExtras(); pDocSh->SetDocumentModified(); SfxApplication* pSfxApp = SfxGetpApp(); // Navigator pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) ); pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); } bool ScViewFunc::DeleteTables(const vector &TheTabs, bool bRecord ) { ScDocShell* pDocSh = GetViewData().GetDocShell(); ScDocument& rDoc = pDocSh->GetDocument(); bool bVbaEnabled = rDoc.IsInVBAMode(); SCTAB nNewTab = TheTabs.front(); weld::WaitObject aWait(GetViewData().GetDialogParent()); if (bRecord && !rDoc.IsUndoEnabled()) bRecord = false; if ( bVbaEnabled ) bRecord = false; while ( nNewTab > 0 && !rDoc.IsVisible( nNewTab ) ) --nNewTab; bool bWasLinked = false; ScDocumentUniquePtr pUndoDoc; std::unique_ptr pUndoData; if (bRecord) { pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); SCTAB nCount = rDoc.GetTableCount(); OUString aOldName; for(size_t i=0; iInitUndo( rDoc, nTab,nTab, true,true ); // incl. column/fow flags else pUndoDoc->AddUndoTab( nTab,nTab, true,true ); // incl. column/fow flags rDoc.CopyToDocument(0,0,nTab, rDoc.MaxCol(), rDoc.MaxRow(), nTab, InsertDeleteFlags::ALL,false, *pUndoDoc ); rDoc.GetName( nTab, aOldName ); pUndoDoc->RenameTab( nTab, aOldName ); if (rDoc.IsLinked(nTab)) { bWasLinked = true; pUndoDoc->SetLink( nTab, rDoc.GetLinkMode(nTab), rDoc.GetLinkDoc(nTab), rDoc.GetLinkFlt(nTab), rDoc.GetLinkOpt(nTab), rDoc.GetLinkTab(nTab), rDoc.GetLinkRefreshDelay(nTab) ); } if ( rDoc.IsScenario(nTab) ) { pUndoDoc->SetScenario( nTab, true ); OUString aComment; Color aColor; ScScenarioFlags nScenFlags; rDoc.GetScenarioData( nTab, aComment, aColor, nScenFlags ); pUndoDoc->SetScenarioData( nTab, aComment, aColor, nScenFlags ); bool bActive = rDoc.IsActiveScenario( nTab ); pUndoDoc->SetActiveScenario( nTab, bActive ); } pUndoDoc->SetVisible( nTab, rDoc.IsVisible( nTab ) ); pUndoDoc->SetTabBgColor( nTab, rDoc.GetTabBgColor(nTab) ); auto pSheetEvents = rDoc.GetSheetEvents( nTab ); pUndoDoc->SetSheetEvents( nTab, std::unique_ptr(pSheetEvents ? new ScSheetEvents(*pSheetEvents) : nullptr) ); pUndoDoc->SetLayoutRTL( nTab, rDoc.IsLayoutRTL( nTab ) ); if ( rDoc.IsTabProtected( nTab ) ) pUndoDoc->SetTabProtection(nTab, rDoc.GetTabProtection(nTab)); // Drawing-Layer is responsible for its Undo !!! // pUndoDoc->TransferDrawPage(rDoc, nTab,nTab); } pUndoDoc->AddUndoTab( 0, nCount-1 ); // all Tabs for references rDoc.BeginDrawUndo(); // DeleteTab creates a SdrUndoDelPage pUndoData.reset(new ScRefUndoData( &rDoc )); } bool bDelDone = false; for(int i=TheTabs.size()-1; i>=0; --i) { OUString sCodeName; bool bHasCodeName = rDoc.GetCodeName( TheTabs[i], sCodeName ); if (rDoc.DeleteTab(TheTabs[i])) { bDelDone = true; if( bVbaEnabled && bHasCodeName ) { VBA_DeleteModule( *pDocSh, sCodeName ); } pDocSh->Broadcast( ScTablesHint( SC_TAB_DELETED, TheTabs[i] ) ); } } if (bRecord) { pDocSh->GetUndoManager()->AddUndoAction( std::make_unique( GetViewData().GetDocShell(), TheTabs, std::move(pUndoDoc), std::move(pUndoData) )); } if (bDelDone) { if ( nNewTab >= rDoc.GetTableCount() ) nNewTab = rDoc.GetTableCount() - 1; SetTabNo( nNewTab, true ); if (bWasLinked) { pDocSh->UpdateLinks(); // update Link-Manager GetViewData().GetBindings().Invalidate(SID_LINKS); } pDocSh->PostPaintExtras(); pDocSh->SetDocumentModified(); SfxApplication* pSfxApp = SfxGetpApp(); // Navigator pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) ); pSfxApp->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) ); pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreaLinksChanged ) ); } return bDelDone; } bool ScViewFunc::RenameTable( const OUString& rName, SCTAB nTab ) { // order Table/Name is inverted for DocFunc bool bSuccess = GetViewData().GetDocShell()->GetDocFunc(). RenameTable( nTab, rName, true, false ); if (bSuccess) { // the table name might be part of a formula GetViewData().GetViewShell()->UpdateInputHandler(); } return bSuccess; } bool ScViewFunc::SetTabBgColor( const Color& rColor, SCTAB nTab ) { bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().SetTabBgColor( nTab, rColor, true, false ); if (bSuccess) { GetViewData().GetViewShell()->UpdateInputHandler(); } return bSuccess; } bool ScViewFunc::SetTabBgColor( ScUndoTabColorInfo::List& rUndoSetTabBgColorInfoList ) { bool bSuccess = GetViewData().GetDocShell()->GetDocFunc().SetTabBgColor( rUndoSetTabBgColorInfoList, false ); if (bSuccess) { GetViewData().GetViewShell()->UpdateInputHandler(); } return bSuccess; } void ScViewFunc::InsertAreaLink( const OUString& rFile, const OUString& rFilter, const OUString& rOptions, const OUString& rSource ) { ScDocShell* pDocSh = GetViewData().GetDocShell(); SCCOL nPosX = GetViewData().GetCurX(); SCROW nPosY = GetViewData().GetCurY(); SCTAB nTab = GetViewData().GetTabNo(); ScAddress aPos( nPosX, nPosY, nTab ); pDocSh->GetDocFunc().InsertAreaLink( rFile, rFilter, rOptions, rSource, aPos, 0/*nRefresh*/, false, false ); } void ScViewFunc::InsertTableLink( const OUString& rFile, const OUString& rFilter, const OUString& rOptions, std::u16string_view rTabName ) { OUString aFilterName = rFilter; OUString aOpt = rOptions; ScDocumentLoader aLoader( rFile, aFilterName, aOpt ); if (aLoader.IsError()) return; ScDocShell* pSrcSh = aLoader.GetDocShell(); ScDocument& rSrcDoc = pSrcSh->GetDocument(); SCTAB nTab = MAXTAB+1; if (rTabName.empty()) // no name given -> first table nTab = 0; else { OUString aTemp; SCTAB nCount = rSrcDoc.GetTableCount(); for (SCTAB i=0; iGetDocument(); ScDocShell* pDocSh = GetViewData().GetDocShell(); ScDocument& rDoc = pDocSh->GetDocument(); bool bUndo(rDoc.IsUndoEnabled()); bool bError = false; bool bRefs = false; bool bName = false; if (rSrcDoc.GetDrawLayer()) pDocSh->MakeDrawLayer(); if (bUndo) rDoc.BeginDrawUndo(); // drawing layer must do its own undo actions SCTAB nInsCount = 0; SCTAB i; for( i=0; iTransferTab( *pSrcShell, nSrcTab, nDestTab1, false, false ); // no insert switch (nErrVal) { case 0: // internal error or full of errors bError = true; break; case 2: bRefs = true; break; case 3: bName = true; break; case 4: bRefs = bName = true; break; } } if (bLink) { sfx2::LinkManager* pLinkManager = rDoc.GetLinkManager(); SfxMedium* pMed = pSrcShell->GetMedium(); OUString aFileName = pMed->GetName(); OUString aFilterName; if (pMed->GetFilter()) aFilterName = pMed->GetFilter()->GetFilterName(); OUString aOptions = ScDocumentLoader::GetOptions(*pMed); bool bWasThere = rDoc.HasLink( aFileName, aFilterName, aOptions ); sal_uLong nRefresh = 0; OUString aTabStr; for (i=0; iSetInCreate( true ); pLinkManager->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, aFileName, &aFilterName ); pLink->Update(); pLink->SetInCreate( false ); SfxBindings& rBindings = GetViewData().GetBindings(); rBindings.Invalidate( SID_LINKS ); } } if (bUndo) { pDocSh->GetUndoManager()->AddUndoAction( std::make_unique( pDocSh, nTab, nCount ) ); } for (i=0; iPostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid | PaintPartFlags::Top | PaintPartFlags::Left | PaintPartFlags::Extras ); SfxApplication* pSfxApp = SfxGetpApp(); pSfxApp->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); pSfxApp->Broadcast( SfxHint( SfxHintId::ScAreasChanged ) ); pDocSh->PostPaintExtras(); pDocSh->PostPaintGridAll(); pDocSh->SetDocumentModified(); if (bRefs) ErrorMessage(STR_ABSREFLOST); if (bName) ErrorMessage(STR_NAMECONFLICT); } // Move/Copy table to another document void ScViewFunc::MoveTable( sal_uInt16 nDestDocNo, SCTAB nDestTab, bool bCopy, const OUString* pNewTabName ) { ScDocument& rDoc = GetViewData().GetDocument(); ScDocShell* pDocShell = GetViewData().GetDocShell(); ScDocShell* pDestShell = nullptr; ScTabViewShell* pDestViewSh = nullptr; bool bUndo (rDoc.IsUndoEnabled()); bool bRename = pNewTabName && !pNewTabName->isEmpty(); bool bNewDoc = (nDestDocNo == SC_DOC_NEW); if ( bNewDoc ) { nDestTab = 0; // firstly insert // execute without SfxCallMode::RECORD, because already contained in move command SfxStringItem aItem( SID_FILE_NAME, "private:factory/" + STRING_SCAPP ); SfxStringItem aTarget( SID_TARGETNAME, "_blank" ); const SfxPoolItemHolder aResult(GetViewData().GetDispatcher().ExecuteList( SID_OPENDOC, SfxCallMode::API|SfxCallMode::SYNCHRON, { &aItem, &aTarget })); if (nullptr != aResult.getItem()) { if ( auto pObjectItem = dynamic_cast(aResult.getItem()) ) pDestShell = dynamic_cast( pObjectItem->GetShell() ); else if ( auto pViewFrameItem = dynamic_cast(aResult.getItem())) { SfxViewFrame* pFrm = pViewFrameItem->GetFrame(); if (pFrm) pDestShell = dynamic_cast( pFrm->GetObjectShell() ); } if (pDestShell) pDestViewSh = pDestShell->GetBestViewShell(); } } else pDestShell = ScDocShell::GetShellByNum( nDestDocNo ); if (!pDestShell) { OSL_FAIL("Destination document not found !!!"); return; } ScMarkData& rMark = GetViewData().GetMarkData(); if (bRename && rMark.GetSelectCount() != 1) { // Custom sheet name is provided, but more than one sheet is selected. // We don't support this scenario at the moment. return; } ScDocument& rDestDoc = pDestShell->GetDocument(); if (&rDestDoc != &rDoc) { if (bNewDoc) { while (rDestDoc.GetTableCount() > 1) rDestDoc.DeleteTab(0); rDestDoc.RenameTab( 0, "______42_____" ); } SCTAB nTabCount = rDoc.GetTableCount(); SCTAB nTabSelCount = rMark.GetSelectCount(); vector TheTabs; for(SCTAB i=0; iEnterWait(); if (rDoc.GetDrawLayer()) pDestShell->MakeDrawLayer(); if (!bNewDoc && bUndo) rDestDoc.BeginDrawUndo(); // drawing layer must do its own undo actions sal_uLong nErrVal =1; if(nDestTab==SC_TAB_APPEND) nDestTab=rDestDoc.GetTableCount(); SCTAB nDestTab1=nDestTab; ScClipParam aParam; for( size_t j=0; j 0 ) { nDestTab1 = nDestTab; for(SCTAB nTab : TheTabs) { nErrVal = pDestShell->TransferTab( *pDocShell, nTab, nDestTab1, false, false ); nDestTab1++; } } if (!bNewDoc && bUndo) { OUString sName; rDestDoc.GetName(nDestTab, sName); pDestShell->GetUndoManager()->AddUndoAction( std::make_unique( pDestShell, nDestTab, static_cast(TheTabs.size()))); } else { pDestShell->GetUndoManager()->Clear(); } GetFrameWin()->LeaveWait(); switch (nErrVal) { case 0: // internal error or full of errors { ErrorMessage(STR_TABINSERT_ERROR); return; } case 2: ErrorMessage(STR_ABSREFLOST); break; case 3: ErrorMessage(STR_NAMECONFLICT); break; case 4: { ErrorMessage(STR_ABSREFLOST); ErrorMessage(STR_NAMECONFLICT); } break; default: break; } if (!bCopy) { if(nTabCount!=nTabSelCount) DeleteTables(TheTabs); // incl. Paint & Undo else ErrorMessage(STR_TABREMOVE_ERROR); } if (bNewDoc) { // ChartListenerCollection must be updated before DeleteTab if ( rDestDoc.IsChartListenerCollectionNeedsUpdate() ) rDestDoc.UpdateChartListenerCollection(); SCTAB nNumTabsInserted = static_cast(TheTabs.size()); pDestShell->Broadcast( ScTablesHint( SC_TABS_INSERTED, 0, nNumTabsInserted ) ); rDestDoc.DeleteTab( nNumTabsInserted ); // old first table pDestShell->Broadcast( ScTablesHint( SC_TAB_DELETED, nNumTabsInserted ) ); if (pDestViewSh) { // Make sure to clear the cached page view after sheet // deletion, which still points to the sdr page belonging to // the deleted sheet. SdrView* pSdrView = pDestViewSh->GetScDrawView(); if (pSdrView) pSdrView->ClearPageView(); pDestViewSh->TabChanged(); // pages on the drawing layer } pDestShell->PostPaint( 0,0,0, rDoc.MaxCol(), rDoc.MaxRow(), MAXTAB, PaintPartFlags::Grid | PaintPartFlags::Top | PaintPartFlags::Left | PaintPartFlags::Extras | PaintPartFlags::Size ); // PaintPartFlags::Size for outline } else { pDestShell->Broadcast( ScTablesHint( SC_TAB_INSERTED, nDestTab ) ); pDestShell->PostPaintExtras(); pDestShell->PostPaintGridAll(); } TheTabs.clear(); pDestShell->SetDocumentModified(); SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); } else { // Move or copy within the same document. SCTAB nTabCount = rDoc.GetTableCount(); unique_ptr< vector > pSrcTabs(new vector); unique_ptr< vector > pDestTabs(new vector); unique_ptr< vector > pTabNames(new vector); unique_ptr< vector > pDestNames; pSrcTabs->reserve(nTabCount); pDestTabs->reserve(nTabCount); pTabNames->reserve(nTabCount); OUString aDestName; for(SCTAB i=0;ipush_back(aTabName); for(SCTAB j=i+1;jpush_back(aTabName); i=j; } else break; } } } if (bCopy && bUndo) rDoc.BeginDrawUndo(); // drawing layer must do its own undo actions rDoc.GetName( nDestTab, aDestName); SCTAB nDestTab1=nDestTab; SCTAB nMovTab=0; for (size_t j = 0, n = pTabNames->size(); j < n; ++j) { nTabCount = rDoc.GetTableCount(); const OUString& rStr = (*pTabNames)[j]; if(!rDoc.GetTable(rStr,nMovTab)) { nMovTab=nTabCount; } if(!rDoc.GetTable(aDestName,nDestTab1)) { nDestTab1=nTabCount; } pDocShell->MoveTable( nMovTab, nDestTab1, bCopy, false ); // Undo is here // tdf#43175 - Adjust chart references on every copied sheet if (bCopy) { // New position of source table after moving SCTAB nSrcTab = (nDestTab1 <= nMovTab) ? nMovTab + 1 : nMovTab; //#i29848# adjust references to data on the copied sheet ScChartHelper::AdjustRangesOfChartsOnDestinationPage(rDoc, rDestDoc, nSrcTab, nDestTab1); } if(bCopy && rDoc.IsScenario(nMovTab)) { OUString aComment; Color aColor; ScScenarioFlags nFlags; rDoc.GetScenarioData(nMovTab, aComment,aColor, nFlags); rDoc.SetScenario(nDestTab1,true); rDoc.SetScenarioData(nDestTab1,aComment,aColor,nFlags); bool bActive = rDoc.IsActiveScenario(nMovTab ); rDoc.SetActiveScenario( nDestTab1, bActive ); bool bVisible=rDoc.IsVisible(nMovTab); rDoc.SetVisible(nDestTab1,bVisible ); } pSrcTabs->push_back(nMovTab); if(!bCopy) { if(!rDoc.GetTable(rStr,nDestTab1)) { nDestTab1=nTabCount; } } pDestTabs->push_back(nDestTab1); } // Rename must be done after all sheets have been moved. if (bRename) { pDestNames.reset(new vector); size_t n = pDestTabs->size(); pDestNames->reserve(n); for (size_t j = 0; j < n; ++j) { SCTAB nRenameTab = (*pDestTabs)[j]; OUString aTabName = *pNewTabName; rDoc.CreateValidTabName( aTabName ); pDestNames->push_back(aTabName); rDoc.RenameTab(nRenameTab, aTabName); } } else // No need to keep this around when we are not renaming. pTabNames.reset(); SCTAB nTab = GetViewData().GetTabNo(); if (bUndo) { if (bCopy) { pDocShell->GetUndoManager()->AddUndoAction( std::make_unique( pDocShell, std::move(pSrcTabs), std::move(pDestTabs), std::move(pDestNames))); } else { pDocShell->GetUndoManager()->AddUndoAction( std::make_unique( pDocShell, std::move(pSrcTabs), std::move(pDestTabs), std::move(pTabNames), std::move(pDestNames))); } } SCTAB nNewTab = nDestTab; if (nNewTab == SC_TAB_APPEND) nNewTab = rDoc.GetTableCount()-1; else if (!bCopy && nTab& rNames ) { ScDocShell* pDocSh = GetViewData().GetDocShell(); ScDocument& rDoc = pDocSh->GetDocument(); bool bUndo(rDoc.IsUndoEnabled()); std::vector undoTabs; SCTAB nPos = 0; bool bFound(false); for (const OUString& aName : rNames) { if (rDoc.GetTable(aName, nPos)) { rDoc.SetVisible( nPos, true ); SetTabNo( nPos, true ); SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); if (!bFound) bFound = true; if (bUndo) undoTabs.push_back(nPos); } } if (bFound) { if (bUndo) { pDocSh->GetUndoManager()->AddUndoAction( std::make_unique( pDocSh, std::move(undoTabs), true ) ); } pDocSh->PostPaint(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Extras); pDocSh->SetDocumentModified(); } } void ScViewFunc::HideTable( const ScMarkData& rMark, SCTAB nTabToSelect ) { ScDocShell* pDocSh = GetViewData().GetDocShell(); ScDocument& rDoc = pDocSh->GetDocument(); bool bUndo(rDoc.IsUndoEnabled()); SCTAB nVisible = 0; SCTAB nTabCount = rDoc.GetTableCount(); SCTAB nTabSelCount = rMark.GetSelectCount(); // check to make sure we won't hide all sheets. we need at least one visible at all times. for ( SCTAB i=0; i < nTabCount && nVisible <= nTabSelCount ; i++ ) if (rDoc.IsVisible(i)) ++nVisible; if (nVisible <= nTabSelCount) return; std::vector undoTabs; // need to take a copy of selectedtabs since it is modified in the loop const ScMarkData::MarkedTabsType selectedTabs = rMark.GetSelectedTabs(); for (const SCTAB& nTab : selectedTabs) { if (rDoc.IsVisible( nTab )) { rDoc.SetVisible( nTab, false ); // Update views pDocSh->Broadcast( ScTablesHint( SC_TAB_HIDDEN, nTab ) ); SetTabNo( nTab, true ); // Store for undo if (bUndo) undoTabs.push_back(nTab); } } if (nTabToSelect != -1) SetTabNo(nTabToSelect); if (bUndo) { pDocSh->GetUndoManager()->AddUndoAction( std::make_unique( pDocSh, std::move(undoTabs), false ) ); } // Update views SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); pDocSh->PostPaint(0,0,0,rDoc.MaxCol(),rDoc.MaxRow(),MAXTAB, PaintPartFlags::Extras); pDocSh->SetDocumentModified(); } void ScViewFunc::InsertSpecialChar( const OUString& rStr, const vcl::Font& rFont ) { ScEditableTester aTester( this ); if (!aTester.IsEditable()) { ErrorMessage(aTester.GetMessageId()); return; } const sal_Unicode* pChar = rStr.getStr(); ScTabViewShell* pViewShell = GetViewData().GetViewShell(); SvxFontItem aFontItem( rFont.GetFamilyType(), rFont.GetFamilyName(), rFont.GetStyleName(), rFont.GetPitch(), rFont.GetCharSet(), ATTR_FONT ); // if string contains WEAK characters, set all fonts SvtScriptType nScript; ScDocument& rDoc = GetViewData().GetDocument(); if ( rDoc.HasStringWeakCharacters( rStr ) ) nScript = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; else nScript = rDoc.GetStringScriptType( rStr ); SvxScriptSetItem aSetItem( SID_ATTR_CHAR_FONT, pViewShell->GetPool() ); aSetItem.PutItemForScriptType( nScript, aFontItem ); ApplyUserItemSet( aSetItem.GetItemSet() ); while ( *pChar ) pViewShell->TabKeyInput( KeyEvent( *(pChar++), vcl::KeyCode() ) ); } void ScViewFunc::UpdateLineAttrs( SvxBorderLine& rLine, const SvxBorderLine* pDestLine, const SvxBorderLine* pSrcLine, bool bColor ) { if ( !(pSrcLine && pDestLine) ) return; if ( bColor ) { rLine.SetColor ( pSrcLine->GetColor() ); rLine.SetBorderLineStyle(pDestLine->GetBorderLineStyle()); rLine.SetWidth ( pDestLine->GetWidth() ); } else { rLine.SetColor ( pDestLine->GetColor() ); rLine.SetBorderLineStyle(pSrcLine->GetBorderLineStyle()); rLine.SetWidth ( pSrcLine->GetWidth() ); } } #define SET_LINE_ATTRIBUTES(LINE,BOXLINE) \ pBoxLine = aBoxItem.Get##LINE(); \ if ( pBoxLine ) \ { \ if ( pLine ) \ { \ UpdateLineAttrs( aLine, pBoxLine, pLine, bColorOnly ); \ aBoxItem.SetLine( &aLine, BOXLINE ); \ } \ else \ aBoxItem.SetLine( nullptr, BOXLINE ); \ } void ScViewFunc::SetSelectionFrameLines( const SvxBorderLine* pLine, bool bColorOnly ) { // Not editable only due to a matrix? Attribute is ok anyhow. bool bOnlyNotBecauseOfMatrix; if ( !SelectionEditable( &bOnlyNotBecauseOfMatrix ) && !bOnlyNotBecauseOfMatrix ) { ErrorMessage(STR_PROTECTIONERR); return; } ScDocument& rDoc = GetViewData().GetDocument(); ScMarkData aFuncMark( GetViewData().GetMarkData() ); // local copy for UnmarkFiltered ScViewUtil::UnmarkFiltered( aFuncMark, rDoc ); ScDocShell* pDocSh = GetViewData().GetDocShell(); const ScPatternAttr* pSelAttrs = GetSelectionPattern(); const SfxItemSet& rSelItemSet = pSelAttrs->GetItemSet(); const SfxPoolItem* pBorderAttr = nullptr; SfxItemState eItemState = rSelItemSet.GetItemState( ATTR_BORDER, true, &pBorderAttr ); const SfxPoolItem* pTLBRItem = nullptr; SfxItemState eTLBRState = rSelItemSet.GetItemState( ATTR_BORDER_TLBR, true, &pTLBRItem ); const SfxPoolItem* pBLTRItem = nullptr; SfxItemState eBLTRState = rSelItemSet.GetItemState( ATTR_BORDER_BLTR, true, &pBLTRItem ); // any of the lines visible? if( !((eItemState != SfxItemState::DEFAULT) || (eTLBRState != SfxItemState::DEFAULT) || (eBLTRState != SfxItemState::DEFAULT)) ) return; // none of the lines don't care? if( (eItemState != SfxItemState::DONTCARE) && (eTLBRState != SfxItemState::DONTCARE) && (eBLTRState != SfxItemState::DONTCARE) ) { SfxItemSetFixed aOldSet( *rDoc.GetPool() ); SfxItemSetFixed aNewSet( *rDoc.GetPool() ); SvxBorderLine aLine; if( pBorderAttr ) { const SvxBorderLine* pBoxLine = nullptr; SvxBoxItem aBoxItem( *static_cast(pBorderAttr) ); SvxBoxInfoItem aBoxInfoItem( ATTR_BORDER_INNER ); // here pBoxLine is used SET_LINE_ATTRIBUTES(Top,SvxBoxItemLine::TOP) SET_LINE_ATTRIBUTES(Bottom,SvxBoxItemLine::BOTTOM) SET_LINE_ATTRIBUTES(Left,SvxBoxItemLine::LEFT) SET_LINE_ATTRIBUTES(Right,SvxBoxItemLine::RIGHT) aBoxInfoItem.SetLine( aBoxItem.GetTop(), SvxBoxInfoItemLine::HORI ); aBoxInfoItem.SetLine( aBoxItem.GetLeft(), SvxBoxInfoItemLine::VERT ); aBoxInfoItem.ResetFlags(); // set Lines to Valid aOldSet.Put( *pBorderAttr ); aNewSet.Put( aBoxItem ); aNewSet.Put( aBoxInfoItem ); } if( pTLBRItem && static_cast(pTLBRItem)->GetLine() ) { SvxLineItem aTLBRItem( *static_cast(pTLBRItem) ); UpdateLineAttrs( aLine, aTLBRItem.GetLine(), pLine, bColorOnly ); aTLBRItem.SetLine( &aLine ); aOldSet.Put( *pTLBRItem ); aNewSet.Put( aTLBRItem ); } if( pBLTRItem && static_cast(pBLTRItem)->GetLine() ) { SvxLineItem aBLTRItem( *static_cast(pBLTRItem) ); UpdateLineAttrs( aLine, aBLTRItem.GetLine(), pLine, bColorOnly ); aBLTRItem.SetLine( &aLine ); aOldSet.Put( *pBLTRItem ); aNewSet.Put( aBLTRItem ); } ApplyAttributes( aNewSet, aOldSet ); } else // if ( eItemState == SfxItemState::DONTCARE ) { aFuncMark.MarkToMulti(); rDoc.ApplySelectionLineStyle( aFuncMark, pLine, bColorOnly ); } const ScRange& aMarkRange = aFuncMark.GetMultiMarkArea(); SCCOL nStartCol = aMarkRange.aStart.Col(); SCROW nStartRow = aMarkRange.aStart.Row(); SCTAB nStartTab = aMarkRange.aStart.Tab(); SCCOL nEndCol = aMarkRange.aEnd.Col(); SCROW nEndRow = aMarkRange.aEnd.Row(); SCTAB nEndTab = aMarkRange.aEnd.Tab(); pDocSh->PostPaint( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab, PaintPartFlags::Grid, SC_PF_LINES | SC_PF_TESTMERGE ); pDocSh->UpdateOle(GetViewData()); pDocSh->SetDocumentModified(); } #undef SET_LINE_ATTRIBUTES void ScViewFunc::SetValidation( const ScValidationData& rNew ) { ScDocument& rDoc = GetViewData().GetDocument(); sal_uInt32 nIndex = rDoc.AddValidationEntry(rNew); // for it there is no Undo SfxUInt32Item aItem( ATTR_VALIDDATA, nIndex ); ApplyAttr( aItem ); // with Paint and Undo... } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */