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