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_accessibility.hxx"
26 #include <accessibility/standard/vclxaccessibletabcontrol.hxx>
27 #include <accessibility/standard/vclxaccessibletabpage.hxx>
28 
29 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
30 #include <com/sun/star/accessibility/AccessibleRole.hpp>
31 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
32 #include <unotools/accessiblestatesethelper.hxx>
33 #include <vcl/tabctrl.hxx>
34 #include <vcl/tabpage.hxx>
35 
36 #include <vector>
37 
38 using namespace ::com::sun::star;
39 using namespace ::com::sun::star::uno;
40 using namespace ::com::sun::star::lang;
41 using namespace ::com::sun::star::accessibility;
42 using namespace ::comphelper;
43 
44 
45 //	----------------------------------------------------
46 //	class VCLXAccessibleTabControl
47 //	----------------------------------------------------
48 
49 VCLXAccessibleTabControl::VCLXAccessibleTabControl( VCLXWindow* pVCLXWindow )
50 	:VCLXAccessibleComponent( pVCLXWindow )
51 {
52 	m_pTabControl = static_cast< TabControl* >( GetWindow() );
53 
54 	if ( m_pTabControl )
55 		m_aAccessibleChildren.assign( m_pTabControl->GetPageCount(), Reference< XAccessible >() );
56 }
57 
58 // -----------------------------------------------------------------------------
59 
60 VCLXAccessibleTabControl::~VCLXAccessibleTabControl()
61 {
62 }
63 
64 // -----------------------------------------------------------------------------
65 
66 void VCLXAccessibleTabControl::UpdateFocused()
67 {
68 	for ( sal_uInt32 i = 0; i < m_aAccessibleChildren.size(); ++i )
69 	{
70 		Reference< XAccessible > xChild( m_aAccessibleChildren[i] );
71 		if ( xChild.is() )
72 		{
73 			VCLXAccessibleTabPage* pVCLXAccessibleTabPage = static_cast< VCLXAccessibleTabPage* >( xChild.get() );
74 			if ( pVCLXAccessibleTabPage )
75 				pVCLXAccessibleTabPage->SetFocused( pVCLXAccessibleTabPage->IsFocused() );
76 		}
77 	}
78 }
79 
80 // -----------------------------------------------------------------------------
81 
82 void VCLXAccessibleTabControl::UpdateSelected( sal_Int32 i, bool bSelected )
83 {
84 	// IAccessible2 implementation, 2009
85 	//NotifyAccessibleEvent( AccessibleEventId::SELECTION_CHANGED, Any(), Any() );
86 
87 	if ( i >= 0 && i < (sal_Int32)m_aAccessibleChildren.size() )
88 	{
89 		Reference< XAccessible > xChild( m_aAccessibleChildren[i] );
90 		if ( xChild.is() )
91 		{
92 			VCLXAccessibleTabPage* pVCLXAccessibleTabPage = static_cast< VCLXAccessibleTabPage* >( xChild.get() );
93 			if ( pVCLXAccessibleTabPage )
94 				pVCLXAccessibleTabPage->SetSelected( bSelected );
95 		}
96 	}
97 }
98 
99 // -----------------------------------------------------------------------------
100 
101 void VCLXAccessibleTabControl::UpdatePageText( sal_Int32 i )
102 {
103     if ( i >= 0 && i < (sal_Int32)m_aAccessibleChildren.size() )
104     {
105         Reference< XAccessible > xChild( m_aAccessibleChildren[i] );
106         if ( xChild.is() )
107         {
108             VCLXAccessibleTabPage* pVCLXAccessibleTabPage = static_cast< VCLXAccessibleTabPage* >( xChild.get() );
109             if ( pVCLXAccessibleTabPage )
110                 pVCLXAccessibleTabPage->SetPageText( pVCLXAccessibleTabPage->GetPageText() );
111         }
112     }
113 }
114 
115 // -----------------------------------------------------------------------------
116 
117 void VCLXAccessibleTabControl::UpdateTabPage( sal_Int32 i, bool bNew )
118 {
119 	if ( i >= 0 && i < (sal_Int32)m_aAccessibleChildren.size() )
120 	{
121 		Reference< XAccessible > xChild( m_aAccessibleChildren[i] );
122 		if ( xChild.is() )
123 		{
124 			VCLXAccessibleTabPage* pVCLXAccessibleTabPage = static_cast< VCLXAccessibleTabPage* >( xChild.get() );
125 			if ( pVCLXAccessibleTabPage )
126 				pVCLXAccessibleTabPage->Update( bNew );
127 		}
128 	}
129 }
130 
131 // -----------------------------------------------------------------------------
132 
133 void VCLXAccessibleTabControl::InsertChild( sal_Int32 i )
134 {
135 	if ( i >= 0 && i <= (sal_Int32)m_aAccessibleChildren.size() )
136 	{
137 		// insert entry in child list
138 		m_aAccessibleChildren.insert( m_aAccessibleChildren.begin() + i, Reference< XAccessible >() );
139 
140 		// send accessible child event
141 		Reference< XAccessible > xChild( getAccessibleChild( i ) );
142 		if ( xChild.is() )
143 		{
144 			Any aOldValue, aNewValue;
145 			aNewValue <<= xChild;
146 			NotifyAccessibleEvent( AccessibleEventId::CHILD, aOldValue, aNewValue );
147 		}
148 	}
149 }
150 
151 // -----------------------------------------------------------------------------
152 
153 void VCLXAccessibleTabControl::RemoveChild( sal_Int32 i )
154 {
155 	if ( i >= 0 && i < (sal_Int32)m_aAccessibleChildren.size() )
156 	{
157 		// get the accessible of the removed page
158 		Reference< XAccessible > xChild( m_aAccessibleChildren[i] );
159 
160 		// remove entry in child list
161 		m_aAccessibleChildren.erase( m_aAccessibleChildren.begin() + i );
162 
163 		// send accessible child event
164 		if ( xChild.is() )
165 		{
166 			Any aOldValue, aNewValue;
167 			aOldValue <<= xChild;
168 			NotifyAccessibleEvent( AccessibleEventId::CHILD, aOldValue, aNewValue );
169 
170 			Reference< XComponent > xComponent( xChild, UNO_QUERY );
171 			if ( xComponent.is() )
172 				xComponent->dispose();
173 		}
174 	}
175 }
176 
177 // -----------------------------------------------------------------------------
178 
179 void VCLXAccessibleTabControl::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent )
180 {
181     switch ( rVclWindowEvent.GetId() )
182     {
183 		case VCLEVENT_TABPAGE_ACTIVATE:
184 		case VCLEVENT_TABPAGE_DEACTIVATE:
185         {
186 			if ( m_pTabControl )
187 			{
188                 sal_uInt16 nPageId = (sal_uInt16)(sal_IntPtr) rVclWindowEvent.GetData();
189 				sal_uInt16 nPagePos = m_pTabControl->GetPagePos( nPageId );
190                 UpdateFocused();
191 				UpdateSelected( nPagePos, rVclWindowEvent.GetId() == VCLEVENT_TABPAGE_ACTIVATE );
192 			}
193         }
194         break;
195 		case VCLEVENT_TABPAGE_PAGETEXTCHANGED:
196         {
197 			if ( m_pTabControl )
198 			{
199                 sal_uInt16 nPageId = (sal_uInt16)(sal_IntPtr) rVclWindowEvent.GetData();
200 				sal_uInt16 nPagePos = m_pTabControl->GetPagePos( nPageId );
201 				UpdatePageText( nPagePos );
202 			}
203         }
204         break;
205 		case VCLEVENT_TABPAGE_INSERTED:
206         {
207 			if ( m_pTabControl )
208 			{
209                 sal_uInt16 nPageId = (sal_uInt16)(sal_IntPtr) rVclWindowEvent.GetData();
210 				sal_uInt16 nPagePos = m_pTabControl->GetPagePos( nPageId );
211 				InsertChild( nPagePos );
212 			}
213         }
214         break;
215 		case VCLEVENT_TABPAGE_REMOVED:
216         {
217 			if ( m_pTabControl )
218 			{
219                 sal_uInt16 nPageId = (sal_uInt16)(sal_IntPtr) rVclWindowEvent.GetData();
220 				for ( sal_Int32 i = 0, nCount = getAccessibleChildCount(); i < nCount; ++i )
221 				{
222 					Reference< XAccessible > xChild( getAccessibleChild( i ) );
223 					if ( xChild.is() )
224 					{
225 						VCLXAccessibleTabPage* pVCLXAccessibleTabPage = static_cast< VCLXAccessibleTabPage* >( xChild.get() );
226 						if ( pVCLXAccessibleTabPage && pVCLXAccessibleTabPage->GetPageId() == nPageId )
227 						{
228 							RemoveChild( i );
229 							break;
230 						}
231 					}
232 				}
233 			}
234         }
235         break;
236 		case VCLEVENT_TABPAGE_REMOVEDALL:
237         {
238 			for ( sal_Int32 i = m_aAccessibleChildren.size() - 1; i >= 0; --i )
239 				RemoveChild( i );
240         }
241         break;
242 		case VCLEVENT_WINDOW_GETFOCUS:
243 		case VCLEVENT_WINDOW_LOSEFOCUS:
244         {
245             UpdateFocused();
246         }
247         break;
248 		case VCLEVENT_OBJECT_DYING:
249         {
250 			if ( m_pTabControl )
251 			{
252 				m_pTabControl = NULL;
253 
254 				// dispose all tab pages
255 				for ( sal_uInt32 i = 0; i < m_aAccessibleChildren.size(); ++i )
256 				{
257 					Reference< XComponent > xComponent( m_aAccessibleChildren[i], UNO_QUERY );
258 					if ( xComponent.is() )
259 						xComponent->dispose();
260 				}
261 				m_aAccessibleChildren.clear();
262 			}
263 
264 			VCLXAccessibleComponent::ProcessWindowEvent( rVclWindowEvent );
265         }
266         break;
267 		default:
268 			VCLXAccessibleComponent::ProcessWindowEvent( rVclWindowEvent );
269    }
270 }
271 
272 // -----------------------------------------------------------------------------
273 
274 void VCLXAccessibleTabControl::ProcessWindowChildEvent( const VclWindowEvent& rVclWindowEvent )
275 {
276 	switch ( rVclWindowEvent.GetId() )
277 	{
278 		case VCLEVENT_WINDOW_SHOW:
279         case VCLEVENT_WINDOW_HIDE:
280         {
281             if ( m_pTabControl )
282             {
283 			    Window* pChild = static_cast< Window* >( rVclWindowEvent.GetData() );
284 			    if ( pChild && pChild->GetType() == WINDOW_TABPAGE )
285 			    {
286                     for ( sal_Int32 i = 0, nCount = m_pTabControl->GetPageCount(); i < nCount; ++i )
287                     {
288                         sal_uInt16 nPageId = m_pTabControl->GetPageId( (sal_uInt16)i );
289 		                TabPage* pTabPage = m_pTabControl->GetTabPage( nPageId );
290                         if ( pTabPage == (TabPage*) pChild )
291                             UpdateTabPage( i, rVclWindowEvent.GetId() == VCLEVENT_WINDOW_SHOW );
292                     }
293                 }
294             }
295         }
296         break;
297 		default:
298 			VCLXAccessibleComponent::ProcessWindowChildEvent( rVclWindowEvent );
299 	}
300 }
301 
302 
303 // -----------------------------------------------------------------------------
304 
305 void VCLXAccessibleTabControl::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet )
306 {
307 	VCLXAccessibleComponent::FillAccessibleStateSet( rStateSet );
308 
309 	if ( m_pTabControl )
310 		rStateSet.AddState( AccessibleStateType::FOCUSABLE );
311 }
312 
313 // -----------------------------------------------------------------------------
314 // XInterface
315 // -----------------------------------------------------------------------------
316 
317 IMPLEMENT_FORWARD_XINTERFACE2( VCLXAccessibleTabControl, VCLXAccessibleComponent, VCLXAccessibleTabControl_BASE )
318 
319 // -----------------------------------------------------------------------------
320 // XTypeProvider
321 // -----------------------------------------------------------------------------
322 
323 IMPLEMENT_FORWARD_XTYPEPROVIDER2( VCLXAccessibleTabControl, VCLXAccessibleComponent, VCLXAccessibleTabControl_BASE )
324 
325 // -----------------------------------------------------------------------------
326 // XComponent
327 // -----------------------------------------------------------------------------
328 
329 void VCLXAccessibleTabControl::disposing()
330 {
331 	VCLXAccessibleComponent::disposing();
332 
333 	if ( m_pTabControl )
334 	{
335 		m_pTabControl = NULL;
336 
337 		// dispose all tab pages
338 		for ( sal_uInt32 i = 0; i < m_aAccessibleChildren.size(); ++i )
339 		{
340 			Reference< XComponent > xComponent( m_aAccessibleChildren[i], UNO_QUERY );
341 			if ( xComponent.is() )
342 				xComponent->dispose();
343 		}
344 		m_aAccessibleChildren.clear();
345 	}
346 }
347 
348 // -----------------------------------------------------------------------------
349 // XServiceInfo
350 // -----------------------------------------------------------------------------
351 
352 ::rtl::OUString VCLXAccessibleTabControl::getImplementationName() throw (RuntimeException)
353 {
354 	return ::rtl::OUString::createFromAscii( "com.sun.star.comp.toolkit.AccessibleTabControl" );
355 }
356 
357 // -----------------------------------------------------------------------------
358 
359 Sequence< ::rtl::OUString > VCLXAccessibleTabControl::getSupportedServiceNames() throw (RuntimeException)
360 {
361 	Sequence< ::rtl::OUString > aNames(1);
362 	aNames[0] = ::rtl::OUString::createFromAscii( "com.sun.star.awt.AccessibleTabControl" );
363 	return aNames;
364 }
365 
366 // -----------------------------------------------------------------------------
367 // XAccessibleContext
368 // -----------------------------------------------------------------------------
369 
370 sal_Int32 VCLXAccessibleTabControl::getAccessibleChildCount() throw (RuntimeException)
371 {
372 	OExternalLockGuard aGuard( this );
373 
374 	return m_aAccessibleChildren.size();
375 }
376 
377 // -----------------------------------------------------------------------------
378 
379 Reference< XAccessible > VCLXAccessibleTabControl::getAccessibleChild( sal_Int32 i ) throw (IndexOutOfBoundsException, RuntimeException)
380 {
381 	OExternalLockGuard aGuard( this );
382 
383 	if ( i < 0 || i >= getAccessibleChildCount() )
384 		throw IndexOutOfBoundsException();
385 
386 	Reference< XAccessible > xChild = m_aAccessibleChildren[i];
387 	if ( !xChild.is() )
388 	{
389 		if ( m_pTabControl )
390 		{
391 			sal_uInt16 nPageId = m_pTabControl->GetPageId( (sal_uInt16)i );
392 
393 			xChild = new VCLXAccessibleTabPage( m_pTabControl, nPageId );
394 
395 			// insert into tab page list
396 			m_aAccessibleChildren[i] = xChild;
397 		}
398 	}
399 
400     return xChild;
401 }
402 
403 // -----------------------------------------------------------------------------
404 
405 sal_Int16 VCLXAccessibleTabControl::getAccessibleRole(  ) throw (RuntimeException)
406 {
407 	OExternalLockGuard aGuard( this );
408 
409 	return AccessibleRole::PAGE_TAB_LIST;
410 }
411 
412 // -----------------------------------------------------------------------------
413 
414 ::rtl::OUString VCLXAccessibleTabControl::getAccessibleName(  ) throw (RuntimeException)
415 {
416 	OExternalLockGuard aGuard( this );
417 
418 	return ::rtl::OUString();
419 }
420 
421 // -----------------------------------------------------------------------------
422 // XAccessibleSelection
423 // -----------------------------------------------------------------------------
424 
425 void VCLXAccessibleTabControl::selectAccessibleChild( sal_Int32 nChildIndex ) throw (IndexOutOfBoundsException, RuntimeException)
426 {
427 	OExternalLockGuard aGuard( this );
428 
429 	if ( nChildIndex < 0 || nChildIndex >= getAccessibleChildCount() )
430 		throw IndexOutOfBoundsException();
431 
432 	if ( m_pTabControl )
433 		m_pTabControl->SelectTabPage( m_pTabControl->GetPageId( (sal_uInt16)nChildIndex ) );
434 }
435 
436 // -----------------------------------------------------------------------------
437 
438 sal_Bool VCLXAccessibleTabControl::isAccessibleChildSelected( sal_Int32 nChildIndex ) throw (IndexOutOfBoundsException, RuntimeException)
439 {
440     OExternalLockGuard aGuard( this );
441 
442     if ( nChildIndex < 0 || nChildIndex >= getAccessibleChildCount() )
443         throw IndexOutOfBoundsException();
444 
445     sal_Bool bSelected = sal_False;
446     if ( m_pTabControl && m_pTabControl->GetCurPageId() == m_pTabControl->GetPageId( (sal_uInt16)nChildIndex ) )
447         bSelected = sal_True;
448 
449     return bSelected;
450 }
451 
452 // -----------------------------------------------------------------------------
453 
454 void VCLXAccessibleTabControl::clearAccessibleSelection(  ) throw (RuntimeException)
455 {
456 	// This method makes no sense in a tab control, and so does nothing.
457 }
458 
459 // -----------------------------------------------------------------------------
460 
461 void VCLXAccessibleTabControl::selectAllAccessibleChildren(  ) throw (RuntimeException)
462 {
463 	OExternalLockGuard aGuard( this );
464 
465 	selectAccessibleChild( 0 );
466 }
467 
468 // -----------------------------------------------------------------------------
469 
470 sal_Int32 VCLXAccessibleTabControl::getSelectedAccessibleChildCount(  ) throw (RuntimeException)
471 {
472 	OExternalLockGuard aGuard( this );
473 
474 	return 1;
475 }
476 
477 // -----------------------------------------------------------------------------
478 
479 Reference< XAccessible > VCLXAccessibleTabControl::getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) throw (IndexOutOfBoundsException, RuntimeException)
480 {
481 	OExternalLockGuard aGuard( this );
482 
483 	if ( nSelectedChildIndex < 0 || nSelectedChildIndex >= getSelectedAccessibleChildCount() )
484 		throw IndexOutOfBoundsException();
485 
486 	Reference< XAccessible > xChild;
487 
488 	for ( sal_Int32 i = 0, j = 0, nCount = getAccessibleChildCount(); i < nCount; i++ )
489 	{
490 		if ( isAccessibleChildSelected( i ) && ( j++ == nSelectedChildIndex ) )
491 		{
492 			xChild = getAccessibleChild( i );
493 			break;
494 		}
495 	}
496 
497 	return xChild;
498 }
499 
500 // -----------------------------------------------------------------------------
501 
502 void VCLXAccessibleTabControl::deselectAccessibleChild( sal_Int32 nChildIndex ) throw (IndexOutOfBoundsException, RuntimeException)
503 {
504 	OExternalLockGuard aGuard( this );
505 
506 	if ( nChildIndex < 0 || nChildIndex >= getAccessibleChildCount() )
507 		throw IndexOutOfBoundsException();
508 
509 	// This method makes no sense in a tab control, and so does nothing.
510 }
511 
512 // -----------------------------------------------------------------------------
513