/**************************************************************
 * 
 * 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_basctl.hxx"

#include <vector>
#define _BASIC_TEXTPORTIONS
#include <basic/sbdef.hxx>
#include <ide_pch.hxx>


#include <tools/urlobj.hxx>
#include <unotools/charclass.hxx>
#include <svl/urihelper.hxx>
#include <basic/sbx.hxx>
#include <vcl/sound.hxx>
#include <svtools/xtextedt.hxx>
#include <svtools/txtattr.hxx>
#include <svtools/textwindowpeer.hxx>
#include <basic/sbuno.hxx>

#include <helpid.hrc>
#include <baside2.hrc>
#include <baside2.hxx>
#include <brkdlg.hxx>
#include <objdlg.hxx>
#include <basobj.hxx>
#include <iderdll.hxx>
#include <iderdll2.hxx>
#include <vcl/taskpanelist.hxx>
#include <vcl/help.hxx>

//#ifndef _SFX_HELP_HXX //autogen
//#include <sfx2/sfxhelp.hxx>
//#endif
#include <unotools/sourceviewconfig.hxx>

#ifndef _COM_SUN_STAR_SCRIPT_XLIBRYARYCONTAINER2_HPP_
#include <com/sun/star/script/XLibraryContainer2.hpp>
#endif
#include <comphelper/processfactory.hxx>


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


long nVirtToolBoxHeight;	// wird im WatchWindow init., im Stackwindow verw.
long nHeaderBarHeight;

#define SCROLL_LINE		12
#define SCROLL_PAGE		60

#define DWBORDER		3

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

MapUnit eEditMapUnit = MAP_100TH_MM;


// #108672 Helper functions to get/set text in TextEngine
// using the stream interface (get/setText() only supports
// tools Strings limited to 64K).
::rtl::OUString getTextEngineText( ExtTextEngine* pEngine )
{
    SvMemoryStream aMemStream;
    aMemStream.SetStreamCharSet( RTL_TEXTENCODING_UTF8 );
    aMemStream.SetLineDelimiter( LINEEND_LF );
    pEngine->Write( aMemStream );
    sal_uLong nSize = aMemStream.Tell();
    ::rtl::OUString aText( (const sal_Char*)aMemStream.GetData(),
        nSize, RTL_TEXTENCODING_UTF8 );
    return aText;
}

void setTextEngineText( ExtTextEngine* pEngine, const ::rtl::OUString aStr )
{
    pEngine->SetText( String() );
    ::rtl::OString aUTF8Str = ::rtl::OUStringToOString( aStr, RTL_TEXTENCODING_UTF8 );
	SvMemoryStream aMemStream( (void*)aUTF8Str.getStr(), aUTF8Str.getLength(),
        STREAM_READ | STREAM_SEEK_TO_BEGIN );
    aMemStream.SetStreamCharSet( RTL_TEXTENCODING_UTF8 );
    aMemStream.SetLineDelimiter( LINEEND_LF );
	pEngine->Read( aMemStream );
}

void lcl_DrawIDEWindowFrame( DockingWindow* pWin )
{
    // The result of using explicit colors here appears to be harmless when
    // switching to high contrast mode:
	if ( !pWin->IsFloatingMode() )
	{
		Size aSz = pWin->GetOutputSizePixel();
		const Color aOldLineColor( pWin->GetLineColor() );
		pWin->SetLineColor( Color( COL_WHITE ) );
		// oben eine weisse..
		pWin->DrawLine( Point( 0, 0 ), Point( aSz.Width(), 0 ) );
		// unten eine schwarze...
		pWin->SetLineColor( Color( COL_BLACK ) );
		pWin->DrawLine( Point( 0, aSz.Height() - 1 ), Point( aSz.Width(), aSz.Height() - 1 ) );
		pWin->SetLineColor( aOldLineColor );
	}
}

void lcl_SeparateNameAndIndex( const String& rVName, String& rVar, String& rIndex )
{
	rVar = rVName;
	rIndex.Erase();
	sal_uInt16 nIndexStart = rVar.Search( '(' );
	if ( nIndexStart != STRING_NOTFOUND )
	{
		sal_uInt16 nIndexEnd = rVar.Search( ')', nIndexStart );
		if ( nIndexStart != STRING_NOTFOUND )
		{
			rIndex = rVar.Copy( nIndexStart+1, nIndexEnd-nIndexStart-1 );
			rVar.Erase( nIndexStart );
			rVar.EraseTrailingChars();
			rIndex.EraseLeadingChars();
			rIndex.EraseTrailingChars();
		}
	}

	if ( rVar.Len() )
	{
		sal_uInt16 nLastChar = rVar.Len()-1;
		if ( strchr( cSuffixes, rVar.GetChar( nLastChar ) ) )
			rVar.Erase( nLastChar, 1 );
	}
	if ( rIndex.Len() )
	{
		sal_uInt16 nLastChar = rIndex.Len()-1;
		if ( strchr( cSuffixes, rIndex.GetChar( nLastChar ) ) )
			rIndex.Erase( nLastChar, 1 );
	}
}


EditorWindow::EditorWindow( Window* pParent ) :
	Window( pParent, WB_BORDER )
{
	bDoSyntaxHighlight = sal_True;
	bDelayHighlight = sal_True;
	pModulWindow = 0;
	pEditView = 0;
	pEditEngine = 0;
    pSourceViewConfig = new utl::SourceViewConfig;
	bHighlightning = sal_False;
	pProgress = 0;
	nCurTextWidth = 0;
	SetBackground(
        Wallpaper(GetSettings().GetStyleSettings().GetFieldColor()));
	SetPointer( Pointer( POINTER_TEXT ) );

	SetHelpId( HID_BASICIDE_EDITORWINDOW );
    pSourceViewConfig->AddListener(this);
}



__EXPORT EditorWindow::~EditorWindow()
{
    pSourceViewConfig->RemoveListener(this);
    delete pSourceViewConfig;

	aSyntaxIdleTimer.Stop();

	if ( pEditEngine )
	{
		EndListening( *pEditEngine );
		pEditEngine->RemoveView( pEditView );
//		pEditEngine->SetViewWin( 0 );
		delete pEditView;
		delete pEditEngine;
	}
}

String EditorWindow::GetWordAtCursor()
{
    String aWord;

    if ( pEditView )
    {
        TextEngine* pTextEngine = pEditView->GetTextEngine();
        if ( pTextEngine )
        {
            // check first, if the cursor is at a help URL
            const TextSelection& rSelection = pEditView->GetSelection();
            const TextPaM& rSelStart = rSelection.GetStart();
            const TextPaM& rSelEnd = rSelection.GetEnd();
            String aText = pTextEngine->GetText( rSelEnd.GetPara() );
            CharClass aClass( ::comphelper::getProcessServiceFactory() , Application::GetSettings().GetLocale() );
            xub_StrLen nSelStart = static_cast< xub_StrLen >( rSelStart.GetIndex() );
            xub_StrLen nSelEnd = static_cast< xub_StrLen >( rSelEnd.GetIndex() );
            xub_StrLen nLength = static_cast< xub_StrLen >( aText.Len() );
            xub_StrLen nStart = 0;
            xub_StrLen nEnd = nLength;
            while ( nStart < nLength )
            {
                String aURL( URIHelper::FindFirstURLInText( aText, nStart, nEnd, aClass ) );
	            INetURLObject aURLObj( aURL );
                if ( aURLObj.GetProtocol() == INET_PROT_VND_SUN_STAR_HELP
                     && nSelStart >= nStart && nSelStart <= nEnd && nSelEnd >= nStart && nSelEnd <= nEnd )
                {
                    aWord = aURL;
                    break;
                }
                nStart = nEnd;
                nEnd = nLength;
            }

            // Nicht den Selektierten Bereich, sondern an der CursorPosition,
            // falls Teil eines Worts markiert.
            if ( !aWord.Len() )
                aWord = pTextEngine->GetWord( rSelEnd );

            // Kann leer sein, wenn komplettes Word markiert, da Cursor dahinter.
            if ( !aWord.Len() && pEditView->HasSelection() )
                aWord = pTextEngine->GetWord( rSelStart );
        }
    }

    return aWord;
}

void __EXPORT EditorWindow::RequestHelp( const HelpEvent& rHEvt )
{
    sal_Bool bDone = sal_False;

	// Sollte eigentlich mal aktiviert werden...
	if ( pEditEngine )
	{
        if ( rHEvt.GetMode() & HELPMODE_CONTEXT )
		{
			String aKeyword = GetWordAtCursor();
			Application::GetHelp()->SearchKeyword( aKeyword );
            bDone = sal_True;
        }
		else if ( rHEvt.GetMode() & HELPMODE_QUICK )
		{
			String aHelpText;
			Point aTopLeft;
			if ( StarBASIC::IsRunning() )
			{
				Point aWindowPos = rHEvt.GetMousePosPixel();
				aWindowPos = ScreenToOutputPixel( aWindowPos );
				Point aDocPos = GetEditView()->GetDocPos( aWindowPos );
				TextPaM aCursor = GetEditView()->GetTextEngine()->GetPaM( aDocPos, sal_False );
				TextPaM aStartOfWord;
				String aWord = GetEditView()->GetTextEngine()->GetWord( aCursor, &aStartOfWord );
				if ( aWord.Len() && !ByteString( aWord, RTL_TEXTENCODING_UTF8 ).IsNumericAscii() )
				{
					sal_uInt16 nLastChar =aWord.Len()-1;
					if ( strchr( cSuffixes, aWord.GetChar( nLastChar ) ) )
						aWord.Erase( nLastChar, 1 );
					SbxBase* pSBX = StarBASIC::FindSBXInCurrentScope( aWord );
					if ( pSBX && pSBX->ISA( SbxVariable ) && !pSBX->ISA( SbxMethod ) )
					{
						SbxVariable* pVar = (SbxVariable*)pSBX;
						SbxDataType eType = pVar->GetType();
						if ( (sal_uInt8)eType == (sal_uInt8)SbxOBJECT )
							// Kann zu Absturz, z.B. bei Selections-Objekt fuehren
							// Type == Object heisst nicht, dass pVar == Object!
							; // aHelpText = ((SbxObject*)pVar)->GetClassName();
						else if ( eType & SbxARRAY )
							; // aHelpText = "{...}";
						else if ( (sal_uInt8)eType != (sal_uInt8)SbxEMPTY )
						{
							aHelpText = pVar->GetName();
							if ( !aHelpText.Len() ) 	// Bei Uebergabeparametern wird der Name nicht kopiert
								aHelpText = aWord;
							aHelpText += '=';
							aHelpText += pVar->GetString();
						}
					}
					if ( aHelpText.Len() )
					{
						aTopLeft = GetEditView()->GetTextEngine()->PaMtoEditCursor( aStartOfWord ).BottomLeft();
						aTopLeft = GetEditView()->GetWindowPos( aTopLeft );
						aTopLeft.X() += 5;
						aTopLeft.Y() += 5;
						aTopLeft = OutputToScreenPixel( aTopLeft );
					}
				}
			}
			Help::ShowQuickHelp( this, Rectangle( aTopLeft, Size( 1, 1 ) ), aHelpText, QUICKHELP_TOP|QUICKHELP_LEFT);
            bDone = sal_True;
		}
	}

    if ( !bDone )
        Window::RequestHelp( rHEvt );
}


void __EXPORT EditorWindow::Resize()
{
	// ScrollBars, etc. passiert in Adjust...
	if ( pEditView )
	{
		long nVisY = pEditView->GetStartDocPos().Y();
//		pEditView->SetOutputArea( Rectangle( Point( 0, 0 ), GetOutputSize() ) );
		pEditView->ShowCursor();
		Size aOutSz( GetOutputSizePixel() );
		long nMaxVisAreaStart = pEditView->GetTextEngine()->GetTextHeight() - aOutSz.Height();
		if ( nMaxVisAreaStart < 0 )
			nMaxVisAreaStart = 0;
		if ( pEditView->GetStartDocPos().Y() > nMaxVisAreaStart )
		{
			Point aStartDocPos( pEditView->GetStartDocPos() );
			aStartDocPos.Y() = nMaxVisAreaStart;
			pEditView->SetStartDocPos( aStartDocPos );
			pEditView->ShowCursor();
			pModulWindow->GetBreakPointWindow().GetCurYOffset() = aStartDocPos.Y();
		}
		InitScrollBars();
		if ( nVisY != pEditView->GetStartDocPos().Y() )
			Invalidate();
	}
}



void __EXPORT EditorWindow::MouseMove( const MouseEvent &rEvt )
{
	if ( pEditView )
		pEditView->MouseMove( rEvt );
}



void __EXPORT EditorWindow::MouseButtonUp( const MouseEvent &rEvt )
{
	if ( pEditView )
	{
		pEditView->MouseButtonUp( rEvt );
        SfxBindings* pBindings = BasicIDE::GetBindingsPtr();
        if ( pBindings )
        {
            pBindings->Invalidate( SID_COPY );
            pBindings->Invalidate( SID_CUT );
            pBindings->Invalidate( SID_BASICIDE_STAT_POS );
        }
	}
}

void __EXPORT EditorWindow::MouseButtonDown( const MouseEvent &rEvt )
{
	GrabFocus();
	if ( pEditView )
	{
		pEditView->MouseButtonDown( rEvt );
	}
}

void __EXPORT EditorWindow::Command( const CommandEvent& rCEvt )
{
	if ( pEditView )
	{
		pEditView->Command( rCEvt );
		if ( ( rCEvt.GetCommand() == COMMAND_WHEEL ) ||
			 ( rCEvt.GetCommand() == COMMAND_STARTAUTOSCROLL ) ||
			 ( rCEvt.GetCommand() == COMMAND_AUTOSCROLL ) )
		{
			HandleScrollCommand( rCEvt, pModulWindow->GetHScrollBar(), &pModulWindow->GetEditVScrollBar() );
		}
	}
}

sal_Bool EditorWindow::ImpCanModify()
{
	sal_Bool bCanModify = sal_True;
	if ( StarBASIC::IsRunning() )
	{
		// Wenn im Trace-Mode, entweder Trace abbrechen oder
		// Eingabe verweigern
		// Im Notify bei Basic::Stoped die Markierungen in den Modulen
		// entfernen!
		if ( QueryBox( 0, WB_OK_CANCEL, String( IDEResId( RID_STR_WILLSTOPPRG ) ) ).Execute() == RET_OK )
		{
			pModulWindow->GetBasicStatus().bIsRunning = sal_False;
			BasicIDE::StopBasic();
		}
		else
			bCanModify = sal_False;
	}
	return bCanModify;
}

void __EXPORT EditorWindow::KeyInput( const KeyEvent& rKEvt )
{
	if ( !pEditView )	// Passiert unter W95 bei letzte Version, Ctrl-Tab
		return;

#if OSL_DEBUG_LEVEL > 1
    Range aRange = pModulWindow->GetHScrollBar()->GetRange(); (void)aRange;
    long nVisSz = pModulWindow->GetHScrollBar()->GetVisibleSize(); (void)nVisSz;
    long nPapSz = pModulWindow->GetHScrollBar()->GetPageSize(); (void)nPapSz;
    long nLinSz = pModulWindow->GetHScrollBar()->GetLineSize(); (void)nLinSz;
    long nThumb = pModulWindow->GetHScrollBar()->GetThumbPos(); (void)nThumb;
#endif
	sal_Bool bDone = sal_False;
	sal_Bool bWasModified = pEditEngine->IsModified();
	if ( !TextEngine::DoesKeyChangeText( rKEvt ) || ImpCanModify() )
	{
		if ( ( rKEvt.GetKeyCode().GetCode() == KEY_A) && rKEvt.GetKeyCode().IsMod1() )
			pEditView->SetSelection( TextSelection( TextPaM( 0, 0 ), TextPaM( 0xFFFFFFFF, 0xFFFF ) ) );
		else if ( ( rKEvt.GetKeyCode().GetCode() == KEY_Y ) && rKEvt.GetKeyCode().IsMod1() )
			bDone = sal_True; // CTRL-Y schlucken, damit kein Vorlagenkatalog
		else
		{
			if ( ( rKEvt.GetKeyCode().GetCode() == KEY_TAB ) && !rKEvt.GetKeyCode().IsMod1() &&
				  !rKEvt.GetKeyCode().IsMod2() && !GetEditView()->IsReadOnly() )
			{
				TextSelection aSel( pEditView->GetSelection() );
				if ( aSel.GetStart().GetPara() != aSel.GetEnd().GetPara() )
				{
					bDelayHighlight = sal_False;
					if ( !rKEvt.GetKeyCode().IsShift() )
						pEditView->IndentBlock();
					else
						pEditView->UnindentBlock();
					bDelayHighlight = sal_True;
					bDone = sal_True;
				}
			}
			if ( !bDone )
				bDone = pEditView->KeyInput( rKEvt );
		}
	}
	if ( !bDone )
	{
		if ( !SfxViewShell::Current()->KeyInput( rKEvt ) )
			Window::KeyInput( rKEvt );
	}
	else
	{
        SfxBindings* pBindings = BasicIDE::GetBindingsPtr();
        if ( pBindings )
        {
            pBindings->Invalidate( SID_CUT );
            pBindings->Invalidate( SID_COPY );
            pBindings->Invalidate( SID_BASICIDE_STAT_POS );

            if ( rKEvt.GetKeyCode().GetGroup() == KEYGROUP_CURSOR )
                pBindings->Update( SID_BASICIDE_STAT_POS );

            if ( !bWasModified && pEditEngine->IsModified() )
            {
                pBindings->Invalidate( SID_SAVEDOC );
                pBindings->Invalidate( SID_DOC_MODIFIED );
                pBindings->Invalidate( SID_UNDO );
            }

            if ( rKEvt.GetKeyCode().GetCode() == KEY_INSERT )
                pBindings->Invalidate( SID_ATTR_INSERT );
        }
	}
}

void __EXPORT EditorWindow::Paint( const Rectangle& rRect )
{
	if ( !pEditEngine )		// spaetestens jetzt brauche ich sie...
		CreateEditEngine();

	pEditView->Paint( rRect );
}

void __EXPORT EditorWindow::LoseFocus()
{
	SetSourceInBasic();
	Window::LoseFocus();
}

sal_Bool EditorWindow::SetSourceInBasic( sal_Bool bQuiet )
{
	(void)bQuiet;

	sal_Bool bChanged = sal_False;
	if ( pEditEngine && pEditEngine->IsModified()
		&& !GetEditView()->IsReadOnly() )	// Added because of #i60626, otherwise
			// any read only bug in the text engine could lead to a crash later
	{
        if ( !StarBASIC::IsRunning() ) // Nicht zur Laufzeit!
		{
            ::rtl::OUString aModule = getTextEngineText( pEditEngine );

            // update module in basic
#ifdef DBG_UTIL
            SbModule* pModule = pModulWindow->GetSbModule();
#endif
            DBG_ASSERT(pModule, "EditorWindow::SetSourceInBasic: No Module found!");

            // update module in module window
            pModulWindow->SetModule( aModule );

            // update module in library
            ScriptDocument aDocument( pModulWindow->GetDocument() );
            String aLibName = pModulWindow->GetLibName();
	        String aName = pModulWindow->GetName();
            OSL_VERIFY( aDocument.updateModule( aLibName, aName, aModule ) );

            pEditEngine->SetModified( sal_False );
            BasicIDE::MarkDocumentModified( aDocument );
            bChanged = sal_True;
		}
	}
	return bChanged;
}


// Returns the position of the last character of any of the following
// EOL char combinations: CR, CR/LF, LF, return -1 if no EOL is found
sal_Int32 searchEOL( const ::rtl::OUString& rStr, sal_Int32 fromIndex )
{
	sal_Int32 iRetPos = -1;

	sal_Int32 iLF = rStr.indexOf( LINE_SEP, fromIndex );
	if( iLF != -1 )
	{
		iRetPos = iLF;
	}
	else
	{
		iRetPos = rStr.indexOf( LINE_SEP_CR, fromIndex );
	}
	return iRetPos;
}


void EditorWindow::CreateEditEngine()
{
	if ( pEditEngine )
		return;

	pEditEngine = new ExtTextEngine;
	pEditView = new ExtTextView( pEditEngine, this );
	pEditView->SetAutoIndentMode( sal_True );
	pEditEngine->SetUpdateMode( sal_False );
	pEditEngine->InsertView( pEditView );

	ImplSetFont();

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

	aHighlighter.initialize( HIGHLIGHT_BASIC );

	sal_Bool bWasDoSyntaxHighlight = bDoSyntaxHighlight;
	bDoSyntaxHighlight = sal_False;	// Bei grossen Texten zu langsam...
    ::rtl::OUString aOUSource( pModulWindow->GetModule() );
	sal_Int32 nLines = 0;
    sal_Int32 nIndex = -1;
    do
    {
        nLines++;
		nIndex = searchEOL( aOUSource, nIndex+1 );
    }
    while ( nIndex >= 0 );

	// nLines*4: SetText+Formatting+DoHighlight+Formatting
	// 1 Formatting koennte eingespart werden, aber dann wartet man
	// bei einem langen Sourcecode noch laenger auf den Text...
	pProgress = new ProgressInfo( IDE_DLL()->GetShell()->GetViewFrame()->GetObjectShell(), String( IDEResId( RID_STR_GENERATESOURCE ) ), nLines*4 );
    setTextEngineText( pEditEngine, aOUSource );

	pEditView->SetStartDocPos( Point( 0, 0 ) );
	pEditView->SetSelection( TextSelection() );
	pModulWindow->GetBreakPointWindow().GetCurYOffset() = 0;
	pEditEngine->SetUpdateMode( sal_True );
	Update();	// Es wurde bei UpdateMode = sal_True nur Invalidiert

	// Die anderen Fenster auch, damit keine halben Sachen auf dem Bildschirm!
	pModulWindow->GetLayout()->GetWatchWindow().Update();
	pModulWindow->GetLayout()->GetStackWindow().Update();
	pModulWindow->GetBreakPointWindow().Update();

	pEditView->ShowCursor( sal_True, sal_True );

	StartListening( *pEditEngine );

	// Das Syntax-Highlightning legt ein rel. groesse VDev an.
	aSyntaxIdleTimer.Stop();
	bDoSyntaxHighlight = bWasDoSyntaxHighlight;


	for ( sal_uInt16 nLine = 0; nLine < nLines; nLine++ )
		aSyntaxLineTable.Insert( nLine, (void*)(sal_uInt16)1 );
	ForceSyntaxTimeout();

	DELETEZ( pProgress );

	pEditView->EraseVirtualDevice();
	pEditEngine->SetModified( sal_False );
	pEditEngine->EnableUndo( sal_True );

	InitScrollBars();

    SfxBindings* pBindings = BasicIDE::GetBindingsPtr();
    if ( pBindings )
        pBindings->Invalidate( SID_BASICIDE_STAT_POS );

	DBG_ASSERT( pModulWindow->GetBreakPointWindow().GetCurYOffset() == 0, "CreateEditEngine: Brechpunkte verschoben?" );

    // set readonly mode for readonly libraries
    ScriptDocument aDocument( pModulWindow->GetDocument() );
    ::rtl::OUString aOULibName( pModulWindow->GetLibName() );
    Reference< script::XLibraryContainer2 > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY );
    if ( xModLibContainer.is() && xModLibContainer->hasByName( aOULibName ) && xModLibContainer->isLibraryReadOnly( aOULibName ) )
    {
        pModulWindow->SetReadOnly( sal_True );
    }

    if ( aDocument.isDocument() && aDocument.isReadOnly() )
		pModulWindow->SetReadOnly( sal_True );
}

// virtual
void EditorWindow::DataChanged(DataChangedEvent const & rDCEvt)
{
    Window::DataChanged(rDCEvt);
    if (rDCEvt.GetType() == DATACHANGED_SETTINGS
        && (rDCEvt.GetFlags() & SETTINGS_STYLE) != 0)
    {
        Color aColor(GetSettings().GetStyleSettings().GetFieldColor());
        if (aColor
            != rDCEvt.GetOldSettings()->GetStyleSettings().GetFieldColor())
        {
            SetBackground(Wallpaper(aColor));
            Invalidate();
        }
        if (pEditEngine != 0)
        {
            aColor = GetSettings().GetStyleSettings().GetFieldTextColor();
            if (aColor != rDCEvt.GetOldSettings()->
                GetStyleSettings().GetFieldTextColor())
            {
                Font aFont(pEditEngine->GetFont());
                aFont.SetColor(aColor);
                pEditEngine->SetFont(aFont);
            }
        }
    }
}

void EditorWindow::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
{
	if ( rHint.ISA( TextHint ) )
	{
		const TextHint& rTextHint = (const TextHint&)rHint;
		if( rTextHint.GetId() == TEXT_HINT_VIEWSCROLLED )
		{
			if ( pModulWindow->GetHScrollBar() )
				pModulWindow->GetHScrollBar()->SetThumbPos( pEditView->GetStartDocPos().X() );
			pModulWindow->GetEditVScrollBar().SetThumbPos( pEditView->GetStartDocPos().Y() );
			pModulWindow->GetBreakPointWindow().DoScroll
				( 0, pModulWindow->GetBreakPointWindow().GetCurYOffset() - pEditView->GetStartDocPos().Y() );
		}
		else if( rTextHint.GetId() == TEXT_HINT_TEXTHEIGHTCHANGED )
		{
			if ( pEditView->GetStartDocPos().Y() )
			{
				long nOutHeight = GetOutputSizePixel().Height();
				long nTextHeight = pEditEngine->GetTextHeight();
				if ( nTextHeight < nOutHeight )
					pEditView->Scroll( 0, pEditView->GetStartDocPos().Y() );
			}

			SetScrollBarRanges();
		}
		else if( rTextHint.GetId() == TEXT_HINT_TEXTFORMATTED )
		{
			if ( pModulWindow->GetHScrollBar() )
			{
				sal_uLong nWidth = pEditEngine->CalcTextWidth();
				if ( (long)nWidth != nCurTextWidth )
				{
					nCurTextWidth = nWidth;
					pModulWindow->GetHScrollBar()->SetRange( Range( 0, (long)nCurTextWidth-1) );
					pModulWindow->GetHScrollBar()->SetThumbPos( pEditView->GetStartDocPos().X() );
				}
			}
			long nPrevTextWidth = nCurTextWidth;
			nCurTextWidth = pEditEngine->CalcTextWidth();
			if ( nCurTextWidth != nPrevTextWidth )
				SetScrollBarRanges();
		}
		else if( rTextHint.GetId() == TEXT_HINT_PARAINSERTED )
		{
			ParagraphInsertedDeleted( rTextHint.GetValue(), sal_True );
			DoDelayedSyntaxHighlight( rTextHint.GetValue() );
		}
		else if( rTextHint.GetId() == TEXT_HINT_PARAREMOVED )
		{
			ParagraphInsertedDeleted( rTextHint.GetValue(), sal_False );
		}
		else if( rTextHint.GetId() == TEXT_HINT_PARACONTENTCHANGED )
		{
			DoDelayedSyntaxHighlight( rTextHint.GetValue() );
		}
	}
}

void EditorWindow::ConfigurationChanged( utl::ConfigurationBroadcaster*, sal_uInt32 )
{
    ImplSetFont();
}

void EditorWindow::SetScrollBarRanges()
{
	// Extra-Methode, nicht InitScrollBars, da auch fuer EditEngine-Events.
	if ( !pEditEngine )
		return;

	if ( pModulWindow->GetHScrollBar() )
		pModulWindow->GetHScrollBar()->SetRange( Range( 0, nCurTextWidth-1 ) );

	pModulWindow->GetEditVScrollBar().SetRange( Range( 0, pEditEngine->GetTextHeight()-1 ) );
}

void EditorWindow::InitScrollBars()
{
	if ( !pEditEngine )
		return;

	SetScrollBarRanges();
	Size aOutSz( GetOutputSizePixel() );
	pModulWindow->GetEditVScrollBar().SetVisibleSize( aOutSz.Height() );
	pModulWindow->GetEditVScrollBar().SetPageSize( aOutSz.Height() * 8 / 10 );
	pModulWindow->GetEditVScrollBar().SetLineSize( GetTextHeight() );
	pModulWindow->GetEditVScrollBar().SetThumbPos( pEditView->GetStartDocPos().Y() );
	pModulWindow->GetEditVScrollBar().Show();

	if ( pModulWindow->GetHScrollBar() )
	{
		pModulWindow->GetHScrollBar()->SetVisibleSize( aOutSz.Width() );
		pModulWindow->GetHScrollBar()->SetPageSize( aOutSz.Width() * 8 / 10 );
		pModulWindow->GetHScrollBar()->SetLineSize( GetTextWidth( 'x' ) );
		pModulWindow->GetHScrollBar()->SetThumbPos( pEditView->GetStartDocPos().X() );
		pModulWindow->GetHScrollBar()->Show();
	}
}

void EditorWindow::ImpDoHighlight( sal_uLong nLine )
{
	if ( bDoSyntaxHighlight )
	{
		String aLine( pEditEngine->GetText( nLine ) );
		Range aChanges = aHighlighter.notifyChange( nLine, 0, &aLine, 1 );
		if ( aChanges.Len() )
		{
			for ( long n = aChanges.Min() + 1; n <= aChanges.Max(); n++ )
				aSyntaxLineTable.Insert( n, (void*)(sal_uLong)1 );
			aSyntaxIdleTimer.Start();
		}

		sal_Bool bWasModified = pEditEngine->IsModified();
		pEditEngine->RemoveAttribs( nLine, sal_True );
		HighlightPortions aPortions;
		aHighlighter.getHighlightPortions( nLine, aLine, aPortions );

		for ( size_t i = 0; i < aPortions.size(); i++ )
		{
			HighlightPortion& r = aPortions[i];
			const Color& rColor = ((ModulWindowLayout*)pModulWindow->GetLayoutWindow())->getSyntaxColor(r.tokenType);
			pEditEngine->SetAttrib( TextAttribFontColor( rColor ), nLine, r.nBegin, r.nEnd, sal_True );
		}

		// Das Highlighten soll kein Modify setzen
		pEditEngine->SetModified( bWasModified );
	}
}

void EditorWindow::ImplSetFont()
{
    if ( pSourceViewConfig )
	{
		String sFontName = pSourceViewConfig->GetFontName();
		if ( !sFontName.Len() )
		{
			Font aTmpFont( OutputDevice::GetDefaultFont( DEFAULTFONT_FIXED, Application::GetSettings().GetUILanguage(), 0 , this ) );
			sFontName = aTmpFont.GetName();
		}
		Size aFontSize( 0, pSourceViewConfig->GetFontHeight() );
		Font aFont( sFontName, aFontSize );
		aFont.SetColor( GetSettings().GetStyleSettings().GetFieldTextColor() );
		SetPointFont( aFont );
		aFont = GetFont();

		if ( pModulWindow )
			pModulWindow->GetBreakPointWindow().SetFont( aFont );

		if ( pEditEngine )
		{
			sal_Bool bModified = pEditEngine->IsModified();
			pEditEngine->SetFont( aFont );
			pEditEngine->SetModified( bModified );
		}
	}
}

void EditorWindow::DoSyntaxHighlight( sal_uLong nPara )
{
	// Durch das DelayedSyntaxHighlight kann es passieren,
	// dass die Zeile nicht mehr existiert!
	if ( nPara < pEditEngine->GetParagraphCount() )
	{
		// leider weis ich nicht, ob genau diese Zeile Modified() ...
		if ( pProgress )
			pProgress->StepProgress();
		ImpDoHighlight( nPara );
	}
}

void EditorWindow::DoDelayedSyntaxHighlight( sal_uLong nPara )
{
	// Zeile wird nur in 'Liste' aufgenommen, im TimerHdl abgearbeitet.
	// => Nicht Absaetze manipulieren, waehrend EditEngine formatiert.
	if ( pProgress )
		pProgress->StepProgress();

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

IMPL_LINK( EditorWindow, SyntaxTimerHdl, Timer *, EMPTYARG )
{
	DBG_ASSERT( pEditView, "Noch keine View, aber Syntax-Highlight ?!" );

	sal_Bool bWasModified = pEditEngine->IsModified();
	// pEditEngine->SetUpdateMode( sal_False );

	bHighlightning = sal_True;
	sal_uInt16 nLine;
	void* p = aSyntaxLineTable.First();
	while ( p )
	{
		nLine = (sal_uInt16)aSyntaxLineTable.GetCurKey();
		DoSyntaxHighlight( nLine );
		p = aSyntaxLineTable.Next();
	}

    // MT: Removed, because of idle format now when set/remove attribs...
    // pEditView->SetAutoScroll( sal_False );  // #101043# Don't scroll because of syntax highlight
    // pEditEngine->SetUpdateMode( sal_True );
	// pEditView->ShowCursor( sal_False, sal_True );
    // pEditView->SetAutoScroll( sal_True );

    // #i45572#
    if ( pEditView )
        pEditView->ShowCursor( sal_False, sal_True );

	pEditEngine->SetModified( bWasModified );

	aSyntaxLineTable.Clear();
	bHighlightning = sal_False;

	return 0;
}

void EditorWindow::ParagraphInsertedDeleted( sal_uLong nPara, sal_Bool bInserted )
{
	if ( pProgress )
		pProgress->StepProgress();

	if ( !bInserted && ( nPara == TEXT_PARA_ALL ) )
	{
		pModulWindow->GetBreakPoints().reset();
		pModulWindow->GetBreakPointWindow().Invalidate();
		aHighlighter.initialize( HIGHLIGHT_BASIC );
	}
	else
	{
		// Brechpunkte Aktualisieren...
		// keine Sonderbehandlung fuer EditEngine-CTOR ( Erste-Zeile-Problem ),
		// da in diesem Moment noch keine BreakPoints.
		// +1: Basic-Zeilen beginnen bei 1!
		pModulWindow->GetBreakPoints().AdjustBreakPoints( (sal_uInt16)nPara+1, bInserted );

		// Im BreakPointWindow invalidieren...
		long nLineHeight = GetTextHeight();
		Size aSz = pModulWindow->GetBreakPointWindow().GetOutputSize();
		Rectangle aInvRec( Point( 0, 0 ), aSz );
		long nY = nPara*nLineHeight - pModulWindow->GetBreakPointWindow().GetCurYOffset();
		aInvRec.Top() = nY;
		pModulWindow->GetBreakPointWindow().Invalidate( aInvRec );

		if ( bDoSyntaxHighlight )
		{
			String aDummy;
			aHighlighter.notifyChange( nPara, bInserted ? 1 : (-1), &aDummy, 1 );
		}
	}
}

void EditorWindow::CreateProgress( const String& rText, sal_uLong nRange )
{
	DBG_ASSERT( !pProgress, "ProgressInfo existiert schon" );
	pProgress = new ProgressInfo( IDE_DLL()->GetShell()->GetViewFrame()->GetObjectShell(), rText, nRange );
}

void EditorWindow::DestroyProgress()
{
	DELETEZ( pProgress );
}

void EditorWindow::ForceSyntaxTimeout()
{
	aSyntaxIdleTimer.Stop();
	((Link&)aSyntaxIdleTimer.GetTimeoutHdl()).Call( &aSyntaxIdleTimer );
}



BreakPointWindow::BreakPointWindow( Window* pParent ) :
	Window( pParent, WB_BORDER )
{
	pModulWindow = 0;
	nCurYOffset = 0;
    setBackgroundColor(GetSettings().GetStyleSettings().GetFieldColor());
    m_bHighContrastMode = GetSettings().GetStyleSettings().GetHighContrastMode();
	nMarkerPos = MARKER_NOMARKER;

	// nCurYOffset merken und nicht von EditEngine holen.
	// Falls in EditEngine autom. gescrollt wurde, wuesste ich sonst nicht,
	// wo ich gerade stehe.

	SetHelpId( HID_BASICIDE_BREAKPOINTWINDOW );
}



__EXPORT BreakPointWindow::~BreakPointWindow()
{
}



void __EXPORT BreakPointWindow::Resize()
{
///	Invalidate();
}



void __EXPORT BreakPointWindow::Paint( const Rectangle& )
{
	if ( SyncYOffset() )
		return;

	Size aOutSz( GetOutputSize() );
	long nLineHeight = GetTextHeight();

	Image aBrk1(((ModulWindowLayout *) pModulWindow->GetLayoutWindow())->
                getImage(IMGID_BRKENABLED, m_bHighContrastMode));
	Image aBrk0(((ModulWindowLayout *) pModulWindow->GetLayoutWindow())->
                getImage(IMGID_BRKDISABLED, m_bHighContrastMode));
	Size aBmpSz( aBrk1.GetSizePixel() );
	aBmpSz = PixelToLogic( aBmpSz );
	Point aBmpOff( 0, 0 );
	aBmpOff.X() = ( aOutSz.Width() - aBmpSz.Width() ) / 2;
	aBmpOff.Y() = ( nLineHeight - aBmpSz.Height() ) / 2;

	BreakPoint* pBrk = GetBreakPoints().First();
	while ( pBrk )
	{
		sal_uLong nLine = pBrk->nLine-1;
		sal_uLong nY = nLine*nLineHeight - nCurYOffset;
		DrawImage( Point( 0, nY ) + aBmpOff, pBrk->bEnabled ? aBrk1 : aBrk0 );
		pBrk = GetBreakPoints().Next();
	}
	ShowMarker( sal_True );
}



void BreakPointWindow::DoScroll( long nHorzScroll, long nVertScroll )
{
	nCurYOffset -= nVertScroll;
	Window::Scroll( nHorzScroll, nVertScroll );
}



void BreakPointWindow::SetMarkerPos( sal_uInt16 nLine, sal_Bool bError )
{
	if ( SyncYOffset() )
		Update();

	ShowMarker( sal_False );	// Alten wegzeichen...
	nMarkerPos = nLine;
	bErrorMarker = bError;
	ShowMarker( sal_True );		// Neuen zeichnen...
}

void BreakPointWindow::ShowMarker( sal_Bool bShow )
{
	if ( nMarkerPos == MARKER_NOMARKER )
		return;

	Size aOutSz( GetOutputSize() );
	long nLineHeight = GetTextHeight();

	Image aMarker(((ModulWindowLayout*)pModulWindow->GetLayoutWindow())->
                  getImage(bErrorMarker
                           ? IMGID_ERRORMARKER : IMGID_STEPMARKER,
                           m_bHighContrastMode));

	Size aMarkerSz( aMarker.GetSizePixel() );
	aMarkerSz = PixelToLogic( aMarkerSz );
	Point aMarkerOff( 0, 0 );
	aMarkerOff.X() = ( aOutSz.Width() - aMarkerSz.Width() ) / 2;
	aMarkerOff.Y() = ( nLineHeight - aMarkerSz.Height() ) / 2;

	sal_uLong nY = nMarkerPos*nLineHeight - nCurYOffset;
	Point aPos( 0, nY );
	aPos += aMarkerOff;
	if ( bShow )
		DrawImage( aPos, aMarker );
	else
		Invalidate( Rectangle( aPos, aMarkerSz ) );
}




BreakPoint*	BreakPointWindow::FindBreakPoint( const Point& rMousePos )
{
	long nLineHeight = GetTextHeight();
	long nYPos = rMousePos.Y() + nCurYOffset;
//	Image aBrk( ((ModulWindowLayout*)pModulWindow->GetLayoutWindow())->GetImage( IMGID_BRKENABLED ) );
//	Size aBmpSz( aBrk.GetSizePixel() );
//	aBmpSz = PixelToLogic( aBmpSz );

	BreakPoint* pBrk = GetBreakPoints().First();
	while ( pBrk )
	{
		sal_uLong nLine = pBrk->nLine-1;
		long nY = nLine*nLineHeight;
		if ( ( nYPos > nY ) && ( nYPos < ( nY + nLineHeight ) ) )
			return pBrk;
		pBrk = GetBreakPoints().Next();
	}
	return 0;
}

void __EXPORT BreakPointWindow::MouseButtonDown( const MouseEvent& rMEvt )
{
	if ( rMEvt.GetClicks() == 2 )
	{
		Point aMousePos( PixelToLogic( rMEvt.GetPosPixel() ) );
		long nLineHeight = GetTextHeight();
		long nYPos = aMousePos.Y() + nCurYOffset;
		long nLine = nYPos / nLineHeight + 1;
		pModulWindow->ToggleBreakPoint( (sal_uLong)nLine );
		// vielleicht mal etwas genauer...
		Invalidate();
	}
}



void __EXPORT BreakPointWindow::Command( const CommandEvent& rCEvt )
{
	if ( rCEvt.GetCommand() == COMMAND_CONTEXTMENU )
	{
		Point aPos( rCEvt.IsMouseEvent() ? rCEvt.GetMousePosPixel() : Point(1,1) );
		Point aEventPos( PixelToLogic( aPos ) );
		BreakPoint* pBrk = rCEvt.IsMouseEvent() ? FindBreakPoint( aEventPos ) : 0;
		if ( pBrk )
		{
			// prueffen, ob Brechpunkt enabled....
			PopupMenu aBrkPropMenu( IDEResId( RID_POPUP_BRKPROPS ) );
			aBrkPropMenu.CheckItem( RID_ACTIV, pBrk->bEnabled );
			switch ( aBrkPropMenu.Execute( this, aPos ) )
			{
				case RID_ACTIV:
				{
					pBrk->bEnabled = pBrk->bEnabled ? sal_False : sal_True;
					pModulWindow->UpdateBreakPoint( *pBrk );
					Invalidate();
				}
				break;
				case RID_BRKPROPS:
				{
					BreakPointDialog aBrkDlg( this, GetBreakPoints() );
					aBrkDlg.SetCurrentBreakPoint( pBrk );
					aBrkDlg.Execute();
					Invalidate();
				}
				break;
			}
		}
		else
		{
			PopupMenu aBrkListMenu( IDEResId( RID_POPUP_BRKDLG ) );
			switch ( aBrkListMenu.Execute( this, aPos ) )
			{
				case RID_BRKDLG:
				{
					BreakPointDialog aBrkDlg( this, GetBreakPoints() );
					aBrkDlg.Execute();
					Invalidate();
				}
				break;
			}
		}
	}
}

sal_Bool BreakPointWindow::SyncYOffset()
{
	TextView* pView = pModulWindow->GetEditView();
	if ( pView )
	{
		long nViewYOffset = pView->GetStartDocPos().Y();
		if ( nCurYOffset != nViewYOffset )
		{
			nCurYOffset = nViewYOffset;
			Invalidate();
			return sal_True;
		}
	}
	return sal_False;
}

// virtual
void BreakPointWindow::DataChanged(DataChangedEvent const & rDCEvt)
{
    Window::DataChanged(rDCEvt);
    if (rDCEvt.GetType() == DATACHANGED_SETTINGS
        && (rDCEvt.GetFlags() & SETTINGS_STYLE) != 0)
    {
        Color aColor(GetSettings().GetStyleSettings().GetFieldColor());
        if (aColor
            != rDCEvt.GetOldSettings()->GetStyleSettings().GetFieldColor())
        {
            setBackgroundColor(aColor);
            m_bHighContrastMode = GetSettings().GetStyleSettings().GetHighContrastMode();
            Invalidate();
        }
    }
}

void BreakPointWindow::setBackgroundColor(Color aColor)
{
	SetBackground(Wallpaper(aColor));
}


const sal_uInt16 ITEM_ID_VARIABLE = 1;
const sal_uInt16 ITEM_ID_VALUE = 2;
const sal_uInt16 ITEM_ID_TYPE = 3;

WatchWindow::WatchWindow( Window* pParent ) :
	BasicDockingWindow( pParent ),
	aWatchStr( IDEResId( RID_STR_REMOVEWATCH ) ),
	aXEdit( this, IDEResId( RID_EDT_WATCHEDIT ) ),
	aRemoveWatchButton( this, IDEResId( RID_IMGBTN_REMOVEWATCH ) ),
	aTreeListBox( this, WB_BORDER | WB_3DLOOK | WB_HASBUTTONS | WB_HASLINES | WB_HSCROLL | WB_TABSTOP
								  | WB_HASLINESATROOT | WB_HASBUTTONSATROOT ),
	aHeaderBar( this, WB_BUTTONSTYLE | WB_BORDER )
{
	aXEdit.SetAccessibleName(String(IDEResId( RID_STR_WATCHNAME)));
	aTreeListBox.SetAccessibleName(String(IDEResId(RID_STR_WATCHNAME)));		
	
	nVirtToolBoxHeight = aXEdit.GetSizePixel().Height() + 7;
	nHeaderBarHeight = 16;

    aTreeListBox.SetHelpId(HID_BASICIDE_WATCHWINDOW_LIST);
	aTreeListBox.EnableInplaceEditing( sal_True );
	aTreeListBox.SetSelectHdl( LINK( this, WatchWindow, TreeListHdl ) );
	aTreeListBox.SetPosPixel( Point( DWBORDER, nVirtToolBoxHeight + nHeaderBarHeight ) );
	aTreeListBox.SetHighlightRange( 1, 5 );

	Point aPnt( DWBORDER, nVirtToolBoxHeight + 1 );
	aHeaderBar.SetPosPixel( aPnt );
	aHeaderBar.SetEndDragHdl( LINK( this, WatchWindow, implEndDragHdl ) );

	long nVarTabWidth = 220;
	long nValueTabWidth = 100;
	long nTypeTabWidth = 1250;
    aHeaderBar.InsertItem( ITEM_ID_VARIABLE, String( IDEResId( RID_STR_WATCHVARIABLE ) ), nVarTabWidth );
    aHeaderBar.InsertItem( ITEM_ID_VALUE, String( IDEResId( RID_STR_WATCHVALUE ) ), nValueTabWidth );
    aHeaderBar.InsertItem( ITEM_ID_TYPE, String( IDEResId( RID_STR_WATCHTYPE ) ), nTypeTabWidth );

    long tabs[ 4 ];
    tabs[ 0 ] = 3; // two tabs
    tabs[ 1 ] = 0;
    tabs[ 2 ] = nVarTabWidth;
    tabs[ 3 ] = nVarTabWidth + nValueTabWidth;
	aTreeListBox.SvHeaderTabListBox::SetTabs( tabs, MAP_PIXEL );
	aTreeListBox.InitHeaderBar( &aHeaderBar );

	aTreeListBox.SetNodeDefaultImages( );

	aHeaderBar.Show();

	aRemoveWatchButton.Disable();

	aTreeListBox.Show();

	long nTextLen = GetTextWidth( aWatchStr ) + DWBORDER;
	aXEdit.SetPosPixel( Point( nTextLen, 3 ) );
	aXEdit.SetAccHdl( LINK( this, WatchWindow, EditAccHdl ) );
	aXEdit.GetAccelerator().InsertItem( 1, KeyCode( KEY_RETURN ) );
	aXEdit.GetAccelerator().InsertItem( 2, KeyCode( KEY_ESCAPE ) );
	aXEdit.Show();

    aRemoveWatchButton.SetModeImage(Image(IDEResId(RID_IMG_REMOVEWATCH_HC)),
                                    BMP_COLOR_HIGHCONTRAST);
	aRemoveWatchButton.SetClickHdl( LINK( this, WatchWindow, ButtonHdl ) );
	aRemoveWatchButton.SetPosPixel( Point( nTextLen + aXEdit.GetSizePixel().Width() + 4, 2 ) );
	Size aSz( aRemoveWatchButton.GetModeImage().GetSizePixel() );
	aSz.Width() += 6;
	aSz.Height() += 6;
	aRemoveWatchButton.SetSizePixel( aSz );
	aRemoveWatchButton.Show();

	SetText( String( IDEResId( RID_STR_WATCHNAME ) ) );

	SetHelpId( HID_BASICIDE_WATCHWINDOW );

	// make watch window keyboard accessible
	GetSystemWindow()->GetTaskPaneList()->AddWindow( this );
}



__EXPORT WatchWindow::~WatchWindow()
{
	GetSystemWindow()->GetTaskPaneList()->RemoveWindow( this );
}



void __EXPORT WatchWindow::Paint( const Rectangle& )
{
	DrawText( Point( DWBORDER, 7 ), aWatchStr );
	lcl_DrawIDEWindowFrame( this );
}



void __EXPORT WatchWindow::Resize()
{
	Size aSz = GetOutputSizePixel();
	Size aBoxSz( aSz.Width() - 2*DWBORDER, aSz.Height() - nVirtToolBoxHeight - DWBORDER );

	if ( aBoxSz.Width() < 4 )	// < 4, weil noch Border...
		aBoxSz.Width() = 0;
	if ( aBoxSz.Height() < 4 )
		aBoxSz.Height() = 0;

	aBoxSz.Height() -= nHeaderBarHeight;
	aTreeListBox.SetSizePixel( aBoxSz );
    aTreeListBox.GetHScroll()->SetPageSize( aTreeListBox.GetHScroll()->GetVisibleSize() );

	aBoxSz.Height() = nHeaderBarHeight;
	aHeaderBar.SetSizePixel( aBoxSz );

	Invalidate();	//Wegen DrawLine im Paint...
}

struct MemberList
{
	String*			mpMemberNames;
	int				mnMemberCount;

	MemberList( void )
		: mpMemberNames( NULL )
		, mnMemberCount( 0 )
	{}
	~MemberList()
	{
		clear();
	}

	void clear( void );
	void allocList( int nCount );
};

void MemberList::clear( void )
{
	if( mnMemberCount )
	{
		delete[] mpMemberNames;
		mnMemberCount = 0;
	}
}

void MemberList::allocList( int nCount )
{
	clear();
	if( nCount > 0 )
	{
		mnMemberCount = nCount;
		mpMemberNames = new String[ mnMemberCount ];
	}
}

struct WatchItem
{
    String          maName;
    String          maDisplayName;
    SbxObjectRef    mpObject;
	MemberList		maMemberList;

    SbxDimArrayRef	mpArray;
	int				nDimLevel;	// 0 = Root
	int				nDimCount;
	short*			pIndices;

	WatchItem*		mpArrayParentItem;

    WatchItem( void )
		: nDimLevel( 0 )
		, nDimCount( 0 )
		, pIndices( NULL )
		, mpArrayParentItem( NULL )
    {}
    ~WatchItem()
		{ clearWatchItem(); }

	void clearWatchItem( bool bIncludeArrayData=true )
	{
		mpObject = NULL;
		maMemberList.clear();
		if( bIncludeArrayData )
		{
			mpArray = NULL;
			nDimLevel = 0;
			nDimCount = 0;
			delete[] pIndices;
			pIndices = NULL;
		}
	}

	WatchItem* GetRootItem( void );
	SbxDimArray* GetRootArray( void );
};

WatchItem* WatchItem::GetRootItem( void )
{
	WatchItem* pItem = mpArrayParentItem;
	while( pItem )
	{
		if( pItem->mpArray.Is() )
			break;
		pItem = pItem->mpArrayParentItem;
	}
	return pItem;
}

SbxDimArray* WatchItem::GetRootArray( void )
{
	WatchItem* pRootItem = GetRootItem();
	SbxDimArray* pRet = NULL;
	if( pRootItem )
		pRet = pRootItem->mpArray;
	return pRet;
}

void WatchWindow::AddWatch( const String& rVName )
{
	WatchItem* pWatchItem = new WatchItem;
	String aVar, aIndex;
	lcl_SeparateNameAndIndex( rVName, aVar, aIndex );
	pWatchItem->maName = aVar;

	String aWatchStr_( aVar );
	aWatchStr_ += String( RTL_CONSTASCII_USTRINGPARAM( "\t\t" ) );
	SvLBoxEntry* pNewEntry = aTreeListBox.InsertEntry( aWatchStr_, 0, sal_True, LIST_APPEND );
	pNewEntry->SetUserData( pWatchItem );

	aTreeListBox.Select( pNewEntry, sal_True );
	aTreeListBox.MakeVisible( pNewEntry );
	aRemoveWatchButton.Enable();
}

sal_Bool WatchWindow::RemoveSelectedWatch()
{
	SvLBoxEntry* pEntry = aTreeListBox.GetCurEntry();
	if ( pEntry )
	{
		aTreeListBox.GetModel()->Remove( pEntry );
		pEntry = aTreeListBox.GetCurEntry();
		if ( pEntry )
			aXEdit.SetText( ((WatchItem*)pEntry->GetUserData())->maName );
		else
			aXEdit.SetText( String() );
		if ( !aTreeListBox.GetEntryCount() )
			aRemoveWatchButton.Disable();
		return sal_True;
	}
	else
		return sal_False;
}


IMPL_LINK_INLINE_START( WatchWindow, ButtonHdl, ImageButton *, pButton )
{
	if ( pButton == &aRemoveWatchButton )
	{
        BasicIDEShell* pIDEShell = IDE_DLL()->GetShell();
        SfxViewFrame* pViewFrame = pIDEShell ? pIDEShell->GetViewFrame() : NULL;
	    SfxDispatcher* pDispatcher = pViewFrame ? pViewFrame->GetDispatcher() : NULL;
        if( pDispatcher )
		{
			pDispatcher->Execute( SID_BASICIDE_REMOVEWATCH );
		}
	}
	return 0;
}
IMPL_LINK_INLINE_END( WatchWindow, ButtonHdl, ImageButton *, pButton )



IMPL_LINK_INLINE_START( WatchWindow, TreeListHdl, SvTreeListBox *, EMPTYARG )
{
	SvLBoxEntry* pCurEntry = aTreeListBox.GetCurEntry();
	if ( pCurEntry && pCurEntry->GetUserData() )
		aXEdit.SetText( ((WatchItem*)pCurEntry->GetUserData())->maName );

	return 0;
}
IMPL_LINK_INLINE_END( WatchWindow, TreeListHdl, SvTreeListBox *, EMPTYARG )


IMPL_LINK_INLINE_START( WatchWindow, implEndDragHdl, HeaderBar *, pBar )
{
	(void)pBar;

    const sal_Int32 TAB_WIDTH_MIN = 10;
	sal_Int32 nMaxWidth =
        aHeaderBar.GetSizePixel().getWidth() - 2 * TAB_WIDTH_MIN;

    sal_Int32 nVariableWith = aHeaderBar.GetItemSize( ITEM_ID_VARIABLE );
    if( nVariableWith < TAB_WIDTH_MIN )
        aHeaderBar.SetItemSize( ITEM_ID_VARIABLE, TAB_WIDTH_MIN );
    else if( nVariableWith > nMaxWidth )
        aHeaderBar.SetItemSize( ITEM_ID_VARIABLE, nMaxWidth );

    sal_Int32 nValueWith = aHeaderBar.GetItemSize( ITEM_ID_VALUE );
    if( nValueWith < TAB_WIDTH_MIN )
        aHeaderBar.SetItemSize( ITEM_ID_VALUE, TAB_WIDTH_MIN );
    else if( nValueWith > nMaxWidth )
        aHeaderBar.SetItemSize( ITEM_ID_VALUE, nMaxWidth );

    if (aHeaderBar.GetItemSize( ITEM_ID_TYPE ) < TAB_WIDTH_MIN)
        aHeaderBar.SetItemSize( ITEM_ID_TYPE, TAB_WIDTH_MIN );

    sal_Int32 nPos = 0;
	sal_uInt16 nTabs = aHeaderBar.GetItemCount();
    // OSL_ASSERT( m_treelb->TabCount() == nTabs );
	for( sal_uInt16 i = 1 ; i < nTabs ; ++i )
	{
		nPos += aHeaderBar.GetItemSize( i );
		aTreeListBox.SetTab( i, nPos, MAP_PIXEL );
	}
	return 0;
}
IMPL_LINK_INLINE_END( WatchWindow, implEndDragHdl, HeaderBar *, pBar )


IMPL_LINK( WatchWindow, EditAccHdl, Accelerator *, pAcc )
{
	switch ( pAcc->GetCurKeyCode().GetCode() )
	{
		case KEY_RETURN:
		{
			String aCurText( aXEdit.GetText() );
			if ( aCurText.Len() )
			{
				AddWatch( aCurText );
				aXEdit.SetSelection( Selection( 0, 0xFFFF ) );
				UpdateWatches();
			}
			else
				Sound::Beep();
		}
		break;
		case KEY_ESCAPE:
		{
			aXEdit.SetText( String() );
		}
		break;
	}

	return 0;
}

void WatchWindow::UpdateWatches( bool bBasicStopped )
{
	aTreeListBox.UpdateWatches( bBasicStopped );
}


StackWindow::StackWindow( Window* pParent ) :
	BasicDockingWindow( pParent ),
	aTreeListBox( this, WB_BORDER | WB_3DLOOK | WB_HSCROLL | WB_TABSTOP ),
	aGotoCallButton( this, IDEResId( RID_IMGBTN_GOTOCALL ) ),
	aStackStr( IDEResId( RID_STR_STACK ) )
{
   	aTreeListBox.SetHelpId(HID_BASICIDE_STACKWINDOW_LIST);
	aTreeListBox.SetAccessibleName(String( IDEResId(RID_STR_STACKNAME)));		
	aTreeListBox.SetPosPixel( Point( DWBORDER, nVirtToolBoxHeight ) );
	aTreeListBox.SetHighlightRange();
    aTreeListBox.SetSelectionMode( NO_SELECTION );
    aTreeListBox.InsertEntry( String(), 0, sal_False, LIST_APPEND );
	aTreeListBox.Show();

	SetText( String( IDEResId( RID_STR_STACKNAME ) ) );

	SetHelpId( HID_BASICIDE_STACKWINDOW );

	aGotoCallButton.SetClickHdl( LINK( this, StackWindow, ButtonHdl ) );
	aGotoCallButton.SetPosPixel( Point( DWBORDER, 2 ) );
	Size aSz( aGotoCallButton.GetModeImage().GetSizePixel() );
	aSz.Width() += 6;
	aSz.Height() += 6;
	aGotoCallButton.SetSizePixel( aSz );
//	aGotoCallButton.Show();	// wird vom Basic noch nicht unterstuetzt!
	aGotoCallButton.Hide();

	// make stack window keyboard accessible
	GetSystemWindow()->GetTaskPaneList()->AddWindow( this );
}



__EXPORT StackWindow::~StackWindow()
{
	GetSystemWindow()->GetTaskPaneList()->RemoveWindow( this );
}



void __EXPORT StackWindow::Paint( const Rectangle& )
{
	DrawText( Point( DWBORDER, 7 ), aStackStr );
	lcl_DrawIDEWindowFrame( this );
}



void __EXPORT StackWindow::Resize()
{
	Size aSz = GetOutputSizePixel();
	Size aBoxSz( aSz.Width() - 2*DWBORDER, aSz.Height() - nVirtToolBoxHeight - DWBORDER );

	if ( aBoxSz.Width() < 4 )	// < 4, weil noch Border...
		aBoxSz.Width() = 0;
	if ( aBoxSz.Height() < 4 )
		aBoxSz.Height() = 0;

	aTreeListBox.SetSizePixel( aBoxSz );

	Invalidate();	//Wegen DrawLine im Paint...
}



IMPL_LINK_INLINE_START( StackWindow, ButtonHdl, ImageButton *, pButton )
{
	if ( pButton == &aGotoCallButton )
	{
        BasicIDEShell* pIDEShell = IDE_DLL()->GetShell();
        SfxViewFrame* pViewFrame = pIDEShell ? pIDEShell->GetViewFrame() : NULL;
	    SfxDispatcher* pDispatcher = pViewFrame ? pViewFrame->GetDispatcher() : NULL;
        if( pDispatcher )
		{
			pDispatcher->Execute( SID_BASICIDE_GOTOCALL );
		}
	}
	return 0;
}
IMPL_LINK_INLINE_END( StackWindow, ButtonHdl, ImageButton *, pButton )



void __EXPORT StackWindow::UpdateCalls()
{
	aTreeListBox.SetUpdateMode( sal_False );
	aTreeListBox.Clear();

    if ( StarBASIC::IsRunning() )
    {
	    SbxError eOld = SbxBase::GetError();
        aTreeListBox.SetSelectionMode( SINGLE_SELECTION );

	    sal_uInt16 nScope = 0;
	    SbMethod* pMethod = StarBASIC::GetActiveMethod( nScope );
	    while ( pMethod )
	    {
		    String aEntry( String::CreateFromInt32(nScope ));
		    if ( aEntry.Len() < 2 )
			    aEntry.Insert( ' ', 0 );
		    aEntry += String( RTL_CONSTASCII_USTRINGPARAM( ": " ) );
		    aEntry += pMethod->GetName();
		    SbxArray* pParams = pMethod->GetParameters();
		    SbxInfo* pInfo = pMethod->GetInfo();
		    if ( pParams )
		    {
			    aEntry += '(';
			    // 0 ist der Name der Sub...
			    for ( sal_uInt16 nParam = 1; nParam < pParams->Count(); nParam++ )
			    {
				    SbxVariable* pVar = pParams->Get( nParam );
				    DBG_ASSERT( pVar, "Parameter?!" );
				    if ( pVar->GetName().Len() )
					    aEntry += pVar->GetName();
				    else if ( pInfo )
				    {
					    const SbxParamInfo* pParam = pInfo->GetParam( nParam );
					    if ( pParam )
						    aEntry += pParam->aName;
				    }
				    aEntry += '=';
					SbxDataType eType = pVar->GetType();
	    			if( eType & SbxARRAY )
					    aEntry += String( RTL_CONSTASCII_USTRINGPARAM( "..." ) );
				    else if( eType != SbxOBJECT )
					    aEntry += pVar->GetString();
				    if ( nParam < ( pParams->Count() - 1 ) )
					    aEntry += String( RTL_CONSTASCII_USTRINGPARAM( ", " ) );
			    }
			    aEntry += ')';
		    }
		    aTreeListBox.InsertEntry( aEntry, 0, sal_False, LIST_APPEND );
		    nScope++;
		    pMethod = StarBASIC::GetActiveMethod( nScope );
	    }

	    SbxBase::ResetError();
	    if( eOld != SbxERR_OK )
		    SbxBase::SetError( eOld );
    }
    else
    {
        aTreeListBox.SetSelectionMode( NO_SELECTION );
        aTreeListBox.InsertEntry( String(), 0, sal_False, LIST_APPEND );
    }

	aTreeListBox.SetUpdateMode( sal_True );
}




ComplexEditorWindow::ComplexEditorWindow( ModulWindow* pParent ) :
	Window( pParent, WB_3DLOOK | WB_CLIPCHILDREN ),
	aBrkWindow( this ),
	aEdtWindow( this ),
	aEWVScrollBar( this, WB_VSCROLL | WB_DRAG )
{
	aEdtWindow.SetModulWindow( pParent );
	aBrkWindow.SetModulWindow( pParent );
	aEdtWindow.Show();
	aBrkWindow.Show();

	aEWVScrollBar.SetLineSize( SCROLL_LINE );
	aEWVScrollBar.SetPageSize( SCROLL_PAGE );
	aEWVScrollBar.SetScrollHdl( LINK( this, ComplexEditorWindow, ScrollHdl ) );
	aEWVScrollBar.Show();
}



void __EXPORT ComplexEditorWindow::Resize()
{
	Size aOutSz = GetOutputSizePixel();
	Size aSz( aOutSz );
	aSz.Width() -= 2*DWBORDER;
	aSz.Height() -= 2*DWBORDER;
	long nBrkWidth = 20;
	long nSBWidth = aEWVScrollBar.GetSizePixel().Width();

	Size aBrkSz( Size( nBrkWidth, aSz.Height() ) );
	aBrkWindow.SetPosSizePixel( Point( DWBORDER, DWBORDER ), aBrkSz );

    Size aEWSz( Size( aSz.Width() - nBrkWidth - nSBWidth + 2, aSz.Height() ) );
	aEdtWindow.SetPosSizePixel( Point( DWBORDER+aBrkSz.Width()-1, DWBORDER ), aEWSz );

	aEWVScrollBar.SetPosSizePixel( Point( aOutSz.Width()-DWBORDER-nSBWidth, DWBORDER ), Size( nSBWidth, aSz.Height() ) );

	// Macht das EditorWindow, ausserdem hier falsch, da Pixel
//	aEWVScrollBar.SetPageSize( aEWSz.Height() * 8 / 10 );
//	aEWVScrollBar.SetVisibleSize( aSz.Height() );
//	Invalidate();
}

IMPL_LINK( ComplexEditorWindow, ScrollHdl, ScrollBar *, pCurScrollBar )
{
	if ( aEdtWindow.GetEditView() )
	{
		DBG_ASSERT( pCurScrollBar == &aEWVScrollBar, "Wer scrollt hier ?" );
		long nDiff = aEdtWindow.GetEditView()->GetStartDocPos().Y() - pCurScrollBar->GetThumbPos();
		aEdtWindow.GetEditView()->Scroll( 0, nDiff );
		aBrkWindow.DoScroll( 0, nDiff );
		aEdtWindow.GetEditView()->ShowCursor( sal_False, sal_True );
		pCurScrollBar->SetThumbPos( aEdtWindow.GetEditView()->GetStartDocPos().Y() );
	}

	return 0;
}

// virtual
void ComplexEditorWindow::DataChanged(DataChangedEvent const & rDCEvt)
{
    Window::DataChanged(rDCEvt);
    if (rDCEvt.GetType() == DATACHANGED_SETTINGS
        && (rDCEvt.GetFlags() & SETTINGS_STYLE) != 0)
    {
        Color aColor(GetSettings().GetStyleSettings().GetFaceColor());
        if (aColor
            != rDCEvt.GetOldSettings()->GetStyleSettings().GetFaceColor())
        {
            SetBackground(Wallpaper(aColor));
            Invalidate();
        }
    }
}

// virtual
uno::Reference< awt::XWindowPeer >
EditorWindow::GetComponentInterface(sal_Bool bCreate)
{
    uno::Reference< awt::XWindowPeer > xPeer(
        Window::GetComponentInterface(false));
    if (!xPeer.is() && bCreate)
    {
        // Make sure edit engine and view are available:
        if (!pEditEngine)
            CreateEditEngine();

        xPeer = new ::svt::TextWindowPeer(*GetEditView());
        SetComponentInterface(xPeer);
    }
    return xPeer;
}

WatchTreeListBox::WatchTreeListBox( Window* pParent, WinBits nWinBits )
	: SvHeaderTabListBox( pParent, nWinBits )
{}

WatchTreeListBox::~WatchTreeListBox()
{
	// User-Daten zerstoeren...
	SvLBoxEntry* pEntry = First();
	while ( pEntry )
	{
		delete (WatchItem*)pEntry->GetUserData();
		pEntry = Next( pEntry );
	}
}

void WatchTreeListBox::SetTabs()
{
	SvHeaderTabListBox::SetTabs();
	sal_uInt16 nTabCount_ = aTabs.Count();
	for( sal_uInt16 i = 0 ; i < nTabCount_ ; i++ )
	{
		SvLBoxTab* pTab = (SvLBoxTab*)aTabs.GetObject(i);
		if( i == 2 )
			pTab->nFlags |= SV_LBOXTAB_EDITABLE;
		else
			pTab->nFlags &= ~SV_LBOXTAB_EDITABLE;
	}
}

void WatchTreeListBox::RequestingChilds( SvLBoxEntry * pParent )
{
	if( !StarBASIC::IsRunning() )
        return;

    if( GetChildCount( pParent ) > 0 )
        return;

	SvLBoxEntry * pEntry = pParent;
    WatchItem* pItem = (WatchItem*)pEntry->GetUserData();

	SbxDimArray* pArray = pItem->mpArray;
	SbxDimArray* pRootArray = pItem->GetRootArray();
	bool bArrayIsRootArray = false;
	if( !pArray && pRootArray )
	{
		pArray = pRootArray;
		bArrayIsRootArray = true;
	}

    SbxObject* pObj = pItem->mpObject;
    if( pObj )
    {
		createAllObjectProperties( pObj );
 	    SbxArray* pProps = pObj->GetProperties();
	    sal_uInt16 nPropCount = pProps->Count();
		pItem->maMemberList.allocList( nPropCount );

	    for( sal_uInt16 i = 0 ; i < nPropCount - 3 ; i++ )
	    {
		    SbxVariable* pVar = pProps->Get( i );

            String aName( pVar->GetName() );
			pItem->maMemberList.mpMemberNames[i] = aName;
            SvLBoxEntry* pChildEntry = SvTreeListBox::InsertEntry( aName, pEntry );
            WatchItem* pChildItem = new WatchItem();
			pChildItem->maName = aName;
	        pChildEntry->SetUserData( pChildItem );
	    }
		if( nPropCount > 0 )
		{
			UpdateWatches();
		}
    }
	else if( pArray )
	{
	    sal_uInt16 nElementCount = 0;

		// Loop through indices of current level
		int nParentLevel = bArrayIsRootArray ? pItem->nDimLevel : 0;
		int nThisLevel = nParentLevel + 1;
		sal_Int32 nMin, nMax;
		pArray->GetDim32( nThisLevel, nMin, nMax );
		for( sal_Int32 i = nMin ; i <= nMax ; i++ )
		{
            WatchItem* pChildItem = new WatchItem();

			// Copy data and create name
            String aBaseName( pItem->maName );
			pChildItem->maName = aBaseName;

			String aIndexStr = String( RTL_CONSTASCII_USTRINGPARAM( "(" ) );
			// pChildItem->mpArray = pItem->mpArray;
			pChildItem->mpArrayParentItem = pItem;
			pChildItem->nDimLevel = nThisLevel;
			pChildItem->nDimCount = pItem->nDimCount;
			pChildItem->pIndices = new short[ pChildItem->nDimCount ];
			sal_uInt16 j;
			for( j = 0 ; j < nParentLevel ; j++ )
			{
				short n = pChildItem->pIndices[j] = pItem->pIndices[j];
				aIndexStr += String::CreateFromInt32( n );
				aIndexStr += String( RTL_CONSTASCII_USTRINGPARAM( "," ) );
			}
			pChildItem->pIndices[ nParentLevel ] = sal::static_int_cast<short>( i );
			aIndexStr += String::CreateFromInt32( i );
			aIndexStr += String( RTL_CONSTASCII_USTRINGPARAM( ")" ) );

			String aDisplayName;
			WatchItem* pArrayRootItem = pChildItem->GetRootItem();
			if( pArrayRootItem && pArrayRootItem->mpArrayParentItem )
				aDisplayName = pItem->maDisplayName;
			else
				aDisplayName = aBaseName;
			aDisplayName += aIndexStr;
			pChildItem->maDisplayName = aDisplayName;

            SvLBoxEntry* pChildEntry = SvTreeListBox::InsertEntry( aDisplayName, pEntry );
			nElementCount++;
			pChildEntry->SetUserData( pChildItem );
		}
		if( nElementCount > 0 )
		{
			UpdateWatches();
		}
	}
}

SbxBase* WatchTreeListBox::ImplGetSBXForEntry( SvLBoxEntry* pEntry, bool& rbArrayElement )
{
	SbxBase* pSBX = NULL;
	rbArrayElement = false;

    WatchItem* pItem = (WatchItem*)pEntry->GetUserData();
	String aVName( pItem->maName );

    SvLBoxEntry* pParentEntry = GetParent( pEntry );
	WatchItem* pParentItem = pParentEntry ? (WatchItem*)pParentEntry->GetUserData() : NULL;
    if( pParentItem )
    {
        SbxObject* pObj = pParentItem->mpObject;
        SbxDimArray* pArray;
        if( pObj )
		{
    		pSBX = pObj->Find( aVName, SbxCLASS_DONTCARE );

			SbxVariable* pVar;
			if ( pSBX && (pVar = PTR_CAST( SbxVariable, pSBX )) != NULL
						&& !pSBX->ISA( SbxMethod ) )
			{
				// Force getting value
				SbxValues aRes;
				aRes.eType = SbxVOID;
				pVar->Get( aRes );
			}
		}
		// Array?
		else if( (pArray = pItem->GetRootArray()) != NULL )
		// else if( (pArray = pItem->mpArray) != NULL )
		{
			rbArrayElement = true;
			if( pParentItem->nDimLevel + 1 == pParentItem->nDimCount )
			// if( pItem->nDimLevel == pItem->nDimCount )
				pSBX = pArray->Get( pItem->pIndices );
			// else
				// pSBX = pArray;
		}
    }
	else
	{
		pSBX = StarBASIC::FindSBXInCurrentScope( aVName );
	}
	return pSBX;
}

sal_Bool __EXPORT WatchTreeListBox::EditingEntry( SvLBoxEntry* pEntry, Selection& )
{
    WatchItem* pItem = (WatchItem*)pEntry->GetUserData();

	sal_Bool bEdit = sal_False;
	if ( StarBASIC::IsRunning() && StarBASIC::GetActiveMethod() && !SbxBase::IsError() )
	{
		// No out of scope entries
		bool bArrayElement;
		SbxBase* pSBX = ImplGetSBXForEntry( pEntry, bArrayElement );
		if ( ( pSBX && pSBX->ISA( SbxVariable ) && !pSBX->ISA( SbxMethod ) ) || bArrayElement )
		{
			// Accept no objects and only end nodes of arrays for editing
			if( !pItem->mpObject && (pItem->mpArray == NULL || pItem->nDimLevel == pItem->nDimCount) )
			{
				aEditingRes = SvHeaderTabListBox::GetEntryText( pEntry, ITEM_ID_VALUE-1 );
				aEditingRes.EraseLeadingChars();
				aEditingRes.EraseTrailingChars();
				bEdit = sal_True;
			}
		}
	}

	if ( !bEdit )
		Sound::Beep();

	return bEdit;
}

sal_Bool __EXPORT WatchTreeListBox::EditedEntry( SvLBoxEntry* pEntry, const String& rNewText )
{
    WatchItem* pItem = (WatchItem*)pEntry->GetUserData();
	String aVName( pItem->maName );

	String aResult = rNewText;
	aResult.EraseLeadingChars();
	aResult.EraseTrailingChars();

	sal_uInt16 nResultLen = aResult.Len();
	sal_Unicode cFirst = aResult.GetChar( 0 );
	sal_Unicode cLast  = aResult.GetChar( nResultLen - 1 );
	if( cFirst == '\"' && cLast == '\"' )
		aResult = aResult.Copy( 1, nResultLen - 2 );

	sal_Bool bResModified = ( aResult != aEditingRes ) ? sal_True : sal_False;
	sal_Bool bError = sal_False;
	if ( !aVName.Len() )
	{
		bError = sal_True;
	}

	sal_Bool bRet = sal_False;

	if ( bError )
	{
		Sound::Beep();
	}
	else if ( bResModified )
	{
		bRet = ImplBasicEntryEdited( pEntry, aResult );
	}

	return bRet;
}

sal_Bool WatchTreeListBox::ImplBasicEntryEdited( SvLBoxEntry* pEntry, const String& rResult )
{
    WatchItem* pItem = (WatchItem*)pEntry->GetUserData();
	String aVName( pItem->maName );

	sal_Bool bError = sal_False;
	String aResult( rResult );
	String aIndex;
	bool bArrayElement;
	SbxBase* pSBX = ImplGetSBXForEntry( pEntry, bArrayElement );

	SbxBase* pToBeChanged = NULL;
	if ( pSBX && pSBX->ISA( SbxVariable ) && !pSBX->ISA( SbxMethod ) )
	{
		SbxVariable* pVar = (SbxVariable*)pSBX;
		SbxDataType eType = pVar->GetType();
		if ( (sal_uInt8)eType == (sal_uInt8)SbxOBJECT )
			bError = sal_True;
		else if ( eType & SbxARRAY )
			bError = sal_True;
		else
			pToBeChanged = pSBX;
	}

	if ( pToBeChanged )
	{
		if ( pToBeChanged->ISA( SbxVariable ) )
		{
			// Wenn der Typ variabel ist, macht die Konvertierung des SBX nichts,
			// bei festem Typ wird der String konvertiert.
			((SbxVariable*)pToBeChanged)->PutStringExt( aResult );
		}
		else
			bError = sal_True;
	}

	// Wenn jemand z.B. einen zu grossen Wert fuer ein Int eingegeben hat,
	// folgt beim naechsten Step() ein Runtime-Error.
	if ( SbxBase::IsError() )
	{
		bError = sal_True;
		SbxBase::ResetError();
	}

	if ( bError )
		Sound::Beep();

	UpdateWatches();

	// Der Text soll niemals 1-zu-1 uebernommen werden, weil dann das
	// UpdateWatches verlorengeht.
	return sal_False;
}


static void implCollapseModifiedObjectEntry( SvLBoxEntry* pParent, WatchTreeListBox* pThis )
{
	pThis->Collapse( pParent );

	SvLBoxTreeList*	pModel = pThis->GetModel();
	SvLBoxEntry* pDeleteEntry;
	while( (pDeleteEntry = pThis->SvTreeListBox::GetEntry( pParent, 0 )) != NULL )
	{
		implCollapseModifiedObjectEntry( pDeleteEntry, pThis );

        WatchItem* pItem = (WatchItem*)pDeleteEntry->GetUserData();
		delete pItem;
        pModel->Remove( pDeleteEntry );
	}
}

static String implCreateTypeStringForDimArray( WatchItem* pItem, SbxDataType eType )
{
	String aRetStr = getBasicTypeName( eType );

	SbxDimArray* pArray = pItem->mpArray;
	if( !pArray )
		pArray = pItem->GetRootArray();
	if( pArray )
	{
		int nDimLevel = pItem->nDimLevel;
		int nDims = pItem->nDimCount;
		if( nDimLevel < nDims )
		{
			aRetStr += '(';
			for( int i = nDimLevel ; i < nDims ; i++ )
			{
				short nMin, nMax;
				pArray->GetDim( sal::static_int_cast<short>( i+1 ), nMin, nMax );
				aRetStr += String::CreateFromInt32( nMin );
				aRetStr += String( RTL_CONSTASCII_USTRINGPARAM( " to " ) );
				aRetStr += String::CreateFromInt32( nMax );
				if( i < nDims - 1 )
					aRetStr += String( RTL_CONSTASCII_USTRINGPARAM( ", " ) );
			}
			aRetStr += ')';
		}
	}
	return aRetStr;
}


void implEnableChildren( SvLBoxEntry* pEntry, bool bEnable )
// inline void implEnableChildren( SvLBoxEntry* pEntry, bool bEnable )
{
	if( bEnable )
	{
		pEntry->SetFlags(
			(pEntry->GetFlags() &
			~(SV_ENTRYFLAG_NO_NODEBMP | SV_ENTRYFLAG_HAD_CHILDREN))
			| SV_ENTRYFLAG_CHILDS_ON_DEMAND );
	}
	else
	{
		pEntry->SetFlags(
			(pEntry->GetFlags() & ~(SV_ENTRYFLAG_CHILDS_ON_DEMAND)) );
	}
}

void WatchTreeListBox::UpdateWatches( bool bBasicStopped )
{
	SbMethod* pCurMethod = StarBASIC::GetActiveMethod();

	SbxError eOld = SbxBase::GetError();
	setBasicWatchMode( true );

	SvLBoxEntry* pEntry = First();
	while ( pEntry )
	{
        WatchItem* pItem = (WatchItem*)pEntry->GetUserData();
		String aVName( pItem->maName );
		DBG_ASSERT( aVName.Len(), "Var? - Darf nicht leer sein!" );
		String aWatchStr;
		String aTypeStr;
		if ( pCurMethod )
		{
			bool bArrayElement;
			SbxBase* pSBX = ImplGetSBXForEntry( pEntry, bArrayElement );

			// Array? If no end node create type string
			if( bArrayElement && pItem->nDimLevel < pItem->nDimCount )
			{
				SbxDimArray* pRootArray = pItem->GetRootArray();
				SbxDataType eType = pRootArray->GetType();
				// SbxDataType eType = pItem->mpArray->GetType();
				aTypeStr = implCreateTypeStringForDimArray( pItem, eType );
				implEnableChildren( pEntry, true );
			}

			bool bCollapse = false;
			if ( pSBX && pSBX->ISA( SbxVariable ) && !pSBX->ISA( SbxMethod ) )
			{
				SbxVariable* pVar = (SbxVariable*)pSBX;
				// Sonderbehandlung fuer Arrays:
				SbxDataType eType = pVar->GetType();
				if ( eType & SbxARRAY )
				{
					// Mehrdimensionale Arrays beruecksichtigen!
					SbxBase* pBase = pVar->GetObject();
					if ( pBase && pBase->ISA( SbxDimArray ) )
					{
						SbxDimArray* pNewArray = (SbxDimArray*)pBase;
						SbxDimArray* pOldArray = pItem->mpArray;

						bool bArrayChanged = false;
						if( pNewArray != NULL && pOldArray != NULL )
						{
							// Compare Array dimensions to see if array has changed
							// Can be a copy, so comparing pointers does not work
							sal_uInt16 nOldDims = pOldArray->GetDims();
							sal_uInt16 nNewDims = pNewArray->GetDims();
							if( nOldDims != nNewDims )
							{
								bArrayChanged = true;
							}
							else
							{
								for( int i = 0 ; i < nOldDims ; i++ )
								{
									short nOldMin, nOldMax;
									short nNewMin, nNewMax;

									pOldArray->GetDim( sal::static_int_cast<short>( i+1 ), nOldMin, nOldMax );
									pNewArray->GetDim( sal::static_int_cast<short>( i+1 ), nNewMin, nNewMax );
									if( nOldMin != nNewMin || nOldMax != nNewMax )
									{
										bArrayChanged = true;
										break;
									}
								}
							}
						}
						else if( pNewArray == NULL || pOldArray == NULL )
							bArrayChanged = true;

						if( pNewArray )
							implEnableChildren( pEntry, true );

						// #i37227 Clear always and replace array
						if( pNewArray != pOldArray )
						{
							pItem->clearWatchItem( false );
							if( pNewArray )
							{
								implEnableChildren( pEntry, true );

								pItem->mpArray = pNewArray;
								sal_uInt16 nDims = pNewArray->GetDims();
								pItem->nDimLevel = 0;
								pItem->nDimCount = nDims;
							}
						}
						if( bArrayChanged && pOldArray != NULL )
							bCollapse = true;

						aTypeStr = implCreateTypeStringForDimArray( pItem, eType );
					}
					else
						aWatchStr += String( RTL_CONSTASCII_USTRINGPARAM( "<?>" ) );
				}
				else if ( (sal_uInt8)eType == (sal_uInt8)SbxOBJECT )
				{
                    SbxObject* pObj = NULL;
			        SbxBase* pBase = pVar->GetObject();
			        if( pBase && pBase->ISA( SbxObject ) )
                        pObj = (SbxObject*)pBase;

                    if( pObj )
                    {
						// Check if member list has changed
						bool bObjChanged = false;
						if( pItem->mpObject != NULL && pItem->maMemberList.mpMemberNames != NULL )
						{
							SbxArray* pProps = pObj->GetProperties();
							sal_uInt16 nPropCount = pProps->Count();
							for( sal_uInt16 i = 0 ; i < nPropCount - 3 ; i++ )
							{
								SbxVariable* pVar_ = pProps->Get( i );
								String aName( pVar_->GetName() );
								if( pItem->maMemberList.mpMemberNames[i] != aName )
								{
									bObjChanged = true;
									break;
								}
							}
							if( bObjChanged )
								bCollapse = true;
						}

						pItem->mpObject = pObj;
						implEnableChildren( pEntry, true );
						aTypeStr = getBasicObjectTypeName( pObj );
					}
					else
					{
						aWatchStr = String( RTL_CONSTASCII_USTRINGPARAM( "Null" ) );
						if( pItem->mpObject != NULL )
						{
							bCollapse = true;
							pItem->clearWatchItem( false );

							implEnableChildren( pEntry, false );
						}
					}
				}
				else
				{
					if( pItem->mpObject != NULL )
					{
						bCollapse = true;
						pItem->clearWatchItem( false );

						implEnableChildren( pEntry, false );
					}

					bool bString = ((sal_uInt8)eType == (sal_uInt8)SbxSTRING);
					String aStrStr( RTL_CONSTASCII_USTRINGPARAM( "\"" ) );
					if( bString )
						aWatchStr += aStrStr;
					aWatchStr += pVar->GetString();
					if( bString )
						aWatchStr += aStrStr;
				}
				if( !aTypeStr.Len() )
				{
					if( !pVar->IsFixed() )
						aTypeStr = String( RTL_CONSTASCII_USTRINGPARAM( "Variant/" ) );
					aTypeStr += getBasicTypeName( pVar->GetType() );
				}
			}
			else if( !bArrayElement )
				aWatchStr += String( RTL_CONSTASCII_USTRINGPARAM( "<Out of Scope>" ) );

			if( bCollapse )
				implCollapseModifiedObjectEntry( pEntry, this );

		}
		else if( bBasicStopped )
		{
            if( pItem->mpObject || pItem->mpArray )
			{
				implCollapseModifiedObjectEntry( pEntry, this );
				pItem->mpObject = NULL;
			}
		}

		SvHeaderTabListBox::SetEntryText( aWatchStr, pEntry, ITEM_ID_VALUE-1 );
		SvHeaderTabListBox::SetEntryText( aTypeStr, pEntry, ITEM_ID_TYPE-1 );

		pEntry = Next( pEntry );
	}

	// Force redraw
    Invalidate();

	SbxBase::ResetError();
	if( eOld != SbxERR_OK )
		SbxBase::SetError( eOld );
	setBasicWatchMode( false );
}

