Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / crsr / swcrsr.cxx
blob8d0246bed14f1cca8dc14945eee4f4352e00b737
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
30 #include <swcrsr.hxx>
31 #include <unocrsr.hxx>
32 #include <bookmark.hxx>
33 #include <doc.hxx>
34 #include <IDocumentUndoRedo.hxx>
35 #include <IDocumentRedlineAccess.hxx>
36 #include <IDocumentLayoutAccess.hxx>
37 #include <docary.hxx>
38 #include <ndtxt.hxx>
39 #include <section.hxx>
40 #include <swtable.hxx>
41 #include <cntfrm.hxx>
42 #include <rootfrm.hxx>
43 #include <txtfrm.hxx>
44 #include <notxtfrm.hxx>
45 #include <scriptinfo.hxx>
46 #include <crstate.hxx>
47 #include <docsh.hxx>
48 #include <viewsh.hxx>
49 #include <frmatr.hxx>
50 #include <breakit.hxx>
51 #include <mdiexp.hxx>
52 #include <strings.hrc>
53 #include <redline.hxx>
54 #include <txatbase.hxx>
55 #include <IDocumentMarkAccess.hxx>
56 #include <memory>
57 #include <comphelper/lok.hxx>
58 #include <editsh.hxx>
60 #include <viewopt.hxx>
62 using namespace ::com::sun::star::i18n;
64 const sal_uInt16 coSrchRplcThreshold = 60000;
66 namespace {
68 struct PercentHdl
70 SwDocShell* pDSh;
71 sal_uLong nActPos;
72 bool bBack, bNodeIdx;
74 PercentHdl( sal_uLong nStt, sal_uLong nEnd, SwDocShell* pSh )
75 : pDSh(pSh), nActPos(nStt), bBack(false), bNodeIdx(false)
77 bBack = (nStt > nEnd);
78 if( bBack )
79 std::swap( nStt, nEnd );
80 ::StartProgress( STR_STATSTR_SEARCH, nStt, nEnd );
83 explicit PercentHdl( const SwPaM& rPam )
84 : pDSh( rPam.GetDoc().GetDocShell() )
86 sal_Int32 nStt, nEnd;
87 if( rPam.GetPoint()->GetNode() == rPam.GetMark()->GetNode() )
89 bNodeIdx = false;
90 nStt = rPam.GetMark()->GetContentIndex();
91 nEnd = rPam.GetPoint()->GetContentIndex();
93 else
95 bNodeIdx = true;
96 nStt = sal_Int32(rPam.GetMark()->GetNodeIndex());
97 nEnd = sal_Int32(rPam.GetPoint()->GetNodeIndex());
99 nActPos = nStt;
100 bBack = (nStt > nEnd );
101 if( bBack )
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
113 sal_Int32 nPos;
114 if( bNodeIdx )
115 nPos = sal_Int32(rPos.GetNodeIndex());
116 else
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
152 return false;
155 bool SwCursor::IsSkipOverHiddenSections() const
157 return true;
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)
189 return false;
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)
198 && HasMark() )
200 SwNodeIndex aOldPos( rNds, GetSavePos()->nNode );
201 if( !CheckNodesRange( aOldPos.GetNode(), GetPoint()->GetNode(), true ))
203 GetPoint()->Assign( aOldPos );
204 GetPoint()->SetContent( GetSavePos()->nContent );
205 return true;
208 return SwCursor::IsSelOvrCheck(eFlags);
211 namespace
213 const SwTextAttr* InputFieldAtPos(SwPosition const *pPos)
215 SwTextNode* pTextNd = pPos->GetNode().GetTextNode();
216 if (!pTextNd)
217 return nullptr;
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 ) )
232 return true;
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();
242 if( pSectNd &&
243 ((bSkipOverHiddenSections && pSectNd->GetSection().IsHiddenFlag() ) ||
244 (bSkipOverProtectSections && pSectNd->GetSection().IsProtectFlag() )))
246 if( !( SwCursorSelOverFlags::ChangePos & eFlags ) )
248 // then we're already done
249 RestoreSavePos();
250 return true;
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 ))
262 bGoNxt = !bGoNxt;
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();
274 if( !pCNd )
276 bIsValidPos = false;
277 nContentPos = 0;
278 rPtPos.Assign( aIdx );
279 pCNd = rPtPos.GetNode().GetContentNode();
280 if( !pCNd )
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 ) )
294 return 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() )
321 RestoreSavePos();
322 return true;
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() );
334 // ^ null
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();
343 for (;;)
345 pFrame = bGoNxt ? pFrame->FindNextCnt(true) : pFrame->FindPrevCnt();
346 if (!pFrame || 0 != pFrame->getFrameArea().Height() )
347 break;
350 // #i72394# skip to prev/next valid paragraph with a layout in case
351 // the first search did not succeed:
352 if( !pFrame )
354 bGoNxt = !bGoNxt;
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()));
370 else
372 assert(pFrame->IsNoTextFrame());
373 SwContentNode *const pCNd = const_cast<SwContentNode*>(
374 static_cast<SwNoTextFrame const*>(pFrame)->GetNode());
375 assert(pCNd);
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
390 pFrame = nullptr;
393 if ( IsInProtectTable( true ) )
395 // new position in protected table
396 // --> trigger restore of saved pos by setting <pFrame> to NULL - see below
397 pFrame = nullptr;
402 if( !pFrame )
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()))
410 DeleteMark();
411 RestoreSavePos();
412 return true; // we need a frame
417 // is the cursor allowed to be in a protected node?
418 if( !( SwCursorSelOverFlags::ChangePos & eFlags ) && !IsAtValidPos() )
420 DeleteMark();
421 RestoreSavePos();
422 return true;
425 if( !HasMark() )
426 return false;
428 // check for invalid sections
429 if( !::CheckNodesRange( GetMark()->GetNode(), GetPoint()->GetNode(), true ))
431 DeleteMark();
432 RestoreSavePos();
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) )
441 DeleteMark();
442 RestoreSavePos();
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 )
486 return false;
488 // in different tables or only mark in table
489 if( pMrkNd )
491 // not allowed, so go back to old position
492 RestoreSavePos();
493 // Cursor stays at old position
494 return true;
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;
509 if( bSelTop )
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() ) )
518 pMyNd = bSelTop
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)
524 break;
526 pPtNd = pMyNd->FindTableNode();
527 if( pPtNd )
528 continue;
531 // we permit these
532 if( pMyNd->IsContentNode() &&
533 ::CheckNodesRange( GetMark()->GetNode(),
534 GetPoint()->GetNode(), true ))
536 // table in table
537 const SwTableNode* pOuterTableNd = pMyNd->FindTableNode();
538 if ( pOuterTableNd )
539 pMyNd = pOuterTableNd;
540 else
542 SwContentNode* pCNd = const_cast<SwContentNode*>(static_cast<const SwContentNode*>(pMyNd));
543 GetPoint()->SetContent( bSelTop ? pCNd->Len() : 0 );
544 return false;
547 if( bSelTop )
549 if ( !pMyNd->IsEndNode() )
550 break;
551 pPtNd = pMyNd->FindTableNode();
553 else
554 pPtNd = pMyNd->GetTableNode();
555 if (!pPtNd)
556 break;
557 } while( true );
560 // stay on old position
561 RestoreSavePos();
562 return true;
565 bool SwCursor::IsInProtectTable( bool bMove, bool bChgCursor )
567 SwContentNode* pCNd = GetPointContentNode();
568 if( !pCNd )
569 return false;
571 // No table, no protected cell:
572 const SwTableNode* pTableNode = pCNd->FindTableNode();
573 if ( !pTableNode )
574 return false;
576 // Current position == last save position?
577 if (m_vSavePos.back().nNode == GetPoint()->GetNodeIndex())
578 return false;
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() )
593 return false;
595 // Cursor in protected cells allowed?
596 if ( IsReadOnlyAvailable() )
597 return false;
600 // If we reach this point, we are in a protected or covered table cell!
602 if( !bMove )
604 if( bChgCursor )
605 // restore the last save position
606 RestoreSavePos();
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 );
618 bool bProt = true;
619 GoNextCell:
620 for (;;) {
621 if( !aCellStt.GetNode().IsStartNode() )
622 break;
623 ++aCellStt;
624 pCNd = aCellStt.GetNode().GetContentNode();
625 if( !pCNd )
626 pCNd = aCellStt.GetNodes().GoNext( &aCellStt );
627 bProt = pCNd->IsProtect();
628 if( !bProt )
629 break;
630 aCellStt.Assign( *pCNd->FindTableBoxStartNode()->EndOfSectionNode(), 1 );
633 SetNextCursor:
634 if( !bProt ) // found free cell
636 GetPoint()->Assign( aCellStt );
637 SwContentNode* pTmpCNd = GetPointContentNode();
638 if( pTmpCNd )
640 GetPoint()->SetContent( 0 );
641 return false;
643 return IsSelOvr( SwCursorSelOverFlags::Toggle |
644 SwCursorSelOverFlags::ChangePos );
646 // end of table, so go to next node
647 ++aCellStt;
648 SwNode* pNd = &aCellStt.GetNode();
649 if( pNd->IsEndNode() || HasMark())
651 // if only table in FlyFrame or SSelection then stay on old position
652 if( bChgCursor )
653 RestoreSavePos();
654 return true;
656 else if( pNd->IsTableNode() )
658 ++aCellStt;
659 goto GoNextCell;
662 bProt = false; // index is now on a content node
663 goto SetNextCursor;
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 );
671 SwNode* pNd;
672 bool bProt = true;
673 GoPrevCell:
674 for (;;) {
675 pNd = &aCellStt.GetNode();
676 if( !pNd->IsEndNode() )
677 break;
678 aCellStt.Assign( *pNd->StartOfSectionNode(), +1 );
679 pCNd = aCellStt.GetNode().GetContentNode();
680 if( !pCNd )
681 pCNd = pNd->GetNodes().GoNext( &aCellStt );
682 bProt = pCNd->IsProtect();
683 if( !bProt )
684 break;
685 aCellStt.Assign( *pNd->FindTableBoxStartNode(), -1 );
688 SetPrevCursor:
689 if( !bProt ) // found free cell
691 GetPoint()->Assign( aCellStt );
692 SwContentNode* pTmpCNd = GetPointContentNode();
693 if( pTmpCNd )
695 GetPoint()->SetContent( 0 );
696 return false;
698 return IsSelOvr( SwCursorSelOverFlags::Toggle |
699 SwCursorSelOverFlags::ChangePos );
701 // at the beginning of a table, so go to next node
702 --aCellStt;
703 pNd = &aCellStt.GetNode();
704 if( pNd->IsStartNode() || HasMark() )
706 // if only table in FlyFrame or SSelection then stay on old position
707 if( bChgCursor )
708 RestoreSavePos();
709 return true;
711 else if( pNd->StartOfSectionNode()->IsTableNode() )
713 --aCellStt;
714 goto GoPrevCell;
717 bProt = false; // index is now on a content node
718 goto SetPrevCursor;
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) )
732 return false;
735 // #i45129# - in UI-ReadOnly everything is allowed
736 if( !rDoc.GetDocShell() || !rDoc.GetDocShell()->IsReadOnlyUI() )
737 return true;
739 const bool bCursorInReadOnly = IsReadOnlyAvailable();
740 if( !bCursorInReadOnly && pNd->IsProtect() )
741 return false;
743 const SwSectionNode* pSectNd = pNd->FindSectionNode();
744 return !pSectNd
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
755 pRange->SetMark();
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();
773 int nFndRet = 0;
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() ))
786 ++nCursorCnt;
787 if( nCursorCnt && !bIsUnoCursor )
788 pPHdl.reset(new PercentHdl( 0, nCursorCnt, rDoc.GetDocShell() ));
790 else
791 pSaveCursor = pSaveCursor->GetPrev();
793 bool bEnd = false;
794 do {
795 aRegion.SetMark();
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();
802 if( bSrchBkwrd )
803 aRegion.Exchange();
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);
812 if( 0 == nFndRet ||
813 ( pFndRing &&
814 *pFndRing->GetPoint() == *pCurrentCursor->GetPoint() &&
815 *pFndRing->GetMark() == *pCurrentCursor->GetMark() ))
816 break;
817 if( !( FIND_NO_RING & nFndRet ))
819 // #i24084# - create ring similar to the one in CreateCursor
820 SwCursor* pNew = pCurrentCursor->Create( pFndRing );
821 if( !pFndRing )
822 pFndRing = pNew;
824 pNew->SetMark();
825 *pNew->GetMark() = *pCurrentCursor->GetMark();
828 ++nFound;
830 if( !( eFndRngs & FindRanges::InSelAll) )
832 bEnd = true;
833 break;
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);
846 else
848 bEnd = true;
849 if(RET_CANCEL == nRet)
851 bCancel = true;
853 break;
857 if( bSrchBkwrd )
858 // move pEndPos in front of the found area
859 *pEndPos = *pCurrentCursor->Start();
860 else
861 // move pSttPos behind the found area
862 *pSttPos = *pCurrentCursor->End();
864 if( *pSttPos == *pEndPos )
865 // in area but at the end => done
866 break;
868 if( !nCursorCnt && pPHdl )
870 pPHdl->NextPos( *aRegion.GetMark() );
874 if( bEnd || !( eFndRngs & ( FindRanges::InSelAll | FindRanges::InSel )) )
875 break;
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);
889 return nFound;
892 static bool lcl_MakeSelFwrd( const SwNode& rSttNd, const SwNode& rEndNd,
893 SwPaM& rPam, bool bFirst )
895 if( rSttNd.GetIndex() + 1 == rEndNd.GetIndex() )
896 return false;
898 SwNodes& rNds = rPam.GetDoc().GetNodes();
899 rPam.DeleteMark();
900 SwContentNode* pCNd;
901 if( !bFirst )
903 rPam.GetPoint()->Assign(rSttNd);
904 pCNd = rNds.GoNext( rPam.GetPoint() );
905 if( !pCNd )
906 return false;
907 rPam.GetPoint()->AssignStartIndex(*pCNd);
909 else if( rSttNd.GetIndex() > rPam.GetPoint()->GetNodeIndex() ||
910 rPam.GetPoint()->GetNodeIndex() >= rEndNd.GetIndex() )
911 // not in this section
912 return false;
914 rPam.SetMark();
915 rPam.GetPoint()->Assign(rEndNd);
916 pCNd = SwNodes::GoPrevious( rPam.GetPoint() );
917 if( !pCNd )
918 return false;
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() )
928 return false;
930 SwNodes& rNds = rPam.GetDoc().GetNodes();
931 rPam.DeleteMark();
932 SwContentNode* pCNd;
933 if( !bFirst )
935 rPam.GetPoint()->Assign(rSttNd);
936 pCNd = SwNodes::GoPrevious( rPam.GetPoint() );
937 if( !pCNd )
938 return false;
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
945 rPam.SetMark();
946 rPam.GetPoint()->Assign(rEndNd);
947 pCNd = rNds.GoNext( rPam.GetPoint() );
948 if( !pCNd )
949 return false;
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 )
961 bCancel = false;
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
980 // stays unchanged)
981 nFound = lcl_FindSelection( rParas, this, fnMove,
982 pFndRing, aRegion, eFndRngs,
983 bInReadOnly, bCancel );
984 if( 0 == nFound )
985 return nFound;
987 // found string at least once; it's all in new Cursor ring thus delete old one
988 while( GetNext() != this )
989 delete GetNext();
991 *GetPoint() = *pFndRing->GetPoint();
992 SetMark();
993 *GetMark() = *pFndRing->GetMark();
994 pFndRing->GetRingContainer().merge( GetRingContainer() );
995 delete pFndRing;
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
1004 // 1. base section
1005 if( bMvBkwrd
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 );
1019 if( !nFound )
1021 // put back the old one
1022 *GetPoint() = *pSav->GetPoint();
1023 if( pSav->HasMark() )
1025 SetMark();
1026 *GetMark() = *pSav->GetMark();
1028 else
1029 DeleteMark();
1030 return 0;
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();
1039 SetMark();
1040 *GetMark() = *pFndRing->GetMark();
1042 else
1044 // found string at least once; it's all in new Cursor ring thus delete old one
1045 while( GetNext() != this )
1046 delete GetNext();
1048 *GetPoint() = *pFndRing->GetPoint();
1049 SetMark();
1050 *GetMark() = *pFndRing->GetMark();
1051 pFndRing->GetRingContainer().merge( GetRingContainer() );
1053 delete pFndRing;
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();
1063 if( bMvBkwrd
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 );
1071 if( !nFound )
1073 // put back the old one
1074 *GetPoint() = *pSav->GetPoint();
1075 if( pSav->HasMark() )
1077 SetMark();
1078 *GetMark() = *pSav->GetMark();
1080 else
1081 DeleteMark();
1082 return 0;
1084 while( GetNext() != this )
1085 delete GetNext();
1087 *GetPoint() = *pFndRing->GetPoint();
1088 SetMark();
1089 *GetMark() = *pFndRing->GetMark();
1090 pFndRing->GetRingContainer().merge( GetRingContainer() );
1091 delete pFndRing;
1093 else
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 ) )
1106 nFound = 0;
1107 return nFound;
1110 void SwCursor::FillFindPos( SwDocPositions ePos, SwPosition& rPos ) const
1112 bool bIsStart = true;
1113 SwContentNode* pCNd = nullptr;
1114 SwNodes& rNds = GetDoc().GetNodes();
1116 switch( ePos )
1118 case SwDocPositions::Start:
1119 rPos.Assign(*rNds.GetEndOfContent().StartOfSectionNode());
1120 pCNd = rNds.GoNext( &rPos );
1121 break;
1122 case SwDocPositions::End:
1123 rPos.Assign(rNds.GetEndOfContent());
1124 pCNd = SwNodes::GoPrevious( &rPos );
1125 bIsStart = false;
1126 break;
1127 case SwDocPositions::OtherStart:
1128 rPos.Assign( *rNds[ SwNodeOffset(0) ] );
1129 pCNd = rNds.GoNext( &rPos );
1130 break;
1131 case SwDocPositions::OtherEnd:
1132 rPos.Assign( *rNds.GetEndOfContent().StartOfSectionNode() );
1133 pCNd = SwNodes::GoPrevious( &rPos );
1134 bIsStart = false;
1135 break;
1136 default:
1137 rPos = *GetPoint();
1140 if( pCNd && !bIsStart )
1142 rPos.AssignEndIndex( *pCNd );
1146 short SwCursor::MaxReplaceArived()
1148 return RET_YES;
1151 namespace {
1153 struct HideWrapper
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)
1169 , m_pFrame(nullptr)
1170 , m_rpTextNode(rpTextNode)
1171 , m_rPtPos(rPtPos)
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));
1179 else
1181 if (!m_pText)
1183 m_pText = &rpTextNode->GetText();
1185 m_nPtIndex = rPtPos;
1188 ~HideWrapper()
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;
1201 else
1203 rPtPos = m_nPtIndex;
1208 } // namespace
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
1217 bool bRet = false;
1218 SwTextNode* pTextNd = GetPointNode().GetTextNode();
1219 if (pTextNd)
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 )),
1228 nWordType );
1230 return bRet;
1233 bool SwCursor::IsEndWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout) const
1235 bool bRet = false;
1236 SwTextNode* pTextNd = GetPointNode().GetTextNode();
1237 if (pTextNd)
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 ) ),
1246 nWordType );
1249 return bRet;
1252 bool SwCursor::IsInWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout) const
1254 bool bRet = false;
1255 SwTextNode* pTextNd = GetPointNode().GetTextNode();
1256 if (pTextNd)
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 ) ),
1266 nWordType,
1267 true );
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...
1274 if(bRet)
1276 const CharClass& rCC = GetAppCharClass();
1277 bRet = rCC.isLetterNumeric(pTextNd->GetText(), nPtPos);
1280 return bRet;
1283 bool SwCursor::IsStartEndSentence(bool bEnd, SwRootFrame const*const pLayout) const
1285 bool bRet = bEnd ?
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();
1296 return bRet;
1299 bool SwCursor::GoStartWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout)
1301 bool bRet = false;
1302 SwTextNode* pTextNd = GetPointNode().GetTextNode();
1303 if (pTextNd)
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 ) ),
1314 nWordType,
1315 false ).startPos;
1318 if (nPtPos < pTextNd->GetText().getLength() && nPtPos >= 0)
1320 GetPoint()->Assign(*pTextNd, nPtPos);
1321 if( !IsSelOvr() )
1322 bRet = true;
1325 return bRet;
1328 bool SwCursor::GoEndWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout)
1330 bool bRet = false;
1331 SwTextNode* pTextNd = GetPointNode().GetTextNode();
1332 if (pTextNd)
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 ) ),
1343 nWordType,
1344 true ).endPos;
1347 if (nPtPos <= pTextNd->GetText().getLength() && nPtPos >= 0 &&
1348 GetPoint()->GetContentIndex() != nPtPos )
1350 GetPoint()->Assign(*pTextNd, nPtPos);
1351 if( !IsSelOvr() )
1352 bRet = true;
1355 return bRet;
1358 bool SwCursor::GoNextWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout)
1360 bool bRet = false;
1361 SwTextNode* pTextNd = GetPointNode().GetTextNode();
1362 if (pTextNd)
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);
1379 if( !IsSelOvr() )
1380 bRet = true;
1383 return bRet;
1386 bool SwCursor::GoPrevWordWT(sal_Int16 nWordType, SwRootFrame const*const pLayout)
1388 bool bRet = false;
1389 SwTextNode* pTextNd = GetPointNode().GetTextNode();
1390 if (pTextNd)
1392 SwCursorSaveState aSave( *this );
1393 sal_Int32 nPtPos = GetPoint()->GetContentIndex();
1396 HideWrapper w(pLayout, pTextNd, nPtPos);
1398 const sal_Int32 nPtStart = w.m_nPtIndex;
1399 if (w.m_nPtIndex)
1401 --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);
1414 if( !IsSelOvr() )
1415 bRet = true;
1418 return bRet;
1421 bool SwCursor::SelectWordWT( SwViewShell const * pViewShell, sal_Int16 nWordType, const Point* pPt )
1423 SwCursorSaveState aSave( *this );
1425 bool bRet = false;
1426 DeleteMark();
1427 const SwRootFrame* pLayout = pViewShell->GetLayout();
1428 if( pPt && nullptr != pLayout )
1430 // set the cursor to the layout position
1431 Point aPt( *pPt );
1432 pLayout->GetModelPositionForViewPoint( GetPoint(), aPt );
1435 SwTextNode* pTextNd = GetPointNode().GetTextNode();
1436 if (pTextNd)
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());
1450 SetMark();
1451 *GetMark() = rEnd;
1452 GetMark()->AdjustContent(-1); // Don't select the end delimiter
1454 bRet = true;
1456 else
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 ) ),
1466 nWordType,
1467 bForward ));
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.
1472 --w.m_nPtIndex;
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 ) ),
1478 nWordType,
1479 bForward );
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);
1496 if( !IsSelOvr() )
1498 SetMark();
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);
1512 if( !IsSelOvr() )
1513 bRet = true;
1519 if( !bRet )
1521 DeleteMark();
1522 RestoreSavePos();
1524 return bRet;
1527 static OUString lcl_MaskDeletedRedlines( const SwTextNode* pTextNd )
1529 OUString aRes;
1530 if (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() );
1536 if ( bShowChg )
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 )
1543 break;
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) );
1555 aRes = sNodeText;
1557 return aRes;
1560 bool SwCursor::GoSentence(SentenceMoveType eMoveType, SwRootFrame const*const pLayout)
1562 bool bRet = false;
1563 SwTextNode* pTextNd = GetPointNode().GetTextNode();
1564 if (pTextNd)
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)));
1580 break;
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)));
1585 break;
1586 case NEXT_SENT:
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())
1595 ++w.m_nPtIndex;
1597 while (w.m_nPtIndex < w.m_pText->getLength()
1598 && (*w.m_pText)[w.m_nPtIndex] == ' ');
1600 break;
1602 case PREV_SENT:
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)));
1615 break;
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);
1624 if( !IsSelOvr() )
1625 bRet = true;
1628 return bRet;
1631 void SwCursor::ExpandToSentenceBorders(SwRootFrame const*const pLayout)
1633 SwTextNode* pStartNd = Start()->GetNode().GetTextNode();
1634 SwTextNode* pEndNd = End()->GetNode().GetTextNode();
1635 if (!pStartNd || !pEndNd)
1636 return;
1638 if (!HasMark())
1639 SetMark();
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
1702 Point aPt;
1703 std::pair<Point, bool> const tmp(aPt, true);
1704 pSttFrame = rTNd.getLayoutFrame(
1705 GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
1706 GetPoint(), &tmp);
1707 if( pSttFrame )
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;
1721 else
1723 SwTextFrame const* pFrame;
1724 const SwScriptInfo* pSI = SwScriptInfo::GetScriptInfo(rTNd, &pFrame);
1725 if ( pSI )
1727 const sal_Int32 nMoveOverPos = io_rbLeft ?
1728 ( nPos ? nPos - 1 : 0 ) :
1729 nPos;
1730 TextFrameIndex nIndex(pFrame->MapModelToView(&rTNd, nMoveOverPos));
1731 SetCursorBidiLevel( pSI->DirType(nIndex) );
1735 return pSttFrame;
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;
1751 SwGoInDoc fnGo;
1752 if ( bSkipHidden )
1753 fnGo = SwCursorSkipMode::Cells == nMode ? GoInContentCellsSkipHidden : GoInContentSkipHidden;
1754 else
1755 fnGo = SwCursorSkipMode::Cells == nMode ? GoInContentCells : GoInContent;
1757 SwTextFrame const* pFrame(nullptr);
1758 if (pLayout)
1760 pFrame = static_cast<SwTextFrame*>(rNode.GetContentNode()->getLayoutFrame(pLayout));
1761 if (pFrame)
1763 while (pFrame->GetPrecede())
1765 pFrame = static_cast<SwTextFrame const*>(pFrame->GetPrecede());
1770 while( nCnt )
1772 SwNodeIndex aOldNodeIdx( GetPoint()->GetNode() );
1774 TextFrameIndex beforeIndex(-1);
1775 if (pFrame)
1777 beforeIndex = pFrame->MapModelToViewPos(*GetPoint());
1780 if (!bLeft && pLayout && pLayout->GetFieldmarkMode() == sw::FieldmarkMode::ShowResult)
1782 SwTextNode const*const pNode(GetPoint()->GetNode().GetTextNode());
1783 assert(pNode);
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()));
1789 assert(pMark);
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.
1806 RestoreSavePos();
1808 break;
1811 if (pFrame)
1813 SwTextFrame const* pNewFrame(static_cast<SwTextFrame const*>(
1814 GetPoint()->GetNode().GetContentNode()->getLayoutFrame(pLayout)));
1815 if (pNewFrame)
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...
1830 else
1832 // assume iteration is stable & returns the same frame
1833 assert(!pFrame->IsAnFollow(pNewFrame) && !pNewFrame->IsAnFollow(pFrame));
1834 pFrame = pNewFrame;
1838 if (bLeft && pLayout && pLayout->GetFieldmarkMode() == sw::FieldmarkMode::ShowCommand)
1840 SwTextNode const*const pNode(GetPoint()->GetNode().GetTextNode());
1841 assert(pNode);
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()));
1847 assert(pMark);
1848 *GetPoint() = sw::mark::FindFieldSep(*pMark);
1852 if (isFieldNames)
1854 SwTextNode const*const pNode(GetPoint()->GetNode().GetTextNode());
1855 assert(pNode);
1856 SwTextAttr const*const pInputField(pNode->GetTextAttrAt(
1857 GetPoint()->GetContentIndex(), RES_TXTATR_INPUTFIELD, ::sw::GetTextAttrMode::Parent));
1858 if (pInputField)
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;
1880 if ( bCellChanged )
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();
1895 if ( pContentNode )
1897 GetPoint()->SetContent( bLeft ? pContentNode->Len() : 0 );
1899 // Redo the move:
1900 if ( !Move( fnMove, fnGo ) )
1901 break;
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();
1928 if ( pContentNode )
1930 GetPoint()->SetContent( bLeft ? pContentNode->Len() : 0 );
1934 --nCnt;
1937 // here come some special rules for visual cursor travelling
1938 if ( pSttFrame )
1940 SwNode& rTmpNode = GetPoint()->GetNode();
1941 if ( &rTmpNode != &rNode && rTmpNode.IsTextNode() )
1943 Point aPt;
1944 std::pair<Point, bool> const tmp(aPt, true);
1945 const SwContentFrame* pEndFrame = rTmpNode.GetTextNode()->getLayoutFrame(
1946 GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
1947 GetPoint(), &tmp);
1948 if ( pEndFrame )
1950 if ( ! pEndFrame->IsRightToLeft() != ! pSttFrame->IsRightToLeft() )
1952 if ( ! bLeft )
1953 pEndFrame->RightMargin( this );
1954 else
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() )
1971 return;
1973 SwTextFrame const* pFrame;
1974 const SwScriptInfo* pSI =
1975 SwScriptInfo::GetScriptInfo( *rNode.GetTextNode(), &pFrame );
1976 if ( !pSI )
1977 return;
1979 const sal_Int32 nPos = GetPoint()->GetContentIndex();
1981 if (!(nPos && nPos < rNode.GetTextNode()->GetText().getLength()))
1982 return;
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 ) );
1993 else
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() )
2010 Exchange();
2011 bAdjustTableCursor = true;
2014 bool bRet = false;
2015 Point aPt;
2016 if( pPt )
2017 aPt = *pPt;
2018 std::pair<Point, bool> const temp(aPt, true);
2019 SwContentFrame* pFrame = GetPointContentNode()->getLayoutFrame(&rLayout, GetPoint(), &temp);
2021 if( pFrame )
2023 SwCursorSaveState aSave( *this );
2025 if( !pPt )
2027 SwRect aTmpRect;
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);
2060 while( nCnt &&
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);
2067 --nCnt;
2070 // iterate over whole number of items?
2071 if( !nCnt && !IsSelOvr( SwCursorSelOverFlags::Toggle |
2072 SwCursorSelOverFlags::ChangePos ) )
2074 if( !pTableCursor )
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;
2082 SwRect aTmpRect;
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);
2090 else
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.
2107 if(!bUp)
2109 SwTextNode* pTextNd = GetPoint()->GetNode().GetTextNode();
2110 if (pTextNd)
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();
2121 bRet = false;
2123 else{
2124 *GetPoint() = aPos; // just give a new position
2125 bRet = true;
2129 else
2130 *GetPoint() = aOldPos;
2132 DoSetBidiLevelUpDown(); // calculate cursor bidi level
2134 return bRet;
2137 bool SwCursor::LeftRightMargin(SwRootFrame const& rLayout, bool bLeft, bool bAPI)
2139 Point aPt;
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
2145 if ( pFrame )
2146 SetCursorBidiLevel( pFrame->IsRightToLeft() ? 1 : 0 );
2148 SwCursorSaveState aSave( *this );
2149 return pFrame
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
2156 bool bRet = false;
2157 Point aPt;
2158 std::pair<Point, bool> const tmp(aPt, true);
2159 SwContentFrame const*const pFrame = GetPointContentNode()->getLayoutFrame(
2160 &rLayout, GetPoint(), &tmp);
2161 if( pFrame )
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()));
2172 return bRet;
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 );
2187 return bRet;
2190 bool SwCursor::GoPrevNextCell( bool bNext, sal_uInt16 nCnt )
2192 const SwTableNode* pTableNd = GetPoint()->GetNode().FindTableNode();
2193 if( !pTableNd )
2194 return false;
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();
2201 while( nCnt-- )
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
2207 // proceeding:
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() :
2222 pTableBoxStartNode;
2224 SwNodeIndex aCellIdx( *pTmpNode, bNext ? 1 : -1 );
2225 if( (bNext && !aCellIdx.GetNode().IsStartNode()) ||
2226 (!bNext && !aCellIdx.GetNode().IsEndNode()) )
2227 return false;
2229 if (bNext)
2230 rPtPos.Assign( aCellIdx );
2231 else
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 )
2260 bool bRet = false;
2261 if ( !HasMark() )
2263 SwTable* pTmpTable = SwTable::FindTable( GetDoc().FindTableFormatByName( rName ) );
2264 if( pTmpTable )
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 );
2271 bRet = !IsSelOvr();
2274 return bRet;
2277 bool SwCursor::GotoTableBox( const OUString& rName )
2279 bool bRet = false;
2280 const SwTableNode* pTableNd = GetPoint()->GetNode().FindTableNode();
2281 if( pTableNd )
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 );
2292 bRet = !IsSelOvr();
2295 return bRet;
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 )
2305 // #i41048#
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();
2310 if ( pContentNd )
2312 const sal_Int32 nSttEnd = &fnPosPara == &fnMoveForward ? 0 : pContentNd->Len();
2313 if ( GetPoint()->GetContentIndex() != nSttEnd )
2314 bShortCut = true;
2317 else
2319 if ( pNd->IsTextNode() &&
2320 pNd->GetNodes()[ pNd->GetIndex() +
2321 SwNodeOffset(fnWhichPara == GoNextPara ? 1 : -1 ) ]->IsTextNode() )
2322 bShortCut = true;
2325 if ( bShortCut )
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)
2356 return;
2358 GetPoint()->Assign( m_vSavePos.back().nNode );
2360 sal_Int32 nIdx = 0;
2361 if ( GetPointContentNode() )
2363 if (m_vSavePos.back().nContent <= GetPointContentNode()->Len())
2364 nIdx = m_vSavePos.back().nContent;
2365 else
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 )
2377 m_bParked = false;
2378 m_bChanged = false;
2379 m_nTablePtNd = SwNodeOffset(0);
2380 m_nTableMkNd = SwNodeOffset(0);
2381 m_nTablePtCnt = 0;
2382 m_nTableMkCnt = 0;
2385 SwTableCursor::~SwTableCursor() {}
2387 static bool
2388 lcl_SeekEntry(const SwSelBoxes& rTmp, SwStartNode const*const pSrch,
2389 size_t & o_rFndPos)
2391 SwNodeOffset nIdx = pSrch->GetIndex();
2393 size_t nO = rTmp.size();
2394 if( nO > 0 )
2396 nO--;
2397 size_t nU = 0;
2398 while( nU <= nO )
2400 size_t nM = nU + ( nO - nU ) / 2;
2401 if( rTmp[ nM ]->GetSttNd() == pSrch )
2403 o_rFndPos = nM;
2404 return true;
2406 else if( rTmp[ nM ]->GetSttIdx() < nIdx )
2407 nU = nM + 1;
2408 else if( nM == 0 )
2409 return false;
2410 else
2411 nO = nM - 1;
2414 return false;
2417 SwCursor* SwTableCursor::MakeBoxSels( SwCursor* pCurrentCursor )
2419 if (m_bChanged)
2421 if (m_bParked)
2423 // move back into content
2424 Exchange();
2425 Move( fnMoveForward );
2426 Exchange();
2427 Move( fnMoveForward );
2428 m_bParked = false;
2431 m_bChanged = false;
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;
2441 do {
2442 size_t nPos;
2443 bool bDel = false;
2444 pSttNd = pCur->GetPoint()->GetNode().FindTableBoxStartNode();
2445 if( !pCur->HasMark() || !pSttNd ||
2446 pSttNd != pCur->GetMark()->GetNode().FindTableBoxStartNode() )
2447 bDel = true;
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 );
2473 else
2474 bDel = true;
2476 pCur = pCur->GetNext();
2477 if( bDel )
2479 SwCursor* pDel = pCur->GetPrev();
2480 if (pDel == dynamic_cast<SwShellCursor*>(pCurrentCursor))
2481 pCurrentCursor = pDel->GetPrev();
2483 if( pDel == pCurrentCursor )
2484 pCurrentCursor->DeleteMark();
2485 else
2486 delete pDel;
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 )
2496 break;
2497 SwNode* pNd = &aIdx.GetNode();
2498 if( !pNd->IsContentNode() )
2499 pNd = rNds.GoNextSection( &aIdx, true, false );
2501 SwPaM *const pNew = (!pCurrentCursor->IsMultiSelection() && !pCurrentCursor->HasMark())
2502 ? pCurrentCursor
2503 : pCurrentCursor->Create( pCurrentCursor );
2504 pNew->GetPoint()->Assign( *pNd );
2505 pNew->SetMark();
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 );
2512 if (pNd)
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);
2523 m_bChanged = true;
2526 void SwTableCursor::DeleteBox(size_t const nPos)
2528 m_SelectedBoxes.erase(m_SelectedBoxes.begin() + nPos);
2529 m_bChanged = true;
2532 bool SwTableCursor::NewTableSelection()
2534 bool bRet = false;
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() )
2543 bRet = true;
2544 SwSelBoxes aNew(m_SelectedBoxes);
2545 pTableNode->GetTable().CreateSelection( pStart, pEnd, aNew,
2546 SwTable::SEARCH_NONE, false );
2547 ActualizeSelection( aNew );
2550 return bRet;
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
2562 ++nOld;
2563 ++nNew;
2565 else if( pPOld->GetSttIdx() < pPNew->GetSttIdx() )
2567 DeleteBox( nOld ); // this box has to go
2569 else
2571 InsertBox( *pPNew ); // this is a new one
2572 ++nOld;
2573 ++nNew;
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() )
2591 return false;
2593 m_nTableMkNd = GetMark()->GetNodeIndex();
2594 m_nTablePtNd = GetPoint()->GetNodeIndex();
2595 m_nTableMkCnt = GetMark()->GetContentIndex();
2596 m_nTablePtCnt = GetPoint()->GetContentIndex();
2597 return true;
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);
2614 m_bChanged = true;
2615 m_bParked = true;
2618 bool SwTableCursor::HasReadOnlyBoxSel() const
2620 bool bRet = false;
2621 for (size_t n = m_SelectedBoxes.size(); n; )
2623 if (m_SelectedBoxes[--n]->GetFrameFormat()->GetProtect().IsContentProtected())
2625 bRet = true;
2626 break;
2629 return bRet;
2632 bool SwTableCursor::HasHiddenBoxSel() const
2634 bool bRet = false;
2635 for (size_t n = m_SelectedBoxes.size(); n; )
2637 if (m_SelectedBoxes[--n]->GetFrameFormat()->IsHidden())
2639 bRet = true;
2640 break;
2643 return bRet;
2646 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */