Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / sw / source / core / crsr / crstrvl.cxx
blob3face1677c130ae965aea5472ae3b9f2d4902b83
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 <memory>
21 #include <utility>
22 #include <hintids.hxx>
23 #include <comphelper/string.hxx>
24 #include <svl/itemiter.hxx>
25 #include <editeng/lrspitem.hxx>
26 #include <editeng/adjustitem.hxx>
27 #include <editeng/formatbreakitem.hxx>
28 #include <svx/svdobj.hxx>
29 #include <osl/diagnose.h>
30 #include <crsrsh.hxx>
31 #include <doc.hxx>
32 #include <IDocumentUndoRedo.hxx>
33 #include <IDocumentRedlineAccess.hxx>
34 #include <IDocumentFieldsAccess.hxx>
35 #include <IDocumentLayoutAccess.hxx>
36 #include <pagefrm.hxx>
37 #include <cntfrm.hxx>
38 #include <rootfrm.hxx>
39 #include <pam.hxx>
40 #include <ndtxt.hxx>
41 #include <fldbas.hxx>
42 #include <swtable.hxx>
43 #include <docary.hxx>
44 #include <txtfld.hxx>
45 #include <fmtfld.hxx>
46 #include <txtftn.hxx>
47 #include <txtinet.hxx>
48 #include <fmtinfmt.hxx>
49 #include <txttxmrk.hxx>
50 #include <frmfmt.hxx>
51 #include <flyfrm.hxx>
52 #include <viscrs.hxx>
53 #include "callnk.hxx"
54 #include <doctxm.hxx>
55 #include <docfld.hxx>
56 #include <expfld.hxx>
57 #include <reffld.hxx>
58 #include <flddat.hxx>
59 #include <cellatr.hxx>
60 #include <swundo.hxx>
61 #include <redline.hxx>
62 #include <fmtcntnt.hxx>
63 #include <fmthdft.hxx>
64 #include <pagedesc.hxx>
65 #include <fesh.hxx>
66 #include <charfmt.hxx>
67 #include <fmturl.hxx>
68 #include <txtfrm.hxx>
69 #include <wrong.hxx>
70 #include <calbck.hxx>
71 #include <unotools/intlwrapper.hxx>
72 #include <docufld.hxx>
73 #include <svx/srchdlg.hxx>
74 #include <frameformats.hxx>
75 #include <docsh.hxx>
76 #include <wrtsh.hxx>
77 #include <textcontentcontrol.hxx>
79 using namespace ::com::sun::star;
81 void SwCursorShell::MoveCursorToNum()
83 SwCallLink aLk( *this ); // watch Cursor-Moves
84 SwCursorSaveState aSaveState( *m_pCurrentCursor );
85 if( ActionPend() )
86 return;
87 CurrShell aCurr( this );
88 // try to set cursor onto this position, at half of the char-
89 // SRectangle's height
90 Point aPt( m_pCurrentCursor->GetPtPos() );
91 std::pair<Point, bool> const tmp(aPt, true);
92 SwContentFrame * pFrame = m_pCurrentCursor->GetPointContentNode()->getLayoutFrame(
93 GetLayout(), m_pCurrentCursor->GetPoint(), &tmp);
94 pFrame->GetCharRect( m_aCharRect, *m_pCurrentCursor->GetPoint() );
95 pFrame->Calc(GetOut());
96 if( pFrame->IsVertical() )
98 aPt.setX(m_aCharRect.Center().getX());
99 aPt.setY(pFrame->getFrameArea().Top() + GetUpDownX());
101 else
103 aPt.setY(m_aCharRect.Center().getY());
104 aPt.setX(pFrame->getFrameArea().Left() + GetUpDownX());
106 pFrame->GetModelPositionForViewPoint( m_pCurrentCursor->GetPoint(), aPt );
107 if ( !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::Toggle |
108 SwCursorSelOverFlags::ChangePos ))
110 UpdateCursor(SwCursorShell::UPDOWN |
111 SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
112 SwCursorShell::READONLY );
116 /// go to next/previous point on the same level
117 void SwCursorShell::GotoNextNum()
119 if (!SwDoc::GotoNextNum(*m_pCurrentCursor->GetPoint(), GetLayout()))
120 return;
121 MoveCursorToNum();
124 void SwCursorShell::GotoPrevNum()
126 if (!SwDoc::GotoPrevNum(*m_pCurrentCursor->GetPoint(), GetLayout()))
127 return;
128 MoveCursorToNum();
131 /// jump from content to header
132 bool SwCursorShell::GotoHeaderText()
134 const SwFrame* pFrame = GetCurrFrame()->FindPageFrame();
135 while( pFrame && !pFrame->IsHeaderFrame() )
136 pFrame = pFrame->GetLower();
137 // found header, search 1. content frame
138 while( pFrame && !pFrame->IsContentFrame() )
139 pFrame = pFrame->GetLower();
141 if( !pFrame )
142 return false;
144 CurrShell aCurr( this );
145 // get header frame
146 SwCallLink aLk( *this ); // watch Cursor-Moves
147 SwCursor *pTmpCursor = getShellCursor( true );
148 SwCursorSaveState aSaveState( *pTmpCursor );
149 pFrame->Calc(GetOut());
150 Point aPt( pFrame->getFrameArea().Pos() + pFrame->getFramePrintArea().Pos() );
151 pFrame->GetModelPositionForViewPoint( pTmpCursor->GetPoint(), aPt );
152 if( !pTmpCursor->IsSelOvr() )
153 UpdateCursor();
154 else
155 pFrame = nullptr;
156 return nullptr != pFrame;
159 /// jump from content to footer
160 bool SwCursorShell::GotoFooterText()
162 const SwPageFrame* pFrame = GetCurrFrame()->FindPageFrame();
163 if( !pFrame )
164 return false;
166 const SwFrame* pLower = pFrame->GetLastLower();
168 while( pLower && !pLower->IsFooterFrame() )
169 pLower = pLower->GetLower();
170 // found footer, search 1. content frame
171 while( pLower && !pLower->IsContentFrame() )
172 pLower = pLower->GetLower();
174 if( !pLower )
175 return false;
177 SwCursor *pTmpCursor = getShellCursor( true );
178 CurrShell aCurr( this );
179 // get position in footer
180 SwCallLink aLk( *this ); // watch Cursor-Moves
181 SwCursorSaveState aSaveState( *pTmpCursor );
182 pLower->Calc(GetOut());
183 Point aPt( pLower->getFrameArea().Pos() + pLower->getFramePrintArea().Pos() );
184 pLower->GetModelPositionForViewPoint( pTmpCursor->GetPoint(), aPt );
185 if( !pTmpCursor->IsSelOvr() )
186 UpdateCursor();
187 else
188 pFrame = nullptr;
189 return nullptr != pFrame;
192 bool SwCursorShell::SetCursorInHdFt(size_t nDescNo, bool bInHeader, bool bEven, bool bFirst)
194 SwDoc *pMyDoc = GetDoc();
195 const SwPageDesc* pDesc = nullptr;
197 CurrShell aCurr( this );
199 if( SIZE_MAX == nDescNo )
201 // take the current one
202 const SwContentFrame *pCurrFrame = GetCurrFrame();
203 const SwPageFrame* pPage = (pCurrFrame == nullptr) ? nullptr : pCurrFrame->FindPageFrame();
204 if( pPage && pMyDoc->ContainsPageDesc(
205 pPage->GetPageDesc(), &nDescNo) )
206 pDesc = pPage->GetPageDesc();
208 else
209 if (nDescNo < pMyDoc->GetPageDescCnt())
210 pDesc = &pMyDoc->GetPageDesc( nDescNo );
212 if( !pDesc )
213 return false;
215 // check if the attribute exists
216 const SwFormatContent* pCnt = nullptr;
217 if( bInHeader )
219 const SwFormatHeader& rHd
220 = bEven ? bFirst ? pDesc->GetFirstLeft().GetHeader() : pDesc->GetLeft().GetHeader()
221 : bFirst ? pDesc->GetFirstMaster().GetHeader() : pDesc->GetMaster().GetHeader();
222 if( rHd.GetHeaderFormat() )
223 pCnt = &rHd.GetHeaderFormat()->GetContent();
225 else
227 const SwFormatFooter& rFt
228 = bEven ? bFirst ? pDesc->GetFirstLeft().GetFooter() : pDesc->GetLeft().GetFooter()
229 : bFirst ? pDesc->GetFirstMaster().GetFooter() : pDesc->GetMaster().GetFooter();
230 if( rFt.GetFooterFormat() )
231 pCnt = &rFt.GetFooterFormat()->GetContent();
234 if( !pCnt || !pCnt->GetContentIdx() )
235 return false;
237 SwNodeIndex aIdx( *pCnt->GetContentIdx(), 1 );
238 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
239 if( !pCNd )
240 pCNd = pMyDoc->GetNodes().GoNext( &aIdx );
242 Point aPt( m_pCurrentCursor->GetPtPos() );
244 std::pair<Point, bool> const tmp(aPt, false);
245 if (!pCNd || nullptr == pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp))
246 return false;
248 // then we can set the cursor in here
249 SwCallLink aLk( *this ); // watch Cursor-Moves
250 SwCursorSaveState aSaveState( *m_pCurrentCursor );
252 ClearMark();
254 SwPosition& rPos = *m_pCurrentCursor->GetPoint();
255 rPos.Assign( *pCNd );
257 if (m_pCurrentCursor->IsSelOvr())
258 return false;
260 UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
261 SwCursorShell::READONLY );
262 return true;
265 /// jump to the next index
266 bool SwCursorShell::GotoNextTOXBase( const OUString* pName )
268 const SwSectionFormats& rFormats = GetDoc()->GetSections();
269 SwContentNode* pFnd = nullptr;
270 for( SwSectionFormats::size_type n = rFormats.size(); n; )
272 const SwSection* pSect = rFormats[ --n ]->GetSection();
273 if (SectionType::ToxContent == pSect->GetType())
275 SwSectionNode const*const pSectNd(
276 pSect->GetFormat()->GetSectionNode());
277 if ( pSectNd
278 && m_pCurrentCursor->GetPoint()->GetNode() < *pSectNd
279 && (!pFnd || pFnd->GetIndex() > pSectNd->GetIndex())
280 && (!pName || *pName ==
281 static_cast<SwTOXBaseSection const*>(pSect)->GetTOXName()))
283 SwNodeIndex aIdx(*pSectNd, 1);
284 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
285 if (!pCNd)
286 pCNd = GetDoc()->GetNodes().GoNext( &aIdx );
287 if (pCNd &&
288 pCNd->EndOfSectionIndex() <= pSectNd->EndOfSectionIndex())
290 SwContentFrame const*const pCFrame(
291 pCNd->getLayoutFrame(GetLayout()));
292 if (pCFrame &&
293 (IsReadOnlyAvailable() || !pCFrame->IsProtected()))
295 pFnd = pCNd;
301 if( !pFnd )
302 return false;
303 SwCallLink aLk( *this ); // watch Cursor-Moves
304 SwCursorSaveState aSaveState( *m_pCurrentCursor );
305 m_pCurrentCursor->GetPoint()->Assign( *pFnd );
306 bool bRet = !m_pCurrentCursor->IsSelOvr();
307 if( bRet )
308 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
309 return bRet;
312 /// jump to previous index
313 bool SwCursorShell::GotoPrevTOXBase( const OUString* pName )
315 const SwSectionFormats& rFormats = GetDoc()->GetSections();
316 SwContentNode* pFnd = nullptr;
317 for( SwSectionFormats::size_type n = rFormats.size(); n; )
319 const SwSection* pSect = rFormats[ --n ]->GetSection();
320 if (SectionType::ToxContent == pSect->GetType())
322 SwSectionNode const*const pSectNd(
323 pSect->GetFormat()->GetSectionNode());
324 if ( pSectNd
325 && m_pCurrentCursor->GetPoint()->GetNode() > *pSectNd->EndOfSectionNode()
326 && (!pFnd || *pFnd < *pSectNd)
327 && (!pName || *pName ==
328 static_cast<SwTOXBaseSection const*>(pSect)->GetTOXName()))
330 SwNodeIndex aIdx(*pSectNd, 1);
331 SwContentNode* pCNd = aIdx.GetNode().GetContentNode();
332 if (!pCNd)
333 pCNd = GetDoc()->GetNodes().GoNext( &aIdx );
334 if (pCNd &&
335 pCNd->EndOfSectionIndex() <= pSectNd->EndOfSectionIndex())
337 SwContentFrame const*const pCFrame(
338 pCNd->getLayoutFrame(GetLayout()));
339 if (pCFrame &&
340 (IsReadOnlyAvailable() || !pCFrame->IsProtected()))
342 pFnd = pCNd;
349 if( !pFnd )
350 return false;
352 SwCallLink aLk( *this ); // watch Cursor-Moves
353 SwCursorSaveState aSaveState( *m_pCurrentCursor );
354 m_pCurrentCursor->GetPoint()->Assign(*pFnd);
355 bool bRet = !m_pCurrentCursor->IsSelOvr();
356 if( bRet )
357 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
358 return bRet;
361 /// jump to index of TOXMark
362 void SwCursorShell::GotoTOXMarkBase()
364 SwTOXMarks aMarks;
365 sal_uInt16 nCnt = SwDoc::GetCurTOXMark(*m_pCurrentCursor->GetPoint(), aMarks);
366 if(!nCnt)
367 return;
368 // Take the 1. and get the index type. Ask it for the actual index.
369 const SwTOXType* pType = aMarks[0]->GetTOXType();
370 auto pContentFrame = pType->FindContentFrame(*GetDoc(), *GetLayout());
371 if(!pContentFrame)
372 return;
373 SwCallLink aLk(*this); // watch Cursor-Moves
374 SwCursorSaveState aSaveState(*m_pCurrentCursor);
375 assert(pContentFrame->IsTextFrame());
376 *m_pCurrentCursor->GetPoint() = static_cast<SwTextFrame const*>(pContentFrame)->MapViewToModelPos(TextFrameIndex(0));
377 if(!m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr())
378 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
381 /// Jump to next/previous table formula
382 /// Optionally it is possible to also jump to broken formulas
383 bool SwCursorShell::GotoNxtPrvTableFormula( bool bNext, bool bOnlyErrors )
385 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
387 if( IsTableMode() )
388 return false;
390 bool bFnd = false;
391 SwPosition aOldPos = *m_pCurrentCursor->GetPoint();
392 SwPosition& rPos = *m_pCurrentCursor->GetPoint();
394 Point aPt;
395 SwPosition aFndPos( GetDoc()->GetNodes().GetEndOfContent() );
396 if( !bNext )
397 aFndPos.Assign(SwNodeOffset(0));
398 SetGetExpField aFndGEF( aFndPos ), aCurGEF( rPos );
401 const SwNode* pSttNd = rPos.GetNode().FindTableBoxStartNode();
402 if( pSttNd )
404 const SwTableBox* pTBox = pSttNd->FindTableNode()->GetTable().
405 GetTableBox( pSttNd->GetIndex() );
406 if( pTBox )
407 aCurGEF = SetGetExpField( *pTBox );
411 if( rPos.GetNode() < GetDoc()->GetNodes().GetEndOfExtras() )
413 // also at collection use only the first frame
414 std::pair<Point, bool> const tmp(aPt, false);
415 aCurGEF.SetBodyPos( *rPos.GetNode().GetContentNode()->getLayoutFrame( GetLayout(),
416 &rPos, &tmp) );
419 sal_uInt32 nMaxItems = GetDoc()->GetAttrPool().GetItemCount2( RES_BOXATR_FORMULA );
420 if( nMaxItems > 0 )
422 sal_uInt8 nMaxDo = 2;
423 do {
424 for (const SfxPoolItem* pItem : GetDoc()->GetAttrPool().GetItemSurrogates(RES_BOXATR_FORMULA))
426 const SwTableBox* pTBox;
427 auto pFormulaItem = dynamic_cast<const SwTableBoxFormula*>(pItem);
428 if( !pFormulaItem )
429 continue;
430 pTBox = pFormulaItem->GetTableBox();
431 if( pTBox &&
432 pTBox->GetSttNd() &&
433 pTBox->GetSttNd()->GetNodes().IsDocNodes() &&
434 ( !bOnlyErrors ||
435 !pFormulaItem->HasValidBoxes() ) )
437 SwNodeIndex aIdx( *pTBox->GetSttNd() );
438 const SwContentNode* pCNd = GetDoc()->GetNodes().GoNext( &aIdx );
439 std::pair<Point, bool> const tmp(aPt, false);
440 if (pCNd)
442 const SwContentFrame* pCFrame = pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
443 if (pCFrame && (IsReadOnlyAvailable() || !pCFrame->IsProtected() ))
445 SetGetExpField aCmp( *pTBox );
446 aCmp.SetBodyPos( *pCFrame );
448 if( bNext ? ( aCurGEF < aCmp && aCmp < aFndGEF )
449 : ( aCmp < aCurGEF && aFndGEF < aCmp ))
451 aFndGEF = aCmp;
452 bFnd = true;
458 if( !bFnd )
460 if( bNext )
462 rPos.Assign(SwNodeOffset(0), 0);
463 aCurGEF = SetGetExpField( rPos );
464 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
466 else
468 aCurGEF = SetGetExpField( SwPosition( GetDoc()->GetNodes().GetEndOfContent() ) );
469 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
472 } while( !bFnd && --nMaxDo );
475 if( !bFnd )
477 rPos = aOldPos;
478 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
479 return false;
482 CurrShell aCurr( this );
483 SwCallLink aLk( *this ); // watch Cursor-Moves
484 SwCursorSaveState aSaveState( *m_pCurrentCursor );
486 aFndGEF.GetPosOfContent( rPos );
487 m_pCurrentCursor->DeleteMark();
489 bFnd = !m_pCurrentCursor->IsSelOvr();
490 if( bFnd )
491 UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
492 SwCursorShell::READONLY );
494 return bFnd;
497 /// jump to next/previous index marker
498 bool SwCursorShell::GotoNxtPrvTOXMark( bool bNext )
500 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
502 if( IsTableMode() )
503 return false;
505 bool bFnd = false;
506 SwPosition& rPos = *m_pCurrentCursor->GetPoint();
508 Point aPt;
509 SwPosition aFndPos( GetDoc()->GetNodes().GetEndOfContent() );
510 if( !bNext )
511 aFndPos.Assign(SwNodeOffset(0));
512 SetGetExpField aFndGEF( aFndPos ), aCurGEF( rPos );
514 if( rPos.GetNodeIndex() < GetDoc()->GetNodes().GetEndOfExtras().GetIndex() )
516 // also at collection use only the first frame
517 std::pair<Point, bool> const tmp(aPt, false);
518 aCurGEF.SetBodyPos( *rPos.GetNode().
519 GetContentNode()->getLayoutFrame(GetLayout(), &rPos, &tmp));
522 const SwTextNode* pTextNd;
523 const SwTextTOXMark* pTextTOX;
524 sal_uInt32 nMaxItems = GetDoc()->GetAttrPool().GetItemCount2( RES_TXTATR_TOXMARK );
525 if( nMaxItems == 0 )
527 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
528 return false;
531 do {
532 for (const SfxPoolItem* pItem : GetDoc()->GetAttrPool().GetItemSurrogates(RES_TXTATR_TOXMARK))
534 auto pToxMarkItem = dynamic_cast<const SwTOXMark*>(pItem);
535 if( !pToxMarkItem )
536 continue;
537 pTextTOX = pToxMarkItem->GetTextTOXMark();
538 if( !pTextTOX )
539 continue;
540 pTextNd = &pTextTOX->GetTextNode();
541 if( !pTextNd->GetNodes().IsDocNodes() )
542 continue;
543 std::pair<Point, bool> const tmp(aPt, false);
544 const SwContentFrame* pCFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
545 if( pCFrame && ( IsReadOnlyAvailable() || !pCFrame->IsProtected() ))
547 SetGetExpField aCmp( *pTextNd, *pTextTOX );
548 aCmp.SetBodyPos( *pCFrame );
550 if( bNext ? ( aCurGEF < aCmp && aCmp < aFndGEF )
551 : ( aCmp < aCurGEF && aFndGEF < aCmp ))
553 aFndGEF = aCmp;
554 bFnd = true;
558 if( !bFnd )
560 if ( bNext )
562 rPos.Assign(SwNodeOffset(0), 0);
563 aCurGEF = SetGetExpField( rPos );
564 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
566 else
568 aCurGEF = SetGetExpField( SwPosition( GetDoc()->GetNodes().GetEndOfContent() ) );
569 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
572 } while ( !bFnd );
574 CurrShell aCurr( this );
575 SwCallLink aLk( *this ); // watch Cursor-Moves
576 SwCursorSaveState aSaveState( *m_pCurrentCursor );
578 aFndGEF.GetPosOfContent( rPos );
580 bFnd = !m_pCurrentCursor->IsSelOvr();
581 if( bFnd )
582 UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
583 SwCursorShell::READONLY );
584 return bFnd;
587 /// traveling between marks
588 const SwTOXMark& SwCursorShell::GotoTOXMark( const SwTOXMark& rStart,
589 SwTOXSearch eDir )
591 CurrShell aCurr( this );
592 SwCallLink aLk( *this ); // watch Cursor-Moves
593 SwCursorSaveState aSaveState( *m_pCurrentCursor );
595 const SwTOXMark& rNewMark = GetDoc()->GotoTOXMark( rStart, eDir,
596 IsReadOnlyAvailable() );
597 // set position
598 SwPosition& rPos = *GetCursor()->GetPoint();
599 rPos.Assign(rNewMark.GetTextTOXMark()->GetTextNode(),
600 rNewMark.GetTextTOXMark()->GetStart() );
602 if( !m_pCurrentCursor->IsSelOvr() )
603 UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE |
604 SwCursorShell::READONLY );
606 return rNewMark;
609 /// jump to next/previous field type
610 static void lcl_MakeFieldLst(
611 SetGetExpFields& rLst,
612 const SwFieldType& rFieldType,
613 const bool bInReadOnly,
614 const bool bChkInpFlag = false )
616 // always search the 1. frame
617 Point aPt;
618 std::vector<SwFormatField*> vFields;
619 rFieldType.GatherFields(vFields, false);
620 for(SwFormatField* pFormatField: vFields)
622 SwTextField* pTextField = pFormatField->GetTextField();
623 if ( pTextField != nullptr
624 && ( !bChkInpFlag
625 || static_cast<const SwSetExpField*>(pTextField->GetFormatField().GetField())->GetInputFlag() ) )
627 const SwTextNode& rTextNode = pTextField->GetTextNode();
628 std::pair<Point, bool> const tmp(aPt, false);
629 const SwContentFrame* pCFrame =
630 rTextNode.getLayoutFrame(
631 rTextNode.GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(),
632 nullptr, &tmp);
633 if ( pCFrame != nullptr
634 && ( bInReadOnly || !pCFrame->IsProtected() ) )
636 std::unique_ptr<SetGetExpField> pNew(new SetGetExpField( rTextNode, pTextField ));
637 pNew->SetBodyPos( *pCFrame );
638 rLst.insert( std::move(pNew) );
644 static SetGetExpFields::const_iterator
645 lcl_FindField(bool & o_rFound, SetGetExpFields const& rSrtLst,
646 SwRootFrame const *const pLayout, SwTextNode *const pTextNode,
647 SwTextField const *const pTextField, SwPosition const& rPos,
648 sal_Int32 const nContentOffset)
650 std::optional<SetGetExpField> oSrch;
651 if (-1 == nContentOffset)
653 oSrch.emplace(rPos.GetNode(), pTextField, rPos.GetContentIndex());
655 else
657 oSrch.emplace(rPos.GetNode(), pTextField, nContentOffset);
660 if (rPos.GetNodeIndex() < pTextNode->GetNodes().GetEndOfExtras().GetIndex())
662 // also at collection use only the first frame
663 Point aPt;
664 std::pair<Point, bool> const tmp(aPt, false);
665 oSrch->SetBodyPos(*pTextNode->getLayoutFrame(pLayout, &rPos, &tmp));
668 SetGetExpFields::const_iterator it = rSrtLst.lower_bound(&*oSrch);
670 o_rFound = (it != rSrtLst.end()) && (**it == *oSrch);
671 return it;
674 bool SwCursorShell::MoveFieldType(
675 const SwFieldType* pFieldType,
676 const bool bNext,
677 const SwFieldIds nResType,
678 const bool bAddSetExpressionFieldsToInputFields )
680 // sorted list of all fields
681 SetGetExpFields aSrtLst;
683 if ( pFieldType )
685 if( SwFieldIds::Input != pFieldType->Which() && !pFieldType->HasWriterListeners() )
687 return false;
690 // found Modify object, add all fields to array
691 ::lcl_MakeFieldLst( aSrtLst, *pFieldType, IsReadOnlyAvailable() );
693 if( SwFieldIds::Input == pFieldType->Which() && bAddSetExpressionFieldsToInputFields )
695 // there are hidden input fields in the set exp. fields
696 const SwFieldTypes& rFieldTypes = *mxDoc->getIDocumentFieldsAccess().GetFieldTypes();
697 const size_t nSize = rFieldTypes.size();
698 for( size_t i=0; i < nSize; ++i )
700 pFieldType = rFieldTypes[ i ].get();
701 if ( SwFieldIds::SetExp == pFieldType->Which() )
703 ::lcl_MakeFieldLst( aSrtLst, *pFieldType, IsReadOnlyAvailable(), true );
708 else
710 const SwFieldTypes& rFieldTypes = *mxDoc->getIDocumentFieldsAccess().GetFieldTypes();
711 const size_t nSize = rFieldTypes.size();
712 const bool bAllFieldTypes = nResType == SwFieldIds::Unknown;
713 for( size_t i=0; i < nSize; ++i )
715 pFieldType = rFieldTypes[ i ].get();
716 if (bAllFieldTypes || nResType == pFieldType->Which())
718 ::lcl_MakeFieldLst( aSrtLst, *pFieldType, IsReadOnlyAvailable() );
723 // found no fields?
724 if( aSrtLst.empty() )
725 return false;
727 SetGetExpFields::const_iterator it;
728 SwCursor* pCursor = getShellCursor( true );
730 // (1998): Always use field for search so that the right one is found as
731 // well some are in frames that are anchored to a paragraph that has a
732 // field
733 const SwPosition& rPos = *pCursor->GetPoint();
735 SwTextNode* pTNd = rPos.GetNode().GetTextNode();
736 OSL_ENSURE( pTNd, "No ContentNode" );
738 SwTextField * pTextField = pTNd->GetFieldTextAttrAt(rPos.GetContentIndex(), ::sw::GetTextAttrMode::Default);
739 const bool bDelField = ( pTextField == nullptr );
740 sal_Int32 nContentOffset = -1;
742 if( bDelField )
744 // create dummy for the search
745 SwFormatField* pFormatField = new SwFormatField( SwDateTimeField(
746 static_cast<SwDateTimeFieldType*>(mxDoc->getIDocumentFieldsAccess().GetSysFieldType( SwFieldIds::DateTime ) ) ) );
748 pTextField = new SwTextField( *pFormatField, rPos.GetContentIndex(),
749 mxDoc->IsClipBoard() );
750 pTextField->ChgTextNode( pTNd );
752 else
754 // the cursor might be anywhere inside the input field,
755 // but we will be searching for the field start
756 if (pTextField->Which() == RES_TXTATR_INPUTFIELD
757 && rPos.GetContentIndex() != pTextField->GetStart())
758 nContentOffset = pTextField->GetStart();
760 bool isSrch;
761 it = lcl_FindField(isSrch, aSrtLst,
762 GetLayout(), pTNd, pTextField, rPos, nContentOffset);
764 if( bDelField )
766 auto const pFormat(static_cast<SwFormatField*>(&pTextField->GetAttr()));
767 delete pTextField;
768 delete pFormat;
771 if( it != aSrtLst.end() && isSrch ) // found
773 if( bNext )
775 if( ++it == aSrtLst.end() )
776 return false; // already at the end
778 else
780 if( it == aSrtLst.begin() )
781 return false; // no more steps backward possible
782 --it;
785 else // not found
787 if( bNext )
789 if( it == aSrtLst.end() )
790 return false;
792 else
794 if( it == aSrtLst.begin() )
795 return false; // no more steps backward possible
796 --it;
800 const SetGetExpField& rFnd = **it;
802 CurrShell aCurr( this );
803 SwCallLink aLk( *this ); // watch Cursor-Moves
804 SwCursorSaveState aSaveState( *pCursor );
806 rFnd.GetPosOfContent( *pCursor->GetPoint() );
807 bool bRet = !m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
808 SwCursorSelOverFlags::Toggle );
809 if( bRet )
810 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
811 return bRet;
814 bool SwCursorShell::GotoFootnoteAnchor(const SwTextFootnote& rTextFootnote)
816 if (SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(this))
817 pWrtSh->addCurrentPosition();
819 bool bRet = false;
820 SwCursor* pCursor = getShellCursor(true);
822 CurrShell aCurr(this);
823 SwCallLink aLk(*this); // watch Cursor-Moves
824 SwCursorSaveState aSaveState(*pCursor);
826 pCursor->GetPoint()->Assign(rTextFootnote.GetTextNode(),
827 rTextFootnote.GetStart());
828 bRet = !pCursor->IsSelOvr();
829 if (bRet)
830 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
831 return bRet;
834 bool SwCursorShell::GotoFormatContentControl(const SwFormatContentControl& rContentControl)
836 std::shared_ptr<SwContentControl> pContentControl = rContentControl.GetContentControl();
837 const SwTextContentControl* pTextContentControl = pContentControl->GetTextAttr();
838 if (!pTextContentControl)
839 return false;
841 CurrShell aCurr(this);
842 SwCallLink aLink(*this);
844 SwCursor* pCursor = getShellCursor(true);
845 SwCursorSaveState aSaveState(*pCursor);
847 SwTextNode* pTextNode = pContentControl->GetTextNode();
848 // Don't select the text attribute itself at the start.
849 sal_Int32 nStart = pTextContentControl->GetStart() + 1;
850 pCursor->GetPoint()->Assign(*pTextNode, nStart);
852 bool bRet = true;
853 // select contents for certain controls or conditions
854 if (pContentControl->GetShowingPlaceHolder() || pContentControl->GetCheckbox()
855 || pContentControl->GetSelectedListItem() || pContentControl->GetSelectedDate())
857 pCursor->SetMark();
858 // Don't select the CH_TXTATR_BREAKWORD itself at the end.
859 sal_Int32 nEnd = *pTextContentControl->End() - 1;
860 pCursor->GetMark()->Assign(*pTextNode, nEnd);
861 bRet = !pCursor->IsSelOvr();
863 else
864 ClearMark();
866 if (bRet)
868 UpdateCursor(SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE
869 | SwCursorShell::READONLY);
872 return bRet;
876 * Go to the next (or previous) form control, based first on tabIndex and then paragraph position,
877 * where a tabIndex of 1 is first, 0 is last, and -1 is excluded.
879 void SwCursorShell::GotoFormControl(bool bNext)
881 // (note: this only applies to modern content controls and legacy fieldmarks,
882 // since activeX richText controls aren't exposed to SW keystrokes)
884 struct FormControlSort
886 bool operator()(std::pair<const SwPosition&, sal_uInt32> rLHS,
887 std::pair<const SwPosition&, sal_uInt32> rRHS) const
889 assert(rLHS.second && rRHS.second && "tabIndex zero must be changed to SAL_MAX_UINT32");
890 //first compare tabIndexes where 1 has the priority.
891 if (rLHS.second < rRHS.second)
892 return true;
893 if (rLHS.second > rRHS.second)
894 return false;
896 // when tabIndexes are equal (and they usually are) then sort by paragraph position
897 return rLHS.first < rRHS.first;
900 std::map<std::pair<SwPosition, sal_uInt32>,
901 std::pair<SwTextContentControl*, sw::mark::IFieldmark*>, FormControlSort> aFormMap;
903 // add all of the eligible modern Content Controls into a sorted map
904 SwContentControlManager& rManager = GetDoc()->GetContentControlManager();
905 for (size_t i = 0; i < rManager.GetCount(); ++i)
907 SwTextContentControl* pTCC = rManager.UnsortedGet(i);
908 if (!pTCC || !pTCC->GetTextNode())
909 continue;
910 auto pCC = pTCC->GetContentControl().GetContentControl();
912 // -1 indicates the control should not participate in keyboard tab navigation
913 if (pCC && pCC->GetTabIndex() == SAL_MAX_UINT32)
914 continue;
916 const SwPosition nPos(*pTCC->GetTextNode(), pTCC->GetStart());
918 // since 0 is the lowest priority (1 is the highest), and -1 has already been excluded,
919 // use SAL_MAX_UINT32 as zero's tabIndex so that automatic sorting is correct.
920 sal_uInt32 nTabIndex = pCC && pCC->GetTabIndex() ? pCC->GetTabIndex() : SAL_MAX_UINT32;
922 const std::pair<SwTextContentControl*, sw::mark::IFieldmark*> pFormControl(pTCC, nullptr);
923 aFormMap[std::make_pair(nPos, nTabIndex)] = pFormControl;
926 if (aFormMap.begin() == aFormMap.end())
928 // only legacy fields exist. Avoid reprocessing everything and use legacy code path.
929 GotoFieldmark(bNext ? GetFieldmarkAfter(/*Loop=*/true) : GetFieldmarkBefore(/*Loop=*/true));
930 return;
933 // add all of the legacy form field controls into the sorted map
934 IDocumentMarkAccess* pMarkAccess = GetDoc()->getIDocumentMarkAccess();
935 for (auto it = pMarkAccess->getFieldmarksBegin(); it != pMarkAccess->getFieldmarksEnd(); ++it)
937 auto pFieldMark = dynamic_cast<sw::mark::IFieldmark*>(*it);
938 assert(pFieldMark);
939 std::pair<SwTextContentControl*, sw::mark::IFieldmark*> pFormControl(nullptr, pFieldMark);
940 // legacy form fields do not have (functional) tabIndexes - use lowest priority for them
941 aFormMap[std::make_pair((*it)->GetMarkStart(), SAL_MAX_UINT32)] = pFormControl;
944 if (aFormMap.begin() == aFormMap.end())
945 return;
947 // Identify the current location in the document, and the current tab index priority
949 // A content control could contain a Fieldmark, so check for legacy fieldmarks first
950 sw::mark::IFieldmark* pFieldMark = GetCurrentFieldmark();
951 SwTextContentControl* pTCC = !pFieldMark ? CursorInsideContentControl() : nullptr;
953 auto pCC = pTCC ? pTCC->GetContentControl().GetContentControl() : nullptr;
954 const sal_Int32 nCurTabIndex = pCC && pCC->GetTabIndex() ? pCC->GetTabIndex() : SAL_MAX_UINT32;
956 SwPosition nCurPos(*GetCursor()->GetPoint());
957 if (pFieldMark)
958 nCurPos = pFieldMark->GetMarkStart();
959 else if (pTCC && pTCC->GetTextNode())
960 nCurPos = SwPosition(*pTCC->GetTextNode(), pTCC->GetStart());
962 // Find the previous (or next) tab control and navigate to it
963 const std::pair<SwPosition, sal_uInt32> nOldPos(nCurPos, nCurTabIndex);
965 // lower_bound acts like find, and returns a pointer to nFindPos if it exists,
966 // otherwise it will point to the previous entry.
967 auto aNewPos = aFormMap.lower_bound(nOldPos);
968 if (bNext && aNewPos != aFormMap.end())
969 ++aNewPos;
970 else if (!bNext && aNewPos != aFormMap.end() && aNewPos->first == nOldPos)
972 // Found the current position - need to return previous
973 if (aNewPos == aFormMap.begin())
974 aNewPos = aFormMap.end(); // prepare to loop around
975 else
976 --aNewPos;
979 if (aNewPos == aFormMap.end())
981 // Loop around to the other side
982 if (bNext)
983 aNewPos = aFormMap.begin();
984 else
985 --aNewPos;
988 // the entry contains a pointer to either a Content Control (first) or Fieldmark (second)
989 if (aNewPos->second.first)
991 auto& rFCC = static_cast<SwFormatContentControl&>(aNewPos->second.first->GetAttr());
992 GotoFormatContentControl(rFCC);
994 else
996 assert(aNewPos->second.second);
997 GotoFieldmark(aNewPos->second.second);
1001 bool SwCursorShell::GotoFormatField( const SwFormatField& rField )
1003 SwTextField const*const pTextField(rField.GetTextField());
1004 if (!pTextField
1005 || (GetLayout()->IsHideRedlines()
1006 && sw::IsFieldDeletedInModel(
1007 GetDoc()->getIDocumentRedlineAccess(), *pTextField)))
1008 return false;
1010 CurrShell aCurr( this );
1011 SwCallLink aLk( *this ); // watch Cursor-Moves
1013 SwCursor* pCursor = getShellCursor( true );
1014 SwCursorSaveState aSaveState( *pCursor );
1016 SwTextNode* pTNd = pTextField->GetpTextNode();
1017 pCursor->GetPoint()->Assign(*pTNd, pTextField->GetStart() );
1019 bool bRet = !pCursor->IsSelOvr();
1020 if( bRet )
1021 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
1022 return bRet;
1025 SwTextField * SwCursorShell::GetTextFieldAtPos(
1026 const SwPosition* pPos,
1027 ::sw::GetTextAttrMode const eMode)
1029 SwTextField* pTextField = nullptr;
1031 SwTextNode * const pNode = pPos->GetNode().GetTextNode();
1032 if ( pNode != nullptr )
1034 pTextField = pNode->GetFieldTextAttrAt(pPos->GetContentIndex(), eMode);
1037 return pTextField;
1040 SwTextField* SwCursorShell::GetTextFieldAtCursor(
1041 const SwPaM* pCursor,
1042 ::sw::GetTextAttrMode const eMode)
1044 SwTextField* pTextField = GetTextFieldAtPos(pCursor->Start(), eMode);
1045 if ( !pTextField
1046 || pCursor->Start()->GetNode() != pCursor->End()->GetNode() )
1047 return nullptr;
1049 SwTextField* pFieldAtCursor = nullptr;
1050 const sal_Int32 nTextFieldLength =
1051 pTextField->End() != nullptr
1052 ? *(pTextField->End()) - pTextField->GetStart()
1053 : 1;
1054 if ( ( pCursor->End()->GetContentIndex() - pCursor->Start()->GetContentIndex() ) <= nTextFieldLength )
1056 pFieldAtCursor = pTextField;
1059 return pFieldAtCursor;
1062 SwField* SwCursorShell::GetFieldAtCursor(
1063 const SwPaM *const pCursor,
1064 const bool bIncludeInputFieldAtStart)
1066 SwTextField *const pField(GetTextFieldAtCursor(pCursor,
1067 bIncludeInputFieldAtStart ? ::sw::GetTextAttrMode::Default : ::sw::GetTextAttrMode::Expand));
1068 return pField
1069 ? const_cast<SwField*>(pField->GetFormatField().GetField())
1070 : nullptr;
1073 SwField* SwCursorShell::GetCurField( const bool bIncludeInputFieldAtStart ) const
1075 SwPaM* pCursor = GetCursor();
1076 if ( pCursor->IsMultiSelection() )
1078 // multi selection not handled.
1079 return nullptr;
1082 SwField* pCurField = GetFieldAtCursor( pCursor, bIncludeInputFieldAtStart );
1083 if ( pCurField != nullptr
1084 && SwFieldIds::Table == pCurField->GetTyp()->Which() )
1086 // table formula? convert internal name into external
1087 const SwTableNode* pTableNd = IsCursorInTable();
1088 static_cast<SwTableField*>(pCurField)->PtrToBoxNm( pTableNd ? &pTableNd->GetTable() : nullptr );
1091 return pCurField;
1094 bool SwCursorShell::CursorInsideInputField() const
1096 for(SwPaM& rCursor : GetCursor()->GetRingContainer())
1098 if (dynamic_cast<const SwTextInputField*>(GetTextFieldAtCursor(&rCursor, ::sw::GetTextAttrMode::Parent)))
1099 return true;
1101 return false;
1104 SwTextContentControl* SwCursorShell::CursorInsideContentControl() const
1106 for (SwPaM& rCursor : GetCursor()->GetRingContainer())
1108 const SwPosition* pStart = rCursor.Start();
1109 SwTextNode* pTextNode = pStart->GetNode().GetTextNode();
1110 if (!pTextNode)
1112 continue;
1115 sal_Int32 nIndex = pStart->GetContentIndex();
1116 if (SwTextAttr* pAttr = pTextNode->GetTextAttrAt(nIndex, RES_TXTATR_CONTENTCONTROL, ::sw::GetTextAttrMode::Parent))
1118 return static_txtattr_cast<SwTextContentControl*>(pAttr);
1122 return nullptr;
1125 bool SwCursorShell::PosInsideInputField( const SwPosition& rPos )
1127 return dynamic_cast<const SwTextInputField*>(GetTextFieldAtPos(&rPos, ::sw::GetTextAttrMode::Parent)) != nullptr;
1130 bool SwCursorShell::DocPtInsideInputField( const Point& rDocPt ) const
1132 SwPosition aPos( *(GetCursor()->Start()) );
1133 Point aDocPt( rDocPt );
1134 if ( GetLayout()->GetModelPositionForViewPoint( &aPos, aDocPt ) )
1136 return PosInsideInputField( aPos );
1138 return false;
1141 sal_Int32 SwCursorShell::StartOfInputFieldAtPos( const SwPosition& rPos )
1143 const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextFieldAtPos(&rPos, ::sw::GetTextAttrMode::Default));
1144 assert(pTextInputField != nullptr
1145 && "<SwEditShell::StartOfInputFieldAtPos(..)> - no Input Field at given position");
1146 return pTextInputField->GetStart();
1149 sal_Int32 SwCursorShell::EndOfInputFieldAtPos( const SwPosition& rPos )
1151 const SwTextInputField* pTextInputField = dynamic_cast<const SwTextInputField*>(GetTextFieldAtPos(&rPos, ::sw::GetTextAttrMode::Default));
1152 assert(pTextInputField != nullptr
1153 && "<SwEditShell::EndOfInputFieldAtPos(..)> - no Input Field at given position");
1154 return *(pTextInputField->End());
1157 void SwCursorShell::GotoOutline( SwOutlineNodes::size_type nIdx )
1159 SwCursor* pCursor = getShellCursor( true );
1161 CurrShell aCurr( this );
1162 SwCallLink aLk( *this ); // watch Cursor-Moves
1163 SwCursorSaveState aSaveState( *pCursor );
1165 const SwNodes& rNds = GetDoc()->GetNodes();
1166 SwTextNode* pTextNd = rNds.GetOutLineNds()[ nIdx ]->GetTextNode();
1167 pCursor->GetPoint()->Assign(*pTextNd);
1169 if( !pCursor->IsSelOvr() )
1170 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
1173 bool SwCursorShell::GotoOutline( const OUString& rName )
1175 SwCursor* pCursor = getShellCursor( true );
1177 CurrShell aCurr( this );
1178 SwCallLink aLk( *this ); // watch Cursor-Moves
1179 SwCursorSaveState aSaveState( *pCursor );
1181 bool bRet = false;
1182 if (mxDoc->GotoOutline(*pCursor->GetPoint(), rName, GetLayout())
1183 && !pCursor->IsSelOvr())
1185 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
1186 bRet = true;
1188 return bRet;
1191 /// jump to next node with outline num.
1192 bool SwCursorShell::GotoNextOutline()
1194 const SwNodes& rNds = GetDoc()->GetNodes();
1196 if ( rNds.GetOutLineNds().empty() )
1198 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
1199 return false;
1202 SwCursor* pCursor = getShellCursor( true );
1203 SwNode* pNd = &(pCursor->GetPointNode());
1204 SwOutlineNodes::size_type nPos;
1205 bool bUseFirst = !rNds.GetOutLineNds().Seek_Entry( pNd, &nPos );
1206 SwOutlineNodes::size_type const nStartPos(nPos);
1210 if (!bUseFirst)
1212 ++nPos;
1214 if (rNds.GetOutLineNds().size() <= nPos)
1216 nPos = 0;
1219 if (bUseFirst)
1221 bUseFirst = false;
1223 else
1225 if (nPos == nStartPos)
1227 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
1228 return false;
1232 pNd = rNds.GetOutLineNds()[ nPos ];
1234 while (!sw::IsParaPropsNode(*GetLayout(), *pNd->GetTextNode()));
1236 if (nPos < nStartPos)
1238 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::EndWrapped );
1240 else
1242 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
1245 CurrShell aCurr( this );
1246 SwCallLink aLk( *this ); // watch Cursor-Moves
1247 SwCursorSaveState aSaveState( *pCursor );
1248 pCursor->GetPoint()->Assign(*pNd);
1250 bool bRet = !pCursor->IsSelOvr();
1251 if( bRet )
1252 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
1253 return bRet;
1256 /// jump to previous node with outline num.
1257 bool SwCursorShell::GotoPrevOutline()
1259 const SwNodes& rNds = GetDoc()->GetNodes();
1261 if ( rNds.GetOutLineNds().empty() )
1263 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
1264 return false;
1267 SwCursor* pCursor = getShellCursor( true );
1268 SwNode* pNd = &(pCursor->GetPointNode());
1269 SwOutlineNodes::size_type nPos;
1270 (void)rNds.GetOutLineNds().Seek_Entry(pNd, &nPos);
1271 SwOutlineNodes::size_type const nStartPos(nPos);
1275 if (nPos == 0)
1277 nPos = rNds.GetOutLineNds().size() - 1;
1279 else
1281 --nPos; // before
1283 if (nPos == nStartPos)
1285 pNd = nullptr;
1286 break;
1289 pNd = rNds.GetOutLineNds()[ nPos ];
1291 while (!sw::IsParaPropsNode(*GetLayout(), *pNd->GetTextNode()));
1293 if (!pNd)
1295 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::NavElementNotFound );
1296 return false;
1299 if (nStartPos < nPos)
1301 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::StartWrapped );
1303 else
1305 SvxSearchDialogWrapper::SetSearchLabel( SearchLabel::Empty );
1307 CurrShell aCurr( this );
1308 SwCallLink aLk( *this ); // watch Cursor-Moves
1309 SwCursorSaveState aSaveState( *pCursor );
1310 pCursor->GetPoint()->Assign(*pNd);
1312 bool bRet = !pCursor->IsSelOvr();
1313 if( bRet )
1314 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
1315 return bRet;
1318 /// search "outline position" before previous outline node at given level
1319 SwOutlineNodes::size_type SwCursorShell::GetOutlinePos(sal_uInt8 nLevel, SwPaM* pPaM)
1321 SwPaM* pCursor = pPaM ? pPaM : getShellCursor(true);
1322 const SwNodes& rNds = GetDoc()->GetNodes();
1324 SwNode* pNd = &(pCursor->GetPointNode());
1325 SwOutlineNodes::size_type nPos;
1326 if( rNds.GetOutLineNds().Seek_Entry( pNd, &nPos ))
1327 nPos++; // is at correct position; take next for while
1329 while( nPos-- ) // check the one in front of the current
1331 pNd = rNds.GetOutLineNds()[ nPos ];
1333 if (sw::IsParaPropsNode(*GetLayout(), *pNd->GetTextNode())
1334 && pNd->GetTextNode()->GetAttrOutlineLevel()-1 <= nLevel)
1336 if (pNd->GetIndex() < rNds.GetEndOfExtras().GetIndex()
1337 && pCursor->GetPointNode().GetIndex() > rNds.GetEndOfExtras().GetIndex())
1339 // node found in extras but cursor position is not in extras
1340 return SwOutlineNodes::npos;
1342 return nPos;
1345 return SwOutlineNodes::npos; // no more left
1348 void SwCursorShell::MakeOutlineSel(SwOutlineNodes::size_type nSttPos, SwOutlineNodes::size_type nEndPos,
1349 bool bWithChildren , bool bKillPams)
1351 const SwNodes& rNds = GetDoc()->GetNodes();
1352 const SwOutlineNodes& rOutlNds = rNds.GetOutLineNds();
1353 if( rOutlNds.empty() )
1354 return;
1356 CurrShell aCurr( this );
1357 SwCallLink aLk( *this ); // watch Cursor-Moves
1359 if( nSttPos > nEndPos ) // parameters switched?
1361 OSL_ENSURE( false, "Start > End for array access" );
1362 std::swap(nSttPos, nEndPos);
1365 SwNode* pSttNd = rOutlNds[ nSttPos ];
1366 SwNode* pEndNd = rOutlNds[ nEndPos ];
1368 if( bWithChildren )
1370 const int nLevel = pEndNd->GetTextNode()->GetAttrOutlineLevel()-1;
1371 for( ++nEndPos; nEndPos < rOutlNds.size(); ++nEndPos )
1373 pEndNd = rOutlNds[ nEndPos ];
1374 const int nNxtLevel = pEndNd->GetTextNode()->GetAttrOutlineLevel()-1;
1375 if( nNxtLevel <= nLevel )
1376 break; // EndPos is now on the next one
1379 // if without children then set onto next one
1380 else if( ++nEndPos < rOutlNds.size() )
1381 pEndNd = rOutlNds[ nEndPos ];
1383 if( nEndPos == rOutlNds.size() ) // no end found
1384 pEndNd = &rNds.GetEndOfContent();
1386 if( bKillPams )
1387 KillPams();
1389 SwCursorSaveState aSaveState( *m_pCurrentCursor );
1391 // set end to the end of the previous content node
1392 m_pCurrentCursor->GetPoint()->Assign(*pSttNd);
1393 m_pCurrentCursor->SetMark();
1394 m_pCurrentCursor->GetPoint()->Assign(*pEndNd);
1395 m_pCurrentCursor->Move( fnMoveBackward, GoInNode ); // end of predecessor
1397 // and everything is already selected
1398 bool bRet = !m_pCurrentCursor->IsSelOvr();
1399 if( bRet )
1400 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
1403 /// jump to reference marker
1404 bool SwCursorShell::GotoRefMark( const OUString& rRefMark, sal_uInt16 nSubType,
1405 sal_uInt16 nSeqNo )
1407 CurrShell aCurr( this );
1408 SwCallLink aLk( *this ); // watch Cursor-Moves
1409 SwCursorSaveState aSaveState( *m_pCurrentCursor );
1411 sal_Int32 nPos = -1;
1412 SwTextNode* pTextNd = SwGetRefFieldType::FindAnchor( GetDoc(), rRefMark,
1413 nSubType, nSeqNo, &nPos, nullptr, GetLayout());
1414 if( !pTextNd || !pTextNd->GetNodes().IsDocNodes() )
1415 return false;
1417 m_pCurrentCursor->GetPoint()->Assign(*pTextNd, nPos );
1419 if( m_pCurrentCursor->IsSelOvr() )
1420 return false;
1422 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
1423 return true;
1426 bool SwCursorShell::IsPageAtPos( const Point &rPt ) const
1428 if( GetLayout() )
1429 return nullptr != GetLayout()->GetPageAtPos( rPt );
1430 return false;
1433 bool SwCursorShell::GetContentAtPos( const Point& rPt,
1434 SwContentAtPos& rContentAtPos,
1435 bool bSetCursor,
1436 SwRect* pFieldRect )
1438 CurrShell aCurr( this );
1439 bool bRet = false;
1441 if( IsTableMode() )
1443 rContentAtPos.eContentAtPos = IsAttrAtPos::NONE;
1444 rContentAtPos.aFnd.pField = nullptr;
1445 return false;
1448 Point aPt( rPt );
1449 SwPosition aPos( *m_pCurrentCursor->GetPoint() );
1451 SwTextNode* pTextNd;
1452 SwCursorMoveState aTmpState;
1453 aTmpState.m_bFieldInfo = true;
1454 aTmpState.m_bExactOnly = !( IsAttrAtPos::Outline & rContentAtPos.eContentAtPos );
1455 aTmpState.m_bContentCheck = bool(IsAttrAtPos::ContentCheck & rContentAtPos.eContentAtPos);
1456 aTmpState.m_bSetInReadOnly = IsReadOnlyAvailable();
1458 SwSpecialPos aSpecialPos;
1459 aTmpState.m_pSpecialPos = ( IsAttrAtPos::SmartTag & rContentAtPos.eContentAtPos ) ?
1460 &aSpecialPos : nullptr;
1462 const bool bCursorFoundExact = GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aTmpState );
1463 pTextNd = aPos.GetNode().GetTextNode();
1465 const SwNodes& rNds = GetDoc()->GetNodes();
1466 if( pTextNd
1467 && IsAttrAtPos::Outline & rContentAtPos.eContentAtPos
1468 && !rNds.GetOutLineNds().empty() )
1470 // only for nodes in outline nodes
1471 SwOutlineNodes::size_type nPos = 0;
1472 bool bFoundOutline = rNds.GetOutLineNds().Seek_Entry(pTextNd, &nPos);
1473 if (!bFoundOutline && nPos && (IsAttrAtPos::AllowContaining & rContentAtPos.eContentAtPos))
1475 // nPos points to the first found outline node not before pTextNd, or to end();
1476 // when bFoundOutline is false, and nPos is not 0, it means that there were
1477 // outline nodes before pTextNd, and nPos-1 points to the last of those.
1478 pTextNd = rNds.GetOutLineNds()[nPos - 1]->GetTextNode();
1479 bFoundOutline = true;
1481 if (bFoundOutline)
1483 rContentAtPos.eContentAtPos = IsAttrAtPos::Outline;
1484 rContentAtPos.sStr = sw::GetExpandTextMerged(GetLayout(), *pTextNd, true, false, ExpandMode::ExpandFootnote);
1485 rContentAtPos.aFnd.pNode = pTextNd;
1486 bRet = true;
1489 else if ( IsAttrAtPos::ContentCheck & rContentAtPos.eContentAtPos
1490 && bCursorFoundExact )
1492 bRet = true;
1494 else if( pTextNd
1495 && IsAttrAtPos::NumLabel & rContentAtPos.eContentAtPos)
1497 bRet = aTmpState.m_bInNumPortion;
1498 rContentAtPos.aFnd.pNode = sw::GetParaPropsNode(*GetLayout(), aPos.GetNode());
1500 Size aSizeLogic(aTmpState.m_nInNumPortionOffset, 0);
1501 Size aSizePixel = GetWin()->LogicToPixel(aSizeLogic);
1502 rContentAtPos.nDist = aSizePixel.Width();
1504 else if( bCursorFoundExact && pTextNd )
1506 SwContentFrame *pFrame(nullptr);
1507 if( !aTmpState.m_bPosCorr )
1509 SwTextAttr* pTextAttr;
1510 if ( IsAttrAtPos::SmartTag & rContentAtPos.eContentAtPos
1511 && !aTmpState.m_bFootnoteNoInfo )
1513 const SwWrongList* pSmartTagList = pTextNd->GetSmartTags();
1514 sal_Int32 nCurrent = aPos.GetContentIndex();
1515 const sal_Int32 nBegin = nCurrent;
1516 sal_Int32 nLen = 1;
1518 if (pSmartTagList && pSmartTagList->InWrongWord(nCurrent, nLen) && !pTextNd->IsSymbolAt(nBegin))
1520 const sal_uInt16 nIndex = pSmartTagList->GetWrongPos( nBegin );
1521 const SwWrongList* pSubList = pSmartTagList->SubList( nIndex );
1522 if ( pSubList )
1524 nCurrent = aTmpState.m_pSpecialPos->nCharOfst;
1526 if ( pSubList->InWrongWord( nCurrent, nLen ) )
1527 bRet = true;
1529 else
1530 bRet = true;
1532 if( bRet && bSetCursor )
1534 SwCursorSaveState aSaveState( *m_pCurrentCursor );
1535 SwCallLink aLk( *this ); // watch Cursor-Moves
1536 m_pCurrentCursor->DeleteMark();
1537 *m_pCurrentCursor->GetPoint() = aPos;
1538 if( m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection | SwCursorSelOverFlags::Toggle) )
1539 bRet = false;
1540 else
1541 UpdateCursor();
1543 if( bRet )
1545 rContentAtPos.eContentAtPos = IsAttrAtPos::SmartTag;
1547 std::pair<Point, bool> tmp(aPt, true);
1548 if (pFieldRect)
1550 pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
1551 if (pFrame)
1552 pFrame->GetCharRect( *pFieldRect, aPos, &aTmpState );
1558 if ( !bRet
1559 && ( IsAttrAtPos::Field | IsAttrAtPos::ClickField ) & rContentAtPos.eContentAtPos
1560 && !aTmpState.m_bFootnoteNoInfo )
1562 pTextAttr = pTextNd->GetFieldTextAttrAt( aPos.GetContentIndex() );
1563 const SwField* pField = pTextAttr != nullptr
1564 ? pTextAttr->GetFormatField().GetField()
1565 : nullptr;
1566 if ( IsAttrAtPos::ClickField & rContentAtPos.eContentAtPos
1567 && pField && !pField->HasClickHdl() )
1569 pField = nullptr;
1572 if ( pField )
1574 if (pFieldRect)
1576 std::pair<Point, bool> tmp(aPt, true);
1577 pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
1578 if (pFrame)
1580 //tdf#116397 now that we looking for the bounds of the field drop the SmartTag
1581 //index within field setting so we don't the bounds of the char within the field
1582 SwSpecialPos* pSpecialPos = aTmpState.m_pSpecialPos;
1583 aTmpState.m_pSpecialPos = nullptr;
1584 pFrame->GetCharRect( *pFieldRect, aPos, &aTmpState );
1585 aTmpState.m_pSpecialPos = pSpecialPos;
1589 if( bSetCursor )
1591 SwCallLink aLk( *this ); // watch Cursor-Moves
1592 SwCursorSaveState aSaveState( *m_pCurrentCursor );
1593 m_pCurrentCursor->DeleteMark();
1594 *m_pCurrentCursor->GetPoint() = aPos;
1595 if( m_pCurrentCursor->IsSelOvr() )
1597 // allow click fields in protected sections
1598 // only placeholder is not possible
1599 if( IsAttrAtPos::Field & rContentAtPos.eContentAtPos
1600 || SwFieldIds::JumpEdit == pField->Which() )
1601 pField = nullptr;
1603 else
1604 UpdateCursor();
1606 else if( SwFieldIds::Table == pField->Which() &&
1607 static_cast<const SwTableField*>(pField)->IsIntrnlName() )
1609 // create from internal (for CORE) the external
1610 // (for UI) formula
1611 const SwTableNode* pTableNd = pTextNd->FindTableNode();
1612 if( pTableNd ) // is in a table
1613 const_cast<SwTableField*>(static_cast<const SwTableField*>(pField))->PtrToBoxNm( &pTableNd->GetTable() );
1617 if( pField )
1619 rContentAtPos.aFnd.pField = pField;
1620 rContentAtPos.pFndTextAttr = pTextAttr;
1621 rContentAtPos.eContentAtPos = IsAttrAtPos::Field;
1622 bRet = true;
1626 if( !bRet && IsAttrAtPos::FormControl & rContentAtPos.eContentAtPos )
1628 IDocumentMarkAccess* pMarksAccess = GetDoc()->getIDocumentMarkAccess( );
1629 sw::mark::IFieldmark* pFieldBookmark = pMarksAccess->getInnerFieldmarkFor(aPos);
1630 if (bCursorFoundExact && pFieldBookmark)
1632 rContentAtPos.eContentAtPos = IsAttrAtPos::FormControl;
1633 rContentAtPos.aFnd.pFieldmark = pFieldBookmark;
1634 bRet=true;
1638 if (!bRet && rContentAtPos.eContentAtPos & IsAttrAtPos::ContentControl)
1640 SwTextAttr* pAttr = pTextNd->GetTextAttrAt(
1641 aPos.GetContentIndex(), RES_TXTATR_CONTENTCONTROL, ::sw::GetTextAttrMode::Parent);
1642 if (pAttr)
1644 rContentAtPos.eContentAtPos = IsAttrAtPos::ContentControl;
1645 rContentAtPos.pFndTextAttr = pAttr;
1646 bRet = true;
1650 if( !bRet && IsAttrAtPos::Ftn & rContentAtPos.eContentAtPos )
1652 if( aTmpState.m_bFootnoteNoInfo )
1654 // over the footnote's char
1655 bRet = true;
1656 if( bSetCursor )
1658 *m_pCurrentCursor->GetPoint() = aPos;
1659 if( !GotoFootnoteAnchor() )
1660 bRet = false;
1662 if( bRet )
1663 rContentAtPos.eContentAtPos = IsAttrAtPos::Ftn;
1665 else if ( nullptr != ( pTextAttr = pTextNd->GetTextAttrForCharAt(
1666 aPos.GetContentIndex(), RES_TXTATR_FTN )) )
1668 bRet = true;
1669 if( bSetCursor )
1671 if (SwWrtShell* pWrtSh = dynamic_cast<SwWrtShell*>(this))
1672 pWrtSh->addCurrentPosition();
1674 SwCallLink aLk( *this ); // watch Cursor-Moves
1675 SwCursorSaveState aSaveState( *m_pCurrentCursor );
1676 m_pCurrentCursor->GetPoint()->Assign( *static_cast<SwTextFootnote*>(pTextAttr)->GetStartNode() );
1677 SwContentNode* pCNd = GetDoc()->GetNodes().GoNextSection(
1678 m_pCurrentCursor->GetPoint(),
1679 true, !IsReadOnlyAvailable() );
1681 if( pCNd )
1683 if( m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
1684 SwCursorSelOverFlags::Toggle ))
1685 bRet = false;
1686 else
1687 UpdateCursor();
1689 else
1690 bRet = false;
1693 if( bRet )
1695 rContentAtPos.eContentAtPos = IsAttrAtPos::Ftn;
1696 rContentAtPos.pFndTextAttr = pTextAttr;
1697 rContentAtPos.aFnd.pAttr = &pTextAttr->GetAttr();
1699 if (pFieldRect)
1701 std::pair<Point, bool> tmp(aPt, true);
1702 pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
1703 if (pFrame)
1704 pFrame->GetCharRect( *pFieldRect, aPos, &aTmpState );
1710 if( !bRet
1711 && ( IsAttrAtPos::ToxMark | IsAttrAtPos::RefMark ) & rContentAtPos.eContentAtPos
1712 && !aTmpState.m_bFootnoteNoInfo )
1714 pTextAttr = nullptr;
1715 if( IsAttrAtPos::ToxMark & rContentAtPos.eContentAtPos )
1717 std::vector<SwTextAttr *> const marks(
1718 pTextNd->GetTextAttrsAt(
1719 aPos.GetContentIndex(), RES_TXTATR_TOXMARK));
1720 if (!marks.empty())
1721 { // hmm... can only return 1 here
1722 pTextAttr = *marks.begin();
1726 if( !pTextAttr &&
1727 IsAttrAtPos::RefMark & rContentAtPos.eContentAtPos )
1729 std::vector<SwTextAttr *> const marks(
1730 pTextNd->GetTextAttrsAt(
1731 aPos.GetContentIndex(), RES_TXTATR_REFMARK));
1732 if (!marks.empty())
1733 { // hmm... can only return 1 here
1734 pTextAttr = *marks.begin();
1738 if( pTextAttr )
1740 bRet = true;
1741 if( bSetCursor )
1743 SwCallLink aLk( *this ); // watch Cursor-Moves
1744 SwCursorSaveState aSaveState( *m_pCurrentCursor );
1745 m_pCurrentCursor->DeleteMark();
1746 *m_pCurrentCursor->GetPoint() = aPos;
1747 if( m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection | SwCursorSelOverFlags::Toggle ) )
1748 bRet = false;
1749 else
1750 UpdateCursor();
1753 if( bRet )
1755 const sal_Int32* pEnd = pTextAttr->GetEnd();
1756 if( pEnd )
1757 rContentAtPos.sStr =
1758 pTextNd->GetExpandText(GetLayout(), pTextAttr->GetStart(), *pEnd - pTextAttr->GetStart());
1759 else if( RES_TXTATR_TOXMARK == pTextAttr->Which())
1760 rContentAtPos.sStr =
1761 pTextAttr->GetTOXMark().GetAlternativeText();
1763 rContentAtPos.eContentAtPos =
1764 RES_TXTATR_TOXMARK == pTextAttr->Which()
1765 ? IsAttrAtPos::ToxMark
1766 : IsAttrAtPos::RefMark;
1767 rContentAtPos.pFndTextAttr = pTextAttr;
1768 rContentAtPos.aFnd.pAttr = &pTextAttr->GetAttr();
1770 std::pair<Point, bool> tmp(aPt, true);
1771 if (pFieldRect)
1773 pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
1774 if (pFrame)
1775 pFrame->GetCharRect( *pFieldRect, aPos, &aTmpState );
1781 if ( !bRet
1782 && IsAttrAtPos::InetAttr & rContentAtPos.eContentAtPos
1783 && !aTmpState.m_bFootnoteNoInfo )
1785 sal_Int32 index = aPos.GetContentIndex();
1786 pTextAttr = pTextNd->GetTextAttrAt(index, RES_TXTATR_INETFMT);
1788 if(!pTextAttr && index > 0)
1789 pTextAttr = pTextNd->GetTextAttrAt(index - 1, RES_TXTATR_INETFMT);
1790 // "detect" only INetAttrs with URLs
1791 if( pTextAttr && !pTextAttr->GetINetFormat().GetValue().isEmpty() )
1793 bRet = true;
1794 if( bSetCursor )
1796 SwCursorSaveState aSaveState( *m_pCurrentCursor );
1797 SwCallLink aLk( *this ); // watch Cursor-Moves
1798 m_pCurrentCursor->DeleteMark();
1799 *m_pCurrentCursor->GetPoint() = aPos;
1800 if( m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
1801 SwCursorSelOverFlags::Toggle) )
1802 bRet = false;
1803 else
1804 UpdateCursor();
1806 if( bRet )
1808 const sal_Int32 nSt = pTextAttr->GetStart();
1809 const sal_Int32 nEnd = *pTextAttr->End();
1811 rContentAtPos.sStr = pTextNd->GetExpandText(GetLayout(), nSt, nEnd-nSt);
1813 rContentAtPos.aFnd.pAttr = &pTextAttr->GetAttr();
1814 rContentAtPos.eContentAtPos = IsAttrAtPos::InetAttr;
1815 rContentAtPos.pFndTextAttr = pTextAttr;
1817 if (pFieldRect)
1819 std::pair<Point, bool> tmp(aPt, true);
1820 pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
1821 if (pFrame)
1823 //get bounding box of range
1824 SwRect aStart;
1825 SwPosition aStartPos(*pTextNd, nSt);
1826 pFrame->GetCharRect(aStart, aStartPos, &aTmpState);
1827 SwRect aEnd;
1828 SwPosition aEndPos(*pTextNd, nEnd);
1829 pFrame->GetCharRect(aEnd, aEndPos, &aTmpState);
1830 if (aStart.Top() != aEnd.Top() || aStart.Bottom() != aEnd.Bottom())
1832 aStart.Left(pFrame->getFrameArea().Left());
1833 aEnd.Right(pFrame->getFrameArea().Right());
1835 *pFieldRect = aStart.Union(aEnd);
1842 if( !bRet && IsAttrAtPos::Redline & rContentAtPos.eContentAtPos )
1844 const SwRangeRedline* pRedl = GetDoc()->getIDocumentRedlineAccess().GetRedline(aPos, nullptr);
1846 if( pRedl )
1848 rContentAtPos.aFnd.pRedl = pRedl;
1849 rContentAtPos.eContentAtPos = IsAttrAtPos::Redline;
1850 rContentAtPos.pFndTextAttr = nullptr;
1851 bRet = true;
1853 if (pFieldRect)
1855 std::pair<Point, bool> tmp(aPt, true);
1856 pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
1857 if( pFrame )
1859 // not sure if this should be limited to one
1860 // paragraph, or mark the entire redline; let's
1861 // leave it limited to one for now...
1862 sal_Int32 nStart;
1863 sal_Int32 nEnd;
1864 pRedl->CalcStartEnd(pTextNd->GetIndex(), nStart, nEnd);
1865 if (nStart == COMPLETE_STRING)
1867 // consistency: found pRedl, so there must be
1868 // something in pTextNd
1869 assert(nEnd != COMPLETE_STRING);
1870 nStart = 0;
1872 if (nEnd == COMPLETE_STRING)
1874 nEnd = pTextNd->Len();
1876 //get bounding box of range
1877 SwRect aStart;
1878 pFrame->GetCharRect(aStart, SwPosition(*pTextNd, nStart), &aTmpState);
1879 SwRect aEnd;
1880 pFrame->GetCharRect(aEnd, SwPosition(*pTextNd, nEnd), &aTmpState);
1881 if (aStart.Top() != aEnd.Top() || aStart.Bottom() != aEnd.Bottom())
1883 aStart.Left(pFrame->getFrameArea().Left());
1884 aEnd.Right(pFrame->getFrameArea().Right());
1886 *pFieldRect = aStart.Union(aEnd);
1893 if( !bRet && ( ( IsAttrAtPos::TableRedline & rContentAtPos.eContentAtPos ) ||
1894 ( IsAttrAtPos::TableColRedline & rContentAtPos.eContentAtPos ) ) )
1896 const SwTableNode* pTableNd;
1897 const SwTableBox* pBox;
1898 const SwTableLine* pTableLine;
1899 const SwStartNode* pSttNd = pTextNd->FindTableBoxStartNode();
1900 if( pSttNd && nullptr != ( pTableNd = pTextNd->FindTableNode()) &&
1901 nullptr != ( pBox = pTableNd->GetTable().GetTableBox(
1902 pSttNd->GetIndex() )) &&
1903 nullptr != ( pTableLine = pBox->GetUpper() ) &&
1904 ( RedlineType::None != pBox->GetRedlineType() ||
1905 RedlineType::None != pTableLine->GetRedlineType() ) )
1907 const SwRedlineTable& aRedlineTable = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
1908 if ( RedlineType::None != pTableLine->GetRedlineType() )
1910 SwRedlineTable::size_type nPos = 0;
1911 nPos = pTableLine->UpdateTextChangesOnly(nPos);
1912 if ( nPos != SwRedlineTable::npos )
1914 rContentAtPos.aFnd.pRedl = aRedlineTable[nPos];
1915 rContentAtPos.eContentAtPos = IsAttrAtPos::TableRedline;
1916 bRet = true;
1919 else
1921 SwRedlineTable::size_type n = 0;
1922 SwNodeIndex aIdx( *pSttNd, 1 );
1923 const SwPosition aBoxStart(aIdx);
1924 const SwRangeRedline* pFnd = aRedlineTable.FindAtPosition( aBoxStart, n, /*next=*/true );
1925 if( pFnd && RedlineType::Delete == pFnd->GetType() )
1927 rContentAtPos.aFnd.pRedl = aRedlineTable[n];
1928 rContentAtPos.eContentAtPos = IsAttrAtPos::TableColRedline;
1929 bRet = true;
1935 if( !bRet
1936 && ( IsAttrAtPos::TableBoxFml & rContentAtPos.eContentAtPos
1937 #ifdef DBG_UTIL
1938 || IsAttrAtPos::TableBoxValue & rContentAtPos.eContentAtPos
1939 #endif
1942 const SwTableNode* pTableNd;
1943 const SwTableBox* pBox;
1944 const SwStartNode* pSttNd = pTextNd->FindTableBoxStartNode();
1945 const SwTableBoxFormula* pItem;
1946 #ifdef DBG_UTIL
1947 const SwTableBoxValue* pItem2 = nullptr;
1948 #endif
1949 if( pSttNd && nullptr != ( pTableNd = pTextNd->FindTableNode()) &&
1950 nullptr != ( pBox = pTableNd->GetTable().GetTableBox(
1951 pSttNd->GetIndex() )) &&
1952 #ifdef DBG_UTIL
1953 ( (pItem = pBox->GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMULA, false )) ||
1954 (pItem2 = pBox->GetFrameFormat()->GetItemIfSet( RES_BOXATR_VALUE, false )) )
1955 #else
1956 (pItem = pBox->GetFrameFormat()->GetItemIfSet( RES_BOXATR_FORMULA, false ))
1957 #endif
1960 std::pair<Point, bool> tmp(aPt, true);
1961 SwFrame* pF = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
1962 if( pF )
1964 // then the CellFrame
1965 pFrame = static_cast<SwContentFrame*>(pF);
1966 while( pF && !pF->IsCellFrame() )
1967 pF = pF->GetUpper();
1970 if( aTmpState.m_bPosCorr )
1972 if( pF && !pF->getFrameArea().Contains( aPt ))
1973 pF = nullptr;
1975 else if( !pF )
1976 pF = pFrame;
1978 if( pF ) // only then it is valid
1980 // create from internal (for CORE) the external
1981 // (for UI) formula
1982 rContentAtPos.eContentAtPos = IsAttrAtPos::TableBoxFml;
1983 #ifdef DBG_UTIL
1984 if( pItem2 )
1985 rContentAtPos.eContentAtPos = IsAttrAtPos::TableBoxValue;
1986 else
1987 #endif
1988 const_cast<SwTableBoxFormula&>(*pItem).PtrToBoxNm( &pTableNd->GetTable() );
1990 bRet = true;
1991 if( bSetCursor )
1993 SwCallLink aLk( *this ); // watch Cursor-Moves
1994 SwCursorSaveState aSaveState( *m_pCurrentCursor );
1995 *m_pCurrentCursor->GetPoint() = aPos;
1996 if( m_pCurrentCursor->IsSelOvr( SwCursorSelOverFlags::CheckNodeSection |
1997 SwCursorSelOverFlags::Toggle) )
1998 bRet = false;
1999 else
2000 UpdateCursor();
2003 if( bRet )
2005 if( pFieldRect )
2007 *pFieldRect = pF->getFramePrintArea();
2008 *pFieldRect += pF->getFrameArea().Pos();
2010 rContentAtPos.pFndTextAttr = nullptr;
2011 rContentAtPos.aFnd.pAttr = pItem;
2017 #ifdef DBG_UTIL
2018 if( !bRet && IsAttrAtPos::CurrAttrs & rContentAtPos.eContentAtPos )
2020 const sal_Int32 n = aPos.GetContentIndex();
2021 SfxItemSetFixed<POOLATTR_BEGIN, POOLATTR_END - 1> aSet( GetDoc()->GetAttrPool() );
2022 if( pTextNd->GetpSwpHints() )
2024 for( size_t i = 0; i < pTextNd->GetSwpHints().Count(); ++i )
2026 const SwTextAttr* pHt = pTextNd->GetSwpHints().Get(i);
2027 const sal_Int32 nAttrStart = pHt->GetStart();
2028 if( nAttrStart > n ) // over the section
2029 break;
2031 if( nullptr != pHt->End() && (
2032 ( nAttrStart < n &&
2033 ( pHt->DontExpand() ? n < *pHt->End()
2034 : n <= *pHt->End() )) ||
2035 ( n == nAttrStart &&
2036 ( nAttrStart == *pHt->End() || !n ))) )
2038 aSet.Put( pHt->GetAttr() );
2041 if( pTextNd->HasSwAttrSet() &&
2042 pTextNd->GetpSwAttrSet()->Count() )
2044 SfxItemSet aFormatSet( pTextNd->GetSwAttrSet() );
2045 // remove all from format set that are also in TextSet
2046 aFormatSet.Differentiate( aSet );
2047 // now merge all together
2048 aSet.Put( aFormatSet );
2051 else
2052 pTextNd->SwContentNode::GetAttr( aSet );
2054 rContentAtPos.sStr = "Pos: (";
2055 rContentAtPos.sStr += OUString::number( sal_Int32(aPos.GetNodeIndex()));
2056 rContentAtPos.sStr += ":";
2057 rContentAtPos.sStr += OUString::number( aPos.GetContentIndex());
2058 rContentAtPos.sStr += ")";
2059 rContentAtPos.sStr += "\nParagraph Style: ";
2060 rContentAtPos.sStr += pTextNd->GetFormatColl()->GetName();
2061 if( pTextNd->GetCondFormatColl() )
2063 rContentAtPos.sStr += "\nConditional Style: " + pTextNd->GetCondFormatColl()->GetName();
2066 if( aSet.Count() )
2068 OUStringBuffer sAttrs;
2069 SfxItemIter aIter( aSet );
2070 const SfxPoolItem* pItem = aIter.GetCurItem();
2071 const IntlWrapper aInt(SvtSysLocale().GetUILanguageTag());
2074 if( !IsInvalidItem( pItem ))
2076 OUString aStr;
2077 GetDoc()->GetAttrPool().GetPresentation(*pItem,
2078 MapUnit::MapCM, aStr, aInt);
2079 if (!sAttrs.isEmpty())
2080 sAttrs.append(", ");
2081 sAttrs.append(aStr);
2083 pItem = aIter.NextItem();
2084 } while (pItem);
2085 if (!sAttrs.isEmpty())
2087 if( !rContentAtPos.sStr.isEmpty() )
2088 rContentAtPos.sStr += "\n";
2089 rContentAtPos.sStr += "Attr: " + sAttrs;
2092 bRet = true;
2093 rContentAtPos.eContentAtPos = IsAttrAtPos::CurrAttrs;
2095 #endif
2098 if( !bRet )
2100 rContentAtPos.eContentAtPos = IsAttrAtPos::NONE;
2101 rContentAtPos.aFnd.pField = nullptr;
2103 return bRet;
2106 // #i90516#
2107 const SwPostItField* SwCursorShell::GetPostItFieldAtCursor() const
2109 if ( IsTableMode() )
2110 return nullptr;
2112 const SwPosition* pCursorPos = GetCursor_()->GetPoint();
2113 const SwTextNode* pTextNd = pCursorPos->GetNode().GetTextNode();
2114 if ( !pTextNd )
2115 return nullptr;
2117 const SwPostItField* pPostItField = nullptr;
2118 SwTextAttr* pTextAttr = pTextNd->GetFieldTextAttrAt( pCursorPos->GetContentIndex() );
2119 const SwField* pField = pTextAttr != nullptr ? pTextAttr->GetFormatField().GetField() : nullptr;
2120 if ( pField && pField->Which()== SwFieldIds::Postit )
2122 pPostItField = static_cast<const SwPostItField*>(pField);
2125 return pPostItField;
2128 /// is the node in a protected section?
2129 bool SwContentAtPos::IsInProtectSect() const
2131 const SwTextNode* pNd = nullptr;
2132 if( pFndTextAttr )
2134 switch( eContentAtPos )
2136 case IsAttrAtPos::Field:
2137 case IsAttrAtPos::ClickField:
2138 pNd = static_txtattr_cast<SwTextField const*>(pFndTextAttr)->GetpTextNode();
2139 break;
2141 case IsAttrAtPos::Ftn:
2142 pNd = &static_cast<const SwTextFootnote*>(pFndTextAttr)->GetTextNode();
2143 break;
2145 case IsAttrAtPos::InetAttr:
2146 pNd = static_txtattr_cast<SwTextINetFormat const*>(pFndTextAttr)->GetpTextNode();
2147 break;
2149 default:
2150 break;
2154 if( !pNd )
2155 return false;
2156 if( pNd->IsInProtectSect() )
2157 return true;
2159 const SwContentFrame* pFrame = pNd->getLayoutFrame(pNd->GetDoc().getIDocumentLayoutAccess().GetCurrentLayout(), nullptr, nullptr);
2160 return pFrame && pFrame->IsProtected() ;
2163 bool SwContentAtPos::IsInRTLText()const
2165 const SwTextNode* pNd = nullptr;
2166 if (!pFndTextAttr || (eContentAtPos != IsAttrAtPos::Ftn))
2167 return false;
2169 const SwTextFootnote* pTextFootnote = static_cast<const SwTextFootnote*>(pFndTextAttr);
2170 if(!pTextFootnote->GetStartNode())
2171 return false;
2173 SwStartNode* pSttNd = pTextFootnote->GetStartNode()->GetNode().GetStartNode();
2174 SwPaM aTemp( *pSttNd );
2175 aTemp.Move(fnMoveForward, GoInNode);
2176 SwContentNode* pContentNode = aTemp.GetPointContentNode();
2177 if(pContentNode && pContentNode->IsTextNode())
2178 pNd = pContentNode->GetTextNode();
2179 if(!pNd)
2180 return false;
2182 bool bRet = false;
2183 SwIterator<SwTextFrame, SwTextNode, sw::IteratorMode::UnwrapMulti> aIter(*pNd);
2184 SwTextFrame* pTmpFrame = aIter.First();
2185 while( pTmpFrame )
2187 if ( !pTmpFrame->IsFollow())
2189 bRet = pTmpFrame->IsRightToLeft();
2190 break;
2192 pTmpFrame = aIter.Next();
2194 return bRet;
2197 bool SwCursorShell::SelectTextModel( const sal_Int32 nStart,
2198 const sal_Int32 nEnd )
2200 CurrShell aCurr( this );
2201 bool bRet = false;
2203 SwCallLink aLk( *this );
2204 SwCursorSaveState aSaveState( *m_pCurrentCursor );
2206 SwPosition& rPos = *m_pCurrentCursor->GetPoint();
2207 m_pCurrentCursor->DeleteMark();
2208 rPos.SetContent(nStart);
2209 m_pCurrentCursor->SetMark();
2210 rPos.SetContent(nEnd);
2212 if( !m_pCurrentCursor->IsSelOvr() )
2214 UpdateCursor();
2215 bRet = true;
2218 return bRet;
2221 TextFrameIndex SwCursorShell::GetCursorPointAsViewIndex() const
2223 SwPosition const*const pPos(GetCursor()->GetPoint());
2224 SwTextNode const*const pTextNode(pPos->GetNode().GetTextNode());
2225 assert(pTextNode);
2226 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(pTextNode->getLayoutFrame(GetLayout())));
2227 assert(pFrame);
2228 return pFrame->MapModelToViewPos(*pPos);
2231 bool SwCursorShell::SelectTextView(TextFrameIndex const nStart,
2232 TextFrameIndex const nEnd)
2234 CurrShell aCurr( this );
2235 bool bRet = false;
2237 SwCallLink aLk( *this );
2238 SwCursorSaveState aSaveState( *m_pCurrentCursor );
2240 SwPosition& rPos = *m_pCurrentCursor->GetPoint();
2241 m_pCurrentCursor->DeleteMark();
2242 // indexes must correspond to cursor point!
2243 SwTextFrame const*const pFrame(static_cast<SwTextFrame const*>(m_pCurrentCursor->GetPoint()->GetNode().GetTextNode()->getLayoutFrame(GetLayout())));
2244 assert(pFrame);
2245 rPos = pFrame->MapViewToModelPos(nStart);
2246 m_pCurrentCursor->SetMark();
2247 rPos = pFrame->MapViewToModelPos(nEnd);
2249 if (!m_pCurrentCursor->IsSelOvr())
2251 UpdateCursor();
2252 bRet = true;
2255 return bRet;
2258 bool SwCursorShell::SelectTextAttr( sal_uInt16 nWhich,
2259 bool bExpand,
2260 const SwTextAttr* pTextAttr )
2262 CurrShell aCurr( this );
2264 if( IsTableMode() )
2265 return false;
2267 if( !pTextAttr )
2269 SwPosition& rPos = *m_pCurrentCursor->GetPoint();
2270 SwTextNode* pTextNd = rPos.GetNode().GetTextNode();
2271 pTextAttr = pTextNd
2272 ? pTextNd->GetTextAttrAt(rPos.GetContentIndex(),
2273 nWhich,
2274 bExpand ? ::sw::GetTextAttrMode::Expand : ::sw::GetTextAttrMode::Default)
2275 : nullptr;
2277 if( !pTextAttr )
2278 return false;
2280 const sal_Int32* pEnd = pTextAttr->End();
2281 bool bRet = SelectTextModel(pTextAttr->GetStart(), (pEnd ? *pEnd : pTextAttr->GetStart() + 1));
2282 return bRet;
2285 bool SwCursorShell::GotoINetAttr( const SwTextINetFormat& rAttr )
2287 if( !rAttr.GetpTextNode() )
2288 return false;
2289 SwCursor* pCursor = getShellCursor( true );
2291 CurrShell aCurr( this );
2292 SwCallLink aLk( *this ); // watch Cursor-Moves
2293 SwCursorSaveState aSaveState( *pCursor );
2295 pCursor->GetPoint()->Assign(*rAttr.GetpTextNode(), rAttr.GetStart() );
2296 bool bRet = !pCursor->IsSelOvr();
2297 if( bRet )
2298 UpdateCursor(SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
2299 return bRet;
2302 const SwFormatINetFormat* SwCursorShell::FindINetAttr( std::u16string_view rName ) const
2304 return mxDoc->FindINetAttr( rName );
2307 bool SwCursorShell::GetShadowCursorPos( const Point& rPt, SwFillMode eFillMode,
2308 SwRect& rRect, sal_Int16& rOrient )
2311 CurrShell aCurr( this );
2313 if (IsTableMode() || HasSelection()
2314 || !GetDoc()->GetIDocumentUndoRedo().DoesUndo())
2315 return false;
2317 Point aPt( rPt );
2318 SwPosition aPos( *m_pCurrentCursor->GetPoint() );
2320 SwFillCursorPos aFPos( eFillMode );
2321 SwCursorMoveState aTmpState( &aFPos );
2323 bool bRet = false;
2324 if( GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aTmpState ) &&
2325 !aPos.GetNode().IsProtect())
2327 // start position in protected section?
2328 rRect = aFPos.aCursor;
2329 rOrient = aFPos.eOrient;
2330 bRet = true;
2332 return bRet;
2335 bool SwCursorShell::SetShadowCursorPos( const Point& rPt, SwFillMode eFillMode )
2337 CurrShell aCurr( this );
2339 if (IsTableMode() || HasSelection()
2340 || !GetDoc()->GetIDocumentUndoRedo().DoesUndo())
2341 return false;
2343 Point aPt( rPt );
2344 SwPosition aPos( *m_pCurrentCursor->GetPoint() );
2346 SwFillCursorPos aFPos( eFillMode );
2347 SwCursorMoveState aTmpState( &aFPos );
2349 if( !GetLayout()->GetModelPositionForViewPoint( &aPos, aPt, &aTmpState ) )
2350 return false;
2352 SwCallLink aLk( *this ); // watch Cursor-Moves
2353 StartAction();
2355 SwContentNode* pCNd = aPos.GetNode().GetContentNode();
2356 SwUndoId nUndoId = SwUndoId::INS_FROM_SHADOWCRSR;
2357 // If only the paragraph attributes "Adjust" or "LRSpace" are set,
2358 // then the following should not delete those again.
2359 if( 0 == aFPos.nParaCnt + aFPos.nColumnCnt &&
2360 ( SwFillMode::Indent == aFPos.eMode ||
2361 ( text::HoriOrientation::NONE != aFPos.eOrient &&
2362 0 == aFPos.nTabCnt + aFPos.nSpaceCnt )) &&
2363 pCNd && pCNd->Len() )
2364 nUndoId = SwUndoId::EMPTY;
2366 GetDoc()->GetIDocumentUndoRedo().StartUndo( nUndoId, nullptr );
2368 SwTextFormatColl* pNextFormat = nullptr;
2369 SwTextNode* pTNd = pCNd ? pCNd->GetTextNode() : nullptr;
2370 if( pTNd )
2371 pNextFormat = &pTNd->GetTextColl()->GetNextTextFormatColl();
2373 const SwSectionNode* pSectNd = pCNd ? pCNd->FindSectionNode() : nullptr;
2374 if( pSectNd && aFPos.nParaCnt )
2376 SwNodeIndex aEnd( aPos.GetNode(), 1 );
2377 while( aEnd.GetNode().IsEndNode() &&
2378 &aEnd.GetNode() !=
2379 pSectNd->EndOfSectionNode() )
2380 ++aEnd;
2382 if( aEnd.GetNode().IsEndNode() &&
2383 pCNd->Len() == aPos.GetContentIndex() )
2384 aPos.Assign( *pSectNd->EndOfSectionNode() );
2387 for( sal_uInt16 n = 0; n < aFPos.nParaCnt + aFPos.nColumnCnt; ++n )
2389 GetDoc()->getIDocumentContentOperations().AppendTextNode( aPos );
2390 if( !n && pNextFormat )
2392 *m_pCurrentCursor->GetPoint() = aPos;
2393 GetDoc()->SetTextFormatColl( *m_pCurrentCursor, pNextFormat, false );
2395 if( n < aFPos.nColumnCnt )
2397 *m_pCurrentCursor->GetPoint() = aPos;
2398 GetDoc()->getIDocumentContentOperations().InsertPoolItem( *m_pCurrentCursor,
2399 SvxFormatBreakItem( SvxBreak::ColumnBefore, RES_BREAK ) );
2403 *m_pCurrentCursor->GetPoint() = aPos;
2404 switch( aFPos.eMode )
2406 case SwFillMode::Indent:
2407 if( nullptr != (pCNd = aPos.GetNode().GetContentNode() ))
2409 assert(pCNd->IsTextNode()); // ???
2410 SfxItemSetFixed<
2411 RES_PARATR_ADJUST, RES_PARATR_ADJUST,
2412 RES_MARGIN_FIRSTLINE, RES_MARGIN_TEXTLEFT> aSet(GetDoc()->GetAttrPool());
2413 SvxFirstLineIndentItem firstLine(pCNd->GetAttr(RES_MARGIN_FIRSTLINE));
2414 SvxTextLeftMarginItem leftMargin(pCNd->GetAttr(RES_MARGIN_TEXTLEFT));
2415 firstLine.SetTextFirstLineOffset(0);
2416 leftMargin.SetTextLeft(aFPos.nTabCnt);
2417 aSet.Put(firstLine);
2418 aSet.Put(leftMargin);
2420 const SvxAdjustItem& rAdj = pCNd->GetAttr(RES_PARATR_ADJUST);
2421 if( SvxAdjust::Left != rAdj.GetAdjust() )
2422 aSet.Put( SvxAdjustItem( SvxAdjust::Left, RES_PARATR_ADJUST ) );
2424 GetDoc()->getIDocumentContentOperations().InsertItemSet( *m_pCurrentCursor, aSet );
2426 else {
2427 OSL_ENSURE( false, "No ContentNode" );
2429 break;
2431 case SwFillMode::Tab:
2432 case SwFillMode::TabSpace:
2433 case SwFillMode::Space:
2435 OUStringBuffer sInsert;
2436 if (aFPos.eMode == SwFillMode::Space)
2438 comphelper::string::padToLength(sInsert, sInsert.getLength() + aFPos.nSpaceOnlyCnt, ' ');
2440 else
2442 if (aFPos.nTabCnt)
2443 comphelper::string::padToLength(sInsert, aFPos.nTabCnt, '\t');
2444 if (aFPos.nSpaceCnt)
2445 comphelper::string::padToLength(sInsert, sInsert.getLength() + aFPos.nSpaceCnt, ' ');
2447 if (!sInsert.isEmpty())
2448 GetDoc()->getIDocumentContentOperations().InsertString( *m_pCurrentCursor, sInsert.makeStringAndClear());
2450 [[fallthrough]]; // still need to set orientation
2451 case SwFillMode::Margin:
2452 if( text::HoriOrientation::NONE != aFPos.eOrient )
2454 SvxAdjustItem aAdj( SvxAdjust::Left, RES_PARATR_ADJUST );
2455 switch( aFPos.eOrient )
2457 case text::HoriOrientation::CENTER:
2458 aAdj.SetAdjust( SvxAdjust::Center );
2459 break;
2460 case text::HoriOrientation::RIGHT:
2461 aAdj.SetAdjust( SvxAdjust::Right );
2462 break;
2463 default:
2464 break;
2466 GetDoc()->getIDocumentContentOperations().InsertPoolItem( *m_pCurrentCursor, aAdj );
2468 break;
2471 GetDoc()->GetIDocumentUndoRedo().EndUndo( nUndoId, nullptr );
2472 EndAction();
2474 return true;
2477 const SwRangeRedline* SwCursorShell::SelNextRedline()
2479 if( IsTableMode() )
2480 return nullptr;
2482 CurrShell aCurr( this );
2483 SwCallLink aLk( *this ); // watch Cursor-Moves
2484 SwCursorSaveState aSaveState( *m_pCurrentCursor );
2486 // ensure point is at the end so alternating SelNext/SelPrev works
2487 NormalizePam(false);
2488 const SwRangeRedline* pFnd = GetDoc()->getIDocumentRedlineAccess().SelNextRedline( *m_pCurrentCursor );
2490 // at the end of the document, go to the start of the document, and try again
2491 if ( !pFnd )
2493 GetDoc()->GetDocShell()->GetWrtShell()->StartOfSection();
2494 pFnd = GetDoc()->getIDocumentRedlineAccess().SelNextRedline( *m_pCurrentCursor );
2497 if( pFnd && !m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr() )
2498 UpdateCursor( SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
2499 else
2500 pFnd = nullptr;
2501 return pFnd;
2504 const SwRangeRedline* SwCursorShell::SelPrevRedline()
2506 if( IsTableMode() )
2507 return nullptr;
2509 CurrShell aCurr( this );
2510 SwCallLink aLk( *this ); // watch Cursor-Moves
2511 SwCursorSaveState aSaveState( *m_pCurrentCursor );
2513 // ensure point is at the start so alternating SelNext/SelPrev works
2514 NormalizePam(true);
2515 const SwRangeRedline* pFnd = GetDoc()->getIDocumentRedlineAccess().SelPrevRedline( *m_pCurrentCursor );
2517 // at the start of the document, go to the end of the document, and try again
2518 if ( !pFnd )
2520 GetDoc()->GetDocShell()->GetWrtShell()->EndOfSection();
2521 pFnd = GetDoc()->getIDocumentRedlineAccess().SelPrevRedline( *m_pCurrentCursor );
2524 if( pFnd && !m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr() )
2525 UpdateCursor( SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|SwCursorShell::READONLY);
2526 else
2527 pFnd = nullptr;
2528 return pFnd;
2531 const SwRangeRedline* SwCursorShell::GotoRedline_( SwRedlineTable::size_type nArrPos, bool bSelect )
2533 const SwRangeRedline* pFnd = nullptr;
2534 SwCallLink aLk( *this ); // watch Cursor-Moves
2535 SwCursorSaveState aSaveState( *m_pCurrentCursor );
2537 pFnd = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable()[ nArrPos ];
2538 if( !pFnd )
2539 return nullptr;
2541 *m_pCurrentCursor->GetPoint() = *pFnd->Start();
2543 SwPosition* pPtPos = m_pCurrentCursor->GetPoint();
2544 if( !pPtPos->GetNode().IsContentNode() )
2546 SwContentNode* pCNd = GetDoc()->GetNodes().GoNextSection( pPtPos,
2547 true, IsReadOnlyAvailable() );
2548 if( pCNd )
2550 if( pPtPos->GetNode() <= pFnd->End()->GetNode() )
2551 pPtPos->SetContent( 0 );
2552 else
2553 pFnd = nullptr;
2557 if( pFnd && bSelect )
2559 m_pCurrentCursor->SetMark();
2560 if( RedlineType::FmtColl == pFnd->GetType() )
2562 SwContentNode* pCNd = pPtPos->GetNode().GetContentNode();
2563 m_pCurrentCursor->GetPoint()->SetContent( pCNd->Len() );
2564 m_pCurrentCursor->GetMark()->Assign( *pCNd, 0 );
2566 else
2567 *m_pCurrentCursor->GetPoint() = *pFnd->End();
2569 pPtPos = m_pCurrentCursor->GetPoint();
2570 if( !pPtPos->GetNode().IsContentNode() )
2572 SwContentNode* pCNd = SwNodes::GoPrevSection( pPtPos,
2573 true, IsReadOnlyAvailable() );
2574 if( pCNd )
2576 if( pPtPos->GetNode() >= m_pCurrentCursor->GetMark()->GetNode() )
2577 pPtPos->SetContent( pCNd->Len() );
2578 else
2579 pFnd = nullptr;
2584 if( !pFnd )
2586 m_pCurrentCursor->DeleteMark();
2587 m_pCurrentCursor->RestoreSavePos();
2589 else if( bSelect && *m_pCurrentCursor->GetMark() == *m_pCurrentCursor->GetPoint() )
2590 m_pCurrentCursor->DeleteMark();
2592 if( pFnd && !m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr() )
2593 UpdateCursor( SwCursorShell::SCROLLWIN | SwCursorShell::CHKRANGE
2594 | SwCursorShell::READONLY );
2595 else
2597 pFnd = nullptr;
2598 if( bSelect )
2599 m_pCurrentCursor->DeleteMark();
2601 return pFnd;
2604 const SwRangeRedline* SwCursorShell::GotoRedline( SwRedlineTable::size_type nArrPos, bool bSelect )
2606 const SwRangeRedline* pFnd = nullptr;
2607 if( IsTableMode() )
2608 return nullptr;
2610 CurrShell aCurr( this );
2612 const SwRedlineTable& rTable = GetDoc()->getIDocumentRedlineAccess().GetRedlineTable();
2613 const SwRangeRedline* pTmp = rTable[ nArrPos ];
2614 sal_uInt16 nSeqNo = pTmp->GetSeqNo();
2615 if( !nSeqNo || !bSelect )
2617 pFnd = GotoRedline_( nArrPos, bSelect );
2618 return pFnd;
2621 bool bCheck = false;
2622 int nLoopCnt = 2;
2623 SwRedlineTable::size_type nArrSavPos = nArrPos;
2625 do {
2626 pTmp = GotoRedline_( nArrPos, true );
2628 if( !pFnd )
2629 pFnd = pTmp;
2631 if( pTmp && bCheck )
2633 // Check for overlaps. These can happen when FormatColl-
2634 // Redlines were stretched over a whole paragraph
2635 SwPaM* pCur = m_pCurrentCursor;
2636 SwPaM* pNextPam = pCur->GetNext();
2637 auto [pCStt, pCEnd] = pCur->StartEnd(); // SwPosition*
2638 while( pCur != pNextPam )
2640 auto [pNStt, pNEnd] = pNextPam->StartEnd(); // SwPosition*
2642 bool bDel = true;
2643 switch( ::ComparePosition( *pCStt, *pCEnd,
2644 *pNStt, *pNEnd ))
2646 case SwComparePosition::Inside: // Pos1 is completely in Pos2
2647 if( !pCur->HasMark() )
2649 pCur->SetMark();
2650 *pCur->GetMark() = *pNStt;
2652 else
2653 *pCStt = *pNStt;
2654 *pCEnd = *pNEnd;
2655 break;
2657 case SwComparePosition::Outside: // Pos2 is completely in Pos1
2658 case SwComparePosition::Equal: // Pos1 has same size as Pos2
2659 break;
2661 case SwComparePosition::OverlapBefore: // Pos1 overlaps Pos2 at beginning
2662 if( !pCur->HasMark() )
2663 pCur->SetMark();
2664 *pCEnd = *pNEnd;
2665 break;
2666 case SwComparePosition::OverlapBehind: // Pos1 overlaps Pos2 at end
2667 if( !pCur->HasMark() )
2669 pCur->SetMark();
2670 *pCur->GetMark() = *pNStt;
2672 else
2673 *pCStt = *pNStt;
2674 break;
2676 default:
2677 bDel = false;
2680 if( bDel )
2682 // not needed anymore
2683 SwPaM* pPrevPam = pNextPam->GetPrev();
2684 delete pNextPam;
2685 pNextPam = pPrevPam;
2687 pNextPam = pNextPam->GetNext();
2691 SwRedlineTable::size_type nFndPos = 2 == nLoopCnt
2692 ? rTable.FindNextOfSeqNo( nArrPos )
2693 : rTable.FindPrevOfSeqNo( nArrPos );
2694 if( SwRedlineTable::npos != nFndPos ||
2695 ( 0 != ( --nLoopCnt ) && SwRedlineTable::npos != (
2696 nFndPos = rTable.FindPrevOfSeqNo( nArrSavPos ))) )
2698 if( pTmp )
2700 // create new cursor
2701 CreateCursor();
2702 bCheck = true;
2704 nArrPos = nFndPos;
2706 else
2707 nLoopCnt = 0;
2709 } while( nLoopCnt );
2710 return pFnd;
2713 bool SwCursorShell::SelectNxtPrvHyperlink( bool bNext )
2715 SwNodes& rNds = GetDoc()->GetNodes();
2716 const SwNode* pBodyEndNd = &rNds.GetEndOfContent();
2717 const SwNode* pBodySttNd = pBodyEndNd->StartOfSectionNode();
2718 SwNodeOffset nBodySttNdIdx = pBodySttNd->GetIndex();
2719 Point aPt;
2721 SetGetExpField aCmpPos( SwPosition( bNext ? *pBodyEndNd : *pBodySttNd ) );
2722 SetGetExpField aCurPos( bNext ? *m_pCurrentCursor->End() : *m_pCurrentCursor->Start() );
2723 if( aCurPos.GetNode() < nBodySttNdIdx )
2725 const SwContentNode* pCNd = aCurPos.GetNodeFromContent()->GetContentNode();
2726 std::pair<Point, bool> tmp(aPt, true);
2727 if (pCNd)
2729 SwContentFrame* pFrame = pCNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
2730 if( pFrame )
2731 aCurPos.SetBodyPos( *pFrame );
2735 // check first all the hyperlink fields
2737 const SwTextNode* pTextNd;
2738 const SwCharFormats* pFormats = GetDoc()->GetCharFormats();
2739 for( SwCharFormats::size_type n = pFormats->size(); 1 < n; )
2741 SwIterator<SwTextINetFormat,SwCharFormat> aIter(*(*pFormats)[--n]);
2743 for( SwTextINetFormat* pFnd = aIter.First(); pFnd; pFnd = aIter.Next() )
2745 pTextNd = pFnd->GetpTextNode();
2746 if( pTextNd && pTextNd->GetNodes().IsDocNodes() )
2748 SwTextINetFormat& rAttr = *pFnd;
2749 SetGetExpField aPos( *pTextNd, rAttr );
2750 if (pTextNd->GetIndex() < nBodySttNdIdx)
2752 std::pair<Point, bool> tmp(aPt, true);
2753 SwContentFrame* pFrame = pTextNd->getLayoutFrame(GetLayout(), nullptr, &tmp);
2754 if (pFrame)
2756 aPos.SetBodyPos( *pFrame );
2760 if( bNext
2761 ? ( aPos < aCmpPos && aCurPos < aPos )
2762 : ( aCmpPos < aPos && aPos < aCurPos ))
2764 OUString sText(pTextNd->GetExpandText(GetLayout(),
2765 rAttr.GetStart(),
2766 *rAttr.GetEnd() - rAttr.GetStart() ) );
2768 sText = sText.replaceAll("\x0a", "");
2769 sText = comphelper::string::strip(sText, ' ');
2771 if( !sText.isEmpty() )
2772 aCmpPos = aPos;
2779 // then check all the Flys with a URL or image map
2781 for(sw::SpzFrameFormat* pSpz: *GetDoc()->GetSpzFrameFormats())
2783 auto pFormat = static_cast<SwFlyFrameFormat*>(pSpz);
2784 const SwFormatURL& rURLItem = pFormat->GetURL();
2785 if( rURLItem.GetMap() || !rURLItem.GetURL().isEmpty() )
2787 SwFlyFrame* pFly = pFormat->GetFrame( &aPt );
2788 SwPosition aTmpPos( *pBodySttNd );
2789 if( pFly &&
2790 GetBodyTextNode( *GetDoc(), aTmpPos, *pFly->GetLower() ) )
2792 SetGetExpField aPos( *pFormat, &aTmpPos );
2794 if( bNext
2795 ? ( aPos < aCmpPos && aCurPos < aPos )
2796 : ( aCmpPos < aPos && aPos < aCurPos ))
2797 aCmpPos = aPos;
2803 // found any URL ?
2804 const SwTextINetFormat* pFndAttr = aCmpPos.GetINetFormat();
2805 const SwFlyFrameFormat* pFndFormat = aCmpPos.GetFlyFormat();
2806 if( !pFndAttr && !pFndFormat )
2807 return false;
2809 CurrShell aCurr( this );
2810 SwCallLink aLk( *this );
2812 bool bRet = false;
2813 // found a text attribute ?
2814 if( pFndAttr )
2816 SwCursorSaveState aSaveState( *m_pCurrentCursor );
2818 aCmpPos.GetPosOfContent( *m_pCurrentCursor->GetPoint() );
2819 m_pCurrentCursor->DeleteMark();
2820 m_pCurrentCursor->SetMark();
2821 m_pCurrentCursor->GetPoint()->SetContent( *pFndAttr->End() );
2823 if( !m_pCurrentCursor->IsInProtectTable() && !m_pCurrentCursor->IsSelOvr() )
2825 UpdateCursor( SwCursorShell::SCROLLWIN|SwCursorShell::CHKRANGE|
2826 SwCursorShell::READONLY );
2827 bRet = true;
2830 // found a draw object ?
2831 else if( RES_DRAWFRMFMT == pFndFormat->Which() )
2833 const SdrObject* pSObj = pFndFormat->FindSdrObject();
2834 if (pSObj)
2836 static_cast<SwFEShell*>(this)->SelectObj( pSObj->GetCurrentBoundRect().Center() );
2837 MakeSelVisible();
2838 bRet = true;
2841 else // then is it a fly
2843 SwFlyFrame* pFly = pFndFormat->GetFrame(&aPt);
2844 if( pFly )
2846 static_cast<SwFEShell*>(this)->SelectFlyFrame( *pFly );
2847 MakeSelVisible();
2848 bRet = true;
2851 return bRet;
2854 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */