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