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 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_accessibility.hxx"
26 #include <accessibility/standard/vclxaccessiblebox.hxx>
27 #include <accessibility/standard/vclxaccessibletextfield.hxx>
28 #include <accessibility/standard/vclxaccessibleedit.hxx>
29 #include <accessibility/standard/vclxaccessiblelist.hxx>
30 #include <accessibility/helper/listboxhelper.hxx>
31 
32 #include <unotools/accessiblestatesethelper.hxx>
33 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
34 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
35 #include <com/sun/star/accessibility/AccessibleRole.hpp>
36 #include <vcl/svapp.hxx>
37 #include <vcl/combobox.hxx>
38 #include <vcl/lstbox.hxx>
39 #include <accessibility/helper/accresmgr.hxx>
40 #include <accessibility/helper/accessiblestrings.hrc>
41 
42 using namespace ::com::sun::star;
43 using namespace ::com::sun::star::uno;
44 using namespace ::com::sun::star::lang;
45 using namespace ::com::sun::star::beans;
46 using namespace ::com::sun::star::accessibility;
47 
48 VCLXAccessibleBox::VCLXAccessibleBox (VCLXWindow* pVCLWindow, BoxType aType, bool bIsDropDownBox)
49     : VCLXAccessibleComponent (pVCLWindow),
50       m_aBoxType (aType),
51       m_bIsDropDownBox (bIsDropDownBox),
52       m_nIndexInParent (DEFAULT_INDEX_IN_PARENT)
53 {
54     // Set up the flags that indicate which children this object has.
55     m_bHasListChild = true;
56 
57     // A text field is not present for non drop down list boxes.
58     if ((m_aBoxType==LISTBOX) && ! m_bIsDropDownBox)
59         m_bHasTextChild = false;
60     else
61         m_bHasTextChild = true;
62 }
63 
64 VCLXAccessibleBox::~VCLXAccessibleBox (void)
65 {
66 }
67 
68 void VCLXAccessibleBox::ProcessWindowChildEvent( const VclWindowEvent& rVclWindowEvent )
69 {
70    	uno::Any aOldValue, aNewValue;
71     uno::Reference<XAccessible> xAcc;
72 
73 	switch ( rVclWindowEvent.GetId() )
74 	{
75         case VCLEVENT_WINDOW_SHOW:
76         case VCLEVENT_WINDOW_HIDE:
77         {
78             Window* pChildWindow = (Window *) rVclWindowEvent.GetData();
79             // Just compare to the combo box text field.  All other children
80             // are identical to this object in which case this object will
81             // be removed in a short time.
82             if (m_aBoxType==COMBOBOX)
83             {
84                 ComboBox* pComboBox = static_cast<ComboBox*>(GetWindow());
85                 if ( ( pComboBox != NULL ) && ( pChildWindow != NULL ) )
86                     if (pChildWindow == pComboBox->GetSubEdit())
87                     {
88                         if (rVclWindowEvent.GetId() == VCLEVENT_WINDOW_SHOW)
89                         {
90                             // Instantiate text field.
91                             getAccessibleChild (0);
92                             aNewValue <<= m_xText;
93                         }
94                         else
95                         {
96                             // Release text field.
97                             aOldValue <<= m_xText;
98                             m_xText = NULL;
99                         }
100                         // Tell the listeners about the new/removed child.
101                         NotifyAccessibleEvent (
102                             AccessibleEventId::CHILD,
103                             aOldValue, aNewValue);
104                     }
105 
106             }
107         }
108         break;
109 
110         default:
111             VCLXAccessibleComponent::ProcessWindowChildEvent (rVclWindowEvent);
112 	}
113 }
114 
115 void VCLXAccessibleBox::ProcessWindowEvent (const VclWindowEvent& rVclWindowEvent)
116 {
117 	switch ( rVclWindowEvent.GetId() )
118 	{
119 		case VCLEVENT_LISTBOX_SELECT:
120 		case VCLEVENT_LISTBOX_FOCUSITEMCHANGED:
121 
122 		{
123 	            // Forward the call to the list child.
124 	            VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
125 	            if ( pList == NULL )
126 			{
127 				getAccessibleChild ( m_bHasTextChild ? 1 : 0 );
128 				pList = static_cast<VCLXAccessibleList*>(m_xList.get());
129 			}
130 			if ( pList != NULL )
131 			{
132 				pList->ProcessWindowEvent (rVclWindowEvent, m_bIsDropDownBox);
133 				if(m_bIsDropDownBox)
134 				{
135 					NotifyAccessibleEvent(AccessibleEventId::VALUE_CHANGED, Any(), Any());
136 					Any aOldValue;
137 					Any aNewValue;
138 					aOldValue <<= AccessibleStateType::INDETERMINATE;
139 					NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue);
140 
141 				}
142 			}
143 			break;
144 		}
145 		case VCLEVENT_DROPDOWN_OPEN:
146 		{
147 	             VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
148 	             if ( pList == NULL )
149 			{
150 				getAccessibleChild ( m_bHasTextChild ? 1 : 0 );
151 				pList = static_cast<VCLXAccessibleList*>(m_xList.get());
152 			}
153 			if ( pList != NULL )
154 			{
155 				pList->ProcessWindowEvent (rVclWindowEvent);
156 				pList->HandleDropOpen();
157 			}
158 			break;
159 		}
160 		case VCLEVENT_DROPDOWN_CLOSE:
161 		{
162 	             VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
163 	             if ( pList == NULL )
164 			{
165 				getAccessibleChild ( m_bHasTextChild ? 1 : 0 );
166 				pList = static_cast<VCLXAccessibleList*>(m_xList.get());
167 			}
168 			if ( pList != NULL )
169 			{
170 				pList->ProcessWindowEvent (rVclWindowEvent);
171 			}
172 			Window* pWindow = GetWindow();
173 			if( pWindow && (pWindow->HasFocus() || pWindow->HasChildPathFocus()) )
174 			{
175 				Any aOldValue, aNewValue;
176 				aNewValue <<= AccessibleStateType::FOCUSED;
177 				NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue );
178 			}
179 			break;
180 		}
181 		case VCLEVENT_COMBOBOX_SELECT:
182 		{
183 	             VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
184 	             if (pList != NULL)
185 	             {
186 		                Reference<XAccessibleText> xText (m_xText->getAccessibleContext(), UNO_QUERY);
187 		                if ( xText.is() )
188 				{
189 					::rtl::OUString sText = xText->getSelectedText();
190 					if ( !sText.getLength() )
191 						sText = xText->getText();
192 		                    pList->UpdateSelection_Acc (sText, m_bIsDropDownBox);
193 					//if(m_bIsDropDownBox && !pList->IsInDropDown())
194 					if (m_bIsDropDownBox || ( !m_bIsDropDownBox && m_aBoxType==COMBOBOX))
195 						NotifyAccessibleEvent(AccessibleEventId::VALUE_CHANGED, Any(), Any());
196 
197 						Any aOldValue;
198 						Any aNewValue;
199 						aOldValue <<= AccessibleStateType::INDETERMINATE;
200 						NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue);
201 
202 				}
203 	            	}
204 			break;
205 		}
206 		//case VCLEVENT_DROPDOWN_OPEN:
207 		//case VCLEVENT_DROPDOWN_CLOSE:
208 		case VCLEVENT_LISTBOX_DOUBLECLICK:
209 		case VCLEVENT_LISTBOX_SCROLLED:
210 		//case VCLEVENT_LISTBOX_SELECT:
211         case VCLEVENT_LISTBOX_ITEMADDED:
212         case VCLEVENT_LISTBOX_ITEMREMOVED:
213         case VCLEVENT_COMBOBOX_ITEMADDED:
214         case VCLEVENT_COMBOBOX_ITEMREMOVED:
215 		case VCLEVENT_COMBOBOX_SCROLLED:
216         {
217             // Forward the call to the list child.
218             VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
219             if ( pList == NULL )
220 			{
221 				getAccessibleChild ( m_bHasTextChild ? 1 : 0 );
222 				pList = static_cast<VCLXAccessibleList*>(m_xList.get());
223 			}
224 			if ( pList != NULL )
225 				pList->ProcessWindowEvent (rVclWindowEvent);
226             break;
227         }
228 
229 		//case VCLEVENT_COMBOBOX_SELECT:
230         case VCLEVENT_COMBOBOX_DESELECT:
231         {
232             // Selection is handled by VCLXAccessibleList which operates on
233             // the same VCL object as this box does.  In case of the
234             // combobox, however, we have to help the list with providing
235             // the text of the currently selected item.
236             VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
237             if (pList != NULL && m_xText.is())
238             {
239                 Reference<XAccessibleText> xText (m_xText->getAccessibleContext(), UNO_QUERY);
240                 if ( xText.is() )
241 				{
242 					::rtl::OUString sText = xText->getSelectedText();
243 					if ( !sText.getLength() )
244 						sText = xText->getText();
245                     pList->UpdateSelection (sText);
246 				}
247             }
248 			break;
249         }
250 
251 		case VCLEVENT_EDIT_MODIFY:
252         case VCLEVENT_EDIT_SELECTIONCHANGED:
253 	 // case VCLEVENT_EDIT_CARETCHANGED:
254             // Modify/Selection events are handled by the combo box instead of
255             // directly by the edit field (Why?).  Therefore, delegate this
256             // call to the edit field.
257             if (m_aBoxType==COMBOBOX)
258             {
259                 if (m_xText.is())
260                 {
261                     Reference<XAccessibleContext> xContext = m_xText->getAccessibleContext();
262                     VCLXAccessibleEdit* pEdit = static_cast<VCLXAccessibleEdit*>(xContext.get());
263                     if (pEdit != NULL)
264                         pEdit->ProcessWindowEvent (rVclWindowEvent);
265                 }
266             }
267             break;
268 		/*
269 		// MT: Not sending VCLEVENT_LISTBOX_STATEUPDATE, see comment in ListBox::SelectEntryPos
270 		case VCLEVENT_LISTBOX_STATEUPDATE:
271 		{
272 			// Need to update the INDETERMINATE state sometimes, Sym2_1986
273 			if (m_bIsDropDownBox && m_aBoxType==LISTBOX)
274 			{
275 				sal_Int32 nSelectedEntryCount = 0;
276 				ListBox* pListBox = static_cast< ListBox* >( GetWindow() );
277 				if (pListBox != NULL && pListBox->GetEntryCount() > 0)
278 				{
279 	        		nSelectedEntryCount = pListBox->GetSelectEntryCount();
280 					Any aOldValue;
281 					Any aNewValue;
282 					if ( nSelectedEntryCount == 0)
283 						aNewValue <<= AccessibleStateType::INDETERMINATE;
284 					else
285 						aOldValue <<= AccessibleStateType::INDETERMINATE;
286 					NotifyAccessibleEvent(AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue);
287 				}
288 			}
289 			break;
290 		}
291 		*/
292 		default:
293 			VCLXAccessibleComponent::ProcessWindowEvent( rVclWindowEvent );
294 	}
295 }
296 
297 IMPLEMENT_FORWARD_XINTERFACE2(VCLXAccessibleBox, VCLXAccessibleComponent, VCLXAccessibleBox_BASE)
298 IMPLEMENT_FORWARD_XTYPEPROVIDER2(VCLXAccessibleBox, VCLXAccessibleComponent, VCLXAccessibleBox_BASE)
299 
300 //=====  XAccessible  =========================================================
301 
302 Reference< XAccessibleContext > SAL_CALL VCLXAccessibleBox::getAccessibleContext(  )
303 	throw (RuntimeException)
304 {
305 	::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
306 
307 	return this;
308 }
309 
310 //=====  XAccessibleContext  ==================================================
311 
312 sal_Int32 SAL_CALL VCLXAccessibleBox::getAccessibleChildCount (void)
313     throw (RuntimeException)
314 {
315 	vos::OGuard aSolarGuard( Application::GetSolarMutex() );
316 	::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
317 
318     // Usually a box has a text field and a list of items as its children.
319     // Non drop down list boxes have no text field.  Additionally check
320     // whether the object is valid.
321     sal_Int32 nCount = 0;
322     if (IsValid())
323         nCount += (m_bHasTextChild?1:0) + (m_bHasListChild?1:0);
324     else
325     {
326         // Object not valid anymore.  Release references to children.
327         m_bHasTextChild = false;
328         m_xText = NULL;
329         m_bHasListChild = false;
330         m_xList = NULL;
331     }
332 
333     return nCount;
334 }
335 
336 Reference<XAccessible> SAL_CALL VCLXAccessibleBox::getAccessibleChild (sal_Int32 i)
337     throw (IndexOutOfBoundsException, RuntimeException)
338 {
339 	vos::OGuard aSolarGuard( Application::GetSolarMutex() );
340 	::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
341 
342 	if (i<0 || i>=getAccessibleChildCount())
343 		throw IndexOutOfBoundsException();
344 
345 	Reference< XAccessible > xChild;
346     if (IsValid())
347     {
348         if (i==1 || ! m_bHasTextChild)
349         {
350             // List.
351             if ( ! m_xList.is())
352             {
353                 VCLXAccessibleList* pList = new VCLXAccessibleList ( GetVCLXWindow(),
354 					(m_aBoxType == LISTBOX ? VCLXAccessibleList::LISTBOX : VCLXAccessibleList::COMBOBOX),
355 																	this);
356                 pList->SetIndexInParent (i);
357                 m_xList = pList;
358             }
359             xChild = m_xList;
360         }
361         else
362         {
363             // Text Field.
364             if ( ! m_xText.is())
365             {
366                 if (m_aBoxType==COMBOBOX)
367                 {
368                     ComboBox* pComboBox = static_cast<ComboBox*>(GetWindow());
369                     if (pComboBox!=NULL && pComboBox->GetSubEdit()!=NULL)
370 					//Set the edit's acc name the same as parent
371 					{
372 						pComboBox->GetSubEdit()->SetAccessibleName(getAccessibleName());
373                         m_xText = pComboBox->GetSubEdit()->GetAccessible();
374 					}
375                 }
376                 else if (m_bIsDropDownBox)
377                     m_xText = new VCLXAccessibleTextField (GetVCLXWindow(),this);
378             }
379             xChild = m_xText;
380         }
381     }
382 
383     return xChild;
384 }
385 
386 sal_Int16 SAL_CALL VCLXAccessibleBox::getAccessibleRole (void) throw (RuntimeException)
387 {
388 	::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
389 
390     // Return the role <const>COMBO_BOX</const> for both VCL combo boxes and
391     // VCL list boxes in DropDown-Mode else <const>PANEL</const>.
392 	// This way the Java bridge has not to handle both independently.
393     //return m_bIsDropDownBox ? AccessibleRole::COMBO_BOX : AccessibleRole::PANEL;
394 	if (m_bIsDropDownBox || (!m_bIsDropDownBox && m_aBoxType == COMBOBOX ))
395 		return AccessibleRole::COMBO_BOX;
396 	else
397 		return AccessibleRole::PANEL;
398 }
399 
400 sal_Int32 SAL_CALL VCLXAccessibleBox::getAccessibleIndexInParent (void)
401     throw (::com::sun::star::uno::RuntimeException)
402 {
403     if (m_nIndexInParent != DEFAULT_INDEX_IN_PARENT)
404         return m_nIndexInParent;
405     else
406         return VCLXAccessibleComponent::getAccessibleIndexInParent();
407 }
408 
409 //=====  XAccessibleAction  ===================================================
410 
411 sal_Int32 SAL_CALL VCLXAccessibleBox::getAccessibleActionCount (void)
412     throw (RuntimeException)
413 {
414 	::osl::Guard< ::osl::Mutex> aGuard (GetMutex());
415 
416     // There is one action for drop down boxes (toggle popup) and none for
417     // the other boxes.
418 	return m_bIsDropDownBox ? 0 : 1;
419 }
420 
421 sal_Bool SAL_CALL VCLXAccessibleBox::doAccessibleAction (sal_Int32 nIndex)
422     throw (IndexOutOfBoundsException, RuntimeException)
423 {
424 	sal_Bool bNotify = sal_False;
425 
426 	{
427 		vos::OGuard aSolarGuard( Application::GetSolarMutex() );
428 		::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
429 
430         if (nIndex<0 || nIndex>=getAccessibleActionCount())
431             throw ::com::sun::star::lang::IndexOutOfBoundsException();
432 
433 		if (m_aBoxType == COMBOBOX)
434         {
435             ComboBox* pComboBox = static_cast< ComboBox* >( GetWindow() );
436             if (pComboBox != NULL)
437             {
438                 pComboBox->ToggleDropDown();
439                 bNotify = sal_True;
440             }
441         }
442         else if (m_aBoxType == LISTBOX)
443         {
444             ListBox* pListBox = static_cast< ListBox* >( GetWindow() );
445             if (pListBox != NULL)
446             {
447                 pListBox->ToggleDropDown();
448                 bNotify = sal_True;
449             }
450         }
451     }
452 
453 	if (bNotify)
454 		NotifyAccessibleEvent (AccessibleEventId::ACTION_CHANGED, Any(), Any());
455 
456 	return bNotify;
457 }
458 
459 ::rtl::OUString SAL_CALL VCLXAccessibleBox::getAccessibleActionDescription (sal_Int32 nIndex)
460     throw (IndexOutOfBoundsException, RuntimeException)
461 {
462 	::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
463     if (nIndex<0 || nIndex>=getAccessibleActionCount())
464         throw ::com::sun::star::lang::IndexOutOfBoundsException();
465 	//Solution:When combo_box,it should not has action information.
466     //return TK_RES_STRING( RID_STR_ACC_ACTION_TOGGLEPOPUP);
467 	if (m_aBoxType == LISTBOX)
468 		return ::rtl::OUString();
469 	return m_bIsDropDownBox?::rtl::OUString():TK_RES_STRING( RID_STR_ACC_ACTION_TOGGLEPOPUP);
470 }
471 
472 Reference< XAccessibleKeyBinding > VCLXAccessibleBox::getAccessibleActionKeyBinding( sal_Int32 nIndex )
473 	throw (IndexOutOfBoundsException, RuntimeException)
474 {
475 	::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
476 
477 	Reference< XAccessibleKeyBinding > xRet;
478 
479     if (nIndex<0 || nIndex>=getAccessibleActionCount())
480         throw ::com::sun::star::lang::IndexOutOfBoundsException();
481 
482 	// ... which key?
483 	return xRet;
484 }
485 
486 //=====  XComponent  ==========================================================
487 
488 void SAL_CALL VCLXAccessibleBox::disposing (void)
489 {
490 	VCLXAccessibleComponent::disposing();
491 }
492 
493 // =====  XAccessibleValue  ===============================================
494 Any VCLXAccessibleBox::getCurrentValue( )
495     throw( RuntimeException )
496 {
497 	vos::OGuard aSolarGuard( Application::GetSolarMutex() );
498 	::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
499 
500 	Any aAny;
501 	if( m_xList.is() && m_xText.is())
502 	{
503 		// VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
504 		Reference<XAccessibleText> xText (m_xText->getAccessibleContext(), UNO_QUERY);
505 		if ( xText.is() )
506 		{
507 			::rtl::OUString sText = xText->getText();
508 			aAny <<= sText;
509 		}
510 	}
511 	if (m_aBoxType == LISTBOX && m_bIsDropDownBox  && m_xList.is() )
512 	{
513 
514 		VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
515 		if(pList->IsInDropDown())
516 		{
517 			if(pList->getSelectedAccessibleChildCount()>0)
518 			{
519 				Reference<XAccessibleContext> xName (pList->getSelectedAccessibleChild((sal_Int32)(0)), UNO_QUERY);
520 				if(xName.is())
521 				{
522 					aAny <<= xName->getAccessibleName();
523 				}
524 			}
525 		}
526 	}
527 
528     return aAny;
529 }
530 
531 sal_Bool VCLXAccessibleBox::setCurrentValue( const Any& aNumber )
532     throw( RuntimeException )
533 {
534 	vos::OGuard aSolarGuard( Application::GetSolarMutex() );
535 	::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
536 
537     ::rtl::OUString  fValue;
538     sal_Bool bValid = (aNumber >>= fValue);
539     if( bValid )
540     {
541 
542     }
543     return bValid;
544 
545 }
546 
547 Any VCLXAccessibleBox::getMaximumValue( )
548     throw( RuntimeException )
549 {
550     Any aAny;
551     return aAny;
552 }
553 
554 Any VCLXAccessibleBox::getMinimumValue(  )
555     throw( RuntimeException )
556 {
557     Any aAny;
558     return aAny;
559 }
560 
561 // Set the INDETERMINATE state when there is no selected item for combobox
562 void VCLXAccessibleBox::FillAccessibleStateSet( utl::AccessibleStateSetHelper& rStateSet )
563 {
564 	VCLXAccessibleComponent::FillAccessibleStateSet(rStateSet);
565 	if (m_aBoxType == COMBOBOX )
566 	{
567 		::rtl::OUString sText;
568 		sal_Int32 nEntryCount = 0;
569 		ComboBox* pComboBox = static_cast<ComboBox*>(GetWindow());
570 		if (pComboBox != NULL)
571 		{
572 			Edit* pSubEdit = pComboBox->GetSubEdit();
573 			if ( pSubEdit)
574 				sText = pSubEdit->GetText();
575 			nEntryCount = pComboBox->GetEntryCount();
576 		}
577 		if (sText.getLength() == 0 && nEntryCount >0)
578 			rStateSet.AddState(AccessibleStateType::INDETERMINATE);
579 	}
580 	else if (m_aBoxType == LISTBOX && m_bIsDropDownBox == true)
581 	{
582 		sal_Int32 nSelectedEntryCount = 0;
583 		ListBox* pListBox = static_cast< ListBox* >( GetWindow() );
584 		if (pListBox != NULL && pListBox->GetEntryCount() > 0)
585 		{
586 	        	nSelectedEntryCount = pListBox->GetSelectEntryCount();
587 			if ( nSelectedEntryCount == 0)
588 				rStateSet.AddState(AccessibleStateType::INDETERMINATE);
589 		}
590 	}
591 }
592