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