/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"

#include <tools/rc.h>
#include <vcl/decoview.hxx>
#include <vcl/event.hxx>
#include <vcl/cursor.hxx>
#include <vcl/virdev.hxx>
#include <vcl/menu.hxx>
#include <vcl/cmdevt.h>
#include <vcl/edit.hxx>
#include <vcl/svapp.hxx>
#include <vcl/msgbox.hxx>

#include <window.h>
#include <svdata.hxx>
#include <svids.hrc>
#include <subedit.hxx>
#include <controldata.hxx>

#include <vos/mutex.hxx>


#include <com/sun/star/i18n/XBreakIterator.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>

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

#ifndef _COM_SUN_STAR_I18N_XEXTENDEDINPUTSEQUENCECHECKER_HDL_
#include <com/sun/star/i18n/XExtendedInputSequenceChecker.hpp>
#endif
#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/configurationhelper.hxx>

#include <sot/exchange.hxx>
#include <sot/formats.hxx>
#include <rtl/memory.h>

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




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

// - Redo
// - Bei Tracking-Cancel DefaultSelection wieder herstellen

// =======================================================================

static FncGetSpecialChars pImplFncGetSpecialChars = NULL;

// =======================================================================

#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
{
	Cursor			aCursor;
    Selection       aDndStartSel;
	xub_StrLen		nDropPos;
	sal_Bool			bStarterOfDD;
	sal_Bool			bDroppedInMe;
	sal_Bool			bVisCursor;
    sal_Bool            bIsStringSupported;

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

// =======================================================================

struct Impl_IMEInfos
{
    String      aOldTextAfterStartPos;
	sal_uInt16* 	pAttribs;
	xub_StrLen	nPos;
	xub_StrLen	nLen;
	sal_Bool		bCursor;
	sal_Bool		bWasCursorOverwrite;

				Impl_IMEInfos( xub_StrLen nPos, const String& rOldTextAfterStartPos );
				~Impl_IMEInfos();

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

// -----------------------------------------------------------------------

Impl_IMEInfos::Impl_IMEInfos( xub_StrLen nP, const String& rOldTextAfterStartPos )
 : aOldTextAfterStartPos( rOldTextAfterStartPos )
{
	nPos = nP;
	nLen = 0;
	bCursor = sal_True;
	pAttribs = NULL;
	bWasCursorOverwrite = sal_False;
}

// -----------------------------------------------------------------------

Impl_IMEInfos::~Impl_IMEInfos()
{
	delete[] pAttribs;
}

// -----------------------------------------------------------------------

void Impl_IMEInfos::CopyAttribs( const xub_StrLen* pA, xub_StrLen nL )
{
	nLen = nL;
	delete[] pAttribs;
	pAttribs = new sal_uInt16[ nL ];
	rtl_copyMemory( pAttribs, pA, nL*sizeof(sal_uInt16) );
}

// -----------------------------------------------------------------------

void Impl_IMEInfos::DestroyAttribs()
{
	delete[] pAttribs;
	pAttribs = NULL;
	nLen = 0;
}

// =======================================================================

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

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

Edit::Edit( Window* pParent, const ResId& rResId ) :
    Control( WINDOW_EDIT )
{
    ImplInitEditData();
    rResId.SetRT( RSC_EDIT );
    WinBits nStyle = ImplInitRes( rResId );
    ImplInit( pParent, nStyle );
    ImplLoadRes( rResId );

    // Derived MultiLineEdit takes care to call Show only after MultiLineEdit
    // ctor has already started:
    if ( !(nStyle & WB_HIDE) && rResId.GetRT() != RSC_MULTILINEEDIT )
        Show();
}

// -----------------------------------------------------------------------

Edit::Edit( Window* pParent, const ResId& rResId, bool bDisableAccessibleLabeledByRelation ) :
    Control( WINDOW_EDIT )
{
    ImplInitEditData();
    rResId.SetRT( RSC_EDIT );
    WinBits nStyle = ImplInitRes( rResId );
    ImplInit( pParent, nStyle );
    ImplLoadRes( rResId );
    if ( bDisableAccessibleLabeledByRelation )
        ImplGetWindowImpl()->mbDisableAccessibleLabeledByRelation = sal_True;

    // Derived MultiLineEdit takes care to call Show only after MultiLineEdit
    // ctor has already started:
    if ( !(nStyle & WB_HIDE) && rResId.GetRT() != RSC_MULTILINEEDIT )
        Show();
}

// -----------------------------------------------------------------------

Edit::~Edit()
{
	delete mpDDInfo;
	Cursor* pCursor = GetCursor();
	if ( pCursor )
	{
		SetCursor( NULL );
		delete pCursor;
	}

	delete mpIMEInfos;

	if ( mpUpdateDataTimer )
		delete mpUpdateDataTimer;

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

// -----------------------------------------------------------------------

void Edit::ImplInitEditData()
{
	mpSubEdit				= NULL;
	mpUpdateDataTimer		= NULL;
	mnXOffset				= 0;
	mnAlign 				= EDIT_ALIGN_LEFT;
	mnMaxTextLen			= EDIT_NOLIMIT;
	meAutocompleteAction	= AUTOCOMPLETE_KEYINPUT;
	mbModified				= sal_False;
	mbInternModified		= sal_False;
	mbReadOnly				= sal_False;
	mbInsertMode			= sal_True;
	mbClickedInSelection	= sal_False;
	mbActivePopup			= sal_False;
	mbIsSubEdit 			= sal_False;
	mbInMBDown				= sal_False;
	mpDDInfo				= NULL;
	mpIMEInfos				= NULL;
	mcEchoChar				= 0;

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

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

// -----------------------------------------------------------------------

bool Edit::ImplUseNativeBorder( WinBits nStyle )
{
    bool bRet =
        IsNativeControlSupported(ImplGetNativeControlType(), HAS_BACKGROUND_TEXTURE)
                                 && ((nStyle&WB_BORDER) && !(nStyle&WB_NOBORDER));
    if( ! bRet && mbIsSubEdit )
    {
        Window* pWindow = GetParent();
        nStyle = pWindow->GetStyle();
        bRet = pWindow->IsNativeControlSupported(ImplGetNativeControlType(), HAS_BACKGROUND_TEXTURE)
               && ((nStyle&WB_BORDER) && !(nStyle&WB_NOBORDER));
    }
    return bRet;
}

void Edit::ImplInit( Window* pParent, WinBits nStyle )
{
	nStyle = ImplInitStyle( nStyle );
	if ( !(nStyle & (WB_CENTER | WB_RIGHT)) )
		nStyle |= WB_LEFT;

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

	mbReadOnly = (nStyle & WB_READONLY) != 0;

	mnAlign = EDIT_ALIGN_LEFT;

    // --- RTL --- 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 Cursor );

    SetPointer( Pointer( POINTER_TEXT ) );
    ImplInitSettings( sal_True, sal_True, sal_True );

    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( sal_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;
}

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

void Edit::ImplModified()
{
	mbModified = sal_True;
	Modify();
}

// -----------------------------------------------------------------------

void Edit::ImplInitSettings( sal_Bool bFont, sal_Bool bForeground, sal_Bool bBackground )
{
	const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();

	if ( bFont )
	{
		Font aFont = rStyleSettings.GetFieldFont();
		if ( IsControlFont() )
			aFont.Merge( GetControlFont() );
		SetZoomedPointFont( aFont );
        ImplClearLayoutData();
	}

	if ( bFont || bForeground )
	{
		Color aTextColor = rStyleSettings.GetFieldTextColor();
		if ( IsControlForeground() )
			aTextColor = GetControlForeground();
		SetTextColor( aTextColor );
	}

	if ( bBackground )
	{
		if ( ImplUseNativeBorder( GetStyle() ) || IsPaintTransparent() )
		{
			// Transparent background
			SetBackground();
			SetFillColor();
		}
		else if ( IsControlBackground() )
		{
			SetBackground( GetControlBackground() );
			SetFillColor( GetControlBackground() );
		}
		else
		{
			SetBackground( rStyleSettings.GetFieldColor() );
			SetFillColor( rStyleSettings.GetFieldColor() );
		}
	}
}

// -----------------------------------------------------------------------

long Edit::ImplGetExtraOffset() 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;
}


// -----------------------------------------------------------------------

XubString Edit::ImplGetText() const
{
	if ( mcEchoChar || (GetStyle() & WB_PASSWORD) )
	{
		XubString	aText;
		xub_Unicode cEchoChar;
		if ( mcEchoChar )
			cEchoChar = mcEchoChar;
		else
			cEchoChar = '*';
		aText.Fill( maText.Len(), cEchoChar );
		return aText;
	}
	else
		return maText;
}

// -----------------------------------------------------------------------

void Edit::ImplInvalidateOrRepaint( xub_StrLen nStart, xub_StrLen nEnd )
{
    if( IsPaintTransparent() )
    {
        Invalidate();
        // FIXME: this is currently only on aqua
        if( ImplGetSVData()->maNWFData.mbNoFocusRects )
            Update();
    }
    else
        ImplRepaint( nStart, nEnd );
}

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

void Edit::ImplRepaint( xub_StrLen nStart, xub_StrLen nEnd, bool bLayout )
{
	if ( !IsReallyVisible() )
		return;

	XubString aText = ImplGetText();
    nStart = 0;
    nEnd = aText.Len();

    sal_Int32	nDXBuffer[256];
    sal_Int32*	pDXBuffer = NULL;
    sal_Int32*	pDX = nDXBuffer;

    if( aText.Len() )
    {
        if( 2*aText.Len() > xub_StrLen(sizeof(nDXBuffer)/sizeof(nDXBuffer[0])) )
        {
            pDXBuffer = new sal_Int32[2*(aText.Len()+1)];
            pDX = pDXBuffer;
        }

        GetCaretPositions( aText, pDX, nStart, nEnd );
    }

	long	nTH = GetTextHeight();
	Point	aPos( mnXOffset, ImplGetTextYPosition() );

    if( bLayout )
    {
        long nPos = nStart ? pDX[2*nStart] : 0;
        aPos.X() = nPos + mnXOffset + ImplGetExtraOffset();

        MetricVector* pVector = &mpControlData->mpLayoutData->m_aUnicodeBoundRects;
        String* pDisplayText = &mpControlData->mpLayoutData->m_aDisplayText;

		DrawText( aPos, aText, nStart, nEnd - nStart, pVector, pDisplayText );

        if( pDXBuffer )
            delete [] pDXBuffer;
        return;
    }

	Cursor* pCursor = GetCursor();
	sal_Bool bVisCursor = pCursor ? pCursor->IsVisible() : sal_False;
	if ( pCursor )
		pCursor->Hide();

    ImplClearBackground( 0, GetOutputSizePixel().Width() );

	const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
	if ( IsEnabled() )
		ImplInitSettings( sal_False, sal_True, sal_False );
	else
		SetTextColor( rStyleSettings.GetDisableColor() );

    // Set background color of the normal text
    if( (GetStyle() & WB_FORCECTRLBACKGROUND) != 0 && IsControlBackground() )
    {
        // check if we need to set ControlBackground even in NWF case
        Push( PUSH_FILLCOLOR | PUSH_LINECOLOR );
        SetLineColor();
        SetFillColor( GetControlBackground() );
        DrawRect( Rectangle( aPos, Size( GetOutputSizePixel().Width() - 2*mnXOffset, nTH ) ) );
        Pop();

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

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

    long nPos = nStart ? pDX[2*nStart] : 0;
    aPos.X() = nPos + mnXOffset + ImplGetExtraOffset();
	if ( !bDrawSelection && !mpIMEInfos )
	{
		DrawText( aPos, aText, nStart, nEnd - nStart );
	}
	else
	{
        // save graphics state
        Push();
        // first calculate higlighted and non highlighted clip regions
        Region aHiglightClipRegion;
        Region aNormalClipRegion;
		Selection aTmpSel( maSelection );
		aTmpSel.Justify();
        // selection is highlighted
        int i;
        for( i = 0; i < aText.Len(); i++ )
        {
            Rectangle aRect( aPos, Size( 10, nTH ) );
            aRect.Left() = pDX[2*i] + mnXOffset + ImplGetExtraOffset();
            aRect.Right() = pDX[2*i+1] + mnXOffset + ImplGetExtraOffset();
            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] & EXTTEXTINPUT_ATTR_HIGHLIGHT) )
                bHighlight = true;

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

        if( IsPaintTransparent() )
            SetTextFillColor();
        else
        {
            // Set background color when part of the text is selected
            if ( ImplUseNativeBorder( GetStyle() ) )
            {
                if( (GetStyle() & WB_FORCECTRLBACKGROUND) != 0 && IsControlBackground() )
                    SetTextFillColor( GetControlBackground() );
                else
                    SetTextFillColor();
            }
            else
                SetTextFillColor( IsControlBackground() ? GetControlBackground() : rStyleSettings.GetFieldColor() );
        }
		DrawText( aPos, aText, nStart, nEnd - nStart );

        // draw highlighted text
        SetClipRegion( aHiglightClipRegion );
        SetTextColor( rStyleSettings.GetHighlightTextColor() );
        SetTextFillColor( rStyleSettings.GetHighlightColor() );
		DrawText( aPos, aText, nStart, nEnd - nStart );

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

                for( i = 0; i < mpIMEInfos->nLen; )
                {
                    sal_uInt16 nAttr = mpIMEInfos->pAttribs[i];
                    Region aClip;
                    int nIndex = i;
                    while( nIndex < mpIMEInfos->nLen && mpIMEInfos->pAttribs[nIndex] == nAttr)  // #112631# check nIndex before using it
                    {
                        Rectangle aRect( aPos, Size( 10, nTH ) );
                        aRect.Left() = pDX[2*(nIndex+mpIMEInfos->nPos)] + mnXOffset + ImplGetExtraOffset();
                        aRect.Right() = pDX[2*(nIndex+mpIMEInfos->nPos)+1] + mnXOffset + ImplGetExtraOffset();
                        aRect.Justify();
                        aClip.Union( aRect );
                        nIndex++;
                    }
                    i = nIndex;
                    if( aClip.Intersect( aRegion ) && nAttr )
                    {
                        Font aFont = GetFont();
                        if ( nAttr & EXTTEXTINPUT_ATTR_UNDERLINE )
                            aFont.SetUnderline( UNDERLINE_SINGLE );
                        else if ( nAttr & EXTTEXTINPUT_ATTR_BOLDUNDERLINE )
                            aFont.SetUnderline( UNDERLINE_BOLD );
                        else if ( nAttr & EXTTEXTINPUT_ATTR_DOTTEDUNDERLINE )
                            aFont.SetUnderline( UNDERLINE_DOTTED );
                        else if ( nAttr & EXTTEXTINPUT_ATTR_DASHDOTUNDERLINE )
                            aFont.SetUnderline( UNDERLINE_DOTTED );
                        else if ( nAttr & EXTTEXTINPUT_ATTR_GRAYWAVELINE )
                        {
                            aFont.SetUnderline( UNDERLINE_WAVE );
                            SetTextLineColor( Color( COL_LIGHTGRAY ) );
                        }
                        SetFont( aFont );

                        if ( nAttr & EXTTEXTINPUT_ATTR_REDTEXT )
                            SetTextColor( Color( COL_RED ) );
                        else if ( nAttr & EXTTEXTINPUT_ATTR_HALFTONETEXT )
                            SetTextColor( Color( COL_LIGHTGRAY ) );

                        SetClipRegion( aClip );
                        DrawText( aPos, aText, nStart, nEnd - nStart );
                    }
                }
            }
        }

        // restore graphics state
        Pop();
	}

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

    if( pDXBuffer )
        delete [] pDXBuffer;
}

// -----------------------------------------------------------------------

void Edit::ImplDelete( const Selection& rSelection, sal_uInt8 nDirection, sal_uInt8 nMode )
{
	XubString aText = ImplGetText();

	// loeschen moeglich?
	if ( !rSelection.Len() &&
		 (((rSelection.Min() == 0) && (nDirection == EDIT_DEL_LEFT)) ||
		  ((rSelection.Max() == aText.Len()) && (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, aSelection.Min(), GetSettings().GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True );
				if ( aBoundary.startPos == aSelection.Min() )
					aBoundary = xBI->previousWord( maText, aSelection.Min(), GetSettings().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, aSelection.Min(), GetSettings().GetLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
			}
		}
		else
		{
			if ( nMode == EDIT_DELMODE_RESTOFWORD )
			{
				i18n::Boundary aBoundary = xBI->nextWord( maText, aSelection.Max(), GetSettings().GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
				aSelection.Max() = aBoundary.startPos;
			}
			else if ( nMode == EDIT_DELMODE_RESTOFCONTENT )
			{
				aSelection.Max() = aText.Len();
			}
			else
			{
				sal_Int32 nCount = 1;
				aSelection.Max() = xBI->nextCharacters( maText, aSelection.Max(), GetSettings().GetLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );;
			}
		}
	}

	maText.Erase( (xub_StrLen)aSelection.Min(), (xub_StrLen)aSelection.Len() );
	maSelection.Min() = aSelection.Min();
	maSelection.Max() = aSelection.Min();
	ImplAlignAndPaint();
	mbInternModified = sal_True;
}

// -----------------------------------------------------------------------

String Edit::ImplGetValidString( const String& rString ) const
{
    String aValidString( rString );
	aValidString.EraseAllChars( _LF );
	aValidString.EraseAllChars( _CR );
	aValidString.SearchAndReplaceAll( '\t', ' ' );
    return aValidString;
}

// -----------------------------------------------------------------------
uno::Reference < i18n::XBreakIterator > Edit::ImplGetBreakIterator() const
{
    //!! 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 < i18n::XBreakIterator > xBI;
//    if ( !xBI.is() )
    {
        uno::Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
        uno::Reference < XInterface > xI = xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.BreakIterator" ) );
        if ( xI.is() )
        {
            Any x = xI->queryInterface( ::getCppuType((const uno::Reference< i18n::XBreakIterator >*)0) );
            x >>= xBI;
        }
    }
    return xBI;
}
// -----------------------------------------------------------------------

uno::Reference < i18n::XExtendedInputSequenceChecker > Edit::ImplGetInputSequenceChecker() const
{
    //!! 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 < i18n::XExtendedInputSequenceChecker > xISC;
//    if ( !xISC.is() )
    {
        uno::Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
        uno::Reference < XInterface > xI = xMSF->createInstance( OUString::createFromAscii( "com.sun.star.i18n.InputSequenceChecker" ) );
        if ( xI.is() )
        {
            Any x = xI->queryInterface( ::getCppuType((const uno::Reference< i18n::XExtendedInputSequenceChecker >*)0) );
            x >>= xISC;
        }
    }
    return xISC;
}

// -----------------------------------------------------------------------

void Edit::ShowTruncationWarning( Window* pParent )
{
    ResMgr* pResMgr = ImplGetResMgr();
    if( pResMgr )
    {
        WarningBox aBox( pParent, ResId( SV_EDIT_WARNING_BOX, *pResMgr ) );
        aBox.Execute();
    }
}

// -----------------------------------------------------------------------

bool Edit::ImplTruncateToMaxLen( rtl::OUString& rStr, sal_uInt32 nSelectionLen ) const
{
    bool bWasTruncated = false;
    const sal_uInt32 nMaxLen = mnMaxTextLen < 65534 ? mnMaxTextLen : 65534;
    sal_uInt32 nLenAfter = static_cast<sal_uInt32>(maText.Len()) + rStr.getLength() - nSelectionLen;
    if ( nLenAfter > nMaxLen )
	{
        sal_uInt32 nErasePos = nMaxLen - static_cast<sal_uInt32>(maText.Len()) + nSelectionLen;
        rStr = rStr.copy( 0, nErasePos );
        bWasTruncated = true;
    }
    return bWasTruncated;
}

// -----------------------------------------------------------------------

void Edit::ImplInsertText( const XubString& rStr, const Selection* pNewSel, sal_Bool bIsUserInput )
{
	Selection aSelection( maSelection );
	aSelection.Justify();

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

    ImplClearLayoutData();

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

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

        // determine if input-sequence-checking should be applied or not
        //
        static OUString sModule( OUString::createFromAscii( "/org.openoffice.Office.Common/I18N" ) );
        static OUString sRelNode( OUString::createFromAscii( "CTL" ) );
        static OUString sCTLSequenceChecking( OUString::createFromAscii( "CTLSequenceChecking" ) );
        static OUString sCTLSequenceCheckingRestricted( OUString::createFromAscii( "CTLSequenceCheckingRestricted" ) );
        static OUString sCTLSequenceCheckingTypeAndReplace( OUString::createFromAscii( "CTLSequenceCheckingTypeAndReplace" ) );
        static OUString sCTLFont( OUString::createFromAscii( "CTLFont" ) );
        //
        sal_Bool bCTLSequenceChecking               = sal_False;
        sal_Bool bCTLSequenceCheckingRestricted     = sal_False;
        sal_Bool bCTLSequenceCheckingTypeAndReplace = sal_False;
		sal_Bool bCTLFontEnabled					= sal_False;
        sal_Bool bIsInputSequenceChecking			= sal_False;
        //
        // get access to the configuration of this office module
		try
		{
			uno::Reference< lang::XMultiServiceFactory > xMSF = ::comphelper::getProcessServiceFactory();
			uno::Reference< container::XNameAccess > xModuleCfg( ::comphelper::ConfigurationHelper::openConfig(
									xMSF,
									sModule,
									::comphelper::ConfigurationHelper::E_READONLY ),
								uno::UNO_QUERY );

			//!! get values from configuration.
			//!! we can't use SvtCTLOptions here since vcl must not be linked
			//!! against svtools. (It is already the other way around.)
			Any aCTLSequenceChecking                = ::comphelper::ConfigurationHelper::readRelativeKey( xModuleCfg, sRelNode, sCTLSequenceChecking );
			Any aCTLSequenceCheckingRestricted      = ::comphelper::ConfigurationHelper::readRelativeKey( xModuleCfg, sRelNode, sCTLSequenceCheckingRestricted );
			Any aCTLSequenceCheckingTypeAndReplace  = ::comphelper::ConfigurationHelper::readRelativeKey( xModuleCfg, sRelNode, sCTLSequenceCheckingTypeAndReplace );
            Any aCTLFontEnabled                     = ::comphelper::ConfigurationHelper::readRelativeKey( xModuleCfg, sRelNode, sCTLFont );
			aCTLSequenceChecking                >>= bCTLSequenceChecking;
			aCTLSequenceCheckingRestricted      >>= bCTLSequenceCheckingRestricted;
			aCTLSequenceCheckingTypeAndReplace  >>= bCTLSequenceCheckingTypeAndReplace;
            aCTLFontEnabled                     >>= bCTLFontEnabled;
		}
		catch(...)
		{
			bIsInputSequenceChecking = sal_False;	// continue with inserting the new text
		}
        //
        uno::Reference < i18n::XBreakIterator > xBI( ImplGetBreakIterator(), UNO_QUERY );
        bIsInputSequenceChecking = rStr.Len() == 1 &&
                bCTLFontEnabled &&
                bCTLSequenceChecking &&
                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.GetChar(0);
            xub_StrLen nTmpPos = static_cast< xub_StrLen >( aSelection.Min() );
            sal_Int16 nCheckMode = bCTLSequenceCheckingRestricted ?
                    i18n::InputSequenceCheckMode::STRICT : i18n::InputSequenceCheckMode::BASIC;

            // the text that needs to be checked is only the one
            // before the current cursor position
            rtl::OUString aOldText( maText.Copy(0, nTmpPos) );
            rtl::OUString aTmpText( aOldText );
            if (bCTLSequenceCheckingTypeAndReplace)
            {
                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;

                xub_StrLen nChgLen = static_cast< xub_StrLen >( nTmpLen - nChgPos );
                String aChgText( aTmpText.copy( nChgPos ), nChgLen );

                // remove text from first pos to be changed to current pos
                maText.Erase( static_cast< xub_StrLen >( nChgPos ), static_cast< xub_StrLen >( nTmpPos - nChgPos ) );

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

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

	if ( aNewText.getLength() )
		maText.Insert( String( aNewText ), (xub_StrLen)aSelection.Min() );

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

	ImplAlignAndPaint();
	mbInternModified = sal_True;
}

// -----------------------------------------------------------------------

void Edit::ImplSetText( const XubString& rText, const Selection* pNewSelection )
{
	// Der Text wird dadurch geloescht das der alte Text komplett 'selektiert'
	// wird, dann InsertText, damit flackerfrei.
	if ( ( rText.Len() <= mnMaxTextLen ) && ( (rText != maText) || (pNewSelection && (*pNewSelection != maSelection)) ) )
	{
        ImplClearLayoutData();
		maSelection.Min() = 0;
		maSelection.Max() = maText.Len();
		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, sal_False );

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

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

        ImplCallEventListeners( VCLEVENT_EDIT_MODIFY );
	}
}

// -----------------------------------------------------------------------

int Edit::ImplGetNativeControlType()
{
    int nCtrl = 0;
    Window *pControl = mbIsSubEdit ? GetParent() : this;

    switch( pControl->GetType() )
    {
        case WINDOW_COMBOBOX:
        case WINDOW_PATTERNBOX:
        case WINDOW_NUMERICBOX:
        case WINDOW_METRICBOX:
        case WINDOW_CURRENCYBOX:
        case WINDOW_DATEBOX:
        case WINDOW_TIMEBOX:
        case WINDOW_LONGCURRENCYBOX:
            nCtrl = CTRL_COMBOBOX;
            break;

        case WINDOW_MULTILINEEDIT:
            if ( GetWindow( WINDOW_BORDER ) != this )
                nCtrl = CTRL_MULTILINE_EDITBOX;
            else
                nCtrl = CTRL_EDITBOX_NOBORDER;
            break;

        case WINDOW_EDIT:
        case WINDOW_PATTERNFIELD:
        case WINDOW_METRICFIELD:
        case WINDOW_CURRENCYFIELD:
        case WINDOW_DATEFIELD:
        case WINDOW_TIMEFIELD:
        case WINDOW_LONGCURRENCYFIELD:
        case WINDOW_NUMERICFIELD:
        case WINDOW_SPINFIELD:
            if( pControl->GetStyle() & WB_SPIN )
                nCtrl = CTRL_SPINBOX;
            else
            {
                if ( GetWindow( WINDOW_BORDER ) != this )
                    nCtrl = CTRL_EDITBOX;
                else
                    nCtrl = CTRL_EDITBOX_NOBORDER;
            }
            break;

        default:
            nCtrl = CTRL_EDITBOX;
    }
    return nCtrl;
}

void Edit::ImplClearBackground( long nXStart, long nXEnd )
{
    /*
    * note: at this point the cursor must be switched off already
    */
	Point aTmpPoint;
	Rectangle aRect( aTmpPoint, GetOutputSizePixel() );
	aRect.Left() = nXStart;
	aRect.Right() = nXEnd;

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

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

                    // mirror
                    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() );
                }

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

                pBorder->Paint( Rectangle() );

                pBorder->SetClipRegion( oldRgn );
            }
            else
                pBorder->Paint( Rectangle() );

        }
    }
    else
	    Erase( aRect );
}

// -----------------------------------------------------------------------

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

	Cursor* 	pCursor = GetCursor();
	XubString	aText = ImplGetText();

    long nTextPos = 0;

    sal_Int32	nDXBuffer[256];
    sal_Int32*	pDXBuffer = NULL;
    sal_Int32*	pDX = nDXBuffer;

    if( aText.Len() )
    {
        if( 2*aText.Len() > xub_StrLen(sizeof(nDXBuffer)/sizeof(nDXBuffer[0])) )
        {
            pDXBuffer = new sal_Int32[2*(aText.Len()+1)];
            pDX = pDXBuffer;
        }

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

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

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

	// Cursor muss im sichtbaren Bereich landen:
	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()-ImplGetExtraOffset()) - nTextPos;
			// Etwas mehr?
			if ( (aOutSize.Width()-ImplGetExtraOffset()) < nTextPos )
			{
				long nMaxNegX = (aOutSize.Width()-ImplGetExtraOffset()) - GetTextWidth( aText );
				mnXOffset -= aOutSize.Width() / 5;
				if ( mnXOffset < nMaxNegX )  // beides negativ...
					mnXOffset = nMaxNegX;
			}
		}

		nCursorPosX = nTextPos + mnXOffset + ImplGetExtraOffset();
		if ( nCursorPosX == aOutSize.Width() )	// dann nicht sichtbar...
			nCursorPosX--;

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

	const long nTextHeight = GetTextHeight();
	const long nCursorPosY = ImplGetTextYPosition();
	pCursor->SetPos( Point( nCursorPosX, nCursorPosY ) );
	pCursor->SetSize( Size( nCursorWidth, nTextHeight ) );
	pCursor->Show();

    if( pDXBuffer )
        delete [] pDXBuffer;
}

// -----------------------------------------------------------------------

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 - ImplGetExtraOffset();
        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 )
	{
		// Mit Abfrage schoener, wenn gescrollt, dann aber nicht zentriert im gescrollten Zustand...
//		if ( nTextWidth < nOutWidth )
			mnXOffset = (nOutWidth - nTextWidth) / 2;
	}
}


// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

xub_StrLen Edit::ImplGetCharPos( const Point& rWindowPos ) const
{
    xub_StrLen nIndex = STRING_LEN;
    String aText = ImplGetText();

    sal_Int32	nDXBuffer[256];
    sal_Int32*	pDXBuffer = NULL;
    sal_Int32*	pDX = nDXBuffer;
    if( 2*aText.Len() > xub_StrLen(sizeof(nDXBuffer)/sizeof(nDXBuffer[0])) )
    {
        pDXBuffer = new sal_Int32[2*(aText.Len()+1)];
        pDX = pDXBuffer;
    }

    GetCaretPositions( aText, pDX, 0, aText.Len() );
    long nX = rWindowPos.X() - mnXOffset - ImplGetExtraOffset();
    for( int i = 0; i < aText.Len(); i++ )
    {
        if( (pDX[2*i] >= nX && pDX[2*i+1] <= nX) ||
            (pDX[2*i+1] >= nX && pDX[2*i] <= nX))
        {
            nIndex = sal::static_int_cast<xub_StrLen>(i);
            if( pDX[2*i] < pDX[2*i+1] )
            {
                if( nX > (pDX[2*i]+pDX[2*i+1])/2 )
                    nIndex++;
            }
            else
            {
                if( nX < (pDX[2*i]+pDX[2*i+1])/2 )
                    nIndex++;
            }
            break;
        }
    }
    if( nIndex == STRING_LEN )
    {
        nIndex = 0;
        long nDiff = Abs( pDX[0]-nX );
        for( int i = 1; i < aText.Len(); i++ )
        {
            long nNewDiff = Abs( pDX[2*i]-nX );

            if( nNewDiff < nDiff )
            {
                nIndex = sal::static_int_cast<xub_StrLen>(i);
                nDiff = nNewDiff;
            }
        }
        if( nIndex == aText.Len()-1 && Abs( pDX[2*nIndex+1] - nX ) < nDiff )
            nIndex = STRING_LEN;
    }

    if( pDXBuffer )
        delete [] pDXBuffer;

    return nIndex;
}

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

void Edit::ImplLoadRes( const ResId& rResId )
{
	Control::ImplLoadRes( rResId );

	xub_StrLen nTextLength = ReadShortRes();
	if ( nTextLength )
		SetMaxTextLen( nTextLength );
}

// -----------------------------------------------------------------------

void Edit::ImplCopyToSelectionClipboard()
{
    if ( GetSelection().Len() )
    {
        ::com::sun::star::uno::Reference<com::sun::star::datatransfer::clipboard::XClipboard> aSelection(GetPrimarySelection());
        ImplCopy( aSelection );
    }
}

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

// -----------------------------------------------------------------------

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

		const sal_uInt32 nRef = Application::ReleaseSolarMutex();

        try
		{
            xDataObj = rxClipboard->getContents();
		}
		catch( const ::com::sun::star::uno::Exception& )
		{
		}

        Application::AcquireSolarMutex( nRef );

        if ( xDataObj.is() )
		{
			datatransfer::DataFlavor aFlavor;
			SotExchange::GetFormatDataFlavor( SOT_FORMAT_STRING, aFlavor );
            try
            {
				uno::Any aData = xDataObj->getTransferData( aFlavor );
				::rtl::OUString aText;
				aData >>= aText;
                if( ImplTruncateToMaxLen( aText, maSelection.Len() ) )
                    ShowTruncationWarning( const_cast<Edit*>(this) );
				ReplaceSelected( aText );
			}
            catch( const ::com::sun::star::uno::Exception& )
            {
            }
		}
	}
}

// -----------------------------------------------------------------------

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

	xub_StrLen nChar = ImplGetCharPos( rMEvt.GetPosPixel() );
	Selection aSelection( maSelection );
	aSelection.Justify();

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

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

		if ( !mbClickedInSelection && rMEvt.IsLeft() && ( rMEvt.GetClicks() == 1 ) )
			StartTracking( STARTTRACK_SCROLLREPEAT );
	}

	mbInMBDown = sal_True;	// Dann im GetFocus nicht alles selektieren
	GrabFocus();
	mbInMBDown = sal_False;
}

// -----------------------------------------------------------------------

void Edit::MouseButtonUp( const MouseEvent& rMEvt )
{
	if ( mbClickedInSelection && rMEvt.IsLeft() )
	{
		xub_StrLen nChar = ImplGetCharPos( rMEvt.GetPosPixel() );
		ImplSetCursorPos( nChar, sal_False );
		mbClickedInSelection = sal_False;
	}
    else if ( rMEvt.IsMiddle() && !mbReadOnly &&
              ( GetSettings().GetMouseSettings().GetMiddleButtonAction() == MOUSE_MIDDLE_PASTESELECTION ) )
    {
        ::com::sun::star::uno::Reference<com::sun::star::datatransfer::clipboard::XClipboard> aSelection(Window::GetPrimarySelection());
        ImplPaste( aSelection );
        ImplModified();
    }
}

// -----------------------------------------------------------------------

void Edit::Tracking( const TrackingEvent& rTEvt )
{
	if ( rTEvt.IsTrackingEnded() )
	{
		if ( mbClickedInSelection )
		{
			xub_StrLen nChar = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() );
			ImplSetCursorPos( nChar, sal_False );
			mbClickedInSelection = sal_False;
		}
        else if ( rTEvt.GetMouseEvent().IsLeft() )
        {
            ImplCopyToSelectionClipboard();
        }
	}
	else
	{
		if( !mbClickedInSelection )
		{
			xub_StrLen nChar = ImplGetCharPos( rTEvt.GetMouseEvent().GetPosPixel() );
			ImplSetCursorPos( nChar, sal_True );
		}
	}

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

// -----------------------------------------------------------------------

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

	mbInternModified = sal_False;

	if ( eFunc != KEYFUNC_DONTKNOW )
	{
		switch ( eFunc )
		{
			case KEYFUNC_CUT:
			{
				if ( !mbReadOnly && maSelection.Len() && !(GetStyle() & WB_PASSWORD) )
				{
					Cut();
					ImplModified();
					bDone = sal_True;
				}
			}
			break;

			case KEYFUNC_COPY:
			{
				if ( !(GetStyle() & WB_PASSWORD) )
				{
					Copy();
					bDone = sal_True;
				}
			}
			break;

			case KEYFUNC_PASTE:
			{
				if ( !mbReadOnly )
				{
					Paste();
					bDone = sal_True;
				}
			}
			break;

			case KEYFUNC_UNDO:
			{
				if ( !mbReadOnly )
				{
					Undo();
					bDone = sal_True;
				}
			}
			break;

			default: // wird dann evtl. unten bearbeitet.
				eFunc = KEYFUNC_DONTKNOW;
		}
	}

	if ( !bDone && rKEvt.GetKeyCode().IsMod1() && !rKEvt.GetKeyCode().IsMod2() )
	{
		if ( nCode == KEY_A )
		{
			ImplSetSelection( Selection( 0, maText.Len() ) );
			bDone = sal_True;
		}
		else if ( rKEvt.GetKeyCode().IsShift() && (nCode == KEY_S) )
		{
			if ( pImplFncGetSpecialChars )
			{
				Selection aSaveSel = GetSelection();	// Falls jemand in Get/LoseFocus die Selektion verbiegt, z.B. URL-Zeile...
				XubString aChars = pImplFncGetSpecialChars( this, GetFont() );
				SetSelection( aSaveSel );
				if ( aChars.Len() )
				{
					ImplInsertText( aChars );
					ImplModified();
				}
				bDone = sal_True;
			}
		}
	}

	if ( eFunc == KEYFUNC_DONTKNOW && ! bDone )
	{
		switch ( nCode )
		{
            case com::sun::star::awt::Key::SELECT_ALL:
            {
                ImplSetSelection( Selection( 0, maText.Len() ) );
                bDone = sal_True;
            }
            break;
            
			case KEY_LEFT:
			case KEY_RIGHT:
			case KEY_HOME:
			case KEY_END:
            case com::sun::star::awt::Key::MOVE_WORD_FORWARD:
            case com::sun::star::awt::Key::SELECT_WORD_FORWARD:
            case com::sun::star::awt::Key::MOVE_WORD_BACKWARD:
            case com::sun::star::awt::Key::SELECT_WORD_BACKWARD:
            case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE:
            case com::sun::star::awt::Key::MOVE_TO_END_OF_LINE:
            case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE:
            case com::sun::star::awt::Key::SELECT_TO_END_OF_LINE:
            case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
            case com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
            case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
            case com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
            case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
            case com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT:
            case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
            case com::sun::star::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 com::sun::star::awt::Key::MOVE_WORD_FORWARD:
                        bGoRight = bWord = true;break;
                    case com::sun::star::awt::Key::SELECT_WORD_FORWARD:
                        bGoRight = bSelect = bWord = true;break;
                    case com::sun::star::awt::Key::MOVE_WORD_BACKWARD:
                        bGoLeft = bWord = true;break;
                    case com::sun::star::awt::Key::SELECT_WORD_BACKWARD:
                        bGoLeft = bSelect = bWord = true;break;
                    case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_LINE:
                    case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_PARAGRAPH:
                    case com::sun::star::awt::Key::SELECT_TO_BEGIN_OF_DOCUMENT:
                        bSelect = true;
                        // fallthrough intended
                    case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_LINE:
                    case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_PARAGRAPH:
                    case com::sun::star::awt::Key::MOVE_TO_BEGIN_OF_DOCUMENT:
                        bGoHome = true;break;
                    case com::sun::star::awt::Key::SELECT_TO_END_OF_LINE:
                    case com::sun::star::awt::Key::SELECT_TO_END_OF_PARAGRAPH:
                    case com::sun::star::awt::Key::SELECT_TO_END_OF_DOCUMENT:
                        bSelect = true;
                        // fallthrough intended
                    case com::sun::star::awt::Key::MOVE_TO_END_OF_LINE:
                    case com::sun::star::awt::Key::MOVE_TO_END_OF_PARAGRAPH:
                    case com::sun::star::awt::Key::MOVE_TO_END_OF_DOCUMENT:
                        bGoEnd = true;break;
                    default:
                        break;
                    };
                    
					// Range wird in ImplSetSelection geprueft...
					if ( bGoLeft && aSel.Max() )
					{
						if ( bWord )
						{
							i18n::Boundary aBoundary = xBI->getWordBoundary( maText, aSel.Max(), GetSettings().GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES, sal_True );
							if ( aBoundary.startPos == aSel.Max() )
								aBoundary = xBI->previousWord( maText, aSel.Max(), GetSettings().GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
							aSel.Max() = aBoundary.startPos;
						}
						else
						{
							sal_Int32 nCount = 1;
							aSel.Max() = xBI->previousCharacters( maText, aSel.Max(), GetSettings().GetLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
						}
					}
					else if ( bGoRight && ( aSel.Max() < maText.Len() ) )
					{
						if ( bWord )
					   	{
							i18n::Boundary aBoundary = xBI->nextWord( maText, aSel.Max(), GetSettings().GetLocale(), i18n::WordType::ANYWORD_IGNOREWHITESPACES );
							aSel.Max() = aBoundary.startPos;
						}
						else
						{
							sal_Int32 nCount = 1;
							aSel.Max() = xBI->nextCharacters( maText, aSel.Max(), GetSettings().GetLocale(), i18n::CharacterIteratorMode::SKIPCHARACTER, nCount, nCount );
						}
					}
					else if ( bGoHome )
                    {
						aSel.Max() = 0;
                    }
					else if ( bGoEnd )
                    {
						aSel.Max() = 0xFFFF;
                    }

					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.Len()) )
						{
							meAutocompleteAction = AUTOCOMPLETE_KEYINPUT;
							maAutocompleteHdl.Call( this );
						}
					}

					bDone = sal_True;
				}
			}
			break;

            case com::sun::star::awt::Key::DELETE_WORD_BACKWARD:
            case com::sun::star::awt::Key::DELETE_WORD_FORWARD:
            case com::sun::star::awt::Key::DELETE_TO_BEGIN_OF_LINE:
            case com::sun::star::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 com::sun::star::awt::Key::DELETE_WORD_BACKWARD:
                        nDel = EDIT_DEL_LEFT;
                        nMode = EDIT_DELMODE_RESTOFWORD;
                        break;
                    case com::sun::star::awt::Key::DELETE_WORD_FORWARD:
                        nDel = EDIT_DEL_RIGHT;
                        nMode = EDIT_DELMODE_RESTOFWORD;
                        break;
                    case com::sun::star::awt::Key::DELETE_TO_BEGIN_OF_LINE:
                        nDel = EDIT_DEL_LEFT;
                        nMode = EDIT_DELMODE_RESTOFCONTENT;
                        break;
                    case com::sun::star::awt::Key::DELETE_TO_END_OF_LINE:
                        nDel = EDIT_DEL_RIGHT;
                        nMode = EDIT_DELMODE_RESTOFCONTENT;
                        break;
                    default: break;
                    }
					xub_StrLen nOldLen = maText.Len();
					ImplDelete( maSelection, nDel, nMode );
					if ( maText.Len() != nOldLen )
						ImplModified();
					bDone = sal_True;
				}
			}
			break;

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

			/* #i101255# disable autocomplete tab forward/backward
			   users expect tab/shif-tab to move the focus to other controls
			   not suddenly to cycle the autocompletion
			case KEY_TAB:
			{
				if ( !mbReadOnly && maAutocompleteHdl.IsSet() &&
					 maSelection.Min() && (maSelection.Min() == maText.Len()) &&
					 !rKEvt.GetKeyCode().IsMod1() && !rKEvt.GetKeyCode().IsMod2() )
				{
					// Kein Autocomplete wenn alles Selektiert oder Edit leer, weil dann
					// keine vernuenftige Tab-Steuerung!
					if ( rKEvt.GetKeyCode().IsShift() )
						meAutocompleteAction = AUTOCOMPLETE_TABBACKWARD;
					else
						meAutocompleteAction = AUTOCOMPLETE_TABFORWARD;

					maAutocompleteHdl.Call( this );

					// Wurde nichts veraendert, dann TAB fuer DialogControl
					if ( GetSelection().Len() )
						bDone = sal_True;
				}
			}
			break;
			*/

			default:
			{
				if ( IsCharInput( rKEvt ) )
				{
					bDone = sal_True;	// Auch bei ReadOnly die Zeichen schlucken.
					if ( !mbReadOnly )
					{
                        ImplInsertText( rKEvt.GetCharCode(), 0, sal_True );
						if ( maAutocompleteHdl.IsSet() )
						{
							if ( (maSelection.Min() == maSelection.Max()) && (maSelection.Min() == maText.Len()) )
							{
								meAutocompleteAction = AUTOCOMPLETE_KEYINPUT;
								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 = new vcl::ControlLayoutData();
    const_cast<Edit*>(this)->ImplRepaint( 0, STRING_LEN, true );
}

// -----------------------------------------------------------------------

void Edit::Paint( const Rectangle& )
{
	if ( !mpSubEdit )
		ImplRepaint();
}

// -----------------------------------------------------------------------

void Edit::Resize()
{
	if ( !mpSubEdit && IsReallyVisible() )
	{
        Control::Resize();
		// Wegen vertikaler Zentrierung...
		mnXOffset = 0;
		ImplAlign();
		Invalidate();
		ImplShowCursor();
	}
}

// -----------------------------------------------------------------------

void Edit::Draw( OutputDevice* pDev, const Point& rPos, const Size& rSize, sal_uLong nFlags )
{
    ImplInitSettings( sal_True, sal_True, sal_True );

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

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

	// Border/Background
	pDev->SetLineColor();
	pDev->SetFillColor();
	sal_Bool bBorder = !(nFlags & WINDOW_DRAW_NOBORDER ) && (GetStyle() & WB_BORDER);
	sal_Bool bBackground = !(nFlags & WINDOW_DRAW_NOBACKGROUND) && IsControlBackground();
	if ( bBorder || bBackground )
	{
		Rectangle aRect( aPos, aSize );
		if ( bBorder )
		{
            ImplDrawFrame( pDev, aRect );
		}
		if ( bBackground )
		{
			pDev->SetFillColor( GetControlBackground() );
			pDev->DrawRect( aRect );
		}
	}

	// Inhalt
	if ( ( nFlags & WINDOW_DRAW_MONO ) || ( eOutDevType == OUTDEV_PRINTER ) )
		pDev->SetTextColor( Color( COL_BLACK ) );
	else
	{
		if ( !(nFlags & WINDOW_DRAW_NODISABLE ) && !IsEnabled() )
		{
			const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
			pDev->SetTextColor( rStyleSettings.GetDisableColor() );
		}
		else
		{
			pDev->SetTextColor( GetTextColor() );
		}
	}

	XubString	aText = ImplGetText();
	long		nTextHeight = pDev->GetTextHeight();
	long		nTextWidth = pDev->GetTextWidth( aText );
	long		nOnePixel = GetDrawPixel( pDev, 1 );
	long		nOffX = 3*nOnePixel;
	long		nOffY = (aSize.Height() - nTextHeight) / 2;

	// Clipping?
	if ( (nOffY < 0) ||
		 ((nOffY+nTextHeight) > aSize.Height()) ||
		 ((nOffX+nTextWidth) > aSize.Width()) )
	{
		Rectangle aClip( aPos, aSize );
		if ( nTextHeight > aSize.Height() )
			aClip.Bottom() += nTextHeight-aSize.Height()+1;  // Damit HP-Drucker nicht 'weg-optimieren'
		pDev->IntersectClipRegion( aClip );
	}

	if ( GetStyle() & WB_CENTER )
	{
		aPos.X() += (aSize.Width() - nTextWidth) / 2;
		nOffX = 0;
	}
	else if ( GetStyle() & WB_RIGHT )
	{
		aPos.X() += aSize.Width() - nTextWidth;
		nOffX = -nOffX;
	}

	pDev->DrawText( Point( aPos.X() + nOffX, aPos.Y() + nOffY ), aText );
	pDev->Pop();

    if ( GetSubEdit() )
    {
	    GetSubEdit()->Draw( pDev, rPos, rSize, nFlags );
    }
}

// -----------------------------------------------------------------------

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

    pInvalWin->Invalidate( INVALIDATE_CHILDREN | INVALIDATE_UPDATE );
}

void Edit::GetFocus()
{
	if ( mpSubEdit )
		mpSubEdit->ImplGrabFocus( GetGetFocusFlags() );
	else if ( !mbActivePopup )
	{
		maUndoText = maText;

		sal_uLong nSelOptions = GetSettings().GetStyleSettings().GetSelectionOptions();
		if ( !( GetStyle() & (WB_NOHIDESELECTION|WB_READONLY) )
				&& ( GetGetFocusFlags() & (GETFOCUS_INIT|GETFOCUS_TAB|GETFOCUS_CURSOR|GETFOCUS_MNEMONIC) ) )
		{
			if ( nSelOptions & SELECTION_OPTION_SHOWFIRST )
			{
				maSelection.Min() = maText.Len();
				maSelection.Max() = 0;
			}
			else
			{
				maSelection.Min() = 0;
				maSelection.Max() = maText.Len();
			}
			if ( mbIsSubEdit )
				((Edit*)GetParent())->ImplCallEventListeners( VCLEVENT_EDIT_SELECTIONCHANGED );
			else
				ImplCallEventListeners( VCLEVENT_EDIT_SELECTIONCHANGED );
		}

		ImplShowCursor();

        // FIXME: this is currently only on aqua
        // check for other platforms that need similar handling
        if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
            IsNativeWidgetEnabled() &&
            IsNativeControlSupported( CTRL_EDITBOX, PART_ENTIRE_CONTROL ) )
        {
            ImplInvalidateOutermostBorder( mbIsSubEdit ? GetParent() : this );
        }
		else if ( maSelection.Len() )
		{
			// Selektion malen
			if ( !HasPaintEvent() )
				ImplInvalidateOrRepaint();
			else
				Invalidate();
		}

		SetInputContext( InputContext( GetFont(), !IsReadOnly() ? INPUTCONTEXT_TEXT|INPUTCONTEXT_EXTTEXTINPUT : 0 ) );
	}

	Control::GetFocus();
}

// -----------------------------------------------------------------------

Window* Edit::GetPreferredKeyInputWindow()
{
	if ( mpSubEdit )
        return mpSubEdit->GetPreferredKeyInputWindow();
    else
        return this;
}

// -----------------------------------------------------------------------

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

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

		if ( !mbActivePopup && !( GetStyle() & WB_NOHIDESELECTION ) && maSelection.Len() )
			ImplInvalidateOrRepaint();	  // Selektion malen
	}

	Control::LoseFocus();
}

// -----------------------------------------------------------------------

void Edit::Command( const CommandEvent& rCEvt )
{
	if ( rCEvt.GetCommand() == COMMAND_CONTEXTMENU )
	{
		PopupMenu* pPopup = Edit::CreatePopupMenu();
		const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
		if ( rStyleSettings.GetOptions() & STYLE_OPTION_HIDEDISABLED )
			pPopup->SetMenuFlags( MENU_FLAG_HIDEDISABLEDENTRIES );

		if ( !maSelection.Len() )
		{
			pPopup->EnableItem( SV_MENU_EDIT_CUT, sal_False );
			pPopup->EnableItem( SV_MENU_EDIT_COPY, sal_False );
			pPopup->EnableItem( SV_MENU_EDIT_DELETE, sal_False );
		}

		if ( IsReadOnly() )
		{
			pPopup->EnableItem( SV_MENU_EDIT_CUT, sal_False );
			pPopup->EnableItem( SV_MENU_EDIT_PASTE, sal_False );
			pPopup->EnableItem( SV_MENU_EDIT_DELETE, sal_False );
			pPopup->EnableItem( SV_MENU_EDIT_INSERTSYMBOL, sal_False );
		}
		else
		{
			// Paste nur, wenn Text im Clipboard
			sal_Bool bData = sal_False;
			uno::Reference< datatransfer::clipboard::XClipboard > xClipboard = GetClipboard();
			if ( xClipboard.is() )
			{
				const sal_uInt32 nRef = Application::ReleaseSolarMutex();
				uno::Reference< datatransfer::XTransferable > xDataObj = xClipboard->getContents();
				Application::AcquireSolarMutex( nRef );
				if ( xDataObj.is() )
				{
					datatransfer::DataFlavor aFlavor;
					SotExchange::GetFormatDataFlavor( SOT_FORMAT_STRING, aFlavor );
					bData = xDataObj->isDataFlavorSupported( aFlavor );
				}
			}
			pPopup->EnableItem( SV_MENU_EDIT_PASTE, bData );
		}

		if ( maUndoText == maText )
			pPopup->EnableItem( SV_MENU_EDIT_UNDO, sal_False );
		if ( ( maSelection.Min() == 0 ) && ( maSelection.Max() == maText.Len() ) )
			pPopup->EnableItem( SV_MENU_EDIT_SELECTALL, sal_False );
		if ( !pImplFncGetSpecialChars )
		{
			sal_uInt16 nPos = pPopup->GetItemPos( SV_MENU_EDIT_INSERTSYMBOL );
			pPopup->RemoveItem( nPos );
			pPopup->RemoveItem( nPos-1 );
		}

		mbActivePopup = sal_True;
		Selection aSaveSel = GetSelection();	// Falls jemand in Get/LoseFocus die Selektion verbiegt, z.B. URL-Zeile...
		Point aPos = rCEvt.GetMousePosPixel();
		if ( !rCEvt.IsMouseEvent() )
		{
			// !!! Irgendwann einmal Menu zentriert in der Selektion anzeigen !!!
			Size aSize = GetOutputSizePixel();
			aPos = Point( aSize.Width()/2, aSize.Height()/2 );
		}
		sal_uInt16 n = pPopup->Execute( this, aPos );
		Edit::DeletePopupMenu( pPopup );
		SetSelection( aSaveSel );
		switch ( n )
		{
			case SV_MENU_EDIT_UNDO:
				Undo();
				ImplModified();
				break;
			case SV_MENU_EDIT_CUT:
				Cut();
				ImplModified();
				break;
			case SV_MENU_EDIT_COPY:
				Copy();
				break;
			case SV_MENU_EDIT_PASTE:
				Paste();
				ImplModified();
				break;
			case SV_MENU_EDIT_DELETE:
				DeleteSelected();
				ImplModified();
				break;
			case SV_MENU_EDIT_SELECTALL:
				ImplSetSelection( Selection( 0, maText.Len() ) );
				break;
			case SV_MENU_EDIT_INSERTSYMBOL:
				{
					XubString aChars = pImplFncGetSpecialChars( this, GetFont() );
					SetSelection( aSaveSel );
					if ( aChars.Len() )
					{
						ImplInsertText( aChars );
						ImplModified();
					}
				}
				break;
		}
		mbActivePopup = sal_False;
	}
	else if ( rCEvt.GetCommand() == COMMAND_VOICE )
	{
		const CommandVoiceData* pData = rCEvt.GetVoiceData();
		if ( pData->GetType() == VOICECOMMANDTYPE_DICTATION )
		{
			switch ( pData->GetCommand() )
			{
				case DICTATIONCOMMAND_UNKNOWN:
				{
					ReplaceSelected( pData->GetText() );
				}
				break;
				case DICTATIONCOMMAND_LEFT:
				{
					ImplHandleKeyEvent( KeyEvent( 0, KeyCode( KEY_LEFT, KEY_MOD1  ) ) );
				}
				break;
				case DICTATIONCOMMAND_RIGHT:
				{
					ImplHandleKeyEvent( KeyEvent( 0, KeyCode( KEY_RIGHT, KEY_MOD1  ) ) );
				}
				break;
				case DICTATIONCOMMAND_UNDO:
				{
					Undo();
				}
				break;
				case DICTATIONCOMMAND_DEL:
				{
					ImplHandleKeyEvent( KeyEvent( 0, KeyCode( KEY_LEFT, KEY_MOD1|KEY_SHIFT	) ) );
					DeleteSelected();
				}
				break;
			}
		}
	}
	else if ( rCEvt.GetCommand() == COMMAND_STARTEXTTEXTINPUT )
	{
		DeleteSelected();
		delete mpIMEInfos;
		xub_StrLen nPos = (xub_StrLen)maSelection.Max();
		mpIMEInfos = new Impl_IMEInfos( nPos, maText.Copy( nPos ) );
		mpIMEInfos->bWasCursorOverwrite = !IsInsertMode();
	}
	else if ( rCEvt.GetCommand() == COMMAND_ENDEXTTEXTINPUT )
	{
		sal_Bool bInsertMode = !mpIMEInfos->bWasCursorOverwrite;
		delete mpIMEInfos;
		mpIMEInfos = NULL;
		// Font wieder ohne Attribute einstellen, wird jetzt im Repaint nicht
		// mehr neu initialisiert
		ImplInitSettings( sal_True, sal_False, sal_False );

		SetInsertMode( bInsertMode );

		ImplModified();

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

		maText.Erase( mpIMEInfos->nPos, mpIMEInfos->nLen );
		maText.Insert( pData->GetText(), mpIMEInfos->nPos );
        if ( mpIMEInfos->bWasCursorOverwrite )
        {
            sal_uInt16 nOldIMETextLen = mpIMEInfos->nLen;
            sal_uInt16 nNewIMETextLen = pData->GetText().Len();
            if ( ( nOldIMETextLen > nNewIMETextLen ) &&
                 ( nNewIMETextLen < mpIMEInfos->aOldTextAfterStartPos.Len() ) )
            {
                // restore old characters
                sal_uInt16 nRestore = nOldIMETextLen - nNewIMETextLen;
                maText.Insert( mpIMEInfos->aOldTextAfterStartPos.Copy( nNewIMETextLen, nRestore ), mpIMEInfos->nPos + nNewIMETextLen );
            }
            else if ( ( nOldIMETextLen < nNewIMETextLen ) &&
                      ( nOldIMETextLen < mpIMEInfos->aOldTextAfterStartPos.Len() ) )
            {
                // overwrite
                sal_uInt16 nOverwrite = nNewIMETextLen - nOldIMETextLen;
                if ( ( nOldIMETextLen + nOverwrite ) > mpIMEInfos->aOldTextAfterStartPos.Len() )
                    nOverwrite = mpIMEInfos->aOldTextAfterStartPos.Len() - nOldIMETextLen;
                maText.Erase( mpIMEInfos->nPos + nNewIMETextLen, nOverwrite );
            }
        }


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

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

		if ( pData->IsCursorVisible() )
			GetCursor()->Show();
		else
			GetCursor()->Hide();
	}
	else if ( rCEvt.GetCommand() == COMMAND_CURSORPOS )
	{
		if ( mpIMEInfos )
		{
			xub_StrLen nCursorPos = (sal_uInt16)GetSelection().Max();
			SetCursorRect( NULL, GetTextWidth(
				maText, nCursorPos, mpIMEInfos->nPos+mpIMEInfos->nLen-nCursorPos ) );
		}
		else
		{
			SetCursorRect();
		}
	}
	else if ( rCEvt.GetCommand() == COMMAND_SELECTIONCHANGE )
	{
	    const CommandSelectionChangeData *pData = rCEvt.GetSelectionChangeData();
	    Selection aSelection( pData->GetStart(), pData->GetEnd() );
	    SetSelection(aSelection);
	}
	else
		Control::Command( rCEvt );
}

// -----------------------------------------------------------------------

void Edit::StateChanged( StateChangedType nType )
{
	if ( nType == STATE_CHANGE_INITSHOW )
	{
		if ( !mpSubEdit )
		{
			mnXOffset = 0;	// Falls vorher GrabFocus, als Groesse noch falsch.
			ImplAlign();
			if ( !mpSubEdit )
				ImplShowCursor( sal_False );
		}
        // update background (eventual SetPaintTransparent)
        ImplInitSettings( sal_False, sal_False, sal_True );
	}
	else if ( nType == STATE_CHANGE_ENABLE )
	{
		if ( !mpSubEdit )
		{
			// Es aendert sich nur die Textfarbe...
			ImplInvalidateOrRepaint( 0, 0xFFFF );
		}
	}
	else if ( nType == STATE_CHANGE_STYLE || nType == STATE_CHANGE_MIRRORING )
	{
        WinBits nStyle = GetStyle();
        if( nType == STATE_CHANGE_STYLE )
        {
            nStyle = ImplInitStyle( GetStyle() );
            SetStyle( nStyle );
        }

		sal_uInt16 nOldAlign = mnAlign;
		mnAlign = EDIT_ALIGN_LEFT;

        // --- RTL --- 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 == STATE_CHANGE_MIRRORING )
                SetLayoutMode( TEXT_LAYOUT_BIDI_RTL | TEXT_LAYOUT_TEXTORIGIN_LEFT );
        }
        else if( mbIsSubEdit && !GetParent()->IsRTLEnabled() )
        {
            if ( nType == STATE_CHANGE_MIRRORING )
                SetLayoutMode( TEXT_LAYOUT_BIDI_LTR | TEXT_LAYOUT_TEXTORIGIN_LEFT );
        }

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

	}
	else if ( nType == STATE_CHANGE_ZOOM )
	{
		if ( !mpSubEdit )
		{
			ImplInitSettings( sal_True, sal_False, sal_False );
			ImplShowCursor( sal_True );
			Invalidate();
		}
	}
	else if ( nType == STATE_CHANGE_CONTROLFONT )
	{
		if ( !mpSubEdit )
		{
			ImplInitSettings( sal_True, sal_False, sal_False );
			ImplShowCursor();
			Invalidate();
		}
	}
	else if ( nType == STATE_CHANGE_CONTROLFOREGROUND )
	{
		if ( !mpSubEdit )
		{
			ImplInitSettings( sal_False, sal_True, sal_False );
			Invalidate();
		}
	}
	else if ( nType == STATE_CHANGE_CONTROLBACKGROUND )
	{
		if ( !mpSubEdit )
		{
			ImplInitSettings( sal_False, sal_False, sal_True );
			Invalidate();
		}
	}

	Control::StateChanged( nType );
}

// -----------------------------------------------------------------------

void Edit::DataChanged( const DataChangedEvent& rDCEvt )
{
	if ( (rDCEvt.GetType() == DATACHANGED_FONTS) ||
		 (rDCEvt.GetType() == DATACHANGED_FONTSUBSTITUTION) ||
		 ((rDCEvt.GetType() == DATACHANGED_SETTINGS) &&
		  (rDCEvt.GetFlags() & SETTINGS_STYLE)) )
	{
		if ( !mpSubEdit )
		{
			ImplInitSettings( sal_True, sal_True, sal_True );
			ImplShowCursor( sal_True );
			Invalidate();
		}
	}

	Control::DataChanged( rDCEvt );
}

// -----------------------------------------------------------------------

void Edit::ImplShowDDCursor()
{
	if ( !mpDDInfo->bVisCursor )
	{
		long nTextWidth = GetTextWidth( maText, 0, mpDDInfo->nDropPos );
		long nTextHeight = GetTextHeight();
		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 = sal_True;
	}
}

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

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

        if ( ImplCallEventListenersAndHandler( VCLEVENT_EDIT_MODIFY, maModifyHdl, this ) )
            // have been destroyed while calling into the handlers
            return;

        // #i13677# notify edit listeners about caret position change
//IAccessibility2 Implementation 2009-----
        //ImplCallEventListeners( VCLEVENT_EDIT_SELECTIONCHANGED );
        ImplCallEventListeners( VCLEVENT_EDIT_CARETCHANGED );
//-----IAccessibility2 Implementation 2009
        // FIXME: this is currently only on aqua
        // check for other platforms that need similar handling
        if( ImplGetSVData()->maNWFData.mbNoFocusRects &&
            IsNativeWidgetEnabled() &&
            IsNativeControlSupported( CTRL_EDITBOX, PART_ENTIRE_CONTROL ) )
        {
            ImplInvalidateOutermostBorder( this );
        }
	}
}

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

IMPL_LINK( Edit, ImplUpdateDataHdl, Timer*, EMPTYARG )
{
	UpdateData();
	return 0;
}

// -----------------------------------------------------------------------

void Edit::EnableUpdateData( sal_uLong nTimeout )
{
	if ( !nTimeout )
		DisableUpdateData();
	else
	{
		if ( !mpUpdateDataTimer )
		{
			mpUpdateDataTimer = new Timer;
			mpUpdateDataTimer->SetTimeoutHdl( LINK( this, Edit, ImplUpdateDataHdl ) );
		}

		mpUpdateDataTimer->SetTimeout( nTimeout );
	}
}

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

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

		StateChanged( STATE_CHANGE_READONLY );
	}
}

// -----------------------------------------------------------------------

void Edit::SetAutocompleteHdl( const Link& rHdl )
{
	maAutocompleteHdl = rHdl;
	if ( mpSubEdit )
		mpSubEdit->SetAutocompleteHdl( rHdl );
}

// -----------------------------------------------------------------------

void Edit::SetInsertMode( sal_Bool bInsert )
{
	if ( bInsert != mbInsertMode )
	{
		mbInsertMode = bInsert;
		if ( mpSubEdit )
			mpSubEdit->SetInsertMode( bInsert );
		else
			ImplShowCursor();
	}
}

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

void Edit::SetMaxTextLen( xub_StrLen nMaxLen )
{
	mnMaxTextLen = nMaxLen ? nMaxLen : EDIT_NOLIMIT;

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

// -----------------------------------------------------------------------

void Edit::SetSelection( const Selection& rSelection )
{
	// Wenn von aussen z.B. im MouseButtonDown die Selektion geaendert wird,
	// soll nicht gleich ein Tracking() zuschlagen und die Selektion aendern.
	if ( IsTracking() )
		EndTracking();
	else if ( mpSubEdit && mpSubEdit->IsTracking() )
		mpSubEdit->EndTracking();

	ImplSetSelection( rSelection );
}

// -----------------------------------------------------------------------

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

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

			if ( aNew != maSelection )
			{
                ImplClearLayoutData();
//IAccessibility2 Implementation 2009-----
				Selection aTemp = maSelection;
//-----IAccessibility2 Implementation 2009
				maSelection = aNew;

				if ( bPaint && ( aOld.Len() || aNew.Len() || IsPaintTransparent() ) )
                    ImplInvalidateOrRepaint( 0, maText.Len() );
				ImplShowCursor();
//IAccessibility2 Implementation 2009-----
				sal_Bool bCaret = sal_False, bSelection = sal_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 = sal_True;
				if (nGap != 0 || oGap != 0)
					bSelection = sal_True;
				if (bCaret)
				{
					if ( mbIsSubEdit )
						((Edit*)GetParent())->ImplCallEventListeners( VCLEVENT_EDIT_CARETCHANGED );
					else
						ImplCallEventListeners( VCLEVENT_EDIT_CARETCHANGED );
				}
				if (bSelection)
				{
					if ( mbIsSubEdit )
						((Edit*)GetParent())->ImplCallEventListeners( VCLEVENT_EDIT_SELECTIONCHANGED );
					else
						ImplCallEventListeners( VCLEVENT_EDIT_SELECTIONCHANGED );
				}
//-----IAccessibility2 Implementation 2009
                // #103511# notify combobox listeners of deselection
                if( !maSelection && GetParent() && GetParent()->GetType() == WINDOW_COMBOBOX )
                    ((Edit*)GetParent())->ImplCallEventListeners( VCLEVENT_COMBOBOX_DESELECT );
			}
		}
	}
}

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

void Edit::ReplaceSelected( const XubString& 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 );
	}
}

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

void Edit::Cut()
{
	if ( !(GetStyle() & WB_PASSWORD ) )
	{
		Copy();
		ReplaceSelected( ImplGetSVEmptyStr() );
	}
}

// -----------------------------------------------------------------------

void Edit::Copy()
{
	if ( !(GetStyle() & WB_PASSWORD ) )
	{
        ::com::sun::star::uno::Reference<com::sun::star::datatransfer::clipboard::XClipboard> aClipboard(GetClipboard());
        ImplCopy( aClipboard );
	}
}

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

void Edit::SetText( const XubString& rStr )
{
	if ( mpSubEdit )
		mpSubEdit->SetText( rStr ); 	// Nicht direkt ImplSetText, falls SetText ueberladen
	else
	{
		Selection aNewSel( 0, 0 );	// Damit nicht gescrollt wird
		ImplSetText( rStr, &aNewSel );
	}
}

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

void Edit::SetSubEdit( Edit* pEdit )
{
	mpSubEdit = pEdit;
	if ( mpSubEdit )
	{
		SetPointer( POINTER_ARROW );	// Nur das SubEdit hat den BEAM...
		mpSubEdit->mbIsSubEdit = sal_True;

        mpSubEdit->SetReadOnly( mbReadOnly );
	}
}

// -----------------------------------------------------------------------

Size Edit::CalcMinimumSize() const
{
	Size aSize ( GetTextWidth( GetText() ), GetTextHeight() );
    // do not create edit fields in which one cannot enter anything
    // a default minimum width should exist for at least 3 characters
    Size aMinSize ( CalcSize( 3 ) );
    if( aSize.Width() < aMinSize.Width() )
        aSize.Width() = aMinSize.Width();
    // add some space between text entry and border
    aSize.Height() += 4;
    
	aSize = CalcWindowSize( aSize );

    // ask NWF what if it has an opinion, too
    ImplControlValue aControlValue;
    Rectangle aRect( Point( 0, 0 ), aSize );
    Rectangle aContent, aBound;
    if( const_cast<Edit*>(this)->GetNativeControlRegion(
                   CTRL_EDITBOX, PART_ENTIRE_CONTROL,
                   aRect, 0, aControlValue, rtl::OUString(), aBound, aContent) )
    {
        if( aBound.GetHeight() > aSize.Height() )
            aSize.Height() = aBound.GetHeight();
    }
    return aSize;
}

Size Edit::GetMinimumEditSize()
{
    Window* pDefWin = ImplGetDefaultWindow();
    Edit aEdit( pDefWin, WB_BORDER );
    Size aSize( aEdit.CalcMinimumSize() );
    return aSize;    
}

// -----------------------------------------------------------------------

Size Edit::GetOptimalSize(WindowSizeType eType) const
{
    switch (eType) {
    case WINDOWSIZE_MINIMUM:
        return CalcMinimumSize();
    default:
        return Control::GetOptimalSize( eType );
    }
}

// -----------------------------------------------------------------------

Size Edit::CalcSize( xub_StrLen nChars ) const
{
	// Breite fuer n Zeichen, unabhaengig vom Inhalt.
	// Funktioniert nur bei FixedFont richtig, sonst Mittelwert.
	Size aSz( GetTextWidth( XubString( 'x' ) ), GetTextHeight() );
	aSz.Width() *= nChars;
	aSz = CalcWindowSize( aSz );
	return aSz;
}

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

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

// -----------------------------------------------------------------------

PopupMenu* Edit::CreatePopupMenu()
{
    ResMgr* pResMgr = ImplGetResMgr();
    if( ! pResMgr )
        return new PopupMenu();

	PopupMenu* pPopup = new PopupMenu( ResId( SV_RESID_MENU_EDIT, *pResMgr ) );
	pPopup->SetAccelKey( SV_MENU_EDIT_UNDO, KeyCode( KEYFUNC_UNDO ) );
	pPopup->SetAccelKey( SV_MENU_EDIT_CUT, KeyCode( KEYFUNC_CUT ) );
	pPopup->SetAccelKey( SV_MENU_EDIT_COPY, KeyCode( KEYFUNC_COPY ) );
	pPopup->SetAccelKey( SV_MENU_EDIT_PASTE, KeyCode( KEYFUNC_PASTE ) );
	pPopup->SetAccelKey( SV_MENU_EDIT_DELETE, KeyCode( KEYFUNC_DELETE ) );
	pPopup->SetAccelKey( SV_MENU_EDIT_SELECTALL, KeyCode( KEY_A, sal_False, sal_True, sal_False, sal_False ) );
	pPopup->SetAccelKey( SV_MENU_EDIT_INSERTSYMBOL, KeyCode( KEY_S, sal_True, sal_True, sal_False, sal_False ) );
	return pPopup;
}

// -----------------------------------------------------------------------

void Edit::DeletePopupMenu( PopupMenu* pMenu )
{
	delete pMenu;
}

// ::com::sun::star::datatransfer::dnd::XDragGestureListener
void Edit::dragGestureRecognized( const ::com::sun::star::datatransfer::dnd::DragGestureEvent& rDGE ) throw (::com::sun::star::uno::RuntimeException)
{
	vos::OGuard aVclGuard( Application::GetSolarMutex() );

    if ( !IsTracking() && maSelection.Len() &&
		 !(GetStyle() & WB_PASSWORD) && (!mpDDInfo || mpDDInfo->bStarterOfDD == sal_False) ) // Kein Mehrfach D&D
	{
		Selection aSel( maSelection );
		aSel.Justify();

		// Nur wenn Maus in der Selektion...
		Point aMousePos( rDGE.DragOriginX, rDGE.DragOriginY );
		xub_StrLen nChar = ImplGetCharPos( aMousePos );
		if ( (nChar >= aSel.Min()) && (nChar < aSel.Max()) )
		{
			if ( !mpDDInfo )
				mpDDInfo = new DDInfo;

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


			if ( IsTracking() )
				EndTracking();	// Vor D&D Tracking ausschalten

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

		}
	}
}

// ::com::sun::star::datatransfer::dnd::XDragSourceListener
void Edit::dragDropEnd( const ::com::sun::star::datatransfer::dnd::DragSourceDropEvent& rDSDE ) throw (::com::sun::star::uno::RuntimeException)
{
	vos::OGuard aVclGuard( Application::GetSolarMutex() );

	if ( rDSDE.DropSuccess && ( rDSDE.DropAction & datatransfer::dnd::DNDConstants::ACTION_MOVE ) )
	{
        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();
	delete mpDDInfo;
	mpDDInfo = NULL;
}

// ::com::sun::star::datatransfer::dnd::XDropTargetListener
void Edit::drop( const ::com::sun::star::datatransfer::dnd::DropTargetDropEvent& rDTDE ) throw (::com::sun::star::uno::RuntimeException)
{
	vos::OGuard aVclGuard( Application::GetSolarMutex() );

	sal_Bool bChanges = sal_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 = sal_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( SOT_FORMAT_STRING, aFlavor );
			if ( xDataObj->isDataFlavorSupported( aFlavor ) )
			{
				uno::Any aData = xDataObj->getTransferData( aFlavor );
				::rtl::OUString aText;
				aData >>= aText;
			    ImplInsertText( aText );
                bChanges = sal_True;
		        ImplModified();
			}
		}

		if ( !mpDDInfo->bStarterOfDD )
		{
			delete mpDDInfo;
			mpDDInfo = NULL;
		}
	}

    rDTDE.Context->dropComplete( bChanges );
}

void Edit::dragEnter( const ::com::sun::star::datatransfer::dnd::DropTargetDragEnterEvent& rDTDE ) throw (::com::sun::star::uno::RuntimeException)
{
    if ( !mpDDInfo )
    {
		mpDDInfo = new DDInfo;
    }
    // search for string data type
    const Sequence< com::sun::star::datatransfer::DataFlavor >& rFlavors( rDTDE.SupportedDataFlavors );
    sal_Int32 nEle = rFlavors.getLength();
    mpDDInfo->bIsStringSupported = sal_False;
    for( sal_Int32 i = 0; i < nEle; i++ )
    {
        sal_Int32 nIndex = 0;
        rtl::OUString aMimetype = rFlavors[i].MimeType.getToken( 0, ';', nIndex );
        if( aMimetype.equalsAscii( "text/plain" ) )
        {
            mpDDInfo->bIsStringSupported = sal_True;
            break;
        }
    }
}

void Edit::dragExit( const ::com::sun::star::datatransfer::dnd::DropTargetEvent& ) throw (::com::sun::star::uno::RuntimeException)
{
	vos::OGuard aVclGuard( Application::GetSolarMutex() );

    ImplHideDDCursor();
}

void Edit::dragOver( const ::com::sun::star::datatransfer::dnd::DropTargetDragEvent& rDTDE ) throw (::com::sun::star::uno::RuntimeException)
{
	vos::OGuard aVclGuard( Application::GetSolarMutex() );

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

	xub_StrLen 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
    {
	    // Alten Cursor wegzeichnen...
	    if ( !mpDDInfo->bVisCursor || ( nPrevDropPos != mpDDInfo->nDropPos ) )
	    {
		    ImplHideDDCursor();
		    ImplShowDDCursor();
	    }
        rDTDE.Context->acceptDrag( rDTDE.DropAction );
    }
}

ImplSubEdit::ImplSubEdit( Edit* pParent, WinBits nStyle ) :
	Edit( pParent, nStyle )
{
	pParent->SetSubEdit( this );
}

// -----------------------------------------------------------------------

void ImplSubEdit::Modify()
{
	GetParent()->Modify();
}

XubString Edit::GetSurroundingText() const
{
  if ( mpSubEdit )
    return mpSubEdit->GetSurroundingText();
  else
    return maText;
}

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