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