LibreOffice Module sw (master)  1
unoportenum.cxx
Go to the documentation of this file.
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 #include <sal/log.hxx>
22 
23 #include <utility>
24 
25 #include <unoport.hxx>
26 #include <IMark.hxx>
27 #include <crossrefbookmark.hxx>
28 #include <annotationmark.hxx>
29 #include <doc.hxx>
31 #include <txatbase.hxx>
32 #include <txtatr.hxx>
33 #include <ndhints.hxx>
34 #include <ndtxt.hxx>
35 #include <unocrsr.hxx>
36 #include <docary.hxx>
37 #include <textboxhelper.hxx>
38 #include <tox.hxx>
39 #include <unoparaframeenum.hxx>
40 #include <unocrsrhelper.hxx>
41 #include <unorefmark.hxx>
42 #include <unobookmark.hxx>
43 #include <unofield.hxx>
44 #include <unometa.hxx>
45 #include <fmtfld.hxx>
46 #include <fldbas.hxx>
47 #include <fmtmeta.hxx>
48 #include <fmtanchr.hxx>
49 #include <fmtrfmrk.hxx>
50 #include <frmfmt.hxx>
51 #include <fmtflcnt.hxx>
52 #include <unoidx.hxx>
53 #include <unocoll.hxx>
54 #include <redline.hxx>
55 #include <txtannotationfld.hxx>
56 #include <vcl/svapp.hxx>
57 #include <comphelper/string.hxx>
60 #include <com/sun/star/container/XEnumeration.hpp>
61 #include <algorithm>
62 #include <memory>
63 #include <set>
64 #include <stack>
65 
66 using namespace ::com::sun::star;
67 using namespace ::com::sun::star::uno;
68 using namespace ::com::sun::star::text;
69 using namespace ::std;
70 
71 typedef std::pair< TextRangeList_t * const, SwTextAttr const * const > PortionList_t;
72 typedef std::stack< PortionList_t > PortionStack_t;
73 
74 static void lcl_CreatePortions(
75  TextRangeList_t & i_rPortions,
76  uno::Reference< text::XText > const& i_xParentText,
77  SwUnoCursor* pUnoCursor,
78  FrameClientSortList_t & i_rFrames,
79  const sal_Int32 i_nStartPos, const sal_Int32 i_nEndPos );
80 
81 namespace
82 {
83  enum class BkmType {
84  Start, End, StartEnd
85  };
86 
87  struct SwXBookmarkPortion_Impl
88  {
89  Reference<XTextContent> xBookmark;
90  BkmType nBkmType;
91  const SwPosition aPosition;
92 
93  SwXBookmarkPortion_Impl(uno::Reference<text::XTextContent> const& xMark,
94  const BkmType nType, SwPosition const& rPosition)
95  : xBookmark ( xMark )
96  , nBkmType ( nType )
97  , aPosition ( rPosition )
98  {
99  }
100  sal_Int32 getIndex () const
101  {
102  return aPosition.nContent.GetIndex();
103  }
104  };
105  typedef std::shared_ptr < SwXBookmarkPortion_Impl > SwXBookmarkPortion_ImplSharedPtr;
106  struct BookmarkCompareStruct
107  {
108  bool operator () ( const SwXBookmarkPortion_ImplSharedPtr &r1,
109  const SwXBookmarkPortion_ImplSharedPtr &r2 ) const
110  {
111  // #i16896# for bookmark portions at the same position, the start should
112  // always precede the end. Hence compare positions, and use bookmark type
113  // as tie-breaker for same position.
114  // return ( r1->nIndex == r2->nIndex )
115  // ? ( r1->nBkmType < r2->nBkmType )
116  // : ( r1->nIndex < r2->nIndex );
117 
118  // Note that the above code does not correctly handle
119  // the case when one bookmark ends, and another begins in the same
120  // position. When this occurs, the above code will return the
121  // start of the 2nd bookmark BEFORE the end of the first bookmark
122  // See bug #i58438# for more details. The below code is correct and
123  // fixes both #i58438 and #i16896#
124  return r1->aPosition < r2->aPosition;
125  }
126  };
127  typedef std::multiset < SwXBookmarkPortion_ImplSharedPtr, BookmarkCompareStruct > SwXBookmarkPortion_ImplList;
128 
130  void lcl_FillBookmark(sw::mark::IMark* const pBkmk, const SwNodeIndex& nOwnNode, SwDoc& rDoc, SwXBookmarkPortion_ImplList& rBkmArr)
131  {
132  bool const hasOther = pBkmk->IsExpanded();
133 
134  const SwPosition& rStartPos = pBkmk->GetMarkStart();
135  if(rStartPos.nNode == nOwnNode)
136  {
137  // #i109272#: cross reference marks: need special handling!
138  ::sw::mark::CrossRefBookmark *const pCrossRefMark(dynamic_cast< ::sw::mark::CrossRefBookmark*>(pBkmk));
139  BkmType const nType = (hasOther || pCrossRefMark)
140  ? BkmType::Start : BkmType::StartEnd;
141  rBkmArr.insert(std::make_shared<SwXBookmarkPortion_Impl>(
142  SwXBookmark::CreateXBookmark(rDoc, pBkmk),
143  nType, rStartPos));
144  }
145 
146  const SwPosition& rEndPos = pBkmk->GetMarkEnd();
147  if(rEndPos.nNode != nOwnNode)
148  return;
149 
150  unique_ptr<SwPosition> pCrossRefEndPos;
151  const SwPosition* pEndPos = nullptr;
152  ::sw::mark::CrossRefBookmark *const pCrossRefMark(dynamic_cast< ::sw::mark::CrossRefBookmark*>(pBkmk));
153  if(hasOther)
154  {
155  pEndPos = &rEndPos;
156  }
157  else if (pCrossRefMark)
158  {
159  // Crossrefbookmarks only remember the start position but have to span the whole paragraph
160  pCrossRefEndPos = std::make_unique<SwPosition>(rEndPos);
161  pCrossRefEndPos->nContent = pCrossRefEndPos->nNode.GetNode().GetTextNode()->Len();
162  pEndPos = pCrossRefEndPos.get();
163  }
164  if(pEndPos)
165  {
166  rBkmArr.insert(std::make_shared<SwXBookmarkPortion_Impl>(
167  SwXBookmark::CreateXBookmark(rDoc, pBkmk),
168  BkmType::End, *pEndPos));
169  }
170  }
171 
172  void lcl_FillBookmarkArray(SwDoc& rDoc, SwUnoCursor& rUnoCursor, SwXBookmarkPortion_ImplList& rBkmArr)
173  {
174  IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
175  if(!pMarkAccess->getBookmarksCount())
176  return;
177 
178  const SwNodeIndex nOwnNode = rUnoCursor.GetPoint()->nNode;
179  SwTextNode* pTextNode = nOwnNode.GetNode().GetTextNode();
180  assert(pTextNode);
181  // A text node already knows its marks via its SwIndexes.
183  for (const SwIndex* pIndex = pTextNode->GetFirstIndex(); pIndex; pIndex = pIndex->GetNext())
184  {
185  // Need a non-cost mark here, as we'll create a UNO wrapper around it.
186  sw::mark::IMark* pBkmk = const_cast<sw::mark::IMark*>(pIndex->GetMark());
187  if (!pBkmk)
188  continue;
190  // These are the types stored in the container otherwise accessible via getBookmarks*()
193  continue;
194  // Only handle bookmarks once, if they start and end at this node as well.
195  if (!aSeenMarks.insert(pBkmk).second)
196  continue;
197  lcl_FillBookmark(pBkmk, nOwnNode, rDoc, rBkmArr);
198  }
199  }
200 
201  class theSwXTextPortionEnumerationUnoTunnelId : public rtl::Static< UnoTunnelIdInit, theSwXTextPortionEnumerationUnoTunnelId > {};
202  struct SwAnnotationStartPortion_Impl
203  {
204 
205  uno::Reference< text::XTextField > mxAnnotationField;
206  const SwPosition maPosition;
207 
208  SwAnnotationStartPortion_Impl(
209  uno::Reference< text::XTextField > const& xAnnotationField,
210  SwPosition const& rPosition)
211  : mxAnnotationField ( xAnnotationField )
212  , maPosition ( rPosition )
213  {
214  }
215 
216  sal_Int32 getIndex () const
217  {
218  return maPosition.nContent.GetIndex();
219  }
220  };
221  typedef std::shared_ptr < SwAnnotationStartPortion_Impl > SwAnnotationStartPortion_ImplSharedPtr;
222  struct AnnotationStartCompareStruct
223  {
224  bool operator () ( const SwAnnotationStartPortion_ImplSharedPtr &r1,
225  const SwAnnotationStartPortion_ImplSharedPtr &r2 )
226  const
227  {
228  return r1->maPosition < r2->maPosition;
229  }
230  };
231  typedef std::multiset < SwAnnotationStartPortion_ImplSharedPtr, AnnotationStartCompareStruct > SwAnnotationStartPortion_ImplList;
232 
233  void lcl_FillAnnotationStartArray(
234  SwDoc& rDoc,
235  SwUnoCursor& rUnoCursor,
236  SwAnnotationStartPortion_ImplList& rAnnotationStartArr )
237  {
238  IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
239  if ( pMarkAccess->getAnnotationMarksCount() == 0 )
240  {
241  return;
242  }
243 
244  // no need to consider annotation marks starting after aEndOfPara
245  SwPosition aEndOfPara(*rUnoCursor.GetPoint());
246  aEndOfPara.nContent = aEndOfPara.nNode.GetNode().GetTextNode()->Len();
247  const IDocumentMarkAccess::const_iterator_t pCandidatesEnd =
248  pMarkAccess->findFirstAnnotationStartsAfter(aEndOfPara);
249 
250  // search for all annotation marks that have its start position in this paragraph
251  const SwNodeIndex nOwnNode = rUnoCursor.GetPoint()->nNode;
253  ppMark != pCandidatesEnd;
254  ++ppMark )
255  {
256  ::sw::mark::AnnotationMark* const pAnnotationMark =
257  dynamic_cast< ::sw::mark::AnnotationMark* >(*ppMark);
258 
259  if (!pAnnotationMark)
260  continue;
261 
262  const SwPosition& rStartPos = pAnnotationMark->GetMarkStart();
263  if (rStartPos.nNode != nOwnNode)
264  continue;
265 
266  const SwFormatField* pAnnotationFormatField = pAnnotationMark->GetAnnotationFormatField();
267  if (!pAnnotationFormatField)
268  {
269  SAL_WARN("sw.core", "missing annotation format field");
270  continue;
271  }
272 
273  rAnnotationStartArr.insert(
274  std::make_shared<SwAnnotationStartPortion_Impl>(
276  pAnnotationFormatField),
277  rStartPos));
278  }
279  }
280 }
281 
282 const uno::Sequence< sal_Int8 > & SwXTextPortionEnumeration::getUnoTunnelId()
283 {
285 }
286 
288  const uno::Sequence< sal_Int8 >& rId )
289 {
290  if( isUnoTunnelId<SwXTextPortionEnumeration>(rId) )
291  {
292  return sal::static_int_cast< sal_Int64 >( reinterpret_cast< sal_IntPtr >( this ) );
293  }
294  return 0;
295 }
296 
298 {
299  return "SwXTextPortionEnumeration";
300 }
301 
302 sal_Bool
303 SwXTextPortionEnumeration::supportsService(const OUString& rServiceName)
304 {
305  return cppu::supportsService(this, rServiceName);
306 }
307 
309 {
310  return { "com.sun.star.text.TextPortionEnumeration" };
311 }
312 
314  SwPaM& rParaCursor,
315  uno::Reference< XText > const & xParentText,
316  const sal_Int32 nStart,
317  const sal_Int32 nEnd )
318  : m_Portions()
319 {
320  m_pUnoCursor = rParaCursor.GetDoc().CreateUnoCursor(*rParaCursor.GetPoint());
321 
322  OSL_ENSURE(nEnd == -1 || (nStart <= nEnd &&
323  nEnd <= m_pUnoCursor->Start()->nNode.GetNode().GetTextNode()->GetText().getLength()),
324  "start or end value invalid!");
325 
326  // find all frames, graphics and OLEs that are bound AT character in para
327  FrameClientSortList_t frames;
328  ::CollectFrameAtNode(m_pUnoCursor->GetPoint()->nNode, frames, true);
329  lcl_CreatePortions(m_Portions, xParentText, &*m_pUnoCursor, frames, nStart, nEnd);
330 }
331 
333  SwPaM& rParaCursor,
334  TextRangeList_t const & rPortions )
335  : m_Portions( rPortions )
336 {
337  m_pUnoCursor = rParaCursor.GetDoc().CreateUnoCursor(*rParaCursor.GetPoint());
338 }
339 
341 {
342  SolarMutexGuard aGuard;
343  m_pUnoCursor.reset(nullptr);
344 }
345 
347 {
348  SolarMutexGuard aGuard;
349 
350  return !m_Portions.empty();
351 }
352 
354 {
355  SolarMutexGuard aGuard;
356 
357  if (m_Portions.empty())
358  throw container::NoSuchElementException();
359 
360  Any any;
361  any <<= m_Portions.front();
362  m_Portions.pop_front();
363  return any;
364 }
365 
366 static void
367 lcl_FillFieldMarkArray(std::deque<sal_Int32> & rFieldMarks, SwUnoCursor const & rUnoCursor,
368  const sal_Int32 i_nStartPos)
369 {
370  const SwTextNode * const pTextNode =
371  rUnoCursor.GetPoint()->nNode.GetNode().GetTextNode();
372  if (!pTextNode) return;
373 
374  const sal_Unicode fld[] = {
376  sal_Int32 pos = std::max(static_cast<sal_Int32>(0), i_nStartPos);
377  while ((pos = ::comphelper::string::indexOfAny(pTextNode->GetText(), fld, pos)) != -1)
378  {
379  rFieldMarks.push_back(pos);
380  ++pos;
381  }
382 }
383 
384 static uno::Reference<text::XTextRange>
386  uno::Reference< text::XText > const & i_xParentText,
387  SwUnoCursor * const pUnoCursor,
388  const SwTextNode * const pTextNode )
389 {
390  uno::Reference<text::XTextRange> xRef;
391  SwDoc& rDoc = pUnoCursor->GetDoc();
392  // maybe it's a good idea to add a special hint to the hints array and rely on the hint segmentation...
393  const sal_Int32 start = pUnoCursor->Start()->nContent.GetIndex();
394  OSL_ENSURE(pUnoCursor->End()->nContent.GetIndex() == start,
395  "hmm --- why is this different");
396 
397  pUnoCursor->Right(1);
398  if ( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
399  {
400  OSL_FAIL("cannot move cursor?");
401  return nullptr;
402  }
403 
404  const sal_Unicode Char = pTextNode->GetText()[start];
405  if (CH_TXT_ATR_FIELDSTART == Char)
406  {
407  ::sw::mark::IFieldmark* pFieldmark = nullptr;
408  pFieldmark = rDoc.getIDocumentMarkAccess()->
409  getFieldmarkAt(*pUnoCursor->GetMark());
410  SwXTextPortion* pPortion = new SwXTextPortion(
411  pUnoCursor, i_xParentText, PORTION_FIELD_START);
412  xRef = pPortion;
413  if (pFieldmark)
414  {
415  pPortion->SetBookmark(
416  SwXFieldmark::CreateXFieldmark(rDoc, pFieldmark));
417  }
418  }
419  else if (CH_TXT_ATR_FIELDSEP == Char)
420  {
421  // TODO how to get the field?
422  SwXTextPortion* pPortion = new SwXTextPortion(
423  pUnoCursor, i_xParentText, PORTION_FIELD_SEP);
424  xRef = pPortion;
425  }
426  else if (CH_TXT_ATR_FIELDEND == Char)
427  {
428  ::sw::mark::IFieldmark* pFieldmark = nullptr;
429  pFieldmark = rDoc.getIDocumentMarkAccess()->
430  getFieldmarkAt(*pUnoCursor->GetMark());
431  SwXTextPortion* pPortion = new SwXTextPortion(
432  pUnoCursor, i_xParentText, PORTION_FIELD_END);
433  xRef = pPortion;
434  if (pFieldmark)
435  {
436  pPortion->SetBookmark(
437  SwXFieldmark::CreateXFieldmark(rDoc, pFieldmark));
438  }
439  }
440  else if (CH_TXT_ATR_FORMELEMENT == Char)
441  {
442  ::sw::mark::IFieldmark* pFieldmark =
443  rDoc.getIDocumentMarkAccess()->getFieldmarkAt(*pUnoCursor->GetMark());
444  SwXTextPortion* pPortion = new SwXTextPortion(
445  pUnoCursor, i_xParentText, PORTION_FIELD_START_END);
446  xRef = pPortion;
447  if (pFieldmark)
448  {
449  pPortion->SetBookmark(
450  SwXFieldmark::CreateXFieldmark(rDoc, pFieldmark));
451  }
452  }
453  else
454  {
455  OSL_FAIL("no fieldmark found?");
456  }
457  return xRef;
458 }
459 
460 static Reference<XTextRange>
462  Reference<XText> const& xParent,
463  const SwUnoCursor * const pUnoCursor,
464  const SwTextAttr & rAttr, const bool bEnd)
465 {
466  SwDoc& rDoc = pUnoCursor->GetDoc();
467  SwFormatRefMark& rRefMark = const_cast<SwFormatRefMark&>(
468  static_cast<const SwFormatRefMark&>(rAttr.GetAttr()));
469  Reference<XTextContent> xContent;
470  if (!xContent.is())
471  {
472  xContent = SwXReferenceMark::CreateXReferenceMark(rDoc, &rRefMark);
473  }
474 
475  SwXTextPortion* pPortion = nullptr;
476  if (!bEnd)
477  {
478  pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_REFMARK_START);
479  pPortion->SetRefMark(xContent);
480  pPortion->SetCollapsed(rAttr.End() == nullptr);
481  }
482  else
483  {
484  pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_REFMARK_END);
485  pPortion->SetRefMark(xContent);
486  }
487  return pPortion;
488 }
489 
490 static void
492  TextRangeList_t & rPortions,
493  Reference<XText> const& xParent,
494  const SwUnoCursor * const pUnoCursor,
495  const SwTextAttr & rAttr, const bool bEnd)
496 {
497  SwXTextPortion* pPortion = new SwXTextPortion(pUnoCursor,
498  static_txtattr_cast<const SwTextRuby&>(rAttr), xParent, bEnd);
499  rPortions.emplace_back(pPortion);
500  pPortion->SetCollapsed(rAttr.End() == nullptr);
501 }
502 
503 static Reference<XTextRange>
505  Reference<XText> const& xParent,
506  const SwUnoCursor * const pUnoCursor,
507  SwTextAttr & rAttr, const bool bEnd)
508 {
509  SwDoc& rDoc = pUnoCursor->GetDoc();
510  SwTOXMark & rTOXMark = static_cast<SwTOXMark&>(rAttr.GetAttr());
511 
512  const Reference<XTextContent> xContent =
514 
515  SwXTextPortion* pPortion = nullptr;
516  if (!bEnd)
517  {
518  pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_TOXMARK_START);
519  pPortion->SetTOXMark(xContent);
520  pPortion->SetCollapsed(rAttr.GetEnd() == nullptr);
521  }
522  else
523  {
524  pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_TOXMARK_END);
525  pPortion->SetTOXMark(xContent);
526  }
527  return pPortion;
528 }
529 
530 static uno::Reference<text::XTextRange>
532  uno::Reference<text::XText> const& xParent,
533  const SwUnoCursor * const pUnoCursor,
534  SwTextAttr & rAttr, std::unique_ptr<TextRangeList_t const> && pPortions)
535 {
536  const uno::Reference<rdf::XMetadatable> xMeta( SwXMeta::CreateXMeta(
537  *static_cast<SwFormatMeta &>(rAttr.GetAttr()).GetMeta(),
538  xParent, std::move(pPortions)));
539  SwXTextPortion * pPortion(nullptr);
540  if (RES_TXTATR_META == rAttr.Which())
541  {
542  const uno::Reference<text::XTextContent> xContent(xMeta,
543  uno::UNO_QUERY);
544  pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_META);
545  pPortion->SetMeta(xContent);
546  }
547  else
548  {
549  const uno::Reference<text::XTextField> xField(xMeta, uno::UNO_QUERY);
550  pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_FIELD);
551  pPortion->SetTextField(xField);
552  }
553  return pPortion;
554 }
555 
570 static void lcl_ExportBookmark(
571  TextRangeList_t & rPortions,
572  Reference<XText> const& xParent,
573  const SwUnoCursor * const pUnoCursor,
574  SwXBookmarkPortion_ImplList& rBkmArr,
575  const sal_Int32 nIndex,
576  const o3tl::sorted_vector<sal_Int32>& rFramePositions,
577  bool bOnlyFrameStarts)
578 {
579  for ( SwXBookmarkPortion_ImplList::iterator aIter = rBkmArr.begin(), aEnd = rBkmArr.end(); aIter != aEnd; )
580  {
581  const SwXBookmarkPortion_ImplSharedPtr& pPtr = *aIter;
582  if ( nIndex > pPtr->getIndex() )
583  {
584  if (bOnlyFrameStarts)
585  ++aIter;
586  else
587  aIter = rBkmArr.erase(aIter);
588  continue;
589  }
590  if ( nIndex < pPtr->getIndex() )
591  break;
592 
593  if ((BkmType::Start == pPtr->nBkmType && bOnlyFrameStarts) ||
594  (BkmType::StartEnd == pPtr->nBkmType))
595  {
596  bool bFrameStart = rFramePositions.find(nIndex) != rFramePositions.end();
597  bool bEnd = pPtr->nBkmType == BkmType::StartEnd && bFrameStart && !bOnlyFrameStarts;
598  if (pPtr->nBkmType == BkmType::Start || bFrameStart || !bOnlyFrameStarts)
599  {
600  // At this we create a text portion, due to one of these
601  // reasons:
602  // - this is the real start of a non-collapsed bookmark
603  // - this is the real position of a collapsed bookmark
604  // - this is the start or end (depending on bOnlyFrameStarts)
605  // of a collapsed bookmark at the same position as an at-char
606  // anchored frame
607  SwXTextPortion* pPortion =
608  new SwXTextPortion(pUnoCursor, xParent, bEnd ? PORTION_BOOKMARK_END : PORTION_BOOKMARK_START);
609  rPortions.emplace_back(pPortion);
610  pPortion->SetBookmark(pPtr->xBookmark);
611  pPortion->SetCollapsed( BkmType::StartEnd == pPtr->nBkmType && !bFrameStart );
612  }
613  }
614  else if (BkmType::End == pPtr->nBkmType && !bOnlyFrameStarts)
615  {
616  SwXTextPortion* pPortion =
617  new SwXTextPortion(pUnoCursor, xParent, PORTION_BOOKMARK_END);
618  rPortions.emplace_back(pPortion);
619  pPortion->SetBookmark(pPtr->xBookmark);
620  }
621 
622  // next bookmark
623  if (bOnlyFrameStarts)
624  ++aIter;
625  else
626  aIter = rBkmArr.erase(aIter);
627  }
628 }
629 
631  TextRangeList_t & rPortions,
632  Reference<XText> const& xParent,
633  const SwUnoCursor * const pUnoCursor,
634  SwSoftPageBreakList& rBreakArr,
635  const sal_Int32 nIndex)
636 {
637  for ( auto aIter = rBreakArr.begin(); aIter != rBreakArr.end(); )
638  {
639  if ( nIndex > *aIter )
640  {
641  aIter = rBreakArr.erase(aIter);
642  continue;
643  }
644  if ( nIndex < *aIter )
645  break;
646 
647  rPortions.push_back(
648  new SwXTextPortion(pUnoCursor, xParent, PORTION_SOFT_PAGEBREAK) );
649  aIter = rBreakArr.erase(aIter);
650  }
651 }
652 
653 namespace {
654 
655 struct SwXRedlinePortion_Impl
656 {
657  const SwRangeRedline* m_pRedline;
658  const bool m_bStart;
659 
660  SwXRedlinePortion_Impl ( const SwRangeRedline* pRed, const bool bIsStart )
661  : m_pRedline(pRed)
662  , m_bStart(bIsStart)
663  {
664  }
665 
666  sal_Int32 getRealIndex () const
667  {
668  return m_bStart ? m_pRedline->Start()->nContent.GetIndex()
669  : m_pRedline->End() ->nContent.GetIndex();
670  }
671 };
672 
673 }
674 
675 typedef std::shared_ptr < SwXRedlinePortion_Impl >
677 
678 namespace {
679 
680 struct RedlineCompareStruct
681 {
682  static const SwPosition& getPosition ( const SwXRedlinePortion_ImplSharedPtr &r )
683  {
684  return *(r->m_bStart ? r->m_pRedline->Start() : r->m_pRedline->End());
685  }
686 
687  bool operator () ( const SwXRedlinePortion_ImplSharedPtr &r1,
688  const SwXRedlinePortion_ImplSharedPtr &r2 ) const
689  {
690  return getPosition ( r1 ) < getPosition ( r2 );
691  }
692 };
693 
694 }
695 
696 typedef std::multiset < SwXRedlinePortion_ImplSharedPtr, RedlineCompareStruct >
698 
699 static Reference<XTextRange>
701  PortionStack_t & rPortionStack,
702  const Reference<XText> & xParent,
703  SwUnoCursor * const pUnoCursor,
704  SwpHints const * const pHints,
705  const sal_Int32 i_nStartPos,
706  const sal_Int32 i_nEndPos,
707  const sal_Int32 nCurrentIndex,
708  const bool bRightMoveForbidden,
709  bool & o_rbCursorMoved,
710  sal_Int32 & o_rNextAttrPosition)
711 {
712  // if the attribute has a dummy character, then xRef is set (except META)
713  // otherwise, the portion for the attribute is inserted into rPortions!
714  Reference<XTextRange> xRef;
715  SwDoc& rDoc = pUnoCursor->GetDoc();
716  //search for special text attributes - first some ends
717  size_t nEndIndex = 0;
718  sal_Int32 nNextEnd = 0;
719  while(nEndIndex < pHints->Count() &&
720  (!pHints->GetSortedByEnd(nEndIndex)->GetEnd() ||
721  nCurrentIndex >= (nNextEnd = (*pHints->GetSortedByEnd(nEndIndex)->GetEnd()))))
722  {
723  if(pHints->GetSortedByEnd(nEndIndex)->GetEnd())
724  {
725  SwTextAttr * const pAttr = pHints->GetSortedByEnd(nEndIndex);
726  if (nNextEnd == nCurrentIndex)
727  {
728  const sal_uInt16 nWhich( pAttr->Which() );
729  switch (nWhich)
730  {
731  case RES_TXTATR_TOXMARK:
732  {
733  Reference<XTextRange> xTmp = lcl_CreateTOXMarkPortion(
734  xParent, pUnoCursor, *pAttr, true);
735  rPortionStack.top().first->push_back(xTmp);
736  }
737  break;
738  case RES_TXTATR_REFMARK:
739  {
740  Reference<XTextRange> xTmp = lcl_CreateRefMarkPortion(
741  xParent, pUnoCursor, *pAttr, true);
742  rPortionStack.top().first->push_back(xTmp);
743  }
744  break;
745  case RES_TXTATR_CJK_RUBY:
746  //#i91534# GetEnd() == 0 mixes the order of ruby start/end
747  if( *pAttr->GetEnd() == pAttr->GetStart())
748  {
749  lcl_InsertRubyPortion( *rPortionStack.top().first,
750  xParent, pUnoCursor, *pAttr, false);
751  }
752  lcl_InsertRubyPortion( *rPortionStack.top().first,
753  xParent, pUnoCursor, *pAttr, true);
754  break;
755  case RES_TXTATR_META:
757  {
758  OSL_ENSURE(pAttr->GetStart() != *pAttr->GetEnd(),
759  "empty meta?");
760  if ((i_nStartPos > 0) &&
761  (pAttr->GetStart() < i_nStartPos))
762  {
763  // force skip pAttr and rest of attribute ends
764  // at nCurrentIndex
765  // because they are not contained in the meta pAttr
766  // and the meta pAttr itself is outside selection!
767  // (necessary for SwXMeta::createEnumeration)
768  if (pAttr->GetStart() + 1 == i_nStartPos)
769  {
770  nEndIndex = pHints->Count() - 1;
771  }
772  break;
773  }
774  PortionList_t Top = rPortionStack.top();
775  if (Top.second != pAttr)
776  {
777  OSL_FAIL("ExportHints: stack error" );
778  }
779  else
780  {
781  std::unique_ptr<const TextRangeList_t>
782  pCurrentPortions(Top.first);
783  rPortionStack.pop();
784  const uno::Reference<text::XTextRange> xPortion(
785  lcl_CreateMetaPortion(xParent, pUnoCursor,
786  *pAttr, std::move(pCurrentPortions)));
787  rPortionStack.top().first->push_back(xPortion);
788  }
789  }
790  break;
791  }
792  }
793  }
794  nEndIndex++;
795  }
796 
797  // then some starts
798  size_t nStartIndex = 0;
799  sal_Int32 nNextStart = 0;
800  while(nStartIndex < pHints->Count() &&
801  nCurrentIndex >= (nNextStart = pHints->Get(nStartIndex)->GetStart()))
802  {
803  SwTextAttr * const pAttr = pHints->Get(nStartIndex);
804  sal_uInt16 nAttrWhich = pAttr->Which();
805  if (nNextStart == nCurrentIndex)
806  {
807  switch( nAttrWhich )
808  {
809  case RES_TXTATR_FIELD:
810  if(!bRightMoveForbidden)
811  {
812  pUnoCursor->Right(1);
813  if( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
814  break;
815  SwXTextPortion* pPortion;
816  xRef = pPortion =
817  new SwXTextPortion(
818  pUnoCursor, xParent, PORTION_FIELD);
819  Reference<XTextField> const xField =
821  &pAttr->GetFormatField());
822  pPortion->SetTextField(xField);
823  }
824  break;
825 
827  if(!bRightMoveForbidden)
828  {
829  pUnoCursor->Right(1);
830  if( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
831  break;
832 
833  const SwTextAnnotationField* pTextAnnotationField = dynamic_cast<const SwTextAnnotationField*>( pAttr );
834  ::sw::mark::IMark* pAnnotationMark = pTextAnnotationField ? pTextAnnotationField->GetAnnotationMark() : nullptr;
835  if ( pAnnotationMark != nullptr )
836  {
837  SwXTextPortion* pPortion = new SwXTextPortion( pUnoCursor, xParent, PORTION_ANNOTATION_END );
839  rDoc, pAnnotationMark));
840  xRef = pPortion;
841  }
842  else
843  {
844  SwXTextPortion* pPortion = new SwXTextPortion( pUnoCursor, xParent, PORTION_ANNOTATION );
845  Reference<XTextField> xField =
847  &pAttr->GetFormatField());
848  pPortion->SetTextField(xField);
849  xRef = pPortion;
850  }
851  }
852  break;
853 
855  if(!bRightMoveForbidden)
856  {
857 
858  pUnoCursor->Right(
859  pAttr->GetFormatField().GetField()->ExpandField(true, nullptr).getLength() + 2 );
860  if( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
861  break;
862  SwXTextPortion* pPortion =
863  new SwXTextPortion( pUnoCursor, xParent, PORTION_FIELD);
864  xRef = pPortion;
865  Reference<XTextField> xField =
867  &pAttr->GetFormatField());
868  pPortion->SetTextField(xField);
869  }
870  break;
871 
872  case RES_TXTATR_FLYCNT:
873  if(!bRightMoveForbidden)
874  {
875  pUnoCursor->Right(1);
876  if( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
877  break; // Robust #i81708# content in covered cells
878 
879  // Do not expose inline anchored textboxes.
881  break;
882 
883  pUnoCursor->Exchange();
884  xRef = new SwXTextPortion( pUnoCursor, xParent, PORTION_FRAME);
885  }
886  break;
887 
888  case RES_TXTATR_FTN:
889  {
890  if(!bRightMoveForbidden)
891  {
892  pUnoCursor->Right(1);
893  if( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
894  break;
895  SwXTextPortion* pPortion;
896  xRef = pPortion = new SwXTextPortion(
897  pUnoCursor, xParent, PORTION_FOOTNOTE);
898  Reference<XFootnote> xContent =
899  SwXFootnotes::GetObject(rDoc, pAttr->GetFootnote());
900  pPortion->SetFootnote(xContent);
901  }
902  }
903  break;
904 
905  case RES_TXTATR_TOXMARK:
906  case RES_TXTATR_REFMARK:
907  {
908  bool bIsPoint = !(pAttr->GetEnd());
909  if (!bRightMoveForbidden || !bIsPoint)
910  {
911  if (bIsPoint)
912  {
913  pUnoCursor->Right(1);
914  }
915  Reference<XTextRange> xTmp =
916  (RES_TXTATR_REFMARK == nAttrWhich)
918  xParent, pUnoCursor, *pAttr, false)
920  xParent, pUnoCursor, *pAttr, false);
921  if (bIsPoint) // consume CH_TXTATR!
922  {
923  pUnoCursor->Normalize(false);
924  pUnoCursor->DeleteMark();
925  xRef = xTmp;
926  }
927  else // just insert it
928  {
929  rPortionStack.top().first->push_back(xTmp);
930  }
931  }
932  }
933  break;
934  case RES_TXTATR_CJK_RUBY:
935  //#i91534# GetEnd() == 0 mixes the order of ruby start/end
936  if(pAttr->GetEnd() && (*pAttr->GetEnd() != pAttr->GetStart()))
937  {
938  lcl_InsertRubyPortion( *rPortionStack.top().first,
939  xParent, pUnoCursor, *pAttr, false);
940  }
941  break;
942  case RES_TXTATR_META:
944  if (pAttr->GetStart() != *pAttr->GetEnd())
945  {
946  if (!bRightMoveForbidden)
947  {
948  pUnoCursor->Right(1);
949  o_rbCursorMoved = true;
950  // only if the end is included in selection!
951  if ((i_nEndPos < 0) ||
952  (*pAttr->GetEnd() <= i_nEndPos))
953  {
954  rPortionStack.push( std::make_pair(
955  new TextRangeList_t, pAttr ));
956  }
957  }
958  }
959  break;
960  case RES_TXTATR_AUTOFMT:
961  case RES_TXTATR_INETFMT:
962  case RES_TXTATR_CHARFMT:
963  break; // these are handled as properties of a "Text" portion
964  default:
965  OSL_FAIL("unknown attribute");
966  break;
967  }
968  }
969  nStartIndex++;
970  }
971 
972  if (xRef.is()) // implies that we have moved the cursor
973  {
974  o_rbCursorMoved = true;
975  }
976  if (!o_rbCursorMoved)
977  {
978  // search for attribute changes behind the current cursor position
979  // break up at frames, bookmarks, redlines
980 
981  nStartIndex = 0;
982  nNextStart = 0;
983  while(nStartIndex < pHints->Count() &&
984  nCurrentIndex >= (nNextStart = pHints->Get(nStartIndex)->GetStart()))
985  nStartIndex++;
986 
987  nEndIndex = 0;
988  nNextEnd = 0;
989  while(nEndIndex < pHints->Count() &&
990  nCurrentIndex >= (nNextEnd = pHints->GetSortedByEnd(nEndIndex)->GetAnyEnd()))
991  nEndIndex++;
992 
993  sal_Int32 nNextPos =
994  ((nNextStart > nCurrentIndex) && (nNextStart < nNextEnd))
995  ? nNextStart : nNextEnd;
996  if (nNextPos > nCurrentIndex)
997  {
998  o_rNextAttrPosition = nNextPos;
999  }
1000  }
1001  return xRef;
1002 }
1003 
1004 static void lcl_MoveCursor( SwUnoCursor * const pUnoCursor,
1005  const sal_Int32 nCurrentIndex,
1006  const sal_Int32 nNextFrameIndex,
1007  const sal_Int32 nNextPortionIndex,
1008  const sal_Int32 nNextAttrIndex,
1009  const sal_Int32 nNextMarkIndex,
1010  const sal_Int32 nEndPos )
1011 {
1012  sal_Int32 nMovePos = pUnoCursor->GetContentNode()->Len();
1013 
1014  if ((nEndPos >= 0) && (nEndPos < nMovePos))
1015  {
1016  nMovePos = nEndPos;
1017  }
1018 
1019  if ((nNextFrameIndex >= 0) && (nNextFrameIndex < nMovePos))
1020  {
1021  nMovePos = nNextFrameIndex;
1022  }
1023 
1024  if ((nNextPortionIndex >= 0) && (nNextPortionIndex < nMovePos))
1025  {
1026  nMovePos = nNextPortionIndex;
1027  }
1028 
1029  if ((nNextAttrIndex >= 0) && (nNextAttrIndex < nMovePos))
1030  {
1031  nMovePos = nNextAttrIndex;
1032  }
1033 
1034  if ((nNextMarkIndex >= 0) && (nNextMarkIndex < nMovePos))
1035  {
1036  nMovePos = nNextMarkIndex;
1037  }
1038 
1039  if (nMovePos > nCurrentIndex)
1040  {
1041  pUnoCursor->GetPoint()->nContent = nMovePos;
1042  }
1043 }
1044 
1046  SwDoc const & rDoc,
1047  SwUnoCursor const & rUnoCursor,
1048  SwXRedlinePortion_ImplList& rRedArr )
1049 {
1050  const SwRedlineTable& rRedTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
1051  const size_t nRedTableCount = rRedTable.size();
1052 
1053  if ( nRedTableCount <= 0 )
1054  return;
1055 
1056  const SwPosition* pStart = rUnoCursor.GetPoint();
1057  const SwNodeIndex nOwnNode = pStart->nNode;
1058 
1059  for(size_t nRed = 0; nRed < nRedTableCount; ++nRed)
1060  {
1061  const SwRangeRedline* pRedline = rRedTable[nRed];
1062  const SwPosition* pRedStart = pRedline->Start();
1063  const SwNodeIndex nRedNode = pRedStart->nNode;
1064  if ( nOwnNode == nRedNode )
1065  rRedArr.insert( std::make_shared<SwXRedlinePortion_Impl>(
1066  pRedline, true ) );
1067  if( pRedline->HasMark() && pRedline->End()->nNode == nOwnNode )
1068  rRedArr.insert( std::make_shared<SwXRedlinePortion_Impl>(
1069  pRedline, false ) );
1070  }
1071 }
1072 
1074  SwUnoCursor const & rUnoCursor,
1075  SwSoftPageBreakList& rBreakArr )
1076 {
1077  const SwTextNode *pTextNode =
1078  rUnoCursor.GetPoint()->nNode.GetNode().GetTextNode();
1079  if( pTextNode )
1080  pTextNode->fillSoftPageBreakList( rBreakArr );
1081 }
1082 
1083 static void lcl_ExportRedline(
1084  TextRangeList_t & rPortions,
1085  Reference<XText> const& xParent,
1086  const SwUnoCursor * const pUnoCursor,
1087  SwXRedlinePortion_ImplList& rRedlineArr,
1088  const sal_Int32 nIndex)
1089 {
1090 
1091  // We want this loop to iterate over all red lines in this
1092  // array. We will only insert the ones with index matches
1093  for ( SwXRedlinePortion_ImplList::iterator aIter = rRedlineArr.begin(), aEnd = rRedlineArr.end();
1094  aIter != aEnd; )
1095  {
1096  SwXRedlinePortion_ImplSharedPtr pPtr = *aIter;
1097  sal_Int32 nRealIndex = pPtr->getRealIndex();
1098  // If there are elements before nIndex, remove them
1099  if ( nIndex > nRealIndex )
1100  aIter = rRedlineArr.erase(aIter);
1101  // If the elements match, and them to the list
1102  else if ( nIndex == nRealIndex )
1103  {
1104  rPortions.push_back( new SwXRedlinePortion(
1105  *pPtr->m_pRedline, pUnoCursor, xParent, pPtr->m_bStart));
1106  aIter = rRedlineArr.erase(aIter);
1107  }
1108  // If we've iterated past nIndex, exit the loop
1109  else
1110  break;
1111  }
1112 }
1113 
1115  TextRangeList_t & rPortions,
1116  Reference<XText> const & xParent,
1117  const SwUnoCursor * const pUnoCursor,
1118  SwXBookmarkPortion_ImplList& rBkmArr,
1119  SwXRedlinePortion_ImplList& rRedlineArr,
1120  SwSoftPageBreakList& rBreakArr,
1121  const sal_Int32 nIndex,
1122  const o3tl::sorted_vector<sal_Int32>& rFramePositions,
1123  bool bOnlyFrameBookmarkStarts)
1124 {
1125  if (!rBkmArr.empty())
1126  lcl_ExportBookmark(rPortions, xParent, pUnoCursor, rBkmArr, nIndex, rFramePositions,
1127  bOnlyFrameBookmarkStarts);
1128 
1129  if (bOnlyFrameBookmarkStarts)
1130  // Only exporting the start of some collapsed bookmarks: no export of
1131  // other arrays.
1132  return;
1133 
1134  if (!rRedlineArr.empty())
1135  lcl_ExportRedline(rPortions, xParent, pUnoCursor, rRedlineArr, nIndex);
1136 
1137  if (!rBreakArr.empty())
1138  lcl_ExportSoftPageBreak(rPortions, xParent, pUnoCursor, rBreakArr, nIndex);
1139 }
1140 
1153  TextRangeList_t & rPortions,
1154  Reference<XText> const & xParent,
1155  const SwUnoCursor * const pUnoCursor,
1156  SwAnnotationStartPortion_ImplList& rAnnotationStartArr,
1157  const sal_Int32 nIndex,
1158  const o3tl::sorted_vector<sal_Int32>& rFramePositions,
1159  bool bOnlyFrame)
1160 {
1161  for ( SwAnnotationStartPortion_ImplList::iterator aIter = rAnnotationStartArr.begin(), aEnd = rAnnotationStartArr.end();
1162  aIter != aEnd; )
1163  {
1164  SwAnnotationStartPortion_ImplSharedPtr pPtr = *aIter;
1165  if ( nIndex > pPtr->getIndex() )
1166  {
1167  aIter = rAnnotationStartArr.erase(aIter);
1168  continue;
1169  }
1170  if ( pPtr->getIndex() > nIndex )
1171  {
1172  break;
1173  }
1174 
1175  bool bFrameStart = rFramePositions.find(nIndex) != rFramePositions.end();
1176  if (bFrameStart || !bOnlyFrame)
1177  {
1178  SwXTextPortion* pPortion =
1179  new SwXTextPortion( pUnoCursor, xParent, PORTION_ANNOTATION );
1180  pPortion->SetTextField( pPtr->mxAnnotationField );
1181  rPortions.emplace_back(pPortion);
1182 
1183  aIter = rAnnotationStartArr.erase(aIter);
1184  }
1185  else
1186  ++aIter;
1187  }
1188 }
1189 
1191 static void lcl_ExtractFramePositions(FrameClientSortList_t& rFrames, sal_Int32 nCurrentIndex,
1192  o3tl::sorted_vector<sal_Int32>& rFramePositions)
1193 {
1194  for (const auto& rFrame : rFrames)
1195  {
1196  if (rFrame.nIndex < nCurrentIndex)
1197  continue;
1198 
1199  if (rFrame.nIndex > nCurrentIndex)
1200  break;
1201 
1202  const auto pFrame = static_cast<const SwFrameFormat*>(rFrame.pFrameClient->GetRegisteredIn());
1203  if (!pFrame)
1204  continue;
1205 
1206  auto& rFormat = *const_cast<SwFrameFormat*>(pFrame);
1207  const SwFormatAnchor& rAnchor = rFormat.GetAnchor();
1208  const SwPosition* pPosition = rAnchor.GetContentAnchor();
1209  if (!pPosition)
1210  continue;
1211 
1212  rFramePositions.insert(pPosition->nContent.GetIndex());
1213  }
1214 }
1215 
1222 static sal_Int32 lcl_ExportFrames(
1223  TextRangeList_t & rPortions,
1224  Reference<XText> const & i_xParent,
1225  SwUnoCursor const * const i_pUnoCursor,
1226  FrameClientSortList_t & i_rFrames,
1227  sal_Int32 const i_nCurrentIndex)
1228 {
1229  // Ignore frames which are not exported, as we are exporting a selection
1230  // and they are anchored before the start of the selection.
1231  while (!i_rFrames.empty() && i_rFrames.front().nIndex < i_nCurrentIndex)
1232  i_rFrames.pop_front();
1233 
1234  // find first Frame in (sorted) i_rFrames at current position
1235  while (!i_rFrames.empty() && (i_rFrames.front().nIndex == i_nCurrentIndex))
1236  // do not check for i_nEnd here; this is done implicitly by lcl_MoveCursor
1237  {
1238  auto pFrame = static_cast<SwFrameFormat*>(i_rFrames.front().pFrameClient->GetRegisteredIn());
1239  if (pFrame) // Frame could be disposed
1240  {
1241  SwXTextPortion* pPortion = new SwXTextPortion(i_pUnoCursor, i_xParent, *pFrame );
1242  rPortions.emplace_back(pPortion);
1243  }
1244  i_rFrames.pop_front();
1245  }
1246 
1247  return !i_rFrames.empty() ? i_rFrames.front().nIndex : -1;
1248 }
1249 
1250 static sal_Int32 lcl_GetNextIndex(
1251  SwXBookmarkPortion_ImplList const & rBkmArr,
1252  SwXRedlinePortion_ImplList const & rRedlineArr,
1253  SwSoftPageBreakList const & rBreakArr )
1254 {
1255  sal_Int32 nRet = -1;
1256  if(!rBkmArr.empty())
1257  {
1258  SwXBookmarkPortion_ImplSharedPtr pPtr = *rBkmArr.begin();
1259  nRet = pPtr->getIndex();
1260  }
1261  if(!rRedlineArr.empty())
1262  {
1263  SwXRedlinePortion_ImplSharedPtr pPtr = *rRedlineArr.begin();
1264  sal_Int32 nTmp = pPtr->getRealIndex();
1265  if(nRet < 0 || nTmp < nRet)
1266  nRet = nTmp;
1267  }
1268  if(!rBreakArr.empty())
1269  {
1270  if(nRet < 0 || *rBreakArr.begin() < nRet)
1271  nRet = *rBreakArr.begin();
1272  }
1273  return nRet;
1274 };
1275 
1277  TextRangeList_t & i_rPortions,
1278  uno::Reference< text::XText > const & i_xParentText,
1279  SwUnoCursor * const pUnoCursor,
1280  FrameClientSortList_t & i_rFrames,
1281  const sal_Int32 i_nStartPos,
1282  const sal_Int32 i_nEndPos )
1283 {
1284  if (!pUnoCursor)
1285  return;
1286 
1287  // set the start if a selection should be exported
1288  if ((i_nStartPos > 0) &&
1289  (pUnoCursor->Start()->nContent.GetIndex() != i_nStartPos))
1290  {
1291  pUnoCursor->DeleteMark();
1292  OSL_ENSURE(pUnoCursor->Start()->nNode.GetNode().GetTextNode() &&
1293  (i_nStartPos <= pUnoCursor->Start()->nNode.GetNode().GetTextNode()->
1294  GetText().getLength()), "Incorrect start position" );
1295  // ??? should this be i_nStartPos - current position ?
1296  pUnoCursor->Right(i_nStartPos);
1297  }
1298 
1299  SwDoc& rDoc = pUnoCursor->GetDoc();
1300 
1301  std::deque<sal_Int32> FieldMarks;
1302  lcl_FillFieldMarkArray(FieldMarks, *pUnoCursor, i_nStartPos);
1303 
1304  SwXBookmarkPortion_ImplList Bookmarks;
1305  lcl_FillBookmarkArray(rDoc, *pUnoCursor, Bookmarks);
1306 
1307  SwXRedlinePortion_ImplList Redlines;
1308  lcl_FillRedlineArray(rDoc, *pUnoCursor, Redlines);
1309 
1310  SwSoftPageBreakList SoftPageBreaks;
1311  lcl_FillSoftPageBreakArray(*pUnoCursor, SoftPageBreaks);
1312 
1313  SwAnnotationStartPortion_ImplList AnnotationStarts;
1314  lcl_FillAnnotationStartArray( rDoc, *pUnoCursor, AnnotationStarts );
1315 
1316  PortionStack_t PortionStack;
1317  PortionStack.push( PortionList_t(&i_rPortions, nullptr) );
1318 
1319  bool bAtEnd( false );
1320  while (!bAtEnd) // every iteration consumes at least current character!
1321  {
1322  if (pUnoCursor->HasMark())
1323  {
1324  pUnoCursor->Normalize(false);
1325  pUnoCursor->DeleteMark();
1326  }
1327 
1328  SwTextNode * const pTextNode = pUnoCursor->GetNode().GetTextNode();
1329  if (!pTextNode)
1330  {
1331  OSL_FAIL("lcl_CreatePortions: no TextNode - what now ?");
1332  return;
1333  }
1334 
1335  SwpHints * const pHints = pTextNode->GetpSwpHints();
1336  const sal_Int32 nCurrentIndex =
1337  pUnoCursor->GetPoint()->nContent.GetIndex();
1338  // this contains the portion which consumes the character in the
1339  // text at nCurrentIndex; i.e. it must be set _once_ per iteration
1340  uno::Reference< XTextRange > xRef;
1341 
1342  SwUnoCursorHelper::SelectPam(*pUnoCursor, true); // set mark
1343 
1344  // First remember the frame positions.
1345  o3tl::sorted_vector<sal_Int32> aFramePositions;
1346  lcl_ExtractFramePositions(i_rFrames, nCurrentIndex, aFramePositions);
1347 
1348  // Then export start of collapsed bookmarks which "cover" at-char
1349  // anchored frames.
1350  lcl_ExportBkmAndRedline( *PortionStack.top().first, i_xParentText,
1351  pUnoCursor, Bookmarks, Redlines, SoftPageBreaks, nCurrentIndex, aFramePositions, /*bOnlyFrameBookmarkStarts=*/true );
1352 
1354  *PortionStack.top().first,
1355  i_xParentText,
1356  pUnoCursor,
1357  AnnotationStarts,
1358  nCurrentIndex,
1359  aFramePositions,
1360  /*bOnlyFrame=*/true );
1361 
1362  const sal_Int32 nFirstFrameIndex =
1363  lcl_ExportFrames( *PortionStack.top().first,
1364  i_xParentText, pUnoCursor, i_rFrames, nCurrentIndex);
1365 
1366  // Export ends of the previously started collapsed bookmarks + all
1367  // other bookmarks, redlines, etc.
1368  lcl_ExportBkmAndRedline( *PortionStack.top().first, i_xParentText,
1369  pUnoCursor, Bookmarks, Redlines, SoftPageBreaks, nCurrentIndex, aFramePositions, /*bOnlyFrameBookmarkStarts=*/false );
1370 
1372  *PortionStack.top().first,
1373  i_xParentText,
1374  pUnoCursor,
1375  AnnotationStarts,
1376  nCurrentIndex,
1377  aFramePositions,
1378  /*bOnlyFrame=*/false );
1379 
1380  bool bCursorMoved( false );
1381  sal_Int32 nNextAttrIndex = -1;
1382  // #111716# the cursor must not move right at the
1383  // end position of a selection!
1384  bAtEnd = ((i_nEndPos >= 0) && (nCurrentIndex >= i_nEndPos))
1385  || (nCurrentIndex >= pTextNode->Len());
1386  if (pHints)
1387  {
1388  // N.B.: side-effects nNextAttrIndex, bCursorMoved; may move cursor
1389  xRef = lcl_ExportHints(PortionStack, i_xParentText, pUnoCursor,
1390  pHints, i_nStartPos, i_nEndPos, nCurrentIndex, bAtEnd,
1391  bCursorMoved, nNextAttrIndex);
1392  if (PortionStack.empty())
1393  {
1394  OSL_FAIL("CreatePortions: stack underflow");
1395  return;
1396  }
1397  }
1398 
1399  if (!xRef.is() && !bCursorMoved)
1400  {
1401  if (!bAtEnd &&
1402  !FieldMarks.empty() && (FieldMarks.front() == nCurrentIndex))
1403  {
1404  // moves cursor
1405  xRef = lcl_ExportFieldMark(i_xParentText, pUnoCursor, pTextNode);
1406  FieldMarks.pop_front();
1407  }
1408  }
1409  else
1410  {
1411  OSL_ENSURE(FieldMarks.empty() ||
1412  (FieldMarks.front() != nCurrentIndex),
1413  "fieldmark and hint with CH_TXTATR at same pos?");
1414  }
1415 
1416  if (!bAtEnd && !xRef.is() && !bCursorMoved)
1417  {
1418  const sal_Int32 nNextPortionIndex =
1419  lcl_GetNextIndex(Bookmarks, Redlines, SoftPageBreaks);
1420 
1421  sal_Int32 nNextMarkIndex = ( !FieldMarks.empty() ? FieldMarks.front() : -1 );
1422  if ( !AnnotationStarts.empty()
1423  && ( nNextMarkIndex == -1
1424  || (*AnnotationStarts.begin())->getIndex() < nNextMarkIndex ) )
1425  {
1426  nNextMarkIndex = (*AnnotationStarts.begin())->getIndex();
1427  }
1428 
1430  pUnoCursor,
1431  nCurrentIndex,
1432  nFirstFrameIndex,
1433  nNextPortionIndex,
1434  nNextAttrIndex,
1435  nNextMarkIndex,
1436  i_nEndPos );
1437 
1438  xRef = new SwXTextPortion(pUnoCursor, i_xParentText, PORTION_TEXT);
1439  }
1440  else if (bAtEnd && !xRef.is() && !pTextNode->Len())
1441  {
1442  // special case: for an empty paragraph, we better put out a
1443  // text portion because there may be a hyperlink attribute
1444  xRef = new SwXTextPortion(pUnoCursor, i_xParentText, PORTION_TEXT);
1445  }
1446 
1447  if (xRef.is())
1448  {
1449  PortionStack.top().first->push_back(xRef);
1450  }
1451  }
1452 
1453  OSL_ENSURE((PortionStack.size() == 1) && !PortionStack.top().second,
1454  "CreatePortions: stack error" );
1455 }
1456 
1457 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
css::uno::Reference< css::linguistic2::XProofreadingIterator > get(css::uno::Reference< css::uno::XComponentContext > const &context)
std::deque< css::uno::Reference< css::text::XTextRange > > TextRangeList_t
Definition: unometa.hxx:42
static css::uno::Reference< css::rdf::XMetadatable > CreateXMeta(::sw::Meta &rMeta, css::uno::Reference< css::text::XText > const &xParentText=nullptr, std::unique_ptr< TextRangeList_t const > &&pPortions=std::unique_ptr< TextRangeList_t const >())
std::shared_ptr< SwUnoCursor > CreateUnoCursor(const SwPosition &rPos, bool bTableCursor=false)
Definition: doc.cxx:1779
virtual sal_Int32 Len() const
Definition: node.cxx:1241
void DeleteMark()
Definition: pam.hxx:177
SwNode & GetNode(bool bPoint=true) const
Definition: pam.hxx:223
virtual ::sw::mark::IFieldmark * getFieldmarkAt(const SwPosition &rPos) const =0
get Fieldmark for CH_TXT_ATR_FIELDSTART/CH_TXT_ATR_FIELDEND at rPos
Marks a position in the document model.
Definition: pam.hxx:35
static Reference< XTextRange > lcl_CreateTOXMarkPortion(Reference< XText > const &xParent, const SwUnoCursor *const pUnoCursor, SwTextAttr &rAttr, const bool bEnd)
virtual css::uno::Any SAL_CALL nextElement() override
const SwField * GetField() const
Definition: fmtfld.hxx:111
constexpr TypedWhichId< SwFormatMeta > RES_TXTATR_METAFIELD(49)
const OUString & GetText() const
Definition: ndtxt.hxx:212
SwpHints * GetpSwpHints()
Definition: ndtxt.hxx:220
constexpr TypedWhichId< SwFormatMeta > RES_TXTATR_META(48)
SwNodeIndex nNode
Definition: pam.hxx:37
virtual sal_Int32 getBookmarksCount() const =0
returns the number of IBookmarks.
wrapper iterator: wraps iterator of implementation while hiding MarkBase class; only IMark instances ...
std::deque< FrameClientSortListEntry > FrameClientSortList_t
virtual const sal_Int32 * GetEnd() const
end position
Definition: txatbase.cxx:48
virtual sal_Int32 Len() const override
Definition: ndtxt.cxx:275
static SW_DLLPUBLIC MarkType GetType(const ::sw::mark::IMark &rMark)
Returns the MarkType used to create the mark.
Definition: docbm.cxx:475
virtual sal_Bool SAL_CALL hasMoreElements() override
const SwPosition * GetMark() const
Definition: pam.hxx:209
Provides access to the marks of a document.
Definition: doc.hxx:186
virtual SwPosition & GetMarkStart() const override
Definition: bookmrk.hxx:56
constexpr TypedWhichId< SwFormatFlyCnt > RES_TXTATR_FLYCNT(57)
virtual ~SwXTextPortionEnumeration() override
std::shared_ptr< SwXRedlinePortion_Impl > SwXRedlinePortion_ImplSharedPtr
const_iterator find(const Value &x) const
SwNode & GetNode() const
Definition: ndindex.hxx:119
geometry::RealPoint2D maPosition
SwTextAttr * GetSortedByEnd(size_t nPos) const
Definition: ndhints.hxx:158
IDocumentMarkAccess * getIDocumentMarkAccess()
Definition: docbm.cxx:1704
sal_uInt16 Which() const
Definition: txatbase.hxx:110
static css::uno::Reference< css::text::XTextField > CreateXTextField(SwDoc *pDoc, SwFormatField const *pFormat, SwServiceType nServiceId=SwServiceType::Invalid)
Definition: unofield.cxx:1228
virtual sal_Int32 getAnnotationMarksCount() const =0
sal_Int32 GetAnyEnd() const
end (if available), else start
Definition: txatbase.hxx:153
constexpr TypedWhichId< SwFormatField > RES_TXTATR_ANNOTATION(59)
#define CH_TXT_ATR_FORMELEMENT
Definition: hintids.hxx:176
const SwIndex * GetNext() const
Definition: index.hxx:98
size_type size() const
Definition: docary.hxx:269
SwContentNode * GetContentNode(bool bPoint=true) const
Definition: pam.hxx:229
sal_uInt16 sal_Unicode
SwIndex nContent
Definition: pam.hxx:38
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
size_t pos
static void lcl_FillRedlineArray(SwDoc const &rDoc, SwUnoCursor const &rUnoCursor, SwXRedlinePortion_ImplList &rRedArr)
static bool isTextBox(const SwFrameFormat *pFormat, sal_uInt16 nType)
Is the frame format a text box?
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
sal_Int32 GetStart() const
Definition: txatbase.hxx:82
virtual const_iterator_t findFirstAnnotationStartsAfter(const SwPosition &rPos) const =0
Finds the first mark that is starting after.
static css::uno::Reference< css::text::XTextContent > CreateXFieldmark(SwDoc &rDoc,::sw::mark::IMark *pMark, bool isReplacementObject=false)
Definition: unobkm.cxx:692
void SetTOXMark(css::uno::Reference< css::text::XTextContent > const &xMark)
Definition: unoport.hxx:210
constexpr TypedWhichId< SwFormatRuby > RES_TXTATR_CJK_RUBY(53)
constexpr TypedWhichId< SwFormatCharFormat > RES_TXTATR_CHARFMT(52)
const SwFormatField & GetFormatField() const
Definition: txatbase.hxx:191
constexpr TypedWhichId< SwFormatAutoFormat > RES_TXTATR_AUTOFMT(50)
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
constexpr TypedWhichId< SwFormatFootnote > RES_TXTATR_FTN(58)
void Normalize(bool bPointFirst=true)
Normalizes PaM, i.e.
Definition: pam.cxx:538
static Reference< XTextRange > lcl_CreateRefMarkPortion(Reference< XText > const &xParent, const SwUnoCursor *const pUnoCursor, const SwTextAttr &rAttr, const bool bEnd)
constexpr TypedWhichId< SwFormatINetFormat > RES_TXTATR_INETFMT(51)
void SelectPam(SwPaM &rPam, const bool bExpand)
Definition: unoobj.cxx:111
static void lcl_ExtractFramePositions(FrameClientSortList_t &rFrames, sal_Int32 nCurrentIndex, o3tl::sorted_vector< sal_Int32 > &rFramePositions)
Fills character positions from rFrames into rFramePositions.
DocumentType eType
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
PaM is Point and Mark: a selection of the document model.
Definition: pam.hxx:136
void SetFootnote(css::uno::Reference< css::text::XFootnote > const &xNote)
Definition: unoport.hxx:216
static css::uno::Reference< css::text::XTextContent > CreateXBookmark(SwDoc &rDoc,::sw::mark::IMark *pBookmark)
Definition: unobkm.cxx:160
constexpr TypedWhichId< SwFormatField > RES_TXTATR_FIELD(RES_TXTATR_NOEND_BEGIN)
Style of a layout element.
Definition: frmfmt.hxx:58
static void lcl_ExportBookmark(TextRangeList_t &rPortions, Reference< XText > const &xParent, const SwUnoCursor *const pUnoCursor, SwXBookmarkPortion_ImplList &rBkmArr, const sal_Int32 nIndex, const o3tl::sorted_vector< sal_Int32 > &rFramePositions, bool bOnlyFrameStarts)
Exports all bookmarks from rBkmArr into rPortions that have the same start or end position as nIndex...
size_t Count() const
Definition: ndhints.hxx:142
SwTextAttr * Get(size_t nPos) const
Definition: ndhints.hxx:144
static void lcl_ExportRedline(TextRangeList_t &rPortions, Reference< XText > const &xParent, const SwUnoCursor *const pUnoCursor, SwXRedlinePortion_ImplList &rRedlineArr, const sal_Int32 nIndex)
virtual OUString SAL_CALL getImplementationName() override
::sw::mark::IMark * GetAnnotationMark() const
Definition: atrfld.cxx:713
const SwPosition * GetPoint() const
Definition: pam.hxx:207
const SwPosition * GetContentAnchor() const
Definition: fmtanchr.hxx:67
void SetBookmark(css::uno::Reference< css::text::XTextContent > const &xMark)
Definition: unoport.hxx:213
Count
void Exchange()
Definition: pam.cxx:489
static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId()
static uno::Reference< text::XTextRange > lcl_ExportFieldMark(uno::Reference< text::XText > const &i_xParentText, SwUnoCursor *const pUnoCursor, const SwTextNode *const pTextNode)
FlyAnchors.
Definition: fmtanchr.hxx:34
void CollectFrameAtNode(const SwNodeIndex &rIdx, FrameClientSortList_t &rFrames, const bool bAtCharAnchoredObjs)
Definition: unoobj2.cxx:138
bool HasMark() const
A PaM marks a selection if Point and Mark are distinct positions.
Definition: pam.hxx:205
const Any & any
sal_Int32 indexOfAny(OUString const &rIn, sal_Unicode const *const pChars, sal_Int32 const nPos)
Marks a character position inside a document model node.
Definition: index.hxx:33
#define CH_TXT_ATR_FIELDSTART
Definition: hintids.hxx:178
unsigned char sal_Bool
std::pair< TextRangeList_t *const, SwTextAttr const *const > PortionList_t
Definition: unoportenum.cxx:71
static css::uno::Reference< css::text::XDocumentIndexMark > CreateXDocumentIndexMark(SwDoc &rDoc, SwTOXMark *pMark, TOXTypes eType=TOX_INDEX)
Definition: unoidx.cxx:1628
void SetTextField(css::uno::Reference< css::text::XTextField > const &xField)
Definition: unoport.hxx:219
static void lcl_InsertRubyPortion(TextRangeList_t &rPortions, Reference< XText > const &xParent, const SwUnoCursor *const pUnoCursor, const SwTextAttr &rAttr, const bool bEnd)
virtual const_iterator_t getAnnotationMarksBegin() const =0
Marks a node in the document model.
Definition: ndindex.hxx:31
void SetCollapsed(bool bSet)
Definition: unoport.hxx:225
const_iterator end() const
bool empty() const
std::stack< PortionList_t > PortionStack_t
Definition: unoportenum.cxx:72
static css::uno::Reference< css::text::XTextContent > CreateXReferenceMark(SwDoc &rDoc, SwFormatRefMark *pMarkFormat)
Definition: unorefmk.cxx:129
const SwFormatFootnote & GetFootnote() const
Definition: txatbase.hxx:200
static sal_Int32 lcl_GetNextIndex(SwXBookmarkPortion_ImplList const &rBkmArr, SwXRedlinePortion_ImplList const &rRedlineArr, SwSoftPageBreakList const &rBreakArr)
const SwPosition * Start() const
Definition: pam.hxx:212
BkmType
Definition: unoportenum.cxx:83
const_iterator begin() const
static void lcl_FillFieldMarkArray(std::deque< sal_Int32 > &rFieldMarks, SwUnoCursor const &rUnoCursor, const sal_Int32 i_nStartPos)
void fillSoftPageBreakList(SwSoftPageBreakList &rBreak) const
SwTextNode is a paragraph in the document model.
Definition: ndtxt.hxx:80
OUString ExpandField(bool bCached, SwRootFrame const *pLayout) const
expand the field.
Definition: fldbas.cxx:455
IDocumentRedlineAccess const & getIDocumentRedlineAccess() const
Definition: doc.cxx:335
SwXTextPortionEnumeration(SwPaM &rParaCursor, css::uno::Reference< css::text::XText > const &xParent, const sal_Int32 nStart, const sal_Int32 nEnd)
static void lcl_FillSoftPageBreakArray(SwUnoCursor const &rUnoCursor, SwSoftPageBreakList &rBreakArr)
An SwTextAttr container, stores all directly formatted text portions for a text node.
Definition: ndhints.hxx:67
static void lcl_ExportAnnotationStarts(TextRangeList_t &rPortions, Reference< XText > const &xParent, const SwUnoCursor *const pUnoCursor, SwAnnotationStartPortion_ImplList &rAnnotationStartArr, const sal_Int32 nIndex, const o3tl::sorted_vector< sal_Int32 > &rFramePositions, bool bOnlyFrame)
Exports all start annotation marks from rAnnotationStartArr into rPortions that have the same start p...
sw::UnoCursorPointer m_pUnoCursor
Definition: unoport.hxx:241
std::multiset< SwXRedlinePortion_ImplSharedPtr, RedlineCompareStruct > SwXRedlinePortion_ImplList
void reset(std::shared_ptr< SwUnoCursor > pNew)
Definition: unocrsr.hxx:157
bool Right(sal_uInt16 nCnt)
Definition: swcrsr.hxx:173
sal_Int32 GetIndex() const
Definition: index.hxx:91
virtual sal_Int64 SAL_CALL getSomething(const css::uno::Sequence< sal_Int8 > &aIdentifier) override
const SwPosition * End() const
Definition: pam.hxx:217
const sal_Int32 * End() const
Definition: txatbase.hxx:148
QPRO_FUNC_TYPE nType
static Reference< XTextRange > lcl_ExportHints(PortionStack_t &rPortionStack, const Reference< XText > &xParent, SwUnoCursor *const pUnoCursor, SwpHints const *const pHints, const sal_Int32 i_nStartPos, const sal_Int32 i_nEndPos, const sal_Int32 nCurrentIndex, const bool bRightMoveForbidden, bool &o_rbCursorMoved, sal_Int32 &o_rNextAttrPosition)
constexpr TypedWhichId< SwTOXMark > RES_TXTATR_TOXMARK(47)
void SetRefMark(css::uno::Reference< css::text::XTextContent > const &xMark)
Definition: unoport.hxx:207
virtual const SwPosition & GetMarkEnd() const =0
constexpr TypedWhichId< SwFormatField > RES_TXTATR_INPUTFIELD(55)
#define CH_TXT_ATR_FIELDEND
Definition: hintids.hxx:180
constexpr TypedWhichId< SwFlyFrameFormat > RES_FLYFRMFMT(154)
const SfxPoolItem & GetAttr() const
Definition: txatbase.hxx:159
uno::Reference< ucb::XContent > xContent
#define SAL_WARN(area, stream)
SwFrameFormat * GetFrameFormat() const
Definition: fmtflcnt.hxx:45
static void lcl_MoveCursor(SwUnoCursor *const pUnoCursor, const sal_Int32 nCurrentIndex, const sal_Int32 nNextFrameIndex, const sal_Int32 nNextPortionIndex, const sal_Int32 nNextAttrIndex, const sal_Int32 nNextMarkIndex, const sal_Int32 nEndPos)
SwDoc & GetDoc() const
Definition: pam.hxx:243
void SetMeta(css::uno::Reference< css::text::XTextContent > const &xMeta)
Definition: unoport.hxx:222
const SwIndex * GetFirstIndex() const
Definition: index.hxx:125
virtual const SwPosition & GetMarkStart() const =0
static void lcl_ExportBkmAndRedline(TextRangeList_t &rPortions, Reference< XText > const &xParent, const SwUnoCursor *const pUnoCursor, SwXBookmarkPortion_ImplList &rBkmArr, SwXRedlinePortion_ImplList &rRedlineArr, SwSoftPageBreakList &rBreakArr, const sal_Int32 nIndex, const o3tl::sorted_vector< sal_Int32 > &rFramePositions, bool bOnlyFrameBookmarkStarts)
constexpr TypedWhichId< SwFormatRefMark > RES_TXTATR_REFMARK(RES_TXTATR_WITHEND_BEGIN)
static sal_Int32 lcl_ExportFrames(TextRangeList_t &rPortions, Reference< XText > const &i_xParent, SwUnoCursor const *const i_pUnoCursor, FrameClientSortList_t &i_rFrames, sal_Int32 const i_nCurrentIndex)
Exports at-char anchored frames.
TextRangeList_t m_Portions
Definition: unoport.hxx:240
virtual const SwRedlineTable & GetRedlineTable() const =0
std::pair< const_iterator, bool > insert(Value &&x)
#define CH_TXT_ATR_FIELDSEP
Definition: hintids.hxx:179
virtual bool IsExpanded() const =0
static uno::Reference< text::XTextRange > lcl_CreateMetaPortion(uno::Reference< text::XText > const &xParent, const SwUnoCursor *const pUnoCursor, SwTextAttr &rAttr, std::unique_ptr< TextRangeList_t const > &&pPortions)
const SwFormatField * GetAnnotationFormatField() const
static void lcl_ExportSoftPageBreak(TextRangeList_t &rPortions, Reference< XText > const &xParent, const SwUnoCursor *const pUnoCursor, SwSoftPageBreakList &rBreakArr, const sal_Int32 nIndex)
SwTextNode * GetTextNode()
Inline methods from Node.hxx.
Definition: ndtxt.hxx:845
size_type erase(const Value &x)
static css::uno::Reference< css::text::XFootnote > GetObject(SwDoc &rDoc, const SwFormatFootnote &rFormat)
Definition: unocoll.cxx:1819
static void lcl_CreatePortions(TextRangeList_t &i_rPortions, uno::Reference< text::XText > const &i_xParentText, SwUnoCursor *pUnoCursor, FrameClientSortList_t &i_rFrames, const sal_Int32 i_nStartPos, const sal_Int32 i_nEndPos)
const SwFormatFlyCnt & GetFlyCnt() const
Definition: txatbase.hxx:206