Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/editeng/source/editeng/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 32 kB image not shown  

Quelle  ContentNode.cxx   Sprache: C

 
/* -*- 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 <ContentNode.hxx>
#include <editeng/tstpitem.hxx>
#include "impedit.hxx"

#include <rtl/ustrbuf.hxx>
#include <osl/diagnose.h>
#include <libxml/xmlwriter.h>

#include <memory>
#include <set>

ContentNode::ContentNode( SfxItemPool& rPool )
    : maContentAttribs( rPool )
{
}

ContentNode::ContentNode( const OUString& rStr, const ContentAttribs& rContentAttribs )
    : maString(rStr)
    , maContentAttribs(rContentAttribs)
{
}

void ContentNode::ExpandAttribs( sal_Int32 nIndex, sal_Int32 nNew )
{
    if ( !nNew )
        return;

#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
    CharAttribList::DbgCheckAttribs(maCharAttribList);
#endif

    // Since features are treated differently than normal character attributes,
    // but can also affect the order of the start list.    // In every if ...,  in the next (n) opportunities due to bFeature or
    // an existing special case, must (n-1) opportunities be provided with
    // bResort. The most likely possibility receives no bResort, so that is
    // not sorted anew when all attributes are the same.
    bool bResort = false;
    bool bExpandedEmptyAtIndexNull = false;

    std::size_t nAttr = 0;
    CharAttribList::AttribsType& rAttribs = maCharAttribList.GetAttribs();
    EditCharAttrib* pAttrib = GetAttrib(rAttribs, nAttr);
    while ( pAttrib )
    {
        if ( pAttrib->GetEnd() >= nIndex )
        {
            // Move all attributes behind the insertion point...
            if ( pAttrib->GetStart() > nIndex )
            {
                pAttrib->MoveForward( nNew );
            }
            // 0: Expand empty attribute, if at insertion point
            else if (pAttrib->IsEmpty() && pAttrib->IsExpandable())
            {
                // Do not check Index, an empty one could only be there
                // When later checking it anyhow:
                //   Special case: Start == 0; AbsLen == 1, nNew = 1
                // => Expand, because of paragraph break!
                // Start <= nIndex, End >= nIndex => Start=End=nIndex!
//              if ( pAttrib->GetStart() == nIndex )
                pAttrib->Expand( nNew );
                bResort = true;
                if ( pAttrib->GetStart() == 0 )
                    bExpandedEmptyAtIndexNull = true;
            }
            // 1: Attribute starts before, goes to index ...
            else if ( pAttrib->GetEnd() == nIndex ) // Start must be before
            {
                // Only expand when there is no feature
                // and if not in exclude list!
                // Otherwise, a UL will go on until a new ULDB, expanding both
//              if ( !pAttrib->IsFeature() && !rExclList.FindAttrib( pAttrib->Which() ) )
                if (pAttrib->IsExpandable()
                    && !maCharAttribList.FindEmptyAttrib(pAttrib->Which(), nIndex))
                {
                    if ( !pAttrib->IsEdge() )
                        pAttrib->Expand( nNew );
                }
                else
                    bResort = true;
            }
            // 2: Attribute starts before, goes past the Index...
            else if ( ( pAttrib->GetStart() < nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
            {
                DBG_ASSERT( !pAttrib->IsFeature(), "Large Feature?!" );
                pAttrib->Expand( nNew );
            }
            // 3: Attribute starts on index...
            else if ( pAttrib->GetStart() == nIndex )
            {
                if (!pAttrib->IsExpandable())
                {
                    pAttrib->MoveForward( nNew );
                    bResort = true;
                }
                else
                {
                    bool bExpand = false;
                    if ( nIndex == 0 )
                    {
                        bExpand = true;
                        if( bExpandedEmptyAtIndexNull )
                        {
                            // Check if this kind of attribute was empty and expanded here...
                            sal_uInt16 nW = pAttrib->GetItem()->Which();
                            for ( std::size_t nA = 0; nA < nAttr; nA++ )
                            {
                                const EditCharAttrib& r = *maCharAttribList.GetAttribs()[nA];
                                if ( ( r.GetStart() == 0 ) && ( r.GetItem()->Which() == nW ) )
                                {
                                    bExpand = false;
                                    break;
                                }
                            }

                        }
                    }
                    if ( bExpand )
                    {
                        pAttrib->Expand( nNew );
                        bResort = true;
                    }
                    else
                    {
                        pAttrib->MoveForward( nNew );
                    }
                }
            }
        }

        if ( pAttrib->IsEdge() )
            pAttrib->SetEdge(false);

        DBG_ASSERT( !pAttrib->IsFeature() || ( pAttrib->GetLen() == 1 ), "Expand: FeaturesLen != 1" );

        DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Expand: Attribute distorted!" );
        DBG_ASSERT( ( pAttrib->GetEnd() <= Len() ), "Expand: Attribute larger than paragraph!" );
        if ( pAttrib->IsEmpty() )
        {
            OSL_FAIL( "Empty Attribute after ExpandAttribs?" );
            bResort = true;
            rAttribs.erase(rAttribs.begin()+nAttr);
        }
        else
        {
            ++nAttr;
        }
        pAttrib = GetAttrib(rAttribs, nAttr);
    }

    if ( bResort )
        maCharAttribList.ResortAttribs();

    if (mpWrongList)
    {
        bool bSep = ( maString[ nIndex ] == ' ' ) || IsFeature( nIndex );
        mpWrongList->TextInserted( nIndex, nNew, bSep );
    }

#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
    CharAttribList::DbgCheckAttribs(maCharAttribList);
#endif
}

void ContentNode::CollapseAttribs( sal_Int32 nIndex, sal_Int32 nDeleted )
{
    if ( !nDeleted )
        return;

#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
    CharAttribList::DbgCheckAttribs(maCharAttribList);
#endif

    // Since features are treated differently than normal character attributes,
    // but can also affect the order of the start list
    bool bResort = false;
    sal_Int32 nEndChanges = nIndex+nDeleted;

    std::size_t nAttr = 0;
    CharAttribList::AttribsType& rAttribs = maCharAttribList.GetAttribs();
    EditCharAttrib* pAttrib = GetAttrib(rAttribs, nAttr);
    while ( pAttrib )
    {
        bool bDelAttr = false;
        if ( pAttrib->GetEnd() >= nIndex )
        {
            // Move all Attribute behind the insert point...
            if ( pAttrib->GetStart() >= nEndChanges )
            {
                pAttrib->MoveBackward( nDeleted );
            }
            // 1. Delete Internal attributes...
            else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() <= nEndChanges ) )
            {
                // Special case: Attribute covers the area exactly
                // => keep as empty Attribute.
                if ( !pAttrib->IsFeature() && ( pAttrib->GetStart() == nIndex ) && ( pAttrib->GetEnd() == nEndChanges ) )
                {
                    pAttrib->GetEnd() = nIndex; // empty
                    bResort = true;
                }
                else
                    bDelAttr = true;
            }
            // 2. Attribute starts earlier, ends inside or behind it ...
            else if ( ( pAttrib->GetStart() <= nIndex ) && ( pAttrib->GetEnd() > nIndex ) )
            {
                DBG_ASSERT( !pAttrib->IsFeature(), "Collapsing Feature!" );
                if ( pAttrib->GetEnd() <= nEndChanges ) // ends inside
                    pAttrib->GetEnd() = nIndex;
                else
                    pAttrib->Collaps( nDeleted );       // ends behind
            }
            // 3. Attribute starts inside, ending behind ...
            else if ( ( pAttrib->GetStart() >= nIndex ) && ( pAttrib->GetEnd() > nEndChanges ) )
            {
                // Features not allowed to expand!
                if ( pAttrib->IsFeature() )
                {
                    pAttrib->MoveBackward( nDeleted );
                    bResort = true;
                }
                else
                {
                    pAttrib->GetStart() = nEndChanges;
                    pAttrib->MoveBackward( nDeleted );
                }
            }
        }
        DBG_ASSERT( !pAttrib->IsFeature() || ( pAttrib->GetLen() == 1 ), "Expand: FeaturesLen != 1" );

        DBG_ASSERT( pAttrib->GetStart() <= pAttrib->GetEnd(), "Collapse: Attribute distorted!" );
        DBG_ASSERT( ( pAttrib->GetEnd() <= Len()) || bDelAttr, "Collapse: Attribute larger than paragraph!" );
        if ( bDelAttr )
        {
            bResort = true;
            rAttribs.erase(rAttribs.begin()+nAttr);
        }
        else
        {
            if ( pAttrib->IsEmpty() )
                maCharAttribList.SetHasEmptyAttribs(true);
            nAttr++;
        }

        pAttrib = GetAttrib(rAttribs, nAttr);
    }

    if ( bResort )
        maCharAttribList.ResortAttribs();

    if (mpWrongList)
        mpWrongList->TextDeleted(nIndex, nDeleted);

#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
    CharAttribList::DbgCheckAttribs(maCharAttribList);
#endif
}

void ContentNode::CopyAndCutAttribs( ContentNode* pPrevNode, SfxItemPool& rPool, bool bKeepEndingAttribs )
{
    assert(pPrevNode);

#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
    CharAttribList::DbgCheckAttribs(maCharAttribList);
    CharAttribList::DbgCheckAttribs(pPrevNode->maCharAttribList);
#endif

    sal_Int32 nCut = pPrevNode->Len();

    std::size_t nAttr = 0;
    CharAttribList::AttribsType& rPrevAttribs = pPrevNode->GetCharAttribs().GetAttribs();
    EditCharAttrib* pAttrib = GetAttrib(rPrevAttribs, nAttr);
    while ( pAttrib )
    {
        if ( pAttrib->GetEnd() < nCut )
        {
            // remain unchanged...
            nAttr++;
        }
        else if ( pAttrib->GetEnd() == nCut )
        {
            // must be copied as an empty attributes.
            if ( bKeepEndingAttribs && !pAttrib->IsFeature() && !maCharAttribList.FindAttrib( pAttrib->GetItem()->Which(), 0 ) )
            {
                EditCharAttrib* pNewAttrib = MakeCharAttrib( rPool, *(pAttrib->GetItem()), 0, 0 );
                assert(pNewAttrib);
                maCharAttribList.InsertAttrib( pNewAttrib );
            }
            nAttr++;
        }
        else if ( pAttrib->IsInside( nCut ) || ( !nCut && !pAttrib->GetStart() && !pAttrib->IsFeature() ) )
        {
            // If cut is done right at the front then the attribute must be
            // kept! Has to be copied and changed.
            EditCharAttrib* pNewAttrib = MakeCharAttrib( rPool, *(pAttrib->GetItem()), 0, pAttrib->GetEnd()-nCut );
            assert(pNewAttrib);
            maCharAttribList.InsertAttrib( pNewAttrib );
            pAttrib->GetEnd() = nCut;
            nAttr++;
        }
        else
        {
            // Move all attributes in the current node (this)
            CharAttribList::AttribsType::iterator it = rPrevAttribs.begin() + nAttr;
            maCharAttribList.InsertAttrib(it->release());
            rPrevAttribs.erase(it);
            pAttrib->MoveBackward( nCut );
        }
        pAttrib = GetAttrib(rPrevAttribs, nAttr);
    }

#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
    CharAttribList::DbgCheckAttribs(maCharAttribList);
    CharAttribList::DbgCheckAttribs(pPrevNode->maCharAttribList);
#endif
}

void ContentNode::AppendAttribs( ContentNode* pNextNode )
{
    assert(pNextNode);

    sal_Int32 nNewStart = maString.getLength();

#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
    CharAttribList::DbgCheckAttribs(maCharAttribList);
    CharAttribList::DbgCheckAttribs(pNextNode->maCharAttribList);
#endif

    std::size_t nAttr = 0;
    CharAttribList::AttribsType& rNextAttribs = pNextNode->GetCharAttribs().GetAttribs();
    EditCharAttrib* pAttrib = GetAttrib(rNextAttribs, nAttr);
    while ( pAttrib )
    {
        // Move all attributes in the current node (this)
        bool bMelted = false;
        if ( ( pAttrib->GetStart() == 0 ) && ( !pAttrib->IsFeature() ) )
        {
            // Attributes can possibly be summarized as:
            std::size_t nTmpAttr = 0;
            EditCharAttrib* pTmpAttrib = GetAttrib( maCharAttribList.GetAttribs(), nTmpAttr );
            while ( !bMelted && pTmpAttrib )
            {
                ++nTmpAttr;
                if ( pTmpAttrib->GetEnd() == nNewStart )
                {
                    if (pTmpAttrib->Which() == pAttrib->Which())
                    {
                        // prevent adding 2 0-length attributes at same position
                        if ((*(pTmpAttrib->GetItem()) == *(pAttrib->GetItem()))
                                || (0 == pAttrib->GetLen()))
                        {
                            pTmpAttrib->GetEnd() =
                                pTmpAttrib->GetEnd() + pAttrib->GetLen();
                            rNextAttribs.erase(rNextAttribs.begin()+nAttr);
                            // Unsubscribe from the pool?!
                            bMelted = true;
                        }
                        else if (0 == pTmpAttrib->GetLen())
                        {
                            --nTmpAttr; // to cancel earlier increment...
                            maCharAttribList.Remove(nTmpAttr);
                        }
                    }
                }
                pTmpAttrib = GetAttrib( maCharAttribList.GetAttribs(), nTmpAttr );
            }
        }

        if ( !bMelted )
        {
            pAttrib->GetStart() = pAttrib->GetStart() + nNewStart;
            pAttrib->GetEnd() = pAttrib->GetEnd() + nNewStart;
            CharAttribList::AttribsType::iterator it = rNextAttribs.begin() + nAttr;
            maCharAttribList.InsertAttrib(it->release());
            rNextAttribs.erase(it);
        }
        pAttrib = GetAttrib(rNextAttribs, nAttr);
    }
    // For the Attributes that just moved over:
    rNextAttribs.clear();

#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
    CharAttribList::DbgCheckAttribs(maCharAttribList);
    CharAttribList::DbgCheckAttribs(pNextNode->maCharAttribList);
#endif
}

void ContentNode::CreateDefFont()
{
    // First use the information from the style ...
    SfxStyleSheet* pS = maContentAttribs.GetStyleSheet();
    if ( pS )
        CreateFont( GetCharAttribs().GetDefFont(), pS->GetItemSet() );

    // ... then iron out the hard paragraph formatting...
    CreateFont( GetCharAttribs().GetDefFont(),
        GetContentAttribs().GetItems(), pS == nullptr );
}

void ContentNode::SetStyleSheet( SfxStyleSheet* pS, const SvxFont& rFontFromStyle )
{
    maContentAttribs.SetStyleSheet( pS );


    // First use the information from the style ...
    GetCharAttribs().GetDefFont() = rFontFromStyle;
    // ... then iron out the hard paragraph formatting...
    CreateFont( GetCharAttribs().GetDefFont(),
                GetContentAttribs().GetItems(), pS == nullptr );
}

void ContentNode::SetStyleSheet( SfxStyleSheet* pS, bool bRecalcFont )
{
    maContentAttribs.SetStyleSheet( pS );
    if ( bRecalcFont )
        CreateDefFont();
}

bool ContentNode::IsFeature( sal_Int32 nPos ) const
{
    return maString[nPos] == CH_FEATURE;
}

sal_Int32 ContentNode::Len() const
{
    return maString.getLength();
}

sal_Int32 ContentNode::GetExpandedLen() const
{
    sal_Int32 nLen = maString.getLength();

    // Fields can be longer than the placeholder in the Node
    const CharAttribList::AttribsType& rAttrs = GetCharAttribs().GetAttribs();
    for (sal_Int32 nAttr = rAttrs.size(); nAttr; )
    {
        const EditCharAttrib& rAttr = *rAttrs[--nAttr];
        if (rAttr.Which() == EE_FEATURE_FIELD)
        {
            nLen += static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength();
            --nLen; // Standalone, to avoid corner cases when previous getLength() returns 0
        }
    }

    return nLen;
}

OUString ContentNode::GetExpandedText(sal_Int32 nStartPos, sal_Int32 nEndPos) const
{
    if ( nEndPos < 0 || nEndPos > Len() )
        nEndPos = Len();

    DBG_ASSERT( nStartPos <= nEndPos, "Start and End reversed?" );

    sal_Int32 nIndex = nStartPos;
    OUStringBuffer aStr(256);
    const EditCharAttrib* pNextFeature = GetCharAttribs().FindFeature( nIndex );
    while ( nIndex < nEndPos )
    {
        sal_Int32 nEnd = nEndPos;
        if ( pNextFeature && ( pNextFeature->GetStart() < nEnd ) )
            nEnd = pNextFeature->GetStart();
        else
            pNextFeature = nullptr;   // Feature does not interest the below

        DBG_ASSERT( nEnd >= nIndex, "End in front of the index?" );
        //!! beware of sub string length  of -1
        if (nEnd > nIndex)
            aStr.append( GetString().subView(nIndex, nEnd - nIndex) );

        if ( pNextFeature )
        {
            switch ( pNextFeature->GetItem()->Which() )
            {
                case EE_FEATURE_TAB:    aStr.append( "\t" );
                break;
                case EE_FEATURE_LINEBR: aStr.append( "\x0A" );
                break;
                case EE_FEATURE_FIELD:
                    aStr.append( static_cast<const EditCharAttribField*>(pNextFeature)->GetFieldValue() );
                break;
                default:    OSL_FAIL( "What feature?" );
            }
            pNextFeature = GetCharAttribs().FindFeature( ++nEnd );
        }
        nIndex = nEnd;
    }
    return aStr.makeStringAndClear();
}

void ContentNode::UnExpandPosition( sal_Int32 &rPos, bool bBiasStart )
{
    sal_Int32 nOffset = 0;

    const CharAttribList::AttribsType& rAttrs = GetCharAttribs().GetAttribs();
    for (size_t nAttr = 0; nAttr < rAttrs.size(); ++nAttr )
    {
        const EditCharAttrib& rAttr = *rAttrs[nAttr];
        assert (!(nAttr < rAttrs.size() - 1) ||
                rAttrs[nAttr]->GetStart() <= rAttrs[nAttr + 1]->GetStart());

        nOffset = rAttr.GetStart();

        if (nOffset >= rPos) // happens after the position
            return;

        if (rAttr.Which() == EE_FEATURE_FIELD)
        {
            sal_Int32 nChunk = static_cast<const EditCharAttribField&>(rAttr).GetFieldValue().getLength();
            nChunk--; // Character representing the field in the string

            if (nOffset + nChunk >= rPos) // we're inside the field
            {
                if (bBiasStart)
                    rPos = rAttr.GetStart();
                else
                    rPos = rAttr.GetEnd();
                return;
            }
            // Adjust for the position
            rPos -= nChunk;
        }
    }
    assert (rPos <= Len());
}

/*
 * Fields are represented by a single character in the underlying string
 * and/or selection, however, they can be expanded to the full value of
 * the field. When we're dealing with selection / offsets however we need
 * to deal in character positions inside the real (unexpanded) string.
 * This method maps us back to character offsets.
 */

void ContentNode::UnExpandPositions( sal_Int32 &rStartPos, sal_Int32 &rEndPos )
{
    UnExpandPosition( rStartPos, true );
    UnExpandPosition( rEndPos, false );
}

void ContentNode::SetChar(sal_Int32 nPos, sal_Unicode c)
{
    maString = maString.replaceAt(nPos, 1, rtl::OUStringChar(c));
}

void ContentNode::Insert(const OUString& rStr, sal_Int32 nPos)
{
    maString = maString.replaceAt(nPos, 0, rStr);
}

void ContentNode::Append(std::u16string_view rStr)
{
    maString += rStr;
}

void ContentNode::Erase(sal_Int32 nPos)
{
    maString = maString.copy(0, nPos);
}

void ContentNode::Erase(sal_Int32 nPos, sal_Int32 nCount)
{
    maString = maString.replaceAt(nPos, nCount, u"");
}

OUString ContentNode::Copy(sal_Int32 nPos) const
{
    return maString.copy(nPos);
}

OUString ContentNode::Copy(sal_Int32 nPos, sal_Int32 nCount) const
{
    return maString.copy(nPos, nCount);
}

sal_Unicode ContentNode::GetChar(sal_Int32 nPos) const
{
    return maString[nPos];
}

void ContentNode::EnsureWrongList()
{
    if (!mpWrongList)
        CreateWrongList();
}

WrongList* ContentNode::GetWrongList()
{
    return mpWrongList.get();
}

const WrongList* ContentNode::GetWrongList() const
{
    return mpWrongList.get();
}

void ContentNode::SetWrongList( WrongList* p )
{
    mpWrongList.reset(p);
}

void ContentNode::CreateWrongList()
{
    SAL_WARN_IF( mpWrongList && !mpWrongList->empty(), "editeng""WrongList already exist!");
    if (!mpWrongList || !mpWrongList->empty())
        mpWrongList.reset(new WrongList);
}

void ContentNode::DestroyWrongList()
{
    mpWrongList.reset();
}

void ContentNode::dumpAsXml(xmlTextWriterPtr pWriter) const
{
    (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ContentNode"));
    (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("maString"), BAD_CAST(maString.toUtf8().getStr()));
    maContentAttribs.dumpAsXml(pWriter);
    maCharAttribList.dumpAsXml(pWriter);
    (void)xmlTextWriterEndElement(pWriter);
}

void ContentNode::checkAndDeleteEmptyAttribs() const
{
    // Delete empty attributes, but only if paragraph is not empty!
    if (GetCharAttribs().HasEmptyAttribs() && Len())
    {
        const_cast<ContentNode*>(this)->GetCharAttribs().DeleteEmptyAttribs();
    }
}

ContentAttribs::ContentAttribs( SfxItemPool& rPool )
    : maAttribSet(SfxItemSet::makeFixedSfxItemSet<EE_PARA_START, EE_CHAR_END>(rPool))
{
}

SvxTabStop ContentAttribs::FindTabStop( sal_Int32 nCurPos, sal_uInt16 nDefTab )
{
    const SvxTabStopItem& rTabs = GetItem( EE_PARA_TABS );
    for ( sal_uInt16 i = 0; i < rTabs.Count(); i++ )
    {
        const SvxTabStop& rTab = rTabs[i];
        if ( rTab.GetTabPos() > nCurPos  )
            return rTab;
    }

    // if there's a default tab size defined for this item use that instead
    if (rTabs.GetDefaultDistance())
        nDefTab = rTabs.GetDefaultDistance();

    // Determine DefTab ...
    SvxTabStop aTabStop;
    const sal_Int32 x = nCurPos / nDefTab + 1;
    aTabStop.GetTabPos() = nDefTab * x;
    return aTabStop;
}

void ContentAttribs::SetStyleSheet( SfxStyleSheet* pS )
{
    bool bStyleChanged = ( mpStyle != pS );
    mpStyle = pS;
    // Only when other style sheet, not when current style sheet modified
    if ( !(mpStyle && bStyleChanged) )
        return;

    // Selectively remove the attributes from the paragraph formatting
    // which are specified in the style, so that the attributes of the
    // style can have an affect.
    const SfxItemSet& rStyleAttribs = mpStyle->GetItemSet();
    for ( sal_uInt16 nWhich = EE_PARA_START; nWhich <= EE_CHAR_END; nWhich++ )
    {
        // Don't change bullet on/off
        if ( ( nWhich != EE_PARA_BULLETSTATE ) && ( rStyleAttribs.GetItemState( nWhich ) == SfxItemState::SET ) )
            maAttribSet.ClearItem( nWhich );
    }
}

const SfxPoolItem& ContentAttribs::GetItem( sal_uInt16 nWhich ) const
{
    // Hard paragraph attributes take precedence!
    const SfxItemSet* pTakeFrom = &maAttribSet;
    if ( mpStyle && ( maAttribSet.GetItemState( nWhich, false ) != SfxItemState::SET  ) )
        pTakeFrom = &mpStyle->GetItemSet();

    return pTakeFrom->Get( nWhich );
}

bool ContentAttribs::HasItem( sal_uInt16 nWhich ) const
{
    bool bHasItem = false;
    if ( maAttribSet.GetItemState( nWhich, false ) == SfxItemState::SET  )
        bHasItem = true;
    else if ( mpStyle && mpStyle->GetItemSet().GetItemState( nWhich ) == SfxItemState::SET )
        bHasItem = true;

    return bHasItem;
}

void ContentAttribs::dumpAsXml(xmlTextWriterPtr pWriter) const
{
    (void)xmlTextWriterStartElement(pWriter, BAD_CAST("ContentAttribs"));
    (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("style"), "%s", mpStyle->GetName().toUtf8().getStr());
    maAttribSet.dumpAsXml(pWriter);
    (void)xmlTextWriterEndElement(pWriter);
}

namespace {

struct LessByStart
{
    bool operator() (const std::unique_ptr<EditCharAttrib>& left, const std::unique_ptr<EditCharAttrib>& right) const
    {
        return left->GetStart() < right->GetStart();
    }
};

}

void CharAttribList::InsertAttrib( EditCharAttrib* pAttrib )
{
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// optimize: binary search?    !
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    // Maybe just simply iterate backwards:
    // The most common and critical case: Attributes are already sorted
    // (InsertTextObject!) binary search would not be optimal here.
    // => Would bring something!

    const sal_Int32 nStart = pAttrib->GetStart(); // may be better for Comp.Opt.

#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
    CharAttribList::DbgCheckAttribs(*this);
#endif

    if ( pAttrib->IsEmpty() )
        mbHasEmptyAttribs = true;

    bool bInsert(true);
    for (sal_Int32 i = 0, n = maAttribs.size(); i < n; ++i)
    {
        const EditCharAttrib& rCurAttrib = *maAttribs[i];
        if (rCurAttrib.GetStart() > nStart)
        {
            maAttribs.insert(maAttribs.begin()+i, std::unique_ptr<EditCharAttrib>(pAttrib));
            bInsert = false;
            break;
        }
    }

    if (bInsert) maAttribs.push_back(std::unique_ptr<EditCharAttrib>(pAttrib));

#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
    CharAttribList::DbgCheckAttribs(*this);
#endif
}

void CharAttribList::ResortAttribs()
{
    std::sort(maAttribs.begin(), maAttribs.end(), LessByStart());

#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
    CharAttribList::DbgCheckAttribs(*this);
#endif
}

void CharAttribList::OptimizeRanges()
{
#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
    CharAttribList::DbgCheckAttribs(*this);
#endif
    for (sal_Int32 i = 0; i < static_cast<sal_Int32>(maAttribs.size()); ++i)
    {
        EditCharAttrib& rAttr = *maAttribs[i];
        for (sal_Int32 nNext = i+1; nNext < static_cast<sal_Int32>(maAttribs.size()); ++nNext)
        {
            EditCharAttrib& rNext = *maAttribs[nNext];
            if (!rAttr.IsFeature() && rNext.GetStart() == rAttr.GetEnd() && rNext.Which() == rAttr.Which())
            {
                if (*rNext.GetItem() == *rAttr.GetItem())
                {
                    rAttr.GetEnd() = rNext.GetEnd();
                    maAttribs.erase(maAttribs.begin()+nNext);
                }
                break;  // only 1 attr with same which can start here.
            }
            else if (rNext.GetStart() > rAttr.GetEnd())
            {
                break;
            }
        }
    }
#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
    CharAttribList::DbgCheckAttribs(*this);
#endif
}

sal_Int32 CharAttribList::Count() const
{
    return maAttribs.size();
}

const EditCharAttrib* CharAttribList::FindAttrib( sal_uInt16 nWhich, sal_Int32 nPos ) const
{
    // Backwards, if one ends where the next starts.
    // => The starting one is the valid one ...
    AttribsType::const_reverse_iterator it = std::find_if(maAttribs.rbegin(), maAttribs.rend(),
        [&nWhich, &nPos](const AttribsType::value_type& rxAttr) {
            return rxAttr->Which() == nWhich && rxAttr->IsIn(nPos); });
    if (it != maAttribs.rend())
    {
        const EditCharAttrib& rAttr = **it;
        return &rAttr;
    }
    return nullptr;
}

EditCharAttrib* CharAttribList::FindAttrib( sal_uInt16 nWhich, sal_Int32 nPos )
{
    // Backwards, if one ends where the next starts.
    // => The starting one is the valid one ...
    AttribsType::reverse_iterator it = std::find_if(maAttribs.rbegin(), maAttribs.rend(),
        [&nWhich, &nPos](AttribsType::value_type& rxAttr) {
            return rxAttr->Which() == nWhich && rxAttr->IsIn(nPos); });
    if (it != maAttribs.rend())
    {
        EditCharAttrib& rAttr = **it;
        return &rAttr;
    }
    return nullptr;
}

EditCharAttrib* CharAttribList::FindAttribRightOpen( sal_uInt16 nWhich, sal_Int32 nPos )
{
    AttribsType::reverse_iterator it = std::find_if(maAttribs.rbegin(), maAttribs.rend(),
        [&nWhich, &nPos](AttribsType::value_type& rxAttr) {
            return rxAttr->Which() == nWhich && rxAttr->IsInLeftClosedRightOpen(nPos); });
    if (it != maAttribs.rend())
    {
        EditCharAttrib& rAttr = **it;
        return &rAttr;
    }
    return nullptr;
}

const EditCharAttrib* CharAttribList::FindNextAttrib( sal_uInt16 nWhich, sal_Int32 nFromPos ) const
{
    assert(nWhich);
    for (auto const& attrib : maAttribs)
    {
        const EditCharAttrib& rAttr = *attrib;
        if (rAttr.GetStart() >= nFromPos && rAttr.Which() == nWhich)
            return &rAttr;
    }
    return nullptr;
}

bool CharAttribList::HasAttrib( sal_Int32 nStartPos, sal_Int32 nEndPos ) const
{
    return std::any_of(maAttribs.rbegin(), maAttribs.rend(),
        [&nStartPos, &nEndPos](const AttribsType::value_type& rxAttr) {
            return rxAttr->GetStart() < nEndPos && rxAttr->GetEnd() > nStartPos; });
}


namespace {

class FindByAddress
{
    const EditCharAttrib* mpAttr;
public:
    explicit FindByAddress(const EditCharAttrib* p) : mpAttr(p) {}
    bool operator() (const std::unique_ptr<EditCharAttrib>& r) const
    {
        return r.get() == mpAttr;
    }
};

}

void CharAttribList::Remove(const EditCharAttrib* p)
{
    AttribsType::iterator it = std::find_if(maAttribs.begin(), maAttribs.end(), FindByAddress(p));
    if (it != maAttribs.end())
        maAttribs.erase(it);
}

void CharAttribList::Remove(sal_Int32 nPos)
{
    maAttribs.erase(maAttribs.begin()+nPos);
}

void CharAttribList::SetHasEmptyAttribs(bool b)
{
    mbHasEmptyAttribs = b;
}

bool CharAttribList::HasBoundingAttrib( sal_Int32 nBound ) const
{
    // Backwards, if one ends where the next starts.
    // => The starting one is the valid one ...
    AttribsType::const_reverse_iterator it = maAttribs.rbegin(), itEnd = maAttribs.rend();
    for (; it != itEnd; ++it)
    {
        const EditCharAttrib& rAttr = **it;
        if (rAttr.GetEnd() < nBound)
            return false;

        if (rAttr.GetStart() == nBound || rAttr.GetEnd() == nBound)
            return true;
    }
    return false;
}

EditCharAttrib* CharAttribList::FindEmptyAttrib( sal_uInt16 nWhich, sal_Int32 nPos )
{
    if ( !mbHasEmptyAttribs )
        return nullptr;

    for (const std::unique_ptr<EditCharAttrib>& rAttr : maAttribs)
    {
        if (rAttr->GetStart() == nPos && rAttr->GetEnd() == nPos && rAttr->Which() == nWhich)
            return rAttr.get();
    }
    return nullptr;
}

namespace
{

class FindByStartPos
{
    sal_Int32 mnPos;
public:
    explicit FindByStartPos(sal_Int32 nPos)
        : mnPos(nPos)
    {}

    bool operator() (std::unique_ptr<EditCharAttrib> const& pCharAttrib) const
    {
        return pCharAttrib->GetStart() >= mnPos;
    }
};

}

const EditCharAttrib* CharAttribList::FindFeature( sal_Int32 nPos ) const
{
    // First, find the first attribute that starts at or after specified position.
    AttribsType::const_iterator iterator =
        std::find_if(maAttribs.begin(), maAttribs.end(), FindByStartPos(nPos));

    if (iterator == maAttribs.end())
    {
        // All attributes are before the specified position.
        return nullptr;
    }

    // And find the first attribute with feature.
    iterator = std::find_if(iterator, maAttribs.end(), [](const std::unique_ptr<EditCharAttrib>& aAttrib) {
        return aAttrib->IsFeature();
    });

    if (iterator == maAttribs.end())
    {
        // Couldn't find the feature
        return nullptr;
    }

    // Found
    return iterator->get();
}

void CharAttribList::DeleteEmptyAttribs()
{
    std::erase_if(maAttribs, [](const std::unique_ptr<EditCharAttrib>& aAttrib) { return aAttrib->IsEmpty(); } );
    mbHasEmptyAttribs = false;
}

#if OSL_DEBUG_LEVEL > 0 && !defined NDEBUG
void CharAttribList::DbgCheckAttribs(CharAttribList const& rAttribs)
{
    std::set<std::pair<sal_Int32, sal_uInt16>> zero_set;
    for (const std::unique_ptr<EditCharAttrib>& rAttr : rAttribs.maAttribs)
    {
        assert(rAttr->GetStart() <= rAttr->GetEnd());
        assert(!rAttr->IsFeature() || rAttr->GetLen() == 1);
        if (0 == rAttr->GetLen())
        {
            // not sure if 0-length attributes allowed at all in non-empty para?
            assert(zero_set.insert(std::make_pair(rAttr->GetStart(), rAttr->Which())).second && "duplicate 0-length attribute detected");
        }
    }
    CheckOrderedList(rAttribs.GetAttribs());
}
#endif

void CharAttribList::dumpAsXml(xmlTextWriterPtr pWriter) const
{
    (void)xmlTextWriterStartElement(pWriter, BAD_CAST("CharAttribList"));
    for (auto const & i : maAttribs) {
        i->dumpAsXml(pWriter);
    }
    (void)xmlTextWriterEndElement(pWriter);
}

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

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

¤ Dauer der Verarbeitung: 0.18 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.