Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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: */