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 <memory> 21 #include <com/sun/star/text/XTextRange.hpp> 22 23 #include <hintids.hxx> 24 #include <svx/svdmodel.hxx> 25 #include <svx/srchdlg.hxx> 26 #include <editeng/frmdiritem.hxx> 27 #include <sfx2/viewsh.hxx> 28 #include <SwSmartTagMgr.hxx> 29 #include <doc.hxx> 30 #include <rootfrm.hxx> 31 #include <pagefrm.hxx> 32 #include <cntfrm.hxx> 33 #include <viewimp.hxx> 34 #include <pam.hxx> 35 #include <swselectionlist.hxx> 36 #include "BlockCursor.hxx" 37 #include <ndtxt.hxx> 38 #include <flyfrm.hxx> 39 #include <dview.hxx> 40 #include <viewopt.hxx> 41 #include <frmtool.hxx> 42 #include <crsrsh.hxx> 43 #include <tabfrm.hxx> 44 #include <txtfrm.hxx> 45 #include <sectfrm.hxx> 46 #include <swtable.hxx> 47 #include "callnk.hxx" 48 #include <viscrs.hxx> 49 #include <section.hxx> 50 #include <docsh.hxx> 51 #include <scriptinfo.hxx> 52 #include <globdoc.hxx> 53 #include <pamtyp.hxx> 54 #include <mdiexp.hxx> 55 #include <fmteiro.hxx> 56 #include <wrong.hxx> 57 #include <unotextrange.hxx> 58 #include <vcl/svapp.hxx> 59 #include <vcl/settings.hxx> 60 #include <numrule.hxx> 61 #include <IGrammarContact.hxx> 62 #include <comphelper/flagguard.hxx> 63 #include <globals.hrc> 64 #include <strings.hrc> 65 #include <IDocumentLayoutAccess.hxx> 66 #include <LibreOfficeKit/LibreOfficeKitEnums.h> 67 #include <comphelper/lok.hxx> 68 #include <comphelper/sequence.hxx> 69 #include <sfx2/lokhelper.hxx> 70 #include <editeng/editview.hxx> 71 #include <sal/log.hxx> 72 #include <PostItMgr.hxx> 73 #include <DocumentSettingManager.hxx> 74 #include <vcl/uitest/logger.hxx> 75 #include <vcl/uitest/eventdescription.hxx> 76 77 using namespace com::sun::star; 78 using namespace util; 79 80 /** 81 * Check if pCurrentCursor points into already existing ranges and delete those. 82 * @param Pointer to SwCursor object 83 */ 84 static void CheckRange( SwCursor* pCurrentCursor ) 85 { 86 const SwPosition *pStt = pCurrentCursor->Start(), 87 *pEnd = pCurrentCursor->GetPoint() == pStt ? pCurrentCursor->GetMark() : pCurrentCursor->GetPoint(); 88 89 SwPaM *pTmpDel = nullptr, 90 *pTmp = pCurrentCursor->GetNext(); 91 92 // Search the complete ring 93 while( pTmp != pCurrentCursor ) 94 { 95 const SwPosition *pTmpStt = pTmp->Start(), 96 *pTmpEnd = pTmp->GetPoint() == pTmpStt ? 97 pTmp->GetMark() : pTmp->GetPoint(); 98 if( *pStt <= *pTmpStt ) 99 { 100 if( *pEnd > *pTmpStt || 101 ( *pEnd == *pTmpStt && *pEnd == *pTmpEnd )) 102 pTmpDel = pTmp; 103 } 104 else 105 if( *pStt < *pTmpEnd ) 106 pTmpDel = pTmp; 107 108 // If Point or Mark is within the Cursor range, we need to remove the old 109 // range. Take note that Point does not belong to the range anymore. 110 pTmp = pTmp->GetNext(); 111 delete pTmpDel; // Remove old range 112 pTmpDel = nullptr; 113 } 114 } 115 116 // SwCursorShell 117 118 SwPaM * SwCursorShell::CreateCursor() 119 { 120 // don't create new Cursor with active table Selection 121 assert(!IsTableMode()); 122 123 // New cursor as copy of current one. Add to the ring. 124 // Links point to previously created one, ie forward. 125 SwShellCursor* pNew = new SwShellCursor( *m_pCurrentCursor ); 126 127 // Hide PaM logically, to avoid undoing the inverting from 128 // copied PaM (#i75172#) 129 pNew->swapContent(*m_pCurrentCursor); 130 131 m_pCurrentCursor->DeleteMark(); 132 133 UpdateCursor( SwCursorShell::SCROLLWIN ); 134 return pNew; 135 } 136 137 /** 138 * Delete current Cursor, making the following one the current. 139 * Note, this function does not delete anything if there is no other cursor. 140 * @return - returns true if there was another cursor and we deleted one. 141 */ 142 void SwCursorShell::DestroyCursor() 143 { 144 // don't delete Cursor with active table Selection 145 assert(!IsTableMode()); 146 147 // Is there a next one? Don't do anything if not. 148 if(!m_pCurrentCursor->IsMultiSelection()) 149 return; 150 151 SwCallLink aLk( *this ); // watch Cursor-Moves 152 SwCursor* pNextCursor = static_cast<SwCursor*>(m_pCurrentCursor->GetNext()); 153 delete m_pCurrentCursor; 154 m_pCurrentCursor = dynamic_cast<SwShellCursor*>(pNextCursor); 155 UpdateCursor(); 156 } 157 158 /** 159 * Create and return a new shell cursor. 160 * Simply returns the current shell cursor if there is no selection 161 * (HasSelection()). 162 */ 163 SwPaM & SwCursorShell::CreateNewShellCursor() 164 { 165 if (HasSelection()) 166 { 167 (void) CreateCursor(); // n.b. returns old cursor 168 } 169 return *GetCursor(); 170 } 171 172 /** 173 * Return the current shell cursor 174 * @return - returns current `SwPaM` shell cursor 175 */ 176 SwPaM & SwCursorShell::GetCurrentShellCursor() 177 { 178 return *GetCursor(); 179 } 180 181 /** 182 * Return pointer to the current shell cursor 183 * @return - returns pointer to current `SwPaM` shell cursor 184 */ 185 SwPaM* SwCursorShell::GetCursor( bool bMakeTableCursor ) const 186 { 187 if( m_pTableCursor ) 188 { 189 if( bMakeTableCursor && m_pTableCursor->IsCursorMovedUpdate() ) 190 { 191 //don't re-create 'parked' cursors 192 const SwContentNode* pCNd; 193 if( m_pTableCursor->GetPoint()->nNode.GetIndex() && 194 m_pTableCursor->GetMark()->nNode.GetIndex() && 195 nullptr != ( pCNd = m_pTableCursor->GetContentNode() ) && pCNd->getLayoutFrame( GetLayout() ) && 196 nullptr != ( pCNd = m_pTableCursor->GetContentNode(false) ) && pCNd->getLayoutFrame( GetLayout() ) ) 197 { 198 SwShellTableCursor* pTC = m_pTableCursor; 199 GetLayout()->MakeTableCursors( *pTC ); 200 } 201 } 202 203 if( m_pTableCursor->IsChgd() ) 204 { 205 const_cast<SwCursorShell*>(this)->m_pCurrentCursor = 206 dynamic_cast<SwShellCursor*>(m_pTableCursor->MakeBoxSels( m_pCurrentCursor )); 207 } 208 } 209 return m_pCurrentCursor; 210 } 211 212 void SwCursorShell::StartAction() 213 { 214 if( !ActionPend() ) 215 { 216 // save for update of the ribbon bar 217 const SwNode& rNd = m_pCurrentCursor->GetPoint()->nNode.GetNode(); 218 m_nCurrentNode = rNd.GetIndex(); 219 m_nCurrentContent = m_pCurrentCursor->GetPoint()->nContent.GetIndex(); 220 m_nCurrentNdTyp = rNd.GetNodeType(); 221 if( rNd.IsTextNode() ) 222 m_nLeftFramePos = SwCallLink::getLayoutFrame( GetLayout(), *rNd.GetTextNode(), m_nCurrentContent, true ); 223 else 224 m_nLeftFramePos = 0; 225 } 226 SwViewShell::StartAction(); // to the SwViewShell 227 } 228 229 void SwCursorShell::EndAction( const bool bIdleEnd, const bool DoSetPosX ) 230 { 231 comphelper::FlagRestorationGuard g(mbSelectAll, StartsWithTable() && ExtendedSelectedAll()); 232 bool bVis = m_bSVCursorVis; 233 234 sal_uInt16 eFlags = SwCursorShell::CHKRANGE; 235 if ( !DoSetPosX ) 236 eFlags |= SwCursorShell::UPDOWN; 237 238 239 // Idle-formatting? 240 if( bIdleEnd && Imp()->GetRegion() ) 241 { 242 m_pCurrentCursor->Hide(); 243 } 244 245 // Update all invalid numberings before the last action 246 if( 1 == mnStartAction ) 247 GetDoc()->UpdateNumRule(); 248 249 // #i76923#: Don't show the cursor in the SwViewShell::EndAction() - call. 250 // Only the UpdateCursor shows the cursor. 251 bool bSavSVCursorVis = m_bSVCursorVis; 252 m_bSVCursorVis = false; 253 254 SwViewShell::EndAction( bIdleEnd ); // have SwViewShell go first 255 256 m_bSVCursorVis = bSavSVCursorVis; 257 258 if( ActionPend() ) 259 { 260 if( bVis ) // display SV-Cursor again 261 m_pVisibleCursor->Show(); 262 263 return; 264 } 265 266 if ( !bIdleEnd ) 267 eFlags |= SwCursorShell::SCROLLWIN; 268 269 UpdateCursor( eFlags, bIdleEnd ); // Show Cursor changes 270 271 { 272 SwCallLink aLk( *this ); // Watch cursor moves, 273 aLk.nNode = m_nCurrentNode; // possibly call the link 274 aLk.nNdTyp = m_nCurrentNdTyp; 275 aLk.nContent = m_nCurrentContent; 276 aLk.nLeftFramePos = m_nLeftFramePos; 277 278 if( !m_nCursorMove || 279 ( 1 == m_nCursorMove && m_bInCMvVisportChgd ) ) 280 // display Cursor & Selections again 281 ShowCursors( m_bSVCursorVis ); 282 } 283 // call ChgCall if there is still one 284 if( m_bCallChgLnk && m_bChgCallFlag && m_aChgLnk.IsSet() ) 285 { 286 m_aChgLnk.Call( this ); 287 m_bChgCallFlag = false; // reset flag 288 } 289 } 290 291 void SwCursorShell::SttCursorMove() 292 { 293 #ifdef DBG_UTIL 294 OSL_ENSURE( m_nCursorMove < USHRT_MAX, "Too many nested CursorMoves." ); 295 #endif 296 ++m_nCursorMove; 297 StartAction(); 298 } 299 300 void SwCursorShell::EndCursorMove( const bool bIdleEnd ) 301 { 302 #ifdef DBG_UTIL 303 OSL_ENSURE( m_nCursorMove, "EndCursorMove() without SttCursorMove()." ); 304 #endif 305 EndAction( bIdleEnd, true ); 306 --m_nCursorMove; 307 #ifdef DBG_UTIL 308 if( !m_nCursorMove ) 309 m_bInCMvVisportChgd = false; 310 #endif 311 } 312 313 bool SwCursorShell::LeftRight( bool bLeft, sal_uInt16 nCnt, sal_uInt16 nMode, 314 bool bVisualAllowed ) 315 { 316 if( IsTableMode() ) 317 return bLeft ? GoPrevCell() : GoNextCell(); 318 319 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 320 bool bRet = false; 321 322 // #i27615# Handle cursor in front of label. 323 const SwTextNode* pTextNd = nullptr; 324 325 if( m_pBlockCursor ) 326 m_pBlockCursor->clearPoints(); 327 328 // 1. CASE: Cursor is in front of label. A move to the right 329 // will simply reset the bInFrontOfLabel flag: 330 SwShellCursor* pShellCursor = getShellCursor( true ); 331 if ( !bLeft && pShellCursor->IsInFrontOfLabel() ) 332 { 333 SetInFrontOfLabel( false ); 334 bRet = true; 335 } 336 // 2. CASE: Cursor is at beginning of numbered paragraph. A move 337 // to the left will simply set the bInFrontOfLabel flag: 338 else if (bLeft 339 && pShellCursor->GetPoint()->nNode.GetNode().IsTextNode() 340 && static_cast<SwTextFrame const*>( 341 pShellCursor->GetPoint()->nNode.GetNode().GetTextNode()->getLayoutFrame(GetLayout()) 342 )->MapModelToViewPos(*pShellCursor->GetPoint()) == TextFrameIndex(0) 343 && !pShellCursor->IsInFrontOfLabel() 344 && !pShellCursor->HasMark() 345 && nullptr != (pTextNd = sw::GetParaPropsNode(*GetLayout(), pShellCursor->GetPoint()->nNode)) 346 && pTextNd->HasVisibleNumberingOrBullet()) 347 { 348 SetInFrontOfLabel( true ); 349 bRet = true; 350 } 351 // 3. CASE: Regular cursor move. Reset the bInFrontOfLabel flag: 352 else 353 { 354 const bool bSkipHidden = !GetViewOptions()->IsShowHiddenChar(); 355 // #i107447# 356 // To avoid loop the reset of <bInFrontOfLabel> flag is no longer 357 // reflected in the return value <bRet>. 358 const bool bResetOfInFrontOfLabel = SetInFrontOfLabel( false ); 359 bRet = pShellCursor->LeftRight( bLeft, nCnt, nMode, bVisualAllowed, 360 bSkipHidden, !IsOverwriteCursor(), 361 GetLayout()); 362 if ( !bRet && bLeft && bResetOfInFrontOfLabel ) 363 { 364 // undo reset of <bInFrontOfLabel> flag 365 SetInFrontOfLabel( true ); 366 } 367 } 368 369 if( bRet ) 370 { 371 UpdateCursor(); 372 } 373 374 return bRet; 375 } 376 377 void SwCursorShell::MarkListLevel( const OUString& sListId, 378 const int nListLevel ) 379 { 380 if ( sListId != m_sMarkedListId || 381 nListLevel != m_nMarkedListLevel) 382 { 383 if ( !m_sMarkedListId.isEmpty() ) 384 mxDoc->MarkListLevel( m_sMarkedListId, m_nMarkedListLevel, false ); 385 386 if ( !sListId.isEmpty() ) 387 { 388 mxDoc->MarkListLevel( sListId, nListLevel, true ); 389 } 390 391 m_sMarkedListId = sListId; 392 m_nMarkedListLevel = nListLevel; 393 } 394 } 395 396 void SwCursorShell::UpdateMarkedListLevel() 397 { 398 SwTextNode const*const pTextNd = sw::GetParaPropsNode(*GetLayout(), 399 GetCursor_()->GetPoint()->nNode); 400 401 if ( pTextNd ) 402 { 403 if (!pTextNd->IsNumbered(GetLayout())) 404 { 405 m_pCurrentCursor->SetInFrontOfLabel_( false ); 406 MarkListLevel( OUString(), 0 ); 407 } 408 else if ( m_pCurrentCursor->IsInFrontOfLabel() ) 409 { 410 if ( pTextNd->IsInList() ) 411 { 412 assert(pTextNd->GetActualListLevel() >= 0 && 413 pTextNd->GetActualListLevel() < MAXLEVEL); 414 MarkListLevel( pTextNd->GetListId(), 415 pTextNd->GetActualListLevel() ); 416 } 417 } 418 else 419 { 420 MarkListLevel( OUString(), 0 ); 421 } 422 } 423 } 424 425 void SwCursorShell::FirePageChangeEvent(sal_uInt16 nOldPage, sal_uInt16 nNewPage) 426 { 427 #ifdef ACCESSIBLE_LAYOUT 428 if( Imp()->IsAccessible() ) 429 Imp()->FirePageChangeEvent( nOldPage, nNewPage ); 430 #else 431 (void)nOldPage; 432 (void)nNewPage; 433 #endif 434 } 435 436 void SwCursorShell::FireColumnChangeEvent(sal_uInt16 nOldColumn, sal_uInt16 nNewColumn) 437 { 438 #ifdef ACCESSIBLE_LAYOUT 439 if( Imp()->IsAccessible() ) 440 Imp()->FireColumnChangeEvent( nOldColumn, nNewColumn); 441 #else 442 (void)nOldColumn; 443 (void)nNewColumn; 444 #endif 445 } 446 447 void SwCursorShell::FireSectionChangeEvent(sal_uInt16 nOldSection, sal_uInt16 nNewSection) 448 { 449 #ifdef ACCESSIBLE_LAYOUT 450 if( Imp()->IsAccessible() ) 451 Imp()->FireSectionChangeEvent( nOldSection, nNewSection ); 452 #else 453 (void)nOldSection; 454 (void)nNewSection; 455 #endif 456 } 457 458 bool SwCursorShell::bColumnChange() 459 { 460 SwFrame* pCurrFrame = GetCurrFrame(false); 461 462 if (pCurrFrame == nullptr) 463 { 464 return false; 465 } 466 467 SwFrame* pCurrCol=pCurrFrame->FindColFrame(); 468 469 while(pCurrCol== nullptr && pCurrFrame!=nullptr ) 470 { 471 SwLayoutFrame* pParent = pCurrFrame->GetUpper(); 472 if(pParent!=nullptr) 473 { 474 pCurrCol=static_cast<SwFrame*>(pParent)->FindColFrame(); 475 pCurrFrame = static_cast<SwFrame*>(pParent); 476 } 477 else 478 { 479 break; 480 } 481 } 482 483 if(m_oldColFrame == pCurrCol) 484 return false; 485 else 486 { 487 m_oldColFrame = pCurrCol; 488 return true; 489 } 490 } 491 492 bool SwCursorShell::UpDown( bool bUp, sal_uInt16 nCnt ) 493 { 494 SET_CURR_SHELL( this ); 495 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 496 497 bool bTableMode = IsTableMode(); 498 SwShellCursor* pTmpCursor = getShellCursor( true ); 499 500 bool bRet = pTmpCursor->UpDown( bUp, nCnt ); 501 // #i40019# UpDown should always reset the bInFrontOfLabel flag: 502 bRet |= SetInFrontOfLabel(false); 503 504 if( m_pBlockCursor ) 505 m_pBlockCursor->clearPoints(); 506 507 if( bRet ) 508 { 509 m_eMvState = MV_UPDOWN; // status for Cursor travelling - GetCursorOfst 510 if( !ActionPend() ) 511 { 512 CursorFlag eUpdateMode = SwCursorShell::SCROLLWIN; 513 if( !bTableMode ) 514 eUpdateMode = static_cast<CursorFlag>(eUpdateMode 515 | SwCursorShell::UPDOWN | SwCursorShell::CHKRANGE); 516 UpdateCursor( static_cast<sal_uInt16>(eUpdateMode) ); 517 } 518 } 519 return bRet; 520 } 521 522 bool SwCursorShell::LRMargin( bool bLeft, bool bAPI) 523 { 524 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 525 SET_CURR_SHELL( this ); 526 m_eMvState = MV_LEFTMARGIN; // status for Cursor travelling - GetCursorOfst 527 528 const bool bTableMode = IsTableMode(); 529 SwShellCursor* pTmpCursor = getShellCursor( true ); 530 531 if( m_pBlockCursor ) 532 m_pBlockCursor->clearPoints(); 533 534 const bool bWasAtLM = GetCursor_()->IsAtLeftRightMargin(*GetLayout(), true, bAPI); 535 536 bool bRet = pTmpCursor->LeftRightMargin(*GetLayout(), bLeft, bAPI); 537 538 if ( bLeft && !bTableMode && bRet && bWasAtLM && !GetCursor_()->HasMark() ) 539 { 540 const SwTextNode * pTextNd = GetCursor_()->GetNode().GetTextNode(); 541 assert(sw::GetParaPropsNode(*GetLayout(), GetCursor_()->GetPoint()->nNode) == pTextNd); 542 if ( pTextNd && pTextNd->HasVisibleNumberingOrBullet() ) 543 SetInFrontOfLabel( true ); 544 } 545 else if ( !bLeft ) 546 { 547 bRet = SetInFrontOfLabel( false ) || bRet; 548 } 549 550 if( bRet ) 551 { 552 UpdateCursor(); 553 } 554 return bRet; 555 } 556 557 bool SwCursorShell::IsAtLRMargin( bool bLeft, bool bAPI ) const 558 { 559 const SwShellCursor* pTmpCursor = getShellCursor( true ); 560 return pTmpCursor->IsAtLeftRightMargin(*GetLayout(), bLeft, bAPI); 561 } 562 563 bool SwCursorShell::SttEndDoc( bool bStt ) 564 { 565 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 566 567 SwShellCursor* pTmpCursor = m_pBlockCursor ? &m_pBlockCursor->getShellCursor() : m_pCurrentCursor; 568 bool bRet = pTmpCursor->SttEndDoc( bStt ); 569 if( bRet ) 570 { 571 if( bStt ) 572 pTmpCursor->GetPtPos().setY( 0 ); // set to 0 explicitly (table header) 573 if( m_pBlockCursor ) 574 { 575 m_pBlockCursor->clearPoints(); 576 RefreshBlockCursor(); 577 } 578 579 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY); 580 } 581 return bRet; 582 } 583 584 void SwCursorShell::ExtendedSelectAll(bool bFootnotes) 585 { 586 SwNodes& rNodes = GetDoc()->GetNodes(); 587 SwPosition* pPos = m_pCurrentCursor->GetPoint(); 588 pPos->nNode = bFootnotes ? rNodes.GetEndOfPostIts() : rNodes.GetEndOfAutotext(); 589 pPos->nContent.Assign( rNodes.GoNext( &pPos->nNode ), 0 ); 590 pPos = m_pCurrentCursor->GetMark(); 591 pPos->nNode = rNodes.GetEndOfContent(); 592 SwContentNode* pCNd = SwNodes::GoPrevious( &pPos->nNode ); 593 pPos->nContent.Assign( pCNd, pCNd ? pCNd->Len() : 0 ); 594 } 595 596 bool SwCursorShell::ExtendedSelectedAll() 597 { 598 SwNodes& rNodes = GetDoc()->GetNodes(); 599 SwNodeIndex nNode = rNodes.GetEndOfAutotext(); 600 SwContentNode* pStart = rNodes.GoNext(&nNode); 601 602 nNode = rNodes.GetEndOfContent(); 603 SwContentNode* pEnd = SwNodes::GoPrevious(&nNode); 604 605 if (!pStart || !pEnd) 606 return false; 607 608 SwPosition aStart(*pStart, 0); 609 SwPosition aEnd(*pEnd, pEnd->Len()); 610 SwShellCursor* pShellCursor = getShellCursor(false); 611 return aStart == *pShellCursor->Start() && aEnd == *pShellCursor->End(); 612 } 613 614 bool SwCursorShell::StartsWithTable() 615 { 616 SwNodes& rNodes = GetDoc()->GetNodes(); 617 SwNodeIndex nNode(rNodes.GetEndOfExtras()); 618 SwContentNode* pContentNode = rNodes.GoNext(&nNode); 619 return pContentNode->FindTableNode(); 620 } 621 622 bool SwCursorShell::MovePage( SwWhichPage fnWhichPage, SwPosPage fnPosPage ) 623 { 624 bool bRet = false; 625 626 // never jump of section borders at selection 627 if( !m_pCurrentCursor->HasMark() || !m_pCurrentCursor->IsNoContent() ) 628 { 629 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 630 SET_CURR_SHELL( this ); 631 632 SwCursorSaveState aSaveState( *m_pCurrentCursor ); 633 Point& rPt = m_pCurrentCursor->GetPtPos(); 634 std::pair<Point, bool> tmp(rPt, false); 635 SwContentFrame * pFrame = m_pCurrentCursor->GetContentNode()-> 636 getLayoutFrame(GetLayout(), m_pCurrentCursor->GetPoint(), &tmp); 637 if( pFrame && ( bRet = GetFrameInPage( pFrame, fnWhichPage, 638 fnPosPage, m_pCurrentCursor ) ) && 639 !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle | 640 SwCursorSelOverFlags::ChangePos )) 641 UpdateCursor(); 642 else 643 bRet = false; 644 } 645 return bRet; 646 } 647 648 bool SwCursorShell::isInHiddenTextFrame(SwShellCursor* pShellCursor) 649 { 650 SwContentNode *pCNode = pShellCursor->GetContentNode(); 651 std::pair<Point, bool> tmp(pShellCursor->GetPtPos(), false); 652 SwContentFrame *const pFrame = pCNode 653 ? pCNode->getLayoutFrame(GetLayout(), pShellCursor->GetPoint(), &tmp) 654 : nullptr; 655 return !pFrame || (pFrame->IsTextFrame() && static_cast<SwTextFrame*>(pFrame)->IsHiddenNow()); 656 } 657 658 // sw_redlinehide: this should work for all cases: GoCurrPara, GoNextPara, GoPrevPara 659 static bool IsAtStartOrEndOfFrame(SwCursorShell const*const pShell, 660 SwShellCursor const*const pShellCursor, SwMoveFnCollection const& fnPosPara) 661 { 662 SwContentNode *const pCNode = pShellCursor->GetContentNode(); 663 assert(pCNode); // surely can't have moved otherwise? 664 std::pair<Point, bool> tmp(pShellCursor->GetPtPos(), false); 665 SwContentFrame const*const pFrame = pCNode->getLayoutFrame( 666 pShell->GetLayout(), pShellCursor->GetPoint(), &tmp); 667 if (!pFrame || !pFrame->IsTextFrame()) 668 { 669 return false; 670 } 671 SwTextFrame const& rTextFrame(static_cast<SwTextFrame const&>(*pFrame)); 672 TextFrameIndex const ix(rTextFrame.MapModelToViewPos(*pShellCursor->GetPoint())); 673 if (&fnParaStart == &fnPosPara) 674 { 675 return ix == TextFrameIndex(0); 676 } 677 else 678 { 679 assert(&fnParaEnd == &fnPosPara); 680 return ix == TextFrameIndex(rTextFrame.GetText().getLength()); 681 } 682 } 683 684 bool SwCursorShell::MovePara(SwWhichPara fnWhichPara, SwMoveFnCollection const & fnPosPara ) 685 { 686 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 687 SwShellCursor* pTmpCursor = getShellCursor( true ); 688 bool bRet = pTmpCursor->MovePara( fnWhichPara, fnPosPara ); 689 if( bRet ) 690 { 691 //keep going until we get something visible, i.e. skip 692 //over hidden paragraphs, don't get stuck at the start 693 //which is what SwCursorShell::UpdateCursorPos will reset 694 //the position to if we pass it a position in an 695 //invisible hidden paragraph field 696 while (isInHiddenTextFrame(pTmpCursor) 697 || !IsAtStartOrEndOfFrame(this, pTmpCursor, fnPosPara)) 698 { 699 if (!pTmpCursor->MovePara(fnWhichPara, fnPosPara)) 700 break; 701 } 702 703 UpdateCursor(); 704 } 705 return bRet; 706 } 707 708 bool SwCursorShell::MoveSection( SwWhichSection fnWhichSect, 709 SwMoveFnCollection const & fnPosSect) 710 { 711 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 712 SwCursor* pTmpCursor = getShellCursor( true ); 713 bool bRet = pTmpCursor->MoveSection( fnWhichSect, fnPosSect ); 714 if( bRet ) 715 UpdateCursor(); 716 return bRet; 717 718 } 719 720 // position cursor 721 722 static SwFrame* lcl_IsInHeaderFooter( const SwNodeIndex& rIdx, Point& rPt ) 723 { 724 SwFrame* pFrame = nullptr; 725 SwContentNode* pCNd = rIdx.GetNode().GetContentNode(); 726 if( pCNd ) 727 { 728 std::pair<Point, bool> tmp(rPt, false); 729 SwContentFrame *pContentFrame = pCNd->getLayoutFrame( 730 pCNd->GetDoc()->getIDocumentLayoutAccess().GetCurrentLayout(), 731 nullptr, &tmp); 732 pFrame = pContentFrame ? pContentFrame->GetUpper() : nullptr; 733 while( pFrame && !pFrame->IsHeaderFrame() && !pFrame->IsFooterFrame() ) 734 pFrame = pFrame->IsFlyFrame() ? static_cast<SwFlyFrame*>(pFrame)->AnchorFrame() 735 : pFrame->GetUpper(); 736 } 737 return pFrame; 738 } 739 740 bool SwCursorShell::IsInHeaderFooter( bool* pbInHeader ) const 741 { 742 Point aPt; 743 SwFrame* pFrame = ::lcl_IsInHeaderFooter( m_pCurrentCursor->GetPoint()->nNode, aPt ); 744 if( pFrame && pbInHeader ) 745 *pbInHeader = pFrame->IsHeaderFrame(); 746 return nullptr != pFrame; 747 } 748 749 int SwCursorShell::SetCursor( const Point &rLPt, bool bOnlyText, bool bBlock ) 750 { 751 SET_CURR_SHELL( this ); 752 753 SwShellCursor* pCursor = getShellCursor( bBlock ); 754 SwPosition aPos( *pCursor->GetPoint() ); 755 Point aPt( rLPt ); 756 Point & rCurrentCursorPt = pCursor->GetPtPos(); 757 SwCursorMoveState aTmpState( IsTableMode() ? MV_TBLSEL : 758 bOnlyText ? MV_SETONLYTEXT : MV_NONE ); 759 aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable(); 760 761 SwTextNode const*const pTextNd = sw::GetParaPropsNode(*GetLayout(), pCursor->GetPoint()->nNode); 762 763 if ( pTextNd && !IsTableMode() && 764 // #i37515# No bInFrontOfLabel during selection 765 !pCursor->HasMark() && 766 pTextNd->HasVisibleNumberingOrBullet() ) 767 { 768 aTmpState.m_bInFrontOfLabel = true; // #i27615# 769 } 770 else 771 { 772 aTmpState.m_bInFrontOfLabel = false; 773 } 774 775 int bRet = CRSR_POSOLD | 776 ( GetLayout()->GetCursorOfst( &aPos, aPt, &aTmpState ) 777 ? 0 : CRSR_POSCHG ); 778 779 const bool bOldInFrontOfLabel = IsInFrontOfLabel(); 780 const bool bNewInFrontOfLabel = aTmpState.m_bInFrontOfLabel; 781 782 pCursor->SetCursorBidiLevel( aTmpState.m_nCursorBidiLevel ); 783 784 if( MV_RIGHTMARGIN == aTmpState.m_eState ) 785 m_eMvState = MV_RIGHTMARGIN; 786 // is the new position in header or footer? 787 SwFrame* pFrame = lcl_IsInHeaderFooter( aPos.nNode, aPt ); 788 if( IsTableMode() && !pFrame && aPos.nNode.GetNode().StartOfSectionNode() == 789 pCursor->GetPoint()->nNode.GetNode().StartOfSectionNode() ) 790 // same table column and not in header/footer -> back 791 return bRet; 792 793 if( m_pBlockCursor && bBlock ) 794 { 795 m_pBlockCursor->setEndPoint( rLPt ); 796 if( !pCursor->HasMark() ) 797 m_pBlockCursor->setStartPoint( rLPt ); 798 else if( !m_pBlockCursor->getStartPoint() ) 799 m_pBlockCursor->setStartPoint( pCursor->GetMkPos() ); 800 } 801 if( !pCursor->HasMark() ) 802 { 803 // is at the same position and if in header/footer -> in the same 804 if( aPos == *pCursor->GetPoint() && 805 bOldInFrontOfLabel == bNewInFrontOfLabel ) 806 { 807 if( pFrame ) 808 { 809 if( pFrame->getFrameArea().IsInside( rCurrentCursorPt )) 810 return bRet; 811 } 812 else if( aPos.nNode.GetNode().IsContentNode() ) 813 { 814 // in the same frame? 815 std::pair<Point, bool> tmp(m_aCharRect.Pos(), false); 816 SwFrame* pOld = static_cast<SwContentNode&>(aPos.nNode.GetNode()).getLayoutFrame( 817 GetLayout(), nullptr, &tmp); 818 tmp.first = aPt; 819 SwFrame* pNew = static_cast<SwContentNode&>(aPos.nNode.GetNode()).getLayoutFrame( 820 GetLayout(), nullptr, &tmp); 821 if( pNew == pOld ) 822 return bRet; 823 } 824 } 825 } 826 else 827 { 828 // SSelection over not allowed sections or if in header/footer -> different 829 if( !CheckNodesRange( aPos.nNode, pCursor->GetMark()->nNode, true ) 830 || ( pFrame && !pFrame->getFrameArea().IsInside( pCursor->GetMkPos() ) )) 831 return bRet; 832 833 // is at same position but not in header/footer 834 if( aPos == *pCursor->GetPoint() ) 835 return bRet; 836 } 837 838 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 839 SwCursorSaveState aSaveState( *pCursor ); 840 841 *pCursor->GetPoint() = aPos; 842 rCurrentCursorPt = aPt; 843 844 // #i41424# Only update the marked number levels if necessary 845 // Force update of marked number levels if necessary. 846 if ( bNewInFrontOfLabel || bOldInFrontOfLabel ) 847 m_pCurrentCursor->SetInFrontOfLabel_( !bNewInFrontOfLabel ); 848 SetInFrontOfLabel( bNewInFrontOfLabel ); 849 850 if( !pCursor->IsSelOvr( SwCursorSelOverFlags::ChangePos ) ) 851 { 852 UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE ); 853 bRet &= ~CRSR_POSOLD; 854 } 855 else if( bOnlyText && !m_pCurrentCursor->HasMark() ) 856 { 857 if( FindValidContentNode( bOnlyText ) ) 858 { 859 // position cursor in a valid content 860 if( aPos == *pCursor->GetPoint() ) 861 bRet = CRSR_POSOLD; 862 else 863 { 864 UpdateCursor(); 865 bRet &= ~CRSR_POSOLD; 866 } 867 } 868 else 869 { 870 // there is no valid content -> hide cursor 871 m_pVisibleCursor->Hide(); // always hide visible cursor 872 m_eMvState = MV_NONE; // status for Cursor travelling 873 m_bAllProtect = true; 874 if( GetDoc()->GetDocShell() ) 875 { 876 GetDoc()->GetDocShell()->SetReadOnlyUI(); 877 CallChgLnk(); // notify UI 878 } 879 } 880 } 881 return bRet; 882 } 883 884 void SwCursorShell::TableCursorToCursor() 885 { 886 assert(m_pTableCursor); 887 delete m_pTableCursor; 888 m_pTableCursor = nullptr; 889 } 890 891 void SwCursorShell::BlockCursorToCursor() 892 { 893 assert(m_pBlockCursor); 894 if( m_pBlockCursor && !HasSelection() ) 895 { 896 SwPaM& rPam = m_pBlockCursor->getShellCursor(); 897 m_pCurrentCursor->SetMark(); 898 *m_pCurrentCursor->GetPoint() = *rPam.GetPoint(); 899 if( rPam.HasMark() ) 900 *m_pCurrentCursor->GetMark() = *rPam.GetMark(); 901 else 902 m_pCurrentCursor->DeleteMark(); 903 } 904 delete m_pBlockCursor; 905 m_pBlockCursor = nullptr; 906 } 907 908 void SwCursorShell::CursorToBlockCursor() 909 { 910 if( !m_pBlockCursor ) 911 { 912 SwPosition aPos( *m_pCurrentCursor->GetPoint() ); 913 m_pBlockCursor = new SwBlockCursor( *this, aPos ); 914 SwShellCursor &rBlock = m_pBlockCursor->getShellCursor(); 915 rBlock.GetPtPos() = m_pCurrentCursor->GetPtPos(); 916 if( m_pCurrentCursor->HasMark() ) 917 { 918 rBlock.SetMark(); 919 *rBlock.GetMark() = *m_pCurrentCursor->GetMark(); 920 rBlock.GetMkPos() = m_pCurrentCursor->GetMkPos(); 921 } 922 } 923 m_pBlockCursor->clearPoints(); 924 RefreshBlockCursor(); 925 } 926 927 void SwCursorShell::ClearMark() 928 { 929 // is there any GetMark? 930 if( m_pTableCursor ) 931 { 932 std::vector<SwPaM*> vCursors; 933 for(auto& rCursor : m_pCurrentCursor->GetRingContainer()) 934 if(&rCursor != m_pCurrentCursor) 935 vCursors.push_back(&rCursor); 936 for(auto pCursor : vCursors) 937 delete pCursor; 938 m_pTableCursor->DeleteMark(); 939 940 m_pCurrentCursor->DeleteMark(); 941 942 *m_pCurrentCursor->GetPoint() = *m_pTableCursor->GetPoint(); 943 m_pCurrentCursor->GetPtPos() = m_pTableCursor->GetPtPos(); 944 delete m_pTableCursor; 945 m_pTableCursor = nullptr; 946 m_pCurrentCursor->SwSelPaintRects::Show(); 947 } 948 else 949 { 950 if( !m_pCurrentCursor->HasMark() ) 951 return; 952 m_pCurrentCursor->DeleteMark(); 953 if( !m_nCursorMove ) 954 m_pCurrentCursor->SwSelPaintRects::Show(); 955 } 956 } 957 958 void SwCursorShell::NormalizePam(bool bPointFirst) 959 { 960 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 961 m_pCurrentCursor->Normalize(bPointFirst); 962 } 963 964 void SwCursorShell::SwapPam() 965 { 966 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 967 m_pCurrentCursor->Exchange(); 968 } 969 970 //TODO: provide documentation 971 /** Search in the selected area for a Selection that covers the given point. 972 973 It checks if a Selection exists but does 974 not move the current cursor. 975 976 @param rPt The point to search at. 977 @param bTstHit ??? 978 */ 979 bool SwCursorShell::TestCurrPam( 980 const Point & rPt, 981 bool bTstHit ) 982 { 983 SET_CURR_SHELL( this ); 984 985 // check if the SPoint is in a table selection 986 if( m_pTableCursor ) 987 return m_pTableCursor->IsInside( rPt ); 988 989 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 990 // search position <rPt> in document 991 SwPosition aPtPos( *m_pCurrentCursor->GetPoint() ); 992 Point aPt( rPt ); 993 994 SwCursorMoveState aTmpState( MV_NONE ); 995 aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable(); 996 if ( !GetLayout()->GetCursorOfst( &aPtPos, aPt, &aTmpState ) && bTstHit ) 997 return false; 998 999 // search in all selections for this position 1000 SwShellCursor* pCmp = m_pCurrentCursor; // keep the pointer on cursor 1001 do 1002 { 1003 if (pCmp->HasMark() && *pCmp->Start() <= aPtPos && *pCmp->End() > aPtPos) 1004 return true; // return without update 1005 pCmp = pCmp->GetNext(); 1006 } while (m_pCurrentCursor != pCmp); 1007 return false; 1008 } 1009 1010 void SwCursorShell::KillPams() 1011 { 1012 // Does any exist for deletion? 1013 if( !m_pTableCursor && !m_pBlockCursor && !m_pCurrentCursor->IsMultiSelection() ) 1014 return; 1015 1016 while( m_pCurrentCursor->GetNext() != m_pCurrentCursor ) 1017 delete m_pCurrentCursor->GetNext(); 1018 m_pCurrentCursor->SetColumnSelection( false ); 1019 1020 if( m_pTableCursor ) 1021 { 1022 // delete the ring of cursors 1023 m_pCurrentCursor->DeleteMark(); 1024 *m_pCurrentCursor->GetPoint() = *m_pTableCursor->GetPoint(); 1025 m_pCurrentCursor->GetPtPos() = m_pTableCursor->GetPtPos(); 1026 delete m_pTableCursor; 1027 m_pTableCursor = nullptr; 1028 } 1029 else if( m_pBlockCursor ) 1030 { 1031 // delete the ring of cursors 1032 m_pCurrentCursor->DeleteMark(); 1033 SwShellCursor &rBlock = m_pBlockCursor->getShellCursor(); 1034 *m_pCurrentCursor->GetPoint() = *rBlock.GetPoint(); 1035 m_pCurrentCursor->GetPtPos() = rBlock.GetPtPos(); 1036 rBlock.DeleteMark(); 1037 m_pBlockCursor->clearPoints(); 1038 } 1039 UpdateCursor( SwCursorShell::SCROLLWIN ); 1040 } 1041 1042 int SwCursorShell::CompareCursorStackMkCurrPt() const 1043 { 1044 int nRet = 0; 1045 const SwPosition *pFirst = nullptr, *pSecond = nullptr; 1046 const SwPaM *pCur = GetCursor(), *pStack = m_pStackCursor; 1047 // cursor on stack is needed if we compare against stack 1048 if( pStack ) 1049 { 1050 pFirst = pStack->GetMark(); 1051 pSecond = pCur->GetPoint(); 1052 } 1053 if( !pFirst || !pSecond ) 1054 nRet = INT_MAX; 1055 else if( *pFirst < *pSecond ) 1056 nRet = -1; 1057 else if( *pFirst == *pSecond ) 1058 nRet = 0; 1059 else 1060 nRet = 1; 1061 return nRet; 1062 } 1063 1064 bool SwCursorShell::IsSelOnePara() const 1065 { 1066 if (m_pCurrentCursor->IsMultiSelection()) 1067 { 1068 return false; 1069 } 1070 if (m_pCurrentCursor->GetPoint()->nNode == m_pCurrentCursor->GetMark()->nNode) 1071 { 1072 return true; 1073 } 1074 if (GetLayout()->IsHideRedlines()) 1075 { 1076 SwContentFrame const*const pFrame(GetCurrFrame(false)); 1077 auto const n(m_pCurrentCursor->GetMark()->nNode.GetIndex()); 1078 return FrameContainsNode(*pFrame, n); 1079 } 1080 return false; 1081 } 1082 1083 bool SwCursorShell::IsSttPara() const 1084 { 1085 if (GetLayout()->IsHideRedlines()) 1086 { 1087 SwTextNode const*const pNode(m_pCurrentCursor->GetPoint()->nNode.GetNode().GetTextNode()); 1088 if (pNode) 1089 { 1090 SwTextFrame const*const pFrame(static_cast<SwTextFrame*>( 1091 pNode->getLayoutFrame(GetLayout()))); 1092 if (pFrame) 1093 { 1094 return pFrame->MapModelToViewPos(*m_pCurrentCursor->GetPoint()) 1095 == TextFrameIndex(0); 1096 } 1097 } 1098 } 1099 return m_pCurrentCursor->GetPoint()->nContent == 0; 1100 } 1101 1102 bool SwCursorShell::IsEndPara() const 1103 { 1104 if (GetLayout()->IsHideRedlines()) 1105 { 1106 SwTextNode const*const pNode(m_pCurrentCursor->GetPoint()->nNode.GetNode().GetTextNode()); 1107 if (pNode) 1108 { 1109 SwTextFrame const*const pFrame(static_cast<SwTextFrame*>( 1110 pNode->getLayoutFrame(GetLayout()))); 1111 if (pFrame) 1112 { 1113 return pFrame->MapModelToViewPos(*m_pCurrentCursor->GetPoint()) 1114 == TextFrameIndex(pFrame->GetText().getLength()); 1115 } 1116 } 1117 } 1118 return m_pCurrentCursor->GetPoint()->nContent == m_pCurrentCursor->GetContentNode()->Len(); 1119 } 1120 1121 bool SwCursorShell::IsEndOfTable() const 1122 { 1123 if (IsTableMode() || IsBlockMode() || !IsEndPara()) 1124 { 1125 return false; 1126 } 1127 SwTableNode const*const pTableNode( IsCursorInTable() ); 1128 if (!pTableNode) 1129 { 1130 return false; 1131 } 1132 SwEndNode const*const pEndTableNode(pTableNode->EndOfSectionNode()); 1133 SwNodeIndex const lastNode(*pEndTableNode, -2); 1134 SAL_WARN_IF(!lastNode.GetNode().GetTextNode(), "sw.core", 1135 "text node expected"); 1136 return (lastNode == m_pCurrentCursor->GetPoint()->nNode); 1137 } 1138 1139 bool SwCursorShell::IsCursorInFootnote() const 1140 { 1141 SwStartNodeType aStartNodeType = m_pCurrentCursor->GetNode().StartOfSectionNode()->GetStartNodeType(); 1142 return aStartNodeType == SwStartNodeType::SwFootnoteStartNode; 1143 } 1144 1145 bool SwCursorShell::IsInFrontOfLabel() const 1146 { 1147 return m_pCurrentCursor->IsInFrontOfLabel(); 1148 } 1149 1150 bool SwCursorShell::SetInFrontOfLabel( bool bNew ) 1151 { 1152 if ( bNew != IsInFrontOfLabel() ) 1153 { 1154 m_pCurrentCursor->SetInFrontOfLabel_( bNew ); 1155 UpdateMarkedListLevel(); 1156 return true; 1157 } 1158 return false; 1159 } 1160 1161 namespace { 1162 1163 void collectUIInformation(const OUString& aPage) 1164 { 1165 EventDescription aDescription; 1166 aDescription.aAction = "GOTO"; 1167 aDescription.aParameters = {{"PAGE", aPage}}; 1168 aDescription.aID = "writer_edit"; 1169 aDescription.aKeyWord = "SwEditWinUIObject"; 1170 aDescription.aParent = "MainWindow"; 1171 UITestLogger::getInstance().logEvent(aDescription); 1172 } 1173 1174 } 1175 1176 bool SwCursorShell::GotoPage( sal_uInt16 nPage ) 1177 { 1178 SET_CURR_SHELL( this ); 1179 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 1180 SwCursorSaveState aSaveState( *m_pCurrentCursor ); 1181 bool bRet = GetLayout()->SetCurrPage( m_pCurrentCursor, nPage ) && 1182 !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle | 1183 SwCursorSelOverFlags::ChangePos ); 1184 if( bRet ) 1185 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY); 1186 1187 collectUIInformation(OUString::number(nPage)); 1188 return bRet; 1189 } 1190 1191 void SwCursorShell::GetCharRectAt(SwRect& rRect, const SwPosition* pPos) 1192 { 1193 SwContentFrame* pFrame = GetCurrFrame(); 1194 pFrame->GetCharRect( rRect, *pPos ); 1195 } 1196 1197 void SwCursorShell::GetPageNum( sal_uInt16 &rnPhyNum, sal_uInt16 &rnVirtNum, 1198 bool bAtCursorPos, const bool bCalcFrame ) 1199 { 1200 SET_CURR_SHELL( this ); 1201 // page number: first visible page or the one at the cursor 1202 const SwContentFrame* pCFrame; 1203 const SwPageFrame *pPg = nullptr; 1204 1205 if( !bAtCursorPos || nullptr == (pCFrame = GetCurrFrame( bCalcFrame )) || 1206 nullptr == (pPg = pCFrame->FindPageFrame()) ) 1207 { 1208 pPg = Imp()->GetFirstVisPage(GetOut()); 1209 while( pPg && pPg->IsEmptyPage() ) 1210 pPg = static_cast<const SwPageFrame *>(pPg->GetNext()); 1211 } 1212 // pPg has to exist with a default of 1 for the special case "Writerstart" 1213 rnPhyNum = pPg? pPg->GetPhyPageNum() : 1; 1214 rnVirtNum = pPg? pPg->GetVirtPageNum() : 1; 1215 } 1216 1217 sal_uInt16 SwCursorShell::GetPageNumSeqNonEmpty() 1218 { 1219 SET_CURR_SHELL(this); 1220 // page number: first visible page or the one at the cursor 1221 const SwContentFrame* pCFrame = GetCurrFrame(/*bCalcFrame*/true); 1222 const SwPageFrame* pPg = nullptr; 1223 1224 if (!pCFrame || nullptr == (pPg = pCFrame->FindPageFrame())) 1225 { 1226 pPg = Imp()->GetFirstVisPage(GetOut()); 1227 while (pPg && pPg->IsEmptyPage()) 1228 pPg = static_cast<const SwPageFrame*>(pPg->GetNext()); 1229 } 1230 1231 sal_uInt16 nPageNo = 0; 1232 while (pPg) 1233 { 1234 if (!pPg->IsEmptyPage()) 1235 ++nPageNo; 1236 pPg = static_cast<const SwPageFrame*>(pPg->GetPrev()); 1237 } 1238 return nPageNo; 1239 } 1240 1241 sal_uInt16 SwCursorShell::GetNextPrevPageNum( bool bNext ) 1242 { 1243 SET_CURR_SHELL( this ); 1244 // page number: first visible page or the one at the cursor 1245 const SwPageFrame *pPg = Imp()->GetFirstVisPage(GetOut()); 1246 if( pPg ) 1247 { 1248 const SwTwips nPageTop = pPg->getFrameArea().Top(); 1249 1250 if( bNext ) 1251 { 1252 // go to next view layout row: 1253 do 1254 { 1255 pPg = static_cast<const SwPageFrame *>(pPg->GetNext()); 1256 } 1257 while( pPg && pPg->getFrameArea().Top() == nPageTop ); 1258 1259 while( pPg && pPg->IsEmptyPage() ) 1260 pPg = static_cast<const SwPageFrame *>(pPg->GetNext()); 1261 } 1262 else 1263 { 1264 // go to previous view layout row: 1265 do 1266 { 1267 pPg = static_cast<const SwPageFrame *>(pPg->GetPrev()); 1268 } 1269 while( pPg && pPg->getFrameArea().Top() == nPageTop ); 1270 1271 while( pPg && pPg->IsEmptyPage() ) 1272 pPg = static_cast<const SwPageFrame *>(pPg->GetPrev()); 1273 } 1274 } 1275 // pPg has to exist with a default of 1 for the special case "Writerstart" 1276 return pPg ? pPg->GetPhyPageNum() : USHRT_MAX; 1277 } 1278 1279 sal_uInt16 SwCursorShell::GetPageCnt() 1280 { 1281 SET_CURR_SHELL( this ); 1282 // return number of pages 1283 return GetLayout()->GetPageNum(); 1284 } 1285 1286 OUString SwCursorShell::getPageRectangles() 1287 { 1288 CurrShell aCurr(this); 1289 SwRootFrame* pLayout = GetLayout(); 1290 OUStringBuffer aBuf; 1291 for (const SwFrame* pFrame = pLayout->GetLower(); pFrame; pFrame = pFrame->GetNext()) 1292 { 1293 aBuf.append(pFrame->getFrameArea().Left()); 1294 aBuf.append(", "); 1295 aBuf.append(pFrame->getFrameArea().Top()); 1296 aBuf.append(", "); 1297 aBuf.append(pFrame->getFrameArea().Width()); 1298 aBuf.append(", "); 1299 aBuf.append(pFrame->getFrameArea().Height()); 1300 aBuf.append("; "); 1301 } 1302 if (!aBuf.isEmpty()) 1303 aBuf.setLength( aBuf.getLength() - 2); // remove the last "; " 1304 return aBuf.makeStringAndClear(); 1305 } 1306 1307 void SwCursorShell::NotifyCursor(SfxViewShell* pOtherShell) const 1308 { 1309 auto pView = const_cast<SdrView*>(GetDrawView()); 1310 if (pView->GetTextEditObject()) 1311 { 1312 // Blinking cursor. 1313 EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView(); 1314 rEditView.RegisterOtherShell(pOtherShell); 1315 rEditView.ShowCursor(); 1316 rEditView.RegisterOtherShell(nullptr); 1317 // Text selection, if any. 1318 rEditView.DrawSelectionXOR(pOtherShell); 1319 1320 // Shape text lock. 1321 if (OutlinerView* pOutlinerView = pView->GetTextEditOutlinerView()) 1322 { 1323 OString sRect = pOutlinerView->GetOutputArea().toString(); 1324 SfxLokHelper::notifyOtherView(GetSfxViewShell(), pOtherShell, LOK_CALLBACK_VIEW_LOCK, "rectangle", sRect); 1325 } 1326 } 1327 else 1328 { 1329 // Cursor position. 1330 m_pVisibleCursor->SetPosAndShow(pOtherShell); 1331 // Cursor visibility. 1332 if (GetSfxViewShell() != pOtherShell) 1333 { 1334 OString aPayload = OString::boolean(m_bSVCursorVis); 1335 SfxLokHelper::notifyOtherView(GetSfxViewShell(), pOtherShell, LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", aPayload); 1336 } 1337 // Text selection. 1338 m_pCurrentCursor->Show(pOtherShell); 1339 // Graphic selection. 1340 pView->AdjustMarkHdl(pOtherShell); 1341 } 1342 } 1343 1344 /// go to the next SSelection 1345 bool SwCursorShell::GoNextCursor() 1346 { 1347 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty ); 1348 1349 if( !m_pCurrentCursor->IsMultiSelection() ) 1350 { 1351 if( !m_pCurrentCursor->HasMark() ) 1352 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound ); 1353 return false; 1354 } 1355 1356 SET_CURR_SHELL( this ); 1357 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 1358 m_pCurrentCursor = m_pCurrentCursor->GetNext(); 1359 1360 // #i24086#: show also all others 1361 if( !ActionPend() ) 1362 { 1363 UpdateCursor(); 1364 m_pCurrentCursor->Show(nullptr); 1365 } 1366 return true; 1367 } 1368 1369 /// go to the previous SSelection 1370 bool SwCursorShell::GoPrevCursor() 1371 { 1372 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty ); 1373 1374 if( !m_pCurrentCursor->IsMultiSelection() ) 1375 { 1376 if( !m_pCurrentCursor->HasMark() ) 1377 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound ); 1378 return false; 1379 } 1380 1381 SET_CURR_SHELL( this ); 1382 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 1383 m_pCurrentCursor = m_pCurrentCursor->GetPrev(); 1384 1385 // #i24086#: show also all others 1386 if( !ActionPend() ) 1387 { 1388 UpdateCursor(); 1389 m_pCurrentCursor->Show(nullptr); 1390 } 1391 return true; 1392 } 1393 1394 void SwCursorShell::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle &rRect) 1395 { 1396 comphelper::FlagRestorationGuard g(mbSelectAll, StartsWithTable() && ExtendedSelectedAll()); 1397 SET_CURR_SHELL( this ); 1398 1399 // always switch off all cursors when painting 1400 SwRect aRect( rRect ); 1401 1402 bool bVis = false; 1403 // if a cursor is visible then hide the SV cursor 1404 if( m_pVisibleCursor->IsVisible() && !aRect.IsOver( m_aCharRect ) ) 1405 { 1406 bVis = true; 1407 m_pVisibleCursor->Hide(); 1408 } 1409 1410 // re-paint area 1411 SwViewShell::Paint(rRenderContext, rRect); 1412 1413 if( m_bHasFocus && !m_bBasicHideCursor ) 1414 { 1415 SwShellCursor* pCurrentCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor; 1416 1417 if( !ActionPend() ) 1418 { 1419 // so that right/bottom borders will not be cropped 1420 pCurrentCursor->Invalidate( VisArea() ); 1421 pCurrentCursor->Show(nullptr); 1422 } 1423 else 1424 pCurrentCursor->Invalidate( aRect ); 1425 1426 } 1427 1428 if (SwPostItMgr* pPostItMgr = GetPostItMgr()) 1429 { 1430 // No point in showing the cursor for Writer text when there is an 1431 // active annotation edit. 1432 if (bVis) 1433 bVis = !pPostItMgr->HasActiveSidebarWin(); 1434 } 1435 1436 if( m_bSVCursorVis && bVis ) // also show SV cursor again 1437 m_pVisibleCursor->Show(); 1438 } 1439 1440 void SwCursorShell::VisPortChgd( const SwRect & rRect ) 1441 { 1442 SET_CURR_SHELL( this ); 1443 bool bVis; // switch off all cursors when scrolling 1444 1445 // if a cursor is visible then hide the SV cursor 1446 bVis = m_pVisibleCursor->IsVisible(); 1447 if( bVis ) 1448 m_pVisibleCursor->Hide(); 1449 1450 m_bVisPortChgd = true; 1451 m_aOldRBPos.setX(VisArea().Right()); 1452 m_aOldRBPos.setY(VisArea().Bottom()); 1453 1454 // For not having problems with the SV cursor, Update() is called for the 1455 // Window in SwViewShell::VisPo... 1456 // During painting no selections should be shown, thus the call is encapsulated. <- TODO: old artefact? 1457 SwViewShell::VisPortChgd( rRect ); // move area 1458 1459 if( m_bSVCursorVis && bVis ) // show SV cursor again 1460 m_pVisibleCursor->Show(); 1461 1462 if( m_nCursorMove ) 1463 m_bInCMvVisportChgd = true; 1464 1465 m_bVisPortChgd = false; 1466 } 1467 1468 /** Set the cursor back into content. 1469 1470 This should only be called if the cursor was move somewhere else (e.g. when 1471 deleting a border). The new position is calculated from its current position 1472 in the layout. 1473 */ 1474 void SwCursorShell::UpdateCursorPos() 1475 { 1476 SET_CURR_SHELL( this ); 1477 ++mnStartAction; 1478 SwShellCursor* pShellCursor = getShellCursor( true ); 1479 Size aOldSz( GetDocSize() ); 1480 1481 if( isInHiddenTextFrame(pShellCursor) ) 1482 { 1483 SwCursorMoveState aTmpState( MV_NONE ); 1484 aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable(); 1485 GetLayout()->GetCursorOfst( pShellCursor->GetPoint(), pShellCursor->GetPtPos(), 1486 &aTmpState ); 1487 pShellCursor->DeleteMark(); 1488 } 1489 IGrammarContact *pGrammarContact = GetDoc() ? GetDoc()->getGrammarContact() : nullptr; 1490 if( pGrammarContact ) 1491 pGrammarContact->updateCursorPosition( *m_pCurrentCursor->GetPoint() ); 1492 --mnStartAction; 1493 if( aOldSz != GetDocSize() ) 1494 SizeChgNotify(); 1495 } 1496 1497 // #i65475# - if Point/Mark in hidden sections, move them out 1498 static bool lcl_CheckHiddenSection( SwNodeIndex& rIdx ) 1499 { 1500 bool bOk = true; 1501 const SwSectionNode* pSectNd = rIdx.GetNode().FindSectionNode(); 1502 if( pSectNd && pSectNd->GetSection().IsHiddenFlag() ) 1503 { 1504 SwNodeIndex aTmp( *pSectNd ); 1505 const SwNode* pFrameNd = 1506 rIdx.GetNodes().FindPrvNxtFrameNode( aTmp, pSectNd->EndOfSectionNode() ); 1507 bOk = pFrameNd != nullptr; 1508 SAL_WARN_IF(!bOk, "sw.core", "found no Node with Frames"); 1509 rIdx = aTmp; 1510 } 1511 return bOk; 1512 } 1513 1514 /// Try to set the cursor to the next visible content node. 1515 static void lcl_CheckHiddenPara( SwPosition& rPos ) 1516 { 1517 SwNodeIndex aTmp( rPos.nNode ); 1518 SwTextNode* pTextNd = aTmp.GetNode().GetTextNode(); 1519 while( pTextNd && pTextNd->HasHiddenCharAttribute( true ) ) 1520 { 1521 SwContentNode* pContent = aTmp.GetNodes().GoNext( &aTmp ); 1522 if ( pContent && pContent->IsTextNode() ) 1523 pTextNd = pContent->GetTextNode(); 1524 else 1525 pTextNd = nullptr; 1526 } 1527 1528 if ( pTextNd ) 1529 rPos = SwPosition( aTmp, SwIndex( pTextNd, 0 ) ); 1530 } 1531 1532 // #i27301# - helper class that notifies the accessibility about invalid text 1533 // selections in its destructor 1534 class SwNotifyAccAboutInvalidTextSelections 1535 { 1536 private: 1537 SwCursorShell& mrCursorSh; 1538 1539 public: 1540 explicit SwNotifyAccAboutInvalidTextSelections( SwCursorShell& _rCursorSh ) 1541 : mrCursorSh( _rCursorSh ) 1542 {} 1543 1544 ~SwNotifyAccAboutInvalidTextSelections() COVERITY_NOEXCEPT_FALSE 1545 { 1546 mrCursorSh.InvalidateAccessibleParaTextSelection(); 1547 } 1548 }; 1549 1550 void SwCursorShell::UpdateCursor( sal_uInt16 eFlags, bool bIdleEnd ) 1551 { 1552 SET_CURR_SHELL( this ); 1553 ClearUpCursors(); 1554 1555 if (ActionPend()) 1556 { 1557 if ( eFlags & SwCursorShell::READONLY ) 1558 m_bIgnoreReadonly = true; 1559 return; // if not then no update 1560 } 1561 1562 SwNotifyAccAboutInvalidTextSelections aInvalidateTextSelections( *this ); 1563 1564 if ( m_bIgnoreReadonly ) 1565 { 1566 m_bIgnoreReadonly = false; 1567 eFlags |= SwCursorShell::READONLY; 1568 } 1569 1570 if( eFlags & SwCursorShell::CHKRANGE ) // check all cursor moves for 1571 CheckRange( m_pCurrentCursor ); // overlapping ranges 1572 1573 if( !bIdleEnd ) 1574 CheckTableBoxContent(); 1575 1576 // If the current cursor is in a table and point/mark in different boxes, 1577 // then the table mode is active (also if it is already active: m_pTableCursor) 1578 SwPaM* pTstCursor = getShellCursor( true ); 1579 if( pTstCursor->HasMark() && !m_pBlockCursor && 1580 mxDoc->IsIdxInTable( pTstCursor->GetPoint()->nNode ) && 1581 ( m_pTableCursor || 1582 pTstCursor->GetNode().StartOfSectionNode() != 1583 pTstCursor->GetNode( false ).StartOfSectionNode() ) && !mbSelectAll) 1584 { 1585 SwShellCursor* pITmpCursor = getShellCursor( true ); 1586 Point aTmpPt( pITmpCursor->GetPtPos() ); 1587 Point aTmpMk( pITmpCursor->GetMkPos() ); 1588 SwPosition* pPos = pITmpCursor->GetPoint(); 1589 1590 // Bug 65475 (1999) - if Point/Mark in hidden sections, move them out 1591 lcl_CheckHiddenSection( pPos->nNode ); 1592 lcl_CheckHiddenSection( pITmpCursor->GetMark()->nNode ); 1593 1594 // Move cursor out of hidden paragraphs 1595 if ( !GetViewOptions()->IsShowHiddenChar() ) 1596 { 1597 lcl_CheckHiddenPara( *pPos ); 1598 lcl_CheckHiddenPara( *pITmpCursor->GetMark() ); 1599 } 1600 1601 std::pair<Point, bool> const tmp(aTmpPt, false); 1602 SwContentFrame *pTableFrame = pPos->nNode.GetNode().GetContentNode()-> 1603 getLayoutFrame( GetLayout(), pPos, &tmp); 1604 1605 OSL_ENSURE( pTableFrame, "Table Cursor not in Content ??" ); 1606 1607 // --> Make code robust. The table cursor may point 1608 // to a table in a currently inactive header. 1609 SwTabFrame *pTab = pTableFrame ? pTableFrame->FindTabFrame() : nullptr; 1610 1611 if ( pTab && pTab->GetTable()->GetRowsToRepeat() > 0 ) 1612 { 1613 // First check if point is in repeated headline: 1614 bool bInRepeatedHeadline = pTab->IsFollow() && pTab->IsInHeadline( *pTableFrame ); 1615 1616 // Second check if mark is in repeated headline: 1617 if ( !bInRepeatedHeadline ) 1618 { 1619 std::pair<Point, bool> const tmp1(aTmpMk, false); 1620 SwContentFrame* pMarkTableFrame = pITmpCursor->GetContentNode( false )-> 1621 getLayoutFrame(GetLayout(), pITmpCursor->GetMark(), &tmp1); 1622 OSL_ENSURE( pMarkTableFrame, "Table Cursor not in Content ??" ); 1623 1624 if ( pMarkTableFrame ) 1625 { 1626 SwTabFrame* pMarkTab = pMarkTableFrame->FindTabFrame(); 1627 OSL_ENSURE( pMarkTab, "Table Cursor not in Content ??" ); 1628 1629 // Make code robust: 1630 if ( pMarkTab ) 1631 { 1632 bInRepeatedHeadline = pMarkTab->IsFollow() && pMarkTab->IsInHeadline( *pMarkTableFrame ); 1633 } 1634 } 1635 } 1636 1637 // No table cursor in repeated headlines: 1638 if ( bInRepeatedHeadline ) 1639 { 1640 pTableFrame = nullptr; 1641 1642 SwMoveFnCollection const & fnPosSect = *pPos < *pITmpCursor->GetMark() 1643 ? fnSectionStart 1644 : fnSectionEnd; 1645 1646 // then only select inside the Box 1647 if( m_pTableCursor ) 1648 { 1649 m_pCurrentCursor->SetMark(); 1650 *m_pCurrentCursor->GetMark() = *m_pTableCursor->GetMark(); 1651 m_pCurrentCursor->GetMkPos() = m_pTableCursor->GetMkPos(); 1652 m_pTableCursor->DeleteMark(); 1653 m_pTableCursor->SwSelPaintRects::Hide(); 1654 } 1655 1656 *m_pCurrentCursor->GetPoint() = *m_pCurrentCursor->GetMark(); 1657 GoCurrSection( *m_pCurrentCursor, fnPosSect ); 1658 } 1659 } 1660 1661 // we really want a table selection 1662 if( pTab && pTableFrame ) 1663 { 1664 if( !m_pTableCursor ) 1665 { 1666 m_pTableCursor = new SwShellTableCursor( *this, 1667 *m_pCurrentCursor->GetMark(), m_pCurrentCursor->GetMkPos(), 1668 *pPos, aTmpPt ); 1669 m_pCurrentCursor->DeleteMark(); 1670 m_pCurrentCursor->SwSelPaintRects::Hide(); 1671 1672 CheckTableBoxContent(); 1673 if(!m_pTableCursor) 1674 { 1675 SAL_WARN("sw.core", "fdo#74854: " 1676 "this should not happen, but better lose the selection " 1677 "rather than crashing"); 1678 return; 1679 } 1680 } 1681 1682 SwCursorMoveState aTmpState( MV_NONE ); 1683 aTmpState.m_bRealHeight = true; 1684 { 1685 DisableCallbackAction a(*GetLayout()); 1686 if (!pTableFrame->GetCharRect( m_aCharRect, *m_pTableCursor->GetPoint(), &aTmpState)) 1687 { 1688 Point aCentrPt( m_aCharRect.Center() ); 1689 aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable(); 1690 pTableFrame->GetCursorOfst(m_pTableCursor->GetPoint(), aCentrPt, &aTmpState); 1691 bool const bResult = 1692 pTableFrame->GetCharRect(m_aCharRect, *m_pTableCursor->GetPoint()); 1693 OSL_ENSURE( bResult, "GetCharRect failed." ); 1694 } 1695 } 1696 1697 m_pVisibleCursor->Hide(); // always hide visible Cursor 1698 // scroll Cursor to visible area 1699 if( eFlags & SwCursorShell::SCROLLWIN && 1700 (HasSelection() || eFlags & SwCursorShell::READONLY || 1701 !IsCursorReadonly()) ) 1702 { 1703 SwFrame* pBoxFrame = pTableFrame; 1704 while( pBoxFrame && !pBoxFrame->IsCellFrame() ) 1705 pBoxFrame = pBoxFrame->GetUpper(); 1706 if( pBoxFrame && pBoxFrame->getFrameArea().HasArea() ) 1707 MakeVisible( pBoxFrame->getFrameArea() ); 1708 else 1709 MakeVisible( m_aCharRect ); 1710 } 1711 1712 // let Layout create the Cursors in the Boxes 1713 if( m_pTableCursor->IsCursorMovedUpdate() ) 1714 GetLayout()->MakeTableCursors( *m_pTableCursor ); 1715 if( m_bHasFocus && !m_bBasicHideCursor ) 1716 m_pTableCursor->Show(nullptr); 1717 1718 // set Cursor-Points to the new Positions 1719 m_pTableCursor->GetPtPos().setX(m_aCharRect.Left()); 1720 m_pTableCursor->GetPtPos().setY(m_aCharRect.Top()); 1721 1722 if( m_bSVCursorVis ) 1723 { 1724 m_aCursorHeight.setX(0); 1725 m_aCursorHeight.setY(aTmpState.m_aRealHeight.getY() < 0 ? 1726 -m_aCharRect.Width() : m_aCharRect.Height()); 1727 m_pVisibleCursor->Show(); // show again 1728 } 1729 m_eMvState = MV_NONE; // state for cursor travelling - GetCursorOfst 1730 if (Imp()->IsAccessible()) 1731 Imp()->InvalidateAccessibleCursorPosition( pTableFrame ); 1732 return; 1733 } 1734 } 1735 1736 if( m_pTableCursor ) 1737 { 1738 // delete Ring 1739 while( m_pCurrentCursor->GetNext() != m_pCurrentCursor ) 1740 delete m_pCurrentCursor->GetNext(); 1741 m_pCurrentCursor->DeleteMark(); 1742 *m_pCurrentCursor->GetPoint() = *m_pTableCursor->GetPoint(); 1743 m_pCurrentCursor->GetPtPos() = m_pTableCursor->GetPtPos(); 1744 delete m_pTableCursor; 1745 m_pTableCursor = nullptr; 1746 } 1747 1748 m_pVisibleCursor->Hide(); // always hide visible Cursor 1749 1750 // are we perhaps in a protected / hidden Section ? 1751 { 1752 SwShellCursor* pShellCursor = getShellCursor( true ); 1753 bool bChgState = true; 1754 const SwSectionNode* pSectNd = pShellCursor->GetNode().FindSectionNode(); 1755 if( pSectNd && ( pSectNd->GetSection().IsHiddenFlag() || 1756 ( !IsReadOnlyAvailable() && 1757 pSectNd->GetSection().IsProtectFlag() && 1758 ( !mxDoc->GetDocShell() || 1759 !mxDoc->GetDocShell()->IsReadOnly() || m_bAllProtect )) ) ) 1760 { 1761 if( !FindValidContentNode( !HasDrawView() || 1762 0 == Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount())) 1763 { 1764 // everything protected/hidden -> special mode 1765 if( m_bAllProtect && !IsReadOnlyAvailable() && 1766 pSectNd->GetSection().IsProtectFlag() ) 1767 bChgState = false; 1768 else 1769 { 1770 m_eMvState = MV_NONE; // state for cursor travelling 1771 m_bAllProtect = true; 1772 if( GetDoc()->GetDocShell() ) 1773 { 1774 GetDoc()->GetDocShell()->SetReadOnlyUI(); 1775 CallChgLnk(); // notify UI! 1776 } 1777 return; 1778 } 1779 } 1780 } 1781 if( bChgState ) 1782 { 1783 bool bWasAllProtect = m_bAllProtect; 1784 m_bAllProtect = false; 1785 if( bWasAllProtect && GetDoc()->GetDocShell() && 1786 GetDoc()->GetDocShell()->IsReadOnlyUI() ) 1787 { 1788 GetDoc()->GetDocShell()->SetReadOnlyUI( false ); 1789 CallChgLnk(); // notify UI! 1790 } 1791 } 1792 } 1793 1794 UpdateCursorPos(); 1795 1796 // The cursor must always point into content; there's some code 1797 // that relies on this. (E.g. in SwEditShell::GetScriptType, which always 1798 // loops _behind_ the last node in the selection, which always works if you 1799 // are in content.) To achieve this, we'll force cursor(s) to point into 1800 // content, if UpdateCursorPos() hasn't already done so. 1801 for(SwPaM& rCmp : m_pCurrentCursor->GetRingContainer()) 1802 { 1803 // start will move forwards, end will move backwards 1804 bool bPointIsStart = ( rCmp.Start() == rCmp.GetPoint() ); 1805 1806 // move point; forward if it's the start, backwards if it's the end 1807 if( ! rCmp.GetPoint()->nNode.GetNode().IsContentNode() ) 1808 rCmp.Move( bPointIsStart ? fnMoveForward : fnMoveBackward, 1809 GoInContent ); 1810 1811 // move mark (if exists); forward if it's the start, else backwards 1812 if( rCmp.HasMark() ) 1813 { 1814 if( ! rCmp.GetMark()->nNode.GetNode().IsContentNode() ) 1815 { 1816 rCmp.Exchange(); 1817 rCmp.Move( !bPointIsStart ? fnMoveForward : fnMoveBackward, 1818 GoInContent ); 1819 rCmp.Exchange(); 1820 } 1821 } 1822 } 1823 1824 SwRect aOld( m_aCharRect ); 1825 bool bFirst = true; 1826 SwContentFrame *pFrame; 1827 int nLoopCnt = 100; 1828 SwShellCursor* pShellCursor = getShellCursor( true ); 1829 1830 do { 1831 bool bAgainst; 1832 do { 1833 bAgainst = false; 1834 std::pair<Point, bool> const tmp1(pShellCursor->GetPtPos(), false); 1835 pFrame = pShellCursor->GetContentNode()->getLayoutFrame(GetLayout(), 1836 pShellCursor->GetPoint(), &tmp1); 1837 // if the Frame doesn't exist anymore, the complete Layout has to be 1838 // created, because there used to be a Frame here! 1839 if ( !pFrame ) 1840 { 1841 do 1842 { 1843 CalcLayout(); 1844 std::pair<Point, bool> const tmp(pShellCursor->GetPtPos(), false); 1845 pFrame = pShellCursor->GetContentNode()->getLayoutFrame( 1846 GetLayout(), pShellCursor->GetPoint(), &tmp); 1847 } while( !pFrame ); 1848 } 1849 else if ( Imp()->IsIdleAction() ) 1850 // Guarantee everything's properly formatted 1851 pFrame->PrepareCursor(); 1852 1853 // In protected Fly? but ignore in case of frame selection 1854 if( !IsReadOnlyAvailable() && pFrame->IsProtected() && 1855 ( !Imp()->GetDrawView() || 1856 !Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() ) && 1857 (!mxDoc->GetDocShell() || 1858 !mxDoc->GetDocShell()->IsReadOnly() || m_bAllProtect ) ) 1859 { 1860 // look for a valid position 1861 bool bChgState = true; 1862 if( !FindValidContentNode(!HasDrawView() || 1863 0 == Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount())) 1864 { 1865 // everything is protected / hidden -> special Mode 1866 if( m_bAllProtect ) 1867 bChgState = false; 1868 else 1869 { 1870 m_eMvState = MV_NONE; // state for cursor travelling 1871 m_bAllProtect = true; 1872 if( GetDoc()->GetDocShell() ) 1873 { 1874 GetDoc()->GetDocShell()->SetReadOnlyUI(); 1875 CallChgLnk(); // notify UI! 1876 } 1877 return; 1878 } 1879 } 1880 1881 if( bChgState ) 1882 { 1883 bool bWasAllProtect = m_bAllProtect; 1884 m_bAllProtect = false; 1885 if( bWasAllProtect && GetDoc()->GetDocShell() && 1886 GetDoc()->GetDocShell()->IsReadOnlyUI() ) 1887 { 1888 GetDoc()->GetDocShell()->SetReadOnlyUI( false ); 1889 CallChgLnk(); // notify UI! 1890 } 1891 m_bAllProtect = false; 1892 bAgainst = true; // look for the right Frame again 1893 } 1894 } 1895 } while( bAgainst ); 1896 1897 SwCursorMoveState aTmpState( m_eMvState ); 1898 aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable(); 1899 aTmpState.m_bRealHeight = true; 1900 aTmpState.m_bRealWidth = IsOverwriteCursor(); 1901 aTmpState.m_nCursorBidiLevel = pShellCursor->GetCursorBidiLevel(); 1902 1903 // #i27615#,#i30453# 1904 SwSpecialPos aSpecialPos; 1905 aSpecialPos.nExtendRange = SwSPExtendRange::BEFORE; 1906 if (pShellCursor->IsInFrontOfLabel()) 1907 { 1908 aTmpState.m_pSpecialPos = &aSpecialPos; 1909 } 1910 1911 { 1912 DisableCallbackAction a(*GetLayout()); // tdf#91602 prevent recursive Action 1913 if (!pFrame->GetCharRect(m_aCharRect, *pShellCursor->GetPoint(), &aTmpState)) 1914 { 1915 Point& rPt = pShellCursor->GetPtPos(); 1916 rPt = m_aCharRect.Center(); 1917 pFrame->GetCursorOfst( pShellCursor->GetPoint(), rPt, &aTmpState ); 1918 } 1919 } 1920 UISizeNotify(); // tdf#96256 update view size 1921 1922 if( !pShellCursor->HasMark() ) 1923 m_aCursorHeight = aTmpState.m_aRealHeight; 1924 else 1925 { 1926 m_aCursorHeight.setX(0); 1927 m_aCursorHeight.setY(aTmpState.m_aRealHeight.getY() < 0 ? 1928 -m_aCharRect.Width() : m_aCharRect.Height()); 1929 } 1930 1931 if( !bFirst && aOld == m_aCharRect ) 1932 break; 1933 1934 // if the layout says that we are after the 100th iteration still in 1935 // flow then we should always take the current position for granted. 1936 // (see bug: 29658) 1937 if( !--nLoopCnt ) 1938 { 1939 OSL_ENSURE( false, "endless loop? CharRect != OldCharRect "); 1940 break; 1941 } 1942 aOld = m_aCharRect; 1943 bFirst = false; 1944 1945 // update cursor Points to the new Positions 1946 pShellCursor->GetPtPos().setX(m_aCharRect.Left()); 1947 pShellCursor->GetPtPos().setY(m_aCharRect.Top()); 1948 1949 if( !(eFlags & SwCursorShell::UPDOWN )) // delete old Pos. of Up/Down 1950 { 1951 DisableCallbackAction a(*GetLayout()); 1952 pFrame->Calc(GetOut()); 1953 m_nUpDownX = pFrame->IsVertical() ? 1954 m_aCharRect.Top() - pFrame->getFrameArea().Top() : 1955 m_aCharRect.Left() - pFrame->getFrameArea().Left(); 1956 } 1957 1958 // scroll Cursor to visible area 1959 if( m_bHasFocus && eFlags & SwCursorShell::SCROLLWIN && 1960 (HasSelection() || eFlags & SwCursorShell::READONLY || 1961 !IsCursorReadonly() || GetViewOptions()->IsSelectionInReadonly()) ) 1962 { 1963 // in case of scrolling this EndAction doesn't show the SV cursor 1964 // again, thus save and reset the flag here 1965 bool bSav = m_bSVCursorVis; 1966 m_bSVCursorVis = false; 1967 MakeSelVisible(); 1968 m_bSVCursorVis = bSav; 1969 } 1970 1971 } while( eFlags & SwCursorShell::SCROLLWIN ); 1972 1973 assert(pFrame); 1974 1975 if( m_pBlockCursor ) 1976 RefreshBlockCursor(); 1977 1978 // We should not restrict cursor update to the active view when using LOK 1979 bool bCheckFocus = m_bHasFocus || comphelper::LibreOfficeKit::isActive(); 1980 1981 if( !bIdleEnd && bCheckFocus && !m_bBasicHideCursor ) 1982 { 1983 if( m_pTableCursor ) 1984 m_pTableCursor->SwSelPaintRects::Show(); 1985 else 1986 { 1987 m_pCurrentCursor->SwSelPaintRects::Show(); 1988 if( m_pBlockCursor ) 1989 { 1990 SwShellCursor* pNxt = m_pCurrentCursor->GetNext(); 1991 while( pNxt && pNxt != m_pCurrentCursor ) 1992 { 1993 pNxt->SwSelPaintRects::Show(); 1994 pNxt = pNxt->GetNext(); 1995 } 1996 } 1997 } 1998 } 1999 2000 m_eMvState = MV_NONE; // state for cursor travelling - GetCursorOfst 2001 2002 if (Imp()->IsAccessible()) 2003 Imp()->InvalidateAccessibleCursorPosition( pFrame ); 2004 2005 // switch from blinking cursor to read-only-text-selection cursor 2006 const sal_uInt64 nBlinkTime = GetOut()->GetSettings().GetStyleSettings(). 2007 GetCursorBlinkTime(); 2008 2009 if ( (IsCursorReadonly() && GetViewOptions()->IsSelectionInReadonly()) == 2010 ( nBlinkTime != STYLE_CURSOR_NOBLINKTIME ) ) 2011 { 2012 // non blinking cursor in read only - text selection mode 2013 AllSettings aSettings = GetOut()->GetSettings(); 2014 StyleSettings aStyleSettings = aSettings.GetStyleSettings(); 2015 const sal_uInt64 nNewBlinkTime = nBlinkTime == STYLE_CURSOR_NOBLINKTIME ? 2016 Application::GetSettings().GetStyleSettings().GetCursorBlinkTime() : 2017 STYLE_CURSOR_NOBLINKTIME; 2018 aStyleSettings.SetCursorBlinkTime( nNewBlinkTime ); 2019 aSettings.SetStyleSettings( aStyleSettings ); 2020 GetOut()->SetSettings( aSettings ); 2021 } 2022 2023 if( m_bSVCursorVis ) 2024 m_pVisibleCursor->Show(); // show again 2025 2026 getIDocumentMarkAccess()->NotifyCursorUpdate(*this); 2027 } 2028 2029 void SwCursorShell::RefreshBlockCursor() 2030 { 2031 assert(m_pBlockCursor); 2032 SwShellCursor &rBlock = m_pBlockCursor->getShellCursor(); 2033 Point aPt = rBlock.GetPtPos(); 2034 std::pair<Point, bool> const tmp(aPt, false); 2035 SwContentFrame* pFrame = rBlock.GetContentNode()->getLayoutFrame( 2036 GetLayout(), rBlock.GetPoint(), &tmp); 2037 Point aMk; 2038 if( m_pBlockCursor->getEndPoint() && m_pBlockCursor->getStartPoint() ) 2039 { 2040 aPt = *m_pBlockCursor->getStartPoint(); 2041 aMk = *m_pBlockCursor->getEndPoint(); 2042 } 2043 else 2044 { 2045 aPt = rBlock.GetPtPos(); 2046 if( pFrame ) 2047 { 2048 if( pFrame->IsVertical() ) 2049 aPt.setY(pFrame->getFrameArea().Top() + GetUpDownX()); 2050 else 2051 aPt.setX(pFrame->getFrameArea().Left() + GetUpDownX()); 2052 } 2053 aMk = rBlock.GetMkPos(); 2054 } 2055 SwRect aRect( aMk, aPt ); 2056 aRect.Justify(); 2057 SwSelectionList aSelList( pFrame ); 2058 2059 if( GetLayout()->FillSelection( aSelList, aRect ) ) 2060 { 2061 SwCursor* pNxt = static_cast<SwCursor*>(m_pCurrentCursor->GetNext()); 2062 while( pNxt != m_pCurrentCursor ) 2063 { 2064 delete pNxt; 2065 pNxt = static_cast<SwCursor*>(m_pCurrentCursor->GetNext()); 2066 } 2067 2068 std::list<SwPaM*>::iterator pStart = aSelList.getStart(); 2069 std::list<SwPaM*>::iterator pPam = aSelList.getEnd(); 2070 OSL_ENSURE( pPam != pStart, "FillSelection should deliver at least one PaM" ); 2071 m_pCurrentCursor->SetMark(); 2072 --pPam; 2073 // If there is only one text portion inside the rectangle, a simple 2074 // selection is created 2075 if( pPam == pStart ) 2076 { 2077 *m_pCurrentCursor->GetPoint() = *(*pPam)->GetPoint(); 2078 if( (*pPam)->HasMark() ) 2079 *m_pCurrentCursor->GetMark() = *(*pPam)->GetMark(); 2080 else 2081 m_pCurrentCursor->DeleteMark(); 2082 delete *pPam; 2083 m_pCurrentCursor->SetColumnSelection( false ); 2084 } 2085 else 2086 { 2087 // The order of the SwSelectionList has to be preserved but 2088 // the order inside the ring created by CreateCursor() is not like 2089 // expected => First create the selections before the last one 2090 // downto the first selection. 2091 // At least create the cursor for the last selection 2092 --pPam; 2093 *m_pCurrentCursor->GetPoint() = *(*pPam)->GetPoint(); // n-1 (if n == number of selections) 2094 if( (*pPam)->HasMark() ) 2095 *m_pCurrentCursor->GetMark() = *(*pPam)->GetMark(); 2096 else 2097 m_pCurrentCursor->DeleteMark(); 2098 delete *pPam; 2099 m_pCurrentCursor->SetColumnSelection( true ); 2100 while( pPam != pStart ) 2101 { 2102 --pPam; 2103 2104 SwShellCursor* pNew = new SwShellCursor( *m_pCurrentCursor ); 2105 pNew->insert( pNew->begin(), m_pCurrentCursor->begin(), m_pCurrentCursor->end()); 2106 m_pCurrentCursor->clear(); 2107 m_pCurrentCursor->DeleteMark(); 2108 2109 *m_pCurrentCursor->GetPoint() = *(*pPam)->GetPoint(); // n-2, n-3, .., 2, 1 2110 if( (*pPam)->HasMark() ) 2111 { 2112 m_pCurrentCursor->SetMark(); 2113 *m_pCurrentCursor->GetMark() = *(*pPam)->GetMark(); 2114 } 2115 else 2116 m_pCurrentCursor->DeleteMark(); 2117 m_pCurrentCursor->SetColumnSelection( true ); 2118 delete *pPam; 2119 } 2120 { 2121 SwShellCursor* pNew = new SwShellCursor( *m_pCurrentCursor ); 2122 pNew->insert( pNew->begin(), m_pCurrentCursor->begin(), m_pCurrentCursor->end() ); 2123 m_pCurrentCursor->clear(); 2124 m_pCurrentCursor->DeleteMark(); 2125 } 2126 pPam = aSelList.getEnd(); 2127 --pPam; 2128 *m_pCurrentCursor->GetPoint() = *(*pPam)->GetPoint(); // n, the last selection 2129 if( (*pPam)->HasMark() ) 2130 { 2131 m_pCurrentCursor->SetMark(); 2132 *m_pCurrentCursor->GetMark() = *(*pPam)->GetMark(); 2133 } 2134 else 2135 m_pCurrentCursor->DeleteMark(); 2136 m_pCurrentCursor->SetColumnSelection( true ); 2137 delete *pPam; 2138 } 2139 } 2140 } 2141 2142 /// create a copy of the cursor and save it in the stack 2143 void SwCursorShell::Push() 2144 { 2145 // fdo#60513: if we have a table cursor, copy that; else copy current. 2146 // This seems to work because UpdateCursor() will fix this up on Pop(), 2147 // then MakeBoxSels() will re-create the current m_pCurrentCursor cell ring. 2148 SwShellCursor *const pCurrent(m_pTableCursor ? m_pTableCursor : m_pCurrentCursor); 2149 m_pStackCursor = new SwShellCursor( *this, *pCurrent->GetPoint(), 2150 pCurrent->GetPtPos(), m_pStackCursor ); 2151 2152 if (pCurrent->HasMark()) 2153 { 2154 m_pStackCursor->SetMark(); 2155 *m_pStackCursor->GetMark() = *pCurrent->GetMark(); 2156 } 2157 } 2158 2159 /** delete cursor 2160 2161 @param eDelete delete from stack, or delete current 2162 and assign the one from stack as the new current cursor. 2163 @return <true> if there was one on the stack, <false> otherwise 2164 */ 2165 bool SwCursorShell::Pop(PopMode const eDelete) 2166 { 2167 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 2168 2169 // are there any left? 2170 if (nullptr == m_pStackCursor) 2171 return false; 2172 2173 SwShellCursor *pTmp = nullptr, *pOldStack = m_pStackCursor; 2174 2175 // the successor becomes the current one 2176 if (m_pStackCursor->GetNext() != m_pStackCursor) 2177 { 2178 pTmp = m_pStackCursor->GetNext(); 2179 } 2180 2181 if (PopMode::DeleteStack == eDelete) 2182 delete m_pStackCursor; 2183 2184 m_pStackCursor = pTmp; // assign new one 2185 2186 if (PopMode::DeleteCurrent == eDelete) 2187 { 2188 SwCursorSaveState aSaveState( *m_pCurrentCursor ); 2189 2190 // If the visible SSelection was not changed 2191 const Point& rPoint = pOldStack->GetPtPos(); 2192 if (rPoint == m_pCurrentCursor->GetPtPos() || rPoint == m_pCurrentCursor->GetMkPos()) 2193 { 2194 // move "Selections Rectangles" 2195 m_pCurrentCursor->insert( m_pCurrentCursor->begin(), pOldStack->begin(), pOldStack->end() ); 2196 pOldStack->clear(); 2197 } 2198 2199 if( pOldStack->HasMark() ) 2200 { 2201 m_pCurrentCursor->SetMark(); 2202 *m_pCurrentCursor->GetMark() = *pOldStack->GetMark(); 2203 m_pCurrentCursor->GetMkPos() = pOldStack->GetMkPos(); 2204 } 2205 else 2206 // no selection so revoke old one and set to old position 2207 m_pCurrentCursor->DeleteMark(); 2208 *m_pCurrentCursor->GetPoint() = *pOldStack->GetPoint(); 2209 m_pCurrentCursor->GetPtPos() = pOldStack->GetPtPos(); 2210 delete pOldStack; 2211 2212 if( !m_pCurrentCursor->IsInProtectTable( true ) && 2213 !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle | 2214 SwCursorSelOverFlags::ChangePos ) ) 2215 { 2216 UpdateCursor(); // update current cursor 2217 if (m_pTableCursor) 2218 { // tdf#106929 ensure m_pCurrentCursor ring is recreated from table 2219 m_pTableCursor->SetChgd(); 2220 } 2221 } 2222 } 2223 return true; 2224 } 2225 2226 /** Combine two cursors 2227 2228 Delete topmost from stack and use its GetMark in the current. 2229 */ 2230 void SwCursorShell::Combine() 2231 { 2232 // any others left? 2233 if (nullptr == m_pStackCursor) 2234 return; 2235 2236 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 2237 // rhbz#689053: IsSelOvr must restore the saved stack position, not the 2238 // current one, because current point + stack mark may be invalid PaM 2239 SwCursorSaveState aSaveState(*m_pStackCursor); 2240 // stack cursor & current cursor in same Section? 2241 assert(!m_pStackCursor->HasMark() || 2242 CheckNodesRange(m_pStackCursor->GetMark()->nNode, 2243 m_pCurrentCursor->GetPoint()->nNode, true)); 2244 *m_pStackCursor->GetPoint() = *m_pCurrentCursor->GetPoint(); 2245 m_pStackCursor->GetPtPos() = m_pCurrentCursor->GetPtPos(); 2246 2247 SwShellCursor * pTmp = nullptr; 2248 if (m_pStackCursor->GetNext() != m_pStackCursor) 2249 { 2250 pTmp = m_pStackCursor->GetNext(); 2251 } 2252 delete m_pCurrentCursor; 2253 m_pCurrentCursor = m_pStackCursor; 2254 m_pStackCursor->MoveTo(nullptr); // remove from ring 2255 m_pStackCursor = pTmp; 2256 if( !m_pCurrentCursor->IsInProtectTable( true ) && 2257 !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle | 2258 SwCursorSelOverFlags::ChangePos ) ) 2259 { 2260 UpdateCursor(); // update current cursor 2261 } 2262 } 2263 2264 void SwCursorShell::HideCursors() 2265 { 2266 if( !m_bHasFocus || m_bBasicHideCursor ) 2267 return; 2268 2269 // if cursor is visible then hide SV cursor 2270 if( m_pVisibleCursor->IsVisible() ) 2271 { 2272 SET_CURR_SHELL( this ); 2273 m_pVisibleCursor->Hide(); 2274 } 2275 // revoke inversion of SSelection 2276 SwShellCursor* pCurrentCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor; 2277 pCurrentCursor->Hide(); 2278 } 2279 2280 void SwCursorShell::ShowCursors( bool bCursorVis ) 2281 { 2282 if( !m_bHasFocus || m_bAllProtect || m_bBasicHideCursor ) 2283 return; 2284 2285 SET_CURR_SHELL( this ); 2286 SwShellCursor* pCurrentCursor = m_pTableCursor ? m_pTableCursor : m_pCurrentCursor; 2287 pCurrentCursor->Show(nullptr); 2288 2289 if( m_bSVCursorVis && bCursorVis ) // also show SV cursor again 2290 m_pVisibleCursor->Show(); 2291 } 2292 2293 void SwCursorShell::ShowCursor() 2294 { 2295 if( !m_bBasicHideCursor ) 2296 { 2297 m_bSVCursorVis = true; 2298 m_pCurrentCursor->SetShowTextInputFieldOverlay( true ); 2299 2300 if (comphelper::LibreOfficeKit::isActive()) 2301 { 2302 OString aPayload = OString::boolean(m_bSVCursorVis); 2303 GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE, aPayload.getStr()); 2304 SfxLokHelper::notifyOtherViews(GetSfxViewShell(), LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", aPayload); 2305 } 2306 2307 UpdateCursor(); 2308 } 2309 } 2310 2311 void SwCursorShell::HideCursor() 2312 { 2313 if( !m_bBasicHideCursor ) 2314 { 2315 m_bSVCursorVis = false; 2316 // possibly reverse selected areas!! 2317 SET_CURR_SHELL( this ); 2318 m_pCurrentCursor->SetShowTextInputFieldOverlay( false ); 2319 m_pVisibleCursor->Hide(); 2320 2321 if (comphelper::LibreOfficeKit::isActive()) 2322 { 2323 OString aPayload = OString::boolean(m_bSVCursorVis); 2324 GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE, aPayload.getStr()); 2325 SfxLokHelper::notifyOtherViews(GetSfxViewShell(), LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", aPayload); 2326 } 2327 } 2328 } 2329 2330 void SwCursorShell::ShellLoseFocus() 2331 { 2332 if( !m_bBasicHideCursor ) 2333 HideCursors(); 2334 m_bHasFocus = false; 2335 } 2336 2337 void SwCursorShell::ShellGetFocus() 2338 { 2339 m_bHasFocus = true; 2340 if( !m_bBasicHideCursor && VisArea().Width() ) 2341 { 2342 UpdateCursor( static_cast<sal_uInt16>( SwCursorShell::CHKRANGE ) ); 2343 ShowCursors( m_bSVCursorVis ); 2344 } 2345 } 2346 2347 /** Get current frame in which the cursor is positioned. */ 2348 SwContentFrame *SwCursorShell::GetCurrFrame( const bool bCalcFrame ) const 2349 { 2350 SET_CURR_SHELL( const_cast<SwCursorShell*>(this) ); 2351 SwContentFrame *pRet = nullptr; 2352 SwContentNode *pNd = m_pCurrentCursor->GetContentNode(); 2353 if ( pNd ) 2354 { 2355 if ( bCalcFrame ) 2356 { 2357 sal_uInt16* pST = const_cast<sal_uInt16*>(&mnStartAction); 2358 ++(*pST); 2359 const Size aOldSz( GetDocSize() ); 2360 std::pair<Point, bool> const tmp(m_pCurrentCursor->GetPtPos(), true); 2361 pRet = pNd->getLayoutFrame(GetLayout(), m_pCurrentCursor->GetPoint(), &tmp); 2362 --(*pST); 2363 if( aOldSz != GetDocSize() ) 2364 const_cast<SwCursorShell*>(this)->SizeChgNotify(); 2365 } 2366 else 2367 { 2368 std::pair<Point, bool> const tmp(m_pCurrentCursor->GetPtPos(), false); 2369 pRet = pNd->getLayoutFrame(GetLayout(), m_pCurrentCursor->GetPoint(), &tmp); 2370 } 2371 } 2372 return pRet; 2373 } 2374 2375 //TODO: provide documentation 2376 /** forward all attribute/format changes at the current node to the Link 2377 2378 @param pOld ??? 2379 @param pNew ??? 2380 */ 2381 void SwCursorShell::Modify( const SfxPoolItem* pOld, const SfxPoolItem* pNew ) 2382 { 2383 const sal_uInt16 nWhich = pOld ? 2384 pOld->Which() : 2385 pNew ? 2386 pNew->Which() : 2387 sal::static_int_cast<sal_uInt16>(RES_MSG_BEGIN); 2388 2389 if( m_bCallChgLnk && 2390 ( nWhich < RES_MSG_BEGIN || nWhich >= RES_MSG_END || 2391 nWhich == RES_FMT_CHG || nWhich == RES_UPDATE_ATTR || 2392 nWhich == RES_ATTRSET_CHG )) 2393 // messages are not forwarded 2394 // #i6681#: RES_UPDATE_ATTR is implicitly unset in 2395 // SwTextNode::Insert(SwTextHint*, sal_uInt16); we react here and thus do 2396 // not need to send the expensive RES_FMT_CHG in Insert. 2397 CallChgLnk(); 2398 2399 if( m_aGrfArrivedLnk.IsSet() && 2400 ( RES_GRAPHIC_ARRIVED == nWhich || RES_GRAPHIC_SWAPIN == nWhich )) 2401 m_aGrfArrivedLnk.Call( *this ); 2402 } 2403 2404 /** Does the current cursor create a selection? 2405 2406 This means checking if GetMark is set and if SPoint and GetMark differ. 2407 */ 2408 bool SwCursorShell::HasSelection() const 2409 { 2410 const SwPaM* pCursor = getShellCursor( true ); 2411 return IsTableMode() || ( pCursor->HasMark() && *pCursor->GetPoint() != *pCursor->GetMark() ); 2412 } 2413 2414 void SwCursorShell::CallChgLnk() 2415 { 2416 // Do not make any call in StartAction/EndAction but just set the flag. 2417 // This will be handled in EndAction. 2418 if (ActionPend()) 2419 m_bChgCallFlag = true; // remember change 2420 else if( m_aChgLnk.IsSet() ) 2421 { 2422 if( m_bCallChgLnk ) 2423 m_aChgLnk.Call( this ); 2424 m_bChgCallFlag = false; // reset flag 2425 } 2426 } 2427 2428 /// get selected text of a node at current cursor 2429 OUString SwCursorShell::GetSelText() const 2430 { 2431 OUString aText; 2432 if (GetLayout()->IsHideRedlines()) 2433 { 2434 SwContentFrame const*const pFrame(GetCurrFrame(false)); 2435 if (FrameContainsNode(*pFrame, m_pCurrentCursor->GetMark()->nNode.GetIndex())) 2436 { 2437 OUStringBuffer buf; 2438 SwPosition const*const pStart(m_pCurrentCursor->Start()); 2439 SwPosition const*const pEnd(m_pCurrentCursor->End()); 2440 for (sal_uLong i = pStart->nNode.GetIndex(); i <= pEnd->nNode.GetIndex(); ++i) 2441 { 2442 SwNode const& rNode(*pStart->nNode.GetNodes()[i]); 2443 assert(!rNode.IsEndNode()); 2444 if (rNode.IsStartNode()) 2445 { 2446 i = rNode.EndOfSectionIndex(); 2447 } 2448 else if (rNode.IsTextNode()) 2449 { 2450 sal_Int32 const nStart(i == pStart->nNode.GetIndex() 2451 ? pStart->nContent.GetIndex() 2452 : 0); 2453 sal_Int32 const nEnd(i == pEnd->nNode.GetIndex() 2454 ? pEnd->nContent.GetIndex() 2455 : rNode.GetTextNode()->Len()); 2456 buf.append(rNode.GetTextNode()->GetExpandText( 2457 GetLayout(), 2458 nStart, nEnd - nStart, false, false, false, 2459 ExpandMode::HideDeletions)); 2460 2461 } 2462 } 2463 aText = buf.makeStringAndClear(); 2464 } 2465 } 2466 else if( m_pCurrentCursor->GetPoint()->nNode.GetIndex() == 2467 m_pCurrentCursor->GetMark()->nNode.GetIndex() ) 2468 { 2469 SwTextNode* pTextNd = m_pCurrentCursor->GetNode().GetTextNode(); 2470 if( pTextNd ) 2471 { 2472 const sal_Int32 nStt = m_pCurrentCursor->Start()->nContent.GetIndex(); 2473 aText = pTextNd->GetExpandText(GetLayout(), nStt, 2474 m_pCurrentCursor->End()->nContent.GetIndex() - nStt ); 2475 } 2476 } 2477 return aText; 2478 } 2479 2480 /** get the nth character of the current SSelection 2481 2482 @param bEnd Start counting from the end? From start otherwise. 2483 @param nOffset position of the character 2484 */ 2485 sal_Unicode SwCursorShell::GetChar( bool bEnd, long nOffset ) 2486 { 2487 if( IsTableMode() ) // not possible in table mode 2488 return 0; 2489 2490 const SwPosition* pPos = !m_pCurrentCursor->HasMark() ? m_pCurrentCursor->GetPoint() 2491 : bEnd ? m_pCurrentCursor->End() : m_pCurrentCursor->Start(); 2492 SwTextNode* pTextNd = pPos->nNode.GetNode().GetTextNode(); 2493 if( !pTextNd ) 2494 return 0; 2495 2496 const sal_Int32 nPos = pPos->nContent.GetIndex(); 2497 const OUString& rStr = pTextNd->GetText(); 2498 sal_Unicode cCh = 0; 2499 2500 if (((nPos+nOffset) >= 0 ) && (nPos+nOffset) < rStr.getLength()) 2501 cCh = rStr[nPos + nOffset]; 2502 2503 return cCh; 2504 } 2505 2506 /** extend current SSelection by n characters 2507 2508 @param bEnd Start counting from the end? From start otherwise. 2509 @param nCount Number of characters. 2510 */ 2511 bool SwCursorShell::ExtendSelection( bool bEnd, sal_Int32 nCount ) 2512 { 2513 if( !m_pCurrentCursor->HasMark() || IsTableMode() ) 2514 return false; // no selection 2515 2516 SwPosition* pPos = bEnd ? m_pCurrentCursor->End() : m_pCurrentCursor->Start(); 2517 SwTextNode* pTextNd = pPos->nNode.GetNode().GetTextNode(); 2518 assert(pTextNd); 2519 2520 sal_Int32 nPos = pPos->nContent.GetIndex(); 2521 if( bEnd ) 2522 { 2523 if ((nPos + nCount) <= pTextNd->GetText().getLength()) 2524 nPos = nPos + nCount; 2525 else 2526 return false; // not possible 2527 } 2528 else if( nPos >= nCount ) 2529 nPos = nPos - nCount; 2530 else 2531 return false; // not possible anymore 2532 2533 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 2534 2535 pPos->nContent = nPos; 2536 UpdateCursor(); 2537 2538 return true; 2539 } 2540 2541 /** Move visible cursor to given position in document. 2542 2543 @param rPt The position to move the visible cursor to. 2544 @return <false> if SPoint was corrected by the layout. 2545 */ 2546 bool SwCursorShell::SetVisibleCursor( const Point &rPt ) 2547 { 2548 SET_CURR_SHELL( this ); 2549 Point aPt( rPt ); 2550 SwPosition aPos( *m_pCurrentCursor->GetPoint() ); 2551 SwCursorMoveState aTmpState( MV_SETONLYTEXT ); 2552 aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable(); 2553 aTmpState.m_bRealHeight = true; 2554 2555 const bool bRet = GetLayout()->GetCursorOfst( &aPos, aPt /*, &aTmpState*/ ); 2556 2557 SetInFrontOfLabel( false ); // #i27615# 2558 2559 // show only in TextNodes 2560 SwTextNode* pTextNd = aPos.nNode.GetNode().GetTextNode(); 2561 if( !pTextNd ) 2562 return false; 2563 2564 const SwSectionNode* pSectNd = pTextNd->FindSectionNode(); 2565 if( pSectNd && (pSectNd->GetSection().IsHiddenFlag() || 2566 ( !IsReadOnlyAvailable() && 2567 pSectNd->GetSection().IsProtectFlag())) ) 2568 return false; 2569 2570 std::pair<Point, bool> const tmp(aPt, true); 2571 SwContentFrame *pFrame = pTextNd->getLayoutFrame(GetLayout(), &aPos, &tmp); 2572 if ( Imp()->IsIdleAction() ) 2573 pFrame->PrepareCursor(); 2574 SwRect aTmp( m_aCharRect ); 2575 2576 pFrame->GetCharRect( m_aCharRect, aPos, &aTmpState ); 2577 2578 // #i10137# 2579 if( aTmp == m_aCharRect && m_pVisibleCursor->IsVisible() ) 2580 return true; 2581 2582 m_pVisibleCursor->Hide(); // always hide visible cursor 2583 if( IsScrollMDI( this, m_aCharRect )) 2584 { 2585 MakeVisible( m_aCharRect ); 2586 m_pCurrentCursor->Show(nullptr); 2587 } 2588 2589 { 2590 if( aTmpState.m_bRealHeight ) 2591 m_aCursorHeight = aTmpState.m_aRealHeight; 2592 else 2593 { 2594 m_aCursorHeight.setX(0); 2595 m_aCursorHeight.setY(m_aCharRect.Height()); 2596 } 2597 2598 m_pVisibleCursor->SetDragCursor(); 2599 m_pVisibleCursor->Show(); // show again 2600 } 2601 return bRet; 2602 } 2603 2604 bool SwCursorShell::IsOverReadOnlyPos( const Point& rPt ) const 2605 { 2606 Point aPt( rPt ); 2607 SwPaM aPam( *m_pCurrentCursor->GetPoint() ); 2608 GetLayout()->GetCursorOfst( aPam.GetPoint(), aPt ); 2609 // form view 2610 return aPam.HasReadonlySel( GetViewOptions()->IsFormView() ); 2611 } 2612 2613 /** Get the number of elements in the ring of cursors 2614 2615 @param bAll If <false> get only spanned ones (= with selections) (Basic). 2616 */ 2617 sal_uInt16 SwCursorShell::GetCursorCnt( bool bAll ) const 2618 { 2619 SwPaM* pTmp = GetCursor()->GetNext(); 2620 sal_uInt16 n = (bAll || ( m_pCurrentCursor->HasMark() && 2621 *m_pCurrentCursor->GetPoint() != *m_pCurrentCursor->GetMark())) ? 1 : 0; 2622 while( pTmp != m_pCurrentCursor ) 2623 { 2624 if( bAll || ( pTmp->HasMark() && 2625 *pTmp->GetPoint() != *pTmp->GetMark())) 2626 ++n; 2627 pTmp = pTmp->GetNext(); 2628 } 2629 return n; 2630 } 2631 2632 bool SwCursorShell::IsStartOfDoc() const 2633 { 2634 if( m_pCurrentCursor->GetPoint()->nContent.GetIndex() ) 2635 return false; 2636 2637 // after EndOfIcons comes the content selection (EndNd+StNd+ContentNd) 2638 SwNodeIndex aIdx( GetDoc()->GetNodes().GetEndOfExtras(), 2 ); 2639 if( !aIdx.GetNode().IsContentNode() ) 2640 GetDoc()->GetNodes().GoNext( &aIdx ); 2641 return aIdx == m_pCurrentCursor->GetPoint()->nNode; 2642 } 2643 2644 bool SwCursorShell::IsEndOfDoc() const 2645 { 2646 SwNodeIndex aIdx( GetDoc()->GetNodes().GetEndOfContent(), -1 ); 2647 SwContentNode* pCNd = aIdx.GetNode().GetContentNode(); 2648 if( !pCNd ) 2649 pCNd = SwNodes::GoPrevious( &aIdx ); 2650 2651 return aIdx == m_pCurrentCursor->GetPoint()->nNode && 2652 pCNd->Len() == m_pCurrentCursor->GetPoint()->nContent.GetIndex(); 2653 } 2654 2655 /** Invalidate cursors 2656 2657 Delete all created cursors, set table crsr and last crsr to their TextNode 2658 (or StartNode?). They will then all re-created at the next ::GetCursor() call. 2659 2660 This is needed for Drag&Drop/ Clipboard-paste in tables. 2661 */ 2662 bool SwCursorShell::ParkTableCursor() 2663 { 2664 if( !m_pTableCursor ) 2665 return false; 2666 2667 m_pTableCursor->ParkCursor(); 2668 2669 while( m_pCurrentCursor->GetNext() != m_pCurrentCursor ) 2670 delete m_pCurrentCursor->GetNext(); 2671 2672 // *always* move cursor's Point and Mark 2673 m_pCurrentCursor->DeleteMark(); 2674 *m_pCurrentCursor->GetPoint() = *m_pTableCursor->GetPoint(); 2675 2676 return true; 2677 } 2678 2679 void SwCursorShell::ParkPams( SwPaM* pDelRg, SwShellCursor** ppDelRing ) 2680 { 2681 const SwPosition *pStt = pDelRg->Start(), 2682 *pEnd = pDelRg->GetPoint() == pStt ? pDelRg->GetMark() : pDelRg->GetPoint(); 2683 2684 SwPaM *pTmpDel = nullptr, *pTmp = *ppDelRing; 2685 2686 // search over the whole ring 2687 bool bGoNext; 2688 do { 2689 2690 if (!pTmp) 2691 break; 2692 2693 const SwPosition *pTmpStt = pTmp->Start(), 2694 *pTmpEnd = pTmp->GetPoint() == pTmpStt ? 2695 pTmp->GetMark() : pTmp->GetPoint(); 2696 // If a SPoint or GetMark are in a cursor area then cancel the old area. 2697 // During comparison keep in mind that End() is outside the area. 2698 if( *pStt <= *pTmpStt ) 2699 { 2700 if( *pEnd > *pTmpStt || 2701 ( *pEnd == *pTmpStt && *pEnd == *pTmpEnd )) 2702 pTmpDel = pTmp; 2703 } 2704 else 2705 if( *pStt < *pTmpEnd ) 2706 pTmpDel = pTmp; 2707 2708 bGoNext = true; 2709 if (pTmpDel) // is the pam in the range -> delete 2710 { 2711 bool bDelete = true; 2712 if( *ppDelRing == pTmpDel ) 2713 { 2714 if( *ppDelRing == m_pCurrentCursor ) 2715 { 2716 bDelete = GoNextCursor(); 2717 if( bDelete ) 2718 { 2719 bGoNext = false; 2720 pTmp = pTmp->GetNext(); 2721 } 2722 } 2723 else 2724 bDelete = false; // never delete the StackCursor 2725 } 2726 2727 if( bDelete ) 2728 { 2729 if (pTmp == pTmpDel) 2730 pTmp = nullptr; 2731 delete pTmpDel; // invalidate old area 2732 } 2733 else 2734 { 2735 pTmpDel->GetPoint()->nContent.Assign(nullptr, 0); 2736 pTmpDel->GetPoint()->nNode = 0; 2737 pTmpDel->DeleteMark(); 2738 } 2739 pTmpDel = nullptr; 2740 } 2741 if( bGoNext && pTmp ) 2742 pTmp = pTmp->GetNext(); 2743 2744 } while( !bGoNext || *ppDelRing != pTmp ); 2745 } 2746 2747 //TODO: provide documentation 2748 /** Remove selections and additional cursors of all shells. 2749 2750 The remaining cursor of the shell is parked. 2751 2752 @param rIdx ??? 2753 */ 2754 void SwCursorShell::ParkCursor( const SwNodeIndex &rIdx ) 2755 { 2756 SwNode *pNode = &rIdx.GetNode(); 2757 2758 // create a new PaM 2759 std::unique_ptr<SwPaM> pNew( new SwPaM( *GetCursor()->GetPoint() ) ); 2760 if( pNode->GetStartNode() ) 2761 { 2762 if( ( pNode = pNode->StartOfSectionNode())->IsTableNode() ) 2763 { 2764 // the given node is in a table, thus park cursor to table node 2765 // (outside of the table) 2766 pNew->GetPoint()->nNode = *pNode->StartOfSectionNode(); 2767 } 2768 else 2769 // Also on the start node itself. Then we need to request the start 2770 // node always via its end node! (StartOfSelection of StartNode is 2771 // the parent) 2772 pNew->GetPoint()->nNode = *pNode->EndOfSectionNode()->StartOfSectionNode(); 2773 } 2774 else 2775 pNew->GetPoint()->nNode = *pNode->StartOfSectionNode(); 2776 pNew->SetMark(); 2777 pNew->GetPoint()->nNode = *pNode->EndOfSectionNode(); 2778 2779 // take care of all shells 2780 for(SwViewShell& rTmp : GetRingContainer()) 2781 { 2782 if( dynamic_cast<const SwCursorShell *>(&rTmp) != nullptr) 2783 { 2784 SwCursorShell* pSh = static_cast<SwCursorShell*>(&rTmp); 2785 if (pSh->m_pStackCursor) 2786 pSh->ParkPams(pNew.get(), &pSh->m_pStackCursor); 2787 2788 pSh->ParkPams( pNew.get(), &pSh->m_pCurrentCursor ); 2789 if( pSh->m_pTableCursor ) 2790 { 2791 // set table cursor always to 0 and the current one always to 2792 // the beginning of the table 2793 SwPaM* pTCursor = pSh->GetTableCrs(); 2794 SwNode* pTableNd = pTCursor->GetPoint()->nNode.GetNode().FindTableNode(); 2795 if ( pTableNd ) 2796 { 2797 pTCursor->GetPoint()->nContent.Assign(nullptr, 0); 2798 pTCursor->GetPoint()->nNode = 0; 2799 pTCursor->DeleteMark(); 2800 pSh->m_pCurrentCursor->GetPoint()->nNode = *pTableNd; 2801 } 2802 } 2803 } 2804 } 2805 } 2806 2807 /** Copy constructor 2808 2809 Copy cursor position and add it to the ring. 2810 All views of a document are in the ring of the shell. 2811 */ 2812 SwCursorShell::SwCursorShell( SwCursorShell& rShell, vcl::Window *pInitWin ) 2813 : SwViewShell( rShell, pInitWin ) 2814 , SwModify( nullptr ) 2815 , m_pStackCursor( nullptr ) 2816 , m_pBlockCursor( nullptr ) 2817 , m_pTableCursor( nullptr ) 2818 , m_pBoxIdx( nullptr ) 2819 , m_pBoxPtr( nullptr ) 2820 , m_nUpDownX(0) 2821 , m_nLeftFramePos(0) 2822 , m_nCurrentNode(0) 2823 , m_nCurrentContent(0) 2824 , m_nCurrentNdTyp(SwNodeType::NONE) 2825 , m_nCursorMove( 0 ) 2826 , m_eMvState( MV_NONE ) 2827 , m_sMarkedListId() 2828 , m_nMarkedListLevel( 0 ) 2829 , m_oldColFrame(nullptr) 2830 { 2831 SET_CURR_SHELL( this ); 2832 // only keep the position of the current cursor of the copy shell 2833 m_pCurrentCursor = new SwShellCursor( *this, *(rShell.m_pCurrentCursor->GetPoint()) ); 2834 m_pCurrentCursor->GetContentNode()->Add( this ); 2835 2836 m_bAllProtect = m_bVisPortChgd = m_bChgCallFlag = m_bInCMvVisportChgd = 2837 m_bGCAttr = m_bIgnoreReadonly = m_bSelTableCells = m_bBasicHideCursor = 2838 m_bOverwriteCursor = false; 2839 m_bCallChgLnk = m_bHasFocus = m_bAutoUpdateCells = true; 2840 m_bSVCursorVis = true; 2841 m_bSetCursorInReadOnly = true; 2842 m_pVisibleCursor = new SwVisibleCursor( this ); 2843 m_bMacroExecAllowed = rShell.IsMacroExecAllowed(); 2844 } 2845 2846 /// default constructor 2847 SwCursorShell::SwCursorShell( SwDoc& rDoc, vcl::Window *pInitWin, 2848 const SwViewOption *pInitOpt ) 2849 : SwViewShell( rDoc, pInitWin, pInitOpt ) 2850 , SwModify( nullptr ) 2851 , m_pStackCursor( nullptr ) 2852 , m_pBlockCursor( nullptr ) 2853 , m_pTableCursor( nullptr ) 2854 , m_pBoxIdx( nullptr ) 2855 , m_pBoxPtr( nullptr ) 2856 , m_nUpDownX(0) 2857 , m_nLeftFramePos(0) 2858 , m_nCurrentNode(0) 2859 , m_nCurrentContent(0) 2860 , m_nCurrentNdTyp(SwNodeType::NONE) 2861 , m_nCursorMove( 0 ) 2862 , m_eMvState( MV_NONE ) // state for crsr-travelling - GetCursorOfst 2863 , m_sMarkedListId() 2864 , m_nMarkedListLevel( 0 ) 2865 , m_oldColFrame(nullptr) 2866 { 2867 SET_CURR_SHELL( this ); 2868 // create initial cursor and set it to first content position 2869 SwNodes& rNds = rDoc.GetNodes(); 2870 2871 SwNodeIndex aNodeIdx( *rNds.GetEndOfContent().StartOfSectionNode() ); 2872 SwContentNode* pCNd = rNds.GoNext( &aNodeIdx ); // go to the first ContentNode 2873 2874 m_pCurrentCursor = new SwShellCursor( *this, SwPosition( aNodeIdx, SwIndex( pCNd, 0 ))); 2875 2876 // Register shell as dependent at current node. As a result all attribute 2877 // changes can be forwarded via the Link. 2878 pCNd->Add( this ); 2879 2880 m_bAllProtect = m_bVisPortChgd = m_bChgCallFlag = m_bInCMvVisportChgd = 2881 m_bGCAttr = m_bIgnoreReadonly = m_bSelTableCells = m_bBasicHideCursor = 2882 m_bOverwriteCursor = false; 2883 m_bCallChgLnk = m_bHasFocus = m_bAutoUpdateCells = true; 2884 m_bSVCursorVis = true; 2885 m_bSetCursorInReadOnly = true; 2886 2887 m_pVisibleCursor = new SwVisibleCursor( this ); 2888 m_bMacroExecAllowed = true; 2889 } 2890 2891 SwCursorShell::~SwCursorShell() 2892 { 2893 // if it is not the last view then at least the field should be updated 2894 if( !unique() ) 2895 CheckTableBoxContent( m_pCurrentCursor->GetPoint() ); 2896 else 2897 ClearTableBoxContent(); 2898 2899 delete m_pVisibleCursor; 2900 delete m_pBlockCursor; 2901 delete m_pTableCursor; 2902 2903 // release cursors 2904 while(m_pCurrentCursor->GetNext() != m_pCurrentCursor) 2905 delete m_pCurrentCursor->GetNext(); 2906 delete m_pCurrentCursor; 2907 2908 // free stack 2909 if (m_pStackCursor) 2910 { 2911 while (m_pStackCursor->GetNext() != m_pStackCursor) 2912 delete m_pStackCursor->GetNext(); 2913 delete m_pStackCursor; 2914 } 2915 2916 // #i54025# - do not give a HTML parser that might potentially hang as 2917 // a client at the cursor shell the chance to hang itself on a TextNode 2918 EndListeningAll(); 2919 } 2920 2921 SwShellCursor* SwCursorShell::getShellCursor( bool bBlock ) 2922 { 2923 if( m_pTableCursor ) 2924 return m_pTableCursor; 2925 if( m_pBlockCursor && bBlock ) 2926 return &m_pBlockCursor->getShellCursor(); 2927 return m_pCurrentCursor; 2928 } 2929 2930 /** Should WaitPtr be switched on for the clipboard? 2931 2932 Wait for TableMode, multiple selections and more than x selected paragraphs. 2933 */ 2934 bool SwCursorShell::ShouldWait() const 2935 { 2936 if ( IsTableMode() || GetCursorCnt() > 1 ) 2937 return true; 2938 2939 if( HasDrawView() && GetDrawView()->GetMarkedObjectList().GetMarkCount() ) 2940 return true; 2941 2942 SwPaM* pPam = GetCursor(); 2943 return pPam->Start()->nNode.GetIndex() + 10 < 2944 pPam->End()->nNode.GetIndex(); 2945 } 2946 2947 size_t SwCursorShell::UpdateTableSelBoxes() 2948 { 2949 if (m_pTableCursor && (m_pTableCursor->IsChgd() || !m_pTableCursor->GetSelectedBoxesCount())) 2950 { 2951 GetLayout()->MakeTableCursors( *m_pTableCursor ); 2952 } 2953 return m_pTableCursor ? m_pTableCursor->GetSelectedBoxesCount() : 0; 2954 } 2955 2956 /// show the current selected "object" 2957 void SwCursorShell::MakeSelVisible() 2958 { 2959 OSL_ENSURE( m_bHasFocus, "no focus but cursor should be made visible?" ); 2960 if( m_aCursorHeight.Y() < m_aCharRect.Height() && m_aCharRect.Height() > VisArea().Height() ) 2961 { 2962 SwRect aTmp( m_aCharRect ); 2963 long nDiff = m_aCharRect.Height() - VisArea().Height(); 2964 if( nDiff < m_aCursorHeight.getX() ) 2965 aTmp.Top( nDiff + m_aCharRect.Top() ); 2966 else 2967 { 2968 aTmp.Top( m_aCursorHeight.getX() + m_aCharRect.Top() ); 2969 aTmp.Height( m_aCursorHeight.getY() ); 2970 } 2971 if( !aTmp.HasArea() ) 2972 { 2973 aTmp.SSize().AdjustHeight(1 ); 2974 aTmp.SSize().AdjustWidth(1 ); 2975 } 2976 MakeVisible( aTmp ); 2977 } 2978 else 2979 { 2980 if( m_aCharRect.HasArea() ) 2981 MakeVisible( m_aCharRect ); 2982 else 2983 { 2984 SwRect aTmp( m_aCharRect ); 2985 aTmp.SSize().AdjustHeight(1 ); aTmp.SSize().AdjustWidth(1 ); 2986 MakeVisible( aTmp ); 2987 } 2988 } 2989 } 2990 2991 /// search a valid content position (not protected/hidden) 2992 bool SwCursorShell::FindValidContentNode( bool bOnlyText ) 2993 { 2994 if( m_pTableCursor ) 2995 { 2996 assert(!"Did not remove table selection!"); 2997 return false; 2998 } 2999 3000 // #i45129# - everything is allowed in UI-readonly 3001 if( !m_bAllProtect && GetDoc()->GetDocShell() && 3002 GetDoc()->GetDocShell()->IsReadOnlyUI() ) 3003 return true; 3004 3005 if( m_pCurrentCursor->HasMark() ) 3006 ClearMark(); 3007 3008 // first check for frames 3009 SwNodeIndex& rNdIdx = m_pCurrentCursor->GetPoint()->nNode; 3010 sal_uLong nNdIdx = rNdIdx.GetIndex(); // keep backup 3011 SwNodes& rNds = mxDoc->GetNodes(); 3012 SwContentNode* pCNd = rNdIdx.GetNode().GetContentNode(); 3013 const SwContentFrame * pFrame; 3014 3015 if (pCNd && nullptr != (pFrame = pCNd->getLayoutFrame(GetLayout(), m_pCurrentCursor->GetPoint())) && 3016 !IsReadOnlyAvailable() && pFrame->IsProtected() && 3017 nNdIdx < rNds.GetEndOfExtras().GetIndex() ) 3018 { 3019 // skip protected frame 3020 SwPaM aPam( *m_pCurrentCursor->GetPoint() ); 3021 aPam.SetMark(); 3022 aPam.GetMark()->nNode = rNds.GetEndOfContent(); 3023 aPam.GetPoint()->nNode = *pCNd->EndOfSectionNode(); 3024 3025 bool bFirst = false; 3026 if( nullptr == (pCNd = ::GetNode( aPam, bFirst, fnMoveForward ))) 3027 { 3028 aPam.GetMark()->nNode = *rNds.GetEndOfPostIts().StartOfSectionNode(); 3029 pCNd = ::GetNode( aPam, bFirst, fnMoveBackward ); 3030 } 3031 3032 if( !pCNd ) // should *never* happen 3033 { 3034 rNdIdx = nNdIdx; // back to old node 3035 return false; 3036 } 3037 *m_pCurrentCursor->GetPoint() = *aPam.GetPoint(); 3038 } 3039 else if( bOnlyText && pCNd && pCNd->IsNoTextNode() ) 3040 { 3041 // set to beginning of document 3042 rNdIdx = mxDoc->GetNodes().GetEndOfExtras(); 3043 m_pCurrentCursor->GetPoint()->nContent.Assign( mxDoc->GetNodes().GoNext( 3044 &rNdIdx ), 0 ); 3045 nNdIdx = rNdIdx.GetIndex(); 3046 } 3047 3048 bool bOk = true; 3049 3050 // #i9059# cursor may not stand in protected cells 3051 // (unless cursor in protected areas is OK.) 3052 const SwTableNode* pTableNode = rNdIdx.GetNode().FindTableNode(); 3053 if( !IsReadOnlyAvailable() && 3054 pTableNode != nullptr && rNdIdx.GetNode().IsProtect() ) 3055 { 3056 // we're in a table, and we're in a protected area, so we're 3057 // probably in a protected cell. 3058 3059 // move forward into non-protected area. 3060 SwPaM aPam( rNdIdx.GetNode(), 0 ); 3061 while( aPam.GetNode().IsProtect() && 3062 aPam.Move( fnMoveForward, GoInContent ) ) 3063 ; // nothing to do in the loop; the aPam.Move does the moving! 3064 3065 // didn't work? then go backwards! 3066 if( aPam.GetNode().IsProtect() ) 3067 { 3068 SwPaM aTmpPaM( rNdIdx.GetNode(), 0 ); 3069 aPam = aTmpPaM; 3070 while( aPam.GetNode().IsProtect() && 3071 aPam.Move( fnMoveBackward, GoInContent ) ) 3072 ; // nothing to do in the loop; the aPam.Move does the moving! 3073 } 3074 3075 // if we're successful, set the new position 3076 if( ! aPam.GetNode().IsProtect() ) 3077 { 3078 *m_pCurrentCursor->GetPoint() = *aPam.GetPoint(); 3079 } 3080 } 3081 3082 // in a protected frame 3083 const SwSectionNode* pSectNd = rNdIdx.GetNode().FindSectionNode(); 3084 if( pSectNd && ( pSectNd->GetSection().IsHiddenFlag() || 3085 ( !IsReadOnlyAvailable() && 3086 pSectNd->GetSection().IsProtectFlag() )) ) 3087 { 3088 bOk = false; 3089 bool bGoNextSection = true; 3090 for( int nLoopCnt = 0; !bOk && nLoopCnt < 2; ++nLoopCnt ) 3091 { 3092 bool bContinue; 3093 do { 3094 bContinue = false; 3095 for (;;) 3096 { 3097 if (bGoNextSection) 3098 pCNd = rNds.GoNextSection( &rNdIdx, 3099 true, !IsReadOnlyAvailable() ); 3100 else 3101 pCNd = SwNodes::GoPrevSection( &rNdIdx, 3102 true, !IsReadOnlyAvailable() ); 3103 if ( pCNd == nullptr) break; 3104 // moved inside a table -> check if it is protected 3105 if( pCNd->FindTableNode() ) 3106 { 3107 SwCallLink aTmp( *this ); 3108 SwCursorSaveState aSaveState( *m_pCurrentCursor ); 3109 aTmp.nNdTyp = SwNodeType::NONE; // don't do anything in DTOR 3110 if( !m_pCurrentCursor->IsInProtectTable( true ) ) 3111 { 3112 const SwSectionNode* pSNd = pCNd->FindSectionNode(); 3113 if( !pSNd || !pSNd->GetSection().IsHiddenFlag() 3114 || (!IsReadOnlyAvailable() && 3115 pSNd->GetSection().IsProtectFlag() )) 3116 { 3117 bOk = true; 3118 break; // found non-protected cell 3119 } 3120 continue; // continue search 3121 } 3122 } 3123 else 3124 { 3125 bOk = true; 3126 break; // found non-protected cell 3127 } 3128 } 3129 3130 if( bOk && rNdIdx.GetIndex() < rNds.GetEndOfExtras().GetIndex() ) 3131 { 3132 // also check for Fly - might be protected as well 3133 pFrame = pCNd->getLayoutFrame(GetLayout(), nullptr, nullptr); 3134 if (nullptr == pFrame || 3135 ( !IsReadOnlyAvailable() && pFrame->IsProtected() ) || 3136 ( bOnlyText && pCNd->IsNoTextNode() ) ) 3137 { 3138 // continue search 3139 bOk = false; 3140 bContinue = true; 3141 } 3142 } 3143 } while( bContinue ); 3144 3145 if( !bOk ) 3146 { 3147 if( !nLoopCnt ) 3148 bGoNextSection = false; 3149 rNdIdx = nNdIdx; 3150 } 3151 } 3152 } 3153 if( bOk ) 3154 { 3155 pCNd = rNdIdx.GetNode().GetContentNode(); 3156 const sal_Int32 nContent = rNdIdx.GetIndex() < nNdIdx ? pCNd->Len() : 0; 3157 m_pCurrentCursor->GetPoint()->nContent.Assign( pCNd, nContent ); 3158 } 3159 else 3160 { 3161 pCNd = rNdIdx.GetNode().GetContentNode(); 3162 // if cursor in hidden frame, always move it 3163 if (!pCNd || !pCNd->getLayoutFrame(GetLayout(), nullptr, nullptr)) 3164 { 3165 SwCursorMoveState aTmpState( MV_NONE ); 3166 aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable(); 3167 GetLayout()->GetCursorOfst( m_pCurrentCursor->GetPoint(), m_pCurrentCursor->GetPtPos(), 3168 &aTmpState ); 3169 } 3170 } 3171 return bOk; 3172 } 3173 3174 bool SwCursorShell::IsCursorReadonly() const 3175 { 3176 if ( GetViewOptions()->IsReadonly() || 3177 GetViewOptions()->IsFormView() /* Formula view */ ) 3178 { 3179 SwFrame *pFrame = GetCurrFrame( false ); 3180 const SwFlyFrame* pFly; 3181 const SwSection* pSection; 3182 3183 if( pFrame && pFrame->IsInFly() && 3184 (pFly = pFrame->FindFlyFrame())->GetFormat()->GetEditInReadonly().GetValue() && 3185 pFly->Lower() && 3186 !pFly->Lower()->IsNoTextFrame() && 3187 !GetDrawView()->GetMarkedObjectList().GetMarkCount() ) 3188 { 3189 return false; 3190 } 3191 // edit in readonly sections 3192 else if ( pFrame && pFrame->IsInSct() && 3193 nullptr != ( pSection = pFrame->FindSctFrame()->GetSection() ) && 3194 pSection->IsEditInReadonlyFlag() ) 3195 { 3196 return false; 3197 } 3198 else if ( !IsMultiSelection() && CursorInsideInputField() ) 3199 { 3200 return false; 3201 } 3202 3203 return true; 3204 } 3205 return false; 3206 } 3207 3208 /// is the cursor allowed to enter ReadOnly sections? 3209 void SwCursorShell::SetReadOnlyAvailable( bool bFlag ) 3210 { 3211 // *never* switch in GlobalDoc 3212 if( (!GetDoc()->GetDocShell() || 3213 dynamic_cast<const SwGlobalDocShell*>(GetDoc()->GetDocShell()) == nullptr ) && 3214 bFlag != m_bSetCursorInReadOnly ) 3215 { 3216 // If the flag is switched off then all selections need to be 3217 // invalidated. Otherwise we would trust that nothing protected is selected. 3218 if( !bFlag ) 3219 { 3220 ClearMark(); 3221 } 3222 m_bSetCursorInReadOnly = bFlag; 3223 UpdateCursor(); 3224 } 3225 } 3226 3227 bool SwCursorShell::HasReadonlySel() const 3228 { 3229 bool bRet = false; 3230 // If protected area is to be ignored, then selections are never read-only. 3231 if ((IsReadOnlyAvailable() || GetViewOptions()->IsFormView() || 3232 GetDoc()->GetDocumentSettingManager().get( DocumentSettingId::PROTECT_FORM )) && 3233 !SwViewOption::IsIgnoreProtectedArea()) 3234 { 3235 if ( m_pTableCursor != nullptr ) 3236 { 3237 bRet = m_pTableCursor->HasReadOnlyBoxSel() 3238 || m_pTableCursor->HasReadonlySel( GetViewOptions()->IsFormView() ); 3239 } 3240 else 3241 { 3242 for(const SwPaM& rCursor : m_pCurrentCursor->GetRingContainer()) 3243 { 3244 if( rCursor.HasReadonlySel( GetViewOptions()->IsFormView() ) ) 3245 { 3246 bRet = true; 3247 break; 3248 } 3249 } 3250 } 3251 } 3252 return bRet; 3253 } 3254 3255 bool SwCursorShell::IsSelFullPara() const 3256 { 3257 bool bRet = false; 3258 3259 if( m_pCurrentCursor->GetPoint()->nNode.GetIndex() == 3260 m_pCurrentCursor->GetMark()->nNode.GetIndex() && !m_pCurrentCursor->IsMultiSelection() ) 3261 { 3262 sal_Int32 nStt = m_pCurrentCursor->GetPoint()->nContent.GetIndex(); 3263 sal_Int32 nEnd = m_pCurrentCursor->GetMark()->nContent.GetIndex(); 3264 if( nStt > nEnd ) 3265 { 3266 sal_Int32 nTmp = nStt; 3267 nStt = nEnd; 3268 nEnd = nTmp; 3269 } 3270 const SwContentNode* pCNd = m_pCurrentCursor->GetContentNode(); 3271 bRet = pCNd && !nStt && nEnd == pCNd->Len(); 3272 } 3273 return bRet; 3274 } 3275 3276 SvxFrameDirection SwCursorShell::GetTextDirection( const Point* pPt ) const 3277 { 3278 SwPosition aPos( *m_pCurrentCursor->GetPoint() ); 3279 Point aPt( pPt ? *pPt : m_pCurrentCursor->GetPtPos() ); 3280 if( pPt ) 3281 { 3282 SwCursorMoveState aTmpState( MV_NONE ); 3283 aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable(); 3284 3285 GetLayout()->GetCursorOfst( &aPos, aPt, &aTmpState ); 3286 } 3287 3288 return mxDoc->GetTextDirection( aPos, &aPt ); 3289 } 3290 3291 bool SwCursorShell::IsInVerticalText( const Point* pPt ) const 3292 { 3293 const SvxFrameDirection nDir = GetTextDirection( pPt ); 3294 return SvxFrameDirection::Vertical_RL_TB == nDir || SvxFrameDirection::Vertical_LR_TB == nDir 3295 || nDir == SvxFrameDirection::Vertical_LR_BT; 3296 } 3297 3298 bool SwCursorShell::IsInRightToLeftText() const 3299 { 3300 const SvxFrameDirection nDir = GetTextDirection(); 3301 // GetTextDirection uses SvxFrameDirection::Vertical_LR_TB to indicate RTL in 3302 // vertical environment 3303 return SvxFrameDirection::Vertical_LR_TB == nDir || SvxFrameDirection::Horizontal_RL_TB == nDir; 3304 } 3305 3306 /// If the current cursor position is inside a hidden range, the hidden range 3307 /// is selected. 3308 bool SwCursorShell::SelectHiddenRange() 3309 { 3310 bool bRet = false; 3311 if ( !GetViewOptions()->IsShowHiddenChar() && !m_pCurrentCursor->HasMark() ) 3312 { 3313 SwPosition& rPt = *m_pCurrentCursor->GetPoint(); 3314 const SwTextNode* pNode = rPt.nNode.GetNode().GetTextNode(); 3315 if ( pNode ) 3316 { 3317 const sal_Int32 nPos = rPt.nContent.GetIndex(); 3318 3319 // check if nPos is in hidden range 3320 sal_Int32 nHiddenStart; 3321 sal_Int32 nHiddenEnd; 3322 SwScriptInfo::GetBoundsOfHiddenRange( *pNode, nPos, nHiddenStart, nHiddenEnd ); 3323 if ( COMPLETE_STRING != nHiddenStart ) 3324 { 3325 // make selection: 3326 m_pCurrentCursor->SetMark(); 3327 m_pCurrentCursor->GetMark()->nContent = nHiddenEnd; 3328 bRet = true; 3329 } 3330 } 3331 } 3332 3333 return bRet; 3334 } 3335 3336 sal_uLong SwCursorShell::Find_Text( const i18nutil::SearchOptions2& rSearchOpt, 3337 bool bSearchInNotes, 3338 SwDocPositions eStart, SwDocPositions eEnd, 3339 bool& bCancel, 3340 FindRanges eRng, 3341 bool bReplace ) 3342 { 3343 if( m_pTableCursor ) 3344 GetCursor(); 3345 delete m_pTableCursor; 3346 m_pTableCursor = nullptr; 3347 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 3348 sal_uLong nRet = m_pCurrentCursor->Find_Text(rSearchOpt, bSearchInNotes, eStart, eEnd, 3349 bCancel, eRng, bReplace, GetLayout()); 3350 if( nRet || bCancel ) 3351 UpdateCursor(); 3352 return nRet; 3353 } 3354 3355 sal_uLong SwCursorShell::FindFormat( const SwTextFormatColl& rFormatColl, 3356 SwDocPositions eStart, SwDocPositions eEnd, 3357 bool& bCancel, 3358 FindRanges eRng, 3359 const SwTextFormatColl* pReplFormat ) 3360 { 3361 if( m_pTableCursor ) 3362 GetCursor(); 3363 delete m_pTableCursor; 3364 m_pTableCursor = nullptr; 3365 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 3366 sal_uLong nRet = m_pCurrentCursor->FindFormat(rFormatColl, eStart, eEnd, bCancel, eRng, 3367 pReplFormat ); 3368 if( nRet ) 3369 UpdateCursor(); 3370 return nRet; 3371 } 3372 3373 sal_uLong SwCursorShell::FindAttrs( const SfxItemSet& rSet, 3374 bool bNoCollections, 3375 SwDocPositions eStart, SwDocPositions eEnd, 3376 bool& bCancel, 3377 FindRanges eRng, 3378 const i18nutil::SearchOptions2* pSearchOpt, 3379 const SfxItemSet* rReplSet ) 3380 { 3381 if( m_pTableCursor ) 3382 GetCursor(); 3383 delete m_pTableCursor; 3384 m_pTableCursor = nullptr; 3385 SwCallLink aLk( *this ); // watch Cursor-Moves; call Link if needed 3386 sal_uLong nRet = m_pCurrentCursor->FindAttrs(rSet, bNoCollections, eStart, eEnd, 3387 bCancel, eRng, pSearchOpt, rReplSet, GetLayout()); 3388 if( nRet ) 3389 UpdateCursor(); 3390 return nRet; 3391 } 3392 3393 void SwCursorShell::SetSelection( const SwPaM& rCursor ) 3394 { 3395 StartAction(); 3396 SwPaM* pCursor = GetCursor(); 3397 *pCursor->GetPoint() = *rCursor.GetPoint(); 3398 if(rCursor.HasMark()) 3399 { 3400 pCursor->SetMark(); 3401 *pCursor->GetMark() = *rCursor.GetMark(); 3402 } 3403 if(rCursor.GetNext() != &rCursor) 3404 { 3405 const SwPaM *_pStartCursor = rCursor.GetNext(); 3406 do 3407 { 3408 SwPaM* pCurrentCursor = CreateCursor(); 3409 *pCurrentCursor->GetPoint() = *_pStartCursor->GetPoint(); 3410 if(_pStartCursor->HasMark()) 3411 { 3412 pCurrentCursor->SetMark(); 3413 *pCurrentCursor->GetMark() = *_pStartCursor->GetMark(); 3414 } 3415 } while( (_pStartCursor = _pStartCursor->GetNext()) != &rCursor ); 3416 } 3417 EndAction(); 3418 } 3419 3420 static const SwStartNode* lcl_NodeContext( const SwNode& rNode ) 3421 { 3422 const SwStartNode *pRet = rNode.StartOfSectionNode(); 3423 while( pRet->IsSectionNode() || pRet->IsTableNode() || 3424 pRet->GetStartNodeType() == SwTableBoxStartNode ) 3425 { 3426 pRet = pRet->StartOfSectionNode(); 3427 } 3428 return pRet; 3429 } 3430 3431 /** 3432 Checks if a position is valid. To be valid the position's node must 3433 be a content node and the content must not be unregistered. 3434 3435 @param aPos the position to check. 3436 */ 3437 bool sw_PosOk(const SwPosition & aPos) 3438 { 3439 return nullptr != aPos.nNode.GetNode().GetContentNode() && 3440 aPos.nContent.GetIdxReg(); 3441 } 3442 3443 /** 3444 Checks if a PaM is valid. For a PaM to be valid its point must be 3445 valid. Additionally if the PaM has a mark this has to be valid, too. 3446 3447 @param aPam the PaM to check 3448 */ 3449 static bool lcl_CursorOk(SwPaM & aPam) 3450 { 3451 return sw_PosOk(*aPam.GetPoint()) && (! aPam.HasMark() 3452 || sw_PosOk(*aPam.GetMark())); 3453 } 3454 3455 void SwCursorShell::ClearUpCursors() 3456 { 3457 // start of the ring 3458 SwPaM * pStartCursor = GetCursor(); 3459 // start loop with second entry of the ring 3460 SwPaM * pCursor = pStartCursor->GetNext(); 3461 SwPaM * pTmpCursor; 3462 bool bChanged = false; 3463 3464 // For all entries in the ring except the start entry delete the entry if 3465 // it is invalid. 3466 while (pCursor != pStartCursor) 3467 { 3468 pTmpCursor = pCursor->GetNext(); 3469 if ( ! lcl_CursorOk(*pCursor)) 3470 { 3471 delete pCursor; 3472 bChanged = true; 3473 } 3474 pCursor = pTmpCursor; 3475 } 3476 3477 if( pStartCursor->HasMark() && !sw_PosOk( *pStartCursor->GetMark() ) ) 3478 { 3479 pStartCursor->DeleteMark(); 3480 bChanged = true; 3481 } 3482 if( !sw_PosOk( *pStartCursor->GetPoint() ) ) 3483 { 3484 SwNodes & aNodes = GetDoc()->GetNodes(); 3485 const SwNode* pStart = lcl_NodeContext( pStartCursor->GetPoint()->nNode.GetNode() ); 3486 SwNodeIndex aIdx( pStartCursor->GetPoint()->nNode ); 3487 SwNode * pNode = SwNodes::GoPrevious(&aIdx); 3488 if( pNode == nullptr || lcl_NodeContext( *pNode ) != pStart ) 3489 aNodes.GoNext( &aIdx ); 3490 if( pNode == nullptr || lcl_NodeContext( *pNode ) != pStart ) 3491 { 3492 // If the start entry of the ring is invalid replace it with a 3493 // cursor pointing to the beginning of the first content node in the 3494 // document. 3495 aIdx = *(aNodes.GetEndOfContent().StartOfSectionNode()); 3496 pNode = aNodes.GoNext( &aIdx ); 3497 } 3498 bool bFound = (pNode != nullptr); 3499 3500 assert(bFound); 3501 3502 if (bFound) 3503 { 3504 SwPaM aTmpPam(*pNode); 3505 *pStartCursor = aTmpPam; 3506 } 3507 3508 bChanged = true; 3509 } 3510 3511 // If at least one of the cursors in the ring have been deleted or replaced, 3512 // remove the table cursor. 3513 if (m_pTableCursor != nullptr && bChanged) 3514 TableCursorToCursor(); 3515 } 3516 3517 OUString SwCursorShell::GetCursorDescr() const 3518 { 3519 OUString aResult; 3520 3521 if (IsMultiSelection()) 3522 aResult += SwResId(STR_MULTISEL); 3523 else 3524 aResult = SwDoc::GetPaMDescr(*GetCursor()); 3525 3526 return aResult; 3527 } 3528 3529 void SwCursorShell::dumpAsXml(xmlTextWriterPtr pWriter) const 3530 { 3531 xmlTextWriterStartElement(pWriter, BAD_CAST("SwCursorShell")); 3532 3533 SwViewShell::dumpAsXml(pWriter); 3534 3535 xmlTextWriterStartElement(pWriter, BAD_CAST("m_pCurrentCursor")); 3536 for (SwPaM& rPaM : m_pCurrentCursor->GetRingContainer()) 3537 rPaM.dumpAsXml(pWriter); 3538 xmlTextWriterEndElement(pWriter); 3539 3540 xmlTextWriterEndElement(pWriter); 3541 } 3542 3543 static void lcl_FillRecognizerData( std::vector< OUString >& rSmartTagTypes, 3544 uno::Sequence< uno::Reference< container::XStringKeyMap > >& rStringKeyMaps, 3545 const SwWrongList& rSmartTagList, sal_Int32 nCurrent ) 3546 { 3547 // Insert smart tag information 3548 std::vector< uno::Reference< container::XStringKeyMap > > aStringKeyMaps; 3549 3550 for ( sal_uInt16 i = 0; i < rSmartTagList.Count(); ++i ) 3551 { 3552 const sal_Int32 nSTPos = rSmartTagList.Pos( i ); 3553 const sal_Int32 nSTLen = rSmartTagList.Len( i ); 3554 3555 if ( nSTPos <= nCurrent && nCurrent < nSTPos + nSTLen ) 3556 { 3557 const SwWrongArea* pArea = rSmartTagList.GetElement( i ); 3558 if ( pArea ) 3559 { 3560 rSmartTagTypes.push_back( pArea->maType ); 3561 } 3562 } 3563 } 3564 3565 if ( !rSmartTagTypes.empty() ) 3566 { 3567 rStringKeyMaps = comphelper::containerToSequence(aStringKeyMaps); 3568 } 3569 } 3570 3571 static void lcl_FillTextRange( uno::Reference<text::XTextRange>& rRange, 3572 SwTextNode& rNode, sal_Int32 nBegin, sal_Int32 nLen ) 3573 { 3574 // create SwPosition for nStartIndex 3575 SwIndex aIndex( &rNode, nBegin ); 3576 SwPosition aStartPos( rNode, aIndex ); 3577 3578 // create SwPosition for nEndIndex 3579 SwPosition aEndPos( aStartPos ); 3580 aEndPos.nContent = nBegin + nLen; 3581 3582 const uno::Reference<text::XTextRange> xRange = 3583 SwXTextRange::CreateXTextRange(*rNode.GetDoc(), aStartPos, &aEndPos); 3584 3585 rRange = xRange; 3586 } 3587 3588 void SwCursorShell::GetSmartTagTerm( std::vector< OUString >& rSmartTagTypes, 3589 uno::Sequence< uno::Reference< container::XStringKeyMap > >& rStringKeyMaps, 3590 uno::Reference< text::XTextRange>& rRange ) const 3591 { 3592 if ( !SwSmartTagMgr::Get().IsSmartTagsEnabled() ) 3593 return; 3594 3595 SwPaM* pCursor = GetCursor(); 3596 SwPosition aPos( *pCursor->GetPoint() ); 3597 SwTextNode *pNode = aPos.nNode.GetNode().GetTextNode(); 3598 if ( pNode && !pNode->IsInProtectSect() ) 3599 { 3600 const SwWrongList *pSmartTagList = pNode->GetSmartTags(); 3601 if ( pSmartTagList ) 3602 { 3603 sal_Int32 nCurrent = aPos.nContent.GetIndex(); 3604 sal_Int32 nBegin = nCurrent; 3605 sal_Int32 nLen = 1; 3606 3607 if (pSmartTagList->InWrongWord(nBegin, nLen) && !pNode->IsSymbolAt(nBegin)) 3608 { 3609 const sal_uInt16 nIndex = pSmartTagList->GetWrongPos( nBegin ); 3610 const SwWrongList* pSubList = pSmartTagList->SubList( nIndex ); 3611 if ( pSubList ) 3612 { 3613 pSmartTagList = pSubList; 3614 nCurrent = 0; 3615 } 3616 3617 lcl_FillRecognizerData( rSmartTagTypes, rStringKeyMaps, *pSmartTagList, nCurrent ); 3618 lcl_FillTextRange( rRange, *pNode, nBegin, nLen ); 3619 } 3620 } 3621 } 3622 } 3623 3624 // see also SwEditShell::GetCorrection( const Point* pPt, SwRect& rSelectRect ) 3625 void SwCursorShell::GetSmartTagRect( const Point& rPt, SwRect& rSelectRect ) 3626 { 3627 SwPaM* pCursor = GetCursor(); 3628 SwPosition aPos( *pCursor->GetPoint() ); 3629 Point aPt( rPt ); 3630 SwCursorMoveState eTmpState( MV_SETONLYTEXT ); 3631 SwSpecialPos aSpecialPos; 3632 eTmpState.m_pSpecialPos = &aSpecialPos; 3633 SwTextNode *pNode; 3634 const SwWrongList *pSmartTagList; 3635 3636 if( GetLayout()->GetCursorOfst( &aPos, aPt, &eTmpState ) && 3637 nullptr != (pNode = aPos.nNode.GetNode().GetTextNode()) && 3638 nullptr != (pSmartTagList = pNode->GetSmartTags()) && 3639 !pNode->IsInProtectSect() ) 3640 { 3641 sal_Int32 nBegin = aPos.nContent.GetIndex(); 3642 sal_Int32 nLen = 1; 3643 3644 if (pSmartTagList->InWrongWord(nBegin, nLen) && !pNode->IsSymbolAt(nBegin)) 3645 { 3646 // get smarttag word 3647 OUString aText( pNode->GetText().copy(nBegin, nLen) ); 3648 3649 //save the start and end positions of the line and the starting point 3650 Push(); 3651 LeftMargin(); 3652 const sal_Int32 nLineStart = GetCursor()->GetPoint()->nContent.GetIndex(); 3653 RightMargin(); 3654 const sal_Int32 nLineEnd = GetCursor()->GetPoint()->nContent.GetIndex(); 3655 Pop(PopMode::DeleteCurrent); 3656 3657 // make sure the selection build later from the data below does not 3658 // include "in word" character to the left and right in order to 3659 // preserve those. Therefore count those "in words" in order to 3660 // modify the selection accordingly. 3661 const sal_Unicode* pChar = aText.getStr(); 3662 sal_Int32 nLeft = 0; 3663 while (*pChar++ == CH_TXTATR_INWORD) 3664 ++nLeft; 3665 pChar = aText.getLength() ? aText.getStr() + aText.getLength() - 1 : nullptr; 3666 sal_Int32 nRight = 0; 3667 while (pChar && *pChar-- == CH_TXTATR_INWORD) 3668 ++nRight; 3669 3670 aPos.nContent = nBegin + nLeft; 3671 pCursor = GetCursor(); 3672 *pCursor->GetPoint() = aPos; 3673 pCursor->SetMark(); 3674 ExtendSelection( true, nLen - nLeft - nRight ); 3675 // do not determine the rectangle in the current line 3676 const sal_Int32 nWordStart = (nBegin + nLeft) < nLineStart ? nLineStart : nBegin + nLeft; 3677 // take one less than the line end - otherwise the next line would 3678 // be calculated 3679 const sal_Int32 nWordEnd = std::min(nBegin + nLen - nLeft - nRight, nLineEnd); 3680 Push(); 3681 pCursor->DeleteMark(); 3682 SwIndex& rContent = GetCursor()->GetPoint()->nContent; 3683 rContent = nWordStart; 3684 SwRect aStartRect; 3685 SwCursorMoveState aState; 3686 aState.m_bRealWidth = true; 3687 SwContentNode* pContentNode = pCursor->GetContentNode(); 3688 std::pair<Point, bool> const tmp(rPt, false); 3689 SwContentFrame *pContentFrame = pContentNode->getLayoutFrame( 3690 GetLayout(), pCursor->GetPoint(), &tmp); 3691 3692 pContentFrame->GetCharRect( aStartRect, *pCursor->GetPoint(), &aState ); 3693 rContent = nWordEnd - 1; 3694 SwRect aEndRect; 3695 pContentFrame->GetCharRect( aEndRect, *pCursor->GetPoint(),&aState ); 3696 rSelectRect = aStartRect.Union( aEndRect ); 3697 Pop(PopMode::DeleteCurrent); 3698 } 3699 } 3700 } 3701 3702 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 3703
