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 #include <DocumentContentOperationsManager.hxx> 20 #include <DocumentRedlineManager.hxx> 21 #include <wrtsh.hxx> 22 #include <doc.hxx> 23 #include <IDocumentUndoRedo.hxx> 24 #include <IDocumentMarkAccess.hxx> 25 #include <IDocumentState.hxx> 26 #include <IDocumentLayoutAccess.hxx> 27 #include <IDocumentRedlineAccess.hxx> 28 #include <IDocumentStylePoolAccess.hxx> 29 #include <IDocumentSettingAccess.hxx> 30 #include <UndoManager.hxx> 31 #include <docary.hxx> 32 #include <textboxhelper.hxx> 33 #include <dcontact.hxx> 34 #include <grfatr.hxx> 35 #include <numrule.hxx> 36 #include <charfmt.hxx> 37 #include <ndgrf.hxx> 38 #include <ndnotxt.hxx> 39 #include <ndole.hxx> 40 #include <breakit.hxx> 41 #include <frmfmt.hxx> 42 #include <fmtanchr.hxx> 43 #include <fmtcntnt.hxx> 44 #include <fmtinfmt.hxx> 45 #include <fmtpdsc.hxx> 46 #include <fmtcnct.hxx> 47 #include <SwStyleNameMapper.hxx> 48 #include <redline.hxx> 49 #include <txtfrm.hxx> 50 #include <rootfrm.hxx> 51 #include <frmtool.hxx> 52 #include <unocrsr.hxx> 53 #include <mvsave.hxx> 54 #include <ndtxt.hxx> 55 #include <poolfmt.hxx> 56 #include <paratr.hxx> 57 #include <txatbase.hxx> 58 #include <UndoRedline.hxx> 59 #include <undobj.hxx> 60 #include <UndoBookmark.hxx> 61 #include <UndoDelete.hxx> 62 #include <UndoSplitMove.hxx> 63 #include <UndoOverwrite.hxx> 64 #include <UndoInsert.hxx> 65 #include <UndoAttribute.hxx> 66 #include <rolbck.hxx> 67 #include <acorrect.hxx> 68 #include <bookmark.hxx> 69 #include <ftnidx.hxx> 70 #include <txtftn.hxx> 71 #include <hints.hxx> 72 #include <fmtflcnt.hxx> 73 #include <docedt.hxx> 74 #include <frameformats.hxx> 75 #include <formatflysplit.hxx> 76 #include <o3tl/safeint.hxx> 77 #include <sal/log.hxx> 78 #include <unotools/charclass.hxx> 79 #include <unotools/configmgr.hxx> 80 #include <unotools/transliterationwrapper.hxx> 81 #include <i18nutil/transliteration.hxx> 82 #include <sfx2/Metadatable.hxx> 83 #include <sot/exchange.hxx> 84 #include <svl/stritem.hxx> 85 #include <svl/itemiter.hxx> 86 #include <svx/svdobj.hxx> 87 #include <svx/svdouno.hxx> 88 #include <tools/globname.hxx> 89 #include <editeng/formatbreakitem.hxx> 90 #include <com/sun/star/i18n/Boundary.hpp> 91 #include <com/sun/star/i18n/WordType.hpp> 92 #include <com/sun/star/i18n/XBreakIterator.hpp> 93 #include <com/sun/star/embed/XEmbeddedObject.hpp> 94 95 #include <tuple> 96 #include <memory> 97 98 using namespace ::com::sun::star::i18n; 99 100 namespace 101 { 102 // Copy method from SwDoc 103 // Prevent copying into Flys that are anchored in the range 104 bool lcl_ChkFlyFly( SwDoc& rDoc, SwNodeOffset nSttNd, SwNodeOffset nEndNd, 105 SwNodeOffset nInsNd ) 106 { 107 108 for(sw::SpzFrameFormat* pFormat: *rDoc.GetSpzFrameFormats()) 109 { 110 SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor(); 111 SwNode const*const pAnchorNode = pAnchor->GetAnchorNode(); 112 if (pAnchorNode && 113 ((RndStdIds::FLY_AS_CHAR == pAnchor->GetAnchorId()) || 114 (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId()) || 115 (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId()) || 116 (RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId())) && 117 nSttNd <= pAnchorNode->GetIndex() && 118 pAnchorNode->GetIndex() < nEndNd ) 119 { 120 const SwFormatContent& rContent = pFormat->GetContent(); 121 SwStartNode* pSNd; 122 if( !rContent.GetContentIdx() || 123 nullptr == ( pSNd = rContent.GetContentIdx()->GetNode().GetStartNode() )) 124 continue; 125 126 if( pSNd->GetIndex() < nInsNd && 127 nInsNd < pSNd->EndOfSectionIndex() ) 128 // Do not copy ! 129 return true; 130 131 if( lcl_ChkFlyFly( rDoc, pSNd->GetIndex(), 132 pSNd->EndOfSectionIndex(), nInsNd ) ) 133 // Do not copy ! 134 return true; 135 } 136 } 137 138 return false; 139 } 140 141 SwNodeIndex InitDelCount(SwPaM const& rSourcePaM, SwNodeOffset & rDelCount) 142 { 143 SwPosition const& rStart(*rSourcePaM.Start()); 144 // Special handling for SwDoc::AppendDoc 145 if (rSourcePaM.GetDoc().GetNodes().GetEndOfExtras().GetIndex() + 1 146 == rStart.GetNodeIndex()) 147 { 148 rDelCount = SwNodeOffset(1); 149 return SwNodeIndex(rStart.GetNode(), +1); 150 } 151 else 152 { 153 rDelCount = SwNodeOffset(0); 154 return SwNodeIndex(rStart.GetNode()); 155 } 156 } 157 158 /* 159 The CopyBookmarks function has to copy bookmarks from the source to the destination nodes 160 array. It is called after a call of the CopyNodes(..) function. But this function does not copy 161 every node (at least at the moment: 2/08/2006 ), section start and end nodes will not be copied 162 if the corresponding end/start node is outside the copied pam. 163 The lcl_NonCopyCount function counts the number of these nodes, given the copied pam and a node 164 index inside the pam. 165 rPam is the original source pam, rLastIdx is the last calculated position, rDelCount the number 166 of "non-copy" nodes between rPam.Start() and rLastIdx. 167 nNewIdx is the new position of interest. 168 */ 169 void lcl_NonCopyCount( const SwPaM& rPam, SwNodeIndex& rLastIdx, const SwNodeOffset nNewIdx, SwNodeOffset& rDelCount ) 170 { 171 SwNodeOffset nStart = rPam.Start()->GetNodeIndex(); 172 SwNodeOffset nEnd = rPam.End()->GetNodeIndex(); 173 if( rLastIdx.GetIndex() < nNewIdx ) // Moving forward? 174 { 175 // We never copy the StartOfContent node 176 do // count "non-copy" nodes 177 { 178 SwNode& rNode = rLastIdx.GetNode(); 179 if( ( rNode.IsSectionNode() && rNode.EndOfSectionIndex() >= nEnd ) 180 || ( rNode.IsEndNode() && rNode.StartOfSectionNode()->GetIndex() < nStart ) ) 181 { 182 ++rDelCount; 183 } 184 ++rLastIdx; 185 } 186 while( rLastIdx.GetIndex() < nNewIdx ); 187 } 188 else if( rDelCount ) // optimization: if there are no "non-copy" nodes until now, 189 // no move backward needed 190 { 191 while( rLastIdx.GetIndex() > nNewIdx ) 192 { 193 SwNode& rNode = rLastIdx.GetNode(); 194 if( ( rNode.IsSectionNode() && rNode.EndOfSectionIndex() >= nEnd ) 195 || ( rNode.IsEndNode() && rNode.StartOfSectionNode()->GetIndex() < nStart ) ) 196 { 197 --rDelCount; 198 } 199 --rLastIdx; 200 } 201 } 202 } 203 204 void lcl_SetCpyPos( const SwPosition& rOrigPos, 205 const SwPosition& rOrigStt, 206 const SwPosition& rCpyStt, 207 SwPosition& rChgPos, 208 SwNodeOffset nDelCount ) 209 { 210 SwNodeOffset nNdOff = rOrigPos.GetNodeIndex(); 211 nNdOff -= rOrigStt.GetNodeIndex(); 212 nNdOff -= nDelCount; 213 sal_Int32 nContentPos = rOrigPos.GetContentIndex(); 214 215 // Always adjust <nNode> at to be changed <SwPosition> instance <rChgPos> 216 rChgPos.Assign( nNdOff + rCpyStt.GetNodeIndex() ); 217 if (!rChgPos.GetNode().GetContentNode()) 218 return; 219 if( !nNdOff ) 220 { 221 // just adapt the content index 222 if( nContentPos > rOrigStt.GetContentIndex() ) 223 nContentPos -= rOrigStt.GetContentIndex(); 224 else 225 nContentPos = 0; 226 nContentPos += rCpyStt.GetContentIndex(); 227 } 228 rChgPos.SetContent( nContentPos ); 229 } 230 231 } 232 233 namespace sw 234 { 235 // TODO: use SaveBookmark (from DelBookmarks) 236 void CopyBookmarks(const SwPaM& rPam, const SwPosition& rCpyPam, SwCopyFlags flags) 237 { 238 const SwDoc& rSrcDoc = rPam.GetDoc(); 239 SwDoc& rDestDoc = rCpyPam.GetDoc(); 240 const IDocumentMarkAccess* const pSrcMarkAccess = rSrcDoc.getIDocumentMarkAccess(); 241 ::sw::UndoGuard const undoGuard(rDestDoc.GetIDocumentUndoRedo()); 242 243 const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End(); 244 SwPosition const*const pCpyStt = &rCpyPam; 245 246 std::vector< const ::sw::mark::IMark* > vMarksToCopy; 247 for ( IDocumentMarkAccess::const_iterator_t ppMark = pSrcMarkAccess->getAllMarksBegin(); 248 ppMark != pSrcMarkAccess->getAllMarksEnd(); 249 ++ppMark ) 250 { 251 const ::sw::mark::IMark* const pMark = *ppMark; 252 253 const SwPosition& rMarkStart = pMark->GetMarkStart(); 254 const SwPosition& rMarkEnd = pMark->GetMarkEnd(); 255 // only include marks that are in the range and not touching both start and end 256 // - not for annotation or checkbox marks. 257 bool const isIncludeStart( 258 (rStt.GetContentIndex() == 0 // paragraph start selected? 259 // also: only if inserting at the start - cross reference 260 // marks require index to be 0, and there could be one 261 // on the target node already 262 && rCpyPam.GetContentIndex() == 0) 263 || rMarkStart != rStt); 264 bool const isIncludeEnd( 265 (rEnd.GetNode().IsTextNode() // paragraph end selected? 266 && rEnd.GetContentIndex() == rEnd.GetNode().GetTextNode()->Len()) 267 || rMarkEnd != rEnd); 268 const bool bIsNotOnBoundary = 269 pMark->IsExpanded() 270 ? (isIncludeStart || isIncludeEnd) // rMarkStart != rMarkEnd 271 : (isIncludeStart && isIncludeEnd); // rMarkStart == rMarkEnd 272 const IDocumentMarkAccess::MarkType aMarkType = IDocumentMarkAccess::GetType(*pMark); 273 if ( rMarkStart >= rStt && rMarkEnd <= rEnd 274 && ( bIsNotOnBoundary 275 || aMarkType == IDocumentMarkAccess::MarkType::ANNOTATIONMARK 276 || aMarkType == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK 277 || aMarkType == IDocumentMarkAccess::MarkType::CHECKBOX_FIELDMARK 278 || aMarkType == IDocumentMarkAccess::MarkType::DROPDOWN_FIELDMARK 279 || aMarkType == IDocumentMarkAccess::MarkType::DATE_FIELDMARK)) 280 { 281 vMarksToCopy.push_back(pMark); 282 } 283 } 284 // We have to count the "non-copied" nodes... 285 SwNodeOffset nDelCount; 286 SwNodeIndex aCorrIdx(InitDelCount(rPam, nDelCount)); 287 for(const sw::mark::IMark* const pMark : vMarksToCopy) 288 { 289 SwPaM aTmpPam(*pCpyStt); 290 lcl_NonCopyCount(rPam, aCorrIdx, pMark->GetMarkPos().GetNodeIndex(), nDelCount); 291 lcl_SetCpyPos( pMark->GetMarkPos(), rStt, *pCpyStt, *aTmpPam.GetPoint(), nDelCount); 292 if(pMark->IsExpanded()) 293 { 294 aTmpPam.SetMark(); 295 lcl_NonCopyCount(rPam, aCorrIdx, pMark->GetOtherMarkPos().GetNodeIndex(), nDelCount); 296 lcl_SetCpyPos(pMark->GetOtherMarkPos(), rStt, *pCpyStt, *aTmpPam.GetMark(), nDelCount); 297 } 298 299 OUString sRequestedName = pMark->GetName(); 300 if (flags & SwCopyFlags::IsMoveToFly) 301 { 302 assert(&rSrcDoc == &rDestDoc); 303 // Ensure the name can be given to NewMark, since this is ultimately a move 304 auto pSoonToBeDeletedMark = const_cast<sw::mark::IMark*>(pMark); 305 rDestDoc.getIDocumentMarkAccess()->renameMark(pSoonToBeDeletedMark, 306 sRequestedName + "COPY_IS_MOVE"); 307 } 308 309 ::sw::mark::IMark* const pNewMark = rDestDoc.getIDocumentMarkAccess()->makeMark( 310 aTmpPam, 311 sRequestedName, 312 IDocumentMarkAccess::GetType(*pMark), 313 ::sw::mark::InsertMode::CopyText); 314 // Explicitly try to get exactly the same name as in the source 315 // because NavigatorReminders, DdeBookmarks etc. ignore the proposed name 316 if (pNewMark == nullptr) 317 { 318 assert(IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK 319 || IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK); 320 continue; // can't insert duplicate cross reference mark 321 } 322 rDestDoc.getIDocumentMarkAccess()->renameMark(pNewMark, sRequestedName); 323 324 // copying additional attributes for bookmarks or fieldmarks 325 ::sw::mark::IBookmark* const pNewBookmark = 326 dynamic_cast< ::sw::mark::IBookmark* const >(pNewMark); 327 const ::sw::mark::IBookmark* const pOldBookmark = 328 dynamic_cast< const ::sw::mark::IBookmark* >(pMark); 329 if (pNewBookmark && pOldBookmark) 330 { 331 pNewBookmark->SetKeyCode(pOldBookmark->GetKeyCode()); 332 pNewBookmark->SetShortName(pOldBookmark->GetShortName()); 333 pNewBookmark->Hide(pOldBookmark->IsHidden()); 334 pNewBookmark->SetHideCondition(pOldBookmark->GetHideCondition()); 335 } 336 ::sw::mark::IFieldmark* const pNewFieldmark = 337 dynamic_cast< ::sw::mark::IFieldmark* const >(pNewMark); 338 const ::sw::mark::IFieldmark* const pOldFieldmark = 339 dynamic_cast< const ::sw::mark::IFieldmark* >(pMark); 340 if (pNewFieldmark && pOldFieldmark) 341 { 342 pNewFieldmark->SetFieldname(pOldFieldmark->GetFieldname()); 343 pNewFieldmark->SetFieldHelptext(pOldFieldmark->GetFieldHelptext()); 344 ::sw::mark::IFieldmark::parameter_map_t* pNewParams = pNewFieldmark->GetParameters(); 345 const ::sw::mark::IFieldmark::parameter_map_t* pOldParams = pOldFieldmark->GetParameters(); 346 for (const auto& rEntry : *pOldParams ) 347 { 348 pNewParams->insert( rEntry ); 349 } 350 } 351 352 ::sfx2::Metadatable const*const pMetadatable( 353 dynamic_cast< ::sfx2::Metadatable const* >(pMark)); 354 ::sfx2::Metadatable *const pNewMetadatable( 355 dynamic_cast< ::sfx2::Metadatable * >(pNewMark)); 356 if (pMetadatable && pNewMetadatable) 357 { 358 pNewMetadatable->RegisterAsCopyOf(*pMetadatable); 359 } 360 } 361 } 362 } // namespace sw 363 364 namespace 365 { 366 void lcl_DeleteRedlines( const SwPaM& rPam, SwPaM& rCpyPam ) 367 { 368 const SwDoc& rSrcDoc = rPam.GetDoc(); 369 const SwRedlineTable& rTable = rSrcDoc.getIDocumentRedlineAccess().GetRedlineTable(); 370 if( rTable.empty() ) 371 return; 372 373 SwDoc& rDestDoc = rCpyPam.GetDoc(); 374 SwPosition* pCpyStt = rCpyPam.Start(), *pCpyEnd = rCpyPam.End(); 375 std::unique_ptr<SwPaM> pDelPam; 376 auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition* 377 // We have to count the "non-copied" nodes 378 SwNodeOffset nDelCount; 379 SwNodeIndex aCorrIdx(InitDelCount(rPam, nDelCount)); 380 381 SwRedlineTable::size_type n = 0; 382 rSrcDoc.getIDocumentRedlineAccess().GetRedline( *pStt, &n ); 383 for( ; n < rTable.size(); ++n ) 384 { 385 const SwRangeRedline* pRedl = rTable[ n ]; 386 if( RedlineType::Delete == pRedl->GetType() && pRedl->IsVisible() ) 387 { 388 auto [pRStt, pREnd] = pRedl->StartEnd(); // SwPosition* 389 390 SwComparePosition eCmpPos = ComparePosition( *pStt, *pEnd, *pRStt, *pREnd ); 391 switch( eCmpPos ) 392 { 393 case SwComparePosition::CollideEnd: 394 case SwComparePosition::Before: 395 // Pos1 is before Pos2 396 break; 397 398 case SwComparePosition::CollideStart: 399 case SwComparePosition::Behind: 400 // Pos1 is after Pos2 401 n = rTable.size(); 402 break; 403 404 default: 405 { 406 pDelPam.reset(new SwPaM( *pCpyStt, pDelPam.release() )); 407 if( *pStt < *pRStt ) 408 { 409 lcl_NonCopyCount( rPam, aCorrIdx, pRStt->GetNodeIndex(), nDelCount ); 410 lcl_SetCpyPos( *pRStt, *pStt, *pCpyStt, 411 *pDelPam->GetPoint(), nDelCount ); 412 } 413 pDelPam->SetMark(); 414 415 if( *pEnd < *pREnd ) 416 *pDelPam->GetPoint() = *pCpyEnd; 417 else 418 { 419 lcl_NonCopyCount( rPam, aCorrIdx, pREnd->GetNodeIndex(), nDelCount ); 420 lcl_SetCpyPos( *pREnd, *pStt, *pCpyStt, 421 *pDelPam->GetPoint(), nDelCount ); 422 } 423 424 if (pDelPam->GetNext() != pDelPam.get() 425 && *pDelPam->GetNext()->End() == *pDelPam->Start()) 426 { 427 *pDelPam->GetNext()->End() = *pDelPam->End(); 428 pDelPam.reset(pDelPam->GetNext()); 429 } 430 } 431 } 432 } 433 } 434 435 if( !pDelPam ) 436 return; 437 438 RedlineFlags eOld = rDestDoc.getIDocumentRedlineAccess().GetRedlineFlags(); 439 rDestDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld | RedlineFlags::Ignore ); 440 441 ::sw::UndoGuard const undoGuard(rDestDoc.GetIDocumentUndoRedo()); 442 443 // At this point, pDelPam points to the last of maybe several disjoint selections, organized 444 // in reverse order in document (so every GetNext() returns a PaM closer to document start, 445 // until wrap to pDelPam). Removal of the selections must be from last in document to first, 446 // to avoid situations when another PaM in chain points into the node that will be destroyed 447 // (joined to previous) by removal of the currently processed PaM. 448 do { 449 rDestDoc.getIDocumentContentOperations().DeleteAndJoin(*pDelPam); 450 if( !pDelPam->IsMultiSelection() ) 451 break; 452 pDelPam.reset(pDelPam->GetNext()); 453 } while( true ); 454 455 rDestDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); 456 } 457 458 void lcl_DeleteRedlines( const SwNodeRange& rRg, SwNodeRange const & rCpyRg ) 459 { 460 SwDoc& rSrcDoc = rRg.aStart.GetNode().GetDoc(); 461 if( !rSrcDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ) 462 { 463 SwPaM aRgTmp( rRg.aStart, rRg.aEnd ); 464 SwPaM aCpyTmp( rCpyRg.aStart, rCpyRg.aEnd ); 465 lcl_DeleteRedlines( aRgTmp, aCpyTmp ); 466 } 467 } 468 469 void lcl_ChainFormats( SwFlyFrameFormat *pSrc, SwFlyFrameFormat *pDest ) 470 { 471 SwFormatChain aSrc( pSrc->GetChain() ); 472 if ( !aSrc.GetNext() ) 473 { 474 aSrc.SetNext( pDest ); 475 pSrc->SetFormatAttr( aSrc ); 476 } 477 SwFormatChain aDest( pDest->GetChain() ); 478 if ( !aDest.GetPrev() ) 479 { 480 aDest.SetPrev( pSrc ); 481 pDest->SetFormatAttr( aDest ); 482 } 483 } 484 485 // #i86492# 486 bool lcl_ContainsOnlyParagraphsInList( const SwPaM& rPam ) 487 { 488 bool bRet = false; 489 490 const SwTextNode* pTextNd = rPam.Start()->GetNode().GetTextNode(); 491 const SwTextNode* pEndTextNd = rPam.End()->GetNode().GetTextNode(); 492 if ( pTextNd && pTextNd->IsInList() && 493 pEndTextNd && pEndTextNd->IsInList() ) 494 { 495 bRet = true; 496 SwNodeIndex aIdx(rPam.Start()->GetNode()); 497 498 do 499 { 500 ++aIdx; 501 pTextNd = aIdx.GetNode().GetTextNode(); 502 503 if ( !pTextNd || !pTextNd->IsInList() ) 504 { 505 bRet = false; 506 break; 507 } 508 } while (pTextNd != pEndTextNd); 509 } 510 511 return bRet; 512 } 513 514 bool lcl_MarksWholeNode(const SwPaM & rPam) 515 { 516 bool bResult = false; 517 auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition* 518 519 if (nullptr != pStt && nullptr != pEnd) 520 { 521 const SwTextNode* pSttNd = pStt->GetNode().GetTextNode(); 522 const SwTextNode* pEndNd = pEnd->GetNode().GetTextNode(); 523 524 if (nullptr != pSttNd && nullptr != pEndNd && 525 pStt->GetContentIndex() == 0 && 526 pEnd->GetContentIndex() == pEndNd->Len()) 527 { 528 bResult = true; 529 } 530 } 531 532 return bResult; 533 } 534 } 535 536 //local functions originally from sw/source/core/doc/docedt.cxx 537 namespace sw 538 { 539 void CalcBreaks(std::vector<std::pair<SwNodeOffset, sal_Int32>> & rBreaks, 540 SwPaM const & rPam, bool const isOnlyFieldmarks) 541 { 542 SwNodeOffset const nStartNode(rPam.Start()->GetNodeIndex()); 543 SwNodeOffset const nEndNode(rPam.End()->GetNodeIndex()); 544 SwNodes const& rNodes(rPam.GetPoint()->GetNodes()); 545 IDocumentMarkAccess const& rIDMA(*rPam.GetDoc().getIDocumentMarkAccess()); 546 547 std::stack<std::tuple<sw::mark::IFieldmark const*, bool, SwNodeOffset, sal_Int32>> startedFields; 548 549 for (SwNodeOffset n = nStartNode; n <= nEndNode; ++n) 550 { 551 SwNode *const pNode(rNodes[n]); 552 if (pNode->IsTextNode()) 553 { 554 SwTextNode & rTextNode(*pNode->GetTextNode()); 555 sal_Int32 const nStart(n == nStartNode 556 ? rPam.Start()->GetContentIndex() 557 : 0); 558 sal_Int32 const nEnd(n == nEndNode 559 ? rPam.End()->GetContentIndex() 560 : rTextNode.Len()); 561 for (sal_Int32 i = nStart; i < nEnd; ++i) 562 { 563 const sal_Unicode c(rTextNode.GetText()[i]); 564 switch (c) 565 { 566 // note: CH_TXT_ATR_FORMELEMENT does not need handling 567 // not sure how CH_TXT_ATR_INPUTFIELDSTART/END are currently handled 568 case CH_TXTATR_INWORD: 569 case CH_TXTATR_BREAKWORD: 570 { 571 // META hints only have dummy char at the start, not 572 // at the end, so no need to check in nStartNode 573 if (n == nEndNode && !isOnlyFieldmarks) 574 { 575 SwTextAttr const* pAttr(rTextNode.GetTextAttrForCharAt(i)); 576 if (pAttr && pAttr->End() && (nEnd < *pAttr->End())) 577 { 578 assert(pAttr->HasDummyChar()); 579 rBreaks.emplace_back(n, i); 580 } 581 582 if (!pAttr) 583 { 584 // See if this is an end dummy character for a content control. 585 pAttr = rTextNode.GetTextAttrForEndCharAt(i, RES_TXTATR_CONTENTCONTROL); 586 if (pAttr && (nStart > pAttr->GetStart())) 587 { 588 rBreaks.emplace_back(n, i); 589 } 590 } 591 } 592 break; 593 } 594 case CH_TXT_ATR_FIELDSTART: 595 { 596 auto const pFieldMark(rIDMA.getFieldmarkAt(SwPosition(rTextNode, i))); 597 startedFields.emplace(pFieldMark, false, 0, 0); 598 break; 599 } 600 case CH_TXT_ATR_FIELDSEP: 601 { 602 if (startedFields.empty()) 603 { 604 rBreaks.emplace_back(n, i); 605 } 606 else 607 { // no way to find the field via MarkManager... 608 assert(std::get<0>(startedFields.top())->IsCoveringPosition(SwPosition(rTextNode, i))); 609 std::get<1>(startedFields.top()) = true; 610 std::get<2>(startedFields.top()) = n; 611 std::get<3>(startedFields.top()) = i; 612 } 613 break; 614 } 615 case CH_TXT_ATR_FIELDEND: 616 { 617 if (startedFields.empty()) 618 { 619 rBreaks.emplace_back(n, i); 620 } 621 else 622 { // fieldmarks must not overlap => stack 623 assert(std::get<0>(startedFields.top()) == rIDMA.getFieldmarkAt(SwPosition(rTextNode, i))); 624 startedFields.pop(); 625 } 626 break; 627 } 628 } 629 } 630 } 631 else if (pNode->IsStartNode()) 632 { 633 if (pNode->EndOfSectionIndex() <= nEndNode) 634 { // fieldmark cannot overlap node section 635 n = pNode->EndOfSectionIndex(); 636 } 637 } 638 else 639 { // EndNode can actually happen with sections :( 640 assert(pNode->IsEndNode() || pNode->IsNoTextNode()); 641 } 642 } 643 while (!startedFields.empty()) 644 { 645 if (const sw::mark::IFieldmark* pMark = std::get<0>(startedFields.top())) 646 { 647 SwPosition const& rStart(pMark->GetMarkStart()); 648 std::pair<SwNodeOffset, sal_Int32> const pos( 649 rStart.GetNodeIndex(), rStart.GetContentIndex()); 650 auto it = std::lower_bound(rBreaks.begin(), rBreaks.end(), pos); 651 assert(it == rBreaks.end() || *it != pos); 652 rBreaks.insert(it, pos); 653 } 654 if (std::get<1>(startedFields.top())) 655 { 656 std::pair<SwNodeOffset, sal_Int32> const posSep( 657 std::get<2>(startedFields.top()), 658 std::get<3>(startedFields.top())); 659 auto it = std::lower_bound(rBreaks.begin(), rBreaks.end(), posSep); 660 assert(it == rBreaks.end() || *it != posSep); 661 rBreaks.insert(it, posSep); 662 } 663 startedFields.pop(); 664 } 665 } 666 } 667 668 namespace 669 { 670 671 bool lcl_DoWithBreaks(::sw::DocumentContentOperationsManager & rDocumentContentOperations, 672 SwPaM & rPam, SwDeleteFlags const flags, 673 bool (::sw::DocumentContentOperationsManager::*pFunc)(SwPaM&, SwDeleteFlags)) 674 { 675 std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks; 676 677 sw::CalcBreaks(Breaks, rPam); 678 679 if (Breaks.empty()) 680 { 681 return (rDocumentContentOperations.*pFunc)(rPam, flags); 682 } 683 684 // Deletion must be split into several parts if the text node 685 // contains a text attribute with end and with dummy character 686 // and the selection does not contain the text attribute completely, 687 // but overlaps its start (left), where the dummy character is. 688 689 SwPosition const & rSelectionEnd( *rPam.End() ); 690 691 bool bRet( true ); 692 // iterate from end to start, to avoid invalidating the offsets! 693 auto iter( Breaks.rbegin() ); 694 SwNodeOffset nOffset(0); 695 SwNodes const& rNodes(rPam.GetPoint()->GetNodes()); 696 SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node! 697 SwPosition & rEnd( *aPam.End() ); 698 SwPosition & rStart( *aPam.Start() ); 699 700 while (iter != Breaks.rend()) 701 { 702 rStart.Assign(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1); 703 if (rStart < rEnd) // check if part is empty 704 { 705 bRet &= (rDocumentContentOperations.*pFunc)(aPam, flags); 706 nOffset = iter->first - rStart.GetNodeIndex(); // deleted fly nodes... 707 } 708 rEnd.Assign(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second); 709 ++iter; 710 } 711 712 rStart = *rPam.Start(); // set to original start 713 if (rStart < rEnd) // check if part is empty 714 { 715 bRet &= (rDocumentContentOperations.*pFunc)(aPam, flags); 716 } 717 718 return bRet; 719 } 720 721 bool lcl_StrLenOverflow( const SwPaM& rPam ) 722 { 723 // If we try to merge two paragraphs we have to test if afterwards 724 // the string doesn't exceed the allowed string length 725 if( rPam.GetPoint()->GetNode() != rPam.GetMark()->GetNode() ) 726 { 727 auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition* 728 const SwTextNode* pEndNd = pEnd->GetNode().GetTextNode(); 729 if( (nullptr != pEndNd) && pStt->GetNode().IsTextNode() ) 730 { 731 const sal_uInt64 nSum = pStt->GetContentIndex() + 732 pEndNd->GetText().getLength() - pEnd->GetContentIndex(); 733 return nSum > o3tl::make_unsigned(SAL_MAX_INT32); 734 } 735 } 736 return false; 737 } 738 739 struct SaveRedline 740 { 741 SwRangeRedline* pRedl; 742 SwNodeOffset nStt, nEnd; 743 sal_Int32 nSttCnt; 744 sal_Int32 nEndCnt; 745 746 SaveRedline( SwRangeRedline* pR, const SwNodeIndex& rSttIdx ) 747 : pRedl(pR) 748 , nEnd(0) 749 , nEndCnt(0) 750 { 751 auto [pStt, pEnd] = pR->StartEnd(); // SwPosition* 752 SwNodeOffset nSttIdx = rSttIdx.GetIndex(); 753 nStt = pStt->GetNodeIndex() - nSttIdx; 754 nSttCnt = pStt->GetContentIndex(); 755 if( pR->HasMark() ) 756 { 757 nEnd = pEnd->GetNodeIndex() - nSttIdx; 758 nEndCnt = pEnd->GetContentIndex(); 759 } 760 761 pRedl->GetPoint()->Assign( SwNodeOffset(0) ); 762 pRedl->GetMark()->Assign( SwNodeOffset(0) ); 763 } 764 765 SaveRedline( SwRangeRedline* pR, const SwPosition& rPos ) 766 : pRedl(pR) 767 , nEnd(0) 768 , nEndCnt(0) 769 { 770 auto [pStt, pEnd] = pR->StartEnd(); // SwPosition* 771 SwNodeOffset nSttIdx = rPos.GetNodeIndex(); 772 nStt = pStt->GetNodeIndex() - nSttIdx; 773 nSttCnt = pStt->GetContentIndex(); 774 if( nStt == SwNodeOffset(0) ) 775 nSttCnt = nSttCnt - rPos.GetContentIndex(); 776 if( pR->HasMark() ) 777 { 778 nEnd = pEnd->GetNodeIndex() - nSttIdx; 779 nEndCnt = pEnd->GetContentIndex(); 780 if( nEnd == SwNodeOffset(0) ) 781 nEndCnt = nEndCnt - rPos.GetContentIndex(); 782 } 783 784 pRedl->GetPoint()->Assign( SwNodeOffset(0) ); 785 pRedl->GetMark()->Assign( SwNodeOffset(0) ); 786 } 787 788 void SetPos( SwNodeOffset nInsPos ) 789 { 790 pRedl->GetPoint()->Assign( nInsPos + nStt, nSttCnt ); 791 if( pRedl->HasMark() ) 792 { 793 pRedl->GetMark()->Assign( nInsPos + nEnd, nEndCnt ); 794 } 795 } 796 797 void SetPos( const SwPosition& aPos ) 798 { 799 pRedl->GetPoint()->Assign( aPos.GetNodeIndex() + nStt, 800 nSttCnt + ( nStt == SwNodeOffset(0) ? aPos.GetContentIndex() : 0 ) ); 801 if( pRedl->HasMark() ) 802 { 803 pRedl->GetMark()->Assign( aPos.GetNodeIndex() + nEnd, 804 nEndCnt + ( nEnd == SwNodeOffset(0) ? aPos.GetContentIndex() : 0 ) ); 805 } 806 } 807 }; 808 809 typedef std::vector< SaveRedline > SaveRedlines_t; 810 811 void lcl_SaveRedlines(const SwPaM& aPam, SaveRedlines_t& rArr) 812 { 813 SwDoc& rDoc = aPam.GetPointNode().GetDoc(); 814 815 auto [pStart, pEnd] = aPam.StartEnd(); // SwPosition* 816 817 // get first relevant redline 818 SwRedlineTable::size_type nCurrentRedline; 819 rDoc.getIDocumentRedlineAccess().GetRedline( *pStart, &nCurrentRedline ); 820 if( nCurrentRedline > 0) 821 nCurrentRedline--; 822 823 // redline mode RedlineFlags::Ignore|RedlineFlags::On; save old mode 824 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); 825 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On ); 826 827 // iterate over relevant redlines and decide for each whether it should 828 // be saved, or split + saved 829 SwRedlineTable& rRedlineTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable(); 830 for( ; nCurrentRedline < rRedlineTable.size(); nCurrentRedline++ ) 831 { 832 SwRangeRedline* pCurrent = rRedlineTable[ nCurrentRedline ]; 833 SwComparePosition eCompare = 834 ComparePosition( *pCurrent->Start(), *pCurrent->End(), 835 *pStart, *pEnd); 836 837 // we must save this redline if it overlaps aPam 838 // (we may have to split it, too) 839 if( eCompare == SwComparePosition::OverlapBehind || 840 eCompare == SwComparePosition::OverlapBefore || 841 eCompare == SwComparePosition::Outside || 842 eCompare == SwComparePosition::Inside || 843 eCompare == SwComparePosition::Equal ) 844 { 845 rRedlineTable.Remove( nCurrentRedline-- ); 846 847 // split beginning, if necessary 848 if( eCompare == SwComparePosition::OverlapBefore || 849 eCompare == SwComparePosition::Outside ) 850 { 851 SwRangeRedline* pNewRedline = new SwRangeRedline( *pCurrent ); 852 *pNewRedline->End() = *pStart; 853 *pCurrent->Start() = *pStart; 854 rDoc.getIDocumentRedlineAccess().AppendRedline( pNewRedline, true ); 855 } 856 857 // split end, if necessary 858 if( eCompare == SwComparePosition::OverlapBehind || 859 eCompare == SwComparePosition::Outside ) 860 { 861 SwRangeRedline* pNewRedline = new SwRangeRedline( *pCurrent ); 862 *pNewRedline->Start() = *pEnd; 863 *pCurrent->End() = *pEnd; 864 rDoc.getIDocumentRedlineAccess().AppendRedline( pNewRedline, true ); 865 } 866 867 // save the current redline 868 rArr.emplace_back( pCurrent, *pStart ); 869 } 870 } 871 872 // restore old redline mode 873 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); 874 } 875 876 void lcl_RestoreRedlines(SwDoc& rDoc, const SwPosition& rPos, SaveRedlines_t& rArr) 877 { 878 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); 879 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On ); 880 881 for(SaveRedline & rSvRedLine : rArr) 882 { 883 rSvRedLine.SetPos( rPos ); 884 rDoc.getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true ); 885 } 886 887 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); 888 } 889 890 void lcl_SaveRedlines(const SwNodeRange& rRg, SaveRedlines_t& rArr) 891 { 892 SwDoc& rDoc = rRg.aStart.GetNode().GetDoc(); 893 SwRedlineTable::size_type nRedlPos; 894 SwPosition aSrchPos( rRg.aStart ); 895 aSrchPos.Adjust(SwNodeOffset(-1)); 896 if( rDoc.getIDocumentRedlineAccess().GetRedline( aSrchPos, &nRedlPos ) && nRedlPos ) 897 --nRedlPos; 898 else if( nRedlPos >= rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() ) 899 return ; 900 901 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); 902 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On ); 903 SwRedlineTable& rRedlTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable(); 904 905 do { 906 SwRangeRedline* pTmp = rRedlTable[ nRedlPos ]; 907 908 auto [pRStt, pREnd] = pTmp->StartEnd(); // SwPosition* 909 910 if( pRStt->GetNode() < rRg.aStart.GetNode() ) 911 { 912 if( pREnd->GetNode() > rRg.aStart.GetNode() && pREnd->GetNode() < rRg.aEnd.GetNode() ) 913 { 914 // Create a copy and set the end of the original to the end of the MoveArea. 915 // The copy is moved too. 916 SwRangeRedline* pNewRedl = new SwRangeRedline( *pTmp ); 917 SwPosition* pTmpPos = pNewRedl->Start(); 918 pTmpPos->Assign(rRg.aStart); 919 920 rArr.emplace_back(pNewRedl, rRg.aStart); 921 922 pTmpPos = pTmp->End(); 923 pTmpPos->Assign(rRg.aEnd); 924 } 925 else if( pREnd->GetNode() == rRg.aStart.GetNode() ) 926 { 927 SwPosition* pTmpPos = pTmp->End(); 928 pTmpPos->Assign(rRg.aEnd); 929 } 930 } 931 else if( pRStt->GetNode() < rRg.aEnd.GetNode() ) 932 { 933 rRedlTable.Remove( nRedlPos-- ); 934 if( pREnd->GetNode() < rRg.aEnd.GetNode() || 935 ( pREnd->GetNode() == rRg.aEnd.GetNode() && !pREnd->GetContentIndex()) ) 936 { 937 // move everything 938 rArr.emplace_back( pTmp, rRg.aStart ); 939 } 940 else 941 { 942 // split 943 SwRangeRedline* pNewRedl = new SwRangeRedline( *pTmp ); 944 SwPosition* pTmpPos = pNewRedl->End(); 945 pTmpPos->Assign(rRg.aEnd); 946 947 rArr.emplace_back( pNewRedl, rRg.aStart ); 948 949 pTmpPos = pTmp->Start(); 950 pTmpPos->Assign(rRg.aEnd); 951 rDoc.getIDocumentRedlineAccess().AppendRedline( pTmp, true ); 952 } 953 } 954 else 955 break; 956 957 } while( ++nRedlPos < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size() ); 958 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); 959 } 960 961 void lcl_RestoreRedlines(SwDoc& rDoc, SwNodeOffset const nInsPos, SaveRedlines_t& rArr) 962 { 963 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); 964 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( ( eOld & ~RedlineFlags::Ignore) | RedlineFlags::On ); 965 966 for(SaveRedline & rSvRedLine : rArr) 967 { 968 rSvRedLine.SetPos( nInsPos ); 969 IDocumentRedlineAccess::AppendResult const result( 970 rDoc.getIDocumentRedlineAccess().AppendRedline( rSvRedLine.pRedl, true )); 971 if ( IDocumentRedlineAccess::AppendResult::APPENDED == result && 972 rSvRedLine.pRedl->GetType() == RedlineType::Delete ) 973 { 974 UpdateFramesForAddDeleteRedline(rDoc, *rSvRedLine.pRedl); 975 } 976 } 977 978 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); 979 } 980 981 bool lcl_SaveFootnote( const SwNode& rSttNd, const SwNode& rEndNd, 982 const SwNode& rInsPos, 983 SwFootnoteIdxs& rFootnoteArr, SwFootnoteIdxs& rSaveArr, 984 std::optional<sal_Int32> oSttCnt = std::nullopt, std::optional<sal_Int32> oEndCnt = std::nullopt ) 985 { 986 bool bUpdateFootnote = false; 987 const SwNodes& rNds = rInsPos.GetNodes(); 988 const bool bDelFootnote = rInsPos.GetIndex() < rNds.GetEndOfAutotext().GetIndex() && 989 rSttNd.GetIndex() >= rNds.GetEndOfAutotext().GetIndex(); 990 const bool bSaveFootnote = !bDelFootnote && 991 rInsPos.GetIndex() >= rNds.GetEndOfExtras().GetIndex(); 992 if( !rFootnoteArr.empty() ) 993 { 994 995 size_t nPos = 0; 996 rFootnoteArr.SeekEntry( rSttNd, &nPos ); 997 SwTextFootnote* pSrch; 998 const SwNode* pFootnoteNd; 999 1000 // Delete/save all that come after it 1001 while( nPos < rFootnoteArr.size() && ( pFootnoteNd = 1002 &( pSrch = rFootnoteArr[ nPos ] )->GetTextNode())->GetIndex() 1003 <= rEndNd.GetIndex() ) 1004 { 1005 const sal_Int32 nFootnoteSttIdx = pSrch->GetStart(); 1006 if( ( oEndCnt && oSttCnt ) 1007 ? (( &rSttNd == pFootnoteNd && 1008 *oSttCnt > nFootnoteSttIdx) || 1009 ( &rEndNd == pFootnoteNd && 1010 nFootnoteSttIdx >= *oEndCnt )) 1011 : ( &rEndNd == pFootnoteNd )) 1012 { 1013 ++nPos; // continue searching 1014 } 1015 else 1016 { 1017 // delete it 1018 if( bDelFootnote ) 1019 { 1020 SwTextNode& rTextNd = const_cast<SwTextNode&>(pSrch->GetTextNode()); 1021 SwContentIndex aIdx( &rTextNd, nFootnoteSttIdx ); 1022 rTextNd.EraseText( aIdx, 1 ); 1023 } 1024 else 1025 { 1026 pSrch->DelFrames(nullptr); 1027 rFootnoteArr.erase( rFootnoteArr.begin() + nPos ); 1028 if( bSaveFootnote ) 1029 rSaveArr.insert( pSrch ); 1030 } 1031 bUpdateFootnote = true; 1032 } 1033 } 1034 1035 while( nPos-- && ( pFootnoteNd = &( pSrch = rFootnoteArr[ nPos ] )-> 1036 GetTextNode())->GetIndex() >= rSttNd.GetIndex() ) 1037 { 1038 const sal_Int32 nFootnoteSttIdx = pSrch->GetStart(); 1039 if( !oEndCnt || !oSttCnt || 1040 ! (( &rSttNd == pFootnoteNd && 1041 *oSttCnt > nFootnoteSttIdx ) || 1042 ( &rEndNd == pFootnoteNd && 1043 nFootnoteSttIdx >= *oEndCnt )) ) 1044 { 1045 if( bDelFootnote ) 1046 { 1047 // delete it 1048 SwTextNode& rTextNd = const_cast<SwTextNode&>(pSrch->GetTextNode()); 1049 SwContentIndex aIdx( &rTextNd, nFootnoteSttIdx ); 1050 rTextNd.EraseText( aIdx, 1 ); 1051 } 1052 else 1053 { 1054 pSrch->DelFrames(nullptr); 1055 rFootnoteArr.erase( rFootnoteArr.begin() + nPos ); 1056 if( bSaveFootnote ) 1057 rSaveArr.insert( pSrch ); 1058 } 1059 bUpdateFootnote = true; 1060 } 1061 } 1062 } 1063 // When moving from redline section into document content section, e.g. 1064 // after loading a document with (delete-)redlines, the footnote array 1065 // has to be adjusted... (#i70572) 1066 if( bSaveFootnote ) 1067 { 1068 SwNodeIndex aIdx( rSttNd ); 1069 while( aIdx < rEndNd ) // Check the moved section 1070 { 1071 SwNode* pNode = &aIdx.GetNode(); 1072 if( pNode->IsTextNode() ) // Looking for text nodes... 1073 { 1074 SwpHints *pHints = pNode->GetTextNode()->GetpSwpHints(); 1075 if( pHints && pHints->HasFootnote() ) //...with footnotes 1076 { 1077 bUpdateFootnote = true; // Heureka 1078 const size_t nCount = pHints->Count(); 1079 for( size_t i = 0; i < nCount; ++i ) 1080 { 1081 SwTextAttr *pAttr = pHints->Get( i ); 1082 if ( pAttr->Which() == RES_TXTATR_FTN ) 1083 { 1084 rSaveArr.insert( static_cast<SwTextFootnote*>(pAttr) ); 1085 } 1086 } 1087 } 1088 } 1089 ++aIdx; 1090 } 1091 } 1092 return bUpdateFootnote; 1093 } 1094 1095 bool lcl_MayOverwrite( const SwTextNode *pNode, const sal_Int32 nPos ) 1096 { 1097 sal_Unicode const cChr = pNode->GetText()[nPos]; 1098 switch (cChr) 1099 { 1100 case CH_TXTATR_BREAKWORD: 1101 case CH_TXTATR_INWORD: 1102 return !pNode->GetTextAttrForCharAt(nPos);// how could there be none? 1103 case CH_TXT_ATR_INPUTFIELDSTART: 1104 case CH_TXT_ATR_INPUTFIELDEND: 1105 case CH_TXT_ATR_FIELDSTART: 1106 case CH_TXT_ATR_FIELDSEP: 1107 case CH_TXT_ATR_FIELDEND: 1108 case CH_TXT_ATR_FORMELEMENT: 1109 return false; 1110 default: 1111 return true; 1112 } 1113 } 1114 1115 void lcl_SkipAttr( const SwTextNode *pNode, SwPosition &rIdx, sal_Int32 &rStart ) 1116 { 1117 if( !lcl_MayOverwrite( pNode, rStart ) ) 1118 { 1119 // skip all special attributes 1120 do { 1121 rIdx.AdjustContent(+1); 1122 rStart = rIdx.GetContentIndex(); 1123 } while (rStart < pNode->GetText().getLength() 1124 && !lcl_MayOverwrite(pNode, rStart) ); 1125 } 1126 } 1127 1128 bool lcl_GetTokenToParaBreak( OUString& rStr, OUString& rRet, bool bRegExpRplc ) 1129 { 1130 if( bRegExpRplc ) 1131 { 1132 sal_Int32 nPos = 0; 1133 static constexpr OUString sPara(u"\\n"_ustr); 1134 for (;;) 1135 { 1136 nPos = rStr.indexOf( sPara, nPos ); 1137 if (nPos<0) 1138 { 1139 break; 1140 } 1141 // Has this been escaped? 1142 if( nPos && '\\' == rStr[nPos-1]) 1143 { 1144 ++nPos; 1145 if( nPos >= rStr.getLength() ) 1146 { 1147 break; 1148 } 1149 } 1150 else 1151 { 1152 rRet = rStr.copy( 0, nPos ); 1153 rStr = rStr.copy( nPos + sPara.getLength() ); 1154 return true; 1155 } 1156 } 1157 } 1158 rRet = rStr; 1159 rStr.clear(); 1160 return false; 1161 } 1162 } 1163 1164 namespace //local functions originally from docfmt.cxx 1165 { 1166 1167 bool lcl_ApplyOtherSet( 1168 SwContentNode & rNode, 1169 SwHistory *const pHistory, 1170 SfxItemSet const& rOtherSet, 1171 SfxItemSet const& rFirstSet, 1172 SfxItemSet const& rPropsSet, 1173 SwRootFrame const*const pLayout, 1174 SwNodeIndex *const o_pIndex = nullptr) 1175 { 1176 assert(rOtherSet.Count()); 1177 1178 bool ret(false); 1179 SwTextNode *const pTNd = rNode.GetTextNode(); 1180 sw::MergedPara const* pMerged(nullptr); 1181 if (pLayout && pLayout->HasMergedParas() && pTNd) 1182 { 1183 SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>( 1184 pTNd->getLayoutFrame(pLayout))); 1185 if (pTextFrame) 1186 { 1187 pMerged = pTextFrame->GetMergedPara(); 1188 } 1189 if (pMerged) 1190 { 1191 if (rFirstSet.Count()) 1192 { 1193 if (pHistory) 1194 { 1195 SwRegHistory aRegH(pMerged->pFirstNode, *pMerged->pFirstNode, pHistory); 1196 ret = pMerged->pFirstNode->SetAttr(rFirstSet); 1197 } 1198 else 1199 { 1200 ret = pMerged->pFirstNode->SetAttr(rFirstSet); 1201 } 1202 } 1203 if (rPropsSet.Count()) 1204 { 1205 if (pHistory) 1206 { 1207 SwRegHistory aRegH(pMerged->pParaPropsNode, *pMerged->pParaPropsNode, pHistory); 1208 ret = pMerged->pParaPropsNode->SetAttr(rPropsSet) || ret; 1209 } 1210 else 1211 { 1212 ret = pMerged->pParaPropsNode->SetAttr(rPropsSet) || ret; 1213 } 1214 } 1215 if (o_pIndex) 1216 { 1217 *o_pIndex = *pMerged->pLastNode; // skip hidden 1218 } 1219 } 1220 } 1221 1222 // input cursor can't be on hidden node, and iteration skips them 1223 assert(!pLayout || !pLayout->HasMergedParas() 1224 || rNode.GetRedlineMergeFlag() != SwNode::Merge::Hidden); 1225 1226 if (!pMerged) 1227 { 1228 if (pHistory) 1229 { 1230 SwRegHistory aRegH(&rNode, rNode, pHistory); 1231 ret = rNode.SetAttr( rOtherSet ); 1232 } 1233 else 1234 { 1235 ret = rNode.SetAttr( rOtherSet ); 1236 } 1237 } 1238 return ret; 1239 } 1240 1241 #define DELETECHARSETS if ( bDelete ) { delete pCharSet; delete pOtherSet; } 1242 1243 // set format redline with extra data for lcl_InsAttr() 1244 void lcl_SetRedline( 1245 SwDoc& rDoc, 1246 const SwPaM &rRg) 1247 { 1248 std::unique_ptr<SwRedlineExtraData_FormatColl> xExtra; 1249 1250 // check existing redline on the same range, and use its extra data, if it exists 1251 SwRedlineTable::size_type nRedlPos = rDoc.getIDocumentRedlineAccess().GetRedlinePos( 1252 rRg.Start()->GetNode(), RedlineType::Format ); 1253 if( SwRedlineTable::npos != nRedlPos ) 1254 { 1255 const SwPosition *pRStt, *pREnd; 1256 do { 1257 SwRangeRedline* pTmp = rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ]; 1258 pRStt = pTmp->Start(); 1259 pREnd = pTmp->End(); 1260 SwComparePosition eCompare = ComparePosition( *rRg.Start(), *rRg.End(), *pRStt, *pREnd ); 1261 if ( eCompare == SwComparePosition::Inside || eCompare == SwComparePosition::Equal ) 1262 { 1263 if (pTmp->GetExtraData()) 1264 { 1265 const SwRedlineExtraData* pExtraData = pTmp->GetExtraData(); 1266 const SwRedlineExtraData_FormatColl* pFormattingChanges = 1267 dynamic_cast<const SwRedlineExtraData_FormatColl*>(pExtraData); 1268 // Check if the extra data is of type 'formatting changes' 1269 if (pFormattingChanges) 1270 { 1271 // Get the item set that holds all the changes properties 1272 const SfxItemSet *pChangesSet = pFormattingChanges->GetItemSet(); 1273 xExtra.reset(new SwRedlineExtraData_FormatColl("", USHRT_MAX, pChangesSet)); 1274 break; 1275 } 1276 } 1277 } 1278 } while( pRStt <= rRg.Start() && ++nRedlPos < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size()); 1279 } 1280 1281 SwRangeRedline * pRedline = new SwRangeRedline( RedlineType::Format, rRg ); 1282 auto const result(rDoc.getIDocumentRedlineAccess().AppendRedline( pRedline, true)); 1283 // store original text attributes to reject formatting change 1284 if (IDocumentRedlineAccess::AppendResult::IGNORED == result) 1285 return; 1286 1287 // no existing format redline in the range 1288 if (!xExtra) 1289 { 1290 // Apply the first character's attributes to the ReplaceText 1291 SfxItemSetFixed<RES_CHRATR_BEGIN, RES_TXTATR_WITHEND_END - 1, 1292 RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1> aSet( rDoc.GetAttrPool() ); 1293 SwTextNode * pNode = rRg.Start()->GetNode().GetTextNode(); 1294 pNode->GetParaAttr( aSet, rRg.Start()->GetContentIndex() + 1, rRg.End()->GetContentIndex() ); 1295 1296 aSet.ClearItem( RES_TXTATR_REFMARK ); 1297 aSet.ClearItem( RES_TXTATR_TOXMARK ); 1298 aSet.ClearItem( RES_TXTATR_CJK_RUBY ); 1299 aSet.ClearItem( RES_TXTATR_INETFMT ); 1300 aSet.ClearItem( RES_TXTATR_META ); 1301 aSet.ClearItem( RES_TXTATR_METAFIELD ); 1302 1303 // After GetParaAttr aSet can contain invalid/dontcare items (true == IsInvalidItem, 1304 // DONTCARE == SfxItemState), e.g. RES_TXTATR_CHARFMT and (a copy of) this 1305 // SfxItemSet can be passed to MSWordExportBase::OutputItemSet 1306 // which doesn't handle invalid/dontcare items so clear them here 1307 aSet.ClearInvalidItems(); 1308 1309 xExtra.reset(new SwRedlineExtraData_FormatColl("", USHRT_MAX, &aSet)); 1310 } 1311 1312 if (xExtra) 1313 { 1314 pRedline->SetExtraData(xExtra.get() ); 1315 } 1316 } 1317 1318 // create format redline(s) for the given range: 1319 // to track the original formatting stored in the 1320 // hints, create redlines for all parts of the 1321 // range partitioned by boundaries of the hints. 1322 void lcl_SetRedlines( 1323 SwDoc& rDoc, 1324 const SwPaM &rRg) 1325 { 1326 SwNodeIndex aIdx( rRg.Start()->GetNode() ); 1327 const SwNodeIndex aEndNd( rRg.End()->GetNode() ); 1328 while( aIdx <= aEndNd ) 1329 { 1330 SwTextNode *pNode = aIdx.GetNode().GetTextNode(); 1331 if( pNode ) 1332 { 1333 const sal_Int32 nStart = aIdx == rRg.Start()->GetNode() 1334 ? rRg.Start()->GetContentIndex() 1335 : 0; 1336 const sal_Int32 nEnd = aIdx < aEndNd 1337 ? pNode->GetText().getLength() 1338 : rRg.End()->GetContentIndex(); 1339 1340 if( SwpHints *pHints = pNode->GetpSwpHints() ) 1341 { 1342 const size_t nCount = pHints->Count(); 1343 sal_Int32 nRedEnd = nStart; 1344 for( size_t i = 0; i < nCount; ++i ) 1345 { 1346 SwTextAttr *pAttr = pHints->Get( i ); 1347 1348 if ( pAttr->GetStart() > nEnd ) 1349 { 1350 break; // after the range 1351 } 1352 1353 if ( !pAttr->GetEnd() || *pAttr->GetEnd() < nStart ) 1354 { 1355 continue; // before the range 1356 } 1357 1358 // range part before the hint 1359 if ( nRedEnd < pAttr->GetStart() ) 1360 { 1361 SwPaM aPam( *pNode, nRedEnd, *pNode, pAttr->GetStart() ); 1362 lcl_SetRedline(rDoc, aPam); 1363 } 1364 1365 // range part at the hint 1366 sal_Int32 nRedStart = std::max(pAttr->GetStart(), nStart); 1367 nRedEnd = std::min(*pAttr->GetEnd(), nEnd); 1368 SwPaM aPam2( *pNode, nRedStart, *pNode, nRedEnd ); 1369 lcl_SetRedline(rDoc, aPam2); 1370 } 1371 1372 // range part after the last hint 1373 if ( nRedEnd < nEnd ) 1374 { 1375 SwPaM aPam( *pNode, nRedEnd, *pNode, nEnd ); 1376 lcl_SetRedline(rDoc, aPam); 1377 } 1378 } 1379 else 1380 { 1381 SwPaM aPam( *pNode, nStart, *pNode, nEnd ); 1382 lcl_SetRedline(rDoc, aPam); 1383 } 1384 } 1385 ++aIdx; 1386 } 1387 } 1388 1389 /// Insert Hints according to content types; 1390 // Is used in SwDoc::Insert(..., SwFormatHint &rHt) 1391 1392 bool lcl_InsAttr( 1393 SwDoc& rDoc, 1394 const SwPaM &rRg, 1395 const SfxItemSet& rChgSet, 1396 const SetAttrMode nFlags, 1397 SwUndoAttr *const pUndo, 1398 SwRootFrame const*const pLayout, 1399 SwTextAttr **ppNewTextAttr) 1400 { 1401 // Divide the Sets (for selections in Nodes) 1402 const SfxItemSet* pCharSet = nullptr; 1403 const SfxItemSet* pOtherSet = nullptr; 1404 bool bDelete = false; 1405 bool bCharAttr = false; 1406 bool bOtherAttr = false; 1407 1408 // Check, if we can work with rChgSet or if we have to create additional SfxItemSets 1409 if ( 1 == rChgSet.Count() ) 1410 { 1411 SfxItemIter aIter( rChgSet ); 1412 const SfxPoolItem* pItem = aIter.GetCurItem(); 1413 if (pItem && !IsInvalidItem(pItem)) 1414 { 1415 const sal_uInt16 nWhich = pItem->Which(); 1416 1417 if ( isCHRATR(nWhich) || 1418 (RES_TXTATR_CHARFMT == nWhich) || 1419 (RES_TXTATR_INETFMT == nWhich) || 1420 (RES_TXTATR_AUTOFMT == nWhich) || 1421 (RES_TXTATR_UNKNOWN_CONTAINER == nWhich) ) 1422 { 1423 pCharSet = &rChgSet; 1424 bCharAttr = true; 1425 } 1426 1427 if ( isPARATR(nWhich) 1428 || isPARATR_LIST(nWhich) 1429 || isFRMATR(nWhich) 1430 || isGRFATR(nWhich) 1431 || isUNKNOWNATR(nWhich) 1432 || isDrawingLayerAttribute(nWhich) ) 1433 { 1434 pOtherSet = &rChgSet; 1435 bOtherAttr = true; 1436 } 1437 } 1438 } 1439 1440 // Build new itemset if either 1441 // - rChgSet.Count() > 1 or 1442 // - The attribute in rChgSet does not belong to one of the above categories 1443 if ( !bCharAttr && !bOtherAttr ) 1444 { 1445 SfxItemSet* pTmpCharItemSet = new SfxItemSetFixed< 1446 RES_CHRATR_BEGIN, RES_CHRATR_END - 1, 1447 RES_TXTATR_AUTOFMT, RES_TXTATR_CHARFMT, 1448 RES_TXTATR_UNKNOWN_CONTAINER, 1449 RES_TXTATR_UNKNOWN_CONTAINER>( rDoc.GetAttrPool() ); 1450 1451 SfxItemSet* pTmpOtherItemSet = new SfxItemSetFixed< 1452 RES_PARATR_BEGIN, RES_GRFATR_END - 1, 1453 RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END - 1, 1454 // FillAttribute support: 1455 XATTR_FILL_FIRST, XATTR_FILL_LAST>( rDoc.GetAttrPool() ); 1456 1457 pTmpCharItemSet->Put( rChgSet ); 1458 pTmpOtherItemSet->Put( rChgSet ); 1459 1460 pCharSet = pTmpCharItemSet; 1461 pOtherSet = pTmpOtherItemSet; 1462 1463 bDelete = true; 1464 } 1465 1466 SwHistory* pHistory = pUndo ? &pUndo->GetHistory() : nullptr; 1467 bool bRet = false; 1468 const SwPosition *pStt = rRg.Start(), *pEnd = rRg.End(); 1469 SwContentNode* pNode = pStt->GetNode().GetContentNode(); 1470 1471 if( pNode && pNode->IsTextNode() ) 1472 { 1473 // tdf#127606 at editing, remove different formatting of DOCX-like numbering symbol 1474 if (pLayout && pNode->GetTextNode()->getIDocumentSettingAccess()-> 1475 get(DocumentSettingId::APPLY_PARAGRAPH_MARK_FORMAT_TO_NUMBERING )) 1476 { 1477 SwContentNode* pEndNode = pEnd->GetNode().GetContentNode(); 1478 SwContentNode* pCurrentNode = pEndNode; 1479 auto nStartIndex = pNode->GetIndex(); 1480 auto nEndIndex = pEndNode->GetIndex(); 1481 SwNodeIndex aIdx( pEnd->GetNode() ); 1482 while ( pCurrentNode != nullptr && nStartIndex <= pCurrentNode->GetIndex() ) 1483 { 1484 if (pCurrentNode->GetSwAttrSet().HasItem(RES_PARATR_LIST_AUTOFMT) && 1485 // remove character formatting only on wholly selected paragraphs 1486 (nStartIndex < pCurrentNode->GetIndex() || pStt->GetContentIndex() == 0) && 1487 (pCurrentNode->GetIndex() < nEndIndex || pEnd->GetContentIndex() == pEndNode->Len())) 1488 { 1489 pCurrentNode->ResetAttr(RES_PARATR_LIST_AUTOFMT); 1490 // reset also paragraph marker 1491 pCurrentNode->GetTextNode()->RstTextAttr(pCurrentNode->Len(), 1); 1492 } 1493 pCurrentNode = SwNodes::GoPrevious( &aIdx ); 1494 } 1495 } 1496 // #i27615# 1497 if (rRg.IsInFrontOfLabel()) 1498 { 1499 SwTextNode * pTextNd = pNode->GetTextNode(); 1500 if (pLayout) 1501 { 1502 pTextNd = sw::GetParaPropsNode(*pLayout, *pTextNd); 1503 } 1504 SwNumRule * pNumRule = pTextNd->GetNumRule(); 1505 1506 if ( !pNumRule ) 1507 { 1508 OSL_FAIL( "<InsAttr(..)> - PaM in front of label, but text node has no numbering rule set. This is a serious defect." ); 1509 DELETECHARSETS 1510 return false; 1511 } 1512 1513 int nLevel = pTextNd->GetActualListLevel(); 1514 1515 if (nLevel < 0) 1516 nLevel = 0; 1517 1518 if (nLevel >= MAXLEVEL) 1519 nLevel = MAXLEVEL - 1; 1520 1521 SwNumFormat aNumFormat = pNumRule->Get(o3tl::narrowing<sal_uInt16>(nLevel)); 1522 SwCharFormat * pCharFormat = 1523 rDoc.FindCharFormatByName(aNumFormat.GetCharFormatName()); 1524 1525 if (pCharFormat) 1526 { 1527 if (pHistory) 1528 pHistory->AddCharFormat(pCharFormat->GetAttrSet(), *pCharFormat); 1529 1530 if ( pCharSet ) 1531 pCharFormat->SetFormatAttr(*pCharSet); 1532 } 1533 1534 DELETECHARSETS 1535 return true; 1536 } 1537 1538 // Attributes without an end do not have a range 1539 if ( !bCharAttr && !bOtherAttr ) 1540 { 1541 SfxItemSetFixed<RES_TXTATR_NOEND_BEGIN, RES_TXTATR_NOEND_END-1> 1542 aTextSet( rDoc.GetAttrPool() ); 1543 aTextSet.Put( rChgSet ); 1544 if( aTextSet.Count() ) 1545 { 1546 SwRegHistory history( pNode, *pNode, pHistory ); 1547 bRet = history.InsertItems( 1548 aTextSet, pStt->GetContentIndex(), pStt->GetContentIndex(), nFlags, /*ppNewTextAttr*/nullptr ) || bRet; 1549 1550 if (bRet && (rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() 1551 && !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()))) 1552 { 1553 SwPaM aPam( pStt->GetNode(), pStt->GetContentIndex()-1, 1554 pStt->GetNode(), pStt->GetContentIndex() ); 1555 1556 if( pUndo ) 1557 pUndo->SaveRedlineData( aPam, true ); 1558 1559 if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() ) 1560 rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true); 1561 else 1562 rDoc.getIDocumentRedlineAccess().SplitRedline( aPam ); 1563 } 1564 } 1565 } 1566 1567 // TextAttributes with an end never expand their range 1568 if ( !bCharAttr && !bOtherAttr ) 1569 { 1570 // CharFormat and URL attributes are treated separately! 1571 // TEST_TEMP ToDo: AutoFormat! 1572 SfxItemSetFixed< 1573 RES_TXTATR_REFMARK, RES_TXTATR_METAFIELD, 1574 RES_TXTATR_CJK_RUBY, RES_TXTATR_CJK_RUBY, 1575 RES_TXTATR_INPUTFIELD, RES_TXTATR_CONTENTCONTROL> 1576 aTextSet(rDoc.GetAttrPool()); 1577 1578 aTextSet.Put( rChgSet ); 1579 if( aTextSet.Count() ) 1580 { 1581 const sal_Int32 nInsCnt = pStt->GetContentIndex(); 1582 const sal_Int32 nEnd = pStt->GetNode() == pEnd->GetNode() 1583 ? pEnd->GetContentIndex() 1584 : pNode->Len(); 1585 SwRegHistory history( pNode, *pNode, pHistory ); 1586 bRet = history.InsertItems( aTextSet, nInsCnt, nEnd, nFlags, ppNewTextAttr ) 1587 || bRet; 1588 1589 if (bRet && (rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() 1590 && !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()))) 1591 { 1592 // Was text content inserted? (RefMark/TOXMarks without an end) 1593 bool bTextIns = nInsCnt != pStt->GetContentIndex(); 1594 // Was content inserted or set over the selection? 1595 SwPaM aPam( pStt->GetNode(), bTextIns ? nInsCnt + 1 : nEnd, 1596 pStt->GetNode(), nInsCnt ); 1597 if( pUndo ) 1598 pUndo->SaveRedlineData( aPam, bTextIns ); 1599 1600 if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() ) 1601 rDoc.getIDocumentRedlineAccess().AppendRedline( 1602 new SwRangeRedline( 1603 bTextIns ? RedlineType::Insert : RedlineType::Format, aPam ), 1604 true); 1605 else if( bTextIns ) 1606 rDoc.getIDocumentRedlineAccess().SplitRedline( aPam ); 1607 } 1608 } 1609 } 1610 } 1611 1612 // We always have to set the auto flag for PageDescs that are set at the Node! 1613 if( pOtherSet && pOtherSet->Count() ) 1614 { 1615 SwTableNode* pTableNd; 1616 const SwFormatPageDesc* pDesc = pOtherSet->GetItemIfSet( RES_PAGEDESC, false ); 1617 if( pDesc ) 1618 { 1619 if( pNode ) 1620 { 1621 // Set auto flag. Only in the template it's without auto! 1622 SwFormatPageDesc aNew( *pDesc ); 1623 1624 // Tables now also know line breaks 1625 if( !(nFlags & SetAttrMode::APICALL) && 1626 nullptr != ( pTableNd = pNode->FindTableNode() ) ) 1627 { 1628 SwTableNode* pCurTableNd = pTableNd; 1629 while ( nullptr != ( pCurTableNd = pCurTableNd->StartOfSectionNode()->FindTableNode() ) ) 1630 pTableNd = pCurTableNd; 1631 1632 // set the table format 1633 SwFrameFormat* pFormat = pTableNd->GetTable().GetFrameFormat(); 1634 SwRegHistory aRegH( pFormat, *pTableNd, pHistory ); 1635 pFormat->SetFormatAttr( aNew ); 1636 bRet = true; 1637 } 1638 else 1639 { 1640 SwContentNode * pFirstNode(pNode); 1641 if (pLayout && pLayout->HasMergedParas()) 1642 { 1643 pFirstNode = sw::GetFirstAndLastNode(*pLayout, pStt->GetNode()).first; 1644 } 1645 SwRegHistory aRegH( pFirstNode, *pFirstNode, pHistory ); 1646 bRet = pFirstNode->SetAttr( aNew ) || bRet; 1647 } 1648 } 1649 1650 // bOtherAttr = true means that pOtherSet == rChgSet. In this case 1651 // we know, that there is only one attribute in pOtherSet. We cannot 1652 // perform the following operations, instead we return: 1653 if ( bOtherAttr ) 1654 return bRet; 1655 1656 const_cast<SfxItemSet*>(pOtherSet)->ClearItem( RES_PAGEDESC ); 1657 if( !pOtherSet->Count() ) 1658 { 1659 DELETECHARSETS 1660 return bRet; 1661 } 1662 } 1663 1664 // Tables now also know line breaks 1665 const SvxFormatBreakItem* pBreak; 1666 if( pNode && !(nFlags & SetAttrMode::APICALL) && 1667 nullptr != (pTableNd = pNode->FindTableNode() ) && 1668 (pBreak = pOtherSet->GetItemIfSet( RES_BREAK, false )) ) 1669 { 1670 SwTableNode* pCurTableNd = pTableNd; 1671 while ( nullptr != ( pCurTableNd = pCurTableNd->StartOfSectionNode()->FindTableNode() ) ) 1672 pTableNd = pCurTableNd; 1673 1674 // set the table format 1675 SwFrameFormat* pFormat = pTableNd->GetTable().GetFrameFormat(); 1676 SwRegHistory aRegH( pFormat, *pTableNd, pHistory ); 1677 pFormat->SetFormatAttr( *pBreak ); 1678 bRet = true; 1679 1680 // bOtherAttr = true means that pOtherSet == rChgSet. In this case 1681 // we know, that there is only one attribute in pOtherSet. We cannot 1682 // perform the following operations, instead we return: 1683 if ( bOtherAttr ) 1684 return bRet; 1685 1686 const_cast<SfxItemSet*>(pOtherSet)->ClearItem( RES_BREAK ); 1687 if( !pOtherSet->Count() ) 1688 { 1689 DELETECHARSETS 1690 return bRet; 1691 } 1692 } 1693 1694 { 1695 // If we have a PoolNumRule, create it if needed 1696 sal_uInt16 nPoolId=0; 1697 const SwNumRuleItem* pRule = pOtherSet->GetItemIfSet( RES_PARATR_NUMRULE, false ); 1698 if( pRule && 1699 !rDoc.FindNumRulePtr( pRule->GetValue() ) && 1700 USHRT_MAX != (nPoolId = SwStyleNameMapper::GetPoolIdFromUIName ( pRule->GetValue(), 1701 SwGetPoolIdFromName::NumRule )) ) 1702 rDoc.getIDocumentStylePoolAccess().GetNumRuleFromPool( nPoolId ); 1703 } 1704 } 1705 1706 SfxItemSetFixed<RES_PAGEDESC, RES_BREAK> firstSet(rDoc.GetAttrPool()); 1707 if (pOtherSet && pOtherSet->Count()) 1708 { // actually only RES_BREAK is possible here... 1709 firstSet.Put(*pOtherSet); 1710 } 1711 SfxItemSetFixed 1712 <RES_PARATR_BEGIN, RES_PAGEDESC, 1713 RES_BREAK+1, RES_FRMATR_END, 1714 XATTR_FILL_FIRST, XATTR_FILL_LAST+1> propsSet(rDoc.GetAttrPool()); 1715 if (pOtherSet && pOtherSet->Count()) 1716 { 1717 propsSet.Put(*pOtherSet); 1718 } 1719 1720 if( !rRg.HasMark() ) // no range 1721 { 1722 if( !pNode ) 1723 { 1724 DELETECHARSETS 1725 return bRet; 1726 } 1727 1728 if( pNode->IsTextNode() && pCharSet && pCharSet->Count() ) 1729 { 1730 SwTextNode* pTextNd = pNode->GetTextNode(); 1731 sal_Int32 nMkPos, nPtPos = pStt->GetContentIndex(); 1732 const OUString& rStr = pTextNd->GetText(); 1733 1734 // Special case: if the Cursor is located within a URL attribute, we take over it's area 1735 SwTextAttr const*const pURLAttr( 1736 pTextNd->GetTextAttrAt(pStt->GetContentIndex(), RES_TXTATR_INETFMT)); 1737 if (pURLAttr && !pURLAttr->GetINetFormat().GetValue().isEmpty()) 1738 { 1739 nMkPos = pURLAttr->GetStart(); 1740 nPtPos = *pURLAttr->End(); 1741 } 1742 else 1743 { 1744 assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is()); 1745 Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary( 1746 pTextNd->GetText(), nPtPos, 1747 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ), 1748 WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/, 1749 true); 1750 1751 if( aBndry.startPos < nPtPos && nPtPos < aBndry.endPos ) 1752 { 1753 nMkPos = aBndry.startPos; 1754 nPtPos = aBndry.endPos; 1755 } 1756 else 1757 nPtPos = nMkPos = pStt->GetContentIndex(); 1758 } 1759 1760 // Remove the overriding attributes from the SwpHintsArray, 1761 // if the selection spans across the whole paragraph. 1762 // These attributes are inserted as FormatAttributes and 1763 // never override the TextAttributes! 1764 if( !(nFlags & SetAttrMode::DONTREPLACE ) && 1765 pTextNd->HasHints() && !nMkPos && nPtPos == rStr.getLength()) 1766 { 1767 if( pHistory ) 1768 { 1769 // Save all attributes for the Undo. 1770 SwRegHistory aRHst( *pTextNd, pHistory ); 1771 pTextNd->GetpSwpHints()->Register( &aRHst ); 1772 pTextNd->RstTextAttr( 0, nPtPos, 0, pCharSet ); 1773 if( pTextNd->GetpSwpHints() ) 1774 pTextNd->GetpSwpHints()->DeRegister(); 1775 } 1776 else 1777 pTextNd->RstTextAttr( 0, nPtPos, 0, pCharSet ); 1778 } 1779 1780 if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() ) 1781 { 1782 SwPaM aPam( *pNode, nMkPos, *pNode, nPtPos ); 1783 1784 if( pUndo ) 1785 pUndo->SaveRedlineData( aPam, false ); 1786 1787 lcl_SetRedlines(rDoc, aPam); 1788 } 1789 1790 // the SwRegHistory inserts the attribute into the TextNode! 1791 SwRegHistory history( pNode, *pNode, pHistory ); 1792 1793 bRet = history.InsertItems( *pCharSet, nMkPos, nPtPos, nFlags, /*ppNewTextAttr*/nullptr ) 1794 || bRet; 1795 1796 } 1797 if( pOtherSet && pOtherSet->Count() ) 1798 { 1799 // Need to check for unique item for DrawingLayer items of type NameOrIndex 1800 // and evtl. correct that item to ensure unique names for that type. This call may 1801 // modify/correct entries inside of the given SfxItemSet 1802 SfxItemSet aTempLocalCopy(*pOtherSet); 1803 1804 rDoc.CheckForUniqueItemForLineFillNameOrIndex(aTempLocalCopy); 1805 bRet = lcl_ApplyOtherSet(*pNode, pHistory, aTempLocalCopy, firstSet, propsSet, pLayout) || bRet; 1806 } 1807 1808 DELETECHARSETS 1809 return bRet; 1810 } 1811 1812 if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() && pCharSet && pCharSet->Count() ) 1813 { 1814 if( pUndo ) 1815 pUndo->SaveRedlineData( rRg, false ); 1816 1817 lcl_SetRedlines(rDoc, rRg); 1818 } 1819 1820 /* now if range */ 1821 sal_uLong nNodes = 0; 1822 1823 SwNodeIndex aSt( rDoc.GetNodes() ); 1824 SwNodeIndex aEnd( rDoc.GetNodes() ); 1825 SwContentIndex aCntEnd( pEnd->GetContentNode(), pEnd->GetContentIndex() ); 1826 1827 if( pNode ) 1828 { 1829 const sal_Int32 nLen = pNode->Len(); 1830 if( pStt->GetNode() != pEnd->GetNode() ) 1831 aCntEnd.Assign( pNode, nLen ); 1832 1833 if( pStt->GetContentIndex() != 0 || aCntEnd.GetIndex() != nLen ) 1834 { 1835 // the SwRegHistory inserts the attribute into the TextNode! 1836 if( pNode->IsTextNode() && pCharSet && pCharSet->Count() ) 1837 { 1838 SwRegHistory history( pNode, *pNode, pHistory ); 1839 bRet = history.InsertItems(*pCharSet, 1840 pStt->GetContentIndex(), aCntEnd.GetIndex(), nFlags, /*ppNewTextAttr*/nullptr) 1841 || bRet; 1842 } 1843 1844 if( pOtherSet && pOtherSet->Count() ) 1845 { 1846 bRet = lcl_ApplyOtherSet(*pNode, pHistory, *pOtherSet, firstSet, propsSet, pLayout) || bRet; 1847 } 1848 1849 // Only selection in a Node. 1850 if( pStt->GetNode() == pEnd->GetNode() ) 1851 { 1852 DELETECHARSETS 1853 return bRet; 1854 } 1855 ++nNodes; 1856 aSt.Assign( pStt->GetNode(), +1 ); 1857 } 1858 else 1859 aSt = pStt->GetNode(); 1860 aCntEnd.Assign(pEnd->GetContentNode(), pEnd->GetContentIndex()); // aEnd was changed! 1861 } 1862 else 1863 aSt.Assign( pStt->GetNode(), +1 ); 1864 1865 // aSt points to the first full Node now 1866 1867 /* 1868 * The selection spans more than one Node. 1869 */ 1870 if( pStt->GetNode() < pEnd->GetNode() ) 1871 { 1872 pNode = pEnd->GetNode().GetContentNode(); 1873 if(pNode) 1874 { 1875 if( aCntEnd.GetIndex() != pNode->Len() ) 1876 { 1877 // the SwRegHistory inserts the attribute into the TextNode! 1878 if( pNode->IsTextNode() && pCharSet && pCharSet->Count() ) 1879 { 1880 SwRegHistory history( pNode, *pNode, pHistory ); 1881 (void)history.InsertItems(*pCharSet, 1882 0, aCntEnd.GetIndex(), nFlags, /*ppNewTextAttr*/nullptr); 1883 } 1884 1885 if( pOtherSet && pOtherSet->Count() ) 1886 { 1887 lcl_ApplyOtherSet(*pNode, pHistory, *pOtherSet, firstSet, propsSet, pLayout); 1888 } 1889 1890 ++nNodes; 1891 aEnd = pEnd->GetNode(); 1892 } 1893 else 1894 aEnd.Assign( pEnd->GetNode(), +1 ); 1895 } 1896 else 1897 aEnd = pEnd->GetNode(); 1898 } 1899 else 1900 aEnd.Assign( pEnd->GetNode(), +1 ); 1901 1902 // aEnd points BEHIND the last full node now 1903 1904 /* Edit the fully selected Nodes. */ 1905 // Reset all attributes from the set! 1906 if( pCharSet && pCharSet->Count() && !( SetAttrMode::DONTREPLACE & nFlags ) ) 1907 { 1908 ::sw::DocumentContentOperationsManager::ParaRstFormat aPara( 1909 pStt, pEnd, pHistory, pCharSet, pLayout); 1910 rDoc.GetNodes().ForEach( aSt, aEnd, ::sw::DocumentContentOperationsManager::lcl_RstTextAttr, &aPara ); 1911 } 1912 1913 bool bCreateSwpHints = pCharSet && ( 1914 SfxItemState::SET == pCharSet->GetItemState( RES_TXTATR_CHARFMT, false ) || 1915 SfxItemState::SET == pCharSet->GetItemState( RES_TXTATR_INETFMT, false ) ); 1916 1917 for (SwNodeIndex current = aSt; current < aEnd; ++current) 1918 { 1919 SwTextNode *const pTNd = current.GetNode().GetTextNode(); 1920 if (!pTNd) 1921 continue; 1922 1923 if (pLayout && pLayout->HasMergedParas() 1924 && pTNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden) 1925 { // not really sure what to do here, but applying to hidden 1926 continue; // nodes doesn't make sense... 1927 } 1928 1929 if( pHistory ) 1930 { 1931 SwRegHistory aRegH( pTNd, *pTNd, pHistory ); 1932 1933 if (pCharSet && pCharSet->Count()) 1934 { 1935 if (SwpHints *pSwpHints = bCreateSwpHints ? &pTNd->GetOrCreateSwpHints() 1936 : pTNd->GetpSwpHints()) 1937 { 1938 pSwpHints->Register( &aRegH ); 1939 } 1940 1941 pTNd->SetAttr(*pCharSet, 0, pTNd->GetText().getLength(), nFlags); 1942 1943 // re-fetch as it may be deleted by SetAttr 1944 if (SwpHints *pSwpHints = pTNd->GetpSwpHints()) 1945 pSwpHints->DeRegister(); 1946 } 1947 } 1948 else 1949 { 1950 if (pCharSet && pCharSet->Count()) 1951 pTNd->SetAttr(*pCharSet, 0, pTNd->GetText().getLength(), nFlags); 1952 } 1953 ++nNodes; 1954 } 1955 1956 if (pOtherSet && pOtherSet->Count()) 1957 { 1958 for (; aSt < aEnd; ++aSt) 1959 { 1960 pNode = aSt.GetNode().GetContentNode(); 1961 if (!pNode) 1962 continue; 1963 1964 lcl_ApplyOtherSet(*pNode, pHistory, *pOtherSet, firstSet, propsSet, pLayout, &aSt); 1965 ++nNodes; 1966 } 1967 } 1968 1969 DELETECHARSETS 1970 return (nNodes != 0) || bRet; 1971 } 1972 } 1973 1974 namespace sw 1975 { 1976 1977 namespace mark 1978 { 1979 bool IsFieldmarkOverlap(SwPaM const& rPaM) 1980 { 1981 std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks; 1982 sw::CalcBreaks(Breaks, rPaM); 1983 return !Breaks.empty(); 1984 } 1985 } 1986 1987 DocumentContentOperationsManager::DocumentContentOperationsManager( SwDoc& i_rSwdoc ) : m_rDoc( i_rSwdoc ) 1988 { 1989 } 1990 1991 /** 1992 * Checks if rStart..rEnd mark a range that makes sense to copy. 1993 * 1994 * IsMoveToFly means the copy is a move to create a fly 1995 * and so existing flys at the edge must not be copied. 1996 */ 1997 static bool IsEmptyRange(const SwPosition& rStart, const SwPosition& rEnd, 1998 SwCopyFlags const flags) 1999 { 2000 if (rStart == rEnd) 2001 { // check if a fly anchored there would be copied - then copy... 2002 return !IsDestroyFrameAnchoredAtChar(rStart, rStart, rEnd, 2003 (flags & SwCopyFlags::IsMoveToFly) 2004 ? DelContentType::WriterfilterHack|DelContentType::AllMask 2005 : DelContentType::AllMask); 2006 } 2007 else 2008 { 2009 return rEnd < rStart; 2010 } 2011 } 2012 2013 // Copy an area into this document or into another document 2014 bool DocumentContentOperationsManager::CopyRange(SwPaM& rPam, SwPosition& rPos, 2015 SwCopyFlags const flags, 2016 sal_uInt32 nMovedID) const 2017 { 2018 const SwPosition *pStt = rPam.Start(), *pEnd = rPam.End(); 2019 2020 SwDoc& rDoc = rPos.GetNode().GetDoc(); 2021 bool bColumnSel = rDoc.IsClipBoard() && rDoc.IsColumnSelection(); 2022 2023 // Catch if there's no copy to do 2024 if (!rPam.HasMark() || (IsEmptyRange(*pStt, *pEnd, flags) && !bColumnSel)) 2025 return false; 2026 2027 // Prevent copying into Flys that are anchored in the source range 2028 if (&rDoc == &m_rDoc && (flags & SwCopyFlags::CheckPosInFly)) 2029 { 2030 // Correct the Start-/EndNode 2031 SwNodeOffset nStt = pStt->GetNodeIndex(), 2032 nEnd = pEnd->GetNodeIndex(), 2033 nDiff = nEnd - nStt +1; 2034 SwNode* pNd = m_rDoc.GetNodes()[ nStt ]; 2035 if( pNd->IsContentNode() && pStt->GetContentIndex() ) 2036 { 2037 ++nStt; 2038 --nDiff; 2039 } 2040 if( (pNd = m_rDoc.GetNodes()[ nEnd ])->IsContentNode() && 2041 static_cast<SwContentNode*>(pNd)->Len() != pEnd->GetContentIndex() ) 2042 { 2043 --nEnd; 2044 --nDiff; 2045 } 2046 if( nDiff && 2047 lcl_ChkFlyFly( rDoc, nStt, nEnd, rPos.GetNodeIndex() ) ) 2048 { 2049 return false; 2050 } 2051 } 2052 2053 SwPaM* pRedlineRange = nullptr; 2054 if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() || 2055 (!rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ) ) 2056 pRedlineRange = new SwPaM( rPos ); 2057 2058 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); 2059 2060 bool bRet = false; 2061 2062 if( &rDoc != &m_rDoc ) 2063 { // ordinary copy 2064 bRet = CopyImpl(rPam, rPos, flags & ~SwCopyFlags::CheckPosInFly, pRedlineRange); 2065 } 2066 else if( ! ( *pStt <= rPos && rPos < *pEnd && 2067 ( pStt->GetNode() != pEnd->GetNode() || 2068 !pStt->GetNode().IsTextNode() )) ) 2069 { 2070 // Copy to a position outside of the area, or copy a single TextNode 2071 // Do an ordinary copy 2072 bRet = CopyImpl(rPam, rPos, flags & ~SwCopyFlags::CheckPosInFly, pRedlineRange); 2073 } 2074 else 2075 { 2076 // Copy the range in itself 2077 assert(!"mst: this is assumed to be dead code"); 2078 } 2079 2080 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); 2081 if( pRedlineRange ) 2082 { 2083 if( rDoc.getIDocumentRedlineAccess().IsRedlineOn() ) 2084 rDoc.getIDocumentRedlineAccess().AppendRedline( 2085 new SwRangeRedline(RedlineType::Insert, *pRedlineRange, nMovedID), true); 2086 else 2087 rDoc.getIDocumentRedlineAccess().SplitRedline( *pRedlineRange ); 2088 delete pRedlineRange; 2089 } 2090 2091 return bRet; 2092 } 2093 2094 static auto GetCorrPosition(SwPaM const& rPam) -> SwPosition 2095 { 2096 // tdf#152710 target position must be on node that survives deletion 2097 // so that PaMCorrAbs can invalidate SwUnoCursors properly 2098 return rPam.GetPoint()->GetNode().IsContentNode() 2099 ? *rPam.GetPoint() 2100 : rPam.GetMark()->GetNode().IsContentNode() 2101 ? *rPam.GetMark() 2102 // this would be the result in SwNodes::RemoveNode() 2103 : SwPosition(rPam.End()->GetNode(), SwNodeOffset(+1)); 2104 } 2105 2106 /// Delete a full Section of the NodeArray. 2107 /// The passed Node is located somewhere in the designated Section. 2108 void DocumentContentOperationsManager::DeleteSection( SwNode *pNode ) 2109 { 2110 assert(pNode && "Didn't pass a Node."); 2111 2112 SwStartNode* pSttNd = pNode->IsStartNode() ? static_cast<SwStartNode*>(pNode) 2113 : pNode->StartOfSectionNode(); 2114 SwNodeIndex aSttIdx( *pSttNd ), aEndIdx( *pNode->EndOfSectionNode() ); 2115 2116 // delete all Flys, Bookmarks, ... 2117 DelFlyInRange( aSttIdx.GetNode(), aEndIdx.GetNode() ); 2118 m_rDoc.getIDocumentRedlineAccess().DeleteRedline( *pSttNd, true, RedlineType::Any ); 2119 DelBookmarks(aSttIdx.GetNode(), aEndIdx.GetNode()); 2120 2121 { 2122 // move all Cursor/StackCursor/UnoCursor out of the to-be-deleted area 2123 SwPaM const range(aSttIdx, aEndIdx); 2124 SwPosition const pos(GetCorrPosition(range)); 2125 ::PaMCorrAbs(range, pos); 2126 } 2127 2128 m_rDoc.GetNodes().DelNodes( aSttIdx, aEndIdx.GetIndex() - aSttIdx.GetIndex() + 1 ); 2129 } 2130 2131 void DocumentContentOperationsManager::DeleteDummyChar( 2132 SwPosition const& rPos, sal_Unicode const cDummy) 2133 { 2134 SwPaM aPam(rPos, rPos); 2135 aPam.GetPoint()->AdjustContent(+1); 2136 assert(aPam.GetText().getLength() == 1 && aPam.GetText()[0] == cDummy); 2137 (void) cDummy; 2138 2139 DeleteRangeImpl(aPam, SwDeleteFlags::Default); 2140 2141 if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() 2142 && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()) 2143 { 2144 m_rDoc.getIDocumentRedlineAccess().CompressRedlines(); 2145 } 2146 } 2147 2148 void DocumentContentOperationsManager::DeleteRange( SwPaM & rPam ) 2149 { 2150 // Seek all redlines that are in that PaM to be deleted.. 2151 SwRedlineTable::size_type nRedlStart = m_rDoc.getIDocumentRedlineAccess().GetRedlinePos( 2152 rPam.Start()->GetNode(), RedlineType::Any); 2153 SwRedlineTable::size_type nRedlEnd = m_rDoc.getIDocumentRedlineAccess().GetRedlineEndPos( 2154 nRedlStart, rPam.End()->GetNode(), RedlineType::Any); 2155 2156 lcl_DoWithBreaks(*this, rPam, SwDeleteFlags::Default, &DocumentContentOperationsManager::DeleteRangeImpl); 2157 2158 // update all redlines was in the Pam that is 2159 m_rDoc.getIDocumentRedlineAccess().UpdateRedlineContentNode(nRedlStart, nRedlEnd); 2160 2161 if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() 2162 && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()) 2163 { 2164 m_rDoc.getIDocumentRedlineAccess().CompressRedlines(); 2165 } 2166 } 2167 2168 bool DocumentContentOperationsManager::DelFullPara( SwPaM& rPam ) 2169 { 2170 const SwPosition &rStt = *rPam.Start(), &rEnd = *rPam.End(); 2171 const SwNode* pNd = &rStt.GetNode(); 2172 SwNodeOffset nSectDiff = pNd->StartOfSectionNode()->EndOfSectionIndex() - 2173 pNd->StartOfSectionIndex(); 2174 SwNodeOffset nNodeDiff = rEnd.GetNodeIndex() - rStt.GetNodeIndex(); 2175 2176 if ( nSectDiff-SwNodeOffset(2) <= nNodeDiff || m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || 2177 /* #i9185# Prevent getting the node after the end node (see below) */ 2178 rEnd.GetNodeIndex() + 1 == m_rDoc.GetNodes().Count() ) 2179 { 2180 return false; 2181 } 2182 2183 { 2184 SwPaM temp(rPam, nullptr); 2185 if (!temp.HasMark()) 2186 { 2187 temp.SetMark(); 2188 } 2189 if (SwTextNode *const pNode = temp.Start()->GetNode().GetTextNode()) 2190 { // rPam may not have nContent set but IsFieldmarkOverlap requires it 2191 temp.Start()->AssignStartIndex(*pNode); 2192 } 2193 if (SwTextNode *const pNode = temp.End()->GetNode().GetTextNode()) 2194 { 2195 temp.End()->AssignEndIndex(*pNode); 2196 } 2197 if (sw::mark::IsFieldmarkOverlap(temp)) 2198 { // a bit of a problem: we want to completely remove the nodes 2199 // but then how can the CH_TXT_ATR survive? 2200 return false; 2201 } 2202 } 2203 2204 // Move hard page breaks to the following Node. 2205 bool bSavePageBreak = false, bSavePageDesc = false; 2206 2207 /* #i9185# This would lead to a segmentation fault if not caught above. */ 2208 SwNodeOffset nNextNd = rEnd.GetNodeIndex() + 1; 2209 SwTableNode *const pTableNd = m_rDoc.GetNodes()[ nNextNd ]->GetTableNode(); 2210 2211 if( pTableNd && pNd->IsContentNode() ) 2212 { 2213 SwFrameFormat* pTableFormat = pTableNd->GetTable().GetFrameFormat(); 2214 2215 { 2216 const SfxPoolItem *pItem; 2217 const SfxItemSet* pSet = static_cast<const SwContentNode*>(pNd)->GetpSwAttrSet(); 2218 if( pSet && SfxItemState::SET == pSet->GetItemState( RES_PAGEDESC, 2219 false, &pItem ) ) 2220 { 2221 pTableFormat->SetFormatAttr( *pItem ); 2222 bSavePageDesc = true; 2223 } 2224 2225 if( pSet && SfxItemState::SET == pSet->GetItemState( RES_BREAK, 2226 false, &pItem ) ) 2227 { 2228 pTableFormat->SetFormatAttr( *pItem ); 2229 bSavePageBreak = true; 2230 } 2231 } 2232 } 2233 2234 bool const bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo(); 2235 if( bDoesUndo ) 2236 { 2237 if( !rPam.HasMark() ) 2238 rPam.SetMark(); 2239 else if( rPam.GetPoint() == &rStt ) 2240 rPam.Exchange(); 2241 rPam.GetPoint()->Adjust(SwNodeOffset(1)); 2242 2243 SwContentNode *pTmpNode = rPam.GetPoint()->GetNode().GetContentNode(); 2244 bool bGoNext = (nullptr == pTmpNode); 2245 2246 if (rPam.GetMark()->GetContentNode()) 2247 rPam.GetMark()->SetContent( 0 ); 2248 2249 m_rDoc.GetIDocumentUndoRedo().ClearRedo(); 2250 2251 SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() ); 2252 { 2253 SwPosition aTmpPos( *aDelPam.GetPoint() ); 2254 if( bGoNext ) 2255 { 2256 SwNodes::GoNext(&aTmpPos); 2257 } 2258 ::PaMCorrAbs( aDelPam, aTmpPos ); 2259 } 2260 2261 std::unique_ptr<SwUndoDelete> pUndo(new SwUndoDelete(aDelPam, SwDeleteFlags::Default, true)); 2262 2263 *rPam.GetPoint() = *aDelPam.GetPoint(); 2264 pUndo->SetPgBrkFlags( bSavePageBreak, bSavePageDesc ); 2265 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo)); 2266 rPam.DeleteMark(); 2267 } 2268 else 2269 { 2270 SwNodeRange aRg( rStt.GetNode(), rEnd.GetNode() ); 2271 rPam.Normalize(false); 2272 2273 // Try to move past the End 2274 if( !rPam.Move( fnMoveForward, GoInNode ) ) 2275 { 2276 // Fair enough, at the Beginning then 2277 rPam.Exchange(); 2278 if( !rPam.Move( fnMoveBackward, GoInNode )) 2279 { 2280 SAL_WARN("sw.core", "DelFullPara: no more Nodes"); 2281 return false; 2282 } 2283 } 2284 2285 // must delete all fieldmarks before CorrAbs(), or they'll remain 2286 // moved to wrong node without their CH_TXT_ATR_FIELD* 2287 // (note: deleteMarks() doesn't help here, in case of partially 2288 // selected fieldmarks; let's delete these as re-inserting their chars 2289 // elsewhere looks difficult) 2290 ::std::set<::sw::mark::IFieldmark*> fieldmarks; 2291 for (SwNodeIndex i = aRg.aStart; i <= aRg.aEnd; ++i) 2292 { 2293 if (SwTextNode *const pTextNode = i.GetNode().GetTextNode()) 2294 { 2295 for (sal_Int32 j = 0; j < pTextNode->GetText().getLength(); ++j) 2296 { 2297 switch (pTextNode->GetText()[j]) 2298 { 2299 case CH_TXT_ATR_FIELDSTART: 2300 case CH_TXT_ATR_FIELDEND: 2301 fieldmarks.insert(m_rDoc.getIDocumentMarkAccess()->getFieldmarkAt(SwPosition(*pTextNode, j))); 2302 break; 2303 case CH_TXT_ATR_FIELDSEP: 2304 fieldmarks.insert(m_rDoc.getIDocumentMarkAccess()->getInnerFieldmarkFor(SwPosition(*pTextNode, j))); 2305 break; 2306 } 2307 } 2308 } 2309 } 2310 for (auto const pFieldMark : fieldmarks) 2311 { 2312 m_rDoc.getIDocumentMarkAccess()->deleteMark(pFieldMark); 2313 } 2314 2315 // move bookmarks, redlines etc. 2316 if (aRg.aStart == aRg.aEnd) // only first CorrAbs variant handles this 2317 { 2318 m_rDoc.CorrAbs( aRg.aStart.GetNode(), *rPam.GetPoint(), 0, true ); 2319 } 2320 else 2321 { 2322 SwDoc::CorrAbs( aRg.aStart, aRg.aEnd, *rPam.GetPoint(), true ); 2323 } 2324 2325 // What's with Flys? 2326 { 2327 // If there are FlyFrames left, delete these too 2328 for( size_t n = 0; n < m_rDoc.GetSpzFrameFormats()->size(); ++n ) 2329 { 2330 sw::SpzFrameFormat* pFly = (*m_rDoc.GetSpzFrameFormats())[n]; 2331 const SwFormatAnchor* pAnchor = &pFly->GetAnchor(); 2332 SwNode const*const pAnchorNode = pAnchor->GetAnchorNode(); 2333 if (pAnchorNode && 2334 ((RndStdIds::FLY_AT_PARA == pAnchor->GetAnchorId()) || 2335 (RndStdIds::FLY_AT_CHAR == pAnchor->GetAnchorId())) && 2336 // note: here use <= not < like in 2337 // IsDestroyFrameAnchoredAtChar() because of the increment 2338 // of rPam in the bDoesUndo path above! 2339 aRg.aStart <= *pAnchorNode && *pAnchorNode <= aRg.aEnd.GetNode() ) 2340 { 2341 m_rDoc.getIDocumentLayoutAccess().DelLayoutFormat( pFly ); 2342 --n; 2343 } 2344 } 2345 } 2346 2347 rPam.DeleteMark(); 2348 m_rDoc.GetNodes().Delete( aRg.aStart, nNodeDiff+1 ); 2349 } 2350 2351 if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() 2352 && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()) 2353 { 2354 m_rDoc.getIDocumentRedlineAccess().CompressRedlines(); 2355 } 2356 2357 m_rDoc.getIDocumentState().SetModified(); 2358 2359 return true; 2360 } 2361 2362 bool DocumentContentOperationsManager::DeleteAndJoin(SwPaM & rPam, SwDeleteFlags const flags) 2363 { 2364 if ( lcl_StrLenOverflow( rPam ) ) 2365 return false; 2366 2367 bool const ret = lcl_DoWithBreaks( *this, rPam, flags, (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn()) 2368 ? &DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl 2369 : &DocumentContentOperationsManager::DeleteAndJoinImpl ); 2370 2371 return ret; 2372 } 2373 2374 // It seems that this is mostly used by SwDoc internals; the only 2375 // way to call this from the outside seems to be the special case in 2376 // SwDoc::CopyRange (but I have not managed to actually hit that case). 2377 bool DocumentContentOperationsManager::MoveRange( SwPaM& rPaM, SwPosition& rPos, SwMoveFlags eMvFlags ) 2378 { 2379 // nothing moved: return 2380 const SwPosition *pStt = rPaM.Start(), *pEnd = rPaM.End(); 2381 if( !rPaM.HasMark() || *pStt >= *pEnd || (*pStt <= rPos && rPos < *pEnd)) 2382 return false; 2383 2384 assert(!sw::mark::IsFieldmarkOverlap(rPaM)); // probably an invalid redline was created? 2385 2386 // Save the paragraph anchored Flys, so that they can be moved. 2387 SaveFlyArr aSaveFlyArr; 2388 SaveFlyInRange( rPaM, rPos, aSaveFlyArr, bool( SwMoveFlags::ALLFLYS & eMvFlags ) ); 2389 2390 // save redlines (if DOC_MOVEREDLINES is used) 2391 SaveRedlines_t aSaveRedl; 2392 if( SwMoveFlags::REDLINES & eMvFlags && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ) 2393 { 2394 lcl_SaveRedlines( rPaM, aSaveRedl ); 2395 2396 // #i17764# unfortunately, code below relies on undos being 2397 // in a particular order, and presence of bookmarks 2398 // will change this order. Hence, we delete bookmarks 2399 // here without undo. 2400 ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo()); 2401 DelBookmarks( 2402 pStt->GetNode(), 2403 pEnd->GetNode(), 2404 nullptr, 2405 pStt->GetContentIndex(), 2406 pEnd->GetContentIndex()); 2407 } 2408 2409 bool bUpdateFootnote = false; 2410 SwFootnoteIdxs aTmpFntIdx; 2411 2412 std::unique_ptr<SwUndoMove> pUndoMove; 2413 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 2414 { 2415 m_rDoc.GetIDocumentUndoRedo().ClearRedo(); 2416 pUndoMove.reset(new SwUndoMove( rPaM, rPos )); 2417 pUndoMove->SetMoveRedlines( eMvFlags == SwMoveFlags::REDLINES ); 2418 } 2419 else 2420 { 2421 bUpdateFootnote = lcl_SaveFootnote( pStt->GetNode(), pEnd->GetNode(), rPos.GetNode(), 2422 m_rDoc.GetFootnoteIdxs(), aTmpFntIdx, 2423 pStt->GetContentIndex(), pEnd->GetContentIndex() ); 2424 } 2425 2426 bool bSplit = false; 2427 SwPaM aSavePam( rPos, rPos ); 2428 2429 // Move the SPoint to the beginning of the range 2430 if( rPaM.GetPoint() == pEnd ) 2431 rPaM.Exchange(); 2432 2433 // If there is a TextNode before and after the Move, create a JoinNext in the EditShell. 2434 SwTextNode* pSrcNd = rPaM.GetPoint()->GetNode().GetTextNode(); 2435 bool bCorrSavePam = pSrcNd && pStt->GetNode() != pEnd->GetNode(); 2436 2437 // If one or more TextNodes are moved, SwNodes::Move will do a SplitNode. 2438 // However, this does not update the cursor. So we create a TextNode to keep 2439 // updating the indices. After the Move the Node is optionally deleted. 2440 SwTextNode * pTNd = rPos.GetNode().GetTextNode(); 2441 if( pTNd && rPaM.GetPoint()->GetNode() != rPaM.GetMark()->GetNode() && 2442 ( rPos.GetContentIndex() || ( pTNd->Len() && bCorrSavePam )) ) 2443 { 2444 bSplit = true; 2445 const sal_Int32 nMkContent = rPaM.GetMark()->GetContentIndex(); 2446 2447 const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create()); 2448 pContentStore->Save( m_rDoc, rPos.GetNodeIndex(), rPos.GetContentIndex(), true ); 2449 2450 SwTextNode * pOrigNode = pTNd; 2451 assert(*aSavePam.GetPoint() == *aSavePam.GetMark() && 2452 *aSavePam.GetPoint() == rPos); 2453 assert(aSavePam.GetPoint()->GetContentNode() == pOrigNode); 2454 assert(aSavePam.GetPoint()->GetNode() == rPos.GetNode()); 2455 assert(rPos.GetNodeIndex() == pOrigNode->GetIndex()); 2456 2457 std::function<void (SwTextNode *, sw::mark::RestoreMode, bool)> restoreFunc( 2458 [&](SwTextNode *const, sw::mark::RestoreMode const eMode, bool) 2459 { 2460 if (!pContentStore->Empty()) 2461 { 2462 pContentStore->Restore(m_rDoc, pOrigNode->GetIndex()-SwNodeOffset(1), 0, true, false, eMode); 2463 } 2464 }); 2465 pTNd->SplitContentNode(rPos, &restoreFunc); 2466 2467 //A new node was inserted before the orig pTNd and the content up to 2468 //rPos moved into it. The old node is returned with the remainder 2469 //of the content in it. 2470 // 2471 //aSavePam was created with rPos, it continues to point to the 2472 //old node, but with the *original* content index into the node. 2473 //Seeing as all the orignode content before that index has 2474 //been removed, the new index into the original node should now be set 2475 //to 0 and the content index of rPos should also be adapted to the 2476 //truncated node 2477 assert(*aSavePam.GetPoint() == *aSavePam.GetMark() && 2478 *aSavePam.GetPoint() == rPos); 2479 assert(aSavePam.GetPoint()->GetContentNode() == pOrigNode); 2480 assert(aSavePam.GetPoint()->GetNode() == rPos.GetNode()); 2481 assert(rPos.GetNodeIndex() == pOrigNode->GetIndex()); 2482 aSavePam.GetPoint()->SetContent(0); 2483 rPos = *aSavePam.GetMark() = *aSavePam.GetPoint(); 2484 2485 // correct the PaM! 2486 if( rPos.GetNode() == rPaM.GetMark()->GetNode() ) 2487 { 2488 rPaM.GetMark()->Assign( rPos.GetNodeIndex() - SwNodeOffset(1) ); 2489 rPaM.GetMark()->SetContent( nMkContent ); 2490 } 2491 } 2492 2493 // Put back the Pam by one "content"; so that it's always outside of 2494 // the manipulated range. 2495 // tdf#99692 don't Move() back if that would end up in another node 2496 // because moving backward is not necessarily the inverse of forward then. 2497 // (but do Move() back if we have split the node) 2498 const bool bNullContent = !bSplit && aSavePam.GetPoint()->GetContentIndex() == 0; 2499 if( bNullContent ) 2500 { 2501 aSavePam.GetPoint()->Adjust(SwNodeOffset(-1)); 2502 } 2503 else 2504 { 2505 bool const success(aSavePam.Move(fnMoveBackward, GoInContent)); 2506 assert(success); 2507 (void) success; 2508 } 2509 2510 // Copy all Bookmarks that are within the Move range into an array, 2511 // that saves the position as an offset. 2512 std::vector< ::sw::mark::SaveBookmark> aSaveBkmks; 2513 DelBookmarks( 2514 pStt->GetNode(), 2515 pEnd->GetNode(), 2516 &aSaveBkmks, 2517 pStt->GetContentIndex(), 2518 pEnd->GetContentIndex()); 2519 2520 // If there is no range anymore due to the above deletions (e.g. the 2521 // footnotes got deleted), it's still a valid Move! 2522 if( *rPaM.GetPoint() != *rPaM.GetMark() ) 2523 { 2524 // now do the actual move 2525 m_rDoc.GetNodes().MoveRange( rPaM, rPos, m_rDoc.GetNodes() ); 2526 2527 // after a MoveRange() the Mark is deleted 2528 if ( rPaM.HasMark() ) // => no Move occurred! 2529 { 2530 return false; 2531 } 2532 } 2533 else 2534 rPaM.DeleteMark(); 2535 2536 OSL_ENSURE( *aSavePam.GetMark() == rPos || 2537 ( aSavePam.GetMark()->GetNode().GetContentNode() == nullptr ), 2538 "PaM was not moved. Aren't there ContentNodes at the beginning/end?" ); 2539 *aSavePam.GetMark() = rPos; 2540 2541 rPaM.SetMark(); // create a Sel. around the new range 2542 pTNd = aSavePam.GetPointNode().GetTextNode(); 2543 assert(!m_rDoc.GetIDocumentUndoRedo().DoesUndo()); 2544 bool bRemove = true; 2545 // Do two Nodes have to be joined at the SavePam? 2546 if (bSplit && pTNd) 2547 { 2548 if (pTNd->CanJoinNext()) 2549 { 2550 // Always join next, because <pTNd> has to stay as it is. 2551 // A join previous from its next would more or less delete <pTNd> 2552 pTNd->JoinNext(); 2553 bRemove = false; 2554 } 2555 } 2556 if (bNullContent) 2557 { 2558 aSavePam.GetPoint()->Adjust(SwNodeOffset(1)); 2559 } 2560 else if (bRemove) // No move forward after joining with next paragraph 2561 { 2562 aSavePam.Move( fnMoveForward, GoInContent ); 2563 } 2564 2565 // Insert the Bookmarks back into the Document. 2566 *rPaM.GetMark() = *aSavePam.Start(); 2567 for(auto& rBkmk : aSaveBkmks) 2568 rBkmk.SetInDoc( 2569 &m_rDoc, 2570 rPaM.GetMark()->GetNode(), 2571 rPaM.GetMark()->GetContentIndex()); 2572 *rPaM.GetPoint() = *aSavePam.End(); 2573 2574 // Move the Flys to the new position. 2575 // note: rPos is at the end here; can't really tell flys that used to be 2576 // at the start of rPam from flys that used to be at the end of rPam 2577 // unfortunately, so some of them are going to end up with wrong anchor... 2578 RestFlyInRange( aSaveFlyArr, *rPaM.Start(), &rPos.GetNode() ); 2579 2580 // restore redlines (if DOC_MOVEREDLINES is used) 2581 if( !aSaveRedl.empty() ) 2582 { 2583 lcl_RestoreRedlines( m_rDoc, *aSavePam.Start(), aSaveRedl ); 2584 } 2585 2586 if( bUpdateFootnote ) 2587 { 2588 if( !aTmpFntIdx.empty() ) 2589 { 2590 m_rDoc.GetFootnoteIdxs().insert( aTmpFntIdx ); 2591 aTmpFntIdx.clear(); 2592 } 2593 2594 m_rDoc.GetFootnoteIdxs().UpdateAllFootnote(); 2595 } 2596 2597 m_rDoc.getIDocumentState().SetModified(); 2598 return true; 2599 } 2600 2601 bool DocumentContentOperationsManager::MoveNodeRange( SwNodeRange& rRange, SwNode& rDestNd, 2602 SwMoveFlags eMvFlags ) 2603 { 2604 // Moves all Nodes to the new position. 2605 // Bookmarks are moved too (currently without Undo support). 2606 2607 // If footnotes are being moved to the special section, remove them now. 2608 2609 // Or else delete the Frames for all footnotes that are being moved 2610 // and have it rebuild after the Move (footnotes can change pages). 2611 // Additionally we have to correct the FootnoteIdx array's sorting. 2612 bool bUpdateFootnote = false; 2613 SwFootnoteIdxs aTmpFntIdx; 2614 2615 std::unique_ptr<SwUndoMove> pUndo; 2616 if ((SwMoveFlags::CREATEUNDOOBJ & eMvFlags ) && m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 2617 { 2618 pUndo.reset(new SwUndoMove( m_rDoc, rRange, rDestNd )); 2619 } 2620 else 2621 { 2622 bUpdateFootnote = lcl_SaveFootnote( rRange.aStart.GetNode(), rRange.aEnd.GetNode(), rDestNd, 2623 m_rDoc.GetFootnoteIdxs(), aTmpFntIdx ); 2624 } 2625 2626 SaveRedlines_t aSaveRedl; 2627 std::vector<SwRangeRedline*> aSavRedlInsPosArr; 2628 if( SwMoveFlags::REDLINES & eMvFlags && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ) 2629 { 2630 lcl_SaveRedlines( rRange, aSaveRedl ); 2631 2632 // Find all RedLines that end at the InsPos. 2633 // These have to be moved back to the "old" position after the Move. 2634 SwRedlineTable::size_type nRedlPos = m_rDoc.getIDocumentRedlineAccess().GetRedlinePos( rDestNd, RedlineType::Any ); 2635 if( SwRedlineTable::npos != nRedlPos ) 2636 { 2637 const SwPosition *pRStt, *pREnd; 2638 do { 2639 SwRangeRedline* pTmp = m_rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nRedlPos ]; 2640 pRStt = pTmp->Start(); 2641 pREnd = pTmp->End(); 2642 if( pREnd->GetNode() == rDestNd && pRStt->GetNode() < rDestNd ) 2643 { 2644 aSavRedlInsPosArr.push_back( pTmp ); 2645 } 2646 } while( pRStt->GetNode() < rDestNd && ++nRedlPos < m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().size()); 2647 } 2648 } 2649 2650 // Copy all Bookmarks that are within the Move range into an array 2651 // that stores all references to positions as an offset. 2652 // The final mapping happens after the Move. 2653 std::vector< ::sw::mark::SaveBookmark> aSaveBkmks; 2654 DelBookmarks(rRange.aStart.GetNode(), rRange.aEnd.GetNode(), &aSaveBkmks); 2655 2656 // Save the paragraph-bound Flys, so that they can be moved. 2657 SaveFlyArr aSaveFlyArr; 2658 if( !m_rDoc.GetSpzFrameFormats()->empty() ) 2659 SaveFlyInRange( rRange, aSaveFlyArr ); 2660 2661 // Set it to before the Position, so that it cannot be moved further. 2662 SwNodeIndex aIdx( rDestNd, -1 ); 2663 2664 std::optional<SwNodeIndex> oSaveInsPos; 2665 if( pUndo ) 2666 oSaveInsPos.emplace(rRange.aStart, -1 ); 2667 2668 // move the Nodes 2669 bool bNoDelFrames = bool(SwMoveFlags::NO_DELFRMS & eMvFlags); 2670 if( m_rDoc.GetNodes().MoveNodes( rRange, m_rDoc.GetNodes(), rDestNd, !bNoDelFrames ) ) 2671 { 2672 ++aIdx; // again back to old position 2673 if( oSaveInsPos ) 2674 ++(*oSaveInsPos); 2675 } 2676 else 2677 { 2678 aIdx = rRange.aStart; 2679 pUndo.reset(); 2680 } 2681 2682 // move the Flys to the new position 2683 if( !aSaveFlyArr.empty() ) 2684 { 2685 SwPosition const tmp(aIdx); 2686 RestFlyInRange(aSaveFlyArr, tmp, nullptr); 2687 } 2688 2689 // Add the Bookmarks back to the Document 2690 for(auto& rBkmk : aSaveBkmks) 2691 rBkmk.SetInDoc(&m_rDoc, aIdx.GetNode()); 2692 2693 if( !aSavRedlInsPosArr.empty() ) 2694 { 2695 for(SwRangeRedline* pTmp : aSavRedlInsPosArr) 2696 { 2697 if( m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().Contains( pTmp ) ) 2698 { 2699 SwPosition* pEnd = pTmp->End(); 2700 pEnd->Assign(aIdx); 2701 } 2702 } 2703 } 2704 2705 if( !aSaveRedl.empty() ) 2706 lcl_RestoreRedlines( m_rDoc, aIdx.GetIndex(), aSaveRedl ); 2707 2708 if( pUndo ) 2709 { 2710 pUndo->SetDestRange( aIdx.GetNode(), rDestNd, *oSaveInsPos ); 2711 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo)); 2712 } 2713 2714 oSaveInsPos.reset(); 2715 2716 if( bUpdateFootnote ) 2717 { 2718 if( !aTmpFntIdx.empty() ) 2719 { 2720 m_rDoc.GetFootnoteIdxs().insert( aTmpFntIdx ); 2721 aTmpFntIdx.clear(); 2722 } 2723 2724 m_rDoc.GetFootnoteIdxs().UpdateAllFootnote(); 2725 } 2726 2727 m_rDoc.getIDocumentState().SetModified(); 2728 return true; 2729 } 2730 2731 void DocumentContentOperationsManager::MoveAndJoin( SwPaM& rPaM, SwPosition& rPos ) 2732 { 2733 SwNodeIndex aIdx( rPaM.Start()->GetNode() ); 2734 bool bJoinText = aIdx.GetNode().IsTextNode(); 2735 bool bOneNode = rPaM.GetPoint()->GetNode() == rPaM.GetMark()->GetNode(); 2736 --aIdx; // in front of the move area! 2737 2738 bool bRet = MoveRange( rPaM, rPos, SwMoveFlags::DEFAULT ); 2739 if( !bRet || bOneNode ) 2740 return; 2741 2742 if( bJoinText ) 2743 ++aIdx; 2744 SwTextNode * pTextNd = aIdx.GetNode().GetTextNode(); 2745 SwNodeIndex aNxtIdx( aIdx ); 2746 if( pTextNd && pTextNd->CanJoinNext( &aNxtIdx ) ) 2747 { 2748 { // Block so SwContentIndex into node is deleted before Join 2749 m_rDoc.CorrRel( aNxtIdx.GetNode(), 2750 SwPosition( *pTextNd, pTextNd->GetText().getLength() ), 2751 0, true ); 2752 } 2753 pTextNd->JoinNext(); 2754 } 2755 } 2756 2757 // Overwrite only uses the point of the PaM, the mark is ignored; characters 2758 // are replaced from point until the end of the node; at the end of the node, 2759 // characters are inserted. 2760 bool DocumentContentOperationsManager::Overwrite( const SwPaM &rRg, const OUString &rStr ) 2761 { 2762 assert(rStr.getLength()); 2763 SwPosition& rPt = *const_cast<SwPosition*>(rRg.GetPoint()); 2764 if( m_rDoc.GetAutoCorrExceptWord() ) // Add to AutoCorrect 2765 { 2766 if( 1 == rStr.getLength() ) 2767 m_rDoc.GetAutoCorrExceptWord()->CheckChar( rPt, rStr[ 0 ] ); 2768 m_rDoc.DeleteAutoCorrExceptWord(); 2769 } 2770 2771 SwTextNode *pNode = rPt.GetNode().GetTextNode(); 2772 if (!pNode || rStr.getLength() > pNode->GetSpaceLeft()) // worst case: no erase 2773 { 2774 return false; 2775 } 2776 2777 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 2778 { 2779 m_rDoc.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called 2780 } 2781 2782 const size_t nOldAttrCnt = pNode->GetpSwpHints() 2783 ? pNode->GetpSwpHints()->Count() : 0; 2784 SwDataChanged aTmp( rRg ); 2785 sal_Int32 const nActualStart(rPt.GetContentIndex()); 2786 sal_Int32 nStart = 0; 2787 2788 bool bOldExpFlg = pNode->IsIgnoreDontExpand(); 2789 pNode->SetIgnoreDontExpand( true ); 2790 2791 for( sal_Int32 nCnt = 0; nCnt < rStr.getLength(); ++nCnt ) 2792 { 2793 // start behind the characters (to fix the attributes!) 2794 nStart = rPt.GetContentIndex(); 2795 if (nStart < pNode->GetText().getLength()) 2796 { 2797 lcl_SkipAttr( pNode, rPt, nStart ); 2798 } 2799 sal_Unicode c = rStr[ nCnt ]; 2800 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 2801 { 2802 bool bMerged(false); 2803 if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo()) 2804 { 2805 SwUndo *const pUndo = m_rDoc.GetUndoManager().GetLastUndo(); 2806 SwUndoOverwrite *const pUndoOW( 2807 dynamic_cast<SwUndoOverwrite *>(pUndo) ); 2808 if (pUndoOW) 2809 { 2810 // if CanGrouping() returns true it's already merged 2811 bMerged = pUndoOW->CanGrouping(m_rDoc, rPt, c); 2812 } 2813 } 2814 if (!bMerged) 2815 { 2816 m_rDoc.GetIDocumentUndoRedo().AppendUndo( 2817 std::make_unique<SwUndoOverwrite>(m_rDoc, rPt, c) ); 2818 } 2819 } 2820 else 2821 { 2822 // start behind the characters (to fix the attributes!) 2823 if (nStart < pNode->GetText().getLength()) 2824 rPt.AdjustContent(+1); 2825 pNode->InsertText( OUString(c), rPt, SwInsertFlags::EMPTYEXPAND ); 2826 if( nStart+1 < rPt.GetContentIndex() ) 2827 { 2828 rPt.SetContent(nStart); 2829 pNode->EraseText( rPt, 1 ); 2830 rPt.AdjustContent(+1); 2831 } 2832 } 2833 } 2834 pNode->SetIgnoreDontExpand( bOldExpFlg ); 2835 2836 const size_t nNewAttrCnt = pNode->GetpSwpHints() 2837 ? pNode->GetpSwpHints()->Count() : 0; 2838 if( nOldAttrCnt != nNewAttrCnt ) 2839 { 2840 const SwUpdateAttr aHint(0,0,0); 2841 pNode->TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint)); 2842 } 2843 2844 if (!m_rDoc.GetIDocumentUndoRedo().DoesUndo() && 2845 !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()) 2846 { 2847 SwPaM aPam(rPt.GetNode(), nActualStart, rPt.GetNode(), rPt.GetContentIndex()); 2848 m_rDoc.getIDocumentRedlineAccess().DeleteRedline( aPam, true, RedlineType::Any ); 2849 } 2850 else if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() ) 2851 { 2852 // FIXME: this redline is WRONG: there is no DELETE, and the skipped 2853 // characters are also included in aPam 2854 SwPaM aPam(rPt.GetNode(), nActualStart, rPt.GetNode(), rPt.GetContentIndex()); 2855 m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true); 2856 } 2857 2858 m_rDoc.getIDocumentState().SetModified(); 2859 return true; 2860 } 2861 2862 bool DocumentContentOperationsManager::InsertString( const SwPaM &rRg, const OUString &rStr, 2863 const SwInsertFlags nInsertMode ) 2864 { 2865 // tdf#119019 accept tracked paragraph formatting to do not hide new insertions 2866 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() ) 2867 { 2868 RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); 2869 m_rDoc.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting( rRg ); 2870 if (eOld != m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags()) 2871 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld ); 2872 } 2873 2874 // fetching DoesUndo is surprisingly expensive 2875 bool bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo(); 2876 if (bDoesUndo) 2877 m_rDoc.GetIDocumentUndoRedo().ClearRedo(); // AppendUndo not always called! 2878 2879 const SwPosition& rPos = *rRg.GetPoint(); 2880 2881 if( m_rDoc.GetAutoCorrExceptWord() ) // add to auto correction 2882 { 2883 if( 1 == rStr.getLength() && m_rDoc.GetAutoCorrExceptWord()->IsDeleted() ) 2884 { 2885 m_rDoc.GetAutoCorrExceptWord()->CheckChar( rPos, rStr[ 0 ] ); 2886 } 2887 m_rDoc.DeleteAutoCorrExceptWord(); 2888 } 2889 2890 SwTextNode *const pNode = rPos.GetNode().GetTextNode(); 2891 if(!pNode) 2892 return false; 2893 2894 SwDataChanged aTmp( rRg ); 2895 2896 if (!bDoesUndo || !m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo()) 2897 { 2898 OUString const ins(pNode->InsertText(rStr, rPos, nInsertMode)); 2899 if (bDoesUndo) 2900 { 2901 m_rDoc.GetIDocumentUndoRedo().AppendUndo( 2902 std::make_unique<SwUndoInsert>(rPos.GetNode(), 2903 rPos.GetContentIndex(), ins.getLength(), nInsertMode)); 2904 } 2905 } 2906 else 2907 { // if Undo and grouping is enabled, everything changes! 2908 SwUndoInsert * pUndo = nullptr; 2909 2910 // don't group the start if hints at the start should be expanded 2911 if (!(nInsertMode & SwInsertFlags::FORCEHINTEXPAND)) 2912 { 2913 SwUndo *const pLastUndo = m_rDoc.GetUndoManager().GetLastUndo(); 2914 SwUndoInsert *const pUndoInsert( 2915 dynamic_cast<SwUndoInsert *>(pLastUndo) ); 2916 if (pUndoInsert && pUndoInsert->CanGrouping(rPos)) 2917 { 2918 pUndo = pUndoInsert; 2919 } 2920 } 2921 2922 CharClass const& rCC = GetAppCharClass(); 2923 sal_Int32 nInsPos = rPos.GetContentIndex(); 2924 2925 if (!pUndo) 2926 { 2927 pUndo = new SwUndoInsert( rPos.GetNode(), nInsPos, 0, nInsertMode, 2928 !rCC.isLetterNumeric( rStr, 0 ) ); 2929 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) ); 2930 } 2931 2932 OUString const ins(pNode->InsertText(rStr, rPos, nInsertMode)); 2933 2934 for (sal_Int32 i = 0; i < ins.getLength(); ++i) 2935 { 2936 nInsPos++; 2937 // if CanGrouping() returns true, everything has already been done 2938 if (!pUndo->CanGrouping(ins[i])) 2939 { 2940 pUndo = new SwUndoInsert(rPos.GetNode(), nInsPos, 1, nInsertMode, 2941 !rCC.isLetterNumeric(ins, i)); 2942 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) ); 2943 } 2944 } 2945 } 2946 2947 // To-Do - add 'SwExtraRedlineTable' also ? 2948 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )) 2949 { 2950 SwPaM aPam( rPos.GetNode(), aTmp.GetContent(), 2951 rPos.GetNode(), rPos.GetContentIndex()); 2952 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() ) 2953 { 2954 m_rDoc.getIDocumentRedlineAccess().AppendRedline( 2955 new SwRangeRedline( RedlineType::Insert, aPam ), true); 2956 } 2957 else 2958 { 2959 m_rDoc.getIDocumentRedlineAccess().SplitRedline( aPam ); 2960 } 2961 } 2962 2963 m_rDoc.getIDocumentState().SetModified(); 2964 return true; 2965 } 2966 2967 void DocumentContentOperationsManager::SetIME(bool bIME) 2968 { 2969 m_bIME = bIME; 2970 } 2971 2972 bool DocumentContentOperationsManager::GetIME() const 2973 { 2974 return m_bIME; 2975 } 2976 2977 void DocumentContentOperationsManager::TransliterateText( 2978 const SwPaM& rPaM, 2979 utl::TransliterationWrapper& rTrans ) 2980 { 2981 std::unique_ptr<SwUndoTransliterate> pUndo; 2982 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 2983 pUndo.reset(new SwUndoTransliterate( rPaM, rTrans )); 2984 2985 auto [pStt, pEnd] = rPaM.StartEnd(); // SwPosition* 2986 SwNodeOffset nSttNd = pStt->GetNodeIndex(), 2987 nEndNd = pEnd->GetNodeIndex(); 2988 sal_Int32 nSttCnt = pStt->GetContentIndex(); 2989 sal_Int32 nEndCnt = pEnd->GetContentIndex(); 2990 2991 SwTextNode* pTNd = pStt->GetNode().GetTextNode(); 2992 bool bNoSelection = (pStt == pEnd) && pTNd; // no selection? 2993 if ( bNoSelection ) 2994 { 2995 /* Check if cursor is inside of a word */ 2996 assert(g_pBreakIt && g_pBreakIt->GetBreakIter().is()); 2997 Boundary aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary( 2998 pTNd->GetText(), nSttCnt, 2999 g_pBreakIt->GetLocale( pTNd->GetLang( nSttCnt ) ), 3000 WordType::ANY_WORD /*ANYWORD_IGNOREWHITESPACES*/, 3001 true); 3002 3003 if( aBndry.startPos < nSttCnt && nSttCnt < aBndry.endPos ) 3004 { 3005 /* Cursor is inside of a word */ 3006 if (rTrans.getType() == TransliterationFlags::SENTENCE_CASE) { 3007 /* set current sentence as 'area of effect' */ 3008 nSttCnt = g_pBreakIt->GetBreakIter()->beginOfSentence( 3009 pTNd->GetText(), nSttCnt, 3010 g_pBreakIt->GetLocale( pTNd->GetLang( nSttCnt ) ) ); 3011 nEndCnt = g_pBreakIt->GetBreakIter()->endOfSentence( 3012 pTNd->GetText(), nEndCnt, 3013 g_pBreakIt->GetLocale( pTNd->GetLang( nEndCnt ) ) ); 3014 } else { 3015 /* Set current word as 'area of effect' */ 3016 nSttCnt = aBndry.startPos; 3017 nEndCnt = aBndry.endPos; 3018 } 3019 } else { 3020 /* Cursor is not inside of a word. Nothing should happen. */ 3021 /* Except in the case of change tracking, when the cursor is at the end of the change */ 3022 /* Recognize and reject the previous deleted and inserted words to allow to cycle */ 3023 IDocumentRedlineAccess& rIDRA = m_rDoc.getIDocumentRedlineAccess(); 3024 if ( IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineFlags() ) && 3025 pStt->GetContentIndex() > 0 ) 3026 { 3027 SwPosition aPos(*pStt->GetContentNode(), pStt->GetContentIndex() - 1); 3028 SwRedlineTable::size_type n = 0; 3029 3030 const SwRangeRedline* pFnd = 3031 rIDRA.GetRedlineTable().FindAtPosition( aPos, n ); 3032 if ( pFnd && RedlineType::Insert == pFnd->GetType() && n > 0 ) 3033 { 3034 const SwRangeRedline* pFnd2 = rIDRA.GetRedlineTable()[n-1]; 3035 if ( RedlineType::Delete == pFnd2->GetType() && 3036 m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() && 3037 *pFnd2->End() == *pFnd->Start() && 3038 pFnd->GetAuthor() == pFnd2->GetAuthor() ) 3039 { 3040 SwPosition aPos2(*pFnd2->End()); 3041 rIDRA.RejectRedline(*pFnd, true); 3042 rIDRA.RejectRedline(*pFnd2, true); 3043 // positionate the text cursor inside the changed word to allow to cycle 3044 if ( SwWrtShell *pWrtShell = dynamic_cast<SwWrtShell*>( 3045 m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()) ) 3046 { 3047 pWrtShell->GetCursor()->GetPoint()-> 3048 Assign(*aPos2.GetContentNode(), aPos2.GetContentIndex() - 1); 3049 } 3050 } 3051 } 3052 } 3053 return; 3054 } 3055 } 3056 else 3057 { 3058 bool bHasTrackedChange = false; 3059 IDocumentRedlineAccess& rIDRA = m_rDoc.getIDocumentRedlineAccess(); 3060 if ( IDocumentRedlineAccess::IsShowChanges( rIDRA.GetRedlineFlags() ) && 3061 pEnd->GetContentIndex() > 0 ) 3062 { 3063 // search all own redlines within the selected area 3064 SwRedlineTable::size_type n = SwRedlineTable::npos; 3065 const SwRedlineTable& aRedlineTable = rIDRA.GetRedlineTable(); 3066 for( SwRedlineTable::size_type m = 0; m < aRedlineTable.size(); ++m ) 3067 { 3068 const SwRangeRedline* pRedline = aRedlineTable[ m ]; 3069 3070 if ( *pRedline->Start() > *pEnd ) 3071 break; 3072 3073 if ( *pRedline->Start() >= *pStt ) 3074 n = m; 3075 } 3076 3077 if ( n != SwRedlineTable::npos && n > 0 ) 3078 { 3079 SwWrtShell *pWrtShell = dynamic_cast<SwWrtShell*>( 3080 m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()); 3081 3082 sal_Int32 nRejectedCharacters = 0; 3083 SwRangeRedline* pFnd = rIDRA.GetRedlineTable()[n]; 3084 SwRangeRedline* pFnd2 = rIDRA.GetRedlineTable()[--n]; 3085 // loop on all redlines of a case changing, and reject them 3086 while ( ( ( RedlineType::Insert == pFnd->GetType() && 3087 RedlineType::Delete == pFnd2->GetType() ) || 3088 ( RedlineType::Delete == pFnd->GetType() && 3089 RedlineType::Insert == pFnd2->GetType() ) ) && 3090 pWrtShell && 3091 // use time stamp to recognize the multiple selections in the text, 3092 // not only the changes from the same author within the (sometimes 3093 // incomplete) selection 3094 ( pFnd2->GetTimeStamp() == pFnd->GetTimeStamp() || 3095 ( pStt->GetContentNode() < pFnd2->Start()->GetContentNode() || 3096 ( pStt->GetContentNode() == pFnd2->Start()->GetContentNode() && 3097 nSttCnt <= pFnd2->Start()->GetContentIndex() ) ) ) && 3098 pFnd->GetAuthor() == pFnd2->GetAuthor() ) 3099 { 3100 bHasTrackedChange = true; 3101 3102 if ( RedlineType::Insert == pFnd->GetType() ) 3103 nRejectedCharacters += pFnd->GetText().getLength(); 3104 3105 rIDRA.RejectRedline(*pFnd, true); 3106 3107 pFnd = pFnd2; 3108 if ( n == 0 ) 3109 break; 3110 pFnd2 = rIDRA.GetRedlineTable()[--n]; 3111 } 3112 3113 // remove the last item and restore the original selection within the node 3114 if ( bHasTrackedChange ) 3115 { 3116 if ( nSttNd == nEndNd ) 3117 { 3118 pWrtShell->GetCursor()->GetPoint()-> 3119 Assign(*rPaM.Start()->GetContentNode(), nSttCnt); 3120 if ( nEndCnt >= nRejectedCharacters ) 3121 pWrtShell->GetCursor()->GetMark()-> 3122 Assign(*rPaM.End()->GetContentNode(), nEndCnt - nRejectedCharacters); 3123 } 3124 rIDRA.RejectRedline(*pFnd, true); 3125 } 3126 } 3127 } 3128 3129 // TODO handle title case to lowercase 3130 if ( bHasTrackedChange ) 3131 return; 3132 } 3133 3134 bool bUseRedlining = m_rDoc.getIDocumentRedlineAccess().IsRedlineOn(); 3135 // as a workaround for a known performance problem, switch off redlining 3136 // to avoid freezing, if transliteration could result too many redlines 3137 if ( bUseRedlining ) 3138 { 3139 const sal_uLong nMaxRedlines = 500; 3140 const bool bIsTitleCase = rTrans.getType() == TransliterationFlags::TITLE_CASE; 3141 sal_uLong nAffectedNodes = 0; 3142 sal_uLong nAffectedChars = nEndCnt; 3143 SwNodeIndex aIdx( pStt->GetNode() ); 3144 for( ; aIdx.GetIndex() <= nEndNd; ++aIdx ) 3145 { 3146 SwTextNode* pAffectedNode = aIdx.GetNode().GetTextNode(); 3147 3148 // don't count not text nodes or empty text nodes 3149 if( !pAffectedNode || pAffectedNode->GetText().isEmpty() ) 3150 continue; 3151 3152 nAffectedNodes++; 3153 3154 // count characters of the node (the last - maybe partially 3155 // selected - node was counted at initialization of nAffectedChars) 3156 if( aIdx.GetIndex() < nEndNd ) 3157 nAffectedChars += pAffectedNode->GetText().getLength(); 3158 3159 // transliteration creates n redlines for n nodes, except in the 3160 // case of title case, where it creates n redlines for n words 3161 if( nAffectedNodes > nMaxRedlines || 3162 // estimate word count based on the character count, where 3163 // 6 = average English word length is ~5 letters + space 3164 ( bIsTitleCase && (nAffectedChars - nSttCnt)/6 > nMaxRedlines ) ) 3165 { 3166 bUseRedlining = false; 3167 break; 3168 } 3169 } 3170 } 3171 3172 if( nSttNd != nEndNd ) // is more than one text node involved? 3173 { 3174 // iterate over all affected text nodes, the first and the last one 3175 // may be incomplete because the selection starts and/or ends there 3176 3177 SwNodeIndex aIdx( pStt->GetNode() ); 3178 if( nSttCnt ) 3179 { 3180 ++aIdx; 3181 if( pTNd ) 3182 { 3183 pTNd->TransliterateText( 3184 rTrans, nSttCnt, pTNd->GetText().getLength(), pUndo.get(), bUseRedlining); 3185 } 3186 } 3187 3188 for( ; aIdx.GetIndex() < nEndNd; ++aIdx ) 3189 { 3190 pTNd = aIdx.GetNode().GetTextNode(); 3191 if (pTNd) 3192 { 3193 pTNd->TransliterateText( 3194 rTrans, 0, pTNd->GetText().getLength(), pUndo.get(), bUseRedlining); 3195 } 3196 } 3197 3198 if( nEndCnt && nullptr != ( pTNd = pEnd->GetNode().GetTextNode() )) 3199 { 3200 pTNd->TransliterateText( rTrans, 0, nEndCnt, pUndo.get(), bUseRedlining ); 3201 } 3202 } 3203 else if( pTNd && nSttCnt < nEndCnt ) 3204 { 3205 pTNd->TransliterateText( rTrans, nSttCnt, nEndCnt, pUndo.get(), bUseRedlining ); 3206 } 3207 if( pUndo && pUndo->HasData() ) 3208 { 3209 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(pUndo)); 3210 } 3211 3212 // restore selection after tracked changes 3213 if ( !bNoSelection && bUseRedlining && nSttNd == nEndNd ) 3214 { 3215 if ( SwWrtShell *pWrtShell = dynamic_cast<SwWrtShell*>( 3216 m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell()) ) 3217 { 3218 *pWrtShell->GetCursor()->GetMark() = *pWrtShell->GetCursor()->End(); 3219 pWrtShell->GetCursor()->GetPoint()->Assign(*pStt->GetContentNode(), nSttCnt); 3220 } 3221 } 3222 3223 m_rDoc.getIDocumentState().SetModified(); 3224 } 3225 3226 SwFlyFrameFormat* DocumentContentOperationsManager::InsertGraphic( 3227 const SwPaM &rRg, 3228 const OUString& rGrfName, 3229 const OUString& rFltName, 3230 const Graphic* pGraphic, 3231 const SfxItemSet* pFlyAttrSet, 3232 const SfxItemSet* pGrfAttrSet, 3233 SwFrameFormat* pFrameFormat ) 3234 { 3235 if( !pFrameFormat ) 3236 pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_GRAPHIC ); 3237 SwGrfNode* pSwGrfNode = SwNodes::MakeGrfNode( 3238 m_rDoc.GetNodes().GetEndOfAutotext(), 3239 rGrfName, rFltName, pGraphic, 3240 m_rDoc.GetDfltGrfFormatColl() ); 3241 SwFlyFrameFormat* pSwFlyFrameFormat = InsNoTextNode( *rRg.GetPoint(), pSwGrfNode, 3242 pFlyAttrSet, pGrfAttrSet, pFrameFormat ); 3243 return pSwFlyFrameFormat; 3244 } 3245 3246 SwFlyFrameFormat* DocumentContentOperationsManager::InsertEmbObject( 3247 const SwPaM &rRg, const svt::EmbeddedObjectRef& xObj, 3248 SfxItemSet* pFlyAttrSet) 3249 { 3250 sal_uInt16 nId = RES_POOLFRM_OLE; 3251 if (xObj.is()) 3252 { 3253 SvGlobalName aClassName( xObj->getClassID() ); 3254 if (SotExchange::IsMath(aClassName)) 3255 { 3256 nId = RES_POOLFRM_FORMEL; 3257 } 3258 } 3259 3260 SwFrameFormat* pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( nId ); 3261 3262 return InsNoTextNode( *rRg.GetPoint(), m_rDoc.GetNodes().MakeOLENode( 3263 m_rDoc.GetNodes().GetEndOfAutotext(), 3264 xObj, 3265 m_rDoc.GetDfltGrfFormatColl() ), 3266 pFlyAttrSet, nullptr, 3267 pFrameFormat ); 3268 } 3269 3270 SwFlyFrameFormat* DocumentContentOperationsManager::InsertOLE(const SwPaM &rRg, const OUString& rObjName, 3271 sal_Int64 nAspect, 3272 const SfxItemSet* pFlyAttrSet, 3273 const SfxItemSet* pGrfAttrSet) 3274 { 3275 SwFrameFormat* pFrameFormat = m_rDoc.getIDocumentStylePoolAccess().GetFrameFormatFromPool( RES_POOLFRM_OLE ); 3276 3277 return InsNoTextNode( *rRg.GetPoint(), 3278 m_rDoc.GetNodes().MakeOLENode( 3279 m_rDoc.GetNodes().GetEndOfAutotext(), 3280 rObjName, 3281 nAspect, 3282 m_rDoc.GetDfltGrfFormatColl(), 3283 nullptr ), 3284 pFlyAttrSet, pGrfAttrSet, 3285 pFrameFormat ); 3286 } 3287 3288 void DocumentContentOperationsManager::ReRead( SwPaM& rPam, const OUString& rGrfName, 3289 const OUString& rFltName, const Graphic* pGraphic ) 3290 { 3291 SwGrfNode *pGrfNd; 3292 if( !(( !rPam.HasMark() 3293 || rPam.GetPoint()->GetNodeIndex() == rPam.GetMark()->GetNodeIndex() ) 3294 && nullptr != ( pGrfNd = rPam.GetPoint()->GetNode().GetGrfNode() )) ) 3295 return; 3296 3297 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 3298 { 3299 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoReRead>(rPam, *pGrfNd)); 3300 } 3301 3302 // Because we don't know if we can mirror the graphic, the mirror attribute is always reset 3303 if( MirrorGraph::Dont != pGrfNd->GetSwAttrSet(). 3304 GetMirrorGrf().GetValue() ) 3305 pGrfNd->SetAttr( SwMirrorGrf() ); 3306 3307 pGrfNd->ReRead( rGrfName, rFltName, pGraphic ); 3308 m_rDoc.getIDocumentState().SetModified(); 3309 } 3310 3311 // Insert drawing object, which has to be already inserted in the DrawModel 3312 SwDrawFrameFormat* DocumentContentOperationsManager::InsertDrawObj( 3313 const SwPaM &rRg, 3314 SdrObject& rDrawObj, 3315 const SfxItemSet& rFlyAttrSet ) 3316 { 3317 SwDrawFrameFormat* pFormat = m_rDoc.MakeDrawFrameFormat( OUString(), m_rDoc.GetDfltFrameFormat() ); 3318 3319 const SwFormatAnchor* pAnchor = rFlyAttrSet.GetItemIfSet( RES_ANCHOR, false ); 3320 pFormat->SetFormatAttr( rFlyAttrSet ); 3321 3322 // Didn't set the Anchor yet? 3323 // DrawObjecte must never end up in the Header/Footer! 3324 RndStdIds eAnchorId = pAnchor != nullptr ? pAnchor->GetAnchorId() : pFormat->GetAnchor().GetAnchorId(); 3325 const bool bIsAtContent = (RndStdIds::FLY_AT_PAGE != eAnchorId); 3326 3327 const SwPosition* pChkPos = nullptr; 3328 if ( pAnchor == nullptr ) 3329 { 3330 pChkPos = rRg.GetPoint(); 3331 } 3332 else if ( bIsAtContent ) 3333 { 3334 pChkPos = 3335 pAnchor->GetContentAnchor() ? pAnchor->GetContentAnchor() : rRg.GetPoint(); 3336 } 3337 3338 // allow drawing objects in header/footer, but control objects aren't allowed in header/footer. 3339 if( pChkPos != nullptr 3340 && ::CheckControlLayer( &rDrawObj ) 3341 && m_rDoc.IsInHeaderFooter( pChkPos->GetNode() ) ) 3342 { 3343 // apply at-page anchor format 3344 eAnchorId = RndStdIds::FLY_AT_PAGE; 3345 pFormat->SetFormatAttr( SwFormatAnchor( eAnchorId ) ); 3346 } 3347 else if( pAnchor == nullptr 3348 || ( bIsAtContent 3349 && pAnchor->GetAnchorNode() == nullptr ) ) 3350 { 3351 // apply anchor format 3352 SwFormatAnchor aAnch( pAnchor != nullptr ? *pAnchor : pFormat->GetAnchor() ); 3353 eAnchorId = aAnch.GetAnchorId(); 3354 if ( eAnchorId == RndStdIds::FLY_AT_FLY ) 3355 { 3356 const SwStartNode* pStartNode = rRg.GetPointNode().FindFlyStartNode(); 3357 assert(pStartNode); 3358 SwPosition aPos(*pStartNode); 3359 aAnch.SetAnchor( &aPos ); 3360 } 3361 else 3362 { 3363 aAnch.SetAnchor( rRg.GetPoint() ); 3364 if ( eAnchorId == RndStdIds::FLY_AT_PAGE ) 3365 { 3366 eAnchorId = dynamic_cast<const SdrUnoObj*>( &rDrawObj) != nullptr ? RndStdIds::FLY_AS_CHAR : RndStdIds::FLY_AT_PARA; 3367 aAnch.SetType( eAnchorId ); 3368 } 3369 } 3370 pFormat->SetFormatAttr( aAnch ); 3371 } 3372 3373 // insert text attribute for as-character anchored drawing object 3374 if ( eAnchorId == RndStdIds::FLY_AS_CHAR ) 3375 { 3376 bool bAnchorAtPageAsFallback = true; 3377 const SwFormatAnchor& rDrawObjAnchorFormat = pFormat->GetAnchor(); 3378 if ( rDrawObjAnchorFormat.GetAnchorNode() != nullptr ) 3379 { 3380 SwTextNode* pAnchorTextNode = 3381 rDrawObjAnchorFormat.GetAnchorNode()->GetTextNode(); 3382 if ( pAnchorTextNode != nullptr ) 3383 { 3384 const sal_Int32 nStt = rDrawObjAnchorFormat.GetContentAnchor()->GetContentIndex(); 3385 SwFormatFlyCnt aFormat( pFormat ); 3386 pAnchorTextNode->InsertItem( aFormat, nStt, nStt ); 3387 bAnchorAtPageAsFallback = false; 3388 } 3389 } 3390 3391 if ( bAnchorAtPageAsFallback ) 3392 { 3393 OSL_ENSURE( false, "DocumentContentOperationsManager::InsertDrawObj(..) - missing content anchor for as-character anchored drawing object --> anchor at-page" ); 3394 pFormat->SetFormatAttr( SwFormatAnchor( RndStdIds::FLY_AT_PAGE ) ); 3395 } 3396 } 3397 3398 SwDrawContact* pContact = new SwDrawContact( pFormat, &rDrawObj ); 3399 3400 // Create Frames if necessary 3401 if( m_rDoc.getIDocumentLayoutAccess().GetCurrentViewShell() ) 3402 { 3403 // create layout representation 3404 pFormat->MakeFrames(); 3405 // #i42319# - follow-up of #i35635# 3406 // move object to visible layer 3407 // #i79391# 3408 if ( pContact->GetAnchorFrame() ) 3409 { 3410 pContact->MoveObjToVisibleLayer( &rDrawObj ); 3411 } 3412 } 3413 3414 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 3415 { 3416 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoInsLayFormat>(pFormat, SwNodeOffset(0), 0) ); 3417 } 3418 3419 m_rDoc.getIDocumentState().SetModified(); 3420 return pFormat; 3421 } 3422 3423 bool DocumentContentOperationsManager::SplitNode( const SwPosition &rPos, bool bChkTableStart ) 3424 { 3425 SwContentNode *pNode = rPos.GetNode().GetContentNode(); 3426 if(nullptr == pNode) 3427 return false; 3428 3429 { 3430 // BUG 26675: Send DataChanged before deleting, so that we notice which objects are in scope. 3431 // After that they can be before/after the position. 3432 SwDataChanged aTmp( m_rDoc, rPos ); 3433 } 3434 3435 SwUndoSplitNode* pUndo = nullptr; 3436 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 3437 { 3438 m_rDoc.GetIDocumentUndoRedo().ClearRedo(); 3439 // insert the Undo object (currently only for TextNode) 3440 if( pNode->IsTextNode() ) 3441 { 3442 pUndo = new SwUndoSplitNode( m_rDoc, rPos, bChkTableStart ); 3443 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndo)); 3444 } 3445 } 3446 3447 // Update the rsid of the old and the new node unless 3448 // the old node is split at the beginning or at the end 3449 SwTextNode *pTextNode = rPos.GetNode().GetTextNode(); 3450 const sal_Int32 nPos = rPos.GetContentIndex(); 3451 if( pTextNode && nPos && nPos != pTextNode->Len() ) 3452 { 3453 m_rDoc.UpdateParRsid( pTextNode ); 3454 } 3455 3456 //JP 28.01.97: Special case for SplitNode at table start: 3457 // If it is at the beginning of a Doc/Fly/Footer/... or right at after a table 3458 // then insert a paragraph before it. 3459 if( bChkTableStart && !rPos.GetContentIndex() && pNode->IsTextNode() ) 3460 { 3461 SwNodeOffset nPrevPos = rPos.GetNodeIndex() - 1; 3462 SwTableNode* pTableNd; 3463 const SwNode* pNd = m_rDoc.GetNodes()[ nPrevPos ]; 3464 if( pNd->IsStartNode() && 3465 SwTableBoxStartNode == static_cast<const SwStartNode*>(pNd)->GetStartNodeType() && 3466 nullptr != ( pTableNd = m_rDoc.GetNodes()[ --nPrevPos ]->GetTableNode() ) && 3467 ((( pNd = m_rDoc.GetNodes()[ --nPrevPos ])->IsStartNode() && 3468 SwTableBoxStartNode != static_cast<const SwStartNode*>(pNd)->GetStartNodeType() ) 3469 || ( pNd->IsEndNode() && pNd->StartOfSectionNode()->IsTableNode() ) 3470 || pNd->IsContentNode() )) 3471 { 3472 if( pNd->IsContentNode() ) 3473 { 3474 //JP 30.04.99 Bug 65660: 3475 // There are no page breaks outside of the normal body area, 3476 // so this is not a valid condition to insert a paragraph. 3477 if( nPrevPos < m_rDoc.GetNodes().GetEndOfExtras().GetIndex() ) 3478 pNd = nullptr; 3479 else 3480 { 3481 // Only if the table has page breaks! 3482 const SwFrameFormat* pFrameFormat = pTableNd->GetTable().GetFrameFormat(); 3483 if( SfxItemState::SET != pFrameFormat->GetItemState(RES_PAGEDESC, false) && 3484 SfxItemState::SET != pFrameFormat->GetItemState( RES_BREAK, false ) ) 3485 pNd = nullptr; 3486 } 3487 } 3488 3489 if( pNd ) 3490 { 3491 SwTextNode* pTextNd = m_rDoc.GetNodes().MakeTextNode( 3492 *pTableNd, 3493 m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_TEXT )); 3494 if( pTextNd ) 3495 { 3496 const_cast<SwPosition&>(rPos).Assign( pTableNd->GetIndex() - SwNodeOffset(1) ); 3497 3498 // only add page breaks/styles to the body area 3499 if( nPrevPos > m_rDoc.GetNodes().GetEndOfExtras().GetIndex() ) 3500 { 3501 SwFrameFormat* pFrameFormat = pTableNd->GetTable().GetFrameFormat(); 3502 const SfxPoolItem *pItem; 3503 if( SfxItemState::SET == pFrameFormat->GetItemState( RES_PAGEDESC, 3504 false, &pItem ) ) 3505 { 3506 pTextNd->SetAttr( *pItem ); 3507 pFrameFormat->ResetFormatAttr( RES_PAGEDESC ); 3508 } 3509 if( SfxItemState::SET == pFrameFormat->GetItemState( RES_BREAK, 3510 false, &pItem ) ) 3511 { 3512 pTextNd->SetAttr( *pItem ); 3513 pFrameFormat->ResetFormatAttr( RES_BREAK ); 3514 } 3515 } 3516 3517 if( pUndo ) 3518 pUndo->SetTableFlag(); 3519 m_rDoc.getIDocumentState().SetModified(); 3520 return true; 3521 } 3522 } 3523 } 3524 } 3525 3526 const std::shared_ptr<sw::mark::ContentIdxStore> pContentStore(sw::mark::ContentIdxStore::Create()); 3527 pContentStore->Save( m_rDoc, rPos.GetNodeIndex(), rPos.GetContentIndex(), true ); 3528 assert(pNode->IsTextNode()); 3529 std::function<void (SwTextNode *, sw::mark::RestoreMode, bool bAtStart)> restoreFunc( 3530 [&](SwTextNode *const, sw::mark::RestoreMode const eMode, bool const bAtStart) 3531 { 3532 if (!pContentStore->Empty()) 3533 { // move all bookmarks, TOXMarks, FlyAtCnt 3534 pContentStore->Restore(m_rDoc, rPos.GetNodeIndex()-SwNodeOffset(1), 0, true, bAtStart && (eMode & sw::mark::RestoreMode::Flys), eMode); 3535 } 3536 if (eMode & sw::mark::RestoreMode::NonFlys) 3537 { 3538 // To-Do - add 'SwExtraRedlineTable' also ? 3539 if (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || 3540 (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && 3541 !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty())) 3542 { 3543 SwPaM aPam( rPos ); 3544 aPam.SetMark(); 3545 aPam.Move( fnMoveBackward ); 3546 if (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn()) 3547 { 3548 m_rDoc.getIDocumentRedlineAccess().AppendRedline( 3549 new SwRangeRedline(RedlineType::Insert, aPam), true); 3550 } 3551 else 3552 { 3553 m_rDoc.getIDocumentRedlineAccess().SplitRedline(aPam); 3554 } 3555 } 3556 } 3557 }); 3558 pNode->GetTextNode()->SplitContentNode(rPos, &restoreFunc); 3559 3560 m_rDoc.getIDocumentState().SetModified(); 3561 return true; 3562 } 3563 3564 bool DocumentContentOperationsManager::AppendTextNode( SwPosition& rPos ) 3565 { 3566 // create new node before EndOfContent 3567 SwTextNode * pCurNode = rPos.GetNode().GetTextNode(); 3568 if( !pCurNode ) 3569 { 3570 // so then one can be created! 3571 SwNodeIndex aIdx( rPos.GetNode(), 1 ); 3572 pCurNode = m_rDoc.GetNodes().MakeTextNode( aIdx.GetNode(), 3573 m_rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool( RES_POOLCOLL_STANDARD )); 3574 } 3575 else 3576 pCurNode = pCurNode->AppendNode( rPos )->GetTextNode(); 3577 3578 rPos.Adjust(SwNodeOffset(1)); 3579 3580 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 3581 { 3582 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::make_unique<SwUndoInsert>( rPos.GetNode() ) ); 3583 } 3584 3585 // To-Do - add 'SwExtraRedlineTable' also ? 3586 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() || (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() )) 3587 { 3588 SwPaM aPam( rPos ); 3589 aPam.SetMark(); 3590 aPam.Move( fnMoveBackward ); 3591 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() ) 3592 m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Insert, aPam ), true); 3593 else 3594 m_rDoc.getIDocumentRedlineAccess().SplitRedline( aPam ); 3595 } 3596 3597 m_rDoc.getIDocumentState().SetModified(); 3598 return true; 3599 } 3600 3601 bool DocumentContentOperationsManager::ReplaceRange( SwPaM& rPam, const OUString& rStr, 3602 const bool bRegExReplace ) 3603 { 3604 // unfortunately replace works slightly differently from delete, 3605 // so we cannot use lcl_DoWithBreaks here... 3606 3607 std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks; 3608 3609 SwPaM aPam( *rPam.GetMark(), *rPam.GetPoint() ); 3610 aPam.Normalize(false); 3611 if (aPam.GetPoint()->GetNode() != aPam.GetMark()->GetNode()) 3612 { 3613 aPam.Move(fnMoveBackward); 3614 } 3615 OSL_ENSURE((aPam.GetPoint()->GetNode() == aPam.GetMark()->GetNode()), "invalid pam?"); 3616 3617 sw::CalcBreaks(Breaks, aPam); 3618 3619 while (!Breaks.empty() // skip over prefix of dummy chars 3620 && (aPam.GetMark()->GetNodeIndex() == Breaks.begin()->first) 3621 && (aPam.GetMark()->GetContentIndex() == Breaks.begin()->second)) 3622 { 3623 // skip! 3624 aPam.GetMark()->AdjustContent(+1); // always in bounds if Breaks valid 3625 Breaks.erase(Breaks.begin()); 3626 } 3627 *rPam.Start() = *aPam.GetMark(); // update start of original pam w/ prefix 3628 3629 if (Breaks.empty()) 3630 { 3631 // park aPam somewhere so it does not point to node that is deleted 3632 aPam.DeleteMark(); 3633 aPam.GetPoint()->Assign(m_rDoc.GetNodes().GetEndOfContent()); 3634 return ReplaceRangeImpl(rPam, rStr, bRegExReplace); // original pam! 3635 } 3636 3637 // Deletion must be split into several parts if the text node 3638 // contains a text attribute with end and with dummy character 3639 // and the selection does not contain the text attribute completely, 3640 // but overlaps its start (left), where the dummy character is. 3641 3642 bool bRet( true ); 3643 // iterate from end to start, to avoid invalidating the offsets! 3644 auto iter( Breaks.rbegin() ); 3645 SwNodeOffset nOffset(0); 3646 SwNodes const& rNodes(rPam.GetPoint()->GetNodes()); 3647 OSL_ENSURE(aPam.GetPoint() == aPam.End(), "wrong!"); 3648 SwPosition & rEnd( *aPam.End() ); 3649 SwPosition & rStart( *aPam.Start() ); 3650 3651 // set end of temp pam to original end (undo Move backward above) 3652 rEnd = *rPam.End(); 3653 // after first deletion, rEnd will point into the original text node again! 3654 3655 while (iter != Breaks.rend()) 3656 { 3657 rStart.Assign(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1); 3658 if (rStart < rEnd) // check if part is empty 3659 { 3660 bRet &= (m_rDoc.getIDocumentRedlineAccess().IsRedlineOn()) 3661 ? DeleteAndJoinWithRedlineImpl(aPam, SwDeleteFlags::Default) 3662 : DeleteAndJoinImpl(aPam, SwDeleteFlags::Default); 3663 nOffset = iter->first - rStart.GetNodeIndex(); // deleted fly nodes... 3664 } 3665 rEnd.Assign(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second); 3666 ++iter; 3667 } 3668 3669 rStart = *rPam.Start(); // set to original start 3670 assert(rStart < rEnd && "replace part empty!"); 3671 if (rStart < rEnd) // check if part is empty 3672 { 3673 bRet &= ReplaceRangeImpl(aPam, rStr, bRegExReplace); 3674 } 3675 3676 rPam = aPam; // update original pam (is this required?) 3677 3678 return bRet; 3679 } 3680 3681 bool DocumentContentOperationsManager::InsertPoolItem( 3682 const SwPaM &rRg, 3683 const SfxPoolItem &rHt, 3684 const SetAttrMode nFlags, 3685 SwRootFrame const*const pLayout, 3686 SwTextAttr **ppNewTextAttr) 3687 { 3688 SwDataChanged aTmp( rRg ); 3689 std::unique_ptr<SwUndoAttr> pUndoAttr; 3690 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 3691 { 3692 m_rDoc.GetIDocumentUndoRedo().ClearRedo(); 3693 pUndoAttr.reset(new SwUndoAttr( rRg, rHt, nFlags )); 3694 } 3695 3696 SfxItemSet aSet( m_rDoc.GetAttrPool(), rHt.Which(), rHt.Which() ); 3697 aSet.Put( rHt ); 3698 const bool bRet = lcl_InsAttr(m_rDoc, rRg, aSet, nFlags, pUndoAttr.get(), pLayout, ppNewTextAttr); 3699 3700 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 3701 { 3702 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr) ); 3703 } 3704 3705 if( bRet ) 3706 { 3707 m_rDoc.getIDocumentState().SetModified(); 3708 } 3709 return bRet; 3710 } 3711 3712 void DocumentContentOperationsManager::InsertItemSet ( const SwPaM &rRg, const SfxItemSet &rSet, 3713 const SetAttrMode nFlags, SwRootFrame const*const pLayout) 3714 { 3715 SwDataChanged aTmp( rRg ); 3716 std::unique_ptr<SwUndoAttr> pUndoAttr; 3717 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 3718 { 3719 m_rDoc.GetIDocumentUndoRedo().ClearRedo(); 3720 pUndoAttr.reset(new SwUndoAttr( rRg, rSet, nFlags )); 3721 } 3722 3723 bool bRet = lcl_InsAttr(m_rDoc, rRg, rSet, nFlags, pUndoAttr.get(), pLayout, /*ppNewTextAttr*/nullptr ); 3724 3725 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 3726 { 3727 m_rDoc.GetIDocumentUndoRedo().AppendUndo( std::move(pUndoAttr) ); 3728 } 3729 3730 if( bRet ) 3731 m_rDoc.getIDocumentState().SetModified(); 3732 } 3733 3734 void DocumentContentOperationsManager::RemoveLeadingWhiteSpace(const SwPosition & rPos ) 3735 { 3736 const SwTextNode* pTNd = rPos.GetNode().GetTextNode(); 3737 if ( !pTNd ) 3738 return; 3739 3740 const OUString& rText = pTNd->GetText(); 3741 sal_Int32 nIdx = 0; 3742 while (nIdx < rText.getLength()) 3743 { 3744 sal_Unicode const cCh = rText[nIdx]; 3745 if (('\t' != cCh) && (' ' != cCh)) 3746 { 3747 break; 3748 } 3749 ++nIdx; 3750 } 3751 3752 if ( nIdx > 0 ) 3753 { 3754 SwPaM aPam(rPos); 3755 aPam.GetPoint()->SetContent(0); 3756 aPam.SetMark(); 3757 aPam.GetMark()->SetContent(nIdx); 3758 DeleteRange( aPam ); 3759 } 3760 } 3761 3762 void DocumentContentOperationsManager::RemoveLeadingWhiteSpace(SwPaM& rPaM ) 3763 { 3764 for (SwPaM& rSel :rPaM.GetRingContainer()) 3765 { 3766 SwNodeOffset nStt = rSel.Start()->GetNodeIndex(); 3767 SwNodeOffset nEnd = rSel.End()->GetNodeIndex(); 3768 for (SwNodeOffset nPos = nStt; nPos<=nEnd; nPos++) 3769 RemoveLeadingWhiteSpace(SwPosition(rSel.GetBound().GetNodes(), nPos)); 3770 } 3771 } 3772 3773 // Copy method from SwDoc - "copy Flys in Flys" 3774 /// note: rRg/rInsPos *exclude* a partially selected start text node; 3775 /// pCopiedPaM *includes* a partially selected start text node 3776 void DocumentContentOperationsManager::CopyWithFlyInFly( 3777 const SwNodeRange& rRg, 3778 SwNode& rInsPos, 3779 const std::pair<const SwPaM&, const SwPosition&>* pCopiedPaM /*and real insert pos*/, 3780 const bool bMakeNewFrames, 3781 const bool bDelRedlines, 3782 const bool bCopyFlyAtFly, 3783 SwCopyFlags const flags) const 3784 { 3785 assert(!pCopiedPaM || pCopiedPaM->first.End()->GetNode() == rRg.aEnd.GetNode()); 3786 assert(!pCopiedPaM || pCopiedPaM->second.GetNode() <= rInsPos); 3787 3788 SwDoc& rDest = rInsPos.GetDoc(); 3789 SwNodeIndex aSavePos( rInsPos ); 3790 3791 if (rRg.aStart != rRg.aEnd) 3792 { 3793 bool bEndIsEqualEndPos = rInsPos == rRg.aEnd.GetNode(); 3794 --aSavePos; 3795 SaveRedlEndPosForRestore aRedlRest( rInsPos, 0 ); 3796 3797 // insert behind the already copied start node 3798 m_rDoc.GetNodes().CopyNodes( rRg, rInsPos, false, true ); 3799 aRedlRest.Restore(); 3800 3801 if (bEndIsEqualEndPos) 3802 { 3803 const_cast<SwNodeIndex&>(rRg.aEnd).Assign(aSavePos.GetNode(), +1); 3804 } 3805 } 3806 3807 // Also copy all bookmarks 3808 // guess this must be done before the DelDummyNodes below as that 3809 // deletes nodes so would mess up the index arithmetic 3810 // sw_fieldmarkhide: also needs to be done before making frames 3811 if (m_rDoc.getIDocumentMarkAccess()->getAllMarksCount()) 3812 { 3813 SwPaM aRgTmp( rRg.aStart, rRg.aEnd ); 3814 SwPosition targetPos(aSavePos, SwNodeOffset(rRg.aStart != rRg.aEnd ? +1 : 0)); 3815 if (pCopiedPaM && rRg.aStart != pCopiedPaM->first.Start()->GetNode()) 3816 { 3817 // there is 1 (partially selected, maybe) paragraph before 3818 assert(SwNodeIndex(rRg.aStart, -1) == pCopiedPaM->first.Start()->GetNode()); 3819 // only use the passed in target SwPosition if the source PaM point 3820 // is on a different node; if it was the same node then the target 3821 // position was likely moved along by the copy operation and now 3822 // points to the end of the range! 3823 targetPos = pCopiedPaM->second; 3824 } 3825 3826 sw::CopyBookmarks(pCopiedPaM ? pCopiedPaM->first : aRgTmp, targetPos, flags); 3827 } 3828 3829 if (rRg.aStart != rRg.aEnd) 3830 { 3831 ++aSavePos; 3832 } 3833 3834 #if OSL_DEBUG_LEVEL > 0 3835 { 3836 //JP 17.06.99: Bug 66973 - check count only if the selection is in 3837 // the same section or there's no section, because sections that are 3838 // not fully selected are not copied. 3839 const SwSectionNode* pSSectNd = rRg.aStart.GetNode().FindSectionNode(); 3840 SwNodeIndex aTmpI( rRg.aEnd, -1 ); 3841 const SwSectionNode* pESectNd = aTmpI.GetNode().FindSectionNode(); 3842 if( pSSectNd == pESectNd && 3843 !rRg.aStart.GetNode().IsSectionNode() && 3844 !aTmpI.GetNode().IsEndNode() ) 3845 { 3846 // If the range starts with a SwStartNode, it isn't copied 3847 SwNodeOffset offset( (rRg.aStart.GetNode().GetNodeType() != SwNodeType::Start) ? 1 : 0 ); 3848 OSL_ENSURE( rInsPos.GetIndex() - aSavePos.GetIndex() == 3849 rRg.aEnd.GetIndex() - rRg.aStart.GetIndex() - 1 + offset, 3850 "An insufficient number of nodes were copied!" ); 3851 } 3852 } 3853 #endif 3854 3855 { 3856 ::sw::UndoGuard const undoGuard(rDest.GetIDocumentUndoRedo()); 3857 // this must happen before lcl_DeleteRedlines() because it counts nodes 3858 CopyFlyInFlyImpl(rRg, pCopiedPaM ? &pCopiedPaM->first : nullptr, 3859 // see comment below regarding use of pCopiedPaM->second 3860 (pCopiedPaM && rRg.aStart != pCopiedPaM->first.Start()->GetNode()) 3861 ? pCopiedPaM->second.GetNode() 3862 : aSavePos.GetNode(), 3863 bCopyFlyAtFly, 3864 flags, 3865 false); 3866 } 3867 3868 SwNodeRange aCpyRange( aSavePos.GetNode(), rInsPos ); 3869 3870 if( bDelRedlines && ( RedlineFlags::DeleteRedlines & rDest.getIDocumentRedlineAccess().GetRedlineFlags() )) 3871 lcl_DeleteRedlines( rRg, aCpyRange ); 3872 3873 rDest.GetNodes().DelDummyNodes( aCpyRange ); 3874 3875 // tdf#159023 create layout frames after DelDummyNodes(): 3876 // InsertCnt_() does early return on the first SwPlaceholderNode 3877 if (rRg.aStart != rRg.aEnd) 3878 { 3879 --aSavePos; // restore temporarily... 3880 bool isRecreateEndNode(false); 3881 if (bMakeNewFrames) // tdf#130685 only after aRedlRest 3882 { // recreate from previous node (could be merged now) 3883 o3tl::sorted_vector<SwTextFrame*> frames; 3884 SwTextNode * pNode(aSavePos.GetNode().GetTextNode()); 3885 SwTextNode *const pEndNode(rInsPos.GetTextNode()); 3886 if (pEndNode) 3887 { 3888 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pEndNode); 3889 for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) 3890 { 3891 if (pFrame->getRootFrame()->HasMergedParas()) 3892 { 3893 frames.insert(pFrame); 3894 // tdf#135061 check if end node is merged to a preceding node 3895 if (pNode == nullptr && pFrame->GetMergedPara() 3896 && pFrame->GetMergedPara()->pFirstNode->GetIndex() < aSavePos.GetIndex()) 3897 { 3898 pNode = pFrame->GetMergedPara()->pFirstNode; 3899 } 3900 } 3901 } 3902 } 3903 if (pNode != nullptr) 3904 { 3905 sw::RecreateStartTextFrames(*pNode); 3906 if (!frames.empty()) 3907 { // tdf#132187 check if the end node needs new frames 3908 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pEndNode); 3909 for (SwTextFrame* pFrame = aIter.First(); pFrame; pFrame = aIter.Next()) 3910 { 3911 if (pFrame->getRootFrame()->HasMergedParas()) 3912 { 3913 auto const it(frames.find(pFrame)); 3914 if (it != frames.end()) 3915 { 3916 frames.erase(it); 3917 } 3918 } 3919 } 3920 if (!frames.empty()) // existing frame was deleted 3921 { // all layouts because MakeFrames recreates all layouts 3922 pEndNode->DelFrames(nullptr); 3923 isRecreateEndNode = true; 3924 } 3925 } 3926 } 3927 } 3928 bool const isAtStartOfSection(aSavePos.GetNode().IsStartNode()); 3929 ++aSavePos; 3930 if (bMakeNewFrames) 3931 { 3932 // it's possible that CheckParaRedlineMerge() deleted frames 3933 // on rInsPos so have to include it, but it must not be included 3934 // if it was the first node in the document so that MakeFrames() 3935 // will find the existing (wasn't deleted) frame on it 3936 SwNodeIndex const end(rInsPos, 3937 SwNodeOffset((!isRecreateEndNode || isAtStartOfSection) 3938 ? 0 : +1)); 3939 ::MakeFrames(&rDest, aSavePos.GetNode(), end.GetNode()); 3940 } 3941 } 3942 } 3943 3944 // note: for the redline Show/Hide this must be in sync with 3945 // SwRangeRedline::CopyToSection()/DelCopyOfSection()/MoveFromSection() 3946 void DocumentContentOperationsManager::CopyFlyInFlyImpl( 3947 const SwNodeRange& rRg, 3948 SwPaM const*const pCopiedPaM, 3949 SwNode& rStartIdx, 3950 const bool bCopyFlyAtFly, 3951 SwCopyFlags const flags, 3952 bool const bMakeNewFrames) const 3953 { 3954 assert(!pCopiedPaM || pCopiedPaM->End()->GetNode() == rRg.aEnd.GetNode()); 3955 3956 // First collect all Flys, sort them according to their ordering number, 3957 // and then only copy them. This maintains the ordering numbers (which are only 3958 // managed in the DrawModel). 3959 SwDoc& rDest = rStartIdx.GetDoc(); 3960 std::set< ZSortFly > aSet; 3961 const size_t nArrLen = m_rDoc.GetSpzFrameFormats()->size(); 3962 3963 SwTextBoxHelper::SavedLink aOldTextBoxes; 3964 SwTextBoxHelper::saveLinks(*m_rDoc.GetSpzFrameFormats(), aOldTextBoxes); 3965 3966 for ( size_t n = 0; n < nArrLen; ++n ) 3967 { 3968 SwFrameFormat* pFormat = (*m_rDoc.GetSpzFrameFormats())[n]; 3969 SwFormatAnchor const*const pAnchor = &pFormat->GetAnchor(); 3970 SwNode const*const pAnchorNode = pAnchor->GetAnchorNode(); 3971 if ( !pAnchorNode ) 3972 continue; 3973 bool bAdd = false; 3974 SwNodeOffset nSkipAfter = pAnchorNode->GetIndex(); 3975 SwNodeOffset nStart = rRg.aStart.GetIndex(); 3976 switch ( pAnchor->GetAnchorId() ) 3977 { 3978 case RndStdIds::FLY_AT_FLY: 3979 if(bCopyFlyAtFly) 3980 ++nSkipAfter; 3981 else if(m_rDoc.getIDocumentRedlineAccess().IsRedlineMove()) 3982 ++nStart; 3983 break; 3984 case RndStdIds::FLY_AT_PARA: 3985 { 3986 bAdd = IsSelectFrameAnchoredAtPara(*pAnchor->GetContentAnchor(), 3987 pCopiedPaM ? *pCopiedPaM->Start() : SwPosition(rRg.aStart), 3988 pCopiedPaM ? *pCopiedPaM->End() : SwPosition(rRg.aEnd), 3989 (flags & SwCopyFlags::IsMoveToFly) 3990 ? DelContentType::AllMask|DelContentType::WriterfilterHack 3991 : DelContentType::AllMask); 3992 } 3993 break; 3994 case RndStdIds::FLY_AT_CHAR: 3995 { 3996 bAdd = IsDestroyFrameAnchoredAtChar(*pAnchor->GetContentAnchor(), 3997 pCopiedPaM ? *pCopiedPaM->Start() : SwPosition(rRg.aStart), 3998 pCopiedPaM ? *pCopiedPaM->End() : SwPosition(rRg.aEnd), 3999 (flags & SwCopyFlags::IsMoveToFly) 4000 ? DelContentType::AllMask|DelContentType::WriterfilterHack 4001 : DelContentType::AllMask); 4002 } 4003 break; 4004 default: 4005 continue; 4006 } 4007 if (RndStdIds::FLY_AT_FLY == pAnchor->GetAnchorId()) 4008 { 4009 if (nStart > nSkipAfter) 4010 continue; 4011 if (*pAnchorNode > rRg.aEnd.GetNode()) 4012 continue; 4013 //frames at the last source node are not always copied: 4014 //- if the node is empty and is the last node of the document or a table cell 4015 // or a text frame then they have to be copied 4016 //- if the content index in this node is > 0 then paragraph and frame bound objects are copied 4017 //- to-character bound objects are copied if their index is <= nEndContentIndex 4018 if (*pAnchorNode < rRg.aEnd.GetNode()) 4019 bAdd = true; 4020 if (!bAdd && !m_rDoc.getIDocumentRedlineAccess().IsRedlineMove()) // fdo#40599: not for redline move 4021 { 4022 if (!bAdd) 4023 { 4024 // technically old code checked nContent of AT_FLY which is pointless 4025 bAdd = pCopiedPaM && 0 < pCopiedPaM->End()->GetContentIndex(); 4026 } 4027 } 4028 } 4029 if( bAdd ) 4030 { 4031 aSet.insert( ZSortFly( pFormat, pAnchor, nArrLen + aSet.size() )); 4032 } 4033 } 4034 4035 // Store all copied (and also the newly created) frames in another array. 4036 // They are stored as matching the originals, so that we will be later 4037 // able to build the chains accordingly. 4038 std::vector< SwFrameFormat* > aVecSwFrameFormat; 4039 std::set< ZSortFly >::const_iterator it=aSet.begin(); 4040 4041 while (it != aSet.end()) 4042 { 4043 // #i59964# 4044 // correct determination of new anchor position 4045 SwFormatAnchor aAnchor( *(*it).GetAnchor() ); 4046 assert( aAnchor.GetContentAnchor() != nullptr ); 4047 SwPosition newPos = *aAnchor.GetContentAnchor(); 4048 // for at-paragraph and at-character anchored objects the new anchor 4049 // position can *not* be determined by the difference of the current 4050 // anchor position to the start of the copied range, because not 4051 // complete selected sections in the copied range aren't copied - see 4052 // method <SwNodes::CopyNodes(..)>. 4053 // Thus, the new anchor position in the destination document is found 4054 // by counting the text nodes. 4055 if ((aAnchor.GetAnchorId() == RndStdIds::FLY_AT_PARA) || 4056 (aAnchor.GetAnchorId() == RndStdIds::FLY_AT_CHAR) ) 4057 { 4058 // First, determine number of anchor text node in the copied range. 4059 // Note: The anchor text node *have* to be inside the copied range. 4060 sal_uLong nAnchorTextNdNumInRange( 0 ); 4061 bool bAnchorTextNdFound( false ); 4062 // start at the first node for which flys are copied 4063 SwNodeIndex aIdx(pCopiedPaM ? pCopiedPaM->Start()->GetNode() : rRg.aStart.GetNode()); 4064 while ( !bAnchorTextNdFound && aIdx <= rRg.aEnd ) 4065 { 4066 if ( aIdx.GetNode().IsTextNode() ) 4067 { 4068 ++nAnchorTextNdNumInRange; 4069 bAnchorTextNdFound = *aAnchor.GetAnchorNode() == aIdx.GetNode(); 4070 } 4071 4072 ++aIdx; 4073 } 4074 4075 if ( !bAnchorTextNdFound ) 4076 { 4077 // This case can *not* happen, but to be robust take the first 4078 // text node in the destination document. 4079 OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - anchor text node in copied range not found" ); 4080 nAnchorTextNdNumInRange = 1; 4081 } 4082 // Second, search corresponding text node in destination document 4083 // by counting forward from start insert position <rStartIdx> the 4084 // determined number of text nodes. 4085 aIdx = rStartIdx; 4086 SwNodeIndex aAnchorNdIdx( rStartIdx ); 4087 const SwNode& aEndOfContentNd = 4088 aIdx.GetNode().GetNodes().GetEndOfContent(); 4089 while ( nAnchorTextNdNumInRange > 0 && 4090 aIdx.GetNode() != aEndOfContentNd ) 4091 { 4092 if ( aIdx.GetNode().IsTextNode() ) 4093 { 4094 --nAnchorTextNdNumInRange; 4095 aAnchorNdIdx = aIdx; 4096 } 4097 4098 ++aIdx; 4099 } 4100 if ( !aAnchorNdIdx.GetNode().IsTextNode() ) 4101 { 4102 // This case can *not* happen, but to be robust take the first 4103 // text node in the destination document. 4104 OSL_FAIL( "<SwDoc::_CopyFlyInFly(..)> - found anchor node index isn't a text node" ); 4105 aAnchorNdIdx = rStartIdx; 4106 while ( !aAnchorNdIdx.GetNode().IsTextNode() ) 4107 { 4108 ++aAnchorNdIdx; 4109 } 4110 } 4111 // apply found anchor text node as new anchor position 4112 newPos.Assign( aAnchorNdIdx ); 4113 } 4114 else 4115 { 4116 SwNodeOffset nOffset = newPos.GetNodeIndex() - rRg.aStart.GetIndex(); 4117 newPos.Assign( rStartIdx, nOffset ); 4118 } 4119 // Set the character bound Flys back at the original character 4120 if ((RndStdIds::FLY_AT_CHAR == aAnchor.GetAnchorId()) && 4121 newPos.GetNode().IsTextNode() ) 4122 { 4123 // only if pCopiedPaM: care about partially selected start node 4124 sal_Int32 const nContent = pCopiedPaM && pCopiedPaM->Start()->GetNode() == *aAnchor.GetAnchorNode() 4125 ? newPos.GetContentIndex() - pCopiedPaM->Start()->GetContentIndex() 4126 : newPos.GetContentIndex(); 4127 newPos.SetContent(nContent); 4128 } 4129 aAnchor.SetAnchor( &newPos ); 4130 4131 // Check recursion: if copying content inside the same frame, then don't copy the format. 4132 if( &rDest == &m_rDoc ) 4133 { 4134 const SwFormatContent& rContent = (*it).GetFormat()->GetContent(); 4135 const SwStartNode* pSNd; 4136 if( rContent.GetContentIdx() && 4137 nullptr != ( pSNd = rContent.GetContentIdx()->GetNode().GetStartNode() ) && 4138 pSNd->GetIndex() < rStartIdx.GetIndex() && 4139 rStartIdx.GetIndex() < pSNd->EndOfSectionIndex() ) 4140 { 4141 it = aSet.erase(it); 4142 continue; 4143 } 4144 } 4145 4146 // Ignore TextBoxes, they are already handled in 4147 // sw::DocumentLayoutManager::CopyLayoutFormat(). 4148 if (SwTextBoxHelper::isTextBox(it->GetFormat(), RES_FLYFRMFMT)) 4149 { 4150 it = aSet.erase(it); 4151 continue; 4152 } 4153 4154 // Copy the format and set the new anchor 4155 aVecSwFrameFormat.push_back( rDest.getIDocumentLayoutAccess().CopyLayoutFormat( *(*it).GetFormat(), 4156 aAnchor, false, bMakeNewFrames) ); 4157 ++it; 4158 } 4159 4160 // Rebuild as much as possible of all chains that are available in the original, 4161 OSL_ENSURE( aSet.size() == aVecSwFrameFormat.size(), "Missing new Flys" ); 4162 if ( aSet.size() != aVecSwFrameFormat.size() ) 4163 return; 4164 4165 size_t n = 0; 4166 for (const auto& rFlyN : aSet) 4167 { 4168 const SwFrameFormat *pFormatN = rFlyN.GetFormat(); 4169 const SwFormatChain &rChain = pFormatN->GetChain(); 4170 size_t k = 0; 4171 for (const auto& rFlyK : aSet) 4172 { 4173 const SwFrameFormat *pFormatK = rFlyK.GetFormat(); 4174 if ( rChain.GetPrev() == pFormatK ) 4175 { 4176 ::lcl_ChainFormats( static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[k]), 4177 static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[n]) ); 4178 } 4179 else if ( rChain.GetNext() == pFormatK ) 4180 { 4181 ::lcl_ChainFormats( static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[n]), 4182 static_cast< SwFlyFrameFormat* >(aVecSwFrameFormat[k]) ); 4183 } 4184 ++k; 4185 } 4186 ++n; 4187 } 4188 4189 // Re-create content property of draw formats, knowing how old shapes 4190 // were paired with old fly formats (aOldTextBoxes) and that aSet is 4191 // parallel with aVecSwFrameFormat. 4192 SwTextBoxHelper::restoreLinks(aSet, aVecSwFrameFormat, aOldTextBoxes); 4193 } 4194 4195 /* 4196 * Reset the text's hard formatting 4197 */ 4198 /** @params pArgs contains the document's ChrFormatTable 4199 * Is need for selections at the beginning/end and with no SSelection. 4200 */ 4201 bool DocumentContentOperationsManager::lcl_RstTextAttr( SwNode* pNd, void* pArgs ) 4202 { 4203 ParaRstFormat* pPara = static_cast<ParaRstFormat*>(pArgs); 4204 if (pPara->pLayout && pPara->pLayout->HasMergedParas() 4205 && pNd->GetRedlineMergeFlag() == SwNode::Merge::Hidden) 4206 { 4207 return true; // skip hidden, since new items aren't applied 4208 } 4209 SwTextNode * pTextNode = pNd->GetTextNode(); 4210 if( pTextNode && pTextNode->GetpSwpHints() ) 4211 { 4212 SwContentIndex aSt( pTextNode, 0 ); 4213 sal_Int32 nEnd = pTextNode->Len(); 4214 4215 if( &pPara->pSttNd->GetNode() == pTextNode && 4216 pPara->pSttNd->GetContentIndex() ) 4217 aSt = pPara->pSttNd->GetContentIndex(); 4218 4219 if( &pPara->pEndNd->GetNode() == pNd ) 4220 nEnd = pPara->pEndNd->GetContentIndex(); 4221 4222 if( pPara->pHistory ) 4223 { 4224 // Save all attributes for the Undo. 4225 SwRegHistory aRHst( *pTextNode, pPara->pHistory ); 4226 pTextNode->GetpSwpHints()->Register( &aRHst ); 4227 pTextNode->RstTextAttr( aSt.GetIndex(), nEnd - aSt.GetIndex(), pPara->nWhich, 4228 pPara->pDelSet, pPara->bInclRefToxMark, pPara->bExactRange ); 4229 if( pTextNode->GetpSwpHints() ) 4230 pTextNode->GetpSwpHints()->DeRegister(); 4231 } 4232 else 4233 pTextNode->RstTextAttr( aSt.GetIndex(), nEnd - aSt.GetIndex(), pPara->nWhich, 4234 pPara->pDelSet, pPara->bInclRefToxMark, pPara->bExactRange ); 4235 } 4236 return true; 4237 } 4238 4239 DocumentContentOperationsManager::~DocumentContentOperationsManager() 4240 { 4241 } 4242 //Private methods 4243 4244 bool DocumentContentOperationsManager::DeleteAndJoinWithRedlineImpl(SwPaM & rPam, SwDeleteFlags const flags) 4245 { 4246 assert(m_rDoc.getIDocumentRedlineAccess().IsRedlineOn()); 4247 4248 RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); 4249 4250 if (*rPam.GetPoint() == *rPam.GetMark()) 4251 { 4252 return false; // do not add empty redlines 4253 } 4254 4255 std::vector<std::unique_ptr<SwRangeRedline>> redlines; 4256 { 4257 auto pRedline(std::make_unique<SwRangeRedline>(RedlineType::Delete, rPam)); 4258 if (pRedline->HasValidRange()) 4259 { 4260 redlines.push_back(std::move(pRedline)); 4261 } 4262 else // sigh ... why is such a selection even possible... 4263 { // split it up so we get one SwUndoRedlineDelete per inserted RL 4264 redlines = GetAllValidRanges(std::move(pRedline)); 4265 } 4266 } 4267 4268 if (redlines.empty()) 4269 { 4270 return false; 4271 } 4272 4273 // tdf#54819 current redlining needs also modification of paragraph style and 4274 // attributes added to the same grouped Undo 4275 if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo()) 4276 m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr); 4277 4278 auto & rDMA(*m_rDoc.getIDocumentMarkAccess()); 4279 std::vector<std::unique_ptr<SwUndo>> MarkUndos; 4280 for (auto iter = rDMA.getAnnotationMarksBegin(); 4281 iter != rDMA.getAnnotationMarksEnd(); ) 4282 { 4283 // tdf#111524 remove annotation marks that have their field 4284 // characters deleted 4285 SwPosition const& rEndPos((**iter).GetMarkEnd()); 4286 if (*rPam.Start() < rEndPos && rEndPos <= *rPam.End()) 4287 { 4288 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 4289 { 4290 MarkUndos.emplace_back(std::make_unique<SwUndoDeleteBookmark>(**iter)); 4291 } 4292 // iter is into annotation mark vector so must be dereferenced! 4293 rDMA.deleteMark(&**iter); 4294 // this invalidates iter, have to start over... 4295 iter = rDMA.getAnnotationMarksBegin(); 4296 } 4297 else 4298 { // marks are sorted by start 4299 if (*rPam.End() < (**iter).GetMarkStart()) 4300 { 4301 break; 4302 } 4303 ++iter; 4304 } 4305 } 4306 4307 // tdf#119019 accept tracked paragraph formatting to do not hide new deletions 4308 if (*rPam.GetPoint() != *rPam.GetMark()) 4309 m_rDoc.getIDocumentRedlineAccess().AcceptRedlineParagraphFormatting(rPam); 4310 4311 std::vector<std::unique_ptr<SwUndoRedlineDelete>> undos; 4312 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 4313 { 4314 // this should no longer happen in calls from the UI but maybe via API 4315 // (randomTest and testTdf54819 triggers it) 4316 SAL_WARN_IF((eOld & RedlineFlags::ShowMask) != RedlineFlags::ShowMask, 4317 "sw.core", "redlines will be moved in DeleteAndJoin"); 4318 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( 4319 RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete); 4320 4321 for (std::unique_ptr<SwRangeRedline> & pRedline : redlines) 4322 { 4323 assert(pRedline->HasValidRange()); 4324 undos.emplace_back(std::make_unique<SwUndoRedlineDelete>( 4325 *pRedline, SwUndoId::DELETE, flags)); 4326 } 4327 const SwRewriter aRewriter = undos.front()->GetRewriter(); 4328 // can only group a single undo action 4329 if (MarkUndos.empty() && undos.size() == 1 4330 && m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo()) 4331 { 4332 SwUndo * const pLastUndo( m_rDoc.GetUndoManager().GetLastUndo() ); 4333 SwUndoRedlineDelete *const pUndoRedlineDel(dynamic_cast<SwUndoRedlineDelete*>(pLastUndo)); 4334 bool const bMerged = pUndoRedlineDel 4335 && pUndoRedlineDel->CanGrouping(*undos.front()); 4336 if (!bMerged) 4337 { 4338 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(undos.front())); 4339 } 4340 undos.clear(); // prevent unmatched EndUndo 4341 } 4342 else 4343 { 4344 m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::DELETE, &aRewriter); 4345 for (auto& it : MarkUndos) 4346 { 4347 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(it)); 4348 } 4349 for (auto & it : undos) 4350 { 4351 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::move(it)); 4352 } 4353 } 4354 } 4355 4356 for (std::unique_ptr<SwRangeRedline> & pRedline : redlines) 4357 { 4358 // note: 1. the pRedline can still be merged & deleted 4359 // 2. the impl. can even DeleteAndJoin the range => no plain PaM 4360 std::shared_ptr<SwUnoCursor> const pCursor(m_rDoc.CreateUnoCursor(*pRedline->GetMark())); 4361 pCursor->SetMark(); 4362 *pCursor->GetPoint() = *pRedline->GetPoint(); 4363 m_rDoc.getIDocumentRedlineAccess().AppendRedline(pRedline.release(), true); 4364 // sw_redlinehide: 2 reasons why this is needed: 4365 // 1. it's the first redline in node => RedlineDelText was sent but ignored 4366 // 2. redline spans multiple nodes => must merge text frames 4367 sw::UpdateFramesForAddDeleteRedline(m_rDoc, *pCursor); 4368 } 4369 m_rDoc.getIDocumentState().SetModified(); 4370 4371 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 4372 { 4373 if (!undos.empty()) 4374 { 4375 m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr); 4376 } 4377 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld ); 4378 } 4379 4380 if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo()) 4381 m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr); 4382 4383 return true; 4384 } 4385 4386 bool DocumentContentOperationsManager::DeleteAndJoinImpl(SwPaM & rPam, SwDeleteFlags const flags) 4387 { 4388 bool bJoinText, bJoinPrev; 4389 ::sw_GetJoinFlags( rPam, bJoinText, bJoinPrev ); 4390 4391 bool const bSuccess( DeleteRangeImpl(rPam, flags) ); 4392 if (!bSuccess) 4393 return false; 4394 4395 if( bJoinText ) 4396 { 4397 ::sw_JoinText( rPam, bJoinPrev ); 4398 } 4399 4400 if (!m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() 4401 && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty()) 4402 { 4403 m_rDoc.getIDocumentRedlineAccess().CompressRedlines(); 4404 } 4405 4406 return true; 4407 } 4408 4409 bool DocumentContentOperationsManager::DeleteRangeImpl(SwPaM & rPam, SwDeleteFlags const flags) 4410 { 4411 // Move all cursors out of the deleted range, but first copy the 4412 // passed PaM, because it could be a cursor that would be moved! 4413 SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() ); 4414 { 4415 SwPosition const pos(GetCorrPosition(aDelPam)); 4416 ::PaMCorrAbs(aDelPam, pos); 4417 } 4418 4419 bool const bSuccess( DeleteRangeImplImpl(aDelPam, flags) ); 4420 if (bSuccess) 4421 { // now copy position from temp copy to given PaM 4422 *rPam.GetPoint() = *aDelPam.GetPoint(); 4423 } 4424 4425 return bSuccess; 4426 } 4427 4428 bool DocumentContentOperationsManager::DeleteRangeImplImpl(SwPaM & rPam, SwDeleteFlags const flags) 4429 { 4430 auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition* 4431 4432 if (!rPam.HasMark() 4433 || (*pStt == *pEnd && !IsFlySelectedByCursor(m_rDoc, *pStt, *pEnd))) 4434 { 4435 return false; 4436 } 4437 4438 if( m_rDoc.GetAutoCorrExceptWord() ) 4439 { 4440 // if necessary the saved Word for the exception 4441 if( m_rDoc.GetAutoCorrExceptWord()->IsDeleted() || pStt->GetNode() != pEnd->GetNode() || 4442 pStt->GetContentIndex() + 1 != pEnd->GetContentIndex() || 4443 !m_rDoc.GetAutoCorrExceptWord()->CheckDelChar( *pStt )) 4444 { m_rDoc.DeleteAutoCorrExceptWord(); } 4445 } 4446 4447 { 4448 // Delete all empty TextHints at the Mark's position 4449 SwTextNode* pTextNd = rPam.GetMark()->GetNode().GetTextNode(); 4450 SwpHints* pHts; 4451 if( pTextNd && nullptr != ( pHts = pTextNd->GetpSwpHints()) && pHts->Count() ) 4452 { 4453 const sal_Int32 nMkCntPos = rPam.GetMark()->GetContentIndex(); 4454 for( size_t n = pHts->Count(); n; ) 4455 { 4456 const SwTextAttr* pAttr = pHts->Get( --n ); 4457 if( nMkCntPos > pAttr->GetStart() ) 4458 break; 4459 4460 const sal_Int32 *pEndIdx; 4461 if( nMkCntPos == pAttr->GetStart() && 4462 nullptr != (pEndIdx = pAttr->End()) && 4463 *pEndIdx == pAttr->GetStart() ) 4464 pTextNd->DestroyAttr( pHts->Cut( n ) ); 4465 } 4466 } 4467 } 4468 4469 { 4470 // Send DataChanged before deletion, so that we still know 4471 // which objects are in the range. 4472 // Afterwards they could be before/after the Position. 4473 SwDataChanged aTmp( rPam ); 4474 } 4475 4476 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 4477 { 4478 m_rDoc.GetIDocumentUndoRedo().ClearRedo(); 4479 bool bMerged(false); 4480 if (m_rDoc.GetIDocumentUndoRedo().DoesGroupUndo()) 4481 { 4482 SwUndo *const pLastUndo( m_rDoc.GetUndoManager().GetLastUndo() ); 4483 SwUndoDelete *const pUndoDelete( 4484 dynamic_cast<SwUndoDelete *>(pLastUndo) ); 4485 if (pUndoDelete) 4486 { 4487 bMerged = pUndoDelete->CanGrouping(m_rDoc, rPam); 4488 // if CanGrouping() returns true it's already merged 4489 } 4490 } 4491 if (!bMerged) 4492 { 4493 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::make_unique<SwUndoDelete>(rPam, flags)); 4494 } 4495 4496 m_rDoc.getIDocumentState().SetModified(); 4497 4498 return true; 4499 } 4500 4501 if( !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ) 4502 m_rDoc.getIDocumentRedlineAccess().DeleteRedline( rPam, true, RedlineType::Any ); 4503 4504 // Delete and move all "Flys at the paragraph", which are within the Selection 4505 if (!(flags & SwDeleteFlags::ArtificialSelection)) 4506 { 4507 DelFlyInRange(rPam.GetMark()->GetNode(), rPam.GetPoint()->GetNode(), 4508 rPam.GetMark()->GetContentIndex(), rPam.GetPoint()->GetContentIndex()); 4509 } 4510 DelBookmarks( 4511 pStt->GetNode(), 4512 pEnd->GetNode(), 4513 nullptr, 4514 pStt->GetContentIndex(), 4515 pEnd->GetContentIndex(), 4516 bool(flags & SwDeleteFlags::ArtificialSelection)); 4517 4518 SwNodeIndex aSttIdx( pStt->GetNode() ); 4519 SwContentNode * pCNd = aSttIdx.GetNode().GetContentNode(); 4520 4521 do { // middle checked loop! 4522 if( pCNd ) 4523 { 4524 SwTextNode * pStartTextNode( pCNd->GetTextNode() ); 4525 if ( pStartTextNode ) 4526 { 4527 // now move the Content to the new Node 4528 bool bOneNd = pStt->GetNode() == pEnd->GetNode(); 4529 const sal_Int32 nLen = ( bOneNd ? pEnd->GetContentIndex() 4530 : pCNd->Len() ) 4531 - pStt->GetContentIndex(); 4532 4533 // Don't call again, if already empty 4534 if( nLen ) 4535 { 4536 pStartTextNode->EraseText( *pStt, nLen ); 4537 4538 if( !pStartTextNode->Len() ) 4539 { 4540 // METADATA: remove reference if empty (consider node deleted) 4541 pStartTextNode->RemoveMetadataReference(); 4542 } 4543 } 4544 4545 if( bOneNd ) // that's it 4546 break; 4547 4548 ++aSttIdx; 4549 } 4550 else 4551 { 4552 // So that there are no indices left registered when deleted, 4553 // we remove a SwPaM from the Content here. 4554 pStt->nContent.Assign( nullptr, 0 ); 4555 } 4556 } 4557 4558 pCNd = pEnd->GetNode().GetContentNode(); 4559 if( pCNd ) 4560 { 4561 SwTextNode * pEndTextNode( pCNd->GetTextNode() ); 4562 if( pEndTextNode ) 4563 { 4564 // if already empty, don't call again 4565 if( pEnd->GetContentIndex() ) 4566 { 4567 SwContentIndex aIdx( pCNd, 0 ); 4568 pEndTextNode->EraseText( aIdx, pEnd->GetContentIndex() ); 4569 4570 if( !pEndTextNode->Len() ) 4571 { 4572 // METADATA: remove reference if empty (consider node deleted) 4573 pEndTextNode->RemoveMetadataReference(); 4574 } 4575 } 4576 } 4577 else 4578 { 4579 // So that there are no indices left registered when deleted, 4580 // we remove a SwPaM from the Content here. 4581 pEnd->nContent.Assign( nullptr, 0 ); 4582 } 4583 } 4584 4585 // if the end is not a content node, delete it as well 4586 SwNodeOffset nEnd = pEnd->GetNodeIndex(); 4587 if( pCNd == nullptr ) 4588 nEnd++; 4589 4590 if( aSttIdx != nEnd ) 4591 { 4592 // tdf#134436 delete section nodes like SwUndoDelete::SwUndoDelete 4593 SwNode *pTmpNd; 4594 while (pEnd == rPam.GetPoint() 4595 && nEnd + SwNodeOffset(2) < m_rDoc.GetNodes().Count() 4596 && (pTmpNd = m_rDoc.GetNodes()[nEnd + 1])->IsEndNode() 4597 && pTmpNd->StartOfSectionNode()->IsSectionNode() 4598 && aSttIdx <= pTmpNd->StartOfSectionNode()->GetIndex()) 4599 { 4600 SwNodeRange range(*pTmpNd->StartOfSectionNode(), *pTmpNd); 4601 m_rDoc.GetNodes().SectionUp(&range); 4602 --nEnd; // account for deleted start node 4603 } 4604 4605 // delete the Nodes from the NodesArray 4606 m_rDoc.GetNodes().Delete( aSttIdx, nEnd - aSttIdx.GetIndex() ); 4607 } 4608 4609 // If the Node that contained the Cursor has been deleted, 4610 // the Content has to be assigned to the current Content. 4611 if (pStt->GetNode().GetContentNode()) 4612 pStt->SetContent( pStt->GetContentIndex() ); 4613 4614 // If we deleted across Node boundaries we have to correct the PaM, 4615 // because they are in different Nodes now. 4616 // Also, the Selection is revoked. 4617 *pEnd = *pStt; 4618 rPam.DeleteMark(); 4619 4620 } while( false ); 4621 4622 m_rDoc.getIDocumentState().SetModified(); 4623 4624 return true; 4625 } 4626 4627 // It's possible to call Replace with a PaM that spans 2 paragraphs: 4628 // search with regex for "$", then replace _all_ 4629 bool DocumentContentOperationsManager::ReplaceRangeImpl( SwPaM& rPam, const OUString& rStr, 4630 const bool bRegExReplace ) 4631 { 4632 if (!rPam.HasMark()) 4633 return false; 4634 4635 bool bJoinText, bJoinPrev; 4636 ::sw_GetJoinFlags( rPam, bJoinText, bJoinPrev ); 4637 4638 { 4639 // Create a copy of the Cursor in order to move all Pams from 4640 // the other views out of the deletion range. 4641 // Except for itself! 4642 SwPaM aDelPam( *rPam.GetMark(), *rPam.GetPoint() ); 4643 ::PaMCorrAbs( aDelPam, *aDelPam.End() ); 4644 4645 auto [pStt, pEnd] = aDelPam.StartEnd(); // SwPosition* 4646 bool bOneNode = pStt->GetNode() == pEnd->GetNode(); 4647 4648 // Own Undo? 4649 OUString sRepl( rStr ); 4650 SwTextNode* pTextNd = pStt->GetNode().GetTextNode(); 4651 sal_Int32 nStt = pStt->GetContentIndex(); 4652 sal_Int32 nEnd; 4653 4654 SwDataChanged aTmp( aDelPam ); 4655 4656 if( m_rDoc.getIDocumentRedlineAccess().IsRedlineOn() ) 4657 { 4658 RedlineFlags eOld = m_rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); 4659 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 4660 { 4661 // this should no longer happen in calls from the UI but maybe via API 4662 SAL_WARN_IF((eOld & RedlineFlags::ShowMask) != RedlineFlags::ShowMask, 4663 "sw.core", "redlines will be moved in ReplaceRange"); 4664 4665 m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::EMPTY, nullptr); 4666 4667 // If any Redline will change (split!) the node 4668 const ::sw::mark::IMark* pBkmk = 4669 m_rDoc.getIDocumentMarkAccess()->makeMark( aDelPam, 4670 OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK, 4671 ::sw::mark::InsertMode::New); 4672 4673 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( 4674 RedlineFlags::On | RedlineFlags::ShowInsert | RedlineFlags::ShowDelete ); 4675 4676 *aDelPam.GetPoint() = pBkmk->GetMarkPos(); 4677 if(pBkmk->IsExpanded()) 4678 *aDelPam.GetMark() = pBkmk->GetOtherMarkPos(); 4679 m_rDoc.getIDocumentMarkAccess()->deleteMark(pBkmk); 4680 pStt = aDelPam.Start(); 4681 pTextNd = pStt->GetNode().GetTextNode(); 4682 nStt = pStt->GetContentIndex(); 4683 } 4684 4685 if( !sRepl.isEmpty() ) 4686 { 4687 // Apply the first character's attributes to the ReplaceText 4688 SfxItemSetFixed 4689 <RES_CHRATR_BEGIN, RES_TXTATR_WITHEND_END - 1, 4690 RES_UNKNOWNATR_BEGIN, RES_UNKNOWNATR_END-1> aSet( m_rDoc.GetAttrPool() ); 4691 pTextNd->GetParaAttr( aSet, nStt+1, nStt+1 ); 4692 4693 aSet.ClearItem( RES_TXTATR_REFMARK ); 4694 aSet.ClearItem( RES_TXTATR_TOXMARK ); 4695 aSet.ClearItem( RES_TXTATR_CJK_RUBY ); 4696 aSet.ClearItem( RES_TXTATR_INETFMT ); 4697 aSet.ClearItem( RES_TXTATR_META ); 4698 aSet.ClearItem( RES_TXTATR_METAFIELD ); 4699 4700 if( aDelPam.GetPoint() != aDelPam.End() ) 4701 aDelPam.Exchange(); 4702 4703 // Remember the End 4704 SwNodeIndex aPtNd( aDelPam.GetPoint()->GetNode(), -1 ); 4705 const sal_Int32 nPtCnt = aDelPam.GetPoint()->GetContentIndex(); 4706 4707 bool bFirst = true; 4708 OUString sIns; 4709 while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) ) 4710 { 4711 InsertString( aDelPam, sIns ); 4712 if( bFirst ) 4713 { 4714 SwNodeIndex aMkNd( aDelPam.GetMark()->GetNode(), -1 ); 4715 const sal_Int32 nMkCnt = aDelPam.GetMark()->GetContentIndex(); 4716 4717 SplitNode( *aDelPam.GetPoint(), false ); 4718 4719 ++aMkNd; 4720 aDelPam.GetMark()->Assign( aMkNd, nMkCnt ); 4721 bFirst = false; 4722 } 4723 else 4724 SplitNode( *aDelPam.GetPoint(), false ); 4725 } 4726 if( !sIns.isEmpty() ) 4727 { 4728 InsertString( aDelPam, sIns ); 4729 } 4730 4731 SwPaM aTmpRange( *aDelPam.GetPoint() ); 4732 aTmpRange.SetMark(); 4733 4734 ++aPtNd; 4735 aDelPam.GetPoint()->Assign(aPtNd, nPtCnt); 4736 *aTmpRange.GetMark() = *aDelPam.GetPoint(); 4737 4738 m_rDoc.RstTextAttrs( aTmpRange ); 4739 InsertItemSet( aTmpRange, aSet ); 4740 } 4741 4742 // tdf#139982: Appending the redline may immediately delete flys 4743 // anchored in the previous text if it's inside an insert redline. 4744 // Also flys will be deleted if the redline is accepted. Move them 4745 // to the position between the previous text and the new text, 4746 // there the chance of surviving both accept and reject is best. 4747 SaveFlyArr flys; 4748 SaveFlyInRange(aDelPam, *aDelPam.End(), flys, false); 4749 4750 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 4751 { 4752 m_rDoc.GetIDocumentUndoRedo().AppendUndo( 4753 std::make_unique<SwUndoRedlineDelete>( aDelPam, SwUndoId::REPLACE )); 4754 } 4755 // add redline similar to DeleteAndJoinWithRedlineImpl() 4756 std::shared_ptr<SwUnoCursor> const pCursor(m_rDoc.CreateUnoCursor(*aDelPam.GetMark())); 4757 pCursor->SetMark(); 4758 *pCursor->GetPoint() = *aDelPam.GetPoint(); 4759 m_rDoc.getIDocumentRedlineAccess().AppendRedline( new SwRangeRedline( RedlineType::Delete, aDelPam ), true); 4760 RestFlyInRange(flys, *aDelPam.End(), &aDelPam.End()->GetNode(), true); 4761 sw::UpdateFramesForAddDeleteRedline(m_rDoc, *pCursor); 4762 4763 *rPam.GetMark() = *aDelPam.GetMark(); 4764 if (m_rDoc.GetIDocumentUndoRedo().DoesUndo()) 4765 { 4766 *aDelPam.GetPoint() = *rPam.GetPoint(); 4767 m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::EMPTY, nullptr); 4768 4769 // If any Redline will change (split!) the node 4770 const ::sw::mark::IMark* pBkmk = 4771 m_rDoc.getIDocumentMarkAccess()->makeMark( aDelPam, 4772 OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK, 4773 ::sw::mark::InsertMode::New); 4774 4775 aDelPam.GetPoint()->Assign( SwNodeOffset(0) ); 4776 aDelPam.GetMark()->Assign( SwNodeOffset(0) ); 4777 rPam.GetPoint()->Assign( SwNodeOffset(0) ); 4778 *rPam.GetMark() = *rPam.GetPoint(); 4779 m_rDoc.getIDocumentRedlineAccess().SetRedlineFlags( eOld ); 4780 4781 *rPam.GetPoint() = pBkmk->GetMarkPos(); 4782 *rPam.GetMark() = pBkmk->IsExpanded() ? pBkmk->GetOtherMarkPos() : pBkmk->GetMarkPos(); 4783 4784 m_rDoc.getIDocumentMarkAccess()->deleteMark(pBkmk); 4785 } 4786 bJoinText = false; 4787 } 4788 else 4789 { 4790 assert((pStt->GetNode() == pEnd->GetNode() || 4791 ( pStt->GetNodeIndex() + 1 == pEnd->GetNodeIndex() && 4792 !pEnd->GetContentIndex() )) && 4793 "invalid range: Point and Mark on different nodes" ); 4794 4795 if( !m_rDoc.getIDocumentRedlineAccess().IsIgnoreRedline() && !m_rDoc.getIDocumentRedlineAccess().GetRedlineTable().empty() ) 4796 m_rDoc.getIDocumentRedlineAccess().DeleteRedline( aDelPam, true, RedlineType::Any ); 4797 4798 SwUndoReplace* pUndoRpl = nullptr; 4799 bool const bDoesUndo = m_rDoc.GetIDocumentUndoRedo().DoesUndo(); 4800 if (bDoesUndo) 4801 { 4802 pUndoRpl = new SwUndoReplace(aDelPam, sRepl, bRegExReplace); 4803 m_rDoc.GetIDocumentUndoRedo().AppendUndo(std::unique_ptr<SwUndo>(pUndoRpl)); 4804 } 4805 ::sw::UndoGuard const undoGuard(m_rDoc.GetIDocumentUndoRedo()); 4806 4807 if( aDelPam.GetPoint() != pStt ) 4808 aDelPam.Exchange(); 4809 4810 SwNodeIndex aPtNd( pStt->GetNode(), -1 ); 4811 const sal_Int32 nPtCnt = pStt->GetContentIndex(); 4812 4813 // Set the values again, if Frames or footnotes on the Text have been removed. 4814 nStt = nPtCnt; 4815 nEnd = bOneNode ? pEnd->GetContentIndex() 4816 : pTextNd->GetText().getLength(); 4817 4818 bool bFirst = true; 4819 OUString sIns; 4820 while ( lcl_GetTokenToParaBreak( sRepl, sIns, bRegExReplace ) ) 4821 { 4822 if (!bFirst || nStt == pTextNd->GetText().getLength()) 4823 { 4824 InsertString( aDelPam, sIns ); 4825 } 4826 else if( nStt < nEnd || !sIns.isEmpty() ) 4827 { 4828 pTextNd->ReplaceText( *pStt, nEnd - nStt, sIns ); 4829 } 4830 SplitNode( *pStt, false); 4831 bFirst = false; 4832 } 4833 4834 if( bFirst || !sIns.isEmpty() ) 4835 { 4836 if (!bFirst || nStt == pTextNd->GetText().getLength()) 4837 { 4838 InsertString( aDelPam, sIns ); 4839 } 4840 else if( nStt < nEnd || !sIns.isEmpty() ) 4841 { 4842 pTextNd->ReplaceText( *pStt, nEnd - nStt, sIns ); 4843 } 4844 } 4845 4846 *rPam.GetPoint() = *aDelPam.GetMark(); 4847 ++aPtNd; 4848 rPam.GetMark()->Assign( aPtNd, nPtCnt ); 4849 4850 if (bJoinText) 4851 { 4852 assert(rPam.GetPoint() == rPam.End()); 4853 // move so that SetEnd remembers position after sw_JoinText 4854 rPam.Move(fnMoveBackward); 4855 } 4856 else if (aDelPam.GetPoint() == pStt) // backward selection? 4857 { 4858 assert(*rPam.GetMark() <= *rPam.GetPoint()); 4859 rPam.Exchange(); // swap so that rPam is backwards 4860 } 4861 4862 if( pUndoRpl ) 4863 { 4864 pUndoRpl->SetEnd(rPam); 4865 } 4866 } 4867 } 4868 4869 bool bRet(true); 4870 if (bJoinText) 4871 { 4872 bRet = ::sw_JoinText(rPam, bJoinPrev); 4873 } 4874 4875 m_rDoc.getIDocumentState().SetModified(); 4876 return bRet; 4877 } 4878 4879 SwFlyFrameFormat* DocumentContentOperationsManager::InsNoTextNode( const SwPosition& rPos, SwNoTextNode* pNode, 4880 const SfxItemSet* pFlyAttrSet, 4881 const SfxItemSet* pGrfAttrSet, 4882 SwFrameFormat* pFrameFormat) 4883 { 4884 SwFlyFrameFormat *pFormat = nullptr; 4885 if( pNode ) 4886 { 4887 pFormat = m_rDoc.MakeFlySection_( rPos, *pNode, RndStdIds::FLY_AT_PARA, 4888 pFlyAttrSet, pFrameFormat ); 4889 if( pGrfAttrSet ) 4890 pNode->SetAttr( *pGrfAttrSet ); 4891 } 4892 return pFormat; 4893 } 4894 4895 #define NUMRULE_STATE \ 4896 std::shared_ptr<SwNumRuleItem> aNumRuleItemHolderIfSet; \ 4897 std::shared_ptr<SfxStringItem> aListIdItemHolderIfSet; \ 4898 4899 #define PUSH_NUMRULE_STATE \ 4900 lcl_PushNumruleState( aNumRuleItemHolderIfSet, aListIdItemHolderIfSet, pDestTextNd ); 4901 4902 #define POP_NUMRULE_STATE \ 4903 lcl_PopNumruleState( aNumRuleItemHolderIfSet, aListIdItemHolderIfSet, pDestTextNd, rPam ); 4904 4905 static void lcl_PushNumruleState( 4906 std::shared_ptr<SwNumRuleItem>& aNumRuleItemHolderIfSet, 4907 std::shared_ptr<SfxStringItem>& aListIdItemHolderIfSet, 4908 const SwTextNode *pDestTextNd ) 4909 { 4910 // Safe numrule item at destination. 4911 // #i86492# - Safe also <ListId> item of destination. 4912 const SfxItemSet * pAttrSet = pDestTextNd->GetpSwAttrSet(); 4913 if (pAttrSet == nullptr) 4914 return; 4915 4916 if (const SwNumRuleItem* pItem = pAttrSet->GetItemIfSet(RES_PARATR_NUMRULE, false)) 4917 { 4918 aNumRuleItemHolderIfSet.reset(pItem->Clone()); 4919 } 4920 4921 if (const SfxStringItem* pItem = pAttrSet->GetItemIfSet(RES_PARATR_LIST_ID, false)) 4922 { 4923 aListIdItemHolderIfSet.reset(pItem->Clone()); 4924 } 4925 } 4926 4927 static void lcl_PopNumruleState( 4928 const std::shared_ptr<SwNumRuleItem>& aNumRuleItemHolderIfSet, 4929 const std::shared_ptr<SfxStringItem>& aListIdItemHolderIfSet, 4930 SwTextNode *pDestTextNd, const SwPaM& rPam ) 4931 { 4932 /* If only a part of one paragraph is copied 4933 restore the numrule at the destination. */ 4934 // #i86492# - restore also <ListId> item 4935 if ( lcl_MarksWholeNode(rPam) ) 4936 return; 4937 4938 if (aNumRuleItemHolderIfSet) 4939 { 4940 pDestTextNd->SetAttr(*aNumRuleItemHolderIfSet); 4941 } 4942 else 4943 { 4944 pDestTextNd->ResetAttr(RES_PARATR_NUMRULE); 4945 } 4946 4947 if (aListIdItemHolderIfSet) 4948 { 4949 pDestTextNd->SetAttr(*aListIdItemHolderIfSet); 4950 } 4951 else 4952 { 4953 pDestTextNd->ResetAttr(RES_PARATR_LIST_ID); 4954 } 4955 } 4956 4957 bool DocumentContentOperationsManager::CopyImpl(SwPaM& rPam, SwPosition& rPos, 4958 SwCopyFlags const flags, 4959 SwPaM *const pCopyRange) const 4960 { 4961 std::vector<std::pair<SwNodeOffset, sal_Int32>> Breaks; 4962 4963 sw::CalcBreaks(Breaks, rPam, true); 4964 4965 if (Breaks.empty()) 4966 { 4967 return CopyImplImpl(rPam, rPos, flags, pCopyRange); 4968 } 4969 4970 SwPosition const & rSelectionEnd( *rPam.End() ); 4971 4972 bool bRet(true); 4973 bool bFirst(true); 4974 // iterate from end to start, ... don't think it's necessary here? 4975 auto iter( Breaks.rbegin() ); 4976 SwNodeOffset nOffset(0); 4977 SwNodes const& rNodes(rPam.GetPoint()->GetNodes()); 4978 SwPaM aPam( rSelectionEnd, rSelectionEnd ); // end node! 4979 SwPosition & rEnd( *aPam.End() ); 4980 SwPosition & rStart( *aPam.Start() ); 4981 SwPaM copyRange(rPos, rPos); 4982 4983 while (iter != Breaks.rend()) 4984 { 4985 rStart.Assign(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second + 1); 4986 if (rStart < rEnd) // check if part is empty 4987 { 4988 // pass in copyRange member as rPos; should work ... 4989 bRet &= CopyImplImpl(aPam, *copyRange.Start(), flags & ~SwCopyFlags::IsMoveToFly, ©Range); 4990 nOffset = iter->first - rStart.GetNodeIndex(); // fly nodes... 4991 if (pCopyRange) 4992 { 4993 if (bFirst) 4994 { 4995 pCopyRange->SetMark(); 4996 *pCopyRange->GetMark() = *copyRange.End(); 4997 } 4998 *pCopyRange->GetPoint() = *copyRange.Start(); 4999 } 5000 bFirst = false; 5001 } 5002 rEnd.Assign(*rNodes[iter->first - nOffset]->GetTextNode(), iter->second); 5003 ++iter; 5004 } 5005 5006 rStart = *rPam.Start(); // set to original start 5007 if (rStart < rEnd) // check if part is empty 5008 { 5009 bRet &= CopyImplImpl(aPam, *copyRange.Start(), flags & ~SwCopyFlags::IsMoveToFly, ©Range); 5010 if (pCopyRange) 5011 { 5012 if (bFirst) 5013 { 5014 pCopyRange->SetMark(); 5015 *pCopyRange->GetMark() = *copyRange.End(); 5016 } 5017 *pCopyRange->GetPoint() = *copyRange.Start(); 5018 } 5019 } 5020 5021 return bRet; 5022 } 5023 5024 bool DocumentContentOperationsManager::CopyImplImpl(SwPaM& rPam, SwPosition& rPos, 5025 SwCopyFlags const flags, 5026 SwPaM *const pCpyRange) const 5027 { 5028 SwDoc& rDoc = rPos.GetNode().GetDoc(); 5029 const bool bColumnSel = rDoc.IsClipBoard() && rDoc.IsColumnSelection(); 5030 5031 auto [pStt, pEnd] = rPam.StartEnd(); // SwPosition* 5032 5033 // Catch when there's no copy to do. 5034 if (!rPam.HasMark() || (IsEmptyRange(*pStt, *pEnd, flags) && !bColumnSel) || 5035 //JP 29.6.2001: 88963 - don't copy if inspos is in region of start to end 5036 //JP 15.11.2001: don't test inclusive the end, ever exclusive 5037 ( &rDoc == &m_rDoc && *pStt <= rPos && rPos < *pEnd )) 5038 { 5039 return false; 5040 } 5041 5042 const bool bEndEqualIns = &rDoc == &m_rDoc && rPos == *pEnd; 5043 5044 // If Undo is enabled, create the UndoCopy object 5045 SwUndoCpyDoc* pUndo = nullptr; 5046 // lcl_DeleteRedlines may delete the start or end node of the cursor when 5047 // removing the redlines so use cursor that is corrected by PaMCorrAbs 5048 std::shared_ptr<SwUnoCursor> const pCopyPam(rDoc.CreateUnoCursor(rPos)); 5049 5050 SwTableNumFormatMerge aTNFM( m_rDoc, rDoc ); 5051 std::optional<std::vector<SwFrameFormat*>> pFlys; 5052 std::vector<SwFrameFormat*> const* pFlysAtInsPos; 5053 5054 if (rDoc.GetIDocumentUndoRedo().DoesUndo()) 5055 { 5056 pUndo = new SwUndoCpyDoc(*pCopyPam); 5057 pFlysAtInsPos = pUndo->GetFlysAnchoredAt(); 5058 } 5059 else 5060 { 5061 pFlys = sw::GetFlysAnchoredAt(rDoc, rPos.GetNodeIndex()); 5062 pFlysAtInsPos = pFlys ? &*pFlys : nullptr; 5063 } 5064 5065 RedlineFlags eOld = rDoc.getIDocumentRedlineAccess().GetRedlineFlags(); 5066 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern(eOld | RedlineFlags::Ignore); 5067 5068 // Move the PaM one node back from the insert position, so that 5069 // the position doesn't get moved 5070 pCopyPam->SetMark(); 5071 bool bCanMoveBack = pCopyPam->Move(fnMoveBackward, GoInContent); 5072 // If the position was shifted from more than one node, an end node has been skipped 5073 bool bAfterTable = false; 5074 if ((rPos.GetNodeIndex() - pCopyPam->GetPoint()->GetNodeIndex()) > SwNodeOffset(1)) 5075 { 5076 // First go back to the original place 5077 *(pCopyPam->GetPoint()) = rPos; 5078 5079 bCanMoveBack = false; 5080 bAfterTable = true; 5081 } 5082 if( !bCanMoveBack ) 5083 { 5084 pCopyPam->GetPoint()->Adjust(SwNodeOffset(-1)); 5085 assert(pCopyPam->GetPoint()->GetContentIndex() == 0); 5086 } 5087 5088 SwNodeRange aRg( pStt->GetNode(), pEnd->GetNode() ); 5089 SwNodeIndex aInsPos( rPos.GetNode() ); 5090 const bool bOneNode = pStt->GetNode() == pEnd->GetNode(); 5091 SwTextNode* pSttTextNd = pStt->GetNode().GetTextNode(); 5092 SwTextNode* pEndTextNd = pEnd->GetNode().GetTextNode(); 5093 SwTextNode* pDestTextNd = aInsPos.GetNode().GetTextNode(); 5094 bool bCopyCollFormat = !rDoc.IsInsOnlyTextGlossary() && 5095 ( (pDestTextNd && !pDestTextNd->GetText().getLength()) || 5096 ( !bOneNode && !rPos.GetContentIndex() ) ); 5097 bool bCopyBookmarks = true; 5098 bool bCopyPageSource = false; 5099 SwNodeOffset nDeleteTextNodes(0); 5100 5101 // #i104585# copy outline num rule to clipboard (for ASCII filter) 5102 if (rDoc.IsClipBoard() && m_rDoc.GetOutlineNumRule()) 5103 { 5104 rDoc.SetOutlineNumRule(*m_rDoc.GetOutlineNumRule()); 5105 } 5106 5107 // #i86492# 5108 // Correct the search for a previous list: 5109 // First search for non-outline numbering list. Then search for non-outline 5110 // bullet list. 5111 // Keep also the <ListId> value for possible propagation. 5112 OUString aListIdToPropagate; 5113 const SwNumRule* pNumRuleToPropagate = 5114 rDoc.SearchNumRule( rPos, false, true, false, 0, aListIdToPropagate, nullptr, true ); 5115 if ( !pNumRuleToPropagate ) 5116 { 5117 pNumRuleToPropagate = 5118 rDoc.SearchNumRule( rPos, false, false, false, 0, aListIdToPropagate, nullptr, true ); 5119 } 5120 // #i86492# 5121 // Do not propagate previous found list, if 5122 // - destination is an empty paragraph which is not in a list and 5123 // - source contains at least one paragraph which is not in a list 5124 // or 5125 // - source is a table 5126 if ( pNumRuleToPropagate && 5127 ((pDestTextNd && !pDestTextNd->GetText().getLength() && 5128 !pDestTextNd->IsInList() && 5129 !lcl_ContainsOnlyParagraphsInList(rPam)) || 5130 rPam.GetBound().nNode.GetNode().GetNodeType() == SwNodeType::Table) ) 5131 { 5132 pNumRuleToPropagate = nullptr; 5133 } 5134 5135 // This do/while block is only there so that we can break out of it! 5136 do { 5137 if( pSttTextNd ) 5138 { 5139 ++nDeleteTextNodes; // must be joined in Undo 5140 // Don't copy the beginning completely? 5141 if( !bCopyCollFormat || bColumnSel || pStt->GetContentIndex() ) 5142 { 5143 SwContentIndex aDestIdx( rPos.GetContentNode(), rPos.GetContentIndex() ); 5144 bool bCopyOk = false; 5145 if( !pDestTextNd ) 5146 { 5147 if( pStt->GetContentIndex() || bOneNode ) 5148 pDestTextNd = rDoc.GetNodes().MakeTextNode( aInsPos.GetNode(), 5149 rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD)); 5150 else 5151 { 5152 pDestTextNd = pSttTextNd->MakeCopy(rDoc, aInsPos.GetNode(), true)->GetTextNode(); 5153 bCopyOk = true; 5154 } 5155 aDestIdx.Assign( pDestTextNd, 0 ); 5156 bCopyCollFormat = true; 5157 } 5158 else if( !bOneNode || bColumnSel ) 5159 { 5160 const sal_Int32 nContentEnd = pEnd->GetContentIndex(); 5161 { 5162 ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo()); 5163 rDoc.getIDocumentContentOperations().SplitNode( rPos, false ); 5164 } 5165 5166 if (bCanMoveBack && rPos == *pCopyPam->GetPoint()) 5167 { 5168 // after the SplitNode, span the CpyPam correctly again 5169 pCopyPam->Move( fnMoveBackward, GoInContent ); 5170 pCopyPam->Move( fnMoveBackward, GoInContent ); 5171 } 5172 5173 pDestTextNd = rDoc.GetNodes()[ aInsPos.GetIndex()-SwNodeOffset(1) ]->GetTextNode(); 5174 aDestIdx.Assign( 5175 pDestTextNd, pDestTextNd->GetText().getLength()); 5176 5177 // Correct the area again 5178 if( bEndEqualIns ) 5179 { 5180 bool bChg = pEnd != rPam.GetPoint(); 5181 if( bChg ) 5182 rPam.Exchange(); 5183 rPam.Move( fnMoveBackward, GoInContent ); 5184 if( bChg ) 5185 rPam.Exchange(); 5186 } 5187 else if( rPos == *pEnd ) 5188 { 5189 // The end was also moved 5190 pEnd->Adjust(SwNodeOffset(-1)); 5191 pEnd->SetContent( nContentEnd ); 5192 } 5193 // tdf#63022 always reset pEndTextNd after SplitNode 5194 aRg.aEnd = pEnd->GetNode(); 5195 pEndTextNd = pEnd->GetNode().GetTextNode(); 5196 } 5197 5198 NUMRULE_STATE 5199 if( bCopyCollFormat && bOneNode ) 5200 { 5201 PUSH_NUMRULE_STATE 5202 } 5203 5204 if( !bCopyOk ) 5205 { 5206 const sal_Int32 nCpyLen = ( bOneNode 5207 ? pEnd->GetContentIndex() 5208 : pSttTextNd->GetText().getLength()) 5209 - pStt->GetContentIndex(); 5210 pSttTextNd->CopyText( pDestTextNd, aDestIdx, *pStt, nCpyLen ); 5211 if( bEndEqualIns ) 5212 pEnd->AdjustContent( -nCpyLen ); 5213 } 5214 5215 ++aRg.aStart; 5216 5217 if( bOneNode ) 5218 { 5219 if (bCopyCollFormat) 5220 { 5221 // tdf#138897 no Undo for applying style, SwUndoInserts does it 5222 pSttTextNd->CopyCollFormat(*pDestTextNd, false); 5223 POP_NUMRULE_STATE 5224 } 5225 5226 // Copy at-char flys in rPam. 5227 // Update to new (start) node for flys. 5228 // tdf#126626 prevent duplicate Undos. 5229 ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo()); 5230 CopyFlyInFlyImpl(aRg, &rPam, *pDestTextNd, false); 5231 5232 break; 5233 } 5234 } 5235 } 5236 else if( pDestTextNd ) 5237 { 5238 // Problems with insertion of table selections into "normal" text solved. 5239 // We have to set the correct PaM for Undo, if this PaM starts in a textnode, 5240 // the undo operation will try to merge this node after removing the table. 5241 // If we didn't split a textnode, the PaM should start at the inserted table node 5242 if( rPos.GetContentIndex() == pDestTextNd->Len() ) 5243 { // Insertion at the last position of a textnode (empty or not) 5244 ++aInsPos; // The table will be inserted behind the text node 5245 } 5246 else if( rPos.GetContentIndex() ) 5247 { // Insertion in the middle of a text node, it has to be split 5248 // (and joined from undo) 5249 ++nDeleteTextNodes; 5250 5251 const sal_Int32 nContentEnd = pEnd->GetContentIndex(); 5252 { 5253 ::sw::UndoGuard const ug(rDoc.GetIDocumentUndoRedo()); 5254 rDoc.getIDocumentContentOperations().SplitNode( rPos, false ); 5255 } 5256 5257 if (bCanMoveBack && rPos == *pCopyPam->GetPoint()) 5258 { 5259 // after the SplitNode, span the CpyPam correctly again 5260 pCopyPam->Move( fnMoveBackward, GoInContent ); 5261 pCopyPam->Move( fnMoveBackward, GoInContent ); 5262 } 5263 5264 // Correct the area again 5265 if( bEndEqualIns ) 5266 --aRg.aEnd; 5267 // The end would also be moved 5268 else if( rPos == *pEnd ) 5269 { 5270 rPos.Adjust(SwNodeOffset(-1)); 5271 rPos.SetContent( nContentEnd ); 5272 --aRg.aEnd; 5273 } 5274 } 5275 else if( bCanMoveBack ) 5276 { // Insertion at the first position of a text node. It will not be split, the table 5277 // will be inserted before the text node. 5278 // See below, before the SetInsertRange function of the undo object will be called, 5279 // the CpyPam would be moved to the next content position. This has to be avoided 5280 // We want to be moved to the table node itself thus we have to set bCanMoveBack 5281 // and to manipulate pCopyPam. 5282 bCanMoveBack = false; 5283 pCopyPam->GetPoint()->Adjust(SwNodeOffset(-1)); 5284 } 5285 } 5286 5287 pDestTextNd = aInsPos.GetNode().GetTextNode(); 5288 if (pEndTextNd) 5289 { 5290 SwContentIndex aDestIdx( aInsPos.GetNode().GetContentNode(), rPos.GetContentIndex() ); 5291 if( !pDestTextNd ) 5292 { 5293 pDestTextNd = rDoc.GetNodes().MakeTextNode( aInsPos.GetNode(), 5294 rDoc.getIDocumentStylePoolAccess().GetTextCollFromPool(RES_POOLCOLL_STANDARD)); 5295 aDestIdx.Assign( pDestTextNd, 0 ); 5296 --aInsPos; 5297 5298 // if we have to insert an extra text node 5299 // at the destination, this node will be our new destination 5300 // (text) node, and thus we increment nDeleteTextNodes. This 5301 // will ensure that this node will be deleted during Undo. 5302 ++nDeleteTextNodes; // must be deleted 5303 } 5304 5305 const bool bEmptyDestNd = pDestTextNd->GetText().isEmpty(); 5306 5307 NUMRULE_STATE 5308 if( bCopyCollFormat && ( bOneNode || bEmptyDestNd )) 5309 { 5310 PUSH_NUMRULE_STATE 5311 } 5312 5313 pEndTextNd->CopyText( pDestTextNd, aDestIdx, SwContentIndex( pEndTextNd ), 5314 pEnd->GetContentIndex() ); 5315 5316 // Also copy all format templates 5317 if( bCopyCollFormat && ( bOneNode || bEmptyDestNd )) 5318 { 5319 // tdf#138897 no Undo for applying style, SwUndoInserts does it 5320 pEndTextNd->CopyCollFormat(*pDestTextNd, false); 5321 if ( bOneNode ) 5322 { 5323 POP_NUMRULE_STATE 5324 } 5325 } 5326 } 5327 5328 SfxItemSet aBrkSet( rDoc.GetAttrPool(), aBreakSetRange ); 5329 if ((flags & SwCopyFlags::CopyAll) || aRg.aStart != aRg.aEnd) 5330 { 5331 if (pSttTextNd && bCopyCollFormat && pDestTextNd->HasSwAttrSet()) 5332 { 5333 aBrkSet.Put( *pDestTextNd->GetpSwAttrSet() ); 5334 if( SfxItemState::SET == aBrkSet.GetItemState( RES_BREAK, false ) ) 5335 pDestTextNd->ResetAttr( RES_BREAK ); 5336 if( SfxItemState::SET == aBrkSet.GetItemState( RES_PAGEDESC, false ) ) 5337 pDestTextNd->ResetAttr( RES_PAGEDESC ); 5338 } 5339 } 5340 5341 { 5342 SwPosition startPos(pCopyPam->GetPoint()->GetNode(), SwNodeOffset(+1)); 5343 if (bCanMoveBack) 5344 { // pCopyPam is actually 1 before the copy range so move it fwd 5345 SwPaM temp(*pCopyPam->GetPoint()); 5346 temp.Move(fnMoveForward, GoInContent); 5347 startPos = *temp.GetPoint(); 5348 } 5349 assert(startPos.GetNode().IsContentNode()); 5350 std::pair<SwPaM const&, SwPosition const&> tmp(rPam, startPos); 5351 if( aInsPos == pEnd->GetNode() ) 5352 { 5353 SwNodeIndex aSaveIdx( aInsPos, -1 ); 5354 assert(pStt->GetNode() != pEnd->GetNode()); 5355 pEnd->SetContent(0); // TODO why this? 5356 CopyWithFlyInFly(aRg, aInsPos.GetNode(), &tmp, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags); 5357 ++aSaveIdx; 5358 pEnd->Assign(aSaveIdx); 5359 } 5360 else 5361 CopyWithFlyInFly(aRg, aInsPos.GetNode(), &tmp, /*bMakeNewFrames*/true, false, /*bCopyFlyAtFly=*/false, flags); 5362 5363 bCopyBookmarks = false; 5364 } 5365 5366 // at-char anchors post SplitNode are on index 0 of 2nd node and will 5367 // remain there - move them back to the start (end would also work?) 5368 // ... also for at-para anchors; here start is preferable because 5369 // it's consistent with SplitNode from SwUndoInserts::RedoImpl() 5370 if (pFlysAtInsPos) 5371 { 5372 // init *again* - because CopyWithFlyInFly moved startPos 5373 SwPosition startPos(pCopyPam->GetPoint()->GetNode(), SwNodeOffset(+1)); 5374 if (bCanMoveBack) 5375 { // pCopyPam is actually 1 before the copy range so move it fwd 5376 SwPaM temp(*pCopyPam->GetPoint()); 5377 temp.Move(fnMoveForward, GoInContent); 5378 startPos = *temp.GetPoint(); 5379 } 5380 assert(startPos.GetNode().IsContentNode()); 5381 SwPosition startPosAtPara(startPos); 5382 startPosAtPara.nContent.Assign(nullptr, 0); 5383 5384 for (SwFrameFormat * pFly : *pFlysAtInsPos) 5385 { 5386 SwFormatAnchor const*const pAnchor = &pFly->GetAnchor(); 5387 if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_CHAR) 5388 { 5389 SwFormatAnchor anchor(*pAnchor); 5390 anchor.SetAnchor( &startPos ); 5391 pFly->SetFormatAttr(anchor); 5392 } 5393 else if (pAnchor->GetAnchorId() == RndStdIds::FLY_AT_PARA) 5394 { 5395 SwFormatAnchor anchor(*pAnchor); 5396 anchor.SetAnchor( &startPosAtPara ); 5397 5398 bool bSplitFly = false; 5399 if (pFly->GetFlySplit().GetValue()) 5400 { 5401 SwIterator<SwFrame, SwModify> aIter(*pFly); 5402 bSplitFly = aIter.First() && aIter.Next(); 5403 } 5404 if (bSplitFly) 5405 { 5406 // This fly format has multiple frames, and we change the anchor. Remove the 5407 // old frames, which were based on the old anchor position. 5408 pFly->DelFrames(); 5409 } 5410 5411 pFly->SetFormatAttr(anchor); 5412 5413 if (bSplitFly) 5414 { 5415 // Re-create the frames now that the new anchor is set. 5416 pFly->MakeFrames(); 5417 } 5418 } 5419 } 5420 } 5421 5422 if ((flags & SwCopyFlags::CopyAll) || aRg.aStart != aRg.aEnd) 5423 { 5424 // Put the breaks back into the first node 5425 if( aBrkSet.Count() && nullptr != ( pDestTextNd = rDoc.GetNodes()[ 5426 pCopyPam->GetPoint()->GetNodeIndex()+1 ]->GetTextNode())) 5427 { 5428 pDestTextNd->SetAttr( aBrkSet ); 5429 bCopyPageSource = true; 5430 } 5431 } 5432 } while( false ); 5433 5434 5435 // it is not possible to make this test when copy from the clipBoard to document 5436 // in this case the PageNum not exist anymore 5437 // tdf#39400 and tdf#97526 5438 // when copy from document to ClipBoard, and it is from the first page 5439 // and not the source has the page break 5440 if (rDoc.IsClipBoard() && (rPam.GetPageNum(pStt == rPam.GetPoint()) == 1) && !bCopyPageSource) 5441 { 5442 if (pDestTextNd) 5443 { 5444 pDestTextNd->ResetAttr(RES_BREAK); // remove the page-break 5445 pDestTextNd->ResetAttr(RES_PAGEDESC); 5446 } 5447 } 5448 5449 5450 // Adjust position (in case it was moved / in another node) 5451 rPos.nContent.Assign( rPos.GetNode().GetContentNode(), 5452 rPos.GetContentIndex() ); 5453 5454 if( rPos.GetNode() != aInsPos.GetNode() ) 5455 { 5456 if (aInsPos < rPos.GetNode()) 5457 { // tdf#134250 decremented in (pEndTextNd && !pDestTextNd) above 5458 pCopyPam->GetMark()->AssignEndIndex(*aInsPos.GetNode().GetContentNode()); 5459 } 5460 else // incremented in (!pSttTextNd && pDestTextNd) above 5461 { 5462 pCopyPam->GetMark()->Assign(aInsPos); 5463 } 5464 rPos = *pCopyPam->GetMark(); 5465 } 5466 else 5467 *pCopyPam->GetMark() = rPos; 5468 5469 if ( !bAfterTable ) 5470 pCopyPam->Move( fnMoveForward, bCanMoveBack ? GoInContent : GoInNode ); 5471 else 5472 { 5473 // Reset the offset to 0 as it was before the insertion 5474 pCopyPam->GetPoint()->Adjust(SwNodeOffset(+1)); 5475 5476 // If the next node is a start node, then step back: the start node 5477 // has been copied and needs to be in the selection for the undo 5478 if (pCopyPam->GetPoint()->GetNode().IsStartNode()) 5479 pCopyPam->GetPoint()->Adjust(SwNodeOffset(-1)); 5480 5481 } 5482 pCopyPam->Exchange(); 5483 5484 // Also copy all bookmarks 5485 if( bCopyBookmarks && m_rDoc.getIDocumentMarkAccess()->getAllMarksCount() ) 5486 { 5487 sw::CopyBookmarks(rPam, *pCopyPam->Start()); 5488 } 5489 5490 if( RedlineFlags::DeleteRedlines & eOld ) 5491 { 5492 assert(*pCopyPam->GetPoint() == rPos); 5493 // the Node rPos points to may be deleted so unregister ... 5494 rPos.nContent.Assign(nullptr, 0); 5495 lcl_DeleteRedlines(rPam, *pCopyPam); 5496 rPos = *pCopyPam->GetPoint(); // ... and restore. 5497 } 5498 5499 // If Undo is enabled, store the inserted area 5500 if (rDoc.GetIDocumentUndoRedo().DoesUndo()) 5501 { 5502 // append it after styles have been copied when copying nodes 5503 rDoc.GetIDocumentUndoRedo().AppendUndo( std::unique_ptr<SwUndo>(pUndo) ); 5504 pUndo->SetInsertRange(*pCopyPam, true, nDeleteTextNodes); 5505 } 5506 5507 if( pCpyRange ) 5508 { 5509 pCpyRange->SetMark(); 5510 *pCpyRange->GetPoint() = *pCopyPam->GetPoint(); 5511 *pCpyRange->GetMark() = *pCopyPam->GetMark(); 5512 } 5513 5514 if ( pNumRuleToPropagate != nullptr ) 5515 { 5516 // #i86492# - use <SwDoc::SetNumRule(..)>, because it also handles the <ListId> 5517 // Don't reset indent attributes, that would mean loss of direct 5518 // formatting. 5519 rDoc.SetNumRule( *pCopyPam, *pNumRuleToPropagate, false, nullptr, 5520 aListIdToPropagate, true, /*bResetIndentAttrs=*/false ); 5521 } 5522 5523 rDoc.getIDocumentRedlineAccess().SetRedlineFlags_intern( eOld ); 5524 rDoc.getIDocumentState().SetModified(); 5525 5526 return true; 5527 } 5528 5529 5530 } 5531 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 5532
