/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_basic.hxx"
#include <tools/stream.hxx>
#include <svtools/texteng.hxx>
#include <svtools/textview.hxx>
#include <svtools/txtattr.hxx>
#include <basic/sbxmeth.hxx>
#ifndef _BASIC_TTRESHLP_HXX
#include <basic/ttstrhlp.hxx>
#endif

#include "basic.hrc"
#include "textedit.hxx"
#include "appedit.hxx"
#include "brkpnts.hxx"
#include <basic/testtool.hxx>  // defines for syntax highlighting

TextEditImp::TextEditImp( AppEdit* pParent, const WinBits& aBits )
: Window( pParent, aBits )
, pAppEdit( pParent )
, bHighlightning( sal_False )
, bDoSyntaxHighlight( sal_False )
, bDelayHighlight( sal_True )
, nTipId( 0 )
, bViewMoved( sal_False )
{
	pTextEngine = new TextEngine();
	pTextEngine->SetMaxTextLen( STRING_MAXLEN );
	pTextEngine->EnableUndo( sal_True );

	pTextView = new TextView( pTextEngine, this );
	pTextEngine->InsertView( pTextView );
	pTextEngine->SetModified( sal_False );

	aSyntaxIdleTimer.SetTimeout( 200 );
	aSyntaxIdleTimer.SetTimeoutHdl( LINK( this, TextEditImp, SyntaxTimerHdl ) );

	aImplSyntaxIdleTimer.SetTimeout( 1 );
	aImplSyntaxIdleTimer.SetTimeoutHdl( LINK( this, TextEditImp, SyntaxTimerHdl ) );

	StartListening( *pTextEngine );

	HideTipTimer.SetTimeout( 5000 );	// 5 seconds
	ShowTipTimer.SetTimeout( 500 );		// 1/2 seconds
	HideTipTimer.SetTimeoutHdl( LINK( this, TextEditImp, HideVarContents ) );
	ShowTipTimer.SetTimeoutHdl( LINK( this, TextEditImp, ShowVarContents ) );
}

TextEditImp::~TextEditImp()
{
	pTextEngine->RemoveView( pTextView );
	delete pTextView;
	delete pTextEngine;
}

sal_Bool TextEditImp::ViewMoved()
{
    sal_Bool bOld = bViewMoved;
    bViewMoved = sal_False;
    return bOld;
}

void TextEditImp::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
{
    (void) rBC; /* avoid warning about unused parameter */ 
	if ( rHint.ISA( TextHint ) )
	{
		const TextHint& rTextHint = (const TextHint&)rHint;
		if( rTextHint.GetId() == TEXT_HINT_VIEWSCROLLED )
		{
			pAppEdit->pHScroll->SetThumbPos( pTextView->GetStartDocPos().X() );
			pAppEdit->pVScroll->SetThumbPos( pTextView->GetStartDocPos().Y() );
			if ( ((TextEdit*)(pAppEdit->pDataEdit))->GetBreakpointWindow() )
				((TextEdit*)(pAppEdit->pDataEdit))->GetBreakpointWindow()->Scroll( 0, ((TextEdit*)(pAppEdit->pDataEdit))->GetBreakpointWindow()->GetCurYOffset() - pTextView->GetStartDocPos().Y() );
            bViewMoved = sal_True;
		}
		else if( rTextHint.GetId() == TEXT_HINT_TEXTHEIGHTCHANGED )
		{
			if ( pTextView->GetStartDocPos().Y() )
			{
				long nOutHeight = GetOutputSizePixel().Height();
				long nTextHeight = pTextEngine->GetTextHeight();
				if ( nTextHeight < nOutHeight )
					pTextView->Scroll( 0, pTextView->GetStartDocPos().Y() );
			}

			pAppEdit->SetScrollBarRanges();
		}
		else if( rTextHint.GetId() == TEXT_HINT_TEXTFORMATTED )
		{
			sal_uIntPtr nWidth = pTextEngine->CalcTextWidth();
			if ( (sal_uIntPtr)nWidth != pAppEdit->nCurTextWidth )
			{
				pAppEdit->nCurTextWidth = nWidth;
				if ( pAppEdit->pHScroll )
				{	// Initialization finished?
					pAppEdit->pHScroll->SetRange( Range( 0, (long)nWidth) );
					pAppEdit->pHScroll->SetThumbPos( pTextView->GetStartDocPos().X() );
				}
			}
		}
		else if( rTextHint.GetId() == TEXT_HINT_PARAINSERTED )
		{
			if ( ((TextEdit*)(pAppEdit->pDataEdit))->GetBreakpointWindow() )
				((TextEdit*)(pAppEdit->pDataEdit))->GetBreakpointWindow()->AdjustBreakpoints( rTextHint.GetValue()+1, sal_True );
		}
		else if( rTextHint.GetId() == TEXT_HINT_PARAREMOVED )
		{
			if ( ((TextEdit*)(pAppEdit->pDataEdit))->GetBreakpointWindow() )
				((TextEdit*)(pAppEdit->pDataEdit))->GetBreakpointWindow()->AdjustBreakpoints( rTextHint.GetValue()+1, sal_False );

			// Itchy adaption for two signs at line ends instead of one (hard coded default)
			pTextEngine->SetMaxTextLen( STRING_MAXLEN - pTextEngine->GetParagraphCount() );
		}
		else if( rTextHint.GetId() == TEXT_HINT_FORMATPARA )
		{
			DoDelayedSyntaxHighlight(
                sal::static_int_cast< xub_StrLen >( rTextHint.GetValue() ) );
			if ( pTextView->GetTextEngine()->IsModified() )
                ModifyHdl.Call( NULL );
		}
	}
}

#define TEXTATTR_SPECHIAL	55
class TextAttribSpechial : public TextAttrib
{
private:
	FontWeight	maFontWeight;

public:
    TextAttribSpechial( const FontWeight& rFontWeight );
    TextAttribSpechial( const TextAttribSpechial& rAttr );
    ~TextAttribSpechial() {;}

	virtual void 			SetFont( Font& rFont ) const;
	virtual TextAttrib*		Clone() const;
	virtual int				operator==( const TextAttrib& rAttr ) const;
};

TextAttribSpechial::TextAttribSpechial( const FontWeight& rFontWeight )
	: TextAttrib( TEXTATTR_SPECHIAL ), maFontWeight( rFontWeight )
{}

TextAttribSpechial::TextAttribSpechial( const TextAttribSpechial& rAttr )
	: TextAttrib( rAttr ), maFontWeight( rAttr.maFontWeight )
{}

void TextAttribSpechial::SetFont( Font& rFont ) const
{
	rFont.SetWeight( maFontWeight );
}

TextAttrib* TextAttribSpechial::Clone() const
{
	return new TextAttribSpechial( *this );
}

int TextAttribSpechial::operator==( const TextAttrib& rAttr ) const
{
	return ( ( TextAttrib::operator==(rAttr ) ) &&
				( maFontWeight == ((const TextAttribSpechial&)rAttr).maFontWeight ) );
}

void TextEditImp::ImpDoHighlight( const String& rSource, sal_uIntPtr nLineOff )
{
	SbTextPortions aPortionList;
	pAppEdit->GetBasicFrame()->Basic().Highlight( rSource, aPortionList );

	sal_uInt16 nCount = aPortionList.Count();
	if ( !nCount )
		return;

	SbTextPortion& rLast = aPortionList[nCount-1];
	if ( rLast.nStart > rLast.nEnd ) 	// Nur bis Bug von MD behoben
	{
#if OSL_DEBUG_LEVEL > 1
		DBG_ERROR( "MD-Bug nicht beseitigt!" );
#endif
		nCount--;
		aPortionList.Remove( nCount);
		if ( !nCount )
			return;
	}

	// here is the postprocessing for types for the TestTool
	sal_uInt16 i;
	sal_Bool bWasTTControl = sal_False;
	for ( i = 0; i < aPortionList.Count(); i++ )
	{
		SbTextPortion& r = aPortionList[i];
//		DBG_ASSERT( r.nStart <= r.nEnd, "Highlight: Start > End?" );
		if ( r.nStart > r.nEnd ) 	// Nur bis Bug von MD behoben
			continue;

		SbTextType eType = r.eType;
		Color aColor;
		switch ( eType )
		{
			case SB_SYMBOL:
				{
 					String aSymbol = rSource.Copy( r.nStart, r.nEnd - r.nStart +1 );
					r.eType = pAppEdit->GetBasicFrame()->Basic().GetSymbolType( aSymbol, bWasTTControl );

					if ( r.eType == TT_CONTROL )
						bWasTTControl = sal_True;
					else
						bWasTTControl = sal_False;
				}
				break;
			case SB_PUNCTUATION:
				{
 					String aPunctuation = rSource.Copy( r.nStart, r.nEnd - r.nStart +1 );
					if ( aPunctuation.CompareToAscii( "." ) != COMPARE_EQUAL )
						bWasTTControl = sal_False;
				}
				break;
			default:
				bWasTTControl = sal_False;
		}
	}

		// Es muessen nur die Blanks und Tabs mit attributiert werden.
		// If there are two equal attributes one after another,
		// they are optimized by the EditEngine.
		xub_StrLen nLastEnd = 0;
#ifdef DBG_UTIL
        xub_StrLen nLine1 = aPortionList[0].nLine;
#endif
		for ( i = 0; i < nCount; i++ )
		{
			SbTextPortion& r = aPortionList[i];
			DBG_ASSERT( r.nLine == nLine1, "doch mehrere Zeilen ?" );
			DBG_ASSERT( r.nStart <= r.nEnd, "Highlight: Start > End?" );
			if ( r.nStart > r.nEnd ) 	// Nur bis Bug von MD behoben
				continue;

			if ( r.nStart > nLastEnd )
			{
				// Kann ich mich drauf verlassen, dass alle ausser
				// Blank und Tab gehighlightet wird ?!
				r.nStart = nLastEnd;
			}
			nLastEnd = r.nEnd+1;
			if ( ( i == (nCount-1) ) && ( r.nEnd < rSource.Len() ) )
				r.nEnd = rSource.Len()-1;
		}

	sal_Bool bWasModified = pTextEngine->IsModified();
	for ( i = 0; i < aPortionList.Count(); i++ )
	{
		SbTextPortion& r = aPortionList[i];
//		DBG_ASSERT( r.nStart <= r.nEnd, "Highlight: Start > End?" );
		if ( r.nStart > r.nEnd ) 	// Nur bis Bug von MD behoben
			continue;

		SbTextType eCol = r.eType;
		Color aColor;
		sal_uIntPtr nLine = nLineOff+r.nLine-1; // -1, because BASIC starts with 1
		switch ( +eCol )
		{
			case SB_KEYWORD:
				aColor = Color( COL_BLUE );
				break;
			case SB_SYMBOL:
				aColor = Color( RGB_COLORDATA( 0x00, 0x60, 0x00 ) );
				break;
			case SB_STRING:
				aColor = Color( COL_RED );
				break;
			case SB_NUMBER:
				aColor = Color( COL_MAGENTA );
				break;
			case SB_PUNCTUATION:
				aColor = Color( COL_BLACK );
				break;
			case SB_COMMENT:
				aColor = Color( COL_GRAY );
				break;
			case TT_KEYWORD:
			case TT_LOCALCMD:
				aColor = Color( COL_LIGHTBLUE );
				break;
			case TT_REMOTECMD:
				aColor = Color( RGB_COLORDATA( 0x00, 0xB0, 0xB0 ) );
				break;
			case TT_CONTROL:
			case TT_SLOT:
				aColor = Color( RGB_COLORDATA( 0x00, 0xB0, 0x00 ) );
				break;
			case TT_METHOD:
				aColor = Color( RGB_COLORDATA( 0x00, 0xB0, 0x00 ) );
				break;
			case TT_NOMETHOD:
				{
					aColor = Color( COL_RED );
					pTextEngine->SetAttrib( TextAttribSpechial( WEIGHT_BOLD ), nLine, r.nStart, r.nEnd+1 );
				}
				break;
			default:
				{
					aColor = Color( RGB_COLORDATA( 0xff, 0x80, 0x80 ) );
					DBG_ERROR( "Unknown syntax color" );
				}
		}
		pTextEngine->SetAttrib( TextAttribFontColor( aColor ), nLine, r.nStart, r.nEnd+1 );
	}
	// Highlighting should not modify the text
	pTextEngine->SetModified( bWasModified );
}

void TextEditImp::DoSyntaxHighlight( sal_uIntPtr nPara )
{
	// Due to delayed syntax highlight it can happend that the 
        // paragraph does no longer exist
	if ( nPara < pTextEngine->GetParagraphCount() )
	{
		// leider weis ich nicht, ob genau diese Zeile Modified() ...
//		if ( pProgress )
//			pProgress->StepProgress();
		pTextEngine->RemoveAttribs( nPara );
		String aSource( pTextEngine->GetText( nPara ) );
		ImpDoHighlight( aSource, nPara );
	}
}

void TextEditImp::DoDelayedSyntaxHighlight( xub_StrLen nPara )
{
	// Paragraph is added to 'List', processed in TimerHdl.
	// => Do not manipulate paragraphs while EditEngine is formatting
//	if ( pProgress )
//		pProgress->StepProgress();

	if ( !bHighlightning && bDoSyntaxHighlight )
	{
		if ( bDelayHighlight )
		{
			aSyntaxLineTable.Insert( nPara, (void*)(sal_uIntPtr)1 );
			aSyntaxIdleTimer.Start();
		}
		else
			DoSyntaxHighlight( nPara );
	}
}

IMPL_LINK( TextEditImp, SyntaxTimerHdl, Timer *, EMPTYARG )
{
	DBG_ASSERT( pTextView, "Not yet a View but Syntax-Highlight ?!" );
	pTextEngine->SetUpdateMode( sal_False );

	bHighlightning = sal_True;
	sal_uInt16 nLine;
	while ( aSyntaxLineTable.First() && !Application::AnyInput( INPUT_MOUSEANDKEYBOARD ) )
	{
		nLine = (sal_uInt16)aSyntaxLineTable.GetCurKey();
		DoSyntaxHighlight( nLine );
		aSyntaxLineTable.Remove( nLine );
/*		if ( Application::AnyInput() )
		{
			aSyntaxIdleTimer.Start();		// Starten, falls wir in einem Dialog landen
			pTextView->ShowCursor( sal_True, sal_True );
			pTextEngine->SetUpdateMode( sal_True );
			bHighlightning = sal_False;
			GetpApp()->Reschedule();
			bHighlightning = sal_True;
			pTextEngine->SetUpdateMode( sal_False );
		}*/
	}

	sal_Bool bWasModified = pTextEngine->IsModified();
	if ( aSyntaxLineTable.Count() > 3 ) 				// Without VDev
	{
		pTextEngine->SetUpdateMode( sal_True );
		pTextView->ShowCursor( sal_True, sal_True );
	}
	else
		pTextEngine->SetUpdateMode( sal_True );				// ! With VDev
//	pTextView->ForceUpdate();

	// SetUpdateMode( sal_True ) soll kein Modify setzen
	pTextEngine->SetModified( bWasModified );

	// SyntaxTimerHdl wird gerufen, wenn Text-Aenderung
	// => gute Gelegenheit, Textbreite zu ermitteln!
//	long nPrevTextWidth = nCurTextWidth;
//	nCurTextWidth = pTextEngine->CalcTextWidth();
//	if ( nCurTextWidth != nPrevTextWidth )
//		SetScrollBarRanges();
	bHighlightning = sal_False;

	if ( aSyntaxLineTable.First() )
		aImplSyntaxIdleTimer.Start();

//	while ( Application::AnyInput() )
//		Application::Reschedule();	// Reschedule, da der UserEvent keine Paints etc. durchlässt

	return 0;
}

void TextEditImp::InvalidateSyntaxHighlight()
{
	for ( xub_StrLen i = 0; i < pTextEngine->GetParagraphCount(); i++ )
		DoDelayedSyntaxHighlight( i );
}

void TextEditImp::SyntaxHighlight( sal_Bool bNew )
{
	if ( ( bNew && bDoSyntaxHighlight ) || ( !bNew && !bDoSyntaxHighlight ) )
		return;

	bDoSyntaxHighlight = bNew;
	if ( !pTextEngine )
		return;


	if ( bDoSyntaxHighlight )
	{
		InvalidateSyntaxHighlight();
	}
	else
	{
		aSyntaxIdleTimer.Stop();
		pTextEngine->SetUpdateMode( sal_False );
		for ( sal_uIntPtr i = 0; i < pTextEngine->GetParagraphCount(); i++ )
			pTextEngine->RemoveAttribs( i );

//		pTextEngine->QuickFormatDoc();
		pTextEngine->SetUpdateMode( sal_True );
		pTextView->ShowCursor(sal_True, sal_True );
	}
}


void TextEditImp::SetFont( const Font& rNewFont )
{
	pTextEngine->SetFont(rNewFont);
}

sal_Bool TextEditImp::IsModified()
{
	return pTextEngine->IsModified();
}

void TextEditImp::KeyInput( const KeyEvent& rKeyEvent )
{
	sal_Bool bWasModified = pTextView->GetTextEngine()->IsModified();
	pTextView->GetTextEngine()->SetModified( sal_False );

	if ( !pTextView->KeyInput( rKeyEvent ) )
		Window::KeyInput( rKeyEvent );

	if ( pTextView->GetTextEngine()->IsModified() )
		ModifyHdl.Call( NULL );
	else
		pTextView->GetTextEngine()->SetModified( bWasModified );
}

void TextEditImp::Paint( const Rectangle& rRect ){ pTextView->Paint( rRect );}
void TextEditImp::MouseButtonUp( const MouseEvent& rMouseEvent ){ pTextView->MouseButtonUp( rMouseEvent );}
//void TextEditImp::MouseButtonDown( const MouseEvent& rMouseEvent ){ pTextView->MouseButtonDown( rMouseEvent );}
//void TextEditImp::MouseMove( const MouseEvent& rMouseEvent ){ pTextView->MouseMove( rMouseEvent );}
//void TextEditImp::Command( const CommandEvent& rCEvt ){ pTextView->Command( rCEvt );}
//sal_Bool TextEditImp::Drop( const DropEvent& rEvt ){ return sal_False /*pTextView->Drop( rEvt )*/;}
//sal_Bool TextEditImp::QueryDrop( DropEvent& rEvt ){ return sal_False /*pTextView->QueryDrop( rEvt )*/;}


void TextEditImp::Command( const CommandEvent& rCEvt )
{
	switch( rCEvt.GetCommand() ) {
		case COMMAND_CONTEXTMENU:
		case COMMAND_WHEEL:
			GetParent()->Command( rCEvt );
			break;
		default:
			pTextView->Command( rCEvt );
	}
}


void TextEditImp::MouseButtonDown( const MouseEvent& rMouseEvent )
{
	pTextView->MouseButtonDown( rMouseEvent );
	HideVarContents( NULL );
	ShowTipTimer.Stop();
}


void TextEditImp::MouseMove( const MouseEvent& rMEvt )
{
	pTextView->MouseMove( rMEvt );
	HideVarContents( NULL );
	if ( rMEvt.GetButtons() == 0 )
		ShowTipTimer.Start();
	if ( rMEvt.IsLeaveWindow() )
		ShowTipTimer.Stop();
}


IMPL_LINK( TextEditImp, HideVarContents, void*, EMPTYARG )
{
	if ( nTipId )
	{
		Help::HideTip( nTipId );
		nTipId = 0;
		aTipWord = String();
	}
	return 0;
}

static const char cSuffixes[] = "%&!#@$";


SbxBase* TextEditImp::GetSbxAtMousePos( String &aWord )
{
	Point aPos = GetPointerPosPixel();
	Point aDocPos = pTextView->GetDocPos( aPos );
	aWord = pTextEngine->GetWord( pTextEngine->GetPaM( aDocPos ) );

	if ( aWord.Len() /*&& !Application::GetAppInternational().IsNumeric( aWord )*/ )
	{
		xub_StrLen nLastChar = aWord.Len()-1;
		String aSuffixes = CUniString( cSuffixes );
		if ( aSuffixes.Search( aWord.GetChar(nLastChar) ) != STRING_NOTFOUND )
			aWord.Erase( nLastChar, 1 );
        // because perhaps TestTools throws an error
		sal_Bool bWasError = SbxBase::IsError();
        pAppEdit->GetBasicFrame()->Basic().DebugFindNoErrors( sal_True );
		SbxBase* pSBX = StarBASIC::FindSBXInCurrentScope( aWord );
        pAppEdit->GetBasicFrame()->Basic().DebugFindNoErrors( sal_False );
		DBG_ASSERT( !( !bWasError && SbxBase::IsError()), "Error generated while retrieving Variable data for viewing" );
		if ( !bWasError && SbxBase::IsError() )
			SbxBase::ResetError();

		return pSBX;
	}
	return NULL;
}


IMPL_LINK( TextEditImp, ShowVarContents, void*, EMPTYARG )
{
	String aWord;
	SbxBase* pSBX = GetSbxAtMousePos( aWord );
	String aHelpText;
	Point aPos = GetPointerPosPixel();

	if ( pSBX && pSBX->ISA( SbxVariable ) && !pSBX->ISA( SbxMethod ) )
	{
		SbxVariable* pVar = (SbxVariable*)pSBX;
		SbxDataType eType = pVar->GetType();
		if ( eType == SbxOBJECT )
		{
        // Can cause a crash: Type == Object does not mean pVar == Object
			if ( pVar->GetObject() && pVar->GetObject()->ISA( SbxObject ) )
				aHelpText = ((SbxObject*)(pVar->GetObject()))->GetClassName();
			else
				aHelpText = CUniString("Object");
		}
		else if ( eType & SbxARRAY )
			aHelpText = CUniString("{...}");
		else if ( eType != SbxEMPTY )
		{
			aHelpText = pVar->GetName();
			if ( !aHelpText.Len() ) // Name is not copied in arguments
				aHelpText = aWord;
			aHelpText += '=';
			aHelpText += pVar->GetString();
		}
	}


	if ( aHelpText.Len() && aTipPos != aPos && aTipWord != aWord )
	{
		if ( nTipId )
			Help::HideTip( nTipId );
		nTipId = Help::ShowTip( this, Rectangle(), aHelpText );

		HideTipTimer.Start();
		aTipWord = aWord;
		aTipPos = aPos;
	}
	if ( nTipId && aTipPos != aPos )
	{
		Help::HideTip( nTipId );
		nTipId = 0;
		aTipWord = String();
	}

	return 0;
}


void TextEditImp::BuildKontextMenu( PopupMenu *&pMenu )
{
	String aWord;
	SbxBase* pSBX = GetSbxAtMousePos( aWord );
	if ( pSBX && pSBX->ISA( SbxVariable ) && !pSBX->ISA( SbxMethod ) )
	{
		SbxVariable* pVar = (SbxVariable*)pSBX;
		SbxDataType eType = pVar->GetType();

		if ( ( eType & ( SbxVECTOR | SbxARRAY | SbxBYREF )) == 0 )
		{

/*
Boolean
Currency
Date
Double
Integer
Long
Object
Single
String
Variant(Empty)
*/
			switch ( eType )
			{
				case SbxBOOL:
//				case SbxCURRENCY:
//				case SbxDATE:
				case SbxDOUBLE:
				case SbxINTEGER:
				case SbxLONG:
//				case SbxOBJECT:		// cannot be edited
				case SbxSINGLE:
				case SbxSTRING:

				case SbxVARIANT:	// does not occure, instead SbxEMPTY
				case SbxEMPTY:
					{
						pAppEdit->GetBasicFrame()->SetEditVar( pVar );
						if ( !pMenu )
							pMenu = new PopupMenu();
						else
							pMenu->InsertSeparator();

						pMenu->InsertItem( RID_POPUPEDITVAR, ((BasicFrame*)GetpApp()->GetAppWindow())->GenRealString( GEN_RES_STR1( IDS_EDIT_VAR, aWord ) ) );
					}
					break;
                default:
                    ;
			}
		}
	}
}




DBG_NAME(TextEdit)

TextEdit::TextEdit( AppEdit* pParent, const WinBits& aBits )
: pBreakpointWindow( NULL )
, bFileWasUTF8( sal_False )
, bSaveAsUTF8( sal_False )
, aEdit( pParent, aBits | WB_NOHIDESELECTION )
{
DBG_CTOR(TextEdit,0);
}

TextEdit::~TextEdit()
{DBG_DTOR(TextEdit,0);}

void TextEdit::Highlight( sal_uIntPtr nLine, xub_StrLen nCol1, xub_StrLen nCol2 )
{
    if ( nLine )    // Should not occure but at 'Sub expected' in first line
        nLine--;

    String s = aEdit.pTextEngine->GetText( nLine );

    if( nCol1 == STRING_NOTFOUND )
    {
        // No column given
        nCol1 = 0;
        nCol2 = STRING_NOTFOUND;
    }
    if( nCol2 == STRING_NOTFOUND )
    {
        nCol2 = s.Len();
    }
    // Adaption to the Precompiler | EditText != Compilied Text
    if ( nCol2 > s.Len() )
        nCol2 = s.Len();
    if ( nCol1 >= nCol2 )
        nCol1 = 0;

    // Because nCol2 *may* point after the current statement
    // (because the next one starts there) there are space
    // that must be removed
    sal_Bool bColon = sal_False;

	while ( s.GetChar( nCol2 ) == ' ' && nCol2 > nCol1 && !bColon )
	{
		nCol2--;
		if ( s.GetChar( nCol2 ) == ':' )
		{
			nCol2--;
			bColon = sal_True;
		}
	}

    aEdit.ViewMoved();
	aEdit.pTextView->SetSelection( TextSelection(TextPaM(nLine,nCol2+1), TextPaM(nLine,nCol1)) );
    if ( aEdit.ViewMoved() )
    {
    	aEdit.pTextView->SetSelection( TextSelection(TextPaM(TEXT_PARA_ALL,1)) );   // fix #105169#
        aEdit.pTextView->SetSelection( TextSelection(TextPaM((nLine>=2?nLine-2:0),nCol2+1)) );
    	aEdit.pTextView->SetSelection( TextSelection(TextPaM(nLine,nCol2+1), TextPaM(nLine,nCol1)) );
    }
}


void TextEdit::Delete(){ aEdit.pTextView->KeyInput( KeyEvent( 0, KeyCode( KEYFUNC_DELETE ) )); }
void TextEdit::Cut(){ aEdit.pTextView->Cut(); }
void TextEdit::Copy(){ aEdit.pTextView->Copy(); }
void TextEdit::Paste(){ aEdit.pTextView->Paste(); }
void TextEdit::Undo(){ aEdit.pTextView->Undo(); }
void TextEdit::Redo(){ aEdit.pTextView->Redo(); }
String TextEdit::GetSelected(){ return aEdit.pTextView->GetSelected(); }
TextSelection TextEdit::GetSelection() const{ return aEdit.pTextView->GetSelection(); }
void TextEdit::SetSelection( const TextSelection& rSelection ){ aEdit.pTextView->SetSelection( rSelection ); }

sal_uInt16 TextEdit::GetLineNr() const
{
	return sal::static_int_cast< sal_uInt16 >(
        aEdit.pTextView->GetSelection().GetEnd().GetPara()+1);
}

void TextEdit::ReplaceSelected( const String& rStr ){ aEdit.pTextView->InsertText(rStr); }
sal_Bool TextEdit::IsModified(){ return aEdit.IsModified(); }

String TextEdit::GetText() const
{
	return aEdit.pTextEngine->GetText( GetSystemLineEnd() );
}

void TextEdit::SetText( const String& rStr ){ aEdit.pTextEngine->SetText(rStr); aEdit.pTextEngine->SetModified( sal_False ); }
void TextEdit::SetModifyHdl( Link l ){ aEdit.SetModifyHdl(l); }
sal_Bool TextEdit::HasText() const { return aEdit.pTextEngine->GetTextLen() > 0; }

// Search from the beginning or at mark + 1
sal_Bool TextEdit::Find( const String& s )
{
	DBG_CHKTHIS(TextEdit,0);

	TextSelection aSelection = aEdit.pTextView->GetSelection();
	sal_uIntPtr nPara = aSelection.GetStart().GetPara();
	xub_StrLen nIndex = aSelection.GetStart().GetIndex();

	if ( aSelection.HasRange() )
		nIndex ++;

	while ( nPara <= aEdit.pTextEngine->GetParagraphCount() )
	{
		String aText = aEdit.pTextEngine->GetText( nPara );

		nIndex = aText.Search( s, nIndex );
		if( nIndex != STRING_NOTFOUND )
		{
			aEdit.pTextView->SetSelection( TextSelection( TextPaM( nPara, nIndex ), TextPaM( nPara, nIndex + s.Len() ) ) );
			return sal_True;
		}
		nIndex = 0;
		nPara++;
	}
	return sal_False;
}

sal_Bool TextEdit::Load( const String& aName )
{
DBG_CHKTHIS(TextEdit,0);
	sal_Bool bOk = sal_True;
	SvFileStream aStrm( aName, STREAM_STD_READ );
	if( aStrm.IsOpen() )
	{
		String aText, aLine, aLineBreak;
		sal_Bool bIsFirstLine = sal_True;
        aLineBreak += '\n';
        aLineBreak.ConvertLineEnd();
		rtl_TextEncoding aFileEncoding = RTL_TEXTENCODING_IBM_850;
		while( !aStrm.IsEof() && bOk )
		{
			aStrm.ReadByteStringLine( aLine, aFileEncoding );
			if ( bIsFirstLine && IsTTSignatureForUnicodeTextfile( aLine ) )
			{
				aFileEncoding = RTL_TEXTENCODING_UTF8;
				bFileWasUTF8 = sal_True;
			}
			else
			{
				if ( !bIsFirstLine )
					aText += aLineBreak;
				aText += aLine;
				bIsFirstLine = sal_False;
			}
			if( aStrm.GetError() != SVSTREAM_OK )
				bOk = sal_False;
		}
		SetText( aText );
	}
	else
		bOk = sal_False;
	return bOk;
}

sal_Bool TextEdit::Save( const String& aName )
{
DBG_CHKTHIS(TextEdit,0);
	sal_Bool bOk = sal_True;
	SvFileStream aStrm( aName, STREAM_STD_WRITE | STREAM_TRUNC );
	rtl_TextEncoding aFileEncoding = RTL_TEXTENCODING_IBM_850;
	if( aStrm.IsOpen() )
	{
		if ( bFileWasUTF8 || bSaveAsUTF8 )
		{
			aStrm << TT_SIGNATURE_FOR_UNICODE_TEXTFILES;
			aStrm << sal_Char(_LF);
			aFileEncoding = RTL_TEXTENCODING_UTF8;
		}
		String aSave = GetText();
		aSave.ConvertLineEnd(LINEEND_LF);
		aStrm << ByteString( aSave, aFileEncoding ).GetBuffer();
		if( aStrm.GetError() != SVSTREAM_OK )
			bOk = sal_False;
		else
			aEdit.pTextEngine->SetModified(sal_False);
	} else bOk = sal_False;
	return bOk;
}


void TextEdit::BuildKontextMenu( PopupMenu *&pMenu )
{
	DataEdit::BuildKontextMenu( pMenu );
	aEdit.BuildKontextMenu( pMenu );
}


