xref: /core/sw/source/core/unocore/unoobj2.cxx (revision fbc038cc)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <utility>
23 
24 #include <comphelper/servicehelper.hxx>
25 #include <cppuhelper/supportsservice.hxx>
26 #include <editeng/brushitem.hxx>
27 #include <editeng/flstitem.hxx>
28 #include <editeng/unolingu.hxx>
29 #include <rtl/ustrbuf.hxx>
30 #include <svl/listener.hxx>
31 #include <sfx2/docfile.hxx>
32 #include <sfx2/docfilt.hxx>
33 #include <sfx2/fcontnr.hxx>
34 #include <sfx2/linkmgr.hxx>
35 #include <svtools/ctrltool.hxx>
36 #include <vcl/svapp.hxx>
37 
38 #include <swtypes.hxx>
39 #include <hintids.hxx>
40 #include <cmdid.h>
41 #include <hints.hxx>
42 #include <IMark.hxx>
43 #include <bookmrk.hxx>
44 #include <frmfmt.hxx>
45 #include <doc.hxx>
46 #include <IDocumentUndoRedo.hxx>
47 #include <IDocumentLayoutAccess.hxx>
48 #include <IDocumentMarkAccess.hxx>
49 #include <textboxhelper.hxx>
50 #include <ndtxt.hxx>
51 #include <ndnotxt.hxx>
52 #include <unocrsr.hxx>
53 #include <swundo.hxx>
54 #include <rootfrm.hxx>
55 #include <flyfrm.hxx>
56 #include <ftnidx.hxx>
57 #include <docary.hxx>
58 #include <paratr.hxx>
59 #include <pam.hxx>
60 #include <shellio.hxx>
61 #include <swerror.h>
62 #include <swtblfmt.hxx>
63 #include <docsh.hxx>
64 #include <docstyle.hxx>
65 #include <charfmt.hxx>
66 #include <txtfld.hxx>
67 #include <fmtfld.hxx>
68 #include <fmtpdsc.hxx>
69 #include <pagedesc.hxx>
70 #include <poolfmt.hxx>
71 #include <edimp.hxx>
72 #include <fchrfmt.hxx>
73 #include <cntfrm.hxx>
74 #include <pagefrm.hxx>
75 #include <doctxm.hxx>
76 #include <fmtrfmrk.hxx>
77 #include <txtrfmrk.hxx>
78 #include <unoparaframeenum.hxx>
79 #include <unofootnote.hxx>
80 #include <unotextbodyhf.hxx>
81 #include <unotextrange.hxx>
82 #include <unoparagraph.hxx>
83 #include <unomap.hxx>
84 #include <unoport.hxx>
85 #include <unocrsrhelper.hxx>
86 #include <unosett.hxx>
87 #include <unoprnms.hxx>
88 #include <unotbl.hxx>
89 #include <unodraw.hxx>
90 #include <unocoll.hxx>
91 #include <unostyle.hxx>
92 #include <fmtanchr.hxx>
93 #include <flypos.hxx>
94 #include <txtftn.hxx>
95 #include <fmtftn.hxx>
96 #include <fmtcntnt.hxx>
97 #include <com/sun/star/text/WrapTextMode.hpp>
98 #include <com/sun/star/text/TextContentAnchorType.hpp>
99 #include <com/sun/star/style/PageStyleLayout.hpp>
100 #include <com/sun/star/text/XTextDocument.hpp>
101 #include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
102 #include <com/sun/star/drawing/XDrawPageSupplier.hpp>
103 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
104 #include <unoframe.hxx>
105 #include <fmthdft.hxx>
106 #include <fmtflcnt.hxx>
107 #include <fmtclds.hxx>
108 #include <dcontact.hxx>
109 #include <dflyobj.hxx>
110 #include <vector>
111 #include <sortedobjs.hxx>
112 #include <sortopt.hxx>
113 #include <algorithm>
114 #include <iterator>
115 
116 using namespace ::com::sun::star;
117 
118 namespace sw {
119 
120 uno::Sequence< OUString >
121 GetSupportedServiceNamesImpl(
122         size_t const nServices, char const*const pServices[])
123 {
124     uno::Sequence< OUString > ret(nServices);
125     for (size_t i = 0; i < nServices; ++i)
126     {
127         ret[i] = OUString::createFromAscii(pServices[i]);
128     }
129     return ret;
130 }
131 
132 } // namespace sw
133 
134 namespace sw {
135 
136 void DeepCopyPaM(SwPaM const & rSource, SwPaM & rTarget)
137 {
138     rTarget = rSource;
139 
140     if (rSource.GetNext() != &rSource)
141     {
142         SwPaM *pPam = const_cast<SwPaM*>(rSource.GetNext());
143         do
144         {
145             // create new PaM
146             SwPaM *const pNew = new SwPaM(*pPam, nullptr);
147             // insert into ring
148             pNew->MoveTo(&rTarget);
149             pPam = pPam->GetNext();
150         }
151         while (pPam != &rSource);
152     }
153 }
154 
155 } // namespace sw
156 
157 struct FrameClientSortListLess
158 {
159     bool operator() (FrameClientSortListEntry const& r1,
160                      FrameClientSortListEntry const& r2) const
161     {
162         return  (r1.nIndex <  r2.nIndex)
163             || ((r1.nIndex == r2.nIndex) && (r1.nOrder < r2.nOrder));
164     }
165 };
166 
167 namespace
168 {
169     void lcl_CollectFrameAtNodeWithLayout(const SwContentFrame* pCFrame,
170             FrameClientSortList_t& rFrames,
171             const RndStdIds nAnchorType)
172     {
173         auto pObjs = pCFrame->GetDrawObjs();
174         if(!pObjs)
175             return;
176         for(const auto pAnchoredObj : *pObjs)
177         {
178             SwFrameFormat& rFormat = pAnchoredObj->GetFrameFormat();
179             // Filter out textboxes, which are not interesting at a UNO level.
180             if(SwTextBoxHelper::isTextBox(&rFormat, RES_FLYFRMFMT))
181                 continue;
182             if(rFormat.GetAnchor().GetAnchorId() == nAnchorType)
183             {
184                 const auto nIdx =
185                     rFormat.GetAnchor().GetContentAnchor()->nContent.GetIndex();
186                 const auto nOrder = rFormat.GetAnchor().GetOrder();
187                 FrameClientSortListEntry entry(nIdx, nOrder, new sw::FrameClient(&rFormat));
188                 rFrames.push_back(entry);
189             }
190         }
191     }
192 }
193 
194 
195 void CollectFrameAtNode( const SwNodeIndex& rIdx,
196                          FrameClientSortList_t& rFrames,
197                          const bool bAtCharAnchoredObjs )
198 {
199     // _bAtCharAnchoredObjs:
200     // <true>: at-character anchored objects are collected
201     // <false>: at-paragraph anchored objects are collected
202 
203     // search all borders, images, and OLEs that are connected to the paragraph
204     SwDoc* pDoc = rIdx.GetNode().GetDoc();
205 
206     const auto nChkType = bAtCharAnchoredObjs ? RndStdIds::FLY_AT_CHAR : RndStdIds::FLY_AT_PARA;
207     const SwContentFrame* pCFrame;
208     const SwContentNode* pCNd;
209     if( pDoc->getIDocumentLayoutAccess().GetCurrentViewShell() &&
210         nullptr != (pCNd = rIdx.GetNode().GetContentNode()) &&
211         nullptr != (pCFrame = pCNd->getLayoutFrame( pDoc->getIDocumentLayoutAccess().GetCurrentLayout())) )
212     {
213         lcl_CollectFrameAtNodeWithLayout(pCFrame, rFrames, nChkType);
214     }
215     else
216     {
217         const SwFrameFormats& rFormats = *pDoc->GetSpzFrameFormats();
218         const size_t nSize = rFormats.size();
219         for ( size_t i = 0; i < nSize; i++)
220         {
221             const SwFrameFormat* pFormat = rFormats[ i ];
222             const SwFormatAnchor& rAnchor = pFormat->GetAnchor();
223             const SwPosition* pAnchorPos;
224             if( rAnchor.GetAnchorId() == nChkType &&
225                 nullptr != (pAnchorPos = rAnchor.GetContentAnchor()) &&
226                     pAnchorPos->nNode == rIdx )
227             {
228 
229                 // OD 2004-05-07 #i28701# - determine insert position for
230                 // sorted <rFrameArr>
231                 const sal_Int32 nIndex = pAnchorPos->nContent.GetIndex();
232                 sal_uInt32 nOrder = rAnchor.GetOrder();
233 
234                 FrameClientSortListEntry entry(nIndex, nOrder, new sw::FrameClient(const_cast<SwFrameFormat*>(pFormat)));
235                 rFrames.push_back(entry);
236             }
237         }
238         std::sort(rFrames.begin(), rFrames.end(), FrameClientSortListLess());
239     }
240 }
241 
242 UnoActionContext::UnoActionContext(SwDoc *const pDoc)
243     : m_pDoc(pDoc)
244 {
245     SwRootFrame *const pRootFrame = m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
246     if (pRootFrame)
247     {
248         pRootFrame->StartAllAction();
249     }
250 }
251 
252 UnoActionContext::~UnoActionContext() COVERITY_NOEXCEPT_FALSE
253 {
254     // Doc may already have been removed here
255     if (m_pDoc)
256     {
257         SwRootFrame *const pRootFrame = m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
258         if (pRootFrame)
259         {
260             pRootFrame->EndAllAction();
261         }
262     }
263 }
264 
265 static void lcl_RemoveImpl(SwDoc *const pDoc)
266 {
267     assert(pDoc);
268     SwRootFrame *const pRootFrame = pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
269     if (pRootFrame)
270     {
271         pRootFrame->UnoRemoveAllActions();
272     }
273 }
274 
275 UnoActionRemoveContext::UnoActionRemoveContext(SwDoc *const pDoc)
276     : m_pDoc(pDoc)
277 {
278     lcl_RemoveImpl(m_pDoc);
279 }
280 
281 static SwDoc * lcl_IsNewStyleTable(SwUnoTableCursor const& rCursor)
282 {
283     SwTableNode *const pTableNode = rCursor.GetNode().FindTableNode();
284     return (pTableNode && !pTableNode->GetTable().IsNewModel())
285         ? rCursor.GetDoc()
286         : nullptr;
287 }
288 
289 UnoActionRemoveContext::UnoActionRemoveContext(SwUnoTableCursor const& rCursor)
290     : m_pDoc(lcl_IsNewStyleTable(rCursor))
291 {
292     // this insanity is only necessary for old-style tables
293     // because SwRootFrame::MakeTableCursors() creates the table cursor for these
294     if (m_pDoc)
295     {
296         lcl_RemoveImpl(m_pDoc);
297     }
298 }
299 
300 UnoActionRemoveContext::~UnoActionRemoveContext() COVERITY_NOEXCEPT_FALSE
301 {
302     if (m_pDoc)
303     {
304         SwRootFrame *const pRootFrame = m_pDoc->getIDocumentLayoutAccess().GetCurrentLayout();
305         if (pRootFrame)
306         {
307             pRootFrame->UnoRestoreAllActions();
308         }
309     }
310 }
311 
312 void ClientModify(SwClient* pClient, const SfxPoolItem *pOld, const SfxPoolItem *pNew)
313 {
314     switch( pOld ? pOld->Which() : 0 )
315     {
316     case RES_REMOVE_UNO_OBJECT:
317     case RES_OBJECTDYING:
318         if( static_cast<void*>(pClient->GetRegisteredIn()) == static_cast<const SwPtrMsgPoolItem *>(pOld)->pObject )
319             pClient->EndListeningAll();
320         break;
321 
322     case RES_FMT_CHG:
323         // Is the move to the new one finished and will the old one be deleted?
324         if( static_cast<const SwFormatChg*>(pNew)->pChangedFormat == pClient->GetRegisteredIn() &&
325             static_cast<const SwFormatChg*>(pOld)->pChangedFormat->IsFormatInDTOR() )
326             pClient->EndListeningAll();
327         break;
328     }
329 }
330 
331 void SwUnoCursorHelper::SetCursorAttr(SwPaM & rPam,
332         const SfxItemSet& rSet,
333         const SetAttrMode nAttrMode, const bool bTableMode)
334 {
335     const SetAttrMode nFlags = nAttrMode | SetAttrMode::APICALL;
336     SwDoc* pDoc = rPam.GetDoc();
337     //StartEndAction
338     UnoActionContext aAction(pDoc);
339     if (rPam.GetNext() != &rPam)    // Ring of Cursors
340     {
341         pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSATTR, nullptr);
342 
343         for(SwPaM& rCurrent : rPam.GetRingContainer())
344         {
345             if (rCurrent.HasMark() &&
346                 ( bTableMode ||
347                   (*rCurrent.GetPoint() != *rCurrent.GetMark()) ))
348             {
349                 pDoc->getIDocumentContentOperations().InsertItemSet(rCurrent, rSet, nFlags);
350             }
351         }
352 
353         pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSATTR, nullptr);
354     }
355     else
356     {
357         pDoc->getIDocumentContentOperations().InsertItemSet( rPam, rSet, nFlags );
358     }
359 
360     if( rSet.GetItemState( RES_PARATR_OUTLINELEVEL, false ) >= SfxItemState::DEFAULT )
361     {
362         SwTextNode * pTmpNode = rPam.GetNode().GetTextNode();
363         if ( pTmpNode )
364         {
365             rPam.GetDoc()->GetNodes().UpdateOutlineNode( *pTmpNode );
366         }
367     }
368 }
369 
370 // #i63870#
371 // split third parameter <bCurrentAttrOnly> into new parameters <bOnlyTextAttr>
372 // and <bGetFromChrFormat> to get better control about resulting <SfxItemSet>
373 void SwUnoCursorHelper::GetCursorAttr(SwPaM & rPam,
374         SfxItemSet & rSet, const bool bOnlyTextAttr, const bool bGetFromChrFormat)
375 {
376     static const sal_uLong nMaxLookup = 1000;
377     SfxItemSet aSet( *rSet.GetPool(), rSet.GetRanges() );
378     SfxItemSet *pSet = &rSet;
379     for(SwPaM& rCurrent : rPam.GetRingContainer())
380     {
381         SwPosition const & rStart( *rCurrent.Start() );
382         SwPosition const & rEnd( *rCurrent.End() );
383         const sal_uLong nSttNd = rStart.nNode.GetIndex();
384         const sal_uLong nEndNd = rEnd  .nNode.GetIndex();
385 
386         if (nEndNd - nSttNd >= nMaxLookup)
387         {
388             rSet.ClearItem();
389             rSet.InvalidateAllItems();
390             return;// uno::Any();
391         }
392 
393         // the first node inserts the values into the get set
394         // all other nodes merge their values into the get set
395         for (sal_uLong n = nSttNd; n <= nEndNd; ++n)
396         {
397             SwNode *const pNd = rPam.GetDoc()->GetNodes()[ n ];
398             switch (pNd->GetNodeType())
399             {
400                 case SwNodeType::Text:
401                 {
402                     const sal_Int32 nStart = (n == nSttNd)
403                         ? rStart.nContent.GetIndex() : 0;
404                     const sal_Int32 nEnd   = (n == nEndNd)
405                         ? rEnd.nContent.GetIndex()
406                         : pNd->GetTextNode()->GetText().getLength();
407                     pNd->GetTextNode()->GetParaAttr(*pSet, nStart, nEnd, bOnlyTextAttr, bGetFromChrFormat);
408                 }
409                 break;
410 
411                 case SwNodeType::Grf:
412                 case SwNodeType::Ole:
413                     static_cast<SwContentNode*>(pNd)->GetAttr( *pSet );
414                 break;
415 
416                 default:
417                     continue; // skip this node
418             }
419 
420             if (pSet != &rSet)
421             {
422                 rSet.MergeValues( aSet );
423             }
424             else
425             {
426                 pSet = &aSet;
427             }
428 
429             if (aSet.Count())
430             {
431                 aSet.ClearItem();
432             }
433         }
434     }
435 }
436 
437 struct SwXParagraphEnumerationImpl final : public SwXParagraphEnumeration
438 {
439     uno::Reference< text::XText > const m_xParentText;
440     const CursorType m_eCursorType;
441     /// Start node of the cell _or_ table the enumeration belongs to.
442     /// Used to restrict the movement of the UNO cursor to the cell and its
443     /// embedded tables.
444     SwStartNode const*const m_pOwnStartNode;
445     SwTable const*const m_pOwnTable;
446     const sal_uLong m_nEndIndex;
447     sal_Int32 m_nFirstParaStart;
448     sal_Int32 m_nLastParaEnd;
449     bool m_bFirstParagraph;
450     uno::Reference< text::XTextContent > m_xNextPara;
451     sw::UnoCursorPointer m_pCursor;
452 
453     SwXParagraphEnumerationImpl(
454             uno::Reference< text::XText > const& xParent,
455             const std::shared_ptr<SwUnoCursor>& pCursor,
456             const CursorType eType,
457             SwStartNode const*const pStartNode, SwTable const*const pTable)
458         : m_xParentText( xParent )
459         , m_eCursorType( eType )
460         // remember table and start node for later travelling
461         // (used in export of tables in tables)
462         , m_pOwnStartNode( pStartNode )
463         // for import of tables in tables we have to remember the actual
464         // table and start node of the current position in the enumeration.
465         , m_pOwnTable( pTable )
466         , m_nEndIndex( pCursor->End()->nNode.GetIndex() )
467         , m_nFirstParaStart( -1 )
468         , m_nLastParaEnd( -1 )
469         , m_bFirstParagraph( true )
470         , m_pCursor(pCursor)
471     {
472         OSL_ENSURE(m_xParentText.is(), "SwXParagraphEnumeration: no parent?");
473         OSL_ENSURE(   !((CursorType::SelectionInTable == eType) ||
474                         (CursorType::TableText == eType))
475                    || (m_pOwnTable && m_pOwnStartNode),
476             "SwXParagraphEnumeration: table type but no start node or table?");
477 
478         if ((CursorType::Selection == m_eCursorType) ||
479             (CursorType::SelectionInTable == m_eCursorType))
480         {
481             SwUnoCursor & rCursor = GetCursor();
482             rCursor.Normalize();
483             m_nFirstParaStart = rCursor.GetPoint()->nContent.GetIndex();
484             m_nLastParaEnd = rCursor.GetMark()->nContent.GetIndex();
485             rCursor.DeleteMark();
486         }
487     }
488 
489     virtual ~SwXParagraphEnumerationImpl() override
490         { m_pCursor.reset(nullptr); }
491     virtual void SAL_CALL release() throw () override
492     {
493         SolarMutexGuard g;
494         OWeakObject::release();
495     }
496 
497     // XServiceInfo
498     virtual OUString SAL_CALL getImplementationName() override
499         { return OUString("SwXParagraphEnumeration"); }
500     virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName) override
501         { return cppu::supportsService(this, rServiceName); };
502     virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
503         { return {"com.sun.star.text.ParagraphEnumeration"}; };
504 
505     // XEnumeration
506     virtual sal_Bool SAL_CALL hasMoreElements() override;
507     virtual css::uno::Any SAL_CALL nextElement() override;
508 
509     SwUnoCursor& GetCursor()
510         { return *m_pCursor; }
511     /// @throws container::NoSuchElementException
512     /// @throws lang::WrappedTargetException
513     /// @throws uno::RuntimeException
514     uno::Reference< text::XTextContent > NextElement_Impl();
515 
516     /**
517      * Determines if the last element in the enumeration should be ignored or
518      * not.
519      */
520     bool IgnoreLastElement(SwUnoCursor& rCursor, bool bMovedFromTable);
521 };
522 
523 SwXParagraphEnumeration* SwXParagraphEnumeration::Create(
524     uno::Reference< text::XText > const& xParent,
525     const std::shared_ptr<SwUnoCursor>& pCursor,
526     const CursorType eType,
527     SwStartNode const*const pStartNode,
528     SwTable const*const pTable)
529 {
530     return new SwXParagraphEnumerationImpl(xParent, pCursor, eType, pStartNode, pTable);
531 }
532 
533 sal_Bool SAL_CALL
534 SwXParagraphEnumerationImpl::hasMoreElements()
535 {
536     SolarMutexGuard aGuard;
537     return m_bFirstParagraph || m_xNextPara.is();
538 }
539 
540 //!! compare to SwShellTableCursor::FillRects() in viscrs.cxx
541 static SwTableNode *
542 lcl_FindTopLevelTable(
543         SwTableNode *const pTableNode, SwTable const*const pOwnTable)
544 {
545     // find top-most table in current context (section) level
546 
547     SwTableNode * pLast = pTableNode;
548     for (SwTableNode* pTmp = pLast;
549          pTmp != nullptr  &&  &pTmp->GetTable() != pOwnTable;  /* we must not go up higher than the own table! */
550          pTmp = pTmp->StartOfSectionNode()->FindTableNode() )
551     {
552         pLast = pTmp;
553     }
554     return pLast;
555 }
556 
557 static bool
558 lcl_CursorIsInSection(
559         SwUnoCursor const*const pUnoCursor, SwStartNode const*const pOwnStartNode)
560 {
561     // returns true if the cursor is in the section (or in a sub section!)
562     // represented by pOwnStartNode
563 
564     bool bRes = true;
565     if (pUnoCursor && pOwnStartNode)
566     {
567         const SwEndNode * pOwnEndNode = pOwnStartNode->EndOfSectionNode();
568         bRes = pOwnStartNode->GetIndex() <= pUnoCursor->Start()->nNode.GetIndex() &&
569                pUnoCursor->End()->nNode.GetIndex() <= pOwnEndNode->GetIndex();
570     }
571     return bRes;
572 }
573 
574 bool SwXParagraphEnumerationImpl::IgnoreLastElement(SwUnoCursor& rCursor, bool bMovedFromTable)
575 {
576     // Ignore the last element of a selection enumeration if this is a stub
577     // paragraph (directly after table, selection ends at paragaph start).
578 
579     if (rCursor.Start()->nNode.GetIndex() != m_nEndIndex)
580         return false;
581 
582     if (m_eCursorType != CursorType::Selection)
583         return false;
584 
585     if (!bMovedFromTable)
586         return false;
587 
588     return m_nLastParaEnd == 0;
589 }
590 
591 uno::Reference< text::XTextContent >
592 SwXParagraphEnumerationImpl::NextElement_Impl()
593 {
594     SwUnoCursor& rUnoCursor = GetCursor();
595 
596     // check for exceeding selections
597     if (!m_bFirstParagraph &&
598         ((CursorType::Selection == m_eCursorType) ||
599          (CursorType::SelectionInTable == m_eCursorType)))
600     {
601         SwPosition* pStart = rUnoCursor.Start();
602         auto aNewCursor(rUnoCursor.GetDoc()->CreateUnoCursor(*pStart));
603         // one may also go into tables here
604         if (CursorType::SelectionInTable != m_eCursorType)
605         {
606             aNewCursor->SetRemainInSection( false );
607         }
608 
609         // os 2005-01-14: This part is only necessary to detect movements out
610         // of a selection; if there is no selection we don't have to care
611         SwTableNode *const pTableNode = aNewCursor->GetNode().FindTableNode();
612         bool bMovedFromTable = false;
613         if (CursorType::SelectionInTable != m_eCursorType && pTableNode)
614         {
615             aNewCursor->GetPoint()->nNode = pTableNode->EndOfSectionIndex();
616             aNewCursor->Move(fnMoveForward, GoInNode);
617             bMovedFromTable = true;
618         }
619         else
620         {
621             aNewCursor->MovePara(GoNextPara, fnParaStart);
622         }
623         if (m_nEndIndex < aNewCursor->Start()->nNode.GetIndex())
624         {
625             return nullptr;
626         }
627 
628         if (IgnoreLastElement(*aNewCursor, bMovedFromTable))
629         {
630             return nullptr;
631         }
632     }
633 
634     bool bInTable = false;
635     if (!m_bFirstParagraph)
636     {
637         rUnoCursor.SetRemainInSection( false );
638         // what to do if already in a table?
639         SwTableNode * pTableNode = rUnoCursor.GetNode().FindTableNode();
640         pTableNode = lcl_FindTopLevelTable( pTableNode, m_pOwnTable );
641         if (pTableNode && (&pTableNode->GetTable() != m_pOwnTable))
642         {
643             // this is a foreign table: go to end
644             rUnoCursor.GetPoint()->nNode = pTableNode->EndOfSectionIndex();
645             if (!rUnoCursor.Move(fnMoveForward, GoInNode))
646             {
647                 return nullptr;
648             }
649             bInTable = true;
650         }
651     }
652 
653     uno::Reference< text::XTextContent >  xRef;
654     // the cursor must remain in the current section or a subsection
655     // before AND after the movement...
656     if (lcl_CursorIsInSection( &rUnoCursor, m_pOwnStartNode ) &&
657         (m_bFirstParagraph || bInTable ||
658         (rUnoCursor.MovePara(GoNextPara, fnParaStart) &&
659             lcl_CursorIsInSection( &rUnoCursor, m_pOwnStartNode ))))
660     {
661         if (m_eCursorType == CursorType::Selection || m_eCursorType == CursorType::SelectionInTable)
662         {
663             // This is a selection, check if the cursor would go past the end
664             // of the selection.
665             if (rUnoCursor.Start()->nNode.GetIndex() > m_nEndIndex)
666                 return nullptr;
667         }
668 
669         SwPosition* pStart = rUnoCursor.Start();
670         const sal_Int32 nFirstContent =
671             m_bFirstParagraph ? m_nFirstParaStart : -1;
672         const sal_Int32 nLastContent =
673             (m_nEndIndex == pStart->nNode.GetIndex()) ? m_nLastParaEnd : -1;
674 
675         // position in a table, or in a simple paragraph?
676         SwTableNode * pTableNode = rUnoCursor.GetNode().FindTableNode();
677         pTableNode = lcl_FindTopLevelTable( pTableNode, m_pOwnTable );
678         if (/*CursorType::TableText != eCursorType && CursorType::SelectionInTable != eCursorType && */
679             pTableNode && (&pTableNode->GetTable() != m_pOwnTable))
680         {
681             // this is a foreign table
682             SwFrameFormat* pTableFormat =
683                 static_cast<SwFrameFormat*>(pTableNode->GetTable().GetFrameFormat());
684             xRef = SwXTextTable::CreateXTextTable(pTableFormat);
685         }
686         else
687         {
688             text::XText *const pText = m_xParentText.get();
689             xRef = SwXParagraph::CreateXParagraph(*rUnoCursor.GetDoc(),
690                 pStart->nNode.GetNode().GetTextNode(),
691                 static_cast<SwXText*>(pText), nFirstContent, nLastContent);
692         }
693     }
694 
695     return xRef;
696 }
697 
698 uno::Any SAL_CALL SwXParagraphEnumerationImpl::nextElement()
699 {
700     SolarMutexGuard aGuard;
701     if (m_bFirstParagraph)
702     {
703         m_xNextPara = NextElement_Impl();
704         m_bFirstParagraph = false;
705     }
706     const uno::Reference< text::XTextContent > xRef = m_xNextPara;
707     if (!xRef.is())
708     {
709         throw container::NoSuchElementException();
710     }
711     m_xNextPara = NextElement_Impl();
712 
713     uno::Any aRet;
714     aRet <<= xRef;
715     return aRet;
716 }
717 
718 class SwXTextRange::Impl
719     : public SvtListener
720 {
721 public:
722     const SfxItemPropertySet& m_rPropSet;
723     const enum RangePosition m_eRangePosition;
724     SwDoc& m_rDoc;
725     uno::Reference<text::XText> m_xParentText;
726     const SwFrameFormat* m_pTableFormat;
727     const ::sw::mark::IMark* m_pMark;
728 
729     Impl(SwDoc& rDoc, const enum RangePosition eRange,
730             SwFrameFormat* const pTableFormat,
731             const uno::Reference<text::XText>& xParent = nullptr)
732         : m_rPropSet(*aSwMapProvider.GetPropertySet(PROPERTY_MAP_TEXT_CURSOR))
733         , m_eRangePosition(eRange)
734         , m_rDoc(rDoc)
735         , m_xParentText(xParent)
736         , m_pTableFormat(pTableFormat)
737         , m_pMark(nullptr)
738     {
739         m_pTableFormat && StartListening(pTableFormat->GetNotifier());
740     }
741 
742     virtual ~Impl() override
743     {
744         // Impl owns the bookmark; delete it here: SolarMutex is locked
745         Invalidate();
746     }
747 
748     void Invalidate()
749     {
750         if (m_pMark)
751         {
752             m_rDoc.getIDocumentMarkAccess()->deleteMark(m_pMark);
753             m_pMark = nullptr;
754         }
755         m_pTableFormat = nullptr;
756         EndListeningAll();
757     }
758 
759     const ::sw::mark::IMark* GetBookmark() const { return m_pMark; }
760     void SetMark(::sw::mark::IMark& rMark)
761     {
762         EndListeningAll();
763         m_pTableFormat = nullptr;
764         m_pMark = &rMark;
765         StartListening(rMark.GetNotifier());
766     }
767 
768 protected:
769     virtual void Notify(const SfxHint&) override;
770 };
771 
772 void SwXTextRange::Impl::Notify(const SfxHint& rHint)
773 {
774     if(rHint.GetId() == SfxHintId::Dying)
775     {
776         EndListeningAll();
777         m_pTableFormat = nullptr;
778         m_pMark = nullptr;
779     }
780 }
781 
782 SwXTextRange::SwXTextRange(SwPaM const & rPam,
783         const uno::Reference< text::XText > & xParent,
784         const enum RangePosition eRange)
785     : m_pImpl( new SwXTextRange::Impl(*rPam.GetDoc(), eRange, nullptr, xParent) )
786 {
787     SetPositions(rPam);
788 }
789 
790 SwXTextRange::SwXTextRange(SwFrameFormat& rTableFormat)
791     : m_pImpl(
792         new SwXTextRange::Impl(*rTableFormat.GetDoc(), RANGE_IS_TABLE, &rTableFormat) )
793 {
794     SwTable *const pTable = SwTable::FindTable( &rTableFormat );
795     SwTableNode *const pTableNode = pTable->GetTableNode();
796     SwPosition aPosition( *pTableNode );
797     SwPaM aPam( aPosition );
798 
799     SetPositions( aPam );
800 }
801 
802 SwXTextRange::~SwXTextRange()
803 {
804 }
805 
806 const SwDoc& SwXTextRange::GetDoc() const
807 {
808     return m_pImpl->m_rDoc;
809 }
810 
811 SwDoc& SwXTextRange::GetDoc()
812 {
813     return m_pImpl->m_rDoc;
814 }
815 
816 void SwXTextRange::Invalidate()
817 {
818     m_pImpl->Invalidate();
819 }
820 
821 void SwXTextRange::SetPositions(const SwPaM& rPam)
822 {
823     m_pImpl->Invalidate();
824     IDocumentMarkAccess* const pMA = m_pImpl->m_rDoc.getIDocumentMarkAccess();
825     auto pMark = pMA->makeMark(rPam, OUString(), IDocumentMarkAccess::MarkType::UNO_BOOKMARK, sw::mark::InsertMode::New);
826     m_pImpl->SetMark(*pMark);
827 }
828 
829 void SwXTextRange::DeleteAndInsert(
830         const OUString& rText, const bool bForceExpandHints)
831 {
832     if (RANGE_IS_TABLE == m_pImpl->m_eRangePosition)
833     {
834         // setString on table not allowed
835         throw uno::RuntimeException();
836     }
837 
838     const SwPosition aPos(GetDoc().GetNodes().GetEndOfContent());
839     SwCursor aCursor(aPos, nullptr);
840     if (GetPositions(aCursor))
841     {
842         UnoActionContext aAction(& m_pImpl->m_rDoc);
843         m_pImpl->m_rDoc.GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
844         if (aCursor.HasMark())
845         {
846             m_pImpl->m_rDoc.getIDocumentContentOperations().DeleteAndJoin(aCursor);
847         }
848 
849         if (!rText.isEmpty())
850         {
851             SwUnoCursorHelper::DocInsertStringSplitCR(
852                     m_pImpl->m_rDoc, aCursor, rText, bForceExpandHints);
853 
854             SwUnoCursorHelper::SelectPam(aCursor, true);
855             aCursor.Left(rText.getLength());
856         }
857         SetPositions(aCursor);
858         m_pImpl->m_rDoc.GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
859     }
860 }
861 
862 namespace
863 {
864     class theSwXTextRangeUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextRangeUnoTunnelId > {};
865 }
866 
867 const uno::Sequence< sal_Int8 > & SwXTextRange::getUnoTunnelId()
868 {
869     return theSwXTextRangeUnoTunnelId::get().getSeq();
870 }
871 
872 // XUnoTunnel
873 sal_Int64 SAL_CALL
874 SwXTextRange::getSomething(const uno::Sequence< sal_Int8 >& rId)
875 {
876     return ::sw::UnoTunnelImpl<SwXTextRange>(rId, this);
877 }
878 
879 OUString SAL_CALL
880 SwXTextRange::getImplementationName()
881 {
882     return OUString("SwXTextRange");
883 }
884 
885 static char const*const g_ServicesTextRange[] =
886 {
887     "com.sun.star.text.TextRange",
888     "com.sun.star.style.CharacterProperties",
889     "com.sun.star.style.CharacterPropertiesAsian",
890     "com.sun.star.style.CharacterPropertiesComplex",
891     "com.sun.star.style.ParagraphProperties",
892     "com.sun.star.style.ParagraphPropertiesAsian",
893     "com.sun.star.style.ParagraphPropertiesComplex",
894 };
895 
896 static const size_t g_nServicesTextRange(SAL_N_ELEMENTS(g_ServicesTextRange));
897 
898 sal_Bool SAL_CALL SwXTextRange::supportsService(const OUString& rServiceName)
899 {
900     return cppu::supportsService(this, rServiceName);
901 }
902 
903 uno::Sequence< OUString > SAL_CALL
904 SwXTextRange::getSupportedServiceNames()
905 {
906     return ::sw::GetSupportedServiceNamesImpl(
907             g_nServicesTextRange, g_ServicesTextRange);
908 }
909 
910 uno::Reference< text::XText > SAL_CALL
911 SwXTextRange::getText()
912 {
913     SolarMutexGuard aGuard;
914 
915     if (!m_pImpl->m_xParentText.is())
916     {
917         if (m_pImpl->m_eRangePosition == RANGE_IS_TABLE &&
918             m_pImpl->m_pTableFormat)
919         {
920             SwTable const*const pTable = SwTable::FindTable( m_pImpl->m_pTableFormat );
921             SwTableNode const*const pTableNode = pTable->GetTableNode();
922             const SwPosition aPosition( *pTableNode );
923             m_pImpl->m_xParentText =
924                 ::sw::CreateParentXText(m_pImpl->m_rDoc, aPosition);
925         }
926     }
927     OSL_ENSURE(m_pImpl->m_xParentText.is(), "SwXTextRange::getText: no text");
928     return m_pImpl->m_xParentText;
929 }
930 
931 uno::Reference< text::XTextRange > SAL_CALL
932 SwXTextRange::getStart()
933 {
934     SolarMutexGuard aGuard;
935 
936     uno::Reference< text::XTextRange >  xRet;
937     ::sw::mark::IMark const * const pBkmk = m_pImpl->GetBookmark();
938     if (!m_pImpl->m_xParentText.is())
939     {
940         getText();
941     }
942     if(pBkmk)
943     {
944         SwPaM aPam(pBkmk->GetMarkStart());
945         xRet = new SwXTextRange(aPam, m_pImpl->m_xParentText);
946     }
947     else if (RANGE_IS_TABLE == m_pImpl->m_eRangePosition)
948     {
949         // start and end are this, if it's a table
950         xRet = this;
951     }
952     else
953     {
954         throw uno::RuntimeException();
955     }
956     return xRet;
957 }
958 
959 uno::Reference< text::XTextRange > SAL_CALL
960 SwXTextRange::getEnd()
961 {
962     SolarMutexGuard aGuard;
963 
964     uno::Reference< text::XTextRange >  xRet;
965     ::sw::mark::IMark const * const pBkmk = m_pImpl->GetBookmark();
966     if (!m_pImpl->m_xParentText.is())
967     {
968         getText();
969     }
970     if(pBkmk)
971     {
972         SwPaM aPam(pBkmk->GetMarkEnd());
973         xRet = new SwXTextRange(aPam, m_pImpl->m_xParentText);
974     }
975     else if (RANGE_IS_TABLE == m_pImpl->m_eRangePosition)
976     {
977         // start and end are this, if it's a table
978         xRet = this;
979     }
980     else
981     {
982         throw uno::RuntimeException();
983     }
984     return xRet;
985 }
986 
987 OUString SAL_CALL SwXTextRange::getString()
988 {
989     SolarMutexGuard aGuard;
990 
991     OUString sRet;
992     // for tables there is no bookmark, thus also no text
993     // one could export the table as ASCII here maybe?
994     SwPaM aPaM(GetDoc().GetNodes());
995     if (GetPositions(aPaM) && aPaM.HasMark())
996     {
997         SwUnoCursorHelper::GetTextFromPam(aPaM, sRet);
998     }
999     return sRet;
1000 }
1001 
1002 void SAL_CALL SwXTextRange::setString(const OUString& rString)
1003 {
1004     SolarMutexGuard aGuard;
1005 
1006     DeleteAndInsert(rString, false);
1007 }
1008 
1009 bool SwXTextRange::GetPositions(SwPaM& rToFill) const
1010 {
1011     ::sw::mark::IMark const * const pBkmk = m_pImpl->GetBookmark();
1012     if(pBkmk)
1013     {
1014         *rToFill.GetPoint() = pBkmk->GetMarkPos();
1015         if(pBkmk->IsExpanded())
1016         {
1017             rToFill.SetMark();
1018             *rToFill.GetMark() = pBkmk->GetOtherMarkPos();
1019         }
1020         else
1021         {
1022             rToFill.DeleteMark();
1023         }
1024         return true;
1025     }
1026     return false;
1027 }
1028 
1029 namespace sw {
1030 
1031 bool XTextRangeToSwPaM( SwUnoInternalPaM & rToFill,
1032         const uno::Reference< text::XTextRange > & xTextRange)
1033 {
1034     bool bRet = false;
1035 
1036     uno::Reference<lang::XUnoTunnel> xRangeTunnel( xTextRange, uno::UNO_QUERY);
1037     SwXTextRange* pRange = nullptr;
1038     OTextCursorHelper* pCursor = nullptr;
1039     SwXTextPortion* pPortion = nullptr;
1040     SwXText* pText = nullptr;
1041     SwXParagraph* pPara = nullptr;
1042     if(xRangeTunnel.is())
1043     {
1044         pRange  = ::sw::UnoTunnelGetImplementation<SwXTextRange>(xRangeTunnel);
1045         pCursor =
1046             ::sw::UnoTunnelGetImplementation<OTextCursorHelper>(xRangeTunnel);
1047         pPortion=
1048             ::sw::UnoTunnelGetImplementation<SwXTextPortion>(xRangeTunnel);
1049         pText   = ::sw::UnoTunnelGetImplementation<SwXText>(xRangeTunnel);
1050         pPara   = ::sw::UnoTunnelGetImplementation<SwXParagraph>(xRangeTunnel);
1051     }
1052 
1053     // if it's a text then create a temporary cursor there and re-use
1054     // the pCursor variable
1055     // #i108489#: Reference in outside scope to keep cursor alive
1056     uno::Reference< text::XTextCursor > xTextCursor;
1057     if (pText)
1058     {
1059         xTextCursor.set( pText->CreateCursor() );
1060         xTextCursor->gotoEnd(true);
1061         const uno::Reference<lang::XUnoTunnel> xCursorTunnel(
1062                 xTextCursor, uno::UNO_QUERY);
1063         pCursor =
1064             ::sw::UnoTunnelGetImplementation<OTextCursorHelper>(xCursorTunnel);
1065     }
1066     if(pRange && &pRange->GetDoc() == rToFill.GetDoc())
1067     {
1068         bRet = pRange->GetPositions(rToFill);
1069     }
1070     else
1071     {
1072         if (pPara)
1073         {
1074             bRet = pPara->SelectPaM(rToFill);
1075         }
1076         else
1077         {
1078             SwDoc* const pDoc = pCursor ? pCursor->GetDoc()
1079                 : (pPortion ? pPortion->GetCursor().GetDoc() : nullptr);
1080             const SwPaM* const pUnoCursor = pCursor ? pCursor->GetPaM()
1081                 : (pPortion ? &pPortion->GetCursor() : nullptr);
1082             if (pUnoCursor && pDoc == rToFill.GetDoc())
1083             {
1084                 OSL_ENSURE(!pUnoCursor->IsMultiSelection(),
1085                         "what to do about rings?");
1086                 bRet = true;
1087                 *rToFill.GetPoint() = *pUnoCursor->GetPoint();
1088                 if (pUnoCursor->HasMark())
1089                 {
1090                     rToFill.SetMark();
1091                     *rToFill.GetMark() = *pUnoCursor->GetMark();
1092                 }
1093                 else
1094                     rToFill.DeleteMark();
1095             }
1096         }
1097     }
1098     return bRet;
1099 }
1100 
1101 static bool
1102 lcl_IsStartNodeInFormat(const bool bHeader, SwStartNode const *const pSttNode,
1103     SwFrameFormat const*const pFrameFormat, SwFrameFormat*& rpFormat)
1104 {
1105     bool bRet = false;
1106     const SfxItemSet& rSet = pFrameFormat->GetAttrSet();
1107     const SfxPoolItem* pItem;
1108     if (SfxItemState::SET == rSet.GetItemState(
1109             bHeader ? sal_uInt16(RES_HEADER) : sal_uInt16(RES_FOOTER),
1110             true, &pItem))
1111     {
1112         SfxPoolItem *const pItemNonConst(const_cast<SfxPoolItem *>(pItem));
1113         SwFrameFormat *const pHeadFootFormat = bHeader ?
1114             static_cast<SwFormatHeader*>(pItemNonConst)->GetHeaderFormat() :
1115             static_cast<SwFormatFooter*>(pItemNonConst)->GetFooterFormat();
1116         if (pHeadFootFormat)
1117         {
1118             const SwFormatContent& rFlyContent = pHeadFootFormat->GetContent();
1119             const SwNode& rNode = rFlyContent.GetContentIdx()->GetNode();
1120             SwStartNode const*const pCurSttNode = rNode.FindSttNodeByType(
1121                 bHeader ? SwHeaderStartNode : SwFooterStartNode);
1122             if (pCurSttNode && (pCurSttNode == pSttNode))
1123             {
1124                 rpFormat = pHeadFootFormat;
1125                 bRet = true;
1126             }
1127         }
1128     }
1129     return bRet;
1130 }
1131 
1132 } // namespace sw
1133 
1134 uno::Reference< text::XTextRange >
1135 SwXTextRange::CreateXTextRange(
1136     SwDoc & rDoc, const SwPosition& rPos, const SwPosition *const pMark)
1137 {
1138     const uno::Reference<text::XText> xParentText(
1139             ::sw::CreateParentXText(rDoc, rPos));
1140     const auto pNewCursor(rDoc.CreateUnoCursor(rPos));
1141     if(pMark)
1142     {
1143         pNewCursor->SetMark();
1144         *pNewCursor->GetMark() = *pMark;
1145     }
1146     const bool isCell( dynamic_cast<SwXCell*>(xParentText.get()) );
1147     const uno::Reference< text::XTextRange > xRet(
1148         new SwXTextRange(*pNewCursor, xParentText,
1149             isCell ? RANGE_IN_CELL : RANGE_IN_TEXT) );
1150     return xRet;
1151 }
1152 
1153 namespace sw {
1154 
1155 uno::Reference< text::XText >
1156 CreateParentXText(SwDoc & rDoc, const SwPosition& rPos)
1157 {
1158     uno::Reference< text::XText > xParentText;
1159     SwStartNode* pSttNode = rPos.nNode.GetNode().StartOfSectionNode();
1160     while(pSttNode && pSttNode->IsSectionNode())
1161     {
1162         pSttNode = pSttNode->StartOfSectionNode();
1163     }
1164     SwStartNodeType eType = pSttNode ? pSttNode->GetStartNodeType() : SwNormalStartNode;
1165     switch(eType)
1166     {
1167         case SwTableBoxStartNode:
1168         {
1169             SwTableNode const*const pTableNode = pSttNode->FindTableNode();
1170             SwFrameFormat *const pTableFormat =
1171                 static_cast<SwFrameFormat*>(pTableNode->GetTable().GetFrameFormat());
1172             SwTableBox *const  pBox = pSttNode->GetTableBox();
1173 
1174             xParentText = pBox
1175                 ? SwXCell::CreateXCell( pTableFormat, pBox )
1176                 : new SwXCell( pTableFormat, *pSttNode );
1177         }
1178         break;
1179         case SwFlyStartNode:
1180         {
1181             SwFrameFormat *const pFormat = pSttNode->GetFlyFormat();
1182             if (nullptr != pFormat)
1183             {
1184                 xParentText.set(SwXTextFrame::CreateXTextFrame(rDoc, pFormat),
1185                         uno::UNO_QUERY);
1186             }
1187         }
1188         break;
1189         case SwHeaderStartNode:
1190         case SwFooterStartNode:
1191         {
1192             const bool bHeader = (SwHeaderStartNode == eType);
1193             const size_t nPDescCount = rDoc.GetPageDescCnt();
1194             for(size_t i = 0; i < nPDescCount; i++)
1195             {
1196                 const SwPageDesc& rDesc = rDoc.GetPageDesc( i );
1197                 const SwFrameFormat* pFrameFormatMaster = &rDesc.GetMaster();
1198                 const SwFrameFormat* pFrameFormatLeft = &rDesc.GetLeft();
1199 
1200                 SwFrameFormat* pHeadFootFormat = nullptr;
1201                 if (!lcl_IsStartNodeInFormat(bHeader, pSttNode, pFrameFormatMaster,
1202                             pHeadFootFormat))
1203                 {
1204                     lcl_IsStartNodeInFormat(bHeader, pSttNode, pFrameFormatLeft,
1205                             pHeadFootFormat);
1206                 }
1207 
1208                 if (pHeadFootFormat)
1209                 {
1210                     xParentText = SwXHeadFootText::CreateXHeadFootText(
1211                             *pHeadFootFormat, bHeader);
1212                 }
1213             }
1214         }
1215         break;
1216         case SwFootnoteStartNode:
1217         {
1218             const size_t nFootnoteCnt = rDoc.GetFootnoteIdxs().size();
1219             for (size_t n = 0; n < nFootnoteCnt; ++n )
1220             {
1221                 const SwTextFootnote* pTextFootnote = rDoc.GetFootnoteIdxs()[ n ];
1222                 const SwFormatFootnote& rFootnote = pTextFootnote->GetFootnote();
1223                 pTextFootnote = rFootnote.GetTextFootnote();
1224 
1225                 if (pSttNode == pTextFootnote->GetStartNode()->GetNode().
1226                                     FindSttNodeByType(SwFootnoteStartNode))
1227                 {
1228                     xParentText.set(SwXFootnote::CreateXFootnote(rDoc,
1229                             &const_cast<SwFormatFootnote&>(rFootnote)), uno::UNO_QUERY);
1230                     break;
1231                 }
1232             }
1233         }
1234         break;
1235         default:
1236         {
1237             // then it is the body text
1238             const uno::Reference<frame::XModel> xModel =
1239                 rDoc.GetDocShell()->GetBaseModel();
1240             const uno::Reference< text::XTextDocument > xDoc(
1241                 xModel, uno::UNO_QUERY);
1242             xParentText = xDoc->getText();
1243         }
1244     }
1245     OSL_ENSURE(xParentText.is(), "no parent text?");
1246     return xParentText;
1247 }
1248 
1249 } // namespace sw
1250 
1251 uno::Reference< container::XEnumeration > SAL_CALL
1252 SwXTextRange::createContentEnumeration(const OUString& rServiceName)
1253 {
1254     SolarMutexGuard g;
1255 
1256     if ( rServiceName != "com.sun.star.text.TextContent" )
1257     {
1258         throw uno::RuntimeException();
1259     }
1260 
1261     if (!m_pImpl->GetBookmark())
1262     {
1263         throw uno::RuntimeException();
1264     }
1265     const SwPosition aPos(GetDoc().GetNodes().GetEndOfContent());
1266     const auto pNewCursor(m_pImpl->m_rDoc.CreateUnoCursor(aPos));
1267     if (!GetPositions(*pNewCursor))
1268     {
1269         throw uno::RuntimeException();
1270     }
1271 
1272     return SwXParaFrameEnumeration::Create(*pNewCursor, PARAFRAME_PORTION_TEXTRANGE);
1273 }
1274 
1275 uno::Reference< container::XEnumeration > SAL_CALL
1276 SwXTextRange::createEnumeration()
1277 {
1278     SolarMutexGuard g;
1279 
1280     if (!m_pImpl->GetBookmark())
1281     {
1282         throw uno::RuntimeException();
1283     }
1284     const SwPosition aPos(GetDoc().GetNodes().GetEndOfContent());
1285     auto pNewCursor(m_pImpl->m_rDoc.CreateUnoCursor(aPos));
1286     if (!GetPositions(*pNewCursor))
1287     {
1288         throw uno::RuntimeException();
1289     }
1290     if (!m_pImpl->m_xParentText.is())
1291     {
1292         getText();
1293     }
1294 
1295     const CursorType eSetType = (RANGE_IN_CELL == m_pImpl->m_eRangePosition)
1296             ? CursorType::SelectionInTable : CursorType::Selection;
1297     return SwXParagraphEnumeration::Create(m_pImpl->m_xParentText, pNewCursor, eSetType);
1298 }
1299 
1300 uno::Type SAL_CALL SwXTextRange::getElementType()
1301 {
1302     return cppu::UnoType<text::XTextRange>::get();
1303 }
1304 
1305 sal_Bool SAL_CALL SwXTextRange::hasElements()
1306 {
1307     return true;
1308 }
1309 
1310 uno::Sequence< OUString > SAL_CALL
1311 SwXTextRange::getAvailableServiceNames()
1312 {
1313     uno::Sequence<OUString> aRet { "com.sun.star.text.TextContent" };
1314     return aRet;
1315 }
1316 
1317 uno::Reference< beans::XPropertySetInfo > SAL_CALL
1318 SwXTextRange::getPropertySetInfo()
1319 {
1320     SolarMutexGuard aGuard;
1321 
1322     static uno::Reference< beans::XPropertySetInfo > xRef =
1323         m_pImpl->m_rPropSet.getPropertySetInfo();
1324     return xRef;
1325 }
1326 
1327 void SAL_CALL
1328 SwXTextRange::setPropertyValue(
1329         const OUString& rPropertyName, const uno::Any& rValue)
1330 {
1331     SolarMutexGuard aGuard;
1332 
1333     if (!m_pImpl->GetBookmark())
1334     {
1335         throw uno::RuntimeException();
1336     }
1337     SwPaM aPaM(GetDoc().GetNodes());
1338     GetPositions(aPaM);
1339     SwUnoCursorHelper::SetPropertyValue(aPaM, m_pImpl->m_rPropSet,
1340             rPropertyName, rValue);
1341 }
1342 
1343 uno::Any SAL_CALL
1344 SwXTextRange::getPropertyValue(const OUString& rPropertyName)
1345 {
1346     SolarMutexGuard aGuard;
1347 
1348     if (!m_pImpl->GetBookmark())
1349     {
1350         throw uno::RuntimeException();
1351     }
1352     SwPaM aPaM(GetDoc().GetNodes());
1353     GetPositions(aPaM);
1354     return SwUnoCursorHelper::GetPropertyValue(aPaM, m_pImpl->m_rPropSet,
1355             rPropertyName);
1356 }
1357 
1358 void SAL_CALL
1359 SwXTextRange::addPropertyChangeListener(
1360         const OUString& /*rPropertyName*/,
1361         const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
1362 {
1363     OSL_FAIL("SwXTextRange::addPropertyChangeListener(): not implemented");
1364 }
1365 
1366 void SAL_CALL
1367 SwXTextRange::removePropertyChangeListener(
1368         const OUString& /*rPropertyName*/,
1369         const uno::Reference< beans::XPropertyChangeListener >& /*xListener*/)
1370 {
1371     OSL_FAIL("SwXTextRange::removePropertyChangeListener(): not implemented");
1372 }
1373 
1374 void SAL_CALL
1375 SwXTextRange::addVetoableChangeListener(
1376         const OUString& /*rPropertyName*/,
1377         const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
1378 {
1379     OSL_FAIL("SwXTextRange::addVetoableChangeListener(): not implemented");
1380 }
1381 
1382 void SAL_CALL
1383 SwXTextRange::removeVetoableChangeListener(
1384         const OUString& /*rPropertyName*/,
1385         const uno::Reference< beans::XVetoableChangeListener >& /*xListener*/)
1386 {
1387     OSL_FAIL("SwXTextRange::removeVetoableChangeListener(): not implemented");
1388 }
1389 
1390 beans::PropertyState SAL_CALL
1391 SwXTextRange::getPropertyState(const OUString& rPropertyName)
1392 {
1393     SolarMutexGuard aGuard;
1394 
1395     if (!m_pImpl->GetBookmark())
1396     {
1397         throw uno::RuntimeException();
1398     }
1399     SwPaM aPaM(GetDoc().GetNodes());
1400     GetPositions(aPaM);
1401     return SwUnoCursorHelper::GetPropertyState(aPaM, m_pImpl->m_rPropSet,
1402             rPropertyName);
1403 }
1404 
1405 uno::Sequence< beans::PropertyState > SAL_CALL
1406 SwXTextRange::getPropertyStates(const uno::Sequence< OUString >& rPropertyName)
1407 {
1408     SolarMutexGuard g;
1409 
1410     if (!m_pImpl->GetBookmark())
1411     {
1412         throw uno::RuntimeException();
1413     }
1414     SwPaM aPaM(GetDoc().GetNodes());
1415     GetPositions(aPaM);
1416     return SwUnoCursorHelper::GetPropertyStates(aPaM, m_pImpl->m_rPropSet,
1417             rPropertyName);
1418 }
1419 
1420 void SAL_CALL SwXTextRange::setPropertyToDefault(const OUString& rPropertyName)
1421 {
1422     SolarMutexGuard aGuard;
1423 
1424     if (!m_pImpl->GetBookmark())
1425     {
1426         throw uno::RuntimeException();
1427     }
1428     SwPaM aPaM(GetDoc().GetNodes());
1429     GetPositions(aPaM);
1430     SwUnoCursorHelper::SetPropertyToDefault(aPaM, m_pImpl->m_rPropSet,
1431             rPropertyName);
1432 }
1433 
1434 uno::Any SAL_CALL
1435 SwXTextRange::getPropertyDefault(const OUString& rPropertyName)
1436 {
1437     SolarMutexGuard aGuard;
1438 
1439     if (!m_pImpl->GetBookmark())
1440     {
1441         throw uno::RuntimeException();
1442     }
1443     SwPaM aPaM(GetDoc().GetNodes());
1444     GetPositions(aPaM);
1445     return SwUnoCursorHelper::GetPropertyDefault(aPaM, m_pImpl->m_rPropSet,
1446             rPropertyName);
1447 }
1448 
1449 void SAL_CALL
1450 SwXTextRange::makeRedline(
1451     const OUString& rRedlineType,
1452     const uno::Sequence< beans::PropertyValue >& rRedlineProperties )
1453 {
1454     SolarMutexGuard aGuard;
1455 
1456     if (!m_pImpl->GetBookmark())
1457     {
1458         throw uno::RuntimeException();
1459     }
1460     SwPaM aPaM(GetDoc().GetNodes());
1461     SwXTextRange::GetPositions(aPaM);
1462     SwUnoCursorHelper::makeRedline( aPaM, rRedlineType, rRedlineProperties );
1463 }
1464 
1465 struct SwXTextRangesImpl final : public SwXTextRanges
1466 {
1467 
1468     // XUnoTunnel
1469     virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& rIdentifier) override;
1470 
1471     // XServiceInfo
1472     virtual OUString SAL_CALL getImplementationName() override
1473         { return OUString("SwXTextRanges"); };
1474     virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName) override
1475         { return cppu::supportsService(this, rServiceName); };
1476     virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
1477         { return { "com.sun.star.text.TextRanges" }; };
1478 
1479     // XElementAccess
1480     virtual css::uno::Type SAL_CALL getElementType() override
1481         { return cppu::UnoType<text::XTextRange>::get(); };
1482     virtual sal_Bool SAL_CALL hasElements() override
1483         { return getCount() > 0; };
1484     // XIndexAccess
1485     virtual sal_Int32 SAL_CALL getCount() override;
1486     virtual css::uno::Any SAL_CALL getByIndex(sal_Int32 nIndex) override;
1487 
1488     explicit SwXTextRangesImpl(SwPaM *const pPaM)
1489     {
1490         if (pPaM)
1491         {
1492             m_pUnoCursor.reset(pPaM->GetDoc()->CreateUnoCursor(*pPaM->GetPoint()));
1493             ::sw::DeepCopyPaM(*pPaM, *GetCursor());
1494         }
1495         MakeRanges();
1496     }
1497     virtual void SAL_CALL release() throw () override
1498     {
1499         SolarMutexGuard g;
1500         OWeakObject::release();
1501     }
1502     virtual SwUnoCursor* GetCursor() override
1503         { return &(*m_pUnoCursor); };
1504     void MakeRanges();
1505     std::vector< uno::Reference< text::XTextRange > > m_Ranges;
1506     sw::UnoCursorPointer m_pUnoCursor;
1507 };
1508 
1509 void SwXTextRangesImpl::MakeRanges()
1510 {
1511     if (GetCursor())
1512     {
1513         for(SwPaM& rTmpCursor : GetCursor()->GetRingContainer())
1514         {
1515             const uno::Reference< text::XTextRange > xRange(
1516                     SwXTextRange::CreateXTextRange(
1517                         *rTmpCursor.GetDoc(),
1518                         *rTmpCursor.GetPoint(), rTmpCursor.GetMark()));
1519             if (xRange.is())
1520             {
1521                 m_Ranges.push_back(xRange);
1522             }
1523         }
1524     }
1525 }
1526 
1527 SwXTextRanges* SwXTextRanges::Create(SwPaM *const pPaM)
1528     { return new SwXTextRangesImpl(pPaM); }
1529 
1530 namespace
1531 {
1532     class theSwXTextRangesUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextRangesUnoTunnelId > {};
1533 }
1534 
1535 const uno::Sequence< sal_Int8 > & SwXTextRanges::getUnoTunnelId()
1536     { return theSwXTextRangesUnoTunnelId::get().getSeq(); }
1537 
1538 sal_Int64 SAL_CALL
1539 SwXTextRangesImpl::getSomething(const uno::Sequence< sal_Int8 >& rId)
1540 {
1541     return ::sw::UnoTunnelImpl<SwXTextRanges>(rId, this);
1542 }
1543 
1544 /*
1545  *  Text positions
1546  * Up to the first access to a text position, only a SwCursor is stored.
1547  * Afterwards, an array with uno::Reference<XTextPosition> will be created.
1548  */
1549 
1550 sal_Int32 SAL_CALL SwXTextRangesImpl::getCount()
1551 {
1552     SolarMutexGuard aGuard;
1553     return static_cast<sal_Int32>(m_Ranges.size());
1554 }
1555 
1556 uno::Any SAL_CALL SwXTextRangesImpl::getByIndex(sal_Int32 nIndex)
1557 {
1558     SolarMutexGuard aGuard;
1559     if ((nIndex < 0) || (static_cast<size_t>(nIndex) >= m_Ranges.size()))
1560         throw lang::IndexOutOfBoundsException();
1561     uno::Any ret;
1562     ret <<= m_Ranges.at(nIndex);
1563     return ret;
1564 }
1565 
1566 void SwUnoCursorHelper::SetString(SwCursor & rCursor, const OUString& rString)
1567 {
1568     // Start/EndAction
1569     SwDoc *const pDoc = rCursor.GetDoc();
1570     UnoActionContext aAction(pDoc);
1571     pDoc->GetIDocumentUndoRedo().StartUndo(SwUndoId::INSERT, nullptr);
1572     if (rCursor.HasMark())
1573     {
1574         pDoc->getIDocumentContentOperations().DeleteAndJoin(rCursor);
1575     }
1576     if (!rString.isEmpty())
1577     {
1578         const bool bSuccess( SwUnoCursorHelper::DocInsertStringSplitCR(
1579                     *pDoc, rCursor, rString, false ) );
1580         OSL_ENSURE( bSuccess, "DocInsertStringSplitCR" );
1581         SwUnoCursorHelper::SelectPam(rCursor, true);
1582         rCursor.Left(rString.getLength());
1583     }
1584     pDoc->GetIDocumentUndoRedo().EndUndo(SwUndoId::INSERT, nullptr);
1585 }
1586 
1587 struct SwXParaFrameEnumerationImpl final : public SwXParaFrameEnumeration
1588 {
1589     // XServiceInfo
1590     virtual OUString SAL_CALL getImplementationName() override
1591         { return OUString("SwXParaFrameEnumeration"); };
1592     virtual sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override
1593         { return cppu::supportsService(this, rServiceName); };
1594     virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
1595         { return {"com.sun.star.util.ContentEnumeration"}; };
1596 
1597     // XEnumeration
1598     virtual sal_Bool SAL_CALL hasMoreElements() override;
1599     virtual css::uno::Any SAL_CALL nextElement() override;
1600 
1601     SwXParaFrameEnumerationImpl(const SwPaM& rPaM, const enum ParaFrameMode eParaFrameMode, SwFrameFormat* const pFormat);
1602     virtual void SAL_CALL release() throw () override
1603     {
1604         SolarMutexGuard g;
1605         OWeakObject::release();
1606     }
1607     SwUnoCursor& GetCursor()
1608         { return *m_pUnoCursor; }
1609     void PurgeFrameClients()
1610     {
1611         if(!m_pUnoCursor)
1612         {
1613             m_vFrames.clear();
1614             m_xNextObject = nullptr;
1615         }
1616         else
1617         {
1618             // removing orphaned Clients
1619             const auto iter = std::remove_if(m_vFrames.begin(), m_vFrames.end(),
1620                     [] (std::shared_ptr<sw::FrameClient>& rEntry) -> bool { return !rEntry->GetRegisteredIn(); });
1621             m_vFrames.erase(iter, m_vFrames.end());
1622         }
1623     }
1624     void FillFrame();
1625     bool CreateNextObject();
1626     uno::Reference< text::XTextContent > m_xNextObject;
1627     FrameClientList_t m_vFrames;
1628     ::sw::UnoCursorPointer m_pUnoCursor;
1629 };
1630 
1631 
1632 SwXParaFrameEnumeration* SwXParaFrameEnumeration::Create(const SwPaM& rPaM, const enum ParaFrameMode eParaFrameMode, SwFrameFormat* const pFormat)
1633     { return new SwXParaFrameEnumerationImpl(rPaM, eParaFrameMode, pFormat); }
1634 
1635 SwXParaFrameEnumerationImpl::SwXParaFrameEnumerationImpl(
1636         const SwPaM& rPaM, const enum ParaFrameMode eParaFrameMode,
1637         SwFrameFormat* const pFormat)
1638     : m_pUnoCursor(rPaM.GetDoc()->CreateUnoCursor(*rPaM.GetPoint()))
1639 {
1640     if (rPaM.HasMark())
1641     {
1642         GetCursor().SetMark();
1643         *GetCursor().GetMark() = *rPaM.GetMark();
1644     }
1645     if (PARAFRAME_PORTION_PARAGRAPH == eParaFrameMode)
1646     {
1647         FrameClientSortList_t vFrames;
1648         ::CollectFrameAtNode(rPaM.GetPoint()->nNode, vFrames, false);
1649         std::transform(vFrames.begin(), vFrames.end(),
1650             std::back_inserter(m_vFrames),
1651             [] (const FrameClientSortListEntry& rEntry) { return rEntry.pFrameClient; });
1652     }
1653     else if (pFormat)
1654     {
1655         m_vFrames.push_back(std::make_shared<sw::FrameClient>(pFormat));
1656     }
1657     else if ((PARAFRAME_PORTION_CHAR == eParaFrameMode) ||
1658              (PARAFRAME_PORTION_TEXTRANGE == eParaFrameMode))
1659     {
1660         if (PARAFRAME_PORTION_TEXTRANGE == eParaFrameMode)
1661         {
1662             //get all frames that are bound at paragraph or at character
1663             for(const auto& pFlyFrame : rPaM.GetDoc()->GetAllFlyFormats(&GetCursor(), false, true))
1664             {
1665                 const auto pFrameFormat = const_cast<SwFrameFormat*>(&pFlyFrame->GetFormat());
1666                 m_vFrames.push_back(std::make_shared<sw::FrameClient>(pFrameFormat));
1667             }
1668         }
1669         FillFrame();
1670     }
1671 }
1672 
1673 // Search for a FLYCNT text attribute at the cursor point and fill the frame
1674 // into the array
1675 void SwXParaFrameEnumerationImpl::FillFrame()
1676 {
1677     if(!m_pUnoCursor->GetNode().IsTextNode())
1678         return;
1679     // search for objects at the cursor - anchored at/as char
1680     const auto pTextAttr = m_pUnoCursor->GetNode().GetTextNode()->GetTextAttrForCharAt(
1681             m_pUnoCursor->GetPoint()->nContent.GetIndex(), RES_TXTATR_FLYCNT);
1682     if(!pTextAttr)
1683         return;
1684     const SwFormatFlyCnt& rFlyCnt = pTextAttr->GetFlyCnt();
1685     SwFrameFormat* const pFrameFormat = rFlyCnt.GetFrameFormat();
1686     m_vFrames.push_back(std::make_shared<sw::FrameClient>(pFrameFormat));
1687 }
1688 
1689 bool SwXParaFrameEnumerationImpl::CreateNextObject()
1690 {
1691     if (m_vFrames.empty())
1692         return false;
1693 
1694     SwFrameFormat* const pFormat = static_cast<SwFrameFormat*>(
1695             m_vFrames.front()->GetRegisteredIn());
1696     m_vFrames.pop_front();
1697     // the format should be valid here, otherwise the client
1698     // would have been removed by PurgeFrameClients
1699     // check for a shape first
1700     if(pFormat->Which() == RES_DRAWFRMFMT)
1701     {
1702         SdrObject* pObject(nullptr);
1703         pFormat->CallSwClientNotify(sw::FindSdrObjectHint(pObject));
1704         if(pObject)
1705             m_xNextObject.set(pObject->getUnoShape(), uno::UNO_QUERY);
1706     }
1707     else
1708     {
1709         const SwNodeIndex* pIdx = pFormat->GetContent().GetContentIdx();
1710         OSL_ENSURE(pIdx, "where is the index?");
1711         SwNode const*const pNd =
1712             m_pUnoCursor->GetDoc()->GetNodes()[ pIdx->GetIndex() + 1 ];
1713 
1714         if (!pNd->IsNoTextNode())
1715         {
1716             m_xNextObject.set(SwXTextFrame::CreateXTextFrame(
1717                         *pFormat->GetDoc(), pFormat));
1718         }
1719         else if (pNd->IsGrfNode())
1720         {
1721             m_xNextObject.set(SwXTextGraphicObject::CreateXTextGraphicObject(
1722                         *pFormat->GetDoc(), pFormat));
1723         }
1724         else
1725         {
1726             assert(pNd->IsOLENode());
1727             m_xNextObject.set(SwXTextEmbeddedObject::CreateXTextEmbeddedObject(
1728                         *pFormat->GetDoc(), pFormat));
1729         }
1730     }
1731     return m_xNextObject.is();
1732 }
1733 
1734 sal_Bool SAL_CALL
1735 SwXParaFrameEnumerationImpl::hasMoreElements()
1736 {
1737     SolarMutexGuard aGuard;
1738     PurgeFrameClients();
1739     return m_xNextObject.is() || CreateNextObject();
1740 }
1741 
1742 uno::Any SAL_CALL SwXParaFrameEnumerationImpl::nextElement()
1743 {
1744     SolarMutexGuard aGuard;
1745     PurgeFrameClients();
1746     if (!m_xNextObject.is() && !m_vFrames.empty())
1747         CreateNextObject();
1748     if (!m_xNextObject.is())
1749         throw container::NoSuchElementException();
1750     uno::Any aRet;
1751     aRet <<= m_xNextObject;
1752     m_xNextObject = nullptr;
1753     return aRet;
1754 }
1755 
1756 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1757