1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <config_wasm_strip.h>
22 #include <com/sun/star/text/XTextRange.hpp>
24 #include <hintids.hxx>
25 #include <svx/srchdlg.hxx>
26 #include <sfx2/viewsh.hxx>
27 #include <SwSmartTagMgr.hxx>
29 #include <rootfrm.hxx>
30 #include <pagefrm.hxx>
32 #include <viewimp.hxx>
34 #include <swselectionlist.hxx>
35 #include "BlockCursor.hxx"
39 #include <viewopt.hxx>
43 #include <sectfrm.hxx>
44 #include <swtable.hxx>
47 #include <section.hxx>
49 #include <scriptinfo.hxx>
50 #include <globdoc.hxx>
53 #include <fmteiro.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>
79 #include <tools/json_writer.hxx>
80 #include <redline.hxx>
82 using namespace com::sun::star
;
86 * Check if pCurrentCursor points into already existing ranges and delete those.
87 * @param Pointer to SwCursor object
89 static void CheckRange( SwCursor
* pCurrentCursor
)
91 auto [pStt
, pEnd
] = pCurrentCursor
->StartEnd(); // SwPosition*
93 SwPaM
*pTmpDel
= nullptr,
94 *pTmp
= pCurrentCursor
->GetNext();
96 // Search the complete ring
97 while( pTmp
!= pCurrentCursor
)
99 auto [pTmpStt
, pTmpEnd
] = pTmp
->StartEnd(); // SwPosition*
100 if( *pStt
<= *pTmpStt
)
102 if( *pEnd
> *pTmpStt
||
103 ( *pEnd
== *pTmpStt
&& *pEnd
== *pTmpEnd
))
107 if( *pStt
< *pTmpEnd
)
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
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.
124 SwPaM
* SwCursorShell::CreateCursor()
126 // don't create new Cursor with active table Selection
127 assert(!IsTableMode());
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
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
);
137 // Hide PaM logically, to avoid undoing the inverting from
138 // copied PaM (#i75172#)
139 pNew
->swapContent(*m_pCurrentCursor
);
141 m_pCurrentCursor
->DeleteMark();
143 UpdateCursor( SwCursorShell::SCROLLWIN
);
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.
152 void SwCursorShell::DestroyCursor()
154 // don't delete Cursor with active table Selection
155 assert(!IsTableMode());
157 // Is there a next one? Don't do anything if not.
158 if(!m_pCurrentCursor
->IsMultiSelection())
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
);
169 * Create and return a new shell cursor.
170 * Simply returns the current shell cursor if there is no selection
173 SwCursor
& SwCursorShell::CreateNewShellCursor()
177 (void) CreateCursor(); // n.b. returns old cursor
183 * Return the current shell cursor
184 * @return - returns current `SwPaM` shell cursor
186 SwCursor
& SwCursorShell::GetCurrentShellCursor()
192 * Return pointer to the current shell cursor
193 * @return - returns pointer to current `SwCursor` shell cursor
195 SwCursor
* SwCursorShell::GetCursor( bool bMakeTableCursor
) const
199 if( bMakeTableCursor
&& m_pTableCursor
->IsCursorMovedUpdate() )
201 //don't re-create 'parked' cursors
202 if( m_pTableCursor
->GetPoint()->GetNodeIndex() &&
203 m_pTableCursor
->GetMark()->GetNodeIndex() )
205 const SwContentNode
* pCNd
= m_pTableCursor
->GetPointContentNode();
206 if( pCNd
&& pCNd
->getLayoutFrame( GetLayout() ) )
208 pCNd
= m_pTableCursor
->GetMarkContentNode();
209 if( pCNd
&& pCNd
->getLayoutFrame( GetLayout() ) )
211 SwShellTableCursor
* pTC
= m_pTableCursor
;
212 GetLayout()->MakeTableCursors( *pTC
);
218 if( m_pTableCursor
->IsChgd() )
220 const_cast<SwCursorShell
*>(this)->m_pCurrentCursor
=
221 dynamic_cast<SwShellCursor
*>(m_pTableCursor
->MakeBoxSels( m_pCurrentCursor
));
224 return m_pCurrentCursor
;
227 void SwCursorShell::StartAction()
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 );
241 SwViewShell::StartAction(); // to the SwViewShell
244 void SwCursorShell::EndAction( const bool bIdleEnd
)
246 comphelper::FlagRestorationGuard
g(mbSelectAll
, StartsWith_() != StartsWith::None
&& ExtendedSelectedAll());
247 bool bVis
= m_bSVCursorVis
;
250 if( bIdleEnd
&& Imp()->HasPaintRegion() )
252 m_pCurrentCursor
->Hide();
255 // Update all invalid numberings before the last action
256 if( 1 == mnStartAction
)
257 GetDoc()->UpdateNumRule();
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;
264 SwViewShell::EndAction( bIdleEnd
); // have SwViewShell go first
266 m_bSVCursorVis
= bSavSVCursorVis
;
270 if( bVis
) // display SV-Cursor again
271 m_pVisibleCursor
->Show();
276 sal_uInt16 eFlags
= SwCursorShell::CHKRANGE
;
278 eFlags
|= SwCursorShell::SCROLLWIN
;
280 UpdateCursor( eFlags
, bIdleEnd
); // Show Cursor changes
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
;
289 if( !m_nCursorMove
||
290 ( 1 == m_nCursorMove
&& m_bInCMvVisportChgd
) )
291 // display Cursor & Selections again
292 ShowCursors( m_bSVCursorVis
);
294 // call ChgCall if there is still one
295 if( m_bCallChgLnk
&& m_bChgCallFlag
&& m_aChgLnk
.IsSet() )
297 m_aChgLnk
.Call(nullptr);
298 m_bChgCallFlag
= false; // reset flag
302 void SwCursorShell::SttCursorMove()
305 OSL_ENSURE( m_nCursorMove
< USHRT_MAX
, "Too many nested CursorMoves." );
311 void SwCursorShell::EndCursorMove( const bool bIdleEnd
)
314 OSL_ENSURE( m_nCursorMove
, "EndCursorMove() without SttCursorMove()." );
316 EndAction( bIdleEnd
);
320 m_bInCMvVisportChgd
= false;
324 bool SwCursorShell::LeftRight( bool bLeft
, sal_uInt16 nCnt
, SwCursorSkipMode nMode
,
325 bool bVisualAllowed
)
328 return bLeft
? GoPrevCell() : GoNextCell();
330 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
333 // #i27615# Handle cursor in front of label.
334 const SwTextNode
* pTextNd
= nullptr;
337 m_pBlockCursor
->clearPoints();
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() )
344 SetInFrontOfLabel( false );
347 // 2. CASE: Cursor is at beginning of numbered paragraph. A move
348 // to the left will simply set the bInFrontOfLabel flag:
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())
359 SetInFrontOfLabel( true );
362 // 3. CASE: Regular cursor move. Reset the bInFrontOfLabel flag:
365 const bool bSkipHidden
= !GetViewOptions()->IsShowHiddenChar();
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(),
373 GetViewOptions()->IsFieldName());
374 if ( !bRet
&& bLeft
&& bResetOfInFrontOfLabel
)
376 // undo reset of <bInFrontOfLabel> flag
377 SetInFrontOfLabel( true );
389 void SwCursorShell::MarkListLevel( const OUString
& sListId
,
390 const int nListLevel
)
392 if (sListId
== m_sMarkedListId
&& nListLevel
== m_nMarkedListLevel
)
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
399 const bool bVisuallyMarked(GetViewOptions()->IsFieldShadings());
402 if ( !m_sMarkedListId
.isEmpty() )
403 mxDoc
->MarkListLevel( m_sMarkedListId
, m_nMarkedListLevel
, false );
405 if ( !sListId
.isEmpty() )
406 mxDoc
->MarkListLevel( sListId
, nListLevel
, true );
409 m_sMarkedListId
= sListId
;
410 m_nMarkedListLevel
= nListLevel
;
413 void SwCursorShell::UpdateMarkedListLevel()
415 SwTextNode
const*const pTextNd
= sw::GetParaPropsNode(*GetLayout(),
416 GetCursor_()->GetPoint()->GetNode());
421 if (!pTextNd
->IsNumbered(GetLayout()))
423 m_pCurrentCursor
->SetInFrontOfLabel_( false );
424 MarkListLevel( OUString(), 0 );
426 else if ( m_pCurrentCursor
->IsInFrontOfLabel() )
428 if ( pTextNd
->IsInList() )
430 assert(pTextNd
->GetActualListLevel() >= 0 &&
431 pTextNd
->GetActualListLevel() < MAXLEVEL
);
432 MarkListLevel( pTextNd
->GetListId(),
433 pTextNd
->GetActualListLevel() );
438 MarkListLevel( OUString(), 0 );
442 void SwCursorShell::FirePageChangeEvent(sal_uInt16 nOldPage
, sal_uInt16 nNewPage
)
444 #ifdef ACCESSIBLE_LAYOUT
445 if( Imp()->IsAccessible() )
446 Imp()->FirePageChangeEvent( nOldPage
, nNewPage
);
453 void SwCursorShell::FireColumnChangeEvent(sal_uInt16 nOldColumn
, sal_uInt16 nNewColumn
)
455 #ifdef ACCESSIBLE_LAYOUT
456 if( Imp()->IsAccessible() )
457 Imp()->FireColumnChangeEvent( nOldColumn
, nNewColumn
);
464 void SwCursorShell::FireSectionChangeEvent(sal_uInt16 nOldSection
, sal_uInt16 nNewSection
)
466 #ifdef ACCESSIBLE_LAYOUT
467 if( Imp()->IsAccessible() )
468 Imp()->FireSectionChangeEvent( nOldSection
, nNewSection
);
475 bool SwCursorShell::bColumnChange()
477 SwFrame
* pCurrFrame
= GetCurrFrame(false);
479 if (pCurrFrame
== nullptr)
484 SwFrame
* pCurrCol
=pCurrFrame
->FindColFrame();
486 while(pCurrCol
== nullptr && pCurrFrame
!=nullptr )
488 SwLayoutFrame
* pParent
= pCurrFrame
->GetUpper();
491 pCurrCol
=static_cast<SwFrame
*>(pParent
)->FindColFrame();
492 pCurrFrame
= pParent
;
500 if(m_oldColFrame
== pCurrCol
)
504 m_oldColFrame
= pCurrCol
;
509 bool SwCursorShell::UpDown( bool bUp
, sal_uInt16 nCnt
)
511 CurrShell
aCurr( this );
512 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
514 bool bTableMode
= IsTableMode();
515 SwShellCursor
* pTmpCursor
= getShellCursor( true );
517 bool bRet
= pTmpCursor
->UpDown( bUp
, nCnt
);
518 // #i40019# UpDown should always reset the bInFrontOfLabel flag:
519 bRet
|= SetInFrontOfLabel(false);
522 m_pBlockCursor
->clearPoints();
526 m_eMvState
= CursorMoveState::UpDown
; // status for Cursor travelling - GetModelPositionForViewPoint
529 CursorFlag eUpdateMode
= SwCursorShell::SCROLLWIN
;
531 eUpdateMode
= static_cast<CursorFlag
>(eUpdateMode
532 | SwCursorShell::UPDOWN
| SwCursorShell::CHKRANGE
);
533 UpdateCursor( o3tl::narrowing
<sal_uInt16
>(eUpdateMode
) );
539 bool SwCursorShell::LRMargin( bool bLeft
, bool bAPI
)
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
545 const bool bTableMode
= IsTableMode();
546 SwShellCursor
* pTmpCursor
= getShellCursor( true );
549 m_pBlockCursor
->clearPoints();
551 const bool bWasAtLM
= GetCursor_()->IsAtLeftRightMargin(*GetLayout(), true, bAPI
);
553 bool bRet
= pTmpCursor
->LeftRightMargin(*GetLayout(), bLeft
, bAPI
);
555 if ( bLeft
&& !bTableMode
&& bRet
&& bWasAtLM
&& !GetCursor_()->HasMark() )
557 const SwTextNode
* pTextNd
= GetCursor_()->GetPointNode().GetTextNode();
558 assert(sw::GetParaPropsNode(*GetLayout(), GetCursor_()->GetPoint()->GetNode()) == pTextNd
);
559 if ( pTextNd
&& pTextNd
->HasVisibleNumberingOrBullet() )
560 SetInFrontOfLabel( true );
564 bRet
= SetInFrontOfLabel( false ) || bRet
;
574 bool SwCursorShell::IsAtLRMargin( bool bLeft
, bool bAPI
) const
576 const SwShellCursor
* pTmpCursor
= getShellCursor( true );
577 return pTmpCursor
->IsAtLeftRightMargin(*GetLayout(), bLeft
, bAPI
);
580 bool SwCursorShell::SttEndDoc( bool bStt
)
582 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
584 SwShellCursor
* pTmpCursor
= m_pBlockCursor
? &m_pBlockCursor
->getShellCursor() : m_pCurrentCursor
;
585 bool bRet
= pTmpCursor
->SttEndDoc( bStt
);
589 pTmpCursor
->GetPtPos().setY( 0 ); // set to 0 explicitly (table header)
592 m_pBlockCursor
->clearPoints();
593 RefreshBlockCursor();
596 UpdateCursor(SwCursorShell::SCROLLWIN
|SwCursorShell::CHKRANGE
|SwCursorShell::READONLY
);
601 const SwTableNode
* SwCursorShell::IsCursorInTable() const
603 if (m_pTableCursor
&& m_pTableCursor
->GetSelectedBoxesCount())
604 { // find the table that has the selected boxes
605 return m_pTableCursor
->GetSelectedBoxes()[0]->GetSttNd()->FindTableNode();
607 return m_pCurrentCursor
->GetPointNode().FindTableNode();
610 // fun cases to consider:
612 // - into para => SA/ESA
613 // - into prev/next table => continue...
614 // - no prev/next => done
616 // - into containing cell => SA/ESA
617 // - into prev/next of containing cell
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
628 bool SwCursorShell::MoveOutOfTable()
630 SwPosition
const point(*getShellCursor(false)->GetPoint());
631 SwPosition
const mark(*getShellCursor(false)->GetMark());
633 for (auto const fnMove
: {&fnMoveBackward
, &fnMoveForward
})
636 SwCursor
*const pCursor(getShellCursor(false));
638 pCursor
->Normalize(fnMove
== &fnMoveBackward
);
639 pCursor
->DeleteMark();
640 SwTableNode
const*const pTable(pCursor
->GetPoint()->GetNode().FindTableNode());
642 while (MovePara(GoInContent
, *fnMove
))
644 SwStartNode
const*const pBox(pCursor
->GetPoint()->GetNode().FindTableBoxStartNode());
647 Pop(SwCursorShell::PopMode::DeleteStack
);
648 return true; // moved to paragraph at top-level of text
650 if (pBox
->GetIndex() < pTable
->GetIndex()
651 && pTable
->EndOfSectionIndex() < pBox
->EndOfSectionIndex())
653 Pop(SwCursorShell::PopMode::DeleteStack
);
654 return true; // pBox contains start position (pTable)
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
;
667 bool SwCursorShell::TrySelectOuterTable()
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());
678 // manually select boxes of pOuterTable
679 SwNodeIndex
firstCell(*pOuterTable
, +1);
680 SwNodeIndex
lastCell(*rNodes
[pOuterTable
->EndOfSectionIndex()-1]->StartOfSectionNode());
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
= rNodes
.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
696 // this will update m_pCurrentCursor based on m_pTableCursor
697 UpdateCursor(SwCursorShell::SCROLLWIN
|SwCursorShell::CHKRANGE
|SwCursorShell::READONLY
);
702 /// find XText start node
703 static SwStartNode
const* FindTextStart(SwPosition
const& rPos
)
705 SwStartNode
const* pStartNode(rPos
.GetNode().StartOfSectionNode());
706 while (pStartNode
&& (pStartNode
->IsSectionNode() || pStartNode
->IsTableNode()))
708 pStartNode
= pStartNode
->StartOfSectionNode();
713 static SwStartNode
const* FindParentText(SwShellCursor
const& rCursor
)
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())
720 pStartNode
= pStartNode
->StartOfSectionNode();
722 while (pStartNode
->GetIndex() < pEndNode
->StartOfSectionNode()->GetIndex())
724 pEndNode
= pEndNode
->StartOfSectionNode()->StartOfSectionNode()->EndOfSectionNode();
726 assert(pStartNode
->EndOfSectionNode() == pEndNode
);
728 return (pStartNode
->IsSectionNode() || pStartNode
->IsTableNode())
729 ? FindTextStart(SwPosition(*pStartNode
))
733 bool SwCursorShell::MoveStartText()
735 SwPosition
const old(*m_pCurrentCursor
->GetPoint());
736 SwStartNode
const*const pStartNode(FindParentText(*getShellCursor(false)));
738 SwTableNode
const*const pTable(pStartNode
->FindTableNode());
739 m_pCurrentCursor
->GetPoint()->Assign(*pStartNode
);
740 GetDoc()->GetNodes().GoNext(m_pCurrentCursor
->GetPoint());
741 while (m_pCurrentCursor
->GetPoint()->GetNode().FindTableNode() != pTable
742 && (!pTable
|| pTable
->GetIndex() < m_pCurrentCursor
->GetPoint()->GetNode().FindTableNode()->GetIndex())
743 && MoveOutOfTable());
744 UpdateCursor(SwCursorShell::SCROLLWIN
|SwCursorShell::CHKRANGE
|SwCursorShell::READONLY
);
745 return old
!= *m_pCurrentCursor
->GetPoint();
748 // select all inside the current XText, with table or hidden para at start/end
749 void SwCursorShell::ExtendedSelectAll(bool bFootnotes
)
751 // find common ancestor node of both ends of cursor
752 SwStartNode
const*const pStartNode(FindParentText(*getShellCursor(false)));
755 { // convert m_pTableCursor to m_pCurrentCursor after determining pStartNode
756 TableCursorToCursor();
758 SwNodes
& rNodes
= GetDoc()->GetNodes();
759 m_pCurrentCursor
->Normalize(true);
760 SwPosition
* pPos
= m_pCurrentCursor
->GetPoint();
761 pPos
->Assign(bFootnotes
? rNodes
.GetEndOfPostIts() : static_cast<SwNode
const&>(*pStartNode
));
762 rNodes
.GoNext( pPos
);
763 pPos
= m_pCurrentCursor
->GetMark();
764 pPos
->Assign(bFootnotes
? rNodes
.GetEndOfContent() : static_cast<SwNode
const&>(*pStartNode
->EndOfSectionNode()));
765 SwContentNode
* pCNd
= SwNodes::GoPrevious( pPos
);
767 pPos
->AssignEndIndex(*pCNd
);
770 static typename
SwCursorShell::StartsWith
StartsWith(SwStartNode
const& rStart
)
772 for (auto i
= rStart
.GetIndex() + 1; i
< rStart
.EndOfSectionIndex(); ++i
)
774 SwNode
const& rNode(*rStart
.GetNodes()[i
]);
775 switch (rNode
.GetNodeType())
777 case SwNodeType::Section
:
779 case SwNodeType::Table
:
780 return SwCursorShell::StartsWith::Table
;
781 case SwNodeType::Text
:
782 if (rNode
.GetTextNode()->IsHidden())
784 return SwCursorShell::StartsWith::HiddenPara
;
786 return SwCursorShell::StartsWith::None
;
788 return SwCursorShell::StartsWith::None
;
791 return SwCursorShell::StartsWith::None
;
794 static typename
SwCursorShell::StartsWith
EndsWith(SwStartNode
const& rStart
)
796 for (auto i
= rStart
.EndOfSectionIndex() - 1; rStart
.GetIndex() < i
; --i
)
798 SwNode
const& rNode(*rStart
.GetNodes()[i
]);
799 switch (rNode
.GetNodeType())
801 case SwNodeType::End
:
802 if (rNode
.StartOfSectionNode()->IsTableNode())
804 return SwCursorShell::StartsWith::Table
;
806 //TODO buggy SwUndoRedline in testTdf137503? assert(rNode.StartOfSectionNode()->IsSectionNode());
808 case SwNodeType::Text
:
809 if (rNode
.GetTextNode()->IsHidden())
811 return SwCursorShell::StartsWith::HiddenPara
;
813 return SwCursorShell::StartsWith::None
;
815 return SwCursorShell::StartsWith::None
;
818 return SwCursorShell::StartsWith::None
;
821 // return the node that is the start of the extended selection (to include table
822 // or section start nodes; looks like extending for end nodes is not required)
823 ::std::optional
<::std::pair
<SwNode
const*, ::std::vector
<SwTableNode
*>>>
824 SwCursorShell::ExtendedSelectedAll() const
831 SwNodes
& rNodes
= GetDoc()->GetNodes();
832 SwShellCursor
const*const pShellCursor
= getShellCursor(false);
833 SwStartNode
const* pStartNode(FindParentText(*pShellCursor
));
835 SwNodeIndex
nNode(*pStartNode
);
836 SwContentNode
* pStart
= rNodes
.GoNext(&nNode
);
842 nNode
= *pStartNode
->EndOfSectionNode();
843 SwContentNode
* pEnd
= SwNodes::GoPrevious(&nNode
);
849 SwPosition
aStart(*pStart
, 0);
850 SwPosition
aEnd(*pEnd
, pEnd
->Len());
851 if (!(aStart
== *pShellCursor
->Start() && aEnd
== *pShellCursor
->End()))
856 auto const ends(::EndsWith(*pStartNode
));
857 if (::StartsWith(*pStartNode
) == StartsWith::None
858 && ends
== StartsWith::None
)
860 return {}; // "ordinary" selection will work
863 ::std::vector
<SwTableNode
*> tablesAtEnd
;
864 if (ends
== StartsWith::Table
)
866 SwNode
* pLastNode(rNodes
[pStartNode
->EndOfSectionIndex() - 1]);
867 while (pLastNode
->IsEndNode())
869 SwNode
*const pNode(pLastNode
->StartOfSectionNode());
870 if (pNode
->IsTableNode())
872 tablesAtEnd
.push_back(pNode
->GetTableNode());
873 pLastNode
= rNodes
[pNode
->GetIndex() - 1];
875 else if (pNode
->IsSectionNode())
877 pLastNode
= rNodes
[pLastNode
->GetIndex() - 1];
880 assert(!tablesAtEnd
.empty());
883 // tdf#133990 ensure directly containing section is included in SwUndoDelete
884 while (pStartNode
->IsSectionNode()
885 && pStartNode
->GetIndex() == pStartNode
->StartOfSectionNode()->GetIndex() + 1
886 && pStartNode
->EndOfSectionNode()->GetIndex() + 1 == pStartNode
->StartOfSectionNode()->EndOfSectionNode()->GetIndex())
888 pStartNode
= pStartNode
->StartOfSectionNode();
891 // pStartNode is the node that fully contains the selection - the first
892 // node of the selection is the first node inside pStartNode
893 return ::std::make_pair(rNodes
[pStartNode
->GetIndex() + 1], tablesAtEnd
);
896 typename
SwCursorShell::StartsWith
SwCursorShell::StartsWith_()
898 SwShellCursor
const*const pShellCursor
= getShellCursor(false);
899 // first, check if this is invalid; ExtendedSelectAll(true) may result in
900 // a) an ordinary selection that is valid
901 // b) a selection that is extended
902 // c) a selection that is invalid and will cause FindParentText to loop
903 SwNode
const& rEndOfExtras(GetDoc()->GetNodes().GetEndOfExtras());
904 if (pShellCursor
->Start()->nNode
.GetIndex() <= rEndOfExtras
.GetIndex()
905 && rEndOfExtras
.GetIndex() < pShellCursor
->End()->nNode
.GetIndex())
907 return StartsWith::None
; // *very* extended, no ExtendedSelectedAll handling!
909 SwStartNode
const*const pStartNode(FindParentText(*pShellCursor
));
910 if (auto const ret
= ::StartsWith(*pStartNode
); ret
!= StartsWith::None
)
914 if (auto const ret
= ::EndsWith(*pStartNode
); ret
!= StartsWith::None
)
918 return StartsWith::None
;
921 bool SwCursorShell::MovePage( SwWhichPage fnWhichPage
, SwPosPage fnPosPage
)
925 // never jump of section borders at selection
926 if( !m_pCurrentCursor
->HasMark() || !m_pCurrentCursor
->IsNoContent() )
928 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
929 CurrShell
aCurr( this );
931 SwCursorSaveState
aSaveState( *m_pCurrentCursor
);
932 Point
& rPt
= m_pCurrentCursor
->GetPtPos();
933 std::pair
<Point
, bool> tmp(rPt
, false);
934 SwContentFrame
* pFrame
= m_pCurrentCursor
->GetPointContentNode()->
935 getLayoutFrame(GetLayout(), m_pCurrentCursor
->GetPoint(), &tmp
);
936 if( pFrame
&& GetFrameInPage( pFrame
, fnWhichPage
, fnPosPage
, m_pCurrentCursor
) &&
937 !m_pCurrentCursor
->IsSelOvr( SwCursorSelOverFlags::Toggle
|
938 SwCursorSelOverFlags::ChangePos
))
947 bool SwCursorShell::isInHiddenTextFrame(SwShellCursor
* pShellCursor
)
949 SwContentNode
*pCNode
= pShellCursor
->GetPointContentNode();
950 std::pair
<Point
, bool> tmp(pShellCursor
->GetPtPos(), false);
951 SwContentFrame
*const pFrame
= pCNode
952 ? pCNode
->getLayoutFrame(GetLayout(), pShellCursor
->GetPoint(), &tmp
)
954 return !pFrame
|| (pFrame
->IsTextFrame() && static_cast<SwTextFrame
*>(pFrame
)->IsHiddenNow());
957 // sw_redlinehide: this should work for all cases: GoCurrPara, GoNextPara, GoPrevPara
958 static bool IsAtStartOrEndOfFrame(SwCursorShell
const*const pShell
,
959 SwShellCursor
const*const pShellCursor
, SwMoveFnCollection
const& fnPosPara
)
961 SwContentNode
*const pCNode
= pShellCursor
->GetPointContentNode();
962 assert(pCNode
); // surely can't have moved otherwise?
963 std::pair
<Point
, bool> tmp(pShellCursor
->GetPtPos(), false);
964 SwContentFrame
const*const pFrame
= pCNode
->getLayoutFrame(
965 pShell
->GetLayout(), pShellCursor
->GetPoint(), &tmp
);
966 if (!pFrame
|| !pFrame
->IsTextFrame())
970 SwTextFrame
const& rTextFrame(static_cast<SwTextFrame
const&>(*pFrame
));
971 TextFrameIndex
const ix(rTextFrame
.MapModelToViewPos(*pShellCursor
->GetPoint()));
972 if (&fnParaStart
== &fnPosPara
)
974 return ix
== TextFrameIndex(0);
978 assert(&fnParaEnd
== &fnPosPara
);
979 return ix
== TextFrameIndex(rTextFrame
.GetText().getLength());
983 bool SwCursorShell::MovePara(SwWhichPara fnWhichPara
, SwMoveFnCollection
const & fnPosPara
)
985 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
986 SwShellCursor
* pTmpCursor
= getShellCursor( true );
987 bool bRet
= pTmpCursor
->MovePara( fnWhichPara
, fnPosPara
);
990 //keep going until we get something visible, i.e. skip
991 //over hidden paragraphs, don't get stuck at the start
992 //which is what SwCursorShell::UpdateCursorPos will reset
993 //the position to if we pass it a position in an
994 //invisible hidden paragraph field
995 while (isInHiddenTextFrame(pTmpCursor
)
996 || !IsAtStartOrEndOfFrame(this, pTmpCursor
, fnPosPara
))
998 if (!pTmpCursor
->MovePara(fnWhichPara
, fnPosPara
))
1007 bool SwCursorShell::MoveSection( SwWhichSection fnWhichSect
,
1008 SwMoveFnCollection
const & fnPosSect
)
1010 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
1011 SwCursor
* pTmpCursor
= getShellCursor( true );
1012 bool bRet
= pTmpCursor
->MoveSection( fnWhichSect
, fnPosSect
);
1021 static SwFrame
* lcl_IsInHeaderFooter( SwNode
& rNd
, Point
& rPt
)
1023 SwFrame
* pFrame
= nullptr;
1024 SwContentNode
* pCNd
= rNd
.GetContentNode();
1027 std::pair
<Point
, bool> tmp(rPt
, false);
1028 SwContentFrame
*pContentFrame
= pCNd
->getLayoutFrame(
1029 pCNd
->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
1031 pFrame
= pContentFrame
? pContentFrame
->GetUpper() : nullptr;
1032 while( pFrame
&& !pFrame
->IsHeaderFrame() && !pFrame
->IsFooterFrame() )
1033 pFrame
= pFrame
->IsFlyFrame() ? static_cast<SwFlyFrame
*>(pFrame
)->AnchorFrame()
1034 : pFrame
->GetUpper();
1039 bool SwCursorShell::IsInHeaderFooter( bool* pbInHeader
) const
1042 SwFrame
* pFrame
= ::lcl_IsInHeaderFooter( m_pCurrentCursor
->GetPoint()->GetNode(), aPt
);
1043 if( pFrame
&& pbInHeader
)
1044 *pbInHeader
= pFrame
->IsHeaderFrame();
1045 return nullptr != pFrame
;
1048 int SwCursorShell::SetCursor( const Point
&rLPt
, bool bOnlyText
, bool bBlock
)
1050 CurrShell
aCurr( this );
1052 SwShellCursor
* pCursor
= getShellCursor( bBlock
);
1053 SwPosition
aPos( *pCursor
->GetPoint() );
1055 Point
& rCurrentCursorPt
= pCursor
->GetPtPos();
1056 SwCursorMoveState
aTmpState( IsTableMode() ? CursorMoveState::TableSel
:
1057 bOnlyText
? CursorMoveState::SetOnlyText
: CursorMoveState::NONE
);
1058 aTmpState
.m_bSetInReadOnly
= IsReadOnlyAvailable();
1060 SwTextNode
const*const pTextNd
= sw::GetParaPropsNode(*GetLayout(), pCursor
->GetPoint()->GetNode());
1062 if ( pTextNd
&& !IsTableMode() &&
1063 // #i37515# No bInFrontOfLabel during selection
1064 !pCursor
->HasMark() &&
1065 pTextNd
->HasVisibleNumberingOrBullet() )
1067 aTmpState
.m_bInFrontOfLabel
= true; // #i27615#
1071 aTmpState
.m_bInFrontOfLabel
= false;
1074 int bRet
= CRSR_POSOLD
|
1075 ( GetLayout()->GetModelPositionForViewPoint( &aPos
, aPt
, &aTmpState
)
1076 ? 0 : CRSR_POSCHG
);
1078 const bool bOldInFrontOfLabel
= IsInFrontOfLabel();
1079 const bool bNewInFrontOfLabel
= aTmpState
.m_bInFrontOfLabel
;
1081 pCursor
->SetCursorBidiLevel( aTmpState
.m_nCursorBidiLevel
);
1083 if( CursorMoveState::RightMargin
== aTmpState
.m_eState
)
1084 m_eMvState
= CursorMoveState::RightMargin
;
1085 // is the new position in header or footer?
1086 SwFrame
* pFrame
= lcl_IsInHeaderFooter( aPos
.GetNode(), aPt
);
1087 if( IsTableMode() && !pFrame
&& aPos
.GetNode().StartOfSectionNode() ==
1088 pCursor
->GetPoint()->GetNode().StartOfSectionNode() )
1089 // same table column and not in header/footer -> back
1092 if( m_pBlockCursor
&& bBlock
)
1094 m_pBlockCursor
->setEndPoint( rLPt
);
1095 if( !pCursor
->HasMark() )
1096 m_pBlockCursor
->setStartPoint( rLPt
);
1097 else if( !m_pBlockCursor
->getStartPoint() )
1098 m_pBlockCursor
->setStartPoint( pCursor
->GetMkPos() );
1100 if( !pCursor
->HasMark() )
1102 // is at the same position and if in header/footer -> in the same
1103 if( aPos
== *pCursor
->GetPoint() &&
1104 bOldInFrontOfLabel
== bNewInFrontOfLabel
)
1108 if( pFrame
->getFrameArea().Contains( rCurrentCursorPt
))
1111 else if( aPos
.GetNode().IsContentNode() )
1113 // in the same frame?
1114 std::pair
<Point
, bool> tmp(m_aCharRect
.Pos(), false);
1115 SwFrame
* pOld
= static_cast<SwContentNode
&>(aPos
.GetNode()).getLayoutFrame(
1116 GetLayout(), nullptr, &tmp
);
1118 SwFrame
* pNew
= static_cast<SwContentNode
&>(aPos
.GetNode()).getLayoutFrame(
1119 GetLayout(), nullptr, &tmp
);
1127 // SSelection over not allowed sections or if in header/footer -> different
1128 if( !CheckNodesRange( aPos
.GetNode(), pCursor
->GetMark()->GetNode(), true )
1129 || ( pFrame
&& !pFrame
->getFrameArea().Contains( pCursor
->GetMkPos() ) ))
1132 // is at same position but not in header/footer
1133 if( aPos
== *pCursor
->GetPoint() )
1137 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
1138 SwCursorSaveState
aSaveState( *pCursor
);
1140 *pCursor
->GetPoint() = aPos
;
1141 rCurrentCursorPt
= aPt
;
1143 // #i41424# Only update the marked number levels if necessary
1144 // Force update of marked number levels if necessary.
1145 if ( bNewInFrontOfLabel
|| bOldInFrontOfLabel
)
1146 m_pCurrentCursor
->SetInFrontOfLabel_( !bNewInFrontOfLabel
);
1147 SetInFrontOfLabel( bNewInFrontOfLabel
);
1149 if( !pCursor
->IsSelOvr( SwCursorSelOverFlags::ChangePos
) )
1151 UpdateCursor( SwCursorShell::SCROLLWIN
| SwCursorShell::CHKRANGE
);
1152 bRet
&= ~CRSR_POSOLD
;
1154 else if( bOnlyText
&& !m_pCurrentCursor
->HasMark() )
1156 if( FindValidContentNode( bOnlyText
) )
1158 // position cursor in a valid content
1159 if( aPos
== *pCursor
->GetPoint() )
1164 bRet
&= ~CRSR_POSOLD
;
1169 // there is no valid content -> hide cursor
1170 m_pVisibleCursor
->Hide(); // always hide visible cursor
1171 m_eMvState
= CursorMoveState::NONE
; // status for Cursor travelling
1172 m_bAllProtect
= true;
1173 if( GetDoc()->GetDocShell() )
1175 GetDoc()->GetDocShell()->SetReadOnlyUI();
1176 CallChgLnk(); // notify UI
1183 void SwCursorShell::TableCursorToCursor()
1185 assert(m_pTableCursor
);
1186 delete m_pTableCursor
;
1187 m_pTableCursor
= nullptr;
1190 void SwCursorShell::BlockCursorToCursor()
1192 assert(m_pBlockCursor
);
1193 if( m_pBlockCursor
&& !HasSelection() )
1195 SwPaM
& rPam
= m_pBlockCursor
->getShellCursor();
1196 m_pCurrentCursor
->SetMark();
1197 *m_pCurrentCursor
->GetPoint() = *rPam
.GetPoint();
1198 if( rPam
.HasMark() )
1199 *m_pCurrentCursor
->GetMark() = *rPam
.GetMark();
1201 m_pCurrentCursor
->DeleteMark();
1203 delete m_pBlockCursor
;
1204 m_pBlockCursor
= nullptr;
1207 void SwCursorShell::CursorToBlockCursor()
1209 if( !m_pBlockCursor
)
1211 SwPosition
aPos( *m_pCurrentCursor
->GetPoint() );
1212 m_pBlockCursor
= new SwBlockCursor( *this, aPos
);
1213 SwShellCursor
&rBlock
= m_pBlockCursor
->getShellCursor();
1214 rBlock
.GetPtPos() = m_pCurrentCursor
->GetPtPos();
1215 if( m_pCurrentCursor
->HasMark() )
1218 *rBlock
.GetMark() = *m_pCurrentCursor
->GetMark();
1219 rBlock
.GetMkPos() = m_pCurrentCursor
->GetMkPos();
1222 m_pBlockCursor
->clearPoints();
1223 RefreshBlockCursor();
1226 void SwCursorShell::ClearMark()
1228 // is there any GetMark?
1229 if( m_pTableCursor
)
1231 std::vector
<SwPaM
*> vCursors
;
1232 for(auto& rCursor
: m_pCurrentCursor
->GetRingContainer())
1233 if(&rCursor
!= m_pCurrentCursor
)
1234 vCursors
.push_back(&rCursor
);
1235 for(auto pCursor
: vCursors
)
1237 m_pTableCursor
->DeleteMark();
1239 m_pCurrentCursor
->DeleteMark();
1241 *m_pCurrentCursor
->GetPoint() = *m_pTableCursor
->GetPoint();
1242 m_pCurrentCursor
->GetPtPos() = m_pTableCursor
->GetPtPos();
1243 delete m_pTableCursor
;
1244 m_pTableCursor
= nullptr;
1245 m_pCurrentCursor
->SwSelPaintRects::Show();
1249 if( !m_pCurrentCursor
->HasMark() )
1251 m_pCurrentCursor
->DeleteMark();
1252 if( !m_nCursorMove
)
1253 m_pCurrentCursor
->SwSelPaintRects::Show();
1257 void SwCursorShell::NormalizePam(bool bPointFirst
)
1259 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
1260 m_pCurrentCursor
->Normalize(bPointFirst
);
1263 void SwCursorShell::SwapPam()
1265 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
1266 m_pCurrentCursor
->Exchange();
1269 //TODO: provide documentation
1270 /** Search in the selected area for a Selection that covers the given point.
1272 It checks if a Selection exists but does
1273 not move the current cursor.
1275 @param rPt The point to search at.
1278 bool SwCursorShell::TestCurrPam(
1282 CurrShell
aCurr( this );
1284 // check if the SPoint is in a table selection
1285 if( m_pTableCursor
)
1286 return m_pTableCursor
->Contains( rPt
);
1288 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
1289 // search position <rPt> in document
1290 SwPosition
aPtPos( *m_pCurrentCursor
->GetPoint() );
1293 SwCursorMoveState
aTmpState( CursorMoveState::NONE
);
1294 aTmpState
.m_bSetInReadOnly
= IsReadOnlyAvailable();
1295 if ( !GetLayout()->GetModelPositionForViewPoint( &aPtPos
, aPt
, &aTmpState
) && bTstHit
)
1298 // search in all selections for this position
1299 SwShellCursor
* pCmp
= m_pCurrentCursor
; // keep the pointer on cursor
1302 if (pCmp
->HasMark() && *pCmp
->Start() <= aPtPos
&& *pCmp
->End() > aPtPos
)
1303 return true; // return without update
1304 pCmp
= pCmp
->GetNext();
1305 } while (m_pCurrentCursor
!= pCmp
);
1309 void SwCursorShell::KillPams()
1311 // Does any exist for deletion?
1312 if( !m_pTableCursor
&& !m_pBlockCursor
&& !m_pCurrentCursor
->IsMultiSelection() )
1315 while( m_pCurrentCursor
->GetNext() != m_pCurrentCursor
)
1316 delete m_pCurrentCursor
->GetNext();
1317 m_pCurrentCursor
->SetColumnSelection( false );
1319 if( m_pTableCursor
)
1321 // delete the ring of cursors
1322 m_pCurrentCursor
->DeleteMark();
1323 *m_pCurrentCursor
->GetPoint() = *m_pTableCursor
->GetPoint();
1324 m_pCurrentCursor
->GetPtPos() = m_pTableCursor
->GetPtPos();
1325 delete m_pTableCursor
;
1326 m_pTableCursor
= nullptr;
1328 else if( m_pBlockCursor
)
1330 // delete the ring of cursors
1331 m_pCurrentCursor
->DeleteMark();
1332 SwShellCursor
&rBlock
= m_pBlockCursor
->getShellCursor();
1333 *m_pCurrentCursor
->GetPoint() = *rBlock
.GetPoint();
1334 m_pCurrentCursor
->GetPtPos() = rBlock
.GetPtPos();
1335 rBlock
.DeleteMark();
1336 m_pBlockCursor
->clearPoints();
1338 UpdateCursor( SwCursorShell::SCROLLWIN
);
1341 int SwCursorShell::CompareCursorStackMkCurrPt() const
1344 const SwPosition
*pFirst
= nullptr, *pSecond
= nullptr;
1345 const SwCursor
*pCur
= GetCursor(), *pStack
= m_pStackCursor
;
1346 // cursor on stack is needed if we compare against stack
1349 pFirst
= pStack
->GetMark();
1350 pSecond
= pCur
->GetPoint();
1352 if( !pFirst
|| !pSecond
)
1354 else if( *pFirst
< *pSecond
)
1356 else if( *pFirst
== *pSecond
)
1363 bool SwCursorShell::IsSelOnePara() const
1365 if (m_pCurrentCursor
->IsMultiSelection())
1369 if (m_pCurrentCursor
->GetPoint()->GetNode() == m_pCurrentCursor
->GetMark()->GetNode())
1373 if (GetLayout()->HasMergedParas())
1375 SwContentFrame
const*const pFrame(GetCurrFrame(false));
1376 auto const n(m_pCurrentCursor
->GetMark()->GetNodeIndex());
1377 return FrameContainsNode(*pFrame
, n
);
1382 bool SwCursorShell::IsSttPara() const
1384 if (GetLayout()->HasMergedParas())
1386 SwTextNode
const*const pNode(m_pCurrentCursor
->GetPoint()->GetNode().GetTextNode());
1389 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
*>(
1390 pNode
->getLayoutFrame(GetLayout())));
1393 return pFrame
->MapModelToViewPos(*m_pCurrentCursor
->GetPoint())
1394 == TextFrameIndex(0);
1398 return m_pCurrentCursor
->GetPoint()->GetContentIndex() == 0;
1401 bool SwCursorShell::IsEndPara() const
1403 if (GetLayout()->HasMergedParas())
1405 SwTextNode
const*const pNode(m_pCurrentCursor
->GetPoint()->GetNode().GetTextNode());
1408 SwTextFrame
const*const pFrame(static_cast<SwTextFrame
*>(
1409 pNode
->getLayoutFrame(GetLayout())));
1412 return pFrame
->MapModelToViewPos(*m_pCurrentCursor
->GetPoint())
1413 == TextFrameIndex(pFrame
->GetText().getLength());
1417 return m_pCurrentCursor
->GetPoint()->GetContentIndex() == m_pCurrentCursor
->GetPointContentNode()->Len();
1420 bool SwCursorShell::IsEndOfTable() const
1422 if (IsTableMode() || IsBlockMode() || !IsEndPara())
1426 SwTableNode
const*const pTableNode( IsCursorInTable() );
1431 SwEndNode
const*const pEndTableNode(pTableNode
->EndOfSectionNode());
1432 SwNodeIndex
const lastNode(*pEndTableNode
, -2);
1433 SAL_WARN_IF(!lastNode
.GetNode().GetTextNode(), "sw.core",
1434 "text node expected");
1435 return (lastNode
== m_pCurrentCursor
->GetPoint()->GetNode());
1438 bool SwCursorShell::IsCursorInFootnote() const
1440 SwStartNodeType aStartNodeType
= m_pCurrentCursor
->GetPointNode().StartOfSectionNode()->GetStartNodeType();
1441 return aStartNodeType
== SwStartNodeType::SwFootnoteStartNode
;
1444 Point
SwCursorShell::GetCursorPagePos() const
1447 if (SwFrame
*pFrame
= GetCurrFrame())
1449 if (SwPageFrame
* pCurrentPage
= pFrame
->FindPageFrame())
1451 const Point
& rDocPos
= GetCursorDocPos();
1452 aRet
= rDocPos
- pCurrentPage
->getFrameArea().TopLeft();
1458 bool SwCursorShell::IsInFrontOfLabel() const
1460 return m_pCurrentCursor
->IsInFrontOfLabel();
1463 bool SwCursorShell::SetInFrontOfLabel( bool bNew
)
1465 if ( bNew
!= IsInFrontOfLabel() )
1467 m_pCurrentCursor
->SetInFrontOfLabel_( bNew
);
1468 UpdateMarkedListLevel();
1476 void collectUIInformation(const OUString
& aPage
)
1478 EventDescription aDescription
;
1479 aDescription
.aAction
= "GOTO";
1480 aDescription
.aParameters
= {{"PAGE", aPage
}};
1481 aDescription
.aID
= "writer_edit";
1482 aDescription
.aKeyWord
= "SwEditWinUIObject";
1483 aDescription
.aParent
= "MainWindow";
1484 UITestLogger::getInstance().logEvent(aDescription
);
1489 bool SwCursorShell::GotoPage( sal_uInt16 nPage
)
1491 CurrShell
aCurr( this );
1492 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
1493 SwCursorSaveState
aSaveState( *m_pCurrentCursor
);
1494 bool bRet
= GetLayout()->SetCurrPage( m_pCurrentCursor
, nPage
) &&
1495 !m_pCurrentCursor
->IsSelOvr( SwCursorSelOverFlags::Toggle
|
1496 SwCursorSelOverFlags::ChangePos
);
1498 UpdateCursor(SwCursorShell::SCROLLWIN
|SwCursorShell::CHKRANGE
|SwCursorShell::READONLY
);
1500 collectUIInformation(OUString::number(nPage
));
1504 void SwCursorShell::GetCharRectAt(SwRect
& rRect
, const SwPosition
* pPos
)
1506 SwContentFrame
* pFrame
= GetCurrFrame();
1507 pFrame
->GetCharRect( rRect
, *pPos
);
1510 void SwCursorShell::GetPageNum( sal_uInt16
&rnPhyNum
, sal_uInt16
&rnVirtNum
,
1511 bool bAtCursorPos
, const bool bCalcFrame
)
1513 CurrShell
aCurr( this );
1514 // page number: first visible page or the one at the cursor
1515 const SwContentFrame
* pCFrame
;
1516 const SwPageFrame
*pPg
= nullptr;
1518 if( !bAtCursorPos
|| nullptr == (pCFrame
= GetCurrFrame( bCalcFrame
)) ||
1519 nullptr == (pPg
= pCFrame
->FindPageFrame()) )
1521 pPg
= Imp()->GetFirstVisPage(GetOut());
1522 while( pPg
&& pPg
->IsEmptyPage() )
1523 pPg
= static_cast<const SwPageFrame
*>(pPg
->GetNext());
1525 // pPg has to exist with a default of 1 for the special case "Writerstart"
1526 rnPhyNum
= pPg
? pPg
->GetPhyPageNum() : 1;
1527 rnVirtNum
= pPg
? pPg
->GetVirtPageNum() : 1;
1530 sal_uInt16
SwCursorShell::GetPageNumSeqNonEmpty()
1532 CurrShell
aCurr(this);
1533 // page number: first visible page or the one at the cursor
1534 const SwContentFrame
* pCFrame
= GetCurrFrame(/*bCalcFrame*/true);
1535 const SwPageFrame
* pPg
= nullptr;
1537 if (pCFrame
== nullptr || nullptr == (pPg
= pCFrame
->FindPageFrame()))
1539 pPg
= Imp()->GetFirstVisPage(GetOut());
1540 while (pPg
&& pPg
->IsEmptyPage())
1541 pPg
= static_cast<const SwPageFrame
*>(pPg
->GetNext());
1544 sal_uInt16 nPageNo
= 0;
1547 if (!pPg
->IsEmptyPage())
1549 pPg
= static_cast<const SwPageFrame
*>(pPg
->GetPrev());
1554 sal_uInt16
SwCursorShell::GetNextPrevPageNum( bool bNext
)
1556 CurrShell
aCurr( this );
1557 // page number: first visible page or the one at the cursor
1558 const SwPageFrame
*pPg
= Imp()->GetFirstVisPage(GetOut());
1561 const SwTwips nPageTop
= pPg
->getFrameArea().Top();
1565 // go to next view layout row:
1568 pPg
= static_cast<const SwPageFrame
*>(pPg
->GetNext());
1570 while( pPg
&& pPg
->getFrameArea().Top() == nPageTop
);
1572 while( pPg
&& pPg
->IsEmptyPage() )
1573 pPg
= static_cast<const SwPageFrame
*>(pPg
->GetNext());
1577 // go to previous view layout row:
1580 pPg
= static_cast<const SwPageFrame
*>(pPg
->GetPrev());
1582 while( pPg
&& pPg
->getFrameArea().Top() == nPageTop
);
1584 while( pPg
&& pPg
->IsEmptyPage() )
1585 pPg
= static_cast<const SwPageFrame
*>(pPg
->GetPrev());
1588 // pPg has to exist with a default of 1 for the special case "Writerstart"
1589 return pPg
? pPg
->GetPhyPageNum() : USHRT_MAX
;
1592 sal_uInt16
SwCursorShell::GetPageCnt()
1594 CurrShell
aCurr( this );
1595 // return number of pages
1596 return GetLayout()->GetPageNum();
1599 OUString
SwCursorShell::getPageRectangles()
1601 CurrShell
aCurr(this);
1602 SwRootFrame
* pLayout
= GetLayout();
1603 OUStringBuffer aBuf
;
1604 for (const SwFrame
* pFrame
= pLayout
->GetLower(); pFrame
; pFrame
= pFrame
->GetNext())
1606 aBuf
.append(OUString::number(pFrame
->getFrameArea().Left())
1608 + OUString::number(pFrame
->getFrameArea().Top())
1610 + OUString::number(pFrame
->getFrameArea().Width())
1612 + OUString::number(pFrame
->getFrameArea().Height())
1615 if (!aBuf
.isEmpty())
1616 aBuf
.setLength( aBuf
.getLength() - 2); // remove the last "; "
1617 return aBuf
.makeStringAndClear();
1620 void SwCursorShell::NotifyCursor(SfxViewShell
* pOtherShell
) const
1622 auto pView
= const_cast<SdrView
*>(GetDrawView());
1623 if (pView
->GetTextEditObject())
1626 EditView
& rEditView
= pView
->GetTextEditOutlinerView()->GetEditView();
1627 rEditView
.RegisterOtherShell(pOtherShell
);
1628 rEditView
.ShowCursor();
1629 rEditView
.RegisterOtherShell(nullptr);
1630 // Text selection, if any.
1631 rEditView
.DrawSelectionXOR(pOtherShell
);
1634 if (OutlinerView
* pOutlinerView
= pView
->GetTextEditOutlinerView())
1636 OString sRect
= pOutlinerView
->GetOutputArea().toString();
1637 SfxLokHelper::notifyOtherView(GetSfxViewShell(), pOtherShell
, LOK_CALLBACK_VIEW_LOCK
, "rectangle", sRect
);
1643 m_pVisibleCursor
->SetPosAndShow(pOtherShell
);
1644 // Cursor visibility.
1645 if (GetSfxViewShell() != pOtherShell
)
1647 OString aPayload
= OString::boolean(m_bSVCursorVis
);
1648 SfxLokHelper::notifyOtherView(GetSfxViewShell(), pOtherShell
, LOK_CALLBACK_VIEW_CURSOR_VISIBLE
, "visible", aPayload
);
1651 m_pCurrentCursor
->Show(pOtherShell
);
1652 // Graphic selection.
1653 pView
->AdjustMarkHdl(pOtherShell
);
1657 /// go to the next SSelection
1658 bool SwCursorShell::GoNextCursor()
1660 if( !m_pCurrentCursor
->IsMultiSelection() )
1663 CurrShell
aCurr( this );
1664 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
1665 m_pCurrentCursor
= m_pCurrentCursor
->GetNext();
1667 // #i24086#: show also all others
1671 m_pCurrentCursor
->Show(nullptr);
1676 /// go to the previous SSelection
1677 bool SwCursorShell::GoPrevCursor()
1679 if( !m_pCurrentCursor
->IsMultiSelection() )
1682 CurrShell
aCurr( this );
1683 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
1684 m_pCurrentCursor
= m_pCurrentCursor
->GetPrev();
1686 // #i24086#: show also all others
1690 m_pCurrentCursor
->Show(nullptr);
1695 void SwCursorShell::GoNextPrevCursorSetSearchLabel(const bool bNext
)
1697 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty
);
1699 if( !m_pCurrentCursor
->IsMultiSelection() )
1701 if( !m_pCurrentCursor
->HasMark() )
1702 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound
);
1712 void SwCursorShell::Paint(vcl::RenderContext
& rRenderContext
, const tools::Rectangle
&rRect
)
1714 comphelper::FlagRestorationGuard
g(mbSelectAll
, StartsWith_() != StartsWith::None
&& ExtendedSelectedAll());
1715 CurrShell
aCurr( this );
1717 // always switch off all cursors when painting
1718 SwRect
aRect( rRect
);
1721 // if a cursor is visible then hide the SV cursor
1722 if( m_pVisibleCursor
->IsVisible() && !aRect
.Overlaps( m_aCharRect
) )
1725 m_pVisibleCursor
->Hide();
1729 SwViewShell::Paint(rRenderContext
, rRect
);
1731 if( m_bHasFocus
&& !m_bBasicHideCursor
)
1733 SwShellCursor
* pCurrentCursor
= m_pTableCursor
? m_pTableCursor
: m_pCurrentCursor
;
1737 // so that right/bottom borders will not be cropped
1738 pCurrentCursor
->Invalidate( VisArea() );
1739 pCurrentCursor
->Show(nullptr);
1742 pCurrentCursor
->Invalidate( aRect
);
1746 if (SwPostItMgr
* pPostItMgr
= GetPostItMgr())
1748 // No point in showing the cursor for Writer text when there is an
1749 // active annotation edit.
1751 bVis
= !pPostItMgr
->HasActiveSidebarWin();
1754 if( m_bSVCursorVis
&& bVis
) // also show SV cursor again
1755 m_pVisibleCursor
->Show();
1758 void SwCursorShell::VisPortChgd( const SwRect
& rRect
)
1760 CurrShell
aCurr( this );
1761 bool bVis
; // switch off all cursors when scrolling
1763 // if a cursor is visible then hide the SV cursor
1764 bVis
= m_pVisibleCursor
->IsVisible();
1766 m_pVisibleCursor
->Hide();
1768 m_bVisPortChgd
= true;
1769 m_aOldRBPos
.setX(VisArea().Right());
1770 m_aOldRBPos
.setY(VisArea().Bottom());
1772 // For not having problems with the SV cursor, Update() is called for the
1773 // Window in SwViewShell::VisPo...
1774 // During painting no selections should be shown, thus the call is encapsulated. <- TODO: old artefact?
1775 SwViewShell::VisPortChgd( rRect
); // move area
1777 if( m_bSVCursorVis
&& bVis
) // show SV cursor again
1778 m_pVisibleCursor
->Show();
1781 m_bInCMvVisportChgd
= true;
1783 m_bVisPortChgd
= false;
1786 /** Set the cursor back into content.
1788 This should only be called if the cursor was moved (e.g. when deleting a
1789 text frame). The new position is calculated from its current position
1792 void SwCursorShell::UpdateCursorPos()
1794 CurrShell
aCurr( this );
1796 SwShellCursor
* pShellCursor
= getShellCursor( true );
1797 Size
aOldSz( GetDocSize() );
1799 if (isInHiddenTextFrame(pShellCursor
) && !ExtendedSelectedAll())
1801 SwCursorMoveState
aTmpState(CursorMoveState::SetOnlyText
);
1802 aTmpState
.m_bSetInReadOnly
= IsReadOnlyAvailable();
1803 GetLayout()->GetModelPositionForViewPoint( pShellCursor
->GetPoint(), pShellCursor
->GetPtPos(),
1805 pShellCursor
->DeleteMark();
1806 // kde45196-1.html: try to get to a non-hidden paragraph, there must
1807 // be one in the document body
1808 while (isInHiddenTextFrame(pShellCursor
))
1810 if (!pShellCursor
->MovePara(GoNextPara
, fnParaStart
))
1815 while (isInHiddenTextFrame(pShellCursor
))
1817 if (!pShellCursor
->MovePara(GoPrevPara
, fnParaStart
))
1823 auto* pDoc
= GetDoc();
1826 pDoc
->getGrammarContact()->updateCursorPosition(*m_pCurrentCursor
->GetPoint());
1827 pDoc
->getOnlineAccessibilityCheck()->update(*m_pCurrentCursor
->GetPoint());
1831 if( aOldSz
!= GetDocSize() )
1835 // #i65475# - if Point/Mark in hidden sections, move them out
1836 static bool lcl_CheckHiddenSection( SwPosition
& rPos
)
1839 const SwSectionNode
* pSectNd
= rPos
.GetNode().FindSectionNode();
1840 if( pSectNd
&& pSectNd
->GetSection().IsHiddenFlag() )
1842 const SwNode
* pFrameNd
=
1843 rPos
.GetNodes().FindPrvNxtFrameNode( *pSectNd
, pSectNd
->EndOfSectionNode() );
1844 bOk
= pFrameNd
!= nullptr;
1845 SAL_WARN_IF(!bOk
, "sw.core", "found no Node with Frames");
1846 rPos
.Assign( *(bOk
? pFrameNd
: pSectNd
) );
1851 /// Try to set the cursor to the next visible content node.
1852 static void lcl_CheckHiddenPara( SwPosition
& rPos
)
1854 SwNodeIndex
aTmp( rPos
.GetNode() );
1855 SwTextNode
* pTextNd
= aTmp
.GetNode().GetTextNode();
1856 while( pTextNd
&& pTextNd
->HasHiddenCharAttribute( true ) )
1858 SwContentNode
* pContent
= aTmp
.GetNodes().GoNext( &aTmp
);
1859 if ( pContent
&& pContent
->IsTextNode() )
1860 pTextNd
= pContent
->GetTextNode();
1866 rPos
.Assign( *pTextNd
, 0 );
1869 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
1872 // #i27301# - helper class that notifies the accessibility about invalid text
1873 // selections in its destructor
1874 class SwNotifyAccAboutInvalidTextSelections
1877 SwCursorShell
& mrCursorSh
;
1880 explicit SwNotifyAccAboutInvalidTextSelections( SwCursorShell
& _rCursorSh
)
1881 : mrCursorSh( _rCursorSh
)
1884 ~SwNotifyAccAboutInvalidTextSelections() COVERITY_NOEXCEPT_FALSE
1886 mrCursorSh
.InvalidateAccessibleParaTextSelection();
1893 void SwCursorShell::UpdateCursor( sal_uInt16 eFlags
, bool bIdleEnd
)
1895 CurrShell
aCurr( this );
1900 if ( eFlags
& SwCursorShell::READONLY
)
1901 m_bIgnoreReadonly
= true;
1902 return; // if not then no update
1905 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
1906 SwNotifyAccAboutInvalidTextSelections
aInvalidateTextSelections( *this );
1909 if ( m_bIgnoreReadonly
)
1911 m_bIgnoreReadonly
= false;
1912 eFlags
|= SwCursorShell::READONLY
;
1915 if( eFlags
& SwCursorShell::CHKRANGE
) // check all cursor moves for
1916 CheckRange( m_pCurrentCursor
); // overlapping ranges
1919 CheckTableBoxContent();
1921 // If the current cursor is in a table and point/mark in different boxes,
1922 // then the table mode is active (also if it is already active: m_pTableCursor)
1923 SwPaM
* pTstCursor
= getShellCursor( true );
1924 if( pTstCursor
->HasMark() && !m_pBlockCursor
&&
1925 SwDoc::IsInTable( pTstCursor
->GetPoint()->GetNode() ) &&
1927 pTstCursor
->GetPointNode().StartOfSectionNode() !=
1928 pTstCursor
->GetMarkNode().StartOfSectionNode() ) && !mbSelectAll
)
1930 SwShellCursor
* pITmpCursor
= getShellCursor( true );
1931 Point
aTmpPt( pITmpCursor
->GetPtPos() );
1932 Point
aTmpMk( pITmpCursor
->GetMkPos() );
1933 SwPosition
* pPos
= pITmpCursor
->GetPoint();
1935 // Bug 65475 (1999) - if Point/Mark in hidden sections, move them out
1936 lcl_CheckHiddenSection( *pPos
);
1937 lcl_CheckHiddenSection( *pITmpCursor
->GetMark() );
1939 // Move cursor out of hidden paragraphs
1940 if ( !GetViewOptions()->IsShowHiddenChar() )
1942 lcl_CheckHiddenPara( *pPos
);
1943 lcl_CheckHiddenPara( *pITmpCursor
->GetMark() );
1946 std::pair
<Point
, bool> const tmp(aTmpPt
, false);
1947 SwContentFrame
*pTableFrame
= pPos
->GetNode().GetContentNode()->
1948 getLayoutFrame( GetLayout(), pPos
, &tmp
);
1950 OSL_ENSURE( pTableFrame
, "Table Cursor not in Content ??" );
1952 // --> Make code robust. The table cursor may point
1953 // to a table in a currently inactive header.
1954 SwTabFrame
*pTab
= pTableFrame
? pTableFrame
->FindTabFrame() : nullptr;
1956 if ( pTab
&& pTab
->GetTable()->GetRowsToRepeat() > 0 )
1958 // First check if point is in repeated headline:
1959 bool bInRepeatedHeadline
= pTab
->IsFollow() && pTab
->IsInHeadline( *pTableFrame
);
1961 // Second check if mark is in repeated headline:
1962 if ( !bInRepeatedHeadline
)
1964 std::pair
<Point
, bool> const tmp1(aTmpMk
, false);
1965 SwContentFrame
* pMarkTableFrame
= pITmpCursor
->GetMarkContentNode()->
1966 getLayoutFrame(GetLayout(), pITmpCursor
->GetMark(), &tmp1
);
1967 OSL_ENSURE( pMarkTableFrame
, "Table Cursor not in Content ??" );
1969 if ( pMarkTableFrame
)
1971 SwTabFrame
* pMarkTab
= pMarkTableFrame
->FindTabFrame();
1972 OSL_ENSURE( pMarkTab
, "Table Cursor not in Content ??" );
1974 // Make code robust:
1977 bInRepeatedHeadline
= pMarkTab
->IsFollow() && pMarkTab
->IsInHeadline( *pMarkTableFrame
);
1982 // No table cursor in repeated headlines:
1983 if ( bInRepeatedHeadline
)
1985 pTableFrame
= nullptr;
1987 SwMoveFnCollection
const & fnPosSect
= *pPos
< *pITmpCursor
->GetMark()
1991 // then only select inside the Box
1992 if( m_pTableCursor
)
1994 m_pCurrentCursor
->SetMark();
1995 *m_pCurrentCursor
->GetMark() = *m_pTableCursor
->GetMark();
1996 m_pCurrentCursor
->GetMkPos() = m_pTableCursor
->GetMkPos();
1997 m_pTableCursor
->DeleteMark();
1998 m_pTableCursor
->SwSelPaintRects::Hide();
2001 *m_pCurrentCursor
->GetPoint() = *m_pCurrentCursor
->GetMark();
2002 GoCurrSection( *m_pCurrentCursor
, fnPosSect
);
2006 // we really want a table selection
2007 if( pTab
&& pTableFrame
)
2009 if( !m_pTableCursor
)
2011 m_pTableCursor
= new SwShellTableCursor( *this,
2012 *m_pCurrentCursor
->GetMark(), m_pCurrentCursor
->GetMkPos(),
2014 m_pCurrentCursor
->DeleteMark();
2015 m_pCurrentCursor
->SwSelPaintRects::Hide();
2017 CheckTableBoxContent();
2020 SAL_WARN("sw.core", "fdo#74854: "
2021 "this should not happen, but better lose the selection "
2022 "rather than crashing");
2027 SwCursorMoveState
aTmpState( CursorMoveState::NONE
);
2028 aTmpState
.m_bRealHeight
= true;
2030 DisableCallbackAction
a(*GetLayout());
2031 if (!pTableFrame
->GetCharRect( m_aCharRect
, *m_pTableCursor
->GetPoint(), &aTmpState
))
2033 Point
aCentrPt( m_aCharRect
.Center() );
2034 aTmpState
.m_bSetInReadOnly
= IsReadOnlyAvailable();
2035 pTableFrame
->GetModelPositionForViewPoint(m_pTableCursor
->GetPoint(), aCentrPt
, &aTmpState
);
2036 bool const bResult
=
2037 pTableFrame
->GetCharRect(m_aCharRect
, *m_pTableCursor
->GetPoint());
2038 OSL_ENSURE( bResult
, "GetCharRect failed." );
2042 m_pVisibleCursor
->Hide(); // always hide visible Cursor
2043 // scroll Cursor to visible area
2044 if( eFlags
& SwCursorShell::SCROLLWIN
&&
2045 (HasSelection() || eFlags
& SwCursorShell::READONLY
||
2046 !IsCursorReadonly()) )
2048 SwFrame
* pBoxFrame
= pTableFrame
;
2049 while( pBoxFrame
&& !pBoxFrame
->IsCellFrame() )
2050 pBoxFrame
= pBoxFrame
->GetUpper();
2051 if( pBoxFrame
&& pBoxFrame
->getFrameArea().HasArea() )
2052 MakeVisible( pBoxFrame
->getFrameArea() );
2054 MakeVisible( m_aCharRect
);
2057 // let Layout create the Cursors in the Boxes
2058 if( m_pTableCursor
->IsCursorMovedUpdate() )
2059 GetLayout()->MakeTableCursors( *m_pTableCursor
);
2060 if( m_bHasFocus
&& !m_bBasicHideCursor
)
2061 m_pTableCursor
->Show(nullptr);
2063 // set Cursor-Points to the new Positions
2064 m_pTableCursor
->GetPtPos().setX(m_aCharRect
.Left());
2065 m_pTableCursor
->GetPtPos().setY(m_aCharRect
.Top());
2067 if( m_bSVCursorVis
)
2069 m_aCursorHeight
.setX(0);
2070 m_aCursorHeight
.setY(aTmpState
.m_aRealHeight
.getY() < 0 ?
2071 -m_aCharRect
.Width() : m_aCharRect
.Height());
2072 m_pVisibleCursor
->Show(); // show again
2074 m_eMvState
= CursorMoveState::NONE
; // state for cursor travelling - GetModelPositionForViewPoint
2075 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2076 if (Imp()->IsAccessible() && m_bSendAccessibleCursorEvents
)
2077 Imp()->InvalidateAccessibleCursorPosition( pTableFrame
);
2083 if( m_pTableCursor
)
2086 while( m_pCurrentCursor
->GetNext() != m_pCurrentCursor
)
2087 delete m_pCurrentCursor
->GetNext();
2088 m_pCurrentCursor
->DeleteMark();
2089 *m_pCurrentCursor
->GetPoint() = *m_pTableCursor
->GetPoint();
2090 m_pCurrentCursor
->GetPtPos() = m_pTableCursor
->GetPtPos();
2091 delete m_pTableCursor
;
2092 m_pTableCursor
= nullptr;
2095 m_pVisibleCursor
->Hide(); // always hide visible Cursor
2097 // are we perhaps in a protected / hidden Section ?
2099 SwShellCursor
* pShellCursor
= getShellCursor( true );
2100 bool bChgState
= true;
2101 const SwSectionNode
* pSectNd
= pShellCursor
->GetPointNode().FindSectionNode();
2102 if( pSectNd
&& ( pSectNd
->GetSection().IsHiddenFlag() ||
2103 ( !IsReadOnlyAvailable() &&
2104 pSectNd
->GetSection().IsProtectFlag() &&
2105 ( !mxDoc
->GetDocShell() ||
2106 !mxDoc
->GetDocShell()->IsReadOnly() || m_bAllProtect
)) ) )
2108 if( !FindValidContentNode( !HasDrawView() ||
2109 0 == Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount()))
2111 // everything protected/hidden -> special mode
2112 if( m_bAllProtect
&& !IsReadOnlyAvailable() &&
2113 pSectNd
->GetSection().IsProtectFlag() )
2117 m_eMvState
= CursorMoveState::NONE
; // state for cursor travelling
2118 m_bAllProtect
= true;
2119 if( GetDoc()->GetDocShell() )
2121 GetDoc()->GetDocShell()->SetReadOnlyUI();
2122 CallChgLnk(); // notify UI!
2130 bool bWasAllProtect
= m_bAllProtect
;
2131 m_bAllProtect
= false;
2132 if( bWasAllProtect
&& GetDoc()->GetDocShell() &&
2133 GetDoc()->GetDocShell()->IsReadOnlyUI() )
2135 GetDoc()->GetDocShell()->SetReadOnlyUI( false );
2136 CallChgLnk(); // notify UI!
2143 // The cursor must always point into content; there's some code
2144 // that relies on this. (E.g. in SwEditShell::GetScriptType, which always
2145 // loops _behind_ the last node in the selection, which always works if you
2146 // are in content.) To achieve this, we'll force cursor(s) to point into
2147 // content, if UpdateCursorPos() hasn't already done so.
2148 for(SwPaM
& rCmp
: m_pCurrentCursor
->GetRingContainer())
2150 // start will move forwards, end will move backwards
2151 bool bPointIsStart
= ( rCmp
.Start() == rCmp
.GetPoint() );
2153 // move point; forward if it's the start, backwards if it's the end
2154 if( ! rCmp
.GetPoint()->GetNode().IsContentNode() )
2155 rCmp
.Move( bPointIsStart
? fnMoveForward
: fnMoveBackward
,
2158 // move mark (if exists); forward if it's the start, else backwards
2159 if( rCmp
.HasMark() )
2161 if( ! rCmp
.GetMark()->GetNode().IsContentNode() )
2164 rCmp
.Move( !bPointIsStart
? fnMoveForward
: fnMoveBackward
,
2171 SwRect
aOld( m_aCharRect
);
2173 SwContentFrame
*pFrame
;
2175 SwShellCursor
* pShellCursor
= getShellCursor( true );
2181 std::pair
<Point
, bool> const tmp1(pShellCursor
->GetPtPos(), false);
2182 pFrame
= pShellCursor
->GetPointContentNode()->getLayoutFrame(GetLayout(),
2183 pShellCursor
->GetPoint(), &tmp1
);
2184 // if the Frame doesn't exist anymore, the complete Layout has to be
2185 // created, because there used to be a Frame here!
2188 // skip, if it is a hidden deleted cell without frame
2189 if ( GetLayout()->IsHideRedlines() )
2191 const SwStartNode
* pNd
= pShellCursor
->GetPointNode().FindTableBoxStartNode();
2192 if ( pNd
&& pNd
->GetTableBox()->GetRedlineType() == RedlineType::Delete
)
2199 std::pair
<Point
, bool> const tmp(pShellCursor
->GetPtPos(), false);
2200 pFrame
= pShellCursor
->GetPointContentNode()->getLayoutFrame(
2201 GetLayout(), pShellCursor
->GetPoint(), &tmp
);
2204 else if ( Imp()->IsIdleAction() )
2205 // Guarantee everything's properly formatted
2206 pFrame
->PrepareCursor();
2208 // In protected Fly? but ignore in case of frame selection
2209 if( !IsReadOnlyAvailable() && pFrame
->IsProtected() &&
2210 ( !Imp()->GetDrawView() ||
2211 !Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount() ) &&
2212 (!mxDoc
->GetDocShell() ||
2213 !mxDoc
->GetDocShell()->IsReadOnly() || m_bAllProtect
) )
2215 // look for a valid position
2216 bool bChgState
= true;
2217 if( !FindValidContentNode(!HasDrawView() ||
2218 0 == Imp()->GetDrawView()->GetMarkedObjectList().GetMarkCount()))
2220 // everything is protected / hidden -> special Mode
2225 m_eMvState
= CursorMoveState::NONE
; // state for cursor travelling
2226 m_bAllProtect
= true;
2227 if( GetDoc()->GetDocShell() )
2229 GetDoc()->GetDocShell()->SetReadOnlyUI();
2230 CallChgLnk(); // notify UI!
2238 bool bWasAllProtect
= m_bAllProtect
;
2239 m_bAllProtect
= false;
2240 if( bWasAllProtect
&& GetDoc()->GetDocShell() &&
2241 GetDoc()->GetDocShell()->IsReadOnlyUI() )
2243 GetDoc()->GetDocShell()->SetReadOnlyUI( false );
2244 CallChgLnk(); // notify UI!
2246 m_bAllProtect
= false;
2247 bAgainst
= true; // look for the right Frame again
2250 } while( bAgainst
);
2252 SwCursorMoveState
aTmpState( m_eMvState
);
2253 aTmpState
.m_bSetInReadOnly
= IsReadOnlyAvailable();
2254 aTmpState
.m_bRealHeight
= true;
2255 aTmpState
.m_bRealWidth
= IsOverwriteCursor();
2256 aTmpState
.m_nCursorBidiLevel
= pShellCursor
->GetCursorBidiLevel();
2258 // #i27615#,#i30453#
2259 SwSpecialPos aSpecialPos
;
2260 aSpecialPos
.nExtendRange
= SwSPExtendRange::BEFORE
;
2261 if (pShellCursor
->IsInFrontOfLabel())
2263 aTmpState
.m_pSpecialPos
= &aSpecialPos
;
2267 DisableCallbackAction
a(*GetLayout()); // tdf#91602 prevent recursive Action
2268 if (!pFrame
->GetCharRect(m_aCharRect
, *pShellCursor
->GetPoint(), &aTmpState
))
2270 Point
& rPt
= pShellCursor
->GetPtPos();
2271 rPt
= m_aCharRect
.Center();
2272 pFrame
->GetModelPositionForViewPoint( pShellCursor
->GetPoint(), rPt
, &aTmpState
);
2275 UISizeNotify(); // tdf#96256 update view size
2277 if( !pShellCursor
->HasMark() )
2278 m_aCursorHeight
= aTmpState
.m_aRealHeight
;
2281 m_aCursorHeight
.setX(0);
2282 m_aCursorHeight
.setY(aTmpState
.m_aRealHeight
.getY() < 0 ?
2283 -m_aCharRect
.Width() : m_aCharRect
.Height());
2286 if( !bFirst
&& aOld
== m_aCharRect
)
2289 // if the layout says that we are after the 100th iteration still in
2290 // flow then we should always take the current position for granted.
2294 OSL_ENSURE( false, "endless loop? CharRect != OldCharRect ");
2300 // update cursor Points to the new Positions
2301 pShellCursor
->GetPtPos().setX(m_aCharRect
.Left());
2302 pShellCursor
->GetPtPos().setY(m_aCharRect
.Top());
2304 if( !(eFlags
& SwCursorShell::UPDOWN
)) // delete old Pos. of Up/Down
2306 DisableCallbackAction
a(*GetLayout());
2307 pFrame
->Calc(GetOut());
2308 m_nUpDownX
= pFrame
->IsVertical() ?
2309 m_aCharRect
.Top() - pFrame
->getFrameArea().Top() :
2310 m_aCharRect
.Left() - pFrame
->getFrameArea().Left();
2313 // scroll Cursor to visible area
2314 if( m_bHasFocus
&& eFlags
& SwCursorShell::SCROLLWIN
&&
2315 (HasSelection() || eFlags
& SwCursorShell::READONLY
||
2316 !IsCursorReadonly() || GetViewOptions()->IsSelectionInReadonly()) )
2318 // in case of scrolling this EndAction doesn't show the SV cursor
2319 // again, thus save and reset the flag here
2320 bool bSav
= m_bSVCursorVis
;
2321 m_bSVCursorVis
= false;
2323 m_bSVCursorVis
= bSav
;
2326 } while( eFlags
& SwCursorShell::SCROLLWIN
);
2330 if( m_pBlockCursor
)
2331 RefreshBlockCursor();
2333 // We should not restrict cursor update to the active view when using LOK
2334 bool bCheckFocus
= m_bHasFocus
|| comphelper::LibreOfficeKit::isActive();
2336 if( !bIdleEnd
&& bCheckFocus
&& !m_bBasicHideCursor
)
2338 if( m_pTableCursor
)
2339 m_pTableCursor
->SwSelPaintRects::Show();
2342 m_pCurrentCursor
->SwSelPaintRects::Show();
2343 if( m_pBlockCursor
)
2345 SwShellCursor
* pNxt
= m_pCurrentCursor
->GetNext();
2346 while( pNxt
&& pNxt
!= m_pCurrentCursor
)
2348 pNxt
->SwSelPaintRects::Show();
2349 pNxt
= pNxt
->GetNext();
2355 m_eMvState
= CursorMoveState::NONE
; // state for cursor travelling - GetModelPositionForViewPoint
2357 #if !ENABLE_WASM_STRIP_ACCESSIBILITY
2358 if (Imp()->IsAccessible() && m_bSendAccessibleCursorEvents
)
2359 Imp()->InvalidateAccessibleCursorPosition( pFrame
);
2362 // switch from blinking cursor to read-only-text-selection cursor
2363 const sal_uInt64 nBlinkTime
= GetOut()->GetSettings().GetStyleSettings().
2364 GetCursorBlinkTime();
2366 if ( (IsCursorReadonly() && GetViewOptions()->IsSelectionInReadonly()) ==
2367 ( nBlinkTime
!= STYLE_CURSOR_NOBLINKTIME
) )
2369 // non blinking cursor in read only - text selection mode
2370 AllSettings aSettings
= GetOut()->GetSettings();
2371 StyleSettings aStyleSettings
= aSettings
.GetStyleSettings();
2372 const sal_uInt64 nNewBlinkTime
= nBlinkTime
== STYLE_CURSOR_NOBLINKTIME
?
2373 Application::GetSettings().GetStyleSettings().GetCursorBlinkTime() :
2374 STYLE_CURSOR_NOBLINKTIME
;
2375 aStyleSettings
.SetCursorBlinkTime( nNewBlinkTime
);
2376 aSettings
.SetStyleSettings( aStyleSettings
);
2377 GetOut()->SetSettings( aSettings
);
2380 if( m_bSVCursorVis
)
2381 m_pVisibleCursor
->Show(); // show again
2383 if (comphelper::LibreOfficeKit::isActive())
2384 sendLOKCursorUpdates();
2386 getIDocumentMarkAccess()->NotifyCursorUpdate(*this);
2389 void SwCursorShell::sendLOKCursorUpdates()
2391 SwView
* pView
= static_cast<SwView
*>(GetSfxViewShell());
2392 if (!pView
|| !pView
->GetWrtShellPtr())
2395 SwWrtShell
* pShell
= &pView
->GetWrtShell();
2397 SwFrame
* pCurrentFrame
= GetCurrFrame();
2398 SelectionType eType
= pShell
->GetSelectionType();
2400 tools::JsonWriter aJsonWriter
;
2402 if (pCurrentFrame
&& (eType
& SelectionType::Table
) && pCurrentFrame
->IsInTab())
2404 const SwRect
& rPageRect
= pShell
->GetAnyCurRect(CurRectType::Page
, nullptr);
2407 auto columnsNode
= aJsonWriter
.startNode("columns");
2409 pShell
->GetTabCols(aTabCols
);
2411 const int nColumnOffset
= aTabCols
.GetLeftMin() + rPageRect
.Left();
2413 aJsonWriter
.put("left", aTabCols
.GetLeft());
2414 aJsonWriter
.put("right", aTabCols
.GetRight());
2415 aJsonWriter
.put("tableOffset", static_cast<sal_Int64
>(nColumnOffset
));
2418 auto entriesNode
= aJsonWriter
.startArray("entries");
2419 for (size_t i
= 0; i
< aTabCols
.Count(); ++i
)
2421 auto entryNode
= aJsonWriter
.startStruct();
2422 auto const & rEntry
= aTabCols
.GetEntry(i
);
2423 aJsonWriter
.put("position", rEntry
.nPos
);
2424 aJsonWriter
.put("min", rEntry
.nMin
);
2425 aJsonWriter
.put("max", rEntry
.nMax
);
2426 aJsonWriter
.put("hidden", rEntry
.bHidden
);
2432 auto rowsNode
= aJsonWriter
.startNode("rows");
2434 pShell
->GetTabRows(aTabRows
);
2436 const int nRowOffset
= aTabRows
.GetLeftMin() + rPageRect
.Top();
2438 aJsonWriter
.put("left", aTabRows
.GetLeft());
2439 aJsonWriter
.put("right", aTabRows
.GetRight());
2440 aJsonWriter
.put("tableOffset", static_cast<sal_Int64
>(nRowOffset
));
2443 auto entriesNode
= aJsonWriter
.startArray("entries");
2444 for (size_t i
= 0; i
< aTabRows
.Count(); ++i
)
2446 auto entryNode
= aJsonWriter
.startStruct();
2447 auto const & rEntry
= aTabRows
.GetEntry(i
);
2448 aJsonWriter
.put("position", rEntry
.nPos
);
2449 aJsonWriter
.put("min", rEntry
.nMin
);
2450 aJsonWriter
.put("max", rEntry
.nMax
);
2451 aJsonWriter
.put("hidden", rEntry
.bHidden
);
2457 OString pChar
= aJsonWriter
.finishAndGetAsOString();
2458 GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_TABLE_SELECTED
, pChar
);
2461 void SwCursorShell::RefreshBlockCursor()
2463 assert(m_pBlockCursor
);
2464 SwShellCursor
&rBlock
= m_pBlockCursor
->getShellCursor();
2465 Point aPt
= rBlock
.GetPtPos();
2466 std::pair
<Point
, bool> const tmp(aPt
, false);
2467 SwContentFrame
* pFrame
= rBlock
.GetPointContentNode()->getLayoutFrame(
2468 GetLayout(), rBlock
.GetPoint(), &tmp
);
2470 if( m_pBlockCursor
->getEndPoint() && m_pBlockCursor
->getStartPoint() )
2472 aPt
= *m_pBlockCursor
->getStartPoint();
2473 aMk
= *m_pBlockCursor
->getEndPoint();
2477 aPt
= rBlock
.GetPtPos();
2480 if( pFrame
->IsVertical() )
2481 aPt
.setY(pFrame
->getFrameArea().Top() + GetUpDownX());
2483 aPt
.setX(pFrame
->getFrameArea().Left() + GetUpDownX());
2485 aMk
= rBlock
.GetMkPos();
2487 SwRect
aRect( aMk
, aPt
);
2489 SwSelectionList
aSelList( pFrame
);
2491 if( !GetLayout()->FillSelection( aSelList
, aRect
) )
2494 SwCursor
* pNxt
= static_cast<SwCursor
*>(m_pCurrentCursor
->GetNext());
2495 while( pNxt
!= m_pCurrentCursor
)
2498 pNxt
= static_cast<SwCursor
*>(m_pCurrentCursor
->GetNext());
2501 std::list
<SwPaM
*>::iterator pStart
= aSelList
.getStart();
2502 std::list
<SwPaM
*>::iterator pPam
= aSelList
.getEnd();
2503 OSL_ENSURE( pPam
!= pStart
, "FillSelection should deliver at least one PaM" );
2504 m_pCurrentCursor
->SetMark();
2506 // If there is only one text portion inside the rectangle, a simple
2507 // selection is created
2508 if( pPam
== pStart
)
2510 *m_pCurrentCursor
->GetPoint() = *(*pPam
)->GetPoint();
2511 if( (*pPam
)->HasMark() )
2512 *m_pCurrentCursor
->GetMark() = *(*pPam
)->GetMark();
2514 m_pCurrentCursor
->DeleteMark();
2516 m_pCurrentCursor
->SetColumnSelection( false );
2520 // The order of the SwSelectionList has to be preserved but
2521 // the order inside the ring created by CreateCursor() is not like
2522 // expected => First create the selections before the last one
2523 // downto the first selection.
2524 // At least create the cursor for the last selection
2526 *m_pCurrentCursor
->GetPoint() = *(*pPam
)->GetPoint(); // n-1 (if n == number of selections)
2527 if( (*pPam
)->HasMark() )
2528 *m_pCurrentCursor
->GetMark() = *(*pPam
)->GetMark();
2530 m_pCurrentCursor
->DeleteMark();
2532 m_pCurrentCursor
->SetColumnSelection( true );
2533 while( pPam
!= pStart
)
2537 SwShellCursor
* pNew
= new SwShellCursor( *m_pCurrentCursor
);
2538 pNew
->insert( pNew
->begin(), m_pCurrentCursor
->begin(), m_pCurrentCursor
->end());
2539 m_pCurrentCursor
->clear();
2540 m_pCurrentCursor
->DeleteMark();
2542 *m_pCurrentCursor
->GetPoint() = *(*pPam
)->GetPoint(); // n-2, n-3, .., 2, 1
2543 if( (*pPam
)->HasMark() )
2545 m_pCurrentCursor
->SetMark();
2546 *m_pCurrentCursor
->GetMark() = *(*pPam
)->GetMark();
2549 m_pCurrentCursor
->DeleteMark();
2550 m_pCurrentCursor
->SetColumnSelection( true );
2554 SwShellCursor
* pNew
= new SwShellCursor( *m_pCurrentCursor
);
2555 pNew
->insert( pNew
->begin(), m_pCurrentCursor
->begin(), m_pCurrentCursor
->end() );
2556 m_pCurrentCursor
->clear();
2557 m_pCurrentCursor
->DeleteMark();
2559 pPam
= aSelList
.getEnd();
2561 *m_pCurrentCursor
->GetPoint() = *(*pPam
)->GetPoint(); // n, the last selection
2562 if( (*pPam
)->HasMark() )
2564 m_pCurrentCursor
->SetMark();
2565 *m_pCurrentCursor
->GetMark() = *(*pPam
)->GetMark();
2568 m_pCurrentCursor
->DeleteMark();
2569 m_pCurrentCursor
->SetColumnSelection( true );
2574 /// create a copy of the cursor and save it in the stack
2575 void SwCursorShell::Push()
2577 // fdo#60513: if we have a table cursor, copy that; else copy current.
2578 // This seems to work because UpdateCursor() will fix this up on Pop(),
2579 // then MakeBoxSels() will re-create the current m_pCurrentCursor cell ring.
2580 SwShellCursor
*const pCurrent(m_pTableCursor
? m_pTableCursor
: m_pCurrentCursor
);
2581 m_pStackCursor
= new SwShellCursor( *this, *pCurrent
->GetPoint(),
2582 pCurrent
->GetPtPos(), m_pStackCursor
);
2584 if (pCurrent
->HasMark())
2586 m_pStackCursor
->SetMark();
2587 *m_pStackCursor
->GetMark() = *pCurrent
->GetMark();
2593 @param eDelete delete from stack, or delete current
2594 and assign the one from stack as the new current cursor.
2595 @return <true> if there was one on the stack, <false> otherwise
2597 bool SwCursorShell::Pop(PopMode
const eDelete
)
2599 std::optional
<SwCallLink
> aLink(std::in_place
, *this); // watch Cursor-Moves; call Link if needed
2600 return Pop(eDelete
, aLink
);
2603 bool SwCursorShell::Pop(PopMode
const eDelete
,
2604 [[maybe_unused
]] std::optional
<SwCallLink
>& roLink
)
2606 // parameter exists only to be deleted before return
2608 comphelper::ScopeGuard
aGuard( [&]() { roLink
.reset(); } );
2610 // are there any left?
2611 if (nullptr == m_pStackCursor
)
2614 SwShellCursor
*pTmp
= nullptr, *pOldStack
= m_pStackCursor
;
2616 // the successor becomes the current one
2617 if (m_pStackCursor
->GetNext() != m_pStackCursor
)
2619 pTmp
= m_pStackCursor
->GetNext();
2622 if (PopMode::DeleteStack
== eDelete
)
2623 delete m_pStackCursor
;
2625 m_pStackCursor
= pTmp
; // assign new one
2627 if (PopMode::DeleteCurrent
== eDelete
)
2629 ::std::optional
<SwCursorSaveState
> oSaveState( *m_pCurrentCursor
);
2631 // If the visible SSelection was not changed
2632 const Point
& rPoint
= pOldStack
->GetPtPos();
2633 if (rPoint
== m_pCurrentCursor
->GetPtPos() || rPoint
== m_pCurrentCursor
->GetMkPos())
2635 // move "Selections Rectangles"
2636 m_pCurrentCursor
->insert( m_pCurrentCursor
->begin(), pOldStack
->begin(), pOldStack
->end() );
2640 if( pOldStack
->HasMark() )
2642 m_pCurrentCursor
->SetMark();
2643 *m_pCurrentCursor
->GetMark() = *pOldStack
->GetMark();
2644 m_pCurrentCursor
->GetMkPos() = pOldStack
->GetMkPos();
2647 // no selection so revoke old one and set to old position
2648 m_pCurrentCursor
->DeleteMark();
2649 *m_pCurrentCursor
->GetPoint() = *pOldStack
->GetPoint();
2650 m_pCurrentCursor
->GetPtPos() = pOldStack
->GetPtPos();
2653 if( !m_pCurrentCursor
->IsInProtectTable( true ) &&
2654 !m_pCurrentCursor
->IsSelOvr( SwCursorSelOverFlags::Toggle
|
2655 SwCursorSelOverFlags::ChangePos
) )
2657 oSaveState
.reset(); // prevent UAF
2658 UpdateCursor(); // update current cursor
2660 { // tdf#106929 ensure m_pCurrentCursor ring is recreated from table
2661 m_pTableCursor
->SetChgd();
2668 /** Combine two cursors
2670 Delete topmost from stack and use its GetMark in the current.
2672 void SwCursorShell::Combine()
2675 if (nullptr == m_pStackCursor
)
2678 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
2679 // rhbz#689053: IsSelOvr must restore the saved stack position, not the
2680 // current one, because current point + stack mark may be invalid PaM
2681 SwCursorSaveState
aSaveState(*m_pStackCursor
);
2682 // stack cursor & current cursor in same Section?
2683 assert(!m_pStackCursor
->HasMark() ||
2684 CheckNodesRange(m_pStackCursor
->GetMark()->GetNode(),
2685 m_pCurrentCursor
->GetPoint()->GetNode(), true));
2686 *m_pStackCursor
->GetPoint() = *m_pCurrentCursor
->GetPoint();
2687 m_pStackCursor
->GetPtPos() = m_pCurrentCursor
->GetPtPos();
2689 SwShellCursor
* pTmp
= nullptr;
2690 if (m_pStackCursor
->GetNext() != m_pStackCursor
)
2692 pTmp
= m_pStackCursor
->GetNext();
2694 delete m_pCurrentCursor
;
2695 m_pCurrentCursor
= m_pStackCursor
;
2696 m_pStackCursor
->MoveTo(nullptr); // remove from ring
2697 m_pStackCursor
= pTmp
;
2698 if( !m_pCurrentCursor
->IsInProtectTable( true ) &&
2699 !m_pCurrentCursor
->IsSelOvr( SwCursorSelOverFlags::Toggle
|
2700 SwCursorSelOverFlags::ChangePos
) )
2702 UpdateCursor(); // update current cursor
2706 void SwCursorShell::HideCursors()
2708 if( !m_bHasFocus
|| m_bBasicHideCursor
)
2711 // if cursor is visible then hide SV cursor
2712 if( m_pVisibleCursor
->IsVisible() )
2714 CurrShell
aCurr( this );
2715 m_pVisibleCursor
->Hide();
2717 // revoke inversion of SSelection
2718 SwShellCursor
* pCurrentCursor
= m_pTableCursor
? m_pTableCursor
: m_pCurrentCursor
;
2719 pCurrentCursor
->Hide();
2722 void SwCursorShell::ShowCursors( bool bCursorVis
)
2724 if( !m_bHasFocus
|| m_bAllProtect
|| m_bBasicHideCursor
)
2727 CurrShell
aCurr( this );
2728 SwShellCursor
* pCurrentCursor
= m_pTableCursor
? m_pTableCursor
: m_pCurrentCursor
;
2729 pCurrentCursor
->Show(nullptr);
2731 if( m_bSVCursorVis
&& bCursorVis
) // also show SV cursor again
2732 m_pVisibleCursor
->Show();
2735 void SwCursorShell::ShowCursor()
2737 if( m_bBasicHideCursor
)
2740 comphelper::FlagRestorationGuard
g(mbSelectAll
, StartsWith_() != StartsWith::None
&& ExtendedSelectedAll());
2742 m_bSVCursorVis
= true;
2743 m_pCurrentCursor
->SetShowTextInputFieldOverlay( true );
2744 m_pCurrentCursor
->SetShowContentControlOverlay(true);
2746 if (comphelper::LibreOfficeKit::isActive())
2748 const OString aPayload
= OString::boolean(m_bSVCursorVis
);
2749 GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE
, aPayload
);
2750 SfxLokHelper::notifyOtherViews(GetSfxViewShell(), LOK_CALLBACK_VIEW_CURSOR_VISIBLE
, "visible", aPayload
);
2756 void SwCursorShell::HideCursor()
2758 if( m_bBasicHideCursor
)
2761 m_bSVCursorVis
= false;
2762 // possibly reverse selected areas!!
2763 CurrShell
aCurr( this );
2764 m_pCurrentCursor
->SetShowTextInputFieldOverlay( false );
2765 m_pCurrentCursor
->SetShowContentControlOverlay(false);
2766 m_pVisibleCursor
->Hide();
2768 if (comphelper::LibreOfficeKit::isActive())
2770 OString aPayload
= OString::boolean(m_bSVCursorVis
);
2771 GetSfxViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_CURSOR_VISIBLE
, aPayload
);
2772 SfxLokHelper::notifyOtherViews(GetSfxViewShell(), LOK_CALLBACK_VIEW_CURSOR_VISIBLE
, "visible", aPayload
);
2776 void SwCursorShell::ShellLoseFocus()
2778 if( !m_bBasicHideCursor
)
2780 m_bHasFocus
= false;
2783 void SwCursorShell::ShellGetFocus()
2785 comphelper::FlagRestorationGuard
g(mbSelectAll
, StartsWith_() != StartsWith::None
&& ExtendedSelectedAll());
2788 if( !m_bBasicHideCursor
&& VisArea().Width() )
2790 UpdateCursor( o3tl::narrowing
<sal_uInt16
>( SwCursorShell::CHKRANGE
) );
2791 ShowCursors( m_bSVCursorVis
);
2795 /** Get current frame in which the cursor is positioned. */
2796 SwContentFrame
*SwCursorShell::GetCurrFrame( const bool bCalcFrame
) const
2798 CurrShell
aCurr( const_cast<SwCursorShell
*>(this) );
2799 SwContentFrame
*pRet
= nullptr;
2800 SwContentNode
*pNd
= m_pCurrentCursor
->GetPointContentNode();
2805 sal_uInt16
* pST
= const_cast<sal_uInt16
*>(&mnStartAction
);
2807 const Size
aOldSz( GetDocSize() );
2808 std::pair
<Point
, bool> const tmp(m_pCurrentCursor
->GetPtPos(), true);
2809 pRet
= pNd
->getLayoutFrame(GetLayout(), m_pCurrentCursor
->GetPoint(), &tmp
);
2811 if( aOldSz
!= GetDocSize() )
2812 const_cast<SwCursorShell
*>(this)->SizeChgNotify();
2816 std::pair
<Point
, bool> const tmp(m_pCurrentCursor
->GetPtPos(), false);
2817 pRet
= pNd
->getLayoutFrame(GetLayout(), m_pCurrentCursor
->GetPoint(), &tmp
);
2823 //TODO: provide documentation
2824 /** forward all attribute/format changes at the current node to the Link
2829 void SwCursorShell::SwClientNotify(const SwModify
&, const SfxHint
& rHint
)
2831 if(dynamic_cast<const sw::PostGraphicArrivedHint
*>(&rHint
) && m_aGrfArrivedLnk
.IsSet())
2833 m_aGrfArrivedLnk
.Call(*this);
2836 if (rHint
.GetId() != SfxHintId::SwLegacyModify
)
2838 auto pLegacy
= static_cast<const sw::LegacyModifyHint
*>(&rHint
);
2839 auto nWhich
= pLegacy
->GetWhich();
2841 nWhich
= RES_OBJECTDYING
;
2842 if( m_bCallChgLnk
&&
2843 ( !isFormatMessage(nWhich
)
2844 || nWhich
== RES_FMT_CHG
2845 || nWhich
== RES_UPDATE_ATTR
2846 || nWhich
== RES_ATTRSET_CHG
))
2847 // messages are not forwarded
2848 // #i6681#: RES_UPDATE_ATTR is implicitly unset in
2849 // SwTextNode::Insert(SwTextHint*, sal_uInt16); we react here and thus do
2850 // not need to send the expensive RES_FMT_CHG in Insert.
2852 if( nWhich
== RES_OBJECTDYING
)
2859 /** Does the current cursor create a selection?
2861 This means checking if GetMark is set and if SPoint and GetMark differ.
2863 bool SwCursorShell::HasSelection() const
2865 const SwPaM
* pCursor
= getShellCursor( true );
2866 return IsTableMode()
2867 || (pCursor
->HasMark() &&
2868 (*pCursor
->GetPoint() != *pCursor
->GetMark()
2869 || IsFlySelectedByCursor(*GetDoc(), *pCursor
->Start(), *pCursor
->End())));
2872 void SwCursorShell::CallChgLnk()
2874 // Do not make any call in StartAction/EndAction but just set the flag.
2875 // This will be handled in EndAction.
2877 m_bChgCallFlag
= true; // remember change
2878 else if( m_aChgLnk
.IsSet() )
2881 m_aChgLnk
.Call(nullptr);
2882 m_bChgCallFlag
= false; // reset flag
2886 /// get selected text of a node at current cursor
2887 OUString
SwCursorShell::GetSelText() const
2890 if (GetLayout()->HasMergedParas())
2892 SwContentFrame
const*const pFrame(GetCurrFrame(false));
2893 if (pFrame
&& FrameContainsNode(*pFrame
, m_pCurrentCursor
->GetMark()->GetNodeIndex()))
2896 SwPosition
const*const pStart(m_pCurrentCursor
->Start());
2897 SwPosition
const*const pEnd(m_pCurrentCursor
->End());
2898 for (SwNodeOffset i
= pStart
->GetNodeIndex(); i
<= pEnd
->GetNodeIndex(); ++i
)
2900 SwNode
const& rNode(*pStart
->GetNodes()[i
]);
2901 assert(!rNode
.IsEndNode());
2902 if (rNode
.IsStartNode())
2904 i
= rNode
.EndOfSectionIndex();
2906 else if (rNode
.IsTextNode())
2908 sal_Int32
const nStart(i
== pStart
->GetNodeIndex()
2909 ? pStart
->GetContentIndex()
2911 sal_Int32
const nEnd(i
== pEnd
->GetNodeIndex()
2912 ? pEnd
->GetContentIndex()
2913 : rNode
.GetTextNode()->Len());
2914 buf
.append(rNode
.GetTextNode()->GetExpandText(
2916 nStart
, nEnd
- nStart
, false, false, false,
2917 ExpandMode::HideDeletions
));
2921 aText
= buf
.makeStringAndClear();
2924 else if( m_pCurrentCursor
->GetPoint()->GetNodeIndex() ==
2925 m_pCurrentCursor
->GetMark()->GetNodeIndex() )
2927 SwTextNode
* pTextNd
= m_pCurrentCursor
->GetPointNode().GetTextNode();
2930 const sal_Int32 nStt
= m_pCurrentCursor
->Start()->GetContentIndex();
2931 aText
= pTextNd
->GetExpandText(GetLayout(), nStt
,
2932 m_pCurrentCursor
->End()->GetContentIndex() - nStt
);
2938 /** get the nth character of the current SSelection
2940 @param bEnd Start counting from the end? From start otherwise.
2941 @param nOffset position of the character
2943 sal_Unicode
SwCursorShell::GetChar( bool bEnd
, tools::Long nOffset
)
2945 if( IsTableMode() ) // not possible in table mode
2948 const SwPosition
* pPos
= !m_pCurrentCursor
->HasMark() ? m_pCurrentCursor
->GetPoint()
2949 : bEnd
? m_pCurrentCursor
->End() : m_pCurrentCursor
->Start();
2950 SwTextNode
* pTextNd
= pPos
->GetNode().GetTextNode();
2954 const sal_Int32 nPos
= pPos
->GetContentIndex();
2955 const OUString
& rStr
= pTextNd
->GetText();
2956 sal_Unicode cCh
= 0;
2958 if (((nPos
+nOffset
) >= 0 ) && (nPos
+nOffset
) < rStr
.getLength())
2959 cCh
= rStr
[nPos
+ nOffset
];
2964 /** extend current SSelection by n characters
2966 @param bEnd Start counting from the end? From start otherwise.
2967 @param nCount Number of characters.
2969 bool SwCursorShell::ExtendSelection( bool bEnd
, sal_Int32 nCount
)
2971 if( !m_pCurrentCursor
->HasMark() || IsTableMode() )
2972 return false; // no selection
2974 SwPosition
* pPos
= bEnd
? m_pCurrentCursor
->End() : m_pCurrentCursor
->Start();
2975 SwTextNode
* pTextNd
= pPos
->GetNode().GetTextNode();
2978 sal_Int32 nPos
= pPos
->GetContentIndex();
2981 if ((nPos
+ nCount
) <= pTextNd
->GetText().getLength())
2982 nPos
= nPos
+ nCount
;
2984 return false; // not possible
2986 else if( nPos
>= nCount
)
2987 nPos
= nPos
- nCount
;
2989 return false; // not possible anymore
2991 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
2993 pPos
->SetContent(nPos
) ;
2999 /** Move visible cursor to given position in document.
3001 @param rPt The position to move the visible cursor to.
3002 @return <false> if SPoint was corrected by the layout.
3004 bool SwCursorShell::SetVisibleCursor( const Point
&rPt
)
3006 CurrShell
aCurr( this );
3008 SwPosition
aPos( *m_pCurrentCursor
->GetPoint() );
3009 SwCursorMoveState
aTmpState( CursorMoveState::SetOnlyText
);
3010 aTmpState
.m_bSetInReadOnly
= IsReadOnlyAvailable();
3011 aTmpState
.m_bRealHeight
= true;
3013 const bool bRet
= GetLayout()->GetModelPositionForViewPoint( &aPos
, aPt
/*, &aTmpState*/ );
3015 SetInFrontOfLabel( false ); // #i27615#
3017 // show only in TextNodes
3018 SwTextNode
* pTextNd
= aPos
.GetNode().GetTextNode();
3022 const SwSectionNode
* pSectNd
= pTextNd
->FindSectionNode();
3023 if( pSectNd
&& (pSectNd
->GetSection().IsHiddenFlag() ||
3024 ( !IsReadOnlyAvailable() &&
3025 pSectNd
->GetSection().IsProtectFlag())) )
3028 std::pair
<Point
, bool> const tmp(aPt
, true);
3029 SwContentFrame
*pFrame
= pTextNd
->getLayoutFrame(GetLayout(), &aPos
, &tmp
);
3030 if ( Imp()->IsIdleAction() )
3031 pFrame
->PrepareCursor();
3032 SwRect
aTmp( m_aCharRect
);
3034 pFrame
->GetCharRect( m_aCharRect
, aPos
, &aTmpState
);
3037 if( aTmp
== m_aCharRect
&& m_pVisibleCursor
->IsVisible() )
3040 m_pVisibleCursor
->Hide(); // always hide visible cursor
3041 if( IsScrollMDI( this, m_aCharRect
))
3043 MakeVisible( m_aCharRect
);
3044 m_pCurrentCursor
->Show(nullptr);
3048 if( aTmpState
.m_bRealHeight
)
3049 m_aCursorHeight
= aTmpState
.m_aRealHeight
;
3052 m_aCursorHeight
.setX(0);
3053 m_aCursorHeight
.setY(m_aCharRect
.Height());
3056 m_pVisibleCursor
->SetDragCursor();
3057 m_pVisibleCursor
->Show(); // show again
3062 SwVisibleCursor
* SwCursorShell::GetVisibleCursor() const
3064 return m_pVisibleCursor
;
3067 bool SwCursorShell::IsOverReadOnlyPos( const Point
& rPt
) const
3070 SwPaM
aPam( *m_pCurrentCursor
->GetPoint() );
3071 GetLayout()->GetModelPositionForViewPoint( aPam
.GetPoint(), aPt
);
3073 return aPam
.HasReadonlySel(GetViewOptions()->IsFormView(), false);
3076 /** Get the number of elements in the ring of cursors
3078 @param bAll If <false> get only spanned ones (= with selections) (Basic).
3080 sal_uInt16
SwCursorShell::GetCursorCnt( bool bAll
) const
3082 SwPaM
* pTmp
= GetCursor()->GetNext();
3083 sal_uInt16 n
= (bAll
|| ( m_pCurrentCursor
->HasMark() &&
3084 *m_pCurrentCursor
->GetPoint() != *m_pCurrentCursor
->GetMark())) ? 1 : 0;
3085 while( pTmp
!= m_pCurrentCursor
)
3087 if( bAll
|| ( pTmp
->HasMark() &&
3088 *pTmp
->GetPoint() != *pTmp
->GetMark()))
3090 pTmp
= pTmp
->GetNext();
3095 bool SwCursorShell::IsStartOfDoc() const
3097 if( m_pCurrentCursor
->GetPoint()->GetContentIndex() )
3100 // after EndOfIcons comes the content selection (EndNd+StNd+ContentNd)
3101 SwNodeIndex
aIdx( GetDoc()->GetNodes().GetEndOfExtras(), 2 );
3102 if( !aIdx
.GetNode().IsContentNode() )
3103 GetDoc()->GetNodes().GoNext( &aIdx
);
3104 return aIdx
== m_pCurrentCursor
->GetPoint()->GetNode();
3107 bool SwCursorShell::IsEndOfDoc() const
3109 SwNodeIndex
aIdx( GetDoc()->GetNodes().GetEndOfContent(), -1 );
3110 SwContentNode
* pCNd
= aIdx
.GetNode().GetContentNode();
3112 pCNd
= SwNodes::GoPrevious( &aIdx
);
3114 return aIdx
== m_pCurrentCursor
->GetPoint()->GetNode() && pCNd
&&
3115 pCNd
->Len() == m_pCurrentCursor
->GetPoint()->GetContentIndex();
3118 /** Invalidate cursors
3120 Delete all created cursors, set table crsr and last crsr to their TextNode
3121 (or StartNode?). They will then all re-created at the next ::GetCursor() call.
3123 This is needed for Drag&Drop/ Clipboard-paste in tables.
3125 bool SwCursorShell::ParkTableCursor()
3127 if( !m_pTableCursor
)
3130 m_pTableCursor
->ParkCursor();
3132 while( m_pCurrentCursor
->GetNext() != m_pCurrentCursor
)
3133 delete m_pCurrentCursor
->GetNext();
3135 // *always* move cursor's Point and Mark
3136 m_pCurrentCursor
->DeleteMark();
3137 *m_pCurrentCursor
->GetPoint() = *m_pTableCursor
->GetPoint();
3142 void SwCursorShell::ParkPams( SwPaM
* pDelRg
, SwShellCursor
** ppDelRing
)
3144 auto [pStt
, pEnd
] = pDelRg
->StartEnd(); // SwPosition*
3146 SwPaM
*pTmpDel
= nullptr, *pTmp
= *ppDelRing
;
3148 // search over the whole ring
3155 auto [pTmpStt
, pTmpEnd
] = pTmp
->StartEnd(); // SwPosition*
3156 // If a SPoint or GetMark are in a cursor area then cancel the old area.
3157 // During comparison keep in mind that End() is outside the area.
3158 if( *pStt
<= *pTmpStt
)
3160 if( *pEnd
> *pTmpStt
||
3161 ( *pEnd
== *pTmpStt
&& *pEnd
== *pTmpEnd
))
3165 if( *pStt
< *pTmpEnd
)
3169 if (pTmpDel
) // is the pam in the range -> delete
3171 bool bDelete
= true;
3172 if( *ppDelRing
== pTmpDel
)
3174 if( *ppDelRing
== m_pCurrentCursor
)
3176 bDelete
= GoNextCursor();
3180 pTmp
= pTmp
->GetNext();
3184 bDelete
= false; // never delete the StackCursor
3189 if (pTmp
== pTmpDel
)
3191 delete pTmpDel
; // invalidate old area
3195 pTmpDel
->GetPoint()->Assign(SwNodeOffset(0));
3196 pTmpDel
->DeleteMark();
3200 if( bGoNext
&& pTmp
)
3201 pTmp
= pTmp
->GetNext();
3203 } while( !bGoNext
|| *ppDelRing
!= pTmp
);
3206 //TODO: provide documentation
3207 /** Remove selections and additional cursors of all shells.
3209 The remaining cursor of the shell is parked.
3213 void SwCursorShell::ParkCursor( const SwNode
&rIdx
)
3215 const SwNode
*pNode
= &rIdx
;
3218 SwPaM
aNew( *GetCursor()->GetPoint() );
3219 if( pNode
->GetStartNode() )
3221 pNode
= pNode
->StartOfSectionNode();
3222 if( pNode
->IsTableNode() )
3224 // the given node is in a table, thus park cursor to table node
3225 // (outside of the table)
3226 aNew
.GetPoint()->Assign( *pNode
->StartOfSectionNode() );
3229 // Also on the start node itself. Then we need to request the start
3230 // node always via its end node! (StartOfSelection of StartNode is
3232 aNew
.GetPoint()->Assign( *pNode
->EndOfSectionNode()->StartOfSectionNode() );
3235 aNew
.GetPoint()->Assign( *pNode
->StartOfSectionNode() );
3237 aNew
.GetPoint()->Assign(*pNode
->EndOfSectionNode());
3239 // take care of all shells
3240 for(SwViewShell
& rTmp
: GetRingContainer())
3242 if( auto pSh
= dynamic_cast<SwCursorShell
*>(&rTmp
))
3244 if (pSh
->m_pStackCursor
)
3245 pSh
->ParkPams(&aNew
, &pSh
->m_pStackCursor
);
3247 pSh
->ParkPams( &aNew
, &pSh
->m_pCurrentCursor
);
3248 if( pSh
->m_pTableCursor
)
3250 // set table cursor always to 0 and the current one always to
3251 // the beginning of the table
3252 SwPaM
* pTCursor
= pSh
->GetTableCrs();
3253 SwNode
* pTableNd
= pTCursor
->GetPoint()->GetNode().FindTableNode();
3256 pTCursor
->GetPoint()->Assign(SwNodeOffset(0));
3257 pTCursor
->DeleteMark();
3258 pSh
->m_pCurrentCursor
->GetPoint()->Assign( *pTableNd
);
3265 /** Copy constructor
3267 Copy cursor position and add it to the ring.
3268 All views of a document are in the ring of the shell.
3270 SwCursorShell::SwCursorShell( SwCursorShell
& rShell
, vcl::Window
*pInitWin
)
3271 : SwViewShell( rShell
, pInitWin
)
3272 , sw::BroadcastingModify()
3273 , m_pStackCursor( nullptr )
3274 , m_pBlockCursor( nullptr )
3275 , m_pTableCursor( nullptr )
3276 , m_pBoxIdx( nullptr )
3277 , m_pBoxPtr( nullptr )
3279 , m_nLeftFramePos(0)
3281 , m_nCurrentContent(0)
3282 , m_nCurrentNdTyp(SwNodeType::NONE
)
3283 , m_nCursorMove( 0 )
3284 , m_eMvState( CursorMoveState::NONE
)
3285 , m_eEnhancedTableSel(SwTable::SEARCH_NONE
)
3286 , m_nMarkedListLevel( 0 )
3287 , m_oldColFrame(nullptr)
3289 CurrShell
aCurr( this );
3290 // only keep the position of the current cursor of the copy shell
3291 m_pCurrentCursor
= new SwShellCursor( *this, *(rShell
.m_pCurrentCursor
->GetPoint()) );
3292 m_pCurrentCursor
->GetPointContentNode()->Add( this );
3294 m_bAllProtect
= m_bVisPortChgd
= m_bChgCallFlag
= m_bInCMvVisportChgd
=
3295 m_bGCAttr
= m_bIgnoreReadonly
= m_bSelTableCells
= m_bBasicHideCursor
=
3296 m_bOverwriteCursor
= false;
3297 m_bSendAccessibleCursorEvents
= true;
3298 m_bCallChgLnk
= m_bHasFocus
= m_bAutoUpdateCells
= true;
3299 m_bSVCursorVis
= true;
3300 m_bSetCursorInReadOnly
= true;
3301 m_pVisibleCursor
= new SwVisibleCursor( this );
3302 m_bMacroExecAllowed
= rShell
.IsMacroExecAllowed();
3305 /// default constructor
3306 SwCursorShell::SwCursorShell( SwDoc
& rDoc
, vcl::Window
*pInitWin
,
3307 const SwViewOption
*pInitOpt
)
3308 : SwViewShell( rDoc
, pInitWin
, pInitOpt
)
3309 , sw::BroadcastingModify()
3310 , m_pStackCursor( nullptr )
3311 , m_pBlockCursor( nullptr )
3312 , m_pTableCursor( nullptr )
3313 , m_pBoxIdx( nullptr )
3314 , m_pBoxPtr( nullptr )
3316 , m_nLeftFramePos(0)
3318 , m_nCurrentContent(0)
3319 , m_nCurrentNdTyp(SwNodeType::NONE
)
3320 , m_nCursorMove( 0 )
3321 , m_eMvState( CursorMoveState::NONE
) // state for crsr-travelling - GetModelPositionForViewPoint
3322 , m_eEnhancedTableSel(SwTable::SEARCH_NONE
)
3323 , m_nMarkedListLevel( 0 )
3324 , m_oldColFrame(nullptr)
3326 CurrShell
aCurr( this );
3327 // create initial cursor and set it to first content position
3328 SwNodes
& rNds
= rDoc
.GetNodes();
3330 SwNodeIndex
aNodeIdx( *rNds
.GetEndOfContent().StartOfSectionNode() );
3331 SwContentNode
* pCNd
= rNds
.GoNext( &aNodeIdx
); // go to the first ContentNode
3333 m_pCurrentCursor
= new SwShellCursor( *this, SwPosition( aNodeIdx
, pCNd
, 0 ) );
3335 // Register shell as dependent at current node. As a result all attribute
3336 // changes can be forwarded via the Link.
3339 m_bAllProtect
= m_bVisPortChgd
= m_bChgCallFlag
= m_bInCMvVisportChgd
=
3340 m_bGCAttr
= m_bIgnoreReadonly
= m_bSelTableCells
= m_bBasicHideCursor
=
3341 m_bOverwriteCursor
= false;
3342 m_bSendAccessibleCursorEvents
= true;
3343 m_bCallChgLnk
= m_bHasFocus
= m_bAutoUpdateCells
= true;
3344 m_bSVCursorVis
= true;
3345 m_bSetCursorInReadOnly
= true;
3347 m_pVisibleCursor
= new SwVisibleCursor( this );
3348 m_bMacroExecAllowed
= true;
3351 SwCursorShell::~SwCursorShell()
3353 // if it is not the last view then at least the field should be updated
3355 CheckTableBoxContent( m_pCurrentCursor
->GetPoint() );
3357 ClearTableBoxContent();
3359 delete m_pVisibleCursor
;
3360 delete m_pBlockCursor
;
3361 delete m_pTableCursor
;
3364 while(m_pCurrentCursor
->GetNext() != m_pCurrentCursor
)
3365 delete m_pCurrentCursor
->GetNext();
3366 delete m_pCurrentCursor
;
3371 while (m_pStackCursor
->GetNext() != m_pStackCursor
)
3372 delete m_pStackCursor
->GetNext();
3373 delete m_pStackCursor
;
3376 // #i54025# - do not give a HTML parser that might potentially hang as
3377 // a client at the cursor shell the chance to hang itself on a TextNode
3381 SwShellCursor
* SwCursorShell::getShellCursor( bool bBlock
)
3383 if( m_pTableCursor
)
3384 return m_pTableCursor
;
3385 if( m_pBlockCursor
&& bBlock
)
3386 return &m_pBlockCursor
->getShellCursor();
3387 return m_pCurrentCursor
;
3390 /** Should WaitPtr be switched on for the clipboard?
3392 Wait for TableMode, multiple selections and more than x selected paragraphs.
3394 bool SwCursorShell::ShouldWait() const
3396 if ( IsTableMode() || GetCursorCnt() > 1 )
3399 if( HasDrawView() && GetDrawView()->GetMarkedObjectList().GetMarkCount() )
3402 SwPaM
* pPam
= GetCursor();
3403 return pPam
->Start()->GetNodeIndex() + SwNodeOffset(10) <
3404 pPam
->End()->GetNodeIndex();
3407 size_t SwCursorShell::UpdateTableSelBoxes()
3409 if (m_pTableCursor
&& (m_pTableCursor
->IsChgd() || !m_pTableCursor
->GetSelectedBoxesCount()))
3411 GetLayout()->MakeTableCursors( *m_pTableCursor
);
3413 return m_pTableCursor
? m_pTableCursor
->GetSelectedBoxesCount() : 0;
3416 /// show the current selected "object"
3417 void SwCursorShell::MakeSelVisible()
3419 OSL_ENSURE( m_bHasFocus
, "no focus but cursor should be made visible?" );
3420 if( m_aCursorHeight
.Y() < m_aCharRect
.Height() && m_aCharRect
.Height() > VisArea().Height() )
3422 SwRect
aTmp( m_aCharRect
);
3423 tools::Long nDiff
= m_aCharRect
.Height() - VisArea().Height();
3424 if( nDiff
< m_aCursorHeight
.getX() )
3425 aTmp
.Top( nDiff
+ m_aCharRect
.Top() );
3428 aTmp
.Top( m_aCursorHeight
.getX() + m_aCharRect
.Top() );
3429 aTmp
.Height( m_aCursorHeight
.getY() );
3431 if( !aTmp
.HasArea() )
3436 MakeVisible( aTmp
);
3440 if( m_aCharRect
.HasArea() )
3441 MakeVisible( m_aCharRect
);
3444 SwRect
aTmp( m_aCharRect
);
3447 MakeVisible( aTmp
);
3452 /// search a valid content position (not protected/hidden)
3453 bool SwCursorShell::FindValidContentNode( bool bOnlyText
)
3455 if( m_pTableCursor
)
3457 assert(!"Did not remove table selection!");
3461 // #i45129# - everything is allowed in UI-readonly
3462 if( !m_bAllProtect
&& GetDoc()->GetDocShell() &&
3463 GetDoc()->GetDocShell()->IsReadOnlyUI() )
3466 if( m_pCurrentCursor
->HasMark() )
3469 // first check for frames
3470 SwPosition
& rNdPos
= *m_pCurrentCursor
->GetPoint();
3471 SwNodeOffset nNdIdx
= rNdPos
.GetNodeIndex(); // keep backup
3472 SwNodes
& rNds
= mxDoc
->GetNodes();
3473 SwContentNode
* pCNd
= rNdPos
.GetNode().GetContentNode();
3474 const SwContentFrame
* pFrame
;
3476 if (pCNd
&& nullptr != (pFrame
= pCNd
->getLayoutFrame(GetLayout(), m_pCurrentCursor
->GetPoint())) &&
3477 !IsReadOnlyAvailable() && pFrame
->IsProtected() &&
3478 nNdIdx
< rNds
.GetEndOfExtras().GetIndex() )
3480 // skip protected frame
3481 SwPaM
aPam( *m_pCurrentCursor
->GetPoint() );
3483 aPam
.GetMark()->Assign( rNds
.GetEndOfContent() );
3484 aPam
.GetPoint()->Assign( *pCNd
->EndOfSectionNode() );
3486 bool bFirst
= false;
3487 if( nullptr == (pCNd
= ::GetNode( aPam
, bFirst
, fnMoveForward
)))
3489 aPam
.GetMark()->Assign( *rNds
.GetEndOfPostIts().StartOfSectionNode() );
3490 pCNd
= ::GetNode( aPam
, bFirst
, fnMoveBackward
);
3493 if( !pCNd
) // should *never* happen
3495 rNdPos
.Assign(nNdIdx
); // back to old node
3498 *m_pCurrentCursor
->GetPoint() = *aPam
.GetPoint();
3500 else if( bOnlyText
&& pCNd
&& pCNd
->IsNoTextNode() )
3502 // set to beginning of document
3503 rNdPos
.Assign( mxDoc
->GetNodes().GetEndOfExtras() );
3504 mxDoc
->GetNodes().GoNext( &rNdPos
);
3505 nNdIdx
= rNdPos
.GetNodeIndex();
3510 // #i9059# cursor may not stand in protected cells
3511 // (unless cursor in protected areas is OK.)
3512 const SwTableNode
* pTableNode
= rNdPos
.GetNode().FindTableNode();
3513 if( !IsReadOnlyAvailable() &&
3514 pTableNode
!= nullptr && rNdPos
.GetNode().IsProtect() )
3516 // we're in a table, and we're in a protected area, so we're
3517 // probably in a protected cell.
3519 // move forward into non-protected area.
3520 SwPaM
aPam( rNdPos
.GetNode(), 0 );
3521 while( aPam
.GetPointNode().IsProtect() &&
3522 aPam
.Move( fnMoveForward
, GoInContent
) )
3523 ; // nothing to do in the loop; the aPam.Move does the moving!
3525 // didn't work? then go backwards!
3526 if( aPam
.GetPointNode().IsProtect() )
3528 SwPaM
aTmpPaM( rNdPos
.GetNode(), 0 );
3530 while( aPam
.GetPointNode().IsProtect() &&
3531 aPam
.Move( fnMoveBackward
, GoInContent
) )
3532 ; // nothing to do in the loop; the aPam.Move does the moving!
3535 // if we're successful, set the new position
3536 if( ! aPam
.GetPointNode().IsProtect() )
3538 *m_pCurrentCursor
->GetPoint() = *aPam
.GetPoint();
3542 // in a protected frame
3543 const SwSectionNode
* pSectNd
= rNdPos
.GetNode().FindSectionNode();
3544 if( pSectNd
&& ( pSectNd
->GetSection().IsHiddenFlag() ||
3545 ( !IsReadOnlyAvailable() &&
3546 pSectNd
->GetSection().IsProtectFlag() )) )
3549 bool bGoNextSection
= true;
3550 for( int nLoopCnt
= 0; !bOk
&& nLoopCnt
< 2; ++nLoopCnt
)
3558 pCNd
= rNds
.GoNextSection( &rNdPos
,
3559 true, !IsReadOnlyAvailable() );
3561 pCNd
= SwNodes::GoPrevSection( &rNdPos
,
3562 true, !IsReadOnlyAvailable() );
3563 if ( pCNd
== nullptr) break;
3564 // moved inside a table -> check if it is protected
3565 if( pCNd
->FindTableNode() )
3567 SwCallLink
aTmp( *this );
3568 SwCursorSaveState
aSaveState( *m_pCurrentCursor
);
3569 aTmp
.m_nNodeType
= SwNodeType::NONE
; // don't do anything in DTOR
3570 if( !m_pCurrentCursor
->IsInProtectTable( true ) )
3572 const SwSectionNode
* pSNd
= pCNd
->FindSectionNode();
3573 if( !pSNd
|| !pSNd
->GetSection().IsHiddenFlag()
3574 || (!IsReadOnlyAvailable() &&
3575 pSNd
->GetSection().IsProtectFlag() ))
3578 break; // found non-protected cell
3580 continue; // continue search
3586 break; // found non-protected cell
3590 if( bOk
&& rNdPos
.GetNodeIndex() < rNds
.GetEndOfExtras().GetIndex() )
3592 // also check for Fly - might be protected as well
3593 pFrame
= pCNd
->getLayoutFrame(GetLayout(), nullptr, nullptr);
3594 if (nullptr == pFrame
||
3595 ( !IsReadOnlyAvailable() && pFrame
->IsProtected() ) ||
3596 ( bOnlyText
&& pCNd
->IsNoTextNode() ) )
3603 } while( bContinue
);
3608 bGoNextSection
= false;
3609 rNdPos
.Assign( nNdIdx
);
3615 pCNd
= rNdPos
.GetNode().GetContentNode();
3616 const sal_Int32 nContent
= rNdPos
.GetNodeIndex() < nNdIdx
? pCNd
->Len() : 0;
3617 m_pCurrentCursor
->GetPoint()->SetContent( nContent
);
3621 pCNd
= rNdPos
.GetNode().GetContentNode();
3622 // if cursor in hidden frame, always move it
3623 if (!pCNd
|| !pCNd
->getLayoutFrame(GetLayout(), nullptr, nullptr))
3625 SwCursorMoveState
aTmpState( CursorMoveState::NONE
);
3626 aTmpState
.m_bSetInReadOnly
= IsReadOnlyAvailable();
3627 GetLayout()->GetModelPositionForViewPoint( m_pCurrentCursor
->GetPoint(), m_pCurrentCursor
->GetPtPos(),
3634 bool SwCursorShell::IsCursorReadonly() const
3636 if ( GetViewOptions()->IsReadonly() ||
3637 GetViewOptions()->IsFormView() /* Formula view */ )
3639 SwFrame
*pFrame
= GetCurrFrame( false );
3640 const SwFlyFrame
* pFly
;
3641 const SwSection
* pSection
;
3643 if( pFrame
&& pFrame
->IsInFly() &&
3644 (pFly
= pFrame
->FindFlyFrame())->GetFormat()->GetEditInReadonly().GetValue() &&
3646 !pFly
->Lower()->IsNoTextFrame() &&
3647 !GetDrawView()->GetMarkedObjectList().GetMarkCount() )
3651 // edit in readonly sections
3652 else if ( pFrame
&& pFrame
->IsInSct() &&
3653 nullptr != ( pSection
= pFrame
->FindSctFrame()->GetSection() ) &&
3654 pSection
->IsEditInReadonlyFlag() )
3658 else if ( !IsMultiSelection() && CursorInsideInputField() )
3668 /// is the cursor allowed to enter ReadOnly sections?
3669 void SwCursorShell::SetReadOnlyAvailable( bool bFlag
)
3671 // *never* switch in GlobalDoc
3672 if( (!GetDoc()->GetDocShell() ||
3673 dynamic_cast<const SwGlobalDocShell
*>(GetDoc()->GetDocShell()) == nullptr ) &&
3674 bFlag
!= m_bSetCursorInReadOnly
)
3676 // If the flag is switched off then all selections need to be
3677 // invalidated. Otherwise we would trust that nothing protected is selected.
3682 m_bSetCursorInReadOnly
= bFlag
;
3687 bool SwCursorShell::HasReadonlySel(bool const isReplace
) const
3689 // Treat selections that span over start or end of paragraph of an outline node
3690 // with folded outline content as read-only.
3691 if (GetViewOptions()->IsShowOutlineContentVisibilityButton())
3693 SwWrtShell
* pWrtSh
= GetDoc()->GetDocShell()->GetWrtShell();
3694 if (pWrtSh
&& pWrtSh
->HasFoldedOutlineContentSelected())
3698 // If protected area is to be ignored, then selections are never read-only.
3699 if ((IsReadOnlyAvailable() || GetViewOptions()->IsFormView() ||
3700 GetDoc()->GetDocumentSettingManager().get( DocumentSettingId::PROTECT_FORM
)) &&
3701 !SwViewOption::IsIgnoreProtectedArea())
3703 if ( m_pTableCursor
!= nullptr )
3705 // TODO: handling when a table cell (cells) is selected
3706 bRet
= m_pTableCursor
->HasReadOnlyBoxSel()
3707 || m_pTableCursor
->HasReadonlySel(GetViewOptions()->IsFormView(), isReplace
);
3711 for(const SwPaM
& rCursor
: m_pCurrentCursor
->GetRingContainer())
3713 if (rCursor
.HasReadonlySel(GetViewOptions()->IsFormView(), isReplace
))
3724 bool SwCursorShell::HasHiddenSections() const
3726 // Treat selections that span over start or end of paragraph of an outline node
3727 // with folded outline content as read-only.
3728 if (GetViewOptions()->IsShowOutlineContentVisibilityButton())
3730 SwWrtShell
* pWrtSh
= GetDoc()->GetDocShell()->GetWrtShell();
3731 if (pWrtSh
&& pWrtSh
->HasFoldedOutlineContentSelected())
3736 if ( m_pTableCursor
!= nullptr )
3738 bRet
= m_pTableCursor
->HasHiddenBoxSel()
3739 || m_pTableCursor
->HasHiddenSections();
3743 for(const SwPaM
& rCursor
: m_pCurrentCursor
->GetRingContainer())
3745 if (rCursor
.HasHiddenSections())
3756 bool SwCursorShell::IsSelFullPara() const
3760 if( m_pCurrentCursor
->GetPoint()->GetNodeIndex() ==
3761 m_pCurrentCursor
->GetMark()->GetNodeIndex() && !m_pCurrentCursor
->IsMultiSelection() )
3763 sal_Int32 nStt
= m_pCurrentCursor
->GetPoint()->GetContentIndex();
3764 sal_Int32 nEnd
= m_pCurrentCursor
->GetMark()->GetContentIndex();
3766 std::swap( nStt
, nEnd
);
3767 const SwContentNode
* pCNd
= m_pCurrentCursor
->GetPointContentNode();
3768 bRet
= pCNd
&& !nStt
&& nEnd
== pCNd
->Len();
3773 SvxFrameDirection
SwCursorShell::GetTextDirection( const Point
* pPt
) const
3775 SwPosition
aPos( *m_pCurrentCursor
->GetPoint() );
3776 Point
aPt( pPt
? *pPt
: m_pCurrentCursor
->GetPtPos() );
3779 SwCursorMoveState
aTmpState( CursorMoveState::NONE
);
3780 aTmpState
.m_bSetInReadOnly
= IsReadOnlyAvailable();
3782 GetLayout()->GetModelPositionForViewPoint( &aPos
, aPt
, &aTmpState
);
3785 return mxDoc
->GetTextDirection( aPos
, &aPt
);
3788 bool SwCursorShell::IsInVerticalText( const Point
* pPt
) const
3790 const SvxFrameDirection nDir
= GetTextDirection( pPt
);
3791 return SvxFrameDirection::Vertical_RL_TB
== nDir
|| SvxFrameDirection::Vertical_LR_TB
== nDir
3792 || nDir
== SvxFrameDirection::Vertical_LR_BT
;
3795 bool SwCursorShell::IsInRightToLeftText() const
3797 const SvxFrameDirection nDir
= GetTextDirection();
3798 // GetTextDirection uses SvxFrameDirection::Vertical_LR_TB to indicate RTL in
3799 // vertical environment
3800 return SvxFrameDirection::Vertical_LR_TB
== nDir
|| SvxFrameDirection::Horizontal_RL_TB
== nDir
;
3803 /// If the current cursor position is inside a hidden range true is returned. If bSelect is
3804 /// true, the hidden range is selected. If bSelect is false, the hidden range is not selected.
3805 bool SwCursorShell::IsInHiddenRange(const bool bSelect
)
3808 if ( !GetViewOptions()->IsShowHiddenChar() && !m_pCurrentCursor
->HasMark() )
3810 SwPosition
& rPt
= *m_pCurrentCursor
->GetPoint();
3811 const SwTextNode
* pNode
= rPt
.GetNode().GetTextNode();
3814 const sal_Int32 nPos
= rPt
.GetContentIndex();
3816 // check if nPos is in hidden range
3817 sal_Int32 nHiddenStart
;
3818 sal_Int32 nHiddenEnd
;
3819 SwScriptInfo::GetBoundsOfHiddenRange( *pNode
, nPos
, nHiddenStart
, nHiddenEnd
);
3820 if ( COMPLETE_STRING
!= nHiddenStart
)
3825 m_pCurrentCursor
->SetMark();
3826 m_pCurrentCursor
->GetMark()->SetContent(nHiddenEnd
);
3836 sal_Int32
SwCursorShell::Find_Text( const i18nutil::SearchOptions2
& rSearchOpt
,
3837 bool bSearchInNotes
,
3838 SwDocPositions eStart
, SwDocPositions eEnd
,
3843 if( m_pTableCursor
)
3845 delete m_pTableCursor
;
3846 m_pTableCursor
= nullptr;
3847 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
3848 sal_Int32 nRet
= m_pCurrentCursor
->Find_Text(rSearchOpt
, bSearchInNotes
, eStart
, eEnd
,
3849 bCancel
, eRng
, bReplace
, GetLayout());
3850 if( nRet
|| bCancel
)
3855 sal_Int32
SwCursorShell::FindFormat( const SwTextFormatColl
& rFormatColl
,
3856 SwDocPositions eStart
, SwDocPositions eEnd
,
3859 const SwTextFormatColl
* pReplFormat
)
3861 if( m_pTableCursor
)
3863 delete m_pTableCursor
;
3864 m_pTableCursor
= nullptr;
3865 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
3866 sal_Int32 nRet
= m_pCurrentCursor
->FindFormat(rFormatColl
, eStart
, eEnd
, bCancel
, eRng
,
3873 sal_Int32
SwCursorShell::FindAttrs( const SfxItemSet
& rSet
,
3874 bool bNoCollections
,
3875 SwDocPositions eStart
, SwDocPositions eEnd
,
3878 const i18nutil::SearchOptions2
* pSearchOpt
,
3879 const SfxItemSet
* rReplSet
)
3881 if( m_pTableCursor
)
3883 delete m_pTableCursor
;
3884 m_pTableCursor
= nullptr;
3885 SwCallLink
aLk( *this ); // watch Cursor-Moves; call Link if needed
3886 sal_Int32 nRet
= m_pCurrentCursor
->FindAttrs(rSet
, bNoCollections
, eStart
, eEnd
,
3887 bCancel
, eRng
, pSearchOpt
, rReplSet
, GetLayout());
3893 void SwCursorShell::SetSelection( const SwPaM
& rCursor
)
3896 SwCursor
* pCursor
= GetCursor();
3897 *pCursor
->GetPoint() = *rCursor
.GetPoint();
3898 if(rCursor
.GetNext() != &rCursor
)
3900 const SwPaM
*_pStartCursor
= rCursor
.GetNext();
3903 SwPaM
* pCurrentCursor
= CreateCursor();
3904 *pCurrentCursor
->GetPoint() = *_pStartCursor
->GetPoint();
3905 if(_pStartCursor
->HasMark())
3907 pCurrentCursor
->SetMark();
3908 *pCurrentCursor
->GetMark() = *_pStartCursor
->GetMark();
3910 } while( (_pStartCursor
= _pStartCursor
->GetNext()) != &rCursor
);
3912 // CreateCursor() adds a copy of current cursor after current, and then deletes mark of current
3913 // cursor; therefore set current cursor's mark only after creating all other cursors
3914 if (rCursor
.HasMark())
3917 *pCursor
->GetMark() = *rCursor
.GetMark();
3922 static const SwStartNode
* lcl_NodeContext( const SwNode
& rNode
)
3924 const SwStartNode
*pRet
= rNode
.StartOfSectionNode();
3925 while( pRet
->IsSectionNode() || pRet
->IsTableNode() ||
3926 pRet
->GetStartNodeType() == SwTableBoxStartNode
)
3928 pRet
= pRet
->StartOfSectionNode();
3934 Checks if a position is valid. To be valid the position's node must
3935 be a content node and the content must not be unregistered.
3937 @param aPos the position to check.
3939 static bool sw_PosOk(const SwPosition
& aPos
)
3941 return nullptr != aPos
.GetNode().GetContentNode() &&
3942 aPos
.GetContentNode();
3946 Checks if a PaM is valid. For a PaM to be valid its point must be
3947 valid. Additionally if the PaM has a mark this has to be valid, too.
3949 @param aPam the PaM to check
3951 static bool lcl_CursorOk(SwPaM
& aPam
)
3953 return sw_PosOk(*aPam
.GetPoint()) && (! aPam
.HasMark()
3954 || sw_PosOk(*aPam
.GetMark()));
3957 void SwCursorShell::ClearUpCursors()
3959 // start of the ring
3960 SwPaM
* pStartCursor
= GetCursor();
3961 // start loop with second entry of the ring
3962 SwPaM
* pCursor
= pStartCursor
->GetNext();
3964 bool bChanged
= false;
3966 // For all entries in the ring except the start entry delete the entry if
3968 while (pCursor
!= pStartCursor
)
3970 pTmpCursor
= pCursor
->GetNext();
3971 if ( ! lcl_CursorOk(*pCursor
))
3976 pCursor
= pTmpCursor
;
3979 if( pStartCursor
->HasMark() && !sw_PosOk( *pStartCursor
->GetMark() ) )
3981 pStartCursor
->DeleteMark();
3984 if (pStartCursor
->GetPoint()->GetNode().IsTableNode())
3986 // tdf#106959: When cursor points to start of a table, the proper content
3987 // node is the first one inside the table, not the previous one
3988 SwNodes
& aNodes
= GetDoc()->GetNodes();
3989 SwNodeIndex
aIdx(pStartCursor
->GetPoint()->GetNode());
3990 if (SwNode
* pNode
= aNodes
.GoNext(&aIdx
))
3992 SwPaM
aTmpPam(*pNode
);
3993 *pStartCursor
= aTmpPam
;
3997 if( !sw_PosOk( *pStartCursor
->GetPoint() ) )
3999 SwNodes
& aNodes
= GetDoc()->GetNodes();
4000 const SwNode
* pStart
= lcl_NodeContext( pStartCursor
->GetPoint()->GetNode() );
4001 SwNodeIndex
aIdx( pStartCursor
->GetPoint()->GetNode() );
4002 SwNode
* pNode
= SwNodes::GoPrevious(&aIdx
);
4003 if( pNode
== nullptr || lcl_NodeContext( *pNode
) != pStart
)
4005 pNode
= aNodes
.GoNext( &aIdx
);
4006 if( pNode
== nullptr || lcl_NodeContext( *pNode
) != pStart
)
4008 // If the start entry of the ring is invalid replace it with a
4009 // cursor pointing to the beginning of the first content node in the
4011 aIdx
= *(aNodes
.GetEndOfContent().StartOfSectionNode());
4012 pNode
= aNodes
.GoNext( &aIdx
);
4015 bool bFound
= (pNode
!= nullptr);
4021 SwPaM
aTmpPam(*pNode
);
4022 *pStartCursor
= aTmpPam
;
4028 // If at least one of the cursors in the ring have been deleted or replaced,
4029 // remove the table cursor.
4030 if (m_pTableCursor
!= nullptr && bChanged
)
4031 TableCursorToCursor();
4034 OUString
SwCursorShell::GetCursorDescr() const
4038 if (IsMultiSelection())
4039 aResult
+= SwResId(STR_MULTISEL
);
4041 aResult
= SwDoc::GetPaMDescr(*GetCursor());
4046 void SwCursorShell::dumpAsXml(xmlTextWriterPtr pWriter
) const
4048 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("SwCursorShell"));
4050 SwViewShell::dumpAsXml(pWriter
);
4052 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("m_pCurrentCursor"));
4053 for (const SwPaM
& rPaM
: m_pCurrentCursor
->GetRingContainer())
4054 rPaM
.dumpAsXml(pWriter
);
4055 (void)xmlTextWriterEndElement(pWriter
);
4057 (void)xmlTextWriterEndElement(pWriter
);
4060 static void lcl_FillRecognizerData( std::vector
< OUString
>& rSmartTagTypes
,
4061 uno::Sequence
< uno::Reference
< container::XStringKeyMap
> >& rStringKeyMaps
,
4062 const SwWrongList
& rSmartTagList
, sal_Int32 nCurrent
)
4064 // Insert smart tag information
4065 std::vector
< uno::Reference
< container::XStringKeyMap
> > aStringKeyMaps
;
4067 for ( sal_uInt16 i
= 0; i
< rSmartTagList
.Count(); ++i
)
4069 const sal_Int32 nSTPos
= rSmartTagList
.Pos( i
);
4070 const sal_Int32 nSTLen
= rSmartTagList
.Len( i
);
4072 if ( nSTPos
<= nCurrent
&& nCurrent
< nSTPos
+ nSTLen
)
4074 const SwWrongArea
* pArea
= rSmartTagList
.GetElement( i
);
4077 rSmartTagTypes
.push_back( pArea
->maType
);
4078 aStringKeyMaps
.push_back( pArea
->mxPropertyBag
);
4083 if ( !rSmartTagTypes
.empty() )
4085 rStringKeyMaps
= comphelper::containerToSequence(aStringKeyMaps
);
4089 static void lcl_FillTextRange( uno::Reference
<text::XTextRange
>& rRange
,
4090 SwTextNode
& rNode
, sal_Int32 nBegin
, sal_Int32 nLen
)
4092 // create SwPosition for nStartIndex
4093 SwPosition
aStartPos( rNode
, nBegin
);
4094 // create SwPosition for nEndIndex
4095 SwPosition
aEndPos( rNode
, nBegin
+ nLen
);
4097 const rtl::Reference
<SwXTextRange
> xRange
=
4098 SwXTextRange::CreateXTextRange(rNode
.GetDoc(), aStartPos
, &aEndPos
);
4103 void SwCursorShell::GetSmartTagTerm( std::vector
< OUString
>& rSmartTagTypes
,
4104 uno::Sequence
< uno::Reference
< container::XStringKeyMap
> >& rStringKeyMaps
,
4105 uno::Reference
< text::XTextRange
>& rRange
) const
4107 if ( !SwSmartTagMgr::Get().IsSmartTagsEnabled() )
4110 SwPaM
* pCursor
= GetCursor();
4111 SwPosition
aPos( *pCursor
->GetPoint() );
4112 SwTextNode
*pNode
= aPos
.GetNode().GetTextNode();
4113 if ( !pNode
|| pNode
->IsInProtectSect() )
4116 const SwWrongList
*pSmartTagList
= pNode
->GetSmartTags();
4117 if ( !pSmartTagList
)
4120 sal_Int32 nCurrent
= aPos
.GetContentIndex();
4121 sal_Int32 nBegin
= nCurrent
;
4124 if (!pSmartTagList
->InWrongWord(nBegin
, nLen
) || pNode
->IsSymbolAt(nBegin
))
4127 const sal_uInt16 nIndex
= pSmartTagList
->GetWrongPos( nBegin
);
4128 const SwWrongList
* pSubList
= pSmartTagList
->SubList( nIndex
);
4131 pSmartTagList
= pSubList
;
4135 lcl_FillRecognizerData( rSmartTagTypes
, rStringKeyMaps
, *pSmartTagList
, nCurrent
);
4136 lcl_FillTextRange( rRange
, *pNode
, nBegin
, nLen
);
4139 // see also SwEditShell::GetCorrection( const Point* pPt, SwRect& rSelectRect )
4140 void SwCursorShell::GetSmartTagRect( const Point
& rPt
, SwRect
& rSelectRect
)
4142 SwPaM
* pCursor
= GetCursor();
4143 SwPosition
aPos( *pCursor
->GetPoint() );
4145 SwCursorMoveState
eTmpState( CursorMoveState::SetOnlyText
);
4146 SwSpecialPos aSpecialPos
;
4147 eTmpState
.m_pSpecialPos
= &aSpecialPos
;
4149 const SwWrongList
*pSmartTagList
;
4151 if( !GetLayout()->GetModelPositionForViewPoint( &aPos
, aPt
, &eTmpState
) )
4153 pNode
= aPos
.GetNode().GetTextNode();
4156 pSmartTagList
= pNode
->GetSmartTags();
4157 if( !pSmartTagList
)
4159 if( pNode
->IsInProtectSect() )
4162 sal_Int32 nBegin
= aPos
.GetContentIndex();
4165 if (!pSmartTagList
->InWrongWord(nBegin
, nLen
) || pNode
->IsSymbolAt(nBegin
))
4168 // get smarttag word
4169 OUString
aText( pNode
->GetText().copy(nBegin
, nLen
) );
4171 //save the start and end positions of the line and the starting point
4174 const sal_Int32 nLineStart
= GetCursor()->GetPoint()->GetContentIndex();
4176 const sal_Int32 nLineEnd
= GetCursor()->GetPoint()->GetContentIndex();
4177 Pop(PopMode::DeleteCurrent
);
4179 // make sure the selection build later from the data below does not
4180 // include "in word" character to the left and right in order to
4181 // preserve those. Therefore count those "in words" in order to
4182 // modify the selection accordingly.
4183 const sal_Unicode
* pChar
= aText
.getStr();
4184 sal_Int32 nLeft
= 0;
4185 while (*pChar
++ == CH_TXTATR_INWORD
)
4187 pChar
= aText
.getLength() ? aText
.getStr() + aText
.getLength() - 1 : nullptr;
4188 sal_Int32 nRight
= 0;
4189 while (pChar
&& *pChar
-- == CH_TXTATR_INWORD
)
4192 aPos
.SetContent( nBegin
+ nLeft
);
4193 pCursor
= GetCursor();
4194 *pCursor
->GetPoint() = aPos
;
4196 ExtendSelection( true, nLen
- nLeft
- nRight
);
4197 // do not determine the rectangle in the current line
4198 const sal_Int32 nWordStart
= (nBegin
+ nLeft
) < nLineStart
? nLineStart
: nBegin
+ nLeft
;
4199 // take one less than the line end - otherwise the next line would
4201 const sal_Int32 nWordEnd
= std::min(nBegin
+ nLen
- nLeft
- nRight
, nLineEnd
);
4203 pCursor
->DeleteMark();
4204 SwPosition
& rPos
= *GetCursor()->GetPoint();
4205 rPos
.SetContent( nWordStart
);
4207 SwCursorMoveState aState
;
4208 aState
.m_bRealWidth
= true;
4209 SwContentNode
* pContentNode
= pCursor
->GetPointContentNode();
4210 std::pair
<Point
, bool> const tmp(rPt
, false);
4211 SwContentFrame
*pContentFrame
= pContentNode
->getLayoutFrame(
4212 GetLayout(), pCursor
->GetPoint(), &tmp
);
4214 pContentFrame
->GetCharRect( aStartRect
, *pCursor
->GetPoint(), &aState
);
4215 rPos
.SetContent( nWordEnd
- 1 );
4217 pContentFrame
->GetCharRect( aEndRect
, *pCursor
->GetPoint(),&aState
);
4218 rSelectRect
= aStartRect
.Union( aEndRect
);
4219 Pop(PopMode::DeleteCurrent
);
4222 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */