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>
30 #include <vcl/accessibility/strings.hxx>
31 #include <vcl/svapp.hxx>
32 #include <vcl/toolkit/combobox.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(vcl::Window
* pBox
, BoxType aType
, bool bIsDropDownBox
)
41 : ImplInheritanceHelper(pBox
),
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 VCLXAccessibleBox::~VCLXAccessibleBox() {}
57 bool VCLXAccessibleBox::IsValid() const
62 void VCLXAccessibleBox::ProcessWindowChildEvent( const VclWindowEvent
& rVclWindowEvent
)
64 uno::Any aOldValue
, aNewValue
;
66 switch ( rVclWindowEvent
.GetId() )
68 case VclEventId::WindowShow
:
69 case VclEventId::WindowHide
:
71 vcl::Window
* pChildWindow
= static_cast<vcl::Window
*>(rVclWindowEvent
.GetData());
72 // Just compare to the combo box text field. All other children
73 // are identical to this object in which case this object will
74 // be removed in a short time.
75 if (m_aBoxType
==COMBOBOX
)
77 VclPtr
< ComboBox
> pComboBox
= GetAs
< ComboBox
>();
78 if (pComboBox
&& pChildWindow
&& pChildWindow
== pComboBox
->GetSubEdit()
81 if (rVclWindowEvent
.GetId() == VclEventId::WindowShow
)
83 // Instantiate text field.
84 getAccessibleChild (0);
85 aNewValue
<<= m_xText
;
89 // Release text field.
90 aOldValue
<<= m_xText
;
93 // Tell the listeners about the new/removed child.
94 NotifyAccessibleEvent (
95 AccessibleEventId::CHILD
,
96 aOldValue
, aNewValue
);
104 VCLXAccessibleComponent::ProcessWindowChildEvent (rVclWindowEvent
);
108 void VCLXAccessibleBox::ProcessWindowEvent (const VclWindowEvent
& rVclWindowEvent
)
110 switch ( rVclWindowEvent
.GetId() )
112 case VclEventId::DropdownSelect
:
113 case VclEventId::ListboxSelect
:
115 // Forward the call to the list child.
118 getAccessibleChild ( m_bHasTextChild
? 1 : 0 );
122 m_xList
->ProcessWindowEvent(rVclWindowEvent
, m_bIsDropDownBox
);
124 if (m_bIsDropDownBox
)
126 NotifyAccessibleEvent(AccessibleEventId::VALUE_CHANGED
, Any(), Any());
132 case VclEventId::DropdownOpen
:
136 getAccessibleChild ( m_bHasTextChild
? 1 : 0 );
140 m_xList
->ProcessWindowEvent(rVclWindowEvent
);
141 m_xList
->HandleDropOpen();
145 case VclEventId::DropdownClose
:
149 getAccessibleChild ( m_bHasTextChild
? 1 : 0 );
153 m_xList
->ProcessWindowEvent(rVclWindowEvent
);
155 VclPtr
<vcl::Window
> pWindow
= GetWindow();
156 if( pWindow
&& (pWindow
->HasFocus() || pWindow
->HasChildPathFocus()) )
158 Any aOldValue
, aNewValue
;
159 aNewValue
<<= AccessibleStateType::FOCUSED
;
160 NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED
, aOldValue
, aNewValue
);
164 case VclEventId::ComboboxSelect
:
166 if (m_xList
.is() && 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 m_xList
->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.
196 getAccessibleChild ( m_bHasTextChild
? 1 : 0 );
199 m_xList
->ProcessWindowEvent(rVclWindowEvent
);
203 //case VclEventId::ComboboxSelect:
204 case VclEventId::ComboboxDeselect
:
206 // Selection is handled by VCLXAccessibleList which operates on
207 // the same VCL object as this box does. In case of the
208 // combobox, however, we have to help by providing the list with
209 // the text of the currently selected item.
210 if (m_xList
.is() && m_xText
.is())
212 Reference
<XAccessibleText
> xText (m_xText
->getAccessibleContext(), UNO_QUERY
);
215 OUString sText
= xText
->getSelectedText();
216 if ( sText
.isEmpty() )
217 sText
= xText
->getText();
218 m_xList
->UpdateSelection(sText
);
224 case VclEventId::EditModify
:
225 case VclEventId::EditSelectionChanged
:
226 case VclEventId::EditCaretChanged
:
227 // Modify/Selection events are handled by the combo box instead of
228 // directly by the edit field (Why?). Therefore, delegate this
229 // call to the edit field.
230 if (m_aBoxType
==COMBOBOX
)
234 Reference
<XAccessibleContext
> xContext
= m_xText
->getAccessibleContext();
235 VCLXAccessibleEdit
* pEdit
= static_cast<VCLXAccessibleEdit
*>(xContext
.get());
236 if (pEdit
!= nullptr)
237 pEdit
->ProcessWindowEvent (rVclWindowEvent
);
242 VCLXAccessibleComponent::ProcessWindowEvent( rVclWindowEvent
);
246 //===== XAccessible =========================================================
248 Reference
< XAccessibleContext
> SAL_CALL
VCLXAccessibleBox::getAccessibleContext( )
250 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
255 //===== XAccessibleContext ==================================================
257 sal_Int64
VCLXAccessibleBox::getAccessibleChildCount()
259 SolarMutexGuard aSolarGuard
;
260 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
262 return implGetAccessibleChildCount();
265 sal_Int64
VCLXAccessibleBox::implGetAccessibleChildCount()
267 // Usually a box has a text field and a list of items as its children.
268 // Non drop down list boxes have no text field. Additionally check
269 // whether the object is valid.
270 sal_Int64 nCount
= 0;
272 nCount
+= (m_bHasTextChild
?1:0) + (m_bHasListChild
?1:0);
275 // Object not valid anymore. Release references to children.
276 m_bHasTextChild
= false;
278 m_bHasListChild
= false;
285 Reference
<XAccessible
> SAL_CALL
VCLXAccessibleBox::getAccessibleChild (sal_Int64 i
)
287 SolarMutexGuard aSolarGuard
;
288 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
290 if (i
<0 || i
>=implGetAccessibleChildCount())
291 throw IndexOutOfBoundsException();
293 Reference
< XAccessible
> xChild
;
296 if (i
==1 || ! m_bHasTextChild
)
301 m_xList
= new VCLXAccessibleList(GetWindow(),
302 (m_aBoxType
== LISTBOX
? VCLXAccessibleList::LISTBOX
: VCLXAccessibleList::COMBOBOX
),
304 m_xList
->SetIndexInParent(i
);
313 if (m_aBoxType
==COMBOBOX
)
315 VclPtr
< ComboBox
> pComboBox
= GetAs
< ComboBox
>();
316 if (pComboBox
&& pComboBox
->GetSubEdit())
317 m_xText
= pComboBox
->GetSubEdit()->GetAccessible();
319 else if (m_bIsDropDownBox
)
320 m_xText
= new VCLXAccessibleTextField(GetWindow(), this);
329 sal_Int16 SAL_CALL
VCLXAccessibleBox::getAccessibleRole()
331 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
333 // Return the role <const>COMBO_BOX</const> for both VCL combo boxes and
334 // VCL list boxes in DropDown-Mode else <const>PANEL</const>.
335 // This way the Java bridge has not to handle both independently.
336 //return m_bIsDropDownBox ? AccessibleRole::COMBO_BOX : AccessibleRole::PANEL;
337 if (m_bIsDropDownBox
|| (m_aBoxType
== COMBOBOX
))
338 return AccessibleRole::COMBO_BOX
;
340 return AccessibleRole::PANEL
;
343 //===== XAccessibleAction ===================================================
345 sal_Int32 SAL_CALL
VCLXAccessibleBox::getAccessibleActionCount()
347 ::osl::Guard
< ::osl::Mutex
> aGuard (GetMutex());
349 // There is one action for drop down boxes (toggle popup) and none for
351 return m_bIsDropDownBox
? 1 : 0;
354 sal_Bool SAL_CALL
VCLXAccessibleBox::doAccessibleAction (sal_Int32 nIndex
)
356 bool bNotify
= false;
359 SolarMutexGuard aSolarGuard
;
360 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
362 if (nIndex
!=0 || !m_bIsDropDownBox
)
363 throw css::lang::IndexOutOfBoundsException(
364 ("VCLXAccessibleBox::doAccessibleAction: index "
365 + OUString::number(nIndex
) + " not among 0.."
366 + OUString::number(getAccessibleActionCount())),
369 if (m_aBoxType
== COMBOBOX
)
371 VclPtr
< ComboBox
> pComboBox
= GetAs
< ComboBox
>();
372 if (pComboBox
!= nullptr)
374 pComboBox
->ToggleDropDown();
378 else if (m_aBoxType
== LISTBOX
)
380 VclPtr
< ListBox
> pListBox
= GetAs
< ListBox
>();
381 if (pListBox
!= nullptr)
383 pListBox
->ToggleDropDown();
390 NotifyAccessibleEvent (AccessibleEventId::ACTION_CHANGED
, Any(), Any());
395 OUString SAL_CALL
VCLXAccessibleBox::getAccessibleActionDescription (sal_Int32 nIndex
)
397 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
398 if (nIndex
!=0 || !m_bIsDropDownBox
)
399 throw css::lang::IndexOutOfBoundsException();
401 return RID_STR_ACC_ACTION_TOGGLEPOPUP
;
404 Reference
< XAccessibleKeyBinding
> VCLXAccessibleBox::getAccessibleActionKeyBinding( sal_Int32 nIndex
)
406 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
408 Reference
< XAccessibleKeyBinding
> xRet
;
410 if (nIndex
<0 || nIndex
>=getAccessibleActionCount())
411 throw css::lang::IndexOutOfBoundsException();
417 // ===== XAccessibleValue ===============================================
418 Any
VCLXAccessibleBox::getCurrentValue( )
420 SolarMutexGuard aSolarGuard
;
421 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
424 if( m_xList
.is() && m_xText
.is())
426 // VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
427 Reference
<XAccessibleText
> xText (m_xText
->getAccessibleContext(), UNO_QUERY
);
430 OUString sText
= xText
->getText();
434 if (m_aBoxType
== LISTBOX
&& m_bIsDropDownBox
&& m_xList
.is() )
436 if (m_xList
->IsInDropDown())
438 if (m_xList
->getSelectedAccessibleChildCount() > 0)
440 Reference
<XAccessibleContext
> xName (m_xList
->getSelectedAccessibleChild(sal_Int64(0)), UNO_QUERY
);
443 aAny
<<= xName
->getAccessibleName();
452 sal_Bool
VCLXAccessibleBox::setCurrentValue( const Any
& aNumber
)
454 SolarMutexGuard aSolarGuard
;
455 ::osl::Guard
< ::osl::Mutex
> aGuard( GetMutex() );
458 bool bValid
= (aNumber
>>= fValue
);
463 Any
VCLXAccessibleBox::getMaximumValue( )
469 Any
VCLXAccessibleBox::getMinimumValue( )
475 Any
VCLXAccessibleBox::getMinimumIncrement( )
480 // Set the INDETERMINATE state when there is no selected item for combobox
481 void VCLXAccessibleBox::FillAccessibleStateSet( sal_Int64
& rStateSet
)
483 VCLXAccessibleComponent::FillAccessibleStateSet(rStateSet
);
484 if (m_aBoxType
== COMBOBOX
)
487 sal_Int32 nEntryCount
= 0;
488 VclPtr
< ComboBox
> pComboBox
= GetAs
< ComboBox
>();
489 if (pComboBox
!= nullptr)
491 Edit
* pSubEdit
= pComboBox
->GetSubEdit();
493 sText
= pSubEdit
->GetText();
494 nEntryCount
= pComboBox
->GetEntryCount();
496 if ( sText
.isEmpty() && nEntryCount
> 0 )
497 rStateSet
|= AccessibleStateType::INDETERMINATE
;
499 else if (m_aBoxType
== LISTBOX
&& m_bIsDropDownBox
)
501 VclPtr
< ListBox
> pListBox
= GetAs
< ListBox
>();
502 if (pListBox
!= nullptr && pListBox
->GetEntryCount() > 0)
504 sal_Int32 nSelectedEntryCount
= pListBox
->GetSelectedEntryCount();
505 if ( nSelectedEntryCount
== 0)
506 rStateSet
|= AccessibleStateType::INDETERMINATE
;
511 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */