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 #include <com/sun/star/accessibility/XAccessible.hpp>
23 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
24 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
25 #include <com/sun/star/accessibility/AccessibleRole.hpp>
26 #include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
27
28 #include "AccContainerEventListener.hxx"
29 #include "AccObjectManagerAgent.hxx"
30 #include "unomsaaevent.hxx"
31
32 using namespace com::sun::star::uno;
33 using namespace com::sun::star::accessibility;
34
AccContainerEventListener(com::sun::star::accessibility::XAccessible * pAcc,AccObjectManagerAgent * Agent)35 AccContainerEventListener::AccContainerEventListener(com::sun::star::accessibility::XAccessible* pAcc, AccObjectManagerAgent* Agent)
36 :AccEventListener(pAcc, Agent)
37 {
38 }
39
~AccContainerEventListener()40 AccContainerEventListener::~AccContainerEventListener()
41 {
42 }
43
44 /**
45 * Uno's event notifier when event is captured
46 *
47 * @param AccessibleEventObject the event object which contains information about event
48 */
notifyEvent(const::com::sun::star::accessibility::AccessibleEventObject & aEvent)49 void AccContainerEventListener::notifyEvent( const ::com::sun::star::accessibility::AccessibleEventObject& aEvent )
50 throw (::com::sun::star::uno::RuntimeException)
51 {
52 short role = getRole();
53 switch (aEvent.EventId)
54 {
55 case AccessibleEventId::CHILD:
56 handleChildChangedEvent(aEvent.OldValue, aEvent.NewValue);
57 break;
58 case AccessibleEventId::SELECTION_CHANGED:
59 handleSelectionChangedEvent(aEvent.OldValue, aEvent.NewValue);
60 break;
61 case AccessibleEventId::INVALIDATE_ALL_CHILDREN:
62 handleAllChildrenChangedEvent();
63 break;
64 case AccessibleEventId::TEXT_CHANGED:
65 handleTextChangedEvent(aEvent.OldValue, aEvent.NewValue);
66 case AccessibleEventId::VISIBLE_DATA_CHANGED:
67 handleVisibleDataChangedEvent();
68 break;
69 case AccessibleEventId::BOUNDRECT_CHANGED:
70 handleBoundrectChangedEvent();
71 break;
72 case AccessibleEventId::STATE_CHANGED:
73 handleStateChangedEvent(aEvent.OldValue, aEvent.NewValue);
74 break;
75 case AccessibleEventId::VALUE_CHANGED:
76 handleValueChangedEvent(aEvent.OldValue, aEvent.NewValue);
77 break;
78 case AccessibleEventId::SELECTION_CHANGED_ADD:
79 handleSelectionChangedAddEvent(aEvent.OldValue, aEvent.NewValue);
80 break;
81 case AccessibleEventId::SELECTION_CHANGED_REMOVE:
82 handleSelectionChangedRemoveEvent(aEvent.OldValue, aEvent.NewValue);
83 break;
84 case AccessibleEventId::SELECTION_CHANGED_WITHIN:
85 handleSelectionChangedWithinEvent(aEvent.OldValue, aEvent.NewValue);
86 break;
87 case AccessibleEventId::PAGE_CHANGED:
88 handlePageChangedEvent(aEvent.OldValue, aEvent.NewValue);
89 break;
90 case AccessibleEventId::SECTION_CHANGED:
91 handleSectionChangedEvent(aEvent.OldValue, aEvent.NewValue);
92 break;
93 case AccessibleEventId::COLUMN_CHANGED:
94 handleColumnChangedEvent(aEvent.OldValue, aEvent.NewValue);
95 break;
96 default:
97 AccEventListener::notifyEvent(aEvent);
98 break;
99 }
100 }
101
102 /**
103 * handle the VISIBLE_DATA_CHANGED event
104 */
handleVisibleDataChangedEvent()105 void AccContainerEventListener::handleVisibleDataChangedEvent()
106 {
107 AccEventListener::handleVisibleDataChangedEvent();
108 }
109
110 /**
111 * handle the BOUNDRECT_CHANGED event
112 */
handleBoundrectChangedEvent()113 void AccContainerEventListener::handleBoundrectChangedEvent()
114 {
115 AccEventListener::handleBoundrectChangedEvent();
116 }
117
handleStateChangedEvent(Any oldValue,Any newValue)118 void AccContainerEventListener::handleStateChangedEvent(Any oldValue, Any newValue)
119 {
120 short State;
121 if( newValue >>= State)
122 {
123 setComponentState( State,true);
124 }
125 else if (oldValue >>= State)
126 {
127 setComponentState( State,false);
128 }
129
130 }
131
132 /**
133 * handle the CHILD event
134 * @param oldValue the child to be deleted
135 * @param newValue the child to be added
136 */
handleChildChangedEvent(Any oldValue,Any newValue)137 void AccContainerEventListener::handleChildChangedEvent(Any oldValue, Any newValue)
138 {
139 Reference< XAccessible > xChild;
140 if( newValue >>= xChild)
141 {
142 //create a new child
143 if(xChild.is())
144 {
145 XAccessible* pAcc = xChild.get();
146 //add this child
147
148 if(pAgent->InsertAccObj( pAcc,pAccessible))
149 {
150 //add all oldValue's existing children
151 pAgent->InsertChildrenAccObj(pAcc);
152 pAgent->NotifyAccEvent(UM_EVENT_CHILD_ADDED, pAcc);
153 }
154 }
155 else
156 {}
157 }
158 else if (oldValue >>= xChild)
159 {
160 //delete a existing child
161 if(xChild.is())
162 {
163 XAccessible* pAcc = xChild.get();
164 pAgent->NotifyAccEvent(UM_EVENT_CHILD_REMOVED, pAcc);
165 //delete all oldValue's existing children
166 pAgent->DeleteChildrenAccObj( pAcc );
167 //delete this child
168 pAgent->DeleteAccObj( pAcc );
169
170 }
171 else
172 {}
173 }
174
175 }
176
177 /**
178 * handle the SELECTION_CHANGED event
179 * @param oldValue the old value of the source of event
180 * @param newValue the new value of the source of event
181 */
handleSelectionChangedEvent(const Any &,const Any & newValue)182 void AccContainerEventListener::handleSelectionChangedEvent(const Any& /*oldValue*/, const Any& newValue)
183 {
184 if(NotifyChildEvent(UM_EVENT_SELECTION_CHANGED,newValue))
185 {
186 return ;
187 }
188
189 //menu bar does not process selection change event,just same as word behavior
190 if(getRole()!=AccessibleRole::MENU_BAR)
191 pAgent->NotifyAccEvent(UM_EVENT_SELECTION_CHANGED, pAccessible);
192 }
193
194 /**
195 * handle the INVALIDATE_ALL_CHILDREN event
196 */
handleAllChildrenChangedEvent()197 void AccContainerEventListener::handleAllChildrenChangedEvent()
198 {
199 //TODO: update all the children
200 if( pAccessible )
201 {
202 //delete all oldValue's existing children
203 pAgent->DeleteChildrenAccObj( pAccessible );
204 //add all oldValue's existing children
205 pAgent->InsertChildrenAccObj( pAccessible );
206 pAgent->NotifyAccEvent(UM_EVENT_OBJECT_REORDER , pAccessible);
207 }
208 }
209
210 /**
211 * handle the TEXT_CHANGED event
212 */
handleTextChangedEvent(Any oldValue,Any newValue)213 void AccContainerEventListener::handleTextChangedEvent(Any oldValue, Any newValue)
214 {
215 pAgent->UpdateValue(pAccessible, newValue);
216 pAgent->NotifyAccEvent(UM_EVENT_OBJECT_TEXTCHANGE, pAccessible);
217 }
218
219 /**
220 * set the new state and fire the MSAA event
221 * @param state new state id
222 * @param enable true if state is set, false if state is unset
223 */
setComponentState(short state,bool enable)224 void AccContainerEventListener::setComponentState(short state, bool enable )
225 {
226 // only the following state can be fired state event.
227
228 switch (state)
229 {
230 case AccessibleStateType::SELECTED:
231 case AccessibleStateType::BUSY:
232 case AccessibleStateType::INDETERMINATE:
233 case AccessibleStateType::OFFSCREEN:
234 case AccessibleStateType::FOCUSABLE:
235 case AccessibleStateType::SHOWING:
236 case AccessibleStateType::VISIBLE:
237 fireStatePropertyChange(state, enable);
238 break;
239 case AccessibleStateType::FOCUSED:
240 fireStateFocusdChange(enable);
241 break;
242 case AccessibleStateType::ENABLED:
243 if(enable)
244 {
245 pAgent->DecreaseState( pAccessible, AccessibleStateType::DEFUNC);
246 pAgent->IncreaseState( pAccessible, AccessibleStateType::FOCUSABLE);
247 pAgent->UpdateState(pAccessible);
248
249 UpdateAllChildrenState(pAccessible);
250 }
251 else
252 {
253 pAgent->IncreaseState( pAccessible, AccessibleStateType::DEFUNC);
254 pAgent->DecreaseState( pAccessible, AccessibleStateType::FOCUSABLE);
255 pAgent->UpdateState(pAccessible);
256
257 UpdateAllChildrenState(pAccessible);
258 }
259 break;
260 case AccessibleStateType::ACTIVE:
261 // Only frames should be active
262 // no msaa state mapping
263 //for PAGE_TAB_LIST, there will be ACTIVE state, then it should be converted to FOCUSED event.
264 if(getRole() == AccessibleRole::PAGE_TAB_LIST)
265 {
266 if (!enable) /* get the active state */
267 {
268 pAgent->IncreaseState( pAccessible, AccessibleStateType::FOCUSED);
269 }
270
271 else /* lose the active state */
272 {
273 pAgent->DecreaseState( pAccessible, AccessibleStateType::FOCUSED);
274 }
275 }
276 break;
277
278 case AccessibleStateType::EXPANDED:
279 case AccessibleStateType::COLLAPSE:
280 case AccessibleStateType::CHECKED:
281 {
282 pAgent->UpdateState(pAccessible);
283 pAgent->NotifyAccEvent(UM_EVENT_STATE_BUSY, pAccessible);
284 break;
285 }
286
287 default:
288 break;
289 }
290 }
291
292 /**
293 * fire the MSAA state changed event
294 * @param state the state id
295 * @param set true if state is set, false if state is unset
296 */
fireStatePropertyChange(short state,bool set)297 void AccContainerEventListener::fireStatePropertyChange(short state, bool set)
298 {
299 if( set )
300 {
301 // new value
302 switch(state)
303 {
304 case AccessibleStateType::SELECTED:
305 pAgent->IncreaseState( pAccessible, state);
306 break;
307 case AccessibleStateType::INDETERMINATE:
308 case AccessibleStateType::BUSY:
309 case AccessibleStateType::FOCUSABLE:
310 case AccessibleStateType::OFFSCREEN:
311 pAgent->IncreaseState( pAccessible, state);
312 pAgent->NotifyAccEvent(UM_EVENT_STATE_BUSY, pAccessible);
313 break;
314 case AccessibleStateType::SHOWING:
315 // UNO !SHOWING == MSAA OFFSCREEN
316 pAgent->IncreaseState( pAccessible, AccessibleStateType::SHOWING );
317 break;
318 case AccessibleStateType::VISIBLE:
319 // UNO !VISIBLE == MSAA INVISIBLE
320 pAgent->IncreaseState( pAccessible, AccessibleStateType::VISIBLE );
321 break;
322 default:
323 break;
324 }
325 }
326 else
327 {
328 // old value
329 switch(state)
330 {
331 case AccessibleStateType::SELECTED:
332 pAgent->DecreaseState( pAccessible, state );
333 break;
334 case AccessibleStateType::BUSY:
335 case AccessibleStateType::INDETERMINATE:
336 case AccessibleStateType::FOCUSABLE:
337 case AccessibleStateType::OFFSCREEN:
338 pAgent->DecreaseState( pAccessible, state);
339 pAgent->NotifyAccEvent(UM_EVENT_STATE_BUSY, pAccessible);
340 break;
341 case AccessibleStateType::SHOWING:
342 // UNO !SHOWING == MSAA OFFSCREEN
343 pAgent->DecreaseState( pAccessible, AccessibleStateType::SHOWING );
344 break;
345 case AccessibleStateType::VISIBLE:
346 // UNO !VISIBLE == MSAA INVISIBLE
347 pAgent->DecreaseState( pAccessible, AccessibleStateType::VISIBLE );
348 break;
349 default:
350 break;
351 }
352 }
353 }
354
355 /**
356 * handle the focused event
357 * @param enable true if get focus, false if lose focus
358 */
fireStateFocusdChange(bool enable)359 void AccContainerEventListener::fireStateFocusdChange(bool enable)
360 {
361 if(enable)
362 {
363 pAgent->IncreaseState( pAccessible, AccessibleStateType::FOCUSED);
364 //if the acc role is MENU_BAR, MSAA UM_EVENT_MENU_START event should be sent
365 //if the acc role is POPUP_MENU, MSAA UM_EVENT_MENUPOPUPSTART event should be sent
366 short role = getRole();
367 if(role == AccessibleRole::MENU_BAR)
368 {
369 pAgent->NotifyAccEvent(UM_EVENT_MENU_START, pAccessible);
370 }
371 else if (role == AccessibleRole::POPUP_MENU)
372 pAgent->NotifyAccEvent(UM_EVENT_MENUPOPUPSTART, pAccessible);
373 //Disable the focused event on option_pane and Panel.
374 //only disable option_pane for toolbar has panel to get focus
375 else if (role == AccessibleRole::PANEL || role == AccessibleRole::OPTION_PANE )
376 {
377 //don't send focused event on PANEL & OPTION_PANE if the parent is not toolbar
378 short parentRole = getParentRole();
379 if (parentRole == AccessibleRole::TOOL_BAR
380 || parentRole == AccessibleRole::SCROLL_PANE // sidebar
381 || parentRole == AccessibleRole::PANEL) // sidebar
382 pAgent->NotifyAccEvent(UM_EVENT_STATE_FOCUSED, pAccessible);
383 }
384 //to update ComboBox's description
385 else if (role == AccessibleRole::COMBO_BOX )
386 {
387 pAgent->UpdateDescription(pAccessible);
388 //for editable combobox, send focus event on only edit control,
389 bool bSendFocusOnCombobox = true;
390 //send focused event to the first text child
391 Reference<XAccessibleContext> mxContext(pAccessible->getAccessibleContext(),UNO_QUERY);
392 if(mxContext.is())
393 {
394 Reference<XAccessible> mxChild = mxContext->getAccessibleChild(0);
395 if(mxChild.is())
396 {
397 Reference<XAccessibleContext> mxChildContext(mxChild->getAccessibleContext(),UNO_QUERY);
398 short childrole = mxChildContext->getAccessibleRole();
399 if (childrole == AccessibleRole::TEXT)
400 {
401 if (IsEditable(mxChildContext))
402 {
403 pAgent->DecreaseState( pAccessible, AccessibleStateType::FOCUSED);
404 pAgent->IncreaseState( mxChild.get(), AccessibleStateType::FOCUSED);
405 pAgent->NotifyAccEvent(UM_EVENT_STATE_FOCUSED, mxChild.get());
406 bSendFocusOnCombobox = false;
407 }
408 }
409 }
410 }
411 if (bSendFocusOnCombobox)
412 pAgent->NotifyAccEvent(UM_EVENT_STATE_FOCUSED, pAccessible);
413 }
414 else
415 pAgent->NotifyAccEvent(UM_EVENT_STATE_FOCUSED, pAccessible);
416 }
417 else
418 {
419 pAgent->DecreaseState( pAccessible, AccessibleStateType::FOCUSED);
420 //if the acc role is MENU_BAR, MSAA UM_EVENT_MENU_END event should be sent
421 //if the acc role is POPUP_MENU, MSAA UM_EVENT_MENUPOPUPEND event should be sent
422 if(getRole() == AccessibleRole::MENU_BAR)
423 {
424 pAgent->NotifyAccEvent(UM_EVENT_MENU_END, pAccessible);
425 }
426 else if (getRole() == AccessibleRole::POPUP_MENU)
427 {
428 pAgent->NotifyAccEvent(UM_EVENT_MENUPOPUPEND, pAccessible);
429 }
430 }
431 }
432
433 /**
434 * handle the VALUE_CHANGED event
435 *
436 * @param oldValue the old value of the source of event
437 * @param newValue the new value of the source of event
438 */
handleValueChangedEvent(Any oldValue,Any newValue)439 void AccContainerEventListener::handleValueChangedEvent(Any oldValue, Any newValue)
440 {
441 pAgent->UpdateValue(pAccessible);
442 pAgent->NotifyAccEvent(UM_EVENT_OBJECT_VALUECHANGE, pAccessible);
443 }
444
IsEditable(Reference<XAccessibleContext> xContext)445 bool AccContainerEventListener::IsEditable(Reference<XAccessibleContext> xContext)
446 {
447 bool ret = false;
448 Reference< XAccessibleStateSet > pRState = xContext->getAccessibleStateSet();
449 if( !pRState.is() )
450 return false;
451
452 Sequence<short> pStates = pRState->getStates();
453 int count = pStates.getLength();
454 for( int iIndex = 0;iIndex < count;iIndex++ )
455 {
456 if(pStates[iIndex] == AccessibleStateType::EDITABLE)
457 return true;
458 }
459 return ret;
460 }
461
NotifyChildEvent(short nWinEvent,const Any & Value)462 bool AccContainerEventListener::NotifyChildEvent(short nWinEvent,const Any &Value)
463 {
464 Reference< XAccessible > xChild;
465 if(Value >>= xChild )
466 {
467 if(xChild.is())
468 {
469 XAccessible* pAcc = xChild.get();
470 pAgent->NotifyAccEvent(nWinEvent, pAcc);
471 return true;
472 }
473 }
474 return false;
475 }
476
handleSelectionChangedAddEvent(const Any &,const Any & newValue)477 void AccContainerEventListener::handleSelectionChangedAddEvent(const Any& /*oldValue*/, const Any& newValue)
478 {
479 if(NotifyChildEvent(UM_EVENT_SELECTION_CHANGED_ADD,newValue))
480 {
481 return ;
482 }
483 pAgent->NotifyAccEvent(UM_EVENT_SELECTION_CHANGED_ADD,pAccessible);
484 }
handleSelectionChangedRemoveEvent(const Any &,const Any & newValue)485 void AccContainerEventListener::handleSelectionChangedRemoveEvent(const Any& /*oldValue*/, const Any& newValue)
486 {
487 if(NotifyChildEvent(UM_EVENT_SELECTION_CHANGED_REMOVE,newValue))
488 {
489 return ;
490 }
491 pAgent->NotifyAccEvent(UM_EVENT_SELECTION_CHANGED_REMOVE,pAccessible);
492 }
handleSelectionChangedWithinEvent(const Any &,const Any & newValue)493 void AccContainerEventListener::handleSelectionChangedWithinEvent(const Any& /*oldValue*/, const Any& newValue)
494 {
495 if(NotifyChildEvent(UM_EVENT_SELECTION_CHANGED_WITHIN,newValue))
496 {
497 return ;
498 }
499 pAgent->NotifyAccEvent(UM_EVENT_SELECTION_CHANGED_WITHIN,pAccessible);
500 }
501
UpdateAllChildrenState(com::sun::star::accessibility::XAccessible * pXAccessible)502 void SAL_CALL AccContainerEventListener::UpdateAllChildrenState( com::sun::star::accessibility::XAccessible* pXAccessible )
503 {
504 Reference<com::sun::star::accessibility::XAccessibleContext> xContext(pXAccessible->getAccessibleContext(),UNO_QUERY);
505 if(!xContext.is())
506 {
507 return;
508 }
509 com::sun::star::accessibility::XAccessibleContext* pAccessibleContext = xContext.get();
510 if(pAccessibleContext == NULL)
511 {
512 return;
513 }
514
515 if (pAgent && pAgent->IsStateManageDescendant(pXAccessible))
516 {
517 return;
518 }
519
520 int count = pAccessibleContext->getAccessibleChildCount();
521 for (int i=0;i<count;i++)
522 {
523 Reference<com::sun::star::accessibility::XAccessible> mxAccessible
524 = pAccessibleContext->getAccessibleChild(i);
525
526 com::sun::star::accessibility::XAccessible* mpAccessible = mxAccessible.get();
527 if(mpAccessible != NULL)
528 {
529 pAgent->UpdateState(mpAccessible);
530 UpdateAllChildrenState(mpAccessible);
531 }
532 }
533 }
534
535
handlePageChangedEvent(const Any &,const Any &)536 void AccContainerEventListener::handlePageChangedEvent(const Any& /*oldValue*/, const Any& /*newValue*/)
537 {
538 pAgent->NotifyAccEvent(UM_EVENT_OBJECT_PAGECHANGED, pAccessible);
539 }
540
handleSectionChangedEvent(const Any &,const Any &)541 void AccContainerEventListener::handleSectionChangedEvent(const Any& /*oldValue*/, const Any& /*newValue*/ )
542 {
543 pAgent->NotifyAccEvent(UM_EVENT_SECTION_CHANGED, pAccessible);
544 }
545
handleColumnChangedEvent(const Any &,const Any &)546 void AccContainerEventListener::handleColumnChangedEvent(const Any& /*oldValue*/, const Any& /*newValue*/)
547 {
548 pAgent->NotifyAccEvent(UM_EVENT_COLUMN_CHANGED, pAccessible);
549 }
550
handleNameChangedEvent(Any name)551 void AccContainerEventListener::handleNameChangedEvent( Any name )
552 {
553 if (getRole() == AccessibleRole::COMBO_BOX)
554 {
555 Reference<XAccessibleContext> mxContext(pAccessible->getAccessibleContext(),UNO_QUERY);
556 if(mxContext.is())
557 {
558 Reference<XAccessible> mxChild = mxContext->getAccessibleChild(0);
559 if(mxChild.is())
560 {
561 Reference<XAccessibleContext> mxChildContext(mxChild->getAccessibleContext(),UNO_QUERY);
562 short childrole = mxChildContext->getAccessibleRole();
563 if (childrole == AccessibleRole::TEXT)
564 {
565 pAgent->UpdateAccName(mxChild.get(), name);
566 }
567 }
568 }
569 }
570 AccEventListener::handleNameChangedEvent(name);
571 }
572