xref: /core/sw/source/core/crsr/crsrsh.cxx (revision aebf1925)
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