xref: /trunk/main/accessibility/source/standard/vclxaccessiblebox.cxx (revision a9ab3c7b3d31474a75bf54404ada03e2f02464cb)
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
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