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 <hintids.hxx>
21 #include <editeng/protitem.hxx>
22 #include <com/sun/star/i18n/WordType.hpp>
23 #include <com/sun/star/i18n/XBreakIterator.hpp>
24 #include <unotools/charclass.hxx>
25 #include <svl/ctloptions.hxx>
26 #include <svl/srchitem.hxx>
27 #include <swmodule.hxx>
28 #include <fmtcntnt.hxx>
29 #include <swtblfmt.hxx>
31 #include <unocrsr.hxx>
32 #include <bookmark.hxx>
34 #include <IDocumentUndoRedo.hxx>
35 #include <IDocumentRedlineAccess.hxx>
36 #include <IDocumentLayoutAccess.hxx>
39 #include <section.hxx>
40 #include <swtable.hxx>
42 #include <rootfrm.hxx>
44 #include <notxtfrm.hxx>
45 #include <scriptinfo.hxx>
46 #include <crstate.hxx>
50 #include <breakit.hxx>
52 #include <strings.hrc>
53 #include <redline.hxx>
54 #include <txatbase.hxx>
55 #include <IDocumentMarkAccess.hxx>
57 #include <comphelper/lok.hxx>
60 #include <viewopt.hxx>
62 using namespace ::com::sun::star::i18n
;
64 const sal_uInt16 coSrchRplcThreshold
= 60000;
74 PercentHdl( sal_uLong nStt
, sal_uLong nEnd
, SwDocShell
* pSh
)
75 : pDSh(pSh
), nActPos(nStt
), bBack(false), bNodeIdx(false)
77 bBack
= (nStt
> nEnd
);
79 std::swap( nStt
, nEnd
);
80 ::StartProgress( STR_STATSTR_SEARCH
, nStt
, nEnd
);
83 explicit PercentHdl( const SwPaM
& rPam
)
84 : pDSh( rPam
.GetDoc().GetDocShell() )
87 if( rPam
.GetPoint()->GetNode() == rPam
.GetMark()->GetNode() )
90 nStt
= rPam
.GetMark()->GetContentIndex();
91 nEnd
= rPam
.GetPoint()->GetContentIndex();
96 nStt
= sal_Int32(rPam
.GetMark()->GetNodeIndex());
97 nEnd
= sal_Int32(rPam
.GetPoint()->GetNodeIndex());
100 bBack
= (nStt
> nEnd
);
102 std::swap( nStt
, nEnd
);
103 ::StartProgress( STR_STATSTR_SEARCH
, nStt
, nEnd
, pDSh
);
106 ~PercentHdl() { ::EndProgress( pDSh
); }
108 void NextPos( sal_uLong nPos
) const
109 { ::SetProgressState( bBack
? nActPos
- nPos
: nPos
, pDSh
); }
111 void NextPos( SwPosition
const & rPos
) const
115 nPos
= sal_Int32(rPos
.GetNodeIndex());
117 nPos
= rPos
.GetContentIndex();
118 ::SetProgressState( bBack
? nActPos
- nPos
: nPos
, pDSh
);
124 SwCursor::SwCursor( const SwPosition
&rPos
, SwPaM
* pRing
)
125 : SwPaM( rPos
, pRing
)
126 , m_nRowSpanOffset(0)
127 , m_nCursorBidiLevel(0)
128 , m_bColumnSelection(false)
132 // @@@ semantic: no copy ctor.
133 SwCursor::SwCursor(SwCursor
const& rCpy
, SwPaM
*const pRing
)
134 : SwPaM( rCpy
, pRing
)
135 , m_nRowSpanOffset(rCpy
.m_nRowSpanOffset
)
136 , m_nCursorBidiLevel(rCpy
.m_nCursorBidiLevel
)
137 , m_bColumnSelection(rCpy
.m_bColumnSelection
)
141 SwCursor::~SwCursor()
145 SwCursor
* SwCursor::Create( SwPaM
* pRing
) const
147 return new SwCursor( *GetPoint(), pRing
);
150 bool SwCursor::IsReadOnlyAvailable() const
155 bool SwCursor::IsSkipOverHiddenSections() const
160 bool SwCursor::IsSkipOverProtectSections() const
162 return !IsReadOnlyAvailable();
165 // CreateNewSavePos is virtual so that derived classes of cursor can implement
166 // own SaveObjects if needed and validate them in the virtual check routines.
167 void SwCursor::SaveState()
169 m_vSavePos
.emplace_back( *this );
172 void SwCursor::RestoreState()
174 if (!m_vSavePos
.empty()) // Robust
176 m_vSavePos
.pop_back();
180 /// determine if point is outside of the node-array's content area
181 bool SwCursor::IsNoContent() const
183 return GetPoint()->GetNodeIndex() <
184 GetDoc().GetNodes().GetEndOfExtras().GetIndex();
187 bool SwCursor::IsSelOvrCheck(SwCursorSelOverFlags
)
192 // extracted from IsSelOvr()
193 bool SwTableCursor::IsSelOvrCheck(SwCursorSelOverFlags eFlags
)
195 SwNodes
& rNds
= GetDoc().GetNodes();
196 // check sections of nodes array
197 if( (SwCursorSelOverFlags::CheckNodeSection
& eFlags
)
200 SwNodeIndex
aOldPos( rNds
, GetSavePos()->nNode
);
201 if( !CheckNodesRange( aOldPos
.GetNode(), GetPoint()->GetNode(), true ))
203 GetPoint()->Assign( aOldPos
);
204 GetPoint()->SetContent( GetSavePos()->nContent
);
208 return SwCursor::IsSelOvrCheck(eFlags
);
213 const SwTextAttr
* InputFieldAtPos(SwPosition
const *pPos
)
215 SwTextNode
* pTextNd
= pPos
->GetNode().GetTextNode();
218 return pTextNd
->GetTextAttrAt(pPos
->GetContentIndex(), RES_TXTATR_INPUTFIELD
, ::sw::GetTextAttrMode::Parent
);
222 bool SwCursor::IsSelOvr(SwCursorSelOverFlags
const eFlags
)
224 SwDoc
& rDoc
= GetDoc();
225 SwNodes
& rNds
= rDoc
.GetNodes();
227 bool bSkipOverHiddenSections
= IsSkipOverHiddenSections();
228 bool bSkipOverProtectSections
= IsSkipOverProtectSections();
230 if ( IsSelOvrCheck( eFlags
) )
235 if (m_vSavePos
.back().nNode
!= GetPoint()->GetNodeIndex() &&
236 // (1997) in UI-ReadOnly everything is allowed
237 ( !rDoc
.GetDocShell() || !rDoc
.GetDocShell()->IsReadOnlyUI() ))
239 // check new sections
240 SwPosition
& rPtPos
= *GetPoint();
241 const SwSectionNode
* pSectNd
= rPtPos
.GetNode().FindSectionNode();
243 ((bSkipOverHiddenSections
&& pSectNd
->GetSection().IsHiddenFlag() ) ||
244 (bSkipOverProtectSections
&& pSectNd
->GetSection().IsProtectFlag() )))
246 if( !( SwCursorSelOverFlags::ChangePos
& eFlags
) )
248 // then we're already done
253 // set cursor to new position:
254 SwNodeIndex
aIdx( rPtPos
.GetNode() );
255 sal_Int32 nContentPos
= m_vSavePos
.back().nContent
;
256 bool bGoNxt
= m_vSavePos
.back().nNode
< rPtPos
.GetNodeIndex();
257 SwContentNode
* pCNd
= bGoNxt
258 ? rNds
.GoNextSection( &rPtPos
, bSkipOverHiddenSections
, bSkipOverProtectSections
)
259 : SwNodes::GoPrevSection( &rPtPos
, bSkipOverHiddenSections
, bSkipOverProtectSections
);
260 if( !pCNd
&& ( SwCursorSelOverFlags::EnableRevDirection
& eFlags
))
263 pCNd
= bGoNxt
? rNds
.GoNextSection( &rPtPos
, bSkipOverHiddenSections
, bSkipOverProtectSections
)
264 : SwNodes::GoPrevSection( &rPtPos
, bSkipOverHiddenSections
, bSkipOverProtectSections
);
267 bool bIsValidPos
= nullptr != pCNd
;
268 const bool bValidNodesRange
= bIsValidPos
&&
269 ::CheckNodesRange( rPtPos
.GetNode(), aIdx
.GetNode(), true );
270 if( !bValidNodesRange
)
272 rPtPos
.Assign( m_vSavePos
.back().nNode
);
273 pCNd
= rPtPos
.GetNode().GetContentNode();
278 rPtPos
.Assign( aIdx
);
279 pCNd
= rPtPos
.GetNode().GetContentNode();
282 // then to the beginning of the document
283 rPtPos
.Assign( rNds
.GetEndOfExtras() );
284 pCNd
= rNds
.GoNext( &rPtPos
);
289 // register ContentIndex:
290 const sal_Int32 nTmpPos
= bIsValidPos
? (bGoNxt
? 0 : pCNd
->Len()) : nContentPos
;
291 GetPoint()->SetContent( nTmpPos
);
292 if( !bIsValidPos
|| !bValidNodesRange
||
293 IsInProtectTable( true ) )
297 // is there a protected section in the section?
298 if( HasMark() && bSkipOverProtectSections
)
300 SwNodeOffset nSttIdx
= GetMark()->GetNodeIndex(),
301 nEndIdx
= GetPoint()->GetNodeIndex();
302 if( nEndIdx
<= nSttIdx
)
303 std::swap( nSttIdx
, nEndIdx
);
305 const SwSectionFormats
& rFormats
= rDoc
.GetSections();
306 for( SwSectionFormats::size_type n
= 0; n
< rFormats
.size(); ++n
)
308 const SwSectionFormat
* pFormat
= rFormats
[n
];
309 const SvxProtectItem
& rProtect
= pFormat
->GetProtect();
310 if( rProtect
.IsContentProtected() )
312 const SwFormatContent
& rContent
= pFormat
->GetContent(false);
313 OSL_ENSURE( rContent
.GetContentIdx(), "No SectionNode?" );
314 SwNodeOffset nIdx
= rContent
.GetContentIdx()->GetIndex();
315 if( nSttIdx
<= nIdx
&& nEndIdx
>= nIdx
)
317 // if it is no linked section then we cannot select it
318 const SwSection
& rSect
= *pFormat
->GetSection();
319 if( SectionType::Content
== rSect
.GetType() )
330 const SwNode
* pNd
= &GetPoint()->GetNode();
331 if( pNd
->IsContentNode() && !dynamic_cast<SwUnoCursor
*>(this) )
333 const SwContentFrame
* pFrame
= static_cast<const SwContentNode
*>(pNd
)->getLayoutFrame( rDoc
.getIDocumentLayoutAccess().GetCurrentLayout() );
335 if ( (SwCursorSelOverFlags::ChangePos
& eFlags
) //allowed to change position if it's a bad one
336 && pFrame
&& pFrame
->isFrameAreaDefinitionValid()
337 && !pFrame
->getFrameArea().Height() //a bad zero height position
338 && !InputFieldAtPos(GetPoint()) ) //unless it's a (vertical) input field
340 // skip to the next/prev valid paragraph with a layout
341 SwPosition
& rPtPos
= *GetPoint();
342 bool bGoNxt
= m_vSavePos
.back().nNode
< rPtPos
.GetNodeIndex();
345 pFrame
= bGoNxt
? pFrame
->FindNextCnt(true) : pFrame
->FindPrevCnt();
346 if (!pFrame
|| 0 != pFrame
->getFrameArea().Height() )
350 // #i72394# skip to prev/next valid paragraph with a layout in case
351 // the first search did not succeed:
355 pFrame
= static_cast<const SwContentNode
*>(pNd
)->getLayoutFrame( rDoc
.getIDocumentLayoutAccess().GetCurrentLayout() );
356 while ( pFrame
&& 0 == pFrame
->getFrameArea().Height() )
358 pFrame
= bGoNxt
? pFrame
->FindNextCnt(true) : pFrame
->FindPrevCnt();
362 if (pFrame
!= nullptr)
364 if (pFrame
->IsTextFrame())
366 SwTextFrame
const*const pTextFrame(static_cast<SwTextFrame
const*>(pFrame
));
367 *GetPoint() = pTextFrame
->MapViewToModelPos(TextFrameIndex(
368 bGoNxt
? 0 : pTextFrame
->GetText().getLength()));
372 assert(pFrame
->IsNoTextFrame());
373 SwContentNode
*const pCNd
= const_cast<SwContentNode
*>(
374 static_cast<SwNoTextFrame
const*>(pFrame
)->GetNode());
377 // set this ContentNode as new position
378 rPtPos
.Assign( *pCNd
);
379 // assign corresponding ContentIndex
380 const sal_Int32 nTmpPos
= bGoNxt
? 0 : pCNd
->Len();
381 GetPoint()->SetContent( nTmpPos
);
385 if (rPtPos
.GetNodeIndex() == m_vSavePos
.back().nNode
386 && GetPoint()->GetContentIndex() == m_vSavePos
.back().nContent
)
388 // new position equals saved one
389 // --> trigger restore of saved pos by setting <pFrame> to NULL - see below
393 if ( IsInProtectTable( true ) )
395 // new position in protected table
396 // --> trigger restore of saved pos by setting <pFrame> to NULL - see below
404 assert(!m_vSavePos
.empty());
405 SwContentNode
const*const pSaveNode(rNds
[m_vSavePos
.back().nNode
]->GetContentNode());
406 // if the old position already didn't have a frame, allow moving
407 // anyway, hope the caller can handle that
408 if (pSaveNode
&& pSaveNode
->getLayoutFrame(rDoc
.getIDocumentLayoutAccess().GetCurrentLayout()))
412 return true; // we need a frame
417 // is the cursor allowed to be in a protected node?
418 if( !( SwCursorSelOverFlags::ChangePos
& eFlags
) && !IsAtValidPos() )
428 // check for invalid sections
429 if( !::CheckNodesRange( GetMark()->GetNode(), GetPoint()->GetNode(), true ))
433 return true; // we need a frame
436 pNd
= &GetMark()->GetNode();
437 if( pNd
->IsContentNode()
438 && !static_cast<const SwContentNode
*>(pNd
)->getLayoutFrame( rDoc
.getIDocumentLayoutAccess().GetCurrentLayout() )
439 && !dynamic_cast<SwUnoCursor
*>(this) )
443 return true; // we need a frame
446 // ensure that selection is only inside an InputField or contains the InputField completely
448 const SwTextAttr
* pInputFieldTextAttrAtPoint
= InputFieldAtPos(GetPoint());
449 const SwTextAttr
* pInputFieldTextAttrAtMark
= InputFieldAtPos(GetMark());
451 if ( pInputFieldTextAttrAtPoint
!= pInputFieldTextAttrAtMark
)
453 const SwNodeOffset nRefNodeIdx
=
454 ( SwCursorSelOverFlags::Toggle
& eFlags
)
455 ? m_vSavePos
.back().nNode
456 : GetMark()->GetNodeIndex();
457 const sal_Int32 nRefContentIdx
=
458 ( SwCursorSelOverFlags::Toggle
& eFlags
)
459 ? m_vSavePos
.back().nContent
460 : GetMark()->GetContentIndex();
461 const bool bIsForwardSelection
=
462 nRefNodeIdx
< GetPoint()->GetNodeIndex()
463 || ( nRefNodeIdx
== GetPoint()->GetNodeIndex()
464 && nRefContentIdx
< GetPoint()->GetContentIndex() );
466 if ( pInputFieldTextAttrAtPoint
!= nullptr )
468 const sal_Int32 nNewPointPos
=
469 bIsForwardSelection
? *(pInputFieldTextAttrAtPoint
->End()) : pInputFieldTextAttrAtPoint
->GetStart();
470 GetPoint()->SetContent( nNewPointPos
);
473 if ( pInputFieldTextAttrAtMark
!= nullptr )
475 const sal_Int32 nNewMarkPos
=
476 bIsForwardSelection
? pInputFieldTextAttrAtMark
->GetStart() : *(pInputFieldTextAttrAtMark
->End());
477 GetMark()->SetContent( nNewMarkPos
);
482 const SwTableNode
* pPtNd
= GetPoint()->GetNode().FindTableNode();
483 const SwTableNode
* pMrkNd
= GetMark()->GetNode().FindTableNode();
484 // both in no or in same table node
485 if( ( !pMrkNd
&& !pPtNd
) || pPtNd
== pMrkNd
)
488 // in different tables or only mark in table
491 // not allowed, so go back to old position
493 // Cursor stays at old position
497 // Note: this cannot happen in TableMode
498 // Only Point in Table then go behind/in front of table
499 if (SwCursorSelOverFlags::ChangePos
& eFlags
)
501 bool bSelTop
= GetPoint()->GetNodeIndex() <
502 ((SwCursorSelOverFlags::Toggle
& eFlags
)
503 ? m_vSavePos
.back().nNode
: GetMark()->GetNodeIndex());
505 do { // loop for table after table
506 SwNodeOffset nSEIdx
= pPtNd
->EndOfSectionIndex();
507 SwNodeOffset nSttEndTable
= nSEIdx
+ 1;
510 nSttEndTable
= rNds
[ nSEIdx
]->StartOfSectionIndex() - 1;
512 GetPoint()->Assign( nSttEndTable
);
513 const SwNode
* pMyNd
= &(GetPointNode());
515 if( pMyNd
->IsSectionNode() || ( pMyNd
->IsEndNode() &&
516 pMyNd
->StartOfSectionNode()->IsSectionNode() ) )
519 ? SwNodes::GoPrevSection( GetPoint(),true,false )
520 : rNds
.GoNextSection( GetPoint(),true,false );
522 /* #i12312# Handle failure of Go{Prev|Next}Section */
523 if ( nullptr == pMyNd
)
526 pPtNd
= pMyNd
->FindTableNode();
532 if( pMyNd
->IsContentNode() &&
533 ::CheckNodesRange( GetMark()->GetNode(),
534 GetPoint()->GetNode(), true ))
537 const SwTableNode
* pOuterTableNd
= pMyNd
->FindTableNode();
539 pMyNd
= pOuterTableNd
;
542 SwContentNode
* pCNd
= const_cast<SwContentNode
*>(static_cast<const SwContentNode
*>(pMyNd
));
543 GetPoint()->SetContent( bSelTop
? pCNd
->Len() : 0 );
549 if ( !pMyNd
->IsEndNode() )
551 pPtNd
= pMyNd
->FindTableNode();
554 pPtNd
= pMyNd
->GetTableNode();
560 // stay on old position
565 bool SwCursor::IsInProtectTable( bool bMove
, bool bChgCursor
)
567 SwContentNode
* pCNd
= GetPointContentNode();
571 // No table, no protected cell:
572 const SwTableNode
* pTableNode
= pCNd
->FindTableNode();
576 // Current position == last save position?
577 if (m_vSavePos
.back().nNode
== GetPoint()->GetNodeIndex())
580 // Check for covered cell:
581 bool bInCoveredCell
= false;
582 const SwStartNode
* pTmpSttNode
= pCNd
->FindTableBoxStartNode();
583 OSL_ENSURE( pTmpSttNode
, "In table, therefore I expect to get a SwTableBoxStartNode" );
584 const SwTableBox
* pBox
= pTmpSttNode
? pTableNode
->GetTable().GetTableBox( pTmpSttNode
->GetIndex() ) : nullptr; //Robust #151355
585 if ( pBox
&& pBox
->getRowSpan() < 1 ) // Robust #151270
586 bInCoveredCell
= true;
588 // Positions of covered cells are not acceptable:
589 if ( !bInCoveredCell
)
591 // Position not protected?
592 if ( !pCNd
->IsProtect() )
595 // Cursor in protected cells allowed?
596 if ( IsReadOnlyAvailable() )
600 // If we reach this point, we are in a protected or covered table cell!
605 // restore the last save position
608 return true; // Cursor stays at old position
611 // We are in a protected table cell. Traverse top to bottom?
612 if (m_vSavePos
.back().nNode
< GetPoint()->GetNodeIndex())
614 // search next valid box
615 // if there is another StartNode after the EndNode of a cell then
616 // there is another cell
617 SwNodeIndex
aCellStt( *GetPointNode().FindTableBoxStartNode()->EndOfSectionNode(), 1 );
621 if( !aCellStt
.GetNode().IsStartNode() )
624 pCNd
= aCellStt
.GetNode().GetContentNode();
626 pCNd
= aCellStt
.GetNodes().GoNext( &aCellStt
);
627 bProt
= pCNd
->IsProtect();
630 aCellStt
.Assign( *pCNd
->FindTableBoxStartNode()->EndOfSectionNode(), 1 );
634 if( !bProt
) // found free cell
636 GetPoint()->Assign( aCellStt
);
637 SwContentNode
* pTmpCNd
= GetPointContentNode();
640 GetPoint()->SetContent( 0 );
643 return IsSelOvr( SwCursorSelOverFlags::Toggle
|
644 SwCursorSelOverFlags::ChangePos
);
646 // end of table, so go to next node
648 SwNode
* pNd
= &aCellStt
.GetNode();
649 if( pNd
->IsEndNode() || HasMark())
651 // if only table in FlyFrame or SSelection then stay on old position
656 else if( pNd
->IsTableNode() )
662 bProt
= false; // index is now on a content node
666 // search for the previous valid box
668 // if there is another EndNode in front of the StartNode than there
669 // exists a previous cell
670 SwNodeIndex
aCellStt( *GetPointNode().FindTableBoxStartNode(), -1 );
675 pNd
= &aCellStt
.GetNode();
676 if( !pNd
->IsEndNode() )
678 aCellStt
.Assign( *pNd
->StartOfSectionNode(), +1 );
679 pCNd
= aCellStt
.GetNode().GetContentNode();
681 pCNd
= pNd
->GetNodes().GoNext( &aCellStt
);
682 bProt
= pCNd
->IsProtect();
685 aCellStt
.Assign( *pNd
->FindTableBoxStartNode(), -1 );
689 if( !bProt
) // found free cell
691 GetPoint()->Assign( aCellStt
);
692 SwContentNode
* pTmpCNd
= GetPointContentNode();
695 GetPoint()->SetContent( 0 );
698 return IsSelOvr( SwCursorSelOverFlags::Toggle
|
699 SwCursorSelOverFlags::ChangePos
);
701 // at the beginning of a table, so go to next node
703 pNd
= &aCellStt
.GetNode();
704 if( pNd
->IsStartNode() || HasMark() )
706 // if only table in FlyFrame or SSelection then stay on old position
711 else if( pNd
->StartOfSectionNode()->IsTableNode() )
717 bProt
= false; // index is now on a content node
722 /// Return <true> if cursor can be set to this position
723 bool SwCursor::IsAtValidPos( bool bPoint
) const
725 const SwDoc
& rDoc
= GetDoc();
726 const SwPosition
* pPos
= bPoint
? GetPoint() : GetMark();
727 const SwNode
* pNd
= &pPos
->GetNode();
729 if( pNd
->IsContentNode() && !static_cast<const SwContentNode
*>(pNd
)->getLayoutFrame( rDoc
.getIDocumentLayoutAccess().GetCurrentLayout() ) &&
730 !dynamic_cast<const SwUnoCursor
*>(this) )
735 // #i45129# - in UI-ReadOnly everything is allowed
736 if( !rDoc
.GetDocShell() || !rDoc
.GetDocShell()->IsReadOnlyUI() )
739 const bool bCursorInReadOnly
= IsReadOnlyAvailable();
740 if( !bCursorInReadOnly
&& pNd
->IsProtect() )
743 const SwSectionNode
* pSectNd
= pNd
->FindSectionNode();
745 || !(pSectNd
->GetSection().IsHiddenFlag() ||
746 ( !bCursorInReadOnly
&& pSectNd
->GetSection().IsProtectFlag() ));
749 void SwCursor::SaveTableBoxContent( const SwPosition
* ) {}
751 /// set range for search in document
752 SwMoveFnCollection
const & SwCursor::MakeFindRange( SwDocPositions nStart
,
753 SwDocPositions nEnd
, SwPaM
* pRange
) const
756 FillFindPos( nStart
, *pRange
->GetMark() );
757 FillFindPos( nEnd
, *pRange
->GetPoint() );
759 // determine direction of search
760 return ( SwDocPositions::Start
== nStart
|| SwDocPositions::OtherStart
== nStart
||
761 (SwDocPositions::Curr
== nStart
&&
762 (SwDocPositions::End
== nEnd
|| SwDocPositions::OtherEnd
== nEnd
) ))
763 ? fnMoveForward
: fnMoveBackward
;
766 static sal_Int32
lcl_FindSelection( SwFindParas
& rParas
, SwCursor
* pCurrentCursor
,
767 SwMoveFnCollection
const & fnMove
, SwCursor
*& pFndRing
,
768 SwPaM
& aRegion
, FindRanges eFndRngs
,
769 bool bInReadOnly
, bool& bCancel
)
771 SwDoc
& rDoc
= pCurrentCursor
->GetDoc();
772 bool const bDoesUndo
= rDoc
.GetIDocumentUndoRedo().DoesUndo();
774 sal_Int32 nFound
= 0;
775 const bool bSrchBkwrd
= &fnMove
== &fnMoveBackward
;
776 SwPaM
*pTmpCursor
= pCurrentCursor
, *pSaveCursor
= pCurrentCursor
;
777 std::unique_ptr
<SvxSearchItem
> xSearchItem
;
779 // only create progress bar for ShellCursor
780 bool bIsUnoCursor
= dynamic_cast<SwUnoCursor
*>(pCurrentCursor
) != nullptr;
781 std::unique_ptr
<PercentHdl
> pPHdl
;
782 sal_uInt16 nCursorCnt
= 0;
783 if( FindRanges::InSel
& eFndRngs
)
785 while( pCurrentCursor
!= ( pTmpCursor
= pTmpCursor
->GetNext() ))
787 if( nCursorCnt
&& !bIsUnoCursor
)
788 pPHdl
.reset(new PercentHdl( 0, nCursorCnt
, rDoc
.GetDocShell() ));
791 pSaveCursor
= pSaveCursor
->GetPrev();
796 // independent from search direction: SPoint is always bigger than mark
797 // if the search area is valid
798 SwPosition
*pSttPos
= aRegion
.GetMark(),
799 *pEndPos
= aRegion
.GetPoint();
800 *pSttPos
= *pTmpCursor
->Start();
801 *pEndPos
= *pTmpCursor
->End();
805 if( !nCursorCnt
&& !pPHdl
&& !bIsUnoCursor
)
806 pPHdl
.reset(new PercentHdl( aRegion
));
808 // as long as found and not at same position
809 while( *pSttPos
<= *pEndPos
)
811 nFndRet
= rParas
.DoFind(*pCurrentCursor
, fnMove
, aRegion
, bInReadOnly
, xSearchItem
);
814 *pFndRing
->GetPoint() == *pCurrentCursor
->GetPoint() &&
815 *pFndRing
->GetMark() == *pCurrentCursor
->GetMark() ))
817 if( !( FIND_NO_RING
& nFndRet
))
819 // #i24084# - create ring similar to the one in CreateCursor
820 SwCursor
* pNew
= pCurrentCursor
->Create( pFndRing
);
825 *pNew
->GetMark() = *pCurrentCursor
->GetMark();
830 if( !( eFndRngs
& FindRanges::InSelAll
) )
836 if ((coSrchRplcThreshold
== nFound
)
837 && rDoc
.GetIDocumentUndoRedo().DoesUndo()
838 && rParas
.IsReplaceMode())
840 short nRet
= pCurrentCursor
->MaxReplaceArived();
841 if( RET_YES
== nRet
)
843 rDoc
.GetIDocumentUndoRedo().DelAllUndoObj();
844 rDoc
.GetIDocumentUndoRedo().DoUndo(false);
849 if(RET_CANCEL
== nRet
)
858 // move pEndPos in front of the found area
859 *pEndPos
= *pCurrentCursor
->Start();
861 // move pSttPos behind the found area
862 *pSttPos
= *pCurrentCursor
->End();
864 if( *pSttPos
== *pEndPos
)
865 // in area but at the end => done
868 if( !nCursorCnt
&& pPHdl
)
870 pPHdl
->NextPos( *aRegion
.GetMark() );
874 if( bEnd
|| !( eFndRngs
& ( FindRanges::InSelAll
| FindRanges::InSel
)) )
877 pTmpCursor
= pTmpCursor
->GetNext();
878 if( nCursorCnt
&& pPHdl
)
880 pPHdl
->NextPos( ++pPHdl
->nActPos
);
883 } while( pTmpCursor
!= pSaveCursor
&& pTmpCursor
->GetNext() != pTmpCursor
);
885 if( nFound
&& !pFndRing
) // if no ring should be created
886 pFndRing
= pCurrentCursor
->Create();
888 rDoc
.GetIDocumentUndoRedo().DoUndo(bDoesUndo
);
892 static bool lcl_MakeSelFwrd( const SwNode
& rSttNd
, const SwNode
& rEndNd
,
893 SwPaM
& rPam
, bool bFirst
)
895 if( rSttNd
.GetIndex() + 1 == rEndNd
.GetIndex() )
898 SwNodes
& rNds
= rPam
.GetDoc().GetNodes();
903 rPam
.GetPoint()->Assign(rSttNd
);
904 pCNd
= rNds
.GoNext( rPam
.GetPoint() );
907 rPam
.GetPoint()->AssignStartIndex(*pCNd
);
909 else if( rSttNd
.GetIndex() > rPam
.GetPoint()->GetNodeIndex() ||
910 rPam
.GetPoint()->GetNodeIndex() >= rEndNd
.GetIndex() )
911 // not in this section
915 rPam
.GetPoint()->Assign(rEndNd
);
916 pCNd
= SwNodes::GoPrevious( rPam
.GetPoint() );
919 rPam
.GetPoint()->AssignEndIndex(*pCNd
);
921 return *rPam
.GetMark() < *rPam
.GetPoint();
924 static bool lcl_MakeSelBkwrd( const SwNode
& rSttNd
, const SwNode
& rEndNd
,
925 SwPaM
& rPam
, bool bFirst
)
927 if( rEndNd
.GetIndex() + 1 == rSttNd
.GetIndex() )
930 SwNodes
& rNds
= rPam
.GetDoc().GetNodes();
935 rPam
.GetPoint()->Assign(rSttNd
);
936 pCNd
= SwNodes::GoPrevious( rPam
.GetPoint() );
939 rPam
.GetPoint()->AssignEndIndex(*pCNd
);
941 else if( rEndNd
.GetIndex() > rPam
.GetPoint()->GetNodeIndex() ||
942 rPam
.GetPoint()->GetNodeIndex() >= rSttNd
.GetIndex() )
943 return false; // not in this section
946 rPam
.GetPoint()->Assign(rEndNd
);
947 pCNd
= rNds
.GoNext( rPam
.GetPoint() );
950 rPam
.GetPoint()->SetContent(0);
952 return *rPam
.GetPoint() < *rPam
.GetMark();
955 // this method "searches" for all use cases because in SwFindParas is always the
956 // correct parameters and respective search method
957 sal_Int32
SwCursor::FindAll( SwFindParas
& rParas
,
958 SwDocPositions nStart
, SwDocPositions nEnd
,
959 FindRanges eFndRngs
, bool& bCancel
)
962 SwCursorSaveState
aSaveState( *this );
964 // create region without adding it to the ring
965 SwPaM
aRegion( *GetPoint() );
966 SwMoveFnCollection
const & fnMove
= MakeFindRange( nStart
, nEnd
, &aRegion
);
968 sal_Int32 nFound
= 0;
969 const bool bMvBkwrd
= &fnMove
== &fnMoveBackward
;
970 bool bInReadOnly
= IsReadOnlyAvailable();
971 std::unique_ptr
<SvxSearchItem
> xSearchItem
;
973 SwCursor
* pFndRing
= nullptr;
974 SwNodes
& rNds
= GetDoc().GetNodes();
976 // search in sections?
977 if( FindRanges::InSel
& eFndRngs
)
979 // if string was not found in region then get all sections (cursors
981 nFound
= lcl_FindSelection( rParas
, this, fnMove
,
982 pFndRing
, aRegion
, eFndRngs
,
983 bInReadOnly
, bCancel
);
987 // found string at least once; it's all in new Cursor ring thus delete old one
988 while( GetNext() != this )
991 *GetPoint() = *pFndRing
->GetPoint();
993 *GetMark() = *pFndRing
->GetMark();
994 pFndRing
->GetRingContainer().merge( GetRingContainer() );
997 else if( FindRanges::InOther
& eFndRngs
)
999 // put cursor as copy of current into ring
1000 // chaining points always to first created, so forward
1001 SwCursor
* pSav
= Create( this ); // save the current cursor
1003 // if already outside of body text search from this position or start at
1006 ? lcl_MakeSelBkwrd( rNds
.GetEndOfExtras(),
1007 *rNds
.GetEndOfPostIts().StartOfSectionNode(),
1008 *this, rNds
.GetEndOfExtras().GetIndex() >=
1009 GetPoint()->GetNodeIndex() )
1010 : lcl_MakeSelFwrd( *rNds
.GetEndOfPostIts().StartOfSectionNode(),
1011 rNds
.GetEndOfExtras(), *this,
1012 rNds
.GetEndOfExtras().GetIndex() >=
1013 GetPoint()->GetNodeIndex() ))
1015 nFound
= lcl_FindSelection( rParas
, this, fnMove
, pFndRing
,
1016 aRegion
, eFndRngs
, bInReadOnly
, bCancel
);
1021 // put back the old one
1022 *GetPoint() = *pSav
->GetPoint();
1023 if( pSav
->HasMark() )
1026 *GetMark() = *pSav
->GetMark();
1033 if( !( FindRanges::InSelAll
& eFndRngs
))
1035 // there should only be a single one, thus add it
1036 // independent from search direction: SPoint is always bigger than
1037 // mark if the search area is valid
1038 *GetPoint() = *pFndRing
->GetPoint();
1040 *GetMark() = *pFndRing
->GetMark();
1044 // found string at least once; it's all in new Cursor ring thus delete old one
1045 while( GetNext() != this )
1048 *GetPoint() = *pFndRing
->GetPoint();
1050 *GetMark() = *pFndRing
->GetMark();
1051 pFndRing
->GetRingContainer().merge( GetRingContainer() );
1055 else if( FindRanges::InSelAll
& eFndRngs
)
1057 SwCursor
* pSav
= Create( this ); // save the current cursor
1059 const SwNode
* pSttNd
= ( FindRanges::InBodyOnly
& eFndRngs
)
1060 ? rNds
.GetEndOfContent().StartOfSectionNode()
1061 : rNds
.GetEndOfPostIts().StartOfSectionNode();
1064 ? lcl_MakeSelBkwrd( rNds
.GetEndOfContent(), *pSttNd
, *this, false )
1065 : lcl_MakeSelFwrd( *pSttNd
, rNds
.GetEndOfContent(), *this, false ))
1067 nFound
= lcl_FindSelection( rParas
, this, fnMove
, pFndRing
,
1068 aRegion
, eFndRngs
, bInReadOnly
, bCancel
);
1073 // put back the old one
1074 *GetPoint() = *pSav
->GetPoint();
1075 if( pSav
->HasMark() )
1078 *GetMark() = *pSav
->GetMark();
1084 while( GetNext() != this )
1087 *GetPoint() = *pFndRing
->GetPoint();
1089 *GetMark() = *pFndRing
->GetMark();
1090 pFndRing
->GetRingContainer().merge( GetRingContainer() );
1095 // if a GetMark is set then keep the GetMark of the found object
1096 // This allows spanning an area with this search.
1097 SwPosition
aMarkPos( *GetMark() );
1098 const bool bMarkPos
= HasMark() && (eFndRngs
== FindRanges::InBody
);
1100 nFound
= rParas
.DoFind(*this, fnMove
, aRegion
, bInReadOnly
, xSearchItem
) ? 1 : 0;
1101 if (0 != nFound
&& bMarkPos
)
1102 *GetMark() = aMarkPos
;
1105 if( nFound
&& SwCursor::IsSelOvr( SwCursorSelOverFlags::Toggle
) )
1110 void SwCursor::FillFindPos( SwDocPositions ePos
, SwPosition
& rPos
) const
1112 bool bIsStart
= true;
1113 SwContentNode
* pCNd
= nullptr;
1114 SwNodes
& rNds
= GetDoc().GetNodes();
1118 case SwDocPositions::Start
:
1119 rPos
.Assign(*rNds
.GetEndOfContent().StartOfSectionNode());
1120 pCNd
= rNds
.GoNext( &rPos
);
1122 case SwDocPositions::End
:
1123 rPos
.Assign(rNds
.GetEndOfContent());
1124 pCNd
= SwNodes::GoPrevious( &rPos
);
1127 case SwDocPositions::OtherStart
:
1128 rPos
.Assign( *rNds
[ SwNodeOffset(0) ] );
1129 pCNd
= rNds
.GoNext( &rPos
);
1131 case SwDocPositions::OtherEnd
:
1132 rPos
.Assign( *rNds
.GetEndOfContent().StartOfSectionNode() );
1133 pCNd
= SwNodes::GoPrevious( &rPos
);
1140 if( pCNd
&& !bIsStart
)
1142 rPos
.AssignEndIndex( *pCNd
);
1146 short SwCursor::MaxReplaceArived()
1155 // either the frame's text or the node's text (possibly pre-filtered)
1156 OUString
const* m_pText
;
1157 // this is actually a TextFrameIndex but all of the i18n code uses sal_Int32
1158 sal_Int32 m_nPtIndex
;
1159 // if mapping is needed, use this frame
1160 SwTextFrame
* m_pFrame
;
1161 // input in the constructor, output (via mapping) in the destructor
1162 SwTextNode
*& m_rpTextNode
;
1163 sal_Int32
& m_rPtPos
;
1165 HideWrapper(SwRootFrame
const*const pLayout
,
1166 SwTextNode
*& rpTextNode
, sal_Int32
& rPtPos
,
1167 OUString
const*const pFilteredNodeText
= nullptr)
1168 : m_pText(pFilteredNodeText
)
1170 , m_rpTextNode(rpTextNode
)
1173 if (pLayout
&& pLayout
->HasMergedParas())
1175 m_pFrame
= static_cast<SwTextFrame
*>(rpTextNode
->getLayoutFrame(pLayout
));
1176 m_pText
= &m_pFrame
->GetText();
1177 m_nPtIndex
= sal_Int32(m_pFrame
->MapModelToView(rpTextNode
, rPtPos
));
1183 m_pText
= &rpTextNode
->GetText();
1185 m_nPtIndex
= rPtPos
;
1190 AssignBack(m_rpTextNode
, m_rPtPos
);
1192 void AssignBack(SwTextNode
*& rpTextNode
, sal_Int32
& rPtPos
)
1194 if (0 <= m_nPtIndex
&& m_pFrame
)
1196 std::pair
<SwTextNode
*, sal_Int32
> const pos(
1197 m_pFrame
->MapViewToModel(TextFrameIndex(m_nPtIndex
)));
1198 rpTextNode
= pos
.first
;
1199 rPtPos
= pos
.second
;
1203 rPtPos
= m_nPtIndex
;
1210 bool SwCursor::SelectWord( SwViewShell
const * pViewShell
, const Point
* pPt
)
1212 return SelectWordWT( pViewShell
, WordType::ANYWORD_IGNOREWHITESPACES
, pPt
);
1215 bool SwCursor::IsStartWordWT(sal_Int16 nWordType
, SwRootFrame
const*const pLayout
) const
1218 SwTextNode
* pTextNd
= GetPointNode().GetTextNode();
1221 sal_Int32 nPtPos
= GetPoint()->GetContentIndex();
1223 HideWrapper
w(pLayout
, pTextNd
, nPtPos
);
1225 bRet
= g_pBreakIt
->GetBreakIter()->isBeginWord(
1226 *w
.m_pText
, w
.m_nPtIndex
,
1227 g_pBreakIt
->GetLocale( pTextNd
->GetLang( nPtPos
)),
1233 bool SwCursor::IsEndWordWT(sal_Int16 nWordType
, SwRootFrame
const*const pLayout
) const
1236 SwTextNode
* pTextNd
= GetPointNode().GetTextNode();
1239 sal_Int32 nPtPos
= GetPoint()->GetContentIndex();
1241 HideWrapper
w(pLayout
, pTextNd
, nPtPos
);
1243 bRet
= g_pBreakIt
->GetBreakIter()->isEndWord(
1244 *w
.m_pText
, w
.m_nPtIndex
,
1245 g_pBreakIt
->GetLocale( pTextNd
->GetLang( nPtPos
) ),
1252 bool SwCursor::IsInWordWT(sal_Int16 nWordType
, SwRootFrame
const*const pLayout
) const
1255 SwTextNode
* pTextNd
= GetPointNode().GetTextNode();
1258 sal_Int32 nPtPos
= GetPoint()->GetContentIndex();
1261 HideWrapper
w(pLayout
, pTextNd
, nPtPos
);
1263 Boundary aBoundary
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
1264 *w
.m_pText
, w
.m_nPtIndex
,
1265 g_pBreakIt
->GetLocale( pTextNd
->GetLang( nPtPos
) ),
1269 bRet
= aBoundary
.startPos
!= aBoundary
.endPos
&&
1270 aBoundary
.startPos
<= w
.m_nPtIndex
&&
1271 w
.m_nPtIndex
<= aBoundary
.endPos
;
1272 w
.m_nPtIndex
= aBoundary
.startPos
; // hack: convert startPos back...
1276 const CharClass
& rCC
= GetAppCharClass();
1277 bRet
= rCC
.isLetterNumeric(pTextNd
->GetText(), nPtPos
);
1283 bool SwCursor::IsStartEndSentence(bool bEnd
, SwRootFrame
const*const pLayout
) const
1286 GetPointContentNode() && GetPoint()->GetContentIndex() == GetPointContentNode()->Len() :
1287 GetPoint()->GetContentIndex() == 0;
1289 if ((pLayout
!= nullptr && pLayout
->HasMergedParas()) || !bRet
)
1291 SwCursor
aCursor(*GetPoint(), nullptr);
1292 SwPosition aOrigPos
= *aCursor
.GetPoint();
1293 aCursor
.GoSentence(bEnd
? SwCursor::END_SENT
: SwCursor::START_SENT
, pLayout
);
1294 bRet
= aOrigPos
== *aCursor
.GetPoint();
1299 bool SwCursor::GoStartWordWT(sal_Int16 nWordType
, SwRootFrame
const*const pLayout
)
1302 SwTextNode
* pTextNd
= GetPointNode().GetTextNode();
1305 SwCursorSaveState
aSave( *this );
1306 sal_Int32 nPtPos
= GetPoint()->GetContentIndex();
1309 HideWrapper
w(pLayout
, pTextNd
, nPtPos
);
1311 w
.m_nPtIndex
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
1312 *w
.m_pText
, w
.m_nPtIndex
,
1313 g_pBreakIt
->GetLocale( pTextNd
->GetLang( nPtPos
) ),
1318 if (nPtPos
< pTextNd
->GetText().getLength() && nPtPos
>= 0)
1320 GetPoint()->Assign(*pTextNd
, nPtPos
);
1328 bool SwCursor::GoEndWordWT(sal_Int16 nWordType
, SwRootFrame
const*const pLayout
)
1331 SwTextNode
* pTextNd
= GetPointNode().GetTextNode();
1334 SwCursorSaveState
aSave( *this );
1335 sal_Int32 nPtPos
= GetPoint()->GetContentIndex();
1338 HideWrapper
w(pLayout
, pTextNd
, nPtPos
);
1340 w
.m_nPtIndex
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
1341 *w
.m_pText
, w
.m_nPtIndex
,
1342 g_pBreakIt
->GetLocale( pTextNd
->GetLang( nPtPos
) ),
1347 if (nPtPos
<= pTextNd
->GetText().getLength() && nPtPos
>= 0 &&
1348 GetPoint()->GetContentIndex() != nPtPos
)
1350 GetPoint()->Assign(*pTextNd
, nPtPos
);
1358 bool SwCursor::GoNextWordWT(sal_Int16 nWordType
, SwRootFrame
const*const pLayout
)
1361 SwTextNode
* pTextNd
= GetPointNode().GetTextNode();
1364 SwCursorSaveState
aSave( *this );
1365 sal_Int32 nPtPos
= GetPoint()->GetContentIndex();
1368 HideWrapper
w(pLayout
, pTextNd
, nPtPos
);
1370 w
.m_nPtIndex
= g_pBreakIt
->GetBreakIter()->nextWord(
1371 *w
.m_pText
, w
.m_nPtIndex
,
1372 g_pBreakIt
->GetLocale( pTextNd
->GetLang(nPtPos
, 1) ),
1373 nWordType
).startPos
;
1376 if (nPtPos
<= pTextNd
->GetText().getLength() && nPtPos
>= 0)
1378 GetPoint()->Assign(*pTextNd
, nPtPos
);
1386 bool SwCursor::GoPrevWordWT(sal_Int16 nWordType
, SwRootFrame
const*const pLayout
)
1389 SwTextNode
* pTextNd
= GetPointNode().GetTextNode();
1392 SwCursorSaveState
aSave( *this );
1393 sal_Int32 nPtPos
= GetPoint()->GetContentIndex();
1396 HideWrapper
w(pLayout
, pTextNd
, nPtPos
);
1398 const sal_Int32 nPtStart
= w
.m_nPtIndex
;
1402 w
.AssignBack(pTextNd
, nPtPos
);
1405 w
.m_nPtIndex
= g_pBreakIt
->GetBreakIter()->previousWord(
1406 *w
.m_pText
, nPtStart
,
1407 g_pBreakIt
->GetLocale( pTextNd
->GetLang(nPtPos
, 1) ),
1408 nWordType
).startPos
;
1411 if (nPtPos
< pTextNd
->GetText().getLength() && nPtPos
>= 0)
1413 GetPoint()->Assign(*pTextNd
, nPtPos
);
1421 bool SwCursor::SelectWordWT( SwViewShell
const * pViewShell
, sal_Int16 nWordType
, const Point
* pPt
)
1423 SwCursorSaveState
aSave( *this );
1427 const SwRootFrame
* pLayout
= pViewShell
->GetLayout();
1428 if( pPt
&& nullptr != pLayout
)
1430 // set the cursor to the layout position
1432 pLayout
->GetModelPositionForViewPoint( GetPoint(), aPt
);
1435 SwTextNode
* pTextNd
= GetPointNode().GetTextNode();
1438 // Should we select the whole fieldmark?
1439 const IDocumentMarkAccess
* pMarksAccess
= GetDoc().getIDocumentMarkAccess( );
1440 sw::mark::IFieldmark
const*const pMark(pMarksAccess
->getInnerFieldmarkFor(*GetPoint()));
1441 if (pMark
&& (IDocumentMarkAccess::GetType(*pMark
) == IDocumentMarkAccess::MarkType::TEXT_FIELDMARK
1442 || IDocumentMarkAccess::GetType(*pMark
) == IDocumentMarkAccess::MarkType::DATE_FIELDMARK
))
1444 *GetPoint() = sw::mark::FindFieldSep(*pMark
);
1445 GetPoint()->AdjustContent(+1); // Don't select the separator
1447 const SwPosition
& rEnd
= pMark
->GetMarkEnd();
1449 assert(pMark
->GetMarkEnd() != *GetPoint());
1452 GetMark()->AdjustContent(-1); // Don't select the end delimiter
1458 bool bForward
= true;
1459 sal_Int32 nPtPos
= GetPoint()->GetContentIndex();
1461 HideWrapper
w(pViewShell
->GetLayout(), pTextNd
, nPtPos
);
1463 Boundary
aBndry( g_pBreakIt
->GetBreakIter()->getWordBoundary(
1464 *w
.m_pText
, w
.m_nPtIndex
,
1465 g_pBreakIt
->GetLocale( pTextNd
->GetLang( nPtPos
) ),
1469 if (comphelper::LibreOfficeKit::isActive() && aBndry
.startPos
== aBndry
.endPos
&& w
.m_nPtIndex
> 0)
1471 // nPtPos is the end of the paragraph, select the last word then.
1473 w
.AssignBack(pTextNd
, nPtPos
);
1475 aBndry
= g_pBreakIt
->GetBreakIter()->getWordBoundary(
1476 *w
.m_pText
, w
.m_nPtIndex
,
1477 g_pBreakIt
->GetLocale( pTextNd
->GetLang( nPtPos
) ),
1483 SwTextNode
* pStartNode(pTextNd
);
1484 sal_Int32 nStartIndex
;
1485 w
.m_nPtIndex
= aBndry
.startPos
;
1486 w
.AssignBack(pStartNode
, nStartIndex
);
1488 SwTextNode
* pEndNode(pTextNd
);
1489 sal_Int32 nEndIndex
;
1490 w
.m_nPtIndex
= aBndry
.endPos
;
1491 w
.AssignBack(pEndNode
, nEndIndex
);
1493 if( aBndry
.startPos
!= aBndry
.endPos
)
1495 GetPoint()->Assign(*pEndNode
, nEndIndex
);
1499 GetMark()->Assign(*pStartNode
, nStartIndex
);
1500 if (sw::mark::IMark
* pAnnotationMark
= pMarksAccess
->getAnnotationMarkFor(*GetPoint()))
1502 // An annotation mark covers the selected word. Check
1503 // if it covers only the word: in that case we select
1504 // the comment anchor as well.
1505 bool bStartMatch
= GetMark()->GetNode() == pAnnotationMark
->GetMarkStart().GetNode() &&
1506 GetMark()->GetContentIndex() == pAnnotationMark
->GetMarkStart().GetContentIndex();
1507 bool bEndMatch
= GetPoint()->GetNode() == pAnnotationMark
->GetMarkEnd().GetNode() &&
1508 GetPoint()->GetContentIndex() + 1 == pAnnotationMark
->GetMarkEnd().GetContentIndex();
1509 if (bStartMatch
&& bEndMatch
)
1510 GetPoint()->AdjustContent(+1);
1527 static OUString
lcl_MaskDeletedRedlines( const SwTextNode
* pTextNd
)
1532 //mask deleted redlines
1533 OUString
sNodeText(pTextNd
->GetText());
1534 const SwDoc
& rDoc
= pTextNd
->GetDoc();
1535 const bool bShowChg
= IDocumentRedlineAccess::IsShowChanges( rDoc
.getIDocumentRedlineAccess().GetRedlineFlags() );
1538 SwRedlineTable::size_type nAct
= rDoc
.getIDocumentRedlineAccess().GetRedlinePos( *pTextNd
, RedlineType::Any
);
1539 for ( ; nAct
< rDoc
.getIDocumentRedlineAccess().GetRedlineTable().size(); nAct
++ )
1541 const SwRangeRedline
* pRed
= rDoc
.getIDocumentRedlineAccess().GetRedlineTable()[ nAct
];
1542 if ( pRed
->Start()->GetNode() > *pTextNd
)
1545 if( RedlineType::Delete
== pRed
->GetType() )
1547 sal_Int32 nStart
, nEnd
;
1548 pRed
->CalcStartEnd( pTextNd
->GetIndex(), nStart
, nEnd
);
1550 while ( nStart
< nEnd
&& nStart
< sNodeText
.getLength() )
1551 sNodeText
= sNodeText
.replaceAt( nStart
++, 1, rtl::OUStringChar(CH_TXTATR_INWORD
) );
1560 bool SwCursor::GoSentence(SentenceMoveType eMoveType
, SwRootFrame
const*const pLayout
)
1563 SwTextNode
* pTextNd
= GetPointNode().GetTextNode();
1566 OUString
const sNodeText(lcl_MaskDeletedRedlines(pTextNd
));
1568 SwCursorSaveState
aSave( *this );
1569 sal_Int32 nPtPos
= GetPoint()->GetContentIndex();
1572 HideWrapper
w(pLayout
, pTextNd
, nPtPos
, &sNodeText
);
1574 switch ( eMoveType
)
1576 case START_SENT
: /* when modifying: see also ExpandToSentenceBorders below! */
1577 w
.m_nPtIndex
= g_pBreakIt
->GetBreakIter()->beginOfSentence(
1578 *w
.m_pText
, w
.m_nPtIndex
,
1579 g_pBreakIt
->GetLocale(pTextNd
->GetLang(nPtPos
)));
1581 case END_SENT
: /* when modifying: see also ExpandToSentenceBorders below! */
1582 w
.m_nPtIndex
= g_pBreakIt
->GetBreakIter()->endOfSentence(
1583 *w
.m_pText
, w
.m_nPtIndex
,
1584 g_pBreakIt
->GetLocale(pTextNd
->GetLang(nPtPos
)));
1588 w
.m_nPtIndex
= g_pBreakIt
->GetBreakIter()->endOfSentence(
1589 *w
.m_pText
, w
.m_nPtIndex
,
1590 g_pBreakIt
->GetLocale(pTextNd
->GetLang(nPtPos
)));
1591 if (w
.m_nPtIndex
>= 0 && w
.m_nPtIndex
< w
.m_pText
->getLength())
1597 while (w
.m_nPtIndex
< w
.m_pText
->getLength()
1598 && (*w
.m_pText
)[w
.m_nPtIndex
] == ' ');
1603 w
.m_nPtIndex
= g_pBreakIt
->GetBreakIter()->beginOfSentence(
1604 *w
.m_pText
, w
.m_nPtIndex
,
1605 g_pBreakIt
->GetLocale(pTextNd
->GetLang(nPtPos
)));
1607 if (w
.m_nPtIndex
== 0)
1608 return false; // the previous sentence is not in this paragraph
1609 if (w
.m_nPtIndex
> 0)
1611 w
.m_nPtIndex
= g_pBreakIt
->GetBreakIter()->beginOfSentence(
1612 *w
.m_pText
, w
.m_nPtIndex
- 1,
1613 g_pBreakIt
->GetLocale(pTextNd
->GetLang(nPtPos
)));
1619 // it is allowed to place the PaM just behind the last
1620 // character in the text thus <= ...Len
1621 if (nPtPos
<= pTextNd
->GetText().getLength() && nPtPos
>= 0)
1623 GetPoint()->Assign(*pTextNd
, nPtPos
);
1631 void SwCursor::ExpandToSentenceBorders(SwRootFrame
const*const pLayout
)
1633 SwTextNode
* pStartNd
= Start()->GetNode().GetTextNode();
1634 SwTextNode
* pEndNd
= End()->GetNode().GetTextNode();
1635 if (!pStartNd
|| !pEndNd
)
1641 OUString
sStartText( lcl_MaskDeletedRedlines( pStartNd
) );
1642 OUString
sEndText( pStartNd
== pEndNd
? sStartText
: lcl_MaskDeletedRedlines( pEndNd
) );
1644 SwCursorSaveState
aSave( *this );
1645 sal_Int32 nStartPos
= Start()->GetContentIndex();
1646 sal_Int32 nEndPos
= End()->GetContentIndex();
1649 HideWrapper
w(pLayout
, pStartNd
, nStartPos
, &sStartText
);
1651 w
.m_nPtIndex
= g_pBreakIt
->GetBreakIter()->beginOfSentence(
1652 *w
.m_pText
, w
.m_nPtIndex
,
1653 g_pBreakIt
->GetLocale( pStartNd
->GetLang( nStartPos
) ) );
1656 HideWrapper
w(pLayout
, pEndNd
, nEndPos
, &sEndText
);
1658 w
.m_nPtIndex
= g_pBreakIt
->GetBreakIter()->endOfSentence(
1659 *w
.m_pText
, w
.m_nPtIndex
,
1660 g_pBreakIt
->GetLocale( pEndNd
->GetLang( nEndPos
) ) );
1663 // it is allowed to place the PaM just behind the last
1664 // character in the text thus <= ...Len
1665 if (nStartPos
<= pStartNd
->GetText().getLength() && nStartPos
>= 0)
1667 GetMark()->Assign(*pStartNd
, nStartPos
);
1669 if (nEndPos
<= pEndNd
->GetText().getLength() && nEndPos
>= 0)
1671 GetPoint()->Assign(*pEndNd
, nEndPos
);
1675 bool SwTableCursor::LeftRight( bool bLeft
, sal_uInt16 nCnt
, SwCursorSkipMode
/*nMode*/,
1676 bool /*bVisualAllowed*/, bool /*bSkipHidden*/, bool /*bInsertCursor*/,
1677 SwRootFrame
const*, bool /*isFieldNames*/)
1679 return bLeft
? GoPrevCell( nCnt
)
1680 : GoNextCell( nCnt
);
1683 // calculate cursor bidi level: extracted from LeftRight()
1684 const SwContentFrame
*
1685 SwCursor::DoSetBidiLevelLeftRight(
1686 bool & io_rbLeft
, bool bVisualAllowed
, bool bInsertCursor
)
1688 // calculate cursor bidi level
1689 const SwContentFrame
* pSttFrame
= nullptr;
1690 SwNode
& rNode
= GetPoint()->GetNode();
1692 if( rNode
.IsTextNode() )
1694 const SwTextNode
& rTNd
= *rNode
.GetTextNode();
1695 sal_Int32 nPos
= GetPoint()->GetContentIndex();
1697 if ( bVisualAllowed
&& SvtCTLOptions::IsCTLFontEnabled() &&
1698 SvtCTLOptions::MOVEMENT_VISUAL
== SvtCTLOptions::GetCTLCursorMovement() )
1700 // for visual cursor travelling (used in bidi layout)
1701 // we first have to convert the logic to a visual position
1703 std::pair
<Point
, bool> const tmp(aPt
, true);
1704 pSttFrame
= rTNd
.getLayoutFrame(
1705 GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
1709 sal_uInt8 nCursorLevel
= GetCursorBidiLevel();
1710 bool bForward
= ! io_rbLeft
;
1711 SwTextFrame
*const pTF(const_cast<SwTextFrame
*>(
1712 static_cast<const SwTextFrame
*>(pSttFrame
)));
1713 TextFrameIndex
nTFIndex(pTF
->MapModelToViewPos(*GetPoint()));
1714 pTF
->PrepareVisualMove( nTFIndex
, nCursorLevel
,
1715 bForward
, bInsertCursor
);
1716 *GetPoint() = pTF
->MapViewToModelPos(nTFIndex
);
1717 SetCursorBidiLevel( nCursorLevel
);
1718 io_rbLeft
= ! bForward
;
1723 SwTextFrame
const* pFrame
;
1724 const SwScriptInfo
* pSI
= SwScriptInfo::GetScriptInfo(rTNd
, &pFrame
);
1727 const sal_Int32 nMoveOverPos
= io_rbLeft
?
1728 ( nPos
? nPos
- 1 : 0 ) :
1730 TextFrameIndex
nIndex(pFrame
->MapModelToView(&rTNd
, nMoveOverPos
));
1731 SetCursorBidiLevel( pSI
->DirType(nIndex
) );
1738 bool SwCursor::LeftRight( bool bLeft
, sal_uInt16 nCnt
, SwCursorSkipMode nMode
,
1739 bool bVisualAllowed
,bool bSkipHidden
, bool bInsertCursor
,
1740 SwRootFrame
const*const pLayout
, bool isFieldNames
)
1742 // calculate cursor bidi level
1743 SwNode
& rNode
= GetPoint()->GetNode();
1744 const SwContentFrame
* pSttFrame
= // may side-effect bLeft!
1745 DoSetBidiLevelLeftRight(bLeft
, bVisualAllowed
, bInsertCursor
);
1747 // can the cursor be moved n times?
1748 SwCursorSaveState
aSave( *this );
1749 SwMoveFnCollection
const & fnMove
= bLeft
? fnMoveBackward
: fnMoveForward
;
1753 fnGo
= SwCursorSkipMode::Cells
== nMode
? GoInContentCellsSkipHidden
: GoInContentSkipHidden
;
1755 fnGo
= SwCursorSkipMode::Cells
== nMode
? GoInContentCells
: GoInContent
;
1757 SwTextFrame
const* pFrame(nullptr);
1760 pFrame
= static_cast<SwTextFrame
*>(rNode
.GetContentNode()->getLayoutFrame(pLayout
));
1763 while (pFrame
->GetPrecede())
1765 pFrame
= static_cast<SwTextFrame
const*>(pFrame
->GetPrecede());
1772 SwNodeIndex
aOldNodeIdx( GetPoint()->GetNode() );
1774 TextFrameIndex
beforeIndex(-1);
1777 beforeIndex
= pFrame
->MapModelToViewPos(*GetPoint());
1780 if (!bLeft
&& pLayout
&& pLayout
->GetFieldmarkMode() == sw::FieldmarkMode::ShowResult
)
1782 SwTextNode
const*const pNode(GetPoint()->GetNode().GetTextNode());
1784 if (pNode
->Len() != GetPoint()->GetContentIndex()
1785 && pNode
->GetText()[GetPoint()->GetContentIndex()] == CH_TXT_ATR_FIELDSTART
)
1787 IDocumentMarkAccess
const& rIDMA(*GetDoc().getIDocumentMarkAccess());
1788 sw::mark::IFieldmark
const*const pMark(rIDMA
.getFieldmarkAt(*GetPoint()));
1790 *GetPoint() = sw::mark::FindFieldSep(*pMark
);
1794 if ( !Move( fnMove
, fnGo
) )
1796 const SwEditShell
* pSh
= GetDoc().GetEditShell();
1797 const SwViewOption
* pViewOptions
= pSh
? pSh
->GetViewOptions() : nullptr;
1798 if (pViewOptions
&& pViewOptions
->IsShowOutlineContentVisibilityButton())
1800 // Fixes crash that occurs in documents with outline content folded at the end of
1801 // the document. When the cursor is at the end of the visible document and
1802 // right arrow key is pressed Move fails after moving the cursor to the
1803 // end of the document model, which doesn't have a node frame and causes
1804 // weird numbers to be displayed in the statusbar page number count. Left
1805 // arrow, when in this state, causes a crash without RestoredSavePos() added here.
1813 SwTextFrame
const* pNewFrame(static_cast<SwTextFrame
const*>(
1814 GetPoint()->GetNode().GetContentNode()->getLayoutFrame(pLayout
)));
1817 while (pNewFrame
->GetPrecede())
1819 pNewFrame
= static_cast<SwTextFrame
const*>(pNewFrame
->GetPrecede());
1822 // sw_redlinehide: fully redline-deleted nodes don't have frames...
1823 if (pFrame
== pNewFrame
|| !pNewFrame
)
1825 if (!pNewFrame
|| beforeIndex
== pFrame
->MapModelToViewPos(*GetPoint()))
1827 continue; // moving inside delete redline, doesn't count...
1832 // assume iteration is stable & returns the same frame
1833 assert(!pFrame
->IsAnFollow(pNewFrame
) && !pNewFrame
->IsAnFollow(pFrame
));
1838 if (bLeft
&& pLayout
&& pLayout
->GetFieldmarkMode() == sw::FieldmarkMode::ShowCommand
)
1840 SwTextNode
const*const pNode(GetPoint()->GetNode().GetTextNode());
1842 if (pNode
->Len() != GetPoint()->GetContentIndex()
1843 && pNode
->GetText()[GetPoint()->GetContentIndex()] == CH_TXT_ATR_FIELDEND
)
1845 IDocumentMarkAccess
const& rIDMA(*GetDoc().getIDocumentMarkAccess());
1846 sw::mark::IFieldmark
const*const pMark(rIDMA
.getFieldmarkAt(*GetPoint()));
1848 *GetPoint() = sw::mark::FindFieldSep(*pMark
);
1854 SwTextNode
const*const pNode(GetPoint()->GetNode().GetTextNode());
1856 SwTextAttr
const*const pInputField(pNode
->GetTextAttrAt(
1857 GetPoint()->GetContentIndex(), RES_TXTATR_INPUTFIELD
, ::sw::GetTextAttrMode::Parent
));
1860 continue; // skip over input fields
1864 // If we were located inside a covered cell but our position has been
1865 // corrected, we check if the last move has moved the cursor to a
1866 // different table cell. In this case we set the cursor to the stored
1867 // covered position and redo the move:
1868 if (m_nRowSpanOffset
)
1870 const SwNode
* pOldTabBoxSttNode
= aOldNodeIdx
.GetNode().FindTableBoxStartNode();
1871 const SwTableNode
* pOldTabSttNode
= pOldTabBoxSttNode
? pOldTabBoxSttNode
->FindTableNode() : nullptr;
1872 const SwNode
* pNewTabBoxSttNode
= GetPoint()->GetNode().FindTableBoxStartNode();
1873 const SwTableNode
* pNewTabSttNode
= pNewTabBoxSttNode
? pNewTabBoxSttNode
->FindTableNode() : nullptr;
1875 const bool bCellChanged
= pOldTabSttNode
&& pNewTabSttNode
&&
1876 pOldTabSttNode
== pNewTabSttNode
&&
1877 pOldTabBoxSttNode
&& pNewTabBoxSttNode
&&
1878 pOldTabBoxSttNode
!= pNewTabBoxSttNode
;
1882 // Set cursor to start/end of covered cell:
1883 SwTableBox
* pTableBox
= pOldTabBoxSttNode
->GetTableBox();
1884 if ( pTableBox
&& pTableBox
->getRowSpan() > 1 )
1886 pTableBox
= & pTableBox
->FindEndOfRowSpan(
1887 pOldTabSttNode
->GetTable(),
1888 o3tl::narrowing
<sal_uInt16
>(pTableBox
->getRowSpan() + m_nRowSpanOffset
));
1889 SwPosition
& rPtPos
= *GetPoint();
1890 SwNodeIndex
aNewIdx( *pTableBox
->GetSttNd() );
1891 rPtPos
.Assign( aNewIdx
);
1893 GetDoc().GetNodes().GoNextSection( &rPtPos
, false, false );
1894 SwContentNode
* pContentNode
= GetPointContentNode();
1897 GetPoint()->SetContent( bLeft
? pContentNode
->Len() : 0 );
1900 if ( !Move( fnMove
, fnGo
) )
1904 m_nRowSpanOffset
= 0;
1908 // Check if I'm inside a covered cell. Correct cursor if necessary and
1909 // store covered cell:
1910 const SwNode
* pTableBoxStartNode
= GetPoint()->GetNode().FindTableBoxStartNode();
1911 if ( pTableBoxStartNode
)
1913 const SwTableBox
* pTableBox
= pTableBoxStartNode
->GetTableBox();
1914 if ( pTableBox
&& pTableBox
->getRowSpan() < 1 )
1916 // Store the row span offset:
1917 m_nRowSpanOffset
= pTableBox
->getRowSpan();
1919 // Move cursor to non-covered cell:
1920 const SwTableNode
* pTableNd
= pTableBoxStartNode
->FindTableNode();
1921 pTableBox
= & pTableBox
->FindStartOfRowSpan( pTableNd
->GetTable() );
1922 SwPosition
& rPtPos
= *GetPoint();
1923 SwNodeIndex
aNewIdx( *pTableBox
->GetSttNd() );
1924 rPtPos
.Assign( aNewIdx
);
1926 GetDoc().GetNodes().GoNextSection( &rPtPos
, false, false );
1927 SwContentNode
* pContentNode
= GetPointContentNode();
1930 GetPoint()->SetContent( bLeft
? pContentNode
->Len() : 0 );
1937 // here come some special rules for visual cursor travelling
1940 SwNode
& rTmpNode
= GetPoint()->GetNode();
1941 if ( &rTmpNode
!= &rNode
&& rTmpNode
.IsTextNode() )
1944 std::pair
<Point
, bool> const tmp(aPt
, true);
1945 const SwContentFrame
* pEndFrame
= rTmpNode
.GetTextNode()->getLayoutFrame(
1946 GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
1950 if ( ! pEndFrame
->IsRightToLeft() != ! pSttFrame
->IsRightToLeft() )
1953 pEndFrame
->RightMargin( this );
1955 pEndFrame
->LeftMargin( this );
1961 return 0 == nCnt
&& !IsInProtectTable( true ) &&
1962 !IsSelOvr( SwCursorSelOverFlags::Toggle
|
1963 SwCursorSelOverFlags::ChangePos
);
1966 // calculate cursor bidi level: extracted from UpDown()
1967 void SwCursor::DoSetBidiLevelUpDown()
1969 SwNode
& rNode
= GetPoint()->GetNode();
1970 if ( !rNode
.IsTextNode() )
1973 SwTextFrame
const* pFrame
;
1974 const SwScriptInfo
* pSI
=
1975 SwScriptInfo::GetScriptInfo( *rNode
.GetTextNode(), &pFrame
);
1979 const sal_Int32 nPos
= GetPoint()->GetContentIndex();
1981 if (!(nPos
&& nPos
< rNode
.GetTextNode()->GetText().getLength()))
1984 TextFrameIndex
const nIndex(pFrame
->MapModelToView(rNode
.GetTextNode(), nPos
));
1985 const sal_uInt8 nCurrLevel
= pSI
->DirType( nIndex
);
1986 const sal_uInt8 nPrevLevel
= pSI
->DirType( nIndex
- TextFrameIndex(1) );
1988 if ( nCurrLevel
% 2 != nPrevLevel
% 2 )
1990 // set cursor level to the lower of the two levels
1991 SetCursorBidiLevel( std::min( nCurrLevel
, nPrevLevel
) );
1994 SetCursorBidiLevel( nCurrLevel
);
1997 bool SwCursor::UpDown( bool bUp
, sal_uInt16 nCnt
,
1998 Point
const * pPt
, tools::Long nUpDownX
,
1999 SwRootFrame
& rLayout
)
2001 SwTableCursor
* pTableCursor
= dynamic_cast<SwTableCursor
*>(this);
2002 bool bAdjustTableCursor
= false;
2004 // If the point/mark of the table cursor in the same box then set cursor to
2005 // beginning of the box
2006 if( pTableCursor
&& GetPointNode().StartOfSectionNode() ==
2007 GetMarkNode().StartOfSectionNode() )
2009 if ( End() != GetPoint() )
2011 bAdjustTableCursor
= true;
2018 std::pair
<Point
, bool> const temp(aPt
, true);
2019 SwContentFrame
* pFrame
= GetPointContentNode()->getLayoutFrame(&rLayout
, GetPoint(), &temp
);
2023 SwCursorSaveState
aSave( *this );
2028 pFrame
->GetCharRect( aTmpRect
, *GetPoint() );
2029 aPt
= aTmpRect
.Pos();
2031 nUpDownX
= pFrame
->IsVertical() ?
2032 aPt
.getY() - pFrame
->getFrameArea().Top() :
2033 aPt
.getX() - pFrame
->getFrameArea().Left();
2036 // It is allowed to move footnotes in other footnotes but not sections
2037 const bool bChkRange
= !pFrame
->IsInFootnote() || HasMark();
2038 const SwPosition
aOldPos( *GetPoint() );
2039 const bool bInReadOnly
= IsReadOnlyAvailable();
2041 if ( bAdjustTableCursor
&& !bUp
)
2043 // Special case: We have a table cursor but the start box has more
2044 // than one paragraph. If we want to go down, we have to set the
2045 // point to the last frame in the table box. This is only necessary
2046 // if we do not already have a table selection
2047 const SwStartNode
* pTableNd
= GetPointNode().FindTableBoxStartNode();
2048 OSL_ENSURE( pTableNd
, "pTableCursor without SwTableNode?" );
2050 if ( pTableNd
) // safety first
2052 const SwNode
* pEndNd
= pTableNd
->EndOfSectionNode();
2053 GetPoint()->Assign( *pEndNd
);
2054 pTableCursor
->Move( fnMoveBackward
, GoInNode
);
2055 std::pair
<Point
, bool> const tmp(aPt
, true);
2056 pFrame
= GetPointContentNode()->getLayoutFrame(&rLayout
, GetPoint(), &tmp
);
2061 (bUp
? pFrame
->UnitUp( this, nUpDownX
, bInReadOnly
)
2062 : pFrame
->UnitDown( this, nUpDownX
, bInReadOnly
) ) &&
2063 CheckNodesRange( aOldPos
.GetNode(), GetPoint()->GetNode(), bChkRange
))
2065 std::pair
<Point
, bool> const tmp(aPt
, true);
2066 pFrame
= GetPointContentNode()->getLayoutFrame(&rLayout
, GetPoint(), &tmp
);
2070 // iterate over whole number of items?
2071 if( !nCnt
&& !IsSelOvr( SwCursorSelOverFlags::Toggle
|
2072 SwCursorSelOverFlags::ChangePos
) )
2076 // try to position the cursor at half of the char-rect's height
2077 DisableCallbackAction
a(rLayout
);
2078 std::pair
<Point
, bool> const tmp(aPt
, true);
2079 pFrame
= GetPointContentNode()->getLayoutFrame(&rLayout
, GetPoint(), &tmp
);
2080 SwCursorMoveState
eTmpState( CursorMoveState::UpDown
);
2081 eTmpState
.m_bSetInReadOnly
= bInReadOnly
;
2083 pFrame
->GetCharRect( aTmpRect
, *GetPoint(), &eTmpState
);
2084 if ( pFrame
->IsVertical() )
2086 aPt
.setX(aTmpRect
.Center().getX());
2087 pFrame
->Calc(rLayout
.GetCurrShell()->GetOut());
2088 aPt
.setY(pFrame
->getFrameArea().Top() + nUpDownX
);
2092 aPt
.setY(aTmpRect
.Center().getY());
2093 pFrame
->Calc(rLayout
.GetCurrShell()->GetOut());
2094 aPt
.setX(pFrame
->getFrameArea().Left() + nUpDownX
);
2096 pFrame
->GetModelPositionForViewPoint( GetPoint(), aPt
, &eTmpState
);
2098 bRet
= !IsSelOvr( SwCursorSelOverFlags::Toggle
| SwCursorSelOverFlags::ChangePos
);
2100 else if (!pFrame
->IsInFootnote()) // tdf#150457 Jump to the begin/end
2101 // of the first/last line only if the
2102 // cursor is not inside a footnote
2104 sal_Int32 nOffset
= 0;
2106 // Jump to beginning or end of line when the cursor at first or last line.
2109 SwTextNode
* pTextNd
= GetPoint()->GetNode().GetTextNode();
2111 nOffset
= pTextNd
->GetText().getLength();
2113 const SwPosition
aPos(*GetPointContentNode(), nOffset
);
2115 //if cursor has already been at start or end of file,
2116 //Update cursor to change nUpDownX.
2117 if ( aOldPos
.GetContentIndex() == nOffset
)
2119 if (SwEditShell
* pSh
= GetDoc().GetEditShell())
2120 pSh
->UpdateCursor();
2124 *GetPoint() = aPos
; // just give a new position
2130 *GetPoint() = aOldPos
;
2132 DoSetBidiLevelUpDown(); // calculate cursor bidi level
2137 bool SwCursor::LeftRightMargin(SwRootFrame
const& rLayout
, bool bLeft
, bool bAPI
)
2140 std::pair
<Point
, bool> const tmp(aPt
, true);
2141 SwContentFrame
const*const pFrame
= GetPointContentNode()->getLayoutFrame(
2142 &rLayout
, GetPoint(), &tmp
);
2144 // calculate cursor bidi level
2146 SetCursorBidiLevel( pFrame
->IsRightToLeft() ? 1 : 0 );
2148 SwCursorSaveState
aSave( *this );
2150 && (bLeft
? pFrame
->LeftMargin( this ) : pFrame
->RightMargin( this, bAPI
) )
2151 && !IsSelOvr( SwCursorSelOverFlags::Toggle
| SwCursorSelOverFlags::ChangePos
);
2154 bool SwCursor::IsAtLeftRightMargin(SwRootFrame
const& rLayout
, bool bLeft
, bool bAPI
) const
2158 std::pair
<Point
, bool> const tmp(aPt
, true);
2159 SwContentFrame
const*const pFrame
= GetPointContentNode()->getLayoutFrame(
2160 &rLayout
, GetPoint(), &tmp
);
2163 SwPaM
aPam( *GetPoint() );
2164 if( !bLeft
&& aPam
.GetPoint()->GetContentIndex() )
2165 aPam
.GetPoint()->AdjustContent(-1);
2166 bRet
= (bLeft
? pFrame
->LeftMargin( &aPam
)
2167 : pFrame
->RightMargin( &aPam
, bAPI
))
2168 && (!pFrame
->IsTextFrame()
2169 || static_cast<SwTextFrame
const*>(pFrame
)->MapModelToViewPos(*aPam
.GetPoint())
2170 == static_cast<SwTextFrame
const*>(pFrame
)->MapModelToViewPos(*GetPoint()));
2175 bool SwCursor::SttEndDoc( bool bStt
)
2177 SwCursorSaveState
aSave( *this );
2178 // Never jump over section boundaries during selection!
2179 // Can the cursor still moved on?
2180 SwMoveFnCollection
const & fnMove
= bStt
? fnMoveBackward
: fnMoveForward
;
2181 bool bRet
= (!HasMark() || !IsNoContent() ) &&
2182 Move( fnMove
, GoInDoc
) &&
2183 !IsInProtectTable( true ) &&
2184 !IsSelOvr( SwCursorSelOverFlags::Toggle
|
2185 SwCursorSelOverFlags::ChangePos
|
2186 SwCursorSelOverFlags::EnableRevDirection
);
2190 bool SwCursor::GoPrevNextCell( bool bNext
, sal_uInt16 nCnt
)
2192 const SwTableNode
* pTableNd
= GetPoint()->GetNode().FindTableNode();
2196 // If there is another EndNode in front of the cell's StartNode then there
2197 // exists a previous cell
2198 SwCursorSaveState
aSave( *this );
2199 SwPosition
& rPtPos
= *GetPoint();
2203 const SwNode
* pTableBoxStartNode
= rPtPos
.GetNode().FindTableBoxStartNode();
2204 const SwTableBox
* pTableBox
= pTableBoxStartNode
->GetTableBox();
2206 // Check if we have to move the cursor to a covered cell before
2208 if (m_nRowSpanOffset
)
2210 if ( pTableBox
&& pTableBox
->getRowSpan() > 1 )
2212 pTableBox
= & pTableBox
->FindEndOfRowSpan( pTableNd
->GetTable(),
2213 o3tl::narrowing
<sal_uInt16
>(pTableBox
->getRowSpan() + m_nRowSpanOffset
));
2214 rPtPos
.Assign( *pTableBox
->GetSttNd() );
2215 pTableBoxStartNode
= rPtPos
.GetNode().FindTableBoxStartNode();
2217 m_nRowSpanOffset
= 0;
2220 const SwNode
* pTmpNode
= bNext
?
2221 pTableBoxStartNode
->EndOfSectionNode() :
2224 SwNodeIndex
aCellIdx( *pTmpNode
, bNext
? 1 : -1 );
2225 if( (bNext
&& !aCellIdx
.GetNode().IsStartNode()) ||
2226 (!bNext
&& !aCellIdx
.GetNode().IsEndNode()) )
2230 rPtPos
.Assign( aCellIdx
);
2232 rPtPos
.Assign(*aCellIdx
.GetNode().StartOfSectionNode());
2234 pTableBoxStartNode
= rPtPos
.GetNode().FindTableBoxStartNode();
2235 pTableBox
= pTableBoxStartNode
->GetTableBox();
2236 if ( pTableBox
&& pTableBox
->getRowSpan() < 1 )
2238 m_nRowSpanOffset
= pTableBox
->getRowSpan();
2239 // move cursor to non-covered cell:
2240 pTableBox
= & pTableBox
->FindStartOfRowSpan( pTableNd
->GetTable() );
2241 rPtPos
.Assign( *pTableBox
->GetSttNd() );
2245 rPtPos
.Adjust(SwNodeOffset(1));
2246 if( !rPtPos
.GetNode().IsContentNode() )
2247 GetDoc().GetNodes().GoNextSection( &rPtPos
, true, false );
2248 GetPoint()->SetContent( 0 );
2250 return !IsInProtectTable( true );
2253 bool SwTableCursor::GotoTable( const OUString
& )
2255 return false; // invalid action
2258 bool SwCursor::GotoTable( const OUString
& rName
)
2263 SwTable
* pTmpTable
= SwTable::FindTable( GetDoc().FindTableFormatByName( rName
) );
2266 // a table in a normal nodes array
2267 SwCursorSaveState
aSave( *this );
2268 GetPoint()->Assign( *pTmpTable
->GetTabSortBoxes()[ 0 ]->
2269 GetSttNd()->FindTableNode() );
2270 Move( fnMoveForward
, GoInContent
);
2277 bool SwCursor::GotoTableBox( const OUString
& rName
)
2280 const SwTableNode
* pTableNd
= GetPoint()->GetNode().FindTableNode();
2283 // retrieve box by name
2284 const SwTableBox
* pTableBox
= pTableNd
->GetTable().GetTableBox( rName
);
2285 if( pTableBox
&& pTableBox
->GetSttNd() &&
2286 ( !pTableBox
->GetFrameFormat()->GetProtect().IsContentProtected() ||
2287 IsReadOnlyAvailable() ) )
2289 SwCursorSaveState
aSave( *this );
2290 GetPoint()->Assign( *pTableBox
->GetSttNd() );
2291 Move( fnMoveForward
, GoInContent
);
2298 bool SwCursor::MovePara(SwWhichPara fnWhichPara
, SwMoveFnCollection
const & fnPosPara
)
2300 // for optimization test something before
2301 const SwNode
* pNd
= &GetPoint()->GetNode();
2302 bool bShortCut
= false;
2303 if ( fnWhichPara
== GoCurrPara
)
2306 // If fnWhichPara == GoCurrPara then (*fnWhichPara)( *this, fnPosPara )
2307 // can already move the cursor to a different text node. In this case
2308 // we better check if IsSelOvr().
2309 const SwContentNode
* pContentNd
= pNd
->GetContentNode();
2312 const sal_Int32 nSttEnd
= &fnPosPara
== &fnMoveForward
? 0 : pContentNd
->Len();
2313 if ( GetPoint()->GetContentIndex() != nSttEnd
)
2319 if ( pNd
->IsTextNode() &&
2320 pNd
->GetNodes()[ pNd
->GetIndex() +
2321 SwNodeOffset(fnWhichPara
== GoNextPara
? 1 : -1 ) ]->IsTextNode() )
2326 return (*fnWhichPara
)( *this, fnPosPara
);
2328 // else we must use the SaveStructure, because the next/prev is not
2329 // a same node type.
2330 SwCursorSaveState
aSave( *this );
2331 return (*fnWhichPara
)( *this, fnPosPara
) &&
2332 !IsInProtectTable( true ) &&
2333 !IsSelOvr( SwCursorSelOverFlags::Toggle
|
2334 SwCursorSelOverFlags::ChangePos
);
2337 bool SwCursor::MoveSection( SwWhichSection fnWhichSect
,
2338 SwMoveFnCollection
const & fnPosSect
)
2340 SwCursorSaveState
aSave( *this );
2341 return (*fnWhichSect
)( *this, fnPosSect
) &&
2342 !IsInProtectTable( true ) &&
2343 !IsSelOvr( SwCursorSelOverFlags::Toggle
|
2344 SwCursorSelOverFlags::ChangePos
);
2347 void SwCursor::RestoreSavePos()
2349 // This method is not supposed to be used in cases when nodes may be
2350 // deleted; detect such cases, but do not crash (example: fdo#40831).
2351 SwNodeOffset
uNodeCount(GetPoint()->GetNodes().Count());
2352 OSL_ENSURE(m_vSavePos
.empty() || m_vSavePos
.back().nNode
< uNodeCount
,
2353 "SwCursor::RestoreSavePos: invalid node: "
2354 "probably something was deleted; consider using SwUnoCursor instead");
2355 if (m_vSavePos
.empty() || m_vSavePos
.back().nNode
>= uNodeCount
)
2358 GetPoint()->Assign( m_vSavePos
.back().nNode
);
2361 if ( GetPointContentNode() )
2363 if (m_vSavePos
.back().nContent
<= GetPointContentNode()->Len())
2364 nIdx
= m_vSavePos
.back().nContent
;
2367 nIdx
= GetPointContentNode()->Len();
2368 OSL_FAIL("SwCursor::RestoreSavePos: invalid content index");
2371 GetPoint()->SetContent( nIdx
);
2374 SwTableCursor::SwTableCursor( const SwPosition
&rPos
)
2375 : SwCursor( rPos
, nullptr )
2379 m_nTablePtNd
= SwNodeOffset(0);
2380 m_nTableMkNd
= SwNodeOffset(0);
2385 SwTableCursor::~SwTableCursor() {}
2388 lcl_SeekEntry(const SwSelBoxes
& rTmp
, SwStartNode
const*const pSrch
,
2391 SwNodeOffset nIdx
= pSrch
->GetIndex();
2393 size_t nO
= rTmp
.size();
2400 size_t nM
= nU
+ ( nO
- nU
) / 2;
2401 if( rTmp
[ nM
]->GetSttNd() == pSrch
)
2406 else if( rTmp
[ nM
]->GetSttIdx() < nIdx
)
2417 SwCursor
* SwTableCursor::MakeBoxSels( SwCursor
* pCurrentCursor
)
2423 // move back into content
2425 Move( fnMoveForward
);
2427 Move( fnMoveForward
);
2433 // create temporary copies so that all boxes that
2434 // have already cursors can be removed
2435 SwSelBoxes
aTmp(m_SelectedBoxes
);
2437 // compare old and new ones
2438 SwNodes
& rNds
= pCurrentCursor
->GetDoc().GetNodes();
2439 const SwStartNode
* pSttNd
;
2440 SwCursor
* pCur
= pCurrentCursor
;
2444 pSttNd
= pCur
->GetPoint()->GetNode().FindTableBoxStartNode();
2445 if( !pCur
->HasMark() || !pSttNd
||
2446 pSttNd
!= pCur
->GetMark()->GetNode().FindTableBoxStartNode() )
2449 else if( lcl_SeekEntry( aTmp
, pSttNd
, nPos
))
2451 SwNodeIndex
aIdx( *pSttNd
, 1 );
2452 const SwNode
* pNd
= &aIdx
.GetNode();
2453 if( !pNd
->IsContentNode() )
2454 pNd
= rNds
.GoNextSection( &aIdx
, true, false );
2456 SwPosition
* pPos
= pCur
->GetMark();
2457 if( pNd
!= &pPos
->GetNode() )
2458 pPos
->Assign( *pNd
);
2459 pPos
->SetContent( 0 );
2461 aIdx
.Assign( *pSttNd
->EndOfSectionNode(), - 1 );
2462 pNd
= &aIdx
.GetNode();
2463 if( !pNd
->IsContentNode() )
2464 pNd
= SwNodes::GoPrevSection( &aIdx
, true, false );
2466 pPos
= pCur
->GetPoint();
2467 if (pNd
&& pNd
!= &pPos
->GetNode())
2468 pPos
->Assign( *pNd
);
2469 pPos
->SetContent( pNd
? static_cast<const SwContentNode
*>(pNd
)->Len() : 0);
2471 aTmp
.erase( aTmp
.begin() + nPos
);
2476 pCur
= pCur
->GetNext();
2479 SwCursor
* pDel
= pCur
->GetPrev();
2480 if (pDel
== dynamic_cast<SwShellCursor
*>(pCurrentCursor
))
2481 pCurrentCursor
= pDel
->GetPrev();
2483 if( pDel
== pCurrentCursor
)
2484 pCurrentCursor
->DeleteMark();
2488 } while ( pCurrentCursor
!= pCur
);
2490 for (size_t nPos
= 0; nPos
< aTmp
.size(); ++nPos
)
2492 pSttNd
= aTmp
[ nPos
]->GetSttNd();
2494 SwNodeIndex
aIdx( *pSttNd
, 1 );
2495 if( &aIdx
.GetNodes() != &rNds
)
2497 SwNode
* pNd
= &aIdx
.GetNode();
2498 if( !pNd
->IsContentNode() )
2499 pNd
= rNds
.GoNextSection( &aIdx
, true, false );
2501 SwPaM
*const pNew
= (!pCurrentCursor
->IsMultiSelection() && !pCurrentCursor
->HasMark())
2503 : pCurrentCursor
->Create( pCurrentCursor
);
2504 pNew
->GetPoint()->Assign( *pNd
);
2507 SwPosition
* pPos
= pNew
->GetPoint();
2508 pPos
->Assign( *pSttNd
->EndOfSectionNode(), - 1 );
2509 pNd
= &pPos
->GetNode();
2510 if( !pNd
->IsContentNode() )
2511 pNd
= SwNodes::GoPrevSection( pPos
, true, false );
2513 pPos
->AssignEndIndex(*static_cast<SwContentNode
*>(pNd
));
2516 return pCurrentCursor
;
2519 void SwTableCursor::InsertBox( const SwTableBox
& rTableBox
)
2521 SwTableBox
* pBox
= const_cast<SwTableBox
*>(&rTableBox
);
2522 m_SelectedBoxes
.insert(pBox
);
2526 void SwTableCursor::DeleteBox(size_t const nPos
)
2528 m_SelectedBoxes
.erase(m_SelectedBoxes
.begin() + nPos
);
2532 bool SwTableCursor::NewTableSelection()
2535 const SwNode
*pStart
= GetPointNode().FindTableBoxStartNode();
2536 const SwNode
*pEnd
= GetMarkNode().FindTableBoxStartNode();
2537 if( pStart
&& pEnd
)
2539 const SwTableNode
*pTableNode
= pStart
->FindTableNode();
2540 if( pTableNode
== pEnd
->FindTableNode() &&
2541 pTableNode
->GetTable().IsNewModel() )
2544 SwSelBoxes
aNew(m_SelectedBoxes
);
2545 pTableNode
->GetTable().CreateSelection( pStart
, pEnd
, aNew
,
2546 SwTable::SEARCH_NONE
, false );
2547 ActualizeSelection( aNew
);
2553 void SwTableCursor::ActualizeSelection( const SwSelBoxes
&rNew
)
2555 size_t nOld
= 0, nNew
= 0;
2556 while (nOld
< m_SelectedBoxes
.size() && nNew
< rNew
.size())
2558 SwTableBox
const*const pPOld
= m_SelectedBoxes
[ nOld
];
2559 const SwTableBox
* pPNew
= rNew
[ nNew
];
2560 if( pPOld
== pPNew
)
2561 { // this box will stay
2565 else if( pPOld
->GetSttIdx() < pPNew
->GetSttIdx() )
2567 DeleteBox( nOld
); // this box has to go
2571 InsertBox( *pPNew
); // this is a new one
2577 while (nOld
< m_SelectedBoxes
.size())
2579 DeleteBox( nOld
); // some more to delete
2582 for ( ; nNew
< rNew
.size(); ++nNew
) // some more to insert
2584 InsertBox( *rNew
[ nNew
] );
2588 bool SwTableCursor::IsCursorMovedUpdate()
2590 if( !IsCursorMoved() )
2593 m_nTableMkNd
= GetMark()->GetNodeIndex();
2594 m_nTablePtNd
= GetPoint()->GetNodeIndex();
2595 m_nTableMkCnt
= GetMark()->GetContentIndex();
2596 m_nTablePtCnt
= GetPoint()->GetContentIndex();
2600 /// park table cursor on the boxes' start node
2601 void SwTableCursor::ParkCursor()
2603 // de-register index from text node
2604 SwNode
* pNd
= &GetPoint()->GetNode();
2605 if( !pNd
->IsStartNode() )
2606 pNd
= pNd
->StartOfSectionNode();
2607 GetPoint()->Assign(*pNd
);
2609 pNd
= &GetMark()->GetNode();
2610 if( !pNd
->IsStartNode() )
2611 pNd
= pNd
->StartOfSectionNode();
2612 GetMark()->Assign(*pNd
);
2618 bool SwTableCursor::HasReadOnlyBoxSel() const
2621 for (size_t n
= m_SelectedBoxes
.size(); n
; )
2623 if (m_SelectedBoxes
[--n
]->GetFrameFormat()->GetProtect().IsContentProtected())
2632 bool SwTableCursor::HasHiddenBoxSel() const
2635 for (size_t n
= m_SelectedBoxes
.size(); n
; )
2637 if (m_SelectedBoxes
[--n
]->GetFrameFormat()->IsHidden())
2646 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */