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(UM_EVENT_CHILD_ADDED
, pAcc
);
141 else if (oldValue
>>= xChild
)
143 //delete an existing child
146 XAccessible
* pAcc
= xChild
.get();
147 pAgent
->NotifyAccEvent(UM_EVENT_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(UM_EVENT_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(UM_EVENT_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(UM_EVENT_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(UM_EVENT_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(short 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(UM_EVENT_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(short 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(UM_EVENT_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(UM_EVENT_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, MSAA UM_EVENT_MENU_START event should be sent
346 //if the acc role is POPUP_MENU, MSAA UM_EVENT_MENUPOPUPSTART event should be sent
347 short role
= GetRole();
348 if(role
== AccessibleRole::MENU_BAR
)
350 pAgent
->NotifyAccEvent(UM_EVENT_MENU_START
, m_xAccessible
.get());
352 else if (role
== AccessibleRole::POPUP_MENU
)
353 pAgent
->NotifyAccEvent(UM_EVENT_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(UM_EVENT_STATE_FOCUSED
, m_xAccessible
.get());
365 //to update ComboBox's description
366 else if (role
== AccessibleRole::COMBO_BOX
)
368 pAgent
->UpdateDescription(m_xAccessible
.get());
369 //for editable combobox, send focus event on only edit control,
370 bool bSendFocusOnCombobox
= true;
371 //send focused event to the first text child
372 Reference
<XAccessibleContext
> mxContext
= m_xAccessible
.get()->getAccessibleContext();
375 Reference
<XAccessible
> mxChild
= mxContext
->getAccessibleChild(0);
378 Reference
<XAccessibleContext
> mxChildContext
= mxChild
->getAccessibleContext();
379 short childrole
= mxChildContext
->getAccessibleRole();
380 if (childrole
== AccessibleRole::TEXT
)
382 if (IsEditable(mxChildContext
))
384 pAgent
->DecreaseState(m_xAccessible
.get(), AccessibleStateType::FOCUSED
);
385 pAgent
->IncreaseState( mxChild
.get(), AccessibleStateType::FOCUSED
);
386 pAgent
->NotifyAccEvent(UM_EVENT_STATE_FOCUSED
, mxChild
.get());
387 bSendFocusOnCombobox
= false;
392 if (bSendFocusOnCombobox
)
393 pAgent
->NotifyAccEvent(UM_EVENT_STATE_FOCUSED
, m_xAccessible
.get());
396 pAgent
->NotifyAccEvent(UM_EVENT_STATE_FOCUSED
, m_xAccessible
.get());
400 pAgent
->DecreaseState(m_xAccessible
.get(), AccessibleStateType::FOCUSED
);
401 //if the acc role is MENU_BAR, MSAA UM_EVENT_MENU_END event should be sent
402 //if the acc role is POPUP_MENU, MSAA UM_EVENT_MENUPOPUPEND event should be sent
403 if (GetRole() == AccessibleRole::MENU_BAR
)
405 pAgent
->NotifyAccEvent(UM_EVENT_MENU_END
, m_xAccessible
.get());
407 else if (GetRole() == AccessibleRole::POPUP_MENU
)
409 pAgent
->NotifyAccEvent(UM_EVENT_MENUPOPUPEND
, m_xAccessible
.get());
415 * handle the VALUE_CHANGED event
417 * @param oldValue the old value of the source of event
418 * @param newValue the new value of the source of event
420 void AccContainerEventListener::HandleValueChangedEvent(Any
, Any
)
422 pAgent
->UpdateValue(m_xAccessible
.get());
423 pAgent
->NotifyAccEvent(UM_EVENT_OBJECT_VALUECHANGE
, m_xAccessible
.get());
426 bool AccContainerEventListener::IsEditable(Reference
<XAccessibleContext
> const & xContext
)
428 Reference
< XAccessibleStateSet
> pRState
= xContext
->getAccessibleStateSet();
432 Sequence
<short> pStates
= pRState
->getStates();
433 int count
= pStates
.getLength();
434 for( int iIndex
= 0;iIndex
< count
;iIndex
++ )
436 if(pStates
[iIndex
] == AccessibleStateType::EDITABLE
)
442 bool AccContainerEventListener::NotifyChildEvent(short nWinEvent
,const Any
&Value
)
444 Reference
< XAccessible
> xChild
;
445 if(Value
>>= xChild
)
449 XAccessible
* pAcc
= xChild
.get();
450 pAgent
->NotifyAccEvent(nWinEvent
, pAcc
);
457 void AccContainerEventListener::HandleSelectionChangedAddEvent(const Any
& /*oldValue*/, const Any
& newValue
)
459 if(NotifyChildEvent(UM_EVENT_SELECTION_CHANGED_ADD
,newValue
))
463 pAgent
->NotifyAccEvent(UM_EVENT_SELECTION_CHANGED_ADD
, m_xAccessible
.get());
466 void AccContainerEventListener::HandleSelectionChangedRemoveEvent(const Any
& /*oldValue*/, const Any
& newValue
)
468 if(NotifyChildEvent(UM_EVENT_SELECTION_CHANGED_REMOVE
,newValue
))
472 pAgent
->NotifyAccEvent(UM_EVENT_SELECTION_CHANGED_REMOVE
, m_xAccessible
.get());
475 void AccContainerEventListener::HandleSelectionChangedWithinEvent(const Any
& /*oldValue*/, const Any
& newValue
)
477 if(NotifyChildEvent(UM_EVENT_SELECTION_CHANGED_WITHIN
,newValue
))
481 pAgent
->NotifyAccEvent(UM_EVENT_SELECTION_CHANGED_WITHIN
, m_xAccessible
.get());
484 void AccContainerEventListener::UpdateAllChildrenState(XAccessible
* pXAccessible
)
486 Reference
<css::accessibility::XAccessibleContext
> xContext
= pXAccessible
->getAccessibleContext();
491 css::accessibility::XAccessibleContext
* pAccessibleContext
= xContext
.get();
492 if(pAccessibleContext
== nullptr)
497 if (pAgent
&& pAgent
->IsStateManageDescendant(pXAccessible
))
502 int count
= pAccessibleContext
->getAccessibleChildCount();
503 for (int i
=0;i
<count
;i
++)
505 Reference
<css::accessibility::XAccessible
> mxAccessible
506 = pAccessibleContext
->getAccessibleChild(i
);
508 css::accessibility::XAccessible
* mpAccessible
= mxAccessible
.get();
509 if(mpAccessible
!= nullptr)
511 pAgent
->UpdateState(mpAccessible
);
512 UpdateAllChildrenState(mpAccessible
);
517 void AccContainerEventListener::HandlePageChangedEvent(const Any
& /*oldValue*/, const Any
& /*newValue*/)
519 pAgent
->NotifyAccEvent(UM_EVENT_OBJECT_PAGECHANGED
, m_xAccessible
.get());
522 void AccContainerEventListener::HandleSectionChangedEvent(const Any
& /*oldValue*/, const Any
& /*newValue*/ )
524 pAgent
->NotifyAccEvent(UM_EVENT_SECTION_CHANGED
, m_xAccessible
.get());
527 void AccContainerEventListener::HandleColumnChangedEvent(const Any
& /*oldValue*/, const Any
& /*newValue*/)
529 pAgent
->NotifyAccEvent(UM_EVENT_COLUMN_CHANGED
, m_xAccessible
.get());
532 void AccContainerEventListener::HandleNameChangedEvent( Any name
)
534 if (GetRole() == AccessibleRole::COMBO_BOX
)
536 Reference
<XAccessibleContext
> mxContext(m_xAccessible
->getAccessibleContext());
539 Reference
<XAccessible
> mxChild
= mxContext
->getAccessibleChild(0);
542 Reference
<XAccessibleContext
> mxChildContext
= mxChild
->getAccessibleContext();
543 short childrole
= mxChildContext
->getAccessibleRole();
544 if (childrole
== AccessibleRole::TEXT
)
546 pAgent
->UpdateAccName(mxChild
.get(), name
);
551 AccEventListener::HandleNameChangedEvent(name
);
554 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */