1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 /* 3 * This file is part of the LibreOffice project. 4 * 5 * This Source Code Form is subject to the terms of the Mozilla Public 6 * License, v. 2.0. If a copy of the MPL was not distributed with this 7 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 8 * 9 * This file incorporates work covered by the following license notice: 10 * 11 * Licensed to the Apache Software Foundation (ASF) under one or more 12 * contributor license agreements. See the NOTICE file distributed 13 * with this work for additional information regarding copyright 14 * ownership. The ASF licenses this file to you under the Apache 15 * License, Version 2.0 (the "License"); you may not use this file 16 * except in compliance with the License. You may obtain a copy of 17 * the License at http://www.apache.org/licenses/LICENSE-2.0 . 18 */ 19 20 #include <hintids.hxx> 21 #include <editeng/protitem.hxx> 22 #include <com/sun/star/i18n/WordType.hpp> 23 #include <com/sun/star/i18n/XBreakIterator.hpp> 24 #include <unotools/charclass.hxx> 25 #include <svl/ctloptions.hxx> 26 #include <svl/srchitem.hxx> 27 #include <swmodule.hxx> 28 #include <fmtcntnt.hxx> 29 #include <swtblfmt.hxx> 30 #include <swcrsr.hxx> 31 #include <unocrsr.hxx> 32 #include <bookmark.hxx> 33 #include <doc.hxx> 34 #include <IDocumentUndoRedo.hxx> 35 #include <IDocumentRedlineAccess.hxx> 36 #include <IDocumentLayoutAccess.hxx> 37 #include <docary.hxx> 38 #include <ndtxt.hxx> 39 #include <section.hxx> 40 #include <swtable.hxx> 41 #include <cntfrm.hxx> 42 #include <rootfrm.hxx> 43 #include <txtfrm.hxx> 44 #include <notxtfrm.hxx> 45 #include <scriptinfo.hxx> 46 #include <crstate.hxx> 47 #include <docsh.hxx> 48 #include <viewsh.hxx> 49 #include <frmatr.hxx> 50 #include <breakit.hxx> 51 #include <mdiexp.hxx> 52 #include <strings.hrc> 53 #include <redline.hxx> 54 #include <txatbase.hxx> 55 #include <IDocumentMarkAccess.hxx> 56 #include <memory> 57 #include <comphelper/lok.hxx> 58 #include <editsh.hxx> 59 60 #include <viewopt.hxx> 61 62 using namespace ::com::sun::star::i18n; 63 64 const sal_uInt16 coSrchRplcThreshold = 60000; 65 66 namespace { 67 68 struct PercentHdl 69 { 70 SwDocShell* pDSh; 71 sal_uLong nActPos; 72 bool bBack, bNodeIdx; 73 74 PercentHdl( sal_uLong nStt, sal_uLong nEnd, SwDocShell* pSh ) 75 : pDSh(pSh), nActPos(nStt), bBack(false), bNodeIdx(false) 76 { 77 bBack = (nStt > nEnd); 78 if( bBack ) 79 std::swap( nStt, nEnd ); 80 ::StartProgress( STR_STATSTR_SEARCH, nStt, nEnd ); 81 } 82 83 explicit PercentHdl( const SwPaM& rPam ) 84 : pDSh( rPam.GetDoc().GetDocShell() ) 85 { 86 sal_Int32 nStt, nEnd; 87 if( rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode() ) 88 { 89 bNodeIdx = false; 90 nStt = rPam.GetMark()->GetContentIndex(); 91 nEnd = rPam.GetPoint()->GetContentIndex(); 92 } 93 else 94 { 95 bNodeIdx = true; 96 nStt = sal_Int32(rPam.GetMark()->GetNodeIndex()); 97 nEnd = sal_Int32(rPam.GetPoint()->GetNodeIndex()); 98 } 99 nActPos = nStt; 100 bBack = (nStt > nEnd ); 101 if( bBack ) 102 std::swap( nStt, nEnd ); 103 ::StartProgress( STR_STATSTR_SEARCH, nStt, nEnd, pDSh ); 104 } 105 106 ~PercentHdl() { ::EndProgress( pDSh ); } 107 108 void NextPos( sal_uLong nPos ) const 109 { ::SetProgressState( bBack ? nActPos - nPos : nPos, pDSh ); } 110 111 void NextPos( SwPosition const & rPos ) const 112 { 113 sal_Int32 nPos; 114 if( bNodeIdx ) 115 nPos = sal_Int32(rPos.GetNodeIndex()); 116 else 117 nPos = rPos.GetContentIndex(); 118 ::SetProgressState( bBack ? nActPos - nPos : nPos, pDSh ); 119 } 120 }; 121 122 } 123 124 SwCursor::SwCursor( const SwPosition &rPos, SwPaM* pRing ) 125 : SwPaM( rPos, pRing ) 126 , m_nRowSpanOffset(0) 127 , m_nCursorBidiLevel(0) 128 , m_bColumnSelection(false) 129 { 130 } 131 132 // @@@ semantic: no copy ctor. 133 SwCursor::SwCursor(SwCursor const& rCpy, SwPaM *const pRing) 134 : SwPaM( rCpy, pRing ) 135 , m_nRowSpanOffset(rCpy.m_nRowSpanOffset) 136 , m_nCursorBidiLevel(rCpy.m_nCursorBidiLevel) 137 , m_bColumnSelection(rCpy.m_bColumnSelection) 138 { 139 } 140 141 SwCursor::~SwCursor() 142 { 143 } 144 145 SwCursor* SwCursor::Create( SwPaM* pRing ) const 146 { 147 return new SwCursor( *GetPoint(), pRing ); 148 } 149 150 bool SwCursor::IsReadOnlyAvailable() const 151 { 152 return false; 153 } 154 155 bool SwCursor::IsSkipOverHiddenSections() const 156 { 157 return true; 158 } 159 160 bool SwCursor::IsSkipOverProtectSections() const 161 { 162 return !IsReadOnlyAvailable(); 163 } 164 165 // CreateNewSavePos is virtual so that derived classes of cursor can implement 166 // own SaveObjects if needed and validate them in the virtual check routines. 167 void SwCursor::SaveState() 168 { 169 m_vSavePos.emplace_back( *this ); 170 } 171 172 void SwCursor::RestoreState() 173 { 174 if (!m_vSavePos.empty()) // Robust 175 { 176 m_vSavePos.pop_back(); 177 } 178 } 179 180 /// determine if point is outside of the node-array's content area 181 bool SwCursor::IsNoContent() const 182 { 183 return GetPoint()->GetNodeIndex() < 184 GetDoc().GetNodes().GetEndOfExtras().GetIndex(); 185 } 186 187 bool SwCursor::IsSelOvrCheck(SwCursorSelOverFlags) 188 { 189 return false; 190 } 191 192 // extracted from IsSelOvr() 193 bool SwTableCursor::IsSelOvrCheck(SwCursorSelOverFlags eFlags) 194 { 195 SwNodes& rNds = GetDoc().GetNodes(); 196 // check sections of nodes array 197 if( (SwCursorSelOverFlags::CheckNodeSection & eFlags) 198 && HasMark() ) 199 { 200 SwNodeIndex aOldPos( rNds, GetSavePos()->nNode ); 201 if( !CheckNodesRange( aOldPos.GetNode(), GetPoint()->GetNode(), true )) 202 { 203 GetPoint()->Assign( aOldPos ); 204 GetPoint()->SetContent( GetSavePos()->nContent ); 205 return true; 206 } 207 } 208 return SwCursor::IsSelOvrCheck(eFlags); 209 } 210 211 namespace 212 { 213 const SwTextAttr* InputFieldAtPos(SwPosition const *pPos) 214 { 215 SwTextNode* pTextNd = pPos->GetNode().GetTextNode(); 216 if (!pTextNd) 217 return nullptr; 218 return pTextNd->GetTextAttrAt(pPos->GetContentIndex(), RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent); 219 } 220 } 221 222 bool SwCursor::IsSelOvr(SwCursorSelOverFlags const eFlags) 223 { 224 SwDoc& rDoc = GetDoc(); 225 SwNodes& rNds = rDoc.GetNodes(); 226 227 bool bSkipOverHiddenSections = IsSkipOverHiddenSections(); 228 bool bSkipOverProtectSections = IsSkipOverProtectSections(); 229 230 if ( IsSelOvrCheck( eFlags ) ) 231 { 232 return true; 233 } 234 235 if (m_vSavePos.back().nNode != GetPoint()->GetNodeIndex() && 236 // (1997) in UI-ReadOnly everything is allowed 237 ( !rDoc.GetDocShell() || !rDoc.GetDocShell()->IsReadOnlyUI() )) 238 { 239 // check new sections 240 SwPosition& rPtPos = *GetPoint(); 241 const SwSectionNode* pSectNd = rPtPos.GetNode().FindSectionNode(); 242 if( pSectNd && 243 ((bSkipOverHiddenSections && pSectNd->GetSection().IsHiddenFlag() ) || 244 (bSkipOverProtectSections && pSectNd->GetSection().IsProtectFlag() ))) 245 { 246 if( !( SwCursorSelOverFlags::ChangePos & eFlags ) ) 247 { 248 // then we're already done 249 RestoreSavePos(); 250 return true; 251 } 252 253 // set cursor to new position: 254 SwNodeIndex aIdx( rPtPos.GetNode() ); 255 sal_Int32 nContentPos = m_vSavePos.back().nContent; 256 bool bGoNxt = m_vSavePos.back().nNode < rPtPos.GetNodeIndex(); 257 SwContentNode* pCNd = bGoNxt 258 ? rNds.GoNextSection( &rPtPos, bSkipOverHiddenSections, bSkipOverProtectSections) 259 : SwNodes::GoPrevSection( &rPtPos, bSkipOverHiddenSections, bSkipOverProtectSections); 260 if( !pCNd && ( SwCursorSelOverFlags::EnableRevDirection & eFlags )) 261 { 262 bGoNxt = !bGoNxt; 263 pCNd = bGoNxt ? rNds.GoNextSection( &rPtPos, bSkipOverHiddenSections, bSkipOverProtectSections) 264 : SwNodes::GoPrevSection( &rPtPos, bSkipOverHiddenSections, bSkipOverProtectSections); 265 } 266 267 bool bIsValidPos = nullptr != pCNd; 268 const bool bValidNodesRange = bIsValidPos && 269 ::CheckNodesRange( rPtPos.GetNode(), aIdx.GetNode(), true ); 270 if( !bValidNodesRange ) 271 { 272 rPtPos.Assign( m_vSavePos.back().nNode ); 273 pCNd = rPtPos.GetNode().GetContentNode(); 274 if( !pCNd ) 275 { 276 bIsValidPos = false; 277 nContentPos = 0; 278 rPtPos.Assign( aIdx ); 279 pCNd = rPtPos.GetNode().GetContentNode(); 280 if( !pCNd ) 281 { 282 // then to the beginning of the document 283 rPtPos.Assign( rNds.GetEndOfExtras() ); 284 pCNd = rNds.GoNext( &rPtPos ); 285 } 286 } 287 } 288 289 // register ContentIndex: 290 const sal_Int32 nTmpPos = bIsValidPos ? (bGoNxt ? 0 : pCNd->Len()) : nContentPos; 291 GetPoint()->SetContent( nTmpPos ); 292 if( !bIsValidPos || !bValidNodesRange || 293 IsInProtectTable( true ) ) 294 return true; 295 } 296 297 // is there a protected section in the section? 298 if( HasMark() && bSkipOverProtectSections) 299 { 300 SwNodeOffset nSttIdx = GetMark()->GetNodeIndex(), 301 nEndIdx = GetPoint()->GetNodeIndex(); 302 if( nEndIdx <= nSttIdx ) 303 std::swap( nSttIdx, nEndIdx ); 304 305 const SwSectionFormats& rFormats = rDoc.GetSections(); 306 for( SwSectionFormats::size_type n = 0; n < rFormats.size(); ++n ) 307 { 308 const SwSectionFormat* pFormat = rFormats[n]; 309 const SvxProtectItem& rProtect = pFormat->GetProtect(); 310 if( rProtect.IsContentProtected() ) 311 { 312 const SwFormatContent& rContent = pFormat->GetContent(false); 313 OSL_ENSURE( rContent.GetContentIdx(), "No SectionNode?" ); 314 SwNodeOffset nIdx = rContent.GetContentIdx()->GetIndex(); 315 if( nSttIdx <= nIdx && nEndIdx >= nIdx ) 316 { 317 // if it is no linked section then we cannot select it 318 const SwSection& rSect = *pFormat->GetSection(); 319 if( SectionType::Content == rSect.GetType() ) 320 { 321 RestoreSavePos(); 322 return true; 323 } 324 } 325 } 326 } 327 } 328 } 329 330 const SwNode* pNd = &GetPoint()->GetNode(); 331 if( pNd->IsContentNode() && !dynamic_cast<SwUnoCursor*>(this) ) 332 { 333 const SwContentFrame* pFrame = static_cast<const SwContentNode*>(pNd)->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ); 334 if ( (SwCursorSelOverFlags::ChangePos & eFlags) //allowed to change position if it's a bad one 335 && pFrame && pFrame->isFrameAreaDefinitionValid() 336 && !pFrame->getFrameArea().Height() //a bad zero height position 337 && !InputFieldAtPos(GetPoint()) ) //unless it's a (vertical) input field 338 { 339 // skip to the next/prev valid paragraph with a layout 340 SwPosition& rPtPos = *GetPoint(); 341 bool bGoNxt = m_vSavePos.back().nNode < rPtPos.GetNodeIndex(); 342 for (;;) 343 { 344 pFrame = bGoNxt ? pFrame->FindNextCnt(true) : pFrame->FindPrevCnt(); 345 if (!pFrame || 0 != pFrame->getFrameArea().Height() ) 346 break; 347 } 348 349 // #i72394# skip to prev/next valid paragraph with a layout in case 350 // the first search did not succeed: 351 if( !pFrame ) 352 { 353 bGoNxt = !bGoNxt; 354 pFrame = static_cast<const SwContentNode*>(pNd)->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ); 355 while ( pFrame && 0 == pFrame->getFrameArea().Height() ) 356 { 357 pFrame = bGoNxt ? pFrame->FindNextCnt(true) : pFrame->FindPrevCnt(); 358 } 359 } 360 361 if (pFrame != nullptr) 362 { 363 if (pFrame->IsTextFrame()) 364 { 365 SwTextFrame const*const pTextFrame(static_cast<SwTextFrame const*>(pFrame)); 366 *GetPoint() = pTextFrame->MapViewToModelPos(TextFrameIndex( 367 bGoNxt ? 0 : pTextFrame->GetText().getLength())); 368 } 369 else 370 { 371 assert(pFrame->IsNoTextFrame()); 372 SwContentNode *const pCNd = const_cast<SwContentNode*>( 373 static_cast<SwNoTextFrame const*>(pFrame)->GetNode()); 374 assert(pCNd); 375 376 // set this ContentNode as new position 377 rPtPos.Assign( *pCNd ); 378 // assign corresponding ContentIndex 379 const sal_Int32 nTmpPos = bGoNxt ? 0 : pCNd->Len(); 380 GetPoint()->SetContent( nTmpPos ); 381 } 382 383 384 if (rPtPos.GetNodeIndex() == m_vSavePos.back().nNode 385 && GetPoint()->GetContentIndex() == m_vSavePos.back().nContent) 386 { 387 // new position equals saved one 388 // --> trigger restore of saved pos by setting <pFrame> to NULL - see below 389 pFrame = nullptr; 390 } 391 392 if ( IsInProtectTable( true ) ) 393 { 394 // new position in protected table 395 // --> trigger restore of saved pos by setting <pFrame> to NULL - see below 396 pFrame = nullptr; 397 } 398 } 399 } 400 401 if( !pFrame ) 402 { 403 DeleteMark(); 404 RestoreSavePos(); 405 return true; // we need a frame 406 } 407 } 408 409 // is the cursor allowed to be in a protected node? 410 if( !( SwCursorSelOverFlags::ChangePos & eFlags ) && !IsAtValidPos() ) 411 { 412 DeleteMark(); 413 RestoreSavePos(); 414 return true; 415 } 416 417 if( !HasMark() ) 418 return false; 419 420 // check for invalid sections 421 if( !::CheckNodesRange( GetMark()->GetNode(), GetPoint()->GetNode(), true )) 422 { 423 DeleteMark(); 424 RestoreSavePos(); 425 return true; // we need a frame 426 } 427 428 pNd = &GetMark()->GetNode(); 429 if( pNd->IsContentNode() 430 && !static_cast<const SwContentNode*>(pNd)->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ) 431 && !dynamic_cast<SwUnoCursor*>(this) ) 432 { 433 DeleteMark(); 434 RestoreSavePos(); 435 return true; // we need a frame 436 } 437 438 // ensure that selection is only inside an InputField or contains the InputField completely 439 { 440 const SwTextAttr* pInputFieldTextAttrAtPoint = InputFieldAtPos(GetPoint()); 441 const SwTextAttr* pInputFieldTextAttrAtMark = InputFieldAtPos(GetMark()); 442 443 if ( pInputFieldTextAttrAtPoint != pInputFieldTextAttrAtMark ) 444 { 445 const SwNodeOffset nRefNodeIdx = 446 ( SwCursorSelOverFlags::Toggle & eFlags ) 447 ? m_vSavePos.back().nNode 448 : GetMark()->GetNodeIndex(); 449 const sal_Int32 nRefContentIdx = 450 ( SwCursorSelOverFlags::Toggle & eFlags ) 451 ? m_vSavePos.back().nContent 452 : GetMark()->GetContentIndex(); 453 const bool bIsForwardSelection = 454 nRefNodeIdx < GetPoint()->GetNodeIndex() 455 || ( nRefNodeIdx == GetPoint()->GetNodeIndex() 456 && nRefContentIdx < GetPoint()->GetContentIndex() ); 457 458 if ( pInputFieldTextAttrAtPoint != nullptr ) 459 { 460 const sal_Int32 nNewPointPos = 461 bIsForwardSelection ? *(pInputFieldTextAttrAtPoint->End()) : pInputFieldTextAttrAtPoint->GetStart(); 462 GetPoint()->SetContent( nNewPointPos ); 463 } 464 465 if ( pInputFieldTextAttrAtMark != nullptr ) 466 { 467 const sal_Int32 nNewMarkPos = 468 bIsForwardSelection ? pInputFieldTextAttrAtMark->GetStart() : *(pInputFieldTextAttrAtMark->End()); 469 GetMark()->SetContent( nNewMarkPos ); 470 } 471 } 472 } 473 474 const SwTableNode* pPtNd = GetPoint()->GetNode().FindTableNode(); 475 const SwTableNode* pMrkNd = GetMark()->GetNode().FindTableNode(); 476 // both in no or in same table node 477 if( ( !pMrkNd && !pPtNd ) || pPtNd == pMrkNd ) 478 return false; 479 480 // in different tables or only mark in table 481 if( pMrkNd ) 482 { 483 // not allowed, so go back to old position 484 RestoreSavePos(); 485 // Cursor stays at old position 486 return true; 487 } 488 489 // Note: this cannot happen in TableMode 490 // Only Point in Table then go behind/in front of table 491 if (SwCursorSelOverFlags::ChangePos & eFlags) 492 { 493 bool bSelTop = GetPoint()->GetNodeIndex() < 494 ((SwCursorSelOverFlags::Toggle & eFlags) 495 ? m_vSavePos.back().nNode : GetMark()->GetNodeIndex()); 496 497 do { // loop for table after table 498 SwNodeOffset nSEIdx = pPtNd->EndOfSectionIndex(); 499 SwNodeOffset nSttEndTable = nSEIdx + 1; 500 501 if( bSelTop ) 502 nSttEndTable = rNds[ nSEIdx ]->StartOfSectionIndex() - 1; 503 504 GetPoint()->Assign( nSttEndTable ); 505 const SwNode* pMyNd = &(GetPointNode()); 506 507 if( pMyNd->IsSectionNode() || ( pMyNd->IsEndNode() && 508 pMyNd->StartOfSectionNode()->IsSectionNode() ) ) 509 { 510 pMyNd = bSelTop 511 ? SwNodes::GoPrevSection( GetPoint(),true,false ) 512 : rNds.GoNextSection( GetPoint(),true,false ); 513 514 /* #i12312# Handle failure of Go{Prev|Next}Section */ 515 if ( nullptr == pMyNd) 516 break; 517 518 pPtNd = pMyNd->FindTableNode(); 519 if( pPtNd ) 520 continue; 521 } 522 523 // we permit these 524 if( pMyNd->IsContentNode() && 525 ::CheckNodesRange( GetMark()->GetNode(), 526 GetPoint()->GetNode(), true )) 527 { 528 // table in table 529 const SwTableNode* pOuterTableNd = pMyNd->FindTableNode(); 530 if ( pOuterTableNd ) 531 pMyNd = pOuterTableNd; 532 else 533 { 534 SwContentNode* pCNd = const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pMyNd)); 535 GetPoint()->SetContent( bSelTop ? pCNd->Len() : 0 ); 536 return false; 537 } 538 } 539 if( bSelTop ) 540 { 541 if ( !pMyNd->IsEndNode() ) 542 break; 543 pPtNd = pMyNd->FindTableNode(); 544 } 545 else 546 pPtNd = pMyNd->GetTableNode(); 547 if (!pPtNd) 548 break; 549 } while( true ); 550 } 551 552 // stay on old position 553 RestoreSavePos(); 554 return true; 555 } 556 557 bool SwCursor::IsInProtectTable( bool bMove, bool bChgCursor ) 558 { 559 SwContentNode* pCNd = GetPointContentNode(); 560 if( !pCNd ) 561 return false; 562 563 // No table, no protected cell: 564 const SwTableNode* pTableNode = pCNd->FindTableNode(); 565 if ( !pTableNode ) 566 return false; 567 568 // Current position == last save position? 569 if (m_vSavePos.back().nNode == GetPoint()->GetNodeIndex()) 570 return false; 571 572 // Check for covered cell: 573 bool bInCoveredCell = false; 574 const SwStartNode* pTmpSttNode = pCNd->FindTableBoxStartNode(); 575 OSL_ENSURE( pTmpSttNode, "In table, therefore I expect to get a SwTableBoxStartNode" ); 576 const SwTableBox* pBox = pTmpSttNode ? pTableNode->GetTable().GetTableBox( pTmpSttNode->GetIndex() ) : nullptr; //Robust #151355 577 if ( pBox && pBox->getRowSpan() < 1 ) // Robust #151270 578 bInCoveredCell = true; 579 580 // Positions of covered cells are not acceptable: 581 if ( !bInCoveredCell ) 582 { 583 // Position not protected? 584 if ( !pCNd->IsProtect() ) 585 return false; 586 587 // Cursor in protected cells allowed? 588 if ( IsReadOnlyAvailable() ) 589 return false; 590 } 591 592 // If we reach this point, we are in a protected or covered table cell! 593 594 if( !bMove ) 595 { 596 if( bChgCursor ) 597 // restore the last save position 598 RestoreSavePos(); 599 600 return true; // Cursor stays at old position 601 } 602 603 // We are in a protected table cell. Traverse top to bottom? 604 if (m_vSavePos.back().nNode < GetPoint()->GetNodeIndex()) 605 { 606 // search next valid box 607 // if there is another StartNode after the EndNode of a cell then 608 // there is another cell 609 SwNodeIndex aCellStt( *GetPointNode().FindTableBoxStartNode()->EndOfSectionNode(), 1 ); 610 bool bProt = true; 611 GoNextCell: 612 for (;;) { 613 if( !aCellStt.GetNode().IsStartNode() ) 614 break; 615 ++aCellStt; 616 pCNd = aCellStt.GetNode().GetContentNode(); 617 if( !pCNd ) 618 pCNd = aCellStt.GetNodes().GoNext( &aCellStt ); 619 bProt = pCNd->IsProtect(); 620 if( !bProt ) 621 break; 622 aCellStt.Assign( *pCNd->FindTableBoxStartNode()->EndOfSectionNode(), 1 ); 623 } 624 625 SetNextCursor: 626 if( !bProt ) // found free cell 627 { 628 GetPoint()->Assign( aCellStt ); 629 SwContentNode* pTmpCNd = GetPointContentNode(); 630 if( pTmpCNd ) 631 { 632 GetPoint()->SetContent( 0 ); 633 return false; 634 } 635 return IsSelOvr( SwCursorSelOverFlags::Toggle | 636 SwCursorSelOverFlags::ChangePos ); 637 } 638 // end of table, so go to next node 639 ++aCellStt; 640 SwNode* pNd = &aCellStt.GetNode(); 641 if( pNd->IsEndNode() || HasMark()) 642 { 643 // if only table in FlyFrame or SSelection then stay on old position 644 if( bChgCursor ) 645 RestoreSavePos(); 646 return true; 647 } 648 else if( pNd->IsTableNode() ) 649 { 650 ++aCellStt; 651 goto GoNextCell; 652 } 653 654 bProt = false; // index is now on a content node 655 goto SetNextCursor; 656 } 657 658 // search for the previous valid box 659 { 660 // if there is another EndNode in front of the StartNode than there 661 // exists a previous cell 662 SwNodeIndex aCellStt( *GetPointNode().FindTableBoxStartNode(), -1 ); 663 SwNode* pNd; 664 bool bProt = true; 665 GoPrevCell: 666 for (;;) { 667 pNd = &aCellStt.GetNode(); 668 if( !pNd->IsEndNode() ) 669 break; 670 aCellStt.Assign( *pNd->StartOfSectionNode(), +1 ); 671 pCNd = aCellStt.GetNode().GetContentNode(); 672 if( !pCNd ) 673 pCNd = pNd->GetNodes().GoNext( &aCellStt ); 674 bProt = pCNd->IsProtect(); 675 if( !bProt ) 676 break; 677 aCellStt.Assign( *pNd->FindTableBoxStartNode(), -1 ); 678 } 679 680 SetPrevCursor: 681 if( !bProt ) // found free cell 682 { 683 GetPoint()->Assign( aCellStt ); 684 SwContentNode* pTmpCNd = GetPointContentNode(); 685 if( pTmpCNd ) 686 { 687 GetPoint()->SetContent( 0 ); 688 return false; 689 } 690 return IsSelOvr( SwCursorSelOverFlags::Toggle | 691 SwCursorSelOverFlags::ChangePos ); 692 } 693 // at the beginning of a table, so go to next node 694 --aCellStt; 695 pNd = &aCellStt.GetNode(); 696 if( pNd->IsStartNode() || HasMark() ) 697 { 698 // if only table in FlyFrame or SSelection then stay on old position 699 if( bChgCursor ) 700 RestoreSavePos(); 701 return true; 702 } 703 else if( pNd->StartOfSectionNode()->IsTableNode() ) 704 { 705 --aCellStt; 706 goto GoPrevCell; 707 } 708 709 bProt = false; // index is now on a content node 710 goto SetPrevCursor; 711 } 712 } 713 714 /// Return <true> if cursor can be set to this position 715 bool SwCursor::IsAtValidPos( bool bPoint ) const 716 { 717 const SwDoc& rDoc = GetDoc(); 718 const SwPosition* pPos = bPoint ? GetPoint() : GetMark(); 719 const SwNode* pNd = &pPos->GetNode(); 720 721 if( pNd->IsContentNode() && !static_cast<const SwContentNode*>(pNd)->getLayoutFrame( rDoc.getIDocumentLayoutAccess().GetCurrentLayout() ) && 722 !dynamic_cast<const SwUnoCursor*>(this) ) 723 { 724 return false; 725 } 726 727 // #i45129# - in UI-ReadOnly everything is allowed 728 if( !rDoc.GetDocShell() || !rDoc.GetDocShell()->IsReadOnlyUI() ) 729 return true; 730 731 const bool bCursorInReadOnly = IsReadOnlyAvailable(); 732 if( !bCursorInReadOnly && pNd->IsProtect() ) 733 return false; 734 735 const SwSectionNode* pSectNd = pNd->FindSectionNode(); 736 return !pSectNd 737 || !(pSectNd->GetSection().IsHiddenFlag() || 738 ( !bCursorInReadOnly && pSectNd->GetSection().IsProtectFlag() )); 739 } 740 741 void SwCursor::SaveTableBoxContent( const SwPosition* ) {} 742 743 /// set range for search in document 744 SwMoveFnCollection const & SwCursor::MakeFindRange( SwDocPositions nStart, 745 SwDocPositions nEnd, SwPaM* pRange ) const 746 { 747 pRange->SetMark(); 748 FillFindPos( nStart, *pRange->GetMark() ); 749 FillFindPos( nEnd, *pRange->GetPoint() ); 750 751 // determine direction of search 752 return ( SwDocPositions::Start == nStart || SwDocPositions::OtherStart == nStart || 753 (SwDocPositions::Curr == nStart && 754 (SwDocPositions::End == nEnd || SwDocPositions::OtherEnd == nEnd ) )) 755 ? fnMoveForward : fnMoveBackward; 756 } 757 758 static sal_Int32 lcl_FindSelection( SwFindParas& rParas, SwCursor* pCurrentCursor, 759 SwMoveFnCollection const & fnMove, SwCursor*& pFndRing, 760 SwPaM& aRegion, FindRanges eFndRngs, 761 bool bInReadOnly, bool& bCancel ) 762 { 763 SwDoc& rDoc = pCurrentCursor->GetDoc(); 764 bool const bDoesUndo = rDoc.GetIDocumentUndoRedo().DoesUndo(); 765 int nFndRet = 0; 766 sal_Int32 nFound = 0; 767 const bool bSrchBkwrd = &fnMove == &fnMoveBackward; 768 SwPaM *pTmpCursor = pCurrentCursor, *pSaveCursor = pCurrentCursor; 769 std::unique_ptr<SvxSearchItem> xSearchItem; 770 771 // only create progress bar for ShellCursor 772 bool bIsUnoCursor = dynamic_cast<SwUnoCursor*>(pCurrentCursor) != nullptr; 773 std::unique_ptr<PercentHdl> pPHdl; 774 sal_uInt16 nCursorCnt = 0; 775 if( FindRanges::InSel & eFndRngs ) 776 { 777 while( pCurrentCursor != ( pTmpCursor = pTmpCursor->GetNext() )) 778 ++nCursorCnt; 779 if( nCursorCnt && !bIsUnoCursor ) 780 pPHdl.reset(new PercentHdl( 0, nCursorCnt, rDoc.GetDocShell() )); 781 } 782 else 783 pSaveCursor = pSaveCursor->GetPrev(); 784 785 bool bEnd = false; 786 do { 787 aRegion.SetMark(); 788 // independent from search direction: SPoint is always bigger than mark 789 // if the search area is valid 790 SwPosition *pSttPos = aRegion.GetMark(), 791 *pEndPos = aRegion.GetPoint(); 792 *pSttPos = *pTmpCursor->Start(); 793 *pEndPos = *pTmpCursor->End(); 794 if( bSrchBkwrd ) 795 aRegion.Exchange(); 796 797 if( !nCursorCnt && !pPHdl && !bIsUnoCursor ) 798 pPHdl.reset(new PercentHdl( aRegion )); 799 800 // as long as found and not at same position 801 while( *pSttPos <= *pEndPos ) 802 { 803 nFndRet = rParas.DoFind(*pCurrentCursor, fnMove, aRegion, bInReadOnly, xSearchItem); 804 if( 0 == nFndRet || 805 ( pFndRing && 806 *pFndRing->GetPoint() == *pCurrentCursor->GetPoint() && 807 *pFndRing->GetMark() == *pCurrentCursor->GetMark() )) 808 break; 809 if( !( FIND_NO_RING & nFndRet )) 810 { 811 // #i24084# - create ring similar to the one in CreateCursor 812 SwCursor* pNew = pCurrentCursor->Create( pFndRing ); 813 if( !pFndRing ) 814 pFndRing = pNew; 815 816 pNew->SetMark(); 817 *pNew->GetMark() = *pCurrentCursor->GetMark(); 818 } 819 820 ++nFound; 821 822 if( !( eFndRngs & FindRanges::InSelAll) ) 823 { 824 bEnd = true; 825 break; 826 } 827 828 if ((coSrchRplcThreshold == nFound) 829 && rDoc.GetIDocumentUndoRedo().DoesUndo() 830 && rParas.IsReplaceMode()) 831 { 832 short nRet = pCurrentCursor->MaxReplaceArived(); 833 if( RET_YES == nRet ) 834 { 835 rDoc.GetIDocumentUndoRedo().DelAllUndoObj(); 836 rDoc.GetIDocumentUndoRedo().DoUndo(false); 837 } 838 else 839 { 840 bEnd = true; 841 if(RET_CANCEL == nRet) 842 { 843 bCancel = true; 844 } 845 break; 846 } 847 } 848 849 if( bSrchBkwrd ) 850 // move pEndPos in front of the found area 851 *pEndPos = *pCurrentCursor->Start(); 852 else 853 // move pSttPos behind the found area 854 *pSttPos = *pCurrentCursor->End(); 855 856 if( *pSttPos == *pEndPos ) 857 // in area but at the end => done 858 break; 859 860 if( !nCursorCnt && pPHdl ) 861 { 862 pPHdl->NextPos( *aRegion.GetMark() ); 863 } 864 } 865 866 if( bEnd || !( eFndRngs & ( FindRanges::InSelAll | FindRanges::InSel )) ) 867 break; 868 869 pTmpCursor = pTmpCursor->GetNext(); 870 if( nCursorCnt && pPHdl ) 871 { 872 pPHdl->NextPos( ++pPHdl->nActPos ); 873 } 874 875 } while( pTmpCursor != pSaveCursor && pTmpCursor->GetNext() != pTmpCursor); 876 877 if( nFound && !pFndRing ) // if no ring should be created 878 pFndRing = pCurrentCursor->Create(); 879 880 rDoc.GetIDocumentUndoRedo().DoUndo(bDoesUndo); 881 return nFound; 882 } 883 884 static bool lcl_MakeSelFwrd( const SwNode& rSttNd, const SwNode& rEndNd, 885 SwPaM& rPam, bool bFirst ) 886 { 887 if( rSttNd.GetIndex() + 1 == rEndNd.GetIndex() ) 888 return false; 889 890 SwNodes& rNds = rPam.GetDoc().GetNodes(); 891 rPam.DeleteMark(); 892 SwContentNode* pCNd; 893 if( !bFirst ) 894 { 895 rPam.GetPoint()->Assign(rSttNd); 896 pCNd = rNds.GoNext( rPam.GetPoint() ); 897 if( !pCNd ) 898 return false; 899 rPam.GetPoint()->AssignStartIndex(*pCNd); 900 } 901 else if( rSttNd.GetIndex() > rPam.GetPoint()->GetNodeIndex() || 902 rPam.GetPoint()->GetNodeIndex() >= rEndNd.GetIndex() ) 903 // not in this section 904 return false; 905 906 rPam.SetMark(); 907 rPam.GetPoint()->Assign(rEndNd); 908 pCNd = SwNodes::GoPrevious( rPam.GetPoint() ); 909 if( !pCNd ) 910 return false; 911 rPam.GetPoint()->AssignEndIndex(*pCNd); 912 913 return *rPam.GetMark() < *rPam.GetPoint(); 914 } 915 916 static bool lcl_MakeSelBkwrd( const SwNode& rSttNd, const SwNode& rEndNd, 917 SwPaM& rPam, bool bFirst ) 918 { 919 if( rEndNd.GetIndex() + 1 == rSttNd.GetIndex() ) 920 return false; 921 922 SwNodes& rNds = rPam.GetDoc().GetNodes(); 923 rPam.DeleteMark(); 924 SwContentNode* pCNd; 925 if( !bFirst ) 926 { 927 rPam.GetPoint()->Assign(rSttNd); 928 pCNd = SwNodes::GoPrevious( rPam.GetPoint() ); 929 if( !pCNd ) 930 return false; 931 rPam.GetPoint()->AssignEndIndex(*pCNd); 932 } 933 else if( rEndNd.GetIndex() > rPam.GetPoint()->GetNodeIndex() || 934 rPam.GetPoint()->GetNodeIndex() >= rSttNd.GetIndex() ) 935 return false; // not in this section 936 937 rPam.SetMark(); 938 rPam.GetPoint()->Assign(rEndNd); 939 pCNd = rNds.GoNext( rPam.GetPoint() ); 940 if( !pCNd ) 941 return false; 942 rPam.GetPoint()->SetContent(0); 943 944 return *rPam.GetPoint() < *rPam.GetMark(); 945 } 946 947 // this method "searches" for all use cases because in SwFindParas is always the 948 // correct parameters and respective search method 949 sal_Int32 SwCursor::FindAll( SwFindParas& rParas, 950 SwDocPositions nStart, SwDocPositions nEnd, 951 FindRanges eFndRngs, bool& bCancel ) 952 { 953 bCancel = false; 954 SwCursorSaveState aSaveState( *this ); 955 956 // create region without adding it to the ring 957 SwPaM aRegion( *GetPoint() ); 958 SwMoveFnCollection const & fnMove = MakeFindRange( nStart, nEnd, &aRegion ); 959 960 sal_Int32 nFound = 0; 961 const bool bMvBkwrd = &fnMove == &fnMoveBackward; 962 bool bInReadOnly = IsReadOnlyAvailable(); 963 std::unique_ptr<SvxSearchItem> xSearchItem; 964 965 SwCursor* pFndRing = nullptr; 966 SwNodes& rNds = GetDoc().GetNodes(); 967 968 // search in sections? 969 if( FindRanges::InSel & eFndRngs ) 970 { 971 // if string was not found in region then get all sections (cursors 972 // stays unchanged) 973 nFound = lcl_FindSelection( rParas, this, fnMove, 974 pFndRing, aRegion, eFndRngs, 975 bInReadOnly, bCancel ); 976 if( 0 == nFound ) 977 return nFound; 978 979 // found string at least once; it's all in new Cursor ring thus delete old one 980 while( GetNext() != this ) 981 delete GetNext(); 982 983 *GetPoint() = *pFndRing->GetPoint(); 984 SetMark(); 985 *GetMark() = *pFndRing->GetMark(); 986 pFndRing->GetRingContainer().merge( GetRingContainer() ); 987 delete pFndRing; 988 } 989 else if( FindRanges::InOther & eFndRngs ) 990 { 991 // put cursor as copy of current into ring 992 // chaining points always to first created, so forward 993 SwCursor* pSav = Create( this ); // save the current cursor 994 995 // if already outside of body text search from this position or start at 996 // 1. base section 997 if( bMvBkwrd 998 ? lcl_MakeSelBkwrd( rNds.GetEndOfExtras(), 999 *rNds.GetEndOfPostIts().StartOfSectionNode(), 1000 *this, rNds.GetEndOfExtras().GetIndex() >= 1001 GetPoint()->GetNodeIndex() ) 1002 : lcl_MakeSelFwrd( *rNds.GetEndOfPostIts().StartOfSectionNode(), 1003 rNds.GetEndOfExtras(), *this, 1004 rNds.GetEndOfExtras().GetIndex() >= 1005 GetPoint()->GetNodeIndex() )) 1006 { 1007 nFound = lcl_FindSelection( rParas, this, fnMove, pFndRing, 1008 aRegion, eFndRngs, bInReadOnly, bCancel ); 1009 } 1010 1011 if( !nFound ) 1012 { 1013 // put back the old one 1014 *GetPoint() = *pSav->GetPoint(); 1015 if( pSav->HasMark() ) 1016 { 1017 SetMark(); 1018 *GetMark() = *pSav->GetMark(); 1019 } 1020 else 1021 DeleteMark(); 1022 return 0; 1023 } 1024 1025 if( !( FindRanges::InSelAll & eFndRngs )) 1026 { 1027 // there should only be a single one, thus add it 1028 // independent from search direction: SPoint is always bigger than 1029 // mark if the search area is valid 1030 *GetPoint() = *pFndRing->GetPoint(); 1031 SetMark(); 1032 *GetMark() = *pFndRing->GetMark(); 1033 } 1034 else 1035 { 1036 // found string at least once; it's all in new Cursor ring thus delete old one 1037 while( GetNext() != this ) 1038 delete GetNext(); 1039 1040 *GetPoint() = *pFndRing->GetPoint(); 1041 SetMark(); 1042 *GetMark() = *pFndRing->GetMark(); 1043 pFndRing->GetRingContainer().merge( GetRingContainer() ); 1044 } 1045 delete pFndRing; 1046 } 1047 else if( FindRanges::InSelAll & eFndRngs ) 1048 { 1049 SwCursor* pSav = Create( this ); // save the current cursor 1050 1051 const SwNode* pSttNd = ( FindRanges::InBodyOnly & eFndRngs ) 1052 ? rNds.GetEndOfContent().StartOfSectionNode() 1053 : rNds.GetEndOfPostIts().StartOfSectionNode(); 1054 1055 if( bMvBkwrd 1056 ? lcl_MakeSelBkwrd( rNds.GetEndOfContent(), *pSttNd, *this, false ) 1057 : lcl_MakeSelFwrd( *pSttNd, rNds.GetEndOfContent(), *this, false )) 1058 { 1059 nFound = lcl_FindSelection( rParas, this, fnMove, pFndRing, 1060 aRegion, eFndRngs, bInReadOnly, bCancel ); 1061 } 1062 1063 if( !nFound ) 1064 { 1065 // put back the old one 1066 *GetPoint() = *pSav->GetPoint(); 1067 if( pSav->HasMark() ) 1068 { 1069 SetMark(); 1070 *GetMark() = *pSav->GetMark(); 1071 } 1072 else 1073 DeleteMark(); 1074 return 0; 1075 } 1076 while( GetNext() != this ) 1077 delete GetNext(); 1078 1079 *GetPoint() = *pFndRing->GetPoint(); 1080 SetMark(); 1081 *GetMark() = *pFndRing->GetMark(); 1082 pFndRing->GetRingContainer().merge( GetRingContainer() ); 1083 delete pFndRing; 1084 } 1085 else 1086 { 1087 // if a GetMark is set then keep the GetMark of the found object 1088 // This allows spanning an area with this search. 1089 SwPosition aMarkPos( *GetMark() ); 1090 const bool bMarkPos = HasMark() && (eFndRngs == FindRanges::InBody); 1091 1092 nFound = rParas.DoFind(*this, fnMove, aRegion, bInReadOnly, xSearchItem) ? 1 : 0; 1093 if (0 != nFound && bMarkPos) 1094 *GetMark() = aMarkPos; 1095 } 1096 1097 if( nFound && SwCursor::IsSelOvr( SwCursorSelOverFlags::Toggle ) ) 1098 nFound = 0; 1099 return nFound; 1100 } 1101 1102 void SwCursor::FillFindPos( SwDocPositions ePos, SwPosition& rPos ) const 1103 { 1104 bool bIsStart = true; 1105 SwContentNode* pCNd = nullptr; 1106 SwNodes& rNds = GetDoc().GetNodes(); 1107 1108 switch( ePos ) 1109 { 1110 case SwDocPositions::Start: 1111 rPos.Assign(*rNds.GetEndOfContent().StartOfSectionNode()); 1112 pCNd = rNds.GoNext( &rPos ); 1113 break; 1114 case SwDocPositions::End: 1115 rPos.Assign(rNds.GetEndOfContent()); 1116 pCNd = SwNodes::GoPrevious( &rPos ); 1117 bIsStart = false; 1118 break; 1119 case SwDocPositions::OtherStart: 1120 rPos.Assign( *rNds[ SwNodeOffset(0) ] ); 1121 pCNd = rNds.GoNext( &rPos ); 1122 break; 1123 case SwDocPositions::OtherEnd: 1124 rPos.Assign( *rNds.GetEndOfContent().StartOfSectionNode() ); 1125 pCNd = SwNodes::GoPrevious( &rPos ); 1126 bIsStart = false; 1127 break; 1128 default: 1129 rPos = *GetPoint(); 1130 } 1131 1132 if( pCNd && !bIsStart ) 1133 { 1134 rPos.AssignEndIndex( *pCNd ); 1135 } 1136 } 1137 1138 short SwCursor::MaxReplaceArived() 1139 { 1140 return RET_YES; 1141 } 1142 1143 namespace { 1144 1145 struct HideWrapper 1146 { 1147 // either the frame's text or the node's text (possibly pre-filtered) 1148 OUString const* m_pText; 1149 // this is actually a TextFrameIndex but all of the i18n code uses sal_Int32 1150 sal_Int32 m_nPtIndex; 1151 // if mapping is needed, use this frame 1152 SwTextFrame * m_pFrame; 1153 // input in the constructor, output (via mapping) in the destructor 1154 SwTextNode *& m_rpTextNode; 1155 sal_Int32 & m_rPtPos; 1156 1157 HideWrapper(SwRootFrame const*const pLayout, 1158 SwTextNode *& rpTextNode, sal_Int32 & rPtPos, 1159 OUString const*const pFilteredNodeText = nullptr) 1160 : m_pText(pFilteredNodeText) 1161 , m_pFrame(nullptr) 1162 , m_rpTextNode(rpTextNode) 1163 , m_rPtPos(rPtPos) 1164 { 1165 if (pLayout && pLayout->HasMergedParas()) 1166 { 1167 m_pFrame = static_cast<SwTextFrame*>(rpTextNode->getLayoutFrame(pLayout)); 1168 m_pText = &m_pFrame->GetText(); 1169 m_nPtIndex = sal_Int32(m_pFrame->MapModelToView(rpTextNode, rPtPos)); 1170 } 1171 else 1172 { 1173 if (!m_pText) 1174 { 1175 m_pText = &rpTextNode->GetText(); 1176 } 1177 m_nPtIndex = rPtPos; 1178 } 1179 } 1180 ~HideWrapper() 1181 { 1182 AssignBack(m_rpTextNode, m_rPtPos); 1183 } 1184 void AssignBack(SwTextNode *& rpTextNode, sal_Int32 & rPtPos) 1185 { 1186 if (0 <= m_nPtIndex && m_pFrame) 1187 { 1188 std::pair<SwTextNode*, sal_Int32> const pos( 1189 m_pFrame->MapViewToModel(TextFrameIndex(m_nPtIndex))); 1190 rpTextNode = pos.first; 1191 rPtPos = pos.second; 1192 } 1193 else 1194 { 1195 rPtPos = m_nPtIndex; 1196 } 1197 } 1198 }; 1199 1200 } // namespace 1201 1202 bool SwCursor::SelectWord( SwViewShell const * pViewShell, const Point* pPt ) 1203 { 1204 return SelectWordWT( pViewShell, WordType::ANYWORD_IGNOREWHITESPACES, pPt ); 1205 } 1206 1207 bool SwCursor::IsStartWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout) const 1208 { 1209 bool bRet = false; 1210 SwTextNode* pTextNd = GetPointNode().GetTextNode(); 1211 if (pTextNd) 1212 { 1213 sal_Int32 nPtPos = GetPoint()->GetContentIndex(); 1214 1215 HideWrapper w(pLayout, pTextNd, nPtPos); 1216 1217 bRet = g_pBreakIt->GetBreakIter()->isBeginWord( 1218 *w.m_pText, w.m_nPtIndex, 1219 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos )), 1220 nWordType ); 1221 } 1222 return bRet; 1223 } 1224 1225 bool SwCursor::IsEndWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout) const 1226 { 1227 bool bRet = false; 1228 SwTextNode* pTextNd = GetPointNode().GetTextNode(); 1229 if (pTextNd) 1230 { 1231 sal_Int32 nPtPos = GetPoint()->GetContentIndex(); 1232 1233 HideWrapper w(pLayout, pTextNd, nPtPos); 1234 1235 bRet = g_pBreakIt->GetBreakIter()->isEndWord( 1236 *w.m_pText, w.m_nPtIndex, 1237 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ), 1238 nWordType ); 1239 1240 } 1241 return bRet; 1242 } 1243 1244 bool SwCursor::IsInWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout) const 1245 { 1246 bool bRet = false; 1247 SwTextNode* pTextNd = GetPointNode().GetTextNode(); 1248 if (pTextNd) 1249 { 1250 sal_Int32 nPtPos = GetPoint()->GetContentIndex(); 1251 1252 { 1253 HideWrapper w(pLayout, pTextNd, nPtPos); 1254 1255 Boundary aBoundary = g_pBreakIt->GetBreakIter()->getWordBoundary( 1256 *w.m_pText, w.m_nPtIndex, 1257 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ), 1258 nWordType, 1259 true ); 1260 1261 bRet = aBoundary.startPos != aBoundary.endPos && 1262 aBoundary.startPos <= w.m_nPtIndex && 1263 w.m_nPtIndex <= aBoundary.endPos; 1264 w.m_nPtIndex = aBoundary.startPos; // hack: convert startPos back... 1265 } 1266 if(bRet) 1267 { 1268 const CharClass& rCC = GetAppCharClass(); 1269 bRet = rCC.isLetterNumeric(pTextNd->GetText(), nPtPos); 1270 } 1271 } 1272 return bRet; 1273 } 1274 1275 bool SwCursor::IsStartEndSentence(bool bEnd, SwRootFrame const*const pLayout) const 1276 { 1277 bool bRet = bEnd ? 1278 GetPointContentNode() && GetPoint()->GetContentIndex() == GetPointContentNode()->Len() : 1279 GetPoint()->GetContentIndex() == 0; 1280 1281 if ((pLayout != nullptr && pLayout->HasMergedParas()) || !bRet) 1282 { 1283 SwCursor aCursor(*GetPoint(), nullptr); 1284 SwPosition aOrigPos = *aCursor.GetPoint(); 1285 aCursor.GoSentence(bEnd ? SwCursor::END_SENT : SwCursor::START_SENT, pLayout); 1286 bRet = aOrigPos == *aCursor.GetPoint(); 1287 } 1288 return bRet; 1289 } 1290 1291 bool SwCursor::GoStartWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout) 1292 { 1293 bool bRet = false; 1294 SwTextNode* pTextNd = GetPointNode().GetTextNode(); 1295 if (pTextNd) 1296 { 1297 SwCursorSaveState aSave( *this ); 1298 sal_Int32 nPtPos = GetPoint()->GetContentIndex(); 1299 1300 { 1301 HideWrapper w(pLayout, pTextNd, nPtPos); 1302 1303 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->getWordBoundary( 1304 *w.m_pText, w.m_nPtIndex, 1305 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ), 1306 nWordType, 1307 false ).startPos; 1308 } 1309 1310 if (nPtPos < pTextNd->GetText().getLength() && nPtPos >= 0) 1311 { 1312 GetPoint()->Assign(*pTextNd, nPtPos); 1313 if( !IsSelOvr() ) 1314 bRet = true; 1315 } 1316 } 1317 return bRet; 1318 } 1319 1320 bool SwCursor::GoEndWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout) 1321 { 1322 bool bRet = false; 1323 SwTextNode* pTextNd = GetPointNode().GetTextNode(); 1324 if (pTextNd) 1325 { 1326 SwCursorSaveState aSave( *this ); 1327 sal_Int32 nPtPos = GetPoint()->GetContentIndex(); 1328 1329 { 1330 HideWrapper w(pLayout, pTextNd, nPtPos); 1331 1332 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->getWordBoundary( 1333 *w.m_pText, w.m_nPtIndex, 1334 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ), 1335 nWordType, 1336 true ).endPos; 1337 } 1338 1339 if (nPtPos <= pTextNd->GetText().getLength() && nPtPos >= 0 && 1340 GetPoint()->GetContentIndex() != nPtPos ) 1341 { 1342 GetPoint()->Assign(*pTextNd, nPtPos); 1343 if( !IsSelOvr() ) 1344 bRet = true; 1345 } 1346 } 1347 return bRet; 1348 } 1349 1350 bool SwCursor::GoNextWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout) 1351 { 1352 bool bRet = false; 1353 SwTextNode* pTextNd = GetPointNode().GetTextNode(); 1354 if (pTextNd) 1355 { 1356 SwCursorSaveState aSave( *this ); 1357 sal_Int32 nPtPos = GetPoint()->GetContentIndex(); 1358 1359 { 1360 HideWrapper w(pLayout, pTextNd, nPtPos); 1361 1362 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->nextWord( 1363 *w.m_pText, w.m_nPtIndex, 1364 g_pBreakIt->GetLocale( pTextNd->GetLang(nPtPos, 1) ), 1365 nWordType ).startPos; 1366 } 1367 1368 if (nPtPos <= pTextNd->GetText().getLength() && nPtPos >= 0) 1369 { 1370 GetPoint()->Assign(*pTextNd, nPtPos); 1371 if( !IsSelOvr() ) 1372 bRet = true; 1373 } 1374 } 1375 return bRet; 1376 } 1377 1378 bool SwCursor::GoPrevWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout) 1379 { 1380 bool bRet = false; 1381 SwTextNode* pTextNd = GetPointNode().GetTextNode(); 1382 if (pTextNd) 1383 { 1384 SwCursorSaveState aSave( *this ); 1385 sal_Int32 nPtPos = GetPoint()->GetContentIndex(); 1386 1387 { 1388 HideWrapper w(pLayout, pTextNd, nPtPos); 1389 1390 const sal_Int32 nPtStart = w.m_nPtIndex; 1391 if (w.m_nPtIndex) 1392 { 1393 --w.m_nPtIndex; 1394 w.AssignBack(pTextNd, nPtPos); 1395 } 1396 1397 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->previousWord( 1398 *w.m_pText, nPtStart, 1399 g_pBreakIt->GetLocale( pTextNd->GetLang(nPtPos, 1) ), 1400 nWordType ).startPos; 1401 } 1402 1403 if (nPtPos < pTextNd->GetText().getLength() && nPtPos >= 0) 1404 { 1405 GetPoint()->Assign(*pTextNd, nPtPos); 1406 if( !IsSelOvr() ) 1407 bRet = true; 1408 } 1409 } 1410 return bRet; 1411 } 1412 1413 bool SwCursor::SelectWordWT( SwViewShell const * pViewShell, sal_Int16 nWordType, const Point* pPt ) 1414 { 1415 SwCursorSaveState aSave( *this ); 1416 1417 bool bRet = false; 1418 DeleteMark(); 1419 const SwRootFrame* pLayout = pViewShell->GetLayout(); 1420 if( pPt && nullptr != pLayout ) 1421 { 1422 // set the cursor to the layout position 1423 Point aPt( *pPt ); 1424 pLayout->GetModelPositionForViewPoint( GetPoint(), aPt ); 1425 } 1426 1427 SwTextNode* pTextNd = GetPointNode().GetTextNode(); 1428 if (pTextNd) 1429 { 1430 // Should we select the whole fieldmark? 1431 const IDocumentMarkAccess* pMarksAccess = GetDoc().getIDocumentMarkAccess( ); 1432 sw::mark::IFieldmark const*const pMark(pMarksAccess->getInnerFieldmarkFor(*GetPoint())); 1433 if (pMark && (IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK 1434 || IDocumentMarkAccess::GetType(*pMark) == IDocumentMarkAccess::MarkType::DATE_FIELDMARK)) 1435 { 1436 *GetPoint() = sw::mark::FindFieldSep(*pMark); 1437 GetPoint()->AdjustContent(+1); // Don't select the separator 1438 1439 const SwPosition& rEnd = pMark->GetMarkEnd(); 1440 1441 assert(pMark->GetMarkEnd() != *GetPoint()); 1442 SetMark(); 1443 *GetMark() = rEnd; 1444 GetMark()->AdjustContent(-1); // Don't select the end delimiter 1445 1446 bRet = true; 1447 } 1448 else 1449 { 1450 bool bForward = true; 1451 sal_Int32 nPtPos = GetPoint()->GetContentIndex(); 1452 1453 HideWrapper w(pViewShell->GetLayout(), pTextNd, nPtPos); 1454 1455 Boundary aBndry( g_pBreakIt->GetBreakIter()->getWordBoundary( 1456 *w.m_pText, w.m_nPtIndex, 1457 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ), 1458 nWordType, 1459 bForward )); 1460 1461 if (comphelper::LibreOfficeKit::isActive() && aBndry.startPos == aBndry.endPos && w.m_nPtIndex > 0) 1462 { 1463 // nPtPos is the end of the paragraph, select the last word then. 1464 --w.m_nPtIndex; 1465 w.AssignBack(pTextNd, nPtPos); 1466 1467 aBndry = g_pBreakIt->GetBreakIter()->getWordBoundary( 1468 *w.m_pText, w.m_nPtIndex, 1469 g_pBreakIt->GetLocale( pTextNd->GetLang( nPtPos ) ), 1470 nWordType, 1471 bForward ); 1472 1473 } 1474 1475 SwTextNode * pStartNode(pTextNd); 1476 sal_Int32 nStartIndex; 1477 w.m_nPtIndex = aBndry.startPos; 1478 w.AssignBack(pStartNode, nStartIndex); 1479 1480 SwTextNode * pEndNode(pTextNd); 1481 sal_Int32 nEndIndex; 1482 w.m_nPtIndex = aBndry.endPos; 1483 w.AssignBack(pEndNode, nEndIndex); 1484 1485 if( aBndry.startPos != aBndry.endPos ) 1486 { 1487 GetPoint()->Assign(*pEndNode, nEndIndex); 1488 if( !IsSelOvr() ) 1489 { 1490 SetMark(); 1491 GetMark()->Assign(*pStartNode, nStartIndex); 1492 if (sw::mark::IMark* pAnnotationMark = pMarksAccess->getAnnotationMarkFor(*GetPoint())) 1493 { 1494 // An annotation mark covers the selected word. Check 1495 // if it covers only the word: in that case we select 1496 // the comment anchor as well. 1497 bool bStartMatch = GetMark()->GetNode() == pAnnotationMark->GetMarkStart().GetNode() && 1498 GetMark()->GetContentIndex() == pAnnotationMark->GetMarkStart().GetContentIndex(); 1499 bool bEndMatch = GetPoint()->GetNode() == pAnnotationMark->GetMarkEnd().GetNode() && 1500 GetPoint()->GetContentIndex() + 1 == pAnnotationMark->GetMarkEnd().GetContentIndex(); 1501 if (bStartMatch && bEndMatch) 1502 GetPoint()->AdjustContent(+1); 1503 } 1504 if( !IsSelOvr() ) 1505 bRet = true; 1506 } 1507 } 1508 } 1509 } 1510 1511 if( !bRet ) 1512 { 1513 DeleteMark(); 1514 RestoreSavePos(); 1515 } 1516 return bRet; 1517 } 1518 1519 static OUString lcl_MaskDeletedRedlines( const SwTextNode* pTextNd ) 1520 { 1521 OUString aRes; 1522 if (pTextNd) 1523 { 1524 //mask deleted redlines 1525 OUString sNodeText(pTextNd->GetText()); 1526 const SwDoc& rDoc = pTextNd->GetDoc(); 1527 const bool bShowChg = IDocumentRedlineAccess::IsShowChanges( rDoc.getIDocumentRedlineAccess().GetRedlineFlags() ); 1528 if ( bShowChg ) 1529 { 1530 SwRedlineTable::size_type nAct = rDoc.getIDocumentRedlineAccess().GetRedlinePos( *pTextNd, RedlineType::Any ); 1531 for ( ; nAct < rDoc.getIDocumentRedlineAccess().GetRedlineTable().size(); nAct++ ) 1532 { 1533 const SwRangeRedline* pRed = rDoc.getIDocumentRedlineAccess().GetRedlineTable()[ nAct ]; 1534 if ( pRed->Start()->GetNode() > *pTextNd ) 1535 break; 1536 1537 if( RedlineType::Delete == pRed->GetType() ) 1538 { 1539 sal_Int32 nStart, nEnd; 1540 pRed->CalcStartEnd( pTextNd->GetIndex(), nStart, nEnd ); 1541 1542 while ( nStart < nEnd && nStart < sNodeText.getLength() ) 1543 sNodeText = sNodeText.replaceAt( nStart++, 1, rtl::OUStringChar(CH_TXTATR_INWORD) ); 1544 } 1545 } 1546 } 1547 aRes = sNodeText; 1548 } 1549 return aRes; 1550 } 1551 1552 bool SwCursor::GoSentence(SentenceMoveType eMoveType, SwRootFrame const*const pLayout) 1553 { 1554 bool bRet = false; 1555 SwTextNode* pTextNd = GetPointNode().GetTextNode(); 1556 if (pTextNd) 1557 { 1558 OUString const sNodeText(lcl_MaskDeletedRedlines(pTextNd)); 1559 1560 SwCursorSaveState aSave( *this ); 1561 sal_Int32 nPtPos = GetPoint()->GetContentIndex(); 1562 1563 { 1564 HideWrapper w(pLayout, pTextNd, nPtPos, &sNodeText); 1565 1566 switch ( eMoveType ) 1567 { 1568 case START_SENT: /* when modifying: see also ExpandToSentenceBorders below! */ 1569 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->beginOfSentence( 1570 *w.m_pText, w.m_nPtIndex, 1571 g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos))); 1572 break; 1573 case END_SENT: /* when modifying: see also ExpandToSentenceBorders below! */ 1574 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->endOfSentence( 1575 *w.m_pText, w.m_nPtIndex, 1576 g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos))); 1577 break; 1578 case NEXT_SENT: 1579 { 1580 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->endOfSentence( 1581 *w.m_pText, w.m_nPtIndex, 1582 g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos))); 1583 if (w.m_nPtIndex >= 0 && w.m_nPtIndex < w.m_pText->getLength()) 1584 { 1585 do 1586 { 1587 ++w.m_nPtIndex; 1588 } 1589 while (w.m_nPtIndex < w.m_pText->getLength() 1590 && (*w.m_pText)[w.m_nPtIndex] == ' '); 1591 } 1592 break; 1593 } 1594 case PREV_SENT: 1595 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->beginOfSentence( 1596 *w.m_pText, w.m_nPtIndex, 1597 g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos))); 1598 1599 if (w.m_nPtIndex == 0) 1600 return false; // the previous sentence is not in this paragraph 1601 if (w.m_nPtIndex > 0) 1602 { 1603 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->beginOfSentence( 1604 *w.m_pText, w.m_nPtIndex - 1, 1605 g_pBreakIt->GetLocale(pTextNd->GetLang(nPtPos))); 1606 } 1607 break; 1608 } 1609 } 1610 1611 // it is allowed to place the PaM just behind the last 1612 // character in the text thus <= ...Len 1613 if (nPtPos <= pTextNd->GetText().getLength() && nPtPos >= 0) 1614 { 1615 GetPoint()->Assign(*pTextNd, nPtPos); 1616 if( !IsSelOvr() ) 1617 bRet = true; 1618 } 1619 } 1620 return bRet; 1621 } 1622 1623 void SwCursor::ExpandToSentenceBorders(SwRootFrame const*const pLayout) 1624 { 1625 SwTextNode* pStartNd = Start()->GetNode().GetTextNode(); 1626 SwTextNode* pEndNd = End()->GetNode().GetTextNode(); 1627 if (!pStartNd || !pEndNd) 1628 return; 1629 1630 if (!HasMark()) 1631 SetMark(); 1632 1633 OUString sStartText( lcl_MaskDeletedRedlines( pStartNd ) ); 1634 OUString sEndText( pStartNd == pEndNd? sStartText : lcl_MaskDeletedRedlines( pEndNd ) ); 1635 1636 SwCursorSaveState aSave( *this ); 1637 sal_Int32 nStartPos = Start()->GetContentIndex(); 1638 sal_Int32 nEndPos = End()->GetContentIndex(); 1639 1640 { 1641 HideWrapper w(pLayout, pStartNd, nStartPos, &sStartText); 1642 1643 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->beginOfSentence( 1644 *w.m_pText, w.m_nPtIndex, 1645 g_pBreakIt->GetLocale( pStartNd->GetLang( nStartPos ) ) ); 1646 } 1647 { 1648 HideWrapper w(pLayout, pEndNd, nEndPos, &sEndText); 1649 1650 w.m_nPtIndex = g_pBreakIt->GetBreakIter()->endOfSentence( 1651 *w.m_pText, w.m_nPtIndex, 1652 g_pBreakIt->GetLocale( pEndNd->GetLang( nEndPos ) ) ); 1653 } 1654 1655 // it is allowed to place the PaM just behind the last 1656 // character in the text thus <= ...Len 1657 if (nStartPos <= pStartNd->GetText().getLength() && nStartPos >= 0) 1658 { 1659 GetMark()->Assign(*pStartNd, nStartPos); 1660 } 1661 if (nEndPos <= pEndNd->GetText().getLength() && nEndPos >= 0) 1662 { 1663 GetPoint()->Assign(*pEndNd, nEndPos); 1664 } 1665 } 1666 1667 bool SwTableCursor::LeftRight( bool bLeft, sal_uInt16 nCnt, SwCursorSkipMode /*nMode*/, 1668 bool /*bVisualAllowed*/, bool /*bSkipHidden*/, bool /*bInsertCursor*/, 1669 SwRootFrame const*, bool /*isFieldNames*/) 1670 { 1671 return bLeft ? GoPrevCell( nCnt ) 1672 : GoNextCell( nCnt ); 1673 } 1674 1675 // calculate cursor bidi level: extracted from LeftRight() 1676 const SwContentFrame* 1677 SwCursor::DoSetBidiLevelLeftRight( 1678 bool & io_rbLeft, bool bVisualAllowed, bool bInsertCursor) 1679 { 1680 // calculate cursor bidi level 1681 const SwContentFrame* pSttFrame = nullptr; 1682 SwNode& rNode = GetPoint()->GetNode(); 1683 1684 if( rNode.IsTextNode() ) 1685 { 1686 const SwTextNode& rTNd = *rNode.GetTextNode(); 1687 sal_Int32 nPos = GetPoint()->GetContentIndex(); 1688 1689 if ( bVisualAllowed && SvtCTLOptions::IsCTLFontEnabled() && 1690 SvtCTLOptions::MOVEMENT_VISUAL == SvtCTLOptions::GetCTLCursorMovement() ) 1691 { 1692 // for visual cursor travelling (used in bidi layout) 1693 // we first have to convert the logic to a visual position 1694 Point aPt; 1695 std::pair<Point, bool> const tmp(aPt, true); 1696 pSttFrame = rTNd.getLayoutFrame( 1697 GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), 1698 GetPoint(), &tmp); 1699 if( pSttFrame ) 1700 { 1701 sal_uInt8 nCursorLevel = GetCursorBidiLevel(); 1702 bool bForward = ! io_rbLeft; 1703 SwTextFrame *const pTF(const_cast<SwTextFrame*>( 1704 static_cast<const SwTextFrame*>(pSttFrame))); 1705 TextFrameIndex nTFIndex(pTF->MapModelToViewPos(*GetPoint())); 1706 pTF->PrepareVisualMove( nTFIndex, nCursorLevel, 1707 bForward, bInsertCursor ); 1708 *GetPoint() = pTF->MapViewToModelPos(nTFIndex); 1709 SetCursorBidiLevel( nCursorLevel ); 1710 io_rbLeft = ! bForward; 1711 } 1712 } 1713 else 1714 { 1715 SwTextFrame const* pFrame; 1716 const SwScriptInfo* pSI = SwScriptInfo::GetScriptInfo(rTNd, &pFrame); 1717 if ( pSI ) 1718 { 1719 const sal_Int32 nMoveOverPos = io_rbLeft ? 1720 ( nPos ? nPos - 1 : 0 ) : 1721 nPos; 1722 TextFrameIndex nIndex(pFrame->MapModelToView(&rTNd, nMoveOverPos)); 1723 SetCursorBidiLevel( pSI->DirType(nIndex) ); 1724 } 1725 } 1726 } 1727 return pSttFrame; 1728 } 1729 1730 bool SwCursor::LeftRight( bool bLeft, sal_uInt16 nCnt, SwCursorSkipMode nMode, 1731 bool bVisualAllowed,bool bSkipHidden, bool bInsertCursor, 1732 SwRootFrame const*const pLayout, bool isFieldNames) 1733 { 1734 // calculate cursor bidi level 1735 SwNode& rNode = GetPoint()->GetNode(); 1736 const SwContentFrame* pSttFrame = // may side-effect bLeft! 1737 DoSetBidiLevelLeftRight(bLeft, bVisualAllowed, bInsertCursor); 1738 1739 // can the cursor be moved n times? 1740 SwCursorSaveState aSave( *this ); 1741 SwMoveFnCollection const & fnMove = bLeft ? fnMoveBackward : fnMoveForward; 1742 1743 SwGoInDoc fnGo; 1744 if ( bSkipHidden ) 1745 fnGo = SwCursorSkipMode::Cells == nMode ? GoInContentCellsSkipHidden : GoInContentSkipHidden; 1746 else 1747 fnGo = SwCursorSkipMode::Cells == nMode ? GoInContentCells : GoInContent; 1748 1749 SwTextFrame const* pFrame(nullptr); 1750 if (pLayout) 1751 { 1752 pFrame = static_cast<SwTextFrame*>(rNode.GetContentNode()->getLayoutFrame(pLayout)); 1753 if (pFrame) 1754 { 1755 while (pFrame->GetPrecede()) 1756 { 1757 pFrame = static_cast<SwTextFrame const*>(pFrame->GetPrecede()); 1758 } 1759 } 1760 } 1761 1762 while( nCnt ) 1763 { 1764 SwNodeIndex aOldNodeIdx( GetPoint()->GetNode() ); 1765 1766 TextFrameIndex beforeIndex(-1); 1767 if (pFrame) 1768 { 1769 beforeIndex = pFrame->MapModelToViewPos(*GetPoint()); 1770 } 1771 1772 if (!bLeft && pLayout && pLayout->GetFieldmarkMode() == sw::FieldmarkMode::ShowResult) 1773 { 1774 SwTextNode const*const pNode(GetPoint()->GetNode().GetTextNode()); 1775 assert(pNode); 1776 if (pNode->Len() != GetPoint()->GetContentIndex() 1777 && pNode->GetText()[GetPoint()->GetContentIndex()] == CH_TXT_ATR_FIELDSTART) 1778 { 1779 IDocumentMarkAccess const& rIDMA(*GetDoc().getIDocumentMarkAccess()); 1780 sw::mark::IFieldmark const*const pMark(rIDMA.getFieldmarkAt(*GetPoint())); 1781 assert(pMark); 1782 *GetPoint() = sw::mark::FindFieldSep(*pMark); 1783 } 1784 } 1785 1786 if ( !Move( fnMove, fnGo ) ) 1787 { 1788 const SwEditShell* pSh = GetDoc().GetEditShell(); 1789 const SwViewOption* pViewOptions = pSh ? pSh->GetViewOptions() : nullptr; 1790 if (pViewOptions && pViewOptions->IsShowOutlineContentVisibilityButton()) 1791 { 1792 // Fixes crash that occurs in documents with outline content folded at the end of 1793 // the document. When the cursor is at the end of the visible document and 1794 // right arrow key is pressed Move fails after moving the cursor to the 1795 // end of the document model, which doesn't have a node frame and causes 1796 // weird numbers to be displayed in the statusbar page number count. Left 1797 // arrow, when in this state, causes a crash without RestoredSavePos() added here. 1798 RestoreSavePos(); 1799 } 1800 break; 1801 } 1802 1803 if (pFrame) 1804 { 1805 SwTextFrame const* pNewFrame(static_cast<SwTextFrame const*>( 1806 GetPoint()->GetNode().GetContentNode()->getLayoutFrame(pLayout))); 1807 if (pNewFrame) 1808 { 1809 while (pNewFrame->GetPrecede()) 1810 { 1811 pNewFrame = static_cast<SwTextFrame const*>(pNewFrame->GetPrecede()); 1812 } 1813 } 1814 // sw_redlinehide: fully redline-deleted nodes don't have frames... 1815 if (pFrame == pNewFrame || !pNewFrame) 1816 { 1817 if (!pNewFrame || beforeIndex == pFrame->MapModelToViewPos(*GetPoint())) 1818 { 1819 continue; // moving inside delete redline, doesn't count... 1820 } 1821 } 1822 else 1823 { 1824 // assume iteration is stable & returns the same frame 1825 assert(!pFrame->IsAnFollow(pNewFrame) && !pNewFrame->IsAnFollow(pFrame)); 1826 pFrame = pNewFrame; 1827 } 1828 } 1829 1830 if (bLeft && pLayout && pLayout->GetFieldmarkMode() == sw::FieldmarkMode::ShowCommand) 1831 { 1832 SwTextNode const*const pNode(GetPoint()->GetNode().GetTextNode()); 1833 assert(pNode); 1834 if (pNode->Len() != GetPoint()->GetContentIndex() 1835 && pNode->GetText()[GetPoint()->GetContentIndex()] == CH_TXT_ATR_FIELDEND) 1836 { 1837 IDocumentMarkAccess const& rIDMA(*GetDoc().getIDocumentMarkAccess()); 1838 sw::mark::IFieldmark const*const pMark(rIDMA.getFieldmarkAt(*GetPoint())); 1839 assert(pMark); 1840 *GetPoint() = sw::mark::FindFieldSep(*pMark); 1841 } 1842 } 1843 1844 if (isFieldNames) 1845 { 1846 SwTextNode const*const pNode(GetPoint()->GetNode().GetTextNode()); 1847 assert(pNode); 1848 SwTextAttr const*const pInputField(pNode->GetTextAttrAt( 1849 GetPoint()->GetContentIndex(), RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent)); 1850 if (pInputField) 1851 { 1852 continue; // skip over input fields 1853 } 1854 } 1855 1856 // If we were located inside a covered cell but our position has been 1857 // corrected, we check if the last move has moved the cursor to a 1858 // different table cell. In this case we set the cursor to the stored 1859 // covered position and redo the move: 1860 if (m_nRowSpanOffset) 1861 { 1862 const SwNode* pOldTabBoxSttNode = aOldNodeIdx.GetNode().FindTableBoxStartNode(); 1863 const SwTableNode* pOldTabSttNode = pOldTabBoxSttNode ? pOldTabBoxSttNode->FindTableNode() : nullptr; 1864 const SwNode* pNewTabBoxSttNode = GetPoint()->GetNode().FindTableBoxStartNode(); 1865 const SwTableNode* pNewTabSttNode = pNewTabBoxSttNode ? pNewTabBoxSttNode->FindTableNode() : nullptr; 1866 1867 const bool bCellChanged = pOldTabSttNode && pNewTabSttNode && 1868 pOldTabSttNode == pNewTabSttNode && 1869 pOldTabBoxSttNode && pNewTabBoxSttNode && 1870 pOldTabBoxSttNode != pNewTabBoxSttNode; 1871 1872 if ( bCellChanged ) 1873 { 1874 // Set cursor to start/end of covered cell: 1875 SwTableBox* pTableBox = pOldTabBoxSttNode->GetTableBox(); 1876 if ( pTableBox && pTableBox->getRowSpan() > 1 ) 1877 { 1878 pTableBox = & pTableBox->FindEndOfRowSpan( 1879 pOldTabSttNode->GetTable(), 1880 o3tl::narrowing<sal_uInt16>(pTableBox->getRowSpan() + m_nRowSpanOffset)); 1881 SwPosition& rPtPos = *GetPoint(); 1882 SwNodeIndex aNewIdx( *pTableBox->GetSttNd() ); 1883 rPtPos.Assign( aNewIdx ); 1884 1885 GetDoc().GetNodes().GoNextSection( &rPtPos, false, false ); 1886 SwContentNode* pContentNode = GetPointContentNode(); 1887 if ( pContentNode ) 1888 { 1889 GetPoint()->SetContent( bLeft ? pContentNode->Len() : 0 ); 1890 1891 // Redo the move: 1892 if ( !Move( fnMove, fnGo ) ) 1893 break; 1894 } 1895 } 1896 m_nRowSpanOffset = 0; 1897 } 1898 } 1899 1900 // Check if I'm inside a covered cell. Correct cursor if necessary and 1901 // store covered cell: 1902 const SwNode* pTableBoxStartNode = GetPoint()->GetNode().FindTableBoxStartNode(); 1903 if ( pTableBoxStartNode ) 1904 { 1905 const SwTableBox* pTableBox = pTableBoxStartNode->GetTableBox(); 1906 if ( pTableBox && pTableBox->getRowSpan() < 1 ) 1907 { 1908 // Store the row span offset: 1909 m_nRowSpanOffset = pTableBox->getRowSpan(); 1910 1911 // Move cursor to non-covered cell: 1912 const SwTableNode* pTableNd = pTableBoxStartNode->FindTableNode(); 1913 pTableBox = & pTableBox->FindStartOfRowSpan( pTableNd->GetTable() ); 1914 SwPosition& rPtPos = *GetPoint(); 1915 SwNodeIndex aNewIdx( *pTableBox->GetSttNd() ); 1916 rPtPos.Assign( aNewIdx ); 1917 1918 GetDoc().GetNodes().GoNextSection( &rPtPos, false, false ); 1919 SwContentNode* pContentNode = GetPointContentNode(); 1920 if ( pContentNode ) 1921 { 1922 GetPoint()->SetContent( bLeft ? pContentNode->Len() : 0 ); 1923 } 1924 } 1925 } 1926 --nCnt; 1927 } 1928 1929 // here come some special rules for visual cursor travelling 1930 if ( pSttFrame ) 1931 { 1932 SwNode& rTmpNode = GetPoint()->GetNode(); 1933 if ( &rTmpNode != &rNode && rTmpNode.IsTextNode() ) 1934 { 1935 Point aPt; 1936 std::pair<Point, bool> const tmp(aPt, true); 1937 const SwContentFrame* pEndFrame = rTmpNode.GetTextNode()->getLayoutFrame( 1938 GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), 1939 GetPoint(), &tmp); 1940 if ( pEndFrame ) 1941 { 1942 if ( ! pEndFrame->IsRightToLeft() != ! pSttFrame->IsRightToLeft() ) 1943 { 1944 if ( ! bLeft ) 1945 pEndFrame->RightMargin( this ); 1946 else 1947 pEndFrame->LeftMargin( this ); 1948 } 1949 } 1950 } 1951 } 1952 1953 return 0 == nCnt && !IsInProtectTable( true ) && 1954 !IsSelOvr( SwCursorSelOverFlags::Toggle | 1955 SwCursorSelOverFlags::ChangePos ); 1956 } 1957 1958 // calculate cursor bidi level: extracted from UpDown() 1959 void SwCursor::DoSetBidiLevelUpDown() 1960 { 1961 SwNode& rNode = GetPoint()->GetNode(); 1962 if ( !rNode.IsTextNode() ) 1963 return; 1964 1965 SwTextFrame const* pFrame; 1966 const SwScriptInfo* pSI = 1967 SwScriptInfo::GetScriptInfo( *rNode.GetTextNode(), &pFrame ); 1968 if ( !pSI ) 1969 return; 1970 1971 const sal_Int32 nPos = GetPoint()->GetContentIndex(); 1972 1973 if (!(nPos && nPos < rNode.GetTextNode()->GetText().getLength())) 1974 return; 1975 1976 TextFrameIndex const nIndex(pFrame->MapModelToView(rNode.GetTextNode(), nPos)); 1977 const sal_uInt8 nCurrLevel = pSI->DirType( nIndex ); 1978 const sal_uInt8 nPrevLevel = pSI->DirType( nIndex - TextFrameIndex(1) ); 1979 1980 if ( nCurrLevel % 2 != nPrevLevel % 2 ) 1981 { 1982 // set cursor level to the lower of the two levels 1983 SetCursorBidiLevel( std::min( nCurrLevel, nPrevLevel ) ); 1984 } 1985 else 1986 SetCursorBidiLevel( nCurrLevel ); 1987 } 1988 1989 bool SwCursor::UpDown( bool bUp, sal_uInt16 nCnt, 1990 Point const * pPt, tools::Long nUpDownX, 1991 SwRootFrame & rLayout) 1992 { 1993 SwTableCursor* pTableCursor = dynamic_cast<SwTableCursor*>(this); 1994 bool bAdjustTableCursor = false; 1995 1996 // If the point/mark of the table cursor in the same box then set cursor to 1997 // beginning of the box 1998 if( pTableCursor && GetPointNode().StartOfSectionNode() == 1999 GetMarkNode().StartOfSectionNode() ) 2000 { 2001 if ( End() != GetPoint() ) 2002 Exchange(); 2003 bAdjustTableCursor = true; 2004 } 2005 2006 bool bRet = false; 2007 Point aPt; 2008 if( pPt ) 2009 aPt = *pPt; 2010 std::pair<Point, bool> const temp(aPt, true); 2011 SwContentFrame* pFrame = GetPointContentNode()->getLayoutFrame(&rLayout, GetPoint(), &temp); 2012 2013 if( pFrame ) 2014 { 2015 SwCursorSaveState aSave( *this ); 2016 2017 if( !pPt ) 2018 { 2019 SwRect aTmpRect; 2020 pFrame->GetCharRect( aTmpRect, *GetPoint() ); 2021 aPt = aTmpRect.Pos(); 2022 2023 nUpDownX = pFrame->IsVertical() ? 2024 aPt.getY() - pFrame->getFrameArea().Top() : 2025 aPt.getX() - pFrame->getFrameArea().Left(); 2026 } 2027 2028 // It is allowed to move footnotes in other footnotes but not sections 2029 const bool bChkRange = !pFrame->IsInFootnote() || HasMark(); 2030 const SwPosition aOldPos( *GetPoint() ); 2031 const bool bInReadOnly = IsReadOnlyAvailable(); 2032 2033 if ( bAdjustTableCursor && !bUp ) 2034 { 2035 // Special case: We have a table cursor but the start box has more 2036 // than one paragraph. If we want to go down, we have to set the 2037 // point to the last frame in the table box. This is only necessary 2038 // if we do not already have a table selection 2039 const SwStartNode* pTableNd = GetPointNode().FindTableBoxStartNode(); 2040 OSL_ENSURE( pTableNd, "pTableCursor without SwTableNode?" ); 2041 2042 if ( pTableNd ) // safety first 2043 { 2044 const SwNode* pEndNd = pTableNd->EndOfSectionNode(); 2045 GetPoint()->Assign( *pEndNd ); 2046 pTableCursor->Move( fnMoveBackward, GoInNode ); 2047 std::pair<Point, bool> const tmp(aPt, true); 2048 pFrame = GetPointContentNode()->getLayoutFrame(&rLayout, GetPoint(), &tmp); 2049 } 2050 } 2051 2052 while( nCnt && 2053 (bUp ? pFrame->UnitUp( this, nUpDownX, bInReadOnly ) 2054 : pFrame->UnitDown( this, nUpDownX, bInReadOnly ) ) && 2055 CheckNodesRange( aOldPos.GetNode(), GetPoint()->GetNode(), bChkRange )) 2056 { 2057 std::pair<Point, bool> const tmp(aPt, true); 2058 pFrame = GetPointContentNode()->getLayoutFrame(&rLayout, GetPoint(), &tmp); 2059 --nCnt; 2060 } 2061 2062 // iterate over whole number of items? 2063 if( !nCnt && !IsSelOvr( SwCursorSelOverFlags::Toggle | 2064 SwCursorSelOverFlags::ChangePos ) ) 2065 { 2066 if( !pTableCursor ) 2067 { 2068 // try to position the cursor at half of the char-rect's height 2069 DisableCallbackAction a(rLayout); 2070 std::pair<Point, bool> const tmp(aPt, true); 2071 pFrame = GetPointContentNode()->getLayoutFrame(&rLayout, GetPoint(), &tmp); 2072 SwCursorMoveState eTmpState( CursorMoveState::UpDown ); 2073 eTmpState.m_bSetInReadOnly = bInReadOnly; 2074 SwRect aTmpRect; 2075 pFrame->GetCharRect( aTmpRect, *GetPoint(), &eTmpState ); 2076 if ( pFrame->IsVertical() ) 2077 { 2078 aPt.setX(aTmpRect.Center().getX()); 2079 pFrame->Calc(rLayout.GetCurrShell()->GetOut()); 2080 aPt.setY(pFrame->getFrameArea().Top() + nUpDownX); 2081 } 2082 else 2083 { 2084 aPt.setY(aTmpRect.Center().getY()); 2085 pFrame->Calc(rLayout.GetCurrShell()->GetOut()); 2086 aPt.setX(pFrame->getFrameArea().Left() + nUpDownX); 2087 } 2088 pFrame->GetModelPositionForViewPoint( GetPoint(), aPt, &eTmpState ); 2089 } 2090 bRet = !IsSelOvr( SwCursorSelOverFlags::Toggle | SwCursorSelOverFlags::ChangePos ); 2091 } 2092 else if (!pFrame->IsInFootnote()) // tdf#150457 Jump to the begin/end 2093 // of the first/last line only if the 2094 // cursor is not inside a footnote 2095 { 2096 sal_Int32 nOffset = 0; 2097 2098 // Jump to beginning or end of line when the cursor at first or last line. 2099 if(!bUp) 2100 { 2101 SwTextNode* pTextNd = GetPoint()->GetNode().GetTextNode(); 2102 if (pTextNd) 2103 nOffset = pTextNd->GetText().getLength(); 2104 } 2105 const SwPosition aPos(*GetPointContentNode(), nOffset); 2106 2107 //if cursor has already been at start or end of file, 2108 //Update cursor to change nUpDownX. 2109 if ( aOldPos.GetContentIndex() == nOffset ) 2110 { 2111 if (SwEditShell* pSh = GetDoc().GetEditShell()) 2112 pSh->UpdateCursor(); 2113 bRet = false; 2114 } 2115 else{ 2116 *GetPoint() = aPos; // just give a new position 2117 bRet = true; 2118 } 2119 2120 } 2121 else 2122 *GetPoint() = aOldPos; 2123 2124 DoSetBidiLevelUpDown(); // calculate cursor bidi level 2125 } 2126 return bRet; 2127 } 2128 2129 bool SwCursor::LeftRightMargin(SwRootFrame const& rLayout, bool bLeft, bool bAPI) 2130 { 2131 Point aPt; 2132 std::pair<Point, bool> const tmp(aPt, true); 2133 SwContentFrame const*const pFrame = GetPointContentNode()->getLayoutFrame( 2134 &rLayout, GetPoint(), &tmp); 2135 2136 // calculate cursor bidi level 2137 if ( pFrame ) 2138 SetCursorBidiLevel( pFrame->IsRightToLeft() ? 1 : 0 ); 2139 2140 SwCursorSaveState aSave( *this ); 2141 return pFrame 2142 && (bLeft ? pFrame->LeftMargin( this ) : pFrame->RightMargin( this, bAPI ) ) 2143 && !IsSelOvr( SwCursorSelOverFlags::Toggle | SwCursorSelOverFlags::ChangePos ); 2144 } 2145 2146 bool SwCursor::IsAtLeftRightMargin(SwRootFrame const& rLayout, bool bLeft, bool bAPI) const 2147 { 2148 bool bRet = false; 2149 Point aPt; 2150 std::pair<Point, bool> const tmp(aPt, true); 2151 SwContentFrame const*const pFrame = GetPointContentNode()->getLayoutFrame( 2152 &rLayout, GetPoint(), &tmp); 2153 if( pFrame ) 2154 { 2155 SwPaM aPam( *GetPoint() ); 2156 if( !bLeft && aPam.GetPoint()->GetContentIndex() ) 2157 aPam.GetPoint()->AdjustContent(-1); 2158 bRet = (bLeft ? pFrame->LeftMargin( &aPam ) 2159 : pFrame->RightMargin( &aPam, bAPI )) 2160 && (!pFrame->IsTextFrame() 2161 || static_cast<SwTextFrame const*>(pFrame)->MapModelToViewPos(*aPam.GetPoint()) 2162 == static_cast<SwTextFrame const*>(pFrame)->MapModelToViewPos(*GetPoint())); 2163 } 2164 return bRet; 2165 } 2166 2167 bool SwCursor::SttEndDoc( bool bStt ) 2168 { 2169 SwCursorSaveState aSave( *this ); 2170 // Never jump over section boundaries during selection! 2171 // Can the cursor still moved on? 2172 SwMoveFnCollection const & fnMove = bStt ? fnMoveBackward : fnMoveForward; 2173 bool bRet = (!HasMark() || !IsNoContent() ) && 2174 Move( fnMove, GoInDoc ) && 2175 !IsInProtectTable( true ) && 2176 !IsSelOvr( SwCursorSelOverFlags::Toggle | 2177 SwCursorSelOverFlags::ChangePos | 2178 SwCursorSelOverFlags::EnableRevDirection ); 2179 return bRet; 2180 } 2181 2182 bool SwCursor::GoPrevNextCell( bool bNext, sal_uInt16 nCnt ) 2183 { 2184 const SwTableNode* pTableNd = GetPoint()->GetNode().FindTableNode(); 2185 if( !pTableNd ) 2186 return false; 2187 2188 // If there is another EndNode in front of the cell's StartNode then there 2189 // exists a previous cell 2190 SwCursorSaveState aSave( *this ); 2191 SwPosition& rPtPos = *GetPoint(); 2192 2193 while( nCnt-- ) 2194 { 2195 const SwNode* pTableBoxStartNode = rPtPos.GetNode().FindTableBoxStartNode(); 2196 const SwTableBox* pTableBox = pTableBoxStartNode->GetTableBox(); 2197 2198 // Check if we have to move the cursor to a covered cell before 2199 // proceeding: 2200 if (m_nRowSpanOffset) 2201 { 2202 if ( pTableBox && pTableBox->getRowSpan() > 1 ) 2203 { 2204 pTableBox = & pTableBox->FindEndOfRowSpan( pTableNd->GetTable(), 2205 o3tl::narrowing<sal_uInt16>(pTableBox->getRowSpan() + m_nRowSpanOffset)); 2206 rPtPos.Assign( *pTableBox->GetSttNd() ); 2207 pTableBoxStartNode = rPtPos.GetNode().FindTableBoxStartNode(); 2208 } 2209 m_nRowSpanOffset = 0; 2210 } 2211 2212 const SwNode* pTmpNode = bNext ? 2213 pTableBoxStartNode->EndOfSectionNode() : 2214 pTableBoxStartNode; 2215 2216 SwNodeIndex aCellIdx( *pTmpNode, bNext ? 1 : -1 ); 2217 if( (bNext && !aCellIdx.GetNode().IsStartNode()) || 2218 (!bNext && !aCellIdx.GetNode().IsEndNode()) ) 2219 return false; 2220 2221 if (bNext) 2222 rPtPos.Assign( aCellIdx ); 2223 else 2224 rPtPos.Assign(*aCellIdx.GetNode().StartOfSectionNode()); 2225 2226 pTableBoxStartNode = rPtPos.GetNode().FindTableBoxStartNode(); 2227 pTableBox = pTableBoxStartNode->GetTableBox(); 2228 if ( pTableBox && pTableBox->getRowSpan() < 1 ) 2229 { 2230 m_nRowSpanOffset = pTableBox->getRowSpan(); 2231 // move cursor to non-covered cell: 2232 pTableBox = & pTableBox->FindStartOfRowSpan( pTableNd->GetTable() ); 2233 rPtPos.Assign( *pTableBox->GetSttNd() ); 2234 } 2235 } 2236 2237 rPtPos.Adjust(SwNodeOffset(1)); 2238 if( !rPtPos.GetNode().IsContentNode() ) 2239 GetDoc().GetNodes().GoNextSection( &rPtPos, true, false ); 2240 GetPoint()->SetContent( 0 ); 2241 2242 return !IsInProtectTable( true ); 2243 } 2244 2245 bool SwTableCursor::GotoTable( const OUString& ) 2246 { 2247 return false; // invalid action 2248 } 2249 2250 bool SwCursor::GotoTable( const OUString& rName ) 2251 { 2252 bool bRet = false; 2253 if ( !HasMark() ) 2254 { 2255 SwTable* pTmpTable = SwTable::FindTable( GetDoc().FindTableFormatByName( rName ) ); 2256 if( pTmpTable ) 2257 { 2258 // a table in a normal nodes array 2259 SwCursorSaveState aSave( *this ); 2260 GetPoint()->Assign( *pTmpTable->GetTabSortBoxes()[ 0 ]-> 2261 GetSttNd()->FindTableNode() ); 2262 Move( fnMoveForward, GoInContent ); 2263 bRet = !IsSelOvr(); 2264 } 2265 } 2266 return bRet; 2267 } 2268 2269 bool SwCursor::GotoTableBox( const OUString& rName ) 2270 { 2271 bool bRet = false; 2272 const SwTableNode* pTableNd = GetPoint()->GetNode().FindTableNode(); 2273 if( pTableNd ) 2274 { 2275 // retrieve box by name 2276 const SwTableBox* pTableBox = pTableNd->GetTable().GetTableBox( rName ); 2277 if( pTableBox && pTableBox->GetSttNd() && 2278 ( !pTableBox->GetFrameFormat()->GetProtect().IsContentProtected() || 2279 IsReadOnlyAvailable() ) ) 2280 { 2281 SwCursorSaveState aSave( *this ); 2282 GetPoint()->Assign( *pTableBox->GetSttNd() ); 2283 Move( fnMoveForward, GoInContent ); 2284 bRet = !IsSelOvr(); 2285 } 2286 } 2287 return bRet; 2288 } 2289 2290 bool SwCursor::MovePara(SwWhichPara fnWhichPara, SwMoveFnCollection const & fnPosPara ) 2291 { 2292 // for optimization test something before 2293 const SwNode* pNd = &GetPoint()->GetNode(); 2294 bool bShortCut = false; 2295 if ( fnWhichPara == GoCurrPara ) 2296 { 2297 // #i41048# 2298 // If fnWhichPara == GoCurrPara then (*fnWhichPara)( *this, fnPosPara ) 2299 // can already move the cursor to a different text node. In this case 2300 // we better check if IsSelOvr(). 2301 const SwContentNode* pContentNd = pNd->GetContentNode(); 2302 if ( pContentNd ) 2303 { 2304 const sal_Int32 nSttEnd = &fnPosPara == &fnMoveForward ? 0 : pContentNd->Len(); 2305 if ( GetPoint()->GetContentIndex() != nSttEnd ) 2306 bShortCut = true; 2307 } 2308 } 2309 else 2310 { 2311 if ( pNd->IsTextNode() && 2312 pNd->GetNodes()[ pNd->GetIndex() + 2313 SwNodeOffset(fnWhichPara == GoNextPara ? 1 : -1 ) ]->IsTextNode() ) 2314 bShortCut = true; 2315 } 2316 2317 if ( bShortCut ) 2318 return (*fnWhichPara)( *this, fnPosPara ); 2319 2320 // else we must use the SaveStructure, because the next/prev is not 2321 // a same node type. 2322 SwCursorSaveState aSave( *this ); 2323 return (*fnWhichPara)( *this, fnPosPara ) && 2324 !IsInProtectTable( true ) && 2325 !IsSelOvr( SwCursorSelOverFlags::Toggle | 2326 SwCursorSelOverFlags::ChangePos ); 2327 } 2328 2329 bool SwCursor::MoveSection( SwWhichSection fnWhichSect, 2330 SwMoveFnCollection const & fnPosSect) 2331 { 2332 SwCursorSaveState aSave( *this ); 2333 return (*fnWhichSect)( *this, fnPosSect ) && 2334 !IsInProtectTable( true ) && 2335 !IsSelOvr( SwCursorSelOverFlags::Toggle | 2336 SwCursorSelOverFlags::ChangePos ); 2337 } 2338 2339 void SwCursor::RestoreSavePos() 2340 { 2341 // This method is not supposed to be used in cases when nodes may be 2342 // deleted; detect such cases, but do not crash (example: fdo#40831). 2343 SwNodeOffset uNodeCount(GetPoint()->GetNodes().Count()); 2344 OSL_ENSURE(m_vSavePos.empty() || m_vSavePos.back().nNode < uNodeCount, 2345 "SwCursor::RestoreSavePos: invalid node: " 2346 "probably something was deleted; consider using SwUnoCursor instead"); 2347 if (m_vSavePos.empty() || m_vSavePos.back().nNode >= uNodeCount) 2348 return; 2349 2350 GetPoint()->Assign( m_vSavePos.back().nNode ); 2351 2352 sal_Int32 nIdx = 0; 2353 if ( GetPointContentNode() ) 2354 { 2355 if (m_vSavePos.back().nContent <= GetPointContentNode()->Len()) 2356 nIdx = m_vSavePos.back().nContent; 2357 else 2358 { 2359 nIdx = GetPointContentNode()->Len(); 2360 OSL_FAIL("SwCursor::RestoreSavePos: invalid content index"); 2361 } 2362 } 2363 GetPoint()->SetContent( nIdx ); 2364 } 2365 2366 SwTableCursor::SwTableCursor( const SwPosition &rPos ) 2367 : SwCursor( rPos, nullptr ) 2368 { 2369 m_bParked = false; 2370 m_bChanged = false; 2371 m_nTablePtNd = SwNodeOffset(0); 2372 m_nTableMkNd = SwNodeOffset(0); 2373 m_nTablePtCnt = 0; 2374 m_nTableMkCnt = 0; 2375 } 2376 2377 SwTableCursor::~SwTableCursor() {} 2378 2379 static bool 2380 lcl_SeekEntry(const SwSelBoxes& rTmp, SwStartNode const*const pSrch, 2381 size_t & o_rFndPos) 2382 { 2383 SwNodeOffset nIdx = pSrch->GetIndex(); 2384 2385 size_t nO = rTmp.size(); 2386 if( nO > 0 ) 2387 { 2388 nO--; 2389 size_t nU = 0; 2390 while( nU <= nO ) 2391 { 2392 size_t nM = nU + ( nO - nU ) / 2; 2393 if( rTmp[ nM ]->GetSttNd() == pSrch ) 2394 { 2395 o_rFndPos = nM; 2396 return true; 2397 } 2398 else if( rTmp[ nM ]->GetSttIdx() < nIdx ) 2399 nU = nM + 1; 2400 else if( nM == 0 ) 2401 return false; 2402 else 2403 nO = nM - 1; 2404 } 2405 } 2406 return false; 2407 } 2408 2409 SwCursor* SwTableCursor::MakeBoxSels( SwCursor* pCurrentCursor ) 2410 { 2411 if (m_bChanged) 2412 { 2413 if (m_bParked) 2414 { 2415 // move back into content 2416 Exchange(); 2417 Move( fnMoveForward ); 2418 Exchange(); 2419 Move( fnMoveForward ); 2420 m_bParked = false; 2421 } 2422 2423 m_bChanged = false; 2424 2425 // create temporary copies so that all boxes that 2426 // have already cursors can be removed 2427 SwSelBoxes aTmp(m_SelectedBoxes); 2428 2429 // compare old and new ones 2430 SwNodes& rNds = pCurrentCursor->GetDoc().GetNodes(); 2431 const SwStartNode* pSttNd; 2432 SwCursor* pCur = pCurrentCursor; 2433 do { 2434 size_t nPos; 2435 bool bDel = false; 2436 pSttNd = pCur->GetPoint()->GetNode().FindTableBoxStartNode(); 2437 if( !pCur->HasMark() || !pSttNd || 2438 pSttNd != pCur->GetMark()->GetNode().FindTableBoxStartNode() ) 2439 bDel = true; 2440 2441 else if( lcl_SeekEntry( aTmp, pSttNd, nPos )) 2442 { 2443 SwNodeIndex aIdx( *pSttNd, 1 ); 2444 const SwNode* pNd = &aIdx.GetNode(); 2445 if( !pNd->IsContentNode() ) 2446 pNd = rNds.GoNextSection( &aIdx, true, false ); 2447 2448 SwPosition* pPos = pCur->GetMark(); 2449 if( pNd != &pPos->GetNode() ) 2450 pPos->Assign( *pNd ); 2451 pPos->SetContent( 0 ); 2452 2453 aIdx.Assign( *pSttNd->EndOfSectionNode(), - 1 ); 2454 pNd = &aIdx.GetNode(); 2455 if( !pNd->IsContentNode() ) 2456 pNd = SwNodes::GoPrevSection( &aIdx, true, false ); 2457 2458 pPos = pCur->GetPoint(); 2459 if (pNd && pNd != &pPos->GetNode()) 2460 pPos->Assign( *pNd ); 2461 pPos->SetContent( pNd ? static_cast<const SwContentNode*>(pNd)->Len() : 0); 2462 2463 aTmp.erase( aTmp.begin() + nPos ); 2464 } 2465 else 2466 bDel = true; 2467 2468 pCur = pCur->GetNext(); 2469 if( bDel ) 2470 { 2471 SwCursor* pDel = pCur->GetPrev(); 2472 if (pDel == dynamic_cast<SwShellCursor*>(pCurrentCursor)) 2473 pCurrentCursor = pDel->GetPrev(); 2474 2475 if( pDel == pCurrentCursor ) 2476 pCurrentCursor->DeleteMark(); 2477 else 2478 delete pDel; 2479 } 2480 } while ( pCurrentCursor != pCur ); 2481 2482 for (size_t nPos = 0; nPos < aTmp.size(); ++nPos) 2483 { 2484 pSttNd = aTmp[ nPos ]->GetSttNd(); 2485 2486 SwNodeIndex aIdx( *pSttNd, 1 ); 2487 if( &aIdx.GetNodes() != &rNds ) 2488 break; 2489 SwNode* pNd = &aIdx.GetNode(); 2490 if( !pNd->IsContentNode() ) 2491 pNd = rNds.GoNextSection( &aIdx, true, false ); 2492 2493 SwPaM *const pNew = (!pCurrentCursor->IsMultiSelection() && !pCurrentCursor->HasMark()) 2494 ? pCurrentCursor 2495 : pCurrentCursor->Create( pCurrentCursor ); 2496 pNew->GetPoint()->Assign( *pNd ); 2497 pNew->SetMark(); 2498 2499 SwPosition* pPos = pNew->GetPoint(); 2500 pPos->Assign( *pSttNd->EndOfSectionNode(), - 1 ); 2501 pNd = &pPos->GetNode(); 2502 if( !pNd->IsContentNode() ) 2503 pNd = SwNodes::GoPrevSection( pPos, true, false ); 2504 if (pNd) 2505 pPos->AssignEndIndex(*static_cast<SwContentNode*>(pNd)); 2506 } 2507 } 2508 return pCurrentCursor; 2509 } 2510 2511 void SwTableCursor::InsertBox( const SwTableBox& rTableBox ) 2512 { 2513 SwTableBox* pBox = const_cast<SwTableBox*>(&rTableBox); 2514 m_SelectedBoxes.insert(pBox); 2515 m_bChanged = true; 2516 } 2517 2518 void SwTableCursor::DeleteBox(size_t const nPos) 2519 { 2520 m_SelectedBoxes.erase(m_SelectedBoxes.begin() + nPos); 2521 m_bChanged = true; 2522 } 2523 2524 bool SwTableCursor::NewTableSelection() 2525 { 2526 bool bRet = false; 2527 const SwNode *pStart = GetPointNode().FindTableBoxStartNode(); 2528 const SwNode *pEnd = GetMarkNode().FindTableBoxStartNode(); 2529 if( pStart && pEnd ) 2530 { 2531 const SwTableNode *pTableNode = pStart->FindTableNode(); 2532 if( pTableNode == pEnd->FindTableNode() && 2533 pTableNode->GetTable().IsNewModel() ) 2534 { 2535 bRet = true; 2536 SwSelBoxes aNew(m_SelectedBoxes); 2537 pTableNode->GetTable().CreateSelection( pStart, pEnd, aNew, 2538 SwTable::SEARCH_NONE, false ); 2539 ActualizeSelection( aNew ); 2540 } 2541 } 2542 return bRet; 2543 } 2544 2545 void SwTableCursor::ActualizeSelection( const SwSelBoxes &rNew ) 2546 { 2547 size_t nOld = 0, nNew = 0; 2548 while (nOld < m_SelectedBoxes.size() && nNew < rNew.size()) 2549 { 2550 SwTableBox const*const pPOld = m_SelectedBoxes[ nOld ]; 2551 const SwTableBox* pPNew = rNew[ nNew ]; 2552 if( pPOld == pPNew ) 2553 { // this box will stay 2554 ++nOld; 2555 ++nNew; 2556 } 2557 else if( pPOld->GetSttIdx() < pPNew->GetSttIdx() ) 2558 { 2559 DeleteBox( nOld ); // this box has to go 2560 } 2561 else 2562 { 2563 InsertBox( *pPNew ); // this is a new one 2564 ++nOld; 2565 ++nNew; 2566 } 2567 } 2568 2569 while (nOld < m_SelectedBoxes.size()) 2570 { 2571 DeleteBox( nOld ); // some more to delete 2572 } 2573 2574 for ( ; nNew < rNew.size(); ++nNew ) // some more to insert 2575 { 2576 InsertBox( *rNew[ nNew ] ); 2577 } 2578 } 2579 2580 bool SwTableCursor::IsCursorMovedUpdate() 2581 { 2582 if( !IsCursorMoved() ) 2583 return false; 2584 2585 m_nTableMkNd = GetMark()->GetNodeIndex(); 2586 m_nTablePtNd = GetPoint()->GetNodeIndex(); 2587 m_nTableMkCnt = GetMark()->GetContentIndex(); 2588 m_nTablePtCnt = GetPoint()->GetContentIndex(); 2589 return true; 2590 } 2591 2592 /// park table cursor on the boxes' start node 2593 void SwTableCursor::ParkCursor() 2594 { 2595 // de-register index from text node 2596 SwNode* pNd = &GetPoint()->GetNode(); 2597 if( !pNd->IsStartNode() ) 2598 pNd = pNd->StartOfSectionNode(); 2599 GetPoint()->Assign(*pNd); 2600 2601 pNd = &GetMark()->GetNode(); 2602 if( !pNd->IsStartNode() ) 2603 pNd = pNd->StartOfSectionNode(); 2604 GetMark()->Assign(*pNd); 2605 2606 m_bChanged = true; 2607 m_bParked = true; 2608 } 2609 2610 bool SwTableCursor::HasReadOnlyBoxSel() const 2611 { 2612 bool bRet = false; 2613 for (size_t n = m_SelectedBoxes.size(); n; ) 2614 { 2615 if (m_SelectedBoxes[--n]->GetFrameFormat()->GetProtect().IsContentProtected()) 2616 { 2617 bRet = true; 2618 break; 2619 } 2620 } 2621 return bRet; 2622 } 2623 2624 bool SwTableCursor::HasHiddenBoxSel() const 2625 { 2626 bool bRet = false; 2627 for (size_t n = m_SelectedBoxes.size(); n; ) 2628 { 2629 if (m_SelectedBoxes[--n]->GetFrameFormat()->IsHidden()) 2630 { 2631 bRet = true; 2632 break; 2633 } 2634 } 2635 return bRet; 2636 } 2637 2638 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 2639
