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 182 kB image not shown  

Quelle  impedit2.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 <vcl/svapp.hxx>
#include <vcl/window.hxx>
#include <editeng/lspcitem.hxx>
#include <editeng/flditem.hxx>
#include "impedit.hxx"
#include <editeng/editeng.hxx>
#include <editeng/editview.hxx>
#include <eerdll2.hxx>
#include <editeng/eerdll.hxx>
#include <edtspell.hxx>
#include "eeobj.hxx"
#include <editeng/txtrange.hxx>
#include <sfx2/app.hxx>
#include <sfx2/mieclip.hxx>
#include <sfx2/viewsh.hxx>
#include <svtools/colorcfg.hxx>
#include <svl/ctloptions.hxx>
#include <unotools/securityoptions.hxx>
#include <editeng/acorrcfg.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/ulspitem.hxx>
#include <editeng/adjustitem.hxx>
#include <editeng/frmdiritem.hxx>
#include <editeng/justifyitem.hxx>
#include <editeng/udlnitem.hxx>
#include <editeng/scripthintitem.hxx>

#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
#include <com/sun/star/i18n/WordType.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/lang/Locale.hpp>
#include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
#include <com/sun/star/system/SystemShellExecute.hpp>
#include <com/sun/star/system/SystemShellExecuteFlags.hpp>
#include <com/sun/star/system/XSystemShellExecute.hpp>
#include <com/sun/star/i18n/UnicodeType.hpp>

#include <rtl/character.hxx>

#include <sal/log.hxx>
#include <o3tl/safeint.hxx>
#include <osl/diagnose.h>
#include <sot/exchange.hxx>
#include <sot/formats.hxx>
#include <svl/asiancfg.hxx>
#include <svl/voiditem.hxx>
#include <i18nutil/unicode.hxx>
#include <i18nutil/scriptchangescanner.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <comphelper/flagguard.hxx>
#include <comphelper/lok.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/configuration.hxx>

#include <unicode/ubidi.h>
#include <algorithm>
#include <limits>
#include <memory>
#include <string_view>
#include <fstream>
#include <outleeng.hxx>

using namespace ::com::sun::star;

static sal_uInt16 lcl_CalcExtraSpace( const SvxLineSpacingItem& rLSItem )
{
    sal_uInt16 nExtra = 0;
    if ( rLSItem.GetInterLineSpaceRule() == SvxInterLineSpaceRule::Fix )
    {
        nExtra = rLSItem.GetInterLineSpace();
    }

    return nExtra;
}

constexpr tools::Long constMaxPaperSize = 0x7FFFFFFF;

ImpEditEngine::ImpEditEngine( EditEngine* pEE, SfxItemPool* pItemPool ) :
    pSharedVCL(EditDLL::Get().GetSharedVclResources()),
    maPaperSize(constMaxPaperSize, constMaxPaperSize),
    maMinAutoPaperSize(0, 0),
    maMaxAutoPaperSize(constMaxPaperSize, constMaxPaperSize),
    maEditDoc( pItemPool ),
    mpEditEngine(pEE),
    mpActiveView(nullptr),
    mpStylePool(nullptr),
    mpTextObjectPool(nullptr),
    mpUndoManager(nullptr),
    maWordDelimiters(u" .,;:-`'?!_=\"{}()[]"_ustr),
    maBackgroundColor(COL_AUTO),
    mbRoundToNearestPt(false),
    mnAsianCompressionMode(CharCompressType::NONE),
    meDefaultHorizontalTextDirection(EEHorizontalTextDirection::Default),
    mnBigTextObjectStart(20),
    meDefLanguage(LANGUAGE_DONTKNOW),
    mnCurTextHeight(0),
    maOnlineSpellTimer("editeng::ImpEditEngine aOnlineSpellTimer"),
    maStatusTimer("editeng::ImpEditEngine aStatusTimer"),
    mbKernAsianPunctuation(false),
    mbAddExtLeading(false),
    mbIsFormatting(false),
    mbFormatted(false),
    mbInSelection(false),
    mbIsInUndo(false),
    mbUpdateLayout(true),
    mbUndoEnabled(true),
    mbDowning(false),
    mbUseAutoColor(true),
    mbForceAutoColor(false),
    mbCallParaInsertedOrDeleted(false),
    mbFirstWordCapitalization(true),
    mbLastTryMerge(false),
    mbReplaceLeadingSingleQuotationMark(true),
    mbSkipOutsideFormat(false),
    mbFuzzing(comphelper::IsFuzzing()),
    mbNbspRunNext(false)
{
    maStatus.GetControlWord() =  EEControlBits::USECHARATTRIBS | EEControlBits::DOIDLEFORMAT |
                                EEControlBits::PASTESPECIAL | EEControlBits::UNDOATTRIBS |
                                EEControlBits::ALLOWBIGOBJS | EEControlBits::RTFSTYLESHEETS;

    maSelEngine.SetFunctionSet(&maSelFuncSet);

    maStatusTimer.SetTimeout(200);
    maStatusTimer.SetInvokeHandler(LINK(this, ImpEditEngine, StatusTimerHdl));

    maIdleFormatter.SetPriority(TaskPriority::REPAINT);
    maIdleFormatter.SetInvokeHandler(LINK(this, ImpEditEngine, IdleFormatHdl));

    maOnlineSpellTimer.SetTimeout(100);
    maOnlineSpellTimer.SetInvokeHandler(LINK( this, ImpEditEngine, OnlineSpellHdl));

    // Access data already from here on!
    SetRefDevice( nullptr );
    InitDoc( false );

    mbCallParaInsertedOrDeleted = true;

    maEditDoc.SetModifyHdl( LINK( this, ImpEditEngine, DocModified ) );
    StartListening(*SfxGetpApp());
}

void ImpEditEngine::Dispose()
{
    SolarMutexGuard g;
    auto pApp = SfxApplication::Get();
    if(pApp)
        EndListening(*pApp);
    mpVirtDev.disposeAndClear();
    mpOwnDev.disposeAndClear();
    pSharedVCL.reset();
}

ImpEditEngine::~ImpEditEngine()
{
    maStatusTimer.Stop();
    maOnlineSpellTimer.Stop();
    maIdleFormatter.Stop();

    // Destroying templates may otherwise cause unnecessary formatting,
    // when a parent template is destroyed.
    // And this after the destruction of the data!
    mbDowning = true;
    SetUpdateLayout( false );

    Dispose();
    // it's only legal to delete the mpUndoManager if it was created by
    // ImpEditEngine; if it was set by SetUndoManager() it must be cleared
    // before destroying the ImpEditEngine!
    assert(!mpUndoManager || typeid(*mpUndoManager) == typeid(EditUndoManager));
    delete mpUndoManager;
    mpTextRanger.reset();
    mpIMEInfos.reset();
    mpSpellInfo.reset();
}

void ImpEditEngine::SetRefDevice(OutputDevice* pRef)
{
    if (pRef)
        mpRefDev = pRef;
    else
        mpRefDev = pSharedVCL->GetVirtualDevice();

    mnOnePixelInRef = static_cast<sal_uInt16>(mpRefDev->PixelToLogic( Size( 1, 0 ) ).Width());

    if ( IsFormatted() )
    {
        FormatFullDoc();
        UpdateViews();
    }
}

void ImpEditEngine::SetRefMapMode( const MapMode& rMapMode )
{
    if ( GetRefDevice()->GetMapMode() == rMapMode )
        return;

    mpOwnDev.disposeAndClear();
    mpOwnDev = VclPtr<VirtualDevice>::Create();
    mpRefDev = mpOwnDev;
    mpRefDev->SetMapMode(MapMode(MapUnit::MapTwip));
    SetRefDevice(mpRefDev);

    mpRefDev->SetMapMode( rMapMode );
    mnOnePixelInRef = static_cast<sal_uInt16>(mpRefDev->PixelToLogic(Size(1, 0)).Width());
    if ( IsFormatted() )
    {
        FormatFullDoc();
        UpdateViews();
    }
}

void ImpEditEngine::InitDoc(bool bKeepParaAttribs)
{
    sal_Int32 nParas = maEditDoc.Count();
    for ( sal_Int32 n = bKeepParaAttribs ? 1 : 0; n < nParas; n++ )
    {
        if (maEditDoc.GetObject(n)->GetStyleSheet())
            EndListening( *maEditDoc.GetObject(n)->GetStyleSheet() );
    }

    if ( bKeepParaAttribs )
        maEditDoc.RemoveText();
    else
        maEditDoc.Clear();

    GetParaPortions().Reset();

    GetParaPortions().Insert(0, std::make_unique<ParaPortion>(maEditDoc.GetObject(0)));

    mbFormatted = false;

    if ( IsCallParaInsertedOrDeleted() )
    {
        GetEditEnginePtr()->ParagraphDeleted(EE_PARA_MAX);
        GetEditEnginePtr()->ParagraphInserted( 0 );
    }

    if ( GetStatus().DoOnlineSpelling() )
        maEditDoc.GetObject( 0 )->CreateWrongList();
}

EditPaM ImpEditEngine::DeleteSelected(const EditSelection& rSel)
{
    EditPaM aPaM (ImpDeleteSelection(rSel));
    return aPaM;
}

OUString ImpEditEngine::GetText( const ESelection& rESelection )
{
    return GetSelected(CreateSel(rESelection));
}

OUString ImpEditEngine::GetSelected( const EditSelection& rSel  ) const
{
    if ( !rSel.HasRange() )
        return OUString();

    EditSelection aSel( rSel );
    aSel.Adjust( maEditDoc );

    ContentNode* pStartNode = aSel.Min().GetNode();
    ContentNode* pEndNode = aSel.Max().GetNode();
    sal_Int32 nStartNode = maEditDoc.GetPos( pStartNode );
    sal_Int32 nEndNode = maEditDoc.GetPos( pEndNode );

    OSL_ENSURE( nStartNode <= nEndNode, "Selection not sorted ?" );

    // calculate buffer size we need
    sal_Int32 nBufSize = (aSel.Max().GetIndex() - aSel.Min().GetIndex() + 1)
                         + (nEndNode - nStartNode + 1);
    // protect against sometimes whacky selection
    if (nBufSize < 0 || nBufSize > 64 * 1024)
        nBufSize = 256;
    OUStringBuffer aText(nBufSize);
    const OUString aSep = EditDoc::GetSepStr( LINEEND_LF );

    // iterate over the paragraphs ...
    for ( sal_Int32 nNode = nStartNode; nNode <= nEndNode; nNode++ )
    {
        const ContentNode* pNode = maEditDoc.GetObject( nNode );
        assert(pNode);

        const sal_Int32 nStartPos = nNode==nStartNode ? aSel.Min().GetIndex() : 0;
        const sal_Int32 nEndPos = nNode==nEndNode ? aSel.Max().GetIndex() : pNode->Len(); // can also be == nStart!

        aText.append(EditDoc::GetParaAsString( pNode, nStartPos, nEndPos ));
        if ( nNode < nEndNode )
            aText.append(aSep);
    }
    return aText.makeStringAndClear();
}

bool ImpEditEngine::MouseButtonDown( const MouseEvent& rMEvt, EditView* pView )
{
    GetSelEngine().SetCurView( pView );
    SetActiveView( pView );

    if (!GetAutoCompleteText().isEmpty())
        SetAutoCompleteText( OUString(), true );

    GetSelEngine().SelMouseButtonDown( rMEvt );
    // Special treatment
    EditSelection aCurSel( pView->getImpl().GetEditSelection() );
    if ( rMEvt.IsShift() )
        return true;

    if ( rMEvt.GetClicks() == 2 )
    {
        // So that the SelectionEngine knows about the anchor.
        maSelEngine.CursorPosChanging( truefalse );

        EditSelection aNewSelection( SelectWord( aCurSel ) );
        pView->getImpl().DrawSelectionXOR();
        pView->getImpl().SetEditSelection( aNewSelection );
        pView->getImpl().DrawSelectionXOR();
        pView->ShowCursor();
    }
    else if ( rMEvt.GetClicks() == 3 )
    {
        // So that the SelectionEngine knows about the anchor.
        maSelEngine.CursorPosChanging( truefalse );

        EditSelection aNewSelection( aCurSel );
        aNewSelection.Min().SetIndex( 0 );
        aNewSelection.Max().SetIndex( aCurSel.Min().GetNode()->Len() );
        pView->getImpl().DrawSelectionXOR();
        pView->getImpl().SetEditSelection( aNewSelection );
        pView->getImpl().DrawSelectionXOR();
        pView->ShowCursor();
    }
    return true;
}

bool ImpEditEngine::Command( const CommandEvent& rCEvt, EditView* pView )
{
    bool bConsumed = true;

    GetSelEngine().SetCurView( pView );
    SetActiveView( pView );
    if ( rCEvt.GetCommand() == CommandEventId::StartExtTextInput )
    {
        if (!pView->IsReadOnly())
        {
            pView->DeleteSelected();
            mpIMEInfos.reset();
            EditPaM aPaM = pView->getImpl().GetEditSelection().Max();
            OUString aOldTextAfterStartPos = aPaM.GetNode()->Copy( aPaM.GetIndex() );
            sal_Int32 nMax = aOldTextAfterStartPos.indexOf( CH_FEATURE );
            if ( nMax != -1 )  // don't overwrite features!
                aOldTextAfterStartPos = aOldTextAfterStartPos.copy( 0, nMax );
            mpIMEInfos.reset( new ImplIMEInfos( aPaM, aOldTextAfterStartPos ) );
            mpIMEInfos->bWasCursorOverwrite = !pView->IsInsertMode();
            UndoActionStart( EDITUNDO_INSERT );
        }
    }
    else if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput )
    {
        if (!pView->IsReadOnly())
        {
            OSL_ENSURE( mpIMEInfos, "CommandEventId::EndExtTextInput => No start ?" );
            if( mpIMEInfos )
            {
                // #102812# convert quotes in IME text
                // works on the last input character, this is especially in Korean text often done
                // quotes that are inside of the string are not replaced!
                // Borrowed from sw: edtwin.cxx
                if ( mpIMEInfos->nLen )
                {
                    EditSelection aSel( mpIMEInfos->aPos );
                    aSel.Min().SetIndex( aSel.Min().GetIndex() + mpIMEInfos->nLen-1 );
                    aSel.Max().SetIndex( aSel.Max().GetIndex() + mpIMEInfos->nLen );
                    // #102812# convert quotes in IME text
                    // works on the last input character, this is especially in Korean text often done
                    // quotes that are inside of the string are not replaced!
                    // See also tdf#155350
                    const sal_Unicode nCharCode = aSel.Min().GetNode()->GetChar( aSel.Min().GetIndex() );
                    if ( ( GetStatus().DoAutoCorrect() ) && SvxAutoCorrect::IsAutoCorrectChar(nCharCode) )
                    {
                        aSel = DeleteSelected( aSel );
                        aSel = AutoCorrect( aSel, nCharCode, mpIMEInfos->bWasCursorOverwrite );
                        pView->getImpl().SetEditSelection( aSel );
                    }
                }

                ParaPortion* pPortion = FindParaPortion( mpIMEInfos->aPos.GetNode() );
                if (pPortion)
                    pPortion->MarkSelectionInvalid( mpIMEInfos->aPos.GetIndex() );

                bool bWasCursorOverwrite = mpIMEInfos->bWasCursorOverwrite;

                mpIMEInfos.reset();

                FormatAndLayout( pView );

                pView->SetInsertMode( !bWasCursorOverwrite );
            }
            UndoActionEnd();
        }
    }
    else if ( rCEvt.GetCommand() == CommandEventId::ExtTextInput )
    {
        if( mpIMEInfos && !pView->IsReadOnly())
        {
            OSL_ENSURE( mpIMEInfos, "CommandEventId::ExtTextInput => No Start ?" );
            const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();

            if ( !pData->IsOnlyCursorChanged() )
            {
                EditSelection aSel( mpIMEInfos->aPos );
                aSel.Max().SetIndex( aSel.Max().GetIndex() + mpIMEInfos->nLen );
                aSel = DeleteSelected( aSel );
                aSel = ImpInsertText( aSel, pData->GetText() );

                if ( mpIMEInfos->bWasCursorOverwrite )
                {
                    sal_Int32 nOldIMETextLen = mpIMEInfos->nLen;
                    sal_Int32 nNewIMETextLen = pData->GetText().getLength();

                    if ( ( nOldIMETextLen > nNewIMETextLen ) &&
                         ( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
                    {
                        // restore old characters
                        sal_Int32 nRestore = nOldIMETextLen - nNewIMETextLen;
                        EditPaM aPaM( mpIMEInfos->aPos );
                        aPaM.SetIndex( aPaM.GetIndex() + nNewIMETextLen );
                        ImpInsertText( EditSelection(aPaM), mpIMEInfos->aOldTextAfterStartPos.copy( nNewIMETextLen, nRestore ) );
                    }
                    else if ( ( nOldIMETextLen < nNewIMETextLen ) &&
                              ( nOldIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
                    {
                        // overwrite
                        sal_Int32 nOverwrite = nNewIMETextLen - nOldIMETextLen;
                        if ( ( nOldIMETextLen + nOverwrite ) > mpIMEInfos->aOldTextAfterStartPos.getLength() )
                            nOverwrite = mpIMEInfos->aOldTextAfterStartPos.getLength() - nOldIMETextLen;
                        OSL_ENSURE( nOverwrite && (nOverwrite < 0xFF00), "IME Overwrite?!" );
                        EditPaM aPaM( mpIMEInfos->aPos );
                        aPaM.SetIndex( aPaM.GetIndex() + nNewIMETextLen );
                        EditSelection _aSel( aPaM );
                        _aSel.Max().SetIndex( _aSel.Max().GetIndex() + nOverwrite );
                        DeleteSelected( _aSel );
                    }
                }
                if ( pData->GetTextAttr() )
                {
                    mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().getLength() );
                }
                else
                {
                    mpIMEInfos->DestroyAttribs();
                    mpIMEInfos->nLen = pData->GetText().getLength();
                }

                ParaPortion* pPortion = FindParaPortion( mpIMEInfos->aPos.GetNode() );
                pPortion->MarkSelectionInvalid( mpIMEInfos->aPos.GetIndex() );
                FormatAndLayout( pView );
            }

            UpdateSelections(); // other views
            EditSelection aNewSel( EditPaM( mpIMEInfos->aPos.GetNode(), mpIMEInfos->aPos.GetIndex()+pData->GetCursorPos() ) );
            pView->SetSelection( CreateESel( aNewSel ) );
            pView->SetInsertMode( !pData->IsCursorOverwrite() );

            if ( pData->IsCursorVisible() )
                pView->ShowCursor();
            else
                pView->HideCursor();
        }
    }
    else if ( rCEvt.GetCommand() == CommandEventId::InputContextChange )
    {
    }
    else if ( rCEvt.GetCommand() == CommandEventId::CursorPos )
    {
        EditPaM aPaM( pView->getImpl().GetEditSelection().Max() );
        tools::Rectangle aR1 = PaMtoEditCursor( aPaM );

        if ( !IsFormatted() )
            FormatDoc();

        ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( GetEditDoc().GetPos( aPaM.GetNode() ) );
        if (pParaPortion)
        {
            sal_Int32 nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), true );
            const EditLine& rLine = pParaPortion->GetLines()[nLine];

            sal_Int32 nInputEnd;
            if (mpIMEInfos)
                nInputEnd = mpIMEInfos->aPos.GetIndex() + mpIMEInfos->nLen;
            else
                nInputEnd = aPaM.GetIndex();

            if ( nInputEnd > rLine.GetEnd() )
                nInputEnd = rLine.GetEnd();
            tools::Rectangle aR2 = PaMtoEditCursor( EditPaM( aPaM.GetNode(), nInputEnd ), CursorFlags{ .bEndOfLine = true });
            tools::Rectangle aRect = pView->getImpl().GetWindowPos( aR1 );
            auto nExtTextInputWidth = aR2.Left() - aR1.Right();
            if (EditViewCallbacks* pEditViewCallbacks = pView->getEditViewCallbacks())
                pEditViewCallbacks->EditViewCursorRect(aRect, nExtTextInputWidth);
            else if (vcl::Window* pWindow = pView->GetWindow())
                pWindow->SetCursorRect(&aRect, nExtTextInputWidth);
        }
    }
    else if ( rCEvt.GetCommand() == CommandEventId::SelectionChange )
    {
        const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData();

        ESelection aSelection = pView->GetSelection();
        aSelection.Adjust();

        if( pView->HasSelection() )
        {
            aSelection.end.nIndex = aSelection.start.nIndex;
            aSelection.start.nIndex += pData->GetStart();
            aSelection.end.nIndex += pData->GetEnd();
        }
        else
        {
            aSelection.start.nIndex = pData->GetStart();
            aSelection.end.nIndex = pData->GetEnd();
        }
        pView->SetSelection( aSelection );
    }
    else if ( rCEvt.GetCommand() == CommandEventId::PrepareReconversion )
    {
        if ( pView->HasSelection() )
        {
            ESelection aSelection = pView->GetSelection();
            aSelection.Adjust();

            if ( aSelection.start.nPara != aSelection.end.nPara )
            {
                sal_Int32 aParaLen = mpEditEngine->GetTextLen( aSelection.start.nPara );
                aSelection.end.nPara = aSelection.start.nPara;
                aSelection.end.nIndex = aParaLen;
                pView->SetSelection( aSelection );
            }
        }
    }
    else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition )
    {
        if (mpIMEInfos)
        {
            EditPaM aPaM( pView->getImpl().GetEditSelection().Max() );
            if ( !IsFormatted() )
                FormatDoc();

            sal_Int32 nPortionPos = GetEditDoc().GetPos(aPaM.GetNode());
            ParaPortion* pParaPortion = GetParaPortions().SafeGetObject(nPortionPos);
            if (pParaPortion)
            {
                const sal_Int32 nMinPos = mpIMEInfos->aPos.GetIndex();
                const sal_Int32 nMaxPos = nMinPos + mpIMEInfos->nLen - 1;
                std::vector<tools::Rectangle> aRects(mpIMEInfos->nLen);

                auto CollectCharPositions = [&](const LineAreaInfo& rInfo) {
                    if (!rInfo.pLine) // Start of ParaPortion
                    {
                        if (rInfo.nPortion < nPortionPos)
                            return CallbackResult::SkipThisPortion;
                        if (rInfo.nPortion > nPortionPos)
                            return CallbackResult::Stop;
                        assert(&rInfo.rPortion == pParaPortion);
                    }
                    else // This is the needed ParaPortion
                    {
                        if (rInfo.pLine->GetStart() > nMaxPos)
                            return CallbackResult::Stop;
                        if (rInfo.pLine->GetEnd() < nMinPos)
                            return CallbackResult::Continue;
                        for (sal_Int32 n = nMinPos; n <= nMaxPos; ++n)
                        {
                            if (rInfo.pLine->IsIn(n))
                            {
                                tools::Rectangle aR = GetEditCursor(*pParaPortion, *rInfo.pLine, n, CursorFlags());
                                aR.Move(getTopLeftDocOffset(rInfo.aArea));
                                aRects[n - nMinPos] = pView->getImpl().GetWindowPos(aR);
                            }
                        }
                    }
                    return CallbackResult::Continue;
                };
                IterateLineAreas(CollectCharPositions, IterFlag::none);

                if (vcl::Window* pWindow = pView->GetWindow())
                    pWindow->SetCompositionCharRect(aRects.data(), aRects.size());
            }
        }
    }
    else
        bConsumed = false;

    return GetSelEngine().Command(rCEvt) || bConsumed;
}

bool ImpEditEngine::MouseButtonUp( const MouseEvent& rMEvt, EditView* pView )
{
    GetSelEngine().SetCurView( pView );
    GetSelEngine().SelMouseButtonUp( rMEvt );

    // in the tiled rendering case, setting bInSelection here has unexpected
    // consequences - further tiles painting removes the selection
    // FIXME I believe resetting bInSelection should not be here even in the
    // non-tiled-rendering case, but it has been here since 2000 (and before)
    // so who knows what corner case it was supposed to solve back then
    if (!comphelper::LibreOfficeKit::isActive())
        mbInSelection = false;

    // Special treatments
    EditSelection aCurSel( pView->getImpl().GetEditSelection() );
    if ( aCurSel.HasRange() )
        return true;

    if ( ( rMEvt.GetClicks() != 1 ) || !rMEvt.IsLeft() || rMEvt.IsMod2() )
        return true;

    const OutputDevice& rOutDev = pView->getEditViewCallbacks() ? pView->getEditViewCallbacks()->EditViewOutputDevice() : *pView->GetWindow()->GetOutDev();
    Point aLogicClick = rOutDev.PixelToLogic(rMEvt.GetPosPixel());
    const SvxFieldItem* pFld = pView->GetField(aLogicClick);
    if (!pFld)
        return true;

    // tdf#121039 When in edit mode, editeng is responsible for opening the URL on mouse click
    bool bUrlOpened = GetEditEnginePtr()->FieldClicked( *pFld );
    if (bUrlOpened)
        return true;

    if (auto pUrlField = dynamic_cast<const SvxURLField*>(pFld->GetField()))
    {
        bool bCtrlClickHappened = rMEvt.IsMod1();
        bool bCtrlClickSecOption
            = SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::CtrlClickHyperlink);
        if ((bCtrlClickHappened && bCtrlClickSecOption)
            || (!bCtrlClickHappened && !bCtrlClickSecOption))
        {
            css::uno::Reference<css::system::XSystemShellExecute> exec(
                css::system::SystemShellExecute::create(
                    comphelper::getProcessComponentContext()));
            exec->execute(pUrlField->GetURL(), OUString(),
                          css::system::SystemShellExecuteFlags::DEFAULTS);
        }
    }
    return true;
}

void ImpEditEngine::ReleaseMouse()
{
    GetSelEngine().ReleaseMouse();
}

bool ImpEditEngine::MouseMove( const MouseEvent& rMEvt, EditView* pView )
{
    // MouseMove is called directly after ShowQuickHelp()!
    GetSelEngine().SetCurView( pView );
    GetSelEngine().SelMouseMove( rMEvt );
    return true;
}

EditPaM ImpEditEngine::InsertText(const EditSelection& aSel, const OUString& rStr)
{
    EditPaM aPaM = ImpInsertText( aSel, rStr );
    return aPaM;
}

void ImpEditEngine::Clear()
{
    InitDoc( false );

    EditPaM aPaM = maEditDoc.GetStartPaM();
    EditSelection aSel( aPaM );

    mnCurTextHeight = 0;

    ResetUndoManager();

    for (size_t nView = maEditViews.size(); nView; )
    {
        EditView* pView = maEditViews[--nView];
        pView->getImpl().SetEditSelection( aSel );
    }

    // Related: tdf#82115 Fix crash when handling input method events.
    // The nodes in mpIMEInfos may be deleted in ImpEditEngine::Clear() which
    // causes a crash in the CommandEventId::ExtTextInput and
    // CommandEventId::EndExtTextInput event handlers.
    mpIMEInfos.reset();
}

EditPaM ImpEditEngine::RemoveText()
{
    InitDoc( true );

    EditPaM aStartPaM = maEditDoc.GetStartPaM();
    EditSelection aEmptySel( aStartPaM, aStartPaM );
    for (EditView* pView : maEditViews)
    {
        pView->getImpl().SetEditSelection( aEmptySel );
    }
    ResetUndoManager();
    return maEditDoc.GetStartPaM();
}


void ImpEditEngine::SetText(const OUString& rText)
{
    // RemoveText deletes the undo list!
    EditPaM aStartPaM = RemoveText();
    bool bUndoCurrentlyEnabled = IsUndoEnabled();
    // The text inserted manually can not be made reversible by the user
    EnableUndo( false );

    EditSelection aEmptySel( aStartPaM, aStartPaM );
    EditPaM aPaM = aStartPaM;
    if (!rText.isEmpty())
        aPaM = ImpInsertText( aEmptySel, rText );

    for (EditView* pView : maEditViews)
    {
        pView->getImpl().SetEditSelection( EditSelection( aPaM, aPaM ) );
        //  If no text then also no Format&Update
        // => The text remains.
        if (rText.isEmpty() && IsUpdateLayout())
        {
            tools::Rectangle aTmpRect( pView->GetOutputArea().TopLeft(),
                                Size( maPaperSize.Width(), mnCurTextHeight ) );
            aTmpRect.Intersection( pView->GetOutputArea() );
            pView->InvalidateWindow( aTmpRect );
        }
    }
    if (rText.isEmpty())     // otherwise it must be invalidated later, !bFormatted is enough.
        mnCurTextHeight = 0;
    EnableUndo( bUndoCurrentlyEnabled );
    OSL_ENSURE( !HasUndoManager() || !GetUndoManager().GetUndoActionCount(), "Undo after SetText?" );
}


const SfxItemSet& ImpEditEngine::GetEmptyItemSet() const
{
    if ( !pEmptyItemSet )
    {
        pEmptyItemSet = std::make_unique<SfxItemSet>(SfxItemSet::makeFixedSfxItemSet<
                            EE_ITEMS_START, EE_ITEMS_END>(const_cast<SfxItemPool&>(maEditDoc.GetItemPool())));
        for ( sal_uInt16 nWhich = EE_ITEMS_START; nWhich <= EE_CHAR_END; nWhich++)
        {
            pEmptyItemSet->ClearItem( nWhich );
        }
    }
    return *pEmptyItemSet;
}


//  MISC

void ImpEditEngine::TextModified()
{
    mbFormatted = false;

    if ( GetNotifyHdl().IsSet() )
    {
        EENotify aNotify( EE_NOTIFY_TEXTMODIFIED );
        GetNotifyHdl().Call( aNotify );
    }
}


void ImpEditEngine::ParaAttribsChanged( ContentNode const * pNode, bool bIgnoreUndoCheck )
{
    assert(pNode && "ParaAttribsChanged: Which one?");

    maEditDoc.SetModified( true );
    mbFormatted = false;

    ParaPortion* pPortion = FindParaPortion( pNode );
    assert(pPortion);
    pPortion->MarkSelectionInvalid( 0 );

    sal_Int32 nPara = maEditDoc.GetPos( pNode );
    assert(nPara != EE_PARA_MAX);
    if (bIgnoreUndoCheck || mpEditEngine->IsInUndo())
        mpEditEngine->ParaAttribsChanged( nPara );

    ParaPortion* pNextPortion = GetParaPortions().SafeGetObject( nPara+1 );
    // => is formatted again anyway, if Invalid.
    if ( pNextPortion && !pNextPortion->IsInvalid() )
        CalcHeight(*pNextPortion);
}


//  Cursor movements

EditSelection const& ImpEditEngine::MoveCursor(const KeyEvent& rKeyEvent, EditView* pEditView,
                                               CursorFlags* pOutCursorFlags)
{
    // Actually, only necessary for up/down, but whatever.
    CheckIdleFormatter();

    EditPaM aPaM(pEditView->getImpl().GetEditSelection().Max());

    EditPaM aOldPaM( aPaM );

    // tdf#151336: Moving the cursor may advance to another paragraph. When that happens,
    // special handling is needed for correct visual cursor placement in RTL text.
    auto fnSetStartOfLineFlag = [&]
    {
        if (pOutCursorFlags)
        {
            pOutCursorFlags->bStartOfLine = true;
        }
    };

    auto fnSetParaChangeStartOfLineFlag = [&]
    {
        if (pOutCursorFlags && aPaM.GetNode() != aOldPaM.GetNode())
        {
            pOutCursorFlags->bStartOfLine = true;
        }
    };

    auto fnSetEndOfLineFlag = [&]
    {
        if (pOutCursorFlags)
        {
            pOutCursorFlags->bEndOfLine = true;
        }
    };

    auto fnSetParaChangeEndOfLineFlag = [&]
    {
        if (pOutCursorFlags && aPaM.GetNode() != aOldPaM.GetNode())
        {
            pOutCursorFlags->bEndOfLine = true;
        }
    };

    TextDirectionality eTextDirection = TextDirectionality::LeftToRight_TopToBottom;
    if (IsEffectivelyVertical() && IsTopToBottom())
        eTextDirection = TextDirectionality::TopToBottom_RightToLeft;
    else if (IsEffectivelyVertical() && !IsTopToBottom())
        eTextDirection = TextDirectionality::BottomToTop_LeftToRight;
    else if ( IsRightToLeft( GetEditDoc().GetPos( aPaM.GetNode() ) ) )
        eTextDirection = TextDirectionality::RightToLeft_TopToBottom;

    KeyEvent aTranslatedKeyEvent = rKeyEvent.LogicalTextDirectionality( eTextDirection );

    bool bCtrl = aTranslatedKeyEvent.GetKeyCode().IsMod1();
    sal_uInt16 nCode = aTranslatedKeyEvent.GetKeyCode().GetCode();

    if ( DoVisualCursorTraveling() )
    {
        // Only for simple cursor movement...
        if ( !bCtrl && ( ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) ) )
        {
            aPaM = CursorVisualLeftRight( pEditView, aPaM, rKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL, rKeyEvent.GetKeyCode().GetCode() == KEY_LEFT );
            nCode = 0;  // skip switch statement
        }
    }

    bool bKeyModifySelection = aTranslatedKeyEvent.GetKeyCode().IsShift();
    switch ( nCode )
    {
        case KEY_UP:        aPaM = CursorUp( aPaM, pEditView );
                            break;
        case KEY_DOWN:      aPaM = CursorDown( aPaM, pEditView );
                            break;
        case KEY_LEFT:      aPaM = bCtrl ? WordLeft( aPaM ) : CursorLeft( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL );
                            fnSetParaChangeEndOfLineFlag();
                            break;
        case KEY_RIGHT:     aPaM = bCtrl ? WordRight( aPaM ) : CursorRight( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL );
                            fnSetParaChangeStartOfLineFlag();
                            break;
        case KEY_HOME:      aPaM = bCtrl ? CursorStartOfDoc() : CursorStartOfLine( aPaM );
                            fnSetStartOfLineFlag();
                            break;
        case KEY_END:       aPaM = bCtrl ? CursorEndOfDoc() : CursorEndOfLine( aPaM );
                            fnSetEndOfLineFlag();
                            break;
        case KEY_PAGEUP:    aPaM = bCtrl ? CursorStartOfDoc() : PageUp( aPaM, pEditView );
                            break;
        case KEY_PAGEDOWN:  aPaM = bCtrl ? CursorEndOfDoc() : PageDown( aPaM, pEditView );
                            break;
        case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
                            aPaM = CursorStartOfLine( aPaM );
                            fnSetStartOfLineFlag();
                            bKeyModifySelection = false;
                            break;
        case css::awt::Key::MOVE_TO_END_OF_LINE:
                            aPaM = CursorEndOfLine( aPaM );
                            fnSetEndOfLineFlag();
                            bKeyModifySelection = false;
                            break;
        case css::awt::Key::MOVE_WORD_BACKWARD:
                            aPaM = WordLeft( aPaM );
                            bKeyModifySelection = false;
                            break;
        case css::awt::Key::MOVE_WORD_FORWARD:
                            aPaM = WordRight( aPaM );
                            bKeyModifySelection = false;
                            break;
        case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
                            aPaM = CursorStartOfParagraph( aPaM );
                            if( aPaM == aOldPaM )
                            {
                                aPaM = CursorLeft( aPaM );
                                aPaM = CursorStartOfParagraph( aPaM );
                            }
                            bKeyModifySelection = false;
                            break;
        case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
                            aPaM = CursorEndOfParagraph( aPaM );
                            if( aPaM == aOldPaM )
                            {
                                aPaM = CursorRight( aPaM );
                                aPaM = CursorEndOfParagraph( aPaM );
                            }
                            bKeyModifySelection = false;
                            break;
        case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
                            aPaM = CursorStartOfDoc();
                            bKeyModifySelection = false;
                            break;
        case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
                            aPaM = CursorEndOfDoc();
                            bKeyModifySelection = false;
                            break;
        case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
                            aPaM = CursorStartOfLine( aPaM );
                            bKeyModifySelection = true;
                            break;
        case css::awt::Key::SELECT_TO_END_OF_LINE:
                            aPaM = CursorEndOfLine( aPaM );
                            bKeyModifySelection = true;
                            break;
        case css::awt::Key::SELECT_BACKWARD:
                            aPaM = CursorLeft( aPaM );
                            bKeyModifySelection = true;
                            break;
        case css::awt::Key::SELECT_FORWARD:
                            aPaM = CursorRight( aPaM );
                            bKeyModifySelection = true;
                            break;
        case css::awt::Key::SELECT_WORD_BACKWARD:
                            aPaM = WordLeft( aPaM );
                            bKeyModifySelection = true;
                            break;
        case css::awt::Key::SELECT_WORD_FORWARD:
                            aPaM = WordRight( aPaM );
                            bKeyModifySelection = true;
                            break;
        case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
                            aPaM = CursorStartOfParagraph( aPaM );
                            if( aPaM == aOldPaM )
                            {
                                aPaM = CursorLeft( aPaM );
                                aPaM = CursorStartOfParagraph( aPaM );
                            }
                            bKeyModifySelection = true;
                            break;
        case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
                            aPaM = CursorEndOfParagraph( aPaM );
                            if( aPaM == aOldPaM )
                            {
                                aPaM = CursorRight( aPaM );
                                aPaM = CursorEndOfParagraph( aPaM );
                            }
                            bKeyModifySelection = true;
                            break;
        case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
                            aPaM = CursorStartOfDoc();
                            bKeyModifySelection = true;
                            break;
        case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
                            aPaM = CursorEndOfDoc();
                            bKeyModifySelection = true;
                            break;
    }

    if ( aOldPaM != aPaM && nullptr != aOldPaM.GetNode() )
    {
        aOldPaM.GetNode()->checkAndDeleteEmptyAttribs();
    }

    // May cause, a CreateAnchor or deselection all
    maSelEngine.SetCurView(pEditView);
    maSelEngine.CursorPosChanging( bKeyModifySelection, aTranslatedKeyEvent.GetKeyCode().IsMod1() );
    EditPaM aOldEnd(pEditView->getImpl().GetEditSelection().Max());

    {
        EditSelection aNewSelection(pEditView->getImpl().GetEditSelection());
        aNewSelection.Max() = aPaM;
        pEditView->getImpl().SetEditSelection(aNewSelection);
        // const_cast<EditPaM&>(pEditView->getImpl().GetEditSelection().Max()) = aPaM;
    }

    if ( bKeyModifySelection )
    {
        // Then the selection is expanded ... or the whole selection is painted in case of tiled rendering.
        EditSelection aTmpNewSel( comphelper::LibreOfficeKit::isActive() ? pEditView->getImpl().GetEditSelection().Min() : aOldEnd, aPaM );
        pEditView->getImpl().DrawSelectionXOR( aTmpNewSel );
    }
    else
    {
        EditSelection aNewSelection(pEditView->getImpl().GetEditSelection());
        aNewSelection.Min() = aPaM;
        pEditView->getImpl().SetEditSelection(aNewSelection);
        // const_cast<EditPaM&>(pEditView->getImpl().GetEditSelection().Min()) = aPaM;
    }

    return pEditView->getImpl().GetEditSelection();
}

EditPaM ImpEditEngine::CursorVisualStartEnd( EditView const * mpEditView, const EditPaM&&nbsp;rPaM, bool bStart )
{
    EditPaM aPaM( rPaM );

    sal_Int32 nPara = GetEditDoc().GetPos( aPaM.GetNode() );
    ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara );
    if (!pParaPortion)
        return aPaM;

    sal_Int32 nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), false );
    const EditLine& rLine = pParaPortion->GetLines()[nLine];
    bool bEmptyLine = rLine.GetStart() == rLine.GetEnd();

    mpEditView->getImpl().maExtraCursorFlags = CursorFlags();

    if ( !bEmptyLine )
    {
        OUString aLine = aPaM.GetNode()->GetString().copy(rLine.GetStart(), rLine.GetEnd() - rLine.GetStart());

        UErrorCode nError = U_ZERO_ERROR;
        UBiDi* pBidi = ubidi_openSized( aLine.getLength(), 0, &nError );

        const UBiDiLevel  nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
        ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aLine.getStr()), aLine.getLength(), nBidiLevel, nullptr, &nError );

        sal_Int32 nVisPos = bStart ? 0 : aLine.getLength()-1;
        const sal_Int32 nLogPos = ubidi_getLogicalIndex( pBidi, nVisPos, &nError );

        ubidi_close( pBidi );

        aPaM.SetIndex( nLogPos + rLine.GetStart() );

        sal_Int32 nTmp;
        sal_Int32 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nTmp, true );
        const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[nTextPortion];
        bool bPortionRTL = rTextPortion.IsRightToLeft();

        if ( bStart )
        {
            mpEditView->getImpl().SetCursorBidiLevel( bPortionRTL ? 0 : 1 );
            // Maybe we must be *behind* the character
            if (bPortionRTL && mpEditView->IsInsertMode())
                aPaM.SetIndex( aPaM.GetIndex()+1 );
        }
        else
        {
            mpEditView->getImpl().SetCursorBidiLevel( bPortionRTL ? 1 : 0 );
            if ( !bPortionRTL && mpEditView->IsInsertMode() )
                aPaM.SetIndex( aPaM.GetIndex()+1 );
        }
    }

    return aPaM;
}

EditPaM ImpEditEngine::CursorVisualLeftRight( EditView const * pEditView, const EditPaM&&nbsp;rPaM, sal_uInt16 nCharacterIteratorMode, bool bVisualToLeft )
{
    EditPaM aPaM( rPaM );

    sal_Int32 nPara = GetEditDoc().GetPos( aPaM.GetNode() );
    ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara );
    if (!pParaPortion)
        return aPaM;

    sal_Int32 nLine = pParaPortion->GetLines().FindLine( aPaM.GetIndex(), false );
    const EditLine& rLine = pParaPortion->GetLines()[nLine];
    bool bEmptyLine = rLine.GetStart() == rLine.GetEnd();

    pEditView->getImpl().maExtraCursorFlags = CursorFlags();

    bool bParaRTL = IsRightToLeft( nPara );

    bool bDone = false;

    if ( bEmptyLine )
    {
        if ( bVisualToLeft )
        {
            aPaM = CursorUp( aPaM, pEditView );
            if ( aPaM != rPaM )
                aPaM = CursorVisualStartEnd( pEditView, aPaM, false );
        }
        else
        {
            aPaM = CursorDown( aPaM, pEditView );
            if ( aPaM != rPaM )
                aPaM = CursorVisualStartEnd( pEditView, aPaM, true );
        }

        bDone = true;
    }

    bool bLogicalBackward = bParaRTL ? !bVisualToLeft : bVisualToLeft;

    if ( !bDone && pEditView->IsInsertMode() )
    {
        // Check if we are within a portion and don't have overwrite mode, then it's easy...
        sal_Int32 nPortionStart;
        sal_Int32 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart );
        const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[nTextPortion];

        bool bPortionBoundary = ( aPaM.GetIndex() == nPortionStart ) || ( aPaM.GetIndex() == (nPortionStart+rTextPortion.GetLen()) );
        sal_uInt16 nRTLLevel = rTextPortion.GetRightToLeftLevel();

        // Portion boundary doesn't matter if both have same RTL level
        sal_Int32 nRTLLevelNextPortion = -1;
        if ( bPortionBoundary && aPaM.GetIndex() && ( aPaM.GetIndex() < aPaM.GetNode()->Len() ) )
        {
            sal_Int32 nTmp;
            sal_Int32 nNextTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex()+1, nTmp, !bLogicalBackward );
            const TextPortion& rNextTextPortion = pParaPortion->GetTextPortions()[nNextTextPortion];
            nRTLLevelNextPortion = rNextTextPortion.GetRightToLeftLevel();
        }

        if ( !bPortionBoundary || ( nRTLLevel == nRTLLevelNextPortion ) )
        {
            if (bVisualToLeft != bool(nRTLLevel % 2))
            {
                aPaM = CursorLeft( aPaM, nCharacterIteratorMode );
                pEditView->getImpl().SetCursorBidiLevel( 1 );
            }
            else
            {
                aPaM = CursorRight( aPaM, nCharacterIteratorMode );
                pEditView->getImpl().SetCursorBidiLevel( 0 );
            }
            bDone = true;
        }
    }

    if ( !bDone )
    {
        bool bGotoStartOfNextLine = false;
        bool bGotoEndOfPrevLine = false;

        OUString aLine = aPaM.GetNode()->GetString().copy(rLine.GetStart(), rLine.GetEnd() - rLine.GetStart());
        const sal_Int32 nPosInLine = aPaM.GetIndex() - rLine.GetStart();

        UErrorCode nError = U_ZERO_ERROR;
        UBiDi* pBidi = ubidi_openSized( aLine.getLength(), 0, &nError );

        const UBiDiLevel  nBidiLevel = IsRightToLeft( nPara ) ? 1 /*RTL*/ : 0 /*LTR*/;
        ubidi_setPara( pBidi, reinterpret_cast<const UChar *>(aLine.getStr()), aLine.getLength(), nBidiLevel, nullptr, &nError );

        if ( !pEditView->IsInsertMode() )
        {
            bool bEndOfLine = nPosInLine == aLine.getLength();
            sal_Int32 nVisPos = ubidi_getVisualIndex( pBidi, !bEndOfLine ? nPosInLine : nPosInLine-1, &nError&nbsp;);
            if ( bVisualToLeft )
            {
                bGotoEndOfPrevLine = nVisPos == 0;
                if ( !bEndOfLine )
                    nVisPos--;
            }
            else
            {
                bGotoStartOfNextLine = nVisPos == (aLine.getLength() - 1);
                if ( !bEndOfLine )
                    nVisPos++;
            }

            if ( !bGotoEndOfPrevLine && !bGotoStartOfNextLine )
            {
                aPaM.SetIndex( rLine.GetStart() + ubidi_getLogicalIndex( pBidi, nVisPos, &nError ) );
                pEditView->getImpl().SetCursorBidiLevel( 0 );
            }
        }
        else
        {
            bool bWasBehind = false;
            bool bBeforePortion = !nPosInLine || pEditView->getImpl().GetCursorBidiLevel() == 1;
            if ( nPosInLine && ( !bBeforePortion ) ) // before the next portion
                bWasBehind = true;  // step one back, otherwise visual will be unusable when rtl portion follows.

            sal_Int32 nPortionStart;
            sal_Int32 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, bBeforePortion );
            const TextPortion& rTextPortion = pParaPortion->GetTextPortions()[nTextPortion];
            bool bRTLPortion = rTextPortion.IsRightToLeft();

            // -1: We are 'behind' the character
            tools::Long nVisPos = static_cast<tools::Long>(ubidi_getVisualIndex( pBidi, bWasBehind ? nPosInLine-1 : nPosInLine, &nError ));
            if ( bVisualToLeft )
            {
                if ( !bWasBehind || bRTLPortion )
                    nVisPos--;
            }
            else
            {
                if ( bWasBehind || bRTLPortion || bBeforePortion )
                    nVisPos++;
            }

            bGotoEndOfPrevLine = nVisPos < 0;
            bGotoStartOfNextLine = nVisPos >= aLine.getLength();

            if ( !bGotoEndOfPrevLine && !bGotoStartOfNextLine )
            {
                aPaM.SetIndex( rLine.GetStart() + ubidi_getLogicalIndex( pBidi, nVisPos, &nError ) );

                // RTL portion, stay visually on the left side.
                sal_Int32 _nPortionStart;
                // sal_uInt16 nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), nPortionStart, !bRTLPortion );
                sal_Int32 _nTextPortion = pParaPortion->GetTextPortions().FindPortion( aPaM.GetIndex(), _nPortionStart, true );
                const TextPortion& _rTextPortion = pParaPortion->GetTextPortions()[_nTextPortion];
                if ( bVisualToLeft && !bRTLPortion && _rTextPortion.IsRightToLeft() )
                    aPaM.SetIndex( aPaM.GetIndex()+1 );
                else if ( !bVisualToLeft && bRTLPortion && ( bWasBehind || !_rTextPortion.IsRightToLeft() ) )
                    aPaM.SetIndex( aPaM.GetIndex()+1 );

                pEditView->getImpl().SetCursorBidiLevel( _nPortionStart );
            }
        }

        ubidi_close( pBidi );

        if ( bGotoEndOfPrevLine )
        {
            aPaM = CursorUp( aPaM, pEditView );
            if ( aPaM != rPaM )
                aPaM = CursorVisualStartEnd( pEditView, aPaM, false );
        }
        else if ( bGotoStartOfNextLine )
        {
            aPaM = CursorDown( aPaM, pEditView );
            if ( aPaM != rPaM )
                aPaM = CursorVisualStartEnd( pEditView, aPaM, true );
        }
    }
    return aPaM;
}


EditPaM ImpEditEngine::CursorLeft( const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode )
{
    EditPaM aCurPaM( rPaM );
    EditPaM aNewPaM( aCurPaM );

    if ( aCurPaM.GetIndex() )
    {
        sal_Int32 nCount = 1;
        uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
        aNewPaM.SetIndex(
             _xBI->previousCharacters(
                 aNewPaM.GetNode()->GetString(), aNewPaM.GetIndex(), GetLocale( aNewPaM ), nCharacterIteratorMode, nCount, nCount));
    }
    else
    {
        ContentNode* pNode = aCurPaM.GetNode();
        pNode = GetPrevVisNode( pNode );
        if ( pNode )
        {
            aNewPaM.SetNode( pNode );
            aNewPaM.SetIndex( pNode->Len() );
        }
    }

    return aNewPaM;
}

EditPaM ImpEditEngine::CursorRight( const EditPaM& rPaM, sal_uInt16 nCharacterIteratorMode )
{
    EditPaM aCurPaM( rPaM );
    EditPaM aNewPaM( aCurPaM );

    if ( aCurPaM.GetIndex() < aCurPaM.GetNode()->Len() )
    {
        uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
        sal_Int32 nCount = 1;
        aNewPaM.SetIndex(
            _xBI->nextCharacters(
                aNewPaM.GetNode()->GetString(), aNewPaM.GetIndex(), GetLocale( aNewPaM ), nCharacterIteratorMode, nCount, nCount));
    }
    else
    {
        ContentNode* pNode = aCurPaM.GetNode();
        pNode = GetNextVisNode( pNode );
        if ( pNode )
        {
            aNewPaM.SetNode( pNode );
            aNewPaM.SetIndex( 0 );
        }
    }

    return aNewPaM;
}

EditPaM ImpEditEngine::CursorUp( const EditPaM& rPaM, EditView const * pView )
{
    assert(pView && "No View - No Cursor Movement!");

    const ParaPortion* pPPortion = FindParaPortion( rPaM.GetNode() );
    assert(pPPortion);
    sal_Int32 nLine = pPPortion->GetLineNumber( rPaM.GetIndex() );
    assert(nLine >= 0);
    const EditLine& rLine = pPPortion->GetLines()[nLine];

    tools::Long nX;
    if ( pView->getImpl().mnTravelXPos == TRAVEL_X_DONTKNOW )
    {
        nX = GetXPos(*pPPortion, rLine, rPaM.GetIndex());
        pView->getImpl().mnTravelXPos = nX + mnOnePixelInRef;
    }
    else
        nX = pView->getImpl().mnTravelXPos;

    EditPaM aNewPaM( rPaM );
    if ( nLine )    // same paragraph
    {
        assert(nLine >= 1);
        const EditLine& rPrevLine = pPPortion->GetLines()[nLine-1];
        aNewPaM.SetIndex(GetChar(*pPPortion, rPrevLine, nX));
        // If a previous automatically wrapped line, and one has to be exactly
        // at the end of this line, the cursor lands on the current line at the
        // beginning. See Problem: Last character of an automatically wrapped
        // Row = cursor
        if ( aNewPaM.GetIndex() && ( aNewPaM.GetIndex() == rLine.GetStart() ) )
            aNewPaM = CursorLeft( aNewPaM );
    }
    else    // previous paragraph
    {
        const ParaPortion* pPrevPortion = GetPrevVisPortion( pPPortion );
        if ( pPrevPortion )
        {
            const EditLine& rLine2 = pPrevPortion->GetLines()[pPrevPortion->GetLines().Count()-1];
            aNewPaM.SetNode( pPrevPortion->GetNode() );
            aNewPaM.SetIndex(GetChar(*pPrevPortion, rLine2, nX + mnOnePixelInRef));
        }
    }

    return aNewPaM;
}

EditPaM ImpEditEngine::CursorDown( const EditPaM& rPaM, EditView const * pView )
{
    assert(pView);

    const ParaPortion* pPPortion = FindParaPortion( rPaM.GetNode() );
    assert(pPPortion);
    sal_Int32 nLine = pPPortion->GetLineNumber( rPaM.GetIndex() );

    tools::Long nX;
    if ( pView->getImpl().mnTravelXPos == TRAVEL_X_DONTKNOW )
    {
        const EditLine& rLine = pPPortion->GetLines()[nLine];
        nX = GetXPos(*pPPortion, rLine, rPaM.GetIndex());
        pView->getImpl().mnTravelXPos = nX + mnOnePixelInRef;
    }
    else
        nX = pView->getImpl().mnTravelXPos;

    EditPaM aNewPaM( rPaM );
    if ( nLine < pPPortion->GetLines().Count()-1 )
    {
        const EditLine& rNextLine = pPPortion->GetLines()[nLine+1];
        aNewPaM.SetIndex(GetChar(*pPPortion, rNextLine, nX));
        // Special treatment, see CursorUp ...
        if ( ( aNewPaM.GetIndex() == rNextLine.GetEnd() ) && ( aNewPaM.GetIndex() > rNextLine.GetStart() ) && ( aNewPaM.GetIndex() < pPPortion->GetNode()->Len() ) )
            aNewPaM = CursorLeft( aNewPaM );
    }
    else    // next paragraph
    {
        const ParaPortion* pNextPortion = GetNextVisPortion( pPPortion );
        if ( pNextPortion )
        {
            const EditLine& rLine = pNextPortion->GetLines()[0];
            aNewPaM.SetNode( pNextPortion->GetNode() );
            // Never at the very end when several lines, because then a line
            // below the cursor appears.
            aNewPaM.SetIndex(GetChar(*pNextPortion, rLine, nX + mnOnePixelInRef));
            if ( ( aNewPaM.GetIndex() == rLine.GetEnd() ) && ( aNewPaM.GetIndex() > rLine.GetStart() ) && ( pNextPortion->GetLines().Count() > 1 ) )
                aNewPaM = CursorLeft( aNewPaM );
        }
    }

    return aNewPaM;
}

EditPaM ImpEditEngine::CursorStartOfLine( const EditPaM& rPaM )
{
    const ParaPortion* pCurPortion = FindParaPortion( rPaM.GetNode() );
    assert(pCurPortion);
    sal_Int32 nLine = pCurPortion->GetLineNumber( rPaM.GetIndex() );
    const EditLine& rLine = pCurPortion->GetLines()[nLine];

    EditPaM aNewPaM( rPaM );
    aNewPaM.SetIndex( rLine.GetStart() );
    return aNewPaM;
}

EditPaM ImpEditEngine::CursorEndOfLine( const EditPaM& rPaM )
{
    const ParaPortion* pCurPortion = FindParaPortion( rPaM.GetNode() );
    assert(pCurPortion);
    sal_Int32 nLine = pCurPortion->GetLineNumber( rPaM.GetIndex() );
    const EditLine& rLine = pCurPortion->GetLines()[nLine];

    EditPaM aNewPaM( rPaM );
    aNewPaM.SetIndex( rLine.GetEnd() );
    if ( rLine.GetEnd() > rLine.GetStart() )
    {
        if ( aNewPaM.GetNode()->IsFeature( aNewPaM.GetIndex() - 1 ) )
        {
            // When a soft break, be in front of it!
            const EditCharAttrib* pNextFeature = aNewPaM.GetNode()->GetCharAttribs().FindFeature( aNewPaM.GetIndex()-1 );
            if ( pNextFeature && ( pNextFeature->GetItem()->Which() == EE_FEATURE_LINEBR ) )
                aNewPaM = CursorLeft( aNewPaM );
        }
        else if ( ( aNewPaM.GetNode()->GetChar( aNewPaM.GetIndex() - 1 ) == ' ' ) && ( aNewPaM.GetIndex() != aNewPaM.GetNode()->Len() ) )
        {
            // For a Blank in an auto wrapped line, it makes sense, to stand
            // in front of it, since the user wants to be after the word.
            // If this is changed, special treatment for Pos1 to End!
            aNewPaM = CursorLeft( aNewPaM );
        }
    }
    return aNewPaM;
}

EditPaM ImpEditEngine::CursorStartOfParagraph( const EditPaM& rPaM )
{
    EditPaM aPaM(rPaM);
    aPaM.SetIndex(0);
    return aPaM;
}

EditPaM ImpEditEngine::CursorEndOfParagraph( const EditPaM& rPaM )
{
    EditPaM aPaM(rPaM);
    aPaM.SetIndex(rPaM.GetNode()->Len());
    return aPaM;
}

EditPaM ImpEditEngine::CursorStartOfDoc()
{
    EditPaM aPaM( maEditDoc.GetObject( 0 ), 0 );
    return aPaM;
}

EditPaM ImpEditEngine::CursorEndOfDoc()
{
    ContentNode* pLastNode = maEditDoc.GetObject( maEditDoc.Count()-1 );
    ParaPortion* pLastPortion = GetParaPortions().SafeGetObject( maEditDoc.Count()-1 );
    OSL_ENSURE( pLastNode && pLastPortion, "CursorEndOfDoc: Node or Portion not found" );
    if (!(pLastNode && pLastPortion))
        return EditPaM();

    if ( !pLastPortion->IsVisible() )
    {
        pLastNode = GetPrevVisNode( pLastPortion->GetNode() );
        OSL_ENSURE( pLastNode, "No visible paragraph?" );
        if ( !pLastNode )
            pLastNode = maEditDoc.GetObject( maEditDoc.Count()-1 );
    }

    EditPaM aPaM( pLastNode, pLastNode->Len() );
    return aPaM;
}

EditPaM ImpEditEngine::PageUp( const EditPaM& rPaM, EditView const * pView )
{
    tools::Rectangle aRect = PaMtoEditCursor( rPaM );
    Point aTopLeft = aRect.TopLeft();
    aTopLeft.AdjustY( -(pView->GetVisArea().GetHeight() *9/10) );
    aTopLeft.AdjustX(mnOnePixelInRef);
    if ( aTopLeft.Y() < 0 )
    {
        aTopLeft.setY( 0 );
    }
    return GetPaM( aTopLeft );
}

EditPaM ImpEditEngine::PageDown( const EditPaM& rPaM, EditView const * pView )
{
    tools::Rectangle aRect = PaMtoEditCursor( rPaM );
    Point aBottomRight = aRect.BottomRight();
    aBottomRight.AdjustY(pView->GetVisArea().GetHeight() *9/10 );
    aBottomRight.AdjustX(mnOnePixelInRef);
    tools::Long nHeight = GetTextHeight();
    if ( aBottomRight.Y() > nHeight )
    {
        aBottomRight.setY( nHeight-2 );
    }
    return GetPaM( aBottomRight );
}

EditPaM ImpEditEngine::WordLeft( const EditPaM& rPaM )
{
    const sal_Int32 nCurrentPos = rPaM.GetIndex();
    EditPaM aNewPaM( rPaM );
    if ( nCurrentPos == 0 )
    {
        // Previous paragraph...
        sal_Int32 nCurPara = maEditDoc.GetPos( aNewPaM.GetNode() );
        ContentNode* pPrevNode = maEditDoc.GetObject( --nCurPara );
        if ( pPrevNode )
        {
            aNewPaM.SetNode( pPrevNode );
            aNewPaM.SetIndex( pPrevNode->Len() );
        }
    }
    else
    {
        // we need to increase the position by 1 when retrieving the locale
        // since the attribute for the char left to the cursor position is returned
        EditPaM aTmpPaM( aNewPaM );
        if ( aTmpPaM.GetIndex() < rPaM.GetNode()->Len() )
            aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
        lang::Locale aLocale( GetLocale( aTmpPaM ) );

        uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
        i18n::Boundary aBoundary =
            _xBI->getWordBoundary(aNewPaM.GetNode()->GetString(), nCurrentPos, aLocale, css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true);
        if ( aBoundary.startPos >= nCurrentPos )
            aBoundary = _xBI->previousWord(
                aNewPaM.GetNode()->GetString(), nCurrentPos, aLocale, css::i18n::WordType::ANYWORD_IGNOREWHITESPACES);
        aNewPaM.SetIndex( ( aBoundary.startPos != -1 ) ? aBoundary.startPos : 0 );
    }

    return aNewPaM;
}

EditPaM ImpEditEngine::WordRight( const EditPaM& rPaM, sal_Int16 nWordType )
{
    const sal_Int32 nMax = rPaM.GetNode()->Len();
    EditPaM aNewPaM( rPaM );
    if ( aNewPaM.GetIndex() < nMax )
    {
        // we need to increase the position by 1 when retrieving the locale
        // since the attribute for the char left to the cursor position is returned
        EditPaM aTmpPaM( aNewPaM );
        aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
        lang::Locale aLocale( GetLocale( aTmpPaM ) );

        uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
        i18n::Boundary aBoundary = _xBI->nextWord(
            aNewPaM.GetNode()->GetString(), aNewPaM.GetIndex(), aLocale, nWordType);
        aNewPaM.SetIndex( aBoundary.startPos );
    }
    // not 'else', maybe the index reached nMax now...
    if ( aNewPaM.GetIndex() >= nMax )
    {
        // Next paragraph ...
        sal_Int32 nCurPara = maEditDoc.GetPos( aNewPaM.GetNode() );
        ContentNode* pNextNode = maEditDoc.GetObject( ++nCurPara );
        if ( pNextNode )
        {
            aNewPaM.SetNode( pNextNode );
            aNewPaM.SetIndex( 0 );
        }
    }
    return aNewPaM;
}

EditPaM ImpEditEngine::StartOfWord( const EditPaM& rPaM )
{
    EditPaM aNewPaM( rPaM );

    // we need to increase the position by 1 when retrieving the locale
    // since the attribute for the char left to the cursor position is returned
    EditPaM aTmpPaM( aNewPaM );
    if ( aTmpPaM.GetIndex() < rPaM.GetNode()->Len() )
        aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
    lang::Locale aLocale( GetLocale( aTmpPaM ) );

    uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
    // tdf#135761 - since this function is only used when a selection is deleted at the left,
    // change the search preference of the word boundary from forward to backward.
    // For further details of a deletion of a selection check ImpEditEngine::DeleteLeftOrRight.
    i18n::Boundary aBoundary = _xBI->getWordBoundary(
        rPaM.GetNode()->GetString(), rPaM.GetIndex(), aLocale, css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, false);

    aNewPaM.SetIndex( aBoundary.startPos );
    return aNewPaM;
}

EditPaM ImpEditEngine::EndOfWord( const EditPaM& rPaM )
{
    EditPaM aNewPaM( rPaM );

    // we need to increase the position by 1 when retrieving the locale
    // since the attribute for the char left to the cursor position is returned
    EditPaM aTmpPaM( aNewPaM );
    if ( aTmpPaM.GetIndex() < rPaM.GetNode()->Len() )
        aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
    lang::Locale aLocale( GetLocale( aTmpPaM ) );

    uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
    i18n::Boundary aBoundary = _xBI->getWordBoundary(
        rPaM.GetNode()->GetString(), rPaM.GetIndex(), aLocale, css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true);

    aNewPaM.SetIndex( aBoundary.endPos );
    return aNewPaM;
}

EditSelection ImpEditEngine::SelectWord( const EditSelection& rCurSel, sal_Int16 nWordType, bool bAcceptStartOfWord, bool bAcceptEndOfWord )
{
    EditSelection aNewSel( rCurSel );
    EditPaM aPaM( rCurSel.Max() );

    // we need to increase the position by 1 when retrieving the locale
    // since the attribute for the char left to the cursor position is returned
    EditPaM aTmpPaM( aPaM );
    if ( aTmpPaM.GetIndex() < aPaM.GetNode()->Len() )
        aTmpPaM.SetIndex( aTmpPaM.GetIndex() + 1 );
    lang::Locale aLocale( GetLocale( aTmpPaM ) );

    uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
    sal_Int16 nType = _xBI->getWordType(
        aPaM.GetNode()->GetString(), aPaM.GetIndex(), aLocale);

    if ( nType == i18n::WordType::ANY_WORD )
    {
        i18n::Boundary aBoundary = _xBI->getWordBoundary(
            aPaM.GetNode()->GetString(), aPaM.GetIndex(), aLocale, nWordType,
            aPaM.GetNode()->GetString().getLength() == aPaM.GetIndex() ? false : true);

        // don't select when cursor at end of word
        if ( ( aBoundary.endPos > aPaM.GetIndex() || ( bAcceptEndOfWord && aBoundary.endPos == aPaM.GetIndex() ) ) &&
             ( ( aBoundary.startPos < aPaM.GetIndex() ) || ( bAcceptStartOfWord && ( aBoundary.startPos == aPaM.GetIndex() ) ) ) )
        {
            aNewSel.Min().SetIndex( aBoundary.startPos );
            aNewSel.Max().SetIndex( aBoundary.endPos );
        }
    }

    return aNewSel;
}

EditSelection ImpEditEngine::SelectSentence( const EditSelection& rCurSel )
    const
{
    uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );
    const EditPaM& rPaM = rCurSel.Min();
    const ContentNode* pNode = rPaM.GetNode();
    // #i50710# line breaks are marked with 0x01 - the break iterator prefers 0x0a for that
    const OUString sParagraph = pNode->GetString().replaceAll("\x01""\x0a");
    //return Null if search starts at the beginning of the string
    sal_Int32 nStart = rPaM.GetIndex() ? _xBI->beginOfSentence( sParagraph, rPaM.GetIndex(), GetLocale( rPaM ) ) : 0;

    sal_Int32 nEnd = _xBI->endOfSentence(
        pNode->GetString(), rPaM.GetIndex(), GetLocale(rPaM));

    EditSelection aNewSel( rCurSel );
    OSL_ENSURE(pNode->Len() ? (nStart < pNode->Len()) : (nStart == 0), "sentence start index out of range");
    OSL_ENSURE(nEnd <= pNode->Len(), "sentence end index out of range");
    aNewSel.Min().SetIndex( nStart );
    aNewSel.Max().SetIndex( nEnd );
    return aNewSel;
}

bool ImpEditEngine::IsInputSequenceCheckingRequired( sal_Unicode nChar, const EditSelection& rCurSel ) const
{
    uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );

    // get the index that really is first
    const sal_Int32 nFirstPos = std::min(rCurSel.Min().GetIndex(), rCurSel.Max().GetIndex());

    bool bIsSequenceChecking =
        SvtCTLOptions::IsCTLFontEnabled() &&
        SvtCTLOptions::IsCTLSequenceChecking() &&
        nFirstPos != 0 && /* first char needs not to be checked */
        _xBI.is() && i18n::ScriptType::COMPLEX == _xBI->getScriptType( OUString( nChar ), 0 );

    return bIsSequenceChecking;
}

void ImpEditEngine::InitScriptTypes( sal_Int32 nPara )
{
    ParaPortion* pParaPortion = GetParaPortions().SafeGetObject( nPara );
    if (!pParaPortion)
        return;

    ScriptTypePosInfos& rTypes = pParaPortion->getScriptTypePosInfos();
    rTypes.clear();

    ContentNode* pNode = pParaPortion->GetNode();
    if ( !pNode->Len() )
        return;

    uno::Reference < i18n::XBreakIterator > _xBI( ImplGetBreakIterator() );

    OUString aText = pNode->GetString();

    // To handle fields put the character from the field in the string,
    // because endOfScript( ... ) will skip the CH_FEATURE, because this is WEAK
    const EditCharAttrib* pField = pNode->GetCharAttribs().FindNextAttrib( EE_FEATURE_FIELD, 0 );
    while ( pField )
    {
        const OUString aFldText = static_cast<const EditCharAttribField*>(pField)->GetFieldValue();
        if ( !aFldText.isEmpty() )
        {
            aText = aText.replaceAt( pField->GetStart(), 1, aFldText.subView(0,1) );
--> --------------------

--> maximum size reached

--> --------------------

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

¤ Dauer der Verarbeitung: 0.22 Sekunden  ¤

*© 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.