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