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 <standard/vclxaccessiblebox.hxx>
21 #include <standard/vclxaccessibletextfield.hxx>
22 #include <standard/vclxaccessibleedit.hxx>
23 #include <standard/vclxaccessiblelist.hxx>
25 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
26 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
27 #include <com/sun/star/accessibility/AccessibleRole.hpp>
28 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
29 #include <vcl/svapp.hxx>
30 #include <vcl/toolkit/combobox.hxx>
31 #include <vcl/toolkit/lstbox.hxx>
32 #include <strings.hxx>
34 using namespace ::com::sun::star
;
35 using namespace ::com::sun::star::uno
;
36 using namespace ::com::sun::star::lang
;
37 using namespace ::com::sun::star::beans
;
38 using namespace ::com::sun::star::accessibility
;
40 VCLXAccessibleBox::VCLXAccessibleBox (VCLXWindow
* pVCLWindow
, BoxType aType
, bool bIsDropDownBox
)
41 : ImplInheritanceHelper (pVCLWindow
),
43 m_bIsDropDownBox (bIsDropDownBox
)
45 // Set up the flags that indicate which children this object has.
46 m_bHasListChild
= true;
48 // A text field is not present for non drop down list boxes.
49 if ((m_aBoxType
==LISTBOX
) && ! m_bIsDropDownBox
)
50 m_bHasTextChild
= false;
52 m_bHasTextChild
= true;
55 void VCLXAccessibleBox::ProcessWindowChildEvent( const VclWindowEvent
& rVclWindowEvent
)
57 uno::Any aOldValue
, aNewValue
;
59 switch ( rVclWindowEvent
.GetId() )
61 case VclEventId::WindowShow
:
62 case VclEventId::WindowHide
:
64 vcl::Window
* pChildWindow
= static_cast<vcl::Window
*>(rVclWindowEvent
.GetData());
65 // Just compare to the combo box text field. All other children
66 // are identical to this object in which case this object will
67 // be removed in a short time.
68 if (m_aBoxType
==COMBOBOX
)
70 VclPtr
< ComboBox
> pComboBox
= GetAs
< ComboBox
>();
71 if ( ( pComboBox
!= nullptr ) && ( pChildWindow
!= nullptr ) )
72 if (pChildWindow
== pComboBox
->GetSubEdit())
74 if (rVclWindowEvent
.GetId() == VclEventId::WindowShow
)
76 // Instantiate text field.
77 getAccessibleChild (0);
78 aNewValue
<<= m_xText
;
82 // Release text field.
83 aOldValue
<<= m_xText
;
86 // Tell the listeners about the new/removed child.
87 NotifyAccessibleEvent (
88 AccessibleEventId::CHILD
,
89 aOldValue
, aNewValue
);
97 VCLXAccessibleComponent::ProcessWindowChildEvent (rVclWindowEvent
);
101 void VCLXAccessibleBox::ProcessWindowEvent (const VclWindowEvent
& rVclWindowEvent
)
103 switch ( rVclWindowEvent
.GetId() )
105 case VclEventId::DropdownSelect
:
106 case VclEventId::ListboxSelect
:
108 // Forward the call to the list child.
109 VCLXAccessibleList
* pList
= static_cast<VCLXAccessibleList
*>(m_xList
.get());
110 if ( pList
== nullptr )
112 getAccessibleChild ( m_bHasTextChild
? 1 : 0 );
113 pList
= static_cast<VCLXAccessibleList
*>(m_xList
.get());
115 if ( pList
!= nullptr )
117 pList
->ProcessWindowEvent (rVclWindowEvent
, m_bIsDropDownBox
);
119 if (m_bIsDropDownBox
)
121 NotifyAccessibleEvent(AccessibleEventId::VALUE_CHANGED
, Any(), Any());
127 case VclEventId::DropdownOpen
:
129 VCLXAccessibleList
* pList
= static_cast<VCLXAccessibleList
*>(m_xList
.get());
130 if ( pList
== nullptr )
132 getAccessibleChild ( m_bHasTextChild
? 1 : 0 );
133 pList
= static_cast<VCLXAccessibleList
*>(m_xList
.get());
135 if ( pList
!= nullptr )
137 pList
->ProcessWindowEvent (rVclWindowEvent
);
138 pList
->HandleDropOpen();
142 case VclEventId::DropdownClose
:
144 VCLXAccessibleList
* pList
= static_cast<VCLXAccessibleList
*>(m_xList
.get());
145 if ( pList
== nullptr )
147 getAccessibleChild ( m_bHasTextChild
? 1 : 0 );
148 pList
= static_cast<VCLXAccessibleList
*>(m_xList
.get());
150 if ( pList
!= nullptr )
152 pList
->ProcessWindowEvent (rVclWindowEvent
);
154 VclPtr
<vcl::Window
> pWindow
= GetWindow();
155 if( pWindow
&& (pWindow
->HasFocus() || pWindow
->HasChildPathFocus()) )
157 Any aOldValue
, aNewValue
;
158 aNewValue
<<= AccessibleStateType::FOCUSED
;
159 NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED
, aOldValue
, aNewValue
);
163 case VclEventId::ComboboxSelect
:
165 VCLXAccessibleList
* pList
= static_cast<VCLXAccessibleList
*>(m_xList
.get());
166 if (pList
!= nullptr && m_xText
.is())
168 Reference
<XAccessibleText
> xText (m_xText
->getAccessibleContext(), UNO_QUERY
);
171 OUString sText
= xText
->getSelectedText();
172 if ( sText
.isEmpty() )
173 sText
= xText
->getText();
174 pList
->UpdateSelection_Acc(sText
, m_bIsDropDownBox
);
176 if (m_bIsDropDownBox
|| m_aBoxType
==COMBOBOX
)
177 NotifyAccessibleEvent(AccessibleEventId::VALUE_CHANGED
, Any(), Any());
183 //case VclEventId::DropdownOpen:
184 //case VclEventId::DropdownClose:
185 case VclEventId::ListboxDoubleClick
:
186 case VclEventId::ListboxScrolled
:
187 //case VclEventId::ListboxSelect:
188 case VclEventId::ListboxItemAdded
:
189 case VclEventId::ListboxItemRemoved
:
190 case VclEventId::ComboboxItemAdded
:
191 case VclEventId::ComboboxItemRemoved
:
193 // Forward the call to the list child.
194 VCLXAccessibleList
* pList
= static_cast<VCLXAccessibleList
*>(m_xList
.get());
195 if ( pList
== nullptr )
197 getAccessibleChild ( m_bHasTextChild
? 1 : 0 );
198 pList
= static_cast<VCLXAccessibleList
*>(m_xList
.get());
200 if ( pList
!= nullptr )
201 pList
->ProcessWindowEvent (rVclWindowEvent
);
205 //case VclEventId::ComboboxSelect:
206 case VclEventId::ComboboxDeselect
:
208 // Selection is handled by VCLXAccessibleList which operates on
209 // the same VCL object as this box does. In case of the
210 // combobox, however, we have to help by providing the list with
211 // the text of the currently selected item.
212 VCLXAccessibleList
* pList
= static_cast<VCLXAccessibleList
*>(m_xList
.get());
213 if (pList
!= nullptr && m_xText
.is())
215 Reference
<XAccessibleText
> xText (m_xText
->getAccessibleContext(), UNO_QUERY
);
218 OUString sText
= xText
->getSelectedText();
219 if ( sText
.isEmpty() )
220 sText
= xText
->getText();
221 pList
->UpdateSelection (sText
);
227 case VclEventId::EditModify
:
228 case VclEventId::EditSelectionChanged
:
229 case VclEventId::EditCaretChanged
:
230 // Modify/Selection events are handled by the combo box instead of
231 // directly by the edit field (Why?). Therefore, delegate this
232 // call to the edit field.
233 if (m_aBoxType
==COMBOBOX
)
237 Reference
<XAccessibleContext
> xContext
= m_xText
->getAccessibleContext();
238 VCLXAccessibleEdit
* pEdit
= static_cast<VCLXAccessibleEdit
*>(xContext
.get());
239 if (pEdit
!= nullptr)
240 pEdit
->ProcessWindowEvent (rVclWindowEvent
);
245 VCLXAccessibleComponent::ProcessWindowEvent( rVclWindowEvent
);
249 //===== XAccessible =========================================================
251 Reference
< XAccessibleContext
> SAL_CALL
VCLXAccessibleBox::getAccessibleContext( )
253 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
258 //===== XAccessibleContext ==================================================
260 sal_Int64
VCLXAccessibleBox::getAccessibleChildCount()
262 SolarMutexGuard aSolarGuard
;
263 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
265 return implGetAccessibleChildCount();
268 sal_Int64
VCLXAccessibleBox::implGetAccessibleChildCount()
270 // Usually a box has a text field and a list of items as its children.
271 // Non drop down list boxes have no text field. Additionally check
272 // whether the object is valid.
273 sal_Int64 nCount
= 0;
275 nCount
+= (m_bHasTextChild
?1:0) + (m_bHasListChild
?1:0);
278 // Object not valid anymore. Release references to children.
279 m_bHasTextChild
= false;
281 m_bHasListChild
= false;
288 Reference
<XAccessible
> SAL_CALL
VCLXAccessibleBox::getAccessibleChild (sal_Int64 i
)
290 SolarMutexGuard aSolarGuard
;
291 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
293 if (i
<0 || i
>=implGetAccessibleChildCount())
294 throw IndexOutOfBoundsException();
296 Reference
< XAccessible
> xChild
;
299 if (i
==1 || ! m_bHasTextChild
)
304 rtl::Reference
<VCLXAccessibleList
> pList
= new VCLXAccessibleList ( GetVCLXWindow(),
305 (m_aBoxType
== LISTBOX
? VCLXAccessibleList::LISTBOX
: VCLXAccessibleList::COMBOBOX
),
307 pList
->SetIndexInParent (i
);
317 if (m_aBoxType
==COMBOBOX
)
319 VclPtr
< ComboBox
> pComboBox
= GetAs
< ComboBox
>();
320 if (pComboBox
!=nullptr && pComboBox
->GetSubEdit()!=nullptr)
321 //Set the edit's acc name the same as parent
323 pComboBox
->GetSubEdit()->SetAccessibleName(getAccessibleName());
324 m_xText
= pComboBox
->GetSubEdit()->GetAccessible();
327 else if (m_bIsDropDownBox
)
328 m_xText
= new VCLXAccessibleTextField (GetVCLXWindow(),this);
337 sal_Int16 SAL_CALL
VCLXAccessibleBox::getAccessibleRole()
339 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
341 // Return the role <const>COMBO_BOX</const> for both VCL combo boxes and
342 // VCL list boxes in DropDown-Mode else <const>PANEL</const>.
343 // This way the Java bridge has not to handle both independently.
344 //return m_bIsDropDownBox ? AccessibleRole::COMBO_BOX : AccessibleRole::PANEL;
345 if (m_bIsDropDownBox
|| (m_aBoxType
== COMBOBOX
))
346 return AccessibleRole::COMBO_BOX
;
348 return AccessibleRole::PANEL
;
351 //===== XAccessibleAction ===================================================
353 sal_Int32 SAL_CALL
VCLXAccessibleBox::getAccessibleActionCount()
355 ::osl::Guard
< ::osl::Mutex
> aGuard (GetMutex());
357 // There is one action for drop down boxes (toggle popup) and none for
359 return m_bIsDropDownBox
? 1 : 0;
362 sal_Bool SAL_CALL
VCLXAccessibleBox::doAccessibleAction (sal_Int32 nIndex
)
364 bool bNotify
= false;
367 SolarMutexGuard aSolarGuard
;
368 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
370 if (nIndex
!=0 || !m_bIsDropDownBox
)
371 throw css::lang::IndexOutOfBoundsException(
372 ("VCLXAccessibleBox::doAccessibleAction: index "
373 + OUString::number(nIndex
) + " not among 0.."
374 + OUString::number(getAccessibleActionCount())),
377 if (m_aBoxType
== COMBOBOX
)
379 VclPtr
< ComboBox
> pComboBox
= GetAs
< ComboBox
>();
380 if (pComboBox
!= nullptr)
382 pComboBox
->ToggleDropDown();
386 else if (m_aBoxType
== LISTBOX
)
388 VclPtr
< ListBox
> pListBox
= GetAs
< ListBox
>();
389 if (pListBox
!= nullptr)
391 pListBox
->ToggleDropDown();
398 NotifyAccessibleEvent (AccessibleEventId::ACTION_CHANGED
, Any(), Any());
403 OUString SAL_CALL
VCLXAccessibleBox::getAccessibleActionDescription (sal_Int32 nIndex
)
405 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
406 if (nIndex
!=0 || !m_bIsDropDownBox
)
407 throw css::lang::IndexOutOfBoundsException();
409 return RID_STR_ACC_ACTION_TOGGLEPOPUP
;
412 Reference
< XAccessibleKeyBinding
> VCLXAccessibleBox::getAccessibleActionKeyBinding( sal_Int32 nIndex
)
414 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
416 Reference
< XAccessibleKeyBinding
> xRet
;
418 if (nIndex
<0 || nIndex
>=getAccessibleActionCount())
419 throw css::lang::IndexOutOfBoundsException();
425 // ===== XAccessibleValue ===============================================
426 Any
VCLXAccessibleBox::getCurrentValue( )
428 SolarMutexGuard aSolarGuard
;
429 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
432 if( m_xList
.is() && m_xText
.is())
434 // VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
435 Reference
<XAccessibleText
> xText (m_xText
->getAccessibleContext(), UNO_QUERY
);
438 OUString sText
= xText
->getText();
442 if (m_aBoxType
== LISTBOX
&& m_bIsDropDownBox
&& m_xList
.is() )
445 VCLXAccessibleList
* pList
= static_cast<VCLXAccessibleList
*>(m_xList
.get());
446 if(pList
->IsInDropDown())
448 if(pList
->getSelectedAccessibleChildCount()>0)
450 Reference
<XAccessibleContext
> xName (pList
->getSelectedAccessibleChild(sal_Int64(0)), UNO_QUERY
);
453 aAny
<<= xName
->getAccessibleName();
462 sal_Bool
VCLXAccessibleBox::setCurrentValue( const Any
& aNumber
)
464 SolarMutexGuard aSolarGuard
;
465 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
468 bool bValid
= (aNumber
>>= fValue
);
473 Any
VCLXAccessibleBox::getMaximumValue( )
479 Any
VCLXAccessibleBox::getMinimumValue( )
485 Any
VCLXAccessibleBox::getMinimumIncrement( )
490 // Set the INDETERMINATE state when there is no selected item for combobox
491 void VCLXAccessibleBox::FillAccessibleStateSet( sal_Int64
& rStateSet
)
493 VCLXAccessibleComponent::FillAccessibleStateSet(rStateSet
);
494 if (m_aBoxType
== COMBOBOX
)
497 sal_Int32 nEntryCount
= 0;
498 VclPtr
< ComboBox
> pComboBox
= GetAs
< ComboBox
>();
499 if (pComboBox
!= nullptr)
501 Edit
* pSubEdit
= pComboBox
->GetSubEdit();
503 sText
= pSubEdit
->GetText();
504 nEntryCount
= pComboBox
->GetEntryCount();
506 if ( sText
.isEmpty() && nEntryCount
> 0 )
507 rStateSet
|= AccessibleStateType::INDETERMINATE
;
509 else if (m_aBoxType
== LISTBOX
&& m_bIsDropDownBox
)
511 VclPtr
< ListBox
> pListBox
= GetAs
< ListBox
>();
512 if (pListBox
!= nullptr && pListBox
->GetEntryCount() > 0)
514 sal_Int32 nSelectedEntryCount
= pListBox
->GetSelectedEntryCount();
515 if ( nSelectedEntryCount
== 0)
516 rStateSet
|= AccessibleStateType::INDETERMINATE
;
521 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */