/*************************************************************************
 *
 * 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 <vcl/sound.hxx>
#include <basic/sbx.hxx>
#include <basic/sbxbase.hxx>
#include "sbxres.hxx"
#include <svl/brdcst.hxx>

TYPEINIT1(SbxMethod,SbxVariable)
TYPEINIT1(SbxProperty,SbxVariable)
TYPEINIT2(SbxObject,SbxVariable,SfxListener)

static const char* pNameProp;               // Name-Property
static const char* pParentProp;             // Parent-Property

static sal_uInt16 nNameHash = 0, nParentHash = 0;

/////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////

SbxObject::SbxObject( const XubString& rClass )
         : SbxVariable( SbxOBJECT ), aClassName( rClass )
{
    aData.pObj = this;
    if( !nNameHash )
    {
        pNameProp = GetSbxRes( STRING_NAMEPROP );
        pParentProp = GetSbxRes( STRING_PARENTPROP );
        nNameHash = MakeHashCode( String::CreateFromAscii( pNameProp ) );
        nParentHash = MakeHashCode( String::CreateFromAscii( pParentProp ) );
    }
    SbxObject::Clear();
    SbxObject::SetName( rClass );
}

SbxObject::SbxObject( const SbxObject& rObj )
    : SvRefBase( rObj ), SbxVariable( rObj.GetType() ),
      SfxListener( rObj )
{
    *this = rObj;
}

SbxObject& SbxObject::operator=( const SbxObject& r )
{
    if( &r != this )
    {
        SbxVariable::operator=( r );
        aClassName = r.aClassName;
        pMethods   = new SbxArray;
        pProps     = new SbxArray;
        pObjs      = new SbxArray( SbxOBJECT );
        // Die Arrays werden kopiert, die Inhalte uebernommen
        *pMethods  = *r.pMethods;
        *pProps    = *r.pProps;
        *pObjs     = *r.pObjs;
        // Da die Variablen uebernommen wurden, ist dies OK
        pDfltProp  = r.pDfltProp;
        SetName( r.GetName() );
        SetFlags( r.GetFlags() );
        SetModified( sal_True );
    }
    return *this;
}

static void CheckParentsOnDelete( SbxObject* pObj, SbxArray* p )
{
    for( sal_uInt16 i = 0; i < p->Count(); i++ )
    {
        SbxVariableRef& rRef = p->GetRef( i );
        if( rRef->IsBroadcaster() )
            pObj->EndListening( rRef->GetBroadcaster(), sal_True );
        // Hat das Element mehr als eine Referenz und noch einen Listener?
        if( rRef->GetRefCount() > 1 )
        {
            rRef->SetParent( NULL );
            DBG_ASSERT( !rRef->IsBroadcaster() || rRef->GetBroadcaster().GetListenerCount(), "Object element with dangling parent" );
        }
    }
}

SbxObject::~SbxObject()
{
    CheckParentsOnDelete( this, pProps );
    CheckParentsOnDelete( this, pMethods );
    CheckParentsOnDelete( this, pObjs );

    // avoid handling in ~SbxVariable as SBX_DIM_AS_NEW == SBX_GBLSEARCH
    ResetFlag( SBX_DIM_AS_NEW );
}

SbxDataType SbxObject::GetType() const
{
    return SbxOBJECT;
}

SbxClassType SbxObject::GetClass() const
{
    return SbxCLASS_OBJECT;
}

void SbxObject::Clear()
{
    pMethods   = new SbxArray;
    pProps     = new SbxArray;
    pObjs      = new SbxArray( SbxOBJECT );
    SbxVariable* p;
    p = Make( String::CreateFromAscii( pNameProp ), SbxCLASS_PROPERTY, SbxSTRING );
    p->SetFlag( SBX_DONTSTORE );
    p = Make( String::CreateFromAscii( pParentProp ), SbxCLASS_PROPERTY, SbxOBJECT );
    p->ResetFlag( SBX_WRITE );
    p->SetFlag( SBX_DONTSTORE );
    pDfltProp  = NULL;
    SetModified( sal_False );
}

void SbxObject::SFX_NOTIFY( SfxBroadcaster&, const TypeId&,
                            const SfxHint& rHint, const TypeId& )
{
    const SbxHint* p = PTR_CAST(SbxHint,&rHint);
    if( p )
    {
        sal_uIntPtr nId = p->GetId();
        sal_Bool bRead  = sal_Bool( nId == SBX_HINT_DATAWANTED );
        sal_Bool bWrite = sal_Bool( nId == SBX_HINT_DATACHANGED );
        SbxVariable* pVar = p->GetVar();
        if( bRead || bWrite )
        {
            XubString aVarName( pVar->GetName() );
            sal_uInt16 nHash_ = MakeHashCode( aVarName );
            if( nHash_ == nNameHash
             && aVarName.EqualsIgnoreCaseAscii( pNameProp ) )
            {
                if( bRead )
                    pVar->PutString( GetName() );
                else
                    SetName( pVar->GetString() );
            }
            else if( nHash_ == nParentHash
             && aVarName.EqualsIgnoreCaseAscii( pParentProp ) )
            {
                SbxObject* p_ = GetParent();
                if( !p_ )
                    p_ = this;
                pVar->PutObject( p_ );
            }
        }
    }
}

sal_Bool SbxObject::IsClass( const XubString& rName ) const
{
    return sal_Bool( aClassName.EqualsIgnoreCaseAscii( rName ) );
}

SbxVariable* SbxObject::FindUserData( sal_uInt32 nData )
{
    if( !GetAll( SbxCLASS_DONTCARE ) )
        return NULL;

    SbxVariable* pRes = pMethods->FindUserData( nData );
    if( !pRes )
        pRes = pProps->FindUserData( nData );
    if( !pRes )
        pRes = pObjs->FindUserData( nData );
    // Search in den Parents?
    if( !pRes && IsSet( SBX_GBLSEARCH ) )
    {
        SbxObject* pCur = this;
        while( !pRes && pCur->pParent )
        {
            // Ich selbst bin schon durchsucht worden!
            sal_uInt16 nOwn = pCur->GetFlags();
            pCur->ResetFlag( SBX_EXTSEARCH );
            // Ich suche bereits global!
            sal_uInt16 nPar = pCur->pParent->GetFlags();
            pCur->pParent->ResetFlag( SBX_GBLSEARCH );
            pRes = pCur->pParent->FindUserData( nData );
            pCur->SetFlags( nOwn );
            pCur->pParent->SetFlags( nPar );
            pCur = pCur->pParent;
        }
    }
    return pRes;
}

SbxVariable* SbxObject::Find( const XubString& rName, SbxClassType t )
{
#ifdef DBG_UTIL
    static sal_uInt16 nLvl = 0;
    static const char* pCls[] =
    { "DontCare","Array","Value","Variable","Method","Property","Object" };
    ByteString aNameStr1( (const UniString&)rName, RTL_TEXTENCODING_ASCII_US );
    ByteString aNameStr2( (const UniString&)SbxVariable::GetName(), RTL_TEXTENCODING_ASCII_US );
    DbgOutf( "SBX: Search %.*s %s %s in %s",
        nLvl++, "                              ",
        ( t >= SbxCLASS_DONTCARE && t <= SbxCLASS_OBJECT )
         ? pCls[ t-1 ] : "Unknown class", aNameStr1.GetBuffer(), aNameStr1.GetBuffer() );
#endif

    if( !GetAll( t ) )
        return NULL;
    SbxVariable* pRes = NULL;
    pObjs->SetFlag( SBX_EXTSEARCH );
    if( t == SbxCLASS_DONTCARE )
    {
        pRes = pMethods->Find( rName, SbxCLASS_METHOD );
        if( !pRes )
            pRes = pProps->Find( rName, SbxCLASS_PROPERTY );
        if( !pRes )
            pRes = pObjs->Find( rName, t );
    }
    else
    {
        SbxArray* pArray = NULL;
        switch( t )
        {
            case SbxCLASS_VARIABLE:
            case SbxCLASS_PROPERTY: pArray = pProps;    break;
            case SbxCLASS_METHOD:   pArray = pMethods;  break;
            case SbxCLASS_OBJECT:   pArray = pObjs;     break;
            default:
                DBG_ASSERT( !this, "Ungueltige SBX-Klasse" );
        }
        if( pArray )
            pRes = pArray->Find( rName, t );
    }
    // Extended Search im Objekt-Array?
    // Fuer Objekte und DontCare ist das Objektarray bereits
    // durchsucht worden
    if( !pRes && ( t == SbxCLASS_METHOD || t == SbxCLASS_PROPERTY ) )
        pRes = pObjs->Find( rName, t );
    // Search in den Parents?
    if( !pRes && IsSet( SBX_GBLSEARCH ) )
    {
        SbxObject* pCur = this;
        while( !pRes && pCur->pParent )
        {
            // Ich selbst bin schon durchsucht worden!
            sal_uInt16 nOwn = pCur->GetFlags();
            pCur->ResetFlag( SBX_EXTSEARCH );
            // Ich suche bereits global!
            sal_uInt16 nPar = pCur->pParent->GetFlags();
            pCur->pParent->ResetFlag( SBX_GBLSEARCH );
            pRes = pCur->pParent->Find( rName, t );
            pCur->SetFlags( nOwn );
            pCur->pParent->SetFlags( nPar );
            pCur = pCur->pParent;
        }
    }
#ifdef DBG_UTIL
    nLvl--;
    if( pRes )
    {
        ByteString aNameStr3( (const UniString&)rName, RTL_TEXTENCODING_ASCII_US );
        ByteString aNameStr4( (const UniString&)SbxVariable::GetName(), RTL_TEXTENCODING_ASCII_US );
        DbgOutf( "SBX: Found %.*s %s in %s",
            nLvl, "                              ", aNameStr3.GetBuffer(), aNameStr4.GetBuffer() );
    }
#endif
    return pRes;
}

// Kurzform: Die Parent-Kette wird durchsucht
// Das ganze rekursiv, da Call() ueberladen sein kann
// Qualified Names sind zugelassen

sal_Bool SbxObject::Call( const XubString& rName, SbxArray* pParam )
{
    SbxVariable* pMeth = FindQualified( rName, SbxCLASS_DONTCARE);
    if( pMeth && pMeth->ISA(SbxMethod) )
    {
        // FindQualified() koennte schon zugeschlagen haben!
        if( pParam )
            pMeth->SetParameters( pParam );
        pMeth->Broadcast( SBX_HINT_DATAWANTED );
        pMeth->SetParameters( NULL );
        return sal_True;
    }
    SetError( SbxERR_NO_METHOD );
    return sal_False;
}

SbxProperty* SbxObject::GetDfltProperty()
{
    if ( !pDfltProp && aDfltPropName.Len() )
    {
        pDfltProp = (SbxProperty*) Find( aDfltPropName, SbxCLASS_PROPERTY );
        if( !pDfltProp )
            pDfltProp = (SbxProperty*) Make( aDfltPropName, SbxCLASS_PROPERTY, SbxVARIANT );
    }
    return pDfltProp;
}
void SbxObject::SetDfltProperty( const XubString& rName )
{
    if ( rName != aDfltPropName )
        pDfltProp = NULL;
    aDfltPropName = rName;
    SetModified( sal_True );
}

void SbxObject::SetDfltProperty( SbxProperty* p )
{
    if( p )
    {
        sal_uInt16 n;
        SbxArray* pArray = FindVar( p, n );
        pArray->Put( p, n );
        if( p->GetParent() != this )
            p->SetParent( this );
        Broadcast( SBX_HINT_OBJECTCHANGED );
    }
    pDfltProp = p;
    SetModified( sal_True );
}

// Suchen einer bereits vorhandenen Variablen. Falls sie gefunden wurde,
// wird der Index gesetzt, sonst wird der Count des Arrays geliefert.
// In jedem Fall wird das korrekte Array geliefert.

SbxArray* SbxObject::FindVar( SbxVariable* pVar, sal_uInt16& nArrayIdx )
{
    SbxArray* pArray = NULL;
    if( pVar ) switch( pVar->GetClass() )
    {
        case SbxCLASS_VARIABLE:
        case SbxCLASS_PROPERTY: pArray = pProps;    break;
        case SbxCLASS_METHOD:   pArray = pMethods;  break;
        case SbxCLASS_OBJECT:   pArray = pObjs;     break;
        default:
            DBG_ASSERT( !this, "Ungueltige SBX-Klasse" );
    }
    if( pArray )
    {
        nArrayIdx = pArray->Count();
        // ist die Variable per Name vorhanden?
        pArray->ResetFlag( SBX_EXTSEARCH );
        SbxVariable* pOld = pArray->Find( pVar->GetName(), pVar->GetClass() );
        if( pOld )
          for( sal_uInt16 i = 0; i < pArray->Count(); i++ )
        {
            SbxVariableRef& rRef = pArray->GetRef( i );
            if( (SbxVariable*) rRef == pOld )
            {
                nArrayIdx = i; break;
            }
        }
    }
    return pArray;
}

// Falls ein neues Objekt eingerichtet wird, wird es, falls es bereits
// eines mit diesem Namen gibt, indiziert.

SbxVariable* SbxObject::Make( const XubString& rName, SbxClassType ct, SbxDataType dt )
{
    // Ist das Objekt bereits vorhanden?
    SbxArray* pArray = NULL;
    switch( ct )
    {
        case SbxCLASS_VARIABLE:
        case SbxCLASS_PROPERTY: pArray = pProps;    break;
        case SbxCLASS_METHOD:   pArray = pMethods;  break;
        case SbxCLASS_OBJECT:   pArray = pObjs;     break;
        default:
            DBG_ASSERT( !this, "Ungueltige SBX-Klasse" );
    }
    if( !pArray )
        return NULL;
    // Collections duerfen gleichnamige Objekte enthalten
    if( !( ct == SbxCLASS_OBJECT && ISA(SbxCollection) ) )
    {
        SbxVariable* pRes = pArray->Find( rName, ct );
        if( pRes )
        {
/* Wegen haeufiger Probleme (z.B. #67000) erstmal ganz raus
#ifdef DBG_UTIL
            if( pRes->GetHashCode() != nNameHash
             && pRes->GetHashCode() != nParentHash )
            {
                XubString aMsg( "SBX-Element \"" );
                aMsg += pRes->GetName();
                aMsg += "\"\n in Objekt \"";
                aMsg += GetName();
                aMsg += "\" bereits vorhanden";
                DbgError( (const char*)aMsg.GetStr() );
            }
#endif
*/
            return pRes;
        }
    }
    SbxVariable* pVar = NULL;
    switch( ct )
    {
        case SbxCLASS_VARIABLE:
        case SbxCLASS_PROPERTY:
            pVar = new SbxProperty( rName, dt );
            break;
        case SbxCLASS_METHOD:
            pVar = new SbxMethod( rName, dt );
            break;
        case SbxCLASS_OBJECT:
            pVar = CreateObject( rName );
            break;
        default: break;
    }
    pVar->SetParent( this );
    pArray->Put( pVar, pArray->Count() );
    SetModified( sal_True );
    // Das Objekt lauscht immer
    StartListening( pVar->GetBroadcaster(), sal_True );
    Broadcast( SBX_HINT_OBJECTCHANGED );
    return pVar;
}

SbxObject* SbxObject::MakeObject( const XubString& rName, const XubString& rClass )
{
    // Ist das Objekt bereits vorhanden?
    if( !ISA(SbxCollection) )
    {
        SbxVariable* pRes = pObjs->Find( rName, SbxCLASS_OBJECT );
        if( pRes )
        {
/* Wegen haeufiger Probleme (z.B. #67000) erstmal ganz raus
#ifdef DBG_UTIL
            if( pRes->GetHashCode() != nNameHash
             && pRes->GetHashCode() != nParentHash )
            {
                XubString aMsg( "SBX-Objekt \"" );
                aMsg += pRes->GetName();
                aMsg += "\"\n in Objekt \"";
                aMsg += GetName();
                aMsg += "\" bereits vorhanden";
                DbgError( (const char*)aMsg.GetStr() );
            }
#endif
*/
            return PTR_CAST(SbxObject,pRes);
        }
    }
    SbxObject* pVar = CreateObject( rClass );
    if( pVar )
    {
        pVar->SetName( rName );
        pVar->SetParent( this );
        pObjs->Put( pVar, pObjs->Count() );
        SetModified( sal_True );
        // Das Objekt lauscht immer
        StartListening( pVar->GetBroadcaster(), sal_True );
        Broadcast( SBX_HINT_OBJECTCHANGED );
    }
    return pVar;
}

void SbxObject::Insert( SbxVariable* pVar )
{
    sal_uInt16 nIdx;
    SbxArray* pArray = FindVar( pVar, nIdx );
    if( pArray )
    {
        // Hinein damit. Man sollte allerdings auf die Pointer aufpassen!
        if( nIdx < pArray->Count() )
        {
            // dann gibt es dieses Element bereits
            // Bei Collections duerfen gleichnamige Objekte hinein
            if( pArray == pObjs && ISA(SbxCollection) )
                nIdx = pArray->Count();
            else
            {
                SbxVariable* pOld = pArray->Get( nIdx );
                // schon drin: ueberschreiben
                if( pOld == pVar )
                    return;

/* Wegen haeufiger Probleme (z.B. #67000) erstmal ganz raus
#ifdef DBG_UTIL
                if( pOld->GetHashCode() != nNameHash
                 && pOld->GetHashCode() != nParentHash )
                {
                    XubString aMsg( "SBX-Element \"" );
                    aMsg += pVar->GetName();
                    aMsg += "\"\n in Objekt \"";
                    aMsg += GetName();
                    aMsg += "\" bereits vorhanden";
                    DbgError( (const char*)aMsg.GetStr() );
                }
#endif
*/
                EndListening( pOld->GetBroadcaster(), sal_True );
                if( pVar->GetClass() == SbxCLASS_PROPERTY )
                {
                    if( pOld == pDfltProp )
                        pDfltProp = (SbxProperty*) pVar;
                }
            }
        }
        StartListening( pVar->GetBroadcaster(), sal_True );
        pArray->Put( pVar, nIdx );
        if( pVar->GetParent() != this )
            pVar->SetParent( this );
        SetModified( sal_True );
        Broadcast( SBX_HINT_OBJECTCHANGED );
#ifdef DBG_UTIL
    static const char* pCls[] =
    { "DontCare","Array","Value","Variable","Method","Property","Object" };
    XubString aVarName( pVar->GetName() );
    if ( !aVarName.Len() && pVar->ISA(SbxObject) )
        aVarName = PTR_CAST(SbxObject,pVar)->GetClassName();
    ByteString aNameStr1( (const UniString&)aVarName, RTL_TEXTENCODING_ASCII_US );
    ByteString aNameStr2( (const UniString&)SbxVariable::GetName(), RTL_TEXTENCODING_ASCII_US );
    DbgOutf( "SBX: Insert %s %s in %s",
        ( pVar->GetClass() >= SbxCLASS_DONTCARE &&
          pVar->GetClass() <= SbxCLASS_OBJECT )
            ? pCls[ pVar->GetClass()-1 ] : "Unknown class", aNameStr1.GetBuffer(), aNameStr1.GetBuffer() );
#endif
    }
}

// AB 23.4.1997, Optimierung, Einfuegen ohne Ueberpruefung auf doppelte
// Eintraege und ohne Broadcasts, wird nur in SO2/auto.cxx genutzt
void SbxObject::QuickInsert( SbxVariable* pVar )
{
    SbxArray* pArray = NULL;
    if( pVar )
    {
        switch( pVar->GetClass() )
        {
            case SbxCLASS_VARIABLE:
            case SbxCLASS_PROPERTY: pArray = pProps;    break;
            case SbxCLASS_METHOD:   pArray = pMethods;  break;
            case SbxCLASS_OBJECT:   pArray = pObjs;     break;
            default:
                DBG_ASSERT( !this, "Ungueltige SBX-Klasse" );
        }
    }
    if( pArray )
    {
        StartListening( pVar->GetBroadcaster(), sal_True );
        pArray->Put( pVar, pArray->Count() );
        if( pVar->GetParent() != this )
            pVar->SetParent( this );
        SetModified( sal_True );
#ifdef DBG_UTIL
    static const char* pCls[] =
    { "DontCare","Array","Value","Variable","Method","Property","Object" };
    XubString aVarName( pVar->GetName() );
    if ( !aVarName.Len() && pVar->ISA(SbxObject) )
        aVarName = PTR_CAST(SbxObject,pVar)->GetClassName();
    ByteString aNameStr1( (const UniString&)aVarName, RTL_TEXTENCODING_ASCII_US );
    ByteString aNameStr2( (const UniString&)SbxVariable::GetName(), RTL_TEXTENCODING_ASCII_US );
    DbgOutf( "SBX: Insert %s %s in %s",
        ( pVar->GetClass() >= SbxCLASS_DONTCARE &&
          pVar->GetClass() <= SbxCLASS_OBJECT )
            ? pCls[ pVar->GetClass()-1 ] : "Unknown class", aNameStr1.GetBuffer(), aNameStr1.GetBuffer() );
#endif
    }
}

// AB 23.3.1997, Spezial-Methode, gleichnamige Controls zulassen
void SbxObject::VCPtrInsert( SbxVariable* pVar )
{
    SbxArray* pArray = NULL;
    if( pVar )
    {
        switch( pVar->GetClass() )
        {
            case SbxCLASS_VARIABLE:
            case SbxCLASS_PROPERTY: pArray = pProps;    break;
            case SbxCLASS_METHOD:   pArray = pMethods;  break;
            case SbxCLASS_OBJECT:   pArray = pObjs;     break;
            default:
                DBG_ASSERT( !this, "Ungueltige SBX-Klasse" );
        }
    }
    if( pArray )
    {
        StartListening( pVar->GetBroadcaster(), sal_True );
        pArray->Put( pVar, pArray->Count() );
        if( pVar->GetParent() != this )
            pVar->SetParent( this );
        SetModified( sal_True );
        Broadcast( SBX_HINT_OBJECTCHANGED );
    }
}

void SbxObject::Remove( const XubString& rName, SbxClassType t )
{
    Remove( SbxObject::Find( rName, t ) );
}

void SbxObject::Remove( SbxVariable* pVar )
{
    sal_uInt16 nIdx;
    SbxArray* pArray = FindVar( pVar, nIdx );
    if( pArray && nIdx < pArray->Count() )
    {
#ifdef DBG_UTIL
    XubString aVarName( pVar->GetName() );
    if ( !aVarName.Len() && pVar->ISA(SbxObject) )
        aVarName = PTR_CAST(SbxObject,pVar)->GetClassName();
    ByteString aNameStr1( (const UniString&)aVarName, RTL_TEXTENCODING_ASCII_US );
    ByteString aNameStr2( (const UniString&)SbxVariable::GetName(), RTL_TEXTENCODING_ASCII_US );
#endif
        SbxVariableRef pVar_ = pArray->Get( nIdx );
        if( pVar_->IsBroadcaster() )
            EndListening( pVar_->GetBroadcaster(), sal_True );
        if( (SbxVariable*) pVar_ == pDfltProp )
            pDfltProp = NULL;
        pArray->Remove( nIdx );
        if( pVar_->GetParent() == this )
            pVar_->SetParent( NULL );
        SetModified( sal_True );
        Broadcast( SBX_HINT_OBJECTCHANGED );
    }
}

// AB 23.3.1997, Loeschen per Pointer fuer Controls (doppelte Namen!)
void SbxObject::VCPtrRemove( SbxVariable* pVar )
{
    sal_uInt16 nIdx;
    // Neu FindVar-Methode, sonst identisch mit normaler Methode
    SbxArray* pArray = VCPtrFindVar( pVar, nIdx );
    if( pArray && nIdx < pArray->Count() )
    {
        SbxVariableRef xVar = pArray->Get( nIdx );
        if( xVar->IsBroadcaster() )
            EndListening( xVar->GetBroadcaster(), sal_True );
        if( (SbxVariable*) xVar == pDfltProp )
            pDfltProp = NULL;
        pArray->Remove( nIdx );
        if( xVar->GetParent() == this )
            xVar->SetParent( NULL );
        SetModified( sal_True );
        Broadcast( SBX_HINT_OBJECTCHANGED );
    }
}

// AB 23.3.1997, Zugehoerige Spezial-Methode, nur ueber Pointer suchen
SbxArray* SbxObject::VCPtrFindVar( SbxVariable* pVar, sal_uInt16& nArrayIdx )
{
    SbxArray* pArray = NULL;
    if( pVar ) switch( pVar->GetClass() )
    {
        case SbxCLASS_VARIABLE:
        case SbxCLASS_PROPERTY: pArray = pProps;    break;
        case SbxCLASS_METHOD:   pArray = pMethods;  break;
        case SbxCLASS_OBJECT:   pArray = pObjs;     break;
        default:
            DBG_ASSERT( !this, "Ungueltige SBX-Klasse" );
    }
    if( pArray )
    {
        nArrayIdx = pArray->Count();
        for( sal_uInt16 i = 0; i < pArray->Count(); i++ )
        {
            SbxVariableRef& rRef = pArray->GetRef( i );
            if( (SbxVariable*) rRef == pVar )
            {
                nArrayIdx = i; break;
            }
        }
    }
    return pArray;
}



void SbxObject::SetPos( SbxVariable* pVar, sal_uInt16 nPos )
{
    sal_uInt16 nIdx;
    SbxArray* pArray = FindVar( pVar, nIdx );
    if( pArray )
    {
        if( nPos >= pArray->Count() )
            nPos = pArray->Count() - 1;
        if( nIdx < ( pArray->Count() - 1 ) )
        {
            SbxVariableRef refVar = pArray->Get( nIdx );
            pArray->Remove( nIdx );
            pArray->Insert( refVar, nPos );
        }
    }
//  SetModified( sal_True );
//  Broadcast( SBX_HINT_OBJECTCHANGED );
}

static sal_Bool LoadArray( SvStream& rStrm, SbxObject* pThis, SbxArray* pArray )
{
    SbxArrayRef p = (SbxArray*) SbxBase::Load( rStrm );
    if( !p.Is() )
        return sal_False;
    for( sal_uInt16 i = 0; i < p->Count(); i++ )
    {
        SbxVariableRef& r = p->GetRef( i );
        SbxVariable* pVar = r;
        if( pVar )
        {
            pVar->SetParent( pThis );
            pThis->StartListening( pVar->GetBroadcaster(), sal_True );
        }
    }
    pArray->Merge( p );
    return sal_True;
}

// Der Load eines Objekts ist additiv!

sal_Bool SbxObject::LoadData( SvStream& rStrm, sal_uInt16 nVer )
{
    // Hilfe fuer das Einlesen alter Objekte: einfach sal_True zurueck,
    // LoadPrivateData() muss Default-Zustand herstellen
    if( !nVer )
        return sal_True;

    pDfltProp = NULL;
    if( !SbxVariable::LoadData( rStrm, nVer ) )
        return sal_False;
    // Wenn kein fremdes Objekt enthalten ist, uns selbst eintragen
    if( aData.eType == SbxOBJECT && !aData.pObj )
        aData.pObj = this;
    sal_uInt32 nSize;
    XubString aDfltProp;
    rStrm.ReadByteString( aClassName, RTL_TEXTENCODING_ASCII_US );
    rStrm.ReadByteString( aDfltProp, RTL_TEXTENCODING_ASCII_US );
    sal_uIntPtr nPos = rStrm.Tell();
    rStrm >> nSize;
    if( !LoadPrivateData( rStrm, nVer ) )
        return sal_False;
    sal_uIntPtr nNewPos = rStrm.Tell();
    nPos += nSize;
    DBG_ASSERT( nPos >= nNewPos, "SBX: Zu viele Daten eingelesen" );
    if( nPos != nNewPos )
        rStrm.Seek( nPos );
    if( !LoadArray( rStrm, this, pMethods )
     || !LoadArray( rStrm, this, pProps )
     || !LoadArray( rStrm, this, pObjs ) )
        return sal_False;
    // Properties setzen
    if( aDfltProp.Len() )
        pDfltProp = (SbxProperty*) pProps->Find( aDfltProp, SbxCLASS_PROPERTY );
    SetModified( sal_False );
    return sal_True;
}

sal_Bool SbxObject::StoreData( SvStream& rStrm ) const
{
    if( !SbxVariable::StoreData( rStrm ) )
        return sal_False;
    XubString aDfltProp;
    if( pDfltProp )
        aDfltProp = pDfltProp->GetName();
    rStrm.WriteByteString( aClassName, RTL_TEXTENCODING_ASCII_US );
    rStrm.WriteByteString( aDfltProp, RTL_TEXTENCODING_ASCII_US );
    sal_uIntPtr nPos = rStrm.Tell();
    rStrm << (sal_uInt32) 0L;
    if( !StorePrivateData( rStrm ) )
        return sal_False;
    sal_uIntPtr nNew = rStrm.Tell();
    rStrm.Seek( nPos );
    rStrm << (sal_uInt32) ( nNew - nPos );
    rStrm.Seek( nNew );
    if( !pMethods->Store( rStrm ) )
        return sal_False;
    if( !pProps->Store( rStrm ) )
        return sal_False;
    if( !pObjs->Store( rStrm ) )
        return sal_False;
    ((SbxObject*) this)->SetModified( sal_False );
    return sal_True;
}

XubString SbxObject::GenerateSource( const XubString &rLinePrefix,
                                  const SbxObject* )
{
    // Properties in einem String einsammeln
    XubString aSource;
    SbxArrayRef xProps( GetProperties() );
    bool bLineFeed = false;
    for ( sal_uInt16 nProp = 0; nProp < xProps->Count(); ++nProp )
    {
        SbxPropertyRef xProp = (SbxProperty*) xProps->Get(nProp);
        XubString aPropName( xProp->GetName() );
        if ( xProp->CanWrite()
         && !( xProp->GetHashCode() == nNameHash
            && aPropName.EqualsIgnoreCaseAscii( pNameProp ) ) )
        {
            // ausser vor dem ersten Property immer einen Umbruch einfuegen
            if ( bLineFeed )
                aSource.AppendAscii( "\n" );
            else
                bLineFeed = true;

            aSource += rLinePrefix;
            aSource += '.';
            aSource += aPropName;
            aSource.AppendAscii( " = " );

            // den Property-Wert textuell darstellen
            switch ( xProp->GetType() )
            {
                case SbxEMPTY:
                case SbxNULL:
                    // kein Wert
                    break;

                case SbxSTRING:
                {
                    // Strings in Anf"uhrungszeichen
                    aSource.AppendAscii( "\"" );
                    aSource += xProp->GetString();
                    aSource.AppendAscii( "\"" );
                    break;
                }

                default:
                {
                    // sonstiges wie z.B. Zahlen direkt
                    aSource += xProp->GetString();
                    break;
                }
            }
        }
    }
    return aSource;
}

static sal_Bool CollectAttrs( const SbxBase* p, XubString& rRes )
{
    XubString aAttrs;
    if( p->IsHidden() )
        aAttrs.AssignAscii( "Hidden" );
    if( p->IsSet( SBX_EXTSEARCH ) )
    {
        if( aAttrs.Len() )
            aAttrs += ',';
        aAttrs.AppendAscii( "ExtSearch" );
    }
    if( !p->IsVisible() )
    {
        if( aAttrs.Len() )
            aAttrs += ',';
        aAttrs.AppendAscii( "Invisible" );
    }
    if( p->IsSet( SBX_DONTSTORE ) )
    {
        if( aAttrs.Len() )
            aAttrs += ',';
        aAttrs.AppendAscii( "DontStore" );
    }
    if( aAttrs.Len() )
    {
        rRes.AssignAscii( " (" );
        rRes += aAttrs;
        rRes += ')';
        return sal_True;
    }
    else
    {
        rRes.Erase();
        return sal_False;
    }
}

void SbxObject::Dump( SvStream& rStrm, sal_Bool bFill )
{
    // Einr"uckung
    static sal_uInt16 nLevel = 0;
    if ( nLevel > 10 )
    {
        rStrm << "<too deep>" << endl;
        return;
    }
    ++nLevel;
    String aIndent;
    for ( sal_uInt16 n = 1; n < nLevel; ++n )
        aIndent.AppendAscii( "    " );

    // ggf. Objekt vervollst"andigen
    if ( bFill )
        GetAll( SbxCLASS_DONTCARE );

    // Daten des Objekts selbst ausgeben
    ByteString aNameStr( (const UniString&)GetName(), RTL_TEXTENCODING_ASCII_US );
    ByteString aClassNameStr( (const UniString&)aClassName, RTL_TEXTENCODING_ASCII_US );
    rStrm << "Object( "
          << ByteString::CreateFromInt64( (sal_uIntPtr) this ).GetBuffer() << "=='"
          << ( aNameStr.Len() ? aNameStr.GetBuffer() : "<unnamed>" ) << "', "
          << "of class '" << aClassNameStr.GetBuffer() << "', "
          << "counts "
          << ByteString::CreateFromInt64( GetRefCount() ).GetBuffer()
          << " refs, ";
    if ( GetParent() )
    {
        ByteString aParentNameStr( (const UniString&)GetName(), RTL_TEXTENCODING_ASCII_US );
        rStrm << "in parent "
              << ByteString::CreateFromInt64( (sal_uIntPtr) GetParent() ).GetBuffer()
              << "=='" << ( aParentNameStr.Len() ? aParentNameStr.GetBuffer() : "<unnamed>" ) << "'";
    }
    else
        rStrm << "no parent ";
    rStrm << " )" << endl;
    ByteString aIndentNameStr( (const UniString&)aIndent, RTL_TEXTENCODING_ASCII_US );
    rStrm << aIndentNameStr.GetBuffer() << "{" << endl;

    // Flags
    XubString aAttrs;
    if( CollectAttrs( this, aAttrs ) )
    {
        ByteString aAttrStr( (const UniString&)aAttrs, RTL_TEXTENCODING_ASCII_US );
        rStrm << aIndentNameStr.GetBuffer() << "- Flags: " << aAttrStr.GetBuffer() << endl;
    }

    // Methods
    rStrm << aIndentNameStr.GetBuffer() << "- Methods:" << endl;
    for( sal_uInt16 i = 0; i < pMethods->Count(); i++ )
    {
        SbxVariableRef& r = pMethods->GetRef( i );
        SbxVariable* pVar = r;
        if( pVar )
        {
            XubString aLine( aIndent );
            aLine.AppendAscii( "  - " );
            aLine += pVar->GetName( SbxNAME_SHORT_TYPES );
            XubString aAttrs2;
            if( CollectAttrs( pVar, aAttrs2 ) )
                aLine += aAttrs2;
            if( !pVar->IsA( TYPE(SbxMethod) ) )
                aLine.AppendAscii( "  !! Not a Method !!" );
            rStrm.WriteByteString( aLine, RTL_TEXTENCODING_ASCII_US );

            // bei Object-Methods auch das Object ausgeben
            if ( pVar->GetValues_Impl().eType == SbxOBJECT &&
                    pVar->GetValues_Impl().pObj &&
                    pVar->GetValues_Impl().pObj != this &&
                    pVar->GetValues_Impl().pObj != GetParent() )
            {
                rStrm << " contains ";
                ((SbxObject*) pVar->GetValues_Impl().pObj)->Dump( rStrm, bFill );
            }
            else
                rStrm << endl;
        }
    }

    // Properties
    rStrm << aIndentNameStr.GetBuffer() << "- Properties:" << endl;
    {
        for( sal_uInt16 i = 0; i < pProps->Count(); i++ )
        {
            SbxVariableRef& r = pProps->GetRef( i );
            SbxVariable* pVar = r;
            if( pVar )
            {
                XubString aLine( aIndent );
                aLine.AppendAscii( "  - " );
                aLine += pVar->GetName( SbxNAME_SHORT_TYPES );
                XubString aAttrs3;
                if( CollectAttrs( pVar, aAttrs3 ) )
                    aLine += aAttrs3;
                if( !pVar->IsA( TYPE(SbxProperty) ) )
                    aLine.AppendAscii( "  !! Not a Property !!" );
                rStrm.WriteByteString( aLine, RTL_TEXTENCODING_ASCII_US );

                // bei Object-Properties auch das Object ausgeben
                if ( pVar->GetValues_Impl().eType == SbxOBJECT &&
                        pVar->GetValues_Impl().pObj &&
                        pVar->GetValues_Impl().pObj != this &&
                        pVar->GetValues_Impl().pObj != GetParent() )
                {
                    rStrm << " contains ";
                    ((SbxObject*) pVar->GetValues_Impl().pObj)->Dump( rStrm, bFill );
                }
                else
                    rStrm << endl;
            }
        }
    }

    // Objects
    rStrm << aIndentNameStr.GetBuffer() << "- Objects:" << endl;
    {
        for( sal_uInt16 i = 0; i < pObjs->Count(); i++ )
        {
            SbxVariableRef& r = pObjs->GetRef( i );
            SbxVariable* pVar = r;
            if ( pVar )
            {
                rStrm << aIndentNameStr.GetBuffer() << "  - Sub";
                if ( pVar->ISA(SbxObject) )
                    ((SbxObject*) pVar)->Dump( rStrm, bFill );
                else if ( pVar->ISA(SbxVariable) )
                    ((SbxVariable*) pVar)->Dump( rStrm, bFill );
            }
        }
    }

    rStrm << aIndentNameStr.GetBuffer() << "}" << endl << endl;
    --nLevel;
}

SvDispatch* SbxObject::GetSvDispatch()
{
    return NULL;
}

sal_Bool SbxMethod::Run( SbxValues* pValues )
{
    SbxValues aRes;
    if( !pValues )
        pValues = &aRes;
    pValues->eType = SbxVARIANT;
    return Get( *pValues );
}

SbxClassType SbxMethod::GetClass() const
{
    return SbxCLASS_METHOD;
}

SbxClassType SbxProperty::GetClass() const
{
    return SbxCLASS_PROPERTY;
}

void SbxObject::GarbageCollection( sal_uIntPtr nObjects )

/*  [Beschreibung]

    Diese statische Methode durchsucht die n"achsten 'nObjects' der zur Zeit
    existierenden <SbxObject>-Instanzen nach zyklischen Referenzen, die sich
    nur noch selbst am Leben erhalten. Ist 'nObjects==0', dann werden
    alle existierenden durchsucht.

    zur Zeit nur implementiert: Object -> Parent-Property -> Parent -> Object
*/

{
    (void)nObjects;

    static sal_Bool bInGarbageCollection = sal_False;
    if ( bInGarbageCollection )
        return;
    bInGarbageCollection = sal_True;

#if 0
    // erstes Object dieser Runde anspringen
    sal_Bool bAll = !nObjects;
    if ( bAll )
        rObjects.First();
    SbxObject *pObj = rObjects.GetCurObject();
    if ( !pObj )
        pObj = rObjects.First();

    while ( pObj && 0 != nObjects-- )
    {
        // hat der Parent nur noch 1 Ref-Count?
        SbxObject *pParent = PTR_CAST( SbxObject, pObj->GetParent() );
        if ( pParent && 1 == pParent->GetRefCount() )
        {
            // dann alle Properies des Objects durchsuchen
            SbxArray *pProps = pObj->GetProperties();
            for ( sal_uInt16 n = 0; n < pProps->Count(); ++n )
            {
                // verweist die Property auf den Parent des Object?
                SbxVariable *pProp = pProps->Get(n);
                const SbxValues &rValues = pProp->GetValues_Impl();
                if ( SbxOBJECT == rValues.eType &&
                     pParent == rValues.pObj )
                {
#ifdef DBG_UTIL
                    DbgOutf( "SBX: %s.%s with Object %s was garbage",
                             pObj->GetName().GetStr(),
                             pProp->GetName().GetStr(),
                             pParent->GetName().GetStr() );
#endif
                    // dann freigeben
                    pProp->SbxValue::Clear();
                    Sound::Beep();
                    break;
                }
            }
        }

        // zum n"achsten
        pObj = rObjects.Next();
        if ( !bAll && !pObj )
            pObj = rObjects.First();
    }
#endif

// AB 28.10. Zur 507a vorerst raus, da SfxBroadcaster::Enable() wegfaellt
#if 0
#ifdef DBG_UTIL
    SbxVarList_Impl &rVars = GetSbxData_Impl()->aVars;
    DbgOutf( "SBX: garbage collector done, %lu objects remainding",
             rVars.Count() );
    if ( rVars.Count() > 200 && rVars.Count() < 210 )
    {
        SvFileStream aStream( "d:\\tmp\\dump.sbx", STREAM_STD_WRITE );
        SfxBroadcaster::Enable(sal_False);
        for ( sal_uIntPtr n = 0; n < rVars.Count(); ++n )
        {
            SbxVariable *pVar = rVars.GetObject(n);
            SbxObject *pObj = PTR_CAST(SbxObject, pVar);
            sal_uInt16 nFlags = pVar->GetFlags();
            pVar->SetFlag(SBX_NO_BROADCAST);
            if ( pObj )
                pObj->Dump(aStream);
            else if ( !pVar->GetParent() || !pVar->GetParent()->ISA(SbxObject) )
                pVar->Dump(aStream);
            pVar->SetFlags(nFlags);
        }
        SfxBroadcaster::Enable(sal_True);
    }
#endif
#endif
    bInGarbageCollection = sal_False;
}