/* -*- 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 #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; namespace { void collectUIInformation(std::map&& aParameters, const OUString& action) { EventDescription aDescription; aDescription.aID = "grid_window"; aDescription.aAction = action; aDescription.aParameters = std::move(aParameters); aDescription.aParent = "MainWindow"; aDescription.aKeyWord = "ScGridWinUIObject"; UITestLogger::getInstance().logEvent(aDescription); } } // GlobalName of writer-DocShell from comphelper/classids.hxx // C U T void ScViewFunc::CutToClip() { UpdateInputLine(); ScEditableTester aTester( this ); if (!aTester.IsEditable()) // selection editable? { ErrorMessage( aTester.GetMessageId() ); return; } ScRange aRange; // delete this range if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE ) { ScDocument& rDoc = GetViewData().GetDocument(); ScDocShell* pDocSh = GetViewData().GetDocShell(); ScMarkData& rMark = GetViewData().GetMarkData(); const bool bRecord(rDoc.IsUndoEnabled()); // Undo/Redo ScDocShellModificator aModificator( *pDocSh ); if ( !rMark.IsMarked() && !rMark.IsMultiMarked() ) // mark the range if not marked yet { DoneBlockMode(); InitOwnBlockMode( aRange ); rMark.SetMarkArea( aRange ); MarkDataChanged(); } CopyToClip( nullptr, true, false, true/*bIncludeObjects*/ ); // copy to clipboard ScAddress aOldEnd( aRange.aEnd ); // combined cells in this range? rDoc.ExtendMerge( aRange, true ); ScDocumentUniquePtr pUndoDoc; if ( bRecord ) { pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); pUndoDoc->InitUndoSelected( rDoc, rMark ); // all sheets - CopyToDocument skips those that don't exist in pUndoDoc ScRange aCopyRange = aRange; aCopyRange.aStart.SetTab(0); aCopyRange.aEnd.SetTab(rDoc.GetTableCount()-1); rDoc.CopyToDocument( aCopyRange, (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc ); rDoc.BeginDrawUndo(); } sal_uInt16 nExtFlags = 0; pDocSh->UpdatePaintExt( nExtFlags, aRange ); rMark.MarkToMulti(); rDoc.DeleteSelection( InsertDeleteFlags::ALL, rMark ); rDoc.DeleteObjectsInSelection( rMark ); rMark.MarkToSimple(); if ( !AdjustRowHeight( aRange.aStart.Row(), aRange.aEnd.Row(), true ) ) pDocSh->PostPaint( aRange, PaintPartFlags::Grid, nExtFlags ); if ( bRecord ) // Draw-Undo now available pDocSh->GetUndoManager()->AddUndoAction( std::make_unique( pDocSh, aRange, aOldEnd, rMark, std::move(pUndoDoc) ) ); aModificator.SetDocumentModified(); pDocSh->UpdateOle(GetViewData()); CellContentChanged(); OUString aStartAddress = aRange.aStart.GetColRowString(); OUString aEndAddress = aRange.aEnd.GetColRowString(); collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "CUT"); } else ErrorMessage( STR_NOMULTISELECT ); } // C O P Y bool ScViewFunc::CopyToClip( ScDocument* pClipDoc, bool bCut, bool bApi, bool bIncludeObjects, bool bStopEdit ) { ScRange aRange; ScMarkType eMarkType = GetViewData().GetSimpleArea( aRange ); ScMarkData& rMark = GetViewData().GetMarkData(); bool bDone = false; if ( eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED ) { ScRangeList aRangeList( aRange ); bDone = CopyToClip( pClipDoc, aRangeList, bCut, bApi, bIncludeObjects, bStopEdit ); } else if (eMarkType == SC_MARK_MULTI) { ScRangeList aRangeList; rMark.MarkToSimple(); rMark.FillRangeListWithMarks(&aRangeList, false); bDone = CopyToClip( pClipDoc, aRangeList, bCut, bApi, bIncludeObjects, bStopEdit ); } else { if (!bApi) ErrorMessage(STR_NOMULTISELECT); } if( !bCut ){ OUString aStartAddress = aRange.aStart.GetColRowString(); OUString aEndAddress = aRange.aEnd.GetColRowString(); collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "COPY"); } return bDone; } // Copy the content of the Range into clipboard. bool ScViewFunc::CopyToClip( ScDocument* pClipDoc, const ScRangeList& rRanges, bool bCut, bool bApi, bool bIncludeObjects, bool bStopEdit ) { if ( rRanges.empty() ) return false; if ( bStopEdit ) UpdateInputLine(); bool bDone; if (rRanges.size() > 1) // isMultiRange bDone = CopyToClipMultiRange(pClipDoc, rRanges, bCut, bApi, bIncludeObjects); else bDone = CopyToClipSingleRange(pClipDoc, rRanges, bCut, bIncludeObjects); return bDone; } bool ScViewFunc::CopyToClipSingleRange( ScDocument* pClipDoc, const ScRangeList& rRanges, bool bCut, bool bIncludeObjects ) { ScRange aRange = rRanges[0]; ScClipParam aClipParam( aRange, bCut ); aClipParam.maRanges = rRanges; ScDocument& rDoc = GetViewData().GetDocument(); ScMarkData& rMark = GetViewData().GetMarkData(); if (rDoc.HasSelectedBlockMatrixFragment( aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(), rMark ) ) return false; bool bSysClip = false; std::shared_ptr pSysClipDoc; if ( !pClipDoc ) // no clip doc specified { // Create one (deleted by ScTransferObj). pSysClipDoc = std::make_shared( SCDOCMODE_CLIP ); pClipDoc = pSysClipDoc.get(); bSysClip = true; // and copy into system } if ( !bCut ) { ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack(); if ( pChangeTrack ) pChangeTrack->ResetLastCut(); } if ( bSysClip && bIncludeObjects ) { bool bAnyOle = rDoc.HasOLEObjectsInArea( aRange ); // Update ScGlobal::xDrawClipDocShellRef. ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle, pSysClipDoc ) ); } // is this necessary?, will setting the doc id upset the // following paste operation with range? would be nicer to just set this always // and lose the 'if' above aClipParam.setSourceDocID( rDoc.GetDocumentID() ); if (SfxObjectShell* pObjectShell = rDoc.GetDocumentShell()) { // Copy document properties from pObjectShell to pClipDoc (to its clip options, as it has no object shell). uno::Reference xCloneable(pObjectShell->getDocProperties(), uno::UNO_QUERY_THROW); std::unique_ptr pOptions(new ScClipOptions); pOptions->m_xDocumentProperties.set(xCloneable->createClone(), uno::UNO_QUERY); pClipDoc->SetClipOptions(std::move(pOptions)); } rDoc.CopyToClip( aClipParam, pClipDoc, &rMark, false, bIncludeObjects ); if (ScDrawLayer* pDrawLayer = pClipDoc->GetDrawLayer()) { ScClipParam& rClipDocClipParam = pClipDoc->GetClipParam(); ScRangeListVector& rRangesVector = rClipDocClipParam.maProtectedChartRangesVector; SCTAB nTabCount = pClipDoc->GetTableCount(); for ( SCTAB nTab = 0; nTab < nTabCount; ++nTab ) { SdrPage* pPage = pDrawLayer->GetPage( static_cast< sal_uInt16 >( nTab ) ); if ( pPage ) { ScChartHelper::FillProtectedChartRangesVector( rRangesVector, rDoc, pPage ); } } } if ( bSysClip ) { ScDrawLayer::SetGlobalDrawPersist(nullptr); ScGlobal::SetClipDocName( rDoc.GetDocumentShell()->GetTitle( SFX_TITLE_FULLNAME ) ); } pClipDoc->ExtendMerge( aRange, true ); if ( bSysClip ) { ScDocShell* pDocSh = GetViewData().GetDocShell(); TransferableObjectDescriptor aObjDesc; pDocSh->FillTransferableObjectDescriptor( aObjDesc ); aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass(); // maSize is set in ScTransferObj ctor rtl::Reference pTransferObj(new ScTransferObj( pSysClipDoc, aObjDesc )); if ( ScGlobal::xDrawClipDocShellRef.is() ) { SfxObjectShellRef aPersistRef( ScGlobal::xDrawClipDocShellRef.get() ); pTransferObj->SetDrawPersist( aPersistRef );// keep persist for ole objects alive } pTransferObj->CopyToClipboard( GetActiveWin() ); } return true; } bool ScViewFunc::CopyToClipMultiRange( const ScDocument* pInputClipDoc, const ScRangeList& rRanges, bool bCut, bool bApi, bool bIncludeObjects ) { if (bCut) { // We don't support cutting of multi-selections. if (!bApi) ErrorMessage(STR_NOMULTISELECT); return false; } if (pInputClipDoc) { // TODO: What's this for? if (!bApi) ErrorMessage(STR_NOMULTISELECT); return false; } ScClipParam aClipParam( rRanges[0], bCut ); aClipParam.maRanges = rRanges; ScDocument& rDoc = GetViewData().GetDocument(); ScMarkData& rMark = GetViewData().GetMarkData(); bool bDone = false; bool bSuccess = false; aClipParam.mbCutMode = false; do { ScDocumentUniquePtr pDocClip(new ScDocument(SCDOCMODE_CLIP)); // Check for geometrical feasibility of the ranges. bool bValidRanges = true; ScRange const * p = &aClipParam.maRanges.front(); SCCOL nPrevColDelta = 0; SCROW nPrevRowDelta = 0; SCCOL nPrevCol = p->aStart.Col(); SCROW nPrevRow = p->aStart.Row(); SCCOL nPrevColSize = p->aEnd.Col() - p->aStart.Col() + 1; SCROW nPrevRowSize = p->aEnd.Row() - p->aStart.Row() + 1; for ( size_t i = 1; i < aClipParam.maRanges.size(); ++i ) { p = &aClipParam.maRanges[i]; if ( rDoc.HasSelectedBlockMatrixFragment( p->aStart.Col(), p->aStart.Row(), p->aEnd.Col(), p->aEnd.Row(), rMark) ) { if (!bApi) ErrorMessage(STR_MATRIXFRAGMENTERR); return false; } SCCOL nColDelta = p->aStart.Col() - nPrevCol; SCROW nRowDelta = p->aStart.Row() - nPrevRow; if ((nColDelta && nRowDelta) || (nPrevColDelta && nRowDelta) || (nPrevRowDelta && nColDelta)) { bValidRanges = false; break; } if (aClipParam.meDirection == ScClipParam::Unspecified) { if (nColDelta) aClipParam.meDirection = ScClipParam::Column; if (nRowDelta) aClipParam.meDirection = ScClipParam::Row; } SCCOL nColSize = p->aEnd.Col() - p->aStart.Col() + 1; SCROW nRowSize = p->aEnd.Row() - p->aStart.Row() + 1; if (aClipParam.meDirection == ScClipParam::Column && nRowSize != nPrevRowSize) { // column-oriented ranges must have identical row size. bValidRanges = false; break; } if (aClipParam.meDirection == ScClipParam::Row && nColSize != nPrevColSize) { // likewise, row-oriented ranges must have identical // column size. bValidRanges = false; break; } nPrevCol = p->aStart.Col(); nPrevRow = p->aStart.Row(); nPrevColDelta = nColDelta; nPrevRowDelta = nRowDelta; nPrevColSize = nColSize; nPrevRowSize = nRowSize; } if (!bValidRanges) break; rDoc.CopyToClip(aClipParam, pDocClip.get(), &rMark, false, bIncludeObjects ); ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack(); if ( pChangeTrack ) pChangeTrack->ResetLastCut(); // no more cut-mode ScDocShell* pDocSh = GetViewData().GetDocShell(); TransferableObjectDescriptor aObjDesc; pDocSh->FillTransferableObjectDescriptor( aObjDesc ); aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass(); // maSize is set in ScTransferObj ctor rtl::Reference pTransferObj(new ScTransferObj( std::move(pDocClip), aObjDesc )); if ( ScGlobal::xDrawClipDocShellRef.is() ) { SfxObjectShellRef aPersistRef( ScGlobal::xDrawClipDocShellRef.get() ); pTransferObj->SetDrawPersist( aPersistRef ); // keep persist for ole objects alive } pTransferObj->CopyToClipboard( GetActiveWin() ); // system clipboard bSuccess = true; } while (false); if (!bSuccess && !bApi) ErrorMessage(STR_NOMULTISELECT); bDone = bSuccess; return bDone; } rtl::Reference ScViewFunc::CopyToTransferable() { ScRange aRange; if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE ) { ScDocument& rDoc = GetViewData().GetDocument(); ScMarkData& rMark = GetViewData().GetMarkData(); if ( !rDoc.HasSelectedBlockMatrixFragment( aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(), rMark ) ) { ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP )); // create one (deleted by ScTransferObj) bool bAnyOle = rDoc.HasOLEObjectsInArea( aRange, &rMark ); ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle ) ); ScClipParam aClipParam(aRange, false); rDoc.CopyToClip(aClipParam, pClipDoc.get(), &rMark, false, true); ScDrawLayer::SetGlobalDrawPersist(nullptr); pClipDoc->ExtendMerge( aRange, true ); ScDocShell* pDocSh = GetViewData().GetDocShell(); TransferableObjectDescriptor aObjDesc; pDocSh->FillTransferableObjectDescriptor( aObjDesc ); aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass(); return new ScTransferObj( std::move(pClipDoc), aObjDesc ); } } return nullptr; } // P A S T E void ScViewFunc::PasteDraw() { ScViewData& rViewData = GetViewData(); SCCOL nPosX = rViewData.GetCurX(); SCROW nPosY = rViewData.GetCurY(); vcl::Window* pWin = GetActiveWin(); Point aPos = pWin->PixelToLogic( rViewData.GetScrPos( nPosX, nPosY, rViewData.GetActivePart() ) ); const ScDrawTransferObj* pDrawClip = ScDrawTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(rViewData.GetActiveWin())); if (pDrawClip) { const OUString& aSrcShellID = pDrawClip->GetShellID(); OUString aDestShellID = SfxObjectShell::CreateShellID(rViewData.GetDocShell()); PasteDraw(aPos, pDrawClip->GetModel(), false, aSrcShellID, aDestShellID); } } void ScViewFunc::PasteFromSystem() { UpdateInputLine(); vcl::Window* pWin = GetActiveWin(); css::uno::Reference xTransferable2(ScTabViewShell::GetClipData(pWin)); const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(xTransferable2); // keep a reference in case the clipboard is changed during PasteFromClip const ScDrawTransferObj* pDrawClip = ScDrawTransferObj::GetOwnClipboard(xTransferable2); if (pOwnClip) { PasteFromClip( InsertDeleteFlags::ALL, pOwnClip->GetDocument(), ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE, true ); // allow warning dialog } else if (pDrawClip) PasteDraw(); else { TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) ); { SotClipboardFormatId nBiff8 = SotExchange::RegisterFormatName("Biff8"); SotClipboardFormatId nBiff5 = SotExchange::RegisterFormatName("Biff5"); SotClipboardFormatId nFormat; // output param for GetExchangeAction sal_uInt8 nEventAction; // output param for GetExchangeAction uno::Reference xTransferable( aDataHelper.GetXTransferable() ); sal_uInt8 nAction = SotExchange::GetExchangeAction( aDataHelper.GetDataFlavorExVector(), SotExchangeDest::SCDOC_FREE_AREA, EXCHG_IN_ACTION_COPY, EXCHG_IN_ACTION_DEFAULT, nFormat, nEventAction, SotClipboardFormatId::NONE, &xTransferable ); if ( nAction != EXCHG_INOUT_ACTION_NONE ) { switch( nAction ) { case EXCHG_OUT_ACTION_INSERT_SVXB: case EXCHG_OUT_ACTION_INSERT_GDIMETAFILE: case EXCHG_OUT_ACTION_INSERT_BITMAP: case EXCHG_OUT_ACTION_INSERT_GRAPH: // SotClipboardFormatId::BITMAP // SotClipboardFormatId::PNG // SotClipboardFormatId::GDIMETAFILE // SotClipboardFormatId::SVXB PasteFromSystem(nFormat); break; default: nAction = EXCHG_INOUT_ACTION_NONE; } } if ( nAction == EXCHG_INOUT_ACTION_NONE ) { // first SvDraw-model, then drawing // (only one drawing is allowed) if (aDataHelper.HasFormat( SotClipboardFormatId::DRAWING )) { // special case for tables from drawing if( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ) { PasteFromSystem( SotClipboardFormatId::RTF ); } else if( aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) { PasteFromSystem( SotClipboardFormatId::RICHTEXT ); } else { PasteFromSystem( SotClipboardFormatId::DRAWING ); } } else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE )) { // If it's a Writer object, insert RTF instead of OLE // Else, if the class id is all-zero, and SYLK is available, // it probably is spreadsheet cells that have been put // on the clipboard by OOo, so use the SYLK. (fdo#31077) bool bDoRtf = false; TransferableObjectDescriptor aObjDesc; if( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) ) { bDoRtf = ( ( aObjDesc.maClassName == SvGlobalName( SO3_SW_CLASSID ) || aObjDesc.maClassName == SvGlobalName( SO3_SWWEB_CLASSID ) ) && ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) ); } if ( bDoRtf ) PasteFromSystem( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT ); else if ( aObjDesc.maClassName == SvGlobalName( 0,0,0,0,0,0,0,0,0,0,0 ) && aDataHelper.HasFormat( SotClipboardFormatId::SYLK )) PasteFromSystem( SotClipboardFormatId::SYLK ); else PasteFromSystem( SotClipboardFormatId::EMBED_SOURCE ); } else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE )) PasteFromSystem( SotClipboardFormatId::LINK_SOURCE ); // the following format can not affect scenario from #89579# else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE )) PasteFromSystem( SotClipboardFormatId::EMBEDDED_OBJ_OLE ); // SotClipboardFormatId::PRIVATE no longer here (can't work if pOwnClip is NULL) else if (aDataHelper.HasFormat(nBiff8)) // before xxx_OLE formats PasteFromSystem(nBiff8); else if (aDataHelper.HasFormat(nBiff5)) PasteFromSystem(nBiff5); else if (aDataHelper.HasFormat(SotClipboardFormatId::RTF)) PasteFromSystem(SotClipboardFormatId::RTF); else if (aDataHelper.HasFormat(SotClipboardFormatId::RICHTEXT)) PasteFromSystem(SotClipboardFormatId::RICHTEXT); else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML)) PasteFromSystem(SotClipboardFormatId::HTML); else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML_SIMPLE)) PasteFromSystem(SotClipboardFormatId::HTML_SIMPLE); else if (aDataHelper.HasFormat(SotClipboardFormatId::SYLK)) PasteFromSystem(SotClipboardFormatId::SYLK); else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING_TSVC)) PasteFromSystem(SotClipboardFormatId::STRING_TSVC); else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING)) PasteFromSystem(SotClipboardFormatId::STRING); // xxx_OLE formats come last, like in SotExchange tables else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE )) PasteFromSystem( SotClipboardFormatId::EMBED_SOURCE_OLE ); else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE )) PasteFromSystem( SotClipboardFormatId::LINK_SOURCE_OLE ); } } } // no exception-> SID_PASTE has FastCall-flag from idl // will be called in case of empty clipboard (#42531#) } void ScViewFunc::PasteFromTransferable( const uno::Reference& rxTransferable ) { uno::Reference xTunnel( rxTransferable, uno::UNO_QUERY ); if (auto pOwnClip = comphelper::getFromUnoTunnel(xTunnel)) { PasteFromClip( InsertDeleteFlags::ALL, pOwnClip->GetDocument(), ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE, true ); // allow warning dialog } else if (auto pDrawClip = comphelper::getFromUnoTunnel(xTunnel)) { ScViewData& rViewData = GetViewData(); SCCOL nPosX = rViewData.GetCurX(); SCROW nPosY = rViewData.GetCurY(); vcl::Window* pWin = GetActiveWin(); Point aPos = pWin->PixelToLogic( rViewData.GetScrPos( nPosX, nPosY, rViewData.GetActivePart() ) ); PasteDraw( aPos, pDrawClip->GetModel(), false, pDrawClip->GetShellID(), SfxObjectShell::CreateShellID(rViewData.GetDocShell())); } else { TransferableDataHelper aDataHelper( rxTransferable ); SotClipboardFormatId nBiff8 = SotExchange::RegisterFormatName("Biff8"); SotClipboardFormatId nBiff5 = SotExchange::RegisterFormatName("Biff5"); SotClipboardFormatId nFormatId = SotClipboardFormatId::NONE; // first SvDraw-model, then drawing // (only one drawing is allowed) if (aDataHelper.HasFormat( SotClipboardFormatId::DRAWING )) nFormatId = SotClipboardFormatId::DRAWING; else if (aDataHelper.HasFormat( SotClipboardFormatId::SVXB )) nFormatId = SotClipboardFormatId::SVXB; else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE )) { // If it's a Writer object, insert RTF instead of OLE bool bDoRtf = false; TransferableObjectDescriptor aObjDesc; if( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) ) { bDoRtf = ( ( aObjDesc.maClassName == SvGlobalName( SO3_SW_CLASSID ) || aObjDesc.maClassName == SvGlobalName( SO3_SWWEB_CLASSID ) ) && ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) )); } if ( bDoRtf ) nFormatId = aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT; else nFormatId = SotClipboardFormatId::EMBED_SOURCE; } else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE )) nFormatId = SotClipboardFormatId::LINK_SOURCE; // the following format can not affect scenario from #89579# else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE )) nFormatId = SotClipboardFormatId::EMBEDDED_OBJ_OLE; // SotClipboardFormatId::PRIVATE no longer here (can't work if pOwnClip is NULL) else if (aDataHelper.HasFormat(nBiff8)) // before xxx_OLE formats nFormatId = nBiff8; else if (aDataHelper.HasFormat(nBiff5)) nFormatId = nBiff5; else if (aDataHelper.HasFormat(SotClipboardFormatId::RTF)) nFormatId = SotClipboardFormatId::RTF; else if (aDataHelper.HasFormat(SotClipboardFormatId::RICHTEXT)) nFormatId = SotClipboardFormatId::RICHTEXT; else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML)) nFormatId = SotClipboardFormatId::HTML; else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML_SIMPLE)) nFormatId = SotClipboardFormatId::HTML_SIMPLE; else if (aDataHelper.HasFormat(SotClipboardFormatId::SYLK)) nFormatId = SotClipboardFormatId::SYLK; else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING_TSVC)) nFormatId = SotClipboardFormatId::STRING_TSVC; else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING)) nFormatId = SotClipboardFormatId::STRING; else if (aDataHelper.HasFormat(SotClipboardFormatId::GDIMETAFILE)) nFormatId = SotClipboardFormatId::GDIMETAFILE; else if (aDataHelper.HasFormat(SotClipboardFormatId::BITMAP)) nFormatId = SotClipboardFormatId::BITMAP; // xxx_OLE formats come last, like in SotExchange tables else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE )) nFormatId = SotClipboardFormatId::EMBED_SOURCE_OLE; else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE )) nFormatId = SotClipboardFormatId::LINK_SOURCE_OLE; else return; PasteDataFormat( nFormatId, aDataHelper.GetTransferable(), GetViewData().GetCurX(), GetViewData().GetCurY(), nullptr ); } } bool ScViewFunc::PasteFromSystem( SotClipboardFormatId nFormatId, bool bApi ) { UpdateInputLine(); bool bRet = true; vcl::Window* pWin = GetActiveWin(); // keep a reference in case the clipboard is changed during PasteFromClip const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pWin)); if ( nFormatId == SotClipboardFormatId::NONE && pOwnClip ) { PasteFromClip( InsertDeleteFlags::ALL, pOwnClip->GetDocument(), ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE, !bApi ); // allow warning dialog } else { TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) ); if ( !aDataHelper.GetTransferable().is() ) return false; SCCOL nPosX = 0; SCROW nPosY = 0; ScViewData& rViewData = GetViewData(); ScRange aRange; if ( rViewData.GetSimpleArea( aRange ) == SC_MARK_SIMPLE ) { nPosX = aRange.aStart.Col(); nPosY = aRange.aStart.Row(); } else { nPosX = rViewData.GetCurX(); nPosY = rViewData.GetCurY(); } bRet = PasteDataFormat( nFormatId, aDataHelper.GetTransferable(), nPosX, nPosY, nullptr, false, !bApi ); // allow warning dialog if ( !bRet && !bApi ) { ErrorMessage(STR_PASTE_ERROR); } else if (comphelper::LibreOfficeKit::isActive()) { SfxViewShell* pViewShell = rViewData.GetViewShell(); ScTabViewShell::notifyAllViewsSheetGeomInvalidation(pViewShell, true /* bColumns */, true /* bRows */, true /* bSizes */, false /* bHidden */, false /* bFiltered */, false /* bGroups */, rViewData.GetTabNo()); } } return bRet; } // P A S T E bool ScViewFunc::PasteOnDrawObjectLinked( const uno::Reference& rxTransferable, SdrObject& rHitObj) { TransferableDataHelper aDataHelper( rxTransferable ); if ( aDataHelper.HasFormat( SotClipboardFormatId::SVXB ) ) { tools::SvRef xStm; ScDrawView* pScDrawView = GetScDrawView(); if( pScDrawView && aDataHelper.GetSotStorageStream( SotClipboardFormatId::SVXB, xStm ) ) { Graphic aGraphic; TypeSerializer aSerializer(*xStm); aSerializer.readGraphic(aGraphic); const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP)); if(pScDrawView->ApplyGraphicToObject( rHitObj, aGraphic, aBeginUndo, "" )) { return true; } } } else if ( aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE ) ) { GDIMetaFile aMtf; ScDrawView* pScDrawView = GetScDrawView(); if( pScDrawView && aDataHelper.GetGDIMetaFile( SotClipboardFormatId::GDIMETAFILE, aMtf ) ) { const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP)); if(pScDrawView->ApplyGraphicToObject( rHitObj, Graphic(aMtf), aBeginUndo, "" )) { return true; } } } else if ( aDataHelper.HasFormat( SotClipboardFormatId::BITMAP ) || aDataHelper.HasFormat( SotClipboardFormatId::PNG ) ) { BitmapEx aBmpEx; ScDrawView* pScDrawView = GetScDrawView(); if( pScDrawView && aDataHelper.GetBitmapEx( SotClipboardFormatId::BITMAP, aBmpEx ) ) { const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP)); if(pScDrawView->ApplyGraphicToObject( rHitObj, Graphic(aBmpEx), aBeginUndo, "" )) { return true; } } } return false; } static bool lcl_SelHasAttrib( const ScDocument& rDoc, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rTabSelection, HasAttrFlags nMask ) { return std::any_of(rTabSelection.begin(), rTabSelection.end(), [&](const SCTAB& rTab) { return rDoc.HasAttrib( nCol1, nRow1, rTab, nCol2, nRow2, rTab, nMask ); }); } // paste into sheet: // internal paste namespace { bool checkDestRangeForOverwrite(const ScRangeList& rDestRanges, const ScDocument& rDoc, const ScMarkData& rMark, weld::Window* pParentWnd) { bool bIsEmpty = true; size_t nRangeSize = rDestRanges.size(); for (const auto& rTab : rMark) { for (size_t i = 0; i < nRangeSize && bIsEmpty; ++i) { const ScRange& rRange = rDestRanges[i]; bIsEmpty = rDoc.IsBlockEmpty( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), rTab ); } if (!bIsEmpty) break; } if (!bIsEmpty) { ScReplaceWarnBox aBox(pParentWnd); if (aBox.run() != RET_YES) { // changing the configuration is within the ScReplaceWarnBox return false; } } return true; } } bool ScViewFunc::PasteFromClip( InsertDeleteFlags nFlags, ScDocument* pClipDoc, ScPasteFunc nFunction, bool bSkipEmptyCells, bool bTranspose, bool bAsLink, InsCellCmd eMoveMode, InsertDeleteFlags nUndoExtraFlags, bool bAllowDialogs ) { if (!pClipDoc) { OSL_FAIL("PasteFromClip: pClipDoc=0 not allowed"); return false; } if (GetViewData().SelectionForbidsPaste(pClipDoc)) return false; // undo: save all or no content InsertDeleteFlags nContFlags = InsertDeleteFlags::NONE; if (nFlags & InsertDeleteFlags::CONTENTS) nContFlags |= InsertDeleteFlags::CONTENTS; if (nFlags & InsertDeleteFlags::ATTRIB) nContFlags |= InsertDeleteFlags::ATTRIB; // move attributes to undo without copying them from clip to doc InsertDeleteFlags nUndoFlags = nContFlags; if (nUndoExtraFlags & InsertDeleteFlags::ATTRIB) nUndoFlags |= InsertDeleteFlags::ATTRIB; // do not copy note captions into undo document nUndoFlags |= InsertDeleteFlags::NOCAPTIONS; ScClipParam& rClipParam = pClipDoc->GetClipParam(); if (rClipParam.isMultiRange()) { // Source data is multi-range. return PasteMultiRangesFromClip(nFlags, pClipDoc, nFunction, bSkipEmptyCells, bTranspose, bAsLink, bAllowDialogs, eMoveMode, nUndoFlags); } ScMarkData& rMark = GetViewData().GetMarkData(); if (rMark.IsMultiMarked()) { // Source data is single-range but destination is multi-range. return PasteFromClipToMultiRanges( nFlags, pClipDoc, nFunction, bSkipEmptyCells, bTranspose, bAsLink, bAllowDialogs, eMoveMode, nUndoFlags); } bool bCutMode = pClipDoc->IsCutMode(); // if transposing, take from original clipdoc bool bIncludeFiltered = bCutMode; // paste drawing: also if InsertDeleteFlags::NOTE is set (to create drawing layer for note captions) bool bPasteDraw = ( pClipDoc->GetDrawLayer() && ( nFlags & (InsertDeleteFlags::OBJECTS|InsertDeleteFlags::NOTE) ) ); ScDocShellRef aTransShellRef; // for objects in xTransClip - must remain valid as long as xTransClip ScDocument* pOrigClipDoc = nullptr; ScDocumentUniquePtr xTransClip; if ( bTranspose ) { SCCOL nX; SCROW nY; // include filtered rows until TransposeClip can skip them pClipDoc->GetClipArea( nX, nY, true ); if ( nY > static_cast(pClipDoc->MaxCol()) ) // too many lines for transpose { ErrorMessage(STR_PASTE_FULL); return false; } pOrigClipDoc = pClipDoc; // refs if ( bPasteDraw ) { aTransShellRef = new ScDocShell; // DocShell needs a Ref immediately aTransShellRef->DoInitNew(); } ScDrawLayer::SetGlobalDrawPersist( aTransShellRef.get() ); xTransClip.reset( new ScDocument( SCDOCMODE_CLIP )); pClipDoc->TransposeClip(xTransClip.get(), nFlags, bAsLink, bIncludeFiltered); pClipDoc = xTransClip.get(); ScDrawLayer::SetGlobalDrawPersist(nullptr); } // TODO: position this call better for performance. ResetAutoSpellForContentChange(); SCCOL nStartCol; SCROW nStartRow; SCTAB nStartTab; SCCOL nEndCol; SCROW nEndRow; SCTAB nEndTab; SCCOL nClipSizeX; SCROW nClipSizeY; pClipDoc->GetClipArea( nClipSizeX, nClipSizeY, true ); // size in clipboard doc // size in target doc: include filtered rows only if CutMode is set SCCOL nDestSizeX; SCROW nDestSizeY; pClipDoc->GetClipArea( nDestSizeX, nDestSizeY, bIncludeFiltered ); ScDocument& rDoc = GetViewData().GetDocument(); ScDocShell* pDocSh = GetViewData().GetDocShell(); SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager(); const bool bRecord(rDoc.IsUndoEnabled()); ScDocShellModificator aModificator( *pDocSh ); ScRange aMarkRange; ScMarkData aFilteredMark( rMark); // local copy for all modifications ScMarkType eMarkType = GetViewData().GetSimpleArea( aMarkRange, aFilteredMark); bool bMarkIsFiltered = (eMarkType == SC_MARK_SIMPLE_FILTERED); bool bNoPaste = ((eMarkType != SC_MARK_SIMPLE && !bMarkIsFiltered) || (bMarkIsFiltered && (eMoveMode != INS_NONE || bAsLink))); if (!bNoPaste) { if (!rMark.IsMarked()) { // Create a selection with clipboard row count and check that for // filtered. nStartCol = GetViewData().GetCurX(); nStartRow = GetViewData().GetCurY(); nStartTab = GetViewData().GetTabNo(); nEndCol = nStartCol + nDestSizeX; nEndRow = nStartRow + nDestSizeY; nEndTab = nStartTab; aMarkRange = ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab); if (ScViewUtil::HasFiltered(aMarkRange, rDoc)) { bMarkIsFiltered = true; // Fit to clipboard's row count unfiltered rows. If there is no // fit assume that pasting is not possible. Note that nDestSizeY is // size-1 (difference). if (!ScViewUtil::FitToUnfilteredRows(aMarkRange, rDoc, nDestSizeY+1)) bNoPaste = true; } aFilteredMark.SetMarkArea( aMarkRange); } else { // Expand the marked area when the destination area is larger than the // current selection, to get the undo do the right thing. (i#106711) ScRange aRange = aFilteredMark.GetMarkArea(); if( (aRange.aEnd.Col() - aRange.aStart.Col()) < nDestSizeX ) { aRange.aEnd.SetCol(aRange.aStart.Col() + nDestSizeX); aFilteredMark.SetMarkArea(aRange); } } } if (bNoPaste) { ErrorMessage(STR_MSSG_PASTEFROMCLIP_0); return false; } SCROW nUnfilteredRows = aMarkRange.aEnd.Row() - aMarkRange.aStart.Row() + 1; ScRangeList aRangeList; if (bMarkIsFiltered) { ScViewUtil::UnmarkFiltered(aFilteredMark, rDoc); aFilteredMark.FillRangeListWithMarks( &aRangeList, false); nUnfilteredRows = 0; size_t ListSize = aRangeList.size(); for ( size_t i = 0; i < ListSize; ++i ) { ScRange & r = aRangeList[i]; nUnfilteredRows += r.aEnd.Row() - r.aStart.Row() + 1; } #if 0 /* This isn't needed but could be a desired restriction. */ // For filtered, destination rows have to be an exact multiple of // source rows. Note that nDestSizeY is size-1 (difference), so // nDestSizeY==0 fits always. if ((nUnfilteredRows % (nDestSizeY+1)) != 0) { /* FIXME: this should be a more descriptive error message then. */ ErrorMessage(STR_MSSG_PASTEFROMCLIP_0); return false; } #endif } // Also for a filtered selection the area is used, for undo et al. if ( aFilteredMark.IsMarked() || bMarkIsFiltered ) { aMarkRange.GetVars( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab); SCCOL nBlockAddX = nEndCol-nStartCol; SCROW nBlockAddY = nEndRow-nStartRow; // request, if the selection is greater than one row/column, but smaller // as the Clipboard (then inserting is done beyond the selection) // ClipSize is not size, but difference if ( ( nBlockAddX != 0 && nBlockAddX < nDestSizeX ) || ( nBlockAddY != 0 && nBlockAddY < nDestSizeY ) || ( bMarkIsFiltered && nUnfilteredRows < nDestSizeY+1 ) ) { ScWaitCursorOff aWaitOff( GetFrameWin() ); OUString aMessage = ScResId( STR_PASTE_BIGGER ); std::unique_ptr xQueryBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(), VclMessageType::Question, VclButtonsType::YesNo, aMessage)); xQueryBox->set_default_response(RET_NO); if (xQueryBox->run() != RET_YES) { return false; } } if (nBlockAddX <= nDestSizeX) nEndCol = nStartCol + nDestSizeX; if (nBlockAddY <= nDestSizeY) { nEndRow = nStartRow + nDestSizeY; if (bMarkIsFiltered || nEndRow > aMarkRange.aEnd.Row()) { // Same as above if nothing was marked: re-fit selection to // unfiltered rows. Extending the selection actually may // introduce filtered rows where there weren't any before, so // we also need to test for that. aMarkRange = ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab); if (bMarkIsFiltered || ScViewUtil::HasFiltered(aMarkRange, rDoc)) { bMarkIsFiltered = true; // Worst case: all rows up to the end of the sheet are filtered. if (!ScViewUtil::FitToUnfilteredRows(aMarkRange, rDoc, nDestSizeY+1)) { ErrorMessage(STR_PASTE_FULL); return false; } } aMarkRange.GetVars( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab); aFilteredMark.SetMarkArea( aMarkRange); if (bMarkIsFiltered) { ScViewUtil::UnmarkFiltered(aFilteredMark, rDoc); aFilteredMark.FillRangeListWithMarks( &aRangeList, true); } } } } else { nStartCol = GetViewData().GetCurX(); nStartRow = GetViewData().GetCurY(); nStartTab = GetViewData().GetTabNo(); nEndCol = nStartCol + nDestSizeX; nEndRow = nStartRow + nDestSizeY; nEndTab = nStartTab; } bool bOffLimits = !rDoc.ValidCol(nEndCol) || !rDoc.ValidRow(nEndRow); // target-range, as displayed: ScRange aUserRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ); // should lines be inserted? // ( too large nEndCol/nEndRow are detected below) bool bInsertCells = ( eMoveMode != INS_NONE && !bOffLimits ); if ( bInsertCells ) { // Instead of EnterListAction, the paste undo action is merged into the // insert action, so Repeat can insert the right cells MarkRange( aUserRange ); // set through CopyFromClip // CutMode is reset on insertion of cols/rows but needed again on cell move bool bCut = pClipDoc->IsCutMode(); if (!InsertCells( eMoveMode, bRecord, true )) // is inserting possible? { return false; // #i21036# EnterListAction isn't used, and InsertCells doesn't insert // its undo action on failure, so no undo handling is needed here } if ( bCut ) pClipDoc->SetCutMode( bCut ); } else if (!bOffLimits) { bool bAskIfNotEmpty = bAllowDialogs && ( nFlags & InsertDeleteFlags::CONTENTS ) && nFunction == ScPasteFunc::NONE && SC_MOD()->GetInputOptions().GetReplaceCellsWarn(); if ( bAskIfNotEmpty ) { ScRangeList aTestRanges(aUserRange); if (!checkDestRangeForOverwrite(aTestRanges, rDoc, aFilteredMark, GetViewData().GetDialogParent())) return false; } } SCCOL nClipStartX; // enlarge clipboard-range SCROW nClipStartY; pClipDoc->GetClipStart( nClipStartX, nClipStartY ); SCCOL nUndoEndCol = nClipStartX + nClipSizeX; SCROW nUndoEndRow = nClipStartY + nClipSizeY; // end of source area in clipboard document bool bClipOver = false; // #i68690# ExtendMerge for the clip doc must be called with the clipboard's sheet numbers. // The same end column/row can be used for all calls because the clip doc doesn't contain // content outside the clip area. for (SCTAB nClipTab=0; nClipTab < pClipDoc->GetTableCount(); nClipTab++) if ( pClipDoc->HasTable(nClipTab) ) if ( pClipDoc->ExtendMerge( nClipStartX,nClipStartY, nUndoEndCol,nUndoEndRow, nClipTab ) ) bClipOver = true; nUndoEndCol -= nClipStartX + nClipSizeX; nUndoEndRow -= nClipStartY + nClipSizeY; // now contains only the difference added by ExtendMerge nUndoEndCol = sal::static_int_cast( nUndoEndCol + nEndCol ); nUndoEndRow = sal::static_int_cast( nUndoEndRow + nEndRow ); // destination area, expanded for merged cells if (nUndoEndCol>pClipDoc->MaxCol() || nUndoEndRow>pClipDoc->MaxRow()) { ErrorMessage(STR_PASTE_FULL); return false; } rDoc.ExtendMergeSel( nStartCol,nStartRow, nUndoEndCol,nUndoEndRow, aFilteredMark ); // check cell-protection ScEditableTester aTester( rDoc, nStartTab, nStartCol,nStartRow, nUndoEndCol,nUndoEndRow ); if (!aTester.IsEditable()) { ErrorMessage(aTester.GetMessageId()); return false; } //! check overlapping //! just check truly intersection !!!!!!! ScDocFunc& rDocFunc = pDocSh->GetDocFunc(); if ( bRecord ) { OUString aUndo = ScResId( pClipDoc->IsCutMode() ? STR_UNDO_MOVE : STR_UNDO_COPY ); pUndoMgr->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() ); } if (bClipOver) if (lcl_SelHasAttrib( rDoc, nStartCol,nStartRow, nUndoEndCol,nUndoEndRow, aFilteredMark, HasAttrFlags::Overlapped )) { // "Cell merge not possible if cells already merged" ScDocAttrIterator aIter( rDoc, nStartTab, nStartCol, nStartRow, nUndoEndCol, nUndoEndRow ); const ScPatternAttr* pPattern = nullptr; SCCOL nCol = -1; SCROW nRow1 = -1; SCROW nRow2 = -1; while ( ( pPattern = aIter.GetNext( nCol, nRow1, nRow2 ) ) != nullptr ) { const ScMergeAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE); const ScMergeFlagAttr& rMergeFlagAttr = pPattern->GetItem(ATTR_MERGE_FLAG); if (rMergeFlag.IsMerged() || rMergeFlagAttr.IsOverlapped()) { ScRange aRange(nCol, nRow1, nStartTab); rDoc.ExtendOverlapped(aRange); rDoc.ExtendMerge(aRange, true); rDocFunc.UnmergeCells(aRange, bRecord, nullptr /*TODO: should pass combined UndoDoc if bRecord*/); } } } if ( !bCutMode ) { ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack(); if ( pChangeTrack ) pChangeTrack->ResetLastCut(); // no more cut-mode } bool bColInfo = ( nStartRow==0 && nEndRow==rDoc.MaxRow() ); bool bRowInfo = ( nStartCol==0 && nEndCol==rDoc.MaxCol() ); ScDocumentUniquePtr pUndoDoc; std::unique_ptr pRefUndoDoc; std::unique_ptr pUndoData; if ( bRecord ) { pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); pUndoDoc->InitUndoSelected( rDoc, aFilteredMark, bColInfo, bRowInfo ); // all sheets - CopyToDocument skips those that don't exist in pUndoDoc SCTAB nTabCount = rDoc.GetTableCount(); rDoc.CopyToDocument( nStartCol, nStartRow, 0, nUndoEndCol, nUndoEndRow, nTabCount-1, nUndoFlags, false, *pUndoDoc ); if ( bCutMode ) { pRefUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); pRefUndoDoc->InitUndo( rDoc, 0, nTabCount-1 ); pUndoData.reset(new ScRefUndoData( &rDoc )); } } sal_uInt16 nExtFlags = 0; pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ); // content before the change if (GetViewData().IsActive()) { DoneBlockMode(); InitOwnBlockMode( aUserRange ); } rMark.SetMarkArea( aUserRange ); MarkDataChanged(); // copy from clipboard // save original data in case of calculation ScDocumentUniquePtr pMixDoc; if (nFunction != ScPasteFunc::NONE) { bSkipEmptyCells = false; if ( nFlags & InsertDeleteFlags::CONTENTS ) { pMixDoc.reset(new ScDocument( SCDOCMODE_UNDO )); pMixDoc->InitUndo( rDoc, nStartTab, nEndTab ); rDoc.CopyToDocument(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab, InsertDeleteFlags::CONTENTS, false, *pMixDoc); } } /* Make draw layer and start drawing undo. - Needed before AdjustBlockHeight to track moved drawing objects. - Needed before rDoc.CopyFromClip to track inserted note caption objects. */ if ( bPasteDraw ) pDocSh->MakeDrawLayer(); if ( bRecord ) rDoc.BeginDrawUndo(); InsertDeleteFlags nNoObjFlags = nFlags & ~InsertDeleteFlags::OBJECTS; if (!bAsLink) { // copy normally (original range) rDoc.CopyFromClip( aUserRange, aFilteredMark, nNoObjFlags, pRefUndoDoc.get(), pClipDoc, true, false, bIncludeFiltered, bSkipEmptyCells, (bMarkIsFiltered ? &aRangeList : nullptr) ); // adapt refs manually in case of transpose if ( bTranspose && bCutMode && (nFlags & InsertDeleteFlags::CONTENTS) ) rDoc.UpdateTranspose( aUserRange.aStart, pOrigClipDoc, aFilteredMark, pRefUndoDoc.get() ); } else if (!bTranspose) { // copy with bAsLink=TRUE rDoc.CopyFromClip( aUserRange, aFilteredMark, nNoObjFlags, pRefUndoDoc.get(), pClipDoc, true, true, bIncludeFiltered, bSkipEmptyCells ); } else { // copy all content (TransClipDoc contains only formula) rDoc.CopyFromClip( aUserRange, aFilteredMark, nContFlags, pRefUndoDoc.get(), pClipDoc ); } // skipped rows and merged cells don't mix if ( !bIncludeFiltered && pClipDoc->HasClipFilteredRows() ) rDocFunc.UnmergeCells( aUserRange, false, nullptr ); rDoc.ExtendMergeSel( nStartCol, nStartRow, nEndCol, nEndRow, aFilteredMark, true ); // refresh // new range if ( pMixDoc ) // calculate with original data? { rDoc.MixDocument( aUserRange, nFunction, bSkipEmptyCells, *pMixDoc ); } pMixDoc.reset(); AdjustBlockHeight(); // update row heights before pasting objects ::std::vector< OUString > aExcludedChartNames; SdrPage* pPage = nullptr; if ( nFlags & InsertDeleteFlags::OBJECTS ) { ScDrawView* pScDrawView = GetScDrawView(); SdrModel* pModel = ( pScDrawView ? pScDrawView->GetModel() : nullptr ); pPage = ( pModel ? pModel->GetPage( static_cast< sal_uInt16 >( nStartTab ) ) : nullptr ); if ( pPage ) { ScChartHelper::GetChartNames( aExcludedChartNames, pPage ); } // Paste the drawing objects after the row heights have been updated. rDoc.CopyFromClip( aUserRange, aFilteredMark, InsertDeleteFlags::OBJECTS, pRefUndoDoc.get(), pClipDoc, true, false, bIncludeFiltered ); } pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ); // content after the change // if necessary, delete autofilter-heads if (bCutMode) if (rDoc.RefreshAutoFilter( nClipStartX,nClipStartY, nClipStartX+nClipSizeX, nClipStartY+nClipSizeY, nStartTab )) { pDocSh->PostPaint( ScRange(nClipStartX, nClipStartY, nStartTab, nClipStartX+nClipSizeX, nClipStartY, nStartTab), PaintPartFlags::Grid ); } //! remove block-range on RefUndoDoc !!! if ( bRecord ) { ScDocumentUniquePtr pRedoDoc; // copy redo data after appearance of the first undo // don't create Redo-Doc without RefUndoDoc if (pRefUndoDoc) { pRedoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); pRedoDoc->InitUndo( rDoc, nStartTab, nEndTab, bColInfo, bRowInfo ); // move adapted refs to Redo-Doc SCTAB nTabCount = rDoc.GetTableCount(); pRedoDoc->AddUndoTab( 0, nTabCount-1 ); rDoc.CopyUpdated( pRefUndoDoc.get(), pRedoDoc.get() ); // move old refs to Undo-Doc // not charts? pUndoDoc->AddUndoTab( 0, nTabCount-1 ); pRefUndoDoc->DeleteArea( nStartCol, nStartRow, nEndCol, nEndRow, aFilteredMark, InsertDeleteFlags::ALL ); pRefUndoDoc->CopyToDocument( 0,0,0, pUndoDoc->MaxCol(), pUndoDoc->MaxRow(), nTabCount-1, InsertDeleteFlags::FORMULA, false, *pUndoDoc ); pRefUndoDoc.reset(); } // DeleteUnchanged for pUndoData is in ScUndoPaste ctor, // UndoData for redo is made during first undo ScUndoPasteOptions aOptions; // store options for repeat aOptions.nFunction = nFunction; aOptions.bSkipEmptyCells = bSkipEmptyCells; aOptions.bTranspose = bTranspose; aOptions.bAsLink = bAsLink; aOptions.eMoveMode = eMoveMode; std::unique_ptr pUndo(new ScUndoPaste( pDocSh, ScRange(nStartCol, nStartRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab), aFilteredMark, std::move(pUndoDoc), std::move(pRedoDoc), nFlags | nUndoFlags, std::move(pUndoData), false, &aOptions )); // false = Redo data not yet copied if ( bInsertCells ) { // Merge the paste undo action into the insert action. // Use ScUndoWrapper so the ScUndoPaste pointer can be stored in the insert action. pUndoMgr->AddUndoAction( std::make_unique( std::move(pUndo) ), true ); } else pUndoMgr->AddUndoAction( std::move(pUndo) ); pUndoMgr->LeaveListAction(); } PaintPartFlags nPaint = PaintPartFlags::Grid; if (bColInfo) { nPaint |= PaintPartFlags::Top; nUndoEndCol = rDoc.MaxCol(); // just for drawing ! } if (bRowInfo) { nPaint |= PaintPartFlags::Left; nUndoEndRow = rDoc.MaxRow(); // just for drawing ! } pDocSh->PostPaint( ScRange(nStartCol, nStartRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab), nPaint, nExtFlags); // AdjustBlockHeight has already been called above aModificator.SetDocumentModified(); PostPasteFromClip(aUserRange, rMark); if ( nFlags & InsertDeleteFlags::OBJECTS ) { ScModelObj* pModelObj = comphelper::getFromUnoTunnel( pDocSh->GetModel() ); if ( pPage && pModelObj ) { bool bSameDoc = ( rClipParam.getSourceDocID() == rDoc.GetDocumentID() ); const ScRangeListVector& rProtectedChartRangesVector( rClipParam.maProtectedChartRangesVector ); ScChartHelper::CreateProtectedChartListenersAndNotify( rDoc, pPage, pModelObj, nStartTab, rProtectedChartRangesVector, aExcludedChartNames, bSameDoc ); } } OUString aStartAddress = aMarkRange.aStart.GetColRowString(); OUString aEndAddress = aMarkRange.aEnd.GetColRowString(); collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "PASTE"); return true; } bool ScViewFunc::PasteMultiRangesFromClip(InsertDeleteFlags nFlags, ScDocument* pClipDoc, ScPasteFunc nFunction, bool bSkipEmptyCells, bool bTranspose, bool bAsLink, bool bAllowDialogs, InsCellCmd eMoveMode, InsertDeleteFlags nUndoFlags) { ScViewData& rViewData = GetViewData(); ScDocument& rDoc = rViewData.GetDocument(); ScDocShell* pDocSh = rViewData.GetDocShell(); ScMarkData aMark(rViewData.GetMarkData()); const ScAddress& rCurPos = rViewData.GetCurPos(); ScClipParam& rClipParam = pClipDoc->GetClipParam(); SCCOL nColSize = rClipParam.getPasteColSize(); SCROW nRowSize = rClipParam.getPasteRowSize(*pClipDoc, /*bIncludeFiltered*/false); if (bTranspose) { if (static_cast(rCurPos.Col()) + nRowSize-1 > static_cast(pClipDoc->MaxCol())) { ErrorMessage(STR_PASTE_FULL); return false; } ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP)); pClipDoc->TransposeClip(pTransClip.get(), nFlags, bAsLink, /*bIncludeFiltered*/false); pClipDoc = pTransClip.release(); SCCOL nTempColSize = nColSize; nColSize = static_cast(nRowSize); nRowSize = static_cast(nTempColSize); } if (!rDoc.ValidCol(rCurPos.Col()+nColSize-1) || !rDoc.ValidRow(rCurPos.Row()+nRowSize-1)) { ErrorMessage(STR_PASTE_FULL); return false; } // Determine the first and last selected sheet numbers. SCTAB nTab1 = aMark.GetFirstSelected(); SCTAB nTab2 = aMark.GetLastSelected(); ScDocShellModificator aModificator(*pDocSh); // For multi-selection paste, we don't support cell duplication for larger // destination range. In case the destination is marked, we reset it to // the clip size. ScRange aMarkedRange(rCurPos.Col(), rCurPos.Row(), nTab1, rCurPos.Col()+nColSize-1, rCurPos.Row()+nRowSize-1, nTab2); // Extend the marked range to account for filtered rows in the destination // area. if (ScViewUtil::HasFiltered(aMarkedRange, rDoc)) { if (!ScViewUtil::FitToUnfilteredRows(aMarkedRange, rDoc, nRowSize)) return false; } bool bAskIfNotEmpty = bAllowDialogs && (nFlags & InsertDeleteFlags::CONTENTS) && nFunction == ScPasteFunc::NONE && SC_MOD()->GetInputOptions().GetReplaceCellsWarn(); if (bAskIfNotEmpty) { ScRangeList aTestRanges(aMarkedRange); if (!checkDestRangeForOverwrite(aTestRanges, rDoc, aMark, GetViewData().GetDialogParent())) return false; } aMark.SetMarkArea(aMarkedRange); MarkRange(aMarkedRange); bool bInsertCells = (eMoveMode != INS_NONE); if (bInsertCells) { if (!InsertCells(eMoveMode, rDoc.IsUndoEnabled(), true)) return false; } // TODO: position this call better for performance. ResetAutoSpellForContentChange(); bool bRowInfo = ( aMarkedRange.aStart.Col()==0 && aMarkedRange.aEnd.Col()==pClipDoc->MaxCol() ); ScDocumentUniquePtr pUndoDoc; if (rDoc.IsUndoEnabled()) { pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO)); pUndoDoc->InitUndoSelected(rDoc, aMark, false, bRowInfo); rDoc.CopyToDocument(aMarkedRange, nUndoFlags, false, *pUndoDoc, &aMark); } ScDocumentUniquePtr pMixDoc; if ( bSkipEmptyCells || nFunction != ScPasteFunc::NONE) { if ( nFlags & InsertDeleteFlags::CONTENTS ) { pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO)); pMixDoc->InitUndoSelected(rDoc, aMark); rDoc.CopyToDocument(aMarkedRange, InsertDeleteFlags::CONTENTS, false, *pMixDoc, &aMark); } } /* Make draw layer and start drawing undo. - Needed before AdjustBlockHeight to track moved drawing objects. - Needed before rDoc.CopyFromClip to track inserted note caption objects. */ if (nFlags & InsertDeleteFlags::OBJECTS) pDocSh->MakeDrawLayer(); if (rDoc.IsUndoEnabled()) rDoc.BeginDrawUndo(); InsertDeleteFlags nCopyFlags = nFlags & ~InsertDeleteFlags::OBJECTS; // in case of transpose, links were added in TransposeClip() if (bAsLink && bTranspose) nCopyFlags |= InsertDeleteFlags::FORMULA; rDoc.CopyMultiRangeFromClip(rCurPos, aMark, nCopyFlags, pClipDoc, true, bAsLink && !bTranspose, /*bIncludeFiltered*/false, bSkipEmptyCells); if (pMixDoc) rDoc.MixDocument(aMarkedRange, nFunction, bSkipEmptyCells, *pMixDoc); AdjustBlockHeight(); // update row heights before pasting objects if (nFlags & InsertDeleteFlags::OBJECTS) { // Paste the drawing objects after the row heights have been updated. rDoc.CopyMultiRangeFromClip(rCurPos, aMark, InsertDeleteFlags::OBJECTS, pClipDoc, true, false, /*bIncludeFiltered*/false, true); } if (bRowInfo) pDocSh->PostPaint(aMarkedRange.aStart.Col(), aMarkedRange.aStart.Row(), nTab1, pClipDoc->MaxCol(), pClipDoc->MaxRow(), nTab1, PaintPartFlags::Grid|PaintPartFlags::Left); else { ScRange aTmp = aMarkedRange; aTmp.aStart.SetTab(nTab1); aTmp.aEnd.SetTab(nTab1); pDocSh->PostPaint(aTmp, PaintPartFlags::Grid); } if (rDoc.IsUndoEnabled()) { SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager(); OUString aUndo = ScResId( pClipDoc->IsCutMode() ? STR_UNDO_CUT : STR_UNDO_COPY); pUndoMgr->EnterListAction(aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId()); ScUndoPasteOptions aOptions; // store options for repeat aOptions.nFunction = nFunction; aOptions.bSkipEmptyCells = bSkipEmptyCells; aOptions.bTranspose = bTranspose; aOptions.bAsLink = bAsLink; aOptions.eMoveMode = eMoveMode; std::unique_ptr pUndo(new ScUndoPaste(pDocSh, aMarkedRange, aMark, std::move(pUndoDoc), nullptr, nFlags|nUndoFlags, nullptr, false, &aOptions)); if (bInsertCells) pUndoMgr->AddUndoAction(std::make_unique(std::move(pUndo)), true); else pUndoMgr->AddUndoAction(std::move(pUndo)); pUndoMgr->LeaveListAction(); } aModificator.SetDocumentModified(); PostPasteFromClip(aMarkedRange, aMark); return true; } bool ScViewFunc::PasteFromClipToMultiRanges( InsertDeleteFlags nFlags, ScDocument* pClipDoc, ScPasteFunc nFunction, bool bSkipEmptyCells, bool bTranspose, bool bAsLink, bool bAllowDialogs, InsCellCmd eMoveMode, InsertDeleteFlags nUndoFlags ) { if (bTranspose) { // We don't allow transpose for this yet. ErrorMessage(STR_MSSG_PASTEFROMCLIP_0); return false; } if (eMoveMode != INS_NONE) { // We don't allow insertion mode either. Too complicated. ErrorMessage(STR_MSSG_PASTEFROMCLIP_0); return false; } ScViewData& rViewData = GetViewData(); ScClipParam& rClipParam = pClipDoc->GetClipParam(); if (rClipParam.mbCutMode) { // No cut and paste with this, please. ErrorMessage(STR_MSSG_PASTEFROMCLIP_0); return false; } const ScAddress& rCurPos = rViewData.GetCurPos(); ScDocument& rDoc = rViewData.GetDocument(); ScRange aSrcRange = rClipParam.getWholeRange(); SCROW nRowSize = aSrcRange.aEnd.Row() - aSrcRange.aStart.Row() + 1; SCCOL nColSize = aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1; if (!rDoc.ValidCol(rCurPos.Col()+nColSize-1) || !rDoc.ValidRow(rCurPos.Row()+nRowSize-1)) { ErrorMessage(STR_PASTE_FULL); return false; } ScMarkData aMark(rViewData.GetMarkData()); ScRangeList aRanges; aMark.MarkToSimple(); aMark.FillRangeListWithMarks(&aRanges, false); if (!ScClipUtil::CheckDestRanges(rDoc, nColSize, nRowSize, aMark, aRanges)) { ErrorMessage(STR_MSSG_PASTEFROMCLIP_0); return false; } ScDocShell* pDocSh = rViewData.GetDocShell(); ScDocShellModificator aModificator(*pDocSh); bool bAskIfNotEmpty = bAllowDialogs && (nFlags & InsertDeleteFlags::CONTENTS) && nFunction == ScPasteFunc::NONE && SC_MOD()->GetInputOptions().GetReplaceCellsWarn(); if (bAskIfNotEmpty) { if (!checkDestRangeForOverwrite(aRanges, rDoc, aMark, GetViewData().GetDialogParent())) return false; } // TODO: position this call better for performance. ResetAutoSpellForContentChange(); ScDocumentUniquePtr pUndoDoc; if (rDoc.IsUndoEnabled()) { pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO)); pUndoDoc->InitUndoSelected(rDoc, aMark); for (size_t i = 0, n = aRanges.size(); i < n; ++i) { rDoc.CopyToDocument( aRanges[i], nUndoFlags, false, *pUndoDoc, &aMark); } } ScDocumentUniquePtr pMixDoc; if (bSkipEmptyCells || nFunction != ScPasteFunc::NONE) { if (nFlags & InsertDeleteFlags::CONTENTS) { pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO)); pMixDoc->InitUndoSelected(rDoc, aMark); for (size_t i = 0, n = aRanges.size(); i < n; ++i) { rDoc.CopyToDocument( aRanges[i], InsertDeleteFlags::CONTENTS, false, *pMixDoc, &aMark); } } } if (nFlags & InsertDeleteFlags::OBJECTS) pDocSh->MakeDrawLayer(); if (rDoc.IsUndoEnabled()) rDoc.BeginDrawUndo(); // First, paste everything but the drawing objects. for (size_t i = 0, n = aRanges.size(); i < n; ++i) { rDoc.CopyFromClip( aRanges[i], aMark, (nFlags & ~InsertDeleteFlags::OBJECTS), nullptr, pClipDoc, false, false, true, bSkipEmptyCells); } if (pMixDoc) { for (size_t i = 0, n = aRanges.size(); i < n; ++i) rDoc.MixDocument(aRanges[i], nFunction, bSkipEmptyCells, *pMixDoc); } AdjustBlockHeight(); // update row heights before pasting objects // Then paste the objects. if (nFlags & InsertDeleteFlags::OBJECTS) { for (size_t i = 0, n = aRanges.size(); i < n; ++i) { rDoc.CopyFromClip( aRanges[i], aMark, InsertDeleteFlags::OBJECTS, nullptr, pClipDoc, false, false, true, bSkipEmptyCells); } } // Refresh the range that includes all pasted ranges. We only need to // refresh the current sheet. PaintPartFlags nPaint = PaintPartFlags::Grid; bool bRowInfo = (aSrcRange.aStart.Col()==0 && aSrcRange.aEnd.Col()==pClipDoc->MaxCol()); if (bRowInfo) nPaint |= PaintPartFlags::Left; pDocSh->PostPaint(aRanges, nPaint); if (rDoc.IsUndoEnabled()) { SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager(); OUString aUndo = ScResId( pClipDoc->IsCutMode() ? STR_UNDO_CUT : STR_UNDO_COPY); pUndoMgr->EnterListAction(aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId()); ScUndoPasteOptions aOptions; // store options for repeat aOptions.nFunction = nFunction; aOptions.bSkipEmptyCells = bSkipEmptyCells; aOptions.bTranspose = bTranspose; aOptions.bAsLink = bAsLink; aOptions.eMoveMode = eMoveMode; pUndoMgr->AddUndoAction( std::make_unique( pDocSh, aRanges, aMark, std::move(pUndoDoc), nullptr, nFlags|nUndoFlags, nullptr, false, &aOptions)); pUndoMgr->LeaveListAction(); } aModificator.SetDocumentModified(); PostPasteFromClip(aRanges, aMark); return false; } void ScViewFunc::PostPasteFromClip(const ScRangeList& rPasteRanges, const ScMarkData& rMark) { ScViewData& rViewData = GetViewData(); ScDocShell* pDocSh = rViewData.GetDocShell(); pDocSh->UpdateOle(rViewData); SelectionChanged(true); ScModelObj* pModelObj = HelperNotifyChanges::getMustPropagateChangesModel(*pDocSh); if (!pModelObj) return; ScRangeList aChangeRanges; for (size_t i = 0, n = rPasteRanges.size(); i < n; ++i) { const ScRange& r = rPasteRanges[i]; for (const auto& rTab : rMark) { ScRange aChangeRange(r); aChangeRange.aStart.SetTab(rTab); aChangeRange.aEnd.SetTab(rTab); aChangeRanges.push_back(aChangeRange); } } HelperNotifyChanges::Notify(*pModelObj, aChangeRanges); } // D R A G A N D D R O P // inside the doc bool ScViewFunc::MoveBlockTo( const ScRange& rSource, const ScAddress& rDestPos, bool bCut ) { ScDocShell* pDocSh = GetViewData().GetDocShell(); HideAllCursors(); ResetAutoSpellForContentChange(); bool bSuccess = true; SCTAB nDestTab = rDestPos.Tab(); const ScMarkData& rMark = GetViewData().GetMarkData(); if ( rSource.aStart.Tab() == nDestTab && rSource.aEnd.Tab() == nDestTab && rMark.GetSelectCount() > 1 ) { // moving within one table and several tables selected -> apply to all selected tables OUString aUndo = ScResId( bCut ? STR_UNDO_MOVE : STR_UNDO_COPY ); pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() ); // collect ranges of consecutive selected tables ScRange aLocalSource = rSource; ScAddress aLocalDest = rDestPos; SCTAB nTabCount = pDocSh->GetDocument().GetTableCount(); SCTAB nStartTab = 0; while ( nStartTab < nTabCount && bSuccess ) { while ( nStartTab < nTabCount && !rMark.GetTableSelect(nStartTab) ) ++nStartTab; if ( nStartTab < nTabCount ) { SCTAB nEndTab = nStartTab; while ( nEndTab+1 < nTabCount && rMark.GetTableSelect(nEndTab+1) ) ++nEndTab; aLocalSource.aStart.SetTab( nStartTab ); aLocalSource.aEnd.SetTab( nEndTab ); aLocalDest.SetTab( nStartTab ); bSuccess = pDocSh->GetDocFunc().MoveBlock( aLocalSource, aLocalDest, bCut, true/*bRecord*/, true/*bPaint*/, true/*bApi*/ ); nStartTab = nEndTab + 1; } } pDocSh->GetUndoManager()->LeaveListAction(); } else { // move the block as specified bSuccess = pDocSh->GetDocFunc().MoveBlock( rSource, rDestPos, bCut, true/*bRecord*/, true/*bPaint*/, true/*bApi*/ ); } ShowAllCursors(); if (bSuccess) { // mark destination range ScAddress aDestEnd( rDestPos.Col() + rSource.aEnd.Col() - rSource.aStart.Col(), rDestPos.Row() + rSource.aEnd.Row() - rSource.aStart.Row(), nDestTab ); bool bIncludeFiltered = bCut; if ( !bIncludeFiltered ) { // find number of non-filtered rows SCROW nPastedCount = pDocSh->GetDocument().CountNonFilteredRows( rSource.aStart.Row(), rSource.aEnd.Row(), rSource.aStart.Tab()); if ( nPastedCount == 0 ) nPastedCount = 1; aDestEnd.SetRow( rDestPos.Row() + nPastedCount - 1 ); } MarkRange( ScRange( rDestPos, aDestEnd ), false ); //! sal_False ??? pDocSh->UpdateOle(GetViewData()); SelectionChanged(); } return bSuccess; } // link inside the doc bool ScViewFunc::LinkBlock( const ScRange& rSource, const ScAddress& rDestPos ) { // check overlapping if ( rSource.aStart.Tab() == rDestPos.Tab() ) { SCCOL nDestEndCol = rDestPos.Col() + ( rSource.aEnd.Col() - rSource.aStart.Col() ); SCROW nDestEndRow = rDestPos.Row() + ( rSource.aEnd.Row() - rSource.aStart.Row() ); if ( rSource.aStart.Col() <= nDestEndCol && rDestPos.Col() <= rSource.aEnd.Col() && rSource.aStart.Row() <= nDestEndRow && rDestPos.Row() <= rSource.aEnd.Row() ) { return false; } } // run with paste ScDocument& rDoc = GetViewData().GetDocument(); ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP )); rDoc.CopyTabToClip( rSource.aStart.Col(), rSource.aStart.Row(), rSource.aEnd.Col(), rSource.aEnd.Row(), rSource.aStart.Tab(), pClipDoc.get() ); // mark destination area (set cursor, no marks) if ( GetViewData().GetTabNo() != rDestPos.Tab() ) SetTabNo( rDestPos.Tab() ); MoveCursorAbs( rDestPos.Col(), rDestPos.Row(), SC_FOLLOW_NONE, false, false ); // Paste PasteFromClip( InsertDeleteFlags::ALL, pClipDoc.get(), ScPasteFunc::NONE, false, false, true ); // as a link return true; } void ScViewFunc::DataFormPutData( SCROW nCurrentRow , SCROW nStartRow , SCCOL nStartCol , SCROW nEndRow , SCCOL nEndCol , std::vector>& rEdits, sal_uInt16 aColLength ) { ScDocument& rDoc = GetViewData().GetDocument(); ScDocShell* pDocSh = GetViewData().GetDocShell(); ScMarkData& rMark = GetViewData().GetMarkData(); ScDocShellModificator aModificator( *pDocSh ); SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager(); const bool bRecord( rDoc.IsUndoEnabled()); ScDocumentUniquePtr pUndoDoc; ScDocumentUniquePtr pRedoDoc; std::unique_ptr pUndoData; SCTAB nTab = GetViewData().GetTabNo(); SCTAB nStartTab = nTab; SCTAB nEndTab = nTab; { ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack(); if ( pChangeTrack ) pChangeTrack->ResetLastCut(); // no more cut-mode } ScRange aUserRange( nStartCol, nCurrentRow, nStartTab, nEndCol, nCurrentRow, nEndTab ); bool bColInfo = ( nStartRow==0 && nEndRow==rDoc.MaxRow() ); bool bRowInfo = ( nStartCol==0 && nEndCol==rDoc.MaxCol() ); SCCOL nUndoEndCol = nStartCol+aColLength-1; SCROW nUndoEndRow = nCurrentRow; if ( bRecord ) { pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); pUndoDoc->InitUndoSelected( rDoc , rMark , bColInfo , bRowInfo ); rDoc.CopyToDocument( aUserRange , InsertDeleteFlags::VALUE , false, *pUndoDoc ); } sal_uInt16 nExtFlags = 0; pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab , nEndCol, nEndRow, nEndTab ); // content before the change rDoc.BeginDrawUndo(); for(sal_uInt16 i = 0; i < aColLength; i++) { if (rEdits[i] != nullptr) { OUString aFieldName = rEdits[i]->m_xEdit->get_text(); rDoc.SetString( nStartCol + i, nCurrentRow, nTab, aFieldName ); } } pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nCurrentRow, nStartTab, nEndCol, nCurrentRow, nEndTab ); // content after the change std::unique_ptr pUndo( new ScUndoDataForm( pDocSh, nStartCol, nCurrentRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab, rMark, std::move(pUndoDoc), std::move(pRedoDoc), std::move(pUndoData) ) ); pUndoMgr->AddUndoAction( std::make_unique( std::move(pUndo) ), true ); PaintPartFlags nPaint = PaintPartFlags::Grid; if (bColInfo) { nPaint |= PaintPartFlags::Top; nUndoEndCol = rDoc.MaxCol(); // just for drawing ! } if (bRowInfo) { nPaint |= PaintPartFlags::Left; nUndoEndRow = rDoc.MaxRow(); // just for drawing ! } pDocSh->PostPaint( ScRange(nStartCol, nCurrentRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab), nPaint, nExtFlags); pDocSh->UpdateOle(GetViewData()); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */