diff options
Diffstat (limited to 'sc/source/ui/docshell/docsh5.cxx')
-rw-r--r-- | sc/source/ui/docshell/docsh5.cxx | 1037 |
1 files changed, 1037 insertions, 0 deletions
diff --git a/sc/source/ui/docshell/docsh5.cxx b/sc/source/ui/docshell/docsh5.cxx new file mode 100644 index 000000000..d1749fee1 --- /dev/null +++ b/sc/source/ui/docshell/docsh5.cxx @@ -0,0 +1,1037 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * 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 <memory> +#include <sal/config.h> + +#include <cassert> + +#include <osl/diagnose.h> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <sfx2/app.hxx> +#include <sfx2/bindings.hxx> +#include <unotools/charclass.hxx> + +#include <com/sun/star/script/vba/XVBACompatibility.hpp> + +#include <docsh.hxx> +#include <global.hxx> +#include <globstr.hrc> +#include <scresid.hxx> +#include <globalnames.hxx> +#include <undodat.hxx> +#include <undotab.hxx> +#include <undoblk.hxx> +#include <dpobject.hxx> +#include <dpshttab.hxx> +#include <dbdocfun.hxx> +#include <consoli.hxx> +#include <dbdata.hxx> +#include <progress.hxx> +#include <olinetab.hxx> +#include <patattr.hxx> +#include <attrib.hxx> +#include <docpool.hxx> +#include <uiitems.hxx> +#include <sc.hrc> +#include <sizedev.hxx> +#include <clipparam.hxx> +#include <rowheightcontext.hxx> +#include <refupdatecontext.hxx> + +using com::sun::star::script::XLibraryContainer; +using com::sun::star::script::vba::XVBACompatibility; +using com::sun::star::container::XNameContainer; +using com::sun::star::uno::Reference; +using com::sun::star::uno::UNO_QUERY; + +using ::std::unique_ptr; +using ::std::vector; + +// former viewfunc/dbfunc methods + +void ScDocShell::ErrorMessage(TranslateId pGlobStrId) +{ + //! StopMarking at the (active) view? + + weld::Window* pParent = GetActiveDialogParent(); + weld::WaitObject aWaitOff( pParent ); + bool bFocus = pParent && pParent->has_focus(); + + if (pGlobStrId && pGlobStrId == STR_PROTECTIONERR) + { + if (IsReadOnly()) + { + pGlobStrId = STR_READONLYERR; + } + } + + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pParent, + VclMessageType::Info, VclButtonsType::Ok, + ScResId(pGlobStrId))); + xInfoBox->run(); + + if (bFocus) + pParent->grab_focus(); +} + +bool ScDocShell::IsEditable() const +{ + // import into read-only document is possible - must be extended if other filters use api + // #i108547# MSOOXML filter uses "IsChangeReadOnlyEnabled" property + + return !IsReadOnly() || m_pDocument->IsImportingXML() || m_pDocument->IsChangeReadOnlyEnabled(); +} + +void ScDocShell::DBAreaDeleted( SCTAB nTab, SCCOL nX1, SCROW nY1, SCCOL nX2 ) +{ + ScDocShellModificator aModificator( *this ); + // the auto-filter is in the first row of the area + m_pDocument->RemoveFlagsTab( nX1, nY1, nX2, nY1, nTab, ScMF::Auto ); + PostPaint( nX1, nY1, nTab, nX2, nY1, nTab, PaintPartFlags::Grid ); + // No SetDocumentModified, as the unnamed database range might have to be restored later. + // The UNO hint is broadcast directly instead, to keep UNO objects in valid state. + m_pDocument->BroadcastUno( SfxHint( SfxHintId::DataChanged ) ); +} + +ScDBData* ScDocShell::GetDBData( const ScRange& rMarked, ScGetDBMode eMode, ScGetDBSelection eSel ) +{ + SCCOL nCol = rMarked.aStart.Col(); + SCROW nRow = rMarked.aStart.Row(); + SCTAB nTab = rMarked.aStart.Tab(); + + SCCOL nStartCol = nCol; + SCROW nStartRow = nRow; + SCTAB nStartTab = nTab; + SCCOL nEndCol = rMarked.aEnd.Col(); + SCROW nEndRow = rMarked.aEnd.Row(); + // Not simply GetDBAtCursor: The continuous data range for "unnamed" (GetDataArea) may be + // located next to the cursor; so a named DB range needs to be searched for there as well. + ScDBCollection* pColl = m_pDocument->GetDBCollection(); + ScDBData* pData = m_pDocument->GetDBAtArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow ); + if (!pData) + pData = pColl->GetDBNearCursor(nCol, nRow, nTab ); + + bool bSelected = ( eSel == ScGetDBSelection::ForceMark || + (rMarked.aStart != rMarked.aEnd && eSel != ScGetDBSelection::RowDown) ); + bool bOnlyDown = (!bSelected && eSel == ScGetDBSelection::RowDown && rMarked.aStart.Row() == rMarked.aEnd.Row()); + + bool bUseThis = false; + if (pData) + { + // take range, if nothing else is marked + + SCTAB nDummy; + SCCOL nOldCol1; + SCROW nOldRow1; + SCCOL nOldCol2; + SCROW nOldRow2; + pData->GetArea( nDummy, nOldCol1,nOldRow1, nOldCol2,nOldRow2 ); + bool bIsNoName = ( pData->GetName() == STR_DB_LOCAL_NONAME ); + + if (!bSelected) + { + bUseThis = true; + if ( bIsNoName && (eMode == SC_DB_MAKE || eMode == SC_DB_AUTOFILTER) ) + { + // If nothing marked or only one row marked, adapt + // "unnamed" to contiguous area. + nStartCol = nCol; + nStartRow = nRow; + if (bOnlyDown) + { + nEndCol = rMarked.aEnd.Col(); + nEndRow = rMarked.aEnd.Row(); + } + else + { + nEndCol = nStartCol; + nEndRow = nStartRow; + } + m_pDocument->GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow, false, bOnlyDown ); + if ( nOldCol1 != nStartCol || nOldCol2 != nEndCol || nOldRow1 != nStartRow ) + bUseThis = false; // doesn't fit at all + else if ( nOldRow2 != nEndRow ) + { + // extend range to new end row + pData->SetArea( nTab, nOldCol1,nOldRow1, nOldCol2,nEndRow ); + } + } + } + else + { + if ( nOldCol1 == nStartCol && nOldRow1 == nStartRow && + nOldCol2 == nEndCol && nOldRow2 == nEndRow ) // marked precisely? + bUseThis = true; + else + bUseThis = false; // always take marking (Bug 11964) + } + + // never take "unnamed" for import + + if ( bUseThis && eMode == SC_DB_IMPORT && bIsNoName ) + bUseThis = false; + } + + if ( bUseThis ) + { + pData->GetArea( nStartTab, nStartCol,nStartRow, nEndCol,nEndRow ); + } + else if ( eMode == SC_DB_OLD ) + { + pData = nullptr; // nothing found + } + else + { + if ( !bSelected ) + { // continuous range + nStartCol = nCol; + nStartRow = nRow; + if (bOnlyDown) + { + nEndCol = rMarked.aEnd.Col(); + nEndRow = rMarked.aEnd.Row(); + } + else + { + nEndCol = nStartCol; + nEndRow = nStartRow; + } + m_pDocument->GetDataArea( nTab, nStartCol, nStartRow, nEndCol, nEndRow, false, bOnlyDown ); + } + + bool bHasHeader = m_pDocument->HasColHeader( nStartCol,nStartRow, nEndCol,nEndRow, nTab ); + + ScDBData* pNoNameData = m_pDocument->GetAnonymousDBData(nTab); + if ( eMode != SC_DB_IMPORT && pNoNameData) + { + // Do not reset AutoFilter range during temporary operations on + // other ranges, use the document global temporary anonymous range + // instead. But, if AutoFilter is to be toggled then do use the + // sheet-local DB range. + bool bSheetLocal = true; + if (eMode != SC_DB_AUTOFILTER && pNoNameData->HasAutoFilter()) + { + bSheetLocal = false; + pNoNameData = m_pDocument->GetAnonymousDBData(); + if (!pNoNameData) + { + m_pDocument->SetAnonymousDBData( std::unique_ptr<ScDBData>(new ScDBData( STR_DB_LOCAL_NONAME, + nTab, nStartCol, nStartRow, nEndCol, nEndRow, true, bHasHeader) ) ); + pNoNameData = m_pDocument->GetAnonymousDBData(); + } + // ScDocShell::CancelAutoDBRange() would restore the + // sheet-local anonymous DBData from pOldAutoDBRange, unset so + // that won't happen with data of a previous sheet-local + // DBData. + m_pOldAutoDBRange.reset(); + } + else if (!m_pOldAutoDBRange) + { + // store the old unnamed database range with its settings for undo + // (store at the first change, get the state before all changes) + m_pOldAutoDBRange.reset( new ScDBData( *pNoNameData ) ); + } + else if (m_pOldAutoDBRange->GetTab() != pNoNameData->GetTab()) + { + // Different sheet-local unnamed DB range than the previous one. + *m_pOldAutoDBRange = *pNoNameData; + } + + SCCOL nOldX1; // take old range away cleanly + SCROW nOldY1; //! (UNDO ???) + SCCOL nOldX2; + SCROW nOldY2; + SCTAB nOldTab; + pNoNameData->GetArea( nOldTab, nOldX1, nOldY1, nOldX2, nOldY2 ); + + // If previously bHasHeader was set and the new range starts on the + // same row and intersects the old column range, then don't reset + // bHasHeader but assume that the new range still has headers, just + // some are empty or numeric. + if (!bHasHeader && pNoNameData->HasHeader() && nTab == nOldTab && nStartRow == nOldY1 && + nStartCol <= nOldY2 && nOldY1 <= nEndCol) + bHasHeader = true; + + // Remove AutoFilter button flags only for sheet-local DB range, + // not if a temporary is used. + if (bSheetLocal) + DBAreaDeleted( nOldTab, nOldX1, nOldY1, nOldX2 ); + + pNoNameData->SetSortParam( ScSortParam() ); // reset parameter + pNoNameData->SetQueryParam( ScQueryParam() ); + pNoNameData->SetSubTotalParam( ScSubTotalParam() ); + + pNoNameData->SetArea( nTab, nStartCol,nStartRow, nEndCol,nEndRow ); // set anew + pNoNameData->SetByRow( true ); + pNoNameData->SetHeader( bHasHeader ); + pNoNameData->SetAutoFilter( false ); + } + else + { + std::unique_ptr<ScDBCollection> pUndoColl; + + if (eMode==SC_DB_IMPORT) + { + m_pDocument->PreprocessDBDataUpdate(); + pUndoColl.reset( new ScDBCollection( *pColl ) ); // Undo for import range + + OUString aImport = ScResId( STR_DBNAME_IMPORT ); + tools::Long nCount = 0; + const ScDBData* pDummy = nullptr; + ScDBCollection::NamedDBs& rDBs = pColl->getNamedDBs(); + OUString aNewName; + do + { + ++nCount; + aNewName = aImport + OUString::number( nCount ); + pDummy = rDBs.findByUpperName(ScGlobal::getCharClass().uppercase(aNewName)); + } + while (pDummy); + pNoNameData = new ScDBData( aNewName, nTab, + nStartCol,nStartRow, nEndCol,nEndRow, + true, bHasHeader ); + bool ins = rDBs.insert(std::unique_ptr<ScDBData>(pNoNameData)); + assert(ins); (void)ins; + } + else + { + pNoNameData = new ScDBData(STR_DB_LOCAL_NONAME, nTab, + nStartCol,nStartRow, nEndCol,nEndRow, + true, bHasHeader ); + m_pDocument->SetAnonymousDBData(nTab, std::unique_ptr<ScDBData>(pNoNameData)); + } + + if ( pUndoColl ) + { + m_pDocument->CompileHybridFormula(); + + GetUndoManager()->AddUndoAction( std::make_unique<ScUndoDBData>( this, + std::move(pUndoColl), + std::make_unique<ScDBCollection>( *pColl ) ) ); + } + + // no longer needed to register new range at the Sba + + // announce "Import1", etc., at the Navigator + if (eMode==SC_DB_IMPORT) + SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScDbAreasChanged ) ); + } + pData = pNoNameData; + } + + return pData; +} + +ScDBData* ScDocShell::GetAnonymousDBData(const ScRange& rRange) +{ + ScDBCollection* pColl = m_pDocument->GetDBCollection(); + if (!pColl) + return nullptr; + + ScDBData* pData = pColl->getAnonDBs().getByRange(rRange); + if (!pData) + return nullptr; + + if (!pData->HasHeader()) + { + bool bHasHeader = m_pDocument->HasColHeader( + rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aStart.Tab()); + pData->SetHeader(bHasHeader); + } + + return pData; +} + +std::unique_ptr<ScDBData> ScDocShell::GetOldAutoDBRange() +{ + return std::move(m_pOldAutoDBRange); +} + +void ScDocShell::CancelAutoDBRange() +{ + // called when dialog is cancelled +//moggi:TODO + if ( !m_pOldAutoDBRange ) + return; + + SCTAB nTab = GetCurTab(); + ScDBData* pDBData = m_pDocument->GetAnonymousDBData(nTab); + if ( pDBData ) + { + SCCOL nRangeX1; + SCROW nRangeY1; + SCCOL nRangeX2; + SCROW nRangeY2; + SCTAB nRangeTab; + pDBData->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 ); + DBAreaDeleted( nRangeTab, nRangeX1, nRangeY1, nRangeX2 ); + + *pDBData = *m_pOldAutoDBRange; // restore old settings + + if ( m_pOldAutoDBRange->HasAutoFilter() ) + { + // restore AutoFilter buttons + m_pOldAutoDBRange->GetArea( nRangeTab, nRangeX1, nRangeY1, nRangeX2, nRangeY2 ); + m_pDocument->ApplyFlagsTab( nRangeX1, nRangeY1, nRangeX2, nRangeY1, nRangeTab, ScMF::Auto ); + PostPaint( nRangeX1, nRangeY1, nRangeTab, nRangeX2, nRangeY1, nRangeTab, PaintPartFlags::Grid ); + } + } + + m_pOldAutoDBRange.reset(); +} + + // adjust height + //! merge with docfunc + +bool ScDocShell::AdjustRowHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab ) +{ + ScSizeDeviceProvider aProv(this); + Fraction aZoom(1,1); + sc::RowHeightContext aCxt(m_pDocument->MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), aZoom, aZoom, aProv.GetDevice()); + bool bChange = m_pDocument->SetOptimalHeight(aCxt, nStartRow,nEndRow, nTab, true); + + if (bChange) + { + // tdf#76183: recalculate objects' positions + m_pDocument->SetDrawPageSize(nTab); + + PostPaint( 0,nStartRow,nTab, m_pDocument->MaxCol(),m_pDocument->MaxRow(),nTab, PaintPartFlags::Grid|PaintPartFlags::Left ); + } + + return bChange; +} + +void ScDocShell::UpdateAllRowHeights( const ScMarkData* pTabMark ) +{ + // update automatic row heights + + ScSizeDeviceProvider aProv(this); + Fraction aZoom(1,1); + sc::RowHeightContext aCxt(m_pDocument->MaxRow(), aProv.GetPPTX(), aProv.GetPPTY(), aZoom, aZoom, aProv.GetDevice()); + m_pDocument->UpdateAllRowHeights(aCxt, pTabMark); +} + +void ScDocShell::UpdatePendingRowHeights( SCTAB nUpdateTab, bool bBefore ) +{ + bool bIsUndoEnabled = m_pDocument->IsUndoEnabled(); + m_pDocument->EnableUndo( false ); + m_pDocument->LockStreamValid( true ); // ignore draw page size (but not formula results) + if ( bBefore ) // check all sheets up to nUpdateTab + { + SCTAB nTabCount = m_pDocument->GetTableCount(); + if ( nUpdateTab >= nTabCount ) + nUpdateTab = nTabCount-1; // nUpdateTab is inclusive + + ScMarkData aUpdateSheets(m_pDocument->GetSheetLimits()); + SCTAB nTab; + for (nTab=0; nTab<=nUpdateTab; ++nTab) + if ( m_pDocument->IsPendingRowHeights( nTab ) ) + aUpdateSheets.SelectTable( nTab, true ); + + if (aUpdateSheets.GetSelectCount()) + UpdateAllRowHeights(&aUpdateSheets); // update with a single progress bar + + for (nTab=0; nTab<=nUpdateTab; ++nTab) + if ( aUpdateSheets.GetTableSelect( nTab ) ) + { + m_pDocument->UpdatePageBreaks( nTab ); + m_pDocument->SetPendingRowHeights( nTab, false ); + } + } + else // only nUpdateTab + { + if ( m_pDocument->IsPendingRowHeights( nUpdateTab ) ) + { + AdjustRowHeight( 0, m_pDocument->MaxRow(), nUpdateTab ); + m_pDocument->UpdatePageBreaks( nUpdateTab ); + m_pDocument->SetPendingRowHeights( nUpdateTab, false ); + } + } + m_pDocument->LockStreamValid( false ); + m_pDocument->EnableUndo( bIsUndoEnabled ); +} + +void ScDocShell::RefreshPivotTables( const ScRange& rSource ) +{ + ScDPCollection* pColl = m_pDocument->GetDPCollection(); + if (!pColl) + return; + + ScDBDocFunc aFunc(*this); + for (size_t i = 0, n = pColl->GetCount(); i < n; ++i) + { + ScDPObject& rOld = (*pColl)[i]; + + const ScSheetSourceDesc* pSheetDesc = rOld.GetSheetDesc(); + if (pSheetDesc && pSheetDesc->GetSourceRange().Intersects(rSource)) + aFunc.UpdatePivotTable(rOld, true, false); + } +} + +static OUString lcl_GetAreaName( ScDocument* pDoc, const ScArea* pArea ) +{ + ScDBData* pData = pDoc->GetDBAtArea( pArea->nTab, pArea->nColStart, pArea->nRowStart, + pArea->nColEnd, pArea->nRowEnd ); + if (pData) + return pData->GetName(); + + OUString aName; + pDoc->GetName( pArea->nTab, aName ); + return aName; +} + +void ScDocShell::DoConsolidate( const ScConsolidateParam& rParam, bool bRecord ) +{ + ScConsData aData; + + sal_uInt16 nPos; + SCCOL nColSize = 0; + SCROW nRowSize = 0; + bool bErr = false; + for (nPos=0; nPos<rParam.nDataAreaCount; nPos++) + { + ScArea const & rArea = rParam.pDataAreas[nPos]; + nColSize = std::max( nColSize, SCCOL( rArea.nColEnd - rArea.nColStart + 1 ) ); + nRowSize = std::max( nRowSize, SCROW( rArea.nRowEnd - rArea.nRowStart + 1 ) ); + + // test if source data were moved + if (rParam.bReferenceData) + if (rArea.nTab == rParam.nTab && rArea.nRowEnd >= rParam.nRow) + bErr = true; + } + + if (bErr) + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetActiveDialogParent(), + VclMessageType::Info, VclButtonsType::Ok, + ScResId(STR_CONSOLIDATE_ERR1))); + xInfoBox->run(); + return; + } + + // execute + + weld::WaitObject aWait( GetActiveDialogParent() ); + ScDocShellModificator aModificator( *this ); + + ScRange aOldDest; + ScDBData* pDestData = m_pDocument->GetDBAtCursor( rParam.nCol, rParam.nRow, rParam.nTab, ScDBDataPortion::TOP_LEFT ); + if (pDestData) + pDestData->GetArea(aOldDest); + + aData.SetSize( nColSize, nRowSize ); + aData.SetFlags( rParam.eFunction, rParam.bByCol, rParam.bByRow, rParam.bReferenceData ); + if ( rParam.bByCol || rParam.bByRow ) + for (nPos=0; nPos<rParam.nDataAreaCount; nPos++) + { + ScArea const & rArea = rParam.pDataAreas[nPos]; + aData.AddFields( m_pDocument.get(), rArea.nTab, rArea.nColStart, rArea.nRowStart, + rArea.nColEnd, rArea.nRowEnd ); + } + aData.DoneFields(); + for (nPos=0; nPos<rParam.nDataAreaCount; nPos++) + { + ScArea const & rArea = rParam.pDataAreas[nPos]; + aData.AddData( m_pDocument.get(), rArea.nTab, rArea.nColStart, rArea.nRowStart, + rArea.nColEnd, rArea.nRowEnd ); + aData.AddName( lcl_GetAreaName(m_pDocument.get(), &rArea) ); + } + + aData.GetSize( nColSize, nRowSize ); + if (bRecord && nColSize > 0 && nRowSize > 0) + { + std::unique_ptr<ScDBData> pUndoData(pDestData ? new ScDBData(*pDestData) : nullptr); + + SCTAB nDestTab = rParam.nTab; + ScArea aDestArea( rParam.nTab, rParam.nCol, rParam.nRow, + rParam.nCol+nColSize-1, rParam.nRow+nRowSize-1 ); + if (rParam.bByCol) ++aDestArea.nColEnd; + if (rParam.bByRow) ++aDestArea.nRowEnd; + + if (rParam.bReferenceData) + { + SCTAB nTabCount = m_pDocument->GetTableCount(); + SCROW nInsertCount = aData.GetInsertCount(); + + // old outlines + ScOutlineTable* pTable = m_pDocument->GetOutlineTable( nDestTab ); + std::unique_ptr<ScOutlineTable> pUndoTab(pTable ? new ScOutlineTable( *pTable ) : nullptr); + + ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO )); + pUndoDoc->InitUndo( *m_pDocument, 0, nTabCount-1, false, true ); + + // row state + m_pDocument->CopyToDocument(0, 0, nDestTab, m_pDocument->MaxCol(), m_pDocument->MaxRow(), nDestTab, + InsertDeleteFlags::NONE, false, *pUndoDoc); + + // all formulas + m_pDocument->CopyToDocument(0, 0, 0, m_pDocument->MaxCol(), m_pDocument->MaxRow(), nTabCount-1, + InsertDeleteFlags::FORMULA, false, *pUndoDoc); + + // complete output rows + m_pDocument->CopyToDocument(0, aDestArea.nRowStart, nDestTab, + m_pDocument->MaxCol(),aDestArea.nRowEnd, nDestTab, + InsertDeleteFlags::ALL, false, *pUndoDoc); + + // old output range + if (pDestData) + m_pDocument->CopyToDocument(aOldDest, InsertDeleteFlags::ALL, false, *pUndoDoc); + + GetUndoManager()->AddUndoAction( + std::make_unique<ScUndoConsolidate>( this, aDestArea, rParam, std::move(pUndoDoc), + true, nInsertCount, std::move(pUndoTab), std::move(pUndoData) ) ); + } + else + { + ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO )); + pUndoDoc->InitUndo( *m_pDocument, aDestArea.nTab, aDestArea.nTab ); + + m_pDocument->CopyToDocument(aDestArea.nColStart, aDestArea.nRowStart, aDestArea.nTab, + aDestArea.nColEnd, aDestArea.nRowEnd, aDestArea.nTab, + InsertDeleteFlags::ALL, false, *pUndoDoc); + + // old output range + if (pDestData) + m_pDocument->CopyToDocument(aOldDest, InsertDeleteFlags::ALL, false, *pUndoDoc); + + GetUndoManager()->AddUndoAction( + std::make_unique<ScUndoConsolidate>( this, aDestArea, rParam, std::move(pUndoDoc), + false, 0, nullptr, std::move(pUndoData) ) ); + } + } + + if (pDestData) // delete / adjust destination range + { + m_pDocument->DeleteAreaTab(aOldDest, InsertDeleteFlags::CONTENTS); + pDestData->SetArea( rParam.nTab, rParam.nCol, rParam.nRow, + rParam.nCol + nColSize - 1, rParam.nRow + nRowSize - 1 ); + pDestData->SetHeader( rParam.bByRow ); + } + + aData.OutputToDocument( *m_pDocument, rParam.nCol, rParam.nRow, rParam.nTab ); + + SCCOL nPaintStartCol = rParam.nCol; + SCROW nPaintStartRow = rParam.nRow; + SCCOL nPaintEndCol = nPaintStartCol + nColSize - 1; + SCROW nPaintEndRow = nPaintStartRow + nRowSize - 1; + PaintPartFlags nPaintFlags = PaintPartFlags::Grid; + if (rParam.bByCol) + ++nPaintEndRow; + if (rParam.bByRow) + ++nPaintEndCol; + if (rParam.bReferenceData) + { + nPaintStartCol = 0; + nPaintEndCol = m_pDocument->MaxCol(); + nPaintEndRow = m_pDocument->MaxRow(); + nPaintFlags |= PaintPartFlags::Left | PaintPartFlags::Size; + } + if (pDestData) + { + if ( aOldDest.aEnd.Col() > nPaintEndCol ) + nPaintEndCol = aOldDest.aEnd.Col(); + if ( aOldDest.aEnd.Row() > nPaintEndRow ) + nPaintEndRow = aOldDest.aEnd.Row(); + } + PostPaint( nPaintStartCol, nPaintStartRow, rParam.nTab, + nPaintEndCol, nPaintEndRow, rParam.nTab, nPaintFlags ); + aModificator.SetDocumentModified(); +} + +void ScDocShell::UseScenario( SCTAB nTab, const OUString& rName, bool bRecord ) +{ + if (!m_pDocument->IsScenario(nTab)) + { + SCTAB nTabCount = m_pDocument->GetTableCount(); + SCTAB nSrcTab = SCTAB_MAX; + SCTAB nEndTab = nTab; + OUString aCompare; + while ( nEndTab+1 < nTabCount && m_pDocument->IsScenario(nEndTab+1) ) + { + ++nEndTab; + if (nSrcTab > MAXTAB) // still searching for the scenario? + { + m_pDocument->GetName( nEndTab, aCompare ); + if (aCompare == rName) + nSrcTab = nEndTab; // found + } + } + if (ValidTab(nSrcTab)) + { + if ( m_pDocument->TestCopyScenario( nSrcTab, nTab ) ) // test cell protection + { + ScDocShellModificator aModificator( *this ); + ScMarkData aScenMark(m_pDocument->GetSheetLimits()); + m_pDocument->MarkScenario( nSrcTab, nTab, aScenMark ); + const ScRange& aMultiRange = aScenMark.GetMultiMarkArea(); + SCCOL nStartCol = aMultiRange.aStart.Col(); + SCROW nStartRow = aMultiRange.aStart.Row(); + SCCOL nEndCol = aMultiRange.aEnd.Col(); + SCROW nEndRow = aMultiRange.aEnd.Row(); + + if (bRecord) + { + ScDocumentUniquePtr pUndoDoc(new ScDocument( SCDOCMODE_UNDO )); + pUndoDoc->InitUndo( *m_pDocument, nTab,nEndTab ); // also all scenarios + // shown table: + m_pDocument->CopyToDocument(nStartCol, nStartRow, nTab, + nEndCol, nEndRow, nTab, InsertDeleteFlags::ALL, + true, *pUndoDoc, &aScenMark); + // scenarios + for (SCTAB i=nTab+1; i<=nEndTab; i++) + { + pUndoDoc->SetScenario( i, true ); + OUString aComment; + Color aColor; + ScScenarioFlags nScenFlags; + m_pDocument->GetScenarioData( i, aComment, aColor, nScenFlags ); + pUndoDoc->SetScenarioData( i, aComment, aColor, nScenFlags ); + bool bActive = m_pDocument->IsActiveScenario( i ); + pUndoDoc->SetActiveScenario( i, bActive ); + // At copy-back scenarios also contents + if ( nScenFlags & ScScenarioFlags::TwoWay ) + m_pDocument->CopyToDocument(0, 0, i, m_pDocument->MaxCol(), m_pDocument->MaxRow(), i, + InsertDeleteFlags::ALL, false, *pUndoDoc ); + } + + GetUndoManager()->AddUndoAction( + std::make_unique<ScUndoUseScenario>( this, aScenMark, + ScArea( nTab,nStartCol,nStartRow,nEndCol,nEndRow ), + std::move(pUndoDoc), rName ) ); + } + + m_pDocument->CopyScenario( nSrcTab, nTab ); + + sc::SetFormulaDirtyContext aCxt; + m_pDocument->SetAllFormulasDirty(aCxt); + + // paint all, because the active scenario may be modified in other ranges; + //! only if there are visible frames? + PostPaint( 0,0,nTab, m_pDocument->MaxCol(),m_pDocument->MaxRow(),nTab, PaintPartFlags::Grid ); + aModificator.SetDocumentModified(); + } + else + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetActiveDialogParent(), + VclMessageType::Info, VclButtonsType::Ok, + ScResId(STR_PROTECTIONERR))); + xInfoBox->run(); + } + } + else + { + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(GetActiveDialogParent(), + VclMessageType::Info, VclButtonsType::Ok, + ScResId(STR_SCENARIO_NOTFOUND))); + xInfoBox->run(); + } + } + else + { + OSL_FAIL( "UseScenario on Scenario-Sheet" ); + } +} + +void ScDocShell::ModifyScenario( SCTAB nTab, const OUString& rName, const OUString& rComment, + const Color& rColor, ScScenarioFlags nFlags ) +{ + // Undo + OUString aOldName; + m_pDocument->GetName( nTab, aOldName ); + OUString aOldComment; + Color aOldColor; + ScScenarioFlags nOldFlags; + m_pDocument->GetScenarioData( nTab, aOldComment, aOldColor, nOldFlags ); + GetUndoManager()->AddUndoAction( + std::make_unique<ScUndoScenarioFlags>(this, nTab, + aOldName, rName, aOldComment, rComment, + aOldColor, rColor, nOldFlags, nFlags) ); + + // execute + ScDocShellModificator aModificator( *this ); + m_pDocument->RenameTab( nTab, rName ); + m_pDocument->SetScenarioData( nTab, rComment, rColor, nFlags ); + PostPaintGridAll(); + aModificator.SetDocumentModified(); + + if (aOldName != rName) + SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); + + SfxBindings* pBindings = GetViewBindings(); + if (pBindings) + pBindings->Invalidate( SID_SELECT_SCENARIO ); +} + +SCTAB ScDocShell::MakeScenario( SCTAB nTab, const OUString& rName, const OUString& rComment, + const Color& rColor, ScScenarioFlags nFlags, + ScMarkData& rMark, bool bRecord ) +{ + rMark.MarkToMulti(); + if (rMark.IsMultiMarked()) + { + SCTAB nNewTab = nTab + 1; + while (m_pDocument->IsScenario(nNewTab)) + ++nNewTab; + + bool bCopyAll = ( (nFlags & ScScenarioFlags::CopyAll) != ScScenarioFlags::NONE ); + const ScMarkData* pCopyMark = nullptr; + if (!bCopyAll) + pCopyMark = &rMark; + + ScDocShellModificator aModificator( *this ); + + if (bRecord) + m_pDocument->BeginDrawUndo(); // drawing layer must do its own undo actions + + if (m_pDocument->CopyTab( nTab, nNewTab, pCopyMark )) + { + if (bRecord) + { + GetUndoManager()->AddUndoAction( + std::make_unique<ScUndoMakeScenario>( this, nTab, nNewTab, + rName, rComment, rColor, nFlags, rMark )); + } + + m_pDocument->RenameTab( nNewTab, rName); + m_pDocument->SetScenario( nNewTab, true ); + m_pDocument->SetScenarioData( nNewTab, rComment, rColor, nFlags ); + + ScMarkData aDestMark = rMark; + aDestMark.SelectOneTable( nNewTab ); + + //! test for filter / buttons / merging + + ScPatternAttr aProtPattern( m_pDocument->GetPool() ); + aProtPattern.GetItemSet().Put( ScProtectionAttr( true ) ); + m_pDocument->ApplyPatternAreaTab( 0,0, m_pDocument->MaxCol(),m_pDocument->MaxRow(), nNewTab, aProtPattern ); + + ScPatternAttr aPattern( m_pDocument->GetPool() ); + aPattern.GetItemSet().Put( ScMergeFlagAttr( ScMF::Scenario ) ); + aPattern.GetItemSet().Put( ScProtectionAttr( true ) ); + m_pDocument->ApplySelectionPattern( aPattern, aDestMark ); + + if (!bCopyAll) + m_pDocument->SetVisible( nNewTab, false ); + + // this is the active scenario, then + m_pDocument->CopyScenario( nNewTab, nTab, true ); // sal_True - don't copy anything from scenario + + if (nFlags & ScScenarioFlags::ShowFrame) + PostPaint( 0,0,nTab, m_pDocument->MaxCol(),m_pDocument->MaxRow(),nTab, PaintPartFlags::Grid ); // paint frames + PostPaintExtras(); // table tab + aModificator.SetDocumentModified(); + + // A scenario tab is like a hidden sheet, broadcasting also + // notifies ScTabViewShell to add an ScViewData::maTabData entry. + Broadcast( ScTablesHint( SC_TAB_INSERTED, nNewTab )); + SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); + + return nNewTab; + } + } + return nTab; +} + +sal_uLong ScDocShell::TransferTab( ScDocShell& rSrcDocShell, SCTAB nSrcPos, + SCTAB nDestPos, bool bInsertNew, + bool bNotifyAndPaint ) +{ + ScDocument& rSrcDoc = rSrcDocShell.GetDocument(); + + // set the transferred area to the copyparam to make adjusting formulas possible + ScClipParam aParam; + ScRange aRange(0, 0, nSrcPos, m_pDocument->MaxCol(), m_pDocument->MaxRow(), nSrcPos); + aParam.maRanges.push_back(aRange); + rSrcDoc.SetClipParam(aParam); + + sal_uLong nErrVal = m_pDocument->TransferTab( rSrcDoc, nSrcPos, nDestPos, + bInsertNew ); // no insert + + // TransferTab doesn't copy drawing objects with bInsertNew=FALSE + if ( nErrVal > 0 && !bInsertNew) + m_pDocument->TransferDrawPage( rSrcDoc, nSrcPos, nDestPos ); + + if(nErrVal>0 && rSrcDoc.IsScenario( nSrcPos )) + { + OUString aComment; + Color aColor; + ScScenarioFlags nFlags; + + rSrcDoc.GetScenarioData( nSrcPos, aComment,aColor, nFlags); + m_pDocument->SetScenario(nDestPos,true); + m_pDocument->SetScenarioData(nDestPos,aComment,aColor,nFlags); + bool bActive = rSrcDoc.IsActiveScenario(nSrcPos); + m_pDocument->SetActiveScenario(nDestPos, bActive ); + + bool bVisible = rSrcDoc.IsVisible(nSrcPos); + m_pDocument->SetVisible(nDestPos,bVisible ); + + } + + if ( nErrVal > 0 && rSrcDoc.IsTabProtected( nSrcPos ) ) + m_pDocument->SetTabProtection(nDestPos, rSrcDoc.GetTabProtection(nSrcPos)); + if ( bNotifyAndPaint ) + { + Broadcast( ScTablesHint( SC_TAB_INSERTED, nDestPos ) ); + PostPaintExtras(); + PostPaintGridAll(); + } + return nErrVal; +} + +bool ScDocShell::MoveTable( SCTAB nSrcTab, SCTAB nDestTab, bool bCopy, bool bRecord ) +{ + ScDocShellModificator aModificator( *this ); + + // #i92477# be consistent with ScDocFunc::InsertTable: any index past the last sheet means "append" + // #i101139# nDestTab must be the target position, not APPEND (for CopyTabProtection etc.) + if ( nDestTab >= m_pDocument->GetTableCount() ) + nDestTab = m_pDocument->GetTableCount(); + + if (bCopy) + { + if (bRecord) + m_pDocument->BeginDrawUndo(); // drawing layer must do its own undo actions + + OUString sSrcCodeName; + m_pDocument->GetCodeName( nSrcTab, sSrcCodeName ); + if (!m_pDocument->CopyTab( nSrcTab, nDestTab )) + { + //! EndDrawUndo? + return false; + } + else + { + SCTAB nAdjSource = nSrcTab; + if ( nDestTab <= nSrcTab ) + ++nAdjSource; // new position of source table after CopyTab + + if ( m_pDocument->IsTabProtected( nAdjSource ) ) + m_pDocument->CopyTabProtection(nAdjSource, nDestTab); + + if (bRecord) + { + unique_ptr< vector<SCTAB> > pSrcList(new vector<SCTAB>(1, nSrcTab)); + unique_ptr< vector<SCTAB> > pDestList(new vector<SCTAB>(1, nDestTab)); + GetUndoManager()->AddUndoAction( + std::make_unique<ScUndoCopyTab>(this, std::move(pSrcList), std::move(pDestList))); + } + + bool bVbaEnabled = m_pDocument->IsInVBAMode(); + if ( bVbaEnabled ) + { + OUString aLibName( "Standard" ); + Reference< XLibraryContainer > xLibContainer = GetBasicContainer(); + Reference< XVBACompatibility > xVBACompat( xLibContainer, UNO_QUERY ); + + if ( xVBACompat.is() ) + { + aLibName = xVBACompat->getProjectName(); + } + + SCTAB nTabToUse = nDestTab; + if ( nDestTab == SC_TAB_APPEND ) + nTabToUse = m_pDocument->GetMaxTableNumber() - 1; + OUString sSource; + try + { + Reference< XNameContainer > xLib; + if( xLibContainer.is() ) + { + css::uno::Any aLibAny = xLibContainer->getByName( aLibName ); + aLibAny >>= xLib; + } + if( xLib.is() ) + { + xLib->getByName( sSrcCodeName ) >>= sSource; + } + } + catch ( const css::uno::Exception& ) + { + } + VBA_InsertModule( *m_pDocument, nTabToUse, sSource ); + } + } + Broadcast( ScTablesHint( SC_TAB_COPIED, nSrcTab, nDestTab ) ); + } + else + { + if ( m_pDocument->GetChangeTrack() ) + return false; + + if ( nSrcTab<nDestTab && nDestTab!=SC_TAB_APPEND ) + nDestTab--; + + if ( nSrcTab == nDestTab ) + { + //! allow only for api calls? + return true; // nothing to do, but valid + } + + std::optional<ScProgress> pProgress(std::in_place, this, ScResId(STR_UNDO_MOVE_TAB), + m_pDocument->GetCodeCount(), true); + bool bDone = m_pDocument->MoveTab( nSrcTab, nDestTab, &*pProgress ); + pProgress.reset(); + if (!bDone) + { + return false; + } + else if (bRecord) + { + unique_ptr< vector<SCTAB> > pSrcList(new vector<SCTAB>(1, nSrcTab)); + unique_ptr< vector<SCTAB> > pDestList(new vector<SCTAB>(1, nDestTab)); + GetUndoManager()->AddUndoAction( + std::make_unique<ScUndoMoveTab>(this, std::move(pSrcList), std::move(pDestList))); + } + + Broadcast( ScTablesHint( SC_TAB_MOVED, nSrcTab, nDestTab ) ); + } + + PostPaintGridAll(); + PostPaintExtras(); + aModificator.SetDocumentModified(); + SfxGetpApp()->Broadcast( SfxHint( SfxHintId::ScTablesChanged ) ); + + return true; +} + +IMPL_LINK( ScDocShell, RefreshDBDataHdl, Timer*, pRefreshTimer, void ) +{ + ScDBDocFunc aFunc(*this); + + ScDBData* pDBData = static_cast<ScDBData*>(pRefreshTimer); + ScImportParam aImportParam; + pDBData->GetImportParam( aImportParam ); + if (aImportParam.bImport && !pDBData->HasImportSelection()) + { + ScRange aRange; + pDBData->GetArea( aRange ); + bool bContinue = aFunc.DoImport( aRange.aStart.Tab(), aImportParam, nullptr ); //! Api-Flag as parameter + // internal operations (sort, query, subtotal) only if no error + if (bContinue) + { + aFunc.RepeatDB( pDBData->GetName(), true, true ); + RefreshPivotTables(aRange); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |