1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <com/sun/star/accessibility/XAccessible.hpp>
21 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
22 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
23 #include <com/sun/star/accessibility/AccessibleRole.hpp>
24 #include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
26 #include <vcl/svapp.hxx>
28 #include <AccContainerEventListener.hxx>
29 #include <AccObjectManagerAgent.hxx>
30 #include <unomsaaevent.hxx>
32 using namespace com::sun::star::uno
;
33 using namespace com::sun::star::accessibility
;
35 AccContainerEventListener::AccContainerEventListener(css::accessibility::XAccessible
* pAcc
, AccObjectManagerAgent
* Agent
)
36 :AccEventListener(pAcc
, Agent
)
40 AccContainerEventListener::~AccContainerEventListener()
45 * Uno's event notifier when event is captured
47 * @param AccessibleEventObject the event object which contains information about event
49 void AccContainerEventListener::notifyEvent( const css::accessibility::AccessibleEventObject
& aEvent
)
53 switch (aEvent
.EventId
)
55 case AccessibleEventId::CHILD
:
56 HandleChildChangedEvent(aEvent
.OldValue
, aEvent
.NewValue
);
58 case AccessibleEventId::SELECTION_CHANGED
:
59 HandleSelectionChangedEvent(aEvent
.OldValue
, aEvent
.NewValue
);
61 case AccessibleEventId::INVALIDATE_ALL_CHILDREN
:
62 HandleAllChildrenChangedEvent();
64 case AccessibleEventId::TEXT_CHANGED
:
65 HandleTextChangedEvent(aEvent
.OldValue
, aEvent
.NewValue
);
66 [[fallthrough
]]; //TODO ???
67 case AccessibleEventId::VISIBLE_DATA_CHANGED
:
68 HandleVisibleDataChangedEvent();
70 case AccessibleEventId::BOUNDRECT_CHANGED
:
71 HandleBoundrectChangedEvent();
73 case AccessibleEventId::STATE_CHANGED
:
74 HandleStateChangedEvent(aEvent
.OldValue
, aEvent
.NewValue
);
76 case AccessibleEventId::VALUE_CHANGED
:
77 HandleValueChangedEvent(aEvent
.OldValue
, aEvent
.NewValue
);
79 case AccessibleEventId::SELECTION_CHANGED_ADD
:
80 HandleSelectionChangedAddEvent(aEvent
.OldValue
, aEvent
.NewValue
);
82 case AccessibleEventId::SELECTION_CHANGED_REMOVE
:
83 HandleSelectionChangedRemoveEvent(aEvent
.OldValue
, aEvent
.NewValue
);
85 case AccessibleEventId::SELECTION_CHANGED_WITHIN
:
86 HandleSelectionChangedWithinEvent(aEvent
.OldValue
, aEvent
.NewValue
);
88 case AccessibleEventId::PAGE_CHANGED
:
89 HandlePageChangedEvent(aEvent
.OldValue
, aEvent
.NewValue
);
91 case AccessibleEventId::SECTION_CHANGED
:
92 HandleSectionChangedEvent(aEvent
.OldValue
, aEvent
.NewValue
);
94 case AccessibleEventId::COLUMN_CHANGED
:
95 HandleColumnChangedEvent(aEvent
.OldValue
, aEvent
.NewValue
);
98 AccEventListener::notifyEvent(aEvent
);
103 void AccContainerEventListener::HandleStateChangedEvent(Any oldValue
, Any newValue
)
106 if( newValue
>>= State
)
108 SetComponentState(State
, true);
110 else if (oldValue
>>= State
)
112 SetComponentState(State
, false);
118 * handle the CHILD event
119 * @param oldValue the child to be deleted
120 * @param newValue the child to be added
122 void AccContainerEventListener::HandleChildChangedEvent(Any oldValue
, Any newValue
)
124 Reference
< XAccessible
> xChild
;
125 if( newValue
>>= xChild
)
130 XAccessible
* pAcc
= xChild
.get();
133 if (pAgent
->InsertAccObj(pAcc
, m_xAccessible
.get()))
135 //add all oldValue's existing children
136 pAgent
->InsertChildrenAccObj(pAcc
);
137 pAgent
->NotifyAccEvent(UnoMSAAEvent::CHILD_ADDED
, pAcc
);
141 else if (oldValue
>>= xChild
)
143 //delete an existing child
146 XAccessible
* pAcc
= xChild
.get();
147 pAgent
->NotifyAccEvent(UnoMSAAEvent::CHILD_REMOVED
, pAcc
);
148 //delete all oldValue's existing children
149 pAgent
->DeleteChildrenAccObj( pAcc
);
151 pAgent
->DeleteAccObj( pAcc
);
159 * handle the SELECTION_CHANGED event
160 * @param oldValue the old value of the source of event
161 * @param newValue the new value of the source of event
163 void AccContainerEventListener::HandleSelectionChangedEvent(const Any
& /*oldValue*/, const Any
& newValue
)
165 if (NotifyChildEvent(UnoMSAAEvent::SELECTION_CHANGED
, newValue
))
170 //menu bar does not process selection change event,just same as word behavior
171 if (GetRole()!=AccessibleRole::MENU_BAR
)
172 pAgent
->NotifyAccEvent(UnoMSAAEvent::SELECTION_CHANGED
, m_xAccessible
.get());
176 * handle the INVALIDATE_ALL_CHILDREN event
178 void AccContainerEventListener::HandleAllChildrenChangedEvent()
180 //TODO: update all the children
181 if (m_xAccessible
.is())
183 //delete all oldValue's existing children
184 pAgent
->DeleteChildrenAccObj(m_xAccessible
.get());
185 //add all oldValue's existing children
186 pAgent
->InsertChildrenAccObj(m_xAccessible
.get());
187 pAgent
->NotifyAccEvent(UnoMSAAEvent::OBJECT_REORDER
, m_xAccessible
.get());
192 * handle the TEXT_CHANGED event
194 void AccContainerEventListener::HandleTextChangedEvent(Any
, Any newValue
)
196 pAgent
->UpdateValue(m_xAccessible
.get(), newValue
);
197 pAgent
->NotifyAccEvent(UnoMSAAEvent::OBJECT_TEXTCHANGE
, m_xAccessible
.get());
201 * set the new state and fire the MSAA event
202 * @param state new state id
203 * @param enable true if state is set, false if state is unset
205 void AccContainerEventListener::SetComponentState(sal_Int64 state
, bool enable
)
207 // only the following state can be fired state event.
211 case AccessibleStateType::SELECTED
:
212 case AccessibleStateType::BUSY
:
213 case AccessibleStateType::INDETERMINATE
:
214 case AccessibleStateType::OFFSCREEN
:
215 case AccessibleStateType::FOCUSABLE
:
216 case AccessibleStateType::SHOWING
:
217 case AccessibleStateType::VISIBLE
:
218 FireStatePropertyChange(state
, enable
);
220 case AccessibleStateType::FOCUSED
:
221 FireStateFocusedChange(enable
);
223 case AccessibleStateType::ENABLED
:
226 pAgent
->DecreaseState(m_xAccessible
.get(), AccessibleStateType::DEFUNC
);
227 pAgent
->IncreaseState(m_xAccessible
.get(), AccessibleStateType::FOCUSABLE
);
228 pAgent
->UpdateState(m_xAccessible
.get());
230 UpdateAllChildrenState(m_xAccessible
.get());
234 pAgent
->IncreaseState(m_xAccessible
.get(), AccessibleStateType::DEFUNC
);
235 pAgent
->DecreaseState(m_xAccessible
.get(), AccessibleStateType::FOCUSABLE
);
236 pAgent
->UpdateState(m_xAccessible
.get());
238 UpdateAllChildrenState(m_xAccessible
.get());
241 case AccessibleStateType::ACTIVE
:
242 // Only frames should be active
243 // no msaa state mapping
244 //for PAGE_TAB_LIST, there will be ACTIVE state, then it should be converted to FOCUSED event.
245 if (GetRole() == AccessibleRole::PAGE_TAB_LIST
)
247 if (!enable
) /* get the active state */
249 pAgent
->IncreaseState(m_xAccessible
.get(), AccessibleStateType::FOCUSED
);
252 else /* lose the active state */
254 pAgent
->DecreaseState(m_xAccessible
.get(), AccessibleStateType::FOCUSED
);
259 case AccessibleStateType::EXPANDED
:
260 case AccessibleStateType::COLLAPSE
:
261 case AccessibleStateType::CHECKED
:
263 pAgent
->UpdateState(m_xAccessible
.get());
264 pAgent
->NotifyAccEvent(UnoMSAAEvent::STATE_BUSY
, m_xAccessible
.get());
274 * fire the MSAA state changed event
275 * @param state the state id
276 * @param set true if state is set, false if state is unset
278 void AccContainerEventListener::FireStatePropertyChange(sal_Int64 state
, bool set
)
285 case AccessibleStateType::SELECTED
:
286 pAgent
->IncreaseState(m_xAccessible
.get(), state
);
288 case AccessibleStateType::INDETERMINATE
:
289 case AccessibleStateType::BUSY
:
290 case AccessibleStateType::FOCUSABLE
:
291 case AccessibleStateType::OFFSCREEN
:
292 pAgent
->IncreaseState(m_xAccessible
.get(), state
);
293 pAgent
->NotifyAccEvent(UnoMSAAEvent::STATE_BUSY
, m_xAccessible
.get());
295 case AccessibleStateType::SHOWING
:
296 // UNO !SHOWING == MSAA OFFSCREEN
297 pAgent
->IncreaseState(m_xAccessible
.get(), AccessibleStateType::SHOWING
);
299 case AccessibleStateType::VISIBLE
:
300 // UNO !VISIBLE == MSAA INVISIBLE
301 pAgent
->IncreaseState(m_xAccessible
.get(), AccessibleStateType::VISIBLE
);
312 case AccessibleStateType::SELECTED
:
313 pAgent
->DecreaseState(m_xAccessible
.get(), state
);
315 case AccessibleStateType::BUSY
:
316 case AccessibleStateType::INDETERMINATE
:
317 case AccessibleStateType::FOCUSABLE
:
318 case AccessibleStateType::OFFSCREEN
:
319 pAgent
->DecreaseState(m_xAccessible
.get(), state
);
320 pAgent
->NotifyAccEvent(UnoMSAAEvent::STATE_BUSY
, m_xAccessible
.get());
322 case AccessibleStateType::SHOWING
:
323 // UNO !SHOWING == MSAA OFFSCREEN
324 pAgent
->DecreaseState(m_xAccessible
.get(), AccessibleStateType::SHOWING
);
326 case AccessibleStateType::VISIBLE
:
327 // UNO !VISIBLE == MSAA INVISIBLE
328 pAgent
->DecreaseState(m_xAccessible
.get(), AccessibleStateType::VISIBLE
);
337 * handle the focused event
338 * @param enable true if get focus, false if lose focus
340 void AccContainerEventListener::FireStateFocusedChange(bool enable
)
344 pAgent
->IncreaseState(m_xAccessible
.get(), AccessibleStateType::FOCUSED
);
345 // if the acc role is MENU_BAR, UnoMSAAEvent::MENU_START event should be sent
346 // if the acc role is POPUP_MENU, UnoMSAAEvent::MENUPOPUPSTART event should be sent
347 short role
= GetRole();
348 if(role
== AccessibleRole::MENU_BAR
)
350 pAgent
->NotifyAccEvent(UnoMSAAEvent::MENU_START
, m_xAccessible
.get());
352 else if (role
== AccessibleRole::POPUP_MENU
)
353 pAgent
->NotifyAccEvent(UnoMSAAEvent::MENUPOPUPSTART
, m_xAccessible
.get());
354 //Disable the focused event on option_pane and Panel.
355 //only disable option_pane for toolbar has panel to get focus
356 else if (role
== AccessibleRole::PANEL
|| role
== AccessibleRole::OPTION_PANE
)
358 //don't send focused event on PANEL & OPTION_PANE if the parent is not toolbar
359 short parentRole
= GetParentRole();
360 if (parentRole
== AccessibleRole::TOOL_BAR
361 || parentRole
== AccessibleRole::SCROLL_PANE
// sidebar
362 || parentRole
== AccessibleRole::PANEL
) // sidebar
363 pAgent
->NotifyAccEvent(UnoMSAAEvent::STATE_FOCUSED
, m_xAccessible
.get());
365 else if (role
== AccessibleRole::COMBO_BOX
)
367 //for editable combobox, send focus event on only edit control,
368 bool bSendFocusOnCombobox
= true;
369 //send focused event to the first text child
370 Reference
<XAccessibleContext
> mxContext
= m_xAccessible
->getAccessibleContext();
373 Reference
<XAccessible
> mxChild
= mxContext
->getAccessibleChild(0);
376 Reference
<XAccessibleContext
> mxChildContext
= mxChild
->getAccessibleContext();
377 short childrole
= mxChildContext
->getAccessibleRole();
378 if (childrole
== AccessibleRole::TEXT
)
380 if (IsEditable(mxChildContext
))
382 pAgent
->DecreaseState(m_xAccessible
.get(), AccessibleStateType::FOCUSED
);
383 pAgent
->IncreaseState( mxChild
.get(), AccessibleStateType::FOCUSED
);
384 pAgent
->NotifyAccEvent(UnoMSAAEvent::STATE_FOCUSED
, mxChild
.get());
385 bSendFocusOnCombobox
= false;
390 if (bSendFocusOnCombobox
)
391 pAgent
->NotifyAccEvent(UnoMSAAEvent::STATE_FOCUSED
, m_xAccessible
.get());
394 pAgent
->NotifyAccEvent(UnoMSAAEvent::STATE_FOCUSED
, m_xAccessible
.get());
398 pAgent
->DecreaseState(m_xAccessible
.get(), AccessibleStateType::FOCUSED
);
399 // if the acc role is MENU_BAR, UnoMSAAEvent::MENU_END event should be sent
400 // if the acc role is POPUP_MENU, UnoMSAAEvent::MENUPOPUPEND event should be sent
401 if (GetRole() == AccessibleRole::MENU_BAR
)
403 pAgent
->NotifyAccEvent(UnoMSAAEvent::MENU_END
, m_xAccessible
.get());
405 else if (GetRole() == AccessibleRole::POPUP_MENU
)
407 pAgent
->NotifyAccEvent(UnoMSAAEvent::MENUPOPUPEND
, m_xAccessible
.get());
413 * handle the VALUE_CHANGED event
415 * @param oldValue the old value of the source of event
416 * @param newValue the new value of the source of event
418 void AccContainerEventListener::HandleValueChangedEvent(Any
, Any
)
420 pAgent
->UpdateValue(m_xAccessible
.get());
421 pAgent
->NotifyAccEvent(UnoMSAAEvent::OBJECT_VALUECHANGE
, m_xAccessible
.get());
424 bool AccContainerEventListener::IsEditable(Reference
<XAccessibleContext
> const & xContext
)
426 sal_Int64 nRState
= xContext
->getAccessibleStateSet();
427 return nRState
& AccessibleStateType::EDITABLE
;
430 bool AccContainerEventListener::NotifyChildEvent(UnoMSAAEvent eWinEvent
, const Any
& Value
)
432 Reference
< XAccessible
> xChild
;
433 if(Value
>>= xChild
)
437 XAccessible
* pAcc
= xChild
.get();
438 pAgent
->NotifyAccEvent(eWinEvent
, pAcc
);
445 void AccContainerEventListener::HandleSelectionChangedAddEvent(const Any
& /*oldValue*/, const Any
& newValue
)
447 if (NotifyChildEvent(UnoMSAAEvent::SELECTION_CHANGED_ADD
, newValue
))
451 pAgent
->NotifyAccEvent(UnoMSAAEvent::SELECTION_CHANGED_ADD
, m_xAccessible
.get());
454 void AccContainerEventListener::HandleSelectionChangedRemoveEvent(const Any
& /*oldValue*/, const Any
& newValue
)
456 if (NotifyChildEvent(UnoMSAAEvent::SELECTION_CHANGED_REMOVE
, newValue
))
460 pAgent
->NotifyAccEvent(UnoMSAAEvent::SELECTION_CHANGED_REMOVE
, m_xAccessible
.get());
463 void AccContainerEventListener::HandleSelectionChangedWithinEvent(const Any
& /*oldValue*/, const Any
& newValue
)
465 if (NotifyChildEvent(UnoMSAAEvent::SELECTION_CHANGED_WITHIN
, newValue
))
469 pAgent
->NotifyAccEvent(UnoMSAAEvent::SELECTION_CHANGED_WITHIN
, m_xAccessible
.get());
472 void AccContainerEventListener::UpdateAllChildrenState(XAccessible
* pXAccessible
)
474 Reference
<css::accessibility::XAccessibleContext
> xContext
= pXAccessible
->getAccessibleContext();
479 css::accessibility::XAccessibleContext
* pAccessibleContext
= xContext
.get();
480 if(pAccessibleContext
== nullptr)
485 if (pAgent
&& pAgent
->IsStateManageDescendant(pXAccessible
))
490 const sal_Int64 nCount
= pAccessibleContext
->getAccessibleChildCount();
491 for (sal_Int64 i
= 0; i
< nCount
; i
++)
493 Reference
<css::accessibility::XAccessible
> mxAccessible
494 = pAccessibleContext
->getAccessibleChild(i
);
496 css::accessibility::XAccessible
* mpAccessible
= mxAccessible
.get();
497 if(mpAccessible
!= nullptr)
499 pAgent
->UpdateState(mpAccessible
);
500 UpdateAllChildrenState(mpAccessible
);
505 void AccContainerEventListener::HandlePageChangedEvent(const Any
& /*oldValue*/, const Any
& /*newValue*/)
507 pAgent
->NotifyAccEvent(UnoMSAAEvent::OBJECT_PAGECHANGED
, m_xAccessible
.get());
510 void AccContainerEventListener::HandleSectionChangedEvent(const Any
& /*oldValue*/, const Any
& /*newValue*/ )
512 pAgent
->NotifyAccEvent(UnoMSAAEvent::SECTION_CHANGED
, m_xAccessible
.get());
515 void AccContainerEventListener::HandleColumnChangedEvent(const Any
& /*oldValue*/, const Any
& /*newValue*/)
517 pAgent
->NotifyAccEvent(UnoMSAAEvent::COLUMN_CHANGED
, m_xAccessible
.get());
520 void AccContainerEventListener::HandleNameChangedEvent( Any name
)
522 if (GetRole() == AccessibleRole::COMBO_BOX
)
524 Reference
<XAccessibleContext
> mxContext(m_xAccessible
->getAccessibleContext());
527 Reference
<XAccessible
> mxChild
= mxContext
->getAccessibleChild(0);
530 Reference
<XAccessibleContext
> mxChildContext
= mxChild
->getAccessibleContext();
531 short childrole
= mxChildContext
->getAccessibleRole();
532 if (childrole
== AccessibleRole::TEXT
)
534 pAgent
->UpdateAccName(mxChild
.get(), name
);
539 AccEventListener::HandleNameChangedEvent(name
);
542 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */