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