Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  unoportenum.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <sal/config.h>
#include <sal/log.hxx>

#include <utility>

#include <unoport.hxx>
#include <unotext.hxx>
#include <IMark.hxx>
#include <crossrefbookmark.hxx>
#include <annotationmark.hxx>
#include <doc.hxx>
#include <IDocumentRedlineAccess.hxx>
#include <txatbase.hxx>
#include <txtatr.hxx>
#include <ndhints.hxx>
#include <ndtxt.hxx>
#include <unocrsr.hxx>
#include <docary.hxx>
#include <textboxhelper.hxx>
#include <tox.hxx>
#include <unoparaframeenum.hxx>
#include <unocrsrhelper.hxx>
#include <unorefmark.hxx>
#include <unobookmark.hxx>
#include <unofield.hxx>
#include <unometa.hxx>
#include <unolinebreak.hxx>
#include <unocontentcontrol.hxx>
#include <fmtfld.hxx>
#include <fldbas.hxx>
#include <fmtmeta.hxx>
#include <fmtanchr.hxx>
#include <fmtrfmrk.hxx>
#include <frmfmt.hxx>
#include <fmtflcnt.hxx>
#include <unoidx.hxx>
#include <unocoll.hxx>
#include <redline.hxx>
#include <txtannotationfld.hxx>
#include <vcl/svapp.hxx>
#include <comphelper/string.hxx>
#include <comphelper/servicehelper.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <com/sun/star/container/XEnumeration.hpp>
#include <algorithm>
#include <memory>
#include <set>
#include <stack>

using namespace ::com::sun::star;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::text;

typedef std::pair< TextRangeList_t * const, SwTextAttr const * const > PortionList_t;
typedef std::stack< PortionList_t > PortionStack_t;

static void lcl_CreatePortions(
    TextRangeList_t & i_rPortions,
    css::uno::Reference< SwXText > const& i_xParentText,
    SwUnoCursor* pUnoCursor,
    FrameClientSortList_t & i_rFrames,
    const sal_Int32 i_nStartPos, const sal_Int32 i_nEndPos, bool bOnlyTextFields );

namespace
{
    enum class BkmType {
        // The order is important: BookmarkCompareStruct::operator () depends on it.
        // When different bookmarks' starts/ends appear at one position, by default (when there's no
        // frames at the position - see lcl_ExportBookmark), first previous bookmarks close, then
        // collapsed ones appear, then new bookmarks open.
        End, StartEnd, Start
    };

    enum class ExportBookmarkPass
    {
        before_frames,
        after_frames,
    };

    struct SwXBookmarkPortion_Impl
    {
        rtl::Reference<SwXBookmark> xBookmark;
        BkmType                     nBkmType;
        const SwPosition            aPosition;

        SwXBookmarkPortion_Impl(rtl::Reference<SwXBookmark> xMark,
                const BkmType nType, SwPosition _aPosition)
        : xBookmark (std::move( xMark ))
        , nBkmType  ( nType )
        , aPosition (std::move( _aPosition ))
        {
        }
        sal_Int32 getIndex () const
        {
            return aPosition.GetContentIndex();
        }
    };
    typedef std::shared_ptr < SwXBookmarkPortion_Impl > SwXBookmarkPortion_ImplSharedPtr;
    struct BookmarkCompareStruct
    {
        bool operator () ( const SwXBookmarkPortion_ImplSharedPtr &r1,
                           const SwXBookmarkPortion_ImplSharedPtr &r2 ) const
        {
            // #i16896# for bookmark portions at the same position, the start should
            // always precede the end. Hence compare positions, and use bookmark type
            // as tie-breaker for same position.
            // return ( r1->nIndex   == r2->nIndex )
            //   ? ( r1->nBkmType <  r2->nBkmType )
            //   : ( r1->nIndex   <  r2->nIndex );

            // Note that the above code does not correctly handle
            // the case when one bookmark ends, and another begins in the same
            // position. When this occurs, the above code will return the
            // start of the 2nd bookmark BEFORE the end of the first bookmark
            // See bug #i58438# for more details. The below code is correct and
            // fixes both #i58438 and #i16896#
            return std::make_pair(r1->aPosition, r1->nBkmType)
                   < std::make_pair(r2->aPosition, r2->nBkmType);
        }
    };
    typedef std::multiset < SwXBookmarkPortion_ImplSharedPtr, BookmarkCompareStruct > SwXBookmarkPortion_ImplList;

    /// Inserts pBkmk to rBkmArr in case it starts or ends at rOwnNode
    void lcl_FillBookmark(sw::mark::MarkBase* const pBkmk, const SwNode& rOwnNode, SwDoc& rDoc, SwXBookmarkPortion_ImplList& rBkmArr)
    {
        bool const isExpanded = pBkmk->IsExpanded();
        auto [/*const SwPosition&*/ rStartPos, rEndPos] = pBkmk->GetMarkStartEnd();
        // A bookmark where the text was deleted becomes collapsed
        bool const hasOther = isExpanded && rStartPos != rEndPos;
        bool const bStartPosInNode = rStartPos.GetNode() == rOwnNode;
        bool const bEndPosInNode = rEndPos.GetNode() == rOwnNode;
        // tdf#160700: Crossrefbookmarks only need separate start and end, when the start
        // isn't in the end position (so in empty nodes, no need to handle them specially)
        sw::mark::CrossRefBookmark* const pCrossRefMark
            = !isExpanded && bStartPosInNode
                      && rStartPos.GetContentIndex() < rStartPos.GetContentNode()->Len()
                  ? dynamic_cast<sw::mark::CrossRefBookmark*>(pBkmk)
                  : nullptr;

        if (bStartPosInNode)
        {
            // #i109272#: cross reference marks: need special handling!
            BkmType const nType = (hasOther || pCrossRefMark)
                ? BkmType::Start : BkmType::StartEnd;
            rBkmArr.insert(std::make_shared<SwXBookmarkPortion_Impl>(
                        SwXBookmark::CreateXBookmark(rDoc, pBkmk),
                        nType, rStartPos));
        }

        if (!bEndPosInNode)
            return;

        std::optional<SwPosition> oCrossRefEndPos;
        const SwPosition* pEndPos = nullptr;
        if(hasOther)
        {
            pEndPos = &rEndPos;
        }
        else if (pCrossRefMark)
        {
            // Crossrefbookmarks only remember the start position but have to span the whole paragraph
            SwTextNode& rEndNd = *rEndPos.GetNode().GetTextNode();
            oCrossRefEndPos.emplace(rEndNd, rEndNd.Len());
            pEndPos = &*oCrossRefEndPos;
        }
        if(pEndPos)
        {
            rBkmArr.insert(std::make_shared<SwXBookmarkPortion_Impl>(
                        SwXBookmark::CreateXBookmark(rDoc, pBkmk),
                        BkmType::End, *pEndPos));
        }
    }

    void lcl_FillBookmarkArray(SwDoc& rDoc, SwUnoCursor& rUnoCursor, SwXBookmarkPortion_ImplList& rBkmArr)
    {
        IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
        if(!pMarkAccess->getBookmarksCount())
            return;

        SwTextNode* pTextNode = rUnoCursor.GetPoint()->GetNode().GetTextNode();
        assert(pTextNode);
        // A text node already knows its marks via its SwContentIndexes.
        o3tl::sorted_vector<const sw::mark::MarkBase*> aSeenMarks;
        for (const SwContentIndex* pIndex = pTextNode->GetFirstIndex(); pIndex; pIndex = pIndex->GetNext())
        {
            if (!pIndex->GetOwner() || pIndex->GetOwner()->GetOwnerType() != SwContentIndexOwnerType::Mark)
                continue;
            // Need a non-cost mark here, as we'll create a UNO wrapper around it.
            sw::mark::MarkBase* pBkmk = static_cast<sw::mark::MarkBase*>(pIndex->GetOwner());
            if (!pBkmk)
                continue;
            IDocumentMarkAccess::MarkType eType = IDocumentMarkAccess::GetType(*pBkmk);
            // These are the types stored in the container otherwise accessible via getBookmarks*()
            if (eType != IDocumentMarkAccess::MarkType::BOOKMARK && eType != IDocumentMarkAccess::MarkType::CROSSREF_NUMITEM_BOOKMARK &&
                eType != IDocumentMarkAccess::MarkType::CROSSREF_HEADING_BOOKMARK)
                continue;
            // Only handle bookmarks once, if they start and end at this node as well.
            if (!aSeenMarks.insert(pBkmk).second)
                continue;
            lcl_FillBookmark(pBkmk, *pTextNode, rDoc, rBkmArr);
        }
    }

    struct SwAnnotationStartPortion_Impl
    {

        uno::Reference< text::XTextField > mxAnnotationField;
        const SwPosition maPosition;

        SwAnnotationStartPortion_Impl(
            uno::Reference< text::XTextField > xAnnotationField,
            SwPosition aPosition)
        : mxAnnotationField (std::move( xAnnotationField ))
        , maPosition (std::move( aPosition ))
        {
        }

        sal_Int32 getIndex () const
        {
            return maPosition.GetContentIndex();
        }
    };
    typedef std::shared_ptr < SwAnnotationStartPortion_Impl > SwAnnotationStartPortion_ImplSharedPtr;
    struct AnnotationStartCompareStruct
    {
        bool operator () ( const SwAnnotationStartPortion_ImplSharedPtr &r1,
                           const SwAnnotationStartPortion_ImplSharedPtr &r2 )
            const
        {
            return r1->maPosition < r2->maPosition;
        }
    };
    typedef std::multiset < SwAnnotationStartPortion_ImplSharedPtr, AnnotationStartCompareStruct > SwAnnotationStartPortion_ImplList;

    void lcl_FillAnnotationStartArray(
        SwDoc& rDoc,
        SwUnoCursor& rUnoCursor,
        SwAnnotationStartPortion_ImplList& rAnnotationStartArr )
    {
        IDocumentMarkAccess* const pMarkAccess = rDoc.getIDocumentMarkAccess();
        if ( pMarkAccess->getAnnotationMarksCount() == 0 )
        {
            return;
        }

        // search for all annotation marks that have its start position in this paragraph
        const SwNode& rOwnNode = rUnoCursor.GetPoint()->GetNode();
        const auto pCandidatesEnd = pMarkAccess->getAnnotationMarksEnd();
        forauto ppMark = pMarkAccess->findFirstAnnotationMarkNotStartsBefore(rOwnNode);
             ppMark != pCandidatesEnd;
             ++ppMark )
        {
            ::sw::mark::AnnotationMark* const pAnnotationMark = *ppMark;
            assert(pAnnotationMark);
            const SwPosition& rStartPos = pAnnotationMark->GetMarkStart();
            // no need to consider annotation marks starting after
            if (rStartPos.GetNode() > rOwnNode)
                break;
            if (rStartPos.GetNode() != rOwnNode)
                continue;

            const SwFormatField* pAnnotationFormatField = pAnnotationMark->GetAnnotationFormatField();
            if (!pAnnotationFormatField)
            {
                SAL_WARN("sw.core""missing annotation format field");
                continue;
            }

            rAnnotationStartArr.insert(
                std::make_shared<SwAnnotationStartPortion_Impl>(
                        SwXTextField::CreateXTextField(&rDoc,
                            pAnnotationFormatField),
                        rStartPos));
        }
    }
}

OUString SwXTextPortionEnumeration::getImplementationName()
{
    return u"SwXTextPortionEnumeration"_ustr;
}

sal_Bool
SwXTextPortionEnumeration::supportsService(const OUString& rServiceName)
{
    return cppu::supportsService(this, rServiceName);
}

Sequence< OUString > SwXTextPortionEnumeration::getSupportedServiceNames()
{
    return { u"com.sun.star.text.TextPortionEnumeration"_ustr };
}

SwXTextPortionEnumeration::SwXTextPortionEnumeration(
        SwPaM& rParaCursor,
        css::uno::Reference< SwXText > const & xParentText,
        const sal_Int32 nStart,
        const sal_Int32 nEnd,
        bool bOnlyTextFields)
    : m_pUnoCursor(rParaCursor.GetDoc().CreateUnoCursor(*rParaCursor.GetPoint()))
{
    OSL_ENSURE(nEnd == -1 || (nStart <= nEnd &&
        nEnd <= m_pUnoCursor->Start()->GetNode().GetTextNode()->GetText().getLength()),
            "start or end value invalid!");

    // find all frames, graphics and OLEs that are bound AT character in para
    FrameClientSortList_t frames;
    ::CollectFrameAtNode(m_pUnoCursor->GetPoint()->GetNode(), frames, true);
    lcl_CreatePortions(m_Portions, xParentText, &*m_pUnoCursor, frames, nStart, nEnd, bOnlyTextFields);
}

SwXTextPortionEnumeration::SwXTextPortionEnumeration(
        SwPaM& rParaCursor,
        TextRangeList_t && rPortions )
    : m_Portions(std::move(rPortions))
    , m_pUnoCursor(rParaCursor.GetDoc().CreateUnoCursor(*rParaCursor.GetPoint()))
{
}

SwXTextPortionEnumeration::~SwXTextPortionEnumeration()
{
    SolarMutexGuard aGuard;
    if( m_pUnoCursor )
    {
        m_pUnoCursor->GetDoc().cleanupUnoCursorTable();
        m_pUnoCursor.reset(nullptr);
    }
}

sal_Bool SwXTextPortionEnumeration::hasMoreElements()
{
    SolarMutexGuard aGuard;

    return !m_Portions.empty();
}

uno::Any SwXTextPortionEnumeration::nextElement()
{
    SolarMutexGuard aGuard;

    if (m_Portions.empty())
        throw container::NoSuchElementException();

    Any any;
    any <<= uno::Reference<XTextRange>(m_Portions.front());
    m_Portions.pop_front();
    return any;
}

static void
lcl_FillFieldMarkArray(std::deque<sal_Int32> & rFieldMarks, SwUnoCursor const & rUnoCursor,
        const sal_Int32 i_nStartPos)
{
    const SwTextNode * const pTextNode =
        rUnoCursor.GetPoint()->GetNode().GetTextNode();
    if (!pTextNode) return;

    const sal_Unicode fld[] = {
        CH_TXT_ATR_FIELDSTART, CH_TXT_ATR_FIELDSEP, CH_TXT_ATR_FIELDEND, CH_TXT_ATR_FORMELEMENT, 0 };
    sal_Int32 pos = std::max(static_cast<sal_Int32>(0), i_nStartPos);
    while ((pos = ::comphelper::string::indexOfAny(pTextNode->GetText(), fld, pos)) != -1)
    {
        rFieldMarks.push_back(pos);
        ++pos;
    }
}

static rtl::Reference<SwXTextPortion>
lcl_ExportFieldMark(
        uno::Reference< SwXText > const & i_xParentText,
        SwUnoCursor * const pUnoCursor,
        const SwTextNode * const pTextNode )
{
    rtl::Reference<SwXTextPortion> pPortion;
    SwDoc& rDoc = pUnoCursor->GetDoc();
    // maybe it's a good idea to add a special hint to the hints array and rely on the hint segmentation...
    const sal_Int32 start = pUnoCursor->Start()->GetContentIndex();
    OSL_ENSURE(pUnoCursor->End()->GetContentIndex() == start,
               "hmm --- why is this different");

    pUnoCursor->Right(1);
    if ( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
    {
        OSL_FAIL("cannot move cursor?");
        return nullptr;
    }

    const sal_Unicode Char = pTextNode->GetText()[start];
    if (CH_TXT_ATR_FIELDSTART == Char)
    {
        ::sw::mark::Fieldmark* pFieldmark = nullptr;
        pFieldmark = rDoc.getIDocumentMarkAccess()->
            getFieldmarkAt(*pUnoCursor->GetMark());
        pPortion = new SwXTextPortion(
            pUnoCursor, i_xParentText, PORTION_FIELD_START);
        if (pFieldmark)
        {
            pPortion->SetBookmark(
                SwXFieldmark::CreateXFieldmark(rDoc, pFieldmark));
        }
    }
    else if (CH_TXT_ATR_FIELDSEP == Char)
    {
        // TODO how to get the field?
        pPortion = new SwXTextPortion(
            pUnoCursor, i_xParentText, PORTION_FIELD_SEP);
    }
    else if (CH_TXT_ATR_FIELDEND == Char)
    {
        ::sw::mark::Fieldmark* pFieldmark = nullptr;
        pFieldmark = rDoc.getIDocumentMarkAccess()->
            getFieldmarkAt(*pUnoCursor->GetMark());
        pPortion = new SwXTextPortion(
            pUnoCursor, i_xParentText, PORTION_FIELD_END);
        if (pFieldmark)
        {
            pPortion->SetBookmark(
                SwXFieldmark::CreateXFieldmark(rDoc, pFieldmark));
        }
    }
    else if (CH_TXT_ATR_FORMELEMENT == Char)
    {
        ::sw::mark::Fieldmark* pFieldmark =
            rDoc.getIDocumentMarkAccess()->getFieldmarkAt(*pUnoCursor->GetMark());
        pPortion = new SwXTextPortion(
            pUnoCursor, i_xParentText, PORTION_FIELD_START_END);
        if (pFieldmark)
        {
            pPortion->SetBookmark(
                SwXFieldmark::CreateXFieldmark(rDoc, pFieldmark));
        }
    }
    else
    {
        OSL_FAIL("no fieldmark found?");
    }
    return pPortion;
}

static rtl::Reference<SwXTextPortion>
lcl_CreateRefMarkPortion(
    uno::Reference<SwXText> const& xParent,
    const SwUnoCursor * const pUnoCursor,
    const SwTextAttr & rAttr, const bool bEnd)
{
    SwDoc& rDoc = pUnoCursor->GetDoc();
    SwFormatRefMark& rRefMark = const_cast<SwFormatRefMark&>(
            static_cast<const SwFormatRefMark&>(rAttr.GetAttr()));
    rtl::Reference<SwXReferenceMark> xContent = SwXReferenceMark::CreateXReferenceMark(rDoc, &rRefMark);

    rtl::Reference<SwXTextPortion> pPortion;
    if (!bEnd)
    {
        pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_REFMARK_START);
        pPortion->SetRefMark(xContent);
        pPortion->SetCollapsed(rAttr.End() == nullptr);
    }
    else
    {
        pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_REFMARK_END);
        pPortion->SetRefMark(xContent);
    }
    return pPortion;
}

static void
lcl_InsertRubyPortion(
    TextRangeList_t & rPortions,
    uno::Reference<SwXText> const& xParent,
    const SwUnoCursor * const pUnoCursor,
    const SwTextAttr & rAttr, const bool bEnd)
{
    rtl::Reference<SwXTextPortion> pPortion = new SwXTextPortion(pUnoCursor,
            static_txtattr_cast<const SwTextRuby&>(rAttr), xParent, bEnd);
    rPortions.emplace_back(pPortion);
    pPortion->SetCollapsed(rAttr.End() == nullptr);
}

static rtl::Reference<SwXTextPortion>
lcl_CreateTOXMarkPortion(
    uno::Reference<SwXText> const& xParent,
    const SwUnoCursor * const pUnoCursor,
    SwTextAttr & rAttr, const bool bEnd)
{
    SwDoc& rDoc = pUnoCursor->GetDoc();
    SwTOXMark & rTOXMark = static_cast<SwTOXMark&>(rAttr.GetAttr());

    const rtl::Reference<SwXDocumentIndexMark> xContent =
        SwXDocumentIndexMark::CreateXDocumentIndexMark(rDoc, & rTOXMark);

    rtl::Reference<SwXTextPortion> pPortion;
    if (!bEnd)
    {
        pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_TOXMARK_START);
        pPortion->SetTOXMark(xContent);
        pPortion->SetCollapsed(rAttr.GetEnd() == nullptr);
    }
    else
    {
        pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_TOXMARK_END);
        pPortion->SetTOXMark(xContent);
    }
    return pPortion;
}

static rtl::Reference<SwXTextPortion>
lcl_CreateMetaPortion(
    css::uno::Reference<SwXText> const& xParent,
    const SwUnoCursor * const pUnoCursor,
    SwTextAttr & rAttr, std::unique_ptr<TextRangeList_t const> && pPortions)
{
    const rtl::Reference<SwXMeta> xMeta( SwXMeta::CreateXMeta(
            *static_cast<SwFormatMeta &>(rAttr.GetAttr()).GetMeta(),
            xParent, std::move(pPortions)));
    rtl::Reference<SwXTextPortion> pPortion;
    if (RES_TXTATR_META == rAttr.Which())
    {
        pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_META);
        pPortion->SetMeta(xMeta);
    }
    else
    {
        const uno::Reference<text::XTextField> xField(static_cast<cppu::OWeakObject*>(xMeta.get()), uno::UNO_QUERY);
        pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_FIELD);
        pPortion->SetTextField(xField);
    }
    return pPortion;
}

/// Creates a text portion that has a non-empty ContentControl property.
static rtl::Reference<SwXTextPortion>
lcl_CreateContentControlPortion(const css::uno::Reference<SwXText>& xParent,
                                const SwUnoCursor* pUnoCursor, SwTextAttr& rAttr,
                                std::unique_ptr<const TextRangeList_t>&& pPortions)
{
    rtl::Reference<SwXContentControl> xContentControl = SwXContentControl::CreateXContentControl(
        *static_cast<SwFormatContentControl&>(rAttr.GetAttr()).GetContentControl(), xParent,
        std::move(pPortions));
    rtl::Reference<SwXTextPortion> pPortion;
    pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_CONTENT_CONTROL);
    pPortion->SetContentControl(xContentControl);
    return pPortion;
}

/**
 * Exports all bookmarks from rBkmArr into rPortions that have the same start
 * or end position as nIndex.
 *
 * @param rBkmArr the array of bookmarks.
 *
 * @param rFramePositions the list of positions where there is an at-char /
 * anchored frame.
 * Collapsed (BkmType::StartEnd) bookmarks, as well as bookmarks that start/end
 * at the frame anchor position, are considered as wrapping the frames, if any
 * (i.e., starts are output before the frames; ends are output after frames).
 * When there's no frame here, bookmarks are expected to not overlap (#i58438):
 * first, non-collapsed bookmarks' ends are output; then collapsed bookmarks;
 * then non-collapsed bookmarks' starts.
 *
 * @param stage Case before_frames: if there is a frame at this index, output
 * starts of both collapsed and non-collapsed bookmarks (remove non-collapsed
 * starts from rBkmArr, convert collapsed ones to ends); if there's no frame,
 * doesn't output anything.
 * Case after_frames: outputs (and removes from rBkmArr) everything (left) at
 * this index, in the order of occurrence in rBkmArr (see #i58438).
 */

static void lcl_ExportBookmark(
    TextRangeList_t & rPortions,
    uno::Reference<SwXText> const& xParent,
    const SwUnoCursor * const pUnoCursor,
    SwXBookmarkPortion_ImplList& rBkmArr,
    const sal_Int32 nIndex,
    const o3tl::sorted_vector<sal_Int32>& rFramePositions,
    ExportBookmarkPass stage)
{
    for ( SwXBookmarkPortion_ImplList::iterator aIter = rBkmArr.begin(), aEnd = rBkmArr.end(); aIter != aEnd; )
    {
        const SwXBookmarkPortion_ImplSharedPtr& pPtr = *aIter;
        if ( nIndex > pPtr->getIndex() )
        {
            // We may get here, if SwXTextPortionEnumeration ctor was called with nStart greater
            // than this bookmark's index. Just drop it.
            aIter = rBkmArr.erase(aIter);
            continue;
        }
        if ( nIndex < pPtr->getIndex() )
            break;

        if (stage == ExportBookmarkPass::before_frames)
        {
            if (rFramePositions.find(nIndex) == rFramePositions.end()) // No frames at this index
                break// Do nothing; everything will be output at after_frames pass

            if (pPtr->nBkmType == BkmType::End)
            {
                ++aIter;
                continue// Only consider BkmType::Start and BkmType::StartEnd in this pass
            }
        }

        // At this we create a text portion, due to one of these
        // reasons:
        // - this is the real start of a non-collapsed bookmark
        // - this is the real end of a non-collapsed bookmark
        // - this is the real position of a collapsed bookmark
        // - this is the start or end of a collapsed bookmark at the same position as an at-char
        //   anchored frame
        const SwTextPortionType portionType
            = pPtr->nBkmType == BkmType::End ? PORTION_BOOKMARK_END : PORTION_BOOKMARK_START;
        const bool collapsed
            = pPtr->nBkmType == BkmType::StartEnd && stage == ExportBookmarkPass::after_frames;

        rtl::Reference<SwXTextPortion> pPortion = new SwXTextPortion(pUnoCursor, xParent, portionType);
        rPortions.emplace_back(pPortion);
        pPortion->SetBookmark(pPtr->xBookmark);
        pPortion->SetCollapsed(collapsed);

        // next bookmark
        if (pPtr->nBkmType == BkmType::StartEnd && stage == ExportBookmarkPass::before_frames)
        {
            // This is a collapsed bookmark around a frame, and its start portion was just emitted;
            // turn it into an end bookmark to process after_frames
            pPtr->nBkmType = BkmType::End;
            ++aIter;
        }
        else
            aIter = rBkmArr.erase(aIter);
    }
}

static void lcl_ExportSoftPageBreak(
    TextRangeList_t & rPortions,
    uno::Reference<SwXText> const& xParent,
    const SwUnoCursor * const pUnoCursor,
    SwSoftPageBreakList& rBreakArr,
    const sal_Int32 nIndex)
{
    for ( auto aIter = rBreakArr.begin(); aIter != rBreakArr.end(); )
    {
        if ( nIndex > *aIter )
        {
            aIter = rBreakArr.erase(aIter);
            continue;
        }
        if ( nIndex < *aIter )
            break;

        rPortions.push_back(
            new SwXTextPortion(pUnoCursor, xParent, PORTION_SOFT_PAGEBREAK) );
        aIter = rBreakArr.erase(aIter);
    }
}

namespace {

struct SwXRedlinePortion_Impl
{
    const SwRangeRedline*    m_pRedline;
    const bool          m_bStart;

    SwXRedlinePortion_Impl ( const SwRangeRedline* pRed, const bool bIsStart )
    : m_pRedline(pRed)
    , m_bStart(bIsStart)
    {
    }

    sal_Int32 getRealIndex () const
    {
        return m_bStart ? m_pRedline->Start()->GetContentIndex()
                        : m_pRedline->End()  ->GetContentIndex();
    }
};

}

typedef std::shared_ptr < SwXRedlinePortion_Impl >
    SwXRedlinePortion_ImplSharedPtr;

namespace {

struct RedlineCompareStruct
{
    static const SwPosition& getPosition ( const SwXRedlinePortion_ImplSharedPtr &r )
    {
        return *(r->m_bStart ? r->m_pRedline->Start() : r->m_pRedline->End());
    }

    bool operator () ( const SwXRedlinePortion_ImplSharedPtr &r1,
                       const SwXRedlinePortion_ImplSharedPtr &r2 ) const
    {
        return getPosition ( r1 ) < getPosition ( r2 );
    }
};

}

typedef std::multiset < SwXRedlinePortion_ImplSharedPtr, RedlineCompareStruct >
SwXRedlinePortion_ImplList;

static rtl::Reference<SwXTextPortion>
lcl_ExportHints(
    PortionStack_t & rPortionStack,
    const css::uno::Reference<SwXText> & 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)
{
    // if the attribute has a dummy character, then xRef is set (except META and CONTENT_CONTROL)
    // otherwise, the portion for the attribute is inserted into rPortions!
    rtl::Reference<SwXTextPortion> pPortion;
    SwDoc& rDoc = pUnoCursor->GetDoc();
    //search for special text attributes - first some ends
    size_t nEndIndex = 0;
    const auto nHintsCount = pHints->Count();
    for (;;)
    {
        if (nEndIndex >= nHintsCount)
            break;
        SwTextAttr * const pAttr = pHints->GetSortedByEnd(nEndIndex);
        const sal_Int32* pAttrEnd = pAttr->GetEnd();
        sal_Int32 nNextEnd = 0;
        if (pAttrEnd &&
            nCurrentIndex < (nNextEnd = (*pAttrEnd)))
            break;
        if(pAttrEnd)
        {
            if (nNextEnd == nCurrentIndex)
            {
                const sal_uInt16 nWhich( pAttr->Which() );
                switch (nWhich)
                {
                    case RES_TXTATR_TOXMARK:
                    {
                        rtl::Reference<SwXTextPortion> xTmp = lcl_CreateTOXMarkPortion(
                                xParent, pUnoCursor, *pAttr, true);
                        rPortionStack.top().first->push_back(xTmp);
                    }
                    break;
                    case RES_TXTATR_REFMARK:
                    {
                        rtl::Reference<SwXTextPortion> xTmp = lcl_CreateRefMarkPortion(
                                xParent, pUnoCursor, *pAttr, true);
                        rPortionStack.top().first->push_back(xTmp);
                    }
                    break;
                    case RES_TXTATR_CJK_RUBY:
                       //#i91534# GetEnd() == 0 mixes the order of ruby start/end
                        if( *pAttr->GetEnd() == pAttr->GetStart())
                        {
                            lcl_InsertRubyPortion( *rPortionStack.top().first,
                                    xParent, pUnoCursor, *pAttr, false);
                        }
                        lcl_InsertRubyPortion( *rPortionStack.top().first,
                                xParent, pUnoCursor, *pAttr, true);
                    break;
                    case RES_TXTATR_META:
                    case RES_TXTATR_METAFIELD:
                    {
                        OSL_ENSURE(pAttr->GetStart() != *pAttr->GetEnd(),
                                "empty meta?");
                        if ((i_nStartPos > 0) &&
                            (pAttr->GetStart() < i_nStartPos))
                        {
                            // force skip pAttr and rest of attribute ends
                            // at nCurrentIndex
                            // because they are not contained in the meta pAttr
                            // and the meta pAttr itself is outside selection!
                            // (necessary for SwXMeta::createEnumeration)
                            if (pAttr->GetStart() + 1 == i_nStartPos)
                            {
                                nEndIndex = pHints->Count() - 1;
                            }
                            break;
                        }
                        PortionList_t Top = rPortionStack.top();
                        if (Top.second != pAttr)
                        {
                            OSL_FAIL("ExportHints: stack error" );
                        }
                        else
                        {
                            std::unique_ptr<const TextRangeList_t>
                                pCurrentPortions(Top.first);
                            rPortionStack.pop();
                            rtl::Reference<SwXTextPortion> xPortion(
                                lcl_CreateMetaPortion(xParent, pUnoCursor,
                                                      *pAttr, std::move(pCurrentPortions)));
                            rPortionStack.top().first->push_back(xPortion);
                        }
                    }
                    break;
                    case RES_TXTATR_CONTENTCONTROL:
                    {
                        if (pAttr->GetStart() == *pAttr->GetEnd())
                        {
                            SAL_WARN("sw.core""lcl_ExportHints: empty content control");
                        }
                        if ((i_nStartPos > 0) && (pAttr->GetStart() < i_nStartPos))
                        {
                            // If the start pos is the start of the content of the content control,
                            // skip it: it'll be handled in SwXContentControl::createEnumeration().
                            if (pAttr->GetStart() + 1 == i_nStartPos)
                            {
                                nEndIndex = pHints->Count() - 1;
                            }
                            break;
                        }
                        PortionList_t Top = rPortionStack.top();
                        if (Top.second != pAttr)
                        {
                            SAL_WARN("sw.core""lcl_ExportHints: content control is not at the "
                                                "top of the portion stack");
                        }
                        else
                        {
                            std::unique_ptr<const TextRangeList_t> pCurrentPortions(Top.first);
                            rPortionStack.pop();
                            rtl::Reference<SwXTextPortion> xPortion(
                                lcl_CreateContentControlPortion(xParent, pUnoCursor, *pAttr,
                                                                std::move(pCurrentPortions)));
                            rPortionStack.top().first->push_back(xPortion);
                        }
                    }
                    break;
                }
            }
        }
        nEndIndex++;
    }

    // then some starts
    size_t nStartIndex = 0;
    sal_Int32 nNextStart = 0;
    while(nStartIndex < nHintsCount &&
        nCurrentIndex >= (nNextStart = pHints->Get(nStartIndex)->GetStart()))
    {
        SwTextAttr * const pAttr = pHints->Get(nStartIndex);
        sal_uInt16 nAttrWhich = pAttr->Which();
        if (nNextStart == nCurrentIndex)
        {
            switch( nAttrWhich )
            {
                case RES_TXTATR_FIELD:
                    if(!bRightMoveForbidden)
                    {
                        pUnoCursor->Right(1);
                        if( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
                            break;
                        pPortion = new SwXTextPortion(
                                pUnoCursor, xParent, PORTION_FIELD);
                        rtl::Reference<SwXTextField> const xField =
                            SwXTextField::CreateXTextField(&rDoc,
                                    &pAttr->GetFormatField());
                        pPortion->SetTextField(Reference<XTextField>(xField));
                    }
                    break;

                case RES_TXTATR_ANNOTATION:
                    if(!bRightMoveForbidden)
                    {
                        pUnoCursor->Right(1);
                        if( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
                            break;

                        const SwTextAnnotationField* pTextAnnotationField = dynamic_cast<const SwTextAnnotationField*>( pAttr );
                        ::sw::mark::MarkBase* pAnnotationMark = pTextAnnotationField ? pTextAnnotationField->GetAnnotationMark() : nullptr;
                        if ( pAnnotationMark != nullptr )
                        {
                            pPortion = new SwXTextPortion( pUnoCursor, xParent, PORTION_ANNOTATION_END );
                            pPortion->SetBookmark(SwXBookmark::CreateXBookmark(
                                        rDoc, pAnnotationMark));
                        }
                        else
                        {
                            pPortion = new SwXTextPortion( pUnoCursor, xParent, PORTION_ANNOTATION );
                            rtl::Reference<SwXTextField> xField =
                                SwXTextField::CreateXTextField(&rDoc,
                                        &pAttr->GetFormatField());
                            pPortion->SetTextField(Reference<XTextField>(xField));
                        }
                    }
                    break;

                case RES_TXTATR_INPUTFIELD:
                    if(!bRightMoveForbidden)
                    {

                        pUnoCursor->Right(
                            pAttr->GetFormatField().GetField()->ExpandField(true, nullptr).getLength() + 2 );
                        if( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
                            break;
                        pPortion = new SwXTextPortion( pUnoCursor, xParent, PORTION_FIELD);
                        rtl::Reference<SwXTextField> xField =
                            SwXTextField::CreateXTextField(&rDoc,
                                    &pAttr->GetFormatField());
                        pPortion->SetTextField(Reference<XTextField>(xField));
                    }
                    break;

                case RES_TXTATR_FLYCNT:
                    if(!bRightMoveForbidden)
                    {
                        pUnoCursor->Right(1);
                        if( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
                            break// Robust #i81708# content in covered cells

                        // Do not expose inline anchored textboxes.
                        if (SwTextBoxHelper::isTextBox(pAttr->GetFlyCnt().GetFrameFormat(), RES_FLYFRMFMT))
                            break;

                        pUnoCursor->Exchange();
                        pPortion = new SwXTextPortion( pUnoCursor, xParent, PORTION_FRAME);
                    }
                    break;

                case RES_TXTATR_FTN:
                    {
                        if(!bRightMoveForbidden)
                        {
                            pUnoCursor->Right(1);
                            if( *pUnoCursor->GetMark() == *pUnoCursor->GetPoint() )
                                break;
                            pPortion = new SwXTextPortion(
                                pUnoCursor, xParent, PORTION_FOOTNOTE);
                            rtl::Reference<SwXFootnote> xContent =
                                SwXFootnotes::GetObject(rDoc, pAttr->GetFootnote());
                            pPortion->SetFootnote(xContent);
                        }
                    }
                    break;

                case RES_TXTATR_TOXMARK:
                case RES_TXTATR_REFMARK:
                {
                    bool bIsPoint = !(pAttr->GetEnd());
                    if (!bRightMoveForbidden || !bIsPoint)
                    {
                        if (bIsPoint)
                        {
                            pUnoCursor->Right(1);
                        }
                        rtl::Reference<SwXTextPortion> xTmp =
                                (RES_TXTATR_REFMARK == nAttrWhich)
                            ? lcl_CreateRefMarkPortion(
                                xParent, pUnoCursor, *pAttr, false)
                            : lcl_CreateTOXMarkPortion(
                                xParent, pUnoCursor, *pAttr, false);
                        if (bIsPoint) // consume CH_TXTATR!
                        {
                            pUnoCursor->Normalize(false);
                            pUnoCursor->DeleteMark();
                            pPortion = std::move(xTmp);
                        }
                        else // just insert it
                        {
                            rPortionStack.top().first->push_back(xTmp);
                        }
                    }
                }
                break;
                case RES_TXTATR_CJK_RUBY:
                    //#i91534# GetEnd() == 0 mixes the order of ruby start/end
                    if(pAttr->GetEnd() && (*pAttr->GetEnd() != pAttr->GetStart()))
                    {
                        lcl_InsertRubyPortion( *rPortionStack.top().first,
                            xParent, pUnoCursor, *pAttr, false);
                    }
                break;
                case RES_TXTATR_META:
                case RES_TXTATR_METAFIELD:
                case RES_TXTATR_CONTENTCONTROL:
                    if (pAttr->GetStart() != *pAttr->GetEnd())
                    {
                        if (!bRightMoveForbidden)
                        {
                            pUnoCursor->Right(1);
                            o_rbCursorMoved = true;
                            // only if the end is included in selection!
                            if ((i_nEndPos < 0) ||
                                (*pAttr->GetEnd() <= i_nEndPos))
                            {
                                rPortionStack.push( std::make_pair(
                                        new TextRangeList_t, pAttr ));
                            }
                        }
                    }
                break;
                case RES_TXTATR_LINEBREAK:
                    if (!bRightMoveForbidden)
                    {
                        pUnoCursor->Right(1);
                        if (*pUnoCursor->GetMark() == *pUnoCursor->GetPoint())
                            break;
                        pPortion = new SwXTextPortion(pUnoCursor, xParent, PORTION_LINEBREAK);
                        rtl::Reference<SwXLineBreak> xLineBreak
                            = SwXLineBreak::CreateXLineBreak(
                                &const_cast<SwFormatLineBreak&>(pAttr->GetLineBreak()));
                        pPortion->SetLineBreak(xLineBreak);
                    }
                    break;
                case RES_TXTATR_AUTOFMT:
                case RES_TXTATR_INETFMT:
                case RES_TXTATR_CHARFMT:
                break// these are handled as properties of a "Text" portion
                default:
                    OSL_FAIL("unknown attribute");
                break;
            }
        }
        nStartIndex++;
    }

    if (pPortion.is()) // implies that we have moved the cursor
    {
        o_rbCursorMoved = true;
    }
    if (!o_rbCursorMoved)
    {
        // search for attribute changes behind the current cursor position
        // break up at frames, bookmarks, redlines

        nStartIndex = 0;
        nNextStart = 0;
        while(nStartIndex < pHints->Count() &&
            nCurrentIndex >= (nNextStart = pHints->Get(nStartIndex)->GetStart()))
            nStartIndex++;

        nEndIndex = 0;
        sal_Int32 nNextEnd = 0;
        while(nEndIndex < pHints->Count() &&
            nCurrentIndex >= (nNextEnd = pHints->GetSortedByEnd(nEndIndex)->GetAnyEnd()))
            nEndIndex++;

        sal_Int32 nNextPos =
            ((nNextStart > nCurrentIndex) && (nNextStart < nNextEnd))
            ?   nNextStart  :   nNextEnd;
        if (nNextPos > nCurrentIndex)
        {
            o_rNextAttrPosition = nNextPos;
        }
    }
    return pPortion;
}

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 )
{
    sal_Int32 nMovePos = pUnoCursor->GetPointContentNode()->Len();

    if ((nEndPos >= 0) && (nEndPos < nMovePos))
    {
        nMovePos = nEndPos;
    }

    if ((nNextFrameIndex >= 0) && (nNextFrameIndex < nMovePos))
    {
        nMovePos = nNextFrameIndex;
    }

    if ((nNextPortionIndex >= 0) && (nNextPortionIndex < nMovePos))
    {
        nMovePos = nNextPortionIndex;
    }

    if ((nNextAttrIndex >= 0) && (nNextAttrIndex < nMovePos))
    {
        nMovePos = nNextAttrIndex;
    }

    if ((nNextMarkIndex >= 0) && (nNextMarkIndex < nMovePos))
    {
        nMovePos = nNextMarkIndex;
    }

    if (nMovePos > nCurrentIndex)
    {
        pUnoCursor->GetPoint()->SetContent( nMovePos );
    }
}

static void lcl_FillRedlineArray(
    SwDoc const & rDoc,
    SwUnoCursor const & rUnoCursor,
    SwXRedlinePortion_ImplList& rRedArr )
{
    const SwRedlineTable& rRedTable = rDoc.getIDocumentRedlineAccess().GetRedlineTable();
    const size_t nRedTableCount = rRedTable.size();

    if ( nRedTableCount <= 0 )
        return;

    const SwPosition* pStart = rUnoCursor.GetPoint();
    const SwNode& rOwnNode = pStart->GetNode();

    SwRedlineTable::size_type nRed = rDoc.getIDocumentRedlineAccess().GetRedlinePos(rOwnNode, RedlineType::Any);
    for(; nRed < nRedTableCount; ++nRed)
    {
        const SwRangeRedline* pRedline = rRedTable[nRed];
        auto [pRedStart, pRedEnd]= pRedline->StartEnd();
        if ( rOwnNode == pRedStart->GetNode() )
            rRedArr.insert( std::make_shared<SwXRedlinePortion_Impl>(
                pRedline, true ) );
        if( pRedline->HasMark() && pRedEnd->GetNode() == rOwnNode )
            rRedArr.insert( std::make_shared<SwXRedlinePortion_Impl>(
                pRedline, false ) );
    }
}

static void lcl_FillSoftPageBreakArray(
    SwUnoCursor const & rUnoCursor,
    SwSoftPageBreakList& rBreakArr )
{
    const SwTextNode *pTextNode =
        rUnoCursor.GetPoint()->GetNode().GetTextNode();
    if( pTextNode )
        pTextNode->fillSoftPageBreakList( rBreakArr );
}

static void lcl_ExportRedline(
    TextRangeList_t & rPortions,
    uno::Reference<SwXText> const& xParent,
    const SwUnoCursor * const pUnoCursor,
    SwXRedlinePortion_ImplList& rRedlineArr,
    const sal_Int32 nIndex)
{

    // We want this loop to iterate over all red lines in this
    // array. We will only insert the ones with index matches
    for ( SwXRedlinePortion_ImplList::iterator aIter = rRedlineArr.begin(), aEnd = rRedlineArr.end();
          aIter != aEnd; )
    {
        SwXRedlinePortion_ImplSharedPtr pPtr = *aIter;
        sal_Int32 nRealIndex = pPtr->getRealIndex();
        // If there are elements before nIndex, remove them
        if ( nIndex > nRealIndex )
            aIter = rRedlineArr.erase(aIter);
        // If the elements match, and them to the list
        else if ( nIndex == nRealIndex )
        {
            rPortions.push_back( new SwXRedlinePortion(
                        *pPtr->m_pRedline, pUnoCursor, xParent, pPtr->m_bStart));
            aIter = rRedlineArr.erase(aIter);
        }
        // If we've iterated past nIndex, exit the loop
        else
            break;
    }
}

static void lcl_ExportBkmAndRedline(
    TextRangeList_t & rPortions,
    uno::Reference<SwXText> 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,
    ExportBookmarkPass stage)
{
    if (!rBkmArr.empty())
        lcl_ExportBookmark(rPortions, xParent, pUnoCursor, rBkmArr, nIndex, rFramePositions, stage);

    if (stage == ExportBookmarkPass::before_frames)
        // Only exporting the start of some collapsed bookmarks: no export of
        // other arrays.
        return;

    if (!rRedlineArr.empty())
        lcl_ExportRedline(rPortions, xParent, pUnoCursor, rRedlineArr, nIndex);

    if (!rBreakArr.empty())
        lcl_ExportSoftPageBreak(rPortions, xParent, pUnoCursor, rBreakArr, nIndex);
}

/**
 * Exports all start annotation marks from rAnnotationStartArr into rPortions that have the same
 * start position as nIndex.
 *
 * @param rAnnotationStartArr the array of annotation marks. Consumed entries are removed.
 *
 * @param rFramePositions the list of positions where there is an at-char anchored frame.
 *
 * @param bOnlyFrame If true: export only the start of annotation marks which cover an at-char
 * anchored frame. If false: export everything else.
 */

static void lcl_ExportAnnotationStarts(
    TextRangeList_t & rPortions,
    uno::Reference<SwXText> const & xParent,
    const SwUnoCursor * const pUnoCursor,
    SwAnnotationStartPortion_ImplList& rAnnotationStartArr,
    const sal_Int32 nIndex,
    const o3tl::sorted_vector<sal_Int32>& rFramePositions,
    bool bOnlyFrame)
{
    for ( SwAnnotationStartPortion_ImplList::iterator aIter = rAnnotationStartArr.begin(), aEnd = rAnnotationStartArr.end();
          aIter != aEnd; )
    {
        SwAnnotationStartPortion_ImplSharedPtr pPtr = *aIter;
        if ( nIndex > pPtr->getIndex() )
        {
            aIter = rAnnotationStartArr.erase(aIter);
            continue;
        }
        if ( pPtr->getIndex() > nIndex )
        {
            break;
        }

        bool bFrameStart = rFramePositions.find(nIndex) != rFramePositions.end();
        if (bFrameStart || !bOnlyFrame)
        {
            rtl::Reference<SwXTextPortion> pPortion =
                new SwXTextPortion( pUnoCursor, xParent, PORTION_ANNOTATION );
            pPortion->SetTextField( pPtr->mxAnnotationField );
            rPortions.emplace_back(pPortion);

            aIter = rAnnotationStartArr.erase(aIter);
        }
        else
            ++aIter;
    }
}

/// Fills character positions from rFrames into rFramePositions.
static void lcl_ExtractFramePositions(FrameClientSortList_t& rFrames, sal_Int32 nCurrentIndex,
                                      o3tl::sorted_vector<sal_Int32>& rFramePositions)
{
    for (const auto& rFrame : rFrames)
    {
        if (rFrame.nIndex < nCurrentIndex)
            continue;

        if (rFrame.nIndex > nCurrentIndex)
            break;

        const auto pFrame = static_cast<const SwFrameFormat*>(rFrame.pFrameClient->GetRegisteredIn());
        if (!pFrame)
            continue;

        auto& rFormat = *const_cast<SwFrameFormat*>(pFrame);
        const SwFormatAnchor& rAnchor = rFormat.GetAnchor();
        if (!rAnchor.GetAnchorNode())
            continue;

        rFramePositions.insert(rAnchor.GetAnchorContentOffset());
    }
}

/**
 * Exports at-char anchored frames.
 *
 * @param i_rFrames the frames for this paragraph, frames at <= i_nCurrentIndex
 * are removed from the container.
 */

static sal_Int32 lcl_ExportFrames(
    TextRangeList_t & rPortions,
    uno::Reference<SwXText> const & i_xParent,
    SwUnoCursor const * const i_pUnoCursor,
    FrameClientSortList_t & i_rFrames,
    sal_Int32 const i_nCurrentIndex)
{
    // Ignore frames which are not exported, as we are exporting a selection
    // and they are anchored before the start of the selection.
    while (!i_rFrames.empty() && i_rFrames.front().nIndex < i_nCurrentIndex)
        i_rFrames.pop_front();

    // find first Frame in (sorted) i_rFrames at current position
    while (!i_rFrames.empty() && (i_rFrames.front().nIndex == i_nCurrentIndex))
    // do not check for i_nEnd here; this is done implicitly by lcl_MoveCursor
    {
        auto pFrame = static_cast<SwFrameFormat*>(i_rFrames.front().pFrameClient->GetRegisteredIn());
        if (pFrame) // Frame could be disposed
        {
            rtl::Reference<SwXTextPortion> pPortion = new SwXTextPortion(i_pUnoCursor, i_xParent, *pFrame );
            rPortions.emplace_back(pPortion);
        }
        i_rFrames.pop_front();
    }

    return !i_rFrames.empty() ? i_rFrames.front().nIndex : -1;
}

static sal_Int32 lcl_GetNextIndex(
    SwXBookmarkPortion_ImplList const & rBkmArr,
    SwXRedlinePortion_ImplList const & rRedlineArr,
    SwSoftPageBreakList const & rBreakArr )
{
    sal_Int32 nRet = -1;
    if(!rBkmArr.empty())
    {
        SwXBookmarkPortion_ImplSharedPtr pPtr = *rBkmArr.begin();
        nRet = pPtr->getIndex();
    }
    if(!rRedlineArr.empty())
    {
        SwXRedlinePortion_ImplSharedPtr pPtr = *rRedlineArr.begin();
        sal_Int32 nTmp = pPtr->getRealIndex();
        if(nRet < 0 || nTmp < nRet)
            nRet = nTmp;
    }
    if(!rBreakArr.empty())
    {
        if(nRet < 0 || *rBreakArr.begin() < nRet)
            nRet = *rBreakArr.begin();
    }
    return nRet;
};

static void lcl_CreatePortions(
        TextRangeList_t & i_rPortions,
        css::uno::Reference< SwXText > const & i_xParentText,
        SwUnoCursor * const pUnoCursor,
        FrameClientSortList_t & i_rFrames,
        const sal_Int32 i_nStartPos,
        const sal_Int32 i_nEndPos,
        bool bOnlyTextFields )
{
    if (!pUnoCursor)
        return;

    // set the start if a selection should be exported
    if ((i_nStartPos > 0) &&
        (pUnoCursor->Start()->GetContentIndex() != i_nStartPos))
    {
        pUnoCursor->DeleteMark();
        OSL_ENSURE(pUnoCursor->Start()->GetNode().GetTextNode() &&
            (i_nStartPos <= pUnoCursor->Start()->GetNode().GetTextNode()->
                        GetText().getLength()), "Incorrect start position" );
        // ??? should this be i_nStartPos - current position ?
        pUnoCursor->Right(i_nStartPos);
    }

    SwDoc& rDoc = pUnoCursor->GetDoc();

    std::deque<sal_Int32> FieldMarks;
    if (!bOnlyTextFields)
        lcl_FillFieldMarkArray(FieldMarks, *pUnoCursor, i_nStartPos);

    SwXBookmarkPortion_ImplList Bookmarks;
    if (!bOnlyTextFields)
        lcl_FillBookmarkArray(rDoc, *pUnoCursor, Bookmarks);

    SwXRedlinePortion_ImplList Redlines;
    if (!bOnlyTextFields)
        lcl_FillRedlineArray(rDoc, *pUnoCursor, Redlines);

    SwSoftPageBreakList SoftPageBreaks;
    if (!bOnlyTextFields)
        lcl_FillSoftPageBreakArray(*pUnoCursor, SoftPageBreaks);

    SwAnnotationStartPortion_ImplList AnnotationStarts;
    if (!bOnlyTextFields)
        lcl_FillAnnotationStartArray( rDoc, *pUnoCursor, AnnotationStarts );

    PortionStack_t PortionStack;
    PortionStack.push( PortionList_t(&i_rPortions, nullptr) );

    bool bAtEnd( false );
    while (!bAtEnd) // every iteration consumes at least current character!
    {
        if (pUnoCursor->HasMark())
        {
            pUnoCursor->Normalize(false);
            pUnoCursor->DeleteMark();
        }

        SwTextNode * const pTextNode = pUnoCursor->GetPointNode().GetTextNode();
        if (!pTextNode)
        {
            OSL_FAIL("lcl_CreatePortions: no TextNode - what now ?");
            return;
        }

        SwpHints * const pHints = pTextNode->GetpSwpHints();
        const sal_Int32 nCurrentIndex =
            pUnoCursor->GetPoint()->GetContentIndex();
        // this contains the portion which consumes the character in the
        // text at nCurrentIndex; i.e. it must be set _once_ per iteration
        rtl::Reference<SwXTextPortion> xRef;

        SwUnoCursorHelper::SelectPam(*pUnoCursor, true); // set mark

        // First remember the frame positions.
        o3tl::sorted_vector<sal_Int32> aFramePositions;
        lcl_ExtractFramePositions(i_rFrames, nCurrentIndex, aFramePositions);

        // Then export start of collapsed bookmarks which "cover" at-char
        // anchored frames.
        lcl_ExportBkmAndRedline( *PortionStack.top().first, i_xParentText,
            pUnoCursor, Bookmarks, Redlines, SoftPageBreaks, nCurrentIndex, aFramePositions, ExportBookmarkPass::before_frames );

        lcl_ExportAnnotationStarts(
            *PortionStack.top().first,
            i_xParentText,
            pUnoCursor,
            AnnotationStarts,
            nCurrentIndex,
            aFramePositions,
            /*bOnlyFrame=*/true );

        const sal_Int32 nFirstFrameIndex =
            lcl_ExportFrames( *PortionStack.top().first,
                i_xParentText, pUnoCursor, i_rFrames, nCurrentIndex);

        // Export ends of the previously started collapsed bookmarks + all
        // other bookmarks, redlines, etc.
        lcl_ExportBkmAndRedline( *PortionStack.top().first, i_xParentText,
            pUnoCursor, Bookmarks, Redlines, SoftPageBreaks, nCurrentIndex, aFramePositions, ExportBookmarkPass::after_frames );

        lcl_ExportAnnotationStarts(
            *PortionStack.top().first,
            i_xParentText,
            pUnoCursor,
            AnnotationStarts,
            nCurrentIndex,
            aFramePositions,
            /*bOnlyFrame=*/false );

        bool bCursorMoved( false );
        sal_Int32 nNextAttrIndex = -1;
        // #111716# the cursor must not move right at the
        //          end position of a selection!
        bAtEnd = ((i_nEndPos >= 0) && (nCurrentIndex >= i_nEndPos))
              || (nCurrentIndex >= pTextNode->Len());
        if (pHints)
        {
            // N.B.: side-effects nNextAttrIndex, bCursorMoved; may move cursor
            xRef = lcl_ExportHints(PortionStack, i_xParentText, pUnoCursor,
                        pHints, i_nStartPos, i_nEndPos, nCurrentIndex, bAtEnd,
                        bCursorMoved, nNextAttrIndex);
            if (PortionStack.empty())
            {
                OSL_FAIL("CreatePortions: stack underflow");
                return;
            }
        }

        if (!xRef.is() && !bCursorMoved)
        {
            if (!bAtEnd &&
                !FieldMarks.empty() && (FieldMarks.front() == nCurrentIndex))
            {
                // moves cursor
                xRef = lcl_ExportFieldMark(i_xParentText, pUnoCursor, pTextNode);
                FieldMarks.pop_front();
            }
        }
        else
        {
            OSL_ENSURE(FieldMarks.empty() ||
                   (FieldMarks.front() != nCurrentIndex),
                   "fieldmark and hint with CH_TXTATR at same pos?");
        }

        if (!bAtEnd && !xRef.is() && !bCursorMoved)
        {
            const sal_Int32 nNextPortionIndex =
                lcl_GetNextIndex(Bookmarks, Redlines, SoftPageBreaks);

            sal_Int32 nNextMarkIndex = ( !FieldMarks.empty() ? FieldMarks.front() : -1 );
            if ( !AnnotationStarts.empty()
                 && ( nNextMarkIndex == -1
                      || (*AnnotationStarts.begin())->getIndex() < nNextMarkIndex ) )
            {
                nNextMarkIndex = (*AnnotationStarts.begin())->getIndex();
            }

            lcl_MoveCursor(
                pUnoCursor,
                nCurrentIndex,
                nFirstFrameIndex,
                nNextPortionIndex,
                nNextAttrIndex,
                nNextMarkIndex,
                i_nEndPos );

            xRef = new SwXTextPortion(pUnoCursor, i_xParentText, PORTION_TEXT);
        }
        else if (bAtEnd && !xRef.is() && !pTextNode->Len())
        {
            // special case: for an empty paragraph, we better put out a
            // text portion because there may be a hyperlink attribute
            xRef = new SwXTextPortion(pUnoCursor, i_xParentText, PORTION_TEXT);
        }
        else if (bAtEnd && !xRef.is() && pHints)
        {
            // See if there is an empty autofmt at the paragraph end. If so, export it, since that
            // affects the formatting of number portions.
            for (size_t i = 0; i < pHints->Count(); ++i)
            {
                const SwTextAttr* pHint = pHints->GetSortedByEnd(i);
                if (pHint->GetStart() < pTextNode->Len())
                {
                    break;
                }
                if (pHint->Which() == RES_TXTATR_AUTOFMT && pHint->GetEnd()
                    && pHint->GetStart() == *pHint->GetEnd()
                    && pHint->GetStart() == pTextNode->Len())
                {
                    xRef = new SwXTextPortion(pUnoCursor, i_xParentText, PORTION_TEXT);
                    break;
                }
            }
        }

        if (xRef.is())
        {
            PortionStack.top().first->push_back(xRef);
        }
    }

    OSL_ENSURE((PortionStack.size() == 1) && !PortionStack.top().second,
            "CreatePortions: stack error" );
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=93 H=95 G=93

¤ Dauer der Verarbeitung: 0.8 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge