    /* -*- 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/IDialogRenderable.hxx>
    #include <vcl/decoview.hxx>
    #include <vcl/event.hxx>
    #include <vcl/cursor.hxx>
    #include <vcl/virdev.hxx>
    #include <vcl/menu.hxx>
    #include <vcl/edit.hxx>
    #include <vcl/weld.hxx>
    #include <vcl/svapp.hxx>
    #include <vcl/settings.hxx>
    #include <vcl/uitest/uiobject.hxx>

    #include <window.h>
    #include <svdata.hxx>
    #include <strings.hrc>
    #include <controldata.hxx>

    #include <com/sun/star/i18n/BreakIterator.hpp>
    #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
    #include <com/sun/star/i18n/WordType.hpp>
    #include <cppuhelper/weak.hxx>
    #include <com/sun/star/datatransfer/XTransferable.hpp>
    #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
    #include <com/sun/star/lang/XMultiServiceFactory.hpp>

    #include <com/sun/star/datatransfer/dnd/DNDConstants.hpp>
    #include <com/sun/star/datatransfer/dnd/XDragGestureRecognizer.hpp>
    #include <com/sun/star/datatransfer/dnd/XDropTarget.hpp>

    #include <com/sun/star/i18n/InputSequenceChecker.hpp>
    #include <com/sun/star/i18n/InputSequenceCheckMode.hpp>
    #include <com/sun/star/i18n/ScriptType.hpp>
    #include <com/sun/star/container/XNameAccess.hpp>

    #include <com/sun/star/uno/Any.hxx>

    #include <comphelper/processfactory.hxx>
    #include <comphelper/string.hxx>

    #include <sot/exchange.hxx>
    #include <sot/formats.hxx>
    #include <sal/macros.h>
    #include <sal/log.hxx>

    #include <vcl/unohelp.hxx>
    #include <vcl/unohelp2.hxx>

    #include <officecfg/Office/Common.hxx>

    #include <memory>

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

    // - 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 DDInfo
    {
        vcl::Cursor     aCursor;
        Selection       aDndStartSel;
        sal_Int32       nDropPos;
        bool            bStarterOfDD;
        bool            bDroppedInMe;
        bool            bVisCursor;
        bool            bIsStringSupported;

        DDInfo()
        {
            aCursor.SetStyle( CURSOR_SHADOW );
            nDropPos = 0;
            bStarterOfDD = false;
            bDroppedInMe = false;
            bVisCursor = false;
            bIsStringSupported = false;
        }
    };

    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, const OUString& rOldTextAfterStartPos);

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

    Impl_IMEInfos::Impl_IMEInfos(sal_Int32 nP, const OUString& rOldTextAfterStartPos)
        : aOldTextAfterStartPos(rOldTextAfterStartPos),
        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 nType )
        : Control( nType )
    {
        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 OString &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")
        {
            bool bReadOnly = !toBool(rValue);
            SetReadOnly(bReadOnly);
            //disable tab to traverse into readonly editables
            WinBits nBits = GetStyle();
            nBits &= ~(WB_TABSTOP|WB_NOTABSTOP);
            if (!bReadOnly)
                nBits |= WB_TABSTOP;
            else
                nBits |= WB_NOTABSTOP;
            SetStyle(nBits);
        }
        else if (rKey == "visibility")
        {
            mbPassword = false;
            if (!toBool(rValue))
                mbPassword = true;
        }
        else if (rKey == "placeholder-text")
            SetPlaceholderText(rValue);
        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();
        mpUpdateDataTimer.reset();

        if ( mxDnDListener.is() )
        {
            if ( GetDragGestureRecognizer().is() )
            {
                uno::Reference< datatransfer::dnd::XDragGestureListener> xDGL( mxDnDListener, uno::UNO_QUERY );
                GetDragGestureRecognizer()->removeDragGestureListener( xDGL );
            }
            if ( GetDropTarget().is() )
            {
                uno::Reference< datatransfer::dnd::XDropTargetListener> xDTL( mxDnDListener, uno::UNO_QUERY );
                GetDropTarget()->removeDropTargetListener( xDTL );
            }

            uno::Reference< lang::XEventListener> xEL( mxDnDListener, uno::UNO_QUERY );
            xEL->disposing( 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>();
        mpUpdateDataTimer       = nullptr;
        mpFilterText            = nullptr;
        mnXOffset               = 0;
        mnAlign                 = EDIT_ALIGN_LEFT;
        mnMaxTextLen            = EDIT_NOLIMIT;
        mnWidthInChars          = -1;
        mnMaxWidthChars         = -1;
        mbModified              = false;
        mbInternModified        = false;
        mbReadOnly              = false;
        mbSelectAllSingleClick  = 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 );

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

    bool Edit::ImplUseNativeBorder(vcl::RenderContext const & rRenderContext, WinBits nStyle)
    {
        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( Pointer( PointerStyle::Text ) );

        uno::Reference< datatransfer::dnd::XDragGestureListener> xDGL( mxDnDListener, uno::UNO_QUERY );
        uno::Reference< datatransfer::dnd::XDragGestureRecognizer > xDGR = GetDragGestureRecognizer();
        if ( xDGR.is() )
        {
            xDGR->addDragGestureListener( xDGL );
            uno::Reference< datatransfer::dnd::XDropTargetListener> xDTL( mxDnDListener, uno::UNO_QUERY );
            GetDropTarget()->addDropTargetListener( xDTL );
            GetDropTarget()->setActive( true );
            GetDropTarget()->setDefaultActions( 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::ImplModified()
    {
        mbModified = true;
        Modify();
    }

    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 (ImplUseNativeBorder(rRenderContext, GetStyle()) || IsPaintTransparent())
        {
            // Transparent background
            rRenderContext.SetBackground();
            rRenderContext.SetFillColor();
        }
        else if (IsControlBackground())
        {
            rRenderContext.SetBackground(GetControlBackground());
            rRenderContext.SetFillColor(GetControlBackground());
        }
        else
        {
            rRenderContext.SetBackground(rStyleSettings.GetFieldColor());
            rRenderContext.SetFillColor(rStyleSettings.GetFieldColor());
        }
    }

    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
        long nExtraOffset = 0;
        if( ( GetStyle() & WB_BORDER ) || ( mbIsSubEdit && ( GetParent()->GetStyle() & WB_BORDER ) ) )
            nExtraOffset = 2;

        return nExtraOffset;
    }

    long Edit::ImplGetExtraYOffset() const
    {
        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;
            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 OS X
            if( ImplGetSVData()->maNWFData.mbNoFocusRects )
                Update();
        }
        else
            Invalidate();
    }

    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();

        long nDXBuffer[256];
        std::unique_ptr<long[]> pDXBuffer;
        long* pDX = nDXBuffer;

        if (nLen)
        {
            if (static_cast<size_t>(2 * nLen) > SAL_N_ELEMENTS(nDXBuffer))
            {
                pDXBuffer.reset(new long[2 * (nLen + 1)]);
                pDX = pDXBuffer.get();
            }

            GetCaretPositions(aText, pDX, 0, nLen);
        }

        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(PushFlags::FILLCOLOR | 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.Justify();
            // selection is highlighted
            for(sal_Int32 i = 0; i < nLen; ++i)
            {
                tools::Rectangle aRect(aPos, Size(10, nTH));
                aRect.SetLeft( pDX[2 * i] + mnXOffset + ImplGetExtraXOffset() );
                aRect.SetRight( pDX[2 * i + 1] + mnXOffset + ImplGetExtraXOffset() );
                aRect.Justify();
                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( pDX[2 * (nIndex + mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraXOffset() );
                            aRect.SetRight( pDX[2 * (nIndex + mpIMEInfos->nPos) + 1] + mnXOffset + ImplGetExtraXOffset() );
                            aRect.Justify();
                            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::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.Justify();

        if ( !aSelection.Len() )
        {
            uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
            if ( nDirection == EDIT_DEL_LEFT )
            {
                if ( nMode == EDIT_DELMODE_RESTOFWORD )
                {
                    i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSelection.Min(),
                            GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
                    if ( aBoundary.startPos == aSelection.Min() )
                        aBoundary = xBI->previousWord( maText.toString(), aSelection.Min(),
                                GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
                    aSelection.Min() = aBoundary.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(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
                }
            }
            else
            {
                if ( nMode == EDIT_DELMODE_RESTOFWORD )
                {
                    i18n::Boundary aBoundary = xBI->nextWord( maText.toString(), aSelection.Max(),
                            GetSettings().GetLanguageTag().getLocale(), 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(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
                }
            }
        }

        maText.remove( static_cast<sal_Int32>(aSelection.Min()), static_cast<sal_Int32>(aSelection.Len()) );
        maSelection.Min() = aSelection.Min();
        maSelection.Max() = aSelection.Min();
        ImplAlignAndPaint();
        mbInternModified = true;
    }

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

    uno::Reference < i18n::XBreakIterator > Edit::ImplGetBreakIterator()
    {
        //!! since we don't want to become incompatible in the next minor update
        //!! where this code will get integrated into, xISC will be a local
        //!! variable instead of a class member!
        uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
        return i18n::BreakIterator::create(xContext);
    }

    uno::Reference < i18n::XExtendedInputSequenceChecker > const & Edit::ImplGetInputSequenceChecker()
    {
        if ( !mxISC.is() )
        {
            mxISC = 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.Justify();

        OUString aNewText( ImplGetValidString( rStr ) );
        ImplTruncateToMaxLen( aNewText, aSelection.Len() );

        ImplClearLayoutData();

        if ( aSelection.Len() )
            maText.remove( static_cast<sal_Int32>(aSelection.Min()), static_cast<sal_Int32>(aSelection.Len()) );
        else if ( !mbInsertMode && (aSelection.Max() < maText.getLength()) )
            maText.remove( static_cast<sal_Int32>(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

            uno::Reference < i18n::XBreakIterator > xBI( ImplGetBreakIterator(), UNO_QUERY );
            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() && i18n::ScriptType::COMPLEX == xBI->getScriptType( rStr, 0 );

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

                // the text that needs to be checked is only the one
                // before the current cursor position
                const OUString aOldText( maText.getStr(), 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( static_cast<sal_Int32>(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 ) &&
             ( (rText != maText.getStr()) || (pNewSelection && (*pNewSelection != maSelection)) ) )
        {
            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::LONGCURRENCYFIELD:
            case WindowType::NUMERICFIELD:
            case WindowType::SPINFIELD:
                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, long nXStart, 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.
            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())
        {
            // 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)
            {
                // 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;
                        aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(aBorderOffs));
                        aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y());
                    }
                    else
                    {
                        // normal case
                        Point aBorderOffs;
                        aBorderOffs = pBorder->ScreenToOutputPixel(OutputToScreenPixel(aBorderOffs));
                        aClipRgn.Move(aBorderOffs.X(), aBorderOffs.Y());
                    }

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

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

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

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

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

        long nTextPos = 0;

        long   nDXBuffer[256];
        std::unique_ptr<long[]> pDXBuffer;
        long*  pDX = nDXBuffer;

        if( !aText.isEmpty() )
        {
            if( static_cast<size_t>(2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer) )
            {
                pDXBuffer.reset(new long[2*(aText.getLength()+1)]);
                pDX = pDXBuffer.get();
            }

            GetCaretPositions( aText, pDX, 0, aText.getLength() );

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

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

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

            if ( nCursorPosX < 0 )
            {
                mnXOffset = - nTextPos;
                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 )
                {
                    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 long nTextHeight = GetTextHeight();
        const long nCursorPosY = ImplGetTextYPosition();
        if (pCursor)
        {
            pCursor->SetPos( Point( nCursorPosX, nCursorPosY ) );
            pCursor->SetSize( Size( nCursorWidth, nTextHeight ) );
            pCursor->Show();
        }
    }

    void Edit::ImplAlign()
    {
        long nTextWidth = GetTextWidth( ImplGetText() );
        long nOutWidth = GetOutputSizePixel().Width();

        if ( mnAlign == EDIT_ALIGN_LEFT )
        {
            if( mnXOffset && ( nTextWidth < nOutWidth ) )
                mnXOffset = 0;

        }
        else if ( mnAlign == EDIT_ALIGN_RIGHT )
        {
            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();

        long   nDXBuffer[256];
        std::unique_ptr<long[]> pDXBuffer;
        long*  pDX = nDXBuffer;
        if( static_cast<size_t>(2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer) )
        {
            pDXBuffer.reset(new long[2*(aText.getLength()+1)]);
            pDX = pDXBuffer.get();
        }

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

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

                nFinalIndex = i;

                aText.iterateCodePoints(&i);
            }
            if (nIndex == nFinalIndex && std::abs( pDX[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(GetPrimarySelection());
            ImplCopy( aSelection );
        }
    }

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

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

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

            if ( xDataObj.is() )
            {
                datatransfer::DataFlavor aFlavor;
                SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
                try
                {
                    uno::Any aData = xDataObj->getTransferData( aFlavor );
                    OUString aText;
                    aData >>= aText;
                    if( ImplTruncateToMaxLen( aText, maSelection.Len() ) )
                        ShowTruncationWarning(GetFrameWeld());
                    ReplaceSelected( aText );
                }
                catch( const css::uno::Exception& )
                {
                }
            }
        }
    }

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

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

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

            }
            else if ( rMEvt.GetClicks() == 2 )
            {
                uno::Reference < i18n::XBreakIterator > xBI = ImplGetBreakIterator();
                i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSelection.Max(),
                         GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
                ImplSetSelection( Selection( aBoundary.startPos, aBoundary.endPos ) );
                ImplCopyToSelectionClipboard();
            }
            else if ( !rMEvt.IsShift() && HasFocus() && aSelection.IsInside( 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(Window::GetPrimarySelection());
            ImplPaste( aSelection );
            ImplModified();
        }
    }

    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 );
            }
        }

        if ( mpUpdateDataTimer && !mbIsSubEdit && mpUpdateDataTimer->IsActive() )
            mpUpdateDataTimer->Start();//do not update while the user is still travelling in the control
    }

    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();
                        ImplModified();
                        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( this, GetFont() );
                    SetSelection( aSaveSel );
                    if ( !aChars.isEmpty() )
                    {
                        ImplInsertText( aChars );
                        ImplModified();
                    }
                    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();
                        uno::Reference < 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;
                            SAL_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;
                            SAL_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 )
                            {
                                i18n::Boundary aBoundary = xBI->getWordBoundary( maText.toString(), aSel.Max(),
                                        GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, true );
                                if ( aBoundary.startPos == aSel.Max() )
                                    aBoundary = xBI->previousWord( maText.toString(), aSel.Max(),
                                            GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
                                aSel.Max() = aBoundary.startPos;
                            }
                            else
                            {
                                sal_Int32 nCount = 1;
                                aSel.Max() = xBI->previousCharacters( maText.toString(), aSel.Max(),
                                        GetSettings().GetLanguageTag().getLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
                            }
                        }
                        else if ( bGoRight && ( aSel.Max() < maText.getLength() ) )
                        {
                            if ( bWord )
                               {
                                i18n::Boundary aBoundary = xBI->nextWord( maText.toString(), aSel.Max(),
                                        GetSettings().GetLanguageTag().getLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
                                aSel.Max() = aBoundary.startPos;
                            }
                            else
                            {
                                sal_Int32 nCount = 1;
                                aSel.Max() = xBI->nextCharacters( maText.toString(), aSel.Max(),
                                        GetSettings().GetLanguageTag().getLocale(), 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;
                        default: break;
                        }
                        sal_Int32 nOldLen = maText.getLength();
                        ImplDelete( maSelection, nDel, nMode );
                        if ( maText.getLength() != nOldLen )
                            ImplModified();
                        bDone = true;
                    }
                }
                break;

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

                case KEY_RETURN:
                    if (maActivateHdl.IsSet())
                    {
                        maActivateHdl.Call(*this);
                        bDone = true;
                    }
                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 )
            ImplModified();

        return bDone;
    }

    void Edit::KeyInput( const KeyEvent& rKEvt )
    {
        if ( mpUpdateDataTimer && !mbIsSubEdit && mpUpdateDataTimer->IsActive() )
            mpUpdateDataTimer->Start();//do not update while the user is still travelling in the control

        if ( mpSubEdit || !ImplHandleKeyEvent( rKEvt ) )
            Control::KeyInput( rKEvt );
    }

    void Edit::FillLayoutData() const
    {
        mpControlData->mpLayoutData.reset( new vcl::ControlLayoutData );
        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, const Size& rSize, DrawFlags nFlags )
    {
        ApplySettings(*pDev);

        Point aPos = pDev->LogicToPixel( rPos );
        Size aSize = pDev->LogicToPixel( rSize );
        vcl::Font aFont = GetDrawPixelFont( pDev );
        OutDevType eOutDevType = pDev->GetOutDevType();

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

        // Border/Background
        pDev->SetLineColor();
        pDev->SetFillColor();
        bool bBorder = !(nFlags & DrawFlags::NoBorder ) && (GetStyle() & WB_BORDER);
        bool bBackground = !(nFlags & DrawFlags::NoBackground) && 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 & DrawFlags::Mono ) || ( eOutDevType == OUTDEV_PRINTER ) )
            pDev->SetTextColor( COL_BLACK );
        else
        {
            if ( !(nFlags & DrawFlags::NoDisable ) && !IsEnabled() )
            {
                const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
                pDev->SetTextColor( rStyleSettings.GetDisableColor() );
            }
            else
            {
                pDev->SetTextColor( GetTextColor() );
            }
        }

        const long nOnePixel = GetDrawPixel( pDev, 1 );
        const 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();
        long        nTextHeight = pDev->GetTextHeight();
        long        nTextWidth = pDev->GetTextWidth( aText );
        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() )
        {
            GetSubEdit()->Draw( pDev, rPos, rSize, nFlags );
        }
    }

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

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

    void Edit::GetFocus()
    {
        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();

            // FIXME: this is currently only on OS X
            // check for other platforms that need similar handling
            if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
                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 ) );
        }

        Control::GetFocus();
    }

    void Edit::LoseFocus()
    {
        if ( mpUpdateDataTimer && !mbIsSubEdit && mpUpdateDataTimer->IsActive() )
        {
            //notify an update latest when the focus is lost
            mpUpdateDataTimer->Stop();
            mpUpdateDataTimer->Invoke();
        }

        if ( !mpSubEdit )
        {
            // FIXME: this is currently only on OS X
            // check for other platforms that need similar handling
            if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
                IsNativeWidgetEnabled() &&
                IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ) )
            {
                ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this );
            }

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

        Control::LoseFocus();
    }

    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
                bool bData = false;
                uno::Reference< datatransfer::clipboard::XClipboard > xClipboard = GetClipboard();

                if ( xClipboard.is() )
                {
                    uno::Reference< datatransfer::XTransferable > xDataObj;
                    {
                        SolarMutexReleaser aReleaser;
                        xDataObj = xClipboard->getContents();
                    }
                    if ( xDataObj.is() )
                    {
                        datatransfer::DataFlavor aFlavor;
                        SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
                        bData = xDataObj->isDataFlavorSupported( aFlavor );
                    }
                }
                bEnablePaste = bData;
            }

            pPopup->EnableItem(pPopup->GetItemId("cut"), bEnableCut);
            pPopup->EnableItem(pPopup->GetItemId("copy"), bEnableCopy);
            pPopup->EnableItem(pPopup->GetItemId("delete"), bEnableDelete);
            pPopup->EnableItem(pPopup->GetItemId("paste"), bEnablePaste);
            pPopup->EnableItem(pPopup->GetItemId("specialchar"), bEnableSpecialChar);
            pPopup->EnableItem(pPopup->GetItemId("undo"), maUndoText != maText.getStr());
            bool bAllSelected = maSelection.Min() == 0 && maSelection.Max() == maText.getLength();
            pPopup->EnableItem(pPopup->GetItemId("selectall"), !bAllSelected);
            pPopup->ShowItem(pPopup->GetItemId("specialchar"), pImplFncGetSpecialChars != nullptr);

            mbActivePopup = true;
            Selection aSaveSel = GetSelection(); // if someone changes selection in Get/LoseFocus, e.g. URL bar
            Point aPos = rCEvt.GetMousePosPixel();
            if ( !rCEvt.IsMouseEvent() )
            {
                // Show menu eventually centered in selection
                Size aSize = GetOutputSizePixel();
                aPos = Point( aSize.Width()/2, aSize.Height()/2 );
            }
            sal_uInt16 n = pPopup->Execute( this, aPos );
            SetSelection( aSaveSel );
            OString sCommand = pPopup->GetItemIdent(n);
            if (sCommand == "undo")
            {
                Undo();
                ImplModified();
            }
            else if (sCommand == "cut")
            {
                Cut();
                ImplModified();
            }
            else if (sCommand == "copy")
            {
                Copy();
            }
            else if (sCommand == "paste")
            {
                Paste();
                ImplModified();
            }
            else if (sCommand == "delete")
            {
                DeleteSelected();
                ImplModified();
            }
            else if (sCommand == "selectall")
            {
                ImplSetSelection( Selection( 0, maText.getLength() ) );
            }
            else if (sCommand == "specialchar" && pImplFncGetSpecialChars)
            {
                OUString aChars = pImplFncGetSpecialChars( this, GetFont() );
                SetSelection( aSaveSel );
                if (!aChars.isEmpty())
                {
                    ImplInsertText( aChars );
                    ImplModified();
                }
            }
            pPopup.clear();
            mbActivePopup = false;
        }
        else if ( rCEvt.GetCommand() == CommandEventId::StartExtTextInput )
        {
            DeleteSelected();
            sal_Int32 nPos = static_cast<sal_Int32>(maSelection.Max());
            mpIMEInfos.reset(new Impl_IMEInfos( nPos, OUString(maText.getStr() + nPos ) ));
            mpIMEInfos->bWasCursorOverwrite = !IsInsertMode();
        }
        else if ( rCEvt.GetCommand() == CommandEventId::EndExtTextInput )
        {
            bool bInsertMode = !mpIMEInfos->bWasCursorOverwrite;
            mpIMEInfos.reset();

            SetInsertMode(bInsertMode);
            ImplModified();

            Invalidate();

            // #i25161# call auto complete handler for ext text commit also
            if (maAutocompleteHdl.IsSet())
            {
                if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.getLength()) )
                {
                    maAutocompleteHdl.Call(*this);
                }
            }
        }
        else if ( rCEvt.GetCommand() == CommandEventId::ExtTextInput )
        {
            const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();

            maText.remove( mpIMEInfos->nPos, mpIMEInfos->nLen );
            maText.insert( mpIMEInfos->nPos, pData->GetText() );
            if ( mpIMEInfos->bWasCursorOverwrite )
            {
                const sal_Int32 nOldIMETextLen = mpIMEInfos->nLen;
                const sal_Int32 nNewIMETextLen = pData->GetText().getLength();
                if ( ( nOldIMETextLen > nNewIMETextLen ) &&
                     ( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
                {
                    // restore old characters
                    const sal_Int32 nRestore = nOldIMETextLen - nNewIMETextLen;
                    maText.insert( mpIMEInfos->nPos + nNewIMETextLen, mpIMEInfos->aOldTextAfterStartPos.copy( nNewIMETextLen, nRestore ) );
                }
                else if ( ( nOldIMETextLen < nNewIMETextLen ) &&
                          ( nOldIMETextLen < mpIMEInfos->aOldTextAfterStartPos.getLength() ) )
                {
                    const sal_Int32 nOverwrite = ( nNewIMETextLen > mpIMEInfos->aOldTextAfterStartPos.getLength()
                        ? mpIMEInfos->aOldTextAfterStartPos.getLength() : nNewIMETextLen ) - nOldIMETextLen;
                    maText.remove( mpIMEInfos->nPos + nNewIMETextLen, nOverwrite );
                }
            }

            if ( pData->GetTextAttr() )
            {
                mpIMEInfos->CopyAttribs( pData->GetTextAttr(), pData->GetText().getLength() );
                mpIMEInfos->bCursor = pData->IsCursorVisible();
            }
            else
            {
                mpIMEInfos->DestroyAttribs();
            }

            ImplAlignAndPaint();
            sal_Int32 nCursorPos = mpIMEInfos->nPos + pData->GetCursorPos();
            SetSelection( Selection( nCursorPos, nCursorPos ) );
            SetInsertMode( !pData->IsCursorOverwrite() );

            if ( pData->IsCursorVisible() )
                GetCursor()->Show();
            else
                GetCursor()->Hide();
        }
        else if ( rCEvt.GetCommand() == CommandEventId::CursorPos )
        {
            if ( mpIMEInfos )
            {
                sal_Int32 nCursorPos = GetSelection().Max();
                SetCursorRect( nullptr, GetTextWidth( maText.toString(), nCursorPos, mpIMEInfos->nPos+mpIMEInfos->nLen-nCursorPos ) );
            }
            else
            {
                SetCursorRect();
            }
        }
        else if ( rCEvt.GetCommand() == CommandEventId::SelectionChange )
        {
            const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData();
            Selection aSelection( pData->GetStart(), pData->GetEnd() );
            SetSelection(aSelection);
        }
        else if ( rCEvt.GetCommand() == CommandEventId::QueryCharPosition )
        {
            if (mpIMEInfos && mpIMEInfos->nLen > 0)
            {
                OUString aText = ImplGetText();
                long   nDXBuffer[256];
                std::unique_ptr<long[]> pDXBuffer;
                long*  pDX = nDXBuffer;

                if( !aText.isEmpty() )
                {
                    if( static_cast<size_t>(2*aText.getLength()) > SAL_N_ELEMENTS(nDXBuffer) )
                    {
                        pDXBuffer.reset(new long[2*(aText.getLength()+1)]);
                        pDX = pDXBuffer.get();
                    }

                    GetCaretPositions( aText, pDX, 0, aText.getLength() );
                }
                long    nTH = GetTextHeight();
                Point   aPos( mnXOffset, ImplGetTextYPosition() );

                std::unique_ptr<tools::Rectangle[]> aRects(new tools::Rectangle[ mpIMEInfos->nLen ]);
                for ( int nIndex = 0; nIndex < mpIMEInfos->nLen; ++nIndex )
                {
                    tools::Rectangle aRect( aPos, Size( 10, nTH ) );
                    aRect.SetLeft( pDX[2*(nIndex+mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraXOffset() );
                    aRects[ nIndex ] = aRect;
                }
                SetCompositionCharRect( aRects.get(), mpIMEInfos->nLen );
            }
        }
        else
            Control::Command( rCEvt );
    }

    void Edit::StateChanged( StateChangedType nType )
    {
        if (nType == StateChangedType::InitShow)
        {
            if (!mpSubEdit)
            {
                mnXOffset = 0;  // if GrabFocus before while size was still wrong
                ImplAlign();
                if (!mpSubEdit)
                    ImplShowCursor(false);
                Invalidate();
            }
        }
        else if (nType == StateChangedType::Enable)
        {
            if (!mpSubEdit)
            {
                // change text color only
                ImplInvalidateOrRepaint();
            }
        }
        else if (nType == StateChangedType::Style || nType == StateChangedType::Mirroring)
        {
            WinBits nStyle = GetStyle();
            if (nType == StateChangedType::Style)
            {
                nStyle = ImplInitStyle(GetStyle());
                SetStyle(nStyle);
            }

            sal_uInt16 nOldAlign = mnAlign;
            mnAlign = EDIT_ALIGN_LEFT;

            // hack: right align until keyinput and cursor travelling works
            // edits are always RTL disabled
            // however the parent edits contain the correct setting
            if (mbIsSubEdit && GetParent()->IsRTLEnabled())
            {
                if (GetParent()->GetStyle() & WB_LEFT)
                    mnAlign = EDIT_ALIGN_RIGHT;
                if (nType == StateChangedType::Mirroring)
                    SetLayoutMode(ComplexTextLayoutFlags::BiDiRtl | ComplexTextLayoutFlags::TextOriginLeft);
            }
            else if (mbIsSubEdit && !GetParent()->IsRTLEnabled())
            {
                if (nType == StateChangedType::Mirroring)
                    SetLayoutMode(ComplexTextLayoutFlags::TextOriginLeft);
            }

            if (nStyle & WB_RIGHT)
                mnAlign = EDIT_ALIGN_RIGHT;
            else if (nStyle & WB_CENTER)
                mnAlign = EDIT_ALIGN_CENTER;
            if (!maText.isEmpty() && (mnAlign != nOldAlign))
            {
                ImplAlign();
                Invalidate();
            }

        }
        else if (nType == StateChangedType::Zoom)
        {
            if (!mpSubEdit)
            {
                ApplySettings(*this);
                ImplShowCursor();
                Invalidate();
            }
        }
        else if (nType == StateChangedType::ControlFont)
        {
            if (!mpSubEdit)
            {
                ApplySettings(*this);
                ImplShowCursor();
                Invalidate();
            }
        }
        else if (nType == StateChangedType::ControlForeground)
        {
            if (!mpSubEdit)
            {
                ApplySettings(*this);
                Invalidate();
            }
        }
        else if (nType == StateChangedType::ControlBackground)
        {
            if (!mpSubEdit)
            {
                ApplySettings(*this);
                Invalidate();
            }
        }

        Control::StateChanged(nType);
    }

    void Edit::DataChanged( const DataChangedEvent& rDCEvt )
    {
        if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) ||
             (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ||
             ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) &&
              (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) )
        {
            if ( !mpSubEdit )
            {
                ApplySettings(*this);
                ImplShowCursor();
                Invalidate();
            }
        }

        Control::DataChanged( rDCEvt );
    }

    void Edit::ImplShowDDCursor()
    {
        if (!mpDDInfo->bVisCursor)
        {
            long nTextWidth = GetTextWidth( maText.toString(), 0, mpDDInfo->nDropPos );
            long nTextHeight = GetTextHeight();
            tools::Rectangle aCursorRect( Point( nTextWidth + mnXOffset, (GetOutputSize().Height()-nTextHeight)/2 ), Size( 2, nTextHeight ) );
            mpDDInfo->aCursor.SetWindow( this );
            mpDDInfo->aCursor.SetPos( aCursorRect.TopLeft() );
            mpDDInfo->aCursor.SetSize( aCursorRect.GetSize() );
            mpDDInfo->aCursor.Show();
            mpDDInfo->bVisCursor = true;
        }
    }

    void Edit::ImplHideDDCursor()
    {
        if ( mpDDInfo && mpDDInfo->bVisCursor )
        {
            mpDDInfo->aCursor.Hide();
            mpDDInfo->bVisCursor = false;
        }
    }

    TextFilter::TextFilter(const OUString &rForbiddenChars)
        : sForbiddenChars(rForbiddenChars)
    {
    }

    TextFilter::~TextFilter()
    {
    }

    OUString TextFilter::filter(const OUString &rText)
    {
        OUString sTemp(rText);
        for (sal_Int32 i = 0; i < sForbiddenChars.getLength(); ++i)
        {
            sTemp = sTemp.replaceAll(OUStringLiteral1(sForbiddenChars[i]), "");
        }
        return sTemp;
    }

    void Edit::filterText()
    {
        Selection aSel = GetSelection();
        const OUString sOrig = GetText();
        const OUString sNew = mpFilterText->filter(GetText());
        if (sOrig != sNew)
        {
            sal_Int32 nDiff = sOrig.getLength() - sNew.getLength();
            if (nDiff)
            {
                aSel.setMin(aSel.getMin() - nDiff);
                aSel.setMax(aSel.getMin());
            }
            SetText(sNew);
            SetSelection(aSel);
        }
    }

    void Edit::Modify()
    {
        if (mpFilterText)
            filterText();

        if ( mbIsSubEdit )
        {
            static_cast<Edit*>(GetParent())->Modify();
        }
        else
        {
            if ( mpUpdateDataTimer )
                mpUpdateDataTimer->Start();

            if ( ImplCallEventListenersAndHandler( VclEventId::EditModify, [this] () { maModifyHdl.Call(*this); } ) )
                // have been destroyed while calling into the handlers
                return;

            // #i13677# notify edit listeners about caret position change
            CallEventListeners( VclEventId::EditCaretChanged );
            // FIXME: this is currently only on OS X
            // check for other platforms that need similar handling
            if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
                IsNativeWidgetEnabled() &&
                IsNativeControlSupported( ControlType::Editbox, ControlPart::Entire ) )
            {
                ImplInvalidateOutermostBorder( this );
            }
        }
    }

    void Edit::UpdateData()
    {
        maUpdateDataHdl.Call( *this );
    }

    IMPL_LINK_NOARG(Edit, ImplUpdateDataHdl, Timer *, void)
    {
        UpdateData();
    }

    void Edit::EnableUpdateData( sal_uLong nTimeout )
    {
        if ( !nTimeout )
            DisableUpdateData();
        else
        {
            if ( !mpUpdateDataTimer )
            {
                mpUpdateDataTimer.reset(new Timer("UpdateDataTimer"));
                mpUpdateDataTimer->SetInvokeHandler( LINK( this, Edit, ImplUpdateDataHdl ) );
                mpUpdateDataTimer->SetDebugName( "vcl::Edit mpUpdateDataTimer" );
            }

            mpUpdateDataTimer->SetTimeout( nTimeout );
        }
    }

    void Edit::DisableUpdateData()
    {
        mpUpdateDataTimer.reset();
    }

    void Edit::SetEchoChar( sal_Unicode c )
    {
        mcEchoChar = c;
        if ( mpSubEdit )
            mpSubEdit->SetEchoChar( c );
    }

    void Edit::SetReadOnly( bool bReadOnly )
    {
        if ( mbReadOnly != bReadOnly )
        {
            mbReadOnly = bReadOnly;
            if ( mpSubEdit )
                mpSubEdit->SetReadOnly( bReadOnly );

            CompatStateChanged( StateChangedType::ReadOnly );
        }
    }

    void Edit::SetSelectAllSingleClick( bool bSelectAllSingleClick )
    {
        if ( mbSelectAllSingleClick != bSelectAllSingleClick )
        {
            mbSelectAllSingleClick = bSelectAllSingleClick;
            if ( mpSubEdit )
                mpSubEdit->SetSelectAllSingleClick( bSelectAllSingleClick );
        }
    }
    void Edit::SetInsertMode( bool bInsert )
    {
        if ( bInsert != mbInsertMode )
        {
            mbInsertMode = bInsert;
            if ( mpSubEdit )
                mpSubEdit->SetInsertMode( bInsert );
            else
                ImplShowCursor();
        }
    }

    bool Edit::IsInsertMode() const
    {
        if ( mpSubEdit )
            return mpSubEdit->IsInsertMode();
        else
            return mbInsertMode;
    }

    void Edit::SetMaxTextLen(sal_Int32 nMaxLen)
    {
        mnMaxTextLen = nMaxLen > 0 ? nMaxLen : EDIT_NOLIMIT;

        if ( mpSubEdit )
            mpSubEdit->SetMaxTextLen( mnMaxTextLen );
        else
        {
            if ( maText.getLength() > mnMaxTextLen )
                ImplDelete( Selection( mnMaxTextLen, maText.getLength() ), EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
        }
    }

    void Edit::SetSelection( const Selection& rSelection )
    {
        // If the selection was changed from outside, e.g. by MouseButtonDown, don't call Tracking()
        // directly afterwards which would change the selection again
        if ( IsTracking() )
            EndTracking();
        else if ( mpSubEdit && mpSubEdit->IsTracking() )
            mpSubEdit->EndTracking();

        ImplSetSelection( rSelection );
    }

    void Edit::ImplSetSelection( const Selection& rSelection, bool bPaint )
    {
        if ( mpSubEdit )
            mpSubEdit->ImplSetSelection( rSelection );
        else
        {
            if ( rSelection != maSelection )
            {
                Selection aOld( maSelection );
                Selection aNew( rSelection );

                if ( aNew.Min() > maText.getLength() )
                    aNew.Min() = maText.getLength();
                if ( aNew.Max() > maText.getLength() )
                    aNew.Max() = maText.getLength();
                if ( aNew.Min() < 0 )
                    aNew.Min() = 0;
                if ( aNew.Max() < 0 )
                    aNew.Max() = 0;

                if ( aNew != maSelection )
                {
                    ImplClearLayoutData();
                    Selection aTemp = maSelection;
                    maSelection = aNew;

                    if ( bPaint && ( aOld.Len() || aNew.Len() || IsPaintTransparent() ) )
                        ImplInvalidateOrRepaint();
                    ImplShowCursor();

                    bool bCaret = false, bSelection = false;
                    long nB=aNew.Max(), nA=aNew.Min(),oB=aTemp.Max(), oA=aTemp.Min();
                    long nGap = nB-nA, oGap = oB-oA;
                    if (nB != oB)
                        bCaret = true;
                    if (nGap != 0 || oGap != 0)
                        bSelection = true;

                    if (bSelection)
                    {
                        if ( mbIsSubEdit )
                            static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::EditSelectionChanged );
                        else
                            CallEventListeners( VclEventId::EditSelectionChanged );
                    }

                    if (bCaret)
                    {
                        if ( mbIsSubEdit )
                            static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::EditCaretChanged );
                        else
                            CallEventListeners( VclEventId::EditCaretChanged );
                    }

                    // #103511# notify combobox listeners of deselection
                    if( !maSelection && GetParent() && GetParent()->GetType() == WindowType::COMBOBOX )
                        static_cast<Edit*>(GetParent())->CallEventListeners( VclEventId::ComboboxDeselect );
                }
            }
        }
    }

    const Selection& Edit::GetSelection() const
    {
        if ( mpSubEdit )
            return mpSubEdit->GetSelection();
        else
            return maSelection;
    }

    void Edit::ReplaceSelected( const OUString& rStr )
    {
        if ( mpSubEdit )
            mpSubEdit->ReplaceSelected( rStr );
        else
            ImplInsertText( rStr );
    }

    void Edit::DeleteSelected()
    {
        if ( mpSubEdit )
            mpSubEdit->DeleteSelected();
        else
        {
            if ( maSelection.Len() )
                ImplDelete( maSelection, EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
        }
    }

    OUString Edit::GetSelected() const
    {
        if ( mpSubEdit )
            return mpSubEdit->GetSelected();
        else
        {
            Selection aSelection( maSelection );
            aSelection.Justify();
            return OUString( maText.getStr() + aSelection.Min(), aSelection.Len() );
        }
    }

    void Edit::Cut()
    {
        if ( !mbPassword )
        {
            Copy();
            ReplaceSelected( OUString() );
        }
    }

    void Edit::Copy()
    {
        if ( !mbPassword )
        {
            css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetClipboard());
            ImplCopy( aClipboard );
        }
    }

    void Edit::Paste()
    {
        css::uno::Reference<css::datatransfer::clipboard::XClipboard> aClipboard(GetClipboard());
        ImplPaste( aClipboard );
    }

    void Edit::Undo()
    {
        if ( mpSubEdit )
            mpSubEdit->Undo();
        else
        {
            const OUString aText( maText.toString() );
            ImplDelete( Selection( 0, aText.getLength() ), EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
            ImplInsertText( maUndoText );
            ImplSetSelection( Selection( 0, maUndoText.getLength() ) );
            maUndoText = aText;
        }
    }

    void Edit::SetText( const OUString& rStr )
    {
        if ( mpSubEdit )
            mpSubEdit->SetText( rStr ); // not directly ImplSetText if SetText overridden
        else
        {
            Selection aNewSel( 0, 0 );  // prevent scrolling
            ImplSetText( rStr, &aNewSel );
        }
    }

    void Edit::SetText( const OUString& rStr, const Selection& rSelection )
    {
        if ( mpSubEdit )
            mpSubEdit->SetText( rStr, rSelection );
        else
            ImplSetText( rStr, &rSelection );
    }

    OUString Edit::GetText() const
    {
        if ( mpSubEdit )
            return mpSubEdit->GetText();
        else
            return maText.toString();
    }

    void Edit::SetCursorAtLast(){
        ImplSetCursorPos( GetText().getLength(), false );
    }

    void Edit::SetPlaceholderText( const OUString& rStr )
    {
        if ( mpSubEdit )
            mpSubEdit->SetPlaceholderText( rStr );
        else if ( maPlaceholderText != rStr )
        {
            maPlaceholderText = rStr;
            if ( GetText().isEmpty() )
                Invalidate();
        }
    }

    OUString Edit::GetPlaceholderText() const
    {
        if ( mpSubEdit )
            return mpSubEdit->GetPlaceholderText();

        return maPlaceholderText;
    }

    void Edit::SetModifyFlag()
    {
        if ( mpSubEdit )
            mpSubEdit->mbModified = true;
        else
            mbModified = true;
    }

    void Edit::ClearModifyFlag()
    {
        if ( mpSubEdit )
            mpSubEdit->mbModified = false;
        else
            mbModified = false;
    }

    void Edit::SetSubEdit(Edit* pEdit)
    {
        mpSubEdit.disposeAndClear();
        mpSubEdit.set(pEdit);

        if (mpSubEdit)
        {
            SetPointer(PointerStyle::Arrow);    // Only SubEdit has the BEAM...
            mpSubEdit->mbIsSubEdit = true;

            mpSubEdit->SetReadOnly(mbReadOnly);
            mpSubEdit->maAutocompleteHdl = maAutocompleteHdl;
        }
    }

    Size Edit::CalcMinimumSizeForText(const OUString &rString) const
    {
        ControlType eCtrlType = ImplGetNativeControlType();

        Size aSize;
        if (mnWidthInChars != -1)
        {
            //CalcSize calls CalcWindowSize, but we will call that also in this
            //function, so undo the first one with CalcOutputSize
            aSize = CalcOutputSize(CalcSize(mnWidthInChars));
        }
        else
        {
            OUString aString;
            if (mnMaxWidthChars != -1 && mnMaxWidthChars < rString.getLength())
                aString = rString.copy(0, mnMaxWidthChars);
            else
                aString = rString;

            aSize.setHeight( GetTextHeight() );
            aSize.setWidth( GetTextWidth(aString) );
            aSize.AdjustWidth(ImplGetExtraXOffset() * 2 );

            // do not create edit fields in which one cannot enter anything
            // a default minimum width should exist for at least 3 characters

            //CalcSize calls CalcWindowSize, but we will call that also in this
            //function, so undo the first one with CalcOutputSize
            Size aMinSize(CalcOutputSize(CalcSize(3)));
            if (aSize.Width() < aMinSize.Width())
                aSize.setWidth( aMinSize.Width() );
        }

        aSize.AdjustHeight(ImplGetExtraYOffset() * 2 );

        aSize = CalcWindowSize( aSize );

        // ask NWF what if it has an opinion, too
        ImplControlValue aControlValue;
        tools::Rectangle aRect( Point( 0, 0 ), aSize );
        tools::Rectangle aContent, aBound;
        if (GetNativeControlRegion(eCtrlType, ControlPart::Entire, aRect, ControlState::NONE,
                                   aControlValue, aBound, aContent))
        {
            if (aBound.GetHeight() > aSize.Height())
                aSize.setHeight( aBound.GetHeight() );
        }
        return aSize;
    }

    Size Edit::CalcMinimumSize() const
    {
        return CalcMinimumSizeForText(GetText());
    }

    Size Edit::GetMinimumEditSize()
    {
        vcl::Window* pDefWin = ImplGetDefaultWindow();
        ScopedVclPtrInstance< Edit > aEdit(  pDefWin, WB_BORDER  );
        Size aSize( aEdit->CalcMinimumSize() );
        return aSize;
    }

    Size Edit::GetOptimalSize() const
    {
        return CalcMinimumSize();
    }

    Size Edit::CalcSize(sal_Int32 nChars) const
    {
        // width for N characters, independent from content.
        // works only correct for fixed fonts, average otherwise
        Size aSz( GetTextWidth( "x" ), GetTextHeight() );
        aSz.setWidth( aSz.Width() * nChars );
        aSz.AdjustWidth(ImplGetExtraXOffset() * 2 );
        aSz = CalcWindowSize( aSz );
        return aSz;
    }

    sal_Int32 Edit::GetMaxVisChars() const
    {
        const vcl::Window* pW = mpSubEdit ? mpSubEdit : this;
        sal_Int32 nOutWidth = pW->GetOutputSizePixel().Width();
        sal_Int32 nCharWidth = GetTextWidth( "x" );
        return nCharWidth ? nOutWidth/nCharWidth : 0;
    }

    sal_Int32 Edit::GetCharPos( const Point& rWindowPos ) const
    {
        return ImplGetCharPos( rWindowPos );
    }

    void Edit::SetGetSpecialCharsFunction( FncGetSpecialChars fn )
    {
        pImplFncGetSpecialChars = fn;
    }

    FncGetSpecialChars Edit::GetGetSpecialCharsFunction()
    {
        return pImplFncGetSpecialChars;
    }

    VclPtr<PopupMenu> Edit::CreatePopupMenu()
    {
        if (!mpUIBuilder)
            mpUIBuilder.reset(new VclBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "vcl/ui/editmenu.ui", ""));
        VclPtr<PopupMenu> pPopup = mpUIBuilder->get_menu("menu");
        const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
        if (rStyleSettings.GetHideDisabledMenuItems())
            pPopup->SetMenuFlags( MenuFlags::HideDisabledEntries );
        else
            pPopup->SetMenuFlags ( MenuFlags::AlwaysShowDisabledEntries );
        if (rStyleSettings.GetContextMenuShortcuts())
        {
            pPopup->SetAccelKey(pPopup->GetItemId("undo"), vcl::KeyCode( KeyFuncType::UNDO));
            pPopup->SetAccelKey(pPopup->GetItemId("cut"), vcl::KeyCode( KeyFuncType::CUT));
            pPopup->SetAccelKey(pPopup->GetItemId("copy"), vcl::KeyCode( KeyFuncType::COPY));
            pPopup->SetAccelKey(pPopup->GetItemId("paste"), vcl::KeyCode( KeyFuncType::PASTE));
            pPopup->SetAccelKey(pPopup->GetItemId("delete"), vcl::KeyCode( KeyFuncType::DELETE));
            pPopup->SetAccelKey(pPopup->GetItemId("selectall"), vcl::KeyCode( KEY_A, false, true, false, false));
            pPopup->SetAccelKey(pPopup->GetItemId("specialchar"), vcl::KeyCode( KEY_S, true, true, false, false));
        }
        return pPopup;
    }

    // css::datatransfer::dnd::XDragGestureListener
    void Edit::dragGestureRecognized( const css::datatransfer::dnd::DragGestureEvent& rDGE )
    {
        SolarMutexGuard aVclGuard;

        if ( !IsTracking() && maSelection.Len() &&
             !mbPassword && (!mpDDInfo || !mpDDInfo->bStarterOfDD) ) // no repeated D&D
        {
            Selection aSel( maSelection );
            aSel.Justify();

            // only if mouse in the selection...
            Point aMousePos( rDGE.DragOriginX, rDGE.DragOriginY );
            sal_Int32 nCharPos = ImplGetCharPos( aMousePos );
            if ( (nCharPos >= aSel.Min()) && (nCharPos < aSel.Max()) )
            {
                if ( !mpDDInfo )
                    mpDDInfo.reset(new DDInfo);

                mpDDInfo->bStarterOfDD = true;
                mpDDInfo->aDndStartSel = aSel;

                if ( IsTracking() )
                    EndTracking();  // before D&D disable tracking

                vcl::unohelper::TextDataObject* pDataObj = new vcl::unohelper::TextDataObject( GetSelected() );
                sal_Int8 nActions = datatransfer::dnd::DNDConstants::ACTION_COPY;
                if ( !IsReadOnly() )
                    nActions |= datatransfer::dnd::DNDConstants::ACTION_MOVE;
                rDGE.DragSource->startDrag( rDGE, nActions, 0 /*cursor*/, 0 /*image*/, pDataObj, mxDnDListener );
                if ( GetCursor() )
                    GetCursor()->Hide();

            }
        }
    }

    // css::datatransfer::dnd::XDragSourceListener
    void Edit::dragDropEnd( const css::datatransfer::dnd::DragSourceDropEvent& rDSDE )
    {
        SolarMutexGuard aVclGuard;

        if (rDSDE.DropSuccess && (rDSDE.DropAction & datatransfer::dnd::DNDConstants::ACTION_MOVE) && mpDDInfo)
        {
            Selection aSel( mpDDInfo->aDndStartSel );
            if ( mpDDInfo->bDroppedInMe )
            {
                if ( aSel.Max() > mpDDInfo->nDropPos )
                {
                    long nLen = aSel.Len();
                    aSel.Min() += nLen;
                    aSel.Max() += nLen;
                }
            }
            ImplDelete( aSel, EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );
            ImplModified();
        }

        ImplHideDDCursor();
        mpDDInfo.reset();
    }

    // css::datatransfer::dnd::XDropTargetListener
    void Edit::drop( const css::datatransfer::dnd::DropTargetDropEvent& rDTDE )
    {
        SolarMutexGuard aVclGuard;

        bool bChanges = false;
        if ( !mbReadOnly && mpDDInfo )
        {
            ImplHideDDCursor();

            Selection aSel( maSelection );
            aSel.Justify();

            if ( aSel.Len() && !mpDDInfo->bStarterOfDD )
                ImplDelete( aSel, EDIT_DEL_RIGHT, EDIT_DELMODE_SIMPLE );

            mpDDInfo->bDroppedInMe = true;

            aSel.Min() = mpDDInfo->nDropPos;
            aSel.Max() = mpDDInfo->nDropPos;
            ImplSetSelection( aSel );

            uno::Reference< datatransfer::XTransferable > xDataObj = rDTDE.Transferable;
            if ( xDataObj.is() )
            {
                datatransfer::DataFlavor aFlavor;
                SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING, aFlavor );
                if ( xDataObj->isDataFlavorSupported( aFlavor ) )
                {
                    uno::Any aData = xDataObj->getTransferData( aFlavor );
                    OUString aText;
                    aData >>= aText;
                    ImplInsertText( aText );
                    bChanges = true;
                    ImplModified();
                }
            }

            if ( !mpDDInfo->bStarterOfDD )
            {
                mpDDInfo.reset();
            }
        }

        rDTDE.Context->dropComplete( bChanges );
    }

    void Edit::dragEnter( const css::datatransfer::dnd::DropTargetDragEnterEvent& rDTDE )
    {
        if ( !mpDDInfo )
        {
            mpDDInfo.reset(new DDInfo);
        }
        // search for string data type
        const Sequence< css::datatransfer::DataFlavor >& rFlavors( rDTDE.SupportedDataFlavors );
        sal_Int32 nEle = rFlavors.getLength();
        mpDDInfo->bIsStringSupported = false;
        for( sal_Int32 i = 0; i < nEle; i++ )
        {
            sal_Int32 nIndex = 0;
            const OUString aMimetype = rFlavors[i].MimeType.getToken( 0, ';', nIndex );
            if ( aMimetype == "text/plain" )
            {
                mpDDInfo->bIsStringSupported = true;
                break;
            }
        }
    }

    void Edit::dragExit( const css::datatransfer::dnd::DropTargetEvent& )
    {
        SolarMutexGuard aVclGuard;

        ImplHideDDCursor();
    }

    void Edit::dragOver( const css::datatransfer::dnd::DropTargetDragEvent& rDTDE )
    {
        SolarMutexGuard aVclGuard;

        Point aMousePos( rDTDE.LocationX, rDTDE.LocationY );

        sal_Int32 nPrevDropPos = mpDDInfo->nDropPos;
        mpDDInfo->nDropPos = ImplGetCharPos( aMousePos );

        /*
        Size aOutSize = GetOutputSizePixel();
        if ( ( aMousePos.X() < 0 ) || ( aMousePos.X() > aOutSize.Width() ) )
        {
            // Scroll?
            // No, I will not receive events in this case....
        }
        */

        Selection aSel( maSelection );
        aSel.Justify();

        // Don't accept drop in selection or read-only field...
        if ( IsReadOnly() || aSel.IsInside( mpDDInfo->nDropPos ) || ! mpDDInfo->bIsStringSupported )
        {
            ImplHideDDCursor();
            rDTDE.Context->rejectDrag();
        }
        else
        {
            // draw the old cursor away...
            if ( !mpDDInfo->bVisCursor || ( nPrevDropPos != mpDDInfo->nDropPos ) )
            {
                ImplHideDDCursor();
                ImplShowDDCursor();
            }
            rDTDE.Context->acceptDrag( rDTDE.DropAction );
        }
    }

    OUString Edit::GetSurroundingText() const
    {
        if (mpSubEdit)
            return mpSubEdit->GetSurroundingText();
        return maText.toString();
    }

    Selection Edit::GetSurroundingTextSelection() const
    {
      return GetSelection();
    }

    FactoryFunction Edit::GetUITestFactory() const
    {
        return EditUIObject::create;
    }

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

↑ V623 Consider inspecting the '?:' operator. A temporary object is being created and subsequently destroyed. Check third operand.

↑ V656 Variables 'maSelection.Min()', 'maSelection.Max()' are initialized through the call to the same function. It's probably an error or un-optimized code. Consider inspecting the 'aSelection.Min()' expression. Check lines: 740, 741.