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_fpicker.hxx"
26 
27 #include "commonpicker.hxx"
28 #include <com/sun/star/beans/PropertyAttribute.hpp>
29 #include <com/sun/star/beans/NamedValue.hpp>
30 #include <vcl/svapp.hxx>
31 #include <vos/mutex.hxx>
32 #include <toolkit/helper/vclunohelper.hxx>
33 #include <comphelper/weakeventlistener.hxx>
34 #include <comphelper/types.hxx>
35 #include <vcl/msgbox.hxx>
36 #include "iodlg.hxx"
37 
38 //.........................................................................
39 namespace svt
40 {
41 //.........................................................................
42 
43 #define PROPERTY_ID_HELPURL		1
44 #define PROPERTY_ID_WINDOW		2
45 
46 	// using --------------------------------------------------------------
47 
48 	using namespace     ::com::sun::star::lang;
49 	using namespace     ::com::sun::star::ui::dialogs;
50 	using namespace     ::com::sun::star::uno;
51 	using namespace     ::com::sun::star::beans;
52 	using namespace     ::comphelper;
53 
54 	//---------------------------------------------------------------------
OCommonPicker(const::com::sun::star::uno::Reference<::com::sun::star::lang::XMultiServiceFactory> & _rxFactory)55 	OCommonPicker::OCommonPicker( const ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory >& _rxFactory )
56 		:OCommonPicker_Base( m_aMutex )
57 		,OPropertyContainer( GetBroadcastHelper() )
58 		,m_xORB( _rxFactory )
59 		,m_pDlg( NULL )
60 		,m_nCancelEvent( 0 )
61 		,m_bExecuting( sal_False )
62 	{
63 		// the two properties we have
64 		registerProperty(
65 			::rtl::OUString::createFromAscii( "HelpURL" ), PROPERTY_ID_HELPURL,
66 			PropertyAttribute::TRANSIENT,
67 			&m_sHelpURL, ::getCppuType( &m_sHelpURL )
68 		);
69 
70 		registerProperty(
71 			::rtl::OUString::createFromAscii( "Window" ), PROPERTY_ID_WINDOW,
72 			PropertyAttribute::TRANSIENT | PropertyAttribute::READONLY,
73 			&m_xWindow, ::getCppuType( &m_xWindow )
74 		);
75 	}
76 
77 	//---------------------------------------------------------------------
~OCommonPicker()78 	OCommonPicker::~OCommonPicker()
79 	{
80 		if ( !GetBroadcastHelper().bDisposed )
81 		{
82 			acquire();
83 			dispose();
84 		}
85 	}
86 
87 	//---------------------------------------------------------------------
88 	// disambiguate XInterface
89 	//---------------------------------------------------------------------
IMPLEMENT_FORWARD_XINTERFACE2(OCommonPicker,OCommonPicker_Base,OPropertyContainer)90 	IMPLEMENT_FORWARD_XINTERFACE2( OCommonPicker, OCommonPicker_Base, OPropertyContainer )
91 
92 	//---------------------------------------------------------------------
93 	// disambiguate XTypeProvider
94 	//---------------------------------------------------------------------
95 	IMPLEMENT_FORWARD_XTYPEPROVIDER2( OCommonPicker, OCommonPicker_Base, OPropertyContainer )
96 
97 	//---------------------------------------------------------------------
98 	// XComponent related methods
99 	//---------------------------------------------------------------------
100 	void OCommonPicker::checkAlive() const SAL_THROW( (DisposedException) )
101 	{
102 		if ( GetBroadcastHelper().bInDispose || GetBroadcastHelper().bDisposed )
103 			throw DisposedException();
104 	}
105 
prepareDialog()106     void OCommonPicker::prepareDialog()
107     {
108         if ( !getDialog() )
109             createPicker();
110 
111         // set the title
112         if ( m_aTitle.getLength() > 0 )
113             getDialog()->SetText( m_aTitle );
114     }
115 
116 	//---------------------------------------------------------------------
disposing()117 	void SAL_CALL OCommonPicker::disposing()
118 	{
119 		::vos::OGuard aGuard( Application::GetSolarMutex() );
120 
121 		stopWindowListening();
122 
123 		if ( m_nCancelEvent )
124 			Application::RemoveUserEvent( m_nCancelEvent );
125 
126 		{
127 			::osl::MutexGuard aOwnGuard( m_aMutex );
128 			if ( m_bExecuting && m_pDlg )
129 				m_pDlg->EndDialog( RET_CANCEL );
130 		}
131 
132 		delete m_pDlg;
133 		m_pDlg = NULL;
134 		m_xWindow = NULL;
135 		m_xDialogParent = NULL;
136 	}
137 
138 	//---------------------------------------------------------------------
stopWindowListening()139 	void OCommonPicker::stopWindowListening()
140 	{
141 		disposeComponent( m_xWindowListenerAdapter );
142 		disposeComponent( m_xParentListenerAdapter );
143 	}
144 
145 	//---------------------------------------------------------------------
146 	// XEventListener
147 	//---------------------------------------------------------------------
disposing(const EventObject & _rSource)148 	void SAL_CALL OCommonPicker::disposing( const EventObject& _rSource ) throw (RuntimeException)
149 	{
150 		::vos::OGuard aGuard( Application::GetSolarMutex() );
151 		sal_Bool bDialogDying = _rSource.Source == m_xWindow;
152 		sal_Bool bParentDying = _rSource.Source == m_xDialogParent;
153 
154 		if ( bDialogDying || bParentDying )
155 		{
156 			stopWindowListening();
157 
158 			if ( !bDialogDying )	// it's the parent which is dying -> delete the dialog
159 				delete m_pDlg;
160 
161 			m_pDlg = NULL;
162 			m_xWindow = NULL;
163 			m_xDialogParent = NULL;
164 		}
165 		else
166 		{
167 			DBG_ERROR( "OCommonPicker::disposing: where did this come from?" );
168         }
169 	}
170 
171 	//---------------------------------------------------------------------
172 	// property set related methods
173 	//---------------------------------------------------------------------
createArrayHelper() const174 	::cppu::IPropertyArrayHelper* OCommonPicker::createArrayHelper( ) const
175 	{
176 		Sequence< Property > aProps;
177 		describeProperties( aProps );
178 		return new cppu::OPropertyArrayHelper( aProps );
179 	}
180 
181 	//---------------------------------------------------------------------
getInfoHelper()182 	::cppu::IPropertyArrayHelper& SAL_CALL OCommonPicker::getInfoHelper()
183 	{
184 		return *const_cast< OCommonPicker* >( this )->getArrayHelper();
185 	}
186 
187 	//---------------------------------------------------------------------
getPropertySetInfo()188 	Reference< XPropertySetInfo > SAL_CALL OCommonPicker::getPropertySetInfo(  ) throw(RuntimeException)
189 	{
190 		return ::cppu::OPropertySetHelper::createPropertySetInfo( getInfoHelper() );
191 	}
192 
193 	//---------------------------------------------------------------------
setFastPropertyValue_NoBroadcast(sal_Int32 _nHandle,const Any & _rValue)194 	void SAL_CALL OCommonPicker::setFastPropertyValue_NoBroadcast( sal_Int32 _nHandle, const Any& _rValue ) throw (Exception)
195 	{
196 		OPropertyContainer::setFastPropertyValue_NoBroadcast( _nHandle, _rValue );
197 
198 		// if the HelpURL changed, forward this to the dialog
199 		if ( PROPERTY_ID_HELPURL == _nHandle )
200 			if ( m_pDlg )
201 				OControlAccess::setHelpURL( m_pDlg, m_sHelpURL, sal_False );
202 	}
203 
204 
205 	//---------------------------------------------------------------------
createPicker()206 	sal_Bool OCommonPicker::createPicker()
207 	{
208 		::vos::OGuard aGuard( Application::GetSolarMutex() );
209 
210 		if ( !m_pDlg )
211 		{
212 			m_pDlg = implCreateDialog( VCLUnoHelper::GetWindow( m_xDialogParent ) );
213 			DBG_ASSERT( m_pDlg, "OCommonPicker::createPicker: invalid dialog returned!" );
214 
215 			if ( m_pDlg )
216 			{
217 				// synchronize the help id of the dialog with out help URL property
218 				if ( m_sHelpURL.getLength() )
219 				{	// somebody already set the help URL while we had no dialog yet
220 					OControlAccess::setHelpURL( m_pDlg, m_sHelpURL, sal_False );
221 				}
222 				else
223 				{
224 					m_sHelpURL = OControlAccess::getHelpURL( m_pDlg, sal_False );
225 				}
226 
227 				m_xWindow = VCLUnoHelper::GetInterface( m_pDlg );
228 
229 				// add as event listener to the window
230 				Reference< XComponent > xWindowComp( m_xWindow, UNO_QUERY );
231 				OSL_ENSURE( xWindowComp.is(), "OCommonPicker::createFileDialog: invalid window component!" );
232 				if ( xWindowComp.is() )
233 				{
234 					m_xWindowListenerAdapter = new OWeakEventListenerAdapter( this, xWindowComp );
235 						// the adapter will add itself as listener, and forward notifications
236 				}
237 
238 				// _and_ add as event listener to the parent - in case the parent is destroyed
239 				// before we are disposed, our disposal would access dead VCL windows then ....
240 				m_xDialogParent = VCLUnoHelper::GetInterface( m_pDlg->GetParent() );
241 				xWindowComp = xWindowComp.query( m_xDialogParent );
242 				OSL_ENSURE( xWindowComp.is() || !m_pDlg->GetParent(), "OCommonPicker::createFileDialog: invalid window component (the parent this time)!" );
243 				if ( xWindowComp.is() )
244 				{
245 					m_xParentListenerAdapter = new OWeakEventListenerAdapter( this, xWindowComp );
246 						// the adapter will add itself as listener, and forward notifications
247 				}
248 			}
249 		}
250 
251 		return NULL != m_pDlg;
252 	}
253 
254 	//---------------------------------------------------------------------
255 	// XControlAccess functions
256 	//---------------------------------------------------------------------
setControlProperty(const::rtl::OUString & aControlName,const::rtl::OUString & aControlProperty,const Any & aValue)257 	void SAL_CALL OCommonPicker::setControlProperty( const ::rtl::OUString& aControlName, const ::rtl::OUString& aControlProperty, const Any& aValue ) throw (IllegalArgumentException, RuntimeException)
258 	{
259 		checkAlive();
260 
261 		::vos::OGuard aGuard( Application::GetSolarMutex() );
262 		if ( createPicker() )
263 		{
264 			::svt::OControlAccess aAccess( m_pDlg, m_pDlg->GetView() );
265 			aAccess.setControlProperty( aControlName, aControlProperty, aValue );
266 		}
267 	}
268 
269 	//---------------------------------------------------------------------
getControlProperty(const::rtl::OUString & aControlName,const::rtl::OUString & aControlProperty)270 	Any SAL_CALL OCommonPicker::getControlProperty( const ::rtl::OUString& aControlName, const ::rtl::OUString& aControlProperty ) throw (IllegalArgumentException, RuntimeException)
271 	{
272 		checkAlive();
273 
274 		::vos::OGuard aGuard( Application::GetSolarMutex() );
275 		if ( createPicker() )
276 		{
277 			::svt::OControlAccess aAccess( m_pDlg, m_pDlg->GetView() );
278 			return aAccess.getControlProperty( aControlName, aControlProperty );
279 		}
280 
281 		return Any();
282 	}
283 
284 	//---------------------------------------------------------------------
285 	// XControlInformation functions
286 	//---------------------------------------------------------------------
getSupportedControls()287 	Sequence< ::rtl::OUString > SAL_CALL OCommonPicker::getSupportedControls(  ) throw (RuntimeException)
288 	{
289 		checkAlive();
290 
291 		::vos::OGuard aGuard( Application::GetSolarMutex() );
292 		if ( createPicker() )
293 		{
294 			::svt::OControlAccess aAccess( m_pDlg, m_pDlg->GetView() );
295 			return aAccess.getSupportedControls( );
296 		}
297 
298 		return Sequence< ::rtl::OUString >();
299 	}
300 
301 	//---------------------------------------------------------------------
isControlSupported(const::rtl::OUString & aControlName)302 	sal_Bool SAL_CALL OCommonPicker::isControlSupported( const ::rtl::OUString& aControlName ) throw (RuntimeException)
303 	{
304 		checkAlive();
305 
306 		::vos::OGuard aGuard( Application::GetSolarMutex() );
307 		if ( createPicker() )
308 		{
309 			::svt::OControlAccess aAccess( m_pDlg, m_pDlg->GetView() );
310 			return aAccess.isControlSupported( aControlName );
311 		}
312 
313 		return sal_False;
314 	}
315 
316 	//---------------------------------------------------------------------
getSupportedControlProperties(const::rtl::OUString & aControlName)317 	Sequence< ::rtl::OUString > SAL_CALL OCommonPicker::getSupportedControlProperties( const ::rtl::OUString& aControlName ) throw (IllegalArgumentException, RuntimeException)
318 	{
319 		checkAlive();
320 
321 		::vos::OGuard aGuard( Application::GetSolarMutex() );
322 		if ( createPicker() )
323 		{
324 			::svt::OControlAccess aAccess( m_pDlg, m_pDlg->GetView() );
325 			return aAccess.getSupportedControlProperties( aControlName );
326 		}
327 
328 		return Sequence< ::rtl::OUString >();
329 	}
330 
331 	//---------------------------------------------------------------------
isControlPropertySupported(const::rtl::OUString & aControlName,const::rtl::OUString & aControlProperty)332 	sal_Bool SAL_CALL OCommonPicker::isControlPropertySupported( const ::rtl::OUString& aControlName, const ::rtl::OUString& aControlProperty ) throw (IllegalArgumentException, RuntimeException)
333 	{
334 		checkAlive();
335 
336 		::vos::OGuard aGuard( Application::GetSolarMutex() );
337 		if ( createPicker() )
338 		{
339 			::svt::OControlAccess aAccess( m_pDlg, m_pDlg->GetView() );
340 			return aAccess.isControlPropertySupported( aControlName, aControlProperty );
341 		}
342 
343 		return sal_False;
344 	}
345 
346 	//---------------------------------------------------------------------
347 	// XExecutableDialog functions
348 	//---------------------------------------------------------------------
setTitle(const rtl::OUString & _rTitle)349 	void SAL_CALL OCommonPicker::setTitle( const rtl::OUString& _rTitle ) throw( RuntimeException )
350 	{
351 		::vos::OGuard aGuard( Application::GetSolarMutex() );
352 		m_aTitle = _rTitle;
353 	}
354 
355 	//---------------------------------------------------------------------
execute()356 	sal_Int16 OCommonPicker::execute() throw (RuntimeException)
357 	{
358 		::vos::OGuard aGuard( Application::GetSolarMutex() );
359 
360         prepareDialog();
361 
362 		{
363 			::osl::MutexGuard aOwnGuard( m_aMutex );
364 			m_bExecuting = sal_True;
365 		}
366 		sal_Int16 nResult = implExecutePicker();
367 		{
368 			::osl::MutexGuard aOwnGuard( m_aMutex );
369 			m_bExecuting = sal_False;
370 		}
371 
372 		return nResult;
373 	}
374 
375 	//---------------------------------------------------------------------
376 	// XCancellable functions
377 	//---------------------------------------------------------------------
cancel()378 	void SAL_CALL OCommonPicker::cancel(  ) throw (RuntimeException)
379 	{
380 		{
381 			::osl::MutexGuard aGuard( m_aMutex );
382 			if ( m_nCancelEvent )
383 				// nothing to do - the event for cancelling the dialog is already on the way
384 				return;
385 		}
386 
387 		// The thread which executes our dialog has locked the solar mutex for
388 		// sure. Cancelling the dialog should be done with a locked solar mutex, too.
389 		// Thus we post ourself a message for cancelling the dialog. This way, the message
390 		// is either handled in the thread which opened the dialog (which may even be
391 		// this thread here), or, if no dialog is open, in the thread doing scheduling
392 		// currently. Both is okay for us ....
393 		//
394 		// Note that we could do check if we are really executing the dialog currently.
395 		// but the information would be potentially obsolete at the moment our event
396 		// arrives, so we need to check it there, anyway ...
397 		m_nCancelEvent = Application::PostUserEvent( LINK( this, OCommonPicker, OnCancelPicker ) );
398 	}
399 
400 	//---------------------------------------------------------------------
IMPL_LINK(OCommonPicker,OnCancelPicker,void *,EMPTYARG)401 	IMPL_LINK( OCommonPicker, OnCancelPicker, void*, EMPTYARG )
402 	{
403 		// By definition, the solar mutex is locked when we arrive here. Note that this
404 		// is important, as for instance the consistency of m_pDlg depends on this mutex.
405 		::osl::MutexGuard aGuard( m_aMutex );
406 		m_nCancelEvent = 0;
407 
408 		if ( !m_bExecuting )
409 			// nothing to do. This may be because the dialog was cancelled after our cancel method
410 			// posted this async event, or because somebody called cancel without the dialog
411 			// being executed at this time.
412 			return 0;
413 
414 		OSL_ENSURE( getDialog(), "OCommonPicker::OnCancelPicker: executing, but no dialog!" );
415 		if ( getDialog() )
416 			getDialog()->EndDialog( RET_CANCEL );
417 
418 		return 0L;
419 	}
420 
421 	//------------------------------------------------------------------------------------
422 	// XInitialization functions
423 	//------------------------------------------------------------------------------------
initialize(const Sequence<Any> & _rArguments)424 	void SAL_CALL OCommonPicker::initialize( const Sequence< Any >& _rArguments )
425 		throw ( Exception, RuntimeException )
426 	{
427 		checkAlive();
428 
429 		::rtl::OUString sSettingName;
430 		Any				aSettingValue;
431 
432 		PropertyValue	aPropArg;
433 		NamedValue		aPairArg;
434 
435 
436 		const Any* pArguments		= _rArguments.getConstArray();
437 		const Any* pArgumentsEnd	= _rArguments.getConstArray() + _rArguments.getLength();
438 		for	(	const Any* pArgument = pArguments;
439 				pArgument != pArgumentsEnd;
440 				++pArgument
441 			)
442 		{
443 			if ( *pArgument >>= aPropArg )
444 			{
445 				if ( aPropArg.Name.getLength() <= 0)
446 					continue;
447 
448 				sSettingName = aPropArg.Name;
449 				aSettingValue = aPropArg.Value;
450 			}
451 			else if ( *pArgument >>= aPairArg )
452 			{
453 				if ( aPairArg.Name.getLength() <= 0)
454 					continue;
455 
456 				sSettingName = aPairArg.Name;
457 				aSettingValue = aPairArg.Value;
458 
459 
460 			}
461 			else
462 			{
463 				DBG_ERROR(
464 					(	::rtl::OString( "OCommonPicker::initialize: unknown argument type at position " )
465 					+=	::rtl::OString::valueOf( (sal_Int32)( pArguments - _rArguments.getConstArray() ) )
466 					).getStr()
467 				);
468 				continue;
469 			}
470 
471 #ifdef DBG_UTIL
472 			sal_Bool bKnownSetting =
473 #endif
474 			implHandleInitializationArgument( sSettingName, aSettingValue );
475 			DBG_ASSERT( bKnownSetting,
476 				(	::rtl::OString( "OCommonPicker::initialize: unknown argument \"" )
477 				+=	::rtl::OString( sSettingName.getStr(), sSettingName.getLength(), osl_getThreadTextEncoding() )
478 				+=	::rtl::OString( "\"!" )
479 				).getStr()
480 			);
481 		}
482 	}
483 
484 	//---------------------------------------------------------------------
implHandleInitializationArgument(const::rtl::OUString & _rName,const Any & _rValue)485 	sal_Bool OCommonPicker::implHandleInitializationArgument( const ::rtl::OUString& _rName, const Any& _rValue ) SAL_THROW( ( Exception, RuntimeException ) )
486 	{
487 		sal_Bool bKnown = sal_True;
488 		if ( _rName.equalsAscii( "ParentWindow" ) )
489 		{
490 			m_xDialogParent.clear();
491 			OSL_VERIFY( _rValue >>= m_xDialogParent );
492 			OSL_ENSURE( VCLUnoHelper::GetWindow( m_xDialogParent ), "OCommonPicker::implHandleInitializationArgument: invalid parent window given!" );
493 		}
494 		else
495 			bKnown = sal_False;
496 		return bKnown;
497 	}
498 
499 //.........................................................................
500 }	// namespace svt
501 //.........................................................................
502 
503