1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <scitems.hxx> 21 #include <svx/svdpage.hxx> 22 #include <sfx2/docfile.hxx> 23 #include <comphelper/classids.hxx> 24 #include <comphelper/lok.hxx> 25 #include <sot/formats.hxx> 26 #include <sot/storage.hxx> 27 #include <vcl/graph.hxx> 28 #include <vcl/svapp.hxx> 29 #include <vcl/weld.hxx> 30 #include <tools/urlobj.hxx> 31 #include <sot/exchange.hxx> 32 #include <memory> 33 #include <vcl/uitest/logger.hxx> 34 #include <vcl/uitest/eventdescription.hxx> 35 #include <vcl/TypeSerializer.hxx> 36 #include <osl/diagnose.h> 37 38 #include <attrib.hxx> 39 #include <patattr.hxx> 40 #include <dociter.hxx> 41 #include <viewfunc.hxx> 42 #include <tabvwsh.hxx> 43 #include <docsh.hxx> 44 #include <docfunc.hxx> 45 #include <undoblk.hxx> 46 #include <refundo.hxx> 47 #include <globstr.hrc> 48 #include <scresid.hxx> 49 #include <global.hxx> 50 #include <transobj.hxx> 51 #include <drwtrans.hxx> 52 #include <chgtrack.hxx> 53 #include <waitoff.hxx> 54 #include <scmod.hxx> 55 #include <inputopt.hxx> 56 #include <warnbox.hxx> 57 #include <drwlayer.hxx> 58 #include <editable.hxx> 59 #include <docuno.hxx> 60 #include <clipparam.hxx> 61 #include <undodat.hxx> 62 #include <drawview.hxx> 63 #include <cliputil.hxx> 64 #include <clipoptions.hxx> 65 #include <gridwin.hxx> 66 #include <com/sun/star/util/XCloneable.hpp> 67 68 using namespace com::sun::star; 69 70 namespace { 71 72 void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& action) 73 { 74 EventDescription aDescription; 75 aDescription.aID = "grid_window"; 76 aDescription.aAction = action; 77 aDescription.aParameters = std::move(aParameters); 78 aDescription.aParent = "MainWindow"; 79 aDescription.aKeyWord = "ScGridWinUIObject"; 80 81 UITestLogger::getInstance().logEvent(aDescription); 82 } 83 84 } 85 86 // GlobalName of writer-DocShell from comphelper/classids.hxx 87 88 // C U T 89 90 void ScViewFunc::CutToClip() 91 { 92 UpdateInputLine(); 93 94 ScEditableTester aTester( this ); 95 if (!aTester.IsEditable()) // selection editable? 96 { 97 ErrorMessage( aTester.GetMessageId() ); 98 return; 99 } 100 101 ScRange aRange; // delete this range 102 if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE ) 103 { 104 ScDocument& rDoc = GetViewData().GetDocument(); 105 ScDocShell* pDocSh = GetViewData().GetDocShell(); 106 ScMarkData& rMark = GetViewData().GetMarkData(); 107 const bool bRecord(rDoc.IsUndoEnabled()); // Undo/Redo 108 109 ScDocShellModificator aModificator( *pDocSh ); 110 111 if ( !rMark.IsMarked() && !rMark.IsMultiMarked() ) // mark the range if not marked yet 112 { 113 DoneBlockMode(); 114 InitOwnBlockMode(); 115 rMark.SetMarkArea( aRange ); 116 MarkDataChanged(); 117 } 118 119 CopyToClip( nullptr, true, false, true/*bIncludeObjects*/ ); // copy to clipboard 120 121 ScAddress aOldEnd( aRange.aEnd ); // combined cells in this range? 122 rDoc.ExtendMerge( aRange, true ); 123 124 ScDocumentUniquePtr pUndoDoc; 125 if ( bRecord ) 126 { 127 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 128 pUndoDoc->InitUndoSelected( rDoc, rMark ); 129 // all sheets - CopyToDocument skips those that don't exist in pUndoDoc 130 ScRange aCopyRange = aRange; 131 aCopyRange.aStart.SetTab(0); 132 aCopyRange.aEnd.SetTab(rDoc.GetTableCount()-1); 133 rDoc.CopyToDocument( aCopyRange, (InsertDeleteFlags::ALL & ~InsertDeleteFlags::OBJECTS) | InsertDeleteFlags::NOCAPTIONS, false, *pUndoDoc ); 134 rDoc.BeginDrawUndo(); 135 } 136 137 sal_uInt16 nExtFlags = 0; 138 pDocSh->UpdatePaintExt( nExtFlags, aRange ); 139 140 rMark.MarkToMulti(); 141 rDoc.DeleteSelection( InsertDeleteFlags::ALL, rMark ); 142 rDoc.DeleteObjectsInSelection( rMark ); 143 rMark.MarkToSimple(); 144 145 if ( !AdjustRowHeight( aRange.aStart.Row(), aRange.aEnd.Row(), true ) ) 146 pDocSh->PostPaint( aRange, PaintPartFlags::Grid, nExtFlags ); 147 148 if ( bRecord ) // Draw-Undo now available 149 pDocSh->GetUndoManager()->AddUndoAction( 150 std::make_unique<ScUndoCut>( pDocSh, aRange, aOldEnd, rMark, std::move(pUndoDoc) ) ); 151 152 aModificator.SetDocumentModified(); 153 pDocSh->UpdateOle(GetViewData()); 154 155 CellContentChanged(); 156 157 OUString aStartAddress = aRange.aStart.GetColRowString(); 158 OUString aEndAddress = aRange.aEnd.GetColRowString(); 159 160 collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "CUT"); 161 } 162 else 163 ErrorMessage( STR_NOMULTISELECT ); 164 } 165 166 // C O P Y 167 168 bool ScViewFunc::CopyToClip( ScDocument* pClipDoc, bool bCut, bool bApi, bool bIncludeObjects, bool bStopEdit ) 169 { 170 ScRange aRange; 171 ScMarkType eMarkType = GetViewData().GetSimpleArea( aRange ); 172 ScMarkData& rMark = GetViewData().GetMarkData(); 173 bool bDone = false; 174 175 if ( eMarkType == SC_MARK_SIMPLE || eMarkType == SC_MARK_SIMPLE_FILTERED ) 176 { 177 ScRangeList aRangeList( aRange ); 178 bDone = CopyToClip( pClipDoc, aRangeList, bCut, bApi, bIncludeObjects, bStopEdit ); 179 } 180 else if (eMarkType == SC_MARK_MULTI) 181 { 182 ScRangeList aRangeList; 183 rMark.MarkToSimple(); 184 rMark.FillRangeListWithMarks(&aRangeList, false); 185 bDone = CopyToClip( pClipDoc, aRangeList, bCut, bApi, bIncludeObjects, bStopEdit ); 186 } 187 else 188 { 189 if (!bApi) 190 ErrorMessage(STR_NOMULTISELECT); 191 } 192 if( !bCut ){ 193 OUString aStartAddress = aRange.aStart.GetColRowString(); 194 OUString aEndAddress = aRange.aEnd.GetColRowString(); 195 collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "COPY"); 196 } 197 return bDone; 198 } 199 200 // Copy the content of the Range into clipboard. 201 bool ScViewFunc::CopyToClip( ScDocument* pClipDoc, const ScRangeList& rRanges, bool bCut, bool bApi, bool bIncludeObjects, bool bStopEdit ) 202 { 203 if ( rRanges.empty() ) 204 return false; 205 if ( bStopEdit ) 206 UpdateInputLine(); 207 208 bool bDone; 209 if (rRanges.size() > 1) // isMultiRange 210 bDone = CopyToClipMultiRange(pClipDoc, rRanges, bCut, bApi, bIncludeObjects); 211 else 212 bDone = CopyToClipSingleRange(pClipDoc, rRanges, bCut, bIncludeObjects); 213 214 return bDone; 215 } 216 217 bool ScViewFunc::CopyToClipSingleRange( ScDocument* pClipDoc, const ScRangeList& rRanges, bool bCut, bool bIncludeObjects ) 218 { 219 ScRange aRange = rRanges[0]; 220 ScClipParam aClipParam( aRange, bCut ); 221 aClipParam.maRanges = rRanges; 222 ScDocument& rDoc = GetViewData().GetDocument(); 223 ScMarkData& rMark = GetViewData().GetMarkData(); 224 225 if (rDoc.HasSelectedBlockMatrixFragment( aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(), rMark ) ) 226 return false; 227 228 bool bSysClip = false; 229 if ( !pClipDoc ) // no clip doc specified 230 { 231 // Create one (deleted by ScTransferObj). 232 pClipDoc = new ScDocument( SCDOCMODE_CLIP ); 233 bSysClip = true; // and copy into system 234 } 235 if ( !bCut ) 236 { 237 ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack(); 238 if ( pChangeTrack ) 239 pChangeTrack->ResetLastCut(); 240 } 241 242 if ( bSysClip && bIncludeObjects ) 243 { 244 bool bAnyOle = rDoc.HasOLEObjectsInArea( aRange ); 245 // Update ScGlobal::xDrawClipDocShellRef. 246 ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle ) ); 247 } 248 249 // is this necessary?, will setting the doc id upset the 250 // following paste operation with range? would be nicer to just set this always 251 // and lose the 'if' above 252 aClipParam.setSourceDocID( rDoc.GetDocumentID() ); 253 254 if (SfxObjectShell* pObjectShell = rDoc.GetDocumentShell()) 255 { 256 // Copy document properties from pObjectShell to pClipDoc (to its clip options, as it has no object shell). 257 uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier(pObjectShell->GetModel(), uno::UNO_QUERY); 258 uno::Reference<util::XCloneable> xCloneable(xDocumentPropertiesSupplier->getDocumentProperties(), uno::UNO_QUERY); 259 std::unique_ptr<ScClipOptions> pOptions(new ScClipOptions); 260 pOptions->m_xDocumentProperties.set(xCloneable->createClone(), uno::UNO_QUERY); 261 pClipDoc->SetClipOptions(std::move(pOptions)); 262 } 263 264 rDoc.CopyToClip( aClipParam, pClipDoc, &rMark, false, bIncludeObjects ); 265 if (ScDrawLayer* pDrawLayer = pClipDoc->GetDrawLayer()) 266 { 267 ScClipParam& rClipDocClipParam = pClipDoc->GetClipParam(); 268 ScRangeListVector& rRangesVector = rClipDocClipParam.maProtectedChartRangesVector; 269 SCTAB nTabCount = pClipDoc->GetTableCount(); 270 for ( SCTAB nTab = 0; nTab < nTabCount; ++nTab ) 271 { 272 SdrPage* pPage = pDrawLayer->GetPage( static_cast< sal_uInt16 >( nTab ) ); 273 if ( pPage ) 274 { 275 ScChartHelper::FillProtectedChartRangesVector( rRangesVector, rDoc, pPage ); 276 } 277 } 278 } 279 280 if ( bSysClip ) 281 { 282 ScDrawLayer::SetGlobalDrawPersist(nullptr); 283 ScGlobal::SetClipDocName( rDoc.GetDocumentShell()->GetTitle( SFX_TITLE_FULLNAME ) ); 284 } 285 pClipDoc->ExtendMerge( aRange, true ); 286 287 if ( bSysClip ) 288 { 289 ScDocShell* pDocSh = GetViewData().GetDocShell(); 290 TransferableObjectDescriptor aObjDesc; 291 pDocSh->FillTransferableObjectDescriptor( aObjDesc ); 292 aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass(); 293 // maSize is set in ScTransferObj ctor 294 295 rtl::Reference<ScTransferObj> pTransferObj(new ScTransferObj( ScDocumentUniquePtr(pClipDoc), aObjDesc )); 296 if ( ScGlobal::xDrawClipDocShellRef.is() ) 297 { 298 SfxObjectShellRef aPersistRef( ScGlobal::xDrawClipDocShellRef.get() ); 299 pTransferObj->SetDrawPersist( aPersistRef );// keep persist for ole objects alive 300 } 301 pTransferObj->CopyToClipboard( GetActiveWin() ); 302 } 303 304 return true; 305 } 306 307 bool ScViewFunc::CopyToClipMultiRange( const ScDocument* pInputClipDoc, const ScRangeList& rRanges, bool bCut, bool bApi, bool bIncludeObjects ) 308 { 309 if (bCut) 310 { 311 // We don't support cutting of multi-selections. 312 if (!bApi) 313 ErrorMessage(STR_NOMULTISELECT); 314 return false; 315 } 316 if (pInputClipDoc) 317 { 318 // TODO: What's this for? 319 if (!bApi) 320 ErrorMessage(STR_NOMULTISELECT); 321 return false; 322 } 323 324 ScClipParam aClipParam( rRanges[0], bCut ); 325 aClipParam.maRanges = rRanges; 326 ScDocument& rDoc = GetViewData().GetDocument(); 327 ScMarkData& rMark = GetViewData().GetMarkData(); 328 bool bDone = false; 329 bool bSuccess = false; 330 aClipParam.mbCutMode = false; 331 332 do 333 { 334 ScDocumentUniquePtr pDocClip(new ScDocument(SCDOCMODE_CLIP)); 335 336 // Check for geometrical feasibility of the ranges. 337 bool bValidRanges = true; 338 ScRange const * p = &aClipParam.maRanges.front(); 339 SCCOL nPrevColDelta = 0; 340 SCROW nPrevRowDelta = 0; 341 SCCOL nPrevCol = p->aStart.Col(); 342 SCROW nPrevRow = p->aStart.Row(); 343 SCCOL nPrevColSize = p->aEnd.Col() - p->aStart.Col() + 1; 344 SCROW nPrevRowSize = p->aEnd.Row() - p->aStart.Row() + 1; 345 for ( size_t i = 1; i < aClipParam.maRanges.size(); ++i ) 346 { 347 p = &aClipParam.maRanges[i]; 348 if ( rDoc.HasSelectedBlockMatrixFragment( 349 p->aStart.Col(), p->aStart.Row(), p->aEnd.Col(), p->aEnd.Row(), rMark) ) 350 { 351 if (!bApi) 352 ErrorMessage(STR_MATRIXFRAGMENTERR); 353 return false; 354 } 355 356 SCCOL nColDelta = p->aStart.Col() - nPrevCol; 357 SCROW nRowDelta = p->aStart.Row() - nPrevRow; 358 359 if ((nColDelta && nRowDelta) || (nPrevColDelta && nRowDelta) || (nPrevRowDelta && nColDelta)) 360 { 361 bValidRanges = false; 362 break; 363 } 364 365 if (aClipParam.meDirection == ScClipParam::Unspecified) 366 { 367 if (nColDelta) 368 aClipParam.meDirection = ScClipParam::Column; 369 if (nRowDelta) 370 aClipParam.meDirection = ScClipParam::Row; 371 } 372 373 SCCOL nColSize = p->aEnd.Col() - p->aStart.Col() + 1; 374 SCROW nRowSize = p->aEnd.Row() - p->aStart.Row() + 1; 375 376 if (aClipParam.meDirection == ScClipParam::Column && nRowSize != nPrevRowSize) 377 { 378 // column-oriented ranges must have identical row size. 379 bValidRanges = false; 380 break; 381 } 382 if (aClipParam.meDirection == ScClipParam::Row && nColSize != nPrevColSize) 383 { 384 // likewise, row-oriented ranges must have identical 385 // column size. 386 bValidRanges = false; 387 break; 388 } 389 390 nPrevCol = p->aStart.Col(); 391 nPrevRow = p->aStart.Row(); 392 nPrevColDelta = nColDelta; 393 nPrevRowDelta = nRowDelta; 394 nPrevColSize = nColSize; 395 nPrevRowSize = nRowSize; 396 } 397 if (!bValidRanges) 398 break; 399 rDoc.CopyToClip(aClipParam, pDocClip.get(), &rMark, false, bIncludeObjects ); 400 401 ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack(); 402 if ( pChangeTrack ) 403 pChangeTrack->ResetLastCut(); // no more cut-mode 404 405 ScDocShell* pDocSh = GetViewData().GetDocShell(); 406 TransferableObjectDescriptor aObjDesc; 407 pDocSh->FillTransferableObjectDescriptor( aObjDesc ); 408 aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass(); 409 // maSize is set in ScTransferObj ctor 410 411 rtl::Reference<ScTransferObj> pTransferObj(new ScTransferObj( std::move(pDocClip), aObjDesc )); 412 if ( ScGlobal::xDrawClipDocShellRef.is() ) 413 { 414 SfxObjectShellRef aPersistRef( ScGlobal::xDrawClipDocShellRef.get() ); 415 pTransferObj->SetDrawPersist( aPersistRef ); // keep persist for ole objects alive 416 } 417 pTransferObj->CopyToClipboard( GetActiveWin() ); // system clipboard 418 419 bSuccess = true; 420 } 421 while (false); 422 423 if (!bSuccess && !bApi) 424 ErrorMessage(STR_NOMULTISELECT); 425 426 bDone = bSuccess; 427 428 return bDone; 429 } 430 431 rtl::Reference<ScTransferObj> ScViewFunc::CopyToTransferable() 432 { 433 ScRange aRange; 434 if ( GetViewData().GetSimpleArea( aRange ) == SC_MARK_SIMPLE ) 435 { 436 ScDocument& rDoc = GetViewData().GetDocument(); 437 ScMarkData& rMark = GetViewData().GetMarkData(); 438 if ( !rDoc.HasSelectedBlockMatrixFragment( 439 aRange.aStart.Col(), aRange.aStart.Row(), 440 aRange.aEnd.Col(), aRange.aEnd.Row(), 441 rMark ) ) 442 { 443 ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP )); // create one (deleted by ScTransferObj) 444 445 bool bAnyOle = rDoc.HasOLEObjectsInArea( aRange, &rMark ); 446 ScDrawLayer::SetGlobalDrawPersist( ScTransferObj::SetDrawClipDoc( bAnyOle ) ); 447 448 ScClipParam aClipParam(aRange, false); 449 rDoc.CopyToClip(aClipParam, pClipDoc.get(), &rMark, false, true); 450 451 ScDrawLayer::SetGlobalDrawPersist(nullptr); 452 pClipDoc->ExtendMerge( aRange, true ); 453 454 ScDocShell* pDocSh = GetViewData().GetDocShell(); 455 TransferableObjectDescriptor aObjDesc; 456 pDocSh->FillTransferableObjectDescriptor( aObjDesc ); 457 aObjDesc.maDisplayName = pDocSh->GetMedium()->GetURLObject().GetURLNoPass(); 458 return new ScTransferObj( std::move(pClipDoc), aObjDesc ); 459 } 460 } 461 462 return nullptr; 463 } 464 465 // P A S T E 466 467 void ScViewFunc::PasteDraw() 468 { 469 ScViewData& rViewData = GetViewData(); 470 SCCOL nPosX = rViewData.GetCurX(); 471 SCROW nPosY = rViewData.GetCurY(); 472 vcl::Window* pWin = GetActiveWin(); 473 Point aPos = pWin->PixelToLogic( rViewData.GetScrPos( nPosX, nPosY, 474 rViewData.GetActivePart() ) ); 475 const ScDrawTransferObj* pDrawClip = ScDrawTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(rViewData.GetActiveWin())); 476 if (pDrawClip) 477 { 478 const OUString& aSrcShellID = pDrawClip->GetShellID(); 479 OUString aDestShellID = SfxObjectShell::CreateShellID(rViewData.GetDocShell()); 480 PasteDraw(aPos, pDrawClip->GetModel(), false, aSrcShellID, aDestShellID); 481 } 482 } 483 484 void ScViewFunc::PasteFromSystem() 485 { 486 UpdateInputLine(); 487 488 vcl::Window* pWin = GetActiveWin(); 489 css::uno::Reference<css::datatransfer::XTransferable2> xTransferable2(ScTabViewShell::GetClipData(pWin)); 490 const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(xTransferable2); 491 // keep a reference in case the clipboard is changed during PasteFromClip 492 const ScDrawTransferObj* pDrawClip = ScDrawTransferObj::GetOwnClipboard(xTransferable2); 493 if (pOwnClip) 494 { 495 PasteFromClip( InsertDeleteFlags::ALL, pOwnClip->GetDocument(), 496 ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE, 497 true ); // allow warning dialog 498 } 499 else if (pDrawClip) 500 PasteDraw(); 501 else 502 { 503 TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) ); 504 505 { 506 SotClipboardFormatId nBiff8 = SotExchange::RegisterFormatName("Biff8"); 507 SotClipboardFormatId nBiff5 = SotExchange::RegisterFormatName("Biff5"); 508 509 SotClipboardFormatId nFormat; // output param for GetExchangeAction 510 sal_uInt8 nEventAction; // output param for GetExchangeAction 511 512 uno::Reference<css::datatransfer::XTransferable> xTransferable( aDataHelper.GetXTransferable() ); 513 sal_uInt8 nAction = SotExchange::GetExchangeAction( 514 aDataHelper.GetDataFlavorExVector(), 515 SotExchangeDest::SCDOC_FREE_AREA, 516 EXCHG_IN_ACTION_COPY, 517 EXCHG_IN_ACTION_DEFAULT, 518 nFormat, nEventAction, SotClipboardFormatId::NONE, 519 &xTransferable ); 520 521 if ( nAction != EXCHG_INOUT_ACTION_NONE ) 522 { 523 switch( nAction ) 524 { 525 case EXCHG_OUT_ACTION_INSERT_SVXB: 526 case EXCHG_OUT_ACTION_INSERT_GDIMETAFILE: 527 case EXCHG_OUT_ACTION_INSERT_BITMAP: 528 case EXCHG_OUT_ACTION_INSERT_GRAPH: 529 // SotClipboardFormatId::BITMAP 530 // SotClipboardFormatId::PNG 531 // SotClipboardFormatId::GDIMETAFILE 532 // SotClipboardFormatId::SVXB 533 PasteFromSystem(nFormat); 534 break; 535 default: 536 nAction = EXCHG_INOUT_ACTION_NONE; 537 } 538 } 539 540 if ( nAction == EXCHG_INOUT_ACTION_NONE ) 541 { 542 // first SvDraw-model, then drawing 543 // (only one drawing is allowed) 544 545 if (aDataHelper.HasFormat( SotClipboardFormatId::DRAWING )) 546 { 547 // special case for tables from drawing 548 if( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ) 549 { 550 PasteFromSystem( SotClipboardFormatId::RTF ); 551 } 552 else if( aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) 553 { 554 PasteFromSystem( SotClipboardFormatId::RICHTEXT ); 555 } 556 else 557 { 558 PasteFromSystem( SotClipboardFormatId::DRAWING ); 559 } 560 } 561 else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE )) 562 { 563 // If it's a Writer object, insert RTF instead of OLE 564 565 // Else, if the class id is all-zero, and SYLK is available, 566 // it probably is spreadsheet cells that have been put 567 // on the clipboard by OOo, so use the SYLK. (fdo#31077) 568 569 bool bDoRtf = false; 570 TransferableObjectDescriptor aObjDesc; 571 if( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) ) 572 { 573 bDoRtf = ( ( aObjDesc.maClassName == SvGlobalName( SO3_SW_CLASSID ) || 574 aObjDesc.maClassName == SvGlobalName( SO3_SWWEB_CLASSID ) ) 575 && ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) ); 576 } 577 if ( bDoRtf ) 578 PasteFromSystem( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT ); 579 else if ( aObjDesc.maClassName == SvGlobalName( 0,0,0,0,0,0,0,0,0,0,0 ) 580 && aDataHelper.HasFormat( SotClipboardFormatId::SYLK )) 581 PasteFromSystem( SotClipboardFormatId::SYLK ); 582 else 583 PasteFromSystem( SotClipboardFormatId::EMBED_SOURCE ); 584 } 585 else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE )) 586 PasteFromSystem( SotClipboardFormatId::LINK_SOURCE ); 587 // the following format can not affect scenario from #89579# 588 else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE )) 589 PasteFromSystem( SotClipboardFormatId::EMBEDDED_OBJ_OLE ); 590 // SotClipboardFormatId::PRIVATE no longer here (can't work if pOwnClip is NULL) 591 else if (aDataHelper.HasFormat(nBiff8)) // before xxx_OLE formats 592 PasteFromSystem(nBiff8); 593 else if (aDataHelper.HasFormat(nBiff5)) 594 PasteFromSystem(nBiff5); 595 else if (aDataHelper.HasFormat(SotClipboardFormatId::RTF)) 596 PasteFromSystem(SotClipboardFormatId::RTF); 597 else if (aDataHelper.HasFormat(SotClipboardFormatId::RICHTEXT)) 598 PasteFromSystem(SotClipboardFormatId::RICHTEXT); 599 else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML)) 600 PasteFromSystem(SotClipboardFormatId::HTML); 601 else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML_SIMPLE)) 602 PasteFromSystem(SotClipboardFormatId::HTML_SIMPLE); 603 else if (aDataHelper.HasFormat(SotClipboardFormatId::SYLK)) 604 PasteFromSystem(SotClipboardFormatId::SYLK); 605 else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING_TSVC)) 606 PasteFromSystem(SotClipboardFormatId::STRING_TSVC); 607 else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING)) 608 PasteFromSystem(SotClipboardFormatId::STRING); 609 // xxx_OLE formats come last, like in SotExchange tables 610 else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE )) 611 PasteFromSystem( SotClipboardFormatId::EMBED_SOURCE_OLE ); 612 else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE )) 613 PasteFromSystem( SotClipboardFormatId::LINK_SOURCE_OLE ); 614 } 615 } 616 } 617 // no exception-> SID_PASTE has FastCall-flag from idl 618 // will be called in case of empty clipboard (#42531#) 619 } 620 621 void ScViewFunc::PasteFromTransferable( const uno::Reference<datatransfer::XTransferable>& rxTransferable ) 622 { 623 uno::Reference<lang::XUnoTunnel> xTunnel( rxTransferable, uno::UNO_QUERY ); 624 625 if (auto pOwnClip = comphelper::getFromUnoTunnel<ScTransferObj>(xTunnel)) 626 { 627 PasteFromClip( InsertDeleteFlags::ALL, pOwnClip->GetDocument(), 628 ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE, 629 true ); // allow warning dialog 630 } 631 else if (auto pDrawClip = comphelper::getFromUnoTunnel<ScDrawTransferObj>(xTunnel)) 632 { 633 ScViewData& rViewData = GetViewData(); 634 SCCOL nPosX = rViewData.GetCurX(); 635 SCROW nPosY = rViewData.GetCurY(); 636 vcl::Window* pWin = GetActiveWin(); 637 Point aPos = pWin->PixelToLogic( rViewData.GetScrPos( nPosX, nPosY, rViewData.GetActivePart() ) ); 638 PasteDraw( 639 aPos, pDrawClip->GetModel(), false, 640 pDrawClip->GetShellID(), SfxObjectShell::CreateShellID(rViewData.GetDocShell())); 641 } 642 else 643 { 644 TransferableDataHelper aDataHelper( rxTransferable ); 645 SotClipboardFormatId nBiff8 = SotExchange::RegisterFormatName("Biff8"); 646 SotClipboardFormatId nBiff5 = SotExchange::RegisterFormatName("Biff5"); 647 SotClipboardFormatId nFormatId = SotClipboardFormatId::NONE; 648 // first SvDraw-model, then drawing 649 // (only one drawing is allowed) 650 651 if (aDataHelper.HasFormat( SotClipboardFormatId::DRAWING )) 652 nFormatId = SotClipboardFormatId::DRAWING; 653 else if (aDataHelper.HasFormat( SotClipboardFormatId::SVXB )) 654 nFormatId = SotClipboardFormatId::SVXB; 655 else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE )) 656 { 657 // If it's a Writer object, insert RTF instead of OLE 658 bool bDoRtf = false; 659 TransferableObjectDescriptor aObjDesc; 660 if( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc ) ) 661 { 662 bDoRtf = ( ( aObjDesc.maClassName == SvGlobalName( SO3_SW_CLASSID ) || 663 aObjDesc.maClassName == SvGlobalName( SO3_SWWEB_CLASSID ) ) 664 && ( aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) )); 665 } 666 if ( bDoRtf ) 667 nFormatId = aDataHelper.HasFormat( SotClipboardFormatId::RTF ) ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT; 668 else 669 nFormatId = SotClipboardFormatId::EMBED_SOURCE; 670 } 671 else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE )) 672 nFormatId = SotClipboardFormatId::LINK_SOURCE; 673 // the following format can not affect scenario from #89579# 674 else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ_OLE )) 675 nFormatId = SotClipboardFormatId::EMBEDDED_OBJ_OLE; 676 // SotClipboardFormatId::PRIVATE no longer here (can't work if pOwnClip is NULL) 677 else if (aDataHelper.HasFormat(nBiff8)) // before xxx_OLE formats 678 nFormatId = nBiff8; 679 else if (aDataHelper.HasFormat(nBiff5)) 680 nFormatId = nBiff5; 681 else if (aDataHelper.HasFormat(SotClipboardFormatId::RTF)) 682 nFormatId = SotClipboardFormatId::RTF; 683 else if (aDataHelper.HasFormat(SotClipboardFormatId::RICHTEXT)) 684 nFormatId = SotClipboardFormatId::RICHTEXT; 685 else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML)) 686 nFormatId = SotClipboardFormatId::HTML; 687 else if (aDataHelper.HasFormat(SotClipboardFormatId::HTML_SIMPLE)) 688 nFormatId = SotClipboardFormatId::HTML_SIMPLE; 689 else if (aDataHelper.HasFormat(SotClipboardFormatId::SYLK)) 690 nFormatId = SotClipboardFormatId::SYLK; 691 else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING_TSVC)) 692 nFormatId = SotClipboardFormatId::STRING_TSVC; 693 else if (aDataHelper.HasFormat(SotClipboardFormatId::STRING)) 694 nFormatId = SotClipboardFormatId::STRING; 695 else if (aDataHelper.HasFormat(SotClipboardFormatId::GDIMETAFILE)) 696 nFormatId = SotClipboardFormatId::GDIMETAFILE; 697 else if (aDataHelper.HasFormat(SotClipboardFormatId::BITMAP)) 698 nFormatId = SotClipboardFormatId::BITMAP; 699 // xxx_OLE formats come last, like in SotExchange tables 700 else if (aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE_OLE )) 701 nFormatId = SotClipboardFormatId::EMBED_SOURCE_OLE; 702 else if (aDataHelper.HasFormat( SotClipboardFormatId::LINK_SOURCE_OLE )) 703 nFormatId = SotClipboardFormatId::LINK_SOURCE_OLE; 704 else 705 return; 706 707 PasteDataFormat( nFormatId, aDataHelper.GetTransferable(), 708 GetViewData().GetCurX(), GetViewData().GetCurY(), nullptr ); 709 } 710 } 711 712 bool ScViewFunc::PasteFromSystem( SotClipboardFormatId nFormatId, bool bApi ) 713 { 714 UpdateInputLine(); 715 716 bool bRet = true; 717 vcl::Window* pWin = GetActiveWin(); 718 // keep a reference in case the clipboard is changed during PasteFromClip 719 const ScTransferObj* pOwnClip = ScTransferObj::GetOwnClipboard(ScTabViewShell::GetClipData(pWin)); 720 if ( nFormatId == SotClipboardFormatId::NONE && pOwnClip ) 721 { 722 PasteFromClip( InsertDeleteFlags::ALL, pOwnClip->GetDocument(), 723 ScPasteFunc::NONE, false, false, false, INS_NONE, InsertDeleteFlags::NONE, 724 !bApi ); // allow warning dialog 725 } 726 else 727 { 728 TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( pWin ) ); 729 if ( !aDataHelper.GetTransferable().is() ) 730 return false; 731 732 SCCOL nPosX = 0; 733 SCROW nPosY = 0; 734 735 ScViewData& rViewData = GetViewData(); 736 ScRange aRange; 737 if ( rViewData.GetSimpleArea( aRange ) == SC_MARK_SIMPLE ) 738 { 739 nPosX = aRange.aStart.Col(); 740 nPosY = aRange.aStart.Row(); 741 } 742 else 743 { 744 nPosX = rViewData.GetCurX(); 745 nPosY = rViewData.GetCurY(); 746 } 747 748 bRet = PasteDataFormat( nFormatId, aDataHelper.GetTransferable(), 749 nPosX, nPosY, 750 nullptr, false, !bApi ); // allow warning dialog 751 752 if ( !bRet && !bApi ) 753 { 754 ErrorMessage(STR_PASTE_ERROR); 755 } 756 else if (comphelper::LibreOfficeKit::isActive()) 757 { 758 SfxViewShell* pViewShell = rViewData.GetViewShell(); 759 ScTabViewShell::notifyAllViewsSheetGeomInvalidation(pViewShell, true /* bColumns */, true /* bRows */, 760 true /* bSizes */, false /* bHidden */, false /* bFiltered */, false /* bGroups */, rViewData.GetTabNo()); 761 } 762 } 763 return bRet; 764 } 765 766 // P A S T E 767 768 bool ScViewFunc::PasteOnDrawObjectLinked( 769 const uno::Reference<datatransfer::XTransferable>& rxTransferable, 770 SdrObject& rHitObj) 771 { 772 TransferableDataHelper aDataHelper( rxTransferable ); 773 774 if ( aDataHelper.HasFormat( SotClipboardFormatId::SVXB ) ) 775 { 776 tools::SvRef<SotTempStream> xStm; 777 ScDrawView* pScDrawView = GetScDrawView(); 778 779 if( pScDrawView && aDataHelper.GetSotStorageStream( SotClipboardFormatId::SVXB, xStm ) ) 780 { 781 Graphic aGraphic; 782 TypeSerializer aSerializer(*xStm); 783 aSerializer.readGraphic(aGraphic); 784 785 const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP)); 786 787 if(pScDrawView->ApplyGraphicToObject( rHitObj, aGraphic, aBeginUndo, "" )) 788 { 789 return true; 790 } 791 } 792 } 793 else if ( aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE ) ) 794 { 795 GDIMetaFile aMtf; 796 ScDrawView* pScDrawView = GetScDrawView(); 797 798 if( pScDrawView && aDataHelper.GetGDIMetaFile( SotClipboardFormatId::GDIMETAFILE, aMtf ) ) 799 { 800 const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP)); 801 802 if(pScDrawView->ApplyGraphicToObject( rHitObj, Graphic(aMtf), aBeginUndo, "" )) 803 { 804 return true; 805 } 806 } 807 } 808 else if ( aDataHelper.HasFormat( SotClipboardFormatId::BITMAP ) || aDataHelper.HasFormat( SotClipboardFormatId::PNG ) ) 809 { 810 BitmapEx aBmpEx; 811 ScDrawView* pScDrawView = GetScDrawView(); 812 813 if( pScDrawView && aDataHelper.GetBitmapEx( SotClipboardFormatId::BITMAP, aBmpEx ) ) 814 { 815 const OUString aBeginUndo(ScResId(STR_UNDO_DRAGDROP)); 816 817 if(pScDrawView->ApplyGraphicToObject( rHitObj, Graphic(aBmpEx), aBeginUndo, "" )) 818 { 819 return true; 820 } 821 } 822 } 823 824 return false; 825 } 826 827 static bool lcl_SelHasAttrib( const ScDocument& rDoc, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, 828 const ScMarkData& rTabSelection, HasAttrFlags nMask ) 829 { 830 return std::any_of(rTabSelection.begin(), rTabSelection.end(), 831 [&](const SCTAB& rTab) { return rDoc.HasAttrib( nCol1, nRow1, rTab, nCol2, nRow2, rTab, nMask ); }); 832 } 833 834 // paste into sheet: 835 836 // internal paste 837 838 namespace { 839 840 bool checkDestRangeForOverwrite(const ScRangeList& rDestRanges, const ScDocument& rDoc, const ScMarkData& rMark, weld::Window* pParentWnd) 841 { 842 bool bIsEmpty = true; 843 size_t nRangeSize = rDestRanges.size(); 844 for (const auto& rTab : rMark) 845 { 846 for (size_t i = 0; i < nRangeSize && bIsEmpty; ++i) 847 { 848 const ScRange& rRange = rDestRanges[i]; 849 bIsEmpty = rDoc.IsBlockEmpty( 850 rTab, rRange.aStart.Col(), rRange.aStart.Row(), 851 rRange.aEnd.Col(), rRange.aEnd.Row()); 852 } 853 if (!bIsEmpty) 854 break; 855 } 856 857 if (!bIsEmpty) 858 { 859 ScReplaceWarnBox aBox(pParentWnd); 860 if (aBox.run() != RET_YES) 861 { 862 // changing the configuration is within the ScReplaceWarnBox 863 return false; 864 } 865 } 866 return true; 867 } 868 869 } 870 871 bool ScViewFunc::PasteFromClip( InsertDeleteFlags nFlags, ScDocument* pClipDoc, 872 ScPasteFunc nFunction, bool bSkipEmpty, 873 bool bTranspose, bool bAsLink, 874 InsCellCmd eMoveMode, InsertDeleteFlags nUndoExtraFlags, 875 bool bAllowDialogs ) 876 { 877 if (!pClipDoc) 878 { 879 OSL_FAIL("PasteFromClip: pClipDoc=0 not allowed"); 880 return false; 881 } 882 883 if (GetViewData().SelectionForbidsPaste(pClipDoc)) 884 return false; 885 886 // undo: save all or no content 887 InsertDeleteFlags nContFlags = InsertDeleteFlags::NONE; 888 if (nFlags & InsertDeleteFlags::CONTENTS) 889 nContFlags |= InsertDeleteFlags::CONTENTS; 890 if (nFlags & InsertDeleteFlags::ATTRIB) 891 nContFlags |= InsertDeleteFlags::ATTRIB; 892 // move attributes to undo without copying them from clip to doc 893 InsertDeleteFlags nUndoFlags = nContFlags; 894 if (nUndoExtraFlags & InsertDeleteFlags::ATTRIB) 895 nUndoFlags |= InsertDeleteFlags::ATTRIB; 896 // do not copy note captions into undo document 897 nUndoFlags |= InsertDeleteFlags::NOCAPTIONS; 898 899 ScClipParam& rClipParam = pClipDoc->GetClipParam(); 900 if (rClipParam.isMultiRange()) 901 { 902 // Source data is multi-range. 903 return PasteMultiRangesFromClip(nFlags, pClipDoc, nFunction, bSkipEmpty, false, bTranspose, 904 bAsLink, bAllowDialogs, eMoveMode, nUndoFlags); 905 } 906 907 ScMarkData& rMark = GetViewData().GetMarkData(); 908 if (rMark.IsMultiMarked()) 909 { 910 // Source data is single-range but destination is multi-range. 911 return PasteFromClipToMultiRanges( 912 nFlags, pClipDoc, nFunction, bSkipEmpty, bTranspose, bAsLink, bAllowDialogs, 913 eMoveMode, nUndoFlags); 914 } 915 916 bool bCutMode = pClipDoc->IsCutMode(); // if transposing, take from original clipdoc 917 bool bIncludeFiltered = bCutMode; 918 919 // paste drawing: also if InsertDeleteFlags::NOTE is set (to create drawing layer for note captions) 920 bool bPasteDraw = ( pClipDoc->GetDrawLayer() && ( nFlags & (InsertDeleteFlags::OBJECTS|InsertDeleteFlags::NOTE) ) ); 921 922 ScDocShellRef aTransShellRef; // for objects in xTransClip - must remain valid as long as xTransClip 923 ScDocument* pOrigClipDoc = nullptr; 924 ScDocumentUniquePtr xTransClip; 925 if ( bTranspose ) 926 { 927 SCCOL nX; 928 SCROW nY; 929 // include filtered rows until TransposeClip can skip them 930 pClipDoc->GetClipArea( nX, nY, true ); 931 if ( nY > static_cast<sal_Int32>(pClipDoc->MaxCol()) ) // too many lines for transpose 932 { 933 ErrorMessage(STR_PASTE_FULL); 934 return false; 935 } 936 pOrigClipDoc = pClipDoc; // refs 937 938 if ( bPasteDraw ) 939 { 940 aTransShellRef = new ScDocShell; // DocShell needs a Ref immediately 941 aTransShellRef->DoInitNew(); 942 } 943 ScDrawLayer::SetGlobalDrawPersist( aTransShellRef.get() ); 944 945 xTransClip.reset( new ScDocument( SCDOCMODE_CLIP )); 946 pClipDoc->TransposeClip(xTransClip.get(), nFlags, bAsLink, bIncludeFiltered); 947 pClipDoc = xTransClip.get(); 948 949 ScDrawLayer::SetGlobalDrawPersist(nullptr); 950 } 951 952 // TODO: position this call better for performance. 953 ResetAutoSpellForContentChange(); 954 955 SCCOL nStartCol; 956 SCROW nStartRow; 957 SCTAB nStartTab; 958 SCCOL nEndCol; 959 SCROW nEndRow; 960 SCTAB nEndTab; 961 SCCOL nClipSizeX; 962 SCROW nClipSizeY; 963 pClipDoc->GetClipArea( nClipSizeX, nClipSizeY, true ); // size in clipboard doc 964 965 // size in target doc: include filtered rows only if CutMode is set 966 SCCOL nDestSizeX; 967 SCROW nDestSizeY; 968 pClipDoc->GetClipArea( nDestSizeX, nDestSizeY, bIncludeFiltered ); 969 970 ScDocument& rDoc = GetViewData().GetDocument(); 971 ScDocShell* pDocSh = GetViewData().GetDocShell(); 972 SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager(); 973 const bool bRecord(rDoc.IsUndoEnabled()); 974 975 ScDocShellModificator aModificator( *pDocSh ); 976 977 ScRange aMarkRange; 978 ScMarkData aFilteredMark( rMark); // local copy for all modifications 979 ScMarkType eMarkType = GetViewData().GetSimpleArea( aMarkRange, aFilteredMark); 980 bool bMarkIsFiltered = (eMarkType == SC_MARK_SIMPLE_FILTERED); 981 bool bNoPaste = ((eMarkType != SC_MARK_SIMPLE && !bMarkIsFiltered) || 982 (bMarkIsFiltered && (eMoveMode != INS_NONE || bAsLink))); 983 984 if (!bNoPaste) 985 { 986 if (!rMark.IsMarked()) 987 { 988 // Create a selection with clipboard row count and check that for 989 // filtered. 990 nStartCol = GetViewData().GetCurX(); 991 nStartRow = GetViewData().GetCurY(); 992 nStartTab = GetViewData().GetTabNo(); 993 nEndCol = nStartCol + nDestSizeX; 994 nEndRow = nStartRow + nDestSizeY; 995 nEndTab = nStartTab; 996 aMarkRange = ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab); 997 if (ScViewUtil::HasFiltered(aMarkRange, rDoc)) 998 { 999 bMarkIsFiltered = true; 1000 // Fit to clipboard's row count unfiltered rows. If there is no 1001 // fit assume that pasting is not possible. Note that nDestSizeY is 1002 // size-1 (difference). 1003 if (!ScViewUtil::FitToUnfilteredRows(aMarkRange, rDoc, nDestSizeY+1)) 1004 bNoPaste = true; 1005 } 1006 aFilteredMark.SetMarkArea( aMarkRange); 1007 } 1008 else 1009 { 1010 // Expand the marked area when the destination area is larger than the 1011 // current selection, to get the undo do the right thing. (i#106711) 1012 ScRange aRange; 1013 aFilteredMark.GetMarkArea( aRange ); 1014 if( (aRange.aEnd.Col() - aRange.aStart.Col()) < nDestSizeX ) 1015 { 1016 aRange.aEnd.SetCol(aRange.aStart.Col() + nDestSizeX); 1017 aFilteredMark.SetMarkArea(aRange); 1018 } 1019 } 1020 } 1021 1022 if (bNoPaste) 1023 { 1024 ErrorMessage(STR_MSSG_PASTEFROMCLIP_0); 1025 return false; 1026 } 1027 1028 SCROW nUnfilteredRows = aMarkRange.aEnd.Row() - aMarkRange.aStart.Row() + 1; 1029 ScRangeList aRangeList; 1030 if (bMarkIsFiltered) 1031 { 1032 ScViewUtil::UnmarkFiltered(aFilteredMark, rDoc); 1033 aFilteredMark.FillRangeListWithMarks( &aRangeList, false); 1034 nUnfilteredRows = 0; 1035 size_t ListSize = aRangeList.size(); 1036 for ( size_t i = 0; i < ListSize; ++i ) 1037 { 1038 ScRange & r = aRangeList[i]; 1039 nUnfilteredRows += r.aEnd.Row() - r.aStart.Row() + 1; 1040 } 1041 #if 0 1042 /* This isn't needed but could be a desired restriction. */ 1043 // For filtered, destination rows have to be an exact multiple of 1044 // source rows. Note that nDestSizeY is size-1 (difference), so 1045 // nDestSizeY==0 fits always. 1046 if ((nUnfilteredRows % (nDestSizeY+1)) != 0) 1047 { 1048 /* FIXME: this should be a more descriptive error message then. */ 1049 ErrorMessage(STR_MSSG_PASTEFROMCLIP_0); 1050 return false; 1051 } 1052 #endif 1053 } 1054 1055 // Also for a filtered selection the area is used, for undo et al. 1056 if ( aFilteredMark.IsMarked() || bMarkIsFiltered ) 1057 { 1058 aMarkRange.GetVars( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab); 1059 SCCOL nBlockAddX = nEndCol-nStartCol; 1060 SCROW nBlockAddY = nEndRow-nStartRow; 1061 1062 // request, if the selection is greater than one row/column, but smaller 1063 // as the Clipboard (then inserting is done beyond the selection) 1064 1065 // ClipSize is not size, but difference 1066 if ( ( nBlockAddX != 0 && nBlockAddX < nDestSizeX ) || 1067 ( nBlockAddY != 0 && nBlockAddY < nDestSizeY ) || 1068 ( bMarkIsFiltered && nUnfilteredRows < nDestSizeY+1 ) ) 1069 { 1070 ScWaitCursorOff aWaitOff( GetFrameWin() ); 1071 OUString aMessage = ScResId( STR_PASTE_BIGGER ); 1072 1073 std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(GetViewData().GetDialogParent(), 1074 VclMessageType::Question, VclButtonsType::YesNo, 1075 aMessage)); 1076 xQueryBox->set_default_response(RET_NO); 1077 if (xQueryBox->run() != RET_YES) 1078 { 1079 return false; 1080 } 1081 } 1082 1083 if (nBlockAddX <= nDestSizeX) 1084 nEndCol = nStartCol + nDestSizeX; 1085 1086 if (nBlockAddY <= nDestSizeY) 1087 { 1088 nEndRow = nStartRow + nDestSizeY; 1089 if (bMarkIsFiltered || nEndRow > aMarkRange.aEnd.Row()) 1090 { 1091 // Same as above if nothing was marked: re-fit selection to 1092 // unfiltered rows. Extending the selection actually may 1093 // introduce filtered rows where there weren't any before, so 1094 // we also need to test for that. 1095 aMarkRange = ScRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab); 1096 if (bMarkIsFiltered || ScViewUtil::HasFiltered(aMarkRange, rDoc)) 1097 { 1098 bMarkIsFiltered = true; 1099 // Worst case: all rows up to the end of the sheet are filtered. 1100 if (!ScViewUtil::FitToUnfilteredRows(aMarkRange, rDoc, nDestSizeY+1)) 1101 { 1102 ErrorMessage(STR_PASTE_FULL); 1103 return false; 1104 } 1105 } 1106 aMarkRange.GetVars( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab); 1107 aFilteredMark.SetMarkArea( aMarkRange); 1108 if (bMarkIsFiltered) 1109 { 1110 ScViewUtil::UnmarkFiltered(aFilteredMark, rDoc); 1111 aFilteredMark.FillRangeListWithMarks( &aRangeList, true); 1112 } 1113 } 1114 } 1115 } 1116 else 1117 { 1118 nStartCol = GetViewData().GetCurX(); 1119 nStartRow = GetViewData().GetCurY(); 1120 nStartTab = GetViewData().GetTabNo(); 1121 nEndCol = nStartCol + nDestSizeX; 1122 nEndRow = nStartRow + nDestSizeY; 1123 nEndTab = nStartTab; 1124 } 1125 1126 bool bOffLimits = !rDoc.ValidCol(nEndCol) || !rDoc.ValidRow(nEndRow); 1127 1128 // target-range, as displayed: 1129 ScRange aUserRange( nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab ); 1130 1131 // should lines be inserted? 1132 // ( too large nEndCol/nEndRow are detected below) 1133 bool bInsertCells = ( eMoveMode != INS_NONE && !bOffLimits ); 1134 if ( bInsertCells ) 1135 { 1136 // Instead of EnterListAction, the paste undo action is merged into the 1137 // insert action, so Repeat can insert the right cells 1138 1139 MarkRange( aUserRange ); // set through CopyFromClip 1140 1141 // CutMode is reset on insertion of cols/rows but needed again on cell move 1142 bool bCut = pClipDoc->IsCutMode(); 1143 if (!InsertCells( eMoveMode, bRecord, true )) // is inserting possible? 1144 { 1145 return false; 1146 // #i21036# EnterListAction isn't used, and InsertCells doesn't insert 1147 // its undo action on failure, so no undo handling is needed here 1148 } 1149 if ( bCut ) 1150 pClipDoc->SetCutMode( bCut ); 1151 } 1152 else if (!bOffLimits) 1153 { 1154 bool bAskIfNotEmpty = bAllowDialogs && 1155 ( nFlags & InsertDeleteFlags::CONTENTS ) && 1156 nFunction == ScPasteFunc::NONE && 1157 SC_MOD()->GetInputOptions().GetReplaceCellsWarn(); 1158 if ( bAskIfNotEmpty ) 1159 { 1160 ScRangeList aTestRanges(aUserRange); 1161 if (!checkDestRangeForOverwrite(aTestRanges, rDoc, aFilteredMark, GetViewData().GetDialogParent())) 1162 return false; 1163 } 1164 } 1165 1166 SCCOL nClipStartX; // enlarge clipboard-range 1167 SCROW nClipStartY; 1168 pClipDoc->GetClipStart( nClipStartX, nClipStartY ); 1169 SCCOL nUndoEndCol = nClipStartX + nClipSizeX; 1170 SCROW nUndoEndRow = nClipStartY + nClipSizeY; // end of source area in clipboard document 1171 bool bClipOver = false; 1172 // #i68690# ExtendMerge for the clip doc must be called with the clipboard's sheet numbers. 1173 // The same end column/row can be used for all calls because the clip doc doesn't contain 1174 // content outside the clip area. 1175 for (SCTAB nClipTab=0; nClipTab < pClipDoc->GetTableCount(); nClipTab++) 1176 if ( pClipDoc->HasTable(nClipTab) ) 1177 if ( pClipDoc->ExtendMerge( nClipStartX,nClipStartY, nUndoEndCol,nUndoEndRow, nClipTab ) ) 1178 bClipOver = true; 1179 nUndoEndCol -= nClipStartX + nClipSizeX; 1180 nUndoEndRow -= nClipStartY + nClipSizeY; // now contains only the difference added by ExtendMerge 1181 nUndoEndCol = sal::static_int_cast<SCCOL>( nUndoEndCol + nEndCol ); 1182 nUndoEndRow = sal::static_int_cast<SCROW>( nUndoEndRow + nEndRow ); // destination area, expanded for merged cells 1183 1184 if (nUndoEndCol>pClipDoc->MaxCol() || nUndoEndRow>pClipDoc->MaxRow()) 1185 { 1186 ErrorMessage(STR_PASTE_FULL); 1187 return false; 1188 } 1189 1190 rDoc.ExtendMergeSel( nStartCol,nStartRow, nUndoEndCol,nUndoEndRow, aFilteredMark ); 1191 1192 // check cell-protection 1193 1194 ScEditableTester aTester( rDoc, nStartTab, nStartCol,nStartRow, nUndoEndCol,nUndoEndRow ); 1195 if (!aTester.IsEditable()) 1196 { 1197 ErrorMessage(aTester.GetMessageId()); 1198 return false; 1199 } 1200 1201 //! check overlapping 1202 //! just check truly intersection !!!!!!! 1203 1204 ScDocFunc& rDocFunc = pDocSh->GetDocFunc(); 1205 if ( bRecord ) 1206 { 1207 OUString aUndo = ScResId( pClipDoc->IsCutMode() ? STR_UNDO_MOVE : STR_UNDO_COPY ); 1208 pUndoMgr->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() ); 1209 } 1210 1211 if (bClipOver) 1212 if (lcl_SelHasAttrib( rDoc, nStartCol,nStartRow, nUndoEndCol,nUndoEndRow, aFilteredMark, HasAttrFlags::Overlapped )) 1213 { // "Cell merge not possible if cells already merged" 1214 ScDocAttrIterator aIter( rDoc, nStartTab, nStartCol, nStartRow, nUndoEndCol, nUndoEndRow ); 1215 const ScPatternAttr* pPattern = nullptr; 1216 SCCOL nCol = -1; 1217 SCROW nRow1 = -1; 1218 SCROW nRow2 = -1; 1219 while ( ( pPattern = aIter.GetNext( nCol, nRow1, nRow2 ) ) != nullptr ) 1220 { 1221 const ScMergeAttr& rMergeFlag = pPattern->GetItem(ATTR_MERGE); 1222 const ScMergeFlagAttr& rMergeFlagAttr = pPattern->GetItem(ATTR_MERGE_FLAG); 1223 if (rMergeFlag.IsMerged() || rMergeFlagAttr.IsOverlapped()) 1224 { 1225 ScRange aRange(nCol, nRow1, nStartTab); 1226 rDoc.ExtendOverlapped(aRange); 1227 rDoc.ExtendMerge(aRange, true); 1228 rDocFunc.UnmergeCells(aRange, bRecord, nullptr /*TODO: should pass combined UndoDoc if bRecord*/); 1229 } 1230 } 1231 } 1232 1233 if ( !bCutMode ) 1234 { 1235 ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack(); 1236 if ( pChangeTrack ) 1237 pChangeTrack->ResetLastCut(); // no more cut-mode 1238 } 1239 1240 bool bColInfo = ( nStartRow==0 && nEndRow==rDoc.MaxRow() ); 1241 bool bRowInfo = ( nStartCol==0 && nEndCol==rDoc.MaxCol() ); 1242 1243 ScDocumentUniquePtr pUndoDoc; 1244 std::unique_ptr<ScDocument> pRefUndoDoc; 1245 std::unique_ptr<ScRefUndoData> pUndoData; 1246 1247 if ( bRecord ) 1248 { 1249 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 1250 pUndoDoc->InitUndoSelected( rDoc, aFilteredMark, bColInfo, bRowInfo ); 1251 1252 // all sheets - CopyToDocument skips those that don't exist in pUndoDoc 1253 SCTAB nTabCount = rDoc.GetTableCount(); 1254 rDoc.CopyToDocument( nStartCol, nStartRow, 0, nUndoEndCol, nUndoEndRow, nTabCount-1, 1255 nUndoFlags, false, *pUndoDoc ); 1256 1257 if ( bCutMode ) 1258 { 1259 pRefUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 1260 pRefUndoDoc->InitUndo( rDoc, 0, nTabCount-1 ); 1261 1262 pUndoData.reset(new ScRefUndoData( &rDoc )); 1263 } 1264 } 1265 1266 sal_uInt16 nExtFlags = 0; 1267 pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab, 1268 nEndCol, nEndRow, nEndTab ); // content before the change 1269 1270 if (GetViewData().IsActive()) 1271 { 1272 DoneBlockMode(); 1273 InitOwnBlockMode(); 1274 } 1275 rMark.SetMarkArea( aUserRange ); 1276 MarkDataChanged(); 1277 1278 // copy from clipboard 1279 // save original data in case of calculation 1280 1281 ScDocumentUniquePtr pMixDoc; 1282 if (nFunction != ScPasteFunc::NONE) 1283 { 1284 bSkipEmpty = false; 1285 if ( nFlags & InsertDeleteFlags::CONTENTS ) 1286 { 1287 pMixDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 1288 pMixDoc->InitUndo( rDoc, nStartTab, nEndTab ); 1289 rDoc.CopyToDocument(nStartCol, nStartRow, nStartTab, nEndCol, nEndRow, nEndTab, 1290 InsertDeleteFlags::CONTENTS, false, *pMixDoc); 1291 } 1292 } 1293 1294 /* Make draw layer and start drawing undo. 1295 - Needed before AdjustBlockHeight to track moved drawing objects. 1296 - Needed before rDoc.CopyFromClip to track inserted note caption objects. 1297 */ 1298 if ( bPasteDraw ) 1299 pDocSh->MakeDrawLayer(); 1300 if ( bRecord ) 1301 rDoc.BeginDrawUndo(); 1302 1303 InsertDeleteFlags nNoObjFlags = nFlags & ~InsertDeleteFlags::OBJECTS; 1304 if (!bAsLink) 1305 { 1306 // copy normally (original range) 1307 rDoc.CopyFromClip( aUserRange, aFilteredMark, nNoObjFlags, 1308 pRefUndoDoc.get(), pClipDoc, true, false, bIncludeFiltered, 1309 bSkipEmpty, (bMarkIsFiltered ? &aRangeList : nullptr) ); 1310 1311 // adapt refs manually in case of transpose 1312 if ( bTranspose && bCutMode && (nFlags & InsertDeleteFlags::CONTENTS) ) 1313 rDoc.UpdateTranspose( aUserRange.aStart, pOrigClipDoc, aFilteredMark, pRefUndoDoc.get() ); 1314 } 1315 else if (!bTranspose) 1316 { 1317 // copy with bAsLink=TRUE 1318 rDoc.CopyFromClip( aUserRange, aFilteredMark, nNoObjFlags, pRefUndoDoc.get(), pClipDoc, 1319 true, true, bIncludeFiltered, bSkipEmpty ); 1320 } 1321 else 1322 { 1323 // copy all content (TransClipDoc contains only formula) 1324 rDoc.CopyFromClip( aUserRange, aFilteredMark, nContFlags, pRefUndoDoc.get(), pClipDoc ); 1325 } 1326 1327 // skipped rows and merged cells don't mix 1328 if ( !bIncludeFiltered && pClipDoc->HasClipFilteredRows() ) 1329 rDocFunc.UnmergeCells( aUserRange, false, nullptr ); 1330 1331 rDoc.ExtendMergeSel( nStartCol, nStartRow, nEndCol, nEndRow, aFilteredMark, true ); // refresh 1332 // new range 1333 1334 if ( pMixDoc ) // calculate with original data? 1335 { 1336 rDoc.MixDocument( aUserRange, nFunction, bSkipEmpty, *pMixDoc ); 1337 } 1338 pMixDoc.reset(); 1339 1340 AdjustBlockHeight(); // update row heights before pasting objects 1341 1342 ::std::vector< OUString > aExcludedChartNames; 1343 SdrPage* pPage = nullptr; 1344 1345 if ( nFlags & InsertDeleteFlags::OBJECTS ) 1346 { 1347 ScDrawView* pScDrawView = GetScDrawView(); 1348 SdrModel* pModel = ( pScDrawView ? pScDrawView->GetModel() : nullptr ); 1349 pPage = ( pModel ? pModel->GetPage( static_cast< sal_uInt16 >( nStartTab ) ) : nullptr ); 1350 if ( pPage ) 1351 { 1352 ScChartHelper::GetChartNames( aExcludedChartNames, pPage ); 1353 } 1354 1355 // Paste the drawing objects after the row heights have been updated. 1356 1357 rDoc.CopyFromClip( aUserRange, aFilteredMark, InsertDeleteFlags::OBJECTS, pRefUndoDoc.get(), pClipDoc, 1358 true, false, bIncludeFiltered ); 1359 } 1360 1361 pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab, 1362 nEndCol, nEndRow, nEndTab ); // content after the change 1363 1364 // if necessary, delete autofilter-heads 1365 if (bCutMode) 1366 if (rDoc.RefreshAutoFilter( nClipStartX,nClipStartY, nClipStartX+nClipSizeX, 1367 nClipStartY+nClipSizeY, nStartTab )) 1368 { 1369 pDocSh->PostPaint( 1370 ScRange(nClipStartX, nClipStartY, nStartTab, nClipStartX+nClipSizeX, nClipStartY, nStartTab), 1371 PaintPartFlags::Grid ); 1372 } 1373 1374 //! remove block-range on RefUndoDoc !!! 1375 1376 if ( bRecord ) 1377 { 1378 ScDocumentUniquePtr pRedoDoc; 1379 // copy redo data after appearance of the first undo 1380 // don't create Redo-Doc without RefUndoDoc 1381 1382 if (pRefUndoDoc) 1383 { 1384 pRedoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 1385 pRedoDoc->InitUndo( rDoc, nStartTab, nEndTab, bColInfo, bRowInfo ); 1386 1387 // move adapted refs to Redo-Doc 1388 1389 SCTAB nTabCount = rDoc.GetTableCount(); 1390 pRedoDoc->AddUndoTab( 0, nTabCount-1 ); 1391 rDoc.CopyUpdated( pRefUndoDoc.get(), pRedoDoc.get() ); 1392 1393 // move old refs to Undo-Doc 1394 1395 // not charts? 1396 pUndoDoc->AddUndoTab( 0, nTabCount-1 ); 1397 pRefUndoDoc->DeleteArea( nStartCol, nStartRow, nEndCol, nEndRow, aFilteredMark, InsertDeleteFlags::ALL ); 1398 pRefUndoDoc->CopyToDocument( 0,0,0, pUndoDoc->MaxCol(), pUndoDoc->MaxRow(), nTabCount-1, 1399 InsertDeleteFlags::FORMULA, false, *pUndoDoc ); 1400 pRefUndoDoc.reset(); 1401 } 1402 1403 // DeleteUnchanged for pUndoData is in ScUndoPaste ctor, 1404 // UndoData for redo is made during first undo 1405 1406 ScUndoPasteOptions aOptions; // store options for repeat 1407 aOptions.nFunction = nFunction; 1408 aOptions.bSkipEmpty = bSkipEmpty; 1409 aOptions.bTranspose = bTranspose; 1410 aOptions.bAsLink = bAsLink; 1411 aOptions.eMoveMode = eMoveMode; 1412 1413 std::unique_ptr<SfxUndoAction> pUndo(new ScUndoPaste( 1414 pDocSh, ScRange(nStartCol, nStartRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab), 1415 aFilteredMark, std::move(pUndoDoc), std::move(pRedoDoc), nFlags | nUndoFlags, std::move(pUndoData), 1416 false, &aOptions )); // false = Redo data not yet copied 1417 1418 if ( bInsertCells ) 1419 { 1420 // Merge the paste undo action into the insert action. 1421 // Use ScUndoWrapper so the ScUndoPaste pointer can be stored in the insert action. 1422 1423 pUndoMgr->AddUndoAction( std::make_unique<ScUndoWrapper>( std::move(pUndo) ), true ); 1424 } 1425 else 1426 pUndoMgr->AddUndoAction( std::move(pUndo) ); 1427 pUndoMgr->LeaveListAction(); 1428 } 1429 1430 PaintPartFlags nPaint = PaintPartFlags::Grid; 1431 if (bColInfo) 1432 { 1433 nPaint |= PaintPartFlags::Top; 1434 nUndoEndCol = rDoc.MaxCol(); // just for drawing ! 1435 } 1436 if (bRowInfo) 1437 { 1438 nPaint |= PaintPartFlags::Left; 1439 nUndoEndRow = rDoc.MaxRow(); // just for drawing ! 1440 } 1441 pDocSh->PostPaint( 1442 ScRange(nStartCol, nStartRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab), 1443 nPaint, nExtFlags); 1444 // AdjustBlockHeight has already been called above 1445 1446 aModificator.SetDocumentModified(); 1447 PostPasteFromClip(aUserRange, rMark); 1448 1449 if ( nFlags & InsertDeleteFlags::OBJECTS ) 1450 { 1451 ScModelObj* pModelObj = comphelper::getFromUnoTunnel<ScModelObj>( pDocSh->GetModel() ); 1452 if ( pPage && pModelObj ) 1453 { 1454 bool bSameDoc = ( rClipParam.getSourceDocID() == rDoc.GetDocumentID() ); 1455 const ScRangeListVector& rProtectedChartRangesVector( rClipParam.maProtectedChartRangesVector ); 1456 ScChartHelper::CreateProtectedChartListenersAndNotify( rDoc, pPage, pModelObj, nStartTab, 1457 rProtectedChartRangesVector, aExcludedChartNames, bSameDoc ); 1458 } 1459 } 1460 OUString aStartAddress = aMarkRange.aStart.GetColRowString(); 1461 OUString aEndAddress = aMarkRange.aEnd.GetColRowString(); 1462 collectUIInformation({{"RANGE", aStartAddress + ":" + aEndAddress}}, "PASTE"); 1463 return true; 1464 } 1465 1466 bool ScViewFunc::PasteMultiRangesFromClip(InsertDeleteFlags nFlags, ScDocument* pClipDoc, 1467 ScPasteFunc nFunction, bool bSkipEmpty, 1468 bool bIncludeFiltered, bool bTranspose, bool bAsLink, 1469 bool bAllowDialogs, InsCellCmd eMoveMode, 1470 InsertDeleteFlags nUndoFlags) 1471 { 1472 ScViewData& rViewData = GetViewData(); 1473 ScDocument& rDoc = rViewData.GetDocument(); 1474 ScDocShell* pDocSh = rViewData.GetDocShell(); 1475 ScMarkData aMark(rViewData.GetMarkData()); 1476 const ScAddress& rCurPos = rViewData.GetCurPos(); 1477 ScClipParam& rClipParam = pClipDoc->GetClipParam(); 1478 SCCOL nColSize = rClipParam.getPasteColSize(); 1479 SCROW nRowSize = rClipParam.getPasteRowSize(*pClipDoc, bIncludeFiltered); 1480 1481 if (bTranspose) 1482 { 1483 if (static_cast<SCROW>(rCurPos.Col()) + nRowSize-1 > static_cast<SCROW>(pClipDoc->MaxCol())) 1484 { 1485 ErrorMessage(STR_PASTE_FULL); 1486 return false; 1487 } 1488 1489 ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP)); 1490 pClipDoc->TransposeClip(pTransClip.get(), nFlags, bAsLink, bIncludeFiltered); 1491 pClipDoc = pTransClip.release(); 1492 SCCOL nTempColSize = nColSize; 1493 nColSize = static_cast<SCCOL>(nRowSize); 1494 nRowSize = static_cast<SCROW>(nTempColSize); 1495 } 1496 1497 if (!rDoc.ValidCol(rCurPos.Col()+nColSize-1) || !rDoc.ValidRow(rCurPos.Row()+nRowSize-1)) 1498 { 1499 ErrorMessage(STR_PASTE_FULL); 1500 return false; 1501 } 1502 1503 // Determine the first and last selected sheet numbers. 1504 SCTAB nTab1 = aMark.GetFirstSelected(); 1505 SCTAB nTab2 = aMark.GetLastSelected(); 1506 1507 ScDocShellModificator aModificator(*pDocSh); 1508 1509 // For multi-selection paste, we don't support cell duplication for larger 1510 // destination range. In case the destination is marked, we reset it to 1511 // the clip size. 1512 ScRange aMarkedRange(rCurPos.Col(), rCurPos.Row(), nTab1, 1513 rCurPos.Col()+nColSize-1, rCurPos.Row()+nRowSize-1, nTab2); 1514 1515 // Extend the marked range to account for filtered rows in the destination 1516 // area. 1517 if (ScViewUtil::HasFiltered(aMarkedRange, rDoc)) 1518 { 1519 if (!ScViewUtil::FitToUnfilteredRows(aMarkedRange, rDoc, nRowSize)) 1520 return false; 1521 } 1522 1523 bool bAskIfNotEmpty = 1524 bAllowDialogs && (nFlags & InsertDeleteFlags::CONTENTS) && 1525 nFunction == ScPasteFunc::NONE && SC_MOD()->GetInputOptions().GetReplaceCellsWarn(); 1526 1527 if (bAskIfNotEmpty) 1528 { 1529 ScRangeList aTestRanges(aMarkedRange); 1530 if (!checkDestRangeForOverwrite(aTestRanges, rDoc, aMark, GetViewData().GetDialogParent())) 1531 return false; 1532 } 1533 1534 aMark.SetMarkArea(aMarkedRange); 1535 MarkRange(aMarkedRange); 1536 1537 bool bInsertCells = (eMoveMode != INS_NONE); 1538 if (bInsertCells) 1539 { 1540 if (!InsertCells(eMoveMode, rDoc.IsUndoEnabled(), true)) 1541 return false; 1542 } 1543 1544 // TODO: position this call better for performance. 1545 ResetAutoSpellForContentChange(); 1546 1547 bool bRowInfo = ( aMarkedRange.aStart.Col()==0 && aMarkedRange.aEnd.Col()==pClipDoc->MaxCol() ); 1548 ScDocumentUniquePtr pUndoDoc; 1549 if (rDoc.IsUndoEnabled()) 1550 { 1551 pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO)); 1552 pUndoDoc->InitUndoSelected(rDoc, aMark, false, bRowInfo); 1553 rDoc.CopyToDocument(aMarkedRange, nUndoFlags, false, *pUndoDoc, &aMark); 1554 } 1555 1556 ScDocumentUniquePtr pMixDoc; 1557 if ( bSkipEmpty || nFunction != ScPasteFunc::NONE) 1558 { 1559 if ( nFlags & InsertDeleteFlags::CONTENTS ) 1560 { 1561 pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO)); 1562 pMixDoc->InitUndoSelected(rDoc, aMark); 1563 rDoc.CopyToDocument(aMarkedRange, InsertDeleteFlags::CONTENTS, false, *pMixDoc, &aMark); 1564 } 1565 } 1566 1567 /* Make draw layer and start drawing undo. 1568 - Needed before AdjustBlockHeight to track moved drawing objects. 1569 - Needed before rDoc.CopyFromClip to track inserted note caption objects. 1570 */ 1571 if (nFlags & InsertDeleteFlags::OBJECTS) 1572 pDocSh->MakeDrawLayer(); 1573 if (rDoc.IsUndoEnabled()) 1574 rDoc.BeginDrawUndo(); 1575 1576 InsertDeleteFlags nCopyFlags = nFlags & ~InsertDeleteFlags::OBJECTS; 1577 // in case of transpose, links were added in TransposeClip() 1578 if (bAsLink && bTranspose) 1579 nCopyFlags |= InsertDeleteFlags::FORMULA; 1580 rDoc.CopyMultiRangeFromClip(rCurPos, aMark, nCopyFlags, pClipDoc, true, bAsLink && !bTranspose, 1581 bIncludeFiltered, bSkipEmpty); 1582 1583 if (pMixDoc) 1584 rDoc.MixDocument(aMarkedRange, nFunction, bSkipEmpty, *pMixDoc); 1585 1586 AdjustBlockHeight(); // update row heights before pasting objects 1587 1588 if (nFlags & InsertDeleteFlags::OBJECTS) 1589 { 1590 // Paste the drawing objects after the row heights have been updated. 1591 rDoc.CopyMultiRangeFromClip(rCurPos, aMark, InsertDeleteFlags::OBJECTS, pClipDoc, true, 1592 false, bIncludeFiltered, true); 1593 } 1594 1595 if (bRowInfo) 1596 pDocSh->PostPaint(aMarkedRange.aStart.Col(), aMarkedRange.aStart.Row(), nTab1, pClipDoc->MaxCol(), pClipDoc->MaxRow(), nTab1, PaintPartFlags::Grid|PaintPartFlags::Left); 1597 else 1598 { 1599 ScRange aTmp = aMarkedRange; 1600 aTmp.aStart.SetTab(nTab1); 1601 aTmp.aEnd.SetTab(nTab1); 1602 pDocSh->PostPaint(aTmp, PaintPartFlags::Grid); 1603 } 1604 1605 if (rDoc.IsUndoEnabled()) 1606 { 1607 SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager(); 1608 OUString aUndo = ScResId( 1609 pClipDoc->IsCutMode() ? STR_UNDO_CUT : STR_UNDO_COPY); 1610 pUndoMgr->EnterListAction(aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId()); 1611 1612 ScUndoPasteOptions aOptions; // store options for repeat 1613 aOptions.nFunction = nFunction; 1614 aOptions.bSkipEmpty = bSkipEmpty; 1615 aOptions.bTranspose = bTranspose; 1616 aOptions.bAsLink = bAsLink; 1617 aOptions.eMoveMode = eMoveMode; 1618 1619 std::unique_ptr<ScUndoPaste> pUndo(new ScUndoPaste(pDocSh, 1620 aMarkedRange, aMark, std::move(pUndoDoc), nullptr, nFlags|nUndoFlags, nullptr, false, &aOptions)); 1621 1622 if (bInsertCells) 1623 pUndoMgr->AddUndoAction(std::make_unique<ScUndoWrapper>(std::move(pUndo)), true); 1624 else 1625 pUndoMgr->AddUndoAction(std::move(pUndo)); 1626 1627 pUndoMgr->LeaveListAction(); 1628 } 1629 1630 aModificator.SetDocumentModified(); 1631 PostPasteFromClip(aMarkedRange, aMark); 1632 return true; 1633 } 1634 1635 bool ScViewFunc::PasteFromClipToMultiRanges( 1636 InsertDeleteFlags nFlags, ScDocument* pClipDoc, ScPasteFunc nFunction, 1637 bool bSkipEmpty, bool bTranspose, bool bAsLink, bool bAllowDialogs, 1638 InsCellCmd eMoveMode, InsertDeleteFlags nUndoFlags ) 1639 { 1640 if (bTranspose) 1641 { 1642 // We don't allow transpose for this yet. 1643 ErrorMessage(STR_MSSG_PASTEFROMCLIP_0); 1644 return false; 1645 } 1646 1647 if (eMoveMode != INS_NONE) 1648 { 1649 // We don't allow insertion mode either. Too complicated. 1650 ErrorMessage(STR_MSSG_PASTEFROMCLIP_0); 1651 return false; 1652 } 1653 1654 ScViewData& rViewData = GetViewData(); 1655 ScClipParam& rClipParam = pClipDoc->GetClipParam(); 1656 if (rClipParam.mbCutMode) 1657 { 1658 // No cut and paste with this, please. 1659 ErrorMessage(STR_MSSG_PASTEFROMCLIP_0); 1660 return false; 1661 } 1662 1663 const ScAddress& rCurPos = rViewData.GetCurPos(); 1664 ScDocument& rDoc = rViewData.GetDocument(); 1665 1666 ScRange aSrcRange = rClipParam.getWholeRange(); 1667 SCROW nRowSize = aSrcRange.aEnd.Row() - aSrcRange.aStart.Row() + 1; 1668 SCCOL nColSize = aSrcRange.aEnd.Col() - aSrcRange.aStart.Col() + 1; 1669 1670 if (!rDoc.ValidCol(rCurPos.Col()+nColSize-1) || !rDoc.ValidRow(rCurPos.Row()+nRowSize-1)) 1671 { 1672 ErrorMessage(STR_PASTE_FULL); 1673 return false; 1674 } 1675 1676 ScMarkData aMark(rViewData.GetMarkData()); 1677 1678 ScRangeList aRanges; 1679 aMark.MarkToSimple(); 1680 aMark.FillRangeListWithMarks(&aRanges, false); 1681 if (!ScClipUtil::CheckDestRanges(rDoc, nColSize, nRowSize, aMark, aRanges)) 1682 { 1683 ErrorMessage(STR_MSSG_PASTEFROMCLIP_0); 1684 return false; 1685 } 1686 1687 ScDocShell* pDocSh = rViewData.GetDocShell(); 1688 1689 ScDocShellModificator aModificator(*pDocSh); 1690 1691 bool bAskIfNotEmpty = 1692 bAllowDialogs && (nFlags & InsertDeleteFlags::CONTENTS) && 1693 nFunction == ScPasteFunc::NONE && SC_MOD()->GetInputOptions().GetReplaceCellsWarn(); 1694 1695 if (bAskIfNotEmpty) 1696 { 1697 if (!checkDestRangeForOverwrite(aRanges, rDoc, aMark, GetViewData().GetDialogParent())) 1698 return false; 1699 } 1700 1701 // TODO: position this call better for performance. 1702 ResetAutoSpellForContentChange(); 1703 1704 ScDocumentUniquePtr pUndoDoc; 1705 if (rDoc.IsUndoEnabled()) 1706 { 1707 pUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO)); 1708 pUndoDoc->InitUndoSelected(rDoc, aMark); 1709 for (size_t i = 0, n = aRanges.size(); i < n; ++i) 1710 { 1711 rDoc.CopyToDocument( 1712 aRanges[i], nUndoFlags, false, *pUndoDoc, &aMark); 1713 } 1714 } 1715 1716 ScDocumentUniquePtr pMixDoc; 1717 if (bSkipEmpty || nFunction != ScPasteFunc::NONE) 1718 { 1719 if (nFlags & InsertDeleteFlags::CONTENTS) 1720 { 1721 pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO)); 1722 pMixDoc->InitUndoSelected(rDoc, aMark); 1723 for (size_t i = 0, n = aRanges.size(); i < n; ++i) 1724 { 1725 rDoc.CopyToDocument( 1726 aRanges[i], InsertDeleteFlags::CONTENTS, false, *pMixDoc, &aMark); 1727 } 1728 } 1729 } 1730 1731 if (nFlags & InsertDeleteFlags::OBJECTS) 1732 pDocSh->MakeDrawLayer(); 1733 if (rDoc.IsUndoEnabled()) 1734 rDoc.BeginDrawUndo(); 1735 1736 // First, paste everything but the drawing objects. 1737 for (size_t i = 0, n = aRanges.size(); i < n; ++i) 1738 { 1739 rDoc.CopyFromClip( 1740 aRanges[i], aMark, (nFlags & ~InsertDeleteFlags::OBJECTS), nullptr, pClipDoc, 1741 false, false, true, bSkipEmpty); 1742 } 1743 1744 if (pMixDoc) 1745 { 1746 for (size_t i = 0, n = aRanges.size(); i < n; ++i) 1747 rDoc.MixDocument(aRanges[i], nFunction, bSkipEmpty, *pMixDoc); 1748 } 1749 1750 AdjustBlockHeight(); // update row heights before pasting objects 1751 1752 // Then paste the objects. 1753 if (nFlags & InsertDeleteFlags::OBJECTS) 1754 { 1755 for (size_t i = 0, n = aRanges.size(); i < n; ++i) 1756 { 1757 rDoc.CopyFromClip( 1758 aRanges[i], aMark, InsertDeleteFlags::OBJECTS, nullptr, pClipDoc, 1759 false, false, true, bSkipEmpty); 1760 } 1761 } 1762 1763 // Refresh the range that includes all pasted ranges. We only need to 1764 // refresh the current sheet. 1765 PaintPartFlags nPaint = PaintPartFlags::Grid; 1766 bool bRowInfo = (aSrcRange.aStart.Col()==0 && aSrcRange.aEnd.Col()==pClipDoc->MaxCol()); 1767 if (bRowInfo) 1768 nPaint |= PaintPartFlags::Left; 1769 pDocSh->PostPaint(aRanges, nPaint); 1770 1771 if (rDoc.IsUndoEnabled()) 1772 { 1773 SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager(); 1774 OUString aUndo = ScResId( 1775 pClipDoc->IsCutMode() ? STR_UNDO_CUT : STR_UNDO_COPY); 1776 pUndoMgr->EnterListAction(aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId()); 1777 1778 ScUndoPasteOptions aOptions; // store options for repeat 1779 aOptions.nFunction = nFunction; 1780 aOptions.bSkipEmpty = bSkipEmpty; 1781 aOptions.bTranspose = bTranspose; 1782 aOptions.bAsLink = bAsLink; 1783 aOptions.eMoveMode = eMoveMode; 1784 1785 1786 pUndoMgr->AddUndoAction( 1787 std::make_unique<ScUndoPaste>( 1788 pDocSh, aRanges, aMark, std::move(pUndoDoc), nullptr, nFlags|nUndoFlags, nullptr, false, &aOptions)); 1789 pUndoMgr->LeaveListAction(); 1790 } 1791 1792 aModificator.SetDocumentModified(); 1793 PostPasteFromClip(aRanges, aMark); 1794 1795 return false; 1796 } 1797 1798 void ScViewFunc::PostPasteFromClip(const ScRangeList& rPasteRanges, const ScMarkData& rMark) 1799 { 1800 ScViewData& rViewData = GetViewData(); 1801 ScDocShell* pDocSh = rViewData.GetDocShell(); 1802 pDocSh->UpdateOle(rViewData); 1803 1804 SelectionChanged(true); 1805 1806 ScModelObj* pModelObj = HelperNotifyChanges::getMustPropagateChangesModel(*pDocSh); 1807 if (!pModelObj) 1808 return; 1809 1810 ScRangeList aChangeRanges; 1811 for (size_t i = 0, n = rPasteRanges.size(); i < n; ++i) 1812 { 1813 const ScRange& r = rPasteRanges[i]; 1814 for (const auto& rTab : rMark) 1815 { 1816 ScRange aChangeRange(r); 1817 aChangeRange.aStart.SetTab(rTab); 1818 aChangeRange.aEnd.SetTab(rTab); 1819 aChangeRanges.push_back(aChangeRange); 1820 } 1821 } 1822 HelperNotifyChanges::Notify(*pModelObj, aChangeRanges); 1823 } 1824 1825 // D R A G A N D D R O P 1826 1827 // inside the doc 1828 1829 bool ScViewFunc::MoveBlockTo( const ScRange& rSource, const ScAddress& rDestPos, 1830 bool bCut ) 1831 { 1832 ScDocShell* pDocSh = GetViewData().GetDocShell(); 1833 HideAllCursors(); 1834 1835 ResetAutoSpellForContentChange(); 1836 1837 bool bSuccess = true; 1838 SCTAB nDestTab = rDestPos.Tab(); 1839 const ScMarkData& rMark = GetViewData().GetMarkData(); 1840 if ( rSource.aStart.Tab() == nDestTab && rSource.aEnd.Tab() == nDestTab && rMark.GetSelectCount() > 1 ) 1841 { 1842 // moving within one table and several tables selected -> apply to all selected tables 1843 1844 OUString aUndo = ScResId( bCut ? STR_UNDO_MOVE : STR_UNDO_COPY ); 1845 pDocSh->GetUndoManager()->EnterListAction( aUndo, aUndo, 0, GetViewData().GetViewShell()->GetViewShellId() ); 1846 1847 // collect ranges of consecutive selected tables 1848 1849 ScRange aLocalSource = rSource; 1850 ScAddress aLocalDest = rDestPos; 1851 SCTAB nTabCount = pDocSh->GetDocument().GetTableCount(); 1852 SCTAB nStartTab = 0; 1853 while ( nStartTab < nTabCount && bSuccess ) 1854 { 1855 while ( nStartTab < nTabCount && !rMark.GetTableSelect(nStartTab) ) 1856 ++nStartTab; 1857 if ( nStartTab < nTabCount ) 1858 { 1859 SCTAB nEndTab = nStartTab; 1860 while ( nEndTab+1 < nTabCount && rMark.GetTableSelect(nEndTab+1) ) 1861 ++nEndTab; 1862 1863 aLocalSource.aStart.SetTab( nStartTab ); 1864 aLocalSource.aEnd.SetTab( nEndTab ); 1865 aLocalDest.SetTab( nStartTab ); 1866 1867 bSuccess = pDocSh->GetDocFunc().MoveBlock( 1868 aLocalSource, aLocalDest, bCut, true/*bRecord*/, true/*bPaint*/, true/*bApi*/ ); 1869 1870 nStartTab = nEndTab + 1; 1871 } 1872 } 1873 1874 pDocSh->GetUndoManager()->LeaveListAction(); 1875 } 1876 else 1877 { 1878 // move the block as specified 1879 bSuccess = pDocSh->GetDocFunc().MoveBlock( 1880 rSource, rDestPos, bCut, true/*bRecord*/, true/*bPaint*/, true/*bApi*/ ); 1881 } 1882 1883 ShowAllCursors(); 1884 if (bSuccess) 1885 { 1886 // mark destination range 1887 ScAddress aDestEnd( 1888 rDestPos.Col() + rSource.aEnd.Col() - rSource.aStart.Col(), 1889 rDestPos.Row() + rSource.aEnd.Row() - rSource.aStart.Row(), 1890 nDestTab ); 1891 1892 bool bIncludeFiltered = bCut; 1893 if ( !bIncludeFiltered ) 1894 { 1895 // find number of non-filtered rows 1896 SCROW nPastedCount = pDocSh->GetDocument().CountNonFilteredRows( 1897 rSource.aStart.Row(), rSource.aEnd.Row(), rSource.aStart.Tab()); 1898 1899 if ( nPastedCount == 0 ) 1900 nPastedCount = 1; 1901 aDestEnd.SetRow( rDestPos.Row() + nPastedCount - 1 ); 1902 } 1903 1904 MarkRange( ScRange( rDestPos, aDestEnd ), false ); //! sal_False ??? 1905 1906 pDocSh->UpdateOle(GetViewData()); 1907 SelectionChanged(); 1908 } 1909 return bSuccess; 1910 } 1911 1912 // link inside the doc 1913 1914 bool ScViewFunc::LinkBlock( const ScRange& rSource, const ScAddress& rDestPos ) 1915 { 1916 // check overlapping 1917 1918 if ( rSource.aStart.Tab() == rDestPos.Tab() ) 1919 { 1920 SCCOL nDestEndCol = rDestPos.Col() + ( rSource.aEnd.Col() - rSource.aStart.Col() ); 1921 SCROW nDestEndRow = rDestPos.Row() + ( rSource.aEnd.Row() - rSource.aStart.Row() ); 1922 1923 if ( rSource.aStart.Col() <= nDestEndCol && rDestPos.Col() <= rSource.aEnd.Col() && 1924 rSource.aStart.Row() <= nDestEndRow && rDestPos.Row() <= rSource.aEnd.Row() ) 1925 { 1926 return false; 1927 } 1928 } 1929 1930 // run with paste 1931 1932 ScDocument& rDoc = GetViewData().GetDocument(); 1933 ScDocumentUniquePtr pClipDoc(new ScDocument( SCDOCMODE_CLIP )); 1934 rDoc.CopyTabToClip( rSource.aStart.Col(), rSource.aStart.Row(), 1935 rSource.aEnd.Col(), rSource.aEnd.Row(), 1936 rSource.aStart.Tab(), pClipDoc.get() ); 1937 1938 // mark destination area (set cursor, no marks) 1939 1940 if ( GetViewData().GetTabNo() != rDestPos.Tab() ) 1941 SetTabNo( rDestPos.Tab() ); 1942 1943 MoveCursorAbs( rDestPos.Col(), rDestPos.Row(), SC_FOLLOW_NONE, false, false ); 1944 1945 // Paste 1946 1947 PasteFromClip( InsertDeleteFlags::ALL, pClipDoc.get(), ScPasteFunc::NONE, false, false, true ); // as a link 1948 1949 return true; 1950 } 1951 1952 void ScViewFunc::DataFormPutData( SCROW nCurrentRow , 1953 SCROW nStartRow , SCCOL nStartCol , 1954 SCROW nEndRow , SCCOL nEndCol , 1955 std::vector<std::unique_ptr<ScDataFormFragment>>& rEdits, 1956 sal_uInt16 aColLength ) 1957 { 1958 ScDocument& rDoc = GetViewData().GetDocument(); 1959 ScDocShell* pDocSh = GetViewData().GetDocShell(); 1960 ScMarkData& rMark = GetViewData().GetMarkData(); 1961 ScDocShellModificator aModificator( *pDocSh ); 1962 SfxUndoManager* pUndoMgr = pDocSh->GetUndoManager(); 1963 1964 const bool bRecord( rDoc.IsUndoEnabled()); 1965 ScDocumentUniquePtr pUndoDoc; 1966 ScDocumentUniquePtr pRedoDoc; 1967 std::unique_ptr<ScRefUndoData> pUndoData; 1968 SCTAB nTab = GetViewData().GetTabNo(); 1969 SCTAB nStartTab = nTab; 1970 SCTAB nEndTab = nTab; 1971 1972 { 1973 ScChangeTrack* pChangeTrack = rDoc.GetChangeTrack(); 1974 if ( pChangeTrack ) 1975 pChangeTrack->ResetLastCut(); // no more cut-mode 1976 } 1977 ScRange aUserRange( nStartCol, nCurrentRow, nStartTab, nEndCol, nCurrentRow, nEndTab ); 1978 bool bColInfo = ( nStartRow==0 && nEndRow==rDoc.MaxRow() ); 1979 bool bRowInfo = ( nStartCol==0 && nEndCol==rDoc.MaxCol() ); 1980 SCCOL nUndoEndCol = nStartCol+aColLength-1; 1981 SCROW nUndoEndRow = nCurrentRow; 1982 1983 if ( bRecord ) 1984 { 1985 pUndoDoc.reset(new ScDocument( SCDOCMODE_UNDO )); 1986 pUndoDoc->InitUndoSelected( rDoc , rMark , bColInfo , bRowInfo ); 1987 rDoc.CopyToDocument( aUserRange , InsertDeleteFlags::VALUE , false, *pUndoDoc ); 1988 } 1989 sal_uInt16 nExtFlags = 0; 1990 pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nStartRow, nStartTab , nEndCol, nEndRow, nEndTab ); // content before the change 1991 rDoc.BeginDrawUndo(); 1992 1993 for(sal_uInt16 i = 0; i < aColLength; i++) 1994 { 1995 if (rEdits[i] != nullptr) 1996 { 1997 OUString aFieldName = rEdits[i]->m_xEdit->get_text(); 1998 rDoc.SetString( nStartCol + i, nCurrentRow, nTab, aFieldName ); 1999 } 2000 } 2001 pDocSh->UpdatePaintExt( nExtFlags, nStartCol, nCurrentRow, nStartTab, nEndCol, nCurrentRow, nEndTab ); // content after the change 2002 std::unique_ptr<SfxUndoAction> pUndo( new ScUndoDataForm( pDocSh, 2003 nStartCol, nCurrentRow, nStartTab, 2004 nUndoEndCol, nUndoEndRow, nEndTab, rMark, 2005 std::move(pUndoDoc), std::move(pRedoDoc), 2006 std::move(pUndoData) ) ); 2007 pUndoMgr->AddUndoAction( std::make_unique<ScUndoWrapper>( std::move(pUndo) ), true ); 2008 2009 PaintPartFlags nPaint = PaintPartFlags::Grid; 2010 if (bColInfo) 2011 { 2012 nPaint |= PaintPartFlags::Top; 2013 nUndoEndCol = rDoc.MaxCol(); // just for drawing ! 2014 } 2015 if (bRowInfo) 2016 { 2017 nPaint |= PaintPartFlags::Left; 2018 nUndoEndRow = rDoc.MaxRow(); // just for drawing ! 2019 } 2020 2021 pDocSh->PostPaint( 2022 ScRange(nStartCol, nCurrentRow, nStartTab, nUndoEndCol, nUndoEndRow, nEndTab), 2023 nPaint, nExtFlags); 2024 pDocSh->UpdateOle(GetViewData()); 2025 } 2026 2027 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 2028
