/src/libreoffice/vcl/source/accessibility/vclxaccessiblelist.cxx
Line | Count | Source |
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 | | |
20 | | #include <accessibility/listboxhelper.hxx> |
21 | | #include <accessibility/vclxaccessiblelist.hxx> |
22 | | #include <accessibility/vclxaccessiblelistitem.hxx> |
23 | | |
24 | | #include <unotools/accessiblerelationsethelper.hxx> |
25 | | #include <com/sun/star/accessibility/AccessibleStateType.hpp> |
26 | | #include <com/sun/star/accessibility/AccessibleEventId.hpp> |
27 | | #include <com/sun/star/accessibility/AccessibleRelationType.hpp> |
28 | | #include <com/sun/star/accessibility/AccessibleRole.hpp> |
29 | | #include <com/sun/star/lang/IndexOutOfBoundsException.hpp> |
30 | | #include <comphelper/sequence.hxx> |
31 | | #include <o3tl/safeint.hxx> |
32 | | #include <vcl/svapp.hxx> |
33 | | #include <vcl/toolkit/combobox.hxx> |
34 | | #include <vcl/toolkit/lstbox.hxx> |
35 | | #include <vcl/unohelp.hxx> |
36 | | #include <vcl/vclevent.hxx> |
37 | | |
38 | | using namespace ::com::sun::star; |
39 | | using namespace ::com::sun::star::uno; |
40 | | using namespace ::com::sun::star::lang; |
41 | | using namespace ::com::sun::star::accessibility; |
42 | | using namespace ::accessibility; |
43 | | |
44 | | namespace |
45 | | { |
46 | | /// @throws css::lang::IndexOutOfBoundsException |
47 | | void checkSelection_Impl( sal_Int64 _nIndex, const IComboListBoxHelper& _rListBox, bool bSelected ) |
48 | 0 | { |
49 | 0 | sal_Int32 nCount = bSelected ? _rListBox.GetSelectedEntryCount() |
50 | 0 | : _rListBox.GetEntryCount(); |
51 | 0 | if ( _nIndex < 0 || _nIndex >= nCount ) |
52 | 0 | throw css::lang::IndexOutOfBoundsException(); |
53 | 0 | } |
54 | | } |
55 | | |
56 | | VCLXAccessibleList::VCLXAccessibleList(vcl::Window* pWindow, BoxType aBoxType, |
57 | | const rtl::Reference<VCLXAccessibleBox>& _xParent) |
58 | 0 | : ImplInheritanceHelper (pWindow), |
59 | 0 | m_aBoxType (aBoxType), |
60 | 0 | m_nVisibleLineCount (0), |
61 | 0 | m_nIndexInParent (DEFAULT_INDEX_IN_PARENT), |
62 | 0 | m_nLastTopEntry ( 0 ), |
63 | 0 | m_nLastSelectedPos ( LISTBOX_ENTRY_NOTFOUND ), |
64 | 0 | m_bDisableProcessEvent ( false ), |
65 | 0 | m_bVisible ( true ), |
66 | 0 | m_nCurSelectedPos ( LISTBOX_ENTRY_NOTFOUND ), |
67 | 0 | m_xParent ( _xParent ) |
68 | 0 | { |
69 | | // Because combo boxes and list boxes don't have a common interface for |
70 | | // methods with identical signature we have to write down twice the |
71 | | // same code. |
72 | 0 | switch (m_aBoxType) |
73 | 0 | { |
74 | 0 | case COMBOBOX: |
75 | 0 | { |
76 | 0 | VclPtr< ComboBox > pBox = GetAs< ComboBox >(); |
77 | 0 | if ( pBox ) |
78 | 0 | m_pListBoxHelper.reset( new VCLListBoxHelper<ComboBox> (*pBox) ); |
79 | 0 | break; |
80 | 0 | } |
81 | | |
82 | 0 | case LISTBOX: |
83 | 0 | { |
84 | 0 | VclPtr< ListBox > pBox = GetAs< ListBox >(); |
85 | 0 | if ( pBox ) |
86 | 0 | m_pListBoxHelper.reset( new VCLListBoxHelper<ListBox> (*pBox) ); |
87 | 0 | break; |
88 | 0 | } |
89 | 0 | } |
90 | 0 | UpdateVisibleLineCount(); |
91 | 0 | if(m_pListBoxHelper) |
92 | 0 | { |
93 | 0 | m_nCurSelectedPos=m_pListBoxHelper->GetSelectedEntryPos(0); |
94 | 0 | } |
95 | 0 | sal_uInt16 nCount = static_cast<sal_uInt16>(getAccessibleChildCount()); |
96 | 0 | m_aAccessibleChildren.reserve(nCount); |
97 | 0 | } |
98 | | |
99 | | |
100 | | void VCLXAccessibleList::SetIndexInParent (sal_Int32 nIndex) |
101 | 0 | { |
102 | 0 | m_nIndexInParent = nIndex; |
103 | 0 | } |
104 | | |
105 | | |
106 | | void SAL_CALL VCLXAccessibleList::disposing() |
107 | 0 | { |
108 | 0 | VCLXAccessibleComponent::disposing(); |
109 | |
|
110 | 0 | disposeChildren(); |
111 | 0 | m_pListBoxHelper.reset(); |
112 | 0 | } |
113 | | |
114 | | void VCLXAccessibleList::disposeChildren() |
115 | 0 | { |
116 | | // Dispose all items in the list. |
117 | 0 | for (rtl::Reference<VCLXAccessibleListItem>& rxChild : m_aAccessibleChildren) |
118 | 0 | { |
119 | 0 | if (rxChild.is()) |
120 | 0 | rxChild->dispose(); |
121 | 0 | } |
122 | |
|
123 | 0 | m_aAccessibleChildren.clear(); |
124 | 0 | } |
125 | | |
126 | | |
127 | | void VCLXAccessibleList::FillAccessibleStateSet (sal_Int64& rStateSet) |
128 | 0 | { |
129 | 0 | SolarMutexGuard aSolarGuard; |
130 | |
|
131 | 0 | VCLXAccessibleComponent::FillAccessibleStateSet( rStateSet ); |
132 | | // check if our list should be visible |
133 | 0 | if ( m_pListBoxHelper |
134 | 0 | && (m_pListBoxHelper->GetStyle() & WB_DROPDOWN ) == WB_DROPDOWN |
135 | 0 | && !m_pListBoxHelper->IsInDropDown() ) |
136 | 0 | { |
137 | 0 | rStateSet &= ~AccessibleStateType::VISIBLE; |
138 | 0 | rStateSet &= ~AccessibleStateType::SHOWING; |
139 | 0 | m_bVisible = false; |
140 | 0 | } |
141 | | |
142 | | // Both the combo box and list box are handled identical in the |
143 | | // following but for some reason they don't have a common interface for |
144 | | // the methods used. |
145 | 0 | if ( m_pListBoxHelper ) |
146 | 0 | { |
147 | 0 | if ( m_pListBoxHelper->IsMultiSelectionEnabled() ) |
148 | 0 | rStateSet |= AccessibleStateType::MULTI_SELECTABLE; |
149 | 0 | rStateSet |= AccessibleStateType::FOCUSABLE; |
150 | | // All children are transient. |
151 | 0 | rStateSet |= AccessibleStateType::MANAGES_DESCENDANTS; |
152 | 0 | } |
153 | 0 | } |
154 | | |
155 | | void VCLXAccessibleList::notifyVisibleStates(bool _bSetNew ) |
156 | 0 | { |
157 | 0 | m_bVisible = _bSetNew; |
158 | 0 | Any aOldValue, aNewValue; |
159 | 0 | (_bSetNew ? aNewValue : aOldValue ) <<= AccessibleStateType::VISIBLE; |
160 | 0 | NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); |
161 | 0 | (_bSetNew ? aNewValue : aOldValue ) <<= AccessibleStateType::SHOWING; |
162 | 0 | NotifyAccessibleEvent( AccessibleEventId::STATE_CHANGED, aOldValue, aNewValue ); |
163 | |
|
164 | 0 | auto aIter = m_aAccessibleChildren.begin(); |
165 | 0 | UpdateVisibleLineCount(); |
166 | | // adjust the index inside the VCLXAccessibleListItem |
167 | 0 | for ( ; aIter != m_aAccessibleChildren.end(); ) |
168 | 0 | { |
169 | 0 | rtl::Reference<VCLXAccessibleListItem> xChild = *aIter; |
170 | 0 | if (!xChild.is()) |
171 | 0 | { |
172 | 0 | aIter = m_aAccessibleChildren.erase(aIter); |
173 | 0 | } |
174 | 0 | else |
175 | 0 | { |
176 | 0 | const sal_Int32 nTopEntry = m_pListBoxHelper ? m_pListBoxHelper->GetTopEntry() : 0; |
177 | 0 | const sal_Int32 nPos = static_cast<sal_Int32>(aIter - m_aAccessibleChildren.begin()); |
178 | 0 | bool bVisible = ( nPos>=nTopEntry && nPos<( nTopEntry + m_nVisibleLineCount ) ); |
179 | 0 | xChild->SetVisible(m_bVisible && bVisible); |
180 | 0 | ++aIter; |
181 | 0 | } |
182 | |
|
183 | 0 | } |
184 | 0 | } |
185 | | |
186 | | void VCLXAccessibleList::UpdateSelection_Acc (std::u16string_view /*sTextOfSelectedItem*/, bool b_IsDropDownList) |
187 | 0 | { |
188 | 0 | if ( m_aBoxType != COMBOBOX ) |
189 | 0 | return; |
190 | | |
191 | | /* FIXME: is there something missing here? nIndex is unused. Looks like |
192 | | * copy-paste from VCLXAccessibleList::UpdateSelection() */ |
193 | | // VclPtr< ComboBox > pBox = GetAs< ComboBox >(); |
194 | | // if ( pBox ) |
195 | | // { |
196 | | // // Find the index of the selected item inside the VCL control... |
197 | | // sal_Int32 nIndex = pBox->GetEntryPos(sTextOfSelectedItem); |
198 | | // // ...and then find the associated accessibility object. |
199 | | // if ( nIndex == LISTBOX_ENTRY_NOTFOUND ) |
200 | | // nIndex = 0; |
201 | 0 | UpdateSelection_Impl_Acc(b_IsDropDownList); |
202 | | // } |
203 | 0 | } |
204 | | |
205 | | |
206 | | void VCLXAccessibleList::UpdateSelection_Impl_Acc(bool bHasDropDownList) |
207 | 0 | { |
208 | 0 | uno::Any aOldValue, aNewValue; |
209 | |
|
210 | 0 | { |
211 | 0 | SolarMutexGuard aSolarGuard; |
212 | 0 | ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); |
213 | 0 | rtl::Reference< VCLXAccessibleListItem > xNewAcc; |
214 | 0 | if ( m_pListBoxHelper ) |
215 | 0 | { |
216 | 0 | sal_Int32 i=0; |
217 | 0 | m_nCurSelectedPos = LISTBOX_ENTRY_NOTFOUND; |
218 | 0 | for (const rtl::Reference<VCLXAccessibleListItem>& rxChild : m_aAccessibleChildren) |
219 | 0 | { |
220 | 0 | if (rxChild.is()) |
221 | 0 | { |
222 | | // Retrieve the item's index from the list entry. |
223 | 0 | bool bNowSelected = m_pListBoxHelper->IsEntryPosSelected (i); |
224 | 0 | if (bNowSelected) |
225 | 0 | m_nCurSelectedPos = i; |
226 | |
|
227 | 0 | if (bNowSelected && !rxChild->IsSelected()) |
228 | 0 | { |
229 | 0 | xNewAcc = rxChild; |
230 | 0 | aNewValue <<= Reference< XAccessible >(xNewAcc); |
231 | 0 | } |
232 | 0 | else if (rxChild->IsSelected()) |
233 | 0 | m_nLastSelectedPos = i; |
234 | |
|
235 | 0 | rxChild->SetSelected(bNowSelected); |
236 | 0 | } |
237 | 0 | else |
238 | 0 | { // it could happen that a child was not created before |
239 | 0 | checkEntrySelected(i,aNewValue,xNewAcc); |
240 | 0 | } |
241 | 0 | ++i; |
242 | 0 | } |
243 | 0 | const sal_Int32 nCount = m_pListBoxHelper->GetEntryCount(); |
244 | 0 | if ( i < nCount ) // here we have to check the if any other listbox entry is selected |
245 | 0 | { |
246 | 0 | for (; i < nCount && !checkEntrySelected(i,aNewValue,xNewAcc) ;++i ) |
247 | 0 | ; |
248 | 0 | } |
249 | 0 | if ( xNewAcc.is() && GetWindow()->HasFocus() ) |
250 | 0 | { |
251 | 0 | if ( m_nLastSelectedPos != LISTBOX_ENTRY_NOTFOUND ) |
252 | 0 | aOldValue <<= getAccessibleChild( m_nLastSelectedPos ); |
253 | 0 | aNewValue <<= Reference< XAccessible >(xNewAcc); |
254 | 0 | } |
255 | 0 | } |
256 | 0 | } |
257 | | |
258 | | // since an active descendant is the UI element with keyboard focus, only send |
259 | | // ACTIVE_DESCENDANT_CHANGED if the listbox/combobox has focus |
260 | 0 | vcl::Window* pWindow = GetWindow(); |
261 | 0 | assert(pWindow); |
262 | 0 | const bool bFocused = pWindow->HasChildPathFocus(); |
263 | |
|
264 | 0 | if (m_aBoxType == COMBOBOX) |
265 | 0 | { |
266 | | //VCLXAccessibleDropDownComboBox |
267 | | //when in list is dropped down, xText = NULL |
268 | 0 | if (bHasDropDownList && m_pListBoxHelper && m_pListBoxHelper->IsInDropDown()) |
269 | 0 | { |
270 | 0 | if ( aNewValue.hasValue() || aOldValue.hasValue() ) |
271 | 0 | { |
272 | 0 | if (bFocused) |
273 | 0 | { |
274 | 0 | NotifyAccessibleEvent( |
275 | 0 | AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, |
276 | 0 | aOldValue, |
277 | 0 | aNewValue ); |
278 | 0 | } |
279 | |
|
280 | 0 | NotifyListItem(aNewValue); |
281 | 0 | } |
282 | 0 | } |
283 | 0 | else |
284 | 0 | { |
285 | | //VCLXAccessibleComboBox |
286 | 0 | NotifyAccessibleEvent( AccessibleEventId::SELECTION_CHANGED, uno::Any(), uno::Any() ); |
287 | 0 | } |
288 | 0 | } |
289 | 0 | else if (m_aBoxType == LISTBOX) |
290 | 0 | { |
291 | 0 | if ( aNewValue.hasValue() || aOldValue.hasValue() ) |
292 | 0 | { |
293 | 0 | if (bFocused) |
294 | 0 | { |
295 | 0 | NotifyAccessibleEvent( |
296 | 0 | AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, |
297 | 0 | aOldValue, |
298 | 0 | aNewValue ); |
299 | 0 | } |
300 | |
|
301 | 0 | NotifyListItem(aNewValue); |
302 | 0 | } |
303 | 0 | } |
304 | 0 | } |
305 | | |
306 | | void VCLXAccessibleList::NotifyListItem(css::uno::Any const & val) |
307 | 0 | { |
308 | 0 | Reference< XAccessible > xCurItem; |
309 | 0 | val >>= xCurItem; |
310 | 0 | if (xCurItem.is()) |
311 | 0 | { |
312 | 0 | VCLXAccessibleListItem* pCurItem = static_cast< VCLXAccessibleListItem* >(xCurItem.get()); |
313 | 0 | if (pCurItem) |
314 | 0 | { |
315 | 0 | pCurItem->NotifyAccessibleEvent(AccessibleEventId::SELECTION_CHANGED,Any(),Any()); |
316 | 0 | } |
317 | 0 | } |
318 | 0 | } |
319 | | |
320 | | void VCLXAccessibleList::ProcessWindowEvent (const VclWindowEvent& rVclWindowEvent, bool b_IsDropDownList) |
321 | 0 | { |
322 | 0 | switch ( rVclWindowEvent.GetId() ) |
323 | 0 | { |
324 | 0 | case VclEventId::DropdownSelect: |
325 | 0 | case VclEventId::ListboxSelect: |
326 | 0 | if ( !m_bDisableProcessEvent ) |
327 | 0 | UpdateSelection_Impl_Acc(b_IsDropDownList); |
328 | 0 | break; |
329 | 0 | case VclEventId::WindowGetFocus: |
330 | 0 | break; |
331 | 0 | case VclEventId::ControlGetFocus: |
332 | 0 | { |
333 | 0 | VCLXAccessibleComponent::ProcessWindowEvent (rVclWindowEvent); |
334 | 0 | if (m_aBoxType == COMBOBOX && b_IsDropDownList) |
335 | 0 | { |
336 | | //VCLXAccessibleDropDownComboBox |
337 | 0 | } |
338 | 0 | else if (m_aBoxType == LISTBOX && b_IsDropDownList) |
339 | 0 | { |
340 | 0 | } |
341 | 0 | else if ( m_aBoxType == LISTBOX && !b_IsDropDownList) |
342 | 0 | { |
343 | 0 | if ( m_pListBoxHelper ) |
344 | 0 | { |
345 | 0 | uno::Any aOldValue, |
346 | 0 | aNewValue; |
347 | 0 | sal_Int32 nPos = m_nCurSelectedPos; //m_pListBoxHelper->GetSelectedEntryPos(); |
348 | |
|
349 | 0 | if ( nPos == LISTBOX_ENTRY_NOTFOUND ) |
350 | 0 | nPos = m_pListBoxHelper->GetTopEntry(); |
351 | 0 | if ( nPos != LISTBOX_ENTRY_NOTFOUND ) |
352 | 0 | aNewValue <<= uno::Reference<XAccessible>(CreateChild(nPos)); |
353 | 0 | NotifyAccessibleEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, |
354 | 0 | aOldValue, |
355 | 0 | aNewValue ); |
356 | 0 | } |
357 | 0 | } |
358 | 0 | } |
359 | 0 | break; |
360 | 0 | default: |
361 | 0 | break; |
362 | 0 | } |
363 | |
|
364 | 0 | } |
365 | | |
366 | | void VCLXAccessibleList::ProcessWindowEvent (const VclWindowEvent& rVclWindowEvent) |
367 | 0 | { |
368 | | // Create a reference to this object to prevent an early release of the |
369 | | // listbox (VclEventId::ObjectDying). |
370 | 0 | Reference< XAccessible > xHoldAlive = this; |
371 | |
|
372 | 0 | switch ( rVclWindowEvent.GetId() ) |
373 | 0 | { |
374 | 0 | case VclEventId::DropdownOpen: |
375 | 0 | notifyVisibleStates(true); |
376 | 0 | break; |
377 | 0 | case VclEventId::DropdownClose: |
378 | 0 | notifyVisibleStates(false); |
379 | 0 | break; |
380 | 0 | case VclEventId::ListboxScrolled: |
381 | 0 | UpdateEntryRange_Impl(); |
382 | 0 | break; |
383 | | |
384 | | // The selection events VclEventId::ComboboxSelect and |
385 | | // VclEventId::ComboboxDeselect are not handled here because here we |
386 | | // have no access to the edit field. Its text is necessary to |
387 | | // identify the currently selected item. |
388 | | |
389 | 0 | case VclEventId::ObjectDying: |
390 | 0 | { |
391 | 0 | dispose(); |
392 | |
|
393 | 0 | VCLXAccessibleComponent::ProcessWindowEvent (rVclWindowEvent); |
394 | 0 | break; |
395 | 0 | } |
396 | | |
397 | 0 | case VclEventId::ListboxItemRemoved: |
398 | 0 | case VclEventId::ComboboxItemRemoved: |
399 | 0 | case VclEventId::ListboxItemAdded: |
400 | 0 | case VclEventId::ComboboxItemAdded: |
401 | 0 | HandleChangedItemList(); |
402 | 0 | break; |
403 | 0 | case VclEventId::ControlGetFocus: |
404 | 0 | { |
405 | 0 | VCLXAccessibleComponent::ProcessWindowEvent (rVclWindowEvent); |
406 | | // Added by IBM Symphony Acc team to handle the list item focus when List control get focus |
407 | 0 | bool b_IsDropDownList = true; |
408 | 0 | if (m_pListBoxHelper) |
409 | 0 | b_IsDropDownList = ((m_pListBoxHelper->GetStyle() & WB_DROPDOWN ) == WB_DROPDOWN); |
410 | 0 | if ( m_aBoxType == LISTBOX && !b_IsDropDownList ) |
411 | 0 | { |
412 | 0 | if ( m_pListBoxHelper ) |
413 | 0 | { |
414 | 0 | uno::Any aOldValue, |
415 | 0 | aNewValue; |
416 | 0 | sal_Int32 nPos = m_nCurSelectedPos; |
417 | |
|
418 | 0 | if ( nPos == LISTBOX_ENTRY_NOTFOUND ) |
419 | 0 | nPos = m_pListBoxHelper->GetTopEntry(); |
420 | 0 | if ( nPos != LISTBOX_ENTRY_NOTFOUND ) |
421 | 0 | aNewValue <<= Reference<XAccessible>(CreateChild(nPos)); |
422 | 0 | NotifyAccessibleEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, |
423 | 0 | aOldValue, |
424 | 0 | aNewValue ); |
425 | 0 | } |
426 | 0 | } |
427 | 0 | } |
428 | 0 | break; |
429 | | |
430 | 0 | default: |
431 | 0 | VCLXAccessibleComponent::ProcessWindowEvent (rVclWindowEvent); |
432 | 0 | } |
433 | 0 | } |
434 | | |
435 | | void VCLXAccessibleList::FillAccessibleRelationSet( utl::AccessibleRelationSetHelper& rRelationSet ) |
436 | 0 | { |
437 | 0 | VclPtr< ListBox > pBox = GetAs< ListBox >(); |
438 | 0 | if( m_aBoxType == LISTBOX ) |
439 | 0 | { |
440 | 0 | if (m_pListBoxHelper && (m_pListBoxHelper->GetStyle() & WB_DROPDOWN ) != WB_DROPDOWN) |
441 | 0 | { |
442 | 0 | uno::Sequence<uno::Reference<css::accessibility::XAccessible>> aSequence { pBox->GetAccessible() }; |
443 | 0 | rRelationSet.AddRelation( css::accessibility::AccessibleRelation( css::accessibility::AccessibleRelationType_MEMBER_OF, aSequence ) ); |
444 | 0 | } |
445 | 0 | } |
446 | 0 | else |
447 | 0 | { |
448 | 0 | VCLXAccessibleComponent::FillAccessibleRelationSet(rRelationSet); |
449 | 0 | } |
450 | 0 | } |
451 | | |
452 | | |
453 | | /** To find out which item is currently selected and to update the SELECTED |
454 | | state of the associated accessibility objects accordingly we exploit the |
455 | | fact that the |
456 | | */ |
457 | | void VCLXAccessibleList::UpdateSelection (std::u16string_view sTextOfSelectedItem) |
458 | 0 | { |
459 | 0 | if ( m_aBoxType != COMBOBOX ) |
460 | 0 | return; |
461 | | |
462 | 0 | VclPtr< ComboBox > pBox = GetAs< ComboBox >(); |
463 | 0 | if ( pBox ) |
464 | 0 | { |
465 | | // Find the index of the selected item inside the VCL control... |
466 | 0 | sal_Int32 nIndex = pBox->GetEntryPos(sTextOfSelectedItem); |
467 | | // ...and then find the associated accessibility object. |
468 | 0 | if ( nIndex == LISTBOX_ENTRY_NOTFOUND ) |
469 | 0 | nIndex = 0; |
470 | 0 | UpdateSelection_Impl(nIndex); |
471 | 0 | } |
472 | 0 | } |
473 | | |
474 | | |
475 | | rtl::Reference<VCLXAccessibleListItem> VCLXAccessibleList::CreateChild(sal_Int32 nPos) |
476 | 0 | { |
477 | 0 | rtl::Reference<VCLXAccessibleListItem> xChild; |
478 | |
|
479 | 0 | if ( o3tl::make_unsigned(nPos) >= m_aAccessibleChildren.size() ) |
480 | 0 | { |
481 | 0 | m_aAccessibleChildren.resize(nPos + 1); |
482 | | |
483 | | // insert into the container |
484 | 0 | xChild = new VCLXAccessibleListItem(nPos, this); |
485 | 0 | m_aAccessibleChildren[nPos] = xChild; |
486 | 0 | } |
487 | 0 | else |
488 | 0 | { |
489 | 0 | xChild = m_aAccessibleChildren[nPos]; |
490 | | // check if position is empty and can be used else we have to adjust all entries behind this |
491 | 0 | if (!xChild.is()) |
492 | 0 | { |
493 | 0 | xChild = new VCLXAccessibleListItem(nPos, this); |
494 | 0 | m_aAccessibleChildren[nPos] = xChild; |
495 | 0 | } |
496 | 0 | } |
497 | |
|
498 | 0 | if ( xChild.is() ) |
499 | 0 | { |
500 | | // Just add the SELECTED state. |
501 | 0 | bool bNowSelected = false; |
502 | 0 | if ( m_pListBoxHelper ) |
503 | 0 | bNowSelected = m_pListBoxHelper->IsEntryPosSelected(nPos); |
504 | 0 | if (bNowSelected) |
505 | 0 | m_nCurSelectedPos = nPos; |
506 | 0 | xChild->SetSelected(bNowSelected); |
507 | | |
508 | | // Set the child's VISIBLE state. |
509 | 0 | UpdateVisibleLineCount(); |
510 | 0 | const sal_Int32 nTopEntry = m_pListBoxHelper ? m_pListBoxHelper->GetTopEntry() : 0; |
511 | 0 | bool bVisible = ( nPos>=nTopEntry && nPos<( nTopEntry + m_nVisibleLineCount ) ); |
512 | 0 | xChild->SetVisible(m_bVisible && bVisible); |
513 | 0 | } |
514 | |
|
515 | 0 | return xChild; |
516 | 0 | } |
517 | | |
518 | | |
519 | | void VCLXAccessibleList::HandleChangedItemList() |
520 | 0 | { |
521 | 0 | disposeChildren(); |
522 | 0 | NotifyAccessibleEvent ( |
523 | 0 | AccessibleEventId::INVALIDATE_ALL_CHILDREN, |
524 | 0 | Any(), Any()); |
525 | 0 | } |
526 | | |
527 | | // XAccessibleContext |
528 | | |
529 | | sal_Int64 SAL_CALL VCLXAccessibleList::getAccessibleChildCount() |
530 | 0 | { |
531 | 0 | SolarMutexGuard aSolarGuard; |
532 | 0 | ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); |
533 | 0 | return implGetAccessibleChildCount(); |
534 | 0 | } |
535 | | |
536 | | sal_Int64 VCLXAccessibleList::implGetAccessibleChildCount() |
537 | 0 | { |
538 | 0 | sal_Int32 nCount = 0; |
539 | 0 | if ( m_pListBoxHelper ) |
540 | 0 | nCount = m_pListBoxHelper->GetEntryCount(); |
541 | |
|
542 | 0 | return nCount; |
543 | 0 | } |
544 | | |
545 | | Reference<XAccessible> SAL_CALL VCLXAccessibleList::getAccessibleChild (sal_Int64 i) |
546 | 0 | { |
547 | 0 | SolarMutexGuard aSolarGuard; |
548 | 0 | ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); |
549 | |
|
550 | 0 | if ( i < 0 || i >= getAccessibleChildCount() ) |
551 | 0 | throw IndexOutOfBoundsException(); |
552 | | |
553 | 0 | rtl::Reference< VCLXAccessibleListItem > xChild; |
554 | | // search for the child |
555 | 0 | if ( o3tl::make_unsigned(i) >= m_aAccessibleChildren.size() ) |
556 | 0 | xChild = CreateChild (i); |
557 | 0 | else |
558 | 0 | { |
559 | 0 | xChild = m_aAccessibleChildren[i]; |
560 | 0 | if ( !xChild.is() ) |
561 | 0 | xChild = CreateChild (i); |
562 | 0 | } |
563 | 0 | OSL_ENSURE( xChild.is(), "VCLXAccessibleList::getAccessibleChild: returning empty child!" ); |
564 | 0 | return xChild; |
565 | 0 | } |
566 | | |
567 | | Reference< XAccessible > SAL_CALL VCLXAccessibleList::getAccessibleParent( ) |
568 | 0 | { |
569 | 0 | ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); |
570 | |
|
571 | 0 | return m_xParent; |
572 | 0 | } |
573 | | |
574 | | sal_Int64 SAL_CALL VCLXAccessibleList::getAccessibleIndexInParent() |
575 | 0 | { |
576 | 0 | if (m_nIndexInParent != DEFAULT_INDEX_IN_PARENT) |
577 | 0 | return m_nIndexInParent; |
578 | 0 | else |
579 | 0 | return VCLXAccessibleComponent::getAccessibleIndexInParent(); |
580 | 0 | } |
581 | | |
582 | | sal_Int16 SAL_CALL VCLXAccessibleList::getAccessibleRole() |
583 | 0 | { |
584 | 0 | return AccessibleRole::LIST; |
585 | 0 | } |
586 | | |
587 | | // XServiceInfo |
588 | | OUString VCLXAccessibleList::getImplementationName() |
589 | 0 | { |
590 | 0 | return u"com.sun.star.comp.toolkit.AccessibleList"_ustr; |
591 | 0 | } |
592 | | |
593 | | Sequence< OUString > VCLXAccessibleList::getSupportedServiceNames() |
594 | 0 | { |
595 | 0 | return comphelper::concatSequences(VCLXAccessibleComponent::getSupportedServiceNames(), |
596 | 0 | std::initializer_list<OUString>{u"com.sun.star.accessibility.AccessibleList"_ustr}); |
597 | 0 | } |
598 | | |
599 | | void VCLXAccessibleList::UpdateVisibleLineCount() |
600 | 0 | { |
601 | 0 | if ( m_pListBoxHelper ) |
602 | 0 | { |
603 | 0 | if ( (m_pListBoxHelper->GetStyle() & WB_DROPDOWN ) == WB_DROPDOWN ) |
604 | 0 | m_nVisibleLineCount = m_pListBoxHelper->GetDisplayLineCount(); |
605 | 0 | else |
606 | 0 | { |
607 | 0 | sal_uInt16 nCols = 0, |
608 | 0 | nLines = 0; |
609 | 0 | m_pListBoxHelper->GetMaxVisColumnsAndLines (nCols, nLines); |
610 | 0 | m_nVisibleLineCount = nLines; |
611 | 0 | } |
612 | 0 | } |
613 | 0 | } |
614 | | |
615 | | void VCLXAccessibleList::UpdateEntryRange_Impl() |
616 | 0 | { |
617 | 0 | SolarMutexGuard aSolarGuard; |
618 | 0 | ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); |
619 | |
|
620 | 0 | sal_Int32 nTop = m_nLastTopEntry; |
621 | |
|
622 | 0 | if ( m_pListBoxHelper ) |
623 | 0 | nTop = m_pListBoxHelper->GetTopEntry(); |
624 | 0 | if ( nTop != m_nLastTopEntry ) |
625 | 0 | { |
626 | 0 | UpdateVisibleLineCount(); |
627 | 0 | sal_Int32 nBegin = std::min( m_nLastTopEntry, nTop ); |
628 | 0 | sal_Int32 nEnd = std::max( m_nLastTopEntry + m_nVisibleLineCount, nTop + m_nVisibleLineCount ); |
629 | 0 | for (sal_Int32 i = nBegin; (i <= nEnd); ++i) |
630 | 0 | { |
631 | 0 | bool bVisible = ( i >= nTop && i < ( nTop + m_nVisibleLineCount ) ); |
632 | 0 | rtl::Reference<VCLXAccessibleListItem> xChild; |
633 | 0 | if ( o3tl::make_unsigned(i) < m_aAccessibleChildren.size() ) |
634 | 0 | xChild = m_aAccessibleChildren[i]; |
635 | 0 | else if ( bVisible ) |
636 | 0 | xChild = CreateChild(i); |
637 | |
|
638 | 0 | if (xChild.is()) |
639 | 0 | xChild->SetVisible(m_bVisible && bVisible); |
640 | 0 | } |
641 | 0 | } |
642 | |
|
643 | 0 | m_nLastTopEntry = nTop; |
644 | 0 | } |
645 | | |
646 | | bool VCLXAccessibleList::checkEntrySelected(sal_Int32 _nPos,Any& _rNewValue, rtl::Reference< VCLXAccessibleListItem >& _rxNewAcc) |
647 | 0 | { |
648 | 0 | OSL_ENSURE(m_pListBoxHelper,"Helper is not valid!"); |
649 | 0 | bool bNowSelected = false; |
650 | 0 | if ( m_pListBoxHelper ) |
651 | 0 | { |
652 | 0 | bNowSelected = m_pListBoxHelper->IsEntryPosSelected (_nPos); |
653 | 0 | if ( bNowSelected ) |
654 | 0 | { |
655 | 0 | _rxNewAcc = CreateChild(_nPos); |
656 | 0 | _rNewValue <<= uno::Reference<XAccessible>(_rxNewAcc); |
657 | 0 | } |
658 | 0 | } |
659 | 0 | return bNowSelected; |
660 | 0 | } |
661 | | |
662 | | |
663 | | void VCLXAccessibleList::UpdateSelection_Impl(sal_Int32) |
664 | 0 | { |
665 | 0 | uno::Any aOldValue, aNewValue; |
666 | |
|
667 | 0 | { |
668 | 0 | SolarMutexGuard aSolarGuard; |
669 | 0 | ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); |
670 | 0 | rtl::Reference< VCLXAccessibleListItem > xNewAcc; |
671 | |
|
672 | 0 | if ( m_pListBoxHelper ) |
673 | 0 | { |
674 | 0 | sal_Int32 i=0; |
675 | 0 | m_nCurSelectedPos = LISTBOX_ENTRY_NOTFOUND; |
676 | 0 | for (const rtl::Reference<VCLXAccessibleListItem>& rxChild : m_aAccessibleChildren) |
677 | 0 | { |
678 | 0 | if (rxChild.is()) |
679 | 0 | { |
680 | | // Retrieve the item's index from the list entry. |
681 | 0 | bool bNowSelected = m_pListBoxHelper->IsEntryPosSelected (i); |
682 | 0 | if (bNowSelected) |
683 | 0 | m_nCurSelectedPos = i; |
684 | |
|
685 | 0 | if (bNowSelected && !rxChild->IsSelected()) |
686 | 0 | { |
687 | 0 | xNewAcc = rxChild; |
688 | 0 | aNewValue <<= Reference< XAccessible >(xNewAcc); |
689 | 0 | } |
690 | 0 | else if (rxChild->IsSelected()) |
691 | 0 | m_nLastSelectedPos = i; |
692 | |
|
693 | 0 | rxChild->SetSelected(bNowSelected); |
694 | 0 | } |
695 | 0 | else |
696 | 0 | { // it could happen that a child was not created before |
697 | 0 | checkEntrySelected(i,aNewValue,xNewAcc); |
698 | 0 | } |
699 | 0 | ++i; |
700 | 0 | } |
701 | 0 | const sal_Int32 nCount = m_pListBoxHelper->GetEntryCount(); |
702 | 0 | if ( i < nCount ) // here we have to check the if any other listbox entry is selected |
703 | 0 | { |
704 | 0 | for (; i < nCount && !checkEntrySelected(i,aNewValue,xNewAcc) ;++i ) |
705 | 0 | ; |
706 | 0 | } |
707 | 0 | if ( xNewAcc.is() && GetWindow()->HasFocus() ) |
708 | 0 | { |
709 | 0 | if ( m_nLastSelectedPos != LISTBOX_ENTRY_NOTFOUND ) |
710 | 0 | aOldValue <<= getAccessibleChild( m_nLastSelectedPos ); |
711 | 0 | aNewValue <<= Reference< XAccessible >(xNewAcc); |
712 | 0 | } |
713 | 0 | if (m_pListBoxHelper->IsInDropDown()) |
714 | 0 | { |
715 | 0 | if ( aNewValue.hasValue() || aOldValue.hasValue() ) |
716 | 0 | NotifyAccessibleEvent( |
717 | 0 | AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, |
718 | 0 | aOldValue, |
719 | 0 | aNewValue ); |
720 | | //the SELECTION_CHANGED is not necessary |
721 | | //NotifyAccessibleEvent( AccessibleEventId::SELECTION_CHANGED, Any(), Any() ); |
722 | 0 | } |
723 | 0 | } |
724 | 0 | } |
725 | 0 | } |
726 | | |
727 | | |
728 | | // XAccessibleSelection |
729 | | |
730 | | void SAL_CALL VCLXAccessibleList::selectAccessibleChild( sal_Int64 nChildIndex ) |
731 | 0 | { |
732 | 0 | bool bNotify = false; |
733 | |
|
734 | 0 | { |
735 | 0 | SolarMutexGuard aSolarGuard; |
736 | 0 | ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); |
737 | |
|
738 | 0 | if ( m_pListBoxHelper ) |
739 | 0 | { |
740 | 0 | checkSelection_Impl(nChildIndex,*m_pListBoxHelper,false); |
741 | |
|
742 | 0 | m_pListBoxHelper->SelectEntryPos( static_cast<sal_uInt16>(nChildIndex) ); |
743 | | // call the select handler, don't handle events in this time |
744 | 0 | m_bDisableProcessEvent = true; |
745 | 0 | m_pListBoxHelper->Select(); |
746 | 0 | m_bDisableProcessEvent = false; |
747 | 0 | bNotify = true; |
748 | 0 | } |
749 | 0 | } |
750 | |
|
751 | 0 | if ( bNotify ) |
752 | 0 | UpdateSelection_Impl(); |
753 | 0 | } |
754 | | |
755 | | sal_Bool SAL_CALL VCLXAccessibleList::isAccessibleChildSelected( sal_Int64 nChildIndex ) |
756 | 0 | { |
757 | 0 | SolarMutexGuard aSolarGuard; |
758 | 0 | ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); |
759 | |
|
760 | 0 | bool bRet = false; |
761 | 0 | if ( m_pListBoxHelper ) |
762 | 0 | { |
763 | 0 | checkSelection_Impl(nChildIndex,*m_pListBoxHelper,false); |
764 | |
|
765 | 0 | bRet = m_pListBoxHelper->IsEntryPosSelected( static_cast<sal_uInt16>(nChildIndex) ); |
766 | 0 | } |
767 | 0 | return bRet; |
768 | 0 | } |
769 | | |
770 | | void SAL_CALL VCLXAccessibleList::clearAccessibleSelection( ) |
771 | 0 | { |
772 | 0 | bool bNotify = false; |
773 | |
|
774 | 0 | { |
775 | 0 | SolarMutexGuard aSolarGuard; |
776 | 0 | ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); |
777 | |
|
778 | 0 | if ( m_pListBoxHelper ) |
779 | 0 | { |
780 | 0 | m_pListBoxHelper->SetNoSelection(); |
781 | 0 | bNotify = true; |
782 | 0 | } |
783 | 0 | } |
784 | |
|
785 | 0 | if ( bNotify ) |
786 | 0 | UpdateSelection_Impl(); |
787 | 0 | } |
788 | | |
789 | | void SAL_CALL VCLXAccessibleList::selectAllAccessibleChildren( ) |
790 | 0 | { |
791 | 0 | bool bNotify = false; |
792 | |
|
793 | 0 | { |
794 | 0 | SolarMutexGuard aSolarGuard; |
795 | 0 | ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); |
796 | |
|
797 | 0 | if ( m_pListBoxHelper ) |
798 | 0 | { |
799 | 0 | const sal_Int32 nCount = m_pListBoxHelper->GetEntryCount(); |
800 | 0 | for ( sal_Int32 i = 0; i < nCount; ++i ) |
801 | 0 | m_pListBoxHelper->SelectEntryPos( i ); |
802 | | // call the select handler, don't handle events in this time |
803 | 0 | m_bDisableProcessEvent = true; |
804 | 0 | m_pListBoxHelper->Select(); |
805 | 0 | m_bDisableProcessEvent = false; |
806 | 0 | bNotify = true; |
807 | 0 | } |
808 | 0 | } |
809 | |
|
810 | 0 | if ( bNotify ) |
811 | 0 | UpdateSelection_Impl(); |
812 | 0 | } |
813 | | |
814 | | sal_Int64 SAL_CALL VCLXAccessibleList::getSelectedAccessibleChildCount( ) |
815 | 0 | { |
816 | 0 | SolarMutexGuard aSolarGuard; |
817 | 0 | ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); |
818 | |
|
819 | 0 | sal_Int64 nCount = 0; |
820 | 0 | if ( m_pListBoxHelper ) |
821 | 0 | nCount = m_pListBoxHelper->GetSelectedEntryCount(); |
822 | 0 | return nCount; |
823 | 0 | } |
824 | | |
825 | | Reference< XAccessible > SAL_CALL VCLXAccessibleList::getSelectedAccessibleChild( sal_Int64 nSelectedChildIndex ) |
826 | 0 | { |
827 | 0 | SolarMutexGuard aSolarGuard; |
828 | 0 | ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); |
829 | |
|
830 | 0 | if ( m_pListBoxHelper ) |
831 | 0 | { |
832 | 0 | checkSelection_Impl(nSelectedChildIndex,*m_pListBoxHelper,true); |
833 | 0 | return getAccessibleChild( m_pListBoxHelper->GetSelectedEntryPos( static_cast<sal_uInt16>(nSelectedChildIndex) ) ); |
834 | 0 | } |
835 | | |
836 | 0 | return nullptr; |
837 | 0 | } |
838 | | |
839 | | void SAL_CALL VCLXAccessibleList::deselectAccessibleChild( sal_Int64 nSelectedChildIndex ) |
840 | 0 | { |
841 | 0 | bool bNotify = false; |
842 | |
|
843 | 0 | { |
844 | 0 | SolarMutexGuard aSolarGuard; |
845 | 0 | ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); |
846 | |
|
847 | 0 | if ( m_pListBoxHelper ) |
848 | 0 | { |
849 | 0 | checkSelection_Impl(nSelectedChildIndex,*m_pListBoxHelper,false); |
850 | |
|
851 | 0 | m_pListBoxHelper->SelectEntryPos( static_cast<sal_uInt16>(nSelectedChildIndex), false ); |
852 | | // call the select handler, don't handle events in this time |
853 | 0 | m_bDisableProcessEvent = true; |
854 | 0 | m_pListBoxHelper->Select(); |
855 | 0 | m_bDisableProcessEvent = false; |
856 | 0 | bNotify = true; |
857 | 0 | } |
858 | 0 | } |
859 | |
|
860 | 0 | if ( bNotify ) |
861 | 0 | UpdateSelection_Impl(); |
862 | 0 | } |
863 | | |
864 | | awt::Rectangle VCLXAccessibleList::implGetBounds() |
865 | 0 | { |
866 | 0 | awt::Rectangle aBounds ( 0, 0, 0, 0 ); |
867 | 0 | if ( m_pListBoxHelper |
868 | 0 | && (m_pListBoxHelper->GetStyle() & WB_DROPDOWN ) == WB_DROPDOWN ) |
869 | 0 | { |
870 | 0 | if ( m_pListBoxHelper->IsInDropDown() ) |
871 | 0 | aBounds = vcl::unohelper::ConvertToAWTRect(m_pListBoxHelper->GetDropDownPosSizePixel()); |
872 | 0 | } |
873 | 0 | else |
874 | 0 | { |
875 | | // a list has the same bounds as his parent but starts at (0,0) |
876 | 0 | if (m_xParent) |
877 | 0 | aBounds = m_xParent->getBounds(); |
878 | 0 | aBounds.X = 0; |
879 | 0 | aBounds.Y = 0; |
880 | 0 | if ( m_aBoxType == COMBOBOX ) |
881 | 0 | { |
882 | 0 | VclPtr< ComboBox > pBox = GetAs< ComboBox >(); |
883 | 0 | if ( pBox ) |
884 | 0 | { |
885 | 0 | Size aSize = pBox->GetSubEdit()->GetSizePixel(); |
886 | 0 | aBounds.Y += aSize.Height(); |
887 | 0 | aBounds.Height -= aSize.Height(); |
888 | 0 | } |
889 | 0 | } |
890 | 0 | } |
891 | 0 | return aBounds; |
892 | 0 | } |
893 | | |
894 | | |
895 | | awt::Point VCLXAccessibleList::getLocationOnScreen( ) |
896 | 0 | { |
897 | 0 | SolarMutexGuard aSolarGuard; |
898 | 0 | ::osl::Guard< ::osl::Mutex > aGuard( GetMutex() ); |
899 | |
|
900 | 0 | awt::Rectangle aBounds = implGetBounds(); |
901 | 0 | awt::Point aPos(aBounds.X, aBounds.Y); |
902 | 0 | if (m_xParent.is()) |
903 | 0 | { |
904 | 0 | awt::Point aParentLocation = m_xParent->getLocationOnScreen(); |
905 | 0 | aPos.X += aParentLocation.X; |
906 | 0 | aPos.Y += aParentLocation.Y; |
907 | 0 | } |
908 | |
|
909 | 0 | return aPos; |
910 | 0 | } |
911 | | |
912 | | |
913 | | bool VCLXAccessibleList::IsInDropDown() const |
914 | 0 | { |
915 | 0 | return m_pListBoxHelper->IsInDropDown(); |
916 | 0 | } |
917 | | |
918 | | |
919 | | void VCLXAccessibleList::HandleDropOpen() |
920 | 0 | { |
921 | 0 | if ( !m_bDisableProcessEvent ) |
922 | 0 | UpdateSelection_Impl(); |
923 | 0 | if (m_nCurSelectedPos != LISTBOX_ENTRY_NOTFOUND && |
924 | 0 | m_nLastSelectedPos != LISTBOX_ENTRY_NOTFOUND) |
925 | 0 | { |
926 | 0 | Reference< XAccessible > xChild = getAccessibleChild(m_nCurSelectedPos); |
927 | 0 | if(xChild.is()) |
928 | 0 | { |
929 | 0 | uno::Any aNewValue; |
930 | 0 | aNewValue <<= xChild; |
931 | 0 | NotifyAccessibleEvent(AccessibleEventId::ACTIVE_DESCENDANT_CHANGED, uno::Any(), aNewValue ); |
932 | 0 | } |
933 | 0 | } |
934 | 0 | } |
935 | | |
936 | | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |