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