1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_unotools.hxx"
26 
27 //_________________________________________________________________________________________________________________
28 //	includes
29 //_________________________________________________________________________________________________________________
30 
31 #include <unotools/cmdoptions.hxx>
32 #include <unotools/configmgr.hxx>
33 #include <unotools/configitem.hxx>
34 #include <tools/debug.hxx>
35 #include <com/sun/star/uno/Any.hxx>
36 #include <com/sun/star/uno/Sequence.hxx>
37 #include <cppuhelper/weakref.hxx>
38 #include <tools/urlobj.hxx>
39 #include <rtl/ustrbuf.hxx>
40 
41 #include <itemholder1.hxx>
42 
43 #include <algorithm>
44 #include <hash_map>
45 
46 //_________________________________________________________________________________________________________________
47 //	namespaces
48 //_________________________________________________________________________________________________________________
49 
50 using namespace ::std					;
51 using namespace ::utl					;
52 using namespace ::rtl					;
53 using namespace ::osl					;
54 using namespace ::com::sun::star::uno	;
55 using namespace ::com::sun::star::beans	;
56 
57 //_________________________________________________________________________________________________________________
58 //	const
59 //_________________________________________________________________________________________________________________
60 
61 #define ROOTNODE_CMDOPTIONS								OUString(RTL_CONSTASCII_USTRINGPARAM("Office.Commands/Execute"	))
62 #define PATHDELIMITER                                   OUString(RTL_CONSTASCII_USTRINGPARAM("/"						))
63 
64 #define SETNODE_DISABLED                                OUString(RTL_CONSTASCII_USTRINGPARAM("Disabled"					))
65 
66 #define PROPERTYNAME_CMD                                OUString(RTL_CONSTASCII_USTRINGPARAM("Command"					))
67 
68 #define PROPERTYCOUNT                                   1
69 
70 #define OFFSET_CMD                                      0
71 
72 //_________________________________________________________________________________________________________________
73 //	private declarations!
74 //_________________________________________________________________________________________________________________
75 
76 // Method to retrieve a hash code from a string. May be we have to change it to decrease collisions in the hash map
77 struct OUStringHashCode
78 {
79     size_t operator()( const ::rtl::OUString& sString ) const
80 	{
81 		return sString.hashCode();
82 	}
83 };
84 
85 /*-****************************************************************************************************************
86     @descr  support simple command option structures and operations on it
87 ****************************************************************************************************************-*/
88 class SvtCmdOptions
89 {
90     public:
91         //---------------------------------------------------------------------------------------------------------
92         // the only way to free memory!
93         void Clear()
94         {
95 			m_aCommandHashMap.clear();
96 		}
97 
98 		sal_Bool HasEntries() const
99         {
100             return ( m_aCommandHashMap.size() > 0 );
101         }
102 
103         void SetContainerSize( sal_Int32 nSize )
104 		{
105 			m_aCommandHashMap.rehash( nSize );
106 		}
107 
108 		sal_Bool Lookup( const OUString& aCmd ) const
109 		{
110 			CommandHashMap::const_iterator pEntry = m_aCommandHashMap.find( aCmd );
111 			return ( pEntry != m_aCommandHashMap.end() );
112 		}
113 
114 		void AddCommand( const OUString& aCmd )
115 		{
116 			m_aCommandHashMap.insert( CommandHashMap::value_type( aCmd, 0 ) );
117 		}
118 
119 		//---------------------------------------------------------------------------------------------------------
120         // convert internal list to external format
121         // for using it on right menus realy
122         // Notice:   We build a property list with 4 entries and set it on result list then.
123         //           The while-loop starts with pointer on internal member list lSetupEntries, change to
124         //           lUserEntries then and stop after that with NULL!
125         //           Separator entries will be packed in another way then normal entries! We define
126         //           special strings "sEmpty" and "sSeperator" to perform too ...
127         Sequence< OUString > GetList() const
128         {
129             sal_Int32				nCount = (sal_Int32)m_aCommandHashMap.size();
130 			sal_Int32				nIndex = 0;
131             Sequence< OUString >	aList( nCount );
132 
133 			CommandHashMap::const_iterator pEntry = m_aCommandHashMap.begin();
134 			while ( pEntry != m_aCommandHashMap.end() )
135 				aList[nIndex++] = pEntry->first;
136 
137             return aList;
138         }
139 
140     private:
141 		class CommandHashMap : public ::std::hash_map< ::rtl::OUString		,
142 														sal_Int32			,
143 														OUStringHashCode	,
144 														::std::equal_to< ::rtl::OUString >	>
145 		{
146 			public:
147 				inline void free()
148 				{
149 					CommandHashMap().swap( *this );
150 				}
151 		};
152 
153         CommandHashMap m_aCommandHashMap;
154 };
155 
156 typedef ::std::vector< ::com::sun::star::uno::WeakReference< ::com::sun::star::frame::XFrame > > SvtFrameVector;
157 
158 class SvtCommandOptions_Impl : public ConfigItem
159 {
160 	//-------------------------------------------------------------------------------------------------------------
161 	//	public methods
162 	//-------------------------------------------------------------------------------------------------------------
163 
164 	public:
165 
166 		//---------------------------------------------------------------------------------------------------------
167 		//	constructor / destructor
168 		//---------------------------------------------------------------------------------------------------------
169 
170          SvtCommandOptions_Impl();
171         ~SvtCommandOptions_Impl();
172 
173 		//---------------------------------------------------------------------------------------------------------
174 		//	overloaded methods of baseclass
175 		//---------------------------------------------------------------------------------------------------------
176 
177 		/*-****************************************************************************************************//**
178 			@short		called for notify of configmanager
179 			@descr		These method is called from the ConfigManager before application ends or from the
180 			 			PropertyChangeListener if the sub tree broadcasts changes. You must update your
181 						internal values.
182 
183 			@seealso	baseclass ConfigItem
184 
185             @param      "lPropertyNames" is the list of properties which should be updated.
186 			@return		-
187 
188 			@onerror	-
189 		*//*-*****************************************************************************************************/
190 
191         virtual void Notify( const Sequence< OUString >& lPropertyNames );
192 
193 		/*-****************************************************************************************************//**
194 			@short		write changes to configuration
195 			@descr		These method writes the changed values into the sub tree
196 						and should always called in our destructor to guarantee consistency of config data.
197 
198 			@seealso	baseclass ConfigItem
199 
200 			@param		-
201 			@return		-
202 
203 			@onerror	-
204 		*//*-*****************************************************************************************************/
205 
206     	virtual void Commit();
207 
208 		//---------------------------------------------------------------------------------------------------------
209 		//	public interface
210 		//---------------------------------------------------------------------------------------------------------
211 
212 		/*-****************************************************************************************************//**
213             @short      base implementation of public interface for "SvtDynamicMenuOptions"!
214             @descr      These class is used as static member of "SvtDynamicMenuOptions" ...
215 						=> The code exist only for one time and isn't duplicated for every instance!
216 
217 			@seealso	-
218 
219 			@param		-
220 			@return		-
221 
222 			@onerror	-
223 		*//*-*****************************************************************************************************/
224 
225         void					Clear       (	SvtCommandOptions::CmdOption	eCmdOption	);
226         sal_Bool                HasEntries  (   SvtCommandOptions::CmdOption    eOption     ) const;
227 		sal_Bool				Lookup		(	SvtCommandOptions::CmdOption	eCmdOption,	const OUString& ) const;
228         Sequence< OUString >	GetList		(	SvtCommandOptions::CmdOption	eCmdOption	) const ;
229         void					AddCommand	(	SvtCommandOptions::CmdOption	eCmdOption,
230 												const OUString& sURL		);
231         void EstablisFrameCallback(const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& xFrame);
232 
233 	//-------------------------------------------------------------------------------------------------------------
234 	//	private methods
235 	//-------------------------------------------------------------------------------------------------------------
236 
237 	private:
238 
239 		/*-****************************************************************************************************//**
240             @short      return list of key names of our configuration management which represent oue module tree
241 			@descr		These methods return the current list of key names! We need it to get needed values from our
242                         configuration management and support dynamical menu item lists!
243 
244 			@seealso	-
245 
246             @param      "nDisabledCount"	,   returns count of menu entries for "new"
247 			@return		A list of configuration key names is returned.
248 
249 			@onerror	-
250 		*//*-*****************************************************************************************************/
251 
252         Sequence< OUString > impl_GetPropertyNames();
253 
254 	//-------------------------------------------------------------------------------------------------------------
255 	//	private member
256 	//-------------------------------------------------------------------------------------------------------------
257 
258 	private:
259         SvtCmdOptions  m_aDisabledCommands;
260         SvtFrameVector m_lFrames;
261 };
262 
263 //_________________________________________________________________________________________________________________
264 //	definitions
265 //_________________________________________________________________________________________________________________
266 
267 //*****************************************************************************************************************
268 //	constructor
269 //*****************************************************************************************************************
270 SvtCommandOptions_Impl::SvtCommandOptions_Impl()
271 	// Init baseclasses first
272     :   ConfigItem( ROOTNODE_CMDOPTIONS )
273 	// Init member then...
274 {
275     // Get names and values of all accessable menu entries and fill internal structures.
276 	// See impl_GetPropertyNames() for further informations.
277     Sequence< OUString >    lNames              = impl_GetPropertyNames ();
278     Sequence< Any >         lValues             = GetProperties         ( lNames         );
279 
280 	// Safe impossible cases.
281 	// We need values from ALL configuration keys.
282 	// Follow assignment use order of values in relation to our list of key names!
283     DBG_ASSERT( !(lNames.getLength()!=lValues.getLength()), "SvtCommandOptions_Impl::SvtCommandOptions_Impl()\nI miss some values of configuration keys!\n" );
284 
285 	// Copy values from list in right order to ouer internal member.
286 	// Attention: List for names and values have an internal construction pattern!
287     sal_Int32	nItem     = 0 ;
288     OUString    sCmd		  ;
289 
290 	// Set size of hash_map reach a used size of approx. 60%
291 	m_aDisabledCommands.SetContainerSize( lNames.getLength() * 10 / 6 );
292 
293 	// Get names/values for disabled commands.
294     for( nItem=0; nItem < lNames.getLength(); ++nItem )
295 	{
296 		// Currently only one value
297         lValues[nItem] >>= sCmd;
298         m_aDisabledCommands.AddCommand( sCmd );
299 	}
300 
301 /*TODO: Not used in the moment! see Notify() ...
302 	// Enable notification mechanism of ouer baseclass.
303 	// We need it to get information about changes outside these class on ouer used configuration keys! */
304     Sequence< OUString > aNotifySeq( 1 );
305     aNotifySeq[0] = OUString( RTL_CONSTASCII_USTRINGPARAM( "Disabled" ));
306     EnableNotification( aNotifySeq, sal_True );
307 }
308 
309 //*****************************************************************************************************************
310 //	destructor
311 //*****************************************************************************************************************
312 SvtCommandOptions_Impl::~SvtCommandOptions_Impl()
313 {
314 	// We must save our current values .. if user forget it!
315 	if( IsModified() == sal_True )
316 	{
317 		Commit();
318 	}
319 }
320 
321 //*****************************************************************************************************************
322 //	public method
323 //*****************************************************************************************************************
324 void SvtCommandOptions_Impl::Notify( const Sequence< OUString >& )
325 {
326     MutexGuard aGuard( SvtCommandOptions::GetOwnStaticMutex() );
327 
328     Sequence< OUString >    lNames   = impl_GetPropertyNames ();
329     Sequence< Any >         lValues  = GetProperties         ( lNames         );
330 
331 	// Safe impossible cases.
332 	// We need values from ALL configuration keys.
333 	// Follow assignment use order of values in relation to our list of key names!
334     DBG_ASSERT( !(lNames.getLength()!=lValues.getLength()), "SvtCommandOptions_Impl::SvtCommandOptions_Impl()\nI miss some values of configuration keys!\n" );
335 
336 	// Copy values from list in right order to ouer internal member.
337 	// Attention: List for names and values have an internal construction pattern!
338     sal_Int32	nItem     = 0 ;
339     OUString    sCmd		  ;
340 
341 	// Set size of hash_map reach a used size of approx. 60%
342     m_aDisabledCommands.Clear();
343 	m_aDisabledCommands.SetContainerSize( lNames.getLength() * 10 / 6 );
344 
345 	// Get names/values for disabled commands.
346     for( nItem=0; nItem < lNames.getLength(); ++nItem )
347 	{
348 		// Currently only one value
349         lValues[nItem] >>= sCmd;
350         m_aDisabledCommands.AddCommand( sCmd );
351 	}
352 
353     // dont forget to update all existing frames and her might cached dispatch objects!
354     // But look for already killed frames. We hold weak references instead of hard ones ...
355     for (SvtFrameVector::const_iterator pIt  = m_lFrames.begin();
356                                         pIt != m_lFrames.end()  ;
357                                       ++pIt                     )
358     {
359         ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame > xFrame(pIt->get(), ::com::sun::star::uno::UNO_QUERY);
360         if (xFrame.is())
361             xFrame->contextChanged();
362     }
363 }
364 
365 //*****************************************************************************************************************
366 //	public method
367 //*****************************************************************************************************************
368 void SvtCommandOptions_Impl::Commit()
369 {
370     DBG_ERROR( "SvtCommandOptions_Impl::Commit()\nNot implemented yet!\n" );
371 }
372 
373 //*****************************************************************************************************************
374 //	public method
375 //*****************************************************************************************************************
376 void SvtCommandOptions_Impl::Clear( SvtCommandOptions::CmdOption eCmdOption )
377 {
378     switch( eCmdOption )
379 	{
380 		case SvtCommandOptions::CMDOPTION_DISABLED:
381 		{
382 			m_aDisabledCommands.Clear();
383             SetModified();
384 		}
385         break;
386 
387 		default:
388 			DBG_ASSERT( sal_False, "SvtCommandOptions_Impl::Clear()\nUnknown option type given!\n" );
389 	}
390 }
391 
392 //*****************************************************************************************************************
393 //	public method
394 //*****************************************************************************************************************
395 sal_Bool SvtCommandOptions_Impl::HasEntries( SvtCommandOptions::CmdOption eOption ) const
396 {
397     if ( eOption == SvtCommandOptions::CMDOPTION_DISABLED )
398         return ( m_aDisabledCommands.HasEntries() > 0 );
399     else
400         return sal_False;
401 }
402 
403 //*****************************************************************************************************************
404 //	public method
405 //*****************************************************************************************************************
406 Sequence< OUString > SvtCommandOptions_Impl::GetList( SvtCommandOptions::CmdOption eCmdOption ) const
407 {
408     Sequence< OUString > lReturn;
409 
410 	switch( eCmdOption )
411 	{
412         case SvtCommandOptions::CMDOPTION_DISABLED:
413 		{
414 			lReturn = m_aDisabledCommands.GetList();
415 		}
416         break;
417 
418 		default:
419 			DBG_ASSERT( sal_False, "SvtCommandOptions_Impl::GetList()\nUnknown option type given!\n" );
420 	}
421 
422 	return lReturn;
423 }
424 
425 //*****************************************************************************************************************
426 //	public method
427 //*****************************************************************************************************************
428 sal_Bool SvtCommandOptions_Impl::Lookup( SvtCommandOptions::CmdOption eCmdOption, const OUString& aCommand ) const
429 {
430     switch( eCmdOption )
431 	{
432         case SvtCommandOptions::CMDOPTION_DISABLED:
433 		{
434 			return m_aDisabledCommands.Lookup( aCommand );
435 		}
436 		default:
437 			DBG_ASSERT( sal_False, "SvtCommandOptions_Impl::GetList()\nUnknown option type given!\n" );
438 	}
439 
440 	return sal_False;
441 }
442 
443 //*****************************************************************************************************************
444 //	public method
445 //*****************************************************************************************************************
446 void SvtCommandOptions_Impl::AddCommand( SvtCommandOptions::CmdOption eCmdOption, const OUString& sCmd )
447 {
448     switch( eCmdOption )
449 	{
450         case SvtCommandOptions::CMDOPTION_DISABLED:
451 		{
452 			m_aDisabledCommands.AddCommand( sCmd );
453 			SetModified();
454 		}
455 		break;
456 
457 		default:
458 			DBG_ASSERT( sal_False, "SvtCommandOptions_Impl::GetList()\nUnknown option type given!\n" );
459 	}
460 }
461 
462 //*****************************************************************************************************************
463 //  public method
464 //*****************************************************************************************************************
465 void SvtCommandOptions_Impl::EstablisFrameCallback(const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& xFrame)
466 {
467     // check if frame already exists inside list
468     // ignore double registrations
469     // every frame must be notified one times only!
470     ::com::sun::star::uno::WeakReference< ::com::sun::star::frame::XFrame > xWeak(xFrame);
471     SvtFrameVector::const_iterator pIt = ::std::find(m_lFrames.begin(), m_lFrames.end(), xWeak);
472     if (pIt == m_lFrames.end())
473         m_lFrames.push_back(xWeak);
474 }
475 
476 //*****************************************************************************************************************
477 //	private method
478 //*****************************************************************************************************************
479 Sequence< OUString > SvtCommandOptions_Impl::impl_GetPropertyNames()
480 {
481 	// First get ALL names of current existing list items in configuration!
482     Sequence< OUString > lDisabledItems      = GetNodeNames( SETNODE_DISABLED, utl::CONFIG_NAME_LOCAL_PATH );
483 
484 	OUString aSetNode( SETNODE_DISABLED );
485 	aSetNode += PATHDELIMITER;
486 
487 	OUString aCommandKey( PATHDELIMITER );
488 	aCommandKey += PROPERTYNAME_CMD;
489 
490 	// Expand all keys
491 	for (sal_Int32 i=0; i<lDisabledItems.getLength(); ++i )
492 	{
493 		OUStringBuffer aBuffer( 32 );
494 		aBuffer.append( aSetNode );
495 		aBuffer.append( lDisabledItems[i] );
496 		aBuffer.append( aCommandKey );
497 		lDisabledItems[i] = aBuffer.makeStringAndClear();
498 	}
499 
500 	// Return result.
501 	return lDisabledItems;
502 }
503 
504 //*****************************************************************************************************************
505 //	initialize static member
506 //	DON'T DO IT IN YOUR HEADER!
507 //	see definition for further informations
508 //*****************************************************************************************************************
509 SvtCommandOptions_Impl*     SvtCommandOptions::m_pDataContainer = NULL  ;
510 sal_Int32                   SvtCommandOptions::m_nRefCount      = 0     ;
511 
512 //*****************************************************************************************************************
513 //	constructor
514 //*****************************************************************************************************************
515 SvtCommandOptions::SvtCommandOptions()
516 {
517     // Global access, must be guarded (multithreading!).
518     MutexGuard aGuard( GetOwnStaticMutex() );
519 	// Increase ouer refcount ...
520 	++m_nRefCount;
521 	// ... and initialize ouer data container only if it not already exist!
522     if( m_pDataContainer == NULL )
523 	{
524         m_pDataContainer = new SvtCommandOptions_Impl;
525 		ItemHolder1::holdConfigItem(E_CMDOPTIONS);
526 	}
527 }
528 
529 //*****************************************************************************************************************
530 //	destructor
531 //*****************************************************************************************************************
532 SvtCommandOptions::~SvtCommandOptions()
533 {
534     // Global access, must be guarded (multithreading!)
535     MutexGuard aGuard( GetOwnStaticMutex() );
536 	// Decrease ouer refcount.
537 	--m_nRefCount;
538 	// If last instance was deleted ...
539 	// we must destroy ouer static data container!
540     if( m_nRefCount <= 0 )
541 	{
542 		delete m_pDataContainer;
543 		m_pDataContainer = NULL;
544 	}
545 }
546 
547 //*****************************************************************************************************************
548 //	public method
549 //*****************************************************************************************************************
550 void SvtCommandOptions::Clear( CmdOption eCmdOption )
551 {
552     MutexGuard aGuard( GetOwnStaticMutex() );
553     m_pDataContainer->Clear( eCmdOption );
554 }
555 
556 //*****************************************************************************************************************
557 //	public method
558 //*****************************************************************************************************************
559 sal_Bool SvtCommandOptions::HasEntries( CmdOption eOption ) const
560 {
561     MutexGuard aGuard( GetOwnStaticMutex() );
562     return m_pDataContainer->HasEntries( eOption );
563 }
564 
565 //*****************************************************************************************************************
566 //	public method
567 //*****************************************************************************************************************
568 sal_Bool SvtCommandOptions::Lookup( CmdOption eCmdOption, const OUString& aCommandURL ) const
569 {
570     MutexGuard aGuard( GetOwnStaticMutex() );
571     return m_pDataContainer->Lookup( eCmdOption, aCommandURL );
572 }
573 
574 //*****************************************************************************************************************
575 //	public method
576 //*****************************************************************************************************************
577 Sequence< OUString > SvtCommandOptions::GetList( CmdOption eCmdOption ) const
578 {
579     MutexGuard aGuard( GetOwnStaticMutex() );
580     return m_pDataContainer->GetList( eCmdOption );
581 }
582 
583 //*****************************************************************************************************************
584 //	public method
585 //*****************************************************************************************************************
586 void SvtCommandOptions::AddCommand( CmdOption eCmdOption, const OUString& sURL )
587 {
588     MutexGuard aGuard( GetOwnStaticMutex() );
589     m_pDataContainer->AddCommand( eCmdOption, sURL );
590 }
591 
592 //*****************************************************************************************************************
593 //  public method
594 //*****************************************************************************************************************
595 void SvtCommandOptions::EstablisFrameCallback(const ::com::sun::star::uno::Reference< ::com::sun::star::frame::XFrame >& xFrame)
596 {
597     MutexGuard aGuard( GetOwnStaticMutex() );
598     m_pDataContainer->EstablisFrameCallback(xFrame);
599 }
600 
601 //*****************************************************************************************************************
602 //	private method
603 //*****************************************************************************************************************
604 Mutex& SvtCommandOptions::GetOwnStaticMutex()
605 {
606 	// Initialize static mutex only for one time!
607     static Mutex* pMutex = NULL;
608 	// If these method first called (Mutex not already exist!) ...
609     if( pMutex == NULL )
610     {
611 		// ... we must create a new one. Protect follow code with the global mutex -
612 		// It must be - we create a static variable!
613         MutexGuard aGuard( Mutex::getGlobalMutex() );
614 		// We must check our pointer again - because it can be that another instance of ouer class will be fastr then these!
615         if( pMutex == NULL )
616         {
617 			// Create the new mutex and set it for return on static variable.
618             static Mutex aMutex;
619             pMutex = &aMutex;
620         }
621     }
622 	// Return new created or already existing mutex object.
623     return *pMutex;
624 }
625