Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / crsr / pam.cxx
blob25bf8d0ef68e26dd12ad65c861f22be9daaf0080
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 <sal/config.h>
22 #include <tools/gen.hxx>
23 #include <editeng/protitem.hxx>
24 #include <officecfg/Office/Common.hxx>
25 #include <unotools/configmgr.hxx>
27 #include <cntfrm.hxx>
28 #include <pagefrm.hxx>
29 #include <doc.hxx>
30 #include <IDocumentLayoutAccess.hxx>
31 #include <docary.hxx>
32 #include <pam.hxx>
33 #include <pamtyp.hxx>
34 #include <txtfrm.hxx>
35 #include <fmtcntnt.hxx>
36 #include <frmatr.hxx>
37 #include <flyfrm.hxx>
38 #include <fmteiro.hxx>
39 #include <section.hxx>
40 #include <sectfrm.hxx>
41 #include <ndtxt.hxx>
42 #include <swcrsr.hxx>
44 #include <IMark.hxx>
45 #include <DocumentSettingManager.hxx>
46 #include <hints.hxx>
47 #include <txatbase.hxx>
48 #include <osl/diagnose.h>
49 #include <utility>
50 #include <xmloff/odffields.hxx>
51 #include <rtl/ustrbuf.hxx>
53 #include <editsh.hxx>
54 #include <textcontentcontrol.hxx>
56 // for the dump "MSC-" compiler
57 static sal_Int32 GetSttOrEnd( bool bCondition, const SwContentNode& rNd )
59 return bCondition ? 0 : rNd.Len();
62 SwPosition::SwPosition( const SwNodeIndex & rNodeIndex, const SwContentIndex & rContent )
63 : nNode( rNodeIndex ), nContent( rContent )
65 assert((!rNodeIndex.GetNode().GetContentNode() || rNodeIndex.GetNode().GetContentNode() == rContent.GetContentNode())
66 && "parameters point to different nodes");
69 SwPosition::SwPosition( const SwNode & rNode, const SwContentIndex & rContent )
70 : nNode( rNode ), nContent( rContent )
72 assert((!rNode.GetContentNode() || rNode.GetContentNode() == rContent.GetContentNode())
73 && "parameters point to different nodes");
76 SwPosition::SwPosition( const SwNodeIndex & rNodeIndex, const SwContentNode* pContentNode, sal_Int32 nContentOffset )
77 : nNode( rNodeIndex ), nContent( pContentNode, nContentOffset )
79 assert((!pContentNode || pContentNode == &rNodeIndex.GetNode()) &&
80 "parameters point to different nodes");
83 SwPosition::SwPosition( const SwNode & rNode, const SwContentNode* pContentNode, sal_Int32 nContentOffset )
84 : nNode( rNode ), nContent( pContentNode, nContentOffset )
86 assert((!pContentNode || pContentNode == &rNode) &&
87 "parameters point to different nodes");
90 SwPosition::SwPosition( const SwNodeIndex & rNodeIndex, SwNodeOffset nDiff, const SwContentNode* pContentNode, sal_Int32 nContentOffset )
91 : nNode( rNodeIndex, nDiff ), nContent( pContentNode, nContentOffset )
93 assert((!pContentNode || pContentNode == &rNodeIndex.GetNode()) &&
94 "parameters point to different nodes");
97 SwPosition::SwPosition( const SwNodeIndex & rNodeIndex, SwNodeOffset nDiff )
98 : nNode( rNodeIndex, nDiff ), nContent( GetNode().GetContentNode() )
102 SwPosition::SwPosition( const SwNode& rNode, SwNodeOffset nDiff )
103 : nNode( rNode, nDiff ), nContent( GetNode().GetContentNode() )
107 SwPosition::SwPosition( SwNodes& rNodes, SwNodeOffset nIndex )
108 : nNode( rNodes, nIndex ), nContent( GetNode().GetContentNode() )
112 SwPosition::SwPosition( const SwContentNode & rNode, const sal_Int32 nContentOffset )
113 : nNode( rNode ), nContent( &rNode, nContentOffset )
117 SwPosition::SwPosition( const SwContentIndex & rContentIndex, short nDiff )
118 : nNode( *rContentIndex.GetContentNode() ), nContent( rContentIndex, nDiff )
122 bool SwPosition::operator<(const SwPosition &rPos) const
124 // cheaper to check for == first
125 if( nNode == rPos.nNode )
127 // note that positions with text node but no SwContentIndex registered are
128 // created for text frames anchored at para (see SwXFrame::getAnchor())
129 SwContentNode const*const pThisReg(GetContentNode());
130 SwContentNode const*const pOtherReg(rPos.GetContentNode());
131 if (pThisReg && pOtherReg)
133 return (nContent < rPos.nContent);
135 else // by convention position with no index is smaller
137 return pOtherReg != nullptr;
140 return nNode < rPos.nNode;
143 bool SwPosition::operator>(const SwPosition &rPos) const
145 // cheaper to check for == first
146 if( nNode == rPos.nNode )
148 // note that positions with text node but no SwContentIndex registered are
149 // created for text frames anchored at para (see SwXFrame::getAnchor())
150 SwContentNode const*const pThisReg(GetContentNode());
151 SwContentNode const*const pOtherReg(rPos.GetContentNode());
152 if (pThisReg && pOtherReg)
154 return (nContent > rPos.nContent);
156 else // by convention position with no index is smaller
158 return pThisReg != nullptr;
161 return nNode > rPos.nNode;
164 bool SwPosition::operator<=(const SwPosition &rPos) const
166 // cheaper to check for == first
167 if( nNode == rPos.nNode )
169 // note that positions with text node but no SwContentIndex registered are
170 // created for text frames anchored at para (see SwXFrame::getAnchor())
171 SwContentNode const*const pThisReg(GetContentNode());
172 SwContentNode const*const pOtherReg(rPos.GetContentNode());
173 if (pThisReg && pOtherReg)
175 return (nContent <= rPos.nContent);
177 else // by convention position with no index is smaller
179 return pThisReg == nullptr;
182 return nNode < rPos.nNode;
185 bool SwPosition::operator>=(const SwPosition &rPos) const
187 // cheaper to check for == first
188 if( nNode == rPos.nNode )
190 // note that positions with text node but no SwContentIndex registered are
191 // created for text frames anchored at para (see SwXFrame::getAnchor())
192 SwContentNode const*const pThisReg(GetContentNode());
193 SwContentNode const*const pOtherReg(rPos.GetContentNode());
194 if (pThisReg && pOtherReg)
196 return (nContent >= rPos.nContent);
198 else // by convention position with no index is smaller
200 return pOtherReg == nullptr;
203 return nNode > rPos.nNode;
206 bool SwPosition::operator==(const SwPosition &rPos) const
208 return (nNode == rPos.nNode)
209 && (nContent == rPos.nContent);
212 bool SwPosition::operator!=(const SwPosition &rPos) const
214 return (nNode != rPos.nNode)
215 || (nContent != rPos.nContent);
218 SwDoc& SwPosition::GetDoc() const
220 return GetNode().GetDoc();
223 void SwPosition::dumpAsXml(xmlTextWriterPtr pWriter) const
225 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwPosition"));
226 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nNode"), BAD_CAST(OString::number(sal_Int32(GetNodeIndex())).getStr()));
227 (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("nContent"), BAD_CAST(OString::number(GetContentIndex()).getStr()));
228 (void)xmlTextWriterEndElement(pWriter);
231 void SwPosition::Assign( const SwNode& rNd, SwNodeOffset nDelta, sal_Int32 nContentOffset )
233 nNode.Assign(rNd, nDelta);
234 assert((nNode.GetNode().GetContentNode() || nContentOffset == 0) && "setting contentoffset, but node is not SwContentNode");
235 nContent.Assign(nNode.GetNode().GetContentNode(), nContentOffset);
237 void SwPosition::Assign( SwNodeOffset nNodeOffset, sal_Int32 nContentOffset )
239 nNode = nNodeOffset;
240 nContent.Assign(nNode.GetNode().GetContentNode(), nContentOffset);
242 void SwPosition::Assign( const SwContentNode& rNode, sal_Int32 nContentOffset )
244 nNode = rNode;
245 nContent.Assign(&rNode, nContentOffset);
247 void SwPosition::Assign( const SwNode& rNd, sal_Int32 nContentOffset )
249 nNode.Assign(rNd);
250 nContent.Assign(rNd.GetContentNode(), nContentOffset);
252 void SwPosition::Assign( const SwNodeIndex& rNdIdx, sal_Int32 nContentOffset )
254 nNode = rNdIdx;
255 nContent.Assign(nNode.GetNode().GetContentNode(), nContentOffset);
257 void SwPosition::Adjust( SwNodeOffset nDelta )
259 nNode += nDelta;
260 nContent.Assign(nNode.GetNode().GetContentNode(), 0);
262 void SwPosition::AdjustContent( sal_Int32 nDelta )
264 assert(nNode.GetNode().GetContentNode() && "only valid to call this if we point to an SwContentNode");
265 nContent += nDelta;
267 void SwPosition::SetContent( sal_Int32 nContentIndex )
269 assert(nNode.GetNode().GetContentNode() && "only valid to call this if we point to an SwContentNode");
270 nContent = nContentIndex;
272 void SwPosition::AssignStartIndex( const SwContentNode& rNd )
274 nNode = rNd;
275 nContent.Assign(&rNd, 0);
277 void SwPosition::AssignEndIndex( const SwContentNode& rNd )
279 nNode = rNd;
280 nContent.Assign(&rNd, rNd.Len());
284 std::ostream &operator <<(std::ostream& s, const SwPosition& position)
286 return s << "SwPosition (node " << position.GetNodeIndex() << ", offset " << position.GetContentIndex() << ")";
289 namespace {
291 enum CHKSECTION { Chk_Both, Chk_One, Chk_None };
295 static CHKSECTION lcl_TstIdx( SwNodeOffset nSttIdx, SwNodeOffset nEndIdx, const SwNode& rEndNd )
297 SwNodeOffset nStt = rEndNd.StartOfSectionIndex(), nEnd = rEndNd.GetIndex();
298 CHKSECTION eSec = nStt < nSttIdx && nEnd >= nSttIdx ? Chk_One : Chk_None;
299 if( nStt < nEndIdx && nEnd >= nEndIdx )
300 return( eSec == Chk_One ? Chk_Both : Chk_One );
301 return eSec;
304 static bool lcl_ChkOneRange( CHKSECTION eSec, bool bChkSections,
305 const SwNode& rBaseEnd, SwNodeOffset nStt, SwNodeOffset nEnd )
307 if( eSec != Chk_Both )
308 return false;
310 if( !bChkSections )
311 return true;
313 // search the surrounding section
314 const SwNodes& rNds = rBaseEnd.GetNodes();
315 const SwNode *pTmp, *pNd = rNds[ nStt ];
316 if( !pNd->IsStartNode() )
317 pNd = pNd->StartOfSectionNode();
319 if( pNd == rNds[ nEnd ]->StartOfSectionNode() )
320 return true; // same StartNode, same section
322 // already on a base node => error
323 if( !pNd->StartOfSectionIndex() )
324 return false;
326 for (;;)
328 pTmp = pNd->StartOfSectionNode();
329 if (pTmp->EndOfSectionNode() == &rBaseEnd )
330 break;
331 pNd = pTmp;
334 SwNodeOffset nSttIdx = pNd->GetIndex(), nEndIdx = pNd->EndOfSectionIndex();
335 return nSttIdx <= nStt && nStt <= nEndIdx &&
336 nSttIdx <= nEnd && nEnd <= nEndIdx;
339 /** Check if the given range is inside one of the defined top-level sections.
341 * The top-level sections are Content, AutoText, PostIts, Inserts, and Redlines.
343 * @param bChkSection if true, also check that the given range is inside
344 * a single second-level section inside any of the
345 * top-level sections, except for the Content section.
347 * @return <true> if valid range
349 bool CheckNodesRange( const SwNode& rStt,
350 const SwNode& rEnd, bool bChkSection )
352 const SwNodes& rNds = rStt.GetNodes();
353 SwNodeOffset nStt = rStt.GetIndex(), nEnd = rEnd.GetIndex();
354 CHKSECTION eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfContent() );
355 if( Chk_None != eSec )
356 return eSec == Chk_Both;
358 eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfAutotext() );
359 if( Chk_None != eSec )
360 return lcl_ChkOneRange( eSec, bChkSection,
361 rNds.GetEndOfAutotext(), nStt, nEnd );
363 eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfPostIts() );
364 if( Chk_None != eSec )
365 return lcl_ChkOneRange( eSec, bChkSection,
366 rNds.GetEndOfPostIts(), nStt, nEnd );
368 eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfInserts() );
369 if( Chk_None != eSec )
370 return lcl_ChkOneRange( eSec, bChkSection,
371 rNds.GetEndOfInserts(), nStt, nEnd );
373 eSec = lcl_TstIdx( nStt, nEnd, rNds.GetEndOfRedlines() );
374 if( Chk_None != eSec )
375 return lcl_ChkOneRange( eSec, bChkSection,
376 rNds.GetEndOfRedlines(), nStt, nEnd );
378 return false; // somewhere in between => error
381 bool GoNext(SwNode* pNd, SwContentIndex * pIdx, SwCursorSkipMode nMode )
383 if( pNd->IsContentNode() )
384 return static_cast<SwContentNode*>(pNd)->GoNext( pIdx, nMode );
385 return false;
388 bool GoPrevious( SwNode* pNd, SwContentIndex * pIdx, SwCursorSkipMode nMode )
390 if( pNd->IsContentNode() )
391 return static_cast<SwContentNode*>(pNd)->GoPrevious( pIdx, nMode );
392 return false;
395 SwContentNode* GoNextNds( SwNodeIndex* pIdx, bool bChk )
397 SwNodeIndex aIdx( *pIdx );
398 SwContentNode* pNd = aIdx.GetNodes().GoNext( &aIdx );
399 if( pNd )
401 if( bChk && SwNodeOffset(1) != aIdx.GetIndex() - pIdx->GetIndex() &&
402 !CheckNodesRange( pIdx->GetNode(), aIdx.GetNode(), true ) )
403 pNd = nullptr;
404 else
405 *pIdx = aIdx;
407 return pNd;
410 SwContentNode* GoPreviousNds( SwNodeIndex * pIdx, bool bChk )
412 SwNodeIndex aIdx( *pIdx );
413 SwContentNode* pNd = SwNodes::GoPrevious( &aIdx );
414 if( pNd )
416 if( bChk && SwNodeOffset(1) != pIdx->GetIndex() - aIdx.GetIndex() &&
417 !CheckNodesRange( pIdx->GetNode(), aIdx.GetNode(), true ) )
418 pNd = nullptr;
419 else
420 *pIdx = aIdx;
422 return pNd;
425 SwContentNode* GoNextPos( SwPosition* pIdx, bool bChk )
427 SwNodeIndex aIdx( pIdx->GetNode() );
428 SwContentNode* pNd = aIdx.GetNodes().GoNext( &aIdx );
429 if( pNd )
431 if( bChk && SwNodeOffset(1) != aIdx.GetIndex() - pIdx->GetNodeIndex() &&
432 !CheckNodesRange( pIdx->GetNode(), aIdx.GetNode(), true ) )
433 pNd = nullptr;
434 else
435 pIdx->Assign(aIdx);
437 return pNd;
440 SwContentNode* GoPreviousPos( SwPosition * pIdx, bool bChk )
442 SwNodeIndex aIdx( pIdx->GetNode() );
443 SwContentNode* pNd = SwNodes::GoPrevious( &aIdx );
444 if( pNd )
446 if( bChk && SwNodeOffset(1) != pIdx->GetNodeIndex() - aIdx.GetIndex() &&
447 !CheckNodesRange( pIdx->GetNode(), aIdx.GetNode(), true ) )
448 pNd = nullptr;
449 else
450 pIdx->Assign(aIdx);
452 return pNd;
455 SwPaM::SwPaM( const SwPosition& rPos, SwPaM* pRing )
456 : Ring( pRing )
457 , m_Bound1( rPos )
458 , m_Bound2( rPos.GetNode().GetNodes() ) // default initialize
459 , m_pPoint( &m_Bound1 )
460 , m_pMark( m_pPoint )
461 , m_bIsInFrontOfLabel( false )
465 SwPaM::SwPaM( const SwPosition& rMark, const SwPosition& rPoint, SwPaM* pRing )
466 : Ring( pRing )
467 , m_Bound1( rMark )
468 , m_Bound2( rPoint )
469 , m_pPoint( &m_Bound2 )
470 , m_pMark( &m_Bound1 )
471 , m_bIsInFrontOfLabel( false )
475 SwPaM::SwPaM( const SwNodeIndex& rMark, const SwNodeIndex& rPoint,
476 SwNodeOffset nMarkOffset, SwNodeOffset nPointOffset, SwPaM* pRing )
477 : Ring( pRing )
478 , m_Bound1( rMark )
479 , m_Bound2( rPoint )
480 , m_pPoint( &m_Bound2 )
481 , m_pMark( &m_Bound1 )
482 , m_bIsInFrontOfLabel( false )
484 if ( nMarkOffset )
486 m_pMark->nNode += nMarkOffset;
488 if ( nPointOffset )
490 m_pPoint->nNode += nPointOffset;
492 m_Bound1.nContent.Assign( m_Bound1.GetNode().GetContentNode(), 0 );
493 m_Bound2.nContent.Assign( m_Bound2.GetNode().GetContentNode(), 0 );
496 SwPaM::SwPaM( const SwNode& rMark, const SwNode& rPoint,
497 SwNodeOffset nMarkOffset, SwNodeOffset nPointOffset, SwPaM* pRing )
498 : Ring( pRing )
499 , m_Bound1( rMark )
500 , m_Bound2( rPoint )
501 , m_pPoint( &m_Bound2 )
502 , m_pMark( &m_Bound1 )
503 , m_bIsInFrontOfLabel( false )
505 if ( nMarkOffset )
507 m_pMark->nNode += nMarkOffset;
509 if ( nPointOffset )
511 m_pPoint->nNode += nPointOffset;
513 m_Bound1.nContent.Assign( m_Bound1.GetNode().GetContentNode(), 0 );
514 m_Bound2.nContent.Assign( m_Bound2.GetNode().GetContentNode(), 0 );
517 SwPaM::SwPaM( const SwNodeIndex& rMark, sal_Int32 nMarkContent,
518 const SwNodeIndex& rPoint, sal_Int32 nPointContent, SwPaM* pRing )
519 : Ring( pRing )
520 , m_Bound1( rMark )
521 , m_Bound2( rPoint )
522 , m_pPoint( &m_Bound2 )
523 , m_pMark( &m_Bound1 )
524 , m_bIsInFrontOfLabel( false )
526 m_pPoint->nContent.Assign( rPoint.GetNode().GetContentNode(), nPointContent);
527 m_pMark ->nContent.Assign( rMark .GetNode().GetContentNode(), nMarkContent );
530 SwPaM::SwPaM( const SwNode& rMark, sal_Int32 nMarkContent,
531 const SwNode& rPoint, sal_Int32 nPointContent, SwPaM* pRing )
532 : Ring( pRing )
533 , m_Bound1( rMark )
534 , m_Bound2( rPoint )
535 , m_pPoint( &m_Bound2 )
536 , m_pMark( &m_Bound1 )
537 , m_bIsInFrontOfLabel( false )
539 m_pPoint->nContent.Assign( m_pPoint->GetNode().GetContentNode(),
540 nPointContent);
541 m_pMark ->nContent.Assign( m_pMark ->GetNode().GetContentNode(),
542 nMarkContent );
545 SwPaM::SwPaM( const SwNode& rMark, SwNodeOffset nMarkOffset, sal_Int32 nMarkContent,
546 const SwNode& rPoint, SwNodeOffset nPointOffset, sal_Int32 nPointContent, SwPaM* pRing )
547 : Ring( pRing )
548 , m_Bound1( rMark )
549 , m_Bound2( rPoint )
550 , m_pPoint( &m_Bound2 )
551 , m_pMark( &m_Bound1 )
552 , m_bIsInFrontOfLabel( false )
554 if ( nMarkOffset )
556 m_pMark->nNode += nMarkOffset;
558 if ( nPointOffset )
560 m_pPoint->nNode += nPointOffset;
562 m_pPoint->nContent.Assign( m_pPoint->GetNode().GetContentNode(),
563 nPointContent);
564 m_pMark ->nContent.Assign( m_pMark ->GetNode().GetContentNode(),
565 nMarkContent );
568 SwPaM::SwPaM( const SwNode& rNode, sal_Int32 nContent, SwPaM* pRing )
569 : Ring( pRing )
570 , m_Bound1( rNode )
571 , m_Bound2( m_Bound1.GetNode().GetNodes() ) // default initialize
572 , m_pPoint( &m_Bound1 )
573 , m_pMark( &m_Bound1 )
574 , m_bIsInFrontOfLabel( false )
576 m_pPoint->nContent.Assign( m_pPoint->GetNode().GetContentNode(),
577 nContent );
580 SwPaM::SwPaM( const SwNode& rNode, SwNodeOffset nNdOffset, sal_Int32 nContent, SwPaM* pRing )
581 : Ring( pRing )
582 , m_Bound1( rNode, nNdOffset )
583 , m_Bound2( m_Bound1.GetNode().GetNodes() ) // default initialize
584 , m_pPoint( &m_Bound1 )
585 , m_pMark( &m_Bound1 )
586 , m_bIsInFrontOfLabel( false )
588 m_pPoint->nContent.Assign( m_pPoint->GetNode().GetContentNode(),
589 nContent );
592 SwPaM::SwPaM( const SwNodeIndex& rNodeIdx, sal_Int32 nContent, SwPaM* pRing )
593 : Ring( pRing )
594 , m_Bound1( rNodeIdx )
595 , m_Bound2( rNodeIdx.GetNode().GetNodes() ) // default initialize
596 , m_pPoint( &m_Bound1 )
597 , m_pMark( &m_Bound1 )
598 , m_bIsInFrontOfLabel( false )
600 m_pPoint->nContent.Assign( rNodeIdx.GetNode().GetContentNode(), nContent );
603 SwPaM::SwPaM( SwNodes& rNodes, SwNodeOffset nNdOffset, SwPaM* pRing )
604 : Ring( pRing )
605 , m_Bound1( rNodes, nNdOffset )
606 , m_Bound2( rNodes ) // default initialize
607 , m_pPoint( &m_Bound1 )
608 , m_pMark( &m_Bound1 )
609 , m_bIsInFrontOfLabel( false )
613 SwPaM::~SwPaM() {}
615 SwPaM::SwPaM(SwPaM const& rPam, SwPaM *const pRing)
616 : Ring(pRing)
617 , m_Bound1( *(rPam.m_pPoint) )
618 , m_Bound2( *(rPam.m_pMark) )
619 , m_pPoint( &m_Bound1 ), m_pMark( rPam.HasMark() ? &m_Bound2 : m_pPoint )
620 , m_bIsInFrontOfLabel( false )
624 // @@@ semantic: no copy assignment for super class Ring.
625 SwPaM &SwPaM::operator=( const SwPaM &rPam )
627 if(this == &rPam)
628 return *this;
630 *m_pPoint = *( rPam.m_pPoint );
631 if ( rPam.HasMark() )
633 SetMark();
634 *m_pMark = *( rPam.m_pMark );
636 else
638 DeleteMark();
640 return *this;
643 void SwPaM::SetMark()
645 if (m_pPoint == &m_Bound1)
647 m_pMark = &m_Bound2;
649 else
651 m_pMark = &m_Bound1;
653 (*m_pMark) = *m_pPoint;
656 /// movement of cursor
657 bool SwPaM::Move( SwMoveFnCollection const & fnMove, SwGoInDoc fnGo )
659 const bool bRet = (*fnGo)( *this, fnMove );
661 m_bIsInFrontOfLabel = false;
662 return bRet;
665 namespace sw {
667 /** make a new region
669 Sets the first SwPaM onto the given SwPaM, or to the beginning or end of a
670 document. SPoint stays at its position, GetMark will be changed respectively.
672 @param fnMove Contains information if beginning or end of document.
673 @param pOrigRg The given region.
674 @param rPam returns newly created range, in Ring with parameter pOrigRg.
676 void MakeRegion(SwMoveFnCollection const & fnMove,
677 const SwPaM & rOrigRg, std::optional<SwPaM>& rPam)
679 rPam.emplace(rOrigRg, const_cast<SwPaM*>(&rOrigRg)); // given search range
680 // make sure that SPoint is on the "real" start position
681 // FORWARD: SPoint always smaller than GetMark
682 // BACKWARD: SPoint always bigger than GetMark
683 if( (rPam->GetMark()->*fnMove.fnCmpOp)( *rPam->GetPoint() ) )
684 rPam->Exchange();
687 } // namespace sw
689 void SwPaM::Normalize(bool bPointFirst)
691 if (HasMark())
692 if ( ( bPointFirst && *m_pPoint > *m_pMark) ||
693 (!bPointFirst && *m_pPoint < *m_pMark) )
695 Exchange();
699 /// return page number at cursor (for reader and page bound frames)
700 sal_uInt16 SwPaM::GetPageNum( bool bAtPoint, const Point* pLayPos )
702 const SwContentFrame* pCFrame;
703 const SwPageFrame *pPg;
704 const SwContentNode *pNd ;
705 const SwPosition* pPos = bAtPoint ? m_pPoint : m_pMark;
707 std::pair<Point, bool> tmp;
708 if (pLayPos)
710 tmp.first = *pLayPos;
711 tmp.second = false;
713 if( nullptr != ( pNd = pPos->GetNode().GetContentNode() ) &&
714 nullptr != (pCFrame = pNd->getLayoutFrame(pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), pPos, pLayPos ? &tmp : nullptr)) &&
715 nullptr != ( pPg = pCFrame->FindPageFrame() ))
716 return pPg->GetPhyPageNum();
717 return 0;
720 // form view - see also SwCursorShell::IsCursorReadonly()
721 static const SwFrame* lcl_FindEditInReadonlyFrame( const SwFrame& rFrame )
723 const SwFrame* pRet = nullptr;
725 const SwFlyFrame* pFly;
726 const SwSectionFrame* pSectionFrame;
728 if( rFrame.IsInFly() &&
729 (pFly = rFrame.FindFlyFrame())->GetFormat()->GetEditInReadonly().GetValue() &&
730 pFly->Lower() &&
731 !pFly->Lower()->IsNoTextFrame() )
733 pRet = pFly;
735 else if ( rFrame.IsInSct() &&
736 nullptr != ( pSectionFrame = rFrame.FindSctFrame() )->GetSection() &&
737 pSectionFrame->GetSection()->IsEditInReadonlyFlag() )
739 pRet = pSectionFrame;
742 return pRet;
745 /// is in protected section or selection surrounds something protected
746 bool SwPaM::HasReadonlySel(bool bFormView, bool const isReplace) const
748 bool bRet = false;
750 const SwContentNode* pNd = GetPoint()->GetNode().GetContentNode();
751 const SwContentFrame *pFrame = nullptr;
752 if ( pNd != nullptr )
754 Point aTmpPt;
755 std::pair<Point, bool> const tmp(aTmpPt, false);
756 pFrame = pNd->getLayoutFrame(
757 pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
758 GetPoint(), &tmp);
761 // Will be set if point are inside edit-in-readonly environment
762 const SwFrame* pPointEditInReadonlyFrame = nullptr;
763 if ( pFrame != nullptr
764 && ( pFrame->IsProtected()
765 || ( bFormView
766 && nullptr == ( pPointEditInReadonlyFrame = lcl_FindEditInReadonlyFrame( *pFrame ) ) ) ) )
768 bRet = true;
770 else if( pNd != nullptr )
772 const SwSectionNode* pSNd = pNd->GetSectionNode();
773 if ( pSNd != nullptr
774 && ( pSNd->GetSection().IsProtectFlag()
775 || ( bFormView
776 && !pSNd->GetSection().IsEditInReadonlyFlag()) ) )
778 bRet = true;
780 else
782 const SwSectionNode* pParentSectionNd = pNd->FindSectionNode();
783 if ( pParentSectionNd != nullptr
784 && ( pParentSectionNd->GetSection().IsProtectFlag()
785 || ( bFormView && !pParentSectionNd->GetSection().IsEditInReadonlyFlag()) ) )
787 bRet = true;
792 if ( !bRet
793 && HasMark()
794 && GetPoint()->nNode != GetMark()->nNode )
796 pNd = GetMark()->GetNode().GetContentNode();
797 pFrame = nullptr;
798 if ( pNd != nullptr )
800 Point aTmpPt;
801 std::pair<Point, bool> const tmp(aTmpPt, false);
802 pFrame = pNd->getLayoutFrame(
803 pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
804 GetMark(), &tmp);
807 const SwFrame* pMarkEditInReadonlyFrame = nullptr;
808 if ( pFrame != nullptr
809 && ( pFrame->IsProtected()
810 || ( bFormView
811 && nullptr == ( pMarkEditInReadonlyFrame = lcl_FindEditInReadonlyFrame( *pFrame ) ) ) ) )
813 bRet = true;
815 else if( pNd != nullptr )
817 const SwSectionNode* pSNd = pNd->GetSectionNode();
818 if ( pSNd != nullptr
819 && ( pSNd->GetSection().IsProtectFlag()
820 || ( bFormView
821 && !pSNd->GetSection().IsEditInReadonlyFlag()) ) )
823 bRet = true;
827 if ( !bRet && bFormView )
829 // Check if start and end frame are inside the _same_
830 // edit-in-readonly-environment. Otherwise we better return 'true'
831 if ( pPointEditInReadonlyFrame != pMarkEditInReadonlyFrame )
832 bRet = true;
835 // check for protected section inside the selection
836 if( !bRet )
838 SwNodeOffset nSttIdx = GetMark()->GetNodeIndex(),
839 nEndIdx = GetPoint()->GetNodeIndex();
840 if( nEndIdx < nSttIdx )
841 std::swap( nSttIdx, nEndIdx );
843 // If a protected section should be between nodes, then the
844 // selection needs to contain already x nodes.
845 // (TextNd, SectNd, TextNd, EndNd, TextNd )
846 if( nSttIdx + SwNodeOffset(3) < nEndIdx )
848 const SwSectionFormats& rFormats = GetDoc().GetSections();
849 for( SwSectionFormats::size_type n = rFormats.size(); n; )
851 const SwSectionFormat* pFormat = rFormats[ --n ];
852 if( pFormat->GetProtect().IsContentProtected() )
854 const SwFormatContent& rContent = pFormat->GetContent(false);
855 OSL_ENSURE( rContent.GetContentIdx(), "where is the SectionNode?" );
856 SwNodeOffset nIdx = rContent.GetContentIdx()->GetIndex();
857 if( nSttIdx <= nIdx && nEndIdx >= nIdx &&
858 rContent.GetContentIdx()->GetNode().GetNodes().IsDocNodes() )
860 bRet = true;
861 break;
869 const SwDoc& rDoc = GetDoc();
870 // Legacy text/combo/checkbox: never return read-only when inside these form fields.
871 const IDocumentMarkAccess* pMarksAccess = rDoc.getIDocumentMarkAccess();
872 sw::mark::IFieldmark* pA = GetPoint() ? pMarksAccess->getInnerFieldmarkFor(*GetPoint()) : nullptr;
873 sw::mark::IFieldmark* pB = GetMark() ? pMarksAccess->getInnerFieldmarkFor(*GetMark()) : pA;
874 // prevent the user from accidentally deleting the field itself when modifying the text.
875 const bool bAtStartA = (pA != nullptr) && (pA->GetMarkStart() == *GetPoint());
876 const bool bAtStartB = (pB != nullptr) && (pB->GetMarkStart() == *GetMark());
878 if (!utl::ConfigManager::IsFuzzing() && officecfg::Office::Common::Filter::Microsoft::Import::ForceImportWWFieldsAsGenericFields::get())
880 ; // allow editing all fields in generic mode
882 else if (!bRet)
884 bool bUnhandledMark = pA && pA->GetFieldname( ) == ODF_UNHANDLED;
885 // Unhandled fieldmarks case shouldn't be edited manually to avoid breaking anything
886 if ( ( pA == pB ) && bUnhandledMark )
887 bRet = true;
888 else
890 if ((pA == pB) && (bAtStartA != bAtStartB))
891 bRet = true;
892 else if (pA != pB)
894 // If both points are either outside or at marks edges (i.e. selection either
895 // touches fields, or fully encloses it), then don't disable editing
896 bRet = !( ( !pA || bAtStartA ) && ( !pB || bAtStartB ) );
898 if( !bRet && rDoc.GetDocumentSettingManager().get( DocumentSettingId::PROTECT_FORM ) && (pA || pB) )
900 // Form protection case
901 bRet = ( pA == nullptr ) || ( pB == nullptr ) || bAtStartA || bAtStartB;
905 else
907 // Allow editing when the cursor/selection is fully inside of a legacy form field.
908 bRet = !( pA != nullptr && !bAtStartA && !bAtStartB && pA == pB );
910 if (bRet)
912 // Also allow editing inside content controls in general, similar to form fields.
913 // Specific types will be disabled below.
914 if (const SwEditShell* pEditShell = rDoc.GetEditShell())
915 bRet = !pEditShell->CursorInsideContentControl();
919 if (!bRet)
921 // Paragraph Signatures and Classification fields are read-only.
922 if (const SwEditShell* pEditShell = rDoc.GetEditShell())
923 bRet = pEditShell->IsCursorInParagraphMetadataField();
926 if (!bRet &&
927 rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_BOOKMARKS))
929 if (rDoc.getIDocumentMarkAccess()->isBookmarkDeleted(*this, isReplace))
931 return true;
934 if (!bRet &&
935 rDoc.getIDocumentSettingAccess().get(DocumentSettingId::PROTECT_FIELDS))
937 SwPosition const& rStart(*Start());
938 SwPosition const& rEnd(*End());
939 for (SwNodeIndex n(rStart.GetNode()); n <= rEnd.GetNode(); ++n)
941 if (SwTextNode const*const pNode = n.GetNode().GetTextNode())
943 if (SwpHints const*const pHints = pNode->GetpSwpHints())
945 for (size_t i = 0; i < pHints->Count(); ++i)
947 SwTextAttr const*const pHint(pHints->Get(i));
948 if (n == rStart.GetNode() && pHint->GetStart() < rStart.GetContentIndex())
950 continue; // before selection
952 if (n == rEnd.GetNode() && rEnd.GetContentIndex() <= pHint->GetStart())
954 break; // after selection
956 if (pHint->Which() == RES_TXTATR_FIELD
957 // placeholders don't work if you can't delete them
958 && pHint->GetFormatField().GetField()->GetTyp()->Which() != SwFieldIds::JumpEdit)
960 return true;
968 if (!bRet)
970 // See if we're inside a read-only content control.
971 const SwPosition* pStart = Start();
972 SwTextNode* pTextNode = pStart->GetNode().GetTextNode();
973 if (pTextNode)
975 sal_Int32 nIndex = pStart->GetContentIndex();
976 SwTextAttr* pAttr
977 = pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, ::sw::GetTextAttrMode::Parent);
978 auto pTextContentControl = static_txtattr_cast<SwTextContentControl*>(pAttr);
979 if (pTextContentControl)
981 const SwFormatContentControl& rFormatContentControl
982 = pTextContentControl->GetContentControl();
983 std::shared_ptr<SwContentControl> pContentControl
984 = rFormatContentControl.GetContentControl();
985 if (pContentControl && !pContentControl->GetReadWrite())
987 switch (pContentControl->GetType())
989 case SwContentControlType::CHECKBOX:
990 case SwContentControlType::PICTURE:
991 case SwContentControlType::DROP_DOWN_LIST:
992 bRet = true;
993 break;
994 default:
995 break;
1002 return bRet;
1005 bool SwPaM::HasHiddenSections() const
1007 bool bRet = false;
1009 if (HasMark() && GetPoint()->nNode != GetMark()->nNode)
1011 // check for hidden section inside the selection
1012 SwNodeOffset nSttIdx = Start()->GetNodeIndex(), nEndIdx = End()->GetNodeIndex();
1014 if (nSttIdx + SwNodeOffset(3) < nEndIdx)
1016 const SwSectionFormats& rFormats = GetDoc().GetSections();
1017 for (SwSectionFormats::size_type n = rFormats.size(); n;)
1019 const SwSectionFormat* pFormat = rFormats[--n];
1020 if (pFormat->GetSection()->IsHidden())
1022 const SwFormatContent& rContent = pFormat->GetContent(false);
1023 OSL_ENSURE(rContent.GetContentIdx(), "where is the SectionNode?");
1024 SwNodeOffset nIdx = rContent.GetContentIdx()->GetIndex();
1025 if (nSttIdx <= nIdx && nEndIdx >= nIdx
1026 && rContent.GetContentIdx()->GetNode().GetNodes().IsDocNodes())
1028 bRet = true;
1029 break;
1036 return bRet;
1039 /// This function returns the next node in direction of search. If there is no
1040 /// left or the next is out of the area, then a null-pointer is returned.
1041 /// @param rbFirst If <true> then first time request. If so than the position of
1042 /// the PaM must not be changed!
1043 SwContentNode* GetNode( SwPaM & rPam, bool& rbFirst, SwMoveFnCollection const & fnMove,
1044 bool const bInReadOnly, SwRootFrame const*const i_pLayout)
1046 SwRootFrame const*const pLayout(i_pLayout ? i_pLayout :
1047 rPam.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout());
1048 SwContentNode * pNd = nullptr;
1049 if( ((*rPam.GetPoint()).*fnMove.fnCmpOp)( *rPam.GetMark() ) ||
1050 ( *rPam.GetPoint() == *rPam.GetMark() && rbFirst ) )
1052 if( rbFirst )
1054 rbFirst = false;
1055 pNd = rPam.GetPointContentNode();
1056 if( pNd )
1058 SwContentFrame const*const pFrame(pNd->getLayoutFrame(pLayout));
1061 nullptr == pFrame ||
1062 ( !bInReadOnly && pFrame->IsProtected() ) ||
1063 (pFrame->IsTextFrame() && static_cast<SwTextFrame const*>(pFrame)->IsHiddenNow())
1064 ) ||
1065 ( !bInReadOnly && pNd->FindSectionNode() &&
1066 pNd->FindSectionNode()->GetSection().IsProtect()
1070 pNd = nullptr;
1075 if( !pNd ) // is the cursor not on a ContentNode?
1077 SwPosition aPos( *rPam.GetPoint() );
1078 bool bSrchForward = &fnMove == &fnMoveForward;
1079 SwNodes& rNodes = aPos.GetNodes();
1081 // go to next/previous ContentNode
1082 while( true )
1084 if (i_pLayout && aPos.GetNode().IsTextNode())
1086 auto const fal(sw::GetFirstAndLastNode(*pLayout, aPos.GetNode()));
1087 aPos.Assign( bSrchForward ? *fal.second : *fal.first );
1090 pNd = bSrchForward
1091 ? rNodes.GoNextSection( &aPos, true, !bInReadOnly )
1092 : SwNodes::GoPrevSection( &aPos, true, !bInReadOnly );
1093 if( pNd )
1095 if (!bSrchForward)
1096 aPos.AssignEndIndex( *pNd );
1097 // is the position still in the area
1098 if( (aPos.*fnMove.fnCmpOp)( *rPam.GetMark() ) )
1100 // only in AutoTextSection can be nodes that are hidden
1101 SwContentFrame const*const pFrame(pNd->getLayoutFrame(pLayout));
1102 if (nullptr == pFrame ||
1103 ( !bInReadOnly && pFrame->IsProtected() ) ||
1104 ( pFrame->IsTextFrame() &&
1105 static_cast<SwTextFrame const*>(pFrame)->IsHiddenNow()))
1107 pNd = nullptr;
1108 continue;
1110 *rPam.GetPoint() = aPos;
1112 else
1113 pNd = nullptr; // no valid node
1114 break;
1116 break;
1120 return pNd;
1123 void GoStartDoc( SwPosition * pPos )
1125 SwNodes& rNodes = pPos->GetNodes();
1126 pPos->Assign( *rNodes.GetEndOfContent().StartOfSectionNode() );
1127 // we always need to find a ContentNode!
1128 rNodes.GoNext( pPos );
1131 void GoEndDoc( SwPosition * pPos )
1133 SwNodes& rNodes = pPos->GetNodes();
1134 pPos->Assign( rNodes.GetEndOfContent() );
1135 SwContentNode* pCNd = GoPreviousPos( pPos, true );
1136 if( pCNd )
1137 pPos->AssignEndIndex(*pCNd);
1140 void GoStartSection( SwPosition * pPos )
1142 // jump to section's beginning
1143 SwNodes& rNodes = pPos->GetNodes();
1144 sal_uInt16 nLevel = SwNodes::GetSectionLevel( pPos->GetNode() );
1145 if( pPos->GetNode() < *rNodes.GetEndOfContent().StartOfSectionNode() )
1146 nLevel--;
1147 do { SwNodes::GoStartOfSection( &pPos->nNode ); } while( nLevel-- );
1149 // already on a ContentNode
1150 pPos->AssignStartIndex(*pPos->GetNode().GetContentNode());
1153 void GoStartOfSection( SwPosition * pPos )
1155 // jump to section's beginning
1156 SwNodes::GoStartOfSection( &pPos->nNode );
1157 pPos->nContent.Assign(pPos->GetNode().GetContentNode(), 0);
1160 /// go to the end of the current base section
1161 void GoEndSection( SwPosition * pPos )
1163 // jump to section's beginning/end
1164 SwNodes& rNodes = pPos->GetNodes();
1165 sal_uInt16 nLevel = SwNodes::GetSectionLevel( pPos->GetNode() );
1166 if( pPos->GetNode() < *rNodes.GetEndOfContent().StartOfSectionNode() )
1167 nLevel--;
1168 do { SwNodes::GoEndOfSection( &pPos->nNode ); } while( nLevel-- );
1170 // now on an EndNode, thus to the previous ContentNode
1171 if( SwContentNode* pCNd = GoPreviousNds( &pPos->nNode, true ) )
1172 pPos->AssignEndIndex(*pCNd);
1175 void GoEndOfSection( SwPosition * pPos )
1177 SwNodes::GoEndOfSection( &pPos->nNode );
1178 SwContentNode* pCNd = pPos->nNode.GetNode().GetContentNode();
1179 pPos->nContent.Assign(pCNd, pCNd ? pCNd->Len() : 0);
1182 bool GoInDoc( SwPaM & rPam, SwMoveFnCollection const & fnMove )
1184 (*fnMove.fnDoc)( rPam.GetPoint() );
1185 return true;
1188 bool GoInSection( SwPaM & rPam, SwMoveFnCollection const & fnMove )
1190 (*fnMove.fnSections)( rPam.GetPoint() );
1191 return true;
1194 bool GoInNode( SwPaM & rPam, SwMoveFnCollection const & fnMove )
1196 SwContentNode *pNd = (*fnMove.fnPos)( rPam.GetPoint(), true );
1197 if( pNd )
1198 rPam.GetPoint()->SetContent(
1199 ::GetSttOrEnd( &fnMove == &fnMoveForward, *pNd ) );
1200 return pNd;
1203 bool GoInContent( SwPaM & rPam, SwMoveFnCollection const & fnMove )
1205 if( (*fnMove.fnNd)( &rPam.GetPoint()->GetNode(),
1206 &rPam.GetPoint()->nContent, SwCursorSkipMode::Chars ))
1207 return true;
1208 return GoInNode( rPam, fnMove );
1211 bool GoInContentCells( SwPaM & rPam, SwMoveFnCollection const & fnMove )
1213 if( (*fnMove.fnNd)( &rPam.GetPoint()->GetNode(),
1214 &rPam.GetPoint()->nContent, SwCursorSkipMode::Cells ))
1215 return true;
1216 return GoInNode( rPam, fnMove );
1219 bool GoInContentSkipHidden( SwPaM & rPam, SwMoveFnCollection const & fnMove )
1221 if( (*fnMove.fnNd)( &rPam.GetPoint()->GetNode(),
1222 &rPam.GetPoint()->nContent, SwCursorSkipMode::Chars | SwCursorSkipMode::Hidden ) )
1223 return true;
1224 return GoInNode( rPam, fnMove );
1227 bool GoInContentCellsSkipHidden( SwPaM & rPam, SwMoveFnCollection const & fnMove )
1229 if( (*fnMove.fnNd)( &rPam.GetPoint()->GetNode(),
1230 &rPam.GetPoint()->nContent, SwCursorSkipMode::Cells | SwCursorSkipMode::Hidden ) )
1231 return true;
1232 return GoInNode( rPam, fnMove );
1235 bool GoPrevPara( SwPaM & rPam, SwMoveFnCollection const & aPosPara )
1237 if( rPam.Move( fnMoveBackward, GoInNode ) )
1239 // always on a ContentNode
1240 SwPosition& rPos = *rPam.GetPoint();
1241 SwContentNode * pNd = rPos.GetNode().GetContentNode();
1242 rPos.SetContent( ::GetSttOrEnd( &aPosPara == &fnMoveForward, *pNd ) );
1243 return true;
1245 return false;
1248 bool GoCurrPara( SwPaM & rPam, SwMoveFnCollection const & aPosPara )
1250 SwPosition& rPos = *rPam.GetPoint();
1251 SwContentNode * pNd = rPos.GetNode().GetContentNode();
1252 if( pNd )
1254 const sal_Int32 nOld = rPos.GetContentIndex();
1255 const sal_Int32 nNew = &aPosPara == &fnMoveForward ? 0 : pNd->Len();
1256 // if already at beginning/end then to the next/previous
1257 if( nOld != nNew )
1259 rPos.SetContent( nNew );
1260 return true;
1263 // move node to next/previous ContentNode
1264 if( ( &aPosPara==&fnParaStart && nullptr != ( pNd =
1265 GoPreviousPos( &rPos, true ))) ||
1266 ( &aPosPara==&fnParaEnd && nullptr != ( pNd =
1267 GoNextPos( &rPos, true ))) )
1269 rPos.SetContent( ::GetSttOrEnd( &aPosPara == &fnMoveForward, *pNd ));
1270 return true;
1272 return false;
1275 bool GoNextPara( SwPaM & rPam, SwMoveFnCollection const & aPosPara )
1277 if( rPam.Move( fnMoveForward, GoInNode ) )
1279 // always on a ContentNode
1280 SwPosition& rPos = *rPam.GetPoint();
1281 SwContentNode * pNd = rPos.GetNode().GetContentNode();
1282 rPos.SetContent( ::GetSttOrEnd( &aPosPara == &fnMoveForward, *pNd ) );
1283 return true;
1285 return false;
1288 bool GoCurrSection( SwPaM & rPam, SwMoveFnCollection const & fnMove )
1290 SwPosition& rPos = *rPam.GetPoint();
1291 SwPosition aSavePos( rPos ); // position for comparison
1292 (fnMove.fnSection)( &rPos );
1293 SwContentNode *pNd;
1294 if( nullptr == ( pNd = rPos.GetNode().GetContentNode()) &&
1295 nullptr == ( pNd = (*fnMove.fnPos)( &rPos, true )) )
1297 rPos = aSavePos; // do not change cursor
1298 return false;
1301 rPos.SetContent( ::GetSttOrEnd( &fnMove == &fnMoveForward, *pNd ) );
1302 return aSavePos != rPos;
1305 OUString SwPaM::GetText() const
1307 OUStringBuffer aResult;
1309 SwNodeIndex aNodeIndex = Start()->nNode;
1311 // The first node can be already the end node.
1312 // Use a "forever" loop with an exit condition in the middle
1313 // of its body, in order to correctly handle all cases.
1314 bool bIsStartNode = true;
1315 for (;;)
1317 const bool bIsEndNode = aNodeIndex == End()->nNode;
1318 SwTextNode * pTextNode = aNodeIndex.GetNode().GetTextNode();
1320 if (pTextNode != nullptr)
1322 if (!bIsStartNode)
1324 aResult.append(CH_TXTATR_NEWLINE); // use newline for para break
1326 const OUString& aTmpStr = pTextNode->GetText();
1328 if (bIsStartNode || bIsEndNode)
1330 // Handle corner cases of start/end node(s)
1331 const sal_Int32 nStart = bIsStartNode
1332 ? Start()->GetContentIndex()
1333 : 0;
1334 const sal_Int32 nEnd = bIsEndNode
1335 ? End()->GetContentIndex()
1336 : aTmpStr.getLength();
1338 aResult.append(aTmpStr.subView(nStart, nEnd-nStart));
1340 else
1342 aResult.append(aTmpStr);
1346 if (bIsEndNode)
1348 break;
1351 ++aNodeIndex;
1352 bIsStartNode = false;
1355 return aResult.makeStringAndClear();
1358 void SwPaM::InvalidatePaM()
1360 for (SwNodeIndex index(Start()->GetNode()); index <= End()->GetNode(); ++index)
1362 if (SwTextNode *const pTextNode = index.GetNode().GetTextNode())
1364 // pretend that the PaM marks changed formatting to reformat...
1365 sal_Int32 const nStart(
1366 index == Start()->nNode ? Start()->GetContentIndex() : 0);
1367 // this should work even for length of 0
1368 SwUpdateAttr const aHint(
1369 nStart,
1370 index == End()->nNode
1371 ? End()->GetContentIndex() - nStart
1372 : pTextNode->Len() - nStart,
1374 pTextNode->TriggerNodeUpdate(sw::LegacyModifyHint(&aHint, &aHint));
1376 // other node types not invalidated
1380 void SwPaM::dumpAsXml(xmlTextWriterPtr pWriter) const
1382 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SwPaM"));
1384 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("point"));
1385 GetPoint()->dumpAsXml(pWriter);
1386 (void)xmlTextWriterEndElement(pWriter);
1388 if (HasMark())
1390 (void)xmlTextWriterStartElement(pWriter, BAD_CAST("mark"));
1391 GetMark()->dumpAsXml(pWriter);
1392 (void)xmlTextWriterEndElement(pWriter);
1395 (void)xmlTextWriterEndElement(pWriter);
1398 std::ostream &operator <<(std::ostream& s, const SwPaM& pam)
1400 if( pam.HasMark())
1401 return s << "SwPaM (point " << *pam.GetPoint() << ", mark " << *pam.GetMark() << ")";
1402 else
1403 return s << "SwPaM (point " << *pam.GetPoint() << ")";
1407 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */