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

Quelle  edit.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 <comphelper/string.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <o3tl/string_view.hxx>
#include <officecfg/Office/Common.hxx>

#include <vcl/builder.hxx>
#include <vcl/cursor.hxx>
#include <vcl/event.hxx>
#include <vcl/ptrstyle.hxx>
#include <vcl/specialchars.hxx>
#include <vcl/toolkit/edit.hxx>
#include <vcl/transfer.hxx>
#include <vcl/unohelp2.hxx>
#include <vcl/uitest/uiobject.hxx>
#include <vcl/weld.hxx>

#include <accessibility/vclxaccessibleedit.hxx>
#include <window.h>
#include <svdata.hxx>
#include <strings.hrc>

#include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp>
#include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>
#include <com/sun/star/i18n/BreakIterator.hpp>
#include <com/sun/star/i18n/CharacterIteratorMode.hpp>
#include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
#include <com/sun/star/i18n/InputSequenceChecker.hpp>
#include <com/sun/star/i18n/ScriptType.hpp>
#include <com/sun/star/i18n/WordType.hpp>

#include <algorithm>
#include <memory>
#include <string_view>
#include <utility>

// - Redo
// - if Tracking-Cancel recreate DefaultSelection

static FncGetSpecialChars pImplFncGetSpecialChars = nullptr;

#define EDIT_ALIGN_LEFT             1
#define EDIT_ALIGN_CENTER           2
#define EDIT_ALIGN_RIGHT            3

#define EDIT_DEL_LEFT               1
#define EDIT_DEL_RIGHT              2

#define EDIT_DELMODE_SIMPLE         11
#define EDIT_DELMODE_RESTOFWORD     12
#define EDIT_DELMODE_RESTOFCONTENT  13

struct DragDropInfo
{
    vcl::Cursor     aCursor;
    Selection       aDndStartSel;
    sal_Int32       nDropPos = 0;
    bool            bStarterOfDD = false;
    bool            bDroppedInMe = false;
    bool            bVisCursor = false;
    bool            bIsStringSupported = false;

    DragDropInfo()
    {
        aCursor.SetStyle( CURSOR_SHADOW );
    }
};

struct Impl_IMEInfos
{
    OUString      aOldTextAfterStartPos;
    std::unique_ptr<ExtTextInputAttr[]>
                  pAttribs;
    sal_Int32     nPos;
    sal_Int32     nLen;
    bool          bCursor;
    bool          bWasCursorOverwrite;

    Impl_IMEInfos(sal_Int32 nPos, OUString aOldTextAfterStartPos);

    void        CopyAttribs(const ExtTextInputAttr* pA, sal_Int32 nL);
    void        DestroyAttribs();
};

Impl_IMEInfos::Impl_IMEInfos(sal_Int32 nP, OUString _aOldTextAfterStartPos)
    : aOldTextAfterStartPos(std::move(_aOldTextAfterStartPos)),
    nPos(nP),
    nLen(0),
    bCursor(true),
    bWasCursorOverwrite(false)
{
}

void Impl_IMEInfos::CopyAttribs(const ExtTextInputAttr* pA, sal_Int32 nL)
{
    nLen = nL;
    pAttribs.reset(new ExtTextInputAttr[ nL ]);
    memcpy( pAttribs.get(), pA, nL*sizeof(ExtTextInputAttr) );
}

void Impl_IMEInfos::DestroyAttribs()
{
    pAttribs.reset();
    nLen = 0;
}

Edit::Edit( WindowType eType )
    : Control( eType )
{
    ImplInitEditData();
}

Edit::Edit( vcl::Window* pParent, WinBits nStyle )
    : Control( WindowType::EDIT )
{
    ImplInitEditData();
    ImplInit( pParent, nStyle );
}

void Edit::SetWidthInChars(sal_Int32 nWidthInChars)
{
    if (mnWidthInChars != nWidthInChars)
    {
        mnWidthInChars = nWidthInChars;
        queue_resize();
    }
}

void Edit::setMaxWidthChars(sal_Int32 nWidth)
{
    if (nWidth != mnMaxWidthChars)
    {
        mnMaxWidthChars = nWidth;
        queue_resize();
    }
}

bool Edit::set_property(const OUString &rKey, const OUString &rValue)
{
    if (rKey == "width-chars")
        SetWidthInChars(rValue.toInt32());
    else if (rKey == "max-width-chars")
        setMaxWidthChars(rValue.toInt32());
    else if (rKey == "max-length")
    {
        sal_Int32 nTextLen = rValue.toInt32();
        SetMaxTextLen(nTextLen == 0 ? EDIT_NOLIMIT : nTextLen);
    }
    else if (rKey == "editable")
    {
        SetReadOnly(!toBool(rValue));
    }
    else if (rKey == "overwrite-mode")
    {
        SetInsertMode(!toBool(rValue));
    }
    else if (rKey == "visibility")
    {
        mbPassword = false;
        if (!toBool(rValue))
            mbPassword = true;
    }
    else if (rKey == "placeholder-text")
        SetPlaceholderText(rValue);
    else if (rKey == "shadow-type")
    {
        if (GetStyle() & WB_BORDER)
            SetBorderStyle(rValue == "none" ? WindowBorderStyle::MONO : WindowBorderStyle::NORMAL);
    }
    else
        return Control::set_property(rKey, rValue);
    return true;
}

Edit::~Edit()
{
    disposeOnce();
}

void Edit::dispose()
{
    mpUIBuilder.reset();
    mpDDInfo.reset();

    vcl::Cursor* pCursor = GetCursor();
    if ( pCursor )
    {
        SetCursor( nullptr );
        delete pCursor;
    }

    mpIMEInfos.reset();

    if ( mxDnDListener.is() )
    {
        if ( GetDragGestureRecognizer().is() )
        {
            GetDragGestureRecognizer()->removeDragGestureListener( mxDnDListener );
        }
        if ( GetDropTarget().is() )
        {
            GetDropTarget()->removeDropTargetListener( mxDnDListener );
        }

        mxDnDListener->disposing(css::lang::EventObject());  // #95154# #96585# Empty Source means it's the Client
        mxDnDListener.clear();
    }

    SetType(WindowType::WINDOW);

    mpSubEdit.disposeAndClear();
    Control::dispose();
}

void Edit::ImplInitEditData()
{
    mpSubEdit               = VclPtr<Edit>();
    mpFilterText            = nullptr;
    mnXOffset               = 0;
    mnAlign                 = EDIT_ALIGN_LEFT;
    mnMaxTextLen            = EDIT_NOLIMIT;
    mnWidthInChars          = -1;
    mnMaxWidthChars         = -1;
    mbInternModified        = false;
    mbReadOnly              = false;
    mbInsertMode            = true;
    mbClickedInSelection    = false;
    mbActivePopup           = false;
    mbIsSubEdit             = false;
    mbForceControlBackground = false;
    mbPassword              = false;
    mpDDInfo                = nullptr;
    mpIMEInfos              = nullptr;
    mcEchoChar              = 0;

    // no default mirroring for Edit controls
    // note: controls that use a subedit will revert this (SpinField, ComboBox)
    EnableRTL( false );

    mxDnDListener = new vcl::unohelper::DragAndDropWrapper( this );
}

bool Edit::ImplUseNativeBorder(vcl::RenderContext const & rRenderContext, WinBits nStyle) const
{
    bool bRet = rRenderContext.IsNativeControlSupported(ImplGetNativeControlType(),
                                                        ControlPart::HasBackgroundTexture)
                                 && ((nStyle & WB_BORDER) && !(nStyle & WB_NOBORDER));
    if (!bRet && mbIsSubEdit)
    {
        vcl::Window* pWindow = GetParent();
        nStyle = pWindow->GetStyle();
        bRet = pWindow->IsNativeControlSupported(ImplGetNativeControlType(),
                                                 ControlPart::HasBackgroundTexture)
               && ((nStyle & WB_BORDER) && !(nStyle & WB_NOBORDER));
    }
    return bRet;
}

void Edit::ImplInit(vcl::Window* pParent, WinBits nStyle)
{
    nStyle = ImplInitStyle(nStyle);

    if (!(nStyle & (WB_CENTER | WB_RIGHT)))
        nStyle |= WB_LEFT;

    Control::ImplInit(pParent, nStyle, nullptr);

    mbReadOnly = (nStyle & WB_READONLY) != 0;

    mnAlign = EDIT_ALIGN_LEFT;

    // hack: right align until keyinput and cursor travelling works
    if( IsRTLEnabled() )
        mnAlign = EDIT_ALIGN_RIGHT;

    if ( nStyle & WB_RIGHT )
        mnAlign = EDIT_ALIGN_RIGHT;
    else if ( nStyle & WB_CENTER )
        mnAlign = EDIT_ALIGN_CENTER;

    SetCursor( new vcl::Cursor );

    SetPointer( PointerStyle::Text );
    ApplySettings(*GetOutDev());

    css::uno::Reference<css::datatransfer::dnd::XDragGestureRecognizer> xDGR = GetDragGestureRecognizer();
    if ( xDGR.is() )
    {
        xDGR->addDragGestureListener( mxDnDListener );
        GetDropTarget()->addDropTargetListener( mxDnDListener );
        GetDropTarget()->setActive( true );
        GetDropTarget()->setDefaultActions(css::datatransfer::dnd::DNDConstants::ACTION_COPY_OR_MOVE);
    }
}

WinBits Edit::ImplInitStyle( WinBits nStyle )
{
    if ( !(nStyle & WB_NOTABSTOP) )
        nStyle |= WB_TABSTOP;
    if ( !(nStyle & WB_NOGROUP) )
        nStyle |= WB_GROUP;

    return nStyle;
}

bool Edit::IsCharInput( const KeyEvent& rKeyEvent )
{
    // In the future we must use new Unicode functions for this
    sal_Unicode cCharCode = rKeyEvent.GetCharCode();
    return ((cCharCode >= 32) && (cCharCode != 127) &&
            !rKeyEvent.GetKeyCode().IsMod3() &&
            !rKeyEvent.GetKeyCode().IsMod2() &&
            !rKeyEvent.GetKeyCode().IsMod1() );
}

void Edit::ApplySettings(vcl::RenderContext& rRenderContext)
{
    Control::ApplySettings(rRenderContext);

    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();

    const vcl::Font& aFont = rStyleSettings.GetFieldFont();
    ApplyControlFont(rRenderContext, aFont);

    ImplClearLayoutData();

    Color aTextColor = rStyleSettings.GetFieldTextColor();
    ApplyControlForeground(rRenderContext, aTextColor);

    if (IsControlBackground())
    {
        rRenderContext.SetBackground(GetControlBackground());
        rRenderContext.SetFillColor(GetControlBackground());

        if (ImplUseNativeBorder(rRenderContext, GetStyle()))
        {
            // indicates that no non-native drawing of background should take place
            mpWindowImpl->mnNativeBackground = ControlPart::Entire;
        }
    }
    else if (ImplUseNativeBorder(rRenderContext, GetStyle()))
    {
        // Transparent background
        rRenderContext.SetBackground();
        rRenderContext.SetFillColor();
    }
    else
    {
        rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
        rRenderContext.SetFillColor(rStyleSettings.GetFieldColor());
    }
}

tools::Long Edit::ImplGetExtraXOffset() const
{
    // MT 09/2002: nExtraOffsetX should become a member, instead of checking every time,
    // but I need an incompatible update for this...
    // #94095# Use extra offset only when edit has a border
    tools::Long nExtraOffset = 0;
    if( ( GetStyle() & WB_BORDER ) || ( mbIsSubEdit && ( GetParent()->GetStyle() & WB_BORDER ) ) )
        nExtraOffset = 2;

    return nExtraOffset;
}

tools::Long Edit::ImplGetExtraYOffset() const
{
    tools::Long nExtraOffset = 0;
    ControlType eCtrlType = ImplGetNativeControlType();
    if (eCtrlType != ControlType::EditboxNoBorder)
    {
        // add some space between text entry and border
        nExtraOffset = 2;
    }
    return nExtraOffset;
}

OUString Edit::ImplGetText() const
{
    if ( mcEchoChar || mbPassword )
    {
        sal_Unicode cEchoChar;
        if ( mcEchoChar )
            cEchoChar = mcEchoChar;
        else
            cEchoChar = u'\x2022';
        OUStringBuffer aText(maText.getLength());
        comphelper::string::padToLength(aText, maText.getLength(), cEchoChar);
        return aText.makeStringAndClear();
    }
    else
        return maText.toString();
}

void Edit::ImplInvalidateOrRepaint()
{
    if( IsPaintTransparent() )
    {
        Invalidate();
        // FIXME: this is currently only on macOS
        if( ImplGetSVData()->maNWFData.mbNoFocusRects )
            PaintImmediately();
    }
    else
        Invalidate();
}

tools::Long Edit::ImplGetTextYPosition() const
{
    if ( GetStyle() & WB_TOP )
        return ImplGetExtraXOffset();
    else if ( GetStyle() & WB_BOTTOM )
        return GetOutputSizePixel().Height() - GetTextHeight() - ImplGetExtraXOffset();
    return ( GetOutputSizePixel().Height() - GetTextHeight() ) / 2;
}

void Edit::ImplRepaint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle)
{
    if (!IsReallyVisible())
        return;

    ApplySettings(rRenderContext);

    const OUString aText = ImplGetText();
    const sal_Int32 nLen = aText.getLength();

    KernArray aDX;
    if (nLen)
        GetOutDev()->GetCaretPositions(aText, aDX, 0, nLen);

    tools::Long nTH = GetTextHeight();
    Point aPos(mnXOffset, ImplGetTextYPosition());

    vcl::Cursor* pCursor = GetCursor();
    bool bVisCursor = pCursor && pCursor->IsVisible();
    if (pCursor)
        pCursor->Hide();

    ImplClearBackground(rRenderContext, rRectangle, 0, GetOutputSizePixel().Width()-1);

    bool bPaintPlaceholderText = aText.isEmpty() && !maPlaceholderText.isEmpty();

    const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings();

    if (!IsEnabled() || bPaintPlaceholderText)
        rRenderContext.SetTextColor(rStyleSettings.GetDisableColor());

    // Set background color of the normal text
    if (mbForceControlBackground && IsControlBackground())
    {
        // check if we need to set ControlBackground even in NWF case
        rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR);
        rRenderContext.SetLineColor();
        rRenderContext.SetFillColor(GetControlBackground());
        rRenderContext.DrawRect(tools::Rectangle(aPos, Size(GetOutputSizePixel().Width() - 2 * mnXOffset, GetOutputSizePixel().Height())));
        rRenderContext.Pop();

        rRenderContext.SetTextFillColor(GetControlBackground());
    }
    else if (IsPaintTransparent() || ImplUseNativeBorder(rRenderContext, GetStyle()))
        rRenderContext.SetTextFillColor();
    else
        rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());

    ImplPaintBorder(rRenderContext);

    bool bDrawSelection = maSelection.Len() && (HasFocus() || (GetStyle() & WB_NOHIDESELECTION) || mbActivePopup);

    aPos.setX( mnXOffset + ImplGetExtraXOffset() );
    if (bPaintPlaceholderText)
    {
        rRenderContext.DrawText(aPos, maPlaceholderText);
    }
    else if (!bDrawSelection && !mpIMEInfos)
    {
        rRenderContext.DrawText(aPos, aText, 0, nLen);
    }
    else
    {
        // save graphics state
        rRenderContext.Push();
        // first calculate highlighted and non highlighted clip regions
        vcl::Region aHighlightClipRegion;
        vcl::Region aNormalClipRegion;
        Selection aTmpSel(maSelection);
        aTmpSel.Normalize();
        // selection is highlighted
        for(sal_Int32 i = 0; i < nLen; ++i)
        {
            tools::Rectangle aRect(aPos, Size(10, nTH));
            aRect.SetLeft( aDX[2 * i] + mnXOffset + ImplGetExtraXOffset() );
            aRect.SetRight( aDX[2 * i + 1] + mnXOffset + ImplGetExtraXOffset() );
            aRect.Normalize();
            bool bHighlight = false;
            if (i >= aTmpSel.Min() && i < aTmpSel.Max())
                bHighlight = true;

            if (mpIMEInfos && mpIMEInfos->pAttribs &&
                i >= mpIMEInfos->nPos && i < (mpIMEInfos->nPos+mpIMEInfos->nLen) &&
                (mpIMEInfos->pAttribs[i - mpIMEInfos->nPos] & ExtTextInputAttr::Highlight))
            {
                bHighlight = true;
            }

            if (bHighlight)
                aHighlightClipRegion.Union(aRect);
            else
                aNormalClipRegion.Union(aRect);
        }
        // draw normal text
        Color aNormalTextColor = rRenderContext.GetTextColor();
        rRenderContext.SetClipRegion(aNormalClipRegion);

        if (IsPaintTransparent())
            rRenderContext.SetTextFillColor();
        else
        {
            // Set background color when part of the text is selected
            if (ImplUseNativeBorder(rRenderContext, GetStyle()))
            {
                if( mbForceControlBackground && IsControlBackground() )
                    rRenderContext.SetTextFillColor(GetControlBackground());
                else
                    rRenderContext.SetTextFillColor();
            }
            else
            {
                rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
            }
        }
        rRenderContext.DrawText(aPos, aText, 0, nLen);

        // draw highlighted text
        rRenderContext.SetClipRegion(aHighlightClipRegion);
        rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
        rRenderContext.SetTextFillColor(rStyleSettings.GetHighlightColor());
        rRenderContext.DrawText(aPos, aText, 0, nLen);

        // if IME info exists loop over portions and output different font attributes
        if (mpIMEInfos && mpIMEInfos->pAttribs)
        {
            for(int n = 0; n < 2; n++)
            {
                vcl::Region aRegion;
                if (n == 0)
                {
                    rRenderContext.SetTextColor(aNormalTextColor);
                    if (IsPaintTransparent())
                        rRenderContext.SetTextFillColor();
                    else
                        rRenderContext.SetTextFillColor(IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor());
                    aRegion = aNormalClipRegion;
                }
                else
                {
                    rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor());
                    rRenderContext.SetTextFillColor(rStyleSettings.GetHighlightColor());
                    aRegion = aHighlightClipRegion;
                }

                for(int i = 0; i < mpIMEInfos->nLen; )
                {
                    ExtTextInputAttr nAttr = mpIMEInfos->pAttribs[i];
                    vcl::Region aClip;
                    int nIndex = i;
                    while (nIndex < mpIMEInfos->nLen && mpIMEInfos->pAttribs[nIndex] == nAttr)  // #112631# check nIndex before using it
                    {
                        tools::Rectangle aRect( aPos, Size( 10, nTH ) );
                        aRect.SetLeft( aDX[2 * (nIndex + mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraXOffset() );
                        aRect.SetRight( aDX[2 * (nIndex + mpIMEInfos->nPos) + 1] + mnXOffset + ImplGetExtraXOffset() );
                        aRect.Normalize();
                        aClip.Union(aRect);
                        nIndex++;
                    }
                    i = nIndex;
                    aClip.Intersect(aRegion);
                    if (!aClip.IsEmpty() && nAttr != ExtTextInputAttr::NONE)
                    {
                        vcl::Font aFont = rRenderContext.GetFont();
                        if (nAttr & ExtTextInputAttr::Underline)
                            aFont.SetUnderline(LINESTYLE_SINGLE);
                        else if (nAttr & ExtTextInputAttr::DoubleUnderline)
                            aFont.SetUnderline(LINESTYLE_DOUBLE);
                        else if (nAttr & ExtTextInputAttr::BoldUnderline)
                            aFont.SetUnderline( LINESTYLE_BOLD);
                        else if (nAttr & ExtTextInputAttr::DottedUnderline)
                            aFont.SetUnderline( LINESTYLE_DOTTED);
                        else if (nAttr & ExtTextInputAttr::DashDotUnderline)
                            aFont.SetUnderline( LINESTYLE_DASHDOT);
                        else if (nAttr & ExtTextInputAttr::GrayWaveline)
                        {
                            aFont.SetUnderline(LINESTYLE_WAVE);
                            rRenderContext.SetTextLineColor(COL_LIGHTGRAY);
                        }
                        rRenderContext.SetFont(aFont);

                        if (nAttr & ExtTextInputAttr::RedText)
                            rRenderContext.SetTextColor(COL_RED);
                        else if (nAttr & ExtTextInputAttr::HalfToneText)
                            rRenderContext.SetTextColor(COL_LIGHTGRAY);

                        rRenderContext.SetClipRegion(aClip);
                        rRenderContext.DrawText(aPos, aText, 0, nLen);
                    }
                }
            }
        }

        // restore graphics state
        rRenderContext.Pop();
    }

    if (bVisCursor && (!mpIMEInfos || mpIMEInfos->bCursor))
        pCursor->Show();
}

void Edit::ImplDelete( const Selection& rSelection, sal_uInt8 nDirection, sal_uInt8 nMode )
{
    const sal_Int32 nTextLen = ImplGetText().getLength();

    // deleting possible?
    if ( !rSelection.Len() &&
         (((rSelection.Min() == 0) && (nDirection == EDIT_DEL_LEFT)) ||
          ((rSelection.Max() == nTextLen) && (nDirection == EDIT_DEL_RIGHT))) )
        return;

    ImplClearLayoutData();

    Selection aSelection( rSelection );
    aSelection.Normalize();

    if ( !aSelection.Len() )
    {
        css::uno::Reference<css::i18n::XBreakIterator> xBI = ImplGetBreakIterator();
        if ( nDirection == EDIT_DEL_LEFT )
        {
            if ( nMode == EDIT_DELMODE_RESTOFWORD )
            {
                const OUString sText = maText.toString();
                css::i18n::Boundary aBoundary = xBI->getWordBoundary(sText, aSelection.Min(),
                        GetSettings().GetLanguageTag().getLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true);
                auto startPos = aBoundary.startPos;
                if ( startPos == aSelection.Min() )
                {
                    aBoundary = xBI->previousWord(sText, aSelection.Min(),
                            GetSettings().GetLanguageTag().getLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES);
                    startPos = std::max(aBoundary.startPos, sal_Int32(0));
                }
                aSelection.Min() = startPos;
            }
            else if ( nMode == EDIT_DELMODE_RESTOFCONTENT )
               {
                aSelection.Min() = 0;
            }
            else
            {
                sal_Int32 nCount = 1;
                aSelection.Min() = xBI->previousCharacters(maText.toString(), aSelection.Min(),
                        GetSettings().GetLanguageTag().getLocale(), css::i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount);
            }
        }
        else
        {
            if ( nMode == EDIT_DELMODE_RESTOFWORD )
            {
                css::i18n::Boundary aBoundary = xBI->nextWord( maText.toString(), aSelection.Max(),
                        GetSettings().GetLanguageTag().getLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES );
                aSelection.Max() = aBoundary.startPos;
            }
            else if ( nMode == EDIT_DELMODE_RESTOFCONTENT )
            {
                aSelection.Max() = nTextLen;
            }
            else
            {
                sal_Int32 nCount = 1;
                aSelection.Max() = xBI->nextCharacters(maText.toString(), aSelection.Max(),
                        GetSettings().GetLanguageTag().getLocale(), css::i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount);
            }
        }
    }

    const auto nSelectionMin = aSelection.Min();
    maText.remove( nSelectionMin, aSelection.Len() );
    maSelection.Min() = nSelectionMin;
    maSelection.Max() = nSelectionMin;
    ImplAlignAndPaint();
    mbInternModified = true;
}

OUString Edit::ImplGetValidString( const OUString& rString )
{
    OUString aValidString = rString.replaceAll("\n""").replaceAll("\r""");
    aValidString = aValidString.replace('\t'' ');
    return aValidString;
}

css::uno::Reference<css::i18n::XBreakIterator> const& Edit::ImplGetBreakIterator()
{
    if (!mxBreakIterator)
        mxBreakIterator = css::i18n::BreakIterator::create(::comphelper::getProcessComponentContext());
    return mxBreakIterator;
}

css::uno::Reference<css::i18n::XExtendedInputSequenceChecker> const& Edit::ImplGetInputSequenceChecker()
{
    if (!mxISC.is())
        mxISC = css::i18n::InputSequenceChecker::create(::comphelper::getProcessComponentContext());
    return mxISC;
}

void Edit::ShowTruncationWarning(weld::Widget* pParent)
{
    std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(pParent, VclMessageType::Warning,
                                              VclButtonsType::Ok, VclResId(SV_EDIT_WARNING_STR)));
    xBox->run();
}

bool Edit::ImplTruncateToMaxLen( OUString& rStr, sal_Int32 nSelectionLen ) const
{
    bool bWasTruncated = false;
    if (maText.getLength() - nSelectionLen > mnMaxTextLen - rStr.getLength())
    {
        sal_Int32 nErasePos = mnMaxTextLen - maText.getLength() + nSelectionLen;
        rStr = rStr.copy( 0, nErasePos );
        bWasTruncated = true;
    }
    return bWasTruncated;
}

void Edit::ImplInsertText( const OUString& rStr, const Selection* pNewSel, bool bIsUserInput )
{
    Selection aSelection( maSelection );
    aSelection.Normalize();

    OUString aNewText( ImplGetValidString( rStr ) );

    // as below, if there's no selection, but we're in overwrite mode and not beyond
    // the end of the existing text then that's like a selection of 1
    auto nSelectionLen = aSelection.Len();
    if (!nSelectionLen && !mbInsertMode && aSelection.Max() < maText.getLength())
        nSelectionLen = 1;
    ImplTruncateToMaxLen( aNewText, nSelectionLen );

    ImplClearLayoutData();

    if ( aSelection.Len() )
        maText.remove( aSelection.Min(), aSelection.Len() );
    else if (!mbInsertMode && aSelection.Max() < maText.getLength())
        maText.remove( aSelection.Max(), 1 );

    // take care of input-sequence-checking now
    if (bIsUserInput && !rStr.isEmpty())
    {
        SAL_WARN_IF( rStr.getLength() != 1, "vcl""unexpected string length. User input is expected to provide 1 char only!" );

        // determine if input-sequence-checking should be applied or not

        css::uno::Reference<css::i18n::XBreakIterator> xBI = ImplGetBreakIterator();
        bool bIsInputSequenceChecking = rStr.getLength() == 1 &&
                officecfg::Office::Common::I18N::CTL::CTLFont::get() &&
                officecfg::Office::Common::I18N::CTL::CTLSequenceChecking::get() &&
                aSelection.Min() > 0 && /* first char needs not to be checked */
                xBI.is() && css::i18n::ScriptType::COMPLEX == xBI->getScriptType( rStr, 0 );

        if (bIsInputSequenceChecking)
        {
            css::uno::Reference <css::i18n::XExtendedInputSequenceChecker> xISC = ImplGetInputSequenceChecker();
            if (xISC.is())
            {
                sal_Unicode cChar = rStr[0];
                sal_Int32 nTmpPos = aSelection.Min();
                sal_Int16 nCheckMode = officecfg::Office::Common::I18N::CTL::CTLSequenceCheckingRestricted::get()?
                        css::i18n::InputSequenceCheckMode::STRICT : css::i18n::InputSequenceCheckMode::BASIC;

                // the text that needs to be checked is only the one
                // before the current cursor position
                const OUString aOldText( maText.subView(0, nTmpPos) );
                OUString aTmpText( aOldText );
                if (officecfg::Office::Common::I18N::CTL::CTLSequenceCheckingTypeAndReplace::get())
                {
                    xISC->correctInputSequence( aTmpText, nTmpPos - 1, cChar, nCheckMode );

                    // find position of first character that has changed
                    sal_Int32 nOldLen = aOldText.getLength();
                    sal_Int32 nTmpLen = aTmpText.getLength();
                    const sal_Unicode *pOldTxt = aOldText.getStr();
                    const sal_Unicode *pTmpTxt = aTmpText.getStr();
                    sal_Int32 nChgPos = 0;
                    while ( nChgPos < nOldLen && nChgPos < nTmpLen &&
                            pOldTxt[nChgPos] == pTmpTxt[nChgPos] )
                        ++nChgPos;

                    const OUString aChgText( aTmpText.copy( nChgPos ) );

                    // remove text from first pos to be changed to current pos
                    maText.remove( nChgPos, nTmpPos - nChgPos );

                    if (!aChgText.isEmpty())
                    {
                        aNewText = aChgText;
                        aSelection.Min() = nChgPos; // position for new text to be inserted
                    }
                    else
                        aNewText.clear();
                }
                else
                {
                    // should the character be ignored (i.e. not get inserted) ?
                    if (!xISC->checkInputSequence( aOldText, nTmpPos - 1, cChar, nCheckMode ))
                        aNewText.clear();
                }
            }
        }

        // at this point now we will insert the non-empty text 'normally' some lines below...
    }

    if ( !aNewText.isEmpty() )
        maText.insert( aSelection.Min(), aNewText );

    if ( !pNewSel )
    {
        maSelection.Min() = aSelection.Min() + aNewText.getLength();
        maSelection.Max() = maSelection.Min();
    }
    else
    {
        maSelection = *pNewSel;
        if ( maSelection.Min() > maText.getLength() )
            maSelection.Min() = maText.getLength();
        if ( maSelection.Max() > maText.getLength() )
            maSelection.Max() = maText.getLength();
    }

    ImplAlignAndPaint();
    mbInternModified = true;
}

void Edit::ImplSetText( const OUString& rText, const Selection* pNewSelection )
{
    // we delete text by "selecting" the old text completely then calling InsertText; this is flicker free
    if ( ( rText.getLength() > mnMaxTextLen ) ||
         ( std::u16string_view(rText) == std::u16string_view(maText)
           && (!pNewSelection || (*pNewSelection == maSelection)) ) )
        return;

    ImplClearLayoutData();
    maSelection.Min() = 0;
    maSelection.Max() = maText.getLength();
    if ( mnXOffset || HasPaintEvent() )
    {
        mnXOffset = 0;
        maText = ImplGetValidString( rText );

        // #i54929# recalculate mnXOffset before ImplSetSelection,
        // else cursor ends up in wrong position
        ImplAlign();

        if ( pNewSelection )
            ImplSetSelection( *pNewSelection, false );

        if ( mnXOffset && !pNewSelection )
            maSelection.Max() = 0;

        Invalidate();
    }
    else
        ImplInsertText( rText, pNewSelection );

    CallEventListeners( VclEventId::EditModify );
}

ControlType Edit::ImplGetNativeControlType() const
{
    ControlType nCtrl = ControlType::Generic;
    const vcl::Window* pControl = mbIsSubEdit ? GetParent() : this;

    switch (pControl->GetType())
    {
        case WindowType::COMBOBOX:
        case WindowType::PATTERNBOX:
        case WindowType::NUMERICBOX:
        case WindowType::METRICBOX:
        case WindowType::CURRENCYBOX:
        case WindowType::DATEBOX:
        case WindowType::TIMEBOX:
        case WindowType::LONGCURRENCYBOX:
            nCtrl = ControlType::Combobox;
            break;

        case WindowType::MULTILINEEDIT:
            if ( GetWindow( GetWindowType::Border ) != this )
                nCtrl = ControlType::MultilineEditbox;
            else
                nCtrl = ControlType::EditboxNoBorder;
            break;

        case WindowType::EDIT:
        case WindowType::PATTERNFIELD:
        case WindowType::METRICFIELD:
        case WindowType::CURRENCYFIELD:
        case WindowType::DATEFIELD:
        case WindowType::TIMEFIELD:
        case WindowType::SPINFIELD:
        case WindowType::FORMATTEDFIELD:
            if (pControl->GetStyle() & WB_SPIN)
                nCtrl = ControlType::Spinbox;
            else
            {
                if (GetWindow(GetWindowType::Border) != this)
                    nCtrl = ControlType::Editbox;
                else
                    nCtrl = ControlType::EditboxNoBorder;
            }
            break;

        default:
            nCtrl = ControlType::Editbox;
    }
    return nCtrl;
}

void Edit::ImplClearBackground(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle, tools::Long nXStart, tools::Long nXEnd )
{
    /*
    * note: at this point the cursor must be switched off already
    */

    tools::Rectangle aRect(Point(), GetOutputSizePixel());
    aRect.SetLeft( nXStart );
    aRect.SetRight( nXEnd );

    if( !(ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent()))
        rRenderContext.Erase(aRect);
    else if (SupportsDoubleBuffering() && mbIsSubEdit)
    {
        // ImplPaintBorder() is a NOP, we have a native border, and this is a sub-edit of a control.
        // That means we have to draw the parent native widget to paint the edit area to clear our background.
        vcl::PaintBufferGuard g(ImplGetWindowImpl()->mpFrameData, GetParent());
        GetParent()->Paint(rRenderContext, rRectangle);
    }
}

void Edit::ImplPaintBorder(vcl::RenderContext const & rRenderContext)
{
    // this is not needed when double-buffering
    if (SupportsDoubleBuffering())
        return;

    if (!(ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent()))
        return;

    // draw the inner part by painting the whole control using its border window
    vcl::Window* pBorder = GetWindow(GetWindowType::Border);
    if (pBorder == this)
    {
        // we have no border, use parent
        vcl::Window* pControl = mbIsSubEdit ? GetParent() : this;
        pBorder = pControl->GetWindow(GetWindowType::Border);
        if (pBorder == this)
            pBorder = GetParent();
    }

    if (!pBorder)
        return;

    // set proper clipping region to not overdraw the whole control
    vcl::Region aClipRgn = GetPaintRegion();
    if (!aClipRgn.IsNull())
    {
        // transform clipping region to border window's coordinate system
        if (IsRTLEnabled() != pBorder->IsRTLEnabled() && AllSettings::GetLayoutRTL())
        {
            // need to mirror in case border is not RTL but edit is (or vice versa)

            // mirror
            tools::Rectangle aBounds(aClipRgn.GetBoundRect());
            int xNew = GetOutputSizePixel().Width() - aBounds.GetWidth() - aBounds.Left();
            aClipRgn.Move(xNew - aBounds.Left(), 0);

            // move offset of border window
            Point aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(Point()));
            aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y());
        }
        else
        {
            // normal case
            Point aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(Point()));
            aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y());
        }

        vcl::Region oldRgn(pBorder->GetOutDev()->GetClipRegion());
        pBorder->GetOutDev()->SetClipRegion(aClipRgn);

        pBorder->Paint(*pBorder->GetOutDev(), tools::Rectangle());

        pBorder->GetOutDev()->SetClipRegion(oldRgn);
    }
    else
    {
        pBorder->Paint(*pBorder->GetOutDev(), tools::Rectangle());
    }
}

void Edit::ImplShowCursor( bool bOnlyIfVisible )
{
    if ( !IsUpdateMode() || ( bOnlyIfVisible && !IsReallyVisible() ) )
        return;

    vcl::Cursor* pCursor = GetCursor();
    OUString aText = ImplGetText();

    tools::Long nTextPos = 0;

    if( !aText.isEmpty() )
    {
        KernArray aDX;
        GetOutDev()->GetCaretPositions(aText, aDX, 0, aText.getLength());

        if( maSelection.Max() < aText.getLength() )
            nTextPos = aDX[ 2*maSelection.Max() ];
        else
            nTextPos = aDX[ 2*aText.getLength()-1 ];
    }

    tools::Long nCursorWidth = 0;
    if ( !mbInsertMode && !maSelection.Len() && (maSelection.Max() < aText.getLength()) )
        nCursorWidth = GetTextWidth(aText, maSelection.Max(), 1);
    tools::Long nCursorPosX = nTextPos + mnXOffset + ImplGetExtraXOffset();

    // cursor should land in visible area
    const Size aOutSize = GetOutputSizePixel();
    if ( (nCursorPosX < 0) || (nCursorPosX >= aOutSize.Width()) )
    {
        tools::Long nOldXOffset = mnXOffset;

        if ( nCursorPosX < 0 )
        {
            mnXOffset = - nTextPos;
            tools::Long nMaxX = 0;
            mnXOffset += aOutSize.Width() / 5;
            if ( mnXOffset > nMaxX )
                mnXOffset = nMaxX;
        }
        else
        {
            mnXOffset = (aOutSize.Width()-ImplGetExtraXOffset()) - nTextPos;
            // Something more?
            if ( (aOutSize.Width()-ImplGetExtraXOffset()) < nTextPos )
            {
                tools::Long nMaxNegX = (aOutSize.Width()-ImplGetExtraXOffset()) - GetTextWidth( aText );
                mnXOffset -= aOutSize.Width() / 5;
                if ( mnXOffset < nMaxNegX )  // both negative...
                    mnXOffset = nMaxNegX;
            }
        }

        nCursorPosX = nTextPos + mnXOffset + ImplGetExtraXOffset();
        if ( nCursorPosX == aOutSize.Width() )  // then invisible...
            nCursorPosX--;

        if ( mnXOffset != nOldXOffset )
            ImplInvalidateOrRepaint();
    }

    const tools::Long nTextHeight = GetTextHeight();
    const tools::Long nCursorPosY = ImplGetTextYPosition();
    if (pCursor)
    {
        pCursor->SetPos( Point( nCursorPosX, nCursorPosY ) );
        pCursor->SetSize( Size( nCursorWidth, nTextHeight ) );
        pCursor->Show();
    }
}

void Edit::ImplAlign()
{
    if (mnAlign == EDIT_ALIGN_LEFT && !mnXOffset)
    {
        // short circuit common case and avoid slow GetTextWidth() calc
        return;
    }

    tools::Long nTextWidth = GetTextWidth( ImplGetText() );
    tools::Long nOutWidth = GetOutputSizePixel().Width();

    if ( mnAlign == EDIT_ALIGN_LEFT )
    {
        if (nTextWidth < nOutWidth)
            mnXOffset = 0;
    }
    else if ( mnAlign == EDIT_ALIGN_RIGHT )
    {
        tools::Long nMinXOffset = nOutWidth - nTextWidth - 1 - ImplGetExtraXOffset();
        bool bRTL = IsRTLEnabled();
        if( mbIsSubEdit && GetParent() )
            bRTL = GetParent()->IsRTLEnabled();
        if( bRTL )
        {
            if( nTextWidth < nOutWidth )
                mnXOffset = nMinXOffset;
        }
        else
        {
            if( nTextWidth < nOutWidth )
                mnXOffset = nMinXOffset;
            else if ( mnXOffset < nMinXOffset )
                mnXOffset = nMinXOffset;
        }
    }
    else if( mnAlign == EDIT_ALIGN_CENTER )
    {
        // would be nicer with check while scrolling but then it's not centred in scrolled state
        mnXOffset = (nOutWidth - nTextWidth) / 2;
    }
}

void Edit::ImplAlignAndPaint()
{
    ImplAlign();
    ImplInvalidateOrRepaint();
    ImplShowCursor();
}

sal_Int32 Edit::ImplGetCharPos( const Point& rWindowPos ) const
{
    sal_Int32 nIndex = EDIT_NOLIMIT;
    OUString aText = ImplGetText();

    if (aText.isEmpty())
        return nIndex;

    KernArray aDX;
    GetOutDev()->GetCaretPositions(aText, aDX, 0, aText.getLength());
    tools::Long nX = rWindowPos.X() - mnXOffset - ImplGetExtraXOffset();
    for (sal_Int32 i = 0; i < aText.getLength(); aText.iterateCodePoints(&i))
    {
        if( (aDX[2*i] >= nX && aDX[2*i+1] <= nX) ||
            (aDX[2*i+1] >= nX && aDX[2*i] <= nX))
        {
            nIndex = i;
            if( aDX[2*i] < aDX[2*i+1] )
            {
                if( nX > (aDX[2*i]+aDX[2*i+1])/2 )
                    aText.iterateCodePoints(&nIndex);
            }
            else
            {
                if( nX < (aDX[2*i]+aDX[2*i+1])/2 )
                    aText.iterateCodePoints(&nIndex);
            }
            break;
        }
    }
    if( nIndex == EDIT_NOLIMIT )
    {
        nIndex = 0;
        sal_Int32 nFinalIndex = 0;
        tools::Long nDiff = std::abs( aDX[0]-nX );
        sal_Int32 i = 0;
        if (!aText.isEmpty())
        {
            aText.iterateCodePoints(&i);    //skip the first character
        }
        while (i < aText.getLength())
        {
            tools::Long nNewDiff = std::abs( aDX[2*i]-nX );

            if( nNewDiff < nDiff )
            {
                nIndex = i;
                nDiff = nNewDiff;
            }

            nFinalIndex = i;

            aText.iterateCodePoints(&i);
        }
        if (nIndex == nFinalIndex && std::abs( aDX[2*nIndex+1] - nX ) < nDiff)
            nIndex = EDIT_NOLIMIT;
    }

    return nIndex;
}

void Edit::ImplSetCursorPos( sal_Int32 nChar, bool bSelect )
{
    Selection aSelection( maSelection );
    aSelection.Max() = nChar;
    if ( !bSelect )
        aSelection.Min() = aSelection.Max();
    ImplSetSelection( aSelection );
}

void Edit::ImplCopyToSelectionClipboard()
{
    if ( GetSelection().Len() )
    {
        css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(GetSystemPrimarySelection());
        ImplCopy( aSelection );
    }
}

void Edit::ImplCopy(css::uno::Reference<css::datatransfer::clipboard::XClipboard> const & rxClipboard)
{
    vcl::unohelper::TextDataObject::CopyStringTo( GetSelected(), rxClipboard );
}

void Edit::ImplPaste(css::uno::Reference<css::datatransfer::clipboard::XClipboard> const & rxClipboard)
{
    if ( !rxClipboard.is() )
        return;

    css::uno::Reference<css::datatransfer::XTransferable> xDataObj;

    try
        {
            SolarMutexReleaser aReleaser;
            xDataObj = rxClipboard->getContents();
        }
    catchconst css::uno::Exception& )
        {
        }

    if ( !xDataObj.is() )
        return;

    css::datatransfer::DataFlavor aFlavor;
    SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
    try
    {
        css::uno::Any aData = xDataObj->getTransferData(aFlavor);
        OUString aText;
        aData >>= aText;

        // tdf#127588 - extend selection to the entire field or paste the text
        // from the clipboard to the current position if there is no selection
        if (mnMaxTextLen < EDIT_NOLIMIT && maSelection.Len() == 0)
        {
            const sal_Int32 aTextLen = aText.getLength();
            if (aTextLen == mnMaxTextLen)
            {
                maSelection.Min() = 0;
                maSelection.Max() = mnMaxTextLen;
            } else
                maSelection.Max() = std::min<sal_Int32>(maSelection.Min() + aTextLen, mnMaxTextLen);
        }

        Selection aSelection(maSelection);
        aSelection.Normalize();
        if (ImplTruncateToMaxLen(aText, aSelection.Len()))
            ShowTruncationWarning(GetFrameWeld());

        ReplaceSelected( aText );
    }
    catch(const css::uno::Exception&)
    {
    }
}

css::uno::Reference<css::accessibility::XAccessible> Edit::CreateAccessible()
{
    return new VCLXAccessibleEdit(this);
}

void Edit::MouseButtonDown( const MouseEvent& rMEvt )
{
    if ( mpSubEdit )
    {
        Control::MouseButtonDown( rMEvt );
        return;
    }

    sal_Int32 nCharPos = ImplGetCharPos( rMEvt.GetPosPixel() );
    Selection aSelection( maSelection );
    aSelection.Normalize();

    if ( rMEvt.GetClicks() < 4 )
    {
        mbClickedInSelection = false;
        if ( rMEvt.GetClicks() == 3 )
        {
            ImplSetSelection( Selection( 0, EDIT_NOLIMIT) );
            ImplCopyToSelectionClipboard();

        }
        else if ( rMEvt.GetClicks() == 2 )
        {
            css::uno::Reference <css::i18n::XBreakIterator> xBI = ImplGetBreakIterator();
            css::i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSelection.Max(),
                     GetSettings().GetLanguageTag().getLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
            ImplSetSelection( Selection( aBoundary.startPos, aBoundary.endPos ) );
            ImplCopyToSelectionClipboard();
        }
        else if ( !rMEvt.IsShift() && HasFocus() && aSelection.Contains( nCharPos ) )
            mbClickedInSelection = true;
        else if ( rMEvt.IsLeft() )
            ImplSetCursorPos( nCharPos, rMEvt.IsShift() );

        if ( !mbClickedInSelection && rMEvt.IsLeft() && ( rMEvt.GetClicks() == 1 ) )
            StartTracking( StartTrackingFlags::ScrollRepeat );
    }

    GrabFocus();
}

void Edit::MouseButtonUp( const MouseEvent& rMEvt )
{
    if ( mbClickedInSelection && rMEvt.IsLeft() )
    {
        sal_Int32 nCharPos = ImplGetCharPos( rMEvt.GetPosPixel() );
        ImplSetCursorPos( nCharPos, false );
        mbClickedInSelection = false;
    }
    else if ( rMEvt.IsMiddle() && !mbReadOnly &&
              ( GetSettings().GetMouseSettings().GetMiddleButtonAction() == MouseMiddleButtonAction::PasteSelection ) )
    {
        css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(GetSystemPrimarySelection());
        ImplPaste( aSelection );
        Modify();
    }
}

void Edit::Tracking( const TrackingEvent& rTEvt )
{
    if ( rTEvt.IsTrackingEnded() )
    {
        if ( mbClickedInSelection )
        {
            sal_Int32 nCharPos = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() );
            ImplSetCursorPos( nCharPos, false );
            mbClickedInSelection = false;
        }
        else if ( rTEvt.GetMouseEvent().IsLeft() )
        {
            ImplCopyToSelectionClipboard();
        }
    }
    else
    {
        if( !mbClickedInSelection )
        {
            sal_Int32 nCharPos = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() );
            ImplSetCursorPos( nCharPos, true );
        }
    }
}

bool Edit::ImplHandleKeyEvent( const KeyEvent& rKEvt )
{
    bool        bDone = false;
    sal_uInt16      nCode = rKEvt.GetKeyCode().GetCode();
    KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();

    mbInternModified = false;

    if ( eFunc != KeyFuncType::DONTKNOW )
    {
        switch ( eFunc )
        {
            case KeyFuncType::CUT:
            {
                if ( !mbReadOnly && maSelection.Len() && !mbPassword )
                {
                    Cut();
                    Modify();
                    bDone = true;
                }
            }
            break;

            case KeyFuncType::COPY:
            {
                if ( !mbPassword )
                {
                    Copy();
                    bDone = true;
                }
            }
            break;

            case KeyFuncType::PASTE:
            {
                if ( !mbReadOnly )
                {
                    Paste();
                    bDone = true;
                }
            }
            break;

            case KeyFuncType::UNDO:
            {
                if ( !mbReadOnly )
                {
                    Undo();
                    bDone = true;
                }
            }
            break;

            default:
                eFunc = KeyFuncType::DONTKNOW;
        }
    }

    if ( !bDone && rKEvt.GetKeyCode().IsMod1() && !rKEvt.GetKeyCode().IsMod2() )
    {
        if ( nCode == KEY_A )
        {
            ImplSetSelection( Selection( 0, maText.getLength() ) );
            bDone = true;
        }
        else if ( rKEvt.GetKeyCode().IsShift() && (nCode == KEY_S) )
        {
            if ( pImplFncGetSpecialChars )
            {
                Selection aSaveSel = GetSelection(); // if someone changes the selection in Get/LoseFocus, e.g. URL bar
                OUString aChars = pImplFncGetSpecialChars( GetFrameWeld(), GetFont() );
                SetSelection( aSaveSel );
                if ( !aChars.isEmpty() )
                {
                    ImplInsertText( aChars );
                    Modify();
                }
                bDone = true;
            }
        }
    }

    if ( eFunc == KeyFuncType::DONTKNOW && ! bDone )
    {
        switch ( nCode )
        {
            case css::awt::Key::SELECT_ALL:
            {
                ImplSetSelection( Selection( 0, maText.getLength() ) );
                bDone = true;
            }
            break;

            case KEY_LEFT:
            case KEY_RIGHT:
            case KEY_HOME:
            case KEY_END:
            case css::awt::Key::MOVE_WORD_FORWARD:
            case css::awt::Key::SELECT_WORD_FORWARD:
            case css::awt::Key::MOVE_WORD_BACKWARD:
            case css::awt::Key::SELECT_WORD_BACKWARD:
            case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
            case css::awt::Key::MOVE_TO_END_OF_LINE:
            case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
            case css::awt::Key::SELECT_TO_END_OF_LINE:
            case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
            case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
            case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
            case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
            case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
            case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
            case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
            case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
            {
                if ( !rKEvt.GetKeyCode().IsMod2() )
                {
                    ImplClearLayoutData();
                    css::uno::Reference<css::i18n::XBreakIterator> xBI = ImplGetBreakIterator();

                    Selection aSel( maSelection );
                    bool bWord = rKEvt.GetKeyCode().IsMod1();
                    bool bSelect = rKEvt.GetKeyCode().IsShift();
                    bool bGoLeft = (nCode == KEY_LEFT);
                    bool bGoRight = (nCode == KEY_RIGHT);
                    bool bGoHome = (nCode == KEY_HOME);
                    bool bGoEnd = (nCode == KEY_END);

                    switch( nCode )
                    {
                    case css::awt::Key::MOVE_WORD_FORWARD:
                        bGoRight = bWord = true;break;
                    case css::awt::Key::SELECT_WORD_FORWARD:
                        bGoRight = bSelect = bWord = true;break;
                    case css::awt::Key::MOVE_WORD_BACKWARD:
                        bGoLeft = bWord = true;break;
                    case css::awt::Key::SELECT_WORD_BACKWARD:
                        bGoLeft = bSelect = bWord = true;break;
                    case css::awt::Key::SELECT_TO_BEGIN_OF_LINE:
                    case css::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
                    case css::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
                        bSelect = true;
                        [[fallthrough]];
                    case css::awt::Key::MOVE_TO_BEGIN_OF_LINE:
                    case css::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
                    case css::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
                        bGoHome = true;break;
                    case css::awt::Key::SELECT_TO_END_OF_LINE:
                    case css::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
                    case css::awt::Key::SELECT_TO_END_OF_DOCUMENT:
                        bSelect = true;
                        [[fallthrough]];
                    case css::awt::Key::MOVE_TO_END_OF_LINE:
                    case css::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
                    case css::awt::Key::MOVE_TO_END_OF_DOCUMENT:
                        bGoEnd = true;break;
                    default:
                        break;
                    }

                    // range is checked in ImplSetSelection ...
                    if ( bGoLeft && aSel.Max() )
                    {
                        if ( bWord )
                        {
                            const OUString sText = maText.toString();
                            css::i18n::Boundary aBoundary = xBI->getWordBoundary(sText, aSel.Max(),
                                    GetSettings().GetLanguageTag().getLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES, true);
                            if ( aBoundary.startPos == aSel.Max() )
                                aBoundary = xBI->previousWord(sText, aSel.Max(),
                                        GetSettings().GetLanguageTag().getLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES);
                            aSel.Max() = aBoundary.startPos;
                        }
                        else
                        {
                            sal_Int32 nCount = 1;
                            aSel.Max() = xBI->previousCharacters(maText.toString(), aSel.Max(),
                                    GetSettings().GetLanguageTag().getLocale(), css::i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount);
                        }
                    }
                    else if ( bGoRight && ( aSel.Max() < maText.getLength() ) )
                    {
                        if ( bWord )
                        {
                            css::i18n::Boundary aBoundary = xBI->nextWord(maText.toString(), aSel.Max(),
                                    GetSettings().GetLanguageTag().getLocale(), css::i18n::WordType::ANYWORD_IGNOREWHITESPACES);
                            aSel.Max() = aBoundary.startPos;
                        }
                        else
                        {
                            sal_Int32 nCount = 1;
                            aSel.Max() = xBI->nextCharacters(maText.toString(), aSel.Max(),
                                    GetSettings().GetLanguageTag().getLocale(), css::i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount);
                        }
                    }
                    else if ( bGoHome )
                    {
                        aSel.Max() = 0;
                    }
                    else if ( bGoEnd )
                    {
                        aSel.Max() = EDIT_NOLIMIT;
                    }

                    if ( !bSelect )
                        aSel.Min() = aSel.Max();

                    if ( aSel != GetSelection() )
                    {
                        ImplSetSelection( aSel );
                        ImplCopyToSelectionClipboard();
                    }

                    if (bGoEnd && maAutocompleteHdl.IsSet() && !rKEvt.GetKeyCode().GetModifier())
                    {
                        if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
                        {
                            maAutocompleteHdl.Call(*this);
                        }
                    }

                    bDone = true;
                }
            }
            break;

            case css::awt::Key::DELETE_WORD_BACKWARD:
            case css::awt::Key::DELETE_WORD_FORWARD:
            case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
            case css::awt::Key::DELETE_TO_END_OF_LINE:
            case KEY_BACKSPACE:
            case KEY_DELETE:
            {
                if ( !mbReadOnly && !rKEvt.GetKeyCode().IsMod2() )
                {
                    sal_uInt8 nDel = (nCode == KEY_DELETE) ? EDIT_DEL_RIGHT : EDIT_DEL_LEFT;
                    sal_uInt8 nMode = rKEvt.GetKeyCode().IsMod1() ? EDIT_DELMODE_RESTOFWORD : EDIT_DELMODE_SIMPLE;
                    if ( (nMode == EDIT_DELMODE_RESTOFWORD) && rKEvt.GetKeyCode().IsShift() )
                        nMode = EDIT_DELMODE_RESTOFCONTENT;
                    switch( nCode )
                    {
                    case css::awt::Key::DELETE_WORD_BACKWARD:
                        nDel = EDIT_DEL_LEFT;
                        nMode = EDIT_DELMODE_RESTOFWORD;
                        break;
                    case css::awt::Key::DELETE_WORD_FORWARD:
                        nDel = EDIT_DEL_RIGHT;
                        nMode = EDIT_DELMODE_RESTOFWORD;
                        break;
                    case css::awt::Key::DELETE_TO_BEGIN_OF_LINE:
                        nDel = EDIT_DEL_LEFT;
                        nMode = EDIT_DELMODE_RESTOFCONTENT;
                        break;
                    case css::awt::Key::DELETE_TO_END_OF_LINE:
                        nDel = EDIT_DEL_RIGHT;
                        nMode = EDIT_DELMODE_RESTOFCONTENT;
                        break;
                    defaultbreak;
                    }
                    sal_Int32 nOldLen = maText.getLength();
                    ImplDelete( maSelection, nDel, nMode );
                    if ( maText.getLength() != nOldLen )
                        Modify();
                    bDone = true;
                }
            }
            break;

            case KEY_INSERT:
            {
                if ( !mpIMEInfos && !mbReadOnly && !rKEvt.GetKeyCode().IsMod2() )
                {
                    SetInsertMode( !mbInsertMode );
                    bDone = true;
                }
            }
            break;

            case KEY_RETURN:
                if (maActivateHdl.IsSet() && !rKEvt.GetKeyCode().GetModifier())
                    bDone = maActivateHdl.Call(*this);
            break;

            default:
            {
                if ( IsCharInput( rKEvt ) )
                {
                    bDone = true;   // read characters also when in ReadOnly
                    if ( !mbReadOnly )
                    {
                        ImplInsertText(OUString(rKEvt.GetCharCode()), nullptr, true);
                        if (maAutocompleteHdl.IsSet())
                        {
                            if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
                            {
                                maAutocompleteHdl.Call(*this);
                            }
                        }
                    }
                }
            }
        }
    }

    if ( mbInternModified )
        Modify();

    return bDone;
}

void Edit::KeyInput( const KeyEvent& rKEvt )
{
    if ( mpSubEdit || !ImplHandleKeyEvent( rKEvt ) )
        Control::KeyInput( rKEvt );
}

void Edit::FillLayoutData() const
{
    mxLayoutData.emplace();
    const_cast<Edit*>(this)->Invalidate();
}

void Edit::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRectangle)
{
    if (!mpSubEdit)
        ImplRepaint(rRenderContext, rRectangle);
}

void Edit::Resize()
{
    if ( !mpSubEdit && IsReallyVisible() )
    {
        Control::Resize();
        // because of vertical centering...
        mnXOffset = 0;
        ImplAlign();
        Invalidate();
        ImplShowCursor();
    }
}

void Edit::Draw( OutputDevice* pDev, const Point& rPos, SystemTextColorFlags nFlags )
{
    ApplySettings(*pDev);

    Point aPos = pDev->LogicToPixel( rPos );
    Size aSize = GetSizePixel();
    vcl::Font aFont = GetDrawPixelFont( pDev );

    pDev->Push();
    pDev->SetMapMode();
    pDev->SetFont( aFont );
    pDev->SetTextFillColor();

    // Border/Background
    pDev->SetLineColor();
    pDev->SetFillColor();
    bool bBorder = (GetStyle() & WB_BORDER);
    bool bBackground = IsControlBackground();
    if ( bBorder || bBackground )
    {
        tools::Rectangle aRect( aPos, aSize );
        if ( bBorder )
        {
            ImplDrawFrame( pDev, aRect );
        }
        if ( bBackground )
        {
            pDev->SetFillColor( GetControlBackground() );
            pDev->DrawRect( aRect );
        }
    }

    // Content
    if ( nFlags & SystemTextColorFlags::Mono )
        pDev->SetTextColor( COL_BLACK );
    else
    {
        if ( !IsEnabled() )
        {
            const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
            pDev->SetTextColor( rStyleSettings.GetDisableColor() );
        }
        else
        {
            pDev->SetTextColor( GetTextColor() );
        }
    }

    const tools::Long nOnePixel = GetDrawPixel( pDev, 1 );
    const tools::Long nOffX = 3*nOnePixel;
    DrawTextFlags nTextStyle = DrawTextFlags::VCenter;
    tools::Rectangle aTextRect( aPos, aSize );

    if ( GetStyle() & WB_CENTER )
        nTextStyle |= DrawTextFlags::Center;
    else if ( GetStyle() & WB_RIGHT )
        nTextStyle |= DrawTextFlags::Right;
    else
        nTextStyle |= DrawTextFlags::Left;

    aTextRect.AdjustLeft(nOffX );
    aTextRect.AdjustRight( -nOffX );

    OUString    aText = ImplGetText();
    tools::Long        nTextHeight = pDev->GetTextHeight();
    tools::Long        nTextWidth = pDev->GetTextWidth( aText );
    tools::Long        nOffY = (aSize.Height() - nTextHeight) / 2;

    // Clipping?
    if ( (nOffY < 0) ||
         ((nOffY+nTextHeight) > aSize.Height()) ||
         ((nOffX+nTextWidth) > aSize.Width()) )
    {
        tools::Rectangle aClip( aPos, aSize );
        if ( nTextHeight > aSize.Height() )
            aClip.AdjustBottom(nTextHeight-aSize.Height()+1 );  // prevent HP printers from 'optimizing'
        pDev->IntersectClipRegion( aClip );
    }

    pDev->DrawText( aTextRect, aText, nTextStyle );
    pDev->Pop();

    if ( GetSubEdit() )
    {
        Size aOrigSize(GetSubEdit()->GetSizePixel());
        GetSubEdit()->SetSizePixel(GetSizePixel());
        GetSubEdit()->Draw(pDev, rPos, nFlags);
        GetSubEdit()->SetSizePixel(aOrigSize);
    }
}

void Edit::ImplInvalidateOutermostBorder( vcl::Window* pWin )
{
    // allow control to show focused state
    vcl::Window *pInvalWin = pWin;
    for (;;)
    {
        vcl::Window* pBorder = pInvalWin->GetWindow( GetWindowType::Border );
        if (pBorder == pInvalWin || !pBorder ||
           pInvalWin->ImplGetFrame() != pBorder->ImplGetFrame() )
           break;
        pInvalWin = pBorder;
    }

    pInvalWin->Invalidate( InvalidateFlags::Children | InvalidateFlags::Update );
}

void Edit::GetFocus()
{
    Control::GetFocus();

    // tdf#164127 for an Edit in the UNO control property browser, above call to Control::GetFocus
    // can result in it getting disposed - return early in that case
    if (isDisposed())
        return;

    if ( mpSubEdit )
        mpSubEdit->ImplGrabFocus( GetGetFocusFlags() );
    else if ( !mbActivePopup )
    {
        maUndoText = maText.toString();
        SelectionOptions nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
        if ( !( GetStyle() & (WB_NOHIDESELECTION|WB_READONLY) )
                && ( GetGetFocusFlags() & (GetFocusFlags::Init|GetFocusFlags::Tab|GetFocusFlags::CURSOR|GetFocusFlags::Mnemonic) ) )
        {
            if ( nSelOptions & SelectionOptions::ShowFirst )
            {
                maSelection.Min() = maText.getLength();
                maSelection.Max() = 0;
            }
            else
            {
                maSelection.Min() = 0;
                maSelection.Max() = maText.getLength();
            }
            if ( mbIsSubEdit )
                static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::EditSelectionChanged );
            else
                CallEventListeners( VclEventId::EditSelectionChanged );
        }

        ImplShowCursor();

        if (IsNativeWidgetEnabled() &&
            IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ))
        {
            ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this );
        }
        else if ( maSelection.Len() )
        {
            // paint the selection
            if ( !HasPaintEvent() )
                ImplInvalidateOrRepaint();
            else
                Invalidate();
        }

        SetInputContext( InputContext( GetFont(), !IsReadOnly() ? InputContextFlags::Text|InputContextFlags::ExtText : InputContextFlags::NONE ) );
    }
}

void Edit::LoseFocus()
{
    if ( !mpSubEdit )
    {
        if (IsNativeWidgetEnabled() &&
            IsNativeControlSupported(ControlType::Editbox, ControlPart::Entire))
        {
            ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this );
        }

        if ( !mbActivePopup && !( GetStyle() & WB_NOHIDESELECTION ) && maSelection.Len() )
            ImplInvalidateOrRepaint();    // paint the selection
    }

    Control::LoseFocus();
}

bool Edit::PreNotify(NotifyEvent& rNEvt)
{
    if (rNEvt.GetType() == NotifyEventType::MOUSEMOVE)
    {
        const MouseEvent* pMouseEvt = rNEvt.GetMouseEvent();
        if (pMouseEvt && !pMouseEvt->GetButtons() && !pMouseEvt->IsSynthetic() && !pMouseEvt->IsModifierChanged())
        {
            // trigger redraw if mouse over state has changed
            if (pMouseEvt->IsLeaveWindow() || pMouseEvt->IsEnterWindow())
            {
                if (IsNativeWidgetEnabled() &&
                    IsNativeControlSupported(ControlType::Editbox, ControlPart::Entire))
                {
                    ImplInvalidateOutermostBorder(this);
                }
            }
        }
    }

    return Control::PreNotify(rNEvt);
}

void Edit::Command( const CommandEvent& rCEvt )
{
    if ( rCEvt.GetCommand() == CommandEventId::ContextMenu )
    {
        VclPtr<PopupMenu> pPopup = Edit::CreatePopupMenu();

        bool bEnableCut = true;
        bool bEnableCopy = true;
        bool bEnableDelete = true;
        bool bEnablePaste = true;
        bool bEnableSpecialChar = true;

        if ( !maSelection.Len() )
        {
            bEnableCut = false;
            bEnableCopy = false;
            bEnableDelete = false;
        }

        if ( IsReadOnly() )
        {
            bEnableCut = false;
            bEnablePaste = false;
            bEnableDelete = false;
            bEnableSpecialChar = false;
        }
        else
        {
            // only paste if text available in clipboard
--> --------------------

--> maximum size reached

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

Messung V0.5
C=96 H=99 G=97

¤ 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.