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