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