xref: /trunk/main/basic/source/sbx/sbxobj.cxx (revision 1ecadb572e7010ff3b3382ad9bf179dbc6efadbb)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_basic.hxx"
30 #include <tools/stream.hxx>
31 #include <vcl/sound.hxx>
32 #include <basic/sbx.hxx>
33 #include <basic/sbxbase.hxx>
34 #include "sbxres.hxx"
35 #include <svl/brdcst.hxx>
36 
37 TYPEINIT1(SbxMethod,SbxVariable)
38 TYPEINIT1(SbxProperty,SbxVariable)
39 TYPEINIT2(SbxObject,SbxVariable,SfxListener)
40 
41 static const char* pNameProp;               // Name-Property
42 static const char* pParentProp;             // Parent-Property
43 
44 static sal_uInt16 nNameHash = 0, nParentHash = 0;
45 
46 /////////////////////////////////////////////////////////////////////////
47 
48 /////////////////////////////////////////////////////////////////////////
49 
50 SbxObject::SbxObject( const XubString& rClass )
51          : SbxVariable( SbxOBJECT ), aClassName( rClass )
52 {
53     aData.pObj = this;
54     if( !nNameHash )
55     {
56         pNameProp = GetSbxRes( STRING_NAMEPROP );
57         pParentProp = GetSbxRes( STRING_PARENTPROP );
58         nNameHash = MakeHashCode( String::CreateFromAscii( pNameProp ) );
59         nParentHash = MakeHashCode( String::CreateFromAscii( pParentProp ) );
60     }
61     SbxObject::Clear();
62     SbxObject::SetName( rClass );
63 }
64 
65 SbxObject::SbxObject( const SbxObject& rObj )
66     : SvRefBase( rObj ), SbxVariable( rObj.GetType() ),
67       SfxListener( rObj )
68 {
69     *this = rObj;
70 }
71 
72 SbxObject& SbxObject::operator=( const SbxObject& r )
73 {
74     if( &r != this )
75     {
76         SbxVariable::operator=( r );
77         aClassName = r.aClassName;
78         pMethods   = new SbxArray;
79         pProps     = new SbxArray;
80         pObjs      = new SbxArray( SbxOBJECT );
81         // Die Arrays werden kopiert, die Inhalte uebernommen
82         *pMethods  = *r.pMethods;
83         *pProps    = *r.pProps;
84         *pObjs     = *r.pObjs;
85         // Da die Variablen uebernommen wurden, ist dies OK
86         pDfltProp  = r.pDfltProp;
87         SetName( r.GetName() );
88         SetFlags( r.GetFlags() );
89         SetModified( sal_True );
90     }
91     return *this;
92 }
93 
94 static void CheckParentsOnDelete( SbxObject* pObj, SbxArray* p )
95 {
96     for( sal_uInt16 i = 0; i < p->Count(); i++ )
97     {
98         SbxVariableRef& rRef = p->GetRef( i );
99         if( rRef->IsBroadcaster() )
100             pObj->EndListening( rRef->GetBroadcaster(), sal_True );
101         // Hat das Element mehr als eine Referenz und noch einen Listener?
102         if( rRef->GetRefCount() > 1 )
103         {
104             rRef->SetParent( NULL );
105             DBG_ASSERT( !rRef->IsBroadcaster() || rRef->GetBroadcaster().GetListenerCount(), "Object element with dangling parent" );
106         }
107     }
108 }
109 
110 SbxObject::~SbxObject()
111 {
112     CheckParentsOnDelete( this, pProps );
113     CheckParentsOnDelete( this, pMethods );
114     CheckParentsOnDelete( this, pObjs );
115 
116     // avoid handling in ~SbxVariable as SBX_DIM_AS_NEW == SBX_GBLSEARCH
117     ResetFlag( SBX_DIM_AS_NEW );
118 }
119 
120 SbxDataType SbxObject::GetType() const
121 {
122     return SbxOBJECT;
123 }
124 
125 SbxClassType SbxObject::GetClass() const
126 {
127     return SbxCLASS_OBJECT;
128 }
129 
130 void SbxObject::Clear()
131 {
132     pMethods   = new SbxArray;
133     pProps     = new SbxArray;
134     pObjs      = new SbxArray( SbxOBJECT );
135     SbxVariable* p;
136     p = Make( String::CreateFromAscii( pNameProp ), SbxCLASS_PROPERTY, SbxSTRING );
137     p->SetFlag( SBX_DONTSTORE );
138     p = Make( String::CreateFromAscii( pParentProp ), SbxCLASS_PROPERTY, SbxOBJECT );
139     p->ResetFlag( SBX_WRITE );
140     p->SetFlag( SBX_DONTSTORE );
141     pDfltProp  = NULL;
142     SetModified( sal_False );
143 }
144 
145 void SbxObject::SFX_NOTIFY( SfxBroadcaster&, const TypeId&,
146                             const SfxHint& rHint, const TypeId& )
147 {
148     const SbxHint* p = PTR_CAST(SbxHint,&rHint);
149     if( p )
150     {
151         sal_uIntPtr nId = p->GetId();
152         sal_Bool bRead  = sal_Bool( nId == SBX_HINT_DATAWANTED );
153         sal_Bool bWrite = sal_Bool( nId == SBX_HINT_DATACHANGED );
154         SbxVariable* pVar = p->GetVar();
155         if( bRead || bWrite )
156         {
157             XubString aVarName( pVar->GetName() );
158             sal_uInt16 nHash_ = MakeHashCode( aVarName );
159             if( nHash_ == nNameHash
160              && aVarName.EqualsIgnoreCaseAscii( pNameProp ) )
161             {
162                 if( bRead )
163                     pVar->PutString( GetName() );
164                 else
165                     SetName( pVar->GetString() );
166             }
167             else if( nHash_ == nParentHash
168              && aVarName.EqualsIgnoreCaseAscii( pParentProp ) )
169             {
170                 SbxObject* p_ = GetParent();
171                 if( !p_ )
172                     p_ = this;
173                 pVar->PutObject( p_ );
174             }
175         }
176     }
177 }
178 
179 sal_Bool SbxObject::IsClass( const XubString& rName ) const
180 {
181     return sal_Bool( aClassName.EqualsIgnoreCaseAscii( rName ) );
182 }
183 
184 SbxVariable* SbxObject::FindUserData( sal_uInt32 nData )
185 {
186     if( !GetAll( SbxCLASS_DONTCARE ) )
187         return NULL;
188 
189     SbxVariable* pRes = pMethods->FindUserData( nData );
190     if( !pRes )
191         pRes = pProps->FindUserData( nData );
192     if( !pRes )
193         pRes = pObjs->FindUserData( nData );
194     // Search in den Parents?
195     if( !pRes && IsSet( SBX_GBLSEARCH ) )
196     {
197         SbxObject* pCur = this;
198         while( !pRes && pCur->pParent )
199         {
200             // Ich selbst bin schon durchsucht worden!
201             sal_uInt16 nOwn = pCur->GetFlags();
202             pCur->ResetFlag( SBX_EXTSEARCH );
203             // Ich suche bereits global!
204             sal_uInt16 nPar = pCur->pParent->GetFlags();
205             pCur->pParent->ResetFlag( SBX_GBLSEARCH );
206             pRes = pCur->pParent->FindUserData( nData );
207             pCur->SetFlags( nOwn );
208             pCur->pParent->SetFlags( nPar );
209             pCur = pCur->pParent;
210         }
211     }
212     return pRes;
213 }
214 
215 SbxVariable* SbxObject::Find( const XubString& rName, SbxClassType t )
216 {
217 #ifdef DBG_UTIL
218     static sal_uInt16 nLvl = 0;
219     static const char* pCls[] =
220     { "DontCare","Array","Value","Variable","Method","Property","Object" };
221     ByteString aNameStr1( (const UniString&)rName, RTL_TEXTENCODING_ASCII_US );
222     ByteString aNameStr2( (const UniString&)SbxVariable::GetName(), RTL_TEXTENCODING_ASCII_US );
223     DbgOutf( "SBX: Search %.*s %s %s in %s",
224         nLvl++, "                              ",
225         ( t >= SbxCLASS_DONTCARE && t <= SbxCLASS_OBJECT )
226          ? pCls[ t-1 ] : "Unknown class", aNameStr1.GetBuffer(), aNameStr1.GetBuffer() );
227 #endif
228 
229     if( !GetAll( t ) )
230         return NULL;
231     SbxVariable* pRes = NULL;
232     pObjs->SetFlag( SBX_EXTSEARCH );
233     if( t == SbxCLASS_DONTCARE )
234     {
235         pRes = pMethods->Find( rName, SbxCLASS_METHOD );
236         if( !pRes )
237             pRes = pProps->Find( rName, SbxCLASS_PROPERTY );
238         if( !pRes )
239             pRes = pObjs->Find( rName, t );
240     }
241     else
242     {
243         SbxArray* pArray = NULL;
244         switch( t )
245         {
246             case SbxCLASS_VARIABLE:
247             case SbxCLASS_PROPERTY: pArray = pProps;    break;
248             case SbxCLASS_METHOD:   pArray = pMethods;  break;
249             case SbxCLASS_OBJECT:   pArray = pObjs;     break;
250             default:
251                 DBG_ASSERT( !this, "Ungueltige SBX-Klasse" );
252         }
253         if( pArray )
254             pRes = pArray->Find( rName, t );
255     }
256     // Extended Search im Objekt-Array?
257     // Fuer Objekte und DontCare ist das Objektarray bereits
258     // durchsucht worden
259     if( !pRes && ( t == SbxCLASS_METHOD || t == SbxCLASS_PROPERTY ) )
260         pRes = pObjs->Find( rName, t );
261     // Search in den Parents?
262     if( !pRes && IsSet( SBX_GBLSEARCH ) )
263     {
264         SbxObject* pCur = this;
265         while( !pRes && pCur->pParent )
266         {
267             // Ich selbst bin schon durchsucht worden!
268             sal_uInt16 nOwn = pCur->GetFlags();
269             pCur->ResetFlag( SBX_EXTSEARCH );
270             // Ich suche bereits global!
271             sal_uInt16 nPar = pCur->pParent->GetFlags();
272             pCur->pParent->ResetFlag( SBX_GBLSEARCH );
273             pRes = pCur->pParent->Find( rName, t );
274             pCur->SetFlags( nOwn );
275             pCur->pParent->SetFlags( nPar );
276             pCur = pCur->pParent;
277         }
278     }
279 #ifdef DBG_UTIL
280     nLvl--;
281     if( pRes )
282     {
283         ByteString aNameStr3( (const UniString&)rName, RTL_TEXTENCODING_ASCII_US );
284         ByteString aNameStr4( (const UniString&)SbxVariable::GetName(), RTL_TEXTENCODING_ASCII_US );
285         DbgOutf( "SBX: Found %.*s %s in %s",
286             nLvl, "                              ", aNameStr3.GetBuffer(), aNameStr4.GetBuffer() );
287     }
288 #endif
289     return pRes;
290 }
291 
292 // Kurzform: Die Parent-Kette wird durchsucht
293 // Das ganze rekursiv, da Call() ueberladen sein kann
294 // Qualified Names sind zugelassen
295 
296 sal_Bool SbxObject::Call( const XubString& rName, SbxArray* pParam )
297 {
298     SbxVariable* pMeth = FindQualified( rName, SbxCLASS_DONTCARE);
299     if( pMeth && pMeth->ISA(SbxMethod) )
300     {
301         // FindQualified() koennte schon zugeschlagen haben!
302         if( pParam )
303             pMeth->SetParameters( pParam );
304         pMeth->Broadcast( SBX_HINT_DATAWANTED );
305         pMeth->SetParameters( NULL );
306         return sal_True;
307     }
308     SetError( SbxERR_NO_METHOD );
309     return sal_False;
310 }
311 
312 SbxProperty* SbxObject::GetDfltProperty()
313 {
314     if ( !pDfltProp && aDfltPropName.Len() )
315     {
316         pDfltProp = (SbxProperty*) Find( aDfltPropName, SbxCLASS_PROPERTY );
317         if( !pDfltProp )
318             pDfltProp = (SbxProperty*) Make( aDfltPropName, SbxCLASS_PROPERTY, SbxVARIANT );
319     }
320     return pDfltProp;
321 }
322 void SbxObject::SetDfltProperty( const XubString& rName )
323 {
324     if ( rName != aDfltPropName )
325         pDfltProp = NULL;
326     aDfltPropName = rName;
327     SetModified( sal_True );
328 }
329 
330 void SbxObject::SetDfltProperty( SbxProperty* p )
331 {
332     if( p )
333     {
334         sal_uInt16 n;
335         SbxArray* pArray = FindVar( p, n );
336         pArray->Put( p, n );
337         if( p->GetParent() != this )
338             p->SetParent( this );
339         Broadcast( SBX_HINT_OBJECTCHANGED );
340     }
341     pDfltProp = p;
342     SetModified( sal_True );
343 }
344 
345 // Suchen einer bereits vorhandenen Variablen. Falls sie gefunden wurde,
346 // wird der Index gesetzt, sonst wird der Count des Arrays geliefert.
347 // In jedem Fall wird das korrekte Array geliefert.
348 
349 SbxArray* SbxObject::FindVar( SbxVariable* pVar, sal_uInt16& nArrayIdx )
350 {
351     SbxArray* pArray = NULL;
352     if( pVar ) switch( pVar->GetClass() )
353     {
354         case SbxCLASS_VARIABLE:
355         case SbxCLASS_PROPERTY: pArray = pProps;    break;
356         case SbxCLASS_METHOD:   pArray = pMethods;  break;
357         case SbxCLASS_OBJECT:   pArray = pObjs;     break;
358         default:
359             DBG_ASSERT( !this, "Ungueltige SBX-Klasse" );
360     }
361     if( pArray )
362     {
363         nArrayIdx = pArray->Count();
364         // ist die Variable per Name vorhanden?
365         pArray->ResetFlag( SBX_EXTSEARCH );
366         SbxVariable* pOld = pArray->Find( pVar->GetName(), pVar->GetClass() );
367         if( pOld )
368           for( sal_uInt16 i = 0; i < pArray->Count(); i++ )
369         {
370             SbxVariableRef& rRef = pArray->GetRef( i );
371             if( (SbxVariable*) rRef == pOld )
372             {
373                 nArrayIdx = i; break;
374             }
375         }
376     }
377     return pArray;
378 }
379 
380 // Falls ein neues Objekt eingerichtet wird, wird es, falls es bereits
381 // eines mit diesem Namen gibt, indiziert.
382 
383 SbxVariable* SbxObject::Make( const XubString& rName, SbxClassType ct, SbxDataType dt )
384 {
385     // Ist das Objekt bereits vorhanden?
386     SbxArray* pArray = NULL;
387     switch( ct )
388     {
389         case SbxCLASS_VARIABLE:
390         case SbxCLASS_PROPERTY: pArray = pProps;    break;
391         case SbxCLASS_METHOD:   pArray = pMethods;  break;
392         case SbxCLASS_OBJECT:   pArray = pObjs;     break;
393         default:
394             DBG_ASSERT( !this, "Ungueltige SBX-Klasse" );
395     }
396     if( !pArray )
397         return NULL;
398     // Collections duerfen gleichnamige Objekte enthalten
399     if( !( ct == SbxCLASS_OBJECT && ISA(SbxCollection) ) )
400     {
401         SbxVariable* pRes = pArray->Find( rName, ct );
402         if( pRes )
403         {
404 /* Wegen haeufiger Probleme (z.B. #67000) erstmal ganz raus
405 #ifdef DBG_UTIL
406             if( pRes->GetHashCode() != nNameHash
407              && pRes->GetHashCode() != nParentHash )
408             {
409                 XubString aMsg( "SBX-Element \"" );
410                 aMsg += pRes->GetName();
411                 aMsg += "\"\n in Objekt \"";
412                 aMsg += GetName();
413                 aMsg += "\" bereits vorhanden";
414                 DbgError( (const char*)aMsg.GetStr() );
415             }
416 #endif
417 */
418             return pRes;
419         }
420     }
421     SbxVariable* pVar = NULL;
422     switch( ct )
423     {
424         case SbxCLASS_VARIABLE:
425         case SbxCLASS_PROPERTY:
426             pVar = new SbxProperty( rName, dt );
427             break;
428         case SbxCLASS_METHOD:
429             pVar = new SbxMethod( rName, dt );
430             break;
431         case SbxCLASS_OBJECT:
432             pVar = CreateObject( rName );
433             break;
434         default: break;
435     }
436     pVar->SetParent( this );
437     pArray->Put( pVar, pArray->Count() );
438     SetModified( sal_True );
439     // Das Objekt lauscht immer
440     StartListening( pVar->GetBroadcaster(), sal_True );
441     Broadcast( SBX_HINT_OBJECTCHANGED );
442     return pVar;
443 }
444 
445 SbxObject* SbxObject::MakeObject( const XubString& rName, const XubString& rClass )
446 {
447     // Ist das Objekt bereits vorhanden?
448     if( !ISA(SbxCollection) )
449     {
450         SbxVariable* pRes = pObjs->Find( rName, SbxCLASS_OBJECT );
451         if( pRes )
452         {
453 /* Wegen haeufiger Probleme (z.B. #67000) erstmal ganz raus
454 #ifdef DBG_UTIL
455             if( pRes->GetHashCode() != nNameHash
456              && pRes->GetHashCode() != nParentHash )
457             {
458                 XubString aMsg( "SBX-Objekt \"" );
459                 aMsg += pRes->GetName();
460                 aMsg += "\"\n in Objekt \"";
461                 aMsg += GetName();
462                 aMsg += "\" bereits vorhanden";
463                 DbgError( (const char*)aMsg.GetStr() );
464             }
465 #endif
466 */
467             return PTR_CAST(SbxObject,pRes);
468         }
469     }
470     SbxObject* pVar = CreateObject( rClass );
471     if( pVar )
472     {
473         pVar->SetName( rName );
474         pVar->SetParent( this );
475         pObjs->Put( pVar, pObjs->Count() );
476         SetModified( sal_True );
477         // Das Objekt lauscht immer
478         StartListening( pVar->GetBroadcaster(), sal_True );
479         Broadcast( SBX_HINT_OBJECTCHANGED );
480     }
481     return pVar;
482 }
483 
484 void SbxObject::Insert( SbxVariable* pVar )
485 {
486     sal_uInt16 nIdx;
487     SbxArray* pArray = FindVar( pVar, nIdx );
488     if( pArray )
489     {
490         // Hinein damit. Man sollte allerdings auf die Pointer aufpassen!
491         if( nIdx < pArray->Count() )
492         {
493             // dann gibt es dieses Element bereits
494             // Bei Collections duerfen gleichnamige Objekte hinein
495             if( pArray == pObjs && ISA(SbxCollection) )
496                 nIdx = pArray->Count();
497             else
498             {
499                 SbxVariable* pOld = pArray->Get( nIdx );
500                 // schon drin: ueberschreiben
501                 if( pOld == pVar )
502                     return;
503 
504 /* Wegen haeufiger Probleme (z.B. #67000) erstmal ganz raus
505 #ifdef DBG_UTIL
506                 if( pOld->GetHashCode() != nNameHash
507                  && pOld->GetHashCode() != nParentHash )
508                 {
509                     XubString aMsg( "SBX-Element \"" );
510                     aMsg += pVar->GetName();
511                     aMsg += "\"\n in Objekt \"";
512                     aMsg += GetName();
513                     aMsg += "\" bereits vorhanden";
514                     DbgError( (const char*)aMsg.GetStr() );
515                 }
516 #endif
517 */
518                 EndListening( pOld->GetBroadcaster(), sal_True );
519                 if( pVar->GetClass() == SbxCLASS_PROPERTY )
520                 {
521                     if( pOld == pDfltProp )
522                         pDfltProp = (SbxProperty*) pVar;
523                 }
524             }
525         }
526         StartListening( pVar->GetBroadcaster(), sal_True );
527         pArray->Put( pVar, nIdx );
528         if( pVar->GetParent() != this )
529             pVar->SetParent( this );
530         SetModified( sal_True );
531         Broadcast( SBX_HINT_OBJECTCHANGED );
532 #ifdef DBG_UTIL
533     static const char* pCls[] =
534     { "DontCare","Array","Value","Variable","Method","Property","Object" };
535     XubString aVarName( pVar->GetName() );
536     if ( !aVarName.Len() && pVar->ISA(SbxObject) )
537         aVarName = PTR_CAST(SbxObject,pVar)->GetClassName();
538     ByteString aNameStr1( (const UniString&)aVarName, RTL_TEXTENCODING_ASCII_US );
539     ByteString aNameStr2( (const UniString&)SbxVariable::GetName(), RTL_TEXTENCODING_ASCII_US );
540     DbgOutf( "SBX: Insert %s %s in %s",
541         ( pVar->GetClass() >= SbxCLASS_DONTCARE &&
542           pVar->GetClass() <= SbxCLASS_OBJECT )
543             ? pCls[ pVar->GetClass()-1 ] : "Unknown class", aNameStr1.GetBuffer(), aNameStr1.GetBuffer() );
544 #endif
545     }
546 }
547 
548 // AB 23.4.1997, Optimierung, Einfuegen ohne Ueberpruefung auf doppelte
549 // Eintraege und ohne Broadcasts, wird nur in SO2/auto.cxx genutzt
550 void SbxObject::QuickInsert( SbxVariable* pVar )
551 {
552     SbxArray* pArray = NULL;
553     if( pVar )
554     {
555         switch( pVar->GetClass() )
556         {
557             case SbxCLASS_VARIABLE:
558             case SbxCLASS_PROPERTY: pArray = pProps;    break;
559             case SbxCLASS_METHOD:   pArray = pMethods;  break;
560             case SbxCLASS_OBJECT:   pArray = pObjs;     break;
561             default:
562                 DBG_ASSERT( !this, "Ungueltige SBX-Klasse" );
563         }
564     }
565     if( pArray )
566     {
567         StartListening( pVar->GetBroadcaster(), sal_True );
568         pArray->Put( pVar, pArray->Count() );
569         if( pVar->GetParent() != this )
570             pVar->SetParent( this );
571         SetModified( sal_True );
572 #ifdef DBG_UTIL
573     static const char* pCls[] =
574     { "DontCare","Array","Value","Variable","Method","Property","Object" };
575     XubString aVarName( pVar->GetName() );
576     if ( !aVarName.Len() && pVar->ISA(SbxObject) )
577         aVarName = PTR_CAST(SbxObject,pVar)->GetClassName();
578     ByteString aNameStr1( (const UniString&)aVarName, RTL_TEXTENCODING_ASCII_US );
579     ByteString aNameStr2( (const UniString&)SbxVariable::GetName(), RTL_TEXTENCODING_ASCII_US );
580     DbgOutf( "SBX: Insert %s %s in %s",
581         ( pVar->GetClass() >= SbxCLASS_DONTCARE &&
582           pVar->GetClass() <= SbxCLASS_OBJECT )
583             ? pCls[ pVar->GetClass()-1 ] : "Unknown class", aNameStr1.GetBuffer(), aNameStr1.GetBuffer() );
584 #endif
585     }
586 }
587 
588 // AB 23.3.1997, Spezial-Methode, gleichnamige Controls zulassen
589 void SbxObject::VCPtrInsert( SbxVariable* pVar )
590 {
591     SbxArray* pArray = NULL;
592     if( pVar )
593     {
594         switch( pVar->GetClass() )
595         {
596             case SbxCLASS_VARIABLE:
597             case SbxCLASS_PROPERTY: pArray = pProps;    break;
598             case SbxCLASS_METHOD:   pArray = pMethods;  break;
599             case SbxCLASS_OBJECT:   pArray = pObjs;     break;
600             default:
601                 DBG_ASSERT( !this, "Ungueltige SBX-Klasse" );
602         }
603     }
604     if( pArray )
605     {
606         StartListening( pVar->GetBroadcaster(), sal_True );
607         pArray->Put( pVar, pArray->Count() );
608         if( pVar->GetParent() != this )
609             pVar->SetParent( this );
610         SetModified( sal_True );
611         Broadcast( SBX_HINT_OBJECTCHANGED );
612     }
613 }
614 
615 void SbxObject::Remove( const XubString& rName, SbxClassType t )
616 {
617     Remove( SbxObject::Find( rName, t ) );
618 }
619 
620 void SbxObject::Remove( SbxVariable* pVar )
621 {
622     sal_uInt16 nIdx;
623     SbxArray* pArray = FindVar( pVar, nIdx );
624     if( pArray && nIdx < pArray->Count() )
625     {
626 #ifdef DBG_UTIL
627     XubString aVarName( pVar->GetName() );
628     if ( !aVarName.Len() && pVar->ISA(SbxObject) )
629         aVarName = PTR_CAST(SbxObject,pVar)->GetClassName();
630     ByteString aNameStr1( (const UniString&)aVarName, RTL_TEXTENCODING_ASCII_US );
631     ByteString aNameStr2( (const UniString&)SbxVariable::GetName(), RTL_TEXTENCODING_ASCII_US );
632 #endif
633         SbxVariableRef pVar_ = pArray->Get( nIdx );
634         if( pVar_->IsBroadcaster() )
635             EndListening( pVar_->GetBroadcaster(), sal_True );
636         if( (SbxVariable*) pVar_ == pDfltProp )
637             pDfltProp = NULL;
638         pArray->Remove( nIdx );
639         if( pVar_->GetParent() == this )
640             pVar_->SetParent( NULL );
641         SetModified( sal_True );
642         Broadcast( SBX_HINT_OBJECTCHANGED );
643     }
644 }
645 
646 // AB 23.3.1997, Loeschen per Pointer fuer Controls (doppelte Namen!)
647 void SbxObject::VCPtrRemove( SbxVariable* pVar )
648 {
649     sal_uInt16 nIdx;
650     // Neu FindVar-Methode, sonst identisch mit normaler Methode
651     SbxArray* pArray = VCPtrFindVar( pVar, nIdx );
652     if( pArray && nIdx < pArray->Count() )
653     {
654         SbxVariableRef xVar = pArray->Get( nIdx );
655         if( xVar->IsBroadcaster() )
656             EndListening( xVar->GetBroadcaster(), sal_True );
657         if( (SbxVariable*) xVar == pDfltProp )
658             pDfltProp = NULL;
659         pArray->Remove( nIdx );
660         if( xVar->GetParent() == this )
661             xVar->SetParent( NULL );
662         SetModified( sal_True );
663         Broadcast( SBX_HINT_OBJECTCHANGED );
664     }
665 }
666 
667 // AB 23.3.1997, Zugehoerige Spezial-Methode, nur ueber Pointer suchen
668 SbxArray* SbxObject::VCPtrFindVar( SbxVariable* pVar, sal_uInt16& nArrayIdx )
669 {
670     SbxArray* pArray = NULL;
671     if( pVar ) switch( pVar->GetClass() )
672     {
673         case SbxCLASS_VARIABLE:
674         case SbxCLASS_PROPERTY: pArray = pProps;    break;
675         case SbxCLASS_METHOD:   pArray = pMethods;  break;
676         case SbxCLASS_OBJECT:   pArray = pObjs;     break;
677         default:
678             DBG_ASSERT( !this, "Ungueltige SBX-Klasse" );
679     }
680     if( pArray )
681     {
682         nArrayIdx = pArray->Count();
683         for( sal_uInt16 i = 0; i < pArray->Count(); i++ )
684         {
685             SbxVariableRef& rRef = pArray->GetRef( i );
686             if( (SbxVariable*) rRef == pVar )
687             {
688                 nArrayIdx = i; break;
689             }
690         }
691     }
692     return pArray;
693 }
694 
695 
696 
697 void SbxObject::SetPos( SbxVariable* pVar, sal_uInt16 nPos )
698 {
699     sal_uInt16 nIdx;
700     SbxArray* pArray = FindVar( pVar, nIdx );
701     if( pArray )
702     {
703         if( nPos >= pArray->Count() )
704             nPos = pArray->Count() - 1;
705         if( nIdx < ( pArray->Count() - 1 ) )
706         {
707             SbxVariableRef refVar = pArray->Get( nIdx );
708             pArray->Remove( nIdx );
709             pArray->Insert( refVar, nPos );
710         }
711     }
712 //  SetModified( sal_True );
713 //  Broadcast( SBX_HINT_OBJECTCHANGED );
714 }
715 
716 static sal_Bool LoadArray( SvStream& rStrm, SbxObject* pThis, SbxArray* pArray )
717 {
718     SbxArrayRef p = (SbxArray*) SbxBase::Load( rStrm );
719     if( !p.Is() )
720         return sal_False;
721     for( sal_uInt16 i = 0; i < p->Count(); i++ )
722     {
723         SbxVariableRef& r = p->GetRef( i );
724         SbxVariable* pVar = r;
725         if( pVar )
726         {
727             pVar->SetParent( pThis );
728             pThis->StartListening( pVar->GetBroadcaster(), sal_True );
729         }
730     }
731     pArray->Merge( p );
732     return sal_True;
733 }
734 
735 // Der Load eines Objekts ist additiv!
736 
737 sal_Bool SbxObject::LoadData( SvStream& rStrm, sal_uInt16 nVer )
738 {
739     // Hilfe fuer das Einlesen alter Objekte: einfach sal_True zurueck,
740     // LoadPrivateData() muss Default-Zustand herstellen
741     if( !nVer )
742         return sal_True;
743 
744     pDfltProp = NULL;
745     if( !SbxVariable::LoadData( rStrm, nVer ) )
746         return sal_False;
747     // Wenn kein fremdes Objekt enthalten ist, uns selbst eintragen
748     if( aData.eType == SbxOBJECT && !aData.pObj )
749         aData.pObj = this;
750     sal_uInt32 nSize;
751     XubString aDfltProp;
752     rStrm.ReadByteString( aClassName, RTL_TEXTENCODING_ASCII_US );
753     rStrm.ReadByteString( aDfltProp, RTL_TEXTENCODING_ASCII_US );
754     sal_uIntPtr nPos = rStrm.Tell();
755     rStrm >> nSize;
756     if( !LoadPrivateData( rStrm, nVer ) )
757         return sal_False;
758     sal_uIntPtr nNewPos = rStrm.Tell();
759     nPos += nSize;
760     DBG_ASSERT( nPos >= nNewPos, "SBX: Zu viele Daten eingelesen" );
761     if( nPos != nNewPos )
762         rStrm.Seek( nPos );
763     if( !LoadArray( rStrm, this, pMethods )
764      || !LoadArray( rStrm, this, pProps )
765      || !LoadArray( rStrm, this, pObjs ) )
766         return sal_False;
767     // Properties setzen
768     if( aDfltProp.Len() )
769         pDfltProp = (SbxProperty*) pProps->Find( aDfltProp, SbxCLASS_PROPERTY );
770     SetModified( sal_False );
771     return sal_True;
772 }
773 
774 sal_Bool SbxObject::StoreData( SvStream& rStrm ) const
775 {
776     if( !SbxVariable::StoreData( rStrm ) )
777         return sal_False;
778     XubString aDfltProp;
779     if( pDfltProp )
780         aDfltProp = pDfltProp->GetName();
781     rStrm.WriteByteString( aClassName, RTL_TEXTENCODING_ASCII_US );
782     rStrm.WriteByteString( aDfltProp, RTL_TEXTENCODING_ASCII_US );
783     sal_uIntPtr nPos = rStrm.Tell();
784     rStrm << (sal_uInt32) 0L;
785     if( !StorePrivateData( rStrm ) )
786         return sal_False;
787     sal_uIntPtr nNew = rStrm.Tell();
788     rStrm.Seek( nPos );
789     rStrm << (sal_uInt32) ( nNew - nPos );
790     rStrm.Seek( nNew );
791     if( !pMethods->Store( rStrm ) )
792         return sal_False;
793     if( !pProps->Store( rStrm ) )
794         return sal_False;
795     if( !pObjs->Store( rStrm ) )
796         return sal_False;
797     ((SbxObject*) this)->SetModified( sal_False );
798     return sal_True;
799 }
800 
801 XubString SbxObject::GenerateSource( const XubString &rLinePrefix,
802                                   const SbxObject* )
803 {
804     // Properties in einem String einsammeln
805     XubString aSource;
806     SbxArrayRef xProps( GetProperties() );
807     bool bLineFeed = false;
808     for ( sal_uInt16 nProp = 0; nProp < xProps->Count(); ++nProp )
809     {
810         SbxPropertyRef xProp = (SbxProperty*) xProps->Get(nProp);
811         XubString aPropName( xProp->GetName() );
812         if ( xProp->CanWrite()
813          && !( xProp->GetHashCode() == nNameHash
814             && aPropName.EqualsIgnoreCaseAscii( pNameProp ) ) )
815         {
816             // ausser vor dem ersten Property immer einen Umbruch einfuegen
817             if ( bLineFeed )
818                 aSource.AppendAscii( "\n" );
819             else
820                 bLineFeed = true;
821 
822             aSource += rLinePrefix;
823             aSource += '.';
824             aSource += aPropName;
825             aSource.AppendAscii( " = " );
826 
827             // den Property-Wert textuell darstellen
828             switch ( xProp->GetType() )
829             {
830                 case SbxEMPTY:
831                 case SbxNULL:
832                     // kein Wert
833                     break;
834 
835                 case SbxSTRING:
836                 {
837                     // Strings in Anf"uhrungszeichen
838                     aSource.AppendAscii( "\"" );
839                     aSource += xProp->GetString();
840                     aSource.AppendAscii( "\"" );
841                     break;
842                 }
843 
844                 default:
845                 {
846                     // sonstiges wie z.B. Zahlen direkt
847                     aSource += xProp->GetString();
848                     break;
849                 }
850             }
851         }
852     }
853     return aSource;
854 }
855 
856 static sal_Bool CollectAttrs( const SbxBase* p, XubString& rRes )
857 {
858     XubString aAttrs;
859     if( p->IsHidden() )
860         aAttrs.AssignAscii( "Hidden" );
861     if( p->IsSet( SBX_EXTSEARCH ) )
862     {
863         if( aAttrs.Len() )
864             aAttrs += ',';
865         aAttrs.AppendAscii( "ExtSearch" );
866     }
867     if( !p->IsVisible() )
868     {
869         if( aAttrs.Len() )
870             aAttrs += ',';
871         aAttrs.AppendAscii( "Invisible" );
872     }
873     if( p->IsSet( SBX_DONTSTORE ) )
874     {
875         if( aAttrs.Len() )
876             aAttrs += ',';
877         aAttrs.AppendAscii( "DontStore" );
878     }
879     if( aAttrs.Len() )
880     {
881         rRes.AssignAscii( " (" );
882         rRes += aAttrs;
883         rRes += ')';
884         return sal_True;
885     }
886     else
887     {
888         rRes.Erase();
889         return sal_False;
890     }
891 }
892 
893 void SbxObject::Dump( SvStream& rStrm, sal_Bool bFill )
894 {
895     // Einr"uckung
896     static sal_uInt16 nLevel = 0;
897     if ( nLevel > 10 )
898     {
899         rStrm << "<too deep>" << endl;
900         return;
901     }
902     ++nLevel;
903     String aIndent;
904     for ( sal_uInt16 n = 1; n < nLevel; ++n )
905         aIndent.AppendAscii( "    " );
906 
907     // ggf. Objekt vervollst"andigen
908     if ( bFill )
909         GetAll( SbxCLASS_DONTCARE );
910 
911     // Daten des Objekts selbst ausgeben
912     ByteString aNameStr( (const UniString&)GetName(), RTL_TEXTENCODING_ASCII_US );
913     ByteString aClassNameStr( (const UniString&)aClassName, RTL_TEXTENCODING_ASCII_US );
914     rStrm << "Object( "
915           << ByteString::CreateFromInt64( (sal_uIntPtr) this ).GetBuffer() << "=='"
916           << ( aNameStr.Len() ? aNameStr.GetBuffer() : "<unnamed>" ) << "', "
917           << "of class '" << aClassNameStr.GetBuffer() << "', "
918           << "counts "
919           << ByteString::CreateFromInt64( GetRefCount() ).GetBuffer()
920           << " refs, ";
921     if ( GetParent() )
922     {
923         ByteString aParentNameStr( (const UniString&)GetName(), RTL_TEXTENCODING_ASCII_US );
924         rStrm << "in parent "
925               << ByteString::CreateFromInt64( (sal_uIntPtr) GetParent() ).GetBuffer()
926               << "=='" << ( aParentNameStr.Len() ? aParentNameStr.GetBuffer() : "<unnamed>" ) << "'";
927     }
928     else
929         rStrm << "no parent ";
930     rStrm << " )" << endl;
931     ByteString aIndentNameStr( (const UniString&)aIndent, RTL_TEXTENCODING_ASCII_US );
932     rStrm << aIndentNameStr.GetBuffer() << "{" << endl;
933 
934     // Flags
935     XubString aAttrs;
936     if( CollectAttrs( this, aAttrs ) )
937     {
938         ByteString aAttrStr( (const UniString&)aAttrs, RTL_TEXTENCODING_ASCII_US );
939         rStrm << aIndentNameStr.GetBuffer() << "- Flags: " << aAttrStr.GetBuffer() << endl;
940     }
941 
942     // Methods
943     rStrm << aIndentNameStr.GetBuffer() << "- Methods:" << endl;
944     for( sal_uInt16 i = 0; i < pMethods->Count(); i++ )
945     {
946         SbxVariableRef& r = pMethods->GetRef( i );
947         SbxVariable* pVar = r;
948         if( pVar )
949         {
950             XubString aLine( aIndent );
951             aLine.AppendAscii( "  - " );
952             aLine += pVar->GetName( SbxNAME_SHORT_TYPES );
953             XubString aAttrs2;
954             if( CollectAttrs( pVar, aAttrs2 ) )
955                 aLine += aAttrs2;
956             if( !pVar->IsA( TYPE(SbxMethod) ) )
957                 aLine.AppendAscii( "  !! Not a Method !!" );
958             rStrm.WriteByteString( aLine, RTL_TEXTENCODING_ASCII_US );
959 
960             // bei Object-Methods auch das Object ausgeben
961             if ( pVar->GetValues_Impl().eType == SbxOBJECT &&
962                     pVar->GetValues_Impl().pObj &&
963                     pVar->GetValues_Impl().pObj != this &&
964                     pVar->GetValues_Impl().pObj != GetParent() )
965             {
966                 rStrm << " contains ";
967                 ((SbxObject*) pVar->GetValues_Impl().pObj)->Dump( rStrm, bFill );
968             }
969             else
970                 rStrm << endl;
971         }
972     }
973 
974     // Properties
975     rStrm << aIndentNameStr.GetBuffer() << "- Properties:" << endl;
976     {
977         for( sal_uInt16 i = 0; i < pProps->Count(); i++ )
978         {
979             SbxVariableRef& r = pProps->GetRef( i );
980             SbxVariable* pVar = r;
981             if( pVar )
982             {
983                 XubString aLine( aIndent );
984                 aLine.AppendAscii( "  - " );
985                 aLine += pVar->GetName( SbxNAME_SHORT_TYPES );
986                 XubString aAttrs3;
987                 if( CollectAttrs( pVar, aAttrs3 ) )
988                     aLine += aAttrs3;
989                 if( !pVar->IsA( TYPE(SbxProperty) ) )
990                     aLine.AppendAscii( "  !! Not a Property !!" );
991                 rStrm.WriteByteString( aLine, RTL_TEXTENCODING_ASCII_US );
992 
993                 // bei Object-Properties auch das Object ausgeben
994                 if ( pVar->GetValues_Impl().eType == SbxOBJECT &&
995                         pVar->GetValues_Impl().pObj &&
996                         pVar->GetValues_Impl().pObj != this &&
997                         pVar->GetValues_Impl().pObj != GetParent() )
998                 {
999                     rStrm << " contains ";
1000                     ((SbxObject*) pVar->GetValues_Impl().pObj)->Dump( rStrm, bFill );
1001                 }
1002                 else
1003                     rStrm << endl;
1004             }
1005         }
1006     }
1007 
1008     // Objects
1009     rStrm << aIndentNameStr.GetBuffer() << "- Objects:" << endl;
1010     {
1011         for( sal_uInt16 i = 0; i < pObjs->Count(); i++ )
1012         {
1013             SbxVariableRef& r = pObjs->GetRef( i );
1014             SbxVariable* pVar = r;
1015             if ( pVar )
1016             {
1017                 rStrm << aIndentNameStr.GetBuffer() << "  - Sub";
1018                 if ( pVar->ISA(SbxObject) )
1019                     ((SbxObject*) pVar)->Dump( rStrm, bFill );
1020                 else if ( pVar->ISA(SbxVariable) )
1021                     ((SbxVariable*) pVar)->Dump( rStrm, bFill );
1022             }
1023         }
1024     }
1025 
1026     rStrm << aIndentNameStr.GetBuffer() << "}" << endl << endl;
1027     --nLevel;
1028 }
1029 
1030 SvDispatch* SbxObject::GetSvDispatch()
1031 {
1032     return NULL;
1033 }
1034 
1035 sal_Bool SbxMethod::Run( SbxValues* pValues )
1036 {
1037     SbxValues aRes;
1038     if( !pValues )
1039         pValues = &aRes;
1040     pValues->eType = SbxVARIANT;
1041     return Get( *pValues );
1042 }
1043 
1044 SbxClassType SbxMethod::GetClass() const
1045 {
1046     return SbxCLASS_METHOD;
1047 }
1048 
1049 SbxClassType SbxProperty::GetClass() const
1050 {
1051     return SbxCLASS_PROPERTY;
1052 }
1053 
1054 void SbxObject::GarbageCollection( sal_uIntPtr nObjects )
1055 
1056 /*  [Beschreibung]
1057 
1058     Diese statische Methode durchsucht die n"achsten 'nObjects' der zur Zeit
1059     existierenden <SbxObject>-Instanzen nach zyklischen Referenzen, die sich
1060     nur noch selbst am Leben erhalten. Ist 'nObjects==0', dann werden
1061     alle existierenden durchsucht.
1062 
1063     zur Zeit nur implementiert: Object -> Parent-Property -> Parent -> Object
1064 */
1065 
1066 {
1067     (void)nObjects;
1068 
1069     static sal_Bool bInGarbageCollection = sal_False;
1070     if ( bInGarbageCollection )
1071         return;
1072     bInGarbageCollection = sal_True;
1073 
1074 #if 0
1075     // erstes Object dieser Runde anspringen
1076     sal_Bool bAll = !nObjects;
1077     if ( bAll )
1078         rObjects.First();
1079     SbxObject *pObj = rObjects.GetCurObject();
1080     if ( !pObj )
1081         pObj = rObjects.First();
1082 
1083     while ( pObj && 0 != nObjects-- )
1084     {
1085         // hat der Parent nur noch 1 Ref-Count?
1086         SbxObject *pParent = PTR_CAST( SbxObject, pObj->GetParent() );
1087         if ( pParent && 1 == pParent->GetRefCount() )
1088         {
1089             // dann alle Properies des Objects durchsuchen
1090             SbxArray *pProps = pObj->GetProperties();
1091             for ( sal_uInt16 n = 0; n < pProps->Count(); ++n )
1092             {
1093                 // verweist die Property auf den Parent des Object?
1094                 SbxVariable *pProp = pProps->Get(n);
1095                 const SbxValues &rValues = pProp->GetValues_Impl();
1096                 if ( SbxOBJECT == rValues.eType &&
1097                      pParent == rValues.pObj )
1098                 {
1099 #ifdef DBG_UTIL
1100                     DbgOutf( "SBX: %s.%s with Object %s was garbage",
1101                              pObj->GetName().GetStr(),
1102                              pProp->GetName().GetStr(),
1103                              pParent->GetName().GetStr() );
1104 #endif
1105                     // dann freigeben
1106                     pProp->SbxValue::Clear();
1107                     Sound::Beep();
1108                     break;
1109                 }
1110             }
1111         }
1112 
1113         // zum n"achsten
1114         pObj = rObjects.Next();
1115         if ( !bAll && !pObj )
1116             pObj = rObjects.First();
1117     }
1118 #endif
1119 
1120 // AB 28.10. Zur 507a vorerst raus, da SfxBroadcaster::Enable() wegfaellt
1121 #if 0
1122 #ifdef DBG_UTIL
1123     SbxVarList_Impl &rVars = GetSbxData_Impl()->aVars;
1124     DbgOutf( "SBX: garbage collector done, %lu objects remainding",
1125              rVars.Count() );
1126     if ( rVars.Count() > 200 && rVars.Count() < 210 )
1127     {
1128         SvFileStream aStream( "d:\\tmp\\dump.sbx", STREAM_STD_WRITE );
1129         SfxBroadcaster::Enable(sal_False);
1130         for ( sal_uIntPtr n = 0; n < rVars.Count(); ++n )
1131         {
1132             SbxVariable *pVar = rVars.GetObject(n);
1133             SbxObject *pObj = PTR_CAST(SbxObject, pVar);
1134             sal_uInt16 nFlags = pVar->GetFlags();
1135             pVar->SetFlag(SBX_NO_BROADCAST);
1136             if ( pObj )
1137                 pObj->Dump(aStream);
1138             else if ( !pVar->GetParent() || !pVar->GetParent()->ISA(SbxObject) )
1139                 pVar->Dump(aStream);
1140             pVar->SetFlags(nFlags);
1141         }
1142         SfxBroadcaster::Enable(sal_True);
1143     }
1144 #endif
1145 #endif
1146     bInGarbageCollection = sal_False;
1147 }
1148 
1149