LibreOffice Module accessibility (master) 1
accessiblemenubasecomponent.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 */
19
25
26#include <com/sun/star/accessibility/AccessibleEventId.hpp>
27#include <com/sun/star/accessibility/AccessibleRole.hpp>
28#include <com/sun/star/accessibility/AccessibleStateType.hpp>
31#include <o3tl/safeint.hxx>
32#include <vcl/menu.hxx>
33#include <vcl/vclevent.hxx>
34
35#include <array>
36
37using namespace ::com::sun::star;
38using namespace ::com::sun::star::lang;
39using namespace ::com::sun::star::uno;
40using namespace ::com::sun::star::accessibility;
41using namespace ::comphelper;
42
43
44// OAccessibleMenuBaseComponent
45
46
48 :m_pMenu( pMenu )
49 ,m_bEnabled( false )
50 ,m_bFocused( false )
51 ,m_bVisible( false )
52 ,m_bSelected( false )
53 ,m_bChecked( false )
54{
55 if ( m_pMenu )
56 {
57 m_aAccessibleChildren.assign( m_pMenu->GetItemCount(), Reference< XAccessible >() );
58 m_pMenu->AddEventListener( LINK( this, OAccessibleMenuBaseComponent, MenuEventListener ) );
59 }
60}
61
62
64{
65 if ( m_pMenu )
66 m_pMenu->RemoveEventListener( LINK( this, OAccessibleMenuBaseComponent, MenuEventListener ) );
67}
68
69
71{
72 return false;
73}
74
75
77{
78 return false;
79}
80
81
83{
84 return false;
85}
86
87
89{
90 return false;
91}
92
93
95{
96 return false;
97}
98
99
101{
107}
108
109
111{
112 if ( m_bEnabled == bEnabled )
113 return;
114
115 sal_Int64 nStateType=AccessibleStateType::ENABLED;
117 {
118 nStateType = AccessibleStateType::VISIBLE;
119 }
120 std::array<Any, 2> aOldValue, aNewValue;
121 if ( m_bEnabled )
122 {
123 aOldValue[0] <<= AccessibleStateType::SENSITIVE;
124 aOldValue[1] <<= nStateType;
125 }
126 else
127 {
128 aNewValue[0] <<= nStateType;
129 aNewValue[1] <<= AccessibleStateType::SENSITIVE;
130 }
131 m_bEnabled = bEnabled;
132 NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue[0], aNewValue[0] );
133 NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue[1], aNewValue[1] );
134}
135
136
138{
139 if ( m_bFocused != bFocused )
140 {
141 Any aOldValue, aNewValue;
142 if ( m_bFocused )
143 aOldValue <<= AccessibleStateType::FOCUSED;
144 else
145 aNewValue <<= AccessibleStateType::FOCUSED;
146 m_bFocused = bFocused;
147 NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue );
148 }
149}
150
151
153{
154 if ( m_bVisible != bVisible )
155 {
156 Any aOldValue, aNewValue;
157 if ( m_bVisible )
158 aOldValue <<= AccessibleStateType::VISIBLE;
159 else
160 aNewValue <<= AccessibleStateType::VISIBLE;
162 NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue );
163 }
164}
165
166
168{
169 if ( m_bSelected != bSelected )
170 {
171 Any aOldValue, aNewValue;
172 if ( m_bSelected )
173 aOldValue <<= AccessibleStateType::SELECTED;
174 else
175 aNewValue <<= AccessibleStateType::SELECTED;
176 m_bSelected = bSelected;
177 NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue );
178 }
179}
180
181
183{
184 if ( m_bChecked != bChecked )
185 {
186 Any aOldValue, aNewValue;
187 if ( m_bChecked )
188 aOldValue <<= AccessibleStateType::CHECKED;
189 else
190 aNewValue <<= AccessibleStateType::CHECKED;
191 m_bChecked = bChecked;
192 NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue );
193 }
194}
195
196
197void OAccessibleMenuBaseComponent::UpdateEnabled( sal_Int32 i, bool bEnabled )
198{
199 if ( i >= 0 && o3tl::make_unsigned(i) < m_aAccessibleChildren.size() )
200 {
202 if ( xChild.is() )
203 {
204 OAccessibleMenuBaseComponent* pComp = static_cast< OAccessibleMenuBaseComponent* >( xChild.get() );
205 if ( pComp )
206 pComp->SetEnabled( bEnabled );
207 }
208 }
209}
210
211
212void OAccessibleMenuBaseComponent::UpdateFocused( sal_Int32 i, bool bFocused )
213{
214 if ( i >= 0 && o3tl::make_unsigned(i) < m_aAccessibleChildren.size() )
215 {
217 if ( xChild.is() )
218 {
219 OAccessibleMenuBaseComponent* pComp = static_cast< OAccessibleMenuBaseComponent* >( xChild.get() );
220 if ( pComp )
221 pComp->SetFocused( bFocused );
222 }
223 }
224}
225
226
228{
230 for (const Reference<XAccessible>& xChild : m_aAccessibleChildren)
231 {
232 if ( xChild.is() )
233 {
234 OAccessibleMenuBaseComponent* pComp = static_cast< OAccessibleMenuBaseComponent* >( xChild.get() );
235 if ( pComp )
236 pComp->SetVisible( pComp->IsVisible() );
237 }
238 }
239}
240
241
242void OAccessibleMenuBaseComponent::UpdateSelected( sal_Int32 i, bool bSelected )
243{
244 NotifyAccessibleEvent( AccessibleEventId::SELECTION_CHANGED, Any(), Any() );
245
246 if ( i >= 0 && o3tl::make_unsigned(i) < m_aAccessibleChildren.size() )
247 {
249 if ( xChild.is() )
250 {
251 OAccessibleMenuBaseComponent* pComp = static_cast< OAccessibleMenuBaseComponent* >( xChild.get() );
252 if ( pComp )
253 pComp->SetSelected( bSelected );
254 }
255 }
256}
257
258
259void OAccessibleMenuBaseComponent::UpdateChecked( sal_Int32 i, bool bChecked )
260{
261 if ( i >= 0 && o3tl::make_unsigned(i) < m_aAccessibleChildren.size() )
262 {
264 if ( xChild.is() )
265 {
266 OAccessibleMenuBaseComponent* pComp = static_cast< OAccessibleMenuBaseComponent* >( xChild.get() );
267 if ( pComp )
268 pComp->SetChecked( bChecked );
269 }
270 }
271}
272
273
275{
276 if ( i >= 0 && o3tl::make_unsigned(i) < m_aAccessibleChildren.size() )
277 {
279 if ( xChild.is() )
280 {
281 OAccessibleMenuItemComponent* pComp = static_cast< OAccessibleMenuItemComponent* >( xChild.get() );
282 if ( pComp )
283 pComp->SetAccessibleName( pComp->GetAccessibleName() );
284 }
285 }
286}
287
289{
290 if (i < 0 || o3tl::make_unsigned(i) >= m_aAccessibleChildren.size())
291 return;
292
293 Reference<XAccessible> xChild(m_aAccessibleChildren[i]);
294 if (!xChild.is())
295 return;
296
297 OAccessibleMenuItemComponent* pComp = static_cast<OAccessibleMenuItemComponent*>(xChild.get());
298 assert(pComp);
299 pComp->NotifyAccessibleEvent(AccessibleEventId::ROLE_CHANGED, Any(), Any());
300}
301
303{
304 if ( i >= 0 && o3tl::make_unsigned(i) < m_aAccessibleChildren.size() )
305 {
307 if ( xChild.is() )
308 {
309 OAccessibleMenuItemComponent* pComp = static_cast< OAccessibleMenuItemComponent* >( xChild.get() );
310 if ( pComp )
311 pComp->SetItemText( pComp->GetItemText() );
312 }
313 }
314}
315
316
318{
319 return m_aAccessibleChildren.size();
320}
321
322
324{
326 if ( !xChild.is() )
327 {
328 if ( m_pMenu )
329 {
330 // create a new child
332
333 if ( m_pMenu->GetItemType( static_cast<sal_uInt16>(i) ) == MenuItemType::SEPARATOR )
334 {
335 pChild = new VCLXAccessibleMenuSeparator( m_pMenu, static_cast<sal_uInt16>(i) );
336 }
337 else
338 {
339 PopupMenu* pPopupMenu = m_pMenu->GetPopupMenu( m_pMenu->GetItemId( static_cast<sal_uInt16>(i) ) );
340 if ( pPopupMenu )
341 {
342 pChild = new VCLXAccessibleMenu( m_pMenu, static_cast<sal_uInt16>(i), pPopupMenu );
343 pPopupMenu->SetAccessible( pChild );
344 }
345 else
346 {
347 pChild = new VCLXAccessibleMenuItem( m_pMenu, static_cast<sal_uInt16>(i) );
348 }
349 }
350
351 // set states
352 pChild->SetStates();
353
354 xChild = pChild;
355
356 // insert into menu item list
357 m_aAccessibleChildren[i] = xChild;
358 }
359 }
360
361 return xChild;
362}
363
364
366{
368 for ( sal_Int64 i = 0, nCount = getAccessibleChildCount(); i < nCount; ++i )
369 {
370 Reference< XAccessible > xAcc = getAccessibleChild( i );
371 if ( xAcc.is() )
372 {
373 Reference< XAccessibleComponent > xComp( xAcc->getAccessibleContext(), UNO_QUERY );
374 if ( xComp.is() )
375 {
376 tools::Rectangle aRect = VCLRectangle( xComp->getBounds() );
377 Point aPos = VCLPoint( rPoint );
378 if ( aRect.Contains( aPos ) )
379 {
380 xChild = xAcc;
381 break;
382 }
383 }
384 }
385 }
386
387 return xChild;
388}
389
390
392{
393 if ( i < 0 )
394 return;
395
397 i = m_aAccessibleChildren.size();
398
399 // insert entry in child list
401
402 // update item position of accessible children
403 for ( sal_uInt32 j = i, nCount = m_aAccessibleChildren.size(); j < nCount; ++j )
404 {
406 if ( xAcc.is() )
407 {
408 OAccessibleMenuItemComponent* pComp = static_cast< OAccessibleMenuItemComponent* >( xAcc.get() );
409 if ( pComp )
410 pComp->SetItemPos( static_cast<sal_uInt16>(j) );
411 }
412 }
413
414 // send accessible child event
416 if ( xChild.is() )
417 {
418 Any aOldValue, aNewValue;
419 aNewValue <<= xChild;
420 NotifyAccessibleEvent( AccessibleEventId::CHILD, aOldValue, aNewValue );
421 }
422}
423
424
426{
427 if ( i < 0 || o3tl::make_unsigned(i) >= m_aAccessibleChildren.size() )
428 return;
429
430 // keep the accessible of the removed item
432
433 // remove entry in child list
435
436 // update item position of accessible children
437 for ( sal_uInt32 j = i, nCount = m_aAccessibleChildren.size(); j < nCount; ++j )
438 {
440 if ( xAcc.is() )
441 {
442 OAccessibleMenuItemComponent* pComp = static_cast< OAccessibleMenuItemComponent* >( xAcc.get() );
443 if ( pComp )
444 pComp->SetItemPos( static_cast<sal_uInt16>(j) );
445 }
446 }
447
448 // send accessible child event
449 if ( xChild.is() )
450 {
451 Any aOldValue, aNewValue;
452 aOldValue <<= xChild;
453 NotifyAccessibleEvent( AccessibleEventId::CHILD, aOldValue, aNewValue );
454
455 Reference< XComponent > xComponent( xChild, UNO_QUERY );
456 if ( xComponent.is() )
457 xComponent->dispose();
458 }
459}
460
461
463{
464 return false;
465}
466
467
469{
470 bool bChildHighlighted = false;
471
472 for (const Reference<XAccessible>& xChild : m_aAccessibleChildren)
473 {
474 if ( xChild.is() )
475 {
476 OAccessibleMenuBaseComponent* pComp = static_cast< OAccessibleMenuBaseComponent* >( xChild.get() );
477 if ( pComp && pComp->IsHighlighted() )
478 {
479 bChildHighlighted = true;
480 break;
481 }
482 }
483 }
484
485 return bChildHighlighted;
486}
487
488
490{
491 // open the menu
492 if ( getAccessibleRole() == AccessibleRole::MENU && !IsPopupMenuOpen() )
493 Click();
494
495 // highlight the child
496 if ( m_pMenu )
497 m_pMenu->HighlightItem( static_cast<sal_uInt16>(i) );
498}
499
500
502{
503 if ( m_pMenu )
504 m_pMenu->DeHighlight();
505}
506
507
509{
510 bool bSelected = false;
511
512 if ( m_pMenu && m_pMenu->IsHighlighted( static_cast<sal_uInt16>(i) ) )
513 bSelected = true;
514
515 return bSelected;
516}
517
518
520{
521}
522
523
525{
526 return false;
527}
528
529
530IMPL_LINK( OAccessibleMenuBaseComponent, MenuEventListener, VclMenuEvent&, rEvent, void )
531{
532 OSL_ENSURE( rEvent.GetMenu(), "OAccessibleMenuBaseComponent - Menu?" );
533 ProcessMenuEvent( rEvent );
534}
535
536
538{
539 sal_uInt16 nItemPos = rVclMenuEvent.GetItemPos();
540
541 switch ( rVclMenuEvent.GetId() )
542 {
543 case VclEventId::MenuShow:
544 case VclEventId::MenuHide:
545 {
547 }
548 break;
549 case VclEventId::MenuHighlight:
550 {
551 SetFocused( false );
552 UpdateFocused( nItemPos, true );
553 UpdateSelected( nItemPos, true );
554 }
555 break;
556 case VclEventId::MenuDehighlight:
557 {
558 UpdateFocused( nItemPos, false );
559 UpdateSelected( nItemPos, false );
560 }
561 break;
562 case VclEventId::MenuSubmenuActivate:
563 {
564 }
565 break;
566 case VclEventId::MenuSubmenuDeactivate:
567 {
568 UpdateFocused( nItemPos, true );
569 }
570 break;
571 case VclEventId::MenuEnable:
572 {
573 UpdateEnabled( nItemPos, true );
574 }
575 break;
576 case VclEventId::MenuDisable:
577 {
578 UpdateEnabled( nItemPos, false );
579 }
580 break;
581 case VclEventId::MenuSubmenuChanged:
582 {
583 RemoveChild( nItemPos );
584 InsertChild( nItemPos );
585 }
586 break;
587 case VclEventId::MenuInsertItem:
588 {
589 InsertChild( nItemPos );
590 }
591 break;
592 case VclEventId::MenuRemoveItem:
593 {
594 RemoveChild( nItemPos );
595 }
596 break;
597 case VclEventId::MenuAccessibleNameChanged:
598 {
599 UpdateAccessibleName( nItemPos );
600 }
601 break;
602 case VclEventId::MenuItemRoleChanged:
603 {
604 UpdateItemRole(nItemPos);
605 }
606 break;
607 case VclEventId::MenuItemTextChanged:
608 {
609 UpdateAccessibleName( nItemPos );
610 UpdateItemText( nItemPos );
611 }
612 break;
613 case VclEventId::MenuItemChecked:
614 {
615 UpdateChecked( nItemPos, true );
616 }
617 break;
618 case VclEventId::MenuItemUnchecked:
619 {
620 UpdateChecked( nItemPos, false );
621 }
622 break;
623 case VclEventId::ObjectDying:
624 {
625 if ( m_pMenu )
626 {
627 m_pMenu->RemoveEventListener( LINK( this, OAccessibleMenuBaseComponent, MenuEventListener ) );
628
629 m_pMenu = nullptr;
630
631 // dispose all menu items
632 for (const Reference<XAccessible>& i : m_aAccessibleChildren)
633 {
634 Reference< XComponent > xComponent( i, UNO_QUERY );
635 if ( xComponent.is() )
636 xComponent->dispose();
637 }
638 m_aAccessibleChildren.clear();
639 }
640 }
641 break;
642 default:
643 {
644 }
645 break;
646 }
647}
648
649
650// XComponent
651
652
654{
655 OAccessibleExtendedComponentHelper::disposing();
656
657 if ( !m_pMenu )
658 return;
659
660 m_pMenu->RemoveEventListener( LINK( this, OAccessibleMenuBaseComponent, MenuEventListener ) );
661
662 m_pMenu = nullptr;
663
664 // dispose all menu items
665 for (const Reference<XAccessible>& i : m_aAccessibleChildren)
666 {
667 Reference< XComponent > xComponent( i, UNO_QUERY );
668 if ( xComponent.is() )
669 xComponent->dispose();
670 }
671 m_aAccessibleChildren.clear();
672}
673
674
675// XServiceInfo
676
677
679{
680 return cppu::supportsService(this, rServiceName);
681}
682
683
684// XAccessible
685
686
688{
689 OExternalLockGuard aGuard( this );
690
691 return this;
692}
693
694
695// XAccessibleContext
696
697
699{
700 OExternalLockGuard aGuard( this );
701
702 sal_Int64 nStateSet = 0;
703
704 if ( !rBHelper.bDisposed && !rBHelper.bInDispose )
705 {
706 FillAccessibleStateSet( nStateSet );
707 }
708 else
709 {
710 nStateSet |= AccessibleStateType::DEFUNC;
711 }
712
713 return nStateSet;
714}
715
716
718{
719 return false;
720}
721
722/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
IMPL_LINK(OAccessibleMenuBaseComponent, MenuEventListener, VclMenuEvent &, rEvent, void)
void SetAccessible(const css::uno::Reference< css::accessibility::XAccessible > &rxAccessible)
virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext() override
virtual void SAL_CALL disposing() override
virtual sal_Bool SAL_CALL supportsService(const OUString &rServiceName) override
virtual sal_Int64 SAL_CALL getAccessibleStateSet() override
css::uno::Reference< css::accessibility::XAccessible > GetChild(sal_Int64 i)
void UpdateChecked(sal_Int32 i, bool bChecked)
void ProcessMenuEvent(const VclMenuEvent &rVclMenuEvent)
css::uno::Reference< css::accessibility::XAccessible > GetChildAt(const css::awt::Point &rPoint)
void UpdateEnabled(sal_Int32 i, bool bEnabled)
void UpdateFocused(sal_Int32 i, bool bFocused)
void UpdateSelected(sal_Int32 i, bool bSelected)
virtual void FillAccessibleStateSet(sal_Int64 &rStateSet)=0
void SetItemText(const OUString &sItemText)
void SetAccessibleName(const OUString &sAccessibleName)
sal_uInt16 GetItemPos() const
VclEventId GetId() const
bool Contains(const Point &rPOINT) const
inline ::tools::Rectangle VCLRectangle(const css::awt::Rectangle &rAWTRect)
inline ::Point VCLPoint(const css::awt::Point &rAWTPoint)
int nCount
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
int i
constexpr std::enable_if_t< std::is_signed_v< T >, std::make_unsigned_t< T > > make_unsigned(T value)
bool bVisible
unsigned char sal_Bool