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_toolkit.hxx"
30 #include <toolkit/controls/accessiblecontrolcontext.hxx>
31 #include <unotools/accessiblestatesethelper.hxx>
32 #include <com/sun/star/awt/XControl.hpp>
33 #include <com/sun/star/awt/XWindow.hpp>
34 #include <vcl/svapp.hxx>
35 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
36 #include <com/sun/star/accessibility/AccessibleRole.hpp>
37 #include <toolkit/helper/vclunohelper.hxx>
38 #include <vcl/window.hxx>
39 
40 //........................................................................
41 namespace toolkit
42 {
43 //........................................................................
44 
45 	using ::comphelper::OContextEntryGuard;
46 	using namespace ::com::sun::star;
47 	using namespace ::com::sun::star::uno;
48 	using namespace ::com::sun::star::lang;
49 	using namespace ::com::sun::star::beans;
50 	using namespace ::com::sun::star::accessibility;
51 
52 	//====================================================================
53 	//= OAccessibleControlContext
54 	//====================================================================
55 	//--------------------------------------------------------------------
56 	OAccessibleControlContext::OAccessibleControlContext()
57 		:OAccessibleControlContext_Base( )
58 	{
59 		// nothing to do here, we have a late ctor
60 	}
61 
62 	//--------------------------------------------------------------------
63 	OAccessibleControlContext::~OAccessibleControlContext()
64 	{
65 		ensureDisposed();
66 	}
67 
68 	//--------------------------------------------------------------------
69 	IMPLEMENT_FORWARD_XINTERFACE3( OAccessibleControlContext, OAccessibleControlContext_Base, OAccessibleImplementationAccess, OAccessibleControlContext_IBase )
70 	IMPLEMENT_FORWARD_XTYPEPROVIDER3( OAccessibleControlContext, OAccessibleControlContext_Base, OAccessibleImplementationAccess, OAccessibleControlContext_IBase )
71 		// (order matters: the first is the class name, the second is the class doing the ref counting)
72 
73 	//--------------------------------------------------------------------
74 	void OAccessibleControlContext::Init( const Reference< XAccessible >& _rxCreator ) SAL_THROW( ( Exception ) )
75 	{
76 		OContextEntryGuard aGuard( this );
77 
78 		// retrieve the model of the control
79 		OSL_ENSURE( !m_xControlModel.is(), "OAccessibleControlContext::Init: already know a control model....!???" );
80 
81 		Reference< awt::XControl > xControl( _rxCreator, UNO_QUERY );
82 		if ( xControl.is() )
83 			m_xControlModel = m_xControlModel.query( xControl->getModel() );
84 		OSL_ENSURE( m_xControlModel.is(), "OAccessibleControlContext::Init: invalid creator (no control, or control without model!" );
85 		if ( !m_xControlModel.is() )
86 			throw DisposedException();	// caught by the caller (the create method)
87 
88 		// start listening at the model
89 		startModelListening();
90 
91 		// announce the XAccessible to our base class
92 		OAccessibleControlContext_Base::lateInit( _rxCreator );
93 	}
94 
95 	//--------------------------------------------------------------------
96 	OAccessibleControlContext* OAccessibleControlContext::create( const Reference< XAccessible >& _rxCreator ) SAL_THROW( ( ) )
97 	{
98 		OAccessibleControlContext* pNew = NULL;
99 		try
100 		{
101 			pNew = new OAccessibleControlContext;
102 			pNew->Init( _rxCreator );
103 		}
104 		catch( const Exception& )
105 		{
106 			OSL_ENSURE( sal_False, "OAccessibleControlContext::create: caught an exception from the late ctor!" );
107 		}
108 		return pNew;
109 	}
110 
111 	//--------------------------------------------------------------------
112 	void OAccessibleControlContext::startModelListening( ) SAL_THROW( ( Exception ) )
113 	{
114 		Reference< XComponent > xModelComp( m_xControlModel, UNO_QUERY );
115 		OSL_ENSURE( xModelComp.is(), "OAccessibleControlContext::startModelListening: invalid model!" );
116 		if ( xModelComp.is() )
117 			xModelComp->addEventListener( this );
118 	}
119 
120 	//--------------------------------------------------------------------
121 	void OAccessibleControlContext::stopModelListening( ) SAL_THROW( ( Exception ) )
122 	{
123 		Reference< XComponent > xModelComp( m_xControlModel, UNO_QUERY );
124 		OSL_ENSURE( xModelComp.is(), "OAccessibleControlContext::stopModelListening: invalid model!" );
125 		if ( xModelComp.is() )
126 			xModelComp->removeEventListener( this );
127 	}
128 
129 	//--------------------------------------------------------------------
130 	sal_Int32 SAL_CALL OAccessibleControlContext::getAccessibleChildCount(  ) throw (RuntimeException)
131 	{
132 		// we do not have children
133 		return 0;
134 	}
135 
136 	//--------------------------------------------------------------------
137 	Reference< XAccessible > SAL_CALL OAccessibleControlContext::getAccessibleChild( sal_Int32 ) throw (IndexOutOfBoundsException, RuntimeException)
138 	{
139 		// we do not have children
140 		throw IndexOutOfBoundsException();
141 	}
142 
143 	//--------------------------------------------------------------------
144 	Reference< XAccessible > SAL_CALL OAccessibleControlContext::getAccessibleParent(  ) throw (RuntimeException)
145 	{
146 		OContextEntryGuard aGuard( this );
147 		OSL_ENSURE( implGetForeignControlledParent().is(), "OAccessibleControlContext::getAccessibleParent: somebody forgot to set a parent!" );
148 			// this parent of us is foreign controlled - somebody has to set it using the OAccessibleImplementationAccess
149 			// class, before integrating our instance into an AccessibleDocumentModel
150 		return implGetForeignControlledParent();
151 	}
152 
153 	//--------------------------------------------------------------------
154 	sal_Int16 SAL_CALL OAccessibleControlContext::getAccessibleRole(  ) throw (RuntimeException)
155 	{
156 		return AccessibleRole::SHAPE;
157 	}
158 
159 	//--------------------------------------------------------------------
160 	::rtl::OUString SAL_CALL OAccessibleControlContext::getAccessibleDescription(  ) throw (RuntimeException)
161 	{
162 		OContextEntryGuard aGuard( this );
163 		return getModelStringProperty( "HelpText" );
164 	}
165 
166 	//--------------------------------------------------------------------
167 	::rtl::OUString SAL_CALL OAccessibleControlContext::getAccessibleName(  ) throw (RuntimeException)
168 	{
169 		OContextEntryGuard aGuard( this );
170 		return getModelStringProperty( "Name" );
171 	}
172 
173 	//--------------------------------------------------------------------
174 	Reference< XAccessibleRelationSet > SAL_CALL OAccessibleControlContext::getAccessibleRelationSet(  ) throw (RuntimeException)
175 	{
176 		return NULL;
177 	}
178 
179 	//--------------------------------------------------------------------
180 	Reference< XAccessibleStateSet > SAL_CALL OAccessibleControlContext::getAccessibleStateSet(  ) throw (RuntimeException)
181 	{
182 		::osl::MutexGuard aGuard( GetMutex() );
183 			// no OContextEntryGuard here, as we do not want to throw an exception in case we're not alive anymore
184 
185 		::utl::AccessibleStateSetHelper* pStateSet = NULL;
186 		if ( isAlive() )
187 		{
188 			// no own states, only the ones which are foreign controlled
189 			pStateSet = new ::utl::AccessibleStateSetHelper( implGetForeignControlledStates() );
190 		}
191 		else
192 		{	// only the DEFUNC state if we're already disposed
193 			pStateSet = new ::utl::AccessibleStateSetHelper;
194 			pStateSet->AddState( AccessibleStateType::DEFUNC );
195 		}
196 		return pStateSet;
197 	}
198 
199 	//--------------------------------------------------------------------
200 	void SAL_CALL OAccessibleControlContext::disposing( const EventObject&
201     #if OSL_DEBUG_LEVEL > 0
202     _rSource
203     #endif
204     ) throw ( RuntimeException )
205 	{
206 		OSL_ENSURE( Reference< XPropertySet >( _rSource.Source, UNO_QUERY ).get() == m_xControlModel.get(),
207 			"OAccessibleControlContext::disposing: where did this come from?" );
208 
209 		stopModelListening( );
210 		m_xControlModel.clear();
211 		m_xModelPropsInfo.clear();
212 
213 		OAccessibleControlContext_Base::disposing();
214 	}
215 
216 	//--------------------------------------------------------------------
217 	::rtl::OUString	OAccessibleControlContext::getModelStringProperty( const sal_Char* _pPropertyName )
218 	{
219 		::rtl::OUString sReturn;
220 		try
221 		{
222 			if ( !m_xModelPropsInfo.is() && m_xControlModel.is() )
223 				m_xModelPropsInfo = m_xControlModel->getPropertySetInfo();
224 
225 			::rtl::OUString sPropertyName( ::rtl::OUString::createFromAscii( _pPropertyName ) );
226 			if ( m_xModelPropsInfo.is() && m_xModelPropsInfo->hasPropertyByName( sPropertyName ) )
227 				m_xControlModel->getPropertyValue( sPropertyName ) >>= sReturn;
228 		}
229 		catch( const Exception& )
230 		{
231 			OSL_ENSURE( sal_False, "OAccessibleControlContext::getModelStringProperty: caught an exception!" );
232 		}
233 		return sReturn;
234 	}
235 
236 	//--------------------------------------------------------------------
237 	Window*	OAccessibleControlContext::implGetWindow( Reference< awt::XWindow >* _pxUNOWindow ) const
238 	{
239 		Reference< awt::XControl > xControl( getAccessibleCreator(), UNO_QUERY );
240 		Reference< awt::XWindow > xWindow;
241 		if ( xControl.is() )
242 			xWindow = xWindow.query( xControl->getPeer() );
243 
244 		Window* pWindow = xWindow.is() ? VCLUnoHelper::GetWindow( xWindow ) : NULL;
245 
246 		if ( _pxUNOWindow )
247 			*_pxUNOWindow = xWindow;
248 		return pWindow;
249 	}
250 
251 	//--------------------------------------------------------------------
252 	awt::Rectangle SAL_CALL OAccessibleControlContext::implGetBounds(  ) throw (RuntimeException)
253 	{
254 		::vos::OGuard aSolarGuard( Application::GetSolarMutex() );
255 			// want to do some VCL stuff here ...
256 		OContextEntryGuard aGuard( this );
257 
258 		OSL_ENSURE( sal_False, "OAccessibleControlContext::implGetBounds: performance issue: forced to calc the size myself!" );
259 		// In design mode (and this is what this class is for), the surrounding shape (if any) should handle this call
260 		// The problem is that in design mode, our size may not be correct (in the drawing layer, controls are
261 		// positioned/sized for painting only), and that calculation of our position is expensive
262 
263 		// what we know (or can obtain from somewhere):
264 		// * the PosSize of our peer, relative to it's parent window
265 		// * the parent window which the PosSize is relative to
266 		// * our foreign controlled accessible parent
267 		// from this info, we can determine the the position of our peer relative to the foreign parent
268 
269 		// our control
270 		Reference< awt::XWindow > xWindow;
271 		Window* pVCLWindow = implGetWindow( &xWindow );
272 
273 		awt::Rectangle aBounds( 0, 0, 0, 0 );
274 		if ( xWindow.is() )
275 		{
276 			// ugly, but .... though the XWindow has a getPosSize, it is impossible to determine the
277 			// parent which this position/size is relative to. This means we must tunnel UNO and ask the
278 			// implementation
279 			Window* pVCLParent = pVCLWindow ? pVCLWindow->GetParent() : NULL;
280 
281 			// the relative location of the window
282 			::Point aWindowRelativePos( 0, 0);
283 			if ( pVCLWindow )
284 				aWindowRelativePos = pVCLWindow->GetPosPixel();
285 
286 			// the screnn position of the "window parent" of the control
287 			::Point aVCLParentScreenPos( 0, 0 );
288 			if ( pVCLParent )
289 				aVCLParentScreenPos = pVCLParent->GetPosPixel();
290 
291 			// the screen position of the "accessible parent" of the control
292 			Reference< XAccessible > xParentAcc( implGetForeignControlledParent() );
293 			Reference< XAccessibleComponent > xParentAccComponent;
294 			if ( xParentAcc.is() )
295 				xParentAccComponent = xParentAccComponent.query( xParentAcc->getAccessibleContext() );
296 			awt::Point aAccParentScreenPos( 0, 0 );
297 			if ( xParentAccComponent.is() )
298 				aAccParentScreenPos = xParentAccComponent->getLocationOnScreen();
299 
300 			// now the size of the control
301 			aBounds = xWindow->getPosSize();
302 
303 			// correct the pos
304 			aBounds.X = aWindowRelativePos.X() + aVCLParentScreenPos.X() - aAccParentScreenPos.X;
305 			aBounds.Y = aWindowRelativePos.Y() + aVCLParentScreenPos.Y() - aAccParentScreenPos.Y;
306 		}
307 
308 		return aBounds;
309 	}
310 
311 	//--------------------------------------------------------------------
312 	Reference< XAccessible > SAL_CALL OAccessibleControlContext::getAccessibleAtPoint( const awt::Point& /* _rPoint */ ) throw (RuntimeException)
313 	{
314 		// no children at all
315 		return NULL;
316 	}
317 
318 	//--------------------------------------------------------------------
319 	void SAL_CALL OAccessibleControlContext::grabFocus(  ) throw (RuntimeException)
320 	{
321 		OSL_ENSURE( sal_False, "OAccessibleControlContext::grabFocus: !isFocusTraversable, but grabFocus!" );
322 	}
323 
324 	//--------------------------------------------------------------------
325 	Any SAL_CALL OAccessibleControlContext::getAccessibleKeyBinding(  ) throw (RuntimeException)
326 	{
327 		// we do not have any key bindings to activate a UNO control in design mode
328 		return Any();
329 	}
330 
331 	//--------------------------------------------------------------------
332 	sal_Int32 SAL_CALL OAccessibleControlContext::getForeground(  ) throw (::com::sun::star::uno::RuntimeException)
333 	{
334 		::vos::OGuard aSolarGuard( Application::GetSolarMutex() );
335 			// want to do some VCL stuff here ...
336 		OContextEntryGuard aGuard( this );
337 
338 		Window* pWindow = implGetWindow( );
339 		sal_Int32 nColor = 0;
340 		if ( pWindow )
341 		{
342 			if ( pWindow->IsControlForeground() )
343 				nColor = pWindow->GetControlForeground().GetColor();
344 			else
345 			{
346 				Font aFont;
347 				if ( pWindow->IsControlFont() )
348 					aFont = pWindow->GetControlFont();
349 				else
350 					aFont = pWindow->GetFont();
351 				nColor = aFont.GetColor().GetColor();
352 			}
353 		}
354 		return nColor;
355 	}
356 
357 	//--------------------------------------------------------------------
358 	sal_Int32 SAL_CALL OAccessibleControlContext::getBackground(  ) throw (::com::sun::star::uno::RuntimeException)
359 	{
360 		::vos::OGuard aSolarGuard( Application::GetSolarMutex() );
361 			// want to do some VCL stuff here ...
362 		OContextEntryGuard aGuard( this );
363 
364 		Window* pWindow = implGetWindow( );
365 		sal_Int32 nColor = 0;
366 		if ( pWindow )
367 		{
368 			if ( pWindow->IsControlBackground() )
369 				nColor = pWindow->GetControlBackground().GetColor();
370 			else
371 				nColor = pWindow->GetBackground().GetColor().GetColor();
372 		}
373 
374 		return nColor;
375 	}
376 
377 //........................................................................
378 }	//namespace toolkit
379 //........................................................................
380 
381