xref: /aoo41x/main/basic/source/sbx/sbxobj.cxx (revision cdf0e10c)
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