LibreOffice Module accessibility (master) 1
vclxaccessiblebox.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
24
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>
32#include <strings.hxx>
33
34using namespace ::com::sun::star;
35using namespace ::com::sun::star::uno;
36using namespace ::com::sun::star::lang;
37using namespace ::com::sun::star::beans;
38using namespace ::com::sun::star::accessibility;
39
40VCLXAccessibleBox::VCLXAccessibleBox (VCLXWindow* pVCLWindow, BoxType aType, bool bIsDropDownBox)
41 : ImplInheritanceHelper (pVCLWindow),
42 m_aBoxType (aType),
43 m_bIsDropDownBox (bIsDropDownBox)
44{
45 // Set up the flags that indicate which children this object has.
46 m_bHasListChild = true;
47
48 // A text field is not present for non drop down list boxes.
50 m_bHasTextChild = false;
51 else
52 m_bHasTextChild = true;
53}
54
56{
57 uno::Any aOldValue, aNewValue;
58
59 switch ( rVclWindowEvent.GetId() )
60 {
61 case VclEventId::WindowShow:
62 case VclEventId::WindowHide:
63 {
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.
69 {
70 VclPtr< ComboBox > pComboBox = GetAs< ComboBox >();
71 if ( ( pComboBox != nullptr ) && ( pChildWindow != nullptr ) )
72 if (pChildWindow == pComboBox->GetSubEdit())
73 {
74 if (rVclWindowEvent.GetId() == VclEventId::WindowShow)
75 {
76 // Instantiate text field.
78 aNewValue <<= m_xText;
79 }
80 else
81 {
82 // Release text field.
83 aOldValue <<= m_xText;
84 m_xText = nullptr;
85 }
86 // Tell the listeners about the new/removed child.
87 NotifyAccessibleEvent (
88 AccessibleEventId::CHILD,
89 aOldValue, aNewValue);
90 }
91
92 }
93 }
94 break;
95
96 default:
97 VCLXAccessibleComponent::ProcessWindowChildEvent (rVclWindowEvent);
98 }
99}
100
102{
103 switch ( rVclWindowEvent.GetId() )
104 {
105 case VclEventId::DropdownSelect:
106 case VclEventId::ListboxSelect:
107 {
108 // Forward the call to the list child.
109 VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
110 if ( pList == nullptr )
111 {
113 pList = static_cast<VCLXAccessibleList*>(m_xList.get());
114 }
115 if ( pList != nullptr )
116 {
117 pList->ProcessWindowEvent (rVclWindowEvent, m_bIsDropDownBox);
118#if defined(_WIN32)
120 {
121 NotifyAccessibleEvent(AccessibleEventId::VALUE_CHANGED, Any(), Any());
122 }
123#endif
124 }
125 break;
126 }
127 case VclEventId::DropdownOpen:
128 {
129 VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
130 if ( pList == nullptr )
131 {
133 pList = static_cast<VCLXAccessibleList*>(m_xList.get());
134 }
135 if ( pList != nullptr )
136 {
137 pList->ProcessWindowEvent (rVclWindowEvent);
138 pList->HandleDropOpen();
139 }
140 break;
141 }
142 case VclEventId::DropdownClose:
143 {
144 VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
145 if ( pList == nullptr )
146 {
148 pList = static_cast<VCLXAccessibleList*>(m_xList.get());
149 }
150 if ( pList != nullptr )
151 {
152 pList->ProcessWindowEvent (rVclWindowEvent);
153 }
154 VclPtr<vcl::Window> pWindow = GetWindow();
155 if( pWindow && (pWindow->HasFocus() || pWindow->HasChildPathFocus()) )
156 {
157 Any aOldValue, aNewValue;
158 aNewValue <<= AccessibleStateType::FOCUSED;
159 NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue );
160 }
161 break;
162 }
163 case VclEventId::ComboboxSelect:
164 {
165 VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
166 if (pList != nullptr && m_xText.is())
167 {
168 Reference<XAccessibleText> xText (m_xText->getAccessibleContext(), UNO_QUERY);
169 if ( xText.is() )
170 {
171 OUString sText = xText->getSelectedText();
172 if ( sText.isEmpty() )
173 sText = xText->getText();
175#if defined(_WIN32)
177 NotifyAccessibleEvent(AccessibleEventId::VALUE_CHANGED, Any(), Any());
178#endif
179 }
180 }
181 break;
182 }
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:
192 {
193 // Forward the call to the list child.
194 VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
195 if ( pList == nullptr )
196 {
198 pList = static_cast<VCLXAccessibleList*>(m_xList.get());
199 }
200 if ( pList != nullptr )
201 pList->ProcessWindowEvent (rVclWindowEvent);
202 break;
203 }
204
205 //case VclEventId::ComboboxSelect:
206 case VclEventId::ComboboxDeselect:
207 {
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())
214 {
215 Reference<XAccessibleText> xText (m_xText->getAccessibleContext(), UNO_QUERY);
216 if ( xText.is() )
217 {
218 OUString sText = xText->getSelectedText();
219 if ( sText.isEmpty() )
220 sText = xText->getText();
221 pList->UpdateSelection (sText);
222 }
223 }
224 break;
225 }
226
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)
234 {
235 if (m_xText.is())
236 {
237 Reference<XAccessibleContext> xContext = m_xText->getAccessibleContext();
238 VCLXAccessibleEdit* pEdit = static_cast<VCLXAccessibleEdit*>(xContext.get());
239 if (pEdit != nullptr)
240 pEdit->ProcessWindowEvent (rVclWindowEvent);
241 }
242 }
243 break;
244 default:
245 VCLXAccessibleComponent::ProcessWindowEvent( rVclWindowEvent );
246 }
247}
248
249//===== XAccessible =========================================================
250
251Reference< XAccessibleContext > SAL_CALL VCLXAccessibleBox::getAccessibleContext( )
252{
253 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
254
255 return this;
256}
257
258//===== XAccessibleContext ==================================================
259
261{
262 SolarMutexGuard aSolarGuard;
263 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
264
266}
267
269{
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;
274 if (IsValid())
275 nCount += (m_bHasTextChild?1:0) + (m_bHasListChild?1:0);
276 else
277 {
278 // Object not valid anymore. Release references to children.
279 m_bHasTextChild = false;
280 m_xText = nullptr;
281 m_bHasListChild = false;
282 m_xList = nullptr;
283 }
284
285 return nCount;
286}
287
288Reference<XAccessible> SAL_CALL VCLXAccessibleBox::getAccessibleChild (sal_Int64 i)
289{
290 SolarMutexGuard aSolarGuard;
291 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
292
293 if (i<0 || i>=implGetAccessibleChildCount())
294 throw IndexOutOfBoundsException();
295
297 if (IsValid())
298 {
299 if (i==1 || ! m_bHasTextChild)
300 {
301 // List.
302 if ( ! m_xList.is())
303 {
304 rtl::Reference<VCLXAccessibleList> pList = new VCLXAccessibleList ( GetVCLXWindow(),
306 this);
307 pList->SetIndexInParent (i);
308 m_xList = pList;
309 }
310 xChild = m_xList;
311 }
312 else
313 {
314 // Text Field.
315 if ( ! m_xText.is())
316 {
317 if (m_aBoxType==COMBOBOX)
318 {
319 VclPtr< ComboBox > pComboBox = GetAs< ComboBox >();
320 if (pComboBox!=nullptr && pComboBox->GetSubEdit()!=nullptr)
321 //Set the edit's acc name the same as parent
322 {
323 pComboBox->GetSubEdit()->SetAccessibleName(getAccessibleName());
324 m_xText = pComboBox->GetSubEdit()->GetAccessible();
325 }
326 }
327 else if (m_bIsDropDownBox)
328 m_xText = new VCLXAccessibleTextField (GetVCLXWindow(),this);
329 }
330 xChild = m_xText;
331 }
332 }
333
334 return xChild;
335}
336
338{
339 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
340
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;
346 return AccessibleRole::COMBO_BOX;
347 else
348 return AccessibleRole::PANEL;
349}
350
351//===== XAccessibleAction ===================================================
352
354{
355 ::osl::Guard< ::osl::Mutex> aGuard (GetMutex());
356
357 // There is one action for drop down boxes (toggle popup) and none for
358 // the other boxes.
359 return m_bIsDropDownBox ? 1 : 0;
360}
361
363{
364 bool bNotify = false;
365
366 {
367 SolarMutexGuard aSolarGuard;
368 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
369
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())),
375 getXWeak());
376
377 if (m_aBoxType == COMBOBOX)
378 {
379 VclPtr< ComboBox > pComboBox = GetAs< ComboBox >();
380 if (pComboBox != nullptr)
381 {
382 pComboBox->ToggleDropDown();
383 bNotify = true;
384 }
385 }
386 else if (m_aBoxType == LISTBOX)
387 {
388 VclPtr< ListBox > pListBox = GetAs< ListBox >();
389 if (pListBox != nullptr)
390 {
391 pListBox->ToggleDropDown();
392 bNotify = true;
393 }
394 }
395 }
396
397 if (bNotify)
398 NotifyAccessibleEvent (AccessibleEventId::ACTION_CHANGED, Any(), Any());
399
400 return bNotify;
401}
402
403OUString SAL_CALL VCLXAccessibleBox::getAccessibleActionDescription (sal_Int32 nIndex)
404{
405 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
406 if (nIndex!=0 || !m_bIsDropDownBox)
407 throw css::lang::IndexOutOfBoundsException();
408
410}
411
412Reference< XAccessibleKeyBinding > VCLXAccessibleBox::getAccessibleActionKeyBinding( sal_Int32 nIndex )
413{
414 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
415
416 Reference< XAccessibleKeyBinding > xRet;
417
418 if (nIndex<0 || nIndex>=getAccessibleActionCount())
419 throw css::lang::IndexOutOfBoundsException();
420
421 // ... which key?
422 return xRet;
423}
424
425// ===== XAccessibleValue ===============================================
427{
428 SolarMutexGuard aSolarGuard;
429 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
430
431 Any aAny;
432 if( m_xList.is() && m_xText.is())
433 {
434 // VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
435 Reference<XAccessibleText> xText (m_xText->getAccessibleContext(), UNO_QUERY);
436 if ( xText.is() )
437 {
438 OUString sText = xText->getText();
439 aAny <<= sText;
440 }
441 }
442 if (m_aBoxType == LISTBOX && m_bIsDropDownBox && m_xList.is() )
443 {
444
445 VCLXAccessibleList* pList = static_cast<VCLXAccessibleList*>(m_xList.get());
446 if(pList->IsInDropDown())
447 {
449 {
450 Reference<XAccessibleContext> xName (pList->getSelectedAccessibleChild(sal_Int64(0)), UNO_QUERY);
451 if(xName.is())
452 {
453 aAny <<= xName->getAccessibleName();
454 }
455 }
456 }
457 }
458
459 return aAny;
460}
461
463{
464 SolarMutexGuard aSolarGuard;
465 ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() );
466
467 OUString fValue;
468 bool bValid = (aNumber >>= fValue);
469 return bValid;
470
471}
472
474{
475 Any aAny;
476 return aAny;
477}
478
480{
481 Any aAny;
482 return aAny;
483}
484
486{
487 return Any();
488}
489
490// Set the INDETERMINATE state when there is no selected item for combobox
492{
493 VCLXAccessibleComponent::FillAccessibleStateSet(rStateSet);
494 if (m_aBoxType == COMBOBOX )
495 {
496 OUString sText;
497 sal_Int32 nEntryCount = 0;
498 VclPtr< ComboBox > pComboBox = GetAs< ComboBox >();
499 if (pComboBox != nullptr)
500 {
501 Edit* pSubEdit = pComboBox->GetSubEdit();
502 if ( pSubEdit)
503 sText = pSubEdit->GetText();
504 nEntryCount = pComboBox->GetEntryCount();
505 }
506 if ( sText.isEmpty() && nEntryCount > 0 )
507 rStateSet |= AccessibleStateType::INDETERMINATE;
508 }
509 else if (m_aBoxType == LISTBOX && m_bIsDropDownBox)
510 {
511 VclPtr< ListBox > pListBox = GetAs< ListBox >();
512 if (pListBox != nullptr && pListBox->GetEntryCount() > 0)
513 {
514 sal_Int32 nSelectedEntryCount = pListBox->GetSelectedEntryCount();
515 if ( nSelectedEntryCount == 0)
516 rStateSet |= AccessibleStateType::INDETERMINATE;
517 }
518 }
519}
520
521/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
virtual OUString GetText() const override
bool m_bHasListChild
This flag specifies whether an object has a list as child regardless of whether that child being curr...
virtual void ProcessWindowChildEvent(const VclWindowEvent &rVclWindowEvent) override
virtual css::uno::Any SAL_CALL getMinimumValue() override
virtual bool IsValid() const =0
Returns true when the object is valid.
css::uno::Reference< css::accessibility::XAccessible > m_xText
The child that represents the text field if there is one.
virtual css::uno::Any SAL_CALL getCurrentValue() override
virtual void ProcessWindowEvent(const VclWindowEvent &rVclWindowEvent) override
virtual OUString SAL_CALL getAccessibleActionDescription(sal_Int32 nIndex) override
The returned string is associated with resource RID_STR_ACC_ACTION_TOGGLEPOPUP.
virtual sal_Bool SAL_CALL doAccessibleAction(sal_Int32 nIndex) override
The action for drop down boxes lets the user toggle the visibility of the popup menu.
css::uno::Reference< css::accessibility::XAccessible > m_xList
The child that contains the items of this box.
virtual css::uno::Any SAL_CALL getMinimumIncrement() override
BoxType m_aBoxType
Specifies whether the box is a combo box or a list box.
bool m_bHasTextChild
This flag specifies whether an object has a text field as child regardless of whether that child bein...
sal_Int64 SAL_CALL getAccessibleChildCount() final override
Each object has one or two children: an optional text field and the actual list.
virtual sal_Bool SAL_CALL setCurrentValue(const css::uno::Any &aNumber) override
sal_Int16 SAL_CALL getAccessibleRole() override
The role is always AccessibleRole::COMBO_BOX.
sal_Int64 implGetAccessibleChildCount()
virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext() override
virtual void FillAccessibleStateSet(sal_Int64 &rStateSet) override
virtual css::uno::Reference< css::accessibility::XAccessibleKeyBinding > SAL_CALL getAccessibleActionKeyBinding(sal_Int32 nIndex) override
No keybinding returned so far.
css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild(sal_Int64 i) override
For drop down list boxes the text field is a not editable VCLXAccessibleTextField,...
virtual css::uno::Any SAL_CALL getMaximumValue() override
virtual sal_Int32 SAL_CALL getAccessibleActionCount() final override
There is one action for drop down boxes and none for others.
VCLXAccessibleBox(VCLXWindow *pVCLXindow, BoxType aType, bool bIsDropDownBox)
The constructor is initialized with the box type which may be either COMBOBOX or LISTBOX and a flag i...
bool m_bIsDropDownBox
Specifies whether the box is a drop down box and thus has an action.
virtual void ProcessWindowEvent(const VclWindowEvent &rVclWindowEvent) override
Base class for the list contained in list- and combo boxes.
virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getSelectedAccessibleChild(sal_Int64 nSelectedChildIndex) override
void UpdateSelection(std::u16string_view sTextOfSelectedItem)
Called on reception of selection events this method checks all known list items for a possible change...
virtual sal_Int64 SAL_CALL getSelectedAccessibleChildCount() override
void UpdateSelection_Acc(std::u16string_view sTextOfSelectedItem, bool b_IsDropDownList)
virtual void ProcessWindowEvent(const VclWindowEvent &rVclWindowEvent) override
Process some of the events and delegate the rest to the base classes.
This class represents non editable text fields.
VclEventId GetId() const
void * GetData() const
int nCount
sal_Int32 nIndex
int i
constexpr OUStringLiteral RID_STR_ACC_ACTION_TOGGLEPOPUP
Definition: strings.hxx:15
unsigned char sal_Bool