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_svtools.hxx"
30 #ifndef GCC
31 #endif
32 
33 //_________________________________________________________________________________________________________________
34 //	includes
35 //_________________________________________________________________________________________________________________
36 
37 #include <svtools/menuoptions.hxx>
38 #include <unotools/configmgr.hxx>
39 #include <unotools/configitem.hxx>
40 #include <tools/debug.hxx>
41 #include <com/sun/star/uno/Any.hxx>
42 #include <com/sun/star/uno/Sequence.hxx>
43 #include <vcl/svapp.hxx>
44 
45 #include <rtl/logfile.hxx>
46 #include "itemholder2.hxx"
47 
48 //_________________________________________________________________________________________________________________
49 //	namespaces
50 //_________________________________________________________________________________________________________________
51 
52 using namespace ::utl					;
53 using namespace ::rtl					;
54 using namespace ::osl					;
55 using namespace ::com::sun::star::uno	;
56 
57 //_________________________________________________________________________________________________________________
58 //	const
59 //_________________________________________________________________________________________________________________
60 
61 #define	ROOTNODE_MENU							OUString(RTL_CONSTASCII_USTRINGPARAM("Office.Common/View/Menu"	))
62 #define	DEFAULT_DONTHIDEDISABLEDENTRIES			sal_False
63 #define	DEFAULT_FOLLOWMOUSE						sal_True
64 #define	DEFAULT_MENUICONS						2
65 
66 #define	PROPERTYNAME_DONTHIDEDISABLEDENTRIES	OUString(RTL_CONSTASCII_USTRINGPARAM("DontHideDisabledEntry"	))
67 #define	PROPERTYNAME_FOLLOWMOUSE				OUString(RTL_CONSTASCII_USTRINGPARAM("FollowMouse"				))
68 #define PROPERTYNAME_SHOWICONSINMENUES          OUString(RTL_CONSTASCII_USTRINGPARAM("ShowIconsInMenues"        ))
69 #define PROPERTYNAME_SYSTEMICONSINMENUES        OUString(RTL_CONSTASCII_USTRINGPARAM("IsSystemIconsInMenus"     ))
70 
71 #define	PROPERTYHANDLE_DONTHIDEDISABLEDENTRIES	0
72 #define	PROPERTYHANDLE_FOLLOWMOUSE				1
73 #define PROPERTYHANDLE_SHOWICONSINMENUES        2
74 #define PROPERTYHANDLE_SYSTEMICONSINMENUES      3
75 
76 #define PROPERTYCOUNT                           4
77 
78 #include <tools/link.hxx>
79 #include <tools/list.hxx>
80 DECLARE_LIST( LinkList, Link * )
81 
82 //_________________________________________________________________________________________________________________
83 //	private declarations!
84 //_________________________________________________________________________________________________________________
85 
86 class SvtMenuOptions_Impl : public ConfigItem
87 {
88 	//-------------------------------------------------------------------------------------------------------------
89 	//	private member
90 	//-------------------------------------------------------------------------------------------------------------
91 
92 	private:
93         LinkList    aList;
94 		sal_Bool	m_bDontHideDisabledEntries			;	/// cache "DontHideDisabledEntries" of Menu section
95 		sal_Bool	m_bFollowMouse						;	/// cache "FollowMouse" of Menu section
96 		sal_Int16	m_nMenuIcons						;	/// cache "MenuIcons" of Menu section
97 
98 	//-------------------------------------------------------------------------------------------------------------
99 	//	public methods
100 	//-------------------------------------------------------------------------------------------------------------
101 
102 	public:
103 
104 		//---------------------------------------------------------------------------------------------------------
105 		//	constructor / destructor
106 		//---------------------------------------------------------------------------------------------------------
107 
108 		 SvtMenuOptions_Impl();
109 		~SvtMenuOptions_Impl();
110 
111         void AddListenerLink( const Link& rLink );
112         void RemoveListenerLink( const Link& rLink );
113 
114 		//---------------------------------------------------------------------------------------------------------
115 		//	overloaded methods of baseclass
116 		//---------------------------------------------------------------------------------------------------------
117 
118 		/*-****************************************************************************************************//**
119 			@short		called for notify of configmanager
120 			@descr		These method is called from the ConfigManager before application ends or from the
121 			 			PropertyChangeListener if the sub tree broadcasts changes. You must update your
122 						internal values.
123 
124 			@seealso	baseclass ConfigItem
125 
126 			@param		"seqPropertyNames" is the list of properties which should be updated.
127 			@return		-
128 
129 			@onerror	-
130 		*//*-*****************************************************************************************************/
131 
132     	virtual void Notify( const Sequence< OUString >& seqPropertyNames );
133 
134 		/*-****************************************************************************************************//**
135 			@short		write changes to configuration
136 			@descr		These method writes the changed values into the sub tree
137 						and should always called in our destructor to guarantee consistency of config data.
138 
139 			@seealso	baseclass ConfigItem
140 
141 			@param		-
142 			@return		-
143 
144 			@onerror	-
145 		*//*-*****************************************************************************************************/
146 
147     	virtual void Commit();
148 
149 		//---------------------------------------------------------------------------------------------------------
150 		//	public interface
151 		//---------------------------------------------------------------------------------------------------------
152 
153 		/*-****************************************************************************************************//**
154 			@short		access method to get internal values
155 			@descr		These method give us a chance to regulate acces to ouer internal values.
156 						It's not used in the moment - but it's possible for the feature!
157 
158 			@seealso	-
159 
160 			@param		-
161 			@return		-
162 
163 			@onerror	-
164 		*//*-*****************************************************************************************************/
165 
166 		sal_Bool	IsEntryHidingEnabled() const
167 					{ return m_bDontHideDisabledEntries; }
168 
169 		sal_Bool	IsFollowMouseEnabled() const
170 					{ return m_bFollowMouse; }
171 
172 		sal_Int16	GetMenuIconsState() const
173 					{ return m_nMenuIcons; }
174 
175 		void		SetEntryHidingState	( sal_Bool bState )
176                     {
177                         m_bDontHideDisabledEntries = bState;
178                         SetModified();
179                         for ( sal_uInt16 n=0; n<aList.Count(); n++ )
180                             aList.GetObject(n)->Call( this );
181 						Commit();
182                     }
183 
184 		void		SetFollowMouseState	( sal_Bool bState )
185                     {
186                         m_bFollowMouse = bState;
187                         SetModified();
188                         for ( sal_uInt16 n=0; n<aList.Count(); n++ )
189                             aList.GetObject(n)->Call( this );
190 						Commit();
191                     }
192 
193 		void		SetMenuIconsState ( sal_Int16 bState	)
194                     {
195                         m_nMenuIcons = bState;
196                         SetModified();
197                         for ( sal_uInt16 n=0; n<aList.Count(); n++ )
198                             aList.GetObject(n)->Call( this );
199 						Commit();
200                     }
201 
202 	//-------------------------------------------------------------------------------------------------------------
203 	//	private methods
204 	//-------------------------------------------------------------------------------------------------------------
205 
206 	private:
207 
208 		/*-****************************************************************************************************//**
209 			@short		return list of fix key names of ouer configuration management which represent oue module tree
210 			@descr		These methods return a static const list of key names. We need it to get needed values from our
211 						configuration management.
212 
213 			@seealso	-
214 
215 			@param		-
216 			@return		A list of needed configuration keys is returned.
217 
218 			@onerror	-
219 		*//*-*****************************************************************************************************/
220 
221 		static Sequence< OUString > impl_GetPropertyNames();
222 };
223 
224 //_________________________________________________________________________________________________________________
225 //	definitions
226 //_________________________________________________________________________________________________________________
227 
228 //*****************************************************************************************************************
229 //	constructor
230 //*****************************************************************************************************************
231 SvtMenuOptions_Impl::SvtMenuOptions_Impl()
232 	// Init baseclasses first
233     :	ConfigItem					( ROOTNODE_MENU						)
234 	// Init member then.
235 	,	m_bDontHideDisabledEntries	( DEFAULT_DONTHIDEDISABLEDENTRIES	)
236 	,	m_bFollowMouse				( DEFAULT_FOLLOWMOUSE				)
237 	,   m_nMenuIcons				( DEFAULT_MENUICONS 				)
238 {
239 	// Use our static list of configuration keys to get his values.
240 	Sequence< OUString >	seqNames	= impl_GetPropertyNames();
241 	Sequence< Any >			seqValues	= GetProperties( seqNames )	;
242 
243 	// Safe impossible cases.
244 	// We need values from ALL configuration keys.
245 	// Follow assignment use order of values in relation to our list of key names!
246 	DBG_ASSERT( !(seqNames.getLength()!=seqValues.getLength()), "SvtMenuOptions_Impl::SvtMenuOptions_Impl()\nI miss some values of configuration keys!\n" );
247 
248     sal_Bool bMenuIcons = true;
249     sal_Bool bSystemMenuIcons = true;
250 
251 	// Copy values from list in right order to ouer internal member.
252 	sal_Int32 nPropertyCount	=	seqValues.getLength()	;
253 	sal_Int32 nProperty			=	0						;
254 	for( nProperty=0; nProperty<nPropertyCount; ++nProperty )
255 	{
256 		// Safe impossible cases.
257 		// Check any for valid value.
258 		DBG_ASSERT( !(seqValues[nProperty].hasValue()==sal_False), "SvtMenuOptions_Impl::SvtMenuOptions_Impl()\nInvalid property value for property detected!\n" );
259         switch( nProperty )
260         {
261             case PROPERTYHANDLE_DONTHIDEDISABLEDENTRIES	:	{
262 																DBG_ASSERT(!(seqValues[nProperty].getValueTypeClass()!=TypeClass_BOOLEAN), "SvtMenuOptions_Impl::SvtMenuOptions_Impl()\nWho has changed the value type of \"Office.Common\\View\\Menu\\DontHideDisabledEntry\"?" );
263 																seqValues[nProperty] >>= m_bDontHideDisabledEntries;
264 															}
265 															break;
266 
267             case PROPERTYHANDLE_FOLLOWMOUSE				:	{
268 																DBG_ASSERT(!(seqValues[nProperty].getValueTypeClass()!=TypeClass_BOOLEAN), "SvtMenuOptions_Impl::SvtMenuOptions_Impl()\nWho has changed the value type of \"Office.Common\\View\\Menu\\FollowMouse\"?" );
269 																seqValues[nProperty] >>= m_bFollowMouse;
270 															}
271 															break;
272             case PROPERTYHANDLE_SHOWICONSINMENUES       :   {
273                                                                 DBG_ASSERT(!(seqValues[nProperty].getValueTypeClass()!=TypeClass_BOOLEAN), "SvtMenuOptions_Impl::SvtMenuOptions_Impl()\nWho has changed the value type of \"Office.Common\\View\\Menu\\ShowIconsInMenues\"?" );
274                                                                 seqValues[nProperty] >>= bMenuIcons;
275 															}
276 															break;
277             case PROPERTYHANDLE_SYSTEMICONSINMENUES		:   {
278                                                                 DBG_ASSERT(!(seqValues[nProperty].getValueTypeClass()!=TypeClass_BOOLEAN), "SvtMenuOptions_Impl::SvtMenuOptions_Impl()\nWho has changed the value type of \"Office.Common\\View\\Menu\\IsSystemIconsInMenus\"?" );
279                                                                 seqValues[nProperty] >>= bSystemMenuIcons;
280 															}
281 															break;
282         }
283 	}
284 
285 	m_nMenuIcons = bSystemMenuIcons ? 2 : bMenuIcons;
286 
287 	EnableNotification( seqNames );
288 }
289 
290 //*****************************************************************************************************************
291 //	destructor
292 //*****************************************************************************************************************
293 SvtMenuOptions_Impl::~SvtMenuOptions_Impl()
294 {
295 	// Flush data to configuration!
296 	// User has no chance to do that.
297 	if( IsModified() == sal_True )
298 	{
299 		Commit();
300 	}
301 
302     for ( sal_uInt16 n=0; n<aList.Count(); )
303         delete aList.Remove(n);
304 }
305 
306 //*****************************************************************************************************************
307 //	public method
308 //*****************************************************************************************************************
309 void SvtMenuOptions_Impl::Notify( const Sequence< OUString >& seqPropertyNames )
310 {
311 	// Use given list of updated properties to get his values from configuration directly!
312 	Sequence< Any > seqValues = GetProperties( seqPropertyNames );
313 	// Safe impossible cases.
314 	// We need values from ALL notified configuration keys.
315 	DBG_ASSERT( !(seqPropertyNames.getLength()!=seqValues.getLength()), "SvtMenuOptions_Impl::Notify()\nI miss some values of configuration keys!\n" );
316 
317 	sal_Bool bMenuSettingsChanged = sal_False;
318     sal_Bool bMenuIcons = sal_True;
319     sal_Bool bSystemMenuIcons = sal_True;
320     if (m_nMenuIcons == 2)
321         bMenuIcons = (sal_Bool)(Application::GetSettings().GetStyleSettings().GetUseImagesInMenus());
322     else
323     {
324         bSystemMenuIcons = sal_False;
325         bMenuIcons = m_nMenuIcons ? sal_True : sal_False;
326     }
327 
328 	// Step over list of property names and get right value from coreesponding value list to set it on internal members!
329 	sal_Int32 nCount = seqPropertyNames.getLength();
330 	for( sal_Int32 nProperty=0; nProperty<nCount; ++nProperty )
331 	{
332 		if( seqPropertyNames[nProperty] == PROPERTYNAME_DONTHIDEDISABLEDENTRIES )
333 		{
334 			DBG_ASSERT(!(seqValues[nProperty].getValueTypeClass()!=TypeClass_BOOLEAN), "SvtMenuOptions_Impl::Notify()\nWho has changed the value type of \"Office.Common\\View\\Menu\\DontHideDisabledEntry\"?" );
335 			seqValues[nProperty] >>= m_bDontHideDisabledEntries;
336 		}
337 		else if( seqPropertyNames[nProperty] == PROPERTYNAME_FOLLOWMOUSE )
338 		{
339 			DBG_ASSERT(!(seqValues[nProperty].getValueTypeClass()!=TypeClass_BOOLEAN), "SvtMenuOptions_Impl::Notify()\nWho has changed the value type of \"Office.Common\\View\\Menu\\FollowMouse\"?" );
340 			seqValues[nProperty] >>= m_bFollowMouse;
341 		}
342         else if( seqPropertyNames[nProperty] == PROPERTYNAME_SHOWICONSINMENUES )
343 		{
344             DBG_ASSERT(!(seqValues[nProperty].getValueTypeClass()!=TypeClass_BOOLEAN), "SvtMenuOptions_Impl::SvtMenuOptions_Impl()\nWho has changed the value type of \"Office.Common\\View\\Menu\\ShowIconsInMenues\"?" );
345             bMenuSettingsChanged = seqValues[nProperty] >>= bMenuIcons;
346         }
347         else if( seqPropertyNames[nProperty] == PROPERTYNAME_SYSTEMICONSINMENUES )
348 		{
349             DBG_ASSERT(!(seqValues[nProperty].getValueTypeClass()!=TypeClass_BOOLEAN), "SvtMenuOptions_Impl::SvtMenuOptions_Impl()\nWho has changed the value type of \"Office.Common\\View\\Menu\\IsSystemIconsInMenus\"?" );
350             bMenuSettingsChanged = seqValues[nProperty] >>= bSystemMenuIcons;
351         }
352 
353         #if OSL_DEBUG_LEVEL > 1
354 		else DBG_ASSERT( sal_False, "SvtMenuOptions_Impl::Notify()\nUnkown property detected ... I can't handle these!\n" );
355 		#endif
356 	}
357 
358 	if ( bMenuSettingsChanged )
359 		m_nMenuIcons = bSystemMenuIcons ? 2 : bMenuIcons;
360 
361     for ( sal_uInt16 n=0; n<aList.Count(); n++ )
362         aList.GetObject(n)->Call( this );
363 }
364 
365 //*****************************************************************************************************************
366 //	public method
367 //*****************************************************************************************************************
368 void SvtMenuOptions_Impl::Commit()
369 {
370 	// Get names of supported properties, create a list for values and copy current values to it.
371 	Sequence< OUString >	seqNames	= impl_GetPropertyNames();
372 	sal_Int32				nCount		= seqNames.getLength();
373 	Sequence< Any >			seqValues	( nCount );
374 	for( sal_Int32 nProperty=0; nProperty<nCount; ++nProperty )
375 	{
376         switch( nProperty )
377         {
378             case PROPERTYHANDLE_DONTHIDEDISABLEDENTRIES	:	{
379                 												seqValues[nProperty] <<= m_bDontHideDisabledEntries;
380 															}
381                 											break;
382 
383             case PROPERTYHANDLE_FOLLOWMOUSE				:	{
384                 												seqValues[nProperty] <<= m_bFollowMouse;
385 															}
386                 											break;
387 			//Output cache of current setting as possibly modified by System Theme for older version
388             case PROPERTYHANDLE_SHOWICONSINMENUES       :   {
389                                                                 sal_Bool bValue = (sal_Bool)(Application::GetSettings().GetStyleSettings().GetUseImagesInMenus());
390 																seqValues[nProperty] <<= bValue;
391 															}
392                 											break;
393             case PROPERTYHANDLE_SYSTEMICONSINMENUES		:   {
394 																sal_Bool bValue = (m_nMenuIcons == 2 ? sal_True : sal_False) ;
395                                                                 seqValues[nProperty] <<= bValue;
396 															}
397                 											break;
398         }
399 	}
400 	// Set properties in configuration.
401 	PutProperties( seqNames, seqValues );
402 }
403 
404 //*****************************************************************************************************************
405 //	private method
406 //*****************************************************************************************************************
407 Sequence< OUString > SvtMenuOptions_Impl::impl_GetPropertyNames()
408 {
409 	// Build static list of configuration key names.
410 	static const OUString pProperties[] =
411 	{
412 		PROPERTYNAME_DONTHIDEDISABLEDENTRIES	,
413 		PROPERTYNAME_FOLLOWMOUSE				,
414         PROPERTYNAME_SHOWICONSINMENUES			,
415         PROPERTYNAME_SYSTEMICONSINMENUES
416 	};
417 	// Initialize return sequence with these list ...
418 	static const Sequence< OUString > seqPropertyNames( pProperties, PROPERTYCOUNT );
419 	// ... and return it.
420 	return seqPropertyNames;
421 }
422 
423 void SvtMenuOptions_Impl::AddListenerLink( const Link& rLink )
424 {
425     aList.Insert( new Link( rLink ) );
426 }
427 
428 void SvtMenuOptions_Impl::RemoveListenerLink( const Link& rLink )
429 {
430     for ( sal_uInt16 n=0; n<aList.Count(); n++ )
431     {
432         if ( (*aList.GetObject(n) ) == rLink )
433         {
434             delete aList.Remove(n);
435             break;
436         }
437     }
438 }
439 
440 //*****************************************************************************************************************
441 //	initialize static member
442 //	DON'T DO IT IN YOUR HEADER!
443 //	see definition for further informations
444 //*****************************************************************************************************************
445 SvtMenuOptions_Impl*	SvtMenuOptions::m_pDataContainer	= NULL	;
446 sal_Int32				SvtMenuOptions::m_nRefCount			= 0		;
447 
448 //*****************************************************************************************************************
449 //	constructor
450 //*****************************************************************************************************************
451 SvtMenuOptions::SvtMenuOptions()
452 {
453     // Global access, must be guarded (multithreading!).
454     MutexGuard aGuard( GetOwnStaticMutex() );
455 	// Increase ouer refcount ...
456 	++m_nRefCount;
457 	// ... and initialize ouer data container only if it not already!
458     if( m_pDataContainer == NULL )
459 	{
460         RTL_LOGFILE_CONTEXT(aLog, "svtools ( ??? ) ::SvtMenuOptions_Impl::ctor()");
461         m_pDataContainer = new SvtMenuOptions_Impl();
462 
463         ItemHolder2::holdConfigItem(E_MENUOPTIONS);
464 	}
465 }
466 
467 //*****************************************************************************************************************
468 //	destructor
469 //*****************************************************************************************************************
470 SvtMenuOptions::~SvtMenuOptions()
471 {
472     // Global access, must be guarded (multithreading!)
473     MutexGuard aGuard( GetOwnStaticMutex() );
474 	// Decrease ouer refcount.
475 	--m_nRefCount;
476 	// If last instance was deleted ...
477 	// we must destroy ouer static data container!
478     if( m_nRefCount <= 0 )
479 	{
480 		delete m_pDataContainer;
481 		m_pDataContainer = NULL;
482 	}
483 }
484 
485 //*****************************************************************************************************************
486 //	public method
487 //*****************************************************************************************************************
488 sal_Bool SvtMenuOptions::IsEntryHidingEnabled() const
489 {
490     MutexGuard aGuard( GetOwnStaticMutex() );
491 	return m_pDataContainer->IsEntryHidingEnabled();
492 }
493 
494 //*****************************************************************************************************************
495 //	public method
496 //*****************************************************************************************************************
497 sal_Bool SvtMenuOptions::IsFollowMouseEnabled() const
498 {
499     MutexGuard aGuard( GetOwnStaticMutex() );
500 	return m_pDataContainer->IsFollowMouseEnabled();
501 }
502 
503 //*****************************************************************************************************************
504 //	public method
505 //*****************************************************************************************************************
506 void SvtMenuOptions::SetEntryHidingState( sal_Bool bState )
507 {
508     MutexGuard aGuard( GetOwnStaticMutex() );
509 	m_pDataContainer->SetEntryHidingState( bState );
510 }
511 
512 //*****************************************************************************************************************
513 //	public method
514 //*****************************************************************************************************************
515 void SvtMenuOptions::SetFollowMouseState( sal_Bool bState )
516 {
517     MutexGuard aGuard( GetOwnStaticMutex() );
518 	m_pDataContainer->SetFollowMouseState( bState );
519 }
520 
521 //*****************************************************************************************************************
522 //	public method
523 //*****************************************************************************************************************
524 sal_Int16 SvtMenuOptions::GetMenuIconsState() const
525 {
526     MutexGuard aGuard( GetOwnStaticMutex() );
527 	return m_pDataContainer->GetMenuIconsState();
528 }
529 
530 //*****************************************************************************************************************
531 //	public method
532 //*****************************************************************************************************************
533 void SvtMenuOptions::SetMenuIconsState( sal_Int16 bState )
534 {
535     MutexGuard aGuard( GetOwnStaticMutex() );
536 	m_pDataContainer->SetMenuIconsState( bState );
537 }
538 
539 //*****************************************************************************************************************
540 //	private method
541 //*****************************************************************************************************************
542 Mutex& SvtMenuOptions::GetOwnStaticMutex()
543 {
544 	// Initialize static mutex only for one time!
545     static Mutex* pMutex = NULL;
546 	// If these method first called (Mutex not already exist!) ...
547     if( pMutex == NULL )
548     {
549 		// ... we must create a new one. Protect follow code with the global mutex -
550 		// It must be - we create a static variable!
551         MutexGuard aGuard( Mutex::getGlobalMutex() );
552 		// We must check our pointer again - because it can be that another instance of ouer class will be fastr then these!
553         if( pMutex == NULL )
554         {
555 			// Create the new mutex and set it for return on static variable.
556             static Mutex aMutex;
557             pMutex = &aMutex;
558         }
559     }
560 	// Return new created or already existing mutex object.
561     return *pMutex;
562 }
563 
564 void SvtMenuOptions::AddListenerLink( const Link& rLink )
565 {
566     m_pDataContainer->AddListenerLink( rLink );
567 }
568 
569 void SvtMenuOptions::RemoveListenerLink( const Link& rLink )
570 {
571     m_pDataContainer->RemoveListenerLink( rLink );
572 }
573